Implement some basic lossy compression for entity and item network data

progress towards #1327
This commit is contained in:
IntegratedQuantum 2025-04-26 15:59:59 +02:00
parent 6fc7e604e7
commit 29f06de52f
4 changed files with 127 additions and 54 deletions

View File

@ -18,6 +18,13 @@ const NeverFailingAllocator = main.heap.NeverFailingAllocator;
const BinaryReader = main.utils.BinaryReader;
pub const EntityNetworkData = struct {
id: u32,
pos: Vec3d,
vel: Vec3d,
rot: Vec3f,
};
pub const ClientEntity = struct {
interpolatedValues: utils.GenericInterpolation(6) = undefined,
_interpolationPos: [6]f64 = undefined,
@ -228,31 +235,30 @@ pub const ClientEntityManager = struct {
}
}
pub fn serverUpdate(time: i16, reader: *BinaryReader) !void {
pub fn serverUpdate(time: i16, entityData: []EntityNetworkData) void {
mutex.lock();
defer mutex.unlock();
timeDifference.addDataPoint(time);
while(reader.remaining.len != 0) {
const id = try reader.readInt(u32);
for(entityData) |data| {
const pos = [_]f64{
try reader.readFloat(f64),
try reader.readFloat(f64),
try reader.readFloat(f64),
@floatCast(try reader.readFloat(f32)),
@floatCast(try reader.readFloat(f32)),
@floatCast(try reader.readFloat(f32)),
data.pos[0],
data.pos[1],
data.pos[2],
@floatCast(data.rot[0]),
@floatCast(data.rot[1]),
@floatCast(data.rot[2]),
};
const vel = [_]f64{
try reader.readFloat(f64),
try reader.readFloat(f64),
try reader.readFloat(f64),
data.vel[0],
data.vel[1],
data.vel[2],
0,
0,
0,
};
for(entities.items()) |*ent| {
if(ent.id == id) {
if(ent.id == data.id) {
ent.updatePosition(&pos, &vel, time);
break;
}

View File

@ -35,6 +35,12 @@ const ItemDrop = struct { // MARK: ItemDrop
reverseIndex: u16,
};
pub const ItemDropNetworkData = struct {
index: u16,
pos: Vec3d,
vel: Vec3d,
};
pub const ItemDropManager = struct { // MARK: ItemDropManager
/// Half the side length of all item entities hitboxes as a cube.
pub const radius: f64 = 0.1;
@ -116,14 +122,16 @@ pub const ItemDropManager = struct { // MARK: ItemDropManager
}
}
pub fn getPositionAndVelocityData(self: *ItemDropManager, allocator: NeverFailingAllocator) []u8 {
var writer = utils.BinaryWriter.initCapacity(allocator, self.size*50);
for(self.indices[0..self.size]) |i| {
writer.writeInt(u16, i);
writer.writeVec(Vec3d, self.list.items(.pos)[i]);
writer.writeVec(Vec3d, self.list.items(.vel)[i]);
pub fn getPositionAndVelocityData(self: *ItemDropManager, allocator: NeverFailingAllocator) []ItemDropNetworkData {
const result = allocator.alloc(ItemDropNetworkData, self.size);
for(self.indices[0..self.size], result) |i, *res| {
res.* = .{
.index = i,
.pos = self.list.items(.pos)[i],
.vel = self.list.items(.vel)[i],
};
}
return writer.data.toOwnedSlice();
return result;
}
pub fn getInitialList(self: *ItemDropManager, allocator: NeverFailingAllocator) ZonElement {
@ -458,14 +466,13 @@ pub const ClientItemDropManager = struct { // MARK: ClientItemDropManager
self.super.deinit();
}
pub fn readPosition(self: *ClientItemDropManager, reader: *BinaryReader, time: i16) !void {
pub fn readPosition(self: *ClientItemDropManager, time: i16, itemData: []ItemDropNetworkData) void {
self.timeDifference.addDataPoint(time);
var pos: [ItemDropManager.maxCapacity]Vec3d = undefined;
var vel: [ItemDropManager.maxCapacity]Vec3d = undefined;
while(reader.remaining.len != 0) {
const i = try reader.readInt(u16);
pos[i] = try reader.readVec(Vec3d);
vel[i] = try reader.readVec(Vec3d);
for(itemData) |data| {
pos[data.index] = data.pos;
vel[data.index] = data.vel;
}
mutex.lock();
defer mutex.unlock();

View File

@ -850,35 +850,93 @@ pub const Protocols = struct {
pub const asynchronous = false;
const type_entity: u8 = 0;
const type_item: u8 = 1;
const Type = enum(u8) {
noVelocityEntity = 0,
f16VelocityEntity = 1,
f32VelocityEntity = 2,
noVelocityItem = 3,
f16VelocityItem = 4,
f32VelocityItem = 5,
};
fn receive(conn: *Connection, reader: *utils.BinaryReader) !void {
if(conn.isServerSide()) return error.InvalidSide;
if(conn.manager.world) |world| {
const typ = try reader.readInt(u8);
const time = try reader.readInt(i16);
if(typ == type_entity) {
try main.entity.ClientEntityManager.serverUpdate(time, reader);
} else if(typ == type_item) {
try world.itemDrops.readPosition(reader, time);
const playerPos = try reader.readVec(Vec3d);
var entityData: main.List(main.entity.EntityNetworkData) = .init(main.stackAllocator);
defer entityData.deinit();
var itemData: main.List(main.itemdrop.ItemDropNetworkData) = .init(main.stackAllocator);
defer itemData.deinit();
while(reader.remaining.len != 0) {
const typ = try reader.readEnum(Type);
switch(typ) {
.noVelocityEntity, .f16VelocityEntity, .f32VelocityEntity => {
entityData.append(.{
.vel = switch(typ) {
.noVelocityEntity => @splat(0),
.f16VelocityEntity => @floatCast(try reader.readVec(@Vector(3, f16))),
.f32VelocityEntity => @floatCast(try reader.readVec(@Vector(3, f32))),
else => unreachable,
},
.id = try reader.readInt(u32),
.pos = playerPos + try reader.readVec(Vec3f),
.rot = try reader.readVec(Vec3f),
});
},
.noVelocityItem, .f16VelocityItem, .f32VelocityItem => {
itemData.append(.{
.vel = switch(typ) {
.noVelocityItem => @splat(0),
.f16VelocityItem => @floatCast(try reader.readVec(@Vector(3, f16))),
.f32VelocityItem => @floatCast(try reader.readVec(Vec3f)),
else => unreachable,
},
.index = try reader.readInt(u16),
.pos = playerPos + try reader.readVec(Vec3f),
});
},
}
}
main.entity.ClientEntityManager.serverUpdate(time, entityData.items);
world.itemDrops.readPosition(time, itemData.items);
}
}
pub fn send(conn: *Connection, entityData: []const u8, itemData: []const u8) void {
if(entityData.len != 0) {
var writer = utils.BinaryWriter.initCapacity(main.stackAllocator, entityData.len + 3);
defer writer.deinit();
writer.writeInt(u8, type_entity);
writer.writeInt(i16, @truncate(std.time.milliTimestamp()));
writer.writeSlice(entityData);
conn.send(.lossy, id, writer.data.items);
}
pub fn send(conn: *Connection, playerPos: Vec3d, entityData: []main.entity.EntityNetworkData, itemData: []main.itemdrop.ItemDropNetworkData) void {
var writer = utils.BinaryWriter.init(main.stackAllocator);
defer writer.deinit();
if(itemData.len != 0) {
var writer = utils.BinaryWriter.initCapacity(main.stackAllocator, itemData.len + 3);
defer writer.deinit();
writer.writeInt(u8, type_item);
writer.writeInt(i16, @truncate(std.time.milliTimestamp()));
writer.writeSlice(itemData);
conn.send(.lossy, id, writer.data.items);
writer.writeInt(i16, @truncate(std.time.milliTimestamp()));
writer.writeVec(Vec3d, playerPos);
for(entityData) |data| {
const velocityMagnitudeSqr = vec.lengthSquare(data.vel);
if(velocityMagnitudeSqr < 1e-6*1e-6) {
writer.writeEnum(Type, .noVelocityEntity);
} else if(velocityMagnitudeSqr > 1000*1000) {
writer.writeEnum(Type, .f32VelocityEntity);
writer.writeVec(Vec3f, @floatCast(data.vel));
} else {
writer.writeEnum(Type, .f16VelocityEntity);
writer.writeVec(@Vector(3, f16), @floatCast(data.vel));
}
writer.writeInt(u32, data.id);
writer.writeVec(Vec3f, @floatCast(data.pos - playerPos));
writer.writeVec(Vec3f, data.rot);
}
for(itemData) |data| {
const velocityMagnitudeSqr = vec.lengthSquare(data.vel);
if(velocityMagnitudeSqr < 1e-6*1e-6) {
writer.writeEnum(Type, .noVelocityItem);
} else if(velocityMagnitudeSqr > 1000*1000) {
writer.writeEnum(Type, .f32VelocityItem);
writer.writeVec(Vec3f, @floatCast(data.vel));
} else {
writer.writeEnum(Type, .f16VelocityItem);
writer.writeVec(@Vector(3, f16), @floatCast(data.vel));
}
writer.writeInt(u16, data.index);
writer.writeVec(Vec3f, @floatCast(data.pos - playerPos));
}
conn.send(.lossy, id, writer.data.items);
}
};
pub const blockUpdate = struct {

View File

@ -387,21 +387,23 @@ fn update() void { // MARK: update()
}
// Send the entity data:
var writer = BinaryWriter.initCapacity(main.stackAllocator, (4 + 24 + 12 + 24)*userList.len);
defer writer.deinit();
const itemData = world.?.itemDropManager.getPositionAndVelocityData(main.stackAllocator);
defer main.stackAllocator.free(itemData);
var entityData: main.List(main.entity.EntityNetworkData) = .init(main.stackAllocator);
defer entityData.deinit();
for(userList) |user| {
const id = user.id; // TODO
writer.writeInt(u32, id);
writer.writeVec(Vec3d, user.player.pos);
writer.writeVec(Vec3f, user.player.rot);
writer.writeVec(Vec3d, user.player.vel);
entityData.append(.{
.id = id,
.pos = user.player.pos,
.vel = user.player.vel,
.rot = user.player.rot,
});
}
for(userList) |user| {
main.network.Protocols.entityPosition.send(user.conn, writer.data.items, itemData);
main.network.Protocols.entityPosition.send(user.conn, user.player.pos, entityData.items, itemData);
}
while(userDeinitList.dequeue()) |user| {