Skip to content

Instantly share code, notes, and snippets.

@Lanse505
Created August 12, 2022 19:01
Show Gist options
  • Save Lanse505/eeadaf00a23e34e50df92ef749e47a5f to your computer and use it in GitHub Desktop.
Save Lanse505/eeadaf00a23e34e50df92ef749e47a5f to your computer and use it in GitHub Desktop.
package matteroverdrive.core.property;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.inventory.MenuType;
import net.minecraft.world.level.block.entity.BlockEntity;
/**
* Interface for Objects that can manage properties.
* Default Implementations of this is:
* - Containers {@link MenuType}
* - Entities {@link Entity}
* - Block Entities {@link BlockEntity}
*/
public interface IPropertyManaged {
/**
* Gets the {@link PropertyManager} for the managing object.
*
* @return Returns the objects {@link PropertyManager}
*/
PropertyManager getPropertyManager();
}
package matteroverdrive.core.property;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.function.Consumer;
import java.util.function.Supplier;
/**
* This is our Property object class.
* It holds a reference to the {@link PropertyType} itself.
* Then optionally a {@link Supplier<T>} and {@link Consumer<T>} for getting and setting the object value.
*
* @param <T> The T type value object for the property.
*/
public final class Property<T> {
/**
* The Type of the {@link Property}
*/
private final PropertyType<T> propertyType;
/**
* The optional getter for the {@link Property}.
*/
private final Supplier<T> getter;
/**
* The optional setter for the {@link Property}.
*/
private final Consumer<T> setter;
/**
* The currently known value state of the {@link Property}.
*/
private T currentValue;
/**
* The previously known value state of the {@link Property}.
*/
private T lastKnownValue;
/**
* Default Empty Constructor
* This constructor is used currently for Client-Side Construction of Container Properties.
*
* @param propertyType The T {@link PropertyType} of the {@link Property}.
*/
public Property(PropertyType<T> propertyType) {
this.propertyType = propertyType;
this.getter = () -> currentValue;
this.setter = value -> currentValue = value;
}
/**
* Default Filled Constructor
* This constructor is used for common-sided construction of properties as well as
*
* @param propertyType The T {@link PropertyType} of the {@link Property}.
* @param getter The {@link Supplier<T>} getter of the {@link Property}.
* @param setter The {@link Consumer<T>} setter of the {@link Property}
*/
public Property(PropertyType<T> propertyType, Supplier<T> getter, Consumer<T> setter) {
this.propertyType = propertyType;
this.getter = getter;
this.setter = setter;
}
/**
* Checks if the {@link Property} object is dirty and in need of update.
* @return Returns if the {@link Property} is Dirty (changed) and needs updating.
*/
public boolean isDirty() {
T value = this.getter.get();
boolean dirty = lastKnownValue == null || !propertyType.getEquals().test(value, lastKnownValue);
this.lastKnownValue = value;
return dirty;
}
/**
* Gets the {@link Property} T value.
*
* @return Returns the value of T.
*/
@Nullable
public T get() {
return getter.get();
}
/**
* Gets the {@link Property} T value or an replacement.
*
* @param other Replacement value if T is null.
* @return Returns either T or Replacement.
*/
@Nonnull
public T getOrElse(T other) {
T gotten = getter.get();
if (gotten != null) {
return gotten;
} else {
return other;
}
}
/**
* Sets the {@link Property} T value.
*
* @param value The T value to set the {@link Property} T value to.
*/
public void set(T value) {
this.setter.accept(value);
}
/**
* Gets the T-valued {@link PropertyType} of the {@link Property}.
*
* @return Returns the T-valued {@link PropertyType} of the {@link Property}.
*/
public PropertyType<T> getPropertyType() {
return this.propertyType;
}
}
package matteroverdrive.core.property;
import matteroverdrive.MatterOverdrive;
import net.minecraft.network.FriendlyByteBuf;
import java.util.Objects;
import java.util.function.*;
public class PropertyType<T> implements Comparable<PropertyType<?>> {
/**
* The internal name of the {@link PropertyType}.
*/
private final String name;
/**
* The internal T value class of the {@link PropertyType}.
*/
private final Class<T> tClass;
/**
* The internal {@link Function} for reading the value from a {@link FriendlyByteBuf}.
*/
private final Function<FriendlyByteBuf, T> reader;
/**
* The internal {@link BiConsumer} for writing the value to a {@link FriendlyByteBuf}.
*/
private final BiConsumer<FriendlyByteBuf, T> writer;
/**
* The internal {@link BiPredicate} comparison object.
*/
private final BiPredicate<T, T> equals;
/**
* Default Constructor with a straight Objects::equals comparison.
*
* @param name The internal name for the {@link PropertyType}.
* @param tClass The internal T class reference for the {@link PropertyType}.
* @param reader The internal {@link Function} for reading the value from a {@link FriendlyByteBuf}.
* @param writer
*/
public PropertyType(String name, Class<T> tClass, Function<FriendlyByteBuf, T> reader,
BiConsumer<FriendlyByteBuf, T> writer) {
this(name, tClass, reader, writer, Objects::equals);
}
/**
* Default Constructor with a custom comparison predicate.
*
* @param name The internal name for the {@link PropertyType}.
* @param tClass The internal T class reference for the {@link PropertyType}.
* @param reader The internal {@link Function} for reading the value from a {@link FriendlyByteBuf}.
* @param writer The internal {@link BiConsumer} for writing the value to a {@link FriendlyByteBuf}.
* @param equals The internal {@link BiPredicate} comparison object.
*/
public PropertyType(String name, Class<T> tClass, Function<FriendlyByteBuf, T> reader,
BiConsumer<FriendlyByteBuf, T> writer, BiPredicate<T, T> equals) {
this.name = name;
this.tClass = tClass;
this.reader = reader;
this.writer = writer;
this.equals = equals;
}
/**
* Gets the reader {@link Function} object for the {@link PropertyType}.
*
* @return Returns the {@link Function} reader object.
*/
public Function<FriendlyByteBuf, T> getReader() {
return reader;
}
/**
* Gets the writer {@link BiConsumer} object for the {@link PropertyType}.
*
* @return Returns the {@link BiConsumer} writer object.
*/
public BiConsumer<FriendlyByteBuf, T> getWriter() {
return writer;
}
/**
* Gets the comparitor {@link BiPredicate} object for the {@link PropertyType}.
*
* @return Returns the {@link BiPredicate} comparitor object.
*/
public BiPredicate<T, T> getEquals() {
return equals;
}
/**
* Gets the internal {@link PropertyType} name.
*
* @return Returns the internal reference name of the {@link PropertyType}.
*/
public String getName() {
return name;
}
/**
* Create a new Property for this type.
*
* @return Returns a Property of type T matching this {@link PropertyType}'s T.
*/
public Property<T> create() {
return new Property<>(this);
}
/**
* Create a new Property for this type with provided getter.
*
* @param getter Getter for the Property.
* @return Returns a Property of type T matching this {@link PropertyType}'s T.
*/
public Property<T> create(Supplier<T> getter) {
return new Property<>(this, getter, value -> {
});
}
/**
* Create a new Property for this type with provided getter and setter.
*
* @param getter Getter for the Property.
* @param setter Setter for the Property.
* @return Returns a Property of type T matching this {@link PropertyType}'s T.
*/
public Property<T> create(Supplier<T> getter, Consumer<T> setter) {
return new Property<>(this, getter, setter);
}
/**
* Checks if the value of the passed Object is valid towards the T storage values T class value.
*
* @param object The passed in T value object.
* @return Returns whether the passed in object is an instance of the {@link PropertyType} T values class.
*/
public boolean isValid(Object object) {
return tClass.isInstance(object);
}
/**
* Attempts to write the object value to the {@link FriendlyByteBuf} buffer.
*
* @param packetBuffer The {@link FriendlyByteBuf} buffer being passed in.
* @param object The T value object.
*/
public void attemptWrite(FriendlyByteBuf packetBuffer, Object object) {
if (isValid(object)) {
this.getWriter().accept(packetBuffer, tClass.cast(object));
} else {
MatterOverdrive.LOGGER.error("Attempted to Write with Invalid Object.");
}
}
/**
* Attempts to set the T object value to the provided {@link Property}.
*
* @param object The T object value to set to the property.
* @param property The property to attempt to set the value of.
*/
@SuppressWarnings("unchecked")
public void attemptSet(Object object, Property<?> property) {
if (property.getPropertyType() == this) {
if (isValid(object)) {
try {
((Property<T>) property).set(tClass.cast(object));
} catch (ClassCastException classCastException) {
MatterOverdrive.LOGGER.error("Failed to Set Container Property", classCastException);
}
}
}
}
/**
* Default compareTo implementation.
* By default, compares the name of this object and the provided {@link PropertyType} object using case_insensitive matching.
*
* @param o The object to be compared.
* @return Returns 0 if it doesn't match, and 1 if it matches.
*/
@Override
public int compareTo(PropertyType<?> o) {
return String.CASE_INSENSITIVE_ORDER.compare(this.getName(), o.getName());
}
}
package matteroverdrive.core.property;
import com.google.common.collect.Lists;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraftforge.fluids.FluidStack;
import java.util.List;
import java.util.function.BiConsumer;
import java.util.function.BiPredicate;
import java.util.function.Function;
public class PropertyTypes {
/**
* Internally stored list of {@link PropertyType}'s.
*/
private static final List<PropertyType<?>> types = Lists.newArrayList();
/**
* Default {@link PropertyType} implementation for {@link FluidStack} values.
*/
public static PropertyType<FluidStack> FLUID_STACK = addType("fluid_stack", FluidStack.class,
FriendlyByteBuf::readFluidStack, FriendlyByteBuf::writeFluidStack, FluidStack::isFluidEqual);
/**
* Default {@link PropertyType} implementation for {@link Boolean} values.
*/
public static PropertyType<Boolean> BOOLEAN = addType("boolean", Boolean.class, FriendlyByteBuf::readBoolean,
FriendlyByteBuf::writeBoolean);
/**
* Default {@link PropertyType} implementation for {@link Integer} values.
*/
public static PropertyType<Integer> INTEGER = addType("integer", Integer.class, FriendlyByteBuf::readInt,
FriendlyByteBuf::writeInt);
/**
* Default {@link PropertyType} implementation for {@link Double} values.
*/
public static PropertyType<Double> DOUBLE = addType("double", Double.class, FriendlyByteBuf::readDouble,
FriendlyByteBuf::writeDouble);
/**
* Add type method.
*
* @param name The internal name for the {@link PropertyType}.
* @param tClass The internal T class reference for the {@link PropertyType}.
* @param reader The internal {@link Function} for reading the value from a {@link FriendlyByteBuf}.
* @param writer The internal {@link BiConsumer} for writing the value to a {@link FriendlyByteBuf}.
* @param <T> T value type.
* @return Returns a new {@link PropertyType} reference.
*/
public static <T> PropertyType<T> addType(String name, Class<T> tClass, Function<FriendlyByteBuf, T> reader,
BiConsumer<FriendlyByteBuf, T> writer) {
return addType(new PropertyType<>(name, tClass, reader, writer));
}
/**
* Add type method.
*
* @param name The internal name for the {@link PropertyType}.
* @param tClass The internal T class reference for the {@link PropertyType}.
* @param reader The internal {@link Function} for reading the value from a {@link FriendlyByteBuf}.
* @param writer The internal {@link BiConsumer} for writing the value to a {@link FriendlyByteBuf}.
* @param equals The internal {@link BiPredicate} comparison object.
* @param <T> T value type.
* @return Returns a new {@link PropertyType} reference.
*/
public static <T> PropertyType<T> addType(String name, Class<T> tClass, Function<FriendlyByteBuf, T> reader,
BiConsumer<FriendlyByteBuf, T> writer, BiPredicate<T, T> equals) {
return addType(new PropertyType<>(name, tClass, reader, writer, equals));
}
/**
* Default add type method.
*
* @param type The {@link PropertyType} to add.
* @param <T> T value type.
* @return Returns a new {@link PropertyType} reference.
*/
public static <T> PropertyType<T> addType(PropertyType<T> type) {
types.add(type);
types.sort(PropertyType::compareTo);
return type;
}
/**
* Gets the index of the {@link PropertyType} in the internal list.
*
* @param propertyType The {@link PropertyType} you want the index of.
* @return Returns the index of the corresponding to the provided {@link PropertyType}.
*/
public static short getIndex(PropertyType<?> propertyType) {
return (short) types.indexOf(propertyType);
}
/**
* Gets the {@link PropertyType} from the internal list using an index.
*
* @param index The index of the {@link PropertyType} you want to get.
* @return Returns the {@link PropertyType} corresponding to the index.
*/
public static PropertyType<?> getByIndex(short index) {
return types.get(index);
}
}
package matteroverdrive.core.property;
import java.util.List;
public abstract class PropertyManager {
/**
* List of internally stored {@link Property} values.
*/
protected final List<Property<?>> properties;
/**
* Default Constructor.
* This in most cases should be defaulted and passed an empty list.
*
* @param properties A storage list that holds {@link Property} values.
*/
public PropertyManager(List<Property<?>> properties) {
this.properties = properties;
}
/**
* Adds a tracked {@link Property} value to the internally stored list.
*
* @param property The {@link Property} to be added.
* @param <T> The T type of the property.
* @return Returns the {@link Property} itself for field storage.
*/
public <T> Property<T> addTrackedProperty(Property<T> property) {
this.properties.add(property);
return property;
}
/**
* Updates the stored {@link Property} value.
*
* @param propertyType The {@link PropertyType} of the {@link Property}.
* @param propertyId The short-numeric id of the stored {@link Property}.
* @param value The T object value of the {@link Property}.
*/
public void update(PropertyType<?> propertyType, short propertyId, Object value) {
if (propertyId < properties.size()) {
Property<?> property = properties.get(propertyId);
if (property != null && property.getPropertyType() == propertyType) {
propertyType.attemptSet(value, property);
}
}
}
}
package matteroverdrive.core.property.manager;
import com.google.common.collect.Lists;
import matteroverdrive.core.packet.NetworkHandler;
import matteroverdrive.core.property.Property;
import matteroverdrive.core.property.PropertyManager;
import matteroverdrive.core.property.PropertyType;
import matteroverdrive.core.property.message.UpdateClientBlockEntityPropertyMessage;
import matteroverdrive.core.property.message.UpdateServerBlockEntityPropertyMessage;
import net.minecraft.core.BlockPos;
import net.minecraft.world.level.block.entity.BlockEntity;
import org.apache.commons.lang3.tuple.Triple;
import java.util.List;
public class BlockEntityPropertyManager extends PropertyManager {
/**
* The {@link BlockPos} of the {@link BlockEntity}.
*/
private final BlockPos blockPos;
/**
* BlockEntity PropertyManager Constructor
*
* @param blockPos The BlockPos of the BlockEntity
*/
public BlockEntityPropertyManager(BlockPos blockPos) {
super(Lists.newArrayList());
this.blockPos = blockPos;
}
/**
* Client -> Server update method.
*
* @param property Property to update.
* @param value The value to update the property with.
* @param <T> The T value type.
*/
public <T> void updateServerBlockEntity(Property<T> property, T value) {
short propertyId = -1;
for (short i = 0; i < properties.size(); i++) {
if (properties.get(i) == property) {
propertyId = i;
}
}
property.set(value);
NetworkHandler.sendUpdateServerBlockEntityProperties(new UpdateServerBlockEntityPropertyMessage(blockPos, property.getPropertyType(), propertyId, value));
}
/**
* Server -> Client update method.
*
* @param blockPos The {@link BlockPos} of the {@link BlockEntity} in the world.
*/
public void sendBlockEntityChanges(BlockPos blockPos) {
List<Triple<PropertyType<?>, Short, Object>> dirtyProperties = Lists.newArrayList();
for (short i = 0; i < properties.size(); i++) {
Property<?> property = properties.get(i);
if (property.isDirty()) {
dirtyProperties.add(Triple.of(property.getPropertyType(), i, property.get()));
}
}
if (!dirtyProperties.isEmpty()) {
NetworkHandler.sendUpdateClientBlockEntityProperties(new UpdateClientBlockEntityPropertyMessage(blockPos, dirtyProperties));
}
}
}
package matteroverdrive.core.property.manager;
import matteroverdrive.core.packet.NetworkHandler;
import matteroverdrive.core.property.Property;
import matteroverdrive.core.property.PropertyManager;
import matteroverdrive.core.property.PropertyType;
import matteroverdrive.core.property.message.UpdateClientContainerPropertyMessage;
import matteroverdrive.core.property.message.UpdateServerContainerPropertyMessage;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.ContainerListener;
import org.apache.commons.compress.utils.Lists;
import org.apache.commons.lang3.tuple.Triple;
import java.util.Collection;
import java.util.List;
public class ContainerPropertyManager extends PropertyManager {
/**
* The menuId of the Container/Menu.
*/
private final short menuId;
/**
* Container PropertyManager Constructor
*
* @param menuId The id of the container.
*/
public ContainerPropertyManager(short menuId) {
super(Lists.newArrayList());
this.menuId = menuId;
}
/**
* Client -> Server update method.
*
* @param property Property to update.
* @param value The value to update the property with.
* @param <T> The T value type.
*/
public <T> void updateServerContainer(Property<T> property, T value) {
short propertyId = -1;
for (short i = 0; i < properties.size(); i++) {
if (properties.get(i) == property) {
propertyId = i;
}
}
property.set(value);
NetworkHandler.sendUpdateServerContainerProperties(
new UpdateServerContainerPropertyMessage(menuId, property.getPropertyType(), propertyId, value));
}
/**
* Server -> Client update method.
*
* @param containerListeners The {@link ContainerListener}'s of the Container.
* @param firstTime If this is the first sync for a new {@link ContainerListener} entering the container/menu.
*/
public void sendContainerChanges(Collection<ContainerListener> containerListeners, boolean firstTime) {
List<ServerPlayer> playerListeners = Lists.newArrayList();
for (ContainerListener listener : containerListeners) {
if (listener instanceof ServerPlayer player) {
playerListeners.add(player);
}
}
if (!playerListeners.isEmpty()) {
List<Triple<PropertyType<?>, Short, Object>> dirtyProperties = Lists.newArrayList();
for (short i = 0; i < properties.size(); i++) {
Property<?> property = properties.get(i);
if (property.isDirty() || firstTime) {
dirtyProperties.add(Triple.of(property.getPropertyType(), i, property.get()));
}
}
if (!dirtyProperties.isEmpty()) {
for (ServerPlayer playerEntity : playerListeners) {
NetworkHandler.sendUpdateClientContainerProperties(playerEntity,
new UpdateClientContainerPropertyMessage(menuId, dirtyProperties));
}
}
}
}
}
package matteroverdrive.core.property.manager;
import matteroverdrive.core.packet.NetworkHandler;
import matteroverdrive.core.property.Property;
import matteroverdrive.core.property.PropertyManager;
import matteroverdrive.core.property.PropertyType;
import matteroverdrive.core.property.message.UpdateClientEntityPropertyMessage;
import matteroverdrive.core.property.message.UpdateServerEntityPropertyMessage;
import net.minecraft.world.entity.Entity;
import org.apache.commons.compress.utils.Lists;
import org.apache.commons.lang3.tuple.Triple;
import java.util.List;
public class EntityPropertyManager extends PropertyManager {
/**
* The integer entityId of the entity.
*/
private final int entityId;
/**
* Entity PropertyManager Constructor
*
* @param entityId The integer id of the entity.
*/
public EntityPropertyManager(int entityId) {
super(Lists.newArrayList());
this.entityId = entityId;
}
/**
* Client -> Server update method.
*
* @param property Property to update.
* @param value The value to update the property with.
* @param <T> The T value type.
*/
public <T> void updateServerEntity(Property<T> property, T value) {
short propertyId = -1;
for (short i = 0; i < properties.size(); i++) {
if (properties.get(i) == property) {
propertyId = i;
}
}
property.set(value);
NetworkHandler.sendUpdateServerEntityProperties(new UpdateServerEntityPropertyMessage(entityId, property.getPropertyType(), propertyId, value));
}
/**
* Server -> Client update method.
*
* @param entity The integer id of the {@link Entity}.
*/
public void sendEntityChanges(Entity entity) {
List<Triple<PropertyType<?>, Short, Object>> dirtyProperties = Lists.newArrayList();
for (short i = 0; i < properties.size(); i++) {
Property<?> property = properties.get(i);
if (property.isDirty()) {
dirtyProperties.add(Triple.of(property.getPropertyType(), i, property.get()));
}
}
if (!dirtyProperties.isEmpty()) {
NetworkHandler.sendUpdateClientEntityProperties(entity, new UpdateClientEntityPropertyMessage(entityId, dirtyProperties));
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment