mirror of
https://github.com/PixelGuys/Cubyz.git
synced 2025-08-02 18:57:10 -04:00

For reference: https://github.com/ziglang/zig/pull/24329 some commits have been extracted from #1583, but the x86_64 backend has been disabled due to its horrible performance. Remaining work: - [x] Wait for official builds on ziglang.org and upload them to our repository - [x] Add workaround for https://github.com/ziglang/zig/pull/24466 - [x] Fix TODO comment about ANSI support in stdout - [x] Check for compile-time performance changes → it went from 13.1 to 11.9 seconds 🎉
174 lines
6.1 KiB
Zig
174 lines
6.1 KiB
Zig
const std = @import("std");
|
|
|
|
const main = @import("main");
|
|
const ConnectionManager = main.network.ConnectionManager;
|
|
const settings = main.settings;
|
|
const Vec2f = main.vec.Vec2f;
|
|
const NeverFailingAllocator = main.heap.NeverFailingAllocator;
|
|
const Texture = main.graphics.Texture;
|
|
|
|
const gui = @import("../gui.zig");
|
|
const GuiComponent = gui.GuiComponent;
|
|
const GuiWindow = gui.GuiWindow;
|
|
const Button = @import("../components/Button.zig");
|
|
const HorizontalList = @import("../components/HorizontalList.zig");
|
|
const Label = @import("../components/Label.zig");
|
|
const TextInput = @import("../components/TextInput.zig");
|
|
const VerticalList = @import("../components/VerticalList.zig");
|
|
|
|
pub var window = GuiWindow{
|
|
.contentSize = Vec2f{128, 256},
|
|
};
|
|
|
|
const padding: f32 = 8;
|
|
const width: f32 = 128;
|
|
var buttonNameArena: main.heap.NeverFailingArenaAllocator = undefined;
|
|
|
|
pub var needsUpdate: bool = false;
|
|
|
|
var deleteIcon: Texture = undefined;
|
|
var fileExplorerIcon: Texture = undefined;
|
|
|
|
const WorldInfo = struct {
|
|
lastUsedTime: i64,
|
|
name: []const u8,
|
|
fileName: []const u8,
|
|
};
|
|
var worldList: main.ListUnmanaged(WorldInfo) = .{};
|
|
|
|
pub fn init() void {
|
|
deleteIcon = Texture.initFromFile("assets/cubyz/ui/delete_icon.png");
|
|
fileExplorerIcon = Texture.initFromFile("assets/cubyz/ui/file_explorer_icon.png");
|
|
}
|
|
|
|
pub fn deinit() void {
|
|
deleteIcon.deinit();
|
|
fileExplorerIcon.deinit();
|
|
}
|
|
|
|
pub fn openWorld(name: []const u8) void {
|
|
const clientConnection = ConnectionManager.init(0, false) 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.start, .{name, clientConnection.localPort}) catch |err| {
|
|
std.log.err("Encountered error while starting server thread: {s}", .{@errorName(err)});
|
|
return;
|
|
};
|
|
main.server.thread.?.setName("Server") catch |err| {
|
|
std.log.err("Failed to rename Server thread: {s}", .{@errorName(err)});
|
|
};
|
|
|
|
while(!main.server.running.load(.acquire)) {
|
|
std.Thread.sleep(1_000_000);
|
|
}
|
|
clientConnection.world = &main.game.testWorld;
|
|
const ipPort = std.fmt.allocPrint(main.stackAllocator.allocator, "127.0.0.1:{}", .{main.server.connectionManager.localPort}) catch unreachable;
|
|
defer main.stackAllocator.free(ipPort);
|
|
main.game.world = &main.game.testWorld;
|
|
main.game.testWorld.init(ipPort, clientConnection) catch |err| {
|
|
std.log.err("Encountered error while opening world: {s}", .{@errorName(err)});
|
|
};
|
|
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);
|
|
}
|
|
|
|
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");
|
|
}
|
|
|
|
fn openFolder(index: usize) void {
|
|
const path = std.fmt.allocPrint(main.stackAllocator.allocator, "saves/{s}", .{worldList.items[index].fileName}) catch unreachable;
|
|
defer main.stackAllocator.free(path);
|
|
|
|
main.files.openDirInWindow(path);
|
|
}
|
|
|
|
pub fn update() void {
|
|
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, "**Select World**", .center));
|
|
list.add(Button.initText(.{0, 0}, 128, "Create New World", gui.openWindowCallback("save_creation")));
|
|
readingSaves: {
|
|
var dir = std.fs.cwd().makeOpenPath("saves", .{.iterate = true}) 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() 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 = std.fmt.allocPrint(main.stackAllocator.allocator, "saves/{s}/world.zig.zon", .{entry.name}) catch unreachable;
|
|
defer main.stackAllocator.free(worldInfoPath);
|
|
const worldInfo = main.files.readToZon(main.stackAllocator, worldInfoPath) catch |err| {
|
|
std.log.err("Couldn't open save {s}: {s}", .{worldInfoPath, @errorName(err)});
|
|
continue;
|
|
};
|
|
defer worldInfo.deinit(main.stackAllocator);
|
|
|
|
worldList.append(main.globalAllocator, .{
|
|
.fileName = main.globalAllocator.dupe(u8, entry.name),
|
|
.lastUsedTime = worldInfo.get(i64, "lastUsedTime", 0),
|
|
.name = main.globalAllocator.dupe(u8, worldInfo.get([]const u8, "name", 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, .{.callback = &openWorldWrap, .arg = i}));
|
|
row.add(Button.initIcon(.{8, 0}, .{16, 16}, fileExplorerIcon, false, .{.callback = &openFolder, .arg = i}));
|
|
row.add(Button.initIcon(.{8, 0}, .{16, 16}, deleteIcon, false, .{.callback = &deleteWorld, .arg = i}));
|
|
row.finish(.{0, 0}, .center);
|
|
list.add(row);
|
|
}
|
|
|
|
list.finish(.center);
|
|
window.rootComponent = list.toComponent();
|
|
window.contentSize = window.rootComponent.?.pos() + window.rootComponent.?.size() + @as(Vec2f, @splat(padding));
|
|
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();
|
|
}
|
|
}
|