#include "AdeptHeaders.hpp" #include "GameSpy.h" /****** gserver.c GameSpy C Engine SDK Copyright 1999 GameSpy Industries, Inc Suite E-204 2900 Bristol Street Costa Mesa, CA 92626 (714)549-7689 Fax(714)549-0757 ****** Updated 10-15-99 (BGW) Modified ServerParseKeyVals to actually parse and store empty values for keys (i.e. "\delete\\" adds key="delete" and value="") Updated 6-17-99 (DDW) Added new tokenize function to handle empty values for keys *******/ #include "nonport.h" #include "goaceng.h" #include "gserver.h" #include #include #include #include static int _cdecl KeyValHash(const void *elem, int numbuckets); static int _cdecl KeyValCompare(const void *entry1, const void *entry2); static int _cdecl CaseInsensitiveCompare(const void *entry1, const void *entry2); static void _cdecl KeyValFree(void *elem); void _cdecl ServerFree(void *elem) { //free a server! GServer server = *(GServer *)elem; TableFree(server->keyvals); free(server); } GServer ServerNew(char *ip, int port) { GServer server; server = (GServer)malloc(sizeof(struct GServerImplementation)); strncpy(server->ip,ip, 16); server->ip[15] = 0; //make sure it's terminated server->port = port; server->ping = 9999; server->keyvals = TableNew(sizeof(GKeyValuePair),16, KeyValHash, KeyValCompare, KeyValFree); return server; } static char *mytok(char *instr, char delim) { char *result; static char *thestr; if (instr) thestr = instr; result=thestr; while (*thestr && *thestr != delim) { thestr++; } if (thestr == result) result = NULL; if (*thestr) //not the null term *thestr++ = '\0'; return result; } void ServerParseKeyVals(GServer server, char *keyvals) { char *k, *v; GKeyValuePair kvpair; k = mytok(++keyvals,'\\'); //skip over starting backslash while (k != NULL) { v = mytok(NULL,'\\'); if (v != NULL) { kvpair.key = _strdup(k); kvpair.value = _strdup(v); TableEnter(server->keyvals, &kvpair); } else { // // Allow empty values (i.e. "\delete\\" adds a key "delete" // with a value "")... (15oct99/bgw) // kvpair.key = _strdup(k); kvpair.value = _strdup(""); TableEnter(server->keyvals, &kvpair); } k = mytok(NULL,'\\'); } } /* ServerGetPing ---------------- Returns the ping for the specified server. */ int ServerGetPing(GServer server) { return server->ping; } /* ServerGetAddress ------------------- Returns the string, dotted IP address for the specified server */ char *ServerGetAddress(GServer server) { return server->ip; } /* ServerGetPort ---------------- Returns the "query" port for the specified server. */ int ServerGetQueryPort(GServer server) { return server->port; } static GKeyValuePair *ServerRuleLookup(GServer server, char *key) { GKeyValuePair kvp; kvp.key = key; return (GKeyValuePair *)TableLookup(server->keyvals, &kvp); } /* ServerGet[]Value ------------------ Returns the value for the specified key. */ char *ServerGetStringValue(GServer server, char *key, char *sdefault) { GKeyValuePair *kv; if (strcmp(key,"hostaddr") == 0) //ooh! they want the hostaddr! return ServerGetAddress(server); kv = ServerRuleLookup(server,key); if (!kv) return sdefault; return kv->value; } int ServerGetIntValue(GServer server, char *key, int idefault) { GKeyValuePair *kv; if (strcmp(key,"ping") == 0) //ooh! they want the ping! return ServerGetPing(server); kv = ServerRuleLookup(server,key); if (!kv) return idefault; return atoi(kv->value); } double ServerGetFloatValue(GServer server, char *key, double fdefault) { GKeyValuePair *kv; kv = ServerRuleLookup(server,key); if (!kv) return fdefault; return atof(kv->value); } char *ServerGetPlayerStringValue(GServer server, int playernum, char *key, char *sdefault) { char newkey[32]; sprintf(newkey,"%s_%d",key,playernum); return ServerGetStringValue(server, newkey, sdefault); } int ServerGetPlayerIntValue(GServer server, int playernum, char *key, int idefault) { char newkey[32]; sprintf(newkey,"%s_%d",key,playernum); return ServerGetIntValue(server, newkey, idefault); } double ServerGetPlayerFloatValue(GServer server, int playernum, char *key, double fdefault) { char newkey[32]; sprintf(newkey,"%s_%d",key,playernum); return ServerGetFloatValue(server, newkey, fdefault); } /* ServerEnumKeys ----------------- Enumerates the keys/values for a given server by calling KeyEnumFn with each key/value. The user-defined instance data will be passed to the KeyFn callback */ static void _cdecl KeyMapF(void *elem, void *clientData) { GKeyValuePair *kv = (GKeyValuePair *)elem; GEnumData *ped = (GEnumData *)clientData; ped->EnumFn(kv->key, kv->value, ped->instance); } void ServerEnumKeys(GServer server, KeyEnumFn KeyFn, void *instance) { GEnumData ed; ed.EnumFn = KeyFn; ed.instance = instance; TableMap(server->keyvals, KeyMapF, &ed); } /*********** * UTILITY FUNCTIONS **********/ static char safetolower(char ch) { return (char)((ch <= 0x7F ? tolower(ch) : ch)); } /* NonTermHash * ---------- * The hash code is computed using a method called "linear congruence." * This hash function has the additional feature of being case-insensitive, */ #define MULTIPLIER -1664117991 static int _cdecl KeyValHash(const void *elem, int numbuckets) { unsigned int i; unsigned long hashcode = 0; char *s = ((GKeyValuePair *)elem)->key; for (i = 0; i < strlen(s); i++) hashcode = hashcode * MULTIPLIER + safetolower(s[i]); return (hashcode % numbuckets); } /* keyval * Compares two gkeyvaluepair (case insensative) */ static int _cdecl KeyValCompare(const void *entry1, const void *entry2) { return CaseInsensitiveCompare(&((GKeyValuePair *)entry1)->key, &((GKeyValuePair *)entry2)->key); } /* CaseInsensitiveCompare * ---------------------- * Comparison function passed to qsort to sort an array of * strings in alphabetical order. It uses strcasecmp which is * identical to strcmp, except that it doesn't consider case of the * characters when comparing them, thus it sorts case-insensitively. */ static int _cdecl CaseInsensitiveCompare(const void *entry1, const void *entry2) { return strcasecmp(*(char **)entry1,*(char **)entry2); } /* KeyValFree * Frees the memory INSIDE a GKeyValuePair structure */ static void _cdecl KeyValFree(void *elem) { free(((GKeyValuePair *)elem)->key); free(((GKeyValuePair *)elem)->value); }