tNew callback code for alerting clients to socket read/write-ready status - 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 ce32bee1e9e077dbfb72701a7ac3b1867871fcf3
 (DIR) parent a756e93c834008f86ef3bd1d41a7c943cea404e8
 (HTM) Author: Ben Webb <ben@salilab.org>
       Date:   Wed, 12 Sep 2001 18:04:06 +0000
       
       New callback code for alerting clients to socket read/write-ready status
       
       
       Diffstat:
         M src/curses_client.c                 |       1 -
         M src/dopewars.h                      |      12 +++++++++---
         M src/gtk_client.c                    |     132 ++++++++++++++++++-------------
         M src/message.c                       |     109 +++++++++++++++++++++----------
         M src/message.h                       |       3 ++-
         M src/serverside.c                    |      57 ++++++++++++++-----------------
       
       6 files changed, 191 insertions(+), 123 deletions(-)
       ---
 (DIR) diff --git a/src/curses_client.c b/src/curses_client.c
       t@@ -1849,7 +1849,6 @@ void CursesLoop(void) {
        
        /* Set up message handlers */
           ClientMessageHandlerPt = HandleClientMessage;
       -   SocketWriteTestPt = NULL;
        
        /* Make the GLib log messages display nicely */
           g_log_set_handler(NULL,LogMask()|G_LOG_LEVEL_MESSAGE | G_LOG_LEVEL_WARNING,
 (DIR) diff --git a/src/dopewars.h b/src/dopewars.h
       t@@ -259,16 +259,23 @@ typedef struct tagConnBuf {
           int DataPresent; /* number of bytes currently in "Data"   */
        } ConnBuf;            
        
       +typedef struct _NetworkBuffer NetworkBuffer;
       +
       +typedef void (*NBCallBack)(NetworkBuffer *NetBuf,gboolean Read,gboolean Write);
       +
        /* Handles reading and writing messages from/to a network connection */
       -typedef struct tagNetworkBuffer {
       +struct _NetworkBuffer {
           int fd;                /* File descriptor of the socket */
           gint InputTag;         /* Identifier for gdk_input routines */
       +   NBCallBack CallBack;   /* Function called when the socket read- or
       +                             write-able status changes */
       +   gpointer CallBackData; /* Data accessible to the callback function */
           char Terminator;       /* Character that separates messages */
           char StripChar;        /* Character that should be removed from messages */
           ConnBuf ReadBuf;       /* New data, waiting for the application */
           ConnBuf WriteBuf;      /* Data waiting to be written to the wire */
           gboolean WaitConnect;  /* TRUE if a non-blocking connect is in progress */
       -} NetworkBuffer;
       +};
        
        struct PLAYER_T {
           guint ID;
       t@@ -287,7 +294,6 @@ struct PLAYER_T {
           Player *OnBehalfOf;
           NetworkBuffer NetBuf;
           Abilities Abil;
       -   gint InputTag;
           GPtrArray *FightArray; /* If non-NULL, a list of players in a fight */
           Player *Attacking;     /* The player that this player is attacking */
           gint CopIndex;  /* if >0,  then this player is a cop, described
 (DIR) diff --git a/src/gtk_client.c b/src/gtk_client.c
       t@@ -68,7 +68,6 @@ struct ClientDataStruct {
           struct InventoryWidgets Drug,Gun,InvenDrug,InvenGun;
           GtkWidget *JetButton,*vbox,*PlayerList,*TalkList;
           guint JetAccel;
       -   gint GdkInputTag;
        };
        
        static struct ClientDataStruct ClientData;
       t@@ -96,7 +95,9 @@ static void UpdateMenus(void);
        #ifdef NETWORKING
        static void GetClientMessage(gpointer data,gint socket,
                                     GdkInputCondition condition);
       -static void SetSocketWriteTest(Player *Play,gboolean WriteTest);
       +static void SocketStatus(NetworkBuffer *NetBuf,gboolean Read,gboolean Write);
       +static void MetaSocketStatus(NetworkBuffer *NetBuf,gboolean Read,
       +                             gboolean Write);
        #endif
        
        static void HandleClientMessage(char *buf,Player *Play);
       t@@ -276,7 +277,6 @@ void GetClientMessage(gpointer data,gint socket,
              }
           }
           if (!DoneOK) {
       -      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 "
       t@@ -287,12 +287,14 @@ 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->NetBuf.fd,
       -                             GDK_INPUT_READ|(WriteTest ? GDK_INPUT_WRITE : 0),
       -                             GetClientMessage,NULL);
       +void SocketStatus(NetworkBuffer *NetBuf,gboolean Read,gboolean Write) {
       +   if (NetBuf->InputTag) gdk_input_remove(NetBuf->InputTag);
       +   NetBuf->InputTag=0;
       +   if (Read || Write) {
       +      NetBuf->InputTag=gdk_input_add(NetBuf->fd,
       +                                     (Read ? GDK_INPUT_READ : 0) |
       +                                     (Write ? GDK_INPUT_WRITE : 0),
       +                                     GetClientMessage,NULL);
           }
        }
        #endif /* NETWORKING */
       t@@ -327,15 +329,15 @@ void HandleClientMessage(char *pt,Player *Play) {
              case C_FIGHTPRINT:
                 DisplayFightMessage(Data); break;
              case C_PUSH:
       -         if (Network) gdk_input_remove(ClientData.GdkInputTag);
        /* The server admin has asked us to leave - so warn the user, and do so */
       +         ShutdownNetworkBuffer(&Play->NetBuf);
                 g_warning(_("You have been pushed from the server."));
                 SwitchToSinglePlayer(Play);
                 UpdateMenus();
                 break;
              case C_QUIT:
       -         if (Network) gdk_input_remove(ClientData.GdkInputTag);
        /* The server has sent us notice that it is shutting down */
       +         ShutdownNetworkBuffer(&Play->NetBuf);
                 g_warning(_("The server has terminated."));
                 SwitchToSinglePlayer(Play);
                 UpdateMenus();
       t@@ -1544,7 +1546,10 @@ void StartGame(void) {
           Play=ClientData.Play=g_new(Player,1);
           FirstClient=AddPlayer(0,Play,FirstClient);
        #ifdef NETWORKING
       -   BindNetworkBufferToSocket(&Play->NetBuf,ClientSock);
       +   if (Network) {
       +      BindNetworkBufferToSocket(&Play->NetBuf,ClientSock);
       +      SetNetworkBufferCallBack(&Play->NetBuf,SocketStatus,NULL);
       +   }
        #endif
           InitAbilities(Play);
           SendAbilities(Play);
       t@@ -1552,9 +1557,6 @@ void StartGame(void) {
           SendNullClientMessage(Play,C_NONE,C_NAME,NULL,ClientData.PlayerName);
           InGame=TRUE;
           UpdateMenus();
       -#ifdef NETWORKING
       -   if (Network) SetSocketWriteTest(Play,TRUE);
       -#endif
           gtk_widget_show_all(ClientData.vbox);
           UpdatePlayerLists();
        }
       t@@ -1565,7 +1567,6 @@ void EndGame(void) {
           gtk_editable_delete_text(GTK_EDITABLE(ClientData.messages),0,-1);
           g_free(ClientData.PlayerName);
           ClientData.PlayerName=g_strdup(GetPlayerName(ClientData.Play));
       -   if (Network) gdk_input_remove(ClientData.GdkInputTag);
           ShutdownNetwork();
           UpdatePlayerLists();
           CleanUpServer();
       t@@ -1735,10 +1736,6 @@ char GtkLoop(int *argc,char **argv[],gboolean ReturnOnFail) {
        
        /* Set up message handlers */
           ClientMessageHandlerPt = HandleClientMessage;
       -   ClientData.GdkInputTag=0;
       -#ifdef NETWORKING
       -   SocketWriteTestPt = SetSocketWriteTest;
       -#endif
        
        /* Have the GLib log messages pop up in a nice dialog box */
           g_log_set_handler(NULL,LogMask()|G_LOG_LEVEL_MESSAGE | G_LOG_LEVEL_WARNING,
       t@@ -1905,10 +1902,25 @@ _("\nFor information on the command line options, type dopewars -h at your\n"
        
        struct StartGameStruct {
           GtkWidget *dialog,*name,*hostname,*port,*antique,*status,*metaserv;
       -   gint ConnectTag,MetaTag;
       +#ifdef NETWORKING
       +   gint ConnectTag;
           HttpConnection *MetaConn;
       +#endif
        };
        
       +static gboolean GetStartGamePlayerName(struct StartGameStruct *widgets,
       +                                       gchar **PlayerName) {
       +   g_free(*PlayerName);
       +   *PlayerName = gtk_editable_get_chars(GTK_EDITABLE(widgets->name),0,-1);
       +   if (*PlayerName && (*PlayerName)[0]) return TRUE;
       +   else {
       +      GtkMessageBox(widgets->dialog,
       +                    _("You can't start the game without giving a name first!"),
       +                    _("New Game"),MB_OK);
       +      return FALSE;
       +   }
       +}
       +
        #ifdef NETWORKING
        static void FinishServerConnect(gpointer data,gint socket,
                                        GdkInputCondition condition) {
       t@@ -1954,15 +1966,13 @@ static void DoConnect(struct StartGameStruct *widgets) {
        
        static void ConnectToServer(GtkWidget *widget,struct StartGameStruct *widgets) {
           gchar *text;
       +
           g_free(ServerName);
           ServerName=gtk_editable_get_chars(GTK_EDITABLE(widgets->hostname),0,-1);
           text=gtk_editable_get_chars(GTK_EDITABLE(widgets->port),0,-1);
       -   Port=atoi(text);
       -   g_free(text);
       -   g_free(ClientData.PlayerName);
       -   ClientData.PlayerName=gtk_editable_get_chars(GTK_EDITABLE(widgets->name),
       -                                                0,-1);
       -   if (!ClientData.PlayerName || !ClientData.PlayerName[0]) return;
       +   Port=atoi(text); g_free(text);
       +
       +   if (!GetStartGamePlayerName(widgets,&ClientData.PlayerName)) return;
           DoConnect(widgets);
        }
        
       t@@ -2015,27 +2025,29 @@ static void HandleMetaSock(gpointer data,gint socket,
           }
           if (!DoneOK) {
              g_print("Metaserver communicated closed\n");
       -      gdk_input_remove(widgets->MetaTag);
              CloseHttpConnection(widgets->MetaConn);
       -      widgets->MetaTag=0; widgets->MetaConn=NULL;
       +      widgets->MetaConn=NULL;
              FillMetaServerList(widgets);
       -   } else if (condition&GDK_INPUT_WRITE &&
       -              !widgets->MetaConn->NetBuf.WriteBuf.DataPresent) {
       -/* If we've written out everything, no need to test for write-ready any more */
       -      gdk_input_remove(widgets->MetaTag);
       -      widgets->MetaTag=gdk_input_add(widgets->MetaConn->NetBuf.fd,
       -                                     GDK_INPUT_READ,
       -                                     HandleMetaSock,(gpointer)widgets);
       +   }
       +}
       +
       +void MetaSocketStatus(NetworkBuffer *NetBuf,gboolean Read,gboolean Write) {
       +   if (NetBuf->InputTag) gdk_input_remove(NetBuf->InputTag);
       +   NetBuf->InputTag=0;
       +   if (Read || Write) {
       +      NetBuf->InputTag=gdk_input_add(NetBuf->fd,
       +                                     (Read ? GDK_INPUT_READ : 0) |
       +                                     (Write ? GDK_INPUT_WRITE : 0),
       +                                     HandleMetaSock,NetBuf->CallBackData);
           }
        }
        
        static void UpdateMetaServerList(GtkWidget *widget,
                                         struct StartGameStruct *widgets) {
           GtkWidget *metaserv;
       -   if (widgets->MetaTag) {
       -      gdk_input_remove(widgets->MetaTag);
       +   if (widgets->MetaConn) {
              CloseHttpConnection(widgets->MetaConn);
       -      widgets->MetaTag=0; widgets->MetaConn=NULL;
       +      widgets->MetaConn=NULL;
           }
        
           widgets->MetaConn = OpenMetaHttpConnection();
       t@@ -2044,9 +2056,8 @@ static void UpdateMetaServerList(GtkWidget *widget,
              metaserv=widgets->metaserv;
              gtk_clist_clear(GTK_CLIST(metaserv));
              ClearServerList();
       -      widgets->MetaTag = gdk_input_add(widgets->MetaConn->NetBuf.fd,
       -                                       GDK_INPUT_READ|GDK_INPUT_WRITE,
       -                                       HandleMetaSock,(gpointer)widgets);
       +      SetNetworkBufferCallBack(&widgets->MetaConn->NetBuf,
       +                               MetaSocketStatus,(gpointer)widgets);
           }
        }
        
       t@@ -2064,10 +2075,8 @@ static void MetaServerConnect(GtkWidget *widget,
              ThisServer=(ServerData *)gtk_clist_get_row_data(GTK_CLIST(clist),row);
              AssignName(&ServerName,ThisServer->Name);
              Port=ThisServer->Port;
       -      g_free(ClientData.PlayerName);
       -      ClientData.PlayerName=gtk_editable_get_chars(GTK_EDITABLE(widgets->name),
       -                                                   0,-1);
       -      if (!ClientData.PlayerName || !ClientData.PlayerName[0]) return;
       +
       +      if (!GetStartGamePlayerName(widgets,&ClientData.PlayerName)) return;
              DoConnect(widgets);
           }
        }
       t@@ -2077,29 +2086,40 @@ static void StartSinglePlayer(GtkWidget *widget,
                                      struct StartGameStruct *widgets) {
           WantAntique=
                  gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widgets->antique));
       -   g_free(ClientData.PlayerName);
       -   ClientData.PlayerName=gtk_editable_get_chars(GTK_EDITABLE(widgets->name),
       -                                                0,-1);
       +   if (!GetStartGamePlayerName(widgets,&ClientData.PlayerName)) return;
           StartGame();
           gtk_widget_destroy(widgets->dialog);
        }
        
        static void CloseNewGameDia(GtkWidget *widget,
                                    struct StartGameStruct *widgets) {
       +   g_log_set_handler(NULL,LogMask()|G_LOG_LEVEL_MESSAGE|G_LOG_LEVEL_WARNING,
       +                     LogMessage,NULL);
        #ifdef NETWORKING
           if (widgets->ConnectTag) {
              gdk_input_remove(widgets->ConnectTag);
              CloseSocket(ClientSock);
              widgets->ConnectTag=0;
           }
       -   if (widgets->MetaTag) {
       -      gdk_input_remove(widgets->MetaTag);
       +   if (widgets->MetaConn) {
              CloseHttpConnection(widgets->MetaConn);
       -      widgets->MetaTag=0; widgets->MetaConn=NULL;
       +      widgets->MetaConn=NULL;
           }
        #endif
        }
        
       +static void NewGameLogMessage(const gchar *log_domain,GLogLevelFlags log_level,
       +                              const gchar *message,gpointer user_data) {
       +   struct StartGameStruct *widgets;
       +   gchar *text;
       +
       +   widgets = (struct StartGameStruct *)user_data;
       +
       +   text=g_strdup_printf(_("Status: %s"),message);
       +   gtk_label_set_text(GTK_LABEL(widgets->status),text);
       +   g_free(text);
       +}
       +
        void NewGameDialog(void) {
           GtkWidget *vbox,*vbox2,*hbox,*label,*entry,*notebook,*frame,*button;
           GtkWidget *dialog;
       t@@ -2117,10 +2137,11 @@ void NewGameDialog(void) {
           server_titles[2]=_("Version");
           server_titles[3]=_("Players");
           server_titles[4]=_("Comment");
       -#endif /* NETWORKING */
        
           widgets.ConnectTag=0;
       -   widgets.MetaTag=0; widgets.MetaConn=NULL;
       +   widgets.MetaConn=NULL;
       +
       +#endif /* NETWORKING */
        
           widgets.dialog=dialog=gtk_window_new(GTK_WINDOW_DIALOG);
           gtk_signal_connect(GTK_OBJECT(dialog),"destroy",
       t@@ -2293,6 +2314,9 @@ void NewGameDialog(void) {
           label=widgets.status=gtk_label_new(_("Status: Waiting for user input"));
           gtk_box_pack_start(GTK_BOX(vbox),label,FALSE,FALSE,0);
        
       +   g_log_set_handler(NULL,LogMask()|G_LOG_LEVEL_MESSAGE|G_LOG_LEVEL_WARNING,
       +                     NewGameLogMessage,(gpointer)&widgets);
       +
           gtk_container_add(GTK_CONTAINER(widgets.dialog),vbox);
        
           gtk_widget_grab_focus(widgets.name);
 (DIR) diff --git a/src/message.c b/src/message.c
       t@@ -97,7 +97,6 @@
        GSList *FirstClient;
        
        void (*ClientMessageHandlerPt) (char *,Player *) = NULL;
       -void (*SocketWriteTestPt) (Player *,gboolean) = NULL;
        
        void SendClientMessage(Player *From,AICode AI,MsgCode Code,
                               Player *To,char *Data) {
       t@@ -148,7 +147,6 @@ void DoSendClientMessage(Player *From,AICode AI,MsgCode Code,
        #if NETWORKING
           } else {
              QueuePlayerMessageForSend(BufOwn,text->str);
       -      if (SocketWriteTestPt) (*SocketWriteTestPt)(BufOwn,TRUE);
           }
        #endif /* NETWORKING */
           g_string_free(text,TRUE);
       t@@ -192,7 +190,6 @@ void SendServerMessage(Player *From,AICode AI,MsgCode Code,
        #if NETWORKING
           } else {
              QueuePlayerMessageForSend(To,text->str);
       -      if (SocketWriteTestPt) (*SocketWriteTestPt)(To,TRUE);
           }
        #endif
           g_string_free(text,TRUE);
       t@@ -277,12 +274,26 @@ gboolean HaveAbility(Player *Play,gint Type) {
        }
        
        #if NETWORKING
       +static void NetBufCallBack(NetworkBuffer *NetBuf) {
       +   if (NetBuf && NetBuf->CallBack) {
       +      (*NetBuf->CallBack)(NetBuf,!NetBuf->WaitConnect,
       +                          NetBuf->WriteBuf.DataPresent || NetBuf->WaitConnect);
       +   }
       +}
       +
       +static void NetBufCallBackStop(NetworkBuffer *NetBuf) {
       +   if (NetBuf && NetBuf->CallBack) (*NetBuf->CallBack)(NetBuf,FALSE,FALSE);
       +}
       +
        void InitNetworkBuffer(NetworkBuffer *NetBuf,char Terminator,char StripChar) {
        /* Initialises the passed network buffer, ready for use. Messages sent */
        /* or received on the buffered connection will be terminated by the    */
        /* given character, and if they end in "StripChar" it will be removed  */
        /* before the messages are sent or received.                           */
           NetBuf->fd=-1;
       +   NetBuf->InputTag=0;
       +   NetBuf->CallBack=NULL;
       +   NetBuf->CallBackData=NULL;
           NetBuf->Terminator=Terminator;
           NetBuf->StripChar=StripChar;
           NetBuf->ReadBuf.Data=NetBuf->WriteBuf.Data=NULL;
       t@@ -291,6 +302,14 @@ void InitNetworkBuffer(NetworkBuffer *NetBuf,char Terminator,char StripChar) {
           NetBuf->WaitConnect=FALSE;
        }
        
       +void SetNetworkBufferCallBack(NetworkBuffer *NetBuf,NBCallBack CallBack,
       +                              gpointer CallBackData) {
       +   NetBufCallBackStop(NetBuf);
       +   NetBuf->CallBack=CallBack;
       +   NetBuf->CallBackData=CallBackData;
       +   NetBufCallBack(NetBuf);
       +}
       +
        void BindNetworkBufferToSocket(NetworkBuffer *NetBuf,int fd) {
        /* Sets up the given network buffer to handle data being sent/received */
        /* through the given socket                                            */
       t@@ -318,6 +337,10 @@ gboolean StartNetworkBufferConnect(NetworkBuffer *NetBuf,gchar *RemoteHost,
              ConnectError(retval); return FALSE;
           } else {
              NetBuf->WaitConnect=TRUE;
       +
       +/* Notify the owner if necessary to check for the connection completing */
       +      NetBufCallBack(NetBuf);
       +
              return TRUE;
           }
        }
       t@@ -325,10 +348,14 @@ gboolean StartNetworkBufferConnect(NetworkBuffer *NetBuf,gchar *RemoteHost,
        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);
       +
       +   NetBufCallBackStop(NetBuf);
       +
       +   if (NetBuf->fd>=0) CloseSocket(NetBuf->fd);
        
           g_free(NetBuf->ReadBuf.Data);
           g_free(NetBuf->WriteBuf.Data);
       +
           InitNetworkBuffer(NetBuf,NetBuf->Terminator,NetBuf->StripChar);
        }
        
       t@@ -355,29 +382,47 @@ static gboolean DoNetworkBufferStuff(NetworkBuffer *NetBuf,gboolean ReadReady,
        /* 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;
       +   gboolean DataWaiting=FALSE,ConnectDone=FALSE;
           gchar *retval;
           *ReadOK=*WriteOK=*ErrorOK=TRUE;
        
       -   if (NetBuf->WaitConnect) {
       +   if (ErrorReady) *ErrorOK=FALSE;
       +   else if (NetBuf->WaitConnect) {
              if (WriteReady) {
                 retval=FinishConnect(NetBuf->fd);
       +         ConnectDone=TRUE;
       +         NetBuf->WaitConnect=FALSE;
       +
                 if (retval) {
                    *WriteOK=FALSE;
                    ConnectError(retval);
       -         } else NetBuf->WaitConnect=FALSE;
       +         }
              }
       -      return FALSE;
       -   }
       -
       -   if (ErrorReady) *ErrorOK=FALSE;
       +   } else {
       +      if (WriteReady) *WriteOK=WriteDataToWire(NetBuf);
        
       -   if (WriteReady) *WriteOK=WriteDataToWire(NetBuf);
       +      if (ReadReady) {
       +         *ReadOK=ReadDataFromWire(NetBuf);
       +         if (NetBuf->ReadBuf.DataPresent>0) DataWaiting=TRUE;
       +      }
       +   }
        
       -   if (ReadReady) {
       -      *ReadOK=ReadDataFromWire(NetBuf);
       -      if (NetBuf->ReadBuf.DataPresent>0) DataWaiting=TRUE;
       +   if (!(*ErrorOK && *WriteOK && *ReadOK)) {
       +/* We don't want to check the socket any more */
       +      NetBufCallBackStop(NetBuf);
       +/* If there were errors, then the socket is now useless - so close it */
       +      CloseSocket(NetBuf->fd);
       +      NetBuf->fd=-1;
       +   } else if (ConnectDone) {
       +/* If we just connected, then no need to listen for write-ready status
       +   any more */
       +      NetBufCallBack(NetBuf);
       +   } else if (WriteReady && NetBuf->WriteBuf.DataPresent==0) {
       +/* If we wrote out everything, then tell the owner so that the socket no
       +   longer needs to be checked for write-ready status */
       +      NetBufCallBack(NetBuf);
           }
       +
           return DataWaiting;
        }
        
       t@@ -426,13 +471,6 @@ gboolean PlayerHandleNetwork(Player *Play,gboolean ReadReady,
           if (!Play) return DataWaiting;
           DataWaiting=NetBufHandleNetwork(&Play->NetBuf,ReadReady,WriteReady,DoneOK);
        
       -/* 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 DataWaiting;
        }
        
       t@@ -547,6 +585,10 @@ void QueueMessageForSend(NetworkBuffer *NetBuf,gchar *data) {
           memcpy(&conn->Data[conn->DataPresent],data,AddLength);
           conn->DataPresent=NewLength;
           conn->Data[NewLength-1]=NetBuf->Terminator;
       +
       +/* If the buffer was empty before, we may need to tell the owner to check
       +   the socket for write-ready status */
       +   if (NewLength==AddLength) NetBufCallBack(NetBuf);
        }
        
        gboolean WritePlayerDataToWire(Player *Play) {
       t@@ -630,7 +672,9 @@ HttpConnection *OpenHttpConnection(gchar *HostName,unsigned Port,
           g_string_sprintf(text,"User-Agent: dopewars/%s",VERSION);
           QueueMessageForSend(&conn->NetBuf,text->str);
        
       -   QueueMessageForSend(&conn->NetBuf,"\n");
       +/* Insert a blank line between headers and body */
       +   QueueMessageForSend(&conn->NetBuf,"");
       +
           if (conn->Body) QueueMessageForSend(&conn->NetBuf,conn->Body);
        
           g_string_free(text,TRUE);
       t@@ -1091,7 +1135,8 @@ char *StartConnect(int *fd,gchar *RemoteHost,unsigned RemotePort,
           }
           *fd=socket(AF_INET,SOCK_STREAM,0);
           if (*fd==SOCKET_ERROR) {
       -      return NoSocket;
       +      return strerror(errno);
       +/*    return NoSocket;*/
           }
        
           ClientAddr.sin_family=AF_INET;
       t@@ -1108,7 +1153,8 @@ char *StartConnect(int *fd,gchar *RemoteHost,unsigned RemotePort,
              if (GetSocketError()==EINPROGRESS) return NULL;
        #endif
              CloseSocket(*fd); *fd=-1;
       -      return NoConnect;
       +      return strerror(errno);
       +/*    return NoConnect;*/
           } else {
              fcntl(*fd,F_SETFL,O_NONBLOCK);
           }
       t@@ -1130,10 +1176,12 @@ char *FinishConnect(int fd) {
        
           optlen=sizeof(optval);
           if (getsockopt(fd,SOL_SOCKET,SO_ERROR,&optval,&optlen)==-1) {
       -      return NoConnect;
       +      return strerror(errno);
       +/*    return NoConnect;*/
           }
           if (optval==0) return NULL;
       -   else return NoConnect;
       +   else return strerror(optval);
       +/* else return NoConnect;*/
        #endif /* CYGWIN */
        }
        
       t@@ -1178,7 +1226,7 @@ void SwitchToSinglePlayer(Player *Play) {
                                       FirstClient);
           }
        #ifdef NETWORKING
       -   CloseSocket(ClientSock);
       +   ShutdownNetworkBuffer(&Play->NetBuf);
        #endif
           CleanUpServer();
           Network=Server=Client=FALSE;
       t@@ -1197,11 +1245,6 @@ void ShutdownNetwork() {
           while (FirstClient) {
              FirstClient=RemovePlayer((Player *)FirstClient->data,FirstClient);
           }
       -#if NETWORKING
       -   if (Client) {
       -      CloseSocket(ClientSock);
       -   }
       -#endif /* NETWORKING */
           Client=Network=Server=FALSE;
        }
        
 (DIR) diff --git a/src/message.h b/src/message.h
       t@@ -98,6 +98,8 @@ char *StartConnect(int *fd,gchar *RemoteHost,unsigned RemotePort,
        char *FinishConnect(int fd);
        
        void InitNetworkBuffer(NetworkBuffer *NetBuf,char Terminator,char StripChar);
       +void SetNetworkBufferCallBack(NetworkBuffer *NetBuf,NBCallBack CallBack,
       +                              gpointer CallBackData);
        gboolean IsNetworkBufferActive(NetworkBuffer *NetBuf);
        void BindNetworkBufferToSocket(NetworkBuffer *NetBuf,int fd);
        gboolean StartNetworkBufferConnect(NetworkBuffer *NetBuf,gchar *RemoteHost,
       t@@ -138,7 +140,6 @@ gchar *bgets(int fd);
        extern GSList *FirstClient;
        
        extern void (*ClientMessageHandlerPt) (char *,Player *);
       -extern void (*SocketWriteTestPt) (Player *,gboolean);
        
        void AddURLEnc(GString *str,gchar *unenc);
        void chomp(char *str);
 (DIR) diff --git a/src/serverside.c b/src/serverside.c
       t@@ -90,11 +90,10 @@ GSList *FirstServer=NULL;
        
        #ifdef NETWORKING
        static GScanner *Scanner;
       -#endif
        
        /* Data waiting to be sent to/read from the metaserver */
        HttpConnection *MetaConn=NULL;
       -gint MetaInputTag=0;
       +#endif
        
        /* Handle to the high score file */
        static FILE *ScoreFP=NULL;
       t@@ -132,6 +131,8 @@ static int OfferObject(Player *To,gboolean ForceBitch);
        #ifdef GUI_SERVER
        static void GuiHandleMeta(gpointer data,gint socket,
                                  GdkInputCondition condition);
       +static void MetaSocketStatus(NetworkBuffer *NetBuf,
       +                             gboolean Read,gboolean Write);
        #endif
        
        void RegisterWithMetaServer(gboolean Up,gboolean SendData,
       t@@ -161,10 +162,6 @@ void RegisterWithMetaServer(gboolean Up,gboolean SendData,
        /* If the previous connect hung for so long that it's still active, then
           break the connection before we start a new one */
           if (MetaConn) CloseHttpConnection(MetaConn);
       -#ifdef GUI_SERVER
       -   if (MetaInputTag) gdk_input_remove(MetaInputTag);
       -   MetaInputTag=0;
       -#endif
        
           headers=g_string_new("");
           body=g_string_new("");
       t@@ -208,7 +205,6 @@ void RegisterWithMetaServer(gboolean Up,gboolean SendData,
           MetaConn=OpenHttpConnection(MetaServer.Name,MetaServer.Port,
                                       MetaServer.ProxyName,MetaServer.ProxyPort,
                                       "POST",MetaServer.Path,headers->str,body->str);
       -   g_print("Sending headers %s and body %s\n",headers->str,body->str);
           g_string_free(headers,TRUE);
           g_string_free(body,TRUE);
        
       t@@ -217,9 +213,7 @@ void RegisterWithMetaServer(gboolean Up,gboolean SendData,
                      MetaServer.Name,MetaServer.Port);
           } else return;
        #ifdef GUI_SERVER
       -   MetaInputTag=gdk_input_add(MetaConn->NetBuf.fd,
       -                              GDK_INPUT_READ|GDK_INPUT_WRITE,
       -                              GuiHandleMeta,NULL);
       +   SetNetworkBufferCallBack(&MetaConn->NetBuf,MetaSocketStatus,NULL);
        #endif
           MetaPlayerPending=FALSE;
        
       t@@ -653,7 +647,6 @@ void StartServer() {
        
           Network=TRUE;
           FirstServer=NULL;
       -   SocketWriteTestPt=NULL;
           ClientMessageHandlerPt=NULL;
           ListenSock=socket(AF_INET,SOCK_STREAM,0);
           if (ListenSock==SOCKET_ERROR) {
       t@@ -800,9 +793,6 @@ void StopServer() {
        }
        
        void RemovePlayerFromServer(Player *Play) {
       -#ifdef GUI_SERVER
       -   if (Play->InputTag) gdk_input_remove(Play->InputTag);
       -#endif
           if (!WantQuit && strlen(GetPlayerName(Play))>0) {
              dopelog(2,_("%s leaves the server!"),GetPlayerName(Play));
              ClientLeftServer(Play);
       t@@ -935,7 +925,7 @@ void ServerLoop() {
        #ifdef GUI_SERVER
        static GtkWidget *TextOutput;
        static gint ListenTag=0;
       -static void SetSocketWriteTest(Player *Play,gboolean WriteTest);
       +static void SocketStatus(NetworkBuffer *NetBuf,gboolean Read,gboolean Write);
        static void GuiSetTimeouts(void);
        static time_t NextTimeout=0;
        static guint TimeoutTag=0;
       t@@ -1021,15 +1011,7 @@ void GuiHandleMeta(gpointer data,gint socket,GdkInputCondition condition) {
           if (!DoneOK) {
              dopelog(4,"MetaServer: (closed)\n");
              CloseHttpConnection(MetaConn); MetaConn=NULL;
       -      gdk_input_remove(MetaInputTag);
       -      MetaInputTag=0;
              if (IsServerShutdown()) GuiQuitServer();
       -   } else if (condition&GDK_INPUT_WRITE &&
       -              !MetaConn->NetBuf.WriteBuf.DataPresent) {
       -/* If we've written out everything, no need to test for write-ready any more */
       -      gdk_input_remove(MetaInputTag);
       -      MetaInputTag=gdk_input_add(MetaConn->NetBuf.fd,
       -                                 GDK_INPUT_READ,GuiHandleMeta,NULL);
           }
        }
        
       t@@ -1053,11 +1035,26 @@ static void GuiHandleSocket(gpointer data,gint socket,
           }
        }
        
       -void SetSocketWriteTest(Player *Play,gboolean WriteTest) {
       -   if (Play->InputTag) gdk_input_remove(Play->InputTag);
       -   Play->InputTag=gdk_input_add(Play->NetBuf.fd,
       -                      GDK_INPUT_READ|(WriteTest ? GDK_INPUT_WRITE : 0),
       -                      GuiHandleSocket,(gpointer)Play);
       +void SocketStatus(NetworkBuffer *NetBuf,gboolean Read,gboolean Write) {
       +   if (NetBuf->InputTag) gdk_input_remove(NetBuf->InputTag);
       +   NetBuf->InputTag=0;
       +   if (Read || Write) {
       +      NetBuf->InputTag=gdk_input_add(NetBuf->fd,
       +                                     (Read ? GDK_INPUT_READ : 0) |
       +                                     (Write ? GDK_INPUT_WRITE : 0),
       +                                     GuiHandleSocket,NetBuf->CallBackData);
       +   }
       +}
       +
       +void MetaSocketStatus(NetworkBuffer *NetBuf,gboolean Read,gboolean Write) {
       +   if (NetBuf->InputTag) gdk_input_remove(NetBuf->InputTag);
       +   NetBuf->InputTag=0;
       +   if (Read || Write) {
       +      NetBuf->InputTag=gdk_input_add(NetBuf->fd,
       +                                     (Read ? GDK_INPUT_READ : 0) |
       +                                     (Write ? GDK_INPUT_WRITE : 0),
       +                                     GuiHandleMeta,NetBuf->CallBackData);
       +   }
        }
        
        static void GuiNewConnect(gpointer data,gint socket,
       t@@ -1065,8 +1062,7 @@ static void GuiNewConnect(gpointer data,gint socket,
           Player *Play;
           if (condition&GDK_INPUT_READ) {
              Play=HandleNewConnection();
       -      Play->InputTag=0;
       -      SetSocketWriteTest(Play,TRUE);
       +      SetNetworkBufferCallBack(&Play->NetBuf,SocketStatus,(gpointer)Play);
           }
        }
        
       t@@ -1121,7 +1117,6 @@ void GuiServerLoop() {
                             GuiServerLogMessage,NULL);
           StartServer();
        
       -   SocketWriteTestPt = SetSocketWriteTest;
           ListenTag=gdk_input_add(ListenSock,GDK_INPUT_READ,GuiNewConnect,NULL);
           gtk_main();
        }