From a7a96a6b6e3844835e634714a58acad85c2cebc2 Mon Sep 17 00:00:00 2001 From: Cryptite Date: Wed, 6 Oct 2021 11:12:18 -0500 Subject: [PATCH] Packet obfuscation and reduction --- patches/server/0001-Build-Changes.patch | 12 +- ...4-Set-BlockData-without-light-update.patch | 8 +- ...ayer-throughout-entire-block-destroy.patch | 8 +- ...09-Add-BlockDestroyedByNeighborEvent.patch | 4 +- ...013-Packet-obfuscation-and-reduction.patch | 217 ++++++++++++++++++ 5 files changed, 233 insertions(+), 16 deletions(-) create mode 100644 patches/server/0013-Packet-obfuscation-and-reduction.patch diff --git a/patches/server/0001-Build-Changes.patch b/patches/server/0001-Build-Changes.patch index a9a24b232..104aa830a 100644 --- a/patches/server/0001-Build-Changes.patch +++ b/patches/server/0001-Build-Changes.patch @@ -5,10 +5,10 @@ Subject: [PATCH] Build Changes diff --git a/build.gradle.kts b/build.gradle.kts -index d80cfcb94db51440b5d0aa589a9a3d8a4189a9aa..9dbad4407dcf1a7070d5c5a692473cf57a4543e7 100644 +index cb1a931417073908be5c7aa1af710477775b3dbd..0c3c936990f161eb3de9a33f8983e4da571befac 100644 --- a/build.gradle.kts +++ b/build.gradle.kts -@@ -29,8 +29,8 @@ repositories { +@@ -28,8 +28,8 @@ repositories { } dependencies { @@ -19,7 +19,7 @@ index d80cfcb94db51440b5d0aa589a9a3d8a4189a9aa..9dbad4407dcf1a7070d5c5a692473cf5 // Paper start implementation("org.jline:jline-terminal-jansi:3.12.1") implementation("net.minecrell:terminalconsoleappender:1.2.0") -@@ -82,7 +82,7 @@ tasks.jar { +@@ -81,7 +81,7 @@ tasks.jar { attributes( "Main-Class" to "org.bukkit.craftbukkit.Main", "Implementation-Title" to "CraftBukkit", @@ -28,7 +28,7 @@ index d80cfcb94db51440b5d0aa589a9a3d8a4189a9aa..9dbad4407dcf1a7070d5c5a692473cf5 "Implementation-Vendor" to date, // Paper "Specification-Title" to "Bukkit", "Specification-Version" to project.version, -@@ -207,7 +207,7 @@ tasks.test { +@@ -206,7 +206,7 @@ tasks.test { fun TaskContainer.registerRunTask( name: String, block: JavaExec.() -> Unit ): TaskProvider = register(name) { @@ -51,10 +51,10 @@ index fe6d5051b139cd6079e288ffdf20e30fdd46fdda..c3c2b44c998ccb73594e4f33a571508a public SystemReport fillSystemReport(SystemReport details) { diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java -index f925130552cb4ff2f813b1ddae9ac2f915e17bd5..7df81489dd9d3c7ed84b3b6bdf2b724903fbcdc4 100644 +index 0adb686d690e1be0c5540625efbecc46bac5d045..d32b296d63e8f16ad4ba6e5e8da259d2905098ef 100644 --- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java +++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java -@@ -249,7 +249,7 @@ import javax.annotation.Nullable; // Paper +@@ -251,7 +251,7 @@ import javax.annotation.Nullable; // Paper import javax.annotation.Nonnull; // Paper public final class CraftServer implements Server { diff --git a/patches/server/0004-Set-BlockData-without-light-update.patch b/patches/server/0004-Set-BlockData-without-light-update.patch index c99307035..8eb7845fc 100644 --- a/patches/server/0004-Set-BlockData-without-light-update.patch +++ b/patches/server/0004-Set-BlockData-without-light-update.patch @@ -27,10 +27,10 @@ index 21d1e0c9c471e9e556b5bd70166a769b46105c7a..d46dc12001bd46596c3bb2e24144bbe4 return false; } else { diff --git a/src/main/java/net/minecraft/world/level/Level.java b/src/main/java/net/minecraft/world/level/Level.java -index b93056b91e7ebd49e6ddb53ccb6c05c056088df9..a9315b7acec3c2ccaf879e0f1d45c062d8e201b1 100644 +index f936e9f9a9fa655fa997d6862b5ed54c04169d35..465a1e179f6ae43d253d692255c8fe144179efe3 100644 --- a/src/main/java/net/minecraft/world/level/Level.java +++ b/src/main/java/net/minecraft/world/level/Level.java -@@ -550,12 +550,12 @@ public abstract class Level implements LevelAccessor, AutoCloseable { +@@ -552,12 +552,12 @@ public abstract class Level implements LevelAccessor, AutoCloseable { } @Override @@ -46,7 +46,7 @@ index b93056b91e7ebd49e6ddb53ccb6c05c056088df9..a9315b7acec3c2ccaf879e0f1d45c062 // CraftBukkit start - tree generation if (this.captureTreeGeneration) { // Paper start -@@ -602,7 +602,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable { +@@ -604,7 +604,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable { } else { BlockState iblockdata2 = this.getBlockState(pos); @@ -55,7 +55,7 @@ index b93056b91e7ebd49e6ddb53ccb6c05c056088df9..a9315b7acec3c2ccaf879e0f1d45c062 this.getProfiler().push("queueCheckLight"); this.getChunkSource().getLightEngine().checkBlock(pos); this.getProfiler().pop(); -@@ -749,7 +749,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable { +@@ -751,7 +751,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable { Block.dropResources(iblockdata, this, pos, tileentity, breakingEntity, ItemStack.EMPTY); } diff --git a/patches/server/0008-Track-Player-throughout-entire-block-destroy.patch b/patches/server/0008-Track-Player-throughout-entire-block-destroy.patch index fbde9ab97..b814e37ed 100644 --- a/patches/server/0008-Track-Player-throughout-entire-block-destroy.patch +++ b/patches/server/0008-Track-Player-throughout-entire-block-destroy.patch @@ -37,10 +37,10 @@ index 3e5e358e24bd84a05785a9391526f316475e95ff..9914a92040a63b6102eb6171f058ea1c CompoundTag newData = this.getTagClone(); int newCount = this.getCount(); diff --git a/src/main/java/net/minecraft/world/level/Level.java b/src/main/java/net/minecraft/world/level/Level.java -index a9315b7acec3c2ccaf879e0f1d45c062d8e201b1..7dec61b831860e372f1785c2ae838cb3ad2fc623 100644 +index 465a1e179f6ae43d253d692255c8fe144179efe3..46a1b0519aaccc2b5625104d42168254bfb27cd9 100644 --- a/src/main/java/net/minecraft/world/level/Level.java +++ b/src/main/java/net/minecraft/world/level/Level.java -@@ -175,6 +175,27 @@ public abstract class Level implements LevelAccessor, AutoCloseable { +@@ -176,6 +176,27 @@ public abstract class Level implements LevelAccessor, AutoCloseable { public final Map explosionDensityCache = new HashMap<>(); // Paper - Optimize explosions public java.util.ArrayDeque redstoneUpdateInfos; // Paper - Move from Map in BlockRedstoneTorch to here @@ -68,7 +68,7 @@ index a9315b7acec3c2ccaf879e0f1d45c062d8e201b1..7dec61b831860e372f1785c2ae838cb3 // Paper start - fix and optimise world upgrading // copied from below public static ResourceKey getDimensionKey(DimensionType manager) { -@@ -704,6 +725,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable { +@@ -706,6 +727,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable { if (!this.preventPoiUpdated) { this.onBlockStateChange(blockposition, iblockdata1, iblockdata2); } @@ -76,7 +76,7 @@ index a9315b7acec3c2ccaf879e0f1d45c062d8e201b1..7dec61b831860e372f1785c2ae838cb3 // CraftBukkit end } } -@@ -809,8 +831,20 @@ public abstract class Level implements LevelAccessor, AutoCloseable { +@@ -811,8 +833,20 @@ public abstract class Level implements LevelAccessor, AutoCloseable { public void neighborChanged(BlockPos pos, Block sourceBlock, BlockPos neighborPos) { if (!this.isClientSide) { BlockState iblockdata = this.getBlockState(pos); diff --git a/patches/server/0009-Add-BlockDestroyedByNeighborEvent.patch b/patches/server/0009-Add-BlockDestroyedByNeighborEvent.patch index d57d90d5a..72bc0a8fd 100644 --- a/patches/server/0009-Add-BlockDestroyedByNeighborEvent.patch +++ b/patches/server/0009-Add-BlockDestroyedByNeighborEvent.patch @@ -5,10 +5,10 @@ Subject: [PATCH] Add BlockDestroyedByNeighborEvent diff --git a/src/main/java/net/minecraft/world/level/Level.java b/src/main/java/net/minecraft/world/level/Level.java -index 7dec61b831860e372f1785c2ae838cb3ad2fc623..9b704693ea8d8677d3c25d5a3c08d4ef26aa1fc3 100644 +index 46a1b0519aaccc2b5625104d42168254bfb27cd9..5e87676aa9dfa26ac052aa0c1fce35b8822e7a50 100644 --- a/src/main/java/net/minecraft/world/level/Level.java +++ b/src/main/java/net/minecraft/world/level/Level.java -@@ -747,6 +747,17 @@ public abstract class Level implements LevelAccessor, AutoCloseable { +@@ -749,6 +749,17 @@ public abstract class Level implements LevelAccessor, AutoCloseable { if (iblockdata.isAir()) { return false; } else { diff --git a/patches/server/0013-Packet-obfuscation-and-reduction.patch b/patches/server/0013-Packet-obfuscation-and-reduction.patch new file mode 100644 index 000000000..33ea37ec8 --- /dev/null +++ b/patches/server/0013-Packet-obfuscation-and-reduction.patch @@ -0,0 +1,217 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Cryptite +Date: Wed, 6 Oct 2021 11:03:01 -0500 +Subject: [PATCH] Packet obfuscation and reduction + +Minecraft is overzealous about packet updates for Entities. In Loka's case, we want to reduce as many unnecessary +packet updates as possible. This patch is likely to be updated over and over in terms of reducing packet sends. + +In summary, this patch creates the concept of a "foreignValue" of a packet's data. We treat packets in two ways: +1) The packet sent to the player itself (the normal way). This always has all of the values as usual. +2) The packet data as seen by any other (foreign) players. + +This patch adds the ability to set a "foreignValue" for an entity value so as to obfuscate data received by other players. +The current packets modified/obfuscated are the following: + +- Health - Foreign Players will only receive packets that say the player is "alive or dead (max health or 0 health). + -- This reduces the amount of health packet updates as well which is great for players in combat. + +- Air Level - Foreign players will only ever see a player as having full oxygen + -- Air level packets are sent PER-TICK, and as such a player with any change in air level will only spam themselves + with packets instead of every single player within tracking distance + +- Score - Foreign players will only see a player's score as 0 and will not update due to gathering xp orbs, etc. + + + +diff --git a/src/main/java/net/minecraft/network/protocol/game/ClientboundSetEntityDataPacket.java b/src/main/java/net/minecraft/network/protocol/game/ClientboundSetEntityDataPacket.java +index 3e17f6131bf590d7c4a16b79c1c145cb4f565bc9..e1233fa58d068448d0accef7a7f6725fcb902848 100644 +--- a/src/main/java/net/minecraft/network/protocol/game/ClientboundSetEntityDataPacket.java ++++ b/src/main/java/net/minecraft/network/protocol/game/ClientboundSetEntityDataPacket.java +@@ -22,6 +22,13 @@ public class ClientboundSetEntityDataPacket implements Packet> packedItems) { ++ this.id = id; ++ this.packedItems = packedItems; ++ } ++ // Slice end ++ + public ClientboundSetEntityDataPacket(FriendlyByteBuf buf) { + this.id = buf.readVarInt(); + this.packedItems = SynchedEntityData.unpack(buf); +diff --git a/src/main/java/net/minecraft/network/syncher/SynchedEntityData.java b/src/main/java/net/minecraft/network/syncher/SynchedEntityData.java +index 4df12454001f0de5f358c88d876e34c35a736c42..72c74d2369a36b14f1103aa74b096f50e7990f4d 100644 +--- a/src/main/java/net/minecraft/network/syncher/SynchedEntityData.java ++++ b/src/main/java/net/minecraft/network/syncher/SynchedEntityData.java +@@ -136,6 +136,11 @@ public class SynchedEntityData { + } + + public void set(EntityDataAccessor key, T value) { ++ //Slice start ++ set(key, value, null); ++ } ++ ++ public void set(EntityDataAccessor key, T value, @Nullable T foreignValue) { // Slice end + SynchedEntityData.DataItem datawatcher_item = this.getItem(key); + + if (ObjectUtils.notEqual(value, datawatcher_item.getValue())) { +@@ -145,6 +150,11 @@ public class SynchedEntityData { + this.isDirty = true; + } + ++ // Slice start ++ if (foreignValue != null && ObjectUtils.notEqual(foreignValue, datawatcher_item.getForeignValue())) { ++ datawatcher_item.setForeignValue(foreignValue); ++ } ++ // Slice end + } + + // CraftBukkit start - add method from above +@@ -200,6 +210,28 @@ public class SynchedEntityData { + return list; + } + ++ // Slice start ++ @Nullable ++ public List> packForeignDirty(List> unpackedData) { ++ List> list = null; ++ ++ for (DataItem dataItem : unpackedData) { ++ DataItem item = itemsById.get(dataItem.accessor.getId()); ++ if (item.isDirty(true)) { ++ item.setForeignDirty(false); ++ ++ if (list == null) { ++ list = Lists.newArrayList(); ++ } ++ ++ list.add(item.copy(true)); ++ } ++ } ++ ++ return list; ++ } ++ // Slice end ++ + @Nullable + public List> getAll() { + List> list = null; +@@ -313,11 +345,14 @@ public class SynchedEntityData { + final EntityDataAccessor accessor; + T value; + private boolean dirty; ++ @Nullable T foreignValue = null; // Slice ++ private boolean foreignDirty; // Slice + + public DataItem(EntityDataAccessor data, T value) { + this.accessor = data; + this.value = value; + this.dirty = true; ++ this.foreignDirty = true; // Slice + } + + public EntityDataAccessor getAccessor() { +@@ -343,5 +378,34 @@ public class SynchedEntityData { + public SynchedEntityData.DataItem copy() { + return new SynchedEntityData.DataItem<>(this.accessor, this.accessor.getSerializer().copy(this.value)); + } ++ ++ // Slice start ++ public void setForeignValue(T foreignValue) { ++ this.foreignValue = foreignValue; ++ this.foreignDirty = true; ++ } ++ ++ public @Nullable T getForeignValue() { ++ return foreignValue; ++ } ++ ++ public boolean isDirty(boolean foreign) { ++ if (foreign) { ++ //There must be a foreign value in order for this to be dirty, otherwise we consider this a normal ++ //value and check the normal dirty flag. ++ return foreignValue == null || this.foreignDirty; ++ } ++ ++ return this.dirty; ++ } ++ ++ public void setForeignDirty(boolean dirty) { ++ this.foreignDirty = dirty; ++ } ++ ++ public SynchedEntityData.DataItem copy(boolean foreign) { ++ return new SynchedEntityData.DataItem<>(this.accessor, this.accessor.getSerializer().copy((foreign && this.foreignValue != null ? this.foreignValue : this.value))); ++ } ++ // Slice end + } + } +diff --git a/src/main/java/net/minecraft/server/level/ServerEntity.java b/src/main/java/net/minecraft/server/level/ServerEntity.java +index b7c9294fdd3d799d410afba4a1118aa371c98533..b5205bfc6f9834dc811df71bfb96494448c8dfed 100644 +--- a/src/main/java/net/minecraft/server/level/ServerEntity.java ++++ b/src/main/java/net/minecraft/server/level/ServerEntity.java +@@ -368,7 +368,21 @@ public class ServerEntity { + SynchedEntityData datawatcher = this.entity.getEntityData(); + + if (datawatcher.isDirty()) { +- this.broadcastAndSend(new ClientboundSetEntityDataPacket(this.entity.getId(), datawatcher, false)); ++ // Slice start ++ ClientboundSetEntityDataPacket dataPacket = new ClientboundSetEntityDataPacket(this.entity.getId(), datawatcher, false); ++ if (this.entity instanceof ServerPlayer serverPlayer) { ++ serverPlayer.connection.send(dataPacket); ++ ++ //Get the packedData that the original packet has, and then determine if any of those are changed in ++ //the foreign version. If null, nothing to notify foreign trackers about. ++ List> dirtyItems = datawatcher.packForeignDirty(dataPacket.getUnpackedData()); ++ if (dirtyItems != null) { ++ this.broadcast(new ClientboundSetEntityDataPacket(this.entity.getId(), dirtyItems)); ++ } ++ } else { ++ this.broadcastAndSend(dataPacket); ++ } ++ // Slice end + } + + if (this.entity instanceof LivingEntity) { +diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java +index 049a9ba9cc96880513e681f7d29bd5f9681fbb0e..37836b6f9b39ab1dcbfa1e4dea90e4d12fc9e5b9 100644 +--- a/src/main/java/net/minecraft/world/entity/Entity.java ++++ b/src/main/java/net/minecraft/world/entity/Entity.java +@@ -2944,7 +2944,7 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, n + if (event.isCancelled()) { + return; + } +- this.entityData.set(Entity.DATA_AIR_SUPPLY_ID, event.getAmount()); ++ this.entityData.set(Entity.DATA_AIR_SUPPLY_ID, event.getAmount(), getMaxAirSupply()); // Slice + // CraftBukkit end + } + +diff --git a/src/main/java/net/minecraft/world/entity/player/Player.java b/src/main/java/net/minecraft/world/entity/player/Player.java +index 4a35056b01b947c57f7ca06c974463008fc4bbef..80a8ea35b47cc649f435856cad19a80d69da8425 100644 +--- a/src/main/java/net/minecraft/world/entity/player/Player.java ++++ b/src/main/java/net/minecraft/world/entity/player/Player.java +@@ -633,7 +633,7 @@ public abstract class Player extends LivingEntity { + public void increaseScore(int score) { + int j = this.getScore(); + +- this.entityData.set(Player.DATA_SCORE_ID, j + score); ++ this.entityData.set(Player.DATA_SCORE_ID, j + score, 0); // Slice + } + + @Override +diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java +index c64911651f3d736c83cc83996de04920b091cc57..cf8d4edba400d5f21737c0af52a1193f69386fa9 100644 +--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java ++++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java +@@ -1997,7 +1997,7 @@ public class CraftPlayer extends CraftHumanEntity implements Player { + this.sendHealthUpdate(); + } + } +- this.getHandle().getEntityData().set(LivingEntity.DATA_HEALTH_ID, (float) this.getScaledHealth()); ++ this.getHandle().getEntityData().set(LivingEntity.DATA_HEALTH_ID, (float) this.getScaledHealth(), isDead() ? 0f : 20f); // Slice + + this.getHandle().maxHealthCache = getMaxHealth(); + }