- Статус
- Оффлайн
- Регистрация
- 13 Фев 2026
- Сообщения
- 682
- Реакции
- 18
Классическая болячка для Source-движка и старых проектов типа Day of Defeat. Суть проблемы: если инжектить софт до захода на сервер или во время загрузки, игра намертво падает в краш. При этом, если заинжектиться уже в процессе катки — всё работает ровно.
В ходе дебага выяснилось, что при загрузке на сервер, где уже есть активные игроки, хук DrawModelExecute (DME) начинает сходить с ума и падать с ошибкой обращения к памяти. Судя по всему, идет попытка считать данные сущностей, которые еще не проинициализированы до конца.
Сам код проблемного хука:
Как оказалось, виновником торжества была попытка проверить класс оружия через IsClassWeapon. Видимо, на этапе загрузки pCC->m_ClassID выдает какую-то дичь, из-за чего всё падает в рекурсию или обращается к невалидному адресу.
Фикс:
Просто убираем проверку оружия в дефолтном кейсе или добавляем более жесткие проверки на валидность ent и pCC перед тем, как лезть в m_ClassID.
Интересно, это специфика DOD или на других Source-играх этот метод тоже так триггерит краши при загрузке?
В ходе дебага выяснилось, что при загрузке на сервер, где уже есть активные игроки, хук DrawModelExecute (DME) начинает сходить с ума и падать с ошибкой обращения к памяти. Судя по всему, идет попытка считать данные сущностей, которые еще не проинициализированы до конца.
Сам код проблемного хука:
Код:
void __fastcall ModelRender::DrawModelExecute::Detour(void* __this, void* ecx, const DrawModelState_t& state, const ModelRenderInfo_t& pInfo, matrix3x4_t* pCustomBoneToWorld)
{
FN o = VTable.Original<FN>(index);
if (!o)
return;
bool didDraw = false;
C_DODPlayer* plr = I::EntityList->GetClientEntity(I::EngineClient->GetLocalPlayer())->Cast<C_DODPlayer*>();
C_DODPlayer* otherPlr = I::EntityList->GetClientEntity(pInfo.entity_index)->Cast<C_DODPlayer*>();
if (I::EngineClient->IsInGame() && I::EngineClient->IsConnected() && pInfo.pRenderable && pInfo.pModel && pInfo.entity_index)
{
IClientEntity* ent = I::EntityList->GetClientEntity(pInfo.entity_index);
if (ent)
{
ClientClass* pCC = ent->GetClientClass();
if (pCC)
{
ChamItem* cham = 0;
switch (pCC->m_ClassID)
{
case EClientClass::CDODPlayer:
{
C_DODPlayer* otherPlr = ent->Cast<C_DODPlayer*>();
C_DODPlayer* plr = I::EntityList->GetClientEntity(I::EngineClient->GetLocalPlayer())->Cast<C_DODPlayer*>();
if (plr && otherPlr)
{
cham = &Hacks::Visuals::Chams.Enemies;
}
break;
}
case EClientClass::CDODRagdoll:
didDraw = true;
cham = &Hacks::Visuals::Chams.Ragdoll;
break;
case EClientClass::CDODViewModel:
didDraw = true;
cham = &Hacks::Visuals::Chams.Viewmodel;
break;
default:
if (U::Game.IsClassWeapon(pCC->m_ClassID))
{
didDraw = true;
cham = &Hacks::Visuals::Chams.Weapons;
}
break;
}
if (didDraw)
{
C_DODPlayer* player = ent->Cast<C_DODPlayer*>();
if (!player->deadflag())
{
IMaterial* material = 0;
if (cham->Material != ChamMaterial::DEFAULT)
{
material = I::MaterialSystem->FindMaterial(Hacks::Visuals::GetMaterialFromChamType(cham->Material), TEXTURE_GROUP_MODEL);
material->AddRef();
material->SetMaterialVarFlag(MATERIAL_VAR_IGNOREZ, true);
}
I::RenderView->SetBlend(cham->clrHidden.a());
I::RenderView->SetColorModulation(cham->clrHidden.fCol);
if(cham->Material != ChamMaterial::DEFAULT)
I::ModelRender->ForcedMaterialOverride(material);
o(__this, ecx, state, pInfo, pCustomBoneToWorld);
if(cham->Material != ChamMaterial::DEFAULT)
material->SetMaterialVarFlag(MATERIAL_VAR_IGNOREZ, false);
I::RenderView->SetBlend(cham->clrVisible.a());
I::RenderView->SetColorModulation(cham->clrVisible.fCol);
if(cham->Material != ChamMaterial::DEFAULT)
I::ModelRender->ForcedMaterialOverride(material);
o(__this, ecx, state, pInfo, pCustomBoneToWorld);
constexpr float white[4] = { 1.f, 1.f, 1.f, 1.f };
I::RenderView->SetBlend(1.f);
I::RenderView->SetColorModulation(white);
if (cham->Material != ChamMaterial::DEFAULT)
I::ModelRender->ForcedMaterialOverride(nullptr);
}
}
}
}
}
if(!didDraw)
o(__this, ecx, state, pInfo, pCustomBoneToWorld);
}
Как оказалось, виновником торжества была попытка проверить класс оружия через IsClassWeapon. Видимо, на этапе загрузки pCC->m_ClassID выдает какую-то дичь, из-за чего всё падает в рекурсию или обращается к невалидному адресу.
Фикс:
Просто убираем проверку оружия в дефолтном кейсе или добавляем более жесткие проверки на валидность ent и pCC перед тем, как лезть в m_ClassID.
Код:
// Этот кусок вешал всё приложение
if (U::Game.IsClassWeapon(pCC->m_ClassID))
{
didDraw = true;
cham = &Hacks::Visuals::Chams.Weapons;
}
Интересно, это специфика DOD или на других Source-играх этот метод тоже так триггерит краши при загрузке?