Skip to content

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 protocol
  • ProtocolIDMismatchError: exception for receiving non-PyGaSe packages
  • DuplicateSequenceError: exception for duplicate packages
  • Header: class for PyGaSe package headers
  • Package: class for PyGaSe UDP packages
  • ClientPackage: subclass of Package for packages sent by clients
  • ServerPackage: subclass of Package for packages sent by servers
  • ConnectionStatus: enum for the status of a client-server connection
  • Connection: class for the core network logic of client-server connections
  • ClientConnection: subclass of Connection for the client side
  • ServerConnection: subclass of Connectoin 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(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 argument
  • ack (int): see corresponding constructor argument
  • ack_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 exceed Package._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 exceed Package._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 a pygase.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 argument
  • event_handler (pygase.event.UniversalEventHandler): see corresponding constructor argument
  • event_wire (pygase.GameStateMachine): see corresponding constructor argument
  • local_sequence (pygase.utils.Sqn): sequence number of the last sent package
  • remote_sequence (pygase.utils.Sqn): sequence number of the last received package
  • ack_bitfield (str): acks for the 32 packages prior to self.remote_sequence
  • latency (float): the last registered RTT (round trip time)
  • status (ConnectionStatus): an integer value that informs about the state of the connections
  • quality (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 a pygase.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 and push_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 argument
  • last_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)