Вопрос Dispatch_SOCreated

  • Автор темы Автор темы Cora32
  • Дата начала Дата начала
Начинающий
Начинающий
Статус
Оффлайн
Регистрация
12 Мар 2025
Сообщения
26
Реакции
4
Что сейчас нужно передавать в dispatch_SOCreated для шмоток? Дота+ анлокается на лету без проблем, со шмотками беда.
Появляются только после перезахода в игру. То есть создается она правильно, только SOCreated не работает.
Пол дня уже убил — решил таки спросить, может кто знает.

C++:
Expand Collapse Copy
    const auto inventory = vmt.gc_client->SOListeners<CDOTAPlayerInventory>()[1];
    const auto so_cache = inventory->get_so_cache();

    SOID_t soid = inventory->get_owner();

    CEconItem* item = inventory->create_item_object();
    CSOEconItem proto;

    proto.set_id(m_itemid_counter++);
    proto.set_inventory(m_invpos_counter++);
    proto.set_account_id(soid.m_unSteamID & 0xFFFFFFFF);
    proto.set_quality(EEconItemQuality::AE_NORMAL);
    proto.set_origin(eEconItemOrigin::kEconItemOrigin_Earned);
    proto.set_flags(eEconItemFlags::kEconItemFlag_NonEconomy);
    proto.set_style(0);
    proto.set_quantity(1);
    proto.set_def_index(def_index); // Передается в функу переменной
    item->DeserializeFromProto(&proto);
    so_cache->add_item(item);

    inventory->dispatch_created(soid, item, ESOCacheEvent::eSOCacheEvent_Incremental); // Пробовал и 0 индекс, и 1 и 2
 
Что сейчас нужно передавать в dispatch_SOCreated для шмоток? Дота+ анлокается на лету без проблем, со шмотками беда.
Появляются только после перезахода в игру. То есть создается она правильно, только SOCreated не работает.
Пол дня уже убил — решил таки спросить, может кто знает.

C++:
Expand Collapse Copy
    const auto inventory = vmt.gc_client->SOListeners<CDOTAPlayerInventory>()[1];
    const auto so_cache = inventory->get_so_cache();

    SOID_t soid = inventory->get_owner();

    CEconItem* item = inventory->create_item_object();
    CSOEconItem proto;

    proto.set_id(m_itemid_counter++);
    proto.set_inventory(m_invpos_counter++);
    proto.set_account_id(soid.m_unSteamID & 0xFFFFFFFF);
    proto.set_quality(EEconItemQuality::AE_NORMAL);
    proto.set_origin(eEconItemOrigin::kEconItemOrigin_Earned);
    proto.set_flags(eEconItemFlags::kEconItemFlag_NonEconomy);
    proto.set_style(0);
    proto.set_quantity(1);
    proto.set_def_index(def_index); // Передается в функу переменной
    item->DeserializeFromProto(&proto);
    so_cache->add_item(item);

    inventory->dispatch_created(soid, item, ESOCacheEvent::eSOCacheEvent_Incremental); // Пробовал и 0 индекс, и 1 и 2
всё норм вроде. ты в мейн треде?
C++:
Expand Collapse Copy
CNetworkClientService::OnClientFrameSimulate(EventClientFrameSimulate_t) <-
    {
        //inside is string xref "CEconItem::DeserializeFromProtoBufItem()"
        auto CEconItem_DeserializeFromProtoBufItem = (void(*)(CEconItem*, CSOEconItem*))
            (client + 0x2975560);

        //inside is string xref "Probably failed to set object type (%d) on the server/client.\n"
        auto GCSDK_CSharedObject_Create = (CEconItem * (*)(EEconTypeID))
            (client + 0x2bc7dc0);

        auto& inv = get_CDOTAPlayerInventory(); //client:$0x5371598
        auto* so_cache = inv.GetSOCache(); //0xA0
        if (so_cache)
        {
            CEconItem* item = GCSDK_CSharedObject_Create(EEconTypeID::k_EEconTypeItem/* 1 */);
            if (item)
            {
                CSOEconItem proto_item{};
                proto_item.set_id(123999);
                proto_item.set_inventory(0); /* 0 = unacknowledged = show "YOU HAVE RECEIVED AN ITEM" window */
                proto_item.set_account_id(inv.GetOwner()/* 0x8 */.steamid.steamid64 & 0xFFFFFFFF);
                proto_item.set_quality(0);
                proto_item.set_origin(0);
                proto_item.set_flags(0);
                proto_item.set_style(0);
                proto_item.set_quantity(1);
                proto_item.set_def_index(7247);
                CEconItem_DeserializeFromProtoBufItem(item, &proto_item);
                so_cache->CallVFunc<3>(item); /* AddObject */
                inv.CallVFunc<0> /* SOCreated */
                    (inv.GetOwner()/* 0x8 */, item, ESOCacheEvent::eSOCacheEvent_Incremental/* 4 */);
            }
        }
    }
