📄 netplay.cpp
字号:
/* * Snes9x - Portable Super Nintendo Entertainment System (TM) emulator. * * (c) Copyright 1996 - 2001 Gary Henderson (gary@daniver.demon.co.uk) and * Jerremy Koot (jkoot@snes9x.com) * * Super FX C emulator code * (c) Copyright 1997 - 1999 Ivar (Ivar@snes9x.com) and * Gary Henderson. * Super FX assembler emulator code (c) Copyright 1998 zsKnight and _Demo_. * * DSP1 emulator code (c) Copyright 1998 Ivar, _Demo_ and Gary Henderson. * C4 asm and some C emulation code (c) Copyright 2000 zsKnight and _Demo_. * C4 C code (c) Copyright 2001 Gary Henderson (gary@daniver.demon.co.uk). * * DOS port code contains the works of other authors. See headers in * individual files. * * Snes9x homepage: 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>#ifndef __WIN32__#include <unistd.h>#include <sys/time.h>#endif#if defined (__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)#else#include <netdb.h>#include <sys/ioctl.h>#include <sys/socket.h>#include <sys/param.h>#include <netinet/in.h>#include <arpa/inet.h>#ifdef __SVR4#include <sys/stropts.h>#endif#endif#ifdef USE_THREADS#include <pthread.h>#include <sched.h>#include <semaphore.h>#endif#include "snes9x.h"#include "cpuexec.h"#include "netplay.h"#include "memmap.h"#include "snapshot.h"#include "display.h"void S9xNPClientLoop (void *);bool8 S9xNPLoadROM (uint32 len);bool8 S9xNPLoadROMDialog (const char *);bool8 S9xNPGetROMImage (uint32 len);void S9xNPGetSRAMData (uint32 len);void S9xNPGetFreezeFile (uint32 len);unsigned long START = 0;bool8 S9xNPConnectToServer (const char *hostname, int port, const char *rom_name){ if (!S9xNPInitialise ()) return (FALSE); S9xNPDisconnect (); NetPlay.MySequenceNum = 0; NetPlay.ServerSequenceNum = 0; NetPlay.Connected = FALSE; NetPlay.Abort = FALSE; NetPlay.Player = 0; NetPlay.Paused = FALSE; NetPlay.PercentageComplete = 0; NetPlay.Socket = 0; if (NetPlay.ServerHostName) free ((char *) NetPlay.ServerHostName); NetPlay.ServerHostName = strdup (hostname); if (NetPlay.ROMName) free ((char *) NetPlay.ROMName); NetPlay.ROMName = strdup (rom_name); NetPlay.Port = port; NetPlay.PendingWait4Sync = FALSE;#ifdef __WIN32__ if (GUI.ClientSemaphore == NULL) GUI.ClientSemaphore = CreateSemaphore (NULL, 0, NP_JOYPAD_HIST_SIZE, NULL); if (NetPlay.ReplyEvent == NULL) NetPlay.ReplyEvent = CreateEvent (NULL, FALSE, FALSE, NULL); _beginthread (S9xNPClientLoop, 0, NULL);#endif return (TRUE);}bool8 S9xNPConnect (){ struct sockaddr_in address; struct hostent *hostinfo; unsigned int addr; address.sin_family = AF_INET; address.sin_port = htons (NetPlay.Port);#ifdef NP_DEBUG printf ("CLIENT: Looking up server's hostname (%s) @%ld\n", NetPlay.ServerHostName, S9xGetMilliTime () - START);#endif S9xNPSetAction ("Looking up server's hostname..."); if ((int) (addr = inet_addr (NetPlay.ServerHostName)) == -1) { if ((hostinfo = gethostbyname (NetPlay.ServerHostName))) { memcpy ((char *)&address.sin_addr, hostinfo->h_addr, hostinfo->h_length); } else { S9xNPSetError ("\Unable to look up server's IP address from hostname.\n\n\Unknown hostname or may be your nameserver isn't set\n\up correctly?"); return (FALSE); } } else { memcpy ((char *)&address.sin_addr, &addr, sizeof (addr)); }#ifdef NP_DEBUG printf ("CLIENT: Creating socket @%ld\n", S9xGetMilliTime () - START);#endif S9xNPSetAction ("Creating network socket..."); if ((NetPlay.Socket = socket (AF_INET, SOCK_STREAM, 0)) < 0) { S9xNPSetError ("Creating network socket failed."); return (FALSE); }#ifdef NP_DEBUG printf ("CLIENT: Trying to connect to server @%ld...\n", S9xGetMilliTime () - START);#endif S9xNPSetAction ("Trying to connect to Snes9X server..."); if (connect (NetPlay.Socket, (struct sockaddr *) &address, sizeof (address)) < 0) { char buf [100];#ifdef __WIN32__ if (WSAGetLastError () == WSAECONNREFUSED)#else if (errno == ECONNREFUSED)#endif { S9xNPSetError ("\Connection to remote server socket refused:\n\n\Is there actually a Snes9X NetPlay server running\n\on the remote machine on this port?"); } else { sprintf (buf, "Connection to server failed with error number %d",#ifdef __WIN32__ WSAGetLastError ()#else errno#endif ); S9xNPDisconnect (); } return (FALSE); } NetPlay.Connected = TRUE;#ifdef NP_DEBUG printf ("CLIENT: Sending 'HELLO' message @%ld...\n", S9xGetMilliTime () - START);#endif S9xNPSetAction ("Sending 'HELLO' message..."); /* Send the server a HELLO packet*/ int len = 7 + 4 + strlen (NetPlay.ROMName) + 1; uint8 *tmp = new uint8 [len]; uint8 *ptr = tmp; *ptr++ = NP_CLNT_MAGIC; *ptr++ = NetPlay.MySequenceNum++; *ptr++ = NP_CLNT_HELLO; WRITE_LONG (ptr, len); ptr += 4;#ifdef __WIN32__ uint32 ft = Settings.FrameTime * 1000; WRITE_LONG (ptr, ft);#else WRITE_LONG (ptr, Settings.FrameTime);#endif ptr += 4; strcpy ((char *) ptr, NetPlay.ROMName); if (!S9xNPSendData (NetPlay.Socket, tmp, len)) { S9xNPSetError ("Sending 'HELLO' message failed."); S9xNPDisconnect (); delete tmp; return (FALSE); } delete tmp;#ifdef NP_DEBUG printf ("CLIENT: Waiting for 'WELCOME' reply from server @%ld...\n", S9xGetMilliTime () - START);#endif S9xNPSetAction ("Waiting for 'HELLO' reply from server..."); uint8 header [7]; if (!S9xNPGetData (NetPlay.Socket, header, 7) || header [0] != NP_SERV_MAGIC || header [1] != 0 || (header [2] & 0x1f) != NP_SERV_HELLO) { S9xNPSetError ("Error in 'HELLO' reply packet received from server."); S9xNPDisconnect (); return (FALSE); }#ifdef NP_DEBUG printf ("CLIENT: Got 'WELCOME' reply @%ld\n", S9xGetMilliTime () - START);#endif len = READ_LONG (&header [3]); if (len > 256) { S9xNPSetError ("Error in 'HELLO' reply packet received from server."); S9xNPDisconnect (); return (FALSE); } uint8 *data = new uint8 [len]; if (!S9xNPGetData (NetPlay.Socket, data, len - 7)) { S9xNPSetError ("Error in 'HELLO' reply packet received from server."); delete data; S9xNPDisconnect (); return (FALSE); } if (data [0] != NP_VERSION) { S9xNPSetError ("\The Snes9X NetPlay server implements a different\n\version of the protocol. Disconnecting."); delete data; S9xNPDisconnect (); return (FALSE); } NetPlay.FrameCount = READ_LONG (&data [2]); if (!(header [2] & 0x80) && strcmp ((char *) data + 4 + 2, NetPlay.ROMName) != 0) { if (!S9xNPLoadROMDialog ((char *) data + 4 + 2)) { delete data; S9xNPDisconnect (); return (FALSE); } } NetPlay.Player = data [1]; delete data; NetPlay.PendingWait4Sync = TRUE; Settings.NetPlay = TRUE; S9xNPResetJoypadReadPos (); NetPlay.ServerSequenceNum = 1; #ifdef NP_DEBUG printf ("CLIENT: Sending 'READY' to server @%ld...\n", S9xGetMilliTime () - START);#endif S9xNPSetAction ("Sending 'READY' to the server..."); return (S9xNPSendReady ((header [2] & 0x80) ? NP_CLNT_WAITING_FOR_ROM_IMAGE : NP_CLNT_READY));}bool8 S9xNPSendReady (uint8 op){ uint8 ready [7]; uint8 *ptr = ready; *ptr++ = NP_CLNT_MAGIC; *ptr++ = NetPlay.MySequenceNum++; *ptr++ = op; WRITE_LONG (ptr, 7); ptr += 4; if (!S9xNPSendData (NetPlay.Socket, ready, 7)) { S9xNPDisconnect (); S9xNPSetError ("Sending 'READY' message failed."); return (FALSE); } return (TRUE);}bool8 S9xNPSendPause (bool8 paused){#ifdef NP_DEBUG printf ("CLIENT: Pause - %s @%ld\n", paused ? "YES" : "NO", S9xGetMilliTime () - START);#endif uint8 pause [7]; uint8 *ptr = pause; *ptr++ = NP_CLNT_MAGIC; *ptr++ = NetPlay.MySequenceNum++; *ptr++ = NP_CLNT_PAUSE | (paused ? 0x80 : 0); WRITE_LONG (ptr, 7); ptr += 4; if (!S9xNPSendData (NetPlay.Socket, pause, 7)) { S9xNPSetError ("Sending 'PAUSE' message failed."); S9xNPDisconnect (); return (FALSE); } return (TRUE);}#ifdef __WIN32__void S9xNPClientLoop (void *){ NetPlay.Waiting4EmulationThread = FALSE; if (S9xNPConnect ()) { S9xClearPause (PAUSE_NETPLAY_CONNECT); while (NetPlay.Connected) { if (S9xNPWaitForHeartBeat ()) { LONG prev; if (!ReleaseSemaphore (GUI.ClientSemaphore, 1, &prev)) {#ifdef NP_DEBUG printf ("CLIENT: ReleaseSemaphore failed - already hit max count (%d) %ld\n", NP_JOYPAD_HIST_SIZE, S9xGetMilliTime () - START);#endif S9xNPSetWarning ("NetPlay: Client may be out of sync with server."); } else { if (!NetPlay.Waiting4EmulationThread && prev == (int) NetPlay.MaxBehindFrameCount) { NetPlay.Waiting4EmulationThread = TRUE; S9xNPSendPause (TRUE); } } } else S9xNPDisconnect (); } } else { S9xClearPause (PAUSE_NETPLAY_CONNECT); }#ifdef NP_DEBUG printf ("CLIENT: Client thread exiting @%ld\n", S9xGetMilliTime () - START);#endif}#endifbool8 S9xNPWaitForHeartBeat (){ uint8 header [3 + 4 + 4 * 5]; while (S9xNPGetData (NetPlay.Socket, header, 3 + 4)) { if (header [0] != NP_SERV_MAGIC) { S9xNPSetError ("Bad magic value from server while waiting for heart-beat message\n"); S9xNPDisconnect (); return (FALSE); } if (header [1] != NetPlay.ServerSequenceNum) { char buf [200]; sprintf (buf, "Unexpected message sequence number from server, expected %d, got %d\n", NetPlay.ServerSequenceNum, header [1]); S9xNPSetWarning (buf); NetPlay.ServerSequenceNum = header [1] + 1; } else NetPlay.ServerSequenceNum++; if ((header [2] & 0x1f) == NP_SERV_JOYPAD) { // Top 2 bits + 1 of opcode is joypad data count. int num = (header [2] >> 6) + 1; if (num) { if (!S9xNPGetData (NetPlay.Socket, header + 3 + 4, num * 4)) { S9xNPSetError ("Error while receiving 'JOYPAD' message."); S9xNPDisconnect (); return (FALSE); } } NetPlay.Frame [NetPlay.JoypadWriteInd] = READ_LONG (&header [3]); for (int i = 0; i < num; i++) { NetPlay.Joypads [NetPlay.JoypadWriteInd][i] = READ_LONG (&header [3 + 4 + i * sizeof (uint32)]); } NetPlay.Paused = (header [2] & 0x20) != 0; NetPlay.JoypadWriteInd = (NetPlay.JoypadWriteInd + 1) % NP_JOYPAD_HIST_SIZE; if (NetPlay.JoypadWriteInd != (NetPlay.JoypadReadInd + 1) % NP_JOYPAD_HIST_SIZE) { //printf ("(%d)", (NetPlay.JoypadWriteInd - NetPlay.JoypadReadInd) % NP_JOYPAD_HIST_SIZE); fflush (stdout); }//printf ("CLIENT: HB: @%d\n", S9xGetMilliTime () - START); return (TRUE); } else { uint32 len = READ_LONG (&header [3]); switch (header [2] & 0x1f) { case NP_SERV_RESET:#ifdef NP_DEBUG printf ("CLIENT: RESET received @%ld\n", S9xGetMilliTime () - START);#endif S9xNPDiscardHeartbeats (); S9xReset (); NetPlay.FrameCount = READ_LONG (&header [3]); S9xNPResetJoypadReadPos (); S9xNPSendReady (); break; case NP_SERV_PAUSE: NetPlay.Paused = (header [2] & 0x20) != 0; break; case NP_SERV_LOAD_ROM:#ifdef NP_DEBUG printf ("CLIENT: LOAD_ROM received @%ld\n", S9xGetMilliTime () - START);#endif S9xNPDiscardHeartbeats (); if (S9xNPLoadROM (len - 7)) S9xNPSendReady (NP_CLNT_LOADED_ROM); break; case NP_SERV_ROM_IMAGE:#ifdef NP_DEBUG printf ("CLIENT: ROM_IMAGE received @%ld\n", S9xGetMilliTime () - START);#endif S9xNPDiscardHeartbeats (); if (S9xNPGetROMImage (len - 7))
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -