feat(multiplayer): ENet networking + player/world sync + lobby menu + chat
Add dedicated server / host / client modes via NetworkManager autoload, PlayerSyncComponent (20 Hz unreliable RPC), WorldSyncComponent (authoritative block break/place), ChatManager (F2), LobbyMenu scene, updated MainMenu with Solo/Heberger/Rejoindre/Quitter buttons. Port changed to 7777 (9999 occupied by sntlkeyssrvr on this machine). Mobs disabled in multi (spawn solo only). Solo mode untouched. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
74
scripts/net/WorldSyncComponent.gd
Normal file
74
scripts/net/WorldSyncComponent.gd
Normal file
@@ -0,0 +1,74 @@
|
||||
extends Node
|
||||
|
||||
# Attached to ChunkManager node.
|
||||
# Server: intercepts block changes and broadcasts to clients.
|
||||
# Client: receives and applies block changes locally.
|
||||
|
||||
var _chunk_manager: Node = null
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
_chunk_manager = get_parent()
|
||||
|
||||
|
||||
# Called by Main.gd instead of world.break_block / world.place_block when in multiplayer.
|
||||
func server_break_block(world_pos: Vector3) -> int:
|
||||
if not NetworkManager.is_server():
|
||||
# Client sends request to server
|
||||
request_break.rpc_id(1, world_pos)
|
||||
return 0
|
||||
var broken_id: int = _chunk_manager.break_block(world_pos)
|
||||
if broken_id > 0:
|
||||
broadcast_block_change.rpc(world_pos, 0)
|
||||
return broken_id
|
||||
|
||||
|
||||
func server_place_block(world_pos: Vector3, block_id: int) -> bool:
|
||||
if not NetworkManager.is_server():
|
||||
# Client sends request to server
|
||||
request_place.rpc_id(1, world_pos, block_id)
|
||||
return false
|
||||
var placed: bool = _chunk_manager.place_block(world_pos, block_id)
|
||||
if placed:
|
||||
broadcast_block_change.rpc(world_pos, block_id)
|
||||
return placed
|
||||
|
||||
|
||||
@rpc("any_peer", "reliable")
|
||||
func request_break(pos: Vector3) -> void:
|
||||
if not NetworkManager.is_server():
|
||||
return
|
||||
var sender_id: int = multiplayer.get_remote_sender_id()
|
||||
var broken_id: int = _chunk_manager.break_block(pos)
|
||||
if broken_id > 0:
|
||||
broadcast_block_change.rpc(pos, 0)
|
||||
# Notify requesting peer of item pickup
|
||||
notify_break_result.rpc_id(sender_id, broken_id)
|
||||
|
||||
|
||||
@rpc("any_peer", "reliable")
|
||||
func request_place(pos: Vector3, block_id: int) -> void:
|
||||
if not NetworkManager.is_server():
|
||||
return
|
||||
var placed: bool = _chunk_manager.place_block(pos, block_id)
|
||||
if placed:
|
||||
broadcast_block_change.rpc(pos, block_id)
|
||||
|
||||
|
||||
@rpc("authority", "call_local", "reliable")
|
||||
func broadcast_block_change(world_pos: Vector3, new_id: int) -> void:
|
||||
# Applied on all peers including server via call_local
|
||||
if multiplayer.is_server():
|
||||
return # already applied on server
|
||||
if new_id == 0:
|
||||
_chunk_manager.break_block(world_pos)
|
||||
else:
|
||||
_chunk_manager.place_block(world_pos, new_id)
|
||||
|
||||
|
||||
@rpc("authority", "reliable")
|
||||
func notify_break_result(broken_id: int) -> void:
|
||||
# Signal to Main.gd to add item to local inventory
|
||||
var main := get_tree().get_first_node_in_group("main")
|
||||
if is_instance_valid(main) and main.has_method("_on_remote_block_broken"):
|
||||
main._on_remote_block_broken(broken_id)
|
||||
Reference in New Issue
Block a user