Member
|
07-27-2011
, 15:57
Re: [EXTENSION] Query Cache 1.7 (Updated!)
|
#127
|
I did compile it on my own, but since it is a new computer I downloaded all SDKs right before compiling, so my guess is my versions are probably correct. And heck, it compiles. Strangely I found another variation of Query Cache bundled with SteamTools and when I compile that and test it (using the same SDKs and paths) the game description is not corrupt. I tried to compare the two extension source codes but couldn't find any difference except slightly different functions/variable names.
Also, on this version of Query Cache, for some reason when my CSS servers had a password on them, they would not show the lock in the server list, and when trying to enter you would get "Bad Password". When trying with the bunlded SteamTools version, the password icon displays. I still can't find the difference between the two, so I'll leave it up to you:
Also, if you plan to run it on a server, make sure you load it at least 30 seconds after the server starts, for some reason it crashes the entire server when started before the server initialized.
Code:
/* This program contains code from the following programs (which are licensed
* under the GPL):
* 1. Query Cache
* (http://forums.alliedmods.net/showthread.php?t=134373)
* by Afronanny
* 2. SrcdsQueryCache
* (http://www.wantedgov.it/page/62-srcds-query-cache/)
* by Beretta Claudio
* 3. SRCDS Denial of Service Protect
* (http://forums.alliedmods.net/showthread.php?t=95312) by raydan
* 4. Left 4 Downtown (http://forums.alliedmods.net/showthread.php?t=91132)
* by Downtown1
* 5. SteamTools (http://forums.alliedmods.net/showthread.php?t=129763)
* by Asher Baker (Asherkin)
*
* =============================================================================
* A huge thank you goes to CrimsonGT for all his help with this extension.
*
* Thanks also to Luigi Auriemma (http://aluigi.org/),
* whose Winsock code from UDP proxy/pipe (http://aluigi.org/mytoolz.htm)
* helped me learn the Winsock API.
* =============================================================================
*
* This program is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, version 3.0, as published by the
* Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>.
*
* *****************************************************************************
* This program is a SourceMod extension, and is effected by the following
* exceptions from SourceMod's authors:
*
* As a special exception, AlliedModders LLC gives you permission to link the
* code of this program (as well as its derivative works) to "Half-Life 2," the
* "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
* by the Valve Corporation. You must obey the GNU General Public License in
* all respects for all other code used. Additionally, AlliedModders LLC grants
* this exception to all derivative works. AlliedModders LLC defines further
* exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
* or <http://www.sourcemod.net/license.php>.
*
* Version: $Id$
*/
#define REQ_PACKET "\xff\xff\xff\xff\x54\x53\x6f\x75\x72\x63\x65\x20\x45\x6e\x67\x69\x6e\x65\x20\x51\x75\x65\x72\x79\x00"
#ifdef _WIN32
#include <windows.h>
#include <winsock.h>
#else
#include <dlfcn.h>
#include <sys/socket.h>
#endif
#include <bitbuf.h>
#include <iserver.h>
#include <icommandline.h>
#include <convar.h>
#include "filesystem.h"
//#include <iostream>
//#include <fstream>
#include <string.h>
#define NO_CSTEAMID_STL
#define INTERFACEOSW_H
#include <Steamworks.h>
//using namespace std;
//using std::string;
#include <ISDKTools.h>
#include "extension.h"
#define S_STRLEN 256
/* //If a MM:S port is done - this should be used for game folder name
// Windows path seperator
#ifdef _WIN32
#define PATH_SEP "\\"
#elif defined _LINUX
#define PATH_SEP "/"
#endif
*/
#define DEFAULT_GAME_VERSION "1.0.9.1"
#define STEAMCLIENT_INTERFACE_VERSION STEAMCLIENT_INTERFACE_VERSION_009
#define STEAMGAMESERVER_INTERFACE_VERSION STEAMGAMESERVER_INTERFACE_VERSION_010
#define SOCKET_ERROR (-1)
// Default protocol version
#define DEFAULT_PROTO_VERSION 15
class ISteamClient: public ISteamClient009 {};
class ISteamGameServer: public ISteamGameServer010 {};
typedef HSteamPipe (*GetPipeFn)();
typedef HSteamUser (*GetUserFn)();
typedef void * (*GetHandleFn)(HSteamUser, HSteamPipe);
typedef bool (*GetCallbackFn)(HSteamPipe hSteamPipe, CallbackMsg_t *pCallbackMsg);
typedef void (*FreeLastCallbackFn)(HSteamPipe hSteamPipe);
GetPipeFn g_GameServerSteamPipe;
GetUserFn g_GameServerSteamUser;
GetHandleFn g_GameServerHandle;
IServerGameDLL *g_pServerGameDLL = NULL;
ISteamGameServer *g_pSteamGameServer = NULL;
IFileSystem *g_pFullFileSystem = NULL;
GetCallbackFn GetCallback;
FreeLastCallbackFn FreeLastCallback;
int g_GameServerSteamAPIActivatedHookID = 0;
SH_DECL_HOOK0_void(IServerGameDLL, GameServerSteamAPIActivated, SH_NOATTRIB, 0);
/**
* @file extension.cpp
* @brief Implement extension code here.
*/
QueryCache g_QueryCache; /**< Global singleton for extension's main interface */
SMEXT_LINK(&g_QueryCache);
IServer *g_pServer = NULL;
ISDKTools *g_pSDKTools = NULL;
ICvar *g_pCvar = NULL;
bool g_recvfrom_hooked = false;
int (*g_real_recvfrom_ptr) (int , char *, int , int , struct sockaddr *, int *);
time_t g_a2s_time = 0;
char g_replyStore[1024];
bf_write g_replyPacket(g_replyStore, 1024);
char g_gameDir[S_STRLEN];
char g_gameDesc[S_STRLEN];
char g_gameVersion[S_STRLEN];
int g_appID;
int g_maxClients;
int g_Port;
static uint64 g_SteamId;
bool QueryCache::SDK_OnLoad(char *error, size_t maxlength, bool late)
{
#ifdef _WIN32
WSAData wsaData;
if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
{
strncpy(error, "Could not init Winsock", maxlength);
return false;
}
#endif
if(!ReadSteamINF())
{
strncpy(error, "Error opening steam.inf", maxlength);
return false;
}
g_GameServerSteamAPIActivatedHookID = SH_ADD_HOOK(IServerGameDLL, GameServerSteamAPIActivated, g_pServerGameDLL, SH_STATIC(Hook_GameServerSteamAPIActivated), true);
return true;
}
void Hook_GameServerSteamAPIActivated(void)
{
ISteamClient *client = NULL;
if (!LoadSteamclient(&client))
{
return;
}
g_pSteamGameServer = (ISteamGameServer *)client->GetISteamGenericInterface(g_GameServerSteamUser(), g_GameServerSteamPipe(), STEAMGAMESERVER_INTERFACE_VERSION);
}
bool QueryCache::SDK_OnMetamodLoad(ISmmAPI *ismm, char *error, size_t maxlen, bool late)
{
// Get ICvar (code from Left4Downtown)
GET_V_IFACE_CURRENT(GetServerFactory, g_pServerGameDLL, IServerGameDLL, INTERFACEVERSION_SERVERGAMEDLL);
GET_V_IFACE_CURRENT(GetEngineFactory, g_pCvar, ICvar, CVAR_INTERFACE_VERSION);
GET_V_IFACE_CURRENT(GetFileSystemFactory, g_pFullFileSystem, IFileSystem, FILESYSTEM_INTERFACE_VERSION);
/*ConVar_Register(0, this);*/
// TODO: Error handling?
return true;
}
void QueryCache::SDK_OnAllLoaded()
{
SM_GET_LATE_IFACE(SDKTOOLS, g_pSDKTools);
g_pServer = g_pSDKTools->GetIServer();
BuildStaticReplyInfo();
g_maxClients = g_pServer->GetMaxClients();
g_Port = g_pServer->GetUDPPort();
EnableReceiveHook();
}
void QueryCache::SDK_OnUnload()
{
if (g_GameServerSteamAPIActivatedHookID != 0)
{
SH_REMOVE_HOOK_ID(g_GameServerSteamAPIActivatedHookID);
g_GameServerSteamAPIActivatedHookID = 0;
}
DisableReceiveHook();
#ifdef _WIN32
WSACleanup();
#endif
}
bool QueryCache::RegisterConCommandBase(ConCommandBase *pVar)
{
return META_REGCVAR(pVar);
}
int RecvFromHook(int s, char *buf, int len, int flags, struct sockaddr *from, int *fromlen)
{
int ret = g_real_recvfrom_ptr(s, buf, len, flags, from, fromlen);
if(ret > 5)
{
/* A2S_INFO */
if(strcmp(buf, REQ_PACKET) == 0)
{
if (time(NULL) - g_a2s_time > 5)
{
GameDescription();
BuildReplyInfo();
}
sendto(s, (const char *)g_replyPacket.GetData(), g_replyPacket.GetNumBytesWritten(), 0, from, *fromlen);
#ifdef _WIN32
flags = WSAETIMEDOUT;
#elif defined _LINUX
flags = ETIMEDOUT;
#endif
return SOCKET_ERROR;
}
}
return ret;
}
void DisableReceiveHook()
{
if(g_recvfrom_hooked)
{
g_pVCR->Hook_recvfrom = g_real_recvfrom_ptr;
g_recvfrom_hooked = false;
}
}
void EnableReceiveHook()
{
if(!g_recvfrom_hooked)
{
g_real_recvfrom_ptr = g_pVCR->Hook_recvfrom;
g_pVCR->Hook_recvfrom = &RecvFromHook;
g_recvfrom_hooked = true;
}
}
void BuildStaticReplyInfo()
{
//Use this method in a MM:S port, using SM for now
/*
engine->GetGameDir(&g_gameDir[0], S_STRLEN);
// Strip everything except the
// actual game dir
string temp(g_gameDir);
int pos = temp.find_last_of(PATH_SEP);
strncpy(&g_gameDir[0],
(temp.substr(pos + 1, temp.size() - pos)).c_str(), S_STRLEN);
*/
const CSteamID *steamid = engine->GetGameServerSteamID();
g_SteamId = steamid->ConvertToUint64();
}
void BuildReplyInfo()
{
g_replyPacket.Reset();
int passByte = 00;
const char *pGamePass = g_pServer->GetPassword();
if(pGamePass && strcmp(pGamePass, ""))
{
passByte = 01;
}
g_replyPacket.WriteLong(-1);
g_replyPacket.WriteByte(73);
g_replyPacket.WriteByte(DEFAULT_PROTO_VERSION);
g_replyPacket.WriteString(g_pServer->GetName());
g_replyPacket.WriteString(g_pServer->GetMapName());
g_replyPacket.WriteString(g_gameDir);
g_replyPacket.WriteString(g_gameDesc);
g_replyPacket.WriteShort(g_appID);
g_replyPacket.WriteByte(g_pServer->GetNumClients());
g_replyPacket.WriteByte(g_maxClients);
g_replyPacket.WriteByte(g_pServer->GetNumFakeClients());
g_replyPacket.WriteByte(100);
#ifdef _WIN32
g_replyPacket.WriteByte(119);
#elif defined _LINUX
g_replyPacket.WriteByte(108);
#endif
g_replyPacket.WriteByte(passByte);
g_replyPacket.WriteByte(1);
g_replyPacket.WriteString(g_gameVersion);
g_replyPacket.WriteByte(176);
g_replyPacket.WriteShort(g_Port);
g_replyPacket.WriteLongLong(g_SteamId);
g_replyPacket.WriteString(g_pCvar->FindVar("sv_tags")->GetString());
g_a2s_time = time(NULL);
}
bool ReadSteamINF()
{
FILE *pFile;
char buff[512];
g_pSM->BuildPath(Path_Game, buff, 512, "steam.inf");
pFile = fopen(buff, "r");
if (pFile == NULL)
{
return false;
}
long lSize;
char * buffer;
size_t result;
// obtain file size:
fseek (pFile , 0 , SEEK_END);
lSize = ftell (pFile);
rewind (pFile);
// allocate memory to contain the whole file:
buffer = (char*) malloc (sizeof(char)*lSize);
if (buffer == NULL) {fputs ("Memory error",stderr); exit (2);}
// copy the file into the buffer:
result = fread (buffer,1,lSize,pFile);
char info [3][12];
int j = 0;
for(int i = 0; i < 3; i++)
{
while(buffer[j++] != '=')
{
}
int k = 0;
while(buffer[j++] != '\n')
{
info[i][k++] = buffer[j - 1];
}
if(info[i][k-1] == '\r')
k -= 1;
info[i][k] = '\0';
}
strcpy(&g_gameVersion[0], info[0]);
strcpy(&g_gameDir[0], g_pSM->GetGameFolderName());
g_appID = atoi(info[2]);
fclose (pFile);
free (buffer);
return true;
}
static void GameDescription()
{
if (strcmp(g_gameDir, "empires") == 0)
{
strcpy(&g_gameDesc[0], "Empires v2.24d");
}
else
{
const char *desc = gamedll->GetGameDescription();
strcpy(&g_gameDesc[0], desc);
}
}
bool LoadSteamclient(ISteamClient **pSteamClient, int method)
{
if(!g_GameServerSteamPipe || !g_GameServerSteamUser || !g_GameServerSteamPipe() || !g_GameServerSteamUser())
return false;
HMODULE steamclient_library = NULL;
ISteamClient *pLocalSteamClient = NULL;
g_SMAPI->ConPrintf("[QueryCache] Trying method %d ...\n", (method + 1));
switch(method)
{
case 0:
{
#if defined _WIN32
CSysModule *pModSteamClient = g_pFullFileSystem->LoadModule("../bin/steamclient.dll", "MOD", false);
#elif defined _LINUX
CSysModule *pModSteamClient = g_pFullFileSystem->LoadModule("../bin/steamclient.so", "MOD", false);
#endif
if (!pModSteamClient)
{
g_pSM->LogError(myself, "Unable to get steamclient handle.");
break;
}
steamclient_library = reinterpret_cast<HMODULE>(pModSteamClient);
break;
}
#ifdef _WIN32
case 1:
{
steamclient_library = GetModuleHandle("steamclient.dll");
break;
}
case 2:
{
HKEY hRegKey;
char pchSteamDir[MAX_PATH];
if (RegOpenKeyExA(HKEY_LOCAL_MACHINE, "Software\\Valve\\Steam", 0, KEY_QUERY_VALUE, &hRegKey) != ERROR_SUCCESS)
{
g_pSM->LogError(myself, "Steam registry key not found.");
break;
}
DWORD dwLength = sizeof(pchSteamDir);
RegQueryValueExA(hRegKey, "InstallPath", NULL, NULL, (BYTE*)pchSteamDir, &dwLength);
RegCloseKey(hRegKey);
strcat(pchSteamDir, "/steamclient.dll");
CSysModule *pModSteamClient = g_pFullFileSystem->LoadModule(pchSteamDir, "MOD", false);
if (!pModSteamClient)
{
g_pSM->LogError(myself, "Unable to get steamclient handle.");
break;
}
steamclient_library = reinterpret_cast<HMODULE>(pModSteamClient);
break;
}
#endif //_WIN32
default:
{
g_pSM->LogError(myself, "Ran out of methods to acquire SteamWorks interfaces.");
return false;
}
}
if (!steamclient_library)
{
return LoadSteamclient(pSteamClient, (method + 1));
}
CreateInterfaceFn steamclient = (CreateInterfaceFn)GetProcAddress(steamclient_library, "CreateInterface");
pLocalSteamClient = (ISteamClient *)steamclient(STEAMCLIENT_INTERFACE_VERSION, NULL);
ISteamGameServer *gameserver = (ISteamGameServer *)pLocalSteamClient->GetISteamGenericInterface(g_GameServerSteamUser(), g_GameServerSteamPipe(), STEAMGAMESERVER_INTERFACE_VERSION);
if (!gameserver)
{
return LoadSteamclient(pSteamClient, (method + 1));
}
g_SMAPI->ConPrintf("[QueryCache] Method %d worked!\n", (method + 1));
*pSteamClient = pLocalSteamClient;
GetCallback = (GetCallbackFn)GetProcAddress(steamclient_library, "Steam_BGetCallback");
FreeLastCallback = (FreeLastCallbackFn)GetProcAddress(steamclient_library, "Steam_FreeLastCallback");
return true;
}
Last edited by eladnava; 07-27-2011 at 16:04.
|
|