package com.bergerkiller.bukkit.common.reflection.classes;
import java.util.Locale;
import net.minecraft.server.CommandBlockListenerAbstract;
import net.minecraft.server.TileEntitySkull;
import net.minecraft.util.com.mojang.authlib.GameProfile;
import org.bukkit.Chunk;
import org.bukkit.SkullType;
import org.bukkit.World;
import org.bukkit.block.Block;
import org.bukkit.block.BlockState;
import org.bukkit.craftbukkit.block.CraftBlockState;
import org.bukkit.material.MaterialData;
import com.bergerkiller.bukkit.common.collections.ClassMap;
import com.bergerkiller.bukkit.common.reflection.CBClassTemplate;
import com.bergerkiller.bukkit.common.reflection.ClassTemplate;
import com.bergerkiller.bukkit.common.reflection.FieldAccessor;
import com.bergerkiller.bukkit.common.reflection.MethodAccessor;
import com.bergerkiller.bukkit.common.reflection.NMSClassTemplate;
import com.bergerkiller.bukkit.common.utils.BlockUtil;
import com.bergerkiller.bukkit.common.utils.CommonUtil;
import com.bergerkiller.bukkit.common.utils.LogicUtil;
import com.bergerkiller.bukkit.common.utils.MaterialUtil;
public class BlockStateRef {
public static final ClassTemplate<?> TEMPLATE = ClassTemplate.create(CommonUtil.getCBClass("block.CraftBlockState"));
private static final ClassMap<TileInstantiator> tileToInst = new ClassMap<TileInstantiator>();
private static final ClassMap<TileInstantiator> stateToInst = new ClassMap<TileInstantiator>();
public static final FieldAccessor<World> world = TEMPLATE.getField("world");
public static final FieldAccessor<Chunk> chunk = TEMPLATE.getField("chunk");
public static final FieldAccessor<Integer> x = TEMPLATE.getField("x");
public static final FieldAccessor<Integer> y = TEMPLATE.getField("y");
public static final FieldAccessor<Integer> z = TEMPLATE.getField("z");
public static final FieldAccessor<Integer> type = TEMPLATE.getField("type");
public static final FieldAccessor<MaterialData> data = TEMPLATE.getField("data");
public static final FieldAccessor<Byte> light = TEMPLATE.getField("light");
private static void registerInst(TileInstantiator inst) {
tileToInst.put(inst.TILE.getType(), inst);
stateToInst.put(inst.STATE.getType(), inst);
}
static {
// Initialize some instantiators
registerInst(new TileInstantiator("Sign") {
private final FieldAccessor<String[]> state_lines = STATE.getField("lines");
private final FieldAccessor<String[]> tile_lines = TILE.getField("lines");
@Override
protected void apply(BlockState state, Object tile) {
state_lines.set(state, LogicUtil.cloneArray(tile_lines.get(tile)));
}
});
registerInst(new TileInstantiator("Skull") {
private final FieldAccessor<GameProfile> state_profile = STATE.getField("profile");
private final FieldAccessor<SkullType> state_type = STATE.getField("skullType");
private final FieldAccessor<Byte> state_rotation = STATE.getField("rotation");
private final MethodAccessor<SkullType> state_getSkullType = STATE.getMethod("getSkullType", int.class);
@Override
protected void apply(BlockState state, Object tile) {
TileEntitySkull t = (TileEntitySkull) tile;
state_profile.set(state, t.getGameProfile());
state_type.set(state, state_getSkullType.invoke(null, t.getSkullType()));
state_rotation.set(state, (byte) t.getRotation());
}
});
registerInst(new TileInstantiator("Command", "CommandBlock", "commandBlock") {
private final FieldAccessor<CommandBlockListenerAbstract> listener = TILE.getField("a");
private final FieldAccessor<String> state_command = STATE.getField("command");
private final FieldAccessor<String> state_name = STATE.getField("name");
@Override
protected void apply(BlockState state, Object tile) {
CommandBlockListenerAbstract list = listener.get(tile);
state_command.set(state, list.e);
state_name.set(state, list.getName());
}
});
registerInst(new TileInstantiator("Furnace"));
registerInst(new TileInstantiator("Dispenser"));
registerInst(new TileInstantiator("Chest"));
registerInst(new TileInstantiator("Dropper"));
registerInst(new TileInstantiator("Beacon"));
registerInst(new TileInstantiator("Hopper"));
registerInst(new TileInstantiator("Chest"));
registerInst(new TileInstantiator("MobSpawner", "CreatureSpawner", "spawner"));
registerInst(new TileInstantiator("Note", "NoteBlock", "note"));
registerInst(new TileInstantiator("RecordPlayer", "Jukebox", "jukebox"));
}
public static Object toTileEntity(BlockState state) {
TileInstantiator inst = stateToInst.get(state);
if (inst == null) {
return TileEntityRef.getFromWorld(state.getBlock());
} else {
return inst.getTileHandle(state);
}
}
public static BlockState toBlockState(Block block) {
Object tileEntity = TileEntityRef.getFromWorld(block);
if (tileEntity != null) {
TileInstantiator inst = tileToInst.get(tileEntity);
if (inst != null) {
return inst.newInstance(block, tileEntity);
}
}
// All BlockState types REQUIRE a tile entity, just return the default BlockState here
return new CraftBlockState(block);
}
public static BlockState toBlockState(Object tileEntity) {
if (tileEntity == null || !TileEntityRef.hasWorld(tileEntity)) {
throw new IllegalArgumentException("Tile Entity is null or has no world set");
}
TileInstantiator inst = tileToInst.get(tileEntity);
if (inst == null) {
return toBlockState(TileEntityRef.getBlock(tileEntity));
} else {
return inst.newInstance(tileEntity);
}
}
private static class TileInstantiator {
private final FieldAccessor<Object> tileField;
private final FieldAccessor<World> secondWorld;
protected final ClassTemplate<?> STATE;
protected final ClassTemplate<?> TILE;
public TileInstantiator(String name) {
this(name, name, name.toLowerCase(Locale.ENGLISH));
}
public TileInstantiator(String tileName, String stateName, String tileFieldName) {
this.TILE = NMSClassTemplate.create("TileEntity" + tileName);
this.STATE = CBClassTemplate.create("block.Craft" + stateName);
this.tileField = this.STATE.getField(tileFieldName);
// Second world, yes, Bukkit is stupid enough to have two world fields. LOL.
this.secondWorld = this.STATE.getField("world");
}
public Object getTileHandle(Object state) {
return tileField.get(state);
}
protected void apply(BlockState state, Object tile) {
}
public BlockState newInstance(Object tileEntity) {
return newInstance(TileEntityRef.getBlock(tileEntity), tileEntity);
}
public BlockState newInstance(Block block, Object tileEntity) {
final BlockState state = (BlockState) STATE.newInstanceNull();
final int typeId = MaterialUtil.getTypeId(block);
tileField.set(state, tileEntity);
world.set(state, block.getWorld());
secondWorld.set(state, block.getWorld());
chunk.set(state, block.getChunk());
type.set(state, typeId);
light.set(state, block.getLightLevel());
x.set(state, block.getX());
y.set(state, block.getY());
z.set(state, block.getZ());
data.set(state, BlockUtil.getData(typeId, MaterialUtil.getRawData(block)));
this.apply(state, tileEntity);
return state;
}
}
}