port 2D step 2: 40250 item_proto/mob_proto through CLZO and pack://locale/<lang>/
proto.cpp reads the 40250 MIPX/MMPT tables with the ported CLZO/TEA (156-byte TItemTable, 255-byte SMobTable), replacing the m2dev XChaCha20 236/335 reader. Names follow CItemData::GetName / GetMonsterName (szLocaleName, cp1252). GDScript callers load AssetRoot.locale_file() = pack://locale/en/<name>, which makes the EterPack chain runtime-reachable; port-map entries for the functions a coverage run hit move to PORTED/ADAPTED. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -45,3 +45,28 @@ static func sub(rel: String) -> String:
|
||||
# 资源目录是否真的存在(缺资源的测试据此跳过)。
|
||||
static func available() -> bool:
|
||||
return DirAccess.dir_exists_absolute(path())
|
||||
|
||||
# --- 40250 client (pack/Index + .eix/.epk), docs/PORT-PLAN.md 2R item 6 / 2D ---
|
||||
# 解析顺序:环境变量 MT_40250_CLIENT;开发树 res://../../40250/Server Client TMP4/Client。
|
||||
static func client_path() -> String:
|
||||
var env := OS.get_environment("MT_40250_CLIENT")
|
||||
if env != "":
|
||||
return env
|
||||
return ProjectSettings.globalize_path("res://../../40250/Server Client TMP4/Client").simplify_path()
|
||||
|
||||
# 按 40250 PackInitialize("pack") 注册 <client>/pack;只注册一次,之后返回首次结果。
|
||||
static func pack_ready() -> bool:
|
||||
if not ClassDB.class_exists("Metin2Pack"):
|
||||
return false
|
||||
return bool(ClassDB.class_call_static("Metin2Pack", "initialize", client_path()))
|
||||
|
||||
# 40250 的 locale 文件(CPythonApplication::Create: "%s/item_proto" % LocalePath,LocalePath = "locale/<lang>"),
|
||||
# 走 pack://;该语言没有时回退 en。pack 没就绪返回 ""。
|
||||
static func locale_file(name: String, lang: String = "en") -> String:
|
||||
if not pack_ready():
|
||||
return ""
|
||||
for l in [lang, "en"]:
|
||||
var p := "pack://locale/%s/%s" % [l, name]
|
||||
if bool(ClassDB.class_call_static("Metin2Pack", "exists", "locale/%s/%s" % [l, name])):
|
||||
return p
|
||||
return ""
|
||||
|
||||
@@ -278,8 +278,8 @@ func _run() -> void:
|
||||
if ClassDB.class_exists("Metin2Proto"):
|
||||
var pr: Object = ClassDB.instantiate("Metin2Proto")
|
||||
get_root().add_child(pr)
|
||||
var ip := assets.path_join("locale/locale/en/item_proto")
|
||||
if FileAccess.file_exists(ip) and pr.call("load_item_proto", ip):
|
||||
var ip := AssetRoot.locale_file("item_proto")
|
||||
if ip != "" and pr.call("load_item_proto", ip):
|
||||
var em3: Node = EquipModel.new()
|
||||
get_root().add_child(em3)
|
||||
em3.setup(fc, il, func() -> Node: return model, assets)
|
||||
|
||||
@@ -488,10 +488,8 @@ func setup(m2client: Node, assets_root: String,
|
||||
if ClassDB.class_exists("Metin2Proto") and assets_root != "":
|
||||
proto = ClassDB.instantiate("Metin2Proto")
|
||||
add_child(proto)
|
||||
var ip := assets_root.path_join("locale/locale/%s/item_proto" % cur_lang)
|
||||
if not FileAccess.file_exists(ip):
|
||||
ip = assets_root.path_join("locale/locale/en/item_proto")
|
||||
if FileAccess.file_exists(ip):
|
||||
var ip := AssetRoot.locale_file("item_proto", cur_lang)
|
||||
if ip != "":
|
||||
proto.call("load_item_proto", ip)
|
||||
# CPythonPlayer::IsEquipItemInSlot and SetItemData both consult the
|
||||
# same ItemManager table. Share this authority with native senders so
|
||||
@@ -505,10 +503,8 @@ func setup(m2client: Node, assets_root: String,
|
||||
chat.set_proto(proto)
|
||||
if quest_dialog and quest_dialog.has_method("set_proto"):
|
||||
quest_dialog.set_proto(proto)
|
||||
var mp := assets_root.path_join("locale/locale/%s/mob_proto" % cur_lang)
|
||||
if not FileAccess.file_exists(mp):
|
||||
mp = assets_root.path_join("locale/locale/en/mob_proto")
|
||||
if FileAccess.file_exists(mp):
|
||||
var mp := AssetRoot.locale_file("mob_proto", cur_lang)
|
||||
if mp != "":
|
||||
proto.call("load_mob_proto", mp)
|
||||
await _yield()
|
||||
net_play.proto = proto # 按 race 分类 NPC/怪(bType 在本 fork 不可靠)
|
||||
|
||||
@@ -175,8 +175,8 @@ func _run() -> void:
|
||||
var audio: Node = Audio.new()
|
||||
get_root().add_child(audio)
|
||||
audio.setup(assets)
|
||||
var mp := assets.path_join("locale/locale/en/mob_proto")
|
||||
if not FileAccess.file_exists(mp) or not proto.call("load_mob_proto", mp):
|
||||
var mp := AssetRoot.locale_file("mob_proto")
|
||||
if mp == "" or not proto.call("load_mob_proto", mp):
|
||||
print(" (skip: 无 mob_proto)")
|
||||
return
|
||||
|
||||
|
||||
@@ -130,12 +130,12 @@ func _test_proto() -> void:
|
||||
return
|
||||
var p: Object = ClassDB.instantiate("Metin2Proto")
|
||||
get_root().add_child(p)
|
||||
var root := AssetRoot.sub("locale/locale/en")
|
||||
if not FileAccess.file_exists(root + "/item_proto"):
|
||||
print(" (skip Metin2Proto load: no item_proto)")
|
||||
var ip := AssetRoot.locale_file("item_proto")
|
||||
if ip == "":
|
||||
print(" (skip Metin2Proto load: no 40250 pack)")
|
||||
p.queue_free()
|
||||
return
|
||||
_ck(p.call("load_item_proto", root + "/item_proto"), "load_item_proto: %s" % p.call("get_last_error"))
|
||||
_ck(p.call("load_item_proto", ip), "load_item_proto: %s" % p.call("get_last_error"))
|
||||
_ck(p.call("item_count") > 1000, "item_count > 1000 (%d)" % p.call("item_count"))
|
||||
var yang: Dictionary = p.call("item", 1)
|
||||
_ck(yang.get("name", "") == "Yang", "item(1).name == Yang")
|
||||
|
||||
@@ -46,8 +46,8 @@ func _run() -> void:
|
||||
if ClassDB.class_exists("Metin2Proto"):
|
||||
proto = ClassDB.instantiate("Metin2Proto")
|
||||
get_root().add_child(proto)
|
||||
var proto_path := assets.path_join("locale/locale/en/item_proto")
|
||||
if not FileAccess.file_exists(proto_path) or not proto.call("load_item_proto", proto_path):
|
||||
var proto_path := AssetRoot.locale_file("item_proto")
|
||||
if proto_path == "" or not proto.call("load_item_proto", proto_path):
|
||||
proto = null
|
||||
var effects := EffectRegistry.new()
|
||||
effects.setup(assets)
|
||||
|
||||
@@ -355,11 +355,9 @@ func _rebuild_model() -> void:
|
||||
if _proto == null and ClassDB.class_exists("Metin2Proto"):
|
||||
_proto = ClassDB.instantiate("Metin2Proto")
|
||||
add_child(_proto)
|
||||
for lang in ["en", "common"]:
|
||||
var ip := _assets.path_join("locale/locale/%s/item_proto" % lang)
|
||||
if FileAccess.file_exists(ip):
|
||||
_proto.call("load_item_proto", ip)
|
||||
break
|
||||
var ip := AssetRoot.locale_file("item_proto")
|
||||
if ip != "":
|
||||
_proto.call("load_item_proto", ip)
|
||||
var equip := EquipModel.new()
|
||||
pv.add_child(equip)
|
||||
equip.setup_remote(null, func() -> Node: return pv, _assets, job,
|
||||
|
||||
Reference in New Issue
Block a user