That's one heck of a commit you've got there...
I may have got a bit behind with version control. A lot behind, in fact. Maybe I'll go back and split this sometime - then again, I probably won't. But hey, at least it's here!
This commit is contained in:
@@ -0,0 +1,118 @@
|
||||
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 spellData of any type to be stored in {@link WizardData} at
|
||||
* runtime. This means spells (or anything else, for that matter) may define their own storedVariables to be stored
|
||||
* with the player and handle those storedVariables 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}.
|
||||
* <p></p>
|
||||
* 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.
|
||||
* <p></p>
|
||||
* @param <T> The type of variable stored.
|
||||
*/
|
||||
public interface IVariable<T> {
|
||||
|
||||
// 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.
|
||||
* <p></p>
|
||||
* @param <T> The type of variable stored.
|
||||
*/
|
||||
class Variable<T> implements IVariable<T> {
|
||||
|
||||
private final Persistence persistence;
|
||||
|
||||
private BiFunction<EntityPlayer, T, T> ticker;
|
||||
|
||||
public Variable(Persistence persistence){
|
||||
this.persistence = persistence;
|
||||
this.ticker = (p, t) -> t;
|
||||
}
|
||||
|
||||
/**
|
||||
* Replaces this variable's update method with the given update function. <i>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.</i>
|
||||
* @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<T> withTicker(BiFunction<EntityPlayer, T, T> 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
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user