Refresh menus chat and player expression

This commit is contained in:
Alexander Sellite 2026-08-08 00:34:06 -04:00
parent 2a5e0a8f26
commit 0d50c324e9
127 changed files with 3222 additions and 221 deletions

View file

@ -79,6 +79,21 @@ func send_local_message(body: String) -> bool:
return true
func broadcast_system_message(body: String) -> bool:
if _session == null or not _session.is_host():
return false
var clean := NetworkChatProtocol.sanitize_body(body)
if clean.is_empty():
return false
_broadcast(_make_message(
NetworkChatProtocol.Kind.SYSTEM,
0,
"",
clean,
))
return true
func get_history() -> Array[Dictionary]:
var result: Array[Dictionary] = []
for message: Dictionary in _history:

View file

@ -2,8 +2,13 @@ class_name NetworkProfilePreferences
extends Node
const FORMAT_VERSION: int = 1
const VoiceProfilesType = preload(
"res://player/animalese_voice_profiles.gd"
)
var profile_id: String = ""
var display_name: String = "Player"
var voice_id: String = VoiceProfilesType.DEFAULT_ID
var speech_speed_id: String = VoiceProfilesType.DEFAULT_SPEED_ID
var created_at_unix: int = 0
var _profile_path := ""
var _expected_hash := ""
@ -48,19 +53,39 @@ func load_or_create() -> bool:
Time.get_ticks_usec(),
]
display_name = "Player"
voice_id = VoiceProfilesType.DEFAULT_ID
speech_speed_id = VoiceProfilesType.DEFAULT_SPEED_ID
created_at_unix = int(Time.get_unix_time_from_system())
return _save_atomic()
func set_display_name(value: String) -> bool:
return set_profile_identity(value, voice_id, speech_speed_id)
func set_profile_identity(
value: String,
selected_voice_id: String,
selected_speech_speed_id: String,
) -> bool:
var clean_name: String = value.strip_edges()
if not is_valid_display_name(clean_name):
if (
not is_valid_display_name(clean_name)
or not VoiceProfilesType.is_valid(selected_voice_id)
or not VoiceProfilesType.is_valid_speed(selected_speech_speed_id)
):
return false
var previous: String = display_name
var previous_name: String = display_name
var previous_voice_id: String = voice_id
var previous_speech_speed_id: String = speech_speed_id
display_name = clean_name
voice_id = selected_voice_id
speech_speed_id = selected_speech_speed_id
if _save_atomic():
return true
display_name = previous
display_name = previous_name
voice_id = previous_voice_id
speech_speed_id = previous_speech_speed_id
return false
@ -109,6 +134,12 @@ func _load_existing() -> bool:
return false
profile_id = loaded_id
display_name = loaded_name
voice_id = VoiceProfilesType.sanitized_id(
str(data.get("voice_id", VoiceProfilesType.DEFAULT_ID))
)
speech_speed_id = VoiceProfilesType.sanitized_speed_id(
str(data.get("speech_speed_id", VoiceProfilesType.DEFAULT_SPEED_ID))
)
created_at_unix = int(data["created_at_unix"])
return true
@ -118,6 +149,8 @@ func _save_atomic() -> bool:
"format_version": FORMAT_VERSION,
"profile_id": profile_id,
"display_name": display_name,
"voice_id": voice_id,
"speech_speed_id": speech_speed_id,
"created_at_unix": created_at_unix,
}
var result := PortableFileGuard.write_guarded(

View file

@ -1,6 +1,10 @@
class_name NetworkProfileService
extends Node
const VoiceProfilesType = preload(
"res://player/animalese_voice_profiles.gd"
)
signal conflict_result(
request_id: String,
has_conflict: bool,
@ -18,6 +22,8 @@ var _preferences: NetworkProfilePreferences
var _appearance_store: PlayerAppearanceStore
var _spawn_service: PlayerSpawnService
var _pending_apply: Dictionary[String, Dictionary] = {}
var _pending_voice_ids: Dictionary[String, String] = {}
var _pending_speech_speed_ids: Dictionary[String, String] = {}
var _host_pending_apply: Dictionary[String, Dictionary] = {}
var _latest_check_id: String = ""
var _latest_check_name: String = ""
@ -40,6 +46,7 @@ func setup(
_session.join_authenticated.connect(_on_join_authenticated)
_session.state_changed.connect(_on_session_state_changed)
_apply_to_avatar(1, _appearance_store.get_snapshot())
_apply_local_voice_to_avatar()
func get_persisted_name() -> String:
@ -50,6 +57,14 @@ func get_persisted_appearance() -> Dictionary:
return _appearance_store.get_snapshot()
func get_persisted_voice_id() -> String:
return _preferences.voice_id
func get_persisted_speech_speed_id() -> String:
return _preferences.speech_speed_id
func get_identity_fingerprint() -> String:
return (
_session.get_local_identity_fingerprint()
@ -87,11 +102,15 @@ func apply_profile(
display_name: String,
appearance: Dictionary,
use_anyway: bool,
voice_id: String = VoiceProfilesType.DEFAULT_ID,
speech_speed_id: String = VoiceProfilesType.DEFAULT_SPEED_ID,
) -> bool:
var clean_name := display_name.strip_edges()
if (
not NetworkProfilePreferences.is_valid_display_name(clean_name)
or not CharacterCustomizationCatalog.validate_snapshot(appearance)
or not VoiceProfilesType.is_valid(voice_id)
or not VoiceProfilesType.is_valid_speed(speech_speed_id)
):
apply_finished.emit(false, "Check the player name and appearance choices.")
return false
@ -108,6 +127,8 @@ func apply_profile(
"profile_update", NetworkProfileProtocol.signature_fields(request)
)
_pending_apply[request_id] = request
_pending_voice_ids[request_id] = voice_id
_pending_speech_speed_ids[request_id] = speech_speed_id
if _session == null or not _session.is_gameplay_session_active():
_apply_local_result(request_id, true, "", false, PackedStringArray())
elif _session.is_host():
@ -264,20 +285,46 @@ func _apply_local_result(
return
if not accepted:
_pending_apply.erase(request_id)
_pending_voice_ids.erase(request_id)
_pending_speech_speed_ids.erase(request_id)
conflict_result.emit(request_id, conflict, suggestions)
apply_finished.emit(false, message)
return
var previous_name := _preferences.display_name
if not _preferences.set_display_name(str(request["display_name"])):
var previous_voice_id := _preferences.voice_id
var previous_speech_speed_id := _preferences.speech_speed_id
var requested_voice_id: String = _pending_voice_ids.get(
request_id,
VoiceProfilesType.DEFAULT_ID,
)
var requested_speech_speed_id: String = _pending_speech_speed_ids.get(
request_id,
VoiceProfilesType.DEFAULT_SPEED_ID,
)
if not _preferences.set_profile_identity(
str(request["display_name"]),
requested_voice_id,
requested_speech_speed_id,
):
_pending_apply.erase(request_id)
_pending_voice_ids.erase(request_id)
_pending_speech_speed_ids.erase(request_id)
apply_finished.emit(false, "Profile could not be saved.")
return
if not _appearance_store.save_snapshot(request["appearance"]):
_preferences.set_display_name(previous_name)
_preferences.set_profile_identity(
previous_name,
previous_voice_id,
previous_speech_speed_id,
)
_pending_apply.erase(request_id)
_pending_voice_ids.erase(request_id)
_pending_speech_speed_ids.erase(request_id)
apply_finished.emit(false, "Profile could not be saved.")
return
_pending_apply.erase(request_id)
_pending_voice_ids.erase(request_id)
_pending_speech_speed_ids.erase(request_id)
if _session != null and _session.is_gameplay_session_active():
if _session.is_host():
_session.apply_canonical_profile(
@ -304,6 +351,7 @@ func _apply_local_result(
_session.get_local_peer_id() if _session != null else 1,
_appearance_store.get_snapshot(),
)
_apply_local_voice_to_avatar()
apply_finished.emit(true, "Profile saved.")
@ -361,6 +409,7 @@ func _on_join_authenticated() -> void:
_apply_to_avatar(
_session.get_local_peer_id(), _appearance_store.get_snapshot()
)
_apply_local_voice_to_avatar()
func _on_peer_removed(_peer_id: int) -> void:
@ -400,6 +449,8 @@ func _refresh_latest_check() -> void:
func _on_session_state_changed(state: NetworkSession.State) -> void:
if state == NetworkSession.State.INACTIVE:
_pending_apply.clear()
_pending_voice_ids.clear()
_pending_speech_speed_ids.clear()
_host_pending_apply.clear()
_latest_check_id = ""
_latest_check_name = ""
@ -465,5 +516,16 @@ func _apply_to_avatar(peer_id: int, appearance: Dictionary) -> void:
avatar.apply_appearance_snapshot(appearance)
func _apply_local_voice_to_avatar() -> void:
if _spawn_service == null:
return
var local_peer_id := (
_session.get_local_peer_id() if _session != null else 1
)
var avatar := _spawn_service.get_avatar(local_peer_id)
if avatar != null:
avatar.apply_animalese_voice_id(_preferences.voice_id)
func _new_id() -> String:
return Crypto.new().generate_random_bytes(16).hex_encode()

View file

@ -17,6 +17,12 @@ func setup(session: NetworkSession, world_time: WorldTimeService) -> void:
_world_time = world_time
_session.state_changed.connect(_on_session_state_changed)
_session.peer_authenticated.connect(_on_peer_authenticated)
if not _world_time.authoritative_time_set.is_connected(
_on_authoritative_time_set
):
_world_time.authoritative_time_set.connect(
_on_authoritative_time_set
)
set_process(true)
@ -82,6 +88,13 @@ func _on_peer_authenticated(peer_id: int, _display_name: String) -> void:
_send_snapshot(peer_id)
func _on_authoritative_time_set(_time_hours: float) -> void:
if _session == null or not _session.is_host():
return
_sync_elapsed = 0.0
_broadcast_snapshot()
func _broadcast_snapshot() -> void:
if _session == null or not _session.is_host():
return