nodelink 0.2.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- nodelink-0.2.0/LICENSE +21 -0
- nodelink-0.2.0/PKG-INFO +233 -0
- nodelink-0.2.0/README.md +203 -0
- nodelink-0.2.0/nodelink/__init__.py +38 -0
- nodelink-0.2.0/nodelink/client.py +221 -0
- nodelink-0.2.0/nodelink/player.py +79 -0
- nodelink-0.2.0/nodelink/server.py +249 -0
- nodelink-0.2.0/nodelink/sync.py +133 -0
- nodelink-0.2.0/nodelink/transport/__init__.py +3 -0
- nodelink-0.2.0/nodelink/transport/websocket.py +113 -0
- nodelink-0.2.0/nodelink.egg-info/PKG-INFO +233 -0
- nodelink-0.2.0/nodelink.egg-info/SOURCES.txt +16 -0
- nodelink-0.2.0/nodelink.egg-info/dependency_links.txt +1 -0
- nodelink-0.2.0/nodelink.egg-info/requires.txt +1 -0
- nodelink-0.2.0/nodelink.egg-info/top_level.txt +1 -0
- nodelink-0.2.0/pyproject.toml +39 -0
- nodelink-0.2.0/setup.cfg +4 -0
- nodelink-0.2.0/tests/test_nodelink.py +140 -0
nodelink-0.2.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 Marcus
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
nodelink-0.2.0/PKG-INFO
ADDED
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: nodelink
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: A high-level Python multiplayer library. Just works.
|
|
5
|
+
Author: Marcus
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/yourusername/nodelink
|
|
8
|
+
Project-URL: Repository, https://github.com/yourusername/nodelink
|
|
9
|
+
Project-URL: Issues, https://github.com/yourusername/nodelink/issues
|
|
10
|
+
Keywords: multiplayer,networking,websocket,gamedev,realtime
|
|
11
|
+
Classifier: Development Status :: 3 - Alpha
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
22
|
+
Classifier: Topic :: Games/Entertainment
|
|
23
|
+
Classifier: Topic :: Internet
|
|
24
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
25
|
+
Requires-Python: >=3.8
|
|
26
|
+
Description-Content-Type: text/markdown
|
|
27
|
+
License-File: LICENSE
|
|
28
|
+
Requires-Dist: websockets>=12.0
|
|
29
|
+
Dynamic: license-file
|
|
30
|
+
|
|
31
|
+
# nodelink
|
|
32
|
+
|
|
33
|
+
**A high-level Python multiplayer library. Just works.**
|
|
34
|
+
|
|
35
|
+
No presets. No opinions. You define the events, the data, and the behavior — nodelink handles the connections, state sync, and networking underneath.
|
|
36
|
+
|
|
37
|
+
```
|
|
38
|
+
pip install nodelink
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
---
|
|
42
|
+
|
|
43
|
+
## Quickstart
|
|
44
|
+
|
|
45
|
+
**Server:**
|
|
46
|
+
```python
|
|
47
|
+
from nodelink import Server
|
|
48
|
+
|
|
49
|
+
server = Server(port=5000)
|
|
50
|
+
|
|
51
|
+
@server.on("connect")
|
|
52
|
+
def on_connect(player):
|
|
53
|
+
server.broadcast("joined", {"id": player.id})
|
|
54
|
+
|
|
55
|
+
@server.on("move")
|
|
56
|
+
def on_move(player, data):
|
|
57
|
+
player.update_state({"x": data["x"], "y": data["y"]})
|
|
58
|
+
|
|
59
|
+
@server.on("disconnect")
|
|
60
|
+
def on_disconnect(player):
|
|
61
|
+
server.broadcast("left", {"id": player.id})
|
|
62
|
+
|
|
63
|
+
@server.on_error()
|
|
64
|
+
def on_error(error, player):
|
|
65
|
+
print(f"Error from {player.id}: {error}")
|
|
66
|
+
|
|
67
|
+
server.start()
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
**Client:**
|
|
71
|
+
```python
|
|
72
|
+
from nodelink import Client
|
|
73
|
+
|
|
74
|
+
client = Client("localhost", 5000)
|
|
75
|
+
|
|
76
|
+
@client.on("connect")
|
|
77
|
+
def on_connect():
|
|
78
|
+
client.send("move", {"x": 10, "y": 20})
|
|
79
|
+
|
|
80
|
+
@client.on("joined")
|
|
81
|
+
def on_joined(data):
|
|
82
|
+
print(f"Player {data['id']} joined!")
|
|
83
|
+
|
|
84
|
+
@client.on("__state_sync__")
|
|
85
|
+
def on_sync(data):
|
|
86
|
+
for player_id, state in data["players"].items():
|
|
87
|
+
print(f"{player_id}: {state}")
|
|
88
|
+
|
|
89
|
+
@client.on_error()
|
|
90
|
+
def on_error(error):
|
|
91
|
+
print(f"Error: {error}")
|
|
92
|
+
|
|
93
|
+
client.connect()
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
---
|
|
97
|
+
|
|
98
|
+
## Core concepts
|
|
99
|
+
|
|
100
|
+
### Events
|
|
101
|
+
Everything is event-based. Send an event from the client, handle it on the server (and vice versa) with a simple decorator.
|
|
102
|
+
|
|
103
|
+
```python
|
|
104
|
+
# Client sends:
|
|
105
|
+
client.send("chat", {"msg": "hello!"})
|
|
106
|
+
|
|
107
|
+
# Server handles:
|
|
108
|
+
@server.on("chat")
|
|
109
|
+
def on_chat(player, data):
|
|
110
|
+
server.broadcast("chat", {"from": player.id, "msg": data["msg"]})
|
|
111
|
+
|
|
112
|
+
# All clients receive:
|
|
113
|
+
@client.on("chat")
|
|
114
|
+
def on_chat(data):
|
|
115
|
+
print(f"[{data['from']}]: {data['msg']}")
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
### Player state sync
|
|
119
|
+
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.
|
|
120
|
+
|
|
121
|
+
```python
|
|
122
|
+
@server.on("move")
|
|
123
|
+
def on_move(player, data):
|
|
124
|
+
player.update_state({"x": data["x"], "y": data["y"]})
|
|
125
|
+
# That's it — state is synced automatically
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
Force an immediate sync if you can't wait for the next tick:
|
|
129
|
+
```python
|
|
130
|
+
server.sync_state() # delta only
|
|
131
|
+
server.sync_state(full=True) # full state
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
### Sending to one player
|
|
135
|
+
```python
|
|
136
|
+
@server.on("connect")
|
|
137
|
+
def on_connect(player):
|
|
138
|
+
# Send something only to this player
|
|
139
|
+
asyncio.ensure_future(player.send("welcome", {"id": player.id}))
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
### Error handling
|
|
143
|
+
Errors in event handlers are caught and routed to your error handler instead of crashing everything.
|
|
144
|
+
|
|
145
|
+
```python
|
|
146
|
+
@server.on_error()
|
|
147
|
+
def on_error(error, player):
|
|
148
|
+
print(f"Error from {player.id}: {error}")
|
|
149
|
+
|
|
150
|
+
@client.on_error()
|
|
151
|
+
def on_error(error):
|
|
152
|
+
print(f"Client error: {error}")
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
### Send queue
|
|
156
|
+
Messages sent before the client is fully connected are automatically queued and delivered the moment the connection opens. No timing issues.
|
|
157
|
+
|
|
158
|
+
```python
|
|
159
|
+
client.send("hello", {"msg": "this works even before connect() is called"})
|
|
160
|
+
client.connect() # queued message is flushed on connect
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
---
|
|
164
|
+
|
|
165
|
+
## API Reference
|
|
166
|
+
|
|
167
|
+
### `Server(host, port, tick_rate, delta_only)`
|
|
168
|
+
|
|
169
|
+
| Arg | Default | Description |
|
|
170
|
+
|-----|---------|-------------|
|
|
171
|
+
| `host` | `"0.0.0.0"` | Hostname to bind to |
|
|
172
|
+
| `port` | `5000` | Port to listen on |
|
|
173
|
+
| `tick_rate` | `20` | State syncs per second |
|
|
174
|
+
| `delta_only` | `True` | Only broadcast changed fields |
|
|
175
|
+
|
|
176
|
+
| Method | Description |
|
|
177
|
+
|--------|-------------|
|
|
178
|
+
| `server.on(event)` | Register an event handler |
|
|
179
|
+
| `server.on_error()` | Register an error handler |
|
|
180
|
+
| `server.broadcast(event, data)` | Send event to all players |
|
|
181
|
+
| `server.sync_state(full=False)` | Force immediate state sync |
|
|
182
|
+
| `server.get_players()` | List of all connected Players |
|
|
183
|
+
| `server.get_player(id)` | Look up a player by ID |
|
|
184
|
+
| `server.player_count` | Number of connected players |
|
|
185
|
+
| `server.start()` | Start the server (blocking) |
|
|
186
|
+
|
|
187
|
+
### `Client(host, port)`
|
|
188
|
+
|
|
189
|
+
| Method | Description |
|
|
190
|
+
|--------|-------------|
|
|
191
|
+
| `client.on(event)` | Register an event handler |
|
|
192
|
+
| `client.on_error()` | Register an error handler |
|
|
193
|
+
| `client.send(event, data)` | Send event to server |
|
|
194
|
+
| `client.connect()` | Connect and start listening (blocking) |
|
|
195
|
+
|
|
196
|
+
### `Player`
|
|
197
|
+
|
|
198
|
+
| Attribute/Method | Description |
|
|
199
|
+
|-----------------|-------------|
|
|
200
|
+
| `player.id` | Unique player ID |
|
|
201
|
+
| `player.state` | Dict of synced values |
|
|
202
|
+
| `player.set_state(key, value)` | Set one state value |
|
|
203
|
+
| `player.get_state(key, default)` | Get one state value |
|
|
204
|
+
| `player.update_state(dict)` | Merge dict into state |
|
|
205
|
+
| `player.clear_state()` | Wipe state entirely |
|
|
206
|
+
| `player.send(event, data)` | Send directly to this player |
|
|
207
|
+
| `player.connected_at` | Unix timestamp of connection time |
|
|
208
|
+
| `player.last_seen` | Unix timestamp of last message |
|
|
209
|
+
|
|
210
|
+
---
|
|
211
|
+
|
|
212
|
+
## Built-in events
|
|
213
|
+
|
|
214
|
+
| Event | Where | Handler signature |
|
|
215
|
+
|-------|-------|------------------|
|
|
216
|
+
| `"connect"` | server | `fn(player)` |
|
|
217
|
+
| `"disconnect"` | server | `fn(player)` |
|
|
218
|
+
| `"connect"` | client | `fn()` |
|
|
219
|
+
| `"disconnect"` | client | `fn()` |
|
|
220
|
+
| `"__state_sync__"` | client | `fn(data)` — `data["players"]` is a dict of `player_id -> state_delta` |
|
|
221
|
+
|
|
222
|
+
---
|
|
223
|
+
|
|
224
|
+
## Requirements
|
|
225
|
+
|
|
226
|
+
- Python 3.8+
|
|
227
|
+
- `websockets >= 12.0`
|
|
228
|
+
|
|
229
|
+
---
|
|
230
|
+
|
|
231
|
+
## License
|
|
232
|
+
|
|
233
|
+
MIT
|
nodelink-0.2.0/README.md
ADDED
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
# nodelink
|
|
2
|
+
|
|
3
|
+
**A high-level Python multiplayer library. Just works.**
|
|
4
|
+
|
|
5
|
+
No presets. No opinions. You define the events, the data, and the behavior — nodelink handles the connections, state sync, and networking underneath.
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
pip install nodelink
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## Quickstart
|
|
14
|
+
|
|
15
|
+
**Server:**
|
|
16
|
+
```python
|
|
17
|
+
from nodelink import Server
|
|
18
|
+
|
|
19
|
+
server = Server(port=5000)
|
|
20
|
+
|
|
21
|
+
@server.on("connect")
|
|
22
|
+
def on_connect(player):
|
|
23
|
+
server.broadcast("joined", {"id": player.id})
|
|
24
|
+
|
|
25
|
+
@server.on("move")
|
|
26
|
+
def on_move(player, data):
|
|
27
|
+
player.update_state({"x": data["x"], "y": data["y"]})
|
|
28
|
+
|
|
29
|
+
@server.on("disconnect")
|
|
30
|
+
def on_disconnect(player):
|
|
31
|
+
server.broadcast("left", {"id": player.id})
|
|
32
|
+
|
|
33
|
+
@server.on_error()
|
|
34
|
+
def on_error(error, player):
|
|
35
|
+
print(f"Error from {player.id}: {error}")
|
|
36
|
+
|
|
37
|
+
server.start()
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
**Client:**
|
|
41
|
+
```python
|
|
42
|
+
from nodelink import Client
|
|
43
|
+
|
|
44
|
+
client = Client("localhost", 5000)
|
|
45
|
+
|
|
46
|
+
@client.on("connect")
|
|
47
|
+
def on_connect():
|
|
48
|
+
client.send("move", {"x": 10, "y": 20})
|
|
49
|
+
|
|
50
|
+
@client.on("joined")
|
|
51
|
+
def on_joined(data):
|
|
52
|
+
print(f"Player {data['id']} joined!")
|
|
53
|
+
|
|
54
|
+
@client.on("__state_sync__")
|
|
55
|
+
def on_sync(data):
|
|
56
|
+
for player_id, state in data["players"].items():
|
|
57
|
+
print(f"{player_id}: {state}")
|
|
58
|
+
|
|
59
|
+
@client.on_error()
|
|
60
|
+
def on_error(error):
|
|
61
|
+
print(f"Error: {error}")
|
|
62
|
+
|
|
63
|
+
client.connect()
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
---
|
|
67
|
+
|
|
68
|
+
## Core concepts
|
|
69
|
+
|
|
70
|
+
### Events
|
|
71
|
+
Everything is event-based. Send an event from the client, handle it on the server (and vice versa) with a simple decorator.
|
|
72
|
+
|
|
73
|
+
```python
|
|
74
|
+
# Client sends:
|
|
75
|
+
client.send("chat", {"msg": "hello!"})
|
|
76
|
+
|
|
77
|
+
# Server handles:
|
|
78
|
+
@server.on("chat")
|
|
79
|
+
def on_chat(player, data):
|
|
80
|
+
server.broadcast("chat", {"from": player.id, "msg": data["msg"]})
|
|
81
|
+
|
|
82
|
+
# All clients receive:
|
|
83
|
+
@client.on("chat")
|
|
84
|
+
def on_chat(data):
|
|
85
|
+
print(f"[{data['from']}]: {data['msg']}")
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
### Player state sync
|
|
89
|
+
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.
|
|
90
|
+
|
|
91
|
+
```python
|
|
92
|
+
@server.on("move")
|
|
93
|
+
def on_move(player, data):
|
|
94
|
+
player.update_state({"x": data["x"], "y": data["y"]})
|
|
95
|
+
# That's it — state is synced automatically
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
Force an immediate sync if you can't wait for the next tick:
|
|
99
|
+
```python
|
|
100
|
+
server.sync_state() # delta only
|
|
101
|
+
server.sync_state(full=True) # full state
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
### Sending to one player
|
|
105
|
+
```python
|
|
106
|
+
@server.on("connect")
|
|
107
|
+
def on_connect(player):
|
|
108
|
+
# Send something only to this player
|
|
109
|
+
asyncio.ensure_future(player.send("welcome", {"id": player.id}))
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
### Error handling
|
|
113
|
+
Errors in event handlers are caught and routed to your error handler instead of crashing everything.
|
|
114
|
+
|
|
115
|
+
```python
|
|
116
|
+
@server.on_error()
|
|
117
|
+
def on_error(error, player):
|
|
118
|
+
print(f"Error from {player.id}: {error}")
|
|
119
|
+
|
|
120
|
+
@client.on_error()
|
|
121
|
+
def on_error(error):
|
|
122
|
+
print(f"Client error: {error}")
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
### Send queue
|
|
126
|
+
Messages sent before the client is fully connected are automatically queued and delivered the moment the connection opens. No timing issues.
|
|
127
|
+
|
|
128
|
+
```python
|
|
129
|
+
client.send("hello", {"msg": "this works even before connect() is called"})
|
|
130
|
+
client.connect() # queued message is flushed on connect
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
---
|
|
134
|
+
|
|
135
|
+
## API Reference
|
|
136
|
+
|
|
137
|
+
### `Server(host, port, tick_rate, delta_only)`
|
|
138
|
+
|
|
139
|
+
| Arg | Default | Description |
|
|
140
|
+
|-----|---------|-------------|
|
|
141
|
+
| `host` | `"0.0.0.0"` | Hostname to bind to |
|
|
142
|
+
| `port` | `5000` | Port to listen on |
|
|
143
|
+
| `tick_rate` | `20` | State syncs per second |
|
|
144
|
+
| `delta_only` | `True` | Only broadcast changed fields |
|
|
145
|
+
|
|
146
|
+
| Method | Description |
|
|
147
|
+
|--------|-------------|
|
|
148
|
+
| `server.on(event)` | Register an event handler |
|
|
149
|
+
| `server.on_error()` | Register an error handler |
|
|
150
|
+
| `server.broadcast(event, data)` | Send event to all players |
|
|
151
|
+
| `server.sync_state(full=False)` | Force immediate state sync |
|
|
152
|
+
| `server.get_players()` | List of all connected Players |
|
|
153
|
+
| `server.get_player(id)` | Look up a player by ID |
|
|
154
|
+
| `server.player_count` | Number of connected players |
|
|
155
|
+
| `server.start()` | Start the server (blocking) |
|
|
156
|
+
|
|
157
|
+
### `Client(host, port)`
|
|
158
|
+
|
|
159
|
+
| Method | Description |
|
|
160
|
+
|--------|-------------|
|
|
161
|
+
| `client.on(event)` | Register an event handler |
|
|
162
|
+
| `client.on_error()` | Register an error handler |
|
|
163
|
+
| `client.send(event, data)` | Send event to server |
|
|
164
|
+
| `client.connect()` | Connect and start listening (blocking) |
|
|
165
|
+
|
|
166
|
+
### `Player`
|
|
167
|
+
|
|
168
|
+
| Attribute/Method | Description |
|
|
169
|
+
|-----------------|-------------|
|
|
170
|
+
| `player.id` | Unique player ID |
|
|
171
|
+
| `player.state` | Dict of synced values |
|
|
172
|
+
| `player.set_state(key, value)` | Set one state value |
|
|
173
|
+
| `player.get_state(key, default)` | Get one state value |
|
|
174
|
+
| `player.update_state(dict)` | Merge dict into state |
|
|
175
|
+
| `player.clear_state()` | Wipe state entirely |
|
|
176
|
+
| `player.send(event, data)` | Send directly to this player |
|
|
177
|
+
| `player.connected_at` | Unix timestamp of connection time |
|
|
178
|
+
| `player.last_seen` | Unix timestamp of last message |
|
|
179
|
+
|
|
180
|
+
---
|
|
181
|
+
|
|
182
|
+
## Built-in events
|
|
183
|
+
|
|
184
|
+
| Event | Where | Handler signature |
|
|
185
|
+
|-------|-------|------------------|
|
|
186
|
+
| `"connect"` | server | `fn(player)` |
|
|
187
|
+
| `"disconnect"` | server | `fn(player)` |
|
|
188
|
+
| `"connect"` | client | `fn()` |
|
|
189
|
+
| `"disconnect"` | client | `fn()` |
|
|
190
|
+
| `"__state_sync__"` | client | `fn(data)` — `data["players"]` is a dict of `player_id -> state_delta` |
|
|
191
|
+
|
|
192
|
+
---
|
|
193
|
+
|
|
194
|
+
## Requirements
|
|
195
|
+
|
|
196
|
+
- Python 3.8+
|
|
197
|
+
- `websockets >= 12.0`
|
|
198
|
+
|
|
199
|
+
---
|
|
200
|
+
|
|
201
|
+
## License
|
|
202
|
+
|
|
203
|
+
MIT
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
"""
|
|
2
|
+
nodelink
|
|
3
|
+
~~~~~~~~
|
|
4
|
+
A high-level Python multiplayer library. Just works.
|
|
5
|
+
|
|
6
|
+
Basic usage::
|
|
7
|
+
|
|
8
|
+
from nodelink import Server, Client
|
|
9
|
+
|
|
10
|
+
# Server
|
|
11
|
+
server = Server(port=5000)
|
|
12
|
+
|
|
13
|
+
@server.on("connect")
|
|
14
|
+
def on_connect(player):
|
|
15
|
+
server.broadcast("joined", {"id": player.id})
|
|
16
|
+
|
|
17
|
+
server.start()
|
|
18
|
+
|
|
19
|
+
# Client
|
|
20
|
+
client = Client("localhost", 5000)
|
|
21
|
+
|
|
22
|
+
@client.on("joined")
|
|
23
|
+
def on_joined(data):
|
|
24
|
+
print(f"Player {data['id']} joined!")
|
|
25
|
+
|
|
26
|
+
client.connect()
|
|
27
|
+
|
|
28
|
+
:copyright: 2024 Marcus
|
|
29
|
+
:license: MIT
|
|
30
|
+
"""
|
|
31
|
+
|
|
32
|
+
from .server import Server
|
|
33
|
+
from .client import Client
|
|
34
|
+
from .player import Player
|
|
35
|
+
|
|
36
|
+
__version__ = "0.2.0"
|
|
37
|
+
__author__ = "Marcus"
|
|
38
|
+
__all__ = ["Server", "Client", "Player"]
|