package electroblob.wizardry.data; import io.netty.buffer.ByteBuf; import net.minecraft.entity.player.EntityPlayer; import java.util.function.BiFunction; /** * Instances of this interface act as keys which allow data of any type to be stored in {@link WizardData} at * runtime. This means spells (or anything else, for that matter) may define their own variables to be stored * with the player and handle those variables themselves. This prevents {@code WizardData} from being cluttered * with spell-specific fields and allows addon mods to leverage {@code WizardData} for their own spells or other data, * rather than defining their own capability. This system is somewhat similar to {@code DataManager}. *

* Instances should be created once and stored statically (or pseudo-statically) in some sensible location, such * as a spell class. They can then be used as keys to access the values themselves via {@link WizardData}. * Encapsulation can also be achieved by simply restricting access to the keys. *

* @param The type of variable stored. */ public interface IVariable { // To reiterate, instances of this interface are KEYS. They are both accessors for the data and define how // it is stored and handled, but they DO NOT CONTAIN THE ACTUAL DATA. // Only one instance exists for each thing to be stored, and is shared across instances of WizardData. /** Convenience method that allows this variable to define tick behaviour. This is particularly useful for * trivial operations such as decrementing a value, for which a dedicated event handling method would be * unnecessarily verbose. */ T update(EntityPlayer player, T value); /** * Returns whether this variable persists when data is copied. * @param respawn True if the player died and is respawning, false if they are just travelling between dimensions. * @return True if the variable should be copied over, false if not. */ boolean isPersistent(boolean respawn); /** * Returns whether this variable requires syncing with clients. * @return True if the variable should be synced with clients, false if not. */ boolean isSynced(); /** * Writes this variable's value to the given {@link ByteBuf}. */ void write(ByteBuf buf, T value); /** * Reads this variable's value from the given {@link ByteBuf}. */ T read(ByteBuf buf); /** If you're storing a lot of data, you can optionally implement this method to define a condition which, if * satisfied, will result in the data being removed from storage, reducing unnecessary syncing and saving. This * is particularly relevant if the value is synced as it reduces packet size. */ default boolean canPurge(EntityPlayer player, T value){ return false; } /** * General-purpose implementation of {@link IVariable} for non-stored variables. These may still, however, persist * across player respawn/dimension change. *

* @param The type of variable stored. */ class Variable implements IVariable { private final Persistence persistence; private BiFunction ticker; public Variable(Persistence persistence){ this.persistence = persistence; this.ticker = (p, t) -> t; } /** * Replaces this variable's update method with the given update function. Beware of auto-unboxing of * primitive types! For lambda expressions, check the second parameter isn't null before operating on it. * For method references, do not reference a method that takes a primitive type. Otherwise, this will cause * a (difficult to debug) {@link NullPointerException} if the key was not stored. * @param ticker A {@link BiFunction} specifying the actions to be performed on this variable each tick. The * {@code BiFunction} returns the new value for this variable. * @return This {@code Variable} object, allowing this method to be chained onto object creation. */ public Variable withTicker(BiFunction ticker){ this.ticker = ticker; return this; } @Override public T update(EntityPlayer player, T value){ return ticker.apply(player, value); } @Override public boolean isPersistent(boolean respawn){ return respawn ? persistence.persistsOnRespawn() : persistence.persistsOnDimensionChange(); } @Override public boolean isSynced(){ return false;// Not implemented for now, maybe we will one day } @Override public void write(ByteBuf buf, T value){ // NYI } @Override public T read(ByteBuf buf){ return null; // NYI } } }