Sort the world list in save selection by least recently used.

Also did some further refactoring to make it easier to deal with different world paths (#606) in the future.
fixes #1311
This commit is contained in:
IntegratedQuantum 2025-05-13 20:38:07 +02:00
parent ab4beca0f4
commit 8bcc00f536
8 changed files with 106 additions and 76 deletions

View File

@ -325,7 +325,7 @@ pub const Sync = struct { // MARK: Sync
defer main.stackAllocator.free(dest);
const hashedName = std.base64.url_safe.Encoder.encode(dest, user.name);
const path = std.fmt.allocPrint(main.stackAllocator.allocator, "saves/{s}/players/{s}.zig.zon", .{main.server.world.?.name, hashedName}) catch unreachable;
const path = std.fmt.allocPrint(main.stackAllocator.allocator, "saves/{s}/players/{s}.zig.zon", .{main.server.world.?.path, hashedName}) catch unreachable;
defer main.stackAllocator.free(path);
const playerData = main.files.readToZon(main.stackAllocator, path) catch .null;

View File

@ -46,7 +46,8 @@ fn createWorld(_: usize) void {
fn flawedCreateWorld() !void {
const worldName = textInput.currentString.items;
const saveFolder = std.fmt.allocPrint(main.stackAllocator.allocator, "saves/{s}", .{worldName}) catch unreachable;
const worldPath = worldName; // TODO: Make sure that only valid file name characters are used, and add a check to allow different worlds of the same name.
const saveFolder = std.fmt.allocPrint(main.stackAllocator.allocator, "saves/{s}", .{worldPath}) catch unreachable;
defer main.stackAllocator.free(saveFolder);
if(std.fs.cwd().openDir(saveFolder, .{})) |_dir| {
var dir = _dir;
@ -55,7 +56,7 @@ fn flawedCreateWorld() !void {
} else |_| {}
try main.files.makeDir(saveFolder);
{
const generatorSettingsPath = std.fmt.allocPrint(main.stackAllocator.allocator, "saves/{s}/generatorSettings.zig.zon", .{worldName}) catch unreachable;
const generatorSettingsPath = std.fmt.allocPrint(main.stackAllocator.allocator, "saves/{s}/generatorSettings.zig.zon", .{worldPath}) catch unreachable;
defer main.stackAllocator.free(generatorSettingsPath);
const generatorSettings = main.ZonElement.initObject(main.stackAllocator);
defer generatorSettings.deinit(main.stackAllocator);
@ -75,7 +76,19 @@ fn flawedCreateWorld() !void {
try main.files.writeZon(generatorSettingsPath, generatorSettings);
}
{
const gamerulePath = std.fmt.allocPrint(main.stackAllocator.allocator, "saves/{s}/gamerules.zig.zon", .{worldName}) catch unreachable;
const worldInfoPath = std.fmt.allocPrint(main.stackAllocator.allocator, "saves/{s}/world.zig.zon", .{worldPath}) catch unreachable;
defer main.stackAllocator.free(worldInfoPath);
const worldInfo = main.ZonElement.initObject(main.stackAllocator);
defer worldInfo.deinit(main.stackAllocator);
worldInfo.put("name", worldName);
worldInfo.put("version", main.server.world_zig.worldDataVersion);
worldInfo.put("lastUsedTime", std.time.milliTimestamp());
try main.files.writeZon(worldInfoPath, worldInfo);
}
{
const gamerulePath = std.fmt.allocPrint(main.stackAllocator.allocator, "saves/{s}/gamerules.zig.zon", .{worldPath}) catch unreachable;
defer main.stackAllocator.free(gamerulePath);
const gamerules = main.ZonElement.initObject(main.stackAllocator);
defer gamerules.deinit(main.stackAllocator);
@ -86,7 +99,7 @@ fn flawedCreateWorld() !void {
try main.files.writeZon(gamerulePath, gamerules);
}
{ // Make assets subfolder
const assetsPath = std.fmt.allocPrint(main.stackAllocator.allocator, "saves/{s}/assets", .{worldName}) catch unreachable;
const assetsPath = std.fmt.allocPrint(main.stackAllocator.allocator, "saves/{s}/assets", .{worldPath}) catch unreachable;
defer main.stackAllocator.free(assetsPath);
try main.files.makeDir(assetsPath);
}

View File

@ -29,6 +29,13 @@ pub var needsUpdate: bool = false;
var deleteIcon: Texture = undefined;
var fileExplorerIcon: Texture = undefined;
const WorldInfo = struct {
lastUsedTime: i64,
name: []const u8,
fileName: []const u8,
};
var worldList: main.ListUnmanaged(WorldInfo) = .{};
pub fn init() void {
deleteIcon = Texture.initFromFile("assets/cubyz/ui/delete_icon.png");
fileExplorerIcon = Texture.initFromFile("assets/cubyz/ui/file_explorer_icon.png");
@ -70,25 +77,18 @@ pub fn openWorld(name: []const u8) void {
gui.openHud();
}
fn openWorldWrap(namePtr: usize) void { // TODO: Improve this situation. Maybe it makes sense to always use 2 arguments in the Callback.
const nullTerminatedName: [*:0]const u8 = @ptrFromInt(namePtr);
const name = std.mem.span(nullTerminatedName);
openWorld(name);
fn openWorldWrap(index: usize) void { // TODO: Improve this situation. Maybe it makes sense to always use 2 arguments in the Callback.
openWorld(worldList.items[index].fileName);
}
fn deleteWorld(namePtr: usize) void {
const nullTerminatedName: [*:0]const u8 = @ptrFromInt(namePtr);
const name = std.mem.span(nullTerminatedName);
fn deleteWorld(index: usize) void {
main.gui.closeWindow("delete_world_confirmation");
main.gui.windowlist.delete_world_confirmation.setDeleteWorldName(name);
main.gui.windowlist.delete_world_confirmation.setDeleteWorldName(worldList.items[index].fileName);
main.gui.openWindow("delete_world_confirmation");
}
fn openFolder(namePtr: usize) void {
const nullTerminatedName: [*:0]const u8 = @ptrFromInt(namePtr);
const name = std.mem.span(nullTerminatedName);
const path = std.fmt.allocPrint(main.stackAllocator.allocator, "saves/{s}", .{name}) catch unreachable;
fn openFolder(index: usize) void {
const path = std.fmt.allocPrint(main.stackAllocator.allocator, "saves/{s}", .{worldList.items[index].fileName}) catch unreachable;
defer main.stackAllocator.free(path);
main.files.openDirInWindow(path);
@ -149,22 +149,41 @@ pub fn onOpen() void {
break :readingSaves;
}) |entry| {
if(entry.kind == .directory) {
const row = HorizontalList.init();
const worldInfoPath = std.fmt.allocPrint(main.stackAllocator.allocator, "saves/{s}/world.zig.zon", .{entry.name}) catch unreachable;
defer main.stackAllocator.free(worldInfoPath);
const worldInfo = main.files.readToZon(main.stackAllocator, worldInfoPath) catch |err| {
std.log.err("Couldn't open save {s}: {s}", .{worldInfoPath, @errorName(err)});
continue;
};
defer worldInfo.deinit(main.stackAllocator);
const decodedName = parseEscapedFolderName(main.stackAllocator, entry.name);
defer main.stackAllocator.free(decodedName);
const name = buttonNameArena.allocator().dupeZ(u8, entry.name); // Null terminate, so we can later recover the string from just the pointer.
const buttonName = std.fmt.allocPrint(buttonNameArena.allocator().allocator, "{s}", .{decodedName}) catch unreachable;
row.add(Button.initText(.{0, 0}, 128, buttonName, .{.callback = &openWorldWrap, .arg = @intFromPtr(name.ptr)}));
row.add(Button.initIcon(.{8, 0}, .{16, 16}, fileExplorerIcon, false, .{.callback = &openFolder, .arg = @intFromPtr(name.ptr)}));
row.add(Button.initIcon(.{8, 0}, .{16, 16}, deleteIcon, false, .{.callback = &deleteWorld, .arg = @intFromPtr(name.ptr)}));
row.finish(.{0, 0}, .center);
list.add(row);
worldList.append(main.globalAllocator, .{
.fileName = main.globalAllocator.dupe(u8, entry.name),
.lastUsedTime = worldInfo.get(i64, "lastUsedTime", 0),
.name = main.globalAllocator.dupe(u8, worldInfo.get([]const u8, "", decodedName)),
});
}
}
}
std.sort.insertion(WorldInfo, worldList.items, {}, struct {
fn lessThan(_: void, lhs: WorldInfo, rhs: WorldInfo)bool {
return rhs.lastUsedTime -% lhs.lastUsedTime < 0;
}
}.lessThan);
for(worldList.items, 0..) |worldInfo, i| {
const row = HorizontalList.init();
row.add(Button.initText(.{0, 0}, 128, worldInfo.name, .{.callback = &openWorldWrap, .arg = i}));
row.add(Button.initIcon(.{8, 0}, .{16, 16}, fileExplorerIcon, false, .{.callback = &openFolder, .arg = i}));
row.add(Button.initIcon(.{8, 0}, .{16, 16}, deleteIcon, false, .{.callback = &deleteWorld, .arg = i}));
row.finish(.{0, 0}, .center);
list.add(row);
}
list.finish(.center);
window.rootComponent = list.toComponent();
window.contentSize = window.rootComponent.?.pos() + window.rootComponent.?.size() + @as(Vec2f, @splat(padding));
@ -172,6 +191,11 @@ pub fn onOpen() void {
}
pub fn onClose() void {
for(worldList.items) |worldInfo| {
main.globalAllocator.free(worldInfo.fileName);
main.globalAllocator.free(worldInfo.name);
}
worldList.clearAndFree(main.globalAllocator);
buttonNameArena.deinit();
if(window.rootComponent) |*comp| {
comp.deinit();

View File

@ -667,7 +667,7 @@ pub const Protocols = struct {
std.log.info("User {s} joined using version {s}.", .{name, version});
{
const path = std.fmt.allocPrint(main.stackAllocator.allocator, "saves/{s}/assets/", .{main.server.world.?.name}) catch unreachable;
const path = std.fmt.allocPrint(main.stackAllocator.allocator, "saves/{s}/assets/", .{main.server.world.?.path}) catch unreachable;
defer main.stackAllocator.free(path);
var dir = try std.fs.cwd().openDir(path, .{.iterate = true});
defer dir.close();

View File

@ -17,7 +17,8 @@ const Blueprint = main.blueprint.Blueprint;
const NeverFailingAllocator = main.heap.NeverFailingAllocator;
const CircularBufferQueue = main.utils.CircularBufferQueue;
pub const ServerWorld = @import("world.zig").ServerWorld;
pub const world_zig = @import("world.zig");
pub const ServerWorld = world_zig.ServerWorld;
pub const terrain = @import("terrain/terrain.zig");
pub const Entity = @import("Entity.zig");
pub const storage = @import("storage.zig");

View File

@ -215,7 +215,7 @@ fn cacheInit(pos: chunk.ChunkPosition) *RegionFile {
return region;
}
hashMapMutex.unlock();
const path: []const u8 = std.fmt.allocPrint(main.stackAllocator.allocator, "saves/{s}/chunks", .{server.world.?.name}) catch unreachable;
const path: []const u8 = std.fmt.allocPrint(main.stackAllocator.allocator, "saves/{s}/chunks", .{server.world.?.path}) catch unreachable;
defer main.stackAllocator.free(path);
return RegionFile.init(pos, path);
}

View File

@ -125,7 +125,7 @@ pub const MapFragment = struct { // MARK: MapFragment
};
pub fn load(self: *MapFragment, biomePalette: *main.assets.Palette, originalHeightMap: ?*[mapSize][mapSize]i32) !NeighborInfo {
const saveFolder: []const u8 = std.fmt.allocPrint(main.stackAllocator.allocator, "saves/{s}/maps", .{main.server.world.?.name}) catch unreachable;
const saveFolder: []const u8 = std.fmt.allocPrint(main.stackAllocator.allocator, "saves/{s}/maps", .{main.server.world.?.path}) catch unreachable;
defer main.stackAllocator.free(saveFolder);
const path = std.fmt.allocPrint(main.stackAllocator.allocator, "{s}/{}/{}/{}.surface", .{saveFolder, self.pos.voxelSize, self.pos.wx, self.pos.wy}) catch unreachable;
@ -208,7 +208,7 @@ pub const MapFragment = struct { // MARK: MapFragment
outputWriter.writeInt(u8, @bitCast(header.neighborInfo));
outputWriter.writeSlice(compressedData);
const saveFolder: []const u8 = std.fmt.allocPrint(main.stackAllocator.allocator, "saves/{s}/maps", .{main.server.world.?.name}) catch unreachable;
const saveFolder: []const u8 = std.fmt.allocPrint(main.stackAllocator.allocator, "saves/{s}/maps", .{main.server.world.?.path}) catch unreachable;
defer main.stackAllocator.free(saveFolder);
const path = std.fmt.allocPrint(main.stackAllocator.allocator, "{s}/{}/{}/{}.surface", .{saveFolder, self.pos.voxelSize, self.pos.wx, self.pos.wy}) catch unreachable;

View File

@ -355,8 +355,9 @@ const ChunkManager = struct { // MARK: ChunkManager
}
};
pub const worldDataVersion: u32 = 2;
const WorldIO = struct { // MARK: WorldIO
const worldDataVersion: u32 = 2;
dir: files.Dir,
world: *ServerWorld,
@ -372,10 +373,6 @@ const WorldIO = struct { // MARK: WorldIO
self.dir.close();
}
pub fn hasWorldData(self: WorldIO) bool {
return self.dir.hasFile("world.zig.zon");
}
/// Load the seed, which is needed before custom item and ore generation.
pub fn loadWorldSeed(self: WorldIO) !u64 {
const worldData = try self.dir.readToZon(main.stackAllocator, "world.zig.zon");
@ -384,7 +381,7 @@ const WorldIO = struct { // MARK: WorldIO
std.log.err("Cannot read world file version {}. Expected version {}.", .{worldData.get(u32, "version", 0), worldDataVersion});
return error.OldWorld;
}
return worldData.get(u64, "seed", 0);
return worldData.get(?u64, "seed", null) orelse main.random.nextInt(u48, &main.seed);
}
pub fn loadWorldData(self: WorldIO) !void {
@ -395,6 +392,7 @@ const WorldIO = struct { // MARK: WorldIO
self.world.gameTime = worldData.get(i64, "gameTime", 0);
self.world.spawn = worldData.get(Vec3i, "spawn", .{0, 0, 0});
self.world.biomeChecksum = worldData.get(i64, "biomeChecksum", 0);
self.world.name = main.globalAllocator.dupe(u8, worldData.get([]const u8, "name", self.world.path));
}
pub fn saveWorldData(self: WorldIO) !void {
@ -406,6 +404,8 @@ const WorldIO = struct { // MARK: WorldIO
worldData.put("gameTime", self.world.gameTime);
worldData.put("spawn", self.world.spawn);
worldData.put("biomeChecksum", self.world.biomeChecksum);
worldData.put("name", self.world.name);
worldData.put("lastUsedTime", std.time.milliTimestamp());
// TODO: Save entities
try self.dir.writeZon("world.zig.zon", worldData);
}
@ -420,8 +420,6 @@ pub const ServerWorld = struct { // MARK: ServerWorld
biomePalette: *main.assets.Palette = undefined,
chunkManager: ChunkManager = undefined,
generated: bool = false,
gameTime: i64 = 0,
milliTime: i64,
lastUpdateTime: i64,
@ -432,7 +430,8 @@ pub const ServerWorld = struct { // MARK: ServerWorld
allowCheats: bool = undefined,
seed: u64,
name: []const u8,
path: []const u8,
name: []const u8 = &.{},
spawn: Vec3i = undefined,
wio: WorldIO = undefined,
@ -454,14 +453,14 @@ pub const ServerWorld = struct { // MARK: ServerWorld
milliTimeStamp: i64,
};
pub fn init(name: []const u8, nullGeneratorSettings: ?ZonElement) !*ServerWorld { // MARK: init()
pub fn init(path: []const u8, nullGeneratorSettings: ?ZonElement) !*ServerWorld { // MARK: init()
covert_old_worlds: { // TODO: Remove after #480
const worldDatPath = try std.fmt.allocPrint(main.stackAllocator.allocator, "saves/{s}/world.dat", .{name});
const worldDatPath = try std.fmt.allocPrint(main.stackAllocator.allocator, "saves/{s}/world.dat", .{path});
defer main.stackAllocator.free(worldDatPath);
if(std.fs.cwd().openFile(worldDatPath, .{})) |file| {
file.close();
std.log.warn("Detected old world in saves/{s}. Converting all .json files to .zig.zon", .{name});
const dirPath = try std.fmt.allocPrint(main.stackAllocator.allocator, "saves/{s}", .{name});
std.log.warn("Detected old world in saves/{s}. Converting all .json files to .zig.zon", .{path});
const dirPath = try std.fmt.allocPrint(main.stackAllocator.allocator, "saves/{s}", .{path});
defer main.stackAllocator.free(dirPath);
var dir = std.fs.cwd().openDir(dirPath, .{.iterate = true}) catch |err| {
std.log.err("Could not open world directory to convert json files: {s}. Conversion aborted", .{@errorName(err)});
@ -490,7 +489,7 @@ pub const ServerWorld = struct { // MARK: ServerWorld
.milliTime = std.time.milliTimestamp(),
.lastUnimportantDataSent = std.time.milliTimestamp(),
.seed = @bitCast(@as(i64, @truncate(std.time.nanoTimestamp()))),
.name = main.globalAllocator.dupe(u8, name),
.path = main.globalAllocator.dupe(u8, path),
.chunkUpdateQueue = .init(main.globalAllocator, 256),
.regionUpdateQueue = .init(main.globalAllocator, 256),
};
@ -505,45 +504,38 @@ pub const ServerWorld = struct { // MARK: ServerWorld
if(nullGeneratorSettings) |_generatorSettings| {
generatorSettings = _generatorSettings;
// Store generator settings:
try files.writeZon(try std.fmt.allocPrint(arenaAllocator.allocator, "saves/{s}/generatorSettings.zig.zon", .{name}), generatorSettings);
try files.writeZon(try std.fmt.allocPrint(arenaAllocator.allocator, "saves/{s}/generatorSettings.zig.zon", .{path}), generatorSettings);
} else { // Read the generator settings:
generatorSettings = try files.readToZon(arenaAllocator, try std.fmt.allocPrint(arenaAllocator.allocator, "saves/{s}/generatorSettings.zig.zon", .{name}));
generatorSettings = try files.readToZon(arenaAllocator, try std.fmt.allocPrint(arenaAllocator.allocator, "saves/{s}/generatorSettings.zig.zon", .{path}));
}
self.wio = WorldIO.init(try files.openDir(try std.fmt.allocPrint(arenaAllocator.allocator, "saves/{s}", .{name})), self);
self.wio = WorldIO.init(try files.openDir(try std.fmt.allocPrint(arenaAllocator.allocator, "saves/{s}", .{path})), self);
errdefer self.wio.deinit();
const blockPaletteZon = files.readToZon(arenaAllocator, try std.fmt.allocPrint(arenaAllocator.allocator, "saves/{s}/palette.zig.zon", .{name})) catch .null;
const blockPaletteZon = files.readToZon(arenaAllocator, try std.fmt.allocPrint(arenaAllocator.allocator, "saves/{s}/palette.zig.zon", .{path})) catch .null;
self.blockPalette = try main.assets.Palette.init(main.globalAllocator, blockPaletteZon, "cubyz:air");
errdefer self.blockPalette.deinit();
std.log.info("Loaded save block palette with {} blocks.", .{self.blockPalette.size()});
const itemPaletteZon = files.readToZon(arenaAllocator, try std.fmt.allocPrint(arenaAllocator.allocator, "saves/{s}/item_palette.zig.zon", .{name})) catch .null;
const itemPaletteZon = files.readToZon(arenaAllocator, try std.fmt.allocPrint(arenaAllocator.allocator, "saves/{s}/item_palette.zig.zon", .{path})) catch .null;
self.itemPalette = try main.assets.Palette.init(main.globalAllocator, itemPaletteZon, null);
errdefer self.itemPalette.deinit();
std.log.info("Loaded save item palette with {} items.", .{self.itemPalette.size()});
const biomePaletteZon = files.readToZon(arenaAllocator, try std.fmt.allocPrint(arenaAllocator.allocator, "saves/{s}/biome_palette.zig.zon", .{name})) catch .null;
const biomePaletteZon = files.readToZon(arenaAllocator, try std.fmt.allocPrint(arenaAllocator.allocator, "saves/{s}/biome_palette.zig.zon", .{path})) catch .null;
self.biomePalette = try main.assets.Palette.init(main.globalAllocator, biomePaletteZon, null);
errdefer self.biomePalette.deinit();
std.log.info("Loaded save biome palette with {} biomes.", .{self.biomePalette.size()});
errdefer main.assets.unloadAssets();
if(self.wio.hasWorldData()) {
self.seed = try self.wio.loadWorldSeed();
self.generated = true;
try main.assets.loadWorldAssets(try std.fmt.allocPrint(arenaAllocator.allocator, "saves/{s}/assets/", .{name}), self.blockPalette, self.itemPalette, self.biomePalette);
} else {
self.seed = main.random.nextInt(u48, &main.seed);
try main.assets.loadWorldAssets(try std.fmt.allocPrint(arenaAllocator.allocator, "saves/{s}/assets/", .{name}), self.blockPalette, self.itemPalette, self.biomePalette);
try self.wio.saveWorldData();
}
self.seed = try self.wio.loadWorldSeed();
try main.assets.loadWorldAssets(try std.fmt.allocPrint(arenaAllocator.allocator, "saves/{s}/assets/", .{path}), self.blockPalette, self.itemPalette, self.biomePalette);
// Store the block palette now that everything is loaded.
try files.writeZon(try std.fmt.allocPrint(arenaAllocator.allocator, "saves/{s}/palette.zig.zon", .{name}), self.blockPalette.storeToZon(arenaAllocator));
try files.writeZon(try std.fmt.allocPrint(arenaAllocator.allocator, "saves/{s}/biome_palette.zig.zon", .{name}), self.biomePalette.storeToZon(arenaAllocator));
try files.writeZon(try std.fmt.allocPrint(arenaAllocator.allocator, "saves/{s}/item_palette.zig.zon", .{name}), self.itemPalette.storeToZon(arenaAllocator));
try files.writeZon(try std.fmt.allocPrint(arenaAllocator.allocator, "saves/{s}/palette.zig.zon", .{path}), self.blockPalette.storeToZon(arenaAllocator));
try files.writeZon(try std.fmt.allocPrint(arenaAllocator.allocator, "saves/{s}/biome_palette.zig.zon", .{path}), self.biomePalette.storeToZon(arenaAllocator));
try files.writeZon(try std.fmt.allocPrint(arenaAllocator.allocator, "saves/{s}/item_palette.zig.zon", .{path}), self.itemPalette.storeToZon(arenaAllocator));
var gamerules = files.readToZon(arenaAllocator, try std.fmt.allocPrint(arenaAllocator.allocator, "saves/{s}/gamerules.zig.zon", .{name})) catch ZonElement.initObject(arenaAllocator);
var gamerules = files.readToZon(arenaAllocator, try std.fmt.allocPrint(arenaAllocator.allocator, "saves/{s}/gamerules.zig.zon", .{path})) catch ZonElement.initObject(arenaAllocator);
self.defaultGamemode = std.meta.stringToEnum(main.game.Gamemode, gamerules.get([]const u8, "default_gamemode", "creative")) orelse .creative;
self.allowCheats = gamerules.get(bool, "cheats", true);
@ -573,7 +565,8 @@ pub const ServerWorld = struct { // MARK: ServerWorld
self.itemPalette.deinit();
self.biomePalette.deinit();
self.wio.deinit();
main.globalAllocator.free(self.name);
main.globalAllocator.free(self.path);
//main.globalAllocator.free(self.name);
main.globalAllocator.destroy(self);
}
@ -666,19 +659,19 @@ pub const ServerWorld = struct { // MARK: ServerWorld
fn regenerateLOD(self: *ServerWorld, newBiomeCheckSum: i64) !void {
std.log.info("Biomes have changed. Regenerating LODs... (this might take some time)", .{});
const hasSurfaceMaps = blk: {
const path = std.fmt.allocPrint(main.stackAllocator.allocator, "saves/{s}/maps", .{self.name}) catch unreachable;
const path = std.fmt.allocPrint(main.stackAllocator.allocator, "saves/{s}/maps", .{self.path}) catch unreachable;
defer main.stackAllocator.free(path);
var dir = std.fs.cwd().openDir(path, .{}) catch break :blk false;
defer dir.close();
break :blk true;
};
if(hasSurfaceMaps) {
try terrain.SurfaceMap.regenerateLOD(self.name);
try terrain.SurfaceMap.regenerateLOD(self.path);
}
// Delete old LODs:
for(1..main.settings.highestSupportedLod + 1) |i| {
const lod = @as(u32, 1) << @intCast(i);
const path = std.fmt.allocPrint(main.stackAllocator.allocator, "saves/{s}/chunks", .{self.name}) catch unreachable;
const path = std.fmt.allocPrint(main.stackAllocator.allocator, "saves/{s}/chunks", .{self.path}) catch unreachable;
defer main.stackAllocator.free(path);
const dir = std.fmt.allocPrint(main.stackAllocator.allocator, "{}", .{lod}) catch unreachable;
defer main.stackAllocator.free(dir);
@ -691,7 +684,7 @@ pub const ServerWorld = struct { // MARK: ServerWorld
// Find all the stored chunks:
var chunkPositions = main.List(ChunkPosition).init(main.stackAllocator);
defer chunkPositions.deinit();
const path = std.fmt.allocPrint(main.stackAllocator.allocator, "saves/{s}/chunks/1", .{self.name}) catch unreachable;
const path = std.fmt.allocPrint(main.stackAllocator.allocator, "saves/{s}/chunks/1", .{self.path}) catch unreachable;
defer main.stackAllocator.free(path);
blk: {
var dirX = std.fs.cwd().openDir(path, .{.iterate = true}) catch |err| {
@ -754,7 +747,7 @@ pub const ServerWorld = struct { // MARK: ServerWorld
pub fn generate(self: *ServerWorld) !void {
try self.wio.loadWorldData(); // load data here in order for entities to also be loaded.
if(!self.generated) {
if(@reduce(.And, self.spawn == Vec3i{0, 0, 0})) {
var seed: u64 = @bitCast(@as(i64, @truncate(std.time.nanoTimestamp())));
std.log.info("Finding position..", .{});
foundPosition: {
@ -812,7 +805,6 @@ pub const ServerWorld = struct { // MARK: ServerWorld
defer map.decreaseRefCount();
self.spawn[2] = map.getHeight(self.spawn[0], self.spawn[1]) + 1;
}
self.generated = true;
const newBiomeCheckSum: i64 = @bitCast(terrain.biomes.getBiomeCheckSum(self.seed));
if(newBiomeCheckSum != self.biomeChecksum) {
self.regenerateLOD(newBiomeCheckSum) catch |err| {
@ -820,7 +812,7 @@ pub const ServerWorld = struct { // MARK: ServerWorld
};
}
try self.wio.saveWorldData();
const itemsPath = std.fmt.allocPrint(main.stackAllocator.allocator, "saves/{s}/items.zig.zon", .{self.name}) catch unreachable;
const itemsPath = std.fmt.allocPrint(main.stackAllocator.allocator, "saves/{s}/items.zig.zon", .{self.path}) catch unreachable;
defer main.stackAllocator.free(itemsPath);
const zon = files.readToZon(main.stackAllocator, itemsPath) catch .null;
defer zon.deinit(main.stackAllocator);
@ -832,7 +824,7 @@ pub const ServerWorld = struct { // MARK: ServerWorld
defer main.stackAllocator.free(dest);
const hashedName = std.base64.url_safe.Encoder.encode(dest, user.name);
const path = std.fmt.allocPrint(main.stackAllocator.allocator, "saves/{s}/players/{s}.zig.zon", .{self.name, hashedName}) catch unreachable;
const path = std.fmt.allocPrint(main.stackAllocator.allocator, "saves/{s}/players/{s}.zig.zon", .{self.path, hashedName}) catch unreachable;
defer main.stackAllocator.free(path);
const playerData = files.readToZon(main.stackAllocator, path) catch .null;
@ -854,7 +846,7 @@ pub const ServerWorld = struct { // MARK: ServerWorld
defer main.stackAllocator.free(dest);
const hashedName = std.base64.url_safe.Encoder.encode(dest, user.name);
const path = std.fmt.allocPrint(main.stackAllocator.allocator, "saves/{s}/players/{s}.zig.zon", .{self.name, hashedName}) catch unreachable;
const path = std.fmt.allocPrint(main.stackAllocator.allocator, "saves/{s}/players/{s}.zig.zon", .{self.path, hashedName}) catch unreachable;
defer main.stackAllocator.free(path);
var playerZon: ZonElement = files.readToZon(main.stackAllocator, path) catch .null;
@ -882,7 +874,7 @@ pub const ServerWorld = struct { // MARK: ServerWorld
}
}
const playerPath = std.fmt.allocPrint(main.stackAllocator.allocator, "saves/{s}/players", .{self.name}) catch unreachable;
const playerPath = std.fmt.allocPrint(main.stackAllocator.allocator, "saves/{s}/players", .{self.path}) catch unreachable;
defer main.stackAllocator.free(playerPath);
try files.makeDir(playerPath);
@ -907,7 +899,7 @@ pub const ServerWorld = struct { // MARK: ServerWorld
const itemDropZon = self.itemDropManager.store(main.stackAllocator);
defer itemDropZon.deinit(main.stackAllocator);
const itemsPath = std.fmt.allocPrint(main.stackAllocator.allocator, "saves/{s}/items.zig.zon", .{self.name}) catch unreachable;
const itemsPath = std.fmt.allocPrint(main.stackAllocator.allocator, "saves/{s}/items.zig.zon", .{self.path}) catch unreachable;
defer main.stackAllocator.free(itemsPath);
try files.writeZon(itemsPath, itemDropZon);
}