Skip to content

Commit

Permalink
Fix #112 (#114)
Browse files Browse the repository at this point in the history
* Fix #112

* Rewrote it to use MethodHandles instead of reflection
  • Loading branch information
2xsaiko authored and amadornes committed Dec 26, 2017
1 parent ef5ff12 commit b9a2dd1
Show file tree
Hide file tree
Showing 2 changed files with 64 additions and 18 deletions.
26 changes: 8 additions & 18 deletions src/main/java/mcmultipart/block/TileMultipartContainer.java
Original file line number Diff line number Diff line change
@@ -1,18 +1,7 @@
package mcmultipart.block;

import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.WeakHashMap;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Consumer;

import com.google.common.base.Preconditions;
import com.google.common.collect.Sets;

import mcmultipart.MCMultiPart;
import mcmultipart.api.container.IMultipartContainer;
import mcmultipart.api.container.IPartInfo;
Expand All @@ -30,17 +19,14 @@
import mcmultipart.network.MultipartNetworkHandler;
import mcmultipart.network.PacketMultipartAdd;
import mcmultipart.network.PacketMultipartRemove;
import mcmultipart.util.WorldExt;
import net.minecraft.block.state.IBlockState;
import net.minecraft.client.renderer.tileentity.TileEntityRendererDispatcher;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.network.NetworkManager;
import net.minecraft.network.play.server.SPacketUpdateTileEntity;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.EnumFacing;
import net.minecraft.util.ITickable;
import net.minecraft.util.Mirror;
import net.minecraft.util.ObjectIntIdentityMap;
import net.minecraft.util.Rotation;
import net.minecraft.util.*;
import net.minecraft.util.math.AxisAlignedBB;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World;
Expand All @@ -50,6 +36,10 @@
import net.minecraftforge.fml.relauncher.SideOnly;
import net.minecraftforge.registries.GameData;

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Consumer;

public class TileMultipartContainer extends TileEntity implements IMultipartContainer {

private boolean isInWorld = true;
Expand Down Expand Up @@ -178,11 +168,11 @@ public void addPart(IPartSlot slot, IBlockState state, IMultipartTile tile) {

if (container != this) { // If we require ticking, place a ticking TE
isInWorld = false;
getWorld().setBlockState(getPos(), MCMultiPart.multipart.getDefaultState()//
WorldExt.setBlockStateHack(getWorld(), getPos(), MCMultiPart.multipart.getDefaultState()//
.withProperty(BlockMultipartContainer.PROPERTY_TICKING, true), 0);
getWorld().setTileEntity(getPos(), container);
} else if (!isInWorld) { // If we aren't in the world, place the TE
getWorld().setBlockState(getPos(), MCMultiPart.multipart.getDefaultState()//
WorldExt.setBlockStateHack(getWorld(), getPos(), MCMultiPart.multipart.getDefaultState()//
.withProperty(BlockMultipartContainer.PROPERTY_TICKING, this instanceof Ticking), 0);
getWorld().setTileEntity(getPos(), this);
}
Expand Down
56 changes: 56 additions & 0 deletions src/main/java/mcmultipart/util/WorldExt.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package mcmultipart.util;

import net.minecraft.block.state.IBlockState;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World;
import net.minecraft.world.chunk.Chunk;
import net.minecraft.world.chunk.storage.ExtendedBlockStorage;
import net.minecraftforge.fml.relauncher.ReflectionHelper;

import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;

public class WorldExt {
private static MethodHandle Chunk$storageArrays = findGetter(Chunk.class, "storageArrays", "field_76652_q");

/**
* Workaround that is World#setBlockState but without calling Block#breakBlock on the old block
*
* @param self The world instance
* @param pos The position where the block state should be set
* @param state The new block state
* @param flags Bit set:
* <p>
* Flag 1 will cause a block update. Flag 2 will send the change to clients. Flag 4 will prevent the block from
* being re-rendered, if this is a client world. Flag 8 will force any re-renders to run on the main thread instead
* of the worker pool, if this is a client world and flag 4 is clear. Flag 16 will prevent observers from seeing
* this change. Flags can be OR-ed
*/
public static void setBlockStateHack(World self, BlockPos pos, IBlockState state, int flags) {
try {
Chunk chunk = self.getChunkFromBlockCoords(pos);

int x = pos.getX() & 15;
int y = pos.getY() & 15;
int z = pos.getZ() & 15;
ExtendedBlockStorage[] storageArrays = (ExtendedBlockStorage[]) Chunk$storageArrays.invoke(chunk);
ExtendedBlockStorage storage = storageArrays[pos.getY() >> 4];
if (storage != null) storage.set(x, y, z, state);
} catch (Throwable e) { // Chunk$storageArrays.invoke throws Throwable
e.printStackTrace();
}

self.setBlockState(pos, state, flags);
}

private static MethodHandle findGetter(Class<?> clazz, String... names) {
try {
return MethodHandles.lookup().unreflectGetter(ReflectionHelper.findField(clazz, names));
} catch (IllegalAccessException e) {
e.printStackTrace();
return null;
}
}

private WorldExt() {}
}

0 comments on commit b9a2dd1

Please sign in to comment.