from nullptr.command_line import CommandLine from nullptr.store import Store from nullptr.analyzer import Analyzer, Point, path_dist import argparse from nullptr.models import * from nullptr.api import Api from .util import * from time import sleep, time from threading import Thread from nullptr.central_command import CentralCommand import readline import os from copy import copy class CommandError(Exception): pass class Commander(CommandLine): def __init__(self, data_dir='data'): store_file = os.path.join(data_dir, 'store.npt') hist_file = os.path.join(data_dir, 'cmd.hst') self.cred_file = os.path.join(data_dir, 'creds.txt') self.hist_file = hist_file if os.path.isfile(hist_file): readline.read_history_file(hist_file) self.store = Store(store_file, False) self.agent = self.select_agent() self.api = Api(self.store, self.agent) self.centcom = CentralCommand(self.store, self.api) self.analyzer = Analyzer(self.store) self.ship = None self.stop_auto= False super().__init__() ######## INFRA ######### def handle_eof(self): self.store.close() readline.write_history_file(self.hist_file) print("Goodbye!") def do_pp(self): pprint(self.api.last_result) def prompt(self): if self.ship: return f'{self.ship.symbol}> ' else: return '> ' def after_cmd(self): self.store.flush() def do_auto(self): self.centcom.run_interactive() def do_log(self, level): if not self.has_ship(): return self.ship._log_level = int(level) ######## Resolvers ######### def ask_obj(self, typ, prompt): obj = None while obj is None: symbol = input(prompt).strip() obj = self.store.get(typ, symbol.upper()) if obj is None: print('not found') return obj def has_ship(self): if self.ship is not None: return True else: print('set a ship') def select_agent(self): agents = self.store.all(Agent) agent = next(agents, None) if agent is None: agent = self.agent_setup() return agent def resolve(self, typ, arg): arg = arg.upper() matches = [c for c in self.store.all(typ) if c.symbol.startswith(arg)] if len(matches) == 1: return matches[0] elif len(matches) > 1: raise CommandError('multiple matches') else: raise CommandError(f'{arg} not found') def resolve_system(self, system_str): if type(system_str) == System: return system_str if system_str == '': if not self.has_ship(): return system = self.ship.location.system else: system = self.store.get(System, system_str) return system def resolve_waypoint(self, w): if type(w) == Waypoint: return w if w == '': if not self.has_ship(): return return self.ship.location p = w.split('-') if len(p) == 1: if not self.has_ship(): return s = self.ship.location.system w = f'{s}-{w}' r = self.store.get(Waypoint, w) if r is None: raise CommandError(f'{w} not found') return r def resolve_ship(self, arg): symbol = f'{self.agent.symbol}-{arg}' ship = self.store.get('Ship', symbol) if ship is None: raise CommandError(f'ship {arg} not found') return ship ######## First run ######### def agent_setup(self): symbol = input('agent name: ') agent = self.store.get(Agent, symbol, create=True) self.agent = agent api = Api(self.store, agent) self.api = api faction = input('faction or token: ') if len(faction) > 50: self.agent.token = faction else: self.do_register(faction) print('=== agent:') print(agent) print('=== ships') self.do_ships('r') print('=== contracts') self.do_contracts('r') ship = self.store.get(Ship, symbol.upper() + '-2') print("=== catalog initial system") self.do_catalog(ship.location.system) self.do_stats() self.store.flush() return agent def do_token(self): print(self.agent.token) def do_register(self, faction): self.api.register(faction.upper()) with open(self.cred_file, 'w') as f: f.write(self.api.agent.symbol) f.write('\n') f.write(self.api.agent.token) pprint(self.api.agent) ######## Fleet ######### def do_info(self, arg=''): if arg.startswith('r'): self.api.info() pprint(self.agent, 100) def do_ships(self, arg=''): if arg.startswith('r'): r = self.api.list_ships() else: r = sorted(list(self.store.all('Ship'))) pprint(r) def do_ship(self, arg=''): if arg != '': ship = self.resolve_ship(arg) self.ship = ship pprint(self.ship, 5) ######## Atlas ######### def do_systems(self, page=1): r = self.api.list_systems(int(page)) pprint(self.api.last_meta) def do_catalog(self, system_str=''): system = self.resolve_system(system_str) r = self.api.list_waypoints(system) for w in r: if 'MARKETPLACE' in w.traits: self.api.marketplace(w) if w.type == 'JUMP_GATE': self.api.jumps(w) def do_system(self, system_str): system = self.store.get(System, system_str) r = self.api.list_waypoints(system) pprint(r) def do_waypoints(self, system_str=''): loc = None if system_str == '': if not self.has_ship(): return loc = self.ship.location system = loc.system else: system = self.store.get(System, system_str) print(f'=== waypoints in {system}') r = self.store.all_members(system, 'Waypoint') for w in r: wname = w.symbol.split('-')[2] traits = ", ".join(w.itraits()) typ = w.type[0] if typ not in ['F','J'] and len(traits) == 0: continue if loc: dist = loc.distance(w) print(f'{wname:4} {typ} {dist:6} {traits}') else: print(f'{wname:4} {typ} {traits}') def do_members(self): if not self.has_ship(): return system = self.ship.location.system pprint(list(self.store.all_members(system))) def do_wp(self, s=''): self.do_waypoints(s) ######## Specials ######### def do_market(self, arg=''): waypoint = self.resolve_waypoint(arg) r = self.api.marketplace(waypoint) pprint(r, 3) def do_atlas(self, state=None): atlas = self.store.get(Atlas, 'ATLAS') if state is not None: atlas.enabled = True if state == 'on' else 'off' pprint(atlas, 5) def do_jumps(self, waypoint_str=None): if waypoint_str is None: if not self.has_ship(): return waypoint = self.ship.location else: waypoint = self.store.get(Waypoint, waypoint_str.upper()) r = self.api.jumps(waypoint) pprint(r) def do_shipyard(self, w=''): location = self.resolve_waypoint(w) if location is None: raise CommandError(f'waypoint {w} not found') data = self.api.shipyard(location) if 'ships' in data: for s in must_get(data, 'ships'): print(s['type'], s['purchasePrice']) else: for s in must_get(data, 'shipTypes'): print(s['type']) ######## Commerce ######### def do_refuel(self, source='market'): if not self.has_ship(): return from_cargo = source != 'market' r = self.api.refuel(self.ship, from_cargo=from_cargo) pprint(r) def do_cargo(self): if not self.has_ship(): return print(f'== Cargo {self.ship.cargo_units}/{self.ship.cargo_capacity} ==') for c, units in self.ship.cargo.items(): print(f'{units:4d} {c}') def do_buy(self, resource, amt=None): if not self.has_ship(): return if amt is None: amt = self.ship.cargo_capacity - self.ship.cargo_units self.api.buy(self.ship, resource.upper(), amt) self.do_cargo() def do_sell(self, resource, amt=None): if not self.has_ship(): return self.api.sell(self.ship, resource.upper(), amt) self.do_cargo() def do_dump(self, resource): if not self.has_ship(): return self.api.jettison(self.ship, resource.upper()) self.do_cargo() def do_transfer(self, resource, dship, amount=None): if not self.has_ship(): return resource = resource.upper() avail = self.ship.get_cargo(resource) if amount is None: amount = avail amount = int(amount) if avail < amount: raise CommandError('resource not in cargo') dship = self.resolve_ship(dship) self.api.transfer(self.ship, dship, resource, amount) def do_purchase(self, ship_type): if not self.has_ship(): return location = self.ship.location ship_type = ship_type.upper() if not ship_type.startswith('SHIP'): ship_type = 'SHIP_' + ship_type s = self.api.purchase(ship_type, location) pprint(s) ######## Mining ######### def do_siphon(self): if not self.has_ship(): return data = self.api.siphon(self.ship) def do_survey(self): if not self.has_ship(): return r = self.api.survey(self.ship) pprint(r) def do_surveys(self): pprint(list(self.store.all('Survey'))) def do_extract(self, survey_str=''): if not self.has_ship(): return survey = None if survey_str != '': survey = self.resolve('Survey', survey_str) result = self.api.extract(self.ship, survey) symbol = mg(result,'extraction.yield.symbol') units = mg(result,'extraction.yield.units') print(units, symbol) ######## Missions ######### def print_mission(self): print(f'mission: {self.ship.mission} ({self.ship.mission_status})') pprint(self.ship.mission_state) def do_role(self, role): roles = [None, 'trader', 'probe', 'siphon', 'hauler'] if not self.has_ship(): return if role == 'none': role = None if role not in roles: print(f'role {role} not found. Choose from {roles}') return self.ship.role = role def do_mission(self, arg=''): if not self.has_ship(): return if arg: self.centcom.init_mission(self.ship, arg) self.print_mission() def do_mrestart(self, status='init'): if not self.has_ship(): return self.centcom.restart_mission(self.ship, status) self.print_mission() def do_mstep(self): if not self.has_ship(): return self.centcom.single_step(self.ship) self.print_mission() def do_mreset(self): if not self.has_ship(): return self.ship.mission_state = {} def do_mset(self, nm, val): if not self.has_ship(): return self.centcom.set_mission_param(self.ship, nm, val) ######## Contracts ######### def active_contract(self): for c in self.store.all('Contract'): if c.accepted and not c.fulfilled: return c raise CommandError('no active contract') def do_contracts(self, arg=''): if arg.startswith('r'): r = self.api.list_contracts() else: r = list(self.store.all('Contract')) pprint(r) def do_negotiate(self): if not self.has_ship(): return r = self.api.negotiate(self.ship) pprint(r) def do_accept(self, c): contract = self.resolve('Contract', c) r = self.api.accept_contract(contract) pprint(r) def do_deliver(self): if not self.has_ship(): return site = self.ship.location contract = self.active_contract() delivery = contract.unfinished_delivery() if delivery is None: raise CommandError('no delivery') resource = delivery['trade_symbol'] self.api.deliver(self.ship, resource, contract) pprint(contract) def do_fulfill(self): contract = self.active_contract() self.api.fulfill(contract) ######## Automissions ######### def do_cmine(self): if not self.has_ship(): return site = self.ship.location contract = self.active_contract() delivery = contract.unfinished_delivery() if delivery is None: raise CommandError('no delivery') resource = delivery['trade_symbol'] destination = delivery['destination'] self.centcom.init_mission(self.ship, 'mine') self.centcom.set_mission_param(self.ship, 'site', site) self.centcom.set_mission_param(self.ship, 'resource', resource) self.centcom.set_mission_param(self.ship, 'dest', destination) self.centcom.set_mission_param(self.ship, 'contract', contract) self.print_mission() def do_chaul(self): if not self.has_ship(): return if len(self.ship.cargo) > 0: raise CommandError('please dump cargo first') contract = self.active_contract() delivery = contract.unfinished_delivery() if delivery is None: raise CommandError('no delivery') resource = delivery['trade_symbol'] destination = delivery['destination'] m = self.analyzer.find_closest_markets(resource, 'buy', destination) if len(m) == 0: m = self.analyzer.find_closest_markets(resource, 'exchange', destination) if len(m) == 0: print('no market found') return _, m, _, _ = m[0] site = self.store.get(Waypoint, m.symbol) self.centcom.init_mission(self.ship, 'trade') self.centcom.set_mission_param(self.ship, 'site', site) self.centcom.set_mission_param(self.ship, 'resource', resource) self.centcom.set_mission_param(self.ship, 'dest', destination) self.centcom.set_mission_param(self.ship, 'contract', contract) self.print_mission() def do_cprobe(self): if not self.has_ship(): return contract = self.active_contract() delivery = contract.unfinished_delivery() if delivery is None: raise CommandError('no delivery') resource = delivery['trade_symbol'] destination = delivery['destination'] m = self.analyzer.find_closest_markets(resource, 'buy,exchange', destination) if len(m) == 0: print('no market found') return markets = [ mkt[1] for mkt in m] markets = self.analyzer.solve_tsp(markets) self.centcom.init_mission(self.ship, 'probe') self.centcom.set_mission_param(self.ship, 'hops', markets) self.print_mission() ######## Travel ######### def do_travel(self, dest): dest = self.resolve('Waypoint', dest) self.centcom.init_mission(self.ship, 'travel') self.centcom.set_mission_param(self.ship, 'dest', dest) self.print_mission() def do_go(self, arg): if not self.has_ship(): return system = self.ship.location.system symbol = f'{system}-{arg}' dest = self.resolve('Waypoint', symbol) self.api.navigate(self.ship, dest) pprint(self.ship) def do_dock(self): if not self.has_ship(): return self.api.dock(self.ship) pprint(self.ship) def do_orbit(self): if not self.has_ship(): return self.api.orbit(self.ship) pprint(self.ship) def do_speed(self, speed): if not self.has_ship(): return speed = speed.upper() speeds = ['DRIFT', 'STEALTH','CRUISE','BURN'] if speed not in speeds: print('please choose from:', speeds) self.api.flight_mode(self.ship, speed) def do_jump(self, waypoint_str): if not self.has_ship(): return w = self.resolve('Waypoint', waypoint_str) self.api.jump(self.ship, w) pprint(self.ship) ######## Analysis ######### def do_stats(self): total = 0 for t in self.store.data: num = len(self.store.data[t]) nam = t.__name__ total += num print(f'{num:5d} {nam}') print(f'{total:5d} total') def do_defrag(self): self.store.defrag() def do_obj(self, oid): if not '.' in oid: print('Usage: obj SYMBOL.ext') return symbol, ext = oid.split('.') symbol = symbol.upper() if not ext in self.store.extensions: print('unknown extension') typ = self.store.extensions[ext] obj = self.store.get(typ, symbol) if obj is None: print('object not found') pprint(obj.__getstate__()) print('=== store ===') h = self.store.get_header(obj) if h: pprint(h, 3) else: print('Not stored') print('Dirty: ', obj in self.store.dirty_objects) def do_query(self, resource): if not self.has_ship(): return location = self.ship.location resource = resource.upper() print('Found markets:') for typ, m, d, plen in self.analyzer.find_closest_markets(resource, 'buy,exchange',location): price = '?' if resource in m.prices: price = m.prices[resource]['buy'] print(m, typ[0], f'{plen-1:3} hops {price}') def do_findtrade(self): if not self.has_ship(): return system = self.ship.location.system t = self.analyzer.find_trade(system) pprint(t) def do_prices(self, resource=None): if not self.has_ship(): return system = self.ship.location.system prices = self.analyzer.prices(system) if resource is not None: prices = {resource: prices[resource.upper()]} for res, p in prices.items(): print('==' + res) for m in p: print(f"{m['wp'].symbol:12s} {m['category']} {m['volume']:5d} {m['buy']:5d} {m['sell']:5d}") def do_path(self, waypoint_str): if not self.has_ship(): return w = self.resolve('Waypoint', waypoint_str) p = self.analyzer.find_nav_path(self.ship.location, w, self.ship.fuel_capacity) pprint(p)