aboutsummaryrefslogtreecommitdiff
path: root/source/server.cpp
diff options
context:
space:
mode:
authorKitty Draper2011-03-05 21:39:25 -0500
committerKitty Draper2011-03-05 21:39:25 -0500
commitd40ae99422e118188a7f48055dc340c6aca022aa (patch)
tree83ab93f49fd9e66e43bcd824091ae1dbcaa0c173 /source/server.cpp
downloadsnes9x2005-d40ae99422e118188a7f48055dc340c6aca022aa.tar.gz
snes9x2005-d40ae99422e118188a7f48055dc340c6aca022aa.tar.bz2
snes9x2005-d40ae99422e118188a7f48055dc340c6aca022aa.zip
first commit
Diffstat (limited to 'source/server.cpp')
-rw-r--r--source/server.cpp1303
1 files changed, 1303 insertions, 0 deletions
diff --git a/source/server.cpp b/source/server.cpp
new file mode 100644
index 0000000..4eb384e
--- /dev/null
+++ b/source/server.cpp
@@ -0,0 +1,1303 @@
+/*******************************************************************************
+ Snes9x - Portable Super Nintendo Entertainment System (TM) emulator.
+
+ (c) Copyright 1996 - 2002 Gary Henderson (gary.henderson@ntlworld.com) and
+ Jerremy Koot (jkoot@snes9x.com)
+
+ (c) Copyright 2001 - 2004 John Weidman (jweidman@slip.net)
+
+ (c) Copyright 2002 - 2004 Brad Jorsch (anomie@users.sourceforge.net),
+ funkyass (funkyass@spam.shaw.ca),
+ Joel Yliluoma (http://iki.fi/bisqwit/)
+ Kris Bleakley (codeviolation@hotmail.com),
+ Matthew Kendora,
+ Nach (n-a-c-h@users.sourceforge.net),
+ Peter Bortas (peter@bortas.org) and
+ zones (kasumitokoduck@yahoo.com)
+
+ C4 x86 assembler and some C emulation code
+ (c) Copyright 2000 - 2003 zsKnight (zsknight@zsnes.com),
+ _Demo_ (_demo_@zsnes.com), and Nach
+
+ C4 C++ code
+ (c) Copyright 2003 Brad Jorsch
+
+ DSP-1 emulator code
+ (c) Copyright 1998 - 2004 Ivar (ivar@snes9x.com), _Demo_, Gary Henderson,
+ John Weidman, neviksti (neviksti@hotmail.com),
+ Kris Bleakley, Andreas Naive
+
+ DSP-2 emulator code
+ (c) Copyright 2003 Kris Bleakley, John Weidman, neviksti, Matthew Kendora, and
+ Lord Nightmare (lord_nightmare@users.sourceforge.net
+
+ OBC1 emulator code
+ (c) Copyright 2001 - 2004 zsKnight, pagefault (pagefault@zsnes.com) and
+ Kris Bleakley
+ Ported from x86 assembler to C by sanmaiwashi
+
+ SPC7110 and RTC C++ emulator code
+ (c) Copyright 2002 Matthew Kendora with research by
+ zsKnight, John Weidman, and Dark Force
+
+ S-DD1 C emulator code
+ (c) Copyright 2003 Brad Jorsch with research by
+ Andreas Naive and John Weidman
+
+ S-RTC C emulator code
+ (c) Copyright 2001 John Weidman
+
+ ST010 C++ emulator code
+ (c) Copyright 2003 Feather, Kris Bleakley, John Weidman and Matthew Kendora
+
+ Super FX x86 assembler emulator code
+ (c) Copyright 1998 - 2003 zsKnight, _Demo_, and pagefault
+
+ Super FX C emulator code
+ (c) Copyright 1997 - 1999 Ivar, Gary Henderson and John Weidman
+
+
+ SH assembler code partly based on x86 assembler code
+ (c) Copyright 2002 - 2004 Marcus Comstedt (marcus@mc.pp.se)
+
+
+ Specific ports contains the works of other authors. See headers in
+ individual files.
+
+ Snes9x homepage: http://www.snes9x.com
+
+ Permission to use, copy, modify and distribute Snes9x in both binary and
+ source form, for non-commercial purposes, is hereby granted without fee,
+ providing that this license information and copyright notice appear with
+ all copies and any derived work.
+
+ This software is provided 'as-is', without any express or implied
+ warranty. In no event shall the authors be held liable for any damages
+ arising from the use of this software.
+
+ Snes9x is freeware for PERSONAL USE only. Commercial users should
+ seek permission of the copyright holders first. Commercial use includes
+ charging money for Snes9x or software derived from Snes9x.
+
+ The copyright holders request that bug fixes and improvements to the code
+ should be forwarded to them so everyone can benefit from the modifications
+ in future versions.
+
+ Super NES and Super Nintendo Entertainment System are trademarks of
+ Nintendo Co., Limited and its subsidiary companies.
+*******************************************************************************/
+
+#ifdef NETPLAY_SUPPORT
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <memory.h>
+#include <sys/types.h>
+#ifdef HAVE_STRINGS_H
+#include <strings.h>
+#endif
+
+#ifndef __WIN32__
+#include <unistd.h>
+#include <sys/time.h>
+#endif
+
+#ifdef __WIN32__
+
+#include <winsock.h>
+#include <process.h>
+#define ioctl ioctlsocket
+#define close closesocket
+#define read(a,b,c) recv(a, b, c, 0)
+#define write(a,b,c) send(a, b, c, 0)
+#define gettimeofday(a,b) S9xGetTimeOfDay (a)
+#define exit(a) _endthread()
+void S9xGetTimeOfDay (struct timeval *n);
+#else
+
+#include <netdb.h>
+#include <sys/socket.h>
+#include <sys/param.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <signal.h>
+
+#ifdef __SVR4
+#include <sys/stropts.h>
+#endif
+
+#endif // !__WIN32__
+
+#include "snes9x.h"
+#include "netplay.h"
+#include "memmap.h"
+#include "snapshot.h"
+
+#define NP_ONE_CLIENT 1
+
+struct SNPServer NPServer;
+
+extern unsigned long START;
+
+void S9xNPSendToAllClients (uint8 *data, int len);
+bool8 S9xNPLoadFreezeFile (const char *fname, uint8 *&data, uint32 &len);
+void S9xNPSendFreezeFile (int c, uint8 *data, uint32 len);
+void S9xNPNoClientReady (int start_index = NP_ONE_CLIENT);
+void S9xNPRecomputePause ();
+void S9xNPWaitForEmulationToComplete ();
+void S9xNPSendROMImageToAllClients ();
+bool8 S9xNPSendROMImageToClient (int client);
+void S9xNPSendSRAMToClient (int c);
+void S9xNPSendSRAMToAllClients ();
+void S9xNPSyncClient (int);
+void S9xNPSendROMLoadRequest (const char *filename);
+void S9xNPSendFreezeFileToAllClients (const char *filename);
+void S9xNPStopServer ();
+
+void S9xNPShutdownClient (int c, bool8 report_error = FALSE)
+{
+ if (NPServer.Clients [c].Connected)
+ {
+ NPServer.Clients [c].Connected = FALSE;
+ NPServer.Clients [c].SaidHello = FALSE;
+
+ close (NPServer.Clients [c].Socket);
+#ifdef NP_DEBUG
+ printf ("SERVER: Player %d disconnecting @%ld\n", c + 1, S9xGetMilliTime () - START);
+#endif
+ if (report_error)
+ {
+ sprintf (NetPlay.ErrorMsg,
+ "Player %d on '%s' has disconnected.", c + 1,
+ NPServer.Clients [c].HostName);
+ S9xNPSetError (NetPlay.ErrorMsg);
+ }
+
+ if (NPServer.Clients [c].HostName)
+ {
+ free ((char *) NPServer.Clients [c].HostName);
+ NPServer.Clients [c].HostName = NULL;
+ }
+ if (NPServer.Clients [c].ROMName)
+ {
+ free ((char *) NPServer.Clients [c].ROMName);
+ NPServer.Clients [c].ROMName = NULL;
+ }
+ if (NPServer.Clients [c].Who)
+ {
+ free ((char *) NPServer.Clients [c].Who);
+ NPServer.Clients [c].Who = NULL;
+ }
+ NPServer.Joypads [c] = 0;
+ NPServer.NumClients--;
+ S9xNPRecomputePause ();
+ }
+}
+
+static bool8 S9xNPSGetData (int socket, uint8 *data, int length)
+{
+ int len = length;
+ uint8 *ptr = data;
+
+ do
+ {
+ int num_bytes = len;
+
+ // Read the data in small chunks, allowing this thread to spot an
+ // abort request from another thread.
+ if (num_bytes > 512)
+ num_bytes = 512;
+
+ int got = read (socket, (char *) ptr, num_bytes);
+ if (got < 0)
+ {
+ if (errno == EINTR
+#ifdef EAGAIN
+ || errno == EAGAIN
+#endif
+#ifdef EWOULDBLOCK
+ || errno == EWOULDBLOCK
+#endif
+#ifdef WSAEWOULDBLOCK
+ || errno == WSAEWOULDBLOCK
+#endif
+ )
+ continue;
+#ifdef WSAEMSGSIZE
+ if (errno != WSAEMSGSIZE)
+ return (FALSE);
+ else
+ {
+ got = num_bytes;
+#ifdef NP_DEBUG
+ printf ("SERVER: WSAEMSGSIZE, actual bytes %d while receiving data @%d\n", got, S9xGetMilliTime () - START);
+#endif
+ }
+#else
+ return (FALSE);
+#endif
+ }
+ else
+ if (got == 0)
+ return (FALSE);
+
+ len -= got;
+ ptr += got;
+ } while (len > 0);
+
+ return (TRUE);
+}
+
+static bool8 S9xNPSSendData (int fd, const uint8 *data, int length)
+{
+ int Percent = 0;
+ int len = length;
+ int chunk = length / 50;
+
+ if (chunk < 1024)
+ chunk = 1024;
+
+ do
+ {
+ int num_bytes = len;
+
+ // Write the data in small chunks, allowing this thread to spot an
+ // abort request from another thread.
+ if (num_bytes > chunk)
+ num_bytes = chunk;
+
+ int sent;
+ sent = write (fd, (char *) data, len);
+
+ if (sent < 0)
+ {
+ if (errno == EINTR
+#ifdef EAGAIN
+ || errno == EAGAIN
+#endif
+#ifdef EWOULDBLOCK
+ || errno == EWOULDBLOCK
+#endif
+ )
+ {
+#ifdef NP_DEBUG
+ printf ("SERVER: EINTR, EAGAIN or EWOULDBLOCK while sending data @%ld\n", S9xGetMilliTime () - START);
+#endif
+ continue;
+ }
+ return (FALSE);
+ }
+ else
+ if (sent == 0)
+ return (FALSE);
+ len -= sent;
+ data += sent;
+ if (length > 1024)
+ {
+ Percent = (uint8) (((length - len) * 100) / length);
+#ifdef __WIN32__
+ PostMessage (GUI.hWnd, WM_USER, Percent, Percent);
+ Sleep (0);
+#endif
+ }
+ } while (len > 0);
+
+ return (TRUE);
+}
+
+void S9xNPSendHeartBeat ()
+{
+ int len = 3;
+ uint8 data [3 + 4 * 5];
+ uint8 *ptr = data;
+ int n;
+
+ for (n = NP_MAX_CLIENTS - 1; n >= 0; n--)
+ {
+ if (NPServer.Clients [n].SaidHello)
+ break;
+ }
+
+ if (n >= 0)
+ {
+ bool8 Paused = NPServer.Paused != 0;
+
+ NPServer.FrameCount++;
+ *ptr++ = NP_SERV_MAGIC;
+ *ptr++ = 0; // Individual client sequence number will get placed here
+ *ptr++ = NP_SERV_JOYPAD | (n << 6) | ((Paused != 0) << 5);
+
+ WRITE_LONG (ptr, NPServer.FrameCount);
+ len += 4;
+ ptr += 4;
+
+ int i;
+
+ for (i = 0; i <= n; i++)
+ {
+ WRITE_LONG (ptr, NPServer.Joypads [i]);
+ len += 4;
+ ptr += 4;
+ }
+
+ S9xNPSendToAllClients (data, len);
+ }
+}
+
+void S9xNPSendToAllClients (uint8 *data, int len)
+{
+ int i;
+
+ for (i = 0; i < NP_MAX_CLIENTS; i++)
+ {
+ if (NPServer.Clients [i].SaidHello)
+ {
+ data [1] = NPServer.Clients [i].SendSequenceNum++;
+ if (!S9xNPSSendData (NPServer.Clients [i].Socket, data, len))
+ S9xNPShutdownClient (i, TRUE);
+ }
+ }
+}
+
+void S9xNPProcessClient (int c)
+{
+ uint8 header [7];
+ uint8 *data;
+ uint32 len;
+ uint8 *ptr;
+
+ if (!S9xNPSGetData (NPServer.Clients [c].Socket, header, 7))
+ {
+ S9xNPSetWarning ("SERVER: Failed to get message header from client.\n");
+ S9xNPShutdownClient (c, TRUE);
+ return;
+ }
+ if (header [0] != NP_CLNT_MAGIC)
+ {
+ S9xNPSetWarning ("SERVER: Bad header magic value received from client.\n");
+ S9xNPShutdownClient (c, TRUE);
+ return;
+ }
+
+ if (header [1] != NPServer.Clients [c].ReceiveSequenceNum)
+ {
+#ifdef NP_DEBUG
+ printf ("SERVER: Messages lost from '%s', expected %d, got %d\n",
+ NPServer.Clients [c].HostName ?
+ NPServer.Clients [c].HostName : "Unknown",
+ NPServer.Clients [c].ReceiveSequenceNum,
+ header [1]);
+#endif
+ sprintf (NetPlay.WarningMsg,
+ "SERVER: Messages lost from '%s', expected %d, got %d\n",
+ NPServer.Clients [c].HostName ?
+ NPServer.Clients [c].HostName : "Unknown",
+ NPServer.Clients [c].ReceiveSequenceNum,
+ header [1]);
+ NPServer.Clients [c].ReceiveSequenceNum = header [1] + 1;
+ S9xNPSetWarning (NetPlay.WarningMsg);
+ }
+ else
+ NPServer.Clients [c].ReceiveSequenceNum++;
+
+ len = READ_LONG (&header [3]);
+
+ switch (header [2] & 0x3f)
+ {
+ case NP_CLNT_HELLO:
+#ifdef NP_DEBUG
+ printf ("SERVER: Got HELLO from client @%ld\n", S9xGetMilliTime () - START);
+#endif
+ S9xNPSetAction ("Got HELLO from client...", TRUE);
+ if (len > 0x10000)
+ {
+ S9xNPSetWarning ("SERVER: Client HELLO message length error.");
+ S9xNPShutdownClient (c, TRUE);
+ return;
+ }
+ data = new uint8 [len - 7];
+ if (!S9xNPSGetData (NPServer.Clients [c].Socket, data, len - 7))
+ {
+ S9xNPSetWarning ("SERVER: Failed to get HELLO message content from client.");
+ S9xNPShutdownClient (c, TRUE);
+ return;
+ }
+
+ if (NPServer.NumClients <= NP_ONE_CLIENT)
+ {
+ NPServer.FrameTime = READ_LONG (data);
+ strncpy (NPServer.ROMName, (char *) &data [4], 29);
+ NPServer.ROMName [29] = 0;
+ }
+
+ NPServer.Clients [c].ROMName = strdup ((char *) &data [4]);
+#ifdef NP_DEBUG
+ printf ("SERVER: Client is playing: %s, Frame Time: %d @%ld\n", data + 4, READ_LONG (data), S9xGetMilliTime () - START);
+#endif
+
+ NPServer.Clients [c].SendSequenceNum = 0;
+
+ len = 7 + 1 + 1 + 4 + strlen (NPServer.ROMName) + 1;
+
+ delete data;
+ ptr = data = new uint8 [len];
+ *ptr++ = NP_SERV_MAGIC;
+ *ptr++ = NPServer.Clients [c].SendSequenceNum++;
+
+ if (NPServer.SendROMImageOnConnect &&
+ NPServer.NumClients > NP_ONE_CLIENT)
+ *ptr++ = NP_SERV_HELLO | 0x80;
+ else
+ *ptr++ = NP_SERV_HELLO;
+ WRITE_LONG (ptr, len);
+ ptr += 4;
+ *ptr++ = NP_VERSION;
+ *ptr++ = c + 1;
+ WRITE_LONG (ptr, NPServer.FrameCount);
+ ptr += 4;
+ strcpy ((char *) ptr, NPServer.ROMName);
+
+#ifdef NP_DEBUG
+ printf ("SERVER: Sending welcome information to client @%ld...\n", S9xGetMilliTime () - START);
+#endif
+ S9xNPSetAction ("SERVER: Sending welcome information to new client...", TRUE);
+ if (!S9xNPSSendData (NPServer.Clients [c].Socket, data, len))
+ {
+ S9xNPSetWarning ("SERVER: Failed to send welcome message to client.");
+ S9xNPShutdownClient (c, TRUE);
+ return;
+ }
+ delete data;
+#ifdef NP_DEBUG
+ printf ("SERVER: Waiting for a response from the client @%ld...\n", S9xGetMilliTime () - START);
+#endif
+ S9xNPSetAction ("SERVER: Waiting for a response from the client...", TRUE);
+ break;
+
+ case NP_CLNT_LOADED_ROM:
+#ifdef NP_DEBUG
+ printf ("SERVER: Client %d loaded requested ROM @%ld...\n", c, S9xGetMilliTime () - START);
+#endif
+ NPServer.Clients [c].SaidHello = TRUE;
+ NPServer.Clients [c].Ready = FALSE;
+ NPServer.Clients [c].Paused = FALSE;
+ S9xNPRecomputePause ();
+ S9xNPWaitForEmulationToComplete ();
+
+ if (NPServer.SyncByReset)
+ {
+ S9xNPServerAddTask (NP_SERVER_SEND_SRAM, (void *) c);
+ S9xNPServerAddTask (NP_SERVER_RESET_ALL, 0);
+ }
+ else
+ S9xNPServerAddTask (NP_SERVER_SYNC_CLIENT, (void *) c);
+ break;
+
+ case NP_CLNT_RECEIVED_ROM_IMAGE:
+#ifdef NP_DEBUG
+ printf ("SERVER: Client %d received ROM image @%ld...\n", c, S9xGetMilliTime () - START);
+#endif
+ NPServer.Clients [c].SaidHello = TRUE;
+ NPServer.Clients [c].Ready = FALSE;
+ NPServer.Clients [c].Paused = FALSE;
+ S9xNPRecomputePause ();
+ S9xNPWaitForEmulationToComplete ();
+
+ if (NPServer.SyncByReset)
+ {
+ S9xNPServerAddTask (NP_SERVER_SEND_SRAM, (void *) c);
+ S9xNPServerAddTask (NP_SERVER_RESET_ALL, 0);
+ }
+ else
+ S9xNPServerAddTask (NP_SERVER_SYNC_CLIENT, (void *) c);
+
+ break;
+
+ case NP_CLNT_WAITING_FOR_ROM_IMAGE:
+#ifdef NP_DEBUG
+ printf ("SERVER: Client %d waiting for ROM image @%ld...\n", c, S9xGetMilliTime () - START);
+#endif
+ NPServer.Clients [c].SaidHello = TRUE;
+ NPServer.Clients [c].Ready = FALSE;
+ NPServer.Clients [c].Paused = FALSE;
+ S9xNPRecomputePause ();
+ S9xNPSendROMImageToClient (c);
+ break;
+
+ case NP_CLNT_READY:
+#ifdef NP_DEBUG
+ printf ("SERVER: Client %d ready @%ld...\n", c, S9xGetMilliTime () - START);
+#endif
+ if (NPServer.Clients [c].SaidHello)
+ {
+ NPServer.Clients [c].Paused = FALSE;
+ NPServer.Clients [c].Ready = TRUE;
+
+ S9xNPRecomputePause ();
+ break;
+ }
+ NPServer.Clients [c].SaidHello = TRUE;
+ NPServer.Clients [c].Ready = TRUE;
+ NPServer.Clients [c].Paused = FALSE;
+ S9xNPRecomputePause ();
+
+//printf ("SERVER: SaidHello = TRUE, SeqNum = %d @%d\n", NPServer.Clients [c].SendSequenceNum, S9xGetMilliTime () - START);
+ if (NPServer.NumClients > NP_ONE_CLIENT)
+ {
+ if (!NPServer.SendROMImageOnConnect)
+ {
+ S9xNPWaitForEmulationToComplete ();
+
+ if (NPServer.SyncByReset)
+ {
+ S9xNPServerAddTask (NP_SERVER_SEND_SRAM, (void *) c);
+ S9xNPServerAddTask (NP_SERVER_RESET_ALL, 0);
+ }
+ else
+ S9xNPServerAddTask (NP_SERVER_SYNC_CLIENT, (void *) c);
+ }
+ }
+ else
+ {
+ NPServer.Clients [c].Ready = TRUE;
+ S9xNPRecomputePause ();
+ }
+ break;
+ case NP_CLNT_JOYPAD:
+ NPServer.Joypads [c] = len;
+ break;
+ case NP_CLNT_PAUSE:
+#ifdef NP_DEBUG
+ printf ("SERVER: Client %d Paused: %s @%ld\n", c, (header [2] & 0x80) ? "YES" : "NO", S9xGetMilliTime () - START);
+#endif
+ NPServer.Clients [c].Paused = (header [2] & 0x80) != 0;
+ if (NPServer.Clients [c].Paused)
+ sprintf (NetPlay.WarningMsg, "SERVER: Client %d has paused.", c + 1);
+ else
+ sprintf (NetPlay.WarningMsg, "SERVER: Client %d has resumed.", c + 1);
+ S9xNPSetWarning (NetPlay.WarningMsg);
+ S9xNPRecomputePause ();
+ break;
+ }
+}
+
+void S9xNPAcceptClient (int Listen, bool8 block)
+{
+ struct sockaddr_in remote_address;
+ struct linger val2;
+ struct hostent *host;
+ int new_fd;
+ int i;
+
+#ifdef NP_DEBUG
+ printf ("SERVER: attempting to accept new client connection @%ld\n", S9xGetMilliTime () - START);
+#endif
+ S9xNPSetAction ("SERVER: Attempting to accept client connection...", TRUE);
+ memset (&remote_address, 0, sizeof (remote_address));
+ ACCEPT_SIZE_T len = sizeof (remote_address);
+
+ new_fd = accept (Listen, (struct sockaddr *)&remote_address, &len);
+
+ S9xNPSetAction ("Setting socket options...", TRUE);
+ val2.l_onoff = 1;
+ val2.l_linger = 0;
+ if (setsockopt (new_fd, SOL_SOCKET, SO_LINGER,
+ (char *) &val2, sizeof (val2)) < 0)
+ {
+ S9xNPSetError ("Setting socket options failed.");
+ close (new_fd);
+ return;
+ }
+
+ for (i = 0; i < NP_MAX_CLIENTS; i++)
+ {
+ if (!NPServer.Clients [i].Connected)
+ {
+ NPServer.NumClients++;
+ NPServer.Clients [i].Socket = new_fd;
+ NPServer.Clients [i].SendSequenceNum = 0;
+ NPServer.Clients [i].ReceiveSequenceNum = 0;
+ NPServer.Clients [i].Connected = TRUE;
+ NPServer.Clients [i].SaidHello = FALSE;
+ NPServer.Clients [i].Paused = FALSE;
+ NPServer.Clients [i].Ready = FALSE;
+ NPServer.Clients [i].ROMName = NULL;
+ NPServer.Clients [i].HostName = NULL;
+ NPServer.Clients [i].Who = NULL;
+ break;
+ }
+ }
+
+ if (i >= NP_MAX_CLIENTS)
+ {
+ S9xNPSetError ("SERVER: Maximum number of NetPlay Clients have already connected.");
+ close (new_fd);
+ return;
+ }
+
+ if (remote_address.sin_family == AF_INET)
+ {
+#ifdef NP_DEBUG
+ printf ("SERVER: Looking up new client's hostname @%ld\n", S9xGetMilliTime () - START);
+#endif
+ S9xNPSetAction ("SERVER: Looking up new client's hostname...", TRUE);
+ host = gethostbyaddr ((char *) &remote_address.sin_addr,
+ sizeof (remote_address.sin_addr), AF_INET);
+
+ if (host)
+ {
+#ifdef NP_DEBUG
+ printf ("SERVER: resolved new client's hostname (%s) @%ld\n", host->h_name, S9xGetMilliTime () - START);
+#endif
+ sprintf (NetPlay.WarningMsg, "SERVER: Player %d on %s has connected.", i + 1, host->h_name);
+ NPServer.Clients [i].HostName = strdup (host->h_name);
+ }
+ else
+ {
+ char *ip = inet_ntoa (remote_address.sin_addr);
+ if (ip)
+ NPServer.Clients [i].HostName = strdup (ip);
+#ifdef NP_DEBUG
+ printf ("SERVER: couldn't resolve new client's hostname (%s) @%ld\n", ip ? ip : "Unknown", S9xGetMilliTime () - START);
+#endif
+ sprintf (NetPlay.WarningMsg, "SERVER: Player %d on %s has connected.", i + 1, ip ? ip : "Unknown");
+ }
+ S9xNPSetWarning (NetPlay.WarningMsg);
+ }
+#ifdef NP_DEBUG
+ printf ("SERVER: waiting for HELLO message from new client @%ld\n", S9xGetMilliTime () - START);
+#endif
+ S9xNPSetAction ("SERVER: Waiting for HELLO message from new client...");
+}
+
+static bool8 server_continue = TRUE;
+
+static bool8 S9xNPServerInit (int port)
+{
+ struct sockaddr_in address;
+ int i;
+ int val;
+
+ if (!S9xNPInitialise ())
+ return (FALSE);
+
+ for (i = 0; i < NP_MAX_CLIENTS; i++)
+ {
+ NPServer.Clients [i].SendSequenceNum = 0;
+ NPServer.Clients [i].ReceiveSequenceNum = 0;
+ NPServer.Clients [i].Connected = FALSE;
+ NPServer.Clients [i].SaidHello = FALSE;
+ NPServer.Clients [i].Paused = FALSE;
+ NPServer.Clients [i].Ready = FALSE;
+ NPServer.Clients [i].Socket = 0;
+ NPServer.Clients [i].ROMName = NULL;
+ NPServer.Clients [i].HostName = NULL;
+ NPServer.Clients [i].Who = NULL;
+ NPServer.Joypads [i] = 0;
+ }
+
+ NPServer.NumClients = 0;
+ NPServer.FrameCount = 0;
+
+#ifdef NP_DEBUG
+ printf ("SERVER: Creating socket @%ld\n", S9xGetMilliTime () - START);
+#endif
+ if ((NPServer.Socket = socket (AF_INET, SOCK_STREAM, 0)) < 0)
+ {
+ S9xNPSetError ("NetPlay Server: Can't create listening socket.");
+ return (FALSE);
+ }
+
+ val = 1;
+ setsockopt (NPServer.Socket, SOL_SOCKET, SO_REUSEADDR,
+ (char *)&val, sizeof (val));
+
+ memset (&address, 0, sizeof (address));
+ address.sin_family = AF_INET;
+ address.sin_addr.s_addr = htonl (INADDR_ANY);
+ address.sin_port = htons (port);
+
+#ifdef NP_DEBUG
+ printf ("SERVER: Binding socket to address and port @%ld\n", S9xGetMilliTime () - START);
+#endif
+ if (bind (NPServer.Socket, (struct sockaddr *) &address, sizeof (address)) < 0)
+ {
+ S9xNPSetError ("NetPlay Server: Can't bind socket to port number.\nPort already in use?");
+ return (FALSE);
+ }
+
+#ifdef NP_DEBUG
+ printf ("SERVER: Getting socket to listen @%ld\n", S9xGetMilliTime () - START);
+#endif
+ if (listen (NPServer.Socket, NP_MAX_CLIENTS) < 0)
+ {
+ S9xNPSetError ("NetPlay Server: Can't get new socket to listen.");
+ return (FALSE);
+ }
+
+#ifdef NP_DEBUG
+ printf ("SERVER: Init complete @%ld\n", S9xGetMilliTime () - START);
+#endif
+ return (TRUE);
+}
+
+void S9xNPServerLoop (void *)
+{
+#ifdef __WIN32__
+ BOOL success = FALSE;
+#else
+ bool8 success = FALSE;
+#endif
+
+ while (server_continue)
+ {
+ fd_set read_fds;
+ struct timeval timeout;
+ int res;
+ int i;
+
+ int max_fd = NPServer.Socket;
+
+#ifdef __WIN32__
+ Sleep (0);
+#endif
+
+ if (success && !Settings.Paused && !Settings.StopEmulation &&
+ !Settings.ForcedPause && !NPServer.Paused)
+ {
+ S9xNPSendHeartBeat ();
+ }
+
+ do
+ {
+ FD_ZERO (&read_fds);
+ FD_SET (NPServer.Socket, &read_fds);
+ for (i = 0; i < NP_MAX_CLIENTS; i++)
+ {
+ if (NPServer.Clients [i].Connected)
+ {
+ FD_SET (NPServer.Clients [i].Socket, &read_fds);
+ if (NPServer.Clients [i].Socket > max_fd)
+ max_fd = NPServer.Clients [i].Socket;
+ }
+ }
+
+ timeout.tv_sec = 0;
+ timeout.tv_usec = 1000;
+ res = select (max_fd + 1, &read_fds, NULL, NULL, &timeout);
+
+ if (res > 0)
+ {
+ if (FD_ISSET (NPServer.Socket, &read_fds))
+ S9xNPAcceptClient (NPServer.Socket, FALSE);
+
+ for (i = 0; i < NP_MAX_CLIENTS; i++)
+ {
+ if (NPServer.Clients [i].Connected &&
+ FD_ISSET (NPServer.Clients [i].Socket, &read_fds))
+ {
+ S9xNPProcessClient (i);
+ }
+ }
+ }
+ } while (res > 0);
+
+#ifdef __WIN32__
+ success = WaitForSingleObject (GUI.ServerTimerSemaphore, 200) == WAIT_OBJECT_0;
+#endif
+
+ while (NPServer.TaskHead != NPServer.TaskTail)
+ {
+ void *task_data = NPServer.TaskQueue [NPServer.TaskHead].Data;
+
+#if defined(NP_DEBUG) && NP_DEBUG == 2
+ printf ("SERVER: task %d @%ld\n", NPServer.TaskQueue [NPServer.TaskHead].Task, S9xGetMilliTime () - START);
+#endif
+
+ switch (NPServer.TaskQueue [NPServer.TaskHead].Task)
+ {
+ case NP_SERVER_SEND_ROM_IMAGE:
+ S9xNPSendROMImageToAllClients ();
+ break;
+ case NP_SERVER_SYNC_CLIENT:
+ NPServer.Clients [(int) task_data].Ready = FALSE;
+ S9xNPRecomputePause ();
+ S9xNPSyncClient ((int) task_data);
+ break;
+ case NP_SERVER_SYNC_ALL:
+ S9xNPSyncClients ();
+ break;
+ case NP_SERVER_SEND_FREEZE_FILE_ALL:
+ S9xNPSendFreezeFileToAllClients ((char *) task_data);
+ free ((char *) task_data);
+ break;
+ case NP_SERVER_SEND_ROM_LOAD_REQUEST_ALL:
+ S9xNPSendROMLoadRequest ((char *) task_data);
+ free ((char *) task_data);
+ break;
+ case NP_SERVER_RESET_ALL:
+ S9xNPNoClientReady (0);
+ S9xNPWaitForEmulationToComplete ();
+ S9xNPSetAction ("SERVER: Sending RESET to all clients...", TRUE);
+#ifdef NP_DEBUG
+ printf ("SERVER: Sending RESET to all clients @%ld\n", S9xGetMilliTime () - START);
+#endif
+ {
+ uint8 reset [7];
+ uint8 *ptr;
+
+ ptr = reset;
+ *ptr++ = NP_SERV_MAGIC;
+ *ptr++ = 0;
+ *ptr++ = NP_SERV_RESET;
+ WRITE_LONG (ptr, NPServer.FrameCount);
+ S9xNPSendToAllClients (reset, 7);
+ }
+ break;
+ case NP_SERVER_SEND_SRAM:
+ NPServer.Clients [(int) task_data].Ready = FALSE;
+ S9xNPRecomputePause ();
+ S9xNPWaitForEmulationToComplete ();
+ S9xNPSendSRAMToClient ((int) task_data);
+ break;
+
+ case NP_SERVER_SEND_SRAM_ALL:
+ S9xNPNoClientReady ();
+ S9xNPWaitForEmulationToComplete ();
+ S9xNPSendSRAMToAllClients ();
+ break;
+
+ default:
+ S9xNPSetError ("SERVER: *** Unknown task ***\n");
+ break;
+ }
+ NPServer.TaskHead = (NPServer.TaskHead + 1) % NP_MAX_TASKS;
+ }
+ }
+#ifdef NP_DEBUG
+ printf ("SERVER: Server thread exiting @%ld\n", S9xGetMilliTime () - START);
+#endif
+ S9xNPStopServer ();
+}
+
+bool8 S9xNPStartServer (int port)
+{
+ static int p;
+
+#ifdef NP_DEBUG
+ printf ("SERVER: Starting server on port %d @%ld\n", port, S9xGetMilliTime () - START);
+#endif
+ p = port;
+ server_continue = TRUE;
+ if (S9xNPServerInit (port))
+#ifdef __WIN32__
+ return (_beginthread (S9xNPServerLoop, 0, &p) != ~0);
+#else
+ return (TRUE);
+#endif
+
+ return (FALSE);
+}
+
+void S9xNPStopServer ()
+{
+#ifdef NP_DEBUG
+ printf ("SERVER: Stopping server @%ld\n", S9xGetMilliTime () - START);
+#endif
+ server_continue = FALSE;
+ close (NPServer.Socket);
+
+ for (int i = 0; i < NP_MAX_CLIENTS; i++)
+ {
+ if (NPServer.Clients [i].Connected)
+ {
+ close (NPServer.Clients [i].Socket);
+ NPServer.Clients [i].Connected = FALSE;
+ NPServer.Clients [i].SaidHello = FALSE;
+ }
+ }
+}
+
+#ifdef __WIN32__
+void S9xGetTimeOfDay (struct timeval *n)
+{
+ unsigned long t = S9xGetMilliTime ();
+
+ n->tv_sec = t / 1000;
+ n->tv_usec = (t % 1000) * 1000;
+}
+#endif
+
+void S9xNPSendROMImageToAllClients ()
+{
+ S9xNPNoClientReady ();
+ S9xNPWaitForEmulationToComplete ();
+
+ int c;
+
+ for (c = NP_ONE_CLIENT; c < NP_MAX_CLIENTS; c++)
+ {
+ if (NPServer.Clients [c].SaidHello)
+ S9xNPSendROMImageToClient (c);
+ }
+
+ if (NPServer.SyncByReset)
+ {
+ S9xNPServerAddTask (NP_SERVER_SEND_SRAM_ALL, 0);
+ S9xNPServerAddTask (NP_SERVER_RESET_ALL, 0);
+ }
+ else
+ S9xNPSyncClient (-1);
+}
+
+bool8 S9xNPSendROMImageToClient (int c)
+{
+#ifdef NP_DEBUG
+ printf ("SERVER: Sending ROM image to player %d @%ld\n", c + 1, S9xGetMilliTime () - START);
+#endif
+ sprintf (NetPlay.ActionMsg, "Sending ROM image to player %d...", c + 1);
+ S9xNPSetAction (NetPlay.ActionMsg, TRUE);
+
+ uint8 header [7 + 1 + 4];
+ uint8 *ptr = header;
+ int len = sizeof (header) + Memory.CalculatedSize +
+ strlen (Memory.ROMFilename) + 1;
+ *ptr++ = NP_SERV_MAGIC;
+ *ptr++ = NPServer.Clients [c].SendSequenceNum++;
+ *ptr++ = NP_SERV_ROM_IMAGE;
+ WRITE_LONG (ptr, len);
+ ptr += 4;
+ *ptr++ = Memory.HiROM;
+ WRITE_LONG (ptr, Memory.CalculatedSize);
+
+ if (!S9xNPSSendData (NPServer.Clients [c].Socket, header, sizeof (header)) ||
+ !S9xNPSSendData (NPServer.Clients [c].Socket, Memory.ROM,
+ Memory.CalculatedSize) ||
+ !S9xNPSSendData (NPServer.Clients [c].Socket, (uint8 *) Memory.ROMFilename,
+ strlen (Memory.ROMFilename) + 1))
+ {
+ S9xNPShutdownClient (c, TRUE);
+ return (FALSE);
+ }
+ return (TRUE);
+}
+
+void S9xNPSyncClients ()
+{
+ S9xNPNoClientReady ();
+ S9xNPSyncClient (-1);
+}
+
+void S9xNPSyncClient (int client)
+{
+#ifdef HAVE_MKSTEMP
+ char fname[] = "/tmp/snes9x_fztmpXXXXXX";
+#else
+ char fname [L_tmpnam];
+#endif
+
+ S9xNPWaitForEmulationToComplete ();
+
+ S9xNPSetAction ("SERVER: Freezing game...", TRUE);
+#ifdef HAVE_MKSTEMP
+ if ( (mkstemp(fname) < 0) && S9xFreezeGame(fname) )
+#else
+ if ( tmpnam(fname) && S9xFreezeGame(fname) )
+#endif
+ {
+ uint8 *data;
+ uint32 len;
+
+ S9xNPSetAction ("SERVER: Loading freeze file...", TRUE);
+ if (S9xNPLoadFreezeFile (fname, data, len))
+ {
+ int c;
+
+ if (client < 0)
+ {
+ for (c = NP_ONE_CLIENT; c < NP_MAX_CLIENTS; c++)
+ {
+ if (NPServer.Clients [c].SaidHello)
+ {
+ NPServer.Clients [client].Ready = FALSE;
+ S9xNPRecomputePause ();
+ S9xNPSendFreezeFile (c, data, len);
+ }
+ }
+ }
+ else
+ {
+ NPServer.Clients [client].Ready = FALSE;
+ S9xNPRecomputePause ();
+ S9xNPSendFreezeFile (client, data, len);
+ }
+ delete data;
+ }
+ remove (fname);
+ }
+}
+
+bool8 S9xNPLoadFreezeFile (const char *fname, uint8 *&data, uint32 &len)
+{
+ FILE *ff;
+
+ if ((ff = fopen (fname, "rb")))
+ {
+ fseek (ff, 0, SEEK_END);
+ len = ftell (ff);
+ fseek (ff, 0, SEEK_SET);
+
+ data = new uint8 [len];
+ bool8 ok = (fread (data, 1, len, ff) == len);
+ fclose (ff);
+
+ return (ok);
+ }
+ return (FALSE);
+}
+
+void S9xNPSendFreezeFile (int c, uint8 *data, uint32 len)
+{
+#ifdef NP_DEBUG
+ printf ("SERVER: Sending freeze file to player %d @%ld\n", c + 1, S9xGetMilliTime () - START);
+#endif
+
+ sprintf (NetPlay.ActionMsg, "SERVER: Sending freeze-file to player %d...", c + 1);
+ S9xNPSetAction (NetPlay.ActionMsg, TRUE);
+ uint8 header [7 + 4];
+ uint8 *ptr = header;
+
+ *ptr++ = NP_SERV_MAGIC;
+ *ptr++ = NPServer.Clients [c].SendSequenceNum++;
+ *ptr++ = NP_SERV_FREEZE_FILE;
+ WRITE_LONG (ptr, len + 7 + 4);
+ ptr += 4;
+ WRITE_LONG (ptr, NPServer.FrameCount);
+
+ if (!S9xNPSSendData (NPServer.Clients [c].Socket, header, 7 + 4) ||
+ !S9xNPSSendData (NPServer.Clients [c].Socket, data, len))
+ {
+ S9xNPShutdownClient (c, TRUE);
+ }
+}
+
+void S9xNPRecomputePause ()
+{
+ int c;
+
+ for (c = 0; c < NP_MAX_CLIENTS; c++)
+ {
+ if (NPServer.Clients [c].SaidHello &&
+ (!NPServer.Clients [c].Ready || NPServer.Clients [c].Paused))
+ {
+#if defined(NP_DEBUG) && NP_DEBUG == 2
+ printf ("SERVER: Paused because of client %d (%d,%d) @%ld\n", c, NPServer.Clients [c].Ready, NPServer.Clients [c].Paused, S9xGetMilliTime () - START);
+#endif
+ NPServer.Paused = TRUE;
+ return;
+ }
+ }
+#if defined(NP_DEBUG) && NP_DEBUG == 2
+ printf ("SERVER: not paused @%ld\n", S9xGetMilliTime () - START);
+#endif
+ NPServer.Paused = FALSE;
+}
+
+void S9xNPNoClientReady (int start_index)
+{
+ int c;
+
+ for (c = start_index; c < NP_MAX_CLIENTS; c++)
+ NPServer.Clients [c].Ready = FALSE;
+ S9xNPRecomputePause ();
+}
+
+void S9xNPSendROMLoadRequest (const char *filename)
+{
+ S9xNPNoClientReady ();
+
+ int len = 7 + strlen (filename) + 1;
+ uint8 *data = new uint8 [len];
+ uint8 *ptr = data;
+ *ptr++ = NP_SERV_MAGIC;
+ *ptr++ = 0;
+ *ptr++ = NP_SERV_LOAD_ROM;
+ WRITE_LONG (ptr, len);
+ ptr += 4;
+ strcpy ((char *) ptr, filename);
+
+ for (int i = NP_ONE_CLIENT; i < NP_MAX_CLIENTS; i++)
+ {
+ if (NPServer.Clients [i].SaidHello)
+ {
+#ifdef NP_DEBUG
+ printf ("SERVER: Sending load ROM requesting to player %d @%ld\n", i + 1, S9xGetMilliTime () - START);
+#endif
+ sprintf (NetPlay.WarningMsg, "SERVER: sending ROM load request to player %d...", i + 1);
+ S9xNPSetAction (NetPlay.WarningMsg, TRUE);
+ data [1] = NPServer.Clients [i].SendSequenceNum++;
+ if (!S9xNPSSendData (NPServer.Clients [i].Socket, data, len))
+ {
+ S9xNPShutdownClient (i, TRUE);
+ }
+ }
+ }
+ delete data;
+}
+
+void S9xNPSendSRAMToAllClients ()
+{
+ int i;
+
+ for (i = NP_ONE_CLIENT; i < NP_MAX_CLIENTS; i++)
+ {
+ if (NPServer.Clients [i].SaidHello)
+ S9xNPSendSRAMToClient (i);
+ }
+}
+
+void S9xNPSendSRAMToClient (int c)
+{
+#ifdef NP_DEBUG
+ printf ("SERVER: Sending S-RAM data to player %d @%ld\n", c + 1, S9xGetMilliTime () - START);
+#endif
+ uint8 sram [7];
+ int SRAMSize = Memory.SRAMSize ?
+ (1 << (Memory.SRAMSize + 3)) * 128 : 0;
+ if (SRAMSize > 0x10000)
+ SRAMSize = 0x10000;
+ int len = 7 + SRAMSize;
+
+ sprintf (NetPlay.ActionMsg, "SERVER: Sending S-RAM to player %d...", c + 1);
+ S9xNPSetAction (NetPlay.ActionMsg, TRUE);
+
+ uint8 *ptr = sram;
+ *ptr++ = NP_SERV_MAGIC;
+ *ptr++ = NPServer.Clients [c].SendSequenceNum++;
+ *ptr++ = NP_SERV_SRAM_DATA;
+ WRITE_LONG (ptr, len);
+ if (!S9xNPSSendData (NPServer.Clients [c].Socket,
+ sram, sizeof (sram)) ||
+ (len > 7 &&
+ !S9xNPSSendData (NPServer.Clients [c].Socket,
+ ::SRAM, len - 7)))
+ {
+ S9xNPShutdownClient (c, TRUE);
+ }
+}
+
+void S9xNPSendFreezeFileToAllClients (const char *filename)
+{
+ uint8 *data;
+ uint32 len;
+
+ if (NPServer.NumClients > NP_ONE_CLIENT && S9xNPLoadFreezeFile (filename, data, len))
+ {
+ S9xNPNoClientReady ();
+
+ for (int c = NP_ONE_CLIENT; c < NP_MAX_CLIENTS; c++)
+ {
+ if (NPServer.Clients [c].SaidHello)
+ S9xNPSendFreezeFile (c, data, len);
+ }
+ delete data;
+ }
+}
+
+void S9xNPServerAddTask (uint32 task, void *data)
+{
+ NPServer.TaskQueue [NPServer.TaskTail].Task = task;
+ NPServer.TaskQueue [NPServer.TaskTail].Data = data;
+
+ NPServer.TaskTail = (NPServer.TaskTail + 1) % NP_MAX_TASKS;
+}
+
+void S9xNPReset ()
+{
+ S9xNPNoClientReady (0);
+ S9xNPServerAddTask (NP_SERVER_RESET_ALL, 0);
+}
+
+void S9xNPWaitForEmulationToComplete ()
+{
+#ifdef NP_DEBUG
+ printf ("SERVER: WaitForEmulationToComplete start @%ld\n", S9xGetMilliTime () - START);
+#endif
+
+ while (!NetPlay.PendingWait4Sync && NetPlay.Connected &&
+ !Settings.ForcedPause && !Settings.StopEmulation &&
+ !Settings.Paused)
+ {
+#ifdef __WIN32__
+ Sleep (40);
+#endif
+ }
+#ifdef NP_DEBUG
+ printf ("SERVER: WaitForEmulationToComplete end @%ld\n", S9xGetMilliTime () - START);
+#endif
+}
+
+void S9xNPServerQueueSyncAll ()
+{
+ if (Settings.NetPlay && Settings.NetPlayServer &&
+ NPServer.NumClients > NP_ONE_CLIENT)
+ {
+ S9xNPNoClientReady ();
+ S9xNPDiscardHeartbeats ();
+ S9xNPServerAddTask (NP_SERVER_SYNC_ALL, 0);
+ }
+}
+
+void S9xNPServerQueueSendingROMImage ()
+{
+ if (Settings.NetPlay && Settings.NetPlayServer &&
+ NPServer.NumClients > NP_ONE_CLIENT)
+ {
+ S9xNPNoClientReady ();
+ S9xNPDiscardHeartbeats ();
+ S9xNPServerAddTask (NP_SERVER_SEND_ROM_IMAGE, 0);
+ }
+}
+
+void S9xNPServerQueueSendingFreezeFile (const char *filename)
+{
+ if (Settings.NetPlay && Settings.NetPlayServer &&
+ NPServer.NumClients > NP_ONE_CLIENT)
+ {
+ S9xNPNoClientReady ();
+ S9xNPDiscardHeartbeats ();
+ S9xNPServerAddTask (NP_SERVER_SEND_FREEZE_FILE_ALL,
+ (void *) strdup (filename));
+ }
+}
+
+void S9xNPServerQueueSendingLoadROMRequest (const char *filename)
+{
+ if (Settings.NetPlay && Settings.NetPlayServer &&
+ NPServer.NumClients > NP_ONE_CLIENT)
+ {
+ S9xNPNoClientReady ();
+ S9xNPDiscardHeartbeats ();
+ S9xNPServerAddTask (NP_SERVER_SEND_ROM_LOAD_REQUEST_ALL,
+ (void *) strdup (filename));
+ }
+}
+
+#ifndef __WIN32__
+uint32 S9xGetMilliTime ()
+{
+ static bool8 first = TRUE;
+ static long start_sec;
+ struct timeval tv;
+
+ gettimeofday (&tv, NULL);
+ if (first)
+ {
+ start_sec = tv.tv_sec;
+ first = FALSE;
+ }
+ return ((uint32) ((tv.tv_sec - start_sec) * 1000 + tv.tv_usec / 1000));
+}
+#endif
+#endif
+