M1c: Gleichschritt im Netz #9

Merged
steven merged 20 commits from m1c-gleichschritt-im-netz into main 2026-10-02 06:41:02 +00:00
2 changed files with 60 additions and 10 deletions
Showing only changes of commit a43ce66a89 - Show all commits
+28 -7
View File
@@ -20,6 +20,9 @@ var connect_seconds := CONNECT_SECONDS
var _peer: ENetMultiplayerPeer var _peer: ENetMultiplayerPeer
var _peers: Dictionary[int, bool] = {} var _peers: Dictionary[int, bool] = {}
## Connections that close_peer is closing. Nothing more goes to them: Godot
## reports an error for every packet to a connection in that state.
var _closing: Dictionary[int, bool] = {}
var _events: Array[Dictionary] = [] var _events: Array[Dictionary] = []
var _connecting := false var _connecting := false
var _waited := 0.0 var _waited := 0.0
@@ -67,6 +70,7 @@ func _start() -> void:
close() close()
_peer = ENetMultiplayerPeer.new() _peer = ENetMultiplayerPeer.new()
_peers.clear() _peers.clear()
_closing.clear()
_peer.peer_connected.connect(_on_connected) _peer.peer_connected.connect(_on_connected)
_peer.peer_disconnected.connect(_on_disconnected) _peer.peer_disconnected.connect(_on_disconnected)
@@ -78,6 +82,7 @@ func _on_connected(peer: int) -> void:
func _on_disconnected(peer: int) -> void: func _on_disconnected(peer: int) -> void:
_peers.erase(peer) _peers.erase(peer)
_closing.erase(peer)
_events.append({"type": "disconnected", "peer": peer}) _events.append({"type": "disconnected", "peer": peer})
@@ -118,7 +123,7 @@ func poll(delta: float) -> Array[Dictionary]:
## Sends a message of the core to `peer`, reliably and in order. A message ## Sends a message of the core to `peer`, reliably and in order. A message
## to a connection that is gone is dropped. ## to a connection that is gone or being closed is dropped.
func send(peer: int, bytes: PackedByteArray) -> void: func send(peer: int, bytes: PackedByteArray) -> void:
_put(peer, bytes, CHANNEL_CORE, MultiplayerPeer.TRANSFER_MODE_RELIABLE) _put(peer, bytes, CHANNEL_CORE, MultiplayerPeer.TRANSFER_MODE_RELIABLE)
@@ -130,19 +135,34 @@ func send_figure(peer: int, bytes: PackedByteArray) -> void:
func _put(peer: int, bytes: PackedByteArray, channel: int, mode: MultiplayerPeer.TransferMode) -> void: func _put(peer: int, bytes: PackedByteArray, channel: int, mode: MultiplayerPeer.TransferMode) -> void:
if not is_connected_to_peers() or _peers.is_empty(): if not is_connected_to_peers():
return return
if peer > 0 and not _peers.has(peer):
return
_peer.set_target_peer(peer)
_peer.set_transfer_channel(channel) _peer.set_transfer_channel(channel)
_peer.set_transfer_mode(mode) _peer.set_transfer_mode(mode)
_peer.put_packet(bytes) # One packet per connection, so that no packet goes to a connection that
# is gone or being closed; Godot's own sending to all would.
for target in _targets(peer):
_peer.set_target_peer(target)
_peer.put_packet(bytes)
## The connections a packet for `peer` goes to: that one, every one if
## `peer` is 0, or every one but -`peer` if `peer` is negative. Connections
## that are being closed get nothing.
func _targets(peer: int) -> Array[int]:
var targets: Array[int] = []
for open: int in _peers:
if _closing.has(open):
continue
if peer == 0 or open == peer or (peer < 0 and open != -peer):
targets.append(open)
return targets
## Closes the connection to `peer` once the messages waiting for it are out. ## Closes the connection to `peer` once the messages waiting for it are out.
func close_peer(peer: int) -> void: func close_peer(peer: int) -> void:
if _peers.has(peer): if _peers.has(peer) and not _closing.has(peer):
_closing[peer] = true
_peer.get_peer(peer).peer_disconnect_later() _peer.get_peer(peer).peer_disconnect_later()
@@ -151,4 +171,5 @@ func close() -> void:
if is_open(): if is_open():
_peer.close() _peer.close()
_peers.clear() _peers.clear()
_closing.clear()
_connecting = false _connecting = false
+32 -3
View File
@@ -100,14 +100,43 @@ func test_a_guest_that_quits_is_seen_leaving() -> void:
host.close() host.close()
func test_messages_to_a_connection_that_is_gone_are_dropped() -> void: ## Godot reports an error for every packet to a connection that is gone or
## being closed; the test runner turns each such error into a failure.
func test_nothing_is_sent_to_connections_that_are_gone_or_closing() -> void:
var host := NetLink.new() var host := NetLink.new()
check_eq(host.host(0, "127.0.0.1"), OK, "the host listens") check_eq(host.host(0, "127.0.0.1"), OK, "the host listens")
var first := NetLink.new()
var second := NetLink.new()
first.join("127.0.0.1", host.local_port())
second.join("127.0.0.1", host.local_port())
await _pump([host, first, second], 10)
var peers: Array = _events[host].map(func(event: Dictionary) -> int: return event["peer"])
check_eq(peers.size(), 2, "two guests connected")
var leaving: int = peers[0]
var staying: int = peers[1]
# Connections that never existed.
host.send(12345, PackedByteArray([1])) host.send(12345, PackedByteArray([1]))
host.send_figure(12345, PackedByteArray([1])) host.send_figure(12345, PackedByteArray([1]))
host.send_figure(-12345, PackedByteArray([1]))
host.close_peer(12345) host.close_peer(12345)
check_eq(host.poll(0.0), [], "nothing happened") # A connection that is being closed, and everybody while it closes.
host.close() host.close_peer(leaving)
for frame in 10:
host.send(leaving, PackedByteArray([2]))
host.send_figure(leaving, PackedByteArray([2]))
host.send_figure(-staying, PackedByteArray([2]))
host.send_figure(0, PackedByteArray([2]))
await _pump([host, first, second], 1)
# The connection is gone now.
host.send(leaving, PackedByteArray([3]))
host.send_figure(-leaving, PackedByteArray([3]))
await _pump([host, first, second], 5)
check(_types(first).has("disconnected"), "the first guest was closed")
check(not _types(second).has("disconnected"), "the second guest stays")
for link in [host, first, second]:
link.close()
func test_a_guest_gives_up_when_nobody_answers() -> void: func test_a_guest_gives_up_when_nobody_answers() -> void: