Вы используете устаревший браузер. Этот и другие сайты могут отображаться в нём некорректно. Вам необходимо обновить браузер или попробовать использовать другой.
ВопросI wanna understand what is the C_DOTAGamerules ?
update your dota(if you haven't already) and re-open the new dll in ida(or just don't use it at all, most of the stuff can be done directly in x64dbg or cheat engine or whatever else). the dll you analyzed is outdated
it's not 20C6510 anymore, it's 20C6530(client.dll - sha1: 377f3f21be0d9d4216f43d3755f7df6ab7ba8b0d timestamp: 1761161544(19:32:24 22 Oct 2025))
ok i got it correctly but to understand the CGCLIENT correct
tis CGClient has something called lobby manager
and this lobby manager the StartFindingMatch and accpet match etc .... right ?
and the CDOTA_DB_Play is global singleton right ?
or did i get it wrong?
ok i got it correctly but to understand the CGCLIENT correct
tis CGClient has something called lobby manager
and this lobby manager the StartFindingMatch and accpet match etc .... right ?
and the CDOTA_DB_Play is global singleton right ?
or did i get it wrong?
forget about the fucking game coordinator client, it's a low-level interface, it will be too difficult for you to figure out.
just CDOTA_DB_Play is enough(and yes there are global pointers to CDOTA_DB_Play), it uses the game coordinator client internally. it's literally the PLAY button in the main menu.
forget about the fucking game coordinator client, it's a low-level interface, it will be too difficult for you to figure out.
just CDOTA_DB_Play is enough(and yes there are global pointers to CDOTA_DB_Play), it uses the game coordinator client internally. it's literally the PLAY button in the main menu.
chill dude i know that i have asked too many questions regarding this but i was just trying to understand the structure nothing more
right now the StartFindingMatch method is taking the CDOTA_DB_Play as argument and iam trying to reverse it to get figure out how can i send the object that has the game mode and region etc..... to be sent to the StartFindingMatching method
chill dude i know that i have asked too many questions regarding this but i was just trying to understand the structure nothing more
right now the StartFindingMatch method is taking the CDOTA_DB_Play as argument and iam trying to reverse it to get figure out how can i send the object that has the game mode and region etc..... to be sent to the StartFindingMatching method
there are global/static pointers to it in client.dll(green addresses in cheat engine)
you can get the CDOTA_DB_Play* from the rcx from the breakpoint at StartFindingMatch, then use cheat engine to search for pointers to that value, you'll find one green address and a bunch of uninteresting stuff, then check xrefs to that green address(x64dbg/ida); here's an example:
there are global/static pointers to it in client.dll(green addresses in cheat engine)
you can get the CDOTA_DB_Play* from the rcx from the breakpoint at StartFindingMatch, then use cheat engine to search for pointers to that value, you'll find one green address and a bunch of uninteresting stuff, then check xrefs to that green address(x64dbg/ida); here's an example:
ok thanks a lot 1 more thing how can i accept match when the match is recieved
i think in a prespective of programming i can subscribe to the dashboard messages then when the game is ready with certine message i can do something upon that right?
ok thanks a lot 1 more thing how can i accept match when the match is recieved
i think in a prespective of programming i can subscribe to the dashboard messages then when the game is ready with certine message i can do something upon that right?
the signature is unacceptably long(the longer it is, the more brittle it will be and the faster it will break between updates). they need to be long enough to be unique, but if they're too long then that means you're taking the wrong approach. it shouldn't take more than 1-2 lines in your code. try extracting from xrefs:
sig that place, then go to the instruction(by adding its offest from the beginning of the place you found), then extract 7FFA62B41490 from the instruction(it's a rel32 address you need to decode it)
OR you can just sig the end of the function(it's unique), e.g. ba 01 00 00 00 48 8b c8 e8 ?? ?? ?? ?? 41 b0, and then add the offset to the beginning from that place(it's -0x113 bytes for that sig). and to be safe, also double check that the beginning(that you obtained by subtracting 0x113) matches the expected pattern of the beginning, e.g. 40 57 48 83 ec ?? 83 79 ?? ?? 48 8d 51 - just in case the offset changes(because the function changed in the middle) (and throw an error if it doesn't match)
there are many ways to check if the match is found, you can either check for lobby updates, or hook popup panel(CDOTA_DB_Popup_AcceptMatch) creation, or simply check for the presence of the popup panel. get creative.
C++:
#include <Windows.h>
#include <string>
template<class T>
struct CUtlVector
{
int size;
T* data;
int capacity;
T const* begin() const
{
return data;
}
T* begin()
{
return data;
}
T const* end() const
{
return data + size;
}
T* end()
{
return data + size;
}
};
struct CPanel2D;
struct CUIPanel
{
auto panel2d()
{
return *(CPanel2D**)((std::uintptr_t)this + 0x8);
}
auto& children()
{
return *(CUtlVector<CUIPanel*>*)((std::uintptr_t)this + 0x28);
}
std::string_view id()
{
auto const result = *(char const**)((std::uintptr_t)this + 0x10);
if (result)
{
return std::string_view{ result };
}
return {};
}
CUIPanel* FindDirectChild(std::string_view el_id)
{
for (auto e : children())
{
if (e && e->id() == el_id)
{
return e;
}
}
return nullptr;
}
CUIPanel* FindDFS(std::string_view el_id)
{
if (id() == el_id)
{
return this;
}
for (auto e : children())
{
if (e)
{
if (auto result = e->FindDFS(el_id); result)
{
return result;
}
}
}
return nullptr;
}
//see eponymous string xref in client.dll
bool BHasClass(std::uint16_t symbol)
{
using fn = bool(*)(CUIPanel*, std::uint16_t);
return ((fn)((*(void***)(this))[155]))(this, symbol);
}
};
struct CPanel2D
{
auto uipanel()
{
return *(CUIPanel**)((std::uintptr_t)this + 0x8);
}
};
int __stdcall DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
{
if (ul_reason_for_call == DLL_PROCESS_ATTACH)
{
char* client = (char*)GetModuleHandleA("client.dll");
if (!client)
{
OutputDebugStringA("failed to get client.dll!");
return TRUE;
}
char* panorama = (char*)GetModuleHandleA("panorama.dll");
if (!panorama)
{
OutputDebugStringA("failed to get panorama.dll!");
return TRUE;
}
/*
00007FFA6339F228 | 48:0F45F9 | cmovne rdi,rcx |
00007FFA6339F22C | 48:8D0D 251AB202 | lea rcx,qword ptr ds:[7FFA65EC0C58] | 00007FFA65EC0C58:"Mismatch addon %s CRC date: Client=%llu Server=%llu\n"
00007FFA6339F233 | 48:8BD7 | mov rdx,rdi |
00007FFA6339F236 | FF15 74D3FD01 | call qword ptr ds:[<&Warning>] |
00007FFA6339F23C | 48:8B0D 653D3004 | mov rcx,qword ptr ds:[7FFA676A2FA8] | <--- dashboard
00007FFA6339F243 | 48:85C9 | test rcx,rcx |
00007FFA6339F246 | 74 1C | je client.7FFA6339F264 |
00007FFA6339F248 | 48:8B49 30 | mov rcx,qword ptr ds:[rcx+30] |
00007FFA6339F24C | 48:85C9 | test rcx,rcx |
00007FFA6339F24F | 74 13 | je client.7FFA6339F264 |
00007FFA6339F251 | 4C:8D05 381AB202 | lea r8,qword ptr ds:[7FFA65EC0C90] | 00007FFA65EC0C90:"#custom_game_mismatch_desc"
00007FFA6339F258 | 48:8D15 511AB202 | lea rdx,qword ptr ds:[7FFA65EC0CB0] | 00007FFA65EC0CB0:"#custom_game_mismatch_title"
00007FFA6339F25F | E8 EC1430FF | call client.7FFA626A0750 |
00007FFA6339F264 | 48:837C24 50 00 | cmp qword ptr ss:[rsp+50],0 |
*/
auto dashboard = *(CPanel2D**)(client + 0x54a2fa8);
if (!dashboard)
{
OutputDebugStringA("failed to get dashboard");
}
auto dashboard_ui = dashboard->uipanel();
if (!dashboard)
{
OutputDebugStringA("failed to get dashboard_ui");
}
auto dashboard_center = dashboard_ui->FindDirectChild("DashboardCenter");
if (!dashboard)
{
OutputDebugStringA("failed to get DashboardCenter");
}
auto popup_manager = dashboard_center->FindDirectChild("DashboardPopupManager");
if (!dashboard)
{
OutputDebugStringA("failed to get DashboardPopupManager");
}
for (auto child : popup_manager->children())
{
auto const id = child->id();
if (id != "SettingsReborn" && id != "BlurBackground" && id != "DimBackground")
{
auto const match_popup = child->FindDFS("PopupAcceptDeclineMatchPanel");
if (match_popup != nullptr)
{
//inside is string xref "Panorama Symbol table %d filled, couldn't add new symbol '%s'"
auto MakeSymbol = (std::uint16_t(*)(void*, char const*))(panorama + 0x956e0);
if (
!child->BHasClass(MakeSymbol(nullptr, "ReadyUpPlayersVisible"))
&& !child->BHasClass(MakeSymbol(nullptr, "MatchAcceptedClicked"))
&& !child->BHasClass(MakeSymbol(nullptr, "Hidden"))
)
{
OutputDebugStringA("found PopupAcceptDeclineMatchPanel!");
struct unk_
{
CPanel2D* panel;
void* fn;
};
unk_ unk{ child->panel2d(), client + 0x2101490 };
//inside is string xref "ui.matchmaking_accept"
((void(*)(CPanel2D*, unk_*))(client + 0x2101490))(child->panel2d(), &unk);
return TRUE;
}
}
}
}
OutputDebugStringA("didn't find PopupAcceptDeclineMatchPanel");
}
return TRUE;
}
the signature is unacceptably long(the longer it is, the more brittle it will be and the faster it will break between updates). they need to be long enough to be unique, but if they're too long then that means you're taking the wrong approach. it shouldn't take more than 1-2 lines in your code. try extracting from xrefs:
sig that place, then go to the instruction(by adding its offest from the beginning of the place you found), then extract 7FFA62B41490 from the instruction(it's a rel32 address you need to decode it)
OR you can just sig the end of the function(it's unique), eg ba 01 00 00 00 48 8b c8 e8 ?? ?? ?? ?? 41 b0, and then add the offset to the beginning from that place(it's -0x113 bytes for that sig). and to be safe, also double check that the beginning(that you obtained by subtracting 0x113) matches the expected pattern of the beginning, eg 40 57 48 83 ec ?? 83 79 ?? ?? 48 8d 51 - just in case the offset changes(because the function changed in the middle) (and throw an error if it doesn't match)
there are many ways to check if the match is found, you can either check for lobby updates, or hook popup panel(CDOTA_DB_Popup_AcceptMatch) creation, or simply check for the presence of the popup panel. get creative. Посмотреть вложение 319109
C++:
#include <Windows.h>
#include <string>
template<class T>
struct CUtlVector
{
int size;
T* data;
int capacity;
T const* begin() const
{
return data;
}
T* begin()
{
return data;
}
T const* end() const
{
return data + size;
}
T* end()
{
return data + size;
}
};
struct CPanel2D;
struct CUIPanel
{
auto panel2d()
{
return *(CPanel2D**)((std::uintptr_t)this + 0x8);
}
auto&children()
{
return *(CUtlVector<CUIPanel*>*)((std::uintptr_t)this + 0x28);
}
std::string_view id()
{
auto const result = *(char const**)((std::uintptr_t)this + 0x10);
if (result)
{
return std::string_view{ result };
}
return {};
}
CUIPanel* FindDirectChild(std::string_view el_id)
{
for (auto e : children())
{
if (e && e->id() == el_id)
{
return e;
}
}
return nullptr;
}
CUIPanel* FindDFS(std::string_view el_id)
{
if (id() == el_id)
{
return this;
}
for (auto e : children())
{
if (e)
{
if (auto result = e->FindDFS(el_id); result)
{
return result;
}
}
}
return nullptr;
}
//see eponymous string xref in client.dll
bool BHasClass(std::uint16_t symbol)
{
using fn = bool(*)(CUIPanel*, std::uint16_t);
return ((fn)((*(void***)(this))[155]))(this, symbol);
}
};
struct CPanel2D
{
auto uipanel()
{
return *(CUIPanel**)((std::uintptr_t)this + 0x8);
}
};
int __stdcall DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
{
if (ul_reason_for_call == DLL_PROCESS_ATTACH)
{
char* client = (char*)GetModuleHandleA("client.dll");
if (!client)
{
OutputDebugStringA("failed to get client.dll!");
return TRUE;
}
char* panorama = (char*)GetModuleHandleA("panorama.dll");
if (!panorama)
{
OutputDebugStringA("failed to get panorama.dll!");
return TRUE;
}
/*
00007FFA6339F228 | 48:0F45F9 | cmovne rdi,rcx |
00007FFA6339F22C | 48:8D0D 251AB202 | lea rcx,qword ptr ds:[7FFA65EC0C58] | 00007FFA65EC0C58:"Mismatch addon %s CRC date: Client=%llu Server=%llu\n"
00007FFA6339F233 | 48:8BD7 | mov rdx,rdi |
00007FFA6339F236 | FF15 74D3FD01 | call qword ptr ds:[<&Warning>] |
00007FFA6339F23C | 48:8B0D 653D3004 | mov rcx,qword ptr ds:[7FFA676A2FA8] | <--- dashboard
00007FFA6339F243 | 48:85C9 | test rcx,rcx |
00007FFA6339F246 | 74 1C | je client.7FFA6339F264 |
00007FFA6339F248 | 48:8B49 30 | mov rcx,qword ptr ds:[rcx+30] |
00007FFA6339F24C | 48:85C9 | test rcx,rcx |
00007FFA6339F24F | 74 13 | je client.7FFA6339F264 |
00007FFA6339F251 | 4C:8D05 381AB202 | lea r8,qword ptr ds:[7FFA65EC0C90] | 00007FFA65EC0C90:"#custom_game_mismatch_desc"
00007FFA6339F258 | 48:8D15 511AB202 | lea rdx,qword ptr ds:[7FFA65EC0CB0] | 00007FFA65EC0CB0:"#custom_game_mismatch_title"
00007FFA6339F25F | E8 EC1430FF | call client.7FFA626A0750 |
00007FFA6339F264 | 48:837C24 50 00 | cmp qword ptr ss:[rsp+50],0 |
*/
auto dashboard = *(CPanel2D**)(client + 0x54a2fa8);
if (!dashboard)
{
OutputDebugStringA("failed to get dashboard");
}
auto dashboard_ui = dashboard->uipanel();
if (!dashboard)
{
OutputDebugStringA("failed to get dashboard_ui");
}
auto dashboard_center = dashboard_ui->FindDirectChild("DashboardCenter");
if (!dashboard)
{
OutputDebugStringA("failed to get DashboardCenter");
}
auto popup_manager = dashboard_center->FindDirectChild("DashboardPopupManager");
if (!dashboard)
{
OutputDebugStringA("failed to get DashboardPopupManager");
}
for (auto child : popup_manager->children())
{
auto const id = child->id();
if (id != "SettingsReborn" && id != "BlurBackground" && id != "DimBackground")
{
auto const match_popup = child->FindDFS("PopupAcceptDeclineMatchPanel");
if (match_popup != nullptr)
{
//inside is string xref "Panorama Symbol table %d filled, couldn't add new symbol '%s'"
auto MakeSymbol = (std::uint16_t(*)(void*, char const*))(panorama + 0x956e0);
if (
!child->BHasClass(MakeSymbol(nullptr, "ReadyUpPlayersVisible"))
&& !child->BHasClass(MakeSymbol(nullptr, "MatchAcceptedClicked"))
&& !child->BHasClass(MakeSymbol(nullptr, "Hidden"))
)
{
OutputDebugStringA("found PopupAcceptDeclineMatchPanel!");
struct unk_
{
CPanel2D* panel;
void* fn;
};
unk_ unk{ child->panel2d(), client + 0x2101490 };
//inside is string xref "ui.matchmaking_accept"
((void(*)(CPanel2D*, unk_*))(client + 0x2101490))(child->panel2d(), &unk);
return TRUE;
}
}
}
}
OutputDebugStringA("didn't find PopupAcceptDeclineMatchPanel");
}
return TRUE;
}
is there a simpler solution? i know that u have pasted the C++ code but i found another approach here comes the solution i found
and i have made quick research about the accept event i found out that i can use the SteamGC to listen on the
hkRetrieveMessage and if the msgId is equal to k_EMsgGCReadyUpStatus that means that the game is found and we can then use the
hkSendMessage to send k_EMsgGCReadyUp is that correct or did i get it incorrect like i always do ? :D
is there a simpler solution? i know that u have pasted the C++ code but i found another approach here comes the solution i found
and i have made quick research about the accept event i found out that i can use the SteamGC to listen on the
hkRetrieveMessage and if the msgId is equal to k_EMsgGCReadyUpStatus that means that the game is found and we can then use the
hkSendMessage to send k_EMsgGCReadyUp is that correct or did i get it incorrect like i always do ? :D
the solution i provided from coding perspective you are right its not simple but as structure and listeners to listen on all messages sent to u by the server and map it in enum EDOTAGCMsg and reply with the same enum events i think it gonna be helping in cases like accept match , party invites , guild invites , etc ...
but is it going to work fine though ? iam trying to collect ur insight about it cuz u are the expert here , also i dont know how can i test it because i need the accept the game to appear more than one to test things
the solution i provided from coding perspective you are right its not simple but as structure and listeners to listen on all messages sent to u by the server and map it in enum EDOTAGCMsg and reply with the same enum events i think it gonna be helping in cases like accept match , party invites , guild invites , etc ...
but is it going to work fine though ? iam trying to collect ur insight about it cuz u are the expert here , also i dont know how can i test it because i need the accept the game to appear more than one to test things
gamecoordinator is tenfold more complicated than wrappers(and other high-level things) over it, and if you're already having trouble with high-level constructs, then you're going to have 10x more trouble with lower-level constructs
try creating a localhost custom game lobby(with a password so that random people don't get in the way of your testing) - when you click "start game" in the lobby the accept-or-decline-match-popup will appear, you can test with that, as many times as you want without penalties
gamecoordinator is tenfold more complicated than wrappers(and other high-level things) over it, and if you're already having trouble with high-level constructs, then you're going to have 10x more trouble with lower-level constructs
try creating a localhost custom game lobby(with a password so that random people don't get in the way of your testing) - when you click "start game" in the lobby the accept-or-decline-match-popup will appear, you can test with that, as many times as you want without penalties
thanks a lot dude i really appreciate the time and effort u put in this forum i swear dude i was in really rough place but u put me on the correct path , again thanks a lot.