Skip to content

Commit 0b45932

Browse files
committed
Fix rendermode migrations breaking maps that used enum values
1 parent 79f858b commit 0b45932

6 files changed

Lines changed: 54 additions & 16 deletions

File tree

src/cs2fixes.cpp

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@ SH_DECL_MANUALHOOK1_void(GoToIntermission, 0, 0, 0, bool);
9595
SH_DECL_MANUALHOOK2_void(PhysicsTouchShuffle, 0, 0, 0, CUtlVector<TouchLinked_t>*, bool);
9696
SH_DECL_MANUALHOOK3_void(DropWeapon, 0, 0, 0, CBasePlayerWeapon*, Vector*, Vector*);
9797
SH_DECL_HOOK1_void(IServer, SetGameSpawnGroupMgr, SH_NOATTRIB, 0, IGameSpawnGroupMgr*);
98+
SH_DECL_HOOK2_void(CEntitySystem, Spawn, SH_NOATTRIB, 0, int, const EntitySpawnInfo_t*);
9899

99100
CS2Fixes g_CS2Fixes;
100101
IGameEventSystem* g_gameEventSystem = nullptr;
@@ -118,6 +119,7 @@ int g_iGoToIntermissionId = -1;
118119
int g_iPhysicsTouchShuffle = -1;
119120
int g_iWeaponServiceDropWeaponId = -1;
120121
int g_iSetGameSpawnGroupMgrId = -1;
122+
int g_iSpawnId = -1;
121123

122124
double g_flUniversalTime = 0.0;
123125
float g_flLastTickedTime = 0.0f;
@@ -317,9 +319,11 @@ bool CS2Fixes::Load(PluginId id, ISmmAPI* ismm, char* error, size_t maxlen, bool
317319
g_iWeaponServiceDropWeaponId = SH_ADD_MANUALDVPHOOK(DropWeapon, pCCSPlayer_WeaponServicesVTable, SH_MEMBER(this, &CS2Fixes::Hook_DropWeaponPost), true);
318320

319321
auto pCGameEventManagerVTable = (IGameEventManager2*)modules::server->FindVirtualTable("CGameEventManager");
320-
321322
g_iLoadEventsFromFileId = SH_ADD_DVPHOOK(IGameEventManager2, LoadEventsFromFile, pCGameEventManagerVTable, SH_MEMBER(this, &CS2Fixes::Hook_LoadEventsFromFile), false);
322323

324+
auto pCEntitySystemVTable = (CEntitySystem*)modules::server->FindVirtualTable("CGameEntitySystem");
325+
g_iSpawnId = SH_ADD_DVPHOOK(CEntitySystem, Spawn, pCEntitySystemVTable, SH_MEMBER(this, &CS2Fixes::Hook_SpawnPost), true);
326+
323327
if (!bRequiredInitLoaded)
324328
{
325329
snprintf(error, maxlen, "One or more address lookups, patches or detours failed, please refer to startup logs for more information");
@@ -441,6 +445,7 @@ bool CS2Fixes::Unload(char* error, size_t maxlen)
441445
SH_REMOVE_HOOK_ID(g_iCGamePlayerEquipPrecacheId);
442446
SH_REMOVE_HOOK_ID(g_iCTriggerGravityPrecacheId);
443447
SH_REMOVE_HOOK_ID(g_iCTriggerGravityEndTouchId);
448+
SH_REMOVE_HOOK_ID(g_iSpawnId);
444449

445450
if (g_iSetGameSpawnGroupMgrId != -1)
446451
SH_REMOVE_HOOK_ID(g_iSetGameSpawnGroupMgrId);
@@ -1214,6 +1219,12 @@ void CS2Fixes::Hook_SetGameSpawnGroupMgr(IGameSpawnGroupMgr* pSpawnGroupMgr)
12141219
g_pSpawnGroupMgr = (CSpawnGroupMgrGameSystem*)pSpawnGroupMgr;
12151220
}
12161221

1222+
void CS2Fixes::Hook_SpawnPost(int nCount, const EntitySpawnInfo_t* pInfo)
1223+
{
1224+
for (int i = 0; i < nCount; i++)
1225+
g_pMapMigrations->OnEntitySpawned(pInfo[i].m_pEntity->m_pInstance, pInfo[i].m_pKeyValues);
1226+
}
1227+
12171228
void* CS2Fixes::OnMetamodQuery(const char* iface, int* ret)
12181229
{
12191230
if (V_strcmp(iface, CS2FIXES_INTERFACE))

src/cs2fixes.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,7 @@ class CS2Fixes : public ISmmPlugin, public IMetamodListener, public ICS2Fixes
109109
void Hook_DropWeaponPost(CBasePlayerWeapon* pWeapon, Vector* pVecTarget, Vector* pVelocity);
110110
int Hook_LoadEventsFromFile(const char* filename, bool bSearchAll);
111111
void Hook_SetGameSpawnGroupMgr(IGameSpawnGroupMgr* pSpawnGroupMgr);
112+
void Hook_SpawnPost(int nCount, const EntitySpawnInfo_t* pInfo);
112113

113114
public: // MetaMod API
114115
void* OnMetamodQuery(const char* iface, int* ret);

src/entitylistener.cpp

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@
2525
#include "entity/cgamerules.h"
2626
#include "entwatch.h"
2727
#include "gameconfig.h"
28-
#include "mapmigrations.h"
2928
#include "plat.h"
3029

3130
CEntityListener* g_pEntityListener = nullptr;
@@ -56,8 +55,6 @@ void CEntityListener::OnEntitySpawned(CEntityInstance* pEntity)
5655

5756
if (g_cvarEnableEntWatch.Get())
5857
EW_OnEntitySpawned(pEntity);
59-
60-
g_pMapMigrations->OnEntitySpawned(pEntity);
6158
}
6259

6360
void CEntityListener::OnEntityCreated(CEntityInstance* pEntity)

src/events.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
#include "idlemanager.h"
3131
#include "leader.h"
3232
#include "map_votes.h"
33+
#include "mapmigrations.h"
3334
#include "panoramavote.h"
3435
#include "recipientfilters.h"
3536
#include "topdefender.h"
@@ -85,6 +86,8 @@ GAME_EVENT_F(round_prestart)
8586

8687
if (g_cvarEnableEntWatch.Get())
8788
EW_RoundPreStart();
89+
90+
g_pMapMigrations->OnRoundPrestart();
8891
}
8992

