Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
c9ac7fa
Add Android client spike with AWT stubs and JNI wrappers.
MaatheusGois Aug 23, 2026
dead7d5
Ship Android display/input fixes and the iOS RoboVM port.
MaatheusGois Aug 23, 2026
1ecbe08
Update README.md
MaatheusGois Aug 23, 2026
fb4914b
Update asset link in README.md
MaatheusGois Aug 23, 2026
7115394
Fix Android IME shrink and use sticky immersive fullscreen.
MaatheusGois Aug 23, 2026
7f34bea
Add real mobile PCM audio via voidsound and document it.
MaatheusGois Aug 23, 2026
0c0c550
Open soft keyboard from in-game text field taps on Android and iOS.
MaatheusGois Aug 23, 2026
d29611c
Document how to run desktop, Android, and iOS clients.
MaatheusGois Aug 23, 2026
466b399
Add client-level Makefile and slim READMEs for run + server IP.
MaatheusGois Aug 23, 2026
4efc991
Add Void app icons for Android and iOS.
MaatheusGois Aug 23, 2026
7c67454
Update README.md
MaatheusGois Aug 23, 2026
3341eed
Fix Android viewport shrinking after resume.
MaatheusGois Aug 24, 2026
6ae26ef
Add 4-finger developer console toggle on mobile.
MaatheusGois Aug 24, 2026
029991d
Halve developer console height on mobile only.
MaatheusGois Aug 24, 2026
c465aa9
Use the Windows arrow for the virtual gamepad cursor on Android and iOS.
MaatheusGois Aug 24, 2026
b91c9cb
Hide the virtual cursor when the gamepad disconnects.
MaatheusGois Aug 24, 2026
fd97b04
Ship mobile soft-keyboard, chat lift, server prefs, and device deploy.
MaatheusGois Aug 25, 2026
5ed3b6d
Document mobile AWT/sound shims and allow iPad window resize.
MaatheusGois Aug 25, 2026
42f12c2
Lift login interface 744 only enough for the focused field to sit abo…
MaatheusGois Aug 25, 2026
0bc94a3
Enable iPad window resize with deferred layout, and stop stealing aud…
MaatheusGois Aug 25, 2026
6a4ad7d
Update README with additional asset link
MaatheusGois Aug 25, 2026
a1d6f1d
Add default left-click swaps, login prefs, and iOS gamepad.
MaatheusGois Aug 25, 2026
dc0f38f
Update README.md
MaatheusGois Aug 25, 2026
d459e2f
Document auto-login, default left-click, and mobile extras in README.
MaatheusGois Aug 25, 2026
3bdfa6a
Disable native SW3D/OpenGL toolkits on macOS desktop.
MaatheusGois Aug 26, 2026
fca40cd
Fix macOS auto-login stuck behind Leave Alone dialog.
MaatheusGois Aug 26, 2026
2488108
Add Microbot runtime with menu-inject API and in-game HUD.
MaatheusGois Aug 26, 2026
4f2780f
Deobfuscate mouse/font APIs and split client into source roots.
MaatheusGois Aug 26, 2026
428c879
Rename deobfuscated Class* types and fix RoboVM AOT for iOS.
MaatheusGois Aug 28, 2026
8168057
Rename client source roots to domain folders.
MaatheusGois Aug 28, 2026
8581020
Organize client/src into fine-grained domain source roots.
MaatheusGois Aug 28, 2026
c54f544
Rename multi-char toolkit shorts to meaningful class names.
MaatheusGois Aug 28, 2026
eacef10
Remove obsolete decode documentation.
MaatheusGois Aug 28, 2026
1bfe55f
Rename evidenced fields and methods across client deobfuscation.
MaatheusGois Aug 28, 2026
1663704
Rename evidenced fields and methods across client deobfuscation.
MaatheusGois Aug 28, 2026
9cb9e0c
Rename evidenced fields and methods across client deobfuscation.
MaatheusGois Aug 28, 2026
1d3fca1
Fix mobile reflection after deob renames and gate future breakage.
MaatheusGois Aug 28, 2026
740f546
Keep iOS screen awake while game is open.
MaatheusGois Aug 28, 2026
2c891e0
Fix iOS splash text orientation via UIGraphics.
MaatheusGois Aug 29, 2026
c090fb1
Note reflection string updates in the deobfuscate skill.
MaatheusGois Aug 29, 2026
de6a4c5
Fix mobile console IME hit-testing and iOS device deploy.
MaatheusGois Aug 29, 2026
a85bb12
Decorate item console echoes with names and wire lean-ctx for Cursor.
MaatheusGois Aug 29, 2026
34b55ff
Fix inverted mobile console drag scroll and rename mouseWheelDelta.
MaatheusGois Aug 29, 2026
ed9561b
Fix item console replay/IME and rename item definition types.
MaatheusGois Aug 29, 2026
5cebeaa
Rename evidenced NPC combat/menu fields and keep Attack on stronger m…
MaatheusGois Aug 29, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
37 changes: 37 additions & 0 deletions .cursor/rules/deobfuscate-on-learn.mdc
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
---
description: On any evidenced understanding of obfuscated client code, run void-client-deobfuscate skill
alwaysApply: true
---

