Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: Clean up world saving/loading codepath (Phase 1 of v2 save format) #5774

Merged
merged 11 commits into from
Dec 7, 2024

Conversation

randombk
Copy link
Contributor

@randombk randombk commented Nov 29, 2024

Checklist

Required

Purpose of change

This PR is Phase 1 of #5771.

It refactors much of the world and player save read/write logic, with a particular focus on consolidating the business logic of what gets saved where. The purpose is to create a clean environment where we can add on support for different save formats in a subsequent PR.

This PR should not change the on-disk format. Everything should continue to work normally. The actual SQLite+compression implementation will happen on top of this refactored backend in a later PR.

Describe the solution

  • Clean up and refactor worldfactory and world.
    • The old WORLD struct is now renamed WORLDINFO to better reflect its actual purpose.
    • Get rid of the WORLDPTR = WORLD* convention. This isn't the Windows API, and nothing else in the codebase does this.
  • Move file saving logic into a new class called world, meant to encapsulate the idea of a loaded world. All reads and writes to world and player files now go through this class, which forms the foundation for future changes to the save format.
  • Simplified the I/O utility methods read_from_file* and write_to_file* found in cata_utility.cpp. This replaces the 2 write and 6 read methods with 1 write and 2 read. This simplification is needed because future save loaders will need to provide their own implementations of these methods.
    • The _optional variants can be replaced with default arguments.
    • The JsonDeserializer variants are never used.
    • The _json variants do save some boilerplate (path) so I'll keep that in for now.

Describe alternatives you've considered

Testing

Awaiting CI signals.
I tested by loading & saving a couple of existing worlds, without obvious issues thus far.

Additional context

  All the other binaries are listed except for the
  ones that cmake actually generates for tiles builds.

Signed-off-by: David Li <jiawei.davidli@gmail.com>
  Clean up and refactor worldfactory and world.
  * The old 'WORLD' struct is now renamed `WORLDINFO`
    to better reflect its actual purpose.
  * Get rid of the WORLDPTR = WORLD* convention. This
    isn't the Windows API, and nothing else does this.
  * Move file saving logic into a new class called
    `world`, meant to encapsulate the idea of a loaded
    world. I will begin consolidating more world loading
    logic into this class.

Signed-off-by: David Li <jiawei.davidli@gmail.com>
  Replace the 2 write and 6 read methods with 1 write and
  2 read. This simplification is needed because future save
  loaders will need to provide their own implementations of these
  methods.
  * The `_optional` variants can be replaced with default arguments.
  * The `JsonDeserializer` variants are never used.
  * The `_json` variants do save some boilerplate (`path`) so I'll
    keep that in for now.

Signed-off-by: David Li <jiawei.davidli@gmail.com>
  Player file IO is still janky, particularly `mm` files - WIP.
  However, the game does work and the on-disk format should be
  unchanged.

Signed-off-by: David Li <jiawei.davidli@gmail.com>
  Clean up how the game determines where to place player-
  specific files, in preparation for the V2 save format.

  NOTE: This removes backwards compatibility for the pre-2020
  map memory ('mm') format. This logic only triggered when loading
  a map that has not seen a save event since 2020 - I think
  it's safe to remove this now.

Signed-off-by: David Li <jiawei.davidli@gmail.com>
@github-actions github-actions bot added src changes related to source code. tests changes related to tests labels Nov 29, 2024
Copy link
Contributor

autofix-ci bot commented Nov 29, 2024

Autofix has formatted code style violation in this PR.

I edit commits locally (e.g: git, github desktop) and want to keep autofix
  1. Run git pull. this will merge the automated commit into your local copy of the PR branch.
  2. Continue working.
I do not want the automated commit
  1. Format your code locally, then commit it.
  2. Run git push --force to force push your branch. This will overwrite the automated commit on remote with your local one.
  3. Continue working.

If you don't do this, your following commits will be based on the old commit, and cause MERGE CONFLICT.

@scarf005
Copy link
Member

gah astyle is going haywire

@randombk
Copy link
Contributor Author

No, astyle is largely correct here. I'll pull and squash. I don't have a proper astyle setup locally so I'm just depending on the bot for now.

@randombk
Copy link
Contributor Author

randombk commented Nov 29, 2024

Actually, if there's no objections I'll the keep asyle commit without squashing - it leaves the individual diffs nice and self-consistent.

Signed-off-by: David Li <jiawei.davidli@gmail.com>
Signed-off-by: David Li <jiawei.davidli@gmail.com>
  The consolidation of write_to_file methods introduced
  a subtle API change where passing nullptr to the third
  argument no longer caused write_to_file to eat exceptions.

  This largely only affected tests and a small number of edge
  cases. Alter the API so that the right way to eat exceptions
  is via passing in the empty string as the message.

Signed-off-by: David Li <jiawei.davidli@gmail.com>
@randombk randombk force-pushed the sqlite-saves-phase1 branch 2 times, most recently from 344b84e to 1229bfa Compare December 1, 2024 02:40
  Plus misc bug fixes

Signed-off-by: David Li <jiawei.davidli@gmail.com>
scarf005
scarf005 previously approved these changes Dec 1, 2024
Copy link
Member

@scarf005 scarf005 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the changes looks good to me, there's some nitpicks here and there but not critical.

@chaosvolt @RoyalFox2140 could you playtest them before merging to be extra sure?

Comment on lines 104 to 106
using file_read_cb = const std::function<void( std::istream & )> &;
using file_read_json_cb = const std::function<void( JsonIn & )> &;
using file_write_cb = const std::function<void( std::ostream & )> &;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think fn(function) would be clearer than cb(callback).

@@ -523,8 +530,12 @@ std::istream &safe_getline( std::istream &ins, std::string &str )
}
}

bool read_from_file( const std::string &path, const std::function<void( std::istream & )> &reader )
bool read_from_file( const std::string &path, file_read_cb reader, bool optional )
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

might be clearer to use enum class over boolean flag, e.g

enum class read_option {
  REQUIRED = 0,
  OPTIONAL = 1
};

src/game.cpp Outdated
Comment on lines 7153 to 7158
bool game::take_screenshot( const std::string &path )
{
return save_screenshot( path );
}

bool game::take_screenshot() const
bool game::take_screenshot()
Copy link
Member

@scarf005 scarf005 Dec 1, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

could you explain why was the const removed? does the function internals now mutate states when taking screenshots?

src/world.h Outdated
*/
/**@{*/
void start_save_tx();
long long commit_save_tx();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

might be clearer to use int64_t.

joveeater
joveeater previously approved these changes Dec 1, 2024
Copy link
Collaborator

@joveeater joveeater left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good to me. Great stuff.

src/game.cpp Outdated
@@ -2732,16 +2727,23 @@ spell_events &game::spell_events_subscriber()
return *spell_events_ptr;
}

static bool save_uistate_data( const game &g )
bool game::save_uistate_data()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does it affect the state? Should be const if it doesn't

@@ -11413,12 +11418,12 @@ void game::quicksave()

void game::quickload()
{
const WORLDPTR active_world = world_generator->active_world;
world *active_world = get_active_world();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does it have to be non-const?

src/world.cpp Outdated
// if( SQLITE_OK != ( ret = sqlite3_exec( db, sql, NULL, NULL, &sqlErrMsg ) ) ) {
// dbg( DL::Error ) << "Failed to init db" << db_path << " (" << sqlErrMsg << ")";
// throw std::runtime_error( "Failed to open db" );
// }
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Commented out code - should be removed from the PR. It's easy to restore it from git history later.

@@ -523,8 +530,12 @@ std::istream &safe_getline( std::istream &ins, std::string &str )
}
}

bool read_from_file( const std::string &path, const std::function<void( std::istream & )> &reader )
bool read_from_file( const std::string &path, file_read_cb reader, bool optional )
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, that bool is hard to understand there. Especially after function definition.
If not an enum flag, the following options would also work:

  • Split into 2 functions: mandatory and optional variant
  • Empty class used just as argument here, for overload

@chaosvolt
Copy link
Member

I'd like to see if the various code nitpicks can be addressed before I test it. Are any of them deal-breakers or are they all just minor things that can be ignored if nessecary?

@scarf005
Copy link
Member

scarf005 commented Dec 1, 2024

I'd like to see if the various code nitpicks can be addressed before I test it. Are any of them deal-breakers or are they all just minor things that can be ignored if nessecary?

they are pure C++ nitpicks and won't affect gameplay.

@chaosvolt
Copy link
Member

In that case will see about testing it in a bit, later today if getting ready to head out and deal with errands doesn't leave be with enough time.

chaosvolt
chaosvolt previously approved these changes Dec 1, 2024
Copy link
Member

@chaosvolt chaosvolt left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. Compile-tested.
  2. Loaded a save from a previous compile-test, no errors seen.
  3. Started a new world, saved and loaded, also worked fine.
  4. Started a new character in this new world with one already in it, saving and loading the second character also works.

@randombk randombk dismissed stale reviews from chaosvolt, joveeater, and scarf005 via c48032f December 1, 2024 23:48
@randombk randombk force-pushed the sqlite-saves-phase1 branch from 8cb8210 to 1f4fa77 Compare December 1, 2024 23:50
@randombk
Copy link
Contributor Author

randombk commented Dec 1, 2024

Addressed comments. The consts were a leftover from a previous iteration - I added them back in where appropriate.

