import json import signal import socket import sys import threading from Classes.Game.Player import Player from Classes.System.GameManager import GameManager from Classes.System.Network.EventHandler import TCPEventHandler from Classes.System.World import World class NetworkManager: class TCP: __Addr:str __Port:str __BufferSize:int = 1024 __tcpSocket:socket __eventHandler: dict __users: dict __TCPClientThread:threading.Thread __gameManager:GameManager def __init__(self, Addr:str, Port:str, gameManager:GameManager): gameManager.getLogger().info("starting up network manager") self.running = True self.__Addr = Addr self.__Port = int(Port) self.__gameManager = gameManager self.__eventHandler = {} gameManager.getLogger().info("starting up tcp server") self.__tcpSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.__tcpSocket.bind((self.__Addr, self.__Port)) self.__tcpSocket.listen() gameManager.getLogger().info("starting up thread for client socket accepting") self.__TCPClientThread = threading.Thread(target=self.accept_connections) self.__TCPClientThread.daemon = True self.__TCPClientThread.start() def accept_connections(self): while self.running: try: client_tcp_socket, client_address = self.__tcpSocket.accept() self.__gameManager.getLogger().info(f"Connected with {client_address}") self.__gameManager.getPlayers()[client_address] = client_tcp_socket self.__eventHandler[client_address] = TCPEventHandler(client_tcp_socket) client_handler_thread = threading.Thread( target=self.receive, args=(client_tcp_socket, client_address) ) self.__gameManager.getLogger().info(f"starting client handler thread for client at address {client_address}") client_handler_thread.daemon = True client_handler_thread.start() except Exception as e: self.__gameManager.getLogger().error(f"tcp socket failed to accept connection due to error: {e}") pass client_handler_thread.join() def receive(self, client_socket, client_address): while self.running: try: data = client_socket.recv(self.__BufferSize) if not data: self.__gameManager.getLogger().info(f"Connection with {client_address} closed.") break try: message = data.decode() messageJson = json.loads(message) self.__gameManager.getLogger().info(f"decoded message {messageJson}") user = messageJson.get("user") self.__gameManager.getLogger().info(f"user in message {user}") except Exception as ex: self.__gameManager.getLogger().info(f"decoding incoming packet failed due to exception: {ex}") # creates a user and counts how many currently are connected to the server # if enough users for a round are connected the server has to start the game if user not in self.__gameManager.getPlayers(): if messageJson["event"] == "login": self.__gameManager.getLogger().info("user logging in") self.__gameManager.getLogger().info("task passed off to gameManager") user = self.__gameManager.addPlayers(Player(messageJson["username"], messageJson["deck"]), client_socket, client_address) self.__gameManager.getLogger().info(f"connected users {len(self.__gameManager.getPlayers())}") self.__gameManager.getLogger().info(f"confirming login for user") self.send({ "event": "loginresponse", "status": "success", "id": user[client_address]["player"].getID(), }, client_address) self.__eventHandler[client_address].handleTCPEvents(messageJson, self.__gameManager, client_address) self.__gameManager.getLogger().info(f"Received message from {client_address}: {message}") except socket.error as e: if e.errno == 10054: self.__gameManager.getLogger().error(f"Connection with {client_address} forcibly closed by remote host.") players = self.__gameManager.getPlayers() self.__gameManager.removePlayers(client_address) self.__gameManager.getLogger().info(f"new player length {len(players)}") break self.__gameManager.getLogger().error(f"Socket error receiving data from {client_address}: {e}") except json.JSONDecodeError as e: self.__gameManager.getLogger().error(f"JSON decoding error receiving data from {client_address}: {e}") # except Exception as e: # self.__gameManager.getLogger().error(f"UknownError receiving data from {client_address} due to error: {e}") def broadcast(self, payload:dict): for user in self.__gameManager.getPlayers().values(): user["socket"].send(json.dumps(payload).encode()) def send(self, payload: dict, user: str): players = self.__gameManager.getPlayers() if user in players and "socket" in players[user]: players[user]["socket"].send(json.dumps(payload).encode()) else: self.__gameManager.getLogger().error(f"user '{user}' or socket was not found 'socket' failed to send data.") def stop(self): self.__TCPClientThread.join() # Wait for the thread to finish before exiting tcp: TCP # udp: UDP def __init__(self, Addr:str, TCPPort:str, UDPPort:str, gameManager:GameManager): self.tcp = self.TCP(Addr, TCPPort, gameManager) signal.signal(signal.SIGINT, self.handle_interrupt) # Register the signal handler def handle_interrupt(self, signum, frame): self.__gameManager.getLogger().info("Received keyboard interrupt. Stopping the server.") self.tcp().stop() sys.exit(0)