Add biome migrations to mitigate damage after biome renames (#1157)

* Add biomes migrations

* Fix error on empty migration file

* Fix migrations_zig.applyBiomePaletteMigrations call

* Fix formatting issues

* Rename thingsMigrations to thingMigrations

* Change log type for incorrect migrations

* Use comptime enum for migration selection

* Fix formatting issues

* Update src/migrations.zig

* Update src/migrations.zig

* Update src/migrations.zig
This commit is contained in:
Krzysztof Wiśniewski 2025-03-06 20:49:11 +01:00 committed by GitHub
parent b497133967
commit ac43b7128e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 72 additions and 30 deletions

View File

@ -11,10 +11,11 @@ const NeverFailingAllocator = main.utils.NeverFailingAllocator;
var arena: main.utils.NeverFailingArenaAllocator = undefined; var arena: main.utils.NeverFailingArenaAllocator = undefined;
var arenaAllocator: NeverFailingAllocator = undefined; var arenaAllocator: NeverFailingAllocator = undefined;
var commonBlocks: std.StringHashMap(ZonElement) = undefined; var commonBlocks: std.StringHashMap(ZonElement) = undefined;
var commonBlocksMigrations: std.StringHashMap(ZonElement) = undefined; var commonBlockMigrations: std.StringHashMap(ZonElement) = undefined;
var commonBiomes: std.StringHashMap(ZonElement) = undefined;
var commonItems: std.StringHashMap(ZonElement) = undefined; var commonItems: std.StringHashMap(ZonElement) = undefined;
var commonTools: std.StringHashMap(ZonElement) = undefined; var commonTools: std.StringHashMap(ZonElement) = undefined;
var commonBiomes: std.StringHashMap(ZonElement) = undefined;
var commonBiomeMigrations: std.StringHashMap(ZonElement) = undefined;
var commonRecipes: std.StringHashMap(ZonElement) = undefined; var commonRecipes: std.StringHashMap(ZonElement) = undefined;
var commonModels: std.StringHashMap([]const u8) = undefined; var commonModels: std.StringHashMap([]const u8) = undefined;
@ -25,10 +26,11 @@ pub fn init() void {
arena = .init(main.globalAllocator); arena = .init(main.globalAllocator);
arenaAllocator = arena.allocator(); arenaAllocator = arena.allocator();
commonBlocks = .init(arenaAllocator.allocator); commonBlocks = .init(arenaAllocator.allocator);
commonBlocksMigrations = .init(arenaAllocator.allocator); commonBlockMigrations = .init(arenaAllocator.allocator);
commonItems = .init(arenaAllocator.allocator); commonItems = .init(arenaAllocator.allocator);
commonTools = .init(arenaAllocator.allocator); commonTools = .init(arenaAllocator.allocator);
commonBiomes = .init(arenaAllocator.allocator); commonBiomes = .init(arenaAllocator.allocator);
commonBiomeMigrations = .init(arenaAllocator.allocator);
commonRecipes = .init(arenaAllocator.allocator); commonRecipes = .init(arenaAllocator.allocator);
commonModels = .init(arenaAllocator.allocator); commonModels = .init(arenaAllocator.allocator);
@ -36,17 +38,18 @@ pub fn init() void {
arenaAllocator, arenaAllocator,
"assets/", "assets/",
&commonBlocks, &commonBlocks,
&commonBlocksMigrations, &commonBlockMigrations,
&commonItems, &commonItems,
&commonTools, &commonTools,
&commonBiomes, &commonBiomes,
&commonBlockMigrations,
&commonRecipes, &commonRecipes,
&commonModels, &commonModels,
); );
std.log.info( std.log.info(
"Finished assets init with {} blocks ({} migrations), {} items, {} tools. {} biomes, {} recipes", "Finished assets init with {} blocks ({} migrations), {} items, {} tools. {} biomes ({} migrations), {} recipes",
.{commonBlocks.count(), commonBlocksMigrations.count(), commonItems.count(), commonTools.count(), commonBiomes.count(), commonRecipes.count()}, .{commonBlocks.count(), commonBlockMigrations.count(), commonItems.count(), commonTools.count(), commonBiomes.count(), commonBiomeMigrations.count(), commonRecipes.count()},
); );
} }
@ -207,7 +210,18 @@ pub fn readAllObjFilesInAddonsHashmap(
} }
} }
pub fn readAssets(externalAllocator: NeverFailingAllocator, assetPath: []const u8, blocks: *std.StringHashMap(ZonElement), blocksMigrations: *std.StringHashMap(ZonElement), items: *std.StringHashMap(ZonElement), tools: *std.StringHashMap(ZonElement), biomes: *std.StringHashMap(ZonElement), recipes: *std.StringHashMap(ZonElement), models: *std.StringHashMap([]const u8)) void { pub fn readAssets(
externalAllocator: NeverFailingAllocator,
assetPath: []const u8,
blocks: *std.StringHashMap(ZonElement),
blockMigrations: *std.StringHashMap(ZonElement),
items: *std.StringHashMap(ZonElement),
tools: *std.StringHashMap(ZonElement),
biomes: *std.StringHashMap(ZonElement),
biomeMigrations: *std.StringHashMap(ZonElement),
recipes: *std.StringHashMap(ZonElement),
models: *std.StringHashMap([]const u8),
) void {
var addons = main.List(std.fs.Dir).init(main.stackAllocator); var addons = main.List(std.fs.Dir).init(main.stackAllocator);
defer addons.deinit(); defer addons.deinit();
var addonNames = main.List([]const u8).init(main.stackAllocator); var addonNames = main.List([]const u8).init(main.stackAllocator);
@ -238,10 +252,10 @@ pub fn readAssets(externalAllocator: NeverFailingAllocator, assetPath: []const u
main.stackAllocator.free(addonName); main.stackAllocator.free(addonName);
}; };
readAllZonFilesInAddons(externalAllocator, addons, addonNames, "blocks", true, blocks, blocksMigrations); readAllZonFilesInAddons(externalAllocator, addons, addonNames, "blocks", true, blocks, blockMigrations);
readAllZonFilesInAddons(externalAllocator, addons, addonNames, "items", true, items, null); readAllZonFilesInAddons(externalAllocator, addons, addonNames, "items", true, items, null);
readAllZonFilesInAddons(externalAllocator, addons, addonNames, "tools", true, tools, null); readAllZonFilesInAddons(externalAllocator, addons, addonNames, "tools", true, tools, null);
readAllZonFilesInAddons(externalAllocator, addons, addonNames, "biomes", true, biomes, null); readAllZonFilesInAddons(externalAllocator, addons, addonNames, "biomes", true, biomes, biomeMigrations);
readAllZonFilesInAddons(externalAllocator, addons, addonNames, "recipes", false, recipes, null); readAllZonFilesInAddons(externalAllocator, addons, addonNames, "recipes", false, recipes, null);
readAllObjFilesInAddonsHashmap(externalAllocator, addons, addonNames, "models", models); readAllObjFilesInAddonsHashmap(externalAllocator, addons, addonNames, "models", models);
} }
@ -347,14 +361,16 @@ pub fn loadWorldAssets(assetFolder: []const u8, blockPalette: *Palette, biomePal
var blocks = commonBlocks.cloneWithAllocator(main.stackAllocator.allocator) catch unreachable; var blocks = commonBlocks.cloneWithAllocator(main.stackAllocator.allocator) catch unreachable;
defer blocks.clearAndFree(); defer blocks.clearAndFree();
var blocksMigrations = commonBlocksMigrations.cloneWithAllocator(main.stackAllocator.allocator) catch unreachable; var blockMigrations = commonBlockMigrations.cloneWithAllocator(main.stackAllocator.allocator) catch unreachable;
defer blocksMigrations.clearAndFree(); defer blockMigrations.clearAndFree();
var items = commonItems.cloneWithAllocator(main.stackAllocator.allocator) catch unreachable; var items = commonItems.cloneWithAllocator(main.stackAllocator.allocator) catch unreachable;
defer items.clearAndFree(); defer items.clearAndFree();
var tools = commonTools.cloneWithAllocator(main.stackAllocator.allocator) catch unreachable; var tools = commonTools.cloneWithAllocator(main.stackAllocator.allocator) catch unreachable;
defer tools.clearAndFree(); defer tools.clearAndFree();
var biomes = commonBiomes.cloneWithAllocator(main.stackAllocator.allocator) catch unreachable; var biomes = commonBiomes.cloneWithAllocator(main.stackAllocator.allocator) catch unreachable;
defer biomes.clearAndFree(); defer biomes.clearAndFree();
var biomeMigrations = commonBiomeMigrations.cloneWithAllocator(main.stackAllocator.allocator) catch unreachable;
defer biomeMigrations.clearAndFree();
var recipes = commonRecipes.cloneWithAllocator(main.stackAllocator.allocator) catch unreachable; var recipes = commonRecipes.cloneWithAllocator(main.stackAllocator.allocator) catch unreachable;
defer recipes.clearAndFree(); defer recipes.clearAndFree();
var models = commonModels.cloneWithAllocator(main.stackAllocator.allocator) catch unreachable; var models = commonModels.cloneWithAllocator(main.stackAllocator.allocator) catch unreachable;
@ -364,17 +380,21 @@ pub fn loadWorldAssets(assetFolder: []const u8, blockPalette: *Palette, biomePal
arenaAllocator, arenaAllocator,
assetFolder, assetFolder,
&blocks, &blocks,
&blocksMigrations, &blockMigrations,
&items, &items,
&tools, &tools,
&biomes, &biomes,
&biomeMigrations,
&recipes, &recipes,
&models, &models,
); );
errdefer unloadAssets(); errdefer unloadAssets();
migrations_zig.registerBlockMigrations(&commonBlocksMigrations); migrations_zig.registerAll(.block, &blockMigrations);
migrations_zig.applyBlockPaletteMigrations(blockPalette); migrations_zig.apply(.block, blockPalette);
migrations_zig.registerAll(.biome, &biomeMigrations);
migrations_zig.apply(.biome, biomePalette);
// models: // models:
var modelIterator = models.iterator(); var modelIterator = models.iterator();
@ -465,8 +485,8 @@ pub fn loadWorldAssets(assetFolder: []const u8, blockPalette: *Palette, biomePal
} }
std.log.info( std.log.info(
"Finished registering assets with {} blocks ({} migrations), {} items {} tools. {} biomes, {} recipes and {} models", "Finished registering assets with {} blocks ({} migrations), {} items {} tools. {} biomes ({} migrations), {} recipes and {} models",
.{blocks.count(), blocksMigrations.count(), items.count(), tools.count(), biomes.count(), recipes.count(), models.count()}, .{blocks.count(), blockMigrations.count(), items.count(), tools.count(), biomes.count(), biomeMigrations.count(), recipes.count(), models.count()},
); );
} }

