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

[CSGO] Getting the serverplugin_empty running


Post New Thread Reply   
 
Thread Tools Display Modes
Author Message
flavy666
New Member
Join Date: Sep 2017
Old 09-30-2017 , 09:17   [CSGO] Getting the serverplugin_empty running
Reply With Quote #1

Hello guys,

My Goal:
A VSP plugin to gather additional player data (e.g. player money, current equipment) to display the data separately on a live stream (not the GOTV overlay).

Reason
The admins where very strict regarding server plugins like metamod/sourcemod. However they agreed on a self implemented small VSP.

Current Development Setup
For convenience current development is done using Windows
Windows 10 Pro
Visual Studio 2013 Express for Desktop
Windows Dedicated Source Server (via steam cmd)

My Problem
Currently sticking with the file system interface

My Road so far
  1. Downloaded the source2013-sdk from github
  2. Installed Visual Studio 2013 Express for Desktop (Update 5)
  3. Run createallprojects.bat to create the everything.sln
  4. Opened the everything.sln with VS2013
  5. Build the serverplugin_empty project as release
  6. Removed the serverplugin_empty.dll from server's bin folder (shipped via steam cmd)
  7. Copied my compiled serverplugin_empty.dll to the server's csgo/addons folder
  8. Created severplugin_empty.vdf file and point to the plugin
  9. Started the server and got: Failed to load plugin "addons/serverplugin_empty"
  10. Also got Unable to load entityinfomanager, ignoring and Unable to load gameinfomanager, ignoring warnings but I ignored them at this point.
  11. To get more information what went wrong I added some outputs to see which interface failed to load (see code below)
  12. Got: DEBUG: g_pFullFileSystem FAIL! and DEBUG: enginetrace FAIL!
  13. Resolved the engine trace error by changing the INTERFACEVERSION_ENGINETRACE_SERVER from "EngineTraceServer003" to "EngineTraceServer004" (some research was necessary)
  14. Researched the g_pFullFileSystem problem. Tried to comment the null pointer check out.
  15. Server loads the plugin but crashes with segfault
  16. Commented the null pointer check back in.
  17. Researched more: g_pFullFileSystem should be initialized in the tier2 by the ConnectTier2Libraries( &interfaceFactory, 1 ); function
  18. Maybe something went wrong in this function (no source available)
  19. Tried to instantiate the file system on my own by adding g_filesystem = (IFileSystem*)interfaceFactory(FILESYSTEM_INT ERFACE_VERSION, NULL);
  20. Also added a check for my own g_filesystem pointer
  21. Now I get DEBUG: g_pFullFileSystem FAIL! from the ConnectTier2Libraries function as well as DEBUG: g_filesystem FAIL! from trying to instantiate the file system interface on my own.

My Code
Code:
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: 
//
// $NoKeywords: $
//
//===========================================================================//

#include <stdio.h>

//#define GAME_DLL
#ifdef GAME_DLL
#include "cbase.h"
#endif

#include <stdio.h>
#include "interface.h"
#include "filesystem.h"
#include "engine/iserverplugin.h"
#include "eiface.h"
#include "igameevents.h"
#include "convar.h"
#include "Color.h"
#include "vstdlib/random.h"
#include "engine/IEngineTrace.h"
#include "tier2/tier2.h"
#include "game/server/pluginvariant.h"
#include "game/server/iplayerinfo.h"
#include "game/server/ientityinfo.h"
#include "game/server/igameinfo.h"

//#define SAMPLE_TF2_PLUGIN
#ifdef SAMPLE_TF2_PLUGIN
#include "tf/tf_shareddefs.h"
#endif
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"

// Interfaces from the engine
IVEngineServer	*engine = NULL; // helper functions (messaging clients, loading content, making entities, running commands, etc)
IGameEventManager *gameeventmanager_ = NULL; // game events interface
#ifndef GAME_DLL
#define gameeventmanager gameeventmanager_
#endif
IPlayerInfoManager *playerinfomanager = NULL; // game dll interface to interact with players
IEntityInfoManager *entityinfomanager = NULL; // game dll interface to interact with all entities (like IPlayerInfo)
IGameInfoManager *gameinfomanager = NULL; // game dll interface to get data from game rules directly
IBotManager *botmanager = NULL; // game dll interface to interact with bots
IServerPluginHelpers *helpers = NULL; // special 3rd party plugin helpers from the engine
IUniformRandomStream *randomStr = NULL;
IEngineTrace *enginetrace = NULL;
IFileSystem* g_filesystem = NULL;


CGlobalVars *gpGlobals = NULL;

// function to initialize any cvars/command in this plugin
void Bot_RunAll( void ); 

// useful helper func
#ifndef GAME_DLL
inline bool FStrEq(const char *sz1, const char *sz2)
{
	return(Q_stricmp(sz1, sz2) == 0);
}
#endif
//---------------------------------------------------------------------------------
// Purpose: a sample 3rd party plugin class
//---------------------------------------------------------------------------------
class CEmptyServerPlugin: public IServerPluginCallbacks, public IGameEventListener
{
public:
	CEmptyServerPlugin();
	~CEmptyServerPlugin();

