From bcbf37599eb7602b92b80d1d9037ece81338c9d1 Mon Sep 17 00:00:00 2001 From: Athanasius Date: Tue, 18 May 2021 16:39:06 +0100 Subject: [PATCH] Odyssey Backpack: Add support for v31 `Backpack` and `BackpackChange` Leaving the, meant to be defunct, `BackPackMaterials` code in for now, as it's only one extra conditional that will never be stepped into if the event never happens. Yes, Frontier now uses 'Backpack' not 'BackPack', but we've already published our plugin API as using the CamelCase version, so not changing the monitor.state key. --- monitor.py | 76 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 76 insertions(+) diff --git a/monitor.py b/monitor.py index d496a2ff..0da62cc9 100644 --- a/monitor.py +++ b/monitor.py @@ -835,6 +835,7 @@ class EDLogs(FileSystemEventHandler): # type: ignore # See below {self.canonicalise(x['Name']): x['Count'] for x in clean_data} ) + # Journal v31 implies this was removed before Odyssey launch elif event_type == 'BackPackMaterials': # alpha4 - # Lists the contents of the backpack, eg when disembarking from ship @@ -865,6 +866,76 @@ class EDLogs(FileSystemEventHandler): # type: ignore # See below {self.canonicalise(x['Name']): x['Count'] for x in clean_data} ) + elif event_type == 'Backpack': + # TODO: v31 doc says this is`backpack.json` ... but Howard Chalkley + # said it's `Backpack.json` + with open(join(self.currentdir, 'Backpack.json'), 'rb') as backpack: # type: ignore + try: + entry = json.load(backpack) + + except json.JSONDecodeError: + logger.exception('Failed decoding Backpack.json', exc_info=True) + + else: + # Assume this reflects the current state when written + self.state['BackPack']['Component'] = defaultdict(int) + self.state['BackPack']['Consumable'] = defaultdict(int) + self.state['BackPack']['Item'] = defaultdict(int) + self.state['BackPack']['Data'] = defaultdict(int) + + clean_components = self.coalesce_cargo(entry['Components']) + self.state['BackPack']['Component'].update( + {self.canonicalise(x['Name']): x['Count'] for x in clean_components} + ) + + clean_consumables = self.coalesce_cargo(entry['Consumables']) + self.state['BackPack']['Consumable'].update( + {self.canonicalise(x['Name']): x['Count'] for x in clean_consumables} + ) + + clean_items = self.coalesce_cargo(entry['Items']) + self.state['BackPack']['Item'].update( + {self.canonicalise(x['Name']): x['Count'] for x in clean_items} + ) + + clean_data = self.coalesce_cargo(entry['Data']) + self.state['BackPack']['Data'].update( + {self.canonicalise(x['Name']): x['Count'] for x in clean_data} + ) + + elif event_type == 'BackpackChange': + # Changes to Odyssey Backpack contents *other* than from a Transfer + # See TransferMicroResources event for that. + + if entry.get('Added') is not None: + changes = 'Added' + + elif entry.get('Removed') is not None: + changes = 'Removed' + + else: + logger.warning(f'BackpackChange with neither Added nor Removed: {entry=}') + changes = '' + + if changes != '': + for c in entry[changes]: + category = self.category(c['Type']) + name = self.canonicalise(c['Name']) + + if changes == 'Removed': + self.state['BackPack'][category][name] -= c['Count'] + + elif changes == 'Added': + self.state['BackPack'][category][name] += c['Count'] + + # Paranoia check to see if anything has gone negative. + # As of Odyssey Alpha Phase 1 Hotfix 2 keeping track of BackPack + # materials is impossible when used/picked up anyway. + for c in self.state['BackPack']: + for m in self.state['BackPack'][c]: + if self.state['BackPack'][c][m] < 0: + self.state['BackPack'][c][m] = 0 + elif event_type == 'BuyMicroResources': # Buying from a Pioneer Supplies, goes directly to ShipLocker. # One event per Item, not an array. @@ -961,6 +1032,11 @@ class EDLogs(FileSystemEventHandler): # type: ignore # See below if self.state['BackPack']['Consumable'][c] < 0: self.state['BackPack']['Consumable'][c] = 0 + # TODO: + # + # also there's one additional journal event that was missed out from + # this version of the docs: "SuitLoadout": # when starting on foot, or + # when disembarking from a ship, with the same info as found in "CreateSuitLoadout" elif event_type == 'SwitchSuitLoadout': loadoutid = entry['LoadoutID'] new_slot = self.suit_loadout_id_from_loadoutid(loadoutid)