Initial full mirror of c:\VWE (source + assets + toolchain + outputs) via Git LFS

Complete disaster-recovery snapshot: engine/game source, game data assets,
VC6 toolchain + DX SDKs, build outputs, deployed game, and _UNUSED archive.
Large binaries in Git LFS; text preserved byte-for-byte (core.autocrlf=false,
no eol attributes). See RECOVERY.md for the one-clone rebuild procedure.
This commit is contained in:
Cyd
2026-06-24 21:28:16 -05:00
commit 2b8ca921cb
66341 changed files with 7923174 additions and 0 deletions
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,192 @@
#include "AdeptHeaders.hpp"
#include "GameSpy.h"
#include "buildnum/buildnum.h"
#include <gameOS/GUNGameList.h>
#include <winsock.h>
#include <assert.h>
#include "goaceng.h"
#define GS_STRING_SIZE 80
//========================================================================
//========================================================================
class _GameSpyClient : public GameSpyClient
{
friend class GameSpyClient;
public:
virtual bool __stdcall Release(void);
virtual GSS __stdcall GetStatus(void);
virtual bool __stdcall Synchronize(void);
public:
virtual int __stdcall GetNumberOfGames(void);
virtual const char *__stdcall GetServerString(int index, const char *szKey);
virtual const char *__stdcall GetServerAddress(int index);
virtual const char *__stdcall GetPlayerString(int index, int playerindex, const char *szKey);
virtual bool __stdcall BeginQuerying(void);
virtual bool __stdcall EndQuerying(void);
private:
char m_szMasterServer[256];
char m_szSecretKey[GS_STRING_SIZE];
int m_iServerListPort;
GSS m_status;
GServerList m_sl;
};
//========================================================================
//========================================================================
GameSpyClient * __stdcall GameSpyClient::Create(char *szMasterServer)
{
_GameSpyClient *p = new _GameSpyClient;
if (p)
{
if (szMasterServer)
strcpy(p->m_szMasterServer, szMasterServer);
else
strcpy(p->m_szMasterServer, GetMasterAddress());
p->m_status = GSS_DISCONNECTED;
p->m_szSecretKey[0] = 0;
p->m_sl = NULL;
p->m_iServerListPort = GetServerListPort();
}
return p;
}
//========================================================================
//========================================================================
bool __stdcall _GameSpyClient::Release(void)
{
EndQuerying();
delete this;
return true;
}
//========================================================================
//========================================================================
GameSpy::GSS __stdcall _GameSpyClient::GetStatus(void)
{
__asm mov ds:[0], 0;
return m_status;
}
//========================================================================
//========================================================================
bool __stdcall _GameSpyClient::Synchronize(void)
{
if (m_sl)
{
if (!IsServerListIdle(m_sl))
ServerListThink(m_sl);
else
return false;
}
return true;
}
//========================================================================
//========================================================================
int __stdcall _GameSpyClient::GetNumberOfGames(void)
{
if (m_sl == NULL)
return 0;
return ServerListCount(m_sl);
}
//========================================================================
//========================================================================
const char * __stdcall _GameSpyClient::GetServerString(int index, const char *szKey)
{
GServer server;
server = ServerListGetServer(m_sl, index);
if (server)
{
return ServerGetStringValue(server, (char *)szKey, (char *)"");
}
return NULL;
}
//========================================================================
//========================================================================
const char * __stdcall _GameSpyClient::GetServerAddress(int index)
{
GServer server;
server = ServerListGetServer(m_sl, index);
if (server)
{
return ServerGetAddress(server);
}
return NULL;
}
//========================================================================
//========================================================================
const char *__stdcall _GameSpyClient::GetPlayerString(int index, int playerindex, const char *szKey)
{
GServer server;
server = ServerListGetServer(m_sl, index);
if (server)
{
return ServerGetPlayerStringValue(server, playerindex, (char *)szKey, (char *)"");
}
return NULL;
}
//========================================================================
//========================================================================
static void ListCallBack1(GServerList serverlist, int msg, void *instance, void *param1, void *param2)
{
}
//========================================================================
//========================================================================
bool __stdcall _GameSpyClient::BeginQuerying(void)
{
EndQuerying();
strncpy(m_szSecretKey, GetSecretKey(), sizeof(m_szSecretKey));
m_sl = ServerListNew(GAMESPY_MASTER_ENGINENAME, GAMESPY_MASTER_ENGINENAME,m_szSecretKey,10,ListCallBack1,GCALLBACK_FUNCTION,NULL);
if (m_sl)
{
if (ServerListUpdate(m_sl, true, m_szMasterServer, m_iServerListPort) != 0)
{
m_status = GSS_ERR_CANNOTFINDMASTER;
ServerListFree(m_sl);
m_sl=NULL;
}
}
return (m_sl != NULL);
}
//========================================================================
//========================================================================
bool __stdcall _GameSpyClient::EndQuerying(void)
{
if (m_sl)
{
ServerListFree(m_sl);
m_sl = false;
}
return true;
}
@@ -0,0 +1,177 @@
#pragma once
#ifdef _DEBUG
#define USE_NCAA
#endif
#define GAMESPY_MASTER_INTERNET_HEARTBEAT_PORT 27900
#define GAMESPY_MASTER_INTERNET_QUERY_PORT 27950
#define GAMESPY_MASTER_INTERNET_SERVERLIST_PORT 28900
#define GAMESPY_MASTER_INTERNET_ADDR "master.gamespy.com"
#ifdef LAB_ONLY
#define GAMESPY_MASTER_LAN_HEARTBEAT_PORT 27900
#define GAMESPY_MASTER_LAN_QUERY_PORT 27950
#define GAMESPY_MASTER_LAN_SERVERLIST_PORT 27900
#define GAMESPY_MASTER_LAN_ADDR "ncaa"
#else
#define GAMESPY_MASTER_LAN_HEARTBEAT_PORT GAMESPY_MASTER_INTERNET_HEARTBEAT_PORT
#define GAMESPY_MASTER_LAN_QUERY_PORT GAMESPY_MASTER_INTERNET_QUERY_PORT
#define GAMESPY_MASTER_LAN_SERVERLIST_PORT GAMESPY_MASTER_INTERNET_SERVERLIST_PORT
#define GAMESPY_MASTER_LAN_ADDR GAMESPY_MASTER_INTERNET_ADDR
#endif
#ifdef USE_NCAA
#define GAMESPY_MASTER_HEARTBEAT_PORT GAMESPY_MASTER_LAN_HEARTBEAT_PORT
#define GAMESPY_MASTER_QUERY_PORT GAMESPY_MASTER_LAN_QUERY_PORT
#define GAMESPY_MASTER_SERVERLIST_PORT GAMESPY_MASTER_LAN_SERVERLIST_PORT
#define GAMESPY_MASTER_ADDR GAMESPY_MASTER_LAN_ADDR
#else
#define GAMESPY_MASTER_HEARTBEAT_PORT GAMESPY_MASTER_INTERNET_HEARTBEAT_PORT
#define GAMESPY_MASTER_QUERY_PORT GAMESPY_MASTER_INTERNET_QUERY_PORT
#define GAMESPY_MASTER_SERVERLIST_PORT GAMESPY_MASTER_INTERNET_SERVERLIST_PORT
#define GAMESPY_MASTER_ADDR GAMESPY_MASTER_INTERNET_ADDR
#endif
#define GAMESPY_MASTER_ENGINENAME "mech4"
#define GAMESPY_MASTER_SERVER_KEY "GameSpy Master Server"
#define GAMESPY_MASTER_HEARTBEAT_PORT_KEY "GameSpy Heartbeat Port"
#define GAMESPY_MASTER_QUERY_PORT_KEY "GameSpy Query Port"
#define GAMESPY_MASTER_SERVERLIST_PORT_KEY "GameSpy ServerList Port"
#define FixSZString(s) ((s)[sizeof(s)-1]) = 0
#define NEW_GAMESPY
#ifndef NEW_GAMESPY
namespace GameSpyOld
{
extern char *GetKeyName(void);
extern bool BeginAdvertisingOnGameSpy(const char * GameName, const char *PlayerName, int MaxPlayers, const char *Password, const GUID &guidInstance, bool Secure, DWORD dwFlags);
extern void EndAdvertisingOnGameSpy(void);
extern bool ProcessGameSpy(void);
extern bool BeginQueryingGameSpy(void);
extern void EndQueryingGameSpy(void);
extern bool Querying(void);
extern char *GetServerAddress(int index);
extern char *GetServerString(int index, const char *szKey, const char *szDefault="");
extern char *GetPlayerString(int index, int playerindex, const char *szKey, const char *szDefault="");
extern int GetNumberOfGames(void);
extern bool SynchronizeServerList(void);
extern void SetKey(const char *szKey, const char *szValue);
extern bool AddPlayer(int id, const char *szName, const char *szTeam, int reserved);
extern bool RemovePlayer(int id, const char *szName, const char *szTeam, int reserved);
extern void GetGameSpyAddress(char *szGameSpyAddress, int size);
extern int GetHeartbeatPort(void);
extern int GetQueryPort(void);
extern int GetServerListPort(void);
extern int __stdcall GetStatus(void);
extern int __stdcall GetNetStatus(void);
extern int __stdcall GetLastError(void);
extern int status;
extern char *szGameSpyAddress;
};
#endif
//========================================================================
// GameSpy
//
// This is the main class the GameSpyHost and GameSpyClient inhereit from.
// It contains the gereral utility functions that are used by both of
// these, such as GetSecretKey() and GetMasterAddress().
//
//========================================================================
class GameSpy
{
public:
enum GSS
{
GSS_RESOLVING_DNS,
GSS_CONNECTING,
GSS_REGISTERING,
GSS_QUERYING,
GSS_DOWNLOADING_GAMES,
GSS_DOWNLOADING_PLAYERS,
GSS_DISCONNECTED,
GSS_ERR_CANNOTFINDMASTER,
};
virtual bool __stdcall Release(void) = 0;
virtual bool __stdcall Synchronize(void) = 0;
static int __stdcall GetStatus(void);
static int __stdcall GetNetStatus(void);
static int __stdcall GetLastError(void);
protected:
static const char * __stdcall GetSecretKey(void);
static const char * __stdcall GetMasterAddress(void);
static int __stdcall GetHeartbeatPort(void);
static int __stdcall GetServerListPort(void);
static int __stdcall GetQueryPort(void);
};
//========================================================================
// GameSpyHost
//
// This is the class that implements the members necessary to advertise
// a GameSpy server.
//========================================================================
class GameSpyHost : public GameSpy
{
public:
static GameSpyHost * __stdcall Create(char *szMasterServer);
virtual bool __stdcall BeginAdvertising(const char *szGameName, int MaxPlayers, const char *szPassword, const GUID &guidInstance, int reserved) = 0;
virtual bool __stdcall EndAdvertising(void) = 0;
virtual bool __stdcall SetKey(const char *szKey, const char *szVal, DWORD dwPlayer=0) = 0;
virtual bool __stdcall SetFlags(const int flags) = 0;
virtual bool __stdcall AddPlayer(int id, const char *szName, const char *szTeam, int reserved) = 0;
virtual bool __stdcall RemovePlayer(int id, const char *szName, const char *szTeam, int reserved) = 0;
};
//========================================================================
//========================================================================
class GameSpyClient : public GameSpy
{
public:
static GameSpyClient * __stdcall Create(char *szMasterServer);
virtual int __stdcall GetNumberOfGames(void) = 0;
virtual const char *__stdcall GetServerString(int index, const char *szKey) = 0;
virtual const char *__stdcall GetServerAddress(int index) = 0;
virtual const char *__stdcall GetPlayerString(int index, int playerindex, const char *szKey) = 0;
virtual bool __stdcall BeginQuerying(void) = 0;
virtual bool __stdcall EndQuerying(void) = 0;
};
extern bool __stdcall InitGameSpyServerBrowser(char *szMasterServer);
extern bool __stdcall InitGameSpyServerAdvertiser(char *szMasterServer);
extern const char GAMESPY_KEY_PRODUCT_NAME[];
extern const char GAMESPY_KEY_GAME_TYPE[];
extern const char GAMESPY_KEY_NUM_PLAYERS[];
extern const char GAMESPY_KEY_MAX_PLAYERS[];
extern const char GAMESPY_KEY_MAP_TYPE[];
extern const char GAMESPY_KEY_GAME_NAME[];
extern const char GAMESPY_KEY_PRODUCT_VERSION[];
extern const char GAMESPY_KEY_INSTANCE[];
extern const char GAMESPY_PLAYER_KEY_PLAYER[];
extern const char GAMESPY_PLAYER_KEY_CLAN[];
extern const char GAMESPY_KEY_LOCATION[];
extern const char GAMESPY_KEY_HOSTNAME[];
extern const char GAMESPY_KEY_HOSTPORT[];
extern const char GAMESPY_KEY_GAMEMODE[];
extern const char GAMESPY_KEY_TIMELIMIT[];
extern const char GAMESPY_KEY_FRAGLIMIT[];
extern const char GAMESPY_KEY_TEAMPLAY[];
extern const char GAMESPY_KEY_RANKEDSERVER[];
extern const char GAMESPY_KEY_FLAGS[];
extern const char GAMESPY_KEY_PASSWORD_PROTECTED[];
@@ -0,0 +1,949 @@
#include "AdeptHeaders.hpp"
#include "GameOS\Network.hpp"
#include "GameOS\GunGameList.h"
#include "GameSpy.h"
#include <dplay.h>
#include <dplobby.h>
#ifdef NEW_GAMESPY
#define NEW_GAMESPY_HOST
#define NEW_GAMESPY_CLIENT
#endif
#ifndef RELEASE_PTR
#define RELEASE_PTR(p) if (p) { (p)->Release(); (p)=NULL; }
#endif
#ifndef NEW_GAMESPY
static char szGameSpyAddress[256];
char * GameSpyOld::szGameSpyAddress = szGameSpyAddress;
int GameSpyOld::status;
#endif
//========================================================================
//========================================================================
int __stdcall GameSpy::GetStatus( void )
{
return 0;
}
//========================================================================
//========================================================================
int __stdcall GameSpy::GetNetStatus( void )
{
return 0;
}
//========================================================================
//========================================================================
int __stdcall GameSpy::GetLastError( void )
{
gosASSERT(0);
return -1;
}
//========================================================================
//========================================================================
//========================================================================
//========================================================================
const char GAMESPY_KEY_PRODUCT_NAME[] = "prodname";
const char GAMESPY_KEY_GAME_TYPE[] = "gametype";
const char GAMESPY_KEY_NUM_PLAYERS[] = "numplayers";
const char GAMESPY_KEY_MAX_PLAYERS[] = "maxplayers";
const char GAMESPY_KEY_MAP_TYPE[] = "mapname";
const char GAMESPY_KEY_GAME_NAME[] = "gamename";
const char GAMESPY_KEY_PRODUCT_VERSION[] = "gamever";
const char GAMESPY_KEY_INSTANCE[] = "guid";
const char GAMESPY_PLAYER_KEY_PLAYER[] = "player";
const char GAMESPY_PLAYER_KEY_CLAN[] = "team";
const char GAMESPY_KEY_LOCATION[] = "location";
const char GAMESPY_KEY_HOSTNAME[] = "hostname";
const char GAMESPY_KEY_HOSTPORT[] = "hostport";
const char GAMESPY_KEY_GAMEMODE[] = "gamemode";
const char GAMESPY_KEY_TIMELIMIT[] = "timelimit";
const char GAMESPY_KEY_FRAGLIMIT[] = "fraglimit";
const char GAMESPY_KEY_TEAMPLAY[] = "teamplay";
const char GAMESPY_KEY_RANKEDSERVER[] = "rankedserver";
const char GAMESPY_KEY_FLAGS[] = "flags";
const char GAMESPY_KEY_PASSWORD_PROTECTED[] = "passworded";
//========================================================================
//========================================================================
class GameSpyServerBrowser : public ServerBrowser
{
friend bool __stdcall InitGameSpyServerBrowser(char *szMasterAddress);
private:
// general use
virtual bool __stdcall Initialize(void);
virtual bool __stdcall Disconnect(void);
virtual int __stdcall GetStatus(void);
virtual bool __stdcall Synchronize(void);
virtual const char *__stdcall GetDescription(void);
virtual bool __stdcall Release(void);
// for browsers
virtual bool __stdcall PrepareJoinGame(const char * szGameName, void **ppDPlay);
// other
virtual bool __stdcall PrepareRefresh(void);
virtual bool __stdcall Refresh(char *gameID);
virtual bool __stdcall RefreshServerInfo(char *gameID);
virtual bool __stdcall StartUpdate(void);
virtual bool __stdcall CancelActivity(void);
virtual bool __stdcall GetNetStatus(void);
static int __stdcall EnumSessionsCallback( LPCDPSESSIONDESC2 lpSessionDesc, LPDWORD lpdwTimeOut, DWORD dwFlags, void* lpContext );
bool m_bDownLoadingGames;
#ifdef NEW_GAMESPY_CLIENT
GameSpyClient * m_pClient;
#endif
};
//========================================================================
//========================================================================
bool __stdcall InitGameSpyServerBrowser(char *szMasterAddress)
{
#ifndef NEW_GAMESPY
GameSpyOld::status = GS_INIT_BROWSER;
#endif
GameSpyServerBrowser *pGS = new GameSpyServerBrowser();
if (pGS == NULL)
return NULL;
#ifdef NEW_GAMESPY_CLIENT
pGS->m_pClient = GameSpyClient::Create(szMasterAddress);
#endif
pGS->Register();
return true;
}
//========================================================================
//========================================================================
bool __stdcall GameSpyServerBrowser::Release(void)
{
Disconnect();
Unregister();
#ifdef NEW_GAMESPY_CLIENT
if (m_pClient)
{
m_pClient->Release();
m_pClient=NULL;
}
#endif
delete this;
return true;
}
//========================================================================
//========================================================================
bool __stdcall GameSpyServerBrowser::Initialize(void)
{
m_bDownLoadingGames = true;
#ifdef NEW_GAMESPY_CLIENT
if (m_pClient)
{
if (!m_pClient->BeginQuerying())
m_bDelete = true;
}
#else
if (GameSpyOld::BeginQueryingGameSpy())
GameSpyOld::status = GS_CONNECTED;
#endif
return true;
}
//========================================================================
//========================================================================
bool __stdcall GameSpyServerBrowser::Disconnect(void)
{
#ifdef NEW_GAMESPY_CLIENT
if (m_pClient)
{
m_pClient->EndQuerying();
}
#else
GameSpyOld::EndQueryingGameSpy();
GameSpyOld::status = GS_DISCONNECTED;
#endif
return true;
}
//========================================================================
//========================================================================
const char *__stdcall GameSpyServerBrowser::GetDescription(void)
{
return "GameSpy";
}
//========================================================================
//========================================================================
int __stdcall GameSpyServerBrowser::GetStatus(void)
{
return true;
}
//========================================================================
//========================================================================
bool __stdcall GameSpyServerBrowser::Synchronize(void)
{
//#ifndef NEW_GAMESPY_HOST
// GameSpy::ProcessGameSpy();
//#endif
#ifdef NEW_GAMESPY_CLIENT
if (m_pClient)
{
if (m_bDownLoadingGames && !m_pClient->Synchronize())
{
// we're done downloading the server list
m_bDownLoadingGames = false;
}
Browse::GameList * pGameList = GetGameList();
for(int c=0 ; c<m_pClient->GetNumberOfGames() ; c++ )
{
char szGameIdentifier[256];
//char szGUID[50];
strncpy( szGameIdentifier, "GS: ", sizeof(szGameIdentifier));
strncat( szGameIdentifier, m_pClient->GetServerString(c, GAMESPY_KEY_HOSTNAME), sizeof(szGameIdentifier));
strncat( szGameIdentifier, m_pClient->GetServerAddress(c), sizeof(szGameIdentifier));
const char * const pszGameName = m_pClient->GetServerString(c, GAMESPY_KEY_HOSTNAME);
if ((pszGameName==NULL) || (pszGameName[0]==0))
continue;
#ifdef PREPEND_PREFIX_TO_GAME_NAMES
char szGameName[256];
strncpy( szGameName, "GS: ", sizeof(szGameName));
strncat( szGameName, pszGameName, sizeof(szGameName));
pszGameName = szGameName;
#endif
if (pGameList->CreateEntry(szGameIdentifier))
{
//
// get the index of the game type
//
int iGameType = -1;
char szGameType[256];
if (m_pClient->GetServerString(c, GAMESPY_KEY_GAME_TYPE))
iGameType = atoi(m_pClient->GetServerString(c, GAMESPY_KEY_GAME_TYPE));
else
{
// go through until we get a NULL, then subtract one to get the last one
for (iGameType=0 ; Environment.GetSpecialGameData(0, iGameType, 0, NULL) != NULL ; iGameType++);
iGameType--;
}
Environment.GetSpecialGameData(0, iGameType, sizeof(szGameType), szGameType);
//
// the game didn't exist yet, so make it now
//
pGameList->SetField(szGameIdentifier, GAMELIST_KEY_MOD_NAME, "Standard");
pGameList->SetField(szGameIdentifier, GAMELIST_KEY_PRODUCT_NAME, m_pClient->GetServerString(c, GAMESPY_KEY_PRODUCT_NAME));
pGameList->SetField(szGameIdentifier, GAMELIST_KEY_PRODUCT_VERSION, m_pClient->GetServerString(c, GAMESPY_KEY_PRODUCT_VERSION));
pGameList->SetField(szGameIdentifier, GAMELIST_KEY_SERVER_ADDRESS, m_pClient->GetServerAddress(c));
pGameList->SetField(szGameIdentifier, GAMELIST_KEY_SERVER_PORT, m_pClient->GetServerString(c, GAMESPY_KEY_HOSTPORT));
pGameList->SetField(szGameIdentifier, GAMELIST_KEY_SERVER_NAME, pszGameName);
pGameList->SetField(szGameIdentifier, GAMELIST_KEY_NUM_PLAYERS, m_pClient->GetServerString(c, GAMESPY_KEY_NUM_PLAYERS));
pGameList->SetField(szGameIdentifier, GAMELIST_KEY_MAX_PLAYERS, m_pClient->GetServerString(c, GAMESPY_KEY_MAX_PLAYERS));
pGameList->SetField(szGameIdentifier, GAMELIST_KEY_MAP_TYPE, m_pClient->GetServerString(c, GAMESPY_KEY_MAP_TYPE));
pGameList->SetField(szGameIdentifier, GAMELIST_KEY_GAME_TYPE, szGameType);
pGameList->SetField(szGameIdentifier, GAMELIST_KEY_PASSWORD_PROTECTED, m_pClient->GetServerString(c, GAMESPY_KEY_PASSWORD_PROTECTED));
pGameList->SetField(szGameIdentifier, GAMELIST_KEY_INSTANCE, m_pClient->GetServerString(c, GAMESPY_KEY_INSTANCE));
pGameList->SetField(szGameIdentifier, GAMELIST_KEY_BROWSER_HANDLE, GetBrowserHandle());
pGameList->SetField(szGameIdentifier, GAMELIST_KEY_FLAGS, m_pClient->GetServerString(c, GAMESPY_KEY_FLAGS));
pGameList->SetField(szGameIdentifier, GAMELIST_KEY_PROTOCOL, GAMELIST_VALUE_PROTOCOL_GAMESPY);
Browse::PlayerList * pList = 0;
pList = pGameList->GetPlayerList( szGameIdentifier );
if (pList)
{
pList->RemoveAll();
int num = atoi(m_pClient->GetServerString(c, GAMESPY_KEY_NUM_PLAYERS));
for (int i = 0; i < num ; i++)
{
pList->CreateEntry(m_pClient->GetPlayerString(c, i, GAMESPY_PLAYER_KEY_PLAYER));
pList->SetField(m_pClient->GetPlayerString(c, i, GAMESPY_PLAYER_KEY_PLAYER), PLAYERLIST_KEY_PLAYER_NAME, m_pClient->GetPlayerString(c, i, GAMESPY_PLAYER_KEY_PLAYER));
pList->SetField(m_pClient->GetPlayerString(c, i, GAMESPY_PLAYER_KEY_PLAYER), PLAYERLIST_KEY_CLAN_NAME, m_pClient->GetPlayerString(c, i, GAMESPY_PLAYER_KEY_CLAN));
}
}
}
}
}
#else
if (m_bDownLoadingGames && !GameSpyOld::SynchronizeServerList())
{
// we're done downloading the server list
m_bDownLoadingGames = false;
}
Browse::GameList * pGameList = GetGameList();
for(int c=0 ; c<GameSpyOld::GetNumberOfGames() ; c++ )
{
char szGameIdentifier[256];
//char szGUID[50];
strncpy( szGameIdentifier, "GS: ", sizeof(szGameIdentifier));
strncat( szGameIdentifier, GameSpyOld::GetServerString(c, GAMESPY_KEY_GAME_NAME), sizeof(szGameIdentifier));
strncat( szGameIdentifier, GameSpyOld::GetServerAddress(c), sizeof(szGameIdentifier));
char *pszGameName = GameSpyOld::GetServerString(c, GAMESPY_KEY_GAME_NAME);
if ((pszGameName==NULL) || (pszGameName[0]==0))
continue;
#ifdef PREPEND_PREFIX_TO_GAME_NAMES
char szGameName[256];
strncpy( szGameName, "GS: ", sizeof(szGameName));
strncat( szGameName, pszGameName, sizeof(szGameName));
pszGameName = szGameName;
#endif
if (pGameList->CreateEntry(szGameIdentifier))
{
//
// the game didn't exist yet, so make it now
//
pGameList->SetField(szGameIdentifier, GAMELIST_KEY_MOD_NAME, "Standard");
pGameList->SetField(szGameIdentifier, GAMELIST_KEY_PRODUCT_NAME, GameSpyOld::GetServerString(c, GAMESPY_KEY_PRODUCT_NAME));
pGameList->SetField(szGameIdentifier, GAMELIST_KEY_PRODUCT_VERSION, GameSpyOld::GetServerString(c, GAMESPY_KEY_PRODUCT_VERSION));
pGameList->SetField(szGameIdentifier, GAMELIST_KEY_SERVER_ADDRESS, GameSpyOld::GetServerAddress(c));
pGameList->SetField(szGameIdentifier, GAMELIST_KEY_SERVER_PORT, "15");
pGameList->SetField(szGameIdentifier, GAMELIST_KEY_SERVER_NAME, pszGameName);
pGameList->SetField(szGameIdentifier, GAMELIST_KEY_NUM_PLAYERS, GameSpyOld::GetServerString(c, GAMESPY_KEY_NUM_PLAYERS));
pGameList->SetField(szGameIdentifier, GAMELIST_KEY_MAX_PLAYERS, GameSpyOld::GetServerString(c, GAMESPY_KEY_MAX_PLAYERS));
pGameList->SetField(szGameIdentifier, GAMELIST_KEY_MAP_TYPE, GameSpyOld::GetServerString(c, GAMESPY_KEY_MAP_TYPE));
pGameList->SetField(szGameIdentifier, GAMELIST_KEY_GAME_TYPE, GameSpyOld::GetServerString(c, GAMESPY_KEY_GAME_TYPE));
pGameList->SetField(szGameIdentifier, GAMELIST_KEY_PASSWORD_PROTECTED, "No");
pGameList->SetField(szGameIdentifier, GAMELIST_KEY_INSTANCE, GameSpyOld::GetServerString(c, GAMESPY_KEY_INSTANCE));
pGameList->SetField(szGameIdentifier, GAMELIST_KEY_BROWSER_HANDLE, GetBrowserHandle());
pGameList->SetField(szGameIdentifier, GAMELIST_KEY_FLAGS, GameSpyOld::GetServerString(c, GAMESPY_KEY_FLAGS));
pGameList->SetField(szGameIdentifier, GAMELIST_KEY_PROTOCOL, GAMELIST_VALUE_PROTOCOL_GAMESPY);
Browse::PlayerList * pList = 0;
pList = pGameList->GetPlayerList( szGameIdentifier );
if (pList)
{
pList->RemoveAll();
int num = atoi(GameSpyOld::GetServerString(c, GAMESPY_KEY_NUM_PLAYERS));
for (int i = 0; i < num ; i++)
{
pList->CreateEntry(GameSpyOld::GetPlayerString(c, i, GAMESPY_PLAYER_KEY_PLAYER));
pList->SetField(GameSpyOld::GetPlayerString(c, i, GAMESPY_PLAYER_KEY_PLAYER), PLAYERLIST_KEY_PLAYER_NAME, GameSpyOld::GetPlayerString(c, i, GAMESPY_PLAYER_KEY_PLAYER));
}
}
}
else
{
//
// just update what may have changed
//
pGameList->SetField(szGameIdentifier, GAMELIST_KEY_PRODUCT_NAME, GameSpyOld::GetServerString(c, GAMESPY_KEY_PRODUCT_NAME));
pGameList->SetField(szGameIdentifier, GAMELIST_KEY_GAME_TYPE, GameSpyOld::GetServerString(c, GAMESPY_KEY_GAME_TYPE));
pGameList->SetField(szGameIdentifier, GAMELIST_KEY_NUM_PLAYERS, GameSpyOld::GetServerString(c, GAMESPY_KEY_NUM_PLAYERS));
pGameList->SetField(szGameIdentifier, GAMELIST_KEY_MAX_PLAYERS, GameSpyOld::GetServerString(c, GAMESPY_KEY_MAX_PLAYERS));
pGameList->SetField(szGameIdentifier, GAMELIST_KEY_MAP_TYPE, GameSpyOld::GetServerString(c, GAMESPY_KEY_MAP_TYPE));
Browse::PlayerList * pList = 0;
pList = pGameList->GetPlayerList( szGameIdentifier );
if (pList)
{
pList->RemoveAll();
int num = atoi(GameSpyOld::GetServerString(c, GAMESPY_KEY_NUM_PLAYERS));
for (int i = 0; i < num ; i++)
{
pList->CreateEntry(GameSpyOld::GetPlayerString(c, i, GAMESPY_PLAYER_KEY_PLAYER));
pList->SetField(GameSpyOld::GetPlayerString(c, i, GAMESPY_PLAYER_KEY_PLAYER), PLAYERLIST_KEY_PLAYER_NAME, GameSpyOld::GetPlayerString(c, i, GAMESPY_PLAYER_KEY_PLAYER));
}
}
}
}
#endif
return true;
}
//========================================================================
//========================================================================
BOOL PASCAL GameSpyServerBrowser::EnumSessionsCallback( LPCDPSESSIONDESC2 lpSessionDesc, LPDWORD lpdwTimeOut, DWORD dwFlags, void* lpContext )
{
return FALSE;
}
//========================================================================
// PrepareJoinGame
//
// This is called just before open is called on a game. In here, we must
// set the DirectPlay object to be the correct protocol and to correctly
// enumerate the game on the LAN
//
//========================================================================
bool __stdcall GameSpyServerBrowser::PrepareJoinGame(const char * szGameName, void **ppDPlay)
{
IDirectPlay4A * lpDP = NULL;
IDirectPlayLobby3 * lpDPL = NULL;
HRESULT hr;
//
// get the IP address of the game in question
//
const char *szIPAddress;
const char *szGameIdentifier;
szGameIdentifier = GetGameList()->GetIDFromField( GAMELIST_KEY_SERVER_NAME, szGameName );
szIPAddress = GetGameList()->GetDataField(szGameIdentifier, GAMELIST_KEY_SERVER_ADDRESS);
const char *szPort = GetGameList()->GetDataField(szGameIdentifier, GAMELIST_KEY_SERVER_PORT);
int port;
if (szPort)
port = atoi(szPort);
else
port = 0;
//
// create the DirectPlay object
//
if (FAILED(hr = CoCreateInstance( CLSID_DirectPlay, NULL, CLSCTX_ALL, IID_IDirectPlay4A, (VOID**)&lpDP)))
goto fail;
//
// release the original GameOS DirectPlay object
//
if (*(IDirectPlay4A **)ppDPlay)
{
(*(IDirectPlay4A **)ppDPlay)->Release();
*(IDirectPlay4A **)ppDPlay = NULL;
}
//
// copy the DirectPlay object back to the GameOS DirectPlay object
//
*(IDirectPlay4A **)ppDPlay = lpDP;
//
// create a lobby object
//
//
// create a DirectPlayLobby object for creation of an address
//
if (FAILED(hr = CoCreateInstance( CLSID_DirectPlayLobby, NULL, CLSCTX_ALL, IID_IDirectPlayLobby3, (VOID**)&lpDPL)))
goto fail;
//
// create the address needed
//
DPCOMPOUNDADDRESSELEMENT comAdd[3];
DWORD Size;
void* p_address;
comAdd[0].guidDataType = DPAID_ServiceProvider;
comAdd[0].dwDataSize = sizeof(GUID);
comAdd[0].lpData = (void*)&DPSPGUID_TCPIP;
comAdd[1].guidDataType = DPAID_INet; // Create a compund address
comAdd[1].dwDataSize = strlen(szIPAddress) + 1;
comAdd[1].lpData = (void *)szIPAddress;
comAdd[2].guidDataType = DPAID_INetPort;
comAdd[2].dwDataSize = sizeof(port);
comAdd[2].lpData = (void *)&port;
int num_elements;
if (port)
num_elements = 3;
else
num_elements = 2;
//
// create the address
//
lpDPL->CreateCompoundAddress(comAdd, num_elements, NULL, &Size );
p_address = (void*)gos_Malloc(Size);
if (FAILED(hr = lpDPL->CreateCompoundAddress(comAdd, num_elements, p_address, &Size )))
goto fail;
//
// initialize the connection
//
if (FAILED(hr = lpDP->InitializeConnection(p_address, 0)))
goto fail;
//
// enumerate
//
if (true)
{
DPSESSIONDESC2 dpsd;
memset( &dpsd, 0, sizeof(dpsd) );
dpsd.dwSize = sizeof(dpsd);
memcpy( &dpsd.guidApplication, &Environment.NetworkGUID, sizeof(GUID) );
DWORD dwFlags = DPENUMSESSIONS_RETURNSTATUS | DPENUMSESSIONS_ALL | DPENUMSESSIONS_PASSWORDREQUIRED;
if (FAILED(hr = lpDP->EnumSessions(&dpsd, 0, EnumSessionsCallback, 0, dwFlags )))
goto fail;
}
return true;
fail:
RELEASE_PTR(lpDPL);
return false;
}
//========================================================================
//========================================================================
bool __stdcall GameSpyServerBrowser::PrepareRefresh(void)
{
return true;
}
//========================================================================
//========================================================================
bool __stdcall GameSpyServerBrowser::Refresh(char *gameID)
{
return true;
}
//========================================================================
//========================================================================
bool __stdcall GameSpyServerBrowser::RefreshServerInfo(char *gameID)
{
Browse::GameList * gameList = ServerBrowser::GetGameList();
if ( gameList == NULL )
{
return false;
}
// find out the protocol type for the game
const char * pProtocol = gameList->GetDataField( gameID, GAMELIST_KEY_PROTOCOL );
if ( pProtocol == NULL )
{
// no such game or no protocol identified for it.
return false;
}
// see if the protocl matches our type
if ( strcmp( pProtocol, GAMELIST_VALUE_PROTOCOL_GAMESPY ) )
{
// this isn't us
return false;
}
// mdm - change this if we want to spawn a player query for the specific server.
// right now all player data is downloaded with an update so we don't have any
// work to do.
// assume we already have the player data, so set success right away.
gameList->SetField( gameID, GAMELIST_KEY_SERVER_INFO_STATUS, "1" );
return true;
}
//========================================================================
//========================================================================
bool __stdcall GameSpyServerBrowser::StartUpdate(void)
{
m_bDownLoadingGames = true;
#ifdef NEW_GAMESPY_CLIENT
if (m_pClient)
m_pClient->BeginQuerying();
#else
GameSpyOld::BeginQueryingGameSpy();
#endif
return true;
}
//========================================================================
//========================================================================
bool __stdcall GameSpyServerBrowser::CancelActivity(void)
{
m_bDownLoadingGames = false;
#ifdef NEW_GAMESPY_CLIENT
if (m_pClient)
m_pClient->EndQuerying();
#else
GameSpyOld::EndQueryingGameSpy();
#endif
return true;
}
//========================================================================
//========================================================================
bool __stdcall GameSpyServerBrowser::GetNetStatus(void)
{
return m_bDownLoadingGames;
}
//========================================================================
//========================================================================
class GameSpyServerAdvertiser : public ServerAdvertiser
{
friend bool __stdcall InitGameSpyServerAdvertiser(char *szMasterAddress);
private:
// general use
virtual bool __stdcall Initialize(void);
virtual bool __stdcall Disconnect(void);
virtual bool __stdcall Synchronize(void);
virtual bool __stdcall Release(void);
virtual const char * __stdcall GetDescription(void);
// for servers
virtual bool __stdcall CreateGame(const char *szGameName, const char *szPlayerName, int MaxPlayers, const char *szPassword, const GUID &guidInstance, DWORD dwFlags);
virtual bool __stdcall CreatePlayer(DWORD dwItemID, const char *szPlayerName, bool bBot);
virtual bool __stdcall RemovePlayer(DWORD dwItemID, const char *szPlayerName, bool bBot);
virtual bool __stdcall SetItemValue(DWORD dwPlayer, const char * szName , const char * szValue);
virtual bool __stdcall SetFlags(DWORD dwFlags);
static int __stdcall EnumSessionsCallback( LPCDPSESSIONDESC2 lpSessionDesc, LPDWORD lpdwTimeOut, DWORD dwFlags, void* lpContext );
GameSpyHost *m_pHost;
};
//========================================================================
//========================================================================
bool __stdcall InitGameSpyServerAdvertiser(char *szMasterAddress)
{
GameSpyServerAdvertiser *pGS = new GameSpyServerAdvertiser();
if (pGS == NULL)
return NULL;
#ifdef NEW_GAMESPY_HOST
pGS->m_pHost = GameSpyHost::Create(szMasterAddress);
#endif
pGS->Register();
return true;
}
//========================================================================
//========================================================================
bool __stdcall GameSpyServerAdvertiser::Release(void)
{
#ifdef NEW_GAMESPY_HOST
m_pHost->Release();
#endif
Disconnect();
Unregister();
delete this;
return true;
}
//========================================================================
//========================================================================
bool __stdcall GameSpyServerAdvertiser::Initialize(void)
{
return true;
}
//========================================================================
//========================================================================
bool __stdcall GameSpyServerAdvertiser::Disconnect(void)
{
return true;
}
//========================================================================
//========================================================================
const char *__stdcall GameSpyServerAdvertiser::GetDescription(void)
{
return "GameSpy";
}
//========================================================================
//========================================================================
bool __stdcall GameSpyServerAdvertiser::Synchronize(void)
{
#ifdef NEW_GAMESPY_HOST
m_pHost->Synchronize();
#else
GameSpyOld::ProcessGameSpy();
#endif
return true;
}
//========================================================================
//========================================================================
bool __stdcall GameSpyServerAdvertiser::CreateGame(const char *szGameName, const char *szPlayerName, int MaxPlayers, const char *szPassword, const GUID &guidInstance, DWORD dwFlags)
{
#ifdef NEW_GAMESPY_HOST
if (m_pHost->BeginAdvertising(szGameName, MaxPlayers, szPassword, guidInstance, dwFlags))
{
if ((dwFlags & NETFLAGS_DEDICATED_FLAG)==0)
m_pHost->AddPlayer((int) gos_NetInformation( gos_MyID, 0, NULL ), szPlayerName, NULL, 0);
m_pHost->SetKey(GAMELIST_KEY_SERVER_PORT, (char *)GetDirectPlayPort());
return true;
}
else
m_bDelete=true;
return false;
#else
return GameSpyOld::BeginAdvertisingOnGameSpy(szGameName, szPlayerName, MaxPlayers, szPassword, guidInstance, false, dwFlags);
#endif
}
//========================================================================
//========================================================================
bool __stdcall GameSpyServerAdvertiser::CreatePlayer(DWORD dwItemID, const char *szPlayerName, bool bBot)
{
#ifdef NEW_GAMESPY_HOST
m_pHost->AddPlayer(dwItemID, szPlayerName, NULL, 0);
#else
GameSpyOld::AddPlayer(dwItemID, szPlayerName, NULL, 0);
#endif
return true;
}
//========================================================================
//========================================================================
bool __stdcall GameSpyServerAdvertiser::RemovePlayer(DWORD dwItemID, const char *szPlayerName, bool bBot)
{
#ifdef NEW_GAMESPY_HOST
m_pHost->RemovePlayer(dwItemID, szPlayerName, NULL, 0);
#else
GameSpyOld::RemovePlayer(dwItemID, szPlayerName, NULL, 0);
#endif
return true;
}
//========================================================================
//========================================================================
bool __stdcall GameSpyServerAdvertiser::SetItemValue(DWORD dwPlayer, const char * szName , const char * szValue)
{
#ifdef NEW_GAMESPY_HOST
m_pHost->SetKey(szName, szValue, dwPlayer);
#else
GameSpyOld::SetKey(szName, szValue);
#endif
return true;
}
//========================================================================
//========================================================================
bool __stdcall GameSpyServerAdvertiser::SetFlags(DWORD dwFlags)
{
#ifdef NEW_GAMESPY_HOST
m_pHost->SetFlags(dwFlags);
#else
char szValue[256];
sprintf(szValue, "%u", dwFlags);
GameSpyOld::SetKey(GAMESPY_KEY_FLAGS, szValue);
#endif
return true;
}
#ifndef NEW_GAMESPY
//========================================================================
//========================================================================
void GameSpyOld::GetGameSpyAddress(char *szGameSpyAddress, int size)
{
int s=size;
gos_LoadDataFromRegistry(GAMESPY_MASTER_SERVER_KEY, szGameSpyAddress, (DWORD *)&size);
if (size == 0)
{
gos_SaveStringToRegistry(GAMESPY_MASTER_SERVER_KEY, GAMESPY_MASTER_ADDR, sizeof(GAMESPY_MASTER_ADDR));
strncpy(szGameSpyAddress, GAMESPY_MASTER_ADDR, s);
}
}
//========================================================================
//========================================================================
int GameSpyOld::GetHeartbeatPort(void)
{
int port;
int size = sizeof(port);
gos_LoadDataFromRegistry(GAMESPY_MASTER_HEARTBEAT_PORT_KEY, &port, (DWORD *)&size);
if (size == 0)
{
port = GAMESPY_MASTER_HEARTBEAT_PORT;
gos_SaveDataToRegistry(GAMESPY_MASTER_HEARTBEAT_PORT_KEY, &port, sizeof(port));
}
return port;
}
//========================================================================
//========================================================================
int GameSpyOld::GetQueryPort(void)
{
int port;
int size = sizeof(port);
gos_LoadDataFromRegistry(GAMESPY_MASTER_QUERY_PORT_KEY, &port, (DWORD *)&size);
if (size == 0)
{
port = GAMESPY_MASTER_QUERY_PORT;
gos_SaveDataToRegistry(GAMESPY_MASTER_QUERY_PORT_KEY, &port, sizeof(port));
}
return port;
}
//========================================================================
//========================================================================
int GameSpyOld::GetServerListPort(void)
{
int port;
int size = sizeof(port);
gos_LoadDataFromRegistry(GAMESPY_MASTER_SERVERLIST_PORT_KEY, &port, (DWORD *)&size);
if (size == 0)
{
port = GAMESPY_MASTER_SERVERLIST_PORT;
gos_SaveDataToRegistry(GAMESPY_MASTER_SERVERLIST_PORT_KEY, &port, sizeof(port));
}
return port;
}
#endif
//========================================================================
//========================================================================
HWND CreateOffScreenNotificationWindow(void)
{
static const char s_szNotificationWindowClass[] = "NotificationClass";
WNDCLASSEX wcex;
wcex.cbSize = sizeof(wcex);
wcex.style = 0;
wcex.lpfnWndProc = 0;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 4; // 4 extra bytes indicating...
wcex.hInstance = 0;
wcex.hIcon = 0;
wcex.hCursor = 0;
wcex.hbrBackground = 0;
wcex.lpszMenuName = 0;
wcex.lpszClassName = 0;
wcex.hIconSm = 0;
//ATOM ThisClass = RegisterClassEx(&wcex);
return NULL;
}
@@ -0,0 +1,254 @@
#include "AdeptHeaders.hpp"
#include "GameSpy.h"
#include <GameOS/GUNGameList.h>
#ifndef NEW_GAMESPY
/******
cengtest.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
******
Please see the GameSpy C Engine SDK documentation for more
information
******/
#include "goaceng.h"
#include <stdio.h>
#include <string.h>
#ifndef _WIN32_WCE
#include <assert.h>
#else
#define assert(a)
#endif
#ifdef _WIN32_WCE
#include <tchar.h>
void RetailOutputA(CHAR *tszErr, ...);
#define printf RetailOutputA
#endif
#define GAMESPY_ASYNC
//========================================================================
//========================================================================
void ListCallBack1(GServerList serverlist, int msg, void *instance, void *param1, void *param2)
{
GServer server;
if (msg == LIST_PROGRESS)
{
server = (GServer)param1;
printf("%s:%d [%d] %s %d/%d %s\n",ServerGetAddress(server),ServerGetQueryPort(server), ServerGetPing(server),ServerGetStringValue(server, "hostname","(NONE)"), ServerGetIntValue(server,"numplayers",0), ServerGetIntValue(server,"maxplayers",0), ServerGetStringValue(server,"mapname","(NO MAP)"));
} else
printf("%d %d\n",msg,ServerListState(serverlist));
}
//========================================================================
//========================================================================
void sampleKeyEnumFn(char *key, char *value, void *instance)
{
printf("\t%s: %s\n",key, value);
}
//========================================================================
//========================================================================
void ListCallBack2(GServerList serverlist, int msg, void *instance, void *param1, void *param2)
{
GServer server;
if (msg == LIST_PROGRESS)
{
server = (GServer)param1;
printf("%s:%d| \n",ServerGetAddress(server),ServerGetQueryPort(server));
ServerEnumKeys(server, sampleKeyEnumFn, NULL);
printf("\n");
}
}
//========================================================================
//========================================================================
void PrintServerList(GServerList sl)
{
GServer server;
int i;
for (i = 0; i < ServerListCount(sl) ; i++)
{
server = ServerListGetServer(sl, i);
assert(server != NULL);
printf("%s:%d [%d] %s %d/%d %s\n",ServerGetAddress(server),ServerGetQueryPort(server), ServerGetPing(server),ServerGetStringValue(server, "hostname","(NONE)"), ServerGetIntValue(server,"numplayers",0), ServerGetIntValue(server,"maxplayers",0), ServerGetStringValue(server,"mapname","(NO MAP)"));
}
printf("\n\n\n");
}
//========================================================================
//========================================================================
char *GameSpyOld::GetKeyName(void)
{
static const unsigned char szKey1[] = { 0x5C,0x2B,0xCA,0x29,0x6E,0x2E,0xA4, };
static const unsigned char szKey2[] = { 0x29,0x65,0xA8,0x71,0x0B,0x48,0xA4, };
static char szKey[10];
char *p = &szKey[-1];
unsigned char const *p1 = szKey1;
unsigned char const *p2 = szKey2;
do
{
*++p = (unsigned char)(*p2++^*p1++);
} while (*p);
return szKey;
}
//========================================================================
//========================================================================
static GServerList s_sl=NULL;
bool GameSpyOld::Querying(void)
{
return !!s_sl;
}
//========================================================================
//========================================================================
bool GameSpyOld::SynchronizeServerList(void)
{
#ifdef GAMESPY_ASYNC
if (s_sl)
{
if (!IsServerListIdle(s_sl))
ServerListThink(s_sl);
else
return false;
}
#endif
return true;
}
//========================================================================
//========================================================================
bool GameSpyOld::BeginQueryingGameSpy(void)
{
EndQueryingGameSpy();
s_sl = ServerListNew(GAMESPY_MASTER_GAMENAME,GAMESPY_MASTER_ENGINENAME,GetKeyName(),10,ListCallBack1,GCALLBACK_FUNCTION,NULL);
#ifdef GAMESPY_ASYNC
if (s_sl)
{
char szAddress[256];
GetGameSpyAddress(szAddress, sizeof(szAddress));
if (ServerListUpdate(s_sl,true, szAddress, GetServerListPort()) != 0)
{
GameSpyOld::status = -GS_FIND_QSERVER;
ServerListFree(s_sl);
s_sl=NULL;
}
}
#else
if (s_sl)
{
if (ServerListUpdate(s_sl,0) != 0)
{
GameSpyOld::status = -GS_FIND_QSERVER;
ServerListFree(s_sl);
s_sl=NULL;
}
}
#endif
return (s_sl != NULL);
}
//========================================================================
//========================================================================
void GameSpyOld::EndQueryingGameSpy(void)
{
if (s_sl)
{
ServerListFree(s_sl);
s_sl=NULL;
}
}
//========================================================================
//========================================================================
char *GameSpyOld::GetServerAddress(int index)
{
GServer server;
server = ServerListGetServer(s_sl, index);
if (server)
{
return ServerGetAddress(server);
//ServerGetAddress(server)
//ServerGetQueryPort(server)
//ServerGetPing(server)
//ServerGetStringValue(server, "hostname","(NONE)")
//ServerGetIntValue(server,"numplayers",0)
//ServerGetIntValue(server,"maxplayers",0)
//ServerGetStringValue(server,"mapname","(NO MAP)"));
}
return NULL;
}
//========================================================================
//========================================================================
char *GameSpyOld::GetServerString(int index, const char *szKey, const char *szDefault)
{
GServer server;
server = ServerListGetServer(s_sl, index);
if (server)
{
return ServerGetStringValue(server, (char *)szKey, (char *)szDefault);
}
return NULL;
}
//========================================================================
//========================================================================
char *GameSpyOld::GetPlayerString(int index, int playerindex, const char *szKey, const char *szDefault)
{
GServer server;
server = ServerListGetServer(s_sl, index);
if (server)
{
return ServerGetPlayerStringValue(server, playerindex, (char *)szKey, (char *)szDefault);
}
return NULL;
}
//========================================================================
//========================================================================
int GameSpyOld::GetNumberOfGames(void)
{
if (s_sl == NULL)
return 0;
return ServerListCount(s_sl);
}
#endif
@@ -0,0 +1,239 @@
#include "AdeptHeaders.hpp"
#include "GameSpy.h"
/*
*
* File: darray.c
* ---------------
* David Wright
* 10/8/98
*
* See darray.h for function descriptions
*/
#ifndef UNDER_CE
#include <assert.h>
#else
#define assert(a)
#endif
#include <stdlib.h>
#include <string.h>
#include "darray.h"
#define DEF_GROWBY 8
// STRUCTURES
struct DArrayImplementation
{
int count, capacity;
int elemsize;
int growby;
ArrayElementFreeFn elemfreefn;
void *list; //array of elements
};
// PROTOTYPES
static void *mylsearch(const void *key, void *base, int count, int size,
ArrayCompareFn comparator);
static void *mybsearch(const void *elem, void *base, int num, int elemsize,
ArrayCompareFn comparator);
// FUNCTIONS
/* FreeElement
* Frees the element at position N in the array
*/
static void FreeElement(DArray array, int n)
{
if (array->elemfreefn != NULL)
array->elemfreefn(ArrayNth(array,n));
}
/* ArrayGrow
* Reallocates the array to a new size, incresed by growby
*/
static void ArrayGrow(DArray array)
{
array->capacity += array->growby;
array->list = realloc(array->list, array->capacity * array->elemsize);
assert(array->list);
}
/* SetElement
* Sets the element at pos to the contents of elem
*/
static void SetElement(DArray array, const void *elem, int pos)
{
memcpy(ArrayNth(array,pos), elem, array->elemsize);
}
DArray ArrayNew(int elemSize, int numElemsToAllocate,
ArrayElementFreeFn elemFreeFn)
{
DArray array;
array = (DArray) malloc(sizeof(struct DArrayImplementation));
assert(array);
assert(elemSize);
if (numElemsToAllocate == 0)
numElemsToAllocate = DEF_GROWBY;
array->count = 0;
array->capacity = numElemsToAllocate;;
array->elemsize = elemSize;
array->growby = numElemsToAllocate;
array->elemfreefn = elemFreeFn;
array->list = malloc(array->capacity * array->elemsize);
assert(array->list);
return array;
}
void ArrayFree(DArray array)
{
int i;
assert(array);
for (i = 0; i < array->count; i++)
{
FreeElement(array, i);
}
free(array->list);
free(array);
}
int ArrayLength(const DArray array)
{
return array->count;
}
void *ArrayNth(DArray array, int n)
{
assert( (n >= 0) && (n < array->count));
return (char *)array->list + array->elemsize*n;
}
/* ArrayAppend
* Just do an Insert at the end of the array
*/
void ArrayAppend(DArray array, const void *newElem)
{
ArrayInsertAt(array, newElem, array->count);
}
void ArrayInsertAt(DArray array, const void *newElem, int n)
{
assert( (n >= 0) && (n <= array->count));
if (array->count == array->capacity)
ArrayGrow(array);
array->count++;
if (n < array->count - 1) //if we aren't appending
memmove(ArrayNth(array, n+1), ArrayNth(array,n),
(array->count - 1 - n) * array->elemsize);
SetElement(array, newElem, n);
}
void ArrayRemoveAt(DArray array, int n)
{
assert( (n >= 0) && (n < array->count));
if (n < array->count - 1) //if not last element
memmove(ArrayNth(array,n),ArrayNth(array,n+1),
(array->count - 1 - n) * array->elemsize);
array->count--;
}
void ArrayDeleteAt(DArray array, int n)
{
assert( (n >= 0) && (n < array->count));
FreeElement(array,n);
ArrayRemoveAt(array, n);
}
void ArrayReplaceAt(DArray array, const void *newElem, int n)
{
assert( (n >= 0) && (n < array->count));
FreeElement(array, n);
SetElement(array, newElem,n);
}
void ArraySort(DArray array, ArrayCompareFn comparator)
{
qsort(array->list, array->count, array->elemsize, comparator);
}
//assert will be raised by ArrayNth if fromindex out of range
int ArraySearch(DArray array, const void *key, ArrayCompareFn comparator,
int fromIndex, int isSorted)
{
void *res;
if (array->count == 0)
return NOT_FOUND;
if (isSorted)
res=mybsearch(key, ArrayNth(array,fromIndex),
array->count - fromIndex, array->elemsize, comparator);
else
res=mylsearch(key, ArrayNth(array, fromIndex),
array->count - fromIndex, array->elemsize, comparator);
if (res != NULL)
return (((char *)res - (char *)array->list) / array->elemsize);
else
return NOT_FOUND;
}
void ArrayMap(DArray array, ArrayMapFn fn, void *clientData)
{
int i;
assert(fn);
for (i = 0; i < array->count; i++)
fn(ArrayNth(array,i), clientData);
}
/* mylsearch
* Implementation of a standard linear search on an array, since we
* couldn't use lfind
*/
static void *mylsearch(const void *key, void *base, int count, int size,
ArrayCompareFn comparator)
{
int i;
for (i = 0; i < count; i++)
{
if (comparator(key, (char *)base + size*i) == 0)
return (char *)base + size*i;
}
return NULL;
}
/* mybsearch
* Implementation of a bsearch, since its not available on all platforms
*/
static void *mybsearch(const void *elem, void *base, int num, int elemsize, ArrayCompareFn comparator)
{
int L, H, I, C;
L = 0;
H = num - 1;
while (L <= H)
{
I = (L + H) >> 1;
C = comparator(((char *)base) + I * elemsize,elem);
if (C < 0)
L = I + 1;
else
{
H = I - 1;
}
}
return ((char *)base) + L * elemsize;
}
@@ -0,0 +1,257 @@
#ifndef _DARRAY_H
#define _DARRAY_H
/* File: darray.h
* --------------
* Defines the interface for the DynamicArray ADT.
* The DArray allows the client to store any number of elements of any desired
* base type and is appropriate for a wide variety of storage problems. It
* supports efficient element access, and appending/inserting/deleting elements
* as well as optional sorting and searching. In all cases, the DArray imposes
* no upper bound on the number of elements and deals with all its own memory
* management. The client specifies the size (in bytes) of the elements that
* will be stored in the array when it is created. Thereafter the client and
* the DArray can refer to elements via (void*) ptrs.
*/
#ifdef __cplusplus
extern "C" {
#endif
/* Type: DArray
* ----------------
* Defines the DArray type itself. The client can declare variables of type
* DArray, but these variables must be initialized with the result of ArrayNew.
* The DArray is implemented with pointers, so all client copies in variables
* or parameters will be "shallow" -- they will all actually point to the
* same DArray structure. Only calls to ArrayNew create new arrays.
* The struct declaration below is "incomplete"- the implementation
* details are literally not visible in the client .h file.
*/
typedef struct DArrayImplementation *DArray;
/* ArrayCompareFn
* --------------
* ArrayCompareFn is a pointer to a client-supplied function which the
* DArray uses to sort or search the elements. The comparator takes two
* (const void*) pointers (these will point to elements) and returns an int.
* The comparator should indicate the ordering of the two elements
* using the same convention as the strcmp library function:
* If elem1 is "less than" elem2, return a negative number.
* If elem1 is "greater than" elem2, return a positive number.
* If the two elements are "equal", return 0.
*/
typedef int (_cdecl *ArrayCompareFn)(const void *elem1, const void *elem2);
/* ArrayMapFn
* ----------
* ArrayMapFn defines the space of functions that can be used to map over
* the elements in a DArray. A map function is called with a pointer to
* the element and a client data pointer passed in from the original
* caller.
*/
typedef void (_cdecl *ArrayMapFn)(void *elem, void *clientData);
/* ArrayElementFreeFn
* ------------------
* ArrayElementFreeFn defines the space of functions that can be used as the
* clean-up function for an element as it is deleted from the array
* or when the entire array of elements is freed. The cleanup function is
* called with a pointer to an element about to be deleted.
*/
typedef void (_cdecl *ArrayElementFreeFn)(void *elem);
/* ArrayNew
* --------
* Creates a new DArray and returns it. There are zero elements in the array.
* to start. The elemSize parameter specifies the number of bytes that a single
* element of this array should take up. For example, if you want to store
* elements of type Binky, you would pass sizeof(Binky) as this parameter.
* An assert is raised if the size is not greater than zero.
*
* The numElemsToAllocate parameter specifies the initial allocated length
* of the array, as well as the dynamic reallocation increment for when the
* array grows. Rather than growing the array one element at a time as
* elements are added (which is rather inefficient), you will grow the array
* in chunks of numElemsToAllocate size. The "allocated length" is the number
* of elements that have been allocated, the "logical length" is the number of
* those slots actually being currently used.
*
* A new array is initially allocated to the size of numElemsToAllocate, the
* logical length is zero. As elements are added, those allocated slots fill
* up and when the initial allocation is all used, grow the array by another
* numElemsToAllocate elements. You will continue growing the array in chunks
* like this as needed. Thus the allocated length will always be a multiple
* of numElemsToAllocate. Don't worry about using realloc to shrink the array
* allocation if many elements are deleted from the array. It turns out that
* many implementations of realloc don't even pay attention to such a request
* so there is little point in asking. Just leave the array over-allocated.
*
* The numElemsToAllocate is the client's opportunity to tune the resizing
* behavior for their particular needs. If constructing large arrays,
* specifying a large allocation chunk size will result in fewer resizing
* operations. If using small arrays, a small allocation chunk size will
* result in less space going unused. If the client passes 0 for
* numElemsToAllocate, the implementation will use the default value of 8.
*
* The elemFreeFn is the function that will be called on an element that
* is about to be deleted (using ArrayDeleteAt) or on each element in the
* array when the entire array is being freed (using ArrayFree). This function
* is your chance to do any deallocation/cleanup required for the element
* (such as freeing any pointers contained in the element). The client can pass
* NULL for the cleanupFn if the elements don't require any handling on free.
*/
DArray ArrayNew(int elemSize, int numElemsToAllocate,
ArrayElementFreeFn elemFreeFn);
/* ArrayFree
* ----------
* Frees up all the memory for the array and elements. It DOES NOT
* automatically free memory owned by pointers embedded in the elements.
* This would require knowledge of the structure of the elements which the
* DArray does not have. However, it will iterate over the elements calling
* the elementFreeFn earlier supplied to ArrayNew and therefore, the client,
* who knows what the elements are, can do the appropriate deallocation of any
* embedded pointers through that function. After calling this, the value of
* what array is pointing to is undefined.
*/
void ArrayFree(DArray array);
/* ArrayLength
* -----------
* Returns the logical length of the array, i.e. the number of elements
* currently in the array. Must run in constant time.
*/
int ArrayLength(const DArray array);
/* ArrayNth
* --------
* Returns a pointer to the element numbered n in the specified array.
* Numbering begins with 0. An assert is raised if n is less than 0 or greater
* than the logical length minus 1. Note this function returns a pointer into
* the DArray's element storage, so the pointer should be used with care.
* This function must operate in constant time.
*
* We could have written the DArray without this sort of access, but it
* is useful and efficient to offer it, although the client needs to be
* careful when using it. In particular, a pointer returned by ArrayNth
* becomes invalid after any calls which involve insertion, deletion or
* sorting the array, as all of these may rearrange the element storage.
*/
void *ArrayNth(DArray array, int n);
/* ArrayAppend
* -----------
* Adds a new element to the end of the specified array. The element is
* passed by address, the element contents are copied from the memory pointed
* to by newElem. Note that right after this call, the new element will be
* the last in the array; i.e. its element number will be the logical length
* minus 1. This function must run in constant time (neglecting
* the memory reallocation time which may be required occasionally).
*/
void ArrayAppend(DArray array, const void *newElem);
/* ArrayInsertAt
* -------------
* Inserts a new element into the array, placing it at the position n.
* An assert is raised if n is less than 0 or greater than the logical length.
* The array elements after position n will be shifted over to make room. The
* element is passed by address, the new element's contents are copied from
* the memory pointed to by newElem. This function runs in linear time.
*/
void ArrayInsertAt(DArray array, const void *newElem, int n);
/* ArrayDeleteAt
* -------------
* Deletes the element numbered n from the array. Before being removed,
* the elemFreeFn that was supplied to ArrayNew will be called on the element.
* An assert is raised if n is less than 0 or greater than the logical length
* minus one. All the elements after position n will be shifted over to fill
* the gap. This function runs in linear time. It does not shrink the
* allocated size of the array when an element is deleted, the array just
* stays over-allocated.
*/
void ArrayDeleteAt(DArray array, int n);
/* ArrayDeleteAt
* -------------
* Removes the element numbered n from the array. The element will not be freed
* before being removed. All the elements after position n will be shifted over to fill
* the gap. This function runs in linear time. It does not shrink the
* allocated size of the array when an element is deleted, the array just
* stays over-allocated.
*/
void ArrayRemoveAt(DArray array, int n);
/* ArrayReplaceAt
* -------------
* Overwrites the element numbered n from the array with a new value. Before
* being overwritten, the elemFreeFn that was supplied to ArrayNew is called
* on the old element. Then that position in the array will get a new value by
* copying the new element's contents from the memory pointed to by newElem.
* An assert is raised if n is less than 0 or greater than the logical length
* minus one. None of the other elements are affected or rearranged by this
* operation and the size of the array remains constant. This function must
* operate in constant time.
*/
void ArrayReplaceAt(DArray array, const void *newElem, int n);
/* ArraySort
* ---------
* Sorts the specified array into ascending order according to the supplied
* comparator. The numbering of the elements will change to reflect the
* new ordering. An assert is raised if the comparator is NULL.
*/
void ArraySort(DArray array, ArrayCompareFn comparator);
#define NOT_FOUND -1 // returned when a search fails to find the key
/* ArraySearch
* -----------
* Searches the specified array for an element whose contents match
* the element passed as the key. Uses the comparator argument to test
* for equality. The "fromIndex" parameter controls where the search
* starts looking from. If the client desires to search the entire array,
* they should pass 0 as the fromIndex. The function will search from
* there to the end of the array. The "isSorted" parameter allows the client
* to specify that the array is already in sorted order, and thus it uses a
* faster binary search. If isSorted is false, a simple linear search is
* used. If a match is found, the position of the matching element is returned
* else the function returns NOT_FOUND. Calling this function does not
* re-arrange or change contents of DArray or modify the key in any way.
* An assert is raised if fromIndex is less than 0 or greater than
* the logical length (although searching from logical length will never
* find anything, allowing this case means you can search an entirely empty
* array from 0 without getting an assert). An assert is raised if the
* comparator is NULL.
*/
int ArraySearch(DArray array, const void *key, ArrayCompareFn comparator,
int fromIndex, int isSorted);
/* ArrayMap
* -----------
* Iterates through each element in the array in order (from element 0 to
* element n-1) and calls the function fn for that element. The function is
* called with the address of the array element and the clientData pointer.
* The clientData value allows the client to pass extra state information to
* the client-supplied function, if necessary. If no client data is required,
* this argument should be NULL. An assert is raised if map function is NULL.
*/
void ArrayMap(DArray array, ArrayMapFn fn, void *clientData);
#ifdef __cplusplus
}
#endif
#endif _DARRAY_
@@ -0,0 +1,261 @@
/************
GameSpy Open Architecture
Portable C Engine
*************/
/******
goaceng.h
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
******
Please see the GameSpy C Engine SDK documentation for more
information
Updated 3/16/00 - DDW
Added ServerListRemoveServer (to remove a server from the serverlist)
Updated ServerListAuxUpdate to update the existing server if its already in the list
instead of adding a duplicate
Note that these functions change the indexes of servers in the list. You should never
rely on the indexes staying constant (they change if you use the sort functions too).
Updated 3/18/00 - DDW
Added Dreamcast CE support (needs to recreate sockets for each query)
******/
//TODO: smartspy filtering
#ifndef _GOACENG_H
#define _GOACENG_H
#ifdef __cplusplus
extern "C" {
#endif
#define ENGINE_VERSION CURRENT_BUILD_FULL_STRING
#define SERVER_TIMEOUT 3000
/* GServerList and GServer are the abstracted serverlist and server data types.
All access to their internal data structures is done via the functions below */
typedef struct GServerListImplementation *GServerList;
typedef struct GServerImplementation *GServer;
extern bool IsServerListIdle(GServerList);
/* A simple error type that we can use for return values to certain functions */
typedef int GError; //we will define some error return values
typedef int gbool; //a simple boolean type
/* Various Server List States
idle - no processing or querying underway, ready to being updates
listreq - a server list request has been or is being sent
listxfer - we are transfering the server list
lanlist - we are waiting for replies from servers on the LAN
querying - the servers on the list are being queried */
typedef enum {sl_idle, sl_listxfer, sl_lanlist, sl_querying} GServerListState;
/* Comparision types for the ServerListSort function
int - assume the values are int and do an integer compare
float - assume the values are float and do a flot compare
strcase - assume the values are strings and do a case sensitive compare
stricase - assume the values are strings and do a case insensitive compare */
typedef enum {cm_int, cm_float, cm_strcase, cm_stricase} GCompareMode;
/* Messages that are passed to the ListCallBackFn */
#define LIST_STATECHANGED 1 // ServerListState changed, no parameters
#define LIST_PROGRESS 2 // New Server updated, param1 = GServer (server updated), param2 = percent done
//Single callback function into the client app for status / progress messages
typedef void (_cdecl *ListCallBackFn)(GServerList serverlist, int msg, void *instance, void *param1, void *param2);
// Callback function used for enumerating the keys/values for a server
typedef void (_cdecl *KeyEnumFn)(char *key, char *value, void *instance);
/* Callback parameter types (e.g. window handle, thread window, event, function etc) */
#define GCALLBACK_FUNCTION 1 //only currently supported callback type
#define GE_NOERROR 0
#define GE_NOSOCKET 1
#define GE_NODNS 2
#define GE_NOCONNECT 3
#define GE_BUSY 4
#define GE_DATAERROR 5
/*********
Server List Functions
**********/
/* ServerListNew
----------------
Creates and returns a new (empty) GServerList.
gamename - game to ask the master for servers
enginename - the engine name that matches your seckey
seckey - secret key used for talking to the master
maxconcupdates - max number of concurent updates (10-15 for modem users, 20-30 for high-bandwidth)
CallBackFn - The function or handle used for progress updates
CallBackFnType - The type of the CallBackFn parameter (from the #define list above)
instance - user-defined instance data (e.g. structure or object pointer) */
GServerList ServerListNew(char *gamename,char *enginename, char *seckey, int maxconcupdates, void *CallBackFn, int CallBackFnType, void *instance);
/* ServerListFree
-----------------
Free a GServerList and all internal sturctures and servers */
void ServerListFree(GServerList serverlist);
/* ServerListUpdate
-------------------
Start updating a GServerList from the master server.
Can only be called when the list is in the sl_idle state, will return an error otherwise.
The server list will be retrieved from the master, then each server in the list will be
added and updated.
If async = false, the function will not return until the entire list has been processed, or
ServerListHalt has been called (from another thread or from the progress callback)
If async = true, the function will return immediately, but you must call ServerListThink
every ~10ms for list processing and querying to occur. */ //TODO: add filter
//GError ServerListUpdate(GServerList serverlist, gbool async);
GError ServerListUpdate(GServerList serverlist, gbool async, char *szMasterServer, int iServerListPort);
/* ServerListLANUpdate
-------------------
Search for servers on the local LAN and start updating them. This will search over a
range of ports for any servers within broadcast range.
Can only be called when the list is in the sl_idle state, will return an error otherwise.
A query will be sent out on each port between start and end (inclusive) on intervals of delta
(e.g. 10,20,2 would search 10,12,14,16,18,20)
If async = false, the function will not return until the entire list has been processed, or
ServerListHalt has been called (from another thread or from the progress callback)
If async = true, the function will return immediately, but you must call ServerListThink
every ~10ms for list processing and querying to occur. */ //TODO: add filter
GError ServerListLANUpdate(GServerList serverlist, gbool async, int startsearchport, int endsearchport, int searchdelta);
/* ServerListAuxUpdate
-------------------
Adds an "auxilliary" server -- an untracked caller-defined ip and port -- to the update
list. If there is an update currently in progress (not sl_idle), the aux server will
be processed along with the current batch. If the state is idle, this function behaves
like ServerListUpdate() and starts the engine.
If async = false, the function will not return until the server has been processed, or
ServerListHalt has been called (from another thread or from the progress callback)
If async = true, the function will return immediately, but you must call ServerListThink
every ~10ms for list processing and querying to occur. Notw that async has no effect
if an update is in progress. */
GError ServerListAuxUpdate(GServerList serverlist, char *ip, int port, gbool async);
/* ServerListRemoveServer
-------------------------
Removes a single server from the list. Frees the memory associated with the GServer.
Do not reference the GServer object after calling this function. Note that this changes
the indexes of the servers in the list. You should never store the indexes of the servers
in the list, just their GServer objects */
void ServerListRemoveServer(GServerList serverlist, char *ip, int port);
/* ServerListThink
------------------
For use with Async Updates. This needs to be called every ~10ms for list processing and
updating to occur during async server list updates */
GError ServerListThink(GServerList serverlist);
/* ServerListHalt
-----------------
Halts the current updates batch */
GError ServerListHalt(GServerList serverlist);
/* ServerListClear
------------------
Clear and free all of the servers from the server list.
List must be in the sl_idle state */
GError ServerListClear(GServerList serverlist);
/* ServerListState
------------------
Returns the current state of the server list */
GServerListState ServerListState(GServerList serverlist);
/* ServerListErrorDesc
----------------------
Returns a static string description of the specified error */
char *ServerListErrorDesc(GServerList serverlist, GError error);
/* ServerListGetServer
----------------------
Returns the server at the specified index, or NULL if the index is out of bounds */
GServer ServerListGetServer(GServerList serverlist, int index);
/* ServerListCount
------------------
Returns the number of servers on the specified list. Indexing is 0 based, so
the actual server indexes are 0 <= valid index < Count */
int ServerListCount(GServerList serverlist);
/* ServerListSort
-----------------
Sort the server list in either ascending or descending order using the
specified comparemode.
sortkey can be a normal server key, or "ping" or "hostaddr" */
void ServerListSort(GServerList serverlist, gbool ascending, char *sortkey, GCompareMode comparemode);
/**************
ServerFunctions
***************/
/* ServerGetPing
----------------
Returns the ping for the specified server.
A ping of 9999 indicates that the server has not been queried or did not respond */
int ServerGetPing(GServer server);
/* ServerGetAddress
-------------------
Returns the string, dotted IP address for the specified server */
char *ServerGetAddress(GServer server);
/* ServerGetPort
----------------
Returns the "query" port for the specified server. If the game uses a seperate
"game" port, it can be retrieved via: ServerGetIntValue(server,"hostport",0) */
int ServerGetQueryPort(GServer server);
/* ServerGet[]Value
------------------
Returns the value for the specified key. If the key does not exist for the
given server, the default value is returned */
char *ServerGetStringValue(GServer server, char *key, char *sdefault);
int ServerGetIntValue(GServer server, char *key, int idefault);
double ServerGetFloatValue(GServer server, char *key, double fdefault);
/* ServerGetPlayer[]Value
------------------
Returns the value for the specified key on the specified player. If the key does not exist for the
given server, the default value is returned */
char *ServerGetPlayerStringValue(GServer server, int playernum, char *key, char *sdefault);
int ServerGetPlayerIntValue(GServer server, int playernum, char *key, int idefault);
double ServerGetPlayerFloatValue(GServer server, int playernum, char *key, double 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 */
void ServerEnumKeys(GServer server, KeyEnumFn KeyFn, void *instance);
#ifdef __cplusplus
}
#endif
#endif _GOACENG_H
@@ -0,0 +1,465 @@
#include "AdeptHeaders.hpp"
#include "GameSpy.h"
#include <gameOS/GUNGameList.h>
#include "buildnum/buildnum.h"
#pragma comment(lib, "wsock32")
#ifndef NEW_GAMESPY
/******
goasample.c
GameSpy Developer SDK Sample App
Copyright 1999 GameSpy Industries, Inc
Suite E-204
2900 Bristol Street
Costa Mesa, CA 92626
(714)549-7689
Fax(714)549-0757
******
This source file includes a "sample" game shell which demonstrates
how to use the GameSpy Developer SDK (goautil.h)
Please see the GameSpy Developer SDK documentation for more
information
******/
#define FixSZString(s) ((s)[sizeof(s)-1]) = 0
namespace GameSpyOld
{
bool bGameSpy;
};
/********
INCLUDES
********/
#include "nonport.h"
#include "goautil.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <conio.h>
/********
DEFINES
********/
#define GAME_VERSION CURRENT_BUILD_FULL_STRING
#define GAME_NAME GAMESPY_MASTER_ENGINENAME
#define MAX_PLAYERS 32
#ifdef _WIN32_WCE
void RetailOutputA(CHAR *tszErr, ...);
#define printf RetailOutputA
#endif
/********
TYPDEFS
********/
//representative of a game player structure
typedef struct
{
char pname[80];
char pteam[80];
int id;
} player_t;
//representative of a game data structure
typedef struct
{
player_t players[MAX_PLAYERS];
char gamename[50];
char mapname[20];
char hostname[120];
char gamemode[200];
char gametype[30];
GUID guid;
int locationid;
int numplayers;
int maxplayers;
int fraglimit;
int timelimit;
int teamplay;
int rankingson;
int hostport;
int flags;
} gamedata_t;
/********
GLOBAL VARS
********/
//just to give us bogus data
char *constnames[MAX_PLAYERS]={"Joe Player","L33t 0n3","Raptor","Gr81","Flubber","Sarge","Void","runaway","Ph3ar","wh00t","gr1nder","Mace","stacy","lamby","Thrush"};
gamedata_t gamedata;
/*************
basic_callback
sends a (sample) response to the basic query
includes the following keys:
\gamename\
\gamever\
\location\
*************/
static void basic_callback(char *outbuf, int maxlen, void *userdata)
{
sprintf(outbuf, "\\%s\\%s\\%s\\%s\\%s\\%d",
GAMESPY_KEY_GAME_NAME, gamedata.gamename,
GAMESPY_KEY_PRODUCT_VERSION, GAME_VERSION,
GAMESPY_KEY_LOCATION, gamedata.locationid);
// printf("Basic callback, sent: %s\n",outbuf);
}
/************
info_callback
Sends a (sample) response to the info query
including the following keys:
\hostname\
\hostport\
\mapname\
\gametype\
\numplayers\
\maxplayers\
\gamemode\
************/
static void info_callback(char *outbuf, int maxlen, void *userdata)
{
char keyvalue[200];
sprintf(keyvalue,"\\%s\\%s", GAMESPY_KEY_HOSTNAME, gamedata.hostname);strcat(outbuf, keyvalue);
sprintf(keyvalue,"\\%s\\%d", GAMESPY_KEY_HOSTPORT, gamedata.hostport);strcat(outbuf, keyvalue);
sprintf(keyvalue,"\\%s\\%s", GAMESPY_KEY_MAP_TYPE, gamedata.mapname);strcat(outbuf, keyvalue);
sprintf(keyvalue,"\\%s\\%s", GAMESPY_KEY_GAME_TYPE, gamedata.gametype);strcat(outbuf, keyvalue);
sprintf(keyvalue,"\\%s\\%d", GAMESPY_KEY_NUM_PLAYERS, gamedata.numplayers);strcat(outbuf, keyvalue);
sprintf(keyvalue,"\\%s\\%d", GAMESPY_KEY_MAX_PLAYERS, gamedata.maxplayers);strcat(outbuf, keyvalue);
sprintf(keyvalue,"\\%s\\%s", GAMESPY_KEY_GAMEMODE, gamedata.gamemode);strcat(outbuf, keyvalue);
sprintf(keyvalue,"\\%s\\%d", GAMESPY_KEY_FLAGS, gamedata.flags);strcat(outbuf, keyvalue);
sprintf(keyvalue,"\\%s\\{%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X}", GAMESPY_KEY_INSTANCE,
gamedata.guid.Data1,
gamedata.guid.Data2,
gamedata.guid.Data3,
gamedata.guid.Data4[0],
gamedata.guid.Data4[1],
gamedata.guid.Data4[2],
gamedata.guid.Data4[3],
gamedata.guid.Data4[4],
gamedata.guid.Data4[5],
gamedata.guid.Data4[6],
gamedata.guid.Data4[7]);strcat(outbuf, keyvalue);
// printf("Info callback, sent: %s\n",outbuf);
}
/***************
rules_callback
Sends a response to the rules query. You may
need to add custom fields for your game in here. Some are provided
as an example
The following rules are included:
\timelimit\
\fraglimit\
\teamplay\
\rankedserver\
****************/
static void rules_callback(char *outbuf, int maxlen, void *userdata)
{
// char keyvalue[80];
// sprintf(keyvalue,"\\%s\\%d", GAMESPY_KEY_TIMELIMIT, gamedata.timelimit);strcat(outbuf, keyvalue);
// sprintf(keyvalue,"\\%s\\%d", GAMESPY_KEY_FRAGLIMIT, gamedata.fraglimit);strcat(outbuf, keyvalue);
// sprintf(keyvalue,"\\%s\\%d", GAMESPY_KEY_TEAMPLAY, gamedata.teamplay);strcat(outbuf, keyvalue);
// //sample custom key/value
// sprintf(keyvalue,"\\%s\\%d", GAMESPY_KEY_RANKEDSERVER, gamedata.rankingson);strcat(outbuf, keyvalue);
// printf("Rules callback, sent: %s\n",outbuf);
}
/***************
players_callback
sends the players and their information.
Note that \ characters are not stripped out of player names. If
your game allows players or team names with the \ character, you will need
to strip or change it here.
The following keys are included for each player:
\player_N\
\frags_N\
\deaths_N\
\skill_N\
\ping_N\
\team_N\
***************/
static void players_callback(char *outbuf, int maxlen, void *userdata)
{
char keyvalue[160];
int i;
for (i = 0; i < gamedata.numplayers ; i++)
{
sprintf(keyvalue,
"\\player_%d\\%s\\team_%d\\%s",
i, gamedata.players[i].pname,
i, gamedata.players[i].pteam);
strcat(outbuf, keyvalue);
}
//printf("Players callback, sent: %s\n",outbuf);
}
/////***********
////init_game
////Initialize the sample data structures with bogus data
////************/
////static void init_game(void)
////{
//// int i;
//// int team;
////
//// srand( current_time() );
//// gamedata.numplayers = rand() % 15;
//// gamedata.maxplayers = MAX_PLAYERS;
//// for (i = 0 ; i < gamedata.numplayers ; i++)
//// {
//// strcpy(gamedata.players[i].pname, constnames[i]);
//// gamedata.players[i].pfrags = rand() % 32;
//// gamedata.players[i].pdeaths = rand() % 32;
//// gamedata.players[i].pskill = rand() % 1000;
//// gamedata.players[i].pping = rand() % 500;
//// team = rand() % 3;
//// if (team == 0)
//// strcpy(gamedata.players[i].pteam,"Red");
//// else if (team == 1)
//// strcpy(gamedata.players[i].pteam,"Blue");
//// else if (team == 2)
//// strcpy(gamedata.players[i].pteam,"");
//// }
//// strcpy(gamedata.mapname,"gmtmap1");
//// strcpy(gamedata.gametype,"arena");
//// strcpy(gamedata.hostname,"GameMaster Arena Server");
//// strcpy(gamedata.gamemode,"openplaying");
//// gamedata.fraglimit = 0;
//// gamedata.timelimit = 40;
//// gamedata.teamplay = 1;
//// gamedata.locationid = 1;
//// gamedata.rankingson = 1;
//// gamedata.hostport = 25000;
////}
/*******
DoGameStuff
Simulate whatever else a game server does
********/
void DoGameStuff(void)
{
msleep(10);
}
/////*******************
//// main
////Simulates a main program loop
////First, initializes the GOA items, then enters a main loop
////*****************/
////int main_1(int argc, char* argv[])
////{
//// //set the callback functions
//// goa_basic_callback = basic_callback;
//// goa_info_callback = info_callback;
//// goa_rules_callback = rules_callback;
//// goa_players_callback = players_callback;
//// //set the secret key, in a semi-obfuscated manner
////
////#if 0
//// goa_secret_key[0] = 'H';
//// goa_secret_key[1] = 'A';
//// goa_secret_key[2] = '6';
//// goa_secret_key[3] = 'z';
//// goa_secret_key[4] = 'k';
//// goa_secret_key[5] = 'S';
//// goa_secret_key[6] = '\0';
////#endif
//// init_game();
////
//// //call goa_init with the query port number and gamename, default IP address, and no user data
//// if (goa_init(27950,GAME_NAME, "localhost", NULL) != 0)
//// {
//// fprintf(stderr,"Error starting query sockets\n");
//// return -1;
//// }
////
//// while (kbhit())
//// _getch();
////
//// printf("Press any key to quit\n");
//// while (!kbhit())
//// {
//// DoGameStuff();
//// //check for / process incoming queries
//// goa_process_queries();
////
//// }
//// _getch();
////
//// //let gamemaster know we are shutting down
//// strcpy(gamedata.gamemode,"exiting");
//// goa_send_statechanged();
//// goa_shutdown();
////
//// return 0;
////
////}
//========================================================================
//========================================================================
bool GameSpyOld::BeginAdvertisingOnGameSpy(const char * GameName, const char *PlayerName, int MaxPlayers, const char *Password, const GUID &guidInstance, bool Secure, DWORD dwFlags)
{
EndAdvertisingOnGameSpy();
gosASSERT(GameName != NULL);
#ifdef _DEBUG
if (!GameName)
GameName = "testgame";
#endif
strcpy(goa_secret_key, GetKeyName());
//set the callback functions
goa_basic_callback = basic_callback;
goa_info_callback = info_callback;
goa_rules_callback = rules_callback;
goa_players_callback = players_callback;
//init_game();
strncpy(gamedata.players[0].pname, PlayerName, sizeof(gamedata.players[0].pname));
strncpy(gamedata.gamename, GameName, sizeof(gamedata.gamename));
//strncpy(gamedata.gametype, Environment.applicationName, sizeof(gamedata.gametype));
FixSZString(gamedata.players[0].pname);
FixSZString(gamedata.gamename);
//FixSZString(gamedata.gametype);
//strncpy(gamedata.mapname, "map", sizeof(gamedata.mapname));
//strncpy(gamedata.hostname, GameName, sizeof(gamedata.hostname));
gamedata.numplayers = 1;
gamedata.maxplayers = MaxPlayers;
gamedata.guid = guidInstance;
gamedata.flags = dwFlags;
//call goa_init with the query port number and gamename, default IP address, and no user data
if (goa_init(GetQueryPort(), GAMESPY_MASTER_ENGINENAME, "localhost", NULL) != 0)
{
return false;
}
bGameSpy = true;
return true;
}
//========================================================================
//========================================================================
void GameSpyOld::SetKey(const char *szKey, const char *szValue)
{
if (!strcmp(szKey, GAMELIST_KEY_MAP_TYPE))
{
strcpy(gamedata.mapname, szValue);
}
if (!strcmp(szKey, GAMELIST_KEY_GAME_TYPE))
{
strcpy(gamedata.gametype, szValue);
}
goa_send_statechanged();
}
//========================================================================
//========================================================================
void GameSpyOld::EndAdvertisingOnGameSpy(void)
{
if (bGameSpy)
{
goa_send_statechanged();
goa_shutdown();
bGameSpy = false;
}
return;
}
//========================================================================
//========================================================================
bool GameSpyOld::AddPlayer(int id, const char *szName, const char *szTeam, int reserved)
{
if (gamedata.numplayers == MAX_PLAYERS)
return false;
if (szName)
{
strncpy(gamedata.players[gamedata.numplayers].pname, szName, sizeof(gamedata.players[gamedata.numplayers].pname));
gamedata.players[gamedata.numplayers].pname[sizeof(gamedata.players[gamedata.numplayers].pname)-1]=0;
}
else
gamedata.players[gamedata.numplayers].pname[0]=0;
if (szTeam)
{
strncpy(gamedata.players[gamedata.numplayers].pteam, szTeam, sizeof(gamedata.players[gamedata.numplayers].pteam));
gamedata.players[gamedata.numplayers].pteam[sizeof(gamedata.players[gamedata.numplayers].pteam)-1]=0;
}
else
gamedata.players[gamedata.numplayers].pteam[0]=0;
gamedata.players[gamedata.numplayers].id = id;
gamedata.numplayers++;
goa_send_statechanged();
return true;
}
//========================================================================
//========================================================================
bool GameSpyOld::RemovePlayer(int id, const char *szName, const char *szTeam, int reserved)
{
int i;
for (i=0 ; i<gamedata.numplayers ; i++)
{
if (gamedata.players[gamedata.numplayers].id == id)
break;
}
if (i==gamedata.numplayers)
return false;
strcpy(gamedata.players[i].pname, gamedata.players[gamedata.numplayers].pname);
strcpy(gamedata.players[i].pteam, gamedata.players[gamedata.numplayers].pteam);
gamedata.players[i].id = gamedata.players[gamedata.numplayers].id;
gamedata.numplayers--;
goa_send_statechanged();
return true;
}
//========================================================================
//========================================================================
extern bool UpdateGameSpyGameList(void);
bool GameSpyOld::ProcessGameSpy(void)
{
if (bGameSpy)
{
goa_process_queries();
}
return true;
}
#endif
@@ -0,0 +1,659 @@
#include "AdeptHeaders.hpp"
#include "GameSpy.h"
#define MAX_SERVERS_PER_MACHINE 100
#ifndef NEW_GAMESPY
/******
goautil.c
GameSpy Developer SDK
Copyright 1999 GameSpy Industries, Inc
Suite E-204
2900 Bristol Street
Costa Mesa, CA 92626
(714)549-7689
Fax(714)549-0757
******
Please see the GameSpy Developer SDK documentation for more
information
Updated 6/9/99 - DDW
Added get_sockaddrin function, and use for resolving
Made portable with additions from CEngine code
Double check that we don't bind to localhost (instead use INADDR_ANY)
Updated 9/1/99 - DDW
Add the ability to pass in 0 as the queryport, to allocate it
automatically.
Updated 10/11/99 - DDW
TCP Heartbeat support (#define TCP_HEARTBEATS)
Updated 11/20/99 - BGW
Split goa_process_queries into two functions: goa_check_queries() and
goa_check_send_heartbeat().
Updated 3/19/00 - DDW
Added Dreamcast CE Support
******/
/********
INCLUDES
********/
#include "nonport.h"
#include "goautil.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#ifndef UNDER_CE
#include <assert.h>
#else
#define assert(a)
#endif
#ifdef __cplusplus
extern "C" {
#endif
/********
DEFINES
********/
#define HB_TIME 300000 /* 5 minutes */
#define MAX_DATA_SIZE 1400
#define INBUF_LEN 256
#define BUF_SIZE 1400
/*****
TCP_HEARTBEATS
Define this to use reliable heartbeats. Only needed for certain
classes of peer-to-peer games, please contact us if you are unsure
whether you need this or not.
******/
//#define TCP_HEARTBEATS
/********
GLOBAL VARS
********/
goa_querycallback_t goa_basic_callback = NULL;
goa_querycallback_t goa_info_callback = NULL;
goa_querycallback_t goa_rules_callback = NULL;
goa_querycallback_t goa_players_callback = NULL;
char goa_secret_key[256];
/********
TYPEDEFS
********/
typedef unsigned char uchar;
typedef enum {qtunknown, qtbasic, qtinfo, qtrules, qtplayers, qtstatus, qtpackets, qtecho, qtsecure} query_t;
/********
VARS
********/
static const char *queries[]={"","basic","info","rules","players","status","packets", "echo", "secure"};
static unsigned long lastheartbeat = 0;
static int queryid = 1;
static int packetnumber;
static int qport;
static void *udata = NULL;
static char gname[24];
static SOCKET querysock = INVALID_SOCKET;
static SOCKET hbsock = INVALID_SOCKET;
/********
PROTOTYPES
********/
static void send_heartbeat(int statechanged);
static void parse_query(char *query, struct sockaddr *sender);
static int get_sockaddrin(char *host, int port, struct sockaddr_in *saddr, struct hostent **savehent);
static int do_connect(SOCKET sock, char *addr, int port);
/****************************************************************************/
/* PUBLIC FUNCTIONS */
/****************************************************************************/
/* goa_init: Initializes the sockets, etc. Returns an error value
if an error occured, or 0 otherwise */
int goa_init(int queryport, char *gamename, char *ip, void *userdata)
{
int lasterror;
struct sockaddr_in saddr;
int saddrlen;
//save off query port,gamename and userdata
qport = queryport;
strncpy(gname, gamename, 23);
udata = userdata;
//create our sockets
SocketStartUp();
do
{
querysock = socket(PF_INET, SOCK_DGRAM, 0);
#ifdef TCP_HEARTBEATS
hbsock = socket(PF_INET, SOCK_STREAM, 0);
#else
hbsock = socket(PF_INET, SOCK_DGRAM, 0);
#endif
if (INVALID_SOCKET == querysock || INVALID_SOCKET == hbsock)
{
return E_GOA_WSOCKERROR;
}
get_sockaddrin(ip,qport,&saddr,NULL);
if (saddr.sin_addr.s_addr == htonl(0x7F000001)) //localhost -- we don't want that!
saddr.sin_addr.s_addr = INADDR_ANY;
lasterror = bind(querysock, (struct sockaddr *)&saddr, sizeof(saddr));
if (lasterror == 0)
break;
//shutdown(querysock, SO_BOTH);
//shutdown(hbsock, SO_BOTH);
closesocket(querysock);
closesocket(hbsock);
qport++;
} while (qport < (queryport + MAX_SERVERS_PER_MACHINE));
if (lasterror)
return E_GOA_BINDERROR;
if (qport == 0) //we need to allocate it ourselves
{
saddrlen = sizeof(saddr);
lasterror = getsockname(querysock,(struct sockaddr *)&saddr, &saddrlen);
if (lasterror)
return E_GOA_BINDERROR;
qport = ntohs(saddr.sin_port);
}
int nGameSpyPort;
GameSpyOld::GetGameSpyAddress(GameSpyOld::szGameSpyAddress, 256);
nGameSpyPort = GameSpyOld::GetHeartbeatPort();
return do_connect(hbsock, GameSpyOld::szGameSpyAddress, nGameSpyPort);
}
/* goa_process_queries: Processes any waiting queries, and sends a
heartbeat if 5 minutes have elapsed */
void goa_process_queries(void)
{
goa_check_send_heartbeat();
goa_check_queries();
}
/* check_queries: Processes any waiting queries */
void goa_check_queries(void)
{
static char indata[INBUF_LEN]; //256 byte input buffer
struct sockaddr saddr;
int saddrlen = sizeof(struct sockaddr);
fd_set set;
struct timeval timeout = {0,0};
int error;
if (INVALID_SOCKET == querysock)
return; //no sockets to work with!
FD_ZERO ( &set );
FD_SET ( querysock, &set );
while (true)
{
error = select(FD_SETSIZE, &set, NULL, NULL, &timeout);
if (SOCKET_ERROR == error || 0 == error)
return;
//else we have data
error = recvfrom(querysock, indata, INBUF_LEN - 1, 0, &saddr, &saddrlen);
if (error != SOCKET_ERROR)
{
indata[error] = '\0';
parse_query(indata, &saddr);
}
}
}
/* check_send_heartbeat: Perform any scheduled outgoing
heartbeats, (every 5 minutes) */
void goa_check_send_heartbeat(void)
{
unsigned int tc = current_time();
if (INVALID_SOCKET == hbsock)
return; //no sockets to work with!
//check if we need to send a heartbet
if (tc - lastheartbeat > HB_TIME)
send_heartbeat(0);
}
/* goa_send_statechanged: Sends a statechanged heartbeat, call when
your gamemode changes */
void goa_send_statechanged(void)
{
send_heartbeat(1);
}
/* goa_shutdown: Cleans up the sockets and shuts down */
void goa_shutdown(void)
{
if (INVALID_SOCKET != querysock)
{
closesocket(querysock);
querysock = INVALID_SOCKET;
}
if (INVALID_SOCKET != hbsock)
{
closesocket(hbsock);
hbsock = INVALID_SOCKET;
}
lastheartbeat = 0;
SocketShutDown();
}
/****************************************************************************/
static int do_connect(SOCKET sock, char *addr, int port)
{
struct sockaddr_in saddr;
if (!get_sockaddrin(addr, port,&saddr,NULL))
return E_GOA_CONNERROR;
if (connect (sock, (struct sockaddr *) &saddr, sizeof(saddr)) == SOCKET_ERROR)
{
return E_GOA_CONNERROR;
}
return 0;
}
/* 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(char *host, int port, struct sockaddr_in *saddr, struct hostent **savehent)
{
struct hostent *hent=NULL;
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 && strcmp(host,"255.255.255.255") != 0)
{
hent = gethostbyname(host);
if (!hent)
return 0;
saddr->sin_addr.s_addr = *(u_long *)hent->h_addr_list[0];
}
if (savehent != NULL)
*savehent = hent;
return 1;
}
/* 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];
}
/*****************************************************************************/
/* Various encryption / encoding routines */
#ifndef _GUTIL
#define _GUTIL
static void swap_byte ( uchar *a, uchar *b )
{
uchar swapByte;
swapByte = *a;
*a = *b;
*b = swapByte;
}
static uchar encode_ct ( uchar c )
{
if (c < 26) return (uchar)('A'+c);
if (c < 52) return (uchar)('a'+c-26);
if (c < 62) return (uchar)('0'+c-52);
if (c == 62) return (uchar)('+');
if (c == 63) return (uchar)('/');
return 0;
}
void gs_encode ( uchar *ins, int size, uchar *result )
{
int i,pos;
uchar trip[3];
uchar kwart[4];
i=0;
while (i < size)
{
for (pos=0 ; pos <= 2 ; pos++, i++)
if (i < size) trip[pos] = *ins++;
else trip[pos] = '\0';
kwart[0] = (uchar)( (trip[0]) >> 2);
kwart[1] = (uchar)((((trip[0]) & 3) << 4) + ((trip[1]) >> 4));
kwart[2] = (uchar)((((trip[1]) & 15) << 2) + ((trip[2]) >> 6));
kwart[3] = (uchar)( (trip[2]) & 63);
for (pos=0; pos <= 3; pos++) *result++ = encode_ct(kwart[pos]);
}
*result='\0';
}
void gs_encrypt ( uchar *key, int key_len, uchar *buffer_ptr, int buffer_len )
{
short counter;
uchar x, y, xorIndex;
uchar state[256];
for ( counter = 0; counter < 256; counter++) state[counter] = (uchar) counter;
x = 0; y = 0;
for ( counter = 0; counter < 256; counter++)
{
y = (uchar)((key[x] + state[counter] + y) % 256);
x = (uchar)((x + 1) % key_len);
swap_byte ( &state[counter], &state[y] );
}
x = 0; y = 0;
for ( counter = 0; counter < buffer_len; counter ++)
{
x = (uchar)((x + buffer_ptr[counter] + 1) % 256);
y = (uchar)((state[x] + y) % 256);
swap_byte ( &state[x], &state[y] );
xorIndex = (uchar)((state[x] + state[y]) % 256);
buffer_ptr[counter] ^= state[xorIndex];
}
}
/*****************************************************************************/
#endif
/* packet_send: sends a key\value packet. Appends the queryid
key\value pair. Clears the buffer */
static void packet_send(struct sockaddr *addr, char *buffer)
{
char keyvalue[80];
if (strlen(buffer) == 0)
return; //dont need to send an empty one!
packetnumber++; //packet numbers start at 1
sprintf(keyvalue,"\\queryid\\%d.%d",queryid, packetnumber);
strcat(buffer,keyvalue);
#ifdef _DEBUG
OutputDebugString("\n");
OutputDebugString(buffer);
OutputDebugString("\n");
#endif
sendto(querysock, buffer, strlen(buffer), 0, addr, sizeof(struct sockaddr));
buffer[0]='\0';
}
/* buffer_send: appends buffer with newdata. If the combined
size would be too large, it flushes buffer first. Space is reserved
on the total size to allow for the queryid key\value */
static void buffer_send(struct sockaddr *sender, char *buffer, char *newdata)
{
char *pos, *lastkey;
int bcount = 0;
if (strlen(buffer) + strlen(newdata) < MAX_DATA_SIZE - 50)
{
strcat(buffer, newdata);
} else
{//test this!
if (strlen(newdata) > MAX_DATA_SIZE - 50) //incoming data is too big already!
{
lastkey = pos = newdata;
while (pos - newdata < MAX_DATA_SIZE-50)
{
if ('\\' == *pos)
{
if (bcount % 2 == 0)
lastkey = pos;
bcount++;
}
pos++;
}
if (lastkey == newdata)
return; //endless loop - single key was too big!
*lastkey = '\0';
buffer_send(sender, buffer, newdata);
*lastkey = '\\';
buffer_send(sender, buffer, lastkey); //send the rest!
} else
{
packet_send(sender, buffer);
strcpy(buffer,newdata);
}
}
}
/* send_basic: sends a response to the basic query */
static void send_basic(struct sockaddr *sender, char *outbuf)
{
char keyvalue[BUF_SIZE] = "";
assert(goa_basic_callback);
goa_basic_callback(keyvalue, sizeof(keyvalue), udata);
buffer_send(sender, outbuf, keyvalue);
}
/* send_info: sends a response to the info query */
static void send_info(struct sockaddr *sender, char *outbuf)
{
char keyvalue[BUF_SIZE] = "";
assert(goa_info_callback);
goa_info_callback(keyvalue, sizeof(keyvalue), udata);
buffer_send(sender, outbuf, keyvalue);
}
/* send_rules: sends a response to the rules query. */
static void send_rules(struct sockaddr *sender, char *outbuf)
{
char keyvalue[BUF_SIZE] = "";
assert(goa_rules_callback);
goa_rules_callback(keyvalue, sizeof(keyvalue), udata);
buffer_send(sender, outbuf, keyvalue);
}
/* send_players: sends the players and their information.*/
static void send_players(struct sockaddr *sender, char *outbuf)
{
char keyvalue[BUF_SIZE] = "";
assert(goa_players_callback);
goa_players_callback(keyvalue, sizeof(keyvalue), udata);
buffer_send(sender, outbuf, keyvalue);
}
/* send_echo: bounces the echostr back to sender
Note: you should always make sure that your echostr doesn't exceed the MAX_DATA_SIZE*/
static void send_echo(struct sockaddr *sender, char *outbuf,char *echostr)
{
char keyvalue[MAX_DATA_SIZE];
if (strlen(echostr) > MAX_DATA_SIZE - 50)
return;
sprintf(keyvalue,"\\echo\\%s",echostr);
buffer_send(sender, outbuf, keyvalue);
}
/* send_final: sends the remaining data in outbuf. Appends the final
key\value to the end. Also adds validation if required. */
static void send_final(struct sockaddr *sender, char *outbuf,char *validation)
{
char keyvalue[256];
char encrypted_val[128]; //don't need to null terminate
char encoded_val[200];
int keylen;
if (validation[0])
{
keylen = strlen(validation);
if (keylen > 128) return;
strcpy(encrypted_val, validation);
gs_encrypt((uchar *)goa_secret_key, strlen(goa_secret_key), (uchar *)encrypted_val, keylen);
gs_encode((uchar *)encrypted_val,keylen, (uchar *)encoded_val);
sprintf(keyvalue,"\\validate\\%s",encoded_val);
buffer_send(sender, outbuf, keyvalue);
}
sprintf(keyvalue,"\\final\\");
buffer_send(sender, outbuf, keyvalue);
packet_send(sender, outbuf);
}
/* parse_query: parse an incoming query (which may contain 1 or more
individual queries) and reply to each query */
static void parse_query(char *query, struct sockaddr *sender)
{
query_t querytype;
char buffer[MAX_DATA_SIZE]="";
char *value;
char validation[256] = "";
queryid++;
packetnumber = 0;
for (querytype = qtbasic; querytype <= qtsecure ; (*(int *)(&querytype))++)
{
if ((value = value_for_key(query, queries[querytype])) != 0)
switch (querytype)
{
case qtbasic:
send_basic(sender,buffer);
break;
case qtinfo:
send_info(sender,buffer);
break;
case qtrules:
send_rules(sender,buffer);
break;
case qtplayers:
send_players(sender,buffer);
break;
case qtstatus:
send_basic(sender,buffer);
send_info(sender,buffer);
send_rules(sender,buffer);
send_players(sender,buffer);
break;
case qtpackets:
/*note: "packets" is NOT a real query type. It is simply here to illustrate
how a large query would look if broken into packets */
send_basic(sender,buffer); packet_send(sender, buffer);
send_info(sender,buffer); packet_send(sender, buffer);
send_rules(sender,buffer); packet_send(sender, buffer);
send_players(sender,buffer);
break;
case qtecho:
//note: \echo\value is the syntax here
send_echo(sender,buffer,value);
break;
case qtsecure:
strcpy(validation, value);
break;
case qtunknown:
break;
}
}
send_final(sender,buffer,validation);
}
/* send_heartbeat: Sends a heartbeat to the gamemaster,
adds \statechanged\ if statechanged != 0 */
static void send_heartbeat(int statechanged)
{
char buf[256];
int ret;
sprintf(buf,"\\heartbeat\\%d\\gamename\\%s",qport, gname);
if (statechanged)
strcat(buf,"\\statechanged\\");
#ifdef _DEBUG
OutputDebugString("send: ");
OutputDebugString(buf);
OutputDebugString("\n");
#endif
ret = send(hbsock, buf, strlen(buf), 0);
if (ret < 0) /* try to reconnect (tcp only) */
{
closesocket(hbsock);
hbsock = socket(PF_INET, SOCK_STREAM, 0);
int nGameSpyPort;
GameSpyOld::GetGameSpyAddress(GameSpyOld::szGameSpyAddress, 256);
nGameSpyPort = GameSpyOld::GetHeartbeatPort();
if (do_connect(hbsock, GameSpyOld::szGameSpyAddress, nGameSpyPort) == 0)
{
#ifdef _DEBUG
OutputDebugString("send: ");
OutputDebugString(buf);
OutputDebugString("\n");
#endif
send(hbsock, buf, strlen(buf), 0); /* try again */
}
}
lastheartbeat = current_time();
}
#ifdef __cplusplus
}
#endif
#endif
@@ -0,0 +1,161 @@
/******
goautil.h
GameSpy Developer SDK
Copyright 1999 GameSpy Industries, Inc
Suite E-204
2900 Bristol Street
Costa Mesa, CA 92626
(714)549-7689
Fax(714)549-0757
******
Please see the GameSpy Developer SDK documentation for more
information
******/
#ifndef _GOAUTIL_H_
#define _GOAUTIL_H_
#ifdef __cplusplus
extern "C" {
#endif
/********
ERROR CONSTANTS
---------------
These constants are returned from goa_init to signal an error condition
***************/
#define E_GOA_WSOCKERROR 1
#define E_GOA_BINDERROR 2
#define E_GOA_DNSERROR 3
#define E_GOA_CONNERROR 4
/********
goa_querycallback_t
-------------------
This is the prototype for the callback functions your game needs to
implement for each of the four basic query types. The callback works the
same for each query type.
[outbuf] is a pre-allocated buffer for you to place the query reply. It's size is
[maxlen] (default is 1400). If you need larger, you can adjust the
defines in goautil.c
[userdata] is the pointer that was passed into goa_init. You can use this for an
object or structure pointer if needed.
Simply fill outbuf with the correct data for the query type (consult the sample
apps and the GameSpy Developer Spec).
outbuf should be a NULL terminated ANSI string.
********/
typedef void (*goa_querycallback_t)(char *outbuf, int maxlen, void *userdata);
/********
GLOBAL CALLBACK VARS
---------------------
Set these callbacks to your implementation of each callback function.
You can set these before or after calling goa_init, but they must be set
prior to the first call to goa_process_queries.
********/
extern goa_querycallback_t goa_basic_callback;
extern goa_querycallback_t goa_info_callback;
extern goa_querycallback_t goa_rules_callback;
extern goa_querycallback_t goa_players_callback;
/*********
SECRET KEY
----------
Initialize this string to the secret key to use for validation.
Make sure you null-terminate the key.
You can set the key before or after calling goa_init, but it must be
set prior to the first call to goa_process_queries.
*********/
extern char goa_secret_key[256];
/************
GOA_INIT
--------
This creates/binds the sockets needed for heartbeats and queries/replies.
[queryport] is the port you want to accept queries on. Only 1 server per
machine can use this port, so you need a way to specify alternate ports if
running more than 1 server.Optionally, you can pass in 0 to have a port
chosen automatically (makes it harder for debugging/testing).
[gamename] is the unique gamename that you were given
[ip] is an optional parameter that determines which dotted IP address to bind to on
a multi-homed machine. You can pass NULL to bind to all IP addresses.
[userdata] is an optional, implementation specific parameter that will be
passed to all callback functions. Use it to store an object or structure
pointer if needed.
Returns
0 is successful, otherwise one of the E_GOA constants above.
E_GOA_BINDERROR usually means that the queryport is already in use.
************/
int goa_init(int queryport, char *gamename, char *ip, void *userdata);
/*******************
GOA_PROCESS_QUERIES
-------------------
This function should be called somewhere in your main program loop to
process any pending server queries and send a heartbeat if 5 minutes has
elapsed.
Query replies are very latency sensative, so you should make sure this
function is called at least every 100ms while your game is in progress.
The function has very low overhead and should not cause any performance
problems.
********************/
void goa_process_queries(void);
/*******************
GOA_CHECK_QUERIES
-------------------
This funtion processes any pending server queries. This is normally
called from goa_process_queries(), which is the function you should call
from somewhere in your main program loop.
********************/
void goa_check_queries(void);
/*******************
GOA_CHECK_SEND_HEARTBEAT
-------------------
This funtion sends a heartbeat if 5 minutes has elapsed. It is normally
called from goa_process_queries(), which should be called from your main
program loop.
********************/
void goa_check_send_heartbeat(void);
/*****************
GOA_SEND_STATECHANGED
--------------------
This function forces a \statechanged\ heartbeat to be sent immediately.
Use it any time you have changed the gamestate of your game to signal the
master to update your status.
Also use it before your game exits by changing the gamestate to "exiting"
and sending a statechanged heartbeat.
*******************/
void goa_send_statechanged(void);
/*****************
GOA_SHUTDOWN
------------
This function closes the sockets created in goa_init and takes care of
any misc. cleanup. You should try to call it when before exiting the server
if goa_init was called.
******************/
void goa_shutdown(void);
#ifdef __cplusplus
}
#endif
#endif
@@ -0,0 +1,291 @@
#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 <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <stdio.h>
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);
}
@@ -0,0 +1,59 @@
/******
gserver.h
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
******
Please see the GameSpy C Engine SDK documentation for more
information
******/
#include "goaceng.h"
#ifndef _GSERVER_H_
#define _GSERVER_H_
#ifdef __cplusplus
extern "C" {
#endif
#include "hashtable.h"
struct GServerImplementation
{
char ip[16];
int port;
int ping;
HashTable keyvals;
};
typedef struct
{
char *key, *value;
} GKeyValuePair;
typedef struct
{
KeyEnumFn EnumFn;
void *instance;
} GEnumData;
void _cdecl ServerFree(void *elem);
GServer ServerNew(char *ip, int port);
void ServerParseKeyVals(GServer server, char *keyvals);
#ifdef __cplusplus
}
#endif
#endif
@@ -0,0 +1,852 @@
#include "AdeptHeaders.hpp"
#include "GameSpy.h"
#include "buildnum\buildnum.h"
/******
gserverlist.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
******
Please see the GameSpy C Engine SDK documentation for more
information
******/
#include "goaceng.h"
#include "gserver.h"
#include "nonport.h"
#include "darray.h"
#include "gutil.h"
#ifndef UNDER_CE
#include <assert.h>
#else
#define assert(a)
#endif
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <mbstring.h>
#define __mbschr(s,c) (char*)_mbschr((const unsigned char*)(s),(c))
#define SERVER_GROWBY 64
#define LAN_SEARCH_TIME 3000 //3 sec
#ifdef __cplusplus
extern "C" {
#endif
//todo: check state changes on error
typedef struct
{
SOCKET s;
GServer currentserver;
unsigned long starttime;
} UpdateInfo;
struct GServerListImplementation
{
GServerListState state;
DArray servers;
UpdateInfo *updatelist; //dynamic array of updateinfos
char gamename[32];
char seckey[32];
char enginename[32];
int maxupdates;
int nextupdate;
int abortupdate;
ListCallBackFn CallBackFn;
void *instance;
char *sortkey;
gbool sortascending;
SOCKET slsocket;
unsigned long lanstarttime;
};
GServerList g_sortserverlist; //global serverlist for sorting info!!
bool IsServerListIdle(GServerList sl)
{
return (sl->state == sl_idle);
}
/* ServerListNew
----------------
Creates and returns a new (empty) GServerList. */
GServerList ServerListNew(char *gamename, char *enginename, char *seckey, int maxconcupdates, void *CallBackFn, int CallBackFnType, void *instance)
{
GServerList list;
list = (GServerList) malloc(sizeof(struct GServerListImplementation));
assert(list != NULL);
list->state = sl_idle;
list->servers = ArrayNew(sizeof(GServer), SERVER_GROWBY, ServerFree);
list->maxupdates = maxconcupdates;
list->updatelist = (UpdateInfo *)malloc(maxconcupdates * sizeof(UpdateInfo));
memset(list->updatelist, 0, maxconcupdates * sizeof(UpdateInfo));
assert(list->updatelist != NULL);
strcpy(list->gamename, gamename);
strcpy(list->seckey, seckey);
strcpy(list->enginename, enginename);
list->CallBackFn = (void (__cdecl *)(struct GServerListImplementation *,int,void *,void *,void *))CallBackFn;
assert(CallBackFn != NULL);
list->instance = instance;
list->sortkey = "";
SocketStartUp();
return list;
}
/* ServerListFree
-----------------
Free a GServerList and all internal sturctures and servers */
void ServerListFree(GServerList serverlist)
{
ArrayFree(serverlist->servers);
free(serverlist->updatelist);
free(serverlist);
SocketShutDown();
}
//create update sockets and init structures
static GError InitUpdateList(GServerList serverlist)
{
int i;
for (i = 0 ; i < serverlist->maxupdates ; i++)
{
serverlist->updatelist[i].s = socket(AF_INET, SOCK_DGRAM,IPPROTO_UDP);
if (serverlist->updatelist[i].s == INVALID_SOCKET)
return GE_NOSOCKET;
serverlist->updatelist[i].currentserver = NULL;
serverlist->updatelist[i].starttime = 0;
}
return 0;
}
//free update sockets
static GError FreeUpdateList(GServerList serverlist)
{
int i;
for (i = 0 ; i < serverlist->maxupdates ; i++)
{
closesocket(serverlist->updatelist[i].s);
}
return 0;
}
//create and connect a server list socket
static GError CreateServerListSocket(GServerList serverlist, char *szMasterServer, int iServerListPort)
{
struct sockaddr_in saddr;
struct hostent *hent;
saddr.sin_family = AF_INET;
saddr.sin_port = htons((unsigned short)(iServerListPort));
saddr.sin_addr.s_addr = inet_addr(szMasterServer);
if (saddr.sin_addr.s_addr == INADDR_NONE)
{
hent = gethostbyname(szMasterServer);
if (!hent)
return GE_NODNS;
saddr.sin_addr.s_addr = *(u_long *)hent->h_addr_list[0];
}
memset ( saddr.sin_zero, 0, 8 );
serverlist->slsocket = socket ( AF_INET, SOCK_STREAM, IPPROTO_TCP );
if (serverlist->slsocket == INVALID_SOCKET)
return GE_NOSOCKET;
if (connect ( serverlist->slsocket, (struct sockaddr *) &saddr, sizeof saddr ) != 0)
{
WSAGetLastError();
closesocket(serverlist->slsocket);
return GE_NOCONNECT;
}
//else we are connected
return 0;
}
//create and connect a server list socket
static GError CreateServerListLANSocket(GServerList serverlist)
{
int optval = 1;
serverlist->slsocket = socket ( AF_INET, SOCK_DGRAM, IPPROTO_UDP );
if (serverlist->slsocket == INVALID_SOCKET)
return GE_NOSOCKET;
if (setsockopt(serverlist->slsocket, SOL_SOCKET, SO_BROADCAST, (char *)&optval, sizeof(optval)) != 0)
return GE_NOSOCKET;
//else we are ready to broadcast
return 0;
}
//trigger the callback and set the new mode
static void ServerListModeChange(GServerList serverlist, GServerListState newstate)
{
serverlist->state = newstate;
serverlist->CallBackFn(serverlist, LIST_STATECHANGED, serverlist->instance, NULL, NULL);
}
// validate us to the master and send a list request
#define SECURE "\\secure\\"
static GError SendListRequest(GServerList serverlist)
{
char data[256], *ptr, result[64];
int len;
len = recv(serverlist->slsocket, data, sizeof(data) - 1, 0);
if (len == SOCKET_ERROR)
return GE_NOCONNECT;
data[len] = '\0'; //null terminate it
ptr = strstr ( data, SECURE );
if (!ptr)
return GE_DATAERROR;
ptr = ptr + strlen(SECURE);
gs_encrypt ( (uchar *) serverlist->seckey, 6, (uchar *)ptr, 6 );
gs_encode ( (uchar *)ptr, 6, (uchar *) result );
//validate to the master
sprintf(data, "\\gamename\\%s\\gamever\\%s\\location\\0\\validate\\%s\\final\\\\queryid\\1.1\\",
serverlist->enginename, ENGINE_VERSION, result); //validate us
len = send ( serverlist->slsocket, data, strlen(data), 0 );
if (len == SOCKET_ERROR || len == 0)
return GE_NOCONNECT;
//send the list request
sprintf(data, "\\list\\gamename\\%s\\final\\", serverlist->gamename);
len = send ( serverlist->slsocket, data, strlen(data), 0 );
if (len == SOCKET_ERROR || len == 0)
return GE_NOCONNECT;
ServerListModeChange(serverlist, sl_listxfer);
return 0;
}
static GError SendBroadcastRequest(GServerList serverlist, int startport, int endport, int delta)
{
struct sockaddr_in saddr;
short i;
saddr.sin_family = AF_INET;
saddr.sin_addr.s_addr = 0xFFFFFFFF; //broadcast
for (i = (short)startport ; i <= (short)endport ; i = (short)(i + delta))
{
saddr.sin_port = htons(i);
sendto(serverlist->slsocket, "\\status\\",8,0,(struct sockaddr *)&saddr,sizeof(saddr));
}
ServerListModeChange(serverlist, sl_lanlist);
serverlist->lanstarttime = current_time();
return 0;
}
//just wait for the server list to become idle
static void DoSyncLoop(GServerList serverlist)
{
while (serverlist->state != sl_idle)
{
ServerListThink(serverlist);
msleep(10);
}
}
/* ServerListUpdate
-------------------
Start updating a GServerList. */
GError ServerListUpdate(GServerList serverlist, gbool async, char *szMasterServer, int iServerListPort)
{
GError error;
if (serverlist->state != sl_idle)
return GE_BUSY;
error = CreateServerListSocket(serverlist, szMasterServer, iServerListPort);
if (error) return error;
error = SendListRequest(serverlist);
if (error) return error;
error = InitUpdateList(serverlist);
if (error) return error;
serverlist->nextupdate = 0;
serverlist->abortupdate = 0;
if (!async)
DoSyncLoop(serverlist);
return 0;
}
/* ServerListLANUpdate
-------------------
Start updating a GServerList from servers on the LAN. */
GError ServerListLANUpdate(GServerList serverlist, gbool async, int startsearchport, int endsearchport, int searchdelta)
{
GError error;
assert(searchdelta > 0);
if (serverlist->state != sl_idle)
return GE_BUSY;
error = InitUpdateList(serverlist);
if (error) return error;
error = CreateServerListLANSocket(serverlist);
if (error) return error;
error = SendBroadcastRequest(serverlist, startsearchport, endsearchport, searchdelta);
if (error) return error;
serverlist->nextupdate = 0;
serverlist->abortupdate = 0;
if (!async)
DoSyncLoop(serverlist);
return 0;
}
//add the server to the list with the given ip, port
static void ServerListAddServer(GServerList serverlist, char *ip, int port)
{
GServer server;
server = ServerNew(ip, port);
ArrayAppend(serverlist->servers,&server);
//printf("%d %s:%d\n",++count, ip,port);
}
//add the server to the list with the given ip, port
static void ServerListInsertServer(GServerList serverlist, char *ip, int port, int pos)
{
GServer server;
server = ServerNew(ip, port);
ArrayInsertAt(serverlist->servers,&server,pos);
//printf("%d %s:%d\n",++count, ip,port);
}
//find the server in the list, returns -1 if it does not exist
static int ServerListFindServer(GServerList serverlist, char *ip, int port)
{
int i;
GServer server;
for (i = 0; i < ArrayLength(serverlist->servers) ; i++)
{
server = *(GServer *)ArrayNth(serverlist->servers,i);
if (port == ServerGetQueryPort(server) && strcmp(ip, ServerGetAddress(server)) == 0)
{
return i;
}
}
return -1;
}
//finds the server in the list of servers currently being queried
// returns -1 if it does not exist
static int ServerListFindServerInUpdateList(GServerList serverlist, GServer server)
{
int i;
for (i = 0 ; i < serverlist->maxupdates ; i++)
{
if (serverlist->updatelist[i].currentserver == server)
return i;
}
return -1;
}
/* ServerListRemoveServer
-------------------------
Removes a single server from the list. Frees the memory associated with the GServer */
void ServerListRemoveServer(GServerList serverlist, char *ip, int port)
{
int currentindex = ServerListFindServer(serverlist, ip, port);
int updateindex;
if (currentindex == -1)
return; //can't do anything, it doesn't exist
//check to see whether we need to change the updatelist or move the nextupdate
if (serverlist->state != sl_idle && serverlist->nextupdate > currentindex)
{
GServer holdserver = *(GServer *)ArrayNth(serverlist->servers,currentindex);
updateindex = ServerListFindServerInUpdateList(serverlist, holdserver);
if (updateindex != -1) //is currently being queried, stop it
serverlist->updatelist[updateindex].currentserver = NULL;
serverlist->nextupdate--; //decrement the next update, since we are removing a server
}
ArrayDeleteAt(serverlist->servers, currentindex);
}
/* ServerListUpdate
-------------------
Adds an auxilliary (non-fetched) server to the update list.
If the engine is idle, the server is added and the engine started. */
GError ServerListAuxUpdate(GServerList serverlist, char *ip, int port, gbool async)
{
GError error;
int currentindex;
int updateindex;
//first, see if the server already exists
currentindex = ServerListFindServer(serverlist,ip,port);
//if we're idle, start things up
if (serverlist->state == sl_idle)
{
//prepare as if we're going to do a normal list fetch,
//but skip the call to SendListRequest().
error = InitUpdateList(serverlist);
if (error) return error;
if (currentindex != -1) //we need to "move" this server to the end of the list
{ //move the server to the end of the array
GServer holdserver = *(GServer *)ArrayNth(serverlist->servers,currentindex);
holdserver->ping = 9999;//clear the ping so it gets recalculated
ArrayRemoveAt(serverlist->servers,currentindex);
ArrayAppend(serverlist->servers,&holdserver);
} else
{ //add the aux server
ServerListAddServer(serverlist, ip, port);
}
serverlist->nextupdate = ArrayLength(serverlist->servers) - 1;
serverlist->abortupdate = 0;
//chane the mode straight to querying
ServerListModeChange(serverlist, sl_querying);
//is it's a sync call, do it until done
if (!async)
DoSyncLoop(serverlist);
}
else
{
//if we're in the middle of an update, we should
//be able to just slip the aux server in for querying
//ServerListAddServer(serverlist, ip, port);
//crt -- make it the next server to be queried
//note: this should NEVER be called in a different thread from think!!
if (currentindex == -1) //it doesn't exist yet
ServerListInsertServer(serverlist, ip, port, serverlist->nextupdate);
else
{ //it exists, find out whats happening to it
GServer holdserver = *(GServer *)ArrayNth(serverlist->servers,currentindex);
if (currentindex >= serverlist->nextupdate) //hasn't been queried yet!
return 0; //it will be queried soon anyway
holdserver->ping = 9999;//clear the ping so it gets recalculated
updateindex = ServerListFindServerInUpdateList(serverlist, holdserver);
if (updateindex != -1) //is currently being queried, stop it
serverlist->updatelist[updateindex].currentserver = NULL;
ArrayInsertAt(serverlist->servers,&holdserver, serverlist->nextupdate); //insert at new place
ArrayRemoveAt(serverlist->servers,currentindex); //remove the old one
serverlist->nextupdate--; //decrement the next update, since we are removing a server
}
}
return 0;
}
static GError ServerListLANList(GServerList serverlist)
{
struct timeval timeout = {0,0};
fd_set set;
char indata[1500];
struct sockaddr_in saddr;
int saddrlen = sizeof(saddr);
int error;
while (1) //we break if the select fails
{
FD_ZERO(&set);
FD_SET( serverlist->slsocket, &set);
error = select(FD_SETSIZE, &set, NULL, NULL, &timeout);
if (SOCKET_ERROR == error || 0 == error) //no data
break;
error = recvfrom(serverlist->slsocket, indata, sizeof(indata) - 1, 0, (struct sockaddr *)&saddr, &saddrlen );
if (SOCKET_ERROR == error)
continue;
//we got data, add the server to the list to update
if (strstr(indata,"\\final\\") != NULL)
ServerListAddServer(serverlist, inet_ntoa(saddr.sin_addr), ntohs(saddr.sin_port));
}
if (current_time() - serverlist->lanstarttime > LAN_SEARCH_TIME) //done waiting for replies
{
closesocket(serverlist->slsocket);
serverlist->slsocket = INVALID_SOCKET;
ServerListModeChange(serverlist, sl_querying);
}
return 0;
}
//reads the server list from the socket and parses it
static GError ServerListReadList(GServerList serverlist)
{
static char data[2048]; //static input buffer
int len, oldlen;
char *p, ip[32], port[16],*q, *lastip;
//append to data
oldlen = strlen(data);
len = recv(serverlist->slsocket, data + oldlen, sizeof(data) - oldlen - 1, 0);
if (len == SOCKET_ERROR || len == 0)
{
closesocket(serverlist->slsocket);
serverlist->slsocket = INVALID_SOCKET;
data[0] = 0; //clear data so it can be used again
ServerListHalt(serverlist);
ServerListModeChange(serverlist, sl_querying);
return GE_NOCONNECT;
}
data[len + oldlen] = 0; //null terminate it
// data is in the form of '\ip\1.2.3.4:1234\ip\1.2.3.4:1234\final\'
lastip = data;
while (*lastip != '\0')
{
p = lastip;
if (*(p+1) == 'f' || serverlist->abortupdate) //\final\!!
{
closesocket(serverlist->slsocket);
serverlist->slsocket = INVALID_SOCKET;
data[0] = 0; //clear data so it can be used again
ServerListModeChange(serverlist, sl_querying);
return 0; //get out!!
}
if (strlen(p) < 14) //no way it could be a full IP, quit
break;
p += 4; //skip the '\ip\'
if (strchr(p,':') == NULL || __mbschr(p,'\\') == NULL)
break; //it's not the full ip:port
q = ip; //fill the ip buffer
while (*p != 0 && *p != ':')
*q++ = *p++;
*q = '\0'; //null terminate the ip
p++; //skip the :
q = port;
while (*p != 0 && *p != '\\')
*q++ = *p++;
*q = '\0';
lastip = p; //store the new position
ServerListAddServer(serverlist, ip, atoi(port));
}
memmove(data,lastip,strlen(lastip) + 1); //shift it over
return 0;
}
//loop through pending queries and send out new ones
#define STATUS "\\status\\"
static GError ServerListQueryLoop(GServerList serverlist)
{
int i, scount = 0, error, final;
fd_set set;
struct timeval timeout = {0,0};
char indata[1500];
struct sockaddr_in saddr;
int saddrlen = sizeof(saddr);
GServer server;
//first, check for available data
FD_ZERO(&set);
for (i = 0 ; i < serverlist->maxupdates ; i++)
{
if (serverlist->updatelist[i].currentserver != NULL) //there is a server waiting
{
scount++;
FD_SET( serverlist->updatelist[i].s, &set);
}
}
if (scount > 0) //there are sockets to check for data
{
error = select(FD_SETSIZE, &set, NULL, NULL, &timeout);
if (SOCKET_ERROR != error && 0 != error)
for (i = 0 ; i < serverlist->maxupdates ; i++)
if (serverlist->updatelist[i].currentserver != NULL && FD_ISSET(serverlist->updatelist[i].s, &set) ) //there is a server waiting
{ //we can read data!!
error = recv(serverlist->updatelist[i].s, indata, sizeof(indata) - 1, 0/*, &saddr, saddrlen*/ );
if (SOCKET_ERROR != error) //we got data
{
indata[error] = 0; //truncate and parse it
final = (strstr(indata,"\\final\\") != NULL);
server = serverlist->updatelist[i].currentserver;
if (server->ping == 9999) //set the ping
server->ping = current_time() - serverlist->updatelist[i].starttime;
ServerParseKeyVals(server, indata);
if (final) //it's all done
{
serverlist->CallBackFn(serverlist,
LIST_PROGRESS,
serverlist->instance,
server,
(void *)((serverlist->nextupdate * 100) / ArrayLength(serverlist->servers))); //percent done
serverlist->updatelist[i].currentserver = NULL; //reuse the updatelist
}
} else
serverlist->updatelist[i].currentserver = NULL; //reuse the updatelist
}
}
//kill expired ones
for (i = 0 ; i < serverlist->maxupdates ; i++)
{
if (serverlist->updatelist[i].currentserver != NULL && current_time() - serverlist->updatelist[i].starttime > SERVER_TIMEOUT )
{
/* serverlist->CallBackFn(serverlist, //do we want to notify of dead servers? if so, uncomment!
LIST_PROGRESS,
serverlist->instance,
*(GServer *)serverlist->updatelist[i].currentserver,
(void *)((serverlist->nextupdate * 100) / ArrayLength(serverlist->servers))); //percent done
*/
serverlist->updatelist[i].currentserver = NULL; //reuse the updatelist
}
}
if (serverlist->abortupdate || (serverlist->nextupdate >= ArrayLength(serverlist->servers) && scount == 0))
{ //we are done!!
FreeUpdateList(serverlist);
ServerListModeChange(serverlist, sl_idle);
return 0;
}
//now, send out queries on available sockets
for (i = 0 ; i < serverlist->maxupdates && serverlist->nextupdate < ArrayLength(serverlist->servers) ; i++)
{
if (serverlist->updatelist[i].currentserver == NULL) //it's availalbe
{
server = *(GServer *)ArrayNth(serverlist->servers,serverlist->nextupdate);
serverlist->nextupdate++;
serverlist->updatelist[i].currentserver = server;
saddr.sin_family = AF_INET;
#ifdef UNDER_CE
closesocket(serverlist->updatelist[i].s);
serverlist->updatelist[i].s = socket(AF_INET,SOCK_DGRAM,IPPROTO_UDP);
#endif
saddr.sin_addr.s_addr = inet_addr(ServerGetAddress(server));
saddr.sin_port = htons((short)ServerGetQueryPort(server));
error = connect (serverlist->updatelist[i].s, (struct sockaddr *) &saddr, saddrlen);
if (error != 0) //uhh.. bad server address?
{
serverlist->updatelist[i].currentserver = NULL;
continue;
}
send(serverlist->updatelist[i].s,STATUS,strlen(STATUS),0);
serverlist->updatelist[i].starttime = current_time();
}
}
return 0;
}
/* ServerListThink
------------------
For use with Async Updates. This needs to be called every ~10ms for list processing and
updating to occur during async server list updates */
GError ServerListThink(GServerList serverlist)
{
switch (serverlist->state)
{
case sl_idle: return 0;
case sl_listxfer:
//read the data
return ServerListReadList(serverlist);
break;
case sl_lanlist:
return ServerListLANList(serverlist);
case sl_querying:
//do some queries
return ServerListQueryLoop(serverlist);
break;
}
return 0;
}
/* ServerListHalt
-----------------
Halts the current update batch */
GError ServerListHalt(GServerList serverlist)
{
if (serverlist->state != sl_idle)
serverlist->abortupdate = 1;
return 0;
}
/* ServerListClear
------------------
Clear and free all of the servers from the server list.
List must be in the sl_idle state */
GError ServerListClear(GServerList serverlist)
{
if (serverlist->state != sl_idle)
return GE_BUSY;
//fastest way to clear is kill and recreate
ArrayFree(serverlist->servers);
serverlist->servers = ArrayNew(sizeof(GServer), SERVER_GROWBY, ServerFree);
return 0;
}
/* ServerListState
------------------
Returns the current state of the server list */
GServerListState ServerListState(GServerList serverlist)
{
return serverlist->state;
}
/* ServerListErrorDesc
----------------------
Returns a static string description of the specified error */
char *ServerListErrorDesc(GServerList serverlist, GError error)
{
switch (error)
{
case GE_NOERROR: return "";
case GE_NOSOCKET: return "Unable to create socket";
case GE_NODNS: return "Unable to resolve master";
case GE_NOCONNECT: return "Connection to master reset";
case GE_BUSY: return "Server List is busy";
case GE_DATAERROR: return "Unexpected data in server list";
}
return "UNKNOWN ERROR CODE";
}
/* ServerListGetServer
----------------------
Returns the server at the specified index, or NULL if the index is out of bounds */
GServer ServerListGetServer(GServerList serverlist, int index)
{
if (index < 0 || index >= ArrayLength(serverlist->servers))
return NULL;
return *(GServer *)ArrayNth(serverlist->servers,index);
}
/* ServerListCount
------------------
Returns the number of servers on the specified list. Indexing is 0 based, so
the actual server indexes are 0 <= valid index < Count */
int ServerListCount(GServerList serverlist)
{
return ArrayLength(serverlist->servers);
}
/****
Comparision Functions
***/
static int _cdecl IntKeyCompare(const void *entry1, const void *entry2)
{
GServer server1 = *(GServer *)entry1, server2 = *(GServer *)entry2;
int diff;
diff = ServerGetIntValue(server1, g_sortserverlist->sortkey, 0) -
ServerGetIntValue(server2, g_sortserverlist->sortkey, 0);
if (!g_sortserverlist->sortascending)
diff = -diff;
return diff;
}
static int _cdecl FloatKeyCompare(const void *entry1, const void *entry2)
{
GServer server1 = *(GServer *)entry1, server2 = *(GServer *)entry2;
double f = ServerGetFloatValue(server1, g_sortserverlist->sortkey, 0) -
ServerGetFloatValue(server2, g_sortserverlist->sortkey, 0);
if (!g_sortserverlist->sortascending)
f = -f;
if ((float)f > (float)0.0)
return 1;
else if ((float)f < (float)0.0)
return -1;
else
return 0;
}
static int _cdecl StrCaseKeyCompare(const void *entry1, const void *entry2)
{
GServer server1 = *(GServer *)entry1, server2 = *(GServer *)entry2;
int diff = strcmp(ServerGetStringValue(server1, g_sortserverlist->sortkey, ""),
ServerGetStringValue(server2, g_sortserverlist->sortkey, ""));
if (!g_sortserverlist->sortascending)
diff = -diff;
return diff;
}
static int _cdecl StrNoCaseKeyCompare(const void *entry1, const void *entry2)
{
GServer server1 = *(GServer *)entry1, server2 = *(GServer *)entry2;
int diff = strcasecmp(ServerGetStringValue(server1, g_sortserverlist->sortkey, ""),
ServerGetStringValue(server2, g_sortserverlist->sortkey, ""));
if (!g_sortserverlist->sortascending)
diff = -diff;
return diff;
}
/* ServerListSort
-----------------
Sort the server list in either ascending or descending order using the
specified comparemode.
sortkey can be a normal server key, or "ping" or "hostaddr" */
void ServerListSort(GServerList serverlist, gbool ascending, char *sortkey, GCompareMode comparemode)
{
ArrayCompareFn comparator=0;
switch (comparemode)
{
case cm_int: comparator = IntKeyCompare;
break;
case cm_float: comparator = FloatKeyCompare;
break;
case cm_strcase: comparator = StrCaseKeyCompare;
break;
case cm_stricase: comparator = StrNoCaseKeyCompare;
break;
}
serverlist->sortkey = sortkey;
serverlist->sortascending = ascending;
g_sortserverlist = serverlist;
ArraySort(serverlist->servers,comparator);
}
#ifdef __cplusplus
}
#endif
@@ -0,0 +1,95 @@
#include "AdeptHeaders.hpp"
#include "GameSpy.h"
/******
gutil.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
******/
#include "gutil.h"
#ifndef _GUTIL
#define _GUTIL
/*****************************************************************************/
/* Various encryption / encoding routines */
void swap_byte ( uchar *a, uchar *b )
{
uchar swapByte;
swapByte = *a;
*a = *b;
*b = swapByte;
}
uchar encode_ct ( uchar c )
{
if (c < 26) return (uchar)('A'+c);
if (c < 52) return (uchar)('a'+c-26);
if (c < 62) return (uchar)('0'+c-52);
if (c == 62) return (uchar)('+');
if (c == 63) return (uchar)('/');
return 0;
}
void gs_encode ( uchar *ins, int size, uchar *result )
{
int i,pos;
uchar trip[3];
uchar kwart[4];
i=0;
while (i < size)
{
for (pos=0 ; pos <= 2 ; pos++, i++)
if (i < size) trip[pos] = *ins++;
else trip[pos] = '\0';
kwart[0] = (uchar)( (trip[0]) >> 2);
kwart[1] = (uchar)((((trip[0]) & 3) << 4) + ((trip[1]) >> 4));
kwart[2] = (uchar)((((trip[1]) & 15) << 2) + ((trip[2]) >> 6));
kwart[3] = (uchar)( (trip[2]) & 63);
for (pos=0; pos <= 3; pos++) *result++ = encode_ct(kwart[pos]);
}
*result='\0';
}
void gs_encrypt ( uchar *key, int key_len, uchar *buffer_ptr, int buffer_len )
{
short counter;
uchar x, y, xorIndex;
uchar state[256];
for ( counter = 0; counter < 256; counter++) state[counter] = (uchar) counter;
x = 0; y = 0;
for ( counter = 0; counter < 256; counter++)
{
y = (uchar)((key[x] + state[counter] + y) % 256);
x = (uchar)((x + 1) % key_len);
swap_byte ( &state[counter], &state[y] );
}
x = 0; y = 0;
for ( counter = 0; counter < buffer_len; counter ++)
{
x = (uchar)((x + buffer_ptr[counter] + 1) % 256);
y = (uchar)((state[x] + y) % 256);
swap_byte ( &state[x], &state[y] );
xorIndex = (uchar)((state[x] + state[y]) % 256);
buffer_ptr[counter] ^= state[xorIndex];
}
}
/*****************************************************************************/
#endif
@@ -0,0 +1,26 @@
/******
gutil.h
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
******
Please see the GameSpy C Engine SDK documentation for more
information
******/
typedef unsigned char uchar;
void swap_byte ( uchar *a, uchar *b );
uchar encode_ct ( uchar c );
void gs_encode ( uchar *ins, int size, uchar *result );
void gs_encrypt ( uchar *key, int key_len, uchar *buffer_ptr, int buffer_len );
@@ -0,0 +1,136 @@
#include "AdeptHeaders.hpp"
#include "GameSpy.h"
/*
*
* File: hashtable.c
* ---------------
* David Wright
* 10/8/98
*
* See hashtable.h for function comments
* Implmentation is straight-forward, using a fixed dynamically allocated
* array for the buckets, and a DArray for each individual bucket
*/
#ifndef UNDER_CE
#include <assert.h>
#else
#define assert(a)
#endif
#include <stdlib.h>
#include <string.h>
#include "darray.h"
#include "hashtable.h"
struct HashImplementation
{
DArray *buckets;
int nbuckets;
TableElementFreeFn freefn;
TableHashFn hashfn;
TableCompareFn compfn;
};
HashTable TableNew(int elemSize, int nBuckets,
TableHashFn hashFn, TableCompareFn compFn,
TableElementFreeFn freeFn)
{
HashTable table;
int i;
assert(hashFn);
assert(compFn);
assert(elemSize);
assert(nBuckets);
table = (HashTable)malloc(sizeof(struct HashImplementation));
assert(table);
table->buckets = (DArray *)malloc(nBuckets * sizeof(DArray));
assert(table->buckets);
for (i = 0; i < nBuckets; i++) //ArrayNew will assert if allocation fails
table->buckets[i] = ArrayNew(elemSize, 0, freeFn);
table->nbuckets = nBuckets;
table->freefn = freeFn;
table->compfn = compFn;
table->hashfn = hashFn;
return table;
}
void TableFree(HashTable table)
{
int i;
assert(table);
for (i = 0 ; i < table->nbuckets ; i++)
ArrayFree(table->buckets[i]);
free(table->buckets);
free(table);
}
int TableCount(HashTable table)
{
int i, count = 0;
for (i = 0 ; i < table->nbuckets ; i++)
count += ArrayLength(table->buckets[i]);
return count;
}
void TableEnter(HashTable table, const void *newElem)
{
int hash, itempos;
hash = table->hashfn(newElem, table->nbuckets);
itempos = ArraySearch(table->buckets[hash], newElem, table->compfn, 0,0);
if (itempos == NOT_FOUND)
ArrayAppend(table->buckets[hash], newElem);
else
ArrayReplaceAt(table->buckets[hash], newElem, itempos);
}
int TableRemove(HashTable table, const void *delElem)
{
int hash, itempos;
hash = table->hashfn(delElem, table->nbuckets);
itempos = ArraySearch(table->buckets[hash], delElem, table->compfn, 0,0);
if (itempos == NOT_FOUND)
return 0;
else
ArrayDeleteAt(table->buckets[hash], itempos);
return 1;
}
void *TableLookup(HashTable table, const void *elemKey)
{
int hash, itempos;
hash = table->hashfn(elemKey, table->nbuckets);
itempos = ArraySearch(table->buckets[hash], elemKey, table->compfn, 0,
0);
if (itempos == NOT_FOUND)
return NULL;
else
return ArrayNth(table->buckets[hash], itempos);
}
void TableMap(HashTable table, TableMapFn fn, void *clientData)
{
int i;
assert(fn);
for (i = 0 ; i < table->nbuckets ; i++)
ArrayMap(table->buckets[i], fn, clientData);
}
@@ -0,0 +1,186 @@
#ifndef _HASHTABLE_H
#define _HASHTABLE_H
/* File: hashtable.h
* ------------------
* Defines the interface for the HashTable ADT.
* The HashTable allows the client to store any number of elements of any
* type in a hash table for fast storage and retrieval. The client specifies
* the size (in bytes) of the elements that will be stored in the table when
* it is created. Thereafter the client and the HashTable refer to elements
* via (void*) ptrs. The HashTable imposes no upper bound on the number of
* elements and deals with all its own memory management.
*
* The client-supplied information (in the form of the number of buckets
* to use and the hashing function to be applied to each element) is employed
* to divide elements in buckets with hopefully only few collisions, resulting
* in Enter & Lookup performance in constant-time. The HashTable also supports
* iterating over all elements by use of mapping function.
*/
/* Type: HashTable
* ----------------
* Defines the HashTable type itself. The client can declare variables of type
* HashTable, but these variables must be initialized with the result of
* TableNew. The HashTable is implemented with pointers, so all client
* copies in variables or parameters will be "shallow" -- they will all
* actually point to the same HashTable structure. Only calls to TableNew
* create new tables. The struct declaration below is "incomplete"- the
* implementation details are literally not visible in the client .h file.
*/
typedef struct HashImplementation *HashTable;
/* TableHashFn
* -----------
* TableHashFn is a pointer to a client-supplied function which the
* HashTable uses to hash elements. The hash function takes a (const void*)
* pointer to an element and the number of buckets and returns an int,
* which represents the hash code for this element. The returned hash code
* should be within the range 0 to numBuckets-1 and should be stable (i.e.
* an element's hash code should not change over time).
* For best performance, the hash function should be designed to
* uniformly distribute elements over the available number of buckets.
*/
typedef int (_cdecl *TableHashFn)(const void *elem, int numBuckets);
/* TableCompareFn
* --------------
* TableCompareFn is a pointer to a client-supplied function which the
* HashTable uses to compare elements. The comparator takes two
* (const void*) pointers (these will point to elements) and returns an int.
* The comparator should indicate the ordering of the two elements
* using the same convention as the strcmp library function:
* If elem1 is "less than" elem2, return a negative number.
* If elem1 is "greater than" elem2, return a positive number.
* If the two elements are "equal", return 0.
*/
typedef int (_cdecl *TableCompareFn)(const void *elem1, const void *elem2);
/* TableMapFn
* ----------
* TableMapFn defines the space of functions that can be used to map over
* the elements in a HashTable. A map function is called with a pointer to
* the element and a client data pointer passed in from the original caller.
*/
typedef void (_cdecl *TableMapFn)(void *elem, void *clientData);
/* TableElementFreeFn
* ------------------
* TableElementFreeFn defines the space of functions that can be used as the
* clean-up function for each element as it is deleted from the array
* or when the entire array of elements is freed. The cleanup function is
* called with a pointer to an element about to be deleted.
*/
typedef void (_cdecl *TableElementFreeFn)(void *elem);
#ifdef __cplusplus
extern "C" {
#endif
/* TableNew
* --------
* Creates a new HashTable with no entries and returns it. The elemSize
* parameter specifies the number of bytes that a single element of the
* table should take up. For example, if you want to store elements of type
* Binky, you would pass sizeof(Binky) as this parameter. An assert is
* raised if this size is not greater than 0.
*
* The nBuckets parameter specifies the number of buckets that the elements
* will be partitioned into. Once a HashTable is created, this number does
* not change. The nBuckets parameter must be in synch with the behavior of
* the hashFn, which must return a hash code between 0 and nBuckets-1.
* The hashFn parameter specifies the function that is called to retrieve the
* hash code for a given element. See the type declaration of TableHashFn
* above for more information. An assert is raised if nBuckets is not
* greater than 0.
*
* The compFn is used for testing equality between elements. See the
* type declaration for TableCompareFn above for more information.
*
* The elemFreeFn is the function that will be called on an element that is
* about to be overwritten (by a new entry in TableEnter) or on each element
* in the table when the entire table is being freed (using TableFree). This
* function is your chance to do any deallocation/cleanup required,
* (such as freeing any pointers contained in the element). The client can pass
* NULL for the cleanupFn if the elements don't require any handling on free.
* An assert is raised if either the hash or compare functions are NULL.
*/
HashTable TableNew(int elemSize, int nBuckets,
TableHashFn hashFn, TableCompareFn compFn,
TableElementFreeFn freeFn);
/* TableFree
* ----------
* Frees up all the memory for the table and its elements. It DOES NOT
* automatically free memory owned by pointers embedded in the elements. This
* would require knowledge of the structure of the elements which the HashTable
* does not have. However, it will iterate over the elements calling
* the elementFreeFn earlier supplied to TableNew and therefore, the client,
* who knows what the elements are,can do the appropriate deallocation of any
* embedded pointers through that function.
* After calling this, the value of what table points to is undefined.
*/
void TableFree(HashTable table);
/* TableCount
* ----------
* Returns the number of elements currently in the table.
*/
int TableCount(HashTable table);
/* TableEnter
* ----------
* Enters a new element into the table. Uses the hash function to determine
* which bucket to place the new element. Its contents are copied from the
* memory pointed to by newElem. If there is already an element in the table
* which is determined to be equal (using the comparison function) this will
* use the contents of the new element to replace the previous element,
* calling the free function on the replaced element.
*/
void TableEnter(HashTable table, const void *newElem);
/* TableRemove
* ----------
* Remove a element frin the table. If the element does not exist
* the function returns 0. If it exists, it returns 1 and calls the
* free function on the removed element.
*/
int TableRemove(HashTable table, const void *delElem);
/* TableLookup
* ----------
* Returns a pointer to the table element which matches the elemKey parameter
* (equality is determined by the comparison function). If there is no
* matching element, returns NULL. Calling this function does not
* re-arrange or change contents of the table or modify elemKey in any way.
*/
void *TableLookup(HashTable table, const void *elemKey);
/* TableMap
* -----------
* Iterates through each element in the table (in any order) and calls the
* function fn for that element. The function is called with the address of
* the table element and the clientData pointer. The clientData value allows
* the client to pass extra state information to the client-supplied function,
* if necessary. If no client data is required, this argument should be NULL.
* An assert is raised if the map function is NULL.
*/
void TableMap(HashTable table, TableMapFn fn, void *clientData);
#ifdef __cplusplus
}
#endif
#endif _HASHTABLE_H
@@ -0,0 +1,159 @@
#include "AdeptHeaders.hpp"
#include "GameSpy.h"
/******
nonport.c
GameSpy Developer SDK
Copyright 1999 GameSpy Industries, Inc
Suite E-204
2900 Bristol Street
Costa Mesa, CA 92626
(714)549-7689
Fax(714)549-0757
******/
#include "nonport.h"
#ifdef __cplusplus
extern "C" {
#endif
#ifdef UNDER_CE
time_t time(time_t *timer)
{
static time_t ret;
SYSTEMTIME systime;
FILETIME ftime;
LONGLONG holder;
GetLocalTime(&systime);
SystemTimeToFileTime(&systime, &ftime);
holder = (ftime.dwHighDateTime << 32) + ftime.dwLowDateTime;
if (timer == NULL)
timer = &ret;
*timer = (time_t)((holder - 116444736000000000) / 10000000);
return *timer;
}
#endif
unsigned long current_time() //returns current time in msec
{
#ifdef _WIN32
return (GetTickCount());
#else
#ifdef _MACOS
return (TickCount() * 50) / 3;
#else
struct timeval time;
gettimeofday(&time, NULL);
return (time.tv_sec * 1000 + time.tv_usec / 1000);
#endif
#endif
}
void msleep(unsigned long msec)
{
#ifdef _WIN32
Sleep(msec);
#else
#ifdef _MACOS
// EventRecord rec;
WaitNextEvent(everyEvent,/*&rec*/NULL, (msec*1000)/60, NULL);
#else
usleep(msec * 1000);
#endif
#endif
}
void SocketStartUp()
{
#if defined(_WIN32) || defined(_MACOS)
WSADATA data;
WSAStartup(0x0101, &data);
#endif
}
void SocketShutDown()
{
#if defined(_WIN32) || defined(_MACOS)
WSACleanup();
#endif
}
#if defined(_WIN32) && !defined(UNDER_CE)
//do nothign
#else
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
char *_strdup(const char *src)
{
char *res;
if(src == NULL) //PANTS|02.11.00|check for NULL before strlen
return NULL;
res = (char *)malloc(strlen(src) + 1);
if(res != NULL) //PANTS|02.02.00|check for NULL before strcpy
strcpy(res, src);
return res;
}
char *_strlwr(char *string)
{
char *hold = string;
while (*string)
{
*string = tolower(*string);
string++;
}
return hold;
}
char *_strupr(char *string)
{
char *hold = string;
while (*string)
{
*string = toupper(*string);
string++;
}
return hold;
}
#endif
#if defined(_MACOS) || defined(UNDER_CE)
int strcasecmp(const char *string1, const char *string2)
{
while (tolower(*string1) == tolower(*string2) && *string1 != 0 && *string2 != 0)
{
*string1++; *string2++;
}
return tolower(*string1) - tolower(*string2);
}
int strncasecmp(const char *string1, const char *string2, size_t count)
{
while (count-- > 0 && tolower(*string1) == tolower(*string2) && *string1 != 0 && *string2 != 0)
{
*string1++; *string2++;
}
return tolower(*string1) - tolower(*string2);
}
#endif
#ifdef __cplusplus
}
#endif
@@ -0,0 +1,158 @@
/******
nonport.h
GameSpy Developer SDK
Copyright 1999 GameSpy Industries, Inc
Suite E-204
2900 Bristol Street
Costa Mesa, CA 92626
(714)549-7689
Fax(714)549-0757
******/
#if defined(applec) || defined(THINK_C) || defined(__MWERKS__)
#define _MACOS
#endif
#ifdef _WIN32
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <winsock.h>
#else
#ifdef _MACOS
#include <events.h>
#include "mwinsock.h"
#else
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <netinet/in.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <ctype.h>
#include <errno.h>
#include <sys/time.h>
#endif
#endif
#ifdef __cplusplus
extern "C" {
#endif
unsigned long current_time();
void msleep(unsigned long msec);
void SocketStartUp();
void SocketShutDown();
#ifndef SOCKET_ERROR
#define SOCKET_ERROR (-1)
#endif
#ifndef INVALID_SOCKET
#define INVALID_SOCKET (-1)
#endif
#if defined(_WIN32) && !defined(UNDER_CE)
#define strcasecmp _stricmp
#define strncasecmp _strnicmp
#else
char *_strdup(const char *src);
char *_strlwr(char *string);
char *_strupr(char *string);
#endif
#if defined(_MACOS) || defined(UNDER_CE)
int strcasecmp(const char *string1, const char *string2);
int strncasecmp(const char *string1, const char *string2, size_t count);
#endif
#if !defined(_MACOS) && !defined(_WIN32)
#define WSAGetLastError() errno
#define SOCKET int
#define closesocket close
#define ioctlsocket ioctl
#define WSAEWOULDBLOCK EWOULDBLOCK
#define WSAEINPROGRESS EINPROGRESS
#define WSAEALREADY EALREADY
#define WSAENOTSOCK ENOTSOCK
#define WSAEDESTADDRREQ EDESTADDRREQ
#define WSAEMSGSIZE EMSGSIZE
#define WSAEPROTOTYPE EPROTOTYPE
#define WSAENOPROTOOPT ENOPROTOOPT
#define WSAEPROTONOSUPPORT EPROTONOSUPPORT
#define WSAESOCKTNOSUPPORT ESOCKTNOSUPPORT
#define WSAEOPNOTSUPP EOPNOTSUPP
#define WSAEPFNOSUPPORT EPFNOSUPPORT
#define WSAEAFNOSUPPORT EAFNOSUPPORT
#define WSAEADDRINUSE EADDRINUSE
#define WSAEADDRNOTAVAIL EADDRNOTAVAIL
#define WSAENETDOWN ENETDOWN
#define WSAENETUNREACH ENETUNREACH
#define WSAENETRESET ENETRESET
#define WSAECONNABORTED ECONNABORTED
#define WSAECONNRESET ECONNRESET
#define WSAENOBUFS ENOBUFS
#define WSAEISCONN EISCONN
#define WSAENOTCONN ENOTCONN
#define WSAESHUTDOWN ESHUTDOWN
#define WSAETOOMANYREFS ETOOMANYREFS
#define WSAETIMEDOUT ETIMEDOUT
#define WSAECONNREFUSED ECONNREFUSED
#define WSAELOOP ELOOP
#define WSAENAMETOOLONG ENAMETOOLONG
#define WSAEHOSTDOWN EHOSTDOWN
#define WSAEHOSTUNREACH EHOSTUNREACH
#define WSAENOTEMPTY ENOTEMPTY
#define WSAEPROCLIM EPROCLIM
#define WSAEUSERS EUSERS
#define WSAEDQUOT EDQUOT
#define WSAESTALE ESTALE
#define WSAEREMOTE EREMOTE
typedef struct sockaddr SOCKADDR;
typedef struct sockaddr_in SOCKADDR_IN;
typedef struct in_addr IN_ADDR;
typedef struct hostent HOSTENT;
typedef struct timeval TIMEVAL;
#endif
#ifndef max
#define max(a,b) (((a) > (b)) ? (a) : (b))
#define min(a,b) (((a) < (b)) ? (a) : (b))
#endif
#ifdef _WIN32
#define PATHCHAR '\\'
#else
#ifdef MACOS
#define PATHCHAR ':'
#else
#define PATHCHAR '/'
#endif
#endif
#ifdef UNDER_CE
//CE does not have the stdlib time() call
time_t time(time_t *timer);
#else
#include <time.h>
#endif
#ifdef __cplusplus
}
#endif