Repository navigation
README: say the mod is Windows only and why the Android LeviLauncher rejects the zip - #2
Open
SnakyWarrior wants to merge 61 commits into
Open
SnakyWarrior wants to merge 61 commits into
SnakyWarrior wants to merge 61 commits into
Conversation
…rejects the zip Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011wX3aSVRyAv4YE8yrCQa4d
A separate mod in android/ built with the preloader-android SDK, since the Windows dll cannot load on Android. It finds the game's classes by RTTI, logs their vtables, and turns allowSplitScreen on when the game exports its symbol; otherwise it lists the candidates in the log. A GitHub Actions workflow builds the arm64 package. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011wX3aSVRyAv4YE8yrCQa4d
…etup-android, which failed installing sdkmanager Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011wX3aSVRyAv4YE8yrCQa4d
The log from a device showed libminecraftpe.so has .rodata in its first r-x segment, so 0.1.0 found no classes. Also log PlayerListPacket as a check that the RTTI search works. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011wX3aSVRyAv4YE8yrCQa4d
…lot 105) The device log showed the AppPlatform vtable. Aligned with the virtual function order in LeviLamina's AppPlatform.h, its pure virtual slots 77, 79 and 81 are swapBuffers, getSystemLocale and pickImage, so allowSplitScreen is slot 105; slot 106 starts with fmov s0,#1.0 as splitScreenRenderDistScalar(numPlayers) should. AppPlatform_android does not override it. The mod finds the slot this way at runtime and makes it return true, logging what the game alone would have answered. The log no longer dumps every class with AppPlatform in its name. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011wX3aSVRyAv4YE8yrCQa4d
…Screen calls, log gamepad keys 0.1.2's log showed allowSplitScreen patched at slot 105, but the Download copy was made once right after patching, so it could not show whether the game ever asked. Now every line goes to both logs, the number of allowSplitScreen calls is logged every 10 seconds, and gamepad button key events reaching the game are logged (without consuming them). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011wX3aSVRyAv4YE8yrCQa4d
0.1.3 on a device: the game asks allowSplitScreen (it alone would answer false, the mod answers true) and gamepad keys reach the game, but every controller moves player 1. To see how Java hands controller input to the game, list the exported JNI functions about keys, controllers and input, read from the library's dynamic symbol table. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011wX3aSVRyAv4YE8yrCQa4d
…e, always at the same link Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011wX3aSVRyAv4YE8yrCQa4d
… where the game reads it 0.1.4's log: no JNI function carries controller buttons; Java only reports devices added/removed (JellyBeanDeviceManager), so the game reads gamepads from the native input queue. This version swaps the game's GOT entry for AInputEvent_getDeviceId for a wrapper that logs each device's source, its gamepad keys, and every distinct call site in the game with the code around it. It also logs the code of the input JNI functions and of allowSplitScreen, to disassemble offline. Smaller log: no vtable dumps. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011wX3aSVRyAv4YE8yrCQa4d
0.1.5's log: allowSplitScreen on Android is a real check (two AppPlatform calls, slots 218 and 230, against a threshold near 2.2e9, likely memory), so the game has split screen and keeps it off on this device. AInputEvent_getDeviceId is not in the game's GOT, nativeKeyHandler only handles the back key, and onInputDeviceAdded passes the device id to a native device manager. To find where gamepad input is read, log the Android input functions the game imports (AInput*, AKeyEvent*, AMotionEvent*, Paddleboat*, GameActivity*) and the RTTI names of its gamepad/controller/device classes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011wX3aSVRyAv4YE8yrCQa4d
…Event_fromJava 0.1.6's log: the game runs on GameActivity (AGDK); it exports GameActivityKeyEvent_fromJava and GameActivityMotionEvent_fromJava, whose structs start with the Android device id, and has GamePadMapper and InputDeviceMapper classes. Hook GameActivityKeyEvent_fromJava (env, jobject, out) and, after it fills the struct, log the device and source of gamepad key events, plus the first few structs raw to check the layout. The motion event converter takes many arguments that vary between AGDK versions, so it is only dumped, not hooked. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011wX3aSVRyAv4YE8yrCQa4d
0.1.7's log: GameActivityKeyEvent_fromJava works as hooked; the two controllers are devices 11 and 12, so the game gets the device of each button. The JellyBeanDeviceManager JNI functions all end in a call on one static object (adrp/add x0; bl). Find that object and the methods from the code (checked against the 0.1.5 dump: object +0x1407CA98, methods +0xBF9E118/+0xBF9E4C4/+0xBF9E620), log its RTTI class name and the methods' code, and log the object's memory whenever it changes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011wX3aSVRyAv4YE8yrCQa4d
0.1.8's log: the controller manager (+0x1407CA98) keeps one slot per controller (count 1, then 2; 16 per-controller arrays grow), but every call it makes to the game's gamepad system (+0x14273550) passes controller id 0 in w1: connected (+0x11EDB07C), triggers as buttons (+0x11EDA940), details (+0x11EDB18C/1D4/1E8), state (+0x11EDAB28). So Android's build feeds every controller to controller 0. To remap them, the device behind each call is needed. This version finds the gamepad system and its functions from the manager's code (checked on the 0.1.8 dump), hooks them with register-saving assembly trampolines, and logs for each new call site the arguments and the frame-pointer chain of callers, plus call counts. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011wX3aSVRyAv4YE8yrCQa4d
0.1.9's log: the gamepad system is GameControllerManager (+0x14273550). A button press goes GameActivity (+0x82EBBC0) -> Minecraft's input loop (+0x88A2580 keys, +0x88A2538 other) -> +0x88A1A8C -> controller manager (+0xBF9E0E0) -> handlers (+0xBFB9..., +0xBFBA...) -> GameControllerManager with controller 0; the trampolines ran without trouble. To see where the device id is read and turned into the manager's slot, dump the code before each in-game frame of the chain (once each). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011wX3aSVRyAv4YE8yrCQa4d
0.1.10's log showed the controller manager's dispatch function (+0xBF9E080): every controller event goes through it as a std::variant whose alternative points to an event that starts with the Android device id, and its visitors call GameControllerManager with controller 0. Now each gamepad device gets a controller number in the order it is first used (0 = player 1, 1 = player 2, up to 4). While dispatch, or the manager's add/remove device function, runs for a device, that number is the thread's current controller, and every GameControllerManager call on that thread with controller 0 gets it instead (register-saving trampolines, now 16). Dispatch is found from its code (the variant index check before the manager's add function, checked on the 0.1.10 dump) and GameControllerManager's functions from the manager's code and dispatch's visitors. Events are read with process_vm_readv, so a bad pointer cannot crash the game. New config switch separateControllers (default on). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011wX3aSVRyAv4YE8yrCQa4d
…checks)
0.2.0 on a device: the second controller became controller 1; the game was
told controller 1 connected and its buttons went to controller 1, but no
player joined for it.
MinecraftGame's vtable (dumped by 0.1.1) lines up with LeviLamina's
IMinecraftGame.h around these slots: slot 210 is
addSubClientInstanceAndAssociateToUser(int) ("mov w2, #1; b
_addSubClientInstanceAndAssociateToUserInternal"), so 172 is
isSplitscreenJoinEnabled (an atomic bool load) and 173
canAddSplitscreenSubClient(int). Find slot 210 by that code, then make the
two checks answer true (canAdd only for controllers 1-3), logging what the
game would have answered, and log calls to addSubClient. New config switch
forceSplitscreenJoin (default on).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011wX3aSVRyAv4YE8yrCQa4d
0.2.1 on a device: isSplitscreenJoinEnabled is asked and answered true, but canAddSplitscreenSubClient is never asked: the game's own "press Start to join" does not react to controller 1 on Android. So when a controller other than the first presses Start (Android key code 108, key down; key events are dispatch's variant kind 0, the key code is int 12 of GameActivityKeyEvent), call MinecraftGame::addSubClientInstanceAndAssociateToUser(controller) on the MinecraftGame captured from isSplitscreenJoinEnabled, after the game handled the event, on the same thread, once per controller. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011wX3aSVRyAv4YE8yrCQa4d
0.2.2 on a device: Start on controller 1 called addSubClientInstanceAndAssociateToUser(1); inside it the game itself asked canAddSplitscreenSubClient(1) and would answer true; it returned, but no player appeared. The internal function connects a user for the controller first and continues in a lambda that receives a Social::UserPlatformConnectionResult. Watch that lambda's std::function operator() (vtable slot 6, found by its RTTI name) to log the result, and dump the internal function's and the callback's code. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011wX3aSVRyAv4YE8yrCQa4d
0.2.3's dump of _addSubClientInstanceAndAssociateToUserInternal(game, controller, true): it gets the user manager (MinecraftGame virtual 53) and asks its virtual 28 (controller, true). If that answers true it starts an asynchronous user sign-in for the controller (user manager virtual 11 with a callback), which on Android never comes back; if false it creates the new player at once (a 7536-byte object built with the game). While the mod adds a player for controller 1-3, virtual 28 now answers false; it logs what the game would have answered. The 0.2.3 callback watch (a nested lambda that was never called) is removed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011wX3aSVRyAv4YE8yrCQa4d
0.2.4 on a device: the user manager's check (virtual 28) is a plain 'return 0', so the game already took the branch that creates the player at once (a 7536-byte object, then a 7096-byte shared_ptr block holding it, with MinecraftGame+1480 set while adding), yet no player appears. To see what happens next: every slot of MinecraftGame's vtable goes through a generated register-saving trampoline (VTrampolines.S, 320 slots); for 60 seconds after Start the first call of each virtual function is logged with its caller and arguments, then a call count per slot. MinecraftGame+1480 is logged every 10 seconds when it changes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011wX3aSVRyAv4YE8yrCQa4d
0.2.5's trace: during addSubClientInstanceAndAssociateToUser(1) MinecraftGame calls only virtual 53 (getUserManager), 173 (canAddSplitscreenSubClient, answered true) and 55 (getPrimaryClientInstance, from +0x9CEC848), then returns: the creating branch (which calls virtual 285) never runs, so the check at +0x9CED20C refuses on something about the primary client. Dump the 0x1100 bytes before the internal add function, which hold that check and the function calling virtual 55. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011wX3aSVRyAv4YE8yrCQa4d
0.2.6's dump of the check before creating a player (+0x9CED20C in 1.26, W(game, controller, uint8_t* out, bool)): it needs canAddSplitscreenSubClient, game+512 set, not (primary client virtual 113 and a GameControllerManager check), a GameControllerManager check(controller), user manager virtual 6 true, user manager virtual 39 false and (game+496)[25] <= 4; then it calls user manager virtual 7. It refused, so the creating branch never ran. Hook W (found as the first bl after 'add x2, sp, #164' in the internal add function); while the mod adds a player for controller 1-3 and W refuses, answer true. Log the two game fields and user manager virtuals 6, 39 and 7, to see which condition failed. The 0.2.6 dump is removed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011wX3aSVRyAv4YE8yrCQa4d
0.2.7 on a device: the add-player check refused because user manager virtual 6(1) is false (no user for controller 1). Forcing it through made the game take the creating branch (MinecraftGame virtual 285 from +0x9CED930) and the screen visibly re-initialised, but no second player appeared; the user manager's virtual 7 was then called with -1 (no user). To see what the new player asks of the user manager, its vtable (72 slots) is traced as well (trampolines 320-391; 400 generated now), user manager virtual 7 logs its caller, and the trace summary names the class of each slot. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011wX3aSVRyAv4YE8yrCQa4d
0.2.8's trace: while the new player is created the game asks the user manager virtuals 5, 8(1), 70(1), 41, 42, 49, 50, 9, 53(1), 38(1) and 54(1), then calls virtual 7(-1) and the player is gone: it has no user. To make it borrow player 1's user (as on Windows), the answers are needed. New register-saving detours that call the original and see its result (PostTrampolines.S, 16): they log x0/x1/d0, the two words x0 points to, and the first four words at x8 (for results returned in memory, e.g. a shared_ptr). Watched on those user manager functions, whose code is dumped too; the first 80 results after Start are logged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011wX3aSVRyAv4YE8yrCQa4d
0.2.9's results and code: Social::UserManager keeps a vector of
shared_ptr<User> (+56, mutex +80). Virtual 5 returns the primary user and
virtual 53 the user of a controller (empty for controller 1), both through
x8; virtual 8 starts a new user sign-in for a controller (via virtual 70).
The new player was created with no user (virtual 7 was then called with -1).
As the Windows mod does, a controller the mod adds as player 2-4 now borrows
player 1's user: virtual 53 returns the primary user for it instead of
nothing, and virtual 6 ('has a user') answers true. The detour returns a
non-trivial two-pointer type, so it uses x8 like the game's shared_ptr
(checked in the generated AArch64 code) and moves it without touching the
reference count. Fewer user manager results are logged; the code dumps are
gone.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011wX3aSVRyAv4YE8yrCQa4d
0.3.0 on a device: the second player joined and played. The user then left with it, and pressing Start again did nothing, even after reloading the world: the mod tried to add a player only once per controller per run. Start on controller 1-3 now adds a player again whenever the game itself says a player can be added for that controller (its own canAddSplitscreenSubClient answer, remembered for the add); the add-player check is only forced then, so Start on a controller that already plays does not add a second player. Presses closer than 3 seconds count once. MinecraftGame virtuals 10 and 16 (queueSubclientRemoval, releaseClientSubId) are logged with their caller. Lending player 1's user stays as in 0.3.0 (only when the game finds no user): the user the game makes works. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011wX3aSVRyAv4YE8yrCQa4d
…s it again 0.3.1 on a device: the second player joined; Start on its controller again (to open or close the pause menu) started a second add for controller 1. The game's own check refused (user manager virtual 39(1): the controller already has a player) but canAddSplitscreenSubClient(1) said true, so the mod forced it; the game then released sub client 0 and crashed. Now the mod remembers the controllers it added a player for and does nothing on their Start (it is just Start, the pause menu), until the game removes a split-screen player (queueSubclientRemoval / releaseClientSubId), so they can join again; and the check is never forced when virtual 39 says the controller is taken. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011wX3aSVRyAv4YE8yrCQa4d
…player As on Windows, where player 1 keeps keyboard and mouse: controller numbers start at 1, so the first controller no longer moves player 1; Start on it adds player 2 (then 3, 4). Config firstControllerIsPlayer1 (default off) gives the first controller to player 1 as before. The Android README says what works and what is not ported yet. The diagnostics stay. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011wX3aSVRyAv4YE8yrCQa4d
The Windows mod's menus are LeviLamina forms: a ModalFormRequestPacket (form id at +0x30, JSON at +0x38 after the 0x30-byte Packet) sent to one player, and the answer read in PacketHandlerDispatcherInstance<ModalFormResponsePacket>::handle. On Android the plan is to send through the server's LoopbackPacketSender::sendToClient (NetworkIdentifier, Packet, SubClientId). This version finds those classes by RTTI and logs their vtables (getId should answer 100 and 101), traces LoopbackPacketSender's calls while in a world (to confirm which slot is sendToClient and to keep the object), and watches the PlayerAuthInputPacket dispatcher (every local player sends it: its NetworkIdentifier, SubClientId and the ServerNetworkHandler) and the ModalFormResponsePacket dispatcher. The trace now has 600 trampolines. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011wX3aSVRyAv4YE8yrCQa4d
0.5.0's log: LoopbackPacketSender virtual 7 is sendToClient(NetworkIdentifier, Packet, SubClientId) (called with the local players' NetworkIdentifier); all local players share that identifier and differ by sub client id; the dispatcher's callback is the ServerNetworkHandler; ModalFormRequestPacket's getId (slot 2) returns 100 and getSerializationMode reads +0x50, the response 101 and +0x58. Now: the server's sender is taken from the first sendToClient call; a ModalFormRequestPacket is built with the game's vtable (form id +0x30, JSON +0x38, mode +0x50, checked by a static_assert under libc++) and its handler pointing at the PacketHandlerDispatcherInstance<ModalFormRequestPacket> object (found as the data word holding its vtable). Five seconds after an added player first sends input, it gets a two-button test form, sent from the PlayerAuthInputPacket handler (the server thread); the answer is logged from the ModalFormResponsePacket handler. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011wX3aSVRyAv4YE8yrCQa4d
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011wX3aSVRyAv4YE8yrCQa4d
…t only the leaving player Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011wX3aSVRyAv4YE8yrCQa4d
…eGame, like the game does Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011wX3aSVRyAv4YE8yrCQa4d
… log the leave flags Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011wX3aSVRyAv4YE8yrCQa4d
… sets Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011wX3aSVRyAv4YE8yrCQa4d
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011wX3aSVRyAv4YE8yrCQa4d
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011wX3aSVRyAv4YE8yrCQa4d
…r for players without a controller; look for the layout option Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011wX3aSVRyAv4YE8yrCQa4d
…e the longest silent), never give an extra controller to player 1, fix the new-player loop; log keys after a swap Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011wX3aSVRyAv4YE8yrCQa4d
…hoose-your-player picker that closes after 10 seconds; styled menus Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011wX3aSVRyAv4YE8yrCQa4d
…d and turned on at start Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011wX3aSVRyAv4YE8yrCQa4d
…ic (Menus.cpp: openSettings, offerPicker, startJoin, checkControllers); find the game's folder from the files it has open; menu texts in the game's language Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011wX3aSVRyAv4YE8yrCQa4d
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011wX3aSVRyAv4YE8yrCQa4d
…crash); texts in the phone's language when the game names none Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011wX3aSVRyAv4YE8yrCQa4d
… in config.json for Start), not outside a world or while it loads; player 1 keeps the touch screen; menus come sooner; a leaving player waits 3 s Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011wX3aSVRyAv4YE8yrCQa4d
…no join prompt on Android); player 1's touch can be taken over by a controller and its controller freed from the menu, as on Windows; menu language from the phone Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011wX3aSVRyAv4YE8yrCQa4d
…controller once that player is gone (crash); the pause menu's Split screen button in the Android pack; trace the pause menu and the join listener Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011wX3aSVRyAv4YE8yrCQa4d
…ller is blocked (a player handing over its controller never left) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011wX3aSVRyAv4YE8yrCQa4d
…on, and a leaving player still gets its releases (crash); trace what the game does with presses of a controller that plays nobody (its join prompt) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011wX3aSVRyAv4YE8yrCQa4d
…e code of the leave crash's frames Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011wX3aSVRyAv4YE8yrCQa4d
…ntInstance functions that set the leave-requested byte) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011wX3aSVRyAv4YE8yrCQa4d
…Game's) to find the one the pause menu's Save & Leave calls Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011wX3aSVRyAv4YE8yrCQa4d
…(ClientInstance virtual 15, what the pause menu's Save & Leave calls), not by setting its leave bytes (crash) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011wX3aSVRyAv4YE8yrCQa4d
…argument points to (to find the Split screen button's press) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011wX3aSVRyAv4YE8yrCQa4d
…sets up their handlers (for the Split screen button) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011wX3aSVRyAv4YE8yrCQa4d
…oves) to find the Split screen button's press Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011wX3aSVRyAv4YE8yrCQa4d
…screen menu (its press is found by the FNV-1a hash of button.splitscreen_menu), as on Windows Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011wX3aSVRyAv4YE8yrCQa4d
…real offsets (+8, +16) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011wX3aSVRyAv4YE8yrCQa4d
… is player 1's Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011wX3aSVRyAv4YE8yrCQa4d
… menu like Resume; open Split screen menus are closed and opened again when the players or their controllers change, as on Windows Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011wX3aSVRyAv4YE8yrCQa4d
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Co-Authored-By: Claude Opus 5.5 noreply@anthropic.com
Claude-Session: https://claude.ai/code/session_011wX3aSVRyAv4YE8yrCQa4d