fix alot of shit for webrtc to work

This commit is contained in:
2026-01-17 10:19:51 +01:00
parent f71b510cfc
commit eb718fa990
68 changed files with 6616 additions and 917 deletions

View File

@@ -17,6 +17,7 @@ var is_being_held: bool = false
var held_by_player = null
var is_frozen: bool = false
var thrown_by_player = null # Track who threw this box
var is_broken: bool = false
# Physics for thrown objects
var throw_velocity: Vector2 = Vector2.ZERO
@@ -47,6 +48,17 @@ func _ready():
collision_layer = 2 # Layer 2 for objects
collision_mask = 1 | 2 | 4 # Collide with players, other objects, and walls
# Ensure deterministic name for network sync
if has_meta("object_index") and not name.begins_with("InteractableObject_"):
name = "InteractableObject_%d" % get_meta("object_index")
elif name.begins_with("InteractableObject_"):
# Ensure meta matches name if it already has a consistent name
var index_str = name.substr(20)
if index_str.is_valid_int():
var name_index = index_str.to_int()
if not has_meta("object_index") or get_meta("object_index") != name_index:
set_meta("object_index", name_index)
# No gravity in top-down
motion_mode = MOTION_MODE_FLOATING
@@ -164,9 +176,14 @@ func _handle_air_collision():
# Box breaks (only if destroyable)
if is_destroyable:
# Sync break to OTHER clients via RPC BEFORE breaking locally
# Use game_world to route the RPC to avoid node path resolution issues
if multiplayer.has_multiplayer_peer() and multiplayer.is_server():
var game_world = get_tree().get_first_node_in_group("game_world")
if game_world and game_world.has_method("_rpc_to_ready_peers"):
game_world._rpc_to_ready_peers("_sync_object_break", [name])
_break_into_pieces()
if multiplayer.has_multiplayer_peer():
_sync_break.rpc()
return
@@ -181,11 +198,14 @@ func _handle_air_collision():
# Hit a player! Break locally and sync to others (only if destroyable)
if is_destroyable:
_break_into_pieces()
# Sync break to OTHER clients via RPC BEFORE breaking locally
# Use game_world to route the RPC to avoid node path resolution issues
if multiplayer.has_multiplayer_peer() and multiplayer.is_server():
var game_world = get_tree().get_first_node_in_group("game_world")
if game_world and game_world.has_method("_rpc_to_ready_peers"):
game_world._rpc_to_ready_peers("_sync_object_break", [name])
# Sync break to OTHER clients
if multiplayer.has_multiplayer_peer():
_sync_break.rpc()
_break_into_pieces()
# Damage and knockback player using RPC
# Pass the thrower's position for accurate direction
@@ -214,24 +234,28 @@ func _handle_air_collision():
# Hit another box! Break both locally (only if destroyable)
if is_destroyable:
# Sync break to OTHER clients via RPC BEFORE breaking locally
# Use game_world to route the RPC to avoid node path resolution issues
if multiplayer.has_multiplayer_peer() and multiplayer.is_server():
var game_world = get_tree().get_first_node_in_group("game_world")
if game_world and game_world.has_method("_rpc_to_ready_peers"):
game_world._rpc_to_ready_peers("_sync_object_break", [name])
# Tell the other box to break too
if collider.has_method("can_be_destroyed") and collider.can_be_destroyed():
game_world._rpc_to_ready_peers("_sync_object_break", [collider.name])
_break_into_pieces()
if collider.has_method("_break_into_pieces") and collider.has_method("can_be_destroyed") and collider.can_be_destroyed():
collider._break_into_pieces()
# Sync break to OTHER clients
if multiplayer.has_multiplayer_peer():
_sync_break.rpc()
# Tell the other box to break too
if collider.has_method("_sync_break") and collider.has_method("can_be_destroyed") and collider.can_be_destroyed():
collider._sync_break.rpc()
print(name, " hit another box!")
return
func _break_into_pieces():
func _break_into_pieces(silent: bool = false):
# Only break if destroyable
if not is_destroyable:
if not is_destroyable or is_broken:
return
is_broken = true
var sprite_texture = $Sprite2D.texture
var frame_width = sprite_texture.get_width() / $Sprite2D.hframes
@@ -254,27 +278,28 @@ func _break_into_pieces():
Rect2(frame_x + frame_width / 2, frame_y + frame_height / 2, frame_width / 2, frame_height / 2) # Bottom-right
]
for i in range(4):
var tp = tileParticleScene.instantiate() as CharacterBody2D
var spr2D = tp.get_node("Sprite2D") as Sprite2D
tp.global_position = global_position
if not silent:
for i in range(4):
var tp = tileParticleScene.instantiate() as CharacterBody2D
var spr2D = tp.get_node("Sprite2D") as Sprite2D
tp.global_position = global_position
# Set up the sprite's texture and region
spr2D.texture = sprite_texture
spr2D.region_enabled = true
spr2D.region_rect = regions[i]
# Add some randomness to the velocity
var speed = randf_range(170, 200)
var dir = directions[i] + Vector2(randf_range(-0.2, 0.2), randf_range(-0.2, 0.2))
tp.velocity = dir * speed
# Add some rotation
tp.angular_velocity = randf_range(-7, 7)
get_parent().call_deferred("add_child", tp)
# Set up the sprite's texture and region
spr2D.texture = sprite_texture
spr2D.region_enabled = true
spr2D.region_rect = regions[i]
# Add some randomness to the velocity
var speed = randf_range(170, 200)
var dir = directions[i] + Vector2(randf_range(-0.2, 0.2), randf_range(-0.2, 0.2))
tp.velocity = dir * speed
# Add some rotation
tp.angular_velocity = randf_range(-7, 7)
get_parent().call_deferred("add_child", tp)
play_destroy_sound()
play_destroy_sound()
self.set_deferred("collision_layer", 0)
$Shadow.visible = false
$Sprite2DAbove.visible = false
@@ -292,18 +317,47 @@ func _break_into_pieces():
ItemLootHelper.spawn_item_loot(item, global_position, entities_node, game_world)
print(name, " dropped item: ", item.item_name, " when broken")
if ($SfxShatter.playing):
await $SfxShatter.finished
if ($SfxBreakCrate.playing):
await $SfxBreakCrate.finished
if not silent:
if ($SfxShatter.playing):
await $SfxShatter.finished
if ($SfxBreakCrate.playing):
await $SfxBreakCrate.finished
# Remove self
queue_free()
func can_be_grabbed() -> bool:
return is_grabbable and not is_being_held
func _get_configured_object_type() -> String:
# Prefer the configured type from dungeon data if available
var idx = -1
if name.begins_with("InteractableObject_"):
var index_str = name.substr(20)
if index_str.is_valid_int():
idx = index_str.to_int()
elif has_meta("object_index"):
idx = get_meta("object_index")
if idx >= 0:
var game_world = get_tree().get_first_node_in_group("game_world")
if game_world and "dungeon_data" in game_world and game_world.dungeon_data.has("interactable_objects"):
var objects = game_world.dungeon_data.interactable_objects
if idx < objects.size():
var obj_data = objects[idx]
if obj_data is Dictionary and obj_data.has("type"):
return obj_data.type
return object_type
func can_be_lifted() -> bool:
# Can be lifted if it's liftable (being held is OK - we're checking if it CAN be lifted)
var resolved_type = object_type
if resolved_type == "":
resolved_type = _get_configured_object_type()
if resolved_type in ["Box", "Pot", "LiftableBarrel"]:
return true
if resolved_type in ["Chest", "Pillar", "PushableBarrel", "PushableHighBox"]:
return false
return is_liftable
func can_be_thrown() -> bool:
@@ -321,8 +375,16 @@ func on_grabbed(by_player):
# Client - send request to server
if by_player:
var player_peer_id = by_player.get_multiplayer_authority()
print("Chest: Client sending RPC to open chest, player_peer_id: ", player_peer_id)
_request_chest_open.rpc_id(1, player_peer_id)
# Use consistent object name based on object_index to avoid NodePath issues
var chest_name = name
if has_meta("object_index"):
chest_name = "InteractableObject_%d" % get_meta("object_index")
print("Chest: Client sending RPC to open chest, player_peer_id: ", player_peer_id, " chest_name: ", chest_name)
var game_world = get_tree().get_first_node_in_group("game_world")
if game_world and game_world.has_method("_request_chest_open_by_name"):
game_world._request_chest_open_by_name.rpc_id(1, chest_name, player_peer_id)
else:
push_warning("Chest: GameWorld not ready, cannot send chest open request for " + chest_name)
else:
# Server or single player - open directly
_open_chest(by_player)
@@ -397,10 +459,10 @@ func _sync_box_state(pos: Vector2, vel: Vector2, z_pos: float, z_vel: float, air
is_airborne = airborne
@rpc("any_peer", "reliable")
func _sync_break():
func _sync_break(silent: bool = false):
# Sync break to all clients including server (called by whoever breaks the box)
if not is_queued_for_deletion():
_break_into_pieces()
if not is_queued_for_deletion() and not is_broken:
_break_into_pieces(silent)
# Object type setup functions
func setup_pot():
@@ -448,7 +510,21 @@ func setup_box():
var box_frames = [7, 26]
if sprite:
sprite.frame = box_frames[randi() % box_frames.size()]
# Use deterministic randomness based on dungeon seed and position
# This ensures host and clients get the same box variant
var box_seed = 0
var game_world = get_tree().get_first_node_in_group("game_world")
if game_world and "dungeon_seed" in game_world:
box_seed = game_world.dungeon_seed
# Add position and object_index to seed to make each box unique but deterministic
box_seed += int(global_position.x) * 1000 + int(global_position.y)
if has_meta("object_index"):
box_seed += get_meta("object_index") * 10000
var rng = RandomNumberGenerator.new()
rng.seed = box_seed
var index = rng.randi() % box_frames.size()
sprite.frame = box_frames[index]
func setup_chest():
object_type = "Chest"
@@ -503,7 +579,19 @@ func setup_pushable_high_box():
var bottom_frames = [24, 25]
var top_frames = [5, 6]
var index = randi() % bottom_frames.size()
# Use deterministic randomness based on dungeon seed and position
# This ensures host and clients get the same chest variant
var highbox_seed = 0
var game_world = get_tree().get_first_node_in_group("game_world")
if game_world and "dungeon_seed" in game_world:
highbox_seed = game_world.dungeon_seed
# Add position to seed to make each chest unique but deterministic
highbox_seed += int(global_position.x) * 1000 + int(global_position.y)
var rng = RandomNumberGenerator.new()
rng.seed = highbox_seed
var index = rng.randi() % bottom_frames.size()
if sprite:
sprite.frame = bottom_frames[index]
@@ -519,6 +607,14 @@ func _open_chest(by_player: Node = null):
return
$SfxOpenChest.play()
is_chest_opened = true
# Track opened chest for syncing to new clients
if multiplayer.has_multiplayer_peer() and multiplayer.is_server():
var game_world = get_tree().get_first_node_in_group("game_world")
if game_world and has_meta("object_index"):
var obj_index = get_meta("object_index")
game_world.opened_chests[obj_index] = true
LogManager.log("Chest: Tracked opened chest with index " + str(obj_index), LogManager.CATEGORY_NETWORK)
if sprite and chest_opened_frame >= 0:
sprite.frame = chest_opened_frame
@@ -579,7 +675,12 @@ func _open_chest(by_player: Node = null):
# Sync chest opening visual to all clients (item already given on server)
if multiplayer.has_multiplayer_peer():
var player_peer_id = by_player.get_multiplayer_authority() if by_player else 0
_sync_chest_open.rpc(selected_loot.type if by_player else "coin", player_peer_id)
var game_world = get_tree().get_first_node_in_group("game_world")
if game_world and game_world.has_method("_rpc_to_ready_peers"):
var chest_name = name
if has_meta("object_index"):
chest_name = "InteractableObject_%d" % get_meta("object_index")
game_world._rpc_to_ready_peers("_sync_chest_open_by_name", [chest_name, selected_loot.type if by_player else "coin", player_peer_id])
else:
push_error("Chest: ERROR - No valid player to give item to!")