Skip to content
Snippets Groups Projects
Commit a91b6bc2 authored by ZettaScript's avatar ZettaScript
Browse files

Add: new turn, exchanges

parent 21b7b830
No related branches found
No related tags found
No related merge requests found
Pipeline #3605 failed
Ce programme est une implémentation du jeu Ğeconomicus pour jouer par Internet en client-serveur.
Ce programme est une implémentation du jeu Ğeconomicus pour jouer par Internet en client-serveur (et éventuellement un peu en P2P).
Ceci est la partie serveur, en python3.
Un logiciel client sera implémenté aussi, probablement en JavaScript pour être plus facile d'utilisation (pas d'installation pour le joueur).
Le serveur permettra d'héberger plusieurs parties, afin d'épargner aussi aux animateurs l'installation. Les parties pourront être crées à distance, depuis le client.
(Si vous voulez m'aider pour la réalisation du client, ça serait avec grand plaisir !)
N'hésitez pas à me contacter si vous avez des idées ou des critiques concernant le principe ou ma manière d'implémenter, de coder... Il faut adapter les règles du jeu au support informatique, ce qui nécessite de faire des choix (par exemple, j'ai décidé de remplacer le système de billets par une valeur numérique, mais je garde le roulement des valeurs).
......@@ -62,7 +63,8 @@ Les données JSON comprennent un champ "error".
54: Game error: that player does not exist
55: Game error: wrong player password
56: Game error: miss player password
57: Game error: miss exchange data
57: Game error: miss exchanges data
58: Game error: invalid exchange data
128: Server error: source not available
Exemple de paquet valide :
......@@ -71,9 +73,25 @@ Exemple de paquet valide :
#### Échanges
**(Non implémenté)** Un échange de valeurs et/ou d'argent entre joueurs nécessite l'accord de tous les joueurs devant fournir de la richesse.
Un échange de valeurs et/ou d'argent entre joueurs nécessite l'accord de tous les joueurs devant fournir de la richesse.
Pour proposer un échange, les joueurs négocient entre eux (en P2P) les conditions. Quand ils sont en accord, ils envoient chacun au serveur les données de l'échange, contenant la liste des transactions. Le serveur valide les transactions dès qu'il a reçu la confirmation de tous les joueurs impliqués (devant donner quelque chose).
{ "exchanges": {
"player_uid": player_uid,
"player_psw": player_psw,
"exchanges_data": [
[
[from_player_uid, to_player_uid, amount],
...
],
...
]
}
# (int) amount: monnaie
# (iterable) amount: valeurs (liste de taille N_VALUES)
# "exchanges_data": liste des échanges.
# Un échange est une liste de transactions d'un joueur vers un autre.
## Sources
http://www.trm.creationmonetaire.info/TheorieRelativedelaMonnaie.pdf
......@@ -100,8 +118,10 @@ Copyright 2018 Pascal Engélibert
> You should have received a copy of the GNU Affero General Public License
> along with Pygeconomicus-server. If not, see <https://www.gnu.org/licenses/>.
La GNU AGPL impose de publier les sources d'une instance publiquement accessible de ce logiciel, si celles-ci sont modifiées. Pour faciliter cela, le serveur pourra générer et fournir une archive de son propre code source au client si celui-ci en fait la requête.
La GNU AGPL impose de publier les sources d'une instance publiquement accessible de ce logiciel, si celles-ci sont modifiées. Pour faciliter cela, le serveur peut générer et fournir une archive de son propre code source au client si celui-ci en fait la requête.
Si vous hébergez une instance modifiée de ce logiciel accessible publiquement, vous devez juste vérifier que tous les fichiers du logiciel soient également accessibles, donc si vous ajoutez des fichier il faut actualiser la liste `SOURCE_FILES` dans `utils.py`. Cela est aussi valable pour les bibliothèques utilisées, qui doivent être open-source. En cas de doute, vous pouvez chercher dans la FAQ de GNU ou me demander.
## Contact
Contactez-moi via le site de ZettaScript : https://zettascript.org
Contactez-moi via le site de ZettaScript : https://zettascript.org/contact.php
Page du projet : https://zettascript.org/projects/geconomicus/
......@@ -20,9 +20,6 @@
import money_free, money_debt
from utils import *
games = []
games_index = {}
class Player():
def __init__(self, name, address, psw, uid):
# Consts
......@@ -167,6 +164,7 @@ Options:\n\
loop = True
server_address = (HOST, PORT)
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sock.bind(server_address)
sock.listen(1)
logPrint("Server started at "+str(server_address), LOG_INFO)
......@@ -270,7 +268,7 @@ Options:\n\
g_name = p_data["game_name"]
if not g_name in games_index:
sendErrorToClient(client, ERROR_GAME_NONAMED, resp_lng)# Game error: there is no game with this name
sendErrorToClient(client, ERROR_GAME_GAMENOEXIST, resp_lng)# Game error: there is no game with this name
continue
game = games_index[g_name]
......@@ -332,7 +330,7 @@ Options:\n\
g_name = p_data["game_name"]
if not g_name in games_index:
sendErrorToClient(client, ERROR_GAME_NONAMED, resp_lng)# Game error: there is no game with this name
sendErrorToClient(client, ERROR_GAME_GAMENOEXIST, resp_lng)# Game error: there is no game with this name
continue
game = games_index[g_name]
......@@ -370,7 +368,7 @@ Options:\n\
g_name = p_data["game_name"]
if not g_name in games_index:
sendErrorToClient(client, ERROR_GAME_NONAMED, resp_lng)# Game error: there is no game with this name
sendErrorToClient(client, ERROR_GAME_GAMENOEXIST, resp_lng)# Game error: there is no game with this name
continue
game = games_index[g_name]
......@@ -387,6 +385,26 @@ Options:\n\
resp["rm_game_done"] = True
if "new_turn" in p_data:
logPrint("Received new turn command", LOG_TRACE)
p_resp = True
try:
g_name = getGameName(p_data, client, resp_lng)
game = getGame(g_name, client, resp_lng)
except ContinueError:
continue
g_admin_psw = ""
if "admin_psw" in p_data:
g_admin_psw = p_data["admin_psw"]
if g_admin_psw != game.admin_psw:
sendErrorToClient(client, ERROR_GAME_WRONGADMINPSW, resp_lng)# Game error: wrong admin password
continue
game.newTurn()
resp["new_turn_done"] = {"turn":game.turn}
if "new_player" in p_data:
logPrint("Received new player command", LOG_TRACE)
p_resp = True
......@@ -397,7 +415,7 @@ Options:\n\
g_name = p_data["game_name"]
if not g_name in games_index:
sendErrorToClient(client, ERROR_GAME_NONAMED, resp_lng)# Game error: there is no game with this name
sendErrorToClient(client, ERROR_GAME_GAMENOEXIST, resp_lng)# Game error: there is no game with this name
continue
game = games_index[g_name]
......@@ -431,7 +449,7 @@ Options:\n\
g_name = p_data["game_name"]
if not g_name in games_index:
sendErrorToClient(client, ERROR_GAME_NONAMED, resp_lng)# Game error: there is no game with this name
sendErrorToClient(client, ERROR_GAME_GAMENOEXIST, resp_lng)# Game error: there is no game with this name
continue
game = games_index[g_name]
......@@ -465,7 +483,7 @@ Options:\n\
player = None
if "exchange" in p_data:
if "exchanges" in p_data:
logPrint("Received exchange command", LOG_TRACE)
p_resp = True
......@@ -475,11 +493,11 @@ Options:\n\
g_name = p_data["game_name"]
if not g_name in games_index:
sendErrorToClient(client, ERROR_GAME_NONAMED, resp_lng)# Game error: there is no game with this name
sendErrorToClient(client, ERROR_GAME_GAMENOEXIST, resp_lng)# Game error: there is no game with this name
continue
game = games_index[g_name]
g = p_data["exchange"]
g = p_data["exchanges"]
g_player_uid = None
if "player_uid" in g:
......@@ -498,11 +516,32 @@ Options:\n\
sendErrorToClient(client, ERROR_GAME_MISSPLAYERPSW, resp_lng)# Game error: miss player password
continue
if not "exchange_data" in g:
sendErrorToClient(client, ERROR_GAME_MISSEXCHDATA, resp_lng)# Game error: miss exchange data
if not "exchanges_data" in g:
sendErrorToClient(client, ERROR_GAME_MISSEXCHDATA, resp_lng)# Game error: miss exchanges data
continue
try:
for exch in g["exchanges_data"]:
# Check each transaction
for exchterm in exch:
assert len(exchterm) == 3
if not exchterm[0] in game.players_index or not exchterm[1] in game.players_index:
sendErrorToClient(client, ERROR_GAME_PLAYERNOEXIST, resp_lng)# Game error: that player does not exist
raise ContinueError
if type(exchterm[2]) == int or type(exchterm[2]) == float:
assert exchterm[2] >= 0
else:
assert len(exchterm[2]) == game.C["N_VALUES"]
# TODO: add to exchanges list, confirm, apply
except ContinueError:
continue
except (TypeError, AssertionError):
sendErrorToClient(client, ERROR_GAME_INVEXCHDATA, resp_lng)# Game error: invalid exchange data
continue
# TODO: check data, add to exchanges list, confirm, apply
else:
sendErrorToClient(client, ERROR_COM_LANGUAGE, resp_lng)# Communication error: unknown language
......
import socket, time, json
from sys import argv
from utils import *
PROTOCOL_VERSION = b"geco0"
......@@ -31,14 +29,14 @@ CONSTS2 = {
"START_VALUES": 5 # number of random values for beginning
}
HOST = getargv(argv, "-h", "127.0.0.1")
HOST = getargv(sys.argv, "-h", "127.0.0.1")
try:
PORT = int(getargv(argv, "-p", "8651"))
PORT = int(getargv(sys.argv, "-p", "8651"))
except ValueError:
print("Error: port must be an integer")
exit(1)
if "--help" in argv:
if "--help" in sys.argv:
print("\n\
PyĞeconomicus test client\n\
\n\
......@@ -68,6 +66,7 @@ def sdata(data, response_intended, lng="json"):
raw = json.dumps(data).encode()
msg = PROTOCOL_VERSION+b"/"+PROTOCOL_LANG+b"/"+hex(len(raw))[2:].encode()+b"\n"+raw
sock.sendall(msg)
resp = None
if response_intended:
paquet = b""
......@@ -171,6 +170,10 @@ def removeplayer(name="Test game", player_uid=1, player_psw="", admin_psw=None):
else:
return sdata({"game_name":name, "rm_player":{"player_uid":player_uid, "player_psw":player_psw}}, True)
def exchanges(data, name="Test game", player_uid=1, player_psw=""):
"""Send exchanges"""
return sdata({"game_name":name, "exchanges":{"player_uid":player_uid, "player_psw":player_psw, "exchanges_data":data}}, True)
def getsource(outfile="serversourcecode.tar.gz"):
"""Request for source code"""
f = open(outfile, "wb")
......
......@@ -20,21 +20,22 @@
import configparser, socket, json, os, time, random, sys
ERROR_OK = 0
ERROR_COM_HEADER = 16
ERROR_COM_PROTOCOL = 17
ERROR_COM_LANGUAGE = 18
ERROR_COM_JSON = 19
ERROR_GAME_USEDGAMENAME = 48
ERROR_GAME_MISSNEWGAMENAME = 49
ERROR_GAME_MISSGAMENAME = 50
ERROR_GAME_NONAMED = 51
ERROR_GAME_MISSADMINPSW = 52
ERROR_GAME_WRONGADMINPSW = 53
ERROR_GAME_PLAYERNOEXIST = 54
ERROR_GAME_WRONGPLAYERPSW = 55
ERROR_GAME_MISSPLAYERPSW = 56
ERROR_GAME_MISSEXCHDATA = 57
ERROR_SRV_NOSOURCE = 128
ERROR_COM_HEADER = 16 # Communication error: decoding header
ERROR_COM_PROTOCOL = 17 # Communication error: unsupported protocol version
ERROR_COM_LANGUAGE = 18 # Communication error: unknown language
ERROR_COM_JSON = 19 # Communication error: decoding JSON
ERROR_GAME_USEDGAMENAME = 48 # Game error: that game name is already used
ERROR_GAME_MISSNEWGAMENAME = 49 # Game error: miss new game name
ERROR_GAME_MISSGAMENAME = 50 # Game error: miss game name
ERROR_GAME_GAMENOEXIST = 51 # Game error: there is no game with this name
ERROR_GAME_MISSADMINPSW = 52 # Game error: miss admin password
ERROR_GAME_WRONGADMINPSW = 53 # Game error: wrong admin password
ERROR_GAME_PLAYERNOEXIST = 54 # Game error: that player does not exist
ERROR_GAME_WRONGPLAYERPSW = 55 # Game error: wrong player password
ERROR_GAME_MISSPLAYERPSW = 56 # Game error: miss player password
ERROR_GAME_MISSEXCHDATA = 57 # Game error: miss exchanges data
ERROR_GAME_INVEXCHDATA = 58 # Game error: invalid exchange data
ERROR_SRV_NOSOURCE = 128 # Server error: source not available
LOG_INFO = 1
LOG_TRACE = 2
......@@ -65,6 +66,13 @@ PORT = 8651 # server port
SOURCE_FILES = ["server.py", "money_free.py", "money_debt.py", "utils.py", "README.md", "LICENSE"]
SOURCE_ARCHIVE = "sourcecode.tar.gz"
games = []
games_index = {}
class ContinueError(Exception):
def __init__(self):
pass
# Server functions
def getargv(argv, arg, default=""):
......@@ -108,6 +116,18 @@ def tarSource():
tar.close()
logPrint("Source archive created at '"+SOURCE_ARCHIVE+"'", LOG_INFO)
def getGameName(p_data, client, resp_lng):
if not "game_name" in p_data:
sendErrorToClient(client, ERROR_GAME_MISSGAMENAME, resp_lng)# Game error: miss game name
raise ContinueError
return p_data["game_name"]
def getGame(g_name, client, resp_lng):
if not g_name in games_index:
sendErrorToClient(client, ERROR_GAME_NONAMED, resp_lng)# Game error: there is no game with this name
raise ContinueError
return games_index[g_name]
# Money functions
def moneySupply(players):
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment