fix: sync current handlers, repair relic data and restore visible launcher consoles

This commit is contained in:
CAtlas0784 committed 2026-10-16 22:19:52 +07:00
1 parent 163d4433e6
commit 74f3c4411b
46 files changed
+4599 -1402

No files matched your search

+23
View File
@@ -31,3 +31,26 @@
- ใน `GetCurBattleInfo` ตัด fallback ปลอมออก หากไม่มี Token ใน `CURRENT_BATTLE` ให้ตอบ `RET_BATTLE_STAGE_NOT_MATCH` (2000) ป้องกัน NRE
- ใน `StartCocoonStage` และ `StartQuickCocoonStage` ส่ง `battleInfo` ที่สมบูรณ์พร้อมเวฟที่ถูกต้อง และเพิ่ม Log แสดงค่า `CocoonId`, `Wave`, `WorldLevel`
4. คอมไพล์ `gameserver-go.exe` ใหม่และรีสตาร์ทกระบวนการ
## Codex battle investigation correction — main affix (2026-10-16)
- Client stack RVA 0xc4eb77c is the return address after the throw call at 0xc4eb777, reached when main-affix lookup returns null (branch at 0xc4eb2b3). It does NOT prove a null sub-affix array; that throw call would return to 0xc4eb781. Earlier interpretation is withdrawn.
- Captured Calyx/SceneCastSkill payload and inventory UID 483: avatar 1213, relic 63095, main_affix_id 11. Reference relic main group 55 contains IDs 1–10, with 10=ImaginaryAddedRatio. Local AvatarConfig confirms Imaginary for 1213.
- Repaired only this inventory field in freesr-data.json: 11 -> 10. No converter ID override. All 24 relic main affixes in the captured team validate against reference data. Reference lacks new relics 63295/63296, so whole inventory is not fully validated.
- Source: Mar-7th/StarRailRes commit dbe8cdfcfb0bf657b9fe1cf92d5537f118ccb487, index_new/en/relics.json and relic_main_affixes.json.
- Backup: ignored battle_diagnostics/main-affix-repair/. Build completed; root gameserver-go.exe/sdkserver-go.exe updated. In-game battle success still requires a new client run.
- First readback probe reported parser property not found. V2 includes non-public fields and CodedInputStream fallback, but local diagnostic probe is disabled for the repair verification. V2 is not yet verified in client.
## Handler/dispatch update against PearlSR 4.6.53 reference
- Synced 2,690 current command names/IDs from upstream revision 005d2c12db4781071f4b5b68e8f364587886a710; generated table preserves verified old Go type aliases and fails on conflicts.
- Source audit: all 865 existing request/response pairs and 4,435 shared message field-number sets match. This does not validate all field types/defaults or client behavior. Include oneof members (including enum-valued wrappers) when auditing; ignoring oneof produces false missing-field reports.
- Replaced numeric raw registrations/old alias ranges with current named bindings. Removed multi-response guesses. Added missing typed Tierce lineup request8992/response8979, fields4/5; fixed cumulative reward request1747/response1748, group10 -> group12/retcode9.
- Current schema statistics fields1/3/11 are a oneof; do not claim they are absent. Updated Go schema to oneof; statistics handler now echoes group without fabricated past scores, matching upstream.
- Added ReplaceLineupScRsp field14; regenerated protobuf. Peak start/leave bindings restored; empty current Peak response no longer fabricates PeakID1. Challenge leave uses saved overworld scene rather than fixed destination.
- Tutorial handlers load IDs from installed TutorialData/TutorialGuideGroup resources, removing the embedded old list. Removed unused generated activity list.
- ClientDownloadData version51 remains because current upstream still uses51 for Lua payloads; do not infer53 from client patch. Avatar command400 response pairing remains semantically obfuscated; current adjacent schema response is used instead of spraying5 candidates, still requires client verification.
- Added reproducible offline source sync/audit tools and docs/protocol-update.md; README/AGENTS updated. Final run.bat build completed, both root executables updated at22:16 local time. No in-game success claimed; no test suite run in this turn.
## Launcher visibility preference
- User explicitly requested visible SDK/game server consoles and launcher menu closure after5 seconds.
- run.ps1 now launches both root server executables with WindowStyle Normal and no stdout/stderr redirection; each server console remains independent when run.bat exits.
- Interactive menu waits5 seconds after the selected action finishes, preserving exit codes. Argument-based build/check/stop returns immediately. README/AGENTS updated. No binary rebuild needed for this script-only change; did not launch or stop running servers.
+70
View File
@@ -0,0 +1,70 @@
"""Compare command IDs and message field numbers with a local PearlSR checkout.
This is a source audit, not a client compatibility test. It does not verify
field types, default values, assets, or gameplay behavior.
"""
import argparse
import pathlib
import re
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("upstream", type=pathlib.Path)
args = parser.parse_args()
root = pathlib.Path(__file__).resolve().parents[1]
upstream = args.upstream / "protocol/src"
command_source = (upstream / "root.zig").read_text(encoding="utf-8")
commands = {n: int(v) for n, v in re.findall(r"\bCmd(\w+)\s*=\s*(\d+)", command_source)}
go_commands = {}
command_conflicts = []
for filename in ("cmdid.go", "upstream_cmdids.go"):
source = (root / "protocol/proto" / filename).read_text(encoding="utf-8")
for name, value in re.findall(r'^\s*"(\w+)":\s*(\d+),', source, re.M):
number = int(value)
if name in go_commands and go_commands[name] != number:
command_conflicts.append((name, go_commands[name], number))
go_commands[name] = number
command_differences = [(n, go_commands.get(n), v) for n, v in commands.items() if go_commands.get(n) != v]
command_differences.extend(command_conflicts)
zig_messages = {}
source = (upstream / "protocol.pb.zig").read_text(encoding="utf-8")
for name, body in re.findall(r"^pub const (\w+) = struct \{\n(.*?)^};", source, re.M | re.S):
descriptor = re.search(r"^ pub const _desc_table = .\{\n(.*?)^ };", body, re.M | re.S)
if descriptor is None:
continue
numbers = {int(v) for v in re.findall(r"^ \.\w+ = fd\((\d+),", descriptor[1], re.M)}
for union in re.findall(r"pub const _union_desc = .\{(.*?)\n };", body, re.S):
numbers.update(int(v) for v in re.findall(r"\.\w+ = fd\((\d+),", union))
zig_messages[name] = numbers
go_messages, wrappers = {}, {}
for path in (root / "protocol/proto").glob("*.pb.go"):
for name, body in re.findall(r"^type (\w+) struct \{\n(.*?)^}", path.read_text(encoding="utf-8"), re.M | re.S):
numbers = {int(v) for v in re.findall(r'protobuf:"[^,]+,(\d+),', body)}
go_messages[name] = numbers
if ',oneof"' in body and "_" in name:
wrappers.setdefault(name.split("_", 1)[0], set()).update(numbers)
for name, numbers in wrappers.items():
go_messages[name].update(numbers)
shared = go_messages.keys() & zig_messages.keys()
field_differences = [(n, sorted(go_messages[n]), sorted(zig_messages[n])) for n in sorted(shared)
if go_messages[n] != zig_messages[n]]
current_pairs = {}
for name, number in commands.items():
for suffix in ("CsReq", "CSReq", "Req"):
if name.endswith(suffix):
stem = name[:-len(suffix)]
candidates = [commands[n] for n in (stem + "ScRsp", stem + "CSScRsp", stem + "Rsp") if n in commands]
if len(candidates) == 1:
current_pairs[number] = candidates[0]
break
source = (root / "protocol/proto/cmdpairs.go").read_text(encoding="utf-8")
pairs = {int(a): int(b) for a, b in re.findall(r"^\s*(\d+):\s*(\d+),", source, re.M)}
pair_differences = [(a, b, current_pairs.get(a)) for a, b in pairs.items() if current_pairs.get(a) != b]
print(f"Command IDs: {len(commands)} compared, {len(command_differences)} differences")
print(f"Existing request/response pairs: {len(pairs)} compared, {len(pair_differences)} differences")
print(f"Shared message field-number sets: {len(shared)} compared, {len(field_differences)} differences")
for difference in command_differences + pair_differences + field_differences:
print(difference)
raise SystemExit(bool(command_differences or pair_differences or field_differences))
+45
View File
@@ -0,0 +1,45 @@
"""Generate current command names from a local PearlSR checkout; no network I/O."""
import argparse
import pathlib
import re
import subprocess
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("upstream", type=pathlib.Path)
args = parser.parse_args()
checkout = args.upstream.resolve()
revision = subprocess.check_output(
["git", "-C", str(checkout), "rev-parse", "HEAD"], text=True
).strip()
source = (checkout / "protocol/src/root.zig").read_text(encoding="utf-8")
enum = source.split("pub const CmdID = enum(u16) {", 1)[1].split("};", 1)[0]
commands = re.findall(r"\bCmd(\w+)\s*=\s*(\d+)\s*,", enum)
if not commands or len({name for name, _ in commands}) != len(commands):
raise SystemExit("Missing or duplicate upstream command names")
if len({int(value) for _, value in commands}) != len(commands):
raise SystemExit("Duplicate upstream command IDs")
lines = [
"// Code generated by tools/sync_protocol_commands.py. DO NOT EDIT.",
"// Source: https://git.xeondev.com/HonkaiSlopRail/pearl-sr protocol/src/root.zig",
"// Revision: " + revision,
"package proto", "",
'const CommandSourceRevision = "' + revision + '"', "",
"var upstreamCmdIDByName = map[string]uint16{",
]
lines += [f'\t"{name}": {value},' for name, value in commands]
lines += ["}", "", "func init() {",
"\tfor name, id := range upstreamCmdIDByName {",
"\t\tif previous, ok := CmdIDByName[name]; ok && previous != id {",
'\t\t\tpanic("command ID conflict between schema and upstream: " + name)',
"\t\t}", "\t\tCmdIDByName[name] = id", "\t\tNameByCmdID[id] = name",
"\t}", "}", ""]
root = pathlib.Path(__file__).resolve().parents[1]
base_source = (root / "protocol/proto/cmdid.go").read_text(encoding="utf-8")
base_commands = {n: int(v) for n, v in re.findall(r'^\s*"(\w+)":\s*(\d+),', base_source, re.M)}
conflicts = [n for n, v in commands if n in base_commands and base_commands[n] != int(v)]
if conflicts:
raise SystemExit("Update the base schema command IDs first: " + ", ".join(conflicts))
target = root / "protocol/proto/upstream_cmdids.go"
target.write_text("\n".join(lines), encoding="utf-8")
subprocess.run(["gofmt", "-w", str(target)], check=True)
print(f"Generated {len(commands)} commands from {revision}")