Skip to content

Commit

Permalink
misc map features
Browse files Browse the repository at this point in the history
- ability to create an entity without spawning
- export more methods for plugins
- custom weapons can be used in map cfgs
- add EntityCreated hook
- ClientPutInServer called after player spawns (so PlayerByIndex works)
- add debug logs for global states (source of confusing map bugs)
- fix undetected model precache overflows
  • Loading branch information
wootguy committed Jan 5, 2025
1 parent 2df263f commit 827c1f0
Show file tree
Hide file tree
Showing 40 changed files with 140 additions and 81 deletions.
7 changes: 5 additions & 2 deletions dlls/CBaseEntity.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -406,7 +406,8 @@ int CBaseEntity::DamageDecal(int bitsDamageType)

// NOTE: szName must be a pointer to constant memory, e.g. "monster_class" because the entity
// will keep a pointer to it after this call.
CBaseEntity* CBaseEntity::Create(const char* szName, const Vector& vecOrigin, const Vector& vecAngles, edict_t* pentOwner, std::unordered_map<std::string, std::string> keys)
CBaseEntity* CBaseEntity::Create(const char* szName, const Vector& vecOrigin, const Vector& vecAngles,
bool spawn, edict_t* pentOwner, std::unordered_map<std::string, std::string> keys)
{
edict_t* pent;
CBaseEntity* pEntity;
Expand All @@ -431,7 +432,9 @@ CBaseEntity* CBaseEntity::Create(const char* szName, const Vector& vecOrigin, co
DispatchKeyValue(pent, &dat);
}

DispatchSpawn(pEntity->edict());
if (spawn)
DispatchSpawn(pEntity->edict());

return pEntity;
}

Expand Down
2 changes: 1 addition & 1 deletion dlls/CBaseEntity.h
Original file line number Diff line number Diff line change
Expand Up @@ -308,7 +308,7 @@ class EXPORT CBaseEntity


//
static CBaseEntity* Create(const char* szName, const Vector& vecOrigin, const Vector& vecAngles, edict_t* pentOwner = NULL, std::unordered_map<std::string, std::string> keys = std::unordered_map<std::string, std::string>());
static CBaseEntity* Create(const char* szName, const Vector& vecOrigin, const Vector& vecAngles, bool spawn=true, edict_t* pentOwner = NULL, std::unordered_map<std::string, std::string> keys = std::unordered_map<std::string, std::string>());

