Files
Grovewright/game/scripts/session_link.gd
T
Steven Wroblewski 0917450014 Verbindung über ENet
NetLink verbindet Host und Gäste über ENet: Nachrichten des Kerns
zuverlässig und in Reihenfolge, Positionen der Figuren unzuverlässig.
Eine Verbindung schließt erst, wenn wartende Nachrichten draußen sind.
SessionLink trägt die Nachrichten einer Sitzung über die Verbindung und
reicht Positionen weiter. Die Tests verbinden zwei Seiten über die
Loopback-Schnittstelle.

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-10-01 19:53:04 +02:00

104 lines
3.6 KiB
GDScript

class_name SessionLink
extends RefCounted
## Carries the messages of a GwSession over a NetLink, and the positions of
## the figures, which are not part of the shared world. Tells the game what
## happened on the way.
## Bytes of a position: x and y as 32-bit floats.
const POSITION_BYTES := 8
var session: GwSession
var link: NetLink
func _init(game_session: GwSession, net_link: NetLink) -> void:
session = game_session
link = net_link
## Moves messages in both directions and returns notices, oldest first, as
## dictionaries with a `type` entry:
## - "guest_arrived" and "guest_left" (entry `peer`), at the host
## - "world_received" (entry `resync`), "refused" (entry `reason`),
## "protocol_error", "host_lost" and "failed", at a guest
## - "divergence" (entries `peer`, `tick`, `hash`), at the host
## - "figure" (entries `player`, `position`), everywhere
## `delta` is the time since the last call.
func pump(delta: float) -> Array[Dictionary]:
var notices: Array[Dictionary] = []
for event in link.poll(delta):
match event["type"]:
"connected":
if session.is_host():
session.connect_peer(event["peer"])
notices.append({"type": "guest_arrived", "peer": event["peer"]})
"disconnected":
if session.is_host():
session.disconnect_peer(event["peer"])
notices.append({"type": "guest_left", "peer": event["peer"]})
else:
notices.append({"type": "host_lost"})
"failed":
notices.append({"type": "failed"})
"message":
notices.append_array(session.receive(event["peer"], event["bytes"]))
"figure":
_receive_figure(event["peer"], event["bytes"], notices)
for divergence in session.take_divergences():
divergence["type"] = "divergence"
notices.append(divergence)
flush()
return notices
## Sends what the session has for the network and closes the connections
## the host gave up. A guest's messages wait until its host is reached.
func flush() -> void:
if not link.is_connected_to_peers():
return
for message in session.take_messages():
link.send(message["peer"], message["bytes"])
for peer in session.take_closing():
link.close_peer(peer)
## Tells the other machines where this machine's figure stands.
func send_figure(position: Vector2) -> void:
if session.is_host():
var player := session.local_player()
if player >= 0:
link.send_figure(0, _figure_bytes(player, position))
elif session.local_player() >= 0:
link.send_figure(GwSession.HOST_PEER, _position_bytes(position))
func _receive_figure(peer: int, bytes: PackedByteArray, notices: Array[Dictionary]) -> void:
if session.is_host():
# A guest sends only a position; the host knows whose it is and
# passes it on to every other guest.
var player := session.player_of_peer(peer)
if player < 0 or bytes.size() != POSITION_BYTES:
return
var position := Vector2(bytes.decode_float(0), bytes.decode_float(4))
notices.append({"type": "figure", "player": player, "position": position})
link.send_figure(-peer, _figure_bytes(player, position))
elif bytes.size() == 1 + POSITION_BYTES:
var player := bytes.decode_u8(0)
if player != session.local_player():
var position := Vector2(bytes.decode_float(1), bytes.decode_float(5))
notices.append({"type": "figure", "player": player, "position": position})
func _position_bytes(position: Vector2) -> PackedByteArray:
var bytes := PackedByteArray()
bytes.resize(POSITION_BYTES)
bytes.encode_float(0, position.x)
bytes.encode_float(4, position.y)
return bytes
func _figure_bytes(player: int, position: Vector2) -> PackedByteArray:
var bytes := PackedByteArray([player])
bytes.append_array(_position_bytes(position))
return bytes