From 636d36d783059d9585cf142e4f53b77420ec5136 Mon Sep 17 00:00:00 2001 From: do-gamer <208613570+do-gamer@users.noreply.github.com> Date: Sun, 15 Feb 2026 02:27:43 +0700 Subject: [PATCH 1/7] Fix MethodInfo::name to detect getter/setter traits and return accessor-prefixed names (#1) * Fixed method signature formatting to preserve accessor names * Update submodule URL for third_party/subhook to point to Dasharo mirror repository * Fix MethodInfo::name to detect getter/setter traits and return accessor-prefixed names * Removed spaces --- .gitmodules | 2 +- do_lib/avm.cpp | 141 +++++++++++++++++++++++++++++++++++++++++++++++++ do_lib/avm.h | 6 +-- 3 files changed, 144 insertions(+), 5 deletions(-) diff --git a/.gitmodules b/.gitmodules index ca68aaa..a908805 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,3 @@ [submodule "third_party/subhook"] path = third_party/subhook - url = https://github.com/Zeex/subhook.git + url = https://github.com/Dasharo/subhook.git diff --git a/do_lib/avm.cpp b/do_lib/avm.cpp index f22de70..631c598 100755 --- a/do_lib/avm.cpp +++ b/do_lib/avm.cpp @@ -1,6 +1,8 @@ #include "avm.h" #include "binary_stream.h" #include "utils.h" +#include +#include avm::ClassClosure * avm::AbcEnv::finddef(const std::string &name) { @@ -37,6 +39,138 @@ avm::ClassClosure * avm::AbcEnv::finddef(std::function cache; + std::lock_guard lock(cache_mutex); + auto [it, inserted] = cache.try_emplace(this); + if (!inserted) + { + return it->second; + } + + std::string method_name = pool->get_method_name(id); + std::string &resolved_name = it->second; + resolved_name = method_name; + + if (!method_name.empty() && declarer.is_traits()) + { + auto *traits = declarer.traits(); + if (traits && traits->traits_pos) + { + BinaryStream s { traits->traits_pos }; + uint32_t trait_count = 0; + + switch (traits->pos_type) + { + case 0: // instance_info + { + /* name */ s.read_u32(); + /* super_name */ s.read_u32(); + + auto flags = s.read_u32(); + if ((flags & 0x8) != 0) + { + /* protected_ns */ s.read_u32(); + } + + auto interface_count = s.read_u32(); + for (uint32_t i = 0; i < interface_count; i++) + { + /* interface */ s.read_u32(); + } + + /* iinit */ s.read_u32(); + trait_count = s.read_u32(); + break; + } + case 1: // class_info + case 2: // script_info + { + /* cinit/init */ s.read_u32(); + trait_count = s.read_u32(); + break; + } + default: + { + break; + } + } + + for (uint32_t j = 0; j < trait_count; j++) + { + /* name */ s.read_u32(); + unsigned char tag = s.read(); + int kind = (tag & avm::TRAIT_mask); + + switch(kind) + { + case avm::TRAIT_Slot: + case avm::TRAIT_Const: + { + /* slot_id */ s.read_u32(); + /* type_name */ s.read_u32(); + uint32_t vindex = s.read_u32(); + if (vindex) + { + /* vkind */ s.read(); + } + break; + } + case avm::TRAIT_Class: + case avm::TRAIT_Function: + { + /* slot_id */ s.read_u32(); + /* class/function index */ s.read_u32(); + break; + } + case avm::TRAIT_Method: + case avm::TRAIT_Getter: + case avm::TRAIT_Setter: + { + /* disp_id */ s.read_u32(); + uint32_t method_index = s.read_u32(); + + if (static_cast(method_index) == id) + { + switch (kind) + { + case avm::TRAIT_Setter: + resolved_name = "set " + method_name; + break; + case avm::TRAIT_Getter: + resolved_name = "get " + method_name; + break; + default: + break; + } + + return resolved_name; + } + break; + } + default: + { + break; + } + } + + if (tag & avm::ATTR_metadata) + { + uint32_t metadata_count = s.read_u32(); + for (uint32_t i = 0; i < metadata_count; i++) + { + /* metadata index */ s.read_u32(); + } + } + } + } + } + + return resolved_name; +} + avm::MyTraits avm::Traits::parse_traits(avm::PoolObject *custom_pool) { BinaryStream s { traits_pos }; @@ -100,6 +234,13 @@ avm::MyTraits avm::Traits::parse_traits(avm::PoolObject *custom_pool) trait.id = class_index; break; } + case avm::TRAIT_Function: + { + /* uint32_t slot_id = */ s.read_u32(); + uint32_t function_index = s.read_u32(); + trait.id = function_index; + break; + } case avm::TRAIT_Method: case avm::TRAIT_Getter: case avm::TRAIT_Setter: diff --git a/do_lib/avm.h b/do_lib/avm.h index 3ca25ca..1ecbda2 100755 --- a/do_lib/avm.h +++ b/do_lib/avm.h @@ -39,6 +39,7 @@ namespace avm TRAIT_Getter = 0x02, TRAIT_Setter = 0x03, TRAIT_Class = 0x04, + TRAIT_Function = 0x05, TRAIT_Const = 0x06, TRAIT_COUNT = TRAIT_Const+1, TRAIT_mask = 15 @@ -469,10 +470,7 @@ namespace avm return result; } - std::string name() - { - return pool->get_method_name(id); - } + std::string name(); inline bool compiled() { return ((flags >> 21) & 1) == 1; From 5511a9024231f041fb457393710c97662110a008 Mon Sep 17 00:00:00 2001 From: do-gamer <208613570+do-gamer@users.noreply.github.com> Date: Sun, 15 Feb 2026 19:46:03 +0700 Subject: [PATCH 2/7] Fixed browser hide/show handling via X11 (#2) --- client/CMakeLists.txt | 4 + client/bot_client.cpp | 269 +++++++++++++++++++++++++++- client/bot_client.h | 1 + client/eu_darkbot_api_DarkTanos.cpp | 4 +- 4 files changed, 271 insertions(+), 7 deletions(-) diff --git a/client/CMakeLists.txt b/client/CMakeLists.txt index 650524e..03d5702 100644 --- a/client/CMakeLists.txt +++ b/client/CMakeLists.txt @@ -11,4 +11,8 @@ add_library(${PROJECT_NAME} SHARED sock_ipc.cpp ) +find_package(X11 REQUIRED) +target_include_directories(${PROJECT_NAME} PRIVATE ${X11_INCLUDE_DIR}) +target_link_libraries(${PROJECT_NAME} PRIVATE ${X11_LIBRARIES}) + target_compile_options(${PROJECT_NAME} PRIVATE -std=c++17) diff --git a/client/bot_client.cpp b/client/bot_client.cpp index d10c8fd..85d0985 100644 --- a/client/bot_client.cpp +++ b/client/bot_client.cpp @@ -1,9 +1,7 @@ #include "bot_client.h" #include -#include -#include -#include -#include +#include +#include #include "utils.h" #include "proc_util.h" @@ -16,9 +14,251 @@ #include #include +#include +#include + #define MEM_SIZE 1024 +namespace +{ + Window browser_window = 0; + + bool get_window_pid(Display *display, Window window, pid_t &pid) + { + Atom atom_pid = XInternAtom(display, "_NET_WM_PID", True); + if (atom_pid == None) + { + return false; + } + + Atom actual_type = None; + int actual_format = 0; + unsigned long nitems = 0; + unsigned long bytes_after = 0; + unsigned char *prop = nullptr; + + int status = XGetWindowProperty( + display, + window, + atom_pid, + 0, + 1, + False, + XA_CARDINAL, + &actual_type, + &actual_format, + &nitems, + &bytes_after, + &prop); + + if (status != Success || !prop || nitems == 0) + { + if (prop) + { + XFree(prop); + } + return false; + } + + pid = static_cast(*reinterpret_cast(prop)); + XFree(prop); + return true; + } + + bool is_browser_window_pid(pid_t owner_pid, pid_t browser_pid) + { + return owner_pid == browser_pid || ProcUtil::IsChildOf(owner_pid, browser_pid); + } + + bool x11_window_control_available() + { + const char *display = std::getenv("DISPLAY"); + return display && *display; + } + + bool try_get_window_attrs(Display *display, Window window) + { + XWindowAttributes attrs; + if (XGetWindowAttributes(display, window, &attrs) != 0) + { + return true; + } + + XSync(display, False); + return XGetWindowAttributes(display, window, &attrs) != 0; + } + + Window find_toplevel_root_child(Display *display, Window root, Window window) + { + if (!window) + { + return 0; + } + + Window current = window; + while (current) + { + Window root_return = 0; + Window parent_return = 0; + Window *children = nullptr; + unsigned int nchildren = 0; + + if (!XQueryTree(display, current, &root_return, &parent_return, &children, &nchildren)) + { + return current; + } + + if (children) + { + XFree(children); + } + + if (parent_return == 0 || parent_return == root) + { + return current; + } + + current = parent_return; + } + + return window; + } + + Window find_browser_owned_descendant_recursive(Display *display, Window root, pid_t browser_pid) + { + if (!root) + { + return 0; + } + + pid_t owner_pid = -1; + if (get_window_pid(display, root, owner_pid) && is_browser_window_pid(owner_pid, browser_pid)) + { + return root; + } + + Window root_return = 0; + Window parent_return = 0; + Window *children = nullptr; + unsigned int nchildren = 0; + + if (!XQueryTree(display, root, &root_return, &parent_return, &children, &nchildren)) + { + return 0; + } + + Window found = 0; + for (unsigned int i = 0; i < nchildren && !found; i++) + { + found = find_browser_owned_descendant_recursive(display, children[i], browser_pid); + } + + if (children) + { + XFree(children); + } + return found; + } + + Window find_browser_client_window(Display *display, pid_t browser_pid) + { + Window root = DefaultRootWindow(display); + Atom atom_client_list = XInternAtom(display, "_NET_CLIENT_LIST", True); + if (atom_client_list != None) + { + Atom actual_type = None; + int actual_format = 0; + unsigned long nitems = 0; + unsigned long bytes_after = 0; + unsigned char *prop = nullptr; + + int status = XGetWindowProperty( + display, + root, + atom_client_list, + 0, + 4096, + False, + XA_WINDOW, + &actual_type, + &actual_format, + &nitems, + &bytes_after, + &prop); + + if (status == Success && prop && actual_type == XA_WINDOW) + { + Window *windows = reinterpret_cast(prop); + Window child_fallback = 0; + for (unsigned long i = 0; i < nitems; i++) + { + pid_t owner_pid = -1; + if (!get_window_pid(display, windows[i], owner_pid)) + { + continue; + } + + if (owner_pid == browser_pid) + { + XFree(prop); + return windows[i]; + } + + if (!child_fallback && ProcUtil::IsChildOf(owner_pid, browser_pid)) + { + child_fallback = windows[i]; + } + } + + XFree(prop); + if (child_fallback) + { + return child_fallback; + } + } + else if (prop) + { + XFree(prop); + } + } + + Window any_owned = find_browser_owned_descendant_recursive(display, root, browser_pid); + if (!any_owned) + { + return 0; + } + + return find_toplevel_root_child(display, root, any_owned); + } + + void toggle_browser_visibility(pid_t browser_pid, bool visible) + { + Display *display = XOpenDisplay(nullptr); + if (!display) + { + return; // Failed to open display + } + + if (!browser_window || !try_get_window_attrs(display, browser_window)) + { + browser_window = find_browser_client_window(display, browser_pid); + } + + if (!browser_window) + { + XCloseDisplay(display); + return; // Failed to find browser window + } + + // Show or hide the browser window + visible ? XMapWindow(display, browser_window) : XUnmapWindow(display, browser_window); + + XFlush(display); + XCloseDisplay(display); + } +} + enum class MessageType { @@ -125,6 +365,16 @@ BotClient::BotClient() : { } +void BotClient::ToggleBrowserVisibility(bool visible) +{ + if (m_flash_pid == -1 || !x11_window_control_available()) + { + return; + } + + toggle_browser_visibility(m_browser_pid, visible); +} + BotClient::~BotClient() { if (m_browser_pid > 0) @@ -177,7 +427,16 @@ void BotClient::LaunchBrowser() sid.replace(0, 6, ""); } - execle(fpath, fpath, "--sid", sid.c_str(), "--url", url.c_str(), "--launch", NULL, envp.data()); + execle( + fpath, + fpath, + "--sid", sid.c_str(), + "--url", url.c_str(), + "--launch", + "--disable-background-timer-throttling", + "--disable-renderer-backgrounding", + NULL, + envp.data()); break; } default: diff --git a/client/bot_client.h b/client/bot_client.h index b677bb8..4b28067 100644 --- a/client/bot_client.h +++ b/client/bot_client.h @@ -27,6 +27,7 @@ class BotClient bool IsValid(); void SendBrowserCommand(const std::string &&s, int sync); + void ToggleBrowserVisibility(bool visible); void SendFlashCommand(Message *message, Message *response = nullptr); diff --git a/client/eu_darkbot_api_DarkTanos.cpp b/client/eu_darkbot_api_DarkTanos.cpp index 4c00f5a..f17bbd2 100644 --- a/client/eu_darkbot_api_DarkTanos.cpp +++ b/client/eu_darkbot_api_DarkTanos.cpp @@ -38,13 +38,13 @@ JNIEXPORT void JNICALL Java_eu_darkbot_api_DarkTanos_setSize JNIEXPORT void JNICALL Java_eu_darkbot_api_DarkTanos_setVisible (JNIEnv *, jobject, jboolean jv) { - client.SendBrowserCommand(utils::format("setVisible|{}", int(jv)), 0); + client.ToggleBrowserVisibility(jv); } JNIEXPORT void JNICALL Java_eu_darkbot_api_DarkTanos_setMinimized (JNIEnv *, jobject, jboolean jv) { - client.SendBrowserCommand(utils::format("minimize|{}", int(jv)), 0); + client.ToggleBrowserVisibility(!jv); } JNIEXPORT void JNICALL Java_eu_darkbot_api_DarkTanos_reload From 83fa2b80cb7a0cc5b96916bba87fb44e4837ed4d Mon Sep 17 00:00:00 2001 From: do-gamer <208613570+do-gamer@users.noreply.github.com> Date: Sun, 15 Feb 2026 20:15:31 +0700 Subject: [PATCH 3/7] Bump version to 7 --- client/eu_darkbot_api_DarkTanos.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/eu_darkbot_api_DarkTanos.cpp b/client/eu_darkbot_api_DarkTanos.cpp index f17bbd2..38de4ee 100644 --- a/client/eu_darkbot_api_DarkTanos.cpp +++ b/client/eu_darkbot_api_DarkTanos.cpp @@ -72,7 +72,7 @@ JNIEXPORT jlong JNICALL Java_eu_darkbot_api_DarkTanos_getMemoryUsage JNIEXPORT jint JNICALL Java_eu_darkbot_api_DarkTanos_getVersion (JNIEnv *, jobject) { - return 6; + return 7; } JNIEXPORT void JNICALL Java_eu_darkbot_api_DarkTanos_keyClick From 7a9563ec0f032c7468380aaf2d95c0a9661eb1da Mon Sep 17 00:00:00 2001 From: do-gamer <208613570+do-gamer@users.noreply.github.com> Date: Sun, 15 Feb 2026 23:57:06 +0700 Subject: [PATCH 4/7] Make API version configurable via CMake --- CMakeLists.txt | 2 ++ client/CMakeLists.txt | 2 ++ client/eu_darkbot_api_DarkTanos.cpp | 2 +- 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 3ddeff9..3cb143b 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,6 +2,8 @@ cmake_minimum_required(VERSION 3.10) project (DarkTanos) +set(API_VERSION 7) + set(CMAKE_CXX_STANDARD 17) set(CMAKE_BUILD_TYPE Debug) set(CMAKE_POSITION_INDEPENDENT_CODE ON) diff --git a/client/CMakeLists.txt b/client/CMakeLists.txt index 03d5702..d31122e 100644 --- a/client/CMakeLists.txt +++ b/client/CMakeLists.txt @@ -4,6 +4,8 @@ find_package(JNI REQUIRED) include_directories(${PROJECT_NAME} PRIVATE ${JNI_INCLUDE_DIRS}) +add_definitions(-DAPI_VERSION=${API_VERSION}) + add_library(${PROJECT_NAME} SHARED eu_darkbot_api_DarkTanos.cpp bot_client.cpp diff --git a/client/eu_darkbot_api_DarkTanos.cpp b/client/eu_darkbot_api_DarkTanos.cpp index 38de4ee..3927796 100644 --- a/client/eu_darkbot_api_DarkTanos.cpp +++ b/client/eu_darkbot_api_DarkTanos.cpp @@ -72,7 +72,7 @@ JNIEXPORT jlong JNICALL Java_eu_darkbot_api_DarkTanos_getMemoryUsage JNIEXPORT jint JNICALL Java_eu_darkbot_api_DarkTanos_getVersion (JNIEnv *, jobject) { - return 7; + return API_VERSION; } JNIEXPORT void JNICALL Java_eu_darkbot_api_DarkTanos_keyClick From a97850cdf60a8171a13b54cd823eba9daefec5b3 Mon Sep 17 00:00:00 2001 From: do-gamer <208613570+do-gamer@users.noreply.github.com> Date: Mon, 16 Feb 2026 04:25:43 +0700 Subject: [PATCH 5/7] Add flag to force X11 backend in browser launch --- client/bot_client.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/client/bot_client.cpp b/client/bot_client.cpp index 85d0985..92ef718 100644 --- a/client/bot_client.cpp +++ b/client/bot_client.cpp @@ -433,6 +433,7 @@ void BotClient::LaunchBrowser() "--sid", sid.c_str(), "--url", url.c_str(), "--launch", + "--ozone-platform=x11", "--disable-background-timer-throttling", "--disable-renderer-backgrounding", NULL, From 5305f6ed4eb15ec0b50efc14ed02831d9f10ce24 Mon Sep 17 00:00:00 2001 From: do-gamer <208613570+do-gamer@users.noreply.github.com> Date: Mon, 16 Feb 2026 05:52:18 +0700 Subject: [PATCH 6/7] cleanup and remove redundant code --- client/bot_client.cpp | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/client/bot_client.cpp b/client/bot_client.cpp index 92ef718..bcc08ea 100644 --- a/client/bot_client.cpp +++ b/client/bot_client.cpp @@ -509,7 +509,6 @@ void BotClient::reset() m_flash_pid = -1; m_flash_sem = -1; m_flash_shmid = -1; - m_flash_pid = -1; } // Not a great name since it has side-effects like refreshgin or restarting the browser @@ -545,12 +544,10 @@ void BotClient::SendFlashCommand(Message *message, Message *response) return; } + if ((m_flash_shmid = shmget(m_flash_pid, MEM_SIZE, IPC_CREAT | 0666)) < 0) { - if ((m_flash_shmid = shmget(m_flash_pid, MEM_SIZE, IPC_CREAT | 0666)) < 0) - { - fprintf(stderr, "[SendFlashCommand] Failed to get shared memory\n"); - return; - } + fprintf(stderr, "[SendFlashCommand] Failed to get shared memory\n"); + return; } if (!m_shared_mem_flash || m_shared_mem_flash == (void *)-1) @@ -579,7 +576,6 @@ void BotClient::SendFlashCommand(Message *message, Message *response) sembuf sop[2] { { 0, -1, 0 }, { 1, 0, 0 } }; // Notify - sop[0] = { 0, -1, 0 }; if (semtimedop(m_flash_sem, &sop[0], 1, &timeout) == -1) { if (errno == EAGAIN) @@ -592,7 +588,6 @@ void BotClient::SendFlashCommand(Message *message, Message *response) } // Wait - sop[1] = { 1, 0, 0 }; if (semtimedop(m_flash_sem, &sop[1], 1, &timeout) == -1) { if (errno == EAGAIN) From 34c0663a32de1637b0aa3c2083ae5790aa7e9161 Mon Sep 17 00:00:00 2001 From: do-gamer <208613570+do-gamer@users.noreply.github.com> Date: Mon, 16 Feb 2026 12:50:25 +0700 Subject: [PATCH 7/7] Added comment explaining why the same approach is used for minimize to avoid lag --- client/eu_darkbot_api_DarkTanos.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/client/eu_darkbot_api_DarkTanos.cpp b/client/eu_darkbot_api_DarkTanos.cpp index 3927796..3d671f7 100644 --- a/client/eu_darkbot_api_DarkTanos.cpp +++ b/client/eu_darkbot_api_DarkTanos.cpp @@ -44,6 +44,8 @@ JNIEXPORT void JNICALL Java_eu_darkbot_api_DarkTanos_setVisible JNIEXPORT void JNICALL Java_eu_darkbot_api_DarkTanos_setMinimized (JNIEnv *, jobject, jboolean jv) { + // Using the same hiding approach as with "setVisible", since minimizing causes lags and increases the tick. + // The boolean "jv" is inverted because "setMinimized(true)" should hide the window. client.ToggleBrowserVisibility(!jv); }