Porytiles
Loading...
Searching...
No Matches
compile_secondary_tileset.cpp
Go to the documentation of this file.
2
3#include <memory>
4#include <string>
5
13
14namespace porytiles {
15
16ChainableResult<void> CompileSecondaryTileset::compile(const std::string &tileset_name) const
17{
18 // 1. Precondition: tileset must exist. Check if it is Porytiles-managed.
19 assert_or_panic(metadata_provider_->exists(tileset_name), "precondition violated: tileset must exist");
20 if (!tileset_manager_->is_porytiles_managed(tileset_name)) {
21 return FormattableError{
22 "Tileset '{}' exists but is not Porytiles-managed.", FormatParam{tileset_name, Style::bold}};
23 }
24
25 // 2. Read primary pairing configuration.
26 PT_UNWRAP_TILESET_CONFIG_PTR(app_config_, primary_pairing_mode, tileset_name, void);
27 PT_UNWRAP_TILESET_CONFIG_PTR(app_config_, primary_pairing_partners, tileset_name, void);
28
29 // 3. Resolve the partner primary tileset.
31 paired_primary,
33 tileset_name,
34 primary_pairing_mode,
35 primary_pairing_partners,
36 tileset_repo_,
37 metadata_provider_,
38 layout_metadata_provider_,
39 tileset_manager_,
40 diag_),
41 void,
42 "Failed to resolve partner primary for secondary '{}'.",
43 FormatParam(tileset_name, Style::bold));
44
45 // 4. Load the secondary tileset.
47 tileset,
48 tileset_repo_->load(tileset_name),
49 void,
50 diag_->formatter().format("Failed to load tileset '{}'.", FormatParam(tileset_name, Style::bold)));
51
52 // 4b. Cross-tileset extrinsic transparency mismatch warning.
53 {
54 auto secondary_et_result = domain_config_->extrinsic_transparency(ConfigScopeType::tileset, tileset_name);
55 auto primary_et_result =
56 domain_config_->extrinsic_transparency(ConfigScopeType::tileset, paired_primary->name());
57
58 const auto &secondary_et = secondary_et_result.value();
59 const auto &primary_et = primary_et_result.value();
60 if (secondary_et.value() != primary_et.value()) {
61 std::vector<std::string> warn_msg{};
62 warn_msg.emplace_back(diag_->formatter().format(
63 "Secondary tileset '{}' and its paired primary '{}' have different configured "
64 "'extrinsic_transparency' values ('{}' vs '{}').",
65 FormatParam{tileset_name, Style::bold},
66 FormatParam{paired_primary->name(), Style::bold},
67 FormatParam{secondary_et.value().to_jasc_str(), Style::bold},
68 FormatParam{primary_et.value().to_jasc_str(), Style::bold}));
69 warn_msg.emplace_back(
70 "Cross-tileset keyframe matching will use each side's configured value independently.");
71 warn_msg.emplace_back("");
72 warn_msg.emplace_back(
73 "This warning is based on the current domain config, not on the ET each tileset was actually "
74 "compiled under.");
75 warn_msg.emplace_back(
76 "If a tileset was compiled with a different ET and has not been recompiled since, cross-tileset "
77 "matching may produce unexpected results.");
78 warn_msg.emplace_back("Recompile both tilesets if in doubt.");
79 warn_msg.emplace_back("");
80 warn_msg.emplace_back(diagnostic_separator);
81 warn_msg.emplace_back("");
82 warn_msg.append_range(format_config_note(diag_->formatter(), secondary_et));
83 warn_msg.emplace_back("");
84 warn_msg.emplace_back(diagnostic_separator);
85 warn_msg.emplace_back("");
86 warn_msg.append_range(format_config_note(diag_->formatter(), primary_et));
87
88 diag_->warning("cross-tileset-extrinsic-transparency-mismatch", warn_msg);
89 }
90 }
91
92 // 5. Checksum verification.
93 PT_UNWRAP_TILESET_CONFIG_PTR(app_config_, verify_checksums, tileset_name, void);
94 if (!tileset_repo_->checksum_provider().cached_checksums_exist(tileset_name) && verify_checksums) {
95 std::vector<std::string> err_msg{};
96 err_msg.emplace_back(diag_->formatter().format(
97 "No cached checksums found for tileset '{}'.", FormatParam{tileset_name, Style::bold}));
98 err_msg.emplace_back(diag_->formatter().format(
99 "Expected to find file '{}'.",
100 FormatParam{"porytiles/tilesets/" + tileset_name + "/tileset.cache.json", Style::bold}));
101 err_msg.emplace_back("Checksum verification requested via configuration.");
102 err_msg.append_range(format_config_note_with_separator(diag_->formatter(), verify_checksums));
103
105 }
106
107 if (tileset_repo_->checksum_provider().cached_checksums_exist(tileset_name) && verify_checksums.value()) {
108 // Check for unimported Porymap asset changes.
109 if (!tileset->porymap_component().is_empty()) {
111 porymap_keys,
112 tileset_repo_->key_provider().get_porymap_artifact_keys(tileset_name),
113 void,
114 "Failed to get Porymap artifact keys.");
115 const auto mismatched_keys =
116 tileset_repo_->checksum_provider().find_unsynced_tileset_artifacts(tileset_name, porymap_keys);
117 if (!mismatched_keys.empty()) {
118 std::vector<std::string> err_msg{};
119 err_msg.emplace_back("Changes present in Porymap assets:");
120 for (const auto &key : mismatched_keys) {
121 err_msg.emplace_back(diag_->formatter().format(" {}", FormatParam{key.key(), Style::bold}));
122 }
123 err_msg.emplace_back("");
124 err_msg.emplace_back("Compiling now would clobber your Porymap asset changes.");
125 err_msg.emplace_back("To resolve:");
126 err_msg.emplace_back(diag_->formatter().format(
127 " - Run '{} {}' to synchronize assets.",
128 FormatParam{"decompile-tileset", Style::bold},
129 FormatParam{tileset_name, Style::bold}));
130 err_msg.emplace_back(diag_->formatter().format(
131 " - {} disable checksum verification to allow the clobber.", FormatParam{"OR", Style::bold}));
132 err_msg.emplace_back(diag_->formatter().format(
133 " - {} delete '{}' cache file.",
135 FormatParam{"porytiles/tilesets/" + tileset_name + "/tileset.cache.json", Style::bold}));
136 err_msg.append_range(format_config_note_with_separator(diag_->formatter(), verify_checksums));
138 }
139 }
140
141 /*
142 * Primary-side staleness check. Content-aware, unlike the name-only check in
143 * pipeline_helper_register_animations. If the paired primary's sources have drifted from its cached compile
144 * form, refuse to proceed: cross-tileset matching would operate against a stale compiled primary and silently
145 * produce wrong output. Only runs when the paired primary itself has cached checksums; a primary with no cache
146 * cannot be content-verified here (the user may have compiled it externally).
147 */
148 if (tileset_repo_->checksum_provider().cached_checksums_exist(paired_primary->name())) {
150 primary_porytiles_keys,
151 tileset_repo_->key_provider().get_porytiles_artifact_keys(paired_primary->name()),
152 void,
153 "Failed to get Porytiles artifact keys for paired primary '{}'.",
154 FormatParam(paired_primary->name(), Style::bold));
155 const auto primary_mismatched_keys = tileset_repo_->checksum_provider().find_unsynced_tileset_artifacts(
156 paired_primary->name(), primary_porytiles_keys);
157 if (!primary_mismatched_keys.empty()) {
158 std::vector<std::string> err_msg{};
159 err_msg.emplace_back(diag_->formatter().format(
160 "Paired primary tileset '{}' has source changes that have not been compiled.",
161 FormatParam{paired_primary->name(), Style::bold}));
162 err_msg.emplace_back("Changes present in paired primary Porytiles sources:");
163 for (const auto &key : primary_mismatched_keys) {
164 err_msg.emplace_back(diag_->formatter().format(" {}", FormatParam{key.key(), Style::bold}));
165 }
166 err_msg.emplace_back("");
167 err_msg.emplace_back("Compiling the secondary now would match tiles against a stale compiled primary.");
168 err_msg.emplace_back("To resolve:");
169 err_msg.emplace_back(diag_->formatter().format(
170 " - Run '{} {}' to bring the primary's compiled form up to date.",
171 FormatParam{"compile-tileset", Style::bold},
172 FormatParam{paired_primary->name(), Style::bold}));
173 err_msg.append_range(format_config_note_with_separator(diag_->formatter(), verify_checksums));
175 }
176 }
177
178 // Check if Porytiles source assets are unchanged (nothing to do).
180 porytiles_keys,
181 tileset_repo_->key_provider().get_porytiles_artifact_keys(tileset_name),
182 void,
183 "Failed to get Porytiles artifact keys.");
184 if (tileset_repo_->checksum_provider().all_checksums_tileset_match(tileset_name, porytiles_keys)) {
185 diag_->warning(
186 "nothing-to-do",
187 "Skipping compilation for '{}', no changes found.",
188 FormatParam{tileset_name, Style::bold});
189 return {};
190 }
191 }
192
193 // 6. Compile the secondary tileset.
195 new_tileset,
196 compiler_->compile(*tileset, /*is_secondary=*/true, paired_primary.get()),
197 void,
198 "Compilation job failed for '{}'.",
199 FormatParam(tileset_name, Style::bold));
200
201 // 7. Persist the compiled tileset (which also caches the checksums).
203 tileset_repo_->save(*new_tileset),
204 void,
205 "Tileset save job failed for '{}'.",
206 FormatParam(tileset_name, Style::bold));
207
208 // 8. Handle animation code wiring.
209 if (!new_tileset->porymap_component().anims().empty()) {
211 tileset_manager_->wire_anim_code(tileset_name, /*is_secondary=*/true),
212 void,
213 "Failed to wire animation code for '{}'.",
214 FormatParam(tileset_name, Style::bold));
215 }
216 else {
218 tileset_manager_->remove_wired_anim_code(tileset_name, /*is_secondary=*/true),
219 void,
220 "Failed to remove wired animation code for '{}'.",
221 FormatParam(tileset_name, Style::bold));
222 }
223
224 return {};
225}
226
227} // namespace porytiles
#define PT_TRY_ASSIGN_CHAIN_ERR(var, expr, return_type,...)
Unwraps a ChainableResult, chaining a new error message on failure.
#define PT_TRY_CALL_CHAIN_ERR(expr, return_type,...)
Unwraps a void ChainableResult, chaining a new error message on failure.
bool all_checksums_tileset_match(const std::string &name, const std::vector< ArtifactKey > &artifact_keys) const
Checks if all artifact checksums for the given Tileset match their cached values.
std::vector< ArtifactKey > find_unsynced_tileset_artifacts(const std::string &name, const std::vector< ArtifactKey > &keys_to_check) const
Finds all artifacts for the given Tileset with unsynced changes compared to cached checksums.
bool cached_checksums_exist(const std::string &name) const
Check if any cached checksums exist for the given tileset.
A result type that maintains a chainable sequence of errors for debugging and error reporting.
ChainableResult< void > compile(const std::string &tileset_name) const
Compiles the secondary Tileset with the given tileset name.
ChainableResult< ConfigValue< Rgba32 > > extrinsic_transparency(ConfigScopeType type, const std::string &scope) const
A text parameter with associated styling for formatted output.
General-purpose error implementation with formatted message support.
Definition error.hpp:63
virtual ChainableResult< void > wire_anim_code(const std::string &tileset_name, bool is_secondary) const =0
Wires animation code for a tileset that already has its manifest persisted.
virtual bool is_porytiles_managed(const std::string &tileset_name) const =0
Checks whether a tileset is managed by Porytiles.
virtual ChainableResult< void > remove_wired_anim_code(const std::string &tileset_name, bool is_secondary) const =0
Removes wired animation code for a tileset from the project.
static const Style bold
Bold text formatting.
virtual std::string format(const std::string &format_str, const std::vector< FormatParam > &params) const
Formats a string with styled parameters using fmtlib syntax.
virtual ChainableResult< std::vector< ArtifactKey > > get_porytiles_artifact_keys(const std::string &tileset_name) const
Gets the keys for all Porytiles artifacts present in the given Tileset.
virtual ChainableResult< std::vector< ArtifactKey > > get_porymap_artifact_keys(const std::string &tileset_name) const
Gets the keys for all Porymap artifacts present in the given Tileset.
ChainableResult< std::unique_ptr< Tileset > > compile(const Tileset &tileset, bool is_secondary=false, const Tileset *paired_primary=nullptr) const
Compiles the given Tileset, producing a new Tileset with compiled Porymap assets.
virtual bool exists(const std::string &tileset_name) const =0
Checks whether a tileset exists in the backing store for the given tileset name.
ChainableResult< void > save(const Tileset &tileset) const
Persists a given Tileset and caches new artifact checksums.
ChainableResult< std::unique_ptr< Tileset > > load(const std::string &name) const
Loads an existing Tileset from storage.
const TilesetArtifactKeyProvider & key_provider() const
Gets a reference to the TilesetArtifactKeyProvider for this repo.
const ArtifactChecksumProvider & checksum_provider() const
Gets a reference to the ArtifactChecksumProvider for this repo.
const TextFormatter & formatter() const
virtual void warning(const std::string &tag, const std::vector< std::string > &lines) const =0
Display a tagged warning message.
ChainableResult< std::unique_ptr< Tileset > > resolve_partner_primary(const std::string &tileset_name, const ConfigValue< PrimaryPairingMode > &pairing_mode, const ConfigValue< std::vector< std::string > > &partners, const TilesetRepo *tileset_repo, const TilesetMetadataProvider *metadata_provider, const LayoutMetadataProvider *layout_metadata_provider, const PorytilesTilesetManager *tileset_manager, const UserDiagnostics *diag)
Resolves the partner primary tileset for a secondary tileset.
void assert_or_panic(bool condition, const StringViewSourceLoc &s)
Conditionally panics if the given condition is false.
Definition panic.cpp:53
std::vector< std::string > format_config_note(const TextFormatter &format, const ConfigValue< T > &config)
Format a ConfigValue into diagnostic note lines.
std::vector< std::string > format_config_note_with_separator(const TextFormatter &format, const ConfigValue< T > &config)
Format a ConfigValue into diagnostic note lines with a separator.
@ tileset
Configuration scoped to a specific tileset.
constexpr std::string_view diagnostic_separator
#define PT_UNWRAP_TILESET_CONFIG_PTR(ptr, config, tileset_name, return_type)
Unwraps a tileset-scoped config value via pointer access, returning early if the value is not availab...