1746999075617.png
 
всё норм вроде. ты в мейн треде?
C++:
Expand Collapse Copy
CNetworkClientService::OnClientFrameSimulate(EventClientFrameSimulate_t) <-
    {
        //inside is string xref "CEconItem::DeserializeFromProtoBufItem()"
        auto CEconItem_DeserializeFromProtoBufItem = (void(*)(CEconItem*, CSOEconItem*))
            (client + 0x2975560);

        //inside is string xref "Probably failed to set object type (%d) on the server/client.\n"
        auto GCSDK_CSharedObject_Create = (CEconItem * (*)(EEconTypeID))
            (client + 0x2bc7dc0);

        auto& inv = get_CDOTAPlayerInventory(); //client:$0x5371598
        auto* so_cache = inv.GetSOCache(); //0xA0
        if (so_cache)
        {
            CEconItem* item = GCSDK_CSharedObject_Create(EEconTypeID::k_EEconTypeItem/* 1 */);
            if (item)
            {
                CSOEconItem proto_item{};
                proto_item.set_id(123999);
                proto_item.set_inventory(0); /* 0 = unacknowledged = show "YOU HAVE RECEIVED AN ITEM" window */
                proto_item.set_account_id(inv.GetOwner()/* 0x8 */.steamid.steamid64 & 0xFFFFFFFF);
                proto_item.set_quality(0);
                proto_item.set_origin(0);
                proto_item.set_flags(0);
                proto_item.set_style(0);
                proto_item.set_quantity(1);
                proto_item.set_def_index(7247);
                CEconItem_DeserializeFromProtoBufItem(item, &proto_item);
                so_cache->CallVFunc<3>(item); /* AddObject */
                inv.CallVFunc<0> /* SOCreated */
                    (inv.GetOwner()/* 0x8 */, item, ESOCacheEvent::eSOCacheEvent_Incremental/* 4 */);
            }
        }
    }
Посмотреть вложение 306114

Да в том и прикол, что у меня во FrameStageNotify идут подряд анлок плюса и создание шмотки. Плюс-то анлокается.
И код у меня такой же, как у тебя. Я потому и решил тему создать — хрень какая-то, вообще не понимаю, в чем проблема.
Вот что ему не нравится?
C++:
Expand Collapse Copy
struct SOID_t {
    uint64_t m_unSteamID;
    uint32_t m_iType;
};

class CDOTAPlayerInventory : public VClass {
public:
    CEconItem* create_item_object();

    bool dispatch_created(SOID_t soid, CEconItem* sharedObj, ESOCacheEvent ev) {
        return this->call_virtual<0, bool>(soid, *sharedObj, ev);
    }

    bool dispatch_update(SOID_t soid, CEconItem* sharedObj, ESOCacheEvent ev) {
        return this->call_virtual<1, bool>(soid, *sharedObj, ev);
    }

    CGCClientSharedObjectCache* get_so_cache() const {
        return Memory::read_memory<CGCClientSharedObjectCache*>(this + 0xA0).value();
    }

    SOID_t get_owner() const {
        return member<SOID_t>(0x8);
    };
};
 
