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@eventhandlers with optional datatype parsing - Object sync — server creates, updates, and deletes
Serializableobjects 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
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
91a9f78c20c1209fda8f2bc892ff2a2c55d9c900c84c731a780736a25eebfce7
|
|
| MD5 |
73fbdecad59337a19ba1d9af26227738
|
|
| BLAKE2b-256 |
5f31e1c3768dccf4bf30e64a13adb59c3fb14c80e773c3fd743030fd03999b8a
|
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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
e9218b9f4275c0124690976bb7900f8f3fa149a030d34c496c1b4be033922299
|
|
| MD5 |
71cd841eb324fbbe687909d6b491726d
|
|
| BLAKE2b-256 |
5a5fb713101f89e56e7fa7a889b8a72270879ca03940219cd3c0da6635ea96f6
|