From 57621537c4541f3784d5d955d30bc8d800fbe1bd Mon Sep 17 00:00:00 2001 From: steev Date: Wed, 28 Feb 2024 21:10:21 +0100 Subject: [PATCH] removed server added client code --- Assets/Cards/{Arena => 0}/field.png | Bin .../testmonstercard => 1}/card.json | 3 +- .../testmonstercard => 1}/card.png | Bin .../testspellcard => 2}/artworkjson.png | Bin Assets/Cards/2/testspellcard.json | 9 + .../testtrapcard => 3}/artworkjson.png | Bin Assets/Cards/3/testtrapcard.json | 9 + .../testspellcard/testspellcard.json | 6 - .../TrapCards/testtrapcard/testtrapcard.json | 6 - Classes/Game/BoardField.py | 85 ++++++++++ Classes/Game/Cards/Card.py | 110 ++++++++++++ Classes/Game/Cards/MonsterCard.py | 88 ++++++++++ Classes/Game/Cards/SpellCard.py | 72 ++++++++ Classes/Game/Cards/TrapCard.py | 72 ++++++++ Classes/Game/Events/GameStart.py | 17 ++ Classes/Game/Events/Login.py | 25 +++ Classes/Game/Events/PlaceCard.py | 32 ++++ Classes/Game/Player.py | 53 +++--- Classes/Game/World.py | 137 +++++++++++++++ Classes/System/App.py | 141 ++++++++++++++++ Classes/System/Components/InputHandler.py | 53 ++++++ Classes/System/Components/Label.py | 47 ++++++ Classes/System/Components/Window.py | 54 ++++++ Classes/System/GameManager.py | 158 ++---------------- Classes/System/Logger.py | 18 -- Classes/System/Network/EventHandler.py | 47 +++--- Classes/System/Network/NetworkManger.py | 137 --------------- Classes/System/Network/TCPClient.py | 69 ++++++++ Classes/System/PlayerManager.py | 23 --- Classes/System/QueueManager.py | 34 ---- Classes/System/Server.py | 40 ----- Classes/System/Utils/Path.py | 2 +- Classes/System/World.py | 20 --- index.py | 31 ---- main.py | 7 + 35 files changed, 1093 insertions(+), 512 deletions(-) rename Assets/Cards/{Arena => 0}/field.png (100%) rename Assets/Cards/{MonsterCards/testmonstercard => 1}/card.json (87%) rename Assets/Cards/{MonsterCards/testmonstercard => 1}/card.png (100%) rename Assets/Cards/{SpeelCards/testspellcard => 2}/artworkjson.png (100%) create mode 100644 Assets/Cards/2/testspellcard.json rename Assets/Cards/{TrapCards/testtrapcard => 3}/artworkjson.png (100%) create mode 100644 Assets/Cards/3/testtrapcard.json delete mode 100644 Assets/Cards/SpeelCards/testspellcard/testspellcard.json delete mode 100644 Assets/Cards/TrapCards/testtrapcard/testtrapcard.json create mode 100644 Classes/Game/BoardField.py create mode 100644 Classes/Game/Cards/Card.py create mode 100644 Classes/Game/Cards/MonsterCard.py create mode 100644 Classes/Game/Cards/SpellCard.py create mode 100644 Classes/Game/Cards/TrapCard.py create mode 100644 Classes/Game/Events/GameStart.py create mode 100644 Classes/Game/Events/Login.py create mode 100644 Classes/Game/Events/PlaceCard.py create mode 100644 Classes/Game/World.py create mode 100644 Classes/System/App.py create mode 100644 Classes/System/Components/InputHandler.py create mode 100644 Classes/System/Components/Label.py create mode 100644 Classes/System/Components/Window.py delete mode 100644 Classes/System/Logger.py delete mode 100644 Classes/System/Network/NetworkManger.py create mode 100644 Classes/System/Network/TCPClient.py delete mode 100644 Classes/System/PlayerManager.py delete mode 100644 Classes/System/QueueManager.py delete mode 100644 Classes/System/Server.py delete mode 100644 Classes/System/World.py delete mode 100644 index.py create mode 100644 main.py diff --git a/Assets/Cards/Arena/field.png b/Assets/Cards/0/field.png similarity index 100% rename from Assets/Cards/Arena/field.png rename to Assets/Cards/0/field.png diff --git a/Assets/Cards/MonsterCards/testmonstercard/card.json b/Assets/Cards/1/card.json similarity index 87% rename from Assets/Cards/MonsterCards/testmonstercard/card.json rename to Assets/Cards/1/card.json index 0df2ece..3ce8af5 100644 --- a/Assets/Cards/MonsterCards/testmonstercard/card.json +++ b/Assets/Cards/1/card.json @@ -1,7 +1,8 @@ { "id": 1, + "type":"MonsterCard", "name": "Test Monster", - "image": "Assets/Cards/testmonstercard/cards.png", + "image": "Assets/Cards/1/cards.png", "description": "can attack other monsters", "costs": 30, "defense": 40, diff --git a/Assets/Cards/MonsterCards/testmonstercard/card.png b/Assets/Cards/1/card.png similarity index 100% rename from Assets/Cards/MonsterCards/testmonstercard/card.png rename to Assets/Cards/1/card.png diff --git a/Assets/Cards/SpeelCards/testspellcard/artworkjson.png b/Assets/Cards/2/artworkjson.png similarity index 100% rename from Assets/Cards/SpeelCards/testspellcard/artworkjson.png rename to Assets/Cards/2/artworkjson.png diff --git a/Assets/Cards/2/testspellcard.json b/Assets/Cards/2/testspellcard.json new file mode 100644 index 0000000..f812019 --- /dev/null +++ b/Assets/Cards/2/testspellcard.json @@ -0,0 +1,9 @@ +{ + "name": "testspell", + "type":"EffectCard", + "image":"Assets/Cards/2/artwork.png", + "costs": 30, + "defense": 0, + "description":"this is a test spell card", + "attacks": [] +} \ No newline at end of file diff --git a/Assets/Cards/TrapCards/testtrapcard/artworkjson.png b/Assets/Cards/3/artworkjson.png similarity index 100% rename from Assets/Cards/TrapCards/testtrapcard/artworkjson.png rename to Assets/Cards/3/artworkjson.png diff --git a/Assets/Cards/3/testtrapcard.json b/Assets/Cards/3/testtrapcard.json new file mode 100644 index 0000000..13ed51a --- /dev/null +++ b/Assets/Cards/3/testtrapcard.json @@ -0,0 +1,9 @@ +{ + "name": "testtrap", + "type":"EffectCard", + "image":"Assets/Cards/3/artwork.png", + "costs": 30, + "defense": 0, + "description":"this is a test trap card", + "attacks": [] +} \ No newline at end of file diff --git a/Assets/Cards/SpeelCards/testspellcard/testspellcard.json b/Assets/Cards/SpeelCards/testspellcard/testspellcard.json deleted file mode 100644 index 51d4083..0000000 --- a/Assets/Cards/SpeelCards/testspellcard/testspellcard.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "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/testtrapcard.json b/Assets/Cards/TrapCards/testtrapcard/testtrapcard.json deleted file mode 100644 index 6d1cd5a..0000000 --- a/Assets/Cards/TrapCards/testtrapcard/testtrapcard.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "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/BoardField.py b/Classes/Game/BoardField.py new file mode 100644 index 0000000..c490fee --- /dev/null +++ b/Classes/Game/BoardField.py @@ -0,0 +1,85 @@ +import os +import pygame + +class BoardField(pygame.sprite.Sprite): + __fieldID:str + __name:str + __side:str + __type:str + __pos:pygame.Vector2 + __size:tuple + __color:tuple = (255,255,255) + __holdsCard = None + image:pygame.image + rect:pygame.rect + + def __init__(self, name:str, side:str, type:str, pos:pygame.Vector2, imagePath:str, fid:str): + pygame.sprite.Sprite.__init__(self) + self.__name = name + self.__side = side + self.__type = type + self.__pos = pos + self.__holdsCard = None + self.__fieldID = fid + + # Überprüfen des Dateipfads + if not os.path.exists(imagePath): + print("Der Dateipfad zur Bilddatei ist ungültig oder die Datei existiert nicht.") + else: + # Wenn der Pfad gültig ist, versuchen Sie, das Bild zu laden + self.image = pygame.image.load(imagePath).convert_alpha() + self.rect = self.image.get_rect() + self.rect.center = self.__pos + + def getName(self) -> str: + return self.__name + + def getSide(self) -> str: + return self.__side + + def getType(self) -> str: + return self.__type + + def getPos(self) -> pygame.Vector2: + return self.__pos + + def getSize(self) -> tuple: + return self.__size + + def getColor(self) -> tuple: + return self.__color + + def getRect(self) -> pygame.Rect: + return self.rect + + def getImage(self) -> pygame.image: + return self.image + + def getHoldingCard(self): + return self.__holdsCard + + def setName(self, name:str) -> str: + self.__name = name + return self.__name + + def setType(self,type:str) -> str: + self.__type = type + return self.__type + + def setPos(self, pos:pygame.Vector2) -> pygame.Vector2: + self.pos = pos + return self.__pos + + def setSize(self, size:tuple) -> tuple: + self.__size = size + return self.__size + + def setColor(self, color:tuple) -> tuple: + self.__color = color + return self.__color + + def setCardHolding(self, card): + self.__holdsCard = card + + def getFieldID(self) -> str: + return self.__fieldID \ No newline at end of file diff --git a/Classes/Game/Cards/Card.py b/Classes/Game/Cards/Card.py new file mode 100644 index 0000000..c98e175 --- /dev/null +++ b/Classes/Game/Cards/Card.py @@ -0,0 +1,110 @@ +import json +import pygame + +from Classes.System.Components.InputHandler import InputHandler +from Classes.Game.Player import Player + +class Card(pygame.sprite.Sprite): + __name:str + __id:int + __description:str + __attacks = [] + __type:str = "Monster" + __pos:pygame.Vector2 + __dragging:bool = False + __offset:pygame.Vector2 = pygame.Vector2(0,0) + __inputHandler: InputHandler + __owner:Player + __state:str + __dragable:bool = True + image:pygame.image + rect:pygame.rect + + def __init__(self, pos: pygame.Vector2, assetDir: str, inputHandler: InputHandler, owner: Player, dragable:bool=True): + if assetDir == "": + raise ValueError("Card: imagePath cannot be empty") + + pygame.sprite.Sprite.__init__(self) + + with open(assetDir + "/card.json", 'r') as file: + data = json.load(file) + + self.__owner = owner + self.__id = int(data["id"]) + self.__pos = pos + self.__name = data["name"] + self.__type = data.get("type") + self.image = pygame.image.load(assetDir + "/card.png").convert_alpha() + self.rect = self.image.get_rect() + self.__inputHandler = inputHandler + self.rect.center = self.__pos + self.__description = data["description"] + self.original_size = self.image.get_size() + self.original_position = self.rect.center + self.__state = "onHand" + self.__type = "Monster" + + self.__attacks = [] + for attack in data.get("attacks", []): + self.__attacks.append(attack) + + def update(self): + if self.__dragging: + if self.__dragable: + mouse_pos = self.__inputHandler.getMousePos() + self.__pos = mouse_pos + self.rect.center = self.__pos + + def attacks(self): + return self.__attacks + + def getName(self) -> str: + return self.__name + + def getCardSprite(self) -> pygame.image: + return self.__cardSprite + + def getDescription(self): + return self.__description + + def getDragging(self): + return self.__dragging + + def getDragable(self): + return self.__dragable + + def getOffset(self): + return self.__offset + + def getPos(self) -> pygame.Vector2: + return self.__pos + + def getX(self) -> int: + return self.__pos.x + + def getY(self) -> int: + return self.__pos.y + + def getType(self): + return self.__type + + def getID(self) -> int: + return self.__id + + def getOwner(self) -> Player: + return self.__owner + + def getState(self) -> str: + return self.__state + + def setDragging(self, dragging:bool): + self.__dragging = dragging + + def setOffset(self, offset:pygame.Vector2): + self.__offset = offset + + def setPos(self, pos:pygame.Vector2): + self.__pos = pos + + def setState(self, state:str): + self.__state = state \ No newline at end of file diff --git a/Classes/Game/Cards/MonsterCard.py b/Classes/Game/Cards/MonsterCard.py new file mode 100644 index 0000000..63f1e28 --- /dev/null +++ b/Classes/Game/Cards/MonsterCard.py @@ -0,0 +1,88 @@ +import json +import pygame + +from Classes.System.Components.InputHandler import InputHandler +from Classes.Game.Player import Player + +class MonsterCard(pygame.sprite.Sprite): + __name:str + __id:int + __description:str + __attacks = [] + __type:str = "Monster" + __pos:pygame.Vector2 + __dragging:bool = False + __offset:pygame.Vector2 = pygame.Vector2(0,0) + __inputHandler: InputHandler + __owner:Player + image:pygame.image + rect:pygame.rect + + def __init__(self, pos:pygame.Vector2, assetDir:str, inputHandler:InputHandler, owner:Player): + if assetDir == "": + return ValueError.add_note("Card: imagePath cannot be empty") + + pygame.sprite.Sprite.__init__(self) + + data = json.load(open(assetDir + "/card.json")) + self.__owner = owner + self.__id = int(data["id"]) + self.__pos = pos + self.__name = data["name"] + self.image = pygame.image.load(assetDir + "/card.png").convert_alpha() + self.rect = self.image.get_rect() + self.dragging = False + self.offset = pygame.Vector2(0, 0) # Offset zwischen der Karte und der Mausposition + self.__inputHandler = inputHandler + self.rect.center = self.__pos + self.__description = data["description"] + self.original_size = self.image.get_size() + self.original_position = self.rect.center + + for attack in data["attacks"]: + self.__attacks.append(attack) + + def update(self): + if self.__dragging: + mouse_pos = self.__inputHandler.getMousePos() + self.__pos = mouse_pos + self.rect.center = self.__pos + + def attacks(self): + return self.__attacks + + def getName(self) -> str: + return self.__name + + def getCardSprite(self) -> pygame.image: + return self.__cardSprite + + def getDescription(self): + return self.__description + + def getDragging(self): + return self.__dragging + + def getOffset(self): + return self.__offset + + def getPos(self): + return self.__pos + + def getType(self): + return self.__type + + def getID(self) -> int: + return self.__id + + def getOwner(self) -> Player: + return self.__owner + + def setDragging(self, dragging:bool): + self.__dragging = dragging + + def setOffset(self, offset:pygame.Vector2): + self.__offset = offset + + def setPos(self, pos:pygame.Vector2): + self.__pos = pos \ No newline at end of file diff --git a/Classes/Game/Cards/SpellCard.py b/Classes/Game/Cards/SpellCard.py new file mode 100644 index 0000000..5209399 --- /dev/null +++ b/Classes/Game/Cards/SpellCard.py @@ -0,0 +1,72 @@ +import json +from typing import Any + +import pygame + +from Classes.System.Components.InputHandler import InputHandler + +class SpellCard(pygame.sprite.Sprite): + __name:str + __description:str + __pos:pygame.Vector2 + __dragging:bool = False + __offset:pygame.Vector2 = pygame.Vector2(0,0) + __inputHandler: InputHandler + __type:str = "Effect" + image:pygame.image + rect:pygame.rect + + def __init__(self, pos:pygame.Vector2, assetDir:str, inputHandler:InputHandler): + if assetDir == "": + return ValueError.add_note("Card: imagePath cannot be empty") + + pygame.sprite.Sprite.__init__(self) + + data = json.load(open(assetDir + "/card.json")) + self.__pos = pos + self.__name = data["name"] + self.image = pygame.image.load(assetDir + "/card.png").convert_alpha() + self.rect = self.image.get_rect() + self.dragging = False + self.offset = pygame.Vector2(0, 0) # Offset zwischen der Karte und der Mausposition + self.__inputHandler = inputHandler + self.rect.center = self.__pos + self.__description = data["description"] + self.original_size = self.image.get_size() + self.original_position = self.rect.center + + def update(self): + if self.getDragging(): + mouse_pos = self.__inputHandler.getMousePos() + self.__pos = mouse_pos + self.rect.center = self.__pos + + def getName(self) -> str: + return self.__name + + def getCardSprite(self) -> pygame.image: + return self.__cardSprite + + def getDescription(self): + return self.__description + + def getDragging(self): + return self.__dragging + + def getOffset(self): + return self.__offset + + def getPos(self): + return self.__pos + + def getType(self): + return self.__type + + def setDragging(self, dragging:bool): + self.__dragging = dragging + + def setOffset(self, offset:pygame.Vector2): + self.__offset = offset + + def setPos(self, pos:pygame.Vector2): + self.__pos = pos \ No newline at end of file diff --git a/Classes/Game/Cards/TrapCard.py b/Classes/Game/Cards/TrapCard.py new file mode 100644 index 0000000..dcb6f76 --- /dev/null +++ b/Classes/Game/Cards/TrapCard.py @@ -0,0 +1,72 @@ +import json +from typing import Any + +import pygame + +from Classes.System.Components.InputHandler import InputHandler + +class TrapCard(pygame.sprite.Sprite): + __name:str + __description:str + __pos:pygame.Vector2 + __dragging:bool = False + __offset:pygame.Vector2 = pygame.Vector2(0,0) + __inputHandler: InputHandler + __type:str = "Effect" + image:pygame.image + rect:pygame.rect + + def __init__(self, pos:pygame.Vector2, assetDir:str, inputHandler:InputHandler): + if assetDir == "": + return ValueError.add_note("Card: imagePath cannot be empty") + + pygame.sprite.Sprite.__init__(self) + + data = json.load(open(assetDir + "/card.json")) + self.__pos = pos + self.__name = data["name"] + self.__inputHandler = inputHandler + self.__description = data["description"] + self.original_size = self.image.get_size() + self.original_position = self.rect.center + self.offset = pygame.Vector2(0, 0) # Offset zwischen der Karte und der Mausposition + self.rect = self.image.get_rect() + self.rect.center = self.__pos + self.image = pygame.image.load(assetDir + "/card.png").convert_alpha() + self.__dragging = False + + def update(self): + if self.getDragging(): + mouse_pos = self.__inputHandler.getMousePos() + self.__pos = mouse_pos + self.rect.center = self.__pos + + def getName(self) -> str: + return self.__name + + def getCardSprite(self) -> pygame.image: + return self.__cardSprite + + def getDescription(self): + return self.__description + + def getDragging(self): + return self.__dragging + + def getOffset(self): + return self.__offset + + def getPos(self): + return self.__pos + + def getType(self): + return self.__type + + def setDragging(self, dragging:bool): + self.__dragging = dragging + + def setOffset(self, offset:pygame.Vector2): + self.__offset = offset + + def setPos(self, pos:pygame.Vector2): + self.__pos = pos \ No newline at end of file diff --git a/Classes/Game/Events/GameStart.py b/Classes/Game/Events/GameStart.py new file mode 100644 index 0000000..c6c6a27 --- /dev/null +++ b/Classes/Game/Events/GameStart.py @@ -0,0 +1,17 @@ +import pygame +from Classes.Game.World import World +from Classes.Game.Cards.Card import Card +from Classes.Game.Player import Player +from Classes.System.Components.InputHandler import InputHandler +from Classes.System.Utils.Path import PathUtil + + +# send from the server to tell the player the game starts +# gives the client its and the opponents stats (not cards!!) +def GameStart(world: World, handCards:list, inputHandler:InputHandler, owner:Player): + index:int = 0 + + for card in handCards: + #world.AddToPlayerHand(Card(pygame.Vector2(500 + (index * 100), 1050), PathUtil.getAbsolutePathTo(f"Assets/Cards/{card}/"), inputHandler, Player(1000, 0, "test"), dragable=True)) + world.spawnCard(PathUtil.getAbsolutePathTo(f"Assets/Cards/{card}"), pygame.Vector2(500 + (index * 100), 1050), inputHandler, world.getPlayer()) + index=index+1 \ No newline at end of file diff --git a/Classes/Game/Events/Login.py b/Classes/Game/Events/Login.py new file mode 100644 index 0000000..71fef11 --- /dev/null +++ b/Classes/Game/Events/Login.py @@ -0,0 +1,25 @@ +import json +import pygame +from Classes.Game.World import World +from Classes.Game.Player import Player + + +# event the client sends to let the server know it logged in +def Login(tcpClient): + payload = { + "event":"login", + "username": "player", + "deck": [1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1] + } + + tcpClient.send(payload) + +# server response for login event +def LoginResponse(message:dict, world:World): + print("LoginResponse called") + # checks if the response on the login request is successfull + if message["status"] != "success": + print("login failed") + else: + print("receiving login confirmation from server") + world.setPlayer(Player(0,0,message["name"], message["id"])) \ No newline at end of file diff --git a/Classes/Game/Events/PlaceCard.py b/Classes/Game/Events/PlaceCard.py new file mode 100644 index 0000000..5d72646 --- /dev/null +++ b/Classes/Game/Events/PlaceCard.py @@ -0,0 +1,32 @@ +import pygame +from Classes.Game.World import World +from Classes.System.Components.InputHandler import InputHandler +from Classes.Game.Cards.Card import Card +from Classes.System.Utils.Path import PathUtil + +# the event the client sends to the server when it places a card +def PlaceCard(tcpClient, card:Card, player): + # todo: send card information to the server + # todo: required info is: + # - position + # - field type (used for validating what field the card is played in will be compared on server side) + # - card id (server will do the rest to fetch card info) + payload = { + "event":"placecard", + "card": card.getID(), + "type": card.getType(), + "user": player.getID(), + "x": card.getX(), + "y": card.getY(), + } + + tcpClient.send(payload) + +# the event send from the server to display a card on the field +def CardPlaced(world:World, card:int, owner:str, pos:pygame.Vector2, inputHandler:InputHandler): + # todo: make this work with all cardtypes + print("placing enemy card") + world.spawnEnemyCard(PathUtil.getAbsolutePathTo(f"Assets/Cards/{card}"), pos, inputHandler, owner) + +def MovedCard(world:World, card:int, type:str, owner:str, oldPos:pygame.Vector2, newPos:pygame.Vector2, inputHandler:InputHandler): + pass \ No newline at end of file diff --git a/Classes/Game/Player.py b/Classes/Game/Player.py index 2d337be..cb8ec7f 100644 --- a/Classes/Game/Player.py +++ b/Classes/Game/Player.py @@ -1,57 +1,46 @@ -import random - +import pygame 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): + __handCards:pygame.sprite.Group + + def __init__(self, hp:int, mana:int, name:str, id:int=0): self.__hp = hp self.__mana = mana self.__name = name - self.__handCards = [] - self.__deck = deck - self.__id = random.randint(3, 99999) + self.__id = id - def shuffleDeck(self): - self.__deck = random.shuffle(self.__deck) + def setID(self, id:int): + self.__id = id - def getDeck(self) -> list: - return self.__deck + def getID(self) -> int: + return self.__id def getName(self) -> str: return self.__name def getHP(self) -> int: return self.__hp - + + def getMana(self) -> int: + return self.__mana + def adjustHP(self, hp:int) -> int: self.__hp = self.__hp + hp - def getID(self) -> int: - return self.__id - - def getHand(self) -> list: + def getHand(self) -> pygame.sprite.Group: 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) + def AddToHand(self, card) -> pygame.sprite.Group: + self.__handCards.add(card) return self.__handCards - def setHand(self, hand:list): - self.__handCards = hand - - def removeFromHand(self, pos:int) -> list: + def removeFromHand(self, pos:int) -> pygame.sprite.Group: self.__handCards.remove(pos) - return self.__handCards \ No newline at end of file + return self.__handCards + + def setMana(self, mana:int): + self.__mana = mana \ No newline at end of file diff --git a/Classes/Game/World.py b/Classes/Game/World.py new file mode 100644 index 0000000..94383ef --- /dev/null +++ b/Classes/Game/World.py @@ -0,0 +1,137 @@ +import pygame +from Classes.Game.BoardField import BoardField +from Classes.System.Components.Label import Label +from Classes.Game.Cards.MonsterCard import MonsterCard +from Classes.Game.Cards.SpellCard import SpellCard +from Classes.Game.Cards.TrapCard import TrapCard +from Classes.System.Components.InputHandler import InputHandler +from Classes.Game.Player import Player +from Classes.Game.Cards.Card import Card +from Classes.System.Utils.Path import PathUtil + +class World(): + __boardFields:list + player:Player + enemy:Player + __labels:list + __cards:pygame.sprite.Group() + __PlayerHandCards:pygame.sprite.Group() + screen:pygame.surface + __cardWidth:int = 150 + __cardHeight:int = 200 + __cardOffset:int = 400 + + def __init__(self, screen:pygame.surface, cardWidth:int=200, cardHeight:int=250, cardOffset:int=400): + self.__boardFields = [] + self.__labels = [] + self.__cards = pygame.sprite.Group() + self.__PlayerHandCards = pygame.sprite.Group() + self.__screen = screen + self.__cardWidth = cardWidth + self.__cardHeight = cardHeight + self.__cardOffset = cardOffset + self.player = None + self.enemy = None + self.buildGameWorld() + + def buildGameWorld(self): + # construct elements arround the playerfield + + # presets the y position later passed down to the vector2 + eRow1Height = 85 + eRow2Height = (self.__cardHeight + 45) + pRow1Height = ((2 * self.__cardHeight) + 75) + pRow2Height = ((2 * self.__cardHeight) + self.__cardHeight + 30) + + eDeckPos = pygame.Vector2((self.__cardOffset + ((self.__cardWidth + 10) * 0)), eRow1Height) + eGravePos = pygame.Vector2((self.__cardOffset + ((self.__cardWidth + 10) * 0)), eRow2Height) + eNamePos = pygame.Vector2(15, 45) + eHPPos = pygame.Vector2(15, 75) + + pGravePos = pygame.Vector2((self.__cardOffset + (((self.__cardWidth + 10) - 30 ) * 5)), pRow1Height) + pDeckPos = pygame.Vector2((self.__cardOffset + (((self.__cardWidth + 10) - 30 ) * 5)), pRow2Height) + pNamePos = pygame.Vector2(20, pRow2Height + 195) + pHPPos = pygame.Vector2(20, pRow2Height + 225) + pManaPos = pygame.Vector2(20, pRow2Height + 255) + + # labeling + self.__labels.append(Label("PlayerHP", self.__screen, "1000 / 1000", pHPPos)) + self.__labels.append(Label("PlayerName", self.__screen, "Not Connected", pNamePos)) + self.__labels.append(Label("PlayerName", self.__screen, "0", pManaPos)) + self.__labels.append(Label("EnemyHP", self.__screen, "1000 / 1000", eHPPos)) + self.__labels.append(Label("EnemyName", self.__screen, "Not Connected", eNamePos)) + + self.__boardFields.append(BoardField("EnemyDeck", "Enemy", "Deck", eDeckPos, PathUtil.getAbsolutePathTo("Assets/Cards/0/field.png"), "e-deck")) + self.__boardFields.append(BoardField("EnemyGraveyard", "Enemy", "Grave", eGravePos, PathUtil.getAbsolutePathTo("Assets/Cards/0/field.png"), "e-grave")) + self.__boardFields.append(BoardField("PlayerDeck", "Player", "Deck", pDeckPos, PathUtil.getAbsolutePathTo("Assets/Cards/0/field.png"), "P-deck")) + self.__boardFields.append(BoardField("PlayerGraveyard", "Player", "Grave", pGravePos, PathUtil.getAbsolutePathTo("Assets/Cards/0/field.png"), "p-grave")) + + # handle field creation + for i in range(5): + pMonsterPos = pygame.Vector2((self.__cardOffset + (((self.__cardWidth + 10) - 30) * i)), pRow1Height) + pEffectPos = pygame.Vector2((self.__cardOffset + (((self.__cardWidth + 10) - 30) * i)), pRow2Height) + eMonsterPos = pygame.Vector2((self.__cardOffset + (((self.__cardWidth + 10) - 30) * (i+1)), eRow1Height)) + eEffectPos = pygame.Vector2((self.__cardOffset + (((self.__cardWidth + 10) - 30) * (i+1))), eRow2Height) + + self.__boardFields.append(BoardField("PlayerMonsterField-"+str(i), "Player", "Monster", pMonsterPos, PathUtil.getAbsolutePathTo("Assets/Cards/0/field.png"), "pm"+str(id))) + self.__boardFields.append(BoardField("PlayerEffectField-"+str(i), "Player", "Effect", pEffectPos, PathUtil.getAbsolutePathTo("Assets/Cards/0/field.png"), "pe"+str(id))) + self.__boardFields.append(BoardField("EnemyMonsterField-"+str(i), "Enemy", "Monster", eMonsterPos, PathUtil.getAbsolutePathTo("Assets/Cards/0/field.png"), "em"+str(id))) + self.__boardFields.append(BoardField("EnemySpellTrapField-"+str(i), "Enemy", "Effect", eEffectPos, PathUtil.getAbsolutePathTo("Assets/Cards/0/field.png"), "ee"+str(id))) + + def getBoardFields(self) -> list: + return self.__boardFields + + def getLabels(self) -> list: + return self.__labels + + def getCardWidth(self) -> int: + return self.__cardWidth + + def getCardHeight(self) -> int: + return self.__cardHeight + + def getCards(self) -> pygame.sprite.Group: + return self.__cards + + def getHandCards(self) -> pygame.sprite.Group: + return self.__PlayerHandCards + + def getPlayer(self) -> Player: + return self.player + + def getEnemy(self) -> Player: + return self.__enemy + + def setPlayer(self, player:Player): + print(f"setting player {player}") + self.player = player + for label in self.__labels: + if label.getName() == "PlayerName": + label.setText(player.getName()) + + def setEnemy(self, player:Player): + self.enemy = player + for label in self.__labels: + if label.getName() == "EnemyName": + label.setText(player.getName()) + + def spawnCard(self, asset:str, pos:pygame.Vector2, inputHandler:InputHandler, owner:Player) -> Card: + card = Card(pos, asset, inputHandler, owner) + self.__cards.add(card) + return card + + def spawnEnemyCard(self, asset:str, pos:pygame.Vector2, inputHandler:InputHandler, owner:str) -> Card: + pos.y = (self.__cardHeight + 45) + + card = Card(pos, asset, inputHandler, self.enemy, dragable=False) + self.__cards.add(card) + + print(f"new card collection {self.__cards}") + return card + + def spawnCards(self, cards:pygame.sprite.Group): + for card in cards: + self.__cards.add(card) + + def AddToPlayerHand(self, Card:Card): + self.__PlayerHandCards.add(Card) \ No newline at end of file diff --git a/Classes/System/App.py b/Classes/System/App.py new file mode 100644 index 0000000..75e602d --- /dev/null +++ b/Classes/System/App.py @@ -0,0 +1,141 @@ +import pygame +from pygame.locals import * + +from Classes.Game.Cards.MonsterCard import MonsterCard +from Classes.System.Components.Window import Window +from Classes.System.Components.InputHandler import InputHandler +from Classes.Game.World import World +from Classes.System.Network.TCPClient import TCPClient +from Classes.Game.Events.Login import Login +from Classes.Game.Events.PlaceCard import PlaceCard +from Classes.Game.Player import Player +from Classes.Game.Cards.Card import Card +from Classes.System.Utils.Path import PathUtil + +class App: + + __window:Window + __running:bool = True + __FPS = 60 + __clock = pygame.time.Clock() + __myFont:pygame.font + __world:World + __inputHandler: InputHandler + __tcpClient: TCPClient + + def __init__(self, width:int=1920, height:int=1080, title:str="default title"): + pygame.font.init() + self.__myFont = pygame.font.SysFont('Comic Sans MS', 30) + self.__window = Window(width=width, height=height, title=title) + self.__inputHandler = InputHandler() + self.selectedCard = None + + # game word + self.__world = World(self.__window.getScreen()) + + try: + self.__tcpClient = TCPClient("127.0.0.1", "54322", self.__world, self.__inputHandler) + Login(self.__tcpClient) # will login to the server + except Exception as e: + print(f"failed to login due to error {e}") + print("server connection failed or got refused") + pass + + self.startGameLoop() + self.onCleanup() + + def startGameLoop(self): + + # create sprite groups + # todo: remove these and let server handle card creation instead + # blocker: server - client communication [WIP] + # self.__world.spawnCard(PathUtil.getAbsolutePathTo("Assets/Cards/1/"), pygame.Vector2(500, 1050), self.__inputHandler, Player(1000, 0, "test")) + # self.__world.spawnCard(PathUtil.getAbsolutePathTo("Assets/Cards/1/"), pygame.Vector2(600, 1050), self.__inputHandler, Player(1000, 0, "test")) + # self.__world.spawnCard(PathUtil.getAbsolutePathTo("Assets/Cards/1/"), pygame.Vector2(700, 1050), self.__inputHandler, Player(1000, 0, "test")) + # self.__world.spawnCard(PathUtil.getAbsolutePathTo("Assets/Cards/1/"), pygame.Vector2(800, 1050), self.__inputHandler, Player(1000, 0, "test")) + # self.__world.spawnCard(PathUtil.getAbsolutePathTo("Assets/Cards/1/"), pygame.Vector2(900, 1050), self.__inputHandler, Player(1000, 0, "test")) + # self.__world.spawnCard(PathUtil.getAbsolutePathTo("Assets/Cards/1/"), pygame.Vector2(1000, 1050), self.__inputHandler, Player(1000, 0, "test")) + + while self.__running: + self.__clock.tick(self.__FPS) + + self.__window.getScreen().fill((0,0,0)) + + # render world + self.__window.drawWorld(self.__world) + + # updates all cards inside the cards Spritegroup at each step the gameloops does + self.__world.getCards().update() + self.__world.getHandCards().update() + + # draw groups + self.__window.drawSpriteGroup(self.__world.getCards()) + self.__window.drawSpriteGroup(self.__world.getHandCards()) + + # event handler + self.handleEvent(pygame.event.get()) + + # emits update to the game + pygame.display.update() + + # handles incoming event queue + def handleEvent(self, events): + # TODO: fix bug that stacks cards when dragging them around + try: + for event in events: + if event.type == pygame.QUIT: + self.onCleanup() + elif self.__inputHandler.getMousePressed()[0]: + for card in self.__world.getCards(): + if card.getDragable() == True: + if card.rect.collidepoint(self.__inputHandler.getMousePos()): + #self.__logger.info(f"dragging card {card}") + self.selectedCard = card + + # failsafe to prevent NoneType errors + if self.selectedCard != None: + #self.__logger.info(f"working with card: {self.selectedCard}") + self.selectedCard.setDragging(True) + elif event.type == pygame.MOUSEBUTTONUP: + print("left mousebutton") + if event.button == 1: + if self.selectedCard != None: + self.selectedCard.setDragging(False) + print(self.selectedCard) + for field in self.__world.getBoardFields(): + print(f"checking field {field} is field mathinc? {field.getRect().collidepoint(self.__inputHandler.getMousePos())}") + if field.getRect().collidepoint(self.__inputHandler.getMousePos()): + print(f"is position valid? {field.getSide() == 'Player' and field.getType() == self.selectedCard.getType()} ") + print(f"side {field.getSide()} fieldtype {field.getType()} card type {self.selectedCard.getType()}") + + if field.getSide() == "Player" and field.getType() == self.selectedCard.getType(): + print(f"found field {field}") + try: + print(f"placing card {self.selectedCard} in field {field}") + # snap card into the correct field + self.selectedCard.rect.center = field.rect.center + self.selectedCard.setDragging(False) + + print(self.selectedCard) + # TODO: adapt this into the new game engine version + PlaceCard(self.__tcpClient, self.selectedCard, self.__world.getPlayer()) # tells te server that the player placed this card + self.selectedCard = None + except Exception as e: + print(f"failed to place card on server due to error: {e}") + + if self.selectedCard != None: + self.selectedCard = None + else: + raise ValueError("selected card in event handler was found empty this should never happen!") + pass + except: + pass + + # sets the running state for the gameloop + def setRunning(self, running:bool): + self.__running = running + + # ensures the gameloop stops running and the pygame instance is stopped properly + def onCleanup(self): + self.__running = False + pygame.quit() \ No newline at end of file diff --git a/Classes/System/Components/InputHandler.py b/Classes/System/Components/InputHandler.py new file mode 100644 index 0000000..df21df4 --- /dev/null +++ b/Classes/System/Components/InputHandler.py @@ -0,0 +1,53 @@ +import pygame + +from Classes.Game.BoardField import BoardField + +class InputHandler: + # returns pressed key + @staticmethod + def getPressed(): + return pygame.key.get_pressed() + + # returns pressed key + @staticmethod + def getMousePressed(): + return pygame.mouse.get_pressed() + + # takes in movement inputs and maps them to x and y axis + @staticmethod + def getInputAxis() -> tuple: + xvel = 0 + yvel = 0 + + # construct x and y velocity input axis + if InputHandler.getPressed()[pygame.K_a] or InputHandler.getPressed()[pygame.K_LEFT]: + xvel = -1 + if InputHandler.getPressed()[pygame.K_d] or InputHandler.getPressed()[pygame.K_RIGHT]: + xvel = 1 + if InputHandler.getPressed()[pygame.K_w] or InputHandler.getPressed()[pygame.K_UP]: + yvel = -1 + if InputHandler.getPressed()[pygame.K_s] or InputHandler.getPressed()[pygame.K_DOWN]: + yvel = 1 + + return tuple((xvel, yvel)) + + @staticmethod + def getMousePos() -> pygame.Vector2: + return pygame.Vector2(pygame.mouse.get_pos()) + + # get field under mousbutton + @staticmethod + def getMouseHover(mouse_pos: pygame.Vector2, world_card_width: int, world_card_height: int, board_fields: list) -> BoardField: + x_pos = mouse_pos.x / world_card_width + y_pos = mouse_pos.y / world_card_height + + for field in board_fields: + field_x = field.getPos().x + field_y = field.getPos().y + field_width = world_card_width # Annahme: Jedes Feld hat eine Breite von 1 Einheit + field_height = world_card_height # Annahme: Jedes Feld hat eine Höhe von 1 Einheit + + if field_x <= x_pos < field_x + field_width and field_y <= y_pos < field_y + field_height: + return field + + return None diff --git a/Classes/System/Components/Label.py b/Classes/System/Components/Label.py new file mode 100644 index 0000000..ed04c54 --- /dev/null +++ b/Classes/System/Components/Label.py @@ -0,0 +1,47 @@ +import pygame + +class Label: + rect:pygame.rect + image:pygame.image + __screen:pygame.surface + __text:str + __pos:pygame.Vector2 + __font:pygame.font + font:pygame.font + __name:str + + def __init__(self, name:str, screen:pygame.surface, text:str, pos:pygame.Vector2, size:float=20, color:str="white"): + self.__font = pygame.font.SysFont("Arial", size) + self.font = pygame.font.SysFont("Arial", size) + self.image = self.font.render(text, 1, color) + _, _, w, h = self.image.get_rect() + self.__pos = pos + self.rect = pygame.Rect(self.__pos.x, self.__pos.y, w, h) + self.__screen = screen + self.__text = text + self.__name = name + + def getText(self) -> str: + return self.__text + + def getFont(self) -> pygame.font: + return self.__font + + def getPos(self) -> pygame.Vector2: + return self.__pos + + def getName(self) -> str: + return self.__name + + def setText(self, newtext:str, color:str="white"): + self.image = self.font.render(newtext, 1, color) + + def setFont(self, font:pygame.font, size:float, color:str="white"): + self.__font = pygame.font.SysFont(font, size) + self.change_text(self.text, color) + + def setPos(self, pos:pygame.Vector2): + self.__pos = pos + + def draw(self): + self.__screen.blit(self.image, (self.rect)) \ No newline at end of file diff --git a/Classes/System/Components/Window.py b/Classes/System/Components/Window.py new file mode 100644 index 0000000..b4c3439 --- /dev/null +++ b/Classes/System/Components/Window.py @@ -0,0 +1,54 @@ +import pygame +from pygame.locals import * + +from Classes.Game.BoardField import BoardField +from Classes.Game.World import World + +class Window: + __width:int = 800 + __height:int = 600 # takes 80% of width which tranlates to 640 + __title:str = "python game engine" + __screen:pygame.Surface + __clock:pygame.time.Clock + + def __init__(self, width:int=800, height:int=600, title:str="python game engine"): + self.__width = width + self.__height = height + self.__title = title + + pygame.init() + + self.__screen = pygame.display.set_mode((self.__width, self.__height)) + self.__screen.fill((236, 240, 241)) # Hier liegt der Fehler, es muss eine Tuple übergeben werden + pygame.display.set_caption(self.__title) + + self.__clock = pygame.time.Clock() + self.__framerate = 60 # Framerate auf 60 FPS festlegen + + # set framerate (where the fuck is it?) + def Render(self): + # dear future me figure out what past me did! + pass + + def setWidth(self, width:int): + self.__width = width + + def setHeight(self, height:int): + self.__height = height + + def setTitle(self, title:str): + self.__title = title + + def getScreen(self) -> pygame.surface: + return self.__screen + + # draws a passed sprite group to the screen + def drawSpriteGroup(self, group:pygame.sprite.Group): + group.draw(self.__screen) + + # draws a given group of rectangles onto the screen + def drawWorld(self, world:World): + for field in world.getBoardFields(): + pygame.draw.rect(self.__screen, field.getColor(), field.getRect()) + for label in world.getLabels(): + label.draw() \ No newline at end of file diff --git a/Classes/System/GameManager.py b/Classes/System/GameManager.py index 3930a80..ea25200 100644 --- a/Classes/System/GameManager.py +++ b/Classes/System/GameManager.py @@ -1,153 +1,23 @@ -import json -import socket -import time from Classes.Game.Player import Player +from Classes.Game.World import World 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 - # ============================================================================= + player:Player + enemy:Player + world:World - # 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 + def __init__(self, world): + self.world = world - # 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" + def getPlayer(self) -> Player: + return self.player - players = list(self.__players.values()) + def getEnemy(self) -> Player: + return self.enemy - 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]) + def setPlayer(self, player:Player): + self.player = player - # 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 + def setEnemy(self, enemy:Player): + self.enemy = enemy \ No newline at end of file diff --git a/Classes/System/Logger.py b/Classes/System/Logger.py deleted file mode 100644 index b548160..0000000 --- a/Classes/System/Logger.py +++ /dev/null @@ -1,18 +0,0 @@ -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 index 6cca201..9188d33 100644 --- a/Classes/System/Network/EventHandler.py +++ b/Classes/System/Network/EventHandler.py @@ -1,32 +1,41 @@ import socket + +import pygame + +from Classes.Game.Events.PlaceCard import CardPlaced +from Classes.System.Components.InputHandler import InputHandler +from Classes.Game.World import World +from Classes.Game.Events.GameStart import GameStart from Classes.Game.Player import Player -from Classes.System.GameManager import GameManager - -from Classes.System.World import World - +from Classes.Game.Events.Login import LoginResponse class TCPEventHandler: - __tcpSocket:socket - def __init__(self, socket:socket): - self.__tcpSocket = socket + self.tcp_socket = 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"]) + def handleEvents(self, message, inputHandler:InputHandler, world:World): + print(message) + if message["event"] == "loginresponse": + LoginResponse(message, world) pass - elif event["event"] == "MoveCard": + elif message["event"] == "startgame": + print(world.player) + # world.player.setMana(message["player"]["mana"]) + # world.player.setHp(message["player"]["hp"]) + GameStart(world, message["player"]["hand"], inputHandler, world.getPlayer(), Player(message["enemy"]["hp"],0,message["enemy"]["name"], message["enemy"]["id"])) pass - elif event["event"] == "RemoveCard": + elif message["event"] == "placecard": + CardPlaced(world, message["card"], message["type"], message["owner"], pygame.Vector2(int(message["x"]), int(message["y"]), inputHandler)) + pass + elif message["event"] == "MoveCard": pass - elif event["event"] == "AttackCard": + elif message["event"] == "RemoveCard": pass - elif event["event"] == "AttackPlayer": + elif message["event"] == "AttackCard": pass - elif event["event"] == "ActivateEffectCard": + elif message["event"] == "AttackPlayer": pass - elif event["event"] == "ActivateMonsterCard": + elif message["event"] == "ActivateEffectCard": pass - pass \ No newline at end of file + elif message["event"] == "ActivateMonsterCard": + pass \ No newline at end of file diff --git a/Classes/System/Network/NetworkManger.py b/Classes/System/Network/NetworkManger.py deleted file mode 100644 index 60a6a3f..0000000 --- a/Classes/System/Network/NetworkManger.py +++ /dev/null @@ -1,137 +0,0 @@ -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/Network/TCPClient.py b/Classes/System/Network/TCPClient.py new file mode 100644 index 0000000..dcaf1b0 --- /dev/null +++ b/Classes/System/Network/TCPClient.py @@ -0,0 +1,69 @@ +import json +import socket +import threading + +import pygame + +from Classes.Game.World import World +from Classes.System.Components.InputHandler import InputHandler +from Classes.Game.Events.GameStart import GameStart +from Classes.Game.Events.Login import LoginResponse +from Classes.Game.Events.PlaceCard import CardPlaced +from Classes.Game.Player import Player + +class TCPClient: + def __init__(self, addr: str, port: str, world:World, inputHandler:InputHandler): + self.addr = addr + self.port = int(port) + self.tcpSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + self.world = world + self.inputHandler = inputHandler + + try: + self.tcpSocket.connect((self.addr, self.port)) + except Exception as e: + print(f"Error connecting TCP socket: {e}") + + # Starten des Listener-Threads + self.listen() + + def send(self, message: dict): + try: + self.tcpSocket.sendall(json.dumps(message).encode()) + except Exception as e: + print(f"Error sending TCP data: {e}") + + def receive(self): + while True: + try: + data = self.tcpSocket.recv(1024) + if data: + decoded_data = json.loads(data.decode()) + self.handleEvents(decoded_data) + except Exception as e: + print(f"Error receiving TCP data: {e}") + break + + def handleEvents(self, message): + print(message) + if message["event"] == "loginresponse": + if message["status"] != "success": + print("login failed") + else: + print("receiving login confirmation from server") + self.world.setPlayer(Player(0,0,message["name"], message["id"])) + elif message["event"] == "startgame": + print(self.world.player) + # world.player.setMana(message["player"]["mana"]) + # world.player.setHp(message["player"]["hp"]) + self.world.setEnemy(Player(message["enemy"]["hp"],0,message["enemy"]["name"], message["enemy"]["id"])) + GameStart(self.world, message["player"]["hand"], self.inputHandler, self.world.getPlayer()) + pass + elif message["event"] == "PlacedCard": + CardPlaced(self.world, message["card"], message["owner"], pygame.Vector2(message["x"], message["y"]), self.inputHandler) + pass + + def listen(self): + tcpThread = threading.Thread(target=self.receive) + tcpThread.daemon = True + tcpThread.start() \ No newline at end of file diff --git a/Classes/System/PlayerManager.py b/Classes/System/PlayerManager.py deleted file mode 100644 index e149622..0000000 --- a/Classes/System/PlayerManager.py +++ /dev/null @@ -1,23 +0,0 @@ -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 deleted file mode 100644 index be82662..0000000 --- a/Classes/System/QueueManager.py +++ /dev/null @@ -1,34 +0,0 @@ - - -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 deleted file mode 100644 index c3485db..0000000 --- a/Classes/System/Server.py +++ /dev/null @@ -1,40 +0,0 @@ -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 index a274f43..9b01eb9 100644 --- a/Classes/System/Utils/Path.py +++ b/Classes/System/Utils/Path.py @@ -3,4 +3,4 @@ import os class PathUtil: def getAbsolutePathTo(notAbsolutPath:str) -> str: - return os.path.abspath("OLD_Server/" + notAbsolutPath) \ No newline at end of file + return os.path.abspath("Client/" + notAbsolutPath) \ No newline at end of file diff --git a/Classes/System/World.py b/Classes/System/World.py deleted file mode 100644 index f6b2957..0000000 --- a/Classes/System/World.py +++ /dev/null @@ -1,20 +0,0 @@ -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 deleted file mode 100644 index 27fd861..0000000 --- a/index.py +++ /dev/null @@ -1,31 +0,0 @@ -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/main.py b/main.py new file mode 100644 index 0000000..d243f0c --- /dev/null +++ b/main.py @@ -0,0 +1,7 @@ +import pygame +from Classes.System.App import App + +if __name__ == "__main__": + pygame.init() + game = App() + game.onExecute() \ No newline at end of file