Remove part layer api in favor of capabilities (#355)
This commit is contained in:
@@ -29,7 +29,6 @@ import appeng.block.AEBaseTileBlock;
|
||||
import appeng.client.UnlistedProperty;
|
||||
import appeng.client.render.cablebus.CableBusBakedModel;
|
||||
import appeng.client.render.cablebus.CableBusRenderState;
|
||||
import appeng.core.Api;
|
||||
import appeng.core.AppEng;
|
||||
import appeng.core.sync.network.NetworkHandler;
|
||||
import appeng.core.sync.packets.PacketClick;
|
||||
@@ -91,10 +90,6 @@ public class BlockCableBus extends AEBaseTileBlock implements IAEFacade {
|
||||
|
||||
private static final ICableBusContainer NULL_CABLE_BUS = new NullCableBusContainer();
|
||||
|
||||
private static Class<? extends AEBaseTile> noTesrTile;
|
||||
|
||||
private static Class<? extends AEBaseTile> tesrTile;
|
||||
|
||||
public BlockCableBus() {
|
||||
super(AEGlassMaterial.INSTANCE);
|
||||
this.setLightOpacity(0);
|
||||
@@ -378,10 +373,9 @@ public class BlockCableBus extends AEBaseTileBlock implements IAEFacade {
|
||||
}
|
||||
|
||||
public void setupTile() {
|
||||
noTesrTile = Api.INSTANCE.partHelper().getCombinedInstance(TileCableBus.class);
|
||||
this.setTileEntity(noTesrTile);
|
||||
this.setTileEntity(TileCableBus.class);
|
||||
|
||||
GameRegistry.registerTileEntity(noTesrTile, AppEng.MOD_ID.toLowerCase() + ":" + "BlockCableBus");
|
||||
GameRegistry.registerTileEntity(TileCableBus.class, AppEng.MOD_ID.toLowerCase() + ":" + "BlockCableBus");
|
||||
|
||||
if (Platform.isClient()) {
|
||||
setupTesr();
|
||||
@@ -390,9 +384,8 @@ public class BlockCableBus extends AEBaseTileBlock implements IAEFacade {
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
private static void setupTesr() {
|
||||
tesrTile = Api.INSTANCE.partHelper().getCombinedInstance(TileCableBusTESR.class);
|
||||
GameRegistry.registerTileEntity(tesrTile, AppEng.MOD_ID.toLowerCase() + ":" + "ClientOnly_TESR_CableBus");
|
||||
ClientRegistry.bindTileEntitySpecialRenderer(BlockCableBus.getTesrTile(), new CableBusTESR());
|
||||
GameRegistry.registerTileEntity(TileCableBusTESR.class, AppEng.MOD_ID.toLowerCase() + ":" + "ClientOnly_TESR_CableBus");
|
||||
ClientRegistry.bindTileEntitySpecialRenderer(TileCableBusTESR.class, new CableBusTESR());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -438,14 +431,6 @@ public class BlockCableBus extends AEBaseTileBlock implements IAEFacade {
|
||||
super.onEntityWalk(world, pos, entityIn);
|
||||
}
|
||||
|
||||
public static Class<? extends AEBaseTile> getNoTesrTile() {
|
||||
return noTesrTile;
|
||||
}
|
||||
|
||||
public static Class<? extends AEBaseTile> getTesrTile() {
|
||||
return tesrTile;
|
||||
}
|
||||
|
||||
// Helper to get access to the protected constructor
|
||||
@SideOnly(Side.CLIENT)
|
||||
private static class DestroyFX extends ParticleDigging {
|
||||
|
||||
@@ -21,258 +21,19 @@ package appeng.core.api;
|
||||
|
||||
import appeng.api.parts.CableRenderMode;
|
||||
import appeng.api.parts.IPartHelper;
|
||||
import appeng.api.parts.LayerBase;
|
||||
import appeng.core.AELog;
|
||||
import appeng.core.AppEng;
|
||||
import appeng.parts.PartPlacement;
|
||||
import appeng.tile.AEBaseTile;
|
||||
import appeng.tile.networking.TileCableBus;
|
||||
import com.google.common.cache.CacheBuilder;
|
||||
import com.google.common.cache.CacheLoader;
|
||||
import com.google.common.cache.LoadingCache;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.tileentity.TileEntity;
|
||||
import net.minecraft.util.EnumActionResult;
|
||||
import net.minecraft.util.EnumFacing;
|
||||
import net.minecraft.util.EnumHand;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.world.World;
|
||||
import org.objectweb.asm.ClassReader;
|
||||
import org.objectweb.asm.ClassWriter;
|
||||
import org.objectweb.asm.commons.ClassRemapper;
|
||||
import org.objectweb.asm.commons.Remapper;
|
||||
import org.objectweb.asm.tree.AbstractInsnNode;
|
||||
import org.objectweb.asm.tree.ClassNode;
|
||||
import org.objectweb.asm.tree.MethodInsnNode;
|
||||
import org.objectweb.asm.tree.MethodNode;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.*;
|
||||
|
||||
|
||||
public class ApiPart implements IPartHelper {
|
||||
|
||||
private final LoadingCache<CacheKey, Class<? extends AEBaseTile>> cache = CacheBuilder.newBuilder()
|
||||
.build(new CacheLoader<CacheKey, Class<? extends AEBaseTile>>() {
|
||||
@Override
|
||||
public Class<? extends AEBaseTile> load(CacheKey key) throws Exception {
|
||||
return ApiPart.this.generateCombinedClass(key);
|
||||
}
|
||||
});
|
||||
|
||||
private final Map<Class<?>, String> interfaces2Layer = new HashMap<>();
|
||||
private final List<String> desc = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* Conceptually this method will build a new class hierarchy that is rooted at the given base class, and includes a
|
||||
* chain of all registered layers.
|
||||
* <p/>
|
||||
* To accomplish this, it takes the first registered layer, replaces it's inheritance from LayerBase with an
|
||||
* inheritance from the given baseClass,
|
||||
* and uses the resulting class as the parent class for the next registered layer, for which it repeats this
|
||||
* process. This process is then repeated
|
||||
* until a class hierarchy of all layers is formed. While janking out the inheritance from LayerBase, it'll make
|
||||
* also sure that calls to that
|
||||
* classes method will instead be forwarded to the superclass that was inserted as part of the described process.
|
||||
* <p/>
|
||||
* Example: If layers A and B are registered, and TileCableBus is passed in as the baseClass, a synthetic class
|
||||
* A_B_TileCableBus should be returned,
|
||||
* which has A_B_TileCableBus -extends-> B_TileCableBus -extends-> TileCableBus as it's class hierarchy, where
|
||||
* A_B_TileCableBus has been generated
|
||||
* from A, and B_TileCableBus has been generated from B.
|
||||
*/
|
||||
public Class<? extends AEBaseTile> getCombinedInstance(final Class<? extends AEBaseTile> baseClass) {
|
||||
if (this.desc.isEmpty()) {
|
||||
// No layers registered...
|
||||
return baseClass;
|
||||
}
|
||||
|
||||
return this.cache.getUnchecked(new CacheKey(baseClass, this.desc));
|
||||
}
|
||||
|
||||
private Class<? extends AEBaseTile> generateCombinedClass(CacheKey cacheKey) {
|
||||
final Class<? extends AEBaseTile> parentClass;
|
||||
|
||||
// Get the list of interfaces that still need to be implemented beyond the current one
|
||||
List<String> remainingInterfaces = cacheKey.getInterfaces().subList(1, cacheKey.getInterfaces().size());
|
||||
|
||||
// We are not at the root of the class hierarchy yet
|
||||
if (!remainingInterfaces.isEmpty()) {
|
||||
CacheKey parentKey = new CacheKey(cacheKey.getBaseClass(), remainingInterfaces);
|
||||
parentClass = this.cache.getUnchecked(parentKey);
|
||||
} else {
|
||||
parentClass = cacheKey.getBaseClass();
|
||||
}
|
||||
|
||||
// Which interface should be implemented in this layer?
|
||||
String interfaceName = cacheKey.getInterfaces().get(0);
|
||||
|
||||
try {
|
||||
// This is the particular interface that this layer was registered for. Loading the class may fail if i.e.
|
||||
// an API is broken or not present
|
||||
// and in this case, the layer will be skipped!
|
||||
Class<?> interfaceClass = Class.forName(interfaceName);
|
||||
String layerImpl = this.interfaces2Layer.get(interfaceClass);
|
||||
|
||||
return this.getClassByDesc(parentClass, layerImpl);
|
||||
} catch (final Throwable t) {
|
||||
AELog.warn("Error loading " + interfaceName);
|
||||
AELog.debug(t);
|
||||
return parentClass;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private Class<? extends AEBaseTile> getClassByDesc(Class<? extends AEBaseTile> baseClass, final String next) {
|
||||
final ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
|
||||
final ClassNode n = this.getReader(next);
|
||||
final String originalName = n.name;
|
||||
|
||||
try {
|
||||
n.name = n.name + '_' + baseClass.getSimpleName();
|
||||
n.superName = baseClass.getName().replace('.', '/');
|
||||
} catch (final Throwable t) {
|
||||
AELog.debug(t);
|
||||
}
|
||||
|
||||
for (final MethodNode mn : n.methods) {
|
||||
final Iterator<AbstractInsnNode> i = mn.instructions.iterator();
|
||||
while (i.hasNext()) {
|
||||
this.processNode(i.next(), n.superName);
|
||||
}
|
||||
}
|
||||
|
||||
final DefaultPackageClassNameRemapper remapper = new DefaultPackageClassNameRemapper();
|
||||
remapper.inputOutput.put("appeng/api/parts/LayerBase", n.superName);
|
||||
remapper.inputOutput.put(originalName, n.name);
|
||||
n.accept(new ClassRemapper(cw, remapper));
|
||||
// n.accept( cw );
|
||||
|
||||
// n.accept( new TraceClassVisitor( new PrintWriter( System.out ) ) );
|
||||
final byte[] byteArray = cw.toByteArray();
|
||||
final int size = byteArray.length;
|
||||
final Class clazz = this.loadClass(n.name.replace("/", "."), byteArray);
|
||||
|
||||
try {
|
||||
final Object fish = clazz.newInstance();
|
||||
|
||||
boolean hasError = false;
|
||||
|
||||
if (!baseClass.isInstance(fish)) {
|
||||
hasError = true;
|
||||
AELog.error("Error, Expected layer to implement " + baseClass + " did not.");
|
||||
}
|
||||
|
||||
if (fish instanceof LayerBase) {
|
||||
hasError = true;
|
||||
AELog.error("Error, Expected layer to NOT implement LayerBase but it DID.");
|
||||
}
|
||||
|
||||
if (!(fish instanceof TileCableBus)) {
|
||||
hasError = true;
|
||||
AELog.error("Error, Expected layer to implement TileCableBus did not.");
|
||||
}
|
||||
|
||||
if (!(fish instanceof TileEntity)) {
|
||||
hasError = true;
|
||||
AELog.error("Error, Expected layer to implement TileEntity did not.");
|
||||
}
|
||||
|
||||
if (!hasError) {
|
||||
AELog.info("Layer: " + n.name + " loaded successfully - " + size + " bytes");
|
||||
}
|
||||
} catch (final Throwable t) {
|
||||
AELog.error("Layer: " + n.name + " Failed.");
|
||||
AELog.debug(t);
|
||||
}
|
||||
|
||||
return clazz;
|
||||
}
|
||||
|
||||
private ClassNode getReader(final String name) {
|
||||
final String path = '/' + name.replace(".", "/") + ".class";
|
||||
final InputStream is = this.getClass().getResourceAsStream(path);
|
||||
try {
|
||||
final ClassReader cr = new ClassReader(is);
|
||||
|
||||
final ClassNode cn = new ClassNode();
|
||||
cr.accept(cn, ClassReader.EXPAND_FRAMES);
|
||||
|
||||
return cn;
|
||||
} catch (final IOException e) {
|
||||
throw new IllegalStateException("Error loading " + name, e);
|
||||
}
|
||||
}
|
||||
|
||||
private void processNode(final AbstractInsnNode next, final String nePar) {
|
||||
if (next instanceof MethodInsnNode) {
|
||||
final MethodInsnNode min = (MethodInsnNode) next;
|
||||
if (min.owner.equals("appeng/api/parts/LayerBase")) {
|
||||
min.owner = nePar;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Class loadClass(final String name, byte[] b) {
|
||||
// override classDefine (as it is protected) and define the class.
|
||||
Class clazz = null;
|
||||
try {
|
||||
final ClassLoader loader = this.getClass().getClassLoader();// ClassLoader.getSystemClassLoader();
|
||||
final Class<ClassLoader> root = ClassLoader.class;
|
||||
final Class<? extends ClassLoader> cls = loader.getClass();
|
||||
final Method defineClassMethod = root.getDeclaredMethod("defineClass", String.class, byte[].class, int.class, int.class);
|
||||
final Method runTransformersMethod = cls.getDeclaredMethod("runTransformers", String.class, String.class, byte[].class);
|
||||
|
||||
runTransformersMethod.setAccessible(true);
|
||||
defineClassMethod.setAccessible(true);
|
||||
try {
|
||||
final Object[] argsA = {
|
||||
name,
|
||||
name,
|
||||
b
|
||||
};
|
||||
b = (byte[]) runTransformersMethod.invoke(loader, argsA);
|
||||
|
||||
final Object[] args = {
|
||||
name,
|
||||
b,
|
||||
0,
|
||||
b.length
|
||||
};
|
||||
clazz = (Class) defineClassMethod.invoke(loader, args);
|
||||
} finally {
|
||||
runTransformersMethod.setAccessible(false);
|
||||
defineClassMethod.setAccessible(false);
|
||||
}
|
||||
} catch (final Exception e) {
|
||||
AELog.debug(e);
|
||||
throw new IllegalStateException("Unable to manage part API.", e);
|
||||
}
|
||||
return clazz;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean registerNewLayer(final String layer, final String layerInterface) {
|
||||
try {
|
||||
final Class<?> layerInterfaceClass = Class.forName(layerInterface);
|
||||
if (this.interfaces2Layer.get(layerInterfaceClass) == null) {
|
||||
this.interfaces2Layer.put(layerInterfaceClass, layer);
|
||||
this.desc.add(layerInterface);
|
||||
return true;
|
||||
} else {
|
||||
AELog.info("Layer " + layer + " not registered, " + layerInterface + " already has a layer.");
|
||||
}
|
||||
} catch (final Throwable ignored) {
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public EnumActionResult placeBus(final ItemStack is, final BlockPos pos, final EnumFacing side, final EntityPlayer player, final EnumHand hand, final World w) {
|
||||
return PartPlacement.place(is, pos, side, player, hand, w, PartPlacement.PlaceType.PLACE_ITEM, 0);
|
||||
@@ -282,58 +43,4 @@ public class ApiPart implements IPartHelper {
|
||||
public CableRenderMode getCableRenderMode() {
|
||||
return AppEng.proxy.getRenderMode();
|
||||
}
|
||||
|
||||
private static class DefaultPackageClassNameRemapper extends Remapper {
|
||||
|
||||
private final HashMap<String, String> inputOutput = new HashMap<>();
|
||||
|
||||
@Override
|
||||
public String map(final String typeName) {
|
||||
final String o = this.inputOutput.get(typeName);
|
||||
if (o == null) {
|
||||
return typeName;
|
||||
}
|
||||
return o;
|
||||
}
|
||||
}
|
||||
|
||||
private static class CacheKey {
|
||||
private final Class<? extends AEBaseTile> baseClass;
|
||||
|
||||
private final List<String> interfaces;
|
||||
|
||||
private CacheKey(Class<? extends AEBaseTile> baseClass, List<String> interfaces) {
|
||||
this.baseClass = baseClass;
|
||||
this.interfaces = ImmutableList.copyOf(interfaces);
|
||||
}
|
||||
|
||||
private Class<? extends AEBaseTile> getBaseClass() {
|
||||
return this.baseClass;
|
||||
}
|
||||
|
||||
private List<String> getInterfaces() {
|
||||
return this.interfaces;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (o == null || this.getClass() != o.getClass()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
CacheKey cacheKey = (CacheKey) o;
|
||||
|
||||
return this.baseClass.equals(cacheKey.baseClass) && this.interfaces.equals(cacheKey.interfaces);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = this.baseClass.hashCode();
|
||||
result = 31 * result + this.interfaces.hashCode();
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -65,7 +65,6 @@ import java.util.*;
|
||||
public class CableBusContainer extends CableBusStorage implements AEMultiTile, ICableBusContainer {
|
||||
|
||||
private static final ThreadLocal<Boolean> IS_LOADING = new ThreadLocal<>();
|
||||
private final EnumSet<LayerFlags> myLayerFlags = EnumSet.noneOf(LayerFlags.class);
|
||||
private YesNo hasRedstone = YesNo.UNDECIDED;
|
||||
private IPartHost tcb;
|
||||
// TODO 1.10.2-R - does somebody seriously want to make parts TESR??? Hope not.
|
||||
@@ -424,11 +423,6 @@ public class CableBusContainer extends CableBusStorage implements AEMultiTile, I
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<LayerFlags> getLayerFlags() {
|
||||
return this.myLayerFlags;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cleanup() {
|
||||
this.tcb.cleanup();
|
||||
|
||||
@@ -22,13 +22,11 @@ package appeng.tile.networking;
|
||||
import appeng.api.networking.IGridNode;
|
||||
import appeng.api.parts.IFacadeContainer;
|
||||
import appeng.api.parts.IPart;
|
||||
import appeng.api.parts.LayerFlags;
|
||||
import appeng.api.parts.SelectedPart;
|
||||
import appeng.api.util.AECableType;
|
||||
import appeng.api.util.AEColor;
|
||||
import appeng.api.util.AEPartLocation;
|
||||
import appeng.api.util.DimensionalCoord;
|
||||
import appeng.block.networking.BlockCableBus;
|
||||
import appeng.helpers.AEMultiTile;
|
||||
import appeng.helpers.ICustomCollision;
|
||||
import appeng.hooks.TickHandler;
|
||||
@@ -101,7 +99,7 @@ public class TileCableBus extends AEBaseTile implements AEMultiTile, ICustomColl
|
||||
protected void updateTileSetting() {
|
||||
if (this.getCableBus().isRequiresDynamicRender()) {
|
||||
try {
|
||||
final TileCableBus tcb = (TileCableBus) BlockCableBus.getTesrTile().newInstance();
|
||||
final TileCableBus tcb = TileCableBusTESR.class.newInstance();
|
||||
tcb.copyFrom(this);
|
||||
this.getWorld().setTileEntity(this.pos, tcb);
|
||||
} catch (final Throwable ignored) {
|
||||
@@ -283,11 +281,6 @@ public class TileCableBus extends AEBaseTile implements AEMultiTile, ICustomColl
|
||||
return this.getCableBus().isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<LayerFlags> getLayerFlags() {
|
||||
return this.getCableBus().getLayerFlags();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cleanup() {
|
||||
this.getWorld().setBlockToAir(this.pos);
|
||||
|
||||
@@ -19,9 +19,6 @@
|
||||
package appeng.tile.networking;
|
||||
|
||||
|
||||
import appeng.block.networking.BlockCableBus;
|
||||
|
||||
|
||||
public class TileCableBusTESR extends TileCableBus {
|
||||
|
||||
/**
|
||||
@@ -31,7 +28,7 @@ public class TileCableBusTESR extends TileCableBus {
|
||||
protected void updateTileSetting() {
|
||||
if (!this.getCableBus().isRequiresDynamicRender()) {
|
||||
try {
|
||||
final TileCableBus tcb = (TileCableBus) BlockCableBus.getNoTesrTile().newInstance();
|
||||
final TileCableBus tcb = TileCableBus.class.newInstance();
|
||||
tcb.copyFrom(this);
|
||||
this.getWorld().setTileEntity(this.pos, tcb);
|
||||
} catch (final Throwable ignored) {
|
||||
|
||||
Reference in New Issue
Block a user