Compare commits

..

7 Commits

167 changed files with 321 additions and 1165 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>

23
.vscode/launch.json vendored
View File

@@ -1,14 +1,27 @@
{
// 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",
"name": "Old Shadowcrest Engine Server",
"type": "python",
"request": "launch",
"program": "main.py",
"program": "OLD_Server/index.py",
"console": "integratedTerminal",
"justMyCode": true
},
{
"name": "Shadowcrest Engine Server",
"type": "python",
"request": "launch",
"program": "OLD_Server/index.py",
"console": "integratedTerminal",
"justMyCode": true
},
{
"name": "ShadowCrest Python GameEngine Client",
"type": "python",
"request": "launch",
"program": "Client/main.py",
"console": "integratedTerminal",
"justMyCode": true
}

View File

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

View File

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 11 KiB

View File

Before

Width:  |  Height:  |  Size: 612 B

After

Width:  |  Height:  |  Size: 612 B

View File

Before

Width:  |  Height:  |  Size: 9.3 KiB

After

Width:  |  Height:  |  Size: 9.3 KiB

View File

Before

Width:  |  Height:  |  Size: 2.3 KiB

After

Width:  |  Height:  |  Size: 2.3 KiB

View File

Before

Width:  |  Height:  |  Size: 2.3 KiB

After

Width:  |  Height:  |  Size: 2.3 KiB

View File

@@ -9,17 +9,18 @@ class Card(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)
__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):
def __init__(self, pos: pygame.Vector2, assetDir: str, inputHandler: InputHandler, owner: Player, dragable:bool=True):
if assetDir == "":
raise ValueError("Card: imagePath cannot be empty")
@@ -41,6 +42,7 @@ class Card(pygame.sprite.Sprite):
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", []):
@@ -48,9 +50,10 @@ class Card(pygame.sprite.Sprite):
def update(self):
if self.__dragging:
mouse_pos = self.__inputHandler.getMousePos()
self.__pos = mouse_pos
self.rect.center = self.__pos
if self.__dragable:
mouse_pos = self.__inputHandler.getMousePos()
self.__pos = mouse_pos
self.rect.center = self.__pos
def attacks(self):
return self.__attacks
@@ -67,12 +70,21 @@ class Card(pygame.sprite.Sprite):
def getDragging(self):
return self.__dragging
def getDragable(self):
return self.__dragable
def getOffset(self):
return self.__offset
def getPos(self):
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

View File

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

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

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

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

View File

@@ -16,8 +16,10 @@ def Login(tcpClient):
# 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:
world.setPlayer(Player(0,0, message["username"], message["id"]))
print("receiving login confirmation from server")
world.setPlayer(Player(0,0,message["name"], message["id"]))

View File

@@ -1,9 +1,11 @@
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, id):
def PlaceCard(tcpClient, card:Card, player):
# todo: send card information to the server
# todo: required info is:
# - position
@@ -13,25 +15,18 @@ def PlaceCard(tcpClient, card, id):
"event":"placecard",
"card": card.getID(),
"type": card.getType(),
"user": id,
"x": card.getPos().x,
"y": card.getPos().y,
"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, type:str, owner:str, pos:pygame.Vector2, inputHandler:InputHandler):
if type == "MonsterCard":
world.spawnMonsterCard(f"Assets/Cards/{card}/", pos, inputHandler, owner)
pass
elif type == "SpellCard":
world.spawnSpellCard(f"Assets/Cards/{card}/", pos, inputHandler, owner)
pass
elif type == "TrapCard":
world.spawnTrapCard(f"Assets/Cards/{card}/", pos, inputHandler, owner)
pass
pass
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

View File

@@ -7,7 +7,7 @@ class Player:
__name:str
__handCards:pygame.sprite.Group
def __init__(self, hp:int, mana:int, name:str, id:int):
def __init__(self, hp:int, mana:int, name:str, id:int=0):
self.__hp = hp
self.__mana = mana
self.__name = name
@@ -42,3 +42,5 @@ class Player:
self.__handCards.remove(pos)
return self.__handCards
def setMana(self, mana:int):
self.__mana = mana

