initial commit for rework

This commit is contained in:
steev 2024-01-16 23:44:13 +01:00
parent afa2d779a1
commit d6ba33874d
140 changed files with 228 additions and 1212 deletions

8
.idea/.gitignore generated vendored
View File

@ -1,8 +0,0 @@
# Default ignored files
/shelf/
/workspace.xml
# Editor-based HTTP Client requests
/httpRequests/
# Datasource local storage ignored files
/dataSources/
/dataSources.local.xml

21
.idea/Online_TCG.iml generated
View File

@ -1,21 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="PYTHON_MODULE" version="4">
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$">
<excludeFolder url="file://$MODULE_DIR$/venv" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
<component name="PyDocumentationSettings">
<option name="format" value="PLAIN" />
<option name="myDocStringFormat" value="Plain" />
</component>
<component name="TemplatesService">
<option name="TEMPLATE_FOLDERS">
<list>
<option value="$MODULE_DIR$/card files/Templates" />
</list>
</option>
</component>
</module>

View File

@ -1,6 +0,0 @@
<component name="InspectionProjectProfileManager">
<settings>
<option name="USE_PROJECT_PROFILE" value="false" />
<version value="1.0" />
</settings>
</component>

7
.idea/misc.xml generated
View File

@ -1,7 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Black">
<option name="sdkName" value="Python 3.11 (Online_TCG)" />
</component>
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.11 (Online_TCG)" project-jdk-type="Python SDK" />
</project>

8
.idea/modules.xml generated
View File

@ -1,8 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/Online_TCG.iml" filepath="$PROJECT_DIR$/.idea/Online_TCG.iml" />
</modules>
</component>
</project>

6
.idea/vcs.xml generated
View File

@ -1,6 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="" vcs="Git" />
</component>
</project>

16
.vscode/launch.json vendored
View File

@ -1,16 +0,0 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "Python:app",
"type": "python",
"request": "launch",
"program": "Game Client/main.py",
"console": "integratedTerminal",
"justMyCode": true
}
]
}

View File

@ -1,5 +0,0 @@
{
"cSpell.ignoreWords": [
"posx"
]
}

66
Game Client/App.py Normal file
View File

@ -0,0 +1,66 @@
import pygame
from Classes.Engine.EventHandler import EngineEventHandler
from Classes.Engine.InputHandler import InputHandler
from Classes.Engine.Logger import Logger
from Classes.Engine.TCPClient import TCPClient
from Classes.Engine.Window import Window
from Classes.Game.World import World
class App:
__running:bool
__FPS:int = 60
__clock:pygame.time.Clock()
__myFont:pygame.font
__window:Window
__world:World
__inputHandler: InputHandler
__tcpClient: TCPClient
__eventHandler: EngineEventHandler
logger:Logger
def __init__(self, logger:Logger, width:int=1280, height:int=720, title:str="default title"):
self.logger = logger
self.logger.info("initializing dependencies")
pygame.init()
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.__eventHandler = EngineEventHandler(self.__inputHandler, self.__world, self.logger, self.__tcpClient)
pass
def startGameLoop(self):
self.__running = True
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())
self.__eventHandler.handleEvent(pygame.event.get())
# emits update to the game
pygame.display.update()
self.__running = False
# ensures the gameloop stops running and the pygame instance is stopped properly
def onCleanup(self):
self.__running = False
pygame.quit()

View File

@ -24,7 +24,7 @@ class BoardField(pygame.sprite.Sprite):
# Überprüfen des Dateipfads
if not os.path.exists(imagePath):
print("Der Dateipfad zur Bilddatei ist ungültig oder die Datei existiert nicht.")
print("could not find image Location.")
else:
# Wenn der Pfad gültig ist, versuchen Sie, das Bild zu laden
self.image = pygame.image.load(imagePath).convert_alpha()

View File

