massive changes to Player Variables structure

This commit is contained in:
EricPlayZ
2025-01-06 09:11:01 +02:00
parent 2e335c5a9c
commit 24a343d3f9
7 changed files with 477 additions and 243 deletions

View File

@ -4,6 +4,8 @@
namespace EGSDK::GamePH {
class EGameSDK_API InventoryMoney {
public:
ClassHelpers::buffer<0x38, int> oldWorldMoney;
union {
ClassHelpers::buffer<0x38, int> oldWorldMoney;
};
};
}

View File

@ -7,7 +7,7 @@ namespace EGSDK::GamePH {
class EGameSDK_API PlayerState {
public:
union {
ClassHelpers::buffer<0x300, PlayerVariables*> pPlayerVariables;
ClassHelpers::buffer<0x300, PlayerVariables*> playerVariables;
};
static PlayerState* Get();

View File

@ -2,24 +2,66 @@
#include <any>
#include <unordered_map>
#include <vector>
#include <memory>
#include <EGSDK\ClassHelpers.h>
#include <EGSDK\Utils\Values.h>
namespace EGSDK::GamePH {
enum EGameSDK_API PlayerVarType {
NONE = 0,
String,
Float,
Bool
};
class EGameSDK_API PlayerVariable {
public:
PlayerVariable(const std::string& name);
const char* GetName();
void SetName(const std::string& newName);
PlayerVarType GetType();
void SetType(PlayerVarType newType);
private:
static std::unordered_map<PlayerVariable*, std::string> playerVarNames;
static std::unordered_map<PlayerVariable*, PlayerVarType> playerVarTypes;
};
class EGameSDK_API StringPlayerVariable : public PlayerVariable {
public:
union {
EGSDK::ClassHelpers::buffer<0x8, const char*> value; // remove 0x2 bit to access ptr
EGSDK::ClassHelpers::buffer<0x10, const char*> defaultValue; // remove 0x2 bit to access ptr
};
StringPlayerVariable(const std::string& name);
};
class EGameSDK_API FloatPlayerVariable : public PlayerVariable {
public:
union {
EGSDK::ClassHelpers::buffer<0x8, float> value;
EGSDK::ClassHelpers::buffer<0xC, float> defaultValue;
};
FloatPlayerVariable(const std::string& name);
};
class EGameSDK_API BoolPlayerVariable : public PlayerVariable {
public:
union {
EGSDK::ClassHelpers::buffer<0x8, bool> value;
EGSDK::ClassHelpers::buffer<0x9, bool> defaultValue;
};
BoolPlayerVariable(const std::string& name);
};
class EGameSDK_API PlayerVariables {
public:
enum PlayerVarType {
NONE = 0,
String,
Float,
Bool
};
static std::vector<std::pair<std::string, std::pair<void*, std::string>>> playerVars;
static std::vector<std::pair<std::string, std::pair<std::any, std::string>>> playerVarsDefault;
static std::vector<std::pair<std::string, std::pair<std::any, std::string>>> playerCustomVarsDefault;
static std::vector<std::unique_ptr<PlayerVariable>> playerVars;
static std::vector<std::unique_ptr<PlayerVariable>> defaultPlayerVars;
static std::vector<std::unique_ptr<PlayerVariable>> customDefaultPlayerVars;
static bool gotPlayerVars;
static std::unordered_map<std::string, std::any> prevPlayerVarValueMap;
static std::unordered_map<std::string, bool> prevBoolValueMap;
#ifdef EGameSDK_EXPORTS
static void GetPlayerVars();
static bool SortPlayerVars();
@ -27,70 +69,99 @@ namespace EGSDK::GamePH {
template <typename T>
static T getDefaultValue() {
static_assert(std::is_same_v<T, std::string> || std::is_same_v<T, float> || std::is_same_v<T, bool>, "Invalid type: value must be string, float or bool");
if constexpr (std::is_same_v<T, std::string>)
return {};
else if constexpr (std::is_same_v<T, bool>)
return false;
else if constexpr (std::is_same_v<T, float>)
return -404.0f;
else if constexpr (std::is_same_v<T, bool>)
return false;
else
return T();
}
template <typename T>
static T GetPlayerVar(const std::string& playerVar) {
static_assert(std::is_same_v<T, bool> || std::is_same_v<T, float> || std::is_same_v<T, std::string>, "Invalid type: value must be bool, float or string");
static T GetPlayerVarValue(const std::string& name) {
static_assert(std::is_same_v<T, std::string> || std::is_same_v<T, float> || std::is_same_v<T, bool>, "Invalid type: value must be string, float or bool");
if (!gotPlayerVars)
return getDefaultValue<T>();
auto it = std::find_if(PlayerVariables::playerVars.begin(), PlayerVariables::playerVars.end(), [&playerVar](const auto& pair) {
return pair.first == playerVar;
auto playerVarIt = std::find_if(playerVars.begin(), playerVars.end(), [&name](const auto& playerVar) {
return playerVar->GetName() == name;
});
if (it == PlayerVariables::playerVars.end())
if (playerVarIt == playerVars.end())
return getDefaultValue<T>();
return *reinterpret_cast<T*>(it->second.first);
}
template <typename T>
static void ChangePlayerVar(const std::string& playerVar, const T value) {
static_assert(std::is_same_v<T, bool> || std::is_same_v<T, float> || std::is_same_v<T, std::string>, "Invalid type: value must be bool, float or string");
if (!gotPlayerVars)
return;
auto it = std::find_if(PlayerVariables::playerVars.begin(), PlayerVariables::playerVars.end(), [&playerVar](const auto& pair) {
return pair.first == playerVar;
});
if (it == PlayerVariables::playerVars.end())
return;
if (std::is_same_v<T, std::string>) {
std::string valueStr = Utils::Values::to_string(value);
if (it->second.second == "float") {
float* const varValue = reinterpret_cast<float*>(it->second.first);
const float actualValue = std::stof(valueStr);
*varValue = actualValue;
*(varValue + 1) = actualValue;
} else {
bool* const varValue = reinterpret_cast<bool*>(it->second.first);
const bool actualValue = valueStr == "true";
*varValue = actualValue;
*(varValue + 1) = actualValue;
}
} else {
T* const varValue = reinterpret_cast<T*>(it->second.first);
*varValue = value;
*(varValue + 1) = value;
auto playerVar = playerVarIt->get();
if constexpr (std::is_same_v<T, std::string>) {
StringPlayerVariable* stringPlayerVar = reinterpret_cast<StringPlayerVariable*>(playerVar);
return stringPlayerVar->value.data;
} else if constexpr (std::is_same_v<T, float>) {
FloatPlayerVariable* floatPlayerVar = reinterpret_cast<FloatPlayerVariable*>(playerVar);
return floatPlayerVar->value.data;
} else if constexpr (std::is_same_v<T, bool>) {
BoolPlayerVariable* boolPlayerVar = reinterpret_cast<BoolPlayerVariable*>(playerVar);
return boolPlayerVar->value.data;
}
}
template <typename T>
static void ChangePlayerVar(const std::string& name, const T value) {
static_assert(std::is_same_v<T, std::string> || std::is_same_v<T, float> || std::is_same_v<T, bool>, "Invalid type: value must be string, float or bool");
if (!gotPlayerVars)
return;
auto playerVarIt = std::find_if(playerVars.begin(), playerVars.end(), [&name](const auto& playerVar) {
return playerVar->GetName() == name;
});
if (playerVarIt == playerVars.end())
return;
auto playerVar = playerVarIt->get();
if constexpr (std::is_same_v<T, std::string>) {
switch (playerVar->GetType()) {
case PlayerVarType::String:
// TO IMPLEMENT
break;
case PlayerVarType::Float: {
std::string valueStr = Utils::Values::to_string(value);
float actualValue = std::stof(valueStr);
FloatPlayerVariable* floatPlayerVar = reinterpret_cast<FloatPlayerVariable*>(playerVar);
floatPlayerVar->value = actualValue;
floatPlayerVar->defaultValue = actualValue;
break;
}
case PlayerVarType::Bool: {
std::string valueStr = Utils::Values::to_string(value);
bool actualValue = valueStr == "true";
BoolPlayerVariable* boolPlayerVar = reinterpret_cast<BoolPlayerVariable*>(playerVar);
boolPlayerVar->value = actualValue;
boolPlayerVar->defaultValue = actualValue;
break;
}
default:
break;
}
} else if constexpr (std::is_same_v<T, float>) {
if (playerVar->GetType() != PlayerVarType::Float)
return;
FloatPlayerVariable* floatPlayerVar = reinterpret_cast<FloatPlayerVariable*>(playerVar);
floatPlayerVar->value = value;
floatPlayerVar->defaultValue = value;
} else if constexpr (std::is_same_v<T, bool>) {
if (playerVar->GetType() != PlayerVarType::Bool)
return;
BoolPlayerVariable* boolPlayerVar = reinterpret_cast<BoolPlayerVariable*>(playerVar);
boolPlayerVar->value = value;
boolPlayerVar->defaultValue = value;
}
}
static std::unordered_map<std::string, std::any> prevPlayerVarValueMap;
static std::unordered_map<std::string, bool> prevBoolValueMap;
template <typename T>
static void ManagePlayerVarByBool(const std::string& playerVar, const T valueIfTrue, const T valueIfFalse, bool boolVal, bool usePreviousVal = true) {
@ -98,19 +169,19 @@ namespace EGSDK::GamePH {
return;
if (prevPlayerVarValueMap.find(playerVar) == prevPlayerVarValueMap.end())
prevPlayerVarValueMap[playerVar] = GamePH::PlayerVariables::GetPlayerVar<T>(playerVar);
prevPlayerVarValueMap[playerVar] = GetPlayerVarValue<T>(playerVar);
if (prevBoolValueMap.find(playerVar) == prevBoolValueMap.end())
prevBoolValueMap[playerVar] = false;
if (boolVal) {
if (!prevBoolValueMap[playerVar])
prevPlayerVarValueMap[playerVar] = GamePH::PlayerVariables::GetPlayerVar<T>(playerVar);
prevPlayerVarValueMap[playerVar] = GetPlayerVarValue<T>(playerVar);
GamePH::PlayerVariables::ChangePlayerVar(playerVar, valueIfTrue);
ChangePlayerVar(playerVar, valueIfTrue);
prevBoolValueMap[playerVar] = true;
} else if (prevBoolValueMap[playerVar]) {
prevBoolValueMap[playerVar] = false;
GamePH::PlayerVariables::ChangePlayerVar(playerVar, usePreviousVal ? std::any_cast<T>(prevPlayerVarValueMap[playerVar]) : valueIfFalse);
ChangePlayerVar(playerVar, usePreviousVal ? std::any_cast<T>(prevPlayerVarValueMap[playerVar]) : valueIfFalse);
}
}

View File

@ -10,7 +10,8 @@ namespace EGSDK::Utils {
extern EGameSDK_API float round_decimal(float value, int decimal_places = 2);
extern EGameSDK_API bool str_replace(std::string& str, const std::string& from, const std::string& to);
template <typename T> auto to_string(T val) {
template <typename T>
auto to_string(T val) {
if constexpr (std::is_same<T, std::string>::value)
return static_cast<std::string>(val);
else

View File

@ -1,4 +1,5 @@
#include <Windows.h>
#include <algorithm>
#include <spdlog\spdlog.h>
#include <EGSDK\Offsets.h>
#include <EGSDK\GamePH\PlayerState.h>
@ -6,59 +7,160 @@
#include <EGSDK\ClassHelpers.h>
namespace EGSDK::GamePH {
static const int FLOAT_VAR_OFFSET = 3;
static const int BOOL_VAR_OFFSET = 2;
static const int VAR_LOC_OFFSET = 1;
static constexpr int STRING_SIZE_OFFSET = 3;
static constexpr int FLOAT_SIZE_OFFSET = 3;
static constexpr int BOOL_SIZE_OFFSET = 2;
std::vector<std::pair<std::string, std::pair<void*, std::string>>> PlayerVariables::playerVars;
std::vector<std::pair<std::string, std::pair<std::any, std::string>>> PlayerVariables::playerVarsDefault;
std::vector<std::pair<std::string, std::pair<std::any, std::string>>> PlayerVariables::playerCustomVarsDefault;
std::unordered_map<PlayerVariable*, std::string> PlayerVariable::playerVarNames{};
std::unordered_map<PlayerVariable*, PlayerVarType> PlayerVariable::playerVarTypes{};
PlayerVariable::PlayerVariable(const std::string& name) {
playerVarNames[this] = name;
playerVarTypes[this] = PlayerVarType::NONE;
}
const char* PlayerVariable::GetName() {
auto it = playerVarNames.find(this);
if (it != playerVarNames.end()) {
return it->second.c_str();
}
return nullptr;
}
void PlayerVariable::SetName(const std::string& newName) {
playerVarNames[this] = newName;
}
PlayerVarType PlayerVariable::GetType() {
auto it = playerVarTypes.find(this);
if (it != playerVarTypes.end()) {
return it->second;
}
return PlayerVarType::NONE;
}
void PlayerVariable::SetType(PlayerVarType newType) {
playerVarTypes[this] = newType;
}
StringPlayerVariable::StringPlayerVariable(const std::string& name) : PlayerVariable(name) {
SetType(PlayerVarType::String);
}
FloatPlayerVariable::FloatPlayerVariable(const std::string& name) : PlayerVariable(name) {
SetType(PlayerVarType::Float);
}
BoolPlayerVariable::BoolPlayerVariable(const std::string& name) : PlayerVariable(name) {
SetType(PlayerVarType::Bool);
}
std::vector<std::unique_ptr<PlayerVariable>> PlayerVariables::playerVars{};
std::vector<std::unique_ptr<PlayerVariable>> PlayerVariables::defaultPlayerVars{};
std::vector<std::unique_ptr<PlayerVariable>> PlayerVariables::customDefaultPlayerVars{};
bool PlayerVariables::gotPlayerVars = false;
static bool sortedPlayerVars = false;
std::unordered_map<std::string, std::any> PlayerVariables::prevPlayerVarValueMap{};
std::unordered_map<std::string, bool> PlayerVariables::prevBoolValueMap{};
template <typename T>
static void updateDefaultVar(std::vector<std::pair<std::string, std::pair<std::any, std::string>>>& defaultVars, const std::string& varName, T varValue) {
static_assert(std::is_same<T, float>::value || std::is_same<T, bool>::value, "Invalid type: value must be float or bool");
static void updateDefaultVar(std::vector<std::unique_ptr<PlayerVariable>>& defaultVars, const std::string& name, T value, T defaultValue) {
static_assert(std::is_same_v<T, std::string> || std::is_same_v<T, float> || std::is_same_v<T, bool>, "Invalid type: value must be string, float or bool");
auto it = std::find_if(defaultVars.begin(), defaultVars.end(), [&varName](const auto& pair) {
return pair.first == varName;
auto playerVarIt = std::find_if(defaultVars.begin(), defaultVars.end(), [&name](const auto& playerVar) {
return playerVar->GetName() == name;
});
if (it == defaultVars.end())
return;
it->second.first.template emplace<T>(varValue);
if (playerVarIt == defaultVars.end()) {
if constexpr (std::is_same_v<T, std::string>) {
auto stringPlayerVar = std::make_unique<StringPlayerVariable>(name);
stringPlayerVar->value = value;
stringPlayerVar->defaultValue = defaultValue;
defaultVars.emplace_back(std::move(stringPlayerVar));
}
else if constexpr (std::is_same_v<T, float>) {
auto floatPlayerVar = std::make_unique<FloatPlayerVariable>(name);
floatPlayerVar->value = value;
floatPlayerVar->defaultValue = defaultValue;
defaultVars.emplace_back(std::move(floatPlayerVar));
}
else if constexpr (std::is_same_v<T, bool>) {
auto boolPlayerVar = std::make_unique<BoolPlayerVariable>(name);
boolPlayerVar->value = value;
boolPlayerVar->defaultValue = defaultValue;
defaultVars.emplace_back(std::move(boolPlayerVar));
}
} else {
if constexpr (std::is_same_v<T, std::string>) {
// TO IMPLEMENT
return;
} else if constexpr (std::is_same_v<T, float>) {
auto floatPlayerVar = reinterpret_cast<FloatPlayerVariable*>(playerVarIt->get());
floatPlayerVar->value = value;
floatPlayerVar->defaultValue = defaultValue;
} else if constexpr (std::is_same_v<T, bool>) {
auto boolPlayerVar = reinterpret_cast<BoolPlayerVariable*>(playerVarIt->get());
boolPlayerVar->value = value;
boolPlayerVar->defaultValue = defaultValue;
}
}
}
static void processPlayerVar(DWORD64*(*playerVarsGetter)(), std::pair<std::string, std::pair<void*, std::string>>& var) {
static void processPlayerVar(DWORD64*(*playerVarsGetter)(), std::unique_ptr<PlayerVariable>& playerVar) {
static int offset = 0;
static const std::string floatPlayerVarClassName = "FloatPlayerVariable";
static const std::string boolPlayerVarClassName = "BoolPlayerVariable";
int offsetDif = 0;
while (true) {
const std::string vTableName = Utils::RTTI::GetVTableName(playerVarsGetter() + offset);
const bool isFloatPlayerVar = vTableName == floatPlayerVarClassName;
const bool isBoolPlayerVar = vTableName == boolPlayerVarClassName;
std::string vTableName = Utils::RTTI::GetVTableName(playerVarsGetter() + offset);
if (vTableName != "StringPlayerVariable" && vTableName != "FloatPlayerVariable" && vTableName != "BoolPlayerVariable") {
if (offsetDif > 150)
return;
if (isFloatPlayerVar || isBoolPlayerVar) {
var.second.first = playerVarsGetter() + offset + VAR_LOC_OFFSET;
const std::string& varName = var.first;
if (isFloatPlayerVar) {
float* varValue = reinterpret_cast<float*>(var.second.first);
updateDefaultVar(GamePH::PlayerVariables::playerVarsDefault, varName, *varValue);
updateDefaultVar(GamePH::PlayerVariables::playerCustomVarsDefault, varName, *varValue);
offset += FLOAT_VAR_OFFSET;
} else {
bool* varValue = reinterpret_cast<bool*>(var.second.first);
updateDefaultVar(GamePH::PlayerVariables::playerVarsDefault, varName, *varValue);
updateDefaultVar(GamePH::PlayerVariables::playerCustomVarsDefault, varName, *varValue);
offset += BOOL_VAR_OFFSET;
}
break;
} else
offset += 1;
offsetDif += 1;
continue;
}
std::string varName = playerVar->GetName();
PlayerVarType varType = playerVar->GetType();
switch (playerVar->GetType()) {
case PlayerVarType::String: {
if (vTableName != "StringPlayerVariable")
return;
StringPlayerVariable* stringPlayerVar = reinterpret_cast<StringPlayerVariable*>(playerVarsGetter() + offset);
playerVar.reset(stringPlayerVar);
playerVar->SetName(varName);
playerVar->SetType(varType);
// TO IMPLEMENT
offset += STRING_SIZE_OFFSET;
return;
}
case PlayerVarType::Float: {
if (vTableName != "FloatPlayerVariable")
return;
FloatPlayerVariable* floatPlayerVar = reinterpret_cast<FloatPlayerVariable*>(playerVarsGetter() + offset);
playerVar.reset(floatPlayerVar);
playerVar->SetName(varName);
playerVar->SetType(varType);
updateDefaultVar(PlayerVariables::defaultPlayerVars, varName, floatPlayerVar->value.data, floatPlayerVar->defaultValue.data);
updateDefaultVar(PlayerVariables::customDefaultPlayerVars, varName, floatPlayerVar->value.data, floatPlayerVar->defaultValue.data);
offset += FLOAT_SIZE_OFFSET;
return;
}
case PlayerVarType::Bool: {
if (vTableName != "BoolPlayerVariable")
return;
BoolPlayerVariable* boolPlayerVar = reinterpret_cast<BoolPlayerVariable*>(playerVarsGetter() + offset);
playerVar.reset(boolPlayerVar);
playerVar->SetName(varName);
playerVar->SetType(varType);
updateDefaultVar(PlayerVariables::defaultPlayerVars, varName, boolPlayerVar->value.data, boolPlayerVar->defaultValue.data);
updateDefaultVar(PlayerVariables::customDefaultPlayerVars, varName, boolPlayerVar->value.data, boolPlayerVar->defaultValue.data);
offset += BOOL_SIZE_OFFSET;
return;
}
default:
offset += 1;
return;
}
}
}
@ -74,7 +176,7 @@ namespace EGSDK::GamePH {
__try {
processPlayerVar(reinterpret_cast<DWORD64*(*)()>(&Get), var);
} __except (EXCEPTION_EXECUTE_HANDLER) {
SPDLOG_ERROR("Failed to process player variable: {}", var.first);
SPDLOG_ERROR("Failed to process player variable: {}", var->GetName());
}
}
@ -83,12 +185,13 @@ namespace EGSDK::GamePH {
#pragma region Player Variables Sorting
struct VarTypeFieldMeta {
PlayerVariables::PlayerVarType type;
PlayerVarType type;
std::string_view className;
};
const std::vector<VarTypeFieldMeta> varTypeFields = {
{ PlayerVariables::PlayerVarType::Float, "constds::FieldsCollection<PlayerVariables>::TypedFieldMeta<FloatPlayerVariable>" },
{ PlayerVariables::PlayerVarType::Bool, "constds::FieldsCollection<PlayerVariables>::TypedFieldMeta<BoolPlayerVariable>" }
{ PlayerVarType::String, "constds::FieldsCollection<PlayerVariables>::TypedFieldMeta<StringPlayerVariable>" },
{ PlayerVarType::Float, "constds::FieldsCollection<PlayerVariables>::TypedFieldMeta<FloatPlayerVariable>" },
{ PlayerVarType::Bool, "constds::FieldsCollection<PlayerVariables>::TypedFieldMeta<BoolPlayerVariable>" }
};
static bool isRetInstruction(BYTE* address) {
@ -130,8 +233,8 @@ namespace EGSDK::GamePH {
return playerVarName;
}
static PlayerVariables::PlayerVarType getPlayerVarType(BYTE*& funcAddress, DWORD64 startOfFunc) {
PlayerVariables::PlayerVarType playerVarType = PlayerVariables::PlayerVarType::NONE;
static PlayerVarType getPlayerVarType(BYTE*& funcAddress, DWORD64 startOfFunc) {
PlayerVarType playerVarType = PlayerVarType::NONE;
while (!playerVarType && !isRetInstruction(funcAddress) && isBelowFuncSizeLimit(funcAddress, startOfFunc, MAX_FUNC_SIZE)) {
// call LoadPlayerXVariable
@ -141,33 +244,33 @@ namespace EGSDK::GamePH {
}
DWORD64 startOfLoadVarFunc = Utils::Memory::CalcTargetAddrOfRelativeInstr(reinterpret_cast<DWORD64>(funcAddress), 1);
for (const auto& varType : varTypeFields) {
BYTE* loadVarFuncAddress = reinterpret_cast<BYTE*>(startOfLoadVarFunc);
DWORD64 metaVTAddrFromFunc = 0;
BYTE* loadVarFuncAddress = reinterpret_cast<BYTE*>(startOfLoadVarFunc);
DWORD64 metaVTAddrFromFunc = 0;
while (!metaVTAddrFromFunc && !isRetInstruction(loadVarFuncAddress) && isBelowFuncSizeLimit(loadVarFuncAddress, startOfLoadVarFunc, MAX_LOAD_VAR_FUNC_SIZE)) {
// lea rax, typedFieldMetaVT
if (!isLeaInstruction(loadVarFuncAddress, 0x48, 0x05)) {
loadVarFuncAddress++;
continue;
}
metaVTAddrFromFunc = Utils::Memory::CalcTargetAddrOfRelativeInstr(reinterpret_cast<DWORD64>(loadVarFuncAddress), 3);
if (Utils::RTTI::GetVTableNameFromVTPtr(reinterpret_cast<DWORD64*>(metaVTAddrFromFunc)) != varType.className) {
metaVTAddrFromFunc = 0;
loadVarFuncAddress++;
continue;
}
while (!metaVTAddrFromFunc && !isRetInstruction(loadVarFuncAddress) && isBelowFuncSizeLimit(loadVarFuncAddress, startOfLoadVarFunc, MAX_LOAD_VAR_FUNC_SIZE)) {
// lea rax, typedFieldMetaVT
if (!isLeaInstruction(loadVarFuncAddress, 0x48, 0x05)) {
loadVarFuncAddress++;
continue;
}
if (Utils::RTTI::GetVTableNameFromVTPtr(reinterpret_cast<DWORD64*>(metaVTAddrFromFunc)) == varType.className) {
playerVarType = varType.type;
break;
metaVTAddrFromFunc = Utils::Memory::CalcTargetAddrOfRelativeInstr(reinterpret_cast<DWORD64>(loadVarFuncAddress), 3);
std::string vTableName = Utils::RTTI::GetVTableNameFromVTPtr(reinterpret_cast<DWORD64*>(metaVTAddrFromFunc));
auto varTypeIt = std::find_if(varTypeFields.begin(), varTypeFields.end(), [&vTableName](const auto& varType) {
return varType.className == vTableName;
});
if (varTypeIt == varTypeFields.end()) {
metaVTAddrFromFunc = 0;
loadVarFuncAddress++;
continue;
}
playerVarType = varTypeIt->type;
break;
}
// if it's still NONE after seeing the function doesnt reference any of the variables, break so the loop stops
if (playerVarType == PlayerVariables::PlayerVarType::NONE)
if (playerVarType == PlayerVarType::NONE)
break;
}
@ -186,24 +289,20 @@ namespace EGSDK::GamePH {
continue;
PlayerVarType playerVarType = getPlayerVarType(funcAddress, startOfFunc);
if (!playerVarType)
continue;
std::string varType{};
switch (playerVarType) {
case PlayerVarType::String:
playerVars.emplace_back(std::make_unique<StringPlayerVariable>(playerVarName));
break;
case PlayerVarType::Float:
varType = "float";
playerVars.emplace_back(std::make_unique<FloatPlayerVariable>(playerVarName));
break;
case PlayerVarType::Bool:
varType = "bool";
playerVars.emplace_back(std::make_unique<BoolPlayerVariable>(playerVarName));
break;
default:
//playerVars.emplace_back(std::make_unique<PlayerVariable>(playerVarName));
break;
}
PlayerVariables::playerVars.emplace_back(playerVarName, std::make_pair(nullptr, varType));
PlayerVariables::playerVarsDefault.emplace_back(playerVarName, std::make_pair(varType == "float" ? 0.0f : false, varType));
PlayerVariables::playerCustomVarsDefault.emplace_back(playerVarName, std::make_pair(varType == "float" ? 0.0f : false, varType));
}
sortedPlayerVars = true;
@ -211,12 +310,9 @@ namespace EGSDK::GamePH {
}
#pragma endregion
std::unordered_map<std::string, std::any> PlayerVariables::prevPlayerVarValueMap{};
std::unordered_map<std::string, bool> PlayerVariables::prevBoolValueMap{};
static PlayerVariables* GetOffset_PlayerVariables() {
PlayerState* pPlayerState = PlayerState::Get();
return pPlayerState ? pPlayerState->pPlayerVariables : nullptr;
PlayerState* playerState = PlayerState::Get();
return playerState ? playerState->playerVariables : nullptr;
}
PlayerVariables* PlayerVariables::Get() {
return ClassHelpers::SafeGetter<PlayerVariables>(GetOffset_PlayerVariables, false, false);

View File

@ -115,22 +115,34 @@ namespace EGT::Menu {
if (!playerVariables.GetValue())
return;
auto bgn = EGSDK::GamePH::PlayerVariables::playerVars.begin();
for (auto it = bgn; it != EGSDK::GamePH::PlayerVariables::playerVars.end(); ++it) {
if (!it->second.first)
continue;
auto playerVarBeginIt = EGSDK::GamePH::PlayerVariables::playerVars.begin();
for (auto& playerVarIt = playerVarBeginIt; playerVarIt != EGSDK::GamePH::PlayerVariables::playerVars.end(); ++playerVarIt) {
auto playerVar = playerVarIt->get();
try {
auto& valDef = EGSDK::GamePH::PlayerVariables::playerVarsDefault.at(it - bgn);
auto defPlayerVar = &EGSDK::GamePH::PlayerVariables::defaultPlayerVars.at(playerVarIt - playerVarBeginIt);
if (it->second.second == "float") {
float* varAddr = reinterpret_cast<float*>(it->second.first);
if (!EGSDK::Utils::Values::are_samef(*varAddr, *(varAddr + 1)) && !EGSDK::Utils::Values::are_samef(*(varAddr + 1), std::any_cast<float>(valDef.second.first)))
*varAddr = *(varAddr + 1);
} else if (it->second.second == "bool") {
bool* varAddr = reinterpret_cast<bool*>(it->second.first);
if (*varAddr != *(varAddr + 1) && *(varAddr + 1) != std::any_cast<bool>(valDef.second.first))
*varAddr = *(varAddr + 1);
switch (playerVar->GetType()) {
case EGSDK::GamePH::PlayerVarType::String:
// TO IMPLEMENT
break;
case EGSDK::GamePH::PlayerVarType::Float: {
EGSDK::GamePH::FloatPlayerVariable* floatPlayerVar = reinterpret_cast<EGSDK::GamePH::FloatPlayerVariable*>(playerVar);
EGSDK::GamePH::FloatPlayerVariable* defFloatPlayerVar = reinterpret_cast<EGSDK::GamePH::FloatPlayerVariable*>(defPlayerVar->get());
if (!EGSDK::Utils::Values::are_samef(floatPlayerVar->value, floatPlayerVar->defaultValue) && !EGSDK::Utils::Values::are_samef(floatPlayerVar->defaultValue, defFloatPlayerVar->value))
floatPlayerVar->value = floatPlayerVar->defaultValue;
break;
}
case EGSDK::GamePH::PlayerVarType::Bool: {
EGSDK::GamePH::BoolPlayerVariable* boolPlayerVar = reinterpret_cast<EGSDK::GamePH::BoolPlayerVariable*>(playerVar);
EGSDK::GamePH::BoolPlayerVariable* defBoolPlayerVar = reinterpret_cast<EGSDK::GamePH::BoolPlayerVariable*>(defPlayerVar->get());
if (!EGSDK::Utils::Values::are_samef(boolPlayerVar->value, boolPlayerVar->defaultValue) && !EGSDK::Utils::Values::are_samef(boolPlayerVar->defaultValue, defBoolPlayerVar->value))
boolPlayerVar->value = boolPlayerVar->defaultValue;
break;
}
default:
break;
}
} catch (std::exception& e) {
UNREFERENCED_PARAMETER(e);
@ -208,23 +220,33 @@ namespace EGT::Menu {
std::string line{};
while (std::getline(iss, line)) {
const std::string origLine = line;
const std::string paramName = getParamName(line);
if (paramName.empty())
const std::string name = getParamName(line);
if (name.empty())
continue;
auto it = std::find_if(EGSDK::GamePH::PlayerVariables::playerVars.begin(), EGSDK::GamePH::PlayerVariables::playerVars.end(), [&paramName](const auto& pair) {
return pair.first == paramName;
auto playerVarIt = std::find_if(EGSDK::GamePH::PlayerVariables::playerVars.begin(), EGSDK::GamePH::PlayerVariables::playerVars.end(), [&name](const auto& playerVar) {
return playerVar->GetName() == name;
});
if (it == EGSDK::GamePH::PlayerVariables::playerVars.end())
if (playerVarIt == EGSDK::GamePH::PlayerVariables::playerVars.end())
continue;
if (it->second.second == "float") {
float value = *reinterpret_cast<float*>(it->second.first);
replaceParamValue(line, std::to_string(value));
} else {
bool value = *reinterpret_cast<bool*>(it->second.first);
replaceParamValue(line, value ? "true" : "false");
auto playerVar = playerVarIt->get();
switch (playerVar->GetType()) {
case EGSDK::GamePH::PlayerVarType::String:
// TO IMPLEMENT
break;
case EGSDK::GamePH::PlayerVarType::Float: {
EGSDK::GamePH::FloatPlayerVariable* floatPlayerVar = reinterpret_cast<EGSDK::GamePH::FloatPlayerVariable*>(playerVar);
replaceParamValue(line, std::to_string(floatPlayerVar->value));
break;
}
case EGSDK::GamePH::PlayerVarType::Bool: {
EGSDK::GamePH::BoolPlayerVariable* boolPlayerVar = reinterpret_cast<EGSDK::GamePH::BoolPlayerVariable*>(playerVar);
replaceParamValue(line, boolPlayerVar ? "true" : "false");
break;
}
default:
break;
}
EGSDK::Utils::Values::str_replace(tempPlayerVarsSCR, origLine, line);
}
@ -251,14 +273,14 @@ namespace EGT::Menu {
std::string line{};
while (std::getline(file, line)) {
const std::string paramName = getParamName(line);
if (paramName.empty())
std::string name = getParamName(line);
if (name.empty())
continue;
const std::string paramValue = getParamValue(line);
if (paramValue.empty())
std::string value = getParamValue(line);
if (value.empty())
continue;
EGSDK::GamePH::PlayerVariables::ChangePlayerVar(paramName, paramValue);
EGSDK::GamePH::PlayerVariables::ChangePlayerVar(name, value);
}
file.close();
@ -266,82 +288,117 @@ namespace EGT::Menu {
}
static void RestoreVariablesToDefault() {
for (auto const& [key, val] : EGSDK::GamePH::PlayerVariables::playerVars) {
if (!val.first)
continue;
auto& defVars = restoreVarsToSavedVarsEnabled ? EGSDK::GamePH::PlayerVariables::playerCustomVarsDefault : EGSDK::GamePH::PlayerVariables::playerVarsDefault;
auto itDef = std::find_if(defVars.begin(), defVars.end(), [&key](const auto& pair) {
return pair.first == key;
for (auto const& playerVar : EGSDK::GamePH::PlayerVariables::playerVars) {
auto& defVars = restoreVarsToSavedVarsEnabled ? EGSDK::GamePH::PlayerVariables::customDefaultPlayerVars : EGSDK::GamePH::PlayerVariables::defaultPlayerVars;
auto defPlayerVarIt = std::find_if(defVars.begin(), defVars.end(), [&playerVar](const auto& defPlayerVar) {
return defPlayerVar->GetName() == playerVar->GetName();
});
if (itDef == defVars.end())
if (defPlayerVarIt == defVars.end())
continue;
if (val.second == "float")
EGSDK::GamePH::PlayerVariables::ChangePlayerVar(key, std::any_cast<float>(itDef->second.first));
else
EGSDK::GamePH::PlayerVariables::ChangePlayerVar(key, std::any_cast<bool>(itDef->second.first));
switch (playerVar->GetType()) {
case EGSDK::GamePH::PlayerVarType::String:
// TO IMPLEMENT
break;
case EGSDK::GamePH::PlayerVarType::Float: {
EGSDK::GamePH::FloatPlayerVariable* defFloatPlayerVar = reinterpret_cast<EGSDK::GamePH::FloatPlayerVariable*>(defPlayerVarIt->get());
EGSDK::GamePH::PlayerVariables::ChangePlayerVar(playerVar->GetName(), defFloatPlayerVar->value.data);
break;
}
case EGSDK::GamePH::PlayerVarType::Bool: {
EGSDK::GamePH::BoolPlayerVariable* defBoolPlayerVar = reinterpret_cast<EGSDK::GamePH::BoolPlayerVariable*>(defPlayerVarIt->get());
EGSDK::GamePH::PlayerVariables::ChangePlayerVar(playerVar->GetName(), defBoolPlayerVar->value.data);
break;
}
default:
break;
}
}
ImGui::OpenPopup("Restored player variables!");
}
static void SaveVariablesAsDefault() {
for (auto const& [key, val] : EGSDK::GamePH::PlayerVariables::playerVars) {
if (!val.first)
continue;
auto itCustomDef = std::find_if(EGSDK::GamePH::PlayerVariables::playerCustomVarsDefault.begin(), EGSDK::GamePH::PlayerVariables::playerCustomVarsDefault.end(), [&key](const auto& pair) {
return pair.first == key;
for (auto const& playerVar : EGSDK::GamePH::PlayerVariables::playerVars) {
auto defCustomPlayerVarIt = std::find_if(EGSDK::GamePH::PlayerVariables::customDefaultPlayerVars.begin(), EGSDK::GamePH::PlayerVariables::customDefaultPlayerVars.end(), [&playerVar](const auto& defCustomPlayerVar) {
return defCustomPlayerVar->GetName() == playerVar->GetName();
});
if (itCustomDef == EGSDK::GamePH::PlayerVariables::playerCustomVarsDefault.end())
if (defCustomPlayerVarIt == EGSDK::GamePH::PlayerVariables::customDefaultPlayerVars.end())
continue;
if (val.second == "float") {
float* const varAddr = reinterpret_cast<float*>(val.first);
itCustomDef->second.first = *varAddr;
} else if (val.second == "bool") {
bool* const varAddr = reinterpret_cast<bool*>(val.first);
itCustomDef->second.first = *varAddr;
switch (playerVar->GetType()) {
case EGSDK::GamePH::PlayerVarType::String:
// TO IMPLEMENT
break;
case EGSDK::GamePH::PlayerVarType::Float: {
EGSDK::GamePH::FloatPlayerVariable* floatPlayerVar = reinterpret_cast<EGSDK::GamePH::FloatPlayerVariable*>(playerVar.get());
EGSDK::GamePH::FloatPlayerVariable* defCustomFloatPlayerVar = reinterpret_cast<EGSDK::GamePH::FloatPlayerVariable*>(defCustomPlayerVarIt->get());
defCustomFloatPlayerVar->value = floatPlayerVar->value;
break;
}
case EGSDK::GamePH::PlayerVarType::Bool: {
EGSDK::GamePH::BoolPlayerVariable* boolPlayerVar = reinterpret_cast<EGSDK::GamePH::BoolPlayerVariable*>(playerVar.get());
EGSDK::GamePH::BoolPlayerVariable* defCustomBoolPlayerVar = reinterpret_cast<EGSDK::GamePH::BoolPlayerVariable*>(defCustomPlayerVarIt->get());
defCustomBoolPlayerVar->value = boolPlayerVar->value;
break;
}
default:
break;
}
}
ImGui::OpenPopup("Saved current player variables!");
}
static void RestoreVariableToDefault(const std::string& varName) {
auto& defVars = restoreVarsToSavedVarsEnabled ? EGSDK::GamePH::PlayerVariables::playerCustomVarsDefault : EGSDK::GamePH::PlayerVariables::playerVarsDefault;
auto itDef = std::find_if(defVars.begin(), defVars.end(), [&varName](const auto& pair) {
return pair.first == varName;
static void RestoreVariableToDefault(const std::string& name) {
auto& defVars = restoreVarsToSavedVarsEnabled ? EGSDK::GamePH::PlayerVariables::customDefaultPlayerVars : EGSDK::GamePH::PlayerVariables::defaultPlayerVars;
auto defPlayerVarIt = std::find_if(defVars.begin(), defVars.end(), [&name](const auto& defPlayerVar) {
return defPlayerVar->GetName() == name;
});
if (itDef == defVars.end())
if (defPlayerVarIt == defVars.end())
return;
if (itDef->second.second == "float")
EGSDK::GamePH::PlayerVariables::ChangePlayerVar(varName, std::any_cast<float>(itDef->second.first));
else
EGSDK::GamePH::PlayerVariables::ChangePlayerVar(varName, std::any_cast<bool>(itDef->second.first));
switch (defPlayerVarIt->get()->GetType()) {
case EGSDK::GamePH::PlayerVarType::String:
// TO IMPLEMENT
break;
case EGSDK::GamePH::PlayerVarType::Float: {
EGSDK::GamePH::FloatPlayerVariable* defFloatPlayerVar = reinterpret_cast<EGSDK::GamePH::FloatPlayerVariable*>(defPlayerVarIt->get());
EGSDK::GamePH::PlayerVariables::ChangePlayerVar(name, defFloatPlayerVar->value.data);
break;
}
case EGSDK::GamePH::PlayerVarType::Bool: {
EGSDK::GamePH::BoolPlayerVariable* defBoolPlayerVar = reinterpret_cast<EGSDK::GamePH::BoolPlayerVariable*>(defPlayerVarIt->get());
EGSDK::GamePH::PlayerVariables::ChangePlayerVar(name, defBoolPlayerVar->value.data);
break;
}
default:
break;
}
}
static bool shouldDisplayVariable(const std::string& key, const std::string& searchFilter) {
if (searchFilter.empty()) return true;
static bool shouldDisplayVariable(const std::unique_ptr<EGSDK::GamePH::PlayerVariable>& playerVar, const std::string& searchFilter) {
if (playerVar->GetType() == EGSDK::GamePH::PlayerVarType::String || playerVar->GetType() == EGSDK::GamePH::PlayerVarType::NONE) // TO IMPLEMENT
return false;
if (searchFilter.empty())
return true;
// Convert searchFilter to lowercase
std::string lowerFilter = searchFilter;
std::transform(lowerFilter.begin(), lowerFilter.end(), lowerFilter.begin(), ::tolower);
// Convert key to lowercase and check if it contains the filter
std::string lowerKey = key;
// Convert variable name to lowercase and check if it contains the filter
std::string lowerKey = playerVar->GetName();
std::transform(lowerKey.begin(), lowerKey.end(), lowerKey.begin(), ::tolower);
return lowerKey.find(lowerFilter) != std::string::npos;
}
static void renderDebugInfo(const std::string& key, const std::pair<void*, std::string>& val) {
static void renderDebugInfo(const std::unique_ptr<EGSDK::GamePH::PlayerVariable>& playerVar) {
const float maxInputTextWidth = ImGui::CalcTextSize("0x0000000000000000").x;
static std::string labelID{};
labelID = "##DebugAddrInputText" + std::string(key);
DWORD64 finalAddr = val.second == "float" ? reinterpret_cast<DWORD64>(reinterpret_cast<float*>(val.first)) : reinterpret_cast<DWORD64>(reinterpret_cast<bool*>(val.first));
labelID = "##DebugAddrInputText" + std::string(playerVar->GetName());
DWORD64 finalVarValueAddr = reinterpret_cast<DWORD64>(playerVar.get()) + 0x8;
std::stringstream ss;
if (finalAddr)
ss << "0x" << std::uppercase << std::hex << finalAddr;
if (finalVarValueAddr)
ss << "0x" << std::uppercase << std::hex << finalVarValueAddr;
else
ss << "NULL";
@ -352,38 +409,45 @@ namespace EGT::Menu {
//ImGui::SetCursorPosY(ImGui::GetCursorPosY() - ((ImGui::GetFrameHeight() - ImGui::GetTextLineHeight()) / 2.0f));
ImGui::SetNextItemWidth(maxInputTextWidth);
ImGui::PushStyleColor(ImGuiCol_Text, finalAddr ? IM_COL32(0, 255, 0, 255) : IM_COL32(255, 0, 0, 255));
ImGui::PushStyleColor(ImGuiCol_Text, finalVarValueAddr ? IM_COL32(0, 255, 0, 255) : IM_COL32(255, 0, 0, 255));
ImGui::InputText(labelID.c_str(), const_cast<char*>(addrString.c_str()), strlen(addrString.c_str()), ImGuiInputTextFlags_ReadOnly);
ImGui::PopStyleColor();
}
static void renderPlayerVariable(const std::string& key, const std::pair<void*, std::string>& val) {
float* floatVarAddr = nullptr;
bool* boolVarAddr = nullptr;
if (val.second == "float") {
floatVarAddr = reinterpret_cast<float*>(val.first);
float newValue = *floatVarAddr;
if (ImGui::InputFloat(key.c_str(), &newValue)) {
*floatVarAddr = newValue;
*(floatVarAddr + 1) = newValue;
static void renderPlayerVariable(const std::unique_ptr<EGSDK::GamePH::PlayerVariable>& playerVar) {
switch (playerVar->GetType()) {
case EGSDK::GamePH::PlayerVarType::String:
// TO IMPLEMENT
break;
case EGSDK::GamePH::PlayerVarType::Float: {
EGSDK::GamePH::FloatPlayerVariable* floatPlayerVar = reinterpret_cast<EGSDK::GamePH::FloatPlayerVariable*>(playerVar.get());
float newValue = floatPlayerVar->value;
if (ImGui::InputFloat(floatPlayerVar->GetName(), &newValue)) {
floatPlayerVar->value = newValue;
floatPlayerVar->defaultValue = newValue;
}
} else if (val.second == "bool") {
boolVarAddr = reinterpret_cast<bool*>(val.first);
bool newValue = *boolVarAddr;
if (ImGui::Checkbox(key.c_str(), &newValue)) {
*boolVarAddr = newValue;
*(boolVarAddr + 1) = newValue;
break;
}
case EGSDK::GamePH::PlayerVarType::Bool: {
EGSDK::GamePH::BoolPlayerVariable* boolPlayerVar = reinterpret_cast<EGSDK::GamePH::BoolPlayerVariable*>(playerVar.get());
bool newValue = boolPlayerVar->value;
if (ImGui::Checkbox(boolPlayerVar->GetName(), &newValue)) {
boolPlayerVar->value = newValue;
boolPlayerVar->defaultValue = newValue;
}
break;
}
default:
break;
}
ImGui::SameLine();
static std::string restoreBtnName{};
restoreBtnName = "Restore##" + key;
restoreBtnName = "Restore##" + std::string(playerVar->GetName());
if (ImGui::Button(restoreBtnName.c_str(), "Restores player variable to default"))
RestoreVariableToDefault(key);
RestoreVariableToDefault(playerVar->GetName());
if (debugEnabled)
renderDebugInfo(key, val);
renderDebugInfo(playerVar);
}
static void HandlePlayerVariablesList() {
if (!playerVariables.GetValue())
@ -411,11 +475,11 @@ namespace EGT::Menu {
ImGui::Separator();
ImGui::InputTextWithHint("##VarsSearch", "Search variables", playerVarsSearchFilter, 64);
for (auto const& [key, val] : EGSDK::GamePH::PlayerVariables::playerVars) {
if (!val.first || !shouldDisplayVariable(key, playerVarsSearchFilter))
for (auto const& playerVar : EGSDK::GamePH::PlayerVariables::playerVars) {
if (!shouldDisplayVariable(playerVar, playerVarsSearchFilter))
continue;
renderPlayerVariable(key, val);
renderPlayerVariable(playerVar);
}
ImGui::Unindent();

View File

@ -107,7 +107,7 @@ namespace EGT::Menu {
static bool haveResetAntizinDrainBlocked = true;
if (ImGui::SliderFloat("Time", &time, 0.01f, 24.0f, "%.2f", ImGuiSliderFlags_AlwaysClamp)) {
if (haveResetAntizinDrainBlocked)
previousAntizinDrainBlocked = EGSDK::GamePH::PlayerVariables::GetPlayerVar<bool>("AntizinDrainBlocked");
previousAntizinDrainBlocked = EGSDK::GamePH::PlayerVariables::GetPlayerVarValue<bool>("AntizinDrainBlocked");
EGSDK::GamePH::PlayerVariables::ChangePlayerVar("AntizinDrainBlocked", true);
haveResetAntizinDrainBlocked = false;