Remove part layer api in favor of capabilities (#355)

This commit is contained in:
Serenibyss
2024-01-08 09:52:05 -06:00
committed by GitHub
parent 0b7341e4a8
commit 4829ec8286
9 changed files with 8 additions and 496 deletions
@@ -35,32 +35,6 @@ import net.minecraft.world.World;
public interface IPartHelper
{
/**
* Register a new layer with the part layer system, this allows you to write an in between between tile entities and
* parts.
*
* AE By Default includes,
*
* 1. ISidedInventory ( and by extension IInventory. )
*
* 2. IFluidHandler Forge Fluids
*
* 3. IPowerEmitter BC Power output.
*
* 4. IPowerReceptor BC Power input.
*
* 5. IEnergySink IC2 Power input.
*
* 6. IEnergySource IC2 Power output.
*
* 7. IPipeConnection BC Pipe Connections
*
* As long as a valid layer is registered for a interface you can simply implement that interface on a part get
* implement it.
*
* @return true on success, false on failure, usually a error will be logged as well.
*/
boolean registerNewLayer( String string, String layerInterface );
/**
* use in use item, to try and place a IBusItem
+2 -7
View File
@@ -151,7 +151,7 @@ public interface IPartHost extends ICustomCableConnection
void markForSave();
/**
* part of the {@link LayerBase}
* called when parts are added or removed
*/
void partChanged();
@@ -169,11 +169,6 @@ public interface IPartHost extends ICustomCableConnection
*/
boolean isEmpty();
/**
* @return a mutable list of flags you can adjust to track state.
*/
Set<LayerFlags> getLayerFlags();
/**
* remove host from world...
*/
@@ -191,4 +186,4 @@ public interface IPartHost extends ICustomCableConnection
*/
boolean isInWorld();
}
}
@@ -1,98 +0,0 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2013 AlgorithmX2
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package appeng.api.parts;
import java.util.Set;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.EnumFacing;
import appeng.api.util.AEPartLocation;
/**
* All Layers must extends this, this get part implementation is provided to interface with the parts, however a real
* implementation will be used at runtime.
*
* TODO: Consider removing and replacing with capabilities.
*/
public abstract class LayerBase extends TileEntity // implements IPartHost
{
/**
* Grants access for the layer to the parts of the host.
*
* This Method looks silly, that is because its not used at runtime, a real implementation will be used instead.
*
* @param side side of part
*
* @return the part for the requested side.
*/
public IPart getPart( final AEPartLocation side )
{
return null; // place holder.
}
/**
* Grants access for the layer to the parts of the host.
*
* This Method looks silly, that is because its not used at runtime, a real implementation will be used instead.
*
* @param side side of part
*
* @return the part for the requested side.
*/
public IPart getPart( final EnumFacing side )
{
return null; // place holder.
}
/**
* called when the parts change in the container, YOU MUST CALL super.PartChanged();
*/
public void notifyNeighbors()
{
}
/**
* called when the parts change in the container, YOU MUST CALL super.PartChanged();
*/
public void partChanged()
{
}
/**
* @return a mutable list of flags you can adjust to track state.
*/
public Set<LayerFlags> getLayerFlags()
{
return null; // place holder.
}
public void markForSave()
{
// something!
}
}
@@ -1,35 +0,0 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2013 AlgorithmX2
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package appeng.api.parts;
/**
* TODO: Consider removing and replacing with capabilities.
*/
public enum LayerFlags
{
IC2_ENET
}
@@ -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 {
-293
View File
@@ -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) {