1269 lines
34 KiB
C
1269 lines
34 KiB
C
/*
|
|
Copyright (C) 1997-2007 ZSNES Team ( zsKnight, _Demo_, pagefault, Nach )
|
|
|
|
http://www.zsnes.com
|
|
http://sourceforge.net/projects/zsnes
|
|
https://zsnes.bountysource.com
|
|
|
|
This program is free software; you can redistribute it and/or
|
|
modify it under the terms of the GNU General Public License
|
|
version 2 as published by the Free Software Foundation.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program; if not, write to the Free Software
|
|
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
*/
|
|
|
|
|
|
|
|
/**********************************************************\
|
|
* ZSNES TCP/IP MODULE FOR NETWORK PLAY *
|
|
* *
|
|
* Coded by the ZSNES team *
|
|
* TCP/IP drivers coded by _Demo_, revised by Pharos *
|
|
* UDP drivers coded by _Demo_, revised by zsKnight *
|
|
* Gameplay netplay implementation by zsKnight *
|
|
* UDP Packet loss/out of order algorithm/implementation *
|
|
* by zsKnight, assistance on normal packets by Pharos *
|
|
\**********************************************************/
|
|
|
|
// UDP Algorithm:
|
|
//
|
|
// UDP Header (1 byte): 1 = Normal Packet w/ reply req, 2 = Reply Packet,
|
|
// 3 = Gameplay Packet (single byte),
|
|
// 4 = Gameplay Packet (larger packet), 5 = Re-request gameplay
|
|
// packet
|
|
//
|
|
// Normal Packets:
|
|
// Note: The actual implementation turned out to be quite different
|
|
// than the below descriptions.
|
|
// First byte contains the packet counter, followed by packet contents.
|
|
// Remote will send a Reply Packet (just contains packet counter)
|
|
// Each packet buffer will have a timer counter which decreases after
|
|
// every 1/60 seconds (value set at start is 60). If this reaches 0
|
|
// that packet will be re-sent and reset the timer value back to 60.
|
|
// If the local side receives the reply packet, it will set the timer
|
|
// counter to -1.
|
|
//
|
|
// Gameplay Packets:
|
|
// Note: Gameplay counter is separate from normal packet counter.
|
|
// Note2: When referring to TCP/IP, it refers to the Normal Packets above.
|
|
// Each packet in TCP/IP will contain a byte counter when UDP is
|
|
// enabled.
|
|
// Each UDP packet will contain a byte counter, the number of packets,
|
|
// then each packet will contain a byte size only if there are > 1
|
|
// packets. If the packet is just one byte long and contains a value<2,
|
|
// it will follow by a byte containing info on how many packets its has
|
|
// been like that for (it will not go beyond 32). If the packet is
|
|
// more than one byte long, it will repeat that packet as the extra
|
|
// packets for the next 3 packets, with the first byte of those packets
|
|
// as the byte counter of that packet, then the second as the size.
|
|
// Also, the send data will be stored in a 256*32 byte buffer in case
|
|
// of packet loss.
|
|
// When receiving, since no UDP packets will exceed 32bytes in length,
|
|
// there will be a 256*32 byte buffer and a 256 byte flag buffer.
|
|
// The flag clearing pointer will move at an offset of 128 from the
|
|
// actual point of the receive buffer. When it receives data from
|
|
// the UDP (or TCP), if the byte count of the data matches the
|
|
// receive pointer, it will just send the data directly and increase the
|
|
// receive pointer. Else it will fill the buffer accordingly based on
|
|
// the send data (for a maximum of 32 bytes). Then if the bit on the
|
|
// flag buffer is set for the current receive pointer, return the
|
|
// appropriate buffer and increase receive pointer.
|
|
// In case of packet loss, if no data has been received for every 500ms, the
|
|
// local side would send a re-send package request. What this would
|
|
// do is let the remote side build up a package containing all the
|
|
// data from the requested send point to the current receive point.
|
|
// A resend request will start off with 0x00,0xFF, then the counter
|
|
// number. A resent packet will start off with 0x00,0xFE, the # of
|
|
// packets, then the packet data (size of packet, data). A resend will
|
|
// only be done if the requested packet is within the past 64 packets.
|
|
// In-game chat will be moved to a separate packet in TCP/IP
|
|
|
|
#ifdef __UNIXSDL__
|
|
#include "gblhdr.h"
|
|
#define closesocket(A) close(A)
|
|
#define CopyMemory(A,B,C) memcpy(A,B,C)
|
|
#define STUB_FUNCTION fprintf(stderr,"STUB: %s at " __FILE__ ", line %d, thread %d\n",__FUNCTION__,__LINE__,getpid())
|
|
#define UINT unsigned int
|
|
#define WORD unsigned short
|
|
#define SOCKET int
|
|
#define SOCKADDR_IN struct sockaddr_in
|
|
#define LPSOCKADDR struct sockaddr*
|
|
#define LPHOSTENT struct hostent*
|
|
#define HOSTENT struct hostent
|
|
#define LPINADDR struct in_addr*
|
|
#define LPIN_ADDR struct in_addr*
|
|
#define SOCKET_ERROR -1
|
|
#define INVALID_SOCKET -1
|
|
#define ioctlsocket ioctl
|
|
#define FD_SET_VAR fd_set
|
|
#else
|
|
#include <stdio.h>
|
|
#include <time.h>
|
|
#include <windows.h>
|
|
#include <winsock.h>
|
|
#define FD_SET_VAR FD_SET
|
|
#endif
|
|
|
|
int RecvPtr;
|
|
int RecvPtr2;
|
|
unsigned char RecvFlags[256];
|
|
unsigned char RecvBuffer[256*32];
|
|
int RecvBufferSize[256];
|
|
|
|
int SendPtr;
|
|
int SendPtr2;
|
|
unsigned char SendBuffer[256*32];
|
|
int SendBufferSize[256];
|
|
|
|
int SendRepeated;
|
|
|
|
int PrevSPacket[16];
|
|
int PrevSData[16*32];
|
|
int PrevSSize[16];
|
|
int PrevSPtr[16];
|
|
|
|
int tcperr;
|
|
unsigned short portval;
|
|
int UDPEnable = 1;
|
|
int UDPConfig = 1;
|
|
int UDPBackTrace = 6;
|
|
int blahblahblah = 0;
|
|
int CounterA = -1;
|
|
int CounterB = -1;
|
|
int UDPMode2 = 0;
|
|
|
|
int packetnum,packetnumhead;
|
|
int packetrecvhead;
|
|
unsigned char packetdata[2048*16];
|
|
unsigned char packetrdata[2048*32];
|
|
int packetconfirm[256];
|
|
int packetreceived[256];
|
|
int packetreceivesize[256];
|
|
int packetsize[256];
|
|
unsigned char cpacketdata[2048+32];
|
|
UINT ConnectAddr;
|
|
int packettimeleft[256];
|
|
int packetresent[256];
|
|
int PacketCounter=0;
|
|
unsigned char CLatencyVal=0;
|
|
|
|
|
|
SOCKET gamesocket; /* tcp socket for the game */
|
|
SOCKET serversocket; /* tcp socket when the server is listening */
|
|
|
|
SOCKET ugamesocket; /* udp socket sending */
|
|
SOCKET userversocket; /* udp socket listening */
|
|
|
|
SOCKADDR_IN serveraddress; /* address of the server */
|
|
SOCKADDR_IN ugameaddress; /* address of the server */
|
|
SOCKADDR_IN userveraddress; /* address of the server */
|
|
|
|
char blah[256];
|
|
char remotehost[256];
|
|
char hostname[50] = "IP N/A";
|
|
|
|
// Function Prototypes
|
|
|
|
int SendData(int dsize,unsigned char *dptr);
|
|
int GetData(int dsize,unsigned char *dptr);
|
|
int GetLeftUDP();
|
|
|
|
/**********************************************************\
|
|
* Initialize the zsnes tcpip module *
|
|
* - no parameters *
|
|
* - return 0 on success other value on error *
|
|
* *
|
|
* - no known side effects *
|
|
\**********************************************************/
|
|
|
|
int InitTCP()
|
|
{
|
|
#ifndef __UNIXSDL__
|
|
WORD versionneeded = MAKEWORD(2,2);
|
|
WSADATA wsadata;
|
|
#endif
|
|
|
|
UDPEnable=0;
|
|
|
|
#ifndef __UNIXSDL__
|
|
/* Startup winsock */
|
|
WSAStartup(versionneeded, &wsadata);
|
|
|
|
/* Verify version number and exit on wrong version */
|
|
if (wsadata.wVersion != versionneeded)
|
|
{
|
|
return(-1);
|
|
}
|
|
serversocket=INVALID_SOCKET;
|
|
#endif
|
|
return(0);
|
|
}
|
|
|
|
|
|
/**********************************************************\
|
|
* Deinitialize the zsnes tcpip module *
|
|
* - no parameters *
|
|
* *
|
|
* - no known side effects *
|
|
\**********************************************************/
|
|
|
|
void DeInitTCP()
|
|
{
|
|
#ifndef __UNIXSDL__
|
|
WSACleanup();
|
|
#endif
|
|
}
|
|
|
|
/**********************************************************\
|
|
* Gets UDP Status through sending data *
|
|
* - no parameters *
|
|
* *
|
|
* - no known side effects *
|
|
\**********************************************************/
|
|
|
|
void GetUDPStatus() {
|
|
int retval;
|
|
|
|
UDPEnable=UDPConfig;
|
|
|
|
if (!UDPEnable){
|
|
blah[0]=0;
|
|
retval = send(gamesocket,blah,1,0);
|
|
gethostname(blah,255);
|
|
retval = send(gamesocket,blah,strlen(blah),0);
|
|
}
|
|
else {
|
|
blah[0]=1;
|
|
retval = send(gamesocket,blah,1,0);
|
|
gethostname(blah,255);
|
|
retval = send(gamesocket,blah,strlen(&blah[1])+1,0);
|
|
}
|
|
|
|
retval = recv(gamesocket,blah,256,0);
|
|
if (blah[0]==0) UDPEnable=0;
|
|
retval = recv(gamesocket,blah,256,0);
|
|
}
|
|
|
|
/**********************************************************\
|
|
* Connect to game server *
|
|
* - parameters *
|
|
* - pointer server name *
|
|
* - server port *
|
|
* - return 0 on success other value on error *
|
|
* *
|
|
* - no known side effects *
|
|
\**********************************************************/
|
|
|
|
int isipval(char *name){
|
|
int i=0;
|
|
|
|
while(name[i]!=0){
|
|
if (!((name[i]=='.') || ((name[i]>='0') && (name[i]<='9'))))
|
|
return(0);
|
|
i++;
|
|
}
|
|
return(1);
|
|
}
|
|
|
|
int ConnectServer(char *servername, unsigned int port)
|
|
{
|
|
char blah[255];
|
|
int retval,i;
|
|
LPHOSTENT host1=NULL;
|
|
int yesip;
|
|
|
|
packetnum = 0;
|
|
packetnumhead = 0;
|
|
packetrecvhead = 0;
|
|
RecvPtr = 0;
|
|
SendPtr = 0;
|
|
RecvPtr2 = 0;
|
|
SendPtr2 = 0;
|
|
|
|
ConnectAddr = 0;
|
|
SendRepeated = 0;
|
|
for (i=0;i<16;i++)
|
|
PrevSPacket[i]=0;
|
|
|
|
/* get host and verify if it is valid */
|
|
yesip = isipval(servername);
|
|
if (!yesip){
|
|
host1 = gethostbyname(servername);
|
|
if (host1 == NULL)
|
|
{
|
|
return(-1);
|
|
}
|
|
}
|
|
|
|
// return(-1);
|
|
if (UDPConfig) UDPEnable = 1;
|
|
|
|
if (UDPEnable)
|
|
{
|
|
PacketCounter=1;
|
|
for (i=0;i<256;i++) {packettimeleft[i]=-1; packetconfirm[i]=1; packetreceived[i]=0; RecvFlags[i]=0;}
|
|
|
|
userveraddress.sin_family = AF_INET;
|
|
ugameaddress.sin_family = AF_INET;
|
|
|
|
if (!yesip)
|
|
{
|
|
ugameaddress.sin_addr = *( (LPIN_ADDR) *host1->h_addr_list );
|
|
}
|
|
else
|
|
{
|
|
ugameaddress.sin_addr.s_addr = inet_addr(servername);
|
|
}
|
|
|
|
ConnectAddr = ugameaddress.sin_addr.s_addr;
|
|
|
|
userveraddress.sin_addr.s_addr = INADDR_ANY;
|
|
|
|
// port++;
|
|
ugameaddress.sin_port = htons((unsigned short) port);
|
|
userveraddress.sin_port = htons((unsigned short) port);
|
|
// port--;
|
|
|
|
userversocket = socket(AF_INET, SOCK_DGRAM,0);
|
|
ugamesocket = socket(AF_INET, SOCK_DGRAM,0);
|
|
|
|
if (ugamesocket == INVALID_SOCKET)
|
|
{
|
|
#ifdef __UNIXSDL__
|
|
STUB_FUNCTION;
|
|
#else
|
|
tcperr=WSAGetLastError();
|
|
sprintf(blah,"Could not initialize UDP(2) : %d",tcperr);
|
|
MessageBox(NULL,blah,"Error",MB_SYSTEMMODAL|MB_OK);
|
|
#endif
|
|
return(-2);
|
|
}
|
|
|
|
if (userversocket == INVALID_SOCKET)
|
|
{
|
|
#ifdef __UNIXSDL__
|
|
STUB_FUNCTION;
|
|
#else
|
|
tcperr=WSAGetLastError();
|
|
sprintf(blah,"Could not initialize UDP(2.5) : %d",tcperr);
|
|
MessageBox(NULL,blah,"Error",MB_SYSTEMMODAL|MB_OK);
|
|
#endif
|
|
return(-2);
|
|
}
|
|
|
|
if (bind(userversocket,(struct sockaddr*)&userveraddress,sizeof(userveraddress))==
|
|
SOCKET_ERROR)
|
|
{
|
|
#ifdef __UNIXSDL__
|
|
STUB_FUNCTION;
|
|
#else
|
|
tcperr=WSAGetLastError();
|
|
sprintf(blah,"Could not initialize UDP(16) : %d",tcperr);
|
|
MessageBox(NULL,blah,"Error",MB_SYSTEMMODAL|MB_OK);
|
|
#endif
|
|
return(-2);
|
|
}
|
|
|
|
|
|
// blah[0]=1;
|
|
// retval = sendto(ugamesocket,blah,1,0,(struct sockaddr*)&ugameaddress,sizeof(struct sockaddr));
|
|
// if (retval == SOCKET_ERROR) return(-1);
|
|
|
|
blah[0]=1;
|
|
SendData(1,blah);
|
|
|
|
// retval = sendto(ugamesocket,blah,5,0,(struct sockaddr*)&ugameaddress,sizeof(struct sockaddr));
|
|
// blah[0]=0;
|
|
// i = sizeof(struct sockaddr);
|
|
// retval = recvfrom(userversocket,blah,5,0,(struct sockaddr*)&userveraddress,&i);
|
|
|
|
// MessageBox(NULL,blah,
|
|
// "Error",
|
|
// MB_SYSTEMMODAL|MB_OK);
|
|
|
|
return(0);
|
|
|
|
// retval = send(gamesocket,blah,1,0);
|
|
// retval = recv(gamesocket,blah,1,0);
|
|
}
|
|
|
|
|
|
/* create the game socket and verify if it is valid */
|
|
gamesocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
|
|
if (gamesocket == INVALID_SOCKET)
|
|
{
|
|
return(-2);
|
|
}
|
|
|
|
|
|
/* initialize server address */
|
|
serveraddress.sin_family = AF_INET;
|
|
if (!yesip)
|
|
serveraddress.sin_addr = *( (LPIN_ADDR) *host1->h_addr_list );
|
|
else
|
|
serveraddress.sin_addr.s_addr = inet_addr(servername);
|
|
|
|
serveraddress.sin_port = htons((unsigned short)port);
|
|
|
|
|
|
/* try to connect to the server */
|
|
retval = connect( gamesocket,
|
|
(LPSOCKADDR)&serveraddress,
|
|
sizeof(struct sockaddr));
|
|
if (retval == SOCKET_ERROR)
|
|
{
|
|
#ifdef __UNIXSDL__
|
|
STUB_FUNCTION;
|
|
#else
|
|
sprintf(blah,"Could not connect to other side");
|
|
MessageBox(NULL,blah,
|
|
"Error",
|
|
MB_SYSTEMMODAL|MB_OK);
|
|
#endif
|
|
|
|
closesocket(gamesocket);
|
|
return(-3);
|
|
}
|
|
|
|
// GetUDPStatus();
|
|
|
|
return(0);
|
|
}
|
|
|
|
int WaitForServer(){
|
|
int i;
|
|
|
|
if (UDPEnable){
|
|
if ((i=GetData(1,blah))){
|
|
if ((i==1) && (blah[0]==1))
|
|
return(1);
|
|
}
|
|
return(0);
|
|
}
|
|
return(1);
|
|
}
|
|
|
|
|
|
/**********************************************************\
|
|
* Disconnect from game server *
|
|
* - no parameters *
|
|
* *
|
|
* - no known side effects *
|
|
\**********************************************************/
|
|
|
|
void Disconnect()
|
|
{
|
|
if (UDPEnable)
|
|
{
|
|
closesocket(ugamesocket);
|
|
closesocket(userversocket);
|
|
return;
|
|
}
|
|
PacketCounter=0;
|
|
closesocket(gamesocket);
|
|
}
|
|
|
|
|
|
/**********************************************************\
|
|
* Start the game server *
|
|
* - parameters *
|
|
- port number
|
|
* - return 0 on success other value on error *
|
|
* *
|
|
* - no known side effects *
|
|
\**********************************************************/
|
|
|
|
int StartServerCycle(unsigned short port)
|
|
{
|
|
int retval,i;
|
|
|
|
portval = port;
|
|
packetnum = 0;
|
|
packetnumhead = 0;
|
|
packetrecvhead = 0;
|
|
ConnectAddr = 0;
|
|
SendRepeated = 0;
|
|
RecvPtr = 0;
|
|
SendPtr = 0;
|
|
RecvPtr2 = 0;
|
|
SendPtr2 = 0;
|
|
|
|
for (i=0;i<16;i++)
|
|
PrevSPacket[i]=0;
|
|
|
|
|
|
if (UDPConfig) UDPEnable = 1;
|
|
|
|
if (UDPEnable)
|
|
{
|
|
/* get host and verify if it is valid */
|
|
PacketCounter=1;
|
|
for (i=0;i<256;i++) {packettimeleft[i]=-1; packetconfirm[i]=1; packetreceived[i]=0; RecvFlags[i]=0;}
|
|
|
|
userveraddress.sin_family = AF_INET;
|
|
ugameaddress.sin_family = AF_INET;
|
|
|
|
userveraddress.sin_addr.s_addr = INADDR_ANY;
|
|
ugameaddress.sin_addr.s_addr = INADDR_ANY;
|
|
|
|
// portval++;
|
|
ugameaddress.sin_port = htons((unsigned short) portval);
|
|
userveraddress.sin_port = htons((unsigned short) portval);
|
|
// portval--;
|
|
|
|
userversocket = socket(AF_INET, SOCK_DGRAM,0);
|
|
ugamesocket = socket(AF_INET, SOCK_DGRAM,0);
|
|
|
|
if (userversocket == INVALID_SOCKET)
|
|
{
|
|
#ifdef __UNIXSDL__
|
|
STUB_FUNCTION;
|
|
#else
|
|
tcperr=WSAGetLastError();
|
|
sprintf(blah,"Could not initialize UDP(5) : %d",tcperr);
|
|
MessageBox(NULL,blah,"Error",MB_SYSTEMMODAL|MB_OK);
|
|
#endif
|
|
return(-2);
|
|
}
|
|
if (bind(userversocket,(struct sockaddr*)&userveraddress,sizeof(userveraddress))==
|
|
SOCKET_ERROR)
|
|
{
|
|
#ifdef __UNIXSDL__
|
|
STUB_FUNCTION;
|
|
#else
|
|
tcperr=WSAGetLastError();
|
|
sprintf(blah,"Could not initialize UDP(6) : %d",tcperr);
|
|
MessageBox(NULL,blah,"Error",MB_SYSTEMMODAL|MB_OK);
|
|
#endif
|
|
return(-2);
|
|
}
|
|
|
|
|
|
blah[0]=2;
|
|
blah[1]='C';
|
|
blah[2]='B';
|
|
blah[3]='A';
|
|
blah[4]=0;
|
|
|
|
|
|
// retval = recvfrom(userversocket,blah,5,0,
|
|
// (struct sockaddr *)&userveraddress,&socklen);
|
|
|
|
ugameaddress.sin_addr.s_addr = userveraddress.sin_addr.s_addr;
|
|
|
|
ugamesocket = socket(AF_INET, SOCK_DGRAM,0);
|
|
|
|
return(0);
|
|
|
|
// retval = send(gamesocket,blah,1,0);
|
|
// retval = recv(gamesocket,blah,1,0);
|
|
|
|
}
|
|
|
|
/* Create the listen socket */
|
|
serversocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
|
|
if (serversocket == INVALID_SOCKET)
|
|
{
|
|
#ifndef __UNIXSDL__
|
|
tcperr=WSAGetLastError();
|
|
#endif
|
|
|
|
return(-1);
|
|
}
|
|
|
|
serveraddress.sin_family = AF_INET;
|
|
serveraddress.sin_addr.s_addr = INADDR_ANY;
|
|
serveraddress.sin_port = htons(port);
|
|
|
|
/* bind name and socket */
|
|
retval = bind(serversocket,
|
|
(LPSOCKADDR)&serveraddress,
|
|
sizeof(struct sockaddr));
|
|
if (retval == SOCKET_ERROR)
|
|
{
|
|
#ifndef __UNIXSDL__
|
|
tcperr=WSAGetLastError();
|
|
#endif
|
|
closesocket(serversocket);
|
|
return(-2);
|
|
}
|
|
|
|
/* setup socket to listen */
|
|
retval = listen(serversocket, SOMAXCONN);
|
|
if (retval == SOCKET_ERROR)
|
|
{
|
|
#ifndef __UNIXSDL__
|
|
tcperr=WSAGetLastError();
|
|
#endif
|
|
closesocket(serversocket);
|
|
return(-3);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int acceptzuser()
|
|
{
|
|
if (UDPEnable)
|
|
{
|
|
return(0);
|
|
}
|
|
|
|
/* wait for connection */
|
|
|
|
gamesocket = accept(serversocket, NULL, NULL);
|
|
if (gamesocket == INVALID_SOCKET)
|
|
{
|
|
#ifndef __UNIXSDL__
|
|
tcperr=WSAGetLastError();
|
|
#endif
|
|
closesocket(serversocket);
|
|
serversocket=-1;
|
|
return(-1);
|
|
}
|
|
|
|
// GetUDPStatus();
|
|
|
|
return(0);
|
|
}
|
|
|
|
int ServerCheckNewClient()
|
|
{
|
|
FD_SET_VAR zrf;
|
|
struct timeval nto;
|
|
int r;
|
|
|
|
if (UDPEnable)
|
|
{
|
|
r=GetData(256,blah);
|
|
if (r == -1) return(-1);
|
|
if (r > 0){
|
|
ugameaddress.sin_addr.s_addr=userveraddress.sin_addr.s_addr;
|
|
ConnectAddr = ugameaddress.sin_addr.s_addr;
|
|
blah[0]=1;
|
|
r=SendData(1,blah);
|
|
return(1);
|
|
}
|
|
return(0);
|
|
}
|
|
|
|
if(serversocket == INVALID_SOCKET)
|
|
{
|
|
return(-1);
|
|
}
|
|
nto.tv_sec=0;
|
|
nto.tv_usec=0; /* return immediately */
|
|
|
|
FD_ZERO(&zrf);
|
|
FD_SET(serversocket,&zrf);
|
|
r=select(serversocket+1,&zrf,0,0,&nto);
|
|
|
|
if(r == -1)
|
|
{
|
|
#ifndef __UNIXSDL__
|
|
tcperr=WSAGetLastError();
|
|
#endif
|
|
return(-2);
|
|
}
|
|
if(r == 0)
|
|
{
|
|
return(0);
|
|
}
|
|
if(FD_ISSET(serversocket,&zrf))
|
|
{
|
|
return 1;
|
|
}
|
|
return(0);
|
|
|
|
}
|
|
|
|
|
|
/**********************************************************\
|
|
* Stop the game server *
|
|
* - no parameters *
|
|
* *
|
|
* - no known side effects *
|
|
\**********************************************************/
|
|
|
|
void StopServer()
|
|
{
|
|
if (UDPEnable)
|
|
{
|
|
closesocket(ugamesocket);
|
|
closesocket(userversocket);
|
|
return;
|
|
}
|
|
PacketCounter=0;
|
|
closesocket(gamesocket);
|
|
closesocket(serversocket);
|
|
}
|
|
|
|
|
|
/**********************************************************\
|
|
* Send data *
|
|
* - parameters : *
|
|
* - size of data *
|
|
* - pointer to data *
|
|
* - return 0 on success other value on error *
|
|
* *
|
|
* - side effects : *
|
|
* - close the socket on error *
|
|
\**********************************************************/
|
|
|
|
int PacketReceive()
|
|
{
|
|
int dataleft,i,i2,i3,i4,i5,i6,i7,retval;
|
|
|
|
dataleft=GetLeftUDP();
|
|
if (dataleft<=0) return(dataleft);
|
|
i = sizeof(userveraddress);
|
|
retval = recvfrom(userversocket,cpacketdata,2048+32,0,(struct sockaddr *)&userveraddress,&i);
|
|
if ((ConnectAddr!=0) && (ConnectAddr != userveraddress.sin_addr.s_addr)) return(0);
|
|
if (retval == SOCKET_ERROR)
|
|
{
|
|
closesocket(ugamesocket);
|
|
return(-1);
|
|
}
|
|
if ((cpacketdata[0]==1) && (retval>0)) {
|
|
i=(unsigned char)cpacketdata[1];
|
|
blah[0]=2;
|
|
blah[1]=cpacketdata[1];
|
|
sendto(ugamesocket,blah,2,0,(struct sockaddr *)&ugameaddress,sizeof(ugameaddress));
|
|
if (!packetreceived[i]){
|
|
packetreceived[i]=1;
|
|
packetreceivesize[i]=retval-2;
|
|
CopyMemory(&(packetrdata[2048*(i & 0x0F)]),&(cpacketdata[2]),retval-2);
|
|
}
|
|
}
|
|
if (cpacketdata[0]==2){
|
|
packetconfirm[cpacketdata[1]]=1;
|
|
while ((packetconfirm[packetnumhead]) && (packetnum!=packetnumhead))
|
|
packetnumhead=(packetnumhead+1) & 0xFF;
|
|
}
|
|
|
|
if ((cpacketdata[0]==16) && (cpacketdata[1]!=SendPtr)){
|
|
i=cpacketdata[1];
|
|
cpacketdata[0]=17;
|
|
cpacketdata[2]=SendPtr;
|
|
i3=3;
|
|
while (i!=SendPtr){
|
|
cpacketdata[i3]=SendBufferSize[i];
|
|
i3++;
|
|
for (i4=0;i4<SendBufferSize[i];i4++){
|
|
cpacketdata[i3]=SendBuffer[i4+(i << 5)];
|
|
i3++;
|
|
}
|
|
i=(i+1) & 0xFF;
|
|
}
|
|
sendto(ugamesocket,cpacketdata,i3,0,(struct sockaddr *)&ugameaddress,sizeof(ugameaddress));
|
|
return(0);
|
|
}
|
|
|
|
if (cpacketdata[0]==17){
|
|
i2=cpacketdata[1];
|
|
i3=3;
|
|
while (i2!=cpacketdata[2]){
|
|
i4=cpacketdata[i3];
|
|
i3++;
|
|
RecvFlags[i2]=1;
|
|
RecvBufferSize[i2]=i4;
|
|
for (i5=0;i5<i4;i5++){
|
|
RecvBuffer[(i2 << 5)+i5]=cpacketdata[i3];
|
|
i3++;
|
|
}
|
|
i2=(i2+1) & 0xFF;
|
|
}
|
|
}
|
|
|
|
i2=RecvPtr+(RecvPtr2 << 8);
|
|
i3=(cpacketdata[2]+(cpacketdata[3] << 8))-i2;
|
|
if (i3<0) i3+=65536;
|
|
|
|
if ((((cpacketdata[0] & 0xF7)==4) || ((cpacketdata[0] & 0xF7)==5))
|
|
&& ((i3>=0) && (i3<=127))) {
|
|
|
|
|
|
CLatencyVal=cpacketdata[1];
|
|
i=cpacketdata[2];
|
|
i3=0;
|
|
|
|
if ((cpacketdata[0] & 0x07)==4){
|
|
for (i2=0;i2<cpacketdata[4];i2++){
|
|
RecvBuffer[((i-i2) & 0xFF) << 5] = 0;
|
|
RecvBuffer[(((i-i2) & 0xFF) << 5)+1] = CLatencyVal;
|
|
RecvFlags[((i-i2) & 0xFF)] = 1;
|
|
RecvBufferSize[((i-i2) & 0xFF)] = 2;
|
|
}
|
|
i3+=5;
|
|
} else {
|
|
for (i2=0;i2<cpacketdata[4];i2++){
|
|
RecvBuffer[(i << 5) + i2] = cpacketdata[i2+5];
|
|
}
|
|
RecvFlags[i] = 1;
|
|
RecvBufferSize[i] = cpacketdata[4];
|
|
i3+=cpacketdata[4]+5;
|
|
}
|
|
if (cpacketdata[0] & 0x08){
|
|
retval=cpacketdata[i3];
|
|
i3++;
|
|
for (i2=0;i2<retval;i2++){
|
|
i=cpacketdata[i3];
|
|
i5=cpacketdata[i3+1];
|
|
i3+=2;
|
|
RecvFlags[i] = 1;
|
|
RecvBufferSize[i] = i5;
|
|
if ((cpacketdata[i3]==0) && (i5==3)){
|
|
i7 = cpacketdata[i3+2];
|
|
for (i6=0;i6<i7;i6++){
|
|
RecvFlags[(i-i6) & 0xFF] = 1;
|
|
RecvBufferSize[(i-i6) & 0xFF] = 2;
|
|
}
|
|
for (i4=0;i4<i5;i4++){
|
|
for (i6=0;i6<i7;i6++)
|
|
RecvBuffer[(((i-i6) & 0xFF) << 5)+i4]=cpacketdata[i3];
|
|
i3++;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for (i4=0;i4<i5;i4++){
|
|
RecvBuffer[(i << 5)+i4]=cpacketdata[i3];
|
|
i3++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return(0);
|
|
}
|
|
|
|
|
|
void PacketResend()
|
|
{
|
|
int i;
|
|
for (i=0;i<256;i++) {
|
|
if ((packettimeleft[i]==0) && (packetconfirm[i]==0)){
|
|
packettimeleft[i]=180;
|
|
if (packetresent[i]==1) packettimeleft[i]=60;
|
|
if (packetresent[i]==2) packettimeleft[i]=90;
|
|
if (packetsize[i]>512) packettimeleft[packetnum]=60*3;
|
|
packetresent[i]++;
|
|
CopyMemory(&(cpacketdata[2]),&(packetdata[2048*(i & 0x0F)]),packetsize[i]);
|
|
cpacketdata[0]=1;
|
|
cpacketdata[1]=(char)i;
|
|
sendto(ugamesocket,cpacketdata,packetsize[i]+2,0,(struct sockaddr *)&ugameaddress,sizeof(ugameaddress));
|
|
}
|
|
}
|
|
}
|
|
|
|
extern void UpdateVFrame(void);
|
|
|
|
int SendData(int dsize,unsigned char *dptr)
|
|
{
|
|
int retval;
|
|
|
|
if (UDPEnable){
|
|
/* retval = sendto(ugamesocket,dptr,dsize,0,(struct sockaddr *)&ugameaddress,sizeof(ugameaddress));
|
|
if (retval == SOCKET_ERROR)
|
|
{
|
|
closesocket(gamesocket);
|
|
return(-1);
|
|
}
|
|
return(0); */
|
|
|
|
if (((packetnum-packetnumhead) & 0xFF) >= 15){
|
|
// sprintf(message1,"Packet Overflow.");
|
|
// MessageBox (NULL, message1, "Init Error" , MB_ICONERROR );
|
|
|
|
// wait for receive packet, call JoyRead while waiting
|
|
while (((packetnum-packetnumhead) & 0xFF) >= 15){
|
|
PacketResend();
|
|
PacketReceive();
|
|
UpdateVFrame();
|
|
while ((packetconfirm[packetnumhead]) && (packetnum!=packetnumhead))
|
|
packetnumhead=(packetnumhead+1) & 0xFF;
|
|
}
|
|
}
|
|
CopyMemory(&(cpacketdata[2]),dptr,dsize);
|
|
CopyMemory(&(packetdata[2048*(packetnum & 0x0F)]),dptr,dsize);
|
|
packetsize[packetnum]=dsize;
|
|
packetconfirm[packetnum]=0;
|
|
cpacketdata[0]=1;
|
|
cpacketdata[1]=(char)packetnum;
|
|
retval = sendto(ugamesocket,cpacketdata,dsize+2,0,(struct sockaddr *)&ugameaddress,sizeof(ugameaddress));
|
|
packettimeleft[packetnum]=60;
|
|
if (dsize>512) packettimeleft[packetnum]=90;
|
|
packetresent[packetnum]=1;
|
|
packetnum=(packetnum+1) & 0xFF;
|
|
if (retval == SOCKET_ERROR)
|
|
{
|
|
closesocket(ugamesocket);
|
|
return(-1);
|
|
}
|
|
return(0);
|
|
}
|
|
|
|
/* send data with the socket */
|
|
retval = send(gamesocket,dptr,dsize,0);
|
|
if (retval == SOCKET_ERROR)
|
|
{
|
|
closesocket(gamesocket);
|
|
return(-1);
|
|
}
|
|
return(0);
|
|
}
|
|
|
|
extern int PacketSendSize;
|
|
extern unsigned char PacketSendArray[2048+256];
|
|
|
|
int SendDataNop()
|
|
{
|
|
return (SendData(PacketSendSize,PacketSendArray));
|
|
}
|
|
|
|
|
|
/**********************************************************\
|
|
* Send data UDP *
|
|
* - parameters : *
|
|
* - size of data *
|
|
* - pointer to data *
|
|
* - return 0 on success other value on error *
|
|
* *
|
|
* - side effects : *
|
|
* - close the socket on error *
|
|
\**********************************************************/
|
|
|
|
int AttachEnd(int psb){
|
|
int i,i2,i3,ps;
|
|
//int PrevSPacket[4];
|
|
//int PrevSData[4*32];
|
|
//int PrevSSize[4];
|
|
|
|
ps=psb;
|
|
i2=0;
|
|
for (i=0;i<(UDPBackTrace-1);i++){
|
|
if (PrevSPacket[i]) i2++;
|
|
}
|
|
// if (PrevSPacket[0]) i2=0;
|
|
if (i2){
|
|
cpacketdata[0]+=8;
|
|
cpacketdata[ps]=(char)i2;
|
|
ps++;
|
|
for (i=0;i<(UDPBackTrace-1);i++){
|
|
if (PrevSPacket[i]){
|
|
cpacketdata[ps]=PrevSPtr[i];
|
|
cpacketdata[ps+1]=PrevSSize[i];
|
|
ps+=2;
|
|
for (i3=0;i3<PrevSSize[i];i3++){
|
|
cpacketdata[ps]=PrevSData[i*32+i3];
|
|
ps++;
|
|
}
|
|
}
|
|
}
|
|
for (i=0;i<(UDPBackTrace-2);i++){
|
|
PrevSPacket[i]=PrevSPacket[i+1];
|
|
PrevSSize[i]=PrevSSize[i+1];
|
|
PrevSPtr[i]=PrevSPtr[i+1];
|
|
CopyMemory(&(PrevSData[i*32]),&(PrevSData[i*32+32]),32);
|
|
}
|
|
}
|
|
|
|
return ps;
|
|
}
|
|
|
|
int SendDataUDP(int dsize,unsigned char *dptr)
|
|
{
|
|
int retval,i;
|
|
int packetsize;
|
|
|
|
|
|
|
|
// return (SendData(dsize,dptr));
|
|
|
|
if (UDPEnable){
|
|
|
|
/*int SendPtr;
|
|
char SendBuffer[256*32];
|
|
char SendBufferSize[256];*/
|
|
blahblahblah++;
|
|
|
|
packetsize = 0;
|
|
|
|
for (i=0;i<dsize;i++)
|
|
SendBuffer[SendPtr*32+i]=dptr[i];
|
|
SendBufferSize[SendPtr]=dsize;
|
|
|
|
if ((dsize == 2) && (dptr[0]<=1)){
|
|
if (SendRepeated < 32) SendRepeated++;
|
|
cpacketdata[0]=4;
|
|
cpacketdata[1]=dptr[1];
|
|
cpacketdata[2]=(char)SendPtr;
|
|
cpacketdata[3]=(char)SendPtr2;
|
|
cpacketdata[4]=(char)SendRepeated;
|
|
packetsize=5;
|
|
packetsize=AttachEnd(packetsize);
|
|
PrevSPacket[UDPBackTrace-2]=0;
|
|
SendPtr=(SendPtr+1) & 0xFF;
|
|
if (!SendPtr) SendPtr2=(SendPtr2+1) & 0xFF;
|
|
retval = sendto(ugamesocket,cpacketdata,packetsize,0,(struct sockaddr *)&ugameaddress,sizeof(ugameaddress));
|
|
if (retval == SOCKET_ERROR)
|
|
{
|
|
closesocket(gamesocket);
|
|
return(-1);
|
|
}
|
|
} else {
|
|
if (SendRepeated){
|
|
PrevSPacket[UDPBackTrace-2]=1;
|
|
PrevSSize[UDPBackTrace-2]=3;
|
|
PrevSData[(UDPBackTrace-2)*32]=0;
|
|
PrevSData[(UDPBackTrace-2)*32+1]=dptr[1];
|
|
PrevSData[(UDPBackTrace-2)*32+2]=SendRepeated;
|
|
PrevSPtr[UDPBackTrace-2]=(SendPtr-1) & 0xFF;
|
|
}
|
|
SendRepeated=0;
|
|
cpacketdata[0]=5;
|
|
cpacketdata[1]=dptr[1];
|
|
cpacketdata[2]=SendPtr;
|
|
cpacketdata[3]=SendPtr2;
|
|
cpacketdata[4]=dsize;
|
|
packetsize=5;
|
|
for (i=0;i<dsize;i++)
|
|
cpacketdata[i+5]=dptr[i];
|
|
packetsize+=dsize;
|
|
packetsize=AttachEnd(packetsize);
|
|
|
|
PrevSPacket[UDPBackTrace-2]=1;
|
|
PrevSSize[UDPBackTrace-2]=dsize;
|
|
for (i=0;i<dsize;i++)
|
|
PrevSData[(UDPBackTrace-2)*32+i]=dptr[i];
|
|
PrevSPtr[UDPBackTrace-2]=SendPtr;
|
|
|
|
SendPtr=(SendPtr+1) & 0xFF;
|
|
if (!SendPtr) SendPtr2=(SendPtr2+1) & 0xFF;
|
|
retval = sendto(ugamesocket,cpacketdata,packetsize,0,(struct sockaddr *)&ugameaddress,sizeof(ugameaddress));
|
|
if (retval == SOCKET_ERROR)
|
|
{
|
|
closesocket(gamesocket);
|
|
return(-1);
|
|
}
|
|
}
|
|
return(0);
|
|
}
|
|
|
|
/* send data with the socket */
|
|
retval = sendto(gamesocket,dptr,dsize,0,(struct sockaddr *) &ugameaddress,sizeof(struct sockaddr));
|
|
if (retval == SOCKET_ERROR)
|
|
{
|
|
closesocket(gamesocket);
|
|
return(-1);
|
|
}
|
|
return(0);
|
|
}
|
|
|
|
int SendDataUDPNop()
|
|
{
|
|
return (SendDataUDP(PacketSendSize,PacketSendArray));
|
|
}
|
|
|
|
/**********************************************************\
|
|
* Get data left *
|
|
* - return size left on success negative value on error *
|
|
* *
|
|
* - side effects : *
|
|
* - close the socket on error *
|
|
\**********************************************************/
|
|
|
|
int GetLeft()
|
|
{
|
|
int retval;
|
|
int tempsize;
|
|
retval = ioctlsocket(gamesocket,FIONREAD,&tempsize);
|
|
if (retval == SOCKET_ERROR)
|
|
{
|
|
closesocket(gamesocket);
|
|
return(-1);
|
|
}
|
|
return(tempsize);
|
|
}
|
|
|
|
int GetLeftUDP()
|
|
{
|
|
FD_SET_VAR zrf;
|
|
struct timeval nto;
|
|
int r;
|
|
|
|
nto.tv_sec=0;
|
|
nto.tv_usec=0; /* return immediately */
|
|
|
|
FD_ZERO(&zrf);
|
|
FD_SET(userversocket,&zrf);
|
|
r=select(userversocket+1,&zrf,0,0,&nto);
|
|
|
|
if (r == SOCKET_ERROR)
|
|
{
|
|
closesocket(userversocket);
|
|
return(-1);
|
|
}
|
|
return(r);
|
|
}
|
|
|
|
/**********************************************************\
|
|
* Receive data *
|
|
* - parameters : *
|
|
* - size of data *
|
|
* - pointer to data *
|
|
* - return size on success negative value on error *
|
|
* *
|
|
* - side effects : *
|
|
* - close the socket on error *
|
|
\**********************************************************/
|
|
|
|
int GetData(int dsize,unsigned char *dptr)
|
|
{
|
|
int retval,i;
|
|
int dataleft;
|
|
|
|
retval=0;
|
|
|
|
// Temporary UDP routines
|
|
if (UDPEnable) {
|
|
|
|
PacketResend();
|
|
PacketReceive();
|
|
|
|
i=packetrecvhead;
|
|
if (packetreceived[i]){
|
|
CopyMemory(dptr,&(packetrdata[2048*(i & 0x0F)]),packetreceivesize[i]);
|
|
retval = packetreceivesize[i];
|
|
packetreceived[(i+128) & 0xFF]=0;
|
|
packetrecvhead=(packetrecvhead+1) & 0xFF;
|
|
return(retval);
|
|
}
|
|
|
|
i=RecvPtr;
|
|
if ((RecvFlags[i]) && (UDPMode2)){
|
|
CopyMemory(dptr,&(RecvBuffer[32*i]),RecvBufferSize[i]);
|
|
retval = RecvBufferSize[i];
|
|
RecvFlags[(i+128) & 0xFF]=0;
|
|
RecvPtr=(RecvPtr+1) & 0xFF;
|
|
if (!RecvPtr) RecvPtr2=(RecvPtr2+1) & 0xFF;
|
|
CounterA=90;
|
|
return(retval);
|
|
}
|
|
|
|
if ((CounterA==0) & (UDPMode2)){
|
|
// Send 16+RecvPtr
|
|
cpacketdata[0]=16;
|
|
cpacketdata[1]=RecvPtr;
|
|
sendto(ugamesocket,cpacketdata,2,0,(struct sockaddr *)&ugameaddress,sizeof(ugameaddress));
|
|
CounterA=90;
|
|
return(0);
|
|
}
|
|
|
|
return(0);
|
|
}
|
|
|
|
dataleft=GetLeft();
|
|
if(dataleft==0) return(0);
|
|
|
|
if(dataleft<dsize)
|
|
{
|
|
dsize=dataleft;
|
|
}
|
|
/* get data with the socket */
|
|
retval = recv(gamesocket,dptr,dsize,0);
|
|
if (retval == SOCKET_ERROR)
|
|
{
|
|
closesocket(gamesocket);
|
|
return(-1);
|
|
}
|
|
return(retval);
|
|
}
|
|
|
|
extern unsigned char PacketRecvArray[2048+256];
|
|
|
|
int GetDataNop()
|
|
{
|
|
return (GetData(2048,PacketRecvArray));
|
|
}
|
|
|
|
void GetHostName()
|
|
{
|
|
HOSTENT* phe;
|
|
|
|
if (!InitTCP()){
|
|
|
|
strcpy(hostname,"YOUR IP: ");
|
|
gethostname(blah,255);
|
|
phe = gethostbyname(blah);
|
|
strcpy(blah, inet_ntoa(*(struct in_addr*)phe->h_addr));
|
|
strcat(hostname,blah);
|
|
}
|
|
}
|
|
|
|
void UDPWait1Sec(){
|
|
CounterB=60;
|
|
while (CounterB>0)
|
|
UpdateVFrame();
|
|
}
|
|
|
|
void UDPClearVars(){
|
|
int i;
|
|
CounterA=-1;
|
|
RecvPtr = 0;
|
|
SendPtr = 0;
|
|
for (i=0;i<16;i++)
|
|
PrevSPacket[i]=0;
|
|
for (i=0;i<256;i++)
|
|
RecvFlags[i]=0;
|
|
}
|
|
|
|
void UDPEnableMode(){
|
|
UDPMode2=1;
|
|
}
|
|
|
|
void UDPDisableMode(){
|
|
UDPMode2=0;
|
|
}
|
|
|
|
void WinErrorA2(void){
|
|
#ifdef __UNIXSDL__
|
|
STUB_FUNCTION;
|
|
#else
|
|
char message1[256];
|
|
sprintf(message1,"Failed waiting for checksum.");
|
|
MessageBox (NULL, message1, "Init Error" , MB_ICONERROR );
|
|
#endif
|
|
}
|
|
|
|
void WinErrorB2(void){
|
|
#ifdef __UNIXSDL__
|
|
STUB_FUNCTION;
|
|
#else
|
|
char message1[256];
|
|
sprintf(message1,"Failed waiting for confirmation.");
|
|
MessageBox (NULL, message1, "Init Error" , MB_ICONERROR );
|
|
#endif
|
|
}
|
|
|
|
void WinErrorC2(void){
|
|
#ifdef __UNIXSDL__
|
|
STUB_FUNCTION;
|
|
#else
|
|
char message1[256];
|
|
sprintf(message1,"Failed waiting for confirmation(B).");
|
|
MessageBox (NULL, message1, "Init Error" , MB_ICONERROR );
|
|
#endif
|
|
}
|
|
|
|
|
|
|