diff --git a/patches/server/0028-Skip-event-if-no-listeners.patch b/patches/server/0028-Skip-event-if-no-listeners.patch new file mode 100644 index 0000000..d982627 --- /dev/null +++ b/patches/server/0028-Skip-event-if-no-listeners.patch @@ -0,0 +1,30 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: AlphaKR93 +Date: Mon, 4 Dec 2023 23:17:15 +0900 +Subject: [PATCH] Skip event if no listeners + + +diff --git a/src/main/java/io/papermc/paper/plugin/manager/PaperEventManager.java b/src/main/java/io/papermc/paper/plugin/manager/PaperEventManager.java +index 7ce9ebba8ce304d1f3f21d4f15ee5f3560d7700b..23594fb7eb4b2f33146592866608c2858ef23937 100644 +--- a/src/main/java/io/papermc/paper/plugin/manager/PaperEventManager.java ++++ b/src/main/java/io/papermc/paper/plugin/manager/PaperEventManager.java +@@ -36,15 +36,16 @@ class PaperEventManager { + + // SimplePluginManager + public void callEvent(@NotNull Event event) { ++ // Plazma start - Skip event if no listeners ++ RegisteredListener[] listeners = event.getHandlers().getRegisteredListeners(); ++ if (listeners.length == 0) return; ++ // Plazma end + if (event.isAsynchronous() && this.server.isPrimaryThread()) { + throw new IllegalStateException(event.getEventName() + " may only be triggered asynchronously."); + } else if (!event.isAsynchronous() && !this.server.isPrimaryThread() && !this.server.isStopping()) { + throw new IllegalStateException(event.getEventName() + " may only be triggered synchronously."); + } + +- HandlerList handlers = event.getHandlers(); +- RegisteredListener[] listeners = handlers.getRegisteredListeners(); +- + for (RegisteredListener registration : listeners) { + if (!registration.getPlugin().isEnabled()) { + continue; diff --git a/patches/server/0029-Add-entity-spawn-deadlock-timer.patch b/patches/server/0029-Add-entity-spawn-deadlock-timer.patch new file mode 100644 index 0000000..8d7c4c3 --- /dev/null +++ b/patches/server/0029-Add-entity-spawn-deadlock-timer.patch @@ -0,0 +1,81 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: AlphaKR93 +Date: Tue, 5 Dec 2023 13:29:28 +0900 +Subject: [PATCH] Add entity spawn deadlock timer + +[REFERENCE] +- AbsolemJackdaw/FixMySpawnR + +diff --git a/src/main/java/net/minecraft/world/level/BaseSpawner.java b/src/main/java/net/minecraft/world/level/BaseSpawner.java +index 967af8771ff8564c715d89f4b4b69b16c25add59..2ac5fb585636523787e05edaa58a4fa34a39ef28 100644 +--- a/src/main/java/net/minecraft/world/level/BaseSpawner.java ++++ b/src/main/java/net/minecraft/world/level/BaseSpawner.java +@@ -50,6 +50,8 @@ public abstract class BaseSpawner { + public int requiredPlayerRange = 16; + public int spawnRange = 4; + private int tickDelay = 0; // Paper - Configurable mob spawner tick rate ++ private int blockExistsTick = 0; // Plazma - Add entity spawn deadlock timer ++ private boolean blockLockedByTime = false; // Plazma - Add entity spawn deadlock timer + + public BaseSpawner() {} + +@@ -85,6 +87,17 @@ public abstract class BaseSpawner { + } + + public void serverTick(ServerLevel world, BlockPos pos) { ++ // Plazma start - Add entity spawn deadlock timer ++ if (world.plazmaConfig().entity.spawnDeadlockTimer.enabled) { ++ if (!this.blockLockedByTime) { ++ if (this.blockExistsTick > world.plazmaConfig().entity.spawnDeadlockTimer.timerTimeout) ++ blockLockedByTime = true; ++ else blockExistsTick++; ++ } ++ ++ if (blockLockedByTime && world.getBestNeighborSignal(pos) > 0) return; ++ } ++ // Plazma end - Add entity spawn deadlock timer + if (spawnCount <= 0 || maxNearbyEntities <= 0) return; // Paper - Ignore impossible spawn tick + // Paper start - Configurable mob spawner tick rate + if (spawnDelay > 0 && --tickDelay > 0) return; +@@ -290,6 +303,13 @@ public abstract class BaseSpawner { + this.spawnRange = nbt.getShort("SpawnRange"); + } + ++ // Plazma start - Add entity spawn deadlock timer ++ if (nbt.contains("Plazma.SpawnerTicks", 99)) { ++ this.blockExistsTick = nbt.getInt("Plazma.SpawnerTicks"); ++ this.blockLockedByTime = nbt.getBoolean("Plazma.SpawnerLocked"); ++ } ++ // Plazma end - Add entity spawn deadlock timer ++ + this.displayEntity = null; + } + +@@ -318,6 +338,8 @@ public abstract class BaseSpawner { + })); + } + ++ nbt.putInt("Plazma.SpawnerTicks", this.blockExistsTick); // Plazma - Add entity spawn deadlock timer ++ nbt.putBoolean("Plazma.SpawnerLocked", this.blockLockedByTime); // Plazma - Add entity spawn deadlock timer + nbt.put("SpawnPotentials", (Tag) SpawnData.LIST_CODEC.encodeStart(NbtOps.INSTANCE, this.spawnPotentials).getOrThrow()); + return nbt; + } +diff --git a/src/main/java/org/plazmamc/plazma/configurations/WorldConfigurations.java b/src/main/java/org/plazmamc/plazma/configurations/WorldConfigurations.java +index 29514dd01d46ba9f6b123bf3af56981541f670db..6a0cfec24618227d9a5ddc6c71e37d1986147799 100644 +--- a/src/main/java/org/plazmamc/plazma/configurations/WorldConfigurations.java ++++ b/src/main/java/org/plazmamc/plazma/configurations/WorldConfigurations.java +@@ -54,6 +54,14 @@ public class WorldConfigurations extends ConfigurationPart { + + } + ++ public SpawnDeadlockTimer spawnDeadlockTimer; ++ public class SpawnDeadlockTimer extends ConfigurationPart { ++ ++ public boolean enabled = OPTIMIZE; ++ public int timerTimeout = 0; ++ ++ } ++ + } + + public Structure structure; diff --git a/patches/server/0030-Lithium-HashedList.patch b/patches/server/0030-Lithium-HashedList.patch new file mode 100644 index 0000000..f1d44ef --- /dev/null +++ b/patches/server/0030-Lithium-HashedList.patch @@ -0,0 +1,306 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: AlphaKR93 +Date: Wed, 10 Jan 2024 18:08:59 +0900 +Subject: [PATCH] Lithium - HashedList + + +diff --git a/src/main/java/me/jellysquid/mods/lithium/common/util/collections/HashedReferenceList.java b/src/main/java/me/jellysquid/mods/lithium/common/util/collections/HashedReferenceList.java +new file mode 100644 +index 0000000000000000000000000000000000000000..d11579075e653868a43fe826bdf9b41ddc031b85 +--- /dev/null ++++ b/src/main/java/me/jellysquid/mods/lithium/common/util/collections/HashedReferenceList.java +@@ -0,0 +1,277 @@ ++package me.jellysquid.mods.lithium.common.util.collections; ++ ++import it.unimi.dsi.fastutil.objects.Reference2IntOpenHashMap; ++import it.unimi.dsi.fastutil.objects.ReferenceArrayList; ++import it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet; ++import org.jetbrains.annotations.NotNull; ++ ++import java.util.*; ++ ++/** ++ * Wraps a {@link List} with a hash table which provides O(1) lookups for {@link Collection#contains(Object)}. The type ++ * contained by this list must use reference-equality semantics. ++ */ ++@SuppressWarnings("SuspiciousMethodCalls") ++public class HashedReferenceList implements List { ++ private final ReferenceArrayList list; ++ private final Reference2IntOpenHashMap counter; ++ ++ public HashedReferenceList(List list) { ++ this.list = new ReferenceArrayList<>(); ++ this.list.addAll(list); ++ ++ this.counter = new Reference2IntOpenHashMap<>(); ++ this.counter.defaultReturnValue(0); ++ ++ for (T obj : this.list) { ++ this.counter.addTo(obj, 1); ++ } ++ } ++ ++ @Override ++ public int size() { ++ return this.list.size(); ++ } ++ ++ @Override ++ public boolean isEmpty() { ++ return this.list.isEmpty(); ++ } ++ ++ @Override ++ public boolean contains(Object o) { ++ return this.counter.containsKey(o); ++ } ++ ++ @Override ++ public @NotNull Iterator iterator() { ++ return this.listIterator(); ++ } ++ ++ @Override ++ public Object @NotNull [] toArray() { ++ return this.list.toArray(); ++ } ++ ++ @Override ++ public T1 @NotNull [] toArray(T1 @NotNull [] a) { ++ return this.list.toArray(a); ++ } ++ ++ @Override ++ public boolean add(T t) { ++ this.trackReferenceAdded(t); ++ ++ return this.list.add(t); ++ } ++ ++ @Override ++ public boolean remove(Object o) { ++ this.trackReferenceRemoved(o); ++ ++ return this.list.remove(o); ++ } ++ ++ @Override ++ public boolean containsAll(Collection c) { ++ for (Object obj : c) { ++ if (!this.counter.containsKey(obj)) { ++ return false; ++ } ++ } ++ ++ return true; ++ } ++ ++ @Override ++ public boolean addAll(Collection c) { ++ for (T obj : c) { ++ this.trackReferenceAdded(obj); ++ } ++ ++ return this.list.addAll(c); ++ } ++ ++ @Override ++ public boolean addAll(int index, Collection c) { ++ for (T obj : c) { ++ this.trackReferenceAdded(obj); ++ } ++ ++ return this.list.addAll(index, c); ++ } ++ ++ @Override ++ public boolean removeAll(@NotNull Collection c) { ++ if (this.size() >= 2 && c.size() > 4 && c instanceof List) { ++ //HashReferenceList uses reference equality, so using ReferenceOpenHashSet is fine ++ c = new ReferenceOpenHashSet<>(c); ++ } ++ this.counter.keySet().removeAll(c); ++ return this.list.removeAll(c); ++ } ++ ++ @Override ++ public boolean retainAll(@NotNull Collection c) { ++ this.counter.keySet().retainAll(c); ++ return this.list.retainAll(c); ++ } ++ ++ @Override ++ public void clear() { ++ this.counter.clear(); ++ this.list.clear(); ++ } ++ ++ @Override ++ public T get(int index) { ++ return this.list.get(index); ++ } ++ ++ @Override ++ public T set(int index, T element) { ++ T prev = this.list.set(index, element); ++ ++ if (prev != element) { ++ if (prev != null) { ++ this.trackReferenceRemoved(prev); ++ } ++ ++ this.trackReferenceAdded(element); ++ } ++ ++ return prev; ++ } ++ ++ @Override ++ public void add(int index, T element) { ++ this.trackReferenceAdded(element); ++ ++ this.list.add(index, element); ++ } ++ ++ @Override ++ public T remove(int index) { ++ T prev = this.list.remove(index); ++ ++ if (prev != null) { ++ this.trackReferenceRemoved(prev); ++ } ++ ++ return prev; ++ } ++ ++ @Override ++ public int indexOf(Object o) { ++ return this.list.indexOf(o); ++ } ++ ++ @Override ++ public int lastIndexOf(Object o) { ++ return this.list.lastIndexOf(o); ++ } ++ ++ @Override ++ public @NotNull ListIterator listIterator() { ++ return this.listIterator(0); ++ } ++ ++ @Override ++ public @NotNull ListIterator listIterator(int index) { ++ return new ListIterator<>() { ++ private final ListIterator inner = HashedReferenceList.this.list.listIterator(index); ++ ++ @Override ++ public boolean hasNext() { ++ return this.inner.hasNext(); ++ } ++ ++ @Override ++ public T next() { ++ return this.inner.next(); ++ } ++ ++ @Override ++ public boolean hasPrevious() { ++ return this.inner.hasPrevious(); ++ } ++ ++ @Override ++ public T previous() { ++ return this.inner.previous(); ++ } ++ ++ @Override ++ public int nextIndex() { ++ return this.inner.nextIndex(); ++ } ++ ++ @Override ++ public int previousIndex() { ++ return this.inner.previousIndex(); ++ } ++ ++ @Override ++ public void remove() { ++ int last = this.previousIndex(); ++ ++ if (last == -1) { ++ throw new NoSuchElementException(); ++ } ++ ++ T prev = HashedReferenceList.this.get(last); ++ ++ if (prev != null) { ++ HashedReferenceList.this.trackReferenceRemoved(prev); ++ } ++ ++ this.inner.remove(); ++ } ++ ++ @Override ++ public void set(T t) { ++ int last = this.previousIndex(); ++ ++ if (last == -1) { ++ throw new NoSuchElementException(); ++ } ++ ++ T prev = HashedReferenceList.this.get(last); ++ ++ if (prev != t) { ++ if (prev != null) { ++ HashedReferenceList.this.trackReferenceRemoved(prev); ++ } ++ ++ HashedReferenceList.this.trackReferenceAdded(t); ++ } ++ ++ this.inner.remove(); ++ } ++ ++ @Override ++ public void add(T t) { ++ HashedReferenceList.this.trackReferenceAdded(t); ++ ++ this.inner.add(t); ++ } ++ }; ++ } ++ ++ @Override ++ public @NotNull List subList(int fromIndex, int toIndex) { ++ return this.list.subList(fromIndex, toIndex); ++ } ++ ++ private void trackReferenceAdded(T t) { ++ this.counter.addTo(t, 1); ++ } ++ ++ @SuppressWarnings("unchecked") ++ private void trackReferenceRemoved(Object o) { ++ if (this.counter.addTo((T) o, -1) <= 1) { ++ this.counter.removeInt(o); ++ } ++ } ++ ++} +diff --git a/src/main/java/net/minecraft/util/random/WeightedRandomList.java b/src/main/java/net/minecraft/util/random/WeightedRandomList.java +index ef44047c3ea850fe52370b8176efbdf0515d20d6..30efa8704c1ae94027272602687cbc4a2bc8a772 100644 +--- a/src/main/java/net/minecraft/util/random/WeightedRandomList.java ++++ b/src/main/java/net/minecraft/util/random/WeightedRandomList.java +@@ -10,10 +10,10 @@ import net.minecraft.util.RandomSource; + + public class WeightedRandomList { + private final int totalWeight; +- private final ImmutableList items; ++ private final List items; // Plazma - Lithium: collections.mob_spawning + + WeightedRandomList(List entries) { +- this.items = ImmutableList.copyOf(entries); ++ this.items = entries.size() > 4 ? ImmutableList.copyOf(entries) : java.util.Collections.unmodifiableList(new me.jellysquid.mods.lithium.common.util.collections.HashedReferenceList<>(entries)); // Plazma - Lithium: collections.mob_spawning + this.totalWeight = WeightedRandom.getTotalWeight(entries); + } + diff --git a/patches/server/0031-Improve-SwingTime-ticking.patch b/patches/server/0031-Improve-SwingTime-ticking.patch new file mode 100644 index 0000000..f0976ed --- /dev/null +++ b/patches/server/0031-Improve-SwingTime-ticking.patch @@ -0,0 +1,18 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: AlphaKR93 +Date: Wed, 10 Jan 2024 18:11:03 +0900 +Subject: [PATCH] Improve SwingTime ticking + + +diff --git a/src/main/java/net/minecraft/world/entity/LivingEntity.java b/src/main/java/net/minecraft/world/entity/LivingEntity.java +index fa698cfefccdddf5e5e9938a2959004c70f743a7..b24754e36e210a635524be099cffe4553220a6a9 100644 +--- a/src/main/java/net/minecraft/world/entity/LivingEntity.java ++++ b/src/main/java/net/minecraft/world/entity/LivingEntity.java +@@ -2654,6 +2654,7 @@ public abstract class LivingEntity extends Entity implements Attackable { + } + + protected void updateSwingTime() { ++ if (!this.swinging && this.swingTime == 0) return; // Plazma - Improve SwingTime ticking + int i = this.getCurrentSwingDuration(); + + if (this.swinging) { diff --git a/patches/server/0032-Save-Json-list-asynchronously.patch b/patches/server/0032-Save-Json-list-asynchronously.patch new file mode 100644 index 0000000..320733f --- /dev/null +++ b/patches/server/0032-Save-Json-list-asynchronously.patch @@ -0,0 +1,152 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: AlphaKR93 +Date: Thu, 11 Jan 2024 13:40:41 +0900 +Subject: [PATCH] Save Json list asynchronously + + +diff --git a/src/main/java/net/minecraft/server/dedicated/DedicatedPlayerList.java b/src/main/java/net/minecraft/server/dedicated/DedicatedPlayerList.java +index 22c4f8dea99f92a1eb3da2baf0a15bf9d2ca0462..20c531f11b310dab0a867e589c769393ed835df5 100644 +--- a/src/main/java/net/minecraft/server/dedicated/DedicatedPlayerList.java ++++ b/src/main/java/net/minecraft/server/dedicated/DedicatedPlayerList.java +@@ -58,21 +58,23 @@ public class DedicatedPlayerList extends PlayerList { + this.loadWhiteList(); + } + ++ // Plazma start - Save JSON list asynchronously + private void saveIpBanList() { +- try { +- this.getIpBans().save(); +- } catch (IOException var2) { +- LOGGER.warn("Failed to save ip banlist: ", (Throwable)var2); +- } ++ this.getIpBans().save(); + } + + private void saveUserBanList() { +- try { +- this.getBans().save(); +- } catch (IOException var2) { +- LOGGER.warn("Failed to save user banlist: ", (Throwable)var2); +- } ++ this.getBans().save(); ++ } ++ ++ private void saveOps() { ++ this.getOps().save(); ++ } ++ ++ private void saveWhiteList() { ++ this.getWhiteList().save(); + } ++ // Plazma end - Save JSON list asynchronously + + private void loadIpBanList() { + try { +@@ -98,14 +100,6 @@ public class DedicatedPlayerList extends PlayerList { + } + } + +- private void saveOps() { +- try { +- this.getOps().save(); +- } catch (Exception var2) { +- LOGGER.warn("Failed to save operators list: ", (Throwable)var2); +- } +- } +- + private void loadWhiteList() { + try { + this.getWhiteList().load(); +@@ -114,14 +108,6 @@ public class DedicatedPlayerList extends PlayerList { + } + } + +- private void saveWhiteList() { +- try { +- this.getWhiteList().save(); +- } catch (Exception var2) { +- LOGGER.warn("Failed to save white-list: ", (Throwable)var2); +- } +- } +- + @Override + public boolean isWhiteListed(GameProfile profile) { + return !this.isUsingWhitelist() || this.isOp(profile) || this.getWhiteList().isWhiteListed(profile); +diff --git a/src/main/java/net/minecraft/server/players/StoredUserList.java b/src/main/java/net/minecraft/server/players/StoredUserList.java +index c038da20b76c0b7b1c18471b20be01e849d29f3a..0735a0bd182635e1969d19336b46bc72b14e555f 100644 +--- a/src/main/java/net/minecraft/server/players/StoredUserList.java ++++ b/src/main/java/net/minecraft/server/players/StoredUserList.java +@@ -42,13 +42,7 @@ public abstract class StoredUserList> { + + public void add(V entry) { + this.map.put(this.getKeyForUser(entry.getUser()), entry); +- +- try { +- this.save(); +- } catch (IOException ioexception) { +- StoredUserList.LOGGER.warn("Could not save the list after adding a user.", ioexception); +- } +- ++ this.save(); // Plazma - Save Json list asynchronously + } + + @Nullable +@@ -62,13 +56,7 @@ public abstract class StoredUserList> { + + public void remove(K key) { + this.map.remove(this.getKeyForUser(key)); +- +- try { +- this.save(); +- } catch (IOException ioexception) { +- StoredUserList.LOGGER.warn("Could not save the list after removing a user.", ioexception); +- } +- ++ this.save(); // Plazma - Save Json list asynchronously + } + + public void remove(StoredUserEntry entry) { +@@ -102,7 +90,9 @@ public abstract class StoredUserList> { + return this.map.values(); + } + +- public void save() throws IOException { ++ // Plazma start - Save Json list asynchronously ++ public void save()/* throws IOException*/ { ++ io.papermc.paper.util.MCUtil.scheduleAsyncTask(() -> { + this.removeExpired(); // Paper - remove expired values before saving + JsonArray jsonarray = new JsonArray(); + Stream stream = this.map.values().stream().map((jsonlistentry) -> { // CraftBukkit - decompile error +@@ -114,27 +104,16 @@ public abstract class StoredUserList> { + + Objects.requireNonNull(jsonarray); + stream.forEach(jsonarray::add); +- BufferedWriter bufferedwriter = Files.newWriter(this.file, StandardCharsets.UTF_8); + +- try { ++ try (BufferedWriter bufferedwriter = Files.newWriter(this.file, StandardCharsets.UTF_8)) { + StoredUserList.GSON.toJson(jsonarray, StoredUserList.GSON.newJsonWriter(bufferedwriter)); +- } catch (Throwable throwable) { +- if (bufferedwriter != null) { +- try { +- bufferedwriter.close(); +- } catch (Throwable throwable1) { +- throwable.addSuppressed(throwable1); +- } +- } +- +- throw throwable; +- } +- +- if (bufferedwriter != null) { +- bufferedwriter.close(); ++ } catch (IOException e) { ++ StoredUserList.LOGGER.warn("Failed to asynchronously save file " + this.file, e); + } + ++ }); + } ++ // Plazma end - Save Json list asynchronously + + public void load() throws IOException { + if (this.file.exists()) { diff --git a/patches/server/0033-Implement-FreedomChat.patch b/patches/server/0033-Implement-FreedomChat.patch new file mode 100644 index 0000000..54fb22b --- /dev/null +++ b/patches/server/0033-Implement-FreedomChat.patch @@ -0,0 +1,164 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: AlphaKR93 +Date: Mon, 29 Apr 2024 14:49:37 +0900 +Subject: [PATCH] Implement FreedomChat + +ocelotpotpie/FreedomChat +Copyright (C) 2022-2024 ocelotpotpie, Licensed under GNU GPL v3.0 + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +diff --git a/src/main/java/net/minecraft/server/players/PlayerList.java b/src/main/java/net/minecraft/server/players/PlayerList.java +index 2707a96f9e22469d008ec7d2fa8a8c497efaf8cf..9eedd395b3eac5e2511c8267781938a3fd994d90 100644 +--- a/src/main/java/net/minecraft/server/players/PlayerList.java ++++ b/src/main/java/net/minecraft/server/players/PlayerList.java +@@ -135,6 +135,7 @@ public abstract class PlayerList { + private static final Logger LOGGER = LogUtils.getLogger(); + private static final int SEND_PLAYER_INFO_INTERVAL = 600; + private static final SimpleDateFormat BAN_DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd 'at' HH:mm:ss z"); ++ private static final ru.bk.oharass.freedomchat.FreedomChat FREEDOM_HANDLER = new ru.bk.oharass.freedomchat.FreedomChat(); // Plazma - Implement FreedomChat + private final MinecraftServer server; + public final List players = new java.util.concurrent.CopyOnWriteArrayList(); // CraftBukkit - ArrayList -> CopyOnWriteArrayList: Iterator safety + private final Map playersByUUID = Maps.newHashMap(); +@@ -285,6 +286,7 @@ public abstract class PlayerList { + // CraftBukkit - Moved message to after join + // PlayerList.LOGGER.info("{}[{}] logged in with entity id {} at ({}, {}, {})", new Object[]{entityplayer.getName().getString(), s1, entityplayer.getId(), entityplayer.getX(), entityplayer.getY(), entityplayer.getZ()}); + LevelData worlddata = worldserver1.getLevelData(); ++ connection.channel.pipeline().addAfter("packet_handler", "freedom_handler", FREEDOM_HANDLER); // Plazma - Implement FreedomChat + + player.loadGameTypes((CompoundTag) optional.orElse(null)); // CraftBukkit - decompile error + ServerGamePacketListenerImpl playerconnection = new ServerGamePacketListenerImpl(this.server, connection, player, clientData); +diff --git a/src/main/java/org/plazmamc/plazma/configurations/GlobalConfiguration.java b/src/main/java/org/plazmamc/plazma/configurations/GlobalConfiguration.java +index 4f3ed01e12ccb8a6f1a5c4f605451bb36370a236..98f26e3de66a881163e84295e9156c7f362bf7cb 100644 +--- a/src/main/java/org/plazmamc/plazma/configurations/GlobalConfiguration.java ++++ b/src/main/java/org/plazmamc/plazma/configurations/GlobalConfiguration.java +@@ -29,6 +29,16 @@ public class GlobalConfiguration extends ConfigurationPart { + + public boolean checkSpectatorMovedToQuickly = !OPTIMIZE; + ++ public FreedomChat freedomChat; ++ public class FreedomChat extends ConfigurationPart { ++ ++ public boolean enabled = false; ++ public boolean rewriteChat = true; ++ public boolean claimSecureChatEnforced = true; ++ public boolean noChatReports = true; ++ ++ } ++ + } + + @Setting("world-generation") +diff --git a/src/main/java/ru/bk/oharass/freedomchat/FreedomChat.java b/src/main/java/ru/bk/oharass/freedomchat/FreedomChat.java +new file mode 100644 +index 0000000000000000000000000000000000000000..67f21c23e80881f52e7b92f22c6cbbe3a0bd55b5 +--- /dev/null ++++ b/src/main/java/ru/bk/oharass/freedomchat/FreedomChat.java +@@ -0,0 +1,96 @@ ++package ru.bk.oharass.freedomchat; ++ ++import com.google.gson.JsonObject; ++import com.mojang.logging.LogUtils; ++import com.mojang.serialization.JsonOps; ++import io.netty.buffer.ByteBuf; ++import io.netty.channel.ChannelHandler; ++import io.netty.channel.ChannelHandlerContext; ++import io.netty.handler.codec.EncoderException; ++import io.netty.handler.codec.MessageToByteEncoder; ++import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer; ++import net.minecraft.network.Connection; ++import net.minecraft.network.FriendlyByteBuf; ++import net.minecraft.network.chat.ChatType; ++import net.minecraft.network.chat.Component; ++import net.minecraft.network.protocol.Packet; ++import net.minecraft.network.protocol.game.ClientboundPlayerChatPacket; ++import net.minecraft.network.protocol.game.ClientboundServerDataPacket; ++import net.minecraft.network.protocol.game.ClientboundSystemChatPacket; ++import net.minecraft.network.protocol.status.ClientboundStatusResponsePacket; ++import net.minecraft.network.protocol.status.ServerStatus; ++import org.slf4j.Logger; ++ ++import java.util.Optional; ++ ++import static org.plazmamc.plazma.configurations.GlobalConfiguration.get; ++import static net.minecraft.server.MinecraftServer.getServer; ++ ++@ChannelHandler.Sharable ++public class FreedomChat extends MessageToByteEncoder> { ++ ++ private static final Logger LOGGER = LogUtils.getLogger(); ++ ++ @Override ++ public boolean acceptOutboundMessage(final Object msg) { ++ return get().player.freedomChat.rewriteChat && msg instanceof ClientboundPlayerChatPacket ++ || get().player.freedomChat.claimSecureChatEnforced && msg instanceof ClientboundServerDataPacket ++ || get().player.freedomChat.noChatReports && msg instanceof ClientboundStatusResponsePacket; ++ } ++ ++ @Override ++ protected void encode(final ChannelHandlerContext context, final Packet packet, final ByteBuf byteBuf) throws Exception { ++ final FriendlyByteBuf buf = new FriendlyByteBuf(byteBuf); ++ ++ // TODO: JDK 21 - Change to pattern matching ++ if (packet instanceof ClientboundPlayerChatPacket chat) ++ encodeChat(context, chat, buf); ++ else if (packet instanceof ClientboundServerDataPacket data) ++ encodeData(context, data, buf); ++ else if (packet instanceof ClientboundStatusResponsePacket query) ++ encodeQuery(context, query, buf); ++ } ++ ++ private static void encodeChat( ++ final ChannelHandlerContext context, final ClientboundPlayerChatPacket packet, final FriendlyByteBuf byteBuf ++ ) { ++ final Optional bound = packet.chatType().resolve(getServer().registryAccess()); ++ if (bound.isEmpty()) { ++ LOGGER.warn("Failed to resolve chat type: {}", packet.chatType().chatType(), new Throwable()); ++ return; ++ } ++ ++ final ClientboundSystemChatPacket after = new ClientboundSystemChatPacket( bound.orElseThrow().decorate( ++ Optional.ofNullable( packet.unsignedContent() ).orElse( Component.literal(packet.body().content()) ) ++ ), false ); ++ write(context, after, byteBuf); ++ after.write(byteBuf); ++ } ++ ++ private static void encodeData( ++ final ChannelHandlerContext context, final ClientboundServerDataPacket packet, final FriendlyByteBuf byteBuf ++ ) { ++ write(context, packet, byteBuf); ++ byteBuf.writeComponent(packet.getMotd()); ++ byteBuf.writeOptional(packet.getIconBytes(), FriendlyByteBuf::writeByteArray); ++ byteBuf.writeBoolean(true); ++ } ++ ++ private static void encodeQuery( ++ final ChannelHandlerContext context, final ClientboundStatusResponsePacket packet, final FriendlyByteBuf byteBuf ++ ) { ++ final JsonObject status = ServerStatus.CODEC.encodeStart(JsonOps.INSTANCE, packet.status()).get().left() ++ .orElseThrow(() -> new EncoderException("Failed to encode server status query response")).getAsJsonObject(); ++ ++ status.addProperty("preventsChatReports", true); ++ write(context, packet, byteBuf); ++ byteBuf.writeUtf(GsonComponentSerializer.gson().serializer().toJson(status)); ++ } ++ ++ private static void write( ++ final ChannelHandlerContext context, final Packet packet, final FriendlyByteBuf byteBuf ++ ) { ++ byteBuf.writeVarInt(context.channel().attr(Connection.ATTRIBUTE_CLIENTBOUND_PROTOCOL).get().packetId(packet)); ++ } ++ ++}