This repository has been archived on 2026-10-17. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
SashegDev b8e5df778e Модлоадер RustexRemake: агент, патч лаунчера, установщик и сборка
Моды грузятся через -javaagent, game.jar не патчится, переживают ре-обфускацию.

Что внутри:
- агент rsx-bridge.jar: профиль сборки игры, патчи A2/A6, загрузка модов
  через родной загрузчик игры (ilxq -> special.Main)
- патч лаунчера: окно выбора «С модами / Ванилла» по кнопке «Играть»
  и защита от автоапдейта лаунчера
- install/uninstall: всё ставится в %LOCALAPPDATA%\rsx-mod, пути не хардкодятся
- build: три готовых артефакта в dist, нужен только JDK
- testserver: локальный сервер, принимает любой ник и токен

Проверено на живом клиенте: мод грузится и выполняется в игровом потоке,
в логе видно игрока и мир.
2026-10-17 00:32:10 +03:00

160 lines
4.8 KiB
Python

#!/usr/bin/env python3
"""Self-test for server.py: runs the stand-in server on a spare port and talks
to it with a hand-rolled 1.12.2 client, checking handshake, login and the
play-state packets the game needs to reach a world."""
import socket
import struct
import sys
import threading
import time
import uuid
sys.path.insert(0, r"G:\rsx-mod\testserver")
import server as srv # noqa: E402
def read_varint(sock):
value = 0
for shift in range(0, 35, 7):
chunk = sock.recv(1)
if not chunk:
raise AssertionError("closed")
byte = chunk[0]
value |= (byte & 0x7F) << shift
if not byte & 0x80:
return value
raise AssertionError("varint too long")
def read_packet(sock):
length = read_varint(sock)
data = b""
while len(data) < length:
chunk = sock.recv(length - len(data))
if not chunk:
raise AssertionError("closed mid packet")
data += chunk
pid_val = 0
shift = 0
pos = 0
while True:
byte = data[pos]
pos += 1
pid_val |= (byte & 0x7F) << shift
if not byte & 0x80:
break
shift += 7
return pid_val, data[pos:]
def send_packet(sock, pid, payload=b""):
body = srv.varint(pid) + payload
sock.sendall(srv.varint(len(body)) + body)
def drain(sock, wanted, budget=8.0):
seen = {}
deadline = time.time() + budget
while time.time() < deadline and not wanted.issubset(seen):
try:
pid, payload = read_packet(sock)
except (socket.timeout, TimeoutError, AssertionError, OSError):
break
seen.setdefault(pid, payload)
return seen
def main():
port = 25599
listener = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
listener.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
listener.bind(("127.0.0.1", port))
listener.listen(4)
def accept_loop():
try:
sock, peer = listener.accept()
srv.handle(srv.Conn(sock, "%s:%s" % peer))
except Exception:
pass
threading.Thread(target=accept_loop, daemon=True).start()
sock = socket.create_connection(("127.0.0.1", port), timeout=10)
sock.settimeout(10)
results = []
handshake = (srv.varint(srv.PROTOCOL) + srv.mc_string("127.0.0.1")
+ struct.pack(">H", port) + srv.varint(2))
send_packet(sock, 0x00, handshake)
results.append(("handshake sent", True))
send_packet(sock, 0x00,
srv.mc_string("TestPilot")
+ srv.mc_string("00000000-0000-0000-0000-000000000000"))
pid, payload = read_packet(sock)
cursor = srv.Cursor(payload)
uuid_txt = cursor.string()
nick = cursor.string()
results.append(("login success", pid == 0x02 and nick == "TestPilot"))
uuid_ok = len(uuid_txt) in (32, 36)
try:
uuid.UUID(uuid_txt)
except (ValueError, AttributeError):
uuid_ok = False
results.append(("uuid parseable (%d chars)" % len(uuid_txt), uuid_ok))
send_packet(sock, 0x03, b"")
seen = drain(sock, {0x01, 0x08, 0x23})
results.append(("join game received", 0x01 in seen))
results.append(("spawn position received", 0x08 in seen))
results.append(("brand received", 0x23 in seen))
if 0x01 in seen:
cur = srv.Cursor(seen[0x01])
entity = struct.unpack(">i", cur.take(4))[0]
gamemode = cur.ubyte()
dimension = struct.unpack(">i", cur.take(4))[0]
cur.ubyte()
cur.ubyte()
level = cur.string()
results.append(("join game parses (entity=%d gm=%d dim=%d %s)"
% (entity, gamemode, dimension, level),
level == srv.WORLD_NAME))
if 0x08 in seen:
cur = srv.Cursor(seen[0x08])
x = struct.unpack(">d", cur.take(8))[0]
y = struct.unpack(">d", cur.take(8))[0]
z = struct.unpack(">d", cur.take(8))[0]
cur.take(8)
cur.ubyte()
tid = cur.varint()
results.append(("spawn parses (%.1f/%.1f/%.1f tid=%d)"
% (x, y, z, tid), y == 64.0 and tid == 0))
send_packet(sock, 0x00, srv.varint(0x1234))
send_packet(sock, 0x03, srv.mc_string("ru_ru") + bytes([8])
+ srv.varint(0) + b"\x01" + bytes([2]) + bytes([127])
+ srv.varint(1))
send_packet(sock, 0x07,
struct.pack(">dddff?", 0.5, 64.0, 0.5, 0.0, 0.0, True))
alive = drain(sock, {0x00}, budget=18.0)
results.append(("keep alive flow", 0x00 in alive))
sock.close()
listener.close()
print("== self-test ==")
failed = 0
for label, ok in results:
print(" %-40s %s" % (label, "OK" if ok else "FAIL"))
failed += 0 if ok else 1
print(" %d check(s) failed" % failed)
return 1 if failed else 0
if __name__ == "__main__":
sys.exit(main())