Skip to content

Instantly share code, notes, and snippets.

@FormallyMyles
Created July 15, 2016 14:44
Show Gist options
  • Save FormallyMyles/df5981f089c992c41c3619940b066af2 to your computer and use it in GitHub Desktop.
Save FormallyMyles/df5981f089c992c41c3619940b066af2 to your computer and use it in GitHub Desktop.
Panda's Redstone for Bukkit Source (1.10)
package us.myles.fastredstone;
import com.google.common.collect.UnmodifiableIterator;
import net.minecraft.server.v1_10_R1.Block;
import net.minecraft.server.v1_10_R1.Blocks;
import net.minecraft.server.v1_10_R1.IBlockData;
import net.minecraft.server.v1_10_R1.MinecraftKey;
import org.bukkit.plugin.java.JavaPlugin;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
public class FastRedstone extends JavaPlugin {
public void onEnable() {
// inject
MinecraftKey key = new MinecraftKey("redstone_wire");
FastRedstoneWire wire = new FastRedstoneWire();
Block.REGISTRY.a(55, key, wire);
setFinalStatic(Blocks.class, "REDSTONE_WIRE", Block.REGISTRY.get(key));
for (UnmodifiableIterator localUnmodifiableIterator = wire.t().a().iterator(); localUnmodifiableIterator.hasNext(); ) {
IBlockData iblockdata = (IBlockData) localUnmodifiableIterator.next();
int k = Block.REGISTRY.a(wire) << 4 | wire.toLegacyData(iblockdata);
Block.REGISTRY_ID.a(iblockdata, k);
}
System.out.println("Injected!");
}
private void setFinalStatic(Class clazz, String fieldName, Object newValue) {
try {
Field field = clazz.getDeclaredField(fieldName);
field.setAccessible(true);
Field modifiers = field.getClass().getDeclaredField("modifiers");
modifiers.setAccessible(true);
modifiers.setInt(field, field.getModifiers() & ~Modifier.FINAL);
field.set(null, newValue);
} catch (NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException ex) {
ex.printStackTrace(System.err);
}
}
}
package us.myles.fastredstone;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import net.minecraft.server.v1_10_R1.*;
import org.apache.commons.lang3.ArrayUtils;
import org.bukkit.event.block.BlockRedstoneEvent;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
/**
* Based on https://gist.github.com/Panda4994/70ed6d39c89396570e062e4404a8d518
*/
public class FastRedstoneWire extends BlockRedstoneWire {
/**
* I considered replacing the lists with LinkedHashSets for faster lookup,
* but an artificial test showed barely any difference in performance
*/
/**
* Ordered arrays of the facings; Needed for the update order.
* I went with a vertical-first order here, but vertical last would work to.
* However it should be avoided to update the vertical axis between the horizontal ones as this would cause unneeded directional behavior.
**/
private static final EnumDirection[] facingsHorizontal = {EnumDirection.WEST, EnumDirection.EAST, EnumDirection.NORTH, EnumDirection.SOUTH};
private static final EnumDirection[] facingsVertical = {EnumDirection.DOWN, EnumDirection.UP};
private static final EnumDirection[] facings = ArrayUtils.addAll(facingsVertical, facingsHorizontal);
/**
* Offsets for all surrounding blocks that need to receive updates
**/
private static final BaseBlockPosition[] surroundingBlocksOffset;
static {
Set<BaseBlockPosition> set = Sets.newLinkedHashSet();
for (EnumDirection facing : facings) {
set.add(new BaseBlockPosition(facing.getAdjacentX(), facing.getAdjacentY(), facing.getAdjacentZ()));
}
for (EnumDirection facing1 : facings) {
BaseBlockPosition v1 = new BaseBlockPosition(facing1.getAdjacentX(), facing1.getAdjacentY(), facing1.getAdjacentZ());
for (EnumDirection facing2 : facings) {
BaseBlockPosition v2 = new BaseBlockPosition(facing2.getAdjacentX(), facing2.getAdjacentY(), facing2.getAdjacentZ());
// TODO Adding an add-method to Vec3i would be nicer of course
set.add(new BaseBlockPosition(v1.getX() + v2.getX(), v1.getY() + v2.getY(), v1.getZ() + v2.getZ()));
}
}
set.remove(new BaseBlockPosition(0, 0, 0));
surroundingBlocksOffset = set.toArray(new BaseBlockPosition[set.size()]);
}
/**
* Positions that need to be turned off
**/
private final List<BlockPosition> turnOff = Lists.newArrayList();
/**
* Positions that need to be checked to be turned on
**/
private final List<BlockPosition> turnOn = Lists.newArrayList();
/**
* Positions of wire that was updated already (Ordering determines update order and is therefore required!)
**/
private final Set<BlockPosition> updatedRedstoneWire = Sets.newLinkedHashSet();
private boolean g;
public FastRedstoneWire() {
c(0.0F);
a(SoundEffectType.d);
c("redstoneDust");
q();
}
/*
Override methods
*/
@Override
public void onPlace(World world, BlockPosition blockposition, IBlockData iblockdata) {
if (!world.isClientSide) {
updateSurroundingWire(world, blockposition);
Iterator iterator = EnumDirection.EnumDirectionLimit.VERTICAL.iterator();
while (iterator.hasNext()) {
EnumDirection enumdirection = (EnumDirection) iterator.next();
world.applyPhysics(blockposition.shift(enumdirection), this);
}
iterator = EnumDirection.EnumDirectionLimit.HORIZONTAL.iterator();
while (iterator.hasNext()) {
EnumDirection dir = (EnumDirection) iterator.next();
b(world, blockposition.shift(dir));
}
iterator = EnumDirection.EnumDirectionLimit.HORIZONTAL.iterator();
while (iterator.hasNext()) {
EnumDirection dir = (EnumDirection) iterator.next();
BlockPosition pos = blockposition.shift(dir);
if (world.getType(pos).l())
b(world, pos.up());
else
b(world, pos.down());
}
}
}
@Override
public void remove(World world, BlockPosition blockposition, IBlockData iblockdata) {
super.remove(world, blockposition, iblockdata);
if (!world.isClientSide) {
EnumDirection[] dirs = EnumDirection.values();
for (EnumDirection enumdirection : dirs) {
world.applyPhysics(blockposition.shift(enumdirection), this);
}
updateSurroundingWire(world, blockposition);
Iterator iterator = EnumDirection.EnumDirectionLimit.HORIZONTAL.iterator();
while (iterator.hasNext()) {
EnumDirection dir = (EnumDirection) iterator.next();
b(world, blockposition.shift(dir));
}
iterator = EnumDirection.EnumDirectionLimit.HORIZONTAL.iterator();
while (iterator.hasNext()) {
EnumDirection dir = (EnumDirection) iterator.next();
BlockPosition pos = blockposition.shift(dir);
if (world.getType(pos).l())
b(world, pos.up());
else
b(world, pos.down());
}
}
}
// func_189540_a
@Override
public void a(IBlockData iblockdata, World world, BlockPosition blockposition, Block block) {
if (!world.isClientSide)
if (canPlace(world, blockposition)) {
updateSurroundingWire(world, blockposition);
} else {
b(world, blockposition, iblockdata, 0);
world.setAir(blockposition);
}
}
// getWeakPower
@Override
public int b(IBlockData iblockdata, IBlockAccess iblockaccess, BlockPosition blockposition, EnumDirection enumdirection) {
if (!this.g) {
return 0;
}
int i = iblockdata.get(BlockRedstoneWire.POWER);
if (i == 0)
return 0;
if (enumdirection == EnumDirection.UP) {
return i;
}
if (getSidesToPower((World) iblockaccess, blockposition).contains(enumdirection)) {
return i;
}
return 0;
}
/*
Normal Methods
*/
/**
* Takes a world and block location and updates the wires around it
*
* @param world
* @param blockposition
*/
private void updateSurroundingWire(World world, BlockPosition blockposition) {
calculateCurrentChanges(world, blockposition);
Set<BlockPosition> blocksNeedingUpdate = Sets.newLinkedHashSet();
for (BlockPosition blockPos : this.updatedRedstoneWire) {
addBlocksNeedingUpdate(world, blockPos, blocksNeedingUpdate);
}
Iterator iterator = Lists.newLinkedList(this.updatedRedstoneWire).descendingIterator();
while (iterator.hasNext()) {
addAllSurroundingBlocks((BlockPosition) iterator.next(), blocksNeedingUpdate);
}
blocksNeedingUpdate.removeAll(this.updatedRedstoneWire);
this.updatedRedstoneWire.clear();
for (BlockPosition pos : blocksNeedingUpdate)
world.e(pos, this);
}
/**
* Calculate which surrounding blocks need to turn on and off
*
* @param world
* @param blockposition
*/
private void calculateCurrentChanges(World world, BlockPosition blockposition) {
if (world.getType(blockposition).getBlock() == this) {
this.turnOff.add(blockposition);
} else {
checkSurroundingWires(world, blockposition);
}
while (!this.turnOff.isEmpty()) {
BlockPosition pos = this.turnOff.remove(0);
IBlockData state = world.getType(pos);
int oldPower = state.get(POWER);
this.g = false;
int blockPower = world.z(pos);
this.g = true;
int wirePower = getSurroundingWirePower(world, pos);
wirePower--;
int newPower = Math.max(blockPower, wirePower);
if (newPower < oldPower) {
if ((blockPower > 0) && (!this.turnOn.contains(pos))) {
this.turnOn.add(pos);
}
setWireState(world, pos, state, 0);
} else if (newPower > oldPower) {
setWireState(world, pos, state, newPower);
}
checkSurroundingWires(world, pos);
}
while (!this.turnOn.isEmpty()) {
BlockPosition pos = this.turnOn.remove(0);
IBlockData state = world.getType(pos);
int oldPower = state.get(POWER);
this.g = false;
int blockPower = world.z(pos);
this.g = true;
int wirePower = getSurroundingWirePower(world, pos);
wirePower--;
int newPower = Math.max(blockPower, wirePower);
if (oldPower != newPower) {
BlockRedstoneEvent event = new BlockRedstoneEvent(world.getWorld().getBlockAt(blockposition.getX(), blockposition.getY(), blockposition.getZ()), oldPower, newPower);
world.getServer().getPluginManager().callEvent(event);
newPower = event.getNewCurrent();
}
if (newPower > oldPower)
setWireState(world, pos, state, newPower);
checkSurroundingWires(world, pos);
}
this.turnOff.clear();
this.turnOn.clear();
}
/**
* Check surrounding blocks to see which need to be turned on and off
*
* @param worldIn
* @param pos
*/
private void checkSurroundingWires(World worldIn, BlockPosition pos) {
IBlockData state = worldIn.getType(pos);
int ownPower = 0;
if (state.getBlock() == this) {
ownPower = state.get(POWER);
}
for (EnumDirection facing : facingsHorizontal) {
BlockPosition offsetPos = pos.shift(facing);
if (facing.k().c()) {
addWireToList(worldIn, offsetPos, ownPower);
}
}
for (EnumDirection facingVertical : facingsVertical) {
BlockPosition offsetPos = pos.shift(facingVertical);
boolean solidBlock = worldIn.getType(offsetPos).k();
for (EnumDirection facingHorizontal : facingsHorizontal) {
if (((facingVertical == EnumDirection.UP) && (!solidBlock)) || ((facingVertical == EnumDirection.DOWN) && (solidBlock) && (!worldIn.getType(offsetPos.shift(facingHorizontal)).l())))
addWireToList(worldIn, offsetPos.shift(facingHorizontal), ownPower);
}
}
}
/**
* Calculate if a wire needs to be turned on or off
*
* @param worldIn
* @param pos
* @param otherPower
*/
private void addWireToList(World worldIn, BlockPosition pos, int otherPower) {
IBlockData state = worldIn.getType(pos);
if (state.getBlock() == this) {
int power = state.get(POWER);
if ((power < otherPower - 1) && (!this.turnOn.contains(pos))) {
this.turnOn.add(pos);
}
if ((power > otherPower) && (!this.turnOff.contains(pos))) {
this.turnOff.add(pos);
}
}
}
/**
* Get the power of a block from surrounding blocks
*
* @param worldIn
* @param pos
* @return power level
*/
private int getSurroundingWirePower(World worldIn, BlockPosition pos) {
int wirePower = 0;
for (EnumDirection dir : EnumDirection.EnumDirectionLimit.HORIZONTAL) {
BlockPosition offsetPos = pos.shift(dir);
wirePower = getPower(worldIn, offsetPos, wirePower);
if ((worldIn.getType(offsetPos).l()) && (!worldIn.getType(pos.up()).l())) {
wirePower = getPower(worldIn, offsetPos.up(), wirePower);
} else if (!worldIn.getType(offsetPos).l()) {
wirePower = getPower(worldIn, offsetPos.down(), wirePower);
}
}
return wirePower;
}
/**
* Update the power of a block
*
* @param worldIn
* @param pos
* @param state
* @param power
*/
private void setWireState(World worldIn, BlockPosition pos, IBlockData state, int power) {
state = state.set(POWER, power);
worldIn.setTypeAndData(pos, state, 2);
this.updatedRedstoneWire.add(pos);
}
/**
* Add blocks that need the power updating to a set
*
* @param worldIn
* @param pos
* @param set
*/
private void addBlocksNeedingUpdate(World worldIn, BlockPosition pos, Set<BlockPosition> set) {
List connectedSides = getSidesToPower(worldIn, pos);
for (EnumDirection facing : facings) {
BlockPosition offsetPos = pos.shift(facing);
if (((connectedSides.contains(facing.opposite())) || (facing == EnumDirection.DOWN) || ((facing.k().c()) && (a(worldIn.getType(offsetPos), facing)))) &&
(canBlockBePoweredFromSide(worldIn.getType(offsetPos), facing, true))) {
set.add(offsetPos);
}
}
for (EnumDirection facing : facings) {
BlockPosition offsetPos = pos.shift(facing);
if (((connectedSides.contains(facing.opposite())) || (facing == EnumDirection.DOWN)) &&
(worldIn.getType(offsetPos).l())) {
for (EnumDirection facing1 : facings) {
if (canBlockBePoweredFromSide(worldIn.getType(offsetPos.shift(facing1)), facing1, false)) {
set.add(offsetPos.shift(facing1));
}
}
}
}
}
/**
* Get the sides a block needs to provide power to
*
* @param worldIn
* @param pos
* @return
*/
private List<EnumDirection> getSidesToPower(World worldIn, BlockPosition pos) {
List<EnumDirection> sides = Lists.newArrayList();
for (EnumDirection facing : facingsHorizontal) {
if (isPowerSourceAt(worldIn, pos, facing)) {
sides.add(facing);
}
}
if (sides.isEmpty()) {
return Lists.newArrayList(facingsHorizontal);
}
boolean northSouth = (sides.contains(EnumDirection.NORTH)) || (sides.contains(EnumDirection.SOUTH));
boolean eastWest = (sides.contains(EnumDirection.EAST)) || (sides.contains(EnumDirection.WEST));
if (northSouth) {
sides.remove(EnumDirection.EAST);
sides.remove(EnumDirection.WEST);
}
if (eastWest) {
sides.remove(EnumDirection.NORTH);
sides.remove(EnumDirection.SOUTH);
}
return sides;
}
/**
* Checks if a block is a power source
*
* @param iblockaccess
* @param blockposition
* @param enumdirection
* @return
*/
private boolean isPowerSourceAt(IBlockAccess iblockaccess, BlockPosition blockposition, EnumDirection enumdirection) {
BlockPosition newPosition = blockposition.shift(enumdirection);
IBlockData iblockdata = iblockaccess.getType(newPosition);
boolean flag = iblockdata.l();
boolean flag1 = iblockaccess.getType(blockposition.up()).l();
return (!flag1) && (flag) && (c(iblockaccess, newPosition.up()));
}
/**
* Checks if a block can be powered from a side
*
* @param state
* @param side
* @param isWire
* @return True if it can
*/
private boolean canBlockBePoweredFromSide(IBlockData state, EnumDirection side, boolean isWire) {
if (((state.getBlock() instanceof BlockPiston)) && (state.get(BlockPiston.FACING) == side.opposite())) {
return false;
}
if (((state.getBlock() instanceof BlockDiodeAbstract)) && (state.get(BlockDiodeAbstract.FACING) != side.opposite())) {
return (isWire) && ((state.getBlock() instanceof BlockRedstoneComparator)) && (state.get(BlockRedstoneComparator.FACING).k() != side.k()) && (side.k().c());
}
return !(((state.getBlock() instanceof BlockRedstoneTorch)) && (
(isWire) || (state.get(BlockRedstoneTorch.FACING) != side)));
}
/**
* Adds the blocks around a block that need checking
*
* @param pos
* @param set
*/
private void addAllSurroundingBlocks(BlockPosition pos, Set<BlockPosition> set) {
for (BaseBlockPosition baseBlockPosition : surroundingBlocksOffset)
set.add(pos.a(baseBlockPosition));
}
}
This code is originally by Panda4994.
I started work on this to port it to bukkit and ran into some issues with block updating, md_5 has released:
https://www.spigotmc.org/resources/pandawire.26418/
Comparing my code to him I fixed the isses (few obfuscation problems)
You are free to use this code how you may, the license of it is up to the decision of Panda. (Which he has given permission for others to use his code)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment