openmohaa/code/gamespy/gstats/gstats.c
2023-02-04 21:00:01 +01:00

1681 lines
46 KiB
C

/******
gstats.c
GameSpy Stats/Tracking SDK
GameSpy Persistent Storage SDK
Copyright 1999-2007 GameSpy Industries, Inc
******
Please see the GameSpy Stats and Tracking SDK documentation for more info
******/
/********
INCLUDES
********/
#include "gstats.h"
#include "gpersist.h"
#include "../common/gsAvailable.h"
#include "../darray.h"
#include "../md5.h"
#ifdef __cplusplus
extern "C" {
#endif
/********
TYPEDEFS
********/
struct statsgame_s
{
int connid;
int sesskey;
int usebuckets;
bucketset_t buckets;
char challenge[9];
DArray playernums; //for player number translation
DArray teamnums; //for team number translation
int totalplayers, totalteams;
gsi_time sttime;
};
typedef enum {rt_authcb, rt_datacb, rt_savecb, rt_profilecb} reqtype_t;
typedef struct
{
reqtype_t reqtype;
int localid;
int profileid;
persisttype_t pdtype;
int pdindex;
void *instance;
void *callback;
} serverreq_t;
/********
PROTOTYPES
********/
static int ServerOpInt(statsgame_t game,char *name, BucketFunc func, int value, int index);
static double ServerOpFloat(statsgame_t game,char *name, BucketFunc func, double value, int index);
static char *ServerOpString(statsgame_t game,char *name, BucketFunc func, char *value, int index);
static int TeamOpInt(statsgame_t game,char *name, BucketFunc func, int value, int index);
static double TeamOpFloat(statsgame_t game,char *name, BucketFunc func, double value, int index);
static char *TeamOpString(statsgame_t game,char *name, BucketFunc func, char *value, int index);
static int PlayerOpInt(statsgame_t game,char *name, BucketFunc func, int value, int index);
static double PlayerOpFloat(statsgame_t game,char *name, BucketFunc func, double value, int index);
static char *PlayerOpString(statsgame_t game,char *name, BucketFunc func, char *value, int index);
static char *CreateBucketSnapShot(bucketset_t buckets);
#ifdef ALLOW_DISK
static void CheckDiskFile();
static void DiskWrite(char *line, int len);
#endif
static void InternalInit();
static int SendChallengeResponse(const char *indata, int gameport);
static int RecvSessionKey();
static int DoSend(char *data, int len);
static void xcode_buf(char *buf, int len);
static int g_crc32(char *s, int len);
static void create_challenge(int challenge, char chstr[9]);
static char *value_for_key(const char *s, const char *key);
static char *value_for_key_safe(const char *s, const char *key);
static int get_sockaddrin(const char *host, int port, struct sockaddr_in *saddr, struct hostent **savehent);
/**************
PERSISTENT STORAGE PROTOTYPES
**************/
static void AddRequestCallback(reqtype_t reqtype, int localid, int profileid, persisttype_t pdtype, int pdindex, void *callback, void *instance);
static void SendPlayerAuthRequest(char *data, int len, int localid, PersAuthCallbackFn callback, void *instance);
static void SendPlayerAuthRequest(char *data, int len, int localid, PersAuthCallbackFn callback, void *instance);
static int SocketReadable(SOCKET s);
static char *FindFinal(char *buff, int len);
static int FindRequest(reqtype_t reqtype, int localid, int profileid);
static void ProcessPlayerAuth(const char *buf, int len);
static void ProcessGetPid(const char *buf, int len);
static void ProcessGetData(const char *buf, int len);
static void ProcessSetData(const char *buf, int len);
static void ProcessStatement(char *buff, int len);
static int ProcessInBuffer(char *buff, int len);
static void CallReqCallback(int reqindex, int success, time_t modified, char *data, int length);
static void ClosePendingCallbacks();
static void SetPersistDataHelper(int localid, int profileid, persisttype_t type, int index, const char *data, int len, PersDataSaveCallbackFn callback, void *instance, int kvset);
void GetPersistDataValuesA(int localid, int profileid, persisttype_t type, int index, char *keys, PersDataCallbackFn callback, void *instance);
void GetPersistDataValuesModifiedA(int localid, int profileid, persisttype_t type, int index, time_t modifiedsince, char *keys, PersDataCallbackFn callback, void *instance);
/********
DEFINES
********/
//#define SSHOST "207.199.80.230"
#define SSHOST "gamestats." GSI_DOMAIN_NAME
#define SSPORT 29920
#define FIXGAME(g,r) if (g == NULL) g = g_statsgame; if (g == NULL) return r;
#define DoFunc(f,g, n, v, t, r) \
if (g == NULL) g = g_statsgame; \
if (!g) r = v; \
else { \
r = f(g->buckets, n, v); \
if (!r) \
r = BucketNew(g->buckets, n, t, v); }
#define DOXCODE(b, l, e) enc = e; xcode_buf(b,l);
/********
VARS
********/
char gcd_gamename[256] = "";
char gcd_secret_key[256] = "";
static statsgame_t g_statsgame = NULL;
static int connid = 0;
static int sesskey = 0;
static SOCKET sock = INVALID_SOCKET;
/* #define enc1 "GameSpy 3D"
#define enc2 "Industries"
#define enc3 "ProjectAphex"
#define STATSFILE "gstats.dat" */
/* A couple vars to help avoid the string table */
static char enc1[16] = {'\0','a','m','e','S','p','y','3','D','\0'};
static char enc3[16] = {'\0','r','o','j','e','c','t','A','p','h','e','x','\0'};
#ifdef ALLOW_DISK
static char statsfile[16] = {'\0','s','t','a','t','s','.','d','a','t','\0'};
static char enc2[16]= {'\0','n','d','u','s','t','r','i','e','s','\0'};
#endif
static char finalstr[10] = {'\0','f','i','n','a','l','\\','\0'};
static char *enc = enc1;
static int internal_init = 0;
static char *rcvbuffer = NULL;
static int rcvmax = 0;
static int rcvlen = 0;
// Changed By Saad Nader, 09-16-2004
// Due to confliction with MacOS X
///////////////////////////////////////////
static int stats_initstate = init_none;
static int gameport = 0;
static gsi_time initstart = 0;
static gsi_time inittimeout = 20000; // 20 seconds
char StatsServerHostname[64] = SSHOST;
static DArray serverreqs = NULL; //for pre-authentication requests
BucketFunc bucketfuncs[NUMOPS] =
{BucketSet, BucketAdd, BucketSub, BucketMult, BucketDiv, BucketConcat, BucketAvg};
void * bopfuncs[][3] =
{
{ServerOpInt, ServerOpFloat, ServerOpString},
{TeamOpInt, TeamOpFloat, TeamOpString},
{PlayerOpInt, PlayerOpFloat, PlayerOpString},
};
/****************************************************************************/
/* PUBLIC FUNCTIONS */
/****************************************************************************/
#define RAWSIZE 128
char *GenerateAuthA(const char *challenge, const char *password, char response[33])
{
char rawout[RAWSIZE];
/* check to make sure we weren't passed a huge pass/challenge */
if (strlen(password) + strlen(challenge) + 20>= RAWSIZE)
{
strcpy(response,"CD Key or challenge too long");
return response;
}
/* response = MD5(pass + challenge) */
sprintf(rawout, "%s%s",password, challenge );
/* do the response md5 */
MD5Digest((unsigned char *)rawout, strlen(rawout), response);
return response;
}
#ifdef GSI_UNICODE
char *GenerateAuthW(const char* challenge, const unsigned short *password, char response[33])
{
char* password_A = UCS2ToUTF8StringAlloc(password);
GenerateAuthA(challenge, password_A, response);
gsifree(password_A);
return response;
}
#endif
/****************************************************************************/
int InitStatsAsync(int theGamePort, gsi_time theInitTimeout)
{
struct sockaddr_in saddr;
char tempHostname[128];
int ret;
gameport = theGamePort;
if (theInitTimeout != 0)
inittimeout = theInitTimeout;
/* check if the backend is available */
if(__GSIACResult != GSIACAvailable)
return GE_NOSOCKET;
/* Init our hidden strings if needed */
if (!internal_init)
InternalInit();
SocketStartUp();
sesskey = (int)current_time();
/* Get connected */
if (sock != INVALID_SOCKET)
CloseStatsConnection();
rcvlen = 0; //make sure ther receive buffer is cleared
if (inet_addr(StatsServerHostname) == INADDR_NONE)
{
strcpy(tempHostname, gcd_gamename);
strcat(tempHostname,".");
strcat(tempHostname,StatsServerHostname);
} else
strcpy(tempHostname, StatsServerHostname); //it's already been resolved
if (get_sockaddrin(tempHostname,SSPORT,&saddr,NULL) == 0)
return GE_NODNS;
#ifdef INSOCK
sock = socket ( AF_INET, SOCK_STREAM, 0 );
#else
sock = socket ( AF_INET, SOCK_STREAM, IPPROTO_TCP );
#endif
if (sock == INVALID_SOCKET)
return GE_NOSOCKET;
SetSockBlocking(sock, 0);
ret = connect(sock, (struct sockaddr*)&saddr, sizeof(saddr));
if (gsiSocketIsError(ret))
{
int anError = GOAGetLastError(sock);
if ((anError != WSAEWOULDBLOCK) && (anError != WSAETIMEDOUT) && (anError != WSAEINPROGRESS))
{
stats_initstate = init_failed;
closesocket(sock);
return GE_NOCONNECT;
}
}
// allocate the recv buffer
rcvbuffer = gsimalloc(64);
if (rcvbuffer == NULL)
return GE_NOCONNECT; // add a new error code for out of mem?
rcvmax = 64;
rcvlen = 0;
initstart = current_time();
stats_initstate = init_connecting;
return GE_CONNECTING;
}
/****************************************************************************/
int InitStatsThink()
{
switch(stats_initstate)
{
case init_failed: return GE_NOCONNECT;
case init_connecting:
{
// Check if socket is writeable yet
int aWriteFlag = 0;
int aExceptFlag = 0;
int aResult = GSISocketSelect(sock, NULL, &aWriteFlag, &aExceptFlag);
if ((gsiSocketIsError(aResult)) || // socket error
(aResult == 1 && aExceptFlag == 1)) // exception
{
stats_initstate = init_failed;
CloseStatsConnection();
return GE_NOCONNECT;
}
else if (aResult == 0) // no progress yet
{
// Should we continue to wait?
if (current_time() - initstart > inittimeout)
{
stats_initstate = init_failed;
CloseStatsConnection();
return GE_TIMEDOUT;
}
else
return GE_CONNECTING;
}
// Otherwise connected
assert(aResult == 1 && aWriteFlag == 1);
stats_initstate = init_awaitchallenge;
// fall through
}
case init_awaitchallenge:
{
int ret = 0;
// Try to receive data
if (!CanReceiveOnSocket(sock))
{
// should we continue to wait?
if (current_time() - initstart > inittimeout)
{
stats_initstate = init_failed;
CloseStatsConnection();
return GE_TIMEDOUT;
}
return GE_CONNECTING;
}
// Receive the 38 byte challenge
ret = recv(sock, rcvbuffer+rcvlen, rcvmax-rcvlen, 0);
if (gsiSocketIsError(ret))
{
stats_initstate = init_failed;
CloseStatsConnection();
return GE_NOCONNECT;
}
rcvlen += ret;
rcvmax -= ret;
// need at least 38 bytes
if (rcvlen < 38)
return GE_CONNECTING;
// Process challenge
rcvbuffer[rcvlen] = '\0';
stats_initstate = init_awaitsessionkey;
/* Decode it */
DOXCODE(rcvbuffer, rcvlen, enc1);
/* Send a response */
ret = SendChallengeResponse(rcvbuffer, gameport);
if (ret != GE_NOERROR)
{
stats_initstate = init_failed;
CloseStatsConnection();
return ret;
}
stats_initstate = init_awaitsessionkey;
// clear receive buffer for next stage
rcvmax += rcvlen; // reclaim the used bytes as free space
rcvlen = 0;
memset(rcvbuffer, 0, (unsigned int)rcvmax);
// fall through
}
case init_awaitsessionkey:
{
int ret = 0;
// Try to receive data
if (!CanReceiveOnSocket(sock))
{
// should we continue to wait?
if (current_time() - initstart > inittimeout)
{
stats_initstate = init_failed;
CloseStatsConnection();
return GE_TIMEDOUT;
}
return GE_CONNECTING;
}
ret = RecvSessionKey();
if (ret != GE_NOERROR)
{
stats_initstate = init_failed;
CloseStatsConnection();
return ret;
}
// Init complete
// Clear the receive buffer
rcvmax += rcvlen;
rcvlen = 0;
memset(rcvbuffer, 0, (unsigned int)rcvmax);
#ifdef ALLOW_DISK
/* Check for old data */
CheckDiskFile();
#endif
stats_initstate = init_complete;
// fall through
}
case init_complete:
return GE_NOERROR;
default:
return GE_NOCONNECT;
};
}
/****************************************************************************/
// Blocking version of InitStatsAsync, for backwards compatability
int InitStatsConnection(int gameport)
{
int aResult = InitStatsAsync(gameport, 0);
while (aResult == GE_CONNECTING)
{
aResult = InitStatsThink();
msleep(5);
}
return aResult;
}
/****************************************************************************/
void CloseStatsConnection()
{
if (sock != INVALID_SOCKET)
{
shutdown(sock,2);
closesocket(sock);
}
sock = INVALID_SOCKET;
//call any pending callbacks with the data as lost
ClosePendingCallbacks();
if (rcvbuffer != NULL)
{
gsifree(rcvbuffer);
rcvbuffer = NULL;
rcvmax = 0;
rcvlen = 0;
}
}
/****************************************************************************/
int IsStatsConnected()
{
return (sock != INVALID_SOCKET);
}
/****************************************************************************/
#define CHALLENGEXOR 0x38F371E6
char *GetChallenge(statsgame_t game)
{
static char challenge[9];
if (game == NULL)
game = g_statsgame;
if (game == NULL)
{
create_challenge(connid ^ CHALLENGEXOR,challenge);
return challenge;
}
return game->challenge;
}
/****************************************************************************/
statsgame_t NewGame(int usebuckets)
{
statsgame_t game = (statsgame_t)gsimalloc(sizeof (struct statsgame_s));
char data[256];
int len;
if (!internal_init)
InternalInit();
game->connid = connid;
game->sesskey = sesskey++;
game->buckets = NULL;
game->playernums = NULL;
game->teamnums = NULL;
game->usebuckets = usebuckets;
/* If connected, try to send */
if (sock != INVALID_SOCKET)
{
char respformat[] = "\xC\x1C\xA\x1D\x2\x2\x19\x24\x2C\x34\x6\x17\x3E\x1C\x6\xE\x39\x46\x10\x1D\x3\xD\x16\xB\x3B\x17\x16\x36\x40\x7";
//"\newgame\\connid\%d\sesskey\%d"
DOXCODE(respformat, sizeof(respformat)-1, enc3);
len = sprintf(data,respformat,game->connid, game->sesskey);
len = DoSend(data, len);
if (len <= 0)
{
CloseStatsConnection();
}
create_challenge(game->connid ^ CHALLENGEXOR,game->challenge);
}
/* If send failed then write to disk */
if (sock == INVALID_SOCKET)
{
#ifdef ALLOW_DISK
char respformat[] = "\xC\x1C\xA\x1D\x2\x2\x19\x24\x2C\x34\x16\x1D\x23\x1\x4\xF\x1C\x3F\x51\x25\x2C\xB\xD\x19\x3C\x1E\xA\x4\x2\x6\x28\x64\x14";
// "\newgame\\sesskey\%d\challenge\%d";
DOXCODE(respformat, sizeof(respformat)-1, enc3);
len = sprintf(data,respformat,game->sesskey, game->sesskey ^ CHALLENGEXOR);
DiskWrite(data, len);
game->connid = 0;
create_challenge(game->sesskey ^ CHALLENGEXOR,game->challenge);
#else
gsifree(game);
game = NULL;
#endif
}
if (game && game->usebuckets)
{
game->buckets = NewBucketSet();
game->playernums = ArrayNew(sizeof(int),32,NULL);
game->teamnums = ArrayNew(sizeof(int),2,NULL);
game->totalplayers = game->totalteams = 0;
}
if (game)
game->sttime = current_time();
g_statsgame = game;
return game;
}
/****************************************************************************/
void FreeGame(statsgame_t game)
{
if (!game || game == g_statsgame)
{
game = g_statsgame;
g_statsgame = NULL;
}
if (!game)
return;
if (game->usebuckets)
{
if (game->buckets != NULL)
FreeBucketSet(game->buckets);
if (game->playernums != NULL)
ArrayFree(game->playernums);
if (game->teamnums != NULL)
ArrayFree(game->teamnums);
}
gsifree(game);
}
/****************************************************************************/
int SendGameSnapShotA(statsgame_t game, const char *snapshot, int final)
{
int snaplen;
int len;
int ret = GE_NOERROR;
char *snapcopy;
char *data;
FIXGAME(game, GE_DATAERROR);
/* If using buckets, get the data out of the buckets */
if (game->usebuckets)
snapcopy = CreateBucketSnapShot(game->buckets);
else
snapcopy = goastrdup(snapshot);
snaplen = (int)strlen(snapcopy);
data = (char *)gsimalloc((unsigned int)snaplen + 256);
/* Escape the data */
while (snaplen--)
if (snapcopy[snaplen] == '\\')
snapcopy[snaplen] = '\x1';
/* If connected, try to send it */
if (sock != INVALID_SOCKET)
{
// Updated response format to contain connid
//char respformat[] = "\xC\x7\x1F\xE\x2\x2\x19\x24\x2C\x34\x16\x1D\x23\x1\x4\xF\x1C\x3F\x51\x25\x2C\xC\xA\x16\x35\x2E\x4A\xE\x39\x4\x15\x2C\x15\xC\x4\xC\x31\x2E\x4A\x19";
char respformat[] = "\xC\x7\x1F\xE\x2\x2\x19\x24\x2C\x34\x16\x1D\x23\x1\x4\xF\x1C\x3F\x51\x25\x2C\xB\xA\x16\x3E\x1B\xB\x36\x40\x7\x28\x25\x1F\x6\x00\x24\x75\x16\x33\xD\x4\xE\x11\x25\x11\x1C\x4\x24\x75\x1";
// "\updgame\\sesskey\%d\done\%d\gamedata\%s"
// The above string is now:
// "\updgame\\sesskey\%d\connid\%d\done\%d\gamedata\%s"
DOXCODE(respformat, sizeof(respformat)-1, enc3);
len = sprintf(data, respformat, game->sesskey, game->connid, final, snapcopy);
snaplen = DoSend(data, len);
/* If the send failed, close the socket */
if (snaplen <= 0)
{
CloseStatsConnection();
}
}
/* If not connected, or send failed, return error or log to disk */
if (sock == INVALID_SOCKET)
{
#ifdef ALLOW_DISK
char respformat[] = "\xC\x7\x1F\xE\x2\x2\x19\x24\x2C\x34\x16\x1D\x23\x1\x4\xF\x1C\x3F\x51\x25\x2C\xB\xA\x16\x3E\x1B\xB\x36\x40\x7\x28\x25\x1F\x6\x0\x24\x75\x16\x33\xD\x4\xE\x11\x25\x11\x1C\x4\x24\x75\x1\x33\xE\x9\x3F\x45";
//"\updgame\\sesskey\%d\connid\%d\done\%d\gamedata\%s\dl\1"
DOXCODE(respformat, sizeof(respformat)-1, enc3);
len = sprintf(data, respformat, game->sesskey, game->connid, final, snapcopy);
DiskWrite(data, len);
#else
ret = GE_NOCONNECT;
#endif
}
gsifree(snapcopy);
gsifree(data);
return ret;
}
#ifdef GSI_UNICODE
int SendGameSnapShotW(statsgame_t game, const unsigned short*snapshot, int final)
{
char* snapshot_A = UCS2ToUTF8StringAlloc(snapshot);
int result = SendGameSnapShotA(game, snapshot_A, final);
gsifree(snapshot_A);
return result;
}
#endif
/****************************************************************************/
void NewPlayerA(statsgame_t game, int pnum, char *name)
{
int i = -1;
FIXGAME(game, ;)
while (pnum >= ArrayLength(game->playernums))
ArrayAppend(game->playernums, &i);
i = game->totalplayers++;
/* update the pnum array */
ArrayReplaceAt(game->playernums,&i, pnum);
BucketIntOp(game, "ctime",bo_set,(int)(current_time() - game->sttime) / 1000,bl_player,pnum);
BucketStringOp(game,"player",bo_set,name, bl_player,pnum);
}
#ifdef GSI_UNICODE
void NewPlayerW(statsgame_t game, int pnum, unsigned short *name)
{
char* name_A = UCS2ToUTF8StringAlloc(name);
NewPlayerA(game, pnum, name_A);
gsifree(name_A);
}
#endif
/****************************************************************************/
void RemovePlayer(statsgame_t game,int pnum)
{
FIXGAME(game, ;);
BucketIntOp(game,"dtime",bo_set,(int)(current_time() - game->sttime) / 1000, bl_player, pnum);
}
/****************************************************************************/
void NewTeamA(statsgame_t game,int tnum, char *name)
{
int i = -1;
FIXGAME(game, ;)
while (tnum >= ArrayLength(game->teamnums))
ArrayAppend(game->teamnums, &i);
i = game->totalteams++;
/* update the tnum array */
ArrayReplaceAt(game->teamnums,&i, tnum);
BucketIntOp(game, "ctime",bo_set,(int)(current_time() - game->sttime) / 1000,bl_team,tnum);
BucketStringOp(game,"team",bo_set,name, bl_team,tnum);
}
#ifdef GSI_UNICODE
void NewTeamW(statsgame_t game,int tnum, unsigned short *name)
{
char* name_A = UCS2ToUTF8StringAlloc(name);
NewTeamA(game, tnum, name_A);
gsifree(name_A);
}
#endif
/****************************************************************************/
void RemoveTeam(statsgame_t game, int tnum)
{
FIXGAME(game, ;);
BucketIntOp(game,"dtime",bo_set,(int)(current_time() - game->sttime) / 1000, bl_team, tnum);
}
/****************************************************************************
* PERSISTENT STORAGE FUNCTIONS
****************************************************************************/
/****************************************************************************/
void PreAuthenticatePlayerPartner(int localid, const char * authtoken, const char *challengeresponse, PersAuthCallbackFn callback, void *instance)
{
char respformat[] = "\xC\x13\x1A\x1E\xD\x13\x28\x1D\x11\x1D\x11\x10\x24\x1D\x04\x0F\x0B\x3F\x51\x32\x2C\x1A\x00\x0B\x20\x2E\x4A\x19\x39\x0F\x1D\x25\x2C\x4D\x01";
//\authp\\authtoken\%s\resp\%s\lid\%d";
int len;
char data[256];
DOXCODE(respformat, sizeof(respformat)-1, enc3);
len = sprintf(data, respformat, authtoken, challengeresponse, localid);
SendPlayerAuthRequest(data, len, localid, callback, instance);
}
/****************************************************************************/
void PreAuthenticatePlayerPM(int localid, int profileid, const char *challengeresponse, PersAuthCallbackFn callback, void *instance)
{
char respformat[] = "\xC\x13\x1A\x1E\xD\x13\x28\x1D\x0\x1\x1\x24\x75\x16\x33\x18\x0\x10\x4\x1D\x55\x1B\x39\x14\x39\x16\x33\x4F\x1";
//\authp\\pid\%d\resp\%s\lid\%d
int len;
char data[256];
DOXCODE(respformat, sizeof(respformat)-1, enc3);
len = sprintf(data, respformat, profileid, challengeresponse,localid);
SendPlayerAuthRequest(data, len, localid, callback, instance);
}
/****************************************************************************/
void PreAuthenticatePlayerCDA(int localid, const char *nick, const char *keyhash, const char *challengeresponse, PersAuthCallbackFn callback, void *instance)
{
char respformat[] = "\xC\x13\x1A\x1E\xD\x13\x28\x1D\x1E\x1\x6\x13\xC\x57\x1C\x36\xE\x6\xD\x29\x11\x1B\xD\x24\x75\x1\x33\x18\x0\x10\x4\x1D\x55\x1B\x39\x14\x39\x16\x33\x4F\x1";
//\authp\\nick\%s\keyhash\%s\resp\%s\lid\%d
int len;
char data[256];
DOXCODE(respformat, sizeof(respformat)-1, enc3);
len = sprintf(data, respformat, nick, keyhash, challengeresponse,localid);
SendPlayerAuthRequest(data, len, localid, callback, instance);
}
#ifdef GSI_UNICODE
void PreAuthenticatePlayerCDW(int localid, const unsigned short *nick, const char *keyhash, const char *challengeresponse, PersAuthCallbackFn callback, void *instance)
{
char* nick_A = UCS2ToUTF8StringAlloc(nick);
PreAuthenticatePlayerCDA(localid, nick_A, keyhash, challengeresponse, callback, instance);
gsifree(nick_A);
}
#endif
/****************************************************************************/
void GetProfileIDFromCDA(int localid, const char *nick, const char *keyhash, ProfileCallbackFn callback, void *instance)
{
char respformat[] = "\xC\x15\xA\x1E\x15\xA\x10\x1D\x2C\x6\xC\x1B\x3B\x2E\x4A\x19\x39\x8\x11\x38\x18\x9\x16\x10\xC\x57\x1C\x36\x9\xA\x10\x1D\x55\xC";
//\getpid\\nick\%s\keyhash\%s\lid\%d
int len;
char data[512];
DOXCODE(respformat, sizeof(respformat)-1, enc3);
len = sprintf(data, respformat, nick, keyhash,localid);
if (sock != INVALID_SOCKET)
len = DoSend(data, len);
/* If the send failed, close the socket */
if (len <= 0)
{
CloseStatsConnection();
if (callback)
callback(0,-1,0,instance);
} else
{ /* set up the callback */
AddRequestCallback(rt_profilecb, localid, 0,(persisttype_t)0,0,callback, instance);
}
}
#ifdef GSI_UNICODE
void GetProfileIDFromCDW(int localid, const unsigned short *nick, const char *keyhash, ProfileCallbackFn callback, void *instance)
{
char* nick_A = UCS2ToUTF8StringAlloc(nick);
GetProfileIDFromCDA(localid, nick_A, keyhash, callback, instance);
gsifree(nick_A);
}
#endif
/****************************************************************************/
void GetPersistData(int localid, int profileid, persisttype_t type, int index, PersDataCallbackFn callback, void *instance)
{
GetPersistDataValuesModifiedA(localid,profileid, type,index,0,"",callback, instance);
}
void GetPersistDataModified(int localid, int profileid, persisttype_t type, int index, time_t modifiedsince, PersDataCallbackFn callback, void *instance)
{
GetPersistDataValuesModifiedA(localid,profileid, type,index, modifiedsince, "",callback, instance);
}
/****************************************************************************/
void SetPersistData(int localid, int profileid, persisttype_t type, int index, const char *data, int len, PersDataSaveCallbackFn callback, void *instance)
{
SetPersistDataHelper(localid, profileid, type, index, data, len, callback, instance, 0);
}
/****************************************************************************/
void GetPersistDataValuesModifiedA(int localid, int profileid, persisttype_t type, int index, time_t modifiedsince, char *keys, PersDataCallbackFn callback, void *instance)
{
char respformat[] = "\xC\x15\xA\x1E\x15\x7\x28\x1D\x0\x1\x1\x24\x75\x16\x33\x1A\x11\x1A\x4\x24\x2C\x4D\x1\x24\x34\x1B\x1\xE\x0\x1B\x28\x64\x14\x34\xE\x1D\x29\x1\x33\x4F\x16\x3F\x18\x28\x14\x34\x40\x1C";
char modformat[] = {'\\','m','o','d','\\','%','d','\0'}; //\\mod\\%d
//\getpd\\pid\%d\ptype\%d\dindex\%d\keys\%s\lid\%d
int len;
char data[512];
char tempkeys[256];
char *p;
DOXCODE(respformat, sizeof(respformat)-1, enc3);
strcpy(tempkeys, keys);
//replace the \ chars with #1
for (p = tempkeys; *p != 0; p++)
if (*p == '\\')
*p = '\x1';
len = sprintf(data, respformat, profileid, type, index, tempkeys, localid);
if (modifiedsince != 0) //append it
{
len += sprintf(data + len, modformat, modifiedsince);
}
if (sock != INVALID_SOCKET)
len = DoSend(data, len);
/* If the send failed, close the socket */
if (len <= 0)
{
CloseStatsConnection();
if (callback)
callback(localid, profileid,type, index, 0,0,"",0,instance);
} else
{ /* set up the callback */
AddRequestCallback(rt_datacb, localid, profileid,type, index, callback, instance);
}
}
void GetPersistDataValuesA(int localid, int profileid, persisttype_t type, int index, char *keys, PersDataCallbackFn callback, void *instance)
{
GetPersistDataValuesModifiedA(localid,profileid, type,index, 0, keys,callback, instance);
}
#ifdef GSI_UNICODE
void GetPersistDataValuesModifiedW(int localid, int profileid, persisttype_t type, int index, time_t modifiedsince, unsigned short*keys, PersDataCallbackFn callback, void *instance)
{
char* keys_A = UCS2ToUTF8StringAlloc(keys);
GetPersistDataValuesModifiedA(localid, profileid, type, index, modifiedsince, keys_A, callback, instance);
gsifree(keys_A);
}
#endif
#ifdef GSI_UNICODE
void GetPersistDataValuesW(int localid, int profileid, persisttype_t type, int index, unsigned short*keys, PersDataCallbackFn callback, void *instance)
{
GetPersistDataValuesModifiedW(localid,profileid, type,index, 0, keys,callback, instance);
}
#endif
/****************************************************************************/
void SetPersistDataValuesA(int localid, int profileid, persisttype_t type, int index, const char *keyvalues, PersDataSaveCallbackFn callback, void *instance)
{
SetPersistDataHelper(localid, profileid, type, index, keyvalues, (int)strlen(keyvalues) + 1, callback, instance, 1);
}
#ifdef GSI_UNICODE
void SetPersistDataValuesW(int localid, int profileid, persisttype_t type, int index, const unsigned short *keyvalues, PersDataSaveCallbackFn callback, void *instance)
{
char* keyvalues_A = UCS2ToUTF8StringAlloc(keyvalues);
SetPersistDataValuesA(localid, profileid, type, index, keyvalues_A, callback, instance);
gsifree(keyvalues_A);
}
#endif
/****************************************************************************/
int PersistThink()
{
int len;
int processed;
if (sock == INVALID_SOCKET)
return 0;
if (stats_initstate != init_complete)
return 0;
while (SocketReadable(sock))
{
if (rcvmax - rcvlen < 128) //make sure there are at least 128 bytes gsifree in the buffer
{
if (rcvmax < 256)
rcvmax = 256;
else
rcvmax *= 2;
rcvbuffer = gsirealloc(rcvbuffer, (unsigned int)(rcvmax+1));
if (rcvbuffer == NULL)
return 0; //errcon
}
len = recv(sock, rcvbuffer + rcvlen, rcvmax - rcvlen, 0);
if (len <= 0) //lost the connection
{
CloseStatsConnection();
return 0;
}
rcvlen += len;
rcvbuffer[rcvlen] = 0;
processed = ProcessInBuffer(rcvbuffer, rcvlen);
if (processed == rcvlen) //then we can just zero it
rcvlen = 0;
else
{
//shift the remaining data down
memmove(rcvbuffer,rcvbuffer + processed, (unsigned int)(rcvlen - processed));
rcvlen -= processed;
}
}
if (sock == INVALID_SOCKET)
return 0;
else
return 1;
}
/****************************************************************************/
int StatsThink()
{
return PersistThink();
}
/****************************************************************************
* UTILITY FUNCTIONS
****************************************************************************/
void InternalInit()
{
internal_init = 1;
enc1[0] = 'G';
enc3[0] = 'P';
finalstr[0] = '\\';
#ifdef ALLOW_DISK
statsfile[0] = 'g';
enc2[0] = 'I';
#endif
}
static int SendChallengeResponse(const char *indata, int gameport)
{
static char challengestr[] = {'\0','h','a','l','l','e','n','g','e','\0'};
char *challenge;
char resp[128];
char md5val[33];
/* make this harder to find in the string table */
char respformat[] = "\xC\x13\x1A\x1E\xD\x3F\x28\x26\x11\x5\x0\x16\x31\x1F\xA\x36\x40\x10\x28\x33\x15\x1B\x15\x17\x3E\x1\xA\x36\x40\x10\x28\x31\x1F\x1A\x11\x24\x75\x16\x33\x3\x1\x3F\x45";
/* \auth\\gamename\%s\response\%s\port\%d\id\1 */
int len;
challengestr[0] = 'c';
challenge = value_for_key(indata,challengestr );
if (challenge == NULL)
{
closesocket(sock);
return GE_DATAERROR;
}
len = sprintf(resp, "%d%s",g_crc32(challenge,(int)strlen(challenge)), gcd_secret_key);
MD5Digest((unsigned char *)resp, (unsigned int)len, md5val);
DOXCODE(respformat, sizeof(respformat)-1, enc3);
len = sprintf(resp,respformat,gcd_gamename, md5val, gameport);
if ( DoSend(resp, len) <= 0 )
{
closesocket(sock);
return GE_NOCONNECT;
}
return GE_NOERROR;
}
// 09-13-2004 BED Unused parameter removed
static int RecvSessionKey()
{
/* get the response */
static char sesskeystr[] = {'\0','e','s','s','k','e','y','\0'};
char resp[128];
char *stext;
int len = (int)recv(sock, resp,128,0);
if (gsiSocketIsError(len))
{
int anError = GOAGetLastError(sock);
closesocket(sock);
if ((anError != WSAEWOULDBLOCK) && (anError != WSAETIMEDOUT) && (anError != WSAEINPROGRESS))
return GE_NOCONNECT;
else
return GE_DATAERROR; //temp fix in case len == -1, SOCKET_ERROR
}
resp[len] = 0;
DOXCODE(resp, len, enc1);
sesskeystr[0] = 's';
stext = value_for_key(resp, sesskeystr);
if (stext == NULL)
{
closesocket(sock);
return GE_DATAERROR;
} else
connid = atoi(stext);
return GE_NOERROR;
}
static int DoSend(char *data, int len)
{
int sent = 0;
DOXCODE(data,len, enc1);
strcpy(data+len,finalstr);
// Loop to make sure async send goes through!
while(sent < (len+7))
{
// Send remaining data
int ret = send(sock, (data+sent), (len+7-sent), 0);
if (gsiSocketIsError(ret))
{
int anError = GOAGetLastError(sock);
if ((anError != WSAEWOULDBLOCK) && (anError != WSAETIMEDOUT) && (anError != WSAEINPROGRESS))
return anError;
}
else if (ret == 0)
{
// socket was closed
return -1;
}
else
sent += ret;
};
return sent;
}
#ifdef ALLOW_DISK
/* Note: lots of this is byte order and type size specific, but it shouldn't
matter since the data is read/written always on the same machine */
#define DISKLENXOR 0x70F33A5F
static void CheckDiskFile()
{
FILE *f;
char *line;
char *alldata;
int len, check, alllen, fsize;
char filemode[3];
/* hide our file access from the string table */
filemode[0] = 'r'; filemode[1] = 'b'; filemode[2] = 0;
f = fopen(statsfile,filemode);
if (!f)
return;
/* get the size */
fseek(f, 0, SEEK_END);
fsize = ftell(f);
fseek(f, 0, SEEK_SET);
/* make room for the whole thing */
alldata = (char *)gsimalloc(fsize + 2);
alldata[0] = 0;
alllen = 0;
while (!feof(f) && !ferror(f))
{
/* read the check and line values */
if (fread(&check,sizeof(check),1,f) == 0 ||
fread(&len,sizeof(len),1,f) == 0)
break;
len ^= DISKLENXOR;
line = (char *)gsimalloc(len + 1);
/* read the data */
if (fread(line, 1, len, f) != (size_t)len)
break;
line[len] = 0;
/* decode for checking */
DOXCODE(line, len, enc2);
/* double "check" */
if (check != g_crc32(line, len))
{
gsifree(line);
break;
}
/* encode for xmission */
DOXCODE(line, len, enc1);
memcpy(alldata + alllen, line, len);
alllen += len;
memcpy(alldata + alllen, finalstr, 7);
alllen += 7;
gsifree(line);
}
fclose(f);
/* try to send */
len = send(sock, alldata, alllen, 0);
if (len <= 0)
{
closesocket(sock);
sock = INVALID_SOCKET;
} else
remove(statsfile);
}
static void DiskWrite(char *line, int len)
{
FILE *f;
int check;
int temp;
char filemode[3];
/* hide our file access from the string table */
filemode[0] = 'a'; filemode[1] = 'b'; filemode[2] = 0;
f = fopen(statsfile,filemode);
if (!f)
return;
check = g_crc32(line, len);
fwrite(&check, sizeof(check),1,f);
temp = len ^ DISKLENXOR;
fwrite(&temp, sizeof(temp), 1, f);
DOXCODE(line, len, enc2);
fwrite(line, 1, len, f);
fclose(f);
}
#endif /* ALLOW_DISK */
/* simple xor encoding */
static void xcode_buf(char *buf, int len)
{
int i;
char *pos = enc;
for (i = 0 ; i < len ; i++)
{
buf[i] ^= *pos++;
if (*pos == 0)
pos = enc;
}
}
#define MULTIPLIER -1664117991
static int g_crc32(char *s, int len)
{
int i;
int hashcode = 0;
for (i = 0; i < len; i++)
hashcode = hashcode * MULTIPLIER + s[i];
return hashcode;
}
static void create_challenge(int challenge, char chstr[9])
{
char *p = chstr;
sprintf(chstr, "%08x",challenge);
while (*p != 0)
{
*p = (char)((*p) + ('A' - '0') + (p-chstr));
p++;
}
}
/* value_for_key: this returns a value for a certain key in s, where s is a string
containing key\value pairs. If the key does not exist, it returns NULL.
Note: the value is stored in a common buffer. If you want to keep it, make a copy! */
static char *value_for_key(const char *s, const char *key)
{
static int valueindex;
char *pos,*pos2;
char keyspec[256]="\\";
static char value[2][256];
valueindex ^= 1;
strcat(keyspec,key);
strcat(keyspec,"\\");
pos = strstr(s,keyspec);
if (!pos)
return NULL;
pos += strlen(keyspec);
pos2 = value[valueindex];
while (*pos && *pos != '\\')
*pos2++ = *pos++;
*pos2 = '\0';
return value[valueindex];
}
/* like value_for_key, but returns an empty string instead of NULL in the not-found case */
static char *value_for_key_safe(const char *s, const char *key)
{
char *temp;
temp = value_for_key(s, key);
if (!temp)
return "";
else
return temp;
}
/* Return a sockaddrin for the given host (numeric or DNS) and port)
Returns the hostent in savehent if it is not NULL */
static int get_sockaddrin(const char *host, int port, struct sockaddr_in *saddr, struct hostent **savehent)
{
struct hostent *hent = NULL;
memset(saddr,0,sizeof(struct sockaddr_in));
saddr->sin_family = AF_INET;
saddr->sin_port = htons((unsigned short)port);
if (host == NULL)
saddr->sin_addr.s_addr = INADDR_ANY;
else
saddr->sin_addr.s_addr = inet_addr(host);
if (saddr->sin_addr.s_addr == INADDR_NONE)
{
hent = gethostbyname(host);
if (!hent)
return 0;
saddr->sin_addr.s_addr = *(unsigned int *)hent->h_addr_list[0];
}
if (savehent != NULL)
*savehent = hent;
return 1;
}
/* adds a request callback to the list */
static void AddRequestCallback(reqtype_t reqtype, int localid, int profileid, persisttype_t pdtype, int pdindex, void *callback, void *instance)
{
serverreq_t req;
req.callback = callback;
req.instance = instance;
req.localid = localid;
req.reqtype = reqtype;
req.profileid = profileid;
req.pdtype = pdtype;
req.pdindex = pdindex;
if (serverreqs == NULL) //create the callback array
{
serverreqs = ArrayNew(sizeof(serverreq_t),2,NULL);
}
ArrayAppend(serverreqs,&req);
}
/* sends the player authentication request (GP or CD) */
static void SendPlayerAuthRequest(char *data, int len, int localid, PersAuthCallbackFn callback, void *instance)
{
int sentlen = 0;
char connerror[] = "\x13\x1D\x1\x4\x0\x0\x0\x28\x1F\x6\x45\x34\x3F\x1\x1B";
//"Connection Lost"
if (sock != INVALID_SOCKET)
sentlen = DoSend(data, len);
/* If the send failed, close the socket */
if (sentlen <= 0)
{
CloseStatsConnection();
DOXCODE(connerror, sizeof(connerror)-1, enc3);
if (callback)
{
#ifndef GSI_UNICODE
callback(localid, 0,0, connerror ,instance);
#else
unsigned short connerror_W[] = L"\x13\x1D\x1\x4\x0\x0\x0\x28\x1F\x6\x45\x34\x3F\x1\x1B";
callback(localid, 0, 0, connerror_W, instance);
#endif
}
} else
{ /* set up the callback */
AddRequestCallback(rt_authcb, localid, 0,(persisttype_t)0,0,callback, instance);
}
}
/* send a set request, if kvset, then only those keys/values will bet updated */
static void SetPersistDataHelper(int localid, int profileid, persisttype_t type, int index, const char *data, int len, PersDataSaveCallbackFn callback, void *instance, int kvset)
{
char respformat[] = "\xC\x1\xA\x1E\x15\x7\x28\x1D\x0\x1\x1\x24\x75\x16\x33\x1A\x11\x1A\x4\x24\x2C\x4D\x1\x24\x34\x1B\x1\xE\x0\x1B\x28\x64\x14\x34\xE\xE\xC\x57\xB\x36\x9\xA\x10\x1D\x55\xC\x39\x14\x35\x1C\x8\x1E\xD\x3F\x51\x25\x2C\xC\x4\xC\x31\x2E";
//\setpd\\pid\%d\ptype\%d\dindex\%d\kv\%d\lid\%d\length\%d\data\ --
int tlen;
char tdata[512];
char *senddata;
DOXCODE(respformat, sizeof(respformat)-1, enc3);
if (type == pd_private_ro || type == pd_public_ro)
{ //can't set read-only types, check that client side
if (callback)
callback(localid, profileid, type, index, 0, 0, instance);
return;
}
tlen = sprintf(tdata, respformat, profileid, type, index, kvset, localid, len);
if (tlen + len < 480) //we have enough room to put it in the data block
{
memcpy(tdata + tlen, data, (unsigned int)len);
senddata = tdata;
} else //need to alloc a temp buffer
{
senddata = (char *)gsimalloc((unsigned int)(len + tlen + 256));
memcpy(senddata, tdata, (unsigned int)tlen);
memcpy(senddata + tlen, data, (unsigned int)len);
}
if (sock != INVALID_SOCKET)
tlen = DoSend(senddata, tlen + len);
/* If the send failed, close the socket */
if (tlen <= 0)
{
CloseStatsConnection();
if (callback)
callback(localid, profileid, type, index, 0, 0, instance);
} else
{ /* set up the callback */
AddRequestCallback(rt_savecb, localid, profileid,type, index, callback, instance);
}
if (senddata != tdata) //if we alloc'd before sending
gsifree(senddata);
}
/* returns 1 if the socket is readable, 0 otherwise */
static int SocketReadable(SOCKET s)
{
return CanReceiveOnSocket(s);
/*
fd_set set;
struct timeval tv = {0,0};
int n;
if (s == INVALID_SOCKET)
return 0;
FD_ZERO(&set);
FD_SET(s, &set);
n = select(FD_SETSIZE, &set, NULL, NULL, &tv);
return n;
*/
}
/* find the \final\ string */
static char *FindFinal(char *buff, int len)
{
char *pos = buff;
while (pos - buff < len - 6)
{
if (pos[0] == '\\' &&
pos[1] == 'f' &&
pos[2] == 'i' &&
pos[3] == 'n' &&
pos[4] == 'a' &&
pos[5] == 'l' &&
pos[6] == '\\')
{
return pos;
} else
pos++;
}
return NULL;
}
/* find the request in the callback list */
static int FindRequest(reqtype_t reqtype, int localid, int profileid)
{
int i;
serverreq_t *req;
if (serverreqs == NULL)
return -1;
for (i = 0 ; i < ArrayLength(serverreqs); i++)
{
req = (serverreq_t *)ArrayNth(serverreqs, i);
if (req->reqtype == reqtype && req->localid == localid && req->profileid == profileid)
return i;
}
return -1;
}
/* process the playerauth result */
static void ProcessPlayerAuth(const char *buf, int len)
{
// \\pauthr\\100000\\lid\\1
int reqindex;
char *errmsg;
int pid;
int lid;
pid = atoi(value_for_key_safe(buf,"pauthr"));
lid = atoi(value_for_key_safe(buf,"lid"));
errmsg = value_for_key_safe(buf,"errmsg");
reqindex = FindRequest(rt_authcb,lid,0);
if (reqindex == -1)
return;
((serverreq_t *)ArrayNth(serverreqs, reqindex))->profileid = pid;
CallReqCallback(reqindex,(pid > 0),0, errmsg,0);
GSI_UNUSED(len);
}
/* process the get profileid result */
static void ProcessGetPid(const char *buf, int len)
{
// \\getpidr\\100000\\lid\\1
int reqindex;
int pid;
int lid;
pid = atoi(value_for_key_safe(buf,"getpidr"));
lid = atoi(value_for_key_safe(buf,"lid"));
reqindex = FindRequest(rt_profilecb,lid,0);
if (reqindex == -1)
return;
((serverreq_t *)ArrayNth(serverreqs, reqindex))->profileid = pid;
CallReqCallback(reqindex,(pid > 0),0,NULL,0);
GSI_UNUSED(len);
}
/* process the get data result */
static void ProcessGetData(const char *buf, int len)
{
// \\getpdr\\1\\lid\\1\\mod\\1234\\length\\5\\data\\mydata\\final
int reqindex;
int pid;
int lid;
int success;
int length;
time_t modified;
char *data;
success = atoi(value_for_key_safe(buf,"getpdr"));
lid = atoi(value_for_key_safe(buf,"lid"));
pid = atoi(value_for_key_safe(buf,"pid"));
modified = atoi(value_for_key_safe(buf,"mod"));
reqindex = FindRequest(rt_datacb,lid,pid);
if (reqindex == -1)
return;
length = atoi(value_for_key_safe(buf,"length"));
data = strstr(buf,"\\data\\");
if (!data)
{
length = 0;
data = "";
} else
data += 6; //skip the key
CallReqCallback(reqindex,success,modified, data,length);
GSI_UNUSED(len);
}
/* process the set data result */
static void ProcessSetData(const char *buf, int len)
{
// \\setpdr\\1\\lid\\2\\pid\\100000\\mod\\12345
int reqindex;
int pid;
int lid;
int success;
int modified;
success = atoi(value_for_key_safe(buf,"setpdr"));
pid = atoi(value_for_key_safe(buf,"pid"));
lid = atoi(value_for_key_safe(buf,"lid"));
modified = atoi(value_for_key_safe(buf,"mod"));
reqindex = FindRequest(rt_savecb,lid,pid);
if (reqindex == -1)
return;
CallReqCallback(reqindex,success,modified,NULL,0);
GSI_UNUSED(len);
}
/* process a single statement */
static void ProcessStatement(char *buff, int len)
{
//determine the type
buff[len] = 0;
// printf("GOT: %s\n",buff);
if (strncmp(buff,"\\pauthr\\",8) == 0)
{
ProcessPlayerAuth(buff, len);
} else if (strncmp(buff,"\\getpidr\\",9) == 0)
{
ProcessGetPid(buff, len);
} else if (strncmp(buff,"\\getpidr\\",9) == 0)
{
ProcessGetPid(buff, len);
} else if (strncmp(buff,"\\getpdr\\",8) == 0)
{
ProcessGetData(buff, len);
} else if (strncmp(buff,"\\setpdr\\",8) == 0)
{
ProcessSetData(buff, len);
}
}
/* processes statements in the buffer and returns amount processed */
// 09-13-2004 BED Modified loop to silence compiler warning
static int ProcessInBuffer(char *buff, int len)
{
char *pos;
int oldlen = len;
pos = FindFinal(buff, len);
//while (len > 0 && (pos = FindFinal(buff, len)))
while ((len > 0) && (pos != NULL))
{
DOXCODE(buff,pos - buff, enc1);
ProcessStatement(buff, pos - buff);
len -= (pos - buff) + 7;
buff = pos + 7; //skip the final
if (len>0)
pos = FindFinal(buff, len);
}
return oldlen - len; //amount processed
}
/* call a single callback function */
static void CallReqCallback(int reqindex, int success, time_t modified, char *data, int length)
{
serverreq_t *req;
if (reqindex < 0 || reqindex >= ArrayLength(serverreqs))
return;
req = (serverreq_t *)ArrayNth(serverreqs, reqindex);
if (req->callback)
switch (req->reqtype)
{
case rt_authcb:
#ifndef GSI_UNICODE
((PersAuthCallbackFn )req->callback)(req->localid, req->profileid, success, data, req->instance);
#else
{
unsigned short* data_W = UTF8ToUCS2StringAlloc(data);
((PersAuthCallbackFn )req->callback)(req->localid, req->profileid, success, data_W, req->instance);
gsifree(data_W);
}
#endif
break;
case rt_datacb:
((PersDataCallbackFn )req->callback)(req->localid,req->profileid,req->pdtype, req->pdindex, success, modified, data, length, req->instance);
break;
case rt_savecb:
((PersDataSaveCallbackFn )req->callback)(req->localid, req->profileid, req->pdtype, req->pdindex,success, modified, req->instance);
break;
case rt_profilecb:
((ProfileCallbackFn )req->callback)(req->localid, req->profileid, success, req->instance);
break;
}
ArrayDeleteAt(serverreqs,reqindex);
}
/* if we get disconnected while callbacks are still pending, make sure we
call all of them, with a success of 0 */
static void ClosePendingCallbacks()
{
int i;
if (serverreqs == NULL)
return;
for (i = ArrayLength(serverreqs) - 1 ; i >= 0 ; i--)
{
char connerror[] = "\x13\x1D\x1\x4\x0\x0\x0\x28\x1F\x6\x45\x34\x3F\x1\x1B";
//"Connection Lost"
DOXCODE(connerror, sizeof(connerror)-1, enc3);
CallReqCallback(i,0,0,connerror,0);
}
ArrayFree(serverreqs);
serverreqs = NULL;
}
/****************************************************************************/
/* BUCKET FUNCTIONS */
/****************************************************************************/
int GetTeamIndex(statsgame_t game, int tnum)
{
FIXGAME(game, tnum);
return *(int *)ArrayNth(game->teamnums,tnum);
}
int GetPlayerIndex(statsgame_t game, int pnum)
{
FIXGAME(game, pnum);
return *(int *)ArrayNth(game->playernums,pnum);
}
static int ServerOpInt(statsgame_t game,char *name, BucketFunc func, int value, int index)
{
int *ret;
DoFunc(func, game, name, &value, bt_int, ret);
GSI_UNUSED(index);
return *(int *)ret;
}
static double ServerOpFloat(statsgame_t game,char *name, BucketFunc func, double value, int index)
{
double *ret;
DoFunc(func, game, name, &value, bt_float, ret);
GSI_UNUSED(index);
return *(double *)ret;
}
static char *ServerOpString(statsgame_t game,char *name, BucketFunc func, char *value, int index)
{
char *ret;
DoFunc(func, game, name, value, bt_string, ret);
GSI_UNUSED(index);
return ret;
}
static int TeamOpInt(statsgame_t game,char *name, BucketFunc func, int value, int index)
{
char fullname[64];
sprintf(fullname, "%s_t%d",name, GetTeamIndex(game, index));
return ServerOpInt(game, fullname, func, value, index);
}
static double TeamOpFloat(statsgame_t game,char *name, BucketFunc func, double value, int index)
{
char fullname[64];
sprintf(fullname, "%s_t%d",name, GetTeamIndex(game, index));
return ServerOpFloat(game, fullname, func, value, index);
}
static char *TeamOpString(statsgame_t game,char *name, BucketFunc func, char *value, int index)
{
char fullname[64];
sprintf(fullname, "%s_t%d",name, GetTeamIndex(game, index));
return ServerOpString(game, fullname, func, value, index);
}
static int PlayerOpInt(statsgame_t game,char *name, BucketFunc func, int value, int index)
{
char fullname[64];
sprintf(fullname, "%s_%d",name, GetPlayerIndex(game, index));
return ServerOpInt(game, fullname, func, value, index);
}
static double PlayerOpFloat(statsgame_t game,char *name, BucketFunc func, double value, int index)
{
char fullname[64];
sprintf(fullname, "%s_%d",name, GetPlayerIndex(game, index));
return ServerOpFloat(game, fullname, func, value, index);
}
static char *PlayerOpString(statsgame_t game,char *name, BucketFunc func, char *value, int index)
{
char fullname[64];
sprintf(fullname, "%s_%d",name, GetPlayerIndex(game, index));
return ServerOpString(game, fullname, func, value, index);
}
static char *CreateBucketSnapShot(bucketset_t buckets)
{
return DumpBucketSet(buckets);
}
#ifdef __cplusplus
}
#endif