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:
+ *
+ * - The annotated special handler of the given class
+ *
- The annotated special handler(s) of its superclass(es)
+ *
- The registered handlers from this registry
+ *
- The registered handlers from this registry's super-registries
+ *
- {@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:
+ *
+ *
+ * - value: A class implementing {@link IHandler} to use for this class
+ * instead of {@link HandleStorable}.
+ *
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.TYPE)
+public @interface Storable {
+
+ public Class extends IHandler> 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:
+ *
+ *
+ * - value: An array of {@link StoreFor} keys to designate for which targets
+ * the data should be stored.
+ *
- handler: A class implementing {@link IHandler} to use for this field
+ * instead of the registered handler.
+ *
+ *
+ */
+@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.
+ *
+ * - {@link #SAVE}
+ * - {@link #CLIENT}
+ * - {@link #ITEM}
+ *
+ *
+ */
+ 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 extends IHandler> 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 extends Object> 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 extends Object> 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;
+ }
+
+}