Да в том и прикол, что у меня во FrameStageNotify идут подряд анлок плюса и создание шмотки. Плюс-то анлокается.
И код у меня такой же, как у тебя. Я потому и решил тему создать — хрень какая-то, вообще не понимаю, в чем проблема.
Вот что ему не нравится?
C++:
Expand Collapse Copy
struct SOID_t {
    uint64_t m_unSteamID;
    uint32_t m_iType;
};

class CDOTAPlayerInventory : public VClass {
public:
    CEconItem* create_item_object();

    bool dispatch_created(SOID_t soid, CEconItem* sharedObj, ESOCacheEvent ev) {
        return this->call_virtual<0, bool>(soid, *sharedObj, ev);
    }

    bool dispatch_update(SOID_t soid, CEconItem* sharedObj, ESOCacheEvent ev) {
        return this->call_virtual<1, bool>(soid, *sharedObj, ev);
    }

    CGCClientSharedObjectCache* get_so_cache() const {
        return Memory::read_memory<CGCClientSharedObjectCache*>(this + 0xA0).value();
    }

    SOID_t get_owner() const {
        return member<SOID_t>(0x8);
    };
};
ну-ка скинь код у call_virtual.
у тебя не перфект форвадинг случаем?
если да то ты вызываешь если что с аргументами
SOID_t&, CEconItem&, ESOCacheEvent&
а у функции SOID_t&, CEconItem&, ESOCacheEvent
ну или если другими словами SOID_t*, CEconItem*, int
кароче ласт аргумент у тебя указатель на инт(изза перфект форвадинга) а не инт который функция ожидает
не надо путать переменную и значение. переменная это КОНТЕЙНЕР(внутри которого лежит неизвестное значение, и узнать что там лежит можно токо засунув туда руку(считать с переменной). кароче переменная это указатель(ну либо что-то эквивалентное, регистр там и так далее, в общем хранилище для значений)). любая хуйня которая имеет имя это уже сразу контейнер А НЕ ЗНАЧЕНИЕ. у тебя ev в функции это не четыре. это контейнер(переменная) в которой лежит четыре. и ты перфект форвардишь это(ПЕРЕМЕННУЮ) в функцию игровую, и она естественно видит там не 4 а указатель на четверку и ахуевает. убери перфект форвардинг и передавай онли указатели а не ссылки в чужие функции.
если ты хочешь по значению при перфект форвардинге передавать то копию передавай
C++:
Expand Collapse Copy
decltype(ev){ ev }
естественно ты в большинстве случаев именно по значению хочешь передавать так что встаёт вопрос нахуй тогда вообще нужен перфект форвадинг в таких ситауциях и ответ простой - он не нужен))
если не перфект форвадинг, тогда поставь бп на эту SOCreated и постепь(по 1 инстуркции выполняй, там есть отдельные кнопки для этого в дебаггерах которые single-step функционал у процессоров юзают) в дебаггере и смотри какие проверки ты проваливаешь. он в идеале должен вызвать CPlayerInventory::AddEconItem если проверки проходят.
 
Последнее редактирование:
ну-ка скинь код у call_virtual
C++:
Expand Collapse Copy
class Memory {
    public:
    template <typename Ptr>
    static void**& vtable(const Ptr& inst, const size_t offset = 0) {
        return *reinterpret_cast<void***>(reinterpret_cast<uintptr_t>(inst) + offset);
    }

    template <typename Ret, typename Ptr>
    static Ret virtual_function(const Ptr& address_ptr, const size_t index, const size_t offset = 0) {
        return (Ret)vtable(address_ptr, offset)[index];
    }
}

class VClass {
protected:
    template<int index, typename Ret, typename... Args>
    static Ret call_virtual_impl(void* self, Args&&... args) {
        using FnType = Ret(*)(void*, Args...);
        const auto func_addr = Memory::virtual_function<void*>(self, index);
        auto fn = reinterpret_cast<FnType>(func_addr);
        return fn(self, std::forward<Args>(args)...);
    }

public:
    template<int index, typename Ret, typename... Args>
    Ret call_virtual(Args&&... args) {
        return call_virtual_impl<index, Ret>((void*)this, std::forward<Args>(args)...);
    }
};