# Deobfuscate when you learn

When you **figure out** what an obfuscated class / method / field / constant / flow does (call sites, strings, protocol, bit flags — not guesses), **do not leave the names as-is**.

## Mandatory next step

1. Read and follow the skill:
`.cursor/skills/void-client-deobfuscate/SKILL.md`
2. Also follow the workspace rename+comment rule (`void-client-deobfuscate.mdc`).
3. Apply a small lote: rename with evidence, comment densely, sync reflection strings if needed, verify.

## Triggers (any of these)

- Bugfix that reveals intent (`item` console echo, camera, packets, …)
- You can explain a `method####` / `anInt####` / `aBoolean####` / opaque class in plain English
- A misnamed type (e.g. “NumberFormatter” that is actually an item def)
- User asks to clarify / rename / “desofuscar”

## Do not skip

- Waiting for the user to say `/void-client-deobfuscate` — if you learned it, deobfuscate in the same turn (or immediately after the fix).
- Renaming without evidence, global replace of non-unique `methodNNN`, or skipping `check_reflection.py` when hosts look up members by string.

## Verify before claiming done

```bash
python3 .cursor/skills/void-client-deobfuscate/scripts/check_reflection.py
./gradlew :client:compileJava
cd ios && ./gradlew compileJava
```

Commit / push only when the user asks.
38 changes: 38 additions & 0 deletions .cursor/rules/lean-ctx.mdc
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
---
description: "lean-ctx hybrid: MUST use ctx_read/ctx_search (MCP) + lean-ctx -c (CLI). NEVER use native Read/Grep/Shell."
alwaysApply: true
---

# lean-ctx — Context Engineering Layer
<!-- lean-ctx-rules-v12 -->

## Mode: Hybrid (MCP reads + CLI shell)

CRITICAL: ALWAYS use lean-ctx tools. This is NOT optional.

## Tool Mapping (MANDATORY)
| Instead of | MUST USE | Example |
|------------|----------|---------|
| Read/cat/head/tail | `ctx_read(path, mode)` | `ctx_read("src/main.rs")` (omit mode = auto) |
| Grep/rg/find | `ctx_search(pattern, path)` | `ctx_search("fn handle", "src/")` |
| Shell/bash | `lean-ctx -c "<cmd>"` | `lean-ctx -c "cargo test"` |
| ls/find | `lean-ctx ls [path]` | `lean-ctx ls src/ --depth 3` |
| Edit (when Read unavail.) | `ctx_edit(path, old, new)` | `ctx_edit("f.rs", "old", "new")` |

## ctx_read Modes (omit mode to auto-select — recommended)
| Goal | Mode |
|------|------|
| Exploring / unsure (default) | `auto` |
| Edit target | `full` |
| API surface | `signatures` |
| Post-edit check | `diff` |
| Overview | `map` |
| Region | `lines:N-M` |

## Workflow: Orient → Locate → Read → Edit → Verify → Record
Use `ctx_overview` or `ctx_compose` first. Then `ctx_search`/`ctx_semantic_search`.
Read with `ctx_read(path, mode)`. Edit with native Edit or `ctx_edit`.
Verify: `ctx_read(path, "diff")` + `lean-ctx -c "test cmd"`.

REMINDER: You MUST use lean-ctx tools. NEVER use native Read, Grep, or Shell directly.
<!-- /lean-ctx -->
72 changes: 72 additions & 0 deletions .cursor/skills/run-mobile-device/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
---
name: run-mobile-device
description: >-
Build, install, and launch Void OSRS on a physical iPad or Android phone.
Use when the user says restart, deploy, run on device, iPad, iOS device,
make android, install, or relaunch the mobile client.
---

# Run Void on device

Game server must already be up on **TCP 43594**.

This skill lives in the **void-client** repo. Run scripts from `void-client/` (this directory).

**Always execute the scripts** (do not paste gradle/signing flags by hand):

| Device | Command | `block_until_ms` |
|--------|---------|------------------|
| iPad | `bash .cursor/skills/run-mobile-device/scripts/ios-device.sh` | `240000` |
| Android USB | `bash .cursor/skills/run-mobile-device/scripts/android-device.sh` | `180000` |

When the user says **restart** / **deploy** without a platform:

1. Just edited `ios/` or `GameController.java` → iPad script.
2. Just edited `android/` or `MainActivity.java` → Android script.
3. Else: iPad if `xcrun devicectl list devices` shows a physical iPad `available`; else Android if `adb devices` has `device`. If both, iPad.

Do **not** use `make ios` for a physical iPad (Simulator only).

## iOS iPad

The script: checks arm64 JDK 17 → **`robovmInstall`** (flat signed bundle in `build/robovm/`) → **always** re-wrap into a fresh `Void.app` → **`devicectl install` + `launch`**.

Do **not** reuse a previous `Void.app` — `robovmInstall` writes beside it; keeping the old wrap installs a stale binary. Do **not** use `launchIOSDevice` (AppLauncher `--console` hangs).

Defaults (override with env):

| Env | Default |
|-----|---------|
| `JAVA_17` | `$HOME/.jdks/jdk-17.0.20.1+1/Contents/Home` |
| `IOS_UDID` | `00008120-000E7D8830214932` (hardware UDID, **not** the `Identifier` from `devicectl list`) |
| `IOS_SIGN_IDENTITY` | `Apple Development: Matheus Gois (885DD2XQL9)` |
| `IOS_PROVISIONING_PROFILE` | `bdd4f76c-78f2-493c-b2e0-66f72e688f77` |

If install fails, re-query UDID:

```bash
xcrun devicectl list devices
xcrun devicectl device info details --device iPad # Hardware.UDID
```

Signing rediscovery: `security find-identity -v -p codesigning`. Team `CTAFH43K36`.

Bundle id: `world.gregs.voidosrs.ios`.

Simulator only if asked: `make ios` / `make ios-relaunch`.

## Android USB

The script refuses to hang if `adb devices` is empty. Then `make android` (installDebug + reverse `:43594` + launch).

- Package `world.gregs.voidosrs.android` / `.MainActivity`
- USB reverse → phone uses host `127.0.0.1:43594`
- LAN: `make android-server SERVER_IP=<lan-ip>`
- Logs: `make android-log`
- Already installed: `make android-run`

## Server host

Saved in `{user.home}/void-server.txt` (in-app picker). Do not change unless asked.

Overrides: `-Dvoid.server=<host>` (host only, no `http://` / port). Android also `adb shell setprop debug.void.server <host>` then relaunch.
16 changes: 16 additions & 0 deletions .cursor/skills/run-mobile-device/scripts/android-device.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
#!/usr/bin/env bash
# Install debug APK, adb reverse :43594, launch MainActivity.
set -euo pipefail

ROOT="$(cd "$(dirname "$0")/../../../.." && pwd)"

devices="$(adb devices | awk 'NR>1 && $2=="device" {print $1}')"
if [[ -z "$devices" ]]; then
echo "no Android device (adb devices empty / unauthorized). Plug in USB and authorize." >&2
adb devices -l >&2
exit 1
fi

cd "$ROOT"
make android
echo "OK — android launched"
69 changes: 69 additions & 0 deletions .cursor/skills/run-mobile-device/scripts/ios-device.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
#!/usr/bin/env bash
# Build + sign via robovmInstall, wrap flat installDir into Void.app, then
# install/launch on iPad via devicectl (no AppLauncher --console hang).
set -euo pipefail

ROOT="$(cd "$(dirname "$0")/../../../.." && pwd)"
IOS="$ROOT/ios"
INSTALL_DIR="$IOS/build/robovm"
APP="$INSTALL_DIR/Void.app"
JAVA_17="${JAVA_17:-$HOME/.jdks/jdk-17.0.20.1+1/Contents/Home}"
UDID="${IOS_UDID:-00008120-000E7D8830214932}"
SIGN_ID="${IOS_SIGN_IDENTITY:-Apple Development: Matheus Gois (885DD2XQL9)}"
PROFILE="${IOS_PROVISIONING_PROFILE:-bdd4f76c-78f2-493c-b2e0-66f72e688f77}"
BUNDLE="${IOS_BUNDLE:-world.gregs.voidosrs.ios}"

if [[ ! -x "$JAVA_17/bin/java" ]]; then
echo "missing arm64 JDK 17 at JAVA_17=$JAVA_17" >&2
exit 1
fi
if ! "$JAVA_17/bin/java" -XshowSettings:properties -version 2>&1 | grep -q 'os.arch = aarch64'; then
echo "JAVA_17 must be arm64 (os.arch=aarch64)" >&2
"$JAVA_17/bin/java" -XshowSettings:properties -version 2>&1 | grep os.arch >&2 || true
exit 1
fi

# robovmInstall always writes flat into INSTALL_DIR. A leftover Void.app from the
# previous wrap makes getAppDir prefer it and our old script would install that
# stale bundle instead of the freshly linked binary.
rm -rf "$APP"

cd "$IOS"
JAVA_HOME="$JAVA_17" PATH="$JAVA_17/bin:$PATH" ./gradlew --no-daemon robovmInstall \
-Probovm.arch=arm64 \
-Probovm.iosSkipSigning=false \
-Probovm.iosSignIdentity="$SIGN_ID" \
-Probovm.iosProvisioningProfile="$PROFILE"

if [[ ! -x "$INSTALL_DIR/Void" || ! -f "$INSTALL_DIR/Info.plist" ]]; then
echo "build finished but flat install missing ($INSTALL_DIR/Void)" >&2
ls -la "$INSTALL_DIR" >&2 || true
exit 1
fi

# Wrap flat installDir → Void.app for devicectl (every run, never reuse stale wrap).
mkdir -p "$APP"
shopt -s nullglob
for item in "$INSTALL_DIR"/*; do
base="$(basename "$item")"
case "$base" in
Void.app|.gitkeep|ipabuild|*.ipa) continue ;;
esac
mv "$item" "$APP/"
done
shopt -u nullglob

if [[ ! -x "$APP/Void" ]]; then
echo "failed to assemble $APP" >&2
ls -la "$APP" >&2 || true
exit 1
fi

echo "installing $APP ($(du -sh "$APP/Void" | awk '{print $1}'))…"
xcrun devicectl device install app --device "$UDID" "$APP"
if ! xcrun devicectl device process launch --device "$UDID" "$BUNDLE"; then
echo "install OK — unlock the iPad and relaunch:" >&2
echo " xcrun devicectl device process launch --device $UDID $BUNDLE" >&2
exit 1
fi
echo "OK — launched $BUNDLE on $UDID"
157 changes: 157 additions & 0 deletions .cursor/skills/void-client-deobfuscate/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
---
name: void-client-deobfuscate
description: >-
Evidence-based rename of obfuscated Java fields/methods in void-client
(RS634), with aggressive comments, reflection-string sync for mobile hosts,
and desktop/iOS compile gates. Use when the user asks to deobfuscate, rename
obfuscated code, continue deob, lote/batch renames, or clarify
Class*/method####/anInt#### names — especially after camera/viewport/input
renames that touch AwtHost reflection.
---

# void-client deobfuscation

Work from **`void-client/`** (this repo). Canonical sources: **`client/`**.

Also follow the workspace rule [`void-client-deobfuscate.mdc`](../../../../.cursor/rules/void-client-deobfuscate.mdc) (rename + comment when you understand something). Domain map: [`client/Architecture.md`](../../../client/Architecture.md).

## Goal

Rename obfuscated types/fields/methods to descriptive English names **only with evidence**, add dense comments, keep **desktop + iOS** compiling, and **keep mobile reflection strings in sync**. Do **not** treat the job as done while thousands of `method####` remain (check with the script below).

Commit / push **only when the user asks**. Message style:

```
Rename evidenced fields and methods across client deobfuscation.
```

## Package model (critical)

- All 634 types live in the Java **unnamed package**.
- Nested folders under `client/` are Gradle **`srcDir`s**, not packages — never add `package …`.
- Android/iOS flatten + rewrite imports (`java.awt` → `voidawt`, etc.). Edit game logic in `client/` only.

## Lote workflow

Repeat until the user stops or the island is exhausted:

1. **Find** high-evidence islands (`method####`, `anInt####`, `aBoolean####`, `aClass####`, opaque classes).
2. **Prove** uniqueness of each short token (one definition) before global replace.
3. **Rename** whole-word, outside string literals; fix `this.field = field` after field renames.
4. **Sync reflection** if the old name appears in mobile host string literals (mandatory — see below).
5. **Comment** class headers, non-obvious methods, magic constants, “why” on dense blocks.
6. **Verify** reflection check + both compiles (below).
7. Optionally `lean-ctx knowledge remember` / `session decision` for continuity.

Typical lote: **~15–40** renames with clear evidence — not a blind mass rename.

### Evidence that counts

| Signal | Examples |
|--------|----------|
| Strings / UI | console commands, `printConsole`, localized tips |
| Protocol / IO | `Buffer` read/write shapes, UPDATE_INV slots, JS5 |
| Call-site role | camera lerp, FPS ring, ignore list, world map |
| Bit math / flags | `0x800`, `0x18`, plane/bridge flags |
| Stdlib | `quickSort`, `Math.atan2`, `Runtime` memory |

**Skip:** opaque `anInt++` counters; JNI **`native`** method names; guesses without call-site proof.

### Unsafe short names

Tokens like `method83` / `method121` may exist on **multiple** classes. Before `\bmethodNNN\b` replace:

```bash
python3 .cursor/skills/void-client-deobfuscate/scripts/check_defs.py method2284 method1913
```

If more than one definition → rename only the defining file + `ThatClass.method` / override chain (`Applet_Sub1` + `client`).

### Rename mechanics

Prefer a small Python pass: longest-old-first, `\b…\b`, skip `"…"` / `'…'` string ranges.

**String literals that are reflection targets must still be updated** (hosts look up members by name). After any rename that might touch camera, viewport, console, or game state, run the reflection gate.

Class renames: update Android stubs under `android/` if shared; iOS prefer:

```bash
cd ios && ./gradlew clean compileJava
```

