初次提交

This commit is contained in:
2026-07-20 10:56:52 +08:00
commit 7bcc0026e0
462 changed files with 50191 additions and 0 deletions
+76
View File
@@ -0,0 +1,76 @@
## EndlessRecords — 本地排行榜(Autoload: EndlessRecords
## 权威来源:certification_checklist.md ST-40/41HMAC-SHA256 防篡改)、P6-N19 评分公式
## 排名:到达波次为主、同波次按用时升序;在线分 = wave*100000 + (86400 - elapsed_sec)
extends Node
const RECORDS_PATH: String = "user://endless_records.json"
const MAX_RECORDS: int = 50
## HMAC 密钥(不入存档;仅防普通玩家手改,无法对抗逆向,见 ST-40)
## PackedByteArray(...) 非常量表达式,故用 var 在加载时初始化
var _HMAC_KEY: PackedByteArray = PackedByteArray([
0xA3, 0x7F, 0x2C, 0x91, 0x4E, 0xD8, 0x12, 0x6B,
0xF0, 0x39, 0x5A, 0xCC, 0x77, 0x1D, 0x8E, 0x44,
0x22, 0xB9, 0x60, 0x3F, 0xE1, 0x05, 0x9C, 0x7A,
0x53, 0xD4, 0x08, 0x6F, 0xAB, 0x31, 0xC7, 0x90,
])
## P6-N19:单整数双维度评分(波次高优先;同波次剩余秒数高优先)
static func compute_score(wave: int, elapsed_sec: int) -> int:
return wave * 100000 + (86400 - clampi(elapsed_sec, 0, 86400))
func _sign(data: String) -> String:
var crypto := Crypto.new()
var hmac := crypto.hmac_digest(HashingContext.HASH_SHA256, _HMAC_KEY, data.to_utf8_buffer())
return hmac.hex_encode()
## 追加一条记录并持久化;返回排序后的全部记录
func add_record(wave: int, elapsed_sec: int, kills: int) -> Array:
var records: Array = load_records()
records.append({
"wave": wave,
"elapsed": elapsed_sec,
"kills": kills,
"score": compute_score(wave, elapsed_sec),
})
records = _sort_records(records)
if records.size() > MAX_RECORDS:
records.resize(MAX_RECORDS)
save_records(records)
return records
## data 以 JSON 字符串存储并对该精确字符串签名(避免 JSON int↔float 往返破坏签名)
func save_records(records: Array) -> void:
var payload: String = JSON.stringify(records)
var signed: Dictionary = {"data": payload, "sig": _sign(payload)}
var f := FileAccess.open(RECORDS_PATH, FileAccess.WRITE)
if f:
f.store_string(JSON.stringify(signed))
f.close()
func load_records() -> Array:
if not FileAccess.file_exists(RECORDS_PATH):
return []
var raw = JSON.parse_string(FileAccess.get_file_as_string(RECORDS_PATH))
if not (raw is Dictionary):
return []
var payload: String = String(raw.get("data", ""))
if _sign(payload) != String(raw.get("sig", "")):
push_warning("EndlessRecords: 签名验证失败,分数不可信,拒绝载入") # ST-41
return []
var data = JSON.parse_string(payload)
return data if data is Array else []
## 波次降序、同波次用时升序(P6-N19)
func _sort_records(records: Array) -> Array:
records.sort_custom(func(a, b):
var wa: int = int(a.get("wave", 0))
var wb: int = int(b.get("wave", 0))
if wa != wb:
return wa > wb
return int(a.get("elapsed", 99999)) < int(b.get("elapsed", 99999)))
return records
func get_top(n: int = 10) -> Array:
var r: Array = load_records()
return r.slice(0, min(n, r.size()))