Abschlusskorrekturen M1c: Schläge der Gäste, Figuren und Fragebogen
- Der Host hält einen Schlag eines Gastes, der während der Abklingzeit ankommt, bis zum ersten Takt danach. Übers Internet ging sonst etwa jeder fünfte Schlag eines Gastes verloren. - ENet drosselt die Figuren nie mehr; sonst konnten die Figuren eines von zwei Gästen dauerhaft stehen bleiben. Figuren gehen geordnet, und eine stille Verbindung gilt nach höchstens acht Sekunden als verloren. - Positionen, die keine Zahlen sind, und Figuren von Spielern, die nicht online sind, werden verworfen. - Ein Host mit angehaltener Simulation sagt das in der Statuszeile. - Neue Tests: Schlag während der Abklingzeit, Zielauswahl von NetLink, verworfene Figuren, Neuabgleich über die Brücke. - Der Fragebogen sagt, was jeder Rechner braucht, und die Freigabe in der Firewall gehört zum Host. CLAUDE.md beschreibt das Halten der Schläge, die Drosselung und die Zeitüberschreitung und ergänzt die Vertrauensregel um die Schlüssel. Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -3,7 +3,8 @@ extends RefCounted
|
||||
## What the command line asks for. Godot passes the arguments after "--":
|
||||
## --profile=NAME keeps key, save and recordings in a folder of their own,
|
||||
## so that two windows on one machine are two players
|
||||
## --name=NAME the player's name for this start
|
||||
## --name=NAME the player's name; like a name typed in the menu, it is
|
||||
## remembered for the next start
|
||||
## --solo starts playing alone at once, without the menu
|
||||
## --host opens the world for guests at once
|
||||
## --port=PORT the port an open world listens on
|
||||
|
||||
@@ -237,15 +237,17 @@ func _leave(message: String) -> void:
|
||||
|
||||
|
||||
## A broken session runs no more ticks. Instead of leaving the others
|
||||
## waiting, a host closes its world to guests and a guest leaves.
|
||||
## waiting, a host closes its world to guests and a guest leaves. The host's
|
||||
## status line says why its world stands still.
|
||||
func _stop_sharing() -> void:
|
||||
if not session.is_host():
|
||||
_leave(tr("SIMULATION_STOPPED"))
|
||||
return
|
||||
link.link.close()
|
||||
link = null
|
||||
figures.clear()
|
||||
_update_status()
|
||||
if link != null:
|
||||
link.link.close()
|
||||
link = null
|
||||
figures.clear()
|
||||
hud.show_status(tr("SIMULATION_STOPPED"))
|
||||
|
||||
|
||||
## Ends a pause once the host accepted a guest: the guest joins in the next
|
||||
@@ -262,7 +264,7 @@ func _process(delta: float) -> void:
|
||||
if link != null:
|
||||
for notice in link.pump(delta):
|
||||
_on_notice(notice)
|
||||
if session != null and session.is_broken() and link != null:
|
||||
if session != null and session.is_broken():
|
||||
_stop_sharing()
|
||||
if session == null or not session.is_running() or session.is_broken():
|
||||
return
|
||||
|
||||
@@ -2,8 +2,9 @@ class_name NetLink
|
||||
extends RefCounted
|
||||
## The network connection of a session, over ENet. A host listens for guests;
|
||||
## a guest connects to its host. Messages of the core travel on a reliable,
|
||||
## ordered channel. Positions of figures travel on a second, unreliable
|
||||
## channel, because a lost one is soon replaced by the next.
|
||||
## ordered channel. Positions of figures travel on a second channel that is
|
||||
## unreliable but ordered: a lost position is soon replaced by the next, and
|
||||
## an older one that arrives after a newer one is dropped.
|
||||
|
||||
## Port a host listens on unless told otherwise.
|
||||
const DEFAULT_PORT := 24680
|
||||
@@ -14,6 +15,10 @@ const CHANNEL_CORE := 0
|
||||
const CHANNEL_FIGURES := 1
|
||||
## Seconds a guest waits for its host to answer before it gives up.
|
||||
const CONNECT_SECONDS := 8.0
|
||||
## Milliseconds after which a connection whose packets stay unanswered counts
|
||||
## as lost. ENet's own limit of 30 seconds would leave a guest whose host
|
||||
## vanished in a standing world for that long.
|
||||
const TIMEOUT_MSEC := 8000
|
||||
|
||||
## Seconds this guest waits for its host. Tests shorten it.
|
||||
var connect_seconds := CONNECT_SECONDS
|
||||
@@ -77,6 +82,13 @@ func _start() -> void:
|
||||
|
||||
func _on_connected(peer: int) -> void:
|
||||
_peers[peer] = true
|
||||
var connection := _peer.get_peer(peer)
|
||||
# ENet slows unreliable packets down after a spike in the round trip and
|
||||
# can keep them at nothing for the rest of a session, so that the figures
|
||||
# of one guest stop. Positions are a few bytes ten times a second; they
|
||||
# are never slowed down.
|
||||
connection.throttle_configure(5000, ENetPacketPeer.PACKET_THROTTLE_SCALE, 0)
|
||||
connection.set_timeout(32, 5000, TIMEOUT_MSEC)
|
||||
_events.append({"type": "connected", "peer": peer})
|
||||
|
||||
|
||||
@@ -131,7 +143,7 @@ func send(peer: int, bytes: PackedByteArray) -> void:
|
||||
## Sends the position of a figure to `peer`, to every connection if `peer`
|
||||
## is 0, or to every connection but one if `peer` is that one negated.
|
||||
func send_figure(peer: int, bytes: PackedByteArray) -> void:
|
||||
_put(peer, bytes, CHANNEL_FIGURES, MultiplayerPeer.TRANSFER_MODE_UNRELIABLE)
|
||||
_put(peer, bytes, CHANNEL_FIGURES, MultiplayerPeer.TRANSFER_MODE_UNRELIABLE_ORDERED)
|
||||
|
||||
|
||||
func _put(peer: int, bytes: PackedByteArray, channel: int, mode: MultiplayerPeer.TransferMode) -> void:
|
||||
|
||||
@@ -22,7 +22,8 @@ func _init(game_session: GwSession, net_link: NetLink) -> void:
|
||||
## - "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
|
||||
## - "figure" (entries `player`, `position`), everywhere, for another player
|
||||
## who is online, with a position of real numbers
|
||||
## `delta` is the time since the last call.
|
||||
func pump(delta: float) -> Array[Dictionary]:
|
||||
var notices: Array[Dictionary] = []
|
||||
@@ -80,12 +81,17 @@ func _receive_figure(peer: int, bytes: PackedByteArray, notices: Array[Dictionar
|
||||
if player < 0 or bytes.size() != POSITION_BYTES:
|
||||
return
|
||||
var position := Vector2(bytes.decode_float(0), bytes.decode_float(4))
|
||||
# Not a number or infinite, as a broken or changed client may send it:
|
||||
# such a position would spoil the figure for good.
|
||||
if not position.is_finite():
|
||||
return
|
||||
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))
|
||||
var position := Vector2(bytes.decode_float(1), bytes.decode_float(5))
|
||||
# A position can arrive after its player left the world.
|
||||
if player != session.local_player() and session.online_players().has(player) and position.is_finite():
|
||||
notices.append({"type": "figure", "player": player, "position": position})
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user