diff --git a/src/Components/Modules/ModelCache.cpp b/src/Components/Modules/ModelCache.cpp index 6e4629569..d02fccf35 100644 --- a/src/Components/Modules/ModelCache.cpp +++ b/src/Components/Modules/ModelCache.cpp @@ -35,41 +35,64 @@ namespace Components ModelCache::ModelCache() { + static constexpr std::array fieldsToUpdate + { + // clientState + "modelindex", + "attachModelIndex[0]", + "attachModelIndex[1]", + "attachModelIndex[2]", + "attachModelIndex[3]", + "attachModelIndex[4]", + "attachModelIndex[5]", + + // playerState + "viewmodelIndex", + + // entityState + "lerp.u.scriptMover.xModelIndex" + }; + // To push the model limit we need to update the network protocol because it uses custom integer size // (currently 9 bits per model, not enough) - const auto oldBitLength = static_cast(std::floor(std::log2(BASE_GMODEL_COUNT - 1)) + 1); - const auto newBitLength = static_cast(std::floor(std::log2(G_MODELINDEX_LIMIT - 1)) + 1); - - assert(oldBitLength == 9); - - if (oldBitLength != newBitLength) + static constexpr auto oldBitLength = static_cast(std::bit_width(static_cast(BASE_GMODEL_COUNT - 1))); + static constexpr auto newBitLength = static_cast(std::bit_width(static_cast(G_MODELINDEX_LIMIT - 1))); + static_assert(oldBitLength == 9 && newBitLength == 12); + + std::array found{}; + + for (auto& netfield : Game::netfields) { - static const std::unordered_set fieldsToUpdate = { - "modelindex", - "attachModelIndex[0]", - "attachModelIndex[1]", - "attachModelIndex[2]", - "attachModelIndex[3]", - "attachModelIndex[4]", - "attachModelIndex[5]", - }; - - size_t currentBitOffset = 0; - - for (size_t i = 0; i < Game::clientStateFieldsCount; i++) + for (size_t i = 0; i < fieldsToUpdate.size(); ++i) { - auto field = & Game::clientStateFields[i]; - - if (fieldsToUpdate.contains(field->name)) + if (!found[i] && netfield.name == fieldsToUpdate[i]) { - assert(static_cast(field->bits) == oldBitLength); - - Utils::Hook::Set(&field->bits, newBitLength); - currentBitOffset++; + assert(static_cast(netfield.bits) == oldBitLength); + + Utils::Hook::Set(&netfield.bits, newBitLength); + found[i] = true; + + assert(static_cast(netfield.bits) == newBitLength); + break; } } } + + assert(std::accumulate(found.begin(), found.end(), 0u) == found.size()); + for (auto& netfield : Game::netfields) + { + static_assert(offsetof(Game::entityState_s, index) == 144); + + if (netfield.offset == offsetof(Game::entityState_s, index) && (netfield.name == "index"sv || netfield.name == "index.item"sv)) + { + // Some of the bit lengths for these index fields are already 15 + assert(netfield.bits <= 15); + + Utils::Hook::Set(&netfield.bits, std::max(15u, newBitLength)); + } + } + // Reallocate G_ModelIndex Utils::Hook::Set(0x420654 + 3, ModelCache::cached_models_reallocated); Utils::Hook::Set(0x43BCE4 + 3, ModelCache::cached_models_reallocated); diff --git a/src/Game/Game.cpp b/src/Game/Game.cpp index 3189ef6f3..8daf1f5fc 100644 --- a/src/Game/Game.cpp +++ b/src/Game/Game.cpp @@ -22,8 +22,7 @@ namespace Game char(*g_cmdlineCopy)[1024] = reinterpret_cast(0x1AD7AB0); - NetField* clientStateFields = reinterpret_cast(0x741E40); - size_t clientStateFieldsCount = Utils::Hook::Get(0x7433C8); + std::span netfields(reinterpret_cast(0x73E3A0), reinterpret_cast(0x742FA0)); MssLocal* milesGlobal = reinterpret_cast(0x649A1A0); diff --git a/src/Game/Game.hpp b/src/Game/Game.hpp index 368777292..5d95f5561 100644 --- a/src/Game/Game.hpp +++ b/src/Game/Game.hpp @@ -63,8 +63,7 @@ namespace Game extern char(*g_cmdlineCopy)[1024]; // This does not belong anywhere else - extern NetField* clientStateFields; - extern size_t clientStateFieldsCount; + extern std::span netfields; extern MssLocal* milesGlobal; extern WinVars_t* g_wv;