pre cw with probe and a bunch script to read game behavior
This commit is contained in:
1 parent
5120219ae2
commit
da8a32f60d
310 files changed
+219693
-872
No files matched your search
@@ -0,0 +1,85 @@
|
||||
"""Inventory CW's local protobuf graph and dispatcher coverage (no Client calls).
|
||||
|
||||
This is a discovery artifact, not a gameplay or wire-compatibility test.
|
||||
Command identity comes from the revision-tagged upstream table. The message
|
||||
graph retains obfuscated names rather than assigning guessed meanings.
|
||||
"""
|
||||
import argparse
|
||||
import hashlib
|
||||
import json
|
||||
import pathlib
|
||||
import re
|
||||
|
||||
|
||||
def declarations(source):
|
||||
result = {}
|
||||
for match in re.finditer(r"^\s*(message|enum)\s+(\w+)\s*\{", source, re.M):
|
||||
depth, end = 1, match.end()
|
||||
while end < len(source) and depth:
|
||||
depth += (source[end] == "{") - (source[end] == "}")
|
||||
end += 1
|
||||
if depth:
|
||||
raise ValueError("Unterminated declaration: " + match[2])
|
||||
result[match[2]] = (match[1], source[match.end():end - 1])
|
||||
return result
|
||||
|
||||
|
||||
def inventory(root):
|
||||
schema = root / "protocol/proto/StarRail.proto"
|
||||
upstream = (root / "protocol/proto/upstream_cmdids.go").read_text(encoding="utf-8")
|
||||
revision = re.search(r'CommandSourceRevision = "([^"]+)"', upstream)[1]
|
||||
commands = {name: int(number) for name, number in
|
||||
re.findall(r'^\s*"(\w+)":\s*(\d+),', upstream, re.M)}
|
||||
types = declarations(schema.read_text(encoding="utf-8"))
|
||||
family = {name: number for name, number in commands.items()
|
||||
if name.startswith("GridFight")}
|
||||
# BuyGoodRsp is the explicitly named response of GridFightBuyGoodsCsReq
|
||||
# in the current schema. Include the source declaration, without inventing
|
||||
# a GridFightBuyGoodsScRsp alias or choosing a numeric response here.
|
||||
seeds = set(family) | {"GridFightGameInfo", "BuyGoodRsp"}
|
||||
reachable, queue = {}, sorted(seeds)
|
||||
while queue:
|
||||
name = queue.pop()
|
||||
if name in reachable or name not in types:
|
||||
continue
|
||||
kind, body = types[name]
|
||||
deps = sorted(set(re.findall(r"\b\w+\b", body)) & types.keys() - {name})
|
||||
reachable[name] = {"kind": kind, "references": deps, "body": body.strip()}
|
||||
queue.extend(deps)
|
||||
handlers = {}
|
||||
for path in sorted((root / "internal/gameserver").glob("*.go")):
|
||||
text = path.read_text(encoding="utf-8")
|
||||
for match in re.finditer(r'\b(handleNamed|handle|rawNamed|dummy)\("([^"]+)"', text):
|
||||
if match[2].startswith("GridFight"):
|
||||
handlers.setdefault(match[2], []).append({
|
||||
"file": path.relative_to(root).as_posix(), "kind": match[1],
|
||||
"line": text.count("\n", 0, match.start()) + 1,
|
||||
})
|
||||
return {
|
||||
"command_source_revision": revision,
|
||||
"schema_sha256": hashlib.sha256(schema.read_bytes()).hexdigest(),
|
||||
"limitations": [
|
||||
"Local schema graph only; field semantics and current Client tags require separate evidence.",
|
||||
"Handler registration does not establish correct behavior or completed gameplay.",
|
||||
"Non-GridFight command names used by pending selections require separate mapping.",
|
||||
],
|
||||
"commands": dict(sorted(family.items())),
|
||||
"registrations": handlers,
|
||||
"types": dict(sorted(reachable.items())),
|
||||
}
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument("--root", type=pathlib.Path, default=pathlib.Path(__file__).resolve().parents[1])
|
||||
parser.add_argument("--output", type=pathlib.Path, required=True)
|
||||
args = parser.parse_args()
|
||||
result = inventory(args.root.resolve())
|
||||
args.output.parent.mkdir(parents=True, exist_ok=True)
|
||||
args.output.write_text(json.dumps(result, ensure_ascii=False, indent=2) + "\n", encoding="utf-8")
|
||||
print(f"CW: {len(result['commands'])} commands, {len(result['types'])} reachable declarations, "
|
||||
f"{len(result['registrations'])} handler registration names")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in new issue
Block a user