mirror of
https://github.com/PixelGuys/Cubyz.git
synced 2025-09-08 03:29:48 -04:00
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:
parent
ab4beca0f4
commit
8bcc00f536
@ -325,7 +325,7 @@ pub const Sync = struct { // MARK: Sync
|
|||||||
defer main.stackAllocator.free(dest);
|
defer main.stackAllocator.free(dest);
|
||||||
const hashedName = std.base64.url_safe.Encoder.encode(dest, user.name);
|
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);
|
defer main.stackAllocator.free(path);
|
||||||
|
|
||||||
const playerData = main.files.readToZon(main.stackAllocator, path) catch .null;
|
const playerData = main.files.readToZon(main.stackAllocator, path) catch .null;
|
||||||
|
@ -46,7 +46,8 @@ fn createWorld(_: usize) void {
|
|||||||
|
|
||||||
fn flawedCreateWorld() !void {
|
fn flawedCreateWorld() !void {
|
||||||
const worldName = textInput.currentString.items;
|
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);
|
defer main.stackAllocator.free(saveFolder);
|
||||||
if(std.fs.cwd().openDir(saveFolder, .{})) |_dir| {
|
if(std.fs.cwd().openDir(saveFolder, .{})) |_dir| {
|
||||||
var dir = _dir;
|
var dir = _dir;
|
||||||
@ -55,7 +56,7 @@ fn flawedCreateWorld() !void {
|
|||||||
} else |_| {}
|
} else |_| {}
|
||||||
try main.files.makeDir(saveFolder);
|
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);
|
defer main.stackAllocator.free(generatorSettingsPath);
|
||||||
const generatorSettings = main.ZonElement.initObject(main.stackAllocator);
|
const generatorSettings = main.ZonElement.initObject(main.stackAllocator);
|
||||||
defer generatorSettings.deinit(main.stackAllocator);
|
defer generatorSettings.deinit(main.stackAllocator);
|
||||||
@ -75,7 +76,19 @@ fn flawedCreateWorld() !void {
|
|||||||
try main.files.writeZon(generatorSettingsPath, generatorSettings);
|
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);
|
defer main.stackAllocator.free(gamerulePath);
|
||||||
const gamerules = main.ZonElement.initObject(main.stackAllocator);
|
const gamerules = main.ZonElement.initObject(main.stackAllocator);
|
||||||
defer gamerules.deinit(main.stackAllocator);
|
defer gamerules.deinit(main.stackAllocator);
|
||||||
@ -86,7 +99,7 @@ fn flawedCreateWorld() !void {
|
|||||||
try main.files.writeZon(gamerulePath, gamerules);
|
try main.files.writeZon(gamerulePath, gamerules);
|
||||||
}
|
}
|
||||||
{ // Make assets subfolder
|
{ // 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);
|
defer main.stackAllocator.free(assetsPath);
|
||||||
try main.files.makeDir(assetsPath);
|
try main.files.makeDir(assetsPath);
|
||||||
}
|
}
|
||||||
|
@ -29,6 +29,13 @@ pub var needsUpdate: bool = false;
|
|||||||
var deleteIcon: Texture = undefined;
|
var deleteIcon: Texture = undefined;
|
||||||
var fileExplorerIcon: 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 {
|
pub fn init() void {
|
||||||
deleteIcon = Texture.initFromFile("assets/cubyz/ui/delete_icon.png");
|
deleteIcon = Texture.initFromFile("assets/cubyz/ui/delete_icon.png");
|
||||||
fileExplorerIcon = Texture.initFromFile("assets/cubyz/ui/file_explorer_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();
|
gui.openHud();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn openWorldWrap(namePtr: usize) void { // TODO: Improve this situation. Maybe it makes sense to always use 2 arguments in the Callback.
|
fn openWorldWrap(index: 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);
|
openWorld(worldList.items[index].fileName);
|
||||||
const name = std.mem.span(nullTerminatedName);
|
|
||||||
openWorld(name);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn deleteWorld(namePtr: usize) void {
|
fn deleteWorld(index: usize) void {
|
||||||
const nullTerminatedName: [*:0]const u8 = @ptrFromInt(namePtr);
|
|
||||||
const name = std.mem.span(nullTerminatedName);
|
|
||||||
main.gui.closeWindow("delete_world_confirmation");
|
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");
|
main.gui.openWindow("delete_world_confirmation");
|
||||||
}
|
}
|
||||||
|
|
||||||
fn openFolder(namePtr: usize) void {
|
fn openFolder(index: usize) void {
|
||||||
const nullTerminatedName: [*:0]const u8 = @ptrFromInt(namePtr);
|
const path = std.fmt.allocPrint(main.stackAllocator.allocator, "saves/{s}", .{worldList.items[index].fileName}) catch unreachable;
|
||||||
const name = std.mem.span(nullTerminatedName);
|
|
||||||
|
|
||||||
const path = std.fmt.allocPrint(main.stackAllocator.allocator, "saves/{s}", .{name}) catch unreachable;
|
|
||||||
defer main.stackAllocator.free(path);
|
defer main.stackAllocator.free(path);
|
||||||
|
|
||||||
main.files.openDirInWindow(path);
|
main.files.openDirInWindow(path);
|
||||||
@ -149,22 +149,41 @@ pub fn onOpen() void {
|
|||||||
break :readingSaves;
|
break :readingSaves;
|
||||||
}) |entry| {
|
}) |entry| {
|
||||||
if(entry.kind == .directory) {
|
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);
|
const decodedName = parseEscapedFolderName(main.stackAllocator, entry.name);
|
||||||
defer main.stackAllocator.free(decodedName);
|
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)}));
|
worldList.append(main.globalAllocator, .{
|
||||||
row.add(Button.initIcon(.{8, 0}, .{16, 16}, fileExplorerIcon, false, .{.callback = &openFolder, .arg = @intFromPtr(name.ptr)}));
|
.fileName = main.globalAllocator.dupe(u8, entry.name),
|
||||||
row.add(Button.initIcon(.{8, 0}, .{16, 16}, deleteIcon, false, .{.callback = &deleteWorld, .arg = @intFromPtr(name.ptr)}));
|
.lastUsedTime = worldInfo.get(i64, "lastUsedTime", 0),
|
||||||
row.finish(.{0, 0}, .center);
|
.name = main.globalAllocator.dupe(u8, worldInfo.get([]const u8, "", decodedName)),
|
||||||
list.add(row);
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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);
|
list.finish(.center);
|
||||||
window.rootComponent = list.toComponent();
|
window.rootComponent = list.toComponent();
|
||||||
window.contentSize = window.rootComponent.?.pos() + window.rootComponent.?.size() + @as(Vec2f, @splat(padding));
|
window.contentSize = window.rootComponent.?.pos() + window.rootComponent.?.size() + @as(Vec2f, @splat(padding));
|
||||||
@ -172,6 +191,11 @@ pub fn onOpen() void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn onClose() 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();
|
buttonNameArena.deinit();
|
||||||
if(window.rootComponent) |*comp| {
|
if(window.rootComponent) |*comp| {
|
||||||
comp.deinit();
|
comp.deinit();
|
||||||
|
@ -667,7 +667,7 @@ pub const Protocols = struct {
|
|||||||
std.log.info("User {s} joined using version {s}.", .{name, version});
|
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);
|
defer main.stackAllocator.free(path);
|
||||||
var dir = try std.fs.cwd().openDir(path, .{.iterate = true});
|
var dir = try std.fs.cwd().openDir(path, .{.iterate = true});
|
||||||
defer dir.close();
|
defer dir.close();
|
||||||
|
@ -17,7 +17,8 @@ const Blueprint = main.blueprint.Blueprint;
|
|||||||
const NeverFailingAllocator = main.heap.NeverFailingAllocator;
|
const NeverFailingAllocator = main.heap.NeverFailingAllocator;
|
||||||
const CircularBufferQueue = main.utils.CircularBufferQueue;
|
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 terrain = @import("terrain/terrain.zig");
|
||||||
pub const Entity = @import("Entity.zig");
|
pub const Entity = @import("Entity.zig");
|
||||||
pub const storage = @import("storage.zig");
|
pub const storage = @import("storage.zig");
|
||||||
|
@ -215,7 +215,7 @@ fn cacheInit(pos: chunk.ChunkPosition) *RegionFile {
|
|||||||
return region;
|
return region;
|
||||||
}
|
}
|
||||||
hashMapMutex.unlock();
|
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);
|
defer main.stackAllocator.free(path);
|
||||||
return RegionFile.init(pos, path);
|
return RegionFile.init(pos, path);
|
||||||
}
|
}
|
||||||
|
@ -125,7 +125,7 @@ pub const MapFragment = struct { // MARK: MapFragment
|
|||||||
};
|
};
|
||||||
|
|
||||||
pub fn load(self: *MapFragment, biomePalette: *main.assets.Palette, originalHeightMap: ?*[mapSize][mapSize]i32) !NeighborInfo {
|
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);
|
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;
|
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.writeInt(u8, @bitCast(header.neighborInfo));
|
||||||
outputWriter.writeSlice(compressedData);
|
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);
|
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;
|
const path = std.fmt.allocPrint(main.stackAllocator.allocator, "{s}/{}/{}/{}.surface", .{saveFolder, self.pos.voxelSize, self.pos.wx, self.pos.wy}) catch unreachable;
|
||||||
|
@ -355,8 +355,9 @@ const ChunkManager = struct { // MARK: ChunkManager
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
pub const worldDataVersion: u32 = 2;
|
||||||
|
|
||||||
const WorldIO = struct { // MARK: WorldIO
|
const WorldIO = struct { // MARK: WorldIO
|
||||||
const worldDataVersion: u32 = 2;
|
|
||||||
|
|
||||||
dir: files.Dir,
|
dir: files.Dir,
|
||||||
world: *ServerWorld,
|
world: *ServerWorld,
|
||||||
@ -372,10 +373,6 @@ const WorldIO = struct { // MARK: WorldIO
|
|||||||
self.dir.close();
|
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.
|
/// Load the seed, which is needed before custom item and ore generation.
|
||||||
pub fn loadWorldSeed(self: WorldIO) !u64 {
|
pub fn loadWorldSeed(self: WorldIO) !u64 {
|
||||||
const worldData = try self.dir.readToZon(main.stackAllocator, "world.zig.zon");
|
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});
|
std.log.err("Cannot read world file version {}. Expected version {}.", .{worldData.get(u32, "version", 0), worldDataVersion});
|
||||||
return error.OldWorld;
|
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 {
|
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.gameTime = worldData.get(i64, "gameTime", 0);
|
||||||
self.world.spawn = worldData.get(Vec3i, "spawn", .{0, 0, 0});
|
self.world.spawn = worldData.get(Vec3i, "spawn", .{0, 0, 0});
|
||||||
self.world.biomeChecksum = worldData.get(i64, "biomeChecksum", 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 {
|
pub fn saveWorldData(self: WorldIO) !void {
|
||||||
@ -406,6 +404,8 @@ const WorldIO = struct { // MARK: WorldIO
|
|||||||
worldData.put("gameTime", self.world.gameTime);
|
worldData.put("gameTime", self.world.gameTime);
|
||||||
worldData.put("spawn", self.world.spawn);
|
worldData.put("spawn", self.world.spawn);
|
||||||
worldData.put("biomeChecksum", self.world.biomeChecksum);
|
worldData.put("biomeChecksum", self.world.biomeChecksum);
|
||||||
|
worldData.put("name", self.world.name);
|
||||||
|
worldData.put("lastUsedTime", std.time.milliTimestamp());
|
||||||
// TODO: Save entities
|
// TODO: Save entities
|
||||||
try self.dir.writeZon("world.zig.zon", worldData);
|
try self.dir.writeZon("world.zig.zon", worldData);
|
||||||
}
|
}
|
||||||
@ -420,8 +420,6 @@ pub const ServerWorld = struct { // MARK: ServerWorld
|
|||||||
biomePalette: *main.assets.Palette = undefined,
|
biomePalette: *main.assets.Palette = undefined,
|
||||||
chunkManager: ChunkManager = undefined,
|
chunkManager: ChunkManager = undefined,
|
||||||
|
|
||||||
generated: bool = false,
|
|
||||||
|
|
||||||
gameTime: i64 = 0,
|
gameTime: i64 = 0,
|
||||||
milliTime: i64,
|
milliTime: i64,
|
||||||
lastUpdateTime: i64,
|
lastUpdateTime: i64,
|
||||||
@ -432,7 +430,8 @@ pub const ServerWorld = struct { // MARK: ServerWorld
|
|||||||
allowCheats: bool = undefined,
|
allowCheats: bool = undefined,
|
||||||
|
|
||||||
seed: u64,
|
seed: u64,
|
||||||
name: []const u8,
|
path: []const u8,
|
||||||
|
name: []const u8 = &.{},
|
||||||
spawn: Vec3i = undefined,
|
spawn: Vec3i = undefined,
|
||||||
|
|
||||||
wio: WorldIO = undefined,
|
wio: WorldIO = undefined,
|
||||||
@ -454,14 +453,14 @@ pub const ServerWorld = struct { // MARK: ServerWorld
|
|||||||
milliTimeStamp: i64,
|
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
|
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);
|
defer main.stackAllocator.free(worldDatPath);
|
||||||
if(std.fs.cwd().openFile(worldDatPath, .{})) |file| {
|
if(std.fs.cwd().openFile(worldDatPath, .{})) |file| {
|
||||||
file.close();
|
file.close();
|
||||||
std.log.warn("Detected old world in saves/{s}. Converting all .json files to .zig.zon", .{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}", .{name});
|
const dirPath = try std.fmt.allocPrint(main.stackAllocator.allocator, "saves/{s}", .{path});
|
||||||
defer main.stackAllocator.free(dirPath);
|
defer main.stackAllocator.free(dirPath);
|
||||||
var dir = std.fs.cwd().openDir(dirPath, .{.iterate = true}) catch |err| {
|
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)});
|
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(),
|
.milliTime = std.time.milliTimestamp(),
|
||||||
.lastUnimportantDataSent = std.time.milliTimestamp(),
|
.lastUnimportantDataSent = std.time.milliTimestamp(),
|
||||||
.seed = @bitCast(@as(i64, @truncate(std.time.nanoTimestamp()))),
|
.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),
|
.chunkUpdateQueue = .init(main.globalAllocator, 256),
|
||||||
.regionUpdateQueue = .init(main.globalAllocator, 256),
|
.regionUpdateQueue = .init(main.globalAllocator, 256),
|
||||||
};
|
};
|
||||||
@ -505,45 +504,38 @@ pub const ServerWorld = struct { // MARK: ServerWorld
|
|||||||
if(nullGeneratorSettings) |_generatorSettings| {
|
if(nullGeneratorSettings) |_generatorSettings| {
|
||||||
generatorSettings = _generatorSettings;
|
generatorSettings = _generatorSettings;
|
||||||
// Store generator settings:
|
// 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:
|
} 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();
|
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");
|
self.blockPalette = try main.assets.Palette.init(main.globalAllocator, blockPaletteZon, "cubyz:air");
|
||||||
errdefer self.blockPalette.deinit();
|
errdefer self.blockPalette.deinit();
|
||||||
std.log.info("Loaded save block palette with {} blocks.", .{self.blockPalette.size()});
|
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);
|
self.itemPalette = try main.assets.Palette.init(main.globalAllocator, itemPaletteZon, null);
|
||||||
errdefer self.itemPalette.deinit();
|
errdefer self.itemPalette.deinit();
|
||||||
std.log.info("Loaded save item palette with {} items.", .{self.itemPalette.size()});
|
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);
|
self.biomePalette = try main.assets.Palette.init(main.globalAllocator, biomePaletteZon, null);
|
||||||
errdefer self.biomePalette.deinit();
|
errdefer self.biomePalette.deinit();
|
||||||
std.log.info("Loaded save biome palette with {} biomes.", .{self.biomePalette.size()});
|
std.log.info("Loaded save biome palette with {} biomes.", .{self.biomePalette.size()});
|
||||||
|
|
||||||
errdefer main.assets.unloadAssets();
|
errdefer main.assets.unloadAssets();
|
||||||
|
|
||||||
if(self.wio.hasWorldData()) {
|
self.seed = try self.wio.loadWorldSeed();
|
||||||
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);
|
||||||
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();
|
|
||||||
}
|
|
||||||
// Store the block palette now that everything is loaded.
|
// 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}/palette.zig.zon", .{path}), 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}/biome_palette.zig.zon", .{path}), 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}/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.defaultGamemode = std.meta.stringToEnum(main.game.Gamemode, gamerules.get([]const u8, "default_gamemode", "creative")) orelse .creative;
|
||||||
self.allowCheats = gamerules.get(bool, "cheats", true);
|
self.allowCheats = gamerules.get(bool, "cheats", true);
|
||||||
@ -573,7 +565,8 @@ pub const ServerWorld = struct { // MARK: ServerWorld
|
|||||||
self.itemPalette.deinit();
|
self.itemPalette.deinit();
|
||||||
self.biomePalette.deinit();
|
self.biomePalette.deinit();
|
||||||
self.wio.deinit();
|
self.wio.deinit();
|
||||||
main.globalAllocator.free(self.name);
|
main.globalAllocator.free(self.path);
|
||||||
|
//main.globalAllocator.free(self.name);
|
||||||
main.globalAllocator.destroy(self);
|
main.globalAllocator.destroy(self);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -666,19 +659,19 @@ pub const ServerWorld = struct { // MARK: ServerWorld
|
|||||||
fn regenerateLOD(self: *ServerWorld, newBiomeCheckSum: i64) !void {
|
fn regenerateLOD(self: *ServerWorld, newBiomeCheckSum: i64) !void {
|
||||||
std.log.info("Biomes have changed. Regenerating LODs... (this might take some time)", .{});
|
std.log.info("Biomes have changed. Regenerating LODs... (this might take some time)", .{});
|
||||||
const hasSurfaceMaps = blk: {
|
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);
|
defer main.stackAllocator.free(path);
|
||||||
var dir = std.fs.cwd().openDir(path, .{}) catch break :blk false;
|
var dir = std.fs.cwd().openDir(path, .{}) catch break :blk false;
|
||||||
defer dir.close();
|
defer dir.close();
|
||||||
break :blk true;
|
break :blk true;
|
||||||
};
|
};
|
||||||
if(hasSurfaceMaps) {
|
if(hasSurfaceMaps) {
|
||||||
try terrain.SurfaceMap.regenerateLOD(self.name);
|
try terrain.SurfaceMap.regenerateLOD(self.path);
|
||||||
}
|
}
|
||||||
// Delete old LODs:
|
// Delete old LODs:
|
||||||
for(1..main.settings.highestSupportedLod + 1) |i| {
|
for(1..main.settings.highestSupportedLod + 1) |i| {
|
||||||
const lod = @as(u32, 1) << @intCast(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);
|
defer main.stackAllocator.free(path);
|
||||||
const dir = std.fmt.allocPrint(main.stackAllocator.allocator, "{}", .{lod}) catch unreachable;
|
const dir = std.fmt.allocPrint(main.stackAllocator.allocator, "{}", .{lod}) catch unreachable;
|
||||||
defer main.stackAllocator.free(dir);
|
defer main.stackAllocator.free(dir);
|
||||||
@ -691,7 +684,7 @@ pub const ServerWorld = struct { // MARK: ServerWorld
|
|||||||
// Find all the stored chunks:
|
// Find all the stored chunks:
|
||||||
var chunkPositions = main.List(ChunkPosition).init(main.stackAllocator);
|
var chunkPositions = main.List(ChunkPosition).init(main.stackAllocator);
|
||||||
defer chunkPositions.deinit();
|
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);
|
defer main.stackAllocator.free(path);
|
||||||
blk: {
|
blk: {
|
||||||
var dirX = std.fs.cwd().openDir(path, .{.iterate = true}) catch |err| {
|
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 {
|
pub fn generate(self: *ServerWorld) !void {
|
||||||
try self.wio.loadWorldData(); // load data here in order for entities to also be loaded.
|
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())));
|
var seed: u64 = @bitCast(@as(i64, @truncate(std.time.nanoTimestamp())));
|
||||||
std.log.info("Finding position..", .{});
|
std.log.info("Finding position..", .{});
|
||||||
foundPosition: {
|
foundPosition: {
|
||||||
@ -812,7 +805,6 @@ pub const ServerWorld = struct { // MARK: ServerWorld
|
|||||||
defer map.decreaseRefCount();
|
defer map.decreaseRefCount();
|
||||||
self.spawn[2] = map.getHeight(self.spawn[0], self.spawn[1]) + 1;
|
self.spawn[2] = map.getHeight(self.spawn[0], self.spawn[1]) + 1;
|
||||||
}
|
}
|
||||||
self.generated = true;
|
|
||||||
const newBiomeCheckSum: i64 = @bitCast(terrain.biomes.getBiomeCheckSum(self.seed));
|
const newBiomeCheckSum: i64 = @bitCast(terrain.biomes.getBiomeCheckSum(self.seed));
|
||||||
if(newBiomeCheckSum != self.biomeChecksum) {
|
if(newBiomeCheckSum != self.biomeChecksum) {
|
||||||
self.regenerateLOD(newBiomeCheckSum) catch |err| {
|
self.regenerateLOD(newBiomeCheckSum) catch |err| {
|
||||||
@ -820,7 +812,7 @@ pub const ServerWorld = struct { // MARK: ServerWorld
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
try self.wio.saveWorldData();
|
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);
|
defer main.stackAllocator.free(itemsPath);
|
||||||
const zon = files.readToZon(main.stackAllocator, itemsPath) catch .null;
|
const zon = files.readToZon(main.stackAllocator, itemsPath) catch .null;
|
||||||
defer zon.deinit(main.stackAllocator);
|
defer zon.deinit(main.stackAllocator);
|
||||||
@ -832,7 +824,7 @@ pub const ServerWorld = struct { // MARK: ServerWorld
|
|||||||
defer main.stackAllocator.free(dest);
|
defer main.stackAllocator.free(dest);
|
||||||
const hashedName = std.base64.url_safe.Encoder.encode(dest, user.name);
|
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);
|
defer main.stackAllocator.free(path);
|
||||||
|
|
||||||
const playerData = files.readToZon(main.stackAllocator, path) catch .null;
|
const playerData = files.readToZon(main.stackAllocator, path) catch .null;
|
||||||
@ -854,7 +846,7 @@ pub const ServerWorld = struct { // MARK: ServerWorld
|
|||||||
defer main.stackAllocator.free(dest);
|
defer main.stackAllocator.free(dest);
|
||||||
const hashedName = std.base64.url_safe.Encoder.encode(dest, user.name);
|
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);
|
defer main.stackAllocator.free(path);
|
||||||
|
|
||||||
var playerZon: ZonElement = files.readToZon(main.stackAllocator, path) catch .null;
|
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);
|
defer main.stackAllocator.free(playerPath);
|
||||||
|
|
||||||
try files.makeDir(playerPath);
|
try files.makeDir(playerPath);
|
||||||
@ -907,7 +899,7 @@ pub const ServerWorld = struct { // MARK: ServerWorld
|
|||||||
|
|
||||||
const itemDropZon = self.itemDropManager.store(main.stackAllocator);
|
const itemDropZon = self.itemDropManager.store(main.stackAllocator);
|
||||||
defer itemDropZon.deinit(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);
|
defer main.stackAllocator.free(itemsPath);
|
||||||
try files.writeZon(itemsPath, itemDropZon);
|
try files.writeZon(itemsPath, itemDropZon);
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user