tNon-blocking network handling abstracted out into NetworkBuffer datatype - vac… | |
git clone git://src.adamsgaard.dk/vaccinewars | |
Log | |
Files | |
Refs | |
README | |
LICENSE | |
--- | |
commit 7d0b10dce7f72780b488b3df2800347731565a73 | |
parent e694fa9cbc3214dbf38465474571e2d9209f7d23 | |
Author: Ben Webb <[email protected]> | |
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(-) | |
--- | |
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? | |
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(); | |
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); | |
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; | |
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 */ | |
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); | |
diff --git a/src/message.c b/src/message.c | |
t@@ -147,7 +147,7 @@ void DoSendClientMessage(Player *From,char AICode,char Cod… | |
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 | |
diff --git a/src/message.h b/src/message.h | |
t@@ -115,10 +115,26 @@ void SendPrintMessage(Player *From,char AICode,Player *T… | |
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 */ | |
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); | |
} |