Redline/source/tracker.cpp
2016-04-04 23:17:52 +02:00

905 lines
29 KiB
C++

#include <AmbrosiaTools/reggie.h>
#include <AmbrosiaTools/network_tool.h>
#include <AmbrosiaTools/platform.h>
#include <AmbrosiaTools/lsockets.h>
#include <stdio.h>
#include <string.h>
#include "tracker.h"
#include "gameinitexit.h"
#include "gametime.h"
#include "carphysics.h"
#include "config.h"
#include "text.h"
#include "gameframe.h"
#include "error.h"
#include "initexit.h"
#include "network.h"
#include "interfaceutil.h"
#include "gamesystem.h"
#include "screen.h"
ReggieRef gReggieRef=NULL;
int gReggieConnected=false;
int gReggieSearching=false;
int gReggieLastUpdateGInfoVersion;
int gHasCheckedLonelyServer;
int gReggiePortMapped=false;
int gReggieCounting=false;
float gLastQuery=-1000;
float gConnectionTime;
float gReportedIdle=false;
char gMOTD[1024];
extern NT_SessionRef gSessionRef;
extern NetworkDatagramRef NT_GetSessionEndpoint(NT_SessionRef ref);
#define kReggieUpdateInterval 1
int CouldMyNATCauseMyServerToBeLonely() {
NetworkAddress local;
DatagramGetAddress(NT_GetSessionEndpoint(gSessionRef),&local);
ReggieNetworkType network = ReggieProbeCheck(gReggieRef,false,&local,nil);
return((network != kReggieNetworkOffline) && (network <=kReggieNetworkProtective) ? true : false);
}
#define kMessageInputBufferSize 8
#define kMaxMessageSize 1024
char gMessageInputBuffer[kMessageInputBufferSize][kMaxMessageSize];
int gMessageInputBufferPos=0;
int gCheckingVersion=false;
char *gRedlineURL=NULL;
char *gAutoUpdateURL=NULL;
void TrackerStartVersionCheck()
{
if(NetworkStackGetActive(eNetworkStackTCPIP))
{
ReggieVersionBegin(nil,"REDLINE");
gCheckingVersion = true;
}
}
int TrackerVersionCheck(char **newVersion)
{
if(gCheckingVersion)
{
HashRef hashRef=ReggieVersionCheck(nil,"REDLINE");
if(hashRef)
{
if(gConfig->useBetaBuilds)
{
gRedlineURL=HashLookup(hashRef, "$BETAURL", nil);
gAutoUpdateURL=HashLookup(hashRef, "$BETAAUTOUPDATEURL", nil);
Char8 *hashLookupResult;
int versionNum=0;
if(hashLookupResult=HashLookup(hashRef, "$BETAVERSIONNUM", nil))
sscanf(hashLookupResult,"%d",&versionNum);
if(*newVersion=HashLookup(hashRef, "$BETAVERSION", nil))
{
gCheckingVersion = false; /* Don't check again */
if(versionNum)
return versionNum>kVersion;
else
return _stricmp(*newVersion,kVersionString);
}
}
else
{
gRedlineURL=HashLookup(hashRef, "$URL", nil);
gAutoUpdateURL=HashLookup(hashRef, "$AUTOUPDATEURL", nil);
Char8 *hashLookupResult;
int versionNum=0;
if(hashLookupResult=HashLookup(hashRef, "$VERSIONNUM", nil))
sscanf(hashLookupResult,"%d",&versionNum);
if(*newVersion=HashLookup(hashRef, "$VERSION", nil))
{
gCheckingVersion = false; /* Don't check again */
if(versionNum)
return versionNum>kVersion;
else
return _stricmp(*newVersion,kVersionString);
}
}
}
}
return false;
}
int TrackerConnect()
{
gStartIdleTime=TimeGetSeconds();
if(!gSessionRef)
NetworkPreSession();
if(!ReggieCreateShared(&gReggieRef,NULL,NULL,NT_GetSessionEndpoint(gSessionRef),NULL,"redline",gConfig->playerName,""))
{
gReggieLastUpdateGInfoVersion=-1;
gHasCheckedLonelyServer=false;
gConnectionTime=0;
gReggiePortMapped=false;
gReggieConnected=true;
gReggieSearching=false;
gReggieCounting=false;
return true;
}
return false;
}
int ValidateUpdateURL()
{
if(!gAutoUpdateURL)
return false;
char *pos=strstr(gAutoUpdateURL,"://");
if(!pos)return false;
pos+=3;
char *dash=strstr(pos,"/");
if(!dash)return false;
char validurl[]=".ambrosiasw.com";
char test[256];
memcpy(test,dash-strlen(validurl),strlen(validurl));
test[strlen(validurl)]='\0';
if(_stricmp(test,validurl)) {printf("update URL invaild\n");return false;}
return true;
}
void TrackerGetNewVersion()
{
if(ValidateUpdateURL())
{
if(!ScreenNoWindow())
{
if(gConfig->fullscreen)
{
gConfig->fullscreen=false;
ScreenReInit();
gConfig->fullscreen=true;
}
InterfaceDrawStrings("Trying automatic update","Please wait a moment...",-1);
char updateURL[1024];
char failStr[1024]="";
sprintf(updateURL,"%s%s",gAutoUpdateURL,kVersionString);
if(AutoUpdateRedline(updateURL,failStr))
// if(AutoUpdateRedline(gAutoUpdateURL))
{
InterfaceDisplayMessage(-1,"Update successful!","You will have to relaunch Redline now.");
return;
}
else
InterfaceDisplayMessage(-1,"Automatic update failed.",failStr);
}
}
if(gRedlineURL)
{
CFStringRef str=CFStringCreateWithCString(kCFAllocatorDefault,gRedlineURL, kCFStringEncodingASCII);
CFURLRef url=CFURLCreateWithString(kCFAllocatorDefault,str,NULL);
//LSOpenCFURLRef(url,NULL);
LSLaunchURLSpec inLaunchSpec;
inLaunchSpec.appURL=NULL;
inLaunchSpec.itemURLs=CFArrayCreate(kCFAllocatorDefault,(const void**)&url,1,NULL);
inLaunchSpec.passThruParams=NULL;
inLaunchSpec.launchFlags=kLSLaunchNoParams;
inLaunchSpec.asyncRefCon=NULL;
LSOpenFromURLSpec(&inLaunchSpec,NULL);
}
else
{
CFURLRef url=CFURLCreateWithString(kCFAllocatorDefault,CFSTR("http://www.AmbrosiaSW.com/"),NULL);
//LSOpenCFURLRef(url,NULL);
LSLaunchURLSpec inLaunchSpec;
inLaunchSpec.appURL=NULL;
inLaunchSpec.itemURLs=CFArrayCreate(kCFAllocatorDefault,(const void**)&url,1,NULL);
inLaunchSpec.passThruParams=NULL;
inLaunchSpec.launchFlags=kLSLaunchNoParams;
inLaunchSpec.asyncRefCon=NULL;
LSOpenFromURLSpec(&inLaunchSpec,NULL);
}
}
enum{
kReggieAdvGameChannel=1,
kReggieSearchGameChannel,
kReggieSearchPlayersChannel=4,
kReggiePortMapChannel,
kReggieInsertRecordChannel,
kReggieRegistryChannel,
kReggieRecordChannel
};
void TrackerMessageSend(char *message)
{
if(gReggieConnected)
{
HashRef hashRef = HashCreate(kHashFlagReggieKeys);
HashAppend(hashRef, "$KIND", "REDLINE");
HashAppend(hashRef, "$MESSAGE", message);
ReggieMessageSend(gReggieRef,nil,hashRef);
HashDispose(hashRef);
if(gMessageInputBufferPos<kMessageInputBufferSize)
strcpy(gMessageInputBuffer[gMessageInputBufferPos++],message);
}
}
int TrackerMessageReceive(tChatMessage *msg)
{
static char motd[1024]="";
static char lastMessage[1024]="";
if(gReggieConnected)
{
if(strcmp(motd,gMOTD))
{
strcpy(motd,gMOTD);
sprintf(msg->str,"### %s",motd);
msg->flags=kChatFlagSystem|kChatFlagSilent;
return true;
}
HashRef hash=ReggieMessageCheck(gReggieRef);
if(hash)
{
char *hashResult=nil;
do{
hashResult=HashLookup(hash,"$MESSAGE",hashResult);
if(hashResult)
if(gMessageInputBufferPos<kMessageInputBufferSize&&strlen(hashResult)<kMaxMessageSize)
{
int dupe=false;
for(int i=0;i<gMessageInputBufferPos;i++)
if(strcmp(gMessageInputBuffer[i],hashResult)==0)
dupe=true;
if(!dupe)
strcpy(gMessageInputBuffer[gMessageInputBufferPos++],hashResult);
}
}while(hashResult);
}
if(gMessageInputBufferPos)
{
strcpy(msg->str,gMessageInputBuffer[--gMessageInputBufferPos]);
msg->flags=0;
if(strcmp(lastMessage,msg->str))
{
strcpy(lastMessage,msg->str);
return true;
}
else
return false;
}
}
return false;
}
void NetworkAdvertiseIdle(tGameInfo *gInfo,int *showLonelyMessage,int locked,int forceUpdate)
{
if(showLonelyMessage)
*showLonelyMessage=false;
if(gReggieConnected)
{
UInt32 state=ReggieGetStatus(gReggieRef,gMOTD,NULL);
if(state==kReggieStateOnline)
{
if(gConnectionTime==0)
gConnectionTime=TimeGetSeconds();
if((gReggieLastUpdateGInfoVersion!=gInfo->version||forceUpdate)&&gConfig->trackerEnable)
{
char hashString[1024];
if(gInfo->map==kFileErr)
return;
tMapInfo *mapInfo=(tMapInfo*)FileGetParsedDataPtr(gInfo->map,kParserTypeMapInfoDesc,sizeof(tMapInfo));
while(char *ch=strchr(gConfig->gameName,'\''))
*ch='`';
sprintf(hashString,"$REDLINE$HOSTING=1 $REDLINE$PASSWORD='%d' $REDLINE$NUMPLAYERS=%d $REDLINE$NUMNETPLAYERS=%d $REDLINE$MAXPLAYERS=%d $REDLINE$ARCADE=%d $REDLINE$LOCKED=%d $REDLINE$ONLYREGISTERED=%d $REDLINE$LAPS=%d",strlen(gConfig->password)?1:0,gInfo->numPlayers,gInfo->numNetPlayers,gConfig->maxPlayers,gInfo->arcade,locked,gConfig->onlyRegisteredPlayers,gInfo->numLaps);
HashRef hashRef = HashInitialize(kHashFlagReggieKeys,hashString);
HashAppend(hashRef, "$MESSAGE", gConfig->gameName);
HashAppend(hashRef, "$REDLINE$MAP", mapInfo->name);
HashAppend(hashRef, "$REDLINE$MAPFILE",FileGetName(gInfo->map));
for(int i=0;i<gInfo->numPlayers;i++)
{
NT_MemberInfo memberInfo;
char key[256];
sprintf(key,"$REDLINE$PLAYER%dNAME",i);
HashAppend(hashRef, key, gInfo->playerNames[i]);
if(gInfo->playerCars[i]!=-1)
{
tCarDefinition *car=(tCarDefinition*)FileGetParsedDataPtr(gInfo->playerCars[i],kParserTypeCarDesc,sizeof(tCarDefinition));
sprintf(key,"$REDLINE$PLAYER%dCAR",i);
HashAppend(hashRef, key, car->carName);
}
if(i>0&&i<gInfo->numNetPlayers)
{
if(NT_GetMemberInfo(gSessionRef,gInfo->netID[i],&memberInfo))
{
char address[256];
AddressTranscribe(&memberInfo.address,address);
sprintf(key,"$ADDRESS$PLAYER%d",i);
HashAppend(hashRef, key, address);
}
}
else if(i==0)
{
NetworkAddress local,publicAdd;
char address[256];
DatagramGetAddress(NT_GetSessionEndpoint(gSessionRef),&local);
ReggieProbeCheck(gReggieRef,false,&local,&publicAdd);
if(!AddressIsEmpty(&publicAdd))
{
AddressTranscribe(&publicAdd,address);
HashAppend(hashRef, "$ADDRESS$PLAYER0", address);
}
}
}
ReggieInsertBegin(gReggieRef,kReggieAdvGameChannel, kReggieRecordAdvertise ,hashRef);/* Makes own copy of hash */
HashDispose(hashRef); /* Not needed any more */
gReggieLastUpdateGInfoVersion=gInfo->version;
}
if(!gHasCheckedLonelyServer)
if(TimeGetSeconds()>gConnectionTime+3)
if(showLonelyMessage)
{
*showLonelyMessage=CouldMyNATCauseMyServerToBeLonely();
//printf("CouldMyNATCauseMyServerToBeLonely sez %d\n",*showLonelyMessage);
gHasCheckedLonelyServer=true;
}
if(!gReggiePortMapped)
if(TimeGetSeconds()>gConnectionTime+1)
{
NetworkAddress local;
UInt16 localPort;
DatagramGetAddress(NT_GetSessionEndpoint(gSessionRef),&local);
AddressDecomposeTCPIP(&local,&localPort,nil);
U16Swap(localPort);
//printf("PORTMAP: %d\n",ReggiePortmapStatus(gReggieRef, nil, nil, &local));
//if (ReggiePortmapStatus(gReggieRef, nil, nil, &local) > kReggiePortmapNone)
ReggiePortmapBegin(gReggieRef,kReggiePortMapChannel,false,localPort,localPort,"Redline");
gReggiePortMapped=true;
}
}
else if(state!=kReggieStatePending)
{
NetworkStopAdvertising();
ReggieDispose(gReggieRef,true);
gReggieConnected=false;
}
}
else
TrackerConnect();
}
void NetworkStopAdvertising()
{
if(gReggieConnected)
{
ReggieChannelClose(gReggieRef,kReggieAdvGameChannel);
// ReggieDispose(gReggieRef,true);
// gReggieConnected=false;
}
}
void QueryGameSearch()
{
HashRef hashRef = HashInitialize(kHashFlagReggieKeys, "$KIND=redline $REDLINE$HOSTING=1");
ReggieSearchBegin(gReggieRef,kReggieSearchGameChannel,kReggieRecordAdvertise,kReggieSearchFlagNotify,hashRef); /* Makes own copy of hash */
HashDispose(hashRef); /* Not needed any more */
}
void QueryPlayerSearch()
{
HashRef hashRef = HashInitialize(kHashFlagReggieKeys,"$KIND=redline $REDLINE$HOSTING=0");
ReggieSearchBegin(gReggieRef,kReggieSearchPlayersChannel,kReggieRecordAdvertise,kReggieSearchFlagNotify,hashRef);
HashDispose(hashRef); /* Not needed any more */
}
void AdvertisePlayer()
{
char hashString[1024];
float idleAdd=TimeGetSeconds()-gStartIdleTime;
int reportIdle=(idleAdd>300);
gReportedIdle=reportIdle;
UInt32 startIdleLocalTime=TimerGetLocalSeconds()-idleAdd;
if(reportIdle)
sprintf(hashString,"$REDLINE$HOSTING=0 $TIME$IDLE=%d",startIdleLocalTime);
else
sprintf(hashString,"$REDLINE$HOSTING=0");
HashRef hashRef = HashInitialize(kHashFlagReggieKeys,hashString);
char name[1024];
sprintf(name,"%s%s",RT3_IsRegistered()?"":"\255demo.png\255 ",gConfig->playerName);
HashAppend(hashRef, "$NAME", name);
ReggieInsertBegin(gReggieRef,kReggieAdvGameChannel,kReggieRecordAdvertise,hashRef);
HashDispose(hashRef);
}
void NetworkSearchGamesInit()
{
gLastQuery=0;
QueryGameSearch();
QueryPlayerSearch();
AdvertisePlayer();
//ReggieMessageBlock(gReggieRef,NULL,false);
gReggieSearching=true;
}
int NetworkCountPlayers()
{
static UInt32 lastPlayerStamp=-1;
static int lastCount=0;
if(gReggieConnected)
{
UInt32 state=ReggieGetStatus(gReggieRef,gMOTD,NULL);
if(!(state==kReggieStateRefused||state==kReggieStateOffline))
{
if(!gReggieCounting)
{
HashRef hashRef = HashInitialize(kHashFlagReggieKeys,"$KIND=redline $REDLINE$HOSTING=0 COUNT");
ReggieSearchBegin(gReggieRef,kReggieSearchPlayersChannel,kReggieRecordAdvertise,kReggieSearchFlagNotify,hashRef);
HashDispose(hashRef); /* Not needed any more */
gReggieCounting=true;
}
SInt32 reggieCount,totalCount;
UInt32 reggieStamp;
Bool8 outdated;
reggieStamp=ReggieSearchStatus(gReggieRef,kReggieSearchPlayersChannel,&reggieCount,&totalCount,&outdated);
if(reggieStamp!=lastPlayerStamp)
{
if(outdated)
{
HashRef hashRef = HashInitialize(kHashFlagReggieKeys,"$KIND=redline $REDLINE$HOSTING=0 COUNT");
ReggieSearchBegin(gReggieRef,kReggieSearchPlayersChannel,kReggieRecordAdvertise,kReggieSearchFlagNotify,hashRef);
HashDispose(hashRef); /* Not needed any more */
}
lastPlayerStamp=reggieStamp;
}
if(reggieCount!=-1)
lastCount=totalCount;
return lastCount;
}
else
{
ReggieDispose(gReggieRef,true);
gReggieConnected=false;
}
}
else
TrackerConnect();
return 0;
}
void NetworkSearchGames(int *updated,tGameListEntry *games,int *numGames,int maxGames,tGameListPlayerEntry *players,int *numPlayers,int maxPlayers)
{
static UInt32 lastGameStamp=-1,lastPlayerStamp=-1;
static float lastIdleAdd;
*updated=false;
if(gReggieConnected)
{
UInt32 state=ReggieGetStatus(gReggieRef,gMOTD,NULL);
if(!(state==kReggieStateRefused||state==kReggieStateOffline))
{
if(!gReggieSearching)
{
NetworkSearchGamesInit();
lastGameStamp=-1;
lastPlayerStamp=-1;
gReportedIdle=0;
}
float idleAdd=TimeGetSeconds()-gStartIdleTime;
if(idleAdd>lastIdleAdd+15)
AdvertisePlayer();
lastIdleAdd=idleAdd;
int reportIdle=(idleAdd>300);
if(reportIdle!=gReportedIdle)
AdvertisePlayer();
SInt32 reggieCount,totalCount;
UInt32 reggieStamp;
Bool8 outdated;
reggieStamp=ReggieSearchStatus(gReggieRef,kReggieSearchGameChannel,&reggieCount,&totalCount,&outdated);
if(reggieStamp!=lastGameStamp)
{
if(reggieCount==totalCount||reggieCount>*numGames)
// if(reggieCount>-1)
{
int oldNumGames=*numGames;
*numGames=0;
lastGameStamp=reggieStamp;
*updated=true;
NetworkAddress local;
DatagramGetAddress(NT_GetSessionEndpoint(gSessionRef),&local);
int index=0;
for(;index<reggieCount;index++)
{
HashRef hashRef = ReggieSearchIndex(gReggieRef, kReggieSearchGameChannel ,index);
if(hashRef&&*numGames<maxGames)
{
hashRef=HashClone(hashRef);
if(HashLookup(hashRef, "$UNIQUEID", nil))
{
Char8 *hashLookupResult;
strcpy(games[*numGames].host,"127.0.0.1");
strcpy(games[*numGames].alias,"");
strcpy(games[*numGames].map,"");
strcpy(games[*numGames].name,"");
games[*numGames].numPlayers=0;
games[*numGames].locked=false;
games[*numGames].numNetPlayers=0;
games[*numGames].arcade=false;
games[*numGames].password=false;
games[*numGames].loaded=true;
games[*numGames].mapRef=kFileErr;
games[*numGames].onlyRegistered=false;
NetworkAddress addy;
ReggieNetworkType type;
ReggieSearchAddress(gReggieRef,kReggieSearchGameChannel,hashRef,&addy,&type);
ReggiePrejoinType prejoin = ReggieProbePrejoin(gReggieRef,false,&local,&addy);
if(prejoin==kReggiePrejoinSorry)
games[*numGames].joinState=kJoinStateFail;
else if(prejoin!=kReggiePrejoinNormal)
games[*numGames].joinState=kJoinStateReggie;
else
games[*numGames].joinState=kJoinStateOK;
AddressTranscribe(&addy,games[*numGames].host);
if(hashLookupResult=HashLookup(hashRef, "$ALIASES", nil))
{
char aliases[256];
strcpy(aliases,hashLookupResult);
if(strchr(aliases,','))
*strchr(aliases,',')='\0';
strcpy(games[*numGames].alias,aliases);
}
if(hashLookupResult=HashLookup(hashRef, "$REDLINE$MAP", nil))
strcpy(games[*numGames].map,hashLookupResult);
if(hashLookupResult=HashLookup(hashRef, "$REDLINE$MAPFILE",nil))
games[*numGames].mapRef=FileGetReference(hashLookupResult);
if(hashLookupResult=HashLookup(hashRef, "$REDLINE$NUMPLAYERS", nil))
sscanf(hashLookupResult,"%d",&games[*numGames].numPlayers);
if(hashLookupResult=HashLookup(hashRef, "$REDLINE$NUMNETPLAYERS", nil))
sscanf(hashLookupResult,"%d",&games[*numGames].numNetPlayers);
if(hashLookupResult=HashLookup(hashRef, "$REDLINE$MAXPLAYERS", nil))
sscanf(hashLookupResult,"%d",&games[*numGames].maxPlayers);
if(hashLookupResult=HashLookup(hashRef, "$REDLINE$ARCADE", nil))
sscanf(hashLookupResult,"%d",&games[*numGames].arcade);
if(hashLookupResult=HashLookup(hashRef, "$REDLINE$PASSWORD", nil))
sscanf(hashLookupResult,"%d",&games[*numGames].password);
if(hashLookupResult=HashLookup(hashRef, "$REDLINE$LOCKED", nil))
sscanf(hashLookupResult,"%d",&games[*numGames].locked);
if(hashLookupResult=HashLookup(hashRef, "$REDLINE$ONLYREGISTERED", nil))
sscanf(hashLookupResult,"%d",&games[*numGames].onlyRegistered);
if(hashLookupResult=HashLookup(hashRef, "$MESSAGE", nil))
strcpy(games[*numGames].name,hashLookupResult);
for(int i=0;i<games[*numGames].numPlayers;i++)
{
strcpy(games[*numGames].players[i].name,"");
strcpy(games[*numGames].players[i].car,"");
strcpy(games[*numGames].players[i].location,"");
char hashString[32];
sprintf(hashString,"$REDLINE$PLAYER%dNAME",i);
if(hashLookupResult=HashLookup(hashRef,hashString,nil))
strcpy(games[*numGames].players[i].name,hashLookupResult);
sprintf(hashString,"$REDLINE$PLAYER%dCAR",i);
if(hashLookupResult=HashLookup(hashRef,hashString,nil))
strcpy(games[*numGames].players[i].car,hashLookupResult);
sprintf(hashString,"$LOCATION$PLAYER%d",i);
if(hashLookupResult=HashLookup(hashRef,hashString,nil))
strcpy(games[*numGames].players[i].location,hashLookupResult);
else
{
sprintf(hashString,"$DOMAIN$PLAYER%d",i);
if(hashLookupResult=HashLookup(hashRef,hashString,nil))
strcpy(games[*numGames].players[i].location,hashLookupResult);
else
{
sprintf(hashString,"$HOSTNAMEPLAYER%d",i);
if(hashLookupResult=HashLookup(hashRef,hashString,nil))
strcpy(games[*numGames].players[i].location,hashLookupResult);
}
}
if(i>0)
if(strcmp(games[*numGames].players[i].location,"Reserved Location")==0)
strcpy(games[*numGames].players[i].location,games[*numGames].players[0].location);
if(char *loc=strrchr(games[*numGames].players[i].location,','))
memmove(games[*numGames].players[i].location,loc+1,strlen(loc)+1);
}
(*numGames)++;
}
HashDispose(hashRef);
}
}
for(;index<totalCount;index++)
if(*numGames<maxGames)
{
if(oldNumGames<*numGames)
games[*numGames].loaded=false;
/* games[*numGames].loaded=true;
strcpy(games[*numGames].name,"Test");
strcpy(games[*numGames].host,"127.0.0.1");
strcpy(games[*numGames].map,"");
strcpy(games[*numGames].players[0].name,"kalle");
strcpy(games[*numGames].players[0].location,"unna");
games[*numGames].numPlayers=1;
games[*numGames].locked=false;
games[*numGames].numNetPlayers=0;
games[*numGames].arcade=false;
games[*numGames].password=false;
games[*numGames].loaded=true;
games[*numGames].mapRef=kFileErr;*/
(*numGames)++;
}
}
if(outdated&&reggieCount==totalCount)
QueryGameSearch();
}
reggieStamp=ReggieSearchStatus(gReggieRef,kReggieSearchPlayersChannel,&reggieCount,&totalCount,&outdated);
if(reggieStamp!=lastPlayerStamp)
{
if(reggieCount==totalCount||reggieCount>*numPlayers)
//if(reggieCount>-1)
{
int oldNumPlayers=*numPlayers;
*numPlayers=0;
lastPlayerStamp=reggieStamp;
*updated=true;
int index=0;
for(;index<reggieCount;index++)
{
HashRef hashRef = ReggieSearchIndex(gReggieRef, kReggieSearchPlayersChannel ,index);
if(hashRef&&*numPlayers<maxPlayers)
if(HashLookup(hashRef, "$UNIQUEID", nil))
{
Char8 *hashLookupResult;
strcpy(players[*numPlayers].location,"");
strcpy(players[*numPlayers].name,"");
players[*numPlayers].startIdleTime=-1;
if(hashLookupResult=HashLookup(hashRef, "$NAME", nil))
strcpy(players[*numPlayers].name,hashLookupResult);
if(hashLookupResult=HashLookup(hashRef, "$LOCATION", nil))
strcpy(players[*numPlayers].location,hashLookupResult);
if(char *loc=strrchr(players[*numPlayers].location,','))
memmove(players[*numPlayers].location,loc+1,strlen(loc)+1);
if(hashLookupResult=HashLookup(hashRef, "$TIME$IDLE", nil))
sscanf(hashLookupResult,"%d",&players[*numPlayers].startIdleTime);
(*numPlayers)++;
}
}
for(;index<totalCount;index++)
if(*numPlayers<maxPlayers)
{
if(oldNumPlayers<*numPlayers)
{
strcpy(players[*numPlayers].location,"");
strcpy(players[*numPlayers].name,"");
players[*numPlayers].startIdleTime=-1;
//strcpy(players[*numPlayers].location,"area 51");
//sprintf(players[*numPlayers].name,"joe %d",*numPlayers);
}
(*numPlayers)++;
}
//printf("%d,%d=>%d\n",reggieCount,totalCount,*numPlayers);
}
if(outdated&&reggieCount==totalCount)
QueryPlayerSearch();
}
}
else
{
NetworkSearchGamesStop();
ReggieDispose(gReggieRef,true);
gReggieConnected=false;
}
}
else
{
*numGames=0;
TrackerConnect();
}
}
void NetworkSearchGamesStop()
{
if(gReggieConnected)
{
//ReggieMessageBlock(gReggieRef,NULL,true);
ReggieChannelClose(gReggieRef,kReggieAdvGameChannel);
ReggieChannelClose(gReggieRef, kReggieSearchGameChannel);
ReggieChannelClose(gReggieRef, kReggieSearchPlayersChannel);
}
gReggieSearching=false;
}
void NetworkCountPlayersStop()
{
if(gReggieConnected)
{
ReggieChannelClose(gReggieRef, kReggieSearchPlayersChannel);
}
gReggieCounting=false;
}
int gWaitingForRegistry=false;
int gWaitingForRecord=false;
void TrackerFetchRecord(tFileRef track,tFileRef car,int arcade,int reverse)
{
if(gConfig->registerLapTimes)
{
if(!gReggieConnected)
if(!TrackerConnect())
{
TextPrintfToBufferFormatedFading(Vector(0,-0.2),0.07,kTextAlignMiddle,1,2,"Unable to Connect lap time server");
return;
}
char hashString[512];
HashRef hashRef;
tMapInfo *mapInfo=(tMapInfo*)FileGetParsedDataPtr(track,kParserTypeMapInfoDesc,sizeof(tMapInfo));
if(!mapInfo->builtIn)return;
tCarDefinition *carDef=(tCarDefinition*)FileGetParsedDataPtr(car,kParserTypeCarDesc,sizeof(tCarDefinition));
if(!carDef->builtIn)return;
sprintf(hashString,"$REDLINE$MODE='%d' $REDLINE$REVERSE='%d'",arcade,reverse);
hashRef = HashInitialize(kHashFlagReggieKeys, hashString);
HashAppend(hashRef, "$REDLINE$MAP", mapInfo->name);
HashAppend(hashRef, "$REDLINE$CAR", carDef->carName);
HashAppend(hashRef, "SORT", ">$REDLINE$TIME,>$TIME");
HashAppend(hashRef, "LIMIT", "1");
//printf("hash:%s\n",(char*)(*HashSave(hashRef)));
ReggieSearchBegin(gReggieRef,kReggieRecordChannel,kReggieRecordHighScore,0,hashRef);
HashDispose(hashRef);
gWaitingForRecord=true;
}
}
void TrackerRegisterLapTime(tFileRef track,tFileRef car,int time,int arcade,int reverse)
{
if(gConfig->registerLapTimes)
{
if(!gReggieConnected)
if(!TrackerConnect())
{
TextPrintfToBufferFormatedFading(Vector(0,-0.2),0.07,kTextAlignMiddle,1,2,"Unable to Connect to lap time server");
return;
}
char hashString[512];
HashRef hashRef;
tMapInfo *mapInfo=(tMapInfo*)FileGetParsedDataPtr(track,kParserTypeMapInfoDesc,sizeof(tMapInfo));
if(!mapInfo->builtIn)return;
tCarDefinition *carDef=(tCarDefinition*)FileGetParsedDataPtr(car,kParserTypeCarDesc,sizeof(tCarDefinition));
if(!carDef->builtIn)return;
sprintf(hashString,"$REDLINE$TIME='%d' $REDLINE$MODE='%d' $REDLINE$REVERSE='%d' $REDLINE$MAPCHECK='%8X' $REDLINE$CARCHECK='%8X'",time,arcade,reverse,FileGetChecksum(track),FileGetChecksum(car));
hashRef = HashInitialize(kHashFlagReggieKeys, hashString);
HashAppend(hashRef, "$REDLINE$MAPFILE", FileGetName(track));
HashAppend(hashRef, "$REDLINE$CARFILE", FileGetName(car));
HashAppend(hashRef, "$REDLINE$MAP", mapInfo->name);
HashAppend(hashRef, "$REDLINE$CAR", carDef->carName);
HashAppend(hashRef, "$REDLINE$NAME", gConfig->playerName);
ReggieInsertBegin(gReggieRef, kReggieInsertRecordChannel, kReggieRecordHighScore ,hashRef);
HashDispose(hashRef);
sprintf(hashString,"$REDLINE$TIME<='%d' $REDLINE$TIME!='%d' $REDLINE$MODE='%d' $REDLINE$REVERSE='%d' COUNT",time,time,arcade,reverse);
hashRef = HashInitialize(kHashFlagReggieKeys, hashString);
HashAppend(hashRef, "$REDLINE$MAP", mapInfo->name);
HashAppend(hashRef, "$REDLINE$CAR", carDef->carName);
ReggieSearchBegin(gReggieRef,kReggieRegistryChannel,kReggieRecordHighScore,kReggieSearchFlagCount,hashRef);
HashDispose(hashRef);
gWaitingForRegistry=true;
}
}
void TrackerLapTimeRegistryClose()
{
if(gWaitingForRegistry||gWaitingForRecord)
{
ReggieChannelClose(gReggieRef,kReggieInsertRecordChannel);
ReggieChannelClose(gReggieRef,kReggieRegistryChannel);
ReggieChannelClose(gReggieRef,kReggieRecordChannel);
gWaitingForRegistry=gWaitingForRecord=false;
}
}
void TrackerWaitForLapTimeRegistry()
{
if(gWaitingForRegistry||gWaitingForRecord)
{
if(gWaitingForRegistry)
TextPrintfToBufferFormated(Vector(0,-0.2),0.05,kTextAlignMiddle,"Registering Lap Time");
UInt32 state=ReggieGetStatus(gReggieRef,gMOTD,NULL);
if(state==kReggieStateRefused||state==kReggieStateOffline)
{
if(state==kReggieStateRefused)
TextPrintfToBufferFormatedFading(Vector(0,-0.2),0.07,kTextAlignMiddle,1,2,"Connection Refused by lap time server");
else
TextPrintfToBufferFormatedFading(Vector(0,-0.2),0.07,kTextAlignMiddle,1,2,"No response from lap time server");
gWaitingForRegistry=false;
gWaitingForRecord=false;
gReggieConnected=false;
ReggieDispose(gReggieRef,false);
}
else
{
if(gWaitingForRegistry)
{
SInt32 reggieCount,totalCount;
ReggieSearchStatus(gReggieRef,kReggieRegistryChannel,&reggieCount,&totalCount,NULL);
if(reggieCount!=-1)
{
if(totalCount==0)
TextPrintfToBufferFormatedFading(Vector(0,-0.2),0.1,kTextAlignMiddle,1,2,"World Record!!!");
else if(totalCount<=25)
TextPrintfToBufferFormatedFading(Vector(0,-0.2),0.1,kTextAlignMiddle,1,2,"Place %d on World List",totalCount+1);
gWaitingForRegistry=false;
ReggieChannelClose(gReggieRef,kReggieRegistryChannel);
TrackerFetchRecord(gGameInfo->map,gGameInfo->playerCars[0],gGameInfo->arcade,gGameInfo->reverse^gMapInfo->reverse);
}
}
if(gWaitingForRecord)
{
SInt32 reggieCount,totalCount;
ReggieSearchStatus(gReggieRef,kReggieRecordChannel,&reggieCount,&totalCount,NULL);
if(reggieCount!=-1)
{
if(totalCount==0)
{
gWorldRecord=0;
gWaitingForRecord=false;
}
else if(reggieCount>0){
HashRef hashRef = ReggieSearchIndex(gReggieRef,kReggieRecordChannel,0);
//printf("$REDLINE$TIME%s\n",HashLookup(hashRef,"$REDLINE$TIME",nil));
//printf("$REDLINE$NAME%s\n",HashLookup(hashRef,"$REDLINE$NAME",nil));
//printf("hash:%s\n",(char*)(*HashSave(hashRef)));
sscanf(HashLookup(hashRef,"$REDLINE$TIME",nil),"%d",&gWorldRecord);
strcpy(gRecordName,HashLookup(hashRef,"$REDLINE$NAME",nil));
int numChars=0,index=0;
while(numChars<12)
{
if(gRecordName[index]=='\255')
while(gRecordName[++index]!='\255');
index++;
numChars++;
}
gRecordName[index]='\0';
gWaitingForRecord=false;
ReggieChannelClose(gReggieRef,kReggieRecordChannel);
}
}
}
}
}
}