2023-06-18 17:15:51 +00:00
|
|
|
from nullptr.store import Store
|
2023-06-17 18:18:14 +00:00
|
|
|
from nullptr.models.ship import Ship
|
2023-06-25 15:35:06 +00:00
|
|
|
from nullptr.missions import create_mission, get_mission_class
|
2023-06-17 18:18:14 +00:00
|
|
|
from random import choice
|
|
|
|
from time import sleep
|
2023-06-18 17:15:51 +00:00
|
|
|
from threading import Thread
|
2023-07-16 16:48:45 +00:00
|
|
|
from nullptr.atlas_builder import AtlasBuilder
|
2024-01-04 20:34:31 +00:00
|
|
|
from nullptr.analyzer import Analyzer
|
2023-06-17 18:18:14 +00:00
|
|
|
|
2023-06-25 15:35:06 +00:00
|
|
|
class CentralCommandError(Exception):
|
|
|
|
pass
|
|
|
|
|
2023-06-17 18:18:14 +00:00
|
|
|
class CentralCommand:
|
2023-06-18 17:15:51 +00:00
|
|
|
def __init__(self, store, api):
|
2023-06-17 18:18:14 +00:00
|
|
|
self.missions = {}
|
|
|
|
self.stopping = False
|
2023-06-18 17:15:51 +00:00
|
|
|
self.store = store
|
2024-01-04 20:34:31 +00:00
|
|
|
self.analyzer = Analyzer(store)
|
2023-06-17 18:18:14 +00:00
|
|
|
self.api = api
|
2023-07-16 16:48:45 +00:00
|
|
|
self.atlas_builder = AtlasBuilder(store, api)
|
2023-06-17 18:18:14 +00:00
|
|
|
|
|
|
|
def get_ready_missions(self):
|
|
|
|
result = []
|
|
|
|
for ship, mission in self.missions.items():
|
|
|
|
if mission.is_ready():
|
|
|
|
result.append(ship)
|
|
|
|
return result
|
|
|
|
|
|
|
|
def tick(self):
|
2024-01-04 20:34:31 +00:00
|
|
|
self.update_missions()
|
2023-06-17 18:18:14 +00:00
|
|
|
missions = self.get_ready_missions()
|
2023-06-18 17:15:51 +00:00
|
|
|
if len(missions) == 0: return False
|
2023-06-17 18:18:14 +00:00
|
|
|
ship = choice(missions)
|
|
|
|
mission = self.missions[ship]
|
|
|
|
mission.step()
|
2023-06-18 17:15:51 +00:00
|
|
|
return True
|
|
|
|
|
|
|
|
def run_interactive(self):
|
|
|
|
print('auto mode. hit enter to stop')
|
|
|
|
t = Thread(target=self.wait_for_stop)
|
|
|
|
t.daemon = True
|
|
|
|
t.start()
|
|
|
|
self.run()
|
|
|
|
print('manual mode')
|
2023-07-03 17:12:26 +00:00
|
|
|
|
|
|
|
def wait_for_stop(self):
|
|
|
|
try:
|
|
|
|
input()
|
|
|
|
except EOFError:
|
|
|
|
pass
|
|
|
|
self.stopping = True
|
|
|
|
print('stopping...')
|
|
|
|
|
|
|
|
|
2023-06-17 18:18:14 +00:00
|
|
|
def run(self):
|
|
|
|
while not self.stopping:
|
2023-06-18 17:15:51 +00:00
|
|
|
did_step = True
|
|
|
|
request_counter = self.api.requests_sent
|
|
|
|
while request_counter == self.api.requests_sent and did_step:
|
|
|
|
did_step = self.tick()
|
2023-07-16 16:48:45 +00:00
|
|
|
if request_counter == self.api.requests_sent:
|
|
|
|
self.atlas_builder.do_work()
|
2023-07-18 10:43:31 +00:00
|
|
|
else:
|
2023-07-18 10:44:53 +00:00
|
|
|
pass # print('nowork')
|
2023-07-16 16:48:45 +00:00
|
|
|
|
2023-06-18 17:15:51 +00:00
|
|
|
self.store.flush()
|
2023-06-17 18:18:14 +00:00
|
|
|
sleep(0.5)
|
|
|
|
self.stopping = False
|
|
|
|
|
|
|
|
def stop(self):
|
|
|
|
self.stopping = True
|
|
|
|
|
|
|
|
def set_mission_param(self, ship, nm, val):
|
|
|
|
if ship not in self.missions:
|
|
|
|
print('set a mission for this ship first')
|
|
|
|
return
|
|
|
|
mission = self.missions[ship]
|
|
|
|
params = mission.params()
|
|
|
|
if not nm in params:
|
|
|
|
print(f'{nm} is not a valid param')
|
|
|
|
return
|
|
|
|
param = params[nm]
|
|
|
|
try:
|
2023-06-18 17:15:51 +00:00
|
|
|
parsed_val = param.parse(val, self.store)
|
2023-06-17 18:18:14 +00:00
|
|
|
except ValueError as e:
|
2023-06-18 17:15:51 +00:00
|
|
|
raise MissionError(e)
|
2023-06-17 18:18:14 +00:00
|
|
|
return
|
2023-06-18 17:15:51 +00:00
|
|
|
ship.set_mission_state(nm, parsed_val)
|
2024-01-04 20:34:31 +00:00
|
|
|
|
|
|
|
def smipa(self,s,n,v):
|
|
|
|
self.set_mission_param(s,n,v)
|
2023-06-17 18:18:14 +00:00
|
|
|
|
|
|
|
def update_missions(self):
|
2023-06-18 17:15:51 +00:00
|
|
|
for s in self.store.all(Ship):
|
2024-01-04 20:34:31 +00:00
|
|
|
if s.mission_status == 'done':
|
|
|
|
s.mission = None
|
2023-06-17 18:18:14 +00:00
|
|
|
if s.mission is None:
|
|
|
|
if s in self.missions:
|
|
|
|
self.stop_mission(s)
|
2024-01-04 20:34:31 +00:00
|
|
|
if s.mission is None:
|
|
|
|
self.assign_mission(s)
|
|
|
|
if s.mission is not None and s not in self.missions:
|
2023-06-17 18:18:14 +00:00
|
|
|
self.start_mission(s)
|
2023-06-18 18:02:33 +00:00
|
|
|
if s in self.missions:
|
|
|
|
m = self.missions[s]
|
2024-01-04 20:34:31 +00:00
|
|
|
|
|
|
|
|
|
|
|
def assign_mission(self, s):
|
|
|
|
if s.role == 'hauler':
|
|
|
|
self.assign_haul(s)
|
2023-06-17 18:18:14 +00:00
|
|
|
|
2024-01-04 20:34:31 +00:00
|
|
|
def assign_haul(self, s):
|
|
|
|
t = self.analyzer.find_trade(s.location.system)
|
|
|
|
if t is None:
|
|
|
|
print(f"No trade for {s} found. Idling")
|
|
|
|
self.init_mission(s,'idle')
|
|
|
|
self.smipa(s, 'seconds', 600)
|
|
|
|
return
|
|
|
|
print(f'assigning {s} to deliver {t.resource} from {t.source} to {t.dest} at a margin of {t.margin}')
|
|
|
|
self.init_mission(s, 'haul')
|
|
|
|
self.smipa(s, 'resource', t.resource)
|
|
|
|
self.smipa(s, 'site', t.source)
|
|
|
|
self.smipa(s, 'dest', t.dest)
|
|
|
|
self.smipa(s, 'delivery', 'sell')
|
|
|
|
|
2023-06-25 15:35:06 +00:00
|
|
|
def init_mission(self, s, mtyp):
|
|
|
|
if mtyp == 'none':
|
|
|
|
s.mission_state = {}
|
|
|
|
s.mission_status = None
|
|
|
|
s.mission = None
|
|
|
|
return
|
|
|
|
try:
|
|
|
|
mclass = get_mission_class(mtyp)
|
|
|
|
except ValueError:
|
|
|
|
raise CentralCommandError('no such mission')
|
|
|
|
s.mission = mtyp
|
|
|
|
s.mission_status = 'init'
|
|
|
|
s.mission_state = {k: v.default for k,v in mclass.params().items()}
|
|
|
|
self.start_mission(s)
|
2024-01-04 20:34:31 +00:00
|
|
|
|
2024-01-06 06:17:53 +00:00
|
|
|
def restart_mission(self, s, status='init'):
|
2024-01-04 20:34:31 +00:00
|
|
|
if s not in self.missions:
|
|
|
|
raise CentralCommandError("no mission assigned")
|
2024-01-06 06:17:53 +00:00
|
|
|
s.mission_status = status
|
2023-06-25 15:35:06 +00:00
|
|
|
|
2023-06-17 18:18:14 +00:00
|
|
|
def start_mission(self, s):
|
|
|
|
mtype = s.mission
|
2023-06-18 17:15:51 +00:00
|
|
|
m = create_mission(mtype, s, self.store, self.api)
|
2023-06-17 18:18:14 +00:00
|
|
|
self.missions[s] = m
|
|
|
|
return m
|
|
|
|
|
|
|
|
def stop_mission(self, s):
|
|
|
|
if s in self.missions:
|
|
|
|
del self.missions[s]
|