From 77dd634b56fb467ffbb7dc4aff6aab6f88b0f4d5 Mon Sep 17 00:00:00 2001 From: IntegratedQuantum Date: Tue, 14 Oct 2025 23:57:30 +0200 Subject: [PATCH 01/20] list printing and other small stuff --- src/files.zig | 2 +- src/items.zig | 4 +-- src/models.zig | 6 ++-- src/tool/modifiers/bad_at.zig | 2 +- src/tool/modifiers/durable.zig | 2 +- src/tool/modifiers/fragile.zig | 2 +- src/tool/modifiers/good_at.zig | 2 +- src/tool/modifiers/heavy.zig | 2 +- src/tool/modifiers/light.zig | 2 +- src/tool/modifiers/powerful.zig | 2 +- src/tool/modifiers/restrictions/encased.zig | 2 +- src/tool/modifiers/single_use.zig | 2 +- src/tool/modifiers/weak.zig | 2 +- src/utils/heap.zig | 4 +-- src/utils/list.zig | 33 ++++----------------- src/zon.zig | 4 +-- 16 files changed, 25 insertions(+), 48 deletions(-) diff --git a/src/files.zig b/src/files.zig index 0075d8bd51..1c88bf35ac 100644 --- a/src/files.zig +++ b/src/files.zig @@ -99,7 +99,7 @@ pub const Dir = struct { } pub fn read(self: Dir, allocator: NeverFailingAllocator, path: []const u8) ![]u8 { - return self.dir.readFileAlloc(allocator.allocator, path, std.math.maxInt(usize)); + return self.dir.readFileAlloc(path, allocator.allocator, .unlimited); } pub fn readToZon(self: Dir, allocator: NeverFailingAllocator, path: []const u8) !ZonElement { diff --git a/src/items.zig b/src/items.zig index f18e264b4f..68378e3c40 100644 --- a/src/items.zig +++ b/src/items.zig @@ -813,7 +813,7 @@ pub const Tool = struct { // MARK: Tool fn getTooltip(self: *Tool) []const u8 { self.tooltip.clearRetainingCapacity(); - self.tooltip.writer().print( + self.tooltip.print( \\{s} \\{d:.2} swings/s \\Damage: {d:.2} @@ -824,7 +824,7 @@ pub const Tool = struct { // MARK: Tool self.damage, self.durability, std.math.lossyCast(u32, self.maxDurability), - }) catch unreachable; + }); if(self.modifiers.len != 0) { self.tooltip.appendSlice("\nModifiers:\n"); for(self.modifiers) |modifier| { diff --git a/src/models.zig b/src/models.zig index bb59246a6b..f374a366b8 100644 --- a/src/models.zig +++ b/src/models.zig @@ -479,7 +479,7 @@ pub const Model = struct { if(std.mem.eql(u8, line[0..2], "v ")) { var coordsIter = std.mem.splitScalar(u8, line[2..], ' '); - var coords: Vec3f = undefined; + var coords: [3]f32 = undefined; var i: usize = 0; while(coordsIter.next()) |coord| : (i += 1) { coords[i] = std.fmt.parseFloat(f32, coord) catch |e| blk: { @@ -490,7 +490,7 @@ pub const Model = struct { vertices.append(coords); } else if(std.mem.eql(u8, line[0..3], "vn ")) { var coordsIter = std.mem.splitScalar(u8, line[3..], ' '); - var norm: Vec3f = undefined; + var norm: [3]f32 = undefined; var i: usize = 0; while(coordsIter.next()) |coord| : (i += 1) { norm[i] = std.fmt.parseFloat(f32, coord) catch |e| blk: { @@ -501,7 +501,7 @@ pub const Model = struct { normals.append(norm); } else if(std.mem.eql(u8, line[0..3], "vt ")) { var coordsIter = std.mem.splitScalar(u8, line[3..], ' '); - var uv: Vec2f = undefined; + var uv: [2]f32 = undefined; var i: usize = 0; while(coordsIter.next()) |coord| : (i += 1) { uv[i] = std.fmt.parseFloat(f32, coord) catch |e| blk: { diff --git a/src/tool/modifiers/bad_at.zig b/src/tool/modifiers/bad_at.zig index 7262ebe399..9d875e0a7c 100644 --- a/src/tool/modifiers/bad_at.zig +++ b/src/tool/modifiers/bad_at.zig @@ -26,5 +26,5 @@ pub fn changeBlockDamage(damage: f32, block: main.blocks.Block, data: Data) f32 } pub fn printTooltip(outString: *main.List(u8), data: Data) void { - outString.writer().print("#a00050**Bad at**#808080 *Decreases damage by **{d:.0}%** on \n***#a00050{s}#808080*** blocks", .{data.strength*100, data.tag.getName()}) catch unreachable; + outString.print("#a00050**Bad at**#808080 *Decreases damage by **{d:.0}%** on \n***#a00050{s}#808080*** blocks", .{data.strength*100, data.tag.getName()}); } diff --git a/src/tool/modifiers/durable.zig b/src/tool/modifiers/durable.zig index bd2cab940c..fe48e1b33b 100644 --- a/src/tool/modifiers/durable.zig +++ b/src/tool/modifiers/durable.zig @@ -24,5 +24,5 @@ pub fn changeBlockDamage(damage: f32, _: main.blocks.Block, _: Data) f32 { } pub fn printTooltip(outString: *main.List(u8), data: Data) void { - outString.writer().print("#500090**Durable**#808080 *Increases durability by **{d:.0}%", .{data.strength*100}) catch unreachable; + outString.print("#500090**Durable**#808080 *Increases durability by **{d:.0}%", .{data.strength*100}); } diff --git a/src/tool/modifiers/fragile.zig b/src/tool/modifiers/fragile.zig index 38f4c89108..6c86437b05 100644 --- a/src/tool/modifiers/fragile.zig +++ b/src/tool/modifiers/fragile.zig @@ -24,5 +24,5 @@ pub fn changeBlockDamage(damage: f32, _: main.blocks.Block, _: Data) f32 { } pub fn printTooltip(outString: *main.List(u8), data: Data) void { - outString.writer().print("#ccddff**Fragile**#808080 *Decreases durability by **{d:.0}%", .{data.strength*100}) catch unreachable; + outString.print("#ccddff**Fragile**#808080 *Decreases durability by **{d:.0}%", .{data.strength*100}); } diff --git a/src/tool/modifiers/good_at.zig b/src/tool/modifiers/good_at.zig index f1e9f96faa..9319378e55 100644 --- a/src/tool/modifiers/good_at.zig +++ b/src/tool/modifiers/good_at.zig @@ -26,5 +26,5 @@ pub fn changeBlockDamage(damage: f32, block: main.blocks.Block, data: Data) f32 } pub fn printTooltip(outString: *main.List(u8), data: Data) void { - outString.writer().print("#80ff40**Good at**#808080 *Increases damage by **{d:.0}%** on \n***#80ff40{s}#808080*** blocks", .{data.strength*100, data.tag.getName()}) catch unreachable; + outString.print("#80ff40**Good at**#808080 *Increases damage by **{d:.0}%** on \n***#80ff40{s}#808080*** blocks", .{data.strength*100, data.tag.getName()}); } diff --git a/src/tool/modifiers/heavy.zig b/src/tool/modifiers/heavy.zig index 51ff81575b..1fc0b411ae 100644 --- a/src/tool/modifiers/heavy.zig +++ b/src/tool/modifiers/heavy.zig @@ -24,5 +24,5 @@ pub fn changeBlockDamage(damage: f32, _: main.blocks.Block, _: Data) f32 { } pub fn printTooltip(outString: *main.List(u8), data: Data) void { - outString.writer().print("#ffcc30**Heavy**#808080 *Increases swing time by **{d:.0}%", .{data.strength*100}) catch unreachable; + outString.print("#ffcc30**Heavy**#808080 *Increases swing time by **{d:.0}%", .{data.strength*100}); } diff --git a/src/tool/modifiers/light.zig b/src/tool/modifiers/light.zig index b0e12c1bfb..c60209ca74 100644 --- a/src/tool/modifiers/light.zig +++ b/src/tool/modifiers/light.zig @@ -24,5 +24,5 @@ pub fn changeBlockDamage(damage: f32, _: main.blocks.Block, _: Data) f32 { } pub fn printTooltip(outString: *main.List(u8), data: Data) void { - outString.writer().print("#9fffde**Light**#808080 *Decreases swing time by **{d:.0}%", .{data.strength*100}) catch unreachable; + outString.print("#9fffde**Light**#808080 *Decreases swing time by **{d:.0}%", .{data.strength*100}); } diff --git a/src/tool/modifiers/powerful.zig b/src/tool/modifiers/powerful.zig index b5ea676528..a95e74ca92 100644 --- a/src/tool/modifiers/powerful.zig +++ b/src/tool/modifiers/powerful.zig @@ -24,5 +24,5 @@ pub fn changeBlockDamage(damage: f32, _: main.blocks.Block, _: Data) f32 { } pub fn printTooltip(outString: *main.List(u8), data: Data) void { - outString.writer().print("#f84a00**Powerful**#808080 *Increases damage by **{d:.0}%", .{data.strength*100}) catch unreachable; + outString.print("#f84a00**Powerful**#808080 *Increases damage by **{d:.0}%", .{data.strength*100}); } diff --git a/src/tool/modifiers/restrictions/encased.zig b/src/tool/modifiers/restrictions/encased.zig index 7dffff8900..a5ef966dbd 100644 --- a/src/tool/modifiers/restrictions/encased.zig +++ b/src/tool/modifiers/restrictions/encased.zig @@ -31,5 +31,5 @@ pub fn loadFromZon(allocator: NeverFailingAllocator, zon: ZonElement) *const Enc } pub fn printTooltip(self: *const Encased, outString: *main.List(u8)) void { - outString.writer().print("encased in {} .{s}", .{self.amount, self.tag.getName()}) catch unreachable; + outString.print("encased in {} .{s}", .{self.amount, self.tag.getName()}); } diff --git a/src/tool/modifiers/single_use.zig b/src/tool/modifiers/single_use.zig index ec66b86e72..2b3c9a7c22 100644 --- a/src/tool/modifiers/single_use.zig +++ b/src/tool/modifiers/single_use.zig @@ -24,5 +24,5 @@ pub fn changeBlockDamage(damage: f32, _: main.blocks.Block, _: Data) f32 { } pub fn printTooltip(outString: *main.List(u8), data: Data) void { - outString.writer().print("#800000**Single-use**#808080 *Sets durability to **{d:.0}", .{data.strength}) catch unreachable; + outString.print("#800000**Single-use**#808080 *Sets durability to **{d:.0}", .{data.strength}); } diff --git a/src/tool/modifiers/weak.zig b/src/tool/modifiers/weak.zig index 2cb11033ea..bde5a39000 100644 --- a/src/tool/modifiers/weak.zig +++ b/src/tool/modifiers/weak.zig @@ -24,5 +24,5 @@ pub fn changeBlockDamage(damage: f32, _: main.blocks.Block, _: Data) f32 { } pub fn printTooltip(outString: *main.List(u8), data: Data) void { - outString.writer().print("#fcb5e3**Weak**#808080 *Decreases damage by **{d:.0}%", .{data.strength*100}) catch unreachable; + outString.print("#fcb5e3**Weak**#808080 *Decreases damage by **{d:.0}%", .{data.strength*100}); } diff --git a/src/utils/heap.zig b/src/utils/heap.zig index da41177243..e65e23707a 100644 --- a/src/utils/heap.zig +++ b/src/utils/heap.zig @@ -748,7 +748,7 @@ pub const GarbageCollection = struct { // MARK: GarbageCollection if(newTime -% lastSyncPointTime > 20_000) { if(!build_options.isTaggedRelease) { std.log.err("No sync point executed in {} ms for thread. Did you forget to add a sync point in the thread's main loop?", .{newTime -% lastSyncPointTime}); - std.debug.dumpCurrentStackTrace(null); + std.debug.dumpCurrentStackTrace(.{}); } } for(&lists) |*list| { @@ -784,7 +784,7 @@ pub const GarbageCollection = struct { // MARK: GarbageCollection const newTime = std.time.milliTimestamp(); if(newTime -% lastSyncPointTime > 20_000) { std.log.err("No sync point executed in {} ms. Did you forget to add a sync point in the thread's main loop", .{newTime -% lastSyncPointTime}); - std.debug.dumpCurrentStackTrace(null); + std.debug.dumpCurrentStackTrace(.{}); } lastSyncPointTime = newTime; diff --git a/src/utils/list.zig b/src/utils/list.zig index 2b34ba8342..2d8e507e15 100644 --- a/src/utils/list.zig +++ b/src/utils/list.zig @@ -209,19 +209,11 @@ pub fn List(comptime T: type) type { } } - pub const Writer = if(T != u8) - @compileError("The Writer interface is only defined for ArrayList(u8) " ++ - "but the given type is ArrayList(" ++ @typeName(T) ++ ")") - else - std.Io.GenericWriter(*@This(), error{}, appendWrite); - - pub fn writer(self: *@This()) Writer { - return .{.context = self}; - } - - fn appendWrite(self: *@This(), m: []const u8) !usize { - self.appendSlice(m); - return m.len; + pub fn print(self: *@This(), comptime fmt: []const u8, args: anytype) void { + var writer = std.Io.Writer.Allocating.init(main.stackAllocator.allocator); // TODO: Is there no easier way to make this without an extra copy? + defer writer.deinit(); + writer.writer.print(fmt, args) catch unreachable; + self.appendSlice(writer.written()); } }; } @@ -414,21 +406,6 @@ pub fn ListUnmanaged(comptime T: type) type { self.items.len -= len - new_items.len; } } - - pub const Writer = if(T != u8) - @compileError("The Writer interface is only defined for ArrayList(u8) " ++ - "but the given type is ArrayList(" ++ @typeName(T) ++ ")") - else - std.io.Writer(*@This(), error{}, appendWrite); - - pub fn writer(self: *@This()) Writer { - return .{.context = self}; - } - - fn appendWrite(self: *@This(), m: []const u8) !usize { - self.appendSlice(m); - return m.len; - } }; } diff --git a/src/zon.zig b/src/zon.zig index b95116d6ff..3531d62803 100644 --- a/src/zon.zig +++ b/src/zon.zig @@ -353,10 +353,10 @@ pub const ZonElement = union(enum) { // MARK: Zon fn recurseToString(zon: ZonElement, list: *List(u8), tabs: u32, comptime visualCharacters: bool) void { switch(zon) { .int => |value| { - list.writer().print("{d}", .{value}) catch unreachable; + list.print("{d}", .{value}); }, .float => |value| { - list.writer().print("{e}", .{value}) catch unreachable; + list.print("{e}", .{value}); }, .bool => |value| { if(value) { From 562324fd3a918adbe213c0cc2cccd20a4ed8a4cc Mon Sep 17 00:00:00 2001 From: IntegratedQuantum Date: Tue, 14 Oct 2025 23:57:55 +0200 Subject: [PATCH 02/20] Compression, but someone squashed all my mistakes - Compression, but it doesn't work - Use a fixed-size writer - Revert "Use a fixed-size writer" - Actually fix the allocating writer --- src/network/protocols.zig | 10 +++---- src/server/terrain/SurfaceMap.zig | 2 +- src/utils.zig | 48 +++++++++++++++++-------------- 3 files changed, 32 insertions(+), 28 deletions(-) diff --git a/src/network/protocols.zig b/src/network/protocols.zig index 4387d5ec58..ec6755a34a 100644 --- a/src/network/protocols.zig +++ b/src/network/protocols.zig @@ -174,11 +174,11 @@ pub const handShake = struct { // MARK: handShake defer main.stackAllocator.free(path); var dir = try main.files.cubyzDir().openIterableDir(path); defer dir.close(); - var arrayList = main.List(u8).init(main.stackAllocator); - defer arrayList.deinit(); - arrayList.append(@intFromEnum(Connection.HandShakeState.assets)); - try utils.Compression.pack(dir, arrayList.writer()); - conn.send(.fast, id, arrayList.items); + var writer = try std.Io.Writer.Allocating.initCapacity(main.stackAllocator.allocator, 16); + defer writer.deinit(); + try writer.writer.writeByte(@intFromEnum(Connection.HandShakeState.assets)); + try utils.Compression.pack(dir, &writer.writer); + conn.send(.fast, id, writer.written()); } conn.user.?.initPlayer(name); diff --git a/src/server/terrain/SurfaceMap.zig b/src/server/terrain/SurfaceMap.zig index 1896a90c3e..bd71ebeba0 100644 --- a/src/server/terrain/SurfaceMap.zig +++ b/src/server/terrain/SurfaceMap.zig @@ -188,7 +188,7 @@ pub const MapFragment = struct { // MARK: MapFragment for(0..mapSize) |x| for(0..mapSize) |y| writer.writeInt(i32, self.heightMap[x][y]); for(0..mapSize) |x| for(0..mapSize) |y| writer.writeInt(i32, (if(originalData) |map| map else &self.heightMap)[x][y]); - const compressedData = main.utils.Compression.deflate(main.stackAllocator, writer.data.items, .fast); + const compressedData = main.utils.Compression.deflate(main.stackAllocator, writer.data.items, .default); defer main.stackAllocator.free(compressedData); var outputWriter = BinaryWriter.initCapacity(main.stackAllocator, @sizeOf(StorageHeader) + compressedData.len); diff --git a/src/utils.zig b/src/utils.zig index 1c9fdd1be9..10fe95f9a3 100644 --- a/src/utils.zig +++ b/src/utils.zig @@ -10,24 +10,26 @@ pub const file_monitor = @import("utils/file_monitor.zig"); pub const VirtualList = @import("utils/virtual_mem.zig").VirtualList; pub const Compression = struct { // MARK: Compression - pub fn deflate(allocator: NeverFailingAllocator, data: []const u8, level: std.compress.flate.deflate.Level) []u8 { - var result = main.List(u8).init(allocator); - var comp = std.compress.flate.compressor(result.writer(), .{.level = level}) catch unreachable; - _ = comp.write(data) catch unreachable; - comp.finish() catch unreachable; - return result.toOwnedSlice(); + pub fn deflate(allocator: NeverFailingAllocator, data: []const u8, level: std.compress.flate.Compress.Options) []u8 { + var result = std.Io.Writer.Allocating.initCapacity(allocator.allocator, 16) catch unreachable; + var buffer: [65536]u8 = undefined; + var compress = std.compress.flate.Compress.init(&result.writer, &buffer, .raw, level) catch unreachable; + compress.writer.writeAll(data) catch unreachable; + compress.writer.flush() catch unreachable; + result.writer.flush() catch unreachable; + return result.toOwnedSlice() catch unreachable; } pub fn inflateTo(buf: []u8, data: []const u8) !usize { - var streamIn = std.Io.fixedBufferStream(data); - var decomp = std.compress.flate.decompressor(streamIn.reader()); - var streamOut = std.Io.fixedBufferStream(buf); - try decomp.decompress(streamOut.writer()); - return streamOut.getWritten().len; + var reader = std.Io.Reader.fixed(data); + var buffer: [65536]u8 = undefined; + var decompressor = std.compress.flate.Decompress.init(&reader, .raw, &buffer); + return try decompressor.reader.readSliceShort(buf); } - pub fn pack(sourceDir: main.files.Dir, writer: anytype) !void { - var comp = try std.compress.flate.compressor(writer, .{}); + pub fn pack(sourceDir: main.files.Dir, writer: *std.Io.Writer) !void { + var buffer: [65536]u8 = undefined; + var comp = try std.compress.flate.Compress.init(writer, &buffer, .raw, .default); var walker = sourceDir.walk(main.stackAllocator); defer walker.deinit(); @@ -44,25 +46,27 @@ pub const Compression = struct { // MARK: Compression }; var len: [4]u8 = undefined; std.mem.writeInt(u32, &len, @as(u32, @intCast(relPath.len)), endian); - _ = try comp.write(&len); - _ = try comp.write(relPath); + _ = try comp.writer.write(&len); + _ = try comp.writer.write(relPath); const fileData = try sourceDir.read(main.stackAllocator, relPath); defer main.stackAllocator.free(fileData); std.mem.writeInt(u32, &len, @as(u32, @intCast(fileData.len)), endian); - _ = try comp.write(&len); - _ = try comp.write(fileData); + _ = try comp.writer.write(&len); + _ = try comp.writer.write(fileData); } } - try comp.finish(); + try comp.writer.flush(); + try writer.flush(); } pub fn unpack(outDir: main.files.Dir, input: []const u8) !void { - var stream = std.io.fixedBufferStream(input); - var decomp = std.compress.flate.decompressor(stream.reader()); - const reader = decomp.reader(); - const _data = try reader.readAllAlloc(main.stackAllocator.allocator, std.math.maxInt(usize)); + var inputReader = std.Io.Reader.fixed(input); + var buffer: [65536]u8 = undefined; + var decompressor = std.compress.flate.Decompress.init(&inputReader, .raw, &buffer); + const reader = &decompressor.reader; + const _data = try reader.allocRemainingAlignedSentinel(main.stackAllocator.allocator, .unlimited, .@"1", null); defer main.stackAllocator.free(_data); var data = _data; while(data.len != 0) { From f9b483482f20b2e3a01cc805e4270aa142a45828 Mon Sep 17 00:00:00 2001 From: IntegratedQuantum Date: Fri, 21 Nov 2025 19:11:04 +0100 Subject: [PATCH 03/20] The scary Io update --- src/audio.zig | 2 -- src/entity.zig | 2 +- src/game.zig | 26 ++++++++--------- src/graphics.zig | 4 +-- src/gui/components/TextInput.zig | 12 ++++---- src/gui/gui.zig | 16 +++++----- src/gui/windows/advanced_controls.zig | 12 ++++---- src/gui/windows/chat.zig | 4 +-- src/gui/windows/save_selection.zig | 2 +- src/itemdrop.zig | 4 +-- src/main.zig | 34 +++++++++++++--------- src/network.zig | 6 ++-- src/network/protocols.zig | 2 +- src/particles.zig | 26 +++++++---------- src/renderer.zig | 18 +++++------- src/renderer/mesh_storage.zig | 6 ++-- src/server/server.zig | 24 +++++++-------- src/server/storage.zig | 2 +- src/server/terrain/terrain.zig | 4 +-- src/server/world.zig | 38 ++++++++++++------------ src/settings.zig | 42 +++++++++++++++++---------- src/utils.zig | 26 ++++++++--------- src/utils/heap.zig | 20 ++++++------- 23 files changed, 170 insertions(+), 162 deletions(-) diff --git a/src/audio.zig b/src/audio.zig index e8ed01e4b5..344b8650b1 100644 --- a/src/audio.zig +++ b/src/audio.zig @@ -192,7 +192,6 @@ pub fn init() error{miniaudioError}!void { try handleError(c.ma_device_start(&device)); sampleRate = 44100; - lastTime = std.time.milliTimestamp(); } pub fn deinit() void { @@ -239,7 +238,6 @@ const currentMusic = struct { }; var activeMusicId: []const u8 = &.{}; -var lastTime: i64 = 0; var partialFrame: f32 = 0; const animationLengthInSeconds = 5.0; diff --git a/src/entity.zig b/src/entity.zig index 64b3b92940..04885229cc 100644 --- a/src/entity.zig +++ b/src/entity.zig @@ -141,7 +141,7 @@ pub const ClientEntityManager = struct { fn update() void { main.utils.assertLocked(&mutex); - var time: i16 = @truncate(std.time.milliTimestamp() -% settings.entityLookback); + var time: i16 = @truncate(main.timestamp().toMilliseconds() -% settings.entityLookback); time -%= timeDifference.difference.load(.monotonic); for(entities.items()) |*ent| { ent.update(time, lastTime); diff --git a/src/game.zig b/src/game.zig index 4bd87c6270..39ee914bef 100644 --- a/src/game.zig +++ b/src/game.zig @@ -632,7 +632,7 @@ pub const World = struct { // MARK: World .conn = try Connection.init(manager, ip, null), .manager = manager, .name = "client", - .milliTime = std.time.milliTimestamp(), + .milliTime = main.timestamp().toMilliseconds(), }; errdefer self.conn.deinit(); @@ -737,7 +737,7 @@ pub const World = struct { // MARK: World } pub fn update(self: *World) void { - const newTime: i64 = std.time.milliTimestamp(); + const newTime: i64 = main.timestamp().toMilliseconds(); while(self.milliTime +% 100 -% newTime < 0) { self.milliTime +%= 100; var curTime = self.gameTime.load(.monotonic); @@ -765,12 +765,12 @@ pub var projectionMatrix: Mat4f = Mat4f.identity(); var biomeFog = Fog{.skyColor = .{0.8, 0.8, 1}, .fogColor = .{0.8, 0.8, 1}, .density = 1.0/15.0/128.0, .fogLower = 100, .fogHigher = 1000}; pub var fog = Fog{.skyColor = .{0.8, 0.8, 1}, .fogColor = .{0.8, 0.8, 1}, .density = 1.0/15.0/128.0, .fogLower = 100, .fogHigher = 1000}; -var nextBlockPlaceTime: ?i64 = null; -var nextBlockBreakTime: ?i64 = null; +var nextBlockPlaceTime: ?std.Io.Timestamp = null; +var nextBlockBreakTime: ?std.Io.Timestamp = null; pub fn pressPlace(mods: main.Window.Key.Modifiers) void { - const time = std.time.milliTimestamp(); - nextBlockPlaceTime = time + main.settings.updateRepeatDelay; + const time = main.timestamp(); + nextBlockPlaceTime = time.addDuration(main.settings.updateRepeatDelay); Player.placeBlock(mods); } @@ -779,8 +779,8 @@ pub fn releasePlace(_: main.Window.Key.Modifiers) void { } pub fn pressBreak(_: main.Window.Key.Modifiers) void { - const time = std.time.milliTimestamp(); - nextBlockBreakTime = time + main.settings.updateRepeatDelay; + const time = main.timestamp(); + nextBlockBreakTime = time.addDuration(main.settings.updateRepeatDelay); Player.breakBlock(0); } @@ -972,16 +972,16 @@ pub fn update(deltaTime: f64) void { // MARK: update() physics.update(deltaTime, acc, jumping); - const time = std.time.milliTimestamp(); + const time = main.timestamp(); if(nextBlockPlaceTime) |*placeTime| { - if(time -% placeTime.* >= 0) { - placeTime.* += main.settings.updateRepeatSpeed; + if(placeTime.durationTo(time).nanoseconds >= 0) { + placeTime.* = placeTime.addDuration(main.settings.updateRepeatSpeed); Player.placeBlock(main.KeyBoard.key("placeBlock").modsOnPress); } } if(nextBlockBreakTime) |*breakTime| { - if(time -% breakTime.* >= 0 or !Player.isCreative()) { - breakTime.* += main.settings.updateRepeatSpeed; + if(breakTime.durationTo(time).nanoseconds >= 0 or !Player.isCreative()) { + breakTime.* = breakTime.addDuration(main.settings.updateRepeatSpeed); Player.breakBlock(deltaTime); } } diff --git a/src/graphics.zig b/src/graphics.zig index 1f83f95bbe..cf4c5ce24c 100644 --- a/src/graphics.zig +++ b/src/graphics.zig @@ -1930,10 +1930,10 @@ pub fn LargeBuffer(comptime Entry: type) type { // MARK: LargerBuffer pub fn beginRender(self: *Self) void { self.activeFence += 1; if(self.activeFence == self.fences.len) self.activeFence = 0; - const startTime = std.time.milliTimestamp(); + const endTime = main.timestamp().addDuration(.fromMilliseconds(5)); while(self.fencedFreeLists[self.activeFence].popOrNull()) |allocation| { self.finalFree(allocation); - if(std.time.milliTimestamp() -% startTime > 5) break; // TODO: Remove after #1434 + if(main.timestamp().durationTo(endTime).nanoseconds > 0) break; // TODO: Remove after #1434 } _ = c.glClientWaitSync(self.fences[self.activeFence], 0, c.GL_TIMEOUT_IGNORED); // Make sure the render calls that accessed these parts of the buffer have finished. } diff --git a/src/gui/components/TextInput.zig b/src/gui/components/TextInput.zig index 1032234bbb..6d772eac56 100644 --- a/src/gui/components/TextInput.zig +++ b/src/gui/components/TextInput.zig @@ -18,7 +18,7 @@ const TextInput = @This(); const scrollBarWidth = 5; const border: f32 = 3; const fontSize: f32 = 16; -const blinkDurationMs: i64 = 500; +const blinkDuration: std.Io.Duration = .fromMilliseconds(500); var texture: Texture = undefined; @@ -35,7 +35,7 @@ textSize: Vec2f = undefined, scrollBar: *ScrollBar, onNewline: gui.Callback, optional: OptionalCallbacks, -lastBlinkTime: i64 = 0, +lastBlinkTime: std.Io.Timestamp = .fromNanoseconds(0), showCusor: bool = true, pub fn __init() void { @@ -477,7 +477,7 @@ pub fn newline(self: *TextInput, mods: main.Window.Key.Modifiers) void { fn ensureCursorVisibility(self: *TextInput) void { self.showCusor = true; - self.lastBlinkTime = std.time.milliTimestamp(); + self.lastBlinkTime = main.timestamp(); if(self.textSize[1] > self.maxHeight - 2*border) { var y: f32 = 0; const diff = self.textSize[1] - (self.maxHeight - 2*border); @@ -521,9 +521,9 @@ pub fn render(self: *TextInput, mousePosition: Vec2f) void { self.textBuffer.drawSelection(textPos, @min(selectionStart, cursor), @max(selectionStart, cursor)); } - const milliTime = std.time.milliTimestamp(); - if(milliTime -% self.lastBlinkTime > blinkDurationMs) { - self.lastBlinkTime = milliTime; + const currentTime = main.timestamp(); + if(self.lastBlinkTime.durationTo(currentTime).nanoseconds > blinkDuration.nanoseconds) { + self.lastBlinkTime = currentTime; self.showCusor = !self.showCusor; } diff --git a/src/gui/gui.zig b/src/gui/gui.zig index 9ca1436448..cfb7411b0b 100644 --- a/src/gui/gui.zig +++ b/src/gui/gui.zig @@ -606,10 +606,10 @@ pub const inventory = struct { // MARK: inventory var rightClickSlots: List(*ItemSlot) = .init(main.globalAllocator); var recipeItem: ?main.items.Item = null; var initialized: bool = false; - const minCraftingCooldown = 20; - const maxCraftingCooldown = 400; - var nextCraftingAction: i64 = undefined; - var craftingCooldown: u63 = undefined; + const minCraftingCooldown: std.Io.Duration = .fromMilliseconds(20); + const maxCraftingCooldown: std.Io.Duration = .fromMilliseconds(400); + var nextCraftingAction: std.Io.Timestamp = undefined; + var craftingCooldown: std.Io.Duration = undefined; var isCrafting: bool = false; pub fn init() void { @@ -663,15 +663,15 @@ pub const inventory = struct { // MARK: inventory const item = itemSlot.inventory.getItem(itemSlot.itemSlot); if(recipeItem == null and item != null) recipeItem = item.?.clone(); if(!std.meta.eql(item, recipeItem)) return; - const time = std.time.milliTimestamp(); + const time = main.timestamp(); if(!isCrafting) { isCrafting = true; craftingCooldown = maxCraftingCooldown; nextCraftingAction = time; } - while(time -% nextCraftingAction >= 0) { - nextCraftingAction +%= craftingCooldown; - craftingCooldown -= (craftingCooldown - minCraftingCooldown)*craftingCooldown/1000; + while(time.durationTo(nextCraftingAction).nanoseconds <= 0) { + nextCraftingAction = nextCraftingAction.addDuration(craftingCooldown); + craftingCooldown.nanoseconds -= @divTrunc((craftingCooldown.nanoseconds - minCraftingCooldown.nanoseconds)*craftingCooldown.nanoseconds, 1000); if(mainGuiButton.modsOnPress.shift) { itemSlot.inventory.depositToAny(itemSlot.itemSlot, main.game.Player.inventory, itemSlot.inventory.getAmount(itemSlot.itemSlot)); } else { diff --git a/src/gui/windows/advanced_controls.zig b/src/gui/windows/advanced_controls.zig index 39ae872f80..f8b462327d 100644 --- a/src/gui/windows/advanced_controls.zig +++ b/src/gui/windows/advanced_controls.zig @@ -21,27 +21,27 @@ pub var window = GuiWindow{ const padding: f32 = 8; fn delayCallback(newValue: f32) void { - settings.updateRepeatDelay = @intFromFloat(newValue); + settings.updateRepeatDelay.nanoseconds = @intFromFloat(newValue); settings.save(); } fn delayFormatter(allocator: main.heap.NeverFailingAllocator, value: f32) []const u8 { - return std.fmt.allocPrint(allocator.allocator, "#ffffffPlace/Break Delay: {d:.0} ms", .{value}) catch unreachable; + return std.fmt.allocPrint(allocator.allocator, "#ffffffPlace/Break Delay: {d:.0} ms", .{value/1.0e6}) catch unreachable; } fn speedCallback(newValue: f32) void { - settings.updateRepeatSpeed = @intFromFloat(newValue); + settings.updateRepeatSpeed.nanoseconds = @intFromFloat(newValue); settings.save(); } fn speedFormatter(allocator: main.heap.NeverFailingAllocator, value: f32) []const u8 { - return std.fmt.allocPrint(allocator.allocator, "#ffffffPlace/Break Speed: {d:.0} ms", .{value}) catch unreachable; + return std.fmt.allocPrint(allocator.allocator, "#ffffffPlace/Break Speed: {d:.0} ms", .{value/1.0e6}) catch unreachable; } pub fn onOpen() void { const list = VerticalList.init(.{padding, 16 + padding}, 300, 16); - list.add(ContinuousSlider.init(.{0, 0}, 128, 1.0, 1000.0, @floatFromInt(settings.updateRepeatDelay), &delayCallback, &delayFormatter)); - list.add(ContinuousSlider.init(.{0, 0}, 128, 1.0, 500.0, @floatFromInt(settings.updateRepeatSpeed), &speedCallback, &speedFormatter)); + list.add(ContinuousSlider.init(.{0, 0}, 128, 1.0, 1000.0, @floatFromInt(settings.updateRepeatDelay.nanoseconds), &delayCallback, &delayFormatter)); + list.add(ContinuousSlider.init(.{0, 0}, 128, 1.0, 500.0, @floatFromInt(settings.updateRepeatSpeed.nanoseconds), &speedCallback, &speedFormatter)); list.finish(.center); window.rootComponent = list.toComponent(); window.contentSize = window.rootComponent.?.pos() + window.rootComponent.?.size() + @as(Vec2f, @splat(padding)); diff --git a/src/gui/windows/chat.zig b/src/gui/windows/chat.zig index 43da8797b1..5e4798d26d 100644 --- a/src/gui/windows/chat.zig +++ b/src/gui/windows/chat.zig @@ -205,7 +205,7 @@ pub fn onClose() void { pub fn update() void { if(!messageQueue.isEmpty()) { - const currentTime: i32 = @truncate(std.time.milliTimestamp()); + const currentTime: i32 = @truncate(main.timestamp().toMilliseconds()); while(messageQueue.popFront()) |msg| { history.append(Label.init(.{0, 0}, 256, msg, .left)); main.globalAllocator.free(msg); @@ -214,7 +214,7 @@ pub fn update() void { refresh(); } - const currentTime: i32 = @truncate(std.time.milliTimestamp()); + const currentTime: i32 = @truncate(main.timestamp().toMilliseconds()); while(fadeOutEnd < history.items.len and currentTime -% expirationTime.items[fadeOutEnd] >= 0) { fadeOutEnd += 1; } diff --git a/src/gui/windows/save_selection.zig b/src/gui/windows/save_selection.zig index b1a264aad7..6ea871d637 100644 --- a/src/gui/windows/save_selection.zig +++ b/src/gui/windows/save_selection.zig @@ -62,7 +62,7 @@ pub fn openWorld(name: []const u8) void { }; while(!main.server.running.load(.acquire)) { - std.Thread.sleep(1_000_000); + main.io.sleep(.fromMilliseconds(1), .awake) catch {}; main.heap.GarbageCollection.syncPoint(); } clientConnection.world = &main.game.testWorld; diff --git a/src/itemdrop.zig b/src/itemdrop.zig index 7a0a8a392f..a72378e2e7 100644 --- a/src/itemdrop.zig +++ b/src/itemdrop.zig @@ -480,7 +480,7 @@ pub const ClientItemDropManager = struct { // MARK: ClientItemDropManager instance = self; self.* = .{ .super = undefined, - .lastTime = @as(i16, @truncate(std.time.milliTimestamp())) -% settings.entityLookback, + .lastTime = @as(i16, @truncate(main.timestamp().toMilliseconds())) -% settings.entityLookback, }; self.super.init(allocator, null); self.interpolation.init( @@ -510,7 +510,7 @@ pub const ClientItemDropManager = struct { // MARK: ClientItemDropManager pub fn updateInterpolationData(self: *ClientItemDropManager) void { self.super.processChanges(); - var time = @as(i16, @truncate(std.time.milliTimestamp())) -% settings.entityLookback; + var time = @as(i16, @truncate(main.timestamp().toMilliseconds())) -% settings.entityLookback; time -%= self.timeDifference.difference.load(.monotonic); { mutex.lock(); diff --git a/src/main.zig b/src/main.zig index 09b0ea1e41..d58e77ead5 100644 --- a/src/main.zig +++ b/src/main.zig @@ -51,9 +51,11 @@ pub const globalAllocator: heap.NeverFailingAllocator = heap.allocators.handledG pub const globalArena = heap.allocators.globalArenaAllocator.allocator(); pub const worldArena = heap.allocators.worldArenaAllocator.allocator(); pub var threadPool: *utils.ThreadPool = undefined; +var threadedIo: std.Io.Threaded = undefined; +pub var io: std.Io = threadedIo.io(); pub fn initThreadLocals() void { - seed = @bitCast(@as(i64, @truncate(std.time.nanoTimestamp()))); + seed = @bitCast(@as(i64, @truncate(timestamp().nanoseconds))); stackAllocatorBase = heap.StackAllocator.init(globalAllocator, 1 << 23); stackAllocator = stackAllocatorBase.allocator(); heap.GarbageCollection.addThread(); @@ -64,6 +66,10 @@ pub fn deinitThreadLocals() void { heap.GarbageCollection.removeThread(); } +pub fn timestamp() std.Io.Timestamp { + return (std.Io.Clock.Timestamp.now(io, .awake) catch unreachable).raw; +} + fn cacheStringImpl(comptime len: usize, comptime str: [len]u8) []const u8 { return str[0..len]; } @@ -224,9 +230,9 @@ fn initLogging() void { return; }; - const _timestamp = std.time.timestamp(); + const _timestamp = (std.Io.Clock.Timestamp.now(io, .real) catch unreachable).raw; - const _path_str = std.fmt.allocPrint(stackAllocator.allocator, "logs/ts_{}.log", .{_timestamp}) catch unreachable; + const _path_str = std.fmt.allocPrint(stackAllocator.allocator, "logs/ts_{}.log", .{_timestamp.nanoseconds}) catch unreachable; defer stackAllocator.free(_path_str); logFileTs = std.fs.cwd().createFile(_path_str, .{}) catch |err| { @@ -267,7 +273,7 @@ fn logToStdErr(comptime format: []const u8, args: anytype) void { const string = std.fmt.allocPrint(allocator, format, args) catch format; const writer = std.debug.lockStderrWriter(&.{}); defer std.debug.unlockStderrWriter(); - nosuspend writer.writeAll(string) catch {}; + nosuspend writer[0].writeAll(string) catch {}; } // MARK: Callbacks @@ -490,6 +496,8 @@ pub fn main() void { // MARK: main() defer heap.GarbageCollection.assertAllThreadsStopped(); initThreadLocals(); defer deinitThreadLocals(); + threadedIo = .init(globalAllocator.allocator); + defer threadedIo.deinit(); initLogging(); defer deinitLogging(); @@ -585,7 +593,7 @@ pub fn clientMain() void { // MARK: clientMain() const c = Window.c; Window.GLFWCallbacks.framebufferSize(undefined, Window.width, Window.height); - var lastBeginRendering = std.time.nanoTimestamp(); + var lastBeginRendering = timestamp(); if(settings.launchConfig.autoEnterWorld.len != 0) { // Speed up the dev process by entering the world directly. @@ -608,11 +616,11 @@ pub fn clientMain() void { // MARK: clientMain() c.glClear(c.GL_DEPTH_BUFFER_BIT | c.GL_STENCIL_BUFFER_BIT | c.GL_COLOR_BUFFER_BIT); gui.windowlist.gpu_performance_measuring.stopQuery(); } else { - std.Thread.sleep(16_000_000); + io.sleep(.fromMilliseconds(16), .awake) catch {}; } - const endRendering = std.time.nanoTimestamp(); - const frameTime = @as(f64, @floatFromInt(endRendering -% lastBeginRendering))/1e9; + const endRendering = timestamp(); + const frameTime = @as(f64, @floatFromInt(endRendering.nanoseconds -% lastBeginRendering.nanoseconds))/1.0e9; if(settings.developerGPUInfiniteLoopDetection and frameTime > 5) { // On linux a process that runs 10 seconds or longer on the GPU will get stopped. This allows detecting an infinite loop on the GPU. std.log.err("Frame got too long with {} seconds. Infinite loop on GPU?", .{frameTime}); std.posix.exit(1); @@ -621,11 +629,11 @@ pub fn clientMain() void { // MARK: clientMain() if(settings.fpsCap) |fpsCap| { const minFrameTime = @divFloor(1000*1000*1000, fpsCap); - const sleep = @min(minFrameTime, @max(0, minFrameTime - (endRendering -% lastBeginRendering))); - std.Thread.sleep(sleep); + const sleep = @min(minFrameTime, @max(0, minFrameTime - (endRendering.nanoseconds -% lastBeginRendering.nanoseconds))); + io.sleep(.fromNanoseconds(sleep), .awake) catch {}; } - const begin = std.time.nanoTimestamp(); - const deltaTime = @as(f64, @floatFromInt(begin -% lastBeginRendering))/1e9; + const begin = timestamp(); + const deltaTime = @as(f64, @floatFromInt(begin.nanoseconds -% lastBeginRendering.nanoseconds))/1.0e9; lastDeltaTime.store(deltaTime, .monotonic); lastBeginRendering = begin; @@ -643,7 +651,7 @@ pub fn clientMain() void { // MARK: clientMain() renderer.render(game.Player.getEyePosBlocking(), deltaTime); } else { renderer.updateFov(70.0); - renderer.MenuBackGround.render(); + renderer.MenuBackGround.render(deltaTime); } // Render the GUI gui.windowlist.gpu_performance_measuring.startQuery(.gui); diff --git a/src/network.zig b/src/network.zig index 5301739316..db4e955b9b 100644 --- a/src/network.zig +++ b/src/network.zig @@ -14,7 +14,7 @@ pub const protocols = @import("network/protocols.zig"); const ms = 1_000; inline fn networkTimestamp() i64 { - return std.time.microTimestamp(); + return @truncate(@divTrunc(main.timestamp().toNanoseconds(), 1000)); } const Socket = struct { @@ -252,7 +252,7 @@ const stun = struct { // MARK: stun fn requestAddress(connection: *ConnectionManager) Address { var oldAddress: ?Address = null; var seed: [std.Random.DefaultCsprng.secret_seed_length]u8 = @splat(0); - std.mem.writeInt(i128, seed[0..16], std.time.nanoTimestamp(), builtin.cpu.arch.endian()); // Not the best seed, but it's not that important. + std.mem.writeInt(i128, seed[0..16], main.timestamp().toMilliseconds(), builtin.cpu.arch.endian()); // Not the best seed, but it's not that important. var random = std.Random.DefaultCsprng.init(seed); for(0..16) |_| { // Choose a somewhat random server, so we faster notice if any one of them stopped working. @@ -1299,7 +1299,7 @@ pub const Connection = struct { // MARK: Connection self.tryReceive(data) catch |err| { std.log.err("Got error while processing received network data: {s}", .{@errorName(err)}); if(@errorReturnTrace()) |trace| { - std.log.info("{f}", .{trace}); + std.log.info("{f}", .{std.debug.FormatStackTrace{.stack_trace = trace.*, .tty_config = .no_color}}); } std.log.debug("Packet data: {any}", .{data}); self.disconnect(); diff --git a/src/network/protocols.zig b/src/network/protocols.zig index ec6755a34a..55b82b181f 100644 --- a/src/network/protocols.zig +++ b/src/network/protocols.zig @@ -394,7 +394,7 @@ pub const entityPosition = struct { // MARK: entityPosition var writer = utils.BinaryWriter.init(main.stackAllocator); defer writer.deinit(); - writer.writeInt(i16, @truncate(std.time.milliTimestamp())); + writer.writeInt(i16, @truncate(main.timestamp().toMilliseconds())); writer.writeVec(Vec3d, playerPos); for(entityData) |data| { const velocityMagnitudeSqr = vec.lengthSquare(data.vel); diff --git a/src/particles.zig b/src/particles.zig index 437ae974b5..0589f1bbf6 100644 --- a/src/particles.zig +++ b/src/particles.zig @@ -19,8 +19,6 @@ const Vec3f = vec.Vec3f; const Vec4f = vec.Vec4f; const Vec3i = vec.Vec3i; -var seed: u64 = undefined; - pub const ParticleManager = struct { var particleTypesSSBO: SSBO = undefined; var types: main.ListUnmanaged(ParticleType) = .{}; @@ -192,8 +190,6 @@ pub const ParticleSystem = struct { particlesSSBO = SSBO.init(); particlesSSBO.createDynamicBuffer(Particle, maxCapacity); particlesSSBO.bind(13); - - seed = @bitCast(@as(i64, @truncate(std.time.nanoTimestamp()))); } fn deinit() void { @@ -295,8 +291,8 @@ pub const ParticleSystem = struct { } fn addParticle(typ: u32, pos: Vec3d, vel: Vec3f, collides: bool) void { - const lifeTime = properties.lifeTimeMin + random.nextFloat(&seed)*properties.lifeTimeMax; - const rot = if(properties.randomizeRotationOnSpawn) random.nextFloat(&seed)*std.math.pi*2 else 0; + const lifeTime = properties.lifeTimeMin + random.nextFloat(&main.seed)*properties.lifeTimeMax; + const rot = if(properties.randomizeRotationOnSpawn) random.nextFloat(&main.seed)*std.math.pi*2 else 0; particles[particleCount] = Particle{ .pos = @as(Vec3f, @floatCast(pos - previousPlayerPos)), @@ -304,7 +300,7 @@ pub const ParticleSystem = struct { .typ = typ, }; particlesLocal[particleCount] = ParticleLocal{ - .velAndRotationVel = vec.combine(vel, properties.rotVelMin + random.nextFloatSigned(&seed)*properties.rotVelMax), + .velAndRotationVel = vec.combine(vel, properties.rotVelMin + random.nextFloatSigned(&main.seed)*properties.rotVelMax), .lifeVelocity = 1/lifeTime, .collides = collides, }; @@ -382,10 +378,10 @@ pub const Emitter = struct { pub fn spawn(self: SpawnPoint) struct {Vec3d, Vec3f} { const particlePos = self.position; - const speed: Vec3f = @splat(ParticleSystem.properties.velMin + random.nextFloat(&seed)*ParticleSystem.properties.velMax); + const speed: Vec3f = @splat(ParticleSystem.properties.velMin + random.nextFloat(&main.seed)*ParticleSystem.properties.velMax); const dir: Vec3f = switch(self.mode) { .direction => |dir| dir, - .scatter, .spread => vec.normalize(random.nextFloatVectorSigned(3, &seed)), + .scatter, .spread => vec.normalize(random.nextFloatVectorSigned(3, &main.seed)), }; const particleVel = dir*speed; @@ -402,14 +398,14 @@ pub const Emitter = struct { const spawnPos: Vec3f = @splat(self.radius); var offsetPos: Vec3f = undefined; while(true) { - offsetPos = random.nextFloatVectorSigned(3, &seed); + offsetPos = random.nextFloatVectorSigned(3, &main.seed); if(vec.lengthSquare(offsetPos) <= 1) break; } const particlePos = self.position + @as(Vec3d, @floatCast(offsetPos*spawnPos)); - const speed: Vec3f = @splat(ParticleSystem.properties.velMin + random.nextFloat(&seed)*ParticleSystem.properties.velMax); + const speed: Vec3f = @splat(ParticleSystem.properties.velMin + random.nextFloat(&main.seed)*ParticleSystem.properties.velMax); const dir: Vec3f = switch(self.mode) { .direction => |dir| dir, - .scatter => vec.normalize(random.nextFloatVectorSigned(3, &seed)), + .scatter => vec.normalize(random.nextFloatVectorSigned(3, &main.seed)), .spread => @floatCast(offsetPos), }; const particleVel = dir*speed; @@ -425,12 +421,12 @@ pub const Emitter = struct { pub fn spawn(self: SpawnCube) struct {Vec3d, Vec3f} { const spawnPos: Vec3f = self.size; - const offsetPos: Vec3f = random.nextFloatVectorSigned(3, &seed); + const offsetPos: Vec3f = random.nextFloatVectorSigned(3, &main.seed); const particlePos = self.position + @as(Vec3d, @floatCast(offsetPos*spawnPos)); - const speed: Vec3f = @splat(ParticleSystem.properties.velMin + random.nextFloat(&seed)*ParticleSystem.properties.velMax); + const speed: Vec3f = @splat(ParticleSystem.properties.velMin + random.nextFloat(&main.seed)*ParticleSystem.properties.velMax); const dir: Vec3f = switch(self.mode) { .direction => |dir| dir, - .scatter => vec.normalize(random.nextFloatVectorSigned(3, &seed)), + .scatter => vec.normalize(random.nextFloatVectorSigned(3, &main.seed)), .spread => vec.normalize(@as(Vec3f, @floatCast(offsetPos))), }; const particleVel = dir*speed; diff --git a/src/renderer.zig b/src/renderer.zig index 0bf6d48c11..53415afb89 100644 --- a/src/renderer.zig +++ b/src/renderer.zig @@ -28,8 +28,8 @@ const Mat4f = vec.Mat4f; pub const chunk_meshing = @import("renderer/chunk_meshing.zig"); pub const mesh_storage = @import("renderer/mesh_storage.zig"); -/// The number of milliseconds after which no more chunk meshes are created. This allows the game to run smoother on movement. -const maximumMeshTime = 12; +/// Time after which no more chunk meshes are created. This allows the game to run smoother on movement. +const maximumMeshTime: std.Io.Duration = .fromMilliseconds(12); pub const zNear = 0.1; pub const zFar = 65536.0; // TODO: Fix z-fighting problems. @@ -154,8 +154,8 @@ pub fn render(playerPosition: Vec3d, deltaTime: f64) void { itemdrop.ItemDisplayManager.update(deltaTime); renderWorld(game.world.?, ambient, game.fog.skyColor, playerPosition); - const startTime = std.time.milliTimestamp(); - mesh_storage.updateMeshes(startTime + maximumMeshTime); + const startTime = main.timestamp(); + mesh_storage.updateMeshes(startTime.addDuration(maximumMeshTime)); } pub fn crosshairDirection(rotationMatrix: Mat4f, fovY: f32, width: u31, height: u31) Vec3f { @@ -192,7 +192,7 @@ pub fn renderWorld(world: *World, ambientLight: Vec3f, skyColor: Vec3f, playerPo // Uses FrustumCulling on the chunks. const frustum = Frustum.init(Vec3f{0, 0, 0}, game.camera.viewMatrix, lastFov, lastWidth, lastHeight); - const time: u32 = @intCast(std.time.milliTimestamp() & std.math.maxInt(u32)); + const time: u32 = @intCast(main.timestamp().toMilliseconds() & std.math.maxInt(u32)); gpu_performance_measuring.startQuery(.skybox); Skybox.render(); @@ -479,10 +479,8 @@ pub const MenuBackGround = struct { var texture: graphics.Texture = undefined; var angle: f32 = 0; - var lastTime: i128 = undefined; fn init() void { - lastTime = std.time.nanoTimestamp(); pipeline = graphics.Pipeline.init( "assets/cubyz/shaders/background/vertex.vert", "assets/cubyz/shaders/background/fragment.frag", @@ -584,14 +582,12 @@ pub const MenuBackGround = struct { return texture.textureID != 0; } - pub fn render() void { + pub fn render(deltaTime: f64) void { c.glViewport(0, 0, main.Window.width, main.Window.height); if(texture.textureID == 0) return; // Use a simple rotation around the z axis, with a steadily increasing angle. - const newTime = std.time.nanoTimestamp(); - angle += @as(f32, @floatFromInt(newTime - lastTime))/2e10; - lastTime = newTime; + angle += @as(f32, @floatCast(deltaTime))/20.0; const viewMatrix = Mat4f.rotationZ(angle); pipeline.bind(null); c.glUniformMatrix4fv(uniforms.viewMatrix, 1, c.GL_TRUE, @ptrCast(&viewMatrix)); diff --git a/src/renderer/mesh_storage.zig b/src/renderer/mesh_storage.zig index e79731521b..ce4d409b0d 100644 --- a/src/renderer/mesh_storage.zig +++ b/src/renderer/mesh_storage.zig @@ -733,7 +733,7 @@ pub noinline fn updateAndGetRenderChunks(conn: *network.Connection, frustum: *co return meshList.items; } -pub fn updateMeshes(targetTime: i64) void { // MARK: updateMeshes()= +pub fn updateMeshes(targetTime: std.Io.Timestamp) void { // MARK: updateMeshes() if(!blockUpdateList.isEmpty()) batchUpdateBlocks(); mutex.lock(); @@ -747,7 +747,7 @@ pub fn updateMeshes(targetTime: i64) void { // MARK: updateMeshes()= mutex.unlock(); defer mutex.lock(); mesh.uploadData(); - if(std.time.milliTimestamp() >= targetTime) break; // Update at least one mesh. + if(targetTime.durationTo(main.timestamp()).nanoseconds >= 0) break; // Update at least one mesh. } while(mapUpdatableList.popFront()) |map| { if(!isMapInRenderDistance(map.pos)) { @@ -795,7 +795,7 @@ pub fn updateMeshes(targetTime: i64) void { // MARK: updateMeshes()= updateHigherLodNodeFinishedMeshing(pos, true); mesh.uploadData(); } - if(std.time.milliTimestamp() >= targetTime) break; // Update at least one mesh. + if(targetTime.durationTo(main.timestamp()).nanoseconds >= 0) break; // Update at least one mesh. } } diff --git a/src/server/server.zig b/src/server/server.zig index ceaf4ceab4..6adb869d3f 100644 --- a/src/server/server.zig +++ b/src/server/server.zig @@ -98,7 +98,7 @@ pub const User = struct { // MARK: User timeDifference: utils.TimeDifference = .{}, interpolation: utils.GenericInterpolation(3) = undefined, lastTime: i16 = undefined, - lastSaveTime: i64 = 0, + lastSaveTime: std.Io.Timestamp = .fromNanoseconds(0), name: []const u8 = "", renderDistance: u16 = undefined, clientUpdatePos: Vec3i = .{0, 0, 0}, @@ -249,13 +249,13 @@ pub const User = struct { // MARK: User pub fn update(self: *User) void { self.mutex.lock(); defer self.mutex.unlock(); - var time = @as(i16, @truncate(std.time.milliTimestamp())) -% main.settings.entityLookback; + var time = @as(i16, @truncate(main.timestamp().toMilliseconds())) -% main.settings.entityLookback; time -%= self.timeDifference.difference.load(.monotonic); self.interpolation.update(time, self.lastTime); self.lastTime = time; - const saveTime = std.time.milliTimestamp(); - if(saveTime -% self.lastSaveTime > 5000) { + const saveTime = main.timestamp(); + if(self.lastSaveTime.durationTo(saveTime).toSeconds() > 5) { world.?.savePlayer(self) catch |err| { std.log.err("Failed to save player {s}: {s}", .{self.name, @errorName(err)}); }; @@ -288,7 +288,7 @@ pub const User = struct { // MARK: User }; pub const updatesPerSec: u32 = 20; -const updateNanoTime: u32 = 1000000000/20; +const updateTime: std.Io.Duration = .fromNanoseconds(1000000000/20); pub var world: ?*ServerWorld = null; var userMutex: std.Thread.Mutex = .{}; @@ -299,7 +299,7 @@ var userConnectList: main.utils.ConcurrentQueue(*User) = undefined; pub var connectionManager: *ConnectionManager = undefined; pub var running: std.atomic.Value(bool) = .init(false); -var lastTime: i128 = undefined; +var lastTime: std.Io.Timestamp = undefined; pub var thread: ?std.Thread = null; @@ -310,7 +310,7 @@ fn init(name: []const u8, singlePlayerPort: ?u16) void { // MARK: init() users = .init(main.globalAllocator); userDeinitList = .init(main.globalAllocator, 16); userConnectList = .init(main.globalAllocator, 16); - lastTime = std.time.nanoTimestamp(); + lastTime = main.timestamp(); connectionManager = ConnectionManager.init(main.settings.defaultPort, false) catch |err| { std.log.err("Couldn't create socket: {s}", .{@errorName(err)}); @panic("Could not open Server."); @@ -449,12 +449,12 @@ pub fn start(name: []const u8, port: ?u16) void { running.store(true, .release); while(running.load(.monotonic)) { main.heap.GarbageCollection.syncPoint(); - const newTime = std.time.nanoTimestamp(); - if(newTime -% lastTime < updateNanoTime) { - std.Thread.sleep(@intCast(lastTime +% updateNanoTime -% newTime)); - lastTime +%= updateNanoTime; + const newTime = main.timestamp(); + if(lastTime.durationTo(newTime).nanoseconds < updateTime.nanoseconds) { + main.io.sleep(lastTime.durationTo(newTime.addDuration(updateTime)), .awake) catch {}; + lastTime = lastTime.addDuration(updateTime); } else { - std.log.warn("The server is lagging behind by {d:.1} ms", .{@as(f32, @floatFromInt(newTime -% lastTime -% updateNanoTime))/1000000.0}); + std.log.warn("The server is lagging behind by {d:.1} ms", .{@as(f32, @floatFromInt(newTime.nanoseconds -% lastTime.nanoseconds -% updateTime.nanoseconds))/1000000.0}); lastTime = newTime; } update(); diff --git a/src/server/storage.zig b/src/server/storage.zig index 5ec190a4ee..4664451779 100644 --- a/src/server/storage.zig +++ b/src/server/storage.zig @@ -48,7 +48,7 @@ pub const RegionFile = struct { // MARK: RegionFile defer main.stackAllocator.free(data); self.load(path, data) catch { std.log.err("Corrupted region file: {s}", .{path}); - if(@errorReturnTrace()) |trace| std.log.info("{f}", .{trace}); + if(@errorReturnTrace()) |trace| std.log.info("{f}", .{std.debug.FormatStackTrace{.stack_trace = trace.*, .tty_config = .no_color}}); }; return self; } diff --git a/src/server/terrain/terrain.zig b/src/server/terrain/terrain.zig index f5f9747078..7f3806cbfb 100644 --- a/src/server/terrain/terrain.zig +++ b/src/server/terrain/terrain.zig @@ -120,9 +120,9 @@ pub fn globalInit() void { inline for(@typeInfo(list).@"struct".decls) |decl| { BlockGenerator.registerGenerator(@field(list, decl.name)); } - const t1 = std.time.milliTimestamp(); + const t1 = main.timestamp(); noise.BlueNoise.load(); - std.log.info("Blue noise took {} ms to load", .{std.time.milliTimestamp() -% t1}); + std.log.info("Blue noise took {} ms to load", .{t1.durationTo(main.timestamp()).toMilliseconds()}); } pub fn globalDeinit() void { diff --git a/src/server/world.zig b/src/server/world.zig index 2b25193ebc..37096a24b4 100644 --- a/src/server/world.zig +++ b/src/server/world.zig @@ -98,7 +98,7 @@ pub fn tryCreateWorld(worldName: []const u8, worldSettings: Settings) !void { worldInfo.put("name", worldName); worldInfo.put("version", worldDataVersion); - worldInfo.put("lastUsedTime", std.time.milliTimestamp()); + worldInfo.put("lastUsedTime", (try std.Io.Clock.Timestamp.now(main.io, .real)).raw.toMilliseconds()); worldInfo.put("seed", worldSettings.seed); try main.files.cubyzDir().writeZon(worldInfoPath, worldInfo); @@ -514,7 +514,7 @@ const WorldIO = struct { // MARK: WorldIO worldData.put("spawn", self.world.spawn); worldData.put("biomeChecksum", self.world.biomeChecksum); worldData.put("name", self.world.name); - worldData.put("lastUsedTime", std.time.milliTimestamp()); + worldData.put("lastUsedTime", (try std.Io.Clock.Timestamp.now(main.io, .real)).raw.toMilliseconds()); worldData.put("tickSpeed", self.world.tickSpeed.load(.monotonic)); // TODO: Save entities try self.dir.writeZon("world.zig.zon", worldData); @@ -532,9 +532,9 @@ pub const ServerWorld = struct { // MARK: ServerWorld chunkManager: ChunkManager = undefined, gameTime: i64 = 0, - milliTime: i64, - lastUpdateTime: i64, - lastUnimportantDataSent: i64, + milliTime: std.Io.Timestamp, + lastUpdateTime: std.Io.Timestamp, + lastUnimportantDataSent: std.Io.Timestamp, doGameTimeCycle: bool = true, tickSpeed: std.atomic.Value(u32) = .init(12), @@ -571,9 +571,9 @@ pub const ServerWorld = struct { // MARK: ServerWorld const self = main.globalAllocator.create(ServerWorld); errdefer main.globalAllocator.destroy(self); self.* = ServerWorld{ - .lastUpdateTime = std.time.milliTimestamp(), - .milliTime = std.time.milliTimestamp(), - .lastUnimportantDataSent = std.time.milliTimestamp(), + .lastUpdateTime = main.timestamp(), + .milliTime = main.timestamp(), + .lastUnimportantDataSent = main.timestamp(), .path = main.globalAllocator.dupe(u8, path), .chunkUpdateQueue = .init(main.globalAllocator, 256), .regionUpdateQueue = .init(main.globalAllocator, 256), @@ -808,7 +808,7 @@ pub const ServerWorld = struct { // MARK: ServerWorld main.heap.GarbageCollection.syncPoint(); } self.mutex.unlock(); - std.Thread.sleep(1_000_000); + main.io.sleep(.fromMilliseconds(1), .awake) catch {}; main.heap.GarbageCollection.syncPoint(); self.mutex.lock(); if(main.threadPool.queueSize() == 0 and self.chunkUpdateQueue.peekFront() == null and self.regionUpdateQueue.peekFront() == null) break; @@ -1078,19 +1078,19 @@ pub const ServerWorld = struct { // MARK: ServerWorld } pub fn update(self: *ServerWorld) void { // MARK: update() - const newTime = std.time.milliTimestamp(); - var deltaTime = @as(f32, @floatFromInt(newTime - self.lastUpdateTime))/1000.0; + const newTime = main.timestamp(); + var deltaTime = @as(f32, @floatFromInt(self.lastUpdateTime.durationTo(newTime).toNanoseconds()))/1.0e9; self.lastUpdateTime = newTime; if(deltaTime > 0.3) { std.log.warn("Update time is getting too high. It's already at {} s!", .{deltaTime}); deltaTime = 0.3; } - while(self.milliTime + 100 < newTime) { - self.milliTime += 100; + while(self.milliTime.durationTo(newTime).toMilliseconds() < 100) { + self.milliTime = self.milliTime.addDuration(.fromMilliseconds(100)); if(self.doGameTimeCycle) self.gameTime +%= 1; // gameTime is measured in 100ms. } - if(self.lastUnimportantDataSent + 2000 < newTime) { // Send unimportant data every ~2s. + if(self.lastUnimportantDataSent.durationTo(newTime).toSeconds() < 2) { self.lastUnimportantDataSent = newTime; const userList = server.getUserListAndIncreaseRefCount(main.stackAllocator); defer server.freeUserListAndDecreaseRefCount(main.stackAllocator, userList); @@ -1114,7 +1114,7 @@ pub const ServerWorld = struct { // MARK: ServerWorld // Store chunks and regions. // Stores at least one chunk and one region per iteration. // All chunks and regions will be stored within the storage time. - const insertionTime = newTime -% main.settings.storageTime; + const insertionTime = newTime.subDuration(main.settings.storageTime); self.mutex.lock(); defer self.mutex.unlock(); while(self.chunkUpdateQueue.popFront()) |updateRequest| { @@ -1122,14 +1122,14 @@ pub const ServerWorld = struct { // MARK: ServerWorld defer self.mutex.lock(); updateRequest.ch.save(self); updateRequest.ch.decreaseRefCount(); - if(updateRequest.milliTimeStamp -% insertionTime <= 0) break; + if(updateRequest.milliTimeStamp -% insertionTime.toMilliseconds() <= 0) break; } while(self.regionUpdateQueue.popFront()) |updateRequest| { self.mutex.unlock(); defer self.mutex.lock(); updateRequest.region.store(); updateRequest.region.decreaseRefCount(); - if(updateRequest.milliTimeStamp -% insertionTime <= 0) break; + if(updateRequest.milliTimeStamp -% insertionTime.toMilliseconds() <= 0) break; } } @@ -1290,13 +1290,13 @@ pub const ServerWorld = struct { // MARK: ServerWorld pub fn queueChunkUpdateAndDecreaseRefCount(self: *ServerWorld, ch: *ServerChunk) void { self.mutex.lock(); - self.chunkUpdateQueue.pushBack(.{.ch = ch, .milliTimeStamp = std.time.milliTimestamp()}); + self.chunkUpdateQueue.pushBack(.{.ch = ch, .milliTimeStamp = main.timestamp().toMilliseconds()}); self.mutex.unlock(); } pub fn queueRegionFileUpdateAndDecreaseRefCount(self: *ServerWorld, region: *storage.RegionFile) void { self.mutex.lock(); - self.regionUpdateQueue.pushBack(.{.region = region, .milliTimeStamp = std.time.milliTimestamp()}); + self.regionUpdateQueue.pushBack(.{.region = region, .milliTimeStamp = main.timestamp().toMilliseconds()}); self.mutex.unlock(); } }; diff --git a/src/settings.zig b/src/settings.zig index c8473e25c6..094eaacad2 100644 --- a/src/settings.zig +++ b/src/settings.zig @@ -57,11 +57,11 @@ pub var @"lod0.5Distance": f32 = 200; pub var blockContrast: f32 = 0; -pub var storageTime: i64 = 5000; +pub var storageTime: std.Io.Duration = .fromSeconds(5); -pub var updateRepeatSpeed: u31 = 200; +pub var updateRepeatSpeed: std.Io.Duration = .fromMilliseconds(200); -pub var updateRepeatDelay: u31 = 500; +pub var updateRepeatDelay: std.Io.Duration = .fromMilliseconds(500); pub var developerGPUInfiniteLoopDetection: bool = false; @@ -81,14 +81,19 @@ pub fn init() void { inline for(@typeInfo(@This()).@"struct".decls) |decl| { const is_const = @typeInfo(@TypeOf(&@field(@This(), decl.name))).pointer.is_const; // Sadly there is no direct way to check if a declaration is const. if(!is_const) { - const declType = @TypeOf(@field(@This(), decl.name)); - if(@typeInfo(declType) == .@"struct") { + const DeclType = @TypeOf(@field(@This(), decl.name)); + if(@typeInfo(DeclType) == .@"struct") { + if(DeclType == std.Io.Duration) { + const defaultMilli = @as(f64, @floatFromInt(@field(@This(), decl.name).toNanoseconds()))/1.0e6; + @field(@This(), decl.name) = .fromNanoseconds(@intFromFloat(zon.get(f64, decl.name, defaultMilli)*1.0e6)); + continue; + } @compileError("Not implemented yet."); } - @field(@This(), decl.name) = zon.get(declType, decl.name, @field(@This(), decl.name)); - if(@typeInfo(declType) == .pointer) { - if(@typeInfo(declType).pointer.size == .slice) { - @field(@This(), decl.name) = main.globalAllocator.dupe(@typeInfo(declType).pointer.child, @field(@This(), decl.name)); + @field(@This(), decl.name) = zon.get(DeclType, decl.name, @field(@This(), decl.name)); + if(@typeInfo(DeclType) == .pointer) { + if(@typeInfo(DeclType).pointer.size == .slice) { + @field(@This(), decl.name) = main.globalAllocator.dupe(@typeInfo(DeclType).pointer.child, @field(@This(), decl.name)); } else { @compileError("Not implemented yet."); } @@ -116,12 +121,13 @@ pub fn deinit() void { inline for(@typeInfo(@This()).@"struct".decls) |decl| { const is_const = @typeInfo(@TypeOf(&@field(@This(), decl.name))).pointer.is_const; // Sadly there is no direct way to check if a declaration is const. if(!is_const) { - const declType = @TypeOf(@field(@This(), decl.name)); - if(@typeInfo(declType) == .@"struct") { + const DeclType = @TypeOf(@field(@This(), decl.name)); + if(@typeInfo(DeclType) == .@"struct") { + if(DeclType == std.Io.Duration) continue; @compileError("Not implemented yet."); } - if(@typeInfo(declType) == .pointer) { - if(@typeInfo(declType).pointer.size == .slice) { + if(@typeInfo(DeclType) == .pointer) { + if(@typeInfo(DeclType).pointer.size == .slice) { main.globalAllocator.free(@field(@This(), decl.name)); } else { @compileError("Not implemented yet."); @@ -142,11 +148,15 @@ pub fn save() void { } const is_const = @typeInfo(@TypeOf(&@field(@This(), decl.name))).pointer.is_const; // Sadly there is no direct way to check if a declaration is const. if(!is_const) { - const declType = @TypeOf(@field(@This(), decl.name)); - if(@typeInfo(declType) == .@"struct") { + const DeclType = @TypeOf(@field(@This(), decl.name)); + if(@typeInfo(DeclType) == .@"struct") { + if(DeclType == std.Io.Duration) { + zonObject.put(decl.name, @as(f64, @floatFromInt(@field(@This(), decl.name).toNanoseconds()))/1.0e6); + continue; + } @compileError("Not implemented yet."); } - if(declType == []const u8) { + if(DeclType == []const u8) { zonObject.putOwnedString(decl.name, @field(@This(), decl.name)); } else { zonObject.put(decl.name, @field(@This(), decl.name)); diff --git a/src/utils.zig b/src/utils.zig index 10fe95f9a3..61baae36db 100644 --- a/src/utils.zig +++ b/src/utils.zig @@ -651,7 +651,7 @@ pub fn BlockingMaxHeap(comptime T: type) type { // MARK: BlockingMaxHeap self.waitingThreads.broadcast(); while(self.waitingThreadCount != 0) { self.mutex.unlock(); - std.Thread.sleep(1000000); + main.io.sleep(.fromMilliseconds(1), .awake) catch {}; self.mutex.lock(); } self.mutex.unlock(); @@ -749,7 +749,7 @@ pub fn BlockingMaxHeap(comptime T: type) type { // MARK: BlockingMaxHeap self.mutex.lock(); defer self.mutex.unlock(); - const startTime = std.time.nanoTimestamp(); + const startTime = main.timestamp(); while(true) { if(self.size == 0) { @@ -764,7 +764,7 @@ pub fn BlockingMaxHeap(comptime T: type) type { // MARK: BlockingMaxHeap if(self.closed) { return error.Closed; } - if(std.time.nanoTimestamp() -% startTime > 10_000_000) return error.Timeout; + if(startTime.durationTo(main.timestamp()).toMilliseconds() > 10) return error.Timeout; } } @@ -833,7 +833,7 @@ pub const ThreadPool = struct { // MARK: ThreadPool return self.*; } }; - const refreshTime: u32 = 100; // The time after which all priorities get refreshed in milliseconds. + const refreshTime: std.Io.Duration = .fromMilliseconds(100); // The time after which all priorities get refreshed. threads: []std.Thread, currentTasks: []Atomic(?*const VTable), @@ -898,7 +898,7 @@ pub const ThreadPool = struct { // MARK: ThreadPool // Wait for active tasks: for(self.currentTasks) |*task| { while(task.load(.monotonic) == vtable) { - std.Thread.sleep(1e6); + main.io.sleep(.fromMilliseconds(1), .awake) catch {}; } } } @@ -907,7 +907,7 @@ pub const ThreadPool = struct { // MARK: ThreadPool main.initThreadLocals(); defer main.deinitThreadLocals(); - var lastUpdate = std.time.milliTimestamp(); + var lastUpdate = main.timestamp(); outer: while(true) { main.heap.GarbageCollection.syncPoint(); { @@ -916,15 +916,15 @@ pub const ThreadPool = struct { // MARK: ThreadPool error.Closed => break :outer, }; self.currentTasks[id].store(task.vtable, .monotonic); - const start = std.time.microTimestamp(); + const start = main.timestamp(); task.vtable.run(task.self); - const end = std.time.microTimestamp(); - self.performance.add(task.vtable.taskType, end - start); + const end = main.timestamp(); + self.performance.add(task.vtable.taskType, @intCast(@divTrunc(start.durationTo(end).toNanoseconds(), 1000))); self.currentTasks[id].store(null, .monotonic); _ = self.trueQueueSize.fetchSub(1, .monotonic); } - if(id == 0 and std.time.milliTimestamp() -% lastUpdate > refreshTime) { + if(id == 0 and lastUpdate.durationTo(main.timestamp()).nanoseconds > refreshTime.nanoseconds) { var temporaryTaskList = main.List(Task).init(main.stackAllocator); defer temporaryTaskList.deinit(); while(self.loadList.extractAny()) |task| { @@ -938,7 +938,7 @@ pub const ThreadPool = struct { // MARK: ThreadPool } } self.loadList.addMany(temporaryTaskList.items); - lastUpdate = std.time.milliTimestamp(); + lastUpdate = main.timestamp(); } } } @@ -969,7 +969,7 @@ pub const ThreadPool = struct { // MARK: ThreadPool break; } } - std.Thread.sleep(1000000); + main.io.sleep(.fromMilliseconds(1000000), .awake) catch {}; } } @@ -1577,7 +1577,7 @@ pub const TimeDifference = struct { // MARK: TimeDifference firstValue: bool = true, pub fn addDataPoint(self: *TimeDifference, time: i16) void { - const currentTime: i16 = @truncate(std.time.milliTimestamp()); + const currentTime: i16 = @truncate(main.timestamp().toMilliseconds()); const timeDifference = currentTime -% time; if(self.firstValue) { self.difference.store(timeDifference, .monotonic); diff --git a/src/utils/heap.zig b/src/utils/heap.zig index e65e23707a..b0bc1be67a 100644 --- a/src/utils/heap.zig +++ b/src/utils/heap.zig @@ -708,7 +708,7 @@ pub fn MemoryPool(Item: type) type { // MARK: MemoryPool pub const GarbageCollection = struct { // MARK: GarbageCollection var sharedState: std.atomic.Value(u32) = .init(0); threadlocal var threadCycle: u2 = undefined; - threadlocal var lastSyncPointTime: i64 = undefined; + threadlocal var lastSyncPointTime: std.Io.Timestamp = undefined; const FreeItem = struct { ptr: *anyopaque, freeFunction: *const fn(*anyopaque) void, @@ -725,7 +725,7 @@ pub const GarbageCollection = struct { // MARK: GarbageCollection const old: State = @bitCast(sharedState.fetchAdd(@bitCast(State{.totalThreads = 1}), .monotonic)); _ = old.totalThreads + 1; // Assert no overflow threadCycle = old.cycle; - lastSyncPointTime = std.time.milliTimestamp(); + lastSyncPointTime = main.timestamp(); for(&lists) |*list| { list.* = .initCapacity(main.globalAllocator, 1024); } @@ -744,10 +744,10 @@ pub const GarbageCollection = struct { // MARK: GarbageCollection const old: State = @bitCast(sharedState.fetchSub(@bitCast(State{.totalThreads = 1}), .monotonic)); _ = old.totalThreads - 1; // Assert no overflow if(old.cycle != threadCycle) removeThreadFromWaiting(); - const newTime = std.time.milliTimestamp(); - if(newTime -% lastSyncPointTime > 20_000) { + const newTime = main.timestamp(); + if(lastSyncPointTime.durationTo(newTime).toSeconds() > 20) { if(!build_options.isTaggedRelease) { - std.log.err("No sync point executed in {} ms for thread. Did you forget to add a sync point in the thread's main loop?", .{newTime -% lastSyncPointTime}); + std.log.err("No sync point executed in {} ms for thread. Did you forget to add a sync point in the thread's main loop?", .{lastSyncPointTime.durationTo(newTime).toMilliseconds()}); std.debug.dumpCurrentStackTrace(.{}); } } @@ -781,9 +781,9 @@ pub const GarbageCollection = struct { // MARK: GarbageCollection /// Must be called when no objects originating from other threads are held on the current function stack pub fn syncPoint() void { - const newTime = std.time.milliTimestamp(); - if(newTime -% lastSyncPointTime > 20_000) { - std.log.err("No sync point executed in {} ms. Did you forget to add a sync point in the thread's main loop", .{newTime -% lastSyncPointTime}); + const newTime = main.timestamp(); + if(lastSyncPointTime.durationTo(newTime).toSeconds() > 20) { + std.log.err("No sync point executed in {} ms. Did you forget to add a sync point in the thread's main loop", .{lastSyncPointTime.durationTo(newTime).toMilliseconds()}); std.debug.dumpCurrentStackTrace(.{}); } lastSyncPointTime = newTime; @@ -804,11 +804,11 @@ pub const GarbageCollection = struct { // MARK: GarbageCollection const startCycle = threadCycle; while(threadCycle == startCycle) { syncPoint(); - std.Thread.sleep(1_000_000); + main.io.sleep(.fromMilliseconds(1), .awake) catch {}; } while(threadCycle != startCycle) { syncPoint(); - std.Thread.sleep(1_000_000); + main.io.sleep(.fromMilliseconds(1), .awake) catch {}; } } }; From cc4382bc59c3592867df7c85226ccaa91af2d186 Mon Sep 17 00:00:00 2001 From: IntegratedQuantum Date: Sat, 22 Nov 2025 16:50:11 +0100 Subject: [PATCH 04/20] Update .zigversion and scripts --- .zigversion | 2 +- debug_linux.sh | 2 +- debug_windows.bat | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.zigversion b/.zigversion index 8bc08821bb..0dba516b55 100644 --- a/.zigversion +++ b/.zigversion @@ -1 +1 @@ -0.15.0-dev.1034+bd97b6618 \ No newline at end of file +0.16.0-dev.1424+d3e20e71b \ No newline at end of file diff --git a/debug_linux.sh b/debug_linux.sh index ca473dbc76..d48ce7a8fc 100755 --- a/debug_linux.sh +++ b/debug_linux.sh @@ -14,7 +14,7 @@ fi echo "Building Zig Cubyz ($@) from source. This may take a few minutes..." -./compiler/zig/zig build --prominent-compile-errors "$@" +./compiler/zig/zig build --error-style minimal "$@" if [ $? != 0 ] then diff --git a/debug_windows.bat b/debug_windows.bat index 0e2e82d70e..213eeeaf92 100644 --- a/debug_windows.bat +++ b/debug_windows.bat @@ -10,7 +10,7 @@ if errorlevel 1 ( echo Building Zig Cubyz (%*^) from source. This may take a few minutes... -compiler\zig\zig build --prominent-compile-errors %* +compiler\zig\zig build --error-style minimal %* if errorlevel 1 ( exit /b 1 From 35fc3a152d954ec6473f309c021dc8b342c7753d Mon Sep 17 00:00:00 2001 From: IntegratedQuantum Date: Sat, 22 Nov 2025 16:50:37 +0100 Subject: [PATCH 05/20] fix network hostname lookup code --- src/network.zig | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/src/network.zig b/src/network.zig index db4e955b9b..5d87ff8e73 100644 --- a/src/network.zig +++ b/src/network.zig @@ -103,10 +103,26 @@ const Socket = struct { return buffer[0..length]; } - fn resolveIP(addr: []const u8) !u32 { - const list = try std.net.getAddressList(main.stackAllocator.allocator, addr, settings.defaultPort); - defer list.deinit(); - return list.addrs[0].in.sa.addr; + fn resolveIP(name: []const u8) !u32 { + var nameBuf: [255]u8 = undefined; + var buf: [16]std.Io.net.HostName.LookupResult = undefined; + var resultQueue = std.Io.Queue(std.Io.net.HostName.LookupResult).init(&buf); + std.Io.net.HostName.lookup(.{.bytes = name}, main.io, &resultQueue, .{.canonical_name_buffer = &nameBuf, .port = 0}); + while(true) { + const entry = resultQueue.getOneUncancelable(main.io); + switch(entry) { + .address => |addr| { + if(addr != .ip4) continue; + return std.mem.bytesToValue(u32, addr.ip4.bytes[0..4]); + }, + .canonical_name => {}, + .end => |err| { + try err; + break; + }, + } + } + return error.ReachedEndWithoutFindingAnything; } fn getPort(self: Socket) !u16 { From 169cf142f5cb764ef60ef3aabd93917bced7a883 Mon Sep 17 00:00:00 2001 From: IntegratedQuantum Date: Sat, 22 Nov 2025 17:04:41 +0100 Subject: [PATCH 06/20] Always use the same version formatter as the zig version --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4dc8566acf..8c8835b8e5 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -19,7 +19,7 @@ jobs: with: version: ${{ env.VERSION }} - run: sudo apt install glslang-tools - - run: wget -O ./Cubyz-fmt https://github.com/PixelGuys/Cubyz-formatter/releases/download/0.15.0-dev.1034%2Bbd97b6618/Cubyz-fmt-x86_64-linux + - run: wget -O ./Cubyz-fmt https://github.com/PixelGuys/Cubyz-formatter/releases/download/${{ env.VERSION }}/Cubyz-fmt-x86_64-linux - run: chmod +x ./Cubyz-fmt - run: | if ./Cubyz-fmt *.zig src/*.zig src/*/*.zig src/*/*/*.zig src/*/*/*/*.zig src/*/*/*/*/*.zig src/*/*/*/*/*/*.zig src/*/*/*/*/*/*/*.zig src/*/*/*/*/*/*/*/*.zig src/*/*/*/*/*/*/*/*/*.zig src/*/*/*/*/*/*/*/*/*/*.zig | grep -q . ; then From 1e6c8b2e4454f6978b38f2a99f86f13f4c38e1a3 Mon Sep 17 00:00:00 2001 From: IntegratedQuantum Date: Sat, 22 Nov 2025 20:30:01 +0100 Subject: [PATCH 07/20] Actually, we should hardcode the version there, so we can update the formatter and zig version independently --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8c8835b8e5..9b1e89bc6b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -19,7 +19,7 @@ jobs: with: version: ${{ env.VERSION }} - run: sudo apt install glslang-tools - - run: wget -O ./Cubyz-fmt https://github.com/PixelGuys/Cubyz-formatter/releases/download/${{ env.VERSION }}/Cubyz-fmt-x86_64-linux + - run: wget -O ./Cubyz-fmt https://github.com/PixelGuys/Cubyz-formatter/releases/download/0.16.0-dev.1424+d3e20e71b/Cubyz-fmt-x86_64-linux - run: chmod +x ./Cubyz-fmt - run: | if ./Cubyz-fmt *.zig src/*.zig src/*/*.zig src/*/*/*.zig src/*/*/*/*.zig src/*/*/*/*/*.zig src/*/*/*/*/*/*.zig src/*/*/*/*/*/*/*.zig src/*/*/*/*/*/*/*/*.zig src/*/*/*/*/*/*/*/*/*.zig src/*/*/*/*/*/*/*/*/*/*.zig | grep -q . ; then From 0f2c6c0ecbfd56fd49b89ed8ff7f529a220d6ddd Mon Sep 17 00:00:00 2001 From: IntegratedQuantum Date: Sat, 22 Nov 2025 20:36:21 +0100 Subject: [PATCH 08/20] aligncast comes first --- src/utils/file_monitor.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils/file_monitor.zig b/src/utils/file_monitor.zig index 3ef328f5c0..411aa315f4 100644 --- a/src/utils/file_monitor.zig +++ b/src/utils/file_monitor.zig @@ -135,7 +135,7 @@ const LinuxImpl = struct { // MARK: LinuxImpl defer triggeredCallbacks.deinit(); var offset: usize = 0; while(offset < available) { - const eventPtr: *const c.inotify_event = @alignCast(@ptrCast(events.ptr[offset..])); + const eventPtr: *const c.inotify_event = @ptrCast(@alignCast(events.ptr[offset..])); defer offset += @sizeOf(c.inotify_event) + eventPtr.len; const callback = callbacks.get(eventPtr.wd) orelse continue; From 16d29acc6eeb8aa06c5ee1e0ee4dc11240e19162 Mon Sep 17 00:00:00 2001 From: IntegratedQuantum Date: Sat, 22 Nov 2025 21:39:02 +0100 Subject: [PATCH 09/20] fix windows socket problems --- src/network.zig | 113 +++++++++++++++++++++++++++++++++++------------- 1 file changed, 84 insertions(+), 29 deletions(-) diff --git a/src/network.zig b/src/network.zig index 5d87ff8e73..f1d992323d 100644 --- a/src/network.zig +++ b/src/network.zig @@ -18,12 +18,36 @@ inline fn networkTimestamp() i64 { } const Socket = struct { + const ws2 = @cImport({ + @cInclude("winsock2.h"); + }); const posix = std.posix; - socketID: posix.socket_t, + socketID: if(builtin.os.tag == .windows) ws2.SOCKET else posix.socket_t, + + fn windowsError(err: c_int) !void { + if(err == 0) return; + switch(err) { + ws2.WSASYSNOTREADY => return error.WSASYSNOTREADY, + ws2.WSAVERNOTSUPPORTED => return error.WSAVERNOTSUPPORTED, + ws2.WSAEINPROGRESS => return error.WSAEINPROGRESS, + ws2.WSAEPROCLIM => return error.WSAEPROCLIM, + ws2.WSAEFAULT => return error.WSAEFAULT, + ws2.WSANOTINITIALISED => return error.WSANOTINITIALISED, + ws2.WSAENETDOWN => return error.WSAENETDOWN, + ws2.WSAEACCES => return error.WSAEACCES, + ws2.WSAEADDRINUSE => return error.WSAEADDRINUSE, + ws2.WSAEADDRNOTAVAIL => return error.WSAEADDRNOTAVAIL, + ws2.WSAEINVAL => return error.WSAEINVAL, + ws2.WSAENOBUFS => return error.WSAENOBUFS, + ws2.WSAENOTSOCK => return error.WSAENOTSOCK, + else => return error.UNKNOWN, + } + } fn startup() void { if(builtin.os.tag == .windows) { - _ = std.os.windows.WSAStartup(2, 2) catch |err| { + var data: ws2.WSADATA = undefined; + windowsError(ws2.WSAStartup(0x0202, &data)) catch |err| { std.log.err("Could not initialize the Windows Socket API: {s}", .{@errorName(err)}); @panic("Could not init networking."); }; @@ -32,19 +56,40 @@ const Socket = struct { fn init(localPort: u16) !Socket { const self = Socket{ - .socketID = try posix.socket(posix.AF.INET, posix.SOCK.DGRAM, posix.IPPROTO.UDP), + .socketID = blk: { + if(builtin.os.tag == .windows) { + const socket = ws2.socket(ws2.AF_INET, ws2.SOCK_DGRAM, ws2.IPPROTO_UDP); + if(socket == ws2.INVALID_SOCKET) { + try windowsError(ws2.WSAGetLastError()); + return error.UNKNOWN; + } + break :blk socket; + } else { + break :blk try posix.socket(posix.AF.INET, posix.SOCK.DGRAM, posix.IPPROTO.UDP); + } + }, }; errdefer self.deinit(); const bindingAddr = posix.sockaddr.in{ .port = @byteSwap(localPort), .addr = 0, }; - try posix.bind(self.socketID, @ptrCast(&bindingAddr), @sizeOf(posix.sockaddr.in)); + if(builtin.os.tag == .windows) { + if(ws2.bind(self.socketID, @ptrCast(&bindingAddr), @sizeOf(posix.sockaddr.in)) == ws2.SOCKET_ERROR) { + try windowsError(ws2.WSAGetLastError()); + } + } else { + try posix.bind(self.socketID, @ptrCast(&bindingAddr), @sizeOf(posix.sockaddr.in)); + } return self; } fn deinit(self: Socket) void { - posix.close(self.socketID); + if(builtin.os.tag == .windows) { + _ = ws2.closesocket(self.socketID); + } else { + posix.close(self.socketID); + } } fn send(self: Socket, data: []const u8, destination: Address) void { @@ -52,19 +97,17 @@ const Socket = struct { .port = @byteSwap(destination.port), .addr = destination.ip, }; - if(builtin.os.tag == .windows) { // TODO: Upstream error, fix after next Zig update after #24466 is merged - const sendto = struct { - extern "c" fn sendto(sockfd: posix.system.fd_t, buf: *const anyopaque, len: usize, flags: u32, dest_addr: ?*const posix.system.sockaddr, addrlen: posix.system.socklen_t) c_int; - }.sendto; - const result = sendto(self.socketID, data.ptr, data.len, 0, @ptrCast(&addr), @sizeOf(posix.sockaddr.in)); - if(result < 0) { - std.log.info("Got error while sending to {f}: {s}", .{destination, @tagName(std.os.windows.ws2_32.WSAGetLastError())}); + if(builtin.os.tag == .windows) { + const result = ws2.sendto(self.socketID, data.ptr, @intCast(data.len), 0, @ptrCast(&addr), @sizeOf(posix.sockaddr.in)); + if(result == ws2.SOCKET_ERROR) { + const err: anyerror = if(windowsError(ws2.WSAGetLastError())) error.Unknown else |err| err; + std.log.warn("Got error while sending to {f}: {s}", .{destination, @errorName(err)}); } else { std.debug.assert(@as(usize, @intCast(result)) == data.len); } } else { std.debug.assert(data.len == posix.sendto(self.socketID, data, 0, @ptrCast(&addr), @sizeOf(posix.sockaddr.in)) catch |err| { - std.log.info("Got error while sending to {f}: {s}", .{destination, @errorName(err)}); + std.log.warn("Got error while sending to {f}: {s}", .{destination, @errorName(err)}); return; }); } @@ -72,20 +115,14 @@ const Socket = struct { fn receive(self: Socket, buffer: []u8, timeout: i32, resultAddress: *Address) ![]u8 { if(builtin.os.tag == .windows) { // Of course Windows always has it's own special thing. - var pfd = [1]posix.pollfd{ + var pfd = [1]ws2.pollfd{ .{.fd = self.socketID, .events = std.c.POLL.RDNORM | std.c.POLL.RDBAND, .revents = undefined}, }; - const length = std.os.windows.ws2_32.WSAPoll(&pfd, pfd.len, 0); // The timeout is set to zero. Otherwise sendto operations from other threads will block on this. - if(length == std.os.windows.ws2_32.SOCKET_ERROR) { - switch(std.os.windows.ws2_32.WSAGetLastError()) { - .WSANOTINITIALISED => unreachable, - .WSAENETDOWN => return error.NetworkSubsystemFailed, - .WSAENOBUFS => return error.SystemResources, - // TODO: handle more errors - else => |err| return std.os.windows.unexpectedWSAError(err), - } + const length = ws2.WSAPoll(&pfd, pfd.len, 0); // The timeout is set to zero. Otherwise sendto operations from other threads will block on this. + if(length == ws2.SOCKET_ERROR) { + try windowsError(ws2.WSAGetLastError()); } else if(length == 0) { - std.Thread.sleep(1000000); // Manually sleep, since WSAPoll is blocking. + main.io.sleep(.fromMilliseconds(1), .awake) catch {}; // Manually sleep, since WSAPoll is blocking. return error.Timeout; } } else { @@ -96,8 +133,19 @@ const Socket = struct { if(length == 0) return error.Timeout; } var addr: posix.sockaddr.in = undefined; - var addrLen: posix.socklen_t = @sizeOf(posix.sockaddr.in); - const length = try posix.recvfrom(self.socketID, buffer, 0, @ptrCast(&addr), &addrLen); + const length: usize = blk: { + if(builtin.os.tag == .windows) { + var addrLen: c_int = @sizeOf(posix.sockaddr.in); + const result = ws2.recvfrom(self.socketID, buffer.ptr, @intCast(buffer.len), 0, @ptrCast(&addr), &addrLen); + if(result == ws2.SOCKET_ERROR) { + try windowsError(ws2.WSAGetLastError()); + } + break :blk @intCast(result); + } else { + var addrLen: posix.socklen_t = @sizeOf(posix.sockaddr.in); + break :blk try posix.recvfrom(self.socketID, buffer, 0, @ptrCast(&addr), &addrLen); + } + }; resultAddress.ip = addr.addr; resultAddress.port = @byteSwap(addr.port); return buffer[0..length]; @@ -127,8 +175,15 @@ const Socket = struct { fn getPort(self: Socket) !u16 { var addr: posix.sockaddr.in = undefined; - var addrLen: posix.socklen_t = @sizeOf(posix.sockaddr.in); - try posix.getsockname(self.socketID, @ptrCast(&addr), &addrLen); + if(builtin.os.tag == .windows) { + var addrLen: c_int = @sizeOf(posix.sockaddr.in); + if(ws2.getsockname(self.socketID, @ptrCast(&addr), &addrLen) == ws2.SOCKET_ERROR) { + try windowsError(ws2.WSAGetLastError()); + } + } else { + var addrLen: posix.socklen_t = @sizeOf(posix.sockaddr.in); + try posix.getsockname(self.socketID, @ptrCast(&addr), &addrLen); + } return @byteSwap(addr.port); } }; @@ -1509,7 +1564,7 @@ pub const Connection = struct { // MARK: Connection self.manager.send(&.{@intFromEnum(ChannelId.disconnect)}, self.remoteAddress, null); self.connectionState.store(.disconnectDesired, .unordered); if(builtin.os.tag == .windows and !self.isServerSide() and main.server.world != null) { - std.Thread.sleep(10000000); // Windows is too eager to close the socket, without waiting here we get a ConnectionResetByPeer on the other side. + main.io.sleep(.fromMilliseconds(10), .awake) catch {}; // Windows is too eager to close the socket, without waiting here we get a ConnectionResetByPeer on the other side. } self.manager.removeConnection(self); if(self.user) |user| { From 7d00d0d2394efa5e66e288ad4874b8887caab726 Mon Sep 17 00:00:00 2001 From: IntegratedQuantum Date: Sat, 22 Nov 2025 21:48:01 +0100 Subject: [PATCH 10/20] Disable aarch64-windows target in the CI --- .github/workflows/ci.yml | 2 +- .github/workflows/release.yml | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9b1e89bc6b..b57db7e364 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -44,7 +44,7 @@ jobs: - run: zig build - run: zig build -Dtarget=x86_64-windows-gnu - run: zig build -Dtarget=aarch64-linux-gnu - - run: zig build -Dtarget=aarch64-windows-gnu +# - run: zig build -Dtarget=aarch64-windows-gnu - run: zig build test - run: zig build format --summary none - run: find assets/cubyz/shaders -type f | xargs -L1 glslangValidator -G100 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 2fd64d9836..03e0baca2c 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -29,9 +29,9 @@ jobs: - run: tar -czf Windows-x86_64.tar.gz --transform 's,^,Cubyz/,' assets/cubyz launchConfig.zon -C zig-out/bin Cubyz.exe - run: tar -xf Windows-x86_64.tar.gz && zip -r Windows-x86_64.zip Cubyz && rm -r Cubyz - - run: zig build -Dtarget=aarch64-windows-gnu -Doptimize=ReleaseFast -Drelease=true -Dcpu=baseline - - run: tar -czf Windows-aarch64.tar.gz --transform 's,^,Cubyz/,' assets/cubyz launchConfig.zon -C zig-out/bin Cubyz.exe - - run: tar -xf Windows-aarch64.tar.gz && zip -r Windows-aarch64.zip Cubyz && rm -r Cubyz +# - run: zig build -Dtarget=aarch64-windows-gnu -Doptimize=ReleaseFast -Drelease=true -Dcpu=baseline +# - run: tar -czf Windows-aarch64.tar.gz --transform 's,^,Cubyz/,' assets/cubyz launchConfig.zon -C zig-out/bin Cubyz.exe +# - run: tar -xf Windows-aarch64.tar.gz && zip -r Windows-aarch64.zip Cubyz && rm -r Cubyz - name: Create release env: @@ -43,6 +43,6 @@ jobs: --title="${GITHUB_REPOSITORY#*/} ${tag#v}" \ Linux-x86_64.tar.gz \ Linux-aarch64.tar.gz \ - Windows-x86_64.zip \ - Windows-aarch64.zip + Windows-x86_64.zip +# Windows-aarch64.zip gh release From 3a1956be67ee7757eef9bcf9879b1e6ecb88a423 Mon Sep 17 00:00:00 2001 From: IntegratedQuantum Date: Sat, 22 Nov 2025 21:54:01 +0100 Subject: [PATCH 11/20] Update test runner dependency --- build.zig.zon | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build.zig.zon b/build.zig.zon index 112302fd8e..cbfbbdb22d 100644 --- a/build.zig.zon +++ b/build.zig.zon @@ -47,8 +47,8 @@ .hash = "N-V-__8AACQYrweKK0-hEabTekSwmGwxKrIbdY_2QTOEA7TN", }, .cubyz_test_runner = .{ - .url = "https://github.com/PixelGuys/Cubyz-std-lib/archive/refs/tags/0.15.0-dev.1034+bd97b6618+runner-build.zig.tar.gz", - .hash = "zig-0.0.0-Fp4XJConDA0olqTe3d3oYNH6UOlQb_jIZ0YOWKZGHLYQ", + .url = "https://github.com/PixelGuys/Cubyz-std-lib/archive/refs/tags/0.16.0-dev.1424+1.tar.gz", + .hash = "zig-0.0.0-Fp4XJAXmXQ1r23tqehV14LpPFILnnXSyg7bPitqs8mrf", .lazy = true, }, }, From c44b3e98195a9a559df1672e5dd6dc874ae28588 Mon Sep 17 00:00:00 2001 From: IntegratedQuantum Date: Sat, 22 Nov 2025 22:02:08 +0100 Subject: [PATCH 12/20] Remove commented line in release.yml which seems to break everything --- .github/workflows/release.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 03e0baca2c..949b34d331 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -44,5 +44,4 @@ jobs: Linux-x86_64.tar.gz \ Linux-aarch64.tar.gz \ Windows-x86_64.zip -# Windows-aarch64.zip gh release From 611b15cc9f4f90cbcc5b83f59ad4e634000ae5f4 Mon Sep 17 00:00:00 2001 From: IntegratedQuantum Date: Sat, 22 Nov 2025 22:04:51 +0100 Subject: [PATCH 13/20] Fix the formatter --- src/formatter/format.zig | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/formatter/format.zig b/src/formatter/format.zig index b36d9718dd..a8f357d44f 100644 --- a/src/formatter/format.zig +++ b/src/formatter/format.zig @@ -22,13 +22,13 @@ fn printError(msg: []const u8, filePath: []const u8, data: []const u8, charIndex } } - var startLineChars = std.ArrayList(u8).init(globalAllocator); - defer startLineChars.deinit(); + var startLineChars: std.ArrayList(u8) = .{}; + defer startLineChars.deinit(globalAllocator); for(data[lineStart..charIndex]) |c| { if(c == '\t') { - startLineChars.append('\t') catch {}; + startLineChars.append(globalAllocator, '\t') catch {}; } else { - startLineChars.append(' ') catch {}; + startLineChars.append(globalAllocator, ' ') catch {}; } } @@ -38,7 +38,7 @@ fn printError(msg: []const u8, filePath: []const u8, data: []const u8, charIndex } fn checkFile(dir: std.fs.Dir, filePath: []const u8) !void { - const data = try dir.readFileAlloc(globalAllocator, filePath, std.math.maxInt(usize)); + const data = try dir.readFileAlloc(filePath, globalAllocator, .unlimited); defer globalAllocator.free(data); var lineStart: bool = true; From a56318da0a7219904fd0838934859fbcc42835ce Mon Sep 17 00:00:00 2001 From: IntegratedQuantum Date: Sun, 23 Nov 2025 12:19:22 +0100 Subject: [PATCH 14/20] Switch to real time on windows --- src/main.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main.zig b/src/main.zig index d58e77ead5..9409b6f7d4 100644 --- a/src/main.zig +++ b/src/main.zig @@ -67,7 +67,7 @@ pub fn deinitThreadLocals() void { } pub fn timestamp() std.Io.Timestamp { - return (std.Io.Clock.Timestamp.now(io, .awake) catch unreachable).raw; + return (std.Io.Clock.Timestamp.now(io, if(@import("builtin").os.tag == .windows) .real else .awake) catch unreachable).raw; // TODO: On windows the awake time is broken } fn cacheStringImpl(comptime len: usize, comptime str: [len]u8) []const u8 { From 942738b6e1bf69decd51864930bd58b43168325d Mon Sep 17 00:00:00 2001 From: IntegratedQuantum Date: Sun, 23 Nov 2025 12:25:51 +0100 Subject: [PATCH 15/20] Make the controller code work again --- src/graphics/Window.zig | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/graphics/Window.zig b/src/graphics/Window.zig index 0cc47f9ebc..d8f4ed74fa 100644 --- a/src/graphics/Window.zig +++ b/src/graphics/Window.zig @@ -167,15 +167,15 @@ pub const Gamepad = struct { pub fn run(self: *ControllerMappingDownloadTask) void { std.log.info("Starting controller mapping download...", .{}); defer self.clean(); - var client: std.http.Client = .{.allocator = main.stackAllocator.allocator}; + var client: std.http.Client = .{.allocator = main.stackAllocator.allocator, .io = main.io}; defer client.deinit(); - var list = std.ArrayList(u8).init(main.stackAllocator.allocator); - defer list.deinit(); + var writer = std.Io.Writer.Allocating.init(main.stackAllocator.allocator); + defer writer.deinit(); defer controllerMappingsDownloaded.store(true, std.builtin.AtomicOrder.release); const fetchResult = client.fetch(.{ .method = .GET, .location = .{.url = "https://raw.githubusercontent.com/mdqinc/SDL_GameControllerDB/master/gamecontrollerdb.txt"}, - .response_storage = .{.dynamic = &list}, + .response_writer = &writer.writer, }) catch |err| { std.log.err("Failed to download controller mappings: {s}", .{@errorName(err)}); return; @@ -184,7 +184,7 @@ pub const Gamepad = struct { std.log.err("Failed to download controller mappings: HTTP error {d}", .{@intFromEnum(fetchResult.status)}); return; } - files.cwd().write("./gamecontrollerdb.txt", list.items) catch |err| { + files.cwd().write("./gamecontrollerdb.txt", writer.written()) catch |err| { std.log.err("Failed to write controller mappings: {s}", .{@errorName(err)}); return; }; @@ -206,11 +206,11 @@ pub const Gamepad = struct { pub fn downloadControllerMappings() void { if(builtin.mode == .Debug) return; // TODO: The http fetch adds ~5 seconds to the compile time, so it's disabled in debug mode, see #24435 var needsDownload: bool = false; - const curTimestamp = std.time.nanoTimestamp(); - const timestamp: i128 = blk: { + const curTimestamp: i96 = (std.Io.Clock.Timestamp.now(main.io, .real) catch unreachable).raw.nanoseconds; + const timestamp: i96 = blk: { const stamp = files.cwd().read(main.stackAllocator, "./gamecontrollerdb.stamp") catch break :blk 0; defer main.stackAllocator.free(stamp); - break :blk std.fmt.parseInt(i128, stamp, 16) catch 0; + break :blk std.fmt.parseInt(i96, stamp, 16) catch 0; }; const delta = curTimestamp -% timestamp; needsDownload = delta >= 7*std.time.ns_per_day; From 37e7f08a83fa5bd13319a79a8130c979d5b418a4 Mon Sep 17 00:00:00 2001 From: IntegratedQuantum Date: Sun, 23 Nov 2025 13:01:42 +0100 Subject: [PATCH 16/20] Switch run to ReleaseFast since ReleaseSafe doesn't compile --- run_linux.sh | 2 +- run_windows.bat | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/run_linux.sh b/run_linux.sh index a4c9e9c06f..879b7385c6 100755 --- a/run_linux.sh +++ b/run_linux.sh @@ -2,7 +2,7 @@ cd "$(dirname "$0")" -./debug_linux.sh -Doptimize=ReleaseSafe "$@" +./debug_linux.sh -Doptimize=ReleaseFast "$@" if [ ! $NO_PAUSE ]; then echo "Press enter key to continue. (Or export NO_PAUSE=1 to skip this prompt.)" diff --git a/run_windows.bat b/run_windows.bat index 6b9f6cd5e6..bdcfd31975 100644 --- a/run_windows.bat +++ b/run_windows.bat @@ -2,7 +2,7 @@ cd /D "%~dp0" -call debug_windows.bat -Doptimize=ReleaseSafe %* +call debug_windows.bat -Doptimize=ReleaseFast %* IF "%NO_PAUSE%" == "" ( echo Press enter key to continue. (Or set NO_PAUSE=1 to skip this prompt.^) From 71490a95961a0992c981ebfb6fafc964c4fd4fcf Mon Sep 17 00:00:00 2001 From: IntegratedQuantum Date: Sun, 23 Nov 2025 19:00:28 +0100 Subject: [PATCH 17/20] Reinstate ReleaseSafe on Linux --- run_linux.sh | 2 +- src/audio.zig | 1 + src/callbacks/callbacks.zig | 1 + src/graphics.zig | 2 ++ 4 files changed, 5 insertions(+), 1 deletion(-) diff --git a/run_linux.sh b/run_linux.sh index 879b7385c6..a4c9e9c06f 100755 --- a/run_linux.sh +++ b/run_linux.sh @@ -2,7 +2,7 @@ cd "$(dirname "$0")" -./debug_linux.sh -Doptimize=ReleaseFast "$@" +./debug_linux.sh -Doptimize=ReleaseSafe "$@" if [ ! $NO_PAUSE ]; then echo "Press enter key to continue. (Or export NO_PAUSE=1 to skip this prompt.)" diff --git a/src/audio.zig b/src/audio.zig index 344b8650b1..590ff8e592 100644 --- a/src/audio.zig +++ b/src/audio.zig @@ -4,6 +4,7 @@ const main = @import("main"); const utils = main.utils; const c = @cImport({ + @cDefine("_BITS_STDIO2_H", ""); // TODO: Zig fails to include this header file @cInclude("miniaudio.h"); @cDefine("STB_VORBIS_HEADER_ONLY", ""); @cInclude("stb/stb_vorbis.h"); diff --git a/src/callbacks/callbacks.zig b/src/callbacks/callbacks.zig index 9fecdf5f14..cbff464b1a 100644 --- a/src/callbacks/callbacks.zig +++ b/src/callbacks/callbacks.zig @@ -9,6 +9,7 @@ pub const ClientBlockCallback = Callback(struct {block: Block, blockPos: Vec3i}, pub const ServerBlockCallback = Callback(struct {block: Block, chunk: *main.chunk.ServerChunk, x: i32, y: i32, z: i32}, @import("block/server/_list.zig")); pub const BlockTouchCallback = Callback(struct {entity: *main.server.Entity, source: Block, blockPos: Vec3i, deltaTime: f64}, @import("block/touch/_list.zig")); +pub const ChunkEventCallback = Callback(struct {chunk: *main.server.ServerChunk}, @import("chunk/_list.zig")); pub const Result = enum {handled, ignored}; diff --git a/src/graphics.zig b/src/graphics.zig index cf4c5ce24c..6d4e2c913d 100644 --- a/src/graphics.zig +++ b/src/graphics.zig @@ -3,6 +3,7 @@ const std = @import("std"); pub const hbft = @cImport({ + @cDefine("_BITS_STDIO2_H", ""); // TODO: Zig fails to include this header file @cInclude("freetype/ftadvanc.h"); @cInclude("freetype/ftbbox.h"); @cInclude("freetype/ftbitmap.h"); @@ -35,6 +36,7 @@ pub const c = @cImport({ }); pub const stb_image = @cImport({ + @cDefine("_BITS_STDIO2_H", ""); // TODO: Zig fails to include this header file @cInclude("stb/stb_image.h"); @cInclude("stb/stb_image_write.h"); }); From fc4df86028bad08c3efe7702856d8baf2044e5b5 Mon Sep 17 00:00:00 2001 From: IntegratedQuantum Date: Sun, 23 Nov 2025 19:06:37 +0100 Subject: [PATCH 18/20] unintended change --- src/callbacks/callbacks.zig | 1 - 1 file changed, 1 deletion(-) diff --git a/src/callbacks/callbacks.zig b/src/callbacks/callbacks.zig index cbff464b1a..9fecdf5f14 100644 --- a/src/callbacks/callbacks.zig +++ b/src/callbacks/callbacks.zig @@ -9,7 +9,6 @@ pub const ClientBlockCallback = Callback(struct {block: Block, blockPos: Vec3i}, pub const ServerBlockCallback = Callback(struct {block: Block, chunk: *main.chunk.ServerChunk, x: i32, y: i32, z: i32}, @import("block/server/_list.zig")); pub const BlockTouchCallback = Callback(struct {entity: *main.server.Entity, source: Block, blockPos: Vec3i, deltaTime: f64}, @import("block/touch/_list.zig")); -pub const ChunkEventCallback = Callback(struct {chunk: *main.server.ServerChunk}, @import("chunk/_list.zig")); pub const Result = enum {handled, ignored}; From a05ca39b4196ffd0cb0cc34848029790d95c7c8f Mon Sep 17 00:00:00 2001 From: IntegratedQuantum Date: Sun, 23 Nov 2025 19:27:12 +0100 Subject: [PATCH 19/20] Fix the advanced controls slider range --- src/gui/windows/advanced_controls.zig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/gui/windows/advanced_controls.zig b/src/gui/windows/advanced_controls.zig index f8b462327d..b0954870ac 100644 --- a/src/gui/windows/advanced_controls.zig +++ b/src/gui/windows/advanced_controls.zig @@ -40,8 +40,8 @@ fn speedFormatter(allocator: main.heap.NeverFailingAllocator, value: f32) []cons pub fn onOpen() void { const list = VerticalList.init(.{padding, 16 + padding}, 300, 16); - list.add(ContinuousSlider.init(.{0, 0}, 128, 1.0, 1000.0, @floatFromInt(settings.updateRepeatDelay.nanoseconds), &delayCallback, &delayFormatter)); - list.add(ContinuousSlider.init(.{0, 0}, 128, 1.0, 500.0, @floatFromInt(settings.updateRepeatSpeed.nanoseconds), &speedCallback, &speedFormatter)); + list.add(ContinuousSlider.init(.{0, 0}, 128, 1.0e6, 1.0e9, @floatFromInt(settings.updateRepeatDelay.nanoseconds), &delayCallback, &delayFormatter)); + list.add(ContinuousSlider.init(.{0, 0}, 128, 1.0e6, 0.5e9, @floatFromInt(settings.updateRepeatSpeed.nanoseconds), &speedCallback, &speedFormatter)); list.finish(.center); window.rootComponent = list.toComponent(); window.contentSize = window.rootComponent.?.pos() + window.rootComponent.?.size() + @as(Vec2f, @splat(padding)); From 96fdc11ddb621b1447ca65ec4da77cf2dffc058d Mon Sep 17 00:00:00 2001 From: IntegratedQuantum Date: Sun, 23 Nov 2025 20:48:56 +0100 Subject: [PATCH 20/20] I remember now why this needs to use fast compression --- src/server/terrain/SurfaceMap.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/server/terrain/SurfaceMap.zig b/src/server/terrain/SurfaceMap.zig index bd71ebeba0..c6c2db87fd 100644 --- a/src/server/terrain/SurfaceMap.zig +++ b/src/server/terrain/SurfaceMap.zig @@ -188,7 +188,7 @@ pub const MapFragment = struct { // MARK: MapFragment for(0..mapSize) |x| for(0..mapSize) |y| writer.writeInt(i32, self.heightMap[x][y]); for(0..mapSize) |x| for(0..mapSize) |y| writer.writeInt(i32, (if(originalData) |map| map else &self.heightMap)[x][y]); - const compressedData = main.utils.Compression.deflate(main.stackAllocator, writer.data.items, .default); + const compressedData = main.utils.Compression.deflate(main.stackAllocator, writer.data.items, .fastest); // Using fast to increase performance of the regenerating map LODs step defer main.stackAllocator.free(compressedData); var outputWriter = BinaryWriter.initCapacity(main.stackAllocator, @sizeOf(StorageHeader) + compressedData.len);