implement lazy multisave saving using a daemon thread
This commit is contained in:
		
							parent
							
								
									01d793bc00
								
							
						
					
					
						commit
						7e3ee8101f
					
				|  | @ -25,3 +25,4 @@ weights/ | |||
| /logs/ | ||||
| _persistent_storage.yaml | ||||
| mystery_result_*.yaml | ||||
| /db.db3 | ||||
|  |  | |||
|  | @ -11,6 +11,7 @@ import typing | |||
| import inspect | ||||
| import weakref | ||||
| import datetime | ||||
| import threading | ||||
| 
 | ||||
| import ModuleUpdate | ||||
| 
 | ||||
|  | @ -94,14 +95,17 @@ class Context(Node): | |||
|         self.commandprocessor = ServerCommandProcessor(self) | ||||
|         self.embedded_blacklist = {"host", "port"} | ||||
|         self.client_ids: typing.Dict[typing.Tuple[int, int], datetime.datetime] = {} | ||||
|         self.auto_save_interval = 60  # in seconds | ||||
|         self.auto_saver_thread = None | ||||
|         self.save_dirty = False | ||||
| 
 | ||||
|     def load(self, multidatapath: str, use_embedded_server_options: bool = False): | ||||
|         with open(multidatapath, 'rb') as f: | ||||
|             self._load(f, use_embedded_server_options) | ||||
|             self._load(json.loads(zlib.decompress(f.read()).decode("utf-8-sig")), | ||||
|                        use_embedded_server_options) | ||||
|         self.data_filename = multidatapath | ||||
| 
 | ||||
|     def _load(self, fileobj, use_embedded_server_options: bool): | ||||
|         jsonobj = json.loads(zlib.decompress(fileobj.read()).decode("utf-8-sig")) | ||||
|     def _load(self, jsonobj: dict, use_embedded_server_options: bool): | ||||
|         for team, names in enumerate(jsonobj['names']): | ||||
|             for player, name in enumerate(names, 1): | ||||
|                 self.player_names[(team, player)] = name | ||||
|  | @ -126,6 +130,28 @@ class Context(Node): | |||
|                     setattr(self, key, value) | ||||
|         self.item_cheat = not server_options.get("disable_item_cheat", True) | ||||
| 
 | ||||
|     def save(self, now=False) -> bool: | ||||
|         if self.saving: | ||||
|             if now: | ||||
|                 self.save_dirty = False | ||||
|                 return self._save() | ||||
| 
 | ||||
|             self.save_dirty = True | ||||
|             return True | ||||
| 
 | ||||
|         return False | ||||
| 
 | ||||
|     def _save(self) -> bool: | ||||
|         try: | ||||
|             jsonstr = json.dumps(self.get_save()) | ||||
|             with open(self.save_filename, "wb") as f: | ||||
|                 f.write(zlib.compress(jsonstr.encode("utf-8"))) | ||||
|         except Exception as e: | ||||
|             logging.exception(e) | ||||
|             return False | ||||
|         else: | ||||
|             return True | ||||
| 
 | ||||
|     def init_save(self, enabled: bool = True): | ||||
|         self.saving = enabled | ||||
|         if self.saving: | ||||
|  | @ -141,6 +167,22 @@ class Context(Node): | |||
|             except Exception as e: | ||||
|                 logging.exception(e) | ||||
| 
 | ||||
|             if not self.auto_saver_thread: | ||||
|                 def save_regularly(): | ||||
|                     import time | ||||
|                     while self.running: | ||||
|                         time.sleep(self.auto_save_interval) | ||||
|                         if self.save_dirty: | ||||
|                             logging.debug("Saving multisave via thread.") | ||||
|                             self.save_dirty = False | ||||
|                             self._save() | ||||
| 
 | ||||
|                 self.auto_saver_thread = threading.Thread(target=save_regularly, daemon=True) | ||||
|                 self.auto_saver_thread.start() | ||||
| 
 | ||||
|             import atexit | ||||
|             atexit.register(self._save)  # make sure we save on exit too | ||||
| 
 | ||||
