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);
}