multiplayer 1.0.0__tar.gz → 1.1.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.
- {multiplayer-1.0.0 → multiplayer-1.1.0}/PKG-INFO +8 -6
- {multiplayer-1.0.0 → multiplayer-1.1.0}/README.md +6 -5
- {multiplayer-1.0.0 → multiplayer-1.1.0}/pyproject.toml +7 -1
- {multiplayer-1.0.0 → multiplayer-1.1.0}/src/multiplayer/__init__.py +1 -1
- {multiplayer-1.0.0 → multiplayer-1.1.0}/src/multiplayer/client.py +20 -12
- multiplayer-1.1.0/src/multiplayer/data/persistence.py +516 -0
- {multiplayer-1.0.0 → multiplayer-1.1.0}/src/multiplayer/game.py +72 -8
- {multiplayer-1.0.0 → multiplayer-1.1.0}/src/multiplayer/server.py +270 -101
- {multiplayer-1.0.0 → multiplayer-1.1.0}/src/multiplayer/utils.py +23 -0
- multiplayer-1.0.0/src/multiplayer/data/persistence.py +0 -280
- {multiplayer-1.0.0 → multiplayer-1.1.0}/LICENSE.md +0 -0
- {multiplayer-1.0.0 → multiplayer-1.1.0}/src/multiplayer/IPClogging/__init__.py +0 -0
- {multiplayer-1.0.0 → multiplayer-1.1.0}/src/multiplayer/IPClogging/echoing.py +0 -0
- {multiplayer-1.0.0 → multiplayer-1.1.0}/src/multiplayer/IPClogging/server.py +0 -0
- {multiplayer-1.0.0 → multiplayer-1.1.0}/src/multiplayer/IPClogging/test.py +0 -0
- {multiplayer-1.0.0 → multiplayer-1.1.0}/src/multiplayer/data/__init__.py +0 -0
- {multiplayer-1.0.0 → multiplayer-1.1.0}/src/multiplayer/data/cities.csv +0 -0
- {multiplayer-1.0.0 → multiplayer-1.1.0}/src/multiplayer/data/countries.csv +0 -0
- {multiplayer-1.0.0 → multiplayer-1.1.0}/src/multiplayer/data/egyptian_gods.csv +0 -0
- {multiplayer-1.0.0 → multiplayer-1.1.0}/src/multiplayer/data/european_kings.csv +0 -0
- {multiplayer-1.0.0 → multiplayer-1.1.0}/src/multiplayer/data/european_queens.csv +0 -0
- {multiplayer-1.0.0 → multiplayer-1.1.0}/src/multiplayer/data/greek_gods.csv +0 -0
- {multiplayer-1.0.0 → multiplayer-1.1.0}/src/multiplayer/data/planets_moons.csv +0 -0
- {multiplayer-1.0.0 → multiplayer-1.1.0}/src/multiplayer/data/rivers.csv +0 -0
- {multiplayer-1.0.0 → multiplayer-1.1.0}/src/multiplayer/data/roman_gods.csv +0 -0
- {multiplayer-1.0.0 → multiplayer-1.1.0}/src/multiplayer/data/seas_oceans.csv +0 -0
- {multiplayer-1.0.0 → multiplayer-1.1.0}/src/multiplayer/exceptions.py +0 -0
- {multiplayer-1.0.0 → multiplayer-1.1.0}/src/multiplayer/language/__init__.py +0 -0
- {multiplayer-1.0.0 → multiplayer-1.1.0}/src/multiplayer/language/language.py +0 -0
- {multiplayer-1.0.0 → multiplayer-1.1.0}/src/multiplayer/py.typed +0 -0
- {multiplayer-1.0.0 → multiplayer-1.1.0}/src/multiplayer/run_log_server.py +0 -0
- {multiplayer-1.0.0 → multiplayer-1.1.0}/src/multiplayer/run_server.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: multiplayer
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.1.0
|
|
4
4
|
Summary: Library that allows you to manage multiple players, locally, on a network, or on the Internet.
|
|
5
5
|
License-Expression: MIT
|
|
6
6
|
License-File: LICENSE.md
|
|
@@ -13,6 +13,7 @@ Classifier: Programming Language :: Python :: 3.14
|
|
|
13
13
|
Classifier: Topic :: Games/Entertainment
|
|
14
14
|
Requires-Dist: colorlog
|
|
15
15
|
Requires-Dist: cryptography
|
|
16
|
+
Requires-Dist: bcrypt
|
|
16
17
|
Requires-Dist: pytest ; extra == 'dev'
|
|
17
18
|
Requires-Dist: requests ; extra == 'dev'
|
|
18
19
|
Requires-Dist: ruff ; extra == 'dev'
|
|
@@ -41,12 +42,13 @@ For a detailed technical description of all classes and functions, see the [API
|
|
|
41
42
|
* **Observer Support:** Ability to add observers who can view the game state without participating as players.
|
|
42
43
|
* **Administrator Role:** New `ServerAdmin` class to manage the server, kick players/observers, and monitor server status.
|
|
43
44
|
* **Game Grouping:** Organize several game sessions within the same server using the `GameGroup` class.
|
|
44
|
-
* **Multi-Layered Security:** Supports server passwords, admin passwords, and per-game passwords, with optional TLS v1.3 encryption. Passwords can be updated dynamically by administrators.
|
|
45
|
+
* **Multi-Layered Security:** Supports server passwords, admin passwords, and per-game passwords, with optional TLS v1.3 encryption. Passwords can be updated dynamically by administrators and are securely stored using `bcrypt` hashing.
|
|
45
46
|
* **Automatic Server Discovery:** Clients can automatically find running servers on the local network.
|
|
46
47
|
* **Extensible Name Suggestions:** Includes a utility function to suggest creative names for games and players.
|
|
47
48
|
* **Multiple Games:** The server can manage multiple game sessions simultaneously. By default, the game list for standard clients is filtered to hide finished games, but administrators can access all sessions via specialized methods like `list_all_server_games()` or `list_all_group_games()`.
|
|
48
49
|
* **Robust Error Handling:** A clear set of custom exceptions for both game logic and network issues.
|
|
49
|
-
* **Persistent Player Accounts:** Ability for players to create an account with a password for repeated use across the server.
|
|
50
|
+
* **Persistent Player Accounts:** Ability for players to create an account with a password for repeated use across the server. All persistent passwords (players, groups, and server) are protected with `bcrypt`.
|
|
51
|
+
* **Data Persistence:** Optional support for persisting server configuration, player accounts, and game sessions to disk (e.g., in JSON format). Hash-based password storage ensures security even if the data file is compromised.
|
|
50
52
|
|
|
51
53
|
## Installation
|
|
52
54
|
|
|
@@ -60,9 +62,9 @@ pip install multiplayer
|
|
|
60
62
|
### 2. From a Wheel file (GitHub)
|
|
61
63
|
Download the `.whl` file from the [Releases](https://github.com/devfred78/multiplayer/releases) page and run:
|
|
62
64
|
```sh
|
|
63
|
-
pip install multiplayer-1.
|
|
65
|
+
pip install multiplayer-1.1.0-py3-none-any.whl
|
|
64
66
|
```
|
|
65
|
-
*Replace `multiplayer-1.
|
|
67
|
+
*Replace `multiplayer-1.1.0-py3-none-any.whl` with the actual name of the downloaded file.*
|
|
66
68
|
|
|
67
69
|
## Usage
|
|
68
70
|
|
|
@@ -103,7 +105,7 @@ game2 = Game("Match 2")
|
|
|
103
105
|
group.add_game(game1)
|
|
104
106
|
group.add_game(game2)
|
|
105
107
|
|
|
106
|
-
print(f"Game 1 ID: {game1.
|
|
108
|
+
print(f"Game 1 ID: {game1.game_id}")
|
|
107
109
|
# > Game 1 ID: 550e8400-e29b-41d4-a716-446655440000
|
|
108
110
|
|
|
109
111
|
print(f"Group '{group.name}' has {len(group.games)} games.")
|
|
@@ -19,12 +19,13 @@ For a detailed technical description of all classes and functions, see the [API
|
|
|
19
19
|
* **Observer Support:** Ability to add observers who can view the game state without participating as players.
|
|
20
20
|
* **Administrator Role:** New `ServerAdmin` class to manage the server, kick players/observers, and monitor server status.
|
|
21
21
|
* **Game Grouping:** Organize several game sessions within the same server using the `GameGroup` class.
|
|
22
|
-
* **Multi-Layered Security:** Supports server passwords, admin passwords, and per-game passwords, with optional TLS v1.3 encryption. Passwords can be updated dynamically by administrators.
|
|
22
|
+
* **Multi-Layered Security:** Supports server passwords, admin passwords, and per-game passwords, with optional TLS v1.3 encryption. Passwords can be updated dynamically by administrators and are securely stored using `bcrypt` hashing.
|
|
23
23
|
* **Automatic Server Discovery:** Clients can automatically find running servers on the local network.
|
|
24
24
|
* **Extensible Name Suggestions:** Includes a utility function to suggest creative names for games and players.
|
|
25
25
|
* **Multiple Games:** The server can manage multiple game sessions simultaneously. By default, the game list for standard clients is filtered to hide finished games, but administrators can access all sessions via specialized methods like `list_all_server_games()` or `list_all_group_games()`.
|
|
26
26
|
* **Robust Error Handling:** A clear set of custom exceptions for both game logic and network issues.
|
|
27
|
-
* **Persistent Player Accounts:** Ability for players to create an account with a password for repeated use across the server.
|
|
27
|
+
* **Persistent Player Accounts:** Ability for players to create an account with a password for repeated use across the server. All persistent passwords (players, groups, and server) are protected with `bcrypt`.
|
|
28
|
+
* **Data Persistence:** Optional support for persisting server configuration, player accounts, and game sessions to disk (e.g., in JSON format). Hash-based password storage ensures security even if the data file is compromised.
|
|
28
29
|
|
|
29
30
|
## Installation
|
|
30
31
|
|
|
@@ -38,9 +39,9 @@ pip install multiplayer
|
|
|
38
39
|
### 2. From a Wheel file (GitHub)
|
|
39
40
|
Download the `.whl` file from the [Releases](https://github.com/devfred78/multiplayer/releases) page and run:
|
|
40
41
|
```sh
|
|
41
|
-
pip install multiplayer-1.
|
|
42
|
+
pip install multiplayer-1.1.0-py3-none-any.whl
|
|
42
43
|
```
|
|
43
|
-
*Replace `multiplayer-1.
|
|
44
|
+
*Replace `multiplayer-1.1.0-py3-none-any.whl` with the actual name of the downloaded file.*
|
|
44
45
|
|
|
45
46
|
## Usage
|
|
46
47
|
|
|
@@ -81,7 +82,7 @@ game2 = Game("Match 2")
|
|
|
81
82
|
group.add_game(game1)
|
|
82
83
|
group.add_game(game2)
|
|
83
84
|
|
|
84
|
-
print(f"Game 1 ID: {game1.
|
|
85
|
+
print(f"Game 1 ID: {game1.game_id}")
|
|
85
86
|
# > Game 1 ID: 550e8400-e29b-41d4-a716-446655440000
|
|
86
87
|
|
|
87
88
|
print(f"Group '{group.name}' has {len(group.games)} games.")
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "multiplayer"
|
|
3
|
-
version = "1.
|
|
3
|
+
version = "1.1.0"
|
|
4
4
|
description = "Library that allows you to manage multiple players, locally, on a network, or on the Internet."
|
|
5
5
|
license = "MIT"
|
|
6
6
|
license-files = ["LICENSE.md"]
|
|
@@ -19,6 +19,7 @@ classifiers = [
|
|
|
19
19
|
dependencies = [
|
|
20
20
|
"colorlog",
|
|
21
21
|
"cryptography",
|
|
22
|
+
"bcrypt",
|
|
22
23
|
]
|
|
23
24
|
|
|
24
25
|
[project.scripts]
|
|
@@ -40,3 +41,8 @@ build-backend = "uv_build"
|
|
|
40
41
|
module-name = "multiplayer"
|
|
41
42
|
module-root = "src"
|
|
42
43
|
package-data = { "multiplayer" = ["data/*.csv"] }
|
|
44
|
+
|
|
45
|
+
[dependency-groups]
|
|
46
|
+
dev = [
|
|
47
|
+
"requests>=2.34.1",
|
|
48
|
+
]
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"""
|
|
2
2
|
This package provides classes for managing a multiplayer game, both locally and over a network.
|
|
3
3
|
"""
|
|
4
|
-
__version__ = "1.
|
|
4
|
+
__version__ = "1.1.0"
|
|
5
5
|
from .game import Game, Player, Observer, GameState, GameGroup
|
|
6
6
|
from .server import GameServer
|
|
7
7
|
from .client import GameClient, RemoteGame, ServerAdmin, GroupAdmin
|
|
@@ -197,7 +197,7 @@ class GameClient:
|
|
|
197
197
|
if group_id:
|
|
198
198
|
params['group_id'] = group_id
|
|
199
199
|
data = self._send_command('create_game', params)
|
|
200
|
-
remote_game = RemoteGame(data['game_id'], self.host, self.port, self.password, self.use_tls, self.auth_user, self.auth_password)
|
|
200
|
+
remote_game = RemoteGame(data['game_id'], self.host, self.port, game_options.get('password', self.password), self.use_tls, self.auth_user, self.auth_password)
|
|
201
201
|
|
|
202
202
|
# Propagate logging configuration if any
|
|
203
203
|
for h in self._logger.handlers:
|
|
@@ -218,10 +218,14 @@ class GameClient:
|
|
|
218
218
|
pass
|
|
219
219
|
return games_data
|
|
220
220
|
|
|
221
|
+
def list_users(self):
|
|
222
|
+
"""Retrieves a list of names of currently connected users."""
|
|
223
|
+
return self._send_command('list_users')
|
|
224
|
+
|
|
221
225
|
def create_group(self, name, admin_password=None, **attributes):
|
|
222
226
|
"""Requests the server to create a new game group and returns a proxy to it."""
|
|
223
227
|
data = self._send_command('create_group', {'name': name, 'admin_password': admin_password, 'attributes': attributes})
|
|
224
|
-
remote_group = RemoteGroup(data['group_id'], self.host, self.port, self.password, self.use_tls, self.auth_user, self.auth_password)
|
|
228
|
+
remote_group = RemoteGroup(data['group_id'], self.host, self.port, admin_password or self.password, self.use_tls, self.auth_user, self.auth_password)
|
|
225
229
|
|
|
226
230
|
# Propagate logging configuration if any
|
|
227
231
|
for h in self._logger.handlers:
|
|
@@ -370,14 +374,7 @@ class ServerAdmin(GameClient):
|
|
|
370
374
|
|
|
371
375
|
def list_all_server_games(self):
|
|
372
376
|
"""Retrieves a dictionary of all games (even those with GameState.FINISHED) organized by ID."""
|
|
373
|
-
|
|
374
|
-
for gid in games_data:
|
|
375
|
-
if 'state' in games_data[gid]:
|
|
376
|
-
try:
|
|
377
|
-
games_data[gid]['state'] = GameState(games_data[gid]['state'])
|
|
378
|
-
except ValueError:
|
|
379
|
-
pass
|
|
380
|
-
return games_data
|
|
377
|
+
return self._send_command('list_games', {'include_finished': True})
|
|
381
378
|
|
|
382
379
|
def kick_player(self, game_id, player_id):
|
|
383
380
|
"""Kicks a player from a specific game."""
|
|
@@ -436,6 +433,12 @@ class ServerAdmin(GameClient):
|
|
|
436
433
|
'hidden': hidden
|
|
437
434
|
})
|
|
438
435
|
|
|
436
|
+
def set_server_name(self, name):
|
|
437
|
+
"""Sets a new name for the server."""
|
|
438
|
+
return self._send_command('set_server_name', {
|
|
439
|
+
'name': name
|
|
440
|
+
})
|
|
441
|
+
|
|
439
442
|
def update_persistent_player(self, name, role=None, managed_groups=None, password=None, **attributes):
|
|
440
443
|
"""
|
|
441
444
|
Updates a persistent player's information.
|
|
@@ -604,7 +607,12 @@ class RemoteGame:
|
|
|
604
607
|
'game_password': password,
|
|
605
608
|
}
|
|
606
609
|
if isinstance(player, PersistentPlayer):
|
|
607
|
-
|
|
610
|
+
# The server expects the raw password to verify against its hash
|
|
611
|
+
# PersistentPlayer.password is now a hash locally too if initialized with a string
|
|
612
|
+
# But the client test might be using the hash if it's not careful.
|
|
613
|
+
# However, if we want to support this, we need a way to get the raw password or
|
|
614
|
+
# ensure the client doesn't hash it.
|
|
615
|
+
params['persistent_player_password'] = getattr(player, '_raw_password', player.password)
|
|
608
616
|
|
|
609
617
|
self._send_command('add_player', params)
|
|
610
618
|
|
|
@@ -635,7 +643,7 @@ class RemoteGame:
|
|
|
635
643
|
'observer_password': password,
|
|
636
644
|
}
|
|
637
645
|
if isinstance(observer, PersistentPlayer):
|
|
638
|
-
params['persistent_player_password'] = observer.password
|
|
646
|
+
params['persistent_player_password'] = getattr(observer, '_raw_password', observer.password)
|
|
639
647
|
|
|
640
648
|
self._send_command('add_observer', params)
|
|
641
649
|
|