A Python library for Neverwinter Nights networking.
Project description
nwn-message
Message definitions and websocket client for the Neverwinter Nights networking protocol.
This library has the full message index from the game binary, and can encode and decode messages in a simplified wire format as implemented by NWNX_WSNetLayer.
It also ships a fully functional async websocket client that can connect to a server running the plugin, log in as a player, and send/receive messages as structured Python objects.
This message library can only connect to servers that run the NWNX_WSNetLayer plugin. The decision not to expose the current UDP-based transport was made to reduce the likelyhood of abuse for existing servers. Thus, any server that wishes to allow custom clients is required to run the NWNX plugin.
How to use this repository
This project serves two distinct purposes, and how you consume it depends on which one you need:
-
As a Python library: install from PyPI with
pip install nwn-messageoruv add nwn-message. You get the full message definitions, encoding/decoding layer, and async websocket client. This is the right choice for Python bots, custom clients, and server-side tooling. -
As a codegen schema: the same message definitions can generate bindings for other languages (Godot, TypeScript). Because codegen requires the full source tree and the running codegen script, it is excluded from the PyPI package. Clone the repository from GitHub and see "Usage in other languages" below. This may change in the future.
Requirements
- Python 3.14+
- NWN:EE 37.17
Version compatibility
This package only speaks with 8193-37.17; which is the latest official stable version of NWN:EE as of this writing. Earlier (or later) versions are not supported.
The library has some base scaffolding for supporting past or future versions; however the divergences are not implemented to reduce the testing surface.
Message definitions
The source of truth for this library is a registry of Python dataclasses in
src/nwn_message/messages/, indexed by the NWN protocol's (major, minor, direction)
tuple. Each dataclass uses annotated type hints to describe its wire format: field types,
sizes, conditional guards, tagged unions, and size prefixes are all expressed declaratively
through Python's typing.Annotated and custom metadata types.
Most messages are fully declarative: their on-wire layout can be determined entirely from
their type annotations. A handful of messages with highly irregular or context-dependent
formats define custom read/write methods instead. These are the exception, not the
rule.
Because the message definitions are plain Python dataclasses, they serve as both the runtime encoding/decoding layer and the schema for code generation. Any tool that wants to speak the WSNetLayer protocol can use these same types as its source of truth.
Client Usage
The supplied client library is fully async and meant to be wired up using the popular
blinker library. It will emit signals for each message type received, and you can
register handlers for those signals to process messages as they arrive. You can also
register for sub-messages (such as specific game object updates); complex messages are
decomposed inside the library for this purpose.
The example client at examples/client.py walks through the full connect-flow. It is
meant to be copied out into your own parent project and changed there.
This library uses the standard logging framework to emit debug informations. Use the python builtin logging getters to mute the subsystems you don't care about.
Usage in other languages
Codegen to other languages is very, very preliminary (insert Jack Sparrow meme). Targets currently include Godot and TypeScript. The generator is deliberately not exposed through the package or public API, as it has been vibecoded only to serve a singular purpose: The generated code was tested in anger in the Godot-based Glyph client (to be released soon-ish).
To invoke the codegen, clone the repository (codegen is currently excluded from the PyPI
package) and run uv run run_codegen.py --help. Please note that the codegen command may
be quite destructive to your checkout/generated files.
Not all messages are fully declarative (some are bespoke Python read/write). Codegen writes stubs for this into custom/. Implementing these is left as an exercise to the reader and only has to be done once, unless the message format changes.
Each codegen target also includes some glue code you need to import into your project; basically containing the wire reader/writer and helpers.
Stability
The message definitions and wire format are, while complete, not yet API-stable. All messages should be (!) implemented, but some types are stubs (e.g. a int might be a stand-in for a missing enum type, or some nested classes are needlessly complex). These will be filled in or amended as we go.
Changes to the message spec will necessitate changes to any client code that uses this library. If you do build on this library, you will need to watch for updates to message definitions.
The wire format (WSNetLayer) might also see changes but is at least major-versioned.
Contributing
There is no formal contribution process yet. All contributions will require to be licenced under the GPLv3. If in doubt, just open a discussion/issue.
License
This project is licensed under the GPLv3.
The spirit of this license choice is: if you build on this work, share it back. The NWN ecosystem thrives on open tools, not closed ones.
Whether you're building a custom client, a bot, or a whole new server, we hope you'll open-source your work too. And even if you can't open everything, come tell us what you're building anyway. We'd love to hear about it.
Get in touch
- Join the community chat: https://nwn.zulipchat.com/
- Open a new issue or discussion thread here on GitHub.
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 nwn_message-0.1.0.tar.gz.
File metadata
- Download URL: nwn_message-0.1.0.tar.gz
- Upload date:
- Size: 353.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.11.28 {"installer":{"name":"uv","version":"0.11.28","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Fedora Linux","version":"44","id":"","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
835d0d86ec52d6a404c1ce2511553db225b3223d6161060700627582852c879f
|
|
| MD5 |
1ad6f932d8497f6eb3b6d4547fc6f85d
|
|
| BLAKE2b-256 |
ac0cd9f511db890fcedf591bdc2443b3efa01f1a10b43298517eafa98ed126dc
|
File details
Details for the file nwn_message-0.1.0-py3-none-any.whl.
File metadata
- Download URL: nwn_message-0.1.0-py3-none-any.whl
- Upload date:
- Size: 121.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.11.28 {"installer":{"name":"uv","version":"0.11.28","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Fedora Linux","version":"44","id":"","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
6169c8bf881db46e92acc4dc128fdadd756136db5cae03374c398cae2e3cba1e
|
|
| MD5 |
aadba047cf1a2427e93d07bcca329568
|
|
| BLAKE2b-256 |
355dd668312d5adf39f1b9f38822449c32eaf6b0f98ad734ef3c13705e56c6f4
|