Skip to main content

A high-level Python multiplayer library. Just works.

Project description

nodelink

A high-level Python multiplayer library. Just works.

No presets. No opinions. You define the events, the data, and the behavior — nodelink handles the connections, state sync, and networking underneath.

pip install nodelink

Quickstart

Server:

from nodelink import Server

server = Server(port=5000)

@server.on("connect")
def on_connect(player):
    server.broadcast("joined", {"id": player.id})

@server.on("move")
def on_move(player, data):
    player.update_state({"x": data["x"], "y": data["y"]})

@server.on("disconnect")
def on_disconnect(player):
    server.broadcast("left", {"id": player.id})

@server.on_error()
def on_error(error, player):
    print(f"Error from {player.id}: {error}")

server.start()

Client:

from nodelink import Client

client = Client("localhost", 5000)

@client.on("connect")
def on_connect():
    client.send("move", {"x": 10, "y": 20})

@client.on("joined")
def on_joined(data):
    print(f"Player {data['id']} joined!")

@client.on("__state_sync__")
def on_sync(data):
    for player_id, state in data["players"].items():
        print(f"{player_id}: {state}")

@client.on_error()
def on_error(error):
    print(f"Error: {error}")

client.connect()

Core concepts

Events

Everything is event-based. Send an event from the client, handle it on the server (and vice versa) with a simple decorator.

# Client sends:
client.send("chat", {"msg": "hello!"})

# Server handles:
@server.on("chat")
def on_chat(player, data):
    server.broadcast("chat", {"from": player.id, "msg": data["msg"]})

# All clients receive:
@client.on("chat")
def on_chat(data):
    print(f"[{data['from']}]: {data['msg']}")

Player state sync

Each player has a .state dict. Update it on the server and nodelink automatically broadcasts only the changed fields to all clients at 20 ticks/sec.

@server.on("move")
def on_move(player, data):
    player.update_state({"x": data["x"], "y": data["y"]})
    # That's it — state is synced automatically

Force an immediate sync if you can't wait for the next tick:

server.sync_state()        # delta only
server.sync_state(full=True)  # full state

Sending to one player

@server.on("connect")
def on_connect(player):
    # Send something only to this player
    asyncio.ensure_future(player.send("welcome", {"id": player.id}))

Error handling

Errors in event handlers are caught and routed to your error handler instead of crashing everything.

@server.on_error()
def on_error(error, player):
    print(f"Error from {player.id}: {error}")

@client.on_error()
def on_error(error):
    print(f"Client error: {error}")

Send queue

Messages sent before the client is fully connected are automatically queued and delivered the moment the connection opens. No timing issues.

client.send("hello", {"msg": "this works even before connect() is called"})
client.connect()  # queued message is flushed on connect

API Reference

Server(host, port, tick_rate, delta_only)

Arg Default Description
host "0.0.0.0" Hostname to bind to
port 5000 Port to listen on
tick_rate 20 State syncs per second
delta_only True Only broadcast changed fields
Method Description
server.on(event) Register an event handler
server.on_error() Register an error handler
server.broadcast(event, data) Send event to all players (thread-safe)
server.kick(player_id) Disconnect a player by ID (thread-safe)
server.sync_state(full=False) Force immediate state sync (thread-safe)
server.get_players() List of all connected Players
server.get_player(id) Look up a player by ID
server.player_count Number of connected players
server.start() Start the server (blocking)

Client(host, port)

Method Description
client.on(event) Register an event handler
client.on_error() Register an error handler
client.send(event, data) Send event to server
client.connect() Connect and start listening (blocking)

Player

Attribute/Method Description
player.id Unique player ID
player.state Dict of synced values
player.set_state(key, value) Set one state value
player.get_state(key, default) Get one state value
player.update_state(dict) Merge dict into state
player.clear_state() Wipe state entirely
player.send(event, data) Send directly to this player
player.connected_at Unix timestamp of connection time
player.last_seen Unix timestamp of last message

Built-in events

Event Where Handler signature
"connect" server fn(player)
"disconnect" server fn(player)
"connect" client fn()
"disconnect" client fn()
"__state_sync__" client fn(data)data["players"] is a dict of player_id -> state_delta; data["count"] is the current online player count

Requirements

  • Python 3.8+
  • websockets >= 12.0

License

MIT

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

nodelink-0.6.0.tar.gz (21.2 kB view details)

Uploaded Source

Built Distribution

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

nodelink-0.6.0-py3-none-any.whl (22.4 kB view details)

Uploaded Python 3

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