The player data write methods cannot be const to make way for the Phase 2 patch. Because the saveid is not known when the world object is created, we must load the player sqlite file on-demand thus requiring some internal mutable state during player writes.

@scarf005
Copy link
Member

scarf005 commented Dec 2, 2024

strange, MSVC is chocking on here

D:\a\Cataclysm-BN\Cataclysm-BN\src\game.cpp(2510,63): error C2589: ')': illegal token on right side of '::' [D:\a\Cataclysm-BN\Cataclysm-BN\out\build\windows-tiles-sounds-x64-msvc\src\cataclysm-bn-tiles-common.vcxproj]
D:\a\Cataclysm-BN\Cataclysm-BN\src\game.cpp(2510): error C2062: type 'unknown-type' unexpected [D:\a\Cataclysm-BN\Cataclysm-BN\out\build\windows-tiles-sounds-x64-msvc\src\cataclysm-bn-tiles-common.vcxproj]
D:\a\Cataclysm-BN\Cataclysm-BN\src\game.cpp(2564,117): error C2589: ')': illegal token on right side of '::' [D:\a\Cataclysm-BN\Cataclysm-BN\out\build\windows-tiles-sounds-x64-msvc\src\cataclysm-bn-tiles-common.vcxproj]
D:\a\Cataclysm-BN\Cataclysm-BN\src\game.cpp(2564): error C2062: type 'unknown-type' unexpected [D:\a\Cataclysm-BN\Cataclysm-BN\out\build\windows-tiles-sounds-x64-msvc\src\cataclysm-bn-tiles-common.vcxproj]
D:\a\Cataclysm-BN\Cataclysm-BN\src\game.cpp(2590,30): error C2589: ')': illegal token on right side of '::' [D:\a\Cataclysm-BN\Cataclysm-BN\out\build\windows-tiles-sounds-x64-msvc\src\cataclysm-bn-tiles-common.vcxproj]
D:\a\Cataclysm-BN\Cataclysm-BN\src\game.cpp(2590): error C2062: type 'unknown-type' unexpected [D:\a\Cataclysm-BN\Cataclysm-BN\out\build\windows-tiles-sounds-x64-msvc\src\cataclysm-bn-tiles-common.vcxproj]

@randombk randombk force-pushed the sqlite-saves-phase1 branch 3 times, most recently from d9ebad7 to 6cfa61d Compare December 2, 2024 02:29
@randombk
Copy link
Contributor Author

randombk commented Dec 2, 2024

Yea, there's something about enum syntax that MSVC really doesn't like.

@randombk randombk force-pushed the sqlite-saves-phase1 branch from 6cfa61d to 45e1ba9 Compare December 2, 2024 03:33
@scarf005
Copy link
Member

scarf005 commented Dec 2, 2024

weird, other enum classes compile fine.

@randombk randombk force-pushed the sqlite-saves-phase1 branch 2 times, most recently from 45e1ba9 to 0f66a62 Compare December 2, 2024 06:02
@RoyalFox2140
Copy link
Collaborator

the changes looks good to me, there's some nitpicks here and there but not critical.

@chaosvolt @RoyalFox2140 could you playtest them before merging to be extra sure?

If the saves are going to break they're going to break after days of playing the same save file for hours a day, And I can't play self-compile versions. No matter what settings are used my game lags heavily and we've gone over using Release without Debug symbols doing nothing to fix it, and proper releases not lagging. There's nothing for me to do here. If it's going to break everything it's going to do so in a way that won't get caught from standard testing.

MSVC and Ubuntu tests failed, so I'll at least re-run those.

Signed-off-by: David Li <jiawei.davidli@gmail.com>
@randombk randombk force-pushed the sqlite-saves-phase1 branch from 0f66a62 to b4c55df Compare December 3, 2024 05:02
@randombk
Copy link
Contributor Author

randombk commented Dec 3, 2024

I changed the enum back to a bool. There's something weird going on with MSVC and I don't have a local environment to test/debug that right now.

Copy link
Member

@chaosvolt chaosvolt left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. Compiled and load-tested.
  2. Loaded a save from previous compiling, loads as expected. Loading the two different characters in the save also work right.
  3. Tested save/load, no oddities found.
  4. Also quicksaved, debug quit to menu, then loaded with no issues.
  5. Spawned in an APC and used autodrive to move a long distance before quicksaving, also worked.

Quicksave just standing in starting shelter:
image

Quicksave after a nice long drive (about 75 thousand blocks of things to save, whatever the number during save progress is counting):
image

@chaosvolt chaosvolt merged commit 374bd34 into cataclysmbnteam:main Dec 7, 2024
12 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
src changes related to source code. tests changes related to tests
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants