[cli/core/models] Fix a whole bunch of cloud save issues

- Games that didn't already have cloud saves weren't synced
- Games that didn't have local saves didn't behave correctly
- Games that only had local saves also didn't work correctly
This commit is contained in:
derrod 2020-05-16 12:50:28 +02:00
parent 03c503b4e6
commit 730eaebec9
3 changed files with 35 additions and 16 deletions

View file

@ -251,15 +251,14 @@ class LegendaryCLI:
# evaluate current save state for each game. # evaluate current save state for each game.
for igame in igames: for igame in igames:
if igame.app_name not in latest_save:
continue
game = self.core.get_game(igame.app_name) game = self.core.get_game(igame.app_name)
if 'CloudSaveFolder' not in game.metadata['customAttributes']: if 'CloudSaveFolder' not in game.metadata['customAttributes']:
# this should never happen unless cloud save support was removed from a game if igame.app_name in latest_save:
logger.warning(f'{igame.app_name} has remote save(s) but does not support cloud saves?!') # this should never happen unless cloud save support was removed from a game
logger.warning(f'{igame.app_name} has remote save(s) but does not support cloud saves?!')
continue continue
logger.info(f'Checking "{igame.title}" ({igame.app_name})')
# override save path only if app name is specified # override save path only if app name is specified
if args.app_name and args.save_path: if args.app_name and args.save_path:
logger.info(f'Overriding save path with "{args.save_path}"...') logger.info(f'Overriding save path with "{args.save_path}"...')
@ -290,8 +289,11 @@ class LegendaryCLI:
igame.save_path = save_path igame.save_path = save_path
self.core.lgd.set_installed_game(igame.app_name, igame) self.core.lgd.set_installed_game(igame.app_name, igame)
# check if *any* file in the save game directory is newer than the latest uploaded save res, (dt_l, dt_r) = self.core.check_savegame_state(igame.save_path, latest_save.get(igame.app_name))
res, (dt_l, dt_r) = self.core.check_savegame_state(igame.save_path, latest_save[igame.app_name])
if res == SaveGameStatus.NO_SAVE:
logger.info('No cloud or local savegame found.')
continue
if res == SaveGameStatus.SAME_AGE and not (args.force_upload or args.force_download): if res == SaveGameStatus.SAME_AGE and not (args.force_upload or args.force_download):
logger.info(f'Save game for "{igame.title}" is up to date, skipping...') logger.info(f'Save game for "{igame.title}" is up to date, skipping...')
@ -300,8 +302,11 @@ class LegendaryCLI:
if (res == SaveGameStatus.REMOTE_NEWER and not args.force_upload) or args.force_download: if (res == SaveGameStatus.REMOTE_NEWER and not args.force_upload) or args.force_download:
if res == SaveGameStatus.REMOTE_NEWER: # only print this info if not forced if res == SaveGameStatus.REMOTE_NEWER: # only print this info if not forced
logger.info(f'Cloud save for "{igame.title}" is newer:') logger.info(f'Cloud save for "{igame.title}" is newer:')
logger.info(f'- Cloud save date: {dt_l.strftime("%Y-%m-%d %H:%M:%S")}') logger.info(f'- Cloud save date: {dt_r.strftime("%Y-%m-%d %H:%M:%S")}')
logger.info(f'- Local save date: {dt_r.strftime("%Y-%m-%d %H:%M:%S")}') if dt_l:
logger.info(f'- Local save date: {dt_l.strftime("%Y-%m-%d %H:%M:%S")}')
else:
logger.info('- Local save date: N/A')
if args.upload_only: if args.upload_only:
logger.info('Save game downloading is disabled, skipping...') logger.info('Save game downloading is disabled, skipping...')
@ -319,8 +324,11 @@ class LegendaryCLI:
elif res == SaveGameStatus.LOCAL_NEWER or args.force_upload: elif res == SaveGameStatus.LOCAL_NEWER or args.force_upload:
if res == SaveGameStatus.LOCAL_NEWER: if res == SaveGameStatus.LOCAL_NEWER:
logger.info(f'Local save for "{igame.title}" is newer') logger.info(f'Local save for "{igame.title}" is newer')
logger.info(f'- Cloud save date: {dt_l.strftime("%Y-%m-%d %H:%M:%S")}') if dt_r:
logger.info(f'- Local save date: {dt_r.strftime("%Y-%m-%d %H:%M:%S")}') logger.info(f'- Cloud save date: {dt_r.strftime("%Y-%m-%d %H:%M:%S")}')
else:
logger.info('- Cloud save date: N/A')
logger.info(f'- Local save date: {dt_l.strftime("%Y-%m-%d %H:%M:%S")}')
if args.download_only: if args.download_only:
logger.info('Save game uploading is disabled, skipping...') logger.info('Save game uploading is disabled, skipping...')

View file

@ -325,9 +325,18 @@ class LegendaryCore:
s = os.stat(os.path.join(_dir, _file)) s = os.stat(os.path.join(_dir, _file))
latest = max(latest, s.st_mtime) latest = max(latest, s.st_mtime)
if not latest and not save:
return SaveGameStatus.NO_SAVE, (None, None)
# timezones are fun! # timezones are fun!
dt_local = datetime.fromtimestamp(latest).replace(tzinfo=self.local_timezone).astimezone(timezone.utc) dt_local = datetime.fromtimestamp(latest).replace(tzinfo=self.local_timezone).astimezone(timezone.utc)
if not save:
return SaveGameStatus.LOCAL_NEWER, (dt_local, None)
dt_remote = datetime.strptime(save.manifest_name, '%Y.%m.%d-%H.%M.%S.manifest').replace(tzinfo=timezone.utc) dt_remote = datetime.strptime(save.manifest_name, '%Y.%m.%d-%H.%M.%S.manifest').replace(tzinfo=timezone.utc)
if not latest:
return SaveGameStatus.REMOTE_NEWER, (None, dt_remote)
self.log.debug(f'Local save date: {str(dt_local)}, Remote save date: {str(dt_remote)}') self.log.debug(f'Local save date: {str(dt_local)}, Remote save date: {str(dt_remote)}')
# Ideally we check the files themselves based on manifest, # Ideally we check the files themselves based on manifest,
@ -348,9 +357,9 @@ class LegendaryCore:
include_f = exclude_f = None include_f = exclude_f = None
if not disable_filtering: if not disable_filtering:
# get file inclusion and exclusion filters if they exist # get file inclusion and exclusion filters if they exist
if _include := custom_attr.get('CloudIncludeList', {}).get('value', None) is not None: if (_include := custom_attr.get('CloudIncludeList', {}).get('value', None)) is not None:
include_f = _include.split(',') include_f = _include.split(',')
if _exclude := custom_attr.get('CloudExcludeList', {}).get('value', None) is not None: if (_exclude := custom_attr.get('CloudExcludeList', {}).get('value', None)) is not None:
exclude_f = _exclude.split(',') exclude_f = _exclude.split(',')
if not save_path: if not save_path:

View file

@ -124,6 +124,8 @@ class SaveGameFile:
class SaveGameStatus(Enum): class SaveGameStatus(Enum):
LOCAL_NEWER = 1 LOCAL_NEWER = 0
REMOTE_NEWER = -1 REMOTE_NEWER = 1
SAME_AGE = 0 SAME_AGE = 2
NO_SAVE = 3