From b3ebc25a43c42af662d44b70a9d119038c08460a Mon Sep 17 00:00:00 2001 From: Debugger <30207948+Sys0xdbg@users.noreply.github.com> Date: Sat, 19 Sep 2026 18:08:00 -0400 Subject: [PATCH 1/2] Polish Nenyoo documentation --- Nenyoo/README.md | 22 +++++++++++++++------- Nenyoo/TESTING.md | 30 ------------------------------ README.md | 5 ++--- 3 files changed, 17 insertions(+), 40 deletions(-) delete mode 100644 Nenyoo/TESTING.md diff --git a/Nenyoo/README.md b/Nenyoo/README.md index 711d401..300079b 100644 --- a/Nenyoo/README.md +++ b/Nenyoo/README.md @@ -1,13 +1,21 @@ -# Ultimate Menu for Nenyoo (GTA V Legacy and Enhanced) +# Ultimate Menu for Nenyoo -This edition runs the YimMenu Legacy script through a Nenyoo API adapter on GTA V Legacy and Enhanced. The menu hierarchy and action callbacks come from the YimMenu source. YimMenu tabs, controls, and popups appear as nested Nenyoo menu lists. Informational text and live status appear in a companion panel only while their submenu is open; hover the panel and use the mouse wheel for longer instructions. Controls that depend on game state are added when their conditions become true. +Ultimate Menu runs in Nenyoo on GTA V Legacy and Enhanced with the same menu structure and controls as the YimMenu edition. -Copy the contents of this folder to `%LOCALAPPDATA%\Nenyoo\Plus\Scripts\User\UltimateMenu\`, then select `Ultimate_Menu.lua` from Nenyoo's Scripts menu to load its page. Keep the `lib` folder beside the entry script. The script selects the matching address catalog using Nenyoo's game-edition API. The included catalogs target GTA V Legacy build 3889 and Enhanced build 1158.13. Use the online menus only after joining GTA Online. +## Install -When a GTA update changes script addresses, update the named entries in `lib/game_offsets_legacy.offsets` or `lib/game_offsets_enhanced.offsets`. The `.offsets` files contain Lua-readable data but do not appear as runnable Lua scripts. Each catalog separates `globals`, `locals`, and script names and keeps dynamic address formulas beside their bases. `lib/game_offsets.offsets` only detects the edition and loads the appropriate catalog. +1. Copy this folder to `%LOCALAPPDATA%\Nenyoo\Plus\Scripts\User\UltimateMenu\`. +2. Keep `Ultimate_Menu.lua` and the `lib` folder together. +3. In Nenyoo, open **Scripts** and select `Ultimate_Menu.lua`. -The adapter requires a Nenyoo build exposing `ctx.edition`, `__ny_execute_as_script`, and `__ny_request_script_host`. The host request uses Nenyoo's cooperative script-host election and cannot evict a healthy current host. Actions that rely on becoming mission-script host therefore need in-game validation on each edition. +Online features become available after joining GTA Online. -The mock-runtime check in `lib/tests/load_yim_test.lua` covers both edition catalogs, menu construction, all exposed action and input callbacks, stat and native return bridges, and controls that appear after a game-state change. Run it once normally and once with `UM_EDITION=Enhanced` using Lua 5.4 from the repository root. It does not validate GTA Online results or the visual menu in a running game. +## Supported builds -Follow [TESTING.md](TESTING.md) for the in-game checks and a failure-report template. +- GTA V Legacy 3889 +- GTA V Enhanced 1158.13 + +The correct offset catalog is selected automatically. Build-specific globals and locals are stored in: + +- `lib/game_offsets_legacy.offsets` +- `lib/game_offsets_enhanced.offsets` diff --git a/Nenyoo/TESTING.md b/Nenyoo/TESTING.md deleted file mode 100644 index 4ceed7e..0000000 --- a/Nenyoo/TESTING.md +++ /dev/null @@ -1,30 +0,0 @@ -# Nenyoo Legacy and Enhanced in-game verification - -Run this pass separately on GTA V **Legacy build 3889** and **Enhanced build 1158.13** with the Nenyoo DLL that exposes `ctx.edition`, `__ny_execute_as_script`, and `__ny_request_script_host`. The package must be at `%LOCALAPPDATA%\Nenyoo\Plus\Scripts\User\UltimateMenu\` with `Ultimate_Menu.lua`, the three `lib\game_offsets*.offsets` files, and `lib\yim_compat.lua` beside `lib\Ultimate_Menu_Legacy.lua`. Open `Ultimate_Menu.lua` from Nenyoo's Scripts menu; it is a menu page that loads when opened. Confirm its heading says `Legacy` or `Enhanced` to match the running game before testing any action. - -## First run - -1. In Story Mode, open the page. Confirm `Ultimate Menu 1.73 By L7NEG` appears with `Story Mode`, `Self Menu`, `Recovery Menu`, `Online Services Menu`, `Collectibles`, `Events Menu`, `Gun Van Menu`, `YimResupplier`, and `Heists Data Editor Menu`. Open and back out of several nested lists. Pages with explanations should show a companion information panel; the panel should close when leaving the page. Hover and scroll the panel to read long text. Record any missing row, empty page, freeze, or Lua error. -2. In `Story Mode`, record the current character's cash, use `Current Character Cash` to set a distinct value, and confirm the game shows it. Restore the original value. Online-only pages should show `Unavailable in Single Player.` in the information panel rather than actionable controls. -3. Join a GTA Online session and reopen the menu. Verify changing text, toggle, slider, combo, and button controls update the displayed value or game state. Leave the session and confirm Story Mode controls still work. -4. In `Gun Van Menu > Weapons > Edit Weapons`, search for `Pistol`, select its row, and use `Set Weapon` on a recorded slot. Check that the displayed slot and the in-game Gun Van show Pistol. Restore the original weapon. Test `Remove Weapon` and `Close`, then reopen the editor. -5. For a business you own, open `YimResupplier` and verify its controls appear after ownership is detected. Test a teleport to a known property and confirm the landing position. Repeat after changing sessions. - -## Full feature pass - -The mock check inventories over a thousand actions; opening their rows does not prove their effects. For a full claim, test **each** action, toggle, input, slider, and choice under the game conditions it requires. Cover `Self Menu`, `Story Mode`, `Recovery Menu` (rank, skills, awards, account unlocks, LSCM), `Online Services Menu`, `Collectibles`, `Events Menu`, `Ultimate Money Methods Menu` (businesses and warehouse), `Gun Van Menu`, `YimResupplier`, `Reports Menu`, `Heists Data Editor Menu` (Apartment, Agency, Autoshop, Salvage Yard, Cluckin Bell, Doomsday, Casino, Cayo, Kortz), and `Missions Selector And cooldown Menu`. `Credits` needs only a display check. - -Record the exact menu path and control label, session type and relevant property/mission ownership, value before, chosen action, value after, whether it persisted after a session change, and pass/fail. Mark unavailable conditions as **untested**, not passed. Save a screenshot or short recording for visual failures, and the relevant lines from `%LOCALAPPDATA%\Nenyoo\Plus\nenyoo.log` for errors. Some heist and mission actions request script host; test them while the named mission script is active and record whether host was actually acquired. A healthy current host cannot be displaced by Nenyoo's cooperative request, so those effects are not guaranteed in every session. - -Report the first failure with this format: - -``` -GTA build / Nenyoo DLL build: -Session and mission/property state: -Menu path > exact control: -Value before / value entered / value after: -What happened instead: -Error text or log lines (if any): -``` - -Do not mark either edition fully verified until every applicable control has an observed result on that edition. The automated Lua mock and DLL build cover loading and API calls, not GTA Online outcomes. diff --git a/README.md b/README.md index 3dbf858..f72ac19 100644 --- a/README.md +++ b/README.md @@ -50,8 +50,7 @@ Coming Soon (WIP) - For **Nenyoo GTA V Legacy or Enhanced**: Copy the contents of [`Nenyoo`](Nenyoo) to `%LOCALAPPDATA%\Nenyoo\Plus\Scripts\User\UltimateMenu\`. In Nenyoo, open `Scripts` and select `Ultimate_Menu.lua` to load its menu page. The `lib` folder must stay beside the entry script. Join GTA Online before using online actions. - - The Nenyoo edition translates the YimMenu menu hierarchy and controls, with instructions and live status in a companion information panel beside the active submenu. It selects separate named global and script-local catalogs for Legacy build 3889 and Enhanced build 1158.13. It requires a Nenyoo build with the edition, named-script execution, and host-request Lua bridges. Follow [`Nenyoo/TESTING.md`](Nenyoo/TESTING.md) to validate actions in game. + Supported builds: Legacy 3889 and Enhanced 1158.13. The matching offsets are selected automatically. --- @@ -61,7 +60,7 @@ - **Ultimate Menu for ModestMenu**: Last updated on **Dec 12, 2024** *(Final Update)* - **Ultimate Menu for YimMenu Legacy**: Last updated on **Jul 24, 2026** - **Ultimate Menu for YimMenu Enhanced**: **Might Not Coming Soon (WIP Waiting for completion of YimMenuV2 Lua API)** -- **Ultimate Menu for Nenyoo GTA V Legacy and Enhanced**: complete menu adapter with edition-specific offsets; full in-game validation pending +- **Ultimate Menu for Nenyoo**: Legacy and Enhanced support --- From e6d5d3a245939e86839f18a26e9d0c629ef76929 Mon Sep 17 00:00:00 2001 From: Debugger <30207948+Sys0xdbg@users.noreply.github.com> Date: Sat, 19 Sep 2026 18:09:49 -0400 Subject: [PATCH 2/2] Remove Nenyoo development tests --- Nenyoo/lib/tests/load_yim_test.lua | 481 ----------------------------- Nenyoo/lib/tests/lua_runner.c | 18 -- 2 files changed, 499 deletions(-) delete mode 100644 Nenyoo/lib/tests/load_yim_test.lua delete mode 100644 Nenyoo/lib/tests/lua_runner.c diff --git a/Nenyoo/lib/tests/load_yim_test.lua b/Nenyoo/lib/tests/load_yim_test.lua deleted file mode 100644 index 922cb25..0000000 --- a/Nenyoo/lib/tests/load_yim_test.lua +++ /dev/null @@ -1,481 +0,0 @@ --- Mock-runtime inventory check for the unmodified YimMenu script. -local next_ref, counters = 0, {list = 0, action = 0, toggle = 0, text = 0} -local actions, toggles, inputs, sliders, native_calls, list_names = {}, {}, {}, {}, {}, {} -local threads_created = 0 -local thread_callbacks = {} -local thread_origins = {} -local menu_back_calls = 0 -local list_refs, current_page_id, panel_draw, drawn_text, menu_visible = {}, 0, nil, {}, true -local memory_values = {} -local last_pointer -local function ref() - next_ref = next_ref + 1 - return {id = next_ref} -end -menu = { - my_root = ref, - list = function(_, label) - counters.list = counters.list + 1 - list_names[label] = true - local result = ref() - list_refs[label] = result - return result - end, - action = function(_, label, _, _, callback) - counters.action = counters.action + 1 - actions[#actions + 1] = {label = label, callback = callback} - return ref() - end, - toggle = function(_, label, _, _, callback, default) - counters.toggle = counters.toggle + 1 - toggles[#toggles + 1] = {label = label, callback = callback, default = default} - return ref() - end, - slider = function(_, label, _, _, _, _, _, _, callback) - sliders[#sliders + 1] = {label = label, callback = callback} - return ref() - end, - readonly = function() counters.text = counters.text + 1; return ref() end, - divider = function() return ref() end, - is_visible = function() return menu_visible end, - page_id = function() return current_page_id end, - bounds = function() return 100, 80, 420 end, - content_rect = function() return 100, 250, 420, 440 end, - get_value = function() return false end, - set_value = function() end, - get_menu_name = function() return "" end, - set_menu_name = function() end, - set_help_text = function() end, - go_back = function() menu_back_calls = menu_back_calls + 1 end, -} -__smenu = { - text_input = function(_, label, _, _, callback) - inputs[#inputs + 1] = {label = label, callback = callback} - return ref().id - end, - node_in_viewport = function() return true end, -} -util = { - joaat = function(name) - local page = tostring(name):match("^Stand#(%d+)$") - return page and (100000 + tonumber(page)) or #tostring(name) - end, - toast = function() end, - yield = function() - local _, is_main = coroutine.running() - if not is_main then coroutine.yield() end - end, - create_thread = function(callback) - threads_created = threads_created + 1 - thread_callbacks[#thread_callbacks + 1] = callback - thread_origins[#thread_origins + 1] = debug.traceback("", 2) - return {} - end, -} -overlay = {on_draw = function(name, callback) - assert(name == "ultimate_menu_information", "Unexpected overlay registration") - panel_draw = callback -end} -font = {small = 1, item = 2} -ctx = { - screen_w = function() return 1280 end, - screen_h = function() return 900 end, - edition = function() return os.getenv("UM_EDITION") or "Legacy" end, -} -input = {mouse_x = function() return 0 end, mouse_y = function() return 0 end, - mouse_wheel = function() return 0 end} -theme = {accent = function() return 55, 145, 245 end} -text = { - width = function(_, value) return #tostring(value) * 8 end, - height = function() return 16 end, - draw = function(_, _, _, _, _, _, _, value) drawn_text[#drawn_text + 1] = value end, - draw_ellipsis = function(_, _, _, _, _, _, _, value) drawn_text[#drawn_text + 1] = value end, -} -draw = {rect = function() end, rect_outline = function() end, - push_clip = function() end, pop_clip = function() end} -__ny_execute_as_script = function(_, callback) callback(); return true end -__ny_request_script_host = function() return true end -memory = { - alloc_int = function() return 1 end, - alloc_float = function() return 2 end, - read_int = function(address) return memory_values[address] or 0 end, - write_int = function(address, value) memory_values[address] = value end, - read_float = function() return 0 end, - read_bool = function() return false end, - script_global = function() return 0 end, - script_local = function() return nil end, - tunable = function() return 0 end, -} -local last_hash -local session_started = os.getenv("UM_START_OFFLINE") == nil -local owned_stats = os.getenv("UM_OWNED_STATS") == "1" -native_invoker = { - begin_call = function() end, - push_arg_int = function() end, - push_arg_float = function() end, - push_arg_bool = function() end, - push_arg_pointer = function(address) last_pointer = address end, - end_call = function(hash) - last_hash = hash - native_calls[#native_calls + 1] = hash - if hash == "3C5FD37B5499582E" then memory_values[last_pointer] = 42 end - if hash == "767FBC2AC802EF3D" and owned_stats then - memory_values[last_pointer] = 1 - end - end, - get_return_value_bool = function() - return (last_hash == "9DE624D2FC4B603F" and session_started) - or last_hash == "3C5FD37B5499582E" - or (last_hash == "767FBC2AC802EF3D" and owned_stats) - or last_hash == "7239B21A38F536BA" - end, - get_return_value_int = function() return 0 end, - get_return_value_float = function() return 0 end, - get_return_value_vector3 = function() return {x = 1, y = 2, z = 3} end, -} -local direct_native_calls = {} -local load_phase = true -for _, group in ipairs({"CLOCK", "CUTSCENE", "ENTITY", "HUD", "INTERIOR", "NETSHOPPING", "PAD", "PED", "PLAYER", "SCRIPT", "STREAMING", "SYSTEM", "TASK"}) do - _G[group] = setmetatable({}, {__index = function(_, name) - return function() - if load_phase then direct_native_calls[group .. "." .. name] = true end - return 0 - end - end}) -end -ENTITY.DOES_ENTITY_EXIST = function() return true end -__stand_get_all = function() return {} end -__stand_get_weapons = function() return {{n = "WEAPON_PISTOL", d = "Pistol"}} end -vec3 = {new = function(_, x, y, z) return {x = x, y = y, z = z} end} -ImGuiWindowFlags = {Modal = 0, NoCollapse = 0, NoMove = 0, NoResize = 0} -log = {info = function() end, warning = function() end, debug = function() end} - -local fake_files = {} -local original_io_open = io.open -io.open = function(name, mode) - if mode == "r" and not fake_files[name] then return nil end - return { - write = function(_, body) fake_files[name] = body end, - read = function() return fake_files[name] end, - close = function() end, - } -end - -local before_load = #native_calls -local legacy_offsets = dofile("Nenyoo/lib/game_offsets_legacy.offsets") -local enhanced_offsets = dofile("Nenyoo/lib/game_offsets_enhanced.offsets") -for _, group_name in ipairs({"globals", "locals", "scripts"}) do - for name in pairs(legacy_offsets[group_name]) do - assert(enhanced_offsets[group_name][name] ~= nil, - "Enhanced catalog is missing " .. group_name .. "." .. name) - end - for name in pairs(enhanced_offsets[group_name]) do - assert(legacy_offsets[group_name][name] ~= nil, - "Legacy catalog is missing " .. group_name .. "." .. name) - end -end -local offset_catalog = dofile("Nenyoo/lib/game_offsets.offsets") -assert(offset_catalog.globals.TRANSACTION_ERROR_GLOBAL_1 == 4516981, "Global offset catalog did not load") -if ctx.edition() == "Enhanced" then - assert(offset_catalog.edition == "Enhanced" and offset_catalog.build == "1158.13", - "Enhanced catalog was not selected") - assert(offset_catalog.globals.ACg1 == 1936408, "Enhanced apartment cut is incorrect") - assert(offset_catalog.globals.CSg1 == 1575048, "Enhanced session type is incorrect") - assert(offset_catalog.locals.AHLIVESL == 28164, "Enhanced apartment lives local is missing") - assert(offset_catalog.globals.APARTMENT_COOLDOWN(0) == 2686124 + 1 + 76, - "Enhanced dynamic global formula is incorrect") - assert(offset_catalog.locals.KORTZ_CUT_GLASS(4) == 32856 + (4 * 13) + 3, - "Enhanced dynamic local formula is incorrect") -else - assert(offset_catalog.edition == "Legacy" and offset_catalog.build == "3889", - "Legacy catalog was not selected") - assert(offset_catalog.locals.AHLIVESL == 26234 + 1325 + 1, "Apartment lives local is missing") - assert(offset_catalog.globals.APARTMENT_COOLDOWN(0) == 2686119 + 1 + 76, - "Dynamic global formula is incorrect") - assert(offset_catalog.locals.KORTZ_CUT_GLASS(4) == 32453 + 1 + (4 * 13) + 3, - "Dynamic local formula is incorrect") -end -local executed_lines = {} -if os.getenv("UM_SCAN_COVERAGE") then - debug.sethook(function(_, line) - local info = debug.getinfo(2, "S") - if info.source and info.source:find("Ultimate_Menu_Legacy.lua", 1, true) then - executed_lines[line] = true - end - end, "l") -end -local compat = dofile("Nenyoo/Ultimate_Menu.lua") -debug.sethook() -load_phase = false -assert(fake_files["Nenyoo/YimResupplier.json"], "Resupplier config was not kept inside its package") -assert(not fake_files["YimResupplier.json"], "Resupplier config leaked into the game directory") -assert(rawget(_G, "stats") == nil, "Package leaked Yim stats into other scripts") -assert(rawget(memory, "scan_pattern") == nil, "Package changed the global memory API") -if os.getenv("UM_SCAN_COVERAGE") then - local file = assert(original_io_open("Nenyoo/lib/Ultimate_Menu_Legacy.lua", "r")) - local line_number = 0 - for line in file:lines() do - line_number = line_number + 1 - if line:find("ImGui%.[A-Za-z_]+%(") and not executed_lines[line_number] then - print("Unmaterialized UI line " .. line_number .. ": " .. line) - end - end - file:close() -end -local permitted_load_natives = { - ["SCRIPT.GET_NUMBER_OF_THREADS_RUNNING_THE_SCRIPT_WITH_THIS_HASH"] = true, - ["CLOCK.GET_CLOCK_MINUTES"] = true, - ["CLOCK.GET_CLOCK_HOURS"] = true, - ["INTERIOR.GET_INTERIOR_FROM_ENTITY"] = true, - ["PLAYER.PLAYER_PED_ID"] = true, -} -for name in pairs(direct_native_calls) do - assert(permitted_load_natives[name], "Menu materialization called unexpected native: " .. name) -end -if os.getenv("UM_SHOW_LOAD_NATIVES") then - for name in pairs(direct_native_calls) do print(name) end -end -for i = before_load + 1, #native_calls do - assert(native_calls[i] ~= "B3271D7AB655B441", "Menu materialization wrote a stat") -end -assert(counters.list >= 60, "Missing tab hierarchy: " .. counters.list) -if not os.getenv("UM_START_OFFLINE") then - assert(counters.action >= 940, "Missing initial actions: " .. counters.action) -end -assert(#compat.imgui_sections >= 43, "Missing ImGui sections: " .. #compat.imgui_sections) -assert(panel_draw, "Information overlay did not register") -if os.getenv("UM_START_OFFLINE") then - local viewport_thread = coroutine.create(thread_callbacks[#thread_callbacks]) - local ok, message = coroutine.resume(viewport_thread) - assert(ok, message) - local recovery_id = util.joaat("Stand#" .. list_refs["Set Rank menu"].id) - local offline_info = compat.info_for_page(recovery_id) - local warning = false - for _, row in ipairs(offline_info.rows) do - if row.text:find("Unavailable in Single Player", 1, true) then warning = true end - end - assert(warning, "Single Player warning did not reach the information panel") - session_started = true - ok, message = coroutine.resume(viewport_thread) - assert(ok, message) - for _, row in ipairs(compat.info_for_page(recovery_id).rows) do - assert(not row.text:find("Unavailable in Single Player", 1, true), - "Single Player warning remained after joining online") - end - assert(counters.action >= 940, "Online controls did not appear after joining: " .. counters.action) -end -local apartment_id = util.joaat("Stand#" .. list_refs["Apartment Heist "].id) -local apartment_info = compat.info_for_page(apartment_id) -assert(apartment_info and #apartment_info.rows >= 5, "Apartment Heist instructions did not reach the panel") -assert(counters.text == 0, "Informational text still occupies selectable menu rows") -local kortz_id = util.joaat("Stand#" .. list_refs["Kortz Center Heist"].id) -local kortz_info = compat.info_for_page(kortz_id) -local colored_status, expanded_help = false, false -for _, row in ipairs(kortz_info.rows) do - if row.color then colored_status = true end - if row.text:find("Select the main painting", 1, true) then expanded_help = true end -end -assert(colored_status, "Colored YimMenu status text lost its color") -assert(expanded_help, "YimMenu help marker was not expanded in the panel") -local tooltip_explanations = { - "Enables the Trick or Treat event", - "Enables the Snowmen Collectibles event", - "Enables the Yeti Hunt event", - "Enables the special liveries for Knife", -} -local found_explanations = {} -for id in pairs(compat.info_pages) do - local page = compat.info_for_page(id) - for _, row in ipairs(page.rows) do - assert(row.text ~= "[?]", "Unexpanded help marker on " .. tostring(page.title)) - for i, explanation in ipairs(tooltip_explanations) do - if row.text:find(explanation, 1, true) then found_explanations[i] = true end - end - end -end -for i, explanation in ipairs(tooltip_explanations) do - assert(found_explanations[i], "Help text did not reach the panel: " .. explanation) -end -for _, action in ipairs(actions) do - assert(action.label ~= "[?]", "Help marker became an action label") -end -for _, input in ipairs(inputs) do - assert(input.label ~= "[?]", "Help marker became an input label") -end -assert(not list_names["[?]"], "Help marker became a submenu label") -current_page_id = apartment_id -panel_draw() -local found_instruction = false -local found_wrapped_instruction = false -for _, value in ipairs(drawn_text) do - if value:find("Pay for the preparation", 1, true) then found_instruction = true end - if value:find("change the session", 1, true) then found_wrapped_instruction = true end -end -assert(found_instruction, "Long heist instructions were not drawn in the information panel") -assert(found_wrapped_instruction, "Long heist instructions were cut off instead of wrapped") -local drawn_count = #drawn_text -current_page_id = 0 -panel_draw() -assert(#drawn_text == drawn_count, "Information panel appeared outside its submenu") -current_page_id = apartment_id -menu_visible = false -panel_draw() -assert(#drawn_text == drawn_count, "Information panel remained after the menu closed") -menu_visible = true -for _, name in ipairs({"Self Menu", "Story Mode", "Recovery Menu", "Online Services Menu", - "Collectibles", "Events Menu", "YimResupplier", "Heists Data Editor Menu", "Kortz Center Heist"}) do - assert(list_names[name], "Missing original menu section: " .. name) -end -local rank_action -for _, action in ipairs(actions) do - if action.label == "Set Rank" then rank_action = action.callback; break end -end -assert(rank_action, "Missing ImGui rank action") -session_started = false -local offline_before = #native_calls -rank_action() -for i = offline_before + 1, #native_calls do - assert(native_calls[i] ~= "B3271D7AB655B441", "Online action changed stats in Story Mode") -end -session_started = true -local before = #native_calls -rank_action() -local stat_writes = 0 -for i = before + 1, #native_calls do - if native_calls[i] == "B3271D7AB655B441" then stat_writes = stat_writes + 1 end -end -assert(stat_writes == 2, "Rank action did not write both stats: " .. stat_writes) -session_started = false -local story_viewport = coroutine.create(thread_callbacks[#thread_callbacks]) -local story_ok, story_error = coroutine.resume(story_viewport) -assert(story_ok, story_error) -session_started = true -local story_cash_action -for _, action in ipairs(actions) do - if action.label == "Add 1 Mil Cash $" then story_cash_action = action.callback; break end -end -assert(story_cash_action, "Missing Story Mode cash action") -before = #native_calls -story_cash_action() -for i = before + 1, #native_calls do - assert(native_calls[i] ~= "B3271D7AB655B441", "Story Mode action changed stats online") -end -session_started = false -before = #native_calls -story_cash_action() -local story_wrote = false -for i = before + 1, #native_calls do - if native_calls[i] == "B3271D7AB655B441" then story_wrote = true end -end -assert(story_wrote, "Story Mode action did not reach the stat bridge offline") -session_started = true -local panther_action -for _, action in ipairs(actions) do - if action.label == "Panther Statue" then panther_action = action.callback; break end -end -assert(panther_action, "Missing Cayo primary target option") -before = #native_calls -panther_action() -local target_written = false -for i = before + 1, #native_calls do - if native_calls[i] == "B3271D7AB655B441" then target_written = true end -end -assert(target_written, "Cayo target option did not reach the stat bridge") -local office_action -for _, action in ipairs(actions) do - if action.label == "Teleport To Office" then office_action = action.callback; break end -end -assert(office_action, "Missing nested YimResupplier action") -before = threads_created -office_action() -assert(threads_created > before, "Nested tab action did not reach its callback") -local native_table = setmetatable({}, {__index = NETSHOPPING}) -local wrapper_environment = setmetatable({ - NETSHOPPING = native_table, - HUD = setmetatable({}, {__index = HUD}), - ENTITY = setmetatable({}, {__index = ENTITY}), - CUTSCENE = setmetatable({}, {__index = CUTSCENE}), - memory = setmetatable({}, {__index = memory}), -}, {__index = _G}) -assert(loadfile("Nenyoo/lib/yim_compat.lua", "t", wrapper_environment))() -local service_ok, transaction = native_table.NET_GAMESERVER_BEGIN_SERVICE(-1, 1, 2, 3, 4, 5) -assert(service_ok and transaction == 42, "Weapon unlock service lost the output transaction ID") -assert(rawget(NETSHOPPING, "NET_GAMESERVER_BEGIN_SERVICE") == nil, "Package changed global native wrappers") -local blip_position = wrapper_environment.HUD.GET_BLIP_COORDS(1) -assert(blip_position.x == 1 and blip_position.z == 3, "Blip coordinates lost their vector result") -assert(wrapper_environment.HUD.DOES_BLIP_EXIST(1) == false, "Missing blip must be false, not numeric zero") -assert(wrapper_environment.NETSHOPPING.NET_GAMESERVER_BASKET_IS_ACTIVE() == false, "Inactive basket must be false") -assert(wrapper_environment.CUTSCENE.IS_CUTSCENE_PLAYING() == false, "Inactive cutscene must be false") -assert(wrapper_environment.weapons.get_weapon_display_name("WEAPON_PISTOL") == "Pistol", "Weapon editor lost catalog names") -assert(wrapper_environment.weapons.get_weapon_display_name(util.joaat("WEAPON_PISTOL")) == "Pistol", "Gun Van hash did not resolve") -local failures = {} -local initial_action_count = #actions -local initial_toggle_count, initial_input_count, initial_slider_count = #toggles, #inputs, #sliders -for _, action in ipairs(actions) do - local ok, message = pcall(action.callback) - if not ok then failures[#failures + 1] = action.label .. ": " .. tostring(message) end -end -for _, toggle in ipairs(toggles) do - local ok, message = pcall(toggle.callback, not toggle.default) - if not ok then failures[#failures + 1] = toggle.label .. ": " .. tostring(message) end -end -for _, input in ipairs(inputs) do - local ok, message = pcall(input.callback, "1") - if not ok then failures[#failures + 1] = input.label .. ": " .. tostring(message) end -end -for _, slider in ipairs(sliders) do - local ok, message = pcall(slider.callback, 1) - if not ok then failures[#failures + 1] = slider.label .. ": " .. tostring(message) end -end -local scheduled = #thread_callbacks -if os.getenv("UM_OWNED_STATS") == "late" then owned_stats = true end -for i = 1, scheduled do - local thread = coroutine.create(thread_callbacks[i]) - debug.sethook(thread, function() error("Mock thread exceeded one-tick instruction budget") end, "", 1000000) - for tick = 1, 2 do - if coroutine.status(thread) == "dead" then break end - local ok, message = coroutine.resume(thread) - if not ok then failures[#failures + 1] = "thread " .. i .. " tick " .. tick .. ": " .. tostring(message) .. thread_origins[i]; break end - end -end -assert(counters.action >= 1046, "Dynamic Gun Van editor controls did not materialize") -if os.getenv("UM_OWNED_STATS") then - assert(counters.action >= 1069, "Owned-business controls did not materialize") -end -for i = initial_action_count + 1, #actions do - local ok, message = pcall(actions[i].callback) - if not ok then failures[#failures + 1] = actions[i].label .. ": " .. tostring(message) end -end -for i = initial_toggle_count + 1, #toggles do - local ok, message = pcall(toggles[i].callback, not toggles[i].default) - if not ok then failures[#failures + 1] = toggles[i].label .. ": " .. tostring(message) end -end -for i = initial_input_count + 1, #inputs do - local ok, message = pcall(inputs[i].callback, "1") - if not ok then failures[#failures + 1] = inputs[i].label .. ": " .. tostring(message) end -end -for i = initial_slider_count + 1, #sliders do - local ok, message = pcall(sliders[i].callback, 1) - if not ok then failures[#failures + 1] = sliders[i].label .. ": " .. tostring(message) end -end -for _, failure in ipairs(failures) do print(failure) end -assert(#failures == 0, string.format("%d action callbacks failed", #failures)) -assert(menu_back_calls > 0, "Gun Van Close action did not leave its submenu") --- A filtered list changes ordinal positions. An action must still select its --- original weapon rather than whatever weapon moved into that position. -local weapon_filter, pistol_action, set_weapon_action -for _, input in ipairs(inputs) do - if input.label == "Weapon Name" then weapon_filter = input.callback end -end -for _, action in ipairs(actions) do - if action.label == "Pistol" then pistol_action = action.callback end - if action.label == "Set Weapon" then set_weapon_action = action.callback end -end -assert(weapon_filter and pistol_action and set_weapon_action, "Missing Gun Van editor controls") -memory.tunable = function() return 123456 end -weapon_filter("Pistol") -pistol_action() -set_weapon_action() -assert(memory_values[123456] == util.joaat("WEAPON_PISTOL"), "Filtered Gun Van row selected another weapon") -print(string.format("Ultimate Menu loaded: %d nodes, %d lists, %d actions, %d ImGui sections", - next_ref, counters.list, counters.action, #compat.imgui_sections)) diff --git a/Nenyoo/lib/tests/lua_runner.c b/Nenyoo/lib/tests/lua_runner.c deleted file mode 100644 index 79eaddf..0000000 --- a/Nenyoo/lib/tests/lua_runner.c +++ /dev/null @@ -1,18 +0,0 @@ -#include -#include "lua.h" -#include "lauxlib.h" -#include "lualib.h" - -int main(int argc, char **argv) { - if (argc != 2) { - fprintf(stderr, "usage: lua_runner \n"); - return 2; - } - lua_State *lua = luaL_newstate(); - if (!lua) return 2; - luaL_openlibs(lua); - int result = luaL_dofile(lua, argv[1]); - if (result != LUA_OK) fprintf(stderr, "%s\n", lua_tostring(lua, -1)); - lua_close(lua); - return result == LUA_OK ? 0 : 1; -}