-
Notifications
You must be signed in to change notification settings - Fork 110
/
0026-Improve-container-checking-with-a-bitset.patch
477 lines (446 loc) · 19.4 KB
/
0026-Improve-container-checking-with-a-bitset.patch
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Paul Sauve <paul@technove.co>
Date: Wed, 19 May 2021 13:08:26 -0500
Subject: [PATCH] Improve container checking with a bitset
diff --git a/src/main/java/gg/airplane/structs/ItemListWithBitset.java b/src/main/java/gg/airplane/structs/ItemListWithBitset.java
new file mode 100644
index 0000000000000000000000000000000000000000..1b7a4ee47f4445d7f2ac91d3a73ae113edbdddb2
--- /dev/null
+++ b/src/main/java/gg/airplane/structs/ItemListWithBitset.java
@@ -0,0 +1,114 @@
+package gg.airplane.structs;
+
+import net.minecraft.core.NonNullList;
+import net.minecraft.world.item.ItemStack;
+import org.apache.commons.lang.Validate;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.AbstractList;
+import java.util.Arrays;
+import java.util.List;
+
+public class ItemListWithBitset extends AbstractList<ItemStack> {
+ public static ItemListWithBitset fromList(List<ItemStack> list) {
+ if (list instanceof ItemListWithBitset ours) {
+ return ours;
+ }
+ return new ItemListWithBitset(list);
+ }
+
+ private static ItemStack[] createArray(int size) {
+ ItemStack[] array = new ItemStack[size];
+ Arrays.fill(array, ItemStack.EMPTY);
+ return array;
+ }
+
+ private final ItemStack[] items;
+
+ private long bitSet = 0;
+ private final long allBits;
+
+ private static class OurNonNullList extends NonNullList<ItemStack> {
+ protected OurNonNullList(List<ItemStack> delegate) {
+ super(delegate, ItemStack.EMPTY);
+ }
+ }
+
+ public final NonNullList<ItemStack> nonNullList = new OurNonNullList(this);
+
+ private ItemListWithBitset(List<ItemStack> list) {
+ this(list.size());
+
+ for (int i = 0; i < list.size(); i++) {
+ this.set(i, list.get(i));
+ }
+ }
+
+ public ItemListWithBitset(int size) {
+ Validate.isTrue(size < Long.BYTES * 8, "size is too large");
+
+ this.items = createArray(size);
+ this.allBits = ((1L << size) - 1);
+ }
+
+ public boolean isCompletelyEmpty() {
+ return this.bitSet == 0;
+ }
+
+ public boolean hasFullStacks() {
+ return (this.bitSet & this.allBits) == allBits;
+ }
+
+ @Override
+ public ItemStack set(int index, @NotNull ItemStack itemStack) {
+ ItemStack existing = this.items[index];
+
+ this.items[index] = itemStack;
+
+ if (itemStack == ItemStack.EMPTY) {
+ this.bitSet &= ~(1L << index);
+ } else {
+ this.bitSet |= 1L << index;
+ }
+
+ return existing;
+ }
+
+ @NotNull
+ @Override
+ public ItemStack get(int var0) {
+ return this.items[var0];
+ }
+
+ @Override
+ public int size() {
+ return this.items.length;
+ }
+
+ @Override
+ public void clear() {
+ Arrays.fill(this.items, ItemStack.EMPTY);
+ }
+
+ // these are unsupported for block inventories which have a static size
+ @Override
+ public void add(int var0, ItemStack var1) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public ItemStack remove(int var0) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public String toString() {
+ return "ItemListWithBitset{" +
+ "items=" + Arrays.toString(items) +
+ ", bitSet=" + Long.toString(bitSet, 2) +
+ ", allBits=" + Long.toString(allBits, 2) +
+ ", size=" + this.items.length +
+ '}';
+ }
+}
diff --git a/src/main/java/net/minecraft/world/CompoundContainer.java b/src/main/java/net/minecraft/world/CompoundContainer.java
index 241fec02e6869c638d3a160819b32173a081467b..6a8f9e8f5bf108674c47018def28906e2d0a729c 100644
--- a/src/main/java/net/minecraft/world/CompoundContainer.java
+++ b/src/main/java/net/minecraft/world/CompoundContainer.java
@@ -1,5 +1,6 @@
package net.minecraft.world;
+import net.minecraft.core.Direction; // Pufferfish
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.ItemStack;
@@ -64,6 +65,23 @@ public class CompoundContainer implements Container {
this.container2 = second;
}
+ // Pufferfish start
+ @Override
+ public boolean hasEmptySlot(Direction enumdirection) {
+ return this.container1.hasEmptySlot(null) || this.container2.hasEmptySlot(null);
+ }
+
+ @Override
+ public boolean isCompletelyFull(Direction enumdirection) {
+ return this.container1.isCompletelyFull(null) && this.container2.isCompletelyFull(null);
+ }
+
+ @Override
+ public boolean isCompletelyEmpty(Direction enumdirection) {
+ return this.container1.isCompletelyEmpty(null) && this.container2.isCompletelyEmpty(null);
+ }
+ // Pufferfish end
+
@Override
public int getContainerSize() {
return this.container1.getContainerSize() + this.container2.getContainerSize();
diff --git a/src/main/java/net/minecraft/world/Container.java b/src/main/java/net/minecraft/world/Container.java
index d6cbe98e67fdbf8db46338a88ab1356dd63b50a3..20dd3a63b2f955b05a75eb240e33ae4cf6aef28f 100644
--- a/src/main/java/net/minecraft/world/Container.java
+++ b/src/main/java/net/minecraft/world/Container.java
@@ -3,6 +3,8 @@ package net.minecraft.world;
import java.util.Set;
import java.util.function.Predicate;
import net.minecraft.core.BlockPos;
+
+import net.minecraft.core.Direction; // Pufferfish
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.ItemStack;
@@ -14,6 +16,63 @@ import org.bukkit.craftbukkit.entity.CraftHumanEntity;
// CraftBukkit end
public interface Container extends Clearable {
+ // Pufferfish start - allow the inventory to override and optimize these frequent calls
+ default boolean hasEmptySlot(@org.jetbrains.annotations.Nullable Direction enumdirection) { // there is a slot with 0 items in it
+ if (this instanceof WorldlyContainer worldlyContainer) {
+ for (int i : worldlyContainer.getSlotsForFace(enumdirection)) {
+ if (this.getItem(i).isEmpty()) {
+ return true;
+ }
+ }
+ } else {
+ int size = this.getContainerSize();
+ for (int i = 0; i < size; i++) {
+ if (this.getItem(i).isEmpty()) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ default boolean isCompletelyFull(@org.jetbrains.annotations.Nullable Direction enumdirection) { // every stack is maxed
+ if (this instanceof WorldlyContainer worldlyContainer) {
+ for (int i : worldlyContainer.getSlotsForFace(enumdirection)) {
+ ItemStack itemStack = this.getItem(i);
+ if (itemStack.getCount() < itemStack.getMaxStackSize()) {
+ return false;
+ }
+ }
+ } else {
+ int size = this.getContainerSize();
+ for (int i = 0; i < size; i++) {
+ ItemStack itemStack = this.getItem(i);
+ if (itemStack.getCount() < itemStack.getMaxStackSize()) {
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+
+ default boolean isCompletelyEmpty(@org.jetbrains.annotations.Nullable Direction enumdirection) {
+ if (this instanceof WorldlyContainer worldlyContainer) {
+ for (int i : worldlyContainer.getSlotsForFace(enumdirection)) {
+ if (!this.getItem(i).isEmpty()) {
+ return false;
+ }
+ }
+ } else {
+ int size = this.getContainerSize();
+ for (int i = 0; i < size; i++) {
+ if (!this.getItem(i).isEmpty()) {
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+ // Pufferfish end
int LARGE_MAX_STACK_SIZE = 64;
int DEFAULT_DISTANCE_LIMIT = 8;
diff --git a/src/main/java/net/minecraft/world/entity/vehicle/AbstractMinecartContainer.java b/src/main/java/net/minecraft/world/entity/vehicle/AbstractMinecartContainer.java
index 756d0434472921992c9d84597d7c9c824e93614c..38c573d440946ca7ee6016ef92e9c1605031e611 100644
--- a/src/main/java/net/minecraft/world/entity/vehicle/AbstractMinecartContainer.java
+++ b/src/main/java/net/minecraft/world/entity/vehicle/AbstractMinecartContainer.java
@@ -28,7 +28,10 @@ import org.bukkit.inventory.InventoryHolder;
public abstract class AbstractMinecartContainer extends AbstractMinecart implements ContainerEntity {
+ // Pufferfish start
private NonNullList<ItemStack> itemStacks;
+ private gg.airplane.structs.ItemListWithBitset itemStacksOptimized;
+ // Pufferfish end
@Nullable
public ResourceLocation lootTable;
public long lootTableSeed;
@@ -90,12 +93,18 @@ public abstract class AbstractMinecartContainer extends AbstractMinecart impleme
protected AbstractMinecartContainer(EntityType<?> type, Level world) {
super(type, world);
- this.itemStacks = NonNullList.withSize(this.getContainerSize(), ItemStack.EMPTY); // CraftBukkit - SPIGOT-3513
+ // Pufferfish start
+ this.itemStacksOptimized = new gg.airplane.structs.ItemListWithBitset(this.getContainerSize()); // CraftBukkit - SPIGOT-3513
+ this.itemStacks = this.itemStacksOptimized.nonNullList;
+ // Pufferfish end
}
protected AbstractMinecartContainer(EntityType<?> type, double x, double y, double z, Level world) {
super(type, world, x, y, z);
- this.itemStacks = NonNullList.withSize(this.getContainerSize(), ItemStack.EMPTY); // CraftBukkit - SPIGOT-3513
+ // Pufferfish start
+ this.itemStacksOptimized = new gg.airplane.structs.ItemListWithBitset(this.getContainerSize()); // CraftBukkit - SPIGOT-3513
+ this.itemStacks = this.itemStacksOptimized.nonNullList;
+ // Pufferfish end
}
@Override
@@ -164,6 +173,10 @@ public abstract class AbstractMinecartContainer extends AbstractMinecart impleme
protected void readAdditionalSaveData(CompoundTag nbt) {
super.readAdditionalSaveData(nbt);
this.lootableData.loadNbt(nbt); // Paper
+ // Pufferfish start
+ this.itemStacksOptimized = new gg.airplane.structs.ItemListWithBitset(this.getContainerSize());
+ this.itemStacks = this.itemStacksOptimized.nonNullList;
+ // Pufferfish end
this.readChestVehicleSaveData(nbt);
}
diff --git a/src/main/java/net/minecraft/world/level/block/entity/ChestBlockEntity.java b/src/main/java/net/minecraft/world/level/block/entity/ChestBlockEntity.java
index 9b1243d96e0694c62fc9e82e9be540bce0d2b3ad..3514022d898a24052c917ebf55dcef3e757d6836 100644
--- a/src/main/java/net/minecraft/world/level/block/entity/ChestBlockEntity.java
+++ b/src/main/java/net/minecraft/world/level/block/entity/ChestBlockEntity.java
@@ -31,7 +31,10 @@ import org.bukkit.entity.HumanEntity;
public class ChestBlockEntity extends RandomizableContainerBlockEntity implements LidBlockEntity {
private static final int EVENT_SET_OPEN_COUNT = 1;
+ // Pufferfish start
private NonNullList<ItemStack> items;
+ private gg.airplane.structs.ItemListWithBitset optimizedItems;
+ // Pufferfish end
public final ContainerOpenersCounter openersCounter;
private final ChestLidController chestLidController;
@@ -65,9 +68,13 @@ public class ChestBlockEntity extends RandomizableContainerBlockEntity implement
}
// CraftBukkit end
+ private final boolean isNative = getClass().equals(ChestBlockEntity.class); // Pufferfish
protected ChestBlockEntity(BlockEntityType<?> type, BlockPos pos, BlockState state) {
super(type, pos, state);
- this.items = NonNullList.withSize(27, ItemStack.EMPTY);
+ // Pufferfish start
+ this.optimizedItems = new gg.airplane.structs.ItemListWithBitset(27);
+ this.items = this.optimizedItems.nonNullList;
+ // Pufferfish end
this.openersCounter = new ContainerOpenersCounter() {
@Override
protected void onOpen(Level world, BlockPos pos, BlockState state) {
@@ -98,6 +105,23 @@ public class ChestBlockEntity extends RandomizableContainerBlockEntity implement
this.chestLidController = new ChestLidController();
}
+ // Pufferfish start
+ @Override
+ public boolean hasEmptySlot(Direction enumdirection) {
+ return isNative ? !this.optimizedItems.hasFullStacks() : super.hasEmptySlot(enumdirection);
+ }
+
+ @Override
+ public boolean isCompletelyFull(Direction enumdirection) {
+ return isNative ? this.optimizedItems.hasFullStacks() && super.isCompletelyFull(enumdirection) : super.isCompletelyFull(enumdirection);
+ }
+
+ @Override
+ public boolean isCompletelyEmpty(Direction enumdirection) {
+ return isNative && this.optimizedItems.isCompletelyEmpty() || super.isCompletelyEmpty(enumdirection);
+ }
+ // Pufferfish end
+
public ChestBlockEntity(BlockPos pos, BlockState state) {
this(BlockEntityType.CHEST, pos, state);
}
@@ -115,7 +139,10 @@ public class ChestBlockEntity extends RandomizableContainerBlockEntity implement
@Override
public void load(CompoundTag nbt) {
super.load(nbt);
- this.items = NonNullList.withSize(this.getContainerSize(), ItemStack.EMPTY);
+ // Pufferfish start
+ this.optimizedItems = new gg.airplane.structs.ItemListWithBitset(this.getContainerSize());
+ this.items = this.optimizedItems.nonNullList;
+ // Pufferfish end
if (!this.tryLoadLootTable(nbt)) {
ContainerHelper.loadAllItems(nbt, this.items);
}
@@ -187,7 +214,10 @@ public class ChestBlockEntity extends RandomizableContainerBlockEntity implement
@Override
protected void setItems(NonNullList<ItemStack> list) {
- this.items = list;
+ // Pufferfish start
+ this.optimizedItems = gg.airplane.structs.ItemListWithBitset.fromList(list);
+ this.items = this.optimizedItems.nonNullList;
+ // Pufferfish end
}
@Override
diff --git a/src/main/java/net/minecraft/world/level/block/entity/HopperBlockEntity.java b/src/main/java/net/minecraft/world/level/block/entity/HopperBlockEntity.java
index cdb739df2a285032d25d84f4464f202a7a3fa578..6b9cd8543a5bfc2b936ba18f66ffd60f2f792e43 100644
--- a/src/main/java/net/minecraft/world/level/block/entity/HopperBlockEntity.java
+++ b/src/main/java/net/minecraft/world/level/block/entity/HopperBlockEntity.java
@@ -48,7 +48,10 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen
public static final int MOVE_ITEM_SPEED = 8;
public static final int HOPPER_CONTAINER_SIZE = 5;
+ // Pufferfish start
private NonNullList<ItemStack> items;
+ private gg.airplane.structs.ItemListWithBitset optimizedItems; // Pufferfish
+ // Pufferfish end
public int cooldownTime;
private long tickedGameTime;
@@ -84,14 +87,37 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen
public HopperBlockEntity(BlockPos pos, BlockState state) {
super(BlockEntityType.HOPPER, pos, state);
- this.items = NonNullList.withSize(5, ItemStack.EMPTY);
+ // Pufferfish start
+ this.optimizedItems = new gg.airplane.structs.ItemListWithBitset(5);
+ this.items = this.optimizedItems.nonNullList;
+ // Pufferfish end
this.cooldownTime = -1;
}
+ // Pufferfish start
+ @Override
+ public boolean hasEmptySlot(Direction enumdirection) {
+ return !this.optimizedItems.hasFullStacks();
+ }
+
+ @Override
+ public boolean isCompletelyFull(Direction enumdirection) {
+ return this.optimizedItems.hasFullStacks() && super.isCompletelyFull(enumdirection);
+ }
+
+ @Override
+ public boolean isCompletelyEmpty(Direction enumdirection) {
+ return this.optimizedItems.isCompletelyEmpty() || super.isCompletelyEmpty(enumdirection);
+ }
+ // Pufferfish end
+
@Override
public void load(CompoundTag nbt) {
super.load(nbt);
- this.items = NonNullList.withSize(this.getContainerSize(), ItemStack.EMPTY);
+ // Pufferfish start
+ this.optimizedItems = new gg.airplane.structs.ItemListWithBitset(this.getContainerSize());
+ this.items = this.optimizedItems.nonNullList;
+ // Pufferfish end
if (!this.tryLoadLootTable(nbt)) {
ContainerHelper.loadAllItems(nbt, this.items);
}
@@ -492,6 +518,7 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen
}
private static boolean isFullContainer(Container inventory, Direction direction) {
+ if (true) return inventory.isCompletelyFull(direction); // Pufferfish - use bitsets
// Paper start - Perf: Optimize Hoppers
if (inventory instanceof WorldlyContainer worldlyContainer) {
for (final int slot : worldlyContainer.getSlotsForFace(direction)) {
@@ -514,7 +541,10 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen
}
private static boolean isEmptyContainer(Container inv, Direction facing) {
- return allMatch(inv, facing, IS_EMPTY_TEST); // Paper - Perf: Optimize Hoppers
+ // Pufferfish start - use bitsets
+ //return allMatch(inv, facing, IS_EMPTY_TEST); // Paper - Perf: Optimize Hoppers
+ return inv.isCompletelyEmpty(facing);
+ // Pufferfish end
}
public static boolean suckInItems(Level world, Hopper hopper) {
@@ -714,7 +744,7 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen
if (HopperBlockEntity.canPlaceItemInContainer(to, stack, slot, side)) {
boolean flag = false;
- boolean flag1 = to.isEmpty();
+ boolean flag1 = to.isCompletelyEmpty(side); // Pufferfish
if (itemstack1.isEmpty()) {
// Spigot start - SPIGOT-6693, InventorySubcontainer#setItem
@@ -909,7 +939,10 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen
@Override
protected void setItems(NonNullList<ItemStack> list) {
- this.items = list;
+ // Pufferfish start
+ this.optimizedItems = gg.airplane.structs.ItemListWithBitset.fromList(list);
+ this.items = this.optimizedItems.nonNullList;
+ // Pufferfish end
}
public static void entityInside(Level world, BlockPos pos, BlockState state, Entity entity, HopperBlockEntity blockEntity) {
diff --git a/src/main/java/net/minecraft/world/level/block/entity/RandomizableContainerBlockEntity.java b/src/main/java/net/minecraft/world/level/block/entity/RandomizableContainerBlockEntity.java
index dfd1246b735fe64c5beae83567a013861eb00822..fa64bf5ad13c278438039b663ea3134e72108411 100644
--- a/src/main/java/net/minecraft/world/level/block/entity/RandomizableContainerBlockEntity.java
+++ b/src/main/java/net/minecraft/world/level/block/entity/RandomizableContainerBlockEntity.java
@@ -94,12 +94,7 @@ public abstract class RandomizableContainerBlockEntity extends BaseContainerBloc
public boolean isEmpty() {
this.unpackLootTable(null);
// Paper start - Perf: Optimize Hoppers
- for (final ItemStack itemStack : this.getItems()) {
- if (!itemStack.isEmpty()) {
- return false;
- }
- }
- return true;
+ return this.isCompletelyEmpty(null); // Pufferfish - use super
// Paper end - Perf: Optimize Hoppers
}