	// IServerPluginCallbacks methods
	virtual bool			Load(	CreateInterfaceFn interfaceFactory, CreateInterfaceFn gameServerFactory );
	virtual void			Unload( void );
	virtual void			Pause( void );
	virtual void			UnPause( void );
	virtual const char     *GetPluginDescription( void );      
	virtual void			LevelInit( char const *pMapName );
	virtual void			ServerActivate( edict_t *pEdictList, int edictCount, int clientMax );
	virtual void			GameFrame( bool simulating );
	virtual void			LevelShutdown( void );
	virtual void			ClientActive( edict_t *pEntity );
	virtual void			ClientDisconnect( edict_t *pEntity );
	virtual void			ClientPutInServer( edict_t *pEntity, char const *playername );
	virtual void			SetCommandClient( int index );
	virtual void			ClientSettingsChanged( edict_t *pEdict );
	virtual PLUGIN_RESULT	ClientConnect( bool *bAllowConnect, edict_t *pEntity, const char *pszName, const char *pszAddress, char *reject, int maxrejectlen );
	virtual PLUGIN_RESULT	ClientCommand( edict_t *pEntity, const CCommand &args );
	virtual PLUGIN_RESULT	NetworkIDValidated( const char *pszUserName, const char *pszNetworkID );
	virtual void			OnQueryCvarValueFinished( QueryCvarCookie_t iCookie, edict_t *pPlayerEntity, EQueryCvarValueStatus eStatus, const char *pCvarName, const char *pCvarValue );
	virtual void			OnEdictAllocated( edict_t *edict );
	virtual void			OnEdictFreed( const edict_t *edict  );	

	// IGameEventListener Interface
	virtual void FireGameEvent( KeyValues * event );

	virtual int GetCommandIndex() { return m_iClientCommandIndex; }
private:
	int m_iClientCommandIndex;
};


// 
// The plugin is a static singleton that is exported as an interface
//
CEmptyServerPlugin g_EmtpyServerPlugin;
EXPOSE_SINGLE_INTERFACE_GLOBALVAR(CEmptyServerPlugin, IServerPluginCallbacks, INTERFACEVERSION_ISERVERPLUGINCALLBACKS, g_EmtpyServerPlugin );

//---------------------------------------------------------------------------------
// Purpose: constructor/destructor
//---------------------------------------------------------------------------------
CEmptyServerPlugin::CEmptyServerPlugin()
{
	m_iClientCommandIndex = 0;
}

CEmptyServerPlugin::~CEmptyServerPlugin()
{
}

//---------------------------------------------------------------------------------
// Purpose: called when the plugin is loaded, load the interface we need from the engine
//---------------------------------------------------------------------------------
bool CEmptyServerPlugin::Load(	CreateInterfaceFn interfaceFactory, CreateInterfaceFn gameServerFactory )
{
	ConnectTier1Libraries( &interfaceFactory, 1 );
	ConnectTier2Libraries( &interfaceFactory, 1 );

	entityinfomanager = (IEntityInfoManager *)gameServerFactory(INTERFACEVERSION_ENTITYINFOMANAGER,NULL);
	if ( !entityinfomanager )
	{
		Warning( "Unable to load entityinfomanager, ignoring\n" ); // this isn't fatal, we just won't be able to access entity data
	}

	playerinfomanager = (IPlayerInfoManager *)gameServerFactory(INTERFACEVERSION_PLAYERINFOMANAGER,NULL);
	if ( !playerinfomanager )
	{
		Warning( "Unable to load playerinfomanager, ignoring\n" ); // this isn't fatal, we just won't be able to access specific player data
	}

	botmanager = (IBotManager *)gameServerFactory(INTERFACEVERSION_PLAYERBOTMANAGER, NULL);
	if ( !botmanager )
	{
		Warning( "Unable to load botcontroller, ignoring\n" ); // this isn't fatal, we just won't be able to access specific bot functions
	}
	gameinfomanager = (IGameInfoManager *)gameServerFactory(INTERFACEVERSION_GAMEINFOMANAGER, NULL);
	if (!gameinfomanager)
	{
		Warning( "Unable to load gameinfomanager, ignoring\n" );
	}

	engine = (IVEngineServer*)interfaceFactory(INTERFACEVERSION_VENGINESERVER, NULL);
	gameeventmanager = (IGameEventManager *)interfaceFactory(INTERFACEVERSION_GAMEEVENTSMANAGER,NULL);
	helpers = (IServerPluginHelpers*)interfaceFactory(INTERFACEVERSION_ISERVERPLUGINHELPERS, NULL);
	enginetrace = (IEngineTrace *)interfaceFactory(INTERFACEVERSION_ENGINETRACE_SERVER,NULL);
	randomStr = (IUniformRandomStream *)interfaceFactory(VENGINE_SERVER_RANDOM_INTERFACE_VERSION, NULL);

	if (!engine)
	{
		Msg("DEBUG: ENGINE FAIL!\n");
	}

	if (!gameeventmanager)
	{
		Msg("DEBUG: GAMEEVENTMANAGER FAIL!\n");
	}

	if (!g_pFullFileSystem)
	{
		Msg("DEBUG: g_pFullFileSystem FAIL!\n");
	}

	if (!helpers)
	{
		Msg("DEBUG: helpers FAIL!\n");
	}

	if (!enginetrace)
	{
		Msg("DEBUG: enginetrace FAIL!\n");
	}

	if (!randomStr)
	{
		Msg("DEBUG: randomStr FAIL!\n");
	}

	g_filesystem = (IFileSystem*)interfaceFactory(FILESYSTEM_INTERFACE_VERSION, NULL);

	if (!g_filesystem)
	{
		Msg("DEBUG: g_filesystem FAIL!\n");
	}

	// get the interfaces we want to use
	if(	! ( engine && gameeventmanager && g_pFullFileSystem && helpers && enginetrace && randomStr ) )
	{
		return false; // we require all these interface to function
	}

	if ( playerinfomanager )
	{
		gpGlobals = playerinfomanager->GetGlobalVars();
	}

	MathLib_Init( 2.2f, 2.2f, 0.0f, 2.0f );
	ConVar_Register( 0 );
	return true;
}

