Гайд Продолжение серии. шема(оффсеты)+локал игрок

Участник
Статус
Оффлайн
Регистрация
23 Май 2019
Сообщения
851
Реакции[?]
335
Поинты[?]
67K
из-за требований в начале кода: "// c++23 - tested on clang 18.1.5 / msvc 19.40.33808"


нет, м2, но на нём много скульных баз в рантайме


нет, дебаг, так как при попытке собрать релиз 560 ошибок;

вот результат: 6 + 127 + 10 + 41 = 184 / 60 = 3 минуты

schema_registration: 5690ms
printing & saving schema dump: 127322ms
rtti_saving: 9678ms
saving info...
info_saving: 40806ms
дебаг билд для дебаггинга существует а не для быстрой работы. собирай релиз. 560 ошибок у тебя наверно потому что ты с++latest не поставил в релизе(там в настройках проекта сверху можно выбрать конфигурацию и платформу(у каждой связки конфигурация-платформа разные настройки. в этом и вся их суть - дебаг это не магический какойто особенный режим это просто "шаблончик" настроек где выключены всякие оптимизации и тд; релиз тоже самое тоже шаблончик с настройками с оптимизациями и тд))(ты только в дебаге поставил я так понимаю)(можно также выбрать опцию All Configurations - она показывает/разрешает менять настройки со всех конфигураций, можно и на дебаг и на релиз сразу поставить c++latest и тд и тп)
1715526826533.png
и "tested on clang 18.1.5 / msvc 19.40.33808" это не требование это версии на которых я тестил(это самые последние версии компиляторов на данный момент); это не минимальные версии где работает
 
Начинающий
Статус
Оффлайн
Регистрация
14 Янв 2024
Сообщения
91
Реакции[?]
7
Поинты[?]
8K
560 ошибок у тебя наверно потому что ты с++latest не поставил в релизе
да, в релизе стоял 14 стандарт;

новый результат (релиз): 1 + 25 + 1 + 2 = 29 секунд, релиз в ~6 раз быстрее

schema_registration: 1109ms
printing & saving schema dump: 25296ms
rtti_saving: 885ms
saving info...
info_saving: 2285ms
 
