From 3ad7b61b7d6ff2693a9d78e5b57ff897e761a5be Mon Sep 17 00:00:00 2001 From: Alfred Melch Date: Tue, 7 Jan 2020 19:27:05 +0100 Subject: [PATCH] Add a game loop --- srv/ebermergen/__init__.py | 22 ++++++++++++ srv/ebermergen/lib/ebermergen_game.py | 50 +++++++++++++++++++++++++++ srv/ebermergen/lib/gameloop.py | 46 ++++++++++++++++++++++++ srv/ebermergen/models/game.py | 2 ++ 4 files changed, 120 insertions(+) create mode 100644 srv/ebermergen/lib/ebermergen_game.py create mode 100644 srv/ebermergen/lib/gameloop.py diff --git a/srv/ebermergen/__init__.py b/srv/ebermergen/__init__.py index 3868d91..450633a 100644 --- a/srv/ebermergen/__init__.py +++ b/srv/ebermergen/__init__.py @@ -5,9 +5,13 @@ from flask_cors import CORS from ebermergen.models.game import Game from .lib import generate_map +from .lib.ebermergen_game import EbermergenGame + app = Flask(__name__) CORS(app) +games = {} + @app.route('/') def index(): @@ -29,3 +33,21 @@ def state(): a = Game() a.seed_map() return jsonify(a.serialize()) + + +@app.route('/lala') +def lala(): + return jsonify({k: v.state for (k, v) in games.items()}) + + +@app.route('/new-game/', methods=['GET', 'POST']) +def new_game(name): + games[name] = EbermergenGame() + games[name].start_game() + return jsonify({'message': 'success'}) + + +@app.route('/action//') +def action(name, type): + games[name].perform_action(type, {}) + return jsonify({'message': 'success'}) diff --git a/srv/ebermergen/lib/ebermergen_game.py b/srv/ebermergen/lib/ebermergen_game.py new file mode 100644 index 0000000..5446548 --- /dev/null +++ b/srv/ebermergen/lib/ebermergen_game.py @@ -0,0 +1,50 @@ +import atexit +import time +import threading +import queue + +from .gameloop import GameLoop + + +def tick(dt, first, second): + print('%.2f' % dt, first, second) + pass + + +def worker(state, action_queue): + state['counter'] = 0 + state['actions'] = [] + loop = GameLoop(tick=tick, fps=1, fixed_dt=True, args=(1, 2)) + while True: + item = action_queue.get() + if item['action'] == 'STOP': + loop.stop() + if item['action'] == 'START': + loop.start() + state['counter'] += 1 + state['actions'].append(item['action']) + + +class EbermergenGame: + """Runs a single game instance + + Holds the serialized state. + Provides methods to perform actions. + """ + + def __init__(self): + self.state = {} + self.actions = queue.Queue() + self.thread = threading.Thread( + target=worker, args=(self.state, self.actions)) + + def start_game(self): + self.thread.start() + atexit.register(self.interupt) + + def interupt(self): + # handle gracefull shutdown + pass + + def perform_action(self, action, payload={}): + self.actions.put({'action': action, 'payload': payload}) diff --git a/srv/ebermergen/lib/gameloop.py b/srv/ebermergen/lib/gameloop.py new file mode 100644 index 0000000..b48a9a9 --- /dev/null +++ b/srv/ebermergen/lib/gameloop.py @@ -0,0 +1,46 @@ +import threading +import time +import random + + +class GameLoop(): + """Implements a loop that calls tick every 1/fps + + tick gets called with the time delta to the last call as parameter + if fixed_dt is True the tick function gets called with 1/fps + """ + + def __init__(self, fps=1, tick=None, fixed_dt=False, args=None): + self.fps = fps + self.tick = tick if tick is not None else self.default_tick + self.args = args or tuple() + self.fixed_dt = fixed_dt + self.running = False + self.last_frame_time = time.time() + self.thread = threading.Thread(target=self.loop) + + def loop(self): + while self.running: + current_time = time.time() + dt = current_time - self.last_frame_time + self.last_frame_time = current_time + + if self.fixed_dt: + self.tick(1 / self.fps, *self.args) + else: + self.tick(dt, *self.args) + + sleep_time = (1 / self.fps) - (time.time() - self.last_frame_time) + if sleep_time > 0: + time.sleep(sleep_time) + + def start(self): + self.running = True + self.thread.start() + + def stop(self): + self.running = False + + def default_tick(self, dt): + print('ticked. Last tick was %.2f seconds ago' % dt) + time.sleep(random.choice([0.4, 0.5, 0.6, 1.4, 2.1]) * 1/self.fps) diff --git a/srv/ebermergen/models/game.py b/srv/ebermergen/models/game.py index a327800..9226306 100644 --- a/srv/ebermergen/models/game.py +++ b/srv/ebermergen/models/game.py @@ -5,6 +5,8 @@ from .player import Player from .map import Map from .entities import Town +from ebermergen.lib.gameloop import GameLoop + class Game: def __init__(self):