View Single Post
Re: New SDK Bot for Source
Old
  (#14)
Pierre-Marie Baty
Roi de France
 
Pierre-Marie Baty's Avatar
 
Status: Offline
Posts: 5,049
Join Date: Nov 2003
Location: 46°43'60N 0°43'0W 0.187A
Default Re: New SDK Bot for Source - 22-01-2005

If some of you want to understand EXACTLY (hopefully, with enough comments) how the plugin interface works, here's a minimalistic bot plugin that fits in 1 file

I'll from now use that as a template to port some of my plugins (like PMTools) to Source

Code:
///////////////////////////////////////////////////////////////
// Minimalistic Source plugin suitable for bots and most addons
// Wrapped together by Pierre-Marie Baty from Bots-United
// http://forums.bots-united.com
 
#include <stdio.h>
 
 
#define PLUGIN_DESCRIPTION "Bot plugin Source template"
#define WrapAngle(a) ((a) == 180 ? -180 : ((a) >= 180 ? (a) - 360 * abs (((int) (a) + 180) / 360) : ((a) < -180 ? (a) + 360 * abs (((int) (a) - 180) / 360) : (a))))
 
 
///////////////////////////////////////////////////////////////////////////////////////
// START of plugin interface
 
 
#include "interface.h"
#include "filesystem.h"
#undef VECTOR_NO_SLOW_OPERATIONS
#include "vector.h"
#include "edict.h"
#include "engine/iserverplugin.h"
#include "dlls/iplayerinfo.h"
#include "eiface.h"
#include "igameevents.h"
#include "convar.h"
#include "icvar.h"
#include "engine/IEngineTrace.h"
#include "../../game_shared/in_buttons.h"
#include "../../game_shared/shareddefs.h"
#include "tier0/memdbgon.h"// memdbgon must be the last include file in a .cpp file!!!
 
 
// plugin interface virtual function table pointers
IVEngineServer *engine = NULL; // helper engine functions
IFileSystem *filesystem = NULL; // file I/O 
IGameEventManager *gameeventmanager = NULL; // game events interface
IPlayerInfoManager *playerinfomanager = NULL; // game dll interface to interact with players
IBotManager *botmanager = NULL; // game dll interface to interact with bots
IServerPluginHelpers *helpers = NULL; // special 3rd party plugin helpers from the engine
IEngineTrace *enginetrace = NULL; // engine trace facilities (lines and hulls)
ICvar *serverconsole = NULL; // plugin's own console access
CGlobalVars *gpGlobals = NULL; // server globals
 
 
// plugin interface classes
class CConCommandAccessor: public IConCommandBaseAccessor
{
public:
   virtual bool RegisterConCommandBase (ConCommandBase *pCommand)
   {
		pCommand->AddFlags (FCVAR_PLUGIN);
		pCommand->SetNext (0); // Unlink from plugin only list
		serverconsole->RegisterConCommandBase (pCommand); // Link to engine's list instead
		return (true);
   }
};
CConCommandAccessor g_ConVarAccessor; // server console commands AND cvars accessor
 
 
class CPlugin: public IServerPluginCallbacks, public IGameEventListener
{
public:
   // IServerPluginCallbacks interface
   virtual bool Load (CreateInterfaceFn interfaceFactory, CreateInterfaceFn gameServerFactory)
   {
		// get the interfaces we want to use
		if (!(playerinfomanager = (IPlayerInfoManager *) gameServerFactory (INTERFACEVERSION_PLAYERINFOMANAGER,NULL))
			|| !(botmanager = (IBotManager *) gameServerFactory (INTERFACEVERSION_PLAYERBOTMANAGER, NULL))
			|| !(engine = (IVEngineServer *) interfaceFactory (INTERFACEVERSION_VENGINESERVER, NULL))
			|| !(gameeventmanager = (IGameEventManager *) interfaceFactory (INTERFACEVERSION_GAMEEVENTSMANAGER,NULL))
			|| !(filesystem = (IFileSystem *) interfaceFactory (FILESYSTEM_INTERFACE_VERSION, NULL)) // FIXME: not needed for bots perhaps ?
			|| !(helpers = (IServerPluginHelpers *) interfaceFactory (INTERFACEVERSION_ISERVERPLUGINHELPERS, NULL))
			|| !(enginetrace = (IEngineTrace *) interfaceFactory (INTERFACEVERSION_ENGINETRACE_SERVER,NULL))
			|| !(serverconsole = (ICvar *) interfaceFactory (VENGINE_CVAR_INTERFACE_VERSION, NULL)))
			return (false); // we require all these interface to function
 
		gpGlobals = playerinfomanager->GetGlobalVars (); // get a pointer to the engine's globalvars
		ConCommandBaseMgr::OneTimeInit (&g_ConVarAccessor); // register any cvars and server commands
 
		return (true); // return true so as to enable the engine to load this plugin
   }
   virtual void Unload (void) { gameeventmanager->RemoveListener (this); }
   virtual void Pause (void) { }
   virtual void UnPause (void) { }
   virtual const char *GetPluginDescription (void) { return (PLUGIN_DESCRIPTION); }
   virtual void LevelInit (char const *pMapName) { gameeventmanager->AddListener (this, true); }
   virtual void ServerActivate (edict_t *pEdictList, int edictCount, int clientMax) { }
   virtual void GameFrame (bool simulating); // <-- define this one
   virtual void LevelShutdown (void) { gameeventmanager->RemoveListener (this); }
   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) { return (PLUGIN_CONTINUE); }
   virtual PLUGIN_RESULT ClientCommand (edict_t *pEntity) { return (PLUGIN_CONTINUE); }
   virtual PLUGIN_RESULT NetworkIDValidated (const char *pszUserName, const char *pszNetworkID) { return (PLUGIN_CONTINUE); }
 
   // IGameEventListener interface
   virtual void FireGameEvent (KeyValues * event); // <-- define this one
};
 
