diff --git a/.upstream-data b/.upstream-data
index efee68a..9c7e58d 100644
--- a/.upstream-data
+++ b/.upstream-data
@@ -1,2 +1,2 @@
-purpurCommit = 285f9f683d611c151582f94745f4be3e5b52f81e
-pufferfishCommit = c09a1548516bb8b61c2a80367fdbdb5abe977a29
+purpurCommit = f952106e1ba20e799dbe0e67c1d241e3f286b5f4
+pufferfishCommit = b7781634b0800476107ed4a0b38fb24c13da6bf8
diff --git a/build.gradle.kts b/build.gradle.kts
index d4b2303..5e52228 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -1,4 +1,3 @@
-import io.papermc.paperweight.patcher.*
import io.papermc.paperweight.util.*
import io.papermc.paperweight.util.constants.PAPERCLIP_CONFIG
@@ -20,7 +19,7 @@ repositories {
dependencies {
remapper("net.fabricmc:tiny-remapper:0.8.6:fat")
- decompiler("net.minecraftforge:forgeflower:2.0.605.2")
+ decompiler("net.minecraftforge:forgeflower:2.0.627.2")
paperclip("io.papermc:paperclip:3.0.3")
}
diff --git a/gradle.properties b/gradle.properties
index 1709496..cb427e6 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -1,7 +1,7 @@
group = org.plazmamc.plazma
version = 1.19.4-R0.1-SNAPSHOT
-paperCommit = 497b91939cf2972bac5337487f9f71770044fbc8
+paperCommit = 8d040fad9b4ddcc49a7b70c15b80b63c479eac95
org.gradle.caching = true
org.gradle.parallel = true
diff --git a/patches/api/0001-Pufferfish-API-Changes.patch b/patches/api/0001-Pufferfish-API-Changes.patch
index 1efb452..009c88c 100644
--- a/patches/api/0001-Pufferfish-API-Changes.patch
+++ b/patches/api/0001-Pufferfish-API-Changes.patch
@@ -20,7 +20,7 @@ You should have received a copy of the GNU General Public License
along with this program. If not, see .
diff --git a/build.gradle.kts b/build.gradle.kts
-index cad12a2632b9ebb569280441c42869685db1f31a..b83e2c5a0a094002d12aee55ec0cf8d12bf33f3e 100644
+index 279a666e8ea2c07f41ee3f28b768e95dca5f0a10..a93b900889ddb56a2943c54a7fff6f60f42a78f1 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -42,6 +42,7 @@ dependencies {
@@ -47,7 +47,7 @@ index cad12a2632b9ebb569280441c42869685db1f31a..b83e2c5a0a094002d12aee55ec0cf8d1
into("META-INF/maven/${project.group}/${project.name}")
diff --git a/src/main/java/gg/pufferfish/pufferfish/sentry/SentryContext.java b/src/main/java/gg/pufferfish/pufferfish/sentry/SentryContext.java
new file mode 100644
-index 0000000000000000000000000000000000000000..6fbaf2a232745db0a41394b1c2cc0cc90cefc4ee
+index 0000000000000000000000000000000000000000..10310fdd53de28efb8a8250f6d3b0c8eb08fb68a
--- /dev/null
+++ b/src/main/java/gg/pufferfish/pufferfish/sentry/SentryContext.java
@@ -0,0 +1,161 @@
@@ -69,21 +69,21 @@ index 0000000000000000000000000000000000000000..6fbaf2a232745db0a41394b1c2cc0cc9
+import org.jetbrains.annotations.Nullable;
+
+public class SentryContext {
-+
++
+ private static final Gson GSON = new Gson();
-+
++
+ public static void setPluginContext(@Nullable Plugin plugin) {
+ if (plugin != null) {
+ ThreadContext.put("pufferfishsentry_pluginname", plugin.getName());
+ ThreadContext.put("pufferfishsentry_pluginversion", plugin.getDescription().getVersion());
+ }
+ }
-+
++
+ public static void removePluginContext() {
+ ThreadContext.remove("pufferfishsentry_pluginname");
+ ThreadContext.remove("pufferfishsentry_pluginversion");
+ }
-+
++
+ public static void setSenderContext(@Nullable CommandSender sender) {
+ if (sender != null) {
+ ThreadContext.put("pufferfishsentry_playername", sender.getName());
@@ -92,15 +92,15 @@ index 0000000000000000000000000000000000000000..6fbaf2a232745db0a41394b1c2cc0cc9
+ }
+ }
+ }
-+
++
+ public static void removeSenderContext() {
+ ThreadContext.remove("pufferfishsentry_playername");
+ ThreadContext.remove("pufferfishsentry_playerid");
+ }
-+
++
+ public static void setEventContext(Event event, RegisteredListener registration) {
+ setPluginContext(registration.getPlugin());
-+
++
+ try {
+ // Find the player that was involved with this event
+ Player player = null;
@@ -108,36 +108,36 @@ index 0000000000000000000000000000000000000000..6fbaf2a232745db0a41394b1c2cc0cc9
+ player = ((PlayerEvent) event).getPlayer();
+ } else {
+ Class extends Event> eventClass = event.getClass();
-+
++
+ Field playerField = null;
-+
++
+ for (Field field : eventClass.getDeclaredFields()) {
+ if (field.getType().equals(Player.class)) {
+ playerField = field;
+ break;
+ }
+ }
-+
++
+ if (playerField != null) {
+ playerField.setAccessible(true);
+ player = (Player) playerField.get(event);
+ }
+ }
-+
++
+ if (player != null) {
+ setSenderContext(player);
+ }
+ } catch (Exception e) {} // We can't really safely log exceptions.
-+
++
+ ThreadContext.put("pufferfishsentry_eventdata", GSON.toJson(serializeFields(event)));
+ }
-+
++
+ public static void removeEventContext() {
+ removePluginContext();
+ removeSenderContext();
+ ThreadContext.remove("pufferfishsentry_eventdata");
+ }
-+
++
+ private static Map serializeFields(Object object) {
+ Map fields = new TreeMap<>();
+ fields.put("_class", object.getClass().getName());
@@ -146,7 +146,7 @@ index 0000000000000000000000000000000000000000..6fbaf2a232745db0a41394b1c2cc0cc9
+ if (Modifier.isStatic(declaredField.getModifiers())) {
+ continue;
+ }
-+
++
+ String fieldName = declaredField.getName();
+ if (fieldName.equals("handlers")) {
+ continue;
@@ -162,51 +162,51 @@ index 0000000000000000000000000000000000000000..6fbaf2a232745db0a41394b1c2cc0cc9
+ }
+ return fields;
+ }
-+
++
+ public static class State {
-+
++
+ private Plugin plugin;
+ private Command command;
+ private String commandLine;
+ private Event event;
+ private RegisteredListener registeredListener;
-+
++
+ public Plugin getPlugin() {
+ return plugin;
+ }
-+
++
+ public void setPlugin(Plugin plugin) {
+ this.plugin = plugin;
+ }
-+
++
+ public Command getCommand() {
+ return command;
+ }
-+
++
+ public void setCommand(Command command) {
+ this.command = command;
+ }
-+
++
+ public String getCommandLine() {
+ return commandLine;
+ }
-+
++
+ public void setCommandLine(String commandLine) {
+ this.commandLine = commandLine;
+ }
-+
++
+ public Event getEvent() {
+ return event;
+ }
-+
++
+ public void setEvent(Event event) {
+ this.event = event;
+ }
-+
++
+ public RegisteredListener getRegisteredListener() {
+ return registeredListener;
+ }
-+
++
+ public void setRegisteredListener(RegisteredListener registeredListener) {
+ this.registeredListener = registeredListener;
+ }
@@ -214,7 +214,7 @@ index 0000000000000000000000000000000000000000..6fbaf2a232745db0a41394b1c2cc0cc9
+}
diff --git a/src/main/java/gg/pufferfish/pufferfish/simd/SIMDChecker.java b/src/main/java/gg/pufferfish/pufferfish/simd/SIMDChecker.java
new file mode 100644
-index 0000000000000000000000000000000000000000..6d266ebf5e52745ad13e90e5754b524383fa9b29
+index 0000000000000000000000000000000000000000..ab5fea0b03224bf249352ce340e94704ff713345
--- /dev/null
+++ b/src/main/java/gg/pufferfish/pufferfish/simd/SIMDChecker.java
@@ -0,0 +1,40 @@
@@ -231,7 +231,7 @@ index 0000000000000000000000000000000000000000..6d266ebf5e52745ad13e90e5754b5243
+ */
+@Deprecated
+public class SIMDChecker {
-+
++
+ @Deprecated
+ public static boolean canEnable(Logger logger) {
+ try {
@@ -239,13 +239,13 @@ index 0000000000000000000000000000000000000000..6d266ebf5e52745ad13e90e5754b5243
+ return false;
+ } else {
+ SIMDDetection.testRun = true;
-+
++
+ VectorSpecies ISPEC = IntVector.SPECIES_PREFERRED;
+ VectorSpecies FSPEC = FloatVector.SPECIES_PREFERRED;
-+
++
+ logger.log(Level.INFO, "Max SIMD vector size on this system is " + ISPEC.vectorBitSize() + " bits (int)");
+ logger.log(Level.INFO, "Max SIMD vector size on this system is " + FSPEC.vectorBitSize() + " bits (float)");
-+
++
+ if (ISPEC.elementSize() < 2 || FSPEC.elementSize() < 2) {
+ logger.log(Level.WARNING, "SIMD is not properly supported on this system!");
+ return false;
@@ -256,11 +256,11 @@ index 0000000000000000000000000000000000000000..6d266ebf5e52745ad13e90e5754b5243
+ } catch (NoClassDefFoundError | Exception ignored) {} // Basically, we don't do anything. This lets us detect if it's not functional and disable it.
+ return false;
+ }
-+
++
+}
diff --git a/src/main/java/gg/pufferfish/pufferfish/simd/SIMDDetection.java b/src/main/java/gg/pufferfish/pufferfish/simd/SIMDDetection.java
new file mode 100644
-index 0000000000000000000000000000000000000000..fd708554d6dab2ddcd24c3024330b8ebf9462111
+index 0000000000000000000000000000000000000000..a84889d3e9cfc4d7ab5f867820a6484c6070711b
--- /dev/null
+++ b/src/main/java/gg/pufferfish/pufferfish/simd/SIMDDetection.java
@@ -0,0 +1,35 @@
@@ -270,11 +270,11 @@ index 0000000000000000000000000000000000000000..fd708554d6dab2ddcd24c3024330b8eb
+
+@Deprecated
+public class SIMDDetection {
-+
++
+ public static boolean isEnabled = false;
+ public static boolean versionLimited = false;
+ public static boolean testRun = false;
-+
++
+ @Deprecated
+ public static boolean canEnable(Logger logger) {
+ try {
@@ -283,7 +283,7 @@ index 0000000000000000000000000000000000000000..fd708554d6dab2ddcd24c3024330b8eb
+ return false;
+ }
+ }
-+
++
+ @Deprecated
+ public static int getJavaVersion() {
+ // https://stackoverflow.com/a/2591122
@@ -297,11 +297,11 @@ index 0000000000000000000000000000000000000000..fd708554d6dab2ddcd24c3024330b8eb
+ version = version.split("-")[0]; // Azul is stupid
+ return Integer.parseInt(version);
+ }
-+
++
+}
diff --git a/src/main/java/gg/pufferfish/pufferfish/simd/VectorMapPalette.java b/src/main/java/gg/pufferfish/pufferfish/simd/VectorMapPalette.java
new file mode 100644
-index 0000000000000000000000000000000000000000..20ec3b29b0cb4061cc89d635b3929ffe71008e22
+index 0000000000000000000000000000000000000000..ae2464920c9412ac90b819a540ee58be0741465f
--- /dev/null
+++ b/src/main/java/gg/pufferfish/pufferfish/simd/VectorMapPalette.java
@@ -0,0 +1,83 @@
@@ -316,10 +316,10 @@ index 0000000000000000000000000000000000000000..20ec3b29b0cb4061cc89d635b3929ffe
+
+@Deprecated
+public class VectorMapPalette {
-+
++
+ private static final VectorSpecies I_SPEC = IntVector.SPECIES_PREFERRED;
+ private static final VectorSpecies F_SPEC = FloatVector.SPECIES_PREFERRED;
-+
++
+ @Deprecated
+ public static void matchColorVectorized(int[] in, byte[] out) {
+ int speciesLength = I_SPEC.length();
@@ -329,64 +329,64 @@ index 0000000000000000000000000000000000000000..20ec3b29b0cb4061cc89d635b3929ffe
+ float[] bluesArr = new float[speciesLength];
+ float[] greensArr = new float[speciesLength];
+ int[] alphasArr = new int[speciesLength];
-+
++
+ for (int j = 0; j < speciesLength; j++) {
+ alphasArr[j] = (in[i + j] >> 24) & 0xFF;
+ redsArr[j] = (in[i + j] >> 16) & 0xFF;
+ greensArr[j] = (in[i + j] >> 8) & 0xFF;
+ bluesArr[j] = (in[i + j] >> 0) & 0xFF;
+ }
-+
++
+ IntVector alphas = IntVector.fromArray(I_SPEC, alphasArr, 0);
+ FloatVector reds = FloatVector.fromArray(F_SPEC, redsArr, 0);
+ FloatVector greens = FloatVector.fromArray(F_SPEC, greensArr, 0);
+ FloatVector blues = FloatVector.fromArray(F_SPEC, bluesArr, 0);
+ IntVector resultIndex = IntVector.zero(I_SPEC);
+ VectorMask modificationMask = VectorMask.fromLong(I_SPEC, 0xffffffff);
-+
++
+ modificationMask = modificationMask.and(alphas.lt(128).not());
+ FloatVector bestDistances = FloatVector.broadcast(F_SPEC, Float.MAX_VALUE);
-+
++
+ for (int c = 4; c < MapPalette.colors.length; c++) {
+ // We're using 32-bit floats here because it's 2x faster and nobody will know the difference.
+ // For correctness, the original algorithm uses 64-bit floats instead. Completely unnecessary.
+ FloatVector compReds = FloatVector.broadcast(F_SPEC, MapPalette.colors[c].getRed());
+ FloatVector compGreens = FloatVector.broadcast(F_SPEC, MapPalette.colors[c].getGreen());
+ FloatVector compBlues = FloatVector.broadcast(F_SPEC, MapPalette.colors[c].getBlue());
-+
++
+ FloatVector rMean = reds.add(compReds).div(2.0f);
+ FloatVector rDiff = reds.sub(compReds);
+ FloatVector gDiff = greens.sub(compGreens);
+ FloatVector bDiff = blues.sub(compBlues);
-+
++
+ FloatVector weightR = rMean.div(256.0f).add(2);
+ FloatVector weightG = FloatVector.broadcast(F_SPEC, 4.0f);
+ FloatVector weightB = FloatVector.broadcast(F_SPEC, 255.0f).sub(rMean).div(256.0f).add(2.0f);
-+
++
+ FloatVector distance = weightR.mul(rDiff).mul(rDiff).add(weightG.mul(gDiff).mul(gDiff)).add(weightB.mul(bDiff).mul(bDiff));
-+
++
+ // Now we compare to the best distance we've found.
+ // This mask contains a "1" if better, and a "0" otherwise.
+ VectorMask bestDistanceMask = distance.lt(bestDistances);
+ bestDistances = bestDistances.blend(distance, bestDistanceMask); // Update the best distances
-+
++
+ // Update the result array
+ // We also AND with the modification mask because we don't want to interfere if the alpha value isn't large enough.
+ resultIndex = resultIndex.blend(c, bestDistanceMask.cast(I_SPEC).and(modificationMask)); // Update the results
+ }
-+
++
+ for (int j = 0; j < speciesLength; j++) {
+ int index = resultIndex.lane(j);
+ out[i + j] = (byte) (index < 128 ? index : -129 + (index - 127));
+ }
+ }
-+
++
+ // For the final ones, fall back to the regular method
+ for (; i < in.length; i++) {
+ out[i] = MapPalette.matchColor(new Color(in[i], true));
+ }
+ }
-+
++
+}
diff --git a/src/main/java/org/bukkit/map/MapPalette.java b/src/main/java/org/bukkit/map/MapPalette.java
index 3a9aaca2e76411a9c27f9f5e0f22d060d5a66d06..9584e245144b561b4f6745b2f26a4f69a6f92891 100644
diff --git a/patches/api/0002-Purpur-API-Changes.patch b/patches/api/0002-Purpur-API-Changes.patch
index 4bedc6f..7a87a81 100644
--- a/patches/api/0002-Purpur-API-Changes.patch
+++ b/patches/api/0002-Purpur-API-Changes.patch
@@ -1,6 +1,6 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: AlphaKR93
-Date: Tue, 4 Apr 2023 22:23:38 +0000
+From: BillyGalbreath
+Date: Tue, 4 Jan 2022 23:05:41 -0600
Subject: [PATCH] Purpur API Changes
Original: PurpurMC
@@ -25,7 +25,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
diff --git a/build.gradle.kts b/build.gradle.kts
-index b83e2c5a0a094002d12aee55ec0cf8d12bf33f3e..b5835fa536f90b7f88a5ee4df78733cf43e1cb23 100644
+index a93b900889ddb56a2943c54a7fff6f60f42a78f1..417c4324af22c870de669e338a1eee5d540184a4 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -105,6 +105,8 @@ tasks.jar {
@@ -416,10 +416,10 @@ index ea4ceb643239c26851bacbf45fc3f2efef3bb4be..3b8395dcb73e3fb251adf7438cbc7e95
+ // Purpur end
}
diff --git a/src/main/java/org/bukkit/Material.java b/src/main/java/org/bukkit/Material.java
-index 3c5e90f039f0d2991be442168703526405e18e3d..df992c41d736ee4e89773c2621d261d6afcb2148 100644
+index adb2416887cc3b544ec36c552bdf7105328c24a5..d334c963e16590c68ecd9d1d27434c7af6f7e21c 100644
--- a/src/main/java/org/bukkit/Material.java
+++ b/src/main/java/org/bukkit/Material.java
-@@ -11066,4 +11066,40 @@ public enum Material implements Keyed, Translatable, net.kyori.adventure.transla
+@@ -11156,4 +11156,40 @@ public enum Material implements Keyed, Translatable, net.kyori.adventure.transla
public String getItemTranslationKey() {
return Bukkit.getUnsafe().getItemTranslationKey(this);
}
@@ -709,10 +709,10 @@ index 8621dba7f10ab822b5b99ce0d05da58823605cb6..730caafaf9629664ba125f3884559da7
+ // Purpur end
}
diff --git a/src/main/java/org/bukkit/World.java b/src/main/java/org/bukkit/World.java
-index 6716a1185e733f3ddf56b295f3153938f57d4229..ad00e30379df52575bf2697ccb32abb176ecd47a 100644
+index 0b8776355f724927ada826735d5e73f3fb6897d5..cf8b93aa5787e96f54e2ffe2a0f157b0e53e2c5c 100644
--- a/src/main/java/org/bukkit/World.java
+++ b/src/main/java/org/bukkit/World.java
-@@ -4007,6 +4007,86 @@ public interface World extends RegionAccessor, WorldInfo, PluginMessageRecipient
+@@ -4018,6 +4018,86 @@ public interface World extends RegionAccessor, WorldInfo, PluginMessageRecipient
@Nullable
public DragonBattle getEnderDragonBattle();
@@ -933,10 +933,10 @@ index 138d2530de2410f4a9424dabd3e5ce0cd1c1dcd2..10a8d64ad2da0be2c14f34c3e7d1957c
// Paper start
/**
diff --git a/src/main/java/org/bukkit/entity/Entity.java b/src/main/java/org/bukkit/entity/Entity.java
-index 8c58018b155c52a7b2a139f784abceb6aa00a268..dc16fd3f342994dc3ea88b0980e4b3f56621ddf3 100644
+index a2a423d4e4c2702ba5967223cab0432dd7d04732..cc78ce7de88a9a404ed20d5bc61b98d3107f29b3 100644
--- a/src/main/java/org/bukkit/entity/Entity.java
+++ b/src/main/java/org/bukkit/entity/Entity.java
-@@ -953,4 +953,55 @@ public interface Entity extends Metadatable, CommandSender, Nameable, Persistent
+@@ -954,4 +954,55 @@ public interface Entity extends Metadatable, CommandSender, Nameable, Persistent
*/
boolean wouldCollideUsing(@NotNull BoundingBox boundingBox);
// Paper End - Collision API
@@ -1156,10 +1156,10 @@ index bc84b892cae5fe7019a3ad481e9da79956efa1fe..48eb5b00c460cccde29d327cef1d63fc
+ // Purpur end
}
diff --git a/src/main/java/org/bukkit/entity/Player.java b/src/main/java/org/bukkit/entity/Player.java
-index 8fe11fce80c3b29164b79ece8dc9ed8da6fc1790..85c5987758b1d5145843705205dc99164c5f9f44 100644
+index ee446bfb3e6c4b1d4428c00d6406bf9455c68ad5..43cef54d355c8a1b6a8cd6be89a6a805e9f88566 100644
--- a/src/main/java/org/bukkit/entity/Player.java
+++ b/src/main/java/org/bukkit/entity/Player.java
-@@ -3000,4 +3000,139 @@ public interface Player extends HumanEntity, Conversable, OfflinePlayer, PluginM
+@@ -3035,4 +3035,139 @@ public interface Player extends HumanEntity, Conversable, OfflinePlayer, PluginM
@Override
Spigot spigot();
// Spigot end
@@ -1411,10 +1411,10 @@ index c9f395064656dd0126410eb3c6e197baa450c063..13156a12e5df50cdc1e465dc0bd9d941
* When a player gets bad omen after killing a patrol captain.
*/
diff --git a/src/main/java/org/bukkit/event/inventory/InventoryType.java b/src/main/java/org/bukkit/event/inventory/InventoryType.java
-index 94f127fd6ba69ca48d03acbc9886ee499e9be4f3..1f958facfed766c401144b840e4369154c71d3c2 100644
+index a8e631315f2da68895a258cf0ba9875bc88fc48c..d5648ec745e3530aecf18c3e1f3185a5f63f3d11 100644
--- a/src/main/java/org/bukkit/event/inventory/InventoryType.java
+++ b/src/main/java/org/bukkit/event/inventory/InventoryType.java
-@@ -152,7 +152,7 @@ public enum InventoryType {
+@@ -155,7 +155,7 @@ public enum InventoryType {
SMITHING_NEW(4, "Upgrade Gear"),
;
diff --git a/patches/api/0005-Publish-Packages.patch b/patches/api/0005-Publish-Packages.patch
index a070771..1957cea 100644
--- a/patches/api/0005-Publish-Packages.patch
+++ b/patches/api/0005-Publish-Packages.patch
@@ -5,10 +5,10 @@ Subject: [PATCH] Publish Packages
diff --git a/build.gradle.kts b/build.gradle.kts
-index 42de5c470a2fbb1e0bc9b809c033e3afe30502fa..c2c506afd44c7f14de51bd93004aa8a32ff2103e 100644
+index 7b38042c32295ef7c5ea3c212170cc8fcdf0d7a0..1e300e51fa3413f9846c686545f2b7deb0ac5160 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
-@@ -155,3 +155,23 @@ tasks.check {
+@@ -156,3 +156,23 @@ tasks.check {
dependsOn(scanJar)
}
// Paper end
diff --git a/patches/server/0001-Pufferfish-Server-Changes.patch b/patches/server/0001-Pufferfish-Server-Changes.patch
index d8dee44..157653d 100644
--- a/patches/server/0001-Pufferfish-Server-Changes.patch
+++ b/patches/server/0001-Pufferfish-Server-Changes.patch
@@ -542,7 +542,7 @@ index 0000000000000000000000000000000000000000..a7f297ebb569f7c1f205e967ca485be7
+}
diff --git a/src/main/java/gg/pufferfish/pufferfish/PufferfishCommand.java b/src/main/java/gg/pufferfish/pufferfish/PufferfishCommand.java
new file mode 100644
-index 0000000000000000000000000000000000000000..e164237e749bcc43466d4ed7aeada5ab9fddf8a6
+index 0000000000000000000000000000000000000000..020368da69b9a492155f6de6297f74732f4ab6ea
--- /dev/null
+++ b/src/main/java/gg/pufferfish/pufferfish/PufferfishCommand.java
@@ -0,0 +1,68 @@
@@ -570,7 +570,7 @@ index 0000000000000000000000000000000000000000..e164237e749bcc43466d4ed7aeada5ab
+ this.usageMessage = "/pufferfish [reload | version]";
+ this.setPermission("bukkit.command.pufferfish");
+ }
-+
++
+ public static void init() {
+ MinecraftServer.getServer().server.getCommandMap().register("pufferfish", "Pufferfish", new PufferfishCommand());
+ }
@@ -616,7 +616,7 @@ index 0000000000000000000000000000000000000000..e164237e749bcc43466d4ed7aeada5ab
+}
diff --git a/src/main/java/gg/pufferfish/pufferfish/PufferfishConfig.java b/src/main/java/gg/pufferfish/pufferfish/PufferfishConfig.java
new file mode 100644
-index 0000000000000000000000000000000000000000..a8cead500186142115d4dc029c942fdfc68f7fe5
+index 0000000000000000000000000000000000000000..6e441a1a28ba72a8b1cc09fe5fca71b3c70627d4
--- /dev/null
+++ b/src/main/java/gg/pufferfish/pufferfish/PufferfishConfig.java
@@ -0,0 +1,285 @@
@@ -647,10 +647,10 @@ index 0000000000000000000000000000000000000000..a8cead500186142115d4dc029c942fdf
+import org.simpleyaml.exceptions.InvalidConfigurationException;
+
+public class PufferfishConfig {
-+
++
+ private static final YamlFile config = new YamlFile();
+ private static int updates = 0;
-+
++
+ private static ConfigurationSection convertToBukkit(org.simpleyaml.configuration.ConfigurationSection section) {
+ ConfigurationSection newSection = new MemoryConfiguration();
+ for (String key : section.getKeys(false)) {
@@ -662,18 +662,18 @@ index 0000000000000000000000000000000000000000..a8cead500186142115d4dc029c942fdf
+ }
+ return newSection;
+ }
-+
++
+ public static ConfigurationSection getConfigCopy() {
+ return convertToBukkit(config);
+ }
-+
++
+ public static int getUpdates() {
+ return updates;
+ }
-+
++
+ public static void load() throws IOException {
+ File configFile = new File("pufferfish.yml");
-+
++
+ if (configFile.exists()) {
+ try {
+ config.load(configFile);
@@ -681,14 +681,14 @@ index 0000000000000000000000000000000000000000..a8cead500186142115d4dc029c942fdf
+ throw new IOException(e);
+ }
+ }
-+
++
+ getString("info.version", "1.0");
+ setComment("info",
+ "Pufferfish Configuration",
+ "Check out Pufferfish Host for maximum performance server hosting: https://pufferfish.host",
+ "Join our Discord for support: https://discord.gg/reZw4vQV9H",
+ "Download new builds at https://ci.pufferfish.host/job/Pufferfish");
-+
++
+ for (Method method : PufferfishConfig.class.getDeclaredMethods()) {
+ if (Modifier.isStatic(method.getModifiers()) && Modifier.isPrivate(method.getModifiers()) && method.getParameterCount() == 0 &&
+ method.getReturnType() == Void.TYPE && !method.getName().startsWith("lambda")) {
@@ -700,11 +700,11 @@ index 0000000000000000000000000000000000000000..a8cead500186142115d4dc029c942fdf
+ }
+ }
+ }
-+
++
+ updates++;
-+
++
+ config.save(configFile);
-+
++
+ // Attempt to detect vectorization
+ try {
+ SIMDDetection.isEnabled = SIMDDetection.canEnable(PufferfishLogger.LOGGER);
@@ -712,7 +712,7 @@ index 0000000000000000000000000000000000000000..a8cead500186142115d4dc029c942fdf
+ } catch (NoClassDefFoundError | Exception ignored) {
+ ignored.printStackTrace();
+ }
-+
++
+ if (SIMDDetection.isEnabled) {
+ PufferfishLogger.LOGGER.info("SIMD operations detected as functional. Will replace some operations with faster versions.");
+ } else if (SIMDDetection.versionLimited) {
@@ -724,76 +724,76 @@ index 0000000000000000000000000000000000000000..a8cead500186142115d4dc029c942fdf
+ PufferfishLogger.LOGGER.warning("Debug: Java: " + System.getProperty("java.version") + ", test run: " + SIMDDetection.testRun);
+ }
+ }
-+
++
+ private static void setComment(String key, String... comment) {
+ if (config.contains(key)) {
+ config.setComment(key, String.join("\n", comment), CommentType.BLOCK);
+ }
+ }
-+
++
+ private static void ensureDefault(String key, Object defaultValue, String... comment) {
+ if (!config.contains(key)) {
+ config.set(key, defaultValue);
+ config.setComment(key, String.join("\n", comment), CommentType.BLOCK);
+ }
+ }
-+
++
+ private static boolean getBoolean(String key, boolean defaultValue, String... comment) {
+ return getBoolean(key, null, defaultValue, comment);
+ }
-+
++
+ private static boolean getBoolean(String key, @Nullable String oldKey, boolean defaultValue, String... comment) {
+ ensureDefault(key, defaultValue, comment);
+ return config.getBoolean(key, defaultValue);
+ }
-+
++
+ private static int getInt(String key, int defaultValue, String... comment) {
+ return getInt(key, null, defaultValue, comment);
+ }
-+
++
+ private static int getInt(String key, @Nullable String oldKey, int defaultValue, String... comment) {
+ ensureDefault(key, defaultValue, comment);
+ return config.getInt(key, defaultValue);
+ }
-+
++
+ private static double getDouble(String key, double defaultValue, String... comment) {
+ return getDouble(key, null, defaultValue, comment);
+ }
-+
++
+ private static double getDouble(String key, @Nullable String oldKey, double defaultValue, String... comment) {
+ ensureDefault(key, defaultValue, comment);
+ return config.getDouble(key, defaultValue);
+ }
-+
++
+ private static String getString(String key, String defaultValue, String... comment) {
+ return getOldString(key, null, defaultValue, comment);
+ }
-+
++
+ private static String getOldString(String key, @Nullable String oldKey, String defaultValue, String... comment) {
+ ensureDefault(key, defaultValue, comment);
+ return config.getString(key, defaultValue);
+ }
-+
++
+ private static List getStringList(String key, List defaultValue, String... comment) {
+ return getStringList(key, null, defaultValue, comment);
+ }
-+
++
+ private static List getStringList(String key, @Nullable String oldKey, List defaultValue, String... comment) {
+ ensureDefault(key, defaultValue, comment);
+ return config.getStringList(key);
+ }
-+
++
+ public static String sentryDsn;
+ private static void sentry() {
+ String sentryEnvironment = System.getenv("SENTRY_DSN");
+ String sentryConfig = getString("sentry-dsn", "", "Sentry DSN for improved error logging, leave blank to disable", "Obtain from https://sentry.io/");
-+
++
+ sentryDsn = sentryEnvironment == null ? sentryConfig : sentryEnvironment;
+ if (sentryDsn != null && !sentryDsn.isBlank()) {
+ gg.pufferfish.pufferfish.sentry.SentryManager.init();
+ }
+ }
-+
++
+ public static boolean enableBooks;
+ private static void books() {
+ enableBooks = getBoolean("enable-books", true,
@@ -802,7 +802,7 @@ index 0000000000000000000000000000000000000000..a8cead500186142115d4dc029c942fdf
+ "disabling this option.",
+ "This can be overridden per-player with the permission pufferfish.usebooks");
+ }
-+
++
+ public static boolean enableSuffocationOptimization;
+ private static void suffocationOptimization() {
+ enableSuffocationOptimization = getBoolean("enable-suffocation-optimization", true,
@@ -811,7 +811,7 @@ index 0000000000000000000000000000000000000000..a8cead500186142115d4dc029c942fdf
+ "be left enabled on most servers, but is provided as a",
+ "configuration option if the vanilla deviation is undesirable.");
+ }
-+
++
+ public static boolean enableAsyncMobSpawning;
+ public static boolean asyncMobSpawningInitialized;
+ private static void asyncMobSpawning() {
@@ -821,14 +821,14 @@ index 0000000000000000000000000000000000000000..a8cead500186142115d4dc029c942fdf
+ "paper's per-player-mob-spawns setting set to true for this to work.",
+ "One quick note - this does not actually spawn mobs async (that would be very unsafe).",
+ "This just offloads some expensive calculations that are required for mob spawning.");
-+
++
+ // This prevents us from changing the value during a reload.
+ if (!asyncMobSpawningInitialized) {
+ asyncMobSpawningInitialized = true;
+ enableAsyncMobSpawning = temp;
+ }
+ }
-+
++
+ public static int maxProjectileLoadsPerTick;
+ public static int maxProjectileLoadsPerProjectile;
+ private static void projectileLoading() {
@@ -870,7 +870,7 @@ index 0000000000000000000000000000000000000000..a8cead500186142115d4dc029c942fdf
+
+ setComment("dab", "Optimizes entity brains when", "they're far away from the player");
+ }
-+
++
+ public static Map projectileTimeouts;
+ private static void projectileTimeouts() {
+ // Set some defaults
@@ -881,13 +881,13 @@ index 0000000000000000000000000000000000000000..a8cead500186142115d4dc029c942fdf
+ "entity is in this list and it has survived for longer than",
+ "that number of ticks, then it will be removed. Setting a value to",
+ "-1 disables this feature.");
-+
++
+ for (EntityType> entityType : BuiltInRegistries.ENTITY_TYPE) {
+ String type = EntityType.getKey(entityType).getPath().toUpperCase(Locale.ROOT);
+ entityType.ttl = config.getInt("entity_timeouts." + type, -1);
+ }
+ }
-+
++
+ public static boolean throttleInactiveGoalSelectorTick;
+ private static void inactiveGoalSelectorThrottle() {
+ getBoolean("inactive-goal-selector-throttle", "inactive-goal-selector-disable", true,
@@ -929,7 +929,7 @@ index 0000000000000000000000000000000000000000..53f2df00c6809618a9ee3d2ea72e85e8
+}
diff --git a/src/main/java/gg/pufferfish/pufferfish/PufferfishVersionFetcher.java b/src/main/java/gg/pufferfish/pufferfish/PufferfishVersionFetcher.java
new file mode 100644
-index 0000000000000000000000000000000000000000..1adc3de7cdb5b5f309df45f463e4697d6ec2c245
+index 0000000000000000000000000000000000000000..e877921370f6009a4bd204d9b17d2d58834b8822
--- /dev/null
+++ b/src/main/java/gg/pufferfish/pufferfish/PufferfishVersionFetcher.java
@@ -0,0 +1,136 @@
@@ -961,46 +961,46 @@ index 0000000000000000000000000000000000000000..1adc3de7cdb5b5f309df45f463e4697d
+import org.jetbrains.annotations.Nullable;
+
+public class PufferfishVersionFetcher implements VersionFetcher {
-+
++
+ private static final Logger LOGGER = Logger.getLogger("PufferfishVersionFetcher");
+ private static final HttpClient client = HttpClient.newHttpClient();
-+
++
+ private static final URI JENKINS_URI = URI.create("https://ci.pufferfish.host/job/Pufferfish-1.19/lastSuccessfulBuild/buildNumber");
+ private static final String GITHUB_FORMAT = "https://api.github.com/repos/pufferfish-gg/Pufferfish/compare/ver/1.19...%s";
-+
++
+ private static final HttpResponse.BodyHandler JSON_OBJECT_BODY_HANDLER = responseInfo -> HttpResponse.BodySubscribers
+ .mapping(
+ HttpResponse.BodySubscribers.ofString(StandardCharsets.UTF_8),
+ string -> new Gson().fromJson(string, JsonObject.class)
+ );
-+
++
+ @Override
+ public long getCacheTime() {
+ return TimeUnit.MINUTES.toMillis(30);
+ }
-+
++
+ @Override
+ public @NotNull Component getVersionMessage(final @NotNull String serverVersion) {
+ final String[] parts = CraftServer.class.getPackage().getImplementationVersion().split("-");
+ @NotNull Component component;
-+
++
+ if (parts.length != 3) {
+ component = text("Unknown server version.", RED);
+ } else {
+ final String versionString = parts[2];
-+
++
+ try {
+ component = this.fetchJenkinsVersion(Integer.parseInt(versionString));
+ } catch (NumberFormatException e) {
+ component = this.fetchGithubVersion(versionString.substring(1, versionString.length() - 1));
+ }
+ }
-+
++
+ final @Nullable Component history = this.getHistory();
+ return history != null ? Component
+ .join(JoinConfiguration.noSeparators(), component, Component.newline(), this.getHistory()) : component;
+ }
-+
++
+ private @NotNull Component fetchJenkinsVersion(final int versionNumber) {
+ final HttpRequest request = HttpRequest.newBuilder(JENKINS_URI).build();
+ try {
@@ -1008,7 +1008,7 @@ index 0000000000000000000000000000000000000000..1adc3de7cdb5b5f309df45f463e4697d
+ if (response.statusCode() != 200) {
+ return text("Received invalid status code (" + response.statusCode() + ") from server.", RED);
+ }
-+
++
+ int latestVersionNumber;
+ try {
+ latestVersionNumber = Integer.parseInt(response.body());
@@ -1016,7 +1016,7 @@ index 0000000000000000000000000000000000000000..1adc3de7cdb5b5f309df45f463e4697d
+ LOGGER.log(Level.WARNING, "Received invalid response from Jenkins \"" + response.body() + "\".");
+ return text("Received invalid response from server.", RED);
+ }
-+
++
+ final int versionDiff = latestVersionNumber - versionNumber;
+ return this.getResponseMessage(versionDiff);
+ } catch (IOException | InterruptedException e) {
@@ -1024,7 +1024,7 @@ index 0000000000000000000000000000000000000000..1adc3de7cdb5b5f309df45f463e4697d
+ return text("Failed to retrieve version from server.", RED);
+ }
+ }
-+
++
+ // Based off code contributed by Techcable in Paper/GH-65
+ private @NotNull Component fetchGithubVersion(final @NotNull String hash) {
+ final URI uri = URI.create(String.format(GITHUB_FORMAT, hash));
@@ -1034,17 +1034,17 @@ index 0000000000000000000000000000000000000000..1adc3de7cdb5b5f309df45f463e4697d
+ if (response.statusCode() != 200) {
+ return text("Received invalid status code (" + response.statusCode() + ") from server.", RED);
+ }
-+
++
+ final JsonObject obj = response.body();
+ final int versionDiff = obj.get("behind_by").getAsInt();
-+
++
+ return this.getResponseMessage(versionDiff);
+ } catch (IOException | InterruptedException e) {
+ LOGGER.log(Level.WARNING, "Failed to look up version from GitHub", e);
+ return text("Failed to retrieve version from server.", RED);
+ }
+ }
-+
++
+ private @NotNull Component getResponseMessage(final int versionDiff) {
+ return switch (Math.max(-1, Math.min(1, versionDiff))) {
+ case -1 -> text("You are running an unsupported version of Pufferfish.", RED);
@@ -1054,25 +1054,25 @@ index 0000000000000000000000000000000000000000..1adc3de7cdb5b5f309df45f463e4697d
+ RED);
+ };
+ }
-+
++
+ private @Nullable Component getHistory() {
+ final VersionHistoryManager.VersionData data = VersionHistoryManager.INSTANCE.getVersionData();
+ if (data == null) {
+ return null;
+ }
-+
++
+ final String oldVersion = data.getOldVersion();
+ if (oldVersion == null) {
+ return null;
+ }
-+
++
+ return Component.text("Previous version: " + oldVersion, NamedTextColor.GRAY, TextDecoration.ITALIC);
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/gg/pufferfish/pufferfish/sentry/PufferfishSentryAppender.java b/src/main/java/gg/pufferfish/pufferfish/sentry/PufferfishSentryAppender.java
new file mode 100644
-index 0000000000000000000000000000000000000000..d04a8a4336566dbe6e1b9ec0d574cff43e003fa8
+index 0000000000000000000000000000000000000000..731ef11c7a025ae95ed8a757b530d834733d0621
--- /dev/null
+++ b/src/main/java/gg/pufferfish/pufferfish/sentry/PufferfishSentryAppender.java
@@ -0,0 +1,135 @@
@@ -1096,14 +1096,14 @@ index 0000000000000000000000000000000000000000..d04a8a4336566dbe6e1b9ec0d574cff4
+import org.apache.logging.log4j.core.filter.AbstractFilter;
+
+public class PufferfishSentryAppender extends AbstractAppender {
-+
++
+ private static final org.apache.logging.log4j.Logger logger = LogManager.getLogger(PufferfishSentryAppender.class);
+ private static final Gson GSON = new Gson();
-+
++
+ public PufferfishSentryAppender() {
+ super("PufferfishSentryAdapter", new SentryFilter(), null);
+ }
-+
++
+ @Override
+ public void append(LogEvent logEvent) {
+ if (logEvent.getThrown() != null && logEvent.getLevel().isMoreSpecificThan(Level.WARN)) {
@@ -1120,55 +1120,55 @@ index 0000000000000000000000000000000000000000..d04a8a4336566dbe6e1b9ec0d574cff4
+ }
+ }
+ }
-+
++
+ private void logException(LogEvent e) {
+ SentryEvent event = new SentryEvent(e.getThrown());
-+
++
+ Message sentryMessage = new Message();
+ sentryMessage.setMessage(e.getMessage().getFormattedMessage());
-+
++
+ event.setThrowable(e.getThrown());
+ event.setLevel(getLevel(e.getLevel()));
+ event.setLogger(e.getLoggerName());
+ event.setTransaction(e.getLoggerName());
+ event.setExtra("thread_name", e.getThreadName());
-+
++
+ boolean hasContext = e.getContextData() != null;
-+
++
+ if (hasContext && e.getContextData().containsKey("pufferfishsentry_playerid")) {
+ User user = new User();
+ user.setId(e.getContextData().getValue("pufferfishsentry_playerid"));
+ user.setUsername(e.getContextData().getValue("pufferfishsentry_playername"));
+ event.setUser(user);
+ }
-+
++
+ if (hasContext && e.getContextData().containsKey("pufferfishsentry_pluginname")) {
+ event.setExtra("plugin.name", e.getContextData().getValue("pufferfishsentry_pluginname"));
+ event.setExtra("plugin.version", e.getContextData().getValue("pufferfishsentry_pluginversion"));
+ event.setTransaction(e.getContextData().getValue("pufferfishsentry_pluginname"));
+ }
-+
++
+ if (hasContext && e.getContextData().containsKey("pufferfishsentry_eventdata")) {
+ Map eventFields = GSON.fromJson((String) e.getContextData().getValue("pufferfishsentry_eventdata"), new TypeToken