//---------------------------------------------------------------------------------
// Purpose: called when the plugin is unloaded (turned off)
//---------------------------------------------------------------------------------
void CEmptyServerPlugin::Unload( void )
{
	gameeventmanager->RemoveListener( this ); // make sure we are unloaded from the event system

	ConVar_Unregister( );
	DisconnectTier2Libraries( );
	DisconnectTier1Libraries( );
}

//---------------------------------------------------------------------------------
// Purpose: called when the plugin is paused (i.e should stop running but isn't unloaded)
//---------------------------------------------------------------------------------
void CEmptyServerPlugin::Pause( void )
{
}

//---------------------------------------------------------------------------------
// Purpose: called when the plugin is unpaused (i.e should start executing again)
//---------------------------------------------------------------------------------
void CEmptyServerPlugin::UnPause( void )
{
}

//---------------------------------------------------------------------------------
// Purpose: the name of this plugin, returned in "plugin_print" command
//---------------------------------------------------------------------------------
const char *CEmptyServerPlugin::GetPluginDescription( void )
{
	return "Emtpy-Plugin V2, Valve";
}

//---------------------------------------------------------------------------------
// Purpose: called on level start
//---------------------------------------------------------------------------------
void CEmptyServerPlugin::LevelInit( char const *pMapName )
{
	Msg( "Level \"%s\" has been loaded\n", pMapName );
	gameeventmanager->AddListener( this, true );
}

//---------------------------------------------------------------------------------
// Purpose: called on level start, when the server is ready to accept client connections
//		edictCount is the number of entities in the level, clientMax is the max client count
//---------------------------------------------------------------------------------
void CEmptyServerPlugin::ServerActivate( edict_t *pEdictList, int edictCount, int clientMax )
{
}

//---------------------------------------------------------------------------------
// Purpose: called once per server frame, do recurring work here (like checking for timeouts)
//---------------------------------------------------------------------------------
void CEmptyServerPlugin::GameFrame( bool simulating )
{
	if ( simulating )
	{
		Bot_RunAll();
	}
}

//---------------------------------------------------------------------------------
// Purpose: called on level end (as the server is shutting down or going to a new map)
//---------------------------------------------------------------------------------
void CEmptyServerPlugin::LevelShutdown( void ) // !!!!this can get called multiple times per map change
{
	gameeventmanager->RemoveListener( this );
}

//---------------------------------------------------------------------------------
// Purpose: called when a client spawns into a server (i.e as they begin to play)
//---------------------------------------------------------------------------------
void CEmptyServerPlugin::ClientActive( edict_t *pEntity )
{
}

//---------------------------------------------------------------------------------
// Purpose: called when a client leaves a server (or is timed out)
//---------------------------------------------------------------------------------
void CEmptyServerPlugin::ClientDisconnect( edict_t *pEntity )
{
}

//---------------------------------------------------------------------------------
// Purpose: called on 
//---------------------------------------------------------------------------------
void CEmptyServerPlugin::ClientPutInServer( edict_t *pEntity, char const *playername )
{
	KeyValues *kv = new KeyValues( "msg" );
	kv->SetString( "title", "Hello" );
	kv->SetString( "msg", "Hello there" );
	kv->SetColor( "color", Color( 255, 0, 0, 255 ));
	kv->SetInt( "level", 5);
	kv->SetInt( "time", 10);
	helpers->CreateMessage( pEntity, DIALOG_MSG, kv, this );
	kv->deleteThis();
}

//---------------------------------------------------------------------------------
// Purpose: called on level start
//---------------------------------------------------------------------------------
void CEmptyServerPlugin::SetCommandClient( int index )
{
	m_iClientCommandIndex = index;
}

void ClientPrint( edict_t *pEdict, char *format, ... )
{
	va_list		argptr;
	static char		string[1024];
	
	va_start (argptr, format);
	Q_vsnprintf(string, sizeof(string), format,argptr);
	va_end (argptr);

	engine->ClientPrintf( pEdict, string );
}
//---------------------------------------------------------------------------------
// Purpose: called on level start
//---------------------------------------------------------------------------------
void CEmptyServerPlugin::ClientSettingsChanged( edict_t *pEdict )
{
	if ( playerinfomanager )
	{
		IPlayerInfo *playerinfo = playerinfomanager->GetPlayerInfo( pEdict );

		const char * name = engine->GetClientConVarValue( engine->IndexOfEdict(pEdict), "name" );

		// CAN'T use Q_stricmp here, this dll is made by 3rd parties and may not link to tier0/vstdlib
		if ( playerinfo && name && playerinfo->GetName() && 
			 stricmp( name, playerinfo->GetName()) ) // playerinfo may be NULL if the MOD doesn't support access to player data 
													   // OR if you are accessing the player before they are fully connected
		{
			ClientPrint( pEdict, "Your name changed to \"%s\" (from \"%s\"\n", name, playerinfo->GetName() );
						// this is the bad way to check this, the better option it to listen for the "player_changename" event in FireGameEvent()
						// this is here to give a real example of how to use the playerinfo interface
		}
	}
}

