Implement playable Mac client and rendering validation

This commit is contained in:
shen
2026-09-11 14:58:22 +08:00
parent 1ab68bd06e
commit 374d4165d8
233 changed files with 6546 additions and 284 deletions
+43 -1
View File
@@ -303,6 +303,7 @@ func setup(m2client: Node, assets_root: String,
)
if net_play and net_play.has_signal("fishing_feedback") and net_play.has_signal("cannot_act"):
net_play.fishing_feedback.connect(func(code: String): net_play.cannot_act.emit(code))
quickbar.skill_cast_started.connect(_on_skill_cast_started)
if quickbar.has_signal("skill_activated"):
quickbar.skill_activated.connect(func(sid: int):
if skill_fx and player:
@@ -835,6 +836,32 @@ func _on_local_motion_event(type: int, effect: String, sound: String, pos: Vecto
9: # WARP —— 传送起点,不是投射物
pass
var _motion_effect_target: WeakRef
func _on_skill_cast_started(_skill: int, target_vid: int) -> void:
_motion_effect_target = null
var target: Node3D = null
if client and target_vid == int(client.get_main_vid()):
target = player
elif net_world and target_vid != 0:
target = net_world.node_for(target_vid)
if is_instance_valid(target):
_motion_effect_target = weakref(target)
func _on_local_motion_event_detailed(event: Dictionary) -> void:
# Keep sound/projectile dispatch exactly once, independent of visual routing.
if int(event.type) == 1 and String(event.effect) != "":
_on_local_motion_event(int(event.type), "", String(event.sound), event.pos)
if fx and is_instance_valid(player):
fx.spawn_motion(event, player, _mount)
elif int(event.type) == 10:
_on_local_motion_event(int(event.type), "", String(event.sound), event.pos)
var target: Node3D = _motion_effect_target.get_ref() if _motion_effect_target else null
if fx and is_instance_valid(target):
fx.spawn_target(event, target, _mount)
else:
_on_local_motion_event(int(event.type), String(event.effect), String(event.sound), event.pos)
# 一次弓挥击 / 弓技能起手 → 压一个待发 uSkill(对齐 AutoClear handler 的 Set())。
# 满了丢最旧的:真客户端每个动作必到 FLY 帧,headless / 缺 `.msa` 时不至无界增长。§3.6
func _queue_shot(skill: int) -> void:
@@ -936,6 +963,7 @@ func _on_warp(pos: Vector3, same_server: bool) -> void:
_place_player_at_net_pos(pos)
func _on_world_reset() -> void:
_motion_effect_target = null
_main_generation += 1
_main_view_key = ""
_main_attempts = 0
@@ -1249,6 +1277,18 @@ func set_entity_model_factory(factory: Callable) -> void:
if net_world:
net_world.set_model_factory(factory)
# Borrowed-node adapter for the packaged playable test. It is only exposed
# from an explicit test mode; the returned nodes must never be serialized or
# retained across _on_world_reset / AppFlow reconnect.
func get_playable_context() -> Dictionary:
var test_mode := OS.get_environment("MT_TEST_MODE")
if test_mode != "playable" and test_mode != "forest_render":
return {}
return {"scene_ready": _model_built and _map_loaded(), "map_path": map_path,
"net_play": net_play, "net_world": net_world, "world": world,
"player": player, "pc": pc, "hud": hud, "quickbar": quickbar,
"ground_items": ground_items, "skill_fx": skill_fx}
var _mob_view_cache := {} # race -> bool(该 race 是否有可用模型;失败就别再试)
var _remote_player_view_cache := {} # race -> bool(远端 PC 真模型可用性)
@@ -1518,7 +1558,9 @@ func _on_main_set(vid: int) -> void:
_model_built = true
if net_play:
net_play.player_view = pv
if pv.anim and pv.anim.has_signal("motion_event"):
if pv.anim and pv.anim.has_signal("motion_event_detailed"):
pv.anim.motion_event_detailed.connect(_on_local_motion_event_detailed)
elif pv.anim and pv.anim.has_signal("motion_event"):
pv.anim.motion_event.connect(_on_local_motion_event)
if pc and pc.has_signal("anim_state") and not pc.anim_state.is_connected(_on_main_anim_state):
pc.anim_state.connect(_on_main_anim_state)