reworked client netcode

This commit is contained in:
steev 2024-01-04 01:28:28 +01:00
parent 2eddf27f88
commit ccb507369d
26 changed files with 401 additions and 112 deletions

14
Game Server/.vscode/settings.json vendored Normal file
View File

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

View File

@ -0,0 +1,34 @@
import socket
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):
pass
class UDPEventHandler:
__udpSocket:socket
def __init__(self, socket:socket):
self.__udpSocket = socket
def handleUDPEvents(self, 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

View File

@ -0,0 +1,102 @@
import json
import socket
import threading
from Classes.System.Network.Handler.EventHandler import UDPEventHandler, TCPEventHandler
class NetworkManager:
class UDP:
__Addr:str
__Port:str
__BufferSize:int = 1024
__udpSocket:socket
__UDPClientThread:threading.Thread
__eventHandler: UDPEventHandler
def __init__(self, Addr:str, Port:str):
clientUdpSocket, clientUdpAddress = self.__udpSocket.accept()
self.__Addr = Addr
self.__Port = Port
self.__eventHandler = UDPEventHandler(clientUdpSocket)
self.__UDPClientThread = threading.Thread(target=self.handleUDPConnection, args=(clientUdpSocket, clientUdpAddress))
self.__UDPClientThread.start()
# handles ticking the game loop server side converting data and passing of to the event handler
def handleUDPConnection(self, socket:socket, address):
# states that a connection has been established
print(f"Connected with {address}")
# Communication with client
while True:
data = socket.recv(self.__bufferSize)
if not data:
break
# decode message for handling
message = data.decode()
messageJson = json.loads(message)
if messageJson["user"] in self.__users:
self.__eventHandler.handleUDPEvents(messageJson)
else:
break
print(f"received message from {address}: {message}")
def send(payload:bytes):
# todo: lookup how to send data
pass
class TCP:
__Addr:str
__Port:str
__BufferSize:int = 1024
__tcpSocket:socket
__TCPClientThread:threading.Thread
__eventHandler:TCPEventHandler
def __init__(self, Addr:str, Port:str):
clientTcpSocket, clientTcpAddress = self.__tcpSocket.accept()
self.__Addr = Addr
self.__Port = Port
self.__eventHandler = TCPEventHandler(clientTcpSocket)
self.__TCPClientThread = threading.Thread(target=self.handleTCPConnection, args=(clientTcpSocket, clientTcpAddress))
self.__TCPClientThread.start()
# handles ticking the game loop server side converting data and passing of to the event handler
def handleTCPConnection(self, socket:socket, address):
# states that a connection has been established
print(f"Connected with {address}")
# Communication with client
while True:
data = socket.recv(1024)
if not data:
break
# decode message for handling
message = data.decode()
messageJson = json.loads(message)
if messageJson["user"] in self.__users:
self.__eventHandler.handleTCPEvents(messageJson)
else:
break
print(f"received message from {address}: {message}")
def send(payload:bytes):
# todo: lookup how to send data
pass
tcp: TCP
udp: UDP
def __init__(self, Addr:str, TCPPort:str, UDPPort:str):
self.tcp = self.TCP(Addr, TCPPort)
self.udp = self.UDP(Addr, UDPPort)

View File

@ -2,123 +2,24 @@ import json
import socket
import threading
from Classes.System.Network.NetworkManger import NetworkManager
class Server:
__address:str
__tcpPort:str
__udpPort:str
__tcpSocket:socket
__udpSocket:socket
__TCPclientThread:threading.Thread
__UDPclientThread:threading.Thread
networkManager:NetworkManager
def __init__(self, address:str, tcpPort:str, udpPort:str):
self.__address = address
self.__tcpPort = tcpPort
self.__udpPort = udpPort
self.__tcpSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.__udpSocket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
self.__UDPClientThread = None
self.__TCPClientThread = None
self.startServer()
# handles starting the server and assigning socket values to the local reference
def startServer(self):
self.__tcpSocket.bind(self.__address, self.__tcpPort)
self.__tcpSocket.listen()
print(f"tcp server started on: {self.__address}:{self.__tcpPort}")
self.__udpSocket.bind(self.__address, self.__udpPort)
self.__udpSocket.listen()
print(f"tcp server started on: {self.__address}:{self.__udpPort}")
# server loop forwards connection to handleConnection
while True:
# accept incoming connection
# TODO: validate this connection is a valid game connection
clientTcpSocket, clientTcpAddress = self.__tcpSocket.accept()
# create network thread for connection
self.__TCPClientThread = threading.Thread(target=self.handleTCPConnection, args=(clientTcpSocket, clientTcpAddress))
self.__TCPClientThread.start()
clientUdpSocket, clientUdpAddress = self.__udpSocket.accept()
self.__UDPClientThread = threading.Thread(target=self.handleUDPConnection, args=(clientUdpSocket, clientUdpAddress))
self.__UDPClientThread.start()
# handles ticking the game loop server side converting data and passing of to the event handler
def handleTCPConnection(self, socket:socket, address):
# states that a connection has been established
print(f"Connected with {address}")
# Communication with client
while True:
data = socket.recv(1024)
if not data:
break
# decode message for handling
message = data.decode()
messageJson = json.loads(message)
if messageJson["user"] in self.__users:
self.handleTCPEvents(messageJson)
else:
break
print(f"received message from {address}: {message}")
# handles ticking the game loop server side converting data and passing of to the event handler
def handleUDPConnection(self, socket:socket, address):
# states that a connection has been established
print(f"Connected with {address}")
# Communication with client
while True:
data = socket.recv(1024)
if not data:
break
# decode message for handling
message = data.decode()
messageJson = json.loads(message)
if messageJson["user"] in self.__users:
self.handleUDPEvents(messageJson)
else:
break
print(f"received message from {address}: {message}")
# handles passing of event data to the right functions
def handleTCPEvents(self, event):
# decide which event should be performed
if event["event"] == "login":
pass
elif event["event"] == "join_queue":
# count queue
# move start game if 2 players are in the queue
# remove player from the queue once game starts
pass
elif event["event"] == "leave_queue":
# just remove player from the queue
pass
def handleUDPEvents(self, 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"] == "activatemonstereffect":
pass
self.__networkManager = NetworkManager(self.__address, self.__tcpPort, self.__udpPort)

View File

@ -2,5 +2,14 @@
"cSpell.words": [
"pygame",
"yvel"
],
"cSpell.ignoreWords": [
"activateeffectcard",
"activatemonstereffect",
"attackcard",
"attackplayer",
"movecard",
"placecard",
"removecard"
]
}

View File

@ -5,6 +5,7 @@ from Classes.Objects.Cards.MonsterCard import MonsterCard
from Classes.System.Components.Window import Window
from Classes.System.Components.InputHandler import InputHandler
from Classes.Objects.World import World
from Classes.System.Network.NetworkManager import NetworkManager
class App:
@ -15,12 +16,14 @@ class App:
__myFont:pygame.font
__world:World
__inputHandler: InputHandler
__networkManager: NetworkManager
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.__networkManager("127.0.0.1", "54322", "54323")
# game word
self.__world = World(self.__window.getScreen())
@ -32,7 +35,7 @@ class App:
# create sprite groups
# todo: remove these and let server handle card creation instead
# blocker: server - client communication
# blocker: server - client communication [WIP]
self.__world.spawnCard("Assets/Cards/MonsterCards/testmonstercard/", (pygame.Vector2(500, 1050), self.__inputHandler))
self.__world.spawnCard("Assets/Cards/MonsterCards/testmonstercard/", (pygame.Vector2(600, 1050), self.__inputHandler))
self.__world.spawnCard("Assets/Cards/MonsterCards/testmonstercard/", (pygame.Vector2(700, 1050), self.__inputHandler))
@ -64,34 +67,33 @@ class App:
def handleEvent(self, events):
# TODO: fix bug that stacks cards when dragging them around
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 selectedCard == None:
card.setDragging(True)
selectedCard = card
# card.setOffset(mouse_pos - card.getPos())
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.image = pygame.transform.scale(card.image, (field.getSize()[0] - 10, field.getSize()[1] - 10))
card.rect.center = field.rect.center
field.image = card.image.copy()
card.setDragging(False)
# card.kill()
elif field.getType() == "EffectField" and card.getType() == "SpellCard" or field.getType() == "EffectField" and card.getType() == "TrapCard":
# todo: resize card so that it fits into the card field
# card.image = pygame.transform.scale(card.image, (field.getSize()[0] - 10, field.getSize()[1] - 10))
card.rect.center = field.rect.center
field.image = card.image.copy()
card.setDragging(False)
# card.kill()
elif event.type == pygame.MOUSEBUTTONUP:
mouse_x, mouse_y = pygame.mouse.get_pos()
mouse_pos = pygame.Vector2(mouse_x, mouse_y)

View File

@ -0,0 +1,29 @@
import socket
class TCPEventHandler:
def __init__(self, socket:socket):
self.tcp_socket = socket
def handleEvents(self, event):
pass
class UDPEventHandler:
def __init__(self, socket:socket):
self.udp_socket = socket
def handleEvents(self, 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

View File

@ -0,0 +1,73 @@
import json
import socket
import threading
from Classes.System.Network.Handler.EventHandler import TCPEventHandler, UDPEventHandler
class NetworkManager:
class UDP:
def __init__(self, addr: str, port: str):
self.addr = addr
self.port = int(port)
self.udpSocket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
self.eventHandler = UDPEventHandler(self.udpSocket)
# start listener thread
self.listen()
def send(self, message: dict):
self.udpSocket.sendto(json.dumps(message).encode(), (self.addr, self.port))
# starts a listener thread for udp data
def listen(self):
udpThread = threading.Thread(target=self.receive)
udpThread.daemon = True
udpThread.start()
def receive(self):
while True:
try:
data, addr = self.udpSocket.recvfrom(1024)
if data:
decoded_data = json.loads(data.decode())
self.eventHandler.handleEvents(decoded_data)
except Exception as e:
print(f"Error receiving UDP data: {e}")
break
class TCP:
def __init__(self, addr: str, port: str):
self.addr = addr
self.port = int(port)
self.tcpSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.eventHandler = TCPEventHandler(self.tcpSocket)
# start listener thread
self.listen()
def connect(self):
self.tcpSocket.connect((self.addr, self.port))
def send(self, message: dict):
self.tcpSocket.sendall(json.dumps(message).encode())
# starts a listener thread for tcp data
def listen(self):
tcp_thread = threading.Thread(target=self.receive)
tcp_thread.daemon = True
tcp_thread.start()
def receive(self):
while True:
try:
data = self.tcpSocket.recv(1024)
if data:
decoded_data = json.loads(data.decode())
self.eventHandler.handleEvents(decoded_data)
except Exception as e:
print(f"Error receiving TCP data: {e}")
break
def __init__(self, addr: str, port: str):
self.tcp = self.TCP(addr, port)
self.udp = self.UDP(addr, port)

View File

@ -0,0 +1,66 @@
import selectors
import socket
import json
sel = selectors.DefaultSelector()
clients = {} # Dictonary, um Clients zu speichern
def accept_wrapper(sock):
conn, addr = sock.accept()
print(f"Verbunden mit {addr}")
conn.setblocking(False)
data = types.SimpleNamespace(addr=addr, inb=b"", outb=b"")
events = selectors.EVENT_READ | selectors.EVENT_WRITE
sel.register(conn, events, data=data)
def service_connection(key, mask):
sock = key.fileobj
data = key.data
if mask & selectors.EVENT_READ:
recv_data = sock.recv(1024)
if recv_data:
data.outb += recv_data
try:
received_json = json.loads(recv_data.decode())
# Hier könnte man weitere Verarbeitungsschritte für die empfangenen Daten durchführen
except json.JSONDecodeError:
pass
else:
print("Verbindung geschlossen mit", data.addr)
sel.unregister(sock)
sock.close()
del clients[data.addr]
if mask & selectors.EVENT_WRITE:
if data.outb:
sent = sock.send(data.outb)
data.outb = data.outb[sent:]
def start_server():
host = '127.0.0.1'
port_tcp = 65432
port_udp = 65433
server_tcp = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_tcp.bind((host, port_tcp))
server_tcp.listen()
server_tcp.setblocking(False)
sel.register(server_tcp, selectors.EVENT_READ, data=None)
server_udp = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
server_udp.bind((host, port_udp))
server_udp.setblocking(False)
sel.register(server_udp, selectors.EVENT_READ, data=None)
while True:
events = sel.select(timeout=None)
for key, mask in events:
if key.data is None:
accept_wrapper(key.fileobj)
else:
service_connection(key, mask)
if __name__ == "__main__":
start_server()

View File

@ -1,3 +1,3 @@
source_md5="477bc93796f16089b049552bd9aab759"
source_md5="f884a33dc45460879bc3345ad8a19b00"
dest_md5="26b57e7ecef6103a8c1aecaba90772c1"

12
Online_TCG.code-workspace Normal file
View File

@ -0,0 +1,12 @@
{
"folders": [
{
"path": "."
}
],
"settings": {
"cSpell.words": [
"MOUSEBUTTONUP"
]
}
}

Binary file not shown.

View File

@ -0,0 +1,2 @@
# Blender 3.4.1 MTL File: 'MonsterCard.blend'
# www.blender.org

View File

@ -0,0 +1,45 @@
# Blender 3.4.1
# www.blender.org
mtllib MonsterCard.mtl
g Cube_Mesh
v -1.779980 -0.015007 2.505141
v -1.779980 0.015007 2.505141
v -1.779980 -0.015007 -2.505141
v -1.779980 0.015007 -2.505141
v 1.779980 -0.015007 2.505141
v 1.779980 0.015007 2.505141
v 1.779980 -0.015007 -2.505141
v 1.779980 0.015007 -2.505141
vn -1.0000 -0.0000 -0.0000
vn -0.0000 -0.0000 -1.0000
vn 1.0000 -0.0000 -0.0000
vn -0.0000 -0.0000 1.0000
vn -0.0000 -1.0000 -0.0000
vn -0.0000 1.0000 -0.0000
vt 0.375000 0.000000
vt 0.375000 1.000000
vt 0.125000 0.750000
vt 0.625000 0.000000
vt 0.625000 1.000000
vt 0.875000 0.750000
vt 0.125000 0.500000
vt 0.375000 0.250000
vt 0.625000 0.250000
vt 0.875000 0.500000
vt 0.375000 0.750000
vt 0.625000 0.750000
vt 0.375000 0.500000
vt 0.625000 0.500000
s 0
f 2/4/1 3/8/1 1/1/1
f 4/9/2 7/13/2 3/8/2
f 8/14/3 5/11/3 7/13/3
f 6/12/4 1/2/4 5/11/4
f 7/13/5 1/3/5 3/7/5
f 4/10/6 6/12/6 8/14/6
f 2/4/1 4/9/1 3/8/1
f 4/9/2 8/14/2 7/13/2
f 8/14/3 6/12/3 5/11/3
f 6/12/4 2/5/4 1/2/4
f 7/13/5 5/11/5 1/3/5
f 4/10/6 2/6/6 6/12/6

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.2 KiB

Binary file not shown.

Binary file not shown.