123 lines
3.9 KiB
GDScript
123 lines
3.9 KiB
GDScript
extends Node
|
|
# Автолоад "Net". Один и тот же скрипт работает и на клиенте, и на выделенном сервере
|
|
# (RPC требуют одинаковый путь узла и скрипт). Сервер — это headless-запуск Godot.
|
|
|
|
const PORT = 9080
|
|
const DEFAULT_URL = "ws://127.0.0.1:9080"
|
|
const SEND_RATE = 20.0
|
|
const RETRY_DELAY = 3.0
|
|
|
|
const FLAG_SLIDING = 1
|
|
const FLAG_WALLRUN = 2
|
|
const FLAG_DASH = 4
|
|
const FLAG_MANTLE = 8
|
|
|
|
var is_server := false
|
|
var ghosts := {} # peer_id -> Ghost
|
|
var send_timer := 0.0
|
|
var connected := false
|
|
|
|
func _ready() -> void:
|
|
is_server = DisplayServer.get_name() == "headless"
|
|
if is_server:
|
|
_start_server()
|
|
else:
|
|
_connect_client()
|
|
|
|
# ---------- Сервер ----------
|
|
|
|
func _start_server() -> void:
|
|
var peer := WebSocketMultiplayerPeer.new()
|
|
var err := peer.create_server(PORT)
|
|
if err != OK:
|
|
push_error("Не удалось открыть порт %d: %s" % [PORT, error_string(err)])
|
|
get_tree().quit(1)
|
|
return
|
|
multiplayer.multiplayer_peer = peer
|
|
multiplayer.peer_connected.connect(func(id): print("+ игрок ", id))
|
|
multiplayer.peer_disconnected.connect(_server_peer_left)
|
|
print("Сервер запущен на порту ", PORT)
|
|
|
|
func _server_peer_left(id: int) -> void:
|
|
print("- игрок ", id)
|
|
for other in multiplayer.get_peers():
|
|
recv_left.rpc_id(other, id)
|
|
|
|
# Клиент -> сервер: своё состояние. Сервер пересылает остальным.
|
|
@rpc("any_peer", "unreliable_ordered")
|
|
func submit_state(pos: Vector3, yaw: float, pitch: float, flags: int) -> void:
|
|
if not is_server:
|
|
return
|
|
var sender := multiplayer.get_remote_sender_id()
|
|
for other in multiplayer.get_peers():
|
|
if other != sender:
|
|
recv_state.rpc_id(other, sender, pos, yaw, pitch, flags)
|
|
|
|
# ---------- Клиент ----------
|
|
|
|
func _server_url() -> String:
|
|
for arg in OS.get_cmdline_user_args():
|
|
if arg.begins_with("--server-url="):
|
|
return arg.substr("--server-url=".length())
|
|
if OS.has_feature("web"):
|
|
var proto = JavaScriptBridge.eval("window.location.protocol")
|
|
var host = JavaScriptBridge.eval("window.location.host")
|
|
return ("wss://" if proto == "https:" else "ws://") + str(host) + "/ws"
|
|
return DEFAULT_URL
|
|
|
|
func _connect_client() -> void:
|
|
var peer := WebSocketMultiplayerPeer.new()
|
|
var err := peer.create_client(_server_url())
|
|
if err != OK:
|
|
push_warning("Не удалось подключиться: %s" % error_string(err))
|
|
_schedule_retry()
|
|
return
|
|
multiplayer.multiplayer_peer = peer
|
|
if not multiplayer.connected_to_server.is_connected(_on_connected):
|
|
multiplayer.connected_to_server.connect(_on_connected)
|
|
multiplayer.connection_failed.connect(_on_lost)
|
|
multiplayer.server_disconnected.connect(_on_lost)
|
|
|
|
func _on_connected() -> void:
|
|
connected = true
|
|
|
|
func _on_lost() -> void:
|
|
connected = false
|
|
for g in ghosts.values():
|
|
g.queue_free()
|
|
ghosts.clear()
|
|
multiplayer.multiplayer_peer = null
|
|
_schedule_retry()
|
|
|
|
func _schedule_retry() -> void:
|
|
get_tree().create_timer(RETRY_DELAY).timeout.connect(_connect_client)
|
|
|
|
@rpc("authority", "unreliable_ordered")
|
|
func recv_state(id: int, pos: Vector3, yaw: float, pitch: float, flags: int) -> void:
|
|
var ghost: Ghost = ghosts.get(id)
|
|
if ghost == null:
|
|
ghost = Ghost.new()
|
|
ghost.setup(id, pos)
|
|
ghosts[id] = ghost
|
|
get_tree().current_scene.add_child(ghost)
|
|
ghost.set_target(pos, yaw, pitch, flags)
|
|
|
|
@rpc("authority", "reliable")
|
|
func recv_left(id: int) -> void:
|
|
var ghost = ghosts.get(id)
|
|
if ghost:
|
|
ghost.queue_free()
|
|
ghosts.erase(id)
|
|
|
|
func _process(delta: float) -> void:
|
|
if is_server or not connected:
|
|
return
|
|
send_timer += delta
|
|
if send_timer < 1.0 / SEND_RATE:
|
|
return
|
|
send_timer = 0.0
|
|
var player := get_tree().get_first_node_in_group("local_player")
|
|
if player == null:
|
|
return
|
|
submit_state.rpc_id(1, player.global_position, player.rotation.y, player.head.rotation.x, player.get_net_flags())
|