This commit is contained in:
Jesse Boyd 2018-01-11 18:08:15 +11:00
parent 9366acd6dc
commit 4b7c1e987b
No known key found for this signature in database
GPG Key ID: 59F1DE6293AF6E1F
4 changed files with 217 additions and 79 deletions

View File

@ -25,7 +25,7 @@ import org.bukkit.event.world.ChunkLoadEvent;
public abstract class ChunkListener implements Listener { public abstract class ChunkListener implements Listener {
private int rateLimit = 0; protected int rateLimit = 0;
private int[] badLimit = new int[]{Settings.IMP.TICK_LIMITER.PHYSICS_MS, Settings.IMP.TICK_LIMITER.FALLING, Settings.IMP.TICK_LIMITER.ITEMS}; private int[] badLimit = new int[]{Settings.IMP.TICK_LIMITER.PHYSICS_MS, Settings.IMP.TICK_LIMITER.FALLING, Settings.IMP.TICK_LIMITER.ITEMS};
public ChunkListener() { public ChunkListener() {
@ -61,7 +61,7 @@ public abstract class ChunkListener implements Listener {
public static boolean physicsFreeze = false; public static boolean physicsFreeze = false;
public static boolean itemFreeze = false; public static boolean itemFreeze = false;
private Long2ObjectOpenHashMap<Boolean> badChunks = new Long2ObjectOpenHashMap<>(); protected Long2ObjectOpenHashMap<Boolean> badChunks = new Long2ObjectOpenHashMap<>();
private Long2ObjectOpenHashMap<int[]> counter = new Long2ObjectOpenHashMap<>(); private Long2ObjectOpenHashMap<int[]> counter = new Long2ObjectOpenHashMap<>();
private int lastX = Integer.MIN_VALUE, lastZ = Integer.MIN_VALUE; private int lastX = Integer.MIN_VALUE, lastZ = Integer.MIN_VALUE;
private int[] lastCount; private int[] lastCount;
@ -90,12 +90,13 @@ public abstract class ChunkListener implements Listener {
} }
private int physSkip; protected int physSkip;
private boolean physCancel; protected boolean physCancel;
private long physCancelPair; protected long physCancelPair;
protected long physStart;
protected long physTick;
private long physStart;
private long physTick;
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
public void onPhysics(BlockPhysicsEvent event) { public void onPhysics(BlockPhysicsEvent event) {
@ -130,31 +131,40 @@ public abstract class ChunkListener implements Listener {
Exception e = new Exception(); Exception e = new Exception();
int depth = getDepth(e); int depth = getDepth(e);
if (depth >= 256) { if (depth >= 256) {
for (int frame = 25; frame < 33; frame++) { if (containsSetAir(e, event)) {
StackTraceElement elem = getElement(e, frame); Block block = event.getBlock();
String methodName = elem.getMethodName(); int cx = block.getX() >> 4;
// setAir (hacky, but this needs to be efficient) int cz = block.getZ() >> 4;
if (methodName.charAt(0) == 's' && methodName.length() == 6) { physCancelPair = MathMan.pairInt(cx, cz);
Block block = event.getBlock();
int cx = block.getX() >> 4;
int cz = block.getZ() >> 4;
physCancelPair = MathMan.pairInt(cx, cz);
if (rateLimit <= 0) { if (rateLimit <= 0) {
rateLimit = 20; rateLimit = 20;
Fawe.debug("[FAWE `tick-limiter`] Detected and cancelled physics lag source at " + block.getLocation()); Fawe.debug("[FAWE `tick-limiter`] Detected and cancelled physics lag source at " + block.getLocation());
} }
cancelNearby(cx, cz); cancelNearby(cx, cz);
event.setCancelled(true); event.setCancelled(true);
physCancel = true; physCancel = true;
return; return;
}
} }
} }
physSkip = 1; physSkip = 1;
physCancel = false; physCancel = false;
} }
private void cancelNearby(int cx, int cz) { protected boolean containsSetAir(Exception e, BlockPhysicsEvent event) {
for (int frame = 25; frame < 33; frame++) {
StackTraceElement elem = getElement(e, frame);
if (elem != null) {
String methodName = elem.getMethodName();
// setAir (hacky, but this needs to be efficient)
if (methodName.charAt(0) == 's' && methodName.length() == 6) {
return true;
}
}
}
return false;
}
protected void cancelNearby(int cx, int cz) {
cancel(cx, cz); cancel(cx, cz);
cancel(cx + 1, cz); cancel(cx + 1, cz);
cancel(cx - 1, cz); cancel(cx - 1, cz);
@ -219,34 +229,37 @@ public abstract class ChunkListener implements Listener {
*/ */
@EventHandler(priority = EventPriority.LOWEST) @EventHandler(priority = EventPriority.LOWEST)
public void onChunkLoad(ChunkLoadEvent event) { public void onChunkLoad(ChunkLoadEvent event) {
Chunk chunk = event.getChunk(); if (!Settings.IMP.TICK_LIMITER.FIREWORKS_LOAD_CHUNKS) {
Entity[] entities = chunk.getEntities(); Chunk chunk = event.getChunk();
World world = chunk.getWorld(); Entity[] entities = chunk.getEntities();
World world = chunk.getWorld();
Exception e = new Exception(); Exception e = new Exception();
int start = 14; int start = 14;
int end = 22; int end = 22;
int depth = Math.min(end, getDepth(e)); int depth = Math.min(end, getDepth(e));
for (int frame = start; frame < depth; frame++) { for (int frame = start; frame < depth; frame++) {
StackTraceElement elem = getElement(e, frame); StackTraceElement elem = getElement(e, frame);
String className = elem.getClassName(); if (elem == null) return;
int len = className.length(); String className = elem.getClassName();
if (className != null) { int len = className.length();
if (className.charAt(len - 15) == 'E' && className.endsWith("EntityFireworks")) { if (className != null) {
int chunkRange = 2; if (len > 15 && className.charAt(len - 15) == 'E' && className.endsWith("EntityFireworks")) {
for (int ocx = -chunkRange; ocx <= chunkRange; ocx++) { int chunkRange = 2;
for (int ocz = -chunkRange; ocz <= chunkRange; ocz++) { for (int ocx = -chunkRange; ocx <= chunkRange; ocx++) {
int cx = chunk.getX() + ocx; for (int ocz = -chunkRange; ocz <= chunkRange; ocz++) {
int cz = chunk.getZ() + ocz; int cx = chunk.getX() + ocx;
if (world.isChunkLoaded(cx, cz)) { int cz = chunk.getZ() + ocz;
Chunk relativeChunk = world.getChunkAt(cx, cz); if (world.isChunkLoaded(cx, cz)) {
Entity[] ents = relativeChunk.getEntities(); Chunk relativeChunk = world.getChunkAt(cx, cz);
for (Entity ent : ents) { Entity[] ents = relativeChunk.getEntities();
switch (ent.getType()) { for (Entity ent : ents) {
case FIREWORK: switch (ent.getType()) {
Fawe.debug("[FAWE `tick-limiter`] Detected and cancelled rogue FireWork at " + ent.getLocation()); case FIREWORK:
ent.remove(); Fawe.debug("[FAWE `tick-limiter`] Detected and cancelled rogue FireWork at " + ent.getLocation());
ent.remove();
}
} }
} }
} }

View File

@ -1,24 +1,159 @@
package com.boydti.fawe.bukkit.v0; package com.boydti.fawe.bukkit.v0;
import com.boydti.fawe.Fawe;
import com.boydti.fawe.config.Settings; import com.boydti.fawe.config.Settings;
import java.lang.reflect.Method; import com.boydti.fawe.util.FaweTimer;
import com.boydti.fawe.util.MathMan;
import org.bukkit.block.Block;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.block.BlockBurnEvent;
import org.bukkit.event.block.BlockCanBuildEvent;
import org.bukkit.event.block.BlockDamageEvent;
import org.bukkit.event.block.BlockDispenseEvent;
import org.bukkit.event.block.BlockExpEvent;
import org.bukkit.event.block.BlockExplodeEvent;
import org.bukkit.event.block.BlockFadeEvent;
import org.bukkit.event.block.BlockFromToEvent;
import org.bukkit.event.block.BlockGrowEvent;
import org.bukkit.event.block.BlockIgniteEvent;
import org.bukkit.event.block.BlockPhysicsEvent;
import org.bukkit.event.block.BlockPistonEvent;
import org.bukkit.event.block.BlockPlaceEvent;
import org.bukkit.event.block.BlockRedstoneEvent;
import org.bukkit.event.block.CauldronLevelChangeEvent;
import org.bukkit.event.block.LeavesDecayEvent;
import org.bukkit.event.block.NotePlayEvent;
import org.bukkit.event.block.SignChangeEvent;
import org.bukkit.event.inventory.BrewEvent;
import org.bukkit.event.inventory.BrewingStandFuelEvent;
import org.bukkit.event.inventory.FurnaceBurnEvent;
import org.bukkit.event.inventory.FurnaceSmeltEvent;
public class ChunkListener_9 extends ChunkListener { public class ChunkListener_9 extends ChunkListener {
private Method methodDepth;
private Method methodGetStackTraceElement;
private Exception exception; private Exception exception;
private StackTraceElement[] elements; private StackTraceElement[] elements;
public ChunkListener_9() { public ChunkListener_9() {
if (Settings.IMP.TICK_LIMITER.ENABLED) { super();
try { }
this.methodDepth = Throwable.class.getDeclaredMethod("getStackTraceDepth");
this.methodDepth.setAccessible(true); private void reset() {
this.methodGetStackTraceElement = Throwable.class.getDeclaredMethod("getStackTraceElement", int.class); physSkip = 0;
this.methodGetStackTraceElement.setAccessible(true); physStart = System.currentTimeMillis();
} catch (Throwable e) { physCancel = false;
throw new RuntimeException(e); }
@EventHandler(priority = EventPriority.LOWEST)
public void event(BlockBurnEvent event) { reset(); }
@EventHandler(priority = EventPriority.LOWEST)
public void event(BlockCanBuildEvent event) { reset(); }
@EventHandler(priority = EventPriority.LOWEST)
public void event(BlockDamageEvent event) { reset(); }
@EventHandler(priority = EventPriority.LOWEST)
public void event(BlockDispenseEvent event) { reset(); }
@EventHandler(priority = EventPriority.LOWEST)
public void event(BlockExpEvent event) { reset(); }
@EventHandler(priority = EventPriority.LOWEST)
public void event(BlockExplodeEvent event) { reset(); }
@EventHandler(priority = EventPriority.LOWEST)
public void event(BlockFadeEvent event) { reset(); }
@EventHandler(priority = EventPriority.LOWEST)
public void event(BlockFromToEvent event) { reset(); }
@EventHandler(priority = EventPriority.LOWEST)
public void event(BlockGrowEvent event) { reset(); }
@EventHandler(priority = EventPriority.LOWEST)
public void event(BlockIgniteEvent event) { reset(); }
@EventHandler(priority = EventPriority.LOWEST)
public void event( BlockPistonEvent event) { reset(); }
@EventHandler(priority = EventPriority.LOWEST)
public void event(BlockPlaceEvent event) { reset(); }
@EventHandler(priority = EventPriority.LOWEST)
public void event(BrewEvent event) { reset(); }
@EventHandler(priority = EventPriority.LOWEST)
public void event(BrewingStandFuelEvent event) { reset(); }
@EventHandler(priority = EventPriority.LOWEST)
public void event(CauldronLevelChangeEvent event) { reset(); }
@EventHandler(priority = EventPriority.LOWEST)
public void event(FurnaceBurnEvent event) { reset(); }
@EventHandler(priority = EventPriority.LOWEST)
public void event(FurnaceSmeltEvent event) { reset(); }
@EventHandler(priority = EventPriority.LOWEST)
public void event(LeavesDecayEvent event) { reset(); }
@EventHandler(priority = EventPriority.LOWEST)
public void event(NotePlayEvent event) { reset(); }
@EventHandler(priority = EventPriority.LOWEST)
public void event(SignChangeEvent event) { reset(); }
@EventHandler(priority = EventPriority.LOWEST)
public void event(BlockRedstoneEvent event) { reset(); }
@EventHandler(priority = EventPriority.LOWEST)
public void onPhysics(BlockPhysicsEvent event) {
if (physicsFreeze) {
event.setCancelled(true);
return;
}
if (physCancel) {
Block block = event.getBlock();
long pair = MathMan.pairInt(block.getX() >> 4, block.getZ() >> 4);
if (physCancelPair == pair) {
event.setCancelled(true);
return;
}
if (badChunks.containsKey(pair)) {
physCancelPair = pair;
event.setCancelled(true);
return;
}
if (System.currentTimeMillis() - physStart > Settings.IMP.TICK_LIMITER.PHYSICS_MS) {
physCancelPair = pair;
event.setCancelled(true);
return;
}
}
FaweTimer timer = Fawe.get().getTimer();
if (timer.getTick() != physTick) {
physTick = timer.getTick();
physStart = System.currentTimeMillis();
physSkip = 0;
physCancel = false;
return;
}
if ((++physSkip & 1023) == 0) {
if (System.currentTimeMillis() - physStart > Settings.IMP.TICK_LIMITER.PHYSICS_MS) {
Block block = event.getBlock();
int cx = block.getX() >> 4;
int cz = block.getZ() >> 4;
physCancelPair = MathMan.pairInt(cx, cz);
if (rateLimit <= 0) {
rateLimit = 20;
Fawe.debug("[FAWE `tick-limiter`] Detected and cancelled physics lag source at " + block.getLocation());
}
cancelNearby(cx, cz);
event.setCancelled(true);
physCancel = true;
return;
} }
} }
} }
@ -33,27 +168,12 @@ public class ChunkListener_9 extends ChunkListener {
@Override @Override
protected int getDepth(Exception ex) { protected int getDepth(Exception ex) {
if (methodDepth != null) {
try {
return (int) methodDepth.invoke(ex);
} catch (Throwable t) {
methodDepth = null;
t.printStackTrace();
}
}
return getElements(ex).length; return getElements(ex).length;
} }
@Override @Override
protected StackTraceElement getElement(Exception ex, int i) { protected StackTraceElement getElement(Exception ex, int i) {
if (methodGetStackTraceElement != null) { StackTraceElement[] elems = getElements(ex);
try { return elems.length > i ? elems[i] : null;
return (StackTraceElement) methodGetStackTraceElement.invoke(ex, i);
} catch (Throwable t) {
methodGetStackTraceElement = null;
t.printStackTrace();
}
}
return getElements(ex)[i];
} }
} }

View File

@ -357,6 +357,12 @@ public class Settings extends Config {
public int PHYSICS_MS = 50; public int PHYSICS_MS = 50;
@Comment("Max item spawns per interval (per chunk)") @Comment("Max item spawns per interval (per chunk)")
public int ITEMS = 256; public int ITEMS = 256;
@Comment({
"Whether fireworks can load chunks",
" - Fireworks usually travel vertically so do not load any chunks",
" - Horizontal fireworks can be hacked in to crash a server"
})
public boolean FIREWORKS_LOAD_CHUNKS = false;
} }
public static class CLIPBOARD { public static class CLIPBOARD {

View File

@ -794,7 +794,6 @@ public class HeightMapMCAGenerator extends MCAWriter implements SimpleWorld, Faw
@Override @Override
public void setChunk(FaweChunk chunk) { public void setChunk(FaweChunk chunk) {
System.out.println(((SimpleCharFaweChunk) chunk).getTotalCount());
char[][] src = chunk.getCombinedIdArrays(); char[][] src = chunk.getCombinedIdArrays();
for (int i = 0; i < src.length; i++) { for (int i = 0; i < src.length; i++) {
if (src[i] != null) { if (src[i] != null) {