Skip to content

Commit e79f3e1

Browse files
author
github-actions[bot]
committed
chore(engine): updating docs
1 parent 5b1f28d commit e79f3e1

15 files changed

Lines changed: 483 additions & 0 deletions

engine/docs/.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Doxyfile

engine/docs/documentation.rst

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
Documentation
2+
=============
3+
4+
The documentation for the engine is seperated between the different libraries of the engine.
5+
Then everything is packed up and put into this site. The documentation is handled on the engine repository and then automatically pushed.
6+
7+
This documentation is written in restructured text in order for it to be easier to operate with. We use sphinx to generate the relevant generated documentation.

engine/docs/how_to_use.rst

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
Introduction to using the Engine
2+
================================
3+
4+
Whether you work on this engine as a devlopper or you wanna use this
5+
engine you gonna want to have a test project. This is a walkthrough on
6+
how to setup a basic project
7+
8+
As a devlopper on the engine
9+
----------------------------
10+
11+
As a devlopper you want to be able to use your changes in your project.
12+
Therefore it is recommended to use the provided `cli <https://github.com/NanoForge-dev/CLI>`__
13+
14+
As a user
15+
---------
16+
17+
As a user you can either use the template and change the dependencies
18+
location. Or you can create a project and add the nanoforge
19+
dependencies. Note that it is recommended to use bun as a package
20+
manager.

engine/docs/index.rst

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
Engine
2+
======
3+
4+
.. toctree::
5+
:maxdepth: 2
6+
7+
registry
8+
documentation.rst
9+
how_to_use.rst
10+
11+
In this doc you will find both the how to use and why use this engine as well as its library.
12+
To understand how to use this engine please refer to :doc:`/how_to_use`