@ -0,0 +1,105 @@
import socket
import pygame
from Classes.Engine.Logger import Logger
from Classes.Engine.TCPClient import TCPClient
from Classes.Game.NetworkEvents.PlaceCard import CardPlaced
from Classes.Game.NetworkEvents.GameStart import GameStart
from Classes.Game.NetworkEvents.PlaceCard import PlaceCard
from Classes.Engine.InputHandler import InputHandler
from Classes.Game.World import World
from Classes.Game.Player import Player
# network event handler this only handles events coming from the server
class TCPEventHandler:
def __init__(self, socket:socket, inputHandler:InputHandler, world:World):
self.tcp_socket = socket
self.__world = world
self.__inputHandler = inputHandler
def handleEvents(self, message):
if message["event"] == "login":
pass
elif message["event"] == "startgame":
pass
elif message["event"] == "PlaceCard":
pass
elif message["event"] == "MoveCard":
pass
elif message["event"] == "RemoveCard":
pass
elif message["event"] == "AttackCard":
pass
elif message["event"] == "AttackPlayer":
pass
elif message["event"] == "ActivateEffectCard":
pass
elif message["event"] == "ActivateMonsterCard":
pass
# handles engine events this is separate from the network event handler
class EngineEventHandler:
__inputHandler:InputHandler
__world:World
__logger:Logger
__tcpClient:TCPClient
def __init__(self, inputHandler:InputHandler, world:World, logger:Logger, tcpClient:TCPClient):
self.__logger.info("initializing engine Event Handler")
if inputHandler == None:
raise ValueError("InputHandler was found to be None")
if world == None:
raise ValueError("world was found to be None")
if world == tcpClient:
raise ValueError("tcpCLient was found to be None")
self.__inputHandler = inputHandler
self.__world = world
self.__tcpClient = tcpClient
# handles the incoming event queue
def handleEvent(self, events:list[pygame.Event]):
# TODO: fix bug that stacks cards when dragging them around
self.selectedCard = None
self.mousePos = self.__inputHandler.getMousePos()
for event in events:
if event.type == pygame.QUIT:
self.onCleanup()
elif self.__inputHandler.getMousePos()[0]:
for card in self.__world.getCards():
if card.rect.collidepoint(self.mousePos):
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:
if event.button == 1:
if self.selectedCard != None:
for field in self.__world.getBoardFields():
if field.getRect().collidepoint(self.mousePos):
if field.getSide() == "Player" and field.getType() == self.selectedCard.getType():
try:
# snap card into the correct field
self.selectedCard.rect.center = field.rect.center
self.selectedCard.setDragging(False)
print(self.selectedCard)
if self.selectedCard == card:
# TODO: adapt this into the new game engine version
PlaceCard(self.__tcpClient, self.selectedCard) # 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

View File

@ -8,6 +8,11 @@ class InputHandler:
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:

View File

@ -2,9 +2,9 @@ import json
import socket
import threading
from Classes.System.Network.EventHandler import TCPEventHandler
from Classes.Engine.EventHandler import TCPEventHandler
from Classes.Game.World import World
from Classes.System.Components.InputHandler import InputHandler
from Classes.Engine.InputHandler import InputHandler
class TCPClient:
def __init__(self, addr: str, port: str, world:World, inputHandler:InputHandler):

View File

@ -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

View File

@ -1,7 +1,7 @@
import json
import pygame
from Classes.System.Components.InputHandler import InputHandler
from Classes.Engine.InputHandler import InputHandler
from Classes.Game.Player import Player
class MonsterCard(pygame.sprite.Sprite):
@ -9,7 +9,7 @@ class MonsterCard(pygame.sprite.Sprite):
__id:int
__description:str
__attacks = []
__type:str = "MonsterCard"
__type:str = "Monster"
__pos:pygame.Vector2
__dragging:bool = False
__offset:pygame.Vector2 = pygame.Vector2(0,0)

View File

@ -3,7 +3,7 @@ from typing import Any
import pygame
from Classes.System.Components.InputHandler import InputHandler
from Classes.Engine.InputHandler import InputHandler
class SpellCard(pygame.sprite.Sprite):
__name:str
@ -12,7 +12,7 @@ class SpellCard(pygame.sprite.Sprite):
__dragging:bool = False
__offset:pygame.Vector2 = pygame.Vector2(0,0)
__inputHandler: InputHandler
__type:str = "SpellCard"
__type:str = "Effect"
image:pygame.image
rect:pygame.rect

View File

@ -3,7 +3,7 @@ from typing import Any
import pygame
from Classes.System.Components.InputHandler import InputHandler
from Classes.Engine.InputHandler import InputHandler
class TrapCard(pygame.sprite.Sprite):
__name:str
@ -12,7 +12,7 @@ class TrapCard(pygame.sprite.Sprite):
__dragging:bool = False
__offset:pygame.Vector2 = pygame.Vector2(0,0)
__inputHandler: InputHandler
__type:str = "TrapCard"
__type:str = "Effect"
image:pygame.image
rect:pygame.rect

View File

@ -2,7 +2,7 @@ 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.Engine.InputHandler import InputHandler
# send from the server to tell the player the game starts

View File

@ -1,6 +1,6 @@
import json
import pygame
from Classes.System.Network.TCPClient import TCPClient
from Classes.Engine.TCPClient import TCPClient
from Classes.Game.World import World
from Classes.Game.Player import Player

View File

@ -1,6 +1,6 @@
import pygame
from Classes.Game.World import World
from Classes.System.Components.InputHandler import InputHandler
from Classes.Engine.InputHandler import InputHandler
# the event the client sends to the server when it places a card
def PlaceCard(tcpClient, card):

View File

@ -1,10 +1,11 @@
import pygame
from Classes.Game.BoardField import BoardField
from Classes.System.Components.Label import Label
from Classes.Engine.Logger import Logger
from Classes.Engine.Components.BoardField import BoardField
from Classes.Engine.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.Engine.InputHandler import InputHandler
from Classes.Game.Player import Player
from Classes.Game.Cards.Card import Card
@ -19,8 +20,12 @@ class World():
__cardWidth:int = 150
__cardHeight:int = 200
__cardOffset:int = 400
__logger:Logger
def __init__(self, screen:pygame.surface, cardWidth:int=200, cardHeight:int=250, cardOffset:int=400):
def __init__(self, logger:Logger, screen:pygame.surface, cardWidth:int=200, cardHeight:int=250, cardOffset:int=400):
self.__logger = logger
self.__logger.info("initializing world")
self.__boardFields = []
self.__labels = []
self.__cards = pygame.sprite.Group()
@ -35,7 +40,7 @@ class World():
def buildGameWorld(self):
# construct elements arround the playerfield
# Todo add lifepoint label for player and enemy and make them scriptable move to player class
self.__logger.info("building game world")
# presets the y position later passed down to the vector2
eRow1Height = 85
@ -47,22 +52,19 @@ class World():
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)
self.__labels.append(Label("EnemyHP", self.__screen, "1000 / 1000", eHPPos))
self.__labels.append(Label("EnemyName", self.__screen, "Enemy", eNamePos))
self.__boardFields.append(BoardField("EnemyDeck", "Enemy", "Deck", eDeckPos, "Assets/Cards/0/field.png", "e-deck"))
self.__boardFields.append(BoardField("EnemyGraveyard", "Enemy", "Grave", eGravePos, "Assets/Cards/0/field.png", "e-grave"))
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, "Player", 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, "Enemy", eNamePos))
self.__boardFields.append(BoardField("EnemyDeck", "Enemy", "Deck", eDeckPos, "Assets/Cards/0/field.png", "e-deck"))
self.__boardFields.append(BoardField("EnemyGraveyard", "Enemy", "Grave", eGravePos, "Assets/Cards/0/field.png", "e-grave"))
self.__boardFields.append(BoardField("PlayerDeck", "Player", "Deck", pDeckPos, "Assets/Cards/0/field.png", "P-deck"))
self.__boardFields.append(BoardField("PlayerGraveyard", "Player", "Grave", pGravePos, "Assets/Cards/0/field.png", "p-grave"))
@ -70,13 +72,13 @@ class World():
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)
self.__boardFields.append(BoardField("PlayerMonsterField-"+str(i), "Player", "Monster", pMonsterPos, "Assets/Cards/0/field.png", "pm"+str(id)))
self.__boardFields.append(BoardField("PlayerEffectField-"+str(i), "Player", "Effect", pEffectPos, "Assets/Cards/0/field.png", "pe"+str(id)))
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", "MonsterField", pMonsterPos, "Assets/Cards/0/field.png", "pm"+str(id)))
self.__boardFields.append(BoardField("PlayerEffectField-"+str(i), "Player", "EffectField", pEffectPos, "Assets/Cards/0/field.png", "pe"+str(id)))
self.__boardFields.append(BoardField("EnemyMonsterField-"+str(i), "Enemy", "MonsterField", eMonsterPos, "Assets/Cards/0/field.png", "em"+str(id)))
self.__boardFields.append(BoardField("EnemySpellTrapField-"+str(i), "Enemy", "EffectField", eEffectPos, "Assets/Cards/0/field.png", "ee"+str(id)))
self.__boardFields.append(BoardField("EnemyMonsterField-"+str(i), "Enemy", "Monster", eMonsterPos, "Assets/Cards/0/field.png", "em"+str(id)))
self.__boardFields.append(BoardField("EnemySpellTrapField-"+str(i), "Enemy", "Effect", eEffectPos, "Assets/Cards/0/field.png", "ee"+str(id)))
def getBoardFields(self) -> list:
return self.__boardFields

9
Game Client/main.py Normal file
View File

