125 lines
4.0 KiB
GDScript
125 lines
4.0 KiB
GDScript
extends Node
|
|
|
|
const SYNCED_STATS: Array = [
|
|
"health", "shield", "max_health", "max_shield",
|
|
"role", "level", "xp", "xp_to_next",
|
|
"buff_damage", "buff_heal", "buff_shield",
|
|
]
|
|
const SYNC_INTERVAL: float = 0.10
|
|
|
|
var _entities: Dictionary = {}
|
|
var _player_cache: Dictionary = {}
|
|
var _dirty: Dictionary = {}
|
|
var _accum: float = 0.0
|
|
|
|
func _ready() -> void:
|
|
set_process(true)
|
|
|
|
func _process(delta: float) -> void:
|
|
_accum += delta
|
|
if _accum < SYNC_INTERVAL:
|
|
return
|
|
_accum = 0.0
|
|
if multiplayer.multiplayer_peer == null or multiplayer.multiplayer_peer is OfflineMultiplayerPeer or not multiplayer.is_server():
|
|
_dirty.clear()
|
|
return
|
|
if _dirty.is_empty():
|
|
return
|
|
for entity in _dirty.keys():
|
|
if not is_instance_valid(entity) or not entity.is_inside_tree():
|
|
continue
|
|
_sync_stats_batch.rpc(String(entity.get_path()), _dirty[entity])
|
|
_dirty.clear()
|
|
|
|
@rpc("authority", "unreliable_ordered")
|
|
func _sync_stats_batch(path_str: String, changes: Dictionary) -> void:
|
|
var entity := get_node_or_null(NodePath(path_str))
|
|
if entity == null or not entity in _entities:
|
|
return
|
|
for stat in changes.keys():
|
|
_entities[entity][stat] = changes[stat]
|
|
match stat:
|
|
"health":
|
|
EventBus.health_changed.emit(entity, changes[stat], _entities[entity].get("max_health", 100.0))
|
|
"shield":
|
|
EventBus.shield_changed.emit(entity, changes[stat], _entities[entity].get("max_shield", 0.0))
|
|
"role":
|
|
EventBus.role_changed.emit(entity, changes[stat])
|
|
"level":
|
|
EventBus.level_up.emit(entity, changes[stat])
|
|
"buff_damage", "buff_heal", "buff_shield":
|
|
EventBus.buff_changed.emit(entity, StringName(stat), changes[stat])
|
|
|
|
func register(entity: Node, base_resource: Resource) -> void:
|
|
if not is_instance_valid(entity):
|
|
return
|
|
var data: Dictionary = {}
|
|
for prop in base_resource.get_property_list():
|
|
var name: String = prop.name
|
|
if not (prop.usage & PROPERTY_USAGE_STORAGE):
|
|
continue
|
|
if name in ["resource_local_to_scene", "resource_path", "resource_name", "resource_scene_unique_id", "script"]:
|
|
continue
|
|
data[name] = base_resource.get(name)
|
|
data["health"] = data.get("max_health", 100.0)
|
|
data["shield"] = data.get("max_shield", 0.0)
|
|
data["shield_regen_timer"] = 0.0
|
|
data["base_resource"] = base_resource
|
|
_entities[entity] = data
|
|
EventBus.entity_registered.emit(entity)
|
|
|
|
func deregister(entity: Node) -> void:
|
|
if entity in _entities:
|
|
_entities.erase(entity)
|
|
EventBus.entity_deregistered.emit(entity)
|
|
|
|
func has(entity: Node) -> bool:
|
|
return entity in _entities
|
|
|
|
func get_stat(entity: Node, stat: String, default: Variant = 0.0) -> Variant:
|
|
if entity in _entities:
|
|
return _entities[entity].get(stat, default)
|
|
return default
|
|
|
|
func set_stat(entity: Node, stat: String, value: Variant) -> void:
|
|
if not entity in _entities:
|
|
return
|
|
var prev: Variant = _entities[entity].get(stat)
|
|
_entities[entity][stat] = value
|
|
if stat in SYNCED_STATS and prev != value and multiplayer.multiplayer_peer != null and not (multiplayer.multiplayer_peer is OfflineMultiplayerPeer) and multiplayer.is_server() and is_instance_valid(entity) and entity.is_inside_tree():
|
|
if not entity in _dirty:
|
|
_dirty[entity] = {}
|
|
_dirty[entity][stat] = value
|
|
|
|
|
|
func get_all(entity: Node) -> Dictionary:
|
|
return _entities.get(entity, {})
|
|
|
|
func entities() -> Array:
|
|
return _entities.keys()
|
|
|
|
func entities_in_group(group: StringName) -> Array:
|
|
var out: Array = []
|
|
for e in _entities.keys():
|
|
if is_instance_valid(e) and e.is_in_group(group):
|
|
out.append(e)
|
|
return out
|
|
|
|
func cache_player(peer_id: int, entity: Node) -> void:
|
|
if entity in _entities:
|
|
_player_cache[peer_id] = _entities[entity].duplicate(true)
|
|
|
|
func restore_player(peer_id: int, entity: Node) -> void:
|
|
if peer_id in _player_cache:
|
|
_entities[entity] = _player_cache[peer_id].duplicate(true)
|
|
|
|
func clear_player_cache(peer_id: int = -1) -> void:
|
|
if peer_id == -1:
|
|
_player_cache.clear()
|
|
else:
|
|
_player_cache.erase(peer_id)
|
|
|
|
func clear_all() -> void:
|
|
_entities.clear()
|
|
_player_cache.clear()
|