Nichts an schließende oder fehlende Verbindungen senden
NetLink merkt sich die Verbindungen, die es gerade schließt, und schickt ihnen nichts mehr. Pakete an alle oder an alle außer einem gehen einzeln an die offenen Verbindungen, statt Godot ein Ziel zu nennen, das es nicht mehr gibt. Beides löste sonst Fehler in Godot aus, etwa wenn der Host Figuren weiterreicht, während er einen abgelehnten Gast hinauswirft. Ein neuer Test mit zwei Gästen deckt das ab. Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -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
|
||||||
|
|||||||
@@ -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:
|
||||||
|
|||||||
Reference in New Issue
Block a user