Skip to main content

Lightweight Python library for multiplayer game networking over TCP

Project description

netio-game

A lightweight Python library for multiplayer game networking. It provides a custom binary wire format, decorator-based RPC routing, and server-authoritative object synchronization over TCP.

Features

  • Binary serialization — compact encoding for primitives, tuples, lists, and typed objects
  • RPC routing@request, @response, and @event handlers with optional datatype parsing
  • Object sync — server creates, updates, and deletes Serializable objects on connected clients
  • Per-player visibility — override validate() on synced objects to control who sees what

Requirements

  • Python 3.10+

Installation

From PyPI:

pip install netio-game

From source (development):

git clone <your-repo-url>
cd netio-game
pip install -e .
from netio_game import Client, Host, ServerRouter, ClientRouter

Quick start

Install the package, then run the examples from the repo root:

pip install -e .

Terminal 1 — server:

python examples/server.py

Terminal 2 — client:

python examples/client.py

Expected client output:

[client] connected, waiting for handshake...
[client] joined as PlayerData <('127.0.0.1', ...)>
[client] response: pong to 127.0.0.1
[client] done

Core concepts

Host and Client

Class Role
Host Listens for connections, runs the game manager, syncs objects
Client Connects to a host, receives synced objects, sends messages

Both use background threads for I/O. Call .start() after construction.

Message types

MessageType Direction Purpose
CONNECT Client → Server Initial handshake
REQUEST Client → Server Ask the server to run a route handler
RESPONSE Server → Client Reply to a request
EVENT Either way Fire-and-forget notification
CREATE Server → Client Spawn a synced object
SYNCHRONIZE Server → Client Push field updates
DELETE Server → Client Remove a synced object
ERROR Either way Report an error

Routers

Handlers are registered with decorators on ServerRouter (server) or ClientRouter (client):

from netio_game import ServerRouter, ClientRouter
from netio_game.serialization.routing import MessageType

server_router = ServerRouter()

@server_router.request("ping")
def handle_ping(player, _data):
    return (f"pong to {player.address[0]}",)

@server_router.event("chat", datatype=tuple[str])
def handle_chat(player, data):
    print(player, data[0])
client_router = ClientRouter()

@client_router.response("ping", datatype=tuple[str])
def on_pong(data):
    print(data[0])

# after client.start() and handshake:
client.send_message(MessageType.REQUEST, "ping", ())
client.send_message(MessageType.EVENT, "chat", ("hello",))

Important: If a handler receives payload data, pass datatype= to the decorator (e.g. tuple[str]). Without it, the parsed argument is None.

Server handlers receive (player_data, data). Client handlers receive (data) only.

Serializable objects

Synced game state inherits from Serializable. Fields must use Annotated with SerializeField():

from typing import Annotated
from netio_game import Serializable, SerializeField

class Bullet(Serializable):
    x: Annotated[float, SerializeField()]
    y: Annotated[float, SerializeField()]

    def __init__(self, x: float, y: float):
        self.x = x
        self.y = y

On the server, register objects with the sync system:

bullet = Bullet(10.0, 20.0)
host.create_object(bullet)   # sends CREATE to clients
bullet.x = 15.0
host.synchronize()           # pushes deltas
host.delete_object(bullet)   # sends DELETE

Override lifecycle hooks on the client:

def client_on_create(self): ...
def client_on_update(self): ...
def client_on_destroy(self): ...

Override validate(player_data) -> bool to hide objects from specific players.

Player data

Subclass PlayerData for per-connection state. The built-in PlayerData does not serialize address — define your own type if the client needs it (see examples/shared.py):

from typing import Annotated
from netio_game import PlayerData, SerializeField

class GamePlayer(PlayerData):
    address: Annotated[tuple[str, int], SerializeField()]

Pass player_data_type= / pdata_type= to Host and Client respectively.

Linking client and server classes (sync_key)

When client and server define different Python classes for the same networked object, assign them the same sync_key so they share a wire class ID:

from netio_game import PlayerData, SerializeField, sync_key
from typing import Annotated

@sync_key("game_player")
class ServerGamePlayer(PlayerData):
    address: Annotated[tuple[str, int], SerializeField()]

@sync_key("game_player")
class ClientGamePlayer(PlayerData):
    address: Annotated[tuple[str, int], SerializeField()]

Or pass it as a class keyword:

class GamePlayer(PlayerData, sync_key="game_player"):
    ...

The server serializes ServerGamePlayer; the client deserializes into ClientGamePlayer (and vice versa for server-bound types).

Publishing to PyPI

pip install build twine
python -m build
twine check dist/*
twine upload dist/*

Project layout

netio-game/
├── client.py              # Client connection and message loop
├── server.py              # Host, GameManager, connection handling
├── router.py              # Decorator-based route handlers
├── datatypes.py           # PlayerData, ConnectionData
├── serialization/
│   ├── serializer.py      # Serializable base class
│   ├── routing.py         # MessageType, Reader, Writer
│   └── io/io.py           # Binary encode/decode
├── util/                  # GenericType, LazyRef helpers
└── examples/              # Runnable server/client demo

Logging

The library writes debug output to network.log and the server also appends to log.txt during development. Both are listed in .gitignore.

License

MIT — see LICENSE.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

netio_game-0.1.1.tar.gz (18.0 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

netio_game-0.1.1-py3-none-any.whl (21.2 kB view details)

Uploaded Python 3

File details

Details for the file netio_game-0.1.1.tar.gz.

File metadata

  • Download URL: netio_game-0.1.1.tar.gz
  • Upload date:
  • Size: 18.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.4

File hashes

Hashes for netio_game-0.1.1.tar.gz
Algorithm Hash digest
SHA256 91a9f78c20c1209fda8f2bc892ff2a2c55d9c900c84c731a780736a25eebfce7
MD5 73fbdecad59337a19ba1d9af26227738
BLAKE2b-256 5f31e1c3768dccf4bf30e64a13adb59c3fb14c80e773c3fd743030fd03999b8a

See more details on using hashes here.

File details

Details for the file netio_game-0.1.1-py3-none-any.whl.

File metadata

  • Download URL: netio_game-0.1.1-py3-none-any.whl
  • Upload date:
  • Size: 21.2 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.4

File hashes

Hashes for netio_game-0.1.1-py3-none-any.whl
Algorithm Hash digest
SHA256 e9218b9f4275c0124690976bb7900f8f3fa149a030d34c496c1b4be033922299
MD5 71cd841eb324fbbe687909d6b491726d
BLAKE2b-256 5a5fb713101f89e56e7fa7a889b8a72270879ca03940219cd3c0da6635ea96f6

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page