virtual BOOL FBecomeProne(void) { return FALSE; };
edict_t* edict() { return ENT(pev); };
Expand Down
2 changes: 1 addition & 1 deletion dlls/CWorld.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -352,7 +352,7 @@ void CWorld::Precache(void)
if (pev->netname)
{
ALERT(at_aiconsole, "Chapter title: %s\n", STRING(pev->netname));
CBaseEntity* pEntity = CBaseEntity::Create("env_message", g_vecZero, g_vecZero, NULL);
CBaseEntity* pEntity = CBaseEntity::Create("env_message", g_vecZero, g_vecZero);
if (pEntity)
{
pEntity->SetThink(&CBaseEntity::SUB_CallUseToggle);
Expand Down
2 changes: 1 addition & 1 deletion dlls/env/CEnvBeverage.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ void CEnvBeverage::Use(CBaseEntity* pActivator, CBaseEntity* pCaller, USE_TYPE u
return;
}

CBaseEntity* pCan = CBaseEntity::Create("item_sodacan", pev->origin, pev->angles, edict());
CBaseEntity* pCan = CBaseEntity::Create("item_sodacan", pev->origin, pev->angles, true, edict());

if (pev->skin == 6)
{
Expand Down
4 changes: 2 additions & 2 deletions dlls/env/CEnvExplosion.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,7 @@ void CEnvExplosion::Use(CBaseEntity* pActivator, CBaseEntity* pCaller, USE_TYPE

for (int i = 0; i < sparkCount; i++)
{
Create("spark_shower", m_effectOrigin, tr.vecPlaneNormal, NULL);
Create("spark_shower", m_effectOrigin, tr.vecPlaneNormal);
}
}
}
Expand Down Expand Up @@ -202,7 +202,7 @@ void ExplosionCreate(const Vector& center, const Vector& angles, edict_t* pOwner
KeyValueData kvd;
char buf[128];

CBaseEntity* pExplosion = CBaseEntity::Create("env_explosion", center, angles, pOwner);
CBaseEntity* pExplosion = CBaseEntity::Create("env_explosion", center, angles, true, pOwner);
snprintf(buf, 128, "%3d", magnitude);
kvd.szKeyName = "iMagnitude";
kvd.szValue = buf;
Expand Down
4 changes: 2 additions & 2 deletions dlls/env/CEnvWeather.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -688,7 +688,7 @@ void CEnvWeather::Spawn(void)
// reverse order so fog renders correctly
for (int k = FOG_LAYERS - 1; k >= 0; k--) {
if (!g_fog_ents[k]) {
g_fog_ents[k] = Create("fog_layer", g_vecZero, g_vecZero, NULL);
g_fog_ents[k] = Create("fog_layer", g_vecZero, g_vecZero);
if (!m_isActive)
g_fog_ents[k]->pev->effects = EF_NODRAW;;
}
Expand Down Expand Up @@ -982,7 +982,7 @@ void CEnvWeather::WeatherEntsThink() {
{"model", weather.model},
};

CFuncConveyor* ent = (CFuncConveyor*)Create("weather_conveyor", weather.pos, Vector(0, 0, 0), NULL, keys);
CFuncConveyor* ent = (CFuncConveyor*)Create("weather_conveyor", weather.pos, Vector(0, 0, 0), true, NULL, keys);
ent->pev->solid = SOLID_NOT;
ent->pev->movetype = weather.isFloating ? MOVETYPE_NONE : MOVETYPE_TOSS;
ent->pev->rendermode = kRenderTransAdd;
Expand Down
6 changes: 3 additions & 3 deletions dlls/env/CSprite.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,8 @@ class CSprite : public CPointEntity
pev->movetype = MOVETYPE_FOLLOW;
}
}
void TurnOff(void);
void TurnOn(void);
void EXPORT TurnOff(void);
void EXPORT TurnOn(void);
inline float Frames(void) { return m_maxFrame; }
inline void SetTransparency(int rendermode, int r, int g, int b, int a, int fx)
{
Expand Down Expand Up @@ -68,7 +68,7 @@ class CSprite : public CPointEntity
virtual int Save(CSave& save);
virtual int Restore(CRestore& restore);
static TYPEDESCRIPTION m_SaveData[];
static CSprite* SpriteCreate(const char* pSpriteName, const Vector& origin, BOOL animate);
static EXPORT CSprite* SpriteCreate(const char* pSpriteName, const Vector& origin, BOOL animate);

private:

Expand Down
2 changes: 1 addition & 1 deletion dlls/func/CBreakable.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -743,7 +743,7 @@ void CBreakable::Die()
SetThink(&CBreakable::SUB_Remove);
pev->nextthink = pev->ltime + 0.1;
if (m_iszSpawnObject)
CBaseEntity::Create((char*)STRING(m_iszSpawnObject), VecBModelOrigin(pev), pev->angles, edict());
CBaseEntity::Create((char*)STRING(m_iszSpawnObject), VecBModelOrigin(pev), pev->angles, true, edict());


if (Explodable())
Expand Down
2 changes: 1 addition & 1 deletion dlls/func/CFuncTankRocket.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ void CFuncTankRocket::Fire(const Vector& barrelEnd, const Vector& forward, entva
{
for (i = 0; i < bulletCount; i++)
{
CBaseEntity::Create("rpg_rocket", barrelEnd, pev->angles, edict());
CBaseEntity::Create("rpg_rocket", barrelEnd, pev->angles, true, edict());
}
CFuncTank::Fire(barrelEnd, forward, pev);
}
Expand Down
4 changes: 4 additions & 0 deletions dlls/game/game.h
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,10 @@ EXPORT extern bool g_cfgsExecuted; // set to true after server and map cfgs are

EXPORT extern std::unordered_set<std::string> g_nomaptrans; // trigger_changelevel disabled for these maps

// lines in the cfg which could possibly be custom weapons. Not known until map plugins are loaded,
// and map plugins aren't known until the cfg finishes parsing
EXPORT extern std::vector<std::pair<std::string, std::string>> g_unrecognizedCfgEquipment;

// mark a palyer weapon for precaching (alias names are ok)
EXPORT void AddPrecacheWeapon(std::string wepName);

Expand Down
57 changes: 38 additions & 19 deletions dlls/game/gamerules.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,38 @@ std::unordered_set<std::string> timeCriticalCvars = {
"mp_skill_allow",
};

std::vector<std::pair<std::string, std::string>> g_unrecognizedCfgEquipment;
extern int g_mapEquipIdx;

void AddMapEquipment(std::string name, std::string value) {
if (g_mapEquipIdx >= MAX_EQUIP) {
ALERT(at_error, "Failed to add equipment '%s'. Max equipment reached.\n", name.c_str());
return;
}

if (mp_default_medkit.value == 0 && name == "weapon_medkit") {
// don't want medkits by default unless the map specifically places them
// (for a class system or as a special item)
return;
}

g_mapEquipment[g_mapEquipIdx].itemName = ALLOC_STRING(name.c_str());
g_mapEquipment[g_mapEquipIdx].count = value.size() ? atoi(value.c_str()) : 1;

AddPrecacheWeapon(name);

g_mapEquipIdx++;
}

void AddMapPluginEquipment() {
for (auto pair : g_unrecognizedCfgEquipment) {
if (g_weaponClassnames.count(pair.first)) {
AddMapEquipment(pair.first, pair.second);
}
}
g_unrecognizedCfgEquipment.clear();
}

void execMapCfg() {
// Map CFGs are low trust so only whitelisted commands are allowed.
// Server owners shouldn't have to check each map for things like "rcon_password HAHA_GOT_YOU"
Expand Down Expand Up @@ -245,7 +277,7 @@ void execMapCfg() {
std::stringstream data_stream(cfgFile);
string line;

int equipIdx = 0;
g_mapEquipIdx = 0;

while (std::getline(data_stream, line))
{
Expand Down Expand Up @@ -312,24 +344,11 @@ void execMapCfg() {

SERVER_COMMAND(UTIL_VarArgs("%s %s\n", name.c_str(), value.c_str()));
}
else if (itemNames.find(name) != itemNames.end()) {
if (equipIdx >= MAX_EQUIP) {
ALERT(at_error, "Failed to add equipment '%s'. Max equipment reached.\n", line.c_str());
continue;
}

if (mp_default_medkit.value == 0 && name == "weapon_medkit") {
// don't want medkits by default unless the map specifically places them
// (for a class system or as a special item)
continue;
}

g_mapEquipment[equipIdx].itemName = ALLOC_STRING(name.c_str());
g_mapEquipment[equipIdx].count = value.size() ? atoi(value.c_str()) : 1;

AddPrecacheWeapon(name);

equipIdx++;
else if (itemNames.count(name)) {
AddMapEquipment(name, value);
}
else {
g_unrecognizedCfgEquipment.push_back({ name, value });
}
}

Expand Down
2 changes: 1 addition & 1 deletion dlls/game/gamerules.h
Original file line number Diff line number Diff line change
Expand Up @@ -401,4 +401,4 @@ class CHalfLifeMultiplay : public CGameRules
void SendMOTDToClient( edict_t *client );
};

extern DLL_GLOBAL CGameRules* g_pGameRules;
EXPORT extern DLL_GLOBAL CGameRules* g_pGameRules;
4 changes: 4 additions & 0 deletions dlls/hooks/PluginHooks.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

#define HLCOOP_API_VERSION 2

class CBaseEntity;
class CBasePlayer;

struct HOOK_RETURN_DATA {
Expand Down Expand Up @@ -164,6 +165,9 @@ struct HLCOOP_PLUGIN_HOOKS {

// called before a chat message is sent. Update the message pointer to change the message.
HOOK_RETURN_DATA (*pfnChatMessage)(CBasePlayer* plr, const char** message, bool teamOnly);

// called when an entity is created and keyvalues are applied, but before it spawns
HOOK_RETURN_DATA (*pfnEntityCreated)(CBaseEntity* pEntity);
};

// do not call directly, use RegisterPlugin instead
Expand Down
2 changes: 1 addition & 1 deletion dlls/hooks/client_commands.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -375,7 +375,7 @@ void ClientCommand(edict_t* pEntity)
{
pPlayer->SelectItem((char*)CMD_ARGV(1));
}
else if (g_weaponClassnames.count(pcmd))
else if (g_weaponNames.count(pcmd))
{
// custom weapon was selected (weapon name includes a folder path to force clients to load HUD files from there)
const char* wepCname = pcmd;
Expand Down
26 changes: 22 additions & 4 deletions dlls/hooks/hlds_hooks.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ void PM_Init(struct playermove_s* ppmove);
char PM_FindTextureType(char* name);
extern Vector VecBModelOrigin(entvars_t* pevBModel);
extern void CopyToBodyQue(entvars_t* pev);
extern void AddMapPluginEquipment();
extern int giPrecacheGrunt;
extern int gmsgSayText;
extern int g_teamplay;
Expand Down Expand Up @@ -398,10 +399,10 @@ void ClientPutInServer( edict_t *pEntity )
pPlayer->LoadScore();
pPlayer->m_lastUserInput = g_engfuncs.pfnTime();

CALL_HOOKS_VOID(pfnClientPutInServer, pPlayer);

// Allocate a CBasePlayer for pev, and call spawn
pPlayer->Spawn();

CALL_HOOKS_VOID(pfnClientPutInServer, pPlayer);
}

/*
Expand Down Expand Up @@ -514,7 +515,9 @@ void ServerDeactivate( void )
g_mapWeapons.clear();
g_wavInfos.clear();
g_weaponClassnames.clear();
g_weaponNames.clear();
g_nomaptrans.clear();
g_unrecognizedCfgEquipment.clear();
clearNetworkMessageHistory();
g_mp3Command = "";
g_monstersNerfed = false;
Expand Down Expand Up @@ -727,6 +730,8 @@ void ServerActivate( edict_t *pEdictList, int edictCount, int clientMax )
}
}

AddMapPluginEquipment();

PrecacheWeapons();
PrecacheTextureSounds();

Expand Down Expand Up @@ -774,7 +779,7 @@ void ServerActivate( edict_t *pEdictList, int edictCount, int clientMax )
g_tryPrecacheModels.size() + g_bsp.modelCount, g_tryPrecacheModels.size(), g_bsp.modelCount,
g_tryPrecacheSounds.size(), g_tryPrecacheGeneric.size(), g_tryPrecacheEvents.size()));

if (g_tryPrecacheModels.size() > g_precachedModels.size()) {
if (g_tryPrecacheModels.size() + g_bsp.entityBspModelCount + 1 > MAX_PRECACHE_MODEL) {
ALERT(at_error, "Model precache overflow (%d / %d). The following models were not precached:\n",
g_tryPrecacheModels.size() + g_bsp.modelCount, MAX_PRECACHE);

Expand Down Expand Up @@ -2153,6 +2158,8 @@ edict_t* SpawnEdict(edict_t* pent) {
pEntity->pev->absmin = pEntity->pev->origin - Vector(1, 1, 1);
pEntity->pev->absmax = pEntity->pev->origin + Vector(1, 1, 1);

CALL_HOOKS(edict_t*, pfnEntityCreated, pEntity);

pEntity->Spawn();

// Try to get the pointer again, in case the spawn function deleted the entity.
Expand All @@ -2176,16 +2183,27 @@ edict_t* SpawnEdict(edict_t* pent) {
{
// Already dead? delete
if (pGlobal->state == GLOBAL_DEAD) {
// source of bugs
ALERT(at_console, "Removed '%s' (%s) due to global state\n",
STRING(pEntity->pev->targetname), STRING(pEntity->pev->classname));

REMOVE_ENTITY(pent);
return NULL;
}

else if (!FStrEq(STRING(gpGlobals->mapname), pGlobal->levelName))
else if (!FStrEq(STRING(gpGlobals->mapname), pGlobal->levelName)) {
ALERT(at_console, "Hiding '%s' (%s) due to global state\n",
STRING(pEntity->pev->targetname), STRING(pEntity->pev->classname));

pEntity->MakeDormant(); // Hasn't been moved to this level yet, wait but stay alive
}
// In this level & not dead, continue on as normal
}
else
{
ALERT(at_console, "Activating '%s' (%s) due to global state\n",
STRING(pEntity->pev->targetname), STRING(pEntity->pev->classname));

// Spawned entities default to 'On'
gGlobalState.EntityAdd(pEntity->pev->globalname, gpGlobals->mapname, GLOBAL_ON);
// ALERT( at_console, "Added global entity %s (%s)\n", STRING(pEntity->pev->classname), STRING(pEntity->pev->globalname) );
Expand Down
2 changes: 1 addition & 1 deletion dlls/monster/CAGrunt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -390,7 +390,7 @@ void CAGrunt :: HandleAnimEvent( MonsterEvent_t *pEvent )
WRITE_BYTE( 128 ); // brightness
MESSAGE_END();

CBaseEntity *pHornet = CBaseEntity::Create( "hornet", vecArmPos, UTIL_VecToAngles( vecDirToEnemy ), edict() );
CBaseEntity *pHornet = CBaseEntity::Create( "hornet", vecArmPos, UTIL_VecToAngles( vecDirToEnemy ), true, edict() );
UTIL_MakeVectors ( pHornet->pev->angles );
pHornet->pev->velocity = gpGlobals->v_forward * 300;

Expand Down
2 changes: 1 addition & 1 deletion dlls/monster/CApache.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -826,7 +826,7 @@ void CApache :: FireRocket( void )
WRITE_BYTE( 12 ); // framerate
MESSAGE_END();

CBaseEntity *pRocket = CBaseEntity::Create( "hvr_rocket", vecSrc, pev->angles, edict() );
CBaseEntity *pRocket = CBaseEntity::Create( "hvr_rocket", vecSrc, pev->angles, true, edict() );
if (pRocket)
pRocket->pev->velocity = pev->velocity + gpGlobals->v_forward * 100;

Expand Down
2 changes: 1 addition & 1 deletion dlls/monster/CBaseMonster.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3845,7 +3845,7 @@ CBaseEntity* CBaseMonster::DropItem(const char* pszItemName, const Vector& vecPo
return NULL;
}

CBaseEntity* pItem = CBaseEntity::Create(pszItemName, vecPos, vecAng, edict());
CBaseEntity* pItem = CBaseEntity::Create(pszItemName, vecPos, vecAng, true, edict());

if (pItem)
{
Expand Down
2 changes: 1 addition & 1 deletion dlls/monster/CBigMomma.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -662,7 +662,7 @@ void CBigMomma :: LayHeadcrab( void )
keys["classify"] = m_Classify;
}

CBaseEntity *pChild = CBaseEntity::Create( BIG_CHILDCLASS, pev->origin, pev->angles, edict(), keys );
CBaseEntity *pChild = CBaseEntity::Create( BIG_CHILDCLASS, pev->origin, pev->angles, true, edict(), keys );

pChild->pev->spawnflags |= SF_MONSTER_FALL_TO_GROUND;

Expand Down
4 changes: 2 additions & 2 deletions dlls/monster/CController.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -318,7 +318,7 @@ void CController :: HandleAnimEvent( MonsterEvent_t *pEvent )

const char* soundlist = m_soundReplacementPath ? STRING(m_soundReplacementPath) : "";
std::unordered_map<std::string, std::string> keys = { {"soundlist", soundlist} };
CBaseMonster *pBall = (CBaseMonster*)Create( "controller_head_ball", vecStart, pev->angles, edict(), keys);
CBaseMonster *pBall = (CBaseMonster*)Create( "controller_head_ball", vecStart, pev->angles, true, edict(), keys);

pBall->pev->velocity = Vector( 0, 0, 32 );
pBall->m_hEnemy = m_hEnemy;
Expand Down Expand Up @@ -670,7 +670,7 @@ void CController :: RunTask ( Task_t *pTask )
vecDir = vecDir + Vector( RANDOM_FLOAT( -delta, delta ), RANDOM_FLOAT( -delta, delta ), RANDOM_FLOAT( -delta, delta ) ) * gSkillData.sk_controller_speedball;

vecSrc = vecSrc + vecDir * (gpGlobals->time - m_flShootTime);
CBaseMonster *pBall = (CBaseMonster*)Create( "controller_energy_ball", vecSrc, pev->angles, edict() );
CBaseMonster *pBall = (CBaseMonster*)Create( "controller_energy_ball", vecSrc, pev->angles, true, edict() );
pBall->pev->velocity = vecDir;

if (CBaseEntity::IRelationship(Classify(), CLASS_PLAYER) == R_AL) {
Expand Down
Loading

0 comments on commit 827c1f0

Please sign in to comment.