mirror of
https://github.com/EDCD/EDMarketConnector.git
synced 2025-06-05 09:53:33 +03:00
Merge pull request #1161 from EDCD/enhancement/odyssey-inventory-4.0.0.400
Align ShipLocker and BackPack code with ED 4.0.0.400
This commit is contained in:
commit
3005991a3c
@ -603,6 +603,7 @@ Content of `state` (updated to the current journal entry):
|
||||
| `Data` | `dict` | 'Data' MicroResources in Odyssey, `int` count each. |
|
||||
| `BackPack` | `dict` | `dict` of Odyssey MicroResources in backpack. |
|
||||
| `BackpackJSON` | `dict` | Content of Backpack.json as of last read. |
|
||||
| `ShipLockerJSON` | `dict` | Content of ShipLocker.json as of last read. |
|
||||
| `SuitCurrent` | `dict` | CAPI-returned data of currently worn suit. NB: May be `None` if no data. |
|
||||
| `Suits` | `dict`[1] | CAPI-returned data of owned suits. NB: May be `None` if no data. |
|
||||
| `SuitLoadoutCurrent` | `dict` | CAPI-returned data of current Suit Loadout. NB: May be `None` if no data. |
|
||||
@ -672,6 +673,11 @@ New in version 5.1.0:
|
||||
|
||||
`state` entries added for Taxi, Dropship, Body and BodyType.
|
||||
|
||||
New in version 5.1.1:
|
||||
|
||||
`state` now has a `ShipLockerJSON` member containing the un-changed, loaded,
|
||||
JSON from the `ShipLockerJSON.json` file.
|
||||
|
||||
##### Synthetic Events
|
||||
|
||||
A special "StartUp" entry is sent if EDMC is started while the game is already
|
||||
|
202
monitor.py
202
monitor.py
@ -157,6 +157,7 @@ class EDLogs(FileSystemEventHandler): # type: ignore # See below
|
||||
'Data': defaultdict(int), # Backpack Data
|
||||
},
|
||||
'BackpackJSON': None, # Raw JSON from `Backpack.json` file, if available
|
||||
'ShipLockerJSON': None, # Raw JSON from the `ShipLocker.json` file, if available
|
||||
'SuitCurrent': None,
|
||||
'Suits': {},
|
||||
'SuitLoadoutCurrent': None,
|
||||
@ -681,6 +682,10 @@ class EDLogs(FileSystemEventHandler): # type: ignore # See below
|
||||
self.state['OnFoot'] = False
|
||||
self.state['Taxi'] = entry['Taxi']
|
||||
|
||||
# We can't now have anything in the BackPack, it's all in the
|
||||
# ShipLocker.
|
||||
self.backpack_set_empty()
|
||||
|
||||
elif event_type == 'Disembark':
|
||||
# This event is logged when the player steps out of a ship or SRV
|
||||
#
|
||||
@ -843,21 +848,40 @@ class EDLogs(FileSystemEventHandler): # type: ignore # See below
|
||||
# So it's *from* the ship
|
||||
self.state['Cargo'][name] -= c['Count']
|
||||
|
||||
elif event_type == 'ShipLockerMaterials':
|
||||
elif event_type == 'ShipLocker':
|
||||
# As of 4.0.0.400 (2021-06-10)
|
||||
# "ShipLocker" will be a full list written to the journal at startup/boarding/disembarking, and also
|
||||
# written to a separate shiplocker.json file - other updates will just update that file and mention it
|
||||
# has changed with an empty shiplocker event in the main journal.
|
||||
|
||||
# Always attempt loading of this.
|
||||
# Confirmed filename for 4.0.0.400
|
||||
try:
|
||||
currentdir_path = pathlib.Path(str(self.currentdir))
|
||||
with open(currentdir_path / 'ShipLocker.json', 'rb') as h: # type: ignore
|
||||
entry = json.load(h, object_pairs_hook=OrderedDict)
|
||||
self.state['ShipLockerJSON'] = entry
|
||||
|
||||
except FileNotFoundError:
|
||||
logger.warning('ShipLocker event but no ShipLocker.json file')
|
||||
pass
|
||||
|
||||
except json.JSONDecodeError as e:
|
||||
logger.warning(f'ShipLocker.json failed to decode:\n{e!r}\n')
|
||||
pass
|
||||
|
||||
if not all(t in entry for t in ('Components', 'Consumables', 'Data', 'Items')):
|
||||
logger.trace('ShipLocker event is an empty one (missing at least one data type)')
|
||||
|
||||
# This event has the current totals, so drop any current data
|
||||
self.state['Component'] = defaultdict(int)
|
||||
self.state['Consumable'] = defaultdict(int)
|
||||
self.state['Item'] = defaultdict(int)
|
||||
self.state['Data'] = defaultdict(int)
|
||||
# TODO: Really we need a full BackPackMaterials event at the same time.
|
||||
# In lieu of that, empty the backpack. This will explicitly
|
||||
# be wrong if Cmdr relogs at a Settlement with anything in
|
||||
# backpack.
|
||||
# Still no BackPackMaterials at the same time in 4.0.0.31
|
||||
self.state['BackPack']['Component'] = defaultdict(int)
|
||||
self.state['BackPack']['Consumable'] = defaultdict(int)
|
||||
self.state['BackPack']['Item'] = defaultdict(int)
|
||||
self.state['BackPack']['Data'] = defaultdict(int)
|
||||
|
||||
# 4.0.0.400 - No longer zeroing out the BackPack in this event,
|
||||
# as we should now always get either `Backpack` event/file or
|
||||
# `BackpackChange` as needed.
|
||||
|
||||
clean_components = self.coalesce_cargo(entry['Components'])
|
||||
self.state['Component'].update(
|
||||
@ -881,34 +905,9 @@ class EDLogs(FileSystemEventHandler): # type: ignore # See below
|
||||
|
||||
# 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
|
||||
|
||||
# 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}
|
||||
)
|
||||
# Last seen in a 4.0.0.102 journal file.
|
||||
logger.warning(f'We have a BackPackMaterials event, defunct since > 4.0.0.102 ?:\n{entry}\n')
|
||||
pass
|
||||
|
||||
elif event_type in ('BackPack', 'Backpack'): # WORKAROUND 4.0.0.200: BackPack becomes Backpack
|
||||
# TODO: v31 doc says this is`backpack.json` ... but Howard Chalkley
|
||||
@ -943,10 +942,7 @@ class EDLogs(FileSystemEventHandler): # type: ignore # See below
|
||||
self.state['BackpackJSON'] = entry
|
||||
|
||||
# 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)
|
||||
self.backpack_set_empty()
|
||||
|
||||
clean_components = self.coalesce_cargo(entry['Components'])
|
||||
self.state['BackPack']['Component'].update(
|
||||
@ -1002,111 +998,22 @@ class EDLogs(FileSystemEventHandler): # type: ignore # See below
|
||||
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.
|
||||
category = self.category(entry['Category'])
|
||||
name = self.canonicalise(entry['Name'])
|
||||
self.state[category][name] += entry['Count']
|
||||
# From 4.0.0.400 we get an empty (see file) `ShipLocker` event,
|
||||
# so we can ignore this for inventory purposes.
|
||||
|
||||
# But do record the credits balance change.
|
||||
self.state['Credits'] -= entry.get('Price', 0)
|
||||
|
||||
elif event_type == 'SellMicroResources':
|
||||
# Selling to a Bar Tender on-foot.
|
||||
# As of 4.0.0.400 we can ignore this as an empty (see file)
|
||||
# `ShipLocker` event is written for the full new inventory.
|
||||
|
||||
# But still record the credits balance change.
|
||||
self.state['Credits'] += entry.get('Price', 0)
|
||||
# One event per whole sale, so it's an array.
|
||||
for mr in entry['MicroResources']:
|
||||
category = self.category(mr['Category'])
|
||||
name = self.canonicalise(mr['Name'])
|
||||
self.state[category][name] -= mr['Count']
|
||||
|
||||
elif event_type == 'TradeMicroResources':
|
||||
# Trading some MicroResources for another at a Bar Tender
|
||||
# 'Offered' is what we traded away
|
||||
for offer in entry['Offered']:
|
||||
category = self.category(offer['Category'])
|
||||
name = self.canonicalise(offer['Name'])
|
||||
self.state[category][name] -= offer['Count']
|
||||
|
||||
# For a single item name received
|
||||
category = self.category(entry['Category'])
|
||||
name = self.canonicalise(entry['Received'])
|
||||
self.state[category][name] += entry['Count']
|
||||
|
||||
elif event_type == 'TransferMicroResources':
|
||||
# Moving Odyssey MicroResources between ShipLocker and BackPack
|
||||
# Backpack dropped as its done in BackpackChange
|
||||
#
|
||||
# from: 4.0.0.200 -- Locker(Old|New)Count is now a thing.
|
||||
for mr in entry['Transfers']:
|
||||
category = self.category(mr['Category'])
|
||||
name = self.canonicalise(mr['Name'])
|
||||
|
||||
self.state[category][name] = mr['LockerNewCount']
|
||||
if mr['Direction'] not in ('ToShipLocker', 'ToBackpack'):
|
||||
logger.warning(f'TransferMicroResources with unexpected Direction {mr["Direction"]=}: {mr=}')
|
||||
|
||||
# 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 == 'CollectItems':
|
||||
# alpha4
|
||||
# When picking up items from the ground
|
||||
# Parameters:
|
||||
# • Name
|
||||
# • Type
|
||||
# • OwnerID
|
||||
|
||||
# Handled by BackpackChange
|
||||
# for i in self.state['BackPack'][entry['Type']]:
|
||||
# if i == entry['Name']:
|
||||
# self.state['BackPack'][entry['Type']][i] += entry['Count']
|
||||
pass
|
||||
|
||||
elif event_type == 'DropItems':
|
||||
# alpha4
|
||||
# Parameters:
|
||||
# • Name
|
||||
# • Type
|
||||
# • OwnerID
|
||||
# • MissionID
|
||||
# • Count
|
||||
|
||||
# This is handled by BackpackChange.
|
||||
# for i in self.state['BackPack'][entry['Type']]:
|
||||
# if i == entry['Name']:
|
||||
# self.state['BackPack'][entry['Type']][i] -= entry['Count']
|
||||
# # Paranoia in case we lost track
|
||||
# if self.state['BackPack'][entry['Type']][i] < 0:
|
||||
# self.state['BackPack'][entry['Type']][i] = 0
|
||||
pass
|
||||
|
||||
elif event_type == 'UseConsumable':
|
||||
# TODO: XXX: From v31 doc
|
||||
# 12.2 BackpackChange
|
||||
# This is written when there is any change to the contents of the
|
||||
# suit backpack – note this can be written at the same time as other
|
||||
# events like UseConsumable
|
||||
|
||||
# In 4.0.0.100 it is observed that:
|
||||
#
|
||||
# 1. Throw of any grenade type *only* causes a BackpackChange event, no
|
||||
# accompanying 'UseConsumable'.
|
||||
# 2. Using an Energy Cell causes both UseConsumable and BackpackChange,
|
||||
# in that order.
|
||||
# 3. Medkit acts the same as Energy Cell.
|
||||
#
|
||||
# Thus we'll just ignore 'UseConsumable' for now.
|
||||
# for c in self.state['BackPack']['Consumable']:
|
||||
# if c == entry['Name']:
|
||||
# self.state['BackPack']['Consumable'][c] -= 1
|
||||
# # Paranoia in case we lost track
|
||||
# if self.state['BackPack']['Consumable'][c] < 0:
|
||||
# self.state['BackPack']['Consumable'][c] = 0
|
||||
elif event_type in ('TradeMicroResources', 'CollectItems', 'DropItems', 'UseConsumable'):
|
||||
# As of 4.0.0.400 we can ignore these as an empty (see file)
|
||||
# `ShipLocker` event and/or a `BackpackChange` is also written.
|
||||
pass
|
||||
|
||||
# TODO:
|
||||
@ -1321,8 +1228,7 @@ class EDLogs(FileSystemEventHandler): # type: ignore # See below
|
||||
elif event_type == 'UpgradeWeapon':
|
||||
# We're not actually keeping track of all owned weapons, only those in
|
||||
# Suit Loadouts.
|
||||
# alpha4 - credits? Shouldn't cost any!
|
||||
pass
|
||||
self.state['Credits'] -= entry.get('Cost', 0)
|
||||
|
||||
elif event_type == 'ScanOrganic':
|
||||
# Nothing of interest to our state.
|
||||
@ -1649,6 +1555,9 @@ class EDLogs(FileSystemEventHandler): # type: ignore # See below
|
||||
elif event_type == 'Resurrect':
|
||||
self.state['Credits'] -= entry.get('Cost', 0)
|
||||
|
||||
# There should be a `Backpack` event as you 'come to' in the
|
||||
# new location, so no need to zero out BackPack here.
|
||||
|
||||
# HACK (not game related / 2021-06-2): self.planet is moved into a more general self.state['Body'].
|
||||
# This exists to help plugins doing what they SHOULDN'T BE cope. It will be removed at some point.
|
||||
if self.state['Body'] is None or self.state['BodyType'] == 'Planet':
|
||||
@ -1660,6 +1569,13 @@ class EDLogs(FileSystemEventHandler): # type: ignore # See below
|
||||
logger.debug(f'Invalid journal entry:\n{line!r}\n', exc_info=ex)
|
||||
return {'event': None}
|
||||
|
||||
def backpack_set_empty(self):
|
||||
"""Set the BackPack contents to be empty."""
|
||||
self.state['BackPack']['Component'] = defaultdict(int)
|
||||
self.state['BackPack']['Consumable'] = defaultdict(int)
|
||||
self.state['BackPack']['Item'] = defaultdict(int)
|
||||
self.state['BackPack']['Data'] = defaultdict(int)
|
||||
|
||||
def suit_sane_name(self, name: str) -> str:
|
||||
"""
|
||||
Given an input suit name return the best 'sane' name we can.
|
||||
|
@ -1098,7 +1098,14 @@ def journal_entry( # noqa: C901, CCR001
|
||||
|
||||
new_add_event('addCommanderTravelLand', entry['timestamp'], to_send_data)
|
||||
|
||||
elif event_name == 'ShipLockerMaterials':
|
||||
elif event_name == 'ShipLocker':
|
||||
# In ED 4.0.0.400 the event is only full sometimes, other times indicating
|
||||
# ShipLocker.json was written.
|
||||
if not all(t in entry for t in ('Components', 'Consumables', 'Data', 'Items')):
|
||||
# So it's an empty event, core EDMC should have stuffed the data
|
||||
# into state['ShipLockerJSON'].
|
||||
entry = state['ShipLockerJSON']
|
||||
|
||||
odyssey_plural_microresource_types = ('Items', 'Components', 'Data', 'Consumables')
|
||||
# we're getting new data here. so reset it on inara's side just to be sure that we set everything right
|
||||
reset_data = [{'itemType': t} for t in odyssey_plural_microresource_types]
|
||||
|
Loading…
x
Reference in New Issue
Block a user