From 8cf2d4d7f197cf12b304a5a486989a18559742c9 Mon Sep 17 00:00:00 2001 From: qvalentin Date: Sun, 27 Mar 2022 12:59:14 +0200 Subject: [PATCH] init - add basic server for finding a neighbour --- .gitignore | 152 +++++++++++++++++++++++++++ Code/Communication/Direction.py | 6 ++ Code/Communication/Member.py | 7 ++ Code/Communication/Neighbours.py | 22 ++++ Code/Communication/RequestHandler.py | 51 +++++++++ Code/Communication/Server.py | 22 ++++ Code/Main.py | 48 +++++++++ Code/UI/PlayingField.py | 106 +++++++++---------- 8 files changed, 356 insertions(+), 58 deletions(-) create mode 100644 .gitignore create mode 100644 Code/Communication/Direction.py create mode 100644 Code/Communication/Member.py create mode 100644 Code/Communication/Neighbours.py create mode 100644 Code/Communication/RequestHandler.py create mode 100644 Code/Communication/Server.py create mode 100644 Code/Main.py diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..de2d5e0 --- /dev/null +++ b/.gitignore @@ -0,0 +1,152 @@ +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +cover/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +.pybuilder/ +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +# For a library or package, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +# .python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# poetry +# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. +# This is especially recommended for binary packages to ensure reproducibility, and is more +# commonly ignored for libraries. +# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control +#poetry.lock + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# Cython debug symbols +cython_debug/ + +# PyCharm +# JetBrains specific template is maintained in a separate JetBrains.gitignore that can +# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore +# and can be added to the global gitignore or merged into this file. For a more nuclear +# option (not recommended) you can uncomment the following to ignore the entire idea folder. +#.idea/ diff --git a/Code/Communication/Direction.py b/Code/Communication/Direction.py new file mode 100644 index 0000000..e0ad8d2 --- /dev/null +++ b/Code/Communication/Direction.py @@ -0,0 +1,6 @@ +from enum import Enum + + +class Direction(Enum): + LEFT = 1 + RIGHT = 2 diff --git a/Code/Communication/Member.py b/Code/Communication/Member.py new file mode 100644 index 0000000..969a218 --- /dev/null +++ b/Code/Communication/Member.py @@ -0,0 +1,7 @@ +from dataclasses import dataclass + + +@dataclass +class Member: + ip: str + port: int diff --git a/Code/Communication/Neighbours.py b/Code/Communication/Neighbours.py new file mode 100644 index 0000000..34118f0 --- /dev/null +++ b/Code/Communication/Neighbours.py @@ -0,0 +1,22 @@ +from Code.Communication.Direction import Direction +from Code.Communication.Member import Member + + +class Neighbours: + + def __init__(self, own_process: Member): + self.neighbours = {} + self.own_process = own_process + + def connect(self, direction, ip, port): + print(f"connecting to {ip}:{port} on {direction} side") + pass + + def acceptConnection(self, direction: Direction, ip, port) -> tuple[Member, bool]: + if direction in self.neighbours: + return (self.neighbours[direction],False) + + member = Member(ip, port) + print(f"Adding neighbour {member.__repr__()}") + self.neighbours[direction] = member + return (self.own_process,True) diff --git a/Code/Communication/RequestHandler.py b/Code/Communication/RequestHandler.py new file mode 100644 index 0000000..b8c8dc3 --- /dev/null +++ b/Code/Communication/RequestHandler.py @@ -0,0 +1,51 @@ +import json +import re +import socketserver +from dataclasses import asdict +from http.server import BaseHTTPRequestHandler, HTTPServer + +from Code.Communication.Direction import Direction +from Code.Communication.Neighbours import Neighbours + + +class RequestHandler(BaseHTTPRequestHandler): + neighbours: Neighbours = None + + def do_GET(self): + print("got Get request") + if self.path == "/": + self.send_response(200, "running") + self.end_headers() + + def handle(self) -> None: + super().handle() + + + """ + /connect/right + /connect/left + + with body : {ip:"string",port:number} + """ + + def do_POST(self): + print("Got post request") + if self.path.startswith("/connect/"): + direction = re.findall("/connect/(left|right)", self.path, flags=re.IGNORECASE)[0] + data_string = self.rfile.read(int(self.headers['Content-Length'])) + data = json.loads(data_string) + neighbour, accepted = self.neighbours.acceptConnection(Direction[direction.upper()], data["ip"], data["port"]) + + print(f"Sending neighbour: {neighbour}") + if accepted: + self.send_response(200) + self.send_header('Content-Type', 'application/json') + self.end_headers() + self.wfile.write(json.dumps(asdict(neighbour)).encode('utf8')) + else: + self.send_response(303) + self.send_header('Location', f"http://{neighbour.ip}:{neighbour.port}") + self.end_headers() + + self.end_headers() + diff --git a/Code/Communication/Server.py b/Code/Communication/Server.py new file mode 100644 index 0000000..494eea8 --- /dev/null +++ b/Code/Communication/Server.py @@ -0,0 +1,22 @@ +from http.server import HTTPServer + +from Code.Communication.Neighbours import Neighbours +from Code.Communication.RequestHandler import RequestHandler + + +class Server: + def __init__(self, neighbours: Neighbours): + self.neighbours = neighbours + self.port = neighbours.own_process.port + self.ip = neighbours.own_process.ip + + def stop_server(self): + print("Trying to stop server") + self.server.shutdown() + + def start(self): + RequestHandler.neighbours = self.neighbours + print(f"HTTP Server Running on {self.ip}: {self.port}") + self.server = HTTPServer((self.ip, self.port), RequestHandler) + self.server.serve_forever() + print("Stopped server") diff --git a/Code/Main.py b/Code/Main.py new file mode 100644 index 0000000..89e8f3c --- /dev/null +++ b/Code/Main.py @@ -0,0 +1,48 @@ +import sys +import threading + +from Code.Communication.Direction import Direction +from Code.Communication.Member import Member +from Code.Communication.Neighbours import Neighbours +from Code.Communication.Server import Server +from Code.UI.PlayingField import run_game + +if __name__ == "__main__": + + """ + Getting the args + - own port + + Optional: A neighbour: + - ip + - port + - direction + + + """ + + args = sys.argv + + print(args) + if len(args) >= 2: + own_port =int(args[1]) + else: + print("using default port 8080") + own_port = 8080 + + if len(args) >= 4: + n_ip = args[2] + n_port = int(args[3]) + if len(args) > 4: + n_direction = args[4] + + neighbours = Neighbours(own_process=Member("0.0.0.0", own_port)) + server = Server(neighbours) + + neighbours.connect(Direction[n_direction], n_ip, n_port) + + serverThread = threading.Thread(target=server.start) + serverThread.start() + run_game() + print("finished game") + server.stop_server() diff --git a/Code/UI/PlayingField.py b/Code/UI/PlayingField.py index 6aca658..9d78256 100644 --- a/Code/UI/PlayingField.py +++ b/Code/UI/PlayingField.py @@ -1,78 +1,68 @@ -from datetime import time -import time as ti import pygame as pygame -from Code import Config + from Code.Config import GeneralConfig, Colors from Code.GameLogic.Rules import Rules -from Code.UI import Square from Code.UI.Field import Field class GameState: - def __init__(self): - self.run = True - self.pause_for_input = False - self.field = Field() - self.update_field_events= [] + def __init__(self): + self.run = True + self.pause_for_input = False + self.field = Field() + self.update_field_events = [] - def event_handler(self): - for event in pygame.event.get(): - if event.type == pygame.QUIT: - self.run = False - if event.type == pygame.MOUSEBUTTONUP: - self.update_field_events.append(event) - if event.type == pygame.KEYDOWN: - if event.key == pygame.K_SPACE: - self.pause_for_input = not self.pause_for_input + def event_handler(self): + for event in pygame.event.get(): + if event.type == pygame.QUIT: + self.run = False + if event.type == pygame.MOUSEBUTTONUP: + self.update_field_events.append(event) + if event.type == pygame.KEYDOWN: + if event.key == pygame.K_SPACE: + self.pause_for_input = not self.pause_for_input - def update_field_with_input(self, event): - for line in self.field.squares: - for square in line: - if square.rect.collidepoint(event.pos): - square.update(not square.active) + def update_field_with_input(self, event): + for line in self.field.squares: + for square in line: + if square.rect.collidepoint(event.pos): + square.update(not square.active) - def evolve(self): + def evolve(self): - rules = Rules() - self.field.update_squares(rules.evolve_field(self.field)) + rules = Rules() + self.field.update_squares(rules.evolve_field(self.field)) - def redraw_field(self, window): - window.fill(Colors.BLACK) - self.field.draw_squares(window) + def redraw_field(self, window): + window.fill(Colors.BLACK) + self.field.draw_squares(window) def run_game(): - pygame.init() - pygame.display.set_caption(GeneralConfig.window_caption) - window = pygame.display.set_mode((GeneralConfig.width, GeneralConfig.height)) - clock = pygame.time.Clock() - game_state = GameState() - time_elapsed_since_last_action = 0 - while game_state.run: - game_state.event_handler() + pygame.init() + pygame.display.set_caption(GeneralConfig.window_caption) + window = pygame.display.set_mode((GeneralConfig.width, GeneralConfig.height)) + clock = pygame.time.Clock() + game_state = GameState() + time_elapsed_since_last_action = 0 + while game_state.run: + game_state.event_handler() + for event in game_state.update_field_events: + game_state.update_field_with_input(event) + game_state.update_field_events.remove(event) - for event in game_state.update_field_events: - game_state.update_field_with_input(event) - game_state.update_field_events.remove(event) + clock.tick(GeneralConfig.fps) + time_elapsed_since_last_action += clock.get_time() + if game_state.pause_for_input: - clock.tick(GeneralConfig.fps) - time_elapsed_since_last_action += clock.get_time() + if time_elapsed_since_last_action > 100: + # start = ti.time() + game_state.evolve() + # end = ti.time() + # print(end - start) + time_elapsed_since_last_action = 0 # reset it to 0 so you can count again - if game_state.pause_for_input: - - if time_elapsed_since_last_action > 100: - - #start = ti.time() - game_state.evolve() - #end = ti.time() - #print(end - start) - time_elapsed_since_last_action = 0 # reset it to 0 so you can count again - - game_state.redraw_field(window) - pygame.display.update() - - -if __name__ == "__main__": - run_game() + game_state.redraw_field(window) + pygame.display.update()