papermc/CraftBukkit-Patches/0020-Add-oreobfuscator-for-Spigot.patch
md_5 a65fedb3f8 Mob spawning issues - 'fix'. See below for ideal reasoning from MikePrimm, however until ideal reasoning we must live with the CraftBukkit / Vanilla behaviour since this causes far too many issues.
IIRC, the main item I was driving towards was a consequence of persistent passive mobs - specifically, the fact that allowing a population limit of N (independent of view distance, which is what vanilla does) when your view distance limits actual loaded chunks to a much smaller area than default (say a view of 4, which would be 9 x 9 chunks loaded per player - and spawnable - versus default, which is 8 radius spawn, or 17 x 17 chunks) tends to result in more mobs per chunk. For persistent mobs, this is bad - since they count for server load and for population just by being loaded, versus being despawned beyond 128 blocks (8 chunk radius - unconditional of view distance, as well) - so they can cause the population limit to be reached more easily, cutting off spawning nearer to players. The goal was to make it so that the mobs-per-loaded-chunk was about the same for all view distances, versus having low view distances cause higher mob concentrations.

    Now, all of this assumes that loaded chunks beyond those around players are modest (since they could contain passive mobs that would count towards the limits) - which they should be, except that recent CBs leak chunks like mad, from what I can see (chunk-gc has become more required than optional), and I think there may be some issues with even hostile mobs "lurking" around - possibly even after their chunks are unloaded. Anything that causes more mobs to be in places players don't see them is going to drive population limit issues, and resulting low spawn behaviors. The trick for us, trying to make big servers as practical as possible, is to shift the math the other way - given low view distances, how to best make sure that folks get reasonable spawn behavior while minimizing the time/resources spent on the server on mobs that don't help that. Realistically, I think we need to analyse the mob demographics better - especially as it relates to lower view distances (our changes have no net impact on view distances above 7) - particularly to understand how the proportion of "useful" mobs is working out (ones close enough to players to be considered contributing to game play). One thought is to manage the population limit based on mobs that are 'tickable' - if they aren't close enough to be ticking, they aren't interesting. This is likely a big issue for view distance 5 folks, since mobs cannot spawn closer than 24 (approx 1 chunk radius, given that middle chunk is 0), and don't tick when any of the chunks within a 2 chunk radius aren't loaded) - so, effectively, they "live" within a zone of 7 x 7 with the middle 3 x 3 removed (so, about 40 chunks) out of a total load zone of 11 x 11 (121) - so about 2/3 of the area containing mobs has idle mobs. Normal view distance would result in mobs ticking as far out as they can spawn (radius 8 versus a load radius of 11), so 100% of the mobs that spawn are ticking when they spawn, and all hostile mobs that are loaded are ticking (since they always despawn beyond 128 blocks / 8 chunks from a player). One interesting thought would be to limit the chunks we spawn mobs in to those where they would be ticking initially (that is, view-distance minus 2 or 3).
2013-01-29 16:57:06 +11:00

288 lines
14 KiB
Diff

From 116b25dc302af3d7059ee7fc101a570d19362a5f Mon Sep 17 00:00:00 2001
From: lishid <lishid@gmail.com>
Date: Mon, 21 Jan 2013 16:59:04 +1100
Subject: [PATCH] Add oreobfuscator for Spigot.
---
src/main/java/net/minecraft/server/Explosion.java | 1 +
.../net/minecraft/server/Packet51MapChunk.java | 1 +
.../net/minecraft/server/Packet56MapChunkBulk.java | 21 ++++-
.../minecraft/server/PlayerInteractManager.java | 5 ++
.../java/org/bukkit/craftbukkit/CraftServer.java | 5 ++
.../java/org/bukkit/craftbukkit/CraftWorld.java | 4 +
.../bukkit/craftbukkit/OrebfuscatorManager.java | 95 ++++++++++++++++++++++
src/main/java/org/bukkit/craftbukkit/Spigot.java | 4 +
src/main/resources/configurations/bukkit.yml | 5 ++
9 files changed, 140 insertions(+), 1 deletion(-)
create mode 100644 src/main/java/org/bukkit/craftbukkit/OrebfuscatorManager.java
diff --git a/src/main/java/net/minecraft/server/Explosion.java b/src/main/java/net/minecraft/server/Explosion.java
index ba2f88f..8d5b1d8 100644
--- a/src/main/java/net/minecraft/server/Explosion.java
+++ b/src/main/java/net/minecraft/server/Explosion.java
@@ -240,6 +240,7 @@ public class Explosion {
j = chunkposition.y;
k = chunkposition.z;
l = this.world.getTypeId(i, j, k);
+ org.bukkit.craftbukkit.OrebfuscatorManager.updateNearbyBlocks(world, i, j, k); // Spigot (Orebfuscator)
if (flag) {
double d0 = (double) ((float) i + this.world.random.nextFloat());
double d1 = (double) ((float) j + this.world.random.nextFloat());
diff --git a/src/main/java/net/minecraft/server/Packet51MapChunk.java b/src/main/java/net/minecraft/server/Packet51MapChunk.java
index b51d90c..365116a 100644
--- a/src/main/java/net/minecraft/server/Packet51MapChunk.java
+++ b/src/main/java/net/minecraft/server/Packet51MapChunk.java
@@ -46,6 +46,7 @@ public class Packet51MapChunk extends Packet {
this.d = chunkmap.c;
this.c = chunkmap.b;
+ org.bukkit.craftbukkit.OrebfuscatorManager.obfuscate(chunk.x, chunk.z, i, chunkmap.a, chunk.world); // Spigot (Orebfuscator)
try {
this.inflatedBuffer = chunkmap.a;
diff --git a/src/main/java/net/minecraft/server/Packet56MapChunkBulk.java b/src/main/java/net/minecraft/server/Packet56MapChunkBulk.java
index 867ebd3..9d5cee7 100644
--- a/src/main/java/net/minecraft/server/Packet56MapChunkBulk.java
+++ b/src/main/java/net/minecraft/server/Packet56MapChunkBulk.java
@@ -28,6 +28,7 @@ public class Packet56MapChunkBulk extends Packet {
}
};
// CraftBukkit end
+ private World world; // Spigot (Orebfuscator) Keep track of world
public Packet56MapChunkBulk() {}
@@ -46,6 +47,9 @@ public class Packet56MapChunkBulk extends Packet {
Chunk chunk = (Chunk) list.get(k);
ChunkMap chunkmap = Packet51MapChunk.a(chunk, true, '\uffff');
+ world = chunk.world; // Spigot (Orebfuscator)
+ /* Spigot (Orebfuscator) - Don't use the build buffer yet. Copy to it more efficiently once the chunk is obfuscated
+ // Moved to compress()
if (buildBuffer.length < j + chunkmap.a.length) {
byte[] abyte = new byte[j + chunkmap.a.length];
@@ -53,7 +57,7 @@ public class Packet56MapChunkBulk extends Packet {
buildBuffer = abyte;
}
- System.arraycopy(chunkmap.a, 0, buildBuffer, j, chunkmap.a.length);
+ System.arraycopy(chunkmap.a, 0, buildBuffer, j, chunkmap.a.length); */
j += chunkmap.a.length;
this.c[k] = chunk.x;
this.d[k] = chunk.z;
@@ -82,6 +86,21 @@ public class Packet56MapChunkBulk extends Packet {
return;
}
+ // Spigot (Orebfuscator) start - Obfuscate chunks
+ int finalBufferSize = 0;
+ for (int i = 0; i < a.length; i++) {
+ org.bukkit.craftbukkit.OrebfuscatorManager.obfuscate(c[i], d[i], a[i], inflatedBuffers[i], world);
+ finalBufferSize += inflatedBuffers[i].length;
+ }
+
+ // Now it's time to efficiently copy the chunk to the build buffer
+ buildBuffer = new byte[finalBufferSize];
+ int bufferLocation = 0;
+ for (int i = 0; i < a.length; i++) {
+ System.arraycopy(inflatedBuffers[i], 0, buildBuffer, bufferLocation, inflatedBuffers[i].length);
+ bufferLocation += inflatedBuffers[i].length;
+ }
+ // Spigot (Orebfuscator) end
Deflater deflater = localDeflater.get();
deflater.reset();
deflater.setInput(this.buildBuffer);
diff --git a/src/main/java/net/minecraft/server/PlayerInteractManager.java b/src/main/java/net/minecraft/server/PlayerInteractManager.java
index 5faee12..55f9ffa 100644
--- a/src/main/java/net/minecraft/server/PlayerInteractManager.java
+++ b/src/main/java/net/minecraft/server/PlayerInteractManager.java
@@ -291,6 +291,11 @@ public class PlayerInteractManager {
}
return false;
}
+ // Spigot (Orebfuscator) start
+ else {
+ org.bukkit.craftbukkit.OrebfuscatorManager.updateNearbyBlocks(world, i, j, k);
+ }
+ // Spigot (Orebfuscator) end
}
if (false) { // Never trigger
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
index a7785b7..ef52f32 100644
--- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java
+++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
@@ -163,6 +163,11 @@ public final class CraftServer implements Server {
private WarningState warningState = WarningState.DEFAULT;
private final BooleanWrapper online = new BooleanWrapper();
+ // Orebfuscator use
+ public boolean orebfuscatorEnabled = false;
+ public int orebfuscatorUpdateRadius = 2;
+ public List<String> orebfuscatorDisabledWorlds;
+
private final class BooleanWrapper {
private boolean value = true;
}
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
index 94e07fe..21bd64a 100644
--- a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
+++ b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
@@ -119,6 +119,8 @@ public class CraftWorld implements World {
viewDistance = Bukkit.getServer().getViewDistance();
viewDistance = configuration.getInt("world-settings." + name + ".view-distance", viewDistance);
+ obfuscated = !world.getServer().orebfuscatorDisabledWorlds.contains(name);
+
server.getLogger().info("-------------- Spigot ----------------");
server.getLogger().info("-------- World Settings For [" + name + "] --------");
server.getLogger().info("Growth Per Chunk: " + growthPerTick);
@@ -135,6 +137,7 @@ public class CraftWorld implements World {
server.getLogger().info("Tree Growth Modifier: " + treeGrowthModifier);
server.getLogger().info("Mushroom Growth Modifier: " + mushroomGrowthModifier);
server.getLogger().info("View distance: " + viewDistance);
+ server.getLogger().info("Oreobfuscator: " + obfuscated);
server.getLogger().info("-------------------------------------------------");
// Spigot end
}
@@ -146,6 +149,7 @@ public class CraftWorld implements World {
public int mobSpawnRange = 4;
public int aggregateTicks = 4;
public int viewDistance;
+ public boolean obfuscated = false;
//Crop growth rates:
public int wheatGrowthModifier = 100;
public int cactusGrowthModifier = 100;
diff --git a/src/main/java/org/bukkit/craftbukkit/OrebfuscatorManager.java b/src/main/java/org/bukkit/craftbukkit/OrebfuscatorManager.java
new file mode 100644
index 0000000..7178dfb
--- /dev/null
+++ b/src/main/java/org/bukkit/craftbukkit/OrebfuscatorManager.java
@@ -0,0 +1,95 @@
+package org.bukkit.craftbukkit;
+
+import net.minecraft.server.Block;
+import net.minecraft.server.World;
+
+public class OrebfuscatorManager {
+
+ // Used to keep track of which blocks to obfuscate
+ private static boolean[] obfuscateBlocks = new boolean[Short.MAX_VALUE];
+
+ // Default blocks
+ static {
+ obfuscateBlocks[Block.STONE.id] = true;
+ obfuscateBlocks[Block.GOLD_ORE.id] = true;
+ obfuscateBlocks[Block.IRON_ORE.id] = true;
+ obfuscateBlocks[Block.COAL_ORE.id] = true;
+ obfuscateBlocks[Block.LAPIS_ORE.id] = true;
+ obfuscateBlocks[Block.CHEST.id] = true;
+ obfuscateBlocks[Block.DIAMOND_ORE.id] = true;
+ obfuscateBlocks[Block.REDSTONE_ORE.id] = true;
+ obfuscateBlocks[Block.GLOWING_REDSTONE_ORE.id] = true;
+ obfuscateBlocks[Block.EMERALD_ORE.id] = true;
+ obfuscateBlocks[Block.ENDER_CHEST.id] = true;
+ }
+
+ public static void updateNearbyBlocks(World world, int x, int y, int z) {
+ updateNearbyBlocks(world, x, y, z, world.getServer().orebfuscatorUpdateRadius);
+ }
+
+ public static void obfuscate(int chunkX, int chunkY, int bitmask, byte[] buffer, World world) {
+ if (world.getServer().orebfuscatorEnabled && world.getWorld().obfuscated) {
+ int initialRadius = 1;
+ int index = 0;
+ int startX = chunkX << 4;
+ int startZ = chunkY << 4;
+ for (int i = 0; i < 16; i++) {
+ // If the bitmask indicates this chunk is sent...
+ if ((bitmask & 1 << i) != 0) {
+ for (int y = 0; y < 16; y++) {
+ for (int z = 0; z < 16; z++) {
+ for (int x = 0; x < 16; x++) {
+ byte data = buffer[index];
+ // Check if the block should be obfuscated for the default engine modes
+ if (obfuscateBlocks[data & 0xFF]) {
+ if (initialRadius == 0 || !areAjacentBlocksTransparent(world, startX + x, (i << 4) + y, startZ + z, initialRadius)) {
+ // Replace with stone
+ buffer[index] = (byte) Block.STONE.id;
+ }
+ }
+ if (++index >= buffer.length) {
+ return;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ private static void updateNearbyBlocks(World world, int x, int y, int z, int radius) {
+ if (world.getServer().orebfuscatorEnabled && world.getWorld().obfuscated && world.isLoaded(x, y, z)) {
+ // Get block id
+ int id = world.getTypeId(x, y, z);
+
+ // See if it needs update
+ if (obfuscateBlocks[id]) {
+ // Send the update
+ world.notify(x, y, z);
+ }
+
+ // Check other blocks for updates
+ if (radius != 0) {
+ updateNearbyBlocks(world, x + 1, y, z, radius - 1);
+ updateNearbyBlocks(world, x - 1, y, z, radius - 1);
+ updateNearbyBlocks(world, x, y + 1, z, radius - 1);
+ updateNearbyBlocks(world, x, y - 1, z, radius - 1);
+ updateNearbyBlocks(world, x, y, z + 1, radius - 1);
+ updateNearbyBlocks(world, x, y, z - 1, radius - 1);
+ }
+ }
+ }
+
+ private static boolean areAjacentBlocksTransparent(World world, int x, int y, int z, int radius) {
+ return y > 0 && y <= world.getHeight()
+ && world.isLoaded(x, y, z)
+ && !Block.i(world.getTypeId(x, y, z))
+ || (radius > 0 && (areAjacentBlocksTransparent(world, x, y + 1, z, radius - 1)
+ || areAjacentBlocksTransparent(world, x, y - 1, z, radius - 1)
+ || areAjacentBlocksTransparent(world, x + 1, y, z, radius - 1)
+ || areAjacentBlocksTransparent(world, x - 1, y, z, radius - 1)
+ || areAjacentBlocksTransparent(world, x, y, z + 1, radius - 1)
+ || areAjacentBlocksTransparent(world, x, y, z - 1, radius - 1)));
+ }
+}
diff --git a/src/main/java/org/bukkit/craftbukkit/Spigot.java b/src/main/java/org/bukkit/craftbukkit/Spigot.java
index 931356d..83988c3 100644
--- a/src/main/java/org/bukkit/craftbukkit/Spigot.java
+++ b/src/main/java/org/bukkit/craftbukkit/Spigot.java
@@ -24,6 +24,10 @@ public class Spigot {
server.commandComplete = configuration.getBoolean("settings.command-complete", true);
server.spamGuardExclusions = configuration.getStringList("settings.spam-exclusions");
+ server.orebfuscatorEnabled = configuration.getBoolean("orebfuscator.enable", false);
+ server.orebfuscatorUpdateRadius = configuration.getInt("orebfuscator.update-radius", 2);
+ server.orebfuscatorDisabledWorlds = configuration.getStringList("orebfuscator.disabled-worlds");
+
if (server.chunkGCPeriod == 0) {
server.getLogger().severe("[Spigot] You should not disable chunk-gc. Resetting period-in-ticks to 600 ticks.");
server.chunkGCPeriod = 600;
diff --git a/src/main/resources/configurations/bukkit.yml b/src/main/resources/configurations/bukkit.yml
index 9d6d613..6931712 100644
--- a/src/main/resources/configurations/bukkit.yml
+++ b/src/main/resources/configurations/bukkit.yml
@@ -84,3 +84,8 @@ database:
driver: org.sqlite.JDBC
password: walrus
url: jdbc:sqlite:{DIR}{NAME}.db
+orebfuscator:
+ enable: false
+ update-radius: 2
+ disabled-worlds:
+ - world_the_end
--
1.8.1-rc2