Fix 40250 classic login and world loading
This commit is contained in:
@@ -121,6 +121,11 @@ func setup(m2client: Node, assets_root: String,
|
||||
map_path: String = "OutdoorA1/metin2_map_a1") -> void:
|
||||
client = m2client
|
||||
_assets = assets_root
|
||||
# GC_MAIN_CHARACTER only carries the server-space coordinates, not the map
|
||||
# folder. The old fixed A1 fallback puts characters from A2/A3 outside the
|
||||
# rendered terrain, which looks like an empty map. Resolve the map from the
|
||||
# character position before constructing Metin2World.
|
||||
map_path = _resolve_map_path(map_path)
|
||||
_build_lighting()
|
||||
# 关键:setup 是协程。每个重活之间让出一帧,好让 M2Client._process 抽 socket
|
||||
# (否则整段 ~5s 同步阻塞会漏 PONG → 服务器 10s 后 "peer closed")。
|
||||
@@ -672,6 +677,87 @@ func _map_loaded() -> bool:
|
||||
var rep: Dictionary = world.call("get_load_report")
|
||||
return int(rep.get("map_size_x", 0)) > 0
|
||||
|
||||
func _resolve_map_path(requested: String) -> String:
|
||||
if client == null or _assets == "" or not client.has_method("get_main_vid"):
|
||||
return requested
|
||||
var vid := int(client.get_main_vid())
|
||||
if vid == 0 or not client.has_method("get_entity"):
|
||||
return requested
|
||||
var entity: Dictionary = client.get_entity(vid)
|
||||
var pos: Variant = entity.get("pos_cm", null)
|
||||
if not (pos is Vector3):
|
||||
return requested
|
||||
|
||||
var settings: Array[String] = []
|
||||
_collect_map_settings(_assets, settings)
|
||||
var best := requested
|
||||
var best_score := 1000000
|
||||
for setting_path: String in settings:
|
||||
var bounds := _map_bounds(setting_path)
|
||||
if bounds.is_empty():
|
||||
continue
|
||||
var base: Vector2 = bounds["base"]
|
||||
var size: Vector2i = bounds["size"]
|
||||
var max_x := base.x + float(size.x) * 25600.0
|
||||
var max_y := base.y + float(size.y) * 25600.0
|
||||
if pos.x < base.x or pos.x >= max_x or pos.y < base.y or pos.y >= max_y:
|
||||
continue
|
||||
var root := _assets.trim_suffix("/")
|
||||
var rel_setting := setting_path.substr(root.length() + 1)
|
||||
var candidate := rel_setting.get_base_dir()
|
||||
var lower := candidate.to_lower()
|
||||
# Prefer the normal outdoor map over duplicate patch/season copies when
|
||||
# several map packages advertise the same world-space rectangle.
|
||||
var score := candidate.split("/").size()
|
||||
if lower.begins_with("outdoor"):
|
||||
score -= 20
|
||||
if lower.begins_with("season") or lower.begins_with("metin2_patch"):
|
||||
score += 20
|
||||
if candidate == requested:
|
||||
score -= 1000
|
||||
if score < best_score:
|
||||
best_score = score
|
||||
best = candidate
|
||||
|
||||
if best != requested:
|
||||
print("[GameScene] map resolved by server position ", pos, " -> ", best)
|
||||
return best
|
||||
|
||||
func _collect_map_settings(dir_path: String, out: Array[String]) -> void:
|
||||
var dir := DirAccess.open(dir_path)
|
||||
if dir == null:
|
||||
return
|
||||
for file_name: String in dir.get_files():
|
||||
if file_name.to_lower() == "setting.txt":
|
||||
out.append(dir_path.path_join(file_name))
|
||||
for child: String in dir.get_directories():
|
||||
if child in [".godot", ".git", "build", "export"]:
|
||||
continue
|
||||
_collect_map_settings(dir_path.path_join(child), out)
|
||||
|
||||
func _map_bounds(setting_path: String) -> Dictionary:
|
||||
var f := FileAccess.open(setting_path, FileAccess.READ)
|
||||
if f == null:
|
||||
return {}
|
||||
var base := Vector2.ZERO
|
||||
var size := Vector2i.ZERO
|
||||
var have_base := false
|
||||
var have_size := false
|
||||
while not f.eof_reached():
|
||||
var fields := f.get_line().replace("\t", " ").strip_edges().split(" ", false)
|
||||
if fields.size() < 3:
|
||||
continue
|
||||
match String(fields[0]).to_lower():
|
||||
"baseposition":
|
||||
base = Vector2(float(fields[1]), float(fields[2]))
|
||||
have_base = true
|
||||
"mapsize":
|
||||
size = Vector2i(int(fields[1]), int(fields[2]))
|
||||
have_size = size.x > 0 and size.y > 0
|
||||
if not have_base or not have_size:
|
||||
return {}
|
||||
return {"base": base, "size": size}
|
||||
|
||||
func _build_lighting() -> void:
|
||||
var sun := DirectionalLight3D.new()
|
||||
sun.name = "FallbackSun"
|
||||
|
||||
Reference in New Issue
Block a user