pygase.connection
Provide low-level networking logic.
This module is not supposed to be required by users of this library.
Contents
PROTOCOL_ID
: 4 byte identifier for the PyGaSe package protocolProtocolIDMismatchError
: exception for receiving non-PyGaSe packagesDuplicateSequenceError
: exception for duplicate packagesHeader
: class for PyGaSe package headersPackage
: class for PyGaSe UDP packagesClientPackage
: subclass ofPackage
for packages sent by clientsServerPackage
: subclass ofPackage
for packages sent by serversConnectionStatus
: enum for the status of a client-server connectionConnection
: class for the core network logic of client-server connectionsClientConnection
: subclass ofConnection
for the client sideServerConnection
: subclass ofConnectoin
for the server side
ProtocolIDMismatchError
ProtocolIDMismatchError(self, /, *args, **kwargs)
Bytestring could not be identified as a valid PyGaSe package.
DuplicateSequenceError
DuplicateSequenceError(self, /, *args, **kwargs)
Received a package with a sequence number that was already received before.
Header
Header(self, sequence:int, ack:int, ack_bitfield:str)
Create a PyGaSe package header.
Arguments
- sequence (int): package sequence number
- ack (int): sequence number of the last received package
- ack_bitfield (str): A 32 character string representing the 32 sequence numbers prior to the last one received, with the first character corresponding the packge directly preceding it and so forth. '1' means that package has been received, '0' means it hasn't.
Attributes
sequence (int)
: see corresponding constructor argumentack (int)
: see corresponding constructor argumentack_bitfield (str)
: see corresponding constructor argument
Sequence numbers
: A sequence of 0 means no packages have been sent or received. After 65535 sequence numbers wrap around to 1, so they can be stored in 2 bytes.
to_bytearray
Header.to_bytearray(self) -> bytearray
Return 12 bytes representing the header.
destructure
Header.destructure(self) -> tuple
Return the tuple (sequence, ack, ack_bitfield)
.
deconstruct_datagram
Header.deconstruct_datagram(datagram:bytes) -> tuple
Return a tuple containing the header and the rest of the datagram.
Arguments
- datagram (bytes): serialized PyGaSe package to deconstruct
Returns
tuple
: (header, payload)
with payload
being a bytestring of the rest of the datagram
Package
Package(self, header:pygase.connection.Header, events:list=None)
Create a UDP package implementing the PyGaSe protocol.
Arguments
- header (Header): package header
Arguments
- events (pygase.event.Event): list events to attach to this package
Attributes
header (Header)
:
Members
events (pygase.event.Event)
: see corresponding constructor argument
PyGaSe servers and clients use the subclasses ServerPackage
and ClientPackage
respectively.
The Package
class would also work on its own (it's not an 'abstract' class), in which case you would have
all features of PyGaSe except for a synchronized game state.
events
Get a list of the events in the package.
add_event
Package.add_event(self, event:pygase.event.Event) -> None
Add a PyGaSe event to the package.
Arguments
- event (pygase.event.Event): the event to be attached to this package
Raises
OverflowError
: if the package has previously been converted to a datagram and and its size with the added event would exceedPackage._max_size
(2048 bytex)
get_bytesize
Package.get_bytesize(self) -> int
Return the size in bytes the package has as a datagram.
to_datagram
Package.to_datagram(self) -> bytes
Return package compactly serialized to bytes
.
Raises
OverflowError
: if the resulting datagram would exceedPackage._max_size
from_datagram
Package.from_datagram(datagram:bytes) -> 'Package'
Deserialize datagram to Package
.
Arguments
- datagram (bytes): bytestring to deserialize, typically received via network
Returns
Package
: the deserialized package
Raises
ProtocolIDMismatchError
: if the first four bytes don't match the PyGaSe protocol ID
ClientPackage
ClientPackage(self, header:pygase.connection.Header, time_order:int, events:list=None)
Subclass of Package
for packages sent by PyGaSe clients.
Arguments
- time_order (int): the clients last known time order of the game state
Attributes
time_order (int)
: see corresponding constructor argument
to_datagram
ClientPackage.to_datagram(self) -> bytes
Override Package.to_datagram
to include time_order
.
from_datagram
ClientPackage.from_datagram(datagram:bytes) -> 'ClientPackage'
Override Package.from_datagram
to include time_order
.
ServerPackage
ServerPackage(self, header:pygase.connection.Header, game_state_update:pygase.gamestate.GameStateUpdate, events:list=None)
Subclass of Package
for packages sent by PyGaSe servers.
Arguments
- game_state_update (pygase.gamestate.GameStateUpdate): the servers most recent minimal update for the client
to_datagram
ServerPackage.to_datagram(self) -> bytes
Override Package.to_datagram
to include game_state_update
.
from_datagram
ServerPackage.from_datagram(datagram:bytes) -> 'ServerPackage'
Override Package.from_datagram
to include game_state_update
.
ConnectionStatus
ConnectionStatus(self, /, *args, **kwargs)
Enum for the state of a connection.
'Disconnected'
'Connecting'
'Connected'
Connection
Connection(self, remote_address:tuple, event_handler, event_wire=None)
Exchange packages between PyGaSe clients and servers.
PyGaSe connections exchange events with their other side which are handled using custom handler functions. They also keep each other informed about which packages have been sent and received and automatically avoid network congestion.
Arguments
- remote_address (tuple):
('hostname', port)
for the connection partner's address - event_handler (pygase.event.UniversalEventHandler): object that has a callable
handle
attribute that takes apygase.event.Event
as argument - event_wire (pygase.GameStateMachine): object to which events are to be repeated
(has to implement a
_push_event
method)
Attributes
remote_address (tuple)
: see corresponding constructor argumentevent_handler (pygase.event.UniversalEventHandler)
: see corresponding constructor argumentevent_wire (pygase.GameStateMachine)
: see corresponding constructor argumentlocal_sequence (pygase.utils.Sqn)
: sequence number of the last sent packageremote_sequence (pygase.utils.Sqn)
: sequence number of the last received packageack_bitfield (str)
: acks for the 32 packages prior toself.remote_sequence
latency (float)
: the last registered RTT (round trip time)status (ConnectionStatus)
: an integer value that informs about the state of the connectionsquality (str)
: either'good'
or'bad'
depending on latency, used internally for congestion avoidance
PyGaSe servers and clients use the subclasses ServerConnection
and ClientConnection
respectively.
The Connection
class would also work on its own (it's not an 'abstract' class), in which case you would have
all features of PyGaSe except for a synchronized game state.
dispatch_event
Connection.dispatch_event(self, event:pygase.event.Event, ack_callback=None, timeout_callback=None)
Send an event to the connection partner.
Arguments
- event (pygase.event.Event): the event to dispatch
- ack_callback (callable, coroutine): will be executed after the event was received
- timeout_callback (callable, coroutine): will be executed if the event was not received
Using long-running blocking operations in any of the callback functions can disturb the connection.
ClientConnection
ClientConnection(self, remote_address:tuple, event_handler)
Subclass of Connection
to describe the client side of a PyGaSe connection.
Client connections hold a copy of the game state which is continously being updated according to state updates received from the server.
Attributes
game_state_context (pygase.utils.LockedRessource)
: provides thread-safe access to apygase.GameState
shutdown
ClientConnection.shutdown(self, shutdown_server:bool=False)
Shut down the client connection.
This method can also be spawned as a coroutine.
Arguments
- shutdown_server (bool): wether or not the server should be shut down too (only has an effect if the client has host permissions)
loop
ClientConnection.loop(self)
Continously operate the connection.
This method will keep sending and receiving packages and handling events until it is cancelled or the connection receives a shutdown command. It can also be spawned as a coroutine.
ServerConnection
ServerConnection(self, remote_address:tuple, event_handler, game_state_store, last_client_time_order:pygase.utils.Sqn, event_wire=None)
Subclass of Connection
that describes the server side of a PyGaSe connection.
Arguments
- game_state_store (pygase.GameStateStore): object that serves as an interface to the game state repository
(has to provide the methods
get_gamestate
,get_update_cache
andpush_update
) - last_client_time_order (pygase.utils.Sqn): the last time order number known to the client
Attributes
game_state_store (pygase.GameStateStore)
: see corresponding constructor argumentlast_client_time_order (pygase.utils.Sqn)
: see corresponding constructor argument
loop
ServerConnection.loop(hostname:str, port:int, server, event_wire) -> None
Continously orchestrate and operate connections to clients.
This coroutine will keep listening for client packages, create new ServerConnection
objects
when necessary and make sure all packages are handled by and sent via the right connection.
It will return as soon as the server receives a shutdown message.
Arguments
- hostname (str): the hostname or IPv4 address to which to bind the server socket
- port (int): the port number to which to bind the server socket
- server (pygase.Server): the server for which this loop is run
- event_wire (pygase.GameStateMachine): object to which events are to be repeated
(has to implement a
_push_event
method)