View File

@@ -7,15 +7,16 @@ 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
player:Player
enemy:Player
__labels:list
__cards:pygame.sprite.Group()
__PlayerHandCards:pygame.sprite.Group()
__screen:pygame.surface
screen:pygame.surface
__cardWidth:int = 150
__cardHeight:int = 200
__cardOffset:int = 400
@@ -29,13 +30,12 @@ class World():
self.__cardWidth = cardWidth
self.__cardHeight = cardHeight
self.__cardOffset = cardOffset
self.__player = None
self.__enemy = None
self.player = None
self.enemy = None
self.buildGameWorld()
def buildGameWorld(self):
# construct elements arround the playerfield
# Todo add lifepoint label for player and enemy and make them scriptable move to player class
# presets the y position later passed down to the vector2
eRow1Height = 85
@@ -55,16 +55,16 @@ class World():
pManaPos = pygame.Vector2(20, pRow2Height + 255)
# labeling
self.__labels.append(Label("PlayerHP", self.__screen, "0 / 0", pHPPos))
self.__labels.append(Label("PlayerHP", self.__screen, "1000 / 1000", pHPPos))
self.__labels.append(Label("PlayerName", self.__screen, "Not Connected", pNamePos))
self.__labels.append(Label("PlayerMana", self.__screen, "0", pManaPos))
self.__labels.append(Label("EnemyHP", self.__screen, "0 / 0", eHPPos))
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, "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"))
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):
@@ -73,10 +73,10 @@ class World():
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("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
@@ -97,28 +97,38 @@ class World():
return self.__PlayerHandCards
def getPlayer(self) -> Player:
return 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())
self.__player = player
def setEnemy(self, player:Player):
self.enemy = player
for label in self.__labels:
if label.getName() == "EnemyName":
label.setText(player.getName())
self.__enemy = player
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)

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

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

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

@@ -0,0 +1,23 @@
from Classes.Game.Player import Player
from Classes.Game.World import World
class GameManager:
player:Player
enemy:Player
world:World
def __init__(self, world):
self.world = world
def getPlayer(self) -> Player:
return self.player
def getEnemy(self) -> Player:
return self.enemy
def setPlayer(self, player:Player):
self.player = player
def setEnemy(self, enemy:Player):
self.enemy = enemy

View File

@@ -13,27 +13,21 @@ class TCPEventHandler:
def __init__(self, socket:socket):
self.tcp_socket = socket
def handleEvents(self, message:dict, inputHandler:InputHandler, world:World):
def handleEvents(self, message, inputHandler:InputHandler, world:World):
print(message)
if message["event"] == "loginresponse":
# todo: handle login response here
LoginResponse(message, world)
pass
elif message["event"] == "startgame":
print("gamestart")
GameStart(world, message["hand"], inputHandler, world.getPlayer())
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 message["event"] == "PlaceCard":
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":
# CardMoved(
# world,
# message["card"],
# message["type"],
# message["owner"],
# pygame.Vector2(int(message["old_x"]), int(message["old_y"])),
# pygame.Vector2(int(message["new_x"]), int(message["new_y"])),
# inputHandler, world)
pass
elif message["event"] == "RemoveCard":
pass

View File

@@ -2,16 +2,20 @@ import json
import socket
import threading
from Classes.System.Network.EventHandler import TCPEventHandler
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.eventHandler = TCPEventHandler(self.tcpSocket)
self.world = world
self.inputHandler = inputHandler
@@ -35,11 +39,30 @@ class TCPClient:
data = self.tcpSocket.recv(1024)
if data:
decoded_data = json.loads(data.decode())
self.eventHandler.handleEvents(decoded_data, self.inputHandler, self.world)
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

