mirror of
https://github.com/PixelGuys/Cubyz.git
synced 2025-08-03 03:06:55 -04:00
Blueprint asset loading (#1207)
* SBB * Fix rotation code * Remove rotate command * Fix segfault during generation * Delete format.bat * Pregenerate all rotated blueprints * Apply review suggestions * Apply review suggestions * Change child block storage model * Resolve child structures during load * Move structure_building_blocks.zig to terrain * Update src/rotation.zig * Apply review suggestions * Remove sbb blocks * Remove SBBGen * Remove example SBB * Remove blueprint code * Remove createAssetStringID * Apply review suggestions * Apply review suggestions * Add asset ID generation * Revert "Add asset ID generation" This reverts commit ffe8fd281486f2124ab83b0e614b6a7db97a020d. * Remove SBB loading * Apply review requests
This commit is contained in:
parent
0e9ffda1b1
commit
1ec150d624
@ -3,9 +3,12 @@ const std = @import("std");
|
||||
const blocks_zig = @import("blocks.zig");
|
||||
const items_zig = @import("items.zig");
|
||||
const migrations_zig = @import("migrations.zig");
|
||||
const blueprints_zig = @import("blueprint.zig");
|
||||
const Blueprint = blueprints_zig.Blueprint;
|
||||
const ZonElement = @import("zon.zig").ZonElement;
|
||||
const main = @import("main.zig");
|
||||
const biomes_zig = main.server.terrain.biomes;
|
||||
const sbb = main.server.terrain.structure_building_blocks;
|
||||
const NeverFailingAllocator = main.heap.NeverFailingAllocator;
|
||||
|
||||
var arena: main.heap.NeverFailingArenaAllocator = undefined;
|
||||
@ -18,6 +21,7 @@ var commonBiomes: std.StringHashMap(ZonElement) = undefined;
|
||||
var commonBiomeMigrations: std.StringHashMap(ZonElement) = undefined;
|
||||
var commonRecipes: std.StringHashMap(ZonElement) = undefined;
|
||||
var commonModels: std.StringHashMap([]const u8) = undefined;
|
||||
var commonBlueprints: std.StringHashMap([]u8) = undefined;
|
||||
|
||||
pub fn init() void {
|
||||
biomes_zig.init();
|
||||
@ -33,6 +37,7 @@ pub fn init() void {
|
||||
commonBiomeMigrations = .init(arenaAllocator.allocator);
|
||||
commonRecipes = .init(arenaAllocator.allocator);
|
||||
commonModels = .init(arenaAllocator.allocator);
|
||||
commonBlueprints = .init(arenaAllocator.allocator);
|
||||
|
||||
readAssets(
|
||||
arenaAllocator,
|
||||
@ -45,11 +50,12 @@ pub fn init() void {
|
||||
&commonBiomeMigrations,
|
||||
&commonRecipes,
|
||||
&commonModels,
|
||||
&commonBlueprints,
|
||||
);
|
||||
|
||||
std.log.info(
|
||||
"Finished assets init with {} blocks ({} migrations), {} items, {} tools. {} biomes ({} migrations), {} recipes",
|
||||
.{commonBlocks.count(), commonBlockMigrations.count(), commonItems.count(), commonTools.count(), commonBiomes.count(), commonBiomeMigrations.count(), commonRecipes.count()},
|
||||
"Finished assets init with {} blocks ({} migrations), {} items, {} tools, {} biomes ({} migrations), {} recipes, and {} blueprints",
|
||||
.{commonBlocks.count(), commonBlockMigrations.count(), commonItems.count(), commonTools.count(), commonBiomes.count(), commonBiomeMigrations.count(), commonRecipes.count(), commonBlueprints.count()},
|
||||
);
|
||||
}
|
||||
|
||||
@ -149,6 +155,7 @@ pub fn readAllZonFilesInAddons(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn createAssetStringID(
|
||||
externalAllocator: NeverFailingAllocator,
|
||||
addonName: []const u8,
|
||||
@ -173,6 +180,45 @@ fn createAssetStringID(
|
||||
|
||||
return assetId;
|
||||
}
|
||||
|
||||
pub fn readAllBlueprintFilesInAddons(
|
||||
externalAllocator: NeverFailingAllocator,
|
||||
addons: main.List(std.fs.Dir),
|
||||
addonNames: main.List([]const u8),
|
||||
subPath: []const u8,
|
||||
output: *std.StringHashMap([]u8),
|
||||
) void {
|
||||
for(addons.items, addonNames.items) |addon, addonName| {
|
||||
var dir = addon.openDir(subPath, .{.iterate = true}) catch |err| {
|
||||
if(err != error.FileNotFound) {
|
||||
std.log.err("Could not open addon directory {s}: {s}", .{subPath, @errorName(err)});
|
||||
}
|
||||
continue;
|
||||
};
|
||||
defer dir.close();
|
||||
|
||||
var walker = dir.walk(main.stackAllocator.allocator) catch unreachable;
|
||||
defer walker.deinit();
|
||||
|
||||
while(walker.next() catch |err| blk: {
|
||||
std.log.err("Got error while iterating addon directory {s}: {s}", .{subPath, @errorName(err)});
|
||||
break :blk null;
|
||||
}) |entry| {
|
||||
if(entry.kind != .file) continue;
|
||||
if(std.ascii.startsWithIgnoreCase(entry.basename, "_defaults")) continue;
|
||||
if(!std.ascii.endsWithIgnoreCase(entry.basename, ".blp")) continue;
|
||||
if(std.ascii.startsWithIgnoreCase(entry.basename, "_migrations")) continue;
|
||||
|
||||
const stringId: []u8 = createAssetStringID(externalAllocator, addonName, entry.path);
|
||||
const data = main.files.Dir.init(dir).read(externalAllocator, entry.path) catch |err| {
|
||||
std.log.err("Could not open {s}/{s}: {s}", .{subPath, entry.path, @errorName(err)});
|
||||
continue;
|
||||
};
|
||||
output.put(stringId, data) catch unreachable;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Reads obj files recursively from all subfolders.
|
||||
pub fn readAllObjFilesInAddonsHashmap(
|
||||
externalAllocator: NeverFailingAllocator,
|
||||
@ -221,6 +267,7 @@ pub fn readAssets(
|
||||
biomeMigrations: *std.StringHashMap(ZonElement),
|
||||
recipes: *std.StringHashMap(ZonElement),
|
||||
models: *std.StringHashMap([]const u8),
|
||||
blueprints: *std.StringHashMap([]u8),
|
||||
) void {
|
||||
var addons = main.List(std.fs.Dir).init(main.stackAllocator);
|
||||
defer addons.deinit();
|
||||
@ -258,6 +305,7 @@ pub fn readAssets(
|
||||
readAllZonFilesInAddons(externalAllocator, addons, addonNames, "biomes", true, biomes, biomeMigrations);
|
||||
readAllZonFilesInAddons(externalAllocator, addons, addonNames, "recipes", false, recipes, null);
|
||||
readAllObjFilesInAddonsHashmap(externalAllocator, addons, addonNames, "models", models);
|
||||
readAllBlueprintFilesInAddons(externalAllocator, addons, addonNames, "blueprints", blueprints);
|
||||
}
|
||||
|
||||
fn registerItem(assetFolder: []const u8, id: []const u8, zon: ZonElement) !void {
|
||||
@ -424,6 +472,8 @@ pub fn loadWorldAssets(assetFolder: []const u8, blockPalette: *Palette, itemPale
|
||||
defer recipes.clearAndFree();
|
||||
var models = commonModels.cloneWithAllocator(main.stackAllocator.allocator) catch unreachable;
|
||||
defer models.clearAndFree();
|
||||
var blueprints = commonBlueprints.cloneWithAllocator(main.stackAllocator.allocator) catch unreachable;
|
||||
defer blueprints.clearAndFree();
|
||||
|
||||
readAssets(
|
||||
arenaAllocator,
|
||||
@ -436,6 +486,7 @@ pub fn loadWorldAssets(assetFolder: []const u8, blockPalette: *Palette, itemPale
|
||||
&biomeMigrations,
|
||||
&recipes,
|
||||
&models,
|
||||
&blueprints,
|
||||
);
|
||||
errdefer unloadAssets();
|
||||
|
||||
@ -542,6 +593,8 @@ pub fn loadWorldAssets(assetFolder: []const u8, blockPalette: *Palette, itemPale
|
||||
registerRecipesFromZon(entry.value_ptr.*);
|
||||
}
|
||||
|
||||
try sbb.registerBlueprints(&blueprints);
|
||||
|
||||
// Biomes:
|
||||
var nextBiomeNumericId: u32 = 0;
|
||||
for(biomePalette.palette.items) |id| {
|
||||
@ -577,7 +630,7 @@ pub fn loadWorldAssets(assetFolder: []const u8, blockPalette: *Palette, itemPale
|
||||
}
|
||||
|
||||
std.log.info(
|
||||
"Finished registering assets with {} blocks ({} migrations), {} items {} tools. {} biomes ({} migrations), {} recipes and {} models",
|
||||
"Finished registering assets with {} blocks ({} migrations), {} items {} tools, {} biomes ({} migrations), {} recipes and {} models",
|
||||
.{blocks.count(), blockMigrations.count(), items.count(), tools.count(), biomes.count(), biomeMigrations.count(), recipes.count(), models.count()},
|
||||
);
|
||||
}
|
||||
@ -586,6 +639,7 @@ pub fn unloadAssets() void { // MARK: unloadAssets()
|
||||
if(!loadedAssets) return;
|
||||
loadedAssets = false;
|
||||
|
||||
sbb.reset();
|
||||
blocks_zig.reset();
|
||||
items_zig.reset();
|
||||
biomes_zig.reset();
|
||||
|
@ -16,10 +16,12 @@ const rotation = @import("rotation.zig");
|
||||
const RotationMode = rotation.RotationMode;
|
||||
const Degrees = rotation.Degrees;
|
||||
const Entity = main.server.Entity;
|
||||
const sbb = main.server.terrain.structure_building_blocks;
|
||||
|
||||
pub const BlockTag = enum(u32) {
|
||||
air = 0,
|
||||
fluid = 1,
|
||||
sbbChild = 2,
|
||||
_,
|
||||
|
||||
var tagList: main.List([]const u8) = .init(allocator);
|
||||
@ -142,6 +144,12 @@ pub fn register(_: []const u8, id: []const u8, zon: ZonElement) u16 {
|
||||
|
||||
_blockTags[size] = BlockTag.loadFromZon(allocator, zon.getChild("tags"));
|
||||
if(_blockTags[size].len == 0) std.log.err("Block {s} is missing 'tags' field", .{id});
|
||||
for(_blockTags[size]) |tag| {
|
||||
if(tag == BlockTag.sbbChild) {
|
||||
sbb.registerChildBlock(@intCast(size), _id[size]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
_light[size] = zon.get(u32, "emittedLight", 0);
|
||||
_absorption[size] = zon.get(u32, "absorbedLight", 0xffffff);
|
||||
_degradable[size] = zon.get(bool, "degradable", false);
|
||||
|
149
src/server/terrain/structure_building_blocks.zig
Normal file
149
src/server/terrain/structure_building_blocks.zig
Normal file
@ -0,0 +1,149 @@
|
||||
const std = @import("std");
|
||||
|
||||
const main = @import("root");
|
||||
const ZonElement = main.ZonElement;
|
||||
const Blueprint = main.blueprint.Blueprint;
|
||||
const List = main.List;
|
||||
const ListUnmanaged = main.ListUnmanaged;
|
||||
const AliasTable = main.utils.AliasTable;
|
||||
const Neighbor = main.chunk.Neighbor;
|
||||
const Block = main.blocks.Block;
|
||||
const Degrees = main.rotation.Degrees;
|
||||
const NeverFailingAllocator = main.heap.NeverFailingAllocator;
|
||||
|
||||
var arena = main.heap.NeverFailingArenaAllocator.init(main.globalAllocator);
|
||||
const arenaAllocator = arena.allocator();
|
||||
|
||||
var blueprintCache: std.StringHashMapUnmanaged(*[4]BlueprintEntry) = .{};
|
||||
var childrenToResolve: List(struct {parentId: []const u8, colorName: []const u8, colorIndex: usize, childIndex: usize, structureId: []const u8}) = undefined;
|
||||
|
||||
const originBlockStringId = "cubyz:sbb/origin";
|
||||
var originBlockNumericId: u16 = 0;
|
||||
|
||||
// Maps global child block numeric ID to index used to locally represent that child block.
|
||||
var childBlockNumericIdMap: std.AutoHashMapUnmanaged(u16, u16) = .{};
|
||||
var childBlockStringId: ListUnmanaged([]const u8) = .{};
|
||||
|
||||
const BlueprintEntry = struct {
|
||||
blueprint: Blueprint,
|
||||
originBlock: StructureBlock,
|
||||
childBlocks: []StructureBlock,
|
||||
|
||||
const StructureBlock = struct {
|
||||
x: u16,
|
||||
y: u16,
|
||||
z: u16,
|
||||
index: u16,
|
||||
data: u16,
|
||||
|
||||
pub inline fn direction(self: StructureBlock) Neighbor {
|
||||
return @enumFromInt(self.data);
|
||||
}
|
||||
};
|
||||
|
||||
fn init(blueprint: Blueprint, stringId: []const u8) !BlueprintEntry {
|
||||
var self: BlueprintEntry = .{
|
||||
.blueprint = blueprint,
|
||||
.originBlock = undefined,
|
||||
.childBlocks = undefined,
|
||||
};
|
||||
|
||||
var hasOrigin = false;
|
||||
var childBlocks: ListUnmanaged(StructureBlock) = .{};
|
||||
defer childBlocks.deinit(main.stackAllocator);
|
||||
|
||||
for(0..blueprint.blocks.width) |x| {
|
||||
for(0..blueprint.blocks.depth) |y| {
|
||||
for(0..blueprint.blocks.height) |z| {
|
||||
const block = blueprint.blocks.get(x, y, z);
|
||||
if(isOriginBlock(block)) {
|
||||
if(hasOrigin) {
|
||||
std.log.err("[{s}] Multiple origin blocks found.", .{stringId});
|
||||
return error.MultipleOriginBlocks;
|
||||
} else {
|
||||
self.originBlock = StructureBlock{
|
||||
.x = @intCast(x),
|
||||
.y = @intCast(y),
|
||||
.z = @intCast(z),
|
||||
.index = std.math.maxInt(u16),
|
||||
.data = block.data,
|
||||
};
|
||||
hasOrigin = true;
|
||||
}
|
||||
} else if(isChildBlock(block)) {
|
||||
const childBlockLocalId = childBlockNumericIdMap.get(block.typ) orelse return error.ChildBlockNotRecognized;
|
||||
childBlocks.append(main.stackAllocator, .{
|
||||
.x = @intCast(x),
|
||||
.y = @intCast(y),
|
||||
.z = @intCast(z),
|
||||
.index = childBlockLocalId,
|
||||
.data = block.data,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if(!hasOrigin) {
|
||||
std.log.err("[{s}] No origin block found.", .{stringId});
|
||||
return error.NoOriginBlock;
|
||||
}
|
||||
self.childBlocks = arenaAllocator.dupe(StructureBlock, childBlocks.items);
|
||||
|
||||
return self;
|
||||
}
|
||||
};
|
||||
|
||||
pub fn isChildBlock(block: Block) bool {
|
||||
return childBlockNumericIdMap.contains(block.typ);
|
||||
}
|
||||
|
||||
pub fn isOriginBlock(block: Block) bool {
|
||||
return block.typ == originBlockNumericId;
|
||||
}
|
||||
|
||||
pub fn registerChildBlock(numericId: u16, stringId: []const u8) void {
|
||||
const index: u16 = @intCast(childBlockNumericIdMap.count());
|
||||
childBlockNumericIdMap.put(arenaAllocator.allocator, numericId, index) catch unreachable;
|
||||
// Take only color name from the ID.
|
||||
var iterator = std.mem.splitBackwardsScalar(u8, stringId, '/');
|
||||
const colorName = iterator.first();
|
||||
childBlockStringId.append(arenaAllocator, arenaAllocator.dupe(u8, colorName));
|
||||
std.log.debug("Structure child block '{s}' {} ('{s}' {}) ", .{colorName, index, stringId, numericId});
|
||||
}
|
||||
|
||||
pub fn registerBlueprints(blueprints: *std.StringHashMap([]u8)) !void {
|
||||
std.debug.assert(blueprintCache.capacity() == 0);
|
||||
|
||||
originBlockNumericId = main.blocks.parseBlock(originBlockStringId).typ;
|
||||
std.log.debug("Origin block numeric id: {}", .{originBlockNumericId});
|
||||
|
||||
blueprintCache.ensureTotalCapacity(arenaAllocator.allocator, blueprints.count()) catch unreachable;
|
||||
|
||||
var iterator = blueprints.iterator();
|
||||
while(iterator.next()) |entry| {
|
||||
const stringId = entry.key_ptr.*;
|
||||
const blueprint0 = Blueprint.load(arenaAllocator, entry.value_ptr.*) catch |err| {
|
||||
std.log.err("Could not load blueprint {s}: {s}", .{stringId, @errorName(err)});
|
||||
continue;
|
||||
};
|
||||
|
||||
const rotatedBlueprints = arenaAllocator.create([4]BlueprintEntry);
|
||||
rotatedBlueprints.* = .{
|
||||
BlueprintEntry.init(blueprint0, stringId) catch continue,
|
||||
BlueprintEntry.init(blueprint0.rotateZ(arenaAllocator, .@"90"), stringId) catch continue,
|
||||
BlueprintEntry.init(blueprint0.rotateZ(arenaAllocator, .@"180"), stringId) catch continue,
|
||||
BlueprintEntry.init(blueprint0.rotateZ(arenaAllocator, .@"270"), stringId) catch continue,
|
||||
};
|
||||
|
||||
blueprintCache.put(arenaAllocator.allocator, arenaAllocator.dupe(u8, stringId), rotatedBlueprints) catch unreachable;
|
||||
std.log.debug("Registered blueprint: {s}", .{stringId});
|
||||
}
|
||||
}
|
||||
|
||||
pub fn reset() void {
|
||||
childBlockNumericIdMap = .{};
|
||||
childBlockStringId = .{};
|
||||
blueprintCache = .{};
|
||||
|
||||
_ = arena.reset(.free_all);
|
||||
}
|
@ -20,6 +20,8 @@ pub const CaveMap = @import("CaveMap.zig");
|
||||
|
||||
pub const StructureMap = @import("StructureMap.zig");
|
||||
|
||||
pub const structure_building_blocks = @import("structure_building_blocks.zig");
|
||||
|
||||
/// A generator for setting the actual Blocks in each Chunk.
|
||||
pub const BlockGenerator = struct {
|
||||
init: *const fn(parameters: ZonElement) void,
|
||||
|
Loading…
x
Reference in New Issue
Block a user