@ -0,0 +1,9 @@
from App import App
from Classes.Engine.Logger import Logger
from Classes.Engine.Utils.StringUtils import StringUtils
if __name__ == "__main__":
logger = Logger("log/"+StringUtils.get_random_string(8)+".log")
logger.info("starting game engine")
game = App(logger)

View File

@ -1,4 +0,0 @@
HOST="127.0.0.1"
TCPPORT=54322
UDPPORT=54323
ENV="DEV"

View File

@ -1,14 +0,0 @@
{
"cSpell.words": [
"DGRAM"
],
"cSpell.ignoreWords": [
"activateeffectcard",
"activatemonstereffect",
"attackcard",
"attackplayer",
"movecard",
"placecard",
"removecard"
]
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 612 B

View File

@ -1,22 +0,0 @@
{
"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
}
]
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.3 KiB

View File

@ -1,6 +0,0 @@
{
"name": "testspell",
"image":"Assets/Cards/testspelltcard/artwork.png",
"costs": 30,
"description":"this is a test spell card"
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.3 KiB

View File

@ -1,6 +0,0 @@
{
"name": "testtrap",
"image":"Assets/Cards/testtrapcard/artwork.png",
"costs": 30,
"description":"this is a test tryp card"
}

View File

@ -1,56 +0,0 @@
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.__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

View File

@ -1,103 +0,0 @@
import json
import socket
from Classes.Game.Player import Player
class GameManager:
__players:dict
__playingPlayer:Player
__state:str
__round:str
def __init__(self, logger):
self.__players = {}
self.__playingPlayer = None
self.__state = "waiting"
self.__round = "none"
self.logger = logger
pass
def getLogger(self):
return self.logger
# 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, tcpSocket:socket):
self.__state = "running"
print("game starts")
for userAddr in self.__users.keys():
try:
user = self.__serverWorld.getPlayers[userAddr]["player"]
user.addMana(1000)
user.adjustHP(1000)
user.shuffleDeck()
cards = user.getDeck()
user.setHand(cards[:5])
payload = {
"event":"startgame",
"playermana": user.getMana(),
"playerhp": user.getHP(),
"playername": user.getName(),
"hand": user.getHand()
}
tcpSocket.send(json.dumps(payload).encode())
except Exception as e:
self.logger.error(f"failed to start game due to error: {e}")
break
# 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) -> dict:
self.logger.info(f"creating user with id: {player.getID}")
self.__players[clientAddr] = {
"player": player,
"socket":socket
}
self.logger.info(f"new length of user dictionary: {len(self.__players)}")
# counts participating players and starts the game if enough have joined
if len(self.__players) == 2:
self.logger.info("2 players have join game starts")
self.startGame(socket)
return self.__players

View File

@ -1,31 +0,0 @@
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":
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

View File

@ -1,140 +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)
self.__gameManager.getLogger().info(f"connected users {len(self.__gameManager.getPlayers())}")
self.__gameManager.getLogger().info(f"confirming login for user")
self.send({
"event":"loginresponse",
"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.")
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)

View File

@ -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

View File

@ -1,32 +0,0 @@
from Classes.Objects.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

View File

@ -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

View File

@ -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

View File

