800 lines
17 KiB
C
Executable File
800 lines
17 KiB
C
Executable File
/*
|
|
Copyright (C) 1994-1995 Apogee Software, Ltd.
|
|
|
|
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.
|
|
|
|
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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <ctype.h>
|
|
#include <stdarg.h>
|
|
|
|
#ifdef DOS
|
|
#include <conio.h>
|
|
#include <dos.h>
|
|
#include <process.h>
|
|
#include <bios.h>
|
|
#endif
|
|
|
|
#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"
|
|
//MED
|
|
#include "memcheck.h"
|
|
|
|
// GLOBAL VARIABLES
|
|
|
|
// Same as in real mode
|
|
rottcom_t * rottcom;
|
|
int badpacket;
|
|
int consoleplayer;
|
|
byte ROTTpacket[MAXCOMBUFFERSIZE];
|
|
int controlsynctime;
|
|
|
|
// LOCAL VARIABLES
|
|
#ifdef DOS
|
|
static union REGS comregs;
|
|
#endif
|
|
|
|
static int ComStarted=false;
|
|
static int transittimes[MAXPLAYERS];
|
|
|
|
void SyncTime( int client );
|
|
void SetTransitTime( int client, int time );
|
|
|
|
#ifdef PLATFORM_UNIX
|
|
|
|
static int sock = -1;
|
|
|
|
static void ReadUDPPacket()
|
|
{
|
|
rottcom->remotenode = -1;
|
|
}
|
|
|
|
static void WriteUDPPacket()
|
|
{
|
|
}
|
|
|
|
#endif
|
|
|
|
/*
|
|
===============
|
|
=
|
|
= InitROTTNET
|
|
=
|
|
===============
|
|
*/
|
|
|
|
void InitROTTNET (void)
|
|
{
|
|
int netarg;
|
|
long netaddress;
|
|
|
|
if (ComStarted==true)
|
|
return;
|
|
ComStarted=true;
|
|
|
|
#ifdef DOS
|
|
netarg=CheckParm ("net");
|
|
netarg++;
|
|
|
|
netaddress=atol(_argv[netarg]);
|
|
rottcom=(rottcom_t *)netaddress;
|
|
#elif 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)
|
|
{
|
|
#ifdef DOS
|
|
printf("ROTTNET: Communicating on vector %ld\n",(long int)rottcom->intnum);
|
|
#endif
|
|
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 DOS
|
|
int386(rottcom->intnum,&comregs,&comregs);
|
|
#elif PLATFORM_UNIX
|
|
ReadUDPPacket();
|
|
#endif
|
|
|
|
// Is it ready?
|
|
|
|
if (rottcom->remotenode!=-1)
|
|
{
|
|
// calculate crc on packet
|
|
crc=CalculateCRC (&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]);
|
|
|
|
#if 0
|
|
rottcom->command=CMD_OUTQUEBUFFERSIZE;
|
|
int386(rottcom->intnum,&comregs,&comregs);
|
|
SoftError( "outque size=%ld\n",*((short *)&(rottcom->data[0])));
|
|
rottcom->command=CMD_INQUEBUFFERSIZE;
|
|
int386(rottcom->intnum,&comregs,&comregs);
|
|
SoftError( "inque size=%ld\n",*((short *)&(rottcom->data[0])));
|
|
#endif
|
|
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 DOS
|
|
int386(rottcom->intnum,&comregs,&comregs);
|
|
#elif PLATFORM_UNIX
|
|
WriteUDPPacket();
|
|
#endif
|
|
|
|
#if 0
|
|
rottcom->command=CMD_OUTQUEBUFFERSIZE;
|
|
int386(rottcom->intnum,&comregs,&comregs);
|
|
SoftError( "outque size=%ld\n",*((short *)&(rottcom->data[0])));
|
|
rottcom->command=CMD_INQUEBUFFERSIZE;
|
|
int386(rottcom->intnum,&comregs,&comregs);
|
|
SoftError( "inque size=%ld\n",*((short *)&(rottcom->data[0])));
|
|
#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];
|
|
}
|
|
|