From ff3f7255cf73f42eda632502de48a431a8843062 Mon Sep 17 00:00:00 2001 From: Abhay Kagdyal Date: Fri, 31 Jul 2026 22:50:10 +0530 Subject: [PATCH 01/14] Add disable option to TextInput --- src/gui/components/TextInput.zig | 47 +++++++++++++++++++------------- 1 file changed, 28 insertions(+), 19 deletions(-) diff --git a/src/gui/components/TextInput.zig b/src/gui/components/TextInput.zig index 10a2eaab00..ba596f3f0e 100644 --- a/src/gui/components/TextInput.zig +++ b/src/gui/components/TextInput.zig @@ -34,9 +34,9 @@ maxWidth: f32, maxHeight: f32, textSize: Vec2f = undefined, scrollBar: *ScrollBar, -callbacks: Callbacks, +options: Options, lastBlinkTime: std.Io.Timestamp = .fromNanoseconds(0), -showCusor: bool = true, +showCursor: bool = true, pub fn globalInit() void { texture = Texture.initFromFile("assets/cubyz/ui/text_input.png"); @@ -46,14 +46,15 @@ pub fn globalDeinit() void { texture.deinit(); } -const Callbacks = struct { +const Options = struct { + disabled: bool = false, onNewline: main.callbacks.SimpleCallback, onUp: main.callbacks.SimpleCallback = .{}, onDown: main.callbacks.SimpleCallback = .{}, onUpdate: main.callbacks.SimpleCallback = .{}, }; -pub fn init(pos: Vec2f, maxWidth: f32, maxHeight: f32, text: []const u8, callbacks: Callbacks) *TextInput { +pub fn init(pos: Vec2f, maxWidth: f32, maxHeight: f32, text: []const u8, options: Options) *TextInput { const scrollBar = ScrollBar.init(undefined, scrollBarWidth, maxHeight - 2*border, 0); const self = main.globalAllocator.create(TextInput); self.* = TextInput{ @@ -64,7 +65,8 @@ pub fn init(pos: Vec2f, maxWidth: f32, maxHeight: f32, text: []const u8, callbac .maxWidth = maxWidth, .maxHeight = maxHeight, .scrollBar = scrollBar, - .callbacks = callbacks, + .options = options, + .showCursor = !options.disabled, }; self.currentString.appendSlice(text); self.textSize = self.textBuffer.calculateLineBreaks(fontSize, maxWidth - 2*border - scrollBarWidth); @@ -83,7 +85,7 @@ pub fn deinit(self: *const TextInput) void { } pub fn clear(self: *TextInput) void { - if (self.cursor != null) { + if (self.cursor != null and !self.options.disabled) { self.cursor = 0; self.selectionStart = null; } @@ -164,7 +166,7 @@ pub fn deselect(self: *TextInput) void { } fn reloadText(self: *TextInput) void { - self.callbacks.onUpdate.run(); + self.options.onUpdate.run(); self.textBuffer.deinit(); self.textBuffer = TextBuffer.init(main.globalAllocator, self.currentString.items, .{}, true, .left); self.textSize = self.textBuffer.calculateLineBreaks(fontSize, self.maxWidth - 2*border - scrollBarWidth); @@ -293,7 +295,7 @@ pub fn down(self: *TextInput, mods: main.Window.Key.Modifiers) void { self.selectionStart = null; } else { if (self.moveCursorVertically(1) == .same) { - self.callbacks.onDown.run(); + self.options.onDown.run(); } } } @@ -317,7 +319,7 @@ pub fn up(self: *TextInput, mods: main.Window.Key.Modifiers) void { self.selectionStart = null; } else { if (self.moveCursorVertically(-1) == .same) { - self.callbacks.onUp.run(); + self.options.onUp.run(); } } } @@ -386,6 +388,7 @@ pub fn gotoEnd(self: *TextInput, mods: main.Window.Key.Modifiers) void { } fn deleteSelection(self: *TextInput) void { + if (self.options.disabled) return; if (self.selectionStart) |selectionStart| { const start = @min(selectionStart, self.cursor.?); const end = @max(selectionStart, self.cursor.?); @@ -398,7 +401,7 @@ fn deleteSelection(self: *TextInput) void { } pub fn deleteLeft(self: *TextInput, mods: main.Window.Key.Modifiers) void { - if (self.cursor == null) return; + if (self.cursor == null or self.options.disabled) return; if (self.selectionStart == null) { self.selectionStart = self.cursor; self.moveCursorLeft(mods); @@ -409,7 +412,7 @@ pub fn deleteLeft(self: *TextInput, mods: main.Window.Key.Modifiers) void { } pub fn deleteRight(self: *TextInput, mods: main.Window.Key.Modifiers) void { - if (self.cursor == null) return; + if (self.cursor == null or self.options.disabled) return; if (self.selectionStart == null) { self.selectionStart = self.cursor; self.moveCursorRight(mods); @@ -420,6 +423,7 @@ pub fn deleteRight(self: *TextInput, mods: main.Window.Key.Modifiers) void { } pub fn inputCharacter(self: *TextInput, character: u21) void { + if (self.options.disabled) return; if (self.cursor) |*cursor| { self.deleteSelection(); var buf: [4]u8 = undefined; @@ -432,6 +436,7 @@ pub fn inputCharacter(self: *TextInput, character: u21) void { } pub fn setString(self: *TextInput, utf8EncodedString: []const u8) void { + if (self.options.disabled) return; self.clear(); self.currentString.insertSlice(0, utf8EncodedString); self.reloadText(); @@ -472,7 +477,7 @@ pub fn paste(self: *TextInput, mods: main.Window.Key.Modifiers) void { } pub fn cut(self: *TextInput, mods: main.Window.Key.Modifiers) void { - if (mods.control) { + if (mods.control and !self.options.disabled) { self.copy(mods); self.deleteSelection(); self.reloadText(); @@ -481,8 +486,8 @@ pub fn cut(self: *TextInput, mods: main.Window.Key.Modifiers) void { } pub fn newline(self: *TextInput, mods: main.Window.Key.Modifiers) void { - if (!mods.shift and self.callbacks.onNewline.inner != null) { - self.callbacks.onNewline.run(); + if (!mods.shift and self.options.onNewline.inner != null and !self.options.disabled) { + self.options.onNewline.run(); return; } self.inputCharacter('\n'); @@ -490,7 +495,7 @@ pub fn newline(self: *TextInput, mods: main.Window.Key.Modifiers) void { } fn ensureCursorVisibility(self: *TextInput) void { - self.showCusor = true; + self.showCursor = !self.options.disabled; self.lastBlinkTime = main.timestamp(); if (self.textSize[1] > self.maxHeight - 2*border) { var y: f32 = 0; @@ -540,6 +545,8 @@ pub fn render(self: *TextInput, mousePosition: Vec2f) void { self.scrollBar.pos = .{self.size[0] - self.scrollBar.size[0] - border, border}; self.scrollBar.render(mousePosition - self.pos); } + const textColor = draw.setColor(if (self.options.disabled) 0xff808080 else 0xffffffff); + defer draw.restoreColor(textColor); textBuffer.render(textPos[0], textPos[1], fontSize); if (self.pressed) { self.cursor = self.textBuffer.mousePosToIndex(mousePosition - textPos - self.pos, self.currentString.items.len); @@ -555,12 +562,14 @@ pub fn render(self: *TextInput, mousePosition: Vec2f) void { } const currentTime = main.timestamp(); - if (self.lastBlinkTime.durationTo(currentTime).nanoseconds > blinkDuration.nanoseconds) { - self.lastBlinkTime = currentTime; - self.showCusor = !self.showCusor; + if (!self.options.disabled) { + if (self.lastBlinkTime.durationTo(currentTime).nanoseconds > blinkDuration.nanoseconds) { + self.lastBlinkTime = currentTime; + self.showCursor = !self.showCursor; + } } - if (self.showCusor) { + if (self.showCursor) { const oldColor = draw.setColor(0xff000000); defer draw.restoreColor(oldColor); const thickness = @min(@ceil(fontSize/8), 1); From 73745c010d9f36de48778f29f4be8d9bca4cdbf9 Mon Sep 17 00:00:00 2001 From: Abhay Kagdyal Date: Sun, 2 Aug 2026 20:37:22 +0530 Subject: [PATCH 02/14] add edit-world window --- assets/cubyz/ui/edit_icon.png | Bin 0 -> 132 bytes src/gui/windows/_list.zig | 1 + src/gui/windows/edit_world.zig | 59 +++++++ src/gui/windows/save_selection.zig | 242 +++++++++++++++-------------- 4 files changed, 186 insertions(+), 116 deletions(-) create mode 100644 assets/cubyz/ui/edit_icon.png create mode 100644 src/gui/windows/edit_world.zig diff --git a/assets/cubyz/ui/edit_icon.png b/assets/cubyz/ui/edit_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..c8b10e3ae94e12f8f16b7fcc24dcc374afefeb37 GIT binary patch literal 132 zcmeAS@N?(olHy`uVBq!ia0vp^93afW1|*O0@9PFq-kvUwAre!U_8;UtV8FwCUiEx~ z`-ixjamp1a&lr*$ShV+%JI0By|&--xyvQF*cUsj8P2cV_1WGaYrk3d f8I}VN>OQl2xQj4^&tm=uG?u~B)z4*}Q$iB}4ec!m literal 0 HcmV?d00001 diff --git a/src/gui/windows/_list.zig b/src/gui/windows/_list.zig index a9828d444b..57da417492 100644 --- a/src/gui/windows/_list.zig +++ b/src/gui/windows/_list.zig @@ -13,6 +13,7 @@ pub const debug_vulkan_info = @import("debug_vulkan_info.zig"); pub const debug = @import("debug.zig"); pub const delete_world_confirmation = @import("delete_world_confirmation.zig"); pub const download_controller_mappings = @import("download_controller_mappings.zig"); +pub const edit_world = @import("edit_world.zig"); pub const energybar = @import("energybar.zig"); pub const error_prompt = @import("error_prompt.zig"); pub const gpu_performance_measuring = @import("gpu_performance_measuring.zig"); diff --git a/src/gui/windows/edit_world.zig b/src/gui/windows/edit_world.zig new file mode 100644 index 0000000000..c837f60243 --- /dev/null +++ b/src/gui/windows/edit_world.zig @@ -0,0 +1,59 @@ +const std = @import("std"); + +const main = @import("main"); +const Vec2f = main.vec.Vec2f; +const NeverFailingAllocator = main.heap.NeverFailingAllocator; +const ZonElement = main.ZonElement; + +const gui = @import("../gui.zig"); +const GuiWindow = gui.GuiWindow; +const Button = @import("../components/Button.zig"); +const Label = @import("../components/Label.zig"); +const TextInput = @import("../components/TextInput.zig"); +const HorizontalList = @import("../components/HorizontalList.zig"); +const VerticalList = @import("../components/VerticalList.zig"); + +fn pass() void {} + +pub var window = GuiWindow{ + .contentSize = Vec2f{ 128, 256 }, +}; + +const padding: f32 = 8; + +var editWorldName: []const u8 = ""; + +var worldInfo: *ZonElement = undefined; + +var nameInput: *TextInput = undefined; + +pub fn init() void { + editWorldName = ""; +} + +pub fn deinit() void { + main.globalAllocator.free(editWorldName); +} + +pub fn setEditWorldName(name: []const u8) void { + main.globalAllocator.free(editWorldName); + editWorldName = main.globalAllocator.dupe(u8, name); +} + +pub fn onOpen() void { + //const list = VerticalList.init(.{ padding, 16 + padding }, 300, 8); + + // nameInput = TextInput.init(.{ 0, 0 }, 128, 22, editWorldName, .{ .onNewline = .init(pass) }); + // list.add(nameInput); + + //list.finish(.center); + //window.rootComponent = list.toComponent(); + window.contentSize = window.rootComponent.?.pos() + window.rootComponent.?.size() + @as(Vec2f, @splat(padding)); + gui.updateWindowPositions(); +} + +pub fn onClose() void { + if (window.rootComponent) |*comp| { + comp.deinit(); + } +} diff --git a/src/gui/windows/save_selection.zig b/src/gui/windows/save_selection.zig index 5ba1293cce..f7349616e2 100644 --- a/src/gui/windows/save_selection.zig +++ b/src/gui/windows/save_selection.zig @@ -17,7 +17,7 @@ const TextInput = @import("../components/TextInput.zig"); const VerticalList = @import("../components/VerticalList.zig"); pub var window = GuiWindow{ - .contentSize = Vec2f{128, 256}, + .contentSize = Vec2f{ 128, 256 }, }; const padding: f32 = 8; @@ -30,150 +30,160 @@ pub var mode: main.server.ServerWorld.Mode = undefined; var deleteIcon: Texture = undefined; var fileExplorerIcon: Texture = undefined; +var editIcon: Texture = undefined; const WorldInfo = struct { - lastUsedTime: i64, - name: []const u8, - fileName: []const u8, + lastUsedTime: i64, + name: []const u8, + fileName: []const u8, }; var worldList: main.List(WorldInfo) = .empty; pub fn init() void { - deleteIcon = Texture.initFromFile("assets/cubyz/ui/delete_icon.png"); - fileExplorerIcon = Texture.initFromFile("assets/cubyz/ui/file_explorer_icon.png"); + deleteIcon = Texture.initFromFile("assets/cubyz/ui/delete_icon.png"); + fileExplorerIcon = Texture.initFromFile("assets/cubyz/ui/file_explorer_icon.png"); + editIcon = Texture.initFromFile("assets/cubyz/ui/edit_icon.png"); } pub fn deinit() void { - deleteIcon.deinit(); - fileExplorerIcon.deinit(); + deleteIcon.deinit(); + fileExplorerIcon.deinit(); + editIcon.deinit(); } pub fn openWorld(name: []const u8) void { - const clientConnection = ConnectionManager.init(0, .{}) catch |err| { - std.log.err("Encountered error while opening connection: {s}", .{@errorName(err)}); - return; - }; - - std.log.info("Opening world {s}", .{name}); - main.server.thread = std.Thread.spawn(.{}, main.server.startFromNewThread, .{name, clientConnection.localPort, mode}) catch |err| { - std.log.err("Encountered error while starting server thread: {s}", .{@errorName(err)}); - return; - }; - main.server.thread.?.setName(main.io, "Server") catch |err| { - std.log.err("Failed to rename Server thread: {s}", .{@errorName(err)}); - }; - - while (!main.server.running.load(.acquire)) { - main.io.sleep(.fromMilliseconds(1), .awake) catch {}; - main.heap.GarbageCollection.syncPoint(); - } - const ipPort = main.stackAllocator.print("127.0.0.1:{}", .{main.server.connectionManager.localPort}); - defer main.stackAllocator.free(ipPort); - const zon = main.game.testWorld.init(ipPort, clientConnection) catch |err| { - std.log.err("Encountered error while opening world: {s}", .{@errorName(err)}); - return; - }; - main.game.testWorld.finishHandshake(zon) catch |err| { - std.log.err("Encountered error while opening world: {s}", .{@errorName(err)}); - return; - }; - for (gui.openWindows.items) |openWindow| { - gui.closeWindowFromRef(openWindow); - } - gui.openHud(); + const clientConnection = ConnectionManager.init(0, .{}) catch |err| { + std.log.err("Encountered error while opening connection: {s}", .{@errorName(err)}); + return; + }; + + std.log.info("Opening world {s}", .{name}); + main.server.thread = std.Thread.spawn(.{}, main.server.startFromNewThread, .{ name, clientConnection.localPort, mode }) catch |err| { + std.log.err("Encountered error while starting server thread: {s}", .{@errorName(err)}); + return; + }; + main.server.thread.?.setName(main.io, "Server") catch |err| { + std.log.err("Failed to rename Server thread: {s}", .{@errorName(err)}); + }; + + while (!main.server.running.load(.acquire)) { + main.io.sleep(.fromMilliseconds(1), .awake) catch {}; + main.heap.GarbageCollection.syncPoint(); + } + const ipPort = main.stackAllocator.print("127.0.0.1:{}", .{main.server.connectionManager.localPort}); + defer main.stackAllocator.free(ipPort); + const zon = main.game.testWorld.init(ipPort, clientConnection) catch |err| { + std.log.err("Encountered error while opening world: {s}", .{@errorName(err)}); + return; + }; + main.game.testWorld.finishHandshake(zon) catch |err| { + std.log.err("Encountered error while opening world: {s}", .{@errorName(err)}); + return; + }; + for (gui.openWindows.items) |openWindow| { + gui.closeWindowFromRef(openWindow); + } + gui.openHud(); } 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); + openWorld(worldList.items[index].fileName); } fn deleteWorld(index: usize) void { - main.gui.closeWindow("delete_world_confirmation"); - main.gui.windowlist.delete_world_confirmation.setDeleteWorldName(worldList.items[index].fileName); - main.gui.openWindow("delete_world_confirmation"); + main.gui.closeWindow("delete_world_confirmation"); + main.gui.windowlist.delete_world_confirmation.setDeleteWorldName(worldList.items[index].fileName); + main.gui.openWindow("delete_world_confirmation"); } fn openFolder(index: usize) void { - const path = main.stackAllocator.print("{s}/saves/{s}", .{main.files.cubyzDirStr(), worldList.items[index].fileName}); - defer main.stackAllocator.free(path); + const path = main.stackAllocator.print("{s}/saves/{s}", .{ main.files.cubyzDirStr(), worldList.items[index].fileName }); + defer main.stackAllocator.free(path); - main.files.openDirInWindow(path); + main.files.openDirInWindow(path); +} + +fn editWorld(index: usize) void { + main.gui.closeWindow("edit_world"); + main.gui.windowlist.edit_world.setEditWorldName(worldList.items[index].fileName); + main.gui.openWindow("edit_world"); } pub fn update() void { - if (needsUpdate) { - needsUpdate = false; - onClose(); - onOpen(); - } + if (needsUpdate) { + needsUpdate = false; + onClose(); + onOpen(); + } } pub fn onOpen() void { - buttonNameArena = main.heap.NeverFailingArenaAllocator.init(main.globalAllocator); - const list = VerticalList.init(.{padding, 16 + padding}, 300, 8); - list.add(Label.init(.{0, 0}, width, if (mode == .singleplayer) "**Select World**" else "**Select World to Host**", .center)); - list.add(Button.initText(.{0, 0}, 128, "Create New World", .{.onAction = gui.openWindowCallback("save_creation")})); - readingSaves: { - var dir = main.files.cubyzDir().openIterableDir("saves") catch |err| { - list.add(Label.init(.{0, 0}, 128, "Encountered error while trying to open saves folder:", .center)); - list.add(Label.init(.{0, 0}, 128, @errorName(err), .center)); - break :readingSaves; - }; - defer dir.close(); - - var iterator = dir.iterate(); - while (iterator.next(main.io) catch |err| { - list.add(Label.init(.{0, 0}, 128, "Encountered error while iterating over saves folder:", .center)); - list.add(Label.init(.{0, 0}, 128, @errorName(err), .center)); - break :readingSaves; - }) |entry| { - if (entry.kind == .directory) { - const worldInfoPath = main.stackAllocator.print("saves/{s}/world.zig.zon", .{entry.name}); - defer main.stackAllocator.free(worldInfoPath); - const worldInfo = main.files.cubyzDir().readToZon(main.stackAllocator, worldInfoPath) catch |err| { - std.log.err("Couldn't open save {s}: {s}", .{worldInfoPath, @errorName(err)}); - continue; - }; - defer worldInfo.deinit(main.stackAllocator); - - worldList.append(main.globalAllocator, .{ - .fileName = main.globalAllocator.dupe(u8, entry.name), - .lastUsedTime = worldInfo.get(i64, "lastUsedTime") orelse 0, - .name = main.globalAllocator.dupe(u8, worldInfo.get([]const u8, "name") orelse entry.name), - }); - } - } - } - - 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, .{.onAction = .initWithInt(openWorldWrap, i)})); - row.add(Button.initIcon(.{8, 0}, .{16, 16}, fileExplorerIcon, .{.onAction = .initWithInt(openFolder, i)})); - row.add(Button.initIcon(.{8, 0}, .{16, 16}, deleteIcon, .{.onAction = .initWithInt(deleteWorld, 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)); - gui.updateWindowPositions(); + buttonNameArena = main.heap.NeverFailingArenaAllocator.init(main.globalAllocator); + const list = VerticalList.init(.{ padding, 16 + padding }, 300, 8); + list.add(Label.init(.{ 0, 0 }, width, if (mode == .singleplayer) "**Select World**" else "**Select World to Host**", .center)); + list.add(Button.initText(.{ 0, 0 }, 128, "Create New World", .{ .onAction = gui.openWindowCallback("save_creation") })); + readingSaves: { + var dir = main.files.cubyzDir().openIterableDir("saves") catch |err| { + list.add(Label.init(.{ 0, 0 }, 128, "Encountered error while trying to open saves folder:", .center)); + list.add(Label.init(.{ 0, 0 }, 128, @errorName(err), .center)); + break :readingSaves; + }; + defer dir.close(); + + var iterator = dir.iterate(); + while (iterator.next(main.io) catch |err| { + list.add(Label.init(.{ 0, 0 }, 128, "Encountered error while iterating over saves folder:", .center)); + list.add(Label.init(.{ 0, 0 }, 128, @errorName(err), .center)); + break :readingSaves; + }) |entry| { + if (entry.kind == .directory) { + const worldInfoPath = main.stackAllocator.print("saves/{s}/world.zig.zon", .{entry.name}); + defer main.stackAllocator.free(worldInfoPath); + const worldInfo = main.files.cubyzDir().readToZon(main.stackAllocator, worldInfoPath) catch |err| { + std.log.err("Couldn't open save {s}: {s}", .{ worldInfoPath, @errorName(err) }); + continue; + }; + defer worldInfo.deinit(main.stackAllocator); + + worldList.append(main.globalAllocator, .{ + .fileName = main.globalAllocator.dupe(u8, entry.name), + .lastUsedTime = worldInfo.get(i64, "lastUsedTime") orelse 0, + .name = main.globalAllocator.dupe(u8, worldInfo.get([]const u8, "name") orelse entry.name), + }); + } + } + } + + 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, .{ .onAction = .initWithInt(openWorldWrap, i) })); + row.add(Button.initIcon(.{ 4, 0 }, .{ 16, 16 }, editIcon, .{ .onAction = .initWithInt(editWorld, i) })); + row.add(Button.initIcon(.{ 4, 0 }, .{ 16, 16 }, fileExplorerIcon, .{ .onAction = .initWithInt(openFolder, i) })); + row.add(Button.initIcon(.{ 4, 0 }, .{ 16, 16 }, deleteIcon, .{ .onAction = .initWithInt(deleteWorld, 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)); + gui.updateWindowPositions(); } 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(); - } + 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(); + } } From 024ca7e7ab4be73145511493e3c9b6c7db864cd1 Mon Sep 17 00:00:00 2001 From: Abhay Kagdyal Date: Sun, 2 Aug 2026 20:38:18 +0530 Subject: [PATCH 03/14] minor changes --- src/gui/windows/edit_world.zig | 30 ++-- src/gui/windows/save_selection.zig | 244 ++++++++++++++--------------- 2 files changed, 137 insertions(+), 137 deletions(-) diff --git a/src/gui/windows/edit_world.zig b/src/gui/windows/edit_world.zig index c837f60243..7bd68ed103 100644 --- a/src/gui/windows/edit_world.zig +++ b/src/gui/windows/edit_world.zig @@ -16,7 +16,7 @@ const VerticalList = @import("../components/VerticalList.zig"); fn pass() void {} pub var window = GuiWindow{ - .contentSize = Vec2f{ 128, 256 }, + .contentSize = Vec2f{128, 256}, }; const padding: f32 = 8; @@ -28,32 +28,32 @@ var worldInfo: *ZonElement = undefined; var nameInput: *TextInput = undefined; pub fn init() void { - editWorldName = ""; + editWorldName = ""; } pub fn deinit() void { - main.globalAllocator.free(editWorldName); + main.globalAllocator.free(editWorldName); } pub fn setEditWorldName(name: []const u8) void { - main.globalAllocator.free(editWorldName); - editWorldName = main.globalAllocator.dupe(u8, name); + main.globalAllocator.free(editWorldName); + editWorldName = main.globalAllocator.dupe(u8, name); } pub fn onOpen() void { - //const list = VerticalList.init(.{ padding, 16 + padding }, 300, 8); + //const list = VerticalList.init(.{ padding, 16 + padding }, 300, 8); - // nameInput = TextInput.init(.{ 0, 0 }, 128, 22, editWorldName, .{ .onNewline = .init(pass) }); - // list.add(nameInput); + // nameInput = TextInput.init(.{ 0, 0 }, 128, 22, editWorldName, .{ .onNewline = .init(pass) }); + // list.add(nameInput); - //list.finish(.center); - //window.rootComponent = list.toComponent(); - window.contentSize = window.rootComponent.?.pos() + window.rootComponent.?.size() + @as(Vec2f, @splat(padding)); - gui.updateWindowPositions(); + //list.finish(.center); + //window.rootComponent = list.toComponent(); + window.contentSize = window.rootComponent.?.pos() + window.rootComponent.?.size() + @as(Vec2f, @splat(padding)); + gui.updateWindowPositions(); } pub fn onClose() void { - if (window.rootComponent) |*comp| { - comp.deinit(); - } + if (window.rootComponent) |*comp| { + comp.deinit(); + } } diff --git a/src/gui/windows/save_selection.zig b/src/gui/windows/save_selection.zig index f7349616e2..45d8c3329a 100644 --- a/src/gui/windows/save_selection.zig +++ b/src/gui/windows/save_selection.zig @@ -17,7 +17,7 @@ const TextInput = @import("../components/TextInput.zig"); const VerticalList = @import("../components/VerticalList.zig"); pub var window = GuiWindow{ - .contentSize = Vec2f{ 128, 256 }, + .contentSize = Vec2f{128, 256}, }; const padding: f32 = 8; @@ -33,157 +33,157 @@ var fileExplorerIcon: Texture = undefined; var editIcon: Texture = undefined; const WorldInfo = struct { - lastUsedTime: i64, - name: []const u8, - fileName: []const u8, + lastUsedTime: i64, + name: []const u8, + fileName: []const u8, }; var worldList: main.List(WorldInfo) = .empty; pub fn init() void { - deleteIcon = Texture.initFromFile("assets/cubyz/ui/delete_icon.png"); - fileExplorerIcon = Texture.initFromFile("assets/cubyz/ui/file_explorer_icon.png"); - editIcon = Texture.initFromFile("assets/cubyz/ui/edit_icon.png"); + deleteIcon = Texture.initFromFile("assets/cubyz/ui/delete_icon.png"); + fileExplorerIcon = Texture.initFromFile("assets/cubyz/ui/file_explorer_icon.png"); + editIcon = Texture.initFromFile("assets/cubyz/ui/edit_icon.png"); } pub fn deinit() void { - deleteIcon.deinit(); - fileExplorerIcon.deinit(); - editIcon.deinit(); + deleteIcon.deinit(); + fileExplorerIcon.deinit(); + editIcon.deinit(); } pub fn openWorld(name: []const u8) void { - const clientConnection = ConnectionManager.init(0, .{}) catch |err| { - std.log.err("Encountered error while opening connection: {s}", .{@errorName(err)}); - return; - }; - - std.log.info("Opening world {s}", .{name}); - main.server.thread = std.Thread.spawn(.{}, main.server.startFromNewThread, .{ name, clientConnection.localPort, mode }) catch |err| { - std.log.err("Encountered error while starting server thread: {s}", .{@errorName(err)}); - return; - }; - main.server.thread.?.setName(main.io, "Server") catch |err| { - std.log.err("Failed to rename Server thread: {s}", .{@errorName(err)}); - }; - - while (!main.server.running.load(.acquire)) { - main.io.sleep(.fromMilliseconds(1), .awake) catch {}; - main.heap.GarbageCollection.syncPoint(); - } - const ipPort = main.stackAllocator.print("127.0.0.1:{}", .{main.server.connectionManager.localPort}); - defer main.stackAllocator.free(ipPort); - const zon = main.game.testWorld.init(ipPort, clientConnection) catch |err| { - std.log.err("Encountered error while opening world: {s}", .{@errorName(err)}); - return; - }; - main.game.testWorld.finishHandshake(zon) catch |err| { - std.log.err("Encountered error while opening world: {s}", .{@errorName(err)}); - return; - }; - for (gui.openWindows.items) |openWindow| { - gui.closeWindowFromRef(openWindow); - } - gui.openHud(); + const clientConnection = ConnectionManager.init(0, .{}) catch |err| { + std.log.err("Encountered error while opening connection: {s}", .{@errorName(err)}); + return; + }; + + std.log.info("Opening world {s}", .{name}); + main.server.thread = std.Thread.spawn(.{}, main.server.startFromNewThread, .{name, clientConnection.localPort, mode}) catch |err| { + std.log.err("Encountered error while starting server thread: {s}", .{@errorName(err)}); + return; + }; + main.server.thread.?.setName(main.io, "Server") catch |err| { + std.log.err("Failed to rename Server thread: {s}", .{@errorName(err)}); + }; + + while (!main.server.running.load(.acquire)) { + main.io.sleep(.fromMilliseconds(1), .awake) catch {}; + main.heap.GarbageCollection.syncPoint(); + } + const ipPort = main.stackAllocator.print("127.0.0.1:{}", .{main.server.connectionManager.localPort}); + defer main.stackAllocator.free(ipPort); + const zon = main.game.testWorld.init(ipPort, clientConnection) catch |err| { + std.log.err("Encountered error while opening world: {s}", .{@errorName(err)}); + return; + }; + main.game.testWorld.finishHandshake(zon) catch |err| { + std.log.err("Encountered error while opening world: {s}", .{@errorName(err)}); + return; + }; + for (gui.openWindows.items) |openWindow| { + gui.closeWindowFromRef(openWindow); + } + gui.openHud(); } 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); + openWorld(worldList.items[index].fileName); } fn deleteWorld(index: usize) void { - main.gui.closeWindow("delete_world_confirmation"); - main.gui.windowlist.delete_world_confirmation.setDeleteWorldName(worldList.items[index].fileName); - main.gui.openWindow("delete_world_confirmation"); + main.gui.closeWindow("delete_world_confirmation"); + main.gui.windowlist.delete_world_confirmation.setDeleteWorldName(worldList.items[index].fileName); + main.gui.openWindow("delete_world_confirmation"); } fn openFolder(index: usize) void { - const path = main.stackAllocator.print("{s}/saves/{s}", .{ main.files.cubyzDirStr(), worldList.items[index].fileName }); - defer main.stackAllocator.free(path); + const path = main.stackAllocator.print("{s}/saves/{s}", .{main.files.cubyzDirStr(), worldList.items[index].fileName}); + defer main.stackAllocator.free(path); - main.files.openDirInWindow(path); + main.files.openDirInWindow(path); } fn editWorld(index: usize) void { - main.gui.closeWindow("edit_world"); - main.gui.windowlist.edit_world.setEditWorldName(worldList.items[index].fileName); - main.gui.openWindow("edit_world"); + main.gui.closeWindow("edit_world"); + main.gui.windowlist.edit_world.setEditWorldName(worldList.items[index].fileName); + main.gui.openWindow("edit_world"); } pub fn update() void { - if (needsUpdate) { - needsUpdate = false; - onClose(); - onOpen(); - } + if (needsUpdate) { + needsUpdate = false; + onClose(); + onOpen(); + } } pub fn onOpen() void { - buttonNameArena = main.heap.NeverFailingArenaAllocator.init(main.globalAllocator); - const list = VerticalList.init(.{ padding, 16 + padding }, 300, 8); - list.add(Label.init(.{ 0, 0 }, width, if (mode == .singleplayer) "**Select World**" else "**Select World to Host**", .center)); - list.add(Button.initText(.{ 0, 0 }, 128, "Create New World", .{ .onAction = gui.openWindowCallback("save_creation") })); - readingSaves: { - var dir = main.files.cubyzDir().openIterableDir("saves") catch |err| { - list.add(Label.init(.{ 0, 0 }, 128, "Encountered error while trying to open saves folder:", .center)); - list.add(Label.init(.{ 0, 0 }, 128, @errorName(err), .center)); - break :readingSaves; - }; - defer dir.close(); - - var iterator = dir.iterate(); - while (iterator.next(main.io) catch |err| { - list.add(Label.init(.{ 0, 0 }, 128, "Encountered error while iterating over saves folder:", .center)); - list.add(Label.init(.{ 0, 0 }, 128, @errorName(err), .center)); - break :readingSaves; - }) |entry| { - if (entry.kind == .directory) { - const worldInfoPath = main.stackAllocator.print("saves/{s}/world.zig.zon", .{entry.name}); - defer main.stackAllocator.free(worldInfoPath); - const worldInfo = main.files.cubyzDir().readToZon(main.stackAllocator, worldInfoPath) catch |err| { - std.log.err("Couldn't open save {s}: {s}", .{ worldInfoPath, @errorName(err) }); - continue; - }; - defer worldInfo.deinit(main.stackAllocator); - - worldList.append(main.globalAllocator, .{ - .fileName = main.globalAllocator.dupe(u8, entry.name), - .lastUsedTime = worldInfo.get(i64, "lastUsedTime") orelse 0, - .name = main.globalAllocator.dupe(u8, worldInfo.get([]const u8, "name") orelse entry.name), - }); - } - } - } - - 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, .{ .onAction = .initWithInt(openWorldWrap, i) })); - row.add(Button.initIcon(.{ 4, 0 }, .{ 16, 16 }, editIcon, .{ .onAction = .initWithInt(editWorld, i) })); - row.add(Button.initIcon(.{ 4, 0 }, .{ 16, 16 }, fileExplorerIcon, .{ .onAction = .initWithInt(openFolder, i) })); - row.add(Button.initIcon(.{ 4, 0 }, .{ 16, 16 }, deleteIcon, .{ .onAction = .initWithInt(deleteWorld, 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)); - gui.updateWindowPositions(); + buttonNameArena = main.heap.NeverFailingArenaAllocator.init(main.globalAllocator); + const list = VerticalList.init(.{padding, 16 + padding}, 300, 8); + list.add(Label.init(.{0, 0}, width, if (mode == .singleplayer) "**Select World**" else "**Select World to Host**", .center)); + list.add(Button.initText(.{0, 0}, 128, "Create New World", .{.onAction = gui.openWindowCallback("save_creation")})); + readingSaves: { + var dir = main.files.cubyzDir().openIterableDir("saves") catch |err| { + list.add(Label.init(.{0, 0}, 128, "Encountered error while trying to open saves folder:", .center)); + list.add(Label.init(.{0, 0}, 128, @errorName(err), .center)); + break :readingSaves; + }; + defer dir.close(); + + var iterator = dir.iterate(); + while (iterator.next(main.io) catch |err| { + list.add(Label.init(.{0, 0}, 128, "Encountered error while iterating over saves folder:", .center)); + list.add(Label.init(.{0, 0}, 128, @errorName(err), .center)); + break :readingSaves; + }) |entry| { + if (entry.kind == .directory) { + const worldInfoPath = main.stackAllocator.print("saves/{s}/world.zig.zon", .{entry.name}); + defer main.stackAllocator.free(worldInfoPath); + const worldInfo = main.files.cubyzDir().readToZon(main.stackAllocator, worldInfoPath) catch |err| { + std.log.err("Couldn't open save {s}: {s}", .{worldInfoPath, @errorName(err)}); + continue; + }; + defer worldInfo.deinit(main.stackAllocator); + + worldList.append(main.globalAllocator, .{ + .fileName = main.globalAllocator.dupe(u8, entry.name), + .lastUsedTime = worldInfo.get(i64, "lastUsedTime") orelse 0, + .name = main.globalAllocator.dupe(u8, worldInfo.get([]const u8, "name") orelse entry.name), + }); + } + } + } + + 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, .{.onAction = .initWithInt(openWorldWrap, i)})); + row.add(Button.initIcon(.{4, 0}, .{16, 16}, editIcon, .{.onAction = .initWithInt(editWorld, i)})); + row.add(Button.initIcon(.{4, 0}, .{16, 16}, fileExplorerIcon, .{.onAction = .initWithInt(openFolder, i)})); + row.add(Button.initIcon(.{4, 0}, .{16, 16}, deleteIcon, .{.onAction = .initWithInt(deleteWorld, 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)); + gui.updateWindowPositions(); } 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(); - } + 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(); + } } From 5b7b187d2e8336fd8ca9dee2dbb94e14ef2fa6f4 Mon Sep 17 00:00:00 2001 From: Abhay Kagdyal Date: Mon, 3 Aug 2026 15:14:17 +0530 Subject: [PATCH 04/14] add structs to get and store data from world.zig.zon --- src/gui/windows/edit_world.zig | 115 +++++++++++++++++++++++++++++++-- 1 file changed, 108 insertions(+), 7 deletions(-) diff --git a/src/gui/windows/edit_world.zig b/src/gui/windows/edit_world.zig index 7bd68ed103..a43cd203ab 100644 --- a/src/gui/windows/edit_world.zig +++ b/src/gui/windows/edit_world.zig @@ -12,8 +12,69 @@ const Label = @import("../components/Label.zig"); const TextInput = @import("../components/TextInput.zig"); const HorizontalList = @import("../components/HorizontalList.zig"); const VerticalList = @import("../components/VerticalList.zig"); +const CheckBox = @import("../components/CheckBox.zig"); -fn pass() void {} +const Gamemode = main.game.Gamemode; + +const WorldSettings = struct { + name: []const u8, + seed: i128, + defaultgameMode: Gamemode, + allowCheats: bool, + testingMode: bool, + localPlayerIndex: usize, + + pub fn init(name: []const u8, seed: i128, defaultgameMode: ?[]const u8, allowCheats: bool, testingMode: bool, localPlayerIndex: usize) WorldSettings { + return .{ + .name = name, + .seed = seed, + .defaultgameMode = toEnum(defaultgameMode), + .allowCheats = allowCheats, + .testingMode = testingMode, + .localPlayerIndex = localPlayerIndex, + }; + } + + fn toEnum(gamemode: ?[]const u8) Gamemode { + _ = gamemode; + } + + pub fn print(self: WorldSettings) void { + std.debug.print("name: {s}\nseed: {d}\n defaultGameMode: {s}\nallowCheats: {}\ntestingMode: {}\n", .{self.name, self.seed, self.defaultGameMode, self.allowCheats, self.testingMode}); + } +}; + +const WorldZonElement = struct { + worldInfo: ZonElement, + worldInfoSettings: ZonElement, + + fn check(worldInfoPath: []const u8) ZonElement { + if (main.files.cubyzDir().readToZon(main.stackAllocator, worldInfoPath)) |data| { + return data; + } else |err| { + std.log.err("Couldn't open save {s}: {s}", .{worldInfoPath, @errorName(err)}); + return ZonElement.initObject(main.stackAllocator); + } + } + + pub fn init(worldInfoPath: []const u8) *WorldZonElement { + const self = main.globalAllocator.create(WorldZonElement); + self.* = WorldZonElement{ + .worldInfo = WorldZonElement.check(worldInfoPath), + .worldInfoSettings = undefined, + }; + return self; + } + + pub fn initSettings(self: *WorldZonElement) void { + self.worldInfoSettings = self.worldInfo.getChild("settings"); + } + + pub fn deinit(self: @This()) void { + // self.worldInfoSettings.deinit(main.stackAllocator); + self.worldInfo.deinit(main.stackAllocator); + } +}; pub var window = GuiWindow{ .contentSize = Vec2f{128, 256}, @@ -23,9 +84,21 @@ const padding: f32 = 8; var editWorldName: []const u8 = ""; -var worldInfo: *ZonElement = undefined; +var worldZonElement: *WorldZonElement = undefined; +var worldSettings: WorldSettings = undefined; var nameInput: *TextInput = undefined; +var gamemodeInput: *Button = undefined; + +fn pass() void {} + +fn pass2(allow: bool) void { + _ = allow; +} + +fn gameModeCallback() void { + worldSettings.defaultGameMode = std.enums.fromInt(Gamemode, @intFromEnum(worldSettings.defaultGameMode) + 1) orelse @enumFromInt(0); +} pub fn init() void { editWorldName = ""; @@ -41,13 +114,41 @@ pub fn setEditWorldName(name: []const u8) void { } pub fn onOpen() void { - //const list = VerticalList.init(.{ padding, 16 + padding }, 300, 8); + const worldInfoPath = main.stackAllocator.print("saves/{s}/world.zig.zon", .{editWorldName}); + defer main.stackAllocator.free(worldInfoPath); + + worldZonElement = WorldZonElement.init(worldInfoPath); + defer worldZonElement.deinit(); + worldZonElement.initSettings(); + + worldSettings = WorldSettings.init( + worldZonElement.worldInfo.get([]const u8, "name") orelse "", + worldZonElement.worldInfoSettings.get(i128, "seed") orelse 0, + worldZonElement.worldInfoSettings.get([]const u8, "defaultGamemode") orelse "", + worldZonElement.worldInfoSettings.get(bool, "allowCheats") orelse false, + worldZonElement.worldInfoSettings.get(bool, "testingMode") orelse false, + worldZonElement.worldInfo.get(usize, "localPlayer") orelse 0, + ); + const list = VerticalList.init(.{padding, 16 + padding}, 300, 8); + + nameInput = TextInput.init(.{0, 0}, 128, 22, editWorldName, .{.onNewline = .init(pass)}); + list.add(nameInput); + + list.add(CheckBox.init(.{0, 0}, 128, "Allow Cheats", worldSettings.allowCheats, &pass2)); + + list.add(CheckBox.init(.{0, 0}, 128, "Testing Mode", worldSettings.testingMode, &pass2)); - // nameInput = TextInput.init(.{ 0, 0 }, 128, 22, editWorldName, .{ .onNewline = .init(pass) }); - // list.add(nameInput); + const seed = main.stackAllocator.print("{d}", .{worldSettings.seed}); + defer main.stackAllocator.free(seed); + const seedLabel = Label.init(.{0, 0}, 48, "Seed:", .left); + const seedInput = TextInput.init(.{0, 0}, 128 - 48, 22, seed, .{.disabled = true}); + const seedRow = HorizontalList.init(); + seedRow.add(seedLabel); + seedRow.add(seedInput); + list.add(seedRow); - //list.finish(.center); - //window.rootComponent = list.toComponent(); + list.finish(.center); + window.rootComponent = list.toComponent(); window.contentSize = window.rootComponent.?.pos() + window.rootComponent.?.size() + @as(Vec2f, @splat(padding)); gui.updateWindowPositions(); } From 0283da9826e28129f3983f52a1385f22b650dae8 Mon Sep 17 00:00:00 2001 From: Abhay Kagdyal Date: Mon, 3 Aug 2026 23:00:51 +0530 Subject: [PATCH 05/14] finished window structure --- src/gui/components/TextInput.zig | 2 +- src/gui/windows/edit_world.zig | 111 ++++++++----- src/gui/windows/save_selection.zig | 245 +++++++++++++++-------------- 3 files changed, 193 insertions(+), 165 deletions(-) diff --git a/src/gui/components/TextInput.zig b/src/gui/components/TextInput.zig index ba596f3f0e..62db14c662 100644 --- a/src/gui/components/TextInput.zig +++ b/src/gui/components/TextInput.zig @@ -48,7 +48,7 @@ pub fn globalDeinit() void { const Options = struct { disabled: bool = false, - onNewline: main.callbacks.SimpleCallback, + onNewline: main.callbacks.SimpleCallback = .{}, onUp: main.callbacks.SimpleCallback = .{}, onDown: main.callbacks.SimpleCallback = .{}, onUpdate: main.callbacks.SimpleCallback = .{}, diff --git a/src/gui/windows/edit_world.zig b/src/gui/windows/edit_world.zig index a43cd203ab..0382119dd4 100644 --- a/src/gui/windows/edit_world.zig +++ b/src/gui/windows/edit_world.zig @@ -15,20 +15,19 @@ const VerticalList = @import("../components/VerticalList.zig"); const CheckBox = @import("../components/CheckBox.zig"); const Gamemode = main.game.Gamemode; +const DefaultSettings = main.server.world_zig.Settings.defaults; const WorldSettings = struct { - name: []const u8, seed: i128, - defaultgameMode: Gamemode, + defaultGamemode: Gamemode, allowCheats: bool, testingMode: bool, localPlayerIndex: usize, - pub fn init(name: []const u8, seed: i128, defaultgameMode: ?[]const u8, allowCheats: bool, testingMode: bool, localPlayerIndex: usize) WorldSettings { + pub fn init(seed: i128, defaultgameMode: ?[]const u8, allowCheats: bool, testingMode: bool, localPlayerIndex: usize) WorldSettings { return .{ - .name = name, .seed = seed, - .defaultgameMode = toEnum(defaultgameMode), + .defaultGamemode = toEnum(defaultgameMode), .allowCheats = allowCheats, .testingMode = testingMode, .localPlayerIndex = localPlayerIndex, @@ -36,11 +35,18 @@ const WorldSettings = struct { } fn toEnum(gamemode: ?[]const u8) Gamemode { - _ = gamemode; + const mode = gamemode orelse @tagName(DefaultSettings.defaultGamemode); + return std.meta.stringToEnum(Gamemode, mode).?; } pub fn print(self: WorldSettings) void { - std.debug.print("name: {s}\nseed: {d}\n defaultGameMode: {s}\nallowCheats: {}\ntestingMode: {}\n", .{self.name, self.seed, self.defaultGameMode, self.allowCheats, self.testingMode}); + std.debug.print("name: {s}\nseed: {d}\n defaultGameMode: {s}\nallowCheats: {}\ntestingMode: {}\n", .{ + nameInput.currentString.items, + self.seed, + @tagName(self.defaultGamemode), + self.allowCheats, + self.testingMode, + }); } }; @@ -48,19 +54,17 @@ const WorldZonElement = struct { worldInfo: ZonElement, worldInfoSettings: ZonElement, - fn check(worldInfoPath: []const u8) ZonElement { - if (main.files.cubyzDir().readToZon(main.stackAllocator, worldInfoPath)) |data| { - return data; - } else |err| { - std.log.err("Couldn't open save {s}: {s}", .{worldInfoPath, @errorName(err)}); - return ZonElement.initObject(main.stackAllocator); - } - } - pub fn init(worldInfoPath: []const u8) *WorldZonElement { - const self = main.globalAllocator.create(WorldZonElement); + const self = main.stackAllocator.create(WorldZonElement); self.* = WorldZonElement{ - .worldInfo = WorldZonElement.check(worldInfoPath), + .worldInfo = zon: { + if (main.files.cubyzDir().readToZon(main.stackAllocator, worldInfoPath)) |data| { + break :zon data; + } else |err| { + std.log.err("{ant}", .{err}); + break :zon undefined; + } + }, .worldInfoSettings = undefined, }; return self; @@ -70,9 +74,10 @@ const WorldZonElement = struct { self.worldInfoSettings = self.worldInfo.getChild("settings"); } - pub fn deinit(self: @This()) void { - // self.worldInfoSettings.deinit(main.stackAllocator); + pub fn deinit(self: *WorldZonElement) void { + self.worldInfoSettings.deinit(main.stackAllocator); self.worldInfo.deinit(main.stackAllocator); + main.stackAllocator.destroy(self); } }; @@ -84,28 +89,33 @@ const padding: f32 = 8; var editWorldName: []const u8 = ""; -var worldZonElement: *WorldZonElement = undefined; +var worldInfo: *ZonElement = undefined; +var worldInfoSettings: *ZonElement = undefined; var worldSettings: WorldSettings = undefined; var nameInput: *TextInput = undefined; var gamemodeInput: *Button = undefined; -fn pass() void {} - -fn pass2(allow: bool) void { - _ = allow; +fn nameCallback() void { + const newName = nameInput.currentString.items; + _ = newName; } fn gameModeCallback() void { - worldSettings.defaultGameMode = std.enums.fromInt(Gamemode, @intFromEnum(worldSettings.defaultGameMode) + 1) orelse @enumFromInt(0); + worldSettings.defaultGamemode = std.enums.fromInt(Gamemode, @intFromEnum(worldSettings.defaultGamemode) + 1) orelse @enumFromInt(0); + gamemodeInput.child.label.updateText(@tagName(worldSettings.defaultGamemode)); } -pub fn init() void { - editWorldName = ""; +fn allowCheatsCallback(allow: bool) void { + worldSettings.allowCheats = allow; } -pub fn deinit() void { - main.globalAllocator.free(editWorldName); +fn testingModeCallback(enabled: bool) void { + worldSettings.testingMode = enabled; +} + +fn saveChangesCallback() void { + worldSettings.print(); } pub fn setEditWorldName(name: []const u8) void { @@ -117,26 +127,36 @@ pub fn onOpen() void { const worldInfoPath = main.stackAllocator.print("saves/{s}/world.zig.zon", .{editWorldName}); defer main.stackAllocator.free(worldInfoPath); - worldZonElement = WorldZonElement.init(worldInfoPath); - defer worldZonElement.deinit(); - worldZonElement.initSettings(); + worldInfo.* = main.files.cubyzDir().readToZon(main.stackAllocator, worldInfoPath) catch |err| { + std.log.err("Error while creating new world: {s}", .{@errorName(err)}); + return; + }; + defer main.stackAllocator.destroy(worldInfo); + + worldInfoSettings.* = worldInfo.getChild("settings"); + defer main.stackAllocator.destroy(worldInfoSettings); worldSettings = WorldSettings.init( - worldZonElement.worldInfo.get([]const u8, "name") orelse "", - worldZonElement.worldInfoSettings.get(i128, "seed") orelse 0, - worldZonElement.worldInfoSettings.get([]const u8, "defaultGamemode") orelse "", - worldZonElement.worldInfoSettings.get(bool, "allowCheats") orelse false, - worldZonElement.worldInfoSettings.get(bool, "testingMode") orelse false, - worldZonElement.worldInfo.get(usize, "localPlayer") orelse 0, + worldInfoSettings.get(i128, "seed") orelse 0, + worldInfoSettings.get([]const u8, "defaultGamemode"), + worldInfoSettings.get(bool, "allowCheats") orelse DefaultSettings.allowCheats, + worldInfoSettings.get(bool, "testingMode") orelse DefaultSettings.testingMode, + worldInfo.get(usize, "localPlayer") orelse 0, ); + const list = VerticalList.init(.{padding, 16 + padding}, 300, 8); - nameInput = TextInput.init(.{0, 0}, 128, 22, editWorldName, .{.onNewline = .init(pass)}); + const name = main.stackAllocator.print("{s}", .{worldInfo.get([]const u8, "name") orelse ""}); + defer main.stackAllocator.free(name); + nameInput = TextInput.init(.{0, 0}, 128, 22, name, .{.onNewline = .init(nameCallback)}); list.add(nameInput); - list.add(CheckBox.init(.{0, 0}, 128, "Allow Cheats", worldSettings.allowCheats, &pass2)); + gamemodeInput = Button.initText(.{0, 0}, 128, @tagName(worldSettings.defaultGamemode), .{.onAction = .init(gameModeCallback)}); + list.add(gamemodeInput); + + list.add(CheckBox.init(.{0, 0}, 128, "Allow Cheats", worldSettings.allowCheats, &allowCheatsCallback)); - list.add(CheckBox.init(.{0, 0}, 128, "Testing Mode", worldSettings.testingMode, &pass2)); + list.add(CheckBox.init(.{0, 0}, 128, "Testing Mode", worldSettings.testingMode, &testingModeCallback)); const seed = main.stackAllocator.print("{d}", .{worldSettings.seed}); defer main.stackAllocator.free(seed); @@ -147,6 +167,12 @@ pub fn onOpen() void { seedRow.add(seedInput); list.add(seedRow); + const indexLabel = Label.init(.{0, 0}, 128, "Local Player Indiex", .left); + list.add(indexLabel); + + const saveChanges = Button.initText(.{0, 0}, 128, "Save Changes", .{.onAction = .init(saveChangesCallback)}); + list.add(saveChanges); + list.finish(.center); window.rootComponent = list.toComponent(); window.contentSize = window.rootComponent.?.pos() + window.rootComponent.?.size() + @as(Vec2f, @splat(padding)); @@ -157,4 +183,5 @@ pub fn onClose() void { if (window.rootComponent) |*comp| { comp.deinit(); } + main.stackAllocator.free(editWorldName); } diff --git a/src/gui/windows/save_selection.zig b/src/gui/windows/save_selection.zig index 45d8c3329a..a11f851177 100644 --- a/src/gui/windows/save_selection.zig +++ b/src/gui/windows/save_selection.zig @@ -17,7 +17,7 @@ const TextInput = @import("../components/TextInput.zig"); const VerticalList = @import("../components/VerticalList.zig"); pub var window = GuiWindow{ - .contentSize = Vec2f{128, 256}, + .contentSize = Vec2f{ 128, 256 }, }; const padding: f32 = 8; @@ -33,157 +33,158 @@ var fileExplorerIcon: Texture = undefined; var editIcon: Texture = undefined; const WorldInfo = struct { - lastUsedTime: i64, - name: []const u8, - fileName: []const u8, + lastUsedTime: i64, + name: []const u8, + fileName: []const u8, }; var worldList: main.List(WorldInfo) = .empty; pub fn init() void { - deleteIcon = Texture.initFromFile("assets/cubyz/ui/delete_icon.png"); - fileExplorerIcon = Texture.initFromFile("assets/cubyz/ui/file_explorer_icon.png"); - editIcon = Texture.initFromFile("assets/cubyz/ui/edit_icon.png"); + deleteIcon = Texture.initFromFile("assets/cubyz/ui/delete_icon.png"); + fileExplorerIcon = Texture.initFromFile("assets/cubyz/ui/file_explorer_icon.png"); + editIcon = Texture.initFromFile("assets/cubyz/ui/edit_icon.png"); } pub fn deinit() void { - deleteIcon.deinit(); - fileExplorerIcon.deinit(); - editIcon.deinit(); + deleteIcon.deinit(); + fileExplorerIcon.deinit(); + editIcon.deinit(); } pub fn openWorld(name: []const u8) void { - const clientConnection = ConnectionManager.init(0, .{}) catch |err| { - std.log.err("Encountered error while opening connection: {s}", .{@errorName(err)}); - return; - }; - - std.log.info("Opening world {s}", .{name}); - main.server.thread = std.Thread.spawn(.{}, main.server.startFromNewThread, .{name, clientConnection.localPort, mode}) catch |err| { - std.log.err("Encountered error while starting server thread: {s}", .{@errorName(err)}); - return; - }; - main.server.thread.?.setName(main.io, "Server") catch |err| { - std.log.err("Failed to rename Server thread: {s}", .{@errorName(err)}); - }; - - while (!main.server.running.load(.acquire)) { - main.io.sleep(.fromMilliseconds(1), .awake) catch {}; - main.heap.GarbageCollection.syncPoint(); - } - const ipPort = main.stackAllocator.print("127.0.0.1:{}", .{main.server.connectionManager.localPort}); - defer main.stackAllocator.free(ipPort); - const zon = main.game.testWorld.init(ipPort, clientConnection) catch |err| { - std.log.err("Encountered error while opening world: {s}", .{@errorName(err)}); - return; - }; - main.game.testWorld.finishHandshake(zon) catch |err| { - std.log.err("Encountered error while opening world: {s}", .{@errorName(err)}); - return; - }; - for (gui.openWindows.items) |openWindow| { - gui.closeWindowFromRef(openWindow); - } - gui.openHud(); + const clientConnection = ConnectionManager.init(0, .{}) catch |err| { + std.log.err("Encountered error while opening connection: {s}", .{@errorName(err)}); + return; + }; + + std.log.info("Opening world {s}", .{name}); + main.server.thread = std.Thread.spawn(.{}, main.server.startFromNewThread, .{ name, clientConnection.localPort, mode }) catch |err| { + std.log.err("Encountered error while starting server thread: {s}", .{@errorName(err)}); + return; + }; + main.server.thread.?.setName(main.io, "Server") catch |err| { + std.log.err("Failed to rename Server thread: {s}", .{@errorName(err)}); + }; + + while (!main.server.running.load(.acquire)) { + main.io.sleep(.fromMilliseconds(1), .awake) catch {}; + main.heap.GarbageCollection.syncPoint(); + } + const ipPort = main.stackAllocator.print("127.0.0.1:{}", .{main.server.connectionManager.localPort}); + defer main.stackAllocator.free(ipPort); + const zon = main.game.testWorld.init(ipPort, clientConnection) catch |err| { + std.log.err("Encountered error while opening world: {s}", .{@errorName(err)}); + return; + }; + main.game.testWorld.finishHandshake(zon) catch |err| { + std.log.err("Encountered error while opening world: {s}", .{@errorName(err)}); + return; + }; + for (gui.openWindows.items) |openWindow| { + gui.closeWindowFromRef(openWindow); + } + gui.openHud(); } 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); + openWorld(worldList.items[index].fileName); } fn deleteWorld(index: usize) void { - main.gui.closeWindow("delete_world_confirmation"); - main.gui.windowlist.delete_world_confirmation.setDeleteWorldName(worldList.items[index].fileName); - main.gui.openWindow("delete_world_confirmation"); + main.gui.closeWindow("delete_world_confirmation"); + main.gui.windowlist.delete_world_confirmation.setDeleteWorldName(worldList.items[index].fileName); + main.gui.openWindow("delete_world_confirmation"); } fn openFolder(index: usize) void { - const path = main.stackAllocator.print("{s}/saves/{s}", .{main.files.cubyzDirStr(), worldList.items[index].fileName}); - defer main.stackAllocator.free(path); + const path = main.stackAllocator.print("{s}/saves/{s}", .{ main.files.cubyzDirStr(), worldList.items[index].fileName }); + defer main.stackAllocator.free(path); - main.files.openDirInWindow(path); + main.files.openDirInWindow(path); } fn editWorld(index: usize) void { - main.gui.closeWindow("edit_world"); - main.gui.windowlist.edit_world.setEditWorldName(worldList.items[index].fileName); - main.gui.openWindow("edit_world"); + main.gui.windowlist.edit_world.setEditWorldName(worldList.items[index].fileName); } pub fn update() void { - if (needsUpdate) { - needsUpdate = false; - onClose(); - onOpen(); - } + if (needsUpdate) { + needsUpdate = false; + onClose(); + onOpen(); + } } pub fn onOpen() void { - buttonNameArena = main.heap.NeverFailingArenaAllocator.init(main.globalAllocator); - const list = VerticalList.init(.{padding, 16 + padding}, 300, 8); - list.add(Label.init(.{0, 0}, width, if (mode == .singleplayer) "**Select World**" else "**Select World to Host**", .center)); - list.add(Button.initText(.{0, 0}, 128, "Create New World", .{.onAction = gui.openWindowCallback("save_creation")})); - readingSaves: { - var dir = main.files.cubyzDir().openIterableDir("saves") catch |err| { - list.add(Label.init(.{0, 0}, 128, "Encountered error while trying to open saves folder:", .center)); - list.add(Label.init(.{0, 0}, 128, @errorName(err), .center)); - break :readingSaves; - }; - defer dir.close(); - - var iterator = dir.iterate(); - while (iterator.next(main.io) catch |err| { - list.add(Label.init(.{0, 0}, 128, "Encountered error while iterating over saves folder:", .center)); - list.add(Label.init(.{0, 0}, 128, @errorName(err), .center)); - break :readingSaves; - }) |entry| { - if (entry.kind == .directory) { - const worldInfoPath = main.stackAllocator.print("saves/{s}/world.zig.zon", .{entry.name}); - defer main.stackAllocator.free(worldInfoPath); - const worldInfo = main.files.cubyzDir().readToZon(main.stackAllocator, worldInfoPath) catch |err| { - std.log.err("Couldn't open save {s}: {s}", .{worldInfoPath, @errorName(err)}); - continue; - }; - defer worldInfo.deinit(main.stackAllocator); - - worldList.append(main.globalAllocator, .{ - .fileName = main.globalAllocator.dupe(u8, entry.name), - .lastUsedTime = worldInfo.get(i64, "lastUsedTime") orelse 0, - .name = main.globalAllocator.dupe(u8, worldInfo.get([]const u8, "name") orelse entry.name), - }); - } - } - } - - 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, .{.onAction = .initWithInt(openWorldWrap, i)})); - row.add(Button.initIcon(.{4, 0}, .{16, 16}, editIcon, .{.onAction = .initWithInt(editWorld, i)})); - row.add(Button.initIcon(.{4, 0}, .{16, 16}, fileExplorerIcon, .{.onAction = .initWithInt(openFolder, i)})); - row.add(Button.initIcon(.{4, 0}, .{16, 16}, deleteIcon, .{.onAction = .initWithInt(deleteWorld, 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)); - gui.updateWindowPositions(); + buttonNameArena = main.heap.NeverFailingArenaAllocator.init(main.globalAllocator); + const list = VerticalList.init(.{ padding, 16 + padding }, 300, 8); + list.add(Label.init(.{ 0, 0 }, width, if (mode == .singleplayer) "**Select World**" else "**Select World to Host**", .center)); + list.add(Button.initText(.{ 0, 0 }, 128, "Create New World", .{ .onAction = gui.openWindowCallback("save_creation") })); + readingSaves: { + var dir = main.files.cubyzDir().openIterableDir("saves") catch |err| { + list.add(Label.init(.{ 0, 0 }, 128, "Encountered error while trying to open saves folder:", .center)); + list.add(Label.init(.{ 0, 0 }, 128, @errorName(err), .center)); + break :readingSaves; + }; + defer dir.close(); + + var iterator = dir.iterate(); + while (iterator.next(main.io) catch |err| { + list.add(Label.init(.{ 0, 0 }, 128, "Encountered error while iterating over saves folder:", .center)); + list.add(Label.init(.{ 0, 0 }, 128, @errorName(err), .center)); + break :readingSaves; + }) |entry| { + if (entry.kind == .directory) { + const worldInfoPath = main.stackAllocator.print("saves/{s}/world.zig.zon", .{entry.name}); + defer main.stackAllocator.free(worldInfoPath); + const worldInfo = main.files.cubyzDir().readToZon(main.stackAllocator, worldInfoPath) catch |err| { + std.log.err("Couldn't open save {s}: {s}", .{ worldInfoPath, @errorName(err) }); + continue; + }; + defer worldInfo.deinit(main.stackAllocator); + + worldList.append(main.globalAllocator, .{ + .fileName = main.globalAllocator.dupe(u8, entry.name), + .lastUsedTime = worldInfo.get(i64, "lastUsedTime") orelse 0, + .name = main.globalAllocator.dupe(u8, worldInfo.get([]const u8, "name") orelse entry.name), + }); + } + } + } + + 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, .{ .onAction = .initWithInt(openWorldWrap, i) })); + row.add(Button.initIcon(.{ 4, 0 }, .{ 16, 16 }, editIcon, .{ .onAction = callback: { + main.gui.windowlist.edit_world.setEditWorldName(worldList.items[i].fileName); + break :callback main.gui.openWindowCallback("edit_world"); + } })); + row.add(Button.initIcon(.{ 4, 0 }, .{ 16, 16 }, fileExplorerIcon, .{ .onAction = .initWithInt(openFolder, i) })); + row.add(Button.initIcon(.{ 4, 0 }, .{ 16, 16 }, deleteIcon, .{ .onAction = .initWithInt(deleteWorld, 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)); + gui.updateWindowPositions(); } 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(); - } + 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(); + } } From 80095a7b7bb323081068e9ddbdc5723ed1b36198 Mon Sep 17 00:00:00 2001 From: Abhay Kagdyal Date: Mon, 3 Aug 2026 23:02:17 +0530 Subject: [PATCH 06/14] remove WorldZonElement struct --- src/gui/windows/edit_world.zig | 31 ------------------------------- 1 file changed, 31 deletions(-) diff --git a/src/gui/windows/edit_world.zig b/src/gui/windows/edit_world.zig index 0382119dd4..170cd86fa5 100644 --- a/src/gui/windows/edit_world.zig +++ b/src/gui/windows/edit_world.zig @@ -50,37 +50,6 @@ const WorldSettings = struct { } }; -const WorldZonElement = struct { - worldInfo: ZonElement, - worldInfoSettings: ZonElement, - - pub fn init(worldInfoPath: []const u8) *WorldZonElement { - const self = main.stackAllocator.create(WorldZonElement); - self.* = WorldZonElement{ - .worldInfo = zon: { - if (main.files.cubyzDir().readToZon(main.stackAllocator, worldInfoPath)) |data| { - break :zon data; - } else |err| { - std.log.err("{ant}", .{err}); - break :zon undefined; - } - }, - .worldInfoSettings = undefined, - }; - return self; - } - - pub fn initSettings(self: *WorldZonElement) void { - self.worldInfoSettings = self.worldInfo.getChild("settings"); - } - - pub fn deinit(self: *WorldZonElement) void { - self.worldInfoSettings.deinit(main.stackAllocator); - self.worldInfo.deinit(main.stackAllocator); - main.stackAllocator.destroy(self); - } -}; - pub var window = GuiWindow{ .contentSize = Vec2f{128, 256}, }; From 4a52fef2f9f03890a3aa35de45a16aecfea1eba3 Mon Sep 17 00:00:00 2001 From: Abhay Kagdyal Date: Mon, 3 Aug 2026 23:54:57 +0530 Subject: [PATCH 07/14] try to fix memory errors --- src/gui/windows/edit_world.zig | 31 ++-- src/gui/windows/save_selection.zig | 245 ++++++++++++++--------------- 2 files changed, 142 insertions(+), 134 deletions(-) diff --git a/src/gui/windows/edit_world.zig b/src/gui/windows/edit_world.zig index 170cd86fa5..353ed8c974 100644 --- a/src/gui/windows/edit_world.zig +++ b/src/gui/windows/edit_world.zig @@ -18,14 +18,16 @@ const Gamemode = main.game.Gamemode; const DefaultSettings = main.server.world_zig.Settings.defaults; const WorldSettings = struct { + name: []const u8, seed: i128, defaultGamemode: Gamemode, allowCheats: bool, testingMode: bool, localPlayerIndex: usize, - pub fn init(seed: i128, defaultgameMode: ?[]const u8, allowCheats: bool, testingMode: bool, localPlayerIndex: usize) WorldSettings { + pub fn init(name: []const u8, seed: i128, defaultgameMode: ?[]const u8, allowCheats: bool, testingMode: bool, localPlayerIndex: usize) WorldSettings { return .{ + .name = name, .seed = seed, .defaultGamemode = toEnum(defaultgameMode), .allowCheats = allowCheats, @@ -58,8 +60,8 @@ const padding: f32 = 8; var editWorldName: []const u8 = ""; -var worldInfo: *ZonElement = undefined; -var worldInfoSettings: *ZonElement = undefined; +var worldInfo: ZonElement = undefined; +var worldInfoSettings: ZonElement = undefined; var worldSettings: WorldSettings = undefined; var nameInput: *TextInput = undefined; @@ -87,25 +89,35 @@ fn saveChangesCallback() void { worldSettings.print(); } +pub fn init() void { + editWorldName = ""; +} + +pub fn deinit() void { + main.stackAllocator.free(editWorldName); +} + pub fn setEditWorldName(name: []const u8) void { main.globalAllocator.free(editWorldName); editWorldName = main.globalAllocator.dupe(u8, name); } pub fn onOpen() void { + std.debug.print("{s}", .{editWorldName}); const worldInfoPath = main.stackAllocator.print("saves/{s}/world.zig.zon", .{editWorldName}); defer main.stackAllocator.free(worldInfoPath); - worldInfo.* = main.files.cubyzDir().readToZon(main.stackAllocator, worldInfoPath) catch |err| { + worldInfo = main.files.cubyzDir().readToZon(main.stackAllocator, worldInfoPath) catch |err| { std.log.err("Error while creating new world: {s}", .{@errorName(err)}); return; }; - defer main.stackAllocator.destroy(worldInfo); + defer worldInfo.deinit(main.stackAllocator); - worldInfoSettings.* = worldInfo.getChild("settings"); - defer main.stackAllocator.destroy(worldInfoSettings); + worldInfoSettings = worldInfo.getChild("settings"); + defer worldInfoSettings.deinit(main.stackAllocator); worldSettings = WorldSettings.init( + worldInfo.get([]const u8, "name") orelse "", worldInfoSettings.get(i128, "seed") orelse 0, worldInfoSettings.get([]const u8, "defaultGamemode"), worldInfoSettings.get(bool, "allowCheats") orelse DefaultSettings.allowCheats, @@ -115,9 +127,7 @@ pub fn onOpen() void { const list = VerticalList.init(.{padding, 16 + padding}, 300, 8); - const name = main.stackAllocator.print("{s}", .{worldInfo.get([]const u8, "name") orelse ""}); - defer main.stackAllocator.free(name); - nameInput = TextInput.init(.{0, 0}, 128, 22, name, .{.onNewline = .init(nameCallback)}); + nameInput = TextInput.init(.{0, 0}, 128, 22, worldSettings.name, .{.onNewline = .init(nameCallback)}); list.add(nameInput); gamemodeInput = Button.initText(.{0, 0}, 128, @tagName(worldSettings.defaultGamemode), .{.onAction = .init(gameModeCallback)}); @@ -152,5 +162,4 @@ pub fn onClose() void { if (window.rootComponent) |*comp| { comp.deinit(); } - main.stackAllocator.free(editWorldName); } diff --git a/src/gui/windows/save_selection.zig b/src/gui/windows/save_selection.zig index a11f851177..45d8c3329a 100644 --- a/src/gui/windows/save_selection.zig +++ b/src/gui/windows/save_selection.zig @@ -17,7 +17,7 @@ const TextInput = @import("../components/TextInput.zig"); const VerticalList = @import("../components/VerticalList.zig"); pub var window = GuiWindow{ - .contentSize = Vec2f{ 128, 256 }, + .contentSize = Vec2f{128, 256}, }; const padding: f32 = 8; @@ -33,158 +33,157 @@ var fileExplorerIcon: Texture = undefined; var editIcon: Texture = undefined; const WorldInfo = struct { - lastUsedTime: i64, - name: []const u8, - fileName: []const u8, + lastUsedTime: i64, + name: []const u8, + fileName: []const u8, }; var worldList: main.List(WorldInfo) = .empty; pub fn init() void { - deleteIcon = Texture.initFromFile("assets/cubyz/ui/delete_icon.png"); - fileExplorerIcon = Texture.initFromFile("assets/cubyz/ui/file_explorer_icon.png"); - editIcon = Texture.initFromFile("assets/cubyz/ui/edit_icon.png"); + deleteIcon = Texture.initFromFile("assets/cubyz/ui/delete_icon.png"); + fileExplorerIcon = Texture.initFromFile("assets/cubyz/ui/file_explorer_icon.png"); + editIcon = Texture.initFromFile("assets/cubyz/ui/edit_icon.png"); } pub fn deinit() void { - deleteIcon.deinit(); - fileExplorerIcon.deinit(); - editIcon.deinit(); + deleteIcon.deinit(); + fileExplorerIcon.deinit(); + editIcon.deinit(); } pub fn openWorld(name: []const u8) void { - const clientConnection = ConnectionManager.init(0, .{}) catch |err| { - std.log.err("Encountered error while opening connection: {s}", .{@errorName(err)}); - return; - }; - - std.log.info("Opening world {s}", .{name}); - main.server.thread = std.Thread.spawn(.{}, main.server.startFromNewThread, .{ name, clientConnection.localPort, mode }) catch |err| { - std.log.err("Encountered error while starting server thread: {s}", .{@errorName(err)}); - return; - }; - main.server.thread.?.setName(main.io, "Server") catch |err| { - std.log.err("Failed to rename Server thread: {s}", .{@errorName(err)}); - }; - - while (!main.server.running.load(.acquire)) { - main.io.sleep(.fromMilliseconds(1), .awake) catch {}; - main.heap.GarbageCollection.syncPoint(); - } - const ipPort = main.stackAllocator.print("127.0.0.1:{}", .{main.server.connectionManager.localPort}); - defer main.stackAllocator.free(ipPort); - const zon = main.game.testWorld.init(ipPort, clientConnection) catch |err| { - std.log.err("Encountered error while opening world: {s}", .{@errorName(err)}); - return; - }; - main.game.testWorld.finishHandshake(zon) catch |err| { - std.log.err("Encountered error while opening world: {s}", .{@errorName(err)}); - return; - }; - for (gui.openWindows.items) |openWindow| { - gui.closeWindowFromRef(openWindow); - } - gui.openHud(); + const clientConnection = ConnectionManager.init(0, .{}) catch |err| { + std.log.err("Encountered error while opening connection: {s}", .{@errorName(err)}); + return; + }; + + std.log.info("Opening world {s}", .{name}); + main.server.thread = std.Thread.spawn(.{}, main.server.startFromNewThread, .{name, clientConnection.localPort, mode}) catch |err| { + std.log.err("Encountered error while starting server thread: {s}", .{@errorName(err)}); + return; + }; + main.server.thread.?.setName(main.io, "Server") catch |err| { + std.log.err("Failed to rename Server thread: {s}", .{@errorName(err)}); + }; + + while (!main.server.running.load(.acquire)) { + main.io.sleep(.fromMilliseconds(1), .awake) catch {}; + main.heap.GarbageCollection.syncPoint(); + } + const ipPort = main.stackAllocator.print("127.0.0.1:{}", .{main.server.connectionManager.localPort}); + defer main.stackAllocator.free(ipPort); + const zon = main.game.testWorld.init(ipPort, clientConnection) catch |err| { + std.log.err("Encountered error while opening world: {s}", .{@errorName(err)}); + return; + }; + main.game.testWorld.finishHandshake(zon) catch |err| { + std.log.err("Encountered error while opening world: {s}", .{@errorName(err)}); + return; + }; + for (gui.openWindows.items) |openWindow| { + gui.closeWindowFromRef(openWindow); + } + gui.openHud(); } 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); + openWorld(worldList.items[index].fileName); } fn deleteWorld(index: usize) void { - main.gui.closeWindow("delete_world_confirmation"); - main.gui.windowlist.delete_world_confirmation.setDeleteWorldName(worldList.items[index].fileName); - main.gui.openWindow("delete_world_confirmation"); + main.gui.closeWindow("delete_world_confirmation"); + main.gui.windowlist.delete_world_confirmation.setDeleteWorldName(worldList.items[index].fileName); + main.gui.openWindow("delete_world_confirmation"); } fn openFolder(index: usize) void { - const path = main.stackAllocator.print("{s}/saves/{s}", .{ main.files.cubyzDirStr(), worldList.items[index].fileName }); - defer main.stackAllocator.free(path); + const path = main.stackAllocator.print("{s}/saves/{s}", .{main.files.cubyzDirStr(), worldList.items[index].fileName}); + defer main.stackAllocator.free(path); - main.files.openDirInWindow(path); + main.files.openDirInWindow(path); } fn editWorld(index: usize) void { - main.gui.windowlist.edit_world.setEditWorldName(worldList.items[index].fileName); + main.gui.closeWindow("edit_world"); + main.gui.windowlist.edit_world.setEditWorldName(worldList.items[index].fileName); + main.gui.openWindow("edit_world"); } pub fn update() void { - if (needsUpdate) { - needsUpdate = false; - onClose(); - onOpen(); - } + if (needsUpdate) { + needsUpdate = false; + onClose(); + onOpen(); + } } pub fn onOpen() void { - buttonNameArena = main.heap.NeverFailingArenaAllocator.init(main.globalAllocator); - const list = VerticalList.init(.{ padding, 16 + padding }, 300, 8); - list.add(Label.init(.{ 0, 0 }, width, if (mode == .singleplayer) "**Select World**" else "**Select World to Host**", .center)); - list.add(Button.initText(.{ 0, 0 }, 128, "Create New World", .{ .onAction = gui.openWindowCallback("save_creation") })); - readingSaves: { - var dir = main.files.cubyzDir().openIterableDir("saves") catch |err| { - list.add(Label.init(.{ 0, 0 }, 128, "Encountered error while trying to open saves folder:", .center)); - list.add(Label.init(.{ 0, 0 }, 128, @errorName(err), .center)); - break :readingSaves; - }; - defer dir.close(); - - var iterator = dir.iterate(); - while (iterator.next(main.io) catch |err| { - list.add(Label.init(.{ 0, 0 }, 128, "Encountered error while iterating over saves folder:", .center)); - list.add(Label.init(.{ 0, 0 }, 128, @errorName(err), .center)); - break :readingSaves; - }) |entry| { - if (entry.kind == .directory) { - const worldInfoPath = main.stackAllocator.print("saves/{s}/world.zig.zon", .{entry.name}); - defer main.stackAllocator.free(worldInfoPath); - const worldInfo = main.files.cubyzDir().readToZon(main.stackAllocator, worldInfoPath) catch |err| { - std.log.err("Couldn't open save {s}: {s}", .{ worldInfoPath, @errorName(err) }); - continue; - }; - defer worldInfo.deinit(main.stackAllocator); - - worldList.append(main.globalAllocator, .{ - .fileName = main.globalAllocator.dupe(u8, entry.name), - .lastUsedTime = worldInfo.get(i64, "lastUsedTime") orelse 0, - .name = main.globalAllocator.dupe(u8, worldInfo.get([]const u8, "name") orelse entry.name), - }); - } - } - } - - 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, .{ .onAction = .initWithInt(openWorldWrap, i) })); - row.add(Button.initIcon(.{ 4, 0 }, .{ 16, 16 }, editIcon, .{ .onAction = callback: { - main.gui.windowlist.edit_world.setEditWorldName(worldList.items[i].fileName); - break :callback main.gui.openWindowCallback("edit_world"); - } })); - row.add(Button.initIcon(.{ 4, 0 }, .{ 16, 16 }, fileExplorerIcon, .{ .onAction = .initWithInt(openFolder, i) })); - row.add(Button.initIcon(.{ 4, 0 }, .{ 16, 16 }, deleteIcon, .{ .onAction = .initWithInt(deleteWorld, 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)); - gui.updateWindowPositions(); + buttonNameArena = main.heap.NeverFailingArenaAllocator.init(main.globalAllocator); + const list = VerticalList.init(.{padding, 16 + padding}, 300, 8); + list.add(Label.init(.{0, 0}, width, if (mode == .singleplayer) "**Select World**" else "**Select World to Host**", .center)); + list.add(Button.initText(.{0, 0}, 128, "Create New World", .{.onAction = gui.openWindowCallback("save_creation")})); + readingSaves: { + var dir = main.files.cubyzDir().openIterableDir("saves") catch |err| { + list.add(Label.init(.{0, 0}, 128, "Encountered error while trying to open saves folder:", .center)); + list.add(Label.init(.{0, 0}, 128, @errorName(err), .center)); + break :readingSaves; + }; + defer dir.close(); + + var iterator = dir.iterate(); + while (iterator.next(main.io) catch |err| { + list.add(Label.init(.{0, 0}, 128, "Encountered error while iterating over saves folder:", .center)); + list.add(Label.init(.{0, 0}, 128, @errorName(err), .center)); + break :readingSaves; + }) |entry| { + if (entry.kind == .directory) { + const worldInfoPath = main.stackAllocator.print("saves/{s}/world.zig.zon", .{entry.name}); + defer main.stackAllocator.free(worldInfoPath); + const worldInfo = main.files.cubyzDir().readToZon(main.stackAllocator, worldInfoPath) catch |err| { + std.log.err("Couldn't open save {s}: {s}", .{worldInfoPath, @errorName(err)}); + continue; + }; + defer worldInfo.deinit(main.stackAllocator); + + worldList.append(main.globalAllocator, .{ + .fileName = main.globalAllocator.dupe(u8, entry.name), + .lastUsedTime = worldInfo.get(i64, "lastUsedTime") orelse 0, + .name = main.globalAllocator.dupe(u8, worldInfo.get([]const u8, "name") orelse entry.name), + }); + } + } + } + + 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, .{.onAction = .initWithInt(openWorldWrap, i)})); + row.add(Button.initIcon(.{4, 0}, .{16, 16}, editIcon, .{.onAction = .initWithInt(editWorld, i)})); + row.add(Button.initIcon(.{4, 0}, .{16, 16}, fileExplorerIcon, .{.onAction = .initWithInt(openFolder, i)})); + row.add(Button.initIcon(.{4, 0}, .{16, 16}, deleteIcon, .{.onAction = .initWithInt(deleteWorld, 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)); + gui.updateWindowPositions(); } 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(); - } + 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(); + } } From 1b8b80a9443dcbc31d02af1cbb251bd757a2f5df Mon Sep 17 00:00:00 2001 From: Abhay Kagdyal Date: Tue, 4 Aug 2026 03:53:58 +0530 Subject: [PATCH 08/14] try to fix memory errors --- src/gui/windows/edit_world.zig | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/gui/windows/edit_world.zig b/src/gui/windows/edit_world.zig index 353ed8c974..1bebdbcf28 100644 --- a/src/gui/windows/edit_world.zig +++ b/src/gui/windows/edit_world.zig @@ -25,6 +25,8 @@ const WorldSettings = struct { testingMode: bool, localPlayerIndex: usize, + isUpated: bool = false, + pub fn init(name: []const u8, seed: i128, defaultgameMode: ?[]const u8, allowCheats: bool, testingMode: bool, localPlayerIndex: usize) WorldSettings { return .{ .name = name, @@ -69,7 +71,7 @@ var gamemodeInput: *Button = undefined; fn nameCallback() void { const newName = nameInput.currentString.items; - _ = newName; + worldSettings.name = newName; } fn gameModeCallback() void { @@ -94,7 +96,7 @@ pub fn init() void { } pub fn deinit() void { - main.stackAllocator.free(editWorldName); + main.globalAllocator.free(editWorldName); } pub fn setEditWorldName(name: []const u8) void { @@ -103,7 +105,6 @@ pub fn setEditWorldName(name: []const u8) void { } pub fn onOpen() void { - std.debug.print("{s}", .{editWorldName}); const worldInfoPath = main.stackAllocator.print("saves/{s}/world.zig.zon", .{editWorldName}); defer main.stackAllocator.free(worldInfoPath); @@ -111,10 +112,8 @@ pub fn onOpen() void { std.log.err("Error while creating new world: {s}", .{@errorName(err)}); return; }; - defer worldInfo.deinit(main.stackAllocator); worldInfoSettings = worldInfo.getChild("settings"); - defer worldInfoSettings.deinit(main.stackAllocator); worldSettings = WorldSettings.init( worldInfo.get([]const u8, "name") orelse "", From 1a168feb86a802e802dc85dadaecccf4f39e77c0 Mon Sep 17 00:00:00 2001 From: Abhay Kagdyal Date: Tue, 4 Aug 2026 13:54:04 +0530 Subject: [PATCH 09/14] memory error while writing to world.zig.zon --- src/gui/windows/edit_world.zig | 36 ++++++++++++++++++++++++++++++---- 1 file changed, 32 insertions(+), 4 deletions(-) diff --git a/src/gui/windows/edit_world.zig b/src/gui/windows/edit_world.zig index 1bebdbcf28..126a2f1633 100644 --- a/src/gui/windows/edit_world.zig +++ b/src/gui/windows/edit_world.zig @@ -18,7 +18,7 @@ const Gamemode = main.game.Gamemode; const DefaultSettings = main.server.world_zig.Settings.defaults; const WorldSettings = struct { - name: []const u8, + name: []const u8 = &.{}, seed: i128, defaultGamemode: Gamemode, allowCheats: bool, @@ -52,6 +52,11 @@ const WorldSettings = struct { self.testingMode, }); } + + pub fn update(self: *WorldSettings, value: bool) void { + self.isUpated = value; + saveChanges.disabled = !value; + } }; pub var window = GuiWindow{ @@ -63,32 +68,54 @@ const padding: f32 = 8; var editWorldName: []const u8 = ""; var worldInfo: ZonElement = undefined; -var worldInfoSettings: ZonElement = undefined; var worldSettings: WorldSettings = undefined; var nameInput: *TextInput = undefined; var gamemodeInput: *Button = undefined; +var saveChanges: *Button = undefined; fn nameCallback() void { const newName = nameInput.currentString.items; worldSettings.name = newName; + worldSettings.update(true); } fn gameModeCallback() void { worldSettings.defaultGamemode = std.enums.fromInt(Gamemode, @intFromEnum(worldSettings.defaultGamemode) + 1) orelse @enumFromInt(0); gamemodeInput.child.label.updateText(@tagName(worldSettings.defaultGamemode)); + worldSettings.update(true); } fn allowCheatsCallback(allow: bool) void { worldSettings.allowCheats = allow; + worldSettings.update(true); } fn testingModeCallback(enabled: bool) void { worldSettings.testingMode = enabled; + worldSettings.update(true); } fn saveChangesCallback() void { worldSettings.print(); + + const path = main.globalAllocator.print("saves/{s}/world.zig.zon", .{editWorldName}); + defer main.globalAllocator.free(path); + + const worldInfoSettings = worldInfo.getChild("settings"); + + worldInfoSettings.put("defaultGamemode", @tagName(worldSettings.defaultGamemode)); + worldInfoSettings.put("allowCheats", worldSettings.allowCheats); + worldInfoSettings.put("testingMode", worldSettings.testingMode); + + worldInfo.put("localPlayer", worldSettings.localPlayerIndex); + worldInfo.put("name", nameInput.currentString.items); + worldInfo.put("settings", worldInfoSettings.string); + main.files.cubyzDir().writeZon(path, worldInfo) catch |err| { + std.log.err("Error while creating new world: {s}", .{@errorName(err)}); + return; + }; + worldSettings.update(false); } pub fn init() void { @@ -112,8 +139,9 @@ pub fn onOpen() void { std.log.err("Error while creating new world: {s}", .{@errorName(err)}); return; }; + defer worldInfo.deinit(main.stackAllocator); - worldInfoSettings = worldInfo.getChild("settings"); + const worldInfoSettings = worldInfo.getChild("settings"); worldSettings = WorldSettings.init( worldInfo.get([]const u8, "name") orelse "", @@ -148,7 +176,7 @@ pub fn onOpen() void { const indexLabel = Label.init(.{0, 0}, 128, "Local Player Indiex", .left); list.add(indexLabel); - const saveChanges = Button.initText(.{0, 0}, 128, "Save Changes", .{.onAction = .init(saveChangesCallback)}); + saveChanges = Button.initText(.{0, 0}, 128, "Save Changes", .{.disabled = !worldSettings.isUpated, .onAction = .init(saveChangesCallback)}); list.add(saveChanges); list.finish(.center); From 4e3439287193ad0bbcdf9de6f37668cbb374b19d Mon Sep 17 00:00:00 2001 From: Abhay Kagdyal Date: Tue, 4 Aug 2026 14:21:28 +0530 Subject: [PATCH 10/14] error when closing edit_world window --- src/gui/windows/edit_world.zig | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/gui/windows/edit_world.zig b/src/gui/windows/edit_world.zig index 126a2f1633..892e90d697 100644 --- a/src/gui/windows/edit_world.zig +++ b/src/gui/windows/edit_world.zig @@ -99,8 +99,8 @@ fn testingModeCallback(enabled: bool) void { fn saveChangesCallback() void { worldSettings.print(); - const path = main.globalAllocator.print("saves/{s}/world.zig.zon", .{editWorldName}); - defer main.globalAllocator.free(path); + const path = main.stackAllocator.print("saves/{s}/world.zig.zon", .{editWorldName}); + defer main.stackAllocator.free(path); const worldInfoSettings = worldInfo.getChild("settings"); @@ -110,7 +110,7 @@ fn saveChangesCallback() void { worldInfo.put("localPlayer", worldSettings.localPlayerIndex); worldInfo.put("name", nameInput.currentString.items); - worldInfo.put("settings", worldInfoSettings.string); + worldInfo.put("settings", worldInfoSettings.clone(main.stackAllocator)); main.files.cubyzDir().writeZon(path, worldInfo) catch |err| { std.log.err("Error while creating new world: {s}", .{@errorName(err)}); return; @@ -135,11 +135,10 @@ pub fn onOpen() void { const worldInfoPath = main.stackAllocator.print("saves/{s}/world.zig.zon", .{editWorldName}); defer main.stackAllocator.free(worldInfoPath); - worldInfo = main.files.cubyzDir().readToZon(main.stackAllocator, worldInfoPath) catch |err| { + worldInfo = main.files.cubyzDir().readToZon(main.globalAllocator, worldInfoPath) catch |err| { std.log.err("Error while creating new world: {s}", .{@errorName(err)}); return; }; - defer worldInfo.deinit(main.stackAllocator); const worldInfoSettings = worldInfo.getChild("settings"); @@ -189,4 +188,5 @@ pub fn onClose() void { if (window.rootComponent) |*comp| { comp.deinit(); } + worldInfo.deinit(main.globalAllocator); } From e463fa82d61ee77f45f9c1ee5933f895761daa79 Mon Sep 17 00:00:00 2001 From: Abhay Kagdyal Date: Tue, 4 Aug 2026 14:46:10 +0530 Subject: [PATCH 11/14] TextInput not functional --- src/gui/windows/edit_world.zig | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/src/gui/windows/edit_world.zig b/src/gui/windows/edit_world.zig index 892e90d697..068570ec59 100644 --- a/src/gui/windows/edit_world.zig +++ b/src/gui/windows/edit_world.zig @@ -1,5 +1,7 @@ const std = @import("std"); +const build_options = @import("build_options"); + const main = @import("main"); const Vec2f = main.vec.Vec2f; const NeverFailingAllocator = main.heap.NeverFailingAllocator; @@ -18,7 +20,6 @@ const Gamemode = main.game.Gamemode; const DefaultSettings = main.server.world_zig.Settings.defaults; const WorldSettings = struct { - name: []const u8 = &.{}, seed: i128, defaultGamemode: Gamemode, allowCheats: bool, @@ -27,9 +28,8 @@ const WorldSettings = struct { isUpated: bool = false, - pub fn init(name: []const u8, seed: i128, defaultgameMode: ?[]const u8, allowCheats: bool, testingMode: bool, localPlayerIndex: usize) WorldSettings { + pub fn init(seed: i128, defaultgameMode: ?[]const u8, allowCheats: bool, testingMode: bool, localPlayerIndex: usize) WorldSettings { return .{ - .name = name, .seed = seed, .defaultGamemode = toEnum(defaultgameMode), .allowCheats = allowCheats, @@ -75,8 +75,6 @@ var gamemodeInput: *Button = undefined; var saveChanges: *Button = undefined; fn nameCallback() void { - const newName = nameInput.currentString.items; - worldSettings.name = newName; worldSettings.update(true); } @@ -106,16 +104,19 @@ fn saveChangesCallback() void { worldInfoSettings.put("defaultGamemode", @tagName(worldSettings.defaultGamemode)); worldInfoSettings.put("allowCheats", worldSettings.allowCheats); - worldInfoSettings.put("testingMode", worldSettings.testingMode); + if (!build_options.isTaggedRelease) { + worldInfoSettings.put("testingMode", worldSettings.testingMode); + } worldInfo.put("localPlayer", worldSettings.localPlayerIndex); worldInfo.put("name", nameInput.currentString.items); - worldInfo.put("settings", worldInfoSettings.clone(main.stackAllocator)); + worldInfo.put("settings", worldInfoSettings.clone(main.globalAllocator)); main.files.cubyzDir().writeZon(path, worldInfo) catch |err| { std.log.err("Error while creating new world: {s}", .{@errorName(err)}); return; }; worldSettings.update(false); + gui.closeWindowFromRef(&window); } pub fn init() void { @@ -143,7 +144,6 @@ pub fn onOpen() void { const worldInfoSettings = worldInfo.getChild("settings"); worldSettings = WorldSettings.init( - worldInfo.get([]const u8, "name") orelse "", worldInfoSettings.get(i128, "seed") orelse 0, worldInfoSettings.get([]const u8, "defaultGamemode"), worldInfoSettings.get(bool, "allowCheats") orelse DefaultSettings.allowCheats, @@ -153,7 +153,7 @@ pub fn onOpen() void { const list = VerticalList.init(.{padding, 16 + padding}, 300, 8); - nameInput = TextInput.init(.{0, 0}, 128, 22, worldSettings.name, .{.onNewline = .init(nameCallback)}); + nameInput = TextInput.init(.{0, 0}, 128, 22, editWorldName, .{.onNewline = .init(saveChangesCallback)}); list.add(nameInput); gamemodeInput = Button.initText(.{0, 0}, 128, @tagName(worldSettings.defaultGamemode), .{.onAction = .init(gameModeCallback)}); @@ -161,7 +161,9 @@ pub fn onOpen() void { list.add(CheckBox.init(.{0, 0}, 128, "Allow Cheats", worldSettings.allowCheats, &allowCheatsCallback)); - list.add(CheckBox.init(.{0, 0}, 128, "Testing Mode", worldSettings.testingMode, &testingModeCallback)); + if (!build_options.isTaggedRelease) { + list.add(CheckBox.init(.{0, 0}, 128, "Testing Mode", worldSettings.testingMode, &testingModeCallback)); + } const seed = main.stackAllocator.print("{d}", .{worldSettings.seed}); defer main.stackAllocator.free(seed); From 63fcdbf1ddf827a8e722d88410d1014674751c92 Mon Sep 17 00:00:00 2001 From: Abhay Kagdyal Date: Wed, 5 Aug 2026 03:39:39 +0530 Subject: [PATCH 12/14] add edit world icon --- assets/cubyz/ui/edit_icon.png | Bin 132 -> 1221 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/assets/cubyz/ui/edit_icon.png b/assets/cubyz/ui/edit_icon.png index c8b10e3ae94e12f8f16b7fcc24dcc374afefeb37..0e62aafa47bc9552f152b4c009d9bdd445c5bd8a 100644 GIT binary patch literal 1221 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`jKx9jPK-BC>eK@{Ea{HEjtmSN z`?>!lvI6-E$sR$z3=CCj3=9n|3=F@3LJcn%7)lKo7+xhXFj&oCU=S~uvn$XBD8X6a z5n0T@pr;JNj1^1m%YcIHC7!;n?5{XjMC4hM*!JfGt&sL~aSX8#WE5a||CZ{7c&PX~fAHkV+y0NLhwtN9ta8dTsGU~>tI0r)hRHQWIk_T@|v z+&dtX;RX<+8RisZmw*i*MKj1HNueJZ&McG%o5x6wW{}Rlxy%fpGy!&jhH?x8dVsa&lr*$ShV+%JI0By|&--xyvQF*cUsj8P2cV_1WGaYrk3d f8I}VN>OQl2xQj4^&tm=uG?u~B)z4*}Q$iB}4ec!m From b63894f3ecad3cfa0a0fe9762b4d6cc9f2d56460 Mon Sep 17 00:00:00 2001 From: Abhay Kagdyal Date: Sat, 8 Aug 2026 18:42:15 +0530 Subject: [PATCH 13/14] remove redundant function --- src/gui/windows/edit_world.zig | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/gui/windows/edit_world.zig b/src/gui/windows/edit_world.zig index 068570ec59..cd0306e0c0 100644 --- a/src/gui/windows/edit_world.zig +++ b/src/gui/windows/edit_world.zig @@ -153,7 +153,9 @@ pub fn onOpen() void { const list = VerticalList.init(.{padding, 16 + padding}, 300, 8); - nameInput = TextInput.init(.{0, 0}, 128, 22, editWorldName, .{.onNewline = .init(saveChangesCallback)}); + list.add(Label.init(.{0, 0}, 128, "Edit World", .center)); + + nameInput = TextInput.init(.{0, 0}, 128, 22, "", .{.onNewline = .init(nameCallback)}); list.add(nameInput); gamemodeInput = Button.initText(.{0, 0}, 128, @tagName(worldSettings.defaultGamemode), .{.onAction = .init(gameModeCallback)}); From 59214e055ee5d0b932d7426590e5fbcbcf778b8b Mon Sep 17 00:00:00 2001 From: Abhay Kagdyal Date: Sat, 8 Aug 2026 18:57:57 +0530 Subject: [PATCH 14/14] fix github build errors --- src/gui/windows/edit_world.zig | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/gui/windows/edit_world.zig b/src/gui/windows/edit_world.zig index cd0306e0c0..ddf7a7bdb8 100644 --- a/src/gui/windows/edit_world.zig +++ b/src/gui/windows/edit_world.zig @@ -17,7 +17,7 @@ const VerticalList = @import("../components/VerticalList.zig"); const CheckBox = @import("../components/CheckBox.zig"); const Gamemode = main.game.Gamemode; -const DefaultSettings = main.server.world_zig.Settings.defaults; +const defaults = main.server.world_zig.Settings.defaults; const WorldSettings = struct { seed: i128, @@ -39,7 +39,7 @@ const WorldSettings = struct { } fn toEnum(gamemode: ?[]const u8) Gamemode { - const mode = gamemode orelse @tagName(DefaultSettings.defaultGamemode); + const mode = gamemode orelse @tagName(defaults.defaultGamemode); return std.meta.stringToEnum(Gamemode, mode).?; } @@ -146,8 +146,8 @@ pub fn onOpen() void { worldSettings = WorldSettings.init( worldInfoSettings.get(i128, "seed") orelse 0, worldInfoSettings.get([]const u8, "defaultGamemode"), - worldInfoSettings.get(bool, "allowCheats") orelse DefaultSettings.allowCheats, - worldInfoSettings.get(bool, "testingMode") orelse DefaultSettings.testingMode, + worldInfoSettings.get(bool, "allowCheats") orelse defaults.allowCheats, + worldInfoSettings.get(bool, "testingMode") orelse defaults.testingMode, worldInfo.get(usize, "localPlayer") orelse 0, );