//---------------------------------------------------------------------------------
// Purpose: called when a client joins a server
//---------------------------------------------------------------------------------
PLUGIN_RESULT CEmptyServerPlugin::ClientConnect( bool *bAllowConnect, edict_t *pEntity, const char *pszName, const char *pszAddress, char *reject, int maxrejectlen )
{
	return PLUGIN_CONTINUE;
}

CON_COMMAND( DoAskConnect, "Server plugin example of using the ask connect dialog" )
{
	if ( args.ArgC() < 2 )
	{
		Warning ( "DoAskConnect <server IP>\n" );
	}
	else
	{
		const char *pServerIP = args.Arg( 1 );

		KeyValues *kv = new KeyValues( "menu" );
		kv->SetString( "title", pServerIP );	// The IP address of the server to connect to goes in the "title" field.
		kv->SetInt( "time", 3 );

		for ( int i=1; i < gpGlobals->maxClients; i++ )
		{
			edict_t *pEdict = engine->PEntityOfEntIndex( i );
			if ( pEdict )
			{
				helpers->CreateMessage( pEdict, DIALOG_ASKCONNECT, kv, &g_EmtpyServerPlugin );
			}
		}

		kv->deleteThis();
	}
}

#ifdef SAMPLE_TF2_PLUGIN
const char *classNames[] =
{
	"unknown",
	"scout",
	"sniper",
	"soldier",
	"demoman",
	"medic",
	"heavy weapons guy",
	"pyro",
	"spy",
	"engineer",
};

bool TFPlayerHasCondition( int inBits, int condition )
{
	Assert( condition >= 0 && condition < TF_COND_LAST );

	return ( ( inBits & (1<<condition) ) != 0 );
}