@ -1,30 +0,0 @@
import logging
import os
import random
import string
import sys
from Classes.System.Server import Server
from Classes.System.Logger import Logger
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("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()

View File

@ -1,29 +0,0 @@
19:08:13,964 root INFO starting up server
19:08:13,964 root INFO starting up game manager
19:08:13,965 root INFO preparing to start server
19:08:13,965 root INFO starting up network manager
19:08:13,965 root INFO starting up network manager
19:08:13,965 root INFO starting up tcp server
19:08:13,967 root INFO starting up thread for client socket accepting
19:08:18,567 root INFO Connected with ('127.0.0.1', 54324)
19:08:18,567 root INFO starting client handler thread for client at address ('127.0.0.1', 54324)
19:08:18,580 root INFO decoded message {'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]}
19:08:18,588 root INFO user in message None
19:08:18,588 root INFO user logging in
19:08:18,589 root INFO task passed off to gameManager
19:08:18,589 root INFO creating user with id: <bound method Player.getID of <Classes.Game.Player.Player object at 0x000001E4CF425BD0>>
19:08:18,589 root INFO new length of user dictionary: 1
19:08:18,589 root INFO connected users 1
19:08:18,589 root INFO confirming login for user
19:08:18,590 root INFO incommingevent {'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]}
19:08:18,590 root INFO Received message from ('127.0.0.1', 54324): {"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]}
19:10:33,867 root ERROR Connection with ('127.0.0.1', 54324) forcibly closed by remote host.
19:10:36,58 root INFO Connected with ('127.0.0.1', 54355)
19:10:36,58 root INFO starting client handler thread for client at address ('127.0.0.1', 54355)
19:10:36,60 root INFO decoded message {'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]}
19:10:36,60 root INFO user in message None
19:10:36,60 root INFO user logging in
19:10:36,62 root INFO task passed off to gameManager
19:10:36,63 root INFO creating user with id: <bound method Player.getID of <Classes.Game.Player.Player object at 0x000001E4CF425C10>>
19:10:36,63 root INFO new length of user dictionary: 2
19:10:36,63 root INFO 2 players have join game starts

View File

@ -1,42 +0,0 @@
# 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

View File

@ -1,15 +0,0 @@
{
"cSpell.words": [
"pygame",
"yvel"
],
"cSpell.ignoreWords": [
"activateeffectcard",
"activatemonstereffect",
"attackcard",
"attackplayer",
"movecard",
"placecard",
"removecard"
]
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 612 B

View File

@ -1,23 +0,0 @@
{
"id": 1,
"type":"MonsterCard",
"name": "Test Monster",
"image": "Assets/Cards/1/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
}
]
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.3 KiB

View File

@ -1,9 +0,0 @@
{
"name": "testspell",
"type":"EffectCard",
"image":"Assets/Cards/2/artwork.png",
"costs": 30,
"defense": 0,
"description":"this is a test spell card",
"attacks": []
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.3 KiB

View File

@ -1,9 +0,0 @@
{
"name": "testtrap",
"type":"EffectCard",
"image":"Assets/Cards/3/artwork.png",
"costs": 30,
"defense": 0,
"description":"this is a test trap card",
"attacks": []
}

View File

@ -1,139 +0,0 @@
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
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()
# 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("Assets/Cards/1/", pygame.Vector2(500, 1050), self.__inputHandler, Player(1000, 0, "test"))
self.__world.spawnCard("Assets/Cards/1/", pygame.Vector2(600, 1050), self.__inputHandler, Player(1000, 0, "test"))
self.__world.spawnCard("Assets/Cards/1/", pygame.Vector2(700, 1050), self.__inputHandler, Player(1000, 0, "test"))
self.__world.spawnCard("Assets/Cards/1/", pygame.Vector2(800, 1050), self.__inputHandler, Player(1000, 0, "test"))
self.__world.spawnCard("Assets/Cards/1/", pygame.Vector2(900, 1050), self.__inputHandler, Player(1000, 0, "test"))
self.__world.spawnCard("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
self.selectedCard = None
for event in events:
if event.type == pygame.QUIT:
self.onCleanup()
elif pygame.mouse.get_pressed()[0]: # Wenn linke Maustaste gedrückt wird
mouse_x, mouse_y = pygame.mouse.get_pos()
mouse_pos = pygame.Vector2(mouse_x, mouse_y)
for card in self.__world.getCards():
if card.rect.collidepoint(mouse_pos) and self.selectedCard == None:
card.setDragging(True)
self.selectedCard = card
for field in self.__world.getBoardFields():
if field.getRect().collidepoint(mouse_pos):
if field.getSide() == "Player":
if field.getType() == "MonsterField" and card.getType() == "MonsterCard":
# todo: resize card so that it fits into the card field
card.rect.center = field.rect.center
field.image = card.image.copy()
card.setDragging(False)
elif field.getType() == "EffectField" and card.getType() == "EffectCard" or field.getType() == "EffectField" and card.getType() == "EffectCard":
# todo: resize card so that it fits into the card field
card.rect.center = field.rect.center
field.image = card.image.copy()
card.setDragging(False)
elif self.__inputHandler.getPressed()[pygame.K_SPACE]:
print("card spawned")
self.__world.spawnMonsterCard("Assets/Cards/MonsterCards/testmonstercard/", self.__inputHandler.getMousePos(), self.__inputHandler)
elif event.type == pygame.MOUSEBUTTONUP:
mouse_x, mouse_y = pygame.mouse.get_pos()
mouse_pos = pygame.Vector2(mouse_x, mouse_y)
if event.button == 1: # Wenn linke Maustaste losgelassen wird
for card in self.__world.getCards():
card.setDragging(False)
# card.setState("placed")
# TODO: send place card event to server
# resets the currently selected card in order to prevent it getting moved
try:
print(self.selectedCard)
if self.selectedCard == card:
PlaceCard(self.__tcpClient, card) # tells the server that the player placed this card
except Exception as e:
print(f"failed to place card on server due to error: {e}")
if not card == None:
card = None
# 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()

Some files were not shown because too many files have changed in this diff Show More