Skip to content

Commit

Permalink
Reduce latency of itemdrops by sending item drop updates instantly in…
Browse files Browse the repository at this point in the history
…stead of waiting for the next server tick.

progress towards #868
  • Loading branch information
IntegratedQuantum committed Jan 3, 2025
1 parent 2202cd0 commit 850956b
Show file tree
Hide file tree
Showing 2 changed files with 80 additions and 66 deletions.
95 changes: 69 additions & 26 deletions src/itemdrop.zig
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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 {
Expand Down Expand Up @@ -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);
Expand All @@ -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| {
Expand Down Expand Up @@ -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;
}
Expand All @@ -212,32 +209,65 @@ 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,
.itemStack = itemStack,
.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,
.itemStack = itemStack,
.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 {
Expand All @@ -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;
}
Expand All @@ -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 {
Expand Down Expand Up @@ -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;
}
}
Expand Down
51 changes: 11 additions & 40 deletions src/server/server.zig
Original file line number Diff line number Diff line change
Expand Up @@ -296,44 +296,18 @@ 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);
const itemDropList = world.?.itemDropManager.getInitialList(main.stackAllocator);
list.array.appendSlice(itemDropList.array.items);
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;
}
const list = main.ZonElement.initArray(main.stackAllocator);
defer list.deinit(main.stackAllocator);
list.array.append(.null);
const itemDropList = world.?.itemDropManager.getInitialList(main.stackAllocator);
list.array.appendSlice(itemDropList.array.items);
itemDropList.array.items.len = 0;
itemDropList.deinit(main.stackAllocator);
initialList = list.toStringEfficient(allocator, &.{});
return initialList;
}

fn update() void { // MARK: update()
Expand All @@ -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);
Expand Down Expand Up @@ -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;
Expand Down

0 comments on commit 850956b

Please sign in to comment.