Mirror von
https://github.com/PaperMC/Paper.git
synchronisiert 2024-11-15 12:30:06 +01:00
Optimize NibbleArray to use pooled buffers
Massively reduces memory allocation of 2048 byte buffers by using an object pool for these.
Dieser Commit ist enthalten in:
Ursprung
d0a528b1cb
Commit
03c9bb05c1
@ -2077,10 +2077,10 @@ index 0000000000000000000000000000000000000000..e51104e65a07b6ea7bbbcbb6afb066ef
|
||||
+}
|
||||
diff --git a/src/main/java/com/destroystokyo/paper/util/pooled/PooledObjects.java b/src/main/java/com/destroystokyo/paper/util/pooled/PooledObjects.java
|
||||
new file mode 100644
|
||||
index 0000000000000000000000000000000000000000..d4325f1f1152e34f82046dfe24cbe505f6e316ae
|
||||
index 0000000000000000000000000000000000000000..e272b512520486cf7d46fe4e1021ca148d4cf74f
|
||||
--- /dev/null
|
||||
+++ b/src/main/java/com/destroystokyo/paper/util/pooled/PooledObjects.java
|
||||
@@ -0,0 +1,185 @@
|
||||
@@ -0,0 +1,174 @@
|
||||
+package com.destroystokyo.paper.util.pooled;
|
||||
+
|
||||
+import org.apache.commons.lang3.mutable.MutableInt;
|
||||
@ -2090,18 +2090,7 @@ index 0000000000000000000000000000000000000000..d4325f1f1152e34f82046dfe24cbe505
|
||||
+
|
||||
+public final class PooledObjects<E> {
|
||||
+
|
||||
+ public static final PooledObjects<MutableInt> POOLED_MUTABLE_INTEGERS = new PooledObjects<>(new PooledObjectHandler<MutableInt>() {
|
||||
+ @Override
|
||||
+ public MutableInt createNew() {
|
||||
+ return new MutableInt();
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public void onAcquire(final MutableInt value) {}
|
||||
+
|
||||
+ @Override
|
||||
+ public void onRelease(final MutableInt value) {}
|
||||
+ }, 200, -1);
|
||||
+ public static final PooledObjects<MutableInt> POOLED_MUTABLE_INTEGERS = new PooledObjects<>(MutableInt::new, 200, -1);
|
||||
+
|
||||
+ private final PooledObjectHandler<E> handler;
|
||||
+ private final int maxPoolSize;
|
||||
@ -2171,16 +2160,16 @@ index 0000000000000000000000000000000000000000..d4325f1f1152e34f82046dfe24cbe505
|
||||
+ }
|
||||
+
|
||||
+ /** This object is restricted from interacting with any pool */
|
||||
+ static interface PooledObjectHandler<E> {
|
||||
+ public static interface PooledObjectHandler<E> {
|
||||
+
|
||||
+ /**
|
||||
+ * Must return a non-null object
|
||||
+ */
|
||||
+ E createNew();
|
||||
+
|
||||
+ void onAcquire(final E value);
|
||||
+ default void onAcquire(final E value) {}
|
||||
+
|
||||
+ void onRelease(final E value);
|
||||
+ default void onRelease(final E value) {}
|
||||
+ }
|
||||
+
|
||||
+ protected static class IsolatedPool<E> {
|
||||
|
@ -4,7 +4,8 @@ Date: Wed, 6 May 2020 04:53:35 -0400
|
||||
Subject: [PATCH] Optimize Network Manager and add advanced packet support
|
||||
|
||||
Adds ability for 1 packet to bundle other packets to follow it
|
||||
adds ability for a packet to delay sending more packets until a state is ready.
|
||||
Adds ability for a packet to delay sending more packets until a state is ready.
|
||||
Adds ability to clean up a packet when it is finished (not sent, or finished encoding), such as freeing buffers
|
||||
|
||||
Removes synchronization from sending packets
|
||||
Removes processing packet queue off of main thread
|
||||
@ -23,7 +24,7 @@ This should solve some deadlock risks
|
||||
Part of this commit was authored by: Spottedleaf
|
||||
|
||||
diff --git a/src/main/java/net/minecraft/server/NetworkManager.java b/src/main/java/net/minecraft/server/NetworkManager.java
|
||||
index b1dededc15cce686ead74a99bee64c89ac1de22c..f81014973a4cf50c1320f82c22ed1c19fd7d6ac9 100644
|
||||
index b1dededc15cce686ead74a99bee64c89ac1de22c..d24d7df807287c51a8dd5ecf4f7c842b7cdf3976 100644
|
||||
--- a/src/main/java/net/minecraft/server/NetworkManager.java
|
||||
+++ b/src/main/java/net/minecraft/server/NetworkManager.java
|
||||
@@ -64,6 +64,10 @@ public class NetworkManager extends SimpleChannelInboundHandler<Packet<?>> {
|
||||
@ -45,7 +46,7 @@ index b1dededc15cce686ead74a99bee64c89ac1de22c..f81014973a4cf50c1320f82c22ed1c19
|
||||
this.channel.attr(NetworkManager.c).set(enumprotocol);
|
||||
this.channel.config().setAutoRead(true);
|
||||
NetworkManager.LOGGER.debug("Enabled auto read");
|
||||
@@ -158,19 +163,72 @@ public class NetworkManager extends SimpleChannelInboundHandler<Packet<?>> {
|
||||
@@ -158,19 +163,75 @@ public class NetworkManager extends SimpleChannelInboundHandler<Packet<?>> {
|
||||
NetworkManager.LOGGER.debug("Set listener of {} to {}", this, packetlistener);
|
||||
this.packetListener = packetlistener;
|
||||
}
|
||||
@ -93,7 +94,10 @@ index b1dededc15cce686ead74a99bee64c89ac1de22c..f81014973a4cf50c1320f82c22ed1c19
|
||||
- this.packetQueue.add(new NetworkManager.QueuedPacket(packet, genericfuturelistener));
|
||||
+ // Paper start - handle oversized packets better
|
||||
+ boolean connected = this.isConnected();
|
||||
+ if (!connected && !preparing) return; // Do nothing
|
||||
+ if (!connected && !preparing) {
|
||||
+ packet.onPacketDone();
|
||||
+ return; // Do nothing
|
||||
+ }
|
||||
+ if (connected && (InnerUtil.canSendImmediate(this, packet) || (
|
||||
+ MCUtil.isMainThread() && packet.isReady() && this.packetQueue.isEmpty() &&
|
||||
+ (packet.getExtraPackets() == null || packet.getExtraPackets().isEmpty())
|
||||
@ -109,13 +113,13 @@ index b1dededc15cce686ead74a99bee64c89ac1de22c..f81014973a4cf50c1320f82c22ed1c19
|
||||
+ } else {
|
||||
+ java.util.List<NetworkManager.QueuedPacket> packets = new java.util.ArrayList<>(1 + extraPackets.size());
|
||||
+ packets.add(new NetworkManager.QueuedPacket(packet, null)); // delay the future listener until the end of the extra packets
|
||||
|
||||
+
|
||||
+ for (int i = 0, len = extraPackets.size(); i < len;) {
|
||||
+ Packet extra = extraPackets.get(i);
|
||||
+ boolean end = ++i == len;
|
||||
+ packets.add(new NetworkManager.QueuedPacket(extra, end ? genericfuturelistener : null)); // append listener to the end
|
||||
+ }
|
||||
+
|
||||
|
||||
+ this.packetQueue.addAll(packets); // atomic
|
||||
+ }
|
||||
+ this.sendPacketQueue();
|
||||
@ -123,7 +127,7 @@ index b1dededc15cce686ead74a99bee64c89ac1de22c..f81014973a4cf50c1320f82c22ed1c19
|
||||
}
|
||||
|
||||
private void dispatchPacket(Packet<?> packet, @Nullable GenericFutureListener<? extends Future<? super Void>> genericFutureListener) { this.b(packet, genericFutureListener); } // Paper - OBFHELPER
|
||||
@@ -214,21 +272,46 @@ public class NetworkManager extends SimpleChannelInboundHandler<Packet<?>> {
|
||||
@@ -214,21 +275,46 @@ public class NetworkManager extends SimpleChannelInboundHandler<Packet<?>> {
|
||||
|
||||
}
|
||||
|
||||
@ -180,27 +184,54 @@ index b1dededc15cce686ead74a99bee64c89ac1de22c..f81014973a4cf50c1320f82c22ed1c19
|
||||
|
||||
public void a() {
|
||||
this.o();
|
||||
@@ -260,6 +343,7 @@ public class NetworkManager extends SimpleChannelInboundHandler<Packet<?>> {
|
||||
@@ -257,9 +343,11 @@ public class NetworkManager extends SimpleChannelInboundHandler<Packet<?>> {
|
||||
return this.socketAddress;
|
||||
}
|
||||
|
||||
+ public void clearPacketQueue() { QueuedPacket packet; while ((packet = packetQueue.poll()) != null) packet.getPacket().onPacketDone(); } // Paper
|
||||
public void close(IChatBaseComponent ichatbasecomponent) {
|
||||
// Spigot Start
|
||||
this.preparing = false;
|
||||
+ this.packetQueue.clear(); // Paper - just incase its closed before we ever get to the main thread to do this
|
||||
+ clearPacketQueue(); // Paper
|
||||
// Spigot End
|
||||
if (this.channel.isOpen()) {
|
||||
this.channel.close(); // We can't wait as this may be called from an event loop.
|
||||
@@ -335,7 +423,7 @@ public class NetworkManager extends SimpleChannelInboundHandler<Packet<?>> {
|
||||
} else if (this.i() != null) {
|
||||
this.i().a(new ChatMessage("multiplayer.disconnect.generic", new Object[0]));
|
||||
}
|
||||
- this.packetQueue.clear(); // Free up packet queue.
|
||||
+ clearPacketQueue(); // Paper
|
||||
// Paper start - Add PlayerConnectionCloseEvent
|
||||
final PacketListener packetListener = this.i();
|
||||
if (packetListener instanceof PlayerConnection) {
|
||||
diff --git a/src/main/java/net/minecraft/server/Packet.java b/src/main/java/net/minecraft/server/Packet.java
|
||||
index 2d8e6a2f4a0c3c5d74a647d7164b0028781d3bf5..45142ed9d2440e21dd1ff1a32a12759fea4dbcb4 100644
|
||||
index 2d8e6a2f4a0c3c5d74a647d7164b0028781d3bf5..df1b4877b1560f982a1fcaf98404c8fe73e29973 100644
|
||||
--- a/src/main/java/net/minecraft/server/Packet.java
|
||||
+++ b/src/main/java/net/minecraft/server/Packet.java
|
||||
@@ -11,6 +11,8 @@ public interface Packet<T extends PacketListener> {
|
||||
@@ -11,6 +11,9 @@ public interface Packet<T extends PacketListener> {
|
||||
void a(T t0);
|
||||
|
||||
// Paper start
|
||||
+ default void onPacketDone() {}
|
||||
+ default boolean isReady() { return true; }
|
||||
+ default java.util.List<Packet> getExtraPackets() { return null; }
|
||||
default boolean packetTooLarge(NetworkManager manager) {
|
||||
return false;
|
||||
}
|
||||
diff --git a/src/main/java/net/minecraft/server/PacketEncoder.java b/src/main/java/net/minecraft/server/PacketEncoder.java
|
||||
index b0cfef52cbb5e23beae528668e4e98cedecf603c..f46d028016a425a29674e768ae9310c825c088f2 100644
|
||||
--- a/src/main/java/net/minecraft/server/PacketEncoder.java
|
||||
+++ b/src/main/java/net/minecraft/server/PacketEncoder.java
|
||||
@@ -48,7 +48,7 @@ public class PacketEncoder extends MessageToByteEncoder<Packet<?>> {
|
||||
} else {
|
||||
throw throwable;
|
||||
}
|
||||
- }
|
||||
+ } finally { try { packet.onPacketDone(); } catch (Exception e) { e.printStackTrace(); } ; } // Paper
|
||||
|
||||
// Paper start
|
||||
int packetLength = bytebuf.readableBytes();
|
||||
diff --git a/src/main/java/net/minecraft/server/PlayerList.java b/src/main/java/net/minecraft/server/PlayerList.java
|
||||
index e148940ab3721cff27cf791c159c11b9b94191e4..e917d37382dab70ed9e6b62decf1557c33b26065 100644
|
||||
--- a/src/main/java/net/minecraft/server/PlayerList.java
|
||||
|
@ -0,0 +1,215 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Aikar <aikar@aikar.co>
|
||||
Date: Wed, 6 May 2020 23:30:30 -0400
|
||||
Subject: [PATCH] Optimize NibbleArray to use pooled buffers
|
||||
|
||||
Massively reduces memory allocation of 2048 byte buffers by using
|
||||
an object pool for these.
|
||||
|
||||
diff --git a/src/main/java/net/minecraft/server/ChunkRegionLoader.java b/src/main/java/net/minecraft/server/ChunkRegionLoader.java
|
||||
index fa893b14bcef9bab6891dea2c4375b09d74ac038..738f5b975697cd83f1ff35eb0d2d8619ee8d39e4 100644
|
||||
--- a/src/main/java/net/minecraft/server/ChunkRegionLoader.java
|
||||
+++ b/src/main/java/net/minecraft/server/ChunkRegionLoader.java
|
||||
@@ -387,11 +387,11 @@ public class ChunkRegionLoader {
|
||||
}
|
||||
|
||||
if (nibblearray != null && !nibblearray.c()) {
|
||||
- nbttagcompound2.setByteArray("BlockLight", nibblearray.asBytes());
|
||||
+ nbttagcompound2.setByteArray("BlockLight", nibblearray.getIfSet()); // Paper
|
||||
}
|
||||
|
||||
if (nibblearray1 != null && !nibblearray1.c()) {
|
||||
- nbttagcompound2.setByteArray("SkyLight", nibblearray1.asBytes());
|
||||
+ nbttagcompound2.setByteArray("SkyLight", nibblearray1.getIfSet()); // Paper
|
||||
}
|
||||
|
||||
nbttaglist.add(nbttagcompound2);
|
||||
diff --git a/src/main/java/net/minecraft/server/LightEngineStorageSky.java b/src/main/java/net/minecraft/server/LightEngineStorageSky.java
|
||||
index 06bc8371fe9de4d23fdd47e5a3919541bb399fd8..bf37e4ec1f3f4f73c27e1eecffa96423f683a10b 100644
|
||||
--- a/src/main/java/net/minecraft/server/LightEngineStorageSky.java
|
||||
+++ b/src/main/java/net/minecraft/server/LightEngineStorageSky.java
|
||||
@@ -166,7 +166,7 @@ public class LightEngineStorageSky extends LightEngineStorage<LightEngineStorage
|
||||
j = SectionPosition.a(j, EnumDirection.UP);
|
||||
}
|
||||
|
||||
- return new NibbleArray((new NibbleArrayFlat(nibblearray1, 0)).asBytes());
|
||||
+ return new NibbleArray((new NibbleArrayFlat(nibblearray1, 0)).asBytes(), true); // Paper - mark buffer as safe
|
||||
} else {
|
||||
return new NibbleArray();
|
||||
}
|
||||
diff --git a/src/main/java/net/minecraft/server/NibbleArray.java b/src/main/java/net/minecraft/server/NibbleArray.java
|
||||
index 996c8326387b5a7fe62db6a76e000144565cb85b..6423ec30504b53ffe2d400b7363cccc60208e31f 100644
|
||||
--- a/src/main/java/net/minecraft/server/NibbleArray.java
|
||||
+++ b/src/main/java/net/minecraft/server/NibbleArray.java
|
||||
@@ -1,16 +1,58 @@
|
||||
package net.minecraft.server;
|
||||
|
||||
+import com.destroystokyo.paper.util.pooled.PooledObjects;
|
||||
+
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public class NibbleArray {
|
||||
|
||||
- @Nullable
|
||||
- protected byte[] a;
|
||||
+ // Paper start
|
||||
+ public static byte[] EMPTY_NIBBLE = new byte[2048];
|
||||
+ public static final PooledObjects<byte[]> BYTE_2048 = new PooledObjects<>(() -> new byte[2048], 16384, 1);
|
||||
+ public static void releaseBytes(byte[] bytes) {
|
||||
+ if (bytes != EMPTY_NIBBLE) {
|
||||
+ System.arraycopy(EMPTY_NIBBLE, 0, bytes, 0, 2048);
|
||||
+ BYTE_2048.release(bytes);
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ public byte[] getIfSet() {
|
||||
+ return this.a != null ? this.a : EMPTY_NIBBLE;
|
||||
+ }
|
||||
+ public byte[] getCloneIfSet() {
|
||||
+ if (a == null) {
|
||||
+ return EMPTY_NIBBLE;
|
||||
+ }
|
||||
+ byte[] ret = BYTE_2048.acquire();
|
||||
+ System.arraycopy(getIfSet(), 0, ret, 0, 2048);
|
||||
+ return ret;
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ protected void finalize() throws Throwable {
|
||||
+ try {
|
||||
+ if (this.a != null) {
|
||||
+ releaseBytes(this.a);
|
||||
+ this.a = null;
|
||||
+ }
|
||||
+ } finally {
|
||||
+ super.finalize();
|
||||
+ }
|
||||
+ }
|
||||
+ // Paper end
|
||||
+ @Nullable protected byte[] a;
|
||||
+
|
||||
|
||||
public NibbleArray() {}
|
||||
|
||||
public NibbleArray(byte[] abyte) {
|
||||
+ // Paper start
|
||||
+ this(abyte, false);
|
||||
+ }
|
||||
+ public NibbleArray(byte[] abyte, boolean isSafe) {
|
||||
this.a = abyte;
|
||||
+ if (!isSafe) this.a = getCloneIfSet(); // Paper - clone for safety
|
||||
+ // Paper end
|
||||
if (abyte.length != 2048) {
|
||||
throw (IllegalArgumentException) SystemUtils.c(new IllegalArgumentException("ChunkNibbleArrays should be 2048 bytes not: " + abyte.length));
|
||||
}
|
||||
@@ -44,7 +86,7 @@ public class NibbleArray {
|
||||
|
||||
public void a(int i, int j) { // PAIL: private -> public
|
||||
if (this.a == null) {
|
||||
- this.a = new byte[2048];
|
||||
+ this.a = BYTE_2048.acquire(); // Paper
|
||||
}
|
||||
|
||||
int k = this.d(i);
|
||||
@@ -65,7 +107,7 @@ public class NibbleArray {
|
||||
|
||||
public byte[] asBytes() {
|
||||
if (this.a == null) {
|
||||
- this.a = new byte[2048];
|
||||
+ this.a = BYTE_2048.acquire(); // Paper
|
||||
}
|
||||
|
||||
return this.a;
|
||||
@@ -73,7 +115,7 @@ public class NibbleArray {
|
||||
|
||||
public NibbleArray copy() { return this.b(); } // Paper - OBFHELPER
|
||||
public NibbleArray b() {
|
||||
- return this.a == null ? new NibbleArray() : new NibbleArray((byte[]) this.a.clone());
|
||||
+ return this.a == null ? new NibbleArray() : new NibbleArray(this.a); // Paper - clone in ctor
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
diff --git a/src/main/java/net/minecraft/server/NibbleArrayFlat.java b/src/main/java/net/minecraft/server/NibbleArrayFlat.java
|
||||
index 67c960292db9d99ac85b5d0dda50ae48ef942c1b..f7641156beea365a91a935667abf8c9539896dc0 100644
|
||||
--- a/src/main/java/net/minecraft/server/NibbleArrayFlat.java
|
||||
+++ b/src/main/java/net/minecraft/server/NibbleArrayFlat.java
|
||||
@@ -18,7 +18,7 @@ public class NibbleArrayFlat extends NibbleArray {
|
||||
|
||||
@Override
|
||||
public byte[] asBytes() {
|
||||
- byte[] abyte = new byte[2048];
|
||||
+ byte[] abyte = BYTE_2048.acquire(); // Paper
|
||||
|
||||
for (int i = 0; i < 16; ++i) {
|
||||
System.arraycopy(this.a, 0, abyte, i * 128, 128);
|
||||
diff --git a/src/main/java/net/minecraft/server/PacketPlayOutLightUpdate.java b/src/main/java/net/minecraft/server/PacketPlayOutLightUpdate.java
|
||||
index cd1ad45469aa163b9bc41774ae80adfa617fd97b..d6560f4693869a6638963867f7ebc63bf80d534e 100644
|
||||
--- a/src/main/java/net/minecraft/server/PacketPlayOutLightUpdate.java
|
||||
+++ b/src/main/java/net/minecraft/server/PacketPlayOutLightUpdate.java
|
||||
@@ -17,6 +17,15 @@ public class PacketPlayOutLightUpdate implements Packet<PacketListenerPlayOut> {
|
||||
private List<byte[]> h;
|
||||
|
||||
public PacketPlayOutLightUpdate() {}
|
||||
+ // Paper start
|
||||
+ private final java.util.List<byte[]> usedBytes = new java.util.ArrayList<>();
|
||||
+
|
||||
+ @Override
|
||||
+ public void onPacketDone() {
|
||||
+ usedBytes.forEach(NibbleArray::releaseBytes);
|
||||
+ usedBytes.clear();
|
||||
+ }
|
||||
+ // Paper end
|
||||
|
||||
public PacketPlayOutLightUpdate(ChunkCoordIntPair chunkcoordintpair, LightEngine lightengine) {
|
||||
this.a = chunkcoordintpair.x;
|
||||
@@ -24,6 +33,7 @@ public class PacketPlayOutLightUpdate implements Packet<PacketListenerPlayOut> {
|
||||
this.g = Lists.newArrayList();
|
||||
this.h = Lists.newArrayList();
|
||||
|
||||
+ byte[] lastBytes; // Paper
|
||||
for (int i = 0; i < 18; ++i) {
|
||||
NibbleArray nibblearray = lightengine.a(EnumSkyBlock.SKY).a(SectionPosition.a(chunkcoordintpair, -1 + i));
|
||||
NibbleArray nibblearray1 = lightengine.a(EnumSkyBlock.BLOCK).a(SectionPosition.a(chunkcoordintpair, -1 + i));
|
||||
@@ -33,7 +43,7 @@ public class PacketPlayOutLightUpdate implements Packet<PacketListenerPlayOut> {
|
||||
this.e |= 1 << i;
|
||||
} else {
|
||||
this.c |= 1 << i;
|
||||
- this.g.add(nibblearray.asBytes().clone());
|
||||
+ this.g.add(lastBytes = nibblearray.getCloneIfSet()); usedBytes.add(lastBytes); // Paper
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,7 +52,7 @@ public class PacketPlayOutLightUpdate implements Packet<PacketListenerPlayOut> {
|
||||
this.f |= 1 << i;
|
||||
} else {
|
||||
this.d |= 1 << i;
|
||||
- this.h.add(nibblearray1.asBytes().clone());
|
||||
+ this.h.add(lastBytes = nibblearray1.getCloneIfSet()); usedBytes.add(lastBytes); // Paper
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -57,13 +67,14 @@ public class PacketPlayOutLightUpdate implements Packet<PacketListenerPlayOut> {
|
||||
this.g = Lists.newArrayList();
|
||||
this.h = Lists.newArrayList();
|
||||
|
||||
+ byte[] lastBytes; // Paper
|
||||
for (int k = 0; k < 18; ++k) {
|
||||
NibbleArray nibblearray;
|
||||
|
||||
if ((this.c & 1 << k) != 0) {
|
||||
nibblearray = lightengine.a(EnumSkyBlock.SKY).a(SectionPosition.a(chunkcoordintpair, -1 + k));
|
||||
if (nibblearray != null && !nibblearray.c()) {
|
||||
- this.g.add(nibblearray.asBytes().clone());
|
||||
+ this.g.add(lastBytes = nibblearray.getCloneIfSet()); usedBytes.add(lastBytes); // Paper
|
||||
} else {
|
||||
this.c &= ~(1 << k);
|
||||
if (nibblearray != null) {
|
||||
@@ -75,7 +86,7 @@ public class PacketPlayOutLightUpdate implements Packet<PacketListenerPlayOut> {
|
||||
if ((this.d & 1 << k) != 0) {
|
||||
nibblearray = lightengine.a(EnumSkyBlock.BLOCK).a(SectionPosition.a(chunkcoordintpair, -1 + k));
|
||||
if (nibblearray != null && !nibblearray.c()) {
|
||||
- this.h.add(nibblearray.asBytes().clone());
|
||||
+ this.h.add(lastBytes = nibblearray.getCloneIfSet()); usedBytes.add(lastBytes); // Paper
|
||||
} else {
|
||||
this.d &= ~(1 << k);
|
||||
if (nibblearray != null) {
|
Laden…
In neuem Issue referenzieren
Einen Benutzer sperren