diff --git a/src/game.zig b/src/game.zig index 6a8c952f..5b27ad23 100644 --- a/src/game.zig +++ b/src/game.zig @@ -48,6 +48,12 @@ pub const Player = struct { pub var isFlying: std.atomic.Atomic(bool) = std.atomic.Atomic(bool).init(true); var mutex: std.Thread.Mutex = std.Thread.Mutex{}; + pub fn setPosBlocking(newPos: Vec3d) void { + mutex.lock(); + defer mutex.unlock(); + pos = newPos; + } + pub fn getPosBlocking() Vec3d { mutex.lock(); defer mutex.unlock(); @@ -70,7 +76,7 @@ pub const World = struct { clearColor: Vec4f = Vec4f{.x=0, .y=0, .z=0, .w=1}, name: []const u8, milliTime: i64, - gameTime: i64 = 0, + gameTime: std.atomic.Atomic(i64) = std.atomic.Atomic(i64).init(0), spawn: Vec3f = undefined, blockPalette: *assets.BlockPalette = undefined, // TODO: @@ -124,11 +130,14 @@ pub const World = struct { var newTime: i64 = std.time.milliTimestamp(); while(self.milliTime +% 100 -% newTime < 0) { // TODO: Just use milli time directly? self.milliTime +%= 100; - self.gameTime +%= 1; + var curTime = self.gameTime.load(.Monotonic); + while(self.gameTime.tryCompareAndSwap(curTime, curTime +% 1, .Monotonic, .Monotonic)) |actualTime| { + curTime = actualTime; + } } // Ambient light: { - var dayTime = std.math.absInt(@mod(self.gameTime, dayCycle) -% dayCycle/2) catch 0; + var dayTime = std.math.absInt(@mod(self.gameTime.load(.Monotonic), dayCycle) -% dayCycle/2) catch 0; if(dayTime < dayCycle/4 - dayCycle/16) { self.ambientLight = 0.1; self.clearColor.x = 0; diff --git a/src/network.zig b/src/network.zig index c5e92001..38c9a674 100644 --- a/src/network.zig +++ b/src/network.zig @@ -815,8 +815,6 @@ pub const Protocols: struct { }, entity: type = struct { const id: u8 = 8; - const type_entity: u8 = 0; - const type_item: u8 = 1; fn receive(_: *Connection, data: []const u8) !void { const jsonArray = json.parseFromString(main.threadAllocator, data); defer jsonArray.free(main.threadAllocator); @@ -941,6 +939,211 @@ pub const Protocols: struct { // } // } }, + genericUpdate: type = struct { + const id: u8 = 9; + const type_renderDistance: u8 = 0; + const type_teleport: u8 = 1; + const type_cure: u8 = 2; + const type_inventoryAdd: u8 = 3; + const type_inventoryFull: u8 = 4; + const type_inventoryClear: u8 = 5; + const type_itemStackDrop: u8 = 6; + const type_itemStackCollect: u8 = 7; + const type_timeAndBiome: u8 = 8; + fn receive(_: *Connection, data: []const u8) !void { + switch(data[0]) { + type_renderDistance => { + const renderDistance = std.mem.readIntBig(i32, data[1..5]); + const LODFactor = @bitCast(f32, std.mem.readIntBig(u32, data[5..9])); + _ = renderDistance; + _ = LODFactor; + // TODO: +// if(conn instanceof User) { +// User user = (User)conn; +// user.renderDistance = renderDistance; +// user.LODFactor = LODFactor; +// } + }, + type_teleport => { + game.Player.setPosBlocking(Vec3d{ + .x = @bitCast(f64, std.mem.readIntBig(u64, data[1..9])), + .y = @bitCast(f64, std.mem.readIntBig(u64, data[9..17])), + .z = @bitCast(f64, std.mem.readIntBig(u64, data[17..25])), + }); + }, + type_cure => { + // TODO: +// Cubyz.player.health = Cubyz.player.maxHealth; +// Cubyz.player.hunger = Cubyz.player.maxHunger; + }, + type_inventoryAdd => { + const slot = std.mem.readIntBig(u32, data[1..5]); + const amount = std.mem.readIntBig(u32, data[5..9]); + _ = slot; + _ = amount; + // TODO: +// ((User)conn).player.getInventory().getStack(slot).add(amount); + }, + type_inventoryFull => { + // TODO: +// JsonObject json = JsonParser.parseObjectFromString(new String(data, offset + 1, length - 1, StandardCharsets.UTF_8)); +// ((User)conn).player.getInventory().loadFrom(json, Server.world.getCurrentRegistries()); + }, + type_inventoryClear => { + // TODO: +// if(conn instanceof User) { +// Inventory inv = ((User)conn).player.getInventory(); +// for (int i = 0; i < inv.getCapacity(); i++) { +// inv.getStack(i).clear(); +// } +// } else { +// Inventory inv = Cubyz.player.getInventory_AND_DONT_FORGET_TO_SEND_CHANGES_TO_THE_SERVER(); +// for (int i = 0; i < inv.getCapacity(); i++) { +// inv.getStack(i).clear(); +// } +// clearInventory(conn); // Needs to send changes back to server, to ensure correct order. +// } + }, + type_itemStackDrop => { + // TODO: +// JsonObject json = JsonParser.parseObjectFromString(new String(data, offset + 1, length - 1, StandardCharsets.UTF_8)); +// Item item = Item.load(json, Cubyz.world.registries); +// if (item == null) { +// break; +// } +// Server.world.drop( +// new ItemStack(item, json.getInt("amount", 1)), +// new Vector3d(json.getDouble("x", 0), json.getDouble("y", 0), json.getDouble("z", 0)), +// new Vector3f(json.getFloat("dirX", 0), json.getFloat("dirY", 0), json.getFloat("dirZ", 0)), +// json.getFloat("vel", 0), +// Server.UPDATES_PER_SEC*5 +// ); + }, + type_itemStackCollect => { + // TODO: +// JsonObject json = JsonParser.parseObjectFromString(new String(data, offset + 1, length - 1, StandardCharsets.UTF_8)); +// Item item = Item.load(json, Cubyz.world.registries); +// if (item == null) { +// break; +// } +// int remaining = Cubyz.player.getInventory_AND_DONT_FORGET_TO_SEND_CHANGES_TO_THE_SERVER().addItem(item, json.getInt("amount", 1)); +// sendInventory_full(Cubyz.world.serverConnection, Cubyz.player.getInventory_AND_DONT_FORGET_TO_SEND_CHANGES_TO_THE_SERVER()); +// if(remaining != 0) { +// // Couldn't collect everything → drop it again. +// itemStackDrop(Cubyz.world.serverConnection, new ItemStack(item, remaining), Cubyz.player.getPosition(), Camera.getDirection(), 0); +// } + }, + type_timeAndBiome => { + if(game.world) |world| { + const jsonObject = json.parseFromString(main.threadAllocator, data[1..]); + defer jsonObject.free(main.threadAllocator); + var expectedTime = jsonObject.get(i64, "time", 0); + var curTime = world.gameTime.load(.Monotonic); + if(std.math.absInt(curTime -% expectedTime) catch std.math.maxInt(i64) >= 1000) { + world.gameTime.store(expectedTime, .Monotonic); + } else if(curTime < expectedTime) { // world.gameTime++ + while(world.gameTime.tryCompareAndSwap(curTime, curTime +% 1, .Monotonic, .Monotonic)) |actualTime| { + curTime = actualTime; + } + } else { // world.gameTime-- + while(world.gameTime.tryCompareAndSwap(curTime, curTime -% 1, .Monotonic, .Monotonic)) |actualTime| { + curTime = actualTime; + } + } + // TODO: +// world.playerBiome = world.registries.biomeRegistry.getByID(json.getString("biome", "")); + } + }, + else => |unrecognizedType| { + std.log.err("Unrecognized type for genericUpdateProtocol: {}. Data: {any}", .{unrecognizedType, data}); + }, + } + } + + fn addHeaderAndSendImportant(conn: *Connection, header: u8, data: []const u8) !void { + const headeredData = try main.threadAllocator.alloc(u8, data.len + 1); + defer main.threadAllocator.free(headeredData); + headeredData[0] = header; + std.mem.copy(u8, headeredData[1..], data); + try conn.sendImportant(id, headeredData); + } + + fn addHeaderAndSendUnimportant(conn: *Connection, header: u8, data: []const u8) !void { + const headeredData = try main.threadAllocator.alloc(u8, data.len + 1); + defer main.threadAllocator.free(headeredData); + headeredData[0] = header; + std.mem.copy(u8, headeredData[1..], data); + try conn.sendUnimportant(id, headeredData); + } + + pub fn sendRenderDistance(conn: *Connection, renderDistance: i32, LODFactor: f32) !void { + var data: [9]u8 = undefined; + data[0] = type_renderDistance; + std.mem.writeIntBig(i32, data[1..5], renderDistance); + std.mem.writeIntBig(u32, data[5..9], @bitCast(u32, LODFactor)); + try conn.sendImportant(id, &data); + } + + pub fn sendTPCoordinates(conn: *Connection, pos: Vec3d) !void { + var data: [1+24]u8 = undefined; + data[0] = type_teleport; + std.mem.writeIntBig(u64, data[1..9], @bitCast(u64, pos.x)); + std.mem.writeIntBig(u64, data[9..17], @bitCast(u64, pos.y)); + std.mem.writeIntBig(u64, data[17..25], @bitCast(u64, pos.z)); + try conn.sendImportant(id, &data); + } + + pub fn sendCure(conn: *Connection) !void { + var data: [1]u8 = undefined; + data[0] = type_cure; + try conn.sendImportant(id, &data); + } + + pub fn sendInventory_ItemStack_add(conn: *Connection, slot: u32, amount: u32) !void { + var data: [9]u8 = undefined; + data[0] = type_inventoryAdd; + std.mem.writeIntBig(u32, data[1..5], slot); + std.mem.writeIntBig(u32, data[5..9], amount); + try conn.sendImportant(id, &data); + } + + // TODO: +// public void sendInventory_full(ServerConnection conn, Inventory inv) { +// addHeaderAndSendImportant(conn, INVENTORY_FULL, inv.save().toString().getBytes(StandardCharsets.UTF_8)); +// } + + pub fn clearInventory(conn: *Connection) !void { + var data: [1]u8 = undefined; + data[0] = type_inventoryClear; + try conn.sendImportant(id, &data); + } + + // TODO: +// public void itemStackDrop(ServerConnection conn, ItemStack stack, Vector3d pos, Vector3f dir, float vel) { +// JsonObject json = stack.store(); +// json.put("x", pos.x); +// json.put("y", pos.y); +// json.put("z", pos.z); +// json.put("dirX", dir.x); +// json.put("dirY", dir.y); +// json.put("dirZ", dir.z); +// json.put("vel", vel); +// addHeaderAndSendImportant(conn, ITEM_STACK_DROP, json.toString().getBytes(StandardCharsets.UTF_8)); +// } + + // TODO: +// public void itemStackCollect(User user, ItemStack stack) { +// addHeaderAndSendImportant(user, ITEM_STACK_COLLECT, stack.store().toString().getBytes(StandardCharsets.UTF_8)); +// } + + // TODO: +// public void sendTimeAndBiome(User user, ServerWorld world) { +// JsonObject data = new JsonObject(); +// data.put("time", world.gameTime); +// data.put("biome", world.getBiome((int)user.player.getPosition().x, (int)user.player.getPosition().y, (int)user.player.getPosition().z).getRegistryID().toString()); +// addHeaderAndSendUnimportant(user, TIME_AND_BIOME, data.toString().getBytes(StandardCharsets.UTF_8)); +// } + }, } = .{}; diff --git a/src/renderer.zig b/src/renderer.zig index 4140d887..b75e1515 100644 --- a/src/renderer.zig +++ b/src/renderer.zig @@ -506,8 +506,7 @@ pub const RenderStructure = struct { pub fn updateAndGetRenderChunks(conn: *network.Connection, playerPos: Vec3d, renderDistance: i32, LODFactor: f32, frustum: Frustum, meshes: *std.ArrayList(*chunk.meshing.ChunkMesh)) !void { if(lastRD != renderDistance and lastFactor != LODFactor) { - // TODO: -// Protocols.GENERIC_UPDATE.sendRenderDistance(Cubyz.world.serverConnection, renderDistance, LODFactor); + try network.Protocols.genericUpdate.sendRenderDistance(conn, renderDistance, LODFactor); } const px = @floatToInt(chunk.ChunkCoordinate, playerPos.x); const py = @floatToInt(chunk.ChunkCoordinate, playerPos.y);