implement --auto_shutdown <minutes>, shutting down a multiserver after that many minutes of inactivity

and set WebHost to a default of 6 hours
This commit is contained in:
Fabian Dill 2020-06-16 11:26:54 +02:00
parent bd1c9f896b
commit facecdf487
3 changed files with 91 additions and 46 deletions

View File

@ -57,8 +57,10 @@ class Client(Endpoint):
class Context(Node):
def __init__(self, host: str, port: int, password: str, location_check_points: int, hint_cost: int,
item_cheat: bool, forfeit_mode: str = "disabled", remaining_mode: str = "disabled", auto_shutdown=0):
item_cheat: bool, forfeit_mode: str = "disabled", remaining_mode: str = "disabled",
auto_shutdown: typing.SupportsFloat = 0):
super(Context, self).__init__()
self.shutdown_task = None
self.data_filename = None
self.save_filename = None
self.saving = False
@ -88,7 +90,7 @@ class Context(Node):
typing.Tuple[int, int], datetime.datetime] = {} # datetime of last connection
self.client_game_state: typing.Dict[typing.Tuple[int, int], int] = collections.defaultdict(int)
self.er_hint_data: typing.Dict[int, typing.Dict[int, str]] = {}
self.auto_shutdown = 0
self.auto_shutdown = auto_shutdown
self.commandprocessor = ServerCommandProcessor(self)
self.embedded_blacklist = {"host", "port"}
self.client_ids: typing.Dict[typing.Tuple[int, int], datetime.datetime] = {}
@ -948,6 +950,8 @@ class ServerCommandProcessor(CommandProcessor):
def _cmd_exit(self) -> bool:
"""Shutdown the server"""
asyncio.create_task(self.ctx.server.ws_server._close())
if self.ctx.shutdown_task:
self.ctx.shutdown_task.cancel()
self.ctx.running = False
return True
@ -1095,9 +1099,30 @@ def parse_args() -> argparse.Namespace:
return args
async def auto_shutdown(ctx):
# to be implemented soon
pass
async def auto_shutdown(ctx, to_cancel=None):
await asyncio.sleep(ctx.auto_shutdown * 60)
while ctx.running:
if not ctx.client_activity_timers.values():
asyncio.create_task(ctx.server.ws_server._close())
ctx.running = False
if to_cancel:
for task in to_cancel:
task.cancel()
logging.info("Shutting down due to inactivity.")
else:
newest_activity = max(ctx.client_activity_timers.values())
delta = datetime.datetime.now(datetime.timezone.utc) - newest_activity
seconds = ctx.auto_shutdown * 60 - delta.total_seconds()
if seconds < 0:
asyncio.create_task(ctx.server.ws_server._close())
ctx.running = False
if to_cancel:
for task in to_cancel:
task.cancel()
logging.info("Shutting down due to inactivity.")
else:
await asyncio.sleep(seconds)
async def main(args: argparse.Namespace):
logging.basicConfig(format='[%(asctime)s] %(message)s', level=getattr(logging, args.loglevel.upper(), logging.INFO))
@ -1128,9 +1153,18 @@ async def main(args: argparse.Namespace):
ip = args.host if args.host else Utils.get_public_ipv4()
logging.info('Hosting game at %s:%d (%s)' % (ip, ctx.port,
'No password' if not ctx.password else 'Password: %s' % ctx.password))
await ctx.server
await console(ctx)
console_task = asyncio.create_task(console(ctx))
if ctx.auto_shutdown:
ctx.shutdown_task = asyncio.create_task(auto_shutdown(ctx, [console_task]))
await console_task
if ctx.shutdown_task:
await ctx.shutdown_task
if __name__ == '__main__':
loop = asyncio.get_event_loop()
try:
loop.run_until_complete(main(parse_args()))
except asyncio.exceptions.CancelledError:
pass

View File

@ -2,18 +2,16 @@
import os
import logging
import sys
import threading
import typing
import multiprocessing
import functools
from pony.flask import Pony
from pony.orm import Database, Required, Optional, commit, select, db_session
from pony.orm import Database, db_session
import websockets
from flask import Flask, flash, request, redirect, url_for, render_template, Response, g
from flask import Flask, flash, request, redirect, url_for, render_template, Response
from werkzeug.utils import secure_filename
UPLOAD_FOLDER = os.path.relpath('uploads')
LOGS_FOLDER = os.path.relpath('logs')
multidata_folder = os.path.join(UPLOAD_FOLDER, "multidata")
@ -116,39 +114,7 @@ def host_multidata(filename: str):
return render_template("host_multidata.html", filename=filename)
def run_server_process(multidata: str):
async def main():
logging.basicConfig(format='[%(asctime)s] %(message)s',
level=logging.INFO,
filename=os.path.join(LOGS_FOLDER, multidata + ".txt"))
ctx = Context("", 0, "", 1, 1000,
True, "enabled", "goal")
ctx.load(os.path.join(multidata_folder, multidata), True)
ctx.auto_shutdown = 24 * 60 * 60 # 24 hours
ctx.init_save()
ctx.server = websockets.serve(functools.partial(server, ctx=ctx), ctx.host, 0, ping_timeout=None,
ping_interval=None)
await ctx.server
for wssocket in ctx.server.ws_server.sockets:
socketname = wssocket.getsockname()
if wssocket.family == socket.AF_INET6:
logging.info(f'Hosting game at [{get_public_ipv6()}]:{socketname[1]}')
elif wssocket.family == socket.AF_INET:
logging.info(f'Hosting game at {get_public_ipv4()}:{socketname[1]}')
while ctx.running:
await asyncio.sleep(1)
logging.info("Shutting down")
import asyncio
if ".." not in sys.path:
sys.path.append("..")
from MultiServer import Context, server
from Utils import get_public_ipv4, get_public_ipv6
import socket
asyncio.run(main())
from WebHost.customserver import run_server_process
if __name__ == "__main__":
multiprocessing.freeze_support()

45
WebHost/customserver.py Normal file
View File

@ -0,0 +1,45 @@
import functools
import logging
import os
import sys
import websockets
from WebHost import LOGS_FOLDER, multidata_folder
def run_server_process(multidata: str):
async def main():
logging.basicConfig(format='[%(asctime)s] %(message)s',
level=logging.INFO,
filename=os.path.join(LOGS_FOLDER, multidata + ".txt"))
ctx = Context("", 0, "", 1, 1000,
True, "enabled", "goal", 0)
ctx.load(os.path.join(multidata_folder, multidata), True)
ctx.auto_shutdown = 24 * 60 * 60 # 24 hours
ctx.init_save()
ctx.server = websockets.serve(functools.partial(server, ctx=ctx), ctx.host, 0, ping_timeout=None,
ping_interval=None)
await ctx.server
for wssocket in ctx.server.ws_server.sockets:
socketname = wssocket.getsockname()
if wssocket.family == socket.AF_INET6:
logging.info(f'Hosting game at [{get_public_ipv6()}]:{socketname[1]}')
elif wssocket.family == socket.AF_INET:
logging.info(f'Hosting game at {get_public_ipv4()}:{socketname[1]}')
ctx.auto_shutdown = 6 * 60
ctx.shutdown_task = asyncio.create_task(auto_shutdown(ctx, []))
while ctx.running:
await asyncio.sleep(1)
await ctx.shutdown_task
logging.info("Shutting down")
import asyncio
if ".." not in sys.path:
sys.path.append("..")
from MultiServer import Context, server, auto_shutdown
from Utils import get_public_ipv4, get_public_ipv6
import socket
asyncio.run(main())