Raised This Month: $51 Target: $400
 12% 

[EXTENSION] Query Cache 1.7 (Updated!)


Post New Thread Reply   
 
Thread Tools Display Modes
Afronanny
Veteran Member
Join Date: Aug 2009
Old 07-27-2011 , 07:46   Re: [EXTENSION] Query Cache 1.7 (Updated!)
Reply With Quote #121

I think they started having that function return a corrupt string to be able to see which servers run you-know-what. It worked very well, seeing as some of them have had corrupt game descriptions for months.
Afronanny is offline
Zephyrus
Cool Pig B)
Join Date: Jun 2010
Location: Hungary
Old 07-27-2011 , 07:51   Re: [EXTENSION] Query Cache 1.7 (Updated!)
Reply With Quote #122

ive just checked, protocol version is 17 now and not 15, ill look into the binaries maybe theres a function for that or something that i can call, if i dont find anything ill just recompile querycache with the new protocol number
Zephyrus is offline
psychonic

BAFFLED
Join Date: May 2008
Old 07-27-2011 , 07:53   Re: [EXTENSION] Query Cache 1.7 (Updated!)
Reply With Quote #123

Quote:
Originally Posted by CrimsonGT View Post
I wrote my own version of this a very long time ago, and it looks like it broke while I was away for the past months. My assumption as to why it is not working is they most likely changed some of the data that is used by this plugin when building the reply information.

The 2 most notable ones I remember were Protocol version and Server Version. I was able to use some hacks to get the Server Version string for version of this, but I had to hardcode the Protocol in. It is more than likely the protocol has changed, or is different from some of the other games, such as L4D2.
gamedll->GetGameDescription works fine. Make sure that you're using the correct sdk version and that it's fully up to date.

For protocol, you can get it with gpGlobals->network_protocol.
psychonic is offline
Zephyrus
Cool Pig B)
Join Date: Jun 2010
Location: Hungary
Old 07-27-2011 , 08:16   Re: [EXTENSION] Query Cache 1.7 (Updated!)
Reply With Quote #124

Quote:
Originally Posted by psychonic View Post
gamedll->GetGameDescription works fine. Make sure that you're using the correct sdk version and that it's fully up to date.

For protocol, you can get it with gpGlobals->network_protocol.
gpGlobals->network_protocol reports 0
Zephyrus is offline
psychonic

BAFFLED
Join Date: May 2008
Old 07-27-2011 , 09:17   Re: [EXTENSION] Query Cache 1.7 (Updated!)
Reply With Quote #125

Quote:
Originally Posted by Zephyrus View Post
gpGlobals->network_protocol reports 0
Interesting. Someone told me a while back that that would grab it, and I thought it did at the time, but you're right. It's definitely 0 now. Fortunately, it doesn't seem to be affecting anything at the moment.

Game description is still definitely coming through correctly though with the func on IServerGameDLL. I'm not using this extension specifically so not entirely sure of the code without looking, but make sure that you're not calling it before gamerules would exist.
psychonic is offline
Zephyrus
Cool Pig B)
Join Date: Jun 2010
Location: Hungary
Old 07-27-2011 , 09:27   Re: [EXTENSION] Query Cache 1.7 (Updated!)
Reply With Quote #126

no, it is executed before every reply packet gets generated, btw it works for me on my servers with my builds (built against latest sdks) so i guess that guy who said it is corrupt uses an old sdk, i guess he built it for himself
Zephyrus is offline
eladnava
Member
Join Date: Feb 2011
Old 07-27-2011 , 15:57   Re: [EXTENSION] Query Cache 1.7 (Updated!)
Reply With Quote #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.
eladnava is offline
asherkin
SourceMod Developer
Join Date: Aug 2009
Location: OnGameFrame()
Old 07-27-2011 , 16:06   Re: [EXTENSION] Query Cache 1.7 (Updated!)
Reply With Quote #128

Quote:
Originally Posted by eladnava View Post
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.
Check that the Makefile isn't trying to link libsteam.so or steamclient.so, there is a reason the symbols required are looked up at runtime.
__________________
asherkin is offline
eladnava
Member
Join Date: Feb 2011
Old 07-27-2011 , 16:11   Re: [EXTENSION] Query Cache 1.7 (Updated!)
Reply With Quote #129

Apparently in the code I posted steamclient.so is being loaded, couldn't find it in the Makefile though:

Code:
                        CSysModule *pModSteamClient = g_pFullFileSystem->LoadModule("../bin/steamclient.so", "MOD", false);
This is happening in LoadSteamclient().

However, in the original Query Cache, it is loaded on GetGSSteamId(), and differently:

Code:
        steamclienthandle = dlopen("steamclient.so", RTLD_LAZY);
Really strange differences in these versions. Do you know from where the SteamTools version is from?
eladnava is offline
asherkin
SourceMod Developer
Join Date: Aug 2009
Location: OnGameFrame()
Old 07-27-2011 , 16:16   Re: [EXTENSION] Query Cache 1.7 (Updated!)
Reply With Quote #130

Quote:
Originally Posted by eladnava View Post
... code ...
Yes, that's the correct way of loading it. Both are fine, although the newer uses Valve's filesystem interface which is "better".

Quote:
Originally Posted by eladnava View Post
Really strange differences in these versions. Do you know from where the SteamTools version is from?
It's from SteamTools, although I wrote both of the methods used.
The original one was just a hacked-up example from an early version of SteamTools.
If you search this thread and recon0's original one for my posts, you may find more info.
__________________
asherkin is offline
Reply



Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off

Forum Jump


All times are GMT -4. The time now is 02:02.


Powered by vBulletin®
Copyright ©2000 - 2024, vBulletin Solutions, Inc.
Theme made by Freecode