Skip to content
Open
Show file tree
Hide file tree
Changes from 9 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions assets/cubyz/structure_tables/torches_everywhere.zig.zon
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
.{
.id = "cubyz:torches_everywhere",
.biomeTags = .{},

.structures = .{
.{
.id = "cubyz:simple_vegetation",
.chance = 0.1,
.block = "cubyz:torch",
.height = 1,
.height_variation = 0,
},
},
}
39 changes: 36 additions & 3 deletions src/assets.zig
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ pub const Assets = struct {
tools: ZonHashMap,
biomes: ZonHashMap,
biomeMigrations: AddonNameToZonMap,
structureTables: ZonHashMap,
structureTableMigrations: AddonNameToZonMap,
recipes: ZonHashMap,
models: BytesHashMap,
structureBuildingBlocks: ZonHashMap,
Expand All @@ -45,6 +47,8 @@ pub const Assets = struct {
.tools = .{},
.biomes = .{},
.biomeMigrations = .{},
.structureTables = .{},
.structureTableMigrations = .{},
.recipes = .{},
.models = .{},
.structureBuildingBlocks = .{},
Expand All @@ -61,6 +65,8 @@ pub const Assets = struct {
self.tools.deinit(allocator.allocator);
self.biomes.deinit(allocator.allocator);
self.biomeMigrations.deinit(allocator.allocator);
self.structureTables.deinit(allocator.allocator);
self.structureTableMigrations.deinit(allocator.allocator);
self.recipes.deinit(allocator.allocator);
self.models.deinit(allocator.allocator);
self.structureBuildingBlocks.deinit(allocator.allocator);
Expand All @@ -77,6 +83,8 @@ pub const Assets = struct {
.tools = self.tools.clone(allocator.allocator) catch unreachable,
.biomes = self.biomes.clone(allocator.allocator) catch unreachable,
.biomeMigrations = self.biomeMigrations.clone(allocator.allocator) catch unreachable,
.structureTables = self.structureTables.clone(allocator.allocator) catch unreachable,
.structureTableMigrations = self.structureTableMigrations.clone(allocator.allocator) catch unreachable,
.recipes = self.recipes.clone(allocator.allocator) catch unreachable,
.models = self.models.clone(allocator.allocator) catch unreachable,
.structureBuildingBlocks = self.structureBuildingBlocks.clone(allocator.allocator) catch unreachable,
Expand All @@ -94,6 +102,7 @@ pub const Assets = struct {
addon.readAllZon(allocator, "blocks", true, &self.blocks, &self.blockMigrations);
addon.readAllZon(allocator, "items", true, &self.items, &self.itemMigrations);
addon.readAllZon(allocator, "tools", true, &self.tools, null);
addon.readAllZon(allocator, "structure_tables", false, &self.structureTables, &self.structureTableMigrations);
addon.readAllZon(allocator, "biomes", true, &self.biomes, &self.biomeMigrations);
addon.readAllZon(allocator, "recipes", false, &self.recipes, null);
addon.readAllZon(allocator, "sbb", true, &self.structureBuildingBlocks, null);
Expand All @@ -105,8 +114,8 @@ pub const Assets = struct {
}
fn log(self: *Assets, typ: enum { common, world }) void {
std.log.info(
"Finished {s} assets reading with {} blocks, {} items, {} tools, {} biomes, {} recipes, {} structure building blocks, {} blueprints, {} particles, {} world presets",
.{@tagName(typ), self.blocks.count(), self.items.count(), self.tools.count(), self.biomes.count(), self.recipes.count(), self.structureBuildingBlocks.count(), self.blueprints.count(), self.particles.count(), self.worldPresets.count()},
"Finished {s} assets reading with {} blocks, {} items, {} tools, {} biomes, {} structure tables, {} recipes, {} structure building blocks, {} blueprints and {} particles",
.{@tagName(typ), self.blocks.count(), self.items.count(), self.tools.count(), self.biomes.count(), self.structureTables.count(), self.recipes.count(), self.structureBuildingBlocks.count(), self.blueprints.count(), self.particles.count()},
);
}

Expand Down Expand Up @@ -358,6 +367,7 @@ fn createAssetStringID(
}

pub fn init() void {
main.server.terrain.structures.init();
biomes_zig.init();

common = .init();
Expand Down Expand Up @@ -403,6 +413,10 @@ fn registerBiome(numericId: u32, stringId: []const u8, zon: ZonElement) void {
biomes_zig.register(stringId, numericId, zon);
}

fn registerStructureTable(numericId: u32, stringId: []const u8, zon: ZonElement) void {
if (zon == .null) std.log.err("Missing StructureTable: {s}. Will not replace.", .{stringId});
main.server.terrain.structures.register(stringId, numericId, zon);
}
fn registerRecipesFromZon(zon: ZonElement) void {
items_zig.registerRecipes(zon);
}
Expand Down Expand Up @@ -511,7 +525,7 @@ pub const Palette = struct { // MARK: Palette

var loadedAssets: bool = false;

pub fn loadWorldAssets(assetFolder: []const u8, blockPalette: *Palette, itemPalette: *Palette, toolPalette: *Palette, biomePalette: *Palette) !void { // MARK: loadWorldAssets()
pub fn loadWorldAssets(assetFolder: []const u8, blockPalette: *Palette, itemPalette: *Palette, toolPalette: *Palette, biomePalette: *Palette, structureTablePalette: *Palette) !void { // MARK: loadWorldAssets()
if (loadedAssets) return; // The assets already got loaded by the server.
loadedAssets = true;

Expand All @@ -534,6 +548,9 @@ pub fn loadWorldAssets(assetFolder: []const u8, blockPalette: *Palette, itemPale
migrations_zig.registerAll(.biome, &worldAssets.biomeMigrations);
migrations_zig.apply(.biome, biomePalette);

migrations_zig.registerAll(.structuretable, &worldAssets.structureTableMigrations);
migrations_zig.apply(.structuretable, structureTablePalette);

// models:
var modelIterator = worldAssets.models.iterator();
while (modelIterator.next()) |entry| {
Expand Down Expand Up @@ -644,6 +661,20 @@ pub fn loadWorldAssets(assetFolder: []const u8, blockPalette: *Palette, itemPale
particles_zig.ParticleManager.register(assetFolder, entry.key_ptr.*, entry.value_ptr.*);
}

// StructureTables:
var nextStructureTableNumericId: u32 = 0;
for (structureTablePalette.palette.items) |id| {
registerStructureTable(nextStructureTableNumericId, id, worldAssets.structureTables.get(id) orelse .null);
nextStructureTableNumericId += 1;
}
iterator = worldAssets.structureTables.iterator();
while (iterator.next()) |entry| {
if (main.server.terrain.structures.hasRegistered(entry.key_ptr.*)) continue;
registerStructureTable(nextStructureTableNumericId, entry.key_ptr.*, entry.value_ptr.*);
structureTablePalette.add(entry.key_ptr.*);
nextStructureTableNumericId += 1;
}

// Biomes:
var nextBiomeNumericId: u32 = 0;
for (biomePalette.palette.items) |id| {
Expand Down Expand Up @@ -692,6 +723,8 @@ pub fn unloadAssets() void { // MARK: unloadAssets()
migrations_zig.reset();
biomes_zig.reset();
migrations_zig.reset();
main.server.terrain.structures.reset();
migrations_zig.reset();
main.models.reset();
main.particles.ParticleManager.reset();
main.rotation.reset();
Expand Down
5 changes: 4 additions & 1 deletion src/game.zig
Original file line number Diff line number Diff line change
Expand Up @@ -622,6 +622,7 @@ pub const World = struct { // MARK: World
itemPalette: *assets.Palette = undefined,
toolPalette: *assets.Palette = undefined,
biomePalette: *assets.Palette = undefined,
structureTablePalette: *assets.Palette = undefined,
Copy link
Member

Choose a reason for hiding this comment

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

with the sorting, a palette isn't needed

itemDrops: ClientItemDropManager = undefined,
playerBiome: Atomic(*const main.server.terrain.biomes.Biome) = undefined,

Expand Down Expand Up @@ -667,6 +668,7 @@ pub const World = struct { // MARK: World
self.itemPalette.deinit();
self.toolPalette.deinit();
self.biomePalette.deinit();
self.structureTablePalette.deinit();
self.manager.deinit();
main.server.stop();
if (main.server.thread) |serverThread| {
Expand All @@ -682,6 +684,7 @@ pub const World = struct { // MARK: World

pub fn finishHandshake(self: *World, zon: ZonElement) !void {
// TODO: Consider using a per-world allocator.
self.structureTablePalette = try assets.Palette.init(main.globalAllocator, zon.getChild("structureTablePalette"), null);
self.blockPalette = try assets.Palette.init(main.globalAllocator, zon.getChild("blockPalette"), "cubyz:air");
errdefer self.blockPalette.deinit();
self.biomePalette = try assets.Palette.init(main.globalAllocator, zon.getChild("biomePalette"), null);
Expand All @@ -693,7 +696,7 @@ pub const World = struct { // MARK: World

const path = std.fmt.allocPrint(main.stackAllocator.allocator, "{s}/serverAssets", .{main.files.cubyzDirStr()}) catch unreachable;
defer main.stackAllocator.free(path);
try assets.loadWorldAssets(path, self.blockPalette, self.itemPalette, self.toolPalette, self.biomePalette);
try assets.loadWorldAssets(path, self.blockPalette, self.itemPalette, self.toolPalette, self.biomePalette, self.structureTablePalette);
Player.id = zon.get(u32, "player_id", std.math.maxInt(u32));
Player.inventory = ClientInventory.init(main.globalAllocator, Player.inventorySize, .normal, .serverShared, .{.playerInventory = Player.id}, .{});
Player.loadFrom(zon.getChild("player"));
Expand Down
6 changes: 6 additions & 0 deletions src/migrations.zig
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,13 @@ const Assets = main.assets.Assets;
var blockMigrations: std.StringHashMapUnmanaged([]const u8) = .{};
var itemMigrations: std.StringHashMapUnmanaged([]const u8) = .{};
var biomeMigrations: std.StringHashMapUnmanaged([]const u8) = .{};
var structureTableMigrations: std.StringHashMapUnmanaged([]const u8) = .{};

const MigrationType = enum {
block,
item,
biome,
structuretable,
};

pub fn registerAll(comptime typ: MigrationType, migrations: *Assets.AddonNameToZonMap) void {
Expand All @@ -21,6 +23,7 @@ pub fn registerAll(comptime typ: MigrationType, migrations: *Assets.AddonNameToZ
.block => &blockMigrations,
.item => &itemMigrations,
.biome => &biomeMigrations,
.structuretable => &structureTableMigrations,
};
var migrationIterator = migrations.iterator();
while (migrationIterator.next()) |migration| {
Expand Down Expand Up @@ -88,6 +91,7 @@ pub fn applySingle(comptime typ: MigrationType, assetName: []const u8) []const u
.block => blockMigrations,
.item => itemMigrations,
.biome => biomeMigrations,
.structuretable => structureTableMigrations,
};

const newAssetName = migrations.get(assetName) orelse return assetName;
Expand All @@ -100,6 +104,7 @@ pub fn apply(comptime typ: MigrationType, palette: *Palette) void {
.block => blockMigrations,
.item => itemMigrations,
.biome => biomeMigrations,
.structuretable => structureTableMigrations,
};
std.log.info("Applying {} migrations to {s} palette", .{migrations.count(), @tagName(typ)});

Expand All @@ -114,4 +119,5 @@ pub fn reset() void {
biomeMigrations = .{};
blockMigrations = .{};
itemMigrations = .{};
structureTableMigrations = .{};
}
39 changes: 36 additions & 3 deletions src/server/terrain/biomes.zig
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ const vec = @import("main.vec");
const Vec3f = main.vec.Vec3f;
const Vec3d = main.vec.Vec3d;

const StructureTable = terrain.structures.StructureTable;
pub const SimpleStructureModel = terrain.structures.SimpleStructureModel;

const Stripe = struct { // MARK: Stripe
Expand Down Expand Up @@ -253,10 +254,18 @@ pub const Biome = struct { // MARK: Biome
preferredMusic: []const u8, // TODO: Support multiple possibilities that are chosen based on time and danger.
isValidPlayerSpawn: bool,
chance: f32,
biomeTags: [][]const u8,

pub fn init(self: *Biome, id: []const u8, paletteId: u32, zon: ZonElement) void {
const minRadius = zon.get(f32, "radius", zon.get(f32, "minRadius", 256));
const maxRadius = zon.get(f32, "maxRadius", minRadius);
const biome_tags = zon.getChild("biomeTags");
var tags_list = main.ListUnmanaged([]const u8){};
if (biome_tags.toSlice().len > 0) {
for (biome_tags.toSlice()) |tag| {
tags_list.append(main.globalAllocator, tag.toString(main.globalAllocator));
}
}
self.* = Biome{
.id = main.worldArena.dupe(u8, id),
.paletteId = paletteId,
Expand Down Expand Up @@ -293,6 +302,7 @@ pub const Biome = struct { // MARK: Biome
.isValidPlayerSpawn = zon.get(bool, "validPlayerSpawn", false),
.chance = zon.get(f32, "chance", if (zon == .null) 0 else 1),
.maxSubBiomeCount = zon.get(f32, "maxSubBiomeCount", std.math.floatMax(f32)),
.biomeTags = tags_list.items,
};
if (minRadius > maxRadius) {
std.log.err("Biome {s} has invalid radius range ({d}, {d})", .{self.id, minRadius, maxRadius});
Expand Down Expand Up @@ -334,10 +344,33 @@ pub const Biome = struct { // MARK: Biome
var vegetation: main.ListUnmanaged(SimpleStructureModel) = .{};
var totalChance: f32 = 0;
defer vegetation.deinit(main.stackAllocator);
// Add structures from the biome's internal structure table
for (structures.toSlice()) |elem| {
const model = SimpleStructureModel.initModel(elem) orelse continue;
vegetation.append(main.stackAllocator, model);
totalChance += model.chance;
if (SimpleStructureModel.initModel(elem)) |model| {
vegetation.append(main.stackAllocator, model);
totalChance += model.chance;
}
}
// Add structures from structure tables outside of the biome's internal table.
const structure_tables = main.server.terrain.structures.getSlice();
for (structure_tables) |table| {
if (self.biomeTags.len > 0) {
for (self.biomeTags) |tag| {
for (table.biomeTags) |st_tag| {
if (std.mem.eql(u8, tag, st_tag)) {
for (table.structures) |model| {
vegetation.append(main.stackAllocator, model);
totalChance += model.chance;
}
}
}
}
} else if (table.biomeTags.len == 0) {
for (table.structures) |model| {
vegetation.append(main.stackAllocator, model);
totalChance += model.chance;
}
}
}
if (totalChance > 1) {
for (vegetation.items) |*model| {
Expand Down
69 changes: 69 additions & 0 deletions src/server/terrain/structures.zig
Original file line number Diff line number Diff line change
Expand Up @@ -71,3 +71,72 @@ pub const SimpleStructureModel = struct { // MARK: SimpleStructureModel
return self.vtable.hashFunction(self.data);
}
};

pub const StructureTable = struct {
id: []const u8,
biomeTags: [][]const u8,
structures: []SimpleStructureModel = &.{},
paletteId: u32,

pub fn init(self: *StructureTable, id: []const u8, paletteId: u32, zon: ZonElement) void {
const biome_tags = zon.getChild("biomeTags");
var tags_list = main.ListUnmanaged([]const u8){};
for (biome_tags.toSlice()) |tag| {
tags_list.append(main.worldArena, tag.toString(main.worldArena));
}

self.* = .{
.id = main.worldArena.dupe(u8, id),
.paletteId = paletteId,
.biomeTags = tags_list.items,
};

const structures = zon.getChild("structures");
var structure_list = main.ListUnmanaged(SimpleStructureModel){};
var total_chance: f32 = 0;
defer structure_list.deinit(main.stackAllocator);

for (structures.toSlice()) |elem| {
if (SimpleStructureModel.initModel(elem)) |model| {
structure_list.append(main.stackAllocator, model);
total_chance += model.chance;
}
}
if (total_chance > 1) {
for (structure_list.items) |*model| {
model.chance /= total_chance;
}
}
Copy link
Member

Choose a reason for hiding this comment

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

Do we need this normalization here? I think it could change the chances in unexpected ways (.chance = 1 in structure table has a different meaning from .chance = 1 in the host biome) when combined with the host biome normalization or other structure tables.

self.structures = main.worldArena.dupe(SimpleStructureModel, structure_list.items);
}
};

var structureTables: main.ListUnmanaged(StructureTable) = .{};

pub fn init() void {
structureTables = .{};
}

pub fn register(id: []const u8, paletteId: u32, zon: ZonElement) void {
var structure_table: StructureTable = undefined;
structure_table.init(id, paletteId, zon);
structureTables.append(main.worldArena, structure_table);
std.log.debug("Registered structure table: {d: >5} '{s}'", .{paletteId, id});
}
pub fn hasRegistered(id: []const u8) bool {
if (structureTables.items.len == 0) return false;
for (structureTables.items) |entry| {
if (std.mem.eql(u8, id, entry.id)) {
return true;
}
}
return false;
}

pub fn getSlice() []StructureTable {
return structureTables.items;
}

pub fn reset() void {
structureTables = .{};
}
Loading