|     def get_save(self) -> dict: | ||||
|         d = { | ||||
|             "rom_names": list(self.rom_names.items()), | ||||
|  | @ -407,7 +449,7 @@ def register_location_checks(ctx: Context, team: int, slot: int, locations): | |||
|             for client in ctx.endpoints: | ||||
|                 if client.team == team and client.slot == slot: | ||||
|                     asyncio.create_task(ctx.send_msgs(client, [["HintPointUpdate", (get_client_points(ctx, client),)]])) | ||||
|             save(ctx) | ||||
|             ctx.save() | ||||
| 
 | ||||
| 
 | ||||
| def notify_team(ctx: Context, team: int, text: str): | ||||
|  | @ -415,15 +457,6 @@ def notify_team(ctx: Context, team: int, text: str): | |||
|     ctx.broadcast_team(team, [['Print', text]]) | ||||
| 
 | ||||
| 
 | ||||
| def save(ctx: Context): | ||||
|     if ctx.saving: | ||||
|         try: | ||||
|             jsonstr = json.dumps(ctx.get_save()) | ||||
|             with open(ctx.save_filename, "wb") as f: | ||||
|                 f.write(zlib.compress(jsonstr.encode("utf-8"))) | ||||
|         except Exception as e: | ||||
|             logging.exception(e) | ||||
| 
 | ||||
| 
 | ||||
| def collect_hints(ctx: Context, team: int, slot: int, item: str) -> typing.List[Utils.Hint]: | ||||
|     hints = [] | ||||
|  | @ -683,13 +716,13 @@ class ClientMessageProcessor(CommandProcessor): | |||
|             self.ctx.name_aliases[self.client.team, self.client.slot] = alias_name | ||||
|             self.output(f"Hello, {alias_name}") | ||||
|             update_aliases(self.ctx, self.client.team) | ||||
|             save(self.ctx) | ||||
|             self.ctx.save() | ||||
|             return True | ||||
|         elif (self.client.team, self.client.slot) in self.ctx.name_aliases: | ||||
|             del (self.ctx.name_aliases[self.client.team, self.client.slot]) | ||||
|             self.output("Removed Alias") | ||||
|             update_aliases(self.ctx, self.client.team) | ||||
|             save(self.ctx) | ||||
|             self.ctx.save() | ||||
|             return True | ||||
|         return False | ||||
| 
 | ||||
|  | @ -776,7 +809,7 @@ class ClientMessageProcessor(CommandProcessor): | |||
|                                 self.output( | ||||
|                                     "Could not pay for everything. Rerun the hint later with more points to get the remaining hints.") | ||||
|                             notify_hints(self.ctx, self.client.team, found_hints + hints) | ||||
|                             save(self.ctx) | ||||
|                             self.ctx.save() | ||||
|                             return True | ||||
| 
 | ||||
|                         else: | ||||
|  | @ -938,9 +971,13 @@ class ServerCommandProcessor(CommandProcessor): | |||
| 
 | ||||
|     def _cmd_save(self) -> bool: | ||||
|         """Save current state to multidata""" | ||||
|         save(self.ctx) | ||||
|         self.output("Game saved") | ||||
|         return True | ||||
|         if self.ctx.saving: | ||||
|             self.ctx.save(True) | ||||
|             self.output("Game saved") | ||||
|             return True | ||||
|         else: | ||||
|             self.output("Saving is disabled.") | ||||
|             return False | ||||
| 
 | ||||
|     def _cmd_players(self) -> bool: | ||||
|         """Get information about connected players""" | ||||
|  | @ -968,13 +1005,13 @@ class ServerCommandProcessor(CommandProcessor): | |||
|                         self.ctx.name_aliases[team, slot] = alias_name | ||||
|                         self.output(f"Named {player_name} as {alias_name}") | ||||
|                         update_aliases(self.ctx, team) | ||||
|                         save(self.ctx) | ||||
|                         self.ctx.save() | ||||
|                         return True | ||||
|                     else: | ||||
|                         del (self.ctx.name_aliases[team, slot]) | ||||
|                         self.output(f"Removed Alias for {player_name}") | ||||
|                         update_aliases(self.ctx, team) | ||||
|                         save(self.ctx) | ||||
|                         self.ctx.save() | ||||
|                         return True | ||||
|         else: | ||||
|             self.output(response) | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue