Gone are the days of hacked monsters for bots!
In order to create bots in server side doom, here's what I'm doing:-
1) Wrapping the idNetworkSystem API, so that calls to send packets to bot-clients does not happen. This is why previous attempts within the community seem to have failed, as doom 3 does not private a true mechanism for setting a client to be fake (i.e. bot)
2) Code in multiplayerGame's Run() function to call each bot in the game and run it's think() function.
3) A cvar to spawn a bot using one of the upper half of the client numbers (the SDK code defines MAX_CLIENTs as 32, so bot 1 spawns as client 16, so it doesnt conflict with spawning players, who will take up the bottom 8 id's)
In game_local.cpp, swap the line 69:
networkSystem = import->networkSystem;
for:-
networkSystem = new botNetworkSystem(import->networkSystem);
and add:
#include "../botNetwork.h"
to the top after the #pragma hdrstop
Create this file (botNetwork.h) defining your network wrapper class:-
Code:
#ifndef __BOTNETWORK__
#define __BOTNETWORK__ 1
#define BOT_START_INDEX 16
class botNetworkSystem
{
public:
botNetworkSystem (idNetworkSystem *networkSystem);
void ServerSendReliableMessage( int clientNum, const idBitMsg &msg );
void ServerSendReliableMessageExcluding( int clientNum, const idBitMsg &msg );
int ServerGetClientPing( int clientNum );
int ServerGetClientPrediction( int clientNum );
int ServerGetClientTimeSinceLastPacket( int clientNum );
int ServerGetClientTimeSinceLastInput( int clientNum );
int ServerGetClientOutgoingRate( int clientNum );
int ServerGetClientIncomingRate( int clientNum );
float ServerGetClientIncomingPacketLoss( int clientNum );
void ClientSendReliableMessage( const idBitMsg &msg );
int ClientGetPrediction( void );
int ClientGetTimeSinceLastPacket( void );
int ClientGetOutgoingRate( void );
int ClientGetIncomingRate( void );
float ClientGetIncomingPacketLoss( void );
private:
idNetworkSystem *network;
};
extern botNetworkSystem * networkSystem;
#endif __BOTNETWORK__
Create this file (botNetwork.cpp) defining your network wrapper functions:-
Code:
botNetworkSystem::botNetworkSystem(idNetworkSystem *networkSystem)
{
network = networkSystem;
}
float botNetworkSystem::ClientGetIncomingPacketLoss()
{
return network->ClientGetIncomingPacketLoss();
}
int botNetworkSystem::ClientGetIncomingRate()
{
return network->ClientGetIncomingRate();
}
int botNetworkSystem::ClientGetPrediction()
{
return network->ClientGetPrediction();
}
int botNetworkSystem::ClientGetTimeSinceLastPacket()
{
return network->ClientGetTimeSinceLastPacket();
}
int botNetworkSystem::ClientGetOutgoingRate()
{
return network->ClientGetOutgoingRate();
}
float botNetworkSystem::ServerGetClientIncomingPacketLoss(int clientNum)
{
if (clientNum >= BOT_START_INDEX)
return 0;
return network->ServerGetClientIncomingPacketLoss(clientNum);
}
int botNetworkSystem::ServerGetClientIncomingRate(int clientNum)
{
if (clientNum >= BOT_START_INDEX)
return 0;
return network->ServerGetClientIncomingRate(clientNum);
}
int botNetworkSystem::ServerGetClientOutgoingRate(int clientNum)
{
if (clientNum >= BOT_START_INDEX)
return 0;
return network->ServerGetClientOutgoingRate(clientNum);
}
int botNetworkSystem::ServerGetClientPing(int clientNum)
{
if (clientNum >= BOT_START_INDEX)
return 50; // Steve:: Can we make this random to emulate bot latency?
return network->ServerGetClientPing(clientNum);
}
int botNetworkSystem::ServerGetClientTimeSinceLastInput(int clientNum)
{
if (clientNum >= BOT_START_INDEX)
return 0; // Steve:: What are the effects of this?
return network->ServerGetClientTimeSinceLastInput(clientNum);
}
int botNetworkSystem::ServerGetClientTimeSinceLastPacket(int clientNum)
{
if (clientNum >= BOT_START_INDEX)
return 0; // Steve:: What are the effects of this?
return network->ServerGetClientTimeSinceLastPacket(clientNum);
}
int botNetworkSystem::ServerGetClientPrediction(int clientNum)
{
if (clientNum >= BOT_START_INDEX)
return 0;
return network->ServerGetClientPrediction(clientNum);
}
void botNetworkSystem::ServerSendReliableMessage(int clientNum, const idBitMsg &msg)
{
if (clientNum >= BOT_START_INDEX)
return;
network->ServerSendReliableMessage(clientNum, msg);
}
void botNetworkSystem::ServerSendReliableMessageExcluding(int clientNum, const idBitMsg &msg)
{
if (clientNum >= BOT_START_INDEX)
return;
network->ServerSendReliableMessageExcluding(clientNum, msg);
}
void botNetworkSystem::ClientSendReliableMessage(const idBitMsg &msg)
{
network->ClientSendReliableMessage(msg);
}
If you get compile errors regarding networkSystem being defined as idNetworkSystem, you need to edit src/framework/asynch/networksystem.h and remove the reference to extern idNetworkSystem * networkSystem.
Next up, you simply add cvar commands to call methods on your bot class, which in turn call the functions of idGameLocal:
ServerClientConnect(clientID)
ServerClientBegin(clientID)
and so forth. You take the client ID from the upper half the client array, since real players will never be allocated into these ids.
As for where you hook your Think() on your bot class, my recommendation is Run() in idMultiplayergame.
I'll release more code as I polish it, but hopefully people can see how I'm doing this now.