init - add basic server for finding a neighbour

This commit is contained in:
qvalentin 2022-03-27 12:59:14 +02:00
parent e5e441622b
commit 8cf2d4d7f1
Signed by: qvalentin
GPG Key ID: C979FA1EAFCABF1C
8 changed files with 356 additions and 58 deletions

152
.gitignore vendored Normal file
View File

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

View File

@ -0,0 +1,6 @@
from enum import Enum
class Direction(Enum):
LEFT = 1
RIGHT = 2

View File

@ -0,0 +1,7 @@
from dataclasses import dataclass
@dataclass
class Member:
ip: str
port: int

View File

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

View File

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

View File

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

48
Code/Main.py Normal file
View File

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

View File

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