// tell the engine to create a plugin interface instance for us
EXPOSE_SINGLE_INTERFACE (CPlugin, IServerPluginCallbacks, INTERFACEVERSION_ISERVERPLUGINCALLBACKS);
 
// END of plugin interface
///////////////////////////////////////////////////////////////////////////////////////
 
 
typedef struct
{
   edict_t *pEdict;
   bool is_started;
   float f_crouch_time;
   float f_jump_time;
   float f_forward_time;
   float f_backwards_time;
   float f_strafeleft_time;
   float f_straferight_time;
   Vector v_angles;
   IBotController *m_BotInterface;
   IPlayerInfo *m_PlayerInfo;
   CBotCmd cmd;
} bot_t;
 
// global variables
bot_t bots[64];
int bot_count;
 
// prototypes
void Bot_RunAll (void);
void BotThink (bot_t *pBot);
void BotSelectTeamAndClass (bot_t *pBot);
 
// plugin server command handler
CON_COMMAND (bot, "bot server command handler")
{
   char cmd[128];
   char arg1[128];
   char arg2[128];
   char arg3[128];
 
   if (botmanager == NULL)
	  return; // reliability check
 
   sprintf (cmd, engine->Cmd_Argv (0));
   sprintf (arg1, engine->Cmd_Argv (1));
   sprintf (arg2, engine->Cmd_Argv (2));
   sprintf (arg3, engine->Cmd_Argv (3));
 
   // have we been requested to add a bot ?
   if (strcmp (arg1, "add") == 0)
   {
	  bots[bot_count].pEdict = botmanager->CreateBot ("w00t w00t");
	  if (bots[bot_count].pEdict != NULL)
	  {
		  bots[bot_count].m_BotInterface = botmanager->GetBotController (bots[bot_count].pEdict);
		  bots[bot_count].m_PlayerInfo = playerinfomanager->GetPlayerInfo (bots[bot_count].pEdict);
		  bots[bot_count].is_started = false; // need to select team and class
	  }
	  bot_count++;
   }
 
   // else have we been asked to kick a bot ?
   else if (strcmp (arg1, "kick") == 0)
   {
	  ; // bleh
   }
 
   // etc...
}
 
void CPlugin::GameFrame (bool simulating)
{
   if (simulating)
	  Bot_RunAll ();
}
 
void CPlugin::FireGameEvent (KeyValues *event)
{
   // do what you want here to catch any game event (check the keyvalue data)
   //Msg ("FireGameEvent: Got event \"%s\"\n", event->GetName ());
}
 
void Bot_RunAll (void)
{
   if (!botmanager)
	  return;
   for (int i = 0; i < bot_count; i++)
	  BotThink (&bots[i]);
}
 
void BotSelectTeamAndClass (bot_t *pBot)
{
   // this is of course game-specific
   helpers->ClientCommand (pBot->pEdict, "joingame");
   helpers->ClientCommand (pBot->pEdict, "jointeam 3");
   helpers->ClientCommand (pBot->pEdict, "joinclass 0");
}
 
void BotThink (bot_t *pBot)
{
   // Run this Bot's AI for one frame.
   memset (&pBot->cmd, 0, sizeof (pBot->cmd));
 
   // is the bot not started yet ?
   if (!pBot->is_started)
	  BotSelectTeamAndClass (pBot); // if so, select team and class
 
   // is the bot dead ?
   if (pBot->m_PlayerInfo->IsDead ())
	  return; // don't do anything, wait for new round (note: on other mods, press attack)
 
   // bot AI here
 
   // finally, ask the engine to perform the bot client movement
   pBot->m_BotInterface->RunPlayerMove (&pBot->cmd);
}



RACC home - Bots-United: beer, babies & bots (especially the latter)
"Learn to think by yourself, else others will do it for you."

Last edited by Pierre-Marie Baty; 22-01-2005 at 02:22..
  
Reply With Quote