Reduce latency of itemdrops by sending item drop updates instantly instead of waiting for the next server tick.

progress towards #868
This commit is contained in:
IntegratedQuantum 2025-01-03 23:38:23 +01:00
parent 2202cd01da
commit 850956b0af
2 changed files with 80 additions and 66 deletions

View File

@ -62,13 +62,10 @@ pub const ItemDropManager = struct { // MARK: ItemDropManager
size: u32 = 0,
lastUpdates: ZonElement,
pub fn init(self: *ItemDropManager, allocator: NeverFailingAllocator, world: ?*ServerWorld, gravity: f64) void {
self.* = ItemDropManager {
.allocator = allocator,
.list = std.MultiArrayList(ItemDrop){},
.lastUpdates = ZonElement.initArray(allocator),
.isEmpty = .initFull(),
.changeQueue = .init(allocator, 16),
.world = world,
@ -87,7 +84,6 @@ pub const ItemDropManager = struct { // MARK: ItemDropManager
}
}
self.list.deinit(self.allocator.allocator);
self.lastUpdates.deinit(self.allocator);
}
pub fn loadFrom(self: *ItemDropManager, zon: ZonElement) void {
@ -136,18 +132,18 @@ pub const ItemDropManager = struct { // MARK: ItemDropManager
}
pub fn getInitialList(self: *ItemDropManager, allocator: NeverFailingAllocator) ZonElement {
self.processChanges(); // Make sure all the items from the queue are included.
var list = ZonElement.initArray(allocator);
var ii: u32 = 0;
while(ii < self.size) : (ii += 1) {
const i = self.indices[ii];
list.array.append(self.storeSingle(self.lastUpdates.array.allocator, i));
list.array.append(self.storeSingle(allocator, i));
}
return list;
}
fn storeSingle(self: *ItemDropManager, allocator: NeverFailingAllocator, i: u16) ZonElement {
fn storeDrop(allocator: NeverFailingAllocator, itemDrop: ItemDrop, i: u16) ZonElement {
const obj = ZonElement.initObject(allocator);
const itemDrop = self.list.get(i);
obj.put("i", i);
obj.put("pos", itemDrop.pos);
obj.put("vel", itemDrop.vel);
@ -156,6 +152,10 @@ pub const ItemDropManager = struct { // MARK: ItemDropManager
return obj;
}
fn storeSingle(self: *ItemDropManager, allocator: NeverFailingAllocator, i: u16) ZonElement {
return storeDrop(allocator, self.list.get(i), i);
}
pub fn store(self: *ItemDropManager, allocator: NeverFailingAllocator) ZonElement {
const zonArray = ZonElement.initArray(allocator);
for(self.indices[0..self.size]) |i| {
@ -187,10 +187,7 @@ pub const ItemDropManager = struct { // MARK: ItemDropManager
pickupCooldown[i] -= 1;
despawnTime[i] -= 1;
if(despawnTime[i] < 0) {
self.emptyMutex.lock();
self.isEmpty.set(i);
self.emptyMutex.unlock();
self.internalRemove(i);
self.directRemove(i);
} else {
ii += 1;
}
@ -212,8 +209,7 @@ pub const ItemDropManager = struct { // MARK: ItemDropManager
return;
});
self.isEmpty.unset(i);
self.emptyMutex.unlock();
self.changeQueue.enqueue(.{.add = .{i, .{
const drop = ItemDrop {
.pos = pos,
.vel = vel,
.rot = rot,
@ -221,15 +217,32 @@ pub const ItemDropManager = struct { // MARK: ItemDropManager
.despawnTime = despawnTime,
.pickupCooldown = pickupCooldown,
.reverseIndex = undefined,
}}});
};
if(self.world != null) {
const list = ZonElement.initArray(main.stackAllocator);
defer list.deinit(main.stackAllocator);
list.array.append(.null);
list.array.append(storeDrop(main.stackAllocator, drop, i));
const updateData = list.toStringEfficient(main.stackAllocator, &.{});
defer main.stackAllocator.free(updateData);
const userList = main.server.getUserListAndIncreaseRefCount(main.stackAllocator);
defer main.server.freeUserListAndDecreaseRefCount(main.stackAllocator, userList);
for(userList) |user| {
main.network.Protocols.entity.send(user.conn, updateData);
}
}
self.emptyMutex.unlock();
self.changeQueue.enqueue(.{.add = .{i, drop}});
}
fn addWithIndex(self: *ItemDropManager, i: u16, pos: Vec3d, vel: Vec3d, rot: Vec3f, itemStack: ItemStack, despawnTime: i32, pickupCooldown: i32) void {
self.emptyMutex.lock();
std.debug.assert(self.isEmpty.isSet(i));
self.isEmpty.unset(i);
self.emptyMutex.unlock();
self.changeQueue.enqueue(.{.add = .{i, .{
const drop = ItemDrop {
.pos = pos,
.vel = vel,
.rot = rot,
@ -237,7 +250,24 @@ pub const ItemDropManager = struct { // MARK: ItemDropManager
.despawnTime = despawnTime,
.pickupCooldown = pickupCooldown,
.reverseIndex = undefined,
}}});
};
if(self.world != null) {
const list = ZonElement.initArray(main.stackAllocator);
defer list.deinit(main.stackAllocator);
list.array.append(.null);
list.array.append(storeDrop(main.stackAllocator, drop, i));
const updateData = list.toStringEfficient(main.stackAllocator, &.{});
defer main.stackAllocator.free(updateData);
const userList = main.server.getUserListAndIncreaseRefCount(main.stackAllocator);
defer main.server.freeUserListAndDecreaseRefCount(main.stackAllocator, userList);
for(userList) |user| {
main.network.Protocols.entity.send(user.conn, updateData);
}
}
self.emptyMutex.unlock();
self.changeQueue.enqueue(.{.add = .{i, drop}});
}
fn processChanges(self: *ItemDropManager) void {
@ -260,9 +290,6 @@ pub const ItemDropManager = struct { // MARK: ItemDropManager
}
drop.reverseIndex = @intCast(self.size);
self.list.set(i, drop);
if(self.world != null) {
self.lastUpdates.array.append(self.storeSingle(self.lastUpdates.array.allocator, i));
}
self.indices[self.size] = i;
self.size += 1;
}
@ -273,9 +300,28 @@ pub const ItemDropManager = struct { // MARK: ItemDropManager
self.list.items(.itemStack)[i].clear();
self.indices[ii] = self.indices[self.size];
self.list.items(.reverseIndex)[self.indices[self.size]] = ii;
if(self.world != null) {
self.lastUpdates.array.append(.{.int = i});
}
fn directRemove(self: *ItemDropManager, i: u16) void {
std.debug.assert(self.world != null);
self.emptyMutex.lock();
self.isEmpty.set(i);
const list = ZonElement.initArray(main.stackAllocator);
defer list.deinit(main.stackAllocator);
list.array.append(.null);
list.array.append(.{.int = i});
const updateData = list.toStringEfficient(main.stackAllocator, &.{});
defer main.stackAllocator.free(updateData);
const userList = main.server.getUserListAndIncreaseRefCount(main.stackAllocator);
defer main.server.freeUserListAndDecreaseRefCount(main.stackAllocator, userList);
for(userList) |user| {
main.network.Protocols.entity.send(user.conn, updateData);
}
self.emptyMutex.unlock();
self.internalRemove(i);
}
fn updateEnt(self: *ItemDropManager, chunk: *ServerChunk, pos: *Vec3d, vel: *Vec3d, deltaTime: f64) void {
@ -374,10 +420,7 @@ pub const ItemDropManager = struct { // MARK: ItemDropManager
const itemStack = &self.list.items(.itemStack)[i];
main.items.Inventory.Sync.ServerSide.tryCollectingToPlayerInventory(user, itemStack);
if(itemStack.amount == 0) {
self.emptyMutex.lock();
self.isEmpty.set(i);
self.emptyMutex.unlock();
self.internalRemove(i);
self.directRemove(i);
continue;
}
}

View File

@ -296,27 +296,9 @@ pub fn freeUserListAndDecreaseRefCount(allocator: utils.NeverFailingAllocator, l
allocator.free(list);
}
fn sendEntityUpdates(comptime getInitialList: bool, allocator: utils.NeverFailingAllocator) if(getInitialList) []const u8 else void {
fn getInitialEntityList(allocator: utils.NeverFailingAllocator) []const u8 {
// Send the entity updates:
const updateList = main.ZonElement.initArray(main.stackAllocator);
defer updateList.deinit(main.stackAllocator);
defer updateList.array.clearAndFree(); // The children are freed in other locations.
if(world.?.itemDropManager.lastUpdates.array.items.len != 0) {
updateList.array.append(.null);
updateList.array.appendSlice(world.?.itemDropManager.lastUpdates.array.items);
}
if(!getInitialList and updateList.array.items.len == 0) {
return;
}
const updateData = updateList.toStringEfficient(main.stackAllocator, &.{});
defer main.stackAllocator.free(updateData);
if(world.?.itemDropManager.lastUpdates.array.items.len != 0) {
const alloc = world.?.itemDropManager.lastUpdates.array.allocator;
world.?.itemDropManager.lastUpdates.deinit(alloc);
world.?.itemDropManager.lastUpdates = main.ZonElement.initArray(alloc);
}
var initialList: []const u8 = undefined;
if(getInitialList) {
const list = main.ZonElement.initArray(main.stackAllocator);
defer list.deinit(main.stackAllocator);
list.array.append(.null);
@ -325,15 +307,7 @@ fn sendEntityUpdates(comptime getInitialList: bool, allocator: utils.NeverFailin
itemDropList.array.items.len = 0;
itemDropList.deinit(main.stackAllocator);
initialList = list.toStringEfficient(allocator, &.{});
}
const userList = getUserListAndIncreaseRefCount(main.stackAllocator);
defer freeUserListAndDecreaseRefCount(main.stackAllocator, userList);
for(userList) |user| {
main.network.Protocols.entity.send(user.conn, updateData);
}
if(getInitialList) {
return initialList;
}
}
fn update() void { // MARK: update()
@ -349,9 +323,6 @@ fn update() void { // MARK: update()
user.update();
}
sendEntityUpdates(false, main.stackAllocator);
// Send the entity data:
const data = main.stackAllocator.alloc(u8, (4 + 24 + 12 + 24)*userList.len);
defer main.stackAllocator.free(data);
@ -482,7 +453,7 @@ pub fn connectInternal(user: *User) void {
defer main.stackAllocator.free(data);
if(user.connected.load(.unordered)) main.network.Protocols.entity.send(user.conn, data);
}
const initialList = sendEntityUpdates(true, main.stackAllocator);
const initialList = getInitialEntityList(main.stackAllocator);
main.network.Protocols.entity.send(user.conn, initialList);
main.stackAllocator.free(initialList);
const message = std.fmt.allocPrint(main.stackAllocator.allocator, "{s}§#ffff00 joined", .{user.name}) catch unreachable;