From 2365b7fd448e82b19bd97623467d82d8a197ed4a Mon Sep 17 00:00:00 2001 From: Mabeeck <84404489+Mabeeck@users.noreply.github.com> Date: Thu, 23 Jul 2026 17:57:21 +0200 Subject: [PATCH 01/12] Add rotation support for /tp yaw and pitch parameters can be relative or absolute New UpdateType added to update player rotation Player rotation update packet is seperate from teleport packet to prevent repeated automated teleportations from messing with camera rotations. Might be useful in cases where modded servers want to prevent players from walking into a certain area or moving entirely. Update tp.zig Don't send a teleport packet if the target is already at the appropriate coordinates. It just seems right to not send a teleport packet if you are only changing the player rotation since the same is done the other way around as well. For consistency's sake. Is only intended to stop teleport packet sending in case of `/tp ~ ~ ~ `, but works in case of `/tp ~ ~ ~` as well. Does nothing to stop packet sending in case of something like `/tp 0 ~ ~`x2. Adapt to command source changes Adapt to command source changes in c4cf9ec8decaf19408b99e7f17afd367169ad81d Make requested changes --- src/network/protocols.zig | 20 +++++++++++++++++++- src/server/command.zig | 34 ++++++++++++++++++++++++++++++++++ src/server/command/tp.zig | 11 ++++++++++- 3 files changed, 63 insertions(+), 2 deletions(-) diff --git a/src/network/protocols.zig b/src/network/protocols.zig index 50a7350942..bdb4877d4c 100644 --- a/src/network/protocols.zig +++ b/src/network/protocols.zig @@ -606,6 +606,7 @@ pub const genericUpdate = struct { // MARK: genericUpdate biome = 4, particles = 5, clear = 6, + setRotation = 7, }; const WorldEditPosition = enum(u2) { @@ -626,6 +627,13 @@ pub const genericUpdate = struct { // MARK: genericUpdate .teleport => { game.Player.setPosBlocking(try reader.readVec(Vec3d)); }, + .setRotation => { + var rot: Vec3f = try reader.readVec(Vec3f); + const bound = std.math.pi/2.0 - 0.001; + rot[0] = std.math.clamp(rot[0], -bound, bound); + game.camera.rotation[0] = rot[0]; + game.camera.rotation[2] = rot[2]; + }, .worldEditPos => { const typ = try reader.readEnum(WorldEditPosition); const pos: ?Vec3i = switch (typ) { @@ -704,7 +712,7 @@ pub const genericUpdate = struct { // MARK: genericUpdate fn serverReceive(conn: *Connection, reader: *utils.BinaryReader) !void { switch (try reader.readEnum(UpdateType)) { - .gamemode, .teleport, .time, .biome, .particles, .clear => return error.InvalidSide, + .gamemode, .teleport, .setRotation, .time, .biome, .particles, .clear => return error.InvalidSide, .worldEditPos => { const typ = try reader.readEnum(WorldEditPosition); const pos: ?Vec3i = switch (typ) { @@ -737,6 +745,16 @@ pub const genericUpdate = struct { // MARK: genericUpdate conn.send(.secure, id, writer.data.items); } + pub fn sendTPRotation(conn: *Connection, rot: Vec3f) void { + var writer = utils.BinaryWriter.initCapacity(main.stackAllocator, 20); + defer writer.deinit(); + + writer.writeEnum(UpdateType, .setRotation); + writer.writeVec(Vec3f, rot); + + conn.send(.secure, id, writer.data.items); + } + pub fn sendWorldEditPos(conn: *Connection, posType: WorldEditPosition, maybePos: ?Vec3i) void { var writer = utils.BinaryWriter.initCapacity(main.stackAllocator, 25); defer writer.deinit(); diff --git a/src/server/command.zig b/src/server/command.zig index 1e8f0e6485..382f0ebd84 100644 --- a/src/server/command.zig +++ b/src/server/command.zig @@ -108,6 +108,27 @@ pub const Coordinate = union(enum) { } }; +pub const Rotation = union(enum) { + relative: f32, // Relative rotations are indicated by leading `~`. + absolute: f32, + + pub fn parse(_: NeverFailingAllocator, name: []const u8, arg: []const u8, errorMessage: *ListManaged(u8)) error{ParseError}!Rotation { + const isRelative = arg[0] == '~'; + const numberSlice = if (isRelative) arg[1..] else arg; + if (isRelative and numberSlice.len == 0) return .{.relative = 0}; + if (isRelative) { + return .{.relative = std.fmt.parseFloat(f32, numberSlice) catch { + errorMessage.print("Expected number for <{s}>, found \"{s}\"", .{name, numberSlice}); + return error.ParseError; + }}; + } + return .{.absolute = std.fmt.parseFloat(f32, numberSlice) catch { + errorMessage.print("Expected number or \"~\" for <{s}>, found \"{s}\"", .{name, arg}); + return error.ParseError; + }}; + } +}; + pub fn resolveCoordinates(x: Coordinate, y: Coordinate, z: Coordinate, source: Source) error{InvalidArg}!main.vec.Vec3d { if (source != .user and (x == .relative or y == .relative or z == .relative)) { source.sendMessage("Command was run without a user; unable to interpret relative coordinates.", .{}); @@ -121,6 +142,19 @@ pub fn resolveCoordinates(x: Coordinate, y: Coordinate, z: Coordinate, source: S }; } +pub fn resolveRotation(yaw: Rotation, pitch: Rotation, source: Source) error{InvalidArg}!main.vec.Vec3f { + if (source != .user and (yaw == .relative or pitch == .relative)) { + source.sendMessage("Command was run without a user; unable to interpret relative rotation.", .{}); + return error.InvalidArg; + } + const bound = std.math.pi/2.0 - 0.001; + return .{ + std.math.clamp(if (yaw == .relative) source.user.player().rot[0] + yaw.relative*std.math.pi/180 else yaw.absolute*std.math.pi/180, -bound, bound), + 0, + if (pitch == .relative) source.user.player().rot[2] + @mod(pitch.relative, 360)*std.math.pi/180 else @mod(pitch.absolute, 360)*std.math.pi/180, + }; +} + pub const Target = struct { user: *User, diff --git a/src/server/command/tp.zig b/src/server/command/tp.zig index 45d0e94e4c..c30915a90c 100644 --- a/src/server/command/tp.zig +++ b/src/server/command/tp.zig @@ -32,6 +32,14 @@ pub const Args = union(enum) { sourcePlayerIndex: command.PlayerIndex, destinationPlayerIndex: command.PlayerIndex, }, + @"/tp ": struct { + sourcePlayerIndex: ?command.PlayerIndex, + x: command.Coordinate, + y: command.Coordinate, + z: command.Coordinate, + yaw: command.Rotation, + pitch: command.Rotation, + }, }; pub fn execute(args: Args, source: Source) void { @@ -104,5 +112,6 @@ pub fn execute(args: Args, source: Source) void { break :blk dest.user.player().pos; }, }; - main.network.protocols.genericUpdate.sendTPCoordinates(target.user.conn, pos); + + if (!std.meta.eql(target.user.player().pos, pos)) main.network.protocols.genericUpdate.sendTPCoordinates(target.conn, pos); } From 95a5c88d16fe6a417e48358317d506b0bd7f90c2 Mon Sep 17 00:00:00 2001 From: Mabeeck <84404489+Mabeeck@users.noreply.github.com> Date: Tue, 4 Aug 2026 20:05:57 +0200 Subject: [PATCH 02/12] Fix Will do thorough testing once I've migrated to SyncOperations --- src/server/command/tp.zig | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/server/command/tp.zig b/src/server/command/tp.zig index c30915a90c..b5ee7747c4 100644 --- a/src/server/command/tp.zig +++ b/src/server/command/tp.zig @@ -107,11 +107,15 @@ pub fn execute(args: Args, source: Source) void { .@"/tp " => |pos| { break :blk command.resolveCoordinates(pos.x, pos.y, pos.z, source) catch return; }, + .@"/tp " => |pos| { + main.network.protocols.genericUpdate.sendTPRotation(target.user.conn, command.resolveRotation(pos.yaw, pos.pitch, source) catch return); + break :blk command.resolveCoordinates(pos.x, pos.y, pos.z, source) catch return; + }, inline .@"/tp ", .@"/tp " => |index| { const dest = command.Target.fromPlayerIndex(index.destinationPlayerIndex, source) catch return; break :blk dest.user.player().pos; }, }; - if (!std.meta.eql(target.user.player().pos, pos)) main.network.protocols.genericUpdate.sendTPCoordinates(target.conn, pos); + if (!std.meta.eql(target.user.player().pos, pos)) main.network.protocols.genericUpdate.sendTPCoordinates(target.user.conn, pos); } From ac4f01e2af68fa2431a61db08c68456322b114b1 Mon Sep 17 00:00:00 2001 From: Mabeeck <84404489+Mabeeck@users.noreply.github.com> Date: Thu, 6 Aug 2026 18:00:41 +0200 Subject: [PATCH 03/12] Add setRotation SyncOperation The operation gets executed on the server, so it doesn't work. --- src/server/command/tp.zig | 2 +- src/sync.zig | 73 ++++++++++++++++++++++++++++++++++++++- 2 files changed, 73 insertions(+), 2 deletions(-) diff --git a/src/server/command/tp.zig b/src/server/command/tp.zig index b5ee7747c4..1a029aa51d 100644 --- a/src/server/command/tp.zig +++ b/src/server/command/tp.zig @@ -108,7 +108,7 @@ pub fn execute(args: Args, source: Source) void { break :blk command.resolveCoordinates(pos.x, pos.y, pos.z, source) catch return; }, .@"/tp " => |pos| { - main.network.protocols.genericUpdate.sendTPRotation(target.user.conn, command.resolveRotation(pos.yaw, pos.pitch, source) catch return); + main.sync.server.executeCommand(.{.setRotation = .{.target = target.user.id, .rotation = command.resolveRotation(pos.yaw, pos.pitch, source) catch return}}, source.user); break :blk command.resolveCoordinates(pos.x, pos.y, pos.z, source) catch return; }, inline .@"/tp ", .@"/tp " => |index| { diff --git a/src/sync.zig b/src/sync.zig index 7d08c4aeca..2a2e07a2c3 100644 --- a/src/sync.zig +++ b/src/sync.zig @@ -242,6 +242,7 @@ pub const Command = struct { // MARK: Command updateBlock = 9, addHealth = 10, chatCommand = 12, + setRotation = 18, }; pub const Payload = union(PayloadType) { open: Open, @@ -263,6 +264,7 @@ pub const Command = struct { // MARK: Command updateBlock: UpdateBlock, addHealth: AddHealth, chatCommand: ChatCommand, + setRotation: SetRotation, }; const BaseOperationType = enum(u8) { @@ -275,6 +277,7 @@ pub const Command = struct { // MARK: Command useDurability = 4, addHealth = 5, addEnergy = 6, + setRotation = 9, }; pub const BaseOperation = union(BaseOperationType) { @@ -324,6 +327,11 @@ pub const Command = struct { // MARK: Command energy: f32, previous: f32, }, + setRotation: struct { + target: ?*main.server.User, + rotation: Vec3f, + previous: Vec3f, + }, }; const SyncOperationType = enum(u8) { @@ -620,6 +628,9 @@ pub const Command = struct { // MARK: Command .addEnergy => |info| { main.game.Player.super.energy = info.previous; }, + .setRotation => |info| { + main.game.camera.rotation = info.previous; + }, } } } @@ -627,7 +638,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, .setRotation => {}, .delete => |info| { info.item.deinit(); }, @@ -816,6 +827,23 @@ 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); } }, + .setRotation => |*info| { + if (side == .server) { + info.previous = info.target.?.player().rot; + std.log.debug("SetRotation executed on server; target=TRUNCATED, rotation={}", .{info.rotation}); + + info.target.?.player().rot = info.rotation; + self.baseOperations.append(allocator, .{.setRotation = .{ + .target = info.target.?, + .rotation = info.rotation, + .previous = info.previous, + }}); + } else { + std.log.debug("SetRotation executed on client; target=TRUNCATED, rotation={}", .{info.rotation}); + info.previous = main.game.camera.rotation; + main.game.camera.rotation = info.rotation; + } + }, } self.baseOperations.append(allocator, op); } @@ -1759,6 +1787,49 @@ pub const Command = struct { // MARK: Command } }; + const SetRotation = struct { // MARK: SetRotation + target: main.entity.Entity, + rotation: Vec3f, + + fn run(self: SetRotation, ctx: Context) error{serverFailure}!void { + std.log.debug("SetRotation ran; target={}, rotation={}", .{self.target, self.rotation}); + 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(.{.setRotation = .{ + .target = target, + .rotation = self.rotation, + .previous = if (ctx.side == .server) target.?.player().rot else main.game.camera.rotation, + }}); + } + + fn serialize(self: SetRotation, writer: *BinaryWriter) void { + writer.writeEnum(main.entity.Entity, self.target); + writer.writeVec(Vec3f, self.rotation); + } + + fn deserialize(reader: *BinaryReader, _: Side, user: ?*main.server.User) !SetRotation { + const result: SetRotation = .{ + .target = try reader.readEnum(main.entity.Entity), + .rotation = try reader.readVec(Vec3f), + }; + if (user.?.id != result.target) return error.Invalid; + return result; + } + }; + const ChatCommand = struct { // MARK: ChatCommand message: []const u8, From cece174db9b9d464f35686a95f4bc1b138a84a0b Mon Sep 17 00:00:00 2001 From: Mabeeck <84404489+Mabeeck@users.noreply.github.com> Date: Fri, 7 Aug 2026 21:47:58 +0200 Subject: [PATCH 04/12] Update sync.zig Try to fix it --- src/sync.zig | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/src/sync.zig b/src/sync.zig index 2a2e07a2c3..cf97b03188 100644 --- a/src/sync.zig +++ b/src/sync.zig @@ -341,6 +341,7 @@ pub const Command = struct { // MARK: Command health = 3, kill = 4, energy = 5, + rotation = 6, }; const SyncOperation = union(SyncOperationType) { // MARK: SyncOperation @@ -370,6 +371,10 @@ pub const Command = struct { // MARK: Command target: ?*main.server.User, energy: f32, }, + rotation: struct { + target: ?*main.server.User, + rotation: Vec3f, + }, pub fn executeFromData(reader: *BinaryReader) !void { switch (try deserialize(reader)) { @@ -416,6 +421,9 @@ pub const Command = struct { // MARK: Command .energy => |energy| { main.game.Player.super.energy = std.math.clamp(main.game.Player.super.energy + energy.energy, 0, main.game.Player.super.maxEnergy); }, + .rotation => |rotation| { + main.game.camera.rotation = rotation.rotation; + }, } } @@ -429,7 +437,7 @@ pub const Command = struct { // MARK: Command } return result; }, - inline .health, .kill, .energy => |data| { + inline .health, .kill, .energy, .rotation => |data| { const out = allocator.alloc(*main.server.User, 1); out[0] = data.target.?; return out; @@ -439,7 +447,7 @@ pub const Command = struct { // MARK: Command pub fn ignoreSource(self: SyncOperation) bool { return switch (self) { - .create, .delete, .useDurability, .health, .energy => true, + .create, .delete, .useDurability, .health, .energy, .rotation => true, .kill => false, }; } @@ -490,6 +498,12 @@ pub const Command = struct { // MARK: Command .energy = try reader.readFloat(f32), }}; }, + .rotation => { + return .{.rotation = .{ + .target = null, + .rotation = try reader.readVec(Vec3f), + }}; + }, } } @@ -521,6 +535,9 @@ pub const Command = struct { // MARK: Command .energy => |energy| { writer.writeFloat(f32, energy.energy); }, + .rotation => |rotation| { + writer.writeVec(Vec3f, rotation.rotation); + }, } return writer.data.toOwnedSlice(); } @@ -833,10 +850,9 @@ pub const Command = struct { // MARK: Command std.log.debug("SetRotation executed on server; target=TRUNCATED, rotation={}", .{info.rotation}); info.target.?.player().rot = info.rotation; - self.baseOperations.append(allocator, .{.setRotation = .{ + self.syncOperations.append(allocator, .{.rotation = .{ .target = info.target.?, .rotation = info.rotation, - .previous = info.previous, }}); } else { std.log.debug("SetRotation executed on client; target=TRUNCATED, rotation={}", .{info.rotation}); From 5126ed546b72b1f4aab2d83f1c51977dcffd37f3 Mon Sep 17 00:00:00 2001 From: Mabeeck <84404489+Mabeeck@users.noreply.github.com> Date: Sat, 8 Aug 2026 11:34:28 +0200 Subject: [PATCH 05/12] Fixed! Thanks to Wunka --- src/server/command/tp.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/server/command/tp.zig b/src/server/command/tp.zig index 1a029aa51d..a87858cd66 100644 --- a/src/server/command/tp.zig +++ b/src/server/command/tp.zig @@ -108,7 +108,7 @@ pub fn execute(args: Args, source: Source) void { break :blk command.resolveCoordinates(pos.x, pos.y, pos.z, source) catch return; }, .@"/tp " => |pos| { - main.sync.server.executeCommand(.{.setRotation = .{.target = target.user.id, .rotation = command.resolveRotation(pos.yaw, pos.pitch, source) catch return}}, source.user); + main.sync.server.executeCommand(.{.setRotation = .{.target = target.user.id, .rotation = command.resolveRotation(pos.yaw, pos.pitch, source) catch return}}, null); break :blk command.resolveCoordinates(pos.x, pos.y, pos.z, source) catch return; }, inline .@"/tp ", .@"/tp " => |index| { From a8ee3278e17c09e9c2b55c2b2ac5ad2284085548 Mon Sep 17 00:00:00 2001 From: Mabeeck <84404489+Mabeeck@users.noreply.github.com> Date: Sat, 8 Aug 2026 11:41:14 +0200 Subject: [PATCH 06/12] Rip out unused code We don't need it anymore --- src/network/protocols.zig | 20 +------------------- 1 file changed, 1 insertion(+), 19 deletions(-) diff --git a/src/network/protocols.zig b/src/network/protocols.zig index bdb4877d4c..50a7350942 100644 --- a/src/network/protocols.zig +++ b/src/network/protocols.zig @@ -606,7 +606,6 @@ pub const genericUpdate = struct { // MARK: genericUpdate biome = 4, particles = 5, clear = 6, - setRotation = 7, }; const WorldEditPosition = enum(u2) { @@ -627,13 +626,6 @@ pub const genericUpdate = struct { // MARK: genericUpdate .teleport => { game.Player.setPosBlocking(try reader.readVec(Vec3d)); }, - .setRotation => { - var rot: Vec3f = try reader.readVec(Vec3f); - const bound = std.math.pi/2.0 - 0.001; - rot[0] = std.math.clamp(rot[0], -bound, bound); - game.camera.rotation[0] = rot[0]; - game.camera.rotation[2] = rot[2]; - }, .worldEditPos => { const typ = try reader.readEnum(WorldEditPosition); const pos: ?Vec3i = switch (typ) { @@ -712,7 +704,7 @@ pub const genericUpdate = struct { // MARK: genericUpdate fn serverReceive(conn: *Connection, reader: *utils.BinaryReader) !void { switch (try reader.readEnum(UpdateType)) { - .gamemode, .teleport, .setRotation, .time, .biome, .particles, .clear => return error.InvalidSide, + .gamemode, .teleport, .time, .biome, .particles, .clear => return error.InvalidSide, .worldEditPos => { const typ = try reader.readEnum(WorldEditPosition); const pos: ?Vec3i = switch (typ) { @@ -745,16 +737,6 @@ pub const genericUpdate = struct { // MARK: genericUpdate conn.send(.secure, id, writer.data.items); } - pub fn sendTPRotation(conn: *Connection, rot: Vec3f) void { - var writer = utils.BinaryWriter.initCapacity(main.stackAllocator, 20); - defer writer.deinit(); - - writer.writeEnum(UpdateType, .setRotation); - writer.writeVec(Vec3f, rot); - - conn.send(.secure, id, writer.data.items); - } - pub fn sendWorldEditPos(conn: *Connection, posType: WorldEditPosition, maybePos: ?Vec3i) void { var writer = utils.BinaryWriter.initCapacity(main.stackAllocator, 25); defer writer.deinit(); From 9c47bfd6b0b7ec8406eca8cba4d5fd0f48ff87e5 Mon Sep 17 00:00:00 2001 From: Mabeeck <84404489+Mabeeck@users.noreply.github.com> Date: Sun, 16 Aug 2026 18:49:48 +0200 Subject: [PATCH 07/12] Add help --- src/server/command/tp.zig | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/server/command/tp.zig b/src/server/command/tp.zig index a87858cd66..4acfba2a97 100644 --- a/src/server/command/tp.zig +++ b/src/server/command/tp.zig @@ -12,6 +12,8 @@ pub const usage = \\/tp @ \\/tp @ \\/tp @ @ + \\/tp + \\/tp @ ; pub const Args = union(enum) { From d71517308f7596485a9219321aa1d9090c101d16 Mon Sep 17 00:00:00 2001 From: Mabeeck <84404489+Mabeeck@users.noreply.github.com> Date: Mon, 17 Aug 2026 12:09:39 +0200 Subject: [PATCH 08/12] temp --- src/server/command/tp.zig | 3 ++- src/sync.zig | 26 +++++++++++++++----------- 2 files changed, 17 insertions(+), 12 deletions(-) diff --git a/src/server/command/tp.zig b/src/server/command/tp.zig index 4acfba2a97..1a233da441 100644 --- a/src/server/command/tp.zig +++ b/src/server/command/tp.zig @@ -110,7 +110,8 @@ pub fn execute(args: Args, source: Source) void { break :blk command.resolveCoordinates(pos.x, pos.y, pos.z, source) catch return; }, .@"/tp " => |pos| { - main.sync.server.executeCommand(.{.setRotation = .{.target = target.user.id, .rotation = command.resolveRotation(pos.yaw, pos.pitch, source) catch return}}, null); + //main.sync.server.executeCommand(.{.setRotation = .{.target = target.user.id, .rotation = command.resolveRotation(pos.yaw, pos.pitch, source) catch return}}, null); + main.sync.server.sendSyncOperation(.{.rotation = .{.target = source.user, .rotation = command.resolveRotation(pos.yaw, pos.pitch, source) catch return}}, null); break :blk command.resolveCoordinates(pos.x, pos.y, pos.z, source) catch return; }, inline .@"/tp ", .@"/tp " => |index| { diff --git a/src/sync.zig b/src/sync.zig index cf97b03188..da8fe3d490 100644 --- a/src/sync.zig +++ b/src/sync.zig @@ -139,6 +139,19 @@ pub const server = struct { // MARK: server threadContext = .other; } + pub fn sendSyncOperation(op: Command.SyncOperation, source: ?*main.server.User) void { + const syncData = op.serialize(main.stackAllocator); + defer main.stackAllocator.free(syncData); + + const users = op.getUsers(main.stackAllocator); + defer main.stackAllocator.free(users); + + for (users) |user| { + if (user == source and op.ignoreSource()) continue; + main.network.protocols.inventory.sendSyncOperation(user.conn, syncData); + } + } + pub fn executeCommand(payload: Command.Payload, source: ?*main.server.User) void { var command = Command{ .payload = payload, @@ -153,16 +166,7 @@ pub const server = struct { // MARK: server main.network.protocols.inventory.sendConfirmation(source.?.conn, confirmationData); } for (command.syncOperations.items) |op| { - const syncData = op.serialize(main.stackAllocator); - defer main.stackAllocator.free(syncData); - - const users = op.getUsers(main.stackAllocator); - defer main.stackAllocator.free(users); - - for (users) |user| { - if (user == source and op.ignoreSource()) continue; - main.network.protocols.inventory.sendSyncOperation(user.conn, syncData); - } + sendSyncOperation(op, source); } if (source != null and command.payload == .open) { // Send initial items for (command.payload.open.inv._items, 0..) |stack, slot| { @@ -847,7 +851,7 @@ pub const Command = struct { // MARK: Command .setRotation => |*info| { if (side == .server) { info.previous = info.target.?.player().rot; - std.log.debug("SetRotation executed on server; target=TRUNCATED, rotation={}", .{info.rotation}); + std.log.debug("SetRotation executed on server; target=TRUNCATED, rotation={}", .{info.rotation}); // MARK: Remove before merging info.target.?.player().rot = info.rotation; self.syncOperations.append(allocator, .{.rotation = .{ From 1a373bc8fb2f7950efcc09638df84b2565f73cdf Mon Sep 17 00:00:00 2001 From: Mabeeck <84404489+Mabeeck@users.noreply.github.com> Date: Mon, 17 Aug 2026 14:04:55 +0200 Subject: [PATCH 09/12] More work-in-progress --- src/server/command/tp.zig | 2 +- src/sync.zig | 45 --------------------------------------- 2 files changed, 1 insertion(+), 46 deletions(-) diff --git a/src/server/command/tp.zig b/src/server/command/tp.zig index 1a233da441..4d6b2d4492 100644 --- a/src/server/command/tp.zig +++ b/src/server/command/tp.zig @@ -111,7 +111,7 @@ pub fn execute(args: Args, source: Source) void { }, .@"/tp " => |pos| { //main.sync.server.executeCommand(.{.setRotation = .{.target = target.user.id, .rotation = command.resolveRotation(pos.yaw, pos.pitch, source) catch return}}, null); - main.sync.server.sendSyncOperation(.{.rotation = .{.target = source.user, .rotation = command.resolveRotation(pos.yaw, pos.pitch, source) catch return}}, null); + main.sync.server.sendSyncOperation(.{.rotation = .{.target = target.user, .rotation = command.resolveRotation(pos.yaw, pos.pitch, source) catch return}}, null); break :blk command.resolveCoordinates(pos.x, pos.y, pos.z, source) catch return; }, inline .@"/tp ", .@"/tp " => |index| { diff --git a/src/sync.zig b/src/sync.zig index da8fe3d490..f52c91e7d4 100644 --- a/src/sync.zig +++ b/src/sync.zig @@ -246,7 +246,6 @@ pub const Command = struct { // MARK: Command updateBlock = 9, addHealth = 10, chatCommand = 12, - setRotation = 18, }; pub const Payload = union(PayloadType) { open: Open, @@ -268,7 +267,6 @@ pub const Command = struct { // MARK: Command updateBlock: UpdateBlock, addHealth: AddHealth, chatCommand: ChatCommand, - setRotation: SetRotation, }; const BaseOperationType = enum(u8) { @@ -1807,49 +1805,6 @@ pub const Command = struct { // MARK: Command } }; - const SetRotation = struct { // MARK: SetRotation - target: main.entity.Entity, - rotation: Vec3f, - - fn run(self: SetRotation, ctx: Context) error{serverFailure}!void { - std.log.debug("SetRotation ran; target={}, rotation={}", .{self.target, self.rotation}); - 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(.{.setRotation = .{ - .target = target, - .rotation = self.rotation, - .previous = if (ctx.side == .server) target.?.player().rot else main.game.camera.rotation, - }}); - } - - fn serialize(self: SetRotation, writer: *BinaryWriter) void { - writer.writeEnum(main.entity.Entity, self.target); - writer.writeVec(Vec3f, self.rotation); - } - - fn deserialize(reader: *BinaryReader, _: Side, user: ?*main.server.User) !SetRotation { - const result: SetRotation = .{ - .target = try reader.readEnum(main.entity.Entity), - .rotation = try reader.readVec(Vec3f), - }; - if (user.?.id != result.target) return error.Invalid; - return result; - } - }; - const ChatCommand = struct { // MARK: ChatCommand message: []const u8, From 46daa748fccaf19b8dba04ebce5bea8bd480baec Mon Sep 17 00:00:00 2001 From: Mabeeck <84404489+Mabeeck@users.noreply.github.com> Date: Mon, 17 Aug 2026 14:39:03 +0200 Subject: [PATCH 10/12] Complete implementation --- src/server/command/tp.zig | 1 + 1 file changed, 1 insertion(+) diff --git a/src/server/command/tp.zig b/src/server/command/tp.zig index 4d6b2d4492..93d89135fb 100644 --- a/src/server/command/tp.zig +++ b/src/server/command/tp.zig @@ -48,6 +48,7 @@ pub fn execute(args: Args, source: Source) void { const target = switch (args) { inline .@"/tp ", .@"/tp ", + .@"/tp ", .@"/tp ", => |params| command.Target.fromPlayerIndex(params.sourcePlayerIndex, source) catch return, else => command.Target.fromPlayerIndex(null, source) catch return, From 6a37cd645569112774f5a04153762f7d80797117 Mon Sep 17 00:00:00 2001 From: Mabeeck <84404489+Mabeeck@users.noreply.github.com> Date: Mon, 17 Aug 2026 14:41:11 +0200 Subject: [PATCH 11/12] Remove setRotation BaseOperation --- src/sync.zig | 27 +-------------------------- 1 file changed, 1 insertion(+), 26 deletions(-) diff --git a/src/sync.zig b/src/sync.zig index f52c91e7d4..6058d19086 100644 --- a/src/sync.zig +++ b/src/sync.zig @@ -279,7 +279,6 @@ pub const Command = struct { // MARK: Command useDurability = 4, addHealth = 5, addEnergy = 6, - setRotation = 9, }; pub const BaseOperation = union(BaseOperationType) { @@ -329,11 +328,6 @@ pub const Command = struct { // MARK: Command energy: f32, previous: f32, }, - setRotation: struct { - target: ?*main.server.User, - rotation: Vec3f, - previous: Vec3f, - }, }; const SyncOperationType = enum(u8) { @@ -647,9 +641,6 @@ pub const Command = struct { // MARK: Command .addEnergy => |info| { main.game.Player.super.energy = info.previous; }, - .setRotation => |info| { - main.game.camera.rotation = info.previous; - }, } } } @@ -657,7 +648,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, .setRotation => {}, + .move, .swap, .create, .moveToBag, .takeFromBag, .addHealth, .addEnergy => {}, .delete => |info| { info.item.deinit(); }, @@ -846,22 +837,6 @@ 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); } }, - .setRotation => |*info| { - if (side == .server) { - info.previous = info.target.?.player().rot; - std.log.debug("SetRotation executed on server; target=TRUNCATED, rotation={}", .{info.rotation}); // MARK: Remove before merging - - info.target.?.player().rot = info.rotation; - self.syncOperations.append(allocator, .{.rotation = .{ - .target = info.target.?, - .rotation = info.rotation, - }}); - } else { - std.log.debug("SetRotation executed on client; target=TRUNCATED, rotation={}", .{info.rotation}); - info.previous = main.game.camera.rotation; - main.game.camera.rotation = info.rotation; - } - }, } self.baseOperations.append(allocator, op); } From 4f526065fc025be26560a4fcbcc947d42cd34e58 Mon Sep 17 00:00:00 2001 From: Mabeeck <84404489+Mabeeck@users.noreply.github.com> Date: Mon, 17 Aug 2026 14:41:41 +0200 Subject: [PATCH 12/12] Remove commented out statement --- src/server/command/tp.zig | 1 - 1 file changed, 1 deletion(-) diff --git a/src/server/command/tp.zig b/src/server/command/tp.zig index 93d89135fb..980a986eea 100644 --- a/src/server/command/tp.zig +++ b/src/server/command/tp.zig @@ -111,7 +111,6 @@ pub fn execute(args: Args, source: Source) void { break :blk command.resolveCoordinates(pos.x, pos.y, pos.z, source) catch return; }, .@"/tp " => |pos| { - //main.sync.server.executeCommand(.{.setRotation = .{.target = target.user.id, .rotation = command.resolveRotation(pos.yaw, pos.pitch, source) catch return}}, null); main.sync.server.sendSyncOperation(.{.rotation = .{.target = target.user, .rotation = command.resolveRotation(pos.yaw, pos.pitch, source) catch return}}, null); break :blk command.resolveCoordinates(pos.x, pos.y, pos.z, source) catch return; },