В любом случае, спасибо, я решил забить. Энивей это просто фейк шмотки, а чтобы в катке менять — у меня столько времени нет, лучше основной софт продолжу делать, мб потом до ченжера руки дойдут.
 
C++:
Expand Collapse Copy
class Memory {
    public:
    template <typename Ptr>
    static void**& vtable(const Ptr& inst, const size_t offset = 0) {
        return *reinterpret_cast<void***>(reinterpret_cast<uintptr_t>(inst) + offset);
    }

    template <typename Ret, typename Ptr>
    static Ret virtual_function(const Ptr& address_ptr, const size_t index, const size_t offset = 0) {
        return (Ret)vtable(address_ptr, offset)[index];
    }
}

class VClass {
protected:
    template<int index, typename Ret, typename... Args>
    static Ret call_virtual_impl(void* self, Args&&... args) {
        using FnType = Ret(*)(void*, Args...);
        const auto func_addr = Memory::virtual_function<void*>(self, index);
        auto fn = reinterpret_cast<FnType>(func_addr);
        return fn(self, std::forward<Args>(args)...);
    }

public:
    template<int index, typename Ret, typename... Args>
    Ret call_virtual(Args&&... args) {
        return call_virtual_impl<index, Ret>((void*)this, std::forward<Args>(args)...);
    }
};

В любом случае, спасибо, я решил забить. Энивей это просто фейк шмотки, а чтобы в катке менять — у меня столько времени нет, лучше основной софт продолжу делать, мб потом до ченжера руки дойдут.
ну так я же тебе говорю - убери перфект форвадинг, принимай по значению, и не используй ссылки когда работаешь с чужими функциями, и всё будет хорошо. ты не передаешь четыре(eSOCacheEvent_Incremental) в функцию, ты передаешь адрес, где лежит четыре
C++:
Expand Collapse Copy
    template<std::size_t FunctionIndex, typename ReturnType = std::uintptr_t, typename ... ArgTypes>
    ReturnType CallVFunc(ArgTypes... Args) const
    {
        const auto func = tbl.GetFunctionAt(FunctionIndex);
        if (!func)
            __debugbreak();
        return (reinterpret_cast<ReturnType(__thiscall*)(const VClass*, ArgTypes...)>(func))(this, Args...);
    }
это ты будешь и в других местах наступать на эти же грабли пока не поменяешь свою call_virtual
убери && и std::forward, и ссылки не передавай в такие функции, юзай указатели
если я напишу
C++:
Expand Collapse Copy
int a = 123;
то:
1) какое значение имеет a?
ответ - неизвестно. переменная это не заведомо известная константа. значение переменных меняется со временем. нужно проверить ее значение - т.е. считать данные с её хранилища(регистр, оперативная память и т.д.), и только потом можно утверждать что она имеет какоето конкретное значение(и то не факт что оно будет всегда актуальным, может оно кем-то поменяется через одну миллисекунду, например, надо будет заново считывать)
2) какой тип имеет выражение a?
int&
уж точно не int по одной простой причине - нельзя было бы например присвоение юзать
123 это int гарантированно, но 123 = 456 это бессмысленное выражение. потому что нельзя интам присваивать. можно присваивать хранилищу где лежит инт. но вот a = 456 можно написать, потому что a это не инт, это хранилище где лежит инт.
так вот ты когда пишешь
C++:
Expand Collapse Copy
bool dispatch_created(SOID_t soid, CEconItem* sharedObj, ESOCacheEvent ev) {
    return this->call_virtual<0, bool>(soid, *sharedObj, ev);
}
твоё ev это ХРАНИЛИЩЕ(изначально регистр r9, если надо(например если ты попробуешь логнуть адрес этой фигни) то компилятор выльет(register spill) это в стек). вот эти твои && и std::forward чётко захватывают натуру данного выражения - ХРАНИЛИЩЕ - и передают это в функцию. а функция ожидает ЗНАЧЕНИЕ. дефолтные семантики С++ деградируют всё до обычных значений(например a может из int& деградировать просто в int в зависимости от контекста, например если ты напишешь int b = a;), и всякие & и && позволяют это предотвратить, но в твоём случае тебе это абсолютно не нужно и имеет контрпродуктивный эффект. при работе с внешними вещами(игровые функции и т.д.) лучше использовать С и не трогать С++, т.е. не надо передавать/возвращать никаких ссылок(указатели вместо них), никаких структур(вместо этого указатели на них или декомпозиция на члены если они влезают в регистр)
 
Последнее редактирование:
ну так я же тебе говорю - убери перфект форвадинг, принимай по значению, и не используй ссылки когда работаешь с чужими функциями, и всё будет хорошо. ты не передаешь четыре(eSOCacheEvent_Incremental) в функцию, ты передаешь адрес, где лежит четыре
C++:
Expand Collapse Copy
    template<std::size_t FunctionIndex, typename ReturnType = std::uintptr_t, typename ... ArgTypes>
    ReturnType CallVFunc(ArgTypes... Args) const
    {
        const auto func = tbl.GetFunctionAt(FunctionIndex);
        if (!func)
            __debugbreak();
        return (reinterpret_cast<ReturnType(__thiscall*)(const VClass*, ArgTypes...)>(func))(this, Args...);
    }
это ты будешь и в других местах наступать на эти же грабли пока не поменяешь свою call_virtual
убери && и std::forward, и ссылки не передавай в такие функции, юзай указатели
если я напишу
C++:
Expand Collapse Copy
int a = 123;
то:
1) какое значение имеет a?
ответ - неизвестно. переменная это не заведомо известная константа. значение переменных меняется со временем. нужно проверить ее значение - т.е. считать данные с её хранилища(регистр, оперативная память и т.д.), и только потом можно утверждать что она имеет какоето конкретное значение(и то не факт что оно будет всегда актуальным, может оно кем-то поменяется через одну миллисекунду, например, надо будет заново считывать)
2) какой тип имеет выражение a?
int&
уж точно не int по одной простой причине - нельзя было бы например присвоение юзать
123 это int гарантированно, но 123 = 456 это бессмысленное выражение. потому что нельзя интам присваивать. можно присваивать хранилищу где лежит инт. но вот a = 456 можно написать, потому что a это не инт, это хранилище где лежит инт.
так вот ты когда пишешь
C++:
Expand Collapse Copy
bool dispatch_created(SOID_t soid, CEconItem* sharedObj, ESOCacheEvent ev) {
    return this->call_virtual<0, bool>(soid, *sharedObj, ev);
}
твоё ev это ХРАНИЛИЩЕ(изначально регистр r9, если надо(например если ты попробуешь логнуть адрес этой фигни) то компилятор выльет(register spill) это в стек). вот эти твои && и std::forward чётко захватывают натуру данного выражения - ХРАНИЛИЩЕ - и передают это в функцию. а функция ожидает ЗНАЧЕНИЕ. дефолтные семантики С++ деградируют всё до обычных значений(например a может из int& деградировать просто в int в зависимости от контекста, например если ты напишешь int b = a;), и всякие & и && позволяют это предотвратить, но в твоём случае тебе это абсолютно не нужно и имеет контрпродуктивный эффект. при работе с внешними вещами(игровые функции и т.д.) лучше использовать С и не трогать С++, т.е. не надо передавать/возвращать никаких ссылок(указатели вместо них), никаких структур(вместо этого указатели на них или декомпозиция на члены если они влезают в регистр)

Ого, теперь максимальный пон, спасибо. Ну мог и сам догадаться, что дело не в ченжере.

Об этом уже делали гайд, держу в курсе )) https://yougame.biz/threads/294199/

Ничего, что все нужные функи у меня уже есть? Зачем мне гайд о том, что я и так сделал.
 
Назад
Сверху Снизу