void SentryStatus( edict_t *pEntity )
{
	IPlayerInfo *playerinfo = playerinfomanager->GetPlayerInfo( pEntity );
	if (!playerinfo)
	{
		Msg("couldn't get playerinfo\n");
		return;
	}

	Msg("Sentry Status:\n");
	pluginvariant value;
	pluginvariant emptyVariant;
	edict_t *pSentry = NULL;
	if (playerinfo->GetCustomInfo(TFPLAYERINFO_ENTINDEX_SENTRY, value, emptyVariant))
	{
		pSentry = engine->PEntityOfEntIndex( value.Int() );
		if (!pSentry)
		{
			Warning("couldn't attain sentry gun entity\n");
			return;
		}
	}
	else
	{
		Msg("No Sentrygun built.\n");
		return;

	}
	IEntityInfo *entinfo = entityinfomanager->GetEntityInfo( pSentry );
	if (!entinfo)
	{
		Warning("couldn't get entinfo for sentry gun\n");
		return;
	}

	if (playerinfo->GetCustomInfo(TFPLAYERINFO_BUILDING_SENTRY, value, emptyVariant))
	{
		if (value.Bool())
			Msg("Sentry Under Construction...\n");
	}
	if (playerinfo->GetCustomInfo(TFPLAYERINFO_UPGRADING_SENTRY, value, emptyVariant))
	{
		if (value.Bool())
			Msg("Sentry Upgrading...\n");
	}

	int sentryLevel = 0;
	if (playerinfo->GetCustomInfo(TFPLAYERINFO_SENTRY_LEVEL, value, emptyVariant))
	{
		sentryLevel = value.Int();
		Msg("Sentry Level: %i\n", sentryLevel );
	}
	else
		Msg("Unable to retrive sentry level\n");

	if (playerinfo->GetCustomInfo(TFPLAYERINFO_SENTRY_PROGRESS, value, emptyVariant))
	{
		if (sentryLevel < 3)
		{
			int iMetal, iRequiredMetal;
			iRequiredMetal = value.Int() & 0xFF;
			iMetal = (value.Int()>>8) & 0xFF;
			Msg("%i / %i Metal Required for Sentry Level %i\n", iMetal, iRequiredMetal, sentryLevel+1);
		}
		else
			Msg("Sentry cannot be upgraded further.\n");
	}

	Msg("Health: %i\n", entinfo->GetHealth() );

	if (playerinfo->GetCustomInfo(TFPLAYERINFO_SENTRY_KILLS, value, emptyVariant))
		Msg("Kills: %i\n", value.Int() );
	else
		Msg("Unable to retrieve sentry kills\n");

	if (playerinfo->GetCustomInfo(TFPLAYERINFO_SENTRY_AMMO_SHELLS, value, emptyVariant))
	{
		int iShells, iMaxShells;
		iMaxShells = value.Int() & 0xFF;
		iShells = (value.Int()>>8) & 0xFF;
		Msg("Shells: %i / %i\n", iShells, iMaxShells);
	}
	if (sentryLevel > 2)
	{
		if (playerinfo->GetCustomInfo(TFPLAYERINFO_SENTRY_AMMO_ROCKETS, value, emptyVariant))
		{
			int iRockets, iMaxRockets;
			iMaxRockets = value.Int() & 0xFF;
			iRockets = (value.Int()>>8) & 0xFF;
			Msg("Rockets: %i / %i\n", iRockets, iMaxRockets);
		}
	}

}
void DispenserStatus( edict_t *pEntity )
{
	IPlayerInfo *playerinfo = playerinfomanager->GetPlayerInfo( pEntity );
	if (!playerinfo)
	{
		Msg("couldn't get playerinfo\n");
		return;
	}

	Msg("Dispenser Status:\n");
	pluginvariant value;
	pluginvariant emptyVariant;
	edict_t *pDispenser = NULL;
	if (playerinfo->GetCustomInfo(TFPLAYERINFO_ENTINDEX_DISPENSER, value, emptyVariant))
	{
		pDispenser = engine->PEntityOfEntIndex( value.Int() );
		if (!pDispenser)
		{
			Warning("couldn't attain dispenser entity\n");
			return;
		}
	}
	else
	{
		Msg("No dispenser built.\n");
		return;
	}
	IEntityInfo *entinfo = entityinfomanager->GetEntityInfo( pDispenser );
	if (!entinfo)
	{
		Warning("couldn't get entinfo for dispenser\n");
		return;
	}
	if (playerinfo->GetCustomInfo(TFPLAYERINFO_BUILDING_DISPENSER, value, emptyVariant))
	{
		if (value.Bool())
			Msg("Dispenser Under Construction...\n");
	}
	Msg("Health: %i\n", entinfo->GetHealth() );
	if (playerinfo->GetCustomInfo(TFPLAYERINFO_DISPENSER_METAL, value, emptyVariant))
		Msg("Metal: %i\n", value.Int() );
}
void TeleporterStatus( edict_t *pEntity )
{
	IPlayerInfo *playerinfo = playerinfomanager->GetPlayerInfo( pEntity );
	if (!playerinfo)
	{
		Msg("couldn't get playerinfo\n");
		return;
	}

	Msg("Teleporter Status:\n");

	pluginvariant value;
	pluginvariant emptyVariant;
	edict_t *pEntrance = NULL;
	edict_t *pExit = NULL;
	if (playerinfo->GetCustomInfo(TFPLAYERINFO_ENTINDEX_TELEPORTER_ENTRANCE, value, emptyVariant))
	{
		pEntrance = engine->PEntityOfEntIndex( value.Int() );
		if (!pEntrance)
		{
			Warning("couldn't attain entrance entity\n");
		}
	}
	else
	{
		Msg("No Teleporter Entrance built.\n");
	}
	if (playerinfo->GetCustomInfo(TFPLAYERINFO_ENTINDEX_TELEPORTER_EXIT, value, emptyVariant))
	{
		pExit = engine->PEntityOfEntIndex( value.Int() );
		if (!pExit)
		{
			Warning("couldn't attain exit entity\n");
		}
	}
	else
	{
		Msg("No Teleporter Entrance built.\n");
	}
	IEntityInfo *entranceInfo = entityinfomanager->GetEntityInfo( pEntrance );
	if (!entranceInfo)
	{
		Warning("couldn't get entinfo for teleporter entrance\n");
	}
	IEntityInfo *exitInfo = entityinfomanager->GetEntityInfo( pExit );
	if (!exitInfo)
	{
		Warning("couldn't get entinfo for teleporter exit\n");
	}

	if (pEntrance && entranceInfo)
	{
		if (playerinfo->GetCustomInfo(TFPLAYERINFO_BUILDING_TELEPORTER_ENTRANCE, value, emptyVariant))
		{
			if (value.Bool())
				Msg("Entrance Under Construction...\n");
		}
		Msg("Entrance Health: %i\n", entranceInfo->GetHealth() );
		if (playerinfo->GetCustomInfo(TFPLAYERINFO_TELEPORTER_USES, value, emptyVariant))
			Msg("Entrance Used %i Times.\n", value.Int() );

	}
	if (pExit && exitInfo)
	{
		if (playerinfo->GetCustomInfo(TFPLAYERINFO_BUILDING_TELEPORTER_EXIT, value, emptyVariant))
		{
			if (value.Bool())
				Msg("Exit Under Construction...\n");
		}
		Msg("Exit Health: %i\n", exitInfo->GetHealth() );
	}
}
void ClassStatus( edict_t *pEntity )
{
	IPlayerInfo *playerinfo = playerinfomanager->GetPlayerInfo( pEntity );
	if (!playerinfo)
	{
		Msg("couldn't get playerinfo\n");
		return;
	}
	int playerClassId = playerinfo->GetPlayerClassId();

	Msg("Player Class: %s\n", playerinfo->GetPlayerClassName());
	pluginvariant conditionValue;
	pluginvariant emptyVariant;
	if (!playerinfo->GetCustomInfo(TFPLAYERINFO_CONDITIONS, conditionValue, emptyVariant))
	{
		Warning("unable to retrieve conditions!\n");
	}
	if (TFPlayerHasCondition(conditionValue.Int(), TF_COND_INVULNERABLE ))
		Msg("You are Invulnerable!\n");
	if (TFPlayerHasCondition(conditionValue.Int(), TF_COND_SELECTED_TO_TELEPORT ))
		Msg("You are about to Teleport.\n");
	if (TFPlayerHasCondition(conditionValue.Int(), TF_COND_TELEPORTED ))
		Msg("You have recently been teleported.\n");

	switch(playerClassId)
	{
	default:
	case TF_CLASS_MEDIC:
		break;
	case TF_CLASS_ENGINEER:
		Msg("Building Information:\n");
		SentryStatus( pEntity );
		DispenserStatus( pEntity );
		TeleporterStatus( pEntity );
		break;
	case TF_CLASS_SPY:
		{
			int disguiseClass = 0;
			pluginvariant value;

			if (playerinfo->GetCustomInfo(TFPLAYERINFO_SPY_DISGUISEDAS, value, emptyVariant))
				disguiseClass = value.Int();

			if ( TFPlayerHasCondition(conditionValue.Int(), TF_COND_DISGUISING ) )
				Msg("Disguising..\n");
			else if (TFPlayerHasCondition(conditionValue.Int(), TF_COND_DISGUISED ) )
				Msg("Disguised as: %s\n", classNames[disguiseClass] );

			if (TFPlayerHasCondition(conditionValue.Int(), TF_COND_STEALTHED ))
				Msg("Cloaked!\n");
			if (playerinfo->GetCustomInfo(TFPLAYERINFO_SPY_CLOAKCHARGELEVEL, value, emptyVariant))
				Msg("Cloak Charge Percent: %d\n", value.Float() );

			break;
		}
	case TF_CLASS_DEMOMAN:
		break;
	}
}
const char *ctf_flagtype[] =
{
	"ctf",					//TF_FLAGTYPE_CTF = 0,
	"attack / defend",		//TF_FLAGTYPE_ATTACK_DEFEND,
	"territory control",	//TF_FLAGTYPE_TERRITORY_CONTROL,
	"invade",				//TF_FLAGTYPE_INVADE,
	"king of the hill",		//TF_FLAGTYPE_KINGOFTHEHILL,
};
const char *ctf_flagstatus[] =
{
	"unknown",
	"At Home",
	"Dropped",
	"Stolen",
};
void FlagStatus( edict_t *pPlayer )
{
	IPlayerInfo *pInfo = playerinfomanager->GetPlayerInfo( pPlayer );
	if (!pInfo)
	{
		Msg( "couldn't get playerinfo\n" );
		return;
	}
	IGameInfo *gameInfo = gameinfomanager->GetGameInfo();
	if (!gameInfo)
	{
		Msg( "couldn't get gameinfo\n" );
	}

	int gameType = gameInfo->GetInfo_GameType();

	if (gameType != 1)
	{
		Msg( "Game is not CTF.\n" );
		return;
	}
	Msg( "===============================\n" );
	Msg( "Capture The Flag -- Flag Status\n" );
	Msg( "===============================\n" );
	pluginvariant value, options;

	edict_t *pFlag = NULL;
	while ( (pFlag = entityinfomanager->FindEntityByClassname(pFlag, "item_teamflag")) != NULL )
	{
		IEntityInfo *pFlagInfo = entityinfomanager->GetEntityInfo( pFlag );
		if (!pFlagInfo)
			continue;

		Msg( "\nTeam %s's Flag\n",	gameInfo->GetInfo_GetTeamName( pFlagInfo->GetTeamIndex() ) );
		options.SetInt(engine->IndexOfEdict(pFlag));
		if ( gameInfo->GetInfo_Custom( TFGAMEINFO_CTF_FLAG_TYPE, value, options) )
			Msg( "Type: %s\n", ctf_flagtype[value.Int()] );
		if ( gameInfo->GetInfo_Custom( TFGAMEINFO_CTF_FLAG_STATUS, value, options) )
		{
			Msg( "Status: %s\n", ctf_flagstatus[value.Int()] );
			//Tony; if we're carried, find out who has us.
			if (value.Int() == 3)
			{
				edict_t *pPlayer = pFlagInfo->GetOwner();
				if (pPlayer)
				{
					IPlayerInfo *pPlayerInfo = playerinfomanager->GetPlayerInfo( pPlayer );
					if (pPlayerInfo)
						Msg( "Carried by: %s\n", pPlayerInfo->GetName() );
				}
			}
		}
	}


	Msg( "===============================\n" );
}
#endif

