diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 37a531181b..61948d972b 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.16.0+0/Cubyz-fmt-x86_64-linux + - run: wget -O ./Cubyz-fmt https://github.com/PixelGuys/Cubyz-formatter/releases/download/0.17.0+0/Cubyz-fmt-x86_64-linux - run: chmod +x ./Cubyz-fmt - run: | shopt -s nullglob @@ -31,7 +31,7 @@ jobs: git diff --color=always; exit 1; fi - - run: wget -O ./Cubyz-linter https://github.com/PixelGuys/Cubyz-linter/releases/download/0.16.0+6/Cubyz-linter-x86_64-linux + - run: wget -O ./Cubyz-linter https://github.com/PixelGuys/Cubyz-linter/releases/download/0.17.0+0/Cubyz-linter-x86_64-linux - run: chmod +x ./Cubyz-linter - run: ./Cubyz-linter assets/ mods/ src/ *.zon - run: 'find assets/cubyz/shaders -type f ! -name "*.glsl" | xargs -L1 glslangValidator -G100 -P"#extension GL_GOOGLE_include_directive : enable" -Iassets/cubyz/shaders/include -Dgl_VertexIndex=gl_VertexID -DOPEN_GL' diff --git a/.zigversion b/.zigversion index d183d4ace0..07feb82349 100644 --- a/.zigversion +++ b/.zigversion @@ -1 +1 @@ -0.16.0 \ No newline at end of file +0.17.0 \ No newline at end of file diff --git a/build.zig b/build.zig index 4cb5714efa..76a1fb0f26 100644 --- a/build.zig +++ b/build.zig @@ -242,9 +242,7 @@ pub fn build(b: *std.Build) !void { const run_cmd = b.addRunArtifact(exe); run_cmd.step.dependOn(b.getInstallStep()); - if (b.args) |args| { - run_cmd.addArgs(args); - } + run_cmd.addPassthruArgs(); const run_step = b.step("run", "Run the app"); run_step.dependOn(&run_cmd.step); diff --git a/build.zig.zon b/build.zig.zon index 90e68ec5a7..368c3d7115 100644 --- a/build.zig.zon +++ b/build.zig.zon @@ -47,8 +47,8 @@ .hash = "N-V-__8AABst_gbWhL_Rl--L4bUSOeu2484UhidGfW1DNlOJ", }, .cubyz_test_runner = .{ - .url = "https://github.com/PixelGuys/Cubyz-std-lib/archive/refs/tags/0.16.0+1.tar.gz", - .hash = "zig-0.0.0-Fp4XJG_h5A3OLpPXqVTJ1DRl3lZhqHp_fClrI7MUVjU_", + .url = "https://github.com/PixelGuys/Cubyz-std-lib/archive/refs/tags/0.17.0+0.tar.gz", + .hash = "zig-0.0.0-Fp4XJHPdSQ6qeLlHt5m6z6F4LAZbP4F8mJqo6ufkkqvX", .lazy = true, }, }, diff --git a/mods/cubyz/rotations/branch.zig b/mods/cubyz/rotations/branch.zig index a0698710f4..9f04f4ba91 100644 --- a/mods/cubyz/rotations/branch.zig +++ b/mods/cubyz/rotations/branch.zig @@ -88,7 +88,7 @@ fn rotateQuad(originalCorners: [4]Vec2f, pattern: Pattern, min: f32, max: f32, s switch (pattern) { .dot, .cross => {}, inline else => |typ| { - const angle: f32 = @as(f32, @floatFromInt(@intFromEnum(typ)))*std.math.pi/2.0; + const angle: f32 = @as(f32, @floatFromInt(@backingInt(typ)))*std.math.pi/2.0; corners = .{ vec.rotate2d(originalCorners[0], angle, @splat(0.5)), vec.rotate2d(originalCorners[1], angle, @splat(0.5)), @@ -119,7 +119,7 @@ fn rotateQuad(originalCorners: [4]Vec2f, pattern: Pattern, min: f32, max: f32, s }, .cornerUV = .{originalCorners[0], originalCorners[1], originalCorners[2], originalCorners[3]}, .normal = @as(Vec3f, @floatFromInt(side.relPos())), - .textureSlot = textureSlotOffset + @intFromEnum(pattern), + .textureSlot = textureSlotOffset + @backingInt(pattern), }; return res; @@ -360,7 +360,7 @@ pub fn rotateZ(data: u16, angle: Degrees) u16 { const rotationIndex = (data & 0b111100) >> 2; const upDownFlags = data & 0b000011; const runtimeTable = rotationTable; - return runtimeTable[@intFromEnum(angle)][rotationIndex] | upDownFlags; + return runtimeTable[@backingInt(angle)][rotationIndex] | upDownFlags; } pub fn generateData( diff --git a/mods/cubyz/rotations/carpet.zig b/mods/cubyz/rotations/carpet.zig index 7660c6d7fe..45ab65720b 100644 --- a/mods/cubyz/rotations/carpet.zig +++ b/mods/cubyz/rotations/carpet.zig @@ -49,7 +49,7 @@ pub fn rotateZ(data: u16, angle: Degrees) u16 { }; if (data >= 64) return 0; const runtimeTable = rotationTable; - return runtimeTable[@intFromEnum(angle)][data]; + return runtimeTable[@backingInt(angle)][data]; } pub fn init() void { diff --git a/mods/cubyz/rotations/direction.zig b/mods/cubyz/rotations/direction.zig index 8ba9677efc..4b460a7bb4 100644 --- a/mods/cubyz/rotations/direction.zig +++ b/mods/cubyz/rotations/direction.zig @@ -57,13 +57,13 @@ pub fn rotateZ(data: u16, angle: Degrees) u16 { }; comptime for (1..4) |a| { for (0..6) |i| { - const neighbor: Neighbor = @enumFromInt(rotationTable[a - 1][i]); + const neighbor: Neighbor = @fromBackingInt(rotationTable[a - 1][i]); rotationTable[a][i] = neighbor.rotateZ().toInt(); } }; if (data >= 6) return 0; const runtimeTable = rotationTable; - return runtimeTable[@intFromEnum(angle)][data]; + return runtimeTable[@backingInt(angle)][data]; } pub fn generateData(_: *main.game.World, _: Vec3i, _: Vec3f, _: Vec3f, _: Vec3i, neighbor: ?Neighbor, currentData: *Block, _: Block, blockPlacing: bool) bool { @@ -78,5 +78,5 @@ pub fn generateData(_: *main.game.World, _: Vec3i, _: Vec3f, _: Vec3f, _: Vec3i, pub fn updateBlockFromNeighborConnectivity(block: *Block, neighborSupportive: [6]bool) void { if (block.data >= 6) return; - if (!neighborSupportive[@as(Neighbor, @enumFromInt(block.data)).reverse().toInt()]) block.* = .air; + if (!neighborSupportive[@as(Neighbor, @fromBackingInt(@intCast(block.data))).reverse().toInt()]) block.* = .air; } diff --git a/mods/cubyz/rotations/fence.zig b/mods/cubyz/rotations/fence.zig index 2ee55a2356..4167b90063 100644 --- a/mods/cubyz/rotations/fence.zig +++ b/mods/cubyz/rotations/fence.zig @@ -53,7 +53,7 @@ pub fn rotateZ(data: u16, angle: Degrees) u16 { }; if (data >= 16) return 0; const runtimeTable = rotationTable; - return runtimeTable[@intFromEnum(angle)][data]; + return runtimeTable[@backingInt(angle)][data]; } fn fenceTransform(quad: *main.models.QuadInfo, data: FenceData) void { diff --git a/mods/cubyz/rotations/log.zig b/mods/cubyz/rotations/log.zig index 8c4c9bfa55..9c24a9b83f 100644 --- a/mods/cubyz/rotations/log.zig +++ b/mods/cubyz/rotations/log.zig @@ -69,7 +69,7 @@ fn rotateQuad(pattern: Pattern, side: Neighbor) main.models.QuadInfo { switch (pattern) { .dot, .cross, .cut => {}, .line => |dir| { - var angle: f32 = @as(f32, @floatFromInt(@intFromEnum(dir)))*std.math.pi/2.0; + var angle: f32 = @as(f32, @floatFromInt(@backingInt(dir)))*std.math.pi/2.0; if (side.relZ() != 0) { angle *= -1; } @@ -89,7 +89,7 @@ fn rotateQuad(pattern: Pattern, side: Neighbor) main.models.QuadInfo { .bend, .intersection => |dir| { corners = originalCorners; - const angle: f32 = -@as(f32, @floatFromInt(@intFromEnum(dir)))*std.math.pi/2.0; + const angle: f32 = -@as(f32, @floatFromInt(@backingInt(dir)))*std.math.pi/2.0; corners = .{ vec.rotate2d(originalCorners[0], angle, @splat(0.5)), vec.rotate2d(originalCorners[1], angle, @splat(0.5)), @@ -120,7 +120,7 @@ fn rotateQuad(pattern: Pattern, side: Neighbor) main.models.QuadInfo { }, .cornerUV = .{corners[0], corners[1], corners[2], corners[3]}, .normal = @as(Vec3f, @floatFromInt(side.relPos())), - .textureSlot = @intFromEnum(pattern), + .textureSlot = @backingInt(pattern), }; return res; diff --git a/mods/cubyz/rotations/planar.zig b/mods/cubyz/rotations/planar.zig index e4569aadf9..eef3fa62ce 100644 --- a/mods/cubyz/rotations/planar.zig +++ b/mods/cubyz/rotations/planar.zig @@ -55,13 +55,13 @@ pub fn rotateZ(data: u16, angle: Degrees) u16 { }; comptime for (1..4) |a| { for (0..4) |i| { - const neighbor: Neighbor = @enumFromInt(rotationTable[a - 1][i] + 2); + const neighbor: Neighbor = @fromBackingInt(rotationTable[a - 1][i] + 2); rotationTable[a][i] = neighbor.rotateZ().toInt() - 2; } }; if (data >= 4) return 0; const runtimeTable = rotationTable; - return runtimeTable[@intFromEnum(angle)][data]; + return runtimeTable[@backingInt(angle)][data]; } pub fn generateData(_: *main.game.World, _: Vec3i, _: Vec3f, playerDir: Vec3f, _: Vec3i, _: ?Neighbor, currentData: *Block, _: Block, blockPlacing: bool) bool { diff --git a/mods/cubyz/rotations/sign.zig b/mods/cubyz/rotations/sign.zig index ae09a613a4..805f54ee8f 100644 --- a/mods/cubyz/rotations/sign.zig +++ b/mods/cubyz/rotations/sign.zig @@ -89,7 +89,7 @@ pub fn rotateZ(data: u16, angle: Degrees) u16 { }, }; if (data >= 2*centerRotations + sideRotations) return 0; - return rotationTable[@intFromEnum(angle)][data]; + return rotationTable[@backingInt(angle)][data]; } fn getRotationFromDir(dir: Vec3f) u16 { diff --git a/mods/cubyz/rotations/stairs.zig b/mods/cubyz/rotations/stairs.zig index 2f35398f8b..44914a2774 100644 --- a/mods/cubyz/rotations/stairs.zig +++ b/mods/cubyz/rotations/stairs.zig @@ -51,7 +51,7 @@ pub fn rotateZ(data: u16, angle: Degrees) u16 { }; if (data >= 256) return 0; const runtimeTable = rotationTable; - return runtimeTable[@intFromEnum(angle)][data]; + return runtimeTable[@backingInt(angle)][data]; } pub fn init() void {} diff --git a/mods/cubyz/rotations/torch.zig b/mods/cubyz/rotations/torch.zig index 811d2da2d9..cd1db348d1 100644 --- a/mods/cubyz/rotations/torch.zig +++ b/mods/cubyz/rotations/torch.zig @@ -116,7 +116,7 @@ pub fn rotateZ(data: u16, angle: Degrees) u16 { }; if (data >= 32) return 0; const runtimeTable = rotationTable; - return runtimeTable[@intFromEnum(angle)][data]; + return runtimeTable[@backingInt(angle)][data]; } pub fn generateData(_: *main.game.World, _: Vec3i, _: Vec3f, _: Vec3f, relativeDir: Vec3i, neighbor: ?Neighbor, currentData: *Block, neighborBlock: Block, _: bool) bool { diff --git a/src/Inventory.zig b/src/Inventory.zig index 1f364b9067..6dc5848b26 100644 --- a/src/Inventory.zig +++ b/src/Inventory.zig @@ -23,7 +23,7 @@ const ProceduralItemTypeIndex = main.items.ProceduralItemTypeIndex; pub const InventoryId = enum(u32) { _ }; pub const client = struct { // MARK: client - var maxId: InventoryId = @enumFromInt(0); + var maxId: InventoryId = @fromBackingInt(0); var freeIdList: main.List(InventoryId) = .empty; var serverToClientMap: std.AutoHashMap(InventoryId, Inventory) = undefined; @@ -32,7 +32,7 @@ pub const client = struct { // MARK: client } pub fn deinit() void { - std.debug.assert(freeIdList.items.len == @intFromEnum(maxId)); // leak + std.debug.assert(freeIdList.items.len == @backingInt(maxId)); // leak freeIdList.clearAndFree(main.globalAllocator); serverToClientMap.deinit(); } @@ -44,7 +44,7 @@ pub const client = struct { // MARK: client if (freeIdList.popOrNull()) |id| { return id; } - defer maxId = @enumFromInt(@intFromEnum(maxId) + 1); + defer maxId = @fromBackingInt(@backingInt(maxId) + 1); return maxId; } @@ -163,7 +163,7 @@ pub const server = struct { // MARK: server }; var inventories: main.utils.VirtualList(ServerInventory, 1 << 24) = undefined; - var maxId: InventoryId = @enumFromInt(0); + var maxId: InventoryId = @fromBackingInt(0); var freeIdList: main.List(InventoryId) = .empty; var inventoryCreationMutex: main.utils.Mutex = .{}; @@ -177,10 +177,10 @@ pub const server = struct { // MARK: server std.log.err("Leaked inventory with source {}", .{inv.source}); } } - std.debug.assert(freeIdList.items.len == @intFromEnum(maxId)); // leak + std.debug.assert(freeIdList.items.len == @backingInt(maxId)); // leak freeIdList.clearAndFree(main.globalAllocator); inventories.deinit(); - maxId = @enumFromInt(0); + maxId = @fromBackingInt(0); } pub fn disconnectUser(user: *main.server.User) void { @@ -198,7 +198,7 @@ pub const server = struct { // MARK: server if (freeIdList.popOrNull()) |id| { return id; } - defer maxId = @enumFromInt(@intFromEnum(maxId) + 1); + defer maxId = @fromBackingInt(@backingInt(maxId) + 1); _ = inventories.addOne(); return maxId; } @@ -212,7 +212,7 @@ pub const server = struct { // MARK: server inventoryCreationMutex.lock(); defer inventoryCreationMutex.unlock(); const inventory = ServerInventory.init(len, source, .externallyManaged, callbacks); - inventories.items()[@intFromEnum(inventory.inv.id)] = inventory; + inventories.items()[@backingInt(inventory.inv.id)] = inventory; inventory.inv.fromBytes(data); return inventory.inv.id; } @@ -220,20 +220,20 @@ pub const server = struct { // MARK: server pub fn destroyExternallyManagedInventory(invId: InventoryId) void { switch (sync.threadContext) { .server => {}, - .chunkDeiniting => std.debug.assert(inventories.items()[@intFromEnum(invId)].users.items.len == 0), // There should be no users here, since chunks shouldn't be deinited while players are still interacting with them. + .chunkDeiniting => std.debug.assert(inventories.items()[@backingInt(invId)].users.items.len == 0), // There should be no users here, since chunks shouldn't be deinited while players are still interacting with them. else => unreachable, } - std.debug.assert(inventories.items()[@intFromEnum(invId)].managed == .externallyManaged); + std.debug.assert(inventories.items()[@backingInt(invId)].managed == .externallyManaged); inventoryCreationMutex.lock(); defer inventoryCreationMutex.unlock(); - inventories.items()[@intFromEnum(invId)].deinit(); + inventories.items()[@backingInt(invId)].deinit(); } pub fn destroyAndDropExternallyManagedInventory(invId: InventoryId, pos: Vec3i) void { sync.threadContext.assertCorrectContext(.server); - std.debug.assert(inventories.items()[@intFromEnum(invId)].managed == .externallyManaged); - const inv = &inventories.items()[@intFromEnum(invId)]; + std.debug.assert(inventories.items()[@backingInt(invId)].managed == .externallyManaged); + const inv = &inventories.items()[@backingInt(invId)]; for (inv.inv._items) |*itemStack| { if (itemStack.amount == 0) continue; main.server.world.?.drop( @@ -301,8 +301,8 @@ pub const server = struct { // MARK: server const inventory = ServerInventory.init(len, source, .internallyManaged, callbacks); inventoryCreationMutex.unlock(); - inventories.items()[@intFromEnum(inventory.inv.id)] = inventory; - inventories.items()[@intFromEnum(inventory.inv.id)].addUser(user, clientId); + inventories.items()[@backingInt(inventory.inv.id)] = inventory; + inventories.items()[@backingInt(inventory.inv.id)].addUser(user, clientId); switch (source) { .blockInventory => unreachable, // Should be loaded by the block entity @@ -316,13 +316,13 @@ pub const server = struct { // MARK: server pub fn closeInventory(user: *main.server.User, clientId: InventoryId) !void { sync.threadContext.assertCorrectContext(.server); const serverId = user.inventoryClientToServerIdMap.get(clientId) orelse return error.InventoryNotFound; - inventories.items()[@intFromEnum(serverId)].removeUser(user, clientId); + inventories.items()[@backingInt(serverId)].removeUser(user, clientId); } pub fn getInventory(user: *main.server.User, clientId: InventoryId) ?Inventory { sync.threadContext.assertCorrectContext(.server); const serverId = user.inventoryClientToServerIdMap.get(clientId) orelse return null; - return inventories.items()[@intFromEnum(serverId)].inv; + return inventories.items()[@backingInt(serverId)].inv; } pub fn getInventoryFromSource(source: Source) ?Inventory { @@ -339,20 +339,20 @@ pub const server = struct { // MARK: server pub fn getInventoryFromId(serverId: InventoryId) Inventory { sync.threadContext.assertCorrectContext(.server); - return inventories.items()[@intFromEnum(serverId)].inv; + return inventories.items()[@backingInt(serverId)].inv; } pub fn getServerInventory(serverId: InventoryId) ServerInventory { sync.threadContext.assertCorrectContext(.server); - return inventories.items()[@intFromEnum(serverId)]; + return inventories.items()[@backingInt(serverId)]; } pub fn clearPlayerInventory(user: *main.server.User) void { sync.threadContext.assertCorrectContext(.server); var inventoryIdIterator = user.inventoryClientToServerIdMap.valueIterator(); while (inventoryIdIterator.next()) |inventoryId| { - if (inventories.items()[@intFromEnum(inventoryId.*)].source == .playerInventory) { - sync.server.executeCommand(.{.clear = .{.inv = inventories.items()[@intFromEnum(inventoryId.*)].inv}}, null); + if (inventories.items()[@backingInt(inventoryId.*)].source == .playerInventory) { + sync.server.executeCommand(.{.clear = .{.inv = inventories.items()[@backingInt(inventoryId.*)].inv}}, null); } } } @@ -362,8 +362,8 @@ pub const server = struct { // MARK: server sync.threadContext.assertCorrectContext(.server); var inventoryIdIterator = user.inventoryClientToServerIdMap.valueIterator(); outer: while (inventoryIdIterator.next()) |inventoryId| { - if (inventories.items()[@intFromEnum(inventoryId.*)].source == .playerInventory) { - const inv = inventories.items()[@intFromEnum(inventoryId.*)].inv; + if (inventories.items()[@backingInt(inventoryId.*)].source == .playerInventory) { + const inv = inventories.items()[@backingInt(inventoryId.*)].inv; for (inv._items, 0..) |invStack, slot| { if (std.meta.eql(invStack.item, itemStack.item)) { const amount = @min(itemStack.item.stackSize() - invStack.amount, itemStack.amount); diff --git a/src/argparse.zig b/src/argparse.zig index 38c1b6d65b..b19bd058a7 100644 --- a/src/argparse.zig +++ b/src/argparse.zig @@ -69,26 +69,26 @@ pub fn Parser(comptime T: type, comptime options: Options) type { var nextArgument: ?[]const u8 = tokens.next(); - inline for (s.fields) |field| { - const value = resolveArgument(field.type, arena, field.name[0..], nextArgument, &tempErrorMessage); + inline for (s.field_names, s.field_types) |field, FieldType| { + const value = resolveArgument(FieldType, arena, field[0..], nextArgument, &tempErrorMessage); if (value == error.ParseError) { - if (@typeInfo(field.type) == .optional) { - @field(result, field.name) = null; + if (@typeInfo(FieldType) == .optional) { + @field(result, field) = null; tempErrorMessage.clearRetainingCapacity(); } else { errorMessage.appendSlice(tempErrorMessage.items); return error.ParseError; } } else { - @field(result, field.name) = value catch unreachable; + @field(result, field) = value catch unreachable; tempErrorMessage.clearRetainingCapacity(); nextArgument = tokens.next(); } } if (nextArgument != null) { - errorMessage.print("Too many arguments for command, expected {}", .{s.fields.len}); + errorMessage.print("Too many arguments for command, expected {}", .{s.field_names.len}); return error.ParseError; } @@ -153,14 +153,14 @@ pub fn Parser(comptime T: type, comptime options: Options) type { tempErrorMessage.appendSlice("---"); - inline for (u.fields) |field| { + inline for (u.field_names, u.field_types) |field, FieldType| { tempErrorMessage.append('\n'); - tempErrorMessage.appendSlice(field.name); + tempErrorMessage.appendSlice(field); tempErrorMessage.append('\n'); - const result = Parser(field.type, options).resolve(.parse, arena, args, &tempErrorMessage); + const result = Parser(FieldType, options).resolve(.parse, arena, args, &tempErrorMessage); if (result != error.ParseError) { - return @unionInit(T, field.name, result catch unreachable); + return @unionInit(T, field, result catch unreachable); } tempErrorMessage.appendSlice("\n---"); } diff --git a/src/assets.zig b/src/assets.zig index cfee37450b..9f2862d5e4 100644 --- a/src/assets.zig +++ b/src/assets.zig @@ -436,7 +436,7 @@ fn assignBlockItem(stringId: []const u8) !void { const block = blocks.getTypeById(stringId); // TODO: This must be gone in PixelGuys/Cubyz#1205 const index = items.BaseItemIndex.fromId(stringId).?; - const item = &items.itemList[@intFromEnum(index)]; + const item = &items.itemList[@backingInt(index)]; item.block = block; } @@ -761,13 +761,12 @@ pub fn loadWorldAssets(assetFolder: []const u8, blockPalette: *Palette, itemPale } // now give each component it's id: - inline for (@typeInfo(main.entity.components).@"struct".decls) |decl| { - const name = decl.name; - if (map.get(name)) |id| { - @field(main.entity.components, decl.name).entityComponentID = id; + inline for (@typeInfo(main.entity.components).@"struct".decl_names) |decl| { + if (map.get(decl)) |id| { + @field(main.entity.components, decl).entityComponentID = id; } else { - entityComponentPalette.add(name); - @field(main.entity.components, decl.name).entityComponentID = index; + entityComponentPalette.add(decl); + @field(main.entity.components, decl).entityComponentID = index; index += 1; } } diff --git a/src/block_entity.zig b/src/block_entity.zig index a24dac6b98..06e092f2ec 100644 --- a/src/block_entity.zig +++ b/src/block_entity.zig @@ -49,11 +49,11 @@ pub const BlockEntityType = struct { // MARK: BlockEntityType .vtable = undefined, }; - inline for (@typeInfo(BlockEntityType.VTable).@"struct".fields) |field| { - if (!@hasDecl(BlockEntityTypeT, field.name)) { - @compileError("BlockEntityType missing field '" ++ field.name ++ "'"); + inline for (@typeInfo(BlockEntityType.VTable).@"struct".field_names) |field| { + if (!@hasDecl(BlockEntityTypeT, field)) { + @compileError("BlockEntityType missing field '" ++ field ++ "'"); } - @field(class.vtable, field.name) = &@field(BlockEntityTypeT, field.name); + @field(class.vtable, field) = &@field(BlockEntityTypeT, field); } return class; } @@ -94,7 +94,7 @@ pub const BlockEntity = enum(u32) { // MARK: BlockEntity _, var freeIndexList: main.List(BlockEntity) = .empty; - var nextIndex: BlockEntity = @enumFromInt(0); + var nextIndex: BlockEntity = @fromBackingInt(0); var mutex: main.utils.Mutex = .{}; fn globalDeinit() void { @@ -105,14 +105,14 @@ pub const BlockEntity = enum(u32) { // MARK: BlockEntity fn reset() void { freeIndexList.clearRetainingCapacity(); - nextIndex = @enumFromInt(0); + nextIndex = @fromBackingInt(0); } fn create() BlockEntity { mutex.lock(); defer mutex.unlock(); return freeIndexList.popOrNull() orelse { - defer nextIndex = @enumFromInt(@intFromEnum(nextIndex) + 1); + defer nextIndex = @fromBackingInt(@backingInt(nextIndex) + 1); return nextIndex; }; } @@ -522,7 +522,7 @@ pub const BlockEntityTypes = struct { // MARK: BlockEntityTypes signData.renderedTexture.?.bindTo(0); - c.glUniform1i(uniforms.quadIndex, @intFromEnum(quad)); + c.glUniform1i(uniforms.quadIndex, @backingInt(quad)); const mesh = main.renderer.mesh_storage.getMesh(main.chunk.ChunkPosition.initFromWorldPos(signData.blockPos, 1)) orelse continue :outer; const light: [4]u32 = main.renderer.lighting.getLight(mesh, signData.blockPos -% Vec3i{mesh.pos.wx, mesh.pos.wy, mesh.pos.wz}, 0, quad); c.glUniform4ui(uniforms.lightData, light[0], light[1], light[2], light[3]); @@ -538,23 +538,23 @@ pub const BlockEntityTypes = struct { // MARK: BlockEntityTypes var blockyEntityTypes: std.StringHashMapUnmanaged(BlockEntityType) = .{}; pub fn init() void { - inline for (@typeInfo(BlockEntityTypes).@"struct".decls) |declaration| { - const class = BlockEntityType.init(@field(BlockEntityTypes, declaration.name), declaration.name); + inline for (@typeInfo(BlockEntityTypes).@"struct".decl_names) |declaration| { + const class = BlockEntityType.init(@field(BlockEntityTypes, declaration), declaration); blockyEntityTypes.putNoClobber(main.globalAllocator.allocator, class.id, class) catch unreachable; std.log.debug("Registered BlockEntityType '{s}'", .{class.id}); } } pub fn reset() void { - inline for (@typeInfo(BlockEntityTypes).@"struct".decls) |declaration| { - @field(BlockEntityTypes, declaration.name).reset(); + inline for (@typeInfo(BlockEntityTypes).@"struct".decl_names) |declaration| { + @field(BlockEntityTypes, declaration).reset(); } BlockEntity.reset(); } pub fn deinit() void { - inline for (@typeInfo(BlockEntityTypes).@"struct".decls) |declaration| { - @field(BlockEntityTypes, declaration.name).deinit(); + inline for (@typeInfo(BlockEntityTypes).@"struct".decl_names) |declaration| { + @field(BlockEntityTypes, declaration).deinit(); } BlockEntity.globalDeinit(); blockyEntityTypes.deinit(main.globalAllocator.allocator); @@ -568,7 +568,7 @@ pub fn getByID(_id: ?[]const u8) ?*const BlockEntityType { } pub fn renderAll(ambientLight: Vec3f) void { - inline for (@typeInfo(BlockEntityTypes).@"struct".decls) |declaration| { - @field(BlockEntityTypes, declaration.name).renderAll(ambientLight); + inline for (@typeInfo(BlockEntityTypes).@"struct".decl_names) |declaration| { + @field(BlockEntityTypes, declaration).renderAll(ambientLight); } } diff --git a/src/blocks.zig b/src/blocks.zig index 3deb4032fd..301a65d6dd 100644 --- a/src/blocks.zig +++ b/src/blocks.zig @@ -358,7 +358,7 @@ const ParseBlockConfig = struct { }; fn parseBlockData(fullBlockId: []const u8, data: []const u8, comptime config: ParseBlockConfig) ?u16 { - if (std.mem.containsAtLeastScalar(u8, data, 1, ':')) { + if (std.mem.containsAtLeastScalar(u8, data, ':', 1)) { const oreChild = parseBlockWithOptions(data, config); if (oreChild.data != 0) { std.log.warn("Error while parsing ore block data of '{s}': Parent block data must be 0.", .{fullBlockId}); @@ -494,7 +494,7 @@ pub const Block = packed struct(u32) { // MARK: Block } pub inline fn hasTag(self: Block, tag: Tag) bool { - return std.mem.containsAtLeastScalar(Tag, self.tags(), 1, tag); + return std.mem.containsAtLeastScalar(Tag, self.tags(), tag, 1); } pub inline fn light(self: Block) u32 { diff --git a/src/blueprint.zig b/src/blueprint.zig index 3b5fc519b9..1732204233 100644 --- a/src/blueprint.zig +++ b/src/blueprint.zig @@ -384,7 +384,7 @@ pub const Pattern = struct { var blockId = specifier; var weight: f32 = 1.0; - if (std.mem.containsAtLeastScalar(u8, specifier, 1, weightSeparator)) { + if (std.mem.containsAtLeastScalar(u8, specifier, weightSeparator, 1)) { var iterator = std.mem.splitScalar(u8, specifier, weightSeparator); const weightString = iterator.first(); blockId = iterator.rest(); @@ -436,17 +436,17 @@ pub const Mask = struct { // MARK: Mask blockProperty: Property, const Property = blk: { - var fieldNames: [@typeInfo(Block).@"struct".decls.len][]const u8 = undefined; - var fieldValues: [@typeInfo(Block).@"struct".decls.len]u8 = undefined; + var fieldNames: [@typeInfo(Block).@"struct".decl_names.len][]const u8 = undefined; + var fieldValues: [@typeInfo(Block).@"struct".decl_names.len]u8 = undefined; var count = 0; for (std.meta.declarations(Block)) |decl| { - const declInfo = @typeInfo(@TypeOf(@field(Block, decl.name))); + const declInfo = @typeInfo(@TypeOf(@field(Block, decl))); if (declInfo != .@"fn") continue; if (declInfo.@"fn".return_type != bool) continue; - if (declInfo.@"fn".params.len != 1) continue; + if (declInfo.@"fn".param_types.len != 1) continue; - fieldNames[count] = decl.name; + fieldNames[count] = decl; fieldValues[count] = count; count += 1; } diff --git a/src/callbacks/block/server/check_support_blocks.zig b/src/callbacks/block/server/check_support_blocks.zig index 8ceebda059..ec1e84142a 100644 --- a/src/callbacks/block/server/check_support_blocks.zig +++ b/src/callbacks/block/server/check_support_blocks.zig @@ -32,11 +32,11 @@ pub fn run(_: *@This(), params: main.callbacks.ServerBlockCallback.Params) main. var newBlock: Block = params.block; inline for (comptime std.meta.declarations(main.rotation.rotations)) |rotationMode| { - if (params.block.mode() == main.rotation.getByID(rotationMode.name)) { - if (@hasDecl(@field(main.rotation.rotations, rotationMode.name), "updateBlockFromNeighborConnectivity")) { - @field(main.rotation.rotations, rotationMode.name).updateBlockFromNeighborConnectivity(&newBlock, neighborSupportive); + if (params.block.mode() == main.rotation.getByID(rotationMode)) { + if (@hasDecl(@field(main.rotation.rotations, rotationMode), "updateBlockFromNeighborConnectivity")) { + @field(main.rotation.rotations, rotationMode).updateBlockFromNeighborConnectivity(&newBlock, neighborSupportive); } else { - std.log.err("Rotation mode {s} has no updateBlockFromNeighborConnectivity function and cannot be used for {s} callback", .{rotationMode.name, @typeName(@This())}); + std.log.err("Rotation mode {s} has no updateBlockFromNeighborConnectivity function and cannot be used for {s} callback", .{rotationMode, @typeName(@This())}); } } } diff --git a/src/callbacks/callbacks.zig b/src/callbacks/callbacks.zig index 74f8a81bc2..bfe870e40d 100644 --- a/src/callbacks/callbacks.zig +++ b/src/callbacks/callbacks.zig @@ -37,9 +37,9 @@ fn Callback(_Params: type, list: type) type { var eventCreationMap: std.StringHashMapUnmanaged(VTable) = .{}; fn globalInit() void { - inline for (@typeInfo(list).@"struct".decls) |decl| { - const CallbackStruct = @field(list, decl.name); - eventCreationMap.put(main.globalArena.allocator, decl.name, .{ + inline for (@typeInfo(list).@"struct".decl_names) |decl| { + const CallbackStruct = @field(list, decl); + eventCreationMap.put(main.globalArena.allocator, decl, .{ .init = main.meta.castFunctionReturnToOptionalAnyopaque(CallbackStruct.init), .run = main.meta.castFunctionSelfToAnyopaque(CallbackStruct.run), }) catch unreachable; diff --git a/src/chunk.zig b/src/chunk.zig index 60c8473656..9edda8a3f7 100644 --- a/src/chunk.zig +++ b/src/chunk.zig @@ -24,23 +24,23 @@ pub const Neighbor = enum(u3) { // MARK: Neighbor dirNegY = 5, pub inline fn toInt(self: Neighbor) u3 { - return @intFromEnum(self); + return @backingInt(self); } /// Index to relative position pub fn relX(self: Neighbor) i32 { const arr = [_]i32{0, 0, 1, -1, 0, 0}; - return arr[@intFromEnum(self)]; + return arr[@backingInt(self)]; } /// Index to relative position pub fn relY(self: Neighbor) i32 { const arr = [_]i32{0, 0, 0, 0, 1, -1}; - return arr[@intFromEnum(self)]; + return arr[@backingInt(self)]; } /// Index to relative position pub fn relZ(self: Neighbor) i32 { const arr = [_]i32{1, -1, 0, 0, 0, 0}; - return arr[@intFromEnum(self)]; + return arr[@backingInt(self)]; } /// Index to relative position pub fn relPos(self: Neighbor) Vec3i { @@ -68,10 +68,10 @@ pub const Neighbor = enum(u3) { // MARK: Neighbor /// Index to bitMask for bitmap direction data pub inline fn bitMask(self: Neighbor) u6 { - return @as(u6, 1) << @intFromEnum(self); + return @as(u6, 1) << @backingInt(self); } /// To iterate over all neighbors easily - pub const iterable = [_]Neighbor{@enumFromInt(0), @enumFromInt(1), @enumFromInt(2), @enumFromInt(3), @enumFromInt(4), @enumFromInt(5)}; + pub const iterable = [_]Neighbor{@fromBackingInt(0), @fromBackingInt(1), @fromBackingInt(2), @fromBackingInt(3), @fromBackingInt(4), @fromBackingInt(5)}; /// Marks the two dimension that are orthogonal pub fn orthogonalComponents(self: Neighbor) Vec3i { const arr = [_]Vec3i{ @@ -82,7 +82,7 @@ pub const Neighbor = enum(u3) { // MARK: Neighbor .{1, 0, 1}, .{1, 0, 1}, }; - return arr[@intFromEnum(self)]; + return arr[@backingInt(self)]; } pub fn textureX(self: Neighbor) Vec3i { const arr = [_]Vec3i{ @@ -93,7 +93,7 @@ pub const Neighbor = enum(u3) { // MARK: Neighbor .{-1, 0, 0}, .{1, 0, 0}, }; - return arr[@intFromEnum(self)]; + return arr[@backingInt(self)]; } pub fn textureY(self: Neighbor) Vec3i { const arr = [_]Vec3i{ @@ -104,26 +104,26 @@ pub const Neighbor = enum(u3) { // MARK: Neighbor .{0, 0, 1}, .{0, 0, 1}, }; - return arr[@intFromEnum(self)]; + return arr[@backingInt(self)]; } pub inline fn reverse(self: Neighbor) Neighbor { - return @enumFromInt(@intFromEnum(self) ^ 1); + return @fromBackingInt(@backingInt(self) ^ 1); } pub inline fn isPositive(self: Neighbor) bool { - return @intFromEnum(self) & 1 == 0; + return @backingInt(self) & 1 == 0; } const VectorComponentEnum = enum(u2) { x = 0, y = 1, z = 2 }; pub fn vectorComponent(self: Neighbor) VectorComponentEnum { const arr = [_]VectorComponentEnum{.z, .z, .x, .x, .y, .y}; - return arr[@intFromEnum(self)]; + return arr[@backingInt(self)]; } pub fn extractDirectionComponent(self: Neighbor, in: anytype) @TypeOf(in[0]) { switch (self) { inline else => |val| { - return in[@intFromEnum(comptime val.vectorComponent())]; + return in[@backingInt(comptime val.vectorComponent())]; }, } } @@ -131,7 +131,7 @@ pub const Neighbor = enum(u3) { // MARK: Neighbor // Returns the neighbor that is rotated by 90 degrees counterclockwise around the z axis. pub inline fn rotateZ(self: Neighbor) Neighbor { const arr = [_]Neighbor{.dirUp, .dirDown, .dirPosY, .dirNegY, .dirNegX, .dirPosX}; - return arr[@intFromEnum(self)]; + return arr[@backingInt(self)]; } }; @@ -146,27 +146,27 @@ pub const Lod = enum(u5) { @"16" = 4, @"32" = 5, - const min: Lod = @enumFromInt(@typeInfo(Lod).@"enum".fields[0].value); + const min: Lod = @fromBackingInt(@typeInfo(Lod).@"enum".field_values[0]); const max: Lod = blk: { - const fields = @typeInfo(Lod).@"enum".fields; - const maxValue = fields[fields.len - 1].value; - break :blk @enumFromInt(maxValue); + const fields = @typeInfo(Lod).@"enum".field_values; + const maxValue = fields[fields.len - 1]; + break :blk @fromBackingInt(maxValue); }; pub inline fn next(self: Lod) Lod { - return @enumFromInt(@intFromEnum(self) + 1); + return @fromBackingInt(@backingInt(self) + 1); } pub inline fn previous(self: Lod) Lod { - return @enumFromInt(@intFromEnum(self) - 1); + return @fromBackingInt(@backingInt(self) - 1); } pub inline fn toInt(self: Lod) @typeInfo(Lod).@"enum".tag_type { - return @intFromEnum(self); + return @backingInt(self); } pub inline fn voxelSize(self: Lod) u31 { - return 1 << @intFromEnum(self); + return 1 << @backingInt(self); } pub inline fn chunkWidth(self: Lod) u31 { diff --git a/src/client/Entity.zig b/src/client/Entity.zig index 77dd979dc7..8a5db48783 100644 --- a/src/client/Entity.zig +++ b/src/client/Entity.zig @@ -33,7 +33,7 @@ name: []const u8, pub fn init(self: *@This(), zon: ZonElement, allocator: NeverFailingAllocator) !void { self.* = @This(){ - .id = @enumFromInt(zon.get(u32, "id") orelse std.math.maxInt(u32)), + .id = @fromBackingInt(zon.get(u32, "id") orelse std.math.maxInt(u32)), .width = zon.get(f64, "width") orelse 1, .height = zon.get(f64, "height") orelse 1, .name = allocator.dupe(u8, zon.get([]const u8, "name") orelse ""), diff --git a/src/client/entity_manager.zig b/src/client/entity_manager.zig index 96f5537fd9..81cefde72f 100644 --- a/src/client/entity_manager.zig +++ b/src/client/entity_manager.zig @@ -76,19 +76,19 @@ pub fn addEntity(zon: ZonElement) !void { } pub fn getEntity(entity: main.entity.Entity) ?*main.client.Entity { mutex.assertLocked(); - if (@intFromEnum(entity) >= idMapping.items.len) return null; - return &entities.items()[idMapping.items[@intFromEnum(entity)] orelse return null]; + if (@backingInt(entity) >= idMapping.items.len) return null; + return &entities.items()[idMapping.items[@backingInt(entity)] orelse return null]; } pub fn removeEntity(entity: main.entity.Entity) void { mutex.lock(); defer mutex.unlock(); - if (idMapping.items.len <= @intFromEnum(entity)) return; - const index: u32 = idMapping.items[@intFromEnum(entity)] orelse return; + if (idMapping.items.len <= @backingInt(entity)) return; + const index: u32 = idMapping.items[@backingInt(entity)] orelse return; const ent = entities.items()[index]; // remove id - idMapping.items[@intFromEnum(entity)] = null; + idMapping.items[@backingInt(entity)] = null; // remove entity { @@ -97,7 +97,7 @@ pub fn removeEntity(entity: main.entity.Entity) void { _ = entities.swapRemove(index); if (index != entities.len) { - idMapping.items[@intFromEnum(entities.items()[index].id)] = index; + idMapping.items[@backingInt(entities.items()[index].id)] = index; entities.items()[index].interpolatedValues.outPos = &entities.items()[index]._interpolationPos; entities.items()[index].interpolatedValues.outVel = &entities.items()[index]._interpolationVel; } diff --git a/src/entity.zig b/src/entity.zig index 8fb965a8f1..c2107a0eb0 100644 --- a/src/entity.zig +++ b/src/entity.zig @@ -38,19 +38,19 @@ var componentList: []?EntityComponentVTable = undefined; pub fn initComponents() void { var tmpComponentList: main.List(?EntityComponentVTable) = .empty; - inline for (@typeInfo(components).@"struct".decls) |decl| { - @field(components, decl.name).client.init(); - const componentId = @field(components, decl.name).entityComponentID; + inline for (@typeInfo(components).@"struct".decl_names) |decl| { + @field(components, decl).client.init(); + const componentId = @field(components, decl).entityComponentID; if (tmpComponentList.items.len <= componentId) { tmpComponentList.appendNTimes(main.worldArena, null, componentId + 1 - tmpComponentList.items.len); } if (tmpComponentList.items[componentId] == null) { tmpComponentList.items[componentId] = .{ - .serverLoad = @field(components, decl.name).server.loadFromData, - .clientLoad = @field(components, decl.name).client.load, - .serverUnload = @field(components, decl.name).server.unload, - .clientUnload = @field(components, decl.name).client.unload, + .serverLoad = @field(components, decl).server.loadFromData, + .clientLoad = @field(components, decl).client.load, + .serverUnload = @field(components, decl).server.unload, + .clientUnload = @field(components, decl).client.unload, }; } else { std.log.err("entity components: Duplicate list id {}.", .{componentId}); @@ -99,53 +99,53 @@ pub fn unloadComponent(comptime side: main.sync.Side, componentId: EntityCompone pub const client = struct { pub fn init() void { - inline for (@typeInfo(components).@"struct".decls) |decl| { - @field(components, decl.name).client.init(); + inline for (@typeInfo(components).@"struct".decl_names) |decl| { + @field(components, decl).client.init(); } main.client.entity_manager.init(); } pub fn deinit() void { main.client.entity_manager.deinit(); - inline for (@typeInfo(components).@"struct".decls) |decl| { - @field(components, decl.name).client.deinit(); + inline for (@typeInfo(components).@"struct".decl_names) |decl| { + @field(components, decl).client.deinit(); } } pub fn clear() void { main.client.entity_manager.clear(); - inline for (@typeInfo(components).@"struct".decls) |decl| { - @field(components, decl.name).client.clear(); + inline for (@typeInfo(components).@"struct".decl_names) |decl| { + @field(components, decl).client.clear(); } } pub fn removeAllComponents(entity: Entity) void { const list = main.entity.components; - inline for (@typeInfo(list).@"struct".decls) |decl| { - @field(list, decl.name).client.unload(entity); + inline for (@typeInfo(list).@"struct".decl_names) |decl| { + @field(list, decl).client.unload(entity); } } }; pub const server = struct { pub fn init() void { - inline for (@typeInfo(components).@"struct".decls) |decl| { - @field(components, decl.name).server.init(); + inline for (@typeInfo(components).@"struct".decl_names) |decl| { + @field(components, decl).server.init(); } } pub fn deinit() void { - inline for (@typeInfo(components).@"struct".decls) |decl| { - @field(components, decl.name).server.deinit(); + inline for (@typeInfo(components).@"struct".decl_names) |decl| { + @field(components, decl).server.deinit(); } } pub fn componentsToBase64(allocator: main.heap.NeverFailingAllocator, entity: Entity, audience: main.entity.AudienceInfo) main.utils.Base64 { var writer = main.utils.BinaryWriter.init(main.stackAllocator); defer writer.deinit(); - inline for (@typeInfo(main.entity.components).@"struct".decls) |decl| { - if (@field(main.entity.components, decl.name).server.get(entity)) |component| { + inline for (@typeInfo(main.entity.components).@"struct".decl_names) |decl| { + if (@field(main.entity.components, decl).server.get(entity)) |component| { var writerComponent = main.utils.BinaryWriter.init(main.stackAllocator); defer writerComponent.deinit(); if (component.save(&writerComponent, audience) == .save) { - writer.writeVarInt(u32, @field(main.entity.components, decl.name).entityComponentID); - writer.writeVarInt(u32, @field(main.entity.components, decl.name).entityComponentVersion); + writer.writeVarInt(u32, @field(main.entity.components, decl).entityComponentID); + writer.writeVarInt(u32, @field(main.entity.components, decl).entityComponentVersion); writer.writeSliceWithSize(writerComponent.data.items); } } @@ -155,8 +155,8 @@ pub const server = struct { pub fn removeAllComponents(entity: Entity) void { const list = main.entity.components; - inline for (@typeInfo(list).@"struct".decls) |decl| { - @field(list, decl.name).server.unload(entity); + inline for (@typeInfo(list).@"struct".decl_names) |decl| { + @field(list, decl).server.unload(entity); } } diff --git a/src/game.zig b/src/game.zig index cb7b157be7..56e7e52717 100644 --- a/src/game.zig +++ b/src/game.zig @@ -228,7 +228,7 @@ pub const Player = struct { // MARK: Player const block = main.renderer.mesh_storage.getBlockFromRenderThread(selectedPos[0], selectedPos[1], selectedPos[2]) orelse return; const item: items.Item = for (0..items.itemListSize) |idx| { - const baseItem: main.items.BaseItemIndex = @enumFromInt(idx); + const baseItem: main.items.BaseItemIndex = @fromBackingInt(@intCast(idx)); if (baseItem.block() == block.typ) { break .{.baseItem = baseItem}; } @@ -384,7 +384,7 @@ pub const World = struct { // MARK: World const path = main.stackAllocator.print("{s}/serverAssets", .{main.files.cubyzDirStr()}); defer main.stackAllocator.free(path); try assets.loadWorldAssets(path, self.blockPalette, self.itemPalette, self.proceduralItemPalette, self.biomePalette, self.entityModelPalette, self.entityComponentPalette); - Player.id = @enumFromInt(zon.get(u32, "player_id") orelse @intFromEnum(main.entity.Entity.noValue)); + Player.id = @fromBackingInt(zon.get(u32, "player_id") orelse @backingInt(main.entity.Entity.noValue)); Player.inventory = ClientInventory.init(main.globalAllocator, Player.inventorySize, .serverShared, .{.playerInventory = Player.id}, .{}); Player.setGamemode(std.enums.fromInt(Gamemode, zon.get(u8, "gamemode") orelse return error.Invalid) orelse return error.Invalid); self.playerBiome = .init(main.server.terrain.biomes.getPlaceholderBiome()); diff --git a/src/graphics/CommandBuffer.zig b/src/graphics/CommandBuffer.zig index 508f3b5bcb..1b7572a397 100644 --- a/src/graphics/CommandBuffer.zig +++ b/src/graphics/CommandBuffer.zig @@ -261,7 +261,7 @@ pub fn bindDescriptors(self: CommandBuffer, pipeline: main.graphics.Pipeline, bi }, } } - c.vkCmdPushDescriptorSetKHR(self.handle, @intFromEnum(bindPoint), pipeline.pipelineLayout, 0, @intCast(writeInfo.len), writeInfo.ptr); + c.vkCmdPushDescriptorSetKHR(self.handle, @backingInt(bindPoint), pipeline.pipelineLayout, 0, @intCast(writeInfo.len), writeInfo.ptr); } pub fn pushConstants(self: CommandBuffer, pipeline: main.graphics.Pipeline, constants: anytype) void { diff --git a/src/graphics/Window.zig b/src/graphics/Window.zig index 8ad1382f87..b5b901a2a3 100644 --- a/src/graphics/Window.zig +++ b/src/graphics/Window.zig @@ -198,7 +198,7 @@ pub const Gamepad = struct { // MARK: Gamepad return; }; if (fetchResult.status != .ok) { - std.log.err("Failed to download controller mappings: HTTP error {d}", .{@intFromEnum(fetchResult.status)}); + std.log.err("Failed to download controller mappings: HTTP error {d}", .{@backingInt(fetchResult.status)}); return; } files.cwd().write("./gamecontrollerdb.txt", writer.written()) catch |err| { @@ -221,7 +221,7 @@ pub const Gamepad = struct { // MARK: Gamepad } }; 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 + 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: i96 = std.Io.Clock.Timestamp.now(main.io, .real).raw.nanoseconds; const timestamp: i96 = blk: { diff --git a/src/graphics/pipelines.zig b/src/graphics/pipelines.zig index 4689305a67..5935e67c52 100644 --- a/src/graphics/pipelines.zig +++ b/src/graphics/pipelines.zig @@ -30,7 +30,7 @@ const Shader = struct { // MARK: Shader const input = c.glslang_input_t{ .language = c.GLSLANG_SOURCE_GLSL, - .stage = @intFromEnum(shaderStage), + .stage = @backingInt(shaderStage), .client = c.GLSLANG_CLIENT_VULKAN, .client_version = c.GLSLANG_TARGET_VULKAN_1_0, .target_language = c.GLSLANG_TARGET_SPV, @@ -66,7 +66,7 @@ const Shader = struct { // MARK: Shader return error.FailedCompiling; } - c.glslang_program_SPIRV_generate(program, @intFromEnum(shaderStage)); + c.glslang_program_SPIRV_generate(program, @backingInt(shaderStage)); const result = allocator.alloc(c_uint, c.glslang_program_SPIRV_get_size(program)); c.glslang_program_SPIRV_get(program, result.ptr); return result; @@ -182,9 +182,9 @@ const Shader = struct { // MARK: Shader shader.link(fragment) catch return shader; if (@TypeOf(uniformStruct) != @TypeOf(null)) { - inline for (@typeInfo(@TypeOf(uniformStruct.*)).@"struct".fields) |field| { - if (field.type == c_int) { - @field(uniformStruct, field.name) = c.glGetUniformLocation(shader.id, field.name[0..]); + inline for (@typeInfo(@TypeOf(uniformStruct.*)).@"struct".field_names, @typeInfo(@TypeOf(uniformStruct.*)).@"struct".field_types) |field, fieldType| { + if (fieldType == c_int) { + @field(uniformStruct, field) = c.glGetUniformLocation(shader.id, field[0..]); } } } @@ -197,9 +197,9 @@ const Shader = struct { // MARK: Shader shader.link(compute) catch return shader; if (@TypeOf(uniformStruct) != @TypeOf(null)) { - inline for (@typeInfo(@TypeOf(uniformStruct.*)).@"struct".fields) |field| { - if (field.type == c_int) { - @field(uniformStruct, field.name) = c.glGetUniformLocation(shader.id, field.name[0..]); + inline for (@typeInfo(@TypeOf(uniformStruct.*)).@"struct".field_names, @typeInfo(@TypeOf(uniformStruct.*)).@"struct".field_types) |field, fieldType| { + if (fieldType == c_int) { + @field(uniformStruct, field) = c.glGetUniformLocation(shader.id, field[0..]); } } } @@ -271,10 +271,10 @@ const RasterizationState = struct { // MARK: RasterizationState .sType = c.VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO, .depthClampEnable = @intFromBool(self.depthClamp), .rasterizerDiscardEnable = @intFromBool(self.rasterizerDiscard), - .polygonMode = @intFromEnum(self.polygonMode), + .polygonMode = @backingInt(self.polygonMode), .lineWidth = self.lineWidth, - .cullMode = @intFromEnum(self.cullMode), - .frontFace = @intFromEnum(self.frontFace), + .cullMode = @backingInt(self.cullMode), + .frontFace = @backingInt(self.frontFace), .depthBiasEnable = @intFromBool(self.depthBias != null), .depthBiasConstantFactor = if (self.depthBias) |d| d.constantFactor else 0, .depthBiasClamp = if (self.depthBias) |d| d.clamp else 0, @@ -304,7 +304,7 @@ const MultisampleState = struct { // MARK: MultisampleState pub fn toVulkan(self: MultisampleState) c.VkPipelineMultisampleStateCreateInfo { return .{ .sType = c.VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO, - .rasterizationSamples = @intFromEnum(self.rasterizationSamples), + .rasterizationSamples = @backingInt(self.rasterizationSamples), .sampleShadingEnable = @intFromBool(self.sampleShading), .minSampleShading = self.minSampleShading, .pSampleMask = self.sampleMask, @@ -360,10 +360,10 @@ const DepthStencilState = struct { // MARK: DepthStencilState fn toVulkan(self: StencilOpState) c.VkStencilOpState { return .{ - .failOp = @intFromEnum(self.failOp), - .passOp = @intFromEnum(self.passOp), - .depthFailOp = @intFromEnum(self.depthFailOp), - .compareOp = @intFromEnum(self.compareOp), + .failOp = @backingInt(self.failOp), + .passOp = @backingInt(self.passOp), + .depthFailOp = @backingInt(self.depthFailOp), + .compareOp = @backingInt(self.compareOp), .compareMask = self.compareMask, .writeMask = self.writeMask, .reference = self.reference, @@ -382,7 +382,7 @@ const DepthStencilState = struct { // MARK: DepthStencilState .sType = c.VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO, .depthTestEnable = @intFromBool(self.depthTest), .depthWriteEnable = @intFromBool(self.depthWrite), - .depthCompareOp = @intFromEnum(self.depthCompare), + .depthCompareOp = @backingInt(self.depthCompare), .depthBoundsTestEnable = @intFromBool(self.depthBoundsTest != null), .stencilTestEnable = @intFromBool(self.stencilTest != null), .front = if (self.stencilTest) |s| s.front.toVulkan() else .{}, @@ -497,12 +497,12 @@ const ColorBlendAttachmentState = struct { // MARK: ColorBlendAttachmentState pub fn toVulkan(self: ColorBlendAttachmentState) c.VkPipelineColorBlendAttachmentState { return .{ .blendEnable = @intFromBool(self.enabled), - .srcColorBlendFactor = @intFromEnum(self.srcColorBlendFactor), - .dstColorBlendFactor = @intFromEnum(self.dstColorBlendFactor), - .colorBlendOp = @intFromEnum(self.colorBlendOp), - .srcAlphaBlendFactor = @intFromEnum(self.srcAlphaBlendFactor), - .dstAlphaBlendFactor = @intFromEnum(self.dstAlphaBlendFactor), - .alphaBlendOp = @intFromEnum(self.alphaBlendOp), + .srcColorBlendFactor = @backingInt(self.srcColorBlendFactor), + .dstColorBlendFactor = @backingInt(self.dstColorBlendFactor), + .colorBlendOp = @backingInt(self.colorBlendOp), + .srcAlphaBlendFactor = @backingInt(self.srcAlphaBlendFactor), + .dstAlphaBlendFactor = @backingInt(self.dstAlphaBlendFactor), + .alphaBlendOp = @backingInt(self.alphaBlendOp), .colorWriteMask = @as(u4, @bitCast(self.colorWriteMask)), }; } @@ -543,7 +543,7 @@ const ColorBlendState = struct { // MARK: ColorBlendState return .{ .sType = c.VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO, .logicOpEnable = @intFromBool(self.logicOp != null), - .logicOp = if (self.logicOp) |l| @intFromEnum(l) else undefined, + .logicOp = if (self.logicOp) |l| @backingInt(l) else undefined, .attachmentCount = @intCast(attachments.len), .pAttachments = attachments.ptr, .blendConstants = self.blendConstants, @@ -677,7 +677,7 @@ pub const Pipeline = struct { // MARK: Pipeline }; const inputAssembly: c.VkPipelineInputAssemblyStateCreateInfo = .{ .sType = c.VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO, - .topology = @intFromEnum(options.inputAssemblyState.topology), + .topology = @backingInt(options.inputAssemblyState.topology), .primitiveRestartEnable = if (options.inputAssemblyState.primitiveRestartEnable) c.VK_TRUE else c.VK_FALSE, }; const viewport: c.VkViewport = .{}; // overwritten dynamically diff --git a/src/graphics/vulkan.zig b/src/graphics/vulkan.zig index dd3aae360a..2abac356cb 100644 --- a/src/graphics/vulkan.zig +++ b/src/graphics/vulkan.zig @@ -88,8 +88,8 @@ fn checkResultIfAvailable(result: anytype) void { } } -fn allocEnumerationGeneric(function: anytype, allocator: NeverFailingAllocator, args: anytype) []@typeInfo(@typeInfo(@TypeOf(function)).@"fn".params[@typeInfo(@TypeOf(function)).@"fn".params.len - 1].type.?).pointer.child { - const T = @typeInfo(@typeInfo(@TypeOf(function)).@"fn".params[@typeInfo(@TypeOf(function)).@"fn".params.len - 1].type.?).pointer.child; +fn allocEnumerationGeneric(function: anytype, allocator: NeverFailingAllocator, args: anytype) []@typeInfo(@typeInfo(@TypeOf(function)).@"fn".param_types[@typeInfo(@TypeOf(function)).@"fn".param_types.len - 1].?).pointer.child { + const T = @typeInfo(@typeInfo(@TypeOf(function)).@"fn".param_types[@typeInfo(@TypeOf(function)).@"fn".param_types.len - 1].?).pointer.child; var count: u32 = 0; while (true) { checkResultIfAvailable(@call(.auto, function, args ++ .{&count, null})); @@ -969,15 +969,15 @@ pub const Image = struct { // MARK: Image const samplerInfo: c.VkSamplerCreateInfo = .{ .sType = c.VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO, - .magFilter = @intFromEnum(options.magFilter), - .minFilter = @intFromEnum(options.minFilter), + .magFilter = @backingInt(options.magFilter), + .minFilter = @backingInt(options.minFilter), .mipmapMode = switch (options.mipmapFilter) { .nearest => c.VK_SAMPLER_MIPMAP_MODE_NEAREST, .linear => c.VK_SAMPLER_MIPMAP_MODE_LINEAR, }, - .addressModeU = @intFromEnum(options.addressMode), - .addressModeV = @intFromEnum(options.addressMode), - .addressModeW = @intFromEnum(options.addressMode), + .addressModeU = @backingInt(options.addressMode), + .addressModeV = @backingInt(options.addressMode), + .addressModeW = @backingInt(options.addressMode), .mipLodBias = options.mipLodBias, .anisotropyEnable = if (options.maxAnisotropy != null) c.VK_TRUE else c.VK_FALSE, .maxAnisotropy = options.maxAnisotropy orelse 0, diff --git a/src/gui/gui.zig b/src/gui/gui.zig index 87a440a874..86eedc3bfb 100644 --- a/src/gui/gui.zig +++ b/src/gui/gui.zig @@ -98,7 +98,7 @@ const GuiCommandQueue = struct { // MARK: GuiCommandQueue } fn executeOpenModalWindowCommand(window: *GuiWindow) void { - const alreadyOpen = std.mem.containsAtLeastScalar(*GuiWindow, openWindows.items, 1, window); + const alreadyOpen = std.mem.containsAtLeastScalar(*GuiWindow, openWindows.items, window, 1); if (!alreadyOpen) setSelectedTextInput(null); modalWindow = window; executeOpenWindowCommand(window); @@ -127,9 +127,9 @@ pub fn initWindowList() void { windowList = .init(main.globalAllocator); hudWindows = .init(main.globalAllocator); openWindows = .init(main.globalAllocator); - inline for (@typeInfo(windowlist).@"struct".decls) |decl| { - const windowStruct = @field(windowlist, decl.name); - windowStruct.window.id = decl.name; + inline for (@typeInfo(windowlist).@"struct".decl_names) |decl| { + const windowStruct = @field(windowlist, decl); + windowStruct.window.id = decl; addWindow(&windowStruct.window); const functionNames = [_][]const u8{"render", "update", "updateSelected", "updateHovered", "onOpen", "onClose"}; inline for (functionNames) |function| { @@ -148,8 +148,8 @@ pub fn deinitWindowList() void { } pub fn init() void { // MARK: init() - inline for (@typeInfo(windowlist).@"struct".decls) |decl| { - const windowStruct = @field(windowlist, decl.name); + inline for (@typeInfo(windowlist).@"struct".decl_names) |decl| { + const windowStruct = @field(windowlist, decl); if (@hasDecl(windowStruct, "init")) { windowStruct.init(); } @@ -185,8 +185,8 @@ pub fn deinit() void { DiscreteSlider.globalDeinit(); TextInput.globalDeinit(); tooltip.globalDeinit(); - inline for (@typeInfo(windowlist).@"struct".decls) |decl| { - const WindowStruct = @field(windowlist, decl.name); + inline for (@typeInfo(windowlist).@"struct".decl_names) |decl| { + const WindowStruct = @field(windowlist, decl); if (@hasDecl(WindowStruct, "deinit")) { WindowStruct.deinit(); } @@ -207,8 +207,8 @@ pub fn save() void { // MARK: save() }, .attachedToFrame => |attachedToFrame| { relPosZon.put("type", "attachedToFrame"); - relPosZon.put("selfAttachmentPoint", @intFromEnum(attachedToFrame.selfAttachmentPoint)); - relPosZon.put("otherAttachmentPoint", @intFromEnum(attachedToFrame.otherAttachmentPoint)); + relPosZon.put("selfAttachmentPoint", @backingInt(attachedToFrame.selfAttachmentPoint)); + relPosZon.put("otherAttachmentPoint", @backingInt(attachedToFrame.otherAttachmentPoint)); }, .relativeToWindow => |relativeToWindow| { relPosZon.put("type", "relativeToWindow"); @@ -218,8 +218,8 @@ pub fn save() void { // MARK: save() .attachedToWindow => |attachedToWindow| { relPosZon.put("type", "attachedToWindow"); relPosZon.put("reference", attachedToWindow.reference.id); - relPosZon.put("selfAttachmentPoint", @intFromEnum(attachedToWindow.selfAttachmentPoint)); - relPosZon.put("otherAttachmentPoint", @intFromEnum(attachedToWindow.otherAttachmentPoint)); + relPosZon.put("selfAttachmentPoint", @backingInt(attachedToWindow.selfAttachmentPoint)); + relPosZon.put("otherAttachmentPoint", @backingInt(attachedToWindow.otherAttachmentPoint)); }, } windowZon.put(([_][]const u8{"relPos0", "relPos1"})[i], relPosZon); @@ -265,8 +265,8 @@ fn load() void { relPos.* = .{.ratio = relPosZon.get(f32, "ratio") orelse 0.5}; } else if (std.mem.eql(u8, typ, "attachedToFrame")) { relPos.* = .{.attachedToFrame = .{ - .selfAttachmentPoint = @enumFromInt(relPosZon.get(u8, "selfAttachmentPoint") orelse 0), - .otherAttachmentPoint = @enumFromInt(relPosZon.get(u8, "otherAttachmentPoint") orelse 0), + .selfAttachmentPoint = @fromBackingInt(relPosZon.get(u8, "selfAttachmentPoint") orelse 0), + .otherAttachmentPoint = @fromBackingInt(relPosZon.get(u8, "otherAttachmentPoint") orelse 0), }}; } else if (std.mem.eql(u8, typ, "relativeToWindow")) { const reference = getWindowById(relPosZon.get([]const u8, "reference") orelse "") orelse continue; @@ -278,8 +278,8 @@ fn load() void { const reference = getWindowById(relPosZon.get([]const u8, "reference") orelse "") orelse continue; relPos.* = .{.attachedToWindow = .{ .reference = reference, - .selfAttachmentPoint = @enumFromInt(relPosZon.get(u8, "selfAttachmentPoint") orelse 0), - .otherAttachmentPoint = @enumFromInt(relPosZon.get(u8, "otherAttachmentPoint") orelse 0), + .selfAttachmentPoint = @fromBackingInt(relPosZon.get(u8, "selfAttachmentPoint") orelse 0), + .otherAttachmentPoint = @fromBackingInt(relPosZon.get(u8, "otherAttachmentPoint") orelse 0), }}; } else { std.log.err("Unknown window attachment type: {s}", .{typ}); diff --git a/src/gui/windows/authentication/create_account_storage_method.zig b/src/gui/windows/authentication/create_account_storage_method.zig index 6fc2a35824..4de25ec34d 100644 --- a/src/gui/windows/authentication/create_account_storage_method.zig +++ b/src/gui/windows/authentication/create_account_storage_method.zig @@ -25,16 +25,16 @@ const padding: f32 = 8; fn next(storageMethod: usize) void { gui.closeWindowFromRef(&window); - gui.windowlist.@"authentication/create_account_account_code".setStorageMethod(@enumFromInt(storageMethod)); + gui.windowlist.@"authentication/create_account_account_code".setStorageMethod(@fromBackingInt(storageMethod)); gui.openWindow("authentication/create_account_account_code"); } pub fn onOpen() void { const list = VerticalList.init(.{padding, 16 + padding}, 300, 16); list.add(Label.init(.{0, 0}, 300, "Please select how you would like to store your Account Code", .left)); - list.add(Button.initText(.{0, 0}, 300, "Save as file", .{.onAction = .initWithInt(next, @intFromEnum(StorageMethod.file))})); - list.add(Button.initText(.{0, 0}, 300, "Password Manager (recommended)", .{.onAction = .initWithInt(next, @intFromEnum(StorageMethod.passwordManager))})); - list.add(Button.initText(.{0, 0}, 300, "Write it down yourself", .{.onAction = .initWithInt(next, @intFromEnum(StorageMethod.paper))})); + list.add(Button.initText(.{0, 0}, 300, "Save as file", .{.onAction = .initWithInt(next, @backingInt(StorageMethod.file))})); + list.add(Button.initText(.{0, 0}, 300, "Password Manager (recommended)", .{.onAction = .initWithInt(next, @backingInt(StorageMethod.passwordManager))})); + list.add(Button.initText(.{0, 0}, 300, "Write it down yourself", .{.onAction = .initWithInt(next, @backingInt(StorageMethod.paper))})); 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/debug.zig b/src/gui/windows/debug.zig index c53aef5b8a..92737f442c 100644 --- a/src/gui/windows/debug.zig +++ b/src/gui/windows/debug.zig @@ -71,11 +71,11 @@ pub fn render() void { const values = comptime std.enums.values(TaskType); var totalUtime: i64 = 0; for (values) |task| { - totalUtime += perf.utime[@intFromEnum(task)]; + totalUtime += perf.utime[@backingInt(task)]; } for (values) |t| { const name = @tagName(t); - const i = @intFromEnum(t); + const i = @backingInt(t); const taskTime = @divFloor(perf.utime[i], @max(1, perf.tasks[i])); const relativeTime = 100.0*@as(f32, @floatFromInt(perf.utime[i]))/@as(f32, @floatFromInt(totalUtime)); draw.print(" {s}: {} µs/task ({d:.1}%)", .{name, taskTime, relativeTime}, 0, y, 8); diff --git a/src/gui/windows/debug_network.zig b/src/gui/windows/debug_network.zig index d0a9aa41a5..95ef38c5bc 100644 --- a/src/gui/windows/debug_network.zig +++ b/src/gui/windows/debug_network.zig @@ -43,10 +43,10 @@ pub fn render() void { y += 8; draw.print("External header overhead: {}kiB", .{network.Connection.externalHeaderOverhead.load(.monotonic) >> 10}, 0, y, 8); y += 8; - inline for (@typeInfo(network.protocols).@"struct".decls) |decl| { - if (@TypeOf(@field(network.protocols, decl.name)) == type) { - const id = @field(network.protocols, decl.name).id; - draw.print("{s}: received {}kiB sent {}kiB", .{decl.name, network.protocols.bytesReceived[id].load(.monotonic) >> 10, network.protocols.bytesSent[id].load(.monotonic) >> 10}, 0, y, 8); + inline for (@typeInfo(network.protocols).@"struct".decl_names) |decl| { + if (@TypeOf(@field(network.protocols, decl)) == type) { + const id = @field(network.protocols, decl).id; + draw.print("{s}: received {}kiB sent {}kiB", .{decl, network.protocols.bytesReceived[id].load(.monotonic) >> 10, network.protocols.bytesSent[id].load(.monotonic) >> 10}, 0, y, 8); y += 8; } } diff --git a/src/gui/windows/gpu_performance_measuring.zig b/src/gui/windows/gpu_performance_measuring.zig index 092d407670..b1083e7784 100644 --- a/src/gui/windows/gpu_performance_measuring.zig +++ b/src/gui/windows/gpu_performance_measuring.zig @@ -52,7 +52,7 @@ const names = [_][]const u8{ const buffers = 4; var curBuffer: u2 = 0; -var queryObjects: [buffers][@typeInfo(Samples).@"enum".fields.len]c_uint = undefined; +var queryObjects: [buffers][@typeInfo(Samples).@"enum".field_names.len]c_uint = undefined; var activeSample: ?Samples = null; @@ -73,7 +73,7 @@ pub fn deinit() void { pub fn startQuery(sample: Samples) void { std.debug.assert(activeSample == null); // There can be at most one active measurement at a time. activeSample = sample; - c.glBeginQuery(c.GL_TIME_ELAPSED, queryObjects[curBuffer][@intFromEnum(sample)]); + c.glBeginQuery(c.GL_TIME_ELAPSED, queryObjects[curBuffer][@backingInt(sample)]); } pub fn stopQuery() void { diff --git a/src/gui/windows/save_creation.zig b/src/gui/windows/save_creation.zig index 0f1725da23..30d0129231 100644 --- a/src/gui/windows/save_creation.zig +++ b/src/gui/windows/save_creation.zig @@ -49,7 +49,7 @@ fn chooseSeed(seedStr: []const u8) u64 { } fn gamemodeCallback() void { - worldSettings.defaultGamemode = std.enums.fromInt(main.game.Gamemode, @intFromEnum(worldSettings.defaultGamemode) + 1) orelse @enumFromInt(0); + worldSettings.defaultGamemode = std.enums.fromInt(main.game.Gamemode, @backingInt(worldSettings.defaultGamemode) + 1) orelse @fromBackingInt(@intCast(0)); gamemodeInput.child.label.updateText(@tagName(worldSettings.defaultGamemode)); } diff --git a/src/itemdrop.zig b/src/itemdrop.zig index 4a4fb2835c..2276b9ef37 100644 --- a/src/itemdrop.zig +++ b/src/itemdrop.zig @@ -553,13 +553,13 @@ pub const ItemDropRenderer = struct { // MARK: ItemDropRenderer defer data.deinit(); for (model.internalQuads) |quad| { const textureIndex = blocks.meshes.textureIndex(block, quad.quadInfo().textureSlot); - data.append(@as(u32, @intFromEnum(quad)) << 16 | textureIndex); // modelAndTexture + data.append(@as(u32, @backingInt(quad)) << 16 | textureIndex); // modelAndTexture data.append(0); // offsetByNormal } for (model.neighborFacingQuads) |list| { for (list) |quad| { const textureIndex = blocks.meshes.textureIndex(block, quad.quadInfo().textureSlot); - data.append(@as(u32, @intFromEnum(quad)) << 16 | textureIndex); // modelAndTexture + data.append(@as(u32, @backingInt(quad)) << 16 | textureIndex); // modelAndTexture data.append(1); // offsetByNormal } } diff --git a/src/items.zig b/src/items.zig index 28ca3aea16..eef0744fd2 100644 --- a/src/items.zig +++ b/src/items.zig @@ -245,46 +245,46 @@ pub const BaseItemIndex = enum(u16) { // MARK: BaseItemIndex return reverseIndices.get(_id); } pub fn image(self: BaseItemIndex) graphics.Image { - return itemList[@intFromEnum(self)].image; + return itemList[@backingInt(self)].image; } pub fn texture(self: BaseItemIndex) ?graphics.Texture { - return itemList[@intFromEnum(self)].texture; + return itemList[@backingInt(self)].texture; } pub fn id(self: BaseItemIndex) []const u8 { - return itemList[@intFromEnum(self)].id; + return itemList[@backingInt(self)].id; } pub fn name(self: BaseItemIndex) []const u8 { - return itemList[@intFromEnum(self)].name; + return itemList[@backingInt(self)].name; } pub fn tags(self: BaseItemIndex) []const Tag { - return itemList[@intFromEnum(self)].tags; + return itemList[@backingInt(self)].tags; } pub fn stackSize(self: BaseItemIndex) u16 { - return itemList[@intFromEnum(self)].stackSize; + return itemList[@backingInt(self)].stackSize; } pub fn material(self: BaseItemIndex) ?Material { - return itemList[@intFromEnum(self)].material; + return itemList[@backingInt(self)].material; } pub fn block(self: BaseItemIndex) ?u16 { - return itemList[@intFromEnum(self)].block; + return itemList[@backingInt(self)].block; } pub fn displayBlockData(self: BaseItemIndex) ?u16 { - return itemList[@intFromEnum(self)].displayBlockData; + return itemList[@backingInt(self)].displayBlockData; } pub fn hasTag(self: BaseItemIndex, tag: Tag) bool { - return itemList[@intFromEnum(self)].hasTag(tag); + return itemList[@backingInt(self)].hasTag(tag); } pub fn hashCode(self: BaseItemIndex) u32 { - return itemList[@intFromEnum(self)].hashCode(); + return itemList[@backingInt(self)].hashCode(); } pub fn getTexture(self: BaseItemIndex) graphics.Texture { - return itemList[@intFromEnum(self)].getTexture(); + return itemList[@backingInt(self)].getTexture(); } pub fn getTooltip(self: BaseItemIndex) []const u8 { - return itemList[@intFromEnum(self)].getTooltip(); + return itemList[@backingInt(self)].getTooltip(); } pub fn getDisplayBlock(self: BaseItemIndex) ?Block { - return itemList[@intFromEnum(self)].getDisplayBlock(); + return itemList[@backingInt(self)].getDisplayBlock(); } }; @@ -559,7 +559,7 @@ const TextureGenerator = struct { // MARK: TextureGenerator for (tallies.items) |tally| { const better = best == null or tally.score > best.?.score or (tally.score == best.?.score and tally.lightWeight > best.?.lightWeight) or - (tally.score == best.?.score and tally.lightWeight == best.?.lightWeight and @intFromEnum(tally.item) < @intFromEnum(best.?.item)); + (tally.score == best.?.score and tally.lightWeight == best.?.lightWeight and @backingInt(tally.item) < @backingInt(best.?.item)); if (better) best = tally; } return if (best) |b| b.item.material() else null; @@ -751,7 +751,7 @@ pub const ProceduralItemTypeIndex = enum(u16) { pub fn next(self: *ProceduralItemTypeIterator) ?ProceduralItemTypeIndex { if (self.i >= proceduralItemTypeList.items.len) return null; defer self.i += 1; - return @enumFromInt(self.i); + return @fromBackingInt(self.i); } }; @@ -761,7 +761,7 @@ pub const ProceduralItemTypeIndex = enum(u16) { pub fn fromBytes(reader: *BinaryReader) !ProceduralItemTypeIndex { const result = try reader.readEnum(ProceduralItemTypeIndex); - if (@intFromEnum(result) >= proceduralItemTypeList.items.len) return error.InvalidEnumTag; + if (@backingInt(result) >= proceduralItemTypeList.items.len) return error.InvalidEnumTag; return result; } @@ -772,22 +772,22 @@ pub const ProceduralItemTypeIndex = enum(u16) { return proceduralItemTypeIdToIndex.get(_id); } pub fn id(self: ProceduralItemTypeIndex) []const u8 { - return proceduralItemTypeList.items[@intFromEnum(self)].id; + return proceduralItemTypeList.items[@backingInt(self)].id; } pub fn tags(self: ProceduralItemTypeIndex) []const Tag { - return proceduralItemTypeList.items[@intFromEnum(self)].tags; + return proceduralItemTypeList.items[@backingInt(self)].tags; } pub fn properties(self: ProceduralItemTypeIndex) []const PropertyMatrix { - return proceduralItemTypeList.items[@intFromEnum(self)].properties; + return proceduralItemTypeList.items[@backingInt(self)].properties; } pub fn slotInfos(self: ProceduralItemTypeIndex) *const [25]SlotInfo { - return &proceduralItemTypeList.items[@intFromEnum(self)].slotInfos; + return &proceduralItemTypeList.items[@backingInt(self)].slotInfos; } pub fn pixelSources(self: ProceduralItemTypeIndex) *const [16][16]u8 { - return &proceduralItemTypeList.items[@intFromEnum(self)].pixelSources; + return &proceduralItemTypeList.items[@backingInt(self)].pixelSources; } pub fn pixelSourcesOverlay(self: ProceduralItemTypeIndex) *const [16][16]u8 { - return &proceduralItemTypeList.items[@intFromEnum(self)].pixelSourcesOverlay; + return &proceduralItemTypeList.items[@backingInt(self)].pixelSourcesOverlay; } }; @@ -828,7 +828,7 @@ pub const ProceduralItem = struct { // MARK: ProceduralItem type: ProceduralItemTypeIndex, finishedPropertyEvaluation: bool = false, - properties: [@typeInfo(ProceduralItemProperty).@"enum".fields.len]f32 = @splat(0), + properties: [@typeInfo(ProceduralItemProperty).@"enum".field_names.len]f32 = @splat(0), durability: u32, @@ -1012,12 +1012,12 @@ pub const ProceduralItem = struct { // MARK: ProceduralItem } pub fn getProperty(self: *ProceduralItem, prop: ProceduralItemProperty) f32 { - return self.properties[@intFromEnum(prop)]; + return self.properties[@backingInt(prop)]; } pub fn setProperty(self: *ProceduralItem, prop: ProceduralItemProperty, value: f32) void { std.debug.assert(!self.finishedPropertyEvaluation); - self.properties[@intFromEnum(prop)] = value; + self.properties[@backingInt(prop)] = value; } fn getTexture(self: *ProceduralItem) graphics.Texture { @@ -1163,7 +1163,7 @@ pub const Item = union(ItemType) { // MARK: Item switch (typ) { .baseItem => { const index = try reader.readEnum(BaseItemIndex); - return .{.baseItem = itemDeduplicationMap[@intFromEnum(index)]}; + return .{.baseItem = itemDeduplicationMap[@backingInt(index)]}; }, .proceduralItem => { return .{.proceduralItem = try ProceduralItem.fromBytes(reader)}; @@ -1399,13 +1399,13 @@ pub fn globalInit() void { proceduralItemTypeIdToIndex = .{}; itemListSize = 0; - inline for (@typeInfo(modifierList).@"struct".decls) |decl| { - const ModifierStruct: type = @field(modifierList, decl.name); - modifiers.put(main.globalArena.allocator, decl.name, &Modifier.VTable.initFromModifierStruct(ModifierStruct)) catch unreachable; + inline for (@typeInfo(modifierList).@"struct".decl_names) |decl| { + const ModifierStruct: type = @field(modifierList, decl); + modifiers.put(main.globalArena.allocator, decl, &Modifier.VTable.initFromModifierStruct(ModifierStruct)) catch unreachable; } - inline for (@typeInfo(modifierRestrictionList).@"struct".decls) |decl| { - const ModifierRestrictionStruct = @field(modifierRestrictionList, decl.name); - modifierRestrictions.put(main.globalArena.allocator, decl.name, &.{ + inline for (@typeInfo(modifierRestrictionList).@"struct".decl_names) |decl| { + const ModifierRestrictionStruct = @field(modifierRestrictionList, decl); + modifierRestrictions.put(main.globalArena.allocator, decl, &.{ .satisfied = comptime main.meta.castFunctionSelfToAnyopaque(ModifierRestrictionStruct.satisfied), .loadFromZon = comptime main.meta.castFunctionReturnToAnyopaque(ModifierRestrictionStruct.loadFromZon), .printTooltip = comptime main.meta.castFunctionSelfToAnyopaque(ModifierRestrictionStruct.printTooltip), @@ -1433,7 +1433,7 @@ pub fn register(_: []const u8, texturePath: []const u8, replacementTexturePath: newItem.init(main.worldArena, texturePath, replacementTexturePath, colorTexturePath, colorReplacementTexturePath, id, zon); const result = reverseIndices.getOrPut(main.worldArena.allocator, newItem.id) catch unreachable; if (!result.found_existing) { - result.value_ptr.* = @enumFromInt(itemListSize); + result.value_ptr.* = @fromBackingInt(itemListSize); } itemDeduplicationMap[itemListSize] = result.value_ptr.*; @@ -1529,7 +1529,7 @@ pub fn registerProceduralItem(assetFolder: []const u8, id: []const u8, zon: ZonE .pixelSources = pixelSources, .pixelSourcesOverlay = pixelSourcesOverlay, }); - proceduralItemTypeIdToIndex.put(main.worldArena.allocator, idDupe, @enumFromInt(proceduralItemTypeList.items.len - 1)) catch unreachable; + proceduralItemTypeIdToIndex.put(main.worldArena.allocator, idDupe, @fromBackingInt(@intCast(proceduralItemTypeList.items.len - 1))) catch unreachable; std.log.debug("Registered procedural item: '{s}'", .{id}); } diff --git a/src/main.zig b/src/main.zig index 35144e9d84..7aeaf29ae6 100644 --- a/src/main.zig +++ b/src/main.zig @@ -530,18 +530,18 @@ pub fn clientMain() void { // MARK: clientMain() pub fn refAllDeclsRecursiveExceptCImports(comptime T: type) void { if (!@import("builtin").is_test) return; inline for (comptime std.meta.declarations(T)) |decl| blk: { - if (comptime std.mem.eql(u8, decl.name, "c")) continue; - if (comptime std.mem.eql(u8, decl.name, "hbft")) break :blk; - if (comptime std.mem.eql(u8, decl.name, "stb_image")) break :blk; + if (comptime std.mem.eql(u8, decl, "c")) continue; + if (comptime std.mem.eql(u8, decl, "hbft")) break :blk; + if (comptime std.mem.eql(u8, decl, "stb_image")) break :blk; // TODO: Remove this after Zig removes Managed hashmap PixelGuys/Cubyz#308 - if (comptime std.mem.eql(u8, decl.name, "Managed")) continue; - if (@TypeOf(@field(T, decl.name)) == type) { - switch (@typeInfo(@field(T, decl.name))) { - .@"struct", .@"enum", .@"union", .@"opaque" => refAllDeclsRecursiveExceptCImports(@field(T, decl.name)), + if (comptime std.mem.eql(u8, decl, "Managed")) continue; + if (@TypeOf(@field(T, decl)) == type) { + switch (@typeInfo(@field(T, decl))) { + .@"struct", .@"enum", .@"union", .@"opaque" => refAllDeclsRecursiveExceptCImports(@field(T, decl)), else => {}, } } - _ = &@field(T, decl.name); + _ = &@field(T, decl); } } diff --git a/src/meta.zig b/src/meta.zig index 67898222cc..13f0091fd3 100644 --- a/src/meta.zig +++ b/src/meta.zig @@ -2,19 +2,17 @@ const std = @import("std"); // MARK: functionPtrCast() fn CastFunctionSelfToConstAnyopaqueType(Fn: type) type { - var typeInfo = @typeInfo(Fn); - var paramTypes: [typeInfo.@"fn".params.len]type = undefined; - var paramAttributes: [typeInfo.@"fn".params.len]std.builtin.Type.Fn.Param.Attributes = undefined; - for (typeInfo.@"fn".params[0..typeInfo.@"fn".params.len], 0..) |param, i| { - paramTypes[i] = param.type.?; - paramAttributes[i] = .{.@"noalias" = param.is_noalias}; + const typeInfo = @typeInfo(Fn).@"fn"; + var paramTypes: [typeInfo.param_types.len]type = undefined; + for (typeInfo.param_types, 0..) |paramType, i| { + paramTypes[i] = paramType.?; } - const isMutablePointer = @typeInfo(paramTypes[0]) == .pointer and !@typeInfo(paramTypes[0]).pointer.is_const; + const isMutablePointer = @typeInfo(paramTypes[0]) == .pointer and !@typeInfo(paramTypes[0]).pointer.attrs.@"const"; if (@sizeOf(paramTypes[0]) != @sizeOf(*const anyopaque) or @alignOf(paramTypes[0]) != @alignOf(*const anyopaque) or isMutablePointer) { @compileError(std.fmt.comptimePrint("Cannot convert {} to *const anyopaque", .{paramTypes[0]})); } paramTypes[0] = *const anyopaque; - return @Fn(¶mTypes, ¶mAttributes, typeInfo.@"fn".return_type.?, .{.@"callconv" = typeInfo.@"fn".calling_convention, .varargs = typeInfo.@"fn".is_var_args}); + return @Fn(¶mTypes, typeInfo.param_attrs, typeInfo.return_type.?, typeInfo.attrs); } /// Turns the first parameter into a *const anyopaque pub fn castFunctionSelfToConstAnyopaque(function: anytype) *const CastFunctionSelfToConstAnyopaqueType(@TypeOf(function)) { @@ -23,18 +21,16 @@ pub fn castFunctionSelfToConstAnyopaque(function: anytype) *const CastFunctionSe // MARK: functionPtrCast() fn CastFunctionSelfToAnyopaqueType(Fn: type) type { - var typeInfo = @typeInfo(Fn); - var paramTypes: [typeInfo.@"fn".params.len]type = undefined; - var paramAttributes: [typeInfo.@"fn".params.len]std.builtin.Type.Fn.Param.Attributes = undefined; - for (typeInfo.@"fn".params[0..typeInfo.@"fn".params.len], 0..) |param, i| { - paramTypes[i] = param.type.?; - paramAttributes[i] = .{.@"noalias" = param.is_noalias}; + const typeInfo = @typeInfo(Fn).@"fn"; + var paramTypes: [typeInfo.param_types.len]type = undefined; + for (typeInfo.param_types, 0..) |paramType, i| { + paramTypes[i] = paramType.?; } if (@sizeOf(paramTypes[0]) != @sizeOf(*anyopaque) or @alignOf(paramTypes[0]) != @alignOf(*anyopaque)) { @compileError(std.fmt.comptimePrint("Cannot convert {} to *anyopaque", .{paramTypes[0]})); } paramTypes[0] = *anyopaque; - return @Fn(¶mTypes, ¶mAttributes, typeInfo.@"fn".return_type.?, .{.@"callconv" = typeInfo.@"fn".calling_convention, .varargs = typeInfo.@"fn".is_var_args}); + return @Fn(¶mTypes, typeInfo.param_attrs, typeInfo.return_type.?, typeInfo.attrs); } /// Turns the first parameter into a *anyopaque pub fn castFunctionSelfToAnyopaque(function: anytype) *const CastFunctionSelfToAnyopaqueType(@TypeOf(function)) { @@ -42,33 +38,29 @@ pub fn castFunctionSelfToAnyopaque(function: anytype) *const CastFunctionSelfToA } fn CastFunctionReturnToAnyopaqueType(Fn: type) type { - var typeInfo = @typeInfo(Fn); - var paramTypes: [typeInfo.@"fn".params.len]type = undefined; - var paramAttributes: [typeInfo.@"fn".params.len]std.builtin.Type.Fn.Param.Attributes = undefined; - for (typeInfo.@"fn".params[0..typeInfo.@"fn".params.len], 0..) |param, i| { - paramTypes[i] = param.type.?; - paramAttributes[i] = .{.@"noalias" = param.is_noalias}; + const typeInfo = @typeInfo(Fn).@"fn"; + var paramTypes: [typeInfo.param_types.len]type = undefined; + for (typeInfo.param_types, 0..) |paramType, i| { + paramTypes[i] = paramType.?; } - if (@sizeOf(typeInfo.@"fn".return_type.?) != @sizeOf(*anyopaque) or @alignOf(typeInfo.@"fn".return_type.?) != @alignOf(*anyopaque) or @typeInfo(typeInfo.@"fn".return_type.?) == .optional) { - @compileError(std.fmt.comptimePrint("Cannot convert {} to *anyopaque", .{typeInfo.@"fn".return_type.?})); + if (@sizeOf(typeInfo.return_type.?) != @sizeOf(*anyopaque) or @alignOf(typeInfo.return_type.?) != @alignOf(*anyopaque) or @typeInfo(typeInfo.return_type.?) == .optional) { + @compileError(std.fmt.comptimePrint("Cannot convert {} to *anyopaque", .{typeInfo.return_type.?})); } const ReturnType = *anyopaque; - return @Fn(¶mTypes, ¶mAttributes, ReturnType, .{.@"callconv" = typeInfo.@"fn".calling_convention, .varargs = typeInfo.@"fn".is_var_args}); + return @Fn(¶mTypes, typeInfo.param_attrs, ReturnType, typeInfo.attrs); } fn CastFunctionReturnToOptionalAnyopaqueType(Fn: type) type { - var typeInfo = @typeInfo(Fn); - var paramTypes: [typeInfo.@"fn".params.len]type = undefined; - var paramAttributes: [typeInfo.@"fn".params.len]std.builtin.Type.Fn.Param.Attributes = undefined; - for (typeInfo.@"fn".params[0..typeInfo.@"fn".params.len], 0..) |param, i| { - paramTypes[i] = param.type.?; - paramAttributes[i] = .{.@"noalias" = param.is_noalias}; + const typeInfo = @typeInfo(Fn).@"fn"; + var paramTypes: [typeInfo.param_types.len]type = undefined; + for (typeInfo.param_types, 0..) |paramType, i| { + paramTypes[i] = paramType.?; } - if (@sizeOf(typeInfo.@"fn".return_type.?) != @sizeOf(?*anyopaque) or @alignOf(typeInfo.@"fn".return_type.?) != @alignOf(?*anyopaque) or @typeInfo(typeInfo.@"fn".return_type.?) != .optional) { - @compileError(std.fmt.comptimePrint("Cannot convert {} to ?*anyopaque", .{typeInfo.@"fn".return_type.?})); + if (@sizeOf(typeInfo.return_type.?) != @sizeOf(?*anyopaque) or @alignOf(typeInfo.return_type.?) != @alignOf(?*anyopaque) or @typeInfo(typeInfo.return_type.?) != .optional) { + @compileError(std.fmt.comptimePrint("Cannot convert {} to ?*anyopaque", .{typeInfo.return_type.?})); } const ReturnType = ?*anyopaque; - return @Fn(¶mTypes, ¶mAttributes, ReturnType, .{.@"callconv" = typeInfo.@"fn".calling_convention, .varargs = typeInfo.@"fn".is_var_args}); + return @Fn(¶mTypes, typeInfo.param_attrs, ReturnType, typeInfo.attrs); } /// Turns the return parameter into a *anyopaque pub fn castFunctionReturnToAnyopaque(function: anytype) *const CastFunctionReturnToAnyopaqueType(@TypeOf(function)) { diff --git a/src/models.zig b/src/models.zig index 65ed9f2bf9..153d78153a 100644 --- a/src/models.zig +++ b/src/models.zig @@ -75,10 +75,10 @@ pub const ModelIndex = enum(u32) { _, pub fn model(self: ModelIndex) *const Model { - return &models.items()[@intFromEnum(self)]; + return &models.items()[@backingInt(self)]; } pub fn add(self: ModelIndex, offset: u32) ModelIndex { - return @enumFromInt(@intFromEnum(self) + offset); + return @fromBackingInt(@backingInt(self) + offset); } }; @@ -86,11 +86,11 @@ pub const QuadIndex = enum(u16) { _, pub fn quadInfo(self: QuadIndex) *const QuadInfo { - return &quads.items[@intFromEnum(self)]; + return &quads.items[@backingInt(self)]; } pub fn extraQuadInfo(self: QuadIndex) *const ExtraQuadInfo { - return &extraQuadInfos.items[@intFromEnum(self)]; + return &extraQuadInfos.items[@backingInt(self)]; } }; @@ -149,7 +149,7 @@ pub const Model = struct { // MARK: Model // Snap the normals as well: dest.normal = snapToGrid(dest.normal); } - const modelIndex: ModelIndex = @enumFromInt(models.len); + const modelIndex: ModelIndex = @fromBackingInt(@intCast(models.len)); const self = models.addOne(); var amounts: [6]usize = .{0, 0, 0, 0, 0, 0}; var internalAmount: usize = 0; @@ -659,7 +659,7 @@ var nameToIndex: std.StringHashMap(ModelIndex) = undefined; pub fn getModelIndex(string: []const u8) ModelIndex { return nameToIndex.get(string) orelse { std.log.err("Couldn't find voxelModel with name: {s}.", .{string}); - return @enumFromInt(0); + return @fromBackingInt(0); }; } @@ -682,7 +682,7 @@ fn addQuad(info_: QuadInfo) error{Degenerate}!QuadIndex { } } if (cornerEqualities >= 2) return error.Degenerate; // One corner equality is fine, since then the quad degenerates to a triangle, which has a non-zero area. - const index: QuadIndex = @enumFromInt(quads.items.len); + const index: QuadIndex = @fromBackingInt(@intCast(quads.items.len)); if (info.opaqueInLod == 2) { info.opaqueInLod = 0; } else { diff --git a/src/network.zig b/src/network.zig index 4275fe605c..0e6348675f 100644 --- a/src/network.zig +++ b/src/network.zig @@ -189,7 +189,7 @@ const Socket = struct { // MARK: Socket } fn resolveIP(name: []const u8, port: u16) !IpAddress { - var nameBuf: [255]u8 = undefined; + var nameBuf: [254]u8 = undefined; var buf: [16]std.Io.net.HostName.LookupResult = undefined; var resultQueue = std.Io.Queue(std.Io.net.HostName.LookupResult).init(&buf); try std.Io.net.HostName.lookup(try .init(name), main.io, &resultQueue, .{.canonical_name_buffer = &nameBuf, .port = port}); @@ -298,7 +298,7 @@ pub const SocketAddress = struct { const Request = struct { address: SocketAddress, data: []const u8, - requestNotifier: main.utils.Condition = .{}, + requestNotifier: std.Io.Condition = .init, }; /// Implements parts of the STUN(Session Traversal Utilities for NAT) protocol to discover public IP+Port @@ -521,7 +521,7 @@ pub const ConnectionManager = struct { // MARK: ConnectionManager requests: main.List(*Request) = .empty, mutex: main.utils.Mutex = .{}, - waitingToFinishReceive: main.utils.Condition = .{}, + waitingToFinishReceive: std.Io.Condition = .init, allowNewConnections: bool, receiveBuffer: [Connection.maxMtu]u8 = undefined, @@ -601,7 +601,7 @@ pub const ConnectionManager = struct { // MARK: ConnectionManager defer self.mutex.unlock(); for (self.requests.items) |request| { - request.requestNotifier.signal(); + request.requestNotifier.signal(main.io); } self.requests.deinit(main.globalAllocator); @@ -644,7 +644,7 @@ pub const ConnectionManager = struct { // MARK: ConnectionManager defer self.mutex.unlock(); self.requests.append(main.globalAllocator, &request); - request.requestNotifier.timedWait(&self.mutex, timeout) catch {}; + request.requestNotifier.waitTimeout(main.io, &self.mutex.super, .{.duration = .{.raw = timeout, .clock = .awake}}) catch {}; for (self.requests.items, 0..) |req, i| { if (req == &request) { @@ -681,7 +681,7 @@ pub const ConnectionManager = struct { // MARK: ConnectionManager std.debug.assert(self.threadId != std.Thread.getCurrentId()); // WOuld cause deadlock, since we are in a receive. self.mutex.lock(); defer self.mutex.unlock(); - self.waitingToFinishReceive.wait(&self.mutex); + self.waitingToFinishReceive.waitUncancelable(main.io, &self.mutex.super); } pub fn removeConnection(self: *ConnectionManager, conn: *Connection) void { @@ -719,14 +719,14 @@ pub const ConnectionManager = struct { // MARK: ConnectionManager for (self.requests.items) |request| { if (request.address.address.eql(&source.address)) { request.data = main.globalAllocator.dupe(u8, data); - request.requestNotifier.signal(); + request.requestNotifier.signal(main.io); return; } } if (self.online.load(.acquire) and source.address.eql(&self.externalAddress.address)) return; } if (self.allowNewConnections or source.address.eql(&IpAddress{.ip4 = .loopback(source.address.getPort())})) { - if (data.len != 0 and data[0] == @intFromEnum(Connection.ChannelId.init)) { + if (data.len != 0 and data[0] == @backingInt(Connection.ChannelId.init)) { const ip = main.stackAllocator.print("{f}", .{source}); defer main.stackAllocator.free(ip); const user = main.server.User.init(main.server.connectionManager, ip) catch |err| { @@ -751,7 +751,7 @@ pub const ConnectionManager = struct { // MARK: ConnectionManager var lastExternalPacketTime = lastTime; while (self.running.load(.monotonic)) { main.heap.GarbageCollection.syncPoint(); - self.waitingToFinishReceive.broadcast(); + self.waitingToFinishReceive.broadcast(main.io); var source: SocketAddress = undefined; if (self.socket.receive(&self.receiveBuffer, 1, &source)) |data| { self.onReceive(data, source); @@ -1518,7 +1518,7 @@ pub const Connection = struct { // MARK: Connection connectionState: Atomic(ConnectionState), handShakeState: Atomic(HandShakeState) = .init(.start), - handShakeWaiting: main.utils.Condition = .{}, + handShakeWaiting: std.Io.Condition = .init, lastConnectionTime: ?i64, // To distinguish different connections from the same computer to avoid multiple reconnects @@ -1601,7 +1601,7 @@ pub const Connection = struct { // MARK: Connection if (protocolIndex != protocols.reload.id) { // Throw away everything from before the restart - if (conn.restartChannelCounter[@intFromEnum(channelId)] != conn.restartCounter) return .discard; + if (conn.restartChannelCounter[@backingInt(channelId)] != conn.restartCounter) return .discard; return .evaluate; } @@ -1619,11 +1619,11 @@ pub const Connection = struct { // MARK: Connection .connectedVerified, .awaitingReloadVerified => main.game.world.?.shouldReload = true, } main.game.world.?.shouldRestart.store(true, .release); - conn.handShakeWaiting.broadcast(); + conn.handShakeWaiting.broadcast(main.io); } } - if (conn.restartChannelCounter[@intFromEnum(channelId)] < restartCounter) { - conn.restartChannelCounter[@intFromEnum(channelId)] = restartCounter; + if (conn.restartChannelCounter[@backingInt(channelId)] < restartCounter) { + conn.restartChannelCounter[@backingInt(channelId)] = restartCounter; } return .discard; } @@ -1877,7 +1877,7 @@ pub const Connection = struct { // MARK: Connection .awaitingClientConnection => { if (timestamp -% self.nextPacketTimestamp < 0) return; self.nextPacketTimestamp = timestamp +% 100*ms; - self.manager.send(&.{@intFromEnum(ChannelId.keepalive)}, self.remoteAddress, null); + self.manager.send(&.{@backingInt(ChannelId.keepalive)}, self.remoteAddress, null); }, .awaitingServerResponse, .awaitingClientAcknowledgement => { // Send the initial packet once every 100 ms. @@ -1944,7 +1944,7 @@ pub const Connection = struct { // MARK: Connection } pub fn disconnect(self: *Connection) void { - self.manager.send(&.{@intFromEnum(ChannelId.disconnect)}, self.remoteAddress, null); + self.manager.send(&.{@backingInt(ChannelId.disconnect)}, self.remoteAddress, null); self.connectionState.store(.disconnected, .monotonic); if (builtin.os.tag == .windows and !self.isServerSide() and main.server.world != null) { 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. @@ -1953,7 +1953,7 @@ pub const Connection = struct { // MARK: Connection if (self.user) |user| { main.server.disconnect(user); } else { - self.handShakeWaiting.broadcast(); + self.handShakeWaiting.broadcast(main.io); if (self.handShakeState.load(.monotonic) == .complete) { main.exitToMenu(); } diff --git a/src/network/authentication.zig b/src/network/authentication.zig index a92ac246e1..d77020c392 100644 --- a/src/network/authentication.zig +++ b/src/network/authentication.zig @@ -77,10 +77,10 @@ pub const KeyCollection = struct { // Provides multiple methods to allow server hashedResult = out; } - const functionType = @TypeOf(@TypeOf(@field(Storage, decl.name)).generateDeterministic); - const seed = hashedResult[0..@sizeOf(@typeInfo(functionType).@"fn".params[0].type.?)].*; + const functionType = @TypeOf(@TypeOf(@field(Storage, decl)).generateDeterministic); + const seed = hashedResult[0..@sizeOf(@typeInfo(functionType).@"fn".param_types[0].?)].*; - @field(Storage, decl.name) = @TypeOf(@field(Storage, decl.name)).generateDeterministic(seed) catch @panic("Failed to generate key pair for " ++ decl.name); + @field(Storage, decl) = @TypeOf(@field(Storage, decl)).generateDeterministic(seed) catch @panic("Failed to generate key pair for " ++ decl); } initialized = true; } @@ -90,14 +90,14 @@ pub const KeyCollection = struct { // Provides multiple methods to allow server const result = ZonElement.initObject(allocator); inline for (comptime std.meta.declarations(Storage)) |decl| { const bytes = blk: { - if (@hasDecl(@TypeOf(@field(Storage, decl.name).public_key), "toBytes")) { - break :blk @field(Storage, decl.name).public_key.toBytes(); + if (@hasDecl(@TypeOf(@field(Storage, decl).public_key), "toBytes")) { + break :blk @field(Storage, decl).public_key.toBytes(); } else { - break :blk @field(Storage, decl.name).public_key.toUncompressedSec1(); + break :blk @field(Storage, decl).public_key.toUncompressedSec1(); } }; var base64: [std.base64.standard.Encoder.calcSize(bytes.len)]u8 = undefined; - result.putOwnedString(decl.name, std.base64.standard.Encoder.encode(&base64, &bytes)); + result.putOwnedString(decl, std.base64.standard.Encoder.encode(&base64, &bytes)); } return result; } diff --git a/src/network/protocols.zig b/src/network/protocols.zig index 9aa8d9497d..74003e88fe 100644 --- a/src/network/protocols.zig +++ b/src/network/protocols.zig @@ -27,8 +27,8 @@ pub var bytesReceived: [256]Atomic(usize) = @splat(.init(0)); pub var bytesSent: [256]Atomic(usize) = @splat(.init(0)); pub fn init() void { // MARK: init() - inline for (@typeInfo(@This()).@"struct".decls) |decl| { - const Protocol = @field(@This(), decl.name); + inline for (@typeInfo(@This()).@"struct".decl_names) |decl| { + const Protocol = @field(@This(), decl); if (@TypeOf(Protocol) == type and @hasDecl(Protocol, "id")) { const id = Protocol.id; if (clientReceiveList[id] == null and serverReceiveList[id] == null) { @@ -88,13 +88,13 @@ pub const reload = struct { // MARK: reload pub const handShake = struct { // MARK: handShake pub const id: u8 = 1; - var assetsLoadedCondition: main.utils.Condition = .{}; + var assetsLoadedCondition: std.Io.Condition = .init; var hasFinishedLoadingAssets: bool = false; var handshakeZon: ZonElement = undefined; pub fn clientReceive(conn: *Connection, reader: *utils.BinaryReader) !void { const newState = try reader.readEnum(Connection.HandShakeState); - if (@intFromEnum(conn.handShakeState.load(.monotonic)) < @intFromEnum(newState)) { + if (@backingInt(conn.handShakeState.load(.monotonic)) < @backingInt(newState)) { conn.handShakeState.store(newState, .monotonic); switch (newState) { .userData, .signatureResponse, .reload => return error.InvalidSide, @@ -126,10 +126,10 @@ pub const handShake = struct { // MARK: handShake handshakeZon = ZonElement.parseFromString(main.stackAllocator, null, reader.remaining); defer handshakeZon.deinit(main.stackAllocator); conn.handShakeState.store(.complete, .monotonic); - conn.handShakeWaiting.broadcast(); // Notify the waiting client thread. + conn.handShakeWaiting.broadcast(main.io); // Notify the waiting client thread. conn.mutex.lock(); while (!hasFinishedLoadingAssets) { - assetsLoadedCondition.wait(&conn.mutex); + assetsLoadedCondition.waitUncancelable(main.io, &conn.mutex.super); } conn.mutex.unlock(); hasFinishedLoadingAssets = false; @@ -143,7 +143,7 @@ pub const handShake = struct { // MARK: handShake pub fn serverReceive(conn: *Connection, reader: *utils.BinaryReader) !void { const newState = try reader.readEnum(Connection.HandShakeState); - if (@intFromEnum(conn.handShakeState.load(.monotonic)) < @intFromEnum(newState)) { + if (@backingInt(conn.handShakeState.load(.monotonic)) < @backingInt(newState)) { conn.handShakeState.store(newState, .monotonic); stateSwitch: switch (newState) { .userData => { @@ -206,7 +206,7 @@ pub const handShake = struct { // MARK: handShake defer dir.close(); var writer = try std.Io.Writer.Allocating.initCapacity(main.stackAllocator.allocator, 16); defer writer.deinit(); - try writer.writer.writeByte(@intFromEnum(Connection.HandShakeState.assets)); + try writer.writer.writeByte(@backingInt(Connection.HandShakeState.assets)); try utils.Compression.pack(dir, &writer.writer); conn.send(.secure, id, writer.written()); } @@ -230,8 +230,8 @@ pub const handShake = struct { // MARK: handShake const zonObject = ZonElement.initObject(main.stackAllocator); defer zonObject.deinit(main.stackAllocator); zonObject.put("player", conn.user.?.player().save(main.stackAllocator, .playerHimself)); - zonObject.put("player_id", @intFromEnum(conn.user.?.id)); - zonObject.put("gamemode", @intFromEnum(conn.user.?.gamemode.raw)); + zonObject.put("player_id", @backingInt(conn.user.?.id)); + zonObject.put("gamemode", @backingInt(conn.user.?.gamemode.raw)); zonObject.put("blockPalette", main.server.world.?.blockPalette.storeToZon(main.stackAllocator)); zonObject.put("itemPalette", main.server.world.?.itemPalette.storeToZon(main.stackAllocator)); zonObject.put("toolPalette", main.server.world.?.proceduralItemPalette.storeToZon(main.stackAllocator)); @@ -239,7 +239,7 @@ pub const handShake = struct { // MARK: handShake zonObject.put("entityModelPalette", main.server.world.?.entityModelPalette.storeToZon(main.stackAllocator)); zonObject.put("entityComponentPalette", main.server.world.?.entityComponentPalette.storeToZon(main.stackAllocator)); - const outData = zonObject.toStringEfficient(main.stackAllocator, &[1]u8{@intFromEnum(Connection.HandShakeState.serverData)}); + const outData = zonObject.toStringEfficient(main.stackAllocator, &[1]u8{@backingInt(Connection.HandShakeState.serverData)}); defer main.stackAllocator.free(outData); conn.send(.secure, id, outData); } @@ -258,14 +258,14 @@ pub const handShake = struct { // MARK: handShake try conn.secureChannel.startTlsHandshake(); conn.secureChannel.finishedCollectingClientVerificationData = true; - const prefix: [1]u8 = .{@intFromEnum(Connection.HandShakeState.userData)}; + const prefix: [1]u8 = .{@backingInt(Connection.HandShakeState.userData)}; const data = zonObject.toStringEfficient(main.stackAllocator, &prefix); defer main.stackAllocator.free(data); conn.send(.secure, id, data); }, .reload => { - conn.send(.secure, id, &.{@intFromEnum(Connection.HandShakeState.reload)}); + conn.send(.secure, id, &.{@backingInt(Connection.HandShakeState.reload)}); }, else => unreachable, } @@ -276,7 +276,7 @@ pub const handShake = struct { // MARK: handShake const expectedRestartCounter = conn.restartCounter; while (true) { try main.io.checkCancel(); - conn.handShakeWaiting.timedWait(&conn.mutex, .fromMilliseconds(16)) catch { + conn.handShakeWaiting.waitTimeout(main.io, &conn.mutex.super, .{.duration = .{.raw = .fromMilliseconds(16), .clock = .awake}}) catch { main.heap.GarbageCollection.syncPoint(); continue; }; @@ -293,7 +293,7 @@ pub const handShake = struct { // MARK: handShake pub fn signalLoadedAssets() void { main.network.protocols.handShake.hasFinishedLoadingAssets = true; - main.network.protocols.handShake.assetsLoadedCondition.signal(); + main.network.protocols.handShake.assetsLoadedCondition.signal(main.io); } }; @@ -569,7 +569,7 @@ pub const entity = struct { // MARK: entity const elem = zonArray.array.items[i]; switch (elem) { .int => { - main.client.entity_manager.removeEntity(@enumFromInt(elem.as(u32) orelse return error.Invalid)); + main.client.entity_manager.removeEntity(@fromBackingInt(elem.as(u32) orelse return error.Invalid)); }, .object => { try main.client.entity_manager.addEntity(elem); @@ -728,7 +728,7 @@ pub const genericUpdate = struct { // MARK: genericUpdate } pub fn sendGamemode(conn: *Connection, gamemode: main.game.Gamemode) void { - conn.send(.secure, id, &.{@intFromEnum(UpdateType.gamemode), @intFromEnum(gamemode)}); + conn.send(.secure, id, &.{@backingInt(UpdateType.gamemode), @backingInt(gamemode)}); } pub fn sendTPCoordinates(conn: *Connection, pos: Vec3d) void { @@ -792,7 +792,7 @@ pub const genericUpdate = struct { // MARK: genericUpdate } pub fn sendClear(conn: *Connection, cleartype: ClearType) void { - conn.send(.lossy, id, &.{@intFromEnum(UpdateType.clear), @intFromEnum(cleartype)}); // TODO change channel afer #1879 + conn.send(.lossy, id, &.{@backingInt(UpdateType.clear), @backingInt(cleartype)}); // TODO change channel afer #1879 } }; @@ -1074,7 +1074,7 @@ pub const EntityComponentUpdate = struct { // MARK: EntityComponentUpdate }; pub fn clientReceive(_: *Connection, reader: *utils.BinaryReader) !void { - const entityId: main.entity.Entity = @enumFromInt(try reader.readVarInt(u32)); + const entityId: main.entity.Entity = @fromBackingInt(try reader.readVarInt(u32)); const componentId = try reader.readVarInt(u32); const actionType: ActionType = try reader.readEnum(ActionType); @@ -1089,7 +1089,7 @@ pub const EntityComponentUpdate = struct { // MARK: EntityComponentUpdate var writer = utils.BinaryWriter.init(main.stackAllocator); defer writer.deinit(); - writer.writeVarInt(u32, @intFromEnum(entityId)); + writer.writeVarInt(u32, @backingInt(entityId)); writer.writeVarInt(u32, componentId); writer.writeEnum(ActionType, ActionType.unload); @@ -1099,7 +1099,7 @@ pub const EntityComponentUpdate = struct { // MARK: EntityComponentUpdate var writer = utils.BinaryWriter.init(main.stackAllocator); defer writer.deinit(); - writer.writeVarInt(u32, @intFromEnum(entityId)); + writer.writeVarInt(u32, @backingInt(entityId)); writer.writeVarInt(u32, componentId); writer.writeEnum(ActionType, ActionType.load); // specific to `load` diff --git a/src/physics.zig b/src/physics.zig index cd22dda22b..5dbce4511c 100644 --- a/src/physics.zig +++ b/src/physics.zig @@ -267,7 +267,7 @@ pub const collision = struct { // MARK: collision } pub fn collideOrStep(comptime side: main.sync.Side, comptime dir: Direction, amount: f64, pos: Vec3d, hitBox: Box, steppingHeight: f64) Vec3d { - const index = @intFromEnum(dir); + const index = @backingInt(dir); // First argument is amount we end up moving in dir, second argument is how far up we step var resultingMovement: Vec3d = .{0, 0, 0}; diff --git a/src/renderer/chunk_meshing.zig b/src/renderer/chunk_meshing.zig index 5eb4456d75..e3756743c0 100644 --- a/src/renderer/chunk_meshing.zig +++ b/src/renderer/chunk_meshing.zig @@ -327,11 +327,11 @@ const FaceGroups = enum(u32) { optional, pub fn neighbor(n: main.chunk.Neighbor) FaceGroups { - return @enumFromInt(@intFromEnum(FaceGroups.neighbor0) + @intFromEnum(n)); + return @fromBackingInt(@backingInt(FaceGroups.neighbor0) + @backingInt(n)); } pub fn neighborLod(n: main.chunk.Neighbor) FaceGroups { - return @enumFromInt(@intFromEnum(FaceGroups.neighborLod0) + @intFromEnum(n)); + return @fromBackingInt(@backingInt(FaceGroups.neighborLod0) + @backingInt(n)); } }; @@ -387,9 +387,9 @@ const PrimitiveMesh = struct { // MARK: PrimitiveMesh var list: [6][]FaceData = undefined; for (0..6) |i| { if (!isNeighborLod[i]) { - list[i] = self.completeList.getRange(.neighbor(@enumFromInt(i))); + list[i] = self.completeList.getRange(.neighbor(@fromBackingInt(@intCast(i)))); } else { - list[i] = self.completeList.getRange(.neighborLod(@enumFromInt(i))); + list[i] = self.completeList.getRange(.neighborLod(@fromBackingInt(@intCast(i)))); } len += list[i].len; } @@ -412,7 +412,7 @@ const PrimitiveMesh = struct { // MARK: PrimitiveMesh } } if (normal < 6) { - const normalDir: chunk.Neighbor = @enumFromInt(normal); + const normalDir: chunk.Neighbor = @fromBackingInt(@intCast(normal)); @memcpy(fullBuffer[i..][0..list[normalDir.reverse().toInt()].len], list[normalDir.reverse().toInt()]); i += @intCast(list[normalDir.reverse().toInt()].len); } @@ -1458,9 +1458,9 @@ pub const ChunkMesh = struct { // MARK: ChunkMesh var list: [6][]FaceData = undefined; for (0..6) |i| { if (!self.isNeighborLod[i]) { - list[i] = self.transparentMesh.completeList.getRange(.neighbor(@enumFromInt(i))); + list[i] = self.transparentMesh.completeList.getRange(.neighbor(@fromBackingInt(@intCast(i)))); } else { - list[i] = self.transparentMesh.completeList.getRange(.neighborLod(@enumFromInt(i))); + list[i] = self.transparentMesh.completeList.getRange(.neighborLod(@fromBackingInt(@intCast(i)))); } len += list[i].len; } diff --git a/src/renderer/lighting.zig b/src/renderer/lighting.zig index 31ea9ef50d..875aec7311 100644 --- a/src/renderer/lighting.zig +++ b/src/renderer/lighting.zig @@ -249,7 +249,7 @@ pub const ChannelChunk = struct { // MARK: ChannelChunk std.debug.assert(lightQueue.isEmpty()); for (lights) |entry| { var result = entry; - calculateIncomingOcclusion(&result.value, self.ch.data.getValue(entry.pos.toIndex()), self.ch.pos.voxelSize, @enumFromInt(entry.sourceDir)); + calculateIncomingOcclusion(&result.value, self.ch.data.getValue(entry.pos.toIndex()), self.ch.pos.voxelSize, @fromBackingInt(entry.sourceDir)); if (result.value[0] != 0 or result.value[1] != 0 or result.value[2] != 0) lightQueue.pushBack(result); } self.propagateDirect(lightQueue, lightRefreshList); @@ -259,7 +259,7 @@ pub const ChannelChunk = struct { // MARK: ChannelChunk std.debug.assert(lightQueue.isEmpty()); for (lights) |entry| { var result = entry; - calculateIncomingOcclusion(&result.value, self.ch.data.getValue(entry.pos.toIndex()), self.ch.pos.voxelSize, @enumFromInt(entry.sourceDir)); + calculateIncomingOcclusion(&result.value, self.ch.data.getValue(entry.pos.toIndex()), self.ch.pos.voxelSize, @fromBackingInt(entry.sourceDir)); lightQueue.pushBack(result); } return self.propagateDestructive(lightQueue, constructiveEntries, false, lightRefreshList); diff --git a/src/renderer/mesh_storage.zig b/src/renderer/mesh_storage.zig index 30f562ffb4..ba323c436b 100644 --- a/src/renderer/mesh_storage.zig +++ b/src/renderer/mesh_storage.zig @@ -829,7 +829,7 @@ pub fn addBreakingAnimation(pos: Vec3i, breakingProgress: f32) void { } for (&model.neighborFacingQuads, 0..) |quads, n| { for (quads) |quadIndex| { - addBreakingAnimationFace(pos, quadIndex, texture, @enumFromInt(n), block.transparent()); + addBreakingAnimationFace(pos, quadIndex, texture, @fromBackingInt(@intCast(n)), block.transparent()); } } } @@ -890,7 +890,7 @@ pub fn removeBreakingAnimation(pos: Vec3i) void { } for (&model.neighborFacingQuads, 0..) |quads, n| { for (quads) |quadIndex| { - removeBreakingAnimationFace(pos, quadIndex, @enumFromInt(n)); + removeBreakingAnimationFace(pos, quadIndex, @fromBackingInt(@intCast(n))); } } } diff --git a/src/rotation.zig b/src/rotation.zig index 44a062aa3a..7312054df2 100644 --- a/src/rotation.zig +++ b/src/rotation.zig @@ -224,21 +224,21 @@ fn rayTriangleIntersection(origin: Vec3f, direction: Vec3f, triangle: [3]Vec3f) pub fn init() void { rotationModes = .init(main.globalAllocator.allocator); - inline for (@typeInfo(rotations).@"struct".decls) |declaration| { - register(declaration.name, @field(rotations, declaration.name)); + inline for (@typeInfo(rotations).@"struct".decl_names) |declaration| { + register(declaration, @field(rotations, declaration)); } } pub fn reset() void { - inline for (@typeInfo(rotations).@"struct".decls) |declaration| { - @field(rotations, declaration.name).reset(); + inline for (@typeInfo(rotations).@"struct".decl_names) |declaration| { + @field(rotations, declaration).reset(); } } pub fn deinit() void { rotationModes.deinit(); - inline for (@typeInfo(rotations).@"struct".decls) |declaration| { - @field(rotations, declaration.name).deinit(); + inline for (@typeInfo(rotations).@"struct".decl_names) |declaration| { + @field(rotations, declaration).deinit(); } } @@ -251,12 +251,12 @@ pub fn getByID(id: []const u8) *const RotationMode { pub fn register(comptime id: []const u8, comptime Mode: type) void { Mode.init(); var result: RotationMode = RotationMode{}; - inline for (@typeInfo(RotationMode).@"struct".fields) |field| { - if (@hasDecl(Mode, field.name)) { - if (field.type == @TypeOf(@field(Mode, field.name))) { - @field(result, field.name) = @field(Mode, field.name); + inline for (@typeInfo(RotationMode).@"struct".field_names, @typeInfo(RotationMode).@"struct".field_types) |field, FieldType| { + if (@hasDecl(Mode, field)) { + if (FieldType == @TypeOf(@field(Mode, field))) { + @field(result, field) = @field(Mode, field); } else { - @field(result, field.name) = &@field(Mode, field.name); + @field(result, field) = &@field(Mode, field); } } } diff --git a/src/server/Entity.zig b/src/server/Entity.zig index 169412a84f..e86366dfa2 100644 --- a/src/server/Entity.zig +++ b/src/server/Entity.zig @@ -51,7 +51,7 @@ pub fn save(self: *const @This(), allocator: NeverFailingAllocator, audience: ma zon.put("rotation", self.rot); zon.put("health", self.health); zon.put("energy", self.energy); - zon.put("id", @intFromEnum(self.id)); + zon.put("id", @backingInt(self.id)); var base64 = main.entity.server.componentsToBase64(allocator, self.id, audience); defer base64.deinit(allocator); diff --git a/src/server/command.zig b/src/server/command.zig index 9753097a1e..120e450a78 100644 --- a/src/server/command.zig +++ b/src/server/command.zig @@ -56,15 +56,15 @@ fn initExecutionFn(comptime name: []const u8) *const fn (args: []const u8, sourc pub fn init() void { commands = .init(main.globalAllocator.allocator); - inline for (@typeInfo(commandList).@"struct".decls) |decl| { - commands.put(decl.name, .{ - .name = decl.name, - .description = @field(commandList, decl.name).description, - .usage = @field(commandList, decl.name).usage, - .exec = initExecutionFn(decl.name), - .permissionPath = "/command/" ++ decl.name, + inline for (@typeInfo(commandList).@"struct".decl_names) |decl| { + commands.put(decl, .{ + .name = decl, + .description = @field(commandList, decl).description, + .usage = @field(commandList, decl).usage, + .exec = initExecutionFn(decl), + .permissionPath = "/command/" ++ decl, }) catch unreachable; - std.log.debug("Registered command: '/{s}'", .{decl.name}); + std.log.debug("Registered command: '/{s}'", .{decl}); } } diff --git a/src/server/permission.zig b/src/server/permission.zig index c2f3ab6fe5..39adf1f7cb 100644 --- a/src/server/permission.zig +++ b/src/server/permission.zig @@ -156,7 +156,7 @@ const GroupInstance = struct { // MARK: GroupInstance fn save(self: *GroupInstance, id: Group) void { if (builtin.is_test) return; sync.threadContext.assertCorrectContext(.server); - const path = main.stackAllocator.print("{s}/{d}.group", .{groupsPath, @intFromEnum(id)}); + const path = main.stackAllocator.print("{s}/{d}.group", .{groupsPath, @backingInt(id)}); defer main.stackAllocator.free(path); var writer: main.utils.BinaryWriter = .init(main.stackAllocator); @@ -216,7 +216,7 @@ pub const Group = enum(u32) { // MARK: Group if (result.found_existing) return error.AlreadyExists; result.key_ptr.* = groupsArena.allocator().dupe(u8, name); - result.value_ptr.* = @enumFromInt(groups.items.len); + result.value_ptr.* = @fromBackingInt(@intCast(groups.items.len)); groups.append(GroupInstance.init(groupsArena.allocator(), result.value_ptr.*, result.key_ptr.*)); saveMetaData(groupsArena.allocator()) catch |err| { std.log.err("Couldn't save permission groups metadata: {t}", .{err}); @@ -226,7 +226,7 @@ pub const Group = enum(u32) { // MARK: Group pub fn fromBytes(reader: *main.utils.BinaryReader) !Group { const group = try reader.readEnum(Group); - if (groups.items[@intFromEnum(group)] == null) return error.GroupNotFound; + if (groups.items[@backingInt(group)] == null) return error.GroupNotFound; return group; } @@ -240,7 +240,7 @@ pub const Group = enum(u32) { // MARK: Group } fn getInstance(self: Group) error{GroupNotFound}!*GroupInstance { - return groups.items[@intFromEnum(self)] orelse return error.GroupNotFound; + return groups.items[@backingInt(self)] orelse return error.GroupNotFound; } /// If the group still exists, this deletes the group and returns true. Otherwise returns false @@ -249,10 +249,10 @@ pub const Group = enum(u32) { // MARK: Group // if the group already doens't exist anymore we can skip. // but if the group still exists and is not deleted from the groupNameToIdMap then something is wrong, which is why we assert it here std.debug.assert(groupNameToIdMap.remove((self.getInstance() catch return false).name)); - groups.items[@intFromEnum(self)] = null; + groups.items[@backingInt(self)] = null; if (builtin.is_test) return true; - const path = main.stackAllocator.print("{s}/{d}.group", .{groupsPath, @intFromEnum(self)}); + const path = main.stackAllocator.print("{s}/{d}.group", .{groupsPath, @backingInt(self)}); defer main.stackAllocator.free(path); main.files.cubyzDir().deleteFile(path) catch |err| { std.log.err("Couldn't delete group file even though it exits: {t}", .{err}); @@ -296,7 +296,7 @@ pub fn deinit() void { fn addGroupFromBin(group: Group, data: []const u8) void { var reader: main.utils.BinaryReader = .init(data); const groupInstance = GroupInstance.fromBytes(groupsArena.allocator(), &reader) catch |err| { - std.log.err("Group with id {d} has invalid content skipping: {t}", .{@intFromEnum(group), err}); + std.log.err("Group with id {d} has invalid content skipping: {t}", .{@backingInt(group), err}); groups.append(null); return; }; @@ -326,7 +326,7 @@ pub fn loadGroups(dir: main.files.Dir, worldPath: []const u8) !void { } const data = try dir.read(main.stackAllocator, path); defer main.stackAllocator.free(data); - addGroupFromBin(@enumFromInt(id), data); + addGroupFromBin(@fromBackingInt(@intCast(id)), data); } createDefaultPermissionGroups(); } diff --git a/src/server/server.zig b/src/server/server.zig index 03830290d8..beba00f247 100644 --- a/src/server/server.zig +++ b/src/server/server.zig @@ -293,7 +293,7 @@ pub const User = struct { // MARK: User var freeId: u32 = 0; // TODO: Use id provided by the ECS. pub fn initPlayer(self: *User) void { - self.id = @enumFromInt(freeId); + self.id = @fromBackingInt(freeId); freeId += 1; world.?.loadPlayer(self) catch { @@ -825,7 +825,7 @@ pub fn removePlayer(user: *User) void { // MARK: removePlayer() // Let the other clients know about that this new one left. const zonArray = main.ZonElement.initArray(main.stackAllocator); defer zonArray.deinit(main.stackAllocator); - zonArray.array.append(.{.int = @intFromEnum(user.id)}); + zonArray.array.append(.{.int = @backingInt(user.id)}); const data = zonArray.toStringEfficient(main.stackAllocator, &.{}); defer main.stackAllocator.free(data); const userList = getUserList(main.stackAllocator); diff --git a/src/server/terrain/CaveBiomeMap.zig b/src/server/terrain/CaveBiomeMap.zig index 81074728f5..cbc572002b 100644 --- a/src/server/terrain/CaveBiomeMap.zig +++ b/src/server/terrain/CaveBiomeMap.zig @@ -105,10 +105,10 @@ pub const CaveBiomeGenerator = struct { // MARK: CaveBiomeGenerator defaultState: GeneratorState, const generatorRegistry: std.StaticStringMap(CaveBiomeGenerator) = .initComptime(blk: { - const decls = @typeInfo(cave_biome_generators).@"struct".decls; + const decls = @typeInfo(cave_biome_generators).@"struct".decl_names; var generators: [decls.len]struct { []const u8, CaveBiomeGenerator } = undefined; for (0..decls.len) |i| { - const Generator = @field(cave_biome_generators, decls[i].name); + const Generator = @field(cave_biome_generators, decls[i]); generators[i] = .{Generator.id, .{ .init = &Generator.init, .generate = &Generator.generate, diff --git a/src/server/terrain/CaveMap.zig b/src/server/terrain/CaveMap.zig index de7ce7f0db..72c89d7740 100644 --- a/src/server/terrain/CaveMap.zig +++ b/src/server/terrain/CaveMap.zig @@ -95,10 +95,10 @@ pub const CaveGenerator = struct { // MARK: CaveGenerator defaultState: GeneratorState, const generatorRegistry: std.StaticStringMap(CaveGenerator) = .initComptime(blk: { - const decls = @typeInfo(cave_generators).@"struct".decls; + const decls = @typeInfo(cave_generators).@"struct".decl_names; var generators: [decls.len]struct { []const u8, CaveGenerator } = undefined; for (0..decls.len) |i| { - const Generator = @field(cave_generators, decls[i].name); + const Generator = @field(cave_generators, decls[i]); generators[i] = .{Generator.id, .{ .init = &Generator.init, .generate = &Generator.generate, diff --git a/src/server/terrain/ClimateMap.zig b/src/server/terrain/ClimateMap.zig index f09f7811b7..b90a0c8b69 100644 --- a/src/server/terrain/ClimateMap.zig +++ b/src/server/terrain/ClimateMap.zig @@ -75,10 +75,10 @@ pub const ClimateMapGenerator = struct { generateMapFragment: *const fn (fragment: *ClimateMapFragment, seed: u64) void, const generatorRegistry: std.StaticStringMap(ClimateMapGenerator) = .initComptime(blk: { - const decls = @typeInfo(climate_generators).@"struct".decls; + const decls = @typeInfo(climate_generators).@"struct".decl_names; var generators: [decls.len]struct { []const u8, ClimateMapGenerator } = undefined; for (0..decls.len) |i| { - const Generator = @field(climate_generators, decls[i].name); + const Generator = @field(climate_generators, decls[i]); generators[i] = .{Generator.id, .{ .init = &Generator.init, .generateMapFragment = &Generator.generateMapFragment, diff --git a/src/server/terrain/StructureMap.zig b/src/server/terrain/StructureMap.zig index 65ae9b1fd1..c0a22f9ae8 100644 --- a/src/server/terrain/StructureMap.zig +++ b/src/server/terrain/StructureMap.zig @@ -134,10 +134,10 @@ pub const StructureMapGenerator = struct { defaultState: GeneratorState, const generatorRegistry: std.StaticStringMap(StructureMapGenerator) = .initComptime(blk: { - const decls = @typeInfo(structure_map_generators).@"struct".decls; + const decls = @typeInfo(structure_map_generators).@"struct".decl_names; var generators: [decls.len]struct { []const u8, StructureMapGenerator } = undefined; for (0..decls.len) |i| { - const Generator = @field(structure_map_generators, decls[i].name); + const Generator = @field(structure_map_generators, decls[i]); generators[i] = .{Generator.id, .{ .init = &Generator.init, .generate = &Generator.generate, diff --git a/src/server/terrain/SurfaceMap.zig b/src/server/terrain/SurfaceMap.zig index 7b959e243d..349da2a101 100644 --- a/src/server/terrain/SurfaceMap.zig +++ b/src/server/terrain/SurfaceMap.zig @@ -249,10 +249,10 @@ pub const MapGenerator = struct { generateMapFragment: *const fn (fragment: *MapFragment, seed: u64) void, const generatorRegistry: std.StaticStringMap(MapGenerator) = .initComptime(blk: { - const decls = @typeInfo(map_generators).@"struct".decls; + const decls = @typeInfo(map_generators).@"struct".decl_names; var generators: [decls.len]struct { []const u8, MapGenerator } = undefined; for (0..decls.len) |i| { - const Generator = @field(map_generators, decls[i].name); + const Generator = @field(map_generators, decls[i]); generators[i] = .{Generator.id, .{ .init = &Generator.init, .generateMapFragment = &Generator.generateMapFragment, diff --git a/src/server/terrain/biomes.zig b/src/server/terrain/biomes.zig index d80b4838bb..b11fd79fe8 100644 --- a/src/server/terrain/biomes.zig +++ b/src/server/terrain/biomes.zig @@ -83,7 +83,7 @@ pub fn hashGeneric(input: anytype) u64 { const T = @TypeOf(input); return switch (@typeInfo(T)) { .bool => hashCombine(hashInt(@intFromBool(input)), 0xbf58476d1ce4e5b9), - .@"enum" => hashCombine(hashInt(@as(u64, @intFromEnum(input))), 0x94d049bb133111eb), + .@"enum" => hashCombine(hashInt(@as(u64, @backingInt(input))), 0x94d049bb133111eb), .int, .float => blk: { const value = @as(@Int(.unsigned, @bitSizeOf(T)), @bitCast(input)); break :blk hashInt(@as(u64, value)); @@ -93,9 +93,9 @@ pub fn hashGeneric(input: anytype) u64 { break :blk input.getHash(); } var result: u64 = hashGeneric(@typeName(T)); - inline for (@typeInfo(T).@"struct".fields) |field| { - const keyHash = hashGeneric(@as([]const u8, field.name)); - const valueHash = hashGeneric(@field(input, field.name)); + inline for (@typeInfo(T).@"struct".field_names) |field| { + const keyHash = hashGeneric(@as([]const u8, field)); + const valueHash = hashGeneric(@field(input, field)); const keyValueHash = hashCombine(keyHash, valueHash); result = hashCombine(result, keyValueHash); } @@ -197,9 +197,9 @@ pub const Biome = struct { // MARK: Biome var result: ClimateProperties = .{}; outer: for (zon.toSlice()) |child| { const climate = child.as([]const u8) orelse ""; - inline for (@typeInfo(ClimateProperties).@"struct".fields) |field| { - if (std.mem.eql(u8, field.name, climate)) { - @field(result, field.name) = true; + inline for (@typeInfo(ClimateProperties).@"struct".field_names) |field| { + if (std.mem.eql(u8, field, climate)) { + @field(result, field) = true; continue :outer; } } @@ -405,7 +405,7 @@ pub const Biome = struct { // MARK: Biome } pub fn hasTag(self: Biome, tag: Tag) bool { - return std.mem.containsAtLeastScalar(Tag, self.tags, 1, tag); + return std.mem.containsAtLeastScalar(Tag, self.tags, tag, 1); } }; diff --git a/src/server/terrain/cavegen/SdfCaveGenerator.zig b/src/server/terrain/cavegen/SdfCaveGenerator.zig index 3aa91893a3..046724d9d6 100644 --- a/src/server/terrain/cavegen/SdfCaveGenerator.zig +++ b/src/server/terrain/cavegen/SdfCaveGenerator.zig @@ -108,7 +108,7 @@ fn generateMap(map: *CaveMapFragment, output: Array3D(f32), biomeNoiseStrength: const outerSizeShift = std.math.log2_int(u31, outerSize); const outerSizeFloat: f32 = @floatFromInt(outerSize); - const noise = FractalNoise3D.generateAligned(main.stackAllocator, map.pos.wx, map.pos.wy, map.pos.wz, outerSize, CaveMapFragment.width*map.pos.voxelSize/outerSize + 1, CaveMapFragment.width*map.pos.voxelSize/outerSize + 1, CaveMapFragment.height*map.pos.voxelSize/outerSize + 1, worldSeed ^ 4329561871 ^ 112*@intFromEnum(mode), noiseScale); + const noise = FractalNoise3D.generateAligned(main.stackAllocator, map.pos.wx, map.pos.wy, map.pos.wz, outerSize, CaveMapFragment.width*map.pos.voxelSize/outerSize + 1, CaveMapFragment.width*map.pos.voxelSize/outerSize + 1, CaveMapFragment.height*map.pos.voxelSize/outerSize + 1, worldSeed ^ 4329561871 ^ 112*@backingInt(mode), noiseScale); defer noise.deinit(main.stackAllocator); for (noise.mem, output.mem, biomeNoiseStrength.mem) |*val, sdfVal, noiseStrength| { diff --git a/src/server/terrain/sbb.zig b/src/server/terrain/sbb.zig index 61ac5a3882..a347c866fd 100644 --- a/src/server/terrain/sbb.zig +++ b/src/server/terrain/sbb.zig @@ -35,7 +35,7 @@ pub const BlueprintIndex = enum(u32) { return blueprintMap.get(_id); } pub fn get(self: BlueprintIndex) [4]BlueprintEntry { - return blueprintList.items[@intFromEnum(self)]; + return blueprintList.items[@backingInt(self)]; } }; @@ -46,7 +46,7 @@ pub const StructureIndex = enum(u32) { return structureMap.get(_id); } pub fn get(self: StructureIndex) *StructureBuildingBlock { - return &structureList.items[@intFromEnum(self)]; + return &structureList.items[@backingInt(self)]; } }; @@ -55,7 +55,7 @@ pub const LocalBlockIndex = enum(u16) { _, pub fn name(self: LocalBlockIndex) []const u8 { - return childBlockName.items[@intFromEnum(self)]; + return childBlockName.items[@backingInt(self)]; } pub fn fromName(_name: []const u8) ?LocalBlockIndex { return childBlockNameToLocalIndex.get(_name) orelse return null; @@ -81,7 +81,7 @@ const BlueprintEntry = struct { data: u16, pub inline fn direction(self: StructureBlock) Neighbor { - return @enumFromInt(self.data); + return @fromBackingInt(@intCast(self.data)); } pub inline fn pos(self: StructureBlock) Vec3i { @@ -175,7 +175,7 @@ pub const Rotation = union(RotationMode) { pub fn apply(self: Rotation, rotation: FixedRotation) Rotation { return .{.fixed = switch (self) { - .fixed => |fixed| @enumFromInt(@intFromEnum(rotation) +% @intFromEnum(fixed)), + .fixed => |fixed| @fromBackingInt(@backingInt(rotation) +% @backingInt(fixed)), .random, .inherit => rotation, }}; } @@ -187,7 +187,7 @@ pub const Rotation = union(RotationMode) { }; } fn sampleRandom(seed: *u64) Rotation { - return .{.fixed = @enumFromInt(main.random.nextInt(u2, seed))}; + return .{.fixed = @fromBackingInt(main.random.nextInt(u2, seed))}; } pub fn getChildRotation(self: Rotation, seed: *u64, child: Rotation, direction: Neighbor) Rotation { return switch (direction) { @@ -214,8 +214,8 @@ pub const Rotation = union(RotationMode) { } return error.UnknownString; }, - .int => |value| .{.fixed = @enumFromInt(@abs(@divTrunc(value, 90))%4)}, - .float => |value| .{.fixed = @enumFromInt(@abs(@as(u64, @trunc(value/90.0)))%4)}, + .int => |value| .{.fixed = @fromBackingInt(@intCast(@abs(@divTrunc(value, 90))%4))}, + .float => |value| .{.fixed = @fromBackingInt(@intCast(@abs(@as(u64, @trunc(value/90.0)))%4))}, .null => Rotation.random, else => return error.UnknownType, }; @@ -293,7 +293,7 @@ pub const StructureBuildingBlock = struct { // MARK: StructureBuildingBlock while (childrenDictIterator.next()) |entry| { if (LocalBlockIndex.fromName(entry.key_ptr.*)) |localIndex| { switch (entry.value_ptr.*) { - .string, .stringOwned => |_id| childrenToResolve.append(.{.structureId = _id, .structure = &self.children[@intFromEnum(localIndex)]}), + .string, .stringOwned => |_id| childrenToResolve.append(.{.structureId = _id, .structure = &self.children[@backingInt(localIndex)]}), .null => std.log.err("['{s}'] Child '{s}' ID can not be null. Leave child key undefined if it is not used by blueprints.", .{stringId, localIndex.name()}), else => |e| std.log.err("['{s}'->'{s}'] Value has to be a string ID of one of the structures, found {s}.", .{stringId, localIndex.name(), @tagName(e)}), } @@ -316,25 +316,25 @@ pub const StructureBuildingBlock = struct { // MARK: StructureBuildingBlock if (blueprints.items == null) continue; for (blueprints.items.?[0].childBlocks) |child| { - if (std.mem.containsAtLeastScalar(LocalBlockIndex, childBlocksInBlueprints.items, 1, child.index)) continue; + if (std.mem.containsAtLeastScalar(LocalBlockIndex, childBlocksInBlueprints.items, child.index, 1)) continue; childBlocksInBlueprints.append(main.stackAllocator, child.index); // Check that all child blocks present in any of the blueprints have corresponding configurations. - if (self.children[@intFromEnum(child.index)] != null) continue; + if (self.children[@backingInt(child.index)] != null) continue; std.log.err("['{s}'] Blueprint ({}) requires child block {s} but no configuration was specified for it.", .{self.id, blueprintIndex, child.id()}); } } // Check that all configured child blocks are used somewhere in one of the blueprints. for (self.children, 0..) |child, childBlockIndex| { if (child == null) continue; - if (std.mem.containsAtLeastScalar(LocalBlockIndex, childBlocksInBlueprints.items, 1, @enumFromInt(childBlockIndex))) continue; - std.log.err("['{s}'] None of the blueprints contains a child '{s}' but configuration for it was specified.", .{self.id, @as(LocalBlockIndex, @enumFromInt(childBlockIndex)).name()}); + if (std.mem.containsAtLeastScalar(LocalBlockIndex, childBlocksInBlueprints.items, @fromBackingInt(@intCast(childBlockIndex)), 1)) continue; + std.log.err("['{s}'] None of the blueprints contains a child '{s}' but configuration for it was specified.", .{self.id, @as(LocalBlockIndex, @fromBackingInt(@intCast(childBlockIndex))).name()}); } } pub fn getBlueprints(self: StructureBuildingBlock, seed: *u64) *?[4]BlueprintEntry { return &self.blueprints.sample(seed).items; } pub fn getChildStructure(self: StructureBuildingBlock, block: BlueprintEntry.StructureBlock) ?*const StructureBuildingBlock { - return self.children[@intFromEnum(block.index)]; + return self.children[@backingInt(block.index)]; } }; @@ -357,7 +357,7 @@ pub fn registerSBB(structures: *Assets.ZonHashMap) !void { }); const key = main.worldArena.dupe(u8, entry.key_ptr.*); - structureMap.put(main.worldArena.allocator, key, @enumFromInt(loadedCount)) catch unreachable; + structureMap.put(main.worldArena.allocator, key, @fromBackingInt(loadedCount)) catch unreachable; std.log.debug("Registered structure building block: '{s}'", .{entry.key_ptr.*}); loadedCount += 1; @@ -379,14 +379,14 @@ pub fn registerChildBlock(numericId: u16, stringId: []const u8) void { std.debug.assert(numericId != 0); const index: u16 = @intCast(childBlockNumericIdMap.count()); - childBlockNumericIdMap.put(main.worldArena.allocator, numericId, @enumFromInt(index)) catch unreachable; + childBlockNumericIdMap.put(main.worldArena.allocator, numericId, @fromBackingInt(index)) catch unreachable; // Take only color name from the ID. var iterator = std.mem.splitBackwardsScalar(u8, stringId, '/'); const colorName = iterator.first(); const colorNameDupe = main.worldArena.dupe(u8, colorName); childBlockName.append(main.worldArena, colorNameDupe); - childBlockNameToLocalIndex.put(main.worldArena.allocator, colorNameDupe, @enumFromInt(index)) catch unreachable; + childBlockNameToLocalIndex.put(main.worldArena.allocator, colorNameDupe, @fromBackingInt(index)) catch unreachable; } pub fn registerBlueprints(blueprints: *Assets.BytesHashMap) !void { @@ -420,7 +420,7 @@ pub fn registerBlueprints(blueprints: *Assets.BytesHashMap) !void { blueprintList.items[index][2] = BlueprintEntry.init(blueprint180, stringId) catch continue; blueprintList.items[index][3] = BlueprintEntry.init(blueprint270, stringId) catch continue; - blueprintMap.put(main.worldArena.allocator, main.worldArena.dupe(u8, stringId), @enumFromInt(index)) catch unreachable; + blueprintMap.put(main.worldArena.allocator, main.worldArena.dupe(u8, stringId), @fromBackingInt(index)) catch unreachable; std.log.debug("Registered blueprint: '{s}'", .{stringId}); } } diff --git a/src/server/terrain/sdf.zig b/src/server/terrain/sdf.zig index 8690d2d67e..053a859859 100644 --- a/src/server/terrain/sdf.zig +++ b/src/server/terrain/sdf.zig @@ -72,10 +72,10 @@ pub const SdfModel = struct { // MARK: SdfModel } const modelRegistry: std.StaticStringMap(VTable) = .initComptime(blk: { - const decls = @typeInfo(sdf_models).@"struct".decls; + const decls = @typeInfo(sdf_models).@"struct".decl_names; var generators: [decls.len]struct { []const u8, VTable } = undefined; for (0..decls.len) |i| { - const Generator = @field(sdf_models, decls[i].name); + const Generator = @field(sdf_models, decls[i]); generators[i] = .{Generator.id, .{ .initAndGetExtend = Generator.initAndGetExtend, .instantiate = main.meta.castFunctionSelfToAnyopaque(Generator.instantiate), diff --git a/src/server/terrain/simple_structures/FallenTree.zig b/src/server/terrain/simple_structures/FallenTree.zig index e3134ed9b6..55c6f13385 100644 --- a/src/server/terrain/simple_structures/FallenTree.zig +++ b/src/server/terrain/simple_structures/FallenTree.zig @@ -55,7 +55,7 @@ pub fn generateFallen(self: *FallenTree, x: i32, y: i32, z: i32, length: u32, ch var d: ?Neighbor = null; for (0..4) |_| { - const dir: Neighbor = @enumFromInt(main.random.nextIntBounded(u32, seed, 4) + 2); + const dir: Neighbor = @fromBackingInt(@intCast(main.random.nextIntBounded(u32, seed, 4) + 2)); const dx: i32 = dir.relX(); const dy: i32 = dir.relY(); diff --git a/src/server/terrain/simple_structures/SbbGen.zig b/src/server/terrain/simple_structures/SbbGen.zig index c0ac4ae788..160d76129f 100644 --- a/src/server/terrain/simple_structures/SbbGen.zig +++ b/src/server/terrain/simple_structures/SbbGen.zig @@ -23,7 +23,7 @@ placeMode: Blueprint.PasteMode, rotation: sbb.Rotation, pub fn getHash(self: SbbGen) u64 { - return std.hash.Wyhash.hash(@intFromEnum(self.placeMode), self.structureRef.id); + return std.hash.Wyhash.hash(@backingInt(self.placeMode), self.structureRef.id); } pub fn loadModel(parameters: ZonElement) ?*SbbGen { @@ -64,7 +64,7 @@ fn placeSbb(self: *SbbGen, structure: *const sbb.StructureBuildingBlock, placeme std.log.err("Could not align directions for structure '{s}' for directions '{s}'' and '{s}', error: {s}", .{structure.id, @tagName(origin.direction()), @tagName(placementDirection orelse origin.direction()), @errorName(err)}); return; }); - const rotated = &blueprints[@intFromEnum(blueprintRotation.fixed)]; + const rotated = &blueprints[@backingInt(blueprintRotation.fixed)]; const rotatedOrigin = rotated.originBlock.pos(); const pastePosition = placementPosition - rotatedOrigin - (placementDirection orelse origin.direction()).relPos(); @@ -84,7 +84,7 @@ fn alignDirections(input: Neighbor, desired: Neighbor) !sbb.Rotation.FixedRotati var current = in; for (0..4) |i| { if (current == out) { - alignTable[in.toInt()][out.toInt()] = @enumFromInt(i); + alignTable[in.toInt()][out.toInt()] = @fromBackingInt(@intCast(i)); break :blk; } current = current.rotateZ(); diff --git a/src/server/terrain/simple_structures/SimpleTreeModel.zig b/src/server/terrain/simple_structures/SimpleTreeModel.zig index f7c77e4617..209b449afb 100644 --- a/src/server/terrain/simple_structures/SimpleTreeModel.zig +++ b/src/server/terrain/simple_structures/SimpleTreeModel.zig @@ -88,7 +88,7 @@ pub fn initalOrientation(block: main.blocks.Block, orientation: Neighbor, mode: return .{.typ = block.typ, .data = orientation.reverse().bitMask()}; }, .direction => { - return .{.typ = block.typ, .data = @intFromEnum(orientation)}; + return .{.typ = block.typ, .data = @backingInt(orientation)}; }, .unknown => return block, } @@ -115,7 +115,7 @@ pub fn generateStem(self: *SimpleTreeModel, x: i32, y: i32, z: i32, height: i32, if (self.branched) { const chance = @sqrt(@as(f32, @floatFromInt(pz - z))/@as(f32, @floatFromInt(height*2))); if (main.random.nextFloat(seed) < chance) { - const dir: Neighbor = @enumFromInt(main.random.nextIntBounded(u32, seed, 4) + 2); + const dir: Neighbor = @fromBackingInt(@intCast(main.random.nextIntBounded(u32, seed, 4) + 2)); generateBranch(self, x, y, pz, dir, chunk); block = addNeighbor(block, dir, rotationModeType); } diff --git a/src/server/terrain/structures.zig b/src/server/terrain/structures.zig index 0a25a93f2d..637a32acd8 100644 --- a/src/server/terrain/structures.zig +++ b/src/server/terrain/structures.zig @@ -57,10 +57,10 @@ pub const SimpleStructureModel = struct { // MARK: SimpleStructureModel } const modelRegistry: std.StaticStringMap(VTable) = .initComptime(blk: { - const decls = @typeInfo(simple_structures).@"struct".decls; + const decls = @typeInfo(simple_structures).@"struct".decl_names; var generators: [decls.len]struct { []const u8, VTable } = undefined; for (0..decls.len) |i| { - const Generator = @field(simple_structures, decls[i].name); + const Generator = @field(simple_structures, decls[i]); generators[i] = .{Generator.id, .{ .loadModel = main.meta.castFunctionReturnToOptionalAnyopaque(Generator.loadModel), .generate = main.meta.castFunctionSelfToAnyopaque(Generator.generate), diff --git a/src/server/terrain/terrain.zig b/src/server/terrain/terrain.zig index a09cdb0b35..54a1c24761 100644 --- a/src/server/terrain/terrain.zig +++ b/src/server/terrain/terrain.zig @@ -42,10 +42,10 @@ pub const BlockGenerator = struct { defaultState: GeneratorState, const generatorRegistry: std.StaticStringMap(BlockGenerator) = .initComptime(blk: { - const decls = @typeInfo(chunk_generators).@"struct".decls; + const decls = @typeInfo(chunk_generators).@"struct".decl_names; var generators: [decls.len]struct { []const u8, BlockGenerator } = undefined; for (0..decls.len) |i| { - const Generator = @field(chunk_generators, decls[i].name); + const Generator = @field(chunk_generators, decls[i]); generators[i] = .{Generator.id, .{ .init = &Generator.init, .generate = &Generator.generate, diff --git a/src/settings.zig b/src/settings.zig index 1e8b5927c4..73d7348458 100644 --- a/src/settings.zig +++ b/src/settings.zig @@ -69,7 +69,7 @@ pub var updateRepeatDelay: std.Io.Duration = .fromMilliseconds(500); pub var controllerAxisDeadzone: f32 = 0.2; -const settingsFile = if (builtin.mode == .Debug) "debug_settings.zig.zon" else "settings.zig.zon"; +const settingsFile = if (builtin.mode == .debug) "debug_settings.zig.zon" else "settings.zig.zon"; pub fn init() void { const zon: ZonElement = main.files.cubyzDir().readToZon(main.stackAllocator, settingsFile) catch |err| blk: { @@ -80,29 +80,29 @@ pub fn init() void { }; defer zon.deinit(main.stackAllocator); - inline for (@typeInfo(@This()).@"struct".decls) |decl| runtimeContinueInsideOfComptimeBlock: { - 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. + inline for (@typeInfo(@This()).@"struct".decl_names) |decl| runtimeContinueInsideOfComptimeBlock: { + const is_const = @typeInfo(@TypeOf(&@field(@This(), decl))).pointer.attrs.@"const"; // Sadly there is no direct way to check if a declaration is const. if (!is_const) { - comptime var DeclType = @TypeOf(@field(@This(), decl.name)); + comptime var DeclType = @TypeOf(@field(@This(), decl)); if (@typeInfo(DeclType) == .optional) { DeclType = @typeInfo(DeclType).optional.child; } 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(@trunc((zon.get(f64, decl.name) orelse defaultMilli)*1.0e6)); + const defaultMilli = @as(f64, @floatFromInt(@field(@This(), decl).toNanoseconds()))/1.0e6; + @field(@This(), decl) = .fromNanoseconds(@trunc((zon.get(f64, decl) orelse defaultMilli)*1.0e6)); continue; } - @field(@This(), decl.name) = DeclType.fromZon(main.globalAllocator, zon.getChild(decl.name)) catch |err| { - std.log.err("Got error while loading setting {s}: {s}", .{decl.name, @errorName(err)}); + @field(@This(), decl) = DeclType.fromZon(main.globalAllocator, zon.getChild(decl)) catch |err| { + std.log.err("Got error while loading setting {s}: {s}", .{decl, @errorName(err)}); break :runtimeContinueInsideOfComptimeBlock; }; continue; } - @field(@This(), decl.name) = zon.get(DeclType, decl.name) orelse @field(@This(), decl.name); + @field(@This(), decl) = zon.get(DeclType, decl) orelse @field(@This(), decl); 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) = main.globalAllocator.dupe(@typeInfo(DeclType).pointer.child, @field(@This(), decl)); } else { @compileError("Not implemented yet."); } @@ -127,18 +127,18 @@ pub fn init() void { pub fn deinit() void { save(); - 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. + inline for (@typeInfo(@This()).@"struct".decl_names) |decl| { + const is_const = @typeInfo(@TypeOf(&@field(@This(), decl))).pointer.attrs.@"const"; // Sadly there is no direct way to check if a declaration is const. if (!is_const) { - const DeclType = @TypeOf(@field(@This(), decl.name)); + const DeclType = @TypeOf(@field(@This(), decl)); if (@typeInfo(DeclType) == .@"struct") { if (DeclType == std.Io.Duration) continue; - @field(@This(), decl.name).deinit(main.globalAllocator); + @field(@This(), decl).deinit(main.globalAllocator); continue; } if (@typeInfo(DeclType) == .pointer) { if (@typeInfo(DeclType).pointer.size == .slice) { - main.globalAllocator.free(@field(@This(), decl.name)); + main.globalAllocator.free(@field(@This(), decl)); } else { @compileError("Not implemented yet."); } @@ -151,26 +151,26 @@ pub fn save() void { var zonObject = ZonElement.initObject(main.stackAllocator); defer zonObject.deinit(main.stackAllocator); - inline for (@typeInfo(@This()).@"struct".decls) |decl| { - if (comptime std.mem.eql(u8, decl.name, "lastVersionString")) { - zonObject.put(decl.name, version.version); + inline for (@typeInfo(@This()).@"struct".decl_names) |decl| { + if (comptime std.mem.eql(u8, decl, "lastVersionString")) { + zonObject.put(decl, version.version); continue; } - 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. + const is_const = @typeInfo(@TypeOf(&@field(@This(), decl))).pointer.attrs.@"const"; // Sadly there is no direct way to check if a declaration is const. if (!is_const) { - const DeclType = @TypeOf(@field(@This(), decl.name)); + const DeclType = @TypeOf(@field(@This(), decl)); if (@typeInfo(DeclType) == .@"struct") { if (DeclType == std.Io.Duration) { - zonObject.put(decl.name, @as(f64, @floatFromInt(@field(@This(), decl.name).toNanoseconds()))/1.0e6); + zonObject.put(decl, @as(f64, @floatFromInt(@field(@This(), decl).toNanoseconds()))/1.0e6); continue; } - zonObject.put(decl.name, @field(@This(), decl.name).toZon(main.stackAllocator)); + zonObject.put(decl, @field(@This(), decl).toZon(main.stackAllocator)); continue; } if (DeclType == []const u8) { - zonObject.putOwnedString(decl.name, @field(@This(), decl.name)); + zonObject.putOwnedString(decl, @field(@This(), decl)); } else { - zonObject.put(decl.name, @field(@This(), decl.name)); + zonObject.put(decl, @field(@This(), decl)); } } } diff --git a/src/systems.zig b/src/systems.zig index 11ed0b4a22..a42612a43b 100644 --- a/src/systems.zig +++ b/src/systems.zig @@ -9,47 +9,47 @@ pub const systems = @import("systems/_list.zig"); pub const client = struct { pub fn init() void { - inline for (@typeInfo(systems).@"struct".decls) |decl| { - @field(systems, decl.name).client.init(); + inline for (@typeInfo(systems).@"struct".decl_names) |decl| { + @field(systems, decl).client.init(); } } pub fn deinit() void { - inline for (@typeInfo(systems).@"struct".decls) |decl| { - @field(systems, decl.name).client.deinit(); + inline for (@typeInfo(systems).@"struct".decl_names) |decl| { + @field(systems, decl).client.deinit(); } } pub fn clear() void { - inline for (@typeInfo(systems).@"struct".decls) |decl| { - @field(systems, decl.name).client.clear(); + inline for (@typeInfo(systems).@"struct".decl_names) |decl| { + @field(systems, decl).client.clear(); } } pub fn render(ambientLight: Vec3f, playerPos: Vec3d, deltaTime: f64) void { main.client.entity_manager.update(); - inline for (@typeInfo(systems).@"struct".decls) |decl| { - @field(systems, decl.name).client.render(ambientLight, playerPos, deltaTime); + inline for (@typeInfo(systems).@"struct".decl_names) |decl| { + @field(systems, decl).client.render(ambientLight, playerPos, deltaTime); } } pub fn renderHud(ambientLight: Vec3f, playerPos: Vec3d) void { - inline for (@typeInfo(systems).@"struct".decls) |decl| { - @field(systems, decl.name).client.renderHud(ambientLight, playerPos); + inline for (@typeInfo(systems).@"struct".decl_names) |decl| { + @field(systems, decl).client.renderHud(ambientLight, playerPos); } } }; pub const server = struct { pub fn init() void { - inline for (@typeInfo(systems).@"struct".decls) |decl| { - @field(systems, decl.name).server.init(); + inline for (@typeInfo(systems).@"struct".decl_names) |decl| { + @field(systems, decl).server.init(); } } pub fn deinit() void { - inline for (@typeInfo(systems).@"struct".decls) |decl| { - @field(systems, decl.name).server.deinit(); + inline for (@typeInfo(systems).@"struct".decl_names) |decl| { + @field(systems, decl).server.deinit(); } } pub fn update() void { - inline for (@typeInfo(systems).@"struct".decls) |decl| { - @field(systems, decl.name).server.update(); + inline for (@typeInfo(systems).@"struct".decl_names) |decl| { + @field(systems, decl).server.update(); } } }; diff --git a/src/tag.zig b/src/tag.zig index 5474f581af..44570debb2 100644 --- a/src/tag.zig +++ b/src/tag.zig @@ -31,7 +31,7 @@ pub const Tag = enum(u32) { pub fn find(tag: []const u8) Tag { if (tagIds.get(tag)) |res| return res; - const result: Tag = @enumFromInt(tagList.items.len); + const result: Tag = @fromBackingInt(@intCast(tagList.items.len)); const dupedTag = main.worldArena.dupe(u8, tag); tagList.append(main.worldArena, dupedTag); tagIds.put(main.worldArena.allocator, dupedTag, result) catch unreachable; @@ -50,6 +50,6 @@ pub const Tag = enum(u32) { } pub fn getName(tag: Tag) []const u8 { - return tagList.items[@intFromEnum(tag)]; + return tagList.items[@backingInt(tag)]; } }; diff --git a/src/utils.zig b/src/utils.zig index aec1a0c44d..3f4bdf1a44 100644 --- a/src/utils.zig +++ b/src/utils.zig @@ -11,10 +11,6 @@ pub const file_monitor = @import("utils/file_monitor.zig"); const virtual_mem = @import("utils/virtual_mem.zig"); pub const VirtualList = virtual_mem.VirtualList; -pub const Condition = @import("utils/Condition.zig"); -pub const Futex = @import("utils/Futex.zig"); -pub const Semaphore = @import("utils/Semaphore.zig"); - pub const Compression = struct { // MARK: Compression 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; @@ -789,7 +785,7 @@ pub const ThreadPool = struct { // MARK: ThreadPool pub fn add(self: *Performance, task: TaskType, time: i64) void { self.mutex.lock(); defer self.mutex.unlock(); - const i = @intFromEnum(task); + const i = @backingInt(task); self.tasks[i] += 1; self.utime[i] += time; } @@ -815,9 +811,9 @@ pub const ThreadPool = struct { // MARK: ThreadPool currentTasks: []Atomic(?*const VTable), loadList: ConcurrentMaxHeap(Task), playerJobQueue: ConcurrentQueue(main.server.PlayerIndex), - taskCountSemaphore: main.utils.Semaphore = .{}, - stopSemaphore: main.utils.Semaphore = .{}, - startSemaphore: main.utils.Semaphore = .{}, + taskCountSemaphore: std.Io.Semaphore = .{}, + stopSemaphore: std.Io.Semaphore = .{}, + startSemaphore: std.Io.Semaphore = .{}, allocator: NeverFailingAllocator, running: Atomic(bool) = .init(true), paused: Atomic(bool) = .init(false), @@ -871,14 +867,14 @@ pub const ThreadPool = struct { // MARK: ThreadPool pub fn @"continue"(self: *ThreadPool) void { std.debug.assert(self.paused.swap(false, .monotonic)); for (0..self.threads.len) |_| { - self.startSemaphore.post(); + self.startSemaphore.post(main.io); } } pub fn pause(self: *ThreadPool) void { std.debug.assert(!self.paused.swap(true, .monotonic)); for (0..self.threads.len) |_| { - self.stopSemaphore.wait(); + self.stopSemaphore.waitUncancelable(main.io); } } @@ -892,7 +888,7 @@ pub const ThreadPool = struct { // MARK: ThreadPool if (task.vtable == vtable) { task.vtable.clean(task.self); self.loadList.removeIndex(i); - self.taskCountSemaphore.timedWait(.zero) catch {}; + self.taskCountSemaphore.waitTimeout(main.io, .none) catch {}; } else { i += 1; } @@ -907,7 +903,7 @@ pub const ThreadPool = struct { // MARK: ThreadPool pub fn unschedulePlayers(self: *ThreadPool) void { while (self.playerJobQueue.popFront()) |_| { - self.taskCountSemaphore.timedWait(.zero) catch {}; + self.taskCountSemaphore.waitTimeout(main.io, .none) catch {}; _ = self.trueQueueSize.fetchSub(1, .monotonic); } } @@ -927,7 +923,7 @@ pub const ThreadPool = struct { // MARK: ThreadPool .empty => {}, .hasMoreTasks => { self.playerJobQueue.pushBack(player); - self.taskCountSemaphore.post(); + self.taskCountSemaphore.post(main.io); _ = self.trueQueueSize.fetchAdd(1, .monotonic); }, } @@ -945,15 +941,15 @@ pub const ThreadPool = struct { // MARK: ThreadPool main.heap.GarbageCollection.syncPoint(); if (self.paused.load(.monotonic)) { - self.stopSemaphore.post(); + self.stopSemaphore.post(main.io); while (true) { main.heap.GarbageCollection.syncPoint(); - self.startSemaphore.timedWait(.fromMilliseconds(10)) catch continue; + self.startSemaphore.waitTimeout(main.io, .{.duration = .{.raw = .fromMilliseconds(10), .clock = .awake}}) catch continue; break; } } - self.taskCountSemaphore.timedWait(.fromMilliseconds(10)) catch continue :outer; + self.taskCountSemaphore.waitTimeout(main.io, .{.duration = .{.raw = .fromMilliseconds(10), .clock = .awake}}) catch continue :outer; { const task = self.getNextTask() orelse continue :outer; @@ -978,7 +974,7 @@ pub const ThreadPool = struct { // MARK: ThreadPool var temporaryTaskList: main.List(Task) = .empty; defer temporaryTaskList.deinit(main.stackAllocator); while (self.loadList.extractAny()) |task| { - self.taskCountSemaphore.timedWait(.zero) catch {}; + self.taskCountSemaphore.waitTimeout(main.io, .none) catch {}; if (!task.vtable.isStillNeeded(task.self)) { task.vtable.clean(task.self); _ = self.trueQueueSize.fetchSub(1, .monotonic); @@ -990,7 +986,7 @@ pub const ThreadPool = struct { // MARK: ThreadPool } self.loadList.addMany(temporaryTaskList.items); for (0..temporaryTaskList.items.len) |_| { - self.taskCountSemaphore.post(); + self.taskCountSemaphore.post(main.io); } const endTime = main.timestamp(); self.performance.add(.taskPriorityUpdate, @intCast(@divTrunc(startTime.durationTo(endTime).toNanoseconds(), 1000))); @@ -1002,13 +998,13 @@ pub const ThreadPool = struct { // MARK: ThreadPool .vtable = vtable, .self = task, }); - self.taskCountSemaphore.post(); + self.taskCountSemaphore.post(main.io); _ = self.trueQueueSize.fetchAdd(1, .monotonic); } pub fn addPlayer(self: *ThreadPool, player: *main.server.User) void { self.playerJobQueue.pushBack(player.playerIndex); - self.taskCountSemaphore.post(); + self.taskCountSemaphore.post(main.io); _ = self.trueQueueSize.fetchAdd(1, .monotonic); } @@ -1646,30 +1642,22 @@ pub const TimeDifference = struct { // MARK: TimeDifference /// A wrapper over Zig's mutex to avoid having to pass the io everywhere pub const Mutex = struct { // MARK: Mutex - super: if (builtin.os.tag == .windows) @import("utils/Mutex.zig") else std.Io.Mutex = .init, + super: std.Io.Mutex = .init, pub fn tryLock(self: *Mutex) bool { return self.super.tryLock(); } pub fn lock(self: *Mutex) void { - if (builtin.os.tag == .windows) { - self.super.lock(); - } else { - self.super.lockUncancelable(main.io); - } + self.super.lockUncancelable(main.io); } pub fn unlock(self: *Mutex) void { - if (builtin.os.tag == .windows) { - self.super.unlock(); - } else { - self.super.unlock(main.io); - } + self.super.unlock(main.io); } pub fn assertLocked(self: *const main.utils.Mutex) void { - if (builtin.mode == .Debug) { + if (builtin.mode == .debug) { std.debug.assert(!@constCast(self).tryLock()); } } @@ -1823,7 +1811,7 @@ pub const BinaryWriter = struct { // MARK: BinaryWriter } pub fn writeEnum(self: *BinaryWriter, T: type, value: T) void { - self.writeInt(@typeInfo(T).@"enum".tag_type, @intFromEnum(value)); + self.writeInt(@typeInfo(T).@"enum".tag_type, @backingInt(value)); } pub fn writeBool(self: *BinaryWriter, value: bool) void { @@ -2077,7 +2065,7 @@ pub fn DenseId(comptime IdType: type) type { } pub fn SparseSet(comptime T: type, comptime IdType: type) type { // MARK: SparseSet - std.debug.assert(@intFromEnum(IdType.noValue) == std.math.maxInt(@typeInfo(IdType).@"enum".tag_type)); + std.debug.assert(@backingInt(IdType.noValue) == std.math.maxInt(@typeInfo(IdType).@"enum".tag_type)); return struct { const Self = @This(); @@ -2100,21 +2088,21 @@ pub fn SparseSet(comptime T: type, comptime IdType: type) type { // MARK: Sparse pub fn contains(self: *Self, id: IdType) bool { std.debug.assert(id != .noValue); - return @intFromEnum(id) < self.sparseToDenseIndex.items.len and self.sparseToDenseIndex.items[@intFromEnum(id)] != .noValue; + return @backingInt(id) < self.sparseToDenseIndex.items.len and self.sparseToDenseIndex.items[@backingInt(id)] != .noValue; } pub fn add(self: *Self, allocator: NeverFailingAllocator, id: IdType) *T { std.debug.assert(id != .noValue); - const denseId: IdType = @enumFromInt(self.dense.items.len); + const denseId: IdType = @fromBackingInt(@intCast(self.dense.items.len)); - if (@intFromEnum(id) >= self.sparseToDenseIndex.items.len) { - self.sparseToDenseIndex.appendNTimes(allocator, .noValue, @intFromEnum(id) - self.sparseToDenseIndex.items.len + 1); + if (@backingInt(id) >= self.sparseToDenseIndex.items.len) { + self.sparseToDenseIndex.appendNTimes(allocator, .noValue, @backingInt(id) - self.sparseToDenseIndex.items.len + 1); } - std.debug.assert(self.sparseToDenseIndex.items[@intFromEnum(id)] == .noValue); + std.debug.assert(self.sparseToDenseIndex.items[@backingInt(id)] == .noValue); - self.sparseToDenseIndex.items[@intFromEnum(id)] = denseId; + self.sparseToDenseIndex.items[@backingInt(id)] = denseId; self.denseToSparseIndex.append(allocator, id); return self.dense.addOne(allocator); } @@ -2127,14 +2115,14 @@ pub fn SparseSet(comptime T: type, comptime IdType: type) type { // MARK: Sparse pub fn fetchRemove(self: *Self, id: IdType) !T { if (!self.contains(id)) return error.ElementNotFound; - const denseId = @intFromEnum(self.sparseToDenseIndex.items[@intFromEnum(id)]); - self.sparseToDenseIndex.items[@intFromEnum(id)] = .noValue; + const denseId = @backingInt(self.sparseToDenseIndex.items[@backingInt(id)]); + self.sparseToDenseIndex.items[@backingInt(id)] = .noValue; const result = self.dense.swapRemove(denseId); _ = self.denseToSparseIndex.swapRemove(denseId); if (denseId != self.dense.items.len) { - self.sparseToDenseIndex.items[@intFromEnum(self.denseToSparseIndex.items[denseId])] = @enumFromInt(denseId); + self.sparseToDenseIndex.items[@backingInt(self.denseToSparseIndex.items[denseId])] = @fromBackingInt(@intCast(denseId)); } return result; } @@ -2145,10 +2133,10 @@ pub fn SparseSet(comptime T: type, comptime IdType: type) type { // MARK: Sparse pub fn get(self: *Self, id: IdType) ?*T { std.debug.assert(id != .noValue); - if (@intFromEnum(id) >= self.sparseToDenseIndex.items.len) return null; - const index = self.sparseToDenseIndex.items[@intFromEnum(id)]; + if (@backingInt(id) >= self.sparseToDenseIndex.items.len) return null; + const index = self.sparseToDenseIndex.items[@backingInt(id)]; if (index == .noValue) return null; - return &self.dense.items[@intFromEnum(index)]; + return &self.dense.items[@backingInt(index)]; } }; } @@ -2158,7 +2146,7 @@ test "SparseSet/set at zero" { var set: SparseSet(u32, IdType) = .{}; defer set.deinit(main.heap.testingAllocator); - const index: IdType = @enumFromInt(0); + const index: IdType = @fromBackingInt(0); set.set(main.heap.testingAllocator, index, 5); try std.testing.expectEqual(set.get(index).?.*, 5); @@ -2169,7 +2157,7 @@ test "SparseSet/set at 100" { var set: SparseSet(u32, IdType) = .{}; defer set.deinit(main.heap.testingAllocator); - const index: IdType = @enumFromInt(100); + const index: IdType = @fromBackingInt(100); set.set(main.heap.testingAllocator, index, 5); try std.testing.expectEqual(set.get(index).?.*, 5); @@ -2182,8 +2170,8 @@ test "SparseSet/remove first" { const expectSecond: u32 = 100; - const firstId: IdType = @enumFromInt(0); - const secondId: IdType = @enumFromInt(1); + const firstId: IdType = @fromBackingInt(0); + const secondId: IdType = @fromBackingInt(1); set.set(main.heap.testingAllocator, firstId, 5); set.set(main.heap.testingAllocator, secondId, expectSecond); @@ -2198,9 +2186,9 @@ test "SparseSet/remove last" { var set: SparseSet(u32, IdType) = .{}; defer set.deinit(main.heap.testingAllocator); - set.set(main.heap.testingAllocator, @enumFromInt(0), 5); + set.set(main.heap.testingAllocator, @fromBackingInt(0), 5); - try set.remove(@enumFromInt(0)); + try set.remove(@fromBackingInt(0)); } test "SparseSet/remove entry that doesn't exist" { @@ -2208,7 +2196,7 @@ test "SparseSet/remove entry that doesn't exist" { var set: SparseSet(u32, IdType) = .{}; defer set.deinit(main.heap.testingAllocator); - try std.testing.expectError(error.ElementNotFound, set.remove(@enumFromInt(0))); + try std.testing.expectError(error.ElementNotFound, set.remove(@fromBackingInt(0))); } test "SparseSet/remove entry twice" { @@ -2216,10 +2204,10 @@ test "SparseSet/remove entry twice" { var set: SparseSet(u32, IdType) = .{}; defer set.deinit(main.heap.testingAllocator); - set.set(main.heap.testingAllocator, @enumFromInt(0), 5); + set.set(main.heap.testingAllocator, @fromBackingInt(0), 5); - try set.remove(@enumFromInt(0)); - try std.testing.expectError(error.ElementNotFound, set.remove(@enumFromInt(0))); + try set.remove(@fromBackingInt(0)); + try std.testing.expectError(error.ElementNotFound, set.remove(@fromBackingInt(0))); } test "SparseSet/reusing" { @@ -2230,8 +2218,8 @@ test "SparseSet/reusing" { const expectSecond = 100; const expectNew = 10; - const firstId: IdType = @enumFromInt(0); - const secondId: IdType = @enumFromInt(1); + const firstId: IdType = @fromBackingInt(0); + const secondId: IdType = @fromBackingInt(1); set.set(main.heap.testingAllocator, firstId, 5); set.set(main.heap.testingAllocator, secondId, expectSecond); diff --git a/src/utils/Condition.zig b/src/utils/Condition.zig deleted file mode 100644 index 21c3956c88..0000000000 --- a/src/utils/Condition.zig +++ /dev/null @@ -1,296 +0,0 @@ -// TODO: Remove after https://codeberg.org/ziglang/zig/issues/31912 was merged - -// zig fmt: off - -//! Condition variables are used with a Mutex to efficiently wait for an arbitrary condition to occur. -//! It does this by atomically unlocking the mutex, blocking the thread until notified, and finally re-locking the mutex. -//! Condition can be statically initialized and is at most `@sizeOf(u64)` large. -//! -//! Example: -//! ``` -//! var m = Mutex{}; -//! var c = Condition{}; -//! var predicate = false; -//! -//! fn consumer() void { -//! m.lock(); -//! defer m.unlock(); -//! -//! while (!predicate) { -//! c.wait(&m); -//! } -//! } -//! -//! fn producer() void { -//! { -//! m.lock(); -//! defer m.unlock(); -//! predicate = true; -//! } -//! c.signal(); -//! } -//! -//! const thread = try std.Thread.spawn(.{}, producer, .{}); -//! consumer(); -//! thread.join(); -//! ``` -//! -//! Note that condition variables can only reliably unblock threads that are sequenced before them using the same Mutex. -//! This means that the following is allowed to deadlock: -//! ``` -//! thread-1: mutex.lock() -//! thread-1: condition.wait(&mutex) -//! -//! thread-2: // mutex.lock() (without this, the following signal may not see the waiting thread-1) -//! thread-2: // mutex.unlock() (this is optional for correctness once locked above, as signal can be called while holding the mutex) -//! thread-2: condition.signal() -//! ``` - -const std = @import("std"); -const main = @import("main"); -const builtin = @import("builtin"); -const Mutex = main.utils.Mutex; - -const os = std.os; -const assert = std.debug.assert; -const testing = std.testing; -const Futex = main.utils.Futex; - -const Condition = @This(); - -impl: Impl = .{}, - -/// Atomically releases the Mutex, blocks the caller thread, then re-acquires the Mutex on return. -/// "Atomically" here refers to accesses done on the Condition after acquiring the Mutex. -/// -/// The Mutex must be locked by the caller's thread when this function is called. -/// A Mutex can have multiple Conditions waiting with it concurrently, but not the opposite. -/// It is undefined behavior for multiple threads to wait ith different mutexes using the same Condition concurrently. -/// Once threads have finished waiting with one Mutex, the Condition can be used to wait with another Mutex. -/// -/// A blocking call to wait() is unblocked from one of the following conditions: -/// - a spurious ("at random") wake up occurs -/// - a future call to `signal()` or `broadcast()` which has acquired the Mutex and is sequenced after this `wait()`. -/// -/// Given wait() can be interrupted spuriously, the blocking condition should be checked continuously -/// irrespective of any notifications from `signal()` or `broadcast()`. -pub fn wait(self: *Condition, mutex: *Mutex) void { - self.impl.wait(mutex, null) catch |err| switch (err) { - error.Timeout => unreachable, // no timeout provided so we shouldn't have timed-out - }; -} - -/// Atomically releases the Mutex, blocks the caller thread, then re-acquires the Mutex on return. -/// "Atomically" here refers to accesses done on the Condition after acquiring the Mutex. -/// -/// The Mutex must be locked by the caller's thread when this function is called. -/// A Mutex can have multiple Conditions waiting with it concurrently, but not the opposite. -/// It is undefined behavior for multiple threads to wait ith different mutexes using the same Condition concurrently. -/// Once threads have finished waiting with one Mutex, the Condition can be used to wait with another Mutex. -/// -/// A blocking call to `timedWait()` is unblocked from one of the following conditions: -/// - a spurious ("at random") wake occurs -/// - the caller was blocked for around `timeout_ns` nanoseconds, in which `error.Timeout` is returned. -/// - a future call to `signal()` or `broadcast()` which has acquired the Mutex and is sequenced after this `timedWait()`. -/// -/// Given `timedWait()` can be interrupted spuriously, the blocking condition should be checked continuously -/// irrespective of any notifications from `signal()` or `broadcast()`. -pub fn timedWait(self: *Condition, mutex: *Mutex, timeout: std.Io.Duration) error{Timeout}!void { - return self.impl.wait(mutex, @intCast(timeout.nanoseconds)); -} - -/// Unblocks at least one thread blocked in a call to `wait()` or `timedWait()` with a given Mutex. -/// The blocked thread must be sequenced before this call with respect to acquiring the same Mutex in order to be observable for unblocking. -/// `signal()` can be called with or without the relevant Mutex being acquired and have no "effect" if there's no observable blocked threads. -pub fn signal(self: *Condition) void { - self.impl.wake(.one); -} - -/// Unblocks all threads currently blocked in a call to `wait()` or `timedWait()` with a given Mutex. -/// The blocked threads must be sequenced before this call with respect to acquiring the same Mutex in order to be observable for unblocking. -/// `broadcast()` can be called with or without the relevant Mutex being acquired and have no "effect" if there's no observable blocked threads. -pub fn broadcast(self: *Condition) void { - self.impl.wake(.all); -} - -const Impl = if (builtin.single_threaded) SingleThreadedImpl else if (builtin.os.tag == .windows) WindowsImpl else FutexImpl; - -const Notify = enum { - one, // wake up only one thread - all, // wake up all threads -}; - -const SingleThreadedImpl = struct { - fn wait(self: *Impl, mutex: *Mutex, timeout: ?u64) error{Timeout}!void { - _ = self; - _ = mutex; - // There are no other threads to wake us up. - // So if we wait without a timeout we would never wake up. - assert(timeout != null); // Deadlock detected. - return error.Timeout; - } - - fn wake(self: *Impl, comptime notify: Notify) void { - // There are no other threads to wake up. - _ = self; - _ = notify; - } -}; - -const WindowsImpl = struct { - condition: c.CONDITION_VARIABLE = .{}, - - const c = @cImport({ - @cInclude("windows.h"); - }); - - pub extern "ntdll" fn RtlWakeConditionVariable( - ConditionVariable: *c.CONDITION_VARIABLE, - ) callconv(.winapi) void; - pub extern "ntdll" fn RtlWakeAllConditionVariable( - ConditionVariable: *c.CONDITION_VARIABLE, - ) callconv(.winapi) void; - - fn wait(self: *Impl, mutex: *Mutex, timeout: ?u64) error{Timeout}!void { - var timeout_overflowed = false; - var timeout_ms: os.windows.DWORD = c.INFINITE; - - if (timeout) |timeout_ns| { - // Round the nanoseconds to the nearest millisecond, - // then saturating cast it to windows DWORD for use in kernel32 call. - const ms = (timeout_ns +| (std.time.ns_per_ms / 2)) / std.time.ns_per_ms; - timeout_ms = std.math.cast(os.windows.DWORD, ms) orelse std.math.maxInt(os.windows.DWORD); - - // Track if the timeout overflowed into INFINITE and make sure not to wait forever. - if (timeout_ms == c.INFINITE) { - timeout_overflowed = true; - timeout_ms -= 1; - } - } - - const rc = c.SleepConditionVariableSRW( - &self.condition, - @ptrCast(&mutex.super.impl.srwlock), - timeout_ms, - 0, // the srwlock was assumed to acquired in exclusive mode not shared - ); - - // Return error.Timeout if we know the timeout elapsed correctly. - if (rc == c.FALSE) { - assert(os.windows.GetLastError() == .TIMEOUT); - if (!timeout_overflowed) return error.Timeout; - } - } - - fn wake(self: *Impl, comptime notify: Notify) void { - switch (notify) { - .one => RtlWakeConditionVariable(&self.condition), - .all => RtlWakeAllConditionVariable(&self.condition), - } - } -}; - -const FutexImpl = struct { // MARK: FutexImpl - state: std.atomic.Value(u32) = std.atomic.Value(u32).init(0), - epoch: std.atomic.Value(u32) = std.atomic.Value(u32).init(0), - - const one_waiter = 1; - const waiter_mask = 0xffff; - - const one_signal = 1 << 16; - const signal_mask = 0xffff << 16; - - fn wait(self: *Impl, mutex: *Mutex, timeout: ?u64) error{Timeout}!void { - // Observe the epoch, then check the state again to see if we should wake up. - // The epoch must be observed before we check the state or we could potentially miss a wake() and deadlock: - // - // - T1: s = LOAD(&state) - // - T2: UPDATE(&s, signal) - // - T2: UPDATE(&epoch, 1) + FUTEX_WAKE(&epoch) - // - T1: e = LOAD(&epoch) (was reordered after the state load) - // - T1: s & signals == 0 -> FUTEX_WAIT(&epoch, e) (missed the state update + the epoch change) - // - // Acquire barrier to ensure the epoch load happens before the state load. - var epoch = self.epoch.load(.acquire); - var state = self.state.fetchAdd(one_waiter, .monotonic); - assert(state & waiter_mask != waiter_mask); - state += one_waiter; - - mutex.unlock(); - defer mutex.lock(); - - var futex_deadline = Futex.Deadline.init(timeout); - - while (true) { - futex_deadline.wait(&self.epoch, epoch) catch |err| switch (err) { - // On timeout, we must decrement the waiter we added above. - error.Timeout => { - while (true) { - // If there's a signal when we're timing out, consume it and report being woken up instead. - // Acquire barrier ensures code before the wake() which added the signal happens before we decrement it and return. - while (state & signal_mask != 0) { - const new_state = state - one_waiter - one_signal; - state = self.state.cmpxchgWeak(state, new_state, .acquire, .monotonic) orelse return; - } - - // Remove the waiter we added and officially return timed out. - const new_state = state - one_waiter; - state = self.state.cmpxchgWeak(state, new_state, .monotonic, .monotonic) orelse return err; - } - }, - }; - - epoch = self.epoch.load(.acquire); - state = self.state.load(.monotonic); - - // Try to wake up by consuming a signal and decremented the waiter we added previously. - // Acquire barrier ensures code before the wake() which added the signal happens before we decrement it and return. - while (state & signal_mask != 0) { - const new_state = state - one_waiter - one_signal; - state = self.state.cmpxchgWeak(state, new_state, .acquire, .monotonic) orelse return; - } - } - } - - fn wake(self: *Impl, comptime notify: Notify) void { - var state = self.state.load(.monotonic); - while (true) { - const waiters = (state & waiter_mask) / one_waiter; - const signals = (state & signal_mask) / one_signal; - - // Reserves which waiters to wake up by incrementing the signals count. - // Therefore, the signals count is always less than or equal to the waiters count. - // We don't need to Futex.wake if there's nothing to wake up or if other wake() threads have reserved to wake up the current waiters. - const wakeable = waiters - signals; - if (wakeable == 0) { - return; - } - - const to_wake = switch (notify) { - .one => 1, - .all => wakeable, - }; - - // Reserve the amount of waiters to wake by incrementing the signals count. - // Release barrier ensures code before the wake() happens before the signal it posted and consumed by the wait() threads. - const new_state = state + (one_signal * to_wake); - state = self.state.cmpxchgWeak(state, new_state, .release, .monotonic) orelse { - // Wake up the waiting threads we reserved above by changing the epoch value. - // NOTE: a waiting thread could miss a wake up if *exactly* ((1<<32)-1) wake()s happen between it observing the epoch and sleeping on it. - // This is very unlikely due to how many precise amount of Futex.wake() calls that would be between the waiting thread's potential preemption. - // - // Release barrier ensures the signal being added to the state happens before the epoch is changed. - // If not, the waiting thread could potentially deadlock from missing both the state and epoch change: - // - // - T2: UPDATE(&epoch, 1) (reordered before the state change) - // - T1: e = LOAD(&epoch) - // - T1: s = LOAD(&state) - // - T2: UPDATE(&state, signal) + FUTEX_WAKE(&epoch) - // - T1: s & signals == 0 -> FUTEX_WAIT(&epoch, e) (missed both epoch change and state change) - _ = self.epoch.fetchAdd(1, .release); - Futex.wake(&self.epoch, to_wake); - return; - }; - } - } -}; diff --git a/src/utils/Futex.zig b/src/utils/Futex.zig deleted file mode 100644 index 20f348a765..0000000000 --- a/src/utils/Futex.zig +++ /dev/null @@ -1,861 +0,0 @@ -// TODO: Remove after https://codeberg.org/ziglang/zig/issues/31912 was merged - -// zig fmt: off - -//! A mechanism used to block (`wait`) and unblock (`wake`) threads using a -//! 32bit memory address as hints. -//! -//! Blocking a thread is acknowledged only if the 32bit memory address is equal -//! to a given value. This check helps avoid block/unblock deadlocks which -//! occur if a `wake()` happens before a `wait()`. -//! -//! Using Futex, other Thread synchronization primitives can be built which -//! efficiently wait for cross-thread events or signals. - -const std = @import("std"); -const main = @import("main"); -const builtin = @import("builtin"); -const windows = std.os.windows; -const linux = std.os.linux; -const c = std.c; - -const assert = std.debug.assert; -const testing = std.testing; -const atomic = std.atomic; - -const Futex = @This(); - -/// Checks if `ptr` still contains the value `expect` and, if so, blocks the caller until either: -/// - The value at `ptr` is no longer equal to `expect`. -/// - The caller is unblocked by a matching `wake()`. -/// - The caller is unblocked spuriously ("at random"). -/// -/// The checking of `ptr` and `expect`, along with blocking the caller, is done atomically -/// and totally ordered (sequentially consistent) with respect to other wait()/wake() calls on the same `ptr`. -pub fn wait(ptr: *const atomic.Value(u32), expect: u32) void { - @branchHint(.cold); - - Impl.wait(ptr, expect, null) catch |err| switch (err) { - error.Timeout => unreachable, // null timeout meant to wait forever - }; -} - -/// Checks if `ptr` still contains the value `expect` and, if so, blocks the caller until either: -/// - The value at `ptr` is no longer equal to `expect`. -/// - The caller is unblocked by a matching `wake()`. -/// - The caller is unblocked spuriously ("at random"). -/// - The caller blocks for longer than the given timeout. In which case, `error.Timeout` is returned. -/// -/// The checking of `ptr` and `expect`, along with blocking the caller, is done atomically -/// and totally ordered (sequentially consistent) with respect to other wait()/wake() calls on the same `ptr`. -pub fn timedWait(ptr: *const atomic.Value(u32), expect: u32, timeout_ns: u64) error{Timeout}!void { - @branchHint(.cold); - - // Avoid calling into the OS for no-op timeouts. - if (timeout_ns == 0) { - if (ptr.load(.seq_cst) != expect) return; - return error.Timeout; - } - - return Impl.wait(ptr, expect, timeout_ns); -} - -/// Unblocks at most `max_waiters` callers blocked in a `wait()` call on `ptr`. -pub fn wake(ptr: *const atomic.Value(u32), max_waiters: u32) void { - @branchHint(.cold); - - // Avoid calling into the OS if there's nothing to wake up. - if (max_waiters == 0) { - return; - } - - Impl.wake(ptr, max_waiters); -} - -const Impl = if (builtin.single_threaded) SingleThreadedImpl else if (builtin.os.tag == .windows) WindowsImpl else if (builtin.os.tag.isDarwin()) DarwinImpl else if (builtin.os.tag == .linux) LinuxImpl else if (builtin.os.tag == .freebsd) FreebsdImpl else if (builtin.os.tag == .openbsd) OpenbsdImpl else if (builtin.target.cpu.arch.isWasm()) WasmImpl else if (std.Thread.use_pthreads) PosixImpl else UnsupportedImpl; - -/// We can't do @compileError() in the `Impl` switch statement above as its eagerly evaluated. -/// So instead, we @compileError() on the methods themselves for platforms which don't support futex. -const UnsupportedImpl = struct { - fn wait(ptr: *const atomic.Value(u32), expect: u32, timeout: ?u64) error{Timeout}!void { - return unsupported(.{ptr, expect, timeout}); - } - - fn wake(ptr: *const atomic.Value(u32), max_waiters: u32) void { - return unsupported(.{ptr, max_waiters}); - } - - fn unsupported(unused: anytype) noreturn { - _ = unused; - @compileError("Unsupported operating system " ++ @tagName(builtin.target.os.tag)); - } -}; - -const SingleThreadedImpl = struct { - fn wait(ptr: *const atomic.Value(u32), expect: u32, timeout: ?u64) error{Timeout}!void { - if (ptr.raw != expect) { - return; - } - - // There are no threads to wake us up. - // So if we wait without a timeout we would never wake up. - const delay = timeout orelse { - unreachable; // deadlock detected - }; - - _ = delay; - return error.Timeout; - } - - fn wake(ptr: *const atomic.Value(u32), max_waiters: u32) void { - // There are no other threads to possibly wake up - _ = ptr; - _ = max_waiters; - } -}; - -// We use WaitOnAddress through NtDll instead of API-MS-Win-Core-Synch-l1-2-0.dll -// as it's generally already a linked target and is autoloaded into all processes anyway. -const WindowsImpl = struct { - fn wait(ptr: *const atomic.Value(u32), expect: u32, timeout: ?u64) error{Timeout}!void { - var timeout_value: windows.LARGE_INTEGER = undefined; - var timeout_ptr: ?*const windows.LARGE_INTEGER = null; - - // NTDLL functions work with time in units of 100 nanoseconds. - // Positive values are absolute deadlines while negative values are relative durations. - if (timeout) |delay| { - timeout_value = @as(windows.LARGE_INTEGER, @intCast(delay/100)); - timeout_value = -timeout_value; - timeout_ptr = &timeout_value; - } - - const rc = windows.ntdll.RtlWaitOnAddress( - ptr, - &expect, - @sizeOf(@TypeOf(expect)), - timeout_ptr, - ); - - switch (rc) { - .SUCCESS => {}, - .TIMEOUT => { - assert(timeout != null); - return error.Timeout; - }, - else => unreachable, - } - } - - fn wake(ptr: *const atomic.Value(u32), max_waiters: u32) void { - const address: ?*const anyopaque = ptr; - assert(max_waiters != 0); - - switch (max_waiters) { - 1 => windows.ntdll.RtlWakeAddressSingle(address), - else => windows.ntdll.RtlWakeAddressAll(address), - } - } -}; - -const DarwinImpl = struct { - fn wait(ptr: *const atomic.Value(u32), expect: u32, timeout: ?u64) error{Timeout}!void { - // Darwin XNU 7195.50.7.100.1 introduced __ulock_wait2 and migrated code paths (notably pthread_cond_t) towards it: - // https://github.com/apple/darwin-xnu/commit/d4061fb0260b3ed486147341b72468f836ed6c8f#diff-08f993cc40af475663274687b7c326cc6c3031e0db3ac8de7b24624610616be6 - // - // This XNU version appears to correspond to 11.0.1: - // https://kernelshaman.blogspot.com/2021/01/building-xnu-for-macos-big-sur-1101.html - // - // ulock_wait() uses 32-bit micro-second timeouts where 0 = INFINITE or no-timeout - // ulock_wait2() uses 64-bit nano-second timeouts (with the same convention) - const supports_ulock_wait2 = builtin.target.os.version_range.semver.min.major >= 11; - - var timeout_ns: u64 = 0; - if (timeout) |delay| { - assert(delay != 0); // handled by timedWait() - timeout_ns = delay; - } - - // If we're using `__ulock_wait` and `timeout` is too big to fit inside a `u32` count of - // micro-seconds (around 70min), we'll request a shorter timeout. This is fine (users - // should handle spurious wakeups), but we need to remember that we did so, so that - // we don't return `Timeout` incorrectly. If that happens, we set this variable to - // true so that we we know to ignore the ETIMEDOUT result. - var timeout_overflowed = false; - - const addr: *const anyopaque = ptr; - const flags: c.UL = .{ - .op = .COMPARE_AND_WAIT, - .NO_ERRNO = true, - }; - const status = blk: { - if (supports_ulock_wait2) { - break :blk c.__ulock_wait2(flags, addr, expect, timeout_ns, 0); - } - - const timeout_us = std.math.cast(u32, timeout_ns/std.time.ns_per_us) orelse overflow: { - timeout_overflowed = true; - break :overflow std.math.maxInt(u32); - }; - - break :blk c.__ulock_wait(flags, addr, expect, timeout_us); - }; - - if (status >= 0) return; - switch (@as(c.E, @enumFromInt(-status))) { - // Wait was interrupted by the OS or other spurious signalling. - .INTR => {}, - // Address of the futex was paged out. This is unlikely, but possible in theory, and - // pthread/libdispatch on darwin bother to handle it. In this case we'll return - // without waiting, but the caller should retry anyway. - .FAULT => {}, - // Only report Timeout if we didn't have to cap the timeout - .TIMEDOUT => { - assert(timeout != null); - if (!timeout_overflowed) return error.Timeout; - }, - else => unreachable, - } - } - - fn wake(ptr: *const atomic.Value(u32), max_waiters: u32) void { - const flags: c.UL = .{ - .op = .COMPARE_AND_WAIT, - .NO_ERRNO = true, - .WAKE_ALL = max_waiters > 1, - }; - - while (true) { - const addr: *const anyopaque = ptr; - const status = c.__ulock_wake(flags, addr, 0); - - if (status >= 0) return; - switch (@as(c.E, @enumFromInt(-status))) { - .INTR => continue, // spurious wake() - .FAULT => unreachable, // __ulock_wake doesn't generate EFAULT according to darwin pthread_cond_t - .NOENT => return, // nothing was woken up - .ALREADY => unreachable, // only for UL.Op.WAKE_THREAD - else => unreachable, - } - } - } -}; - -// https://man7.org/linux/man-pages/man2/futex.2.html -const LinuxImpl = struct { - fn wait(ptr: *const atomic.Value(u32), expect: u32, timeout: ?u64) error{Timeout}!void { - var ts: linux.timespec = undefined; - if (timeout) |timeout_ns| { - ts.sec = @as(@TypeOf(ts.sec), @intCast(timeout_ns/std.time.ns_per_s)); - ts.nsec = @as(@TypeOf(ts.nsec), @intCast(timeout_ns%std.time.ns_per_s)); - } - - const rc = linux.futex_4arg( - &ptr.raw, - .{.cmd = .WAIT, .private = true}, - expect, - if (timeout != null) &ts else null, - ); - - switch (linux.errno(rc)) { - .SUCCESS => {}, // notified by `wake()` - .INTR => {}, // spurious wakeup - .AGAIN => {}, // ptr.* != expect - .TIMEDOUT => { - assert(timeout != null); - return error.Timeout; - }, - .INVAL => {}, // possibly timeout overflow - .FAULT => unreachable, // ptr was invalid - else => unreachable, - } - } - - fn wake(ptr: *const atomic.Value(u32), max_waiters: u32) void { - const rc = linux.futex_3arg( - &ptr.raw, - .{.cmd = .WAKE, .private = true}, - @min(max_waiters, std.math.maxInt(i32)), - ); - - switch (linux.errno(rc)) { - .SUCCESS => {}, // successful wake up - .INVAL => {}, // invalid futex_wait() on ptr done elsewhere - .FAULT => {}, // pointer became invalid while doing the wake - else => unreachable, - } - } -}; - -// https://www.freebsd.org/cgi/man.cgi?query=_umtx_op&sektion=2&n=1 -const FreebsdImpl = struct { - fn wait(ptr: *const atomic.Value(u32), expect: u32, timeout: ?u64) error{Timeout}!void { - var tm_size: usize = 0; - var tm: c._umtx_time = undefined; - var tm_ptr: ?*const c._umtx_time = null; - - if (timeout) |timeout_ns| { - tm_ptr = &tm; - tm_size = @sizeOf(@TypeOf(tm)); - - tm.flags = 0; // use relative time not UMTX_ABSTIME - tm.clockid = .MONOTONIC; - tm.timeout.sec = @as(@TypeOf(tm.timeout.sec), @intCast(timeout_ns/std.time.ns_per_s)); - tm.timeout.nsec = @as(@TypeOf(tm.timeout.nsec), @intCast(timeout_ns%std.time.ns_per_s)); - } - - const rc = c._umtx_op( - @intFromPtr(&ptr.raw), - @intFromEnum(c.UMTX_OP.WAIT_UINT_PRIVATE), - @as(c_ulong, expect), - tm_size, - @intFromPtr(tm_ptr), - ); - - switch (std.posix.errno(rc)) { - .SUCCESS => {}, - .FAULT => unreachable, // one of the args points to invalid memory - .INVAL => unreachable, // arguments should be correct - .TIMEDOUT => { - assert(timeout != null); - return error.Timeout; - }, - .INTR => {}, // spurious wake - else => unreachable, - } - } - - fn wake(ptr: *const atomic.Value(u32), max_waiters: u32) void { - const rc = c._umtx_op( - @intFromPtr(&ptr.raw), - @intFromEnum(c.UMTX_OP.WAKE_PRIVATE), - @as(c_ulong, max_waiters), - 0, // there is no timeout struct - 0, // there is no timeout struct pointer - ); - - switch (std.posix.errno(rc)) { - .SUCCESS => {}, - .FAULT => {}, // it's ok if the ptr doesn't point to valid memory - .INVAL => unreachable, // arguments should be correct - else => unreachable, - } - } -}; - -// https://man.openbsd.org/futex.2 -const OpenbsdImpl = struct { - fn wait(ptr: *const atomic.Value(u32), expect: u32, timeout: ?u64) error{Timeout}!void { - var ts: c.timespec = undefined; - if (timeout) |timeout_ns| { - ts.sec = @as(@TypeOf(ts.sec), @intCast(timeout_ns/std.time.ns_per_s)); - ts.nsec = @as(@TypeOf(ts.nsec), @intCast(timeout_ns%std.time.ns_per_s)); - } - - const rc = c.futex( - @as(*const volatile u32, @ptrCast(&ptr.raw)), - c.FUTEX.WAIT | c.FUTEX.PRIVATE_FLAG, - @as(c_int, @bitCast(expect)), - if (timeout != null) &ts else null, - null, // FUTEX.WAIT takes no requeue address - ); - - switch (std.posix.errno(rc)) { - .SUCCESS => {}, // woken up by wake - .NOSYS => unreachable, // the futex operation shouldn't be invalid - .FAULT => unreachable, // ptr was invalid - .AGAIN => {}, // ptr != expect - .INVAL => unreachable, // invalid timeout - .TIMEDOUT => { - assert(timeout != null); - return error.Timeout; - }, - .INTR => {}, // spurious wake from signal - .CANCELED => {}, // spurious wake from signal with SA_RESTART - else => unreachable, - } - } - - fn wake(ptr: *const atomic.Value(u32), max_waiters: u32) void { - const rc = c.futex( - @as(*const volatile u32, @ptrCast(&ptr.raw)), - c.FUTEX.WAKE | c.FUTEX.PRIVATE_FLAG, - std.math.cast(c_int, max_waiters) orelse std.math.maxInt(c_int), - null, // FUTEX.WAKE takes no timeout ptr - null, // FUTEX.WAKE takes no requeue address - ); - - // returns number of threads woken up. - assert(rc >= 0); - } -}; - -const WasmImpl = struct { - fn wait(ptr: *const atomic.Value(u32), expect: u32, timeout: ?u64) error{Timeout}!void { - if (!comptime builtin.cpu.has(.wasm, .atomics)) @compileError("WASI target missing cpu feature 'atomics'"); - - const to: i64 = if (timeout) |to| @intCast(to) else -1; - const result = asm volatile ( - \\local.get %[ptr] - \\local.get %[expected] - \\local.get %[timeout] - \\memory.atomic.wait32 0 - \\local.set %[ret] - : [ret] "=r" (-> u32), - : [ptr] "r" (&ptr.raw), - [expected] "r" (@as(i32, @bitCast(expect))), - [timeout] "r" (to), - ); - switch (result) { - 0 => {}, // ok - 1 => {}, // expected =! loaded - 2 => return error.Timeout, - else => unreachable, - } - } - - fn wake(ptr: *const atomic.Value(u32), max_waiters: u32) void { - if (!comptime builtin.cpu.has(.wasm, .atomics)) @compileError("WASI target missing cpu feature 'atomics'"); - - assert(max_waiters != 0); - const woken_count = asm volatile ( - \\local.get %[ptr] - \\local.get %[waiters] - \\memory.atomic.notify 0 - \\local.set %[ret] - : [ret] "=r" (-> u32), - : [ptr] "r" (&ptr.raw), - [waiters] "r" (max_waiters), - ); - _ = woken_count; // can be 0 when linker flag 'shared-memory' is not enabled - } -}; - -/// Modified version of linux's futex and Go's sema to implement userspace wait queues with pthread: -/// https://code.woboq.org/linux/linux/kernel/futex.c.html -/// https://go.dev/src/runtime/sema.go -const PosixImpl = struct { // MARK: PosixImpl - const Event = struct { - cond: c.pthread_cond_t, - mutex: c.pthread_mutex_t, - state: enum { empty, waiting, notified }, - - fn init(self: *Event) void { - // Use static init instead of pthread_cond/mutex_init() since this is generally faster. - self.cond = .{}; - self.mutex = .{}; - self.state = .empty; - } - - fn deinit(self: *Event) void { - // Some platforms reportedly give EINVAL for statically initialized pthread types. - const rc = c.pthread_cond_destroy(&self.cond); - assert(rc == .SUCCESS or rc == .INVAL); - - const rm = c.pthread_mutex_destroy(&self.mutex); - assert(rm == .SUCCESS or rm == .INVAL); - - self.* = undefined; - } - - fn wait(self: *Event, timeout: ?u64) error{Timeout}!void { - assert(c.pthread_mutex_lock(&self.mutex) == .SUCCESS); - defer assert(c.pthread_mutex_unlock(&self.mutex) == .SUCCESS); - - // Early return if the event was already set. - if (self.state == .notified) { - return; - } - - // Compute the absolute timeout if one was specified. - // POSIX requires that REALTIME is used by default for the pthread timedwait functions. - // This can be changed with pthread_condattr_setclock, but it's an extension and may not be available everywhere. - var ts: c.timespec = undefined; - if (timeout) |timeout_ns| { - ts = std.posix.clock_gettime(c.CLOCK.REALTIME) catch unreachable; - ts.sec +|= @as(@TypeOf(ts.sec), @intCast(timeout_ns/std.time.ns_per_s)); - ts.nsec += @as(@TypeOf(ts.nsec), @intCast(timeout_ns%std.time.ns_per_s)); - - if (ts.nsec >= std.time.ns_per_s) { - ts.sec +|= 1; - ts.nsec -= std.time.ns_per_s; - } - } - - // Start waiting on the event - there can be only one thread waiting. - assert(self.state == .empty); - self.state = .waiting; - - while (true) { - // Block using either pthread_cond_wait or pthread_cond_timewait if there's an absolute timeout. - const rc = blk: { - if (timeout == null) break :blk c.pthread_cond_wait(&self.cond, &self.mutex); - break :blk c.pthread_cond_timedwait(&self.cond, &self.mutex, &ts); - }; - - // After waking up, check if the event was set. - if (self.state == .notified) { - return; - } - - assert(self.state == .waiting); - switch (rc) { - .SUCCESS => {}, - .TIMEDOUT => { - // If timed out, reset the event to avoid the set() thread doing an unnecessary signal(). - self.state = .empty; - return error.Timeout; - }, - .INVAL => unreachable, // cond, mutex, and potentially ts should all be valid - .PERM => unreachable, // mutex is locked when cond_*wait() functions are called - else => unreachable, - } - } - } - - fn set(self: *Event) void { - assert(c.pthread_mutex_lock(&self.mutex) == .SUCCESS); - defer assert(c.pthread_mutex_unlock(&self.mutex) == .SUCCESS); - - // Make sure that multiple calls to set() were not done on the same Event. - const old_state = self.state; - assert(old_state != .notified); - - // Mark the event as set and wake up the waiting thread if there was one. - // This must be done while the mutex as the wait() thread could deallocate - // the condition variable once it observes the new state, potentially causing a UAF if done unlocked. - self.state = .notified; - if (old_state == .waiting) { - assert(c.pthread_cond_signal(&self.cond) == .SUCCESS); - } - } - }; - - const Treap = std.Treap(usize, std.math.order); - const Waiter = struct { - node: Treap.Node, - prev: ?*Waiter, - next: ?*Waiter, - tail: ?*Waiter, - is_queued: bool, - event: Event, - }; - - // An unordered set of Waiters - const WaitList = struct { - top: ?*Waiter = null, - len: usize = 0, - - fn push(self: *WaitList, waiter: *Waiter) void { - waiter.next = self.top; - self.top = waiter; - self.len += 1; - } - - fn pop(self: *WaitList) ?*Waiter { - const waiter = self.top orelse return null; - self.top = waiter.next; - self.len -= 1; - return waiter; - } - }; - - const WaitQueue = struct { // MARK: WaitQueue - fn insert(treap: *Treap, address: usize, waiter: *Waiter) void { - // prepare the waiter to be inserted. - waiter.next = null; - waiter.is_queued = true; - - // Find the wait queue entry associated with the address. - // If there isn't a wait queue on the address, this waiter creates the queue. - var entry = treap.getEntryFor(address); - const entry_node = entry.node orelse { - waiter.prev = null; - waiter.tail = waiter; - entry.set(&waiter.node); - return; - }; - - // There's a wait queue on the address; get the queue head and tail. - const head: *Waiter = @fieldParentPtr("node", entry_node); - const tail = head.tail.?; - - // Push the waiter to the tail by replacing it and linking to the previous tail. - head.tail = waiter; - tail.next = waiter; - waiter.prev = tail; - } - - fn remove(treap: *Treap, address: usize, max_waiters: usize) WaitList { - // Find the wait queue associated with this address and get the head/tail if any. - var entry = treap.getEntryFor(address); - var queue_head: ?*Waiter = if (entry.node) |node| @fieldParentPtr("node", node) else null; - const queue_tail = if (queue_head) |head| head.tail else null; - - // Once we're done updating the head, fix it's tail pointer and update the treap's queue head as well. - defer entry.set(blk: { - const new_head = queue_head orelse break :blk null; - new_head.tail = queue_tail; - break :blk &new_head.node; - }); - - var removed = WaitList{}; - while (removed.len < max_waiters) { - // dequeue and collect waiters from their wait queue. - const waiter = queue_head orelse break; - queue_head = waiter.next; - removed.push(waiter); - - // When dequeueing, we must mark is_queued as false. - // This ensures that a waiter which calls tryRemove() returns false. - assert(waiter.is_queued); - waiter.is_queued = false; - } - - return removed; - } - - fn tryRemove(treap: *Treap, address: usize, waiter: *Waiter) bool { - if (!waiter.is_queued) { - return false; - } - - queue_remove: { - // Find the wait queue associated with the address. - var entry = blk: { - // A waiter without a previous link means it's the queue head that's in the treap so we can avoid lookup. - if (waiter.prev == null) { - assert(waiter.node.key == address); - break :blk treap.getEntryForExisting(&waiter.node); - } - break :blk treap.getEntryFor(address); - }; - - // The queue head and tail must exist if we're removing a queued waiter. - const head: *Waiter = @fieldParentPtr("node", entry.node.?); - const tail = head.tail.?; - - // A waiter with a previous link is never the head of the queue. - if (waiter.prev) |prev| { - assert(waiter != head); - prev.next = waiter.next; - - // A waiter with both a previous and next link is in the middle. - // We only need to update the surrounding waiter's links to remove it. - if (waiter.next) |next| { - assert(waiter != tail); - next.prev = waiter.prev; - break :queue_remove; - } - - // A waiter with a previous but no next link means it's the tail of the queue. - // In that case, we need to update the head's tail reference. - assert(waiter == tail); - head.tail = waiter.prev; - break :queue_remove; - } - - // A waiter with no previous link means it's the queue head of queue. - // We must replace (or remove) the head waiter reference in the treap. - assert(waiter == head); - entry.set(blk: { - const new_head = waiter.next orelse break :blk null; - new_head.tail = head.tail; - break :blk &new_head.node; - }); - } - - // Mark the waiter as successfully removed. - waiter.is_queued = false; - return true; - } - }; - - const Bucket = struct { - mutex: c.pthread_mutex_t align(atomic.cache_line) = .{}, - pending: atomic.Value(usize) = atomic.Value(usize).init(0), - treap: Treap = .{}, - - // Global array of buckets that addresses map to. - // Bucket array size is pretty much arbitrary here, but it must be a power of two for fibonacci hashing. - var buckets = [_]Bucket{.{}} ** @bitSizeOf(usize); - - // https://github.com/Amanieu/parking_lot/blob/1cf12744d097233316afa6c8b7d37389e4211756/core/src/parking_lot.rs#L343-L353 - fn from(address: usize) *Bucket { - // The upper `@bitSizeOf(usize)` bits of the fibonacci golden ratio. - // Hashing this via (h * k) >> (64 - b) where k=golden-ration and b=bitsize-of-array - // evenly lays out h=hash values over the bit range even when the hash has poor entropy (identity-hash for pointers). - const max_multiplier_bits = @bitSizeOf(usize); - const fibonacci_multiplier = 0x9E3779B97F4A7C15 >> (64 - max_multiplier_bits); - - const max_bucket_bits = @ctz(buckets.len); - comptime assert(std.math.isPowerOfTwo(buckets.len)); - - const index = (address*%fibonacci_multiplier) >> (max_multiplier_bits - max_bucket_bits); - return &buckets[index]; - } - }; - - const Address = struct { - fn from(ptr: *const atomic.Value(u32)) usize { - // Get the alignment of the pointer. - const alignment = @alignOf(atomic.Value(u32)); - comptime assert(std.math.isPowerOfTwo(alignment)); - - // Make sure the pointer is aligned, - // then cut off the zero bits from the alignment to get the unique address. - const addr = @intFromPtr(ptr); - assert(addr & (alignment - 1) == 0); - return addr >> @ctz(@as(usize, alignment)); - } - }; - - fn wait(ptr: *const atomic.Value(u32), expect: u32, timeout: ?u64) error{Timeout}!void { - const address = Address.from(ptr); - const bucket = Bucket.from(address); - - // Announce that there's a waiter in the bucket before checking the ptr/expect condition. - // If the announcement is reordered after the ptr check, the waiter could deadlock: - // - // - T1: checks ptr == expect which is true - // - T2: updates ptr to != expect - // - T2: does Futex.wake(), sees no pending waiters, exits - // - T1: bumps pending waiters (was reordered after the ptr == expect check) - // - T1: goes to sleep and misses both the ptr change and T2's wake up - // - // acquire barrier to ensure the announcement happens before the ptr check below. - var pending = bucket.pending.fetchAdd(1, .acquire); - assert(pending < std.math.maxInt(usize)); - - // If the wait gets canceled, remove the pending count we previously added. - // This is done outside the mutex lock to keep the critical section short in case of contention. - var canceled = false; - defer if (canceled) { - pending = bucket.pending.fetchSub(1, .monotonic); - assert(pending > 0); - }; - - var waiter: Waiter = undefined; - { - assert(c.pthread_mutex_lock(&bucket.mutex) == .SUCCESS); - defer assert(c.pthread_mutex_unlock(&bucket.mutex) == .SUCCESS); - - canceled = ptr.load(.monotonic) != expect; - if (canceled) { - return; - } - - waiter.event.init(); - WaitQueue.insert(&bucket.treap, address, &waiter); - } - - defer { - assert(!waiter.is_queued); - waiter.event.deinit(); - } - - waiter.event.wait(timeout) catch { - // If we fail to cancel after a timeout, it means a wake() thread dequeued us and will wake us up. - // We must wait until the event is set as that's a signal that the wake() thread won't access the waiter memory anymore. - // If we return early without waiting, the waiter on the stack would be invalidated and the wake() thread risks a UAF. - defer if (!canceled) waiter.event.wait(null) catch unreachable; - - assert(c.pthread_mutex_lock(&bucket.mutex) == .SUCCESS); - defer assert(c.pthread_mutex_unlock(&bucket.mutex) == .SUCCESS); - - canceled = WaitQueue.tryRemove(&bucket.treap, address, &waiter); - if (canceled) { - return error.Timeout; - } - }; - } - - fn wake(ptr: *const atomic.Value(u32), max_waiters: u32) void { - const address = Address.from(ptr); - const bucket = Bucket.from(address); - - // Quick check if there's even anything to wake up. - // The change to the ptr's value must happen before we check for pending waiters. - // If not, the wake() thread could miss a sleeping waiter and have it deadlock: - // - // - T2: p = has pending waiters (reordered before the ptr update) - // - T1: bump pending waiters - // - T1: if ptr == expected: sleep() - // - T2: update ptr != expected - // - T2: p is false from earlier so doesn't wake (T1 missed ptr update and T2 missed T1 sleeping) - // - // What we really want here is a Release load, but that doesn't exist under the C11 memory model. - // We could instead do `bucket.pending.fetchAdd(0, Release) == 0` which achieves effectively the same thing, - // LLVM lowers the fetchAdd(0, .release) into an mfence+load which avoids gaining ownership of the cache-line. - if (bucket.pending.fetchAdd(0, .release) == 0) { - return; - } - - // Keep a list of all the waiters notified and wake then up outside the mutex critical section. - var notified = WaitList{}; - defer if (notified.len > 0) { - const pending = bucket.pending.fetchSub(notified.len, .monotonic); - assert(pending >= notified.len); - - while (notified.pop()) |waiter| { - assert(!waiter.is_queued); - waiter.event.set(); - } - }; - - assert(c.pthread_mutex_lock(&bucket.mutex) == .SUCCESS); - defer assert(c.pthread_mutex_unlock(&bucket.mutex) == .SUCCESS); - - // Another pending check again to avoid the WaitQueue lookup if not necessary. - if (bucket.pending.load(.monotonic) > 0) { - notified = WaitQueue.remove(&bucket.treap, address, max_waiters); - } - } -}; - -/// Deadline is used to wait efficiently for a pointer's value to change using Futex and a fixed timeout. -/// -/// Futex's timedWait() api uses a relative duration which suffers from over-waiting -/// when used in a loop which is often required due to the possibility of spurious wakeups. -/// -/// Deadline instead converts the relative timeout to an absolute one so that multiple calls -/// to Futex timedWait() can block for and report more accurate error.Timeouts. -pub const Deadline = struct { - timeout: ?u64, - started: std.Io.Timestamp, - - /// Create the deadline to expire after the given amount of time in nanoseconds passes. - /// Pass in `null` to have the deadline call `Futex.wait()` and never expire. - pub fn init(expires_in_ns: ?u64) Deadline { - var deadline: Deadline = undefined; - deadline.timeout = expires_in_ns; - - // std.time.Timer is required to be supported for somewhat accurate reportings of error.Timeout. - if (deadline.timeout != null) { - deadline.started = main.timestamp(); - } - - return deadline; - } - - /// Wait until either: - /// - the `ptr`'s value changes from `expect`. - /// - `Futex.wake()` is called on the `ptr`. - /// - A spurious wake occurs. - /// - The deadline expires; In which case `error.Timeout` is returned. - pub fn wait(self: *Deadline, ptr: *const atomic.Value(u32), expect: u32) error{Timeout}!void { - @branchHint(.cold); - - // Check if we actually have a timeout to wait until. - // If not just wait "forever". - const timeout_ns = self.timeout orelse { - return Futex.wait(ptr, expect); - }; - - // Get how much time has passed since we started waiting - // then subtract that from the init() timeout to get how much longer to wait. - // Use overflow to detect when we've been waiting longer than the init() timeout. - const elapsed_ns: u64 = @intCast(self.started.durationTo(main.timestamp()).nanoseconds); - const until_timeout_ns = std.math.sub(u64, timeout_ns, elapsed_ns) catch 0; - return Futex.timedWait(ptr, expect, until_timeout_ns); - } -}; diff --git a/src/utils/Mutex.zig b/src/utils/Mutex.zig deleted file mode 100644 index 73523b215e..0000000000 --- a/src/utils/Mutex.zig +++ /dev/null @@ -1,62 +0,0 @@ -// TODO: Remove after https://codeberg.org/ziglang/zig/issues/31912 was merged - -// zig fmt: off - -//! Mutex is a synchronization primitive which enforces atomic access to a -//! shared region of code known as the "critical section". -//! -//! It does this by blocking ensuring only one thread is in the critical -//! section at any given point in time by blocking the others. -//! -//! Mutex can be statically initialized and is at most `@sizeOf(u64)` large. -//! Use `lock()` or `tryLock()` to enter the critical section and `unlock()` to leave it. - -const std = @import("std"); -const builtin = @import("builtin"); -const Mutex = @This(); - -impl: Impl = .{}, - -pub const init: Mutex = .{}; - -/// Tries to acquire the mutex without blocking the caller's thread. -/// Returns `false` if the calling thread would have to block to acquire it. -/// Otherwise, returns `true` and the caller should `unlock()` the Mutex to release it. -pub fn tryLock(self: *Mutex) bool { - return self.impl.tryLock(); -} - -/// Acquires the mutex, blocking the caller's thread until it can. -/// It is undefined behavior if the mutex is already held by the caller's thread. -/// Once acquired, call `unlock()` on the Mutex to release it. -pub fn lock(self: *Mutex) void { - self.impl.lock(); -} - -/// Releases the mutex which was previously acquired with `lock()` or `tryLock()`. -/// It is undefined behavior if the mutex is unlocked from a different thread that it was locked from. -pub fn unlock(self: *Mutex) void { - self.impl.unlock(); -} - -const Impl = WindowsImpl; - -/// SRWLOCK on windows is almost always faster than Futex solution. -/// It also implements an efficient Condition with requeue support for us. -const WindowsImpl = struct { - srwlock: windows.SRWLOCK = .{}, - - fn tryLock(self: *@This()) bool { - return windows.ntdll.RtlTryAcquireSRWLockExclusive(&self.srwlock) != .FALSE; - } - - fn lock(self: *@This()) void { - windows.ntdll.RtlAcquireSRWLockExclusive(&self.srwlock); - } - - fn unlock(self: *@This()) void { - windows.ntdll.RtlReleaseSRWLockExclusive(&self.srwlock); - } - - const windows = std.os.windows; -}; diff --git a/src/utils/Semaphore.zig b/src/utils/Semaphore.zig deleted file mode 100644 index 634365d4e1..0000000000 --- a/src/utils/Semaphore.zig +++ /dev/null @@ -1,80 +0,0 @@ -// TODO: Remove after https://codeberg.org/ziglang/zig/issues/31912 was merged - -// zig fmt: off - -//! A semaphore is an unsigned integer that blocks the kernel thread if -//! the number would become negative. -//! This API supports static initialization and does not require deinitialization. -//! -//! Example: -//! ``` -//! var s = Semaphore{}; -//! -//! fn consumer() void { -//! s.wait(); -//! } -//! -//! fn producer() void { -//! s.post(); -//! } -//! -//! const thread = try std.Thread.spawn(.{}, producer, .{}); -//! consumer(); -//! thread.join(); -//! ``` - -const std = @import("std"); -const main = @import("main"); -const Mutex = main.utils.Mutex; -const Condition = main.utils.Condition; - -const Semaphore = @This(); - -mutex: Mutex = .{}, -cond: Condition = .{}, -/// It is OK to initialize this field to any value. -permits: usize = 0, - -pub fn wait(sem: *Semaphore) void { - sem.mutex.lock(); - defer sem.mutex.unlock(); - - while (sem.permits == 0) { - sem.cond.wait(&sem.mutex); - } - - sem.permits -= 1; - if (sem.permits > 0) { - sem.cond.signal(); - } -} - -pub fn timedWait(sem: *Semaphore, timeout: std.Io.Duration) error{Timeout}!void { - const start = main.timestamp(); - - sem.mutex.lock(); - defer sem.mutex.unlock(); - - while (sem.permits == 0) { - const elapsed = start.durationTo(main.timestamp()); - if (elapsed.nanoseconds > timeout.nanoseconds) { - return error.Timeout; - } - - const local_timeout_ns = timeout.nanoseconds - elapsed.nanoseconds; - try sem.cond.timedWait(&sem.mutex, .fromNanoseconds(local_timeout_ns)); - } - - sem.permits -= 1; - if (sem.permits > 0) { - sem.cond.signal(); - } -} - -pub fn post(sem: *Semaphore) void { - sem.mutex.lock(); - defer sem.mutex.unlock(); - - sem.permits += 1; - sem.cond.signal(); -} diff --git a/src/utils/heap.zig b/src/utils/heap.zig index dbc24aad27..038b8f9e76 100644 --- a/src/utils/heap.zig +++ b/src/utils/heap.zig @@ -110,7 +110,7 @@ pub const StackAllocator = struct { // MARK: StackAllocator fn alloc(ctx: *anyopaque, len: usize, alignment: std.mem.Alignment, ret_addr: usize) ?[*]u8 { const self: *StackAllocator = @ptrCast(@alignCast(ctx)); - const start = std.mem.alignForward(usize, self.index, @as(usize, 1) << @intCast(@intFromEnum(alignment))); + const start = std.mem.alignForward(usize, self.index, @as(usize, 1) << @intCast(@backingInt(alignment))); const end = getTrueAllocationEnd(start, len); if (end >= self.buffer.len) return self.backingAllocator.rawAlloc(len, alignment, ret_addr); const trailer = self.getTrailerBefore(end); @@ -450,10 +450,7 @@ pub const NeverFailingAllocator = struct { // MARK: NeverFailingAllocator /// do the realloc more efficiently than the caller /// * `resize` which returns `false` when the `Allocator` implementation cannot /// change the size without relocating the allocation. - pub fn realloc(self: NeverFailingAllocator, old_mem: anytype, new_n: usize) t: { - const Slice = @typeInfo(@TypeOf(old_mem)).pointer; - break :t []align(Slice.alignment orelse @alignOf(Slice.child)) Slice.child; - } { + pub fn realloc(self: NeverFailingAllocator, old_mem: anytype, new_n: usize) std.meta.Slice(std.meta.AbsorbSentinel(@TypeOf(old_mem))) { return self.allocator.realloc(old_mem, new_n) catch unreachable; } @@ -462,10 +459,7 @@ pub const NeverFailingAllocator = struct { // MARK: NeverFailingAllocator old_mem: anytype, new_n: usize, return_address: usize, - ) t: { - const Slice = @typeInfo(@TypeOf(old_mem)).pointer; - break :t []align(Slice.alignment) Slice.child; - } { + ) std.meta.Slice(std.meta.AbsorbSentinel(@TypeOf(old_mem))) { return self.allocator.reallocAdvanced(old_mem, new_n, return_address) catch unreachable; } @@ -565,7 +559,7 @@ pub const NeverFailingArenaAllocator = struct { // MARK: NeverFailingArena const node = self.arena.state.buffer_list.first orelse return; const allocBuf = @as([*]u8, @ptrCast(node))[0..node.data]; const dataSize = std.mem.alignForward(usize, @sizeOf(std.SinglyLinkedList(usize).Node) + self.arena.state.end_index, @alignOf(std.SinglyLinkedList(usize).Node)); - if (self.arena.childAllocator.rawResize(allocBuf, @enumFromInt(std.math.log2(@alignOf(std.SinglyLinkedList(usize).Node))), dataSize, @returnAddress())) { + if (self.arena.childAllocator.rawResize(allocBuf, @fromBackingInt(std.math.log2(@alignOf(std.SinglyLinkedList(usize).Node))), dataSize, @returnAddress())) { node.data = dataSize; } } @@ -856,7 +850,7 @@ pub fn PowerOfTwoPoolAllocator(minSize: comptime_int, maxSize: comptime_int, max } fn alloc(ctx: *anyopaque, len: usize, _alignment: std.mem.Alignment, _: usize) ?[*]u8 { - std.debug.assert(@as(usize, 1) << @intFromEnum(_alignment) <= maxAlignment); + std.debug.assert(@as(usize, 1) << @backingInt(_alignment) <= maxAlignment); std.debug.assert(std.math.isPowerOfTwo(len)); std.debug.assert(len >= minSize); std.debug.assert(len <= maxSize); @@ -876,7 +870,7 @@ pub fn PowerOfTwoPoolAllocator(minSize: comptime_int, maxSize: comptime_int, max } fn free(ctx: *anyopaque, memory: []u8, _alignment: std.mem.Alignment, _: usize) void { - std.debug.assert(@as(usize, 1) << @intFromEnum(_alignment) <= maxAlignment); + std.debug.assert(@as(usize, 1) << @backingInt(_alignment) <= maxAlignment); std.debug.assert(std.math.isPowerOfTwo(memory.len)); const self: *Self = @ptrCast(@alignCast(ctx)); const bucket = @ctz(memory.len) - baseShift; diff --git a/src/utils/list.zig b/src/utils/list.zig index bed404252b..d5904e0ce2 100644 --- a/src/utils/list.zig +++ b/src/utils/list.zig @@ -412,7 +412,7 @@ pub fn List(comptime T: type) type { } pub fn print(self: *@This(), allocator: NeverFailingAllocator, comptime fmt: []const u8, args: anytype) void { - var buffer: std.ArrayList(u8) = .{.items = self.items, .capacity = self.capacity}; + var buffer: std.ArrayList(u8) = .{.items = self.items, .capacity = self.capacity, .pointer_stability = .{}}; var writer = std.Io.Writer.Allocating.fromArrayList(allocator.allocator, &buffer); // We don't deinit, we will keep the ownership of the array later on! @@ -492,10 +492,10 @@ test "List.print multiple prints" { /// Holds multiple arrays sequentially in memory. /// Allows addressing and remove each subarray individually, as well as iterating through all of them at once. pub fn MultiArray(T: type, Range: type) type { - const size = @typeInfo(Range).@"enum".fields.len; - std.debug.assert(@typeInfo(Range).@"enum".is_exhaustive); - for (@typeInfo(Range).@"enum".fields) |field| { - std.debug.assert(field.value < size); + const size = @typeInfo(Range).@"enum".field_names.len; + std.debug.assert(@typeInfo(Range).@"enum".mode == .exhaustive); + for (@typeInfo(Range).@"enum".field_values) |fieldValue| { + std.debug.assert(fieldValue < size); } return struct { offsets: [size + 1]usize = @splat(0), @@ -537,7 +537,7 @@ pub fn MultiArray(T: type, Range: type) type { } pub fn replaceRange(self: *@This(), allocator: NeverFailingAllocator, range: Range, elems: []const T) void { - const i: usize = @intFromEnum(range); + const i: usize = @backingInt(range); const oldLen = self.offsets[i + 1] - self.offsets[i]; self.ensureCapacity(allocator, self.offsets[size] - oldLen + elems.len); const startIndex = self.offsets[i + 1]; @@ -556,7 +556,7 @@ pub fn MultiArray(T: type, Range: type) type { } pub fn getRange(self: *@This(), range: Range) []T { - const i: usize = @intFromEnum(range); + const i: usize = @backingInt(range); const startIndex = self.offsets[i]; const endIndex = self.offsets[i + 1]; return self.items[startIndex..endIndex]; diff --git a/src/vec.zig b/src/vec.zig index 17671e8a5c..02962ae05a 100644 --- a/src/vec.zig +++ b/src/vec.zig @@ -20,7 +20,7 @@ pub inline fn swizzle( comptime z: Vec4fComponent, comptime w: Vec4fComponent, ) Vec4f { - return @shuffle(f32, v, undefined, [4]i32{@intFromEnum(x), @intFromEnum(y), @intFromEnum(z), @intFromEnum(w)}); + return @shuffle(f32, v, undefined, [4]i32{@backingInt(x), @backingInt(y), @backingInt(z), @backingInt(w)}); } pub inline fn combine(pos: Vec3f, w: f32) Vec4f {