9
0
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:
XiaoMoMi
2025-02-13 16:55:28 +08:00
parent 02c7ce3904
commit db921ab0b3
10 changed files with 190 additions and 154 deletions

View File

@@ -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;
}
}
}

View File

@@ -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

View File

@@ -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));

View File

@@ -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);
}
}
}

View File

@@ -22,6 +22,4 @@ public abstract class Entity {
public abstract World level();
public abstract Direction getDirection();
}

View File

@@ -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
}