//---------------------------------------------------------------------------------
// Purpose: called when a client types in a command (only a subset of commands however, not CON_COMMAND's)
//---------------------------------------------------------------------------------
PLUGIN_RESULT CEmptyServerPlugin::ClientCommand( edict_t *pEntity, const CCommand &args )
{
	const char *pcmd = args[0];

	if ( !pEntity || pEntity->IsFree() ) 
	{
		return PLUGIN_CONTINUE;
	}

	if ( FStrEq( pcmd, "menu" ) )
	{
		KeyValues *kv = new KeyValues( "menu" );
		kv->SetString( "title", "You've got options, hit ESC" );
		kv->SetInt( "level", 1 );
		kv->SetColor( "color", Color( 255, 0, 0, 255 ));
		kv->SetInt( "time", 20 );
		kv->SetString( "msg", "Pick an option\nOr don't." );
		
		for( int i = 1; i < 9; i++ )
		{
			char num[10], msg[10], cmd[10];
			Q_snprintf( num, sizeof(num), "%i", i );
			Q_snprintf( msg, sizeof(msg), "Option %i", i );
			Q_snprintf( cmd, sizeof(cmd), "option%i", i );

			KeyValues *item1 = kv->FindKey( num, true );
			item1->SetString( "msg", msg );
			item1->SetString( "command", cmd );
		}

		helpers->CreateMessage( pEntity, DIALOG_MENU, kv, this );
		kv->deleteThis();
		return PLUGIN_STOP; // we handled this function
	}
	else if ( FStrEq( pcmd, "rich" ) )
	{
		KeyValues *kv = new KeyValues( "menu" );
		kv->SetString( "title", "A rich message" );
		kv->SetInt( "level", 1 );
		kv->SetInt( "time", 20 );
		kv->SetString( "msg", "This is a long long long text string.\n\nIt also has line breaks." );
		
		helpers->CreateMessage( pEntity, DIALOG_TEXT, kv, this );
		kv->deleteThis();
		return PLUGIN_STOP; // we handled this function
	}
	else if ( FStrEq( pcmd, "msg" ) )
	{
		KeyValues *kv = new KeyValues( "menu" );
		kv->SetString( "title", "Just a simple hello" );
		kv->SetInt( "level", 1 );
		kv->SetInt( "time", 20 );
		
		helpers->CreateMessage( pEntity, DIALOG_MSG, kv, this );
		kv->deleteThis();
		return PLUGIN_STOP; // we handled this function
	}
	else if ( FStrEq( pcmd, "entry" ) )
	{
		KeyValues *kv = new KeyValues( "entry" );
		kv->SetString( "title", "Stuff" );
		kv->SetString( "msg", "Enter something" );
		kv->SetString( "command", "say" ); // anything they enter into the dialog turns into a say command
		kv->SetInt( "level", 1 );
		kv->SetInt( "time", 20 );
		
		helpers->CreateMessage( pEntity, DIALOG_ENTRY, kv, this );
		kv->deleteThis();
		return PLUGIN_STOP; // we handled this function		
	}
#ifdef SAMPLE_TF2_PLUGIN
	else if ( FStrEq( pcmd, "gameinfo" ) )
	{
		IGameInfo *gameInfo = gameinfomanager->GetGameInfo();
		if (!gameInfo)
			return PLUGIN_STOP;

		Msg("=== Game Information ===\n");
		Msg("Game Type: %i / %s\n", gameInfo->GetInfo_GameType(), gameInfo->GetInfo_GameTypeName() );
		int teamCount = gameInfo->GetInfo_GetTeamCount();
		Msg("Num Teams: %i\n", teamCount );

		Msg("Player Counts:\n");
		for (int i = 0;i<teamCount;i++)
		{
			//If this failes, we can assume the rest is invalid too.
			if (!gameInfo->GetInfo_GetTeamName(i) )
				continue;
			Msg("Team: %s, Players: %i\n", gameInfo->GetInfo_GetTeamName(i), gameInfo->GetInfo_NumPlayersOnTeam(i) );
		}
		return PLUGIN_STOP;

	}
	// Sample to use the new CustomInfo added to TF2 for plugins
	else if ( FStrEq( pcmd, "tfcond" ) )
	{
		IPlayerInfo *playerinfo = playerinfomanager->GetPlayerInfo( pEntity );
		if (!playerinfo)
			return PLUGIN_STOP;

		pluginvariant conditionValue;
		pluginvariant emptyVariant;
		if (!playerinfo->GetCustomInfo(TFPLAYERINFO_CONDITIONS, conditionValue, emptyVariant))
		{
			Msg("unable to retrieve conditions!\n");
			return PLUGIN_STOP;
		}

		Msg("Disguising?: %s\n", TFPlayerHasCondition(conditionValue.Int(), TF_COND_DISGUISING ) ? "yes" : "no" );
		Msg("Disguised?: %s\n", TFPlayerHasCondition(conditionValue.Int(), TF_COND_DISGUISED ) ? "yes" : "no" );
		Msg("Stealthed?: %s\n", TFPlayerHasCondition(conditionValue.Int(), TF_COND_STEALTHED ) ? "yes" : "no" );
		Msg("Invulnerable?: %s\n", TFPlayerHasCondition(conditionValue.Int(), TF_COND_INVULNERABLE ) ? "yes" : "no" );
		Msg("Teleported Recently?: %s\n", TFPlayerHasCondition(conditionValue.Int(), TF_COND_TELEPORTED ) ? "yes" : "no" );
		Msg("Selected for Teleportation?: %s\n", TFPlayerHasCondition(conditionValue.Int(), TF_COND_SELECTED_TO_TELEPORT ) ? "yes" : "no" );
		Msg("On Fire?: %s\n", TFPlayerHasCondition(conditionValue.Int(), TF_COND_BURNING ) ? "yes" : "no" );

		return PLUGIN_STOP;
	}
	else if ( FStrEq( pcmd, "sentry_status" ) )
	{
		SentryStatus(pEntity);
		return PLUGIN_STOP;
	}
	else if ( FStrEq( pcmd, "class_status" ) )
	{
		ClassStatus(pEntity);
		return PLUGIN_STOP;
	}
	else if ( FStrEq( pcmd, "flag_status" ) )
	{
		FlagStatus(pEntity);
		return PLUGIN_STOP;
	}
	#ifdef GAME_DLL
		else if ( FStrEq( pcmd, "cbe_test" ) )
		{
			IPlayerInfo *playerinfo = playerinfomanager->GetPlayerInfo( pEntity );
			if (!playerinfo)
				return PLUGIN_STOP;

			CBaseEntity *pEnt = static_cast< CBaseEntity* >(entityinfomanager->GetEntity( pEntity ));
			if (pEnt)
				Msg("got a pointer to CBaseEntity..\n");
			Msg("attempting to print this entities modelname directly..\n");

			Msg("ModelName: %s\n", STRING(pEnt->GetModelName()) );

			return PLUGIN_STOP;
		}
	#endif
#endif


	return PLUGIN_CONTINUE;
}

