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:
parent
bd1c9f896b
commit
facecdf487
|
@ -57,8 +57,10 @@ class Client(Endpoint):
|
||||||
|
|
||||||
class Context(Node):
|
class Context(Node):
|
||||||
def __init__(self, host: str, port: int, password: str, location_check_points: int, hint_cost: int,
|
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__()
|
super(Context, self).__init__()
|
||||||
|
self.shutdown_task = None
|
||||||
self.data_filename = None
|
self.data_filename = None
|
||||||
self.save_filename = None
|
self.save_filename = None
|
||||||
self.saving = False
|
self.saving = False
|
||||||
|
@ -88,7 +90,7 @@ class Context(Node):
|
||||||
typing.Tuple[int, int], datetime.datetime] = {} # datetime of last connection
|
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.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.er_hint_data: typing.Dict[int, typing.Dict[int, str]] = {}
|
||||||
self.auto_shutdown = 0
|
self.auto_shutdown = auto_shutdown
|
||||||
self.commandprocessor = ServerCommandProcessor(self)
|
self.commandprocessor = ServerCommandProcessor(self)
|
||||||
self.embedded_blacklist = {"host", "port"}
|
self.embedded_blacklist = {"host", "port"}
|
||||||
self.client_ids: typing.Dict[typing.Tuple[int, int], datetime.datetime] = {}
|
self.client_ids: typing.Dict[typing.Tuple[int, int], datetime.datetime] = {}
|
||||||
|
@ -948,6 +950,8 @@ class ServerCommandProcessor(CommandProcessor):
|
||||||
def _cmd_exit(self) -> bool:
|
def _cmd_exit(self) -> bool:
|
||||||
"""Shutdown the server"""
|
"""Shutdown the server"""
|
||||||
asyncio.create_task(self.ctx.server.ws_server._close())
|
asyncio.create_task(self.ctx.server.ws_server._close())
|
||||||
|
if self.ctx.shutdown_task:
|
||||||
|
self.ctx.shutdown_task.cancel()
|
||||||
self.ctx.running = False
|
self.ctx.running = False
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
@ -1095,9 +1099,30 @@ def parse_args() -> argparse.Namespace:
|
||||||
return args
|
return args
|
||||||
|
|
||||||
|
|
||||||
async def auto_shutdown(ctx):
|
async def auto_shutdown(ctx, to_cancel=None):
|
||||||
# to be implemented soon
|
await asyncio.sleep(ctx.auto_shutdown * 60)
|
||||||
pass
|
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):
|
async def main(args: argparse.Namespace):
|
||||||
logging.basicConfig(format='[%(asctime)s] %(message)s', level=getattr(logging, args.loglevel.upper(), logging.INFO))
|
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()
|
ip = args.host if args.host else Utils.get_public_ipv4()
|
||||||
logging.info('Hosting game at %s:%d (%s)' % (ip, ctx.port,
|
logging.info('Hosting game at %s:%d (%s)' % (ip, ctx.port,
|
||||||
'No password' if not ctx.password else 'Password: %s' % ctx.password))
|
'No password' if not ctx.password else 'Password: %s' % ctx.password))
|
||||||
|
|
||||||
await ctx.server
|
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__':
|
if __name__ == '__main__':
|
||||||
loop = asyncio.get_event_loop()
|
loop = asyncio.get_event_loop()
|
||||||
loop.run_until_complete(main(parse_args()))
|
try:
|
||||||
|
loop.run_until_complete(main(parse_args()))
|
||||||
|
except asyncio.exceptions.CancelledError:
|
||||||
|
pass
|
||||||
|
|
|
@ -2,18 +2,16 @@
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import logging
|
import logging
|
||||||
import sys
|
|
||||||
import threading
|
import threading
|
||||||
import typing
|
import typing
|
||||||
import multiprocessing
|
import multiprocessing
|
||||||
import functools
|
from pony.orm import Database, db_session
|
||||||
from pony.flask import Pony
|
|
||||||
from pony.orm import Database, Required, Optional, commit, select, db_session
|
|
||||||
|
|
||||||
import websockets
|
from flask import Flask, flash, request, redirect, url_for, render_template, Response
|
||||||
from flask import Flask, flash, request, redirect, url_for, render_template, Response, g
|
|
||||||
from werkzeug.utils import secure_filename
|
from werkzeug.utils import secure_filename
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
UPLOAD_FOLDER = os.path.relpath('uploads')
|
UPLOAD_FOLDER = os.path.relpath('uploads')
|
||||||
LOGS_FOLDER = os.path.relpath('logs')
|
LOGS_FOLDER = os.path.relpath('logs')
|
||||||
multidata_folder = os.path.join(UPLOAD_FOLDER, "multidata")
|
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)
|
return render_template("host_multidata.html", filename=filename)
|
||||||
|
|
||||||
|
|
||||||
def run_server_process(multidata: str):
|
from WebHost.customserver import run_server_process
|
||||||
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())
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
multiprocessing.freeze_support()
|
multiprocessing.freeze_support()
|
||||||
|
|
|
@ -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())
|
Loading…
Reference in New Issue