From 4ff9591f9260cb0c4c6b3d52687e279a1904b246 Mon Sep 17 00:00:00 2001 From: steev Date: Wed, 28 Feb 2024 21:09:03 +0100 Subject: [PATCH] added tcg game from previous project --- Assets/Arenas/default.png | Bin 0 -> 10988 bytes Assets/Cards/Arena/field.png | Bin 0 -> 612 bytes .../MonsterCards/testmonstercard/card.json | 22 +++ .../MonsterCards/testmonstercard/card.png | Bin 0 -> 9570 bytes .../SpeelCards/testspellcard/artworkjson.png | Bin 0 -> 2389 bytes .../testspellcard/testspellcard.json | 6 + .../TrapCards/testtrapcard/artworkjson.png | Bin 0 -> 2389 bytes .../TrapCards/testtrapcard/testtrapcard.json | 6 + Classes/Game/Player.py | 57 +++++++ Classes/System/GameManager.py | 153 ++++++++++++++++++ Classes/System/Logger.py | 18 +++ Classes/System/Network/EventHandler.py | 32 ++++ Classes/System/Network/NetworkManger.py | 137 ++++++++++++++++ Classes/System/PlayerManager.py | 23 +++ Classes/System/QueueManager.py | 34 ++++ Classes/System/Server.py | 40 +++++ Classes/System/Utils/Path.py | 6 + Classes/System/Utils/StringUtils.py | 11 ++ Classes/System/World.py | 20 +++ index.py | 31 ++++ server logic notes.md | 42 +++++ 21 files changed, 638 insertions(+) create mode 100644 Assets/Arenas/default.png create mode 100644 Assets/Cards/Arena/field.png create mode 100644 Assets/Cards/MonsterCards/testmonstercard/card.json create mode 100644 Assets/Cards/MonsterCards/testmonstercard/card.png create mode 100644 Assets/Cards/SpeelCards/testspellcard/artworkjson.png create mode 100644 Assets/Cards/SpeelCards/testspellcard/testspellcard.json create mode 100644 Assets/Cards/TrapCards/testtrapcard/artworkjson.png create mode 100644 Assets/Cards/TrapCards/testtrapcard/testtrapcard.json create mode 100644 Classes/Game/Player.py create mode 100644 Classes/System/GameManager.py create mode 100644 Classes/System/Logger.py create mode 100644 Classes/System/Network/EventHandler.py create mode 100644 Classes/System/Network/NetworkManger.py create mode 100644 Classes/System/PlayerManager.py create mode 100644 Classes/System/QueueManager.py create mode 100644 Classes/System/Server.py create mode 100644 Classes/System/Utils/Path.py create mode 100644 Classes/System/Utils/StringUtils.py create mode 100644 Classes/System/World.py create mode 100644 index.py create mode 100644 server logic notes.md diff --git a/Assets/Arenas/default.png b/Assets/Arenas/default.png new file mode 100644 index 0000000000000000000000000000000000000000..ba5da22ccc75f0be2ff44b31e702347d8e45ddc5 GIT binary patch literal 10988 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 literal 0 HcmV?d00001 diff --git a/Assets/Cards/MonsterCards/testmonstercard/card.json b/Assets/Cards/MonsterCards/testmonstercard/card.json new file mode 100644 index 0000000..0df2ece --- /dev/null +++ b/Assets/Cards/MonsterCards/testmonstercard/card.json @@ -0,0 +1,22 @@ +{ + "id": 1, + "name": "Test Monster", + "image": "Assets/Cards/testmonstercard/cards.png", + "description": "can attack other monsters", + "costs": 30, + "defense": 40, + "attacks":[ + { + "id": 1, + "name":"test attack", + "description": "can attack another Monster", + "damage": 80 + }, + { + "id": 2, + "name":"test attack", + "description": "can attack another Monster", + "damage": 80 + } + ] +} diff --git a/Assets/Cards/MonsterCards/testmonstercard/card.png b/Assets/Cards/MonsterCards/testmonstercard/card.png new file mode 100644 index 0000000000000000000000000000000000000000..320d60d34af39518b06c3adcf397345b883d202c GIT binary patch literal 9570 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 literal 0 HcmV?d00001 diff --git a/Assets/Cards/SpeelCards/testspellcard/artworkjson.png b/Assets/Cards/SpeelCards/testspellcard/artworkjson.png new file mode 100644 index 0000000000000000000000000000000000000000..9955a0cbf1966ebc0956bef3cc107754ed7011da GIT binary patch literal 2389 zcmeAS@N?(olHy`uVBq!ia0y~yV2WU1VEn?t1{8T15PXG!fm6oQ#WAE}&fA-TE1d#G zTrbu>Wi@D!FEF zConP`VG(0+@EK){2Eu4c7|jI(oeSQ)FW6NT{NVMq>&1EhyA>OJBl@P7nyP-teg8c9 z-SfS&4Za^-IT!?lfhD0~BkL$*G!RBp!e}nQ%mqtl{$%KP6?(pDVM0Byxy0b<>gTe~ HDWM4fLIJZ> literal 0 HcmV?d00001 diff --git a/Assets/Cards/SpeelCards/testspellcard/testspellcard.json b/Assets/Cards/SpeelCards/testspellcard/testspellcard.json new file mode 100644 index 0000000..51d4083 --- /dev/null +++ b/Assets/Cards/SpeelCards/testspellcard/testspellcard.json @@ -0,0 +1,6 @@ +{ + "name": "testspell", + "image":"Assets/Cards/testspelltcard/artwork.png", + "costs": 30, + "description":"this is a test spell card" +} \ No newline at end of file diff --git a/Assets/Cards/TrapCards/testtrapcard/artworkjson.png b/Assets/Cards/TrapCards/testtrapcard/artworkjson.png new file mode 100644 index 0000000000000000000000000000000000000000..9955a0cbf1966ebc0956bef3cc107754ed7011da GIT binary patch literal 2389 zcmeAS@N?(olHy`uVBq!ia0y~yV2WU1VEn?t1{8T15PXG!fm6oQ#WAE}&fA-TE1d#G zTrbu>Wi@D!FEF zConP`VG(0+@EK){2Eu4c7|jI(oeSQ)FW6NT{NVMq>&1EhyA>OJBl@P7nyP-teg8c9 z-SfS&4Za^-IT!?lfhD0~BkL$*G!RBp!e}nQ%mqtl{$%KP6?(pDVM0Byxy0b<>gTe~ HDWM4fLIJZ> literal 0 HcmV?d00001 diff --git a/Assets/Cards/TrapCards/testtrapcard/testtrapcard.json b/Assets/Cards/TrapCards/testtrapcard/testtrapcard.json new file mode 100644 index 0000000..6d1cd5a --- /dev/null +++ b/Assets/Cards/TrapCards/testtrapcard/testtrapcard.json @@ -0,0 +1,6 @@ +{ + "name": "testtrap", + "image":"Assets/Cards/testtrapcard/artwork.png", + "costs": 30, + "description":"this is a test tryp card" +} \ No newline at end of file diff --git a/Classes/Game/Player.py b/Classes/Game/Player.py new file mode 100644 index 0000000..2d337be --- /dev/null +++ b/Classes/Game/Player.py @@ -0,0 +1,57 @@ +import random + + +class Player: + __id:int + __hp:int + __mana:int + __name:str + __handCards:list + __deck:list + + def __init__(self, name:str, deck:list, hp:int=1000, mana:int=0): + self.__hp = hp + self.__mana = mana + self.__name = name + self.__handCards = [] + self.__deck = deck + self.__id = random.randint(3, 99999) + + def shuffleDeck(self): + self.__deck = random.shuffle(self.__deck) + + def getDeck(self) -> list: + return self.__deck + + def getName(self) -> str: + return self.__name + + def getHP(self) -> int: + return self.__hp + + def adjustHP(self, hp:int) -> int: + self.__hp = self.__hp + hp + + def getID(self) -> int: + return self.__id + + def getHand(self) -> list: + return self.__handCards + + def getMana(self) -> int: + return self.__mana + + def addMana(self, amount) -> int: + self.__mana + amount + return self.__mana + + def AddToHand(self, card) -> list: + self.__handCards.append(card) + return self.__handCards + + def setHand(self, hand:list): + self.__handCards = hand + + def removeFromHand(self, pos:int) -> list: + self.__handCards.remove(pos) + return self.__handCards \ No newline at end of file diff --git a/Classes/System/GameManager.py b/Classes/System/GameManager.py new file mode 100644 index 0000000..3930a80 --- /dev/null +++ b/Classes/System/GameManager.py @@ -0,0 +1,153 @@ +import json +import socket +import time +from Classes.Game.Player import Player + + +class GameManager: + __players:dict + __playingPlayer:Player + __state:str + __round:str + __cards:list + + def __init__(self, logger): + self.__players = {} + self.__playingPlayer = None + self.__state = "waiting" + self.__round = "none" + self.logger = logger + self.__cards = [] + pass + + def getLogger(self): + return self.logger + + # card management + def spawnCard(self, card, owner, x, y): + # self.logger.info("spawning card") + + self.__cards.append(card) + + payload = { + "event":"PlacedCard", + "owner": owner, + "card": card, + "x": x, + "y": y, + } + + for userAddr in self.__players.keys(): + try: + self.logger.info(f"send to client {self.__players[userAddr]['player'].getID() != owner}") + if self.__players[userAddr]["player"].getID() != owner: + self.__players[userAddr]["socket"].send(json.dumps(payload).encode()) + except: + pass + + # game round management + # this section manages the flow of rounds this should inherit itself + # ============================================================================= + + # this function iterates all + def progressRound(self): + # phases + # - playerPrep => playing player switches, gets a mana point and gets verified + if self.__playingPlayer != None: + for player in self.__players: + if self.__playingPlayer != player: + self.__playingPlayer = player + else: + self.__playingPlayer = next(iter(self.__players)) + # - playerDraw => player draws a card + # - playerPlay => player can place cards and active effects + # - playerEnd => player ends his turn and the code reiterates with the remaining player + pass + + # game state management + # this section mostly only used by the networking and event handling classes + # other parts should never need to interface with this unless really required + # ============================================================================= + def startGame(self): + self.__state = "running" + + players = list(self.__players.values()) + + print("game starts") + self.logger.info("game manager is starting the game") + for userAddr, player_data in self.__players.items(): + try: + user = self.__players[userAddr]["player"] + user.addMana(1000) + user.adjustHP(1000) + user.shuffleDeck() + cards = self.__players[userAddr]["deck"] + user.setHand(cards[:5]) + + # iterates until the enemy player is not anymore equal to current player + enemy = next(player_data["player"] for player_data in players if player_data["player"] != user) + + payload = { + "event": "startgame", + "player": { + "mana": user.getMana(), + "hp": user.getHP(), + "hand": user.getHand() + }, + "enemy": { + "id": enemy.getID(), + "name": enemy.getName(), + "hp": enemy.getHP(), + }, + } + + print(f"user {player_data["socket"]}") + player_data["socket"].send(json.dumps(payload).encode()) + except Exception as e: + self.logger.error(f"failed to start game due to error: {e}") + pass + # handles notifying all players that the game starts + pass + + def stopGame(self): + # handles notifying all players that the game stops + # handles stoping the game itself and notifies server to stop itself + pass + + # player management + # the network manager will create a player instance + # ============================================================= + + # gets all player known to the game manager and returns them + def getPlayers(self) -> dict: + return self.__players + + # creates a player and handles counting all players and if conditions met starting the game + # returns the new dict in which the new player now is added + def addPlayers(self, player:Player, socket:socket, clientAddr, deck) -> dict: + + self.logger.info(f"creating user with id: {player.getID}") + self.__players[clientAddr] = { + "player": player, + "deck": deck, + "socket":socket + } + self.logger.info(f"new length of user dictionary: {len(self.__players)}") + + payload = { + "event":"loginresponse", + "status": "success", + "id": player.getID(), + "name": player.getName() + } + + socket.send(json.dumps(payload).encode()) + + + # counts participating players and starts the game if enough have joined + if len(self.__players) >= 2: + time.sleep(1) + self.logger.info("2 players have join game starts") + self.startGame() + + return self.__players \ No newline at end of file diff --git a/Classes/System/Logger.py b/Classes/System/Logger.py new file mode 100644 index 0000000..b548160 --- /dev/null +++ b/Classes/System/Logger.py @@ -0,0 +1,18 @@ +import logging + + +class Logger: + def __init__(self, filename): + logging.basicConfig(filename=filename, + filemode='a', + format='%(asctime)s,%(msecs)d %(name)s %(levelname)s %(message)s', + datefmt='%H:%M:%S', + level=logging.DEBUG) + + def info(self, message): + print(message) + logging.info(message) + + def error(self, message): + print(message) + logging.error(message) \ No newline at end of file diff --git a/Classes/System/Network/EventHandler.py b/Classes/System/Network/EventHandler.py new file mode 100644 index 0000000..6cca201 --- /dev/null +++ b/Classes/System/Network/EventHandler.py @@ -0,0 +1,32 @@ +import socket +from Classes.Game.Player import Player +from Classes.System.GameManager import GameManager + +from Classes.System.World import World + + +class TCPEventHandler: + __tcpSocket:socket + + def __init__(self, socket:socket): + self.__tcpSocket = socket + + # handles passing of event data to the right functions + def handleTCPEvents(self, event, gameManager:GameManager, address): + gameManager.getLogger().info(f"incommingevent {event}") + if event["event"] == "placecard": + gameManager.spawnCard(event["card"], event["user"], event["x"], event["y"]) + pass + elif event["event"] == "MoveCard": + pass + elif event["event"] == "RemoveCard": + pass + elif event["event"] == "AttackCard": + pass + elif event["event"] == "AttackPlayer": + pass + elif event["event"] == "ActivateEffectCard": + pass + elif event["event"] == "ActivateMonsterCard": + pass + pass \ No newline at end of file diff --git a/Classes/System/Network/NetworkManger.py b/Classes/System/Network/NetworkManger.py new file mode 100644 index 0000000..60a6a3f --- /dev/null +++ b/Classes/System/Network/NetworkManger.py @@ -0,0 +1,137 @@ +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, messageJson["deck"]) + self.__gameManager.getLogger().info(f"connected users {len(self.__gameManager.getPlayers())}") + + self.__gameManager.getLogger().info(f"confirming login for user") + + 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.") + self.__gameManager.getPlayers()[client_address] = None + 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) \ No newline at end of file diff --git a/Classes/System/PlayerManager.py b/Classes/System/PlayerManager.py new file mode 100644 index 0000000..e149622 --- /dev/null +++ b/Classes/System/PlayerManager.py @@ -0,0 +1,23 @@ +import json +class Player: + + def createUser(self, user:json): + self.__users.append(user) + + def createUser(self, user:json): + if self.getUser(user["username"]) == None: + self.__users.append(Player(user["username"])) + + def removeUser(self, user:int): + self.__users.remove(user) + + def removeUser(self, user:str): + self.__users.remove(user) + + def getUsers(self) -> list: + return self.__users + + def getUser(self, user:int): + for user in self.__users: + if int(user["id"]) == user: + return user \ No newline at end of file diff --git a/Classes/System/QueueManager.py b/Classes/System/QueueManager.py new file mode 100644 index 0000000..be82662 --- /dev/null +++ b/Classes/System/QueueManager.py @@ -0,0 +1,34 @@ + + +from Classes.Game.Player import Player + + +class QueueManager: + __queue:list + + def __init__(self): + self.__queue = [] + + def getQueue(self) -> list: + return self.__queue + + def addToQueue(self, user) -> list: + if self.isInQueue(user["id"]): + self.__queue.append(user) + return self.__queue + + def removeFromQueue(self, player:Player) -> list: + self.__queue.remove(player) + return self.__queue + + def isInQueue(self, user:int) -> bool: + for user in self.__queue: + if int(user["id"]) == user: + return True + return False + + def isInQueue(self, user:str) -> bool: + for user in self.__queue: + if user["username"] == user: + return True + return False \ No newline at end of file diff --git a/Classes/System/Server.py b/Classes/System/Server.py new file mode 100644 index 0000000..c3485db --- /dev/null +++ b/Classes/System/Server.py @@ -0,0 +1,40 @@ +import json +import socket +import threading +from Classes.System.GameManager import GameManager + +from Classes.System.Network.NetworkManger import NetworkManager +from Classes.System.PlayerManager import Player +from Classes.System.World import World +from Classes.System.Logger import Logger + +class Server: + + __address:str + __tcpPort:str + __udpPort:str + __world:World + __gameManager:GameManager + + networkManager:NetworkManager + + def __init__(self, address:str, tcpPort:str, udpPort:str, logger:Logger): + self.__address = address + self.__tcpPort = tcpPort + self.__udpPort = udpPort + self.__world = World() + self.logger = logger + + self.logger.info("starting up game manager") + self.__gameManager = GameManager(logger) + + self.logger.info("preparing to start server") + self.startServer(self.__gameManager) + + # handles starting the server and assigning socket values to the local reference + def startServer(self, gameManager:GameManager): + self.logger.info("starting up network manager") + self.__networkManager = NetworkManager(self.__address, self.__tcpPort, self.__udpPort, gameManager) + + def getNetworkManager(self) -> NetworkManager: + return self.__networkManager \ No newline at end of file diff --git a/Classes/System/Utils/Path.py b/Classes/System/Utils/Path.py new file mode 100644 index 0000000..a274f43 --- /dev/null +++ b/Classes/System/Utils/Path.py @@ -0,0 +1,6 @@ +import os + + +class PathUtil: + def getAbsolutePathTo(notAbsolutPath:str) -> str: + return os.path.abspath("OLD_Server/" + notAbsolutPath) \ No newline at end of file diff --git a/Classes/System/Utils/StringUtils.py b/Classes/System/Utils/StringUtils.py new file mode 100644 index 0000000..e4d02a1 --- /dev/null +++ b/Classes/System/Utils/StringUtils.py @@ -0,0 +1,11 @@ +import random +import string + + +class StringUtils: + def get_random_string(length) -> str: + # choose from all lowercase letter + letters = string.ascii_lowercase + result_str = ''.join(random.choice(letters) for i in range(length)) + print("Random string of length", length, "is:", result_str) + return result_str diff --git a/Classes/System/World.py b/Classes/System/World.py new file mode 100644 index 0000000..f6b2957 --- /dev/null +++ b/Classes/System/World.py @@ -0,0 +1,20 @@ +import socket +from Classes.Game.Player import Player + + +class World: + __players:dict + + def __init__(self): + self.__players = {} + + def getPlayers(self) -> list: + return self.__players + + def addPlayers(self, player:Player, socket:socket, clientAddr) -> list: + self.__players[clientAddr] = { + player: player, + socket:socket + } + + return self.__players \ No newline at end of file diff --git a/index.py b/index.py new file mode 100644 index 0000000..27fd861 --- /dev/null +++ b/index.py @@ -0,0 +1,31 @@ +import logging +import os +import random +import string +import sys + +from Classes.System.Server import Server +from Classes.System.Logger import Logger +from Classes.System.Utils.Path import PathUtil + +def get_random_string(length) -> str: + # choose from all lowercase letter + letters = string.ascii_lowercase + result_str = ''.join(random.choice(letters) for i in range(length)) + print("Random string of length", length, "is:", result_str) + return result_str + +def main(): + # retrieves host data from environment + HOST = "127.0.0.1" + TCPPORT = "54322" + UDPPORT = "54323" + + logger = Logger(PathUtil.getAbsolutePathTo("log/"+get_random_string(8)+".log")) + logger.info("starting up server") + server = Server(HOST, TCPPORT, UDPPORT, logging) + server.getNetworkManager().tcp.stop() + sys.exit(0) + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/server logic notes.md b/server logic notes.md new file mode 100644 index 0000000..7370d4a --- /dev/null +++ b/server logic notes.md @@ -0,0 +1,42 @@ +# validation for placing cards: +- is the game still running +- is it the players turn +- does the card exist +- does the player have that card in his deck +- does the player have this card in his hand +- is the type of card allowed in that type of field +- is the field already blocked by another card + +# validation for attacking another player +- is the game still running +- is it the players turn +- does the card exist +- does the player have that card in his deck +- is that card played +- does the enemy have remaining monster cards on his side + - if yes a direct attack would only be possible if a effect allows it +- can this card attack + - is the card of correct type + - is it blocked by effects (will be implemented after card effects are implemented) + +# player death management (win condition) +- does a players hp go to 0? + - make the other player the winner + - if an effect affects the playing player card priority comes first + +# handle a player leaving +- check if game still runs + - make remaining player win if yes + +# turn management +- server keeps track of each turn + - whos turn is it + - what turn state is currently active + - draw state + - place state + - is the player trying to do actions not allowed in the given state + +# drawing cards: +- ensure the player only can have 7 cards + - if limit exceeds the player payes lifepoints and drops a card +- ensure the drawn card for sure still can be in the players deck \ No newline at end of file