diff --git a/src/entity.zig b/src/entity.zig index 8fb965a8f1..b62c9f7be2 100644 --- a/src/entity.zig +++ b/src/entity.zig @@ -5,6 +5,7 @@ const Mat4f = vec.Mat4f; const Vec3d = vec.Vec3d; const Vec3f = vec.Vec3f; const Vec4f = vec.Vec4f; +const BinaryReader = main.utils.BinaryReader; pub const components = @import("entityComponent/_list.zig"); @@ -15,6 +16,13 @@ pub const EntityNetworkData = struct { rot: Vec3f, }; +pub const ComponentActionType = enum(u8) { + unload = 0, + load = 1, + modifyServer = 2, + modifyClient = 3, +}; + pub const EntityComponentLoadError = error{ DecodingBase64, UnreadableId, @@ -29,10 +37,12 @@ pub const Entity = enum(u32) { }; pub const EntityComponentId = u32; const EntityComponentVTable = struct { - serverLoad: *const fn (entity: Entity, reader: *main.utils.BinaryReader, version: u32) EntityComponentLoadError!void, - clientLoad: *const fn (entity: Entity, reader: *main.utils.BinaryReader, version: u32) EntityComponentLoadError!void, + serverLoad: *const fn (entity: Entity, reader: *BinaryReader, version: u32) EntityComponentLoadError!void, + clientLoad: *const fn (entity: Entity, reader: *BinaryReader, version: u32) EntityComponentLoadError!void, serverUnload: *const fn (entity: Entity) void, clientUnload: *const fn (entity: Entity) void, + serverModifyComponent: *const fn (entity: Entity, reader: *BinaryReader) void, + clientModifyComponent: *const fn (entity: Entity, reader: *BinaryReader) void, }; var componentList: []?EntityComponentVTable = undefined; @@ -51,6 +61,8 @@ pub fn initComponents() void { .clientLoad = @field(components, decl.name).client.load, .serverUnload = @field(components, decl.name).server.unload, .clientUnload = @field(components, decl.name).client.unload, + .serverModifyComponent = @field(components, decl.name).server.modifyComponent, + .clientModifyComponent = @field(components, decl.name).client.modifyComponent, }; } else { std.log.err("entity components: Duplicate list id {}.", .{componentId}); @@ -97,6 +109,23 @@ pub fn unloadComponent(comptime side: main.sync.Side, componentId: EntityCompone } } +pub fn modifyComponent(comptime side: main.sync.Side, componentId: EntityComponentId, entity: Entity, componentData: []const u8) EntityComponentLoadError!void { + if (componentId >= componentList.len) { + std.log.err("unknown Component Id {} ", .{componentId}); + return error.UnknownComponentId; + } + var componentReader = BinaryReader.init(componentData); + if (componentList[componentId]) |vtable| { + switch (side) { + .server => vtable.serverModifyComponent(entity, &componentReader), + .client => vtable.clientModifyComponent(entity, &componentReader), + } + } else { + std.log.err("unknown Component Id {} ", .{componentId}); + return error.UnknownComponentId; + } +} + pub const client = struct { pub fn init() void { inline for (@typeInfo(components).@"struct".decls) |decl| { diff --git a/src/entityComponent/_template.zig b/src/entityComponent/_template.zig index 1b2d134c73..c9dfa39921 100644 --- a/src/entityComponent/_template.zig +++ b/src/entityComponent/_template.zig @@ -33,6 +33,15 @@ pub const entityComponentVersion = 0; // ############################# Client only stuff ################################ pub const client = struct { + pub const ExampleComponent = struct { + pub fn save(self: ExampleComponent, writer: *utils.BinaryWriter, audience: main.entity.AudienceInfo) main.entity.ComponentSaveBehaviour { + _ = self; + _ = writer; + _ = audience; + // do i want to be saved? + return .save; + } + }; pub fn load(entity: Entity, reader: *utils.BinaryReader, version: u32) main.entity.EntityComponentLoadError!void { _ = entity; _ = reader; @@ -44,6 +53,15 @@ pub const client = struct { pub fn init() void {} pub fn deinit() void {} pub fn clear() void {} + pub fn get(entity: Entity) ?ExampleComponent { + _ = entity; + return null; + } + + pub fn modifyComponent(entity: Entity, reader: *utils.BinaryReader) void { + _ = entity; + _ = reader; + } }; // ############################# Server only stuff ################################ pub const server = struct { @@ -70,4 +88,8 @@ pub const server = struct { pub fn unload(entity: Entity) void { _ = entity; } + pub fn modifyComponent(entity: Entity, reader: *utils.BinaryReader) void { + _ = entity; + _ = reader; + } }; diff --git a/src/entityComponent/bag.zig b/src/entityComponent/bag.zig index 8942eebcfc..7c357b76dd 100644 --- a/src/entityComponent/bag.zig +++ b/src/entityComponent/bag.zig @@ -37,6 +37,11 @@ const playerBagSizeLimit = 120; pub const client = struct { const Component = struct { bag: items.Inventory.BagInventory, + pub fn save(self: Component, writer: *utils.BinaryWriter, audience: main.entity.AudienceInfo) main.entity.ComponentSaveBehaviour { + if (audience != .disk and audience != .playerHimself) return .discard; + self.bag.toBytes(writer); + return .save; + } }; pub var components: main.utils.SparseSet(Component, Entity) = .{}; @@ -48,6 +53,9 @@ pub const client = struct { components.clear(); } + pub fn get(entity: Entity) ?Component { + return (components.get(entity) orelse return null).*; + } pub fn getBag(entity: Entity) ?*items.Inventory.BagInventory { return &(components.get(entity) orelse return null).bag; } @@ -62,6 +70,10 @@ pub const client = struct { const bag = components.fetchRemove(entity) catch return; bag.bag.deinit(); } + pub fn modifyComponent(entity: Entity, reader: *utils.BinaryReader) void { + _ = entity; + _ = reader; + } }; // ############################# Server only stuff ################################ @@ -103,4 +115,8 @@ pub const server = struct { const bag = components.fetchRemove(entity) catch return; bag.bag.deinit(); } + pub fn modifyComponent(entity: Entity, reader: *utils.BinaryReader) void { + _ = entity; + _ = reader; + } }; diff --git a/src/entityComponent/model.zig b/src/entityComponent/model.zig index 1154d37e5a..8affd24a92 100644 --- a/src/entityComponent/model.zig +++ b/src/entityComponent/model.zig @@ -38,6 +38,11 @@ pub const client = struct { main.systems.systems.modelRenderer.client.nodeBuffer.free(self.bufferAllocation); } + pub fn save(self: Component, writer: *utils.BinaryWriter, audience: main.entity.AudienceInfo) main.entity.ComponentSaveBehaviour { + _ = audience; + writer.writeVarInt(u32, self.entityModel.index); + return .save; + } }; pub var components: main.utils.SparseSet(Component, Entity) = .{}; @@ -75,6 +80,10 @@ pub const client = struct { pub fn get(entity: Entity) ?*Component { return components.get(entity); } + pub fn modifyComponent(entity: Entity, reader: *utils.BinaryReader) void { + _ = entity; + _ = reader; + } }; // ############################# Server only stuff ################################ @@ -114,4 +123,8 @@ pub const server = struct { pub fn get(entity: Entity) ?*const Component { return components.get(entity); } + pub fn modifyComponent(entity: Entity, reader: *utils.BinaryReader) void { + _ = entity; + _ = reader; + } }; diff --git a/src/entityComponent/permissions.zig b/src/entityComponent/permissions.zig index fcb1629a38..052839efe6 100644 --- a/src/entityComponent/permissions.zig +++ b/src/entityComponent/permissions.zig @@ -12,6 +12,15 @@ pub const entityComponentVersion = 0; // ############################# Client only stuff ################################ pub const client = struct { + pub const Component = struct { + pub fn save(self: Component, writer: *utils.BinaryWriter, audience: main.entity.AudienceInfo) main.entity.ComponentSaveBehaviour { + _ = self; + _ = writer; + _ = audience; + // do i want to be saved? + return .save; + } + }; pub fn load(entity: Entity, reader: *BinaryReader, version: u32) main.entity.EntityComponentLoadError!void { _ = entity; _ = reader; @@ -23,22 +32,24 @@ pub const client = struct { pub fn init() void {} pub fn deinit() void {} pub fn clear() void {} + pub fn get(entity: Entity) ?*Component { + _ = entity; + return null; + } + + pub fn modifyComponent(entity: Entity, reader: *utils.BinaryReader) void { + _ = entity; + _ = reader; + } }; // ############################# Server only stuff ################################ -pub const server = struct { +pub const server = struct { // MARK: server pub const Component = struct { permissions: main.server.permission.Permissions, - permissionGroups: std.AutoHashMapUnmanaged(main.server.permission.Group, void), pub fn save(self: Component, writer: *BinaryWriter, audience: main.entity.AudienceInfo) main.entity.ComponentSaveBehaviour { if (audience != .disk) return .discard; self.permissions.toBytes(writer); - - writer.writeVarInt(usize, self.permissionGroups.count()); - var it = self.permissionGroups.keyIterator(); - while (it.next()) |group| { - group.toBytes(writer); - } return .save; } }; @@ -60,25 +71,11 @@ pub const server = struct { return &(components.get(entity) orelse return null).permissions; } - pub fn getPermissionGroups(entity: Entity) ?*std.AutoHashMapUnmanaged(main.server.permission.Group, void) { - return &(components.get(entity) orelse return null).permissionGroups; - } - pub fn hasPermission(entity: Entity, permissionPath: []const u8) bool { - switch ((getPermissions(entity) orelse return false).hasPermission(permissionPath)) { - .yes => return true, - .no => return false, - .neutral => {}, - } - var groupIt = (getPermissionGroups(entity).?).keyIterator(); - while (groupIt.next()) |group| { - const result = group.hasPermission(permissionPath) catch blk: { - std.debug.assert(removeFromGroup(entity, group.*) == true); - break :blk .no; - }; - if (result == .yes) return true; - } - return false; + return switch ((getPermissions(entity) orelse return false).hasPermission(permissionPath)) { + .yes => true, + .no, .neutral => false, + }; } pub fn addPermission(entity: Entity, listType: main.server.permission.Permissions.ListType, permissionPath: []const u8) void { @@ -89,39 +86,25 @@ pub const server = struct { return (getPermissions(entity) orelse return false).removePermission(listType, permissionPath); } - pub fn addToGroup(entity: Entity, group: main.server.permission.Group) void { - (getPermissionGroups(entity) orelse return).put(main.globalAllocator.allocator, group, {}) catch unreachable; - } - - pub fn removeFromGroup(entity: Entity, group: main.server.permission.Group) bool { - return getPermissionGroups(entity).?.remove(group); - } - pub fn loadFromData(entity: Entity, reader: *BinaryReader, version: u32) main.entity.EntityComponentLoadError!void { if (version != entityComponentVersion) return error.InvalidComponentVersion; - const component = components.add(main.globalAllocator, entity); - component.permissions = .init(main.globalAllocator); - component.permissions.fromBytes(reader) catch return error.UnreadableComponentData; - component.permissionGroups = .empty; - const len = reader.readVarInt(usize) catch return; - for (0..len) |_| { - const group = main.server.permission.Group.fromBytes(reader) catch |err| { - if (err == error.GroupNotFound) continue; // if the group is not found we just skip it. - return error.UnreadableComponentData; - }; - addToGroup(entity, group); - } + const permissions = &components.add(main.globalAllocator, entity).permissions; + permissions.* = .init(main.globalAllocator); + permissions.fromBytes(reader) catch return error.UnreadableComponentData; } pub fn loadEmpty(entity: Entity) void { - const component = components.add(main.globalAllocator, entity); - component.permissions = .init(main.globalAllocator); - component.permissionGroups = .empty; + const permissions = &components.add(main.globalAllocator, entity).permissions; + permissions.* = .init(main.globalAllocator); } pub fn unload(entity: Entity) void { - var component = components.fetchRemove(entity) catch return; - component.permissions.deinit(); - component.permissionGroups.deinit(main.globalAllocator.allocator); + const permissions = components.fetchRemove(entity) catch return; + permissions.permissions.deinit(); + } + + pub fn modifyComponent(entity: Entity, reader: *utils.BinaryReader) void { + _ = entity; + _ = reader; } }; diff --git a/src/entityComponent/player.zig b/src/entityComponent/player.zig index d20a73a5f7..b11f97eff7 100644 --- a/src/entityComponent/player.zig +++ b/src/entityComponent/player.zig @@ -26,6 +26,11 @@ pub const entityComponentVersion = 0; pub const client = struct { const Component = struct { playerIndex: u32, + pub fn save(self: Component, writer: *utils.BinaryWriter, audience: main.entity.AudienceInfo) main.entity.ComponentSaveBehaviour { + writer.writeVarInt(u32, self.playerIndex); + if (audience == .disk) return .discard; + return .save; + } }; pub var components: main.utils.SparseSet(Component, Entity) = .{}; @@ -51,6 +56,11 @@ pub const client = struct { pub fn get(entity: Entity) ?*Component { return components.get(entity); } + + pub fn modifyComponent(entity: Entity, reader: *utils.BinaryReader) void { + _ = entity; + _ = reader; + } }; // ############################# Server only stuff ################################ @@ -92,4 +102,9 @@ pub const server = struct { pub fn get(entity: Entity) ?*Component { return components.get(entity); } + + pub fn modifyComponent(entity: Entity, reader: *utils.BinaryReader) void { + _ = entity; + _ = reader; + } }; diff --git a/src/network/protocols.zig b/src/network/protocols.zig index fa59c9474d..cf81a22cc2 100644 --- a/src/network/protocols.zig +++ b/src/network/protocols.zig @@ -17,6 +17,7 @@ const Vec3f = vec.Vec3f; const Vec3i = vec.Vec3i; const NeverFailingAllocator = main.heap.NeverFailingAllocator; const BlockUpdate = renderer.mesh_storage.BlockUpdate; +const ComponentActionType = main.entity.ComponentActionType; const network = main.network; const Connection = network.Connection; @@ -1068,44 +1069,53 @@ pub const blockEntityUpdate = struct { // MARK: blockEntityUpdate pub const EntityComponentUpdate = struct { // MARK: EntityComponentUpdate pub const id: u8 = 15; - const ActionType = enum(u8) { - unload = 0, - load = 1, - }; - - fn clientReceive(_: *Connection, reader: *utils.BinaryReader) !void { - const entityId: main.entity.Entity = @enumFromInt(try reader.readVarInt(u32)); - const componentId = try reader.readVarInt(u32); - const actionType: ActionType = try reader.readEnum(ActionType); - - if (actionType == .load) { - const componentVersion = try reader.readVarInt(u32); - try main.entity.loadComponent(.client, componentId, entityId, reader.remaining, componentVersion); - } else if (actionType == .unload) { - try main.entity.unloadComponent(.client, componentId, entityId); - } - } pub fn unload(conn: *Connection, entityId: main.entity.Entity, componentId: u32) void { var writer = utils.BinaryWriter.init(main.stackAllocator); defer writer.deinit(); writer.writeVarInt(u32, @intFromEnum(entityId)); - writer.writeVarInt(u32, componentId); - writer.writeEnum(ActionType, ActionType.unload); + writer.writeEnum(ComponentActionType, ComponentActionType.unload); - conn.send(.secure, id, writer.data.items); + execute(conn, entityId, componentId, writer.data.items); } pub fn load(conn: *Connection, entityId: main.entity.Entity, componentId: u32, version: u32, componentData: []const u8) void { var writer = utils.BinaryWriter.init(main.stackAllocator); defer writer.deinit(); writer.writeVarInt(u32, @intFromEnum(entityId)); - writer.writeVarInt(u32, componentId); - writer.writeEnum(ActionType, ActionType.load); + writer.writeEnum(ComponentActionType, ComponentActionType.load); // specific to `load` writer.writeVarInt(u32, version); writer.writeSlice(componentData); - conn.send(.secure, id, writer.data.items); + execute(conn, entityId, componentId, writer.data.items); + } + pub fn modifyClient(conn: *Connection, entityId: main.entity.Entity, componentId: u32, componentData: []const u8) void { + var writer = utils.BinaryWriter.init(main.stackAllocator); + defer writer.deinit(); + + writer.writeVarInt(u32, @intFromEnum(entityId)); + writer.writeEnum(ComponentActionType, ComponentActionType.modifyClient); + // specific to `modify` + writer.writeSlice(componentData); + + std.log.debug("running to change", .{}); + + execute(conn, entityId, componentId, writer.data.items); + } + pub fn modifyServer(conn: *Connection, entityId: main.entity.Entity, componentId: u32, componentData: []const u8) void { + var writer = utils.BinaryWriter.init(main.stackAllocator); + defer writer.deinit(); + + writer.writeVarInt(u32, @intFromEnum(entityId)); + writer.writeEnum(ComponentActionType, ComponentActionType.modifyServer); + // specific to `modify` + writer.writeSlice(componentData); + + execute(conn, entityId, componentId, writer.data.items); + } + + fn execute(_: *Connection, entityId: main.entity.Entity, componentId: u32, data: []const u8) void { + main.sync.client.executeCommand(.{.modifyComponent = .{.target = entityId, .componentId = componentId, .modification = data}}); } }; diff --git a/src/sync.zig b/src/sync.zig index e9b74c6020..1e8fac6b36 100644 --- a/src/sync.zig +++ b/src/sync.zig @@ -255,6 +255,7 @@ pub const Command = struct { // MARK: Command updateBlock = 9, addHealth = 10, chatCommand = 12, + modifyComponent = 19, }; pub const Payload = union(PayloadType) { open: Open, @@ -276,6 +277,7 @@ pub const Command = struct { // MARK: Command updateBlock: UpdateBlock, addHealth: AddHealth, chatCommand: ChatCommand, + modifyComponent: ModifyComponent, }; const BaseOperationType = enum(u8) { @@ -288,6 +290,7 @@ pub const Command = struct { // MARK: Command useDurability = 4, addHealth = 5, addEnergy = 6, + modifyComponent = 9, }; /// The BaseOperation is the primitive operation used by Command. It is responsible for executing the operation as @@ -343,6 +346,12 @@ pub const Command = struct { // MARK: Command energy: f32, previous: f32, }, + modifyComponent: struct { + target: ?*main.server.User, + componentId: u32, + modification: []const u8, + previous: []const u8, + }, }; const SyncOperationType = enum(u8) { @@ -658,6 +667,14 @@ pub const Command = struct { // MARK: Command .addEnergy => |info| { main.game.Player.super.energy = info.previous; }, + .modifyComponent => |info| { + var reader = BinaryReader.init(info.previous); + const entityId: main.entity.Entity = @enumFromInt(reader.readVarInt(u32) catch return); + const componentId = info.componentId; + const componentVersion = reader.readVarInt(u32) catch return; + + main.entity.loadComponent(.client, componentId, entityId, reader.remaining, componentVersion) catch return; + }, } } } @@ -665,7 +682,7 @@ pub const Command = struct { // MARK: Command fn finalize(self: Command, allocator: NeverFailingAllocator, side: Side, reader: *BinaryReader) !void { for (self.baseOperations.items) |step| { switch (step) { - .move, .swap, .create, .moveToBag, .takeFromBag, .addHealth, .addEnergy => {}, + .move, .swap, .create, .moveToBag, .takeFromBag, .addHealth, .addEnergy, .modifyComponent => {}, .delete => |info| { info.item.deinit(); }, @@ -854,6 +871,60 @@ pub const Command = struct { // MARK: Command main.game.Player.super.energy = std.math.clamp(main.game.Player.super.energy + info.energy, 0, main.game.Player.super.maxEnergy); } }, + .modifyComponent => |*info| { + if (side == .server) { + var reader = BinaryReader.init(info.modification); + const entityId: main.entity.Entity = @enumFromInt(reader.readVarInt(u32) catch return); + const componentId = info.componentId; + const actionType: main.entity.ComponentActionType = reader.readEnum(main.entity.ComponentActionType) catch return; + std.log.debug("side {} actiontype {}", .{side, actionType}); + + inline for (@typeInfo(main.entity.components).@"struct".decls) |decl| { + if (@field(main.entity.components, decl.name).server.get(entityId)) |component| { + var binaryWriter = main.utils.BinaryWriter.init(main.stackAllocator); + defer binaryWriter.deinit(); + + binaryWriter.writeInt(u32, @intFromEnum(entityId)); + binaryWriter.writeVarInt(u32, @field(main.entity.components, decl.name).entityComponentVersion); + if (component.save(&binaryWriter, .playerNearby) == .save) { + info.previous = binaryWriter.data.items; + } + } + } + + if (actionType == .modifyServer) { + main.entity.modifyComponent(.server, componentId, entityId, reader.remaining) catch return; + } + } else { + var binaryReader = BinaryReader.init(info.modification); + const entityId: main.entity.Entity = @enumFromInt(binaryReader.readVarInt(u32) catch return); + const componentId = info.componentId; + const actionType: main.entity.ComponentActionType = binaryReader.readEnum(main.entity.ComponentActionType) catch return; + std.log.debug("side {} actiontype {}", .{side, actionType}); + + inline for (@typeInfo(main.entity.components).@"struct".decls) |decl| { + if (@field(main.entity.components, decl.name).client.get(entityId)) |component| { + var binaryWriter = main.utils.BinaryWriter.init(main.stackAllocator); + defer binaryWriter.deinit(); + + binaryWriter.writeInt(u32, @intFromEnum(entityId)); + binaryWriter.writeVarInt(u32, @field(main.entity.components, decl.name).entityComponentVersion); + if (component.save(&binaryWriter, .playerNearby) == .save) { + info.previous = binaryWriter.data.items; + } + } + } + + if (actionType == .load) { + const componentVersion = binaryReader.readVarInt(u32) catch return; + main.entity.loadComponent(.client, componentId, entityId, binaryReader.remaining, componentVersion) catch return; + } else if (actionType == .unload) { + main.entity.unloadComponent(.client, componentId, entityId) catch return; + } else if (actionType == .modifyClient) { + main.entity.modifyComponent(.client, componentId, entityId, binaryReader.remaining) catch return; + } + } + }, } self.baseOperations.append(allocator, op); } @@ -1748,6 +1819,52 @@ pub const Command = struct { // MARK: Command }; } }; + + const ModifyComponent = struct { // MARK: ModifyComponent + target: main.entity.Entity, + componentId: u32, + modification: []const u8, + + pub fn run(self: ModifyComponent, ctx: Context) error{serverFailure}!void { + var target: ?*main.server.User = null; + + if (ctx.side == .server) { + const userList = main.server.getUserList(main.stackAllocator); + defer main.stackAllocator.free(userList); + for (userList) |user| { + if (user.id == self.target) { + target = user; + break; + } + } + + if (target == null) return error.serverFailure; + } + + ctx.execute(.{.modifyComponent = .{ + .target = target, + .componentId = self.componentId, + .modification = self.modification, + .previous = "", + }}); + } + + fn serialize(self: ModifyComponent, writer: *BinaryWriter) void { + writer.writeEnum(main.entity.Entity, self.target); + writer.writeInt(u32, @bitCast(self.componentId)); + writer.writeSliceWithSize(self.modification); + } + + fn deserialize(reader: *BinaryReader, _: Side, user: ?*main.server.User) !ModifyComponent { + const result: ModifyComponent = .{ + .target = try reader.readEnum(main.entity.Entity), + .componentId = @bitCast(try reader.readInt(u32)), + .modification = try reader.readSliceWithSize(), + }; + if (user.?.id != result.target) return error.Invalid; + return result; + } + }; }; pub threadlocal var threadContext: ThreadContext = .other;