mirror of
https://github.com/jmarshall23/DoomRTX.git
synced 2026-08-12 08:11:10 +02:00
Bot fixes.
This commit is contained in:
@@ -9,7 +9,7 @@ idCVar bot_goaldist( "bot_goaldist", "20", CVAR_INTEGER | CVAR_CHEAT, "" );
|
||||
idCVar bot_debugnav( "bot_debugnav", "0", CVAR_BOOL | CVAR_CHEAT, "draws navmesh paths for the bot" );
|
||||
idCVar bot_showstate( "bot_showstate", "0", CVAR_BOOL | CVAR_CHEAT, "draws the bot state above the bot" );
|
||||
idCVar bot_debug( "bot_debug", "0", CVAR_BOOL, "shows debug info for the bot" );
|
||||
idCVar bot_skill("bot_skill", "3", CVAR_INTEGER, "");
|
||||
idCVar bot_skill("bot_skill", "4", CVAR_INTEGER, "");
|
||||
|
||||
|
||||
static int BotSafeAmmoCount( const idInventory& inv, const char* ammoName ) {
|
||||
@@ -161,7 +161,16 @@ void rvmBot::Spawn( void )
|
||||
}
|
||||
|
||||
// Load in the bot character.
|
||||
bs.character = botCharacterStatsManager.BotLoadCharacterFromFile( va( "bots/%s_c.c", botName.c_str() ), 1 );
|
||||
const int skill = idMath::ClampInt( 1, 5, bot_skill.GetInteger() );
|
||||
bs.character = botCharacterStatsManager.BotLoadCharacterFromFile( va( "bots/%s_c.c", botName.c_str() ), skill );
|
||||
if( bs.character == NULL && skill != 4 )
|
||||
{
|
||||
bs.character = botCharacterStatsManager.BotLoadCharacterFromFile( va( "bots/%s_c.c", botName.c_str() ), 4 );
|
||||
}
|
||||
if( bs.character == NULL && skill != 1 )
|
||||
{
|
||||
bs.character = botCharacterStatsManager.BotLoadCharacterFromFile( va( "bots/%s_c.c", botName.c_str() ), 1 );
|
||||
}
|
||||
if( !bs.character )
|
||||
{
|
||||
gameLocal.Error( "Failed to load character file for bot %s\n", botName.c_str() );
|
||||
@@ -197,10 +206,12 @@ void rvmBot::Spawn( void )
|
||||
bs.entitynum = entityNumber;
|
||||
bs.setupcount = 4;
|
||||
bs.entergame_time = Bot_Time();
|
||||
bs.weaponnum = spawnArgs.GetInt( "current_weapon", "1" );
|
||||
|
||||
hasSpawned = true;
|
||||
|
||||
bs.botinput.respawn = true;
|
||||
bs.botinput.weapon = bs.weaponnum;
|
||||
bs.botinput.lastWeaponNum = -1;
|
||||
|
||||
stateThread.SetState("state_Respawn");
|
||||
@@ -316,6 +327,15 @@ void rvmBot::ServerThink( void )
|
||||
return;
|
||||
}
|
||||
|
||||
if( bs.enemy >= 0 && bs.botinput.speed > 0.0f )
|
||||
{
|
||||
bs.viewangles = bs.botinput.viewangles;
|
||||
bs.useRandomPosition = false;
|
||||
bs.attackerEntity = NULL;
|
||||
bs.botinput.weapon = bs.weaponnum;
|
||||
return;
|
||||
}
|
||||
|
||||
idVec3 goalOrigin = bs.useRandomPosition ? bs.random_move_position : bs.currentGoal.origin;
|
||||
idVec3 moveGoal = goalOrigin;
|
||||
bool haveMoveGoal = false;
|
||||
@@ -508,4 +528,4 @@ void rvmBot::Think( void )
|
||||
deltaViewAngles.Zero();
|
||||
|
||||
idPlayer::Think();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -132,7 +132,7 @@ class rvmBotAIBotActionBase;
|
||||
#define ENEMY_HEIGHT 201
|
||||
|
||||
#define MAX_AVOIDGOALS 256
|
||||
#define MAX_GOALSTACK 8
|
||||
#define MAX_GOALSTACK 16
|
||||
|
||||
#define GFL_NONE 0
|
||||
#define GFL_ITEM 1
|
||||
@@ -347,7 +347,7 @@ struct weightconfig_t
|
||||
#define DEFAULT_CHARACTER "bots/default_c.c"
|
||||
|
||||
#define MAX_AVOIDGOALS 256
|
||||
#define MAX_GOALSTACK 8
|
||||
#define MAX_GOALSTACK 16
|
||||
|
||||
#define GFL_NONE 0
|
||||
#define GFL_ITEM 1
|
||||
@@ -747,11 +747,11 @@ struct bot_state_t
|
||||
entitynum = 0;
|
||||
setupcount = 0;
|
||||
entergame_time = 0;
|
||||
weaponnum = 0;
|
||||
weaponnum = -1;
|
||||
lasthealth = 0;
|
||||
ltg_time = 0;
|
||||
weaponchange_time = 0;
|
||||
enemy = 0;
|
||||
enemy = -1;
|
||||
enemyvisible_time = 0;
|
||||
enemysuicide = 0;
|
||||
enemysight_time = 0;
|
||||
@@ -872,6 +872,10 @@ private:
|
||||
|
||||
bool HasWeapon( int index )
|
||||
{
|
||||
if( index < 0 || index >= MAX_WEAPONS )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return inventory.weapons & ( 1 << index );
|
||||
}
|
||||
private:
|
||||
@@ -923,4 +927,4 @@ private:
|
||||
idAAS* aas;
|
||||
};
|
||||
|
||||
extern idCVar bot_skill;
|
||||
extern idCVar bot_skill;
|
||||
|
||||
+376
-302
File diff suppressed because it is too large
Load Diff
@@ -113,8 +113,48 @@ stateResult_t rvmBot::state_BattleFight(stateParms_t* parms)
|
||||
}
|
||||
}
|
||||
|
||||
// Move randomly around our AAS area only when there is no useful nearby pickup.
|
||||
BotMoveInRandomDirection(&bs);
|
||||
// Fight like a deathmatch bot: keep pressure on the enemy while changing
|
||||
// strafe direction often enough to be hard to track.
|
||||
if( bs.attackstrafe_time < Bot_Time() )
|
||||
{
|
||||
bs.attackstrafe_time = Bot_Time() + idMath::FRandRange( 0.65f, 1.35f );
|
||||
if( rvmBotUtil::random() < 0.5f )
|
||||
{
|
||||
bs.flags ^= BFL_STRAFERIGHT;
|
||||
}
|
||||
}
|
||||
|
||||
idVec3 toEnemy = target - bs.origin;
|
||||
toEnemy[2] = 0.0f;
|
||||
const float enemyDist = toEnemy.Length();
|
||||
if( enemyDist > 1.0f )
|
||||
{
|
||||
toEnemy /= enemyDist;
|
||||
idVec3 strafeDir = ( bs.flags & BFL_STRAFERIGHT ) ? idVec3( -toEnemy[1], toEnemy[0], 0.0f ) : idVec3( toEnemy[1], -toEnemy[0], 0.0f );
|
||||
idVec3 moveDir = strafeDir;
|
||||
|
||||
if( enemyDist > 520.0f )
|
||||
{
|
||||
moveDir += toEnemy * 0.65f;
|
||||
}
|
||||
else if( enemyDist < 170.0f )
|
||||
{
|
||||
moveDir -= toEnemy * 0.75f;
|
||||
}
|
||||
|
||||
moveDir.Normalize();
|
||||
bs.botinput.dir = moveDir;
|
||||
bs.botinput.speed = pm_runspeed.GetInteger();
|
||||
|
||||
if( enemyDist < 420.0f && rvmBotUtil::random() < 0.025f )
|
||||
{
|
||||
bs.botinput.actionflags |= ACTION_JUMP;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
BotMoveInRandomDirection(&bs);
|
||||
}
|
||||
|
||||
//aim at the enemy
|
||||
BotAimAtEnemy( &bs );
|
||||
|
||||
@@ -259,8 +259,7 @@ int idBotCharacterStatsManager::CheckCharacteristicIndex( bot_character_t* ch, i
|
||||
{
|
||||
if( ch == NULL )
|
||||
{
|
||||
gameLocal.Error( "NULL bot character
|
||||
" );
|
||||
gameLocal.Error( "NULL bot character" );
|
||||
return false;
|
||||
}
|
||||
if( index < 0 || index >= MAX_CHARACTERISTICS )
|
||||
|
||||
+112
-103
@@ -398,49 +398,50 @@ itemconfig_t* idBotGoalManager::LoadItemConfig( char* filename )
|
||||
idBotGoalManager::ItemWeightIndex
|
||||
========================
|
||||
*/
|
||||
/*
|
||||
================
|
||||
Bot_ItemWeightAlias
|
||||
|
||||
Some itemconfig entries come from Quake 3 / Team Arena style bot data, but the
|
||||
Doom 3 weight config usually does not contain every one of those classnames.
|
||||
|
||||
The item weight index must still point at a valid fuzzy weight, so we alias
|
||||
unsupported / missing items to the closest useful existing Doom 3 weight.
|
||||
================
|
||||
*/
|
||||
static const char* Bot_ItemWeightAlias(const char* classname) {
|
||||
if (!classname || !classname[0]) {
|
||||
static const char* Bot_ItemWeightAlias( const char* classname )
|
||||
{
|
||||
if( classname == NULL || classname[0] == '\0' )
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct itemWeightAlias_t {
|
||||
struct itemWeightAlias_t
|
||||
{
|
||||
const char* missingName;
|
||||
const char* fallbackName;
|
||||
};
|
||||
|
||||
static const itemWeightAlias_t aliases[] = {
|
||||
// Missing weapon names commonly seen in Q3 bot item configs.
|
||||
{ "weapon_fists", "weapon_pistol" },
|
||||
{ "weapon_grapplinghook", "weapon_chainsaw" },
|
||||
{ "weapon_grenadelauncher", "weapon_rocketlauncher" },
|
||||
{ "weapon_lightning", "weapon_plasmagun" },
|
||||
{ "weapon_machinegun", "weapon_machinegun" },
|
||||
{ "weapon_railgun", "weapon_rocketlauncher" },
|
||||
{ "weapon_nailgun", "weapon_chaingun" },
|
||||
{ "weapon_prox_launcher", "weapon_rocketlauncher" },
|
||||
static const itemWeightAlias_t aliases[] =
|
||||
{
|
||||
{ "weapon_machinegun_mp", "weapon_machinegun" },
|
||||
{ "weapon_shotgun_mp", "weapon_shotgun" },
|
||||
{ "weapon_rocketlauncher_mp", "weapon_rocketlauncher" },
|
||||
{ "weapon_plasmagun_mp", "weapon_plasmagun" },
|
||||
{ "weapon_bfg_mp", "weapon_bfg" },
|
||||
{ "weapon_grenadelauncher", "weapon_rocketlauncher" },
|
||||
{ "weapon_grenadelauncher_mp", "weapon_rocketlauncher" },
|
||||
{ "weapon_lightning", "weapon_plasmagun" },
|
||||
{ "weapon_lightning_mp", "weapon_plasmagun" },
|
||||
{ "weapon_railgun", "weapon_rocketlauncher" },
|
||||
{ "weapon_railgun_mp", "weapon_rocketlauncher" },
|
||||
{ "weapon_fists", "weapon_pistol" },
|
||||
{ "weapon_grapplinghook", "weapon_chainsaw" },
|
||||
{ "weapon_nailgun", "weapon_chaingun" },
|
||||
{ "weapon_nailgun_mp", "weapon_chaingun" },
|
||||
{ "weapon_prox_launcher", "weapon_rocketlauncher" },
|
||||
{ "weapon_prox_launcher_mp", "weapon_rocketlauncher" },
|
||||
|
||||
// Team Arena / CTF objective entities. These are not normal pickup goals.
|
||||
// Give them a valid low-risk fallback so the index is never invalid.
|
||||
{ "team_redobelisk", "item_armor_shard" },
|
||||
{ "team_blueobelisk", "item_armor_shard" },
|
||||
{ "team_neutralobelisk", "item_armor_shard" },
|
||||
{ "team_redobelisk", "item_armor_shard" },
|
||||
{ "team_blueobelisk", "item_armor_shard" },
|
||||
{ "team_neutralobelisk", "item_armor_shard" },
|
||||
|
||||
{ NULL, NULL }
|
||||
};
|
||||
|
||||
for (int i = 0; aliases[i].missingName != NULL; i++) {
|
||||
if (idStr::Icmp(classname, aliases[i].missingName) == 0) {
|
||||
for( int i = 0; aliases[i].missingName != NULL; i++ )
|
||||
{
|
||||
if( idStr::Icmp( classname, aliases[i].missingName ) == 0 )
|
||||
{
|
||||
return aliases[i].fallbackName;
|
||||
}
|
||||
}
|
||||
@@ -448,81 +449,84 @@ static const char* Bot_ItemWeightAlias(const char* classname) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
idBotGoalManager::ItemWeightIndex
|
||||
================
|
||||
*/
|
||||
int* idBotGoalManager::ItemWeightIndex(weightconfig_t* iwc, itemconfig_t* ic) {
|
||||
int* index;
|
||||
int i;
|
||||
|
||||
if (iwc == NULL || ic == NULL || ic->numiteminfo <= 0) {
|
||||
return NULL;
|
||||
static bool Bot_ItemClassnamesMatch( const char* entityClassname, const char* itemClassname )
|
||||
{
|
||||
if( entityClassname == NULL || itemClassname == NULL )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if( idStr::Icmp( entityClassname, itemClassname ) == 0 )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// initialize item weight index
|
||||
index = new int[ic->numiteminfo]; // jmarshall: Get this off of the heap!
|
||||
|
||||
for (i = 0; i < ic->numiteminfo; i++) {
|
||||
const char* classname = ic->iteminfo[i].classname.c_str();
|
||||
|
||||
index[i] = botFuzzyWeightManager.FindFuzzyWeight(iwc, (char*)classname);
|
||||
|
||||
if (index[i] < 0) {
|
||||
const char* aliasName = Bot_ItemWeightAlias(classname);
|
||||
|
||||
if (aliasName != NULL) {
|
||||
index[i] = botFuzzyWeightManager.FindFuzzyWeight(iwc, (char*)aliasName);
|
||||
|
||||
if (index[i] >= 0) {
|
||||
common->Printf(
|
||||
"Bot item weight alias: item info %d \"%s\" using \"%s\"\n",
|
||||
i,
|
||||
classname,
|
||||
aliasName
|
||||
);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// Last-resort fallback. This keeps the bot goal system from storing
|
||||
// invalid fuzzy weight indexes even if the expected alias is missing.
|
||||
static const char* safeFallbacks[] = {
|
||||
"item_armor_shard",
|
||||
"item_armor",
|
||||
"ammo_clip",
|
||||
"weapon_pistol",
|
||||
"weapon_shotgun",
|
||||
"weapon_machinegun",
|
||||
NULL
|
||||
};
|
||||
|
||||
for (int j = 0; safeFallbacks[j] != NULL; j++) {
|
||||
index[i] = botFuzzyWeightManager.FindFuzzyWeight(iwc, (char*)safeFallbacks[j]);
|
||||
if (index[i] >= 0) {
|
||||
common->Printf(
|
||||
"Bot item weight fallback: item info %d \"%s\" using \"%s\"\n",
|
||||
i,
|
||||
classname,
|
||||
safeFallbacks[j]
|
||||
);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (index[i] < 0) {
|
||||
common->Warning(
|
||||
"item info %d \"%s\" has no fuzzy weight and no fallback weight\r\n",
|
||||
i,
|
||||
classname
|
||||
);
|
||||
}
|
||||
idStr normalizedItem = itemClassname;
|
||||
if( idStr::Icmp( normalizedItem.Right( 3 ).c_str(), "_mp" ) == 0 )
|
||||
{
|
||||
normalizedItem.StripTrailing( "_mp" );
|
||||
if( idStr::Icmp( entityClassname, normalizedItem.c_str() ) == 0 )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
const char* aliasName = Bot_ItemWeightAlias( itemClassname );
|
||||
return aliasName != NULL && idStr::Icmp( entityClassname, aliasName ) == 0;
|
||||
}
|
||||
|
||||
int* idBotGoalManager::ItemWeightIndex( weightconfig_t* iwc, itemconfig_t* ic )
|
||||
{
|
||||
int* index;
|
||||
|
||||
if( iwc == NULL || ic == NULL || ic->numiteminfo <= 0 )
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
//initialize item weight index
|
||||
index = new int[ic->numiteminfo]; // jmarshall: Get this off of the heap!
|
||||
|
||||
for( int i = 0; i < ic->numiteminfo; i++ )
|
||||
{
|
||||
const char* classname = ic->iteminfo[i].classname.c_str();
|
||||
index[i] = botFuzzyWeightManager.FindFuzzyWeight( iwc, ( char* )classname );
|
||||
if( index[i] < 0 )
|
||||
{
|
||||
const char* aliasName = Bot_ItemWeightAlias( classname );
|
||||
if( aliasName != NULL )
|
||||
{
|
||||
index[i] = botFuzzyWeightManager.FindFuzzyWeight( iwc, ( char* )aliasName );
|
||||
}
|
||||
}
|
||||
if( index[i] < 0 )
|
||||
{
|
||||
static const char* safeFallbacks[] =
|
||||
{
|
||||
"item_armor_shard",
|
||||
"item_armor",
|
||||
"ammo_clip",
|
||||
"weapon_machinegun",
|
||||
"weapon_shotgun",
|
||||
NULL
|
||||
};
|
||||
|
||||
for( int j = 0; safeFallbacks[j] != NULL; j++ )
|
||||
{
|
||||
index[i] = botFuzzyWeightManager.FindFuzzyWeight( iwc, ( char* )safeFallbacks[j] );
|
||||
if( index[i] >= 0 )
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if( index[i] < 0 )
|
||||
{
|
||||
common->Warning( "item info %d \"%s\" has no fuzzy weight\r\n", i, classname );
|
||||
}
|
||||
}
|
||||
return index;
|
||||
}
|
||||
|
||||
/*
|
||||
========================
|
||||
idBotGoalManager::InitLevelItemHeap
|
||||
@@ -552,7 +556,7 @@ levelitem_t* idBotGoalManager::AllocLevelItem( void )
|
||||
li = freelevelitems;
|
||||
if( !li )
|
||||
{
|
||||
gameLocal.Error( "out of level items\n" );
|
||||
gameLocal.Warning( "out of bot level items, increase MAX_BOT_LEVEL_ITEMS\n" );
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -751,7 +755,7 @@ void idBotGoalManager::InitLevelItems( void )
|
||||
|
||||
for( i = 0; i < ic->numiteminfo; i++ )
|
||||
{
|
||||
if( !strcmp( classname, ic->iteminfo[i].classname ) )
|
||||
if( Bot_ItemClassnamesMatch( classname.c_str(), ic->iteminfo[i].classname.c_str() ) )
|
||||
{
|
||||
break;
|
||||
}
|
||||
@@ -1517,11 +1521,16 @@ void idBotGoalManager::BotPushGoal( int goalstate, bot_goal_t* goal )
|
||||
{
|
||||
return;
|
||||
}
|
||||
if( gs->goalstacktop > 0 && gs->goalstack[gs->goalstacktop].number == goal->number )
|
||||
{
|
||||
gs->goalstack[gs->goalstacktop] = *goal;
|
||||
return;
|
||||
}
|
||||
if( gs->goalstacktop >= MAX_GOALSTACK - 1 )
|
||||
{
|
||||
gameLocal.Error( "goal heap overflow\n" );
|
||||
gameLocal.Warning( "bot goal stack overflow, clearing stale goals\n" );
|
||||
BotDumpGoalStack( goalstate );
|
||||
return;
|
||||
gs->goalstacktop = 0;
|
||||
}
|
||||
gs->goalstacktop++;
|
||||
gs->goalstack[gs->goalstacktop] = *goal;
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
#define MAX_BOT_ITEM_INFOS 2
|
||||
#define MAX_BOT_ITEM_INFO 256
|
||||
#define MAX_BOT_LEVEL_ITEMS 256
|
||||
#define MAX_BOT_LEVEL_ITEMS 2048
|
||||
|
||||
//#define DEBUG_AI_GOAL
|
||||
#ifdef RANDOMIZE
|
||||
@@ -339,4 +339,4 @@ private:
|
||||
idList<campspot_t> campspots;
|
||||
};
|
||||
|
||||
extern idBotGoalManager botGoalManager;
|
||||
extern idBotGoalManager botGoalManager;
|
||||
|
||||
@@ -8424,7 +8424,7 @@ static classVariableInfo_t bot_goalstate_t_typeInfo[] = {
|
||||
{ "int *", "itemweightindex", (intptr_t)(&((bot_goalstate_t *)0)->itemweightindex), sizeof( ((bot_goalstate_t *)0)->itemweightindex ) },
|
||||
{ "int", "client", (intptr_t)(&((bot_goalstate_t *)0)->client), sizeof( ((bot_goalstate_t *)0)->client ) },
|
||||
{ "int", "lastreachabilityarea", (intptr_t)(&((bot_goalstate_t *)0)->lastreachabilityarea), sizeof( ((bot_goalstate_t *)0)->lastreachabilityarea ) },
|
||||
{ "bot_goal_t[8]", "goalstack", (intptr_t)(&((bot_goalstate_t *)0)->goalstack), sizeof( ((bot_goalstate_t *)0)->goalstack ) },
|
||||
{ "bot_goal_t[16]", "goalstack", (intptr_t)(&((bot_goalstate_t *)0)->goalstack), sizeof( ((bot_goalstate_t *)0)->goalstack ) },
|
||||
{ "int", "goalstacktop", (intptr_t)(&((bot_goalstate_t *)0)->goalstacktop), sizeof( ((bot_goalstate_t *)0)->goalstacktop ) },
|
||||
{ "int[256]", "avoidgoals", (intptr_t)(&((bot_goalstate_t *)0)->avoidgoals), sizeof( ((bot_goalstate_t *)0)->avoidgoals ) },
|
||||
{ "float[256]", "avoidgoaltimes", (intptr_t)(&((bot_goalstate_t *)0)->avoidgoaltimes), sizeof( ((bot_goalstate_t *)0)->avoidgoaltimes ) },
|
||||
@@ -8435,7 +8435,7 @@ static classVariableInfo_t idBotGoalManager_typeInfo[] = {
|
||||
{ ": bot_goalstate_t[33]", "botgoalstates", (intptr_t)(&((idBotGoalManager *)0)->botgoalstates), sizeof( ((idBotGoalManager *)0)->botgoalstates ) },
|
||||
{ "itemconfig_t", "itemconfiglocal", (intptr_t)(&((idBotGoalManager *)0)->itemconfiglocal), sizeof( ((idBotGoalManager *)0)->itemconfiglocal ) },
|
||||
{ "itemconfig_t *", "itemconfig", (intptr_t)(&((idBotGoalManager *)0)->itemconfig), sizeof( ((idBotGoalManager *)0)->itemconfig ) },
|
||||
{ "levelitem_t[256]", "levelitemheap", (intptr_t)(&((idBotGoalManager *)0)->levelitemheap), sizeof( ((idBotGoalManager *)0)->levelitemheap ) },
|
||||
{ "levelitem_t[2048]", "levelitemheap", (intptr_t)(&((idBotGoalManager *)0)->levelitemheap), sizeof( ((idBotGoalManager *)0)->levelitemheap ) },
|
||||
{ "levelitem_t *", "freelevelitems", (intptr_t)(&((idBotGoalManager *)0)->freelevelitems), sizeof( ((idBotGoalManager *)0)->freelevelitems ) },
|
||||
{ "levelitem_t *", "levelitems", (intptr_t)(&((idBotGoalManager *)0)->levelitems), sizeof( ((idBotGoalManager *)0)->levelitems ) },
|
||||
{ "int", "numlevelitems", (intptr_t)(&((idBotGoalManager *)0)->numlevelitems), sizeof( ((idBotGoalManager *)0)->numlevelitems ) },
|
||||
|
||||
@@ -2705,6 +2705,10 @@ void idAsyncServer::RunFrame( void ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (client.channel.GetRemoteAddress().type == NA_BOT) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// send additional message fragments if the last message was too large to send at once
|
||||
if ( client.channel.UnsentFragmentsLeft() ) {
|
||||
client.channel.SendNextFragment( serverPort, serverTime );
|
||||
|
||||
Reference in New Issue
Block a user