mirror of
https://github.com/Xiao-MoMi/craft-engine.git
synced 2025-12-30 12:29:15 +00:00
Added custom string property
This commit is contained in:
@@ -6,30 +6,16 @@ import net.momirealms.craftengine.core.registry.Holder;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class BlockStateHolder {
|
||||
private static final Function<Map.Entry<Property<?>, Comparable<?>>, String> PROPERTY_MAP_PRINTER = entry -> {
|
||||
if (entry == null) {
|
||||
return "<NULL>";
|
||||
}
|
||||
Property<?> property = entry.getKey();
|
||||
return property.name() + "=" + formatValue(property, entry.getValue());
|
||||
};
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private static <T extends Comparable<T>> String formatValue(Property<T> property, Comparable<?> value) {
|
||||
return property.valueName((T) value);
|
||||
}
|
||||
|
||||
protected final Holder<CustomBlock> owner;
|
||||
private final Reference2ObjectArrayMap<Property<?>, Comparable<?>> propertyMap;
|
||||
private Map<Property<?>, ImmutableBlockState[]> withMap;
|
||||
|
||||
public BlockStateHolder(Holder<CustomBlock> owner, Reference2ObjectArrayMap<Property<?>, Comparable<?>> propertyMap) {
|
||||
this.owner = owner;
|
||||
this.propertyMap = propertyMap;
|
||||
this.propertyMap = new Reference2ObjectArrayMap<>(propertyMap);
|
||||
}
|
||||
|
||||
public Holder<CustomBlock> owner() {
|
||||
@@ -37,113 +23,114 @@ public class BlockStateHolder {
|
||||
}
|
||||
|
||||
public <T extends Comparable<T>> ImmutableBlockState cycle(Property<T> property) {
|
||||
return this.with(property, getNextValue(property.possibleValues(), this.get(property)));
|
||||
T currentValue = get(property);
|
||||
List<T> values = property.possibleValues();
|
||||
return with(property, getNextValue(values, currentValue));
|
||||
}
|
||||
|
||||
protected static <T> T getNextValue(List<T> values, T currentValue) {
|
||||
int nextIndex = (values.indexOf(currentValue) + 1) % values.size();
|
||||
return values.get(nextIndex);
|
||||
int index = values.indexOf(currentValue);
|
||||
if (index == -1) {
|
||||
throw new IllegalArgumentException("Current value not found in possible values");
|
||||
}
|
||||
return values.get((index + 1) % values.size());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder result = new StringBuilder();
|
||||
result.append(this.owner.value().id());
|
||||
if (!this.getEntries().isEmpty()) {
|
||||
result.append('[');
|
||||
result.append(this.getEntries().entrySet().stream()
|
||||
.map(PROPERTY_MAP_PRINTER)
|
||||
.collect(Collectors.joining(",")));
|
||||
result.append(']');
|
||||
if (propertyMap.isEmpty()) {
|
||||
return owner.value().id().toString();
|
||||
}
|
||||
return result.toString();
|
||||
return owner.value().id() + "[" + getPropertiesAsString() + "]";
|
||||
}
|
||||
|
||||
public String getPropertiesAsString() {
|
||||
if (!this.getEntries().isEmpty()) {
|
||||
return this.getEntries().entrySet().stream()
|
||||
.map(PROPERTY_MAP_PRINTER)
|
||||
.collect(Collectors.joining(","));
|
||||
}
|
||||
return "";
|
||||
return propertyMap.entrySet().stream()
|
||||
.map(entry -> {
|
||||
Property<?> property = entry.getKey();
|
||||
return property.name() + "=" + formatValue(property, entry.getValue());
|
||||
})
|
||||
.collect(Collectors.joining(","));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private static <T extends Comparable<T>> String formatValue(Property<T> property, Comparable<?> value) {
|
||||
return property.valueName((T) value);
|
||||
}
|
||||
|
||||
public Collection<Property<?>> getProperties() {
|
||||
return Collections.unmodifiableCollection(this.propertyMap.keySet());
|
||||
return Collections.unmodifiableSet(propertyMap.keySet());
|
||||
}
|
||||
|
||||
public <T extends Comparable<T>> boolean contains(Property<T> property) {
|
||||
return this.propertyMap.containsKey(property);
|
||||
return propertyMap.containsKey(property);
|
||||
}
|
||||
|
||||
public <T extends Comparable<T>> T get(Property<T> property) {
|
||||
Comparable<?> value = this.propertyMap.get(property);
|
||||
T value = getNullable(property);
|
||||
if (value == null) {
|
||||
throw new IllegalArgumentException("Cannot get property " + property + " as it does not exist in " + this.owner);
|
||||
throw new IllegalArgumentException("Property " + property + " not found in " + owner.value().id());
|
||||
}
|
||||
return property.valueClass().cast(value);
|
||||
}
|
||||
|
||||
public <T extends Comparable<T>> Optional<T> getOrEmpty(Property<T> property) {
|
||||
return Optional.ofNullable(this.getNullable(property));
|
||||
return value;
|
||||
}
|
||||
|
||||
public <T extends Comparable<T>> T get(Property<T> property, T fallback) {
|
||||
return Objects.requireNonNullElse(this.getNullable(property), fallback);
|
||||
return Objects.requireNonNullElse(getNullable(property), fallback);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public <T extends Comparable<T>> T getNullable(Property<T> property) {
|
||||
Comparable<?> value = this.propertyMap.get(property);
|
||||
return value == null ? null : property.valueClass().cast(value);
|
||||
Comparable<?> value = propertyMap.get(property);
|
||||
return value != null ? property.valueClass().cast(value) : null;
|
||||
}
|
||||
|
||||
public <T extends Comparable<T>, V extends T> ImmutableBlockState with(Property<T> property, V value) {
|
||||
Comparable<?> currentValue = this.propertyMap.get(property);
|
||||
if (currentValue == null) {
|
||||
throw new IllegalArgumentException("Cannot set property " + property + " as it does not exist in " + this.owner);
|
||||
if (!propertyMap.containsKey(property)) {
|
||||
throw new IllegalArgumentException("Property " + property + " not found in " + owner.value().id());
|
||||
}
|
||||
return this.withInternal(property, value, currentValue);
|
||||
return withInternal(property, value);
|
||||
}
|
||||
|
||||
public <T extends Comparable<T>, V extends T> ImmutableBlockState withIfExists(Property<T> property, V value) {
|
||||
Comparable<?> currentValue = this.propertyMap.get(property);
|
||||
return currentValue == null ? ((ImmutableBlockState) this) : this.withInternal(property, value, currentValue);
|
||||
}
|
||||
|
||||
private <T extends Comparable<T>, V extends T> ImmutableBlockState withInternal(Property<T> property, V newValue, Comparable<?> currentValue) {
|
||||
if (currentValue.equals(newValue)) {
|
||||
private <T extends Comparable<T>, V extends T> ImmutableBlockState withInternal(Property<T> property, V newValue) {
|
||||
if (newValue.equals(propertyMap.get(property))) {
|
||||
return (ImmutableBlockState) this;
|
||||
}
|
||||
int valueIndex = property.indexOf(newValue);
|
||||
if (valueIndex < 0) {
|
||||
throw new IllegalArgumentException("Cannot set property " + property + " to " + newValue + " on " + this.owner + ", it is not an allowed value");
|
||||
|
||||
int index = property.indexOf(newValue);
|
||||
if (index == -1) {
|
||||
throw new IllegalArgumentException("Invalid value " + newValue + " for property " + property);
|
||||
}
|
||||
return (this.withMap.get(property))[valueIndex];
|
||||
|
||||
return withMap.get(property)[index];
|
||||
}
|
||||
|
||||
public void createWithMap(Map<Map<Property<?>, Comparable<?>>, ImmutableBlockState> states) {
|
||||
if (this.withMap != null) {
|
||||
throw new IllegalStateException("withMap is already initialized.");
|
||||
if (withMap != null) {
|
||||
throw new IllegalStateException("WithMap already initialized");
|
||||
}
|
||||
Map<Property<?>, ImmutableBlockState[]> map = new Reference2ObjectArrayMap<>(this.propertyMap.size());
|
||||
for (Map.Entry<Property<?>, Comparable<?>> entry : this.propertyMap.entrySet()) {
|
||||
Property<?> property = entry.getKey();
|
||||
ImmutableBlockState[] possibleStates = property.possibleValues().stream()
|
||||
.map(value -> states.get(this.createPropertyMap(property, value)))
|
||||
|
||||
Reference2ObjectArrayMap<Property<?>, ImmutableBlockState[]> map = new Reference2ObjectArrayMap<>(propertyMap.size());
|
||||
|
||||
for (Property<?> property : propertyMap.keySet()) {
|
||||
ImmutableBlockState[] statesArray = property.possibleValues().stream()
|
||||
.map(value -> {
|
||||
Map<Property<?>, Comparable<?>> testMap = new Reference2ObjectArrayMap<>(propertyMap);
|
||||
testMap.put(property, value);
|
||||
ImmutableBlockState state = states.get(testMap);
|
||||
if (state == null) {
|
||||
throw new IllegalStateException("Missing state for " + testMap);
|
||||
}
|
||||
return state;
|
||||
})
|
||||
.toArray(ImmutableBlockState[]::new);
|
||||
map.put(property, possibleStates);
|
||||
|
||||
map.put(property, statesArray);
|
||||
}
|
||||
this.withMap = map;
|
||||
|
||||
this.withMap = Map.copyOf(map);
|
||||
}
|
||||
|
||||
private Map<Property<?>, Comparable<?>> createPropertyMap(Property<?> property, Comparable<?> value) {
|
||||
Map<Property<?>, Comparable<?>> newMap = new Reference2ObjectArrayMap<>(this.propertyMap);
|
||||
newMap.put(property, value);
|
||||
return newMap;
|
||||
public Map<Property<?>, Comparable<?>> propertyEntries() {
|
||||
return Collections.unmodifiableMap(propertyMap);
|
||||
}
|
||||
|
||||
public Map<Property<?>, Comparable<?>> getEntries() {
|
||||
return this.propertyMap;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -83,13 +83,13 @@ public class EnumProperty<T extends Enum<T>> extends Property<T> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String valueName(T enum_) {
|
||||
return enum_.name().toLowerCase(Locale.ENGLISH);
|
||||
public String valueName(T value) {
|
||||
return value.name().toLowerCase(Locale.ENGLISH);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int indexOf(T enum_) {
|
||||
return this.ordinalToIndex[enum_.ordinal()];
|
||||
public int indexOf(T value) {
|
||||
return this.ordinalToIndex[value.ordinal()];
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -14,6 +14,7 @@ import java.util.Map;
|
||||
public class Properties {
|
||||
public static final Key BOOLEAN = Key.of("craftengine:boolean");
|
||||
public static final Key INT = Key.of("craftengine:int");
|
||||
public static final Key STRING = Key.of("craftengine:string");
|
||||
public static final Key AXIS = Key.of("craftengine:axis");
|
||||
public static final Key HORIZONTAL_DIRECTION = Key.of("craftengine:4-direction");
|
||||
public static final Key DIRECTION = Key.of("craftengine:6-direction");
|
||||
@@ -21,6 +22,7 @@ public class Properties {
|
||||
static {
|
||||
register(BOOLEAN, BooleanProperty.FACTORY);
|
||||
register(INT, IntegerProperty.FACTORY);
|
||||
register(STRING, StringProperty.FACTORY);
|
||||
register(AXIS, new EnumProperty.Factory<>(Direction.Axis.class));
|
||||
register(DIRECTION, new EnumProperty.Factory<>(Direction.class));
|
||||
register(HORIZONTAL_DIRECTION, new EnumProperty.Factory<>(HorizontalDirection.class));
|
||||
|
||||
@@ -0,0 +1,107 @@
|
||||
package net.momirealms.craftengine.core.block.properties;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import net.momirealms.craftengine.core.plugin.CraftEngine;
|
||||
import net.momirealms.craftengine.core.util.MiscUtils;
|
||||
import net.momirealms.sparrow.nbt.StringTag;
|
||||
import net.momirealms.sparrow.nbt.Tag;
|
||||
|
||||
import java.lang.reflect.Array;
|
||||
import java.util.*;
|
||||
|
||||
public class StringProperty extends Property<String> {
|
||||
public static final Factory FACTORY = new Factory();
|
||||
private final List<String> values;
|
||||
private final ImmutableMap<String, String> names;
|
||||
|
||||
public StringProperty(String name, List<String> values, String defaultValue) {
|
||||
super(name, String.class, defaultValue);
|
||||
|
||||
this.values = List.copyOf(values);
|
||||
|
||||
ImmutableMap.Builder<String, String> builder = ImmutableMap.builder();
|
||||
for (String value : values) {
|
||||
builder.put(value, value);
|
||||
}
|
||||
this.names = builder.build();
|
||||
|
||||
this.setById(values.toArray(new String[0]));
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> possibleValues() {
|
||||
return this.values;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<String> optional(String valueName) {
|
||||
return Optional.ofNullable(this.names.get(valueName.toLowerCase(Locale.ENGLISH)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<Tag> createOptionalTag(String valueName) {
|
||||
return optional(valueName).map(StringTag::new);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Tag pack(String value) {
|
||||
return new StringTag(valueName(value));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String unpack(Tag tag) {
|
||||
if (tag instanceof StringTag stringTag) {
|
||||
return names.get(stringTag.getAsString());
|
||||
}
|
||||
throw new IllegalArgumentException("Invalid string tag: " + tag);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final int idFor(String value) {
|
||||
int index = indexOf(value.toLowerCase(Locale.ENGLISH));
|
||||
if (index == -1) {
|
||||
throw new IllegalArgumentException("Invalid value: " + value);
|
||||
}
|
||||
return index;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String valueName(String value) {
|
||||
return value.toLowerCase(Locale.ENGLISH);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int indexOf(String value) {
|
||||
return values.indexOf(value.toLowerCase(Locale.ENGLISH));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int generateHashCode() {
|
||||
int i = super.generateHashCode();
|
||||
return 31 * i + this.values.hashCode();
|
||||
}
|
||||
|
||||
public static StringProperty create(String name, List<String> values, String defaultValue) {
|
||||
return new StringProperty(name, values, defaultValue);
|
||||
}
|
||||
|
||||
public static class Factory implements PropertyFactory {
|
||||
|
||||
@Override
|
||||
public Property<?> create(String name, Map<String, Object> arguments) {
|
||||
List<String> values = MiscUtils.getAsStringList(arguments.get("values"))
|
||||
.stream()
|
||||
.map(it -> it.toLowerCase(Locale.ENGLISH))
|
||||
.toList();
|
||||
String defaultValueName = arguments.getOrDefault("default", "").toString().toLowerCase(Locale.ENGLISH);
|
||||
String defaultValue = values.stream()
|
||||
.filter(e -> e.toLowerCase(Locale.ENGLISH).equals(defaultValueName))
|
||||
.findFirst()
|
||||
.orElseGet(() -> {
|
||||
CraftEngine.instance().logger().warn("Invalid default value '" + defaultValueName + "' for property '" + name + "'");
|
||||
return values.getFirst();
|
||||
});
|
||||
return StringProperty.create(name, values, defaultValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -22,6 +22,4 @@ public abstract class Entity {
|
||||
public abstract World level();
|
||||
|
||||
public abstract Direction getDirection();
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -1,63 +1,7 @@
|
||||
package net.momirealms.craftengine.core.entity.player;
|
||||
|
||||
import net.momirealms.craftengine.core.item.Item;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public sealed interface InteractionResult
|
||||
permits InteractionResult.Success, InteractionResult.Fail, InteractionResult.Pass, InteractionResult.TryEmptyHandInteraction {
|
||||
|
||||
InteractionResult.Success SUCCESS = new InteractionResult.Success(InteractionResult.SwingSource.CLIENT, InteractionResult.ItemContext.DEFAULT);
|
||||
InteractionResult.Success SUCCESS_SERVER = new InteractionResult.Success(InteractionResult.SwingSource.SERVER, InteractionResult.ItemContext.DEFAULT);
|
||||
InteractionResult.Success CONSUME = new InteractionResult.Success(InteractionResult.SwingSource.NONE, InteractionResult.ItemContext.DEFAULT);
|
||||
InteractionResult.Fail FAIL = new InteractionResult.Fail();
|
||||
InteractionResult.Pass PASS = new InteractionResult.Pass();
|
||||
InteractionResult.TryEmptyHandInteraction TRY_WITH_EMPTY_HAND = new InteractionResult.TryEmptyHandInteraction();
|
||||
|
||||
default boolean consumesAction() {
|
||||
return false;
|
||||
}
|
||||
|
||||
record Fail() implements InteractionResult {
|
||||
}
|
||||
|
||||
record ItemContext(boolean wasItemInteraction, @Nullable Item<?> heldItemTransformedTo) {
|
||||
static InteractionResult.ItemContext NONE = new InteractionResult.ItemContext(false, null);
|
||||
static InteractionResult.ItemContext DEFAULT = new InteractionResult.ItemContext(true, null);
|
||||
}
|
||||
|
||||
record Pass() implements InteractionResult {
|
||||
}
|
||||
|
||||
record Success(InteractionResult.SwingSource swingSource, InteractionResult.ItemContext itemContext) implements InteractionResult {
|
||||
@Override
|
||||
public boolean consumesAction() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public InteractionResult.Success heldItemTransformedTo(Item<?> newHandStack) {
|
||||
return new InteractionResult.Success(this.swingSource, new InteractionResult.ItemContext(true, newHandStack));
|
||||
}
|
||||
|
||||
public InteractionResult.Success withoutItem() {
|
||||
return new InteractionResult.Success(this.swingSource, InteractionResult.ItemContext.NONE);
|
||||
}
|
||||
|
||||
public boolean wasItemInteraction() {
|
||||
return this.itemContext.wasItemInteraction;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Item<?> heldItemTransformedTo() {
|
||||
return this.itemContext.heldItemTransformedTo;
|
||||
}
|
||||
}
|
||||
|
||||
enum SwingSource {
|
||||
NONE,
|
||||
CLIENT,
|
||||
SERVER;
|
||||
}
|
||||
|
||||
record TryEmptyHandInteraction() implements InteractionResult {
|
||||
}
|
||||
public enum InteractionResult {
|
||||
FAIL,
|
||||
SUCCESS,
|
||||
PASS
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user