#include "fileman.hpp" #include "netdrv.hpp" #include "gserver.hpp" #include "gclient.hpp" #include "undrv.hpp" #include "tcpip.hpp" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define real2shm(type,ptr) (ptr==NULL ? NULL : ((type *)((char *)(ptr)-(char *)base))) #define shm2real(type,ptr) (ptr==NULL ? NULL : ((type *)((long)(ptr)+(long)(base)))) net_driver *driver=NULL; #ifdef __sgi #define next_process() sginap(0) #else #define next_process() usleep(10) #endif #ifdef __sgi void die(...) #else void die(int why) #endif { fprintf(stderr,"dieing\n"); if (driver) { delete driver; driver=NULL; } exit(0); } void mdie(char *reason) { fprintf(stderr,"net driver : %s\n",reason); if (driver) { driver->cleanup(); } exit(0); } void comm_failed() // general communication failure with engine { fprintf(stderr,"net driver : Error occured while trying to communicate with the engine\n"); if (driver) { delete driver; driver=NULL; } exit(0); } int net_driver::add_joiner(int client_id, char *name) { join_array[client_id].next=base->join_list; base->join_list=real2shm(join_struct,&join_array[client_id]); join_array[client_id].client_id=client_id; strcpy(join_array[client_id].name,name); } void net_driver::cleanup() { base->input_state=INPUT_NET_DEAD; fprintf(stderr,"net driver : cleaning up\n"); if (shm_seg_id!=-1) shmctl(shm_seg_id,IPC_RMID,NULL); if (shm_addr!=(void *)-1) { shmdt((char *)shm_addr); shm_addr=(void *)-1; } undrv_cleanup(); unlink(DLOCK_NAME); } net_driver::~net_driver() { cleanup(); } int net_driver::setup_shm() { shm_addr=(void *)-1; // shmat returns -1 on failure shm_seg_id=-1; driver=this; int catch_sigs[]={SIGHUP,SIGINT,SIGQUIT,SIGILL,SIGABRT, SIGIOT,SIGFPE,SIGKILL,SIGUSR1,SIGSEGV, SIGUSR2,SIGPIPE,SIGTERM,SIGCHLD, SIGCONT,SIGSTOP,SIGTSTP,SIGTTIN,SIGTTOU,-1}; int i; for (i=0;catch_sigs[i]!=-1;i++) // catch all signals in case we get signal(catch_sigs[i],die); // interrupted before we remove shmid int alloc_size=sizeof(join_struct)*MAX_JOINERS+sizeof(base_memory_struct); shm_seg_id=shmget(IPC_PRIVATE,alloc_size,IPC_CREAT | 0777); if (shm_seg_id==-1) mdie("Unable to allocate shared memory"); shm_addr=shmat(shm_seg_id,NULL,0); // attach as read/write if (shm_addr==(void *)-1) mdie("could not attach shm seg"); base=(base_memory_struct *)shm_addr; base->join_list=real2shm(join_struct,NULL); base->mem_lock=0; base->calc_crcs=0; base->get_lsf=0; base->wait_reload=0; base->need_reload=0; base->input_state=INPUT_COLLECTING; base->current_tick=0; base->packet.packet_reset(); join_array=(join_struct *) (base+1); // see if we can attach this memory with the abuse engine if (out->write(&shm_seg_id,sizeof(shm_seg_id))!=sizeof(shm_seg_id)) comm_failed(); // wait for engine to ack it has attached uchar ack=0; if (in->read(&ack,1)!=1 || ack!=1) comm_failed(); if (shmctl(shm_seg_id,IPC_RMID,NULL)) // remove the shm id mdie("could not remove shm id"); shm_seg_id=-1; // mark id as not allocated return 1; } int net_driver::connect_to_engine(int argc, char **argv) { if (mkfifo(DIN_NAME,S_IRWXU | S_IRWXG | S_IRWXO)) { perror("Net driver : unable to make fifo in /tmp"); return 0; } chmod(DIN_NAME,S_IRWXU | S_IRWXG | S_IRWXO); // just to be sure umask doesn't screw us if (mkfifo(DOUT_NAME,S_IRWXU | S_IRWXG | S_IRWXO)) { perror("Net driver : unable to make fifo in /tmp"); return 0; } chmod(DOUT_NAME,S_IRWXU | S_IRWXG | S_IRWXO); int driver_out_fd=open(DOUT_NAME,O_RDWR); // open the pipe if (driver_out_fd<0) { perror(DOUT_NAME); exit(1); } int driver_in_fd=open(DIN_NAME,O_RDWR); if (driver_in_fd<0) { close(driver_out_fd); perror(DIN_NAME); exit(1); } in=new unix_fd(driver_in_fd); in->read_selectable(); out=new unix_fd(driver_out_fd); if (in->read(®,sizeof(reg))!=sizeof(reg)) mdie("unable to registration from engine"); } net_driver::net_driver(int argc, char **argv, int comm_port, int game_port, net_protocol *proto) : comm_port(comm_port), game_port(game_port), proto(proto) { debug=0; lsf_wait_list=NULL; crc_wait_list=NULL; base=NULL; in=out=NULL; game_face=new game_handler(); connect_to_engine(argc,argv); setup_shm(); int i; for (i=1;iready_to_read()) // commands from engine? { uchar cmd; if (in->read(&cmd,1)!=1) return 0; if (debug) { if (cmd<=EGCMD_DIE) { char *cmds[]={"open","close","read","write","seek","size","tell","setfs","crc_calced","process_lsf","request_lfs", "equest_entry","become_server","block","reload_start","reload_end","send_input","input_missing", "kill_slackers","die"}; fprintf(stderr,"engine cmd : %s\n",cmds[cmd]); } } switch (cmd) { case EGCMD_DIE : { cmd=game_face->quit(); if (!out->write(&cmd,1)) { mdie("could not write block ack1"); } // send something to unblock engine mdie("received die command"); } break; case NFCMD_RELOAD_START : { cmd=game_face->start_reload(); if (!out->write(&cmd,1)) { mdie("could not write start reload ack"); } // send something to unblock engine } break; case NFCMD_RELOAD_END : { cmd=game_face->end_reload(); if (!out->write(&cmd,1)) { mdie("could not write end reload ack"); } // send something to unblock engine } break; case NFCMD_BLOCK : { if (!out->write(&cmd,1)) { mdie("could not write block ack1"); } // send something to unblock engine if (!in->read(&cmd,1)) { mdie("could not read block ack1"); } // send something to block ourself } break; case NFCMD_INPUT_MISSING : // try to fetch the input via a loss-less net protocol { game_face->input_missing(); if (out->write(&cmd,1)!=1) { mdie("could not write block ack1"); } // send something to unblock engine } break; case NFCMD_KILL_SLACKERS : { if (!game_face->kill_slackers()) { delete game_face; game_face=new game_handler(); } if (out->write(&cmd,1)!=1) { mdie("could not write block ack1"); } // send something to unblock engine } break; case NFCMD_SEND_INPUT : { game_face->add_engine_input(); if (out->write(&cmd,1)!=1) { mdie("could not write send ack1"); } // send something to unblock engine if (in->read(&cmd,1)!=1) { mdie("could not read send ack2"); } // read something to block ourselves for engine } break; case NFCMD_REQUEST_ENTRY : { uchar len; char name[256]; if (in->read(&len,1)!=1) { mdie("could not read server name length"); } if (in->read(name,len)!=len) { mdie("could not read server name"); } ushort success=join_server(name); if (out->write(&success,2)!=2) mdie("cound not send lsf read failure"); } break; case NFCMD_BECOME_SERVER : { cmd=become_server(); if (out->write(&cmd,1)!=1) mdie("cound not send ok"); } break; case NFCMD_REQUEST_LSF : { uchar len; char name[256]; if (in->read(&len,1)!=1) { mdie("could not read lsf name length"); } if (in->read(name,len)!=len) { mdie("could not read lsf name"); } if (!get_lsf(name)) { len=0; if (out->write(&len,1)!=1) mdie("cound not send lsf read failure"); } else { len=strlen(name)+1; if (out->write(&len,1)!=1) mdie("cound not send lsf name len"); if (out->write(name,len)!=len) mdie("cound not send lsf name"); } } break; case NFCMD_PROCESS_LSF : { uchar len,name[256]; if (in->read(&len,1)!=1) { mdie("could not read lsf name length"); } if (in->read(name,len)!=len) { mdie("could not read lsf name"); } while (lsf_wait_list) { lsf_waiter *c=lsf_wait_list; lsf_wait_list=lsf_wait_list->next; uchar status=1; c->sock->write(&len,1); c->sock->write(name,len); delete c; } } break; case NFCMD_CRCS_CALCED : { while (crc_wait_list) { crc_waiter *c=crc_wait_list; crc_wait_list=crc_wait_list->next; uchar status=1; c->sock->write(&status,1); delete c; } } break; case NFCMD_SET_FS : { uchar size; char sn[256]; if (in->read(&size,1)!=1) mdie("could not read filename length"); if (in->read(sn,size)!=size) mdie("could not read server name"); fman->set_default_fs_name(sn); size=fetch_crcs(sn); // return success if (out->write(&size,1)!=1) mdie("could not send ok to engine"); } break; case NFCMD_OPEN : { uchar size[2]; char filename[300],mode[20],*fn; fn=filename; if (in->read(size,2)!=2 || in->read(filename,size[0])!=size[0] || in->read(mode,size[1])!=size[1]) mdie("incomplete open command from engine"); int fd=fman->rf_open_file(fn,mode); if (fd==-2) { uchar st[2]; st[0]=NF_OPEN_LOCAL_FILE; st[1]=strlen(fn)+1; if (out->write(st,2)!=2) comm_failed(); if (out->write(fn,st[1])!=st[1]) comm_failed(); } else if (fd==-1) { uchar st=NF_OPEN_FAILED; if (out->write(&st,1)!=1) comm_failed(); } else { uchar st=NF_OPEN_REMOTE_FILE; if (out->write(&st,1)!=1) comm_failed(); if (out->write(&fd,sizeof(fd))!=sizeof(fd)) comm_failed(); } } break; case NFCMD_CLOSE : case NFCMD_SIZE : case NFCMD_TELL : case NFCMD_SEEK : case NFCMD_READ : { int fd; if (in->read(&fd,sizeof(fd))!=sizeof(fd)) comm_failed(); switch (cmd) { case NFCMD_CLOSE : { fman->rf_close(fd); uchar st=1; if (out->write(&st,1)!=1) comm_failed(); } break; case NFCMD_SIZE : { long x=fman->rf_file_size(fd); if (out->write(&x,sizeof(x))!=sizeof(x)) comm_failed(); } break; case NFCMD_TELL : { long offset=fman->rf_tell(fd); if (out->write(&offset,sizeof(offset))!=sizeof(offset)) comm_failed(); } break; case NFCMD_SEEK : { long offset; if (in->read(&offset,sizeof(offset))!=sizeof(offset)) comm_failed(); offset=fman->rf_seek(fd,offset); if (out->write(&offset,sizeof(offset))!=sizeof(offset)) comm_failed(); } break; case NFCMD_READ : { long size; if (in->read(&size,sizeof(size))!=sizeof(size)) comm_failed(); fman->rf_read(fd,out,size); } break; } } break; default : { fprintf(stderr,"net driver : unknown net command %d\n",cmd); die(0); } } ret=1; } ret|=game_face->process_net(); return ret; } int net_driver::join_server(char *server_name) // ask remote server for entry into game { char sn_start[256]; strcpy(sn_start,server_name); net_socket *sock=connect_to_server(server_name,DEFAULT_COMM_PORT,0); if (!sock) { fprintf(stderr,"unable to connect\n"); return 0; } uchar ctype=CLIENT_ABUSE; ushort port=lstl(game_port),cnum; uchar reg; if (sock->write(&ctype,1)!=1 || // send server out game port sock->read(®,1)!=1) // is remote engine registered? { delete sock; return 0; } // maker sure the two games are both registered or unregistered or sync problems // will occur. if (reg && !registered()) { fprintf(stderr, "Sorry, this server is running REGISTERED ABUSE and you are not.\n" "Ask the server operator to run with -share option or better yet,\n" "Buy ABUSE, registered net games are more fun because you can fly,\n" "turn invisible and have more weapons to duke it out with\n"); delete sock; return 0; } if (!reg && registered()) { fprintf(stderr, "This is server is not running the registered version of abuse, and\n" "you are (thanks!). So that there are no conflict between the two games\n" "please start with the -share option when connecting to this server\n" "example : abuse -net somewhere.someplace.net -share\n"); delete sock; return 0; } char uname[256]; if (getlogin()) strcpy(uname,getlogin()); else strcpy(uname,"unknown"); uchar len=strlen(uname)+1; if (sock->write(&len,1)!=1 || sock->write(uname,len)!=len || sock->write(&port,2)!=2 || // send server out game port sock->read(&port,2)!=2 || // read server's game port sock->read(&cnum,2)!=2 || cnum==0 // read player number (cannot be 0 because 0 is server) ) { delete sock; return 0; } port=lstl(port); cnum=lstl(cnum); server_name=sn_start; net_socket *data_sock=connect_to_server(server_name,port,1,net_socket::SOCKET_FAST); if (!data_sock) { delete sock; return 0; } delete game_face; game_face=new game_client(sn_start,sock,data_sock); return cnum; } net_socket *net_driver::connect_to_server(char *&name, int port, int force_port, net_socket::socket_type sock_type) { char *oname=name; net_address *addr=proto->get_node_address(name, port, force_port); if (!addr) { if (debug) fprintf(stderr,"No IP address for name %s\n",oname); return NULL; } if (debug) fprintf(stderr,"connecting to server %s\n",oname); net_socket *sock=proto->connect_to_server(addr,sock_type); delete addr; return sock; } int net_driver::get_lsf(char *name) // contact remot host and ask for lisp startup file filename { char *name_start=name; net_socket *sock=connect_to_server(name); if (!sock) return 0; uchar ctype=CLIENT_LSF_WAITER; uchar len; if (sock->write(&ctype,1)!=1 || sock->read(&len,1)!=1 || len==0 || sock->read(name_start,len)!=len) { delete sock; return 0; } delete sock; return 1; } int net_driver::fetch_crcs(char *server) { net_socket *sock=connect_to_server(server); if (!sock) return 0; uchar cmd=CLIENT_CRC_WAITER; if (sock->write(&cmd,1)!=1 || sock->read(&cmd,1)!=1) { delete sock; return 0; } delete sock; return cmd; } int net_driver::add_client(int type, net_socket *sock, net_address *from) { switch (type) { case CLIENT_CRC_WAITER : { if (debug) fprintf(stderr,"add crc waiter\n"); crc_wait_list=new crc_waiter(sock,crc_wait_list); base->calc_crcs=1; return 1; } break; case CLIENT_LSF_WAITER : { if (debug) fprintf(stderr,"add lsf waiter\n"); lsf_wait_list=new lsf_waiter(sock,lsf_wait_list); base->get_lsf=1; return 1; } break; default : { int ret=game_face->add_client(type,sock,from); if (!ret && debug) fprintf(stderr,"unknown client type %d\n",type); return ret; } } return 0; }