//---------------------------------------------------------------------------------
// Purpose: called when a client is authenticated
//---------------------------------------------------------------------------------
PLUGIN_RESULT CEmptyServerPlugin::NetworkIDValidated( const char *pszUserName, const char *pszNetworkID )
{
	return PLUGIN_CONTINUE;
}

//---------------------------------------------------------------------------------
// Purpose: called when a cvar value query is finished
//---------------------------------------------------------------------------------
void CEmptyServerPlugin::OnQueryCvarValueFinished( QueryCvarCookie_t iCookie, edict_t *pPlayerEntity, EQueryCvarValueStatus eStatus, const char *pCvarName, const char *pCvarValue )
{
	Msg( "Cvar query (cookie: %d, status: %d) - name: %s, value: %s\n", iCookie, eStatus, pCvarName, pCvarValue );
}
void CEmptyServerPlugin::OnEdictAllocated( edict_t *edict )
{
}
void CEmptyServerPlugin::OnEdictFreed( const edict_t *edict  )
{
}

//---------------------------------------------------------------------------------
// Purpose: called when an event is fired
//---------------------------------------------------------------------------------
void CEmptyServerPlugin::FireGameEvent( KeyValues * event )
{
	const char * name = event->GetName();
	Msg( "CEmptyServerPlugin::FireGameEvent: Got event \"%s\"\n", name );
}

