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

Add plugin hook for map save #16764

Merged
merged 1 commit into from Mar 7, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions distribution/changelog.txt
Expand Up @@ -24,6 +24,7 @@
- Improved: [#16251] openrct2.d.ts: removed unused LabelWidget.onChange property.
- Improved: [#16258] Increased image limit in the engine.
- Improved: [#16408] Improve --version cli option to report more compatibility information.
- Improved: [#16764] [Plugin] Add hook 'map.save', called before the map is about is saved.
- Change: [#14484] Make the Heartline Twister coaster ratings a little bit less hateful.
- Change: [#16077] When importing SV6 files, the RCT1 land types are only added when they were actually used.
- Change: [#16424] Following an entity in the title sequence no longer toggles underground view when it's underground.
Expand Down
4 changes: 3 additions & 1 deletion distribution/openrct2.d.ts
Expand Up @@ -285,6 +285,7 @@ declare global {
subscribe(hook: "action.location", callback: (e: ActionLocationArgs) => void): IDisposable;
subscribe(hook: "guest.generation", callback: (e: GuestGenerationArgs) => void): IDisposable;
subscribe(hook: "vehicle.crash", callback: (e: VehicleCrashArgs) => void): IDisposable;
subscribe(hook: "map.save", callback: () => void): IDisposable;

/**
* Registers a function to be called every so often in realtime, specified by the given delay.
Expand Down Expand Up @@ -385,7 +386,8 @@ declare global {
type HookType =
"interval.tick" | "interval.day" |
"network.chat" | "network.action" | "network.join" | "network.leave" |
"ride.ratings.calculate" | "action.location" | "vehicle.crash";
"ride.ratings.calculate" | "action.location" | "vehicle.crash" |
"map.save";

type ExpenditureType =
"ride_construction" |
Expand Down
14 changes: 14 additions & 0 deletions src/openrct2/Game.cpp
Expand Up @@ -793,3 +793,17 @@ bool stop_silent_record()

return false;
}

void PrepareMapForSave()
{
viewport_set_saved_view();

#ifdef ENABLE_SCRIPTING
auto& scriptEngine = GetContext()->GetScriptEngine();
auto& hookEngine = scriptEngine.GetHookEngine();
if (hookEngine.HasSubscriptions(OpenRCT2::Scripting::HOOK_TYPE::MAP_SAVE))
{
hookEngine.Call(OpenRCT2::Scripting::HOOK_TYPE::MAP_SAVE, false);
}
#endif
}
1 change: 1 addition & 0 deletions src/openrct2/Game.h
Expand Up @@ -173,3 +173,4 @@ void rct2_to_utf8_self(char* buffer, size_t length);
void game_fix_save_vars();
void start_silent_record();
bool stop_silent_record();
void PrepareMapForSave();
oli414 marked this conversation as resolved.
Show resolved Hide resolved
2 changes: 1 addition & 1 deletion src/openrct2/network/NetworkBase.cpp
Expand Up @@ -2758,7 +2758,7 @@ bool NetworkBase::LoadMap(IStream* stream)
bool NetworkBase::SaveMap(IStream* stream, const std::vector<const ObjectRepositoryItem*>& objects) const
{
bool result = false;
viewport_set_saved_view();
PrepareMapForSave();
try
{
auto exporter = std::make_unique<ParkFileExporter>();
Expand Down
2 changes: 1 addition & 1 deletion src/openrct2/park/ParkFile.cpp
Expand Up @@ -2289,7 +2289,7 @@ int32_t scenario_save(u8string_view path, int32_t flags)
window_close_construction_windows();
}

viewport_set_saved_view();
PrepareMapForSave();

bool result = false;
auto parkFile = std::make_unique<OpenRCT2::ParkFile>();
Expand Down
9 changes: 2 additions & 7 deletions src/openrct2/platform/Crash.cpp
Expand Up @@ -175,15 +175,10 @@ static bool OnCrash(
auto saveFilePathUTF8 = String::ToUtf8(saveFilePath);
try
{
auto exporter = std::make_unique<ParkFileExporter>();

// Make sure the save is using the current viewport settings.
viewport_set_saved_view();

// Disable RLE encoding for better compression.
gUseRLE = false;
PrepareMapForSave();
Copy link
Contributor

Choose a reason for hiding this comment

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

What happens if a plugin's map.save event is cause of the crash?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I was aware this might be an issue, but what do you suggest? Not calling the hook ever from crash save?

Copy link
Contributor

@oli414 oli414 Mar 7, 2022

Choose a reason for hiding this comment

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

Would it be possible to add a flag to indicate whether you're already "crash saving" whilst a crash occurs, in which case skip the plugin event to avoid a loop

Copy link
Member

Choose a reason for hiding this comment

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

I don't know if it defeats the purpose of the flag, but what if you call for this hook after the save rather than before?

Copy link
Contributor

Choose a reason for hiding this comment

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

@karst the purpose of this hook is for plugins to fill the parkstorage if they need to add something that has to be included in the save.

Copy link
Member

Choose a reason for hiding this comment

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

ahhh like so. Yea then it wouldn't help haha


// Export all loaded objects to avoid having custom objects missing in the reports.
auto exporter = std::make_unique<ParkFileExporter>();
auto ctx = OpenRCT2::GetContext();
auto& objManager = ctx->GetObjectManager();
exporter->ExportObjectsList = objManager.GetPackableObjects();
Expand Down
1 change: 1 addition & 0 deletions src/openrct2/scripting/HookEngine.cpp
Expand Up @@ -31,6 +31,7 @@ static const EnumMap<HOOK_TYPE> HooksLookupTable({
{ "action.location", HOOK_TYPE::ACTION_LOCATION },
{ "guest.generation", HOOK_TYPE::GUEST_GENERATION },
{ "vehicle.crash", HOOK_TYPE::VEHICLE_CRASH },
{ "map.save", HOOK_TYPE::MAP_SAVE },
});

HOOK_TYPE OpenRCT2::Scripting::GetHookType(const std::string& name)
Expand Down
1 change: 1 addition & 0 deletions src/openrct2/scripting/HookEngine.h
Expand Up @@ -40,6 +40,7 @@ namespace OpenRCT2::Scripting
ACTION_LOCATION,
GUEST_GENERATION,
VEHICLE_CRASH,
MAP_SAVE,
COUNT,
UNDEFINED = -1,
};
Expand Down
2 changes: 1 addition & 1 deletion src/openrct2/scripting/ScriptEngine.h
Expand Up @@ -46,7 +46,7 @@ namespace OpenRCT2

namespace OpenRCT2::Scripting
{
static constexpr int32_t OPENRCT2_PLUGIN_API_VERSION = 46;
static constexpr int32_t OPENRCT2_PLUGIN_API_VERSION = 47;

// Versions marking breaking changes.
static constexpr int32_t API_VERSION_33_PEEP_DEPRECATION = 33;
Expand Down
9 changes: 0 additions & 9 deletions src/openrct2/util/SawyerCoding.cpp
Expand Up @@ -24,8 +24,6 @@ static size_t encode_chunk_rle(const uint8_t* src_buffer, uint8_t* dst_buffer, s
static size_t encode_chunk_repeat(const uint8_t* src_buffer, uint8_t* dst_buffer, size_t length);
static void encode_chunk_rotate(uint8_t* buffer, size_t length);

bool gUseRLE = true;

uint32_t sawyercoding_calculate_checksum(const uint8_t* buffer, size_t length)
{
uint32_t checksum = 0;
Expand All @@ -44,13 +42,6 @@ size_t sawyercoding_write_chunk_buffer(uint8_t* dst_file, const uint8_t* buffer,
{
uint8_t *encode_buffer, *encode_buffer2;

if (!gUseRLE)
{
if (chunkHeader.encoding == CHUNK_ENCODING_RLE || chunkHeader.encoding == CHUNK_ENCODING_RLECOMPRESSED)
{
chunkHeader.encoding = CHUNK_ENCODING_NONE;
}
}
switch (chunkHeader.encoding)
{
case CHUNK_ENCODING_NONE:
Expand Down
2 changes: 0 additions & 2 deletions src/openrct2/util/SawyerCoding.h
Expand Up @@ -41,8 +41,6 @@ enum
FILE_TYPE_SC4 = (2 << 2)
};

extern bool gUseRLE;

uint32_t sawyercoding_calculate_checksum(const uint8_t* buffer, size_t length);
size_t sawyercoding_write_chunk_buffer(uint8_t* dst_file, const uint8_t* src_buffer, sawyercoding_chunk_header chunkHeader);
size_t sawyercoding_decode_sv4(const uint8_t* src, uint8_t* dst, size_t length, size_t bufferLength);
Expand Down