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 {
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};
public ChunkListener() {
@ -61,7 +61,7 @@ public abstract class ChunkListener implements Listener {
public static boolean physicsFreeze = 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 int lastX = Integer.MIN_VALUE, lastZ = Integer.MIN_VALUE;
private int[] lastCount;
@ -90,12 +90,13 @@ public abstract class ChunkListener implements Listener {
}
private int physSkip;
private boolean physCancel;
private long physCancelPair;
protected int physSkip;
protected boolean physCancel;
protected long physCancelPair;
protected long physStart;
protected long physTick;
private long physStart;
private long physTick;
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
public void onPhysics(BlockPhysicsEvent event) {
@ -130,31 +131,40 @@ public abstract class ChunkListener implements Listener {
Exception e = new Exception();
int depth = getDepth(e);
if (depth >= 256) {
for (int frame = 25; frame < 33; frame++) {
StackTraceElement elem = getElement(e, frame);
String methodName = elem.getMethodName();
// setAir (hacky, but this needs to be efficient)
if (methodName.charAt(0) == 's' && methodName.length() == 6) {
Block block = event.getBlock();
int cx = block.getX() >> 4;
int cz = block.getZ() >> 4;
physCancelPair = MathMan.pairInt(cx, cz);
if (containsSetAir(e, event)) {
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());
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;
}
cancelNearby(cx, cz);
event.setCancelled(true);
physCancel = true;
return;
}
}
physSkip = 1;
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 + 1, cz);
cancel(cx - 1, cz);
@ -219,34 +229,37 @@ public abstract class ChunkListener implements Listener {
*/
@EventHandler(priority = EventPriority.LOWEST)
public void onChunkLoad(ChunkLoadEvent event) {
Chunk chunk = event.getChunk();
Entity[] entities = chunk.getEntities();
World world = chunk.getWorld();
if (!Settings.IMP.TICK_LIMITER.FIREWORKS_LOAD_CHUNKS) {
Chunk chunk = event.getChunk();
Entity[] entities = chunk.getEntities();
World world = chunk.getWorld();
Exception e = new Exception();
int start = 14;
int end = 22;
int depth = Math.min(end, getDepth(e));
Exception e = new Exception();
int start = 14;
int end = 22;
int depth = Math.min(end, getDepth(e));
for (int frame = start; frame < depth; frame++) {
StackTraceElement elem = getElement(e, frame);
String className = elem.getClassName();
int len = className.length();
if (className != null) {
if (className.charAt(len - 15) == 'E' && className.endsWith("EntityFireworks")) {
int chunkRange = 2;
for (int ocx = -chunkRange; ocx <= chunkRange; ocx++) {
for (int ocz = -chunkRange; ocz <= chunkRange; ocz++) {
int cx = chunk.getX() + ocx;
int cz = chunk.getZ() + ocz;
if (world.isChunkLoaded(cx, cz)) {
Chunk relativeChunk = world.getChunkAt(cx, cz);
Entity[] ents = relativeChunk.getEntities();
for (Entity ent : ents) {
switch (ent.getType()) {
case FIREWORK:
Fawe.debug("[FAWE `tick-limiter`] Detected and cancelled rogue FireWork at " + ent.getLocation());
ent.remove();
for (int frame = start; frame < depth; frame++) {
StackTraceElement elem = getElement(e, frame);
if (elem == null) return;
String className = elem.getClassName();
int len = className.length();
if (className != null) {
if (len > 15 && className.charAt(len - 15) == 'E' && className.endsWith("EntityFireworks")) {
int chunkRange = 2;
for (int ocx = -chunkRange; ocx <= chunkRange; ocx++) {
for (int ocz = -chunkRange; ocz <= chunkRange; ocz++) {
int cx = chunk.getX() + ocx;
int cz = chunk.getZ() + ocz;
if (world.isChunkLoaded(cx, cz)) {
Chunk relativeChunk = world.getChunkAt(cx, cz);
Entity[] ents = relativeChunk.getEntities();
for (Entity ent : ents) {
switch (ent.getType()) {
case FIREWORK:
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;
import com.boydti.fawe.Fawe;
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 {
private Method methodDepth;
private Method methodGetStackTraceElement;
private Exception exception;
private StackTraceElement[] elements;
public ChunkListener_9() {
if (Settings.IMP.TICK_LIMITER.ENABLED) {
try {
this.methodDepth = Throwable.class.getDeclaredMethod("getStackTraceDepth");
this.methodDepth.setAccessible(true);
this.methodGetStackTraceElement = Throwable.class.getDeclaredMethod("getStackTraceElement", int.class);
this.methodGetStackTraceElement.setAccessible(true);
} catch (Throwable e) {
throw new RuntimeException(e);
super();
}
private void reset() {
physSkip = 0;
physStart = System.currentTimeMillis();
physCancel = false;
}
@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
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;
}
@Override
protected StackTraceElement getElement(Exception ex, int i) {
if (methodGetStackTraceElement != null) {
try {
return (StackTraceElement) methodGetStackTraceElement.invoke(ex, i);
} catch (Throwable t) {
methodGetStackTraceElement = null;
t.printStackTrace();
}
}
return getElements(ex)[i];
StackTraceElement[] elems = getElements(ex);
return elems.length > i ? elems[i] : null;
}
}

View File

@ -357,6 +357,12 @@ public class Settings extends Config {
public int PHYSICS_MS = 50;
@Comment("Max item spawns per interval (per chunk)")
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 {

View File

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