Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 8 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,9 @@
[![PyPI - Python Version](https://img.shields.io/pypi/pyversions/socha?label=Python)](https://pypi.org/project/socha/)
[![Discord](https://img.shields.io/discord/233577109363097601?color=blue&label=Discord)](https://discord.gg/ARZamDptG5)
[![Documentation](https://img.shields.io/badge/Software--Challenge%20-Documentation-%234299e1)](https://docs.software-challenge.de/)
> Please note that this is a very early version, which may still contain some bugs. However, the client is able to play
> a game from start to end.

> Please read the [documentation for this client](https://software-challenge-python-client.readthedocs.io/en/latest/)
> before you asking questions or opening an issue.

This repository contains the Python package for the
[Software-Challenge Germany](https://www.software-challenge.de), a programming competition for students. The students
Expand All @@ -31,6 +32,8 @@ which installs the packages inside the folder.

> Pleas make sure that you have at least **Python 3.6** installed.
> Check with `$ python -V` or `$ python3 -V`.
>
> If not present you can install python with the following commands:
> - Windows: `> winget install -e --id Python.Python.3.6`
> - Debian: `$ sudo apt install python3.6`
> - Arch: `$ sudo pacman -S python`
Expand Down Expand Up @@ -173,9 +176,9 @@ you should have a directory structure that looks something like this:

````
my_player/
|- venv/
|- logic.py
|- start.sh
├── venv/
├── logic.py
└── start.sh
````

The `my_player` directory,
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ build-backend = "hatchling.build"

[project]
name = "socha"
version = "0.9.8"
version = "0.9.9"
authors = [
{ name = "FalconsSky", email = "stu222782@mail.uni-kiel.de" },
]
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

setup(
name='socha',
version='0.9.8',
version='0.9.9',
packages=['socha', 'socha.api', 'socha.api.plugin', 'socha.api.protocol',
'socha.api.networking'],
url='https://github.com/FalconsSky/Software-Challenge-Python-Client',
Expand Down
14 changes: 8 additions & 6 deletions socha/api/networking/_network_interface.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,27 +9,29 @@

class _NetworkInterface:
"""
This interface handels all package transfers. It'll send and _receive data from a given connection.
This interface handels all package transfers. It'll _send and _receive data from a given connection.
"""

def __init__(self, host="localhost", port=13050, timeout=5):
def __init__(self, host="localhost", port=13050, timeout=10):
"""
:param host: Host of the server. Default is localhost.
:param port: Port of the server. Default is 13050.
:param timeout: Timeout for receiving data from the server. Default are 10 seconds.
"""
self.host = host
self.port = port
self.timeout = timeout
self.connected: bool = False
self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.socket.settimeout(timeout)
self.socket = None

self.buffer: bytes = b""

def connect(self):
"""
Connects the socket to the server and will be ready to listen for and send data.
Connects the socket to the server and will be ready to listen for and _send data.
"""
self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.socket.settimeout(self.timeout)
self.socket.connect((self.host, self.port))
self.connected = True
logging.info("Connected to server.")
Expand All @@ -45,7 +47,7 @@ def close(self):
def send(self, data: bytes):
"""
Sends the data to the server. It puts the data in the sending queue and the _SocketHandler thread will get
and send it.
and _send it.
:param data: The data that is being sent as string.
"""
self.socket.sendall(data)
Expand Down
85 changes: 17 additions & 68 deletions socha/api/networking/_xflux.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
Here are all incoming byte streams and all outgoing protocol objects handelt.
"""
import logging
import sys

from xsdata.formats.dataclass.context import XmlContext
from xsdata.formats.dataclass.parsers import XmlParser
Expand Down Expand Up @@ -89,96 +88,46 @@ def __init__(self, host: str, port: int):
:param host: Host of the server.
:param port: Port of the server.
"""
self.network_interface = _NetworkInterface(host, port)
self._network_interface = _NetworkInterface(host, port)
self.connect_to_server()
self.x_flux = _XFlux()
self.running = False
self.first_time = True

def start(self):
"""
Starts the client loop.
"""
self.running = True
self._client_loop()

def _client_loop(self):
"""
The client loop.
This is the main loop,
where the client waits for messages from the server
and handles them accordingly.
"""
while self.running:
response = self._receive()
if isinstance(response, ProtocolPacket):
if isinstance(response, Left):
logging.info("The server left. Shutting down...")
self.handle_disconnect()
else:
logging.debug(f"Received new object: {response}")
self.on_object(response)
elif self.running:
logging.error(f"Received object of unknown class: {response}")
raise NotImplementedError("Received object of unknown class.")
logging.info("Done.")
sys.exit()
self._x_flux = _XFlux()
self._running = False
self._first_time = True

def _receive(self):
"""
Gets a receiving byte stream from the server.
:return: The next object in the stream.
"""
try:
receiving = self.network_interface.receive()
cls = self.x_flux.deserialize_object(receiving)
receiving = self._network_interface.receive()
cls = self._x_flux.deserialize_object(receiving)
return cls
except OSError:
logging.error("Shutting down abnormally...")
self.running = False
self._running = False

def send(self, obj: ProtocolPacket):
def _send(self, obj: ProtocolPacket):
"""
Sends an object to the server.
:param obj: The object to send.
:param obj: The object to _send.
"""
shipment = self.x_flux.serialize_object(obj)
if self.first_time:
shipment = self._x_flux.serialize_object(obj)
if self._first_time:
shipment = "<protocol>".encode("utf-8") + shipment
self.first_time = False
self.network_interface.send(shipment)
self._first_time = False
self._network_interface.send(shipment)

def connect_to_server(self):
"""
Creates a TCP connection with the server.
"""
self.network_interface.connect()
self._network_interface.connect()

def close_connection(self):
"""
Sends a closing xml to the server and closes the connection afterwards.
"""
close_xml = self.x_flux.serialize_object(Close())
self.network_interface.send(close_xml)
self.network_interface.close()

def handle_disconnect(self):
"""
Closes the connection and stops the client loop.
"""
self.close_connection()
self.running = False

def on_object(self, message):
"""
Handles an object received from the server.
:param message: The object to handle.
"""

def stop(self):
"""
Disconnects from the server and stops the client loop.
"""
if self.network_interface.connected:
self.close_connection()
self.running = False
self._send(Close())
self._first_time = True
self._network_interface.close()
Loading