Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions Client/mods/deathmatch/logic/luadefs/CLuaEngineDefs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
*****************************************************************************/

#include "StdInc.h"
#include <multiplayer/CMultiplayer.h>
#include <game/CColPoint.h>
#include <game/CObjectGroupPhysicalProperties.h>
#include <game/CStreaming.h>
Expand Down Expand Up @@ -83,6 +84,17 @@ size_t EngineStreamingGetBufferSize()
return g_pGame->GetStreaming()->GetStreamingBufferSize();
}

// Opt-in SilentPatch-style fixes for stock vehicle.dff hierarchy typos (wheel_lm_dummy vs wheel_lm, etc.).
void EngineSetVehicleHierarchyTypoFixesEnabled(bool enabled)
{
g_pCore->GetMultiplayer()->SetVehicleHierarchyTypoFixesEnabled(enabled);
}

bool EngineGetVehicleHierarchyTypoFixesEnabled()
{
return g_pCore->GetMultiplayer()->GetVehicleHierarchyTypoFixesEnabled();
}

void CLuaEngineDefs::LoadFunctions()
{
constexpr static const std::pair<const char*, lua_CFunction> functions[]{
Expand Down Expand Up @@ -163,6 +175,8 @@ void CLuaEngineDefs::LoadFunctions()
{"enginePreloadWorldArea", ArgumentParser<EnginePreloadWorldArea>},
{"engineRestreamModel", ArgumentParser<EngineRestreamModel>},
{"engineRestream", ArgumentParser<EngineRestream>},
{"engineSetVehicleHierarchyTypoFixesEnabled", ArgumentParser<EngineSetVehicleHierarchyTypoFixesEnabled>},
{"engineGetVehicleHierarchyTypoFixesEnabled", ArgumentParser<EngineGetVehicleHierarchyTypoFixesEnabled>},

// CLuaCFunctions::AddFunction ( "engineReplaceMatchingAtomics", EngineReplaceMatchingAtomics );
// CLuaCFunctions::AddFunction ( "engineReplaceWheelAtomics", EngineReplaceWheelAtomics );
Expand Down Expand Up @@ -212,6 +226,9 @@ void CLuaEngineDefs::AddClass(lua_State* luaVM)
lua_classfunction(luaVM, "setModelTXDID", "engineSetModelTXDID");
lua_classfunction(luaVM, "resetModelTXDID", "engineResetModelTXDID");

lua_classfunction(luaVM, "setVehicleHierarchyTypoFixesEnabled", "engineSetVehicleHierarchyTypoFixesEnabled");
lua_classfunction(luaVM, "getVehicleHierarchyTypoFixesEnabled", "engineGetVehicleHierarchyTypoFixesEnabled");

lua_registerstaticclass(luaVM, "Engine");

// `EngineStreaming` class
Expand Down
3 changes: 3 additions & 0 deletions Client/multiplayer_sa/CMultiplayerSA.h
Original file line number Diff line number Diff line change
Expand Up @@ -356,6 +356,9 @@ class CMultiplayerSA : public CMultiplayer
void SetBoatWaterSplashEnabled(bool bEnabled);
void SetTyreSmokeEnabled(bool bEnabled);

void SetVehicleHierarchyTypoFixesEnabled(bool bEnabled) override;
bool GetVehicleHierarchyTypoFixesEnabled() const override;

void SetLastStaticAnimationPlayed(eAnimGroup dwGroupID, eAnimID dwAnimID, DWORD dwAnimArrayAddress)
{
m_dwLastStaticAnimGroupID = dwGroupID;
Expand Down
82 changes: 82 additions & 0 deletions Client/multiplayer_sa/CMultiplayerSA_VehicleHierarchyTypoFixes.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
/*****************************************************************************
*
* PROJECT: Multi Theft Auto
* LICENSE: See LICENSE in the top level directory
* FILE: multiplayer_sa/CMultiplayerSA_VehicleHierarchyTypoFixes.cpp
* PURPOSE: Optional CVehicleModelInfo hierarchy compare hook (matches SilentPatch SA)
*
* GTA matches IDE/handling component names to frame names via _stricmp inside
* CVehicleModelInfo setup. Stock models use inconsistent spellings; SilentPatch
* wraps that call to accept both canonical and typo names. We expose the same
* behaviour only when enabled so servers can keep fully vanilla visuals.
*
*****************************************************************************/

#include "StdInc.h"
#include "CMultiplayerSA.h"

#include <utility>

namespace
{
// Off by default (matches stock GTA SA without SilentPatch).
bool g_bVehicleHierarchyTypoFixesEnabled = false;

int(__cdecl* g_pfnOrgStricmp)(const char*, const char*) = nullptr;

// Pairs are { correct name, name as in shipping .dff }. Must be sorted by the second string.
static constexpr std::pair<const char*, const char*> typosAndFixes[] = {
{"boat_moving_hi", "boat_moving"},
{"elevator_r", "elevator"},
{"misc_a", "misca"},
{"misc_b", "miscb"},
{"taillights", "tailights"},
{"taillights2", "tailights2"},
{"transmission_f", "transmision_f"},
{"transmission_r", "transmision_r"},
{"wheel_lm_dummy", "wheel_lm"},
};

int __cdecl Hooked_VehicleHierarchy_Stricmp(const char* dataName, const char* nodeName)
{
if (!g_bVehicleHierarchyTypoFixesEnabled)
return g_pfnOrgStricmp(dataName, nodeName);

const int origComp = g_pfnOrgStricmp(dataName, nodeName);
if (origComp == 0)
return 0;

for (const auto& typo : typosAndFixes)
{
const int nodeComp = _stricmp(typo.second, nodeName);
if (nodeComp > 0)
break;

if (nodeComp == 0 && _stricmp(typo.first, dataName) == 0)
return 0;
}

return origComp;
}
} // namespace

void InitVehicleHierarchyTypoFixesHook()
{
// SA 1.0 US: indirect call to _stricmp while CVehicleModelInfo binds handling/IDE component names to clump frames.
// Same site hooked by SilentPatch (`InterceptCall(0x4C5311, ...)`).
constexpr DWORD CALLSITE = 0x4C5311;
HookCheckOriginalByte(CALLSITE, 0xE8);
const DWORD rel = *reinterpret_cast<DWORD*>(CALLSITE + 1);
g_pfnOrgStricmp = reinterpret_cast<decltype(g_pfnOrgStricmp)>(CALLSITE + 5 + rel);
HookInstallCall(CALLSITE, reinterpret_cast<DWORD>(&Hooked_VehicleHierarchy_Stricmp));
}

void CMultiplayerSA::SetVehicleHierarchyTypoFixesEnabled(bool bEnabled)
{
g_bVehicleHierarchyTypoFixesEnabled = bEnabled;
}

bool CMultiplayerSA::GetVehicleHierarchyTypoFixesEnabled() const
{
return g_bVehicleHierarchyTypoFixesEnabled;
}
12 changes: 12 additions & 0 deletions Client/multiplayer_sa/CMultiplayerSA_VehicleHierarchyTypoFixes.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
/*****************************************************************************
*
* PROJECT: Multi Theft Auto
* LICENSE: See LICENSE in the top level directory
* FILE: multiplayer_sa/CMultiplayerSA_VehicleHierarchyTypoFixes.h
* PURPOSE: Init for optional vehicle.dff hierarchy typo matching (SilentPatch-style)
*
*****************************************************************************/

#pragma once

void InitVehicleHierarchyTypoFixesHook();
4 changes: 4 additions & 0 deletions Client/multiplayer_sa/CMultiplayerSA_Vehicles.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
*****************************************************************************/

#include "StdInc.h"
#include "CMultiplayerSA_VehicleHierarchyTypoFixes.h"

static bool __fastcall AreVehicleDoorsUndamageable(CVehicleSAInterface* vehicle)
{
Expand Down Expand Up @@ -72,4 +73,7 @@ static void __declspec(naked) HOOK_CDamageManager__ProgressDoorDamage()
void CMultiplayerSA::InitHooks_Vehicles()
{
EZHookInstall(CDamageManager__ProgressDoorDamage);

// SilentPatch-style optional matching for typo'd vehicle component frame names (see CMultiplayerSA_VehicleHierarchyTypoFixes.cpp).
InitVehicleHierarchyTypoFixesHook();
}
5 changes: 5 additions & 0 deletions Client/sdk/multiplayer/CMultiplayer.h
Original file line number Diff line number Diff line change
Expand Up @@ -463,6 +463,11 @@ class CMultiplayer
virtual void SetBoatWaterSplashEnabled(bool bEnabled) = 0;
virtual void SetTyreSmokeEnabled(bool bEnabled) = 0;

// When enabled, matches canonical vehicle component names to the typo'd hierarchy nodes in stock GTA SA models
// (SilentPatch-style fix). Off by default so behaviour stays vanilla unless a script opts in.
virtual void SetVehicleHierarchyTypoFixesEnabled(bool bEnabled) = 0;
virtual bool GetVehicleHierarchyTypoFixesEnabled() const = 0;

virtual eAnimGroup GetLastStaticAnimationGroupID() = 0;
virtual eAnimID GetLastStaticAnimationID() = 0;
virtual DWORD GetLastAnimArrayAddress() = 0;
Expand Down
Loading