(Stale `ios/build/generated` breaks builds after renames.)

## Reflection bridge (mandatory gate)

Mobile hosts do **not** call client fields directly. They use `Class.forName` + `getDeclaredField` / `getDeclaredMethod` / `setStaticInt` with **string names**. Failures are swallowed (`catch (Throwable ignored)`), so renames silently kill features:

| Symptom | Broken reflection |
|---------|-------------------|
| 1-finger / right-stick camera dead | `cameraYaw` / `cameraPitch` / `clampCameraAngles` |
| Resize / FS mode wrong | `GlToolkitSub2.anInt7666`, `fullscreenAvailable` |
| Server picker never shows | `Component49.clientState`, `errorCount` |
| Dev console open/close flaky | `openDevConsole` / `closeDevConsole` |

### Hotspots to update with the rename

| File | Role |
|------|------|
| `ios/src/main/java/voidawt/AwtHost.java` | camera orbit, viewport sync, console |
| `android/app/src/main/java/voidawt/AwtHost.java` | same (keep in lockstep with iOS) |
| `android/app/src/main/java/world/gregs/voidosrs/ServerPrefs.java` | `clientState` / JS5 `errorCount` (iOS copies this via Gradle) |

When renaming a field/method/class used there: update **both** AwtHosts (and ServerPrefs if applicable) in the **same lote**. Prefer updating the reflective string + a short comment naming the client member.

### Check before claiming lote done

```bash
python3 .cursor/skills/void-client-deobfuscate/scripts/check_reflection.py
```

Must print `PASS`. If you add a new reflective bridge, extend `EXPECTED` / `STALE_STRINGS` in that script.

## Verify (every lote)

From `void-client/`:

```bash
python3 .cursor/skills/void-client-deobfuscate/scripts/check_reflection.py
./gradlew :client:compileJava
cd ios && ./gradlew compileJava
```

Desktop must be green. iOS must be green before claiming the lote done. After class renames use iOS `clean compileJava`.

Count remaining:

```bash
python3 .cursor/skills/void-client-deobfuscate/scripts/count_methods.py
```

## Hot areas (still dense)

| Area | Path hints |
|------|------------|
| Bootstrap / loop | `client/src/client.java`, `Applet_Sub1`, `LoadingManager` |
| Packets | `client/net/packet/PacketReader`, `Buffer` |
| CS2 | `client/script/ClientScriptExecutor` |
| Staging | `client/components/Component*`, `client/misc/DisplayModeManagerContainer*` |
| Toolkit | `client/toolkit/**`, `client/shaders/**` |

Already clarified islands (do not regress names): camera (`cameraYaw`/`cameraPitch`/`clampCameraAngles`/`cameraFocusX`/…), containers (`NodeSub13.itemIds`/`amounts`, `setContainerSlot`), applet lifecycle (`initializeClient`/`pulseGame`/…), loading (`pulseLoading`, `currentLoadingState`), window mode (`fullscreenAvailable`, `getWindowMode`), game FSM (`Component49.clientState`).

## Deploy (only if user asks)

Physical iPad: skill **`run-mobile-device`** → `bash .cursor/skills/run-mobile-device/scripts/ios-device.sh`.

## Anti-patterns

- Renaming without reading call sites
- Global replace of non-unique `methodNNN`
- Renaming JNI natives
- Adding Java packages under `client/`
- Renaming client members used via reflection **without** updating `AwtHost` / `ServerPrefs` strings
- Assuming desktop compile proves mobile input/viewport still works (reflection fails silently)
- Committing unprompted
- Marking “deob complete” while `count_methods.py` still shows thousands
- If the member is looked up by **string reflection** in `voidawt.AwtHost` or `ServerPrefs`, update those strings in the same change (failures are swallowed — camera/viewport/server-picker die silently). Gate: `python3 .cursor/skills/void-client-deobfuscate/scripts/check_reflection.py`.
Loading