//---------------------------------------------------------------------------------
// Purpose: an example of how to implement a new command
//---------------------------------------------------------------------------------
CON_COMMAND( empty_version, "prints the version of the empty plugin" )
{
	Msg( "Version:2.0.0.0\n" );
}

CON_COMMAND( empty_log, "logs the version of the empty plugin" )
{
	engine->LogPrint( "Version:2.0.0.0\n" );
}

//---------------------------------------------------------------------------------
// Purpose: an example cvar
//---------------------------------------------------------------------------------
static ConVar empty_cvar("plugin_empty", "0", FCVAR_NOTIFY, "Example plugin cvar");
Current Situation
It is currently a little bit frustrating especially if a metamod/sourcemod plugin would be so much easier. But this is unfortunately not an option right now .

Question
Can you guys give me a hint where to look next to tackle the problem? I've look already in the source code of metamod but this haven't helped me regarding my problem. The documentation of VSP is very limited and there are not many open source VSP either.

Reward
When we could tackle the problem together and get the plugin running I would gladly donate some money to the AlliedModders project.

Best regards and thank you
Flavy
flavy666 is offline
asherkin
SourceMod Developer
Join Date: Aug 2009
Location: OnGameFrame()
Old 09-30-2017 , 10:07   Re: [CSGO] Getting the serverplugin_empty running
Reply With Quote #2

Quote:
Originally Posted by flavy666 View Post
  1. Downloaded the source2013-sdk from github
Use our CS:GO SDK: https://github.com/alliedmodders/hl2sdk/tree/csgo

The MM:S loader uses the Filesystem interface, and is probably the best reference.

You should really be using MM:S, it adds no overhead, and fixes a lot of fundamental problems with the VSP interface for you (while still allowing you to use the IServerPluginCallbacks interface if you wish.)
__________________

Last edited by asherkin; 09-30-2017 at 10:10.
asherkin is offline
flavy666
New Member
Join Date: Sep 2017
Old 10-01-2017 , 10:24   Re: [CSGO] Getting the serverplugin_empty running
Reply With Quote #3

Hi asherkin,

Regarding the problem
I've discarded the source sdk 2013 in favor of the hl2sdk/csgo one. Therefore I installed VS 2010 Express copied the serverplugin_empty from the original source sdk into the hl2sdk/csgo one. Needed to add a new VS2010 empty project, added all source files and also adapted all include/library paths until it build and linked without errors.

Result was the same: The server crashed. So I started to remove everything from the serverplugin_empty source code except the load method where the pointer to the sdk's interfaces are acquired. Still server crashes. So I started to comment everything out of the load method step by step. When commenting out the ConnectTier2Libraries( &interfaceFactory, 1 ); function call the plugin stops to crash. Now I need to create the interfaces on my own by calling the passed interfaceFactory and gameServerFactory function pointers which suddenly also works without any problems.

Root cause seems to be a faulty tier2 ConnectTier2Libraries implementation of valve which is leading to seg faults. Pretty annoying but I'm glad I found the issue (after several days / the hard way).

Until now the first implementation (gathering connected players and their current money) works fine. Next step would be the event interface.

Regarding the MM:S usage
This is not an technical/logical discussion with the server admins any more. This is much more an emotional argument and the admins are really stubborn at this point.

You know, I know, probably the admins also know that MM:S/sourcemod plugins would be the technically best solution for this issue but at this point they would never admit it

Nevertheless thanks for assisting me with my problem. As promised I donated a little bit. So keep up the good work guys. I'm gonna contact this forum again if I ran into another problem but currently it seems very promising that this works as planed.

Best regards
Flavy
flavy666 is offline
Reply


Thread Tools
Display Modes

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 18:18.


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