Начинающий
Статус
Оффлайн
Регистрация
5 Июн 2024
Сообщения
11
Реакции[?]
3
Поинты[?]
2K
Это очень круто, спасибо что делишься опытом! Может есть смысл сделать CI\CD для автодампа ассетов в gitlab\github?
Можно сделать автозапуск по выходу обновления и тригерить пайплайн
Можешь с Steamdb сколабарироваться и сделать чтобы на каждый новый апдейт дамп делался. Можешь даже просто им написать они мб сами сделают.
Вот этот человек
Пожалуйста, авторизуйтесь для просмотра ссылки.
(вроде один из главных там)
очень интересовался еще старым Source2Gen`ом от прейдога

Учитывая что достаточно дллок и запуск игры не нужен - можно даже на старые версии попробовать сделать, только сигнатура будет ломаться.
 
Участник
Статус
Оффлайн
Регистрация
23 Май 2019
Сообщения
851
Реакции[?]
335
Поинты[?]
67K
дамп от Thu Aug 08 2024 22:32:59
Пожалуйста, авторизуйтесь для просмотра ссылки.
добавил более детальный дамп CEntityClass
если кому-то надо структурки:
C++:
struct datamapField
{
    CSchemaClassBinding* enum_binding;
    const char* name;
    std::int16_t offset;
    std::int16_t unk;
    std::int8_t type;
};

struct EntInput_t
{
    const char* name;
    std::uint32_t unk1;
    std::uint32_t pad;
    union unk
    {
#pragma pack(push, 1)
        struct netvar_info_
        {
            std::uint8_t unk2;
            std::uint16_t netvar_offset;
        } netvar_info;
#pragma pack(pop)
        void* some_struct_ptr;
    } info_stuff;
    void* unk3;
    void* callback;
    void* unk4_func;
};

struct EntOutput_t
{
    const char* name;
    std::uint32_t unk1;
    std::uint32_t CEntityIOOutput_offset;
};

struct EntClassComponentOverride_t
{
    const char* pszBaseComponent;
    const char* pszOverrideComponent;
};

struct CPanoramaGameScriptFunction
{
    const char* name_;
    const char* scoped_name_;
    const char* description_;
    std::string_view name() const
    {
        if (name_)
            return name_;
        return {};
    }
    std::string_view scoped_name() const
    {
        if (scoped_name_)
            return scoped_name_;
        return {};
    }
    std::string_view description() const
    {
        if (description_)
            return description_;
        return {};
    }
    void* unk1;
    void* unk2;
    const char* double_null_terminated_param_list;//'\x00\x00'-terminated
    const char* class_name;
    void* unk3_func;
    void* actual_func;
    void* unk4;
    std::vector<std::string> get_params() const
    {
        std::vector<std::string> result{};
        if (double_null_terminated_param_list)
        {
            auto ptr = double_null_terminated_param_list;
            while (true)
            {
                std::string_view param{ ptr };
                if (param.empty())
                    break;
                ptr = param.data() + param.size() + sizeof('\x00');
                result.emplace_back(param);
            }
        }
        return result;
    }
};

struct CPanoramaGameScriptScope
{
    const char* class_name_;
    const char* class_name2_;
    const char* class_description_;
    std::string_view class_name() const
    {
        if (class_name_)
            return class_name_;
        return {};
    }
    std::string_view class_name2() const
    {
        if (class_name2_)
            return class_name2_;
        return {};
    }
    std::string_view class_description() const
    {
        if (class_description_)
            return class_description_;
        return {};
    }
    CPanoramaGameScriptScope* base_scope;
    CUtlVector<CPanoramaGameScriptFunction> functions;
};

//string xref ...ReferencedPtr
enum class EntityComponent_t : std::uint8_t
{
    ScriptComponent = 0,
    BodyComponentBaseModelEntity = 1,
    BodyComponentBaseAnimGraph = 2,
    PropDataComponent = 3,
    RenderComponent = 4,
    LightComponent = 5,
    HitboxComponent = 6,
    BodyComponentPoint = 7,
    BodyComponentSkeletonInstance = 8,
    BodyComponent = 9,
    _BodyComponentBaseAnimatingOverlay = 0xA,
    _BodyComponentBaseAnimating = 0xB,
};

const char* EntityComponent_t_NAMES[]
{
    "ScriptComponent",
    "BodyComponentBaseModelEntity",
    "BodyComponentBaseAnimGraph",
    "PropDataComponent",
    "RenderComponent",
    "LightComponent",
    "HitboxComponent",
    "BodyComponentPoint",
    "BodyComponentSkeletonInstance",
    "BodyComponent",
    "_BodyComponentBaseAnimatingOverlay",
    "_BodyComponentBaseAnimating",
};

const char* stringify_EntityComponent_t(std::uint8_t val)
{
    if (val >= std::size(EntityComponent_t_NAMES))
    {
        return "";
    }
    return EntityComponent_t_NAMES[val];
}

class CEntityClass
{
public:
    CPanoramaGameScriptScope* m_pPanoramaGameScriptScope; //0x0000
    EntInput_t* m_pInputs; //0x0008
    EntOutput_t* m_pOutputs; //0x0010
    uint32_t m_nInputCount; //0x0018
    uint32_t m_nOutputCount; //0x001C
    EntClassComponentOverride_t* m_pComponentOverrides; //0x0020
    CEntityClassInfo* m_pClassInfo; //0x0028
    CEntityClassInfo* m_pBaseClassInfo; //0x0030
    const char* m_designerName; //0x0038
    void* unk1;//0x40
    std::uint32_t m_flags;//0x48
    std::uint32_t m_nAllHelpersFlags;//0x4C
    CUtlVector<std::int16_t> m_ComponentOffsets; //0x0050
    CUtlVector<void*> m_AllHelpers; //0x0068
    void* BaseClassPlus80; //0x0080
    void* unk2;//0x0088
    datamapField* m_pDataMapFields;//0x0090
    void* unk4;//0x0098
    void* unk5;//0x00A0
    std::uint16_t m_iDataMapFieldCount;//0xA8
    std::uint16_t unk6;//0xAA
    std::uint16_t unk7;//0xAC
    std::uint16_t unk8;//0xAE
    const char* m_pszScopedName; //0x00B0
    FlattenedSerializerHandle_t* m_pFlattenedSerializer; //0x00B8
    CUtlVector<void*> EntInputMap_StringToEntInput_t;//0x00C0
    void* unk9;//0x00D8
    void* unk10;//0x00E0
    void* unk11;//0x00E8
    std::int32_t m_requiredEHandle;//0x00F0
    std::uint32_t pad;//0x00F4
    CEntityClass* m_pForwardLinkNextClass; //0x00F8
    CEntityIdentity* m_pNextClass; //0x0100
    CEntityClassNetworkInstance* m_pNetworkInstance; //0x0108
    auto inputs() const
    {
        return std::span{ m_pInputs, m_nInputCount };
    }
    auto outputs() const
    {
        return std::span{ m_pOutputs, m_nOutputCount };
    }
    auto datamap_fields() const
    {
        return std::span{ m_pDataMapFields, m_iDataMapFieldCount };
    }
};

class CEntityClassNetworkInstance
{
public:
    const char* m_pszClientName;
    const char* m_pszServerName;
    CEntityClass* m_pClass;
    const char* m_pszScopedName;
    FlattenedSerializerHandle_t* m_pSerializerHandle;
    std::int32_t m_ClassID;
    std::int32_t m_iRuntimeIndex;
    void* unk1;
    void* unk2;
};
class CEntityClassInfo
{
public:
    const char* m_pszClassname; //0x0000
    const char* m_pszCPPClassname; //0x0008
    const char* m_pszDescription; //0x0010
    CEntityClass* m_pClass; //0x0018
    CEntityClassInfo* m_pBaseClassInfo; //0x0020
    CSchemaClassBinding* m_pSchemaClassBinding; //0x0028
    void* m_pDataDescMap; //0x0030
    void* m_pPredDescMap; //0x0038
};

struct FlattenedSerializerHandle_t
{
    const char* m_pName;
    CFlattenedSerializer* serializer;
};

enum NetworkFieldChangeCallbackType : std::uint8_t
{
    OBJPTR_OBJPTR = 1,
    OBJPTR_OBJPTR_NEWVALUEPTR = 2,
    OBJPTR = 3,
    UNK_MOVRCX_MOVRDX_MOVR8D_MOVR9 = 4,
    UNK_MOVRCX_MOVRDX_MOVR8D = 5,
    UNK_MOVRCX_LEARDX = 6,
    UNK_MOVRCX_LEARDX_MOVR8 = 7,
};

struct NetworkFieldChangeCallback_t
{
    void* unk1{};
    void* fn_callback{};
    const char* name{};
    std::uint16_t unk2{};
    std::uint16_t unk3{};
    std::uint16_t unk4{};
    NetworkFieldChangeCallbackType callback_type{};
    std::uint8_t pad{};
    void* unk5{};
    std::uint32_t unk6{};
};

struct CFlattenedSerializer
{
    struct NetVarInfo
    {
        const char* name_;
        CSchemaClassBinding* type_binding;
        void* unk1;
        std::int16_t offset;
        std::int16_t maybe_flags1;
        std::int32_t maybe_flags2;
        void* unk2;
        void* unk3;
        void* unk4;
        void* unk5;
        std::int32_t unk6;
        std::int32_t unk7;
        void* unk8;
        void* unk9;
        void* unk10;
        CUtlVector<NetworkFieldChangeCallback_t>* m_pNetVarChangeCallbacks;
        void* unk11;
        void* unk12;
        void* unk13;
        void* unk14;
        const char* m_pClassName;
        const char* m_pNetvarTypeName;
        void* unk15;
        void* unk16;
        void* unk17;
        std::string_view name() const noexcept
        {
            if (name_)
                return { name_ };
            return {};
        }
        std::string_view type_name() const noexcept
        {
            if (m_pNetvarTypeName)
                return { m_pNetvarTypeName };
            return {};
        }
    };

    const char* m_pName;
    CSchemaClassBinding* m_pSchemaClassBinding;
    CUtlVector<NetVarInfo*> netvars;
};
списки классов есть либо в CNetworkGameClient, либо в CEntity2NetworkClasses(указатель на нее есть в CGameEntitySystem, на 0x1538 вроде).
нетворк сериалайзеры есть либо в CFlattenedSerializers, либо в ентити классах.
прежде всего можно регать свои коллбеки на изменение нетвара или менять оффсеты нетваров(дота берет оффсеты из нетворк сериалазйеров), например для того же вбе
PoC вбе(особо не тестил но вроде работает)
C++:
struct test_cb
{
    CUtlVector<NetworkFieldChangeCallback_t> vec{};
    NetworkFieldChangeCallback_t cb{};

    test_cb(const char* name, void* fn)
    {
        vec.capacity = 1;
        vec.data = &cb;
        vec.size = 1;
        cb.name = name;
        cb.fn_callback = fn;
        cb.callback_type = NetworkFieldChangeCallbackType::OBJPTR_OBJPTR_NEWVALUEPTR;
    }
};
void(*ConMsg)(const char*, ...) = nullptr;
test_cb cb
{
    "MyTestCallback",
    +[](void* entity, void* _, std::uint32_t* new_val)
    {
        ConMsg("entity 0x%p value %d\n", entity, *new_val);
    }
};
...
    for (const auto& ni : nc)
    {
        if (ni && ni->m_pszClientName && ni->m_pClass)
        {
            const auto& cls = *ni->m_pClass;
            if (cls.m_pFlattenedSerializer)
            {
                if (cls.m_pFlattenedSerializer->serializer)
                {
                    const auto& netvars = cls.m_pFlattenedSerializer->serializer->netvars.span();
                    if (!netvars.empty())
                    {
                        for (const auto& netvar : netvars)
                        {
                            if (netvar)
                            {
                                if (!netvar->name().empty())
                                {
                                    if (netvar->name() == "m_iTaggedAsVisibleByTeam")
                                    {
                                        netvar->m_pNetVarChangeCallbacks = &cb.vec;
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
 
Последнее редактирование:
Участник
Статус
Оффлайн
Регистрация
23 Май 2019
Сообщения
851
Реакции[?]
335
Поинты[?]
67K
дерьмодампер шемы и ртти оффлайн POC качества(на доту работает на ксго вроде тоже)
shitcode:
Пожалуйста, авторизуйтесь для просмотра ссылки.
если вкратце то регистрация в шеме происходит через CSchemaRegistration_blablabla классы в модулях, эти классы имеют у себя виртуальную функцию RegisterAllBindings. эта функция состоит из двух фаз - предварительной и финальной;
биндинги на диске хранят всё кроме типов - типы заполняются из RegisterAllBindings(изза этого нельзя просто с диска(ну т.е. из самого .dll файла без его прогрузки) брать биндинги и дампить их - ну точнее можно(ток их сначала еще идентифицировать тогда придется) но тогда не будет инфы о типах нетваров никакой - только имена оффсеты мета и прочая хуита)
следовательно надо грузить .dll'ки и вызывать у них эти RegisterAllBindings - здесь можно либо частично ммапить без импортов без нихуя(хотя это не на всех версиях доты работает - на некоторых импорты вызываются(следовательно их тоже либо эмулировать либо грузить)) и эмулировать шема систему(ну это хуйня идея слишком много работы и слишком нестабильно между версиями), либо просто нормально полноценно грузить официальную schemasystem.dll и остальные модули тоже нормально грузить, и потом уже доставать либо из шема системы всю инфу(нестабильно между версиями) либо просто абузить тот факт что всё пролетает через стабильные CSchemaSystemTypeScope::InstallSchemaClassBinding(0 index) и CSchemaSystemTypeScope::InstallSchemaEnumBinding(1 index). собственно весь дампер держится на этих двух хуках(вмт по ртти находится)
существует две "мажорные" версии шемы - 2015-2019 и 2019+ (между ними еще есть 2018-2019 кусочек который похож на 2019+ но чуть чуть отличается я его скипнул там ~6 месяцев всего лишь он жил или меньше даже). разница между этими версиями это сами структуры биндингов и того что они содержат(члены, мета, типы и тд) и порядок параметров в InstallSchemaClassBinding/InstallSchemaEnumBinding.
дллки грузятся, хукаются InstallSchemaClassBinding/InstallSchemaEnumBinding в шеме, вызывается экспорт у каждого из модулей InstallSchemaBindings(это грубо говоря просто g_pInterfaceGlobals._g_pSchemaSystem = addr_of_CSchemaSystem и foreach(CSchemaRegistration_blablabla) RegisterAllBindings). все что пролетает через хуки трансформируется в стабильный вид и сохраняется в список, потом после того как всё зарегалось по списку идут фиксы(например инфа(размер и выравнивание) о некоторых типах недоступна изначально(при первичной регистрации) и такие инвалидные типы фиксятся после того как всё зарегалось, за исключением ~11 типов которых в шеме просто нет(~4/5 штук) или нет в конкретно данных версиях но есть в следующих/предыдущих - они остаются как есть с 0 размером и выравниванием, родители фиксятся(родители должны быть в локальном масштабе, а шема может на указывать дубликаты из другого масштаба в качестве родителей(ну это токо для дампа фулл иерархии))) потом запись всей этой хуйни на диск. единственная "нестабильность" это вызов GetSizeAndAlign у которого индекс нестабильный но он не захардкожен а ищется сканом так что все работает вроде нормально на доту(42 версии проверил 2015-2024) и кс2(8 версий 2023-2024)
если кому-то нужны one-liner'ы компиляции то:
MSVC: x64 Native Tools Command Prompt for VS 2022(в пуске есть)
cl /std:c++latest /O2 /EHsc schema_dumper.cpp
Clang-cl:
clang-cl /std:c++latest /O2 /EHsc schema_dumper.cpp
update 2025
убрали инфу о статик мемберах насколько я понял
Пожалуйста, авторизуйтесь для просмотра ссылки.
заодно дамп от Wed Feb 19 2025 05:23:21
Пожалуйста, авторизуйтесь для просмотра ссылки.
 
Последнее редактирование:
Начинающий
Статус
Оффлайн
Регистрация
30 Мар 2020
Сообщения
355
Реакции[?]
24
Поинты[?]
12K
update 2025
убрали инфу о статик мемберах насколько я понял
Пожалуйста, авторизуйтесь для просмотра ссылки.
заодно дамп от Wed Feb 19 2025 05:23:21
Пожалуйста, авторизуйтесь для просмотра ссылки.
m_vecAbilities теперь вместо m_hAbilities походу

Внутри самой сущности появился новый указатель на класс CEntitySharedGapSignature
 
Последнее редактирование:
Начинающий
Статус
Оффлайн
Регистрация
16 Авг 2022
Сообщения
42
Реакции[?]
4
Поинты[?]
4K
CResourceSystem::FindOrRegisterResourceByName -> 31
GetAbilityTextureName -> 226
GetDamage -> 319
GetEffectiveCastRange -> 281
GetAttackRange -> 303
GetPhyisicalArmor -> 306
GetMagicalArmor -> 307

CDOTAParticleManager -> CUtlVector particles -> 0x98
handle -> 0xE8

also SchemaClassInfoData_t->ParentInfo changed from 0x38 to 0x30.

m_hAbilities -> m_vecAbilities:

Код:
 auto hAbilities = MemberInline<CUtlVector<ENT_HANDLE>>(Netvars["C_DOTA_BaseNPC"]["m_vecAbilities"]);
auto ability = Interfaces::EntitySystem->GetEntity<CDOTABaseAbility>(H2IDX(hAbilities->at(idx)));
 
Сверху Снизу