Files
mtgodot-poc/project/testing/playable_probe.gd
T

116 lines
4.9 KiB
GDScript

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})