def main(args: argparse.Namespace): loadfile: Path if os.path.isabs(args.loadfile): loadfile = Path(args.loadfile) else: # Take paths relative to 'data/saves/' loadfile = common.savefile(args.loadfile) physics_engine = physics.PhysicsEngine(common.load_savefile(loadfile)) initial_state = physics_engine.get_state() gui = flight_gui.FlightGui(initial_state, title=name, running_as_mirror=False) atexit.register(gui.shutdown) if args.flamegraph: common.start_flamegraphing() if args.profile: common.start_profiling() while True: state = physics_engine.get_state() # If we have any commands, process them so the simthread has as # much time as possible to restart before next update. physics_engine.handle_requests(gui.pop_commands()) gui.draw(state) gui.rate(common.FRAMERATE)
def lead_server_loop(args): """Main, 'while True'-style loop for a lead server. Blocking. See help text for command line arguments for what a lead server is.""" # Before you make changes to the lead server architecture, consider that # the GRPC server runs in a separate thread than this thread! state_server = network.StateServer() log.info(f'Loading save at {args.data_location.path}') physics_engine = physics.PEngine( common.load_savefile(Path(args.data_location.path))) if not args.no_gui: global cleanup_function global ungraceful_shutdown gui = flight_gui.FlightGui(physics_engine.get_state(), no_intro=args.no_intro) cleanup_function = gui.shutdown ungraceful_shutdown = gui.ungraceful_shutdown server = grpc.server(concurrent.futures.ThreadPoolExecutor(max_workers=4)) grpc_stubs.add_StateServerServicer_to_server(state_server, server) server.add_insecure_port(f'[::]:{args.serve_on_port}') server.start() # This doesn't block! # Need a context manager from now on, to make sure the server always stops. with common.GrpcServerContext(server): log.info(f'Server running on port {args.serve_on_port}. Ctrl-C exits.') if args.profile: common.start_profiling() while True: user_commands = [] state = physics_engine.get_state() state_server.notify_state_change(copy.deepcopy(state._proto_state)) if not args.no_gui: user_commands += gui.pop_commands() user_commands += state_server.pop_commands() # If we have any commands, process them so the simthread has as # much time as possible to regenerate solutions before next update for command in user_commands: if command.ident == network.Request.NOOP: continue log.info(f'Got command: {command}') physics_engine.handle_request(command) if not args.no_gui: gui.draw(state) gui.rate(common.FRAMERATE) else: time.sleep(1 / common.FRAMERATE)
def mirroring_loop(args): """Main, 'while True'-style loop for a mirroring client. Blocking. See help text for CLI arguments for the difference between mirroring and serving.""" currently_mirroring = True log.info('Connecting to CnC server...') with network.StateClient(args.data_location.hostname, args.data_location.port) as mirror_state: log.info(f'Querying lead server {args.data_location.geturl()}') state = mirror_state() physics_engine = physics.PEngine(state) if not args.no_gui: log.info('Initializing graphics (thanks sean)...') gui = flight_gui.FlightGui(state) global cleanup_function cleanup_function = gui.shutdown while True: try: if currently_mirroring: state = mirror_state() physics_engine.set_state(state) else: state = physics_engine.get_state() if not args.no_gui: gui.draw(state) gui.rate(common.FRAMERATE) if gui.closed: break else: time.sleep(1 / common.FRAMERATE) except KeyboardInterrupt: # TODO: hacky solution to turn off mirroring right now is a ^C if currently_mirroring: currently_mirroring = False else: raise
def main(args: argparse.Namespace): log.info(f'Connecting to physics server {args.physics_server}.') time_of_next_network_update = 0.0 lead_server_connection = network.StateClient(Request.HAB_FLIGHT, args.physics_server) state = lead_server_connection.get_state() physics_engine = physics.PhysicsEngine(state) gui = flight_gui.FlightGui(state, title=name, running_as_mirror=False) atexit.register(gui.shutdown) while True: gui.draw(state) user_commands = gui.pop_commands() current_time = time.monotonic() # If there are user commands or we've gone a while since our last # contact with the physics server, request an update. if user_commands or current_time > time_of_next_network_update: # Our state is stale, get the latest update # TODO: what if this fails? Do anything smarter than an exception? state = lead_server_connection.get_state(user_commands) physics_engine.set_state(state) if len(user_commands) == 0: time_of_next_network_update = ( current_time + common.TIME_BETWEEN_NETWORK_UPDATES) else: # If we sent a user command, still ask for an update soon so # the user input can be reflected in hab flight's GUI as soon # as possible. # Magic number alert! The constant we add should be enough that # the physics server has had enough time to simulate the effect # of our input, but we should minimize the constant to minimize # input lag. time_of_next_network_update = current_time + 0.15 else: state = physics_engine.get_state() gui.rate(common.FRAMERATE)
def main(args: argparse.Namespace): time_of_last_network_update = 0.0 networking = True # Whether data is requested over the network log.info(f'Connecting to physics server {args.physics_server}.') lead_server_connection = network.StateClient(Request.MC_FLIGHT, args.physics_server) state = lead_server_connection.get_state() physics_engine = physics.PhysicsEngine(state) gui = flight_gui.FlightGui(state, title=name, running_as_mirror=True) atexit.register(gui.shutdown) while True: old_networking = networking networking = gui.requesting_read_from_physics_server() if old_networking != networking: log.info(('STARTED' if networking else 'STOPPED') + ' networking with the physics server at ' + args.physics_server) if (networking and time.monotonic() - time_of_last_network_update > common.TIME_BETWEEN_NETWORK_UPDATES): # Our state is stale, get the latest update # TODO: what if this fails? Set networking to False? state = lead_server_connection.get_state() physics_engine.set_state(state) time_of_last_network_update = time.monotonic() else: state = physics_engine.get_state() gui.draw(state) if not networking: # When we're not networking, allow user input. physics_engine.handle_requests(gui.pop_commands()) gui.rate(common.FRAMERATE)