tNon-blocking network handling abstracted out into NetworkBuffer datatype - vaccinewars - be a doctor and try to vaccinate the world (HTM) git clone git://src.adamsgaard.dk/vaccinewars (DIR) Log (DIR) Files (DIR) Refs (DIR) README (DIR) LICENSE --- (DIR) commit 7d0b10dce7f72780b488b3df2800347731565a73 (DIR) parent e694fa9cbc3214dbf38465474571e2d9209f7d23 (HTM) Author: Ben Webb <ben@salilab.org> Date: Tue, 5 Jun 2001 02:45:07 +0000 Non-blocking network handling abstracted out into NetworkBuffer datatype Diffstat: M TODO | 9 ++------- M src/AIPlayer.c | 32 +++++++++++++++---------------- M src/curses_client.c | 31 +++++++++++++------------------ M src/dopewars.c | 17 +++++++---------- M src/dopewars.h | 11 +++++++++-- M src/gtk_client.c | 36 +++++++++++++------------------ M src/message.c | 170 +++++++++++++++++++++++++------ M src/message.h | 24 ++++++++++++++++++++---- M src/serverside.c | 61 ++++++++++--------------------- 9 files changed, 240 insertions(+), 151 deletions(-) --- (DIR) diff --git a/TODO b/TODO t@@ -1,13 +1,8 @@ -DONE - Tidy up display of high scores in GUI client -- Revamp player-player fighting; use same system for fighting the cops and - for fighting other players (perhaps the cops can intervene in fights); - add SWAT teams, soldiers, etc. as dealers get more and more guns -- Increase cops' toughness - they should kill a bitch in 50-70% of encounters - (and damage should be cumulative) +- Fix problem with dialogs popping up while menus are open +- Fix problem with Jet dialog during fights - Increase difficulty of escaping from another player - impose penalty on running (lose drugs, free shot, destination revealed) - Alliances/cartels - several players share cash -- Graphical mode server? (would avoid select() problems under Win32) - Introduce minimum/maximum players options - AI players automatically spawned/killed to "fill the gaps" when humans leave/enter - "Deal" option when meeting players? (DIR) diff --git a/src/AIPlayer.c b/src/AIPlayer.c t@@ -54,7 +54,8 @@ void AIPlayerLoop() { gchar *pt; Player *AIPlay; fd_set readfs,writefs; - gboolean ReadOK,QuitRequest; + gboolean DataWaiting,QuitRequest; + int MaxSock; AIPlay=g_new(Player,1); FirstClient=AddPlayer(0,AIPlay,FirstClient); t@@ -67,7 +68,7 @@ void AIPlayerLoop() { "AI Player terminating abnormally."),_(pt)); return; } - AIPlay->fd=ClientSock; + BindNetworkBufferToSocket(&AIPlay->NetBuf,ClientSock); InitAbilities(AIPlay); SendAbilities(AIPlay); t@@ -81,31 +82,28 @@ void AIPlayerLoop() { while (1) { FD_ZERO(&readfs); FD_ZERO(&writefs); - FD_SET(ClientSock,&readfs); - if (AIPlay->WriteBuf.DataPresent) FD_SET(ClientSock,&writefs); - if (bselect(ClientSock+1,&readfs,&writefs,NULL,NULL)==-1) { + MaxSock=0; + + SetSelectForNetworkBuffer(&AIPlay->NetBuf,&readfs,&writefs,NULL,&MaxSock); + + if (bselect(MaxSock,&readfs,&writefs,NULL,NULL)==-1) { if (errno==EINTR) continue; printf("Error in select\n"); exit(1); } - if (FD_ISSET(ClientSock,&writefs)) { - WriteConnectionBufferToWire(AIPlay); - } - if (FD_ISSET(ClientSock,&readfs)) { - QuitRequest=FALSE; - ReadOK=ReadConnectionBufferFromWire(AIPlay); - while ((pt=ReadFromConnectionBuffer(AIPlay))!=NULL) { + if (!RespondToSelect(&AIPlay->NetBuf,&readfs,&writefs, + NULL,&DataWaiting)) { + g_print(_("Connection to server lost!\n")); + break; + } else if (DataWaiting) { + QuitRequest=FALSE; + while ((pt=GetWaitingPlayerMessage(AIPlay))!=NULL) { if (HandleAIMessage(pt,AIPlay)) { QuitRequest=TRUE; break; } } if (QuitRequest) break; - - if (!ReadOK) { - g_print(_("Connection to server lost!\n")); - break; - } } } ShutdownNetwork(); (DIR) diff --git a/src/curses_client.c b/src/curses_client.c t@@ -1507,7 +1507,7 @@ static void Curses_DoGame(Player *Play) { char HaveWorthless; Player *tmp; struct sigaction sact; - gboolean ReadOK; + gboolean DataWaiting; DisplayMode=DM_NONE; QuitRequest=FALSE; t@@ -1535,7 +1535,7 @@ static void Curses_DoGame(Player *Play) { #if NETWORKING if (WantNetwork) { if (!ConnectToServer(Play)) { end_curses(); exit(1); } - Play->fd=ClientSock; + BindNetworkBufferToSocket(&Play->NetBuf,ClientSock); } #endif /* NETWORKING */ print_status(Play,TRUE); t@@ -1642,9 +1642,8 @@ static void Curses_DoGame(Player *Play) { FD_ZERO(&writefs); FD_SET(0,&readfs); MaxSock=1; if (Client) { - FD_SET(Play->fd,&readfs); - if (Play->WriteBuf.DataPresent) FD_SET(Play->fd,&writefs); - MaxSock=ClientSock+2; + SetSelectForNetworkBuffer(&Play->NetBuf,&readfs,&writefs, + NULL,&MaxSock); } if (bselect(MaxSock,&readfs,&writefs,NULL,NULL)==-1) { if (errno==EINTR) { t@@ -1653,16 +1652,9 @@ static void Curses_DoGame(Player *Play) { } perror("bselect"); exit(1); } - if (Client && FD_ISSET(Play->fd,&readfs)) { - ReadOK=ReadConnectionBufferFromWire(Play); - - while ((pt=ReadFromConnectionBuffer(Play))!=NULL) { - HandleClientMessage(pt,Play); - g_free(pt); - } - if (QuitRequest) return; - - if (!ReadOK) { + if (Client) { + if (!RespondToSelect(&Play->NetBuf,&readfs,&writefs, + NULL,&DataWaiting)) { attrset(TextAttr); clear_line(22); mvaddstr(22,0,_("Connection to server lost! " t@@ -1670,11 +1662,14 @@ static void Curses_DoGame(Player *Play) { nice_wait(); SwitchToSinglePlayer(Play); print_status(Play,TRUE); + } else if (DataWaiting) { + while ((pt=GetWaitingPlayerMessage(Play))!=NULL) { + HandleClientMessage(pt,Play); + g_free(pt); + } + if (QuitRequest) return; } } - if (Client && FD_ISSET(Play->fd,&writefs)) { - WriteConnectionBufferToWire(Play); - } if (FD_ISSET(0,&readfs)) { #elif HAVE_SELECT FD_ZERO(&readfs); (DIR) diff --git a/src/dopewars.c b/src/dopewars.c t@@ -600,7 +600,6 @@ GSList *AddPlayer(int fd,Player *NewPlayer,GSList *First) { list=g_slist_next(list); } } - NewPlayer->fd=-1; NewPlayer->Name=NULL; SetPlayerName(NewPlayer,NULL); NewPlayer->IsAt=0; t@@ -619,11 +618,11 @@ GSList *AddPlayer(int fd,Player *NewPlayer,GSList *First) { NewPlayer->Health=100; NewPlayer->CoatSize=100; NewPlayer->Flags=0; - NewPlayer->ReadBuf.Data=NewPlayer->WriteBuf.Data=NULL; - NewPlayer->ReadBuf.Length=NewPlayer->WriteBuf.Length=0; - NewPlayer->ReadBuf.DataPresent=NewPlayer->WriteBuf.DataPresent=0; +#if NETWORKING + InitNetworkBuffer(&NewPlayer->NetBuf,'\n'); + if (Server) BindNetworkBufferToSocket(&NewPlayer->NetBuf,fd); +#endif InitAbilities(NewPlayer); - if (Server) NewPlayer->fd=fd; NewPlayer->FightArray=NULL; NewPlayer->Attacking=NULL; return g_slist_append(First,(gpointer)NewPlayer); t@@ -643,13 +642,11 @@ GSList *RemovePlayer(Player *Play,GSList *First) { g_assert(First); First=g_slist_remove(First,(gpointer)Play); - if (Server && !IsCop(Play) && Play->fd>=0) { - CloseSocket(Play->fd); - } +#if NETWORKING + if (!IsCop(Play)) ShutdownNetworkBuffer(&Play->NetBuf); +#endif ClearList(&(Play->SpyList)); ClearList(&(Play->TipList)); - g_free(Play->ReadBuf.Data); - g_free(Play->WriteBuf.Data); g_free(Play->Name); g_free(Play); return First; (DIR) diff --git a/src/dopewars.h b/src/dopewars.h t@@ -277,6 +277,14 @@ typedef struct tagConnBuf { int DataPresent; /* number of bytes currently in "Data" */ } ConnBuf; +/* Handles reading and writing messages from/to a network connection */ +typedef struct tagNetworkBuffer { + int fd; /* File descriptor of the socket */ + char Terminator; /* Character that separates messages */ + ConnBuf ReadBuf; /* New data, waiting for the application */ + ConnBuf WriteBuf; /* Data waiting to be written to the wire */ +} NetworkBuffer; + struct PLAYER_T { guint ID; int Turn; t@@ -287,13 +295,12 @@ struct PLAYER_T { char Flags; gchar *Name; Inventory *Guns,*Drugs,Bitches; - int fd; int EventNum,ResyncNum; time_t FightTimeout,IdleTimeout,ConnectTimeout; price_t DocPrice; DopeList SpyList,TipList; Player *OnBehalfOf; - ConnBuf ReadBuf,WriteBuf; + NetworkBuffer NetBuf; Abilities Abil; gint InputTag; GPtrArray *FightArray; /* If non-NULL, a list of players in a fight */ (DIR) diff --git a/src/gtk_client.c b/src/gtk_client.c t@@ -257,26 +257,20 @@ void ListInventory(GtkWidget *widget,gpointer data) { void GetClientMessage(gpointer data,gint socket, GdkInputCondition condition) { gchar *pt; - gboolean ReadOK; - if (condition&GDK_INPUT_WRITE) { - WriteConnectionBufferToWire(ClientData.Play); - if (ClientData.Play->WriteBuf.DataPresent==0) { - SetSocketWriteTest(ClientData.Play,FALSE); - } - } - if (condition&GDK_INPUT_READ) { - ReadOK=ReadConnectionBufferFromWire(ClientData.Play); - while ((pt=ReadFromConnectionBuffer(ClientData.Play))!=NULL) { - HandleClientMessage(pt,ClientData.Play); g_free(pt); - } - if (!ReadOK) { - if (Network) gdk_input_remove(ClientData.GdkInputTag); - if (InGame) { + gboolean DataWaiting; + if (!PlayerHandleNetwork(ClientData.Play,condition&GDK_INPUT_READ, + condition&GDK_INPUT_WRITE,&DataWaiting)) { + if (Network) gdk_input_remove(ClientData.GdkInputTag); + if (InGame) { /* The network connection to the server was dropped unexpectedly */ - g_warning(_("Connection to server lost - switching to " - "single player mode")); - SwitchToSinglePlayer(ClientData.Play); - } + g_warning(_("Connection to server lost - switching to " + "single player mode")); + SwitchToSinglePlayer(ClientData.Play); + } + } else if (DataWaiting) { + while ((pt=GetWaitingPlayerMessage(ClientData.Play))!=NULL) { + HandleClientMessage(pt,ClientData.Play); + g_free(pt); } } } t@@ -284,7 +278,7 @@ void GetClientMessage(gpointer data,gint socket, void SetSocketWriteTest(Player *Play,gboolean WriteTest) { if (Network) { if (ClientData.GdkInputTag) gdk_input_remove(ClientData.GdkInputTag); - ClientData.GdkInputTag=gdk_input_add(Play->fd, + ClientData.GdkInputTag=gdk_input_add(Play->NetBuf.fd, GDK_INPUT_READ|(WriteTest ? GDK_INPUT_WRITE : 0), GetClientMessage,NULL); } t@@ -1513,7 +1507,7 @@ void StartGame() { Player *Play; Play=ClientData.Play=g_new(Player,1); FirstClient=AddPlayer(0,Play,FirstClient); - Play->fd=ClientSock; + BindNetworkBufferToSocket(&Play->NetBuf,ClientSock); InitAbilities(Play); SendAbilities(Play); SetPlayerName(Play,ClientData.PlayerName); (DIR) diff --git a/src/message.c b/src/message.c t@@ -147,7 +147,7 @@ void DoSendClientMessage(Player *From,char AICode,char Code, HandleServerMessage(text->str,ServerFrom); #if NETWORKING } else { - WriteToConnectionBuffer(BufOwn,text->str); + QueuePlayerMessageForSend(BufOwn,text->str); if (SocketWriteTestPt) (*SocketWriteTestPt)(BufOwn,TRUE); } #endif /* NETWORKING */ t@@ -193,7 +193,7 @@ void SendServerMessage(Player *From,char AICode,char Code, } #if NETWORKING } else { - WriteToConnectionBuffer(To,text->str); + QueuePlayerMessageForSend(To,text->str); if (SocketWriteTestPt) (*SocketWriteTestPt)(To,TRUE); } #endif t@@ -279,19 +279,117 @@ gboolean HaveAbility(Player *Play,gint Type) { } #if NETWORKING -gchar *ReadFromConnectionBuffer(Player *Play) { -/* Reads a newline-terminated message from "Play"'s read buffer. The message */ -/* is removed from the buffer, and returned as a null-terminated string (the */ -/* terminating newline is removed). If no complete message is waiting, NULL */ -/* is returned. The string is dynamically allocated, and must be g_free'd by */ -/* the caller. */ +void InitNetworkBuffer(NetworkBuffer *NetBuf,char Terminator) { +/* Initialises the passed network buffer, ready for use. Messages sent */ +/* or received on the buffered connection will be terminated by the */ +/* given character. */ + NetBuf->fd=-1; + NetBuf->Terminator=Terminator; + NetBuf->ReadBuf.Data=NetBuf->WriteBuf.Data=NULL; + NetBuf->ReadBuf.Length=NetBuf->WriteBuf.Length=0; + NetBuf->ReadBuf.DataPresent=NetBuf->WriteBuf.DataPresent=0; +} + +void BindNetworkBufferToSocket(NetworkBuffer *NetBuf,int fd) { +/* Sets up the given network buffer to handle data being sent/received */ +/* through the given socket */ + NetBuf->fd=fd; +} + +void ShutdownNetworkBuffer(NetworkBuffer *NetBuf) { +/* Frees the network buffer's data structures (leaving it in the */ +/* 'initialised' state) and closes the accompanying socket. */ + if (NetBuf->fd>0) CloseSocket(NetBuf->fd); + + g_free(NetBuf->ReadBuf.Data); + g_free(NetBuf->WriteBuf.Data); + InitNetworkBuffer(NetBuf,NetBuf->Terminator); +} + +void SetSelectForNetworkBuffer(NetworkBuffer *NetBuf,fd_set *readfds, + fd_set *writefds,fd_set *errorfds,int *MaxSock) { +/* Updates the sets of read and write file descriptors to monitor */ +/* input to/output from the given network buffer. MaxSock is updated */ +/* with the highest-numbered file descriptor (plus 1) for use in a */ +/* later select() call. */ + if (!NetBuf || NetBuf->fd<=0) return; + FD_SET(NetBuf->fd,readfds); + if (errorfds) FD_SET(NetBuf->fd,errorfds); + if (NetBuf->fd >= *MaxSock) *MaxSock=NetBuf->fd+1; + if (NetBuf->WriteBuf.DataPresent) FD_SET(NetBuf->fd,writefds); +} + +static gboolean DoNetworkBufferStuff(NetworkBuffer *NetBuf,gboolean ReadReady, + gboolean WriteReady,gboolean ErrorReady, + gboolean *ReadOK,gboolean *WriteOK, + gboolean *ErrorOK) { +/* Reads and writes data if the network connection is ready. Sets the */ +/* various OK variables to TRUE if no errors occurred in the relevant */ +/* operations, and returns TRUE if data was read and is waiting for */ +/* processing. */ + gboolean DataWaiting=FALSE; + *ReadOK=*WriteOK=*ErrorOK=TRUE; + + if (ErrorReady) *ErrorOK=FALSE; + + if (WriteReady) *WriteOK=WriteDataToWire(NetBuf); + + if (ReadReady) { + *ReadOK=ReadDataFromWire(NetBuf); + if (ReadOK) DataWaiting=TRUE; + } + return DataWaiting; +} + +gboolean RespondToSelect(NetworkBuffer *NetBuf,fd_set *readfds, + fd_set *writefds,fd_set *errorfds, + gboolean *DataWaiting) { +/* Responds to a select() call by reading/writing data as necessary. */ +/* If any data were read, DataWaiting is set TRUE. Returns TRUE unless */ +/* a fatal error (i.e. the connection was broken) occurred. */ + gboolean ReadOK,WriteOK,ErrorOK; + *DataWaiting=DoNetworkBufferStuff(NetBuf,FD_ISSET(NetBuf->fd,readfds), + FD_ISSET(NetBuf->fd,writefds), + errorfds ? FD_ISSET(NetBuf->fd,errorfds) : FALSE, + &ReadOK,&WriteOK,&ErrorOK); + return (WriteOK && ErrorOK && ReadOK); +} + +gboolean PlayerHandleNetwork(Player *Play,gboolean ReadReady, + gboolean WriteReady,gboolean *DataWaiting) { +/* Reads and writes player data from/to the network if it is ready. */ +/* If any data were read, DataWaiting is set TRUE. Returns TRUE unless */ +/* a fatal error (i.e. the connection was broken) occurred. */ + gboolean ReadOK,WriteOK,ErrorOK; + *DataWaiting=DoNetworkBufferStuff(&Play->NetBuf,ReadReady,WriteReady,FALSE, + &ReadOK,&WriteOK,&ErrorOK); + +/* If we've written out everything, then ask not to be notified of + socket write-ready status in future */ + if (WriteReady && Play->NetBuf.WriteBuf.DataPresent==0 && + SocketWriteTestPt) { + (*SocketWriteTestPt)(Play,FALSE); + } + return (WriteOK && ErrorOK && ReadOK); +} + +gchar *GetWaitingPlayerMessage(Player *Play) { + return GetWaitingMessage(&Play->NetBuf); +} + +gchar *GetWaitingMessage(NetworkBuffer *NetBuf) { +/* Reads a complete (terminated) message from the network buffer. The */ +/* message is removed from the buffer, and returned as a null-terminated */ +/* string (the network terminator is removed). If no complete message is */ +/* waiting, NULL is returned. The string is dynamically allocated, and */ +/* so must be g_free'd by the caller. */ ConnBuf *conn; int MessageLen; char *SepPt; gchar *NewMessage; - conn=&Play->ReadBuf; + conn=&NetBuf->ReadBuf; if (!conn->Data || !conn->DataPresent) return NULL; - SepPt=memchr(conn->Data,'\n',conn->DataPresent); + SepPt=memchr(conn->Data,NetBuf->Terminator,conn->DataPresent); if (!SepPt) return NULL; *SepPt='\0'; MessageLen=SepPt-conn->Data+1; t@@ -305,13 +403,17 @@ gchar *ReadFromConnectionBuffer(Player *Play) { return NewMessage; } -gboolean ReadConnectionBufferFromWire(Player *Play) { -/* Reads any waiting data on the TCP/IP connection for player "Play" into */ -/* the player's read buffer. Returns FALSE if the connection was closed, */ -/* or if the read buffer's maximum size was reached. */ +gboolean ReadPlayerDataFromWire(Player *Play) { + return ReadDataFromWire(&Play->NetBuf); +} + +gboolean ReadDataFromWire(NetworkBuffer *NetBuf) { +/* Reads any waiting data on the given network buffer's TCP/IP connection */ +/* into the read buffer. Returns FALSE if the connection was closed, or */ +/* if the read buffer's maximum size was reached. */ ConnBuf *conn; int CurrentPosition,BytesRead; - conn=&Play->ReadBuf; + conn=&NetBuf->ReadBuf; CurrentPosition=conn->DataPresent; while(1) { if (CurrentPosition>=conn->Length) { t@@ -322,7 +424,7 @@ gboolean ReadConnectionBufferFromWire(Player *Play) { if (conn->Length>MAXREADBUF) conn->Length=MAXREADBUF; conn->Data=g_realloc(conn->Data,conn->Length); } - BytesRead=recv(Play->fd,&conn->Data[CurrentPosition], + BytesRead=recv(NetBuf->fd,&conn->Data[CurrentPosition], conn->Length-CurrentPosition,0); if (BytesRead==SOCKET_ERROR) { #ifdef CYGWIN t@@ -340,15 +442,19 @@ gboolean ReadConnectionBufferFromWire(Player *Play) { return TRUE; } -void WriteToConnectionBuffer(Player *Play,gchar *data) { -/* Writes the null-terminated string "data" to "Play"'s connection buffer. */ -/* The message is automatically newline-terminated. Fails to write the */ -/* message without error if the buffer reaches its maximum size (although */ -/* this error will be detected when the buffer is attempted to be written */ -/* to the wire, below) */ +void QueuePlayerMessageForSend(Player *Play,gchar *data) { + QueueMessageForSend(&Play->NetBuf,data); +} + +void QueueMessageForSend(NetworkBuffer *NetBuf,gchar *data) { +/* Writes the null-terminated string "data" to the network buffer, ready */ +/* to be sent to the wire when the network connection becomes free. The */ +/* message is automatically terminated. Fails to write the message without */ +/* error if the buffer reaches its maximum size (although this error will */ +/* be detected when an attempt is made to write the buffer to the wire). */ int AddLength,NewLength; ConnBuf *conn; - conn=&Play->WriteBuf; + conn=&NetBuf->WriteBuf; AddLength=strlen(data)+1; NewLength=conn->DataPresent+AddLength; if (NewLength > conn->Length) { t@@ -360,21 +466,25 @@ void WriteToConnectionBuffer(Player *Play,gchar *data) { } memcpy(&conn->Data[conn->DataPresent],data,AddLength); conn->DataPresent=NewLength; - conn->Data[NewLength-1]='\n'; + conn->Data[NewLength-1]=NetBuf->Terminator; +} + +gboolean WritePlayerDataToWire(Player *Play) { + return WriteDataToWire(&Play->NetBuf); } -gboolean WriteConnectionBufferToWire(Player *Play) { -/* Writes any waiting data in "Play"'s connection buffer to the wire. */ -/* Returns TRUE on success, or FALSE if the buffer's maximum length is */ -/* reached, or the remote end has closed the connection. */ +gboolean WriteDataToWire(NetworkBuffer *NetBuf) { +/* Writes any waiting data in the network buffer to the wire. Returns */ +/* TRUE on success, or FALSE if the buffer's maximum length is */ +/* reached, or the remote end has closed the connection. */ ConnBuf *conn; int CurrentPosition,BytesSent; - conn=&Play->WriteBuf; + conn=&NetBuf->WriteBuf; if (!conn->Data || !conn->DataPresent) return TRUE; if (conn->Length==MAXWRITEBUF) return FALSE; CurrentPosition=0; while (CurrentPosition<conn->DataPresent) { - BytesSent=send(Play->fd,&conn->Data[CurrentPosition], + BytesSent=send(NetBuf->fd,&conn->Data[CurrentPosition], conn->DataPresent-CurrentPosition,0); if (BytesSent==SOCKET_ERROR) { #ifdef CYGWIN (DIR) diff --git a/src/message.h b/src/message.h t@@ -115,10 +115,26 @@ void SendPrintMessage(Player *From,char AICode,Player *To,char *Data); void SendQuestion(Player *From,char AICode,Player *To,char *Data); #if NETWORKING -gchar *ReadFromConnectionBuffer(Player *Play); -gboolean ReadConnectionBufferFromWire(Player *Play); -void WriteToConnectionBuffer(Player *Play,gchar *data); -gboolean WriteConnectionBufferToWire(Player *Play); +void InitNetworkBuffer(NetworkBuffer *NetBuf,char Terminator); +void BindNetworkBufferToSocket(NetworkBuffer *NetBuf,int fd); +void ShutdownNetworkBuffer(NetworkBuffer *NetBuf); +void SetSelectForNetworkBuffer(NetworkBuffer *NetBuf,fd_set *readfds, + fd_set *writefds,fd_set *errorfds,int *MaxSock); +gboolean RespondToSelect(NetworkBuffer *NetBuf,fd_set *readfds, + fd_set *writefds,fd_set *errorfds, + gboolean *DataWaiting); +gboolean PlayerHandleNetwork(Player *Play,gboolean ReadReady, + gboolean WriteReady,gboolean *DataWaiting); +gboolean ReadPlayerDataFromWire(Player *Play); +void QueuePlayerMessageForSend(Player *Play,gchar *data); +gboolean WritePlayerDataToWire(Player *Play); +gchar *GetWaitingPlayerMessage(Player *Play); + +gboolean ReadDataFromWire(NetworkBuffer *NetBuf); +gboolean WriteDataToWire(NetworkBuffer *NetBuf); +void QueueMessageForSend(NetworkBuffer *NetBuf,gchar *data); +gchar *GetWaitingMessage(NetworkBuffer *NetBuf); + gchar *bgets(int fd); #endif /* NETWORKING */ (DIR) diff --git a/src/serverside.c b/src/serverside.c t@@ -195,7 +195,7 @@ void RegisterWithMetaServer(char Up,char SendData) { void HandleServerPlayer(Player *Play) { gchar *buf; gboolean MessageRead=FALSE; - while ((buf=ReadFromConnectionBuffer(Play))!=NULL) { + while ((buf=GetWaitingPlayerMessage(Play))!=NULL) { MessageRead=TRUE; HandleServerMessage(buf,Play); g_free(buf); t@@ -772,7 +772,7 @@ void ServerLoop() { struct timeval timeout; int MinTimeout; GString *LineBuf; - gboolean EndOfLine; + gboolean EndOfLine,DataWaiting; StartServer(); t@@ -787,11 +787,9 @@ void ServerLoop() { topsock=ListenSock+1; for (list=FirstServer;list;list=g_slist_next(list)) { tmp=(Player *)list->data; - if (!IsCop(tmp) && tmp->fd>0) { - FD_SET(tmp->fd,&readfs); - if (tmp->WriteBuf.DataPresent) FD_SET(tmp->fd,&writefs); - FD_SET(tmp->fd,&errorfs); - if (tmp->fd>=topsock) topsock=tmp->fd+1; + if (!IsCop(tmp)) { + SetSelectForNetworkBuffer(&tmp->NetBuf,&readfs,&writefs, + &errorfs,&topsock); } } MinTimeout=GetMinimumTimeout(FirstServer); t@@ -831,28 +829,14 @@ void ServerLoop() { while (list) { nextlist=g_slist_next(list); tmp=(Player *)list->data; - if (tmp && FD_ISSET(tmp->fd,&errorfs)) { - g_warning("socket error from client: %d",tmp->fd); - CleanUpServer(); bgetch(); break; - } - if (tmp && FD_ISSET(tmp->fd,&writefs)) { -/* Try and empty the player's write buffer */ - if (!WriteConnectionBufferToWire(tmp)) { + if (tmp && !RespondToSelect(&tmp->NetBuf,&readfs,&writefs,&errorfs, + &DataWaiting)) { /* The socket has been shut down, or the buffer was filled - remove player */ - if (RemovePlayerFromServer(tmp,WantQuit)) break; - tmp=NULL; - } - } - if (tmp && FD_ISSET(tmp->fd,&readfs)) { -/* Read any waiting data into the player's read buffer */ - if (!ReadConnectionBufferFromWire(tmp)) { -/* remove player! */ - if (RemovePlayerFromServer(tmp,WantQuit)) break; - tmp=NULL; - } else { + if (RemovePlayerFromServer(tmp,WantQuit)) break; + tmp=NULL; + } else if (tmp && DataWaiting) { /* If any complete messages were read, process them */ - HandleServerPlayer(tmp); - } + HandleServerPlayer(tmp); } list=nextlist; } t@@ -933,31 +917,24 @@ static void GuiDoCommand(GtkWidget *widget,gpointer data) { static void GuiHandleSocket(gpointer data,gint socket, GdkInputCondition condition) { Player *Play; + gboolean DataWaiting; Play = (Player *)data; /* Sanity check - is the player still around? */ if (!g_slist_find(FirstServer,(gpointer)Play)) return; - if (condition&GDK_INPUT_WRITE) { - if (!WriteConnectionBufferToWire(Play)) { - if (RemovePlayerFromServer(Play,WantQuit)) GuiQuitServer(); - } else if (Play->WriteBuf.DataPresent==0) { - SetSocketWriteTest(Play,FALSE); - } - } - if (condition&GDK_INPUT_READ) { - if (!ReadConnectionBufferFromWire(Play)) { - if (RemovePlayerFromServer(Play,WantQuit)) GuiQuitServer(); - } else { - HandleServerPlayer(Play); - GuiSetTimeouts(); /* We may have set some new timeouts */ - } + if (!PlayerHandleNetwork(Play,condition&GDK_INPUT_READ, + condition&GDK_INPUT_WRITE,&DataWaiting)) { + if (RemovePlayerFromServer(Play,WantQuit)) GuiQuitServer(); + } else if (DataWaiting) { + HandleServerPlayer(Play); + GuiSetTimeouts(); /* We may have set some new timeouts */ } } void SetSocketWriteTest(Player *Play,gboolean WriteTest) { if (Play->InputTag) gdk_input_remove(Play->InputTag); - Play->InputTag=gdk_input_add(Play->fd, + Play->InputTag=gdk_input_add(Play->NetBuf.fd, GDK_INPUT_READ|(WriteTest ? GDK_INPUT_WRITE : 0), GuiHandleSocket,(gpointer)Play); }