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

999 lines
20 KiB
C

/*
GameSpy Peer SDK
Dan "Mr. Pants" Schoenblum
dan@gamespy.com
Copyright 1999-2007 GameSpy Industries, Inc
devsupport@gamespy.com
*/
/*************
** INCLUDES **
*************/
#include <stdlib.h>
#include "peerKeys.h"
#include "peerCallbacks.h"
#include "peerRooms.h"
#include "peerGlobalCallbacks.h"
#include "peerMangle.h"
/************
** DEFINES **
************/
#define PI_WATCH_KEYS_NUM_BUCKETS 16
#define PI_WATCH_CACHE_NUM_BUCKETS 128
/**********
** TYPES **
**********/
// Stored in the *WatckKey tables.
//////////////////////////////////
typedef struct piWatchKey
{
char * key;
} piWatchKey;
// Stored in the *WatchCache tables.
////////////////////////////////////
typedef struct piCacheKey
{
char * nick;
char * key;
char * value;
#ifdef GSI_UNICODE
unsigned short * value_W;
#endif
} piCacheKey;
typedef struct piClearWatchKeyCacheData
{
const char * key;
HashTable watchCache;
} piClearWatchKeyCacheData;
typedef struct piRemoveExistingKeysData
{
int num;
const char ** keys;
HashTable watchKeys;
} piRemoveExistingKeysData;
typedef struct piPlayerChangedNickMapData
{
const char * oldNick;
const char * newNick;
} piPlayerChangedNickMapData;
typedef struct piSetupKeysMapData
{
int next;
char ** keys;
} piSetupKeysMapData;
typedef struct piCleanseRoomCacheMapData
{
PEER peer;
RoomType roomType;
} piCleanseRoomCacheMapData;
/**************
** FUNCTIONS **
**************/
#ifndef GSI_UNICODE // NOT GSI_UNICODE, these have to coincide with the chatsdk build
#define piGetRoomKeysCallback piGetRoomKeysCallbackA
#define piGetGlobalKeysCallback piGetGlobalKeysCallbackA
#else
#define piGetRoomKeysCallback piGetRoomKeysCallbackW
#define piGetGlobalKeysCallback piGetGlobalKeysCallbackW
#endif
static int WatchKeysHash
(
const void * elem,
int numBuckets
)
{
piWatchKey * key = (piWatchKey *)elem;
int c;
const char * str;
unsigned int hash;
assert(key->key && key->key[0]);
// Get the hash.
////////////////
str = key->key;
hash = 0;
while((c = *str++) != '\0')
hash += (unsigned int)tolower(c);
hash %= (unsigned int)numBuckets;
return (int)hash;
}
static int GS_STATIC_CALLBACK WatchKeysCompare
(
const void * elem1,
const void * elem2
)
{
piWatchKey * key1 = (piWatchKey *)elem1;
piWatchKey * key2 = (piWatchKey *)elem2;
assert(key1->key && key1->key[0] && key2->key && key2->key[0]);
return strcasecmp(key1->key, key2->key);
}
static void WatchKeysFree
(
void * elem
)
{
piWatchKey * key = (piWatchKey *)elem;
gsifree(key->key);
}
static int WatchCacheHash
(
const void * elem,
int numBuckets
)
{
piCacheKey * key = (piCacheKey *)elem;
int c;
const char * str;
unsigned int hash;
assert(key);
assert(key->nick[0]);
// Get the hash.
////////////////
str = key->key;
hash = 0;
while((c = *str++) != '\0')
hash += (unsigned int)tolower(c);
hash %= (unsigned int)numBuckets;
return (int)hash;
}
static int GS_STATIC_CALLBACK WatchCacheCompare
(
const void * elem1,
const void * elem2
)
{
int rcode;
piCacheKey * key1 = (piCacheKey *)elem1;
piCacheKey * key2 = (piCacheKey *)elem2;
rcode = strcasecmp(key1->nick, key2->nick);
if(rcode)
return rcode;
return strcasecmp(key1->key, key2->key);
}
static void WatchCacheFree
(
void * elem
)
{
piCacheKey * key = (piCacheKey *)elem;
gsifree(key->nick);
gsifree(key->key);
gsifree(key->value);
#ifdef GSI_UNICODE
gsifree(key->value_W);
#endif
}
PEERBool piKeysInit
(
PEER peer
)
{
int roomType;
PEER_CONNECTION;
memset(connection->globalWatchKeys, 0, sizeof(HashTable) * NumRooms);
memset(connection->roomWatchKeys, 0, sizeof(HashTable) * NumRooms);
memset(connection->roomWatchCache, 0, sizeof(HashTable) * NumRooms);
// Create all the tables.
/////////////////////////
connection->globalWatchCache = TableNew(sizeof(piCacheKey), PI_WATCH_CACHE_NUM_BUCKETS,
WatchCacheHash, WatchCacheCompare, WatchCacheFree);
if(!connection->globalWatchCache)
return PEERFalse;
for(roomType = 0 ; roomType < NumRooms ; roomType++)
{
connection->globalWatchKeys[roomType] = TableNew(sizeof(piWatchKey), PI_WATCH_KEYS_NUM_BUCKETS,
WatchKeysHash, WatchKeysCompare, WatchKeysFree);
connection->roomWatchKeys[roomType] = TableNew(sizeof(piWatchKey), PI_WATCH_KEYS_NUM_BUCKETS,
WatchKeysHash, WatchKeysCompare, WatchKeysFree);
connection->roomWatchCache[roomType] = TableNew(sizeof(piCacheKey), PI_WATCH_CACHE_NUM_BUCKETS,
WatchCacheHash, WatchCacheCompare, WatchCacheFree);
if(!connection->globalWatchKeys[roomType] || !connection->roomWatchKeys[roomType] ||
!connection->roomWatchCache[roomType])
{
return PEERFalse;
}
}
return PEERTrue;
}
void piKeysCleanup
(
PEER peer
)
{
int roomType;
PEER_CONNECTION;
if(connection->globalWatchCache)
TableFree(connection->globalWatchCache);
for(roomType = 0 ; roomType < NumRooms ; roomType++)
{
if(connection->globalWatchKeys[roomType])
TableFree(connection->globalWatchKeys[roomType]);
if(connection->roomWatchKeys[roomType])
TableFree(connection->roomWatchKeys[roomType]);
if(connection->roomWatchCache[roomType])
TableFree(connection->roomWatchCache[roomType]);
}
}
static const char * piGetWatchKeyA
(
const char * nick,
const char * key,
HashTable watchCache
)
{
piCacheKey keyTemp;
piCacheKey * cacheKey;
keyTemp.nick = (char *)nick;
keyTemp.key = (char *)key;
cacheKey = (piCacheKey *)TableLookup(watchCache, &keyTemp);
if(!cacheKey)
return NULL;
if(cacheKey->value)
return cacheKey->value;
return "";
}
static const unsigned short * piGetWatchKeyW
(
const unsigned short * nick,
const unsigned short * key,
HashTable watchCache
)
{
#ifndef GSI_UNICODE
GSI_UNUSED(nick);
GSI_UNUSED(key);
GSI_UNUSED(watchCache);
return NULL; // can't use this function unless in GSI_UNICODE mode
#else
piCacheKey keyTemp;
piCacheKey * cacheKey;
char* nick_A = UCS2ToUTF8StringAlloc(nick);
char* key_A = UCS2ToUTF8StringAlloc(key);
keyTemp.nick = (char *)nick_A;
keyTemp.key = (char *)key_A;
cacheKey = (piCacheKey *)TableLookup(watchCache, &keyTemp);
gsifree(nick_A);
gsifree(key_A);
if (!cacheKey)
return NULL;
if(cacheKey->value_W)
return cacheKey->value_W;
return L"";
#endif
}
const char * piGetGlobalWatchKeyA
(
PEER peer,
const char * nick,
const char * key
)
{
PEER_CONNECTION;
return piGetWatchKeyA(nick, key, connection->globalWatchCache);
}
const unsigned short * piGetGlobalWatchKeyW
(
PEER peer,
const unsigned short * nick,
const unsigned short * key
)
{
PEER_CONNECTION;
return piGetWatchKeyW(nick, key, connection->globalWatchCache);
}
const char * piGetRoomWatchKeyA
(
PEER peer,
RoomType roomType,
const char * nick,
const char * key
)
{
PEER_CONNECTION;
return piGetWatchKeyA(nick, key, connection->roomWatchCache[roomType]);
}
const unsigned short * piGetRoomWatchKeyW
(
PEER peer,
RoomType roomType,
const unsigned short * nick,
const unsigned short * key
)
{
PEER_CONNECTION;
return piGetWatchKeyW(nick, key, connection->roomWatchCache[roomType]);
}
static void piCleanseGlobalCacheMap
(
void * elem,
void * clientData
)
{
piPlayer * player;
int roomType;
piCacheKey * cacheKey = (piCacheKey *)elem;
PEER peer = (PEER)clientData;
piConnection * connection = (piConnection *)peer;
piWatchKey watchKeyTemp;
watchKeyTemp.key = cacheKey->key;
player = piGetPlayer(peer, cacheKey->nick);
if(player)
{
for(roomType = 0 ; roomType < NumRooms ; roomType++)
{
if(player->inRoom[roomType])
{
if(TableLookup(connection->globalWatchKeys[roomType], &watchKeyTemp))
return;
}
}
}
TableRemove(connection->globalWatchCache, cacheKey);
}
static void piCleanseRoomCacheMap
(
void * elem,
void * clientData
)
{
piPlayer * player;
piCacheKey * cacheKey = (piCacheKey *)elem;
piCleanseRoomCacheMapData * data = (piCleanseRoomCacheMapData *)clientData;
piConnection * connection = (piConnection *)data->peer;
piWatchKey watchKeyTemp;
watchKeyTemp.key = cacheKey->key;
player = piGetPlayer(data->peer, cacheKey->nick);
if(player && player->inRoom[data->roomType])
{
if(TableLookup(connection->roomWatchKeys[data->roomType], &watchKeyTemp))
return;
}
TableRemove(connection->globalWatchCache, cacheKey);
}
void piKeyCacheCleanse
(
PEER peer
)
{
int roomType;
piCleanseRoomCacheMapData data;
PEER_CONNECTION;
TableMapSafe(connection->globalWatchCache, piCleanseGlobalCacheMap, peer);
data.peer = peer;
for(roomType = 0 ; roomType < NumRooms ; roomType++)
{
if(IN_ROOM || ENTERING_ROOM)
{
data.roomType = (RoomType)roomType;
TableMapSafe(connection->roomWatchCache[roomType], piCleanseRoomCacheMap, &data);
}
else
{
TableClear(connection->roomWatchCache[roomType]);
}
}
}
static void piGetGlobalKeysCallbackA
(
CHAT chat,
CHATBool success,
const char * user,
int num,
const char ** keys,
const char ** values,
void * param
)
{
PEER peer = (PEER)param;
if(success && user)
{
int i;
for(i = 0 ; i < num ; i++)
piGlobalKeyChanged(peer, user, keys[i], values[i]);
}
GSI_UNUSED(chat);
}
#ifdef GSI_UNICODE
static void piGetGlobalKeysCallbackW
(
CHAT chat,
CHATBool success,
const unsigned short * user,
int num,
const unsigned short ** keys,
const unsigned short ** values,
void * param
)
{
char* user_A = UCS2ToUTF8StringAlloc(user);
char** keys_A = UCS2ToUTF8StringArrayAlloc(keys, num);
char** values_A = UCS2ToUTF8StringArrayAlloc(values, num);
int i;
piGetGlobalKeysCallbackA(chat, success, user_A, num, (const char**)keys_A, (const char**)values_A, param);
gsifree(user_A);
for (i=0; i<num; i++)
{
if (keys_A != NULL) gsifree(keys_A[i]);
if (values_A != NULL) gsifree(values_A[i]);
}
gsifree(keys_A);
gsifree(values_A);
}
#endif
static void piGetRoomKeysCallbackA
(
CHAT chat,
CHATBool success,
const char * channel,
const char * user,
int num,
const char ** keys,
const char ** values,
void * param
)
{
PEER peer = (PEER)param;
if(!user && success)
{
//
// If <user> is NULL, then this has been called by the
// GETCKEY list-end handler (ciRplEndGetCKeyHandler),
// and the PlayerInfo callback should be called one final
// time with NULL as the user.
//
// Alter the username and proceed so that all of the
// subsequent handlers and callbacks have a real name to
// work with. Note "(END)" is illegal as an IRC nickname
// (09mar01/bgw).
//
int i;
RoomType roomType;
static const char * szEndName = "(END)";
if(!piRoomToType(peer, channel, &roomType))
return;
for(i = 0 ; i < num ; i++)
piRoomKeyChanged(peer, roomType, szEndName, keys[i], NULL);
return;
}
if(success /*&& user*/)
{
int i;
RoomType roomType;
if(!piRoomToType(peer, channel, &roomType))
return;
for(i = 0 ; i < num ; i++)
piRoomKeyChanged(peer, roomType, user, keys[i], values[i]);
}
GSI_UNUSED(chat);
}
#ifdef GSI_UNICODE
static void piGetRoomKeysCallbackW
(
CHAT chat,
CHATBool success,
const unsigned short * channel,
const unsigned short * user,
int num,
const unsigned short ** keys,
const unsigned short ** values,
void * param
)
{
char* channel_A = UCS2ToUTF8StringAlloc(channel);
char* user_A = UCS2ToUTF8StringAlloc(user);
char** keys_A = UCS2ToUTF8StringArrayAlloc(keys, num);
char** values_A = UCS2ToUTF8StringArrayAlloc(values, num);
int i;
piGetRoomKeysCallbackA(chat, success, channel_A, user_A, num, (const char**)keys_A, (const char**)values_A, param);
gsifree(channel_A);
gsifree(user_A);
for(i=0; i<num; i++)
{
if (keys_A != NULL) gsifree(keys_A[i]);
if (values_A != NULL) gsifree(values_A[i]);
}
gsifree(keys_A);
gsifree(values_A);
}
#endif
static void piRemoveExistingKeysMap
(
void * elem,
void * clientData
)
{
int i;
piWatchKey * key = (piWatchKey *)elem;
piRemoveExistingKeysData * data = (piRemoveExistingKeysData *)clientData;
for(i = 0 ; i < data->num ; i++)
if(strcasecmp(key->key, data->keys[i]) == 0)
return;
TableRemove(data->watchKeys, key);
}
void piSetGlobalWatchKeys
(
PEER peer,
RoomType roomType,
int num,
const char ** keys,
PEERBool addKeys
)
{
piWatchKey watchKey;
int i;
PEER_CONNECTION;
ASSERT_ROOMTYPE(roomType);
assert(num >= 0);
// If no keys.
//////////////
if(num == 0)
{
// Nothing to add.
//////////////////
if(addKeys)
return;
// Clear the list and cache.
////////////////////////////
TableClear(connection->globalWatchKeys[roomType]);
piKeyCacheCleanse(peer);
return;
}
assert(keys);
if(!addKeys)
{
piRemoveExistingKeysData data;
data.num = num;
data.keys = keys;
data.watchKeys = connection->globalWatchKeys[roomType];
TableMapSafe(connection->globalWatchKeys[roomType], piRemoveExistingKeysMap, &data);
}
for(i = 0 ; i < num ; i++)
{
watchKey.key = goastrdup(keys[i]);
TableEnter(connection->globalWatchKeys[roomType], &watchKey);
}
// Update them all.
///////////////////
if(ENTERING_ROOM || IN_ROOM)
chatGetGlobalKeysA(connection->chat, ROOM, num, keys, piGetGlobalKeysCallback, peer, CHATFalse);
}
void piSetRoomWatchKeys
(
PEER peer,
RoomType roomType,
int num,
const char ** keys,
PEERBool addKeys
)
{
piWatchKey watchKey;
int i;
PEER_CONNECTION;
ASSERT_ROOMTYPE(roomType);
assert(num >= 0);
// If no keys.
//////////////
if(num == 0)
{
// Nothing to add.
//////////////////
if(addKeys)
return;
// Clear the list and cache.
////////////////////////////
TableClear(connection->roomWatchKeys[roomType]);
TableClear(connection->roomWatchCache[roomType]);
return;
}
assert(keys);
if(!addKeys)
{
piRemoveExistingKeysData data;
data.num = num;
data.keys = keys;
data.watchKeys = connection->roomWatchKeys[roomType];
TableMapSafe(connection->roomWatchKeys[roomType], piRemoveExistingKeysMap, &data);
}
for(i = 0 ; i < num ; i++)
{
watchKey.key = goastrdup(keys[i]);
TableEnter(connection->roomWatchKeys[roomType], &watchKey);
}
// Update them all.
///////////////////
if(ENTERING_ROOM || IN_ROOM)
chatGetChannelKeysA(connection->chat, ROOM, "*", num, keys, piGetRoomKeysCallback, peer, CHATFalse);
}
static PEERBool piKeyChanged
(
PEER peer,
const char * nick,
const char * key,
const char * value,
HashTable watchKeys,
HashTable watchCache,
PEERBool inRoom,
RoomType roomType
)
{
piWatchKey watchKeyTemp;
piCacheKey cacheKey;
assert(key && key[0]);
// We don't track anything for rooms.
/////////////////////////////////////
if(!nick || !nick[0])
return PEERTrue;
// No NULL values.
//////////////////
if(!value)
value = "";
// Is this a username (IP and Profile ID)?
//////////////////////////////////////////
if(strcasecmp(key, "username") == 0)
{
piPlayer * player;
if(strcmp(nick, "(END)") == 0)
{
//
// If <nick> is "(END)", this is the final message from
// a GETCKEYS list. Call straight into the PlayerInfo
// callback with zeroed arguments (09mar01/bgw).
//
assert(inRoom);
piAddPlayerInfoCallback(peer, roomType, NULL, 0, 0);
return PEERFalse;
}
// Get the player.
//////////////////
player = piGetPlayer(peer, nick);
if(player && !player->gotIPAndProfileID)
{
int profileID;
unsigned int IP;
// Get the info.
////////////////
if(piDemangleUser(value, &IP, &profileID))
{
// Cache the info.
//////////////////
piSetPlayerIPAndProfileID(peer, nick, IP, profileID);
}
}
// If this was in a room, call the callback.
////////////////////////////////////////////
if(inRoom)
{
if(player && player->gotIPAndProfileID)
piAddPlayerInfoCallback(peer, roomType, nick, player->IP, player->profileID);
else
piAddPlayerInfoCallback(peer, roomType, nick, 0, 0);
}
}
// Is this flags?
/////////////////
if(inRoom && strcasecmp(key, "b_flags") == 0)
piSetPlayerRoomFlags(peer, nick, roomType, value);
// Check if this is a watch key.
////////////////////////////////
watchKeyTemp.key = (char *)key;
if(!TableLookup(watchKeys, &watchKeyTemp))
{
// Is it a broadcast key?
/////////////////////////
if(inRoom && (strncmp(key, "b_", 2) == 0))
return PEERTrue;
return PEERFalse;
}
// Cache it.
////////////
memset(&cacheKey, 0, sizeof(piCacheKey));
cacheKey.nick = goastrdup(nick);
cacheKey.key = goastrdup(key);
cacheKey.value = goastrdup(value);
#ifdef GSI_UNICODE
cacheKey.value_W = UTF8ToUCS2StringAlloc(cacheKey.value);
#endif
TableEnter(watchCache, &cacheKey);
return PEERTrue;
}
void piGlobalKeyChanged
(
PEER peer,
const char * nick,
const char * key,
const char * value
)
{
PEER_CONNECTION;
if(piKeyChanged(peer, nick, key, value, connection->globalWatchKeys[TitleRoom], connection->globalWatchCache, PEERFalse, (RoomType)0) ||
piKeyChanged(peer, nick, key, value, connection->globalWatchKeys[GroupRoom], connection->globalWatchCache, PEERFalse, (RoomType)0) ||
piKeyChanged(peer, nick, key, value, connection->globalWatchKeys[StagingRoom], connection->globalWatchCache, PEERFalse, (RoomType)0))
piAddGlobalKeyChangedCallback(peer, nick, key, value);
}
void piRoomKeyChanged
(
PEER peer,
RoomType roomType,
const char * nick,
const char * key,
const char * value
)
{
PEER_CONNECTION;
if(piKeyChanged(peer, nick, key, value, connection->roomWatchKeys[roomType], connection->roomWatchCache[roomType], PEERTrue, roomType))
piAddRoomKeyChangedCallback(peer, roomType, nick, key, value);
}
static void piPlayerChangedNickMap
(
void * elem,
void * clientData
)
{
piCacheKey * key = (piCacheKey *)elem;
piPlayerChangedNickMapData * data = (piPlayerChangedNickMapData *)clientData;
if(strcasecmp(key->nick, data->oldNick) == 0)
{
gsifree(key->nick);
key->nick = goastrdup(data->newNick);
}
}
void piKeyCachePlayerChangedNick
(
PEER peer,
const char * oldNick,
const char * newNick
)
{
piPlayerChangedNickMapData data;
PEER_CONNECTION;
data.oldNick = oldNick;
data.newNick = newNick;
TableMap(connection->globalWatchCache, piPlayerChangedNickMap, &data);
TableMap(connection->roomWatchCache[0], piPlayerChangedNickMap, &data);
TableMap(connection->roomWatchCache[1], piPlayerChangedNickMap, &data);
TableMap(connection->roomWatchCache[2], piPlayerChangedNickMap, &data);
}
static void piSetupKeysMap
(
void * elem,
void * clientData
)
{
piWatchKey * key = (piWatchKey *)elem;
piSetupKeysMapData * data = (piSetupKeysMapData *)clientData;
// Add the key to the list.
///////////////////////////
data->keys[data->next++] = key->key;
}
static void piKeyCacheRefresh
(
PEER peer,
RoomType roomType,
const char * nick
)
{
int num;
piSetupKeysMapData data;
PEERBool getIP;
PEERBool getFlags;
piWatchKey watchKey;
PEER_CONNECTION;
assert(IN_ROOM || ENTERING_ROOM);
if(!IN_ROOM && !ENTERING_ROOM)
return;
// Get the IP if we're pinging this room.
/////////////////////////////////////////
if(!nick)
getIP = (connection->pingRoom[roomType] || connection->alwaysRequestPlayerInfo)?PEERTrue:PEERFalse;
else
getIP = PEERFalse;
// Check if we're already getting the IP.
/////////////////////////////////////////
if(getIP)
{
watchKey.key = "username";
getIP = (PEERBool)(!TableLookup(connection->roomWatchKeys[roomType], &watchKey));
}
// Don't get flags on a player - he just joined, and will set them.
///////////////////////////////////////////////////////////////////
getFlags = (PEERBool)(nick == NULL);
// Check for a b_flags watch key.
/////////////////////////////////
if(getFlags)
{
watchKey.key = "b_flags";
getFlags = (PEERBool)(!(TableLookup(connection->globalWatchKeys[roomType], &watchKey) ||
TableLookup(connection->roomWatchKeys[roomType], &watchKey)));
}
data.next = 0;
num = TableCount(connection->globalWatchKeys[roomType]);
if(num)
{
data.keys = (char **)gsimalloc(sizeof(char *) * num);
if(!data.keys)
return;
TableMap(connection->globalWatchKeys[roomType], piSetupKeysMap, &data);
assert(data.next == num);
chatGetGlobalKeysA(connection->chat, nick?nick:ROOM, num, (const char **)data.keys, piGetGlobalKeysCallback, peer, CHATFalse);
gsifree(data.keys);
}
if(!nick)
{
data.next = 0;
num = TableCount(connection->roomWatchKeys[roomType]);
if(getIP)
num++;
if(getFlags)
num++;
if(num)
{
data.keys = (char **)gsimalloc(sizeof(char *) * num);
if(!data.keys)
return;
TableMap(connection->roomWatchKeys[roomType], piSetupKeysMap, &data);
if(getIP)
data.keys[data.next++] = "username";
if(getFlags)
data.keys[data.next++] = "b_flags";
assert(data.next == num);
chatGetChannelKeysA(connection->chat, ROOM, nick?nick:"*", num, (const char **)data.keys, piGetRoomKeysCallback, peer, CHATFalse);
gsifree(data.keys);
}
}
}
void piKeyCacheRefreshPlayer
(
PEER peer,
RoomType roomType,
const char * nick
)
{
piKeyCacheRefresh(peer, roomType, nick);
}
void piKeyCacheRefreshRoom
(
PEER peer,
RoomType roomType
)
{
piKeyCacheRefresh(peer, roomType, NULL);
}