View File

@ -8,24 +8,41 @@ var arenaAllocator = main.utils.NeverFailingArenaAllocator.init(main.globalAlloc
const migrationAllocator = arenaAllocator.allocator(); const migrationAllocator = arenaAllocator.allocator();
var blockMigrations: std.StringHashMap([]const u8) = .init(migrationAllocator.allocator); var blockMigrations: std.StringHashMap([]const u8) = .init(migrationAllocator.allocator);
var biomeMigrations: std.StringHashMap([]const u8) = .init(migrationAllocator.allocator);
pub fn registerBlockMigrations(migrations: *std.StringHashMap(ZonElement)) void { const MigrationType = enum {
std.log.info("Registering {} block migrations", .{migrations.count()}); block,
biome,
};
pub fn registerAll(comptime typ: MigrationType, migrations: *std.StringHashMap(ZonElement)) void {
std.log.info("Registering {} {s} migrations", .{migrations.count(), @tagName(typ)});
const collection = switch(typ) {
.block => &blockMigrations,
.biome => &biomeMigrations,
};
var migrationIterator = migrations.iterator(); var migrationIterator = migrations.iterator();
while(migrationIterator.next()) |migration| { while(migrationIterator.next()) |migration| {
register(&blockMigrations, "block", migration.key_ptr.*, migration.value_ptr.*); register(typ, collection, migration.key_ptr.*, migration.value_ptr.*);
} }
} }
fn register( fn register(
comptime typ: MigrationType,
collection: *std.StringHashMap([]const u8), collection: *std.StringHashMap([]const u8),
assetType: []const u8,
addonName: []const u8, addonName: []const u8,
migrationZon: ZonElement, migrationZon: ZonElement,
) void { ) void {
if((migrationZon.toSlice().len == 0)) { if(migrationZon != .array) {
std.log.err("Skipping incorrect {s} migration data structure from addon {s}", .{assetType, addonName}); if(migrationZon == .object and migrationZon.object.count() == 0) {
std.log.warn("Skipping empty {s} migration data structure from addon {s}", .{@tagName(typ), addonName});
return;
}
std.log.err("Skipping incorrect {s} migration data structure from addon {s}", .{@tagName(typ), addonName});
return;
}
if(migrationZon.array.items.len == 0) {
std.log.warn("Skipping empty {s} migration data structure from addon {s}", .{@tagName(typ), addonName});
return; return;
} }
@ -34,7 +51,7 @@ fn register(
const newZonOpt = migration.get(?[]const u8, "new", null); const newZonOpt = migration.get(?[]const u8, "new", null);
if(oldZonOpt == null or newZonOpt == null) { if(oldZonOpt == null or newZonOpt == null) {
std.log.err("Skipping incomplete migration in {s} migrations: '{s}:{s}' -> '{s}:{s}'", .{assetType, addonName, oldZonOpt orelse "<null>", addonName, newZonOpt orelse "<null>"}); std.log.err("Skipping incomplete migration in {s} migrations: '{s}:{s}' -> '{s}:{s}'", .{@tagName(typ), addonName, oldZonOpt orelse "<null>", addonName, newZonOpt orelse "<null>"});
continue; continue;
} }
@ -42,7 +59,7 @@ fn register(
const newZon = newZonOpt orelse unreachable; const newZon = newZonOpt orelse unreachable;
if(std.mem.eql(u8, oldZon, newZon)) { if(std.mem.eql(u8, oldZon, newZon)) {
std.log.err("Skipping identity migration in {s} migrations: '{s}:{s}' -> '{s}:{s}'", .{assetType, addonName, oldZon, addonName, newZon}); std.log.err("Skipping identity migration in {s} migrations: '{s}:{s}' -> '{s}:{s}'", .{@tagName(typ), addonName, oldZon, addonName, newZon});
continue; continue;
} }
@ -50,7 +67,7 @@ fn register(
const result = collection.getOrPut(oldAssetId) catch unreachable; const result = collection.getOrPut(oldAssetId) catch unreachable;
if(result.found_existing) { if(result.found_existing) {
std.log.err("Skipping name collision in {s} migration: '{s}' -> '{s}:{s}'", .{assetType, oldAssetId, addonName, newZon}); std.log.err("Skipping name collision in {s} migration: '{s}' -> '{s}:{s}'", .{@tagName(typ), oldAssetId, addonName, newZon});
const existingMigration = collection.get(oldAssetId) orelse unreachable; const existingMigration = collection.get(oldAssetId) orelse unreachable;
std.log.err("Already mapped to '{s}'", .{existingMigration}); std.log.err("Already mapped to '{s}'", .{existingMigration});
@ -60,23 +77,28 @@ fn register(
result.key_ptr.* = oldAssetId; result.key_ptr.* = oldAssetId;
result.value_ptr.* = newAssetId; result.value_ptr.* = newAssetId;
std.log.info("Registered {s} migration: '{s}' -> '{s}'", .{assetType, oldAssetId, newAssetId}); std.log.info("Registered {s} migration: '{s}' -> '{s}'", .{@tagName(typ), oldAssetId, newAssetId});
} }
} }
} }
pub fn applyBlockPaletteMigrations(palette: *Palette) void { pub fn apply(comptime typ: MigrationType, palette: *Palette) void {
std.log.info("Applying {} migrations to block palette", .{blockMigrations.count()}); const migrations = switch(typ) {
.block => blockMigrations,
.biome => biomeMigrations,
};
std.log.info("Applying {} migrations to {s} palette", .{migrations.count(), @tagName(typ)});
for(palette.palette.items, 0..) |assetName, i| { for(palette.palette.items, 0..) |assetName, i| {
const newAssetName = blockMigrations.get(assetName) orelse continue; const newAssetName = migrations.get(assetName) orelse continue;
std.log.info("Migrating block {s} -> {s}", .{assetName, newAssetName}); std.log.info("Migrating {s} {s} -> {s}", .{@tagName(typ), assetName, newAssetName});
palette.replaceEntry(i, newAssetName); palette.replaceEntry(i, newAssetName);
} }
} }
pub fn reset() void { pub fn reset() void {
blockMigrations.clearAndFree(); blockMigrations.clearAndFree();
biomeMigrations.clearAndFree();
_ = arenaAllocator.reset(.free_all); _ = arenaAllocator.reset(.free_all);
} }