mirror of
https://github.com/YimMenu/YimMenuV2.git
synced 2026-10-10 03:43:15 +08:00
fix (StatEditor)Fixed the issue with writing 64-bit statistics in the Statistics Editor (#1069)
* 1.Fix int64/uint64 data overflow when writing。 2.Add packed int/bool stats for all writes and reads。 * Add modifications to the data types of date, userid, and pos * Safe Conversion * update "StatEditor.cpp" * fix userid Write error * Add some header files needed by the compiler
This commit is contained in:
@@ -1,20 +1,23 @@
|
||||
#include "StatEditor.hpp"
|
||||
#include "core/backend/FiberPool.hpp"
|
||||
#include "core/backend/FiberPool.hpp"
|
||||
#include "core/frontend/widgets/imgui_bitfield.hpp"
|
||||
#include "game/backend/AnticheatBypass.hpp"
|
||||
#include "game/pointers/Pointers.hpp"
|
||||
#include "game/gta/Natives.hpp"
|
||||
#include "game/gta/Stats.hpp"
|
||||
#include "game/pointers/Pointers.hpp"
|
||||
#include "StatEditor.hpp"
|
||||
#include "types/stats/CStatsMgr.hpp"
|
||||
#include <charconv>
|
||||
|
||||
namespace YimMenu::Submenus
|
||||
{
|
||||
struct StatInfo
|
||||
{
|
||||
std::string m_Name;
|
||||
std::uint32_t m_NameHash;
|
||||
std::uint32_t m_NameHash = 0;
|
||||
bool m_Normalized = false;
|
||||
sStatData* m_Data = nullptr;
|
||||
|
||||
bool IsValid()
|
||||
bool IsValid() const
|
||||
{
|
||||
return m_Data != nullptr;
|
||||
}
|
||||
@@ -26,18 +29,19 @@ namespace YimMenu::Submenus
|
||||
bool m_IsBoolStat;
|
||||
bool m_IsValid;
|
||||
|
||||
bool IsValid()
|
||||
bool IsValid() const
|
||||
{
|
||||
return m_IsValid;
|
||||
}
|
||||
};
|
||||
|
||||
union StatValue {
|
||||
float m_AsFloat;
|
||||
float m_AsFloat[3];
|
||||
int m_AsInt;
|
||||
bool m_AsBool;
|
||||
std::uint64_t m_AsU64;
|
||||
char m_AsString[12];
|
||||
char m_AsString[21];
|
||||
Date m_Date;
|
||||
};
|
||||
|
||||
// https://stackoverflow.com/questions/66897068/can-trim-of-a-string-be-done-inplace-with-c20-ranges
|
||||
@@ -106,7 +110,7 @@ namespace YimMenu::Submenus
|
||||
return name;
|
||||
}
|
||||
|
||||
static void ReadStat(StatValue& value, sStatData* data)
|
||||
static void ReadStat(std::uint32_t hash,StatValue& value, sStatData* data)
|
||||
{
|
||||
memset(&value, 0, sizeof(StatValue));
|
||||
|
||||
@@ -116,7 +120,7 @@ namespace YimMenu::Submenus
|
||||
value.m_AsBool = data->GetBool();
|
||||
return;
|
||||
case sStatData::Type::FLOAT:
|
||||
value.m_AsFloat = data->GetFloat();
|
||||
value.m_AsFloat[0] = data->GetFloat();
|
||||
return;
|
||||
case sStatData::Type::INT:
|
||||
case sStatData::Type::UINT32:
|
||||
@@ -128,11 +132,27 @@ namespace YimMenu::Submenus
|
||||
value.m_AsU64 = data->GetInt64();
|
||||
return;
|
||||
case sStatData::Type::UINT64:
|
||||
case sStatData::Type::PACKED:
|
||||
value.m_AsU64 = data->GetUInt64();
|
||||
return;
|
||||
case sStatData::Type::STRING:
|
||||
strncpy(value.m_AsString, data->GetString(), sizeof(value.m_AsString));
|
||||
return;
|
||||
case sStatData::Type::POS:
|
||||
STATS::STAT_GET_POS(hash, &value.m_AsFloat[0], &value.m_AsFloat[1], &value.m_AsFloat[2], true);
|
||||
return;
|
||||
case sStatData::Type::DATE:
|
||||
STATS::STAT_GET_DATE(hash, &value.m_Date, sizeof(Date) / 8, true);
|
||||
return;
|
||||
case sStatData::Type::USERID:
|
||||
{
|
||||
char user_id[21]{};
|
||||
data->GetUserID(user_id, sizeof(user_id));
|
||||
value.m_AsU64 = std::strtoull(user_id, nullptr, 10);
|
||||
return;
|
||||
}
|
||||
case sStatData::Type::PROFILE_SETTING:
|
||||
case sStatData::Type::TEXTLABEL:
|
||||
default:
|
||||
return; // data type not supported
|
||||
}
|
||||
@@ -146,7 +166,7 @@ namespace YimMenu::Submenus
|
||||
STATS::STAT_SET_BOOL(hash, value.m_AsBool, true);
|
||||
return;
|
||||
case sStatData::Type::FLOAT:
|
||||
STATS::STAT_SET_FLOAT(hash, value.m_AsFloat, true);
|
||||
STATS::STAT_SET_FLOAT(hash, value.m_AsFloat[0], true);
|
||||
return;
|
||||
case sStatData::Type::INT:
|
||||
case sStatData::Type::UINT32:
|
||||
@@ -154,21 +174,61 @@ namespace YimMenu::Submenus
|
||||
case sStatData::Type::UINT8:
|
||||
STATS::STAT_SET_INT(hash, value.m_AsInt, true);
|
||||
return;
|
||||
case sStatData::Type::INT64:
|
||||
data->SetInt64(value.m_AsU64); // TODO this isn't a good idea! natives can't set this
|
||||
case sStatData::Type::INT64:
|
||||
data->SetInt64(value.m_AsU64 - 1);
|
||||
STATS::STAT_INCREMENT(hash, static_cast<float>(1));
|
||||
return;
|
||||
case sStatData::Type::UINT64:
|
||||
STATS::STAT_SET_MASKED_INT(hash, (std::uint32_t)value.m_AsU64, 0, 32, true);
|
||||
STATS::STAT_SET_MASKED_INT(hash, (std::uint32_t)(value.m_AsU64 >> 32), 32, 32, true);
|
||||
//Stats::SetMaskedAll(hash, value.m_AsU64);
|
||||
//This code is simpler
|
||||
//After writing, restarting will restore the data
|
||||
data->SetUInt64(value.m_AsU64 - 1);
|
||||
//You need to use STATS::STAT_INCREMENT to save the data on the server.
|
||||
STATS::STAT_INCREMENT(hash, static_cast<float>(1));
|
||||
return;
|
||||
case sStatData::Type::STRING:
|
||||
STATS::STAT_SET_STRING(hash, value.m_AsString, true);
|
||||
return;
|
||||
case sStatData::Type::USERID:
|
||||
{
|
||||
std::string user_id = std::to_string(value.m_AsU64);
|
||||
STATS::STAT_SET_USER_ID(hash, user_id.c_str(), true);
|
||||
//data->SetUserID(value.m_AsString);
|
||||
return;
|
||||
}
|
||||
case sStatData::Type::PACKED:
|
||||
/*data->SetUInt64(value.m_AsU64 - 1);
|
||||
Packed data can't be written using STATS::STAT_INCREMENT
|
||||
STATS::STAT_INCREMENT(hash, static_cast<float>(1));*/
|
||||
Stats::SetMaskedAll(hash, value.m_AsU64);
|
||||
return;
|
||||
case sStatData::Type::POS:
|
||||
STATS::STAT_SET_POS(hash, value.m_AsFloat[0], value.m_AsFloat[1], value.m_AsFloat[2], true);
|
||||
return;
|
||||
case sStatData::Type::DATE:
|
||||
STATS::STAT_SET_DATE(hash, &value.m_Date, sizeof(Date) / 8, true);
|
||||
return;
|
||||
case sStatData::Type::PROFILE_SETTING:
|
||||
case sStatData::Type::TEXTLABEL:
|
||||
default:
|
||||
return; // data type not supported
|
||||
}
|
||||
}
|
||||
|
||||
static bool CheckDate(Date date)
|
||||
{
|
||||
int &year = date.Year, &Month = date.Month, &day = date.Day, &Hour = date.Hour, &Minute = date.Minute, &Second = date.Second, &Mil = date.Millisecond;
|
||||
|
||||
int checkfeb = 30 + ((Month % 2) + (Month >= 8)) % 2;
|
||||
checkfeb = checkfeb - (2 * (Month == 2));
|
||||
checkfeb = checkfeb + ((Month == 2) && ((year % 100) && (year % 4 == 0) || (year % 400 == 0)));
|
||||
|
||||
if (year >= 0 && day >= 0 && day <= checkfeb && Month >= 0 && Month <= 12 && Hour >= 0 && Hour < 24 && Minute >= 0 && Minute < 60 && Second >= 0 && Second < 60 && Mil >= 0 && Mil < 1000)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// TODO: don't call std::string_view::data()
|
||||
static void WriteStatWithStringValue(std::uint32_t hash, std::string_view value, sStatData* data)
|
||||
{
|
||||
@@ -182,7 +242,7 @@ namespace YimMenu::Submenus
|
||||
return tolower(c);
|
||||
});
|
||||
|
||||
if (as_string == "true" || as_string == "1")
|
||||
if (as_string != "false" && as_string != "0")
|
||||
{
|
||||
_bool = true;
|
||||
}
|
||||
@@ -208,20 +268,88 @@ namespace YimMenu::Submenus
|
||||
case sStatData::Type::INT64:
|
||||
{
|
||||
auto int64_ = std::strtoll(value.data(), nullptr, 10);
|
||||
data->SetInt64(int64_); // TODO this isn't a good idea! natives can't set this
|
||||
data->SetInt64(int64_-1);
|
||||
STATS::STAT_INCREMENT(hash, static_cast<float>(1));
|
||||
return;
|
||||
}
|
||||
case sStatData::Type::UINT64:
|
||||
{
|
||||
auto uint64_ = std::strtoull(value.data(), nullptr, 10);
|
||||
|
||||
STATS::STAT_SET_MASKED_INT(hash, (std::uint32_t)uint64_, 0, 32, true);
|
||||
STATS::STAT_SET_MASKED_INT(hash, (std::uint32_t)(uint64_ >> 32), 32, 32, true);
|
||||
data->SetUInt64(uint64_ - 1);
|
||||
STATS::STAT_INCREMENT(hash, static_cast<float>(1));
|
||||
return;
|
||||
}
|
||||
case sStatData::Type::STRING:
|
||||
STATS::STAT_SET_STRING(hash, value.data(), true);
|
||||
return;
|
||||
case sStatData::Type::PACKED:
|
||||
{
|
||||
auto uint64_ = std::strtoull(value.data(), nullptr, 10);
|
||||
Stats::SetMaskedAll(hash, uint64_);
|
||||
return;
|
||||
}
|
||||
case sStatData::Type::USERID:
|
||||
if (value.find_first_not_of("0123456789") == std::string::npos && !value.empty())
|
||||
STATS::STAT_SET_USER_ID(hash, value.data(), true);
|
||||
return;
|
||||
case sStatData::Type::DATE:
|
||||
{
|
||||
std::stringstream ss(value.data());
|
||||
std::string token;
|
||||
std::vector<int> date;
|
||||
date.reserve(7);
|
||||
|
||||
while (std::getline(ss, token, ','))
|
||||
{
|
||||
uint32_t date_token = 0;
|
||||
auto [ptr, ec] = std::from_chars(token.c_str(), token.c_str() + token.size(), date_token);
|
||||
if (ec != std::errc())
|
||||
return;
|
||||
|
||||
date.emplace_back(date_token);
|
||||
}
|
||||
|
||||
if (date.size() == 7)
|
||||
{
|
||||
Date temp{
|
||||
date[0], // Year
|
||||
date[1], // Month
|
||||
date[2], // Day
|
||||
date[3], // Hour
|
||||
date[4], // Minute
|
||||
date[5], // Second
|
||||
date[6] // Millisecond
|
||||
};
|
||||
if (CheckDate(temp))
|
||||
{
|
||||
STATS::STAT_SET_DATE(hash, &temp, sizeof(Date) / 8, true);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
case sStatData::Type::POS:
|
||||
{
|
||||
std::stringstream ss(value.data());
|
||||
std::string token;
|
||||
std::vector<float> pos;
|
||||
pos.reserve(3);
|
||||
|
||||
while (std::getline(ss, token, ','))
|
||||
{
|
||||
float pos_token = 0.0f;
|
||||
auto [ptr, ec] = std::from_chars(token.c_str(), token.c_str() + token.size(), pos_token);
|
||||
if (ec != std::errc())
|
||||
return;
|
||||
|
||||
pos.emplace_back(pos_token);
|
||||
}
|
||||
if (pos.size() == 3)
|
||||
{
|
||||
STATS::STAT_SET_POS(hash, pos[0], pos[1], pos[2], true);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
default:
|
||||
return; // data type not supported
|
||||
}
|
||||
@@ -235,7 +363,7 @@ namespace YimMenu::Submenus
|
||||
case sStatData::Type::_BOOL:
|
||||
return ImGui::Checkbox("Value", &value.m_AsBool);
|
||||
case sStatData::Type::FLOAT:
|
||||
return ImGui::InputFloat("Value", &value.m_AsFloat);
|
||||
return ImGui::InputFloat("Value", &value.m_AsFloat[0]);
|
||||
case sStatData::Type::INT:
|
||||
return ImGui::InputInt("Value", &value.m_AsInt);
|
||||
case sStatData::Type::UINT32:
|
||||
@@ -245,11 +373,52 @@ namespace YimMenu::Submenus
|
||||
case sStatData::Type::UINT8:
|
||||
return ImGui::InputScalar("Value", ImGuiDataType_U8, &value.m_AsInt);
|
||||
case sStatData::Type::INT64:
|
||||
return ImGui::InputScalar("Value", ImGuiDataType_S64, &value.m_AsInt);
|
||||
return ImGui::InputScalar("Value", ImGuiDataType_S64, &value.m_AsU64);
|
||||
case sStatData::Type::UINT64:
|
||||
return ImGui::InputScalar("Value", ImGuiDataType_U64, &value.m_AsInt);
|
||||
case sStatData::Type::USERID:
|
||||
return ImGui::InputScalar("Value", ImGuiDataType_U64, &value.m_AsU64);
|
||||
case sStatData::Type::STRING:
|
||||
return ImGui::InputText("Value", value.m_AsString, sizeof(value.m_AsString));
|
||||
case sStatData::Type::PACKED:
|
||||
return ImGui::Bitfield("Value", &value.m_AsU64);
|
||||
case sStatData::Type::POS:
|
||||
ImGui::PushItemWidth(50.0f);
|
||||
ImGui::InputFloat("X", &value.m_AsFloat[0]);
|
||||
ImGui::SameLine();
|
||||
ImGui::InputFloat("Y", &value.m_AsFloat[1]);
|
||||
ImGui::SameLine();
|
||||
ImGui::InputFloat("Z", &value.m_AsFloat[2]);
|
||||
ImGui::PopItemWidth();
|
||||
return true;
|
||||
case sStatData::Type::DATE:
|
||||
{
|
||||
ImGui::PushItemWidth(60.0f);
|
||||
ImGui::InputScalar("Year", ImGuiDataType_U32, &value.m_Date.Year);
|
||||
ImGui::SameLine();
|
||||
ImGui::PopItemWidth();
|
||||
ImGui::PushItemWidth(50.0f);
|
||||
ImGui::InputScalar("Month", ImGuiDataType_U32, &value.m_Date.Month);
|
||||
ImGui::SameLine();
|
||||
ImGui::InputScalar("Day", ImGuiDataType_U32, &value.m_Date.Day);
|
||||
ImGui::SameLine();
|
||||
ImGui::InputScalar("Hour", ImGuiDataType_U32, &value.m_Date.Hour);
|
||||
ImGui::SameLine();
|
||||
ImGui::InputScalar("Minute", ImGuiDataType_U32, &value.m_Date.Minute);
|
||||
ImGui::SameLine();
|
||||
ImGui::InputScalar("Second", ImGuiDataType_U32, &value.m_Date.Second);
|
||||
ImGui::SameLine();
|
||||
ImGui::InputScalar("Millisecond", ImGuiDataType_U32, &value.m_Date.Millisecond);
|
||||
ImGui::PopItemWidth();
|
||||
if (CheckDate(value.m_Date))
|
||||
return true;
|
||||
else
|
||||
{
|
||||
ImGui::TextColored(ImVec4(0.957f, 0.643f, 0.376f, 1.00f), "The entered date or time is invalid, please recheck the input data.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
case sStatData::Type::PROFILE_SETTING:
|
||||
case sStatData::Type::TEXTLABEL:
|
||||
default:
|
||||
ImGui::BeginDisabled();
|
||||
ImGui::Text("Data type not supported");
|
||||
@@ -332,7 +501,7 @@ namespace YimMenu::Submenus
|
||||
{
|
||||
current_info = GetStatInfo(stat_buf);
|
||||
if (current_info.IsValid())
|
||||
ReadStat(value, current_info.m_Data);
|
||||
ReadStat(current_info.m_NameHash,value, current_info.m_Data);
|
||||
}
|
||||
|
||||
if (!current_info.IsValid())
|
||||
@@ -342,12 +511,13 @@ namespace YimMenu::Submenus
|
||||
ImGui::Text("Normalized name to: %s", current_info.m_Name.data());
|
||||
}
|
||||
|
||||
bool can_edit = !current_info.m_Data->IsControlledByNetshop();
|
||||
bool can_edit = RenderStatEditor(value, current_info.m_Data);
|
||||
|
||||
RenderStatEditor(value, current_info.m_Data);
|
||||
if (can_edit)
|
||||
can_edit = !current_info.m_Data->IsControlledByNetshop();
|
||||
|
||||
if (ImGui::Button("Refresh"))
|
||||
ReadStat(value, current_info.m_Data);
|
||||
ReadStat(current_info.m_NameHash,value, current_info.m_Data);
|
||||
ImGui::SameLine();
|
||||
ImGui::BeginDisabled(!can_edit);
|
||||
if (ImGui::Button("Write"))
|
||||
|
||||
+57
-1
@@ -1,4 +1,4 @@
|
||||
#include "Stats.hpp"
|
||||
#include "Stats.hpp"
|
||||
#include "game/gta/Natives.hpp"
|
||||
|
||||
namespace YimMenu::Stats
|
||||
@@ -132,4 +132,60 @@ namespace YimMenu::Stats
|
||||
STATS::STAT_GET_MASKED_INT(Joaat(statName), &value, bitIndex, bitSize, -1);
|
||||
return value;
|
||||
}
|
||||
|
||||
|
||||
void Stats::SetMaskedAll(Hash hash, uint64_t value)
|
||||
{
|
||||
uint64_t uint64_value = value;
|
||||
int part0 = uint64_value & 0xFFFFu;
|
||||
int part1 = (uint64_value >> 16) & 0xFFFFu;
|
||||
int part2 = (uint64_value >> 32) & 0xFFFFu;
|
||||
int part3 = (uint64_value >> 48) & 0xFFFFu;
|
||||
// The second input parameter is of type int. Using -1 will cause the entire data to overflow
|
||||
//so you have to split the 64-bit data into 4 parts to write it.
|
||||
STATS::STAT_SET_MASKED_INT(hash, part0, 0, 16, true); //bit0-bit15
|
||||
STATS::STAT_SET_MASKED_INT(hash, part1, 16, 16, true); //bit16-bit31
|
||||
STATS::STAT_SET_MASKED_INT(hash, part2, 32, 16, true); //bit32-bit47
|
||||
STATS::STAT_SET_MASKED_INT(hash, part3, 48, 16, true); //bit48-bit63
|
||||
}
|
||||
|
||||
void Stats::SetMaskedAll(std::string statName, uint64_t value)
|
||||
{
|
||||
ConvertMPX(statName);
|
||||
uint64_t uint64_value = value;
|
||||
int part0 = uint64_value & 0xFFFFu;
|
||||
int part1 = (uint64_value >> 16) & 0xFFFFu;
|
||||
int part2 = (uint64_value >> 32) & 0xFFFFu;
|
||||
int part3 = (uint64_value >> 48) & 0xFFFFu;
|
||||
|
||||
auto hash = Joaat(statName);
|
||||
STATS::STAT_SET_MASKED_INT(hash, part0, 0, 16, true); //bit0-bit15
|
||||
STATS::STAT_SET_MASKED_INT(hash, part1, 16, 16, true); //bit16-bit31
|
||||
STATS::STAT_SET_MASKED_INT(hash, part2, 32, 16, true); //bit32-bit47
|
||||
STATS::STAT_SET_MASKED_INT(hash, part3, 48, 16, true); //bit48-bit63
|
||||
}
|
||||
|
||||
uint64_t GetMaskedAll(Hash hash, int playerindex)
|
||||
{
|
||||
int part0 = 0, part1 = 0, part2 = 0, part3 = 0;
|
||||
STATS::STAT_GET_MASKED_INT(hash, &part0, 0, 16, playerindex);
|
||||
STATS::STAT_GET_MASKED_INT(hash, &part1, 16, 16, playerindex);
|
||||
STATS::STAT_GET_MASKED_INT(hash, &part2, 32, 16, playerindex);
|
||||
STATS::STAT_GET_MASKED_INT(hash, &part3, 48, 16, playerindex);
|
||||
uint64_t value = (static_cast<uint64_t>(part3) << 48) | (static_cast<uint64_t>(part2) << 32) | (static_cast<uint64_t>(part1) << 16) | part0;
|
||||
return value;
|
||||
}
|
||||
|
||||
uint64_t GetMaskedAll(std::string statName, int playerindex)
|
||||
{
|
||||
Hash hash = Joaat(statName);
|
||||
int part0 = 0, part1 = 0, part2 = 0, part3 = 0;
|
||||
STATS::STAT_GET_MASKED_INT(hash, &part0, 0, 16, playerindex);
|
||||
STATS::STAT_GET_MASKED_INT(hash, &part1, 16, 16, playerindex);
|
||||
STATS::STAT_GET_MASKED_INT(hash, &part2, 32, 16, playerindex);
|
||||
STATS::STAT_GET_MASKED_INT(hash, &part3, 48, 16, playerindex);
|
||||
uint64_t value = (static_cast<uint64_t>(part3) << 48) | (static_cast<uint64_t>(part2) << 32) | (static_cast<uint64_t>(part1) << 16) | part0;
|
||||
return value;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
#pragma once
|
||||
#pragma once
|
||||
#include "core/util/Joaat.hpp"
|
||||
#include "types/script/types.hpp"
|
||||
|
||||
@@ -7,7 +7,7 @@ namespace YimMenu::Stats
|
||||
extern int GetCharIndex();
|
||||
|
||||
extern void SetInt(std::string statName, int value);
|
||||
extern void SetBool(std::string statName, bool value);
|
||||
extern void SetBool(std::string statName, bool value = true);
|
||||
extern void SetFloat(std::string statName, float value);
|
||||
extern void SetDate(std::string statName, Date* value);
|
||||
extern void SetString(std::string statName, const char* value);
|
||||
@@ -26,4 +26,10 @@ namespace YimMenu::Stats
|
||||
extern void SetMaskedInt(std::string statName, int bitStart, int bitSize, int value);
|
||||
extern bool GetMaskedBool(std::string statName, int bitIndex);
|
||||
extern int GetMaskedInt(std::string statName, int bitIndex, int bitSize);
|
||||
|
||||
//You can directly write/read all packed int/bool and uint64 statistics.
|
||||
extern void SetMaskedAll(std::string statName, uint64_t value);
|
||||
extern void SetMaskedAll(Hash hash, uint64_t value);
|
||||
uint64_t GetMaskedAll(Hash hash, int playerindex=-1);
|
||||
uint64_t GetMaskedAll(std::string statName, int playerindex = -1);
|
||||
}
|
||||
@@ -1,10 +1,11 @@
|
||||
#pragma once
|
||||
#pragma once
|
||||
|
||||
class sStatData
|
||||
{
|
||||
public:
|
||||
enum class Type
|
||||
{
|
||||
NONE=0,
|
||||
INT = 1,
|
||||
FLOAT,
|
||||
STRING,
|
||||
@@ -15,7 +16,11 @@ public:
|
||||
UINT64,
|
||||
DATE = 20,
|
||||
POS,
|
||||
INT64 = 26,
|
||||
TEXTLABEL = 22,
|
||||
PACKED = 23,
|
||||
USERID = 24,
|
||||
PROFILE_SETTING = 25,
|
||||
INT64 = 26
|
||||
};
|
||||
|
||||
// it isn't recommended to call the SetXXX() functions directly, use the natives instead
|
||||
|
||||
Reference in New Issue
Block a user