View File

@@ -0,0 +1,6 @@
import os
class PathUtil:
def getAbsolutePathTo(notAbsolutPath:str) -> str:
return os.path.abspath("Client/" + notAbsolutPath)

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

Binary file not shown.

Binary file not shown.

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"
]
}

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
}
]
}

View File

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

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)
self.__mana = mana
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,117 +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"
players = list(self.__players.values())
print("game starts")
self.logger.info("game manager is starting the game")
for userAddr, player_data in self.__players.items():
try:
user = player_data["player"]
user.addMana(1000)
user.adjustHP(1000)
user.shuffleDeck()
cards = user.getDeck()
user.setHand(cards[:5])
# iterates until the enemy player is not anymore equal to current player
enemy = next(player_data["player"] for player_data in players if player_data["player"] != user)
payload = {
"event": "startgame",
"player": {
"mana": user.getMana(),
"hp": user.getHP(),
"hand": user.getHand()
},
"enemy": {
"id": enemy.getID(),
"name": enemy.getName(),
"hp": enemy.getHP(),
},
}
tcpSocket.send(json.dumps(payload).encode())
except Exception as e:
self.logger.error(f"failed to start game due to error: {e}")
break
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
def removePlayers(self, clientAddr):
self.logger.info(f"removing player with address '{clientAddr}' from players database")
del self.__players[clientAddr]

View File

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

View File

@@ -1,45 +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":
gameManager.getLogger().info(f"player {event['user']} attempted to place card {event['card']}")
for playerKey in gameManager.getPlayers().keys():
player = gameManager.getPlayers()[playerKey]
if int(event["user"]) != player["player"].getID():
payload = {
"event":"cardPlaced",
"card": {
"card":event["card"],
"owner": event["user"],
"x": event["x"],
"y": event["y"]
}
}
player["socket"].send(payload)
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,145 +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",
"status": "success",
"username": user[client_address]["player"].getName(),
"id": user[client_address]["player"].getID(),
}, client_address)
self.__eventHandler[client_address].handleTCPEvents(messageJson, self.__gameManager, client_address)
self.__gameManager.getLogger().info(f"Received message from {client_address}: {message}")
except socket.error as e:
if e.errno == 10054:
self.__gameManager.getLogger().error(f"Connection with {client_address} forcibly closed by remote host.")
players = self.__gameManager.getPlayers()
self.__gameManager.removePlayers(client_address)
self.__gameManager.getLogger().info(f"new player length {len(players)}")
break
self.__gameManager.getLogger().error(f"Socket error receiving data from {client_address}: {e}")
except json.JSONDecodeError as e:
self.__gameManager.getLogger().error(f"JSON decoding error receiving data from {client_address}: {e}")
# except Exception as e:
# self.__gameManager.getLogger().error(f"UknownError receiving data from {client_address} due to error: {e}")
def broadcast(self, payload:dict):
for user in self.__gameManager.getPlayers().values():
user["socket"].send(json.dumps(payload).encode())
def send(self, payload: dict, user: str):
players = self.__gameManager.getPlayers()
if user in players and "socket" in players[user]:
players[user]["socket"].send(json.dumps(payload).encode())
else:
self.__gameManager.getLogger().error(f"user '{user}' or socket was not found 'socket' failed to send data.")
def stop(self):
self.__TCPClientThread.join() # Wait for the thread to finish before exiting
tcp: TCP
# udp: UDP
def __init__(self, Addr:str, TCPPort:str, UDPPort:str, gameManager:GameManager):
self.tcp = self.TCP(Addr, TCPPort, gameManager)
signal.signal(signal.SIGINT, self.handle_interrupt) # Register the signal handler
def handle_interrupt(self, signum, frame):
self.__gameManager.getLogger().info("Received keyboard interrupt. Stopping the server.")
self.tcp().stop()
sys.exit(0)

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

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