ref: ee35eec9bac04ce55e5fe1ae1eeb5428b47d91a8
dir: /src/rt_com.c/
/* Copyright (C) 1994-1995 Apogee Software, Ltd. Copyright (C) 2002-2015 icculus.org, GNU/Linux port Copyright (C) 2017-2018 Steven LeVesque This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. 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. You should have received a copy of the GNU General Public License along with this program. If not, see <https://www.gnu.org/licenses/>. */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <ctype.h> #include <stdarg.h> #include "rt_def.h" #include "_rt_com.h" #include "rt_com.h" #include "rt_util.h" #include "rt_in.h" #include "rt_crc.h" #include "rt_playr.h" #include "isr.h" #include "rt_msg.h" #include "rottnet.h" #include "rt_main.h" #include "rt_net.h" #include "rt_draw.h" //#include "rt_ser.h" // GLOBAL VARIABLES // Same as in real mode rottcom_t * rottcom; int badpacket; int consoleplayer; byte ROTTpacket[MAXCOMBUFFERSIZE]; int controlsynctime; // LOCAL VARIABLES static int ComStarted=false; static int transittimes[MAXPLAYERS]; void SyncTime( int client ); void SetTransitTime( int client, int time ); #ifdef PLATFORM_UNIX static void ReadUDPPacket() { rottcom->remotenode = -1; } static void WriteUDPPacket() { } #endif /* =============== = = InitROTTNET = =============== */ void InitROTTNET (void) { int netarg; if (ComStarted==true) return; ComStarted=true; #if defined(PLATFORM_UNIX) /* server-specific options: -net: enables netplay -server: run as rott server (default port 34858) -port: select a non-default port for server -host: select a non-default ip address to bind to -standalone: run as standalone server -remoteridicule: enable remote ridicule -players: number of players to expect client-specific options: -master: request to have control -net: specifies the host to connect to -port: select a non-default port to connect to */ rottcom = (rottcom_t *) malloc (sizeof(rottcom_t)); memset(rottcom, 0, sizeof(rottcom_t)); rottcom->ticstep = 1; rottcom->gametype = 1; rottcom->remotenode = -1; if (CheckParm("server")) { if (CheckParm("standalone")) { rottcom->consoleplayer = 0; } else { rottcom->consoleplayer = 1; } if (CheckParm("remoteridicule")) { rottcom->remoteridicule = 1; } else { rottcom->remoteridicule = 0; } netarg = CheckParm("players"); if (netarg && netarg < _argc-1) { rottcom->numplayers = atoi(_argv[netarg+1]); } else { rottcom->numplayers = 2; } rottcom->client = 0; } else { rottcom->client = 1; /* consoleplayer will be initialized after connecting */ /* numplayers will be initialized after connecting */ /* remoteridicule will be initialized after connecting */ } /* client-server negotiation protocol, as inspired by ipxsetup.c */ /* Best case: client sends a HereIAm packet. server replies with a YouAre packet, and broadcasts an Info packet to the rest, indicating that the new player has joined. client replies with an IAm packet until all players have joined, the server broadcasts an Info packet to the rest, indicating that another player is needed. once server has enough players, it broadcasts an AllDone packet. In detail: Client state: HereIAm (initial connection) Client sends HereIAm packet, waits for YouAre from server. At timeout, Client resends HereIam When client receives YouAre, client switches to IAm state Client state: IAm Client sends IAm packet, waits for IAmAck from server. At timeout, Client resends IAm When client receives IAmAck, client switches to WaitForDone state. Client state: WaitForDone Client waits for AllDone packet. When client receives AllDone, it sends an AllDoneAck. */ #endif remoteridicule = false; remoteridicule = rottcom->remoteridicule; if (rottcom->ticstep != 1) remoteridicule = false; if (remoteridicule == true) { if (!quiet) printf("ROTTNET: LIVE Remote Ridicule Enabled\n"); } if (!quiet) { printf("ROTTNET: consoleplayer=%ld\n",(long int)rottcom->consoleplayer); } } /* ================ = = ReadPacket = ================ */ boolean ReadPacket (void) { word crc; word sentcrc; // Set command (Get Packet) rottcom->command=CMD_GET; badpacket = 0; // Check to see if a packet is ready #ifdef PLATFORM_UNIX ReadUDPPacket(); #endif // Is it ready? if (rottcom->remotenode!=-1) { // calculate crc on packet crc=CalculateCRC ((byte*)&rottcom->data[0], rottcom->datalength-sizeof(word)); // get crc inside packet sentcrc=*((word *)(&rottcom->data[rottcom->datalength-sizeof(word)])); // are the crcs the same? if (crc!=sentcrc) { badpacket=1; SoftError("BADPKT at %d\n",GetTicCount()); } if (networkgame==false) { rottcom->remotenode=server; } else { if ((IsServer==true) && (rottcom->remotenode>0)) rottcom->remotenode--; } memcpy(&ROTTpacket[0], &rottcom->data[0], rottcom->datalength); // SoftError( "ReadPacket: time=%ld size=%ld src=%ld type=%d\n",GetTicCount(), rottcom->datalength,rottcom->remotenode,rottcom->data[0]); return true; } else // Not ready yet.... return false; } /* ============= = = WritePacket = ============= */ void WritePacket (void * buffer, int len, int destination) { word crc; // set send command rottcom->command=CMD_SEND; // set destination rottcom->remotenode=destination; if (len>(int)(MAXCOMBUFFERSIZE-sizeof(word))) { Error("WritePacket: Overflowed buffer\n"); } // copy local buffer into realmode buffer memcpy((byte *)&(rottcom->data[0]),(byte *)buffer,len); // calculate CRC crc=CalculateCRC (buffer, len); // put CRC into realmode buffer packet *((word *)&rottcom->data[len])=crc; // set size of realmode packet including crc rottcom->datalength=len+sizeof(word); if (*((byte *)buffer)==0) Error("Packet type = 0\n"); if (networkgame==true) { if (IsServer==true) rottcom->remotenode++; // server fix-up } // SoftError( "WritePacket: time=%ld size=%ld src=%ld type=%d\n",GetTicCount(),rottcom->datalength,rottcom->remotenode,rottcom->data[0]); // Send It ! #ifdef PLATFORM_UNIX WriteUDPPacket(); #endif } /* ============= = = ValidSyncPacket = ============= */ boolean ValidSyncPacket ( synctype * sync ) { if (ReadPacket() && (badpacket==0)) { if (((syncpackettype *)&(ROTTpacket[0]))->type==COM_SYNC) { memcpy(&(sync->pkt),&(ROTTpacket[0]),sizeof(sync->pkt)); return true; } } return false; } /* ============= = = SendSyncPacket = ============= */ void SendSyncPacket ( synctype * sync, int dest) { sync->pkt.type=COM_SYNC; sync->sendtime=GetTicCount(); WritePacket( &(sync->pkt.type), sizeof(syncpackettype), dest ); } /* ============= = = SlavePhaseHandler = ============= */ boolean SlavePhaseHandler( synctype * sync ) { boolean done; done=false; switch (sync->pkt.phase) { case SYNC_PHASE1: break; case SYNC_PHASE2: ISR_SetTime(sync->pkt.clocktime); break; case SYNC_PHASE3: sync->pkt.clocktime=GetTicCount(); break; case SYNC_PHASE4: ISR_SetTime(GetTicCount()-sync->pkt.delta); sync->pkt.clocktime=GetTicCount(); break; case SYNC_PHASE5: ISR_SetTime(GetTicCount()-sync->pkt.delta); sync->sendtime=sync->pkt.clocktime; done=true; break; } return done; } /* ============= = = MasterPhaseHandler = ============= */ boolean MasterPhaseHandler( synctype * sync ) { boolean done; done=false; switch (sync->pkt.phase) { case SYNC_PHASE1: sync->pkt.phase=SYNC_PHASE2; sync->pkt.clocktime=GetTicCount()+(sync->deltatime>>1); break; case SYNC_PHASE2: sync->pkt.phase=SYNC_PHASE3; break; case SYNC_PHASE3: sync->pkt.delta=sync->pkt.clocktime-GetTicCount()+(sync->deltatime>>1); sync->pkt.phase=SYNC_PHASE4; break; case SYNC_PHASE4: sync->pkt.phase=SYNC_PHASE5; sync->pkt.delta=sync->pkt.clocktime-GetTicCount()+(sync->deltatime>>1); sync->sendtime=GetTicCount()+SYNCTIME; sync->pkt.clocktime=sync->sendtime; done=true; break; } return done; } /* ============= = = ComSetTime = ============= */ void ComSetTime ( void ) { int i; syncpackettype * syncpacket; boolean done=false; syncpacket=(syncpackettype *)SafeMalloc(sizeof(syncpackettype)); // Sync clocks if (networkgame==true) { if (IsServer==true) { for (i=0; i<numplayers; i++) { if (PlayerInGame(i)==false) continue; if (standalone==true) SyncTime(i); else if (i!=consoleplayer) SyncTime(i); if (standalone==true) printf("ComSetTime: player#%ld\n",(long int)i); } } else { SyncTime(0); } } else // Modem 2-player game { if (consoleplayer==0) SyncTime(server); else SyncTime(server); } if ( ( (networkgame==true) && (IsServer==true) ) || ( (networkgame==false) && (consoleplayer==0) ) ) // Master/Server { int nump; int time; syncpacket->type=COM_START; syncpacket->clocktime=GetTicCount(); controlsynctime=syncpacket->clocktime; if (networkgame==true) nump=numplayers; else nump=1; time = GetTicCount(); for (i=0; i<nump; i++) { WritePacket( &(syncpacket->type), sizeof(syncpackettype), i ); } while (GetTicCount()<time+(VBLCOUNTER/4)) ; for (i=0; i<nump; i++) { WritePacket( &(syncpacket->type), sizeof(syncpackettype), i ); } if (standalone==true) printf("ComSetTime: Start packets sent\n"); } else // Slave/Client { while (done==false) { AbortCheck("ComSetTime aborted as client"); if (ReadPacket() && (badpacket==0)) { memcpy(syncpacket,&(ROTTpacket[0]),sizeof(syncpackettype)); if (syncpacket->type==COM_START) { controlsynctime=syncpacket->clocktime; done=true; } } } } if (standalone==false) { AddMessage("All players synched.",MSG_SYSTEM); ThreeDRefresh(); } SafeFree(syncpacket); // // flush out any extras // while (GetTicCount()<controlsynctime+VBLCOUNTER) { ReadPacket (); } } /* ============= = = InitialMasterSync = ============= */ void InitialMasterSync ( synctype * sync, int client ) { boolean done=false; int i; if (networkgame==true) { for (i=0; i<numplayers; i++) { if (i<=client) continue; sync->pkt.type=COM_SYNC; sync->pkt.phase=SYNC_MEMO; sync->pkt.clocktime=client; SendSyncPacket(sync,i); } } // Initialize send time so as soon as we enter the loop, we send sync->sendtime=GetTicCount()-SYNCTIME; while (done==false) { sync->pkt.phase=SYNC_PHASE0; AbortCheck("Initial sync aborted as master"); if ((sync->sendtime+SYNCTIME) <= GetTicCount()) SendSyncPacket(sync,client); if (ValidSyncPacket(sync)==true) { if (sync->pkt.phase==SYNC_PHASE0) { int time=GetTicCount(); while (time+SYNCTIME>GetTicCount()) { ReadPacket(); } time=GetTicCount(); while (time+SYNCTIME>GetTicCount()) {} done=true; } } } } /* ============= = = InitialSlaveSync = ============= */ void InitialSlaveSync ( synctype * sync ) { boolean done=false; while (done==false) { AbortCheck("Initial sync aborted as slave"); if (ValidSyncPacket(sync)==true) { if (sync->pkt.phase==SYNC_MEMO) { char str[50]="Server is synchronizing player "; char str2[10]; strcat(str,itoa(sync->pkt.clocktime+1,str2,10)); AddMessage(str,MSG_SYSTEM); ThreeDRefresh(); } if (sync->pkt.phase==SYNC_PHASE0) { int time=GetTicCount(); SendSyncPacket(sync,server); while (time+SYNCTIME>GetTicCount()) { ReadPacket(); } done=true; } } } AddMessage("Server is synchronizing your system",MSG_SYSTEM); ThreeDRefresh(); } /* ============= = = SyncTime = ============= */ void SyncTime( int client ) { int dtime[NUMSYNCPHASES]; boolean done; int i; synctype * sync; sync=(synctype *)SafeMalloc(sizeof(synctype)); if ( ((networkgame==true) && (IsServer==true)) || ((networkgame==false) && (consoleplayer==0)) ) { // Master InitialMasterSync ( sync, client ); done=false; // Initial setup for Master // Initialize send time so as soon as we enter the loop, we send sync->pkt.phase=SYNC_PHASE1; sync->sendtime=GetTicCount()-SYNCTIME; while (done==false) { // Master AbortCheck("SyncTime aborted as master"); if ((sync->sendtime+SYNCTIME) <= GetTicCount()) SendSyncPacket(sync,client); while (ValidSyncPacket(sync)==true) { // find average delta sync->deltatime=0; // calculate last delta dtime[sync->pkt.phase]=GetTicCount()-sync->sendtime; for (i=0; i<=sync->pkt.phase; i++) sync->deltatime+=dtime[i]; if (i!=0) sync->deltatime/=i; else Error("SyncTime: this should not happen\n"); done = MasterPhaseHandler( sync ); SendSyncPacket(sync,client); } } } else { // Slave InitialSlaveSync ( sync ); done=false; while (done==false) { // Slave AbortCheck("SyncTime aborted as slave"); while (ValidSyncPacket(sync)==true) { done = SlavePhaseHandler( sync ); if (done==false) SendSyncPacket(sync,server); } } } while (sync->sendtime > GetTicCount()) { while (ReadPacket()) {} } while ((sync->sendtime+SYNCTIME) > GetTicCount()) { } if ( ((networkgame==true) && (IsServer==true)) || ((networkgame==false) && (consoleplayer==0)) ) SetTransitTime( client, (sync->deltatime>>1)); SafeFree(sync); } /* ============= = = SetTransitTime = ============= */ void SetTransitTime( int client, int time ) { transittimes[client]=time; } /* ============= = = GetTransitTime = ============= */ int GetTransitTime( int client ) { return transittimes[client]; }