1 from __future__ import annotations
10 from typing import Any, Optional
22 if __name__ == "__main__":
23 Utils.init_logging("SICPelago", exception_logger="Client")
25 from NetUtils import ClientStatus
26 from CommonClient import CommonContext, logger, get_base_parser, ClientCommandProcessor, server_loop
27 from MultiServer import mark_raw
29 from . import all_items
30 from .game import game
31 from .game.scheme import buffer_input, scheme_read, scheme_eval, scheme_format, SchemeError
33 items_by_id = {v.id(): v for v in all_items.values()}
34 macguffins = [game.MacGuffin(x) for x in game.MacGuffins]
36 async def send_locations(ctx: Context, problem: game.Problem):
38 (chapter, exercise) = str(problem).split(' ')[0].split('.')
39 return int(chapter) * 100 + int(exercise)
40 problem_id = id(problem)
41 new_locs = await ctx.check_locations([problem_id * 10, problem_id * 10 + 1])
42 if new_locs and not ctx.watcher_event.is_set():
43 ctx.should_wait_for_events = True
45 async def game_loop(ctx: Context):
48 game.problem_solved_hook = lambda problem: unsent_checks.append(problem) or True
49 env = game.ThePlayerFrame
55 for problem in unsent_checks:
56 await send_locations(ctx, problem)
59 if ctx.should_wait_for_events:
60 await ctx.watcher_event.wait()
61 if ctx.watcher_event.is_set():
62 for msg in ctx.messages:
65 for net_item in ctx.items_received:
66 item = items_by_id.get(net_item.item, None)
69 if item.id() <= len(macguffins):
70 next_mg = macguffins[item.id() - 1]
73 macguffins[item.id() - 1] = None
74 game.receive_reward(next_mg)
75 elif not game.receive_reward(str(item)):
77 ctx.watcher_event.clear()
78 ctx.should_wait_for_events = False
80 if not any(macguffins) and not ctx.finished_game:
81 await ctx.send_msgs([{"cmd": "StatusUpdate", "status": ClientStatus.CLIENT_GOAL}])
82 ctx.finished_game = True
84 src = await asyncio.to_thread(buffer_input)
85 while src.more_on_line:
86 expression = scheme_read(src)
87 result = scheme_eval(expression, env)
88 if result is not None:
89 print(scheme_format(result))
90 except (SchemeError, SyntaxError, ValueError, RuntimeError) as err:
91 if (isinstance(err, RuntimeError) and
92 'maximum recursion depth exceeded' not in err.args[0]):
95 except KeyboardInterrupt: # <Control>-C
96 print("\nKeyboardInterrupt")
97 except EOFError: # <Control>-D, etc.
100 class Context(CommonContext):
102 items_handling = 0b111 # full remote
103 should_wait_for_events = True
106 def __init__(self, *args, **kwargs):
108 super().__init__(*args, **kwargs)
110 async def server_auth(self, password_requested: bool = False):
111 await self.send_connect()
113 async def main(args):
114 parser = get_base_parser(description="SICPelago")
115 parser.add_argument('--name', default=None, help="Slot Name to connect as.")
116 parser.add_argument('--debug', help="Show debug logging on stdout.")
117 args = parser.parse_args(args)
120 root_logger = logging.getLogger()
121 for handler in root_logger.handlers[:]:
122 if isinstance(handler, logging.StreamHandler):
123 root_logger.removeHandler(handler)
126 ctx = Context(args.connect, args.password)
128 ctx.server_task = asyncio.create_task(server_loop(ctx), name="server loop")
133 def on_print(self, args: dict):
134 super().on_print(args)
135 self.messages.append(args['text'])
136 self.watcher_event.set()
138 def on_print_json(self, args: dict):
139 super().on_print_json(args)
140 text = self.jsontotextparser(copy.deepcopy(args["data"]))
141 if 'Now that you are connected' in text:
143 self.messages.append(text)
144 self.watcher_event.set()
146 def run(*args) -> None:
147 asyncio.run(Context.main(args))
149 if __name__ == '__main__':