Implement playable Mac client and rendering validation
This commit is contained in:
@@ -0,0 +1,115 @@
|
||||
class_name PlayableProbe
|
||||
extends Node
|
||||
|
||||
## 只读网络/场景观察器。所有服务端证据必须从 M2Client 信号或状态快照进入,
|
||||
## 本节点不调用移动、攻击、拾取、施法或其它会改变服务器状态的方法。
|
||||
|
||||
signal observed(kind: String, payload: Dictionary)
|
||||
|
||||
var client: Node
|
||||
var flow: Node
|
||||
var connection_epoch := 0
|
||||
var _connections: Array[Dictionary] = []
|
||||
|
||||
func setup(app_flow: Node, m2client: Node) -> void:
|
||||
flow = app_flow
|
||||
client = m2client
|
||||
if client == null:
|
||||
return
|
||||
_bind("entered_game", Callable(self, "_on_entered_game"))
|
||||
_bind("disconnected", Callable(self, "_on_disconnected"))
|
||||
_bind("char_list", Callable(self, "_on_char_list"))
|
||||
_bind("entity_spawned", Callable(self, "_on_entity_spawned"))
|
||||
_bind("entity_despawned", Callable(self, "_on_entity_despawned"))
|
||||
_bind("entity_moved", Callable(self, "_on_entity_moved"))
|
||||
_bind("entity_dead", Callable(self, "_on_entity_dead"))
|
||||
_bind("damage", Callable(self, "_on_damage"))
|
||||
_bind("vitals_changed", Callable(self, "_on_vitals"))
|
||||
_bind("ground_item_added", Callable(self, "_on_ground_item_added"))
|
||||
_bind("ground_item_removed", Callable(self, "_on_ground_item_removed"))
|
||||
_bind("item_picked_up", Callable(self, "_on_item_picked_up"))
|
||||
_bind("inventory_changed", Callable(self, "_on_inventory_changed"))
|
||||
_bind("effect_cue", Callable(self, "_on_effect_cue"))
|
||||
_bind("fly_cue", Callable(self, "_on_fly_cue"))
|
||||
_bind("fly_targeting", Callable(self, "_on_fly_targeting"))
|
||||
|
||||
func snapshot() -> Dictionary:
|
||||
var in_game: bool = client != null and client.has_method("is_in_game") and client.is_in_game()
|
||||
var main_vid := int(client.get_main_vid()) if client != null and client.has_method("get_main_vid") else 0
|
||||
var entity_count: int = client.get_entities().size() if client != null and client.has_method("get_entities") else 0
|
||||
var ground_count: int = client.get_ground_items().size() if client != null and client.has_method("get_ground_items") else 0
|
||||
var state := int(flow.state()) if flow != null and flow.has_method("state") else -1
|
||||
return {"in_game": in_game, "main_vid": main_vid, "entity_count": entity_count,
|
||||
"ground_item_count": ground_count, "app_state": state, "connection_epoch": connection_epoch}
|
||||
|
||||
func disconnect_all() -> void:
|
||||
for entry in _connections:
|
||||
var signal_name: String = entry.signal
|
||||
var callback: Callable = entry.callback
|
||||
if client != null and client.has_signal(signal_name) and client.is_connected(signal_name, callback):
|
||||
client.disconnect(signal_name, callback)
|
||||
_connections.clear()
|
||||
|
||||
func _exit_tree() -> void:
|
||||
disconnect_all()
|
||||
|
||||
func _bind(signal_name: String, callback: Callable) -> void:
|
||||
if client.has_signal(signal_name) and not client.is_connected(signal_name, callback):
|
||||
client.connect(signal_name, callback)
|
||||
_connections.append({"signal": signal_name, "callback": callback})
|
||||
|
||||
func _emit(kind: String, payload := {}) -> void:
|
||||
var data: Dictionary = payload.duplicate(true)
|
||||
data["connection_epoch"] = connection_epoch
|
||||
data["received_us"] = Time.get_ticks_usec()
|
||||
observed.emit(kind, data)
|
||||
|
||||
func _on_entered_game() -> void:
|
||||
connection_epoch += 1
|
||||
_emit("entered_game")
|
||||
|
||||
func _on_disconnected(reason: String) -> void:
|
||||
_emit("disconnected", {"reason": reason})
|
||||
|
||||
func _on_char_list(chars: Array) -> void:
|
||||
_emit("char_list", {"count": chars.size()})
|
||||
|
||||
func _on_entity_spawned(entity: Dictionary) -> void:
|
||||
_emit("entity_spawned", {"vid": int(entity.get("vid", 0)), "race": int(entity.get("race", 0)), "kind": int(entity.get("kind", -1))})
|
||||
|
||||
func _on_entity_despawned(vid: int) -> void:
|
||||
_emit("entity_despawned", {"vid": vid})
|
||||
|
||||
func _on_entity_moved(vid: int) -> void:
|
||||
_emit("entity_moved", {"vid": vid})
|
||||
|
||||
func _on_entity_dead(vid: int) -> void:
|
||||
_emit("entity_dead", {"vid": vid})
|
||||
|
||||
func _on_damage(vid: int, amount: int, flag: int) -> void:
|
||||
_emit("damage", {"vid": vid, "amount": amount, "flag": flag})
|
||||
|
||||
func _on_vitals(vid: int) -> void:
|
||||
_emit("vitals_changed", {"vid": vid})
|
||||
|
||||
func _on_ground_item_added(item: Dictionary) -> void:
|
||||
_emit("ground_item_added", {"vid": int(item.get("vid", 0)), "vnum": int(item.get("vnum", 0)), "owner": String(item.get("owner", ""))})
|
||||
|
||||
func _on_ground_item_removed(vid: int) -> void:
|
||||
_emit("ground_item_removed", {"vid": vid})
|
||||
|
||||
func _on_item_picked_up(vnum: int, count: int, source: String) -> void:
|
||||
_emit("item_picked_up", {"vnum": vnum, "count": count, "source": source})
|
||||
|
||||
func _on_inventory_changed(window: int, cell: int) -> void:
|
||||
_emit("inventory_changed", {"window": window, "cell": cell})
|
||||
|
||||
func _on_effect_cue(vid: int, name: String, special: int) -> void:
|
||||
_emit("effect_cue", {"vid": vid, "name": name, "special": special})
|
||||
|
||||
func _on_fly_cue(kind: int, start_vid: int, end_vid: int) -> void:
|
||||
_emit("fly_cue", {"type": kind, "start_vid": start_vid, "end_vid": end_vid})
|
||||
|
||||
func _on_fly_targeting(shooter_vid: int, target_vid: int, target_cm: Vector2, append: bool) -> void:
|
||||
_emit("fly_targeting", {"shooter_vid": shooter_vid, "target_vid": target_vid,
|
||||
"target_cm": [target_cm.x, target_cm.y], "append": append})
|
||||
Reference in New Issue
Block a user