engine/docs/network/index.rst

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
Network Overview
2+
================
3+
4+
This page describes the global design and rationale for the engine's networking
5+
libraries: the `network-server` and `network-client` TypeScript packages. These
6+
libraries provide a small, pragmatic networking layer used by the example
7+
`pong-network` game and designed to be easy to understand and integrate.
8+
9+
Goals
10+
-----
11+
12+
- Minimal, predictable protocol for multiplayer games.
13+
- Use well-understood transports: TCP for reliable control messages and UDP
14+
for low-latency, best-effort state updates.
15+
- Provide clear validation hooks (magic value, versioning) to avoid processing
16+
```restructuredtext
17+
Network Overview
18+
================
19+
20+
This page explains how the engine's TypeScript networking packages actually
21+
work and the rationale behind important implementation choices. The two
22+
packages are `network-server` and `network-client` and are used by the
23+
`example/pong-network` project.
24+
25+
Design summary
26+
--------------
27+
28+
- Two logical transports are provided: a reliable, ordered channel (called
29+
"TCP" in the packages) and an unreliable, unordered channel (called
30+
"UDP"). Important: these names refer to channel semantics in the library,
31+
not to raw OS sockets. Implementation details:
32+
- The reliable channel is implemented over WebSocket (node `ws` and
33+
browser `WebSocket`) to provide an ordered, byte-stream-like channel.
34+
- The unreliable channel is implemented as a WebRTC `RTCDataChannel`
35+
created with `ordered: false, maxRetransmits: 0`. WebSocket is used for
36+
signaling/ICE exchange between client and server.
37+
38+
- For packet framing and terminator semantics see the dedicated note:
39+
`docs/network/packet-framing.rst`.
40+
41+
Why these choices
42+
------------------
43+
44+
- WebSocket for reliable messages: WebSocket is universally available in
45+
browsers and easy to host in Node.js. Using it for the "TCP" channel avoids
46+
needing a separate TCP server and simplifies browser + native client parity.
47+
48+
- WebRTC DataChannel for unreliable messages: Browsers cannot open raw UDP
49+
sockets; WebRTC provides a browser-friendly unreliable datagram channel
50+
with low latency. The repository uses WebSocket for ICE signaling and
51+
negotiates a `RTCDataChannel` for actual game-state messages.
52+
53+
- Terminator (magic value) appended at packet end: appending a terminator is
54+
robust against fragmented transport frames. Because WebSocket and RTC
55+
DataChannels can split or aggregate application messages, a terminator
56+
allows the receiver to detect full logical packets regardless of chunking.
57+
58+
- Configurable `magicValue`: The default (`PACKET_END`) is a human-readable
59+
sentinel that makes debugging easier; it is configurable via the server or
60+
client config objects if you prefer a shorter or binary marker.
61+
62+
Serialization and extensibility
63+
-------------------------------
64+
65+
- Example code uses JSON payloads for clarity (easy to inspect and debug).
66+
The transport layer operates on `Uint8Array` buffers, so you can replace
67+
JSON with any binary encoding for
68+
production.
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
TCPClient
2+
~~~~~~~~~
3+
4+
**connect** ()
5+
Initiate a WebSocket connection to the server (e.g. `ws://<ip>:<port>`).
6+
7+
:return: Promise<void>
8+
9+
**isConnected** ()
10+
Return `true` when the underlying WebSocket is open.
11+
12+
:return: boolean
13+
14+
**sendData** (*data*)
15+
Send a payload to the server.
16+
17+
:param data: Uint8Array — raw payload bytes.
18+
:return: void
19+
20+
**getReceivedPackets** ()
21+
Return an array of complete packets that were reassembled from received chunks.
22+
23+
:return: Uint8Array[] — array of packet buffers.
24+
25+
26+
UDPClient
27+
~~~~~~~~~
28+
29+
**connect** ()
30+
Open a WebSocket for signaling, create an RTCPeerConnection and initiate an SDP offer.
31+
32+
:return: Promise<void>
33+
34+
**isConnected** ()
35+
Return `true` when the RTCDataChannel is open.
36+
37+
:return: boolean
38+
39+
**sendData** (*data*)
40+
Send a payload on the data channel.
41+
42+
:param data: Uint8Array — raw payload bytes.
43+
:return: void
44+
45+
**getReceivedPackets** ()
46+
Return an array of complete packets reassembled from received data-channel chunks.
47+
48+
:return: Uint8Array[] — array of packet buffers.
49+
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
Client Networking (network-client)
2+
==================================
3+
4+
This document describes how the `network-client` package actually connects
5+
to a `NetworkServerLibrary` instance and the concrete APIs you will use from
6+
client-side code.
7+
8+
Overview
9+
--------
10+
11+
A client connects to a single server instance (one TCP/WebSocket control
12+
channel and optionally a single WebRTC data channel for unreliable traffic).
13+
Client responsibilities in a game are typically:
14+
15+
- Initiate a TCP connection and send control commands (join/play/input).
16+
- Optionally negotiate a WebRTC data channel for receiving server snapshots
17+
or sending low-latency updates.
18+
19+
Example
20+
--------------------------------------------------
21+
22+
It works exactly the same with UDP
23+
24+
.. code-block:: javascript
25+
26+
// Wait for connection to be established
27+
async function waitForConnection(): Promise<void> {
28+
if (network.tcp?.isConnected()) return;
29+
30+
return new Promise((resolve) => {
31+
const check = () => {
32+
if (network.tcp.isConnected()) {
33+
resolve();
34+
} else {
35+
setTimeout(check, 50);
36+
}
37+
};
38+
check();
39+
});
40+
}
41+
await waitForConnection();
42+
43+
// Send a json encoded packet
44+
network.tcp.sendData(new TextEncoder().encode(JSON.stringify({ hello: 'world' })));
45+
46+
// Receive raw server packets
47+
const latestsPackets = getReceivedPackets();
48+
49+
// Decode packets if encoded in json
50+
const decodedPackets = latestsPackets.map((packet) => {
51+
return JSON.parse(new TextDecoder().decode(packet));
52+
});
53+
54+
Notes
55+
-----
56+
57+
- See `docs/network/network-client-api.rst` for the exact list available functions.
58+
- For packet framing/terminator semantics see `docs/network/packet-framing.rst`.
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
TCPServer
2+
~~~~~~~~~
3+
4+
**listen** ()
5+
Start the WebSocket server and begin accepting clients.
6+
7+
:return: void
8+
9+
**getConnectedClients** ()
10+
Return a snapshot array of numeric client IDs currently connected.
11+
12+
:return: number[] — an array of client IDs.
13+
14+
**sendToEverybody** (*data*)
15+
Send a payload to every connected client.
16+
17+
:param data: Uint8Array — raw payload bytes.
18+
:return: void
19+
:throws: none (errors are logged, not thrown)
20+
21+
**sendToClient** (*clientId, data*)
22+
Send a payload to the client identified by `clientId`.
23+
24+
:param clientId: number — numeric client identifier.
25+
:param data: Uint8Array — payload bytes.
26+
:return: void
27+
:throws: none (logs if client unknown)
28+
29+
**getReceivedPackets** ()
30+
Parse and return complete packets received from each client. Each packet is a `Uint8Array` buffer.
31+
32+
:return: Map<number, Uint8Array[]> — mapping client ID to array of packets.
33+
34+
35+
UDPServer
36+
~~~~~~~~~
37+
38+
**listen** ()
39+
Start the signaling WebSocket and accept incoming client offers (SDP/ICE).
40+
41+
:return: void
42+
43+
**getConnectedClients** ()
44+
Return a snapshot array of client IDs with active data channels.
45+
46+
:return: number[] — list of client IDs.
47+
48+
**sendToEverybody** (*data*)
49+
Send a payload to every connected data channel.
50+
51+
:param data: Uint8Array — raw payload bytes.
52+
:return: void
53+
54+
**sendToClient** (*clientId, data*)
55+
Send a payload to a single client data channel.
56+
57+
:param clientId: number
58+
:param data: Uint8Array
59+
:return: void
60+
61+
**getReceivedPackets** ()
62+
Parse incoming channel chunks and return a map of complete packets per client.
63+
64+
:return: Map<number, Uint8Array[]> — mapping client ID to array of packets.
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
Server Networking (network-server)
2+
==================================
3+
4+
This document explains the actual `network-server` package implementation and
5+
the APIs you will use from server-side code.
6+
7+
Overview
8+
--------
9+
10+
The server listens on configured ports and accepts client connections.
11+
A single server process can accept many clients; typical server responsibilities
12+
in a game are:
13+
14+
- Accept reliable control messages from clients over the TCP (WebSocket) channel.
15+
- Optionally establish `RTCPeerConnection`s (via WebSocket signaling) to receive
16+
unreliable, unordered data channels for low-latency state updates.
17+
18+
Example
19+
--------------------------------------------------
20+
21+
It works exactly the same with UDP
22+
23+
.. code-block:: javascript
24+
// Send everybody a json encoded packet
25+
network.tcp.sendToEverybody(
26+
new TextEncoder().encode(JSON.stringify(
27+
{ type: "are you here" }
28+
))
29+
);
30+
31+
// Check connected clients
32+
const connectedClients = getConnectedClients();
33+
34+
// Receive all packets
35+
const allPackets = network.tcp.getReceivedPackets();
36+
37+
// Get first client packets
38+
const firstClientPackets = map.get(connectedClients[0]);
39+
40+
// Decode packets if encoded in json
41+
const decodedPackets = firstClientPackets.map((packet) => {
42+
return JSON.parse(new TextDecoder().decode(packet));
43+
});
44+
45+
Notes
46+
-----
47+
48+
- See `docs/network/network-server-api.rst` for the exact list available functions.
49+
- For packet framing and terminator semantics see `docs/network/packet-framing.rst`.
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
Packet Framing — Magic Terminator
2+
================================
3+
4+
This short note explains how packets are framed and reassembled in the
5+
network packages, at a logical level. The framing strategy is
6+
simple and robust to transport fragmentation or aggregation.
7+
8+
Concept
9+
-------
10+
11+
- Each logical packet is a payload of bytes that the application wants to
12+
send (JSON or binary).
13+
- Before sending, the library *appends* a configurable terminator string
14+
(the "magic value") to the end of the payload. The default value in the
15+
project config is `PACKET_END`.
16+
- The terminator is applied as bytes (UTF-8 encoding of the configured
17+
string) and therefore forms a unique byte suffix that marks the end of a
18+
logical packet.
19+
20+
Why an end terminator
21+
---------------------
22+
23+
- Transports like WebSocket and RTC DataChannels may split or combine
24+
application messages into arbitrary chunks. A terminator lets a receiver
25+
reliably detect the end of each logical packet regardless of how the
26+
transport fragments or aggregates bytes.
27+
- Using a short, human-readable terminator makes debugging easier.
28+
- The terminator is configurable so you can pick a value that does not
29+
collide with your payload contents (especially important if using raw or
30+
binary payloads).
31+
32+
Sender logic
33+
---------------------------
34+
35+
1. Serialize the application message into bytes (e.g., JSON => UTF-8
36+
bytes, or a binary codec output).
37+
2. Append the configured terminator bytes to the end of the payload.
38+
3. Send the resulting buffer on the transport (WebSocket or DataChannel).
39+
40+
Receiver logic
41+
-----------------------------
42+
43+
1. Accumulate incoming chunks of bytes into a per-connection buffer.
44+
2. Repeatedly search the accumulated buffer for the terminator sequence.
45+
3. For each occurrence, extract bytes from buffer start up to (but not
46+
including) the terminator — this is a complete logical packet.
47+
4. Remove the extracted packet and trailing terminator from the buffer and
48+
continue searching; keep any leftover bytes (partial packet) for the
49+
next incoming chunk.

0 commit comments

Comments
 (0)