Fix ESC-crashes-the-game, avatar facing backward, invisible build stamp
checkpoint / checkpoint (push) Successful in 1m47s
checkpoint / checkpoint (push) Successful in 1m47s
ESC: player.gd changed scene directly while game.gd._exit_tree closed the socket, which re-fired server_disconnected -> _on_server_lost ran mid- teardown (add_child on an exiting node, await on a null get_tree(), a second change_scene_to_file). Moved ESC to game.gd as one ordered path: _leave_world() sets a _leaving guard, disconnects the _on_server_lost handlers before closing the connection, then changes scene. _on_server_lost and _exit_tree now respect the guard. Verified headless: ESC -> world_list, no errors; 2p multiplayer still connects. Avatar faced +Z but a Godot node's forward is -Z, so walking forward showed the character its own face. Rebuilt the placeholder facing -Z (face plates, eyes, jaw, toes, and front-mounted attachments all moved to negative Z; backpack/ponytail to +Z). The procedural walk was already written for -Z forward, so it's unchanged. Build stamp was there but tiny/low-contrast bottom-left -- bumped to 20px opaque yellow with a heavy outline, moved to the top-left strip. limb_animator: guard _process with is_instance_valid(_skel) so a frame landing during avatar teardown can't touch a freed skeleton. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015jtxAiu5dAW4WxCNdGkrkJ
This commit is contained in:
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -42,7 +42,7 @@ func setup(skeleton: Skeleton3D) -> void:
|
|||||||
|
|
||||||
|
|
||||||
func _process(delta: float) -> void:
|
func _process(delta: float) -> void:
|
||||||
if _skel == null or _idx.is_empty():
|
if not is_instance_valid(_skel) or _idx.is_empty():
|
||||||
return
|
return
|
||||||
|
|
||||||
var moving := speed > MOVE_EPS
|
var moving := speed > MOVE_EPS
|
||||||
|
|||||||
+51
-18
@@ -22,6 +22,7 @@ const CORRECTION_THRESHOLD := 0.6
|
|||||||
|
|
||||||
var _net: NetworkClient = null
|
var _net: NetworkClient = null
|
||||||
var _networked := false
|
var _networked := false
|
||||||
|
var _leaving := false # a scene change is already in flight
|
||||||
var _aligned := false # snapped to the server spawn yet?
|
var _aligned := false # snapped to the server spawn yet?
|
||||||
var _remotes: Dictionary = {} # peer_id -> Node3D
|
var _remotes: Dictionary = {} # peer_id -> Node3D
|
||||||
var _remote_prev: Dictionary = {} # peer_id -> Vector3 (last snapshot position)
|
var _remote_prev: Dictionary = {} # peer_id -> Vector3 (last snapshot position)
|
||||||
@@ -34,6 +35,37 @@ func _ready() -> void:
|
|||||||
_setup_networked()
|
_setup_networked()
|
||||||
|
|
||||||
|
|
||||||
|
func _unhandled_input(event: InputEvent) -> void:
|
||||||
|
# ESC leaves the world back to the browser. Handled here (the scene root
|
||||||
|
# that owns the connection) rather than in player.gd so the teardown is
|
||||||
|
# a single ordered path -- doing it from two places raced _on_server_lost
|
||||||
|
# against the scene change and looked like a crash.
|
||||||
|
if event.is_action_pressed("ui_cancel"):
|
||||||
|
get_viewport().set_input_as_handled()
|
||||||
|
_leave_world()
|
||||||
|
|
||||||
|
|
||||||
|
func _leave_world() -> void:
|
||||||
|
if _leaving:
|
||||||
|
return
|
||||||
|
_leaving = true
|
||||||
|
Input.mouse_mode = Input.MOUSE_MODE_VISIBLE
|
||||||
|
_teardown_net()
|
||||||
|
Session.clear()
|
||||||
|
get_tree().change_scene_to_file("res://scenes/world_list.tscn")
|
||||||
|
|
||||||
|
|
||||||
|
## Close the ENet connection and stop it from calling us back mid-teardown.
|
||||||
|
func _teardown_net() -> void:
|
||||||
|
if _net == null or not is_instance_valid(_net):
|
||||||
|
return
|
||||||
|
if _net.server_disconnected.is_connected(_on_server_lost):
|
||||||
|
_net.server_disconnected.disconnect(_on_server_lost)
|
||||||
|
if _net.connection_failed.is_connected(_on_server_lost):
|
||||||
|
_net.connection_failed.disconnect(_on_server_lost)
|
||||||
|
_net.disconnect_from_server()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
static func _build_label() -> String:
|
static func _build_label() -> String:
|
||||||
var f := FileAccess.open("res://assets/build_info.json", FileAccess.READ)
|
var f := FileAccess.open("res://assets/build_info.json", FileAccess.READ)
|
||||||
@@ -51,30 +83,28 @@ func _show_build_stamp() -> void:
|
|||||||
add_child(layer)
|
add_child(layer)
|
||||||
var lbl := Label.new()
|
var lbl := Label.new()
|
||||||
lbl.text = "build %s" % _build_label()
|
lbl.text = "build %s" % _build_label()
|
||||||
lbl.add_theme_color_override("font_color", Color(1, 1, 0.45))
|
lbl.add_theme_color_override("font_color", Color(1, 1, 0.35))
|
||||||
lbl.add_theme_color_override("font_outline_color", Color(0, 0, 0, 0.85))
|
lbl.add_theme_color_override("font_outline_color", Color(0, 0, 0, 1))
|
||||||
lbl.add_theme_constant_override("outline_size", 4)
|
lbl.add_theme_constant_override("outline_size", 6)
|
||||||
lbl.add_theme_font_size_override("font_size", 14)
|
lbl.add_theme_font_size_override("font_size", 20)
|
||||||
# Anchor a full-width strip to the bottom of the screen and left-align the
|
# Full-width strip pinned to the TOP-left. (A bare preset + .position on a
|
||||||
# text in it. (A bare preset + .position on a zero-size Label collapses the
|
# zero-size Label collapses the rect to negative width and nothing draws --
|
||||||
# rect to negative width and nothing draws -- which is why this never showed.)
|
# which is why this never showed; explicit offsets fix it.)
|
||||||
lbl.set_anchors_preset(Control.PRESET_BOTTOM_WIDE)
|
lbl.set_anchors_preset(Control.PRESET_TOP_WIDE)
|
||||||
lbl.horizontal_alignment = HORIZONTAL_ALIGNMENT_LEFT
|
lbl.horizontal_alignment = HORIZONTAL_ALIGNMENT_LEFT
|
||||||
lbl.vertical_alignment = VERTICAL_ALIGNMENT_BOTTOM
|
lbl.offset_left = 12
|
||||||
lbl.offset_left = 8
|
lbl.offset_right = -12
|
||||||
lbl.offset_right = -8
|
lbl.offset_top = 8
|
||||||
lbl.offset_top = -30
|
lbl.offset_bottom = 40
|
||||||
lbl.offset_bottom = -6
|
|
||||||
lbl.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
lbl.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
||||||
layer.add_child(lbl)
|
layer.add_child(lbl)
|
||||||
|
|
||||||
|
|
||||||
func _exit_tree() -> void:
|
func _exit_tree() -> void:
|
||||||
# Leaving the world -- close the ENet connection cleanly so the server
|
# Backstop for any exit path that didn't go through _leave_world (e.g. the
|
||||||
# fires peer_disconnected and removes our player (otherwise everyone else
|
# app quitting): close the ENet connection so the server fires
|
||||||
# keeps seeing a frozen "ghost" of us).
|
# peer_disconnected and drops our player instead of leaving a ghost.
|
||||||
if _net != null and is_instance_valid(_net):
|
_teardown_net()
|
||||||
_net.disconnect_from_server()
|
|
||||||
|
|
||||||
|
|
||||||
func _setup_networked() -> void:
|
func _setup_networked() -> void:
|
||||||
@@ -205,6 +235,9 @@ func _all_mesh_instances(node: Node, out: Array = []) -> Array:
|
|||||||
|
|
||||||
|
|
||||||
func _on_server_lost() -> void:
|
func _on_server_lost() -> void:
|
||||||
|
if _leaving:
|
||||||
|
return
|
||||||
|
_leaving = true
|
||||||
_networked = false
|
_networked = false
|
||||||
_show_status("Disconnected from the server. Returning to worlds…")
|
_show_status("Disconnected from the server. Returning to worlds…")
|
||||||
await get_tree().create_timer(1.5).timeout
|
await get_tree().create_timer(1.5).timeout
|
||||||
|
|||||||
+2
-5
@@ -128,11 +128,8 @@ func _unhandled_input(event: InputEvent) -> void:
|
|||||||
_update_input_direction(key)
|
_update_input_direction(key)
|
||||||
elif not key.pressed:
|
elif not key.pressed:
|
||||||
_clear_input_direction(key)
|
_clear_input_direction(key)
|
||||||
# Escape exits the level back to the world browse list, mirroring
|
# ESC (leave the world) is handled by game.gd, the scene root that
|
||||||
# the Back button pattern already used in world_list.gd/main_menu.gd.
|
# owns the network connection, so the teardown is a single path.
|
||||||
if key.pressed and key.keycode == KEY_ESCAPE:
|
|
||||||
Input.mouse_mode = Input.MOUSE_MODE_VISIBLE
|
|
||||||
get_tree().change_scene_to_file("res://scenes/world_list.tscn")
|
|
||||||
elif event is InputEventMouseMotion and Input.mouse_mode == Input.MOUSE_MODE_CAPTURED:
|
elif event is InputEventMouseMotion and Input.mouse_mode == Input.MOUSE_MODE_CAPTURED:
|
||||||
_handle_mouse_look(event as InputEventMouseMotion)
|
_handle_mouse_look(event as InputEventMouseMotion)
|
||||||
|
|
||||||
|
|||||||
@@ -17,6 +17,8 @@ const OUT_DIR := "res://assets/avatars/"
|
|||||||
|
|
||||||
# Canonical rig -- must match avatar_loader.gd CANONICAL_BONES. Each entry:
|
# Canonical rig -- must match avatar_loader.gd CANONICAL_BONES. Each entry:
|
||||||
# name -> [parent_name, global_rest_position] (A-pose, arms down, Y-up, metres)
|
# name -> [parent_name, global_rest_position] (A-pose, arms down, Y-up, metres)
|
||||||
|
# The character faces -Z (Godot's node "forward"), so the face, toes and
|
||||||
|
# front-mounted attachments live at negative Z.
|
||||||
const BONES := {
|
const BONES := {
|
||||||
"Root": ["", Vector3(0.00, 0.00, 0.00)],
|
"Root": ["", Vector3(0.00, 0.00, 0.00)],
|
||||||
"Hips": ["Root", Vector3(0.00, 0.76, 0.00)],
|
"Hips": ["Root", Vector3(0.00, 0.76, 0.00)],
|
||||||
@@ -25,9 +27,9 @@ const BONES := {
|
|||||||
"Neck": ["Chest", Vector3(0.00, 1.50, 0.00)],
|
"Neck": ["Chest", Vector3(0.00, 1.50, 0.00)],
|
||||||
"Head": ["Neck", Vector3(0.00, 1.58, 0.00)],
|
"Head": ["Neck", Vector3(0.00, 1.58, 0.00)],
|
||||||
"HeadTop": ["Head", Vector3(0.00, 2.00, 0.00)],
|
"HeadTop": ["Head", Vector3(0.00, 2.00, 0.00)],
|
||||||
"Jaw": ["Head", Vector3(0.00, 1.64, 0.16)],
|
"Jaw": ["Head", Vector3(0.00, 1.64, -0.16)],
|
||||||
"LeftEye": ["Head", Vector3(0.12, 1.76, 0.27)],
|
"LeftEye": ["Head", Vector3(0.12, 1.76, -0.27)],
|
||||||
"RightEye": ["Head", Vector3(-0.12, 1.76, 0.27)],
|
"RightEye": ["Head", Vector3(-0.12, 1.76, -0.27)],
|
||||||
"LeftShoulder": ["Chest", Vector3(0.42, 1.50, 0.00)],
|
"LeftShoulder": ["Chest", Vector3(0.42, 1.50, 0.00)],
|
||||||
"LeftUpperArm": ["LeftShoulder", Vector3(0.60, 1.44, 0.00)],
|
"LeftUpperArm": ["LeftShoulder", Vector3(0.60, 1.44, 0.00)],
|
||||||
"LeftLowerArm": ["LeftUpperArm", Vector3(0.60, 1.14, 0.00)],
|
"LeftLowerArm": ["LeftUpperArm", Vector3(0.60, 1.14, 0.00)],
|
||||||
@@ -38,10 +40,10 @@ const BONES := {
|
|||||||
"RightHand": ["RightLowerArm", Vector3(-0.60, 0.82, 0.00)],
|
"RightHand": ["RightLowerArm", Vector3(-0.60, 0.82, 0.00)],
|
||||||
"LeftUpperLeg": ["Hips", Vector3(0.21, 0.74, 0.00)],
|
"LeftUpperLeg": ["Hips", Vector3(0.21, 0.74, 0.00)],
|
||||||
"LeftLowerLeg": ["LeftUpperLeg", Vector3(0.21, 0.38, 0.00)],
|
"LeftLowerLeg": ["LeftUpperLeg", Vector3(0.21, 0.38, 0.00)],
|
||||||
"LeftFoot": ["LeftLowerLeg", Vector3(0.21, 0.05, 0.06)],
|
"LeftFoot": ["LeftLowerLeg", Vector3(0.21, 0.05, -0.06)],
|
||||||
"RightUpperLeg": ["Hips", Vector3(-0.21, 0.74, 0.00)],
|
"RightUpperLeg": ["Hips", Vector3(-0.21, 0.74, 0.00)],
|
||||||
"RightLowerLeg": ["RightUpperLeg", Vector3(-0.21, 0.38, 0.00)],
|
"RightLowerLeg": ["RightUpperLeg", Vector3(-0.21, 0.38, 0.00)],
|
||||||
"RightFoot": ["RightLowerLeg", Vector3(-0.21, 0.05, 0.06)],
|
"RightFoot": ["RightLowerLeg", Vector3(-0.21, 0.05, -0.06)],
|
||||||
}
|
}
|
||||||
|
|
||||||
# Order matters: parents before children.
|
# Order matters: parents before children.
|
||||||
@@ -70,10 +72,10 @@ const PARTS := [
|
|||||||
["RightUpperArm", Vector3(-0.58, 1.14, 0.00), Vector3(0.34, 0.78, 0.40), C_SKIN],
|
["RightUpperArm", Vector3(-0.58, 1.14, 0.00), Vector3(0.34, 0.78, 0.40), C_SKIN],
|
||||||
["LeftUpperLeg", Vector3(0.21, 0.38, 0.00), Vector3(0.40, 0.78, 0.40), C_PANTS],
|
["LeftUpperLeg", Vector3(0.21, 0.38, 0.00), Vector3(0.40, 0.78, 0.40), C_PANTS],
|
||||||
["RightUpperLeg", Vector3(-0.21, 0.38, 0.00), Vector3(0.40, 0.78, 0.40), C_PANTS],
|
["RightUpperLeg", Vector3(-0.21, 0.38, 0.00), Vector3(0.40, 0.78, 0.40), C_PANTS],
|
||||||
# painted-on face -- thin plates on the front of the head (+Z), rigged to Head
|
# painted-on face -- thin plates on the front of the head (-Z), rigged to Head
|
||||||
["Head", Vector3(0.13, 1.78, 0.28), Vector3(0.09, 0.12, 0.02), C_FACE],
|
["Head", Vector3(0.13, 1.78, -0.28), Vector3(0.09, 0.12, 0.02), C_FACE],
|
||||||
["Head", Vector3(-0.13, 1.78, 0.28), Vector3(0.09, 0.12, 0.02), C_FACE],
|
["Head", Vector3(-0.13, 1.78, -0.28), Vector3(0.09, 0.12, 0.02), C_FACE],
|
||||||
["Head", Vector3(0.00, 1.64, 0.28), Vector3(0.32, 0.05, 0.02), C_FACE],
|
["Head", Vector3(0.00, 1.64, -0.28), Vector3(0.32, 0.05, 0.02), C_FACE],
|
||||||
]
|
]
|
||||||
|
|
||||||
# Attachments: id -> [Mesh, local_offset_from_mount_bone, colour]
|
# Attachments: id -> [Mesh, local_offset_from_mount_bone, colour]
|
||||||
@@ -81,17 +83,17 @@ const PARTS := [
|
|||||||
const ATTACHMENTS := {
|
const ATTACHMENTS := {
|
||||||
"head_cyber_01": ["head", Vector3(0, 0.04, 0.0), Color(0.20, 0.85, 0.95)],
|
"head_cyber_01": ["head", Vector3(0, 0.04, 0.0), Color(0.20, 0.85, 0.95)],
|
||||||
"hair_mohawk_01": ["mohawk", Vector3(0, 0.0, 0.0), Color(0.90, 0.20, 0.35)],
|
"hair_mohawk_01": ["mohawk", Vector3(0, 0.0, 0.0), Color(0.90, 0.20, 0.35)],
|
||||||
"hair_ponytail_01": ["ponytail", Vector3(0, -0.06, -0.16), Color(0.45, 0.30, 0.15)],
|
"hair_ponytail_01": ["ponytail", Vector3(0, -0.06, 0.16), Color(0.45, 0.30, 0.15)],
|
||||||
"face_glasses_01": ["glasses", Vector3(0, 0.12, 0.16), Color(0.10, 0.10, 0.12)],
|
"face_glasses_01": ["glasses", Vector3(0, 0.12, -0.16), Color(0.10, 0.10, 0.12)],
|
||||||
"chest_bandit_01": ["torso", Vector3(0, -0.12, 0.0), Color(0.55, 0.20, 0.20)],
|
"chest_bandit_01": ["torso", Vector3(0, -0.12, 0.0), Color(0.55, 0.20, 0.20)],
|
||||||
"back_backpack_01": ["box", Vector3(0, -0.06, -0.28), Color(0.30, 0.35, 0.25)],
|
"back_backpack_01": ["box", Vector3(0, -0.06, 0.28), Color(0.30, 0.35, 0.25)],
|
||||||
"pants_cargo_01": ["hips", Vector3(0, -0.04, 0.0), Color(0.30, 0.35, 0.45)],
|
"pants_cargo_01": ["hips", Vector3(0, -0.04, 0.0), Color(0.30, 0.35, 0.45)],
|
||||||
"left_hand_glove_01": ["blob", Vector3(0, 0.0, 0.0), Color(0.15, 0.15, 0.18)],
|
"left_hand_glove_01": ["blob", Vector3(0, 0.0, 0.0), Color(0.15, 0.15, 0.18)],
|
||||||
"right_hand_glove_01": ["blob", Vector3(0, 0.0, 0.0), Color(0.15, 0.15, 0.18)],
|
"right_hand_glove_01": ["blob", Vector3(0, 0.0, 0.0), Color(0.15, 0.15, 0.18)],
|
||||||
"left_foot_boot_01": ["boot", Vector3(0, -0.02, 0.05), Color(0.12, 0.10, 0.09)],
|
"left_foot_boot_01": ["boot", Vector3(0, -0.02, -0.05), Color(0.12, 0.10, 0.09)],
|
||||||
"right_foot_boot_01": ["boot", Vector3(0, -0.02, 0.05), Color(0.12, 0.10, 0.09)],
|
"right_foot_boot_01": ["boot", Vector3(0, -0.02, -0.05), Color(0.12, 0.10, 0.09)],
|
||||||
"accessory_belt_01": ["belt", Vector3(0, 0.74, 0.0), Color(0.20, 0.18, 0.12)],
|
"accessory_belt_01": ["belt", Vector3(0, 0.74, 0.0), Color(0.20, 0.18, 0.12)],
|
||||||
"accessory_necklace_01": ["necklace", Vector3(0, 1.34, 0.10), Color(0.85, 0.75, 0.20)],
|
"accessory_necklace_01": ["necklace", Vector3(0, 1.34, -0.10), Color(0.85, 0.75, 0.20)],
|
||||||
}
|
}
|
||||||
|
|
||||||
var _fail := 0
|
var _fail := 0
|
||||||
|
|||||||
Reference in New Issue
Block a user