123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258 |
- #!/usr/bin/env python3
- import configparser
- import threading
- import socket
- import argparse
- import os
- import json
- import random
- import time
- TITLES = [
- "Python",
- "3",
- "JavaScript",
- "Hamburger",
- "Wieloryb",
- "Szkoła",
- "Uniwersytet",
- "Remiza",
- "Pomidor",
- "Radość",
- "Młot",
- "Baran",
- "Hiacynt",
- "Jedzenie w samolocie",
- "Rozpacz",
- "Ciasto",
- "Słońce",
- "Pantofel",
- "Widmo",
- "Dama",
- "Krawędź",
- "Japonki",
- "Zamek",
- "Szmalcownik",
- "Bazy danych",
- "Szarada",
- "Głaz",
- "Piwnica",
- "Pióro",
- "Rachunek prawdopodobieństwa",
- "Jabłko",
- "Granat",
- "Przystań",
- "Sumo",
- "Baba",
- "Tygrys",
- "Staw",
- "Oliwa",
- "Grzebień",
- "Kuba",
- "Piła",
- "Polka",
- "Warta",
- "Róża",
- "Kosa",
- "Narcyz",
- "Zebra",
- "Kapelusz",
- "Kule",
- "Mars",
- "Kiwi",
- "Mysz"
- ]
- class Server(threading.Thread):
- def __init__(self, host, port):
- super().__init__()
- self.config = configparser.ConfigParser()
- self.config.read("config.ini", "UTF8")
- self.connections = []
- self.host = host
- self.port = port
- self.data = {}
- self.data["title"] = "undefined"
- self.data["state"] = "wait"
- self.data["users"] = {}
- self.data["messages"] = {}
- self.data["scores"] = {}
- self.data["total_scores"] = {}
- self.votes = {}
- def reset(self):
- time.sleep(10)
- print("New round")
- self.votes = {}
- self.data["scores"] = {}
- self.data["messages"] = {}
- self.data["title"] = random.choice(TITLES)
- self.data["state"] = "wait"
- if self.player_count() >= 3:
- self.data["state"] = "game"
- else:
- self.data["state"] = "wait"
- dump = json.dumps(self.data, ensure_ascii=False)
- self.broadcast_all(dump)
- def run(self):
- sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
- sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
- sock.bind((self.host, self.port))
- sock.listen(1)
- print('Listening at', sock.getsockname())
- idx = 0
- try:
- while True:
- sc, sockname = sock.accept()
- print('{} has joined'.format(idx))
- server_socket = ServerSocket(sc, sockname, self, idx)
- idx += 1
- server_socket.start()
- self.connections.append(server_socket)
- except KeyboardInterrupt:
- self.quit()
- def quit(self):
- with open("config.ini", 'w') as config_file:
- self.config.write(config_file)
- os._exit(0)
- def broadcast(self, message, source):
- for connection in self.connections:
- # Send to all connected clients except the source client
- if connection.sockname != source:
- connection.send(message)
- def broadcast_all(self, message):
- for connection in self.connections:
- connection.send(message)
- def remove_connection(self, connection):
- self.connections.remove(connection)
- def player_count(self):
- return len(self.connections)
- class ServerSocket(threading.Thread):
- def __init__(self, sc, sockname, server, id):
- super().__init__()
- self.sc = sc
- self.id = id
- self.sockname = sockname
- self.server = server
- self.username = ""
- def call_quit(self):
- print('{} has left'.format(self.id))
- self.server.data["users"].pop(self.id, None)
- self.server.data["messages"].pop(self.id, None)
- self.server.data["scores"].pop(self.id, None)
- self.server.data["total_scores"].pop(self.id, None)
- self.server.votes.pop(self.id, None)
- self.sc.close()
- server.remove_connection(self)
- if self.server.player_count() == 0:
- self.server.data["state"] = "wait"
- def run(self):
- """
- Receives data from the connected client and broadcasts the message to all other clients.
- If the client has left the connection, closes the connected socket and removes itself
- from the list of ServerSocket threads in the parent Server thread.
- """
- try:
- while True:
- message = self.sc.recv(1024).decode('utf8')
- if not message:
- self.call_quit()
- return
- elif message:
- if self.username == "":
- self.username = message
- self.server.data["users"][self.id] = self.username
- if len(self.server.data["users"]) >= 3 and self.server.data["state"] == "wait":
- self.server.data["title"] = random.choice(TITLES)
- self.server.data["state"] = "game"
- dump = json.dumps(self.server.data, ensure_ascii=False)
- self.server.broadcast(dump, self.sockname)
- data_copy = self.server.data.copy()
- data_copy["playerid"] = self.id
- dump = json.dumps(data_copy, ensure_ascii=False)
- self.send(dump)
- elif self.server.data["state"] == "game":
- self.server.data["messages"][self.id] = message
- if len(self.server.data["messages"]) == self.server.player_count():
- self.server.data["state"] = "vote"
- dump = json.dumps(self.server.data, ensure_ascii=False)
- self.server.broadcast_all(dump)
- elif self.server.data["state"] == "vote":
- self.server.votes[self.id] = int(message)
- if len(self.server.votes) == self.server.player_count():
- for key in self.server.votes:
- player = self.server.votes[key]
- if player not in self.server.data["scores"]:
- self.server.data["scores"][player] = 0
- self.server.data["scores"][player] += 1
- for player in self.server.data["scores"]:
- name = self.server.data["users"][player]
- if player not in self.server.data["total_scores"]:
- if name in self.server.config["DEFAULT"]:
- self.server.data["total_scores"][player] = int(
- self.server.config["DEFAULT"][name])
- else:
- self.server.data["total_scores"][player] = 0
- self.server.config["DEFAULT"][name] = "0"
- self.server.data["total_scores"][player] += self.server.data["scores"][player]
- self.server.config["DEFAULT"][name] = str(int(
- int(self.server.config["DEFAULT"][name]) + int(self.server.data["scores"][player])))
- self.server.data["state"] = "display"
- dump = json.dumps(self.server.data, ensure_ascii=False)
- self.server.broadcast_all(dump)
- with open("config.ini", 'w') as config_file:
- self.server.config.write(config_file)
- self.server.reset()
- # print(self.server.data)
- except KeyboardInterrupt or EOFError:
- print("Caught keyboard interrupt, exiting")
- self.call_quit()
- os._exit(0)
- except ConnectionResetError:
- self.call_quit()
- def send(self, message):
- self.sc.sendall(message.encode('utf8'))
- def quit(server):
- for connection in server.connections:
- connection.sc.close()
- os._exit(0)
- def exit(server):
- try:
- while True:
- input('')
- except KeyboardInterrupt:
- quit(server)
- except EOFError:
- quit(server)
- if __name__ == '__main__':
- parser = argparse.ArgumentParser(description='Aphorism Server')
- parser.add_argument('host', help='Interface the server listens at')
- args = parser.parse_args()
- server = Server(args.host, 7312)
- server.start()
- exit = threading.Thread(target=exit, args=(server,))
- exit.start()
|