9093
CConVar<bool> g_cvarBlockTeamMessages("cs2f_block_team_messages", FCVAR_NONE, "Whether to block team join messages", false);

src/mapmigrations.cpp

Lines changed: 30 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -40,29 +40,49 @@ void CMapMigrations::ApplyGameSettings(KeyValues* pKV)
4040
CMapMigrationWorkshopDetailsQuery::Create(pKV->FindKey("launchoptions")->GetUint64("customgamemode"));
4141
}
4242

43-
void CMapMigrations::OnEntitySpawned(CEntityInstance* pEntity)
43+
void CMapMigrations::OnRoundPrestart()
4444
{
45-
if (g_cvarMapMigrations20260121.Get() == 1 || (g_cvarMapMigrations20260121.Get() == 2 && m_timeMapUpdated < g_time20260121))
46-
Migration_20260121(pEntity);
45+
m_vecModelEntitiesUsingRendermodeEnum.clear();
4746
}
4847

49-
void CMapMigrations::Migration_20260121(CEntityInstance* pEntity)
48+
void CMapMigrations::OnEntitySpawned(CEntityInstance* pEntity, const CEntityKeyValues* pKeyValues)
5049
{
5150
CBaseEntity* pBaseEntity = (CBaseEntity*)pEntity;
5251

52+
// Stupid workaround for CEntityKeyValues being inaccessible after entity spawn
53+
// We need access to this in 2026-01-21 rendermode migrations when called from UpdateMapUpdateTime
54+
if (pBaseEntity->AsBaseModelEntity() && V_StringToInt32(pKeyValues->GetString("rendermode"), -1, NULL, NULL, PARSING_FLAG_SKIP_WARNING) == -1)
55+
m_vecModelEntitiesUsingRendermodeEnum.push_back(pBaseEntity->GetHandle());
56+
57+
RunMigrations(pBaseEntity);
58+
}
59+
60+
void CMapMigrations::RunMigrations(CBaseEntity* pEntity)
61+
{
62+
if (g_cvarMapMigrations20260121.Get() == 1 || (g_cvarMapMigrations20260121.Get() == 2 && m_timeMapUpdated < g_time20260121))
63+
Migrations_20260121(pEntity);
64+
}
65+
66+
void CMapMigrations::Migrations_20260121(CBaseEntity* pEntity)
67+
{
5368
if (!V_strcasecmp(pEntity->GetClassname(), "func_door_rotating"))
5469
{
55-
uint32 spawnFlags = pBaseEntity->m_spawnflags();
70+
uint32 spawnFlags = pEntity->m_spawnflags();
5671

57-
// Force add One-way flag if not present
58-
if (!(spawnFlags & 16))
59-
pBaseEntity->m_spawnflags = spawnFlags + 16;
72+
if (!(spawnFlags & SF_DOOR_ONEWAY))
73+
pEntity->m_spawnflags = spawnFlags + SF_DOOR_ONEWAY;
6074
}
6175

62-
CBaseModelEntity* pModelEntity = pBaseEntity->AsBaseModelEntity();
76+
CBaseModelEntity* pModelEntity = pEntity->AsBaseModelEntity();
6377

6478
if (pModelEntity)
6579
{
80+
// Also need to make sure the entity is using index-based rendermodes, and not enum-based ones (which will already automatically migrate correctly)
81+
// This differentiation is lost after entity spawn, so we had to check the original keyvalue earlier instead
82+
for (int i = 0; i < m_vecModelEntitiesUsingRendermodeEnum.size(); i++)
83+
if (m_vecModelEntitiesUsingRendermodeEnum[i] == pModelEntity->GetHandle())
84+
return;
85+
6686
RenderMode_t renderMode = pModelEntity->m_nRenderMode();
6787

6888
// Legacy kRenderTransAlpha
@@ -85,7 +105,7 @@ void CMapMigrations::UpdateMapUpdateTime(time_t timeMapUpdated)
85105

86106
// May be called late, so also check any existing entities first
87107
while ((pTarget = UTIL_FindEntityByName(pTarget, "*")))
88-
OnEntitySpawned(pTarget);
108+
RunMigrations(pTarget);
89109
}
90110

91111
std::shared_ptr<CMapMigrationWorkshopDetailsQuery> CMapMigrationWorkshopDetailsQuery::Create(uint64 iWorkshopId)

src/mapmigrations.h

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,12 +21,15 @@
2121

2222
#include "KeyValues.h"
2323
#include "convar.h"
24+
#include "ehandle.h"
2425
#include "entitysystem.h"
2526
#include "steam/isteamugc.h"
2627
#include <vector>
2728

2829
extern CConVar<int> g_cvarMapMigrations20260121;
2930

31+
#define SF_DOOR_ONEWAY 16
32+
3033
class CMapMigrationWorkshopDetailsQuery : public std::enable_shared_from_this<CMapMigrationWorkshopDetailsQuery>
3134
{
3235
public:
@@ -48,15 +51,18 @@ class CMapMigrations
4851
{
4952
public:
5053
void ApplyGameSettings(KeyValues* pKV);
51-
void OnEntitySpawned(CEntityInstance* pEntity);
52-
void Migration_20260121(CEntityInstance* pEntity);
54+
void OnRoundPrestart();
55+
void OnEntitySpawned(CEntityInstance* pEntity, const CEntityKeyValues* pKeyValues);
56+
void RunMigrations(CBaseEntity* pEntity);
57+
void Migrations_20260121(CBaseEntity* pEntity);
5358
void UpdateMapUpdateTime(time_t timeMapUpdated);
5459
void AddWorkshopDetailsQuery(std::shared_ptr<CMapMigrationWorkshopDetailsQuery> pQuery) { m_vecWorkshopDetailsQueries.push_back(pQuery); }
5560
void RemoveWorkshopDetailsQuery(std::shared_ptr<CMapMigrationWorkshopDetailsQuery> pQuery) { m_vecWorkshopDetailsQueries.erase(std::remove(m_vecWorkshopDetailsQueries.begin(), m_vecWorkshopDetailsQueries.end(), pQuery), m_vecWorkshopDetailsQueries.end()); }
5661

5762
private:
5863
time_t m_timeMapUpdated = std::numeric_limits<time_t>::max();
5964
std::vector<std::shared_ptr<CMapMigrationWorkshopDetailsQuery>> m_vecWorkshopDetailsQueries;
65+
std::vector<CHandle<CBaseEntity>> m_vecModelEntitiesUsingRendermodeEnum;
6066
};
6167

6268
extern CMapMigrations* g_pMapMigrations;

0 commit comments

Comments
 (0)