diff --git a/gradlew b/gradlew old mode 100644 new mode 100755 diff --git a/src/main/java/com/enderio/core/common/TileEntityBase.java b/src/main/java/com/enderio/core/common/TileEntityBase.java index 9ea39439..64e33429 100644 --- a/src/main/java/com/enderio/core/common/TileEntityBase.java +++ b/src/main/java/com/enderio/core/common/TileEntityBase.java @@ -1,9 +1,5 @@ package com.enderio.core.common; -import com.enderio.core.api.common.util.IProgressTile; -import com.enderio.core.common.network.EnderPacketHandler; -import com.enderio.core.common.network.PacketProgress; - import net.minecraft.block.state.IBlockState; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.item.ItemStack; @@ -16,6 +12,15 @@ import net.minecraft.util.ITickable; import net.minecraft.world.World; +import com.enderio.core.api.common.util.IProgressTile; +import com.enderio.core.common.autosave.Reader; +import com.enderio.core.common.autosave.Writer; +import com.enderio.core.common.autosave.annotations.Storable; +import com.enderio.core.common.autosave.annotations.Store.StoreFor; +import com.enderio.core.common.network.EnderPacketHandler; +import com.enderio.core.common.network.PacketProgress; + +@Storable public abstract class TileEntityBase extends TileEntity implements ITickable { private final int checkOffset = (int) (Math.random() * 20); @@ -69,37 +74,44 @@ protected int getProgressUpdateFreq() { return 20; } - @Override - public final void readFromNBT(NBTTagCompound root) { - super.readFromNBT(root); - readCustomNBT(root); - } - - @Override - public final void writeToNBT(NBTTagCompound root) { - super.writeToNBT(root); - writeCustomNBT(root); - } - - @Override - public Packet getDescriptionPacket() { - NBTTagCompound tag = new NBTTagCompound(); - writeCustomNBT(tag); - return new S35PacketUpdateTileEntity(getPos(), 1, tag); - } - - @Override - public void onDataPacket(NetworkManager net, S35PacketUpdateTileEntity pkt) { - readCustomNBT(pkt.getNbtCompound()); - } - - public boolean canPlayerAccess(EntityPlayer player) { - return !isInvalid() && player.getDistanceSqToCenter(getPos().add(0.5, 0.5, 0.5)) <= 64D; - } - - protected abstract void writeCustomNBT(NBTTagCompound root); - - protected abstract void readCustomNBT(NBTTagCompound root); + @Override + public final void readFromNBT(NBTTagCompound root) { + super.readFromNBT(root); + Reader.read(StoreFor.SAVE, root, this); + } + + @Override + public final void writeToNBT(NBTTagCompound root) { + super.writeToNBT(root); + Writer.write(StoreFor.SAVE, root, this); + } + + @Override + public final Packet getDescriptionPacket() { + NBTTagCompound root = new NBTTagCompound(); + super.writeToNBT(root); + Writer.write(StoreFor.CLIENT, root, this); + return new S35PacketUpdateTileEntity(getPos(), 1, root); + } + + @Override + public final void onDataPacket(NetworkManager net, S35PacketUpdateTileEntity pkt) { + NBTTagCompound root = pkt.getNbtCompound(); + super.readFromNBT(root); + Reader.read(StoreFor.CLIENT, root, this); + } + + protected final void readItemNBT(NBTTagCompound root) { + Reader.read(StoreFor.ITEM, root, this); + } + + protected final void writeItemNBT(NBTTagCompound root) { + Writer.write(StoreFor.ITEM, root, this); + } + + public boolean canPlayerAccess(EntityPlayer player) { + return !isInvalid() && player.getDistanceSqToCenter(getPos().add(0.5, 0.5, 0.5)) <= 64D; + } protected void updateBlock() { if (worldObj != null) { diff --git a/src/main/java/com/enderio/core/common/autosave/Reader.java b/src/main/java/com/enderio/core/common/autosave/Reader.java new file mode 100755 index 00000000..c2274f7e --- /dev/null +++ b/src/main/java/com/enderio/core/common/autosave/Reader.java @@ -0,0 +1,165 @@ +package com.enderio.core.common.autosave; + +import java.util.EnumSet; +import java.util.Set; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +import com.enderio.core.common.autosave.annotations.Storable; +import com.enderio.core.common.autosave.annotations.Store; +import com.enderio.core.common.autosave.annotations.Store.StoreFor; +import com.enderio.core.common.autosave.engine.StorableEngine; +import com.enderio.core.common.autosave.exceptions.NoHandlerFoundException; +import com.enderio.core.common.autosave.handlers.IHandler; +import com.enderio.core.common.util.NullHelper; + +import net.minecraft.nbt.NBTTagCompound; + +/** + * Restore an object's fields from NBT data. + * + */ +public class Reader { + + /** + * Restore an object's fields from NBT data as if its class was annotated + * {@link Storable} without a special handler. + * + *

+ * See also: {@link Store} for the field annotation. + * + * @param registry + * The {@link Registry} to look up {@link IHandler}s for the fields + * of the given object + * @param phase + * A set of {@link StoreFor}s to indicate which fields to process. + * Only fields that are annotated with a matching {@link StoreFor} + * are restored. + * @param tag + * A {@link NBTTagCompound} to read from. This NBTTagCompound + * represents the whole object, with its fields in the tags. + * @param object + * The object that should be restored + */ + public static void read(@Nonnull Registry registry, @Nonnull Set phase, @Nonnull NBTTagCompound tag, @Nonnull T object) { + try { + StorableEngine.read(registry, phase, tag, object); + } catch (IllegalAccessException e) { + throw new RuntimeException(e); + } catch (InstantiationException e) { + throw new RuntimeException(e); + } catch (NoHandlerFoundException e) { + throw new RuntimeException(e); + } + } + + /** + * Restore an object's fields from NBT data as if its class was annotated + * {@link Storable} without a special handler using the {@link Registry} + * {@link Registry#GLOBAL_REGISTRY GLOBAL_REGISTRY}. + * + *

+ * See also: {@link Store} for the field annotation. + * + * @param phase + * A set of {@link StoreFor}s to indicate which fields to process. + * Only fields that are annotated with a matching {@link StoreFor} + * are restored. + * @param tag + * A {@link NBTTagCompound} to read from. This NBTTagCompound + * represents the whole object, with its fields in the tags. + * @param object + * The object that should be restored + */ + public static void read(@Nullable Set phase, @Nullable NBTTagCompound tag, @Nonnull T object) { + read(Registry.GLOBAL_REGISTRY, NullHelper.notnull(phase, "Missing phase"), NullHelper.notnull(tag, "Missing NBT"), object); + } + + /** + * Restore an object's fields from NBT data as if its class was annotated + * {@link Storable} without a special handler. + * + *

+ * See also: {@link Store} for the field annotation. + * + * @param registry + * The {@link Registry} to look up {@link IHandler}s for the fields + * of the given object + * @param phase + * A s{@link StoreFor} to indicate which fields to process. Only + * fields that are annotated with a matching {@link StoreFor} are + * restored. + * @param tag + * A {@link NBTTagCompound} to read from. This NBTTagCompound + * represents the whole object, with its fields in the tags. + * @param object + * The object that should be restored + */ + public static void read(@Nonnull Registry registry, @Nonnull StoreFor phase, @Nullable NBTTagCompound tag, @Nonnull T object) { + read(registry, NullHelper.notnullJ(EnumSet.of(phase), "EnumSet.of()"), NullHelper.notnull(tag, "Missing NBT"), object); + } + + /** + * Restore an object's fields from NBT data as if its class was annotated + * {@link Storable} without a special handler using the {@link Registry} + * {@link Registry#GLOBAL_REGISTRY GLOBAL_REGISTRY}. + * + *

+ * See also: {@link Store} for the field annotation. + * + * @param phase + * A s{@link StoreFor} to indicate which fields to process. Only + * fields that are annotated with a matching {@link StoreFor} are + * restored. + * @param tag + * A {@link NBTTagCompound} to read from. This NBTTagCompound + * represents the whole object, with its fields in the tags. + * @param object + * The object that should be restored + */ + public static void read(@Nonnull StoreFor phase, @Nullable NBTTagCompound tag, @Nonnull T object) { + read(Registry.GLOBAL_REGISTRY, NullHelper.notnullJ(EnumSet.of(phase), "EnumSet.of()"), NullHelper.notnull(tag, "Missing NBT"), object); + } + + /** + * Restore an object's fields from NBT data as if its class was annotated + * {@link Storable} without a special handler, ignoring {@link StoreFor} + * restrictions. + * + *

+ * See also: {@link Store} for the field annotation. + * + * @param registry + * The {@link Registry} to look up {@link IHandler}s for the fields + * of the given object + * @param tag + * A {@link NBTTagCompound} to read from. This NBTTagCompound + * represents the whole object, with its fields in the tags. + * @param object + * The object that should be restored + */ + public static void read(@Nonnull Registry registry, @Nullable NBTTagCompound tag, @Nonnull T object) { + read(registry, NullHelper.notnullJ(EnumSet.allOf(StoreFor.class), "EnumSet.allOf()"), NullHelper.notnull(tag, "Missing NBT"), object); + } + + /** + * Restore an object's fields from NBT data as if its class was annotated + * {@link Storable} without a special handler using the {@link Registry} + * {@link Registry#GLOBAL_REGISTRY GLOBAL_REGISTRY}, ignoring {@link StoreFor} + * restrictions. + * + *

+ * See also: {@link Store} for the field annotation. + * + * @param tag + * A {@link NBTTagCompound} to read from. This NBTTagCompound + * represents the whole object, with its fields in the tags. + * @param object + * The object that should be restored + */ + public static void read(@Nullable NBTTagCompound tag, @Nonnull T object) { + read(Registry.GLOBAL_REGISTRY, NullHelper.notnullJ(EnumSet.allOf(StoreFor.class), "EnumSet.allOf()"), NullHelper.notnull(tag, "Missing NBT"), object); + } + +} diff --git a/src/main/java/com/enderio/core/common/autosave/Registry.java b/src/main/java/com/enderio/core/common/autosave/Registry.java new file mode 100755 index 00000000..5745b6d2 --- /dev/null +++ b/src/main/java/com/enderio/core/common/autosave/Registry.java @@ -0,0 +1,185 @@ +package com.enderio.core.common.autosave; + +import java.util.ArrayList; +import java.util.List; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +import com.enderio.core.common.autosave.annotations.Storable; +import com.enderio.core.common.autosave.handlers.IHandler; +import com.enderio.core.common.autosave.handlers.endercore.HandleBlockCoord; +import com.enderio.core.common.autosave.handlers.forge.HandleFluid; +import com.enderio.core.common.autosave.handlers.forge.HandleFluidStack; +import com.enderio.core.common.autosave.handlers.internal.HandleStorable; +import com.enderio.core.common.autosave.handlers.java.HandleBoolean; +import com.enderio.core.common.autosave.handlers.java.HandleEnum; +import com.enderio.core.common.autosave.handlers.java.HandleFloat; +import com.enderio.core.common.autosave.handlers.java.HandleFloatArray; +import com.enderio.core.common.autosave.handlers.java.HandleInteger; +import com.enderio.core.common.autosave.handlers.minecraft.HandleBlockPos; +import com.enderio.core.common.autosave.handlers.minecraft.HandleItem; +import com.enderio.core.common.autosave.handlers.minecraft.HandleItemStack; + +/** + * A registry for {@link IHandler}s. + * + *

+ * Registries use Java-like inheritance. That means any registry, except the + * base registry {@link Registry#GLOBAL_REGISTRY}, has exactly one + * super-registry. When looking for handlers, all handlers from this registry + * and all its super-registries will be returned in order. + * + */ +public class Registry { + + /** + * This is the super-registry of all registries. It contains handlers for Java + * primitives, Java classes, Minecraft classes and Forge classes. + *

+ * You can register new handlers here if you want other mods to be able to + * store your objects. Otherwise please use your own registry. + */ + @Nonnull + public static final Registry GLOBAL_REGISTRY = new Registry(true); + + static { + // Java primitives + GLOBAL_REGISTRY.register(new HandleFloat()); + GLOBAL_REGISTRY.register(new HandleInteger()); + GLOBAL_REGISTRY.register(new HandleBoolean()); + GLOBAL_REGISTRY.register(new HandleFloatArray()); + GLOBAL_REGISTRY.register(new HandleEnum()); + + // Minecraft basic types + GLOBAL_REGISTRY.register(new HandleItemStack()); + GLOBAL_REGISTRY.register(new HandleItem()); + GLOBAL_REGISTRY.register(new HandleBlockPos()); + + // Forge basic types + GLOBAL_REGISTRY.register(new HandleFluidStack()); + GLOBAL_REGISTRY.register(new HandleFluid()); + + // Annotated objects + GLOBAL_REGISTRY.register(new HandleStorable()); + + // EnderCore types + GLOBAL_REGISTRY.register(new HandleBlockCoord()); + } + + @Nonnull + private final List handlers = new ArrayList(); + @Nullable + private final Registry parent; + + /** + * Creates the {@link Registry#GLOBAL_REGISTRY}. + * + * @param root + * A placeholder + */ + private Registry(boolean root) { + parent = root ? null : null; + } + + /** + * Crates a new registry which extends {@link Registry#GLOBAL_REGISTRY}. + */ + public Registry() { + this(GLOBAL_REGISTRY); + } + + /** + * Creates a new registry which extends the given parent. + * + * @param parent + * The parent to extend + */ + public Registry(@Nonnull Registry parent) { + this.parent = parent; + } + + /** + * Registers a new {@link IHandler}. + * + * @param handler + * The {@link IHandler} to register + */ + public void register(@Nonnull IHandler handler) { + handlers.add(handler); + } + + /** + * Finds all {@link IHandler}s from this registry and all its parents that can + * handle the given class. + * + *

+ * Handlers will be returned in this order: + *

    + *
  1. The annotated special handler of the given class + *
  2. The annotated special handler(s) of its superclass(es) + *
  3. The registered handlers from this registry + *
  4. The registered handlers from this registry's super-registries + *
  5. {@link HandleStorable} if the class is annotated {@link Storable} + * without a special handler + *
+ * + * Note: If a class is annotated {@link Storable}, then all subclasses must be + * annotated {@link Storable}, too. + *

+ * Note 2: If a class is annotated {@link Storable} without a special handler, + * all subclasses must either also be annotated {@link Storable} without a + * special handler or their handlers must be able to handle the inheritance + * because {@link HandleStorable} will not be added to this list in + * this case. + *

+ * Note 3: If a handler can handle a class but not its subclasses, it will not + * be added to this list for the subclasses. + * + * @param clazz + * The class that should be handled + * @return A list of all {@link IHandler}s that can handle the class. If none + * are found, an empty list is returned. + * @throws InstantiationException + * @throws IllegalAccessException + */ + @Nonnull + public List findHandlers(Class clazz) throws InstantiationException, IllegalAccessException { + List result = new ArrayList(); + + Storable annotation = clazz.getAnnotation(Storable.class); + while (annotation != null) { + if (annotation.handler() != HandleStorable.class) { + result.add(annotation.handler().newInstance()); + } + Class superclass = clazz.getSuperclass(); + if (superclass != null) { + annotation = superclass.getAnnotation(Storable.class); + } + } + + findRegisteredHandlers(clazz, result); + + return result; + } + + /** + * Helper method for {@link #findHandlers(Class)}. Looks up only registered + * handlers and adds them to the end of the given list. + * + * @param clazz + * @param result + */ + private void findRegisteredHandlers(Class clazz, List result) { + for (IHandler handler : handlers) { + if (handler.canHandle(clazz)) { + result.add(handler); + } + } + final Registry thisParent = parent; + if (thisParent != null) { + thisParent.findRegisteredHandlers(clazz, result); + } + } + +} diff --git a/src/main/java/com/enderio/core/common/autosave/Writer.java b/src/main/java/com/enderio/core/common/autosave/Writer.java new file mode 100755 index 00000000..2d579df1 --- /dev/null +++ b/src/main/java/com/enderio/core/common/autosave/Writer.java @@ -0,0 +1,165 @@ +package com.enderio.core.common.autosave; + +import java.util.EnumSet; +import java.util.Set; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +import net.minecraft.nbt.NBTTagCompound; + +import com.enderio.core.common.autosave.annotations.Storable; +import com.enderio.core.common.autosave.annotations.Store; +import com.enderio.core.common.autosave.annotations.Store.StoreFor; +import com.enderio.core.common.autosave.engine.StorableEngine; +import com.enderio.core.common.autosave.exceptions.NoHandlerFoundException; +import com.enderio.core.common.autosave.handlers.IHandler; +import com.enderio.core.common.util.NullHelper; + +/** + * Store an object's fields to NBT data. + * + */ +public class Writer { + + /** + * Store an object's fields to NBT data as if its class was annotated + * {@link Storable} without a special handler. + * + *

+ * See also: {@link Store} for the field annotation. + * + * @param registry + * The {@link Registry} to look up {@link IHandler}s for the fields + * of the given object + * @param phase + * A set of {@link StoreFor}s to indicate which fields to process. + * Only fields that are annotated with a matching {@link StoreFor} + * are stored. + * @param tag + * A {@link NBTTagCompound} to write to. This NBTTagCompound + * represents the whole object, with its fields in the tags. + * @param object + * The object that should be stored + */ + public static void write(@Nonnull Registry registry, @Nonnull Set phase, @Nonnull NBTTagCompound tag, @Nonnull T object) { + try { + StorableEngine.store(registry, phase, tag, object); + } catch (IllegalAccessException e) { + throw new RuntimeException(e); + } catch (InstantiationException e) { + throw new RuntimeException(e); + } catch (NoHandlerFoundException e) { + throw new RuntimeException(e); + } + } + + /** + * Store an object's fields to NBT data as if its class was annotated + * {@link Storable} without a special handler using the {@link Registry} + * {@link Registry#GLOBAL_REGISTRY GLOBAL_REGISTRY}. + * + *

+ * See also: {@link Store} for the field annotation. + * + * @param phase + * A set of {@link StoreFor}s to indicate which fields to process. + * Only fields that are annotated with a matching {@link StoreFor} + * are stored. + * @param tag + * A {@link NBTTagCompound} to write to. This NBTTagCompound + * represents the whole object, with its fields in the tags. + * @param object + * The object that should be stored + */ + public static void write(@Nullable Set phase, @Nullable NBTTagCompound tag, @Nonnull T object) { + write(Registry.GLOBAL_REGISTRY, NullHelper.notnull(phase, "Missing phase"), NullHelper.notnull(tag, "Missing NBT"), object); + } + + /** + * Store an object's fields to NBT data as if its class was annotated + * {@link Storable} without a special handler. + * + *

+ * See also: {@link Store} for the field annotation. + * + * @param registry + * The {@link Registry} to look up {@link IHandler}s for the fields + * of the given object + * @param phase + * A s{@link StoreFor} to indicate which fields to process. Only + * fields that are annotated with a matching {@link StoreFor} are + * stored. + * @param tag + * A {@link NBTTagCompound} to write to. This NBTTagCompound + * represents the whole object, with its fields in the tags. + * @param object + * The object that should be stored + */ + public static void write(@Nonnull Registry registry, @Nonnull StoreFor phase, @Nullable NBTTagCompound tag, @Nonnull T object) { + write(registry, NullHelper.notnullJ(EnumSet.of(phase), "EnumSet.of()"), NullHelper.notnull(tag, "Missing NBT"), object); + } + + /** + * Store an object's fields to NBT data as if its class was annotated + * {@link Storable} without a special handler using the {@link Registry} + * {@link Registry#GLOBAL_REGISTRY GLOBAL_REGISTRY}. + * + *

+ * See also: {@link Store} for the field annotation. + * + * @param phase + * A s{@link StoreFor} to indicate which fields to process. Only + * fields that are annotated with a matching {@link StoreFor} are + * stored. + * @param tag + * A {@link NBTTagCompound} to write to. This NBTTagCompound + * represents the whole object, with its fields in the tags. + * @param object + * The object that should be stored + */ + public static void write(@Nonnull StoreFor phase, @Nullable NBTTagCompound tag, @Nonnull T object) { + write(Registry.GLOBAL_REGISTRY, NullHelper.notnullJ(EnumSet.of(phase), "EnumSet.of()"), NullHelper.notnull(tag, "Missing NBT"), object); + } + + /** + * Store an object's fields to NBT data as if its class was annotated + * {@link Storable} without a special handler, ignoring {@link StoreFor} + * restrictions. + * + *

+ * See also: {@link Store} for the field annotation. + * + * @param registry + * The {@link Registry} to look up {@link IHandler}s for the fields + * of the given object + * @param tag + * A {@link NBTTagCompound} to write to. This NBTTagCompound + * represents the whole object, with its fields in the tags. + * @param object + * The object that should be stored + */ + public static void write(@Nonnull Registry registry, @Nullable NBTTagCompound tag, @Nonnull T object) { + write(registry, NullHelper.notnullJ(EnumSet.allOf(StoreFor.class), "EnumSet.allOf()"), NullHelper.notnull(tag, "Missing NBT"), object); + } + + /** + * Store an object's fields to NBT data as if its class was annotated + * {@link Storable} without a special handler using the {@link Registry} + * {@link Registry#GLOBAL_REGISTRY GLOBAL_REGISTRY}, ignoring {@link StoreFor} + * restrictions. + * + *

+ * See also: {@link Store} for the field annotation. + * + * @param tag + * A {@link NBTTagCompound} to write to NBTTagCompound represents the + * whole object, with its fields in the tags. + * @param object + * The object that should be stored + */ + public static void write(@Nullable NBTTagCompound tag, @Nonnull T object) { + write(Registry.GLOBAL_REGISTRY, NullHelper.notnullJ(EnumSet.allOf(StoreFor.class), "EnumSet.allOf()"), NullHelper.notnull(tag, "Missing NBT"), object); + } + +} diff --git a/src/main/java/com/enderio/core/common/autosave/annotations/Storable.java b/src/main/java/com/enderio/core/common/autosave/annotations/Storable.java new file mode 100755 index 00000000..0ca7bada --- /dev/null +++ b/src/main/java/com/enderio/core/common/autosave/annotations/Storable.java @@ -0,0 +1,38 @@ +package com.enderio.core.common.autosave.annotations; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import com.enderio.core.common.autosave.Reader; +import com.enderio.core.common.autosave.Writer; +import com.enderio.core.common.autosave.handlers.IHandler; +import com.enderio.core.common.autosave.handlers.internal.HandleStorable; + +/** + * Marks a class to be stored in NBT. If a special handler is given, it will be + * used when objects of this class are to be (re-)stored to/from NBT as field of + * another class. Otherwise {@link HandleStorable} will handle this class by + * (re-)storing those of its fields that are annotated {@link Store}. + * + *

+ * Please note that while an object's direct class does not need to be annotated + * for the object to be processed by {@link Reader} or {@link Writer}, you + * should nevertheless do so to enable subclassing it to work. + * + *

+ * Parameters: + * + *

+ */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.TYPE) +public @interface Storable { + + public Class handler() default HandleStorable.class; + +} diff --git a/src/main/java/com/enderio/core/common/autosave/annotations/Store.java b/src/main/java/com/enderio/core/common/autosave/annotations/Store.java new file mode 100755 index 00000000..772c8d9d --- /dev/null +++ b/src/main/java/com/enderio/core/common/autosave/annotations/Store.java @@ -0,0 +1,58 @@ +package com.enderio.core.common.autosave.annotations; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import com.enderio.core.common.autosave.handlers.IHandler; +import com.enderio.core.common.autosave.handlers.internal.NullHandler; + +/** + * Marks a field to be stored in NBT. + * + *

+ * Parameters: + * + *

+ * + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.FIELD) +public @interface Store { + + // Note: @Inherit does not work on fields. HandleStorable has special code to handle that. + + /** + * Designates the targets NBT can be stored for. + * + * + */ + public enum StoreFor { + /** + * Store in the world save + */ + SAVE, + /** + * Send to the client on status updates + */ + CLIENT, + /** + * Store in the item when the block is broken + */ + ITEM; + } + + public StoreFor[] value() default { StoreFor.SAVE, StoreFor.CLIENT, StoreFor.ITEM }; + + public Class handler() default NullHandler.class; +} diff --git a/src/main/java/com/enderio/core/common/autosave/engine/StorableEngine.java b/src/main/java/com/enderio/core/common/autosave/engine/StorableEngine.java new file mode 100755 index 00000000..d17f4d2b --- /dev/null +++ b/src/main/java/com/enderio/core/common/autosave/engine/StorableEngine.java @@ -0,0 +1,202 @@ +package com.enderio.core.common.autosave.engine; + +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.EnumSet; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import javax.annotation.Nonnull; + +import com.enderio.core.common.autosave.Reader; +import com.enderio.core.common.autosave.Registry; +import com.enderio.core.common.autosave.Writer; +import com.enderio.core.common.autosave.annotations.Storable; +import com.enderio.core.common.autosave.annotations.Store; +import com.enderio.core.common.autosave.annotations.Store.StoreFor; +import com.enderio.core.common.autosave.exceptions.NoHandlerFoundException; +import com.enderio.core.common.autosave.handlers.IHandler; +import com.enderio.core.common.autosave.handlers.internal.HandleStorable; +import com.enderio.core.common.autosave.handlers.internal.NullHandler; + +import net.minecraft.nbt.NBTTagCompound; + +/** + * The thread-safe engine that handles (re-)storing {@link Storable} objects by + * storing their fields. The fields to (re-)store must be annotated + * {@link Store}. + *

+ * It will also process the annotated fields of superclasses, as long as there + * is an unbroken chain of {@link Storable} annotations (without special + * handlers). Fields that have the same name as a field in a sub-/super-class + * will be processed independently. + *

+ * If the final superclass has an {@link IHandler} registered in the + * {@link Registry}, it will also be processed. However, this will not + * work for handlers that return a new object instead of changing the given one. + * A handler can check for this case by seeing if its "name" parameter is + * {@link StorableEngine#SUPERCLASS_KEY}. + *

+ * Note: If a {@link Storable} object is encountered in a {@link Store} field, + * it is handled by {@link HandleStorable}---which delegates here. + *

+ * Note 2: There are public entrances to this class in {@link Writer} and + * {@link Reader}. + */ +public class StorableEngine { + + private static final ThreadLocal INSTANCE = new ThreadLocal() { + @Override + protected StorableEngine initialValue() { + return new StorableEngine(); + } + }; + + public static final @Nonnull String NULL_POSTFIX = "__null"; + public static final @Nonnull String SUPERCLASS_KEY = "__superclass"; + private final @Nonnull Map, List> fieldCache = new HashMap, List>(); + private final @Nonnull Map> phaseCache = new HashMap>(); + private final @Nonnull Map> fieldHandlerCache = new HashMap>(); + private final @Nonnull Map, Class> superclassCache = new HashMap, Class>(); + private final @Nonnull Map, List> superclassHandlerCache = new HashMap, List>(); + + private StorableEngine() { + } + + public static void read(@Nonnull Registry registry, @Nonnull Set phase, @Nonnull NBTTagCompound tag, @Nonnull T object) + throws IllegalAccessException, InstantiationException, NoHandlerFoundException { + INSTANCE.get().read_impl(registry, phase, tag, object); + } + + public static void store(@Nonnull Registry registry, @Nonnull Set phase, @Nonnull NBTTagCompound tag, @Nonnull T object) + throws IllegalAccessException, InstantiationException, NoHandlerFoundException { + INSTANCE.get().store_impl(registry, phase, tag, object); + } + + public void read_impl(@Nonnull Registry registry, @Nonnull Set phase, @Nonnull NBTTagCompound tag, @Nonnull T object) + throws IllegalAccessException, InstantiationException, NoHandlerFoundException { + Class clazz = object.getClass(); + if (!fieldCache.containsKey(clazz)) { + cacheHandlers(registry, clazz); + } + + for (Field field : fieldCache.get(clazz)) { + if (!Collections.disjoint(phaseCache.get(field), phase)) { + Object fieldData = field.get(object); + String fieldName = field.getName(); + if (!tag.hasKey(fieldName + NULL_POSTFIX) && fieldName != null) { + for (IHandler handler : fieldHandlerCache.get(field)) { + Object result = handler.read(registry, phase, tag, fieldName, fieldData); + if (result != null) { + field.set(object, result); + break; + } + } + } else { + field.set(object, null); + } + } + } + + Class superclazz = superclassCache.get(clazz); + if (superclazz != null) { + for (IHandler handler : superclassHandlerCache.get(superclazz)) { + if (handler.read(registry, phase, tag, SUPERCLASS_KEY, object) != null) { + break; + } + } + } + + } + + public void store_impl(@Nonnull Registry registry, @Nonnull Set phase, @Nonnull NBTTagCompound tag, @Nonnull T object) + throws IllegalAccessException, InstantiationException, NoHandlerFoundException { + Class clazz = object.getClass(); + if (!fieldCache.containsKey(clazz)) { + cacheHandlers(registry, clazz); + } + + for (Field field : fieldCache.get(clazz)) { + if (!Collections.disjoint(phaseCache.get(field), phase)) { + Object fieldData = field.get(object); + String fieldName = field.getName(); + if (fieldData != null && fieldName != null) { + for (IHandler handler : fieldHandlerCache.get(field)) { + if (handler.store(registry, phase, tag, fieldName, fieldData)) { + break; + } + } + } else { + tag.setBoolean(fieldName + NULL_POSTFIX, true); + } + } + } + + Class superclazz = superclassCache.get(clazz); + if (superclazz != null) { + for (IHandler handler : superclassHandlerCache.get(superclazz)) { + if (handler.store(registry, phase, tag, SUPERCLASS_KEY, object)) { + break; + } + } + } + } + + private void cacheHandlers(@Nonnull Registry registry, Class clazz) throws IllegalAccessException, InstantiationException, NoHandlerFoundException { + final ArrayList fieldList = new ArrayList(); + for (Field field : clazz.getDeclaredFields()) { + Store annotation = field.getAnnotation(Store.class); + if (annotation != null) { + ArrayList handlerList = new ArrayList(); + String fieldName = field.getName(); + if (fieldName != null) { + Class fieldType = field.getType(); + if (annotation.handler() != NullHandler.class) { + handlerList.add(annotation.handler().newInstance()); + } + handlerList.addAll(registry.findHandlers(fieldType)); + if (handlerList.isEmpty()) { + throw new NoHandlerFoundException(field, clazz); + } + EnumSet enumSet = EnumSet.noneOf(StoreFor.class); + enumSet.addAll(Arrays.asList(annotation.value())); + phaseCache.put(field, enumSet); + field.setAccessible(true); + fieldList.add(field); + fieldHandlerCache.put(field, handlerList); + } + } + } + + Class superclazz = clazz.getSuperclass(); + if (superclazz != null) { + Storable annotation = superclazz.getAnnotation(Storable.class); + if (annotation != null) { + if (annotation.handler() == HandleStorable.class) { + cacheHandlers(registry, superclazz); + fieldList.addAll(fieldCache.get(superclazz)); + } else { + superclassCache.put(clazz, superclazz); + if (!superclassCache.containsKey(superclazz)) { + superclassHandlerCache.put(superclazz, (List) Arrays.asList(annotation.handler().newInstance())); + } + } + } else { + List handlers = registry.findHandlers(superclazz); + if (!handlers.isEmpty()) { + superclassCache.put(clazz, superclazz); + if (!superclassCache.containsKey(superclazz)) { + superclassHandlerCache.put(superclazz, handlers); + } + } + } + } + + fieldCache.put(clazz, fieldList); + } + +} diff --git a/src/main/java/com/enderio/core/common/autosave/exceptions/NoHandlerFoundException.java b/src/main/java/com/enderio/core/common/autosave/exceptions/NoHandlerFoundException.java new file mode 100755 index 00000000..3fea27e8 --- /dev/null +++ b/src/main/java/com/enderio/core/common/autosave/exceptions/NoHandlerFoundException.java @@ -0,0 +1,11 @@ +package com.enderio.core.common.autosave.exceptions; + +import java.lang.reflect.Field; + +public class NoHandlerFoundException extends Exception { + + public NoHandlerFoundException(Field field, Object o) { + super("No storage handler found for field " + field.getName() + " of type " + field.getType() + " of " + o); + } + +} diff --git a/src/main/java/com/enderio/core/common/autosave/handlers/IHandler.java b/src/main/java/com/enderio/core/common/autosave/handlers/IHandler.java new file mode 100755 index 00000000..07b36a16 --- /dev/null +++ b/src/main/java/com/enderio/core/common/autosave/handlers/IHandler.java @@ -0,0 +1,97 @@ +package com.enderio.core.common.autosave.handlers; + +import java.util.Set; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +import com.enderio.core.common.autosave.Registry; +import com.enderio.core.common.autosave.annotations.Store; +import com.enderio.core.common.autosave.exceptions.NoHandlerFoundException; + +import net.minecraft.nbt.NBTTagCompound; + +/** + * IHandlers convert objects into NBT and vice versa. + * + *

+ * IHandlers can be created for a specific class/interface and registered in the + * {@link Registry}, they can be annotated to classes to overwrite the + * registered class handler, or they can be annotated on specific fields to be + * used for only that field. + * + * @param + * An optional generic to have Java do the class casting of the + * 'object' parameter. + */ +public interface IHandler { + + /** + * Checks if the handler can handle the given class. This is ony used for + * handlers that are registered with the {@link Registry}. + * + * @param clazz + * A class that wants to be handled + * @return true if the handler can handle the class + */ + boolean canHandle(Class clazz); + + /** + * Stores an object in a NBT structure. + * + * @param registry + * The handler registry to use + * @param phase + * The phase to work in. Any sub-elements that are not for this phase + * should be ignored + * @param nbt + * The NBT to store the data + * @param name + * The name of the tag in the nbt where the data should be stored. + * Please note that this will be overwritten be the final handler if + * you are returning false. Use name + "__" as a prefix in this case + * @param object + * The object to store + * @return True if the object was completely handled. False otherwise. In that + * case, the next matching handler will be called. Please note that it + * is ok to partially handle a object and return false. This + * can be used to handle inheritance + * @throws IllegalAccessException + * @throws IllegalArgumentException + * @throws InstantiationException + * @throws NoHandlerFoundException + */ + boolean store(@Nonnull Registry registry, @Nonnull Set phase, @Nonnull NBTTagCompound nbt, @Nonnull String name, @Nonnull T object) + throws IllegalArgumentException, IllegalAccessException, InstantiationException, NoHandlerFoundException; + + /** + * Reads an object from a NBT structure + * + * @param registry + * The handler registry to use + * @param phase + * The phase to work in. Any sub-elements that are not for this phase + * should be ignored + * @param nbt + * The NBT to read the data from + * @param name + * The name of the tag in the nbt where the data should be read from. + * This tag may not exist! + * @param object + * The existing object into which the data should be read, or which + * should be replaced by a new object with the read data. The + * decision which of those two possibilities to execute is open to + * the implementation. This may be null! + * @return The object that should be placed into the field. This may be the + * changed parameter object or a new one. If this returns null, the + * next matching handler will be given a chance to execute. Please + * note that it is ok to change the input object in this case. + * This can be used to implement inheritance. + * @throws IllegalAccessException + * @throws IllegalArgumentException + * @throws InstantiationException + * @throws NoHandlerFoundException + */ + T read(@Nonnull Registry registry, @Nonnull Set phase, @Nonnull NBTTagCompound nbt, @Nonnull String name, @Nullable T object) + throws IllegalArgumentException, IllegalAccessException, InstantiationException, NoHandlerFoundException; +} diff --git a/src/main/java/com/enderio/core/common/autosave/handlers/endercore/HandleBlockCoord.java b/src/main/java/com/enderio/core/common/autosave/handlers/endercore/HandleBlockCoord.java new file mode 100755 index 00000000..4afb83e4 --- /dev/null +++ b/src/main/java/com/enderio/core/common/autosave/handlers/endercore/HandleBlockCoord.java @@ -0,0 +1,43 @@ +package com.enderio.core.common.autosave.handlers.endercore; + +import java.util.Set; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +import net.minecraft.nbt.NBTTagCompound; + +import com.enderio.core.common.autosave.Registry; +import com.enderio.core.common.autosave.annotations.Store.StoreFor; +import com.enderio.core.common.autosave.exceptions.NoHandlerFoundException; +import com.enderio.core.common.autosave.handlers.IHandler; +import com.enderio.core.common.util.BlockCoord; + +public class HandleBlockCoord implements IHandler { + + public HandleBlockCoord() { + } + + @Override + public boolean canHandle(Class clazz) { + return BlockCoord.class.isAssignableFrom(clazz); + } + + @Override + public boolean store(@Nonnull Registry registry, @Nonnull Set phase, @Nonnull NBTTagCompound nbt, @Nonnull String name, @Nonnull BlockCoord object) + throws IllegalArgumentException, IllegalAccessException, InstantiationException, NoHandlerFoundException { + nbt.setIntArray(name, new int[] { object.x, object.y, object.z }); + return true; + } + + @Override + public BlockCoord read(@Nonnull Registry registry, @Nonnull Set phase, @Nonnull NBTTagCompound nbt, @Nonnull String name, + @Nullable BlockCoord object) throws IllegalArgumentException, IllegalAccessException, InstantiationException, NoHandlerFoundException { + if (nbt.hasKey(name)) { + int[] intArray = nbt.getIntArray(name); + return new BlockCoord(intArray[0], intArray[1], intArray[2]); + } + return object; + } + +} diff --git a/src/main/java/com/enderio/core/common/autosave/handlers/forge/HandleFluid.java b/src/main/java/com/enderio/core/common/autosave/handlers/forge/HandleFluid.java new file mode 100755 index 00000000..595618ba --- /dev/null +++ b/src/main/java/com/enderio/core/common/autosave/handlers/forge/HandleFluid.java @@ -0,0 +1,40 @@ +package com.enderio.core.common.autosave.handlers.forge; + +import java.util.Set; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +import com.enderio.core.common.autosave.Registry; +import com.enderio.core.common.autosave.annotations.Store.StoreFor; +import com.enderio.core.common.autosave.exceptions.NoHandlerFoundException; +import com.enderio.core.common.autosave.handlers.IHandler; + +import net.minecraft.nbt.NBTTagCompound; +import net.minecraftforge.fluids.Fluid; +import net.minecraftforge.fluids.FluidRegistry; + +public class HandleFluid implements IHandler { + + public HandleFluid() { + } + + @Override + public boolean canHandle(Class clazz) { + return Fluid.class.isAssignableFrom(clazz); + } + + @Override + public boolean store(@Nonnull Registry registry, @Nonnull Set phase, @Nonnull NBTTagCompound nbt, @Nonnull String name, @Nonnull Fluid object) + throws IllegalArgumentException, IllegalAccessException, InstantiationException, NoHandlerFoundException { + nbt.setString(name, FluidRegistry.getFluidName(object)); + return true; + } + + @Override + public Fluid read(@Nonnull Registry registry, @Nonnull Set phase, @Nonnull NBTTagCompound nbt, @Nonnull String name, @Nullable Fluid object) + throws IllegalArgumentException, IllegalAccessException, InstantiationException, NoHandlerFoundException { + return nbt.hasKey(name) ? FluidRegistry.getFluid(nbt.getString(name)) : object; + } + +} diff --git a/src/main/java/com/enderio/core/common/autosave/handlers/forge/HandleFluidStack.java b/src/main/java/com/enderio/core/common/autosave/handlers/forge/HandleFluidStack.java new file mode 100755 index 00000000..fe08bcc1 --- /dev/null +++ b/src/main/java/com/enderio/core/common/autosave/handlers/forge/HandleFluidStack.java @@ -0,0 +1,44 @@ +package com.enderio.core.common.autosave.handlers.forge; + +import java.util.Set; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +import com.enderio.core.common.autosave.Registry; +import com.enderio.core.common.autosave.annotations.Store.StoreFor; +import com.enderio.core.common.autosave.exceptions.NoHandlerFoundException; +import com.enderio.core.common.autosave.handlers.IHandler; + +import net.minecraft.nbt.NBTTagCompound; +import net.minecraftforge.fluids.FluidStack; + +public class HandleFluidStack implements IHandler { + + public HandleFluidStack() { + } + + @Override + public boolean canHandle(Class clazz) { + return FluidStack.class.isAssignableFrom(clazz); + } + + @Override + public boolean store(@Nonnull Registry registry, @Nonnull Set phase, @Nonnull NBTTagCompound nbt, @Nonnull String name, @Nonnull FluidStack object) + throws IllegalArgumentException, IllegalAccessException, InstantiationException, NoHandlerFoundException { + NBTTagCompound tag = new NBTTagCompound(); + object.writeToNBT(tag); + nbt.setTag(name, tag); + return false; + } + + @Override + public FluidStack read(@Nonnull Registry registry, @Nonnull Set phase, @Nonnull NBTTagCompound nbt, @Nonnull String name, + @Nullable FluidStack object) throws IllegalArgumentException, IllegalAccessException, InstantiationException, NoHandlerFoundException { + if (nbt.hasKey(name)) { + return FluidStack.loadFluidStackFromNBT(nbt.getCompoundTag(name)); + } + return null; + } + +} diff --git a/src/main/java/com/enderio/core/common/autosave/handlers/internal/HandleStorable.java b/src/main/java/com/enderio/core/common/autosave/handlers/internal/HandleStorable.java new file mode 100755 index 00000000..cb5f4ef7 --- /dev/null +++ b/src/main/java/com/enderio/core/common/autosave/handlers/internal/HandleStorable.java @@ -0,0 +1,65 @@ +package com.enderio.core.common.autosave.handlers.internal; + +import java.util.Set; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +import net.minecraft.nbt.NBTTagCompound; + +import com.enderio.core.common.autosave.Registry; +import com.enderio.core.common.autosave.annotations.Storable; +import com.enderio.core.common.autosave.annotations.Store; +import com.enderio.core.common.autosave.annotations.Store.StoreFor; +import com.enderio.core.common.autosave.engine.StorableEngine; +import com.enderio.core.common.autosave.exceptions.NoHandlerFoundException; +import com.enderio.core.common.autosave.handlers.IHandler; +import com.enderio.core.common.util.NullHelper; + +/** + * An {@link IHandler} that can (re-)store objects by storing their fields. The + * fields to (re-)store must be annotated {@link Store}. + *

+ * It will also process the annotated fields of superclasses, as long as there + * is an unbroken chain of {@link Storable} annotations (without special + * handlers). Fields that have the same name as a field in a sub-/super-class + * will be processed independently. + *

+ * If the final superclass has an {@link IHandler} registered in the + * {@link Registry}, it will also be processed. However, this will not + * work for handlers that return a new object instead of changing the given one. + * A handler can check for this case by seeing if its "name" parameter is + * {@link StorableEngine#SUPERCLASS_KEY}. + * + * @param + */ +public class HandleStorable implements IHandler { + + public HandleStorable() { + } + + @Override + public boolean canHandle(Class clazz) { + Storable annotation = clazz.getAnnotation(com.enderio.core.common.autosave.annotations.Storable.class); + return annotation != null && annotation.handler() == this.getClass(); + } + + @Override + public boolean store(@Nonnull Registry registry, @Nonnull Set phase, @Nonnull NBTTagCompound nbt, @Nonnull String name, @Nonnull T object) + throws IllegalArgumentException, IllegalAccessException, InstantiationException, NoHandlerFoundException { + NBTTagCompound tag = new NBTTagCompound(); + StorableEngine.store(registry, phase, tag, object); + nbt.setTag(name, tag); + return true; + } + + @Override + public T read(@Nonnull Registry registry, @Nonnull Set phase, @Nonnull NBTTagCompound nbt, @Nonnull String name, @Nullable T object) + throws IllegalArgumentException, IllegalAccessException, InstantiationException, NoHandlerFoundException { + if (nbt.hasKey(name) && object != null) { + NBTTagCompound tag = NullHelper.notnullM(nbt.getCompoundTag(name), "NBTTagCompound.getCompoundTag()"); + StorableEngine.read(registry, phase, tag, object); + } + return object; + } +} diff --git a/src/main/java/com/enderio/core/common/autosave/handlers/internal/NullHandler.java b/src/main/java/com/enderio/core/common/autosave/handlers/internal/NullHandler.java new file mode 100755 index 00000000..11a5cf41 --- /dev/null +++ b/src/main/java/com/enderio/core/common/autosave/handlers/internal/NullHandler.java @@ -0,0 +1,47 @@ +package com.enderio.core.common.autosave.handlers.internal; + +import java.util.Set; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +import com.enderio.core.common.autosave.Registry; +import com.enderio.core.common.autosave.annotations.Store; +import com.enderio.core.common.autosave.exceptions.NoHandlerFoundException; +import com.enderio.core.common.autosave.handlers.IHandler; + +import net.minecraft.nbt.NBTTagCompound; + +/** + * A dummy {@link IHandler} that is used as default value for fields. It will be + * ignored and the fields' handlers will be looked up in the {@link Registry} + * instead. + *

+ * This is needed because Java annotations do not allow "null" as a default + * value for class parameters. + *

+ * Do not add this handler to an annotation. + */ +public class NullHandler implements IHandler { + + private NullHandler() { + } + + @Override + public boolean canHandle(Class clazz) { + return false; + } + + @Override + public boolean store(@Nonnull Registry registry, @Nonnull Set phase, @Nonnull NBTTagCompound nbt, @Nonnull String name, + @Nonnull NullHandler object) throws IllegalArgumentException, IllegalAccessException, InstantiationException, NoHandlerFoundException { + return false; + } + + @Override + public NullHandler read(@Nonnull Registry registry, @Nonnull Set phase, @Nonnull NBTTagCompound nbt, @Nonnull String name, + @Nullable NullHandler object) throws IllegalArgumentException, IllegalAccessException, InstantiationException, NoHandlerFoundException { + return null; + } + +} diff --git a/src/main/java/com/enderio/core/common/autosave/handlers/java/HandleBoolean.java b/src/main/java/com/enderio/core/common/autosave/handlers/java/HandleBoolean.java new file mode 100755 index 00000000..203b16d5 --- /dev/null +++ b/src/main/java/com/enderio/core/common/autosave/handlers/java/HandleBoolean.java @@ -0,0 +1,37 @@ +package com.enderio.core.common.autosave.handlers.java; + +import java.util.Set; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +import com.enderio.core.common.autosave.Registry; +import com.enderio.core.common.autosave.annotations.Store; +import com.enderio.core.common.autosave.handlers.IHandler; + +import net.minecraft.nbt.NBTTagCompound; + +public class HandleBoolean implements IHandler { + + public HandleBoolean() { + } + + @Override + public boolean canHandle(Class clazz) { + return Boolean.class.isAssignableFrom(clazz) || boolean.class.isAssignableFrom(clazz); + } + + @Override + public boolean store(@Nonnull Registry registry, @Nonnull Set phase, @Nonnull NBTTagCompound nbt, @Nonnull String name, + @Nonnull Boolean object) throws IllegalArgumentException, IllegalAccessException { + nbt.setBoolean(name, object); + return true; + } + + @Override + public Boolean read(@Nonnull Registry registry, @Nonnull Set phase, @Nonnull NBTTagCompound nbt, @Nonnull String name, + @Nullable Boolean object) { + return nbt.hasKey(name) ? nbt.getBoolean(name) : object != null ? object : false; + } + +} diff --git a/src/main/java/com/enderio/core/common/autosave/handlers/java/HandleEnum.java b/src/main/java/com/enderio/core/common/autosave/handlers/java/HandleEnum.java new file mode 100755 index 00000000..ba262c99 --- /dev/null +++ b/src/main/java/com/enderio/core/common/autosave/handlers/java/HandleEnum.java @@ -0,0 +1,41 @@ +package com.enderio.core.common.autosave.handlers.java; + +import java.util.Set; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +import com.enderio.core.common.autosave.Registry; +import com.enderio.core.common.autosave.annotations.Store; +import com.enderio.core.common.autosave.handlers.IHandler; + +import net.minecraft.nbt.NBTTagCompound; + +public class HandleEnum implements IHandler { + + public HandleEnum() { + } + + @Override + public boolean canHandle(Class clazz) { + return Enum.class.isAssignableFrom(clazz); + } + + @Override + public boolean store(@Nonnull Registry registry, @Nonnull Set phase, @Nonnull NBTTagCompound nbt, @Nonnull String name, + @Nonnull Enum object) + throws IllegalArgumentException, IllegalAccessException { + nbt.setInteger(name, object.ordinal()); + return true; + } + + @Override + public Enum read(@Nonnull Registry registry, @Nonnull Set phase, @Nonnull NBTTagCompound nbt, @Nonnull String name, @Nullable Enum object) { + if (nbt.hasKey(name) && object != null) { + return object.getClass().getEnumConstants()[nbt.getInteger(name)]; + } else { + return object; + } + } + +} diff --git a/src/main/java/com/enderio/core/common/autosave/handlers/java/HandleFloat.java b/src/main/java/com/enderio/core/common/autosave/handlers/java/HandleFloat.java new file mode 100755 index 00000000..d628dbeb --- /dev/null +++ b/src/main/java/com/enderio/core/common/autosave/handlers/java/HandleFloat.java @@ -0,0 +1,36 @@ +package com.enderio.core.common.autosave.handlers.java; + +import java.util.Set; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +import com.enderio.core.common.autosave.Registry; +import com.enderio.core.common.autosave.annotations.Store; +import com.enderio.core.common.autosave.handlers.IHandler; + +import net.minecraft.nbt.NBTTagCompound; + +public class HandleFloat implements IHandler { + + public HandleFloat() { + } + + @Override + public boolean canHandle(Class clazz) { + return Float.class.isAssignableFrom(clazz) || float.class.isAssignableFrom(clazz); + } + + @Override + public boolean store(@Nonnull Registry registry, @Nonnull Set phase, @Nonnull NBTTagCompound nbt, @Nonnull String name, @Nonnull Float object) + throws IllegalArgumentException, IllegalAccessException { + nbt.setFloat(name, object); + return true; + } + + @Override + public Float read(@Nonnull Registry registry, @Nonnull Set phase, @Nonnull NBTTagCompound nbt, @Nonnull String name, @Nullable Float object) { + return nbt.hasKey(name) ? nbt.getFloat(name) : object != null ? object : 0f; + } + +} diff --git a/src/main/java/com/enderio/core/common/autosave/handlers/java/HandleFloatArray.java b/src/main/java/com/enderio/core/common/autosave/handlers/java/HandleFloatArray.java new file mode 100755 index 00000000..486307ea --- /dev/null +++ b/src/main/java/com/enderio/core/common/autosave/handlers/java/HandleFloatArray.java @@ -0,0 +1,58 @@ +package com.enderio.core.common.autosave.handlers.java; + +import java.util.Set; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +import com.enderio.core.common.autosave.Registry; +import com.enderio.core.common.autosave.annotations.Store; +import com.enderio.core.common.autosave.handlers.IHandler; + +import net.minecraft.nbt.NBTTagCompound; + +public class HandleFloatArray implements IHandler { + + public HandleFloatArray() { + } + + @Override + public boolean canHandle(Class clazz) { + return float[].class.isAssignableFrom(clazz); + } + + @Override + public boolean store(@Nonnull Registry registry, @Nonnull Set phase, @Nonnull NBTTagCompound nbt, @Nonnull String name, + @Nonnull float[] object) throws IllegalArgumentException, IllegalAccessException { + int len = 0; + for (int i = object.length; i > 0; i--) { + if (object[i - 1] != 0) { + len = i; + break; + } + } + int[] tmp = new int[len]; + for (int i = 0; i < len; i++) { + tmp[i] = Float.floatToIntBits(object[i]); + } + nbt.setIntArray(name, tmp); + return true; + } + + @Override + public float[] read(@Nonnull Registry registry, @Nonnull Set phase, @Nonnull NBTTagCompound nbt, @Nonnull String name, + @Nullable float[] object) { + if (nbt.hasKey(name) && object != null) { + int[] tmp = nbt.getIntArray(name); + for (int i = 0; i < object.length; i++) { + if (i < tmp.length) { + object[i] = Float.intBitsToFloat(tmp[i]); + } else { + object[i] = 0; + } + } + } + return object; + } + +} diff --git a/src/main/java/com/enderio/core/common/autosave/handlers/java/HandleInteger.java b/src/main/java/com/enderio/core/common/autosave/handlers/java/HandleInteger.java new file mode 100755 index 00000000..bb170888 --- /dev/null +++ b/src/main/java/com/enderio/core/common/autosave/handlers/java/HandleInteger.java @@ -0,0 +1,37 @@ +package com.enderio.core.common.autosave.handlers.java; + +import java.util.Set; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +import com.enderio.core.common.autosave.Registry; +import com.enderio.core.common.autosave.annotations.Store; +import com.enderio.core.common.autosave.handlers.IHandler; + +import net.minecraft.nbt.NBTTagCompound; + +public class HandleInteger implements IHandler { + + public HandleInteger() { + } + + @Override + public boolean canHandle(Class clazz) { + return Integer.class.isAssignableFrom(clazz) || int.class.isAssignableFrom(clazz); + } + + @Override + public boolean store(@Nonnull Registry registry, @Nonnull Set phase, @Nonnull NBTTagCompound nbt, @Nonnull String name, + @Nonnull Integer object) throws IllegalArgumentException, IllegalAccessException { + nbt.setInteger(name, object); + return true; + } + + @Override + public Integer read(@Nonnull Registry registry, @Nonnull Set phase, @Nonnull NBTTagCompound nbt, @Nonnull String name, + @Nullable Integer object) { + return nbt.hasKey(name) ? nbt.getInteger(name) : object != null ? object : 0; + } + +} diff --git a/src/main/java/com/enderio/core/common/autosave/handlers/minecraft/HandleBlockPos.java b/src/main/java/com/enderio/core/common/autosave/handlers/minecraft/HandleBlockPos.java new file mode 100755 index 00000000..b74ab4eb --- /dev/null +++ b/src/main/java/com/enderio/core/common/autosave/handlers/minecraft/HandleBlockPos.java @@ -0,0 +1,42 @@ +package com.enderio.core.common.autosave.handlers.minecraft; + +import java.util.Set; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +import com.enderio.core.common.autosave.Registry; +import com.enderio.core.common.autosave.annotations.Store.StoreFor; +import com.enderio.core.common.autosave.exceptions.NoHandlerFoundException; +import com.enderio.core.common.autosave.handlers.IHandler; + +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.util.BlockPos; + +public class HandleBlockPos implements IHandler { + + public HandleBlockPos() { + } + + @Override + public boolean canHandle(Class clazz) { + return BlockPos.class.isAssignableFrom(clazz); + } + + @Override + public boolean store(@Nonnull Registry registry, @Nonnull Set phase, @Nonnull NBTTagCompound nbt, @Nonnull String name, @Nonnull BlockPos object) + throws IllegalArgumentException, IllegalAccessException, InstantiationException, NoHandlerFoundException { + nbt.setLong(name, object.toLong()); + return true; + } + + @Override + public BlockPos read(@Nonnull Registry registry, @Nonnull Set phase, @Nonnull NBTTagCompound nbt, @Nonnull String name, @Nullable BlockPos object) + throws IllegalArgumentException, IllegalAccessException, InstantiationException, NoHandlerFoundException { + if (nbt.hasKey(name)) { + return BlockPos.fromLong(nbt.getLong(name)); + } + return object; + } + +} diff --git a/src/main/java/com/enderio/core/common/autosave/handlers/minecraft/HandleItem.java b/src/main/java/com/enderio/core/common/autosave/handlers/minecraft/HandleItem.java new file mode 100755 index 00000000..a86cff4b --- /dev/null +++ b/src/main/java/com/enderio/core/common/autosave/handlers/minecraft/HandleItem.java @@ -0,0 +1,45 @@ +package com.enderio.core.common.autosave.handlers.minecraft; + +import java.util.Set; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +import com.enderio.core.common.autosave.Registry; +import com.enderio.core.common.autosave.annotations.Store.StoreFor; +import com.enderio.core.common.autosave.exceptions.NoHandlerFoundException; +import com.enderio.core.common.autosave.handlers.IHandler; + +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NBTTagCompound; + +public class HandleItem implements IHandler { + + public HandleItem() { + } + + @Override + public boolean canHandle(Class clazz) { + return ItemStack.class.isAssignableFrom(clazz); + } + + // Note: We can use the item ID here because ItemStack also uses it in its NBT-methods. So if the ID breaks, ItemStacks break even harder than we do here. + + @Override + public boolean store(@Nonnull Registry registry, @Nonnull Set phase, @Nonnull NBTTagCompound nbt, @Nonnull String name, @Nonnull Item object) + throws IllegalArgumentException, IllegalAccessException, InstantiationException, NoHandlerFoundException { + nbt.setShort(name, (short) Item.getIdFromItem(object)); + return true; + } + + @Override + public Item read(@Nonnull Registry registry, @Nonnull Set phase, @Nonnull NBTTagCompound nbt, @Nonnull String name, @Nullable Item object) + throws IllegalArgumentException, IllegalAccessException, InstantiationException, NoHandlerFoundException { + if (nbt.hasKey(name)) { + return Item.getItemById(nbt.getShort(name)); + } + return object; + } + +} diff --git a/src/main/java/com/enderio/core/common/autosave/handlers/minecraft/HandleItemStack.java b/src/main/java/com/enderio/core/common/autosave/handlers/minecraft/HandleItemStack.java new file mode 100755 index 00000000..fdd783d9 --- /dev/null +++ b/src/main/java/com/enderio/core/common/autosave/handlers/minecraft/HandleItemStack.java @@ -0,0 +1,49 @@ +package com.enderio.core.common.autosave.handlers.minecraft; + +import java.util.Set; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +import com.enderio.core.common.autosave.Registry; +import com.enderio.core.common.autosave.annotations.Store.StoreFor; +import com.enderio.core.common.autosave.exceptions.NoHandlerFoundException; +import com.enderio.core.common.autosave.handlers.IHandler; + +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NBTTagCompound; + +public class HandleItemStack implements IHandler { + + public HandleItemStack() { + } + + @Override + public boolean canHandle(Class clazz) { + return ItemStack.class.isAssignableFrom(clazz); + } + + @Override + public boolean store(@Nonnull Registry registry, @Nonnull Set phase, @Nonnull NBTTagCompound nbt, @Nonnull String name, @Nonnull ItemStack object) + throws IllegalArgumentException, IllegalAccessException, InstantiationException, NoHandlerFoundException { + NBTTagCompound tag = new NBTTagCompound(); + object.writeToNBT(tag); + nbt.setTag(name, tag); + return true; + } + + @Override + public ItemStack read(@Nonnull Registry registry, @Nonnull Set phase, @Nonnull NBTTagCompound nbt, @Nonnull String name, @Nullable ItemStack object) + throws IllegalArgumentException, IllegalAccessException, InstantiationException, NoHandlerFoundException { + if (nbt.hasKey(name)) { + NBTTagCompound tag = nbt.getCompoundTag(name); + if (object != null) { + object.readFromNBT(tag); + } else { + return ItemStack.loadItemStackFromNBT(tag); + } + } + return object; + } + +} diff --git a/src/main/java/com/enderio/core/common/util/NullHelper.java b/src/main/java/com/enderio/core/common/util/NullHelper.java new file mode 100755 index 00000000..fd05df16 --- /dev/null +++ b/src/main/java/com/enderio/core/common/util/NullHelper.java @@ -0,0 +1,48 @@ +package com.enderio.core.common.util; + +import javax.annotation.Nonnull; + +public class NullHelper { + + private NullHelper() { + } + + @Nonnull + public static

P notnull(P o, @Nonnull String message) { + if (o == null) { + throw new NullPointerException("Houston we have a problem: '" + message + "'. " + + "Please report that on our bugtracker unless you are using some old version. Thank you."); + } + return o; + } + + @Nonnull + public static

P notnullJ(P o, @Nonnull String message) { + if (o == null) { + throw new NullPointerException("There was a problem with Java: The call '" + message + + "' returned null even though it should not be able to do that. Is you Java broken? " + + "Are you using a version that is much newer than the one Ender IO was developed with?"); + } + return o; + } + + @Nonnull + public static

P notnullM(P o, @Nonnull String message) { + if (o == null) { + throw new NullPointerException("There was a problem with Minecraft: The call '" + message + + "' returned null even though it should not be able to do that. Is you Minecraft broken? Did some other mod brake it?"); + } + return o; + } + + @Nonnull + public static

P notnullF(P o, @Nonnull String message) { + if (o == null) { + throw new NullPointerException("There was a problem with Forge: The call '" + message + + "' returned null even though it should not be able to do that. Is you Forge broken? Did some other mod brake it? " + + "Are you using a version that is much newer than the one Ender IO Addons was developed with?"); + } + return o; + } + +}