/****** gstats.h GameSpy Stats/Tracking SDK Copyright 1999-2007 GameSpy Industries, Inc ****** Please see the GameSpy Stats and Tracking SDK documentation for more info 08-23-00 - DDW Fixed a problem that prevented opening/closing/re-opening of the connection within a single session. *****/ #ifndef _GSTATS_H_ #define _GSTATS_H_ /******** INCLUDES ********/ #include "../common/gsCommon.h" #include "gbucket.h" #ifdef __cplusplus extern "C" { #endif #if defined(_WIN32) // Warnings are generated because we store function ptrs into a void* array #pragma warning(disable: 4152) // function to data ptr #pragma warning(disable: 4055) // data to function ptr #endif /******** TYPEDEFS ********/ /* The abstracted "game" structure */ typedef struct statsgame_s *statsgame_t; /* All of the operations you can do on a bucket */ typedef enum {bo_set, bo_add, bo_sub, bo_mult, bo_div, bo_concat, bo_avg} bucketop_t; #define NUMOPS 7 /* The types of buckets (server info, team info, or player info) */ typedef enum {bl_server, bl_team, bl_player} bucketlevel_t; /* Init states for async initialization */ typedef enum {init_none, init_failed, init_connecting, init_awaitchallenge, init_awaitsessionkey, init_complete} initstate_t; /* Used by the bucket operation macros */ typedef void *(*BucketFunc)(bucketset_t set, char *name,void *value); typedef int (*SetIntFunc)(statsgame_t game,char *name, BucketFunc func, int value, int index); typedef double (*SetFloatFunc)(statsgame_t game,char *name, BucketFunc func, double value, int index); typedef char *(*SetStringFunc)(statsgame_t game,char *name, BucketFunc func, char *value, int index); extern BucketFunc bucketfuncs[NUMOPS]; extern void * bopfuncs[][3]; /******** DEFINES ********/ /* Error codes */ #define GE_NOERROR 0 #define GE_NOSOCKET 1 /* Unable to create a socket */ #define GE_NODNS 2 /* Unable to resolve a DNS name */ #define GE_NOCONNECT 3 /* Unable to connect to stats server, or connection lost */ #define GE_BUSY 4 /* Not used */ #define GE_DATAERROR 5 /* Bad data from the stats server */ #define GE_CONNECTING 6 /* Connect did no immediately complete. Call InitStatsThink() */ #define GE_TIMEDOUT 7 /* Connect attempt timed out */ /* Types of snapshots, update (any snapshot that is not final) or final */ #define SNAP_UPDATE 0 #define SNAP_FINAL 1 /* If you want to allow disk logging in case the stats server isn't available. This has SERIOUS security repercussions, so please read the docs before turning this on */ #define ALLOW_DISK #if defined(NOFILE) #undef ALLOW_DISK #endif /* make sure it's never defined on platforms with no disk! */ /******** VARS ********/ /* You need to fill these in with your game-specific info */ extern char gcd_secret_key[256]; extern char gcd_gamename[256]; /* The hostname of the stats server. If the app resolves the hostname, an IP can be stored here before calling InitStatsConnection */ extern char StatsServerHostname[64]; /******** PROTOTYPES ********/ #ifndef GSI_UNICODE #define GenerateAuth GenerateAuthA #define SendGameSnapShot SendGameSnapShotA #define NewPlayer NewPlayerA #define NewTeam NewTeamA #else #define GenerateAuth GenerateAuthW #define SendGameSnapShot SendGameSnapShotW #define NewPlayer NewPlayerW #define NewTeam NewTeamW #endif /******** InitStatsConnection DESCRIPTION Opens a connection to the stats server. Should be done before calling NewGame or any of the bucket/snapshot functions. May block for 1-2 secs while the connection is established so you will want to do this before gameplay starts or in another thread. PARAMETERS gameport: integer port associated with your server (may be the same as your developer spec query port). Used only to help players differentiate between servers on the same machine (no queries are done on it). If not appropriate for your game, pass in 0. RETURNS GE_NODNS: Unable to resolve stats server DNS GE_NOSOCKET: Unable to create data socket GE_NOCONNECT: Unable to connect to stats server GE_DATAERROR: Unable to receive challenge from stats server, or bad challenge GE_NOERROR: Connected to stats server and ready to send data Note: You can still call ANY of the other Stats SDK functions, even if the connection fails. If you have disk logging enabled, these calls will be logged for future sending, otherwise they will be discarded. *********/ int InitStatsConnection(int gameport); int InitStatsAsync(int gameport, gsi_time theInitTimeout); int InitStatsThink(); /******** StatsThink DESCRIPTION Eats up any incoming keep-alive messages that are sent by the stats server. Returns any errors occur because of a socket problem or if the SDK was not completely initialized. RETURNS 1 if no errors occured during read, 0 on all other errors ********/ int StatsThink(); /******** IsStatsConnected DESCRIPTION Returns whether or not you are currently connected to the stats server. Even if your initial connection was successful, you may lose connection later and want to try to reconnnect RETURNS 1 if connected, 0 otherwise *********/ int IsStatsConnected(); /******** CloseStatsConnection DESCRIPTION Closes the connection to the stats server. You should do this when done with the connection. *********/ void CloseStatsConnection(void); /******** GetChallenge DESCRIPTION Returns a string that should be sent to clients for authentication (using GenerateAuth). You do not have to free the string when done. This string will be constant for the entire length of the game and is generated during the call to NewGame. PARAMETERS game: Game to return the challenge string for. If game is NULL, the last game created with NewGame will be used. RETURNS A string to send to clients so they can authorize. If you game is NULL and you haven't created a game with NewGame, it returns "NULLGAME". *********/ char *GetChallenge(statsgame_t game); /******** GenerateAuth DESCRIPTION Should be used on the CLIENT SIDE to generate an authentication reply (auth_N) for a given challenge and password (CD Key or Profile password) PARAMETERS challenge: The challenge string sent by the server. On the server this should be generated with GetChallenge password: The CD Key (un-hashed) or profile password response: The output authentication string RETURNS A pointer to response *********/ char *GenerateAuth(const char *challenge, const gsi_char *password,/*[out]*/char response[33]); /******** NewGame DESCRIPTION Creates a new game for logging and registers it with the stats server. Creates all the game structures, including buckets if needed. PARAMETERS usebuckets: Set to 1 for bucket based logging, 0 if you are going to create the snapshots yourself. See the SDK for more info. RETURNS A pointer to the new game. If you are not connected, and disk logging is disabled, this will be NULL. You can still pass NULL to any function without causing any errors. Note: The last game created by NewGame is stored internally. If you only create / use one game at a time, you can simply discard the return value and pass NULL for game into all of the bucket and snapshot functions. *********/ statsgame_t NewGame(int usebuckets); /******** FreeGame DESCRIPTION Frees a game and its associated structures (including buckets). You should send a final snapshot for the game (using SendGameSnapShot with SNAP_FINAL) before freeing the game. PARAMETERS game: The game you want to free. If set to NULL, it will free the last game created with NewGame. *********/ void FreeGame(statsgame_t game); /******** SendGameSnapShot DESCRIPTION Sends a snapshot of information about the current game. If bucket based logging is enabled the snapshot will be generated from the buckets, otherwise you should provide it in "snapshot". PARAMETERS game: The game to send a snapshot for. If set to NULL, the last game created with NewGame will be used. snapshot: The snapshot to send. If you are using buckets, this will not be used, so you can pass in NULL final: If this is SNAP_UPDATE, the game is marked as in progress, if it is SNAP_FINAL, the game is marked as complete. RETURNS GE_DATAERROR: If game is NULL and the last game created by NewGame failed (because the connection was lost and disk logging is disabled) GE_NOCONNECT: If the connection is lost and disk logging is disabled GE_NOERROR: The update was sent, or disk logging is enabled and the game was logged *********/ int SendGameSnapShot(statsgame_t game, const gsi_char *snapshot, int final); /****************************** BUCKET FUNCTION PROTOTYPES These functions are only used for bucket-based logging *******************************/ /******** Bucket_____Op DESCRIPTION Performs an operation on a bucket for a game. If the bucket doesn't exist already, the call will set the bucket to whatever "value" is. You can always create each bucket explicitly by using bo_set with whatever initial value you want the bucket to have. Valid operations include set, add, subtract, multiply, divide, concat, and average. Each bucket type (int, float, or string) has its own operation function, always call the same one for each bucket (i.e. don't create a bucket with BucketIntOp then try to add a float with BucketFloatOp). PARAMETERS game: The game to send containing the bucket you want to operate on. If set to NULL, the last game created with NewGame will be used. name: The name of the bucket to update. Note that for player or team buckets, this name does NOT include the "_" or "_t" (e.g. "score" for player score, not "score_N"). The underscore and number will be added automatically. operation: One of the bucketop_t enums defined above value: Argument for the operation (bucket OP= value, e.g. bucket += value, bucket *= value) bucketlevel: One of the bucketlevel_t enums defined above. Determines whether you are referring to a server, player, or team bucket. Note that you can have seperate buckets of each type with the same name (e.g. "score" player bucket for each player and "score" team bucket for each team) index: For player or team buckets, the game index of the player or team (as passed to NewPlayer or NewTeam). This will be translated to the actual index internally. Not used for server buckets (bl_server). *********/ #define BucketIntOp(game, name, operation, value, bucketlevel, index) (((SetIntFunc)bopfuncs[bucketlevel][bt_int])(game,name,bucketfuncs[operation],value,index) ) #define BucketFloatOp(game, name, operation, value, bucketlevel, index) (((SetFloatFunc)bopfuncs[bucketlevel][bt_float])(game,name,bucketfuncs[operation],value,index) ) #define BucketStringOp(game, name, operation, value, bucketlevel, index) (((SetStringFunc)bopfuncs[bucketlevel][bt_string])(game,name,bucketfuncs[operation],value,index) ) /******** NewPlayer DESCRIPTION Adds a "player" to the game and assigns them an internal player number. Sets their connect time to the number of seconds since NewGame was called. PARAMETERS game: The game to add the player to. If set to NULL, the last game created with NewGame will be used. pnum: Your internal reference for this player, use this value in any calls to the Bucket___Op functions. name: The name for this player. If you don't have one yet, set it to empty ("") then call: BucketStringOp(game,"player",bo_set,realplayername, bl_player, pnum) when you get a realplayername. **********/ void NewPlayer(statsgame_t game,int pnum, gsi_char *name); /******** RemovePlayer DESCRIPTION Removes a "player" from the game and sets their disconnect time to the number of seconds since NewGame was called. PARAMETERS game: The game to remove the player from. If set to NULL, the last game created with NewGame will be used. pnum: Your internal reference for this player, use this value in any calls to the Bucket___Op functions. **********/ void RemovePlayer(statsgame_t game,int pnum); /********* NewTeam RemoveTeam DESCRIPTION See the player functions above. These function the same, except for teams **********/ void NewTeam(statsgame_t game,int tnum, gsi_char *name); void RemoveTeam(statsgame_t game,int tnum); /********* GetPlayerIndex GetTeamIndex DESCRIPTION Gets the gstats reference number for that player or team. For example, if you start the game and players 0, 1, and 2 join, then player 1 leaves, and another player 1 joins, the new player 1 will be referenced by gstats as 3. If player 3 joins, it will be referenced as player 4, and so on. Normally this doesn't matter to you, but if you want to do a key name or key value that references a player or team number (for example, setting a player's team number), you need to use the translated values. PARAMETERS game: The game to retrieve the translated value for. If set to NULL,the last game created with NewGame will be used. pnum/tnum: Your internal player or team number (as sent to NewTeam/NewPlayer) **********/ int GetPlayerIndex(statsgame_t game, int pnum); int GetTeamIndex(statsgame_t game, int tnum); #ifdef __cplusplus } #endif #endif