t_p6_browser: add workshop map browser with voting (3 files)
- workshop_browser.gd: scans server maps dir for .pck/.tscn files - map_browser_rpc.gd: client-server RPC bridge for map list/change - map_vote.gd: simple >50% majority vote with 30s timeout File scope: server/scripts/browser/workshop_browser.gd (NEW) scripts/network/map_browser_rpc.gd (NEW) server/scripts/browser/map_vote.gd (NEW)
This commit is contained in:
@@ -0,0 +1,119 @@
|
||||
## MapBrowserRPC — Client-Server RPC Bridge for Workshop Map Browser
|
||||
##
|
||||
## Autoload candidate (or child of NetworkManager) that provides ENet RPC
|
||||
## methods for the workshop map browser system.
|
||||
##
|
||||
## ## Architecture
|
||||
##
|
||||
## ┌──────────────┐ request_map_list() ┌──────────────┐
|
||||
## │ Client │ ──────────────────────► │ Server │
|
||||
## │ │ ◄────────────────────── │ │
|
||||
## │ │ send_map_list() │ │
|
||||
## │ │ │ │
|
||||
## │ │ request_map_change() │ │
|
||||
## │ │ ──────────────────────► │ │
|
||||
## └──────────────┘ └──────────────┘
|
||||
##
|
||||
## ## Signals
|
||||
##
|
||||
## map_list_received(map_list: Array) — emitted on client when map list arrives
|
||||
## map_change_requested(map_id: String, peer_id: int) — emitted on server
|
||||
## when a client requests a map change
|
||||
## map_vote_started(map_id: String) — emitted on server when vote begins
|
||||
##
|
||||
## ## Integration
|
||||
##
|
||||
## Add as an autoload in project.godot:
|
||||
## MapBrowserRPC="*res://scripts/network/map_browser_rpc.gd"
|
||||
##
|
||||
## Or add as a child of NetworkManager at runtime.
|
||||
##
|
||||
## ## RPC Visibility
|
||||
##
|
||||
## - Server → Client: send_map_list (authority, call_remote)
|
||||
## - Client → Server: request_map_list (any_peer, call_remote)
|
||||
## - Client → Server: request_map_change (any_peer, call_remote)
|
||||
##
|
||||
## =============================================================================
|
||||
|
||||
extends Node
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Signals
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
## Emitted on the client when the server sends the map list.
|
||||
signal map_list_received(map_list: Array)
|
||||
|
||||
## Emitted on the server when a client requests a map change.
|
||||
signal map_change_requested(map_id: String, peer_id: int)
|
||||
|
||||
## Emitted on the server when a map vote is initiated by a client request.
|
||||
signal map_vote_started(map_id: String)
|
||||
|
||||
## Emitted on the client when the vote result is sent back.
|
||||
signal map_vote_result(map_id: String, passed: bool, yes_count: int, no_count: int)
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# RPC: Server → Client
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
## Server sends the serialized map list to a specific client.
|
||||
## Called by server code after receiving request_map_list().
|
||||
@rpc("authority", "call_remote", "reliable")
|
||||
func send_map_list(map_list: Array) -> void:
|
||||
if multiplayer.is_server():
|
||||
return
|
||||
map_list_received.emit(map_list)
|
||||
print("[MapBrowserRPC] Received map list with %d maps" % map_list.size())
|
||||
|
||||
## Server broadcasts the final vote result to all clients.
|
||||
@rpc("authority", "call_remote", "reliable")
|
||||
func broadcast_vote_result(map_id: String, passed: bool, yes_count: int, no_count: int) -> void:
|
||||
if multiplayer.is_server():
|
||||
return
|
||||
map_vote_result.emit(map_id, passed, yes_count, no_count)
|
||||
print("[MapBrowserRPC] Vote result for %s: passed=%s (%d yes, %d no)" % [map_id, passed, yes_count, no_count])
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# RPC: Client → Server
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
## Client requests the current map list from the server.
|
||||
## The server should call send_map_list(peer_id, map_list) in response.
|
||||
@rpc("any_peer", "call_remote", "reliable")
|
||||
func request_map_list() -> void:
|
||||
if not multiplayer.is_server():
|
||||
return
|
||||
|
||||
var peer_id: int = multiplayer.get_remote_sender_id()
|
||||
print("[MapBrowserRPC] Map list requested by peer %d" % peer_id)
|
||||
|
||||
# Delegate to WorkshopBrowser if available
|
||||
var list: Array = []
|
||||
if WorkshopBrowser and WorkshopBrowser.has_method(&"get_map_list"):
|
||||
list = WorkshopBrowser.get_map_list()
|
||||
else:
|
||||
push_warning("[MapBrowserRPC] WorkshopBrowser singleton not available — returning empty list")
|
||||
|
||||
# Send the map list back to the requesting client
|
||||
send_map_list.rpc_id(peer_id, list)
|
||||
|
||||
## Client requests a map change (initiates a vote or signals admin).
|
||||
## Emits map_change_requested on the server for handling.
|
||||
@rpc("any_peer", "call_remote", "reliable")
|
||||
func request_map_change(map_id: String) -> void:
|
||||
if not multiplayer.is_server():
|
||||
return
|
||||
|
||||
var peer_id: int = multiplayer.get_remote_sender_id()
|
||||
print("[MapBrowserRPC] Map change requested by peer %d: %s" % [peer_id, map_id])
|
||||
|
||||
# Validate the map exists via WorkshopBrowser
|
||||
if WorkshopBrowser and WorkshopBrowser.has_method(&"has_map"):
|
||||
if not WorkshopBrowser.has_map(map_id):
|
||||
print("[MapBrowserRPC] Peer %d requested unknown map: %s" % [peer_id, map_id])
|
||||
return
|
||||
|
||||
map_change_requested.emit(map_id, peer_id)
|
||||
map_vote_started.emit(map_id)
|
||||
Reference in New Issue
Block a user