diff --git a/README.md b/README.md index 6814053..626bfa4 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,2 @@ -# SpongeToolsAPI +# SpongeTools Various useful utilities for SpongeAPI diff --git a/api/src/main/java/net/hellheim/spongetools/SpongeTools.java b/api/src/main/java/net/hellheim/spongetools/SpongeTools.java new file mode 100644 index 0000000..aca8dc5 --- /dev/null +++ b/api/src/main/java/net/hellheim/spongetools/SpongeTools.java @@ -0,0 +1,152 @@ +package net.hellheim.spongetools; + +import org.spongepowered.api.ResourceKey; +import org.spongepowered.api.Sponge; +import org.spongepowered.api.block.BlockSoundGroup; +import org.spongepowered.api.data.Key; +import org.spongepowered.api.data.value.ListValue; +import org.spongepowered.api.data.value.Value; +import org.spongepowered.api.effect.sound.SoundType; +import org.spongepowered.api.item.inventory.equipment.EquipmentType; +import org.spongepowered.api.map.color.MapColorType; +import org.spongepowered.api.registry.DefaultedRegistryType; +import org.spongepowered.api.registry.RegistryRoots; +import org.spongepowered.api.registry.RegistryType; + +import com.mojang.serialization.MapCodec; + +import net.hellheim.spongetools.custom.type.block.BlockTypeArchetype; +import net.hellheim.spongetools.custom.type.block.ModeledBlock; +import net.hellheim.spongetools.custom.type.entity.EntityTypeArchetype; +import net.hellheim.spongetools.custom.type.entity.ModeledEntity; +import net.hellheim.spongetools.custom.type.item.CustomItemAction; +import net.hellheim.spongetools.custom.type.item.ItemTypeArchetype; +import net.hellheim.spongetools.custom.type.item.LoreProcessor; +import net.hellheim.spongetools.custom.type.item.LoreProvider; +import net.hellheim.spongetools.custom.type.item.ModeledItem; +import net.hellheim.spongetools.resourcepack.Model; +import net.hellheim.spongetools.resourcepack.block.BlockDefinition; +import net.hellheim.spongetools.resourcepack.item.ItemDefinition; + +public final class SpongeTools { + + public static final String NAMESPACE = "spongetools"; + + public static ResourceKey key(final String value) { + return ResourceKey.of(SpongeTools.NAMESPACE, value); + } + + public static final class Registries { + + // Archetypes + + public static final DefaultedRegistryType BLOCK_TYPE_ARCHETYPE = Registries.key("block_type_archetype"); + + public static final DefaultedRegistryType ENTITY_TYPE_ARCHETYPE = Registries.key("entity_type_archetype"); + + public static final DefaultedRegistryType ITEM_TYPE_ARCHETYPE = Registries.key("item_type_archetype"); + + // Modeled types + + public static final DefaultedRegistryType MODELED_BLOCK = Registries.key("modeled_block"); + + public static final DefaultedRegistryType MODELED_ENTITY = Registries.key("modeled_entity"); + + public static final DefaultedRegistryType MODELED_ITEM = Registries.key("modeled_item"); + + /** + * Definitions from this registry will be included in built ResourcePack. + */ + public static final DefaultedRegistryType BLOCK_DEFINITION = Registries.key("blockstates"); + + /** + * Definitions from this registry will be included in built ResourcePack. + */ + public static final DefaultedRegistryType ITEM_DEFINITION = Registries.key("items"); + + /** + * Models from this registry will be included in built ResourcePack. + */ + public static final DefaultedRegistryType MODEL = Registries.key("models"); + + public static final DefaultedRegistryType> LORE_PROCESSOR_TYPE = Registries.key("lore_processor_type"); + + public static final DefaultedRegistryType> LORE_PROVIDER_TYPE = Registries.key("lore_provider_type"); + + public static final DefaultedRegistryType> ITEM_ACTION_CONFIG_TYPE = Registries.key("item_action_config_type"); + + private static DefaultedRegistryType key(final String key) { + return RegistryType.of(RegistryRoots.SPONGE, SpongeTools.key(key)).asDefaultedType(Sponge::game); + } + + private Registries() { + } + } + + public static final class Keys { + + public static final Key> BLOCK_SOUND_GROUP = Keys.key("block_sound_group", BlockSoundGroup.class); + + public static final Key> EQUIPMENT_ASSET = Keys.key("equipment_asset", ResourceKey.class); + + public static final Key> EQUIPMENT_CAMERA_OVERLAY = Keys.key("equipment_camera_overlay", ResourceKey.class); + + public static final Key> EQUIPMENT_DAMAGEABLE = Keys.key("equipment_damageable", Boolean.class); + + public static final Key> EQUIPMENT_DISPENSABLE = Keys.key("equipment_dispensable", Boolean.class); + + public static final Key> EQUIPMENT_SOUND = Keys.key("equipment_sound", SoundType.class); + + public static final Key> EQUIPMENT_SWAPPABLE = Keys.key("equipment_swappable", Boolean.class); + + public static final Key> EQUIPMENT_TYPE = Keys.key("equipment_type", EquipmentType.class); + + public static final Key> FRICTION_FACTOR = Keys.key("friction_factor", Double.class); + + public static final Key> HAS_COLLISION = Keys.key("collision", Boolean.class); + + public static final Key> HAS_DYNAMIC_SHAPE = Keys.key("dynamic_shape", Boolean.class); + + public static final Key> HAS_OCCLUSION = Keys.key("occlusion", Boolean.class); + + public static final Key> JUMP_FACTOR = Keys.key("jump_factor", Double.class); + + public static final Key> LOOT_TABLE_KEY = Keys.key("loot_table_key", ResourceKey.class); + + public static final Key> LORE_PROCESSOR = Keys.key("lore_processor", LoreProcessor.class); + + public static final Key> LORE_PROVIDERS = Keys.listKey("lore_providers", LoreProvider.class); + + public static final Key> MAP_COLOR_TYPE = Keys.key("map_color_type", MapColorType.class); + + public static final Key> REQUIRE_TOOL = Keys.key("require_tool", Boolean.class); + + public static final Key> SPEED_FACTOR = Keys.key("speed_factor", Double.class); + + private static Key> key(final String key, final Class type) { + return Key.from(SpongeTools.key(key), type); + } + + private static Key> listKey(final String key, final Class type) { + return Key.fromList(SpongeTools.key(key), type); + } + + private Keys() { + } + } + + public static final class Queries { + + private Queries() { + } + } + + public static final class Versions { + + private Versions() { + } + } + + private SpongeTools() { + } +} diff --git a/src/main/java/net/hellheim/spongetools/codec/CodecDispatcher.java b/api/src/main/java/net/hellheim/spongetools/codec/CodecDispatcher.java similarity index 100% rename from src/main/java/net/hellheim/spongetools/codec/CodecDispatcher.java rename to api/src/main/java/net/hellheim/spongetools/codec/CodecDispatcher.java diff --git a/src/main/java/net/hellheim/spongetools/codec/CodecMapper.java b/api/src/main/java/net/hellheim/spongetools/codec/CodecMapper.java similarity index 93% rename from src/main/java/net/hellheim/spongetools/codec/CodecMapper.java rename to api/src/main/java/net/hellheim/spongetools/codec/CodecMapper.java index a8457c1..7dfed14 100644 --- a/src/main/java/net/hellheim/spongetools/codec/CodecMapper.java +++ b/api/src/main/java/net/hellheim/spongetools/codec/CodecMapper.java @@ -4,6 +4,8 @@ import com.mojang.serialization.Codec; +import net.hellheim.spongetools.codec.list.TypeCodecs; + public interface CodecMapper { interface M1 extends CodecMapper { diff --git a/src/main/java/net/hellheim/spongetools/codec/CodecMapperDispatcher.java b/api/src/main/java/net/hellheim/spongetools/codec/CodecMapperDispatcher.java similarity index 95% rename from src/main/java/net/hellheim/spongetools/codec/CodecMapperDispatcher.java rename to api/src/main/java/net/hellheim/spongetools/codec/CodecMapperDispatcher.java index ad2f704..493f93a 100644 --- a/src/main/java/net/hellheim/spongetools/codec/CodecMapperDispatcher.java +++ b/api/src/main/java/net/hellheim/spongetools/codec/CodecMapperDispatcher.java @@ -8,6 +8,8 @@ import com.mojang.serialization.Codec; +import net.hellheim.spongetools.codec.list.ExtraCodecs; + public final class CodecMapperDispatcher { private final LateBoundIdMapper> idMapper = new LateBoundIdMapper<>(); diff --git a/src/main/java/net/hellheim/spongetools/codec/LateBoundIdMapper.java b/api/src/main/java/net/hellheim/spongetools/codec/LateBoundIdMapper.java similarity index 84% rename from src/main/java/net/hellheim/spongetools/codec/LateBoundIdMapper.java rename to api/src/main/java/net/hellheim/spongetools/codec/LateBoundIdMapper.java index ab7be67..6d6b11c 100644 --- a/src/main/java/net/hellheim/spongetools/codec/LateBoundIdMapper.java +++ b/api/src/main/java/net/hellheim/spongetools/codec/LateBoundIdMapper.java @@ -6,12 +6,14 @@ import com.google.common.collect.HashBiMap; import com.mojang.serialization.Codec; +import net.hellheim.spongetools.codec.list.ExtraCodecs; + public class LateBoundIdMapper { protected final BiMap idToValue = HashBiMap.create(); public Codec codec(final Codec idCodec) { - BiMap bimap = this.idToValue.inverse(); + final BiMap bimap = this.idToValue.inverse(); return ExtraCodecs.idResolver(idCodec, this.idToValue::get, bimap::get); } diff --git a/src/main/java/net/hellheim/spongetools/codec/MapCodecMapper.java b/api/src/main/java/net/hellheim/spongetools/codec/MapCodecMapper.java similarity index 94% rename from src/main/java/net/hellheim/spongetools/codec/MapCodecMapper.java rename to api/src/main/java/net/hellheim/spongetools/codec/MapCodecMapper.java index 64feb06..c92c21e 100644 --- a/src/main/java/net/hellheim/spongetools/codec/MapCodecMapper.java +++ b/api/src/main/java/net/hellheim/spongetools/codec/MapCodecMapper.java @@ -5,6 +5,8 @@ import com.mojang.serialization.Codec; import com.mojang.serialization.MapCodec; +import net.hellheim.spongetools.codec.list.TypeCodecs; + public interface MapCodecMapper { interface M1 extends MapCodecMapper { diff --git a/src/main/java/net/hellheim/spongetools/codec/StringRepresentableCodec.java b/api/src/main/java/net/hellheim/spongetools/codec/StringRepresentableCodec.java similarity index 83% rename from src/main/java/net/hellheim/spongetools/codec/StringRepresentableCodec.java rename to api/src/main/java/net/hellheim/spongetools/codec/StringRepresentableCodec.java index 01ca46e..10c1ac4 100644 --- a/src/main/java/net/hellheim/spongetools/codec/StringRepresentableCodec.java +++ b/api/src/main/java/net/hellheim/spongetools/codec/StringRepresentableCodec.java @@ -13,6 +13,8 @@ import com.mojang.serialization.DataResult; import com.mojang.serialization.DynamicOps; +import net.hellheim.spongetools.codec.list.ExtraCodecs; + public class StringRepresentableCodec implements Codec { private final Supplier memoized; @@ -30,7 +32,11 @@ public StringRepresentableCodec( ); } - static StringRepresentableCodec fromValues(final Supplier values) { + public static StringRepresentableCodec fromValues(final S[] values) { + return StringRepresentableCodec.fromValues(() -> values); + } + + public static StringRepresentableCodec fromValues(final Supplier values) { final Supplier memoized = Suppliers.memoize(values::get); final Supplier> memoizedList = Suppliers.memoize(() -> List.of(memoized.get())); @@ -40,7 +46,7 @@ static StringRepresentableCodec fromValues(fi return new StringRepresentableCodec<>(memoized, nameLookup, indexLookup); } - static Function nameLookup( + public static Function nameLookup( final Supplier values, final Function keyFunction ) { final Supplier memoized = Suppliers.memoize(values::get); diff --git a/src/main/java/net/hellheim/spongetools/codec/dispatched/TableEntryCodecs.java b/api/src/main/java/net/hellheim/spongetools/codec/dispatched/TableEntryCodecs.java similarity index 98% rename from src/main/java/net/hellheim/spongetools/codec/dispatched/TableEntryCodecs.java rename to api/src/main/java/net/hellheim/spongetools/codec/dispatched/TableEntryCodecs.java index 6b5e68d..ba83c63 100644 --- a/src/main/java/net/hellheim/spongetools/codec/dispatched/TableEntryCodecs.java +++ b/api/src/main/java/net/hellheim/spongetools/codec/dispatched/TableEntryCodecs.java @@ -18,7 +18,7 @@ import net.hellheim.spongetools.codec.CodecMapperDispatcher; import net.hellheim.spongetools.codec.MapCodecMapper; -import net.hellheim.spongetools.codec.SpongeCodecs; +import net.hellheim.spongetools.codec.list.SpongeCodecs; public final class TableEntryCodecs { diff --git a/src/main/java/net/hellheim/spongetools/codec/dispatched/VariableAmountCodecs.java b/api/src/main/java/net/hellheim/spongetools/codec/dispatched/VariableAmountCodecs.java similarity index 95% rename from src/main/java/net/hellheim/spongetools/codec/dispatched/VariableAmountCodecs.java rename to api/src/main/java/net/hellheim/spongetools/codec/dispatched/VariableAmountCodecs.java index 4aa4c62..4f39bd7 100644 --- a/src/main/java/net/hellheim/spongetools/codec/dispatched/VariableAmountCodecs.java +++ b/api/src/main/java/net/hellheim/spongetools/codec/dispatched/VariableAmountCodecs.java @@ -9,7 +9,7 @@ import com.mojang.serialization.codecs.RecordCodecBuilder; import net.hellheim.spongetools.codec.CodecDispatcher; -import net.hellheim.spongetools.codec.SpongeCodecs; +import net.hellheim.spongetools.codec.list.SpongeCodecs; public class VariableAmountCodecs { diff --git a/src/main/java/net/hellheim/spongetools/codec/AdventureCodecs.java b/api/src/main/java/net/hellheim/spongetools/codec/list/AdventureCodecs.java similarity index 97% rename from src/main/java/net/hellheim/spongetools/codec/AdventureCodecs.java rename to api/src/main/java/net/hellheim/spongetools/codec/list/AdventureCodecs.java index 06680bd..87ef375 100644 --- a/src/main/java/net/hellheim/spongetools/codec/AdventureCodecs.java +++ b/api/src/main/java/net/hellheim/spongetools/codec/list/AdventureCodecs.java @@ -1,4 +1,4 @@ -package net.hellheim.spongetools.codec; +package net.hellheim.spongetools.codec.list; import org.spongepowered.api.Sponge; @@ -63,7 +63,7 @@ public final class AdventureCodecs { public static final Codec CHAT_TYPE = AdventureCodecs.factory().chatType(); public static Codec key(final String defaultNamespace) { - if (Key.parseableNamespace(defaultNamespace)) { + if (!Key.parseableNamespace(defaultNamespace)) { throw new IllegalArgumentException("Invalid namespace: " + defaultNamespace); } diff --git a/src/main/java/net/hellheim/spongetools/codec/BuildableCodecs.java b/api/src/main/java/net/hellheim/spongetools/codec/list/BuildableCodecs.java similarity index 98% rename from src/main/java/net/hellheim/spongetools/codec/BuildableCodecs.java rename to api/src/main/java/net/hellheim/spongetools/codec/list/BuildableCodecs.java index 9913f8f..22cf76d 100644 --- a/src/main/java/net/hellheim/spongetools/codec/BuildableCodecs.java +++ b/api/src/main/java/net/hellheim/spongetools/codec/list/BuildableCodecs.java @@ -1,4 +1,4 @@ -package net.hellheim.spongetools.codec; +package net.hellheim.spongetools.codec.list; import java.util.Optional; import java.util.function.Function; diff --git a/src/main/java/net/hellheim/spongetools/codec/DataCodecs.java b/api/src/main/java/net/hellheim/spongetools/codec/list/DataCodecs.java similarity index 60% rename from src/main/java/net/hellheim/spongetools/codec/DataCodecs.java rename to api/src/main/java/net/hellheim/spongetools/codec/list/DataCodecs.java index 273f889..d63b41d 100644 --- a/src/main/java/net/hellheim/spongetools/codec/DataCodecs.java +++ b/api/src/main/java/net/hellheim/spongetools/codec/list/DataCodecs.java @@ -1,4 +1,4 @@ -package net.hellheim.spongetools.codec; +package net.hellheim.spongetools.codec.list; import java.io.IOException; import java.util.HashMap; @@ -11,17 +11,26 @@ import org.spongepowered.api.ResourceKey; import org.spongepowered.api.Sponge; -import org.spongepowered.api.data.DataHolder; import org.spongepowered.api.data.Key; import org.spongepowered.api.data.persistence.DataBuilder; import org.spongepowered.api.data.persistence.DataContainer; +import org.spongepowered.api.data.persistence.DataFormats; import org.spongepowered.api.data.persistence.DataSerializable; +import org.spongepowered.api.data.persistence.DataView; import org.spongepowered.api.data.persistence.InvalidDataException; import org.spongepowered.api.data.persistence.StringDataFormat; import org.spongepowered.api.data.value.Value; +import org.spongepowered.api.data.value.ValueContainer; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.JsonElement; +import com.google.gson.JsonParseException; +import com.google.gson.JsonParser; +import com.mojang.datafixers.util.Pair; import com.mojang.serialization.Codec; import com.mojang.serialization.DataResult; +import com.mojang.serialization.JsonOps; /** * Codecs for SpongeAPI's DataAPI types @@ -30,7 +39,7 @@ public final class DataCodecs { private static final Map> CONTAINERS = new HashMap<>(); - public static Function> keyLookup(final DataHolder keyLookupProvider) { + public static Function> keyLookup(final ValueContainer keyLookupProvider) { return keyLookupProvider.getKeys() .stream() .collect(Collectors.toUnmodifiableMap(Key::key, Function.identity())) @@ -41,7 +50,7 @@ public static Codec byKey(final Key key) { return TypeCodecs.of(key.elementType()); } - public static Codec>> key(final DataHolder keyLookupProvider) { + public static Codec>> key(final ValueContainer keyLookupProvider) { return DataCodecs.key(DataCodecs.keyLookup(keyLookupProvider)); } @@ -56,7 +65,7 @@ public static Codec>> key(final Function>, Object>> map(final DataHolder keyLookupProvider) { + public static Codec>, Object>> map(final ValueContainer keyLookupProvider) { return DataCodecs.map(DataCodecs.keyLookup(keyLookupProvider)); } @@ -64,11 +73,11 @@ public static Codec>, Object>> map(final Function>> valueSet(final DataHolder keyLookupProvider) { + public static Codec>> valueSet(final ValueContainer keyLookupProvider) { return DataCodecs.valueSet(DataCodecs.keyLookup(keyLookupProvider)); } - public static Codec>> valueSet(Function> keyLookup) { + public static Codec>> valueSet(final Function> keyLookup) { return DataCodecs.map(keyLookup).xmap( map -> map.entrySet().stream() .map(DataCodecs::entryToValue) @@ -104,7 +113,7 @@ public static Codec serializable( } return DataResult.success(value.get()); - } catch (InvalidDataException e) { + } catch (final InvalidDataException e) { return DataResult.error(e::toString); } }, DataSerializable::toContainer); @@ -115,23 +124,64 @@ public static Codec container(final Supplier container(final StringDataFormat format) { + return DataCodecs.CONTAINERS.computeIfAbsent(format, f -> DataCodecs.container(Codec.STRING, format)); + } + + public static Codec container(final Codec stringCodec, final Supplier format) { + return DataCodecs.container(stringCodec, format.get()); + } + + public static Codec container(final Codec stringCodec, final StringDataFormat format) { return DataCodecs.CONTAINERS.computeIfAbsent(format, f -> { - return Codec.STRING.flatXmap(string -> { + return stringCodec.flatXmap(string -> { try { return DataResult.success(f.read(string)); - } catch (IOException e) { + } catch (final IOException e) { return DataResult.error(e::toString); } }, container -> { try { return DataResult.success(f.write(container)); - } catch (IOException e) { + } catch (final IOException e) { return DataResult.error(e::toString); } }); }); } + private static final Gson GSON = new GsonBuilder().create(); + public static DataContainer toContainer(final Codec codec, final T value) { + final DataResult encoded = codec.encodeStart(JsonOps.INSTANCE, value); + final JsonElement json = encoded.getOrThrow(IllegalArgumentException::new); + final String str = GSON.toJson(json); + try { + return DataFormats.JSON.get().read(str); + } catch (final IOException | InvalidDataException e) { + // Should never happen + throw new RuntimeException(e); + } + } + + public static T fromContainer(final Codec codec, final DataView container) throws InvalidDataException { + try { + final String str = DataFormats.JSON.get().write(container); + final JsonElement json = JsonParser.parseString(str); + final DataResult decoded = codec.decode(JsonOps.INSTANCE, json).map(Pair::getFirst); + return decoded.getOrThrow(InvalidDataException::new); + } catch (final IOException | JsonParseException e) { + throw new InvalidDataException(e); + } + } + + public static DataBuilder dataBuilder(final Codec codec) { + return new DataBuilder<>() { + @Override + public Optional build(final DataView container) throws InvalidDataException { + return Optional.of(DataCodecs.fromContainer(codec, container)); + } + }; + } + private static > Value.Immutable entryToValue( final Map.Entry, Object> entry ) { diff --git a/src/main/java/net/hellheim/spongetools/codec/ExtraCodecs.java b/api/src/main/java/net/hellheim/spongetools/codec/list/ExtraCodecs.java similarity index 84% rename from src/main/java/net/hellheim/spongetools/codec/ExtraCodecs.java rename to api/src/main/java/net/hellheim/spongetools/codec/list/ExtraCodecs.java index 2ac9292..2aaf87a 100644 --- a/src/main/java/net/hellheim/spongetools/codec/ExtraCodecs.java +++ b/api/src/main/java/net/hellheim/spongetools/codec/list/ExtraCodecs.java @@ -1,4 +1,4 @@ -package net.hellheim.spongetools.codec; +package net.hellheim.spongetools.codec.list; import java.time.format.DateTimeFormatter; import java.time.temporal.TemporalAccessor; @@ -6,16 +6,19 @@ import java.util.Collection; import java.util.HashSet; import java.util.List; +import java.util.Locale; import java.util.Map; import java.util.Optional; import java.util.OptionalLong; import java.util.Set; +import java.util.TimeZone; import java.util.function.BiFunction; import java.util.function.Function; import java.util.function.IntFunction; import java.util.function.ToIntFunction; import java.util.regex.Pattern; +import org.apache.commons.lang3.LocaleUtils; import org.checkerframework.checker.nullness.qual.Nullable; import org.spongepowered.api.Sponge; @@ -51,6 +54,23 @@ public final class ExtraCodecs { public static final Codec PLAYER_NAME = ExtraCodecs.factory().playerName(); public static final Codec PATTERN = ExtraCodecs.factory().pattern(); + public static final Codec TIME_ZONE = Codec.STRING.comapFlatMap(str -> { + for (final String id : TimeZone.getAvailableIDs()) { + if (id.equals(str)) { + return DataResult.success(TimeZone.getTimeZone(str)); + } + } + return DataResult.error(() -> "Unknown timezone: " + str); + }, TimeZone::getID); + + public static final Codec LOCALE = Codec.STRING.comapFlatMap(str -> { + try { + return DataResult.success(LocaleUtils.toLocale(str)); + } catch (final IllegalArgumentException e) { + return DataResult.error(e::toString); + } + }, Locale::toString); + public static Codec converter(final DynamicOps ops) { return ExtraCodecs.factory().converter(ops); } @@ -67,7 +87,15 @@ public static Codec idResolver( final IntFunction decoder, final int notFoundValue ) { - return ExtraCodecs.factory().idResolver(encoder, decoder, notFoundValue); + return Codec.INT.flatXmap( + id -> Optional.ofNullable(decoder.apply(id)) + .map(DataResult::success) + .orElseGet(() -> DataResult.error(() -> "Unknown element id: " + id)), + value -> { + int i = encoder.applyAsInt(value); + return i == notFoundValue ? DataResult.error(() -> "Element with unknown id: " + value) : DataResult.success(i); + } + ); } public static Codec idResolver( @@ -75,7 +103,13 @@ public static Codec idResolver( final Function idToValue, final Function valueToId ) { - return ExtraCodecs.factory().idResolver(idCodec, idToValue, valueToId); + return idCodec.flatXmap(id -> { + E e = idToValue.apply(id); + return e == null ? DataResult.error(() -> "Unknown element id: " + id) : DataResult.success(e); + }, value -> { + I i = valueToId.apply(value); + return i == null ? DataResult.error(() -> "Element with unknown id: " + value) : DataResult.success(i); + }); } public static Codec idResolver(final Codec idCodec, final Index index) { @@ -179,7 +213,7 @@ public static Codec casted(final Codec codec) { @SuppressWarnings("unchecked") final T to = (T) from; return DataResult.success(to); - } catch (ClassCastException e) { + } catch (final ClassCastException e) { return DataResult.error(e::toString); } }, @@ -188,7 +222,7 @@ public static Codec casted(final Codec codec) { @SuppressWarnings("unchecked") final F from = (F) to; return DataResult.success(from); - } catch (ClassCastException e) { + } catch (final ClassCastException e) { return DataResult.error(e::toString); } }); @@ -233,17 +267,6 @@ Codec interval( BiFunction> factory, Function minGetter, Function maxGetter ); - Codec idResolver( - ToIntFunction encoder, - IntFunction decoder, - int notFoundValue); - - Codec idResolver( - Codec idCodec, - Function idToValue, - Function valueToId - ); - Codec orCompressed(Codec first, Codec second); MapCodec orCompressed(MapCodec first, MapCodec second); diff --git a/api/src/main/java/net/hellheim/spongetools/codec/list/MathCodecs.java b/api/src/main/java/net/hellheim/spongetools/codec/list/MathCodecs.java new file mode 100644 index 0000000..84f79a5 --- /dev/null +++ b/api/src/main/java/net/hellheim/spongetools/codec/list/MathCodecs.java @@ -0,0 +1,153 @@ +package net.hellheim.spongetools.codec.list; + +import java.util.List; + +import org.spongepowered.api.util.Transform; +import org.spongepowered.math.imaginary.Complexd; +import org.spongepowered.math.imaginary.Complexf; +import org.spongepowered.math.imaginary.Quaterniond; +import org.spongepowered.math.imaginary.Quaternionf; +import org.spongepowered.math.matrix.Matrix2d; +import org.spongepowered.math.matrix.Matrix2f; +import org.spongepowered.math.matrix.Matrix3d; +import org.spongepowered.math.matrix.Matrix3f; +import org.spongepowered.math.matrix.Matrix4d; +import org.spongepowered.math.matrix.Matrix4f; +import org.spongepowered.math.vector.Vector2d; +import org.spongepowered.math.vector.Vector2f; +import org.spongepowered.math.vector.Vector2i; +import org.spongepowered.math.vector.Vector2l; +import org.spongepowered.math.vector.Vector3d; +import org.spongepowered.math.vector.Vector3f; +import org.spongepowered.math.vector.Vector3i; +import org.spongepowered.math.vector.Vector3l; +import org.spongepowered.math.vector.Vector4d; +import org.spongepowered.math.vector.Vector4f; +import org.spongepowered.math.vector.Vector4i; +import org.spongepowered.math.vector.Vector4l; + +import com.mojang.serialization.Codec; +import com.mojang.serialization.codecs.RecordCodecBuilder; + +public final class MathCodecs { + + public static final Codec VECTOR2I = list(Codec.INT, 2).xmap( + l -> new Vector2i(l.get(0), l.get(1)), v -> List.of(v.x(), v.y())); + public static final Codec VECTOR3I = list(Codec.INT, 3).xmap( + l -> new Vector3i(l.get(0), l.get(1), l.get(2)), v -> List.of(v.x(), v.y(), v.z())); + public static final Codec VECTOR4I = list(Codec.INT, 4).xmap( + l -> new Vector4i(l.get(0), l.get(1), l.get(2), l.get(3)), v -> List.of(v.x(), v.y(), v.z(), v.w())); + + public static final Codec VECTOR2L = list(Codec.LONG, 2).xmap( + l -> new Vector2l(l.get(0), l.get(1)), v -> List.of(v.x(), v.y())); + public static final Codec VECTOR3L = list(Codec.LONG, 3).xmap( + l -> new Vector3l(l.get(0), l.get(1), l.get(2)), v -> List.of(v.x(), v.y(), v.z())); + public static final Codec VECTOR4L = list(Codec.LONG, 4).xmap( + l -> new Vector4l(l.get(0), l.get(1), l.get(2), l.get(3)), v -> List.of(v.x(), v.y(), v.z(), v.w())); + + public static final Codec VECTOR2F = list(Codec.FLOAT, 2).xmap( + l -> new Vector2f(l.get(0), l.get(1)), v -> List.of(v.x(), v.y())); + public static final Codec VECTOR3F = list(Codec.FLOAT, 3).xmap( + l -> new Vector3f(l.get(0), l.get(1), l.get(2)), v -> List.of(v.x(), v.y(), v.z())); + public static final Codec VECTOR4F = list(Codec.FLOAT, 4).xmap( + l -> new Vector4f(l.get(0), l.get(1), l.get(2), l.get(3)), v -> List.of(v.x(), v.y(), v.z(), v.w())); + + public static final Codec VECTOR2D = list(Codec.DOUBLE, 2).xmap( + l -> new Vector2d(l.get(0), l.get(1)), v -> List.of(v.x(), v.y())); + public static final Codec VECTOR3D = list(Codec.DOUBLE, 3).xmap( + l -> new Vector3d(l.get(0), l.get(1), l.get(2)), v -> List.of(v.x(), v.y(), v.z())); + public static final Codec VECTOR4D = list(Codec.DOUBLE, 4).xmap( + l -> new Vector4d(l.get(0), l.get(1), l.get(2), l.get(3)), v -> List.of(v.x(), v.y(), v.z(), v.w())); + + public static final Codec MATRIX2F = list(Codec.FLOAT, 4).xmap( + l -> Matrix2f.from( + l.get(0), l.get(1), + l.get(2), l.get(3) + ), + m -> List.of( + m.get(0, 0), m.get(0, 1), + m.get(1, 0), m.get(1, 1) + )); + public static final Codec MATRIX3F = list(Codec.FLOAT, 9).xmap( + l -> Matrix3f.from( + l.get(0), l.get(1), l.get(2), + l.get(3), l.get(4), l.get(5), + l.get(6), l.get(7), l.get(9) + ), + m -> List.of( + m.get(0, 0), m.get(0, 1), m.get(0, 2), + m.get(1, 0), m.get(1, 1), m.get(1, 2), + m.get(2, 0), m.get(2, 1), m.get(2, 2) + )); + public static final Codec MATRIX4F = list(Codec.FLOAT, 16).xmap( + l -> Matrix4f.from( + l.get(0), l.get(1), l.get(2), l.get(3), + l.get(4), l.get(5), l.get(6), l.get(7), + l.get(8), l.get(9), l.get(10), l.get(11), + l.get(12), l.get(13), l.get(14), l.get(15) + ), + m -> List.of( + m.get(0, 0), m.get(0, 1), m.get(0, 2), m.get(0, 3), + m.get(1, 0), m.get(1, 1), m.get(1, 2), m.get(1, 3), + m.get(2, 0), m.get(2, 1), m.get(2, 2), m.get(2, 3), + m.get(3, 0), m.get(3, 1), m.get(3, 2), m.get(3, 3) + )); + + public static final Codec MATRIX2D = list(Codec.DOUBLE, 4).xmap( + l -> Matrix2d.from( + l.get(0), l.get(1), + l.get(2), l.get(3) + ), + m -> List.of( + m.get(0, 0), m.get(0, 1), + m.get(1, 0), m.get(1, 1) + )); + public static final Codec MATRIX3D = list(Codec.DOUBLE, 9).xmap( + l -> Matrix3d.from( + l.get(0), l.get(1), l.get(2), + l.get(3), l.get(4), l.get(5), + l.get(6), l.get(7), l.get(9) + ), + m -> List.of( + m.get(0, 0), m.get(0, 1), m.get(0, 2), + m.get(1, 0), m.get(1, 1), m.get(1, 2), + m.get(2, 0), m.get(2, 1), m.get(2, 2) + )); + public static final Codec MATRIX4D = list(Codec.DOUBLE, 16).xmap( + l -> Matrix4d.from( + l.get(0), l.get(1), l.get(2), l.get(3), + l.get(4), l.get(5), l.get(6), l.get(7), + l.get(8), l.get(9), l.get(10), l.get(11), + l.get(12), l.get(13), l.get(14), l.get(15) + ), + m -> List.of( + m.get(0, 0), m.get(0, 1), m.get(0, 2), m.get(0, 3), + m.get(1, 0), m.get(1, 1), m.get(1, 2), m.get(1, 3), + m.get(2, 0), m.get(2, 1), m.get(2, 2), m.get(2, 3), + m.get(3, 0), m.get(3, 1), m.get(3, 2), m.get(3, 3) + )); + + public static final Codec COMPLEXF = list(Codec.FLOAT, 2).xmap( + l -> Complexf.from(l.get(0), l.get(1)), c -> List.of(c.x(), c.y())); + public static final Codec COMPLEXD = list(Codec.DOUBLE, 2).xmap( + l -> Complexd.from(l.get(0), l.get(1)), c -> List.of(c.x(), c.y())); + + public static final Codec QUATERNIONF = list(Codec.FLOAT, 4).xmap( + l -> Quaternionf.from(l.get(0), l.get(1), l.get(2), l.get(3)), c -> List.of(c.x(), c.y(), c.z(), c.w())); + public static final Codec QUATERNIOND = list(Codec.DOUBLE, 4).xmap( + l -> Quaterniond.from(l.get(0), l.get(1), l.get(2), l.get(3)), c -> List.of(c.x(), c.y(), c.z(), c.w())); + + public static final Codec TRANSFORM = RecordCodecBuilder.create( + instance -> instance.group( + MathCodecs.VECTOR3D.optionalFieldOf("translation", Vector3d.ZERO).forGetter(Transform::position), + MathCodecs.VECTOR3D.optionalFieldOf("rotation", Vector3d.ZERO).forGetter(Transform::rotation), + MathCodecs.VECTOR3D.optionalFieldOf("scale", Vector3d.ONE).forGetter(Transform::scale) + ).apply(instance, Transform::of)); + + private static Codec> list(final Codec codec, final int size) { + return ExtraCodecs.sizeLimitedCollection(codec.listOf(), size); + } + + private MathCodecs() { + } +} diff --git a/src/main/java/net/hellheim/spongetools/codec/RegistryCodecs.java b/api/src/main/java/net/hellheim/spongetools/codec/list/RegistryCodecs.java similarity index 92% rename from src/main/java/net/hellheim/spongetools/codec/RegistryCodecs.java rename to api/src/main/java/net/hellheim/spongetools/codec/list/RegistryCodecs.java index c455a67..8e3ab9b 100644 --- a/src/main/java/net/hellheim/spongetools/codec/RegistryCodecs.java +++ b/api/src/main/java/net/hellheim/spongetools/codec/list/RegistryCodecs.java @@ -1,4 +1,4 @@ -package net.hellheim.spongetools.codec; +package net.hellheim.spongetools.codec.list; import org.spongepowered.api.Sponge; import org.spongepowered.api.advancement.Advancement; @@ -25,7 +25,6 @@ import org.spongepowered.api.data.type.BambooLeavesType; import org.spongepowered.api.data.type.BannerPatternShape; import org.spongepowered.api.data.type.BellAttachmentType; -import org.spongepowered.api.data.type.BoatType; import org.spongepowered.api.data.type.BodyPart; import org.spongepowered.api.data.type.CatType; import org.spongepowered.api.data.type.ChestAttachmentType; @@ -76,7 +75,6 @@ import org.spongepowered.api.effect.potion.PotionEffectType; import org.spongepowered.api.effect.sound.SoundType; import org.spongepowered.api.effect.sound.music.MusicDisc; -import org.spongepowered.api.entity.Entity; import org.spongepowered.api.entity.EntityCategory; import org.spongepowered.api.entity.EntityType; import org.spongepowered.api.entity.ai.goal.GoalExecutorType; @@ -93,8 +91,8 @@ import org.spongepowered.api.event.cause.entity.MovementType; import org.spongepowered.api.event.cause.entity.SpawnType; import org.spongepowered.api.event.cause.entity.damage.DamageEffect; -import org.spongepowered.api.event.cause.entity.damage.DamageModifierType; import org.spongepowered.api.event.cause.entity.damage.DamageScaling; +import org.spongepowered.api.event.cause.entity.damage.DamageStepType; import org.spongepowered.api.event.cause.entity.damage.DamageType; import org.spongepowered.api.fluid.FluidType; import org.spongepowered.api.item.FireworkShape; @@ -170,12 +168,43 @@ import org.spongepowered.api.world.weather.WeatherType; import com.mojang.serialization.Codec; +import com.mojang.serialization.MapCodec; + +import net.hellheim.spongetools.SpongeTools; +import net.hellheim.spongetools.custom.type.block.BlockTypeArchetype; +import net.hellheim.spongetools.custom.type.item.CustomItemAction; +import net.hellheim.spongetools.custom.type.item.ItemTypeArchetype; +import net.hellheim.spongetools.custom.type.item.LoreProcessor; +import net.hellheim.spongetools.custom.type.item.LoreProvider; +import net.hellheim.spongetools.resourcepack.Model; +import net.hellheim.spongetools.resourcepack.block.BlockDefinition; +import net.hellheim.spongetools.resourcepack.item.ItemDefinition; /** * Codecs for all {@link RegistryType}s provided by SpongeAPI. */ public final class RegistryCodecs { + // SpongeToolsAPI + + public static final Codec BLOCK_ARCHETYPE = RegistryCodecs.register(BlockTypeArchetype.class, BlockTypeArchetype.registry()); + + public static final Codec ITEM_ARCHETYPE = RegistryCodecs.register(ItemTypeArchetype.class, ItemTypeArchetype.registry()); + + public static final Codec ITEM_DEFINITION = RegistryCodecs.register(ItemDefinition.class, ItemDefinition.registry()); + + public static final Codec BLOCK_DEFINITION = RegistryCodecs.register(BlockDefinition.class, BlockDefinition.registry()); + + public static final Codec MODEL = RegistryCodecs.register(Model.class, Model.registry()); + + public static final Codec> LORE_PROCESSOR_TYPE = RegistryCodecs.of(SpongeTools.Registries.LORE_PROCESSOR_TYPE); + + public static final Codec> LORE_PROVIDER_TYPE = RegistryCodecs.of(SpongeTools.Registries.LORE_PROVIDER_TYPE); + + public static final Codec> CUSTOM_CONSUME_EFFECT_TYPE = RegistryCodecs.of(SpongeTools.Registries.ITEM_ACTION_CONFIG_TYPE); + + // SpongeAPI + public static final Codec ADVANCEMENT = RegistryCodecs.register(Advancement.class, RegistryTypes.ADVANCEMENT); public static final Codec ART_TYPE = RegistryCodecs.register(ArtType.class, RegistryTypes.ART_TYPE); @@ -208,7 +237,7 @@ public final class RegistryCodecs { public static final Codec ENTITY_CATEGORY = RegistryCodecs.register(EntityCategory.class, RegistryTypes.ENTITY_CATEGORY); - public static final Codec> ENTITY_TYPE = RegistryCodecs.register(EntityType.class, RegistryTypes.ENTITY_TYPE); + public static final Codec> ENTITY_TYPE = RegistryCodecs.register(EntityType.class, RegistryTypes.ENTITY_TYPE); public static final Codec FEATURE = RegistryCodecs.register(Feature.class, RegistryTypes.FEATURE); @@ -242,7 +271,7 @@ public final class RegistryCodecs { public static final Codec POTION_TYPE = RegistryCodecs.register(PotionType.class, RegistryTypes.POTION_TYPE); - public static final Codec>> RECIPE_TYPE = RegistryCodecs.register(RecipeType.class, RegistryTypes.RECIPE_TYPE); + public static final Codec> RECIPE_TYPE = RegistryCodecs.register(RecipeType.class, RegistryTypes.RECIPE_TYPE); public static final Codec SOUND_TYPE = RegistryCodecs.register(SoundType.class, RegistryTypes.SOUND_TYPE); @@ -256,7 +285,7 @@ public final class RegistryCodecs { public static final Codec STRUCTURE_TYPE = RegistryCodecs.register(StructureType.class, RegistryTypes.STRUCTURE_TYPE); - public static final Codec> TRIGGER = RegistryCodecs.register(Trigger.class, RegistryTypes.TRIGGER); + public static final Codec> TRIGGER = RegistryCodecs.register(Trigger.class, RegistryTypes.TRIGGER); public static final Codec TRIM_MATERIAL = RegistryCodecs.register(TrimMaterial.class, RegistryTypes.TRIM_MATERIAL); @@ -286,8 +315,6 @@ public final class RegistryCodecs { public static final Codec BILLBOARD_TYPE = RegistryCodecs.register(BillboardType.class, RegistryTypes.BILLBOARD_TYPE); - public static final Codec BOAT_TYPE = RegistryCodecs.register(BoatType.class, RegistryTypes.BOAT_TYPE); - public static final Codec BODY_PART = RegistryCodecs.register(BodyPart.class, RegistryTypes.BODY_PART); public static final Codec CAT_TYPE = RegistryCodecs.register(CatType.class, RegistryTypes.CAT_TYPE); @@ -308,7 +335,7 @@ public final class RegistryCodecs { public static final Codec> COMMAND_REGISTRAR_TYPE = RegistryCodecs.register(CommandRegistrarType.class, RegistryTypes.COMMAND_REGISTRAR_TYPE); - public static final Codec> COMMAND_TREE_NODE_TYPE = RegistryCodecs.register(CommandTreeNodeType.class, RegistryTypes.COMMAND_TREE_NODE_TYPE); + public static final Codec> COMMAND_TREE_NODE_TYPE = RegistryCodecs.register(CommandTreeNodeType.class, RegistryTypes.COMMAND_TREE_NODE_TYPE); public static final Codec COMPARATOR_MODE = RegistryCodecs.register(ComparatorMode.class, RegistryTypes.COMPARATOR_MODE); @@ -316,7 +343,7 @@ public final class RegistryCodecs { public static final Codec CURRENCY = RegistryCodecs.register(Currency.class, RegistryTypes.CURRENCY); - public static final Codec DAMAGE_MODIFIER_TYPE = RegistryCodecs.register(DamageModifierType.class, RegistryTypes.DAMAGE_MODIFIER_TYPE); + public static final Codec DAMAGE_STEP_TYPE = RegistryCodecs.register(DamageStepType.class, RegistryTypes.DAMAGE_STEP_TYPE); public static final Codec DAMAGE_TYPE = RegistryCodecs.register(DamageType.class, RegistryTypes.DAMAGE_TYPE); @@ -500,7 +527,7 @@ public final class RegistryCodecs { public static final Codec WEATHER_TYPE = RegistryCodecs.register(WeatherType.class, RegistryTypes.WEATHER_TYPE); - public static final Codec WOLF_VARIANT = RegistryCodecs.register(WolfVariant.class, RegistryTypes.WOLF_VAIRANT); + public static final Codec WOLF_VARIANT = RegistryCodecs.register(WolfVariant.class, RegistryTypes.WOLF_VARIANT); public static final Codec WORLD_ARCHETYPE_TYPE = RegistryCodecs.register(WorldArchetypeType.class, RegistryTypes.WORLD_ARCHETYPE_TYPE); diff --git a/src/main/java/net/hellheim/spongetools/codec/SpongeCodecs.java b/api/src/main/java/net/hellheim/spongetools/codec/list/SpongeCodecs.java similarity index 78% rename from src/main/java/net/hellheim/spongetools/codec/SpongeCodecs.java rename to api/src/main/java/net/hellheim/spongetools/codec/list/SpongeCodecs.java index 5848d17..0647168 100644 --- a/src/main/java/net/hellheim/spongetools/codec/SpongeCodecs.java +++ b/api/src/main/java/net/hellheim/spongetools/codec/list/SpongeCodecs.java @@ -1,4 +1,4 @@ -package net.hellheim.spongetools.codec; +package net.hellheim.spongetools.codec.list; import java.util.List; import java.util.function.Function; @@ -7,8 +7,10 @@ import org.spongepowered.api.ResourceKeyed; import org.spongepowered.api.registry.RegistryKey; import org.spongepowered.api.registry.RegistryType; +import org.spongepowered.api.util.Axis; import org.spongepowered.api.util.Direction; import org.spongepowered.api.util.Ticks; +import org.spongepowered.api.util.rotation.Rotation; import org.spongepowered.api.util.weighted.RandomObjectTable; import org.spongepowered.api.util.weighted.TableEntry; import org.spongepowered.api.util.weighted.VariableAmount; @@ -21,8 +23,10 @@ import com.mojang.serialization.codecs.RecordCodecBuilder.Instance; import com.mojang.serialization.codecs.RecordCodecBuilder.Mu; +import net.hellheim.spongetools.codec.StringRepresentableCodec; import net.hellheim.spongetools.codec.dispatched.TableEntryCodecs; import net.hellheim.spongetools.codec.dispatched.VariableAmountCodecs; +import net.hellheim.spongetools.util.GeomUtil; /** * Codecs for SpongeAPI types @@ -35,6 +39,36 @@ public final class SpongeCodecs { public static final Codec VARIABLE_AMOUNT = VariableAmountCodecs.CODEC; + public static final Codec AXIS = StringRepresentableCodec.fromValues(Axis::values); + + public static final Codec ROTATION_BY_ANGLE = Codec.INT.flatXmap( + angle -> { + if (angle == 0) { + return DataResult.success(GeomUtil.ROT_0.get()); + } else if (angle == 90) { + return DataResult.success(GeomUtil.ROT_90.get()); + } else if (angle == 180) { + return DataResult.success(GeomUtil.ROT_180.get()); + } else if (angle == 270) { + return DataResult.success(GeomUtil.ROT_270.get()); + } else { + return DataResult.error(() -> "Rotation angle must be 0, 90, 180 or 270: " + angle); + } + }, + rotation -> { + if (GeomUtil.is0(rotation)) { + return DataResult.success(0); + } else if (GeomUtil.is90(rotation)) { + return DataResult.success(90); + } else if (GeomUtil.is180(rotation)) { + return DataResult.success(180); + } else if (GeomUtil.is270(rotation)) { + return DataResult.success(270); + } else { + return DataResult.error(() -> "Unknown rotation: " + rotation); + } + }); + public static final Codec DIRECTION = StringRepresentableCodec.fromValues(Direction::values); public static final Codec CARDINAL_DIRECTION = SpongeCodecs.DIRECTION.validate(dir -> dir.isCardinal() diff --git a/src/main/java/net/hellheim/spongetools/codec/StringRepresentableCodecs.java b/api/src/main/java/net/hellheim/spongetools/codec/list/StringRepresentableCodecs.java similarity index 72% rename from src/main/java/net/hellheim/spongetools/codec/StringRepresentableCodecs.java rename to api/src/main/java/net/hellheim/spongetools/codec/list/StringRepresentableCodecs.java index 40c80be..844012d 100644 --- a/src/main/java/net/hellheim/spongetools/codec/StringRepresentableCodecs.java +++ b/api/src/main/java/net/hellheim/spongetools/codec/list/StringRepresentableCodecs.java @@ -1,8 +1,9 @@ -package net.hellheim.spongetools.codec; +package net.hellheim.spongetools.codec.list; import org.spongepowered.api.Sponge; import org.spongepowered.api.data.type.DyeColor; import org.spongepowered.api.data.type.HandPreference; +import org.spongepowered.api.entity.display.ItemDisplayType; import com.mojang.serialization.Codec; @@ -12,6 +13,8 @@ public final class StringRepresentableCodecs { public static final Codec HAND_PREFERENCE = StringRepresentableCodecs.factory().handPreference(); + public static final Codec ITEM_DISPLAY_TYPE = StringRepresentableCodecs.factory().itemDisplayType(); + private static Factory factory() { return Sponge.game().factoryProvider().provide(Factory.class); } @@ -21,6 +24,8 @@ public static interface Factory { Codec dyeColor(); Codec handPreference(); + + Codec itemDisplayType(); } private StringRepresentableCodecs() { diff --git a/src/main/java/net/hellheim/spongetools/codec/TypeCodecs.java b/api/src/main/java/net/hellheim/spongetools/codec/list/TypeCodecs.java similarity index 98% rename from src/main/java/net/hellheim/spongetools/codec/TypeCodecs.java rename to api/src/main/java/net/hellheim/spongetools/codec/list/TypeCodecs.java index ce9bf6f..0ed7637 100644 --- a/src/main/java/net/hellheim/spongetools/codec/TypeCodecs.java +++ b/api/src/main/java/net/hellheim/spongetools/codec/list/TypeCodecs.java @@ -1,4 +1,4 @@ -package net.hellheim.spongetools.codec; +package net.hellheim.spongetools.codec.list; import java.lang.reflect.GenericArrayType; import java.lang.reflect.ParameterizedType; @@ -18,6 +18,8 @@ import com.mojang.serialization.Codec; +import net.hellheim.spongetools.codec.CodecMapper; + public final class TypeCodecs { private static final Map, Predicate>> CLASS_MAPPER = new HashMap<>(); diff --git a/src/main/java/net/hellheim/spongetools/collection/ComponentList.java b/api/src/main/java/net/hellheim/spongetools/collection/ComponentList.java similarity index 100% rename from src/main/java/net/hellheim/spongetools/collection/ComponentList.java rename to api/src/main/java/net/hellheim/spongetools/collection/ComponentList.java diff --git a/src/main/java/net/hellheim/spongetools/collection/StringList.java b/api/src/main/java/net/hellheim/spongetools/collection/StringList.java similarity index 100% rename from src/main/java/net/hellheim/spongetools/collection/StringList.java rename to api/src/main/java/net/hellheim/spongetools/collection/StringList.java diff --git a/src/main/java/net/hellheim/spongetools/collection/TupleList.java b/api/src/main/java/net/hellheim/spongetools/collection/TupleList.java similarity index 100% rename from src/main/java/net/hellheim/spongetools/collection/TupleList.java rename to api/src/main/java/net/hellheim/spongetools/collection/TupleList.java diff --git a/src/main/java/net/hellheim/spongetools/collection/TupleMap.java b/api/src/main/java/net/hellheim/spongetools/collection/TupleMap.java similarity index 100% rename from src/main/java/net/hellheim/spongetools/collection/TupleMap.java rename to api/src/main/java/net/hellheim/spongetools/collection/TupleMap.java diff --git a/src/main/java/net/hellheim/spongetools/collection/UnmodifiableDeque.java b/api/src/main/java/net/hellheim/spongetools/collection/UnmodifiableDeque.java similarity index 100% rename from src/main/java/net/hellheim/spongetools/collection/UnmodifiableDeque.java rename to api/src/main/java/net/hellheim/spongetools/collection/UnmodifiableDeque.java diff --git a/src/main/java/net/hellheim/spongetools/collection/UnmodifiableIterator.java b/api/src/main/java/net/hellheim/spongetools/collection/UnmodifiableIterator.java similarity index 100% rename from src/main/java/net/hellheim/spongetools/collection/UnmodifiableIterator.java rename to api/src/main/java/net/hellheim/spongetools/collection/UnmodifiableIterator.java diff --git a/api/src/main/java/net/hellheim/spongetools/custom/behaviour/Behaviour.java b/api/src/main/java/net/hellheim/spongetools/custom/behaviour/Behaviour.java new file mode 100644 index 0000000..11398d1 --- /dev/null +++ b/api/src/main/java/net/hellheim/spongetools/custom/behaviour/Behaviour.java @@ -0,0 +1,122 @@ +package net.hellheim.spongetools.custom.behaviour; + +import java.util.function.BooleanSupplier; +import java.util.function.DoubleSupplier; +import java.util.function.Function; +import java.util.function.IntSupplier; +import java.util.function.LongSupplier; +import java.util.function.Supplier; + +/** + * Represents some action a {@link BehaviourHolder} can do. + * + * @param Return type + * @param Behaviour arguments + */ +@FunctionalInterface +public interface Behaviour extends Function { + + R call(A args); + + @Override + default R apply(final A args) { + return this.call(args); + } + + @FunctionalInterface + interface SimpleAction extends Behaviour, Runnable { + + @Override + default Void call(final BehaviourArgs args) { + this.call(); + return null; + } + + @Override + default void run() { + this.call(); + } + + void call(); + } + + @FunctionalInterface + interface SimpleBoolean extends Behaviour, BooleanSupplier { + + @Override + default Boolean call(final BehaviourArgs args) { + return this.call(); + } + + @Override + default boolean getAsBoolean() { + return this.call(); + } + + boolean call(); + } + + @FunctionalInterface + interface SimpleInteger extends Behaviour, IntSupplier { + + @Override + default Integer call(final BehaviourArgs args) { + return this.call(); + } + + @Override + default int getAsInt() { + return this.call(); + }; + + int call(); + } + + @FunctionalInterface + interface SimpleLong extends Behaviour, LongSupplier { + + @Override + default Long call(final BehaviourArgs args) { + return this.call(); + } + + @Override + default long getAsLong() { + return this.call(); + }; + + long call(); + } + + @FunctionalInterface + interface SimpleDouble extends Behaviour, DoubleSupplier { + + @Override + default Double call(final BehaviourArgs args) { + return this.call(); + } + + @Override + default double getAsDouble() { + return this.call(); + } + + double call(); + } + + @FunctionalInterface + interface SimpleObject extends Behaviour, Supplier { + + @Override + default V call(final BehaviourArgs args) { + return this.call(); + } + + @Override + default V get() { + return this.call(); + } + + V call(); + } +} diff --git a/api/src/main/java/net/hellheim/spongetools/custom/behaviour/BehaviourArgs.java b/api/src/main/java/net/hellheim/spongetools/custom/behaviour/BehaviourArgs.java new file mode 100644 index 0000000..8c83fe0 --- /dev/null +++ b/api/src/main/java/net/hellheim/spongetools/custom/behaviour/BehaviourArgs.java @@ -0,0 +1,86 @@ +package net.hellheim.spongetools.custom.behaviour; + +import java.util.Objects; +import java.util.function.Supplier; + +import org.spongepowered.api.util.Direction; +import org.spongepowered.api.util.RandomProvider; +import org.spongepowered.api.world.volume.Volume; +import org.spongepowered.math.vector.Vector3d; +import org.spongepowered.math.vector.Vector3i; + +import net.hellheim.spongetools.custom.behaviour.util.HitResult; + +public interface BehaviourArgs { + + interface Randomized> extends BehaviourArgs { + + RandomProvider.Source random(); + + A withRandom(RandomProvider.Source random); + + default A withRandom(final RandomProvider randomProvider) { + Objects.requireNonNull(randomProvider, "randomProvider"); + return this.withRandom(randomProvider.random()); + } + } + + interface Directional> extends BehaviourArgs { + + Direction direction(); + + A withDirection(Direction direction); + } + + interface Positional> extends BehaviourArgs { + + Vector3i position(); + + A withPosition(Vector3i position); + + default A withPosition(final Vector3d position) { + Objects.requireNonNull(position, "position"); + return this.withPosition(position.toInt()); + } + + default A withPosition(final int x, final int y, final int z) { + return this.withPosition(new Vector3i(x, y, z)); + } + + default A withPosition(final double x, final double y, final double z) { + return this.withPosition(new Vector3i(x, y, z)); + } + } + + interface Volumed> extends BehaviourArgs { + + V volume(); + + A withVolume(V volume); + } + + interface EntitySource> extends BehaviourArgs { + + E entity(); + + A withEntity(E entity); + + default A withEntity(final Supplier entitySupplier) { + return this.withEntity(Objects.requireNonNull(entitySupplier, "entitySupplier").get()); + } + } + + interface RayTraced> extends BehaviourArgs { + + H hit(); + + A withHit(H hit); + } + + interface Contextual> extends BehaviourArgs { + + C context(); + + A withContext(C context); + } +} diff --git a/api/src/main/java/net/hellheim/spongetools/custom/behaviour/BehaviourCallback.java b/api/src/main/java/net/hellheim/spongetools/custom/behaviour/BehaviourCallback.java new file mode 100644 index 0000000..c936cb5 --- /dev/null +++ b/api/src/main/java/net/hellheim/spongetools/custom/behaviour/BehaviourCallback.java @@ -0,0 +1,59 @@ +package net.hellheim.spongetools.custom.behaviour; + +import java.util.Objects; +import java.util.function.BiConsumer; +import java.util.function.Consumer; +import java.util.function.Function; +import java.util.function.Supplier; + +/** + * Callbacks are used to add additional behaviour on top of the default one. + * + * @param Behaviour holder + * @param Return type + * @param Behaviour arguments + */ +@FunctionalInterface +public interface BehaviourCallback { + + R call(H holder, Behaviour origin, A args); + + static BehaviourCallback of(final Function behaviour) { + Objects.requireNonNull(behaviour, "behaviour"); + return (holder, origin, args) -> behaviour.apply(args); + } + + static BehaviourCallback result(final Supplier result) { + Objects.requireNonNull(result, "result"); + return (holder, origin, args) -> result.get(); + } + + static BehaviourCallback result(final R result) { + Objects.requireNonNull(result, "result"); + return (holder, origin, args) -> result; + } + + static BehaviourCallback action(final BiConsumer action) { + Objects.requireNonNull(action, "action"); + return (holder, origin, args) -> { + action.accept(holder, args); + return null; + }; + } + + static BehaviourCallback action(final Consumer action) { + Objects.requireNonNull(action, "action"); + return (holder, origin, args) -> { + action.accept(args); + return null; + }; + } + + static BehaviourCallback action(final Runnable action) { + Objects.requireNonNull(action, "action"); + return (holder, origin, args) -> { + action.run(); + return null; + }; + } +} diff --git a/api/src/main/java/net/hellheim/spongetools/custom/behaviour/BehaviourCallbackHolder.java b/api/src/main/java/net/hellheim/spongetools/custom/behaviour/BehaviourCallbackHolder.java new file mode 100644 index 0000000..dc876fc --- /dev/null +++ b/api/src/main/java/net/hellheim/spongetools/custom/behaviour/BehaviourCallbackHolder.java @@ -0,0 +1,159 @@ +package net.hellheim.spongetools.custom.behaviour; + +import java.util.Objects; +import java.util.Optional; +import java.util.Set; +import java.util.function.BinaryOperator; +import java.util.function.Consumer; +import java.util.function.Function; + +import org.checkerframework.checker.nullness.qual.Nullable; + +public interface BehaviourCallbackHolder { + + Set> callbacks(); + + /** + * Returns the current behaviour callback for the given type, if present. + * + * @param Behaviour return type + * @param Behaviour arguments + * @param type The behaviour type + * @return The current behaviour callback for the given type, if present + */ + Optional> callback( + BehaviourLayer layer, BehaviourType> type); + + @SuppressWarnings("unchecked") + interface Mutable> extends BehaviourCallbackHolder { + + private M cast() { + return (M) this; + } + + /** + * Sets the given behaviour callback for the given type.
+ * Overrides any previously registered callbacks. + * + * @param Behaviour return type + * @param
Behaviour arguments + * @param type The behaviour type + * @param callback The behaviour callback + * @return This modifiable behaviour, for chaining + */ + M offer( + BehaviourLayer layer, BehaviourType> type, BehaviourCallback callback + ); + + + default M offer( + final BehaviourType> type, final BehaviourCallback callback + ) { + return this.offer(BehaviourLayer.TOP, type, callback); + } + + default M offer(final TypedBehaviourCallback callback) { + Objects.requireNonNull(callback, "callback"); + return this.offer(callback.layer(), callback.type(), callback.callback()); + } + + default M offerAll(final Iterable> callbacks) { + Objects.requireNonNull(callbacks, "callbacks").forEach(this::offer); + return this.cast(); + } + + default M offerFrom(final BehaviourCallbackHolder holder) { + return this.offerAll(Objects.requireNonNull(holder, "holder").callbacks()); + } + + default M transform( + final BehaviourLayer layer, + final BehaviourType> type, + final Function<@Nullable BehaviourCallback, BehaviourCallback> function + ) { + Objects.requireNonNull(function, "function"); + return this.offer(type, function.apply(this.callback(layer, type).orElse(null))); + } + + /** + * Wraps the given callback around the current callback.
+ * Means that the current callback becomes the "original" for the given one.
+ * If current callback is not present, simply sets the given callback. + * + * @param Behaviour return type + * @param
Behaviour arguments + * @param type The behaviour type + * @param callback The behaviour callback + * @return This modifiable behaviour, for chaining + */ + default M append( + final BehaviourLayer layer, + final BehaviourType> type, + final BehaviourCallback callback + ) { + Objects.requireNonNull(callback, "callback"); + return this.transform(layer, type, old -> old == null + ? callback + : (holder, origin, args) -> + callback.call(holder, (newArgs) -> old.call(holder, origin, newArgs), args)); + } + + + + default M appendAfter( + final BehaviourLayer layer, + final BehaviourType> type, + final Consumer action + ) { + Objects.requireNonNull(action, "action"); + return this.append(layer, type, (holder, origin, args) -> { + final R result = origin.call(args); + action.accept(args); + return result; + }); + } + + default M appendAfter( + final BehaviourLayer layer, + final BehaviourType> type, + final Function behaviour, + final BinaryOperator resultMerger + ) { + Objects.requireNonNull(behaviour, "behaviour"); + Objects.requireNonNull(resultMerger, "resultMerger"); + return this.append(layer, type, (holder, origin, args) -> { + final R oldResult = origin.call(args); + final R newResult = behaviour.apply(args); + return resultMerger.apply(oldResult, newResult); + }); + } + + default M appendBefore( + final BehaviourLayer layer, + final BehaviourType> type, + final Consumer action + ) { + Objects.requireNonNull(action, "action"); + return this.append(layer, type, (holder, origin, args) -> { + action.accept(args); + final R result = origin.call(args); + return result; + }); + } + + default M appendBefore( + final BehaviourLayer layer, + final BehaviourType> type, + final Function behaviour, + final BinaryOperator resultMerger + ) { + Objects.requireNonNull(behaviour, "behaviour"); + Objects.requireNonNull(resultMerger, "resultMerger"); + return this.append(layer, type, (holder, origin, args) -> { + final R newResult = behaviour.apply(args); + final R oldResult = origin.call(args); + return resultMerger.apply(oldResult, newResult); + }); + } + } +} diff --git a/api/src/main/java/net/hellheim/spongetools/custom/behaviour/BehaviourCallbackHolderLogic.java b/api/src/main/java/net/hellheim/spongetools/custom/behaviour/BehaviourCallbackHolderLogic.java new file mode 100644 index 0000000..0766aef --- /dev/null +++ b/api/src/main/java/net/hellheim/spongetools/custom/behaviour/BehaviourCallbackHolderLogic.java @@ -0,0 +1,161 @@ +package net.hellheim.spongetools.custom.behaviour; + +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.Set; + +import org.checkerframework.checker.nullness.qual.Nullable; + +public abstract class BehaviourCallbackHolderLogic implements BehaviourCallbackHolder { + + protected final Set> callbackSet; + protected final Map, TypedBehaviourCallback>> callbackMap; + + protected BehaviourCallbackHolderLogic( + final Set> callbackSet, + final Map, TypedBehaviourCallback>> callbackMap + ) { + this.callbackSet = callbackSet; + this.callbackMap = callbackMap; + } + + public static Mutable mutable() { + return new Mutable<>(); + } + + public static Immutable immutable() { + return new Immutable<>(); + } + + public static Mutable mutableOf(final Collection> callbacks) { + return new Mutable<>(callbacks); + } + + public static Immutable immutableOf(final Collection> callbacks) { + return new Immutable<>(callbacks); + } + + private static Map, TypedBehaviourCallback>> createMap( + final Collection> callbacks + ) { + final Map, TypedBehaviourCallback>> map = new HashMap<>(); + callbacks.forEach(typed -> { + map.computeIfAbsent(typed.layer(), $ -> new HashMap<>()).put(typed.type(), typed); + }); + return map; + } + + @Override + public Set> callbacks() { + return this.callbackSet; + } + + @Override + public Optional> callback( + final BehaviourLayer layer, final BehaviourType> type + ) { + return Optional.ofNullable(this.callbackOrNull(layer, type)); + } + + public @Nullable BehaviourCallback callbackOrNull( + final BehaviourLayer layer, final BehaviourType> type + ) { + Objects.requireNonNull(layer, "layer"); + Objects.requireNonNull(type, "type"); + final var layerCallbacks = this.callbackMap.get(layer); + if (layerCallbacks == null) { + return null; + } + + @SuppressWarnings("unchecked") + final var callback = (TypedBehaviourCallback) layerCallbacks.get(type); + return callback == null ? null : callback.callback(); + } + + public abstract Mutable asMutable(); + + public abstract Mutable asMutableCopy(); + + public abstract Immutable asImmutable(); + + public static class Mutable + extends BehaviourCallbackHolderLogic + implements BehaviourCallbackHolder.Mutable> { + + private Mutable() { + super(new HashSet<>(), new HashMap<>()); + } + + private Mutable(final Collection> callbacks) { + super(new HashSet<>(callbacks), BehaviourCallbackHolderLogic.createMap(callbacks)); + } + + @Override + public BehaviourCallbackHolderLogic.Mutable offer( + final BehaviourLayer layer, + final BehaviourType> type, + final BehaviourCallback callback + ) { + Objects.requireNonNull(layer, "layer"); + Objects.requireNonNull(type, "type"); + Objects.requireNonNull(callback, "callback"); + final TypedBehaviourCallback typed = TypedBehaviourCallback.of(type, callback); + this.callbackMap + .computeIfAbsent(layer, $ -> new HashMap<>()) + .put(type, typed); + this.callbackSet.add(typed); + return this; + } + + @Override + public BehaviourCallbackHolderLogic.Mutable asMutable() { + return this; + } + + @Override + public BehaviourCallbackHolderLogic.Mutable asMutableCopy() { + return BehaviourCallbackHolderLogic.mutableOf(this.callbackSet); + } + + @Override + public BehaviourCallbackHolderLogic.Immutable asImmutable() { + return BehaviourCallbackHolderLogic.immutableOf(this.callbackSet); + } + + public void clear() { + this.callbackSet.clear(); + this.callbackMap.clear(); + } + } + + public static class Immutable + extends BehaviourCallbackHolderLogic { + + private Immutable() { + super(Set.of(), Map.of()); + } + + private Immutable(final Collection> callbacks) { + super(Set.copyOf(callbacks), Map.copyOf(BehaviourCallbackHolderLogic.createMap(callbacks))); + } + + @Override + public BehaviourCallbackHolderLogic.Mutable asMutable() { + return BehaviourCallbackHolderLogic.mutableOf(this.callbackSet); + } + + @Override + public BehaviourCallbackHolderLogic.Mutable asMutableCopy() { + return this.asMutable(); + } + + @Override + public BehaviourCallbackHolderLogic.Immutable asImmutable() { + return this; + } + } +} diff --git a/api/src/main/java/net/hellheim/spongetools/custom/behaviour/BehaviourCallbackHolderProxy.java b/api/src/main/java/net/hellheim/spongetools/custom/behaviour/BehaviourCallbackHolderProxy.java new file mode 100644 index 0000000..64f74bf --- /dev/null +++ b/api/src/main/java/net/hellheim/spongetools/custom/behaviour/BehaviourCallbackHolderProxy.java @@ -0,0 +1,41 @@ +package net.hellheim.spongetools.custom.behaviour; + +import java.util.Optional; +import java.util.Set; + +public interface BehaviourCallbackHolderProxy extends BehaviourCallbackHolder { + + BehaviourCallbackHolder getAsBehaviourCallbackHolder(); + + @Override + default Set> callbacks() { + return this.getAsBehaviourCallbackHolder().callbacks(); + } + + @Override + default Optional> callback( + final BehaviourLayer layer, final BehaviourType> type + ) { + return this.getAsBehaviourCallbackHolder().callback(layer, type); + } + + interface Mutable> extends + BehaviourCallbackHolderProxy, + BehaviourCallbackHolder.Mutable { + + @Override + BehaviourCallbackHolder.Mutable getAsBehaviourCallbackHolder(); + + @Override + default M offer( + final BehaviourLayer layer, + final BehaviourType> type, + final BehaviourCallback callback + ) { + this.getAsBehaviourCallbackHolder().offer(layer, type, callback); + @SuppressWarnings("unchecked") + final M $this = (M) this; + return $this; + } + } +} diff --git a/api/src/main/java/net/hellheim/spongetools/custom/behaviour/BehaviourHolder.java b/api/src/main/java/net/hellheim/spongetools/custom/behaviour/BehaviourHolder.java new file mode 100644 index 0000000..866e9f1 --- /dev/null +++ b/api/src/main/java/net/hellheim/spongetools/custom/behaviour/BehaviourHolder.java @@ -0,0 +1,77 @@ +package net.hellheim.spongetools.custom.behaviour; + +import java.util.NoSuchElementException; +import java.util.Optional; + +/** + * Represents something that can have {@link Behaviour}s. + */ +public interface BehaviourHolder { + + /** + * Returns whether the given behaviour type is registered for this {@link BehaviourHolder}. + * + * @param type The behaviour type + * @return True if this holder supports the given type + */ + boolean supports(BehaviourType type); + + /** + * Returns the actual behaviour this {@link BehaviourHolder} has, if present. + * If no behaviour callbacks are registered, this would be the "vanilla" behaviour. + * + * @param type The behaviour type + * @return The behaviour, if present + */ + > Optional get(BehaviourType type); + + /** + * Returns the actual behaviour this {@link BehaviourHolder} has. + * If no custom behaviour is registered, this would be the "vanilla" behaviour. + * + * @param type The behaviour type + * @return The behaviour + * @throws NoSuchElementException if the behaviour is not present for this {@link BehaviourHolder} + */ + > B require(BehaviourType type); + + interface Defaulted extends BehaviourHolder { + + Object getAsActualBehaviourHolder(); + + @Override + default boolean supports(final BehaviourType type) { + return BehaviourManager.get().supports(this.getAsActualBehaviourHolder(), type); + } + + @Override + default > Optional get(final BehaviourType type) { + return BehaviourManager.get().get(this.getAsActualBehaviourHolder(), type); + } + + @Override + default > B require(final BehaviourType type) { + return BehaviourManager.get().require(this.getAsActualBehaviourHolder(), type); + } + } + + interface Proxy extends BehaviourHolder { + + BehaviourHolder getAsBehaviourHolder(); + + @Override + default boolean supports(final BehaviourType type) { + return this.getAsBehaviourHolder().supports(type); + } + + @Override + default > Optional get(final BehaviourType type) { + return this.getAsBehaviourHolder().get(type); + } + + @Override + default > B require(final BehaviourType type) { + return this.getAsBehaviourHolder().require(type); + } + } +} diff --git a/api/src/main/java/net/hellheim/spongetools/custom/behaviour/BehaviourLayer.java b/api/src/main/java/net/hellheim/spongetools/custom/behaviour/BehaviourLayer.java new file mode 100644 index 0000000..0ca188e --- /dev/null +++ b/api/src/main/java/net/hellheim/spongetools/custom/behaviour/BehaviourLayer.java @@ -0,0 +1,12 @@ +package net.hellheim.spongetools.custom.behaviour; + +public interface BehaviourLayer { + + BehaviourLayer TOP = new BehaviourLayer() { + + @Override + public String toString() { + return "BehaviourLayer#TOP"; + } + }; +} diff --git a/api/src/main/java/net/hellheim/spongetools/custom/behaviour/BehaviourManager.java b/api/src/main/java/net/hellheim/spongetools/custom/behaviour/BehaviourManager.java new file mode 100644 index 0000000..a6e40c0 --- /dev/null +++ b/api/src/main/java/net/hellheim/spongetools/custom/behaviour/BehaviourManager.java @@ -0,0 +1,39 @@ +package net.hellheim.spongetools.custom.behaviour; + +import java.util.NoSuchElementException; +import java.util.Optional; +import java.util.function.Function; + +import org.checkerframework.checker.nullness.qual.Nullable; +import org.spongepowered.api.Sponge; + +public interface BehaviourManager { + + static BehaviourManager get() { + return Sponge.game().factoryProvider().provide(BehaviourManager.class); + } + + boolean supports(H holder, BehaviourType type); + + > Optional get(H holder, BehaviourType type); + + default > B require(final H holder, final BehaviourType type) { + return this.get(holder, type) + .orElseThrow(() -> new NoSuchElementException(String.format( + "No behaviour of type %s is present for holder %s", + type, holder + ))); + } + + BehaviourRegistration registration(Class behaviourHolder); + + interface BehaviourRegistration { + + /** + * Registers behaviour provider + */ + > BehaviourRegistration register( + BehaviourType type, Function behaviourProvider + ); + } +} diff --git a/api/src/main/java/net/hellheim/spongetools/custom/behaviour/BehaviourType.java b/api/src/main/java/net/hellheim/spongetools/custom/behaviour/BehaviourType.java new file mode 100644 index 0000000..b641514 --- /dev/null +++ b/api/src/main/java/net/hellheim/spongetools/custom/behaviour/BehaviourType.java @@ -0,0 +1,17 @@ +package net.hellheim.spongetools.custom.behaviour; + +import java.util.Objects; + +import org.spongepowered.api.ResourceKey; +import org.spongepowered.api.ResourceKeyed; + +public record BehaviourType>(ResourceKey key) implements ResourceKeyed { + + public BehaviourType(final ResourceKey key) { + this.key = Objects.requireNonNull(key, "key"); + } + + public static > BehaviourType of(final ResourceKey key) { + return new BehaviourType<>(key); + } +} diff --git a/api/src/main/java/net/hellheim/spongetools/custom/behaviour/TypedBehaviourCallback.java b/api/src/main/java/net/hellheim/spongetools/custom/behaviour/TypedBehaviourCallback.java new file mode 100644 index 0000000..aa13e4c --- /dev/null +++ b/api/src/main/java/net/hellheim/spongetools/custom/behaviour/TypedBehaviourCallback.java @@ -0,0 +1,52 @@ +package net.hellheim.spongetools.custom.behaviour; + +import java.util.Objects; + +public record TypedBehaviourCallback( + BehaviourLayer layer, + BehaviourType> type, + BehaviourCallback callback + ) { + + public TypedBehaviourCallback( + final BehaviourLayer layer, + final BehaviourType> type, + final BehaviourCallback callback + ) { + this.layer = Objects.requireNonNull(layer, "layer"); + this.type = Objects.requireNonNull(type, "type"); + this.callback = Objects.requireNonNull(callback, "callback"); + } + + public static TypedBehaviourCallback of( + final BehaviourLayer layer, + final BehaviourType> type, + final BehaviourCallback callback + ) { + return new TypedBehaviourCallback<>(layer, type, callback); + } + + public static TypedBehaviourCallback of( + final BehaviourType> type, + final BehaviourCallback callback + ) { + return TypedBehaviourCallback.of(BehaviourLayer.TOP, type, callback); + } + + @Override + public final int hashCode() { + return Objects.hash(this.layer, this.type); + } + + @Override + public final boolean equals(final Object obj) { + if (this == obj) { + return true; + } else if (obj.getClass() != this.getClass()) { + return false; + } + + final TypedBehaviourCallback that = (TypedBehaviourCallback) obj; + return this.layer.equals(that.layer) && this.type.equals(that.type); + } +} diff --git a/api/src/main/java/net/hellheim/spongetools/custom/behaviour/type/BlockStateBehaviour.java b/api/src/main/java/net/hellheim/spongetools/custom/behaviour/type/BlockStateBehaviour.java new file mode 100644 index 0000000..158dda1 --- /dev/null +++ b/api/src/main/java/net/hellheim/spongetools/custom/behaviour/type/BlockStateBehaviour.java @@ -0,0 +1,346 @@ +package net.hellheim.spongetools.custom.behaviour.type; + +import java.util.Objects; +import java.util.Optional; +import java.util.function.BiConsumer; +import java.util.function.Supplier; + +import org.spongepowered.api.block.BlockState; +import org.spongepowered.api.block.BlockType; +import org.spongepowered.api.data.type.HandType; +import org.spongepowered.api.entity.living.player.Player; +import org.spongepowered.api.fluid.FluidType; +import org.spongepowered.api.item.inventory.ItemStack; +import org.spongepowered.api.item.inventory.ItemStackLike; +import org.spongepowered.api.util.Direction; +import org.spongepowered.api.util.RandomProvider; +import org.spongepowered.api.world.World; +import org.spongepowered.api.world.explosion.Explosion; +import org.spongepowered.api.world.server.ServerWorld; +import org.spongepowered.api.world.volume.Volume; +import org.spongepowered.api.world.volume.game.PrimitiveGameVolume; +import org.spongepowered.api.world.volume.game.Region; +import org.spongepowered.api.world.volume.game.UpdatableVolume; +import org.spongepowered.math.vector.Vector3i; + +import net.hellheim.spongetools.custom.behaviour.Behaviour; +import net.hellheim.spongetools.custom.behaviour.BehaviourArgs; +import net.hellheim.spongetools.custom.behaviour.util.HitResult; +import net.hellheim.spongetools.custom.behaviour.util.InteractionResult; +import net.hellheim.spongetools.custom.behaviour.util.SignalOrientation; +import net.hellheim.spongetools.custom.behaviour.util.UseContext; + +@FunctionalInterface +public interface BlockStateBehaviour extends Behaviour { + + @FunctionalInterface + interface Locatable extends BlockStateBehaviour> { + + @Override + default R call(final Args args) { + return this.call(args.volume(), args.position()); + } + + R call(V volume, Vector3i position); + + interface Args extends + BehaviourArgs.Volumed>, + BehaviourArgs.Positional> { + } + } + + @FunctionalInterface + interface LocatableEntity extends BlockStateBehaviour> { + + @Override + default R call(final Args args) { + return this.call(args.volume(), args.position(), args.entity()); + } + + R call(V volume, Vector3i position, E entity); + + interface Args extends + BehaviourArgs.Volumed>, + BehaviourArgs.Positional>, + BehaviourArgs.EntitySource>{ + } + } + + @FunctionalInterface + interface SignalPower extends BlockStateBehaviour { + + @Override + default Integer call(final Args args) { + return this.call(args.volume(), args.position(), args.direction()); + } + + int call(PrimitiveGameVolume volume, Vector3i position, Direction direction); + + interface Args extends + BehaviourArgs.Volumed, + BehaviourArgs.Positional, + BehaviourArgs.Directional { + } + } + + @FunctionalInterface + interface Tick extends BlockStateBehaviour { + + @Override + default Void call(final Args args) { + this.call(args.volume(), args.position(), args.random()); + return null; + } + + void call(ServerWorld volume, Vector3i position, RandomProvider.Source random); + + interface Args extends + BehaviourArgs.Volumed, + BehaviourArgs.Positional, + BehaviourArgs.Randomized { + } + } + + @FunctionalInterface + interface Replace extends BlockStateBehaviour { + + @Override + default Void call(final Args args) { + this.call(args.volume(), args.position(), args.otherState(), args.movedByPiston()); + return null; + } + + void call(World volume, Vector3i position, BlockState otherState, boolean movedByPiston); + + interface Args extends + BehaviourArgs.Volumed, Args>, + BehaviourArgs.Positional { + + BlockState otherState(); + + boolean movedByPiston(); + + Args withOtherState(BlockState otherState); + + Args withMovedByPiston(boolean movedByPiston); + } + } + + @FunctionalInterface + interface ExplosionHit extends BlockStateBehaviour { + + @Override + default Void call(final Args args) { + this.call(args.volume(), args.position(), args.explosion(), args.drop()); + return null; + } + + void call(ServerWorld volume, Vector3i position, Explosion explosion, BiConsumer drop); + + interface Args extends + BehaviourArgs.Volumed, + BehaviourArgs.Positional { + + Explosion explosion(); + + BiConsumer drop(); + + Args withExplosion(Explosion explosion); + + Args withDrop(BiConsumer drop); + } + } + + @FunctionalInterface + interface ShapeUpdate extends BlockStateBehaviour { + + @Override + default BlockState call(final Args args) { + return this.call(args.volume(), args.updates(), args.position(), args.direction(), args.neighbourPosition(), args.neighbourState(), args.random()); + } + + BlockState call( + Region volume, UpdatableVolume updates, Vector3i position, + Direction direction, Vector3i neighbourPosition, BlockState neighbourState, + RandomProvider.Source random + ); + + interface Args extends + BehaviourArgs.Volumed, Args>, + BehaviourArgs.Positional, + BehaviourArgs.Directional, + BehaviourArgs.Randomized { + + UpdatableVolume updates(); + + Vector3i neighbourPosition(); + + BlockState neighbourState(); + + Args withUpdates(UpdatableVolume updates); + + Args withNeighbourPosition(Vector3i neighbourPosition); + + Args withNeighbourState(BlockState neighbourState); + } + } + + @FunctionalInterface + interface SignalUpdate extends BlockStateBehaviour { + + @Override + default Void call(final Args args) { + this.call(args.volume(), args.position(), args.notifier(), args.orientation(), args.movedByPiston()); + return null; + } + + void call( + World volume, Vector3i position, BlockType notifier, + Optional orientation, boolean movedByPiston + ); + + interface Args extends + BehaviourArgs.Volumed, Args>, + BehaviourArgs.Positional { + + BlockType notifier(); + + Optional orientation(); + + boolean movedByPiston(); + + Args withNotifier(BlockType notifier); + + default Args withNotifier(Supplier notifierSupplier) { + return this.withNotifier(Objects.requireNonNull(notifierSupplier, "notifierSupplier").get()); + } + + Args withOrientation(Optional orientation); + + default Args withOrientation(final SignalOrientation orientation) { + return this.withOrientation(Optional.of(Objects.requireNonNull(orientation, "orientation"))); + } + + default Args withoutOrientation() { + return this.withOrientation(Optional.empty()); + } + + Args withMovedByPiston(boolean movedByPiston); + } + } + + @FunctionalInterface + interface UseWithItem extends BlockStateBehaviour { + + @Override + default InteractionResult call(final Args args) { + return this.call(args.volume(), args.entity(), args.hit(), args.hand(), args.item()); + } + + InteractionResult call(World volume, Player entity, HitResult.BlockHitResult hit, HandType hand, ItemStackLike item); + + interface Args extends + BehaviourArgs.Volumed, Args>, + BehaviourArgs.EntitySource, + BehaviourArgs.RayTraced { + + HandType hand(); + + ItemStack item(); + + Args withHand(HandType hand); + + default Args withHand(Supplier handSupplier) { + return this.withHand(Objects.requireNonNull(handSupplier, "handSupplier").get()); + } + + Args withItem(ItemStackLike item); + } + } + + @FunctionalInterface + interface UseWithoutItem extends BlockStateBehaviour { + + @Override + default InteractionResult call(final Args args) { + return this.call(args.volume(), args.entity(), args.hit()); + } + + InteractionResult call(World volume, Player entity, HitResult.BlockHitResult hit); + + interface Args extends + BehaviourArgs.Volumed, Args>, + BehaviourArgs.EntitySource, + BehaviourArgs.RayTraced { + } + } + + @FunctionalInterface + interface ReplaceableByFluid extends BlockStateBehaviour { + + @Override + default Boolean call(final Args args) { + return this.call(args.fluid()); + } + + default boolean call(final Supplier fluidSupplier) { + return this.call(Objects.requireNonNull(fluidSupplier, "fluidSupplier").get()); + } + + boolean call(FluidType fluid); + + interface Args extends BehaviourArgs { + + FluidType fluid(); + + Args withFluid(FluidType fluid); + + default Args withFluid(final Supplier fluidSupplier) { + return this.withFluid(Objects.requireNonNull(fluidSupplier, "fluidSupplier").get()); + } + } + } + + @FunctionalInterface + interface ReplaceableByBlock extends BlockStateBehaviour { + + @Override + default Boolean call(final Args args) { + return this.call(args.context()); + } + + boolean call(UseContext.BlockPlace context); + + interface Args extends + BehaviourArgs.Contextual { + } + } + + @FunctionalInterface + interface CloneItem extends BlockStateBehaviour { + + @Override + default ItemStack call(final Args args) { + return this.call(args.volume(), args.position(), args.data()); + } + + ItemStack call(Region volume, Vector3i position, boolean data); + + interface Args extends + BehaviourArgs.Volumed, Args>, + BehaviourArgs.Positional { + + boolean data(); + + Args withData(boolean data); + + default Args withData() { + return this.withData(true); + } + + default Args withoutData() { + return this.withData(false); + } + } + } +} diff --git a/api/src/main/java/net/hellheim/spongetools/custom/behaviour/type/BlockStateBehaviours.java b/api/src/main/java/net/hellheim/spongetools/custom/behaviour/type/BlockStateBehaviours.java new file mode 100644 index 0000000..45ebe34 --- /dev/null +++ b/api/src/main/java/net/hellheim/spongetools/custom/behaviour/type/BlockStateBehaviours.java @@ -0,0 +1,196 @@ +package net.hellheim.spongetools.custom.behaviour.type; + +import java.util.Optional; + +import org.spongepowered.api.block.BlockState; +import org.spongepowered.api.block.BlockType; +import org.spongepowered.api.block.BlockTypes; +import org.spongepowered.api.data.type.InstrumentType; +import org.spongepowered.api.data.type.PushReaction; +import org.spongepowered.api.effect.sound.SoundType; +import org.spongepowered.api.entity.Entity; +import org.spongepowered.api.entity.EntityType; +import org.spongepowered.api.entity.living.player.Player; +import org.spongepowered.api.fluid.FluidState; +import org.spongepowered.api.item.inventory.ItemStack; +import org.spongepowered.api.map.color.MapColorType; +import org.spongepowered.api.util.AABB; +import org.spongepowered.api.world.World; +import org.spongepowered.api.world.WorldLike; +import org.spongepowered.api.world.volume.game.PrimitiveGameVolume; +import org.spongepowered.api.world.volume.game.Region; +import org.spongepowered.api.world.volume.game.UpdatableVolume; + +import net.hellheim.spongetools.SpongeTools; +import net.hellheim.spongetools.custom.behaviour.Behaviour.SimpleBoolean; +import net.hellheim.spongetools.custom.behaviour.Behaviour.SimpleObject; +import net.hellheim.spongetools.custom.behaviour.BehaviourType; +import net.hellheim.spongetools.custom.behaviour.type.BlockStateBehaviour.CloneItem; +import net.hellheim.spongetools.custom.behaviour.type.BlockStateBehaviour.ExplosionHit; +import net.hellheim.spongetools.custom.behaviour.type.BlockStateBehaviour.Locatable; +import net.hellheim.spongetools.custom.behaviour.type.BlockStateBehaviour.LocatableEntity; +import net.hellheim.spongetools.custom.behaviour.type.BlockStateBehaviour.Replace; +import net.hellheim.spongetools.custom.behaviour.type.BlockStateBehaviour.ReplaceableByBlock; +import net.hellheim.spongetools.custom.behaviour.type.BlockStateBehaviour.ReplaceableByFluid; +import net.hellheim.spongetools.custom.behaviour.type.BlockStateBehaviour.ShapeUpdate; +import net.hellheim.spongetools.custom.behaviour.type.BlockStateBehaviour.SignalPower; +import net.hellheim.spongetools.custom.behaviour.type.BlockStateBehaviour.SignalUpdate; +import net.hellheim.spongetools.custom.behaviour.type.BlockStateBehaviour.Tick; +import net.hellheim.spongetools.custom.behaviour.type.BlockStateBehaviour.UseWithItem; +import net.hellheim.spongetools.custom.behaviour.type.BlockStateBehaviour.UseWithoutItem; + +public final class BlockStateBehaviours { + + public static final BehaviourType>> SPAWN_VALIDATOR = BehaviourType.of(SpongeTools.key("spawn_validator")); + + public static final BehaviourType> MAP_COLOR = BehaviourType.of(SpongeTools.key("map_color")); + + public static final BehaviourType> SIGNAL_CONDUCTOR = BehaviourType.of(SpongeTools.key("signal_conductor")); + + /** + * Signal that will power neighbour blocks. + */ + public static final BehaviourType DIRECT_SIGNAL = BehaviourType.of(SpongeTools.key("direct_signal")); + + /** + * Signal that will go through neighbour blocks.
+ * + * In vanilla this behaviour usually filters result of + * {@link BlockStateExtension#get(BehaviourType)} + * for {@link #DIRECT_SIGNAL} behaviour by side.
+ * + * Used by {@link BlockTypes#REPEATER} (horizontally) and other redstone-related blocks (upwards). + */ + public static final BehaviourType INDIRECT_SIGNAL = BehaviourType.of(SpongeTools.key("indirect_signal")); + + /** + * Result of this behaviour is usually used by {@link BlockTypes#COMPARATOR}. + */ + public static final BehaviourType>> ANALOG_SIGNAL = BehaviourType.of(SpongeTools.key("analog_signal")); + + /** + * The strength of the block for destruction.
+ * The bigger this value, the longer it will take for block to be destroyed.
+ * Special value of -1 makes block unbreakable and not movable by piston. + */ + public static final BehaviourType> DESTRUCTION_RESISTANCE = BehaviourType.of(SpongeTools.key("destruction_resistance")); + + /** + * The increment of the destruction progress per tick.
+ * Value greater than or equal to 1 makes block break instantly. + */ + public static final BehaviourType> DESTRUCTION_INCREMENT = BehaviourType.of(SpongeTools.key("destruction_increment")); + + /** + * Adding this behaviour doesn't make block ticking "naturally".
+ * Ticks must be scheduled through {@link UpdatableVolume#scheduledBlockUpdates()}.
+ * For example, ticks could be scheduled in {@link #PLACE}, {@link #SHAPE_UPDATE} or in {@link #BASE_TICK} itself. + */ + public static final BehaviourType BASE_TICK = BehaviourType.of(SpongeTools.key("base_tick")); + + /** + * Adding this behaviour doesn't make block randomly ticking "naturally".
+ * + * @see #HAS_RANDOM_TICK + */ + public static final BehaviourType RANDOM_TICK = BehaviourType.of(SpongeTools.key("random_tick")); + + /** + * Used to decide whether block should recieve natural random ticks. + * + * @see #RANDOM_TICK + */ + public static final BehaviourType HAS_RANDOM_TICK = BehaviourType.of(SpongeTools.key("has_random_tick")); + + /** + * Called when {@link Entity}'s {@link AABB} collides with the {@link BlockState}. + */ + public static final BehaviourType, Entity>> ENTITY_INSIDE = BehaviourType.of(SpongeTools.key("entity_inside")); + + /** + * Called when {@link BlockState} enters the world. + */ + public static final BehaviourType PLACE = BehaviourType.of(SpongeTools.key("place")); + + /** + * Called when {@link BlockState} leaves the world. + */ + public static final BehaviourType REMOVE = BehaviourType.of(SpongeTools.key("remove")); + + /** + * Called when {@link BlockState} is interacted with by the explosion. + */ + public static final BehaviourType EXPLOSION = BehaviourType.of(SpongeTools.key("explosion")); + + /** + * Called when neighbour {@link BlockState}s are changed.
+ * + * This is usually used to update properties that could be + * considered as "shape" depending on neighbour blocks and + * by waterlogged blocks to schedule liquid ticks. + */ + public static final BehaviourType SHAPE_UPDATE = BehaviourType.of(SpongeTools.key("shape_update")); + + /** + * Called when {@link BlockState} is updated through + * {@link WorldExtension#updateAt(int, int, int, BlockType)} (mostly by signal-related blocks).
+ * This is usually used to update properties that rely on redstone signal. + */ + public static final BehaviourType SIGNAL_UPDATE = BehaviourType.of(SpongeTools.key("signal_update")); + + public static final BehaviourType USE_WITH_ITEM = BehaviourType.of(SpongeTools.key("use_with_item")); + + public static final BehaviourType USE_WITHOUT_ITEM = BehaviourType.of(SpongeTools.key("use_without_item")); + + public static final BehaviourType, Player>> ATTACK = BehaviourType.of(SpongeTools.key("attack")); + + /** + * Used by {@link BlockTypes#PISTON} and {@link BlockTypes#STICKY_PISTON}. + */ + public static final BehaviourType> PUSH_REACTION = BehaviourType.of(SpongeTools.key("push_reaction")); + + /** + * Used by {@link BlockTypes#NOTE_BLOCK}. + */ + public static final BehaviourType> INSTRUMENT = BehaviourType.of(SpongeTools.key("instrument")); + + public static final BehaviourType> FLUID = BehaviourType.of(SpongeTools.key("fluid")); + + public static final BehaviourType REQUIRE_TOOL = BehaviourType.of(SpongeTools.key("require_tool")); + + public static final BehaviourType REPLACEABLE = BehaviourType.of(SpongeTools.key("replaceable")); + + public static final BehaviourType REPLACEABLE_BY_FLUID = BehaviourType.of(SpongeTools.key("replaceable_by_fluid")); + + public static final BehaviourType REPLACEABLE_BY_BLOCK = BehaviourType.of(SpongeTools.key("replaceable_by_block")); + + /** + * Used to decide whether entities will suffocate in the block. + */ + public static final BehaviourType> SUFFOCATION = BehaviourType.of(SpongeTools.key("suffocation")); + + /** + * If this behaviour returns true, {@link #SHAPE_UPDATE} would be called + * for this block with each neighbour around it when generated in the world. + */ + public static final BehaviourType> POST_PROCESSING = BehaviourType.of(SpongeTools.key("post_processing")); + + public static final BehaviourType>> CAN_SURVIVE = BehaviourType.of(SpongeTools.key("can_survive")); + + /** + * The item representation of the block.
+ * Usually it's asked when player uses the middle mouse button.
+ * If block doesn't have an item representation, this behaviour would return {@link ItemStack#empty()}. + */ + public static final BehaviourType CLONE_ITEM = BehaviourType.of(SpongeTools.key("clone_item")); + + /** + * TODO Can do something and return item filled "bucket". + */ + public static final BehaviourType, Optional>> BUCKET_PICKUP_ITEM = BehaviourType.of(SpongeTools.key("bucket_pickup_item")); + + public static final BehaviourType>> BUCKET_PICKUP_SOUND = BehaviourType.of(SpongeTools.key("bucket_pickup_sound")); + + private BlockStateBehaviours() { + } +} diff --git a/api/src/main/java/net/hellheim/spongetools/custom/behaviour/type/BlockStateExtension.java b/api/src/main/java/net/hellheim/spongetools/custom/behaviour/type/BlockStateExtension.java new file mode 100644 index 0000000..b92f8c0 --- /dev/null +++ b/api/src/main/java/net/hellheim/spongetools/custom/behaviour/type/BlockStateExtension.java @@ -0,0 +1,36 @@ +package net.hellheim.spongetools.custom.behaviour.type; + +import java.util.Objects; + +import org.spongepowered.api.block.BlockState; + +import net.hellheim.spongetools.custom.behaviour.BehaviourCallbackHolder; +import net.hellheim.spongetools.custom.behaviour.BehaviourHolder; +import net.hellheim.spongetools.proxy.solid.block.BlockStateProxy; + +/** + * Can be used to override default {@link BlockState} behaviour. + */ +public interface BlockStateExtension extends + BehaviourHolder.Defaulted, + BehaviourCallbackHolder, + BlockStateProxy { + + static BlockStateExtension getFor(final BlockState state) { + return (BlockStateExtension) Objects.requireNonNull(state, "state"); + } + + BlockState state(); + + BlockState display(); + + @Override + default BlockState getAsBlockState() { + return this.state(); + } + + @Override + default Object getAsActualBehaviourHolder() { + return this.state(); + } +} diff --git a/api/src/main/java/net/hellheim/spongetools/custom/behaviour/type/WorldExtension.java b/api/src/main/java/net/hellheim/spongetools/custom/behaviour/type/WorldExtension.java new file mode 100644 index 0000000..e25cd45 --- /dev/null +++ b/api/src/main/java/net/hellheim/spongetools/custom/behaviour/type/WorldExtension.java @@ -0,0 +1,44 @@ +package net.hellheim.spongetools.custom.behaviour.type; + +import java.util.Objects; + +import org.spongepowered.api.block.BlockType; +import org.spongepowered.api.util.Direction; +import org.spongepowered.api.world.World; +import org.spongepowered.math.vector.Vector3i; + +public interface WorldExtension> { + + static > WorldExtension getFor(final W world) { + @SuppressWarnings("unchecked") + final WorldExtension extension = (WorldExtension) world; + return extension; + } + + default W owner() { + @SuppressWarnings("unchecked") + final W world = (W) this; + return world; + } + + void updateAt(int x , int y, int z, BlockType notifier); + + default void updateAt(final Vector3i position, final BlockType notifier) { + Objects.requireNonNull(position, "position"); + this.updateAt(position.x(), position.y(), position.z(), notifier); + } + + void updateAround(int x, int y, int z, BlockType notifier); + + default void updateAround(final Vector3i position, final BlockType notifier) { + Objects.requireNonNull(position, "position"); + this.updateAround(position.x(), position.y(), position.z(), notifier); + } + + void updateAroundExcept(int x, int y, int z, BlockType notifier, Direction direction); + + default void updateAroundExcept(final Vector3i position, final BlockType notifier, final Direction direction) { + Objects.requireNonNull(position, "position"); + this.updateAroundExcept(position.x(), position.y(), position.z(), notifier, direction); + } +} diff --git a/api/src/main/java/net/hellheim/spongetools/custom/behaviour/util/HitResult.java b/api/src/main/java/net/hellheim/spongetools/custom/behaviour/util/HitResult.java new file mode 100644 index 0000000..f73a731 --- /dev/null +++ b/api/src/main/java/net/hellheim/spongetools/custom/behaviour/util/HitResult.java @@ -0,0 +1,133 @@ +package net.hellheim.spongetools.custom.behaviour.util; + +import java.util.Objects; + +import org.spongepowered.api.Sponge; +import org.spongepowered.api.entity.Entity; +import org.spongepowered.api.util.CopyableBuilder; +import org.spongepowered.api.util.Direction; +import org.spongepowered.api.util.blockray.RayTraceResult; +import org.spongepowered.math.vector.Vector3d; +import org.spongepowered.math.vector.Vector3i; + +/** + * Similar to {@link RayTraceResult} but reflects + * vanilla stuff because they can't be mapped 1 to 1. + */ +public interface HitResult { + + static EntityHitResult entity(final RayTraceResult rayTrace) { + return HitResult.entity(rayTrace.selectedObject(), rayTrace.hitPosition()); + } + + static EntityHitResult entity(final Entity entity) { + return HitResult.entity(entity, entity.position()); + } + + static EntityHitResult entity(final Entity entity, final Vector3d hitPosition) { + Objects.requireNonNull(hitPosition, "hitPosition"); + return HitResult.entity(entity, hitPosition.x(), hitPosition.y(), hitPosition.z()); + } + + static EntityHitResult entity(final Entity entity, final double hitX, final double hitY, final double hitZ) { + return Sponge.game().factoryProvider().provide(Factory.class).entity(entity, hitX, hitY, hitZ); + } + + static BlockHitResult.Builder block() { + return Sponge.game().builderProvider().provide(BlockHitResult.Builder.class); + } + + /** + * Returns the position the ray ended at. + * + * @return The position the ray ended at + */ + Vector3d hitPosition(); + + /** + * Returns whether this hit result is considered as missed.
+ * + * Usually this returns true if this result is {@link BlockHitResult} and + * its {@link BlockHitResult.Builder#miss(boolean)} flag is set to true. + * + * @return True if this hit result is considered as missed + */ + boolean miss(); + + interface EntityHitResult extends HitResult { + + /** + * Returns the entity hit by the ray. + * + * @return The entity hit by the ray + */ + Entity entity(); + } + + interface BlockHitResult extends HitResult { + + /** + * Returns the block position hit by the ray. + * + * @return The block position hit by the ray + */ + Vector3i blockPosition(); + + /** + * Returns the direction the ray hit the block from. + * + * @return The direction the ray hit the block from + */ + Direction direction(); + + /** + * Returns whether the ray ended inside the block it hit. + * + * @return True if the ray ended inside the block it hit + */ + boolean inside(); + + /** + * Returns whether the ray hit the world border. + * + * @return True if the ray hit the world border + */ + boolean worldBorder(); + + default Builder toBuilder() { + return HitResult.block().from(this); + } + + interface Builder extends + org.spongepowered.api.util.Builder, + CopyableBuilder { + + Builder hitPosition(double x, double y, double z); + + default Builder hitPosition(final Vector3d position) { + Objects.requireNonNull(position, "position"); + return this.hitPosition(position.x(), position.y(), position.z()); + } + + Builder blockPosition(int x, int y, int z); + + default Builder blockPosition(final Vector3i position) { + Objects.requireNonNull(position, "position"); + return this.blockPosition(position.x(), position.y(), position.z()); + } + + Builder direction(Direction direction); + + Builder miss(boolean miss); + + Builder inside(boolean inside); + + Builder worldBorder(boolean worldBorder); + } + } + + interface Factory { + + EntityHitResult entity(Entity entity, double hitX, double hitY, double hitZ); + } +} diff --git a/api/src/main/java/net/hellheim/spongetools/custom/behaviour/util/InteractionResult.java b/api/src/main/java/net/hellheim/spongetools/custom/behaviour/util/InteractionResult.java new file mode 100644 index 0000000..6dc01d6 --- /dev/null +++ b/api/src/main/java/net/hellheim/spongetools/custom/behaviour/util/InteractionResult.java @@ -0,0 +1,94 @@ +package net.hellheim.spongetools.custom.behaviour.util; + +import java.util.Optional; + +import org.spongepowered.api.Sponge; +import org.spongepowered.api.item.inventory.ItemStack; +import org.spongepowered.api.item.inventory.ItemStackLike; + +public interface InteractionResult { + + InteractionResult.Success SUCCESS_CLIENT = InteractionResult.factory().successClient(); + + InteractionResult.Success SUCCESS_SERVER = InteractionResult.factory().successServer(); + + InteractionResult.Success CONSUME = InteractionResult.factory().consume(); + + InteractionResult FAIL = InteractionResult.factory().fail(); + + InteractionResult PASS = InteractionResult.factory().pass(); + + InteractionResult TRY_WITHOUT_ITEM = InteractionResult.factory().tryWithoutItem(); + + static InteractionResult.Success success(final SwingType swing, final boolean isInteraction) { + return InteractionResult.success(swing, isInteraction, Optional.empty()); + } + + static InteractionResult.Success success(final SwingType swing, final boolean isInteraction, final ItemStackLike result) { + return InteractionResult.success(swing, isInteraction, Optional.of(result.asMutableCopy())); + } + + static InteractionResult.Success success( + final SwingType swing, final boolean isInteraction, final Optional result + ) { + return InteractionResult.factory().success(swing, isInteraction, result); + } + + private static Factory factory() { + return Sponge.game().factoryProvider().provide(Factory.class); + } + + interface Success extends InteractionResult { + + SwingType swing(); + + boolean isInteraction(); + + Optional result(); + + default Success withSwing(final SwingType swing) { + return this.swing() == swing + ? this + : InteractionResult.success(swing, this.isInteraction(), this.result()); + } + + default Success withInteraction(final boolean isInteraction) { + return this.isInteraction() == isInteraction + ? this + : InteractionResult.success(this.swing(), isInteraction, this.result()); + } + + default Success withoutResult() { + return this.result().isEmpty() + ? this + : InteractionResult.success(this.swing(), this.isInteraction(), Optional.empty()); + } + + default Success withResult(final ItemStackLike result) { + return InteractionResult.success(null, isInteraction(), Optional.of(result.asMutableCopy())); + } + + default Success withResult(final Optional result) { + return result.isEmpty() + ? this.withoutResult() + : InteractionResult.success(this.swing(), this.isInteraction(), result); + } + } + + interface Factory { + + InteractionResult.Success successClient(); + + InteractionResult.Success successServer(); + + InteractionResult.Success consume(); + + InteractionResult fail(); + + InteractionResult pass(); + + InteractionResult tryWithoutItem(); + + InteractionResult.Success success(SwingType swing, boolean isInteraction, Optional result); + } +} diff --git a/api/src/main/java/net/hellheim/spongetools/custom/behaviour/util/SignalBias.java b/api/src/main/java/net/hellheim/spongetools/custom/behaviour/util/SignalBias.java new file mode 100644 index 0000000..3709aaf --- /dev/null +++ b/api/src/main/java/net/hellheim/spongetools/custom/behaviour/util/SignalBias.java @@ -0,0 +1,23 @@ +package net.hellheim.spongetools.custom.behaviour.util; + +import org.spongepowered.api.Sponge; + +public interface SignalBias { + + SignalBias LEFT = SignalBias.factory().left(); + + SignalBias RIGHT = SignalBias.factory().right(); + + private static Factory factory() { + return Sponge.game().factoryProvider().provide(Factory.class); + } + + SignalBias opposite(); + + interface Factory { + + SignalBias left(); + + SignalBias right(); + } +} diff --git a/api/src/main/java/net/hellheim/spongetools/custom/behaviour/util/SignalOrientation.java b/api/src/main/java/net/hellheim/spongetools/custom/behaviour/util/SignalOrientation.java new file mode 100644 index 0000000..b9b0ef5 --- /dev/null +++ b/api/src/main/java/net/hellheim/spongetools/custom/behaviour/util/SignalOrientation.java @@ -0,0 +1,73 @@ +package net.hellheim.spongetools.custom.behaviour.util; + +import java.util.Optional; +import java.util.stream.Stream; + +import org.spongepowered.api.Sponge; +import org.spongepowered.api.util.Direction; +import org.spongepowered.api.util.RandomProvider; +import org.spongepowered.api.world.World; + +public interface SignalOrientation { + + static SignalOrientation of(final Direction up, final Direction front, final SignalBias bias) { + return SignalOrientation.factory().of(up, front, bias); + } + + static SignalOrientation random(final RandomProvider.Source random) { + return SignalOrientation.factory().random(random); + } + + static SignalOrientation random(final RandomProvider provider) { + return SignalOrientation.random(provider.random()); + } + + static SignalOrientation randomWith(final World world, final Optional up, final Optional front) { + return SignalOrientation.factory().randomWith(world, up, front); + } + + static SignalOrientation randomWith(final World world, final Direction up, final Direction front) { + return SignalOrientation.randomWith(world, Optional.of(up), Optional.of(front)); + } + + static SignalOrientation randomWithUp(final World world, final Direction up) { + return SignalOrientation.randomWith(world, Optional.of(up), Optional.empty()); + } + + static SignalOrientation randomWithFront(final World world, final Direction front) { + return SignalOrientation.randomWith(world, Optional.empty(), Optional.of(front)); + } + + private static Factory factory() { + return Sponge.game().factoryProvider().provide(Factory.class); + } + + Direction up(); + + Direction front(); + + Direction side(); + + SignalBias bias(); + + SignalOrientation withUp(Direction direction); + + SignalOrientation withFront(Direction direction); + + SignalOrientation withBias(SignalBias bias); + + Stream allDirections(); + + Stream horizontalDirections(); + + Stream verticalDirections(); + + interface Factory { + + SignalOrientation of(Direction up, Direction front, SignalBias bias); + + SignalOrientation random(RandomProvider.Source random); + + SignalOrientation randomWith(World world, Optional up, Optional front); + } +} diff --git a/api/src/main/java/net/hellheim/spongetools/custom/behaviour/util/SwingType.java b/api/src/main/java/net/hellheim/spongetools/custom/behaviour/util/SwingType.java new file mode 100644 index 0000000..d2d3d58 --- /dev/null +++ b/api/src/main/java/net/hellheim/spongetools/custom/behaviour/util/SwingType.java @@ -0,0 +1,25 @@ +package net.hellheim.spongetools.custom.behaviour.util; + +import org.spongepowered.api.Sponge; + +public interface SwingType { + + SwingType NONE = SwingType.factory().none(); + + SwingType SERVER = SwingType.factory().server(); + + SwingType CLIENT = SwingType.factory().client(); + + private static Factory factory() { + return Sponge.game().factoryProvider().provide(Factory.class); + } + + interface Factory { + + SwingType none(); + + SwingType server(); + + SwingType client(); + } +} diff --git a/api/src/main/java/net/hellheim/spongetools/custom/behaviour/util/UseContext.java b/api/src/main/java/net/hellheim/spongetools/custom/behaviour/util/UseContext.java new file mode 100644 index 0000000..fb060f6 --- /dev/null +++ b/api/src/main/java/net/hellheim/spongetools/custom/behaviour/util/UseContext.java @@ -0,0 +1,108 @@ +package net.hellheim.spongetools.custom.behaviour.util; + +import java.util.List; +import java.util.Optional; + +import org.spongepowered.api.data.type.HandType; +import org.spongepowered.api.entity.living.player.Player; +import org.spongepowered.api.item.inventory.ItemStack; +import org.spongepowered.api.util.Direction; +import org.spongepowered.api.world.World; +import org.spongepowered.math.vector.Vector3i; + +/** + * Represents the context of the item usage. + */ +public interface UseContext { + + /** + * Returns the player that used item, if present. + * + * @return The player that used item, if present + */ + Optional player(); + + /** + * Returns the used hand. + * + * @return The used hand + */ + HandType hand(); + + /** + * Returns the hit result of the usage. + * + * @return The hit result of the usage + */ + HitResult.BlockHitResult hit(); + + /** + * Returns the world the item is used in. + * + * @return The world the item is used in + */ + World world(); + + /** + * Returns the used item. This item can be mutated. + * + * @return The used item + */ + ItemStack item(); + + /** + * Returns the position that is considered as true clicked position.
+ * This may differ from the position of the {@link #hit()}. + * + * @return The true clicked block position + */ + Vector3i clickedPosition(); + + /** + * Returns the direction that is considered as true clicked direction.
+ * This may differ from the direction of the {@link #hit()}. + * + * @return The true clicked direction + */ + Direction clickedDirection(); + + /** + * Returns the yaw rotation of the usage.
+ * This may differ from the yaw of the {@link #player()}. + * + * @return The true yaw of the usage + */ + double rotation(); + + /** + * Returns the horizontal direction of the usage. + * + * @return The horizontal direction of the usage + */ + Direction horizontalDirection(); + + /** + * Returns whether the usage has active secondary use. + * + * @return True if the usage has active secondary use + */ + boolean isSecondary(); + + interface BlockPlace extends UseContext { + + boolean canPlace(); + + boolean canReplaceClickedPosition(); + + Direction nearestDirection(); + + Direction nearestVerticalDirection(); + + List nearestDirections(); + } + + interface DirectionaBlockPlace extends BlockPlace { + + Direction baseDirection(); + } +} diff --git a/api/src/main/java/net/hellheim/spongetools/custom/type/CustomTypeArchetype.java b/api/src/main/java/net/hellheim/spongetools/custom/type/CustomTypeArchetype.java new file mode 100644 index 0000000..6e61f46 --- /dev/null +++ b/api/src/main/java/net/hellheim/spongetools/custom/type/CustomTypeArchetype.java @@ -0,0 +1,218 @@ +package net.hellheim.spongetools.custom.type; + +import java.util.Objects; +import java.util.Optional; +import java.util.Set; +import java.util.function.BiConsumer; +import java.util.stream.Stream; + +import org.spongepowered.api.Sponge; +import org.spongepowered.api.registry.DefaultedRegistryValue; +import org.spongepowered.api.registry.Registry; + +import net.hellheim.spongetools.custom.behaviour.BehaviourLayer; +import net.hellheim.spongetools.custom.type.block.BlockTypeArchetype; +import net.hellheim.spongetools.custom.type.entity.EntityTypeArchetype; +import net.hellheim.spongetools.custom.type.item.ItemTypeArchetype; +import net.hellheim.spongetools.object.TypedKey; +import net.hellheim.spongetools.object.TypedKeyMap; + +/** + * Represents the core of the custom type used for {@link CustomTypeBuilder}.
+ * The archetype can declare required/supported context keys and supported behaviour. + * + * @see ItemTypeArchetype + * @see BlockTypeArchetype + * @see EntityTypeArchetype + * + * @param The type of the value this archetype is used to build for + * @param The type of the corresponding instance with the custom {@link T type} + * @param
The child archetype type + */ +public interface CustomTypeArchetype> + extends DefaultedRegistryValue, BehaviourLayer { + + /** + * Validates the archetype's base class against the root class and the archetype's parent archetype. + * + * @param rootClass The root class of all archetypes' base classes + * @param baseClass The base class to validate + * @param parent The parent archetype + */ + static void validate( + final Class rootClass, final Class baseClass, + final Optional> parent + ) { + if (!rootClass.isAssignableFrom(Objects.requireNonNull(baseClass, "baseClass"))) { + throw new IllegalArgumentException(String.format( + "Provided base class (%s) must be a subclass of root class (%s)", + baseClass, rootClass)); + } + + Objects.requireNonNull(parent, "parent").ifPresent(archetype -> { + if (!archetype.baseClass().isAssignableFrom(baseClass)) { + throw new IllegalArgumentException(String.format( + "Parent's base class (%s) must be a parent class of the given base class (%s)", + archetype.baseClass(), baseClass)); + } + }); + } + + /** + * Returns the most appropriate known {@link CustomTypeArchetype} for the given type. + * + * @param The type of values archetype exists for + * @param The type of archetype + * @param archetypes The known archetypes + * @param baseArchetype The base archetype + * @param type The type + * @return The most specific archetype + */ + static > A forType( + final Registry archetypes, final A baseArchetype, final T type + ) { + A archetype = baseArchetype; + for (final A arch : archetypes.stream().toList()) { + if (arch.baseClass().isInstance(type) + && archetype.baseClass().isAssignableFrom(arch.baseClass())) { + archetype = arch; + } + } + return archetype; + } + + /** + * Returns the most appropriate known {@link CustomTypeArchetype} for the given type. + * + * @param The type of value instances archetype exists for + * @param The type of archetype + * @param archetypes The known archetypes + * @param baseArchetype The base archetype + * @param type The type + * @return The most specific archetype + */ + static > A forInstance( + final Registry archetypes, final A baseArchetype, final I instance + ) { + A archetype = baseArchetype; + for (final A arch : archetypes.stream().toList()) { + if (arch.baseClass().isInstance(instance) + && archetype.baseClass().isAssignableFrom(arch.baseClass())) { + archetype = arch; + } + } + return archetype; + } + + /** + * Returns the parent archetype of this archetype.
+ * Archetype inherits all the supported and required features of the parent. + * + * @return The parent archetype + */ + Optional
parent(); + + /** + * Returns the base value class this archetype is based on. + * + * @return The base class + */ + Class baseClass(); + + /** + * Returns the total {@link #requiredKeys()} of this and all parent archetypes. + * + * @return The stream of {@link TypedKey}s + */ + Stream> cumulativeRequiredKeys(); + + /** + * Returns the total {@link #contextExtractor()} of this and all parent archetypes. + * + * @return The {@link TypedKey}s extractor + */ + BiConsumer cumulativeContextExtractor(); + + /** + * {@link CustomTypeArchetype} of type which {@link I instance} + * logic does not depend on corresponding {@link T type} logic.
+ * + * Usually this means that {@link I instance}s are represented by multiple + * classes while {@link T type}s are represented by single class.
+ * This results in {@link #baseClass()} representing some subclass of {@link T type}. + * + * @param The type of the value this archetype is used to build for + * @param The type of the corresponding instance with the custom {@link T type} + * @param
The child archetype type + */ + interface TypeBased> extends CustomTypeArchetype { + + /** + * Returns the {@link TypedKey}s this archetype requires over all parent archetypes. + * + * @return The set of {@link TypedKey}s + */ + Set> requiredKeys(); + + /** + * Returns the extractor of {@link TypedKey}s from {@link T type}. + * + * @return The {@link TypedKey}s extractor + */ + BiConsumer contextExtractor(); + + @Override + default BiConsumer cumulativeContextExtractor() { + return this.parent().isEmpty() + ? this.contextExtractor() + : this.parent().get().cumulativeContextExtractor().andThen(this.contextExtractor()); + } + + @Override + default Stream> cumulativeRequiredKeys() { + return this.parent().isEmpty() + ? this.requiredKeys().stream() + : Stream.concat(this.parent().get().cumulativeRequiredKeys(), this.requiredKeys().stream()); + } + } + + /** + * {@link CustomTypeArchetype} for type which {@link I instance} + * logic does not depend on corresponding {@link T type} logic.
+ * + * Usually this means that {@link I instance}s are represented by multiple + * classes while {@link T type}s are represented by single class. + * This results in {@link #baseClass()} representing some subclass of {@link I instance}. + * + * @param The type of the value this archetype is used to build for + * @param The type of the corresponding instance with the custom {@link T type} + * @param
The child archetype type + */ + interface InstanceBased> extends CustomTypeArchetype { + + /** + * Returns the corresponding {@link CustomTypeArchetype.InstanceBased.Factory} + * that provides all the required context data. + * + * @return The factory class + */ + Class> contextFactory(); + + @Override + default Stream> cumulativeRequiredKeys() { + return Sponge.game().factoryProvider().provide(this.contextFactory()).cumulativeRequiredKeys(); + } + + @Override + default BiConsumer cumulativeContextExtractor() { + return Sponge.game().factoryProvider().provide(this.contextFactory()).cumulativeContextExtractor(); + } + + interface Factory { + + Stream> cumulativeRequiredKeys(); + + BiConsumer cumulativeContextExtractor(); + } + } +} diff --git a/api/src/main/java/net/hellheim/spongetools/custom/type/CustomTypeBuilder.java b/api/src/main/java/net/hellheim/spongetools/custom/type/CustomTypeBuilder.java new file mode 100644 index 0000000..366b15f --- /dev/null +++ b/api/src/main/java/net/hellheim/spongetools/custom/type/CustomTypeBuilder.java @@ -0,0 +1,107 @@ +package net.hellheim.spongetools.custom.type; + +import java.util.Objects; +import java.util.function.Supplier; + +import org.spongepowered.api.data.Key; +import org.spongepowered.api.event.lifecycle.RegisterRegistryValueEvent; +import org.spongepowered.api.registry.RegistryType; +import org.spongepowered.api.util.Builder; +import org.spongepowered.api.util.CopyableBuilder; + +import net.hellheim.spongetools.custom.behaviour.BehaviourCallbackHolder; +import net.hellheim.spongetools.custom.behaviour.BehaviourType; +import net.hellheim.spongetools.custom.type.block.BlockTypeBuilder; +import net.hellheim.spongetools.custom.type.entity.EntityTypeBuilder; +import net.hellheim.spongetools.custom.type.item.ItemTypeBuilder; +import net.hellheim.spongetools.object.DataOperator; +import net.hellheim.spongetools.object.TypedKey; +import net.hellheim.spongetools.object.TypedKeyMap; + +/** + * Builder for custom type. It's represented by some specific concepts:
+ * - {@link A Archetype} - the core of the built type;
+ * - Context (applied through {@link TypedKey}s) - used to construct the type itself;
+ * - Behaviour (applied through {@link BehaviourType}s) - TODO
+ *
+ * Note: All registries that the built type depends on must be passed to {@link RegisterRegistryValueEvent}.
+ * Mandatory dependencies usually can be retrieved via {@link #dependencies()} or + * {@link #dependencies(RegistryType...)} methods of the specific builder classes. + * + * @param The type built by this builder + * @param The type of the corresponding instance with the custom {@link T type} + * @param
The type of the corresponding {@link CustomTypeArchetype} + * @param The child builder type + * + * @see ItemTypeBuilder + * @see BlockTypeBuilder + * @see TODO BlockEntityTypeBuilder + * @see EntityTypeBuilder + * @see TODO PotionEffectTypeBuilder (no data) + */ +public interface CustomTypeBuilder, B extends CustomTypeBuilder> + extends Builder, TypedKeyMap.Operator, BehaviourCallbackHolder.Mutable { + + /** + * Sets the archetype of the built type. + * + * @param archetype The archetype + * @return This builder, for chaining + */ + default B archetype(final Supplier archetype) { + return this.archetype(Objects.requireNonNull(archetype, "archetype").get()); + } + + /** + * Sets the archetype of the built type. + * + * @param archetype The archetype + * @return This builder, for chaining + */ + B archetype(A archetype); + + @Override + B reset(); + + /** + * {@link CustomTypeBuilder} for {@link CustomTypeArchetype.TypeBased}. + * + * @param The type built by this builder + * @param The type of the corresponding instance with the custom {@link T type} + * @param The type of the corresponding {@link CustomTypeArchetype} + * @param The child builder type + */ + interface TypeBased, B extends TypeBased> extends + CustomTypeBuilder, + CopyableBuilder { + } + + /** + * {@link CustomTypeBuilder} for {@link CustomTypeArchetype.InstanceBased}. + * + * @param The type built by this builder + * @param The type of the corresponding instance with the custom {@link T type} + * @param The type of the corresponding {@link CustomTypeArchetype} + * @param The child builder type + */ + interface InstanceBased, B extends InstanceBased> extends + CustomTypeBuilder { + } + + /** + * {@link CustomTypeBuilder} that supports data (applied through {@link Key}s).
+ * All the supported data applies to the {@link I instances}.
+ *
+ * Note: No data involving server-scoped registries should be used while building the type.
+ * It should not be an issue as most of mentioned data is usually applied per mutable {@link I instance}. + * + * @param The type built by this builder + * @param The type of the corresponding instance with the custom {@link T type} + * @param
The type of the corresponding {@link CustomTypeArchetype} + * @param The child builder type + */ + interface WithData, B extends WithData> extends + CustomTypeBuilder, + DataOperator { + } +} diff --git a/api/src/main/java/net/hellheim/spongetools/custom/type/ModeledCustomType.java b/api/src/main/java/net/hellheim/spongetools/custom/type/ModeledCustomType.java new file mode 100644 index 0000000..ef0ab16 --- /dev/null +++ b/api/src/main/java/net/hellheim/spongetools/custom/type/ModeledCustomType.java @@ -0,0 +1,92 @@ +package net.hellheim.spongetools.custom.type; + +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; +import java.util.function.Supplier; +import java.util.function.UnaryOperator; + +import org.spongepowered.api.ResourceKey; + +import net.hellheim.spongetools.custom.type.block.ModeledBlock; +import net.hellheim.spongetools.custom.type.item.ModeledItem; +import net.hellheim.spongetools.resourcepack.Model; +import net.hellheim.spongetools.resourcepack.ModelLike; + +/** + * Wrapper over regular {@link T type} with additional ResourcePack data.
+ * Registering modeled types will register the wrapped type as well as all the model data.
+ * Registry can usually be retrieved via {@link #registry()} method of the corresponding modeled type class.
+ * + * @param The type of the custom type to wrap + * + * @see ModeledItem + * @see ModeledBlock + */ +public interface ModeledCustomType extends Supplier { + + /** + * Returns the wrapped type. + * + * @return The wrapped type + */ + T type(); + + /** + * Returns the models. + * + * @return The models + */ + Map models(); + + @Override + default T get() { + return this.type(); + } + + /** + * Builder for {@link M modeled type}. + * + * @param The type of the custom type to built + * @param The type of the corresponding {@link CustomTypeBuilder} + * @param The type of the corresponding {@link ModeledCustomType} + * @param The type of this builder + */ + abstract class Builder, M extends ModeledCustomType, B extends Builder> + implements org.spongepowered.api.util.Builder { + + protected final ResourceKey key; + protected final Map models = new HashMap<>(); + protected UnaryOperator type = UnaryOperator.identity(); + + public Builder(final ResourceKey key) { + this.key = Objects.requireNonNull(key, "key"); + } + + @SuppressWarnings("unchecked") + private B cast() { + return (B) this; + } + + public B type(final UnaryOperator configurator) { + this.type = this.type.compose(Objects.requireNonNull(configurator, "configurator"))::apply; + return this.cast(); + } + + public B model(final ResourceKey key, final ModelLike model) { + this.models.put(Objects.requireNonNull(key, "key"), Objects.requireNonNull(model, "model").asModel()); + return this.cast(); + } + + @Override + public B reset() { + this.models.clear(); + this.type = UnaryOperator.identity(); + return this.cast(); + } + + protected T buildType(final TB builder) { + return this.type.apply(builder).build(); + } + } +} diff --git a/api/src/main/java/net/hellheim/spongetools/custom/type/block/BlockArchetypes.java b/api/src/main/java/net/hellheim/spongetools/custom/type/block/BlockArchetypes.java new file mode 100644 index 0000000..b85ed57 --- /dev/null +++ b/api/src/main/java/net/hellheim/spongetools/custom/type/block/BlockArchetypes.java @@ -0,0 +1,47 @@ +package net.hellheim.spongetools.custom.type.block; + +import org.spongepowered.api.Sponge; +import org.spongepowered.api.block.BlockTypes; +import org.spongepowered.api.registry.DefaultedRegistryReference; +import org.spongepowered.api.registry.Registry; +import org.spongepowered.api.registry.RegistryKey; + +import net.hellheim.spongetools.SpongeTools; + +public final class BlockArchetypes { + + /** + * Regular block.
+ *
+ * Required Context:
+ * - {@link BlockTypeKeys#TRANSLATION_KEY}
+ *
+ * Supported Context:
+ * - {@link BlockTypeKeys#DEFAULT_STATE}
+ * - {@link BlockTypeKeys#STATE_PROPERTIES}
+ */ + public static final DefaultedRegistryReference DEFAULT = BlockArchetypes.key("default"); + + /** + * Block that behaves like {@link BlockTypes#SCAFFOLDING}.
+ * It's required to provide at least 3 state properties: + * boolean WATERLOGGED and BOTTOM, and integer property named "distance".
+ *
+ * Parent Archetype: {@link #DEFAULT}.
+ *
+ * Required Context:
+ * - {@link BlockTypeKeys#STATE_PROPERTIES} + */ + public static final DefaultedRegistryReference SCAFFOLDING = BlockArchetypes.key("scaffolding"); + + private BlockArchetypes() { + } + + public static Registry registry() { + return BlockTypeArchetype.registry().get(); + } + + private static DefaultedRegistryReference key(final String key) { + return RegistryKey.of(BlockTypeArchetype.registry(), SpongeTools.key(key)).asDefaultedReference(Sponge::game); + } +} diff --git a/api/src/main/java/net/hellheim/spongetools/custom/type/block/BlockSoundGroupBuilder.java b/api/src/main/java/net/hellheim/spongetools/custom/type/block/BlockSoundGroupBuilder.java new file mode 100644 index 0000000..a017f9c --- /dev/null +++ b/api/src/main/java/net/hellheim/spongetools/custom/type/block/BlockSoundGroupBuilder.java @@ -0,0 +1,53 @@ +package net.hellheim.spongetools.custom.type.block; + +import java.util.Objects; +import java.util.function.Supplier; + +import org.spongepowered.api.Sponge; +import org.spongepowered.api.block.BlockSoundGroup; +import org.spongepowered.api.effect.sound.SoundType; +import org.spongepowered.api.util.Builder; +import org.spongepowered.api.util.CopyableBuilder; + +public interface BlockSoundGroupBuilder extends + Builder, + CopyableBuilder { + + static BlockSoundGroupBuilder create() { + return Sponge.game().builderProvider().provide(BlockSoundGroupBuilder.class); + } + + BlockSoundGroupBuilder volume(double volume); + + BlockSoundGroupBuilder pitch(double pitch); + + BlockSoundGroupBuilder breakSound(SoundType sound); + + BlockSoundGroupBuilder stepSound(SoundType sound); + + BlockSoundGroupBuilder placeSound(SoundType sound); + + BlockSoundGroupBuilder hitSound(SoundType sound); + + BlockSoundGroupBuilder fallSound(SoundType sound); + + default BlockSoundGroupBuilder breakSound(Supplier sound) { + return this.breakSound(Objects.requireNonNull(sound, "sound").get()); + } + + default BlockSoundGroupBuilder stepSound(Supplier sound) { + return this.stepSound(Objects.requireNonNull(sound, "sound").get()); + } + + default BlockSoundGroupBuilder placeSound(Supplier sound) { + return this.placeSound(Objects.requireNonNull(sound, "sound").get()); + } + + default BlockSoundGroupBuilder hitSound(Supplier sound) { + return this.hitSound(Objects.requireNonNull(sound, "sound").get()); + } + + default BlockSoundGroupBuilder fallSound(Supplier sound) { + return this.fallSound(Objects.requireNonNull(sound, "sound").get()); + } +} diff --git a/api/src/main/java/net/hellheim/spongetools/custom/type/block/BlockStateDispatcher.java b/api/src/main/java/net/hellheim/spongetools/custom/type/block/BlockStateDispatcher.java new file mode 100644 index 0000000..deca177 --- /dev/null +++ b/api/src/main/java/net/hellheim/spongetools/custom/type/block/BlockStateDispatcher.java @@ -0,0 +1,30 @@ +package net.hellheim.spongetools.custom.type.block; + +import java.util.Optional; + +import org.spongepowered.api.Sponge; +import org.spongepowered.api.block.BlockState; + +public interface BlockStateDispatcher { + + static BlockStateDispatcher get() { + return Sponge.game().factoryProvider().provide(BlockStateDispatcher.class); + } + + Optional get(BlockState state); + + boolean isOccupied(BlockState state); + + default boolean isAvailable(final BlockState state) { + return !this.isOccupied(state); + } + + default void dispatch(final BlockStateHolder holder, final BlockStateProvider provider) { + this.submit(holder, provider); + this.dispatch(); + } + + void submit(BlockStateHolder holder, BlockStateProvider provider); + + void dispatch(); +} diff --git a/api/src/main/java/net/hellheim/spongetools/custom/type/block/BlockStateHolder.java b/api/src/main/java/net/hellheim/spongetools/custom/type/block/BlockStateHolder.java new file mode 100644 index 0000000..6e11192 --- /dev/null +++ b/api/src/main/java/net/hellheim/spongetools/custom/type/block/BlockStateHolder.java @@ -0,0 +1,73 @@ +package net.hellheim.spongetools.custom.type.block; + +import java.util.Objects; + +import org.checkerframework.checker.nullness.qual.MonotonicNonNull; +import org.spongepowered.api.block.BlockState; + +import net.hellheim.spongetools.proxy.solid.block.BlockStateProxy; + +public interface BlockStateHolder extends BlockStateProxy { + + static Simple create() { + return new Simple(); + } + + /** + * @return True if state is bound to this holder + */ + boolean isBound(); + + /** + * @return The state bound to this holder + * @throws IllegalStateException if state is not yet bound + * @see #bind(BlockState) + */ + BlockState state(); + + // TODO move the algorithm description to somewhere else. + /** + * Binds the state to this {@link BlockStateHolder}.
+ * The state is chosen in a way where all submitted + * {@link BlockStateHolder}s get the appropriate result.
+ * If block is already used in the world, the used state would be provided.
+ * If block is not yet used in the world, the state would be chosen from {@link #stateProvider()}.
+ * If it's not possible to chose the state, TODO something is thrown. + * + * @param state The state to bind to this {@link BlockStateHolder} + * @throws IllegalStateException if state is already bound + */ + void bind(BlockState state); + + @Override + default BlockState getAsBlockState() { + return this.state(); + } + + class Simple implements BlockStateHolder { + + private @MonotonicNonNull BlockState state; + + @Override + public boolean isBound() { + return this.state != null; + } + + @Override + public BlockState state() { + if (this.state == null) { + throw new IllegalStateException("State is not yet bound."); + } + return this.state; + } + + @Override + public void bind(final BlockState state) { + if (this.state != null) { + throw new IllegalStateException("State is already bound."); + } + + this.state = Objects.requireNonNull(state, "state"); + } + } +} diff --git a/api/src/main/java/net/hellheim/spongetools/custom/type/block/BlockStateProvider.java b/api/src/main/java/net/hellheim/spongetools/custom/type/block/BlockStateProvider.java new file mode 100644 index 0000000..4e8684e --- /dev/null +++ b/api/src/main/java/net/hellheim/spongetools/custom/type/block/BlockStateProvider.java @@ -0,0 +1,170 @@ +package net.hellheim.spongetools.custom.type.block; + +import java.util.Arrays; +import java.util.Collection; +import java.util.List; +import java.util.Objects; +import java.util.Set; +import java.util.function.Predicate; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import org.spongepowered.api.ResourceKey; +import org.spongepowered.api.block.BlockState; +import org.spongepowered.api.block.BlockType; +import org.spongepowered.api.block.BlockTypes; +import org.spongepowered.api.data.type.SlabPortions; +import org.spongepowered.api.registry.RegistryKey; +import org.spongepowered.api.state.BooleanStateProperties; +import org.spongepowered.api.state.EnumStateProperties; +import org.spongepowered.api.state.IntegerStateProperties; + +import net.hellheim.spongetools.resourcepack.block.StateCondition; + +public interface BlockStateProvider/* extends MapCodecProxy*/ { + + static All all() { + return All.INSTANCE; + } + + static Any any(final BlockStateProvider... providers) { + return new Any(List.of(providers)); + } + + static Any any(final Collection providers) { + return new Any(List.copyOf(providers)); + } + + @SafeVarargs + static AnyBlock blocks(final RegistryKey... blocks) { + return new AnyBlock(Arrays.stream(blocks).map(RegistryKey::location).collect(Collectors.toSet())); + } + + static AnyBlock blocks(final ResourceKey... blocks) { + return new AnyBlock(Set.of(blocks)); + } + + static WithCondition withCondition(final BlockStateProvider provider, final StateCondition condition) { + return new WithCondition(provider, condition); + } + + static WithCondition slab() { + return WithCondition.SLAB; + } + + static WithCondition scaffolding(final boolean waterlogged, final boolean bottom) { + return BlockStateProvider.withCondition(BlockStateProvider.blocks(BlockTypes.SCAFFOLDING), + StateCondition.and( + StateCondition.not(IntegerStateProperties.property_STABILITY_DISTANCE(), 0), + StateCondition.is(BooleanStateProperties.property_WATERLOGGED(), waterlogged), + StateCondition.is(BooleanStateProperties.property_BOTTOM(), bottom) + ) + ); + } + + + /** + * @throws NoAvailableStateException + */ + default BlockState provide() { + return this.provide($ -> true); + } + + /** + * @throws NoAvailableStateException + */ + default BlockState provide(final Predicate predicate) { + return this.availableStates() + .filter(predicate) + .findAny() + .orElseThrow(() -> new IllegalStateException( + "No available states for provider " + this.toString())); + } + + default Stream availableStates() { + return this.allStates().filter(BlockStateDispatcher.get()::isAvailable); + } + + Stream allStates(); + + @Override + String toString(); + + + record All() implements BlockStateProvider { + + private static final All INSTANCE = new All(); + + @Override + public Stream allStates() { + return BlockTypes.registry().stream().flatMap(block -> block.validStates().stream()); + } + + @Override + public final String toString() { + return "All"; + } + } + + record Any(List providers) implements BlockStateProvider { + + public Any(final List providers) { + this.providers = List.copyOf(providers); + } + + @Override + public Stream allStates() { + return this.providers.stream().flatMap(BlockStateProvider::allStates).distinct(); + } + + @Override + public final String toString() { + return String.format("AnyProvider[%s]", + this.providers.stream().map(BlockStateProvider::toString).collect(Collectors.joining(","))); + } + } + + record AnyBlock(Set blocks) implements BlockStateProvider { + + public AnyBlock(final Set blocks) { + this.blocks = Set.copyOf(blocks); + } + + @Override + public Stream allStates() { + return this.blocks.stream() + .map(BlockTypes.registry()::value) + .flatMap(block -> block.validStates().stream()); + } + + @Override + public String toString() { + return String.format("AnyBlock[%s]", + this.blocks.stream().map(ResourceKey::asString).collect(Collectors.joining(","))); + } + } + + record WithCondition(BlockStateProvider provider, StateCondition condition) implements BlockStateProvider { + + private static final WithCondition SLAB = withCondition(all(), StateCondition.and( + StateCondition.is(EnumStateProperties.property_SLAB_TYPE(), SlabPortions.DOUBLE.get()), + StateCondition.is(BooleanStateProperties.property_WATERLOGGED(), true) + )); + + public WithCondition(final BlockStateProvider provider, final StateCondition condition) { + this.provider = Objects.requireNonNull(provider, "provider"); + this.condition = Objects.requireNonNull(condition, "condition"); + } + + @Override + public Stream allStates() { + return this.provider.allStates().filter(this.condition::test); + } + + @Override + public final String toString() { + return String.format("WithCondition[provider=%s;condition=%s]", + this.provider, this.condition); + } + } +} diff --git a/api/src/main/java/net/hellheim/spongetools/custom/type/block/BlockTypeArchetype.java b/api/src/main/java/net/hellheim/spongetools/custom/type/block/BlockTypeArchetype.java new file mode 100644 index 0000000..f386701 --- /dev/null +++ b/api/src/main/java/net/hellheim/spongetools/custom/type/block/BlockTypeArchetype.java @@ -0,0 +1,75 @@ +package net.hellheim.spongetools.custom.type.block; + +import java.util.Objects; +import java.util.Optional; +import java.util.Set; +import java.util.function.BiConsumer; + +import org.spongepowered.api.block.BlockState; +import org.spongepowered.api.block.BlockType; +import org.spongepowered.api.data.value.ValueContainer; +import org.spongepowered.api.registry.DefaultedRegistryType; +import org.spongepowered.api.util.annotation.CatalogedBy; + +import net.hellheim.spongetools.SpongeTools; +import net.hellheim.spongetools.custom.behaviour.BehaviourCallbackHolder; +import net.hellheim.spongetools.custom.type.CustomTypeArchetype; +import net.hellheim.spongetools.function.TriFunction; +import net.hellheim.spongetools.object.TypedKey; +import net.hellheim.spongetools.object.TypedKeyMap; + +/** + * {@link CustomTypeArchetype} of {@link BlockType} used for {@link BlockTypeBuilder}. + */ +@CatalogedBy(BlockArchetypes.class) +public record BlockTypeArchetype( + Optional parent, + Class baseClass, + Set> requiredKeys, + BiConsumer contextExtractor, + TriFunction, BlockType> assembler + ) implements CustomTypeArchetype.TypeBased { + + public BlockTypeArchetype( + final Optional parent, + final Class baseClass, + final Set> requiredKeys, + final BiConsumer contextExtractor, + final TriFunction, BlockType> assembler + ) { + CustomTypeArchetype.validate(BlockType.class, baseClass, parent); + this.parent = parent; + this.baseClass = baseClass; + this.requiredKeys = Objects.requireNonNull(requiredKeys, "requiredKeys"); + this.contextExtractor = Objects.requireNonNull(contextExtractor, "contextExtractor"); + this.assembler = Objects.requireNonNull(assembler, "assembler"); + } + + public static DefaultedRegistryType registry() { + return SpongeTools.Registries.BLOCK_TYPE_ARCHETYPE; + } + + /** + * @param block The {@link BlockType} + * @return The most appropriate known {@link BlockTypeArchetype} + * @see CustomTypeArchetype#forType(org.spongepowered.api.registry.Registry, CustomTypeArchetype, Object) + */ + public static BlockTypeArchetype forType(final BlockType block) { + return CustomTypeArchetype.forType(BlockArchetypes.registry(), BlockArchetypes.DEFAULT.get(), block); + } + + @SuppressWarnings("unchecked") + public static BlockTypeArchetype of( + final Optional parent, + final Class baseClass, + final Set> requiredKeys, + final BiConsumer contextExtractor, + final TriFunction, I> assembler + ) { + return new BlockTypeArchetype( + parent, baseClass, requiredKeys, + (item, context) -> contextExtractor.accept((I) item, context), + (data, context, behaviour) -> (BlockType) assembler.apply(data, context, behaviour) + ); + } +} diff --git a/api/src/main/java/net/hellheim/spongetools/custom/type/block/BlockTypeBuilder.java b/api/src/main/java/net/hellheim/spongetools/custom/type/block/BlockTypeBuilder.java new file mode 100644 index 0000000..d8ed005 --- /dev/null +++ b/api/src/main/java/net/hellheim/spongetools/custom/type/block/BlockTypeBuilder.java @@ -0,0 +1,277 @@ +package net.hellheim.spongetools.custom.type.block; + +import java.util.Collection; +import java.util.List; +import java.util.Set; +import java.util.function.Supplier; + +import org.apache.commons.lang3.ArrayUtils; +import org.spongepowered.api.ResourceKey; +import org.spongepowered.api.Sponge; +import org.spongepowered.api.block.BlockSoundGroup; +import org.spongepowered.api.block.BlockState; +import org.spongepowered.api.block.BlockType; +import org.spongepowered.api.data.Keys; +import org.spongepowered.api.data.type.InstrumentType; +import org.spongepowered.api.data.type.PushReaction; +import org.spongepowered.api.map.color.MapColorType; +import org.spongepowered.api.registry.RegistryType; +import org.spongepowered.api.state.StateProperty; + +import net.hellheim.spongetools.custom.type.CustomTypeBuilder; +import net.hellheim.spongetools.resourcepack.block.StatePropertyValue; +import net.hellheim.spongetools.util.ModelUtil; +import net.hellheim.spongetools.util.TranslationUtil; + +/** + * {@link CustomTypeBuilder} for {@link BlockType}. + */ +public interface BlockTypeBuilder extends + CustomTypeBuilder.TypeBased, + CustomTypeBuilder.WithData { + + /** + * Creates the new {@link BlockTypeBuilder}. + * + * @return The new builder + */ + static BlockTypeBuilder create() { + return Sponge.game().builderProvider().provide(BlockTypeBuilder.class); + } + + /** + * Returns the mandatory registry dependencies that must be used when registering custom type.
+ * These registries include: + *
    + *
  • {@link BlockTypeArchetype#registry()} + *
+ * + * @return The registry types + * @see #dependencies(RegistryType...) + */ + static RegistryType[] dependencies() { + return new RegistryType[] { + BlockTypeArchetype.registry() + }; + } + + /** + * Returns the registry dependencies from {@link #dependencies()}, and the additional given ones. + * + * @param registryTypes The additional registry dependencies + * @return The registry types + */ + static RegistryType[] dependencies(final RegistryType... registryTypes) { + return ArrayUtils.addAll(BlockTypeBuilder.dependencies(), registryTypes); + } + + /** + * Sets {@link #translationKey(ResourceKey)} and {@link #lootTable(ResourceKey)}. + */ + default BlockTypeBuilder id(final ResourceKey key) { + return this.translationKey(key).lootTable(ModelUtil.withPrefix(key, "blocks/")); + } + + /** + * Sets the {@link BlockTypeKeys#TRANSLATION_KEY}. + */ + default BlockTypeBuilder translationKey(final ResourceKey key) { + return this.set(BlockTypeKeys.TRANSLATION_KEY, TranslationUtil.block(key).key()); + } + + /** + * Sets the {@link BlockTypeKeys#LOOT_TABLE}. + */ + default BlockTypeBuilder lootTable(final ResourceKey key) { + return this.add(BlockTypeKeys.LOOT_TABLE, key); + } + + /** + * Sets the {@link BlockTypeKeys#STATE_PROPERTIES}. + */ + default BlockTypeBuilder stateProperties(final StateProperty... properties) { + return this.set(BlockTypeKeys.STATE_PROPERTIES, Set.of(properties)); + } + + /** + * Sets the {@link BlockTypeKeys#STATE_PROPERTIES}. + */ + default BlockTypeBuilder stateProperties(final Collection> properties) { + return this.set(BlockTypeKeys.STATE_PROPERTIES, Set.copyOf(properties)); + } + + /** + * Sets the {@link BlockTypeKeys#DEFAULT_STATE}. + */ + default BlockTypeBuilder defaultState(final StatePropertyValue... properties) { + return this.set(BlockTypeKeys.DEFAULT_STATE, List.of(properties)); + } + + /** + * Sets the {@link BlockTypeKeys#DEFAULT_STATE}. + */ + default BlockTypeBuilder defaultState(final Collection> properties) { + return this.set(BlockTypeKeys.DEFAULT_STATE, List.copyOf(properties)); + } + + /** + * Sets the {@link BlockTypeKeys#SOUND_GROUP}. + */ + default BlockTypeBuilder sound(final BlockSoundGroup soundGroup) { + return this.add(BlockTypeKeys.SOUND_GROUP, soundGroup); + } + + /** + * Sets the {@link BlockTypeKeys#MAP_COLOR}. + */ + default BlockTypeBuilder mapColor(final MapColorType mapColor) { + return this.add(BlockTypeKeys.MAP_COLOR, mapColor); + } + + /** + * Sets the {@link BlockTypeKeys#MAP_COLOR}. + */ + default BlockTypeBuilder mapColor(final Supplier mapColor) { + return this.supply(BlockTypeKeys.MAP_COLOR, mapColor); + } + + /** + * Sets the {@link Keys#REPRESENTED_INSTRUMENT}. + */ + default BlockTypeBuilder instrument(final InstrumentType instrument) { + return this.add(Keys.REPRESENTED_INSTRUMENT, instrument); + } + + /** + * Sets the {@link Keys#REPRESENTED_INSTRUMENT}. + */ + default BlockTypeBuilder instrument(final Supplier instrument) { + return this.supply(Keys.REPRESENTED_INSTRUMENT, instrument); + } + + /** + * Sets the {@link Keys#DESTROY_SPEED}.
+ *
+ * Defines the strength of the block for destruction.
+ * The higher this value, the longer it will take for block to be destroyed.
+ * Special value of -1 makes block unbreakable and not movable by piston. + */ + default BlockTypeBuilder destroyResistance(final double resistance) { + return this.add(Keys.DESTROY_SPEED, resistance); + } + + /** + * Sets the {@link Keys#BLAST_RESISTANCE}.
+ *
+ * Defines the strength of the block for explosion.
+ * The higher this value, the less likely the block will be affected by explosions. + */ + default BlockTypeBuilder blastResistance(final double resistance) { + return this.add(Keys.BLAST_RESISTANCE, resistance); + } + + /** + * Sets {@link #destroyResistance(double)} and {@link #blastResistance(double)}. + */ + default BlockTypeBuilder resistance(final double destroy, final double blast) { + return this.destroyResistance(destroy).blastResistance(blast); + } + + /** + * Sets {@link #destroyResistance(double)} and {@link #blastResistance(double)}. + */ + default BlockTypeBuilder resistance(final double resistance) { + return this.resistance(resistance, resistance); + } + + /** + * Sets {@link #destroyResistance(double)} and {@link #blastResistance(double)} to 0. + */ + default BlockTypeBuilder instabreak() { + return this.resistance(0); + } + + /** + * Sets the {@link BlockTypeKeys#REQUIRE_TOOL} to true. + */ + default BlockTypeBuilder requireTool() { + return this.add(BlockTypeKeys.REQUIRE_TOOL, true); + } + + /** + * Sets the {@link BlockTypeKeys#SPEED_FACTOR}. + */ + default BlockTypeBuilder speedFactor(final double factor) { + return this.add(BlockTypeKeys.SPEED_FACTOR, factor); + } + + /** + * Sets the {@link BlockTypeKeys#JUMP_FACTOR}. + */ + default BlockTypeBuilder jumpFactor(final double factor) { + return this.add(BlockTypeKeys.JUMP_FACTOR, factor); + } + + /** + * Sets the {@link BlockTypeKeys#FRICTION_FACTOR}. + */ + default BlockTypeBuilder frictionFactor(final double factor) { + return this.add(BlockTypeKeys.FRICTION_FACTOR, factor); + } + + /** + * Sets {@link #speedFactor(double)} and {@link #jumpFactor(double)}. + */ + default BlockTypeBuilder movement(final double speed, final double jump) { + return this.speedFactor(speed).jumpFactor(jump); + } + + /** + * Sets {@link #speedFactor(double)}, {@link #jumpFactor(double)} and {@link #frictionFactor(double)}. + */ + default BlockTypeBuilder movement(final double speed, final double jump, final double friction) { + return this.speedFactor(speed).jumpFactor(jump).frictionFactor(friction); + } + + /** + * Sets the {@link Keys#PUSH_REACTION}. + */ + default BlockTypeBuilder pushReaction(final PushReaction reaction) { + return this.add(Keys.PUSH_REACTION, reaction); + } + + /** + * Sets the {@link Keys#PUSH_REACTION}. + */ + default BlockTypeBuilder pushReaction(final Supplier reaction) { + return this.supply(Keys.PUSH_REACTION, reaction); + } + + /** + * Sets the {@link Keys#BURNABLE} to true. + */ + default BlockTypeBuilder burnable() { + return this.add(Keys.BURNABLE, true); + } + + /** + * Sets the {@link BlockTypeKeys#HAS_DYNAMIC_SHAPE} to true. + */ + default BlockTypeBuilder dynamicShape() { + return this.add(BlockTypeKeys.HAS_DYNAMIC_SHAPE, true); + } + + /** + * Sets the {@link BlockTypeKeys#HAS_COLLISION} to false. + */ + default BlockTypeBuilder noCollision() { + return this.add(BlockTypeKeys.HAS_COLLISION, false); + } + + /** + * Sets the {@link BlockTypeKeys#HAS_OCCLUSION} to false. + */ + default BlockTypeBuilder noOcclusion() { + return this.add(BlockTypeKeys.HAS_OCCLUSION, false); + } +} diff --git a/api/src/main/java/net/hellheim/spongetools/custom/type/block/BlockTypeKeys.java b/api/src/main/java/net/hellheim/spongetools/custom/type/block/BlockTypeKeys.java new file mode 100644 index 0000000..b1dcea7 --- /dev/null +++ b/api/src/main/java/net/hellheim/spongetools/custom/type/block/BlockTypeKeys.java @@ -0,0 +1,100 @@ +package net.hellheim.spongetools.custom.type.block; + +import java.util.List; +import java.util.Set; + +import org.spongepowered.api.ResourceKey; +import org.spongepowered.api.block.BlockSoundGroup; +import org.spongepowered.api.block.BlockState; +import org.spongepowered.api.block.BlockType; +import org.spongepowered.api.data.Key; +import org.spongepowered.api.data.type.ToolRule; +import org.spongepowered.api.data.value.Value; +import org.spongepowered.api.map.color.MapColorType; +import org.spongepowered.api.state.StateProperty; + +import io.leangen.geantyref.TypeToken; +import net.hellheim.spongetools.SpongeTools; +import net.hellheim.spongetools.object.TypedKey; +import net.hellheim.spongetools.resourcepack.block.StatePropertyValue; + +public final class BlockTypeKeys { + + /** + * The properties of default {@link BlockState}. + * + * @see BlockType#defaultState() + */ + public static final TypedKey>> DEFAULT_STATE = TypedKey.of(SpongeTools.key("default_state"), new TypeToken>>() {}); + + /** + * The set of used properties to populate valid {@link BlockState}s. + * + * @see BlockType#validStates() + */ + public static final TypedKey>> STATE_PROPERTIES = TypedKey.of(SpongeTools.key("state_properties"), new TypeToken>>() {}); + + /** + * @see BlockType#asComponent() + */ + public static final TypedKey TRANSLATION_KEY = TypedKey.of(SpongeTools.key("translation_key"), String.class); + + /** + * Affects the slipperiness of the block.
+ * The closer value to 1, the more slippery the block is.
+ * Defautls to 0.6. + */ + public static final Key> FRICTION_FACTOR = SpongeTools.Keys.FRICTION_FACTOR; + + /** + * TODO doc + */ + public static final Key> HAS_COLLISION = SpongeTools.Keys.HAS_COLLISION; + + /** + * TODO doc + */ + public static final Key> HAS_DYNAMIC_SHAPE = SpongeTools.Keys.HAS_DYNAMIC_SHAPE; + + /** + * TODO doc + */ + public static final Key> HAS_OCCLUSION = SpongeTools.Keys.HAS_OCCLUSION; + + /** + * Affects the jump strength on the block.
+ * The closer value to 0, the lower the jump is. + * Defaults to 1. + */ + public static final Key> JUMP_FACTOR = SpongeTools.Keys.JUMP_FACTOR; + + /** + * Block drops. + */ + public static final Key> LOOT_TABLE = SpongeTools.Keys.LOOT_TABLE_KEY; + + /** + * Defines the color the block is represented by on a map. + */ + public static final Key> MAP_COLOR = SpongeTools.Keys.MAP_COLOR_TYPE; + + /** + * Defines whether the block requires correct {@link ToolRule} for efficient breaking and loot drops. + */ + public static final Key> REQUIRE_TOOL = SpongeTools.Keys.REQUIRE_TOOL; + + /** + * @see BlockType#soundGroup() + */ + public static final Key> SOUND_GROUP = SpongeTools.Keys.BLOCK_SOUND_GROUP; + + /** + * Affects movement speed on the block.
+ * The closer value to 0, the lower the speed is.
+ * Defaults to 1. + */ + public static final Key> SPEED_FACTOR = SpongeTools.Keys.SPEED_FACTOR; + + private BlockTypeKeys() { + } +} diff --git a/api/src/main/java/net/hellheim/spongetools/custom/type/block/EnumStatePropertyValue.java b/api/src/main/java/net/hellheim/spongetools/custom/type/block/EnumStatePropertyValue.java new file mode 100644 index 0000000..8da1757 --- /dev/null +++ b/api/src/main/java/net/hellheim/spongetools/custom/type/block/EnumStatePropertyValue.java @@ -0,0 +1,11 @@ +package net.hellheim.spongetools.custom.type.block; + +import org.spongepowered.api.data.type.StringRepresentable; +import org.spongepowered.api.state.EnumStateProperty; + +/** + * This interface is supposed to be implemented by classes used to create + * {@link EnumStateProperty} via {@link StateProperties#enumProperty}. + */ +public interface EnumStatePropertyValue extends StringRepresentable { +} diff --git a/api/src/main/java/net/hellheim/spongetools/custom/type/block/ModeledBlock.java b/api/src/main/java/net/hellheim/spongetools/custom/type/block/ModeledBlock.java new file mode 100644 index 0000000..c1b8cac --- /dev/null +++ b/api/src/main/java/net/hellheim/spongetools/custom/type/block/ModeledBlock.java @@ -0,0 +1,156 @@ +package net.hellheim.spongetools.custom.type.block; + +import java.util.HashSet; +import java.util.Map; +import java.util.Objects; +import java.util.Set; +import java.util.function.Function; +import java.util.function.UnaryOperator; + +import org.spongepowered.api.ResourceKey; +import org.spongepowered.api.block.BlockState; +import org.spongepowered.api.block.BlockType; +import org.spongepowered.api.registry.DefaultedRegistryType; +import org.spongepowered.api.util.Tuple; + +import com.google.common.collect.Sets; + +import net.hellheim.spongetools.SpongeTools; +import net.hellheim.spongetools.custom.type.ModeledCustomType; +import net.hellheim.spongetools.resourcepack.Model; +import net.hellheim.spongetools.resourcepack.ModelLike; +import net.hellheim.spongetools.resourcepack.ModelTemplateProvider; +import net.hellheim.spongetools.resourcepack.block.StateDispatch; +import net.hellheim.spongetools.resourcepack.block.StateSelector; +import net.hellheim.spongetools.resourcepack.block.Variant; +import net.hellheim.spongetools.resourcepack.block.VariantList; +import net.hellheim.spongetools.resourcepack.block.VariantListLike; +import net.hellheim.spongetools.util.ModelUtil; + +/** + * {@link ModeledCustomType} for {@link BlockType}. + * + * @see #registry() + * @see #builder(ResourceKey) + */ +public record ModeledBlock( + BlockType type, + StateDispatch providers, + StateDispatch variants, + Map models + ) implements ModeledCustomType { + + public ModeledBlock( + final BlockType type, final StateDispatch providers, + final StateDispatch variants, final Map models + ) { + this.type = Objects.requireNonNull(type, "type"); + this.providers = Objects.requireNonNull(providers, "providers"); + this.variants = Objects.requireNonNull(variants, "variants"); + this.models = Map.copyOf(models); + } + + public static DefaultedRegistryType registry() { + return SpongeTools.Registries.MODELED_BLOCK; + } + + public static Builder builder(final ResourceKey key) { + return new Builder(key); + } + + public Set> uniqueModels() { + final Set> set = new HashSet<>(); + StateSelector.populate(Sets.union(this.providers.properties(), this.variants.properties())).forEach( + selector -> set.add(Tuple.of(this.providers.getFor(selector), this.variants.getFor(selector)))); + return set; + } + + public Tuple model(final BlockState state) { + return Tuple.of(this.providers.getFor(state), this.variants.getFor(state)); + } + + public static final class Builder + extends ModeledCustomType.Builder { + + private final ResourceKey prefixedKey; + private StateDispatch providers; + private StateDispatch variants; + + public Builder(final ResourceKey key) { + super(key); + this.prefixedKey = ModelUtil.withBlockPrefix(this.key); + this.reset(); + } + + public Builder providers(final StateDispatch providers) { + this.providers = Objects.requireNonNull(providers, "providers").map(Function.identity()); + return this; + } + + public Builder providers(final StateDispatch.Builder builder) { + return this.providers(Objects.requireNonNull(builder, "builder").build()); + } + + public Builder provider(final BlockStateProvider provider) { + return this.providers(StateDispatch.of(provider)); + } + + public Builder variants(final StateDispatch variants) { + this.variants = Objects.requireNonNull(variants, "variants").map(VariantListLike::asVariantList); + return this; + } + + public Builder variants(final StateDispatch.Builder builder) { + return this.variants(Objects.requireNonNull(builder, "builder").build()); + } + + public Builder variants(final VariantListLike variants) { + return this.variants(StateDispatch.of(variants)); + } + + public Builder variants(final Function> variants) { + return this.variants(variants.apply(this.prefixedKey)); + } + + public Builder model(final UnaryOperator key, final ModelLike model) { + return this.model(key.apply(this.prefixedKey), model); + } + + public Builder model(final String keySuffix, final ModelLike model) { + return this.model(key -> ModelUtil.withSuffix(key, keySuffix), model); + } + + public Builder simpleModel(final ModelLike model) { + return this + .variants(Variant.model(this.prefixedKey)) + .model(this.prefixedKey, model); + } + + public Builder simpleModel(final Function model) { + return this.simpleModel(model.apply(this.prefixedKey)); + } + + public Builder simpleModel(final ModelTemplateProvider.T1 templateProvider) { + return this.simpleModel(templateProvider::textured); + } + + @Override + public Builder reset() { + this.providers = null; + this.variants = StateDispatch.of(Variant.model(this.prefixedKey).asVariantList()); + return super.reset(); + } + + @Override + public ModeledBlock build() { + if (this.providers == null) { + throw new IllegalStateException("providers must be set"); + } + + final BlockType block = this.buildType(BlockTypeBuilder.create().id(this.key)); + final ModeledBlock modeledBlock = new ModeledBlock(block, this.providers, this.variants, this.models); + modeledBlock.uniqueModels(); // Validation + return modeledBlock; + } + } +} diff --git a/api/src/main/java/net/hellheim/spongetools/custom/type/block/StateProperties.java b/api/src/main/java/net/hellheim/spongetools/custom/type/block/StateProperties.java new file mode 100644 index 0000000..ece366a --- /dev/null +++ b/api/src/main/java/net/hellheim/spongetools/custom/type/block/StateProperties.java @@ -0,0 +1,114 @@ +package net.hellheim.spongetools.custom.type.block; + +import java.util.Arrays; +import java.util.List; +import java.util.function.Predicate; + +import org.spongepowered.api.Sponge; +import org.spongepowered.api.state.BooleanStateProperty; +import org.spongepowered.api.state.EnumStateProperty; +import org.spongepowered.api.state.IntegerStateProperty; +import org.spongepowered.api.state.StateProperty; + +import com.google.common.collect.Streams; + +/** + * Class for creating {@link StateProperty}s. + */ +public final class StateProperties { + + /** + * Creates new {@link BooleanStateProperty}. + * + * @param name The property name + * @return The new state property + */ + public static BooleanStateProperty booleanProperty(final String name) { + return StateProperties.factory().booleanProperty(name); + } + + /** + * Creates new {@link IntegerStateProperty}. + * + * @param name The property name + * @param min The minimum value of the property + * @param max The maximum value of the property + * @return The new state property + * @throws IllegalArgumentException if min is less than 0, or max is less than or equal to min + */ + public static IntegerStateProperty integerProperty(final String name, final int min, final int max) { + return StateProperties.factory().integerProperty(name, min, max); + } + + /** + * Creates new {@link EnumStateProperty} with all the values of the given enum class. + * + * @param The type of the property values + * @param name The property name + * @param clazz The the property values class + * @return The new state property + * @throws IllegalArgumentException if enum is empty + */ + public static & EnumStatePropertyValue> EnumStateProperty enumProperty(final String name, final Class clazz) { + return StateProperties.factory().enumProperty(name, clazz, List.of(clazz.getEnumConstants())); + } + + /** + * Creates new {@link EnumStateProperty} with all the values of the given enum class with applied filter. + * + * @param The type of the property values + * @param name The property name + * @param clazz The the property values class + * @param filter The filter to apply to values + * @return The new state property + * @throws IllegalArgumentException if final values are empty + */ + public static & EnumStatePropertyValue> EnumStateProperty enumProperty(final String name, final Class clazz, final Predicate filter) { + return StateProperties.factory().enumProperty(name, clazz, Arrays.stream(clazz.getEnumConstants()).filter(filter).toList()); + } + + /** + * Creates new {@link EnumStateProperty} with the given values. + * + * @param The type of the property values + * @param name The property name + * @param clazz The the property values class + * @param values The allowed property values + * @return The new state property + * @throws IllegalArgumentException if no values are provided + */ + @SafeVarargs + public static & EnumStatePropertyValue> EnumStateProperty enumProperty(final String name, final Class clazz, final E... values) { + return StateProperties.factory().enumProperty(name, clazz, List.of(values)); + } + + /** + * Creates new {@link EnumStateProperty} with the given values. + * + * @param The type of the property values + * @param name The property name + * @param clazz The the property values class + * @param values The allowed property values + * @return The new state property + * @throws IllegalArgumentException if no values are provided + */ + public static & EnumStatePropertyValue> EnumStateProperty enumProperty(final String name, final Class clazz, final Iterable values) { + return StateProperties.factory().enumProperty(name, clazz, Streams.stream(values).toList()); + } + + private static Factory factory() { + return Sponge.game().factoryProvider().provide(Factory.class); + } + + public interface Factory { + + BooleanStateProperty booleanProperty(String name); + + IntegerStateProperty integerProperty(String name, int min, int max); + + & EnumStatePropertyValue> EnumStateProperty enumProperty(String name, Class clazz, List values); + } + + private StateProperties() { + } +} diff --git a/api/src/main/java/net/hellheim/spongetools/custom/type/entity/AttributeBuilder.java b/api/src/main/java/net/hellheim/spongetools/custom/type/entity/AttributeBuilder.java new file mode 100644 index 0000000..8522289 --- /dev/null +++ b/api/src/main/java/net/hellheim/spongetools/custom/type/entity/AttributeBuilder.java @@ -0,0 +1,73 @@ +package net.hellheim.spongetools.custom.type.entity; + +import java.util.Objects; + +import org.spongepowered.api.ResourceKey; +import org.spongepowered.api.Sponge; +import org.spongepowered.api.entity.attribute.type.AttributeType; +import org.spongepowered.api.registry.RegistryTypes; +import org.spongepowered.api.util.Builder; +import org.spongepowered.api.util.Tristate; + +/** + * Builder for creating custom {@link AttributeType}s.
+ * All attribute types created via this builder must be registered to {@link RegistryTypes#ATTRIBUTE_TYPE}. + */ +public interface AttributeBuilder extends Builder { + + /** + * Returns the new {@link AttributeBuilder}. + * + * @return The new builder + */ + public static AttributeBuilder create() { + return Sponge.game().builderProvider().provide(AttributeBuilder.class); + } + + /** + * Sets the translation key from the given {@link ResourceKey}. + * + * @param key The resource key + * @return This builder, for chaining + */ + default AttributeBuilder translationKey(final ResourceKey key) { + Objects.requireNonNull(key, "key"); + return this.translationKey("attribute.name." + key.namespace() + "." + key.value()); + } + + /** + * Sets the translation key of the attribute. + * + * @param key The translation key + * @return This builder, for chaining + */ + AttributeBuilder translationKey(String key); + + /** + * Sets the default value of the attribute. + * + * @param defaultValue The default value + * @return This builder, for chaining + */ + AttributeBuilder defaultValue(double defaultValue); + + /** + * Sets the sentiment of the attribute. The given value is interpreted in the following way:
+ * {@link Tristate#TRUE} -> Positive sentiment
+ * {@link Tristate#UNDEFINED} -> Neutral sentiment
+ * {@link Tristate#FALSE} -> Negative sentiment + * + * @param sentiment The sentiment + * @return This builder, for chaining + */ + AttributeBuilder sentiment(Tristate sentiment); + + /** + * Sets the range of the possible attribute values. + * + * @param min The min value + * @param max The max value + * @return This builder, for chaining + */ + AttributeBuilder ranged(double min, double max); +} diff --git a/api/src/main/java/net/hellheim/spongetools/custom/type/entity/EntityArchetypes.java b/api/src/main/java/net/hellheim/spongetools/custom/type/entity/EntityArchetypes.java new file mode 100644 index 0000000..ffbfc91 --- /dev/null +++ b/api/src/main/java/net/hellheim/spongetools/custom/type/entity/EntityArchetypes.java @@ -0,0 +1,77 @@ +package net.hellheim.spongetools.custom.type.entity; + +import org.spongepowered.api.Sponge; +import org.spongepowered.api.entity.Aerial; +import org.spongepowered.api.entity.Entity; +import org.spongepowered.api.entity.living.Agent; +import org.spongepowered.api.entity.living.Living; +import org.spongepowered.api.entity.living.Monster; +import org.spongepowered.api.entity.living.PathfinderAgent; +import org.spongepowered.api.registry.DefaultedRegistryReference; +import org.spongepowered.api.registry.Registry; +import org.spongepowered.api.registry.RegistryKey; + +import net.hellheim.spongetools.SpongeTools; + +public final class EntityArchetypes { + + /** + * {@link Aerial}-based archetype.
+ * Parent archetype: {@link #AGENT}. + */ + public static final DefaultedRegistryReference AERIAL = EntityArchetypes.key("aerial"); + + /** + * {@link Agent}-based archetype.
+ * Parent archetype: {@link #LIVING}. + */ + public static final DefaultedRegistryReference AGENT = EntityArchetypes.key("agent"); + + /** + * {@link Entity}-based archetype.
+ *
+ * Required Context:
+ * - {@link EntityTypeKeys#CATEGORY}
+ * - {@link EntityTypeKeys#TRANSLATION_KEY}
+ *
+ * Supported Context:
+ * - {@link EntityTypeKeys#FLAMMABLE}
+ * - {@link EntityTypeKeys#SPAWN_AWAY_FROM_PLAYER}
+ * - {@link EntityTypeKeys#SUMMONABLE}
+ * - {@link EntityTypeKeys#TRANSIENT}
+ * - {@link EntityTypeKeys#LOOT_TABLE}
+ *
+ * Supported behaviour:
+ * - TODO + */ + public static final DefaultedRegistryReference ENTITY = EntityArchetypes.key("entity"); + + /** + * {@link Living}-based archetype.
+ * Parent archetype: {@link #ENTITY}. + */ + public static final DefaultedRegistryReference LIVING = EntityArchetypes.key("living"); + + /** + * {@link Monster}-based archetype.
+ * Parent archetype: {@link #PATHFINDER_AGENT}. + */ + public static final DefaultedRegistryReference MONSTER = EntityArchetypes.key("monster"); + + /** + * {@link PathfinderAgent}-based archetype.
+ * Parent archetype: {@link #AGENT}. + */ + public static final DefaultedRegistryReference PATHFINDER_AGENT = EntityArchetypes.key("pathfinder_agent"); + + private EntityArchetypes() { + } + + public static Registry registry() { + return EntityTypeArchetype.registry().get(); + } + + private static DefaultedRegistryReference key(final String key) { + return RegistryKey.of(EntityTypeArchetype.registry(), SpongeTools.key(key)).asDefaultedReference(Sponge::game); + } +} diff --git a/api/src/main/java/net/hellheim/spongetools/custom/type/entity/EntityBehaviour.java b/api/src/main/java/net/hellheim/spongetools/custom/type/entity/EntityBehaviour.java new file mode 100644 index 0000000..4670da5 --- /dev/null +++ b/api/src/main/java/net/hellheim/spongetools/custom/type/entity/EntityBehaviour.java @@ -0,0 +1,69 @@ +package net.hellheim.spongetools.custom.type.entity; + +import org.spongepowered.api.entity.living.Living; +import org.spongepowered.api.entity.living.player.server.ServerPlayer; + +import net.hellheim.spongetools.custom.behaviour.Behaviour; +import net.hellheim.spongetools.custom.behaviour.BehaviourArgs; + +@FunctionalInterface +public interface EntityBehaviour extends Behaviour { + + interface JumpStart extends EntityBehaviour { + + @Override + default Void call(final Args args) { + this.call(args.power()); + return null; + } + + void call(int power); + + interface Args extends BehaviourArgs { + + int power(); + + Args withPower(int power); + } + } + + interface OpenInventory extends EntityBehaviour { + + @Override + default Void call(final Args args) { + this.call(args.player()); + return null; + } + + void call(ServerPlayer player); + + interface Args extends BehaviourArgs { + + ServerPlayer player(); + + Args withPlayer(ServerPlayer player); + } + } + + interface PerformRangedAttack extends EntityBehaviour { + + @Override + default Void call(final Args args) { + this.call(args.target(), args.velocity()); + return null; + } + + void call(Living target, double velocity); + + interface Args extends BehaviourArgs { + + Living target(); + + double velocity(); + + Args withTarget(Living target); + + Args withVelocity(double velocity); + } + } +} diff --git a/api/src/main/java/net/hellheim/spongetools/custom/type/entity/EntityBehaviours.java b/api/src/main/java/net/hellheim/spongetools/custom/type/entity/EntityBehaviours.java new file mode 100644 index 0000000..0f81bf1 --- /dev/null +++ b/api/src/main/java/net/hellheim/spongetools/custom/type/entity/EntityBehaviours.java @@ -0,0 +1,21 @@ +package net.hellheim.spongetools.custom.type.entity; + +import net.hellheim.spongetools.SpongeTools; +import net.hellheim.spongetools.custom.behaviour.Behaviour; +import net.hellheim.spongetools.custom.behaviour.BehaviourType; + +public final class EntityBehaviours { + + public static final BehaviourType JUMP_READY = BehaviourType.of(SpongeTools.key("jump_ready")); + + public static final BehaviourType JUMP_START = BehaviourType.of(SpongeTools.key("jump_start")); + + public static final BehaviourType OPEN_INVENTORY = BehaviourType.of(SpongeTools.key("open_inventory")); + + public static final BehaviourType PERFORM_RANGED_ATTACK = BehaviourType.of(SpongeTools.key("perform_ranged_attack")); + + public static final BehaviourType REGISTER_GOALS = BehaviourType.of(SpongeTools.key("register_goals")); + + private EntityBehaviours() { + } +} diff --git a/api/src/main/java/net/hellheim/spongetools/custom/type/entity/EntityClientActions.java b/api/src/main/java/net/hellheim/spongetools/custom/type/entity/EntityClientActions.java new file mode 100644 index 0000000..d30ae28 --- /dev/null +++ b/api/src/main/java/net/hellheim/spongetools/custom/type/entity/EntityClientActions.java @@ -0,0 +1,85 @@ +package net.hellheim.spongetools.custom.type.entity; + +public final class EntityClientActions { + + public static final byte ARROW_PARTICLES = 1; + public static final byte RABBIT_JUMP = 1; + public static final byte SPAWNER_MINECART_RESET_DELAY = 1; + public static final byte LIVING_DEATH = 3; + public static final byte EGG_PARTICLES = 3; + public static final byte SNOWBALL_PARTICLES = 3; + public static final byte EVOKER_ATTACK = 4; + public static final byte IRON_GOLEM_ATTACK = 4; + public static final byte CREAKING_ATTACK = 4; + public static final byte RAVAGER_ATTACK = 4; + public static final byte WARDEN_ATTACK = 4; + public static final byte ZOGLIN_ATTACK = 4; + public static final byte HOGLIN_ATTACK = 4; + // TODO what is this for + public static final byte STOP_ATTACKING = 5; + public static final byte HORSELIKE_TAMING_FAILED = 6; + public static final byte TAMEABLE_ANIMA_TAMING_FAILED = 6; + public static final byte HORSELIKE_TAMING_SUCCEEDED = 7; + public static final byte TAMEABLE_ANIMAL_TAMING_SUCCEEDED = 7; + public static final byte WOLF_SHAKE_WETNESS = 8; + public static final byte PLAYER_USE_ITEM_COMPLETE = 9; + public static final byte SHEEP_EAT_GRASS = 10; + public static final byte TNT_MINECART_PRIME_FUSE = 10; + public static final byte IRON_GOLEM_OFFER_FLOWER_START = 11; + public static final byte VILLAGER_LOVE_HEARTS = 12; + public static final byte VILLAGER_ANGRY = 13; + public static final byte VILLAGER_HAPPY = 14; + public static final byte WITCH_HAT_MAGIC = 15; + public static final byte ZOMBIE_VILLAGER_CONVERTING = 16; + public static final byte FIREWORKS_EXPLODE = 17; + public static final byte ANIMAL_IN_LOVE_HEARTS = 18; + public static final byte ALLAY_IN_LOVE_HEARTS = 18; + public static final byte SQUID_ANIM_RESET = 19; + /** + * In vanilla effectively the same as {@link #LIVING_POOF}. + */ + public static final byte AGENT_SPAWN_ANIM = 20; + public static final byte GUARDIAN_ATTACK_SOUND = 21; + public static final byte PLAYER_REDUCED_DEBUG_INFO = 22; + public static final byte PLAYER_FULL_DEBUG_INFO = 23; + public static final byte PLAYER_PERMISSION_LEVEL_ALL = 24; + public static final byte PLAYER_PERMISSION_LEVEL_MODERATORS = 25; + public static final byte PLAYER_PERMISSION_LEVEL_GAMEMASTERS = 26; + public static final byte PLAYER_PERMISSION_LEVEL_ADMINS = 27; + public static final byte PLAYER_PERMISSION_LEVEL_OWNERS = 28; + public static final byte LIVING_SHIELD_BLOCK = 29; + public static final byte LIVING_SHIELD_BREAK = 30; + public static final byte FISHING_ROD_REEL_IN = 31; + public static final byte ARMORSTAND_WOBBLE = 32; + public static final byte IRON_GOLEM_OFFER_FLOWER_STOP = 34; + public static final byte ENTITY_PROTECTED_FROM_DEATH = 35; + public static final byte DOLPHIN_LOOKING_FOR_TREASURE = 38; + public static final byte RAVAGER_STUNNED = 39; + public static final byte OCELOT_TRUSTING_FAILED = 40; + public static final byte OCELOT_TRUSTING_SUCCEEDED = 41; + public static final byte VILLAGER_SWEAT = 42; + public static final byte FOX_EAT = 45; + public static final byte LIVING_TELEPORT_PARTICLES = 46; + public static final byte LIVING_BREAK_MAINHAND = 47; + public static final byte LIVING_BREAK_OFFHAND = 48; + public static final byte LIVING_BREAK_HEAD = 49; + public static final byte LIVING_BREAK_CHEST = 50; + public static final byte LIVING_BREAK_LEGS = 51; + public static final byte LIVING_BREAK_FEET = 52; + public static final byte ENTITY_HONEY_SLIDE = 53; + public static final byte LIVING_HONEY_JUMP = 54; + public static final byte LIVING_SWAP_HANDS = 55; + public static final byte WOLF_CANCEL_SHAKE_WETNESS = 56; + public static final byte GOAT_RAM_START = 58; + public static final byte GOAT_RAM_END = 59; + public static final byte LIVING_POOF = 60; + public static final byte WARDEN_TENDRILS_SHIVER = 61; + public static final byte WARDEN_SONIC_CHARGE = 62; + public static final byte SNIFFER_DIGGING_SOUND = 63; + public static final byte ARMADILLO_PEEK = 64; + public static final byte LIVING_BREAK_BODY = 65; + public static final byte CREAKING_SHAKE = 66; + + private EntityClientActions() { + } +} diff --git a/api/src/main/java/net/hellheim/spongetools/custom/type/entity/EntityDefaultAttributes.java b/api/src/main/java/net/hellheim/spongetools/custom/type/entity/EntityDefaultAttributes.java new file mode 100644 index 0000000..4ca8e16 --- /dev/null +++ b/api/src/main/java/net/hellheim/spongetools/custom/type/entity/EntityDefaultAttributes.java @@ -0,0 +1,57 @@ +package net.hellheim.spongetools.custom.type.entity; + +import java.util.HashMap; +import java.util.Map; +import java.util.OptionalDouble; + +import org.spongepowered.api.ResourceKey; +import org.spongepowered.api.entity.attribute.type.AttributeType; +import org.spongepowered.api.registry.RegistryKey; + +public record EntityDefaultAttributes(Map attributes) { + + public EntityDefaultAttributes(final Map attributes) { + this.attributes = Map.copyOf(attributes); + } + + public static Builder builder() { + return new Builder(); + } + + public static final class Builder implements org.spongepowered.api.util.Builder { + + private final Map attributes = new HashMap<>(); + + public Builder add(final RegistryKey attribute, final double base) { + return this.add(attribute.location(), base); + } + + public Builder add(final RegistryKey attribute) { + return this.add(attribute.location()); + } + + public Builder add(final ResourceKey attribute, final double base) { + return this.add(attribute, OptionalDouble.of(base)); + } + + public Builder add(final ResourceKey attribute) { + return this.add(attribute, OptionalDouble.empty()); + } + + public Builder add(final ResourceKey attribute, final OptionalDouble base) { + this.attributes.put(attribute, base); + return this; + } + + @Override + public Builder reset() { + this.attributes.clear(); + return this; + } + + @Override + public EntityDefaultAttributes build() { + return new EntityDefaultAttributes(this.attributes); + } + } +} diff --git a/api/src/main/java/net/hellheim/spongetools/custom/type/entity/EntityDisplay.java b/api/src/main/java/net/hellheim/spongetools/custom/type/entity/EntityDisplay.java new file mode 100644 index 0000000..383f710 --- /dev/null +++ b/api/src/main/java/net/hellheim/spongetools/custom/type/entity/EntityDisplay.java @@ -0,0 +1,27 @@ +package net.hellheim.spongetools.custom.type.entity; + +import org.spongepowered.api.entity.Entity; + +public interface EntityDisplay { + + /** + * Returns the type used to create {@link #display()}. + * + * @return The entity display type + */ + EntityDisplayType type(); + + /** + * Returns the owner entity of its display entities. + * + * @return The owner entity + */ + Entity owner(); + + /** + * Return the entity client sees. + * + * @return The display entity + */ + Entity display(); +} diff --git a/api/src/main/java/net/hellheim/spongetools/custom/type/entity/EntityDisplayType.java b/api/src/main/java/net/hellheim/spongetools/custom/type/entity/EntityDisplayType.java new file mode 100644 index 0000000..1aac75c --- /dev/null +++ b/api/src/main/java/net/hellheim/spongetools/custom/type/entity/EntityDisplayType.java @@ -0,0 +1,45 @@ +package net.hellheim.spongetools.custom.type.entity; + +import java.util.Objects; +import java.util.Optional; + +import org.spongepowered.api.entity.Entity; +import org.spongepowered.api.entity.EntityArchetype; +import org.spongepowered.api.world.volume.archetype.entity.EntityArchetypeEntry; +import org.spongepowered.math.vector.Vector3d; + +public interface EntityDisplayType { + + static Simple simple(final EntityArchetypeEntry entity) { + return EntityDisplayType.simple(entity.position(), entity.archetype()); + } + + static Simple simple(final EntityArchetype entity) { + return EntityDisplayType.simple(Vector3d.ZERO, entity); + } + + static Simple simple(final Vector3d shift, final EntityArchetype entity) { + return new Simple(shift, entity); + } + + /** + * Creates the display for the given owner. + * + * @param owner + * @return The display + */ + Optional create(Entity owner); + + record Simple(Vector3d shift, EntityArchetype entity) implements EntityDisplayType { + + public Simple(final Vector3d shift, final EntityArchetype entity) { + this.entity = Objects.requireNonNull(entity, "entity"); + this.shift = Objects.requireNonNull(shift, "shift"); + } + + @Override + public Optional create(final Entity owner) { + return this.entity.apply(owner.serverLocation().add(this.shift)); + } + } +} diff --git a/api/src/main/java/net/hellheim/spongetools/custom/type/entity/EntityExtension.java b/api/src/main/java/net/hellheim/spongetools/custom/type/entity/EntityExtension.java new file mode 100644 index 0000000..da7b653 --- /dev/null +++ b/api/src/main/java/net/hellheim/spongetools/custom/type/entity/EntityExtension.java @@ -0,0 +1,37 @@ +package net.hellheim.spongetools.custom.type.entity; + +import java.util.List; + +import org.spongepowered.api.entity.Entity; + +import net.hellheim.spongetools.custom.behaviour.BehaviourHolder; + +public interface EntityExtension extends BehaviourHolder { + + static EntityExtension getFor(final Entity entity) { + return (EntityExtension) entity; + } + + /** + * Returns the owner entity of this extension. + * + * @return The owner entity + */ + Entity owner(); + + /** + * Returns all the entities to display to the client. + * + * @return The entity displays + */ + List display(); + + /** + * Performs the entity action by its id on the client.
+ * Vanilla actions are catalogued in {@link EntityClientActions}.
+ * Only actions supported by entity will have effect on client. + * + * @param action The entity action id + */ + void act(byte action); +} diff --git a/api/src/main/java/net/hellheim/spongetools/custom/type/entity/EntityFlags.java b/api/src/main/java/net/hellheim/spongetools/custom/type/entity/EntityFlags.java new file mode 100644 index 0000000..98a6012 --- /dev/null +++ b/api/src/main/java/net/hellheim/spongetools/custom/type/entity/EntityFlags.java @@ -0,0 +1,84 @@ +package net.hellheim.spongetools.custom.type.entity; + +import org.spongepowered.api.ResourceKey; +import org.spongepowered.api.entity.Angerable; +import org.spongepowered.api.entity.Entity; +import org.spongepowered.api.entity.Leashable; +import org.spongepowered.api.entity.Ranger; +import org.spongepowered.api.entity.Saddleable; +import org.spongepowered.api.entity.ai.goal.Goal; +import org.spongepowered.api.entity.living.Hostile; +import org.spongepowered.api.item.ItemTypes; + +import net.hellheim.spongetools.SpongeTools; + +public final class EntityFlags { + + /** + * Makes {@link Entity} an {@link Angerable}.
+ * Gets notification when player dies, used in some {@link Goal}s. + */ + public static final ResourceKey ANGERABLE = SpongeTools.key("angerable"); + + /** + * Makes {@link Entity} a Bucketable. TODO add interface
+ * Allows using some bucket-pickup logic. + */ + public static final ResourceKey BUCKETABLE = SpongeTools.key("bucketable"); + + /** + * Makes {@link Entity} a {@link Hostile}.
+ * Makes entity targetable by golems, conduit (and sometimes shulker). Cannot be leashed by default. + */ + public static final ResourceKey HOSTILE = SpongeTools.key("hostile"); + + /** + * Makes {@link Entity} an ItemSteerable. TODO add interface
+ * Allows using FoonOnAStick item to perform boost while riding the entity. + */ + public static final ResourceKey ITEM_STEERABLE = SpongeTools.key("item_steerable"); + + /** + * Makes {@link Entity} a {@link Leashable}.
+ * Allows interaction with {@link ItemTypes#LEAD}. + */ + public static final ResourceKey LEASHABLE = SpongeTools.key("leashable"); + + /** + * Makes {@link Entity} a {@link Ranger}.
+ * Allows using some {@link Goal}s. + * - {@link EntityBehaviours#PERFORM_RANGED_ATTACK} + */ + public static final ResourceKey RANGER = SpongeTools.key("ranger"); + + /** + * Makes {@link Entity} an RideableInventory. TODO add interface
+ * Allows riding player to open entity inventory with 'E' button.
+ * Supported behaviour:
+ * - {@link EntityBehaviours#OPEN_INVENTORY} + * + * @implNote player MUST ride vehicle that implements it client-side (boats/horses) + */ + public static final ResourceKey RIDEABLE_INVENTORY = SpongeTools.key("rideable_inventory"); + + /** + * Makes {@link Entity} a RideableJumper. TODO add interface
+ * Adds riding player jump strength bar on client.
+ * Supported behaviour:
+ * - {@link EntityBehaviours#JUMP_READY}
+ * - {@link EntityBehaviours#JUMP_START} + * + * @implNote player MUST ride vehicle that implements it client-side (horses) + */ + public static final ResourceKey RIDEABLE_JUMPER = SpongeTools.key("rideable_jumper"); + + /** + * Makes {@link Entity} a {@link Saddleable}.
+ * Handles hand and dispenser interactions with saddle. + */ + public static final ResourceKey SADDLEABLE = SpongeTools.key("saddleable"); + + private EntityFlags() { + } +} diff --git a/api/src/main/java/net/hellheim/spongetools/custom/type/entity/EntityTypeArchetype.java b/api/src/main/java/net/hellheim/spongetools/custom/type/entity/EntityTypeArchetype.java new file mode 100644 index 0000000..57e9dfc --- /dev/null +++ b/api/src/main/java/net/hellheim/spongetools/custom/type/entity/EntityTypeArchetype.java @@ -0,0 +1,41 @@ +package net.hellheim.spongetools.custom.type.entity; + +import java.util.Optional; + +import org.spongepowered.api.entity.Entity; +import org.spongepowered.api.entity.EntityType; +import org.spongepowered.api.registry.DefaultedRegistryType; +import org.spongepowered.api.util.annotation.CatalogedBy; + +import net.hellheim.spongetools.SpongeTools; +import net.hellheim.spongetools.custom.type.CustomTypeArchetype; + +/** + * {@link CustomTypeArchetype} of {@link EntityType} used for {@link EntityTypeBuilder}. + */ +@CatalogedBy(EntityArchetypes.class) +public record EntityTypeArchetype(Optional parent, Class baseClass) + implements CustomTypeArchetype.InstanceBased, Entity, EntityTypeArchetype> { + + public EntityTypeArchetype(final Optional parent, final Class baseClass) { + CustomTypeArchetype.validate(Entity.class, baseClass, parent); + this.parent = parent; + this.baseClass = baseClass; + } + + public static DefaultedRegistryType registry() { + return SpongeTools.Registries.ENTITY_TYPE_ARCHETYPE; + } + + public static EntityTypeArchetype of(final Optional parent, final Class baseClass) { + return new EntityTypeArchetype(parent, baseClass); + } + + @Override + public Class contextFactory() { + return Factory.class; + } + + public interface Factory extends CustomTypeArchetype.InstanceBased.Factory> { + } +} diff --git a/api/src/main/java/net/hellheim/spongetools/custom/type/entity/EntityTypeBuilder.java b/api/src/main/java/net/hellheim/spongetools/custom/type/entity/EntityTypeBuilder.java new file mode 100644 index 0000000..dc5c4b0 --- /dev/null +++ b/api/src/main/java/net/hellheim/spongetools/custom/type/entity/EntityTypeBuilder.java @@ -0,0 +1,135 @@ +package net.hellheim.spongetools.custom.type.entity; + +import java.util.Collection; +import java.util.Objects; +import java.util.Set; +import java.util.function.Supplier; + +import org.spongepowered.api.ResourceKey; +import org.spongepowered.api.Sponge; +import org.spongepowered.api.entity.Entity; +import org.spongepowered.api.entity.EntityCategory; +import org.spongepowered.api.entity.EntityType; + +import net.hellheim.spongetools.custom.type.CustomTypeBuilder; +import net.hellheim.spongetools.util.ModelUtil; +import net.hellheim.spongetools.util.TranslationUtil; + +public interface EntityTypeBuilder extends + CustomTypeBuilder.InstanceBased, Entity, EntityTypeArchetype, EntityTypeBuilder>, + CustomTypeBuilder.WithData, Entity, EntityTypeArchetype, EntityTypeBuilder> { + + static EntityTypeBuilder create() { + return Sponge.game().builderProvider().provide(EntityTypeBuilder.class); + } + + /** + * Sets {@link #serializationKey(ResourceKey)}, {@link #translationKey(ResourceKey)} + * and {@link #lootTable(ResourceKey)}. + */ + default EntityTypeBuilder id(final ResourceKey key) { + return this.serializationKey(key).translationKey(key).lootTable(ModelUtil.withPrefix(key, "entities/")); + } + + /** + * Sets the {@link EntityTypeKeys#SERIALIZATION_KEY}. + */ + default EntityTypeBuilder serializationKey(final ResourceKey key) { + return this.set(EntityTypeKeys.SERIALIZATION_KEY, key); + } + + /** + * Sets the {@link EntityTypeKeys#TRANSLATION_KEY}. + */ + default EntityTypeBuilder translationKey(final ResourceKey key) { + return this.set(EntityTypeKeys.TRANSLATION_KEY, TranslationUtil.entity(key).key()); + } + + /** + * Sets the {@link EntityTypeKeys#LOOT_TABLE}. + */ + default EntityTypeBuilder lootTable(final ResourceKey key) { + return this.set(EntityTypeKeys.LOOT_TABLE, key); + } + + /** + * Sets the {@link EntityTypeKeys#FLAGS}. + */ + default EntityTypeBuilder flags(final ResourceKey... flags) { + return this.set(EntityTypeKeys.FLAGS, Set.of(flags)); + } + + /** + * Sets the {@link EntityTypeKeys#FLAGS}. + */ + default EntityTypeBuilder flags(final Collection flags) { + return this.set(EntityTypeKeys.FLAGS, Set.copyOf(flags)); + } + + /** + * Sets tee {@link EntityTypeKeys#ATTRIBUTES}. + */ + default EntityTypeBuilder attributes(final EntityDefaultAttributes attributes) { + return this.set(EntityTypeKeys.ATTRIBUTES, attributes); + } + + /** + * Sets tee {@link EntityTypeKeys#ATTRIBUTES}. + */ + default EntityTypeBuilder attributes(final EntityDefaultAttributes.Builder attributes) { + return this.attributes(attributes.build()); + } + + /** + * Sets the {@link EntityTypeKeys#CATEGORY}. + */ + default EntityTypeBuilder category(final EntityCategory category) { + return this.set(EntityTypeKeys.CATEGORY, category); + } + + /** + * Sets the {@link EntityTypeKeys#CATEGORY}. + */ + default EntityTypeBuilder category(final Supplier category) { + return this.category(Objects.requireNonNull(category, "category").get()); + } + + /** + * Sets the {@link EntityTypeKeys#SUMMONABLE} to false. + */ + default EntityTypeBuilder noSummon() { + return this.set(EntityTypeKeys.SUMMONABLE, false); + } + + /** + * Removes the {@link EntityTypeKeys#SERIALIZATION_KEY}. + */ + default EntityTypeBuilder noSave() { + return this.remove(EntityTypeKeys.SERIALIZATION_KEY); + } + + /** + * Sets the {@link EntityTypeKeys#FLAMMABLE} to false. + */ + default EntityTypeBuilder fireImmune() { + return this.set(EntityTypeKeys.FLAMMABLE, false); + } + + /** + * Sets the {@link EntityTypeKeys#SPAWN_AWAY_FROM_PLAYER} to true. + */ + default EntityTypeBuilder canSpawnAwayFromPlayer() { + return this.set(EntityTypeKeys.SPAWN_AWAY_FROM_PLAYER, true); + } + + /** + * Builds and returns typed {@link EntityType}. + * + * @param The type of the entity + * @return The typed entity type + */ + @SuppressWarnings("unchecked") + default EntityType buildTyped() { + return (EntityType) this.build(); + } +} diff --git a/api/src/main/java/net/hellheim/spongetools/custom/type/entity/EntityTypeExtension.java b/api/src/main/java/net/hellheim/spongetools/custom/type/entity/EntityTypeExtension.java new file mode 100644 index 0000000..72c95ce --- /dev/null +++ b/api/src/main/java/net/hellheim/spongetools/custom/type/entity/EntityTypeExtension.java @@ -0,0 +1,19 @@ +package net.hellheim.spongetools.custom.type.entity; + +import java.util.List; + +import org.spongepowered.api.entity.Entity; +import org.spongepowered.api.entity.EntityType; + +import net.hellheim.spongetools.custom.behaviour.BehaviourCallbackHolder; + +public interface EntityTypeExtension extends BehaviourCallbackHolder { + + static EntityTypeExtension getFor(final EntityType type) { + return (EntityTypeExtension) type; + } + + EntityType owner(); + + List display(); +} diff --git a/api/src/main/java/net/hellheim/spongetools/custom/type/entity/EntityTypeKeys.java b/api/src/main/java/net/hellheim/spongetools/custom/type/entity/EntityTypeKeys.java new file mode 100644 index 0000000..9c367e9 --- /dev/null +++ b/api/src/main/java/net/hellheim/spongetools/custom/type/entity/EntityTypeKeys.java @@ -0,0 +1,81 @@ +package net.hellheim.spongetools.custom.type.entity; + +import java.util.List; +import java.util.Set; + +import org.spongepowered.api.ResourceKey; +import org.spongepowered.api.entity.EntityCategory; +import org.spongepowered.api.entity.EntityType; +import org.spongepowered.api.entity.ai.goal.GoalExecutorType; +import org.spongepowered.api.entity.ai.goal.GoalExecutorTypes; +import org.spongepowered.api.entity.attribute.AttributeHolder; +import org.spongepowered.api.entity.living.Agent; + +import io.leangen.geantyref.TypeToken; +import net.hellheim.spongetools.SpongeTools; +import net.hellheim.spongetools.custom.type.entity.goal.GoalTemplate; +import net.hellheim.spongetools.object.TypedKey; + +public final class EntityTypeKeys { + + /** + * The default {@link AttributeHolder}'s attributes. + */ + public static final TypedKey ATTRIBUTES = TypedKey.of(SpongeTools.key("attributes"), EntityDefaultAttributes.class); + + /** + * @see EntityType#category() + */ + public static final TypedKey CATEGORY = TypedKey.of(SpongeTools.key("category"), EntityCategory.class); + + /** + * The list of flags that built entity should implement.
+ * The supported flags are catalogued in {@link EntityFlags}. + */ + public static final TypedKey> FLAGS = TypedKey.of(SpongeTools.key("flags"), new TypeToken>() {}); + + /** + * @see EntityType#isFlammable() + */ + public static final TypedKey FLAMMABLE = TypedKey.of(SpongeTools.key("flammable"), Boolean.class); + + /** + * @see Agent#goal(GoalExecutorType) for {@link GoalExecutorTypes#NORMAL}. + */ + public static final TypedKey>> GOALS_NORMAL = TypedKey.of(SpongeTools.key("normal_goals"), new TypeToken>>() {}); + + /** + * @see Agent#goal(GoalExecutorType) for {@link GoalExecutorTypes#TARGET}. + */ + public static final TypedKey>> GOALS_TARGET = TypedKey.of(SpongeTools.key("target_goals"), new TypeToken>>() {}); + + /** + * The entity drops. + */ + public static final TypedKey LOOT_TABLE = TypedKey.of(SpongeTools.key("loot_table"), ResourceKey.class); + + /** + * If not set, entity will not be serializable. + * + * @see EntityType#isTransient() + */ + public static final TypedKey SERIALIZATION_KEY = TypedKey.of(SpongeTools.key("serialization_key"), ResourceKey.class); + + /** + * @see EntityType#canSpawnAwayFromPlayer() + */ + public static final TypedKey SPAWN_AWAY_FROM_PLAYER = TypedKey.of(SpongeTools.key("spawn_away_from_player"), Boolean.class); + + /** + * @see EntityType#isSummonable() + */ + public static final TypedKey SUMMONABLE = TypedKey.of(SpongeTools.key("summonable"), Boolean.class); + + /** + * @see EntityType#asComponent() + */ + public static final TypedKey TRANSLATION_KEY = TypedKey.of(SpongeTools.key("translation_key"), String.class); + + private EntityTypeKeys() { + } +} diff --git a/api/src/main/java/net/hellheim/spongetools/custom/type/entity/ModeledEntity.java b/api/src/main/java/net/hellheim/spongetools/custom/type/entity/ModeledEntity.java new file mode 100644 index 0000000..f5806a7 --- /dev/null +++ b/api/src/main/java/net/hellheim/spongetools/custom/type/entity/ModeledEntity.java @@ -0,0 +1,76 @@ +package net.hellheim.spongetools.custom.type.entity; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Objects; + +import org.spongepowered.api.ResourceKey; +import org.spongepowered.api.entity.EntityType; +import org.spongepowered.api.registry.DefaultedRegistryType; + +import net.hellheim.spongetools.SpongeTools; +import net.hellheim.spongetools.custom.type.ModeledCustomType; +import net.hellheim.spongetools.resourcepack.Model; + +public record ModeledEntity( + EntityType type, + List display, + Map models + ) implements ModeledCustomType> { + + public ModeledEntity( + final EntityType type, + final List display, + final Map models + ) { + this.type = Objects.requireNonNull(type, "type"); + this.display = List.copyOf(display); + this.models = Map.copyOf(models); + } + + public static DefaultedRegistryType registry() { + return SpongeTools.Registries.MODELED_ENTITY; + } + + public static Builder builder(final ResourceKey key) { + return new Builder(key); + } + + public static final class Builder + extends ModeledCustomType.Builder, EntityTypeBuilder, ModeledEntity, Builder> { + + private final List display = new ArrayList<>(); + + public Builder(final ResourceKey key) { + super(key); + this.reset(); + } + + public Builder display(final EntityDisplayType... displays) { + for (final EntityDisplayType display : Objects.requireNonNull(displays, "displays")) { + this.display.add(Objects.requireNonNull(display, "display")); + } + return this; + } + + public Builder display(final Iterable displays) { + for (final EntityDisplayType display : Objects.requireNonNull(displays, "displays")) { + this.display.add(Objects.requireNonNull(display, "display")); + } + return this; + } + + @Override + public Builder reset() { + this.display.clear(); + return super.reset(); + } + + @Override + public ModeledEntity build() { + final EntityType entity = this.buildType(EntityTypeBuilder.create()); + return new ModeledEntity(entity, this.display, this.models); + } + } +} diff --git a/api/src/main/java/net/hellheim/spongetools/custom/type/entity/goal/GoalTemplate.java b/api/src/main/java/net/hellheim/spongetools/custom/type/entity/goal/GoalTemplate.java new file mode 100644 index 0000000..6d6bd59 --- /dev/null +++ b/api/src/main/java/net/hellheim/spongetools/custom/type/entity/goal/GoalTemplate.java @@ -0,0 +1,51 @@ +package net.hellheim.spongetools.custom.type.entity.goal; + +import java.util.Objects; +import java.util.function.Function; + +import org.spongepowered.api.entity.ai.goal.Goal; +import org.spongepowered.api.entity.ai.goal.GoalBuilder; +import org.spongepowered.api.entity.living.Agent; + +public record GoalTemplate>(int priority, Function goalProvider) { + + /* + TODO this should probably be switched to Encoder to allow making goals from configs + + Codec> CODEC = GoalTemplate.registryCodec().dispatch(GoalTemplate::mapCodec, Function.identity()); + + static DefaultedRegistryType>> registry() { + return SpongeTools.Registries.GOAL_TEMPLATE_TYPE; + } + + static Codec>> registryCodec() { + return RegistryCodecs.GOAL_TEMPLATE_TYPE; + } + */ + + public GoalTemplate(final int priority, final Function goalProvider) { + this.priority = priority; + this.goalProvider = Objects.requireNonNull(goalProvider, "goalProvider"); + } + + public static > GoalTemplate of( + final int priority, final Function goalProvider + ) { + return new GoalTemplate<>(priority, goalProvider); + } + + public static > GoalTemplate of( + final int priority, final GoalBuilder goalBuilder + ) { + return new GoalTemplate<>(priority, goalBuilder::build); + } + + public G create(final O owner) { + return this.goalProvider.apply(owner); + } + + @SuppressWarnings("unchecked") + public G createUnsafe(final Agent owner) { + return this.create((O) owner); + } +} diff --git a/api/src/main/java/net/hellheim/spongetools/custom/type/item/CustomItemAction.java b/api/src/main/java/net/hellheim/spongetools/custom/type/item/CustomItemAction.java new file mode 100644 index 0000000..f7a520f --- /dev/null +++ b/api/src/main/java/net/hellheim/spongetools/custom/type/item/CustomItemAction.java @@ -0,0 +1,42 @@ +package net.hellheim.spongetools.custom.type.item; + +import org.spongepowered.api.Sponge; +import org.spongepowered.api.data.type.ItemAction; +import org.spongepowered.api.entity.living.Living; +import org.spongepowered.api.item.inventory.ItemStackLike; +import org.spongepowered.api.registry.DefaultedRegistryType; + +import com.mojang.serialization.MapCodec; + +import net.hellheim.spongetools.SpongeTools; +import net.hellheim.spongetools.proxy.solid.codec.MapCodecProxy; + +public interface CustomItemAction extends ItemAction { + + static CustomItemAction of(final Config config) { + return Sponge.game().factoryProvider().provide(Factory.class).of(config); + } + + static DefaultedRegistryType> registry() { + return SpongeTools.Registries.ITEM_ACTION_CONFIG_TYPE; + } + + Config config(); + + /** + * This interface is supposed to be implemented by API consumers.
+ * Codec returned by {@link #mapCodec()} must be registered to {@link CustomItemAction#registry()}. + */ + interface Config extends MapCodecProxy { + + /** + * @see ItemAction#apply(Living, ItemStackLike) + */ + boolean apply(Living entity, ItemStackLike stack); + } + + interface Factory { + + CustomItemAction of(Config config); + } +} diff --git a/api/src/main/java/net/hellheim/spongetools/custom/type/item/ItemArchetypes.java b/api/src/main/java/net/hellheim/spongetools/custom/type/item/ItemArchetypes.java new file mode 100644 index 0000000..9459120 --- /dev/null +++ b/api/src/main/java/net/hellheim/spongetools/custom/type/item/ItemArchetypes.java @@ -0,0 +1,68 @@ +package net.hellheim.spongetools.custom.type.item; + +import org.spongepowered.api.Sponge; +import org.spongepowered.api.registry.DefaultedRegistryReference; +import org.spongepowered.api.registry.Registry; +import org.spongepowered.api.registry.RegistryKey; +import org.spongepowered.api.registry.RegistryScope; +import org.spongepowered.api.registry.RegistryScopes; + +import net.hellheim.spongetools.SpongeTools; +import net.hellheim.spongetools.custom.type.block.BlockArchetypes; + +@RegistryScopes(scopes = RegistryScope.GAME) +public final class ItemArchetypes { + + /** + * Item that can be placed into the world.
+ * Parent Archetype: {@link #DEFAULT}.
+ *
+ * Required Context:
+ * - {@link ItemTypeKeys#BLOCK}
+ *
+ * Supported behaviour:
+ * - TODO + */ + public static final DefaultedRegistryReference BLOCK = ItemArchetypes.key("block"); + + /** + * Regular item without any specific behaviour.
+ *
+ * Required Context:
+ * - {@link ItemTypeKeys#TRANSLATION_KEY}
+ *
+ * Supported Context:
+ * - {@link ItemTypeKeys#CONTAINER}
+ *
+ * Supported behaviour:
+ * - TODO + */ + public static final DefaultedRegistryReference DEFAULT = ItemArchetypes.key("item"); + + /** + * Item that can cast a bobber.
+ * Parent Archetype: {@link #DEFAULT}.
+ *
+ * Supported behaviour:
+ * - TODO + */ + public static final DefaultedRegistryReference FISHING_ROD = ItemArchetypes.key("fishing_rod"); + + /** + * Block item that properly handles {@link BlockArchetypes#SCAFFOLDING scaffolding-like} block placement.
+ *
+ * Parent Archetype: {@link #BLOCK}.
+ */ + public static final DefaultedRegistryReference SCAFFOLDING = ItemArchetypes.key("scaffolding"); + + private ItemArchetypes() { + } + + public static Registry registry() { + return ItemTypeArchetype.registry().get(); + } + + private static DefaultedRegistryReference key(final String key) { + return RegistryKey.of(ItemTypeArchetype.registry(), SpongeTools.key(key)).asDefaultedReference(Sponge::game); + } +} diff --git a/api/src/main/java/net/hellheim/spongetools/custom/type/item/ItemTypeArchetype.java b/api/src/main/java/net/hellheim/spongetools/custom/type/item/ItemTypeArchetype.java new file mode 100644 index 0000000..94ad2c7 --- /dev/null +++ b/api/src/main/java/net/hellheim/spongetools/custom/type/item/ItemTypeArchetype.java @@ -0,0 +1,75 @@ +package net.hellheim.spongetools.custom.type.item; + +import java.util.Objects; +import java.util.Optional; +import java.util.Set; +import java.util.function.BiConsumer; + +import org.spongepowered.api.data.value.ValueContainer; +import org.spongepowered.api.item.ItemType; +import org.spongepowered.api.item.inventory.ItemStack; +import org.spongepowered.api.registry.DefaultedRegistryType; +import org.spongepowered.api.util.annotation.CatalogedBy; + +import net.hellheim.spongetools.SpongeTools; +import net.hellheim.spongetools.custom.behaviour.BehaviourCallbackHolder; +import net.hellheim.spongetools.custom.type.CustomTypeArchetype; +import net.hellheim.spongetools.function.TriFunction; +import net.hellheim.spongetools.object.TypedKey; +import net.hellheim.spongetools.object.TypedKeyMap; + +/** + * {@link CustomTypeArchetype} of {@link ItemType} used for {@link ItemTypeBuilder}. + */ +@CatalogedBy(ItemArchetypes.class) +public record ItemTypeArchetype( + Optional parent, + Class baseClass, + Set> requiredKeys, + BiConsumer contextExtractor, + TriFunction, ItemType> assembler + ) implements CustomTypeArchetype.TypeBased { + + public ItemTypeArchetype( + final Optional parent, + final Class baseClass, + final Set> requiredKeys, + final BiConsumer contextExtractor, + final TriFunction, ItemType> assembler + ) { + CustomTypeArchetype.validate(ItemType.class, baseClass, parent); + this.parent = parent; + this.baseClass = baseClass; + this.requiredKeys = Objects.requireNonNull(requiredKeys, "requiredKeys"); + this.contextExtractor = Objects.requireNonNull(contextExtractor, "contextExtractor"); + this.assembler = Objects.requireNonNull(assembler, "assembler"); + } + + public static DefaultedRegistryType registry() { + return SpongeTools.Registries.ITEM_TYPE_ARCHETYPE; + } + + /** + * @param item The {@link ItemType} + * @return The most appropriate known {@link ItemTypeArchetype} + * @see CustomTypeArchetype#forType(org.spongepowered.api.registry.Registry, CustomTypeArchetype, Object) + */ + public static ItemTypeArchetype forType(final ItemType item) { + return CustomTypeArchetype.forType(ItemArchetypes.registry(), ItemArchetypes.DEFAULT.get(), item); + } + + @SuppressWarnings("unchecked") + public static ItemTypeArchetype of( + final Optional parent, + final Class baseClass, + final Set> requiredKeys, + final BiConsumer contextExtractor, + final TriFunction, I> assembler + ) { + return new ItemTypeArchetype( + parent, baseClass, requiredKeys, + (item, context) -> contextExtractor.accept((I) item, context), + (data, context, behaviour) -> (ItemType) assembler.apply(data, context, behaviour) + ); + } +} diff --git a/api/src/main/java/net/hellheim/spongetools/custom/type/item/ItemTypeBuilder.java b/api/src/main/java/net/hellheim/spongetools/custom/type/item/ItemTypeBuilder.java new file mode 100644 index 0000000..19ce29c --- /dev/null +++ b/api/src/main/java/net/hellheim/spongetools/custom/type/item/ItemTypeBuilder.java @@ -0,0 +1,105 @@ +package net.hellheim.spongetools.custom.type.item; + +import java.util.function.Supplier; + +import org.apache.commons.lang3.ArrayUtils; +import org.spongepowered.api.ResourceKey; +import org.spongepowered.api.Sponge; +import org.spongepowered.api.block.BlockType; +import org.spongepowered.api.data.Keys; +import org.spongepowered.api.item.ItemType; +import org.spongepowered.api.item.inventory.ItemStack; +import org.spongepowered.api.registry.RegistryType; + +import net.hellheim.spongetools.custom.type.CustomTypeBuilder; +import net.hellheim.spongetools.util.TranslationUtil; + +/** + * {@link CustomTypeBuilder} for {@link ItemType}. + */ +public interface ItemTypeBuilder extends + CustomTypeBuilder.TypeBased, + CustomTypeBuilder.WithData { + + /** + * Creates the new {@link ItemTypeBuilder}. + * + * @return The new builder + */ + static ItemTypeBuilder create() { + return Sponge.game().builderProvider().provide(ItemTypeBuilder.class); + } + + /** + * Returns the mandatory registry dependencies that must be used when registering custom type.
+ * These registries include: + *
    + *
  • {@link ItemTypeArchetype#registry()} + *
  • {@link LoreProvider#registry()} + *
  • {@link LoreProcessor#registry()} + *
  • {@link CustomItemAction#registry()} + *
+ * + * @return The registry types + * @see #dependencies(RegistryType...) + */ + static RegistryType[] dependencies() { + return new RegistryType[] { + ItemTypeArchetype.registry(), + LoreProvider.registry(), + LoreProcessor.registry(), + CustomItemAction.registry() + }; + } + + /** + * Returns the registry dependencies from {@link #dependencies()}, and the additional given ones. + * + * @param registryTypes The additional registry dependencies + * @return The registry types + */ + static RegistryType[] dependencies(final RegistryType... registryTypes) { + return ArrayUtils.addAll(ItemTypeBuilder.dependencies(), registryTypes); + } + + /** + * Sets the {@link ItemTypeKeys#TRANSLATION_KEY} context, {@link Keys#ITEM_NAME} and {@link Keys#MODEL} data. + */ + default ItemTypeBuilder id(final ResourceKey id) { + final var component = TranslationUtil.item(id); + this.set(ItemTypeKeys.TRANSLATION_KEY, component.key()); + this.add(Keys.ITEM_NAME, component); + this.add(Keys.MODEL, id); + return this; + } + + /** + * Sets the {@link ItemTypeKeys#CONTAINER} context key. + */ + default ItemTypeBuilder container(final Supplier item) { + return this.container(item.get()); + } + + /** + * Sets the {@link ItemTypeKeys#CONTAINER} context key. + */ + default ItemTypeBuilder container(final ItemType item) { + return this.set(ItemTypeKeys.CONTAINER, item); + } + + /** + * Sets the {@link ItemTypeKeys#BLOCK} context key. + */ + default ItemTypeBuilder block(final Supplier block) { + return this.block(block.get()); + } + + /** + * Sets the {@link ItemTypeKeys#BLOCK} context key. + */ + default ItemTypeBuilder block(final BlockType block) { + return this.set(ItemTypeKeys.BLOCK, block); + } + + // TODO projectile() method to support impl ProjectileItem +} diff --git a/api/src/main/java/net/hellheim/spongetools/custom/type/item/ItemTypeKeys.java b/api/src/main/java/net/hellheim/spongetools/custom/type/item/ItemTypeKeys.java new file mode 100644 index 0000000..5ebbdfb --- /dev/null +++ b/api/src/main/java/net/hellheim/spongetools/custom/type/item/ItemTypeKeys.java @@ -0,0 +1,31 @@ +package net.hellheim.spongetools.custom.type.item; + +import org.spongepowered.api.block.BlockType; +import org.spongepowered.api.item.ItemType; + +import net.hellheim.spongetools.SpongeTools; +import net.hellheim.spongetools.object.TypedKey; + +/** + * Contains {@link TypedKey context keys} used for {@link ItemTypeBuilder} by builtin {@link ItemTypeArchetype}s. + */ +public final class ItemTypeKeys { + + /** + * @see ItemType#block() + */ + public static final TypedKey BLOCK = TypedKey.of(SpongeTools.key("block"), BlockType.class); + + /** + * @see ItemType#container() + */ + public static final TypedKey CONTAINER = TypedKey.of(SpongeTools.key("container"), ItemType.class); + + /** + * @see ItemType#asComponent() + */ + public static final TypedKey TRANSLATION_KEY = TypedKey.of(SpongeTools.key("translation_key"), String.class); + + private ItemTypeKeys() { + } +} diff --git a/api/src/main/java/net/hellheim/spongetools/custom/type/item/LoreApplicator.java b/api/src/main/java/net/hellheim/spongetools/custom/type/item/LoreApplicator.java new file mode 100644 index 0000000..c178c83 --- /dev/null +++ b/api/src/main/java/net/hellheim/spongetools/custom/type/item/LoreApplicator.java @@ -0,0 +1,140 @@ +package net.hellheim.spongetools.custom.type.item; + +import java.util.List; +import java.util.Objects; +import java.util.Optional; + +import org.spongepowered.api.item.inventory.ItemStack; +import org.spongepowered.api.item.inventory.ItemStackLike; + +import net.hellheim.spongetools.object.TypedKeyMap; + +/** + * The utility class to apply {@link LoreProcessor} and {@link LoreProvider}s to the item. + */ +public record LoreApplicator( + /** + * Optional lore processor to use if absent on item + */ + Optional defaultProcessor, + /** + * Optional lore providers to use if absent on item + */ + Optional> defaultProviders + ) { + + private static final LoreApplicator EMPTY = new LoreApplicator(Optional.empty(), Optional.empty()); + + public LoreApplicator( + final Optional defaultProcessor, final Optional> defaultProviders + ) { + this.defaultProcessor(); + this.defaultProcessor = Objects.requireNonNull(defaultProcessor, "defaultProcessor"); + this.defaultProviders = Objects.requireNonNull(defaultProviders, "defaultProviders"); + } + + /** + * Returns lore applicator without fallback processor and providers. + * + * @return Lore applicator without fallback processor and providers + */ + public static LoreApplicator empty() { + return LoreApplicator.EMPTY; + } + + /** + * Returns lore applicator with given fallback processor. + * + * @param defaultProcessor Lore processor to use if absent on item + * @return Lore applicator with the given arguments + */ + public static LoreApplicator of(final LoreProcessor defaultProcessor) { + return LoreApplicator.of(Optional.of(defaultProcessor), Optional.empty()); + } + + /** + * Returns lore applicator with given fallback providers. + * + * @param defaultProviders Lore providers to use if absent on item + * @return Lore applicator with the given arguments + */ + public static LoreApplicator of(final List defaultProviders) { + return LoreApplicator.of(Optional.empty(), Optional.of(defaultProviders)); + } + + /** + * Returns lore applicator with given fallback processor and providers. + * + * @param defaultProcessor Lore processor to use if absent on item + * @param defaultProviders Lore providers to use if absent on item + * @return Lore applicator with the given arguments + */ + public static LoreApplicator of(final LoreProcessor defaultProcessor, final List defaultProviders) { + return LoreApplicator.of(Optional.of(defaultProcessor), Optional.of(defaultProviders)); + } + + /** + * Returns lore applicator with given fallback processor and providers. + * + * @param defaultProcessor Optional lore processor to use if absent on item + * @param defaultProviders Optional lore providers to use if absent on item + * @return Lore applicator with the given arguments + */ + public static LoreApplicator of( + final Optional defaultProcessor, final Optional> defaultProviders + ) { + if (defaultProcessor.isEmpty() && defaultProviders.isEmpty()) { + return LoreApplicator.empty(); + } + + return new LoreApplicator(defaultProcessor, defaultProviders); + } + + /** + * Processes and applies lore to the given item with the given context.
+ * Given item is modified only if following conditions are met:
+ * - Item contains {@link LoreProcessor#dataKey()} or {@link #defaultProcessor()} is present;
+ * - Item contains {@link LoreProvider#dataKey()} or {@link #defaultProviders()} is present.
+ * Item's processor and providers are preferred over this applicator's ones.
+ * Moreover, modifications (if any) are applied to + * {@link ItemStackLike#asMutable()} of the given stack.
+ * If there are no modifications to apply, the original stack is returned. + * + * @param stack The item to apply lore to + * @param context The context to use lore providers with + * @return The modified item or the original item if no modifications were made + */ + public ItemStackLike apply(final ItemStackLike stack, final TypedKeyMap context) { + return stack.get(LoreProcessor.dataKey()) + .or(this::defaultProcessor) + .map(processor -> stack.get(LoreProvider.dataKey()) + .or(this::defaultProviders) + .map(providers -> { + final ItemStack mutable = stack.asMutable(); + mutable.offer(processor.processValue(stack, context, providers)); + providers.forEach(provider -> + provider.loreHidingKeys().forEach(key -> + mutable.offer(key, true))); + return (ItemStackLike) mutable; + }) + .orElse(null)) + .orElse(stack); + } + + /** + * Processes and applies lore to the given item with empty context.
+ * Given item is modified only if following conditions are met:
+ * - Item contains {@link LoreProcessor#dataKey()} or {@link #defaultProcessor()} is present;
+ * - Item contains {@link LoreProvider#dataKey()} or {@link #defaultProviders()} is present.
+ * Item's processor and providers are preferred over this applicator's ones.
+ * Moreover, modifications (if any) are applied to + * {@link ItemStackLike#asMutable()} of the given stack.
+ * If there are no modifications to apply, the original stack is returned. + * + * @param stack The item to apply lore to + * @return The modified item or the original item if no modifications were made + */ + public ItemStackLike apply(final ItemStackLike stack) { + return this.apply(stack, TypedKeyMap.empty()); + } +} diff --git a/api/src/main/java/net/hellheim/spongetools/custom/type/item/LoreProcessor.java b/api/src/main/java/net/hellheim/spongetools/custom/type/item/LoreProcessor.java new file mode 100644 index 0000000..7e7af59 --- /dev/null +++ b/api/src/main/java/net/hellheim/spongetools/custom/type/item/LoreProcessor.java @@ -0,0 +1,232 @@ +package net.hellheim.spongetools.custom.type.item; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import java.util.function.Function; + +import org.spongepowered.api.data.Key; +import org.spongepowered.api.data.Keys; +import org.spongepowered.api.data.persistence.DataBuilder; +import org.spongepowered.api.data.value.ListValue; +import org.spongepowered.api.data.value.Value; +import org.spongepowered.api.item.inventory.ItemStackLike; +import org.spongepowered.api.registry.DefaultedRegistryType; +import org.spongepowered.api.util.CopyableBuilder; + +import com.mojang.serialization.Codec; +import com.mojang.serialization.MapCodec; +import com.mojang.serialization.codecs.RecordCodecBuilder; + +import net.hellheim.spongetools.SpongeTools; +import net.hellheim.spongetools.codec.list.AdventureCodecs; +import net.hellheim.spongetools.codec.list.DataCodecs; +import net.hellheim.spongetools.codec.list.RegistryCodecs; +import net.hellheim.spongetools.object.CodecDataSerializable; +import net.hellheim.spongetools.object.TypedKeyMap; +import net.hellheim.spongetools.proxy.solid.codec.MapCodecProxy; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.format.Style; + +public interface LoreProcessor extends CodecDataSerializable, MapCodecProxy { + + Codec CODEC = LoreProcessor.registryCodec() + .dispatch(LoreProcessor::mapCodec, Function.identity()); + + static DefaultedRegistryType> registry() { + return SpongeTools.Registries.LORE_PROCESSOR_TYPE; + } + + static Codec> registryCodec() { + return RegistryCodecs.LORE_PROCESSOR_TYPE; + } + + static Key> dataKey() { + return SpongeTools.Keys.LORE_PROCESSOR; + } + + static DataBuilder dataBuilder() { + return DataCodecs.dataBuilder(CODEC); + } + + static Plain plain() { + return Plain.INSTANCE; + } + + static ApplyFallbackStyle applyFallbackStyle(final LoreProcessor processor, final Style style) { + return new ApplyFallbackStyle(processor, style); + } + + static Separated.Builder separated() { + return new Separated.Builder(); + } + + /** + * Processes all the given {@link LoreProvider}s and returns accumulated list of components. + * + * @param stack The stack to process lore for + * @param context The context to use providers with + * @param providers The list of lore providers + * @return The list of components + */ + List process(ItemStackLike stack, TypedKeyMap context, List providers); + + default ListValue processValue( + final ItemStackLike stack, final TypedKeyMap context, final List providers + ) { + return ListValue.immutableOf(Keys.LORE, this.process(stack, context, providers)); + } + + default LoreProcessor withFallbackStyle(final Style style) { + return LoreProcessor.applyFallbackStyle(this, style); + } + + @Override + default Codec codec() { + return CODEC; + } + + record Plain() implements LoreProcessor { + + public static final Plain INSTANCE = new Plain(); + public static final MapCodec CODEC = MapCodec.unit(INSTANCE); + + @Override + public MapCodec mapCodec() { + return CODEC; + } + + @Override + public List process( + final ItemStackLike stack, final TypedKeyMap context, final List providers + ) { + final List lore = new ArrayList<>(); + providers.forEach(provider -> lore.addAll(provider.provide(stack, context))); + return lore; + } + } + + record ApplyFallbackStyle(LoreProcessor processor, Style style) implements LoreProcessor { + + public static final MapCodec CODEC = RecordCodecBuilder.mapCodec( + instance -> instance.group( + LoreProcessor.CODEC.fieldOf("processor").forGetter(ApplyFallbackStyle::processor), + AdventureCodecs.STYLE.fieldOf("style").forGetter(ApplyFallbackStyle::style) + ).apply(instance, ApplyFallbackStyle::new)); + + public ApplyFallbackStyle(final LoreProcessor processor, final Style style) { + this.processor = Objects.requireNonNull(processor, "processor"); + this.style = Objects.requireNonNull(style, "style"); + } + + @Override + public List process( + final ItemStackLike stack, final TypedKeyMap context, final List providers + ) { + return this.processor.process(stack, context, providers) + .stream() + .map(c -> c.applyFallbackStyle(this.style)) + .toList(); + } + + @Override + public MapCodec mapCodec() { + return CODEC; + } + } + + record Separated(LoreProvider prefix, LoreProvider suffix, LoreProvider separator) implements LoreProcessor { + + public static final LoreProvider DEFAULT_PREFIX = LoreProvider.empty(); + public static final LoreProvider DEFAULT_SUFFIX = LoreProvider.empty(); + public static final LoreProvider DEFAULT_SEPARATOR = LoreProvider.plain(Component.empty()); + public static final MapCodec CODEC = RecordCodecBuilder.mapCodec( + instance -> instance.group( + LoreProvider.CODEC.optionalFieldOf("prefix", DEFAULT_PREFIX).forGetter(Separated::prefix), + LoreProvider.CODEC.optionalFieldOf("suffix", DEFAULT_SUFFIX).forGetter(Separated::suffix), + LoreProvider.CODEC.optionalFieldOf("separator", DEFAULT_SEPARATOR).forGetter(Separated::separator) + ).apply(instance, Separated::new)); + + public Separated(final LoreProvider prefix, final LoreProvider suffix, final LoreProvider separator) { + this.prefix = Objects.requireNonNull(prefix, "prefix"); + this.suffix = Objects.requireNonNull(suffix, "suffix"); + this.separator = Objects.requireNonNull(separator, "separator"); + } + + @Override + public MapCodec mapCodec() { + return CODEC; + } + + @Override + public List process( + final ItemStackLike stack, final TypedKeyMap context, final List providers + ) { + final List lore = new ArrayList<>(); + if (!providers.isEmpty()) { + final List separator = this.separator.provide(stack, context); + providers.forEach(provider -> { + final List loreToAdd = provider.provide(stack, context); + if (!loreToAdd.isEmpty() && !lore.isEmpty()) { + lore.addAll(separator); + } + + lore.addAll(loreToAdd); + }); + + lore.addAll(0, this.prefix.provide(stack, context)); + lore.addAll(this.suffix.provide(stack, context)); + } + return lore; + } + + public static final class Builder implements + org.spongepowered.api.util.Builder, + CopyableBuilder { + + private LoreProvider prefix; + private LoreProvider suffix; + private LoreProvider separator; + + public Builder() { + this.reset(); + } + + public Builder prefix(final LoreProvider prefix) { + this.prefix = Objects.requireNonNull(prefix, "prefix"); + return this; + } + + public Builder suffix(final LoreProvider suffix) { + this.suffix = Objects.requireNonNull(suffix, "suffix"); + return this; + } + + public Builder separator(final LoreProvider separator) { + this.separator = Objects.requireNonNull(separator, "separator"); + return this; + } + + @Override + public Builder reset() { + this.prefix = Separated.DEFAULT_PREFIX; + this.suffix = Separated.DEFAULT_SUFFIX; + this.separator = Separated.DEFAULT_SEPARATOR; + return this; + } + + @Override + public Builder from(final Separated value) { + this.prefix = value.prefix(); + this.suffix = value.suffix(); + this.separator = value.separator(); + return this; + } + + @Override + public Separated build() { + return new Separated(this.prefix, this.suffix, this.separator); + } + } + } +} diff --git a/api/src/main/java/net/hellheim/spongetools/custom/type/item/LoreProvider.java b/api/src/main/java/net/hellheim/spongetools/custom/type/item/LoreProvider.java new file mode 100644 index 0000000..44e96ce --- /dev/null +++ b/api/src/main/java/net/hellheim/spongetools/custom/type/item/LoreProvider.java @@ -0,0 +1,108 @@ +package net.hellheim.spongetools.custom.type.item; + +import java.util.List; +import java.util.Objects; +import java.util.Set; +import java.util.function.Function; + +import org.spongepowered.api.data.Key; +import org.spongepowered.api.data.persistence.DataBuilder; +import org.spongepowered.api.data.value.ListValue; +import org.spongepowered.api.data.value.Value; +import org.spongepowered.api.item.inventory.ItemStackLike; +import org.spongepowered.api.registry.DefaultedRegistryType; + +import com.mojang.serialization.Codec; +import com.mojang.serialization.MapCodec; +import com.mojang.serialization.codecs.RecordCodecBuilder; + +import net.hellheim.spongetools.SpongeTools; +import net.hellheim.spongetools.codec.list.AdventureCodecs; +import net.hellheim.spongetools.codec.list.DataCodecs; +import net.hellheim.spongetools.codec.list.RegistryCodecs; +import net.hellheim.spongetools.object.CodecDataSerializable; +import net.hellheim.spongetools.object.TypedKeyMap; +import net.hellheim.spongetools.proxy.solid.codec.MapCodecProxy; +import net.kyori.adventure.text.Component; + +public interface LoreProvider extends CodecDataSerializable, MapCodecProxy { + + Codec CODEC = LoreProvider.registryCodec() + .dispatch(LoreProvider::mapCodec, Function.identity()); + + static DefaultedRegistryType> registry() { + return SpongeTools.Registries.LORE_PROVIDER_TYPE; + } + + static Codec> registryCodec() { + return RegistryCodecs.LORE_PROVIDER_TYPE; + } + + static Key> dataKey() { + return SpongeTools.Keys.LORE_PROVIDERS; + } + + static DataBuilder dataBuilder() { + return DataCodecs.dataBuilder(CODEC); + } + + static LoreProvider empty() { + return LoreProvider.Plain.EMPTY; + } + + static LoreProvider.Plain plain(final Component... components) { + return LoreProvider.plain(List.of(components)); + } + + static LoreProvider.Plain plain(final List components) { + return new LoreProvider.Plain(components); + } + + /** + * Returns the list of {@link Component}s this provider adds to item lore.
+ * It's totally valid for provider to return empty list (e.g. if it acts only in the specific context). + * + * @param stack The item to provide the lore for + * @param context The context to use the provider with + * @return The list of components + */ + List provide(ItemStackLike stack, TypedKeyMap context); + + /** + * Returns the list of {@link Key}s that hide some parts of regular lore on the item.
+ * Useful for providers that change the appearance of vanilla lore (e.g. enchantments, potions). + * + * @return The list of keys + */ + default Set>> loreHidingKeys() { + return Set.of(); + } + + @Override + default Codec codec() { + return CODEC; + } + + record Plain(List components) implements LoreProvider { + + public static final Plain EMPTY = LoreProvider.plain(); + public static final MapCodec CODEC = RecordCodecBuilder.mapCodec( + instance -> instance.group( + AdventureCodecs.COMPONENT.listOf().fieldOf("components").forGetter(Plain::components) + ).apply(instance, Plain::new)); + + public Plain(final List components) { + this.components = List.copyOf(Objects.requireNonNull(components, "components")); + } + + @Override + public List provide(final ItemStackLike stack, final TypedKeyMap context) { + return this.components; + } + + @Override + public MapCodec mapCodec() { + return Plain.CODEC; + } + } +} diff --git a/api/src/main/java/net/hellheim/spongetools/custom/type/item/ModeledItem.java b/api/src/main/java/net/hellheim/spongetools/custom/type/item/ModeledItem.java new file mode 100644 index 0000000..a099b3a --- /dev/null +++ b/api/src/main/java/net/hellheim/spongetools/custom/type/item/ModeledItem.java @@ -0,0 +1,143 @@ +package net.hellheim.spongetools.custom.type.item; + +import java.util.Map; +import java.util.Objects; +import java.util.function.Function; +import java.util.function.UnaryOperator; + +import org.checkerframework.checker.nullness.qual.Nullable; +import org.spongepowered.api.ResourceKey; +import org.spongepowered.api.item.ItemType; +import org.spongepowered.api.registry.DefaultedRegistryType; + +import net.hellheim.spongetools.SpongeTools; +import net.hellheim.spongetools.custom.type.ModeledCustomType; +import net.hellheim.spongetools.resourcepack.Model; +import net.hellheim.spongetools.resourcepack.ModelLike; +import net.hellheim.spongetools.resourcepack.ModelTemplateProvider; +import net.hellheim.spongetools.resourcepack.item.ItemDefinition; +import net.hellheim.spongetools.resourcepack.item.ItemDefinitionLike; +import net.hellheim.spongetools.resourcepack.item.ItemModel; +import net.hellheim.spongetools.util.ModelUtil; + +/** + * {@link ModeledCustomType} for {@link ItemType}. + * + * @see #registry() + * @see #builder(ResourceKey) + */ +public record ModeledItem(ItemType type, ItemDefinition definition, Map models) + implements ModeledCustomType { + + public ModeledItem(final ItemType type, final ItemDefinition definition, final Map models) { + this.type = Objects.requireNonNull(type, "type"); + this.definition = Objects.requireNonNull(definition, "defitnion"); + this.models = Map.copyOf(models); + } + + public static DefaultedRegistryType registry() { + return SpongeTools.Registries.MODELED_ITEM; + } + + public static Builder builder(final ResourceKey key) { + return new Builder(key); + } + + public static final class Builder + extends ModeledCustomType.Builder { + + private @Nullable ItemDefinition definition; + + private Builder(final ResourceKey key) { + super(key); + this.reset(); + } + + public Builder definition(final ItemDefinitionLike definition) { + this.definition = Objects.requireNonNull(definition, "definition").asDefinition(); + return this; + } + + public Builder definition(final Function definition) { + return this.definition(definition.apply(this.key)); + } + + public Builder itemDefinition(final Function definition) { + return this.definition(definition.compose(ModelUtil::withItemPrefix)); + } + + public Builder blockDefinition(final Function definition) { + return this.definition(definition.compose(ModelUtil::withBlockPrefix)); + } + + public Builder simpleItemDefinition() { + return this.itemDefinition(key -> ItemModel.simple(key)); + } + + public Builder simpleBlockDefinition() { + return this.blockDefinition(key -> ItemModel.simple(key)); + } + + public Builder model(final UnaryOperator key, final ModelLike model) { + return this.model(key.apply(this.key), model); + } + + public Builder itemModel(final UnaryOperator key, final ModelLike model) { + return this.model(k -> ModelUtil.withItemPrefix(key.apply(k)), model); + } + + public Builder itemModel(final String keySuffix, final ModelLike model) { + return this.itemModel(key -> ModelUtil.withSuffix(key, keySuffix), model); + } + + public Builder blockModel(final UnaryOperator key, final ModelLike model) { + return this.model(k -> ModelUtil.withBlockPrefix(key.apply(k)), model); + } + + public Builder blockModel(final String keySuffix, final ModelLike model) { + return this.blockModel(key -> ModelUtil.withSuffix(key, keySuffix), model); + } + + public Builder simpleItemModel(final ModelLike model) { + return this.simpleItemDefinition() + .itemModel(UnaryOperator.identity(), model); + } + + public Builder simpleItemModel(final Function model) { + return this.simpleItemModel(model.apply(ModelUtil.withItemPrefix(this.key))); + } + + public Builder simpleItemModel(final ModelTemplateProvider.T1 templateProvider) { + return this.simpleItemModel(templateProvider::textured); + } + + public Builder simpleBlockModel(final ModelLike model) { + return this.simpleBlockDefinition() + .blockModel(UnaryOperator.identity(), model); + } + + public Builder simpleBlockModel(final Function model) { + return this.simpleBlockModel(model.apply(ModelUtil.withBlockPrefix(this.key))); + } + + public Builder simpleBlockModel(final ModelTemplateProvider.T1 templateProvider) { + return this.simpleBlockModel(templateProvider::textured); + } + + @Override + public Builder reset() { + this.definition = null; + return super.reset(); + } + + @Override + public ModeledItem build() { + if (this.definition == null) { + throw new IllegalStateException("definition must be set"); + } + + final ItemType item = this.buildType(ItemTypeBuilder.create().id(this.key)); + return new ModeledItem(item, this.definition, this.models); + } + } +} diff --git a/api/src/main/java/net/hellheim/spongetools/event/AttributeEvent.java b/api/src/main/java/net/hellheim/spongetools/event/AttributeEvent.java new file mode 100644 index 0000000..227e526 --- /dev/null +++ b/api/src/main/java/net/hellheim/spongetools/event/AttributeEvent.java @@ -0,0 +1,61 @@ +package net.hellheim.spongetools.event; + +import java.util.Objects; +import java.util.function.DoubleUnaryOperator; +import java.util.function.Supplier; + +import org.spongepowered.api.entity.EntityType; +import org.spongepowered.api.entity.attribute.Attribute; +import org.spongepowered.api.entity.attribute.type.AttributeType; +import org.spongepowered.api.event.lifecycle.LifecycleEvent; + +public interface AttributeEvent extends LifecycleEvent { + + /** + * Event for modifying base, min and max values of the {@link AttributeType}. + */ + interface Modify extends AttributeEvent { + + default AttributeStep attribute(final Supplier type) { + return this.attribute(Objects.requireNonNull(type, "type").get()); + } + + AttributeStep attribute(AttributeType type); + + interface AttributeStep { + + AttributeStep base(DoubleUnaryOperator modifier); + + AttributeStep min(DoubleUnaryOperator modifier); + + AttributeStep max(DoubleUnaryOperator modifier); + } + } + + /** + * Event for registering default {@link Attribute}s for {@link EntityType}s. + */ + interface RegisterToEntity { + + default EntityStep entity(final Supplier> type) { + return this.entity(Objects.requireNonNull(type, "type").get()); + } + + EntityStep entity(EntityType type); + + interface EntityStep { + + default EntityStep register(final Supplier type, final double defaultValue) { + return this.register(Objects.requireNonNull(type, "type").get(), defaultValue); + } + + EntityStep register(AttributeType type, double defaultValue); + + default EntityStep register(final Supplier type) { + return this.register(Objects.requireNonNull(type, "type").get()); + } + + EntityStep register(AttributeType type); + } + } +} diff --git a/api/src/main/java/net/hellheim/spongetools/event/BlockStateEvent.java b/api/src/main/java/net/hellheim/spongetools/event/BlockStateEvent.java new file mode 100644 index 0000000..e1dc13e --- /dev/null +++ b/api/src/main/java/net/hellheim/spongetools/event/BlockStateEvent.java @@ -0,0 +1,62 @@ +package net.hellheim.spongetools.event; + +import java.util.function.Function; +import java.util.function.Supplier; + +import org.spongepowered.api.block.BlockState; +import org.spongepowered.api.block.BlockType; +import org.spongepowered.api.event.lifecycle.LifecycleEvent; + +import net.hellheim.spongetools.custom.type.block.BlockStateDispatcher; +import net.hellheim.spongetools.custom.type.block.BlockStateHolder; +import net.hellheim.spongetools.custom.type.block.BlockStateProvider; +import net.hellheim.spongetools.resourcepack.block.VariantList; +import net.hellheim.spongetools.resourcepack.block.VariantListLike; + +public interface BlockStateEvent extends LifecycleEvent { + + interface RegisterHolder extends BlockStateEvent { + + BlockStateDispatcher dispatcher(); + + default void register(final BlockStateHolder holder, final BlockStateProvider provider) { + this.dispatcher().submit(holder, provider); + } + } + + interface RegisterDisplay extends BlockStateEvent { + + /** + * Registers how the {@link BlockState} is displayed to the clients. + * + * @param state The server-side {@link BlockState} + * @param display The client-side {@link BlockState} + * @throws IllegalArgumentException if display does not belong to vanilla block + */ + void register(BlockState state, BlockState display); + + default void allToOne(final BlockType block, final BlockState display) { + block.validStates().forEach(state -> this.register(state, display)); + } + + default void allToOne(final BlockType block, final Function displayProvider) { + this.allToOne(block, displayProvider.apply(block)); + } + + default void allToOne(final Supplier block, final Function displayProvider) { + this.allToOne(block.get(), displayProvider); + } + } + + interface RegisterVariant extends BlockStateEvent { + + /** + * Registers the {@link VariantList} to display the client-side {@link BlockState} as. + * + * @param state The client-side {@link BlockState} + * @param variants The variant list + * @throws IllegalArgumentException if state does not belong to vanilla block + */ + void register(BlockState state, VariantListLike variants); + } +} diff --git a/api/src/main/java/net/hellheim/spongetools/event/RegisterBehaviourCallbackEvent.java b/api/src/main/java/net/hellheim/spongetools/event/RegisterBehaviourCallbackEvent.java new file mode 100644 index 0000000..715f756 --- /dev/null +++ b/api/src/main/java/net/hellheim/spongetools/event/RegisterBehaviourCallbackEvent.java @@ -0,0 +1,15 @@ +package net.hellheim.spongetools.event; + +import org.spongepowered.api.event.lifecycle.LifecycleEvent; + +import net.hellheim.spongetools.custom.behaviour.BehaviourCallbackHolder; + +public interface RegisterBehaviourCallbackEvent extends LifecycleEvent { + + BehaviourStep type(T type); + + interface BehaviourStep extends BehaviourCallbackHolder.Mutable> { + + T type(); + } +} diff --git a/api/src/main/java/net/hellheim/spongetools/event/RegisterBehaviourDataEvent.java b/api/src/main/java/net/hellheim/spongetools/event/RegisterBehaviourDataEvent.java new file mode 100644 index 0000000..df0e364 --- /dev/null +++ b/api/src/main/java/net/hellheim/spongetools/event/RegisterBehaviourDataEvent.java @@ -0,0 +1,10 @@ +package net.hellheim.spongetools.event; + +import org.spongepowered.api.event.lifecycle.LifecycleEvent; + +import net.hellheim.spongetools.custom.behaviour.BehaviourManager; + +public interface RegisterBehaviourDataEvent extends LifecycleEvent { + + BehaviourManager.BehaviourRegistration registration(Class behaviourHolder); +} diff --git a/api/src/main/java/net/hellheim/spongetools/event/RegisterBlockStateBehaviourEvent.java b/api/src/main/java/net/hellheim/spongetools/event/RegisterBlockStateBehaviourEvent.java new file mode 100644 index 0000000..84dbd1e --- /dev/null +++ b/api/src/main/java/net/hellheim/spongetools/event/RegisterBlockStateBehaviourEvent.java @@ -0,0 +1,88 @@ +package net.hellheim.spongetools.event; + +import java.util.Arrays; +import java.util.function.Supplier; +import java.util.stream.Stream; + +import org.spongepowered.api.block.BlockState; +import org.spongepowered.api.block.BlockType; +import org.spongepowered.api.event.lifecycle.LifecycleEvent; + +import com.google.common.collect.Streams; + +import net.hellheim.spongetools.custom.behaviour.BehaviourCallbackHolder; +import net.hellheim.spongetools.custom.behaviour.type.BlockStateExtension; + +public interface RegisterBlockStateBehaviourEvent extends LifecycleEvent { + + BehaviourStep state(BlockState state); + + + default Stream states(final Stream states) { + return states.map(this::state); + } + + default Stream states(final BlockState... states) { + return this.states(Arrays.stream(states)); + } + + @SuppressWarnings("unchecked") + default Stream states(final Supplier... states) { + return this.states(Arrays.stream(states).map(Supplier::get)); + } + + default Stream states(final Iterable states) { + return this.states(Streams.stream(states)); + } + + + default BehaviourStep defaultState(final BlockType type) { + return this.state(type.defaultState()); + } + + default BehaviourStep defaultState(final Supplier type) { + return this.defaultState(type.get()); + } + + + default Stream defaultStates(final Stream types) { + return types.map(this::defaultState); + } + + default Stream defaultStates(final BlockType... types) { + return this.defaultStates(Arrays.stream(types)); + } + + @SuppressWarnings("unchecked") + default Stream defaultStates(final Supplier... types) { + return this.defaultStates(Arrays.stream(types).map(Supplier::get)); + } + + default Stream defaultStates(final Iterable types) { + return this.defaultStates(Streams.stream(types)); + } + + + default Stream allStates(final Stream types) { + return types.flatMap(type -> type.validStates().stream()).map(this::state); + } + + default Stream allStates(final BlockType... types) { + return this.allStates(Arrays.stream(types)); + } + + @SuppressWarnings("unchecked") + default Stream allStates(final Supplier... types) { + return this.allStates(Arrays.stream(types).map(Supplier::get)); + } + + default Stream allStates(final Iterable types) { + return this.allStates(Streams.stream(types)); + } + + + interface BehaviourStep extends BehaviourCallbackHolder.Mutable { + + BlockState state(); + } +} diff --git a/api/src/main/java/net/hellheim/spongetools/event/RegisterEntityDisplayEvent.java b/api/src/main/java/net/hellheim/spongetools/event/RegisterEntityDisplayEvent.java new file mode 100644 index 0000000..23eaaa5 --- /dev/null +++ b/api/src/main/java/net/hellheim/spongetools/event/RegisterEntityDisplayEvent.java @@ -0,0 +1,34 @@ +package net.hellheim.spongetools.event; + +import java.util.List; +import java.util.Objects; +import java.util.function.Supplier; + +import org.spongepowered.api.entity.EntityType; +import org.spongepowered.api.event.lifecycle.LifecycleEvent; + +import net.hellheim.spongetools.custom.type.entity.EntityDisplayType; + +public interface RegisterEntityDisplayEvent extends LifecycleEvent { + + EntityDisplayRegistration type(EntityType type); + + default EntityDisplayRegistration type(final Supplier> type) { + return this.type(Objects.requireNonNull(type, "type").get()); + } + + interface EntityDisplayRegistration { + + EntityType type(); + + List get(); + + EntityDisplayRegistration set(EntityDisplayType... types); + + EntityDisplayRegistration set(Iterable types); + + EntityDisplayRegistration add(EntityDisplayType... types); + + EntityDisplayRegistration add(Iterable types); + } +} diff --git a/src/main/java/net/hellheim/spongetools/function/BooleanBinaryOperator.java b/api/src/main/java/net/hellheim/spongetools/function/BooleanBinaryOperator.java similarity index 100% rename from src/main/java/net/hellheim/spongetools/function/BooleanBinaryOperator.java rename to api/src/main/java/net/hellheim/spongetools/function/BooleanBinaryOperator.java diff --git a/src/main/java/net/hellheim/spongetools/function/IntBiConsumer.java b/api/src/main/java/net/hellheim/spongetools/function/IntBiConsumer.java similarity index 100% rename from src/main/java/net/hellheim/spongetools/function/IntBiConsumer.java rename to api/src/main/java/net/hellheim/spongetools/function/IntBiConsumer.java diff --git a/src/main/java/net/hellheim/spongetools/function/IntTriConsumer.java b/api/src/main/java/net/hellheim/spongetools/function/IntTriConsumer.java similarity index 100% rename from src/main/java/net/hellheim/spongetools/function/IntTriConsumer.java rename to api/src/main/java/net/hellheim/spongetools/function/IntTriConsumer.java diff --git a/src/main/java/net/hellheim/spongetools/function/QuadConsumer.java b/api/src/main/java/net/hellheim/spongetools/function/QuadConsumer.java similarity index 100% rename from src/main/java/net/hellheim/spongetools/function/QuadConsumer.java rename to api/src/main/java/net/hellheim/spongetools/function/QuadConsumer.java diff --git a/src/main/java/net/hellheim/spongetools/function/QuadFunction.java b/api/src/main/java/net/hellheim/spongetools/function/QuadFunction.java similarity index 100% rename from src/main/java/net/hellheim/spongetools/function/QuadFunction.java rename to api/src/main/java/net/hellheim/spongetools/function/QuadFunction.java diff --git a/src/main/java/net/hellheim/spongetools/function/QuadPredicate.java b/api/src/main/java/net/hellheim/spongetools/function/QuadPredicate.java similarity index 100% rename from src/main/java/net/hellheim/spongetools/function/QuadPredicate.java rename to api/src/main/java/net/hellheim/spongetools/function/QuadPredicate.java diff --git a/api/src/main/java/net/hellheim/spongetools/function/QuinFunction.java b/api/src/main/java/net/hellheim/spongetools/function/QuinFunction.java new file mode 100644 index 0000000..2594552 --- /dev/null +++ b/api/src/main/java/net/hellheim/spongetools/function/QuinFunction.java @@ -0,0 +1,15 @@ +package net.hellheim.spongetools.function; + +import java.util.Objects; +import java.util.function.Function; + +@FunctionalInterface +public interface QuinFunction { + + R apply(F f, S s, T t, E e, B b); + + default QuinFunction andThen(final Function after) { + Objects.requireNonNull(after); + return (F f, S s, T t, E e, B b) -> after.apply(apply(f, s, t, e, b)); + } +} diff --git a/src/main/java/net/hellheim/spongetools/function/QuinPredicate.java b/api/src/main/java/net/hellheim/spongetools/function/QuinPredicate.java similarity index 100% rename from src/main/java/net/hellheim/spongetools/function/QuinPredicate.java rename to api/src/main/java/net/hellheim/spongetools/function/QuinPredicate.java diff --git a/src/main/java/net/hellheim/spongetools/function/TriConsumer.java b/api/src/main/java/net/hellheim/spongetools/function/TriConsumer.java similarity index 100% rename from src/main/java/net/hellheim/spongetools/function/TriConsumer.java rename to api/src/main/java/net/hellheim/spongetools/function/TriConsumer.java diff --git a/src/main/java/net/hellheim/spongetools/function/TriFunction.java b/api/src/main/java/net/hellheim/spongetools/function/TriFunction.java similarity index 100% rename from src/main/java/net/hellheim/spongetools/function/TriFunction.java rename to api/src/main/java/net/hellheim/spongetools/function/TriFunction.java diff --git a/src/main/java/net/hellheim/spongetools/function/TriPredicate.java b/api/src/main/java/net/hellheim/spongetools/function/TriPredicate.java similarity index 100% rename from src/main/java/net/hellheim/spongetools/function/TriPredicate.java rename to api/src/main/java/net/hellheim/spongetools/function/TriPredicate.java diff --git a/src/main/java/net/hellheim/spongetools/manager/BasicMenuManager.java b/api/src/main/java/net/hellheim/spongetools/manager/BasicMenuManager.java similarity index 100% rename from src/main/java/net/hellheim/spongetools/manager/BasicMenuManager.java rename to api/src/main/java/net/hellheim/spongetools/manager/BasicMenuManager.java diff --git a/src/main/java/net/hellheim/spongetools/manager/FutureTaskManager.java b/api/src/main/java/net/hellheim/spongetools/manager/FutureTaskManager.java similarity index 100% rename from src/main/java/net/hellheim/spongetools/manager/FutureTaskManager.java rename to api/src/main/java/net/hellheim/spongetools/manager/FutureTaskManager.java diff --git a/src/main/java/net/hellheim/spongetools/manager/IMenuManager.java b/api/src/main/java/net/hellheim/spongetools/manager/IMenuManager.java similarity index 100% rename from src/main/java/net/hellheim/spongetools/manager/IMenuManager.java rename to api/src/main/java/net/hellheim/spongetools/manager/IMenuManager.java diff --git a/src/main/java/net/hellheim/spongetools/manager/Manager.java b/api/src/main/java/net/hellheim/spongetools/manager/Manager.java similarity index 100% rename from src/main/java/net/hellheim/spongetools/manager/Manager.java rename to api/src/main/java/net/hellheim/spongetools/manager/Manager.java diff --git a/src/main/java/net/hellheim/spongetools/manager/TaskManager.java b/api/src/main/java/net/hellheim/spongetools/manager/TaskManager.java similarity index 100% rename from src/main/java/net/hellheim/spongetools/manager/TaskManager.java rename to api/src/main/java/net/hellheim/spongetools/manager/TaskManager.java diff --git a/src/main/java/net/hellheim/spongetools/manager/TranslationManager.java b/api/src/main/java/net/hellheim/spongetools/manager/TranslationManager.java similarity index 100% rename from src/main/java/net/hellheim/spongetools/manager/TranslationManager.java rename to api/src/main/java/net/hellheim/spongetools/manager/TranslationManager.java diff --git a/api/src/main/java/net/hellheim/spongetools/math/mutable/package-info.java b/api/src/main/java/net/hellheim/spongetools/math/mutable/package-info.java new file mode 100644 index 0000000..20bccf1 --- /dev/null +++ b/api/src/main/java/net/hellheim/spongetools/math/mutable/package-info.java @@ -0,0 +1,8 @@ +/** + * This package provides mutable variants of objects from SpongePowered math.
+ *
+ * Most content is copied from the original and adjusted to be mutable. + * + * @see org.spongepowered.math + */ +package net.hellheim.spongetools.math.mutable; diff --git a/api/src/main/java/net/hellheim/spongetools/math/mutable/vector/MutableVector2d.java b/api/src/main/java/net/hellheim/spongetools/math/mutable/vector/MutableVector2d.java new file mode 100644 index 0000000..0f4b3d2 --- /dev/null +++ b/api/src/main/java/net/hellheim/spongetools/math/mutable/vector/MutableVector2d.java @@ -0,0 +1,366 @@ +package net.hellheim.spongetools.math.mutable.vector; + +import org.spongepowered.math.GenericMath; +import org.spongepowered.math.vector.Vector2d; +import org.spongepowered.math.vector.Vector2f; +import org.spongepowered.math.vector.Vector2i; +import org.spongepowered.math.vector.Vector2l; +import org.spongepowered.math.vector.Vectord; + +public final class MutableVector2d implements Vectord { + + private double x; + private double y; + + public MutableVector2d(final double x, final double y) { + this.x = x; + this.y = y; + } + + public double x() { + return this.x; + } + + public double y() { + return this.y; + } + + public MutableVector2d copy() { + return MutableVector2d.from(this); + } + + public MutableVector2d x(final MutableVector2d v) { + return this.x(v.x); + } + + public MutableVector2d x(final Vector2d v) { + return this.x(v.x()); + } + + public MutableVector2d x(final double a) { + this.x = a; + return this; + } + + public MutableVector2d y(final MutableVector2d v) { + return this.y(v.y); + } + + public MutableVector2d y(final Vector2d v) { + return this.y(v.y()); + } + + public MutableVector2d y(final double a) { + this.y = a; + return this; + } + + public MutableVector2d set(final MutableVector2d v) { + return this.set(v.x, v.y); + } + + public MutableVector2d set(final Vector2d v) { + return this.set(v.x(), v.y()); + } + + public MutableVector2d set(final double x, final double y) { + this.x = x; + this.y = y; + return this; + } + + public MutableVector2d add(final MutableVector2d v) { + return this.add(v.x, v.y); + } + + public MutableVector2d add(final Vector2d v) { + return this.add(v.x(), v.y()); + } + + public MutableVector2d add(final double x, final double y) { + this.x += x; + this.y += y; + return this; + } + + public MutableVector2d sub(final MutableVector2d v) { + return this.sub(v.x, v.y); + } + + public MutableVector2d sub(final Vector2d v) { + return this.sub(v.x(), v.y()); + } + + public MutableVector2d sub(final double x, final double y) { + this.x -= x; + this.y -= y; + return this; + } + + @Override + public MutableVector2d mul(final double a) { + return this.mul(a, a); + } + + public MutableVector2d mul(final MutableVector2d v) { + return this.mul(v.x, v.y); + } + + public MutableVector2d mul(final Vector2d v) { + return this.mul(v.x(), v.y()); + } + + public MutableVector2d mul(final double x, final double y) { + this.x *= x; + this.y *= y; + return this; + } + + @Override + public MutableVector2d div(final double a) { + return this.div(a, a); + } + + public MutableVector2d div(final MutableVector2d v) { + return this.div(v.x, v.y); + } + + public MutableVector2d div(final Vector2d v) { + return this.div(v.x(), v.y()); + } + + public MutableVector2d div(final double x, final double y) { + this.x /= x; + this.y /= y; + return this; + } + + public double dot(final MutableVector2d v) { + return this.dot(v.x, v.y); + } + + public double dot(final Vector2d v) { + return this.dot(v.x(), v.y()); + } + + public double dot(final double x, final double y) { + return this.x * x + this.y * y; + } + + public MutableVector2d project(final MutableVector2d v) { + return this.project(v.x, v.y); + } + + public MutableVector2d project(final Vector2d v) { + return this.project(v.x(), v.y()); + } + + public MutableVector2d project(final double x, final double y) { + final double lengthSquared = x * x + y * y; + if (lengthSquared == 0) { + throw new ArithmeticException("Cannot project onto the zero vector"); + } + final double a = this.dot(x, y) / lengthSquared; + return this.set(a * x, a * y); + } + + @Override + public MutableVector2d pow(final double pow) { + return this.pow(pow, pow); + } + + public MutableVector2d pow(final MutableVector2d v) { + return this.pow(v.x, v.y); + } + + public MutableVector2d pow(final Vector2d v) { + return this.pow(v.x(), v.y()); + } + + public MutableVector2d pow(final double x, final double y) { + return this.set(Math.pow(this.x, x), Math.pow(this.y, y)); + } + + @Override + public MutableVector2d ceil() { + return this.set(Math.ceil(this.x), Math.ceil(this.y)); + } + + @Override + public MutableVector2d floor() { + return this.set(GenericMath.floor(this.x), GenericMath.floor(this.y)); + } + + @Override + public MutableVector2d round() { + return this.set(Math.round(this.x), Math.round(this.y)); + } + + @Override + public MutableVector2d abs() { + return this.set(Math.abs(this.x), Math.abs(this.y)); + } + + @Override + public MutableVector2d negate() { + return this.set(-this.x, -this.y); + } + + public MutableVector2d min(final MutableVector2d v) { + return this.min(v.x, v.y); + } + + public MutableVector2d min(final Vector2d v) { + return this.min(v.x(), v.y()); + } + + public MutableVector2d min(final double x, final double y) { + return this.set(Math.min(this.x, x), Math.min(this.y, y)); + } + + public MutableVector2d max(final MutableVector2d v) { + return this.max(v.x, v.y); + } + + public MutableVector2d max(final Vector2d v) { + return this.max(v.x(), v.y()); + } + + public MutableVector2d max(final double x, final double y) { + return this.set(Math.max(this.x, x), Math.max(this.y, y)); + } + + public double distanceSquared(final MutableVector2d v) { + return this.distanceSquared(v.x, v.y); + } + + public double distanceSquared(final Vector2d v) { + return this.distanceSquared(v.x(), v.y()); + } + + public double distanceSquared(final double x, final double y) { + final double dx = this.x - x; + final double dy = this.y - y; + return dx * dx + dy * dy; + } + + public double distance(final MutableVector2d v) { + return this.distance(v.x, v.y); + } + + public double distance(final Vector2d v) { + return this.distance(v.x(), v.y()); + } + + public double distance(final double x, final double y) { + return Math.sqrt(this.distanceSquared(x, y)); + } + + @Override + public double lengthSquared() { + return this.x * this.x + this.y * this.y; + } + + @Override + public double length() { + return Math.sqrt(this.lengthSquared()); + } + + @Override + public MutableVector2d normalize() { + final double length = this.length(); + if (Math.abs(length) < GenericMath.FLT_EPSILON) { + throw new ArithmeticException("Cannot normalize the zero vector"); + } + return this.set(this.x / length, this.y / length); + } + + @Override + public int minAxis() { + return this.x < this.y ? 0 : 1; + } + + @Override + public int maxAxis() { + return this.x > this.y ? 0 : 1; + } + + @Override + public double[] toArray() { + return new double[]{this.x, this.y}; + } + + @Override + public Vector2i toInt() { + return new Vector2i(this.x, this.y); + } + + @Override + public Vector2l toLong() { + return new Vector2l(this.x, this.y); + } + + @Override + public Vector2f toFloat() { + return new Vector2f(this.x, this.y); + } + + @Override + public Vector2d toDouble() { + return new Vector2d((double) this.x, (double) this.y); + } + + @Override + public boolean equals(final Object other) { + if (this == other) { + return true; + } else if (!(other instanceof final MutableVector2d that)) { + return false; + } else { + return Double.compare(that.x, this.x) == 0 + && Double.compare(that.y, this.y) == 0; + } + } + + @Override + public int hashCode() { + return Double.hashCode(this.x) * 31 + Double.hashCode(this.y); + } + + @Override + public String toString() { + return "(" + this.x + ", " + this.y + ")"; + } + + public static MutableVector2d zero() { + return MutableVector2d.of(0, 0); + } + + public static MutableVector2d unitX() { + return MutableVector2d.of(1, 0); + } + + public static MutableVector2d unitY() { + return MutableVector2d.of(0, 1); + } + + public static MutableVector2d one() { + return MutableVector2d.of(1, 1); + } + + public static MutableVector2d from(final double n) { + return MutableVector2d.of(n, n); + } + + public static MutableVector2d from(final MutableVector2d v) { + return MutableVector2d.of(v.x, v.y); + } + + public static MutableVector2d from(final Vector2d v) { + return MutableVector2d.of(v.x(), v.y()); + } + + public static MutableVector2d of(final double x, final double y) { + return new MutableVector2d(x, y); + } +} diff --git a/api/src/main/java/net/hellheim/spongetools/math/mutable/vector/MutableVector2f.java b/api/src/main/java/net/hellheim/spongetools/math/mutable/vector/MutableVector2f.java new file mode 100644 index 0000000..507ee1f --- /dev/null +++ b/api/src/main/java/net/hellheim/spongetools/math/mutable/vector/MutableVector2f.java @@ -0,0 +1,446 @@ +package net.hellheim.spongetools.math.mutable.vector; + +import org.spongepowered.math.GenericMath; +import org.spongepowered.math.vector.Vector2d; +import org.spongepowered.math.vector.Vector2f; +import org.spongepowered.math.vector.Vector2i; +import org.spongepowered.math.vector.Vector2l; +import org.spongepowered.math.vector.Vectorf; + +public final class MutableVector2f implements Vectorf { + + private float x; + private float y; + + public MutableVector2f(final double x, final double y) { + this((float) x, (float) y); + } + + public MutableVector2f(final float x, final float y) { + this.x = x; + this.y = y; + } + + public float x() { + return this.x; + } + + public float y() { + return this.y; + } + + public MutableVector2f copy() { + return MutableVector2f.from(this); + } + + public MutableVector2f x(final MutableVector2f v) { + return this.x(v.x); + } + + public MutableVector2f x(final Vector2f v) { + return this.x(v.x()); + } + + public MutableVector2f x(final double a) { + return this.x((float) a); + } + + public MutableVector2f x(final float a) { + this.x = a; + return this; + } + + public MutableVector2f y(final MutableVector2f v) { + return this.y(v.y); + } + + public MutableVector2f y(final Vector2f v) { + return this.y(v.y()); + } + + public MutableVector2f y(final double a) { + return this.y((float) a); + } + + public MutableVector2f y(final float a) { + this.y = a; + return this; + } + + public MutableVector2f set(final MutableVector2f v) { + return this.set(v.x, v.y); + } + + public MutableVector2f set(final Vector2f v) { + return this.set(v.x(), v.y()); + } + + public MutableVector2f set(final double x, final double y) { + return this.set((float) x, (float) y); + } + + public MutableVector2f set(final float x, final float y) { + this.x = x; + this.y = y; + return this; + } + + public MutableVector2f add(final MutableVector2f v) { + return this.add(v.x, v.y); + } + + public MutableVector2f add(final Vector2f v) { + return this.add(v.x(), v.y()); + } + + public MutableVector2f add(final double x, final double y) { + return this.add((float) x, (float) y); + } + + public MutableVector2f add(final float x, final float y) { + this.x += x; + this.y += y; + return this; + } + + public MutableVector2f sub(final MutableVector2f v) { + return this.sub(v.x, v.y); + } + + public MutableVector2f sub(final Vector2f v) { + return this.sub(v.x(), v.y()); + } + + public MutableVector2f sub(final double x, final double y) { + return this.sub((float) x, (float) y); + } + + public MutableVector2f sub(final float x, final float y) { + this.x -= x; + this.y -= y; + return this; + } + + public MutableVector2f mul(final double a) { + return this.mul((float) a); + } + + @Override + public MutableVector2f mul(final float a) { + return this.mul(a, a); + } + + public MutableVector2f mul(final MutableVector2f v) { + return this.mul(v.x, v.y); + } + + public MutableVector2f mul(final Vector2f v) { + return this.mul(v.x(), v.y()); + } + + public MutableVector2f mul(final double x, final double y) { + return this.mul((float) x, (float) y); + } + + public MutableVector2f mul(final float x, final float y) { + this.x *= x; + this.y *= y; + return this; + } + + public MutableVector2f div(final double a) { + return this.div((float) a); + } + + @Override + public MutableVector2f div(final float a) { + return this.div(a, a); + } + + public MutableVector2f div(final MutableVector2f v) { + return this.div(v.x, v.y); + } + + public MutableVector2f div(final Vector2f v) { + return this.div(v.x(), v.y()); + } + + public MutableVector2f div(final double x, final double y) { + return this.div((float) x, (float) y); + } + + public MutableVector2f div(final float x, final float y) { + this.x /= x; + this.y /= y; + return this; + } + + public float dot(final MutableVector2f v) { + return this.dot(v.x, v.y); + } + + public float dot(final Vector2f v) { + return this.dot(v.x(), v.y()); + } + + public float dot(final double x, final double y) { + return this.dot((float) x, (float) y); + } + + public float dot(final float x, final float y) { + return this.x * x + this.y * y; + } + + public MutableVector2f project(final MutableVector2f v) { + return this.project(v.x, v.y); + } + + public MutableVector2f project(final Vector2f v) { + return this.project(v.x(), v.y()); + } + + public MutableVector2f project(final double x, final double y) { + return this.project((float) x, (float) y); + } + + public MutableVector2f project(final float x, final float y) { + final float lengthSquared = x * x + y * y; + if (lengthSquared == 0) { + throw new ArithmeticException("Cannot project onto the zero vector"); + } + final float a = this.dot(x, y) / lengthSquared; + return this.set(a * x, a * y); + } + + public MutableVector2f pow(final double pow) { + return this.pow((float) pow); + } + + @Override + public MutableVector2f pow(final float pow) { + return this.pow(pow, pow); + } + + public MutableVector2f pow(final MutableVector2f v) { + return this.pow(v.x, v.y); + } + + public MutableVector2f pow(final Vector2f v) { + return this.pow(v.x(), v.y()); + } + + public MutableVector2f pow(final double x, final double y) { + return this.pow((float) x, (float) y); + } + + public MutableVector2f pow(final float x, final float y) { + return this.set(Math.pow(this.x, x), Math.pow(this.y, y)); + } + + @Override + public MutableVector2f ceil() { + return this.set(Math.ceil(this.x), Math.ceil(this.y)); + } + + @Override + public MutableVector2f floor() { + return this.set(GenericMath.floor(this.x), GenericMath.floor(this.y)); + } + + @Override + public MutableVector2f round() { + return this.set(Math.round(this.x), Math.round(this.y)); + } + + @Override + public MutableVector2f abs() { + return this.set(Math.abs(this.x), Math.abs(this.y)); + } + + @Override + public MutableVector2f negate() { + return this.set(-this.x, -this.y); + } + + public MutableVector2f min(final MutableVector2f v) { + return this.min(v.x, v.y); + } + + public MutableVector2f min(final Vector2f v) { + return this.min(v.x(), v.y()); + } + + public MutableVector2f min(final double x, final double y) { + return this.min((float) x, (float) y); + } + + public MutableVector2f min(final float x, final float y) { + return this.set(Math.min(this.x, x), Math.min(this.y, y)); + } + + public MutableVector2f max(final MutableVector2f v) { + return this.max(v.x, v.y); + } + + public MutableVector2f max(final Vector2f v) { + return this.max(v.x(), v.y()); + } + + public MutableVector2f max(final double x, final double y) { + return this.max((float) x, (float) y); + } + + public MutableVector2f max(final float x, final float y) { + return this.set(Math.max(this.x, x), Math.max(this.y, y)); + } + + public float distanceSquared(final MutableVector2f v) { + return this.distanceSquared(v.x, v.y); + } + + public float distanceSquared(final Vector2f v) { + return this.distanceSquared(v.x(), v.y()); + } + + public float distanceSquared(final double x, final double y) { + return this.distanceSquared((float) x, (float) y); + } + + public float distanceSquared(final float x, final float y) { + final float dx = this.x - x; + final float dy = this.y - y; + return dx * dx + dy * dy; + } + + public float distance(final MutableVector2f v) { + return this.distance(v.x, v.y); + } + + public float distance(final Vector2f v) { + return this.distance(v.x(), v.y()); + } + + public float distance(final double x, final double y) { + return this.distance((float) x, (float) y); + } + + public float distance(final float x, final float y) { + return (float) Math.sqrt(this.distanceSquared(x, y)); + } + + @Override + public float lengthSquared() { + return this.x * this.x + this.y * this.y; + } + + @Override + public float length() { + return (float) Math.sqrt(this.lengthSquared()); + } + + @Override + public MutableVector2f normalize() { + final float length = this.length(); + if (Math.abs(length) < GenericMath.FLT_EPSILON) { + throw new ArithmeticException("Cannot normalize the zero vector"); + } + return this.set(this.x / length, this.y / length); + } + + @Override + public int minAxis() { + return this.x < this.y ? 0 : 1; + } + + @Override + public int maxAxis() { + return this.x > this.y ? 0 : 1; + } + + @Override + public float[] toArray() { + return new float[]{this.x, this.y}; + } + + @Override + public Vector2i toInt() { + return new Vector2i(this.x, this.y); + } + + @Override + public Vector2l toLong() { + return new Vector2l(this.x, this.y); + } + + @Override + public Vector2f toFloat() { + return new Vector2f(this.x, this.y); + } + + @Override + public Vector2d toDouble() { + return new Vector2d((double) this.x, (double) this.y); + } + + @Override + public boolean equals(final Object other) { + if (this == other) { + return true; + } else if (!(other instanceof final MutableVector2f that)) { + return false; + } else { + return Float.compare(that.x, this.x) == 0 + && Float.compare(that.y, this.y) == 0; + } + } + + @Override + public int hashCode() { + return Float.hashCode(this.x) * 31 + Float.hashCode(this.y); + } + + @Override + public String toString() { + return "(" + this.x + ", " + this.y + ")"; + } + + public static MutableVector2f zero() { + return MutableVector2f.of(0, 0); + } + + public static MutableVector2f unitX() { + return MutableVector2f.of(1, 0); + } + + public static MutableVector2f unitY() { + return MutableVector2f.of(0, 1); + } + + public static MutableVector2f one() { + return MutableVector2f.of(1, 1); + } + + public static MutableVector2f from(final double n) { + return MutableVector2f.from((float) n); + } + + public static MutableVector2f from(final float n) { + return MutableVector2f.of(n, n); + } + + public static MutableVector2f from(final MutableVector2f v) { + return MutableVector2f.of(v.x, v.y); + } + + public static MutableVector2f from(final Vector2f v) { + return MutableVector2f.of(v.x(), v.y()); + } + + public static MutableVector2f of(final double x, final double y) { + return new MutableVector2f(x, y); + } + + public static MutableVector2f of(final float x, final float y) { + return new MutableVector2f(x, y); + } +} diff --git a/api/src/main/java/net/hellheim/spongetools/math/mutable/vector/MutableVector2i.java b/api/src/main/java/net/hellheim/spongetools/math/mutable/vector/MutableVector2i.java new file mode 100644 index 0000000..56c3b75 --- /dev/null +++ b/api/src/main/java/net/hellheim/spongetools/math/mutable/vector/MutableVector2i.java @@ -0,0 +1,422 @@ +package net.hellheim.spongetools.math.mutable.vector; + +import org.spongepowered.math.GenericMath; +import org.spongepowered.math.vector.Vector2d; +import org.spongepowered.math.vector.Vector2f; +import org.spongepowered.math.vector.Vector2i; +import org.spongepowered.math.vector.Vector2l; +import org.spongepowered.math.vector.Vectori; + +public final class MutableVector2i implements Vectori { + + private int x; + private int y; + + public MutableVector2i(final double x, final double y) { + this(GenericMath.floor(x), GenericMath.floor(y)); + } + + public MutableVector2i(final int x, final int y) { + this.x = x; + this.y = y; + } + + public int x() { + return this.x; + } + + public int y() { + return this.y; + } + + public MutableVector2i copy() { + return MutableVector2i.from(this); + } + + public MutableVector2i x(final MutableVector2i v) { + return this.x(v.x); + } + + public MutableVector2i x(final Vector2i v) { + return this.x(v.x()); + } + + public MutableVector2i x(final double a) { + return this.x(GenericMath.floor(a)); + } + + public MutableVector2i x(final int a) { + this.x = a; + return this; + } + + public MutableVector2i y(final MutableVector2i v) { + return this.y(v.y); + } + + public MutableVector2i y(final Vector2i v) { + return this.y(v.y()); + } + + public MutableVector2i y(final double a) { + return this.y(GenericMath.floor(a)); + } + + public MutableVector2i y(final int a) { + this.y = a; + return this; + } + + public MutableVector2i set(final MutableVector2i v) { + return this.set(v.x, v.y); + } + + public MutableVector2i set(final Vector2i v) { + return this.set(v.x(), v.y()); + } + + public MutableVector2i set(final double x, final double y) { + return this.set(GenericMath.floor(x), GenericMath.floor(y)); + } + + public MutableVector2i set(final int x, final int y) { + this.x = x; + this.y = y; + return this; + } + + public MutableVector2i add(final MutableVector2i v) { + return this.add(v.x, v.y); + } + + public MutableVector2i add(final Vector2i v) { + return this.add(v.x(), v.y()); + } + + public MutableVector2i add(final double x, final double y) { + return this.add(GenericMath.floor(x), GenericMath.floor(y)); + } + + public MutableVector2i add(final int x, final int y) { + this.x += x; + this.y += y; + return this; + } + + public MutableVector2i sub(final MutableVector2i v) { + return this.sub(v.x, v.y); + } + + public MutableVector2i sub(final Vector2i v) { + return this.sub(v.x(), v.y()); + } + + public MutableVector2i sub(final double x, final double y) { + return this.sub(GenericMath.floor(x), GenericMath.floor(y)); + } + + public MutableVector2i sub(final int x, final int y) { + this.x -= x; + this.y -= y; + return this; + } + + public MutableVector2i mul(final double a) { + return this.mul(GenericMath.floor(a)); + } + + @Override + public MutableVector2i mul(final int a) { + return this.mul(a, a); + } + + public MutableVector2i mul(final MutableVector2i v) { + return this.mul(v.x, v.y); + } + + public MutableVector2i mul(final Vector2i v) { + return this.mul(v.x(), v.y()); + } + + public MutableVector2i mul(final double x, final double y) { + return this.mul(GenericMath.floor(x), GenericMath.floor(y)); + } + + public MutableVector2i mul(final int x, final int y) { + this.x *= x; + this.y *= y; + return this; + } + + public MutableVector2i div(final double a) { + return this.div(GenericMath.floor(a)); + } + + @Override + public MutableVector2i div(final int a) { + return this.div(a, a); + } + + public MutableVector2i div(final MutableVector2i v) { + return this.div(v.x, v.y); + } + + public MutableVector2i div(final Vector2i v) { + return this.div(v.x(), v.y()); + } + + public MutableVector2i div(final double x, final double y) { + return this.div(GenericMath.floor(x), GenericMath.floor(y)); + } + + public MutableVector2i div(final int x, final int y) { + this.x /= x; + this.y /= y; + return this; + } + + public int dot(final MutableVector2i v) { + return this.dot(v.x, v.y); + } + + public int dot(final Vector2i v) { + return this.dot(v.x(), v.y()); + } + + public int dot(final double x, final double y) { + return this.dot(GenericMath.floor(x), GenericMath.floor(y)); + } + + public int dot(final int x, final int y) { + return this.x * x + this.y * y; + } + + public MutableVector2i project(final MutableVector2i v) { + return this.project(v.x, v.y); + } + + public MutableVector2i project(final Vector2i v) { + return this.project(v.x(), v.y()); + } + + public MutableVector2i project(final double x, final double y) { + return this.project(GenericMath.floor(x), GenericMath.floor(y)); + } + + public MutableVector2i project(final int x, final int y) { + final int lengthSquared = x * x + y * y; + if (lengthSquared == 0) { + throw new ArithmeticException("Cannot project onto the zero vector"); + } + final float a = (float) this.dot(x, y) / lengthSquared; + return this.set(a * x, a * y); + } + + public MutableVector2i pow(final double pow) { + return this.pow(GenericMath.floor(pow)); + } + + @Override + public MutableVector2i pow(final int pow) { + return this.pow(pow, pow); + } + + public MutableVector2i pow(final MutableVector2i v) { + return this.pow(v.x, v.y); + } + + public MutableVector2i pow(final Vector2i v) { + return this.pow(v.x(), v.y()); + } + + public MutableVector2i pow(final double x, final double y) { + return this.pow(GenericMath.floor(x), GenericMath.floor(y)); + } + + public MutableVector2i pow(final int x, final int y) { + return this.set(Math.pow(this.x, x), Math.pow(this.y, y)); + } + + @Override + public MutableVector2i abs() { + return this.set(Math.abs(this.x), Math.abs(this.y)); + } + + @Override + public MutableVector2i negate() { + return this.set(-this.x, -this.y); + } + + public MutableVector2i min(final MutableVector2i v) { + return this.min(v.x, v.y); + } + + public MutableVector2i min(final Vector2i v) { + return this.min(v.x(), v.y()); + } + + public MutableVector2i min(final double x, final double y) { + return this.min(GenericMath.floor(x), GenericMath.floor(y)); + } + + public MutableVector2i min(final int x, final int y) { + return this.set(Math.min(this.x, x), Math.min(this.y, y)); + } + + public MutableVector2i max(final MutableVector2i v) { + return this.max(v.x, v.y); + } + + public MutableVector2i max(final Vector2i v) { + return this.max(v.x(), v.y()); + } + + public MutableVector2i max(final double x, final double y) { + return this.max(GenericMath.floor(x), GenericMath.floor(y)); + } + + public MutableVector2i max(final int x, final int y) { + return this.set(Math.max(this.x, x), Math.max(this.y, y)); + } + + public int distanceSquared(final MutableVector2i v) { + return this.distanceSquared(v.x, v.y); + } + + public int distanceSquared(final Vector2i v) { + return this.distanceSquared(v.x(), v.y()); + } + + public int distanceSquared(final double x, final double y) { + return this.distanceSquared(GenericMath.floor(x), GenericMath.floor(y)); + } + + public int distanceSquared(final int x, final int y) { + final int dx = this.x - x; + final int dy = this.y - y; + return dx * dx + dy * dy; + } + + public float distance(final MutableVector2i v) { + return this.distance(v.x, v.y); + } + + public float distance(final Vector2i v) { + return this.distance(v.x(), v.y()); + } + + public float distance(final double x, final double y) { + return this.distance(GenericMath.floor(x), GenericMath.floor(y)); + } + + public float distance(final int x, final int y) { + return (float) Math.sqrt(this.distanceSquared(x, y)); + } + + @Override + public int lengthSquared() { + return this.x * this.x + this.y * this.y; + } + + @Override + public float length() { + return (float) Math.sqrt(this.lengthSquared()); + } + + @Override + public int minAxis() { + return this.x < this.y ? 0 : 1; + } + + @Override + public int maxAxis() { + return this.x > this.y ? 0 : 1; + } + + @Override + public int[] toArray() { + return new int[]{this.x, this.y}; + } + + @Override + public Vector2i toInt() { + return new Vector2i(this.x, this.y); + } + + @Override + public Vector2l toLong() { + return new Vector2l(this.x, this.y); + } + + @Override + public Vector2f toFloat() { + return new Vector2f(this.x, this.y); + } + + @Override + public Vector2d toDouble() { + return new Vector2d((double) this.x, (double) this.y); + } + + @Override + public boolean equals(final Object other) { + if (this == other) { + return true; + } else if (!(other instanceof final MutableVector2i that)) { + return false; + } else { + return that.x == this.x + && that.y == this.y; + } + } + + @Override + public int hashCode() { + return Integer.hashCode(this.x) * 31 + Integer.hashCode(this.y); + } + + @Override + public String toString() { + return "(" + this.x + ", " + this.y + ")"; + } + + public static MutableVector2i zero() { + return MutableVector2i.of(0, 0); + } + + public static MutableVector2i unitX() { + return MutableVector2i.of(1, 0); + } + + public static MutableVector2i unitY() { + return MutableVector2i.of(0, 1); + } + + public static MutableVector2i one() { + return MutableVector2i.of(1, 1); + } + + public static MutableVector2i from(final double n) { + return MutableVector2i.from(GenericMath.floor(n)); + } + + public static MutableVector2i from(final int n) { + return MutableVector2i.of(n, n); + } + + public static MutableVector2i from(final MutableVector2i v) { + return MutableVector2i.of(v.x, v.y); + } + + public static MutableVector2i from(final Vector2i v) { + return MutableVector2i.of(v.x(), v.y()); + } + + public static MutableVector2i of(final double x, final double y) { + return new MutableVector2i(x, y); + } + + public static MutableVector2i of(final int x, final int y) { + return new MutableVector2i(x, y); + } +} diff --git a/api/src/main/java/net/hellheim/spongetools/math/mutable/vector/MutableVector2l.java b/api/src/main/java/net/hellheim/spongetools/math/mutable/vector/MutableVector2l.java new file mode 100644 index 0000000..648b955 --- /dev/null +++ b/api/src/main/java/net/hellheim/spongetools/math/mutable/vector/MutableVector2l.java @@ -0,0 +1,422 @@ +package net.hellheim.spongetools.math.mutable.vector; + +import org.spongepowered.math.GenericMath; +import org.spongepowered.math.vector.Vector2d; +import org.spongepowered.math.vector.Vector2f; +import org.spongepowered.math.vector.Vector2i; +import org.spongepowered.math.vector.Vector2l; +import org.spongepowered.math.vector.Vectorl; + +public final class MutableVector2l implements Vectorl { + + private long x; + private long y; + + public MutableVector2l(final double x, final double y) { + this(GenericMath.floorl(x), GenericMath.floorl(y)); + } + + public MutableVector2l(final long x, final long y) { + this.x = x; + this.y = y; + } + + public long x() { + return this.x; + } + + public long y() { + return this.y; + } + + public MutableVector2l copy() { + return MutableVector2l.from(this); + } + + public MutableVector2l x(final MutableVector2l v) { + return this.x(v.x); + } + + public MutableVector2l x(final Vector2l v) { + return this.x(v.x()); + } + + public MutableVector2l x(final double a) { + return this.x(GenericMath.floorl(a)); + } + + public MutableVector2l x(final long a) { + this.x = a; + return this; + } + + public MutableVector2l y(final MutableVector2l v) { + return this.y(v.y); + } + + public MutableVector2l y(final Vector2l v) { + return this.y(v.y()); + } + + public MutableVector2l y(final double a) { + return this.y(GenericMath.floorl(a)); + } + + public MutableVector2l y(final long a) { + this.y = a; + return this; + } + + public MutableVector2l set(final MutableVector2l v) { + return this.set(v.x, v.y); + } + + public MutableVector2l set(final Vector2l v) { + return this.set(v.x(), v.y()); + } + + public MutableVector2l set(final double x, final double y) { + return this.set(GenericMath.floorl(x), GenericMath.floorl(y)); + } + + public MutableVector2l set(final long x, final long y) { + this.x = x; + this.y = y; + return this; + } + + public MutableVector2l add(final MutableVector2l v) { + return this.add(v.x, v.y); + } + + public MutableVector2l add(final Vector2l v) { + return this.add(v.x(), v.y()); + } + + public MutableVector2l add(final double x, final double y) { + return this.add(GenericMath.floorl(x), GenericMath.floorl(y)); + } + + public MutableVector2l add(final long x, final long y) { + this.x += x; + this.y += y; + return this; + } + + public MutableVector2l sub(final MutableVector2l v) { + return this.sub(v.x, v.y); + } + + public MutableVector2l sub(final Vector2l v) { + return this.sub(v.x(), v.y()); + } + + public MutableVector2l sub(final double x, final double y) { + return this.sub(GenericMath.floorl(x), GenericMath.floorl(y)); + } + + public MutableVector2l sub(final long x, final long y) { + this.x -= x; + this.y -= y; + return this; + } + + public MutableVector2l mul(final double a) { + return this.mul(GenericMath.floorl(a)); + } + + @Override + public MutableVector2l mul(final long a) { + return this.mul(a, a); + } + + public MutableVector2l mul(final MutableVector2l v) { + return this.mul(v.x, v.y); + } + + public MutableVector2l mul(final Vector2l v) { + return this.mul(v.x(), v.y()); + } + + public MutableVector2l mul(final double x, final double y) { + return this.mul(GenericMath.floorl(x), GenericMath.floorl(y)); + } + + public MutableVector2l mul(final long x, final long y) { + this.x *= x; + this.y *= y; + return this; + } + + public MutableVector2l div(final double a) { + return this.div(GenericMath.floorl(a)); + } + + @Override + public MutableVector2l div(final long a) { + return this.div(a, a); + } + + public MutableVector2l div(final MutableVector2l v) { + return this.div(v.x, v.y); + } + + public MutableVector2l div(final Vector2l v) { + return this.div(v.x(), v.y()); + } + + public MutableVector2l div(final double x, final double y) { + return this.div(GenericMath.floorl(x), GenericMath.floorl(y)); + } + + public MutableVector2l div(final long x, final long y) { + this.x /= x; + this.y /= y; + return this; + } + + public long dot(final MutableVector2l v) { + return this.dot(v.x, v.y); + } + + public long dot(final Vector2l v) { + return this.dot(v.x(), v.y()); + } + + public long dot(final double x, final double y) { + return this.dot(GenericMath.floorl(x), GenericMath.floorl(y)); + } + + public long dot(final long x, final long y) { + return this.x * x + this.y * y; + } + + public MutableVector2l project(final MutableVector2l v) { + return this.project(v.x, v.y); + } + + public MutableVector2l project(final Vector2l v) { + return this.project(v.x(), v.y()); + } + + public MutableVector2l project(final double x, final double y) { + return this.project(GenericMath.floorl(x), GenericMath.floorl(y)); + } + + public MutableVector2l project(final long x, final long y) { + final long lengthSquared = x * x + y * y; + if (lengthSquared == 0) { + throw new ArithmeticException("Cannot project onto the zero vector"); + } + final float a = (float) this.dot(x, y) / lengthSquared; + return this.set(a * x, a * y); + } + + public MutableVector2l pow(final double pow) { + return this.pow(GenericMath.floorl(pow)); + } + + @Override + public MutableVector2l pow(final long pow) { + return this.pow(pow, pow); + } + + public MutableVector2l pow(final MutableVector2l v) { + return this.pow(v.x, v.y); + } + + public MutableVector2l pow(final Vector2l v) { + return this.pow(v.x(), v.y()); + } + + public MutableVector2l pow(final double x, final double y) { + return this.pow(GenericMath.floorl(x), GenericMath.floorl(y)); + } + + public MutableVector2l pow(final long x, final long y) { + return this.set(Math.pow(this.x, x), Math.pow(this.y, y)); + } + + @Override + public MutableVector2l abs() { + return this.set(Math.abs(this.x), Math.abs(this.y)); + } + + @Override + public MutableVector2l negate() { + return this.set(-this.x, -this.y); + } + + public MutableVector2l min(final MutableVector2l v) { + return this.min(v.x, v.y); + } + + public MutableVector2l min(final Vector2l v) { + return this.min(v.x(), v.y()); + } + + public MutableVector2l min(final double x, final double y) { + return this.min(GenericMath.floorl(x), GenericMath.floorl(y)); + } + + public MutableVector2l min(final long x, final long y) { + return this.set(Math.min(this.x, x), Math.min(this.y, y)); + } + + public MutableVector2l max(final MutableVector2l v) { + return this.max(v.x, v.y); + } + + public MutableVector2l max(final Vector2l v) { + return this.max(v.x(), v.y()); + } + + public MutableVector2l max(final double x, final double y) { + return this.max(GenericMath.floorl(x), GenericMath.floorl(y)); + } + + public MutableVector2l max(final long x, final long y) { + return this.set(Math.max(this.x, x), Math.max(this.y, y)); + } + + public long distanceSquared(final MutableVector2l v) { + return this.distanceSquared(v.x, v.y); + } + + public long distanceSquared(final Vector2l v) { + return this.distanceSquared(v.x(), v.y()); + } + + public long distanceSquared(final double x, final double y) { + return this.distanceSquared(GenericMath.floorl(x), GenericMath.floorl(y)); + } + + public long distanceSquared(final long x, final long y) { + final long dx = this.x - x; + final long dy = this.y - y; + return dx * dx + dy * dy; + } + + public float distance(final MutableVector2l v) { + return this.distance(v.x, v.y); + } + + public float distance(final Vector2l v) { + return this.distance(v.x(), v.y()); + } + + public float distance(final double x, final double y) { + return this.distance(GenericMath.floorl(x), GenericMath.floorl(y)); + } + + public float distance(final long x, final long y) { + return (float) Math.sqrt(this.distanceSquared(x, y)); + } + + @Override + public long lengthSquared() { + return this.x * this.x + this.y * this.y; + } + + @Override + public double length() { + return Math.sqrt(this.lengthSquared()); + } + + @Override + public int minAxis() { + return this.x < this.y ? 0 : 1; + } + + @Override + public int maxAxis() { + return this.x > this.y ? 0 : 1; + } + + @Override + public long[] toArray() { + return new long[]{this.x, this.y}; + } + + @Override + public Vector2i toInt() { + return new Vector2i(this.x, this.y); + } + + @Override + public Vector2l toLong() { + return new Vector2l(this.x, this.y); + } + + @Override + public Vector2f toFloat() { + return new Vector2f(this.x, this.y); + } + + @Override + public Vector2d toDouble() { + return new Vector2d((double) this.x, (double) this.y); + } + + @Override + public boolean equals(final Object other) { + if (this == other) { + return true; + } else if (!(other instanceof final MutableVector2l that)) { + return false; + } else { + return that.x == this.x + && that.y == this.y; + } + } + + @Override + public int hashCode() { + return Long.hashCode(this.x) * 31 + Long.hashCode(this.y); + } + + @Override + public String toString() { + return "(" + this.x + ", " + this.y + ")"; + } + + public static MutableVector2l zero() { + return MutableVector2l.of(0, 0); + } + + public static MutableVector2l unitX() { + return MutableVector2l.of(1, 0); + } + + public static MutableVector2l unitY() { + return MutableVector2l.of(0, 1); + } + + public static MutableVector2l one() { + return MutableVector2l.of(1, 1); + } + + public static MutableVector2l from(final double n) { + return MutableVector2l.from(GenericMath.floorl(n)); + } + + public static MutableVector2l from(final long n) { + return MutableVector2l.of(n, n); + } + + public static MutableVector2l from(final MutableVector2l v) { + return MutableVector2l.of(v.x, v.y); + } + + public static MutableVector2l from(final Vector2l v) { + return MutableVector2l.of(v.x(), v.y()); + } + + public static MutableVector2l of(final double x, final double y) { + return new MutableVector2l(x, y); + } + + public static MutableVector2l of(final long x, final long y) { + return new MutableVector2l(x, y); + } +} diff --git a/api/src/main/java/net/hellheim/spongetools/math/mutable/vector/MutableVector3d.java b/api/src/main/java/net/hellheim/spongetools/math/mutable/vector/MutableVector3d.java new file mode 100644 index 0000000..88aedc5 --- /dev/null +++ b/api/src/main/java/net/hellheim/spongetools/math/mutable/vector/MutableVector3d.java @@ -0,0 +1,411 @@ +package net.hellheim.spongetools.math.mutable.vector; + +import org.spongepowered.math.GenericMath; +import org.spongepowered.math.vector.Vector3d; +import org.spongepowered.math.vector.Vector3f; +import org.spongepowered.math.vector.Vector3i; +import org.spongepowered.math.vector.Vector3l; +import org.spongepowered.math.vector.Vectord; + +public final class MutableVector3d implements Vectord { + + private double x; + private double y; + private double z; + + public MutableVector3d(final double x, final double y, final double z) { + this.x = x; + this.y = y; + this.z = z; + } + + public double x() { + return this.x; + } + + public double y() { + return this.y; + } + + public double z() { + return this.z; + } + + public MutableVector3d copy() { + return MutableVector3d.from(this); + } + + public MutableVector3d x(final MutableVector3d v) { + return this.x(v.x); + } + + public MutableVector3d x(final Vector3d v) { + return this.x(v.x()); + } + + public MutableVector3d x(final double a) { + this.x = a; + return this; + } + + public MutableVector3d y(final MutableVector3d v) { + return this.y(v.y); + } + + public MutableVector3d y(final Vector3d v) { + return this.y(v.y()); + } + + public MutableVector3d y(final double a) { + this.y = a; + return this; + } + + public MutableVector3d z(final MutableVector3d v) { + return this.z(v.z); + } + + public MutableVector3d z(final Vector3d v) { + return this.z(v.z()); + } + + public MutableVector3d z(final double a) { + this.z = a; + return this; + } + + public MutableVector3d set(final MutableVector3d v) { + return this.set(v.x, v.y, v.z); + } + + public MutableVector3d set(final Vector3d v) { + return this.set(v.x(), v.y(), v.z()); + } + + public MutableVector3d set(final double x, final double y, final double z) { + this.x = x; + this.y = y; + this.z = z; + return this; + } + + public MutableVector3d add(final MutableVector3d v) { + return this.add(v.x, v.y, v.z); + } + + public MutableVector3d add(final Vector3d v) { + return this.add(v.x(), v.y(), v.z()); + } + + public MutableVector3d add(final double x, final double y, final double z) { + this.x += x; + this.y += y; + this.z += z; + return this; + } + + public MutableVector3d sub(final MutableVector3d v) { + return this.sub(v.x, v.y, v.z); + } + + public MutableVector3d sub(final Vector3d v) { + return this.sub(v.x(), v.y(), v.z()); + } + + public MutableVector3d sub(final double x, final double y, final double z) { + this.x -= x; + this.y -= y; + this.z -= z; + return this; + } + + @Override + public MutableVector3d mul(final double a) { + return this.mul(a, a, a); + } + + public MutableVector3d mul(final MutableVector3d v) { + return this.mul(v.x, v.y, v.z); + } + + public MutableVector3d mul(final Vector3d v) { + return this.mul(v.x(), v.y(), v.z()); + } + + public MutableVector3d mul(final double x, final double y, final double z) { + this.x *= x; + this.y *= y; + this.z *= z; + return this; + } + + @Override + public MutableVector3d div(final double a) { + return this.div(a, a, a); + } + + public MutableVector3d div(final MutableVector3d v) { + return this.div(v.x, v.y, v.z); + } + + public MutableVector3d div(final Vector3d v) { + return this.div(v.x(), v.y(), v.z()); + } + + public MutableVector3d div(final double x, final double y, final double z) { + this.x /= x; + this.y /= y; + this.z /= z; + return this; + } + + public double dot(final MutableVector3d v) { + return this.dot(v.x, v.y, v.z); + } + + public double dot(final Vector3d v) { + return this.dot(v.x(), v.y(), v.z()); + } + + public double dot(final double x, final double y, final double z) { + return this.x * x + this.y * y + this.z * z; + } + + public MutableVector3d project(final MutableVector3d v) { + return this.project(v.x, v.y, v.z); + } + + public MutableVector3d project(final Vector3d v) { + return this.project(v.x(), v.y(), v.z()); + } + + public MutableVector3d project(final double x, final double y, final double z) { + final double lengthSquared = x * x + y * y + z * z; + if (lengthSquared == 0) { + throw new ArithmeticException("Cannot project onto the zero vector"); + } + final double a = this.dot(x, y, z) / lengthSquared; + return this.set(a * x, a * y, a * z); + } + + public MutableVector3d cross(final MutableVector3d v) { + return this.cross(v.x, v.y, v.z); + } + + public MutableVector3d cross(final Vector3d v) { + return this.cross(v.x(), v.y(), v.z()); + } + + public MutableVector3d cross(final double x, final double y, final double z) { + return this.set(this.y * z - this.z * y, this.z * x - this.x * z, this.x * y - this.y * x); + } + + @Override + public MutableVector3d pow(final double pow) { + return this.pow(pow, pow, pow); + } + + public MutableVector3d pow(final MutableVector3d v) { + return this.pow(v.x, v.y, v.z); + } + + public MutableVector3d pow(final Vector3d v) { + return this.pow(v.x(), v.y(), v.z()); + } + + public MutableVector3d pow(final double x, final double y, final double z) { + return this.set(Math.pow(this.x, x), Math.pow(this.y, y), Math.pow(this.z, z)); + } + + @Override + public MutableVector3d ceil() { + return this.set(Math.ceil(this.x), Math.ceil(this.y), Math.ceil(this.z)); + } + + @Override + public MutableVector3d floor() { + return this.set(GenericMath.floor(this.x), GenericMath.floor(this.y), GenericMath.floor(this.z)); + } + + @Override + public MutableVector3d round() { + return this.set(Math.round(this.x), Math.round(this.y), Math.round(this.z)); + } + + @Override + public MutableVector3d abs() { + return this.set(Math.abs(this.x), Math.abs(this.y), Math.abs(this.z)); + } + + @Override + public MutableVector3d negate() { + return this.set(-this.x, -this.y, -this.z); + } + + public MutableVector3d min(final MutableVector3d v) { + return this.min(v.x, v.y, v.z); + } + + public MutableVector3d min(final Vector3d v) { + return this.min(v.x(), v.y(), v.z()); + } + + public MutableVector3d min(final double x, final double y, final double z) { + return this.set(Math.min(this.x, x), Math.min(this.y, y), Math.min(this.z, z)); + } + + public MutableVector3d max(final MutableVector3d v) { + return this.max(v.x, v.y, v.z); + } + + public MutableVector3d max(final Vector3d v) { + return this.max(v.x(), v.y(), v.z()); + } + + public MutableVector3d max(final double x, final double y, final double z) { + return this.set(Math.max(this.x, x), Math.max(this.y, y), Math.max(this.z, z)); + } + + public double distanceSquared(final MutableVector3d v) { + return this.distanceSquared(v.x, v.y, v.z); + } + + public double distanceSquared(final Vector3d v) { + return this.distanceSquared(v.x(), v.y(), v.z()); + } + + public double distanceSquared(final double x, final double y, final double z) { + final double dx = this.x - x; + final double dy = this.y - y; + final double dz = this.z - z; + return dx * dx + dy * dy + dz * dz; + } + + public double distance(final MutableVector3d v) { + return this.distance(v.x, v.y, v.z); + } + + public double distance(final Vector3d v) { + return this.distance(v.x(), v.y(), v.z()); + } + + public double distance(final double x, final double y, final double z) { + return Math.sqrt(this.distanceSquared(x, y, z)); + } + + @Override + public double lengthSquared() { + return this.x * this.x + this.y * this.y + this.z * this.z; + } + + @Override + public double length() { + return Math.sqrt(this.lengthSquared()); + } + + @Override + public MutableVector3d normalize() { + final double length = this.length(); + if (Math.abs(length) < GenericMath.FLT_EPSILON) { + throw new ArithmeticException("Cannot normalize the zero vector"); + } + return this.set(this.x / length, this.y / length, this.z / length); + } + + @Override + public int minAxis() { + return this.x < this.y ? (this.x < this.z ? 0 : 2) : (this.y < this.z ? 1 : 2); + } + + @Override + public int maxAxis() { + return this.x < this.y ? (this.y < this.z ? 2 : 1) : (this.x < this.z ? 2 : 0); + } + + @Override + public double[] toArray() { + return new double[]{this.x, this.y, this.z}; + } + + @Override + public Vector3i toInt() { + return new Vector3i(this.x, this.y, this.z); + } + + @Override + public Vector3l toLong() { + return new Vector3l(this.x, this.y, this.z); + } + + @Override + public Vector3f toFloat() { + return new Vector3f(this.x, this.y, this.z); + } + + @Override + public Vector3d toDouble() { + return new Vector3d((double) this.x, (double) this.y, (double) this.z); + } + + @Override + public boolean equals(final Object other) { + if (this == other) { + return true; + } else if (!(other instanceof final MutableVector3d that)) { + return false; + } else { + return Double.compare(that.x, this.x) == 0 + && Double.compare(that.y, this.y) == 0 + && Double.compare(that.z, this.z) == 0; + } + } + + @Override + public int hashCode() { + int result = Double.hashCode(this.x); + result = result * 31 + Double.hashCode(this.y); + result = result * 31 + Double.hashCode(this.z); + return result; + } + + @Override + public String toString() { + return "(" + this.x + ", " + this.y + ", " + this.z + ")"; + } + + public static MutableVector3d zero() { + return MutableVector3d.of(0, 0, 0); + } + + public static MutableVector3d unitX() { + return MutableVector3d.of(1, 0, 0); + } + + public static MutableVector3d unitY() { + return MutableVector3d.of(0, 1, 0); + } + + public static MutableVector3d unitZ() { + return MutableVector3d.of(0, 0, 1); + } + + public static MutableVector3d one() { + return MutableVector3d.of(1, 1, 1); + } + + public static MutableVector3d from(final double n) { + return MutableVector3d.of(n, n, n); + } + + public static MutableVector3d from(final MutableVector3d v) { + return MutableVector3d.of(v.x, v.y, v.z); + } + + public static MutableVector3d from(final Vector3d v) { + return MutableVector3d.of(v.x(), v.y(), v.z()); + } + + public static MutableVector3d of(final double x, final double y, final double z) { + return new MutableVector3d(x, y, z); + } +} diff --git a/api/src/main/java/net/hellheim/spongetools/math/mutable/vector/MutableVector3f.java b/api/src/main/java/net/hellheim/spongetools/math/mutable/vector/MutableVector3f.java new file mode 100644 index 0000000..3ba8ac0 --- /dev/null +++ b/api/src/main/java/net/hellheim/spongetools/math/mutable/vector/MutableVector3f.java @@ -0,0 +1,499 @@ +package net.hellheim.spongetools.math.mutable.vector; + +import org.spongepowered.math.GenericMath; +import org.spongepowered.math.vector.Vector3d; +import org.spongepowered.math.vector.Vector3f; +import org.spongepowered.math.vector.Vector3i; +import org.spongepowered.math.vector.Vector3l; +import org.spongepowered.math.vector.Vectorf; + +public final class MutableVector3f implements Vectorf { + + private float x; + private float y; + private float z; + + public MutableVector3f(final double x, final double y, final double z) { + this((float) x, (float) y, (float) z); + } + + public MutableVector3f(final float x, final float y, final float z) { + this.x = x; + this.y = y; + this.z = z; + } + + public float x() { + return this.x; + } + + public float y() { + return this.y; + } + + public float z() { + return this.z; + } + + public MutableVector3f copy() { + return MutableVector3f.from(this); + } + + public MutableVector3f x(final MutableVector3f v) { + return this.x(v.x); + } + + public MutableVector3f x(final Vector3f v) { + return this.x(v.x()); + } + + public MutableVector3f x(final double a) { + return this.x((float) a); + } + + public MutableVector3f x(final float a) { + this.x = a; + return this; + } + + public MutableVector3f y(final MutableVector3f v) { + return this.y(v.y); + } + + public MutableVector3f y(final Vector3f v) { + return this.y(v.y()); + } + + public MutableVector3f y(final double a) { + return this.y((float) a); + } + + public MutableVector3f y(final float a) { + this.y = a; + return this; + } + + public MutableVector3f z(final MutableVector3f v) { + return this.z(v.z); + } + + public MutableVector3f z(final Vector3f v) { + return this.z(v.z()); + } + + public MutableVector3f z(final double a) { + return this.z((float) a); + } + + public MutableVector3f z(final float a) { + this.z = a; + return this; + } + + public MutableVector3f set(final MutableVector3f v) { + return this.set(v.x, v.y, v.z); + } + + public MutableVector3f set(final Vector3f v) { + return this.set(v.x(), v.y(), v.z()); + } + + public MutableVector3f set(final double x, final double y, final double z) { + return this.set((float) x, (float) y, (float) z); + } + + public MutableVector3f set(final float x, final float y, final float z) { + this.x = x; + this.y = y; + this.z = z; + return this; + } + + public MutableVector3f add(final MutableVector3f v) { + return this.add(v.x, v.y, v.z); + } + + public MutableVector3f add(final Vector3f v) { + return this.add(v.x(), v.y(), v.z()); + } + + public MutableVector3f add(final double x, final double y, final double z) { + return this.add((float) x, (float) y, (float) z); + } + + public MutableVector3f add(final float x, final float y, final float z) { + this.x += x; + this.y += y; + this.z += z; + return this; + } + + public MutableVector3f sub(final MutableVector3f v) { + return this.sub(v.x, v.y, v.z); + } + + public MutableVector3f sub(final Vector3f v) { + return this.sub(v.x(), v.y(), v.z()); + } + + public MutableVector3f sub(final double x, final double y, final double z) { + return this.sub((float) x, (float) y, (float) z); + } + + public MutableVector3f sub(final float x, final float y, final float z) { + this.x -= x; + this.y -= y; + this.z -= z; + return this; + } + + public MutableVector3f mul(final double a) { + return this.mul((float) a); + } + + @Override + public MutableVector3f mul(final float a) { + return this.mul(a, a, a); + } + + public MutableVector3f mul(final MutableVector3f v) { + return this.mul(v.x, v.y, v.z); + } + + public MutableVector3f mul(final Vector3f v) { + return this.mul(v.x(), v.y(), v.z()); + } + + public MutableVector3f mul(final double x, final double y, final double z) { + return this.mul((float) x, (float) y, (float) z); + } + + public MutableVector3f mul(final float x, final float y, final float z) { + this.x *= x; + this.y *= y; + this.z *= z; + return this; + } + + public MutableVector3f div(final double a) { + return this.div((float) a); + } + + @Override + public MutableVector3f div(final float a) { + return this.div(a, a, a); + } + + public MutableVector3f div(final MutableVector3f v) { + return this.div(v.x, v.y, v.z); + } + + public MutableVector3f div(final Vector3f v) { + return this.div(v.x(), v.y(), v.z()); + } + + public MutableVector3f div(final double x, final double y, final double z) { + return this.div((float) x, (float) y, (float) z); + } + + public MutableVector3f div(final float x, final float y, final float z) { + this.x /= x; + this.y /= y; + this.z /= z; + return this; + } + + public float dot(final MutableVector3f v) { + return this.dot(v.x, v.y, v.z); + } + + public float dot(final Vector3f v) { + return this.dot(v.x(), v.y(), v.z()); + } + + public float dot(final double x, final double y, final double z) { + return this.dot((float) x, (float) y, (float) z); + } + + public float dot(final float x, final float y, final float z) { + return this.x * x + this.y * y + this.z * z; + } + + public MutableVector3f project(final MutableVector3f v) { + return this.project(v.x, v.y, v.z); + } + + public MutableVector3f project(final Vector3f v) { + return this.project(v.x(), v.y(), v.z()); + } + + public MutableVector3f project(final double x, final double y, final double z) { + return this.project((float) x, (float) y, (float) z); + } + + public MutableVector3f project(final float x, final float y, final float z) { + final float lengthSquared = x * x + y * y + z * z; + if (lengthSquared == 0) { + throw new ArithmeticException("Cannot project onto the zero vector"); + } + final float a = this.dot(x, y, z) / lengthSquared; + return this.set(a * x, a * y, a * z); + } + + public MutableVector3f cross(final MutableVector3f v) { + return this.cross(v.x, v.y, v.z); + } + + public MutableVector3f cross(final Vector3f v) { + return this.cross(v.x(), v.y(), v.z()); + } + + public MutableVector3f cross(final double x, final double y, final double z) { + return this.cross((float) x, (float) y, (float) z); + } + + public MutableVector3f cross(final float x, final float y, final float z) { + return this.set(this.y * z - this.z * y, this.z * x - this.x * z, this.x * y - this.y * x); + } + + public MutableVector3f pow(final double pow) { + return this.pow((float) pow); + } + + @Override + public MutableVector3f pow(final float pow) { + return this.pow(pow, pow, pow); + } + + public MutableVector3f pow(final MutableVector3f v) { + return this.pow(v.x, v.y, v.z); + } + + public MutableVector3f pow(final Vector3f v) { + return this.pow(v.x(), v.y(), v.z()); + } + + public MutableVector3f pow(final double x, final double y, final double z) { + return this.pow((float) x, (float) y, (float) z); + } + + public MutableVector3f pow(final float x, final float y, final float z) { + return this.set(Math.pow(this.x, x), Math.pow(this.y, y), Math.pow(this.z, z)); + } + + @Override + public MutableVector3f ceil() { + return this.set(Math.ceil(this.x), Math.ceil(this.y), Math.ceil(this.z)); + } + + @Override + public MutableVector3f floor() { + return this.set(GenericMath.floor(this.x), GenericMath.floor(this.y), GenericMath.floor(this.z)); + } + + @Override + public MutableVector3f round() { + return this.set(Math.round(this.x), Math.round(this.y), Math.round(this.z)); + } + + @Override + public MutableVector3f abs() { + return this.set(Math.abs(this.x), Math.abs(this.y), Math.abs(this.z)); + } + + @Override + public MutableVector3f negate() { + return this.set(-this.x, -this.y, -this.z); + } + + public MutableVector3f min(final MutableVector3f v) { + return this.min(v.x, v.y, v.z); + } + + public MutableVector3f min(final Vector3f v) { + return this.min(v.x(), v.y(), v.z()); + } + + public MutableVector3f min(final double x, final double y, final double z) { + return this.min((float) x, (float) y, (float) z); + } + + public MutableVector3f min(final float x, final float y, final float z) { + return this.set(Math.min(this.x, x), Math.min(this.y, y), Math.min(this.z, z)); + } + + public MutableVector3f max(final MutableVector3f v) { + return this.max(v.x, v.y, v.z); + } + + public MutableVector3f max(final Vector3f v) { + return this.max(v.x(), v.y(), v.z()); + } + + public MutableVector3f max(final double x, final double y, final double z) { + return this.max((float) x, (float) y, (float) z); + } + + public MutableVector3f max(final float x, final float y, final float z) { + return this.set(Math.max(this.x, x), Math.max(this.y, y), Math.max(this.z, z)); + } + + public float distanceSquared(final MutableVector3f v) { + return this.distanceSquared(v.x, v.y, v.z); + } + + public float distanceSquared(final Vector3f v) { + return this.distanceSquared(v.x(), v.y(), v.z()); + } + + public float distanceSquared(final double x, final double y, final double z) { + return this.distanceSquared((float) x, (float) y, (float) z); + } + + public float distanceSquared(final float x, final float y, final float z) { + final float dx = this.x - x; + final float dy = this.y - y; + final float dz = this.z - z; + return dx * dx + dy * dy + dz * dz; + } + + public float distance(final MutableVector3f v) { + return this.distance(v.x, v.y, v.z); + } + + public float distance(final Vector3f v) { + return this.distance(v.x(), v.y(), v.z()); + } + + public float distance(final double x, final double y, final double z) { + return this.distance((float) x, (float) y, (float) z); + } + + public float distance(final float x, final float y, final float z) { + return (float) Math.sqrt(this.distanceSquared(x, y, z)); + } + + @Override + public float lengthSquared() { + return this.x * this.x + this.y * this.y + this.z * this.z; + } + + @Override + public float length() { + return (float) Math.sqrt(this.lengthSquared()); + } + + @Override + public MutableVector3f normalize() { + final float length = this.length(); + if (Math.abs(length) < GenericMath.FLT_EPSILON) { + throw new ArithmeticException("Cannot normalize the zero vector"); + } + return this.set(this.x / length, this.y / length, this.z / length); + } + + @Override + public int minAxis() { + return this.x < this.y ? (this.x < this.z ? 0 : 2) : (this.y < this.z ? 1 : 2); + } + + @Override + public int maxAxis() { + return this.x < this.y ? (this.y < this.z ? 2 : 1) : (this.x < this.z ? 2 : 0); + } + + @Override + public float[] toArray() { + return new float[]{this.x, this.y, this.z}; + } + + @Override + public Vector3i toInt() { + return new Vector3i(this.x, this.y, this.z); + } + + @Override + public Vector3l toLong() { + return new Vector3l(this.x, this.y, this.z); + } + + @Override + public Vector3f toFloat() { + return new Vector3f(this.x, this.y, this.z); + } + + @Override + public Vector3d toDouble() { + return new Vector3d((double) this.x, (double) this.y, (double) this.z); + } + + @Override + public boolean equals(final Object other) { + if (this == other) { + return true; + } else if (!(other instanceof final MutableVector3f that)) { + return false; + } else { + return Float.compare(that.x, this.x) == 0 + && Float.compare(that.y, this.y) == 0 + && Float.compare(that.z, this.z) == 0; + } + } + + @Override + public int hashCode() { + int result = Float.hashCode(this.x); + result = result * 31 + Float.hashCode(this.y); + result = result * 31 + Float.hashCode(this.z); + return result; + } + + @Override + public String toString() { + return "(" + this.x + ", " + this.y + ", " + this.z + ")"; + } + + public static MutableVector3f zero() { + return MutableVector3f.of(0, 0, 0); + } + + public static MutableVector3f unitX() { + return MutableVector3f.of(1, 0, 0); + } + + public static MutableVector3f unitY() { + return MutableVector3f.of(0, 1, 0); + } + + public static MutableVector3f unitZ() { + return MutableVector3f.of(0, 0, 1); + } + + public static MutableVector3f one() { + return MutableVector3f.of(1, 1, 1); + } + + public static MutableVector3f from(final double n) { + return MutableVector3f.from((float) n); + } + + public static MutableVector3f from(final float n) { + return MutableVector3f.of(n, n, n); + } + + public static MutableVector3f from(final MutableVector3f v) { + return MutableVector3f.of(v.x, v.y, v.z); + } + + public static MutableVector3f from(final Vector3f v) { + return MutableVector3f.of(v.x(), v.y(), v.z()); + } + + public static MutableVector3f of(final double x, final double y, final double z) { + return new MutableVector3f(x, y, z); + } + + public static MutableVector3f of(final float x, final float y, final float z) { + return new MutableVector3f(x, y, z); + } +} diff --git a/api/src/main/java/net/hellheim/spongetools/math/mutable/vector/MutableVector3i.java b/api/src/main/java/net/hellheim/spongetools/math/mutable/vector/MutableVector3i.java new file mode 100644 index 0000000..7055b1c --- /dev/null +++ b/api/src/main/java/net/hellheim/spongetools/math/mutable/vector/MutableVector3i.java @@ -0,0 +1,475 @@ +package net.hellheim.spongetools.math.mutable.vector; + +import org.spongepowered.math.GenericMath; +import org.spongepowered.math.vector.Vector3d; +import org.spongepowered.math.vector.Vector3f; +import org.spongepowered.math.vector.Vector3i; +import org.spongepowered.math.vector.Vector3l; +import org.spongepowered.math.vector.Vectori; + +public final class MutableVector3i implements Vectori { + + private int x; + private int y; + private int z; + + public MutableVector3i(final double x, final double y, final double z) { + this(GenericMath.floor(x), GenericMath.floor(y), GenericMath.floor(z)); + } + + public MutableVector3i(final int x, final int y, final int z) { + this.x = x; + this.y = y; + this.z = z; + } + + public int x() { + return this.x; + } + + public int y() { + return this.y; + } + + public int z() { + return this.z; + } + + public MutableVector3i copy() { + return MutableVector3i.from(this); + } + + public MutableVector3i x(final MutableVector3i v) { + return this.x(v.x); + } + + public MutableVector3i x(final Vector3i v) { + return this.x(v.x()); + } + + public MutableVector3i x(final double a) { + return this.x(GenericMath.floor(a)); + } + + public MutableVector3i x(final int a) { + this.x = a; + return this; + } + + public MutableVector3i y(final MutableVector3i v) { + return this.y(v.y); + } + + public MutableVector3i y(final Vector3i v) { + return this.y(v.y()); + } + + public MutableVector3i y(final double a) { + return this.y(GenericMath.floor(a)); + } + + public MutableVector3i y(final int a) { + this.y = a; + return this; + } + + public MutableVector3i z(final MutableVector3i v) { + return this.z(v.z); + } + + public MutableVector3i z(final Vector3i v) { + return this.z(v.z()); + } + + public MutableVector3i z(final double a) { + return this.z(GenericMath.floor(a)); + } + + public MutableVector3i z(final int a) { + this.z = a; + return this; + } + + public MutableVector3i set(final MutableVector3i v) { + return this.set(v.x, v.y, v.z); + } + + public MutableVector3i set(final Vector3i v) { + return this.set(v.x(), v.y(), v.z()); + } + + public MutableVector3i set(final double x, final double y, final double z) { + return this.set(GenericMath.floor(x), GenericMath.floor(y), GenericMath.floor(z)); + } + + public MutableVector3i set(final int x, final int y, final int z) { + this.x = x; + this.y = y; + this.z = z; + return this; + } + + public MutableVector3i add(final MutableVector3i v) { + return this.add(v.x, v.y, v.z); + } + + public MutableVector3i add(final Vector3i v) { + return this.add(v.x(), v.y(), v.z()); + } + + public MutableVector3i add(final double x, final double y, final double z) { + return this.add(GenericMath.floor(x), GenericMath.floor(y), GenericMath.floor(z)); + } + + public MutableVector3i add(final int x, final int y, final int z) { + this.x += x; + this.y += y; + this.z += z; + return this; + } + + public MutableVector3i sub(final MutableVector3i v) { + return this.sub(v.x, v.y, v.z); + } + + public MutableVector3i sub(final Vector3i v) { + return this.sub(v.x(), v.y(), v.z()); + } + + public MutableVector3i sub(final double x, final double y, final double z) { + return this.sub(GenericMath.floor(x), GenericMath.floor(y), GenericMath.floor(z)); + } + + public MutableVector3i sub(final int x, final int y, final int z) { + this.x -= x; + this.y -= y; + this.z -= z; + return this; + } + + public MutableVector3i mul(final double a) { + return this.mul(GenericMath.floor(a)); + } + + @Override + public MutableVector3i mul(final int a) { + return this.mul(a, a, a); + } + + public MutableVector3i mul(final MutableVector3i v) { + return this.mul(v.x, v.y, v.z); + } + + public MutableVector3i mul(final Vector3i v) { + return this.mul(v.x(), v.y(), v.z()); + } + + public MutableVector3i mul(final double x, final double y, final double z) { + return this.mul(GenericMath.floor(x), GenericMath.floor(y), GenericMath.floor(z)); + } + + public MutableVector3i mul(final int x, final int y, final int z) { + this.x *= x; + this.y *= y; + this.z *= z; + return this; + } + + public MutableVector3i div(final double a) { + return this.div(GenericMath.floor(a)); + } + + @Override + public MutableVector3i div(final int a) { + return this.div(a, a, a); + } + + public MutableVector3i div(final MutableVector3i v) { + return this.div(v.x, v.y, v.z); + } + + public MutableVector3i div(final Vector3i v) { + return this.div(v.x(), v.y(), v.z()); + } + + public MutableVector3i div(final double x, final double y, final double z) { + return this.div(GenericMath.floor(x), GenericMath.floor(y), GenericMath.floor(z)); + } + + public MutableVector3i div(final int x, final int y, final int z) { + this.x /= x; + this.y /= y; + this.z /= z; + return this; + } + + public int dot(final MutableVector3i v) { + return this.dot(v.x, v.y, v.z); + } + + public int dot(final Vector3i v) { + return this.dot(v.x(), v.y(), v.z()); + } + + public int dot(final double x, final double y, final double z) { + return this.dot(GenericMath.floor(x), GenericMath.floor(y), GenericMath.floor(z)); + } + + public int dot(final int x, final int y, final int z) { + return this.x * x + this.y * y + this.z * z; + } + + public MutableVector3i project(final MutableVector3i v) { + return this.project(v.x, v.y, v.z); + } + + public MutableVector3i project(final Vector3i v) { + return this.project(v.x(), v.y(), v.z()); + } + + public MutableVector3i project(final double x, final double y, final double z) { + return this.project(GenericMath.floor(x), GenericMath.floor(y), GenericMath.floor(z)); + } + + public MutableVector3i project(final int x, final int y, final int z) { + final int lengthSquared = x * x + y * y + z * z; + if (lengthSquared == 0) { + throw new ArithmeticException("Cannot project onto the zero vector"); + } + final float a = (float) this.dot(x, y, z) / lengthSquared; + return this.set(a * x, a * y, a * z); + } + + public MutableVector3i cross(final MutableVector3i v) { + return this.cross(v.x, v.y, v.z); + } + + public MutableVector3i cross(final Vector3i v) { + return this.cross(v.x(), v.y(), v.z()); + } + + public MutableVector3i cross(final double x, final double y, final double z) { + return this.cross(GenericMath.floor(x), GenericMath.floor(y), GenericMath.floor(z)); + } + + public MutableVector3i cross(final int x, final int y, final int z) { + return this.set(this.y * z - this.z * y, this.z * x - this.x * z, this.x * y - this.y * x); + } + + public MutableVector3i pow(final double pow) { + return this.pow(GenericMath.floor(pow)); + } + + @Override + public MutableVector3i pow(final int pow) { + return this.pow(pow, pow, pow); + } + + public MutableVector3i pow(final MutableVector3i v) { + return this.pow(v.x, v.y, v.z); + } + + public MutableVector3i pow(final Vector3i v) { + return this.pow(v.x(), v.y(), v.z()); + } + + public MutableVector3i pow(final double x, final double y, final double z) { + return this.pow(GenericMath.floor(x), GenericMath.floor(y), GenericMath.floor(z)); + } + + public MutableVector3i pow(final int x, final int y, final int z) { + return this.set(Math.pow(this.x, x), Math.pow(this.y, y), Math.pow(this.z, z)); + } + + @Override + public MutableVector3i abs() { + return this.set(Math.abs(this.x), Math.abs(this.y), Math.abs(this.z)); + } + + @Override + public MutableVector3i negate() { + return this.set(-this.x, -this.y, -this.z); + } + + public MutableVector3i min(final MutableVector3i v) { + return this.min(v.x, v.y, v.z); + } + + public MutableVector3i min(final Vector3i v) { + return this.min(v.x(), v.y(), v.z()); + } + + public MutableVector3i min(final double x, final double y, final double z) { + return this.min(GenericMath.floor(x), GenericMath.floor(y), GenericMath.floor(z)); + } + + public MutableVector3i min(final int x, final int y, final int z) { + return this.set(Math.min(this.x, x), Math.min(this.y, y), Math.min(this.z, z)); + } + + public MutableVector3i max(final MutableVector3i v) { + return this.max(v.x, v.y, v.z); + } + + public MutableVector3i max(final Vector3i v) { + return this.max(v.x(), v.y(), v.z()); + } + + public MutableVector3i max(final double x, final double y, final double z) { + return this.max(GenericMath.floor(x), GenericMath.floor(y), GenericMath.floor(z)); + } + + public MutableVector3i max(final int x, final int y, final int z) { + return this.set(Math.max(this.x, x), Math.max(this.y, y), Math.max(this.z, z)); + } + + public int distanceSquared(final MutableVector3i v) { + return this.distanceSquared(v.x, v.y, v.z); + } + + public int distanceSquared(final Vector3i v) { + return this.distanceSquared(v.x(), v.y(), v.z()); + } + + public int distanceSquared(final double x, final double y, final double z) { + return this.distanceSquared(GenericMath.floor(x), GenericMath.floor(y), GenericMath.floor(z)); + } + + public int distanceSquared(final int x, final int y, final int z) { + final int dx = this.x - x; + final int dy = this.y - y; + final int dz = this.z - z; + return dx * dx + dy * dy + dz * dz; + } + + public float distance(final MutableVector3i v) { + return this.distance(v.x, v.y, v.z); + } + + public float distance(final Vector3i v) { + return this.distance(v.x(), v.y(), v.z()); + } + + public float distance(final double x, final double y, final double z) { + return this.distance(GenericMath.floor(x), GenericMath.floor(y), GenericMath.floor(z)); + } + + public float distance(final int x, final int y, final int z) { + return (float) Math.sqrt(this.distanceSquared(x, y, z)); + } + + @Override + public int lengthSquared() { + return this.x * this.x + this.y * this.y + this.z * this.z; + } + + @Override + public float length() { + return (float) Math.sqrt(this.lengthSquared()); + } + + @Override + public int minAxis() { + return this.x < this.y ? (this.x < this.z ? 0 : 2) : (this.y < this.z ? 1 : 2); + } + + @Override + public int maxAxis() { + return this.x < this.y ? (this.y < this.z ? 2 : 1) : (this.x < this.z ? 2 : 0); + } + + @Override + public int[] toArray() { + return new int[]{this.x, this.y, this.z}; + } + + @Override + public Vector3i toInt() { + return new Vector3i(this.x, this.y, this.z); + } + + @Override + public Vector3l toLong() { + return new Vector3l(this.x, this.y, this.z); + } + + @Override + public Vector3f toFloat() { + return new Vector3f(this.x, this.y, this.z); + } + + @Override + public Vector3d toDouble() { + return new Vector3d((double) this.x, (double) this.y, (double) this.z); + } + + @Override + public boolean equals(final Object other) { + if (this == other) { + return true; + } else if (!(other instanceof final MutableVector3i that)) { + return false; + } else { + return that.x == this.x + && that.y == this.y + && that.z == this.z; + } + } + + @Override + public int hashCode() { + int result = Integer.hashCode(this.x); + result = result * 31 + Integer.hashCode(this.y); + result = result * 31 + Integer.hashCode(this.z); + return result; + } + + @Override + public String toString() { + return "(" + this.x + ", " + this.y + ", " + this.z + ")"; + } + + public static MutableVector3i zero() { + return MutableVector3i.of(0, 0, 0); + } + + public static MutableVector3i unitX() { + return MutableVector3i.of(1, 0, 0); + } + + public static MutableVector3i unitY() { + return MutableVector3i.of(0, 1, 0); + } + + public static MutableVector3i unitZ() { + return MutableVector3i.of(0, 0, 1); + } + + public static MutableVector3i one() { + return MutableVector3i.of(1, 1, 1); + } + + public static MutableVector3i from(final double n) { + return MutableVector3i.from(GenericMath.floor(n)); + } + + public static MutableVector3i from(final int n) { + return MutableVector3i.of(n, n, n); + } + + public static MutableVector3i from(final MutableVector3i v) { + return MutableVector3i.of(v.x, v.y, v.z); + } + + public static MutableVector3i from(final Vector3i v) { + return MutableVector3i.of(v.x(), v.y(), v.z()); + } + + public static MutableVector3i of(final double x, final double y, final double z) { + return new MutableVector3i(x, y, z); + } + + public static MutableVector3i of(final int x, final int y, final int z) { + return new MutableVector3i(x, y, z); + } +} diff --git a/api/src/main/java/net/hellheim/spongetools/math/mutable/vector/MutableVector3l.java b/api/src/main/java/net/hellheim/spongetools/math/mutable/vector/MutableVector3l.java new file mode 100644 index 0000000..4e50792 --- /dev/null +++ b/api/src/main/java/net/hellheim/spongetools/math/mutable/vector/MutableVector3l.java @@ -0,0 +1,475 @@ +package net.hellheim.spongetools.math.mutable.vector; + +import org.spongepowered.math.GenericMath; +import org.spongepowered.math.vector.Vector3d; +import org.spongepowered.math.vector.Vector3f; +import org.spongepowered.math.vector.Vector3i; +import org.spongepowered.math.vector.Vector3l; +import org.spongepowered.math.vector.Vectorl; + +public final class MutableVector3l implements Vectorl { + + private long x; + private long y; + private long z; + + public MutableVector3l(final double x, final double y, final double z) { + this(GenericMath.floorl(x), GenericMath.floorl(y), GenericMath.floorl(z)); + } + + public MutableVector3l(final long x, final long y, final long z) { + this.x = x; + this.y = y; + this.z = z; + } + + public long x() { + return this.x; + } + + public long y() { + return this.y; + } + + public long z() { + return this.z; + } + + public MutableVector3l copy() { + return MutableVector3l.from(this); + } + + public MutableVector3l x(final MutableVector3l v) { + return this.x(v.x); + } + + public MutableVector3l x(final Vector3l v) { + return this.x(v.x()); + } + + public MutableVector3l x(final double a) { + return this.x(GenericMath.floorl(a)); + } + + public MutableVector3l x(final long a) { + this.x = a; + return this; + } + + public MutableVector3l y(final MutableVector3l v) { + return this.y(v.y); + } + + public MutableVector3l y(final Vector3l v) { + return this.y(v.y()); + } + + public MutableVector3l y(final double a) { + return this.y(GenericMath.floorl(a)); + } + + public MutableVector3l y(final long a) { + this.y = a; + return this; + } + + public MutableVector3l z(final MutableVector3l v) { + return this.z(v.z); + } + + public MutableVector3l z(final Vector3l v) { + return this.z(v.z()); + } + + public MutableVector3l z(final double a) { + return this.z(GenericMath.floorl(a)); + } + + public MutableVector3l z(final long a) { + this.z = a; + return this; + } + + public MutableVector3l set(final MutableVector3l v) { + return this.set(v.x, v.y, v.z); + } + + public MutableVector3l set(final Vector3l v) { + return this.set(v.x(), v.y(), v.z()); + } + + public MutableVector3l set(final double x, final double y, final double z) { + return this.set(GenericMath.floorl(x), GenericMath.floorl(y), GenericMath.floorl(z)); + } + + public MutableVector3l set(final long x, final long y, final long z) { + this.x = x; + this.y = y; + this.z = z; + return this; + } + + public MutableVector3l add(final MutableVector3l v) { + return this.add(v.x, v.y, v.z); + } + + public MutableVector3l add(final Vector3l v) { + return this.add(v.x(), v.y(), v.z()); + } + + public MutableVector3l add(final double x, final double y, final double z) { + return this.add(GenericMath.floorl(x), GenericMath.floorl(y), GenericMath.floorl(z)); + } + + public MutableVector3l add(final long x, final long y, final long z) { + this.x += x; + this.y += y; + this.z += z; + return this; + } + + public MutableVector3l sub(final MutableVector3l v) { + return this.sub(v.x, v.y, v.z); + } + + public MutableVector3l sub(final Vector3l v) { + return this.sub(v.x(), v.y(), v.z()); + } + + public MutableVector3l sub(final double x, final double y, final double z) { + return this.sub(GenericMath.floorl(x), GenericMath.floorl(y), GenericMath.floorl(z)); + } + + public MutableVector3l sub(final long x, final long y, final long z) { + this.x -= x; + this.y -= y; + this.z -= z; + return this; + } + + public MutableVector3l mul(final double a) { + return this.mul(GenericMath.floorl(a)); + } + + @Override + public MutableVector3l mul(final long a) { + return this.mul(a, a, a); + } + + public MutableVector3l mul(final MutableVector3l v) { + return this.mul(v.x, v.y, v.z); + } + + public MutableVector3l mul(final Vector3l v) { + return this.mul(v.x(), v.y(), v.z()); + } + + public MutableVector3l mul(final double x, final double y, final double z) { + return this.mul(GenericMath.floorl(x), GenericMath.floorl(y), GenericMath.floorl(z)); + } + + public MutableVector3l mul(final long x, final long y, final long z) { + this.x *= x; + this.y *= y; + this.z *= z; + return this; + } + + public MutableVector3l div(final double a) { + return this.div(GenericMath.floorl(a)); + } + + @Override + public MutableVector3l div(final long a) { + return this.div(a, a, a); + } + + public MutableVector3l div(final MutableVector3l v) { + return this.div(v.x, v.y, v.z); + } + + public MutableVector3l div(final Vector3l v) { + return this.div(v.x(), v.y(), v.z()); + } + + public MutableVector3l div(final double x, final double y, final double z) { + return this.div(GenericMath.floorl(x), GenericMath.floorl(y), GenericMath.floorl(z)); + } + + public MutableVector3l div(final long x, final long y, final long z) { + this.x /= x; + this.y /= y; + this.z /= z; + return this; + } + + public long dot(final MutableVector3l v) { + return this.dot(v.x, v.y, v.z); + } + + public long dot(final Vector3l v) { + return this.dot(v.x(), v.y(), v.z()); + } + + public long dot(final double x, final double y, final double z) { + return this.dot(GenericMath.floorl(x), GenericMath.floorl(y), GenericMath.floorl(z)); + } + + public long dot(final long x, final long y, final long z) { + return this.x * x + this.y * y + this.z * z; + } + + public MutableVector3l project(final MutableVector3l v) { + return this.project(v.x, v.y, v.z); + } + + public MutableVector3l project(final Vector3l v) { + return this.project(v.x(), v.y(), v.z()); + } + + public MutableVector3l project(final double x, final double y, final double z) { + return this.project(GenericMath.floorl(x), GenericMath.floorl(y), GenericMath.floorl(z)); + } + + public MutableVector3l project(final long x, final long y, final long z) { + final long lengthSquared = x * x + y * y + z * z; + if (lengthSquared == 0) { + throw new ArithmeticException("Cannot project onto the zero vector"); + } + final double a = (double) this.dot(x, y, z) / lengthSquared; + return this.set(a * x, a * y, a * z); + } + + public MutableVector3l cross(final MutableVector3l v) { + return this.cross(v.x, v.y, v.z); + } + + public MutableVector3l cross(final Vector3l v) { + return this.cross(v.x(), v.y(), v.z()); + } + + public MutableVector3l cross(final double x, final double y, final double z) { + return this.cross(GenericMath.floorl(x), GenericMath.floorl(y), GenericMath.floorl(z)); + } + + public MutableVector3l cross(final long x, final long y, final long z) { + return this.set(this.y * z - this.z * y, this.z * x - this.x * z, this.x * y - this.y * x); + } + + public MutableVector3l pow(final double pow) { + return this.pow(GenericMath.floorl(pow)); + } + + @Override + public MutableVector3l pow(final long pow) { + return this.pow(pow, pow, pow); + } + + public MutableVector3l pow(final MutableVector3l v) { + return this.pow(v.x, v.y, v.z); + } + + public MutableVector3l pow(final Vector3l v) { + return this.pow(v.x(), v.y(), v.z()); + } + + public MutableVector3l pow(final double x, final double y, final double z) { + return this.pow(GenericMath.floorl(x), GenericMath.floorl(y), GenericMath.floorl(z)); + } + + public MutableVector3l pow(final long x, final long y, final long z) { + return this.set(Math.pow(this.x, x), Math.pow(this.y, y), Math.pow(this.z, z)); + } + + @Override + public MutableVector3l abs() { + return this.set(Math.abs(this.x), Math.abs(this.y), Math.abs(this.z)); + } + + @Override + public MutableVector3l negate() { + return this.set(-this.x, -this.y, -this.z); + } + + public MutableVector3l min(final MutableVector3l v) { + return this.min(v.x, v.y, v.z); + } + + public MutableVector3l min(final Vector3l v) { + return this.min(v.x(), v.y(), v.z()); + } + + public MutableVector3l min(final double x, final double y, final double z) { + return this.min(GenericMath.floorl(x), GenericMath.floorl(y), GenericMath.floorl(z)); + } + + public MutableVector3l min(final long x, final long y, final long z) { + return this.set(Math.min(this.x, x), Math.min(this.y, y), Math.min(this.z, z)); + } + + public MutableVector3l max(final MutableVector3l v) { + return this.max(v.x, v.y, v.z); + } + + public MutableVector3l max(final Vector3l v) { + return this.max(v.x(), v.y(), v.z()); + } + + public MutableVector3l max(final double x, final double y, final double z) { + return this.max(GenericMath.floorl(x), GenericMath.floorl(y), GenericMath.floorl(z)); + } + + public MutableVector3l max(final long x, final long y, final long z) { + return this.set(Math.max(this.x, x), Math.max(this.y, y), Math.max(this.z, z)); + } + + public long distanceSquared(final MutableVector3l v) { + return this.distanceSquared(v.x, v.y, v.z); + } + + public long distanceSquared(final Vector3l v) { + return this.distanceSquared(v.x(), v.y(), v.z()); + } + + public long distanceSquared(final double x, final double y, final double z) { + return this.distanceSquared(GenericMath.floorl(x), GenericMath.floorl(y), GenericMath.floorl(z)); + } + + public long distanceSquared(final long x, final long y, final long z) { + final long dx = this.x - x; + final long dy = this.y - y; + final long dz = this.z - z; + return dx * dx + dy * dy + dz * dz; + } + + public double distance(final MutableVector3l v) { + return this.distance(v.x, v.y, v.z); + } + + public double distance(final Vector3l v) { + return this.distance(v.x(), v.y(), v.z()); + } + + public double distance(final double x, final double y, final double z) { + return this.distance(GenericMath.floorl(x), GenericMath.floorl(y), GenericMath.floorl(z)); + } + + public double distance(final long x, final long y, final long z) { + return Math.sqrt(this.distanceSquared(x, y, z)); + } + + @Override + public long lengthSquared() { + return this.x * this.x + this.y * this.y + this.z * this.z; + } + + @Override + public double length() { + return Math.sqrt(this.lengthSquared()); + } + + @Override + public int minAxis() { + return this.x < this.y ? (this.x < this.z ? 0 : 2) : (this.y < this.z ? 1 : 2); + } + + @Override + public int maxAxis() { + return this.x < this.y ? (this.y < this.z ? 2 : 1) : (this.x < this.z ? 2 : 0); + } + + @Override + public long[] toArray() { + return new long[]{this.x, this.y, this.z}; + } + + @Override + public Vector3i toInt() { + return new Vector3i(this.x, this.y, this.z); + } + + @Override + public Vector3l toLong() { + return new Vector3l(this.x, this.y, this.z); + } + + @Override + public Vector3f toFloat() { + return new Vector3f(this.x, this.y, this.z); + } + + @Override + public Vector3d toDouble() { + return new Vector3d((double) this.x, (double) this.y, (double) this.z); + } + + @Override + public boolean equals(final Object other) { + if (this == other) { + return true; + } else if (!(other instanceof final MutableVector3l that)) { + return false; + } else { + return that.x == this.x + && that.y == this.y + && that.z == this.z; + } + } + + @Override + public int hashCode() { + int result = Long.hashCode(this.x); + result = result * 31 + Long.hashCode(this.y); + result = result * 31 + Long.hashCode(this.z); + return result; + } + + @Override + public String toString() { + return "(" + this.x + ", " + this.y + ", " + this.z + ")"; + } + + public static MutableVector3l zero() { + return MutableVector3l.of(0, 0, 0); + } + + public static MutableVector3l unitX() { + return MutableVector3l.of(1, 0, 0); + } + + public static MutableVector3l unitY() { + return MutableVector3l.of(0, 1, 0); + } + + public static MutableVector3l unitZ() { + return MutableVector3l.of(0, 0, 1); + } + + public static MutableVector3l one() { + return MutableVector3l.of(1, 1, 1); + } + + public static MutableVector3l from(final double n) { + return MutableVector3l.from(GenericMath.floorl(n)); + } + + public static MutableVector3l from(final long n) { + return MutableVector3l.of(n, n, n); + } + + public static MutableVector3l from(final MutableVector3l v) { + return MutableVector3l.of(v.x, v.y, v.z); + } + + public static MutableVector3l from(final Vector3l v) { + return MutableVector3l.of(v.x(), v.y(), v.z()); + } + + public static MutableVector3l of(final double x, final double y, final double z) { + return new MutableVector3l(x, y, z); + } + + public static MutableVector3l of(final long x, final long y, final long z) { + return new MutableVector3l(x, y, z); + } +} diff --git a/api/src/main/java/net/hellheim/spongetools/math/mutable/vector/MutableVector4d.java b/api/src/main/java/net/hellheim/spongetools/math/mutable/vector/MutableVector4d.java new file mode 100644 index 0000000..e265741 --- /dev/null +++ b/api/src/main/java/net/hellheim/spongetools/math/mutable/vector/MutableVector4d.java @@ -0,0 +1,456 @@ +package net.hellheim.spongetools.math.mutable.vector; + +import org.spongepowered.math.GenericMath; +import org.spongepowered.math.vector.Vector4d; +import org.spongepowered.math.vector.Vector4f; +import org.spongepowered.math.vector.Vector4i; +import org.spongepowered.math.vector.Vector4l; +import org.spongepowered.math.vector.Vectord; + +public final class MutableVector4d implements Vectord { + + private double x; + private double y; + private double z; + private double w; + + public MutableVector4d(final double x, final double y, final double z, final double w) { + this.x = x; + this.y = y; + this.z = z; + this.w = w; + } + + public double x() { + return this.x; + } + + public double y() { + return this.y; + } + + public double z() { + return this.z; + } + + public double w() { + return this.w; + } + + public MutableVector4d copy() { + return MutableVector4d.from(this); + } + + public MutableVector4d x(final MutableVector4d v) { + return this.x(v.x); + } + + public MutableVector4d x(final Vector4d v) { + return this.x(v.x()); + } + + public MutableVector4d x(final double a) { + this.x = a; + return this; + } + + public MutableVector4d y(final MutableVector4d v) { + return this.y(v.y); + } + + public MutableVector4d y(final Vector4d v) { + return this.y(v.y()); + } + + public MutableVector4d y(final double a) { + this.y = a; + return this; + } + + public MutableVector4d z(final MutableVector4d v) { + return this.z(v.z); + } + + public MutableVector4d z(final Vector4d v) { + return this.z(v.z()); + } + + public MutableVector4d z(final double a) { + this.z = a; + return this; + } + + public MutableVector4d w(final MutableVector4d v) { + return this.w(v.z); + } + + public MutableVector4d w(final Vector4d v) { + return this.w(v.z()); + } + + public MutableVector4d w(final double a) { + this.w = a; + return this; + } + + public MutableVector4d set(final MutableVector4d v) { + return this.set(v.x, v.y, v.z, v.w); + } + + public MutableVector4d set(final Vector4d v) { + return this.set(v.x(), v.y(), v.z(), v.w()); + } + + public MutableVector4d set(final double x, final double y, final double z, final double w) { + this.x = x; + this.y = y; + this.z = z; + this.w = w; + return this; + } + + public MutableVector4d add(final MutableVector4d v) { + return this.add(v.x, v.y, v.z, v.w); + } + + public MutableVector4d add(final Vector4d v) { + return this.add(v.x(), v.y(), v.z(), v.w()); + } + + public MutableVector4d add(final double x, final double y, final double z, final double w) { + this.x += x; + this.y += y; + this.z += z; + this.w += w; + return this; + } + + public MutableVector4d sub(final MutableVector4d v) { + return this.sub(v.x, v.y, v.z, v.w); + } + + public MutableVector4d sub(final Vector4d v) { + return this.sub(v.x(), v.y(), v.z(), v.w()); + } + + public MutableVector4d sub(final double x, final double y, final double z, final double w) { + this.x -= x; + this.y -= y; + this.z -= z; + this.w -= w; + return this; + } + + @Override + public MutableVector4d mul(final double a) { + return this.mul(a, a, a, a); + } + + public MutableVector4d mul(final MutableVector4d v) { + return this.mul(v.x, v.y, v.z, v.w); + } + + public MutableVector4d mul(final Vector4d v) { + return this.mul(v.x(), v.y(), v.z(), v.w()); + } + + public MutableVector4d mul(final double x, final double y, final double z, final double w) { + this.x *= x; + this.y *= y; + this.z *= z; + this.w *= w; + return this; + } + + @Override + public MutableVector4d div(final double a) { + return this.div(a, a, a, a); + } + + public MutableVector4d div(final MutableVector4d v) { + return this.div(v.x, v.y, v.z, v.w); + } + + public MutableVector4d div(final Vector4d v) { + return this.div(v.x(), v.y(), v.z(), v.w()); + } + + public MutableVector4d div(final double x, final double y, final double z, final double w) { + this.x /= x; + this.y /= y; + this.z /= z; + this.w /= w; + return this; + } + + public double dot(final MutableVector4d v) { + return this.dot(v.x, v.y, v.z, v.w); + } + + public double dot(final Vector4d v) { + return this.dot(v.x(), v.y(), v.z(), v.w()); + } + + public double dot(final double x, final double y, final double z, final double w) { + return this.x * x + this.y * y + this.z * z + this.w * w; + } + + public MutableVector4d project(final MutableVector4d v) { + return this.project(v.x, v.y, v.z, v.w); + } + + public MutableVector4d project(final Vector4d v) { + return this.project(v.x(), v.y(), v.z(), v.w()); + } + + public MutableVector4d project(final double x, final double y, final double z, final double w) { + final double lengthSquared = x * x + y * y + z * z + w * w; + if (lengthSquared == 0) { + throw new ArithmeticException("Cannot project onto the zero vector"); + } + final double a = this.dot(x, y, z, w) / lengthSquared; + return this.set(a * x, a * y, a * z, a * w); + } + + @Override + public MutableVector4d pow(final double pow) { + return this.pow(pow, pow, pow, pow); + } + + public MutableVector4d pow(final MutableVector4d v) { + return this.pow(v.x, v.y, v.z, v.w); + } + + public MutableVector4d pow(final Vector4d v) { + return this.pow(v.x(), v.y(), v.z(), v.w()); + } + + public MutableVector4d pow(final double x, final double y, final double z, final double w) { + return this.set(Math.pow(this.x, x), Math.pow(this.y, y), Math.pow(this.z, z), Math.pow(this.w, w)); + } + + @Override + public MutableVector4d ceil() { + return this.set(Math.ceil(this.x), Math.ceil(this.y), Math.ceil(this.z), Math.ceil(this.w)); + } + + @Override + public MutableVector4d floor() { + return this.set(GenericMath.floor(this.x), GenericMath.floor(this.y), GenericMath.floor(this.z), GenericMath.floor(this.w)); + } + + @Override + public MutableVector4d round() { + return this.set(Math.round(this.x), Math.round(this.y), Math.round(this.z), Math.round(this.w)); + } + + @Override + public MutableVector4d abs() { + return this.set(Math.abs(this.x), Math.abs(this.y), Math.abs(this.z), Math.abs(this.w)); + } + + @Override + public MutableVector4d negate() { + return this.set(-this.x, -this.y, -this.z, -this.w); + } + + public MutableVector4d min(final MutableVector4d v) { + return this.min(v.x, v.y, v.z, v.w); + } + + public MutableVector4d min(final Vector4d v) { + return this.min(v.x(), v.y(), v.z(), v.w()); + } + + public MutableVector4d min(final double x, final double y, final double z, final double w) { + return this.set(Math.min(this.x, x), Math.min(this.y, y), Math.min(this.z, z), Math.min(this.w, w)); + } + + public MutableVector4d max(final MutableVector4d v) { + return this.max(v.x, v.y, v.z, v.w); + } + + public MutableVector4d max(final Vector4d v) { + return this.max(v.x(), v.y(), v.z(), v.w()); + } + + public MutableVector4d max(final double x, final double y, final double z, final double w) { + return this.set(Math.max(this.x, x), Math.max(this.y, y), Math.max(this.z, z), Math.max(this.w, w)); + } + + public double distanceSquared(final MutableVector4d v) { + return this.distanceSquared(v.x, v.y, v.z, v.w); + } + + public double distanceSquared(final Vector4d v) { + return this.distanceSquared(v.x(), v.y(), v.z(), v.w()); + } + + public double distanceSquared(final double x, final double y, final double z, final double w) { + final double dx = this.x - x; + final double dy = this.y - y; + final double dz = this.z - z; + final double dw = this.w - w; + return dx * dx + dy * dy + dz * dz + dw * dw; + } + + public double distance(final MutableVector4d v) { + return this.distance(v.x, v.y, v.z, v.w); + } + + public double distance(final Vector4d v) { + return this.distance(v.x(), v.y(), v.z(), v.w()); + } + + public double distance(final double x, final double y, final double z, final double w) { + return Math.sqrt(this.distanceSquared(x, y, z, w)); + } + + @Override + public double lengthSquared() { + return this.x * this.x + this.y * this.y + this.z * this.z + this.w * this.w; + } + + @Override + public double length() { + return Math.sqrt(this.lengthSquared()); + } + + @Override + public MutableVector4d normalize() { + final double length = this.length(); + if (Math.abs(length) < GenericMath.FLT_EPSILON) { + throw new ArithmeticException("Cannot normalize the zero vector"); + } + return this.set(this.x / length, this.y / length, this.z / length, this.w / length); + } + + @Override + public int minAxis() { + double value = this.x; + int axis = 0; + if (this.y < value) { + value = this.y; + axis = 1; + } + if (this.z < value) { + value = this.z; + axis = 2; + } + if (this.w < value) { + axis = 3; + } + return axis; + } + + @Override + public int maxAxis() { + double value = this.x; + int axis = 0; + if (this.y > value) { + value = this.y; + axis = 1; + } + if (this.z > value) { + value = this.z; + axis = 2; + } + if (this.w > value) { + axis = 3; + } + return axis; + } + + @Override + public double[] toArray() { + return new double[]{this.x, this.y, this.z, this.w}; + } + + @Override + public Vector4i toInt() { + return new Vector4i(this.x, this.y, this.z, this.w); + } + + @Override + public Vector4l toLong() { + return new Vector4l(this.x, this.y, this.z, this.w); + } + + @Override + public Vector4f toFloat() { + return new Vector4f(this.x, this.y, this.z, this.w); + } + + @Override + public Vector4d toDouble() { + return new Vector4d((double) this.x, (double) this.y, (double) this.z, (double) this.w); + } + + @Override + public boolean equals(final Object other) { + if (this == other) { + return true; + } else if (!(other instanceof final MutableVector4d that)) { + return false; + } else { + return Double.compare(that.x, this.x) == 0 + && Double.compare(that.y, this.y) == 0 + && Double.compare(that.z, this.z) == 0 + && Double.compare(that.w, this.w) == 0; + } + } + + @Override + public int hashCode() { + int result = Double.hashCode(this.x); + result = result * 31 + Double.hashCode(this.y); + result = result * 31 + Double.hashCode(this.z); + result = result * 31 + Double.hashCode(this.w); + return result; + } + + @Override + public String toString() { + return "(" + this.x + ", " + this.y + ", " + this.z + ", " + this.w + ")"; + } + + public static MutableVector4d zero() { + return MutableVector4d.of(0, 0, 0, 0); + } + + public static MutableVector4d unitX() { + return MutableVector4d.of(1, 0, 0, 0); + } + + public static MutableVector4d unitY() { + return MutableVector4d.of(0, 1, 0, 0); + } + + public static MutableVector4d unitZ() { + return MutableVector4d.of(0, 0, 1, 0); + } + + public static MutableVector4d unitW() { + return MutableVector4d.of(0, 0, 0, 1); + } + + public static MutableVector4d one() { + return MutableVector4d.of(1, 1, 1, 1); + } + + public static MutableVector4d from(final double n) { + return MutableVector4d.of(n, n, n, n); + } + + public static MutableVector4d from(final MutableVector4d v) { + return MutableVector4d.of(v.x, v.y, v.z, v.w); + } + + public static MutableVector4d from(final Vector4d v) { + return MutableVector4d.of(v.x(), v.y(), v.z(), v.w()); + } + + public static MutableVector4d of(final double x, final double y, final double z, final double w) { + return new MutableVector4d(x, y, z, w); + } +} diff --git a/api/src/main/java/net/hellheim/spongetools/math/mutable/vector/MutableVector4f.java b/api/src/main/java/net/hellheim/spongetools/math/mutable/vector/MutableVector4f.java new file mode 100644 index 0000000..799e088 --- /dev/null +++ b/api/src/main/java/net/hellheim/spongetools/math/mutable/vector/MutableVector4f.java @@ -0,0 +1,544 @@ +package net.hellheim.spongetools.math.mutable.vector; + +import org.spongepowered.math.GenericMath; +import org.spongepowered.math.vector.Vector4d; +import org.spongepowered.math.vector.Vector4f; +import org.spongepowered.math.vector.Vector4i; +import org.spongepowered.math.vector.Vector4l; +import org.spongepowered.math.vector.Vectorf; + +public final class MutableVector4f implements Vectorf { + + private float x; + private float y; + private float z; + private float w; + + public MutableVector4f(final double x, final double y, final double z, final double w) { + this((float) x, (float) y, (float) z, (float) w); + } + + public MutableVector4f(final float x, final float y, final float z, final float w) { + this.x = x; + this.y = y; + this.z = z; + this.w = w; + } + + public float x() { + return this.x; + } + + public float y() { + return this.y; + } + + public float z() { + return this.z; + } + + public float w() { + return this.w; + } + + public MutableVector4f copy() { + return MutableVector4f.from(this); + } + + public MutableVector4f x(final MutableVector4f v) { + return this.x(v.x); + } + + public MutableVector4f x(final Vector4f v) { + return this.x(v.x()); + } + + public MutableVector4f x(final double a) { + return this.x((float) a); + } + + public MutableVector4f x(final float a) { + this.x = a; + return this; + } + + public MutableVector4f y(final MutableVector4f v) { + return this.y(v.y); + } + + public MutableVector4f y(final Vector4f v) { + return this.y(v.y()); + } + + public MutableVector4f y(final double a) { + return this.y((float) a); + } + + public MutableVector4f y(final float a) { + this.y = a; + return this; + } + + public MutableVector4f z(final MutableVector4f v) { + return this.z(v.z); + } + + public MutableVector4f z(final Vector4f v) { + return this.z(v.z()); + } + + public MutableVector4f z(final double a) { + return this.z((float) a); + } + + public MutableVector4f z(final float a) { + this.z = a; + return this; + } + + public MutableVector4f w(final MutableVector4f v) { + return this.w(v.z); + } + + public MutableVector4f w(final Vector4f v) { + return this.w(v.z()); + } + + public MutableVector4f w(final double a) { + return this.w((float) a); + } + + public MutableVector4f w(final float a) { + this.w = a; + return this; + } + + public MutableVector4f set(final MutableVector4f v) { + return this.set(v.x, v.y, v.z, v.w); + } + + public MutableVector4f set(final Vector4f v) { + return this.set(v.x(), v.y(), v.z(), v.w()); + } + + public MutableVector4f set(final double x, final double y, final double z, final double w) { + return this.set((float) x, (float) y, (float) z, (float) w); + } + + public MutableVector4f set(final float x, final float y, final float z, final float w) { + this.x = x; + this.y = y; + this.z = z; + this.w = w; + return this; + } + + public MutableVector4f add(final MutableVector4f v) { + return this.add(v.x, v.y, v.z, v.w); + } + + public MutableVector4f add(final Vector4f v) { + return this.add(v.x(), v.y(), v.z(), v.w()); + } + + public MutableVector4f add(final double x, final double y, final double z, final double w) { + return this.add((float) x, (float) y, (float) z, (float) w); + } + + public MutableVector4f add(final float x, final float y, final float z, final float w) { + this.x += x; + this.y += y; + this.z += z; + this.w += w; + return this; + } + + public MutableVector4f sub(final MutableVector4f v) { + return this.sub(v.x, v.y, v.z, v.w); + } + + public MutableVector4f sub(final Vector4f v) { + return this.sub(v.x(), v.y(), v.z(), v.w()); + } + + public MutableVector4f sub(final double x, final double y, final double z, final double w) { + return this.sub((float) x, (float) y, (float) z, (float) w); + } + + public MutableVector4f sub(final float x, final float y, final float z, final float w) { + this.x -= x; + this.y -= y; + this.z -= z; + this.w -= w; + return this; + } + + public MutableVector4f mul(final double a) { + return this.mul((float) a); + } + + @Override + public MutableVector4f mul(final float a) { + return this.mul(a, a, a, a); + } + + public MutableVector4f mul(final MutableVector4f v) { + return this.mul(v.x, v.y, v.z, v.w); + } + + public MutableVector4f mul(final Vector4f v) { + return this.mul(v.x(), v.y(), v.z(), v.w()); + } + + public MutableVector4f mul(final double x, final double y, final double z, final double w) { + return this.mul((float) x, (float) y, (float) z, (float) w); + } + + public MutableVector4f mul(final float x, final float y, final float z, final float w) { + this.x *= x; + this.y *= y; + this.z *= z; + this.w *= w; + return this; + } + + public MutableVector4f div(final double a) { + return this.div((float) a); + } + + @Override + public MutableVector4f div(final float a) { + return this.div(a, a, a, a); + } + + public MutableVector4f div(final MutableVector4f v) { + return this.div(v.x, v.y, v.z, v.w); + } + + public MutableVector4f div(final Vector4f v) { + return this.div(v.x(), v.y(), v.z(), v.w()); + } + + public MutableVector4f div(final double x, final double y, final double z, final double w) { + return this.div((float) x, (float) y, (float) z, (float) w); + } + + public MutableVector4f div(final float x, final float y, final float z, final float w) { + this.x /= x; + this.y /= y; + this.z /= z; + this.w /= w; + return this; + } + + public float dot(final MutableVector4f v) { + return this.dot(v.x, v.y, v.z, v.w); + } + + public float dot(final Vector4f v) { + return this.dot(v.x(), v.y(), v.z(), v.w()); + } + + public float dot(final double x, final double y, final double z, final double w) { + return this.dot((float) x, (float) y, (float) z, (float) w); + } + + public float dot(final float x, final float y, final float z, final float w) { + return this.x * x + this.y * y + this.z * z + this.w * w; + } + + public MutableVector4f project(final MutableVector4f v) { + return this.project(v.x, v.y, v.z, v.w); + } + + public MutableVector4f project(final Vector4f v) { + return this.project(v.x(), v.y(), v.z(), v.w()); + } + + public MutableVector4f project(final double x, final double y, final double z, final double w) { + return this.project((float) x, (float) y, (float) z, (float) w); + } + + public MutableVector4f project(final float x, final float y, final float z, final float w) { + final float lengthSquared = x * x + y * y + z * z + w * w; + if (lengthSquared == 0) { + throw new ArithmeticException("Cannot project onto the zero vector"); + } + final float a = this.dot(x, y, z, w) / lengthSquared; + return this.set(a * x, a * y, a * z, a * w); + } + + public MutableVector4f pow(final double pow) { + return this.pow((float) pow); + } + + @Override + public MutableVector4f pow(final float pow) { + return this.pow(pow, pow, pow, pow); + } + + public MutableVector4f pow(final MutableVector4f v) { + return this.pow(v.x, v.y, v.z, v.w); + } + + public MutableVector4f pow(final Vector4f v) { + return this.pow(v.x(), v.y(), v.z(), v.w()); + } + + public MutableVector4f pow(final double x, final double y, final double z, final double w) { + return this.pow((float) x, (float) y, (float) z, (float) w); + } + + public MutableVector4f pow(final float x, final float y, final float z, final float w) { + return this.set(Math.pow(this.x, x), Math.pow(this.y, y), Math.pow(this.z, z), Math.pow(this.w, w)); + } + + @Override + public MutableVector4f ceil() { + return this.set(Math.ceil(this.x), Math.ceil(this.y), Math.ceil(this.z), Math.ceil(this.w)); + } + + @Override + public MutableVector4f floor() { + return this.set(GenericMath.floor(this.x), GenericMath.floor(this.y), GenericMath.floor(this.z), GenericMath.floor(this.w)); + } + + @Override + public MutableVector4f round() { + return this.set(Math.round(this.x), Math.round(this.y), Math.round(this.z), Math.round(this.w)); + } + + @Override + public MutableVector4f abs() { + return this.set(Math.abs(this.x), Math.abs(this.y), Math.abs(this.z), Math.abs(this.w)); + } + + @Override + public MutableVector4f negate() { + return this.set(-this.x, -this.y, -this.z, -this.w); + } + + public MutableVector4f min(final MutableVector4f v) { + return this.min(v.x, v.y, v.z, v.w); + } + + public MutableVector4f min(final Vector4f v) { + return this.min(v.x(), v.y(), v.z(), v.w()); + } + + public MutableVector4f min(final double x, final double y, final double z, final double w) { + return this.min((float) x, (float) y, (float) z, (float) w); + } + + public MutableVector4f min(final float x, final float y, final float z, final float w) { + return this.set(Math.min(this.x, x), Math.min(this.y, y), Math.min(this.z, z), Math.min(this.w, w)); + } + + public MutableVector4f max(final MutableVector4f v) { + return this.max(v.x, v.y, v.z, v.w); + } + + public MutableVector4f max(final Vector4f v) { + return this.max(v.x(), v.y(), v.z(), v.w()); + } + + public MutableVector4f max(final double x, final double y, final double z, final double w) { + return this.max((float) x, (float) y, (float) z, (float) w); + } + + public MutableVector4f max(final float x, final float y, final float z, final float w) { + return this.set(Math.max(this.x, x), Math.max(this.y, y), Math.max(this.z, z), Math.max(this.w, w)); + } + + public float distanceSquared(final MutableVector4f v) { + return this.distanceSquared(v.x, v.y, v.z, v.w); + } + + public float distanceSquared(final Vector4f v) { + return this.distanceSquared(v.x(), v.y(), v.z(), v.w()); + } + + public float distanceSquared(final double x, final double y, final double z, final double w) { + return this.distanceSquared((float) x, (float) y, (float) z, (float) w); + } + + public float distanceSquared(final float x, final float y, final float z, final float w) { + final float dx = this.x - x; + final float dy = this.y - y; + final float dz = this.z - z; + final float dw = this.w - w; + return dx * dx + dy * dy + dz * dz + dw * dw; + } + + public float distance(final MutableVector4f v) { + return this.distance(v.x, v.y, v.z, v.w); + } + + public float distance(final Vector4f v) { + return this.distance(v.x(), v.y(), v.z(), v.w()); + } + + public float distance(final double x, final double y, final double z, final double w) { + return this.distance((float) x, (float) y, (float) z, (float) w); + } + + public float distance(final float x, final float y, final float z, final float w) { + return (float) Math.sqrt(this.distanceSquared(x, y, z, w)); + } + + @Override + public float lengthSquared() { + return this.x * this.x + this.y * this.y + this.z * this.z + this.w * this.w; + } + + @Override + public float length() { + return (float) Math.sqrt(this.lengthSquared()); + } + + @Override + public MutableVector4f normalize() { + final float length = this.length(); + if (Math.abs(length) < GenericMath.FLT_EPSILON) { + throw new ArithmeticException("Cannot normalize the zero vector"); + } + return this.set(this.x / length, this.y / length, this.z / length, this.w / length); + } + + @Override + public int minAxis() { + float value = this.x; + int axis = 0; + if (this.y < value) { + value = this.y; + axis = 1; + } + if (this.z < value) { + value = this.z; + axis = 2; + } + if (this.w < value) { + axis = 3; + } + return axis; + } + + @Override + public int maxAxis() { + float value = this.x; + int axis = 0; + if (this.y > value) { + value = this.y; + axis = 1; + } + if (this.z > value) { + value = this.z; + axis = 2; + } + if (this.w > value) { + axis = 3; + } + return axis; + } + + @Override + public float[] toArray() { + return new float[]{this.x, this.y, this.z, this.w}; + } + + @Override + public Vector4i toInt() { + return new Vector4i(this.x, this.y, this.z, this.w); + } + + @Override + public Vector4l toLong() { + return new Vector4l(this.x, this.y, this.z, this.w); + } + + @Override + public Vector4f toFloat() { + return new Vector4f(this.x, this.y, this.z, this.w); + } + + @Override + public Vector4d toDouble() { + return new Vector4d((double) this.x, (double) this.y, (double) this.z, (double) this.w); + } + + @Override + public boolean equals(final Object other) { + if (this == other) { + return true; + } else if (!(other instanceof final MutableVector4f that)) { + return false; + } else { + return Float.compare(that.x, this.x) == 0 + && Float.compare(that.y, this.y) == 0 + && Float.compare(that.z, this.z) == 0 + && Float.compare(that.w, this.w) == 0; + } + } + + @Override + public int hashCode() { + int result = Float.hashCode(this.x); + result = result * 31 + Float.hashCode(this.y); + result = result * 31 + Float.hashCode(this.z); + result = result * 31 + Float.hashCode(this.w); + return result; + } + + @Override + public String toString() { + return "(" + this.x + ", " + this.y + ", " + this.z + ", " + this.w + ")"; + } + + public static MutableVector4f zero() { + return MutableVector4f.of(0, 0, 0, 0); + } + + public static MutableVector4f unitX() { + return MutableVector4f.of(1, 0, 0, 0); + } + + public static MutableVector4f unitY() { + return MutableVector4f.of(0, 1, 0, 0); + } + + public static MutableVector4f unitZ() { + return MutableVector4f.of(0, 0, 1, 0); + } + + public static MutableVector4f unitW() { + return MutableVector4f.of(0, 0, 0, 1); + } + + public static MutableVector4f one() { + return MutableVector4f.of(1, 1, 1, 1); + } + + public static MutableVector4f from(final double n) { + return MutableVector4f.from((float) n); + } + + public static MutableVector4f from(final float n) { + return MutableVector4f.of(n, n, n, n); + } + + public static MutableVector4f from(final MutableVector4f v) { + return MutableVector4f.of(v.x, v.y, v.z, v.w); + } + + public static MutableVector4f from(final Vector4f v) { + return MutableVector4f.of(v.x(), v.y(), v.z(), v.w()); + } + + public static MutableVector4f of(final double x, final double y, final double z, final double w) { + return new MutableVector4f(x, y, z, w); + } + + public static MutableVector4f of(final float x, final float y, final float z, final float w) { + return new MutableVector4f(x, y, z, w); + } +} diff --git a/api/src/main/java/net/hellheim/spongetools/math/mutable/vector/MutableVector4i.java b/api/src/main/java/net/hellheim/spongetools/math/mutable/vector/MutableVector4i.java new file mode 100644 index 0000000..9b98f7a --- /dev/null +++ b/api/src/main/java/net/hellheim/spongetools/math/mutable/vector/MutableVector4i.java @@ -0,0 +1,520 @@ +package net.hellheim.spongetools.math.mutable.vector; + +import org.spongepowered.math.GenericMath; +import org.spongepowered.math.vector.Vector4d; +import org.spongepowered.math.vector.Vector4f; +import org.spongepowered.math.vector.Vector4i; +import org.spongepowered.math.vector.Vector4l; +import org.spongepowered.math.vector.Vectori; + +public final class MutableVector4i implements Vectori { + + private int x; + private int y; + private int z; + private int w; + + public MutableVector4i(final double x, final double y, final double z, final double w) { + this(GenericMath.floor(x), GenericMath.floor(y), GenericMath.floor(z), GenericMath.floor(w)); + } + + public MutableVector4i(final int x, final int y, final int z, final int w) { + this.x = x; + this.y = y; + this.z = z; + this.w = w; + } + + public int x() { + return this.x; + } + + public int y() { + return this.y; + } + + public int z() { + return this.z; + } + + public int w() { + return this.w; + } + + public MutableVector4i copy() { + return MutableVector4i.from(this); + } + + public MutableVector4i x(final MutableVector4i v) { + return this.x(v.x); + } + + public MutableVector4i x(final Vector4i v) { + return this.x(v.x()); + } + + public MutableVector4i x(final double a) { + return this.x(GenericMath.floor(a)); + } + + public MutableVector4i x(final int a) { + this.x = a; + return this; + } + + public MutableVector4i y(final MutableVector4i v) { + return this.y(v.y); + } + + public MutableVector4i y(final Vector4i v) { + return this.y(v.y()); + } + + public MutableVector4i y(final double a) { + return this.y(GenericMath.floor(a)); + } + + public MutableVector4i y(final int a) { + this.y = a; + return this; + } + + public MutableVector4i z(final MutableVector4i v) { + return this.z(v.z); + } + + public MutableVector4i z(final Vector4i v) { + return this.z(v.z()); + } + + public MutableVector4i z(final double a) { + return this.z(GenericMath.floor(a)); + } + + public MutableVector4i z(final int a) { + this.z = a; + return this; + } + + public MutableVector4i w(final MutableVector4i v) { + return this.w(v.z); + } + + public MutableVector4i w(final Vector4i v) { + return this.w(v.z()); + } + + public MutableVector4i w(final double a) { + return this.w(GenericMath.floor(a)); + } + + public MutableVector4i w(final int a) { + this.w = a; + return this; + } + + public MutableVector4i set(final MutableVector4i v) { + return this.set(v.x, v.y, v.z, v.w); + } + + public MutableVector4i set(final Vector4i v) { + return this.set(v.x(), v.y(), v.z(), v.w()); + } + + public MutableVector4i set(final double x, final double y, final double z, final double w) { + return this.set(GenericMath.floor(x), GenericMath.floor(y), GenericMath.floor(z), GenericMath.floor(w)); + } + + public MutableVector4i set(final int x, final int y, final int z, final int w) { + this.x = x; + this.y = y; + this.z = z; + this.w = w; + return this; + } + + public MutableVector4i add(final MutableVector4i v) { + return this.add(v.x, v.y, v.z, v.w); + } + + public MutableVector4i add(final Vector4i v) { + return this.add(v.x(), v.y(), v.z(), v.w()); + } + + public MutableVector4i add(final double x, final double y, final double z, final double w) { + return this.add(GenericMath.floor(x), GenericMath.floor(y), GenericMath.floor(z), GenericMath.floor(w)); + } + + public MutableVector4i add(final int x, final int y, final int z, final int w) { + this.x += x; + this.y += y; + this.z += z; + this.w += w; + return this; + } + + public MutableVector4i sub(final MutableVector4i v) { + return this.sub(v.x, v.y, v.z, v.w); + } + + public MutableVector4i sub(final Vector4i v) { + return this.sub(v.x(), v.y(), v.z(), v.w()); + } + + public MutableVector4i sub(final double x, final double y, final double z, final double w) { + return this.sub(GenericMath.floor(x), GenericMath.floor(y), GenericMath.floor(z), GenericMath.floor(w)); + } + + public MutableVector4i sub(final int x, final int y, final int z, final int w) { + this.x -= x; + this.y -= y; + this.z -= z; + this.w -= w; + return this; + } + + public MutableVector4i mul(final double a) { + return this.mul(GenericMath.floor(a)); + } + + @Override + public MutableVector4i mul(final int a) { + return this.mul(a, a, a, a); + } + + public MutableVector4i mul(final MutableVector4i v) { + return this.mul(v.x, v.y, v.z, v.w); + } + + public MutableVector4i mul(final Vector4i v) { + return this.mul(v.x(), v.y(), v.z(), v.w()); + } + + public MutableVector4i mul(final double x, final double y, final double z, final double w) { + return this.mul(GenericMath.floor(x), GenericMath.floor(y), GenericMath.floor(z), GenericMath.floor(w)); + } + + public MutableVector4i mul(final int x, final int y, final int z, final int w) { + this.x *= x; + this.y *= y; + this.z *= z; + this.w *= w; + return this; + } + + public MutableVector4i div(final double a) { + return this.div(GenericMath.floor(a)); + } + + @Override + public MutableVector4i div(final int a) { + return this.div(a, a, a, a); + } + + public MutableVector4i div(final MutableVector4i v) { + return this.div(v.x, v.y, v.z, v.w); + } + + public MutableVector4i div(final Vector4i v) { + return this.div(v.x(), v.y(), v.z(), v.w()); + } + + public MutableVector4i div(final double x, final double y, final double z, final double w) { + return this.div(GenericMath.floor(x), GenericMath.floor(y), GenericMath.floor(z), GenericMath.floor(w)); + } + + public MutableVector4i div(final int x, final int y, final int z, final int w) { + this.x /= x; + this.y /= y; + this.z /= z; + this.w /= w; + return this; + } + + public int dot(final MutableVector4i v) { + return this.dot(v.x, v.y, v.z, v.w); + } + + public int dot(final Vector4i v) { + return this.dot(v.x(), v.y(), v.z(), v.w()); + } + + public int dot(final double x, final double y, final double z, final double w) { + return this.dot(GenericMath.floor(x), GenericMath.floor(y), GenericMath.floor(z), GenericMath.floor(w)); + } + + public int dot(final int x, final int y, final int z, final int w) { + return this.x * x + this.y * y + this.z * z + this.w * w; + } + + public MutableVector4i project(final MutableVector4i v) { + return this.project(v.x, v.y, v.z, v.w); + } + + public MutableVector4i project(final Vector4i v) { + return this.project(v.x(), v.y(), v.z(), v.w()); + } + + public MutableVector4i project(final double x, final double y, final double z, final double w) { + return this.project(GenericMath.floor(x), GenericMath.floor(y), GenericMath.floor(z), GenericMath.floor(w)); + } + + public MutableVector4i project(final int x, final int y, final int z, final int w) { + final int lengthSquared = x * x + y * y + z * z + w * w; + if (lengthSquared == 0) { + throw new ArithmeticException("Cannot project onto the zero vector"); + } + final float a = (float) this.dot(x, y, z, w) / lengthSquared; + return this.set(a * x, a * y, a * z, a * w); + } + + public MutableVector4i pow(final double pow) { + return this.pow(GenericMath.floor(pow)); + } + + @Override + public MutableVector4i pow(final int pow) { + return this.pow(pow, pow, pow, pow); + } + + public MutableVector4i pow(final MutableVector4i v) { + return this.pow(v.x, v.y, v.z, v.w); + } + + public MutableVector4i pow(final Vector4i v) { + return this.pow(v.x(), v.y(), v.z(), v.w()); + } + + public MutableVector4i pow(final double x, final double y, final double z, final double w) { + return this.pow(GenericMath.floor(x), GenericMath.floor(y), GenericMath.floor(z), GenericMath.floor(w)); + } + + public MutableVector4i pow(final int x, final int y, final int z, final int w) { + return this.set(Math.pow(this.x, x), Math.pow(this.y, y), Math.pow(this.z, z), Math.pow(this.w, w)); + } + + @Override + public MutableVector4i abs() { + return this.set(Math.abs(this.x), Math.abs(this.y), Math.abs(this.z), Math.abs(this.w)); + } + + @Override + public MutableVector4i negate() { + return this.set(-this.x, -this.y, -this.z, -this.w); + } + + public MutableVector4i min(final MutableVector4i v) { + return this.min(v.x, v.y, v.z, v.w); + } + + public MutableVector4i min(final Vector4i v) { + return this.min(v.x(), v.y(), v.z(), v.w()); + } + + public MutableVector4i min(final double x, final double y, final double z, final double w) { + return this.min(GenericMath.floor(x), GenericMath.floor(y), GenericMath.floor(z), GenericMath.floor(w)); + } + + public MutableVector4i min(final int x, final int y, final int z, final int w) { + return this.set(Math.min(this.x, x), Math.min(this.y, y), Math.min(this.z, z), Math.min(this.w, w)); + } + + public MutableVector4i max(final MutableVector4i v) { + return this.max(v.x, v.y, v.z, v.w); + } + + public MutableVector4i max(final Vector4i v) { + return this.max(v.x(), v.y(), v.z(), v.w()); + } + + public MutableVector4i max(final double x, final double y, final double z, final double w) { + return this.max(GenericMath.floor(x), GenericMath.floor(y), GenericMath.floor(z), GenericMath.floor(w)); + } + + public MutableVector4i max(final int x, final int y, final int z, final int w) { + return this.set(Math.max(this.x, x), Math.max(this.y, y), Math.max(this.z, z), Math.max(this.w, w)); + } + + public int distanceSquared(final MutableVector4i v) { + return this.distanceSquared(v.x, v.y, v.z, v.w); + } + + public int distanceSquared(final Vector4i v) { + return this.distanceSquared(v.x(), v.y(), v.z(), v.w()); + } + + public int distanceSquared(final double x, final double y, final double z, final double w) { + return this.distanceSquared(GenericMath.floor(x), GenericMath.floor(y), GenericMath.floor(z), GenericMath.floor(w)); + } + + public int distanceSquared(final int x, final int y, final int z, final int w) { + final int dx = this.x - x; + final int dy = this.y - y; + final int dz = this.z - z; + final int dw = this.w - w; + return dx * dx + dy * dy + dz * dz + dw * dw; + } + + public float distance(final MutableVector4i v) { + return this.distance(v.x, v.y, v.z, v.w); + } + + public float distance(final Vector4i v) { + return this.distance(v.x(), v.y(), v.z(), v.w()); + } + + public float distance(final double x, final double y, final double z, final double w) { + return this.distance(GenericMath.floor(x), GenericMath.floor(y), GenericMath.floor(z), GenericMath.floor(w)); + } + + public float distance(final int x, final int y, final int z, final int w) { + return (float) Math.sqrt(this.distanceSquared(x, y, z, w)); + } + + @Override + public int lengthSquared() { + return this.x * this.x + this.y * this.y + this.z * this.z + this.w * this.w; + } + + @Override + public float length() { + return (float) Math.sqrt(this.lengthSquared()); + } + + @Override + public int minAxis() { + int value = this.x; + int axis = 0; + if (this.y < value) { + value = this.y; + axis = 1; + } + if (this.z < value) { + value = this.z; + axis = 2; + } + if (this.w < value) { + axis = 3; + } + return axis; + } + + @Override + public int maxAxis() { + int value = this.x; + int axis = 0; + if (this.y > value) { + value = this.y; + axis = 1; + } + if (this.z > value) { + value = this.z; + axis = 2; + } + if (this.w > value) { + axis = 3; + } + return axis; + } + + @Override + public int[] toArray() { + return new int[]{this.x, this.y, this.z, this.w}; + } + + @Override + public Vector4i toInt() { + return new Vector4i(this.x, this.y, this.z, this.w); + } + + @Override + public Vector4l toLong() { + return new Vector4l(this.x, this.y, this.z, this.w); + } + + @Override + public Vector4f toFloat() { + return new Vector4f(this.x, this.y, this.z, this.w); + } + + @Override + public Vector4d toDouble() { + return new Vector4d((double) this.x, (double) this.y, (double) this.z, (double) this.w); + } + + @Override + public boolean equals(final Object other) { + if (this == other) { + return true; + } else if (!(other instanceof final MutableVector4i that)) { + return false; + } else { + return that.x == this.x + && that.y == this.y + && that.z == this.z + && that.w == this.w; + } + } + + @Override + public int hashCode() { + int result = Integer.hashCode(this.x); + result = result * 31 + Integer.hashCode(this.y); + result = result * 31 + Integer.hashCode(this.z); + result = result * 31 + Integer.hashCode(this.w); + return result; + } + + @Override + public String toString() { + return "(" + this.x + ", " + this.y + ", " + this.z + ", " + this.w + ")"; + } + + public static MutableVector4i zero() { + return MutableVector4i.of(0, 0, 0, 0); + } + + public static MutableVector4i unitX() { + return MutableVector4i.of(1, 0, 0, 0); + } + + public static MutableVector4i unitY() { + return MutableVector4i.of(0, 1, 0, 0); + } + + public static MutableVector4i unitZ() { + return MutableVector4i.of(0, 0, 1, 0); + } + + public static MutableVector4i unitW() { + return MutableVector4i.of(0, 0, 0, 1); + } + + public static MutableVector4i one() { + return MutableVector4i.of(1, 1, 1, 1); + } + + public static MutableVector4i from(final double n) { + return MutableVector4i.from(GenericMath.floor(n)); + } + + public static MutableVector4i from(final int n) { + return MutableVector4i.of(n, n, n, n); + } + + public static MutableVector4i from(final MutableVector4i v) { + return MutableVector4i.of(v.x, v.y, v.z, v.w); + } + + public static MutableVector4i from(final Vector4i v) { + return MutableVector4i.of(v.x(), v.y(), v.z(), v.w()); + } + + public static MutableVector4i of(final double x, final double y, final double z, final double w) { + return new MutableVector4i(x, y, z, w); + } + + public static MutableVector4i of(final int x, final int y, final int z, final int w) { + return new MutableVector4i(x, y, z, w); + } +} diff --git a/api/src/main/java/net/hellheim/spongetools/math/mutable/vector/MutableVector4l.java b/api/src/main/java/net/hellheim/spongetools/math/mutable/vector/MutableVector4l.java new file mode 100644 index 0000000..24f9b91 --- /dev/null +++ b/api/src/main/java/net/hellheim/spongetools/math/mutable/vector/MutableVector4l.java @@ -0,0 +1,520 @@ +package net.hellheim.spongetools.math.mutable.vector; + +import org.spongepowered.math.GenericMath; +import org.spongepowered.math.vector.Vector4d; +import org.spongepowered.math.vector.Vector4f; +import org.spongepowered.math.vector.Vector4i; +import org.spongepowered.math.vector.Vector4l; +import org.spongepowered.math.vector.Vectorl; + +public final class MutableVector4l implements Vectorl { + + private long x; + private long y; + private long z; + private long w; + + public MutableVector4l(final double x, final double y, final double z, final double w) { + this(GenericMath.floorl(x), GenericMath.floorl(y), GenericMath.floorl(z), GenericMath.floorl(w)); + } + + public MutableVector4l(final long x, final long y, final long z, final long w) { + this.x = x; + this.y = y; + this.z = z; + this.w = w; + } + + public long x() { + return this.x; + } + + public long y() { + return this.y; + } + + public long z() { + return this.z; + } + + public long w() { + return this.w; + } + + public MutableVector4l copy() { + return MutableVector4l.from(this); + } + + public MutableVector4l x(final MutableVector4l v) { + return this.x(v.x); + } + + public MutableVector4l x(final Vector4l v) { + return this.x(v.x()); + } + + public MutableVector4l x(final double a) { + return this.x(GenericMath.floorl(a)); + } + + public MutableVector4l x(final long a) { + this.x = a; + return this; + } + + public MutableVector4l y(final MutableVector4l v) { + return this.y(v.y); + } + + public MutableVector4l y(final Vector4l v) { + return this.y(v.y()); + } + + public MutableVector4l y(final double a) { + return this.y(GenericMath.floorl(a)); + } + + public MutableVector4l y(final long a) { + this.y = a; + return this; + } + + public MutableVector4l z(final MutableVector4l v) { + return this.z(v.z); + } + + public MutableVector4l z(final Vector4l v) { + return this.z(v.z()); + } + + public MutableVector4l z(final double a) { + return this.z(GenericMath.floorl(a)); + } + + public MutableVector4l z(final long a) { + this.z = a; + return this; + } + + public MutableVector4l w(final MutableVector4l v) { + return this.w(v.z); + } + + public MutableVector4l w(final Vector4l v) { + return this.w(v.z()); + } + + public MutableVector4l w(final double a) { + return this.w(GenericMath.floorl(a)); + } + + public MutableVector4l w(final long a) { + this.w = a; + return this; + } + + public MutableVector4l set(final MutableVector4l v) { + return this.set(v.x, v.y, v.z, v.w); + } + + public MutableVector4l set(final Vector4l v) { + return this.set(v.x(), v.y(), v.z(), v.w()); + } + + public MutableVector4l set(final double x, final double y, final double z, final double w) { + return this.set(GenericMath.floorl(x), GenericMath.floorl(y), GenericMath.floorl(z), GenericMath.floorl(w)); + } + + public MutableVector4l set(final long x, final long y, final long z, final long w) { + this.x = x; + this.y = y; + this.z = z; + this.w = w; + return this; + } + + public MutableVector4l add(final MutableVector4l v) { + return this.add(v.x, v.y, v.z, v.w); + } + + public MutableVector4l add(final Vector4l v) { + return this.add(v.x(), v.y(), v.z(), v.w()); + } + + public MutableVector4l add(final double x, final double y, final double z, final double w) { + return this.add(GenericMath.floorl(x), GenericMath.floorl(y), GenericMath.floorl(z), GenericMath.floorl(w)); + } + + public MutableVector4l add(final long x, final long y, final long z, final long w) { + this.x += x; + this.y += y; + this.z += z; + this.w += w; + return this; + } + + public MutableVector4l sub(final MutableVector4l v) { + return this.sub(v.x, v.y, v.z, v.w); + } + + public MutableVector4l sub(final Vector4l v) { + return this.sub(v.x(), v.y(), v.z(), v.w()); + } + + public MutableVector4l sub(final double x, final double y, final double z, final double w) { + return this.sub(GenericMath.floorl(x), GenericMath.floorl(y), GenericMath.floorl(z), GenericMath.floorl(w)); + } + + public MutableVector4l sub(final long x, final long y, final long z, final long w) { + this.x -= x; + this.y -= y; + this.z -= z; + this.w -= w; + return this; + } + + public MutableVector4l mul(final double a) { + return this.mul(GenericMath.floorl(a)); + } + + @Override + public MutableVector4l mul(final long a) { + return this.mul(a, a, a, a); + } + + public MutableVector4l mul(final MutableVector4l v) { + return this.mul(v.x, v.y, v.z, v.w); + } + + public MutableVector4l mul(final Vector4l v) { + return this.mul(v.x(), v.y(), v.z(), v.w()); + } + + public MutableVector4l mul(final double x, final double y, final double z, final double w) { + return this.mul(GenericMath.floorl(x), GenericMath.floorl(y), GenericMath.floorl(z), GenericMath.floorl(w)); + } + + public MutableVector4l mul(final long x, final long y, final long z, final long w) { + this.x *= x; + this.y *= y; + this.z *= z; + this.w *= w; + return this; + } + + public MutableVector4l div(final double a) { + return this.div(GenericMath.floorl(a)); + } + + @Override + public MutableVector4l div(final long a) { + return this.div(a, a, a, a); + } + + public MutableVector4l div(final MutableVector4l v) { + return this.div(v.x, v.y, v.z, v.w); + } + + public MutableVector4l div(final Vector4l v) { + return this.div(v.x(), v.y(), v.z(), v.w()); + } + + public MutableVector4l div(final double x, final double y, final double z, final double w) { + return this.div(GenericMath.floorl(x), GenericMath.floorl(y), GenericMath.floorl(z), GenericMath.floorl(w)); + } + + public MutableVector4l div(final long x, final long y, final long z, final long w) { + this.x /= x; + this.y /= y; + this.z /= z; + this.w /= w; + return this; + } + + public long dot(final MutableVector4l v) { + return this.dot(v.x, v.y, v.z, v.w); + } + + public long dot(final Vector4l v) { + return this.dot(v.x(), v.y(), v.z(), v.w()); + } + + public long dot(final double x, final double y, final double z, final double w) { + return this.dot(GenericMath.floorl(x), GenericMath.floorl(y), GenericMath.floorl(z), GenericMath.floorl(w)); + } + + public long dot(final long x, final long y, final long z, final long w) { + return this.x * x + this.y * y + this.z * z + this.w * w; + } + + public MutableVector4l project(final MutableVector4l v) { + return this.project(v.x, v.y, v.z, v.w); + } + + public MutableVector4l project(final Vector4l v) { + return this.project(v.x(), v.y(), v.z(), v.w()); + } + + public MutableVector4l project(final double x, final double y, final double z, final double w) { + return this.project(GenericMath.floorl(x), GenericMath.floorl(y), GenericMath.floorl(z), GenericMath.floorl(w)); + } + + public MutableVector4l project(final long x, final long y, final long z, final long w) { + final long lengthSquared = x * x + y * y + z * z + w * w; + if (lengthSquared == 0) { + throw new ArithmeticException("Cannot project onto the zero vector"); + } + final double a = (double) this.dot(x, y, z, w) / lengthSquared; + return this.set(a * x, a * y, a * z, a * w); + } + + public MutableVector4l pow(final double pow) { + return this.pow(GenericMath.floorl(pow)); + } + + @Override + public MutableVector4l pow(final long pow) { + return this.pow(pow, pow, pow, pow); + } + + public MutableVector4l pow(final MutableVector4l v) { + return this.pow(v.x, v.y, v.z, v.w); + } + + public MutableVector4l pow(final Vector4l v) { + return this.pow(v.x(), v.y(), v.z(), v.w()); + } + + public MutableVector4l pow(final double x, final double y, final double z, final double w) { + return this.pow(GenericMath.floorl(x), GenericMath.floorl(y), GenericMath.floorl(z), GenericMath.floorl(w)); + } + + public MutableVector4l pow(final long x, final long y, final long z, final long w) { + return this.set(Math.pow(this.x, x), Math.pow(this.y, y), Math.pow(this.z, z), Math.pow(this.w, w)); + } + + @Override + public MutableVector4l abs() { + return this.set(Math.abs(this.x), Math.abs(this.y), Math.abs(this.z), Math.abs(this.w)); + } + + @Override + public MutableVector4l negate() { + return this.set(-this.x, -this.y, -this.z, -this.w); + } + + public MutableVector4l min(final MutableVector4l v) { + return this.min(v.x, v.y, v.z, v.w); + } + + public MutableVector4l min(final Vector4l v) { + return this.min(v.x(), v.y(), v.z(), v.w()); + } + + public MutableVector4l min(final double x, final double y, final double z, final double w) { + return this.min(GenericMath.floorl(x), GenericMath.floorl(y), GenericMath.floorl(z), GenericMath.floorl(w)); + } + + public MutableVector4l min(final long x, final long y, final long z, final long w) { + return this.set(Math.min(this.x, x), Math.min(this.y, y), Math.min(this.z, z), Math.min(this.w, w)); + } + + public MutableVector4l max(final MutableVector4l v) { + return this.max(v.x, v.y, v.z, v.w); + } + + public MutableVector4l max(final Vector4l v) { + return this.max(v.x(), v.y(), v.z(), v.w()); + } + + public MutableVector4l max(final double x, final double y, final double z, final double w) { + return this.max(GenericMath.floorl(x), GenericMath.floorl(y), GenericMath.floorl(z), GenericMath.floorl(w)); + } + + public MutableVector4l max(final long x, final long y, final long z, final long w) { + return this.set(Math.max(this.x, x), Math.max(this.y, y), Math.max(this.z, z), Math.max(this.w, w)); + } + + public long distanceSquared(final MutableVector4l v) { + return this.distanceSquared(v.x, v.y, v.z, v.w); + } + + public long distanceSquared(final Vector4l v) { + return this.distanceSquared(v.x(), v.y(), v.z(), v.w()); + } + + public long distanceSquared(final double x, final double y, final double z, final double w) { + return this.distanceSquared(GenericMath.floorl(x), GenericMath.floorl(y), GenericMath.floorl(z), GenericMath.floorl(w)); + } + + public long distanceSquared(final long x, final long y, final long z, final long w) { + final long dx = this.x - x; + final long dy = this.y - y; + final long dz = this.z - z; + final long dw = this.w - w; + return dx * dx + dy * dy + dz * dz + dw * dw; + } + + public double distance(final MutableVector4l v) { + return this.distance(v.x, v.y, v.z, v.w); + } + + public double distance(final Vector4l v) { + return this.distance(v.x(), v.y(), v.z(), v.w()); + } + + public double distance(final double x, final double y, final double z, final double w) { + return this.distance(GenericMath.floorl(x), GenericMath.floorl(y), GenericMath.floorl(z), GenericMath.floorl(w)); + } + + public double distance(final long x, final long y, final long z, final long w) { + return Math.sqrt(this.distanceSquared(x, y, z, w)); + } + + @Override + public long lengthSquared() { + return this.x * this.x + this.y * this.y + this.z * this.z + this.w * this.w; + } + + @Override + public double length() { + return Math.sqrt(this.lengthSquared()); + } + + @Override + public int minAxis() { + long value = this.x; + int axis = 0; + if (this.y < value) { + value = this.y; + axis = 1; + } + if (this.z < value) { + value = this.z; + axis = 2; + } + if (this.w < value) { + axis = 3; + } + return axis; + } + + @Override + public int maxAxis() { + long value = this.x; + int axis = 0; + if (this.y > value) { + value = this.y; + axis = 1; + } + if (this.z > value) { + value = this.z; + axis = 2; + } + if (this.w > value) { + axis = 3; + } + return axis; + } + + @Override + public long[] toArray() { + return new long[]{this.x, this.y, this.z, this.w}; + } + + @Override + public Vector4i toInt() { + return new Vector4i(this.x, this.y, this.z, this.w); + } + + @Override + public Vector4l toLong() { + return new Vector4l(this.x, this.y, this.z, this.w); + } + + @Override + public Vector4f toFloat() { + return new Vector4f(this.x, this.y, this.z, this.w); + } + + @Override + public Vector4d toDouble() { + return new Vector4d((double) this.x, (double) this.y, (double) this.z, (double) this.w); + } + + @Override + public boolean equals(final Object other) { + if (this == other) { + return true; + } else if (!(other instanceof final MutableVector4l that)) { + return false; + } else { + return that.x == this.x + && that.y == this.y + && that.z == this.z + && that.w == this.w; + } + } + + @Override + public int hashCode() { + int result = Long.hashCode(this.x); + result = result * 31 + Long.hashCode(this.y); + result = result * 31 + Long.hashCode(this.z); + result = result * 31 + Long.hashCode(this.w); + return result; + } + + @Override + public String toString() { + return "(" + this.x + ", " + this.y + ", " + this.z + ", " + this.w + ")"; + } + + public static MutableVector4l zero() { + return MutableVector4l.of(0, 0, 0, 0); + } + + public static MutableVector4l unitX() { + return MutableVector4l.of(1, 0, 0, 0); + } + + public static MutableVector4l unitY() { + return MutableVector4l.of(0, 1, 0, 0); + } + + public static MutableVector4l unitZ() { + return MutableVector4l.of(0, 0, 1, 0); + } + + public static MutableVector4l unitW() { + return MutableVector4l.of(0, 0, 0, 1); + } + + public static MutableVector4l one() { + return MutableVector4l.of(1, 1, 1, 1); + } + + public static MutableVector4l from(final double n) { + return MutableVector4l.from(GenericMath.floorl(n)); + } + + public static MutableVector4l from(final long n) { + return MutableVector4l.of(n, n, n, n); + } + + public static MutableVector4l from(final MutableVector4l v) { + return MutableVector4l.of(v.x, v.y, v.z, v.w); + } + + public static MutableVector4l from(final Vector4l v) { + return MutableVector4l.of(v.x(), v.y(), v.z(), v.w()); + } + + public static MutableVector4l of(final double x, final double y, final double z, final double w) { + return new MutableVector4l(x, y, z, w); + } + + public static MutableVector4l of(final long x, final long y, final long z, final long w) { + return new MutableVector4l(x, y, z, w); + } +} diff --git a/api/src/main/java/net/hellheim/spongetools/math/optional/package-info.java b/api/src/main/java/net/hellheim/spongetools/math/optional/package-info.java new file mode 100644 index 0000000..9ca9cc0 --- /dev/null +++ b/api/src/main/java/net/hellheim/spongetools/math/optional/package-info.java @@ -0,0 +1,7 @@ +/** + * This package provides variants of objects from SpongePowered + * math with some of their components possibly omitted. + * + * @see org.spongepowered.math + */ +package net.hellheim.spongetools.math.optional; diff --git a/api/src/main/java/net/hellheim/spongetools/math/optional/vector/OptionalVector2d.java b/api/src/main/java/net/hellheim/spongetools/math/optional/vector/OptionalVector2d.java new file mode 100644 index 0000000..10cdd5a --- /dev/null +++ b/api/src/main/java/net/hellheim/spongetools/math/optional/vector/OptionalVector2d.java @@ -0,0 +1,219 @@ +package net.hellheim.spongetools.math.optional.vector; + +import java.util.Objects; +import java.util.Optional; +import java.util.OptionalDouble; + +import org.checkerframework.checker.nullness.qual.Nullable; +import org.spongepowered.math.vector.Vector2d; + +import net.hellheim.spongetools.math.mutable.vector.MutableVector2d; + +public final class OptionalVector2d { + + private final OptionalDouble x; + private final OptionalDouble y; + private final Transformer transformer; + + private OptionalVector2d(final OptionalDouble x, final OptionalDouble y) { + this.x = x; + this.y = y; + + if (this.x.isPresent()) { + final double x0 = this.x.getAsDouble(); + if (this.y.isPresent()) { + final double y0 = this.y.getAsDouble(); + this.transformer = (o, v) -> o.apply(x0, y0); + } else { + this.transformer = (o, v) -> o.apply(x0, v); + } + } else if (this.y.isPresent()) { + final double y0 = this.y.getAsDouble(); + this.transformer = (o, v) -> o.apply(v, y0); + } else { + this.transformer = (o, v) -> o; + } + } + + public OptionalDouble x() { + return this.x; + } + + public OptionalDouble y() { + return this.y; + } + + @SuppressWarnings("unchecked") + private V apply(final Operator operator, final double neutralValue) { + return (V) this.transformer.apply(operator, neutralValue); + } + + public MutableVector2d set(final MutableVector2d v) { + this.x.ifPresent(v::x); + this.y.ifPresent(v::y); + return v; + } + + public Vector2d set(final Vector2d v) { + final double x = this.x.orElse(v.x()); + final double y = this.y.orElse(v.y()); + return new Vector2d(x, y); + } + + public MutableVector2d add(final MutableVector2d v) { + return this.apply(v::add, 0); + } + + public Vector2d add(final Vector2d v) { + return this.apply(v::add, 0); + } + + public MutableVector2d sub(final MutableVector2d v) { + return this.apply(v::sub, 0); + } + + public Vector2d sub(final Vector2d v) { + return this.apply(v::sub, 0); + } + + public MutableVector2d mul(final MutableVector2d v) { + return this.apply(v::mul, 1); + } + + public Vector2d mul(final Vector2d v) { + return this.apply(v::mul, 1); + } + + public MutableVector2d div(final MutableVector2d v) { + return this.apply(v::div, 1); + } + + public Vector2d div(final Vector2d v) { + return this.apply(v::div, 1); + } + + public MutableVector2d pow(final MutableVector2d v) { + return this.apply(v::pow, 1); + } + + public Vector2d pow(final Vector2d v) { + final double x = Math.pow(v.x(), this.x.orElse(1)); + final double y = Math.pow(v.y(), this.y.orElse(1)); + return new Vector2d(x, y); + } + + public MutableVector2d min(final MutableVector2d v) { + return this.apply(v::min, Double.POSITIVE_INFINITY); + } + + public Vector2d min(final Vector2d v) { + return this.apply(v::min, Double.POSITIVE_INFINITY); + } + + public MutableVector2d max(final MutableVector2d v) { + return this.apply(v::max, Double.NEGATIVE_INFINITY); + } + + public Vector2d max(final Vector2d v) { + return this.apply(v::max, Double.NEGATIVE_INFINITY); + } + + @Override + public boolean equals(final Object other) { + if (this == other) { + return true; + } else if (!(other instanceof final OptionalVector2d that)) { + return false; + } else { + return this.x.equals(that.x) + && this.y.equals(that.y); + } + } + + @Override + public int hashCode() { + return Objects.hash(this.x, this.y); + } + + @Override + public String toString() { + return "(" + + "x=" + (this.x.isPresent() ? this.x.getAsDouble() : "?") + + "y=" + (this.y.isPresent() ? this.y.getAsDouble() : "?") + + ")"; + } + + public static OptionalVector2d x(final double x) { + return OptionalVector2d.x(convert(x)); + } + + public static OptionalVector2d x(final @Nullable Double x) { + return OptionalVector2d.x(convert(x)); + } + + public static OptionalVector2d x(final Optional x) { + return OptionalVector2d.x(convert(x)); + } + + public static OptionalVector2d x(final OptionalDouble x) { + return OptionalVector2d.of(x, empty()); + } + + public static OptionalVector2d y(final double y) { + return OptionalVector2d.y(convert(y)); + } + + public static OptionalVector2d y(final @Nullable Double y) { + return OptionalVector2d.y(convert(y)); + } + + public static OptionalVector2d y(final Optional y) { + return OptionalVector2d.y(convert(y)); + } + + public static OptionalVector2d y(final OptionalDouble y) { + return OptionalVector2d.of(empty(), y); + } + + public static OptionalVector2d of(final double x, final double y) { + return OptionalVector2d.of(convert(x), convert(y)); + } + + public static OptionalVector2d of(final @Nullable Double x, final @Nullable Double y) { + return OptionalVector2d.of(convert(x), convert(y)); + } + + public static OptionalVector2d of(final Optional x, final Optional y) { + return OptionalVector2d.of(convert(x), convert(y)); + } + + public static OptionalVector2d of(final OptionalDouble x, final OptionalDouble y) { + return new OptionalVector2d(x, y); + } + + private static OptionalDouble empty() { + return OptionalDouble.empty(); + } + + private static OptionalDouble convert(final double a) { + return OptionalDouble.of(a); + } + + private static OptionalDouble convert(final @Nullable Double a) { + return a == null ? empty() : OptionalDouble.of(a.doubleValue()); + } + + private static OptionalDouble convert(final Optional a) { + return a.map(OptionalDouble::of).orElseGet(OptionalVector2d::empty); + } + + private interface Transformer { + + Object apply(Operator operator, double neutralValue); + } + + private interface Operator { + + V apply(double x, double y); + } +} diff --git a/api/src/main/java/net/hellheim/spongetools/math/optional/vector/OptionalVector2i.java b/api/src/main/java/net/hellheim/spongetools/math/optional/vector/OptionalVector2i.java new file mode 100644 index 0000000..c16b0a6 --- /dev/null +++ b/api/src/main/java/net/hellheim/spongetools/math/optional/vector/OptionalVector2i.java @@ -0,0 +1,220 @@ +package net.hellheim.spongetools.math.optional.vector; + +import java.util.Objects; +import java.util.Optional; +import java.util.OptionalInt; + +import org.checkerframework.checker.nullness.qual.Nullable; +import org.spongepowered.math.GenericMath; +import org.spongepowered.math.vector.Vector2i; + +import net.hellheim.spongetools.math.mutable.vector.MutableVector2i; + +public final class OptionalVector2i { + + private final OptionalInt x; + private final OptionalInt y; + private final Transformer transformer; + + private OptionalVector2i(final OptionalInt x, final OptionalInt y) { + this.x = x; + this.y = y; + + if (this.x.isPresent()) { + final int x0 = this.x.getAsInt(); + if (this.y.isPresent()) { + final int y0 = this.y.getAsInt(); + this.transformer = (o, v) -> o.apply(x0, y0); + } else { + this.transformer = (o, v) -> o.apply(x0, v); + } + } else if (this.y.isPresent()) { + final int y0 = this.y.getAsInt(); + this.transformer = (o, v) -> o.apply(v, y0); + } else { + this.transformer = (o, v) -> o; + } + } + + public OptionalInt x() { + return this.x; + } + + public OptionalInt y() { + return this.y; + } + + @SuppressWarnings("unchecked") + private V apply(final Operator operator, final int neutralValue) { + return (V) this.transformer.apply(operator, neutralValue); + } + + public MutableVector2i set(final MutableVector2i v) { + this.x.ifPresent(v::x); + this.y.ifPresent(v::y); + return v; + } + + public Vector2i set(final Vector2i v) { + final int x = this.x.orElse(v.x()); + final int y = this.y.orElse(v.y()); + return new Vector2i(x, y); + } + + public MutableVector2i add(final MutableVector2i v) { + return this.apply(v::add, 0); + } + + public Vector2i add(final Vector2i v) { + return this.apply(v::add, 0); + } + + public MutableVector2i sub(final MutableVector2i v) { + return this.apply(v::sub, 0); + } + + public Vector2i sub(final Vector2i v) { + return this.apply(v::sub, 0); + } + + public MutableVector2i mul(final MutableVector2i v) { + return this.apply(v::mul, 1); + } + + public Vector2i mul(final Vector2i v) { + return this.apply(v::mul, 1); + } + + public MutableVector2i div(final MutableVector2i v) { + return this.apply(v::div, 1); + } + + public Vector2i div(final Vector2i v) { + return this.apply(v::div, 1); + } + + public MutableVector2i pow(final MutableVector2i v) { + return this.apply(v::pow, 1); + } + + public Vector2i pow(final Vector2i v) { + final int x = GenericMath.floor(Math.pow(v.x(), this.x.orElse(1))); + final int y = GenericMath.floor(Math.pow(v.y(), this.y.orElse(1))); + return new Vector2i(x, y); + } + + public MutableVector2i min(final MutableVector2i v) { + return this.apply(v::min, Integer.MAX_VALUE); + } + + public Vector2i min(final Vector2i v) { + return this.apply(v::min, Integer.MAX_VALUE); + } + + public MutableVector2i max(final MutableVector2i v) { + return this.apply(v::max, Integer.MIN_VALUE); + } + + public Vector2i max(final Vector2i v) { + return this.apply(v::max, Integer.MIN_VALUE); + } + + @Override + public boolean equals(final Object other) { + if (this == other) { + return true; + } else if (!(other instanceof final OptionalVector2i that)) { + return false; + } else { + return this.x.equals(that.x) + && this.y.equals(that.y); + } + } + + @Override + public int hashCode() { + return Objects.hash(this.x, this.y); + } + + @Override + public String toString() { + return "(" + + "x=" + (this.x.isPresent() ? this.x.getAsInt() : "?") + + "y=" + (this.y.isPresent() ? this.y.getAsInt() : "?") + + ")"; + } + + public static OptionalVector2i x(final int x) { + return OptionalVector2i.x(convert(x)); + } + + public static OptionalVector2i x(final @Nullable Integer x) { + return OptionalVector2i.x(convert(x)); + } + + public static OptionalVector2i x(final Optional x) { + return OptionalVector2i.x(convert(x)); + } + + public static OptionalVector2i x(final OptionalInt x) { + return OptionalVector2i.of(x, empty()); + } + + public static OptionalVector2i y(final int y) { + return OptionalVector2i.y(convert(y)); + } + + public static OptionalVector2i y(final @Nullable Integer y) { + return OptionalVector2i.y(convert(y)); + } + + public static OptionalVector2i y(final Optional y) { + return OptionalVector2i.y(convert(y)); + } + + public static OptionalVector2i y(final OptionalInt y) { + return OptionalVector2i.of(empty(), y); + } + + public static OptionalVector2i of(final int x, final int y) { + return OptionalVector2i.of(convert(x), convert(y)); + } + + public static OptionalVector2i of(final @Nullable Integer x, final @Nullable Integer y) { + return OptionalVector2i.of(convert(x), convert(y)); + } + + public static OptionalVector2i of(final Optional x, final Optional y) { + return OptionalVector2i.of(convert(x), convert(y)); + } + + public static OptionalVector2i of(final OptionalInt x, final OptionalInt y) { + return new OptionalVector2i(x, y); + } + + private static OptionalInt empty() { + return OptionalInt.empty(); + } + + private static OptionalInt convert(final int a) { + return OptionalInt.of(a); + } + + private static OptionalInt convert(final @Nullable Integer a) { + return a == null ? empty() : OptionalInt.of(a.intValue()); + } + + private static OptionalInt convert(final Optional a) { + return a.map(OptionalInt::of).orElseGet(OptionalVector2i::empty); + } + + private interface Transformer { + + Object apply(Operator operator, int neutralValue); + } + + private interface Operator { + + V apply(int x, int y); + } +} diff --git a/api/src/main/java/net/hellheim/spongetools/math/optional/vector/OptionalVector2l.java b/api/src/main/java/net/hellheim/spongetools/math/optional/vector/OptionalVector2l.java new file mode 100644 index 0000000..fb1f6e9 --- /dev/null +++ b/api/src/main/java/net/hellheim/spongetools/math/optional/vector/OptionalVector2l.java @@ -0,0 +1,220 @@ +package net.hellheim.spongetools.math.optional.vector; + +import java.util.Objects; +import java.util.Optional; +import java.util.OptionalLong; + +import org.checkerframework.checker.nullness.qual.Nullable; +import org.spongepowered.math.GenericMath; +import org.spongepowered.math.vector.Vector2l; + +import net.hellheim.spongetools.math.mutable.vector.MutableVector2l; + +public final class OptionalVector2l { + + private final OptionalLong x; + private final OptionalLong y; + private final Transformer transformer; + + private OptionalVector2l(final OptionalLong x, final OptionalLong y) { + this.x = x; + this.y = y; + + if (this.x.isPresent()) { + final long x0 = this.x.getAsLong(); + if (this.y.isPresent()) { + final long y0 = this.y.getAsLong(); + this.transformer = (o, v) -> o.apply(x0, y0); + } else { + this.transformer = (o, v) -> o.apply(x0, v); + } + } else if (this.y.isPresent()) { + final long y0 = this.y.getAsLong(); + this.transformer = (o, v) -> o.apply(v, y0); + } else { + this.transformer = (o, v) -> o; + } + } + + public OptionalLong x() { + return this.x; + } + + public OptionalLong y() { + return this.y; + } + + @SuppressWarnings("unchecked") + private V apply(final Operator operator, final long neutralValue) { + return (V) this.transformer.apply(operator, neutralValue); + } + + public MutableVector2l set(final MutableVector2l v) { + this.x.ifPresent(v::x); + this.y.ifPresent(v::y); + return v; + } + + public Vector2l set(final Vector2l v) { + final long x = this.x.orElse(v.x()); + final long y = this.y.orElse(v.y()); + return new Vector2l(x, y); + } + + public MutableVector2l add(final MutableVector2l v) { + return this.apply(v::add, 0); + } + + public Vector2l add(final Vector2l v) { + return this.apply(v::add, 0); + } + + public MutableVector2l sub(final MutableVector2l v) { + return this.apply(v::sub, 0); + } + + public Vector2l sub(final Vector2l v) { + return this.apply(v::sub, 0); + } + + public MutableVector2l mul(final MutableVector2l v) { + return this.apply(v::mul, 1); + } + + public Vector2l mul(final Vector2l v) { + return this.apply(v::mul, 1); + } + + public MutableVector2l div(final MutableVector2l v) { + return this.apply(v::div, 1); + } + + public Vector2l div(final Vector2l v) { + return this.apply(v::div, 1); + } + + public MutableVector2l pow(final MutableVector2l v) { + return this.apply(v::pow, 1); + } + + public Vector2l pow(final Vector2l v) { + final long x = GenericMath.floorl(Math.pow(v.x(), this.x.orElse(1))); + final long y = GenericMath.floorl(Math.pow(v.y(), this.y.orElse(1))); + return new Vector2l(x, y); + } + + public MutableVector2l min(final MutableVector2l v) { + return this.apply(v::min, Long.MAX_VALUE); + } + + public Vector2l min(final Vector2l v) { + return this.apply(v::min, Long.MAX_VALUE); + } + + public MutableVector2l max(final MutableVector2l v) { + return this.apply(v::max, Long.MIN_VALUE); + } + + public Vector2l max(final Vector2l v) { + return this.apply(v::max, Long.MIN_VALUE); + } + + @Override + public boolean equals(final Object other) { + if (this == other) { + return true; + } else if (!(other instanceof final OptionalVector2l that)) { + return false; + } else { + return this.x.equals(that.x) + && this.y.equals(that.y); + } + } + + @Override + public int hashCode() { + return Objects.hash(this.x, this.y); + } + + @Override + public String toString() { + return "(" + + "x=" + (this.x.isPresent() ? this.x.getAsLong() : "?") + + "y=" + (this.y.isPresent() ? this.y.getAsLong() : "?") + + ")"; + } + + public static OptionalVector2l x(final long x) { + return OptionalVector2l.x(convert(x)); + } + + public static OptionalVector2l x(final @Nullable Long x) { + return OptionalVector2l.x(convert(x)); + } + + public static OptionalVector2l x(final Optional x) { + return OptionalVector2l.x(convert(x)); + } + + public static OptionalVector2l x(final OptionalLong x) { + return OptionalVector2l.of(x, empty()); + } + + public static OptionalVector2l y(final long y) { + return OptionalVector2l.y(convert(y)); + } + + public static OptionalVector2l y(final @Nullable Long y) { + return OptionalVector2l.y(convert(y)); + } + + public static OptionalVector2l y(final Optional y) { + return OptionalVector2l.y(convert(y)); + } + + public static OptionalVector2l y(final OptionalLong y) { + return OptionalVector2l.of(empty(), y); + } + + public static OptionalVector2l of(final long x, final long y) { + return OptionalVector2l.of(convert(x), convert(y)); + } + + public static OptionalVector2l of(final @Nullable Long x, final @Nullable Long y) { + return OptionalVector2l.of(convert(x), convert(y)); + } + + public static OptionalVector2l of(final Optional x, final Optional y) { + return OptionalVector2l.of(convert(x), convert(y)); + } + + public static OptionalVector2l of(final OptionalLong x, final OptionalLong y) { + return new OptionalVector2l(x, y); + } + + private static OptionalLong empty() { + return OptionalLong.empty(); + } + + private static OptionalLong convert(final long a) { + return OptionalLong.of(a); + } + + private static OptionalLong convert(final @Nullable Long a) { + return a == null ? empty() : OptionalLong.of(a.longValue()); + } + + private static OptionalLong convert(final Optional a) { + return a.map(OptionalLong::of).orElseGet(OptionalVector2l::empty); + } + + private interface Transformer { + + Object apply(Operator operator, long neutralValue); + } + + private interface Operator { + + V apply(long x, long y); + } +} diff --git a/api/src/main/java/net/hellheim/spongetools/math/optional/vector/OptionalVector3d.java b/api/src/main/java/net/hellheim/spongetools/math/optional/vector/OptionalVector3d.java new file mode 100644 index 0000000..db1dd17 --- /dev/null +++ b/api/src/main/java/net/hellheim/spongetools/math/optional/vector/OptionalVector3d.java @@ -0,0 +1,310 @@ +package net.hellheim.spongetools.math.optional.vector; + +import java.util.Objects; +import java.util.Optional; +import java.util.OptionalDouble; + +import org.checkerframework.checker.nullness.qual.Nullable; +import org.spongepowered.math.vector.Vector3d; + +import net.hellheim.spongetools.math.mutable.vector.MutableVector3d; + +public final class OptionalVector3d { + + private final OptionalDouble x; + private final OptionalDouble y; + private final OptionalDouble z; + private final Transformer transformer; + + private OptionalVector3d(final OptionalDouble x, final OptionalDouble y, final OptionalDouble z) { + this.x = x; + this.y = y; + this.z = z; + + if (this.x.isPresent()) { + final double x0 = this.x.getAsDouble(); + if (this.y.isPresent()) { + final double y0 = this.y.getAsDouble(); + if (this.z.isPresent()) { + final double z0 = this.z.getAsDouble(); + this.transformer = (o, v) -> o.apply(x0, y0, z0); + } else { + this.transformer = (o, v) -> o.apply(x0, y0, v); + } + } else if (this.z.isPresent()) { + final double z0 = this.z.getAsDouble(); + this.transformer = (o, v) -> o.apply(x0, v, z0); + } else { + this.transformer = (o, v) -> o.apply(x0, v, v); + } + } else if (this.y.isPresent()) { + final double y0 = this.y.getAsDouble(); + if (this.z.isPresent()) { + final double z0 = this.z.getAsDouble(); + this.transformer = (o, v) -> o.apply(v, y0, z0); + } else { + this.transformer = (o, v) -> o.apply(v, y0, v); + } + } else if (this.z.isPresent()) { + final double z0 = this.z.getAsDouble(); + this.transformer = (o, v) -> o.apply(v, v, z0); + } else { + this.transformer = (o, v) -> o; + } + } + + public OptionalDouble x() { + return this.x; + } + + public OptionalDouble y() { + return this.y; + } + + public OptionalDouble z() { + return this.z; + } + + @SuppressWarnings("unchecked") + private V apply(final Operator operator, final double neutralValue) { + return (V) this.transformer.apply(operator, neutralValue); + } + + public MutableVector3d set(final MutableVector3d v) { + this.x.ifPresent(v::x); + this.y.ifPresent(v::y); + this.z.ifPresent(v::z); + return v; + } + + public Vector3d set(final Vector3d v) { + final double x = this.x.orElse(v.x()); + final double y = this.y.orElse(v.y()); + final double z = this.z.orElse(v.z()); + return new Vector3d(x, y, z); + } + + public MutableVector3d add(final MutableVector3d v) { + return this.apply(v::add, 0); + } + + public Vector3d add(final Vector3d v) { + return this.apply(v::add, 0); + } + + public MutableVector3d sub(final MutableVector3d v) { + return this.apply(v::sub, 0); + } + + public Vector3d sub(final Vector3d v) { + return this.apply(v::sub, 0); + } + + public MutableVector3d mul(final MutableVector3d v) { + return this.apply(v::mul, 1); + } + + public Vector3d mul(final Vector3d v) { + return this.apply(v::mul, 1); + } + + public MutableVector3d div(final MutableVector3d v) { + return this.apply(v::div, 1); + } + + public Vector3d div(final Vector3d v) { + return this.apply(v::div, 1); + } + + public MutableVector3d pow(final MutableVector3d v) { + return this.apply(v::pow, 1); + } + + public Vector3d pow(final Vector3d v) { + final double x = Math.pow(v.x(), this.x.orElse(1)); + final double y = Math.pow(v.y(), this.y.orElse(1)); + final double z = Math.pow(v.z(), this.z.orElse(1)); + return new Vector3d(x, y, z); + } + + public MutableVector3d min(final MutableVector3d v) { + return this.apply(v::min, Double.POSITIVE_INFINITY); + } + + public Vector3d min(final Vector3d v) { + return this.apply(v::min, Double.POSITIVE_INFINITY); + } + + public MutableVector3d max(final MutableVector3d v) { + return this.apply(v::max, Double.NEGATIVE_INFINITY); + } + + public Vector3d max(final Vector3d v) { + return this.apply(v::max, Double.NEGATIVE_INFINITY); + } + + @Override + public boolean equals(final Object other) { + if (this == other) { + return true; + } else if (!(other instanceof final OptionalVector3d that)) { + return false; + } else { + return this.x.equals(that.x) + && this.y.equals(that.y) + && this.z.equals(that.z); + } + } + + @Override + public int hashCode() { + return Objects.hash(this.x, this.y, this.z); + } + + @Override + public String toString() { + return "(" + + "x=" + (this.x.isPresent() ? this.x.getAsDouble() : "?") + + "y=" + (this.y.isPresent() ? this.y.getAsDouble() : "?") + + "z=" + (this.z.isPresent() ? this.z.getAsDouble() : "?") + + ")"; + } + + public static OptionalVector3d x(final double x) { + return OptionalVector3d.x(convert(x)); + } + + public static OptionalVector3d x(final @Nullable Double x) { + return OptionalVector3d.x(convert(x)); + } + + public static OptionalVector3d x(final Optional x) { + return OptionalVector3d.x(convert(x)); + } + + public static OptionalVector3d x(final OptionalDouble x) { + return OptionalVector3d.of(x, empty(), empty()); + } + + public static OptionalVector3d y(final double y) { + return OptionalVector3d.y(convert(y)); + } + + public static OptionalVector3d y(final @Nullable Double y) { + return OptionalVector3d.y(convert(y)); + } + + public static OptionalVector3d y(final Optional y) { + return OptionalVector3d.y(convert(y)); + } + + public static OptionalVector3d y(final OptionalDouble y) { + return OptionalVector3d.of(empty(), y, empty()); + } + + public static OptionalVector3d z(final double z) { + return OptionalVector3d.z(convert(z)); + } + + public static OptionalVector3d z(final @Nullable Double z) { + return OptionalVector3d.z(convert(z)); + } + + public static OptionalVector3d z(final Optional z) { + return OptionalVector3d.z(convert(z)); + } + + public static OptionalVector3d z(final OptionalDouble z) { + return OptionalVector3d.of(empty(), empty(), z); + } + + public static OptionalVector3d xy(final double x, final double y) { + return OptionalVector3d.xy(convert(x), convert(y)); + } + + public static OptionalVector3d xy(final @Nullable Double x, final @Nullable Double y) { + return OptionalVector3d.xy(convert(x), convert(y)); + } + + public static OptionalVector3d xy(final Optional x, final Optional y) { + return OptionalVector3d.xy(convert(x), convert(y)); + } + + public static OptionalVector3d xy(final OptionalDouble x, final OptionalDouble y) { + return OptionalVector3d.of(x, y, empty()); + } + + public static OptionalVector3d xz(final double x, final double z) { + return OptionalVector3d.xz(convert(x), convert(z)); + } + + public static OptionalVector3d xz(final @Nullable Double x, final @Nullable Double z) { + return OptionalVector3d.xz(convert(x), convert(z)); + } + + public static OptionalVector3d xz(final Optional x, final Optional z) { + return OptionalVector3d.xz(convert(x), convert(z)); + } + + public static OptionalVector3d xz(final OptionalDouble x, final OptionalDouble z) { + return OptionalVector3d.of(x, empty(), z); + } + + public static OptionalVector3d yz(final double y, final double z) { + return OptionalVector3d.yz(convert(y), convert(z)); + } + + public static OptionalVector3d yz(final @Nullable Double y, final @Nullable Double z) { + return OptionalVector3d.yz(convert(y), convert(z)); + } + + public static OptionalVector3d yz(final Optional y, final Optional z) { + return OptionalVector3d.yz(convert(y), convert(z)); + } + + public static OptionalVector3d yz(final OptionalDouble y, final OptionalDouble z) { + return OptionalVector3d.of(empty(), y, z); + } + + public static OptionalVector3d of(final double x, final double y, final double z) { + return OptionalVector3d.of(convert(x), convert(y), convert(z)); + } + + public static OptionalVector3d of(final @Nullable Double x, final @Nullable Double y, final @Nullable Double z) { + return OptionalVector3d.of(convert(x), convert(y), convert(z)); + } + + public static OptionalVector3d of(final Optional x, final Optional y, final Optional z) { + return OptionalVector3d.of(convert(x), convert(y), convert(z)); + } + + public static OptionalVector3d of(final OptionalDouble x, final OptionalDouble y, final OptionalDouble z) { + return new OptionalVector3d(x, y, z); + } + + private static OptionalDouble empty() { + return OptionalDouble.empty(); + } + + private static OptionalDouble convert(final double a) { + return OptionalDouble.of(a); + } + + private static OptionalDouble convert(final @Nullable Double a) { + return a == null ? empty() : OptionalDouble.of(a.doubleValue()); + } + + private static OptionalDouble convert(final Optional a) { + return a.map(OptionalDouble::of).orElseGet(OptionalVector3d::empty); + } + + private interface Transformer { + + Object apply(Operator operator, double neutralValue); + } + + private interface Operator { + + V apply(double x, double y, double z); + } +} diff --git a/api/src/main/java/net/hellheim/spongetools/math/optional/vector/OptionalVector3i.java b/api/src/main/java/net/hellheim/spongetools/math/optional/vector/OptionalVector3i.java new file mode 100644 index 0000000..ca4bdd8 --- /dev/null +++ b/api/src/main/java/net/hellheim/spongetools/math/optional/vector/OptionalVector3i.java @@ -0,0 +1,311 @@ +package net.hellheim.spongetools.math.optional.vector; + +import java.util.Objects; +import java.util.Optional; +import java.util.OptionalInt; + +import org.checkerframework.checker.nullness.qual.Nullable; +import org.spongepowered.math.GenericMath; +import org.spongepowered.math.vector.Vector3i; + +import net.hellheim.spongetools.math.mutable.vector.MutableVector3i; + +public final class OptionalVector3i { + + private final OptionalInt x; + private final OptionalInt y; + private final OptionalInt z; + private final Transformer transformer; + + private OptionalVector3i(final OptionalInt x, final OptionalInt y, final OptionalInt z) { + this.x = x; + this.y = y; + this.z = z; + + if (this.x.isPresent()) { + final int x0 = this.x.getAsInt(); + if (this.y.isPresent()) { + final int y0 = this.y.getAsInt(); + if (this.z.isPresent()) { + final int z0 = this.z.getAsInt(); + this.transformer = (o, v) -> o.apply(x0, y0, z0); + } else { + this.transformer = (o, v) -> o.apply(x0, y0, v); + } + } else if (this.z.isPresent()) { + final int z0 = this.z.getAsInt(); + this.transformer = (o, v) -> o.apply(x0, v, z0); + } else { + this.transformer = (o, v) -> o.apply(x0, v, v); + } + } else if (this.y.isPresent()) { + final int y0 = this.y.getAsInt(); + if (this.z.isPresent()) { + final int z0 = this.z.getAsInt(); + this.transformer = (o, v) -> o.apply(v, y0, z0); + } else { + this.transformer = (o, v) -> o.apply(v, y0, v); + } + } else if (this.z.isPresent()) { + final int z0 = this.z.getAsInt(); + this.transformer = (o, v) -> o.apply(v, v, z0); + } else { + this.transformer = (o, v) -> o; + } + } + + public OptionalInt x() { + return this.x; + } + + public OptionalInt y() { + return this.y; + } + + public OptionalInt z() { + return this.z; + } + + @SuppressWarnings("unchecked") + private V apply(final Operator operator, final int neutralValue) { + return (V) this.transformer.apply(operator, neutralValue); + } + + public MutableVector3i set(final MutableVector3i v) { + this.x.ifPresent(v::x); + this.y.ifPresent(v::y); + this.z.ifPresent(v::z); + return v; + } + + public Vector3i set(final Vector3i v) { + final int x = this.x.orElse(v.x()); + final int y = this.y.orElse(v.y()); + final int z = this.z.orElse(v.z()); + return new Vector3i(x, y, z); + } + + public MutableVector3i add(final MutableVector3i v) { + return this.apply(v::add, 0); + } + + public Vector3i add(final Vector3i v) { + return this.apply(v::add, 0); + } + + public MutableVector3i sub(final MutableVector3i v) { + return this.apply(v::sub, 0); + } + + public Vector3i sub(final Vector3i v) { + return this.apply(v::sub, 0); + } + + public MutableVector3i mul(final MutableVector3i v) { + return this.apply(v::mul, 1); + } + + public Vector3i mul(final Vector3i v) { + return this.apply(v::mul, 1); + } + + public MutableVector3i div(final MutableVector3i v) { + return this.apply(v::div, 1); + } + + public Vector3i div(final Vector3i v) { + return this.apply(v::div, 1); + } + + public MutableVector3i pow(final MutableVector3i v) { + return this.apply(v::pow, 1); + } + + public Vector3i pow(final Vector3i v) { + final int x = GenericMath.floor(Math.pow(v.x(), this.x.orElse(1))); + final int y = GenericMath.floor(Math.pow(v.y(), this.y.orElse(1))); + final int z = GenericMath.floor(Math.pow(v.z(), this.z.orElse(1))); + return new Vector3i(x, y, z); + } + + public MutableVector3i min(final MutableVector3i v) { + return this.apply(v::min, Integer.MAX_VALUE); + } + + public Vector3i min(final Vector3i v) { + return this.apply(v::min, Integer.MAX_VALUE); + } + + public MutableVector3i max(final MutableVector3i v) { + return this.apply(v::max, Integer.MIN_VALUE); + } + + public Vector3i max(final Vector3i v) { + return this.apply(v::max, Integer.MIN_VALUE); + } + + @Override + public boolean equals(final Object other) { + if (this == other) { + return true; + } else if (!(other instanceof final OptionalVector3i that)) { + return false; + } else { + return this.x.equals(that.x) + && this.y.equals(that.y) + && this.z.equals(that.z); + } + } + + @Override + public int hashCode() { + return Objects.hash(this.x, this.y, this.z); + } + + @Override + public String toString() { + return "(" + + "x=" + (this.x.isPresent() ? this.x.getAsInt() : "?") + + "y=" + (this.y.isPresent() ? this.y.getAsInt() : "?") + + "z=" + (this.z.isPresent() ? this.z.getAsInt() : "?") + + ")"; + } + + public static OptionalVector3i x(final int x) { + return OptionalVector3i.x(convert(x)); + } + + public static OptionalVector3i x(final @Nullable Integer x) { + return OptionalVector3i.x(convert(x)); + } + + public static OptionalVector3i x(final Optional x) { + return OptionalVector3i.x(convert(x)); + } + + public static OptionalVector3i x(final OptionalInt x) { + return OptionalVector3i.of(x, empty(), empty()); + } + + public static OptionalVector3i y(final int y) { + return OptionalVector3i.y(convert(y)); + } + + public static OptionalVector3i y(final @Nullable Integer y) { + return OptionalVector3i.y(convert(y)); + } + + public static OptionalVector3i y(final Optional y) { + return OptionalVector3i.y(convert(y)); + } + + public static OptionalVector3i y(final OptionalInt y) { + return OptionalVector3i.of(empty(), y, empty()); + } + + public static OptionalVector3i z(final int z) { + return OptionalVector3i.z(convert(z)); + } + + public static OptionalVector3i z(final @Nullable Integer z) { + return OptionalVector3i.z(convert(z)); + } + + public static OptionalVector3i z(final Optional z) { + return OptionalVector3i.z(convert(z)); + } + + public static OptionalVector3i z(final OptionalInt z) { + return OptionalVector3i.of(empty(), empty(), z); + } + + public static OptionalVector3i xy(final int x, final int y) { + return OptionalVector3i.xy(convert(x), convert(y)); + } + + public static OptionalVector3i xy(final @Nullable Integer x, final @Nullable Integer y) { + return OptionalVector3i.xy(convert(x), convert(y)); + } + + public static OptionalVector3i xy(final Optional x, final Optional y) { + return OptionalVector3i.xy(convert(x), convert(y)); + } + + public static OptionalVector3i xy(final OptionalInt x, final OptionalInt y) { + return OptionalVector3i.of(x, y, empty()); + } + + public static OptionalVector3i xz(final int x, final int z) { + return OptionalVector3i.xz(convert(x), convert(z)); + } + + public static OptionalVector3i xz(final @Nullable Integer x, final @Nullable Integer z) { + return OptionalVector3i.xz(convert(x), convert(z)); + } + + public static OptionalVector3i xz(final Optional x, final Optional z) { + return OptionalVector3i.xz(convert(x), convert(z)); + } + + public static OptionalVector3i xz(final OptionalInt x, final OptionalInt z) { + return OptionalVector3i.of(x, empty(), z); + } + + public static OptionalVector3i yz(final int y, final int z) { + return OptionalVector3i.yz(convert(y), convert(z)); + } + + public static OptionalVector3i yz(final @Nullable Integer y, final @Nullable Integer z) { + return OptionalVector3i.yz(convert(y), convert(z)); + } + + public static OptionalVector3i yz(final Optional y, final Optional z) { + return OptionalVector3i.yz(convert(y), convert(z)); + } + + public static OptionalVector3i yz(final OptionalInt y, final OptionalInt z) { + return OptionalVector3i.of(empty(), y, z); + } + + public static OptionalVector3i of(final int x, final int y, final int z) { + return OptionalVector3i.of(convert(x), convert(y), convert(z)); + } + + public static OptionalVector3i of(final @Nullable Integer x, final @Nullable Integer y, final @Nullable Integer z) { + return OptionalVector3i.of(convert(x), convert(y), convert(z)); + } + + public static OptionalVector3i of(final Optional x, final Optional y, final Optional z) { + return OptionalVector3i.of(convert(x), convert(y), convert(z)); + } + + public static OptionalVector3i of(final OptionalInt x, final OptionalInt y, final OptionalInt z) { + return new OptionalVector3i(x, y, z); + } + + private static OptionalInt empty() { + return OptionalInt.empty(); + } + + private static OptionalInt convert(final int a) { + return OptionalInt.of(a); + } + + private static OptionalInt convert(final @Nullable Integer a) { + return a == null ? empty() : OptionalInt.of(a.intValue()); + } + + private static OptionalInt convert(final Optional a) { + return a.map(OptionalInt::of).orElseGet(OptionalVector3i::empty); + } + + private interface Transformer { + + Object apply(Operator operator, int neutralValue); + } + + private interface Operator { + + V apply(int x, int y, int z); + } +} diff --git a/api/src/main/java/net/hellheim/spongetools/math/optional/vector/OptionalVector3l.java b/api/src/main/java/net/hellheim/spongetools/math/optional/vector/OptionalVector3l.java new file mode 100644 index 0000000..64a7837 --- /dev/null +++ b/api/src/main/java/net/hellheim/spongetools/math/optional/vector/OptionalVector3l.java @@ -0,0 +1,311 @@ +package net.hellheim.spongetools.math.optional.vector; + +import java.util.Objects; +import java.util.Optional; +import java.util.OptionalLong; + +import org.checkerframework.checker.nullness.qual.Nullable; +import org.spongepowered.math.GenericMath; +import org.spongepowered.math.vector.Vector3l; + +import net.hellheim.spongetools.math.mutable.vector.MutableVector3l; + +public final class OptionalVector3l { + + private final OptionalLong x; + private final OptionalLong y; + private final OptionalLong z; + private final Transformer transformer; + + private OptionalVector3l(final OptionalLong x, final OptionalLong y, final OptionalLong z) { + this.x = x; + this.y = y; + this.z = z; + + if (this.x.isPresent()) { + final long x0 = this.x.getAsLong(); + if (this.y.isPresent()) { + final long y0 = this.y.getAsLong(); + if (this.z.isPresent()) { + final long z0 = this.z.getAsLong(); + this.transformer = (o, v) -> o.apply(x0, y0, z0); + } else { + this.transformer = (o, v) -> o.apply(x0, y0, v); + } + } else if (this.z.isPresent()) { + final long z0 = this.z.getAsLong(); + this.transformer = (o, v) -> o.apply(x0, v, z0); + } else { + this.transformer = (o, v) -> o.apply(x0, v, v); + } + } else if (this.y.isPresent()) { + final long y0 = this.y.getAsLong(); + if (this.z.isPresent()) { + final long z0 = this.z.getAsLong(); + this.transformer = (o, v) -> o.apply(v, y0, z0); + } else { + this.transformer = (o, v) -> o.apply(v, y0, v); + } + } else if (this.z.isPresent()) { + final long z0 = this.z.getAsLong(); + this.transformer = (o, v) -> o.apply(v, v, z0); + } else { + this.transformer = (o, v) -> o; + } + } + + public OptionalLong x() { + return this.x; + } + + public OptionalLong y() { + return this.y; + } + + public OptionalLong z() { + return this.z; + } + + @SuppressWarnings("unchecked") + private V apply(final Operator operator, final long neutralValue) { + return (V) this.transformer.apply(operator, neutralValue); + } + + public MutableVector3l set(final MutableVector3l v) { + this.x.ifPresent(v::x); + this.y.ifPresent(v::y); + this.z.ifPresent(v::z); + return v; + } + + public Vector3l set(final Vector3l v) { + final long x = this.x.orElse(v.x()); + final long y = this.y.orElse(v.y()); + final long z = this.z.orElse(v.z()); + return new Vector3l(x, y, z); + } + + public MutableVector3l add(final MutableVector3l v) { + return this.apply(v::add, 0); + } + + public Vector3l add(final Vector3l v) { + return this.apply(v::add, 0); + } + + public MutableVector3l sub(final MutableVector3l v) { + return this.apply(v::sub, 0); + } + + public Vector3l sub(final Vector3l v) { + return this.apply(v::sub, 0); + } + + public MutableVector3l mul(final MutableVector3l v) { + return this.apply(v::mul, 1); + } + + public Vector3l mul(final Vector3l v) { + return this.apply(v::mul, 1); + } + + public MutableVector3l div(final MutableVector3l v) { + return this.apply(v::div, 1); + } + + public Vector3l div(final Vector3l v) { + return this.apply(v::div, 1); + } + + public MutableVector3l pow(final MutableVector3l v) { + return this.apply(v::pow, 1); + } + + public Vector3l pow(final Vector3l v) { + final long x = GenericMath.floorl(Math.pow(v.x(), this.x.orElse(1))); + final long y = GenericMath.floorl(Math.pow(v.y(), this.y.orElse(1))); + final long z = GenericMath.floorl(Math.pow(v.z(), this.z.orElse(1))); + return new Vector3l(x, y, z); + } + + public MutableVector3l min(final MutableVector3l v) { + return this.apply(v::min, Long.MAX_VALUE); + } + + public Vector3l min(final Vector3l v) { + return this.apply(v::min, Long.MAX_VALUE); + } + + public MutableVector3l max(final MutableVector3l v) { + return this.apply(v::max, Long.MIN_VALUE); + } + + public Vector3l max(final Vector3l v) { + return this.apply(v::max, Long.MIN_VALUE); + } + + @Override + public boolean equals(final Object other) { + if (this == other) { + return true; + } else if (!(other instanceof final OptionalVector3l that)) { + return false; + } else { + return this.x.equals(that.x) + && this.y.equals(that.y) + && this.z.equals(that.z); + } + } + + @Override + public int hashCode() { + return Objects.hash(this.x, this.y, this.z); + } + + @Override + public String toString() { + return "(" + + "x=" + (this.x.isPresent() ? this.x.getAsLong() : "?") + + "y=" + (this.y.isPresent() ? this.y.getAsLong() : "?") + + "z=" + (this.z.isPresent() ? this.z.getAsLong() : "?") + + ")"; + } + + public static OptionalVector3l x(final long x) { + return OptionalVector3l.x(convert(x)); + } + + public static OptionalVector3l x(final @Nullable Long x) { + return OptionalVector3l.x(convert(x)); + } + + public static OptionalVector3l x(final Optional x) { + return OptionalVector3l.x(convert(x)); + } + + public static OptionalVector3l x(final OptionalLong x) { + return OptionalVector3l.of(x, empty(), empty()); + } + + public static OptionalVector3l y(final long y) { + return OptionalVector3l.y(convert(y)); + } + + public static OptionalVector3l y(final @Nullable Long y) { + return OptionalVector3l.y(convert(y)); + } + + public static OptionalVector3l y(final Optional y) { + return OptionalVector3l.y(convert(y)); + } + + public static OptionalVector3l y(final OptionalLong y) { + return OptionalVector3l.of(empty(), y, empty()); + } + + public static OptionalVector3l z(final long z) { + return OptionalVector3l.z(convert(z)); + } + + public static OptionalVector3l z(final @Nullable Long z) { + return OptionalVector3l.z(convert(z)); + } + + public static OptionalVector3l z(final Optional z) { + return OptionalVector3l.z(convert(z)); + } + + public static OptionalVector3l z(final OptionalLong z) { + return OptionalVector3l.of(empty(), empty(), z); + } + + public static OptionalVector3l xy(final long x, final long y) { + return OptionalVector3l.xy(convert(x), convert(y)); + } + + public static OptionalVector3l xy(final @Nullable Long x, final @Nullable Long y) { + return OptionalVector3l.xy(convert(x), convert(y)); + } + + public static OptionalVector3l xy(final Optional x, final Optional y) { + return OptionalVector3l.xy(convert(x), convert(y)); + } + + public static OptionalVector3l xy(final OptionalLong x, final OptionalLong y) { + return OptionalVector3l.of(x, y, empty()); + } + + public static OptionalVector3l xz(final long x, final long z) { + return OptionalVector3l.xz(convert(x), convert(z)); + } + + public static OptionalVector3l xz(final @Nullable Long x, final @Nullable Long z) { + return OptionalVector3l.xz(convert(x), convert(z)); + } + + public static OptionalVector3l xz(final Optional x, final Optional z) { + return OptionalVector3l.xz(convert(x), convert(z)); + } + + public static OptionalVector3l xz(final OptionalLong x, final OptionalLong z) { + return OptionalVector3l.of(x, empty(), z); + } + + public static OptionalVector3l yz(final long y, final long z) { + return OptionalVector3l.yz(convert(y), convert(z)); + } + + public static OptionalVector3l yz(final @Nullable Long y, final @Nullable Long z) { + return OptionalVector3l.yz(convert(y), convert(z)); + } + + public static OptionalVector3l yz(final Optional y, final Optional z) { + return OptionalVector3l.yz(convert(y), convert(z)); + } + + public static OptionalVector3l yz(final OptionalLong y, final OptionalLong z) { + return OptionalVector3l.of(empty(), y, z); + } + + public static OptionalVector3l of(final long x, final long y, final long z) { + return OptionalVector3l.of(convert(x), convert(y), convert(z)); + } + + public static OptionalVector3l of(final @Nullable Long x, final @Nullable Long y, final @Nullable Long z) { + return OptionalVector3l.of(convert(x), convert(y), convert(z)); + } + + public static OptionalVector3l of(final Optional x, final Optional y, final Optional z) { + return OptionalVector3l.of(convert(x), convert(y), convert(z)); + } + + public static OptionalVector3l of(final OptionalLong x, final OptionalLong y, final OptionalLong z) { + return new OptionalVector3l(x, y, z); + } + + private static OptionalLong empty() { + return OptionalLong.empty(); + } + + private static OptionalLong convert(final long a) { + return OptionalLong.of(a); + } + + private static OptionalLong convert(final @Nullable Long a) { + return a == null ? empty() : OptionalLong.of(a.longValue()); + } + + private static OptionalLong convert(final Optional a) { + return a.map(OptionalLong::of).orElseGet(OptionalVector3l::empty); + } + + private interface Transformer { + + Object apply(Operator operator, long neutralValue); + } + + private interface Operator { + + V apply(long x, long y, long z); + } +} diff --git a/api/src/main/java/net/hellheim/spongetools/math/optional/vector/OptionalVector4d.java b/api/src/main/java/net/hellheim/spongetools/math/optional/vector/OptionalVector4d.java new file mode 100644 index 0000000..285d30f --- /dev/null +++ b/api/src/main/java/net/hellheim/spongetools/math/optional/vector/OptionalVector4d.java @@ -0,0 +1,481 @@ +package net.hellheim.spongetools.math.optional.vector; + +import java.util.Objects; +import java.util.Optional; +import java.util.OptionalDouble; + +import org.checkerframework.checker.nullness.qual.Nullable; +import org.spongepowered.math.vector.Vector4d; + +import net.hellheim.spongetools.math.mutable.vector.MutableVector4d; + +public final class OptionalVector4d { + + private final OptionalDouble x; + private final OptionalDouble y; + private final OptionalDouble z; + private final OptionalDouble w; + private final Transformer transformer; + + private OptionalVector4d(final OptionalDouble x, final OptionalDouble y, final OptionalDouble z, final OptionalDouble w) { + this.x = x; + this.y = y; + this.z = z; + this.w = w; + + if (this.x.isPresent()) { + final double x0 = this.x.getAsDouble(); + if (this.y.isPresent()) { + final double y0 = this.y.getAsDouble(); + if (this.z.isPresent()) { + final double z0 = this.z.getAsDouble(); + if (this.w.isPresent()) { + final double w0 = this.w.getAsDouble(); + this.transformer = (o, v) -> o.apply(x0, y0, z0, w0); + } else { + this.transformer = (o, v) -> o.apply(x0, y0, z0, v); + } + } else if (this.w.isPresent()) { + final double w0 = this.w.getAsDouble(); + this.transformer = (o, v) -> o.apply(x0, y0, v, w0); + } else { + this.transformer = (o, v) -> o.apply(x0, y0, v, v); + } + } else if (this.z.isPresent()) { + final double z0 = this.z.getAsDouble(); + if (this.w.isPresent()) { + final double w0 = this.w.getAsDouble(); + this.transformer = (o, v) -> o.apply(x0, v, z0, w0); + } else { + this.transformer = (o, v) -> o.apply(x0, v, z0, v); + } + } else if (this.w.isPresent()) { + final double w0 = this.w.getAsDouble(); + this.transformer = (o, v) -> o.apply(x0, v, v, w0); + } else { + this.transformer = (o, v) -> o.apply(x0, v, v, v); + } + } else if (this.y.isPresent()) { + final double y0 = this.y.getAsDouble(); + if (this.z.isPresent()) { + final double z0 = this.z.getAsDouble(); + if (this.w.isPresent()) { + final double w0 = this.w.getAsDouble(); + this.transformer = (o, v) -> o.apply(v, y0, z0, w0); + } else { + this.transformer = (o, v) -> o.apply(v, y0, z0, v); + } + } else if (this.w.isPresent()) { + final double w0 = this.w.getAsDouble(); + this.transformer = (o, v) -> o.apply(v, y0, v, w0); + } else { + this.transformer = (o, v) -> o.apply(v, y0, v, v); + } + } else if (this.z.isPresent()) { + final double z0 = this.z.getAsDouble(); + if (this.w.isPresent()) { + final double w0 = this.w.getAsDouble(); + this.transformer = (o, v) -> o.apply(v, v, z0, w0); + } else { + this.transformer = (o, v) -> o.apply(v, v, z0, v); + } + } else if (this.w.isPresent()) { + final double w0 = this.w.getAsDouble(); + this.transformer = (o, v) -> o.apply(v, v, v, w0); + } else { + this.transformer = (o, v) -> o; + } + } + + public OptionalDouble x() { + return this.x; + } + + public OptionalDouble y() { + return this.y; + } + + public OptionalDouble z() { + return this.z; + } + + public OptionalDouble w() { + return this.w; + } + + @SuppressWarnings("unchecked") + private V apply(final Operator operator, final double neutralValue) { + return (V) this.transformer.apply(operator, neutralValue); + } + + public MutableVector4d set(final MutableVector4d v) { + this.x.ifPresent(v::x); + this.y.ifPresent(v::y); + this.z.ifPresent(v::z); + this.w.ifPresent(v::w); + return v; + } + + public Vector4d set(final Vector4d v) { + final double x = this.x.orElse(v.x()); + final double y = this.y.orElse(v.y()); + final double z = this.z.orElse(v.z()); + final double w = this.w.orElse(v.w()); + return new Vector4d(x, y, z, w); + } + + public MutableVector4d add(final MutableVector4d v) { + return this.apply(v::add, 0); + } + + public Vector4d add(final Vector4d v) { + return this.apply(v::add, 0); + } + + public MutableVector4d sub(final MutableVector4d v) { + return this.apply(v::sub, 0); + } + + public Vector4d sub(final Vector4d v) { + return this.apply(v::sub, 0); + } + + public MutableVector4d mul(final MutableVector4d v) { + return this.apply(v::mul, 1); + } + + public Vector4d mul(final Vector4d v) { + return this.apply(v::mul, 1); + } + + public MutableVector4d div(final MutableVector4d v) { + return this.apply(v::div, 1); + } + + public Vector4d div(final Vector4d v) { + return this.apply(v::div, 1); + } + + public MutableVector4d pow(final MutableVector4d v) { + return this.apply(v::pow, 1); + } + + public Vector4d pow(final Vector4d v) { + final double x = Math.pow(v.x(), this.x.orElse(1)); + final double y = Math.pow(v.y(), this.y.orElse(1)); + final double z = Math.pow(v.z(), this.z.orElse(1)); + final double w = Math.pow(v.w(), this.w.orElse(1)); + return new Vector4d(x, y, z, w); + } + + public MutableVector4d min(final MutableVector4d v) { + return this.apply(v::min, Double.POSITIVE_INFINITY); + } + + public Vector4d min(final Vector4d v) { + return this.apply(v::min, Double.POSITIVE_INFINITY); + } + + public MutableVector4d max(final MutableVector4d v) { + return this.apply(v::max, Double.NEGATIVE_INFINITY); + } + + public Vector4d max(final Vector4d v) { + return this.apply(v::max, Double.NEGATIVE_INFINITY); + } + + @Override + public boolean equals(final Object other) { + if (this == other) { + return true; + } else if (!(other instanceof final OptionalVector4d that)) { + return false; + } else { + return this.x.equals(that.x) + && this.y.equals(that.y) + && this.z.equals(that.z) + && this.w.equals(that.w); + } + } + + @Override + public int hashCode() { + return Objects.hash(this.x, this.y, this.z, this.w); + } + + @Override + public String toString() { + return "(" + + "x=" + (this.x.isPresent() ? this.x.getAsDouble() : "?") + + "y=" + (this.y.isPresent() ? this.y.getAsDouble() : "?") + + "z=" + (this.z.isPresent() ? this.z.getAsDouble() : "?") + + "w=" + (this.w.isPresent() ? this.w.getAsDouble() : "?") + + ")"; + } + + public static OptionalVector4d x(final double x) { + return OptionalVector4d.x(convert(x)); + } + + public static OptionalVector4d x(final @Nullable Double x) { + return OptionalVector4d.x(convert(x)); + } + + public static OptionalVector4d x(final Optional x) { + return OptionalVector4d.x(convert(x)); + } + + public static OptionalVector4d x(final OptionalDouble x) { + return OptionalVector4d.of(x, empty(), empty(), empty()); + } + + public static OptionalVector4d y(final double y) { + return OptionalVector4d.y(convert(y)); + } + + public static OptionalVector4d y(final @Nullable Double y) { + return OptionalVector4d.y(convert(y)); + } + + public static OptionalVector4d y(final Optional y) { + return OptionalVector4d.y(convert(y)); + } + + public static OptionalVector4d y(final OptionalDouble y) { + return OptionalVector4d.of(empty(), y, empty(), empty()); + } + + public static OptionalVector4d z(final double z) { + return OptionalVector4d.z(convert(z)); + } + + public static OptionalVector4d z(final @Nullable Double z) { + return OptionalVector4d.z(convert(z)); + } + + public static OptionalVector4d z(final Optional z) { + return OptionalVector4d.z(convert(z)); + } + + public static OptionalVector4d z(final OptionalDouble z) { + return OptionalVector4d.of(empty(), empty(), z, empty()); + } + + public static OptionalVector4d w(final double w) { + return OptionalVector4d.w(convert(w)); + } + + public static OptionalVector4d w(final @Nullable Double w) { + return OptionalVector4d.w(convert(w)); + } + + public static OptionalVector4d w(final Optional w) { + return OptionalVector4d.w(convert(w)); + } + + public static OptionalVector4d w(final OptionalDouble w) { + return OptionalVector4d.of(empty(), empty(), empty(), w); + } + + public static OptionalVector4d xy(final double x, final double y) { + return OptionalVector4d.xy(convert(x), convert(y)); + } + + public static OptionalVector4d xy(final @Nullable Double x, final @Nullable Double y) { + return OptionalVector4d.xy(convert(x), convert(y)); + } + + public static OptionalVector4d xy(final Optional x, final Optional y) { + return OptionalVector4d.xy(convert(x), convert(y)); + } + + public static OptionalVector4d xy(final OptionalDouble x, final OptionalDouble y) { + return OptionalVector4d.of(x, y, empty(), empty()); + } + + public static OptionalVector4d xz(final double x, final double z) { + return OptionalVector4d.xz(convert(x), convert(z)); + } + + public static OptionalVector4d xz(final @Nullable Double x, final @Nullable Double z) { + return OptionalVector4d.xz(convert(x), convert(z)); + } + + public static OptionalVector4d xz(final Optional x, final Optional z) { + return OptionalVector4d.xz(convert(x), convert(z)); + } + + public static OptionalVector4d xz(final OptionalDouble x, final OptionalDouble z) { + return OptionalVector4d.of(x, empty(), z, empty()); + } + + public static OptionalVector4d xw(final double x, final double w) { + return OptionalVector4d.xw(convert(x), convert(w)); + } + + public static OptionalVector4d xw(final @Nullable Double x, final @Nullable Double w) { + return OptionalVector4d.xw(convert(x), convert(w)); + } + + public static OptionalVector4d xw(final Optional x, final Optional w) { + return OptionalVector4d.xw(convert(x), convert(w)); + } + + public static OptionalVector4d xw(final OptionalDouble x, final OptionalDouble w) { + return OptionalVector4d.of(x, empty(), empty(), w); + } + + public static OptionalVector4d yz(final double y, final double z) { + return OptionalVector4d.yz(convert(y), convert(z)); + } + + public static OptionalVector4d yz(final @Nullable Double y, final @Nullable Double z) { + return OptionalVector4d.yz(convert(y), convert(z)); + } + + public static OptionalVector4d yz(final Optional y, final Optional z) { + return OptionalVector4d.yz(convert(y), convert(z)); + } + + public static OptionalVector4d yz(final OptionalDouble y, final OptionalDouble z) { + return OptionalVector4d.of(empty(), y, z, empty()); + } + + public static OptionalVector4d yw(final double y, final double w) { + return OptionalVector4d.yw(convert(y), convert(w)); + } + + public static OptionalVector4d yw(final @Nullable Double y, final @Nullable Double w) { + return OptionalVector4d.yw(convert(y), convert(w)); + } + + public static OptionalVector4d yw(final Optional y, final Optional w) { + return OptionalVector4d.yw(convert(y), convert(w)); + } + + public static OptionalVector4d yw(final OptionalDouble y, final OptionalDouble w) { + return OptionalVector4d.of(empty(), y, empty(), w); + } + + public static OptionalVector4d zw(final double z, final double w) { + return OptionalVector4d.zw(convert(z), convert(w)); + } + + public static OptionalVector4d zw(final @Nullable Double z, final @Nullable Double w) { + return OptionalVector4d.zw(convert(z), convert(w)); + } + + public static OptionalVector4d zw(final Optional z, final Optional w) { + return OptionalVector4d.zw(convert(z), convert(w)); + } + + public static OptionalVector4d zw(final OptionalDouble z, final OptionalDouble w) { + return OptionalVector4d.of(empty(), empty(), z, w); + } + + public static OptionalVector4d xyz(final double x, final double y, final double z) { + return OptionalVector4d.xyz(convert(x), convert(y), convert(z)); + } + + public static OptionalVector4d xyz(final @Nullable Double x, final @Nullable Double y, final @Nullable Double z) { + return OptionalVector4d.xyz(convert(x), convert(y), convert(z)); + } + + public static OptionalVector4d xyz(final Optional x, final Optional y, final Optional z) { + return OptionalVector4d.xyz(convert(x), convert(y), convert(z)); + } + + public static OptionalVector4d xyz(final OptionalDouble x, final OptionalDouble y, final OptionalDouble z) { + return OptionalVector4d.of(x, y, z, empty()); + } + + public static OptionalVector4d xyw(final double x, final double y, final double w) { + return OptionalVector4d.xyw(convert(x), convert(y), convert(w)); + } + + public static OptionalVector4d xyw(final @Nullable Double x, final @Nullable Double y, final @Nullable Double w) { + return OptionalVector4d.xyw(convert(x), convert(y), convert(w)); + } + + public static OptionalVector4d xyw(final Optional x, final Optional y, final Optional w) { + return OptionalVector4d.xyw(convert(x), convert(y), convert(w)); + } + + public static OptionalVector4d xyw(final OptionalDouble x, final OptionalDouble y, final OptionalDouble w) { + return OptionalVector4d.of(x, y, empty(), w); + } + + public static OptionalVector4d xzw(final double x, final double z, final double w) { + return OptionalVector4d.xzw(convert(x), convert(z), convert(w)); + } + + public static OptionalVector4d xzw(final @Nullable Double x, final @Nullable Double z, final @Nullable Double w) { + return OptionalVector4d.xzw(convert(x), convert(z), convert(w)); + } + + public static OptionalVector4d xzw(final Optional x, final Optional z, final Optional w) { + return OptionalVector4d.xzw(convert(x), convert(z), convert(w)); + } + + public static OptionalVector4d xzw(final OptionalDouble x, final OptionalDouble z, final OptionalDouble w) { + return OptionalVector4d.of(x, empty(), z, w); + } + + public static OptionalVector4d yzw(final double y, final double z, final double w) { + return OptionalVector4d.yzw(convert(y), convert(z), convert(w)); + } + + public static OptionalVector4d yzw(final @Nullable Double y, final @Nullable Double z, final @Nullable Double w) { + return OptionalVector4d.yzw(convert(y), convert(z), convert(w)); + } + + public static OptionalVector4d yzw(final Optional y, final Optional z, final Optional w) { + return OptionalVector4d.yzw(convert(y), convert(z), convert(w)); + } + + public static OptionalVector4d yzw(final OptionalDouble y, final OptionalDouble z, final OptionalDouble w) { + return OptionalVector4d.of(empty(), y, z, w); + } + + public static OptionalVector4d of(final double x, final double y, final double z, final double w) { + return OptionalVector4d.of(convert(x), convert(y), convert(z), convert(w)); + } + + public static OptionalVector4d of(final @Nullable Double x, final @Nullable Double y, final @Nullable Double z, final @Nullable Double w) { + return OptionalVector4d.of(convert(x), convert(y), convert(z), convert(w)); + } + + public static OptionalVector4d of(final Optional x, final Optional y, final Optional z, final Optional w) { + return OptionalVector4d.of(convert(x), convert(y), convert(z), convert(w)); + } + + public static OptionalVector4d of(final OptionalDouble x, final OptionalDouble y, final OptionalDouble z, final OptionalDouble w) { + return new OptionalVector4d(x, y, z, w); + } + + private static OptionalDouble empty() { + return OptionalDouble.empty(); + } + + private static OptionalDouble convert(final double a) { + return OptionalDouble.of(a); + } + + private static OptionalDouble convert(final @Nullable Double a) { + return a == null ? empty() : OptionalDouble.of(a.doubleValue()); + } + + private static OptionalDouble convert(final Optional a) { + return a.map(OptionalDouble::of).orElseGet(OptionalVector4d::empty); + } + + private interface Transformer { + + Object apply(Operator operator, double neutralValue); + } + + private interface Operator { + + V apply(double x, double y, double z, double w); + } +} diff --git a/api/src/main/java/net/hellheim/spongetools/math/optional/vector/OptionalVector4i.java b/api/src/main/java/net/hellheim/spongetools/math/optional/vector/OptionalVector4i.java new file mode 100644 index 0000000..c7fa336 --- /dev/null +++ b/api/src/main/java/net/hellheim/spongetools/math/optional/vector/OptionalVector4i.java @@ -0,0 +1,482 @@ +package net.hellheim.spongetools.math.optional.vector; + +import java.util.Objects; +import java.util.Optional; +import java.util.OptionalInt; + +import org.checkerframework.checker.nullness.qual.Nullable; +import org.spongepowered.math.GenericMath; +import org.spongepowered.math.vector.Vector4i; + +import net.hellheim.spongetools.math.mutable.vector.MutableVector4i; + +public final class OptionalVector4i { + + private final OptionalInt x; + private final OptionalInt y; + private final OptionalInt z; + private final OptionalInt w; + private final Transformer transformer; + + private OptionalVector4i(final OptionalInt x, final OptionalInt y, final OptionalInt z, final OptionalInt w) { + this.x = x; + this.y = y; + this.z = z; + this.w = w; + + if (this.x.isPresent()) { + final int x0 = this.x.getAsInt(); + if (this.y.isPresent()) { + final int y0 = this.y.getAsInt(); + if (this.z.isPresent()) { + final int z0 = this.z.getAsInt(); + if (this.w.isPresent()) { + final int w0 = this.w.getAsInt(); + this.transformer = (o, v) -> o.apply(x0, y0, z0, w0); + } else { + this.transformer = (o, v) -> o.apply(x0, y0, z0, v); + } + } else if (this.w.isPresent()) { + final int w0 = this.w.getAsInt(); + this.transformer = (o, v) -> o.apply(x0, y0, v, w0); + } else { + this.transformer = (o, v) -> o.apply(x0, y0, v, v); + } + } else if (this.z.isPresent()) { + final int z0 = this.z.getAsInt(); + if (this.w.isPresent()) { + final int w0 = this.w.getAsInt(); + this.transformer = (o, v) -> o.apply(x0, v, z0, w0); + } else { + this.transformer = (o, v) -> o.apply(x0, v, z0, v); + } + } else if (this.w.isPresent()) { + final int w0 = this.w.getAsInt(); + this.transformer = (o, v) -> o.apply(x0, v, v, w0); + } else { + this.transformer = (o, v) -> o.apply(x0, v, v, v); + } + } else if (this.y.isPresent()) { + final int y0 = this.y.getAsInt(); + if (this.z.isPresent()) { + final int z0 = this.z.getAsInt(); + if (this.w.isPresent()) { + final int w0 = this.w.getAsInt(); + this.transformer = (o, v) -> o.apply(v, y0, z0, w0); + } else { + this.transformer = (o, v) -> o.apply(v, y0, z0, v); + } + } else if (this.w.isPresent()) { + final int w0 = this.w.getAsInt(); + this.transformer = (o, v) -> o.apply(v, y0, v, w0); + } else { + this.transformer = (o, v) -> o.apply(v, y0, v, v); + } + } else if (this.z.isPresent()) { + final int z0 = this.z.getAsInt(); + if (this.w.isPresent()) { + final int w0 = this.w.getAsInt(); + this.transformer = (o, v) -> o.apply(v, v, z0, w0); + } else { + this.transformer = (o, v) -> o.apply(v, v, z0, v); + } + } else if (this.w.isPresent()) { + final int w0 = this.w.getAsInt(); + this.transformer = (o, v) -> o.apply(v, v, v, w0); + } else { + this.transformer = (o, v) -> o; + } + } + + public OptionalInt x() { + return this.x; + } + + public OptionalInt y() { + return this.y; + } + + public OptionalInt z() { + return this.z; + } + + public OptionalInt w() { + return this.w; + } + + @SuppressWarnings("unchecked") + private V apply(final Operator operator, final int neutralValue) { + return (V) this.transformer.apply(operator, neutralValue); + } + + public MutableVector4i set(final MutableVector4i v) { + this.x.ifPresent(v::x); + this.y.ifPresent(v::y); + this.z.ifPresent(v::z); + this.w.ifPresent(v::w); + return v; + } + + public Vector4i set(final Vector4i v) { + final int x = this.x.orElse(v.x()); + final int y = this.y.orElse(v.y()); + final int z = this.z.orElse(v.z()); + final int w = this.w.orElse(v.w()); + return new Vector4i(x, y, z, w); + } + + public MutableVector4i add(final MutableVector4i v) { + return this.apply(v::add, 0); + } + + public Vector4i add(final Vector4i v) { + return this.apply(v::add, 0); + } + + public MutableVector4i sub(final MutableVector4i v) { + return this.apply(v::sub, 0); + } + + public Vector4i sub(final Vector4i v) { + return this.apply(v::sub, 0); + } + + public MutableVector4i mul(final MutableVector4i v) { + return this.apply(v::mul, 1); + } + + public Vector4i mul(final Vector4i v) { + return this.apply(v::mul, 1); + } + + public MutableVector4i div(final MutableVector4i v) { + return this.apply(v::div, 1); + } + + public Vector4i div(final Vector4i v) { + return this.apply(v::div, 1); + } + + public MutableVector4i pow(final MutableVector4i v) { + return this.apply(v::pow, 1); + } + + public Vector4i pow(final Vector4i v) { + final int x = GenericMath.floor(Math.pow(v.x(), this.x.orElse(1))); + final int y = GenericMath.floor(Math.pow(v.y(), this.y.orElse(1))); + final int z = GenericMath.floor(Math.pow(v.z(), this.z.orElse(1))); + final int w = GenericMath.floor(Math.pow(v.w(), this.w.orElse(1))); + return new Vector4i(x, y, z, w); + } + + public MutableVector4i min(final MutableVector4i v) { + return this.apply(v::min, Integer.MAX_VALUE); + } + + public Vector4i min(final Vector4i v) { + return this.apply(v::min, Integer.MAX_VALUE); + } + + public MutableVector4i max(final MutableVector4i v) { + return this.apply(v::max, Integer.MIN_VALUE); + } + + public Vector4i max(final Vector4i v) { + return this.apply(v::max, Integer.MIN_VALUE); + } + + @Override + public boolean equals(final Object other) { + if (this == other) { + return true; + } else if (!(other instanceof final OptionalVector4i that)) { + return false; + } else { + return this.x.equals(that.x) + && this.y.equals(that.y) + && this.z.equals(that.z) + && this.w.equals(that.w); + } + } + + @Override + public int hashCode() { + return Objects.hash(this.x, this.y, this.z, this.w); + } + + @Override + public String toString() { + return "(" + + "x=" + (this.x.isPresent() ? this.x.getAsInt() : "?") + + "y=" + (this.y.isPresent() ? this.y.getAsInt() : "?") + + "z=" + (this.z.isPresent() ? this.z.getAsInt() : "?") + + "w=" + (this.w.isPresent() ? this.w.getAsInt() : "?") + + ")"; + } + + public static OptionalVector4i x(final int x) { + return OptionalVector4i.x(convert(x)); + } + + public static OptionalVector4i x(final @Nullable Integer x) { + return OptionalVector4i.x(convert(x)); + } + + public static OptionalVector4i x(final Optional x) { + return OptionalVector4i.x(convert(x)); + } + + public static OptionalVector4i x(final OptionalInt x) { + return OptionalVector4i.of(x, empty(), empty(), empty()); + } + + public static OptionalVector4i y(final int y) { + return OptionalVector4i.y(convert(y)); + } + + public static OptionalVector4i y(final @Nullable Integer y) { + return OptionalVector4i.y(convert(y)); + } + + public static OptionalVector4i y(final Optional y) { + return OptionalVector4i.y(convert(y)); + } + + public static OptionalVector4i y(final OptionalInt y) { + return OptionalVector4i.of(empty(), y, empty(), empty()); + } + + public static OptionalVector4i z(final int z) { + return OptionalVector4i.z(convert(z)); + } + + public static OptionalVector4i z(final @Nullable Integer z) { + return OptionalVector4i.z(convert(z)); + } + + public static OptionalVector4i z(final Optional z) { + return OptionalVector4i.z(convert(z)); + } + + public static OptionalVector4i z(final OptionalInt z) { + return OptionalVector4i.of(empty(), empty(), z, empty()); + } + + public static OptionalVector4i w(final int w) { + return OptionalVector4i.w(convert(w)); + } + + public static OptionalVector4i w(final @Nullable Integer w) { + return OptionalVector4i.w(convert(w)); + } + + public static OptionalVector4i w(final Optional w) { + return OptionalVector4i.w(convert(w)); + } + + public static OptionalVector4i w(final OptionalInt w) { + return OptionalVector4i.of(empty(), empty(), empty(), w); + } + + public static OptionalVector4i xy(final int x, final int y) { + return OptionalVector4i.xy(convert(x), convert(y)); + } + + public static OptionalVector4i xy(final @Nullable Integer x, final @Nullable Integer y) { + return OptionalVector4i.xy(convert(x), convert(y)); + } + + public static OptionalVector4i xy(final Optional x, final Optional y) { + return OptionalVector4i.xy(convert(x), convert(y)); + } + + public static OptionalVector4i xy(final OptionalInt x, final OptionalInt y) { + return OptionalVector4i.of(x, y, empty(), empty()); + } + + public static OptionalVector4i xz(final int x, final int z) { + return OptionalVector4i.xz(convert(x), convert(z)); + } + + public static OptionalVector4i xz(final @Nullable Integer x, final @Nullable Integer z) { + return OptionalVector4i.xz(convert(x), convert(z)); + } + + public static OptionalVector4i xz(final Optional x, final Optional z) { + return OptionalVector4i.xz(convert(x), convert(z)); + } + + public static OptionalVector4i xz(final OptionalInt x, final OptionalInt z) { + return OptionalVector4i.of(x, empty(), z, empty()); + } + + public static OptionalVector4i xw(final int x, final int w) { + return OptionalVector4i.xw(convert(x), convert(w)); + } + + public static OptionalVector4i xw(final @Nullable Integer x, final @Nullable Integer w) { + return OptionalVector4i.xw(convert(x), convert(w)); + } + + public static OptionalVector4i xw(final Optional x, final Optional w) { + return OptionalVector4i.xw(convert(x), convert(w)); + } + + public static OptionalVector4i xw(final OptionalInt x, final OptionalInt w) { + return OptionalVector4i.of(x, empty(), empty(), w); + } + + public static OptionalVector4i yz(final int y, final int z) { + return OptionalVector4i.yz(convert(y), convert(z)); + } + + public static OptionalVector4i yz(final @Nullable Integer y, final @Nullable Integer z) { + return OptionalVector4i.yz(convert(y), convert(z)); + } + + public static OptionalVector4i yz(final Optional y, final Optional z) { + return OptionalVector4i.yz(convert(y), convert(z)); + } + + public static OptionalVector4i yz(final OptionalInt y, final OptionalInt z) { + return OptionalVector4i.of(empty(), y, z, empty()); + } + + public static OptionalVector4i yw(final int y, final int w) { + return OptionalVector4i.yw(convert(y), convert(w)); + } + + public static OptionalVector4i yw(final @Nullable Integer y, final @Nullable Integer w) { + return OptionalVector4i.yw(convert(y), convert(w)); + } + + public static OptionalVector4i yw(final Optional y, final Optional w) { + return OptionalVector4i.yw(convert(y), convert(w)); + } + + public static OptionalVector4i yw(final OptionalInt y, final OptionalInt w) { + return OptionalVector4i.of(empty(), y, empty(), w); + } + + public static OptionalVector4i zw(final int z, final int w) { + return OptionalVector4i.zw(convert(z), convert(w)); + } + + public static OptionalVector4i zw(final @Nullable Integer z, final @Nullable Integer w) { + return OptionalVector4i.zw(convert(z), convert(w)); + } + + public static OptionalVector4i zw(final Optional z, final Optional w) { + return OptionalVector4i.zw(convert(z), convert(w)); + } + + public static OptionalVector4i zw(final OptionalInt z, final OptionalInt w) { + return OptionalVector4i.of(empty(), empty(), z, w); + } + + public static OptionalVector4i xyz(final int x, final int y, final int z) { + return OptionalVector4i.xyz(convert(x), convert(y), convert(z)); + } + + public static OptionalVector4i xyz(final @Nullable Integer x, final @Nullable Integer y, final @Nullable Integer z) { + return OptionalVector4i.xyz(convert(x), convert(y), convert(z)); + } + + public static OptionalVector4i xyz(final Optional x, final Optional y, final Optional z) { + return OptionalVector4i.xyz(convert(x), convert(y), convert(z)); + } + + public static OptionalVector4i xyz(final OptionalInt x, final OptionalInt y, final OptionalInt z) { + return OptionalVector4i.of(x, y, z, empty()); + } + + public static OptionalVector4i xyw(final int x, final int y, final int w) { + return OptionalVector4i.xyw(convert(x), convert(y), convert(w)); + } + + public static OptionalVector4i xyw(final @Nullable Integer x, final @Nullable Integer y, final @Nullable Integer w) { + return OptionalVector4i.xyw(convert(x), convert(y), convert(w)); + } + + public static OptionalVector4i xyw(final Optional x, final Optional y, final Optional w) { + return OptionalVector4i.xyw(convert(x), convert(y), convert(w)); + } + + public static OptionalVector4i xyw(final OptionalInt x, final OptionalInt y, final OptionalInt w) { + return OptionalVector4i.of(x, y, empty(), w); + } + + public static OptionalVector4i xzw(final int x, final int z, final int w) { + return OptionalVector4i.xzw(convert(x), convert(z), convert(w)); + } + + public static OptionalVector4i xzw(final @Nullable Integer x, final @Nullable Integer z, final @Nullable Integer w) { + return OptionalVector4i.xzw(convert(x), convert(z), convert(w)); + } + + public static OptionalVector4i xzw(final Optional x, final Optional z, final Optional w) { + return OptionalVector4i.xzw(convert(x), convert(z), convert(w)); + } + + public static OptionalVector4i xzw(final OptionalInt x, final OptionalInt z, final OptionalInt w) { + return OptionalVector4i.of(x, empty(), z, w); + } + + public static OptionalVector4i yzw(final int y, final int z, final int w) { + return OptionalVector4i.yzw(convert(y), convert(z), convert(w)); + } + + public static OptionalVector4i yzw(final @Nullable Integer y, final @Nullable Integer z, final @Nullable Integer w) { + return OptionalVector4i.yzw(convert(y), convert(z), convert(w)); + } + + public static OptionalVector4i yzw(final Optional y, final Optional z, final Optional w) { + return OptionalVector4i.yzw(convert(y), convert(z), convert(w)); + } + + public static OptionalVector4i yzw(final OptionalInt y, final OptionalInt z, final OptionalInt w) { + return OptionalVector4i.of(empty(), y, z, w); + } + + public static OptionalVector4i of(final int x, final int y, final int z, final int w) { + return OptionalVector4i.of(convert(x), convert(y), convert(z), convert(w)); + } + + public static OptionalVector4i of(final @Nullable Integer x, final @Nullable Integer y, final @Nullable Integer z, final @Nullable Integer w) { + return OptionalVector4i.of(convert(x), convert(y), convert(z), convert(w)); + } + + public static OptionalVector4i of(final Optional x, final Optional y, final Optional z, final Optional w) { + return OptionalVector4i.of(convert(x), convert(y), convert(z), convert(w)); + } + + public static OptionalVector4i of(final OptionalInt x, final OptionalInt y, final OptionalInt z, final OptionalInt w) { + return new OptionalVector4i(x, y, z, w); + } + + private static OptionalInt empty() { + return OptionalInt.empty(); + } + + private static OptionalInt convert(final int a) { + return OptionalInt.of(a); + } + + private static OptionalInt convert(final @Nullable Integer a) { + return a == null ? empty() : OptionalInt.of(a.intValue()); + } + + private static OptionalInt convert(final Optional a) { + return a.map(OptionalInt::of).orElseGet(OptionalVector4i::empty); + } + + private interface Transformer { + + Object apply(Operator operator, int neutralValue); + } + + private interface Operator { + + V apply(int x, int y, int z, int w); + } +} diff --git a/api/src/main/java/net/hellheim/spongetools/math/optional/vector/OptionalVector4l.java b/api/src/main/java/net/hellheim/spongetools/math/optional/vector/OptionalVector4l.java new file mode 100644 index 0000000..f3a56c8 --- /dev/null +++ b/api/src/main/java/net/hellheim/spongetools/math/optional/vector/OptionalVector4l.java @@ -0,0 +1,482 @@ +package net.hellheim.spongetools.math.optional.vector; + +import java.util.Objects; +import java.util.Optional; +import java.util.OptionalLong; + +import org.checkerframework.checker.nullness.qual.Nullable; +import org.spongepowered.math.GenericMath; +import org.spongepowered.math.vector.Vector4l; + +import net.hellheim.spongetools.math.mutable.vector.MutableVector4l; + +public final class OptionalVector4l { + + private final OptionalLong x; + private final OptionalLong y; + private final OptionalLong z; + private final OptionalLong w; + private final Transformer transformer; + + private OptionalVector4l(final OptionalLong x, final OptionalLong y, final OptionalLong z, final OptionalLong w) { + this.x = x; + this.y = y; + this.z = z; + this.w = w; + + if (this.x.isPresent()) { + final long x0 = this.x.getAsLong(); + if (this.y.isPresent()) { + final long y0 = this.y.getAsLong(); + if (this.z.isPresent()) { + final long z0 = this.z.getAsLong(); + if (this.w.isPresent()) { + final long w0 = this.w.getAsLong(); + this.transformer = (o, v) -> o.apply(x0, y0, z0, w0); + } else { + this.transformer = (o, v) -> o.apply(x0, y0, z0, v); + } + } else if (this.w.isPresent()) { + final long w0 = this.w.getAsLong(); + this.transformer = (o, v) -> o.apply(x0, y0, v, w0); + } else { + this.transformer = (o, v) -> o.apply(x0, y0, v, v); + } + } else if (this.z.isPresent()) { + final long z0 = this.z.getAsLong(); + if (this.w.isPresent()) { + final long w0 = this.w.getAsLong(); + this.transformer = (o, v) -> o.apply(x0, v, z0, w0); + } else { + this.transformer = (o, v) -> o.apply(x0, v, z0, v); + } + } else if (this.w.isPresent()) { + final long w0 = this.w.getAsLong(); + this.transformer = (o, v) -> o.apply(x0, v, v, w0); + } else { + this.transformer = (o, v) -> o.apply(x0, v, v, v); + } + } else if (this.y.isPresent()) { + final long y0 = this.y.getAsLong(); + if (this.z.isPresent()) { + final long z0 = this.z.getAsLong(); + if (this.w.isPresent()) { + final long w0 = this.w.getAsLong(); + this.transformer = (o, v) -> o.apply(v, y0, z0, w0); + } else { + this.transformer = (o, v) -> o.apply(v, y0, z0, v); + } + } else if (this.w.isPresent()) { + final long w0 = this.w.getAsLong(); + this.transformer = (o, v) -> o.apply(v, y0, v, w0); + } else { + this.transformer = (o, v) -> o.apply(v, y0, v, v); + } + } else if (this.z.isPresent()) { + final long z0 = this.z.getAsLong(); + if (this.w.isPresent()) { + final long w0 = this.w.getAsLong(); + this.transformer = (o, v) -> o.apply(v, v, z0, w0); + } else { + this.transformer = (o, v) -> o.apply(v, v, z0, v); + } + } else if (this.w.isPresent()) { + final long w0 = this.w.getAsLong(); + this.transformer = (o, v) -> o.apply(v, v, v, w0); + } else { + this.transformer = (o, v) -> o; + } + } + + public OptionalLong x() { + return this.x; + } + + public OptionalLong y() { + return this.y; + } + + public OptionalLong z() { + return this.z; + } + + public OptionalLong w() { + return this.w; + } + + @SuppressWarnings("unchecked") + private V apply(final Operator operator, final long neutralValue) { + return (V) this.transformer.apply(operator, neutralValue); + } + + public MutableVector4l set(final MutableVector4l v) { + this.x.ifPresent(v::x); + this.y.ifPresent(v::y); + this.z.ifPresent(v::z); + this.w.ifPresent(v::w); + return v; + } + + public Vector4l set(final Vector4l v) { + final long x = this.x.orElse(v.x()); + final long y = this.y.orElse(v.y()); + final long z = this.z.orElse(v.z()); + final long w = this.w.orElse(v.w()); + return new Vector4l(x, y, z, w); + } + + public MutableVector4l add(final MutableVector4l v) { + return this.apply(v::add, 0); + } + + public Vector4l add(final Vector4l v) { + return this.apply(v::add, 0); + } + + public MutableVector4l sub(final MutableVector4l v) { + return this.apply(v::sub, 0); + } + + public Vector4l sub(final Vector4l v) { + return this.apply(v::sub, 0); + } + + public MutableVector4l mul(final MutableVector4l v) { + return this.apply(v::mul, 1); + } + + public Vector4l mul(final Vector4l v) { + return this.apply(v::mul, 1); + } + + public MutableVector4l div(final MutableVector4l v) { + return this.apply(v::div, 1); + } + + public Vector4l div(final Vector4l v) { + return this.apply(v::div, 1); + } + + public MutableVector4l pow(final MutableVector4l v) { + return this.apply(v::pow, 1); + } + + public Vector4l pow(final Vector4l v) { + final long x = GenericMath.floorl(Math.pow(v.x(), this.x.orElse(1))); + final long y = GenericMath.floorl(Math.pow(v.y(), this.y.orElse(1))); + final long z = GenericMath.floorl(Math.pow(v.z(), this.z.orElse(1))); + final long w = GenericMath.floorl(Math.pow(v.w(), this.w.orElse(1))); + return new Vector4l(x, y, z, w); + } + + public MutableVector4l min(final MutableVector4l v) { + return this.apply(v::min, Long.MAX_VALUE); + } + + public Vector4l min(final Vector4l v) { + return this.apply(v::min, Long.MAX_VALUE); + } + + public MutableVector4l max(final MutableVector4l v) { + return this.apply(v::max, Long.MIN_VALUE); + } + + public Vector4l max(final Vector4l v) { + return this.apply(v::max, Long.MIN_VALUE); + } + + @Override + public boolean equals(final Object other) { + if (this == other) { + return true; + } else if (!(other instanceof final OptionalVector4l that)) { + return false; + } else { + return this.x.equals(that.x) + && this.y.equals(that.y) + && this.z.equals(that.z) + && this.w.equals(that.w); + } + } + + @Override + public int hashCode() { + return Objects.hash(this.x, this.y, this.z, this.w); + } + + @Override + public String toString() { + return "(" + + "x=" + (this.x.isPresent() ? this.x.getAsLong() : "?") + + "y=" + (this.y.isPresent() ? this.y.getAsLong() : "?") + + "z=" + (this.z.isPresent() ? this.z.getAsLong() : "?") + + "w=" + (this.w.isPresent() ? this.w.getAsLong() : "?") + + ")"; + } + + public static OptionalVector4l x(final long x) { + return OptionalVector4l.x(convert(x)); + } + + public static OptionalVector4l x(final @Nullable Long x) { + return OptionalVector4l.x(convert(x)); + } + + public static OptionalVector4l x(final Optional x) { + return OptionalVector4l.x(convert(x)); + } + + public static OptionalVector4l x(final OptionalLong x) { + return OptionalVector4l.of(x, empty(), empty(), empty()); + } + + public static OptionalVector4l y(final long y) { + return OptionalVector4l.y(convert(y)); + } + + public static OptionalVector4l y(final @Nullable Long y) { + return OptionalVector4l.y(convert(y)); + } + + public static OptionalVector4l y(final Optional y) { + return OptionalVector4l.y(convert(y)); + } + + public static OptionalVector4l y(final OptionalLong y) { + return OptionalVector4l.of(empty(), y, empty(), empty()); + } + + public static OptionalVector4l z(final long z) { + return OptionalVector4l.z(convert(z)); + } + + public static OptionalVector4l z(final @Nullable Long z) { + return OptionalVector4l.z(convert(z)); + } + + public static OptionalVector4l z(final Optional z) { + return OptionalVector4l.z(convert(z)); + } + + public static OptionalVector4l z(final OptionalLong z) { + return OptionalVector4l.of(empty(), empty(), z, empty()); + } + + public static OptionalVector4l w(final long w) { + return OptionalVector4l.w(convert(w)); + } + + public static OptionalVector4l w(final @Nullable Long w) { + return OptionalVector4l.w(convert(w)); + } + + public static OptionalVector4l w(final Optional w) { + return OptionalVector4l.w(convert(w)); + } + + public static OptionalVector4l w(final OptionalLong w) { + return OptionalVector4l.of(empty(), empty(), empty(), w); + } + + public static OptionalVector4l xy(final long x, final long y) { + return OptionalVector4l.xy(convert(x), convert(y)); + } + + public static OptionalVector4l xy(final @Nullable Long x, final @Nullable Long y) { + return OptionalVector4l.xy(convert(x), convert(y)); + } + + public static OptionalVector4l xy(final Optional x, final Optional y) { + return OptionalVector4l.xy(convert(x), convert(y)); + } + + public static OptionalVector4l xy(final OptionalLong x, final OptionalLong y) { + return OptionalVector4l.of(x, y, empty(), empty()); + } + + public static OptionalVector4l xz(final long x, final long z) { + return OptionalVector4l.xz(convert(x), convert(z)); + } + + public static OptionalVector4l xz(final @Nullable Long x, final @Nullable Long z) { + return OptionalVector4l.xz(convert(x), convert(z)); + } + + public static OptionalVector4l xz(final Optional x, final Optional z) { + return OptionalVector4l.xz(convert(x), convert(z)); + } + + public static OptionalVector4l xz(final OptionalLong x, final OptionalLong z) { + return OptionalVector4l.of(x, empty(), z, empty()); + } + + public static OptionalVector4l xw(final long x, final long w) { + return OptionalVector4l.xw(convert(x), convert(w)); + } + + public static OptionalVector4l xw(final @Nullable Long x, final @Nullable Long w) { + return OptionalVector4l.xw(convert(x), convert(w)); + } + + public static OptionalVector4l xw(final Optional x, final Optional w) { + return OptionalVector4l.xw(convert(x), convert(w)); + } + + public static OptionalVector4l xw(final OptionalLong x, final OptionalLong w) { + return OptionalVector4l.of(x, empty(), empty(), w); + } + + public static OptionalVector4l yz(final long y, final long z) { + return OptionalVector4l.yz(convert(y), convert(z)); + } + + public static OptionalVector4l yz(final @Nullable Long y, final @Nullable Long z) { + return OptionalVector4l.yz(convert(y), convert(z)); + } + + public static OptionalVector4l yz(final Optional y, final Optional z) { + return OptionalVector4l.yz(convert(y), convert(z)); + } + + public static OptionalVector4l yz(final OptionalLong y, final OptionalLong z) { + return OptionalVector4l.of(empty(), y, z, empty()); + } + + public static OptionalVector4l yw(final long y, final long w) { + return OptionalVector4l.yw(convert(y), convert(w)); + } + + public static OptionalVector4l yw(final @Nullable Long y, final @Nullable Long w) { + return OptionalVector4l.yw(convert(y), convert(w)); + } + + public static OptionalVector4l yw(final Optional y, final Optional w) { + return OptionalVector4l.yw(convert(y), convert(w)); + } + + public static OptionalVector4l yw(final OptionalLong y, final OptionalLong w) { + return OptionalVector4l.of(empty(), y, empty(), w); + } + + public static OptionalVector4l zw(final long z, final long w) { + return OptionalVector4l.zw(convert(z), convert(w)); + } + + public static OptionalVector4l zw(final @Nullable Long z, final @Nullable Long w) { + return OptionalVector4l.zw(convert(z), convert(w)); + } + + public static OptionalVector4l zw(final Optional z, final Optional w) { + return OptionalVector4l.zw(convert(z), convert(w)); + } + + public static OptionalVector4l zw(final OptionalLong z, final OptionalLong w) { + return OptionalVector4l.of(empty(), empty(), z, w); + } + + public static OptionalVector4l xyz(final long x, final long y, final long z) { + return OptionalVector4l.xyz(convert(x), convert(y), convert(z)); + } + + public static OptionalVector4l xyz(final @Nullable Long x, final @Nullable Long y, final @Nullable Long z) { + return OptionalVector4l.xyz(convert(x), convert(y), convert(z)); + } + + public static OptionalVector4l xyz(final Optional x, final Optional y, final Optional z) { + return OptionalVector4l.xyz(convert(x), convert(y), convert(z)); + } + + public static OptionalVector4l xyz(final OptionalLong x, final OptionalLong y, final OptionalLong z) { + return OptionalVector4l.of(x, y, z, empty()); + } + + public static OptionalVector4l xyw(final long x, final long y, final long w) { + return OptionalVector4l.xyw(convert(x), convert(y), convert(w)); + } + + public static OptionalVector4l xyw(final @Nullable Long x, final @Nullable Long y, final @Nullable Long w) { + return OptionalVector4l.xyw(convert(x), convert(y), convert(w)); + } + + public static OptionalVector4l xyw(final Optional x, final Optional y, final Optional w) { + return OptionalVector4l.xyw(convert(x), convert(y), convert(w)); + } + + public static OptionalVector4l xyw(final OptionalLong x, final OptionalLong y, final OptionalLong w) { + return OptionalVector4l.of(x, y, empty(), w); + } + + public static OptionalVector4l xzw(final long x, final long z, final long w) { + return OptionalVector4l.xzw(convert(x), convert(z), convert(w)); + } + + public static OptionalVector4l xzw(final @Nullable Long x, final @Nullable Long z, final @Nullable Long w) { + return OptionalVector4l.xzw(convert(x), convert(z), convert(w)); + } + + public static OptionalVector4l xzw(final Optional x, final Optional z, final Optional w) { + return OptionalVector4l.xzw(convert(x), convert(z), convert(w)); + } + + public static OptionalVector4l xzw(final OptionalLong x, final OptionalLong z, final OptionalLong w) { + return OptionalVector4l.of(x, empty(), z, w); + } + + public static OptionalVector4l yzw(final long y, final long z, final long w) { + return OptionalVector4l.yzw(convert(y), convert(z), convert(w)); + } + + public static OptionalVector4l yzw(final @Nullable Long y, final @Nullable Long z, final @Nullable Long w) { + return OptionalVector4l.yzw(convert(y), convert(z), convert(w)); + } + + public static OptionalVector4l yzw(final Optional y, final Optional z, final Optional w) { + return OptionalVector4l.yzw(convert(y), convert(z), convert(w)); + } + + public static OptionalVector4l yzw(final OptionalLong y, final OptionalLong z, final OptionalLong w) { + return OptionalVector4l.of(empty(), y, z, w); + } + + public static OptionalVector4l of(final long x, final long y, final long z, final long w) { + return OptionalVector4l.of(convert(x), convert(y), convert(z), convert(w)); + } + + public static OptionalVector4l of(final @Nullable Long x, final @Nullable Long y, final @Nullable Long z, final @Nullable Long w) { + return OptionalVector4l.of(convert(x), convert(y), convert(z), convert(w)); + } + + public static OptionalVector4l of(final Optional x, final Optional y, final Optional z, final Optional w) { + return OptionalVector4l.of(convert(x), convert(y), convert(z), convert(w)); + } + + public static OptionalVector4l of(final OptionalLong x, final OptionalLong y, final OptionalLong z, final OptionalLong w) { + return new OptionalVector4l(x, y, z, w); + } + + private static OptionalLong empty() { + return OptionalLong.empty(); + } + + private static OptionalLong convert(final long a) { + return OptionalLong.of(a); + } + + private static OptionalLong convert(final @Nullable Long a) { + return a == null ? empty() : OptionalLong.of(a.longValue()); + } + + private static OptionalLong convert(final Optional a) { + return a.map(OptionalLong::of).orElseGet(OptionalVector4l::empty); + } + + private interface Transformer { + + Object apply(Operator operator, long neutralValue); + } + + private interface Operator { + + V apply(long x, long y, long z, long w); + } +} diff --git a/api/src/main/java/net/hellheim/spongetools/math/set/CardinalNeighbourShape.java b/api/src/main/java/net/hellheim/spongetools/math/set/CardinalNeighbourShape.java new file mode 100644 index 0000000..a852678 --- /dev/null +++ b/api/src/main/java/net/hellheim/spongetools/math/set/CardinalNeighbourShape.java @@ -0,0 +1,22 @@ +package net.hellheim.spongetools.math.set; + +import java.util.random.RandomGenerator; + +import net.hellheim.spongetools.math.mutable.vector.MutableVector3i; +import net.hellheim.spongetools.object.Streamable; + +public class CardinalNeighbourShape implements CountableVectorSet.Vec3i { + + @Override + public Streamable all() { + // TODO Auto-generated method stub + return null; + } + + @Override + public Streamable random(RandomGenerator random) { + // TODO Auto-generated method stub + return null; + } + +} diff --git a/api/src/main/java/net/hellheim/spongetools/math/set/CountableVectorSet.java b/api/src/main/java/net/hellheim/spongetools/math/set/CountableVectorSet.java new file mode 100644 index 0000000..422510f --- /dev/null +++ b/api/src/main/java/net/hellheim/spongetools/math/set/CountableVectorSet.java @@ -0,0 +1,82 @@ +package net.hellheim.spongetools.math.set; + +import java.util.random.RandomGenerator; + +import org.spongepowered.math.vector.Vector2i; +import org.spongepowered.math.vector.Vector3i; +import org.spongepowered.math.vector.Vector4i; + +import net.hellheim.spongetools.math.mutable.vector.MutableVector2i; +import net.hellheim.spongetools.math.mutable.vector.MutableVector3i; +import net.hellheim.spongetools.math.mutable.vector.MutableVector4i; +import net.hellheim.spongetools.object.Streamable; + +/** + * @param The vector type + */ +public interface CountableVectorSet { + + Streamable all(); + + /** + * @return The infinite Streamable + */ + Streamable random(RandomGenerator random); + + interface Vec2i extends CountableVectorSet { + + default Streamable all(final Vector2i translation) { + return this.all(translation.x(), translation.y()); + } + + default Streamable all(final int xTranslation, final int yTranslation) { + return this.all().map(v -> v.add(xTranslation, yTranslation)); + } + + default Streamable random(final RandomGenerator random, final Vector2i translation) { + return this.random(random, translation.x(), translation.y()); + } + + default Streamable random(final RandomGenerator random, final int xTranslation, final int yTranslation) { + return this.random(random).map(v -> v.add(xTranslation, yTranslation)); + } + } + + interface Vec3i extends CountableVectorSet { + + default Streamable all(final Vector3i translation) { + return this.all(translation.x(), translation.y(), translation.z()); + } + + default Streamable all(final int xTranslation, final int yTranslation, final int zTranslation) { + return this.all().map(v -> v.add(xTranslation, yTranslation, zTranslation)); + } + + default Streamable random(final RandomGenerator random, final Vector3i translation) { + return this.random(random, translation.x(), translation.y(), translation.z()); + } + + default Streamable random(final RandomGenerator random, final int xTranslation, final int yTranslation, final int zTranslation) { + return this.random(random).map(v -> v.add(xTranslation, yTranslation, zTranslation)); + } + } + + interface Vec4i extends CountableVectorSet { + + default Streamable all(final Vector4i translation) { + return this.all(translation.x(), translation.y(), translation.z(), translation.w()); + } + + default Streamable all(final int xTranslation, final int yTranslation, final int zTranslation, final int wTranslation) { + return this.all().map(v -> v.add(xTranslation, yTranslation, zTranslation, wTranslation)); + } + + default Streamable random(final RandomGenerator random, final Vector4i translation) { + return this.random(random, translation.x(), translation.y(), translation.z(), translation.w()); + } + + default Streamable random(final RandomGenerator random, final int xTranslation, final int yTranslation, final int zTranslation, final int wTranslation) { + return this.random(random).map(v -> v.add(xTranslation, yTranslation, zTranslation, wTranslation)); + } + } +} diff --git a/api/src/main/java/net/hellheim/spongetools/math/set/Ellipse.java b/api/src/main/java/net/hellheim/spongetools/math/set/Ellipse.java new file mode 100644 index 0000000..455914e --- /dev/null +++ b/api/src/main/java/net/hellheim/spongetools/math/set/Ellipse.java @@ -0,0 +1,89 @@ +package net.hellheim.spongetools.math.set; + +import java.util.random.RandomGenerator; +import java.util.stream.Stream; + +import com.google.common.base.Preconditions; +import com.google.common.collect.AbstractIterator; + +import net.hellheim.spongetools.math.mutable.vector.MutableVector2i; +import net.hellheim.spongetools.object.Streamable; + +public class Ellipse implements CountableVectorSet.Vec2i { + + private final int xRad; + private final int yRad; + + private final int xRad2; + private final int yRad2; + private final double xMul; + + protected Ellipse(final int xRadius, final int yRadius) { + Preconditions.checkArgument(xRadius > 0, "xRadius must be positive"); + Preconditions.checkArgument(yRadius > 0, "yRadius must be positive"); + this.xRad = xRadius; + this.yRad = yRadius; + + this.xRad2 = xRadius * xRadius; + this.yRad2 = yRadius * yRadius; + this.xMul = ((double) this.yRad2) / this.xRad2; + } + + public int xRadius() { + return this.xRad; + } + + public int yRadius() { + return this.yRad; + } + + public boolean isInside(final int x, final int y) { + return this.yRad2 * x * x + this.xRad2 * y * y <= this.xRad2 * this.yRad2; + } + + @Override + public Streamable all() { + return Streamable.of(() -> new AbstractIterator() { + private final MutableVector2i cursor = MutableVector2i.zero(); + private boolean yMirror = false; + private int x = - Ellipse.this.xRad - 1; + private int y; + private int y2Max; + + @Override + protected MutableVector2i computeNext() { + if (this.yMirror) { + this.yMirror = false; + return this.cursor.set(this.x, this.y); + } + + ++this.y; + if (this.y * this.y <= this.y2Max) { + this.yMirror = true; + return this.cursor.set(this.x, this.y); + } + + ++this.x; + if (this.x > Ellipse.this.xRad) { + return this.endOfData(); + } + + this.y = 0; + this.y2Max = (int) (Ellipse.this.yRad2 - Ellipse.this.xMul * this.x); + return this.cursor.set(this.x, 0); + } + }); + } + + @Override + public Streamable random(final RandomGenerator random) { + return Streamable.of(() -> { + final MutableVector2i cursor = MutableVector2i.zero(); + return Stream.generate(() -> { + final int x = random.nextInt(-this.xRad, this.xRad + 1); + final int y = random.nextInt(-this.yRad, this.yRad + 1); + return cursor.set(x, y); + }).filter(v -> this.isInside(v.x(), v.y())); + }); + } +} diff --git a/api/src/main/java/net/hellheim/spongetools/math/set/Ellipsoid.java b/api/src/main/java/net/hellheim/spongetools/math/set/Ellipsoid.java new file mode 100644 index 0000000..9edd649 --- /dev/null +++ b/api/src/main/java/net/hellheim/spongetools/math/set/Ellipsoid.java @@ -0,0 +1,43 @@ +package net.hellheim.spongetools.math.set; + +import java.util.random.RandomGenerator; + +import com.google.common.base.Preconditions; + +import net.hellheim.spongetools.math.mutable.vector.MutableVector3i; +import net.hellheim.spongetools.object.Streamable; + +public class Ellipsoid implements CountableVectorSet.Vec3i { + + private final int xRad; + private final int yRad; + private final int zRad; + + private final int xRad2; + private final int yRad2; + private final double xMul; + + protected Ellipsoid(final int xRadius, final int yRadius, final int zRadius) { + Preconditions.checkArgument(xRadius > 0, "xRadius must be positive"); + Preconditions.checkArgument(yRadius > 0, "yRadius must be positive"); + Preconditions.checkArgument(zRadius > 0, "zRadius must be positive"); + this.xRad = xRadius; + this.yRad = yRadius; + this.zRad = zRadius; + + this.xRad2 = xRadius * xRadius; + this.yRad2 = yRadius * yRadius; + this.xMul = ((double) this.yRad2) / this.xRad2; + } + + @Override + public Streamable all() { + return null; + } + + @Override + public Streamable random(RandomGenerator random) { + // TODO Auto-generated method stub + return null; + } +} diff --git a/api/src/main/java/net/hellheim/spongetools/math/set/Manhattan2.java b/api/src/main/java/net/hellheim/spongetools/math/set/Manhattan2.java new file mode 100644 index 0000000..e275250 --- /dev/null +++ b/api/src/main/java/net/hellheim/spongetools/math/set/Manhattan2.java @@ -0,0 +1,98 @@ +package net.hellheim.spongetools.math.set; + +import java.util.random.RandomGenerator; +import java.util.stream.Stream; + +import com.google.common.base.Preconditions; +import com.google.common.collect.AbstractIterator; + +import net.hellheim.spongetools.math.mutable.vector.MutableVector2i; +import net.hellheim.spongetools.object.Streamable; + +public class Manhattan2 implements CountableVectorSet.Vec2i { + + private final int xDepth; + private final int yDepth; + private final int totalDepth; + + protected Manhattan2(final int xDepth, final int yDepth) { + Preconditions.checkArgument(xDepth >= 0, "xDepth must not be negative"); + Preconditions.checkArgument(yDepth >= 0, "yDepth must not be negative"); + this.xDepth = xDepth; + this.yDepth = yDepth; + this.totalDepth = xDepth + yDepth; + } + + public int xDepth() { + return this.xDepth; + } + + public int yDepth() { + return this.yDepth; + } + + @Override + public Streamable all() { + return Streamable.of(() -> new AbstractIterator() { + private final MutableVector2i cursor = MutableVector2i.zero(); + private int currentDepth; + private int xMax; + private int x; + private boolean yMirror; + + @Override + protected MutableVector2i computeNext() { + if (this.yMirror) { + this.yMirror = false; + return this.cursor.set(this.x - 1, -this.cursor.y()); + } + + MutableVector2i vec; + for (vec = null; vec == null; this.x++) { + if (this.x > this.xMax) { + this.currentDepth++; + if (this.currentDepth > Manhattan2.this.totalDepth) { + return this.endOfData(); + } + + this.xMax = Math.min(Manhattan2.this.xDepth, this.currentDepth); + this.x = -this.xMax; + } + + final int y = this.currentDepth - Math.abs(this.x); + if (y <= Manhattan2.this.yDepth) { + this.yMirror = y != 0; + vec = this.cursor.set(this.x, y); + } + } + + return vec; + }; + }); + } + + @Override + public Streamable random(final RandomGenerator random) { + return Streamable.of(() -> { + final MutableVector2i cursor = MutableVector2i.zero(); + return Stream.generate(() -> { + int x = random.nextInt(this.xDepth + 1); + int y = random.nextInt(this.yDepth + 1); + + if ((x + y) > this.totalDepth) { + x = this.xDepth - x; + y = this.yDepth - y; + } + + if (random.nextBoolean()) { + x = -x; + } + if (random.nextBoolean()) { + y = -y; + } + + return cursor.set(x, y); + }); + }); + } +} diff --git a/api/src/main/java/net/hellheim/spongetools/math/set/Manhattan3.java b/api/src/main/java/net/hellheim/spongetools/math/set/Manhattan3.java new file mode 100644 index 0000000..a774aee --- /dev/null +++ b/api/src/main/java/net/hellheim/spongetools/math/set/Manhattan3.java @@ -0,0 +1,119 @@ +package net.hellheim.spongetools.math.set; + +import java.util.random.RandomGenerator; +import java.util.stream.Stream; + +import com.google.common.base.Preconditions; +import com.google.common.collect.AbstractIterator; + +import net.hellheim.spongetools.math.mutable.vector.MutableVector3i; +import net.hellheim.spongetools.object.Streamable; + +public class Manhattan3 implements CountableVectorSet.Vec3i { + + private final int xDepth; + private final int yDepth; + private final int zDepth; + private final long totalDepth; + + protected Manhattan3(final int xDepth, final int yDepth, final int zDepth) { + Preconditions.checkArgument(xDepth >= 0, "xDepth must not be negative"); + Preconditions.checkArgument(yDepth >= 0, "yDepth must not be negative"); + Preconditions.checkArgument(zDepth >= 0, "zDepth must not be negative"); + this.xDepth = xDepth; + this.yDepth = yDepth; + this.zDepth = zDepth; + this.totalDepth = xDepth + yDepth + zDepth; + } + + public int xDepth() { + return this.xDepth; + } + + public int yDepth() { + return this.yDepth; + } + + public int zDepth() { + return this.zDepth; + } + + @Override + public Streamable all() { + // Copied from Minecraft's BlockPos#withinManhattan + return Streamable.of(() -> new AbstractIterator() { + private final MutableVector3i cursor = MutableVector3i.zero(); + private int currentDepth; + private int xMax; + private int yMax; + private int x; + private int y; + private boolean zMirror; + + @Override + protected MutableVector3i computeNext() { + if (this.zMirror) { + this.zMirror = false; + return this.cursor.set(this.x, this.y - 1, -this.cursor.z()); + } + + MutableVector3i vec; + for (vec = null; vec == null; this.y++) { + if (this.y > this.yMax) { + this.x++; + if (this.x > this.xMax) { + this.currentDepth++; + if (this.currentDepth > Manhattan3.this.totalDepth) { + return this.endOfData(); + } + + this.xMax = Math.min(Manhattan3.this.xDepth, this.currentDepth); + this.x = -this.xMax; + } + + this.yMax = Math.min(Manhattan3.this.yDepth, this.currentDepth - Math.abs(this.x)); + this.y = -this.yMax; + } + + final int z = this.currentDepth - Math.abs(this.x) - Math.abs(this.y); + if (z <= Manhattan3.this.zDepth) { + this.zMirror = z != 0; + vec = this.cursor.set(this.x, this.y, z); + } + } + + return vec; + } + }); + } + + @Override + public Streamable random(final RandomGenerator random) { + return Streamable.of(() -> { + final MutableVector3i cursor = MutableVector3i.zero(); + return Stream.generate(() -> { + int x = random.nextInt(this.xDepth + 1); + int y = random.nextInt(this.yDepth + 1); + int z = random.nextInt(this.zDepth + 1); + + if ((x + y + z) > this.totalDepth) { + x = this.xDepth - x; + y = this.yDepth - y; + z = this.zDepth - z; + } + + if (random.nextBoolean()) { + x = -x; + } + if (random.nextBoolean()) { + y = -y; + } + if (random.nextBoolean()) { + z = -z; + } + + return cursor.set(x, y, z); + }); + }); + } +} diff --git a/api/src/main/java/net/hellheim/spongetools/math/set/Shapes.java b/api/src/main/java/net/hellheim/spongetools/math/set/Shapes.java new file mode 100644 index 0000000..98da0c9 --- /dev/null +++ b/api/src/main/java/net/hellheim/spongetools/math/set/Shapes.java @@ -0,0 +1,34 @@ +package net.hellheim.spongetools.math.set; + +import org.spongepowered.math.vector.Vector2i; +import org.spongepowered.math.vector.Vector3i; + +public final class Shapes { + + public static Manhattan3 manhattan3(final Vector3i depth) { + return Shapes.manhattan3(depth.x(), depth.y(), depth.z()); + } + + public static Manhattan3 manhattan3(final int depth) { + return Shapes.manhattan3(depth, depth, depth); + } + + public static Manhattan3 manhattan3(final int xDepth, final int yDepth, final int zDepth) { + return new Manhattan3(xDepth, yDepth, zDepth); + } + + public static Manhattan2 manhattan2(final Vector2i depth) { + return Shapes.manhattan2(depth.x(), depth.y()); + } + + public static Manhattan2 manhattan2(final int depth) { + return Shapes.manhattan2(depth, depth); + } + + public static Manhattan2 manhattan2(final int xDepth, final int yDepth) { + return new Manhattan2(xDepth, yDepth); + } + + private Shapes() { + } +} diff --git a/src/main/java/net/hellheim/spongetools/menu/BasicMenu.java b/api/src/main/java/net/hellheim/spongetools/menu/BasicMenu.java similarity index 100% rename from src/main/java/net/hellheim/spongetools/menu/BasicMenu.java rename to api/src/main/java/net/hellheim/spongetools/menu/BasicMenu.java diff --git a/src/main/java/net/hellheim/spongetools/menu/DefaultedMenuType.java b/api/src/main/java/net/hellheim/spongetools/menu/DefaultedMenuType.java similarity index 100% rename from src/main/java/net/hellheim/spongetools/menu/DefaultedMenuType.java rename to api/src/main/java/net/hellheim/spongetools/menu/DefaultedMenuType.java diff --git a/src/main/java/net/hellheim/spongetools/menu/IDefaultedMenuType.java b/api/src/main/java/net/hellheim/spongetools/menu/IDefaultedMenuType.java similarity index 100% rename from src/main/java/net/hellheim/spongetools/menu/IDefaultedMenuType.java rename to api/src/main/java/net/hellheim/spongetools/menu/IDefaultedMenuType.java diff --git a/src/main/java/net/hellheim/spongetools/menu/IMenuType.java b/api/src/main/java/net/hellheim/spongetools/menu/IMenuType.java similarity index 100% rename from src/main/java/net/hellheim/spongetools/menu/IMenuType.java rename to api/src/main/java/net/hellheim/spongetools/menu/IMenuType.java diff --git a/src/main/java/net/hellheim/spongetools/menu/Menu.java b/api/src/main/java/net/hellheim/spongetools/menu/Menu.java similarity index 75% rename from src/main/java/net/hellheim/spongetools/menu/Menu.java rename to api/src/main/java/net/hellheim/spongetools/menu/Menu.java index b8a8ed4..51536d7 100644 --- a/src/main/java/net/hellheim/spongetools/menu/Menu.java +++ b/api/src/main/java/net/hellheim/spongetools/menu/Menu.java @@ -3,9 +3,6 @@ import java.util.ArrayDeque; import java.util.Collection; import java.util.Deque; -import java.util.HashMap; -import java.util.Map; -import java.util.NoSuchElementException; import java.util.Objects; import java.util.Optional; import java.util.UUID; @@ -22,6 +19,8 @@ import net.hellheim.spongetools.menu.pagination.Pagination; import net.hellheim.spongetools.menu.pagination.PaginationInitStage; import net.hellheim.spongetools.menu.pagination.PaginationType; +import net.hellheim.spongetools.object.TypedKey; +import net.hellheim.spongetools.object.TypedKeyMap; import net.hellheim.spongetools.proxy.solid.PluginProxy; import net.hellheim.spongetools.util.TaskUtil; import net.kyori.adventure.text.Component; @@ -29,7 +28,7 @@ /** * {@link Menu} is a wrapper for {@link InventoryMenu} with some useful additions:
* - History of visited {@link MenuType}s is stored to surf through them back and forth
- * - Data can be stored via {@link MenuKey}s
+ * - Data can be stored via {@link TypedKey}s
* - {@link Pagination} support
*
* Since menu creation requires {@link PluginContainer}, it's recommended to use {@link IMenuManager}.
@@ -39,7 +38,7 @@ * * @param The menu itself */ -public abstract class Menu> implements PluginProxy, Identifiable { +public abstract class Menu> implements PluginProxy, Identifiable, TypedKeyMap.Mutable.Proxy { private final PluginContainer plugin; private final UUID uniqueId; @@ -48,7 +47,7 @@ public abstract class Menu> implements PluginProxy, Identifiab private final Deque> history = new ArrayDeque<>(); private final UnmodifiableDeque> historyView = new UnmodifiableDeque<>(this.history); - private final Map, Object> data = new HashMap<>(); + private final TypedKeyMap.Mutable data = TypedKeyMap.create(); private @Nullable Pagination pagination = null; private IMenuType type; @@ -81,11 +80,6 @@ private M cast() { return (M) this; } - @SuppressWarnings("unchecked") - private T castValue(final MenuKey key, final Object value) { - return (T) value; - } - private void registerHandlers() { this.type.handlers().forEach(handler -> this.menu.registerHandler(handler.asDefaultHandler(this.cast()))); } @@ -156,121 +150,9 @@ public ViewableInventory inventory() { return this.menu.inventory(); } - - - /** - * Sets value of unknown type for the specified {@link MenuKey} for this {@link Menu}.
- * Useful when deleloper is sure about complex type compatability but IDE complains about it.
- *
- * It's strongly advised to not use this method whenever possible. - * - * @param key The {@link MenuKey key} - * @param value The value - */ - public void setUnsafe(final MenuKey key, final @Nullable Object value) { - this.data.put(key, value); - } - - /** - * Sets value for the specified {@link MenuKey} for this {@link Menu}.
- * Null value is supported. - * - * @param The type of key and value - * @param key The {@link MenuKey key} - * @param value The value - */ - public void set(final MenuKey key, final @Nullable T value) { - this.data.put(key, value); - } - - /** - * - * Sets value for the specified {@link MenuKey} for this {@link Menu} only if provided {@link Optional} is present. - * - * @param The type of key and value - * @param key The key - * @param optionalValue The optional value - */ - public void setIfPresent(final MenuKey key, final Optional optionalValue) { - optionalValue.ifPresent(value -> this.set(key, value)); - } - - /** - * Removes provided {@link MenuKey} for this {@link Menu}.
- *
- * Returns the previous value assosiated with key, or null if there was no value for key. - * - * @param The type of key - * @param key The key - * @return The previous value assosiated with key, or null if there was no value for key. - */ - public @Nullable T remove(final MenuKey key) { - return this.castValue(key, this.data.remove(key)); - } - - /** - * Returns whether this {@link Menu} contains value for provided {@link MenuKey}. - * - * @param key The key - * @return True if this menu contains value for provided key. - */ - public boolean has(final MenuKey key) { - return this.data.containsKey(key); - } - - /** - * Returns the value assosiated with provided {@link MenuKey} for this {@link Menu}, - * or {@link Optional#empty()} if there is no value for key. - * - * @param The type of key - * @param key The key - * @return The value, if available - */ - public Optional get(final MenuKey key) { - return this.data.containsKey(key) ? Optional.of(this.castValue(key, this.data.get(key))) : Optional.empty(); - } - - /** - * Returns the value assosiated with provided {@link MenuKey} for this {@link Menu}, - * or defaultValue if there is no value for key. - * - * @param The type of key and default value - * @param key The key - * @param defaultValue The default value - * @return The value, or default if not set - */ - public @Nullable T getOrElse(final MenuKey key, T defaultValue) { - return this.castValue(key, this.data.getOrDefault(key, defaultValue)); - } - - /** - * Returns the value assosiated with provided {@link MenuKey} for this {@link Menu}, - * or null if there is no value for key. - * - * @param The type of key - * @param key The key - * @return The value, or null if not set - */ - public @Nullable T getOrNull(final MenuKey key) { - return this.castValue(key, this.data.get(key)); - } - - /** - * Returns the value assosiated with provided {@link MenuKey} for this {@link Menu}.
- *
- * If there is no value for key, {@link NoSuchElementException} will be thrown. - * - * @param The type of key - * @param key The {@link MenuKey key} - * @return The value - * @throws NoSuchElementException If there is no value for provided key - */ - public @Nullable T require(final MenuKey key) { - if (!this.data.containsKey(key)) { - throw new NoSuchElementException("No value found for the specified menu key"); - } - - return this.castValue(key, this.data.get(key)); + @Override + public TypedKeyMap.Mutable context() { + return this.data; } diff --git a/src/main/java/net/hellheim/spongetools/menu/MenuAction.java b/api/src/main/java/net/hellheim/spongetools/menu/MenuAction.java similarity index 100% rename from src/main/java/net/hellheim/spongetools/menu/MenuAction.java rename to api/src/main/java/net/hellheim/spongetools/menu/MenuAction.java diff --git a/src/main/java/net/hellheim/spongetools/menu/MenuTypeProperties.java b/api/src/main/java/net/hellheim/spongetools/menu/MenuTypeProperties.java similarity index 100% rename from src/main/java/net/hellheim/spongetools/menu/MenuTypeProperties.java rename to api/src/main/java/net/hellheim/spongetools/menu/MenuTypeProperties.java diff --git a/src/main/java/net/hellheim/spongetools/menu/handler/MenuClickHandler.java b/api/src/main/java/net/hellheim/spongetools/menu/handler/MenuClickHandler.java similarity index 100% rename from src/main/java/net/hellheim/spongetools/menu/handler/MenuClickHandler.java rename to api/src/main/java/net/hellheim/spongetools/menu/handler/MenuClickHandler.java diff --git a/src/main/java/net/hellheim/spongetools/menu/handler/MenuCloseHandler.java b/api/src/main/java/net/hellheim/spongetools/menu/handler/MenuCloseHandler.java similarity index 100% rename from src/main/java/net/hellheim/spongetools/menu/handler/MenuCloseHandler.java rename to api/src/main/java/net/hellheim/spongetools/menu/handler/MenuCloseHandler.java diff --git a/src/main/java/net/hellheim/spongetools/menu/handler/MenuInventoryCallbackHandler.java b/api/src/main/java/net/hellheim/spongetools/menu/handler/MenuInventoryCallbackHandler.java similarity index 100% rename from src/main/java/net/hellheim/spongetools/menu/handler/MenuInventoryCallbackHandler.java rename to api/src/main/java/net/hellheim/spongetools/menu/handler/MenuInventoryCallbackHandler.java diff --git a/src/main/java/net/hellheim/spongetools/menu/handler/MenuKeySwapHandler.java b/api/src/main/java/net/hellheim/spongetools/menu/handler/MenuKeySwapHandler.java similarity index 100% rename from src/main/java/net/hellheim/spongetools/menu/handler/MenuKeySwapHandler.java rename to api/src/main/java/net/hellheim/spongetools/menu/handler/MenuKeySwapHandler.java diff --git a/src/main/java/net/hellheim/spongetools/menu/handler/MenuSlotChangeHandler.java b/api/src/main/java/net/hellheim/spongetools/menu/handler/MenuSlotChangeHandler.java similarity index 100% rename from src/main/java/net/hellheim/spongetools/menu/handler/MenuSlotChangeHandler.java rename to api/src/main/java/net/hellheim/spongetools/menu/handler/MenuSlotChangeHandler.java diff --git a/src/main/java/net/hellheim/spongetools/menu/handler/MenuSlotClickHandler.java b/api/src/main/java/net/hellheim/spongetools/menu/handler/MenuSlotClickHandler.java similarity index 100% rename from src/main/java/net/hellheim/spongetools/menu/handler/MenuSlotClickHandler.java rename to api/src/main/java/net/hellheim/spongetools/menu/handler/MenuSlotClickHandler.java diff --git a/src/main/java/net/hellheim/spongetools/menu/pagination/MultiplePagination.java b/api/src/main/java/net/hellheim/spongetools/menu/pagination/MultiplePagination.java similarity index 100% rename from src/main/java/net/hellheim/spongetools/menu/pagination/MultiplePagination.java rename to api/src/main/java/net/hellheim/spongetools/menu/pagination/MultiplePagination.java diff --git a/src/main/java/net/hellheim/spongetools/menu/pagination/PageContentProvider.java b/api/src/main/java/net/hellheim/spongetools/menu/pagination/PageContentProvider.java similarity index 100% rename from src/main/java/net/hellheim/spongetools/menu/pagination/PageContentProvider.java rename to api/src/main/java/net/hellheim/spongetools/menu/pagination/PageContentProvider.java diff --git a/src/main/java/net/hellheim/spongetools/menu/pagination/Pagination.java b/api/src/main/java/net/hellheim/spongetools/menu/pagination/Pagination.java similarity index 100% rename from src/main/java/net/hellheim/spongetools/menu/pagination/Pagination.java rename to api/src/main/java/net/hellheim/spongetools/menu/pagination/Pagination.java diff --git a/src/main/java/net/hellheim/spongetools/menu/pagination/PaginationButton.java b/api/src/main/java/net/hellheim/spongetools/menu/pagination/PaginationButton.java similarity index 100% rename from src/main/java/net/hellheim/spongetools/menu/pagination/PaginationButton.java rename to api/src/main/java/net/hellheim/spongetools/menu/pagination/PaginationButton.java diff --git a/src/main/java/net/hellheim/spongetools/menu/pagination/PaginationConfig.java b/api/src/main/java/net/hellheim/spongetools/menu/pagination/PaginationConfig.java similarity index 100% rename from src/main/java/net/hellheim/spongetools/menu/pagination/PaginationConfig.java rename to api/src/main/java/net/hellheim/spongetools/menu/pagination/PaginationConfig.java diff --git a/src/main/java/net/hellheim/spongetools/menu/pagination/PaginationInitStage.java b/api/src/main/java/net/hellheim/spongetools/menu/pagination/PaginationInitStage.java similarity index 100% rename from src/main/java/net/hellheim/spongetools/menu/pagination/PaginationInitStage.java rename to api/src/main/java/net/hellheim/spongetools/menu/pagination/PaginationInitStage.java diff --git a/src/main/java/net/hellheim/spongetools/menu/pagination/PaginationType.java b/api/src/main/java/net/hellheim/spongetools/menu/pagination/PaginationType.java similarity index 100% rename from src/main/java/net/hellheim/spongetools/menu/pagination/PaginationType.java rename to api/src/main/java/net/hellheim/spongetools/menu/pagination/PaginationType.java diff --git a/src/main/java/net/hellheim/spongetools/menu/pagination/SinglePagination.java b/api/src/main/java/net/hellheim/spongetools/menu/pagination/SinglePagination.java similarity index 100% rename from src/main/java/net/hellheim/spongetools/menu/pagination/SinglePagination.java rename to api/src/main/java/net/hellheim/spongetools/menu/pagination/SinglePagination.java diff --git a/api/src/main/java/net/hellheim/spongetools/object/AttributeModifierTemplate.java b/api/src/main/java/net/hellheim/spongetools/object/AttributeModifierTemplate.java new file mode 100644 index 0000000..b8b1d5d --- /dev/null +++ b/api/src/main/java/net/hellheim/spongetools/object/AttributeModifierTemplate.java @@ -0,0 +1,40 @@ +package net.hellheim.spongetools.object; + +import java.util.Objects; +import java.util.function.Supplier; + +import org.spongepowered.api.ResourceKey; +import org.spongepowered.api.ResourceKeyed; +import org.spongepowered.api.entity.attribute.AttributeModifier; +import org.spongepowered.api.entity.attribute.AttributeOperation; + +public record AttributeModifierTemplate(ResourceKey key, AttributeOperation operation, double amountPerLevel) implements ResourceKeyed { + + public static AttributeModifierTemplate of( + final ResourceKey key, final Supplier operation, final double amountPerLevel + ) { + return AttributeModifierTemplate.of(key, Objects.requireNonNull(operation, "operation").get(), amountPerLevel); + } + + public static AttributeModifierTemplate of( + final ResourceKey key, final AttributeOperation operation, final double amountPerLevel + ) { + return new AttributeModifierTemplate(key, operation, amountPerLevel); + } + + public AttributeModifierTemplate( + final ResourceKey key, final AttributeOperation operation, final double amountPerLevel + ) { + this.key = Objects.requireNonNull(key, "key"); + this.operation = Objects.requireNonNull(operation, "operation"); + this.amountPerLevel = amountPerLevel; + } + + public AttributeModifier build(final int amplifier) { + return AttributeModifier.builder() + .key(this.key) + .operation(this.operation) + .amount(this.amountPerLevel * (amplifier + 1)) + .build(); + } +} diff --git a/src/main/java/net/hellheim/spongetools/object/Box.java b/api/src/main/java/net/hellheim/spongetools/object/Box.java similarity index 100% rename from src/main/java/net/hellheim/spongetools/object/Box.java rename to api/src/main/java/net/hellheim/spongetools/object/Box.java diff --git a/src/main/java/net/hellheim/spongetools/object/CachedSupplier.java b/api/src/main/java/net/hellheim/spongetools/object/CachedSupplier.java similarity index 100% rename from src/main/java/net/hellheim/spongetools/object/CachedSupplier.java rename to api/src/main/java/net/hellheim/spongetools/object/CachedSupplier.java diff --git a/api/src/main/java/net/hellheim/spongetools/object/CodecDataSerializable.java b/api/src/main/java/net/hellheim/spongetools/object/CodecDataSerializable.java new file mode 100644 index 0000000..9d7530e --- /dev/null +++ b/api/src/main/java/net/hellheim/spongetools/object/CodecDataSerializable.java @@ -0,0 +1,22 @@ +package net.hellheim.spongetools.object; + +import org.spongepowered.api.data.persistence.DataContainer; +import org.spongepowered.api.data.persistence.DataSerializable; + +import net.hellheim.spongetools.codec.list.DataCodecs; +import net.hellheim.spongetools.proxy.solid.codec.CodecProxy; + +public interface CodecDataSerializable extends DataSerializable, CodecProxy { + + @Override + default int contentVersion() { + return 0; + } + + @Override + default DataContainer toContainer() { + @SuppressWarnings("unchecked") + final T $this = (T) this; + return DataCodecs.toContainer(this.codec(), $this); + } +} diff --git a/api/src/main/java/net/hellheim/spongetools/object/DataOperator.java b/api/src/main/java/net/hellheim/spongetools/object/DataOperator.java new file mode 100644 index 0000000..f8d0a87 --- /dev/null +++ b/api/src/main/java/net/hellheim/spongetools/object/DataOperator.java @@ -0,0 +1,222 @@ +package net.hellheim.spongetools.object; + +import java.util.Arrays; +import java.util.Collection; +import java.util.function.Supplier; +import java.util.stream.Stream; + +import org.spongepowered.api.data.Key; +import org.spongepowered.api.data.value.CollectionValue; +import org.spongepowered.api.data.value.Value; +import org.spongepowered.api.data.value.ValueContainer; + +import com.google.common.collect.Streams; + +@SuppressWarnings("unchecked") +public interface DataOperator> { + + /** + * Adds all the {@link Value}s from the {@link ValueContainer} to the builder. + * + * @param container The container to add + * @return This builder, for chaining + */ + default B supplyFrom(final Supplier container) { + return this.addFrom(container.get()); + } + + /** + * Adds all the {@link Value}s from the {@link ValueContainer} to the builder. + * + * @param container The container to add + * @return This builder, for chaining + */ + default B addFrom(final ValueContainer container) { + return this.addAll(container.getValues()); + } + + /** + * Adds all the {@link Value}s to the builder. + * + * @param values The values to add + * @return This builder, for chaining + */ + default B supplyAll(final Supplier>> values) { + return this.addAll(values.get()); + } + + /** + * Adds all the {@link Value}s to the builder. + * + * @param values The values to add + * @return This builder, for chaining + */ + default B addAll(final Iterable> values) { + values.forEach(this::add); + return (B) this; + } + + /** + * Adds the given {@link Value} to the builder. + * + * @param value The value to add + * @return This builder, for chaining + */ + default B supply(final Supplier> value) { + return this.add(value.get()); + } + + /** + * Adds the given {@link Value} to the builder. + * + * @param value The value to add + * @return This builder, for chaining + */ + default B add(final Value value) { + return (B) this.add(value.key(), value.get()); + } + + /** + * Adds the given {@link Key} with the given value. + * + * @param key The key to assign the value with + * @param value The value to assign with the key + * @param The type of the value + * @return This builder, for chaining + */ + default B supply(final Supplier>> key, final Supplier value) { + return this.add(key.get(), value.get()); + } + + /** + * Adds the given {@link Key} with the given value. + * + * @param key The key to assign the value with + * @param value The value to assign with the key + * @param The type of the value + * @return This builder, for chaining + */ + default B supply(final Key> key, final Supplier value) { + return this.add(key, value.get()); + } + + /** + * Adds the given {@link Key} with the given value. + * + * @param key The key to assign the value with + * @param value The value to assign with the key + * @param The type of the value + * @return This builder, for chaining + */ + default B add(final Supplier>> key, final V value) { + return this.add(key.get(), value); + } + + /** + * Adds the given {@link Key} with the given value. + * + * @param key The key to assign the value with + * @param value The value to assign with the key + * @param The type of the value + * @return This builder, for chaining + */ + B add(Key> key, V value); + + default > B supplyAll(final Supplier>> key, final Supplier... elements) { + return this.addAll(key, Arrays.stream(elements).map(Supplier::get)); + } + + default > B supplyAll(final Supplier>> key, final Supplier> elements) { + return this.addAll(key, elements.get()); + } + + default > B supplyAll(final Key> key, final Supplier... elements) { + return this.addAll(key, Arrays.stream(elements).map(Supplier::get)); + } + + default > B supplyAll(final Key> key, final Supplier> elements) { + return this.addAll(key, elements.get()); + } + + default > B addAll(final Supplier>> key, final V... elements) { + return this.addAll(key.get(), elements); + } + + default > B addAll(final Supplier>> key, final Iterable elements) { + return this.addAll(key.get(), elements); + } + + default > B addAll(final Supplier>> key, final Stream elements) { + return this.addAll(key.get(), elements); + } + + default > B addAll(final Key> key, final V... elements) { + return this.addAll(key, Arrays.stream(elements)); + } + + default > B addAll(final Key> key, final Iterable elements) { + return this.addAll(key, Streams.stream(elements)); + } + + > B addAll(final Key> key, final Stream elements); + + default > B supplySingle(final Supplier>> key, final Supplier element) { + return this.addSingle(key.get(), element.get()); + } + + default > B supplySingle(final Key> key, final Supplier element) { + return this.addSingle(key, element.get()); + } + + default > B addSingle(final Supplier>> key, final V element) { + return this.addSingle(key.get(), element); + } + + /** + * Adds the given element to currently present for the given {@link Key}. + * TODO doc + * + * @param key The key to assign the element with + * @param element The element to assign with the key + * @param The type of the element + * @return The builder, for chaining + */ + > B addSingle(Key> key, V element); + + B remove(Key key); + + B reset(); + + public interface Proxy> extends DataOperator { + + DataOperator getAsData(); + + @Override + default B add(final Key> key, final V value) { + this.getAsData().add(key, value); + return (B) this; + } + + @Override + default > B addAll( + final Key> key, final Stream elements + ) { + this.getAsData().addAll(key, elements); + return (B) this; + } + + @Override + default > B addSingle( + final Key> key, final V element + ) { + this.getAsData().addSingle(key, element); + return (B) this; + } + + @Override + default B remove(final Key key) { + this.getAsData().remove(key); + return (B) this; + } + } +} diff --git a/api/src/main/java/net/hellheim/spongetools/object/DeferredValueContainer.java b/api/src/main/java/net/hellheim/spongetools/object/DeferredValueContainer.java new file mode 100644 index 0000000..cf4435b --- /dev/null +++ b/api/src/main/java/net/hellheim/spongetools/object/DeferredValueContainer.java @@ -0,0 +1,66 @@ +package net.hellheim.spongetools.object; + +import java.util.Objects; +import java.util.function.Consumer; +import java.util.function.Function; +import java.util.function.Supplier; + +import org.spongepowered.api.ResourceKey; +import org.spongepowered.api.data.Key; +import org.spongepowered.api.data.value.ValueContainer; + +import com.google.common.base.Suppliers; +import com.mojang.serialization.Codec; + +import net.hellheim.spongetools.codec.list.DataCodecs; +import net.hellheim.spongetools.proxy.solid.data.ValueContainerProxy; + +public class DeferredValueContainer implements ValueContainerProxy { + + public static final DeferredValueContainer EMPTY = DeferredValueContainer.of($ -> {}); + + private final Consumer configurator; + private final Supplier data; + + public DeferredValueContainer(final Consumer configurator) { + this.configurator = Objects.requireNonNull(configurator, "configurator"); + this.data = Suppliers.memoize(() -> { + final ValueSetBuilder builder = new ValueSetBuilder(); + configurator.accept(builder); + return builder.asImmutableManipulator(); + }); + } + + public static DeferredValueContainer of(final Consumer configurator) { + return new DeferredValueContainer(configurator); + } + + public static Codec codec(final ValueContainer keyLookupProvider) { + return DeferredValueContainer.codec(DataCodecs.keyLookup(keyLookupProvider)); + } + + public static Codec codec(final Function> keyLookup) { + return DataCodecs.valueSet(keyLookup).xmap( + set -> DeferredValueContainer.of(builder -> builder.addAll(set)), + data -> data.getAsData().getValues()); + } + + @Override + public ValueContainer getAsData() { + return this.data.get(); + } + + public Consumer configurator() { + return this.configurator; + } + + public DeferredValueContainer withBefore(final Consumer before) { + Objects.requireNonNull(before, "before"); + return DeferredValueContainer.of(before.andThen(this.configurator)); + } + + public DeferredValueContainer withAfter(final Consumer after) { + Objects.requireNonNull(after, "after"); + return DeferredValueContainer.of(this.configurator.andThen(after)); + } +} diff --git a/src/main/java/net/hellheim/spongetools/object/InventoryDecorator.java b/api/src/main/java/net/hellheim/spongetools/object/InventoryDecorator.java similarity index 100% rename from src/main/java/net/hellheim/spongetools/object/InventoryDecorator.java rename to api/src/main/java/net/hellheim/spongetools/object/InventoryDecorator.java diff --git a/src/main/java/net/hellheim/spongetools/object/InventoryDecoratorImpl.java b/api/src/main/java/net/hellheim/spongetools/object/InventoryDecoratorImpl.java similarity index 100% rename from src/main/java/net/hellheim/spongetools/object/InventoryDecoratorImpl.java rename to api/src/main/java/net/hellheim/spongetools/object/InventoryDecoratorImpl.java diff --git a/src/main/java/net/hellheim/spongetools/object/InventoryOperator.java b/api/src/main/java/net/hellheim/spongetools/object/InventoryOperator.java similarity index 100% rename from src/main/java/net/hellheim/spongetools/object/InventoryOperator.java rename to api/src/main/java/net/hellheim/spongetools/object/InventoryOperator.java diff --git a/src/main/java/net/hellheim/spongetools/object/InventoryOperatorImpl.java b/api/src/main/java/net/hellheim/spongetools/object/InventoryOperatorImpl.java similarity index 100% rename from src/main/java/net/hellheim/spongetools/object/InventoryOperatorImpl.java rename to api/src/main/java/net/hellheim/spongetools/object/InventoryOperatorImpl.java diff --git a/src/main/java/net/hellheim/spongetools/object/ItemBuilder.java b/api/src/main/java/net/hellheim/spongetools/object/ItemBuilder.java similarity index 95% rename from src/main/java/net/hellheim/spongetools/object/ItemBuilder.java rename to api/src/main/java/net/hellheim/spongetools/object/ItemBuilder.java index 6784027..e000263 100644 --- a/src/main/java/net/hellheim/spongetools/object/ItemBuilder.java +++ b/api/src/main/java/net/hellheim/spongetools/object/ItemBuilder.java @@ -14,6 +14,7 @@ import org.spongepowered.api.item.inventory.ItemStackLike; import org.spongepowered.api.item.inventory.ItemStackSnapshot; +import net.hellheim.spongetools.custom.type.item.LoreApplicator; import net.hellheim.spongetools.proxy.solid.EnchantmentProxy; import net.hellheim.spongetools.proxy.solid.EnchantmentTypeProxy; import net.hellheim.spongetools.proxy.solid.data.TransitiveMutableDataHolderProxy; @@ -92,7 +93,7 @@ public ItemStackSnapshot getAsItemStackSnapshot() { @Override public ItemStackLike getAsItemStackLike() { - return this.getAsItemStack(); + return this.stack; } @Override @@ -101,7 +102,7 @@ public ItemBuilder itemBuilder() { } @Override - public DataHolder.Mutable getAsDataHolder() { + public DataHolder.Mutable getAsData() { return this.stack; } @@ -114,7 +115,7 @@ private ItemBuilder hideFlags() { this.stack.offer(Keys.HIDE_ATTRIBUTES, true); this.stack.offer(Keys.HIDE_CAN_DESTROY, true); this.stack.offer(Keys.HIDE_CAN_PLACE, true); - //this.stack.offer(Keys.HIDE_ENCHANTMENTS, true); + this.stack.offer(Keys.HIDE_ENCHANTMENTS, true); this.stack.offer(Keys.HIDE_UNBREAKABLE, true); this.stack.offer(Keys.HIDE_MISCELLANEOUS, true); return this; @@ -152,6 +153,16 @@ public ItemBuilder displayName(ComponentLike name) { + public ItemBuilder lore(final LoreApplicator applicator, final TypedKeyMap context) { + applicator.apply(this.stack, context); + return this; + } + + public ItemBuilder lore(final LoreApplicator applicator) { + applicator.apply(this.stack); + return this; + } + public ItemBuilder loreMini(String... lore) { return this.lore(CompUtil.fromMinis(lore)); } diff --git a/src/main/java/net/hellheim/spongetools/object/MutableBigDec.java b/api/src/main/java/net/hellheim/spongetools/object/MutableBigDec.java similarity index 100% rename from src/main/java/net/hellheim/spongetools/object/MutableBigDec.java rename to api/src/main/java/net/hellheim/spongetools/object/MutableBigDec.java diff --git a/src/main/java/net/hellheim/spongetools/object/MutableBigInt.java b/api/src/main/java/net/hellheim/spongetools/object/MutableBigInt.java similarity index 100% rename from src/main/java/net/hellheim/spongetools/object/MutableBigInt.java rename to api/src/main/java/net/hellheim/spongetools/object/MutableBigInt.java diff --git a/src/main/java/net/hellheim/spongetools/object/MutableBigNumber.java b/api/src/main/java/net/hellheim/spongetools/object/MutableBigNumber.java similarity index 100% rename from src/main/java/net/hellheim/spongetools/object/MutableBigNumber.java rename to api/src/main/java/net/hellheim/spongetools/object/MutableBigNumber.java diff --git a/api/src/main/java/net/hellheim/spongetools/object/OptionalRotation.java b/api/src/main/java/net/hellheim/spongetools/object/OptionalRotation.java new file mode 100644 index 0000000..71f58fc --- /dev/null +++ b/api/src/main/java/net/hellheim/spongetools/object/OptionalRotation.java @@ -0,0 +1,60 @@ +package net.hellheim.spongetools.object; + +import java.util.Objects; +import java.util.OptionalDouble; + +import org.spongepowered.api.entity.Entity; +import org.spongepowered.math.vector.Vector3d; + +import net.hellheim.spongetools.math.optional.vector.OptionalVector3d; + +public record OptionalRotation(OptionalVector3d rotation) { + + public OptionalRotation(final OptionalVector3d rotation) { + this.rotation = Objects.requireNonNull(rotation, "rotation"); + } + + public static OptionalRotation of(final OptionalVector3d rotation) { + return new OptionalRotation(rotation); + } + + public static OptionalRotation ofPitch(final double pitch) { + return OptionalRotation.of(OptionalVector3d.x(pitch)); + } + + public static OptionalRotation ofYaw(final double yaw) { + return OptionalRotation.of(OptionalVector3d.y(yaw)); + } + + public static OptionalRotation of(final double pitch, final double yaw) { + return OptionalRotation.of(OptionalVector3d.xy(pitch, yaw)); + } + + public static OptionalRotation of(final double pitch, final double yaw, final double roll) { + return OptionalRotation.of(OptionalVector3d.of(pitch, yaw, roll)); + } + + public Vector3d transform(final Vector3d rotation) { + return this.rotation.set(rotation); + } + + public Vector3d transform(final Entity entity) { + return this.transform(entity.rotation()); + } + + public void apply(final Entity entity) { + entity.setRotation(this.transform(entity)); + } + + public OptionalDouble pitch() { + return this.rotation.x(); + } + + public OptionalDouble yaw() { + return this.rotation.y(); + } + + public OptionalDouble roll() { + return this.rotation.z(); + } +} diff --git a/src/main/java/net/hellheim/spongetools/object/RequestedSupplier.java b/api/src/main/java/net/hellheim/spongetools/object/RequestedSupplier.java similarity index 100% rename from src/main/java/net/hellheim/spongetools/object/RequestedSupplier.java rename to api/src/main/java/net/hellheim/spongetools/object/RequestedSupplier.java diff --git a/src/main/java/net/hellheim/spongetools/object/SpongeCompletableFuture.java b/api/src/main/java/net/hellheim/spongetools/object/SpongeCompletableFuture.java similarity index 100% rename from src/main/java/net/hellheim/spongetools/object/SpongeCompletableFuture.java rename to api/src/main/java/net/hellheim/spongetools/object/SpongeCompletableFuture.java diff --git a/api/src/main/java/net/hellheim/spongetools/object/Streamable.java b/api/src/main/java/net/hellheim/spongetools/object/Streamable.java new file mode 100644 index 0000000..b485cd4 --- /dev/null +++ b/api/src/main/java/net/hellheim/spongetools/object/Streamable.java @@ -0,0 +1,324 @@ +package net.hellheim.spongetools.object; + +import java.util.Comparator; +import java.util.Iterator; +import java.util.List; +import java.util.Objects; +import java.util.Optional; +import java.util.Spliterator; +import java.util.function.BiConsumer; +import java.util.function.BiFunction; +import java.util.function.BinaryOperator; +import java.util.function.Consumer; +import java.util.function.DoubleConsumer; +import java.util.function.Function; +import java.util.function.IntConsumer; +import java.util.function.IntFunction; +import java.util.function.LongConsumer; +import java.util.function.Predicate; +import java.util.function.Supplier; +import java.util.function.ToDoubleFunction; +import java.util.function.ToIntFunction; +import java.util.function.ToLongFunction; +import java.util.stream.Collector; +import java.util.stream.DoubleStream; +import java.util.stream.IntStream; +import java.util.stream.LongStream; +import java.util.stream.Stream; + +import com.google.common.collect.Streams; + +public interface Streamable extends Iterable, Stream { + + Stream stream(); + + @Override + Iterator iterator(); + + @Override + Spliterator spliterator(); + + @Override + void forEach(Consumer action); + + @Override + default boolean isParallel() { + return this.stream().isParallel(); + } + + @Override + default Streamable sequential() { + return of(() -> this.stream().sequential()); + } + + @Override + default Streamable parallel() { + return of(() -> this.stream().parallel()); + } + + @Override + default Streamable unordered() { + return of(() -> this.stream().unordered()); + } + + @Override + default Streamable onClose(final Runnable closeHandler) { + return of(() -> this.stream().onClose(closeHandler)); + } + + @Override + default void close() { + } + + @Override + default Streamable filter(final Predicate predicate) { + return of(() -> this.stream().filter(predicate)); + } + + @Override + default Streamable map(final Function mapper) { + return of(() -> this.stream().map(mapper)); + } + + @Override + default IntStream mapToInt(final ToIntFunction mapper) { + return this.stream().mapToInt(mapper); + } + + @Override + default LongStream mapToLong(final ToLongFunction mapper) { + return this.stream().mapToLong(mapper); + } + + @Override + default DoubleStream mapToDouble(final ToDoubleFunction mapper) { + return this.stream().mapToDouble(mapper); + } + + @Override + default Streamable flatMap(final Function> mapper) { + return of(() -> this.stream().flatMap(mapper)); + } + + @Override + default IntStream flatMapToInt(final Function mapper) { + return this.stream().flatMapToInt(mapper); + } + + @Override + default LongStream flatMapToLong(final Function mapper) { + return this.stream().flatMapToLong(mapper); + } + + @Override + default DoubleStream flatMapToDouble(final Function mapper) { + return this.stream().flatMapToDouble(mapper); + } + + @Override + default Streamable mapMulti(final BiConsumer> mapper) { + return of(() -> this.stream().mapMulti(mapper)); + } + + @Override + default IntStream mapMultiToInt(final BiConsumer mapper) { + return this.stream().mapMultiToInt(mapper); + } + + @Override + default LongStream mapMultiToLong(final BiConsumer mapper) { + return this.stream().mapMultiToLong(mapper); + } + + @Override + default DoubleStream mapMultiToDouble(final BiConsumer mapper) { + return this.stream().mapMultiToDouble(mapper); + } + + @Override + default Streamable distinct() { + return of(() -> this.stream().distinct()); + } + + @Override + default Streamable sorted() { + return of(() -> this.stream().sorted()); + } + + @Override + default Streamable sorted(final Comparator comparator) { + return of(() -> this.stream().sorted(comparator)); + } + + @Override + default Streamable peek(final Consumer action) { + return of(() -> this.stream().peek(action)); + } + + @Override + default Streamable limit(final long maxSize) { + return of(() -> this.stream().limit(maxSize)); + } + + @Override + default Streamable skip(final long n) { + return of(() -> this.stream().skip(n)); + } + + @Override + default Streamable takeWhile(final Predicate predicate) { + return of(() -> this.stream().takeWhile(predicate)); + } + + @Override + default Streamable dropWhile(final Predicate predicate) { + return of(() -> this.stream().dropWhile(predicate)); + } + + @Override + default void forEachOrdered(final Consumer action) { + this.stream().forEachOrdered(action); + } + + @Override + default Object[] toArray() { + return this.stream().toArray(); + } + + @Override + default
A[] toArray(final IntFunction generator) { + return this.stream().toArray(generator); + } + + @Override + default T reduce(final T identity, final BinaryOperator accumulator) { + return this.stream().reduce(identity, accumulator); + } + + @Override + default Optional reduce(final BinaryOperator accumulator) { + return this.stream().reduce(accumulator); + } + + @Override + default U reduce( + final U identity, + final BiFunction accumulator, + final BinaryOperator combiner + ) { + return this.stream().reduce(identity, accumulator, combiner); + } + + @Override + default R collect( + final Supplier supplier, + final BiConsumer accumulator, + final BiConsumer combiner + ) { + return this.stream().collect(supplier, accumulator, combiner); + } + + @Override + default R collect(final Collector collector) { + return this.stream().collect(collector); + } + + @Override + default List toList() { + return this.stream().toList(); + } + + @Override + default Optional min(final Comparator comparator) { + return this.stream().min(comparator); + } + + @Override + default Optional max(Comparator comparator) { + return this.stream().max(comparator); + } + + @Override + default long count() { + return this.stream().count(); + } + + @Override + default boolean anyMatch(final Predicate predicate) { + return this.stream().anyMatch(predicate); + } + + @Override + default boolean allMatch(final Predicate predicate) { + return this.stream().allMatch(predicate); + } + + @Override + default boolean noneMatch(final Predicate predicate) { + return this.stream().noneMatch(predicate); + } + + @Override + default Optional findFirst() { + return this.stream().findFirst(); + } + + @Override + default Optional findAny() { + return this.stream().findAny(); + } + + static Streamable of(final Supplier> streamSupplier) { + Objects.requireNonNull(streamSupplier, "streamSupplier"); + return new Streamable() { + @Override + public Stream stream() { + return streamSupplier.get(); + } + + @Override + public Iterator iterator() { + return this.stream().iterator(); + } + + @Override + public Spliterator spliterator() { + return this.stream().spliterator(); + } + + @Override + public void forEach(final Consumer action) { + this.stream().forEach(action); + } + }; + } + + static Streamable of(final Iterable iterable) { + Objects.requireNonNull(iterable, "iterable"); + if (iterable instanceof final Streamable streamable) { + return streamable; + } + + return new Streamable() { + @Override + public Stream stream() { + return Streams.stream(iterable); + } + + @Override + public Iterator iterator() { + return iterable.iterator(); + } + + @Override + public Spliterator spliterator() { + return iterable.spliterator(); + } + + @Override + public void forEach(final Consumer action) { + iterable.forEach(action); + } + }; + } +} diff --git a/api/src/main/java/net/hellheim/spongetools/object/TypedKey.java b/api/src/main/java/net/hellheim/spongetools/object/TypedKey.java new file mode 100644 index 0000000..4a24322 --- /dev/null +++ b/api/src/main/java/net/hellheim/spongetools/object/TypedKey.java @@ -0,0 +1,49 @@ +package net.hellheim.spongetools.object; + +import java.lang.reflect.Type; +import java.util.Objects; + +import org.spongepowered.api.ResourceKey; +import org.spongepowered.api.ResourceKeyed; + +import io.leangen.geantyref.GenericTypeReflector; +import io.leangen.geantyref.TypeToken; + +public record TypedKey(ResourceKey key, Type type) implements ResourceKeyed, Comparable> { + + public TypedKey(final ResourceKey key, final Type type) { + this.key = Objects.requireNonNull(key, "key"); + this.type = Objects.requireNonNull(type, "type"); + } + + public static TypedKey of(final ResourceKey key, final Class clazz) { + return new TypedKey<>(key, clazz); + } + + public static TypedKey of(final ResourceKey key, final TypeToken token) { + return new TypedKey<>(key, token.getType()); + } + + public boolean isInstance(final Object value) { + return value != null && GenericTypeReflector.erase(this.type).isInstance(value); + } + + public T cast(final Object value) { + @SuppressWarnings("unchecked") + final T casted = (T) value; + return casted; + } + + @Override + public int compareTo(final TypedKey key) { + return this.key().compareTo(key.key()); + } + + @Override + public final String toString() { + return "TypedKey{" + + "key=" + this.key.toString() + + ", type=" + this.type.toString() + + '}'; + } +} diff --git a/api/src/main/java/net/hellheim/spongetools/object/TypedKeyMap.java b/api/src/main/java/net/hellheim/spongetools/object/TypedKeyMap.java new file mode 100644 index 0000000..722d411 --- /dev/null +++ b/api/src/main/java/net/hellheim/spongetools/object/TypedKeyMap.java @@ -0,0 +1,421 @@ +package net.hellheim.spongetools.object; + +import java.util.HashMap; +import java.util.Map; +import java.util.NoSuchElementException; +import java.util.Objects; +import java.util.Optional; +import java.util.Set; +import java.util.StringJoiner; + +import org.checkerframework.checker.nullness.qual.Nullable; + +public interface TypedKeyMap { + + static TypedKeyMap empty() { + return TypedKeyMap.Impl.EMPTY; + } + + static TypedKeyMap.Impl.Mutable create() { + return new TypedKeyMap.Impl.Mutable(); + } + + Set> typedKeySet(); + + /** + * Returns whether this {@link TypedKeyMap} contains value for provided {@link TypedKey}. + * + * @param key The key + * @return True if this {@link TypedKeyMap} contains value for provided key + */ + boolean has(TypedKey key); + + /** + * Returns the value assosiated with provided {@link TypedKey} for this {@link TypedKeyMap}, + * or {@link Optional#empty()} if there is no value for key. + * + * @param The type of key + * @param key The key + * @return The value, if available + */ + Optional get(TypedKey key); + + /** + * Returns the value assosiated with provided {@link TypedKey} for this {@link TypedKeyMap}.
+ *
+ * If there is no value for key, {@link NoSuchElementException} will be thrown. + * + * @param The type of key + * @param key The key + * @return The value + * @throws NoSuchElementException If there is no value for provided key + */ + T require(TypedKey key); + + /** + * Returns the value assosiated with provided {@link TypedKey} for this {@link TypedKeyMap}, + * or defaultValue if there is no value for key. + * + * @param The type of key and default value + * @param key The key + * @param defaultValue The default value + * @return The value, or default if not set + */ + T getOrElse(TypedKey key, T defaultValue); + + /** + * Returns the value assosiated with provided {@link TypedKey} for this {@link TypedKeyMap}, + * or null if there is no value for key. + * + * @param The type of key + * @param key The key + * @return The value, or null if not set + */ + T getOrNull(TypedKey key); + + interface Mutable extends TypedKeyMap { + + /** + * Sets value for the provided {@link TypedKey} for this {@link TypedKeyMap}.
+ * Returns the previously assosiated value, or {@link Optional#empty()} if there was no value for key. + * + * @param The type of key and value + * @param key The key + * @param value The value + * @return Previously assotiated value for key, if present + */ + Optional set(TypedKey key, T value); + + /** + * Removes provided {@link TypedKey} for this {@link TypedKeyMap}.
+ * Returns the previously assosiated value, or {@link Optional#empty()} if there was no value for key. + * + * @param The type of key + * @param key The key + * @return Previously assotiated value for key, if present + */ + Optional remove(TypedKey key); + + /** + * Sets value for the specified {@link TypedKey} for this {@link TypedKeyMap} + * only if provided {@link Optional} is present. + * + * @param The type of key and value + * @param key The key + * @param optValue The optional value + */ + default void trySet(final TypedKey key, final Optional optValue) { + optValue.ifPresent(value -> this.set(key, value)); + } + + /** + * If provided {@link Optional} is present, sets its value for the specified {@link TypedKey}.
+ * If the {@link Optional} is empty, removes provided {@link TypedKey} for this {@link TypedKeyMap}. + * + * @param The type of key and value + * @param key The key + * @param optValue The optionalValue + */ + default void apply(final TypedKey key, final Optional optValue) { + optValue.ifPresentOrElse(value -> this.set(key, value), () -> this.remove(key)); + } + + interface Proxy extends TypedKeyMap.Mutable, TypedKeyMap.Proxy { + + @Override + TypedKeyMap.Mutable context(); + + @Override + default Optional set(final TypedKey key, final T value) { + return this.context().set(key, value); + } + + @Override + default Optional remove(final TypedKey key) { + return this.context().remove(key); + } + + @Override + default void trySet(final TypedKey key, final Optional optValue) { + this.context().trySet(key, optValue); + } + + @Override + default void apply(final TypedKey key, final Optional optValue) { + this.context().apply(key, optValue); + } + } + } + + @SuppressWarnings("unchecked") + interface Operator> extends TypedKeyMap { + + /** + * Sets value for the provided {@link TypedKey} for this {@link TypedKeyMap}. + * + * @param The type of key and value + * @param key The key + * @param value The value + * @return This operator, for chaining + */ + B set(TypedKey key, T value); + + /** + * Removes provided {@link TypedKey} for this {@link TypedKeyMap}.
+ * + * @param The type of key + * @param key The key + * @return This operator, for chaining + */ + B remove(TypedKey key); + + /** + * Sets value for the specified {@link TypedKey} for this {@link TypedKeyMap} + * only if provided {@link Optional} is present. + * + * @param The type of key and value + * @param key The key + * @param optValue The optional value + * @return This operator, for chaining + */ + B trySet(TypedKey key, Optional optValue); + + /** + * If provided {@link Optional} is present, sets its value for the specified {@link TypedKey}.
+ * If the {@link Optional} is empty, removes provided {@link TypedKey} for this {@link TypedKeyMap}. + * + * @param The type of key and value + * @param key The key + * @param optValue The optionalValue + * @return This operator, for chaining + */ + B apply(TypedKey key, Optional optValue); + + interface MutableProxy> extends TypedKeyMap.Operator, TypedKeyMap.Proxy { + + @Override + TypedKeyMap.Mutable context(); + + @Override + default B set(final TypedKey key, final T value) { + this.context().set(key, value); + return (B) this; + } + + @Override + default B remove(final TypedKey key) { + this.context().remove(key); + return (B) this; + } + + @Override + default B trySet(final TypedKey key, final Optional optValue) { + this.context().trySet(key, optValue); + return (B) this; + } + + @Override + default B apply(final TypedKey key, final Optional optValue) { + this.context().apply(key, optValue); + return (B) this; + } + } + + interface OperatorProxy> extends TypedKeyMap.Operator, TypedKeyMap.Proxy { + + @Override + TypedKeyMap.Operator context(); + + @Override + default B set(final TypedKey key, final T value) { + this.context().set(key, value); + return (B) this; + } + + @Override + default B remove(final TypedKey key) { + this.context().remove(key); + return (B) this; + } + + @Override + default B trySet(final TypedKey key, final Optional optValue) { + this.context().trySet(key, optValue); + return (B) this; + } + + @Override + default B apply(final TypedKey key, final Optional optValue) { + this.context().apply(key, optValue); + return (B) this; + } + } + } + + interface Proxy extends TypedKeyMap { + + /** + * Returns the underlying {@link TypedKeyMap} for this {@link TypedKeyMap.Proxy}. + * + * @return The underlying {@link TypedKeyMap}. + */ + TypedKeyMap context(); + + @Override + default Set> typedKeySet() { + return this.context().typedKeySet(); + } + + @Override + default boolean has(final TypedKey key) { + return this.context().has(key); + } + + @Override + default Optional get(final TypedKey key) { + return this.context().get(key); + } + + @Override + default T require(final TypedKey key) { + return this.context().require(key); + } + + @Override + default T getOrElse(final TypedKey key, final T defaultValue) { + return this.context().getOrElse(key, defaultValue); + } + + @Override + default T getOrNull(final TypedKey key) { + return this.context().getOrNull(key); + } + } + + class Impl implements TypedKeyMap { + + private static final TypedKeyMap EMPTY = new Impl(Map.of()); + + protected final Map, Object> values; + + protected Impl(final Map, Object> values) { + this.values = values; + } + + public Impl.Mutable asMutable() { + return new Impl.Mutable(this.values); + } + + public Impl.Mutable asMutableCopy() { + return new Impl.Mutable(this.values); + } + + public Impl asImmutable() { + return this; + } + + @Override + public Set> typedKeySet() { + return this.values.keySet(); + } + + @Override + public boolean has(final TypedKey key) { + return this.values.containsKey(key); + } + + @Override + public Optional get(final TypedKey key) { + return Optional.ofNullable(key.cast(this.values.get(key))); + } + + @Override + public T require(final TypedKey key) { + final @Nullable Object value = this.values.get(key); + if (value == null) { + throw new NoSuchElementException("Could not retrieve value for key " + key.toString()); + } + return key.cast(value); + } + + @Override + public T getOrElse(final TypedKey key, final T defaultValue) { + return key.cast(this.values.getOrDefault(key, defaultValue)); + } + + @Override + public T getOrNull(final TypedKey key) { + return key.cast(this.values.get(key)); + } + + @Override + public int hashCode() { + return this.values.hashCode(); + } + + @Override + public boolean equals(final Object obj) { + if (this == obj) { + return true; + } else if (obj instanceof final TypedKeyMap that) { + if (this.typedKeySet().size() != that.typedKeySet().size()) { + return false; + } + + for (final TypedKey key : that.typedKeySet()) { + if (!this.has(key) || !Objects.equals(this.require(key), that.require(key))) { + return false; + } + } + + return true; + } else { + return false; + } + } + + @Override + public String toString() { + final StringJoiner joiner = new StringJoiner(", "); + for (Map.Entry, Object> entry : this.values.entrySet()) { + joiner.add("\"" + entry.getKey().toString() + "\"=" + entry.getValue().toString()); + } + return "Context[" + joiner.toString() + "]"; + } + + public static class Mutable extends Impl implements TypedKeyMap.Mutable { + + protected Mutable() { + super(new HashMap<>()); + } + + protected Mutable(final Map, Object> values) { + super(new HashMap<>(values)); + } + + @Override + public Impl.Mutable asMutable() { + return this; + } + + @Override + public Impl asImmutable() { + return new Impl(Map.copyOf(this.values)); + } + + @Override + public Optional set(final TypedKey key, final T value) { + return Optional.ofNullable(key.cast(this.values.put(key, Objects.requireNonNull(value, "value")))); + } + + @Override + public Optional remove(final TypedKey key) { + return Optional.ofNullable(key.cast(this.values.remove(key))); + } + + public void clear() { + this.values.clear(); + } + } + } +} diff --git a/src/main/java/net/hellheim/spongetools/object/UpdatableSupplier.java b/api/src/main/java/net/hellheim/spongetools/object/UpdatableSupplier.java similarity index 100% rename from src/main/java/net/hellheim/spongetools/object/UpdatableSupplier.java rename to api/src/main/java/net/hellheim/spongetools/object/UpdatableSupplier.java diff --git a/api/src/main/java/net/hellheim/spongetools/object/ValueSetBuilder.java b/api/src/main/java/net/hellheim/spongetools/object/ValueSetBuilder.java new file mode 100644 index 0000000..f5602fe --- /dev/null +++ b/api/src/main/java/net/hellheim/spongetools/object/ValueSetBuilder.java @@ -0,0 +1,95 @@ +package net.hellheim.spongetools.object; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Map; +import java.util.stream.Stream; + +import org.checkerframework.checker.nullness.qual.Nullable; +import org.spongepowered.api.data.DataManipulator; +import org.spongepowered.api.data.Key; +import org.spongepowered.api.data.value.CollectionValue; +import org.spongepowered.api.data.value.ListValue; +import org.spongepowered.api.data.value.SetValue; +import org.spongepowered.api.data.value.Value; +import org.spongepowered.api.data.value.WeightedCollectionValue; +import org.spongepowered.api.util.weighted.WeightedTable; + +import io.leangen.geantyref.GenericTypeReflector; + +public class ValueSetBuilder implements DataOperator { + + protected final Map, Value> values = new HashMap<>(); + + @Override + public ValueSetBuilder add(final Key> key, final V value) { + this.values.put(key, Value.immutableOf(key, value)); + return this; + } + + @Override + public > ValueSetBuilder addAll(final Key> key, final Stream stream) { + final Iterator iterator = stream.iterator(); + if (!iterator.hasNext()) { + return this; + } + + @SuppressWarnings("unchecked") + final @Nullable CollectionValue elements = (CollectionValue) this.values.get(key); + final C newElements = createCollection(key); + if (elements != null) { + newElements.addAll(elements.all()); + } + iterator.forEachRemaining(newElements::add); + return this.add(key, newElements); + } + + @Override + public > ValueSetBuilder addSingle(final Key> key, final V element) { + @SuppressWarnings("unchecked") + final @Nullable CollectionValue elements = (CollectionValue) this.values.get(key); + final C newElements = createCollection(key); + if (elements != null) { + newElements.addAll(elements.all()); + } + newElements.add(element); + return this.add(key, newElements); + } + + @Override + public ValueSetBuilder remove(final Key key) { + this.values.remove(key); + return this; + } + + @Override + public ValueSetBuilder reset() { + this.values.clear(); + return this; + } + + public DataManipulator.Mutable asMutableManipulator() { + return DataManipulator.mutableOf(this.values.values()); + } + + public DataManipulator.Immutable asImmutableManipulator() { + return DataManipulator.immutableOf(this.values.values()); + } + + @SuppressWarnings("unchecked") + private static > C createCollection(final Key> key) { + final Class rawType = GenericTypeReflector.erase(key.valueType()); + if (ListValue.class.isAssignableFrom(rawType)) { + return (C) new ArrayList<>(); + } else if (SetValue.class.isAssignableFrom(rawType)) { + return (C) new HashSet<>(); + } else if (WeightedCollectionValue.class.isAssignableFrom(rawType)) { + return (C) new WeightedTable<>(); + } + + throw new IllegalArgumentException("Unknown CollectionValue type: " + rawType); + } +} diff --git a/src/main/java/net/hellheim/spongetools/object/ViewableInventoryBuilder.java b/api/src/main/java/net/hellheim/spongetools/object/ViewableInventoryBuilder.java similarity index 100% rename from src/main/java/net/hellheim/spongetools/object/ViewableInventoryBuilder.java rename to api/src/main/java/net/hellheim/spongetools/object/ViewableInventoryBuilder.java diff --git a/src/main/java/net/hellheim/spongetools/object/ViewableInventoryBuilderImpl.java b/api/src/main/java/net/hellheim/spongetools/object/ViewableInventoryBuilderImpl.java similarity index 100% rename from src/main/java/net/hellheim/spongetools/object/ViewableInventoryBuilderImpl.java rename to api/src/main/java/net/hellheim/spongetools/object/ViewableInventoryBuilderImpl.java diff --git a/src/main/java/net/hellheim/spongetools/proxy/optional/data/OptionalDataHolderProxy.java b/api/src/main/java/net/hellheim/spongetools/proxy/optional/data/OptionalDataHolderProxy.java similarity index 100% rename from src/main/java/net/hellheim/spongetools/proxy/optional/data/OptionalDataHolderProxy.java rename to api/src/main/java/net/hellheim/spongetools/proxy/optional/data/OptionalDataHolderProxy.java diff --git a/src/main/java/net/hellheim/spongetools/proxy/optional/data/OptionalImmutableDataHolderProxy.java b/api/src/main/java/net/hellheim/spongetools/proxy/optional/data/OptionalImmutableDataHolderProxy.java similarity index 100% rename from src/main/java/net/hellheim/spongetools/proxy/optional/data/OptionalImmutableDataHolderProxy.java rename to api/src/main/java/net/hellheim/spongetools/proxy/optional/data/OptionalImmutableDataHolderProxy.java diff --git a/src/main/java/net/hellheim/spongetools/proxy/optional/data/OptionalMutableDataHolderProxy.java b/api/src/main/java/net/hellheim/spongetools/proxy/optional/data/OptionalMutableDataHolderProxy.java similarity index 100% rename from src/main/java/net/hellheim/spongetools/proxy/optional/data/OptionalMutableDataHolderProxy.java rename to api/src/main/java/net/hellheim/spongetools/proxy/optional/data/OptionalMutableDataHolderProxy.java diff --git a/src/main/java/net/hellheim/spongetools/proxy/optional/data/OptionalValueContainerProxy.java b/api/src/main/java/net/hellheim/spongetools/proxy/optional/data/OptionalValueContainerProxy.java similarity index 100% rename from src/main/java/net/hellheim/spongetools/proxy/optional/data/OptionalValueContainerProxy.java rename to api/src/main/java/net/hellheim/spongetools/proxy/optional/data/OptionalValueContainerProxy.java diff --git a/src/main/java/net/hellheim/spongetools/proxy/optional/entity/OptionalEntityArchetypeProxy.java b/api/src/main/java/net/hellheim/spongetools/proxy/optional/entity/OptionalEntityArchetypeProxy.java similarity index 100% rename from src/main/java/net/hellheim/spongetools/proxy/optional/entity/OptionalEntityArchetypeProxy.java rename to api/src/main/java/net/hellheim/spongetools/proxy/optional/entity/OptionalEntityArchetypeProxy.java diff --git a/src/main/java/net/hellheim/spongetools/proxy/optional/entity/OptionalEntityProxy.java b/api/src/main/java/net/hellheim/spongetools/proxy/optional/entity/OptionalEntityProxy.java similarity index 100% rename from src/main/java/net/hellheim/spongetools/proxy/optional/entity/OptionalEntityProxy.java rename to api/src/main/java/net/hellheim/spongetools/proxy/optional/entity/OptionalEntityProxy.java diff --git a/src/main/java/net/hellheim/spongetools/proxy/optional/entity/OptionalEntityTypeProxy.java b/api/src/main/java/net/hellheim/spongetools/proxy/optional/entity/OptionalEntityTypeProxy.java similarity index 100% rename from src/main/java/net/hellheim/spongetools/proxy/optional/entity/OptionalEntityTypeProxy.java rename to api/src/main/java/net/hellheim/spongetools/proxy/optional/entity/OptionalEntityTypeProxy.java diff --git a/src/main/java/net/hellheim/spongetools/proxy/optional/entity/OptionalIEntityProxy.java b/api/src/main/java/net/hellheim/spongetools/proxy/optional/entity/OptionalIEntityProxy.java similarity index 100% rename from src/main/java/net/hellheim/spongetools/proxy/optional/entity/OptionalIEntityProxy.java rename to api/src/main/java/net/hellheim/spongetools/proxy/optional/entity/OptionalIEntityProxy.java diff --git a/src/main/java/net/hellheim/spongetools/proxy/optional/entity/instance/OptionalLivingEntityProxy.java b/api/src/main/java/net/hellheim/spongetools/proxy/optional/entity/instance/OptionalLivingEntityProxy.java similarity index 100% rename from src/main/java/net/hellheim/spongetools/proxy/optional/entity/instance/OptionalLivingEntityProxy.java rename to api/src/main/java/net/hellheim/spongetools/proxy/optional/entity/instance/OptionalLivingEntityProxy.java diff --git a/src/main/java/net/hellheim/spongetools/proxy/optional/entity/instance/OptionalPlayerEntityProxy.java b/api/src/main/java/net/hellheim/spongetools/proxy/optional/entity/instance/OptionalPlayerEntityProxy.java similarity index 100% rename from src/main/java/net/hellheim/spongetools/proxy/optional/entity/instance/OptionalPlayerEntityProxy.java rename to api/src/main/java/net/hellheim/spongetools/proxy/optional/entity/instance/OptionalPlayerEntityProxy.java diff --git a/src/main/java/net/hellheim/spongetools/proxy/optional/entity/instance/OptionalServerPlayerEntityProxy.java b/api/src/main/java/net/hellheim/spongetools/proxy/optional/entity/instance/OptionalServerPlayerEntityProxy.java similarity index 100% rename from src/main/java/net/hellheim/spongetools/proxy/optional/entity/instance/OptionalServerPlayerEntityProxy.java rename to api/src/main/java/net/hellheim/spongetools/proxy/optional/entity/instance/OptionalServerPlayerEntityProxy.java diff --git a/src/main/java/net/hellheim/spongetools/proxy/solid/ColorProxy.java b/api/src/main/java/net/hellheim/spongetools/proxy/solid/ColorProxy.java similarity index 100% rename from src/main/java/net/hellheim/spongetools/proxy/solid/ColorProxy.java rename to api/src/main/java/net/hellheim/spongetools/proxy/solid/ColorProxy.java diff --git a/src/main/java/net/hellheim/spongetools/proxy/solid/EnchantmentProxy.java b/api/src/main/java/net/hellheim/spongetools/proxy/solid/EnchantmentProxy.java similarity index 100% rename from src/main/java/net/hellheim/spongetools/proxy/solid/EnchantmentProxy.java rename to api/src/main/java/net/hellheim/spongetools/proxy/solid/EnchantmentProxy.java diff --git a/src/main/java/net/hellheim/spongetools/proxy/solid/EnchantmentTypeProxy.java b/api/src/main/java/net/hellheim/spongetools/proxy/solid/EnchantmentTypeProxy.java similarity index 100% rename from src/main/java/net/hellheim/spongetools/proxy/solid/EnchantmentTypeProxy.java rename to api/src/main/java/net/hellheim/spongetools/proxy/solid/EnchantmentTypeProxy.java diff --git a/src/main/java/net/hellheim/spongetools/proxy/solid/PluginProxy.java b/api/src/main/java/net/hellheim/spongetools/proxy/solid/PluginProxy.java similarity index 100% rename from src/main/java/net/hellheim/spongetools/proxy/solid/PluginProxy.java rename to api/src/main/java/net/hellheim/spongetools/proxy/solid/PluginProxy.java diff --git a/src/main/java/net/hellheim/spongetools/proxy/solid/PotionEffectProxy.java b/api/src/main/java/net/hellheim/spongetools/proxy/solid/PotionEffectProxy.java similarity index 100% rename from src/main/java/net/hellheim/spongetools/proxy/solid/PotionEffectProxy.java rename to api/src/main/java/net/hellheim/spongetools/proxy/solid/PotionEffectProxy.java diff --git a/src/main/java/net/hellheim/spongetools/proxy/solid/PotionEffectTypeProxy.java b/api/src/main/java/net/hellheim/spongetools/proxy/solid/PotionEffectTypeProxy.java similarity index 100% rename from src/main/java/net/hellheim/spongetools/proxy/solid/PotionEffectTypeProxy.java rename to api/src/main/java/net/hellheim/spongetools/proxy/solid/PotionEffectTypeProxy.java diff --git a/src/main/java/net/hellheim/spongetools/proxy/solid/SchedulerProxy.java b/api/src/main/java/net/hellheim/spongetools/proxy/solid/SchedulerProxy.java similarity index 100% rename from src/main/java/net/hellheim/spongetools/proxy/solid/SchedulerProxy.java rename to api/src/main/java/net/hellheim/spongetools/proxy/solid/SchedulerProxy.java diff --git a/src/main/java/net/hellheim/spongetools/proxy/solid/adventure/AudienceProxy.java b/api/src/main/java/net/hellheim/spongetools/proxy/solid/adventure/AudienceProxy.java similarity index 100% rename from src/main/java/net/hellheim/spongetools/proxy/solid/adventure/AudienceProxy.java rename to api/src/main/java/net/hellheim/spongetools/proxy/solid/adventure/AudienceProxy.java diff --git a/src/main/java/net/hellheim/spongetools/proxy/solid/adventure/ForwardingAudienceProxy.java b/api/src/main/java/net/hellheim/spongetools/proxy/solid/adventure/ForwardingAudienceProxy.java similarity index 100% rename from src/main/java/net/hellheim/spongetools/proxy/solid/adventure/ForwardingAudienceProxy.java rename to api/src/main/java/net/hellheim/spongetools/proxy/solid/adventure/ForwardingAudienceProxy.java diff --git a/src/main/java/net/hellheim/spongetools/proxy/solid/adventure/ViewerProxy.java b/api/src/main/java/net/hellheim/spongetools/proxy/solid/adventure/ViewerProxy.java similarity index 93% rename from src/main/java/net/hellheim/spongetools/proxy/solid/adventure/ViewerProxy.java rename to api/src/main/java/net/hellheim/spongetools/proxy/solid/adventure/ViewerProxy.java index 83ff27f..13cb817 100644 --- a/src/main/java/net/hellheim/spongetools/proxy/solid/adventure/ViewerProxy.java +++ b/api/src/main/java/net/hellheim/spongetools/proxy/solid/adventure/ViewerProxy.java @@ -4,6 +4,7 @@ import org.spongepowered.api.effect.Viewer; import org.spongepowered.api.effect.particle.ParticleEffect; import org.spongepowered.api.effect.sound.music.MusicDisc; +import org.spongepowered.api.item.inventory.ItemStackLike; import org.spongepowered.api.world.WorldType; import org.spongepowered.math.vector.Vector3d; import org.spongepowered.math.vector.Vector3i; @@ -20,6 +21,11 @@ default void sendWorldType(final WorldType worldType) { this.getAsAudience().sendWorldType(worldType); } + @Override + default void playTotemOfUndyingEffect(final ItemStackLike stack) { + this.getAsAudience().playTotemOfUndyingEffect(stack); + } + @Override default void spawnParticles(final ParticleEffect particleEffect, final Vector3d position) { this.getAsAudience().spawnParticles(particleEffect, position); diff --git a/src/main/java/net/hellheim/spongetools/proxy/solid/block/BlockStateProxy.java b/api/src/main/java/net/hellheim/spongetools/proxy/solid/block/BlockStateProxy.java similarity index 100% rename from src/main/java/net/hellheim/spongetools/proxy/solid/block/BlockStateProxy.java rename to api/src/main/java/net/hellheim/spongetools/proxy/solid/block/BlockStateProxy.java diff --git a/src/main/java/net/hellheim/spongetools/proxy/solid/block/BlockTypeProxy.java b/api/src/main/java/net/hellheim/spongetools/proxy/solid/block/BlockTypeProxy.java similarity index 100% rename from src/main/java/net/hellheim/spongetools/proxy/solid/block/BlockTypeProxy.java rename to api/src/main/java/net/hellheim/spongetools/proxy/solid/block/BlockTypeProxy.java diff --git a/api/src/main/java/net/hellheim/spongetools/proxy/solid/block/StateContainerProxy.java b/api/src/main/java/net/hellheim/spongetools/proxy/solid/block/StateContainerProxy.java new file mode 100644 index 0000000..49dff14 --- /dev/null +++ b/api/src/main/java/net/hellheim/spongetools/proxy/solid/block/StateContainerProxy.java @@ -0,0 +1,34 @@ +package net.hellheim.spongetools.proxy.solid.block; + +import java.util.Collection; +import java.util.List; +import java.util.Optional; + +import org.spongepowered.api.state.State; +import org.spongepowered.api.state.StateContainer; +import org.spongepowered.api.state.StateProperty; + +public interface StateContainerProxy> extends StateContainer { + + StateContainer getAsStateContainer(); + + @Override + default List validStates() { + return this.getAsStateContainer().validStates(); + } + + @Override + default S defaultState() { + return this.getAsStateContainer().defaultState(); + } + + @Override + default Collection> stateProperties() { + return this.getAsStateContainer().stateProperties(); + } + + @Override + default Optional> findStateProperty(final String name) { + return this.getAsStateContainer().findStateProperty(name); + } +} diff --git a/src/main/java/net/hellheim/spongetools/proxy/solid/codec/CodecProxy.java b/api/src/main/java/net/hellheim/spongetools/proxy/solid/codec/CodecProxy.java similarity index 86% rename from src/main/java/net/hellheim/spongetools/proxy/solid/codec/CodecProxy.java rename to api/src/main/java/net/hellheim/spongetools/proxy/solid/codec/CodecProxy.java index 791ca78..a618e38 100644 --- a/src/main/java/net/hellheim/spongetools/proxy/solid/codec/CodecProxy.java +++ b/api/src/main/java/net/hellheim/spongetools/proxy/solid/codec/CodecProxy.java @@ -5,5 +5,5 @@ public interface CodecProxy extends EncoderProxy, DecoderProxy { @Override - Codec codec(); + Codec codec(); } diff --git a/src/main/java/net/hellheim/spongetools/proxy/solid/codec/DecoderProxy.java b/api/src/main/java/net/hellheim/spongetools/proxy/solid/codec/DecoderProxy.java similarity index 100% rename from src/main/java/net/hellheim/spongetools/proxy/solid/codec/DecoderProxy.java rename to api/src/main/java/net/hellheim/spongetools/proxy/solid/codec/DecoderProxy.java diff --git a/api/src/main/java/net/hellheim/spongetools/proxy/solid/codec/DynamicOpsProxy.java b/api/src/main/java/net/hellheim/spongetools/proxy/solid/codec/DynamicOpsProxy.java new file mode 100644 index 0000000..ff9d9fe --- /dev/null +++ b/api/src/main/java/net/hellheim/spongetools/proxy/solid/codec/DynamicOpsProxy.java @@ -0,0 +1,280 @@ +package net.hellheim.spongetools.proxy.solid.codec; + +import java.nio.ByteBuffer; +import java.util.List; +import java.util.Map; +import java.util.function.BiConsumer; +import java.util.function.Consumer; +import java.util.function.Function; +import java.util.stream.IntStream; +import java.util.stream.LongStream; +import java.util.stream.Stream; + +import com.mojang.datafixers.util.Pair; +import com.mojang.serialization.DataResult; +import com.mojang.serialization.Decoder; +import com.mojang.serialization.DynamicOps; +import com.mojang.serialization.Encoder; +import com.mojang.serialization.ListBuilder; +import com.mojang.serialization.MapLike; +import com.mojang.serialization.RecordBuilder; + +public interface DynamicOpsProxy extends DynamicOps { + + DynamicOps getAsOps(); + + @Override + default T empty() { + return this.getAsOps().empty(); + } + + @Override + default T emptyMap() { + return this.getAsOps().emptyMap(); + } + + @Override + default T emptyList() { + return this.getAsOps().emptyList(); + } + + @Override + default U convertTo(final DynamicOps outOps, final T input) { + return this.getAsOps().convertTo(outOps, input); + } + + @Override + default DataResult getNumberValue(final T input) { + return this.getAsOps().getNumberValue(input); + } + + @Override + default Number getNumberValue(final T input, final Number defaultValue) { + return this.getAsOps().getNumberValue(input, defaultValue); + } + + @Override + default T createNumeric(final Number i) { + return this.getAsOps().createNumeric(i); + } + + @Override + default T createByte(final byte value) { + return this.getAsOps().createByte(value); + } + + @Override + default T createShort(final short value) { + return this.getAsOps().createShort(value); + } + + @Override + default T createInt(final int value) { + return this.getAsOps().createInt(value); + } + + @Override + default T createLong(final long value) { + return this.getAsOps().createLong(value); + } + + @Override + default T createFloat(final float value) { + return this.getAsOps().createFloat(value); + } + + @Override + default T createDouble(final double value) { + return this.getAsOps().createDouble(value); + } + + @Override + default DataResult getBooleanValue(final T input) { + return this.getAsOps().getBooleanValue(input); + } + + @Override + default T createBoolean(final boolean value) { + return this.getAsOps().createBoolean(value); + } + + @Override + default DataResult getStringValue(final T input) { + return this.getAsOps().getStringValue(input); + } + + @Override + default T createString(final String value) { + return this.getAsOps().createString(value); + } + + @Override + default DataResult mergeToList(final T list, final T value) { + return this.getAsOps().mergeToList(list, value); + } + + @Override + default DataResult mergeToList(final T list, final List values) { + return this.getAsOps().mergeToList(list, values); + } + + @Override + default DataResult mergeToMap(final T map, final T key, final T value) { + return this.getAsOps().mergeToMap(map, key, value); + } + + @Override + default DataResult mergeToMap(final T map, final Map values) { + return this.getAsOps().mergeToMap(map, values); + } + + @Override + default DataResult mergeToMap(final T map, final MapLike values) { + return this.getAsOps().mergeToMap(map, values); + } + + @Override + default DataResult mergeToPrimitive(final T prefix, final T value) { + return this.getAsOps().mergeToPrimitive(prefix, value); + } + + @Override + default DataResult>> getMapValues(final T input) { + return this.getAsOps().getMapValues(input); + } + + @Override + default DataResult>> getMapEntries(final T input) { + return this.getAsOps().getMapEntries(input); + } + + @Override + default T createMap(final Stream> map) { + return this.getAsOps().createMap(map); + } + + @Override + default DataResult> getMap(final T input) { + return this.getAsOps().getMap(input); + } + + @Override + default T createMap(final Map map) { + return this.getAsOps().createMap(map); + } + + @Override + default DataResult> getStream(final T input) { + return this.getAsOps().getStream(input); + } + + @Override + default DataResult>> getList(final T input) { + return this.getAsOps().getList(input); + } + + @Override + default T createList(final Stream input) { + return this.getAsOps().createList(input); + } + + @Override + default DataResult getByteBuffer(final T input) { + return this.getAsOps().getByteBuffer(input); + } + + @Override + default T createByteList(final ByteBuffer input) { + return this.getAsOps().createByteList(input); + } + + @Override + default DataResult getIntStream(final T input) { + return this.getAsOps().getIntStream(input); + } + + @Override + default T createIntList(final IntStream input) { + return this.getAsOps().createIntList(input); + } + + @Override + default DataResult getLongStream(final T input) { + return this.getAsOps().getLongStream(input); + } + + @Override + default T createLongList(final LongStream input) { + return this.getAsOps().createLongList(input); + } + + @Override + default T remove(final T input, final String key) { + return this.getAsOps().remove(input, key); + } + + @Override + default boolean compressMaps() { + return this.getAsOps().compressMaps(); + } + + @Override + default DataResult get(final T input, final String key) { + return this.getAsOps().get(input, key); + } + + @Override + default DataResult getGeneric(final T input, final T key) { + return this.getAsOps().getGeneric(input, key); + } + + @Override + default T set(final T input, final String key, final T value) { + return this.getAsOps().set(input, key, value); + } + + @Override + default T update(final T input, final String key, final Function function) { + return this.getAsOps().update(input, key, function); + } + + @Override + default T updateGeneric(final T input, final T key, final Function function) { + return this.getAsOps().updateGeneric(input, key, function); + } + + @Override + default ListBuilder listBuilder() { + return this.getAsOps().listBuilder(); + } + + @Override + default RecordBuilder mapBuilder() { + return this.getAsOps().mapBuilder(); + } + + @Override + default Function> withEncoder(final Encoder encoder) { + return this.getAsOps().withEncoder(encoder); + } + + @Override + default Function>> withDecoder(final Decoder decoder) { + return this.getAsOps().withDecoder(decoder); + } + + @Override + default Function> withParser(final Decoder decoder) { + return this.getAsOps().withParser(decoder); + } + + @Override + default U convertList(final DynamicOps outOps, final T input) { + return this.getAsOps().convertList(outOps, input); + } + + @Override + default U convertMap(final DynamicOps outOps, final T input) { + return this.getAsOps().convertMap(outOps, input); + } +} diff --git a/src/main/java/net/hellheim/spongetools/proxy/solid/codec/EncoderProxy.java b/api/src/main/java/net/hellheim/spongetools/proxy/solid/codec/EncoderProxy.java similarity index 100% rename from src/main/java/net/hellheim/spongetools/proxy/solid/codec/EncoderProxy.java rename to api/src/main/java/net/hellheim/spongetools/proxy/solid/codec/EncoderProxy.java diff --git a/src/main/java/net/hellheim/spongetools/proxy/solid/codec/MapCodecProxy.java b/api/src/main/java/net/hellheim/spongetools/proxy/solid/codec/MapCodecProxy.java similarity index 100% rename from src/main/java/net/hellheim/spongetools/proxy/solid/codec/MapCodecProxy.java rename to api/src/main/java/net/hellheim/spongetools/proxy/solid/codec/MapCodecProxy.java diff --git a/src/main/java/net/hellheim/spongetools/proxy/solid/codec/MapDecoderProxy.java b/api/src/main/java/net/hellheim/spongetools/proxy/solid/codec/MapDecoderProxy.java similarity index 100% rename from src/main/java/net/hellheim/spongetools/proxy/solid/codec/MapDecoderProxy.java rename to api/src/main/java/net/hellheim/spongetools/proxy/solid/codec/MapDecoderProxy.java diff --git a/src/main/java/net/hellheim/spongetools/proxy/solid/codec/MapEncoderProxy.java b/api/src/main/java/net/hellheim/spongetools/proxy/solid/codec/MapEncoderProxy.java similarity index 100% rename from src/main/java/net/hellheim/spongetools/proxy/solid/codec/MapEncoderProxy.java rename to api/src/main/java/net/hellheim/spongetools/proxy/solid/codec/MapEncoderProxy.java diff --git a/src/main/java/net/hellheim/spongetools/proxy/solid/data/DataHolderProxy.java b/api/src/main/java/net/hellheim/spongetools/proxy/solid/data/DataHolderProxy.java similarity index 85% rename from src/main/java/net/hellheim/spongetools/proxy/solid/data/DataHolderProxy.java rename to api/src/main/java/net/hellheim/spongetools/proxy/solid/data/DataHolderProxy.java index 94772ac..a9490bf 100644 --- a/src/main/java/net/hellheim/spongetools/proxy/solid/data/DataHolderProxy.java +++ b/api/src/main/java/net/hellheim/spongetools/proxy/solid/data/DataHolderProxy.java @@ -5,5 +5,5 @@ public interface DataHolderProxy extends ValueContainerProxy, DataHolder { @Override - DataHolder getAsDataHolder(); + DataHolder getAsData(); } diff --git a/api/src/main/java/net/hellheim/spongetools/proxy/solid/data/DataManipulatorProxy.java b/api/src/main/java/net/hellheim/spongetools/proxy/solid/data/DataManipulatorProxy.java new file mode 100644 index 0000000..b3457de --- /dev/null +++ b/api/src/main/java/net/hellheim/spongetools/proxy/solid/data/DataManipulatorProxy.java @@ -0,0 +1,29 @@ +package net.hellheim.spongetools.proxy.solid.data; + +import org.spongepowered.api.data.DataManipulator; + +public interface DataManipulatorProxy extends ValueContainerProxy, DataManipulator { + + @Override + DataManipulator getAsData(); + + @Override + default DataManipulator copy() { + return this.getAsData().copy(); + } + + @Override + default DataManipulator.Mutable asMutableCopy() { + return this.getAsData().asMutableCopy(); + } + + @Override + default DataManipulator.Mutable asMutable() { + return this.getAsData().asMutable(); + } + + @Override + default DataManipulator.Immutable asImmutable() { + return this.getAsData().asImmutable(); + } +} diff --git a/src/main/java/net/hellheim/spongetools/proxy/solid/data/ImmutableDataHolderProxy.java b/api/src/main/java/net/hellheim/spongetools/proxy/solid/data/ImmutableDataHolderProxy.java similarity index 71% rename from src/main/java/net/hellheim/spongetools/proxy/solid/data/ImmutableDataHolderProxy.java rename to api/src/main/java/net/hellheim/spongetools/proxy/solid/data/ImmutableDataHolderProxy.java index 3d31b89..6f2f63a 100644 --- a/src/main/java/net/hellheim/spongetools/proxy/solid/data/ImmutableDataHolderProxy.java +++ b/api/src/main/java/net/hellheim/spongetools/proxy/solid/data/ImmutableDataHolderProxy.java @@ -12,55 +12,55 @@ public interface ImmutableDataHolderProxy> extends DataHolderProxy, DataHolder.Immutable { @Override - DataHolder.Immutable getAsDataHolder(); + DataHolder.Immutable getAsData(); @Override default Optional transform(final Key> key, final Function function) { - return this.getAsDataHolder().transform(key, function); + return this.getAsData().transform(key, function); } @Override default Optional transform(final Supplier>> key, final Function function) { - return this.getAsDataHolder().transform(key, function); + return this.getAsData().transform(key, function); } @Override default Optional with(final Key> key, final E value) { - return this.getAsDataHolder().with(key, value); + return this.getAsData().with(key, value); } @Override default Optional with(final Supplier>> key, final E value) { - return this.getAsDataHolder().with(key, value); + return this.getAsData().with(key, value); } @Override default Optional with(final Value value) { - return this.getAsDataHolder().with(value); + return this.getAsData().with(value); } @Override default Optional without(final Value value) { - return this.getAsDataHolder().without(value); + return this.getAsData().without(value); } @Override default Optional without(final Key key) { - return this.getAsDataHolder().without(key); + return this.getAsData().without(key); } @Override default Optional without(final Supplier> key) { - return this.getAsDataHolder().without(key); + return this.getAsData().without(key); } @Override default I mergeWith(final I that) { - return this.getAsDataHolder().mergeWith(that); + return this.getAsData().mergeWith(that); } @Override default I mergeWith(final I that, final MergeFunction function) { - return this.getAsDataHolder().mergeWith(that, function); + return this.getAsData().mergeWith(that, function); } } diff --git a/api/src/main/java/net/hellheim/spongetools/proxy/solid/data/ImmutableDataManipulatorProxy.java b/api/src/main/java/net/hellheim/spongetools/proxy/solid/data/ImmutableDataManipulatorProxy.java new file mode 100644 index 0000000..a81de65 --- /dev/null +++ b/api/src/main/java/net/hellheim/spongetools/proxy/solid/data/ImmutableDataManipulatorProxy.java @@ -0,0 +1,43 @@ +package net.hellheim.spongetools.proxy.solid.data; + +import java.util.function.Function; + +import org.spongepowered.api.data.DataManipulator; +import org.spongepowered.api.data.Key; +import org.spongepowered.api.data.value.Value; + +public interface ImmutableDataManipulatorProxy extends DataManipulatorProxy, DataManipulator.Immutable { + + @Override + DataManipulator.Immutable getAsData(); + + @Override + default Immutable with(final Key> key, final E value) { + return this.getAsData().with(key, value); + } + + @Override + default Immutable without(final Key key) { + return this.getAsData().without(key); + } + + @Override + default Immutable with(final Value value) { + return this.getAsData().with(value); + } + + @Override + default Immutable transform(final Key> key, final Function function) { + return this.getAsData().transform(key, function); + } + + @Override + default Immutable copy() { + return this.getAsData().copy(); + } + + @Override + default Immutable asImmutable() { + return this.getAsData().asImmutable(); + } +} diff --git a/src/main/java/net/hellheim/spongetools/proxy/solid/data/MutableDataHolderProxy.java b/api/src/main/java/net/hellheim/spongetools/proxy/solid/data/MutableDataHolderProxy.java similarity index 72% rename from src/main/java/net/hellheim/spongetools/proxy/solid/data/MutableDataHolderProxy.java rename to api/src/main/java/net/hellheim/spongetools/proxy/solid/data/MutableDataHolderProxy.java index acce365..b98e580 100644 --- a/src/main/java/net/hellheim/spongetools/proxy/solid/data/MutableDataHolderProxy.java +++ b/api/src/main/java/net/hellheim/spongetools/proxy/solid/data/MutableDataHolderProxy.java @@ -17,175 +17,175 @@ public interface MutableDataHolderProxy extends DataHolderProxy, DataHolder.Mutable { @Override - DataHolder.Mutable getAsDataHolder(); + DataHolder.Mutable getAsData(); @Override default DataTransactionResult transform(final Key> key, final Function function) { - return this.getAsDataHolder().transform(key, function); + return this.getAsData().transform(key, function); } @Override default DataTransactionResult transform(final Supplier>> key, final Function function) { - return this.getAsDataHolder().transform(key, function); + return this.getAsData().transform(key, function); } @Override default DataTransactionResult offer(final Key> key, final E value) { - return this.getAsDataHolder().offer(key, value); + return this.getAsData().offer(key, value); } @Override default DataTransactionResult offer(final Supplier>> key, final E value) { - return this.getAsDataHolder().offer(key, value); + return this.getAsData().offer(key, value); } @Override default DataTransactionResult offer(final Supplier>> key, final Supplier value) { - return this.getAsDataHolder().offer(key, value); + return this.getAsData().offer(key, value); } @Override default DataTransactionResult offer(final Value value) { - return this.getAsDataHolder().offer(value); + return this.getAsData().offer(value); } @Override default DataTransactionResult offerSingle(final Key> key, final E element) { - return this.getAsDataHolder().offerSingle(key, element); + return this.getAsData().offerSingle(key, element); } @Override default DataTransactionResult offerSingle(final Supplier>> key, final E element) { - return this.getAsDataHolder().offerSingle(key, element); + return this.getAsData().offerSingle(key, element); } @Override default DataTransactionResult offerSingle(final Key> key, final K valueKey, final V value) { - return this.getAsDataHolder().offerSingle(key, valueKey, value); + return this.getAsData().offerSingle(key, valueKey, value); } @Override default DataTransactionResult offerSingle(final Supplier>> key, final K valueKey, final V value) { - return this.getAsDataHolder().offerSingle(key, valueKey, value); + return this.getAsData().offerSingle(key, valueKey, value); } @Override default DataTransactionResult offerAll(final Key> key, final Map map) { - return this.getAsDataHolder().offerAll(key, map); + return this.getAsData().offerAll(key, map); } @Override default DataTransactionResult offerAll(final Supplier>> key, final Map map) { - return this.getAsDataHolder().offerAll(key, map); + return this.getAsData().offerAll(key, map); } @Override default DataTransactionResult offerAll(final MapValue value) { - return this.getAsDataHolder().offerAll(value); + return this.getAsData().offerAll(value); } @Override default DataTransactionResult offerAll(final CollectionValue value) { - return this.getAsDataHolder().offerAll(value); + return this.getAsData().offerAll(value); } @Override default DataTransactionResult offerAll(final Key> key, final Collection elements) { - return this.getAsDataHolder().offerAll(key, elements); + return this.getAsData().offerAll(key, elements); } @Override default DataTransactionResult removeSingle(final Key> key, final E element) { - return this.getAsDataHolder().offerSingle(key, element); + return this.getAsData().offerSingle(key, element); } @Override default DataTransactionResult removeSingle(final Supplier>> key, final E element) { - return this.getAsDataHolder().offerSingle(key, element); + return this.getAsData().offerSingle(key, element); } @Override default DataTransactionResult removeKey(final Key> key, final K mapKey) { - return this.getAsDataHolder().removeKey(key, mapKey); + return this.getAsData().removeKey(key, mapKey); } @Override default DataTransactionResult removeKey(final Supplier>> key, final K mapKey) { - return this.getAsDataHolder().removeKey(key, mapKey); + return this.getAsData().removeKey(key, mapKey); } @Override default DataTransactionResult removeAll(final CollectionValue value) { - return this.getAsDataHolder().removeAll(value); + return this.getAsData().removeAll(value); } @Override default DataTransactionResult removeAll(final Key> key, final Collection elements) { - return this.getAsDataHolder().removeAll(key, elements); + return this.getAsData().removeAll(key, elements); } @Override default DataTransactionResult removeAll(final Supplier>> key, final Collection elements) { - return this.getAsDataHolder().removeAll(key, elements); + return this.getAsData().removeAll(key, elements); } @Override default DataTransactionResult removeAll(final MapValue value) { - return this.getAsDataHolder().removeAll(value); + return this.getAsData().removeAll(value); } @Override default DataTransactionResult removeAll(final Key> key, final Map map) { - return this.getAsDataHolder().removeAll(key, map); + return this.getAsData().removeAll(key, map); } @Override default DataTransactionResult removeAll(final Supplier>> key, final Map map) { - return this.getAsDataHolder().removeAll(key, map); + return this.getAsData().removeAll(key, map); } @Override default DataTransactionResult tryOffer(final Key> key, final E value) { - return this.getAsDataHolder().tryOffer(key, value); + return this.getAsData().tryOffer(key, value); } @Override default DataTransactionResult tryOffer(final Supplier>> key, final E value) { - return this.getAsDataHolder().tryOffer(key, value); + return this.getAsData().tryOffer(key, value); } @Override default DataTransactionResult tryOffer(final Value value) throws IllegalArgumentException { - return this.getAsDataHolder().tryOffer(value); + return this.getAsData().tryOffer(value); } @Override default DataTransactionResult remove(final Value value) { - return this.getAsDataHolder().remove(value); + return this.getAsData().remove(value); } @Override default DataTransactionResult remove(final Key key) { - return this.getAsDataHolder().remove(key); + return this.getAsData().remove(key); } @Override default DataTransactionResult remove(final Supplier> key) { - return this.getAsDataHolder().remove(key); + return this.getAsData().remove(key); } @Override default DataTransactionResult undo(final DataTransactionResult result) { - return this.getAsDataHolder().undo(result); + return this.getAsData().undo(result); } @Override default DataTransactionResult copyFrom(final ValueContainer that) { - return this.getAsDataHolder().copyFrom(that); + return this.getAsData().copyFrom(that); } @Override default DataTransactionResult copyFrom(final ValueContainer that, final MergeFunction function) { - return this.getAsDataHolder().copyFrom(that, function); + return this.getAsData().copyFrom(that, function); } } diff --git a/api/src/main/java/net/hellheim/spongetools/proxy/solid/data/MutableDataManipulatorProxy.java b/api/src/main/java/net/hellheim/spongetools/proxy/solid/data/MutableDataManipulatorProxy.java new file mode 100644 index 0000000..7ff8e2b --- /dev/null +++ b/api/src/main/java/net/hellheim/spongetools/proxy/solid/data/MutableDataManipulatorProxy.java @@ -0,0 +1,107 @@ +package net.hellheim.spongetools.proxy.solid.data; + +import java.util.function.Function; +import java.util.function.Predicate; +import java.util.function.Supplier; + +import org.spongepowered.api.data.DataManipulator; +import org.spongepowered.api.data.Key; +import org.spongepowered.api.data.value.MergeFunction; +import org.spongepowered.api.data.value.Value; +import org.spongepowered.api.data.value.ValueContainer; + +public interface MutableDataManipulatorProxy extends DataManipulatorProxy, DataManipulator.Mutable { + + @Override + DataManipulator.Mutable getAsData(); + + @Override + default DataManipulator.Mutable copyFrom(final ValueContainer valueContainer, final Predicate> predicate) { + return this.getAsData().copyFrom(valueContainer, predicate); + } + + @Override + default DataManipulator.Mutable copyFrom(final ValueContainer valueContainer, final MergeFunction overlap, final Predicate> predicate) { + return this.getAsData().copyFrom(valueContainer, overlap, predicate); + } + + @Override + default DataManipulator.Mutable copyFrom(final ValueContainer valueContainer, final Key first, final Key... more) { + return this.getAsData().copyFrom(valueContainer, first, more); + } + + @Override + default DataManipulator.Mutable copyFrom(final ValueContainer valueContainer, final MergeFunction overlap, final Key first, final Key... more) { + return this.getAsData().copyFrom(valueContainer, overlap, first, more); + } + + @Override + default DataManipulator.Mutable copyFrom(final ValueContainer valueContainer, final Iterable> keys) { + return this.getAsData().copyFrom(valueContainer, keys); + } + + @Override + default DataManipulator.Mutable copyFrom(final ValueContainer valueContainer, final MergeFunction overlap, final Iterable> keys) { + return this.getAsData().copyFrom(valueContainer, overlap, keys); + } + + @Override + default DataManipulator.Mutable copyFrom(final ValueContainer valueContainer) { + return this.getAsData().copyFrom(valueContainer); + } + + @Override + default DataManipulator.Mutable copyFrom(final ValueContainer valueContainer, final MergeFunction overlap) { + return this.getAsData().copyFrom(valueContainer, overlap); + } + + @Override + default DataManipulator.Mutable set(final Key> key, final E value) { + return this.getAsData().set(key, value); + } + + @Override + default > DataManipulator.Mutable set(final Supplier> key, final E value) { + return this.getAsData().set(key, value); + } + + @Override + default > DataManipulator.Mutable set(final Supplier> key, final Supplier value) { + return this.getAsData().set(key, value); + } + + @Override + default DataManipulator.Mutable set(final Value value) { + return this.getAsData().set(value); + } + + @Override + default DataManipulator.Mutable set(final Value... values) { + return this.getAsData().set(values); + } + + @Override + default DataManipulator.Mutable set(final Iterable> values) { + return this.getAsData().set(values); + } + + @Override + default DataManipulator.Mutable transform(final Key> key, final Function function) { + return this.getAsData().transform(key, function); + } + + @Override + default DataManipulator.Mutable remove(final Key key) { + return this.getAsData().remove(key); + } + + @Override + default DataManipulator.Mutable asMutable() { + return this.getAsData().asMutable(); + } + + @Override + default DataManipulator.Mutable copy() { + return this.getAsData().copy(); + } +} diff --git a/src/main/java/net/hellheim/spongetools/proxy/solid/data/TransitiveMutableDataHolderProxy.java b/api/src/main/java/net/hellheim/spongetools/proxy/solid/data/TransitiveMutableDataHolderProxy.java similarity index 73% rename from src/main/java/net/hellheim/spongetools/proxy/solid/data/TransitiveMutableDataHolderProxy.java rename to api/src/main/java/net/hellheim/spongetools/proxy/solid/data/TransitiveMutableDataHolderProxy.java index cf7ec26..9e6c0cd 100644 --- a/src/main/java/net/hellheim/spongetools/proxy/solid/data/TransitiveMutableDataHolderProxy.java +++ b/api/src/main/java/net/hellheim/spongetools/proxy/solid/data/TransitiveMutableDataHolderProxy.java @@ -17,170 +17,170 @@ public interface TransitiveMutableDataHolderProxy extends DataHolderProxy { @Override - DataHolder.Mutable getAsDataHolder(); + DataHolder.Mutable getAsData(); default T transform(final Key> key, final Function function) { - this.getAsDataHolder().transform(key, function); + this.getAsData().transform(key, function); return (T) this; } default T transform(final Supplier>> key, final Function function) { - this.getAsDataHolder().transform(key, function); + this.getAsData().transform(key, function); return (T) this; } default T offer(final Key> key, final E value) { - this.getAsDataHolder().offer(key, value); + this.getAsData().offer(key, value); return (T) this; } default T offer(final Supplier>> key, final E value) { - this.getAsDataHolder().offer(key, value); + this.getAsData().offer(key, value); return (T) this; } default T offer(final Supplier>> key, final Supplier value) { - this.getAsDataHolder().offer(key, value); + this.getAsData().offer(key, value); return (T) this; } default T offer(final Value value) { - this.getAsDataHolder().offer(value); + this.getAsData().offer(value); return (T) this; } default T offerSingle(final Key> key, final E element) { - this.getAsDataHolder().offerSingle(key, element); + this.getAsData().offerSingle(key, element); return (T) this; } default T offerSingle(final Supplier>> key, final E element) { - this.getAsDataHolder().offerSingle(key, element); + this.getAsData().offerSingle(key, element); return (T) this; } default T offerSingle(final Key> key, final K valueKey, final V value) { - this.getAsDataHolder().offerSingle(key, valueKey, value); + this.getAsData().offerSingle(key, valueKey, value); return (T) this; } default T offerSingle(final Supplier>> key, final K valueKey, final V value) { - this.getAsDataHolder().offerSingle(key, valueKey, value); + this.getAsData().offerSingle(key, valueKey, value); return (T) this; } default T offerAll(final Key> key, final Map map) { - this.getAsDataHolder().offerAll(key, map); + this.getAsData().offerAll(key, map); return (T) this; } default T offerAll(final Supplier>> key, final Map map) { - this.getAsDataHolder().offerAll(key, map); + this.getAsData().offerAll(key, map); return (T) this; } default T offerAll(final MapValue value) { - this.getAsDataHolder().offerAll(value); + this.getAsData().offerAll(value); return (T) this; } default T offerAll(final CollectionValue value) { - this.getAsDataHolder().offerAll(value); + this.getAsData().offerAll(value); return (T) this; } default T offerAll(final Key> key, final Collection elements) { - this.getAsDataHolder().offerAll(key, elements); + this.getAsData().offerAll(key, elements); return (T) this; } default T removeSingle(final Key> key, final E element) { - this.getAsDataHolder().removeSingle(key, element); + this.getAsData().removeSingle(key, element); return (T) this; } default T removeSingle(final Supplier>> key, final E element) { - this.getAsDataHolder().offerSingle(key, element); + this.getAsData().offerSingle(key, element); return (T) this; } default T removeKey(final Key> key, final K mapKey) { - this.getAsDataHolder().removeKey(key, mapKey); + this.getAsData().removeKey(key, mapKey); return (T) this; } default T removeKey(final Supplier>> key, final K mapKey) { - this.getAsDataHolder().removeKey(key, mapKey); + this.getAsData().removeKey(key, mapKey); return (T) this; } default T removeAll(final CollectionValue value) { - this.getAsDataHolder().removeAll(value); + this.getAsData().removeAll(value); return (T) this; } default T removeAll(final Key> key, final Collection elements) { - this.getAsDataHolder().removeAll(key, elements); + this.getAsData().removeAll(key, elements); return (T) this; } default T removeAll(final Supplier>> key, final Collection elements) { - this.getAsDataHolder().removeAll(key, elements); + this.getAsData().removeAll(key, elements); return (T) this; } default T removeAll(final MapValue value) { - this.getAsDataHolder().removeAll(value); + this.getAsData().removeAll(value); return (T) this; } default T removeAll(final Key> key, final Map map) { - this.getAsDataHolder().removeAll(key, map); + this.getAsData().removeAll(key, map); return (T) this; } default T removeAll(final Supplier>> key, final Map map) { - this.getAsDataHolder().removeAll(key, map); + this.getAsData().removeAll(key, map); return (T) this; } default T tryOffer(final Key> key, E value) { - this.getAsDataHolder().tryOffer(key, value); + this.getAsData().tryOffer(key, value); return (T) this; } default T tryOffer(final Supplier>> key, final E value) { - this.getAsDataHolder().tryOffer(key, value); + this.getAsData().tryOffer(key, value); return (T) this; } default T tryOffer(final Value value) throws IllegalArgumentException { - this.getAsDataHolder().tryOffer(value); + this.getAsData().tryOffer(value); return (T) this; } default T remove(final Value value) { - this.getAsDataHolder().remove(value); + this.getAsData().remove(value); return (T) this; } default T remove(final Key key) { - this.getAsDataHolder().remove(key); + this.getAsData().remove(key); return (T) this; } default T remove(final Supplier> key) { - this.getAsDataHolder().remove(key); + this.getAsData().remove(key); return (T) this; } default T copyFrom(final ValueContainer that) { - this.getAsDataHolder().copyFrom(that); + this.getAsData().copyFrom(that); return (T) this; } default T copyFrom(final ValueContainer that, final MergeFunction function) { - this.getAsDataHolder().copyFrom(that, function); + this.getAsData().copyFrom(that, function); return (T) this; } } diff --git a/src/main/java/net/hellheim/spongetools/proxy/solid/data/ValueContainerProxy.java b/api/src/main/java/net/hellheim/spongetools/proxy/solid/data/ValueContainerProxy.java similarity index 68% rename from src/main/java/net/hellheim/spongetools/proxy/solid/data/ValueContainerProxy.java rename to api/src/main/java/net/hellheim/spongetools/proxy/solid/data/ValueContainerProxy.java index e9ee4de..b707e50 100644 --- a/src/main/java/net/hellheim/spongetools/proxy/solid/data/ValueContainerProxy.java +++ b/api/src/main/java/net/hellheim/spongetools/proxy/solid/data/ValueContainerProxy.java @@ -12,70 +12,70 @@ public interface ValueContainerProxy extends ValueContainer { - ValueContainer getAsDataHolder(); + ValueContainer getAsData(); @Override default Optional get(final Key> key) { - return this.getAsDataHolder().get(key); + return this.getAsData().get(key); } @Override default OptionalInt getInt(final Key> key) { - return this.getAsDataHolder().getInt(key); + return this.getAsData().getInt(key); } @Override default OptionalDouble getDouble(final Key> key) { - return this.getAsDataHolder().getDouble(key); + return this.getAsData().getDouble(key); } @Override default OptionalLong getLong(final Key> key) { - return this.getAsDataHolder().getLong(key); + return this.getAsData().getLong(key); } @Override default E require(final Key> key) { - return this.getAsDataHolder().require(key); + return this.getAsData().require(key); } @Override default E getOrNull(final Key> key) { - return this.getAsDataHolder().getOrNull(key); + return this.getAsData().getOrNull(key); } @Override default E getOrElse(final Key> key, E defaultValue) { - return this.getAsDataHolder().getOrElse(key, defaultValue); + return this.getAsData().getOrElse(key, defaultValue); } @Override default > Optional getValue(final Key key) { - return this.getAsDataHolder().getValue(key); + return this.getAsData().getValue(key); } @Override default > V requireValue(final Key key) { - return this.getAsDataHolder().requireValue(key); + return this.getAsData().requireValue(key); } @Override default boolean supports(final Key key) { - return this.getAsDataHolder().supports(key); + return this.getAsData().supports(key); } @Override default boolean supports(final Value value) { - return this.getAsDataHolder().supports(value); + return this.getAsData().supports(value); } @Override default Set> getKeys() { - return this.getAsDataHolder().getKeys(); + return this.getAsData().getKeys(); } @Override default Set> getValues() { - return this.getAsDataHolder().getValues(); + return this.getAsData().getValues(); } } diff --git a/src/main/java/net/hellheim/spongetools/proxy/solid/entity/EntityArchetypeProxy.java b/api/src/main/java/net/hellheim/spongetools/proxy/solid/entity/EntityArchetypeProxy.java similarity index 100% rename from src/main/java/net/hellheim/spongetools/proxy/solid/entity/EntityArchetypeProxy.java rename to api/src/main/java/net/hellheim/spongetools/proxy/solid/entity/EntityArchetypeProxy.java diff --git a/src/main/java/net/hellheim/spongetools/proxy/solid/entity/EntityProxy.java b/api/src/main/java/net/hellheim/spongetools/proxy/solid/entity/EntityProxy.java similarity index 100% rename from src/main/java/net/hellheim/spongetools/proxy/solid/entity/EntityProxy.java rename to api/src/main/java/net/hellheim/spongetools/proxy/solid/entity/EntityProxy.java diff --git a/src/main/java/net/hellheim/spongetools/proxy/solid/entity/EntityTypeProxy.java b/api/src/main/java/net/hellheim/spongetools/proxy/solid/entity/EntityTypeProxy.java similarity index 100% rename from src/main/java/net/hellheim/spongetools/proxy/solid/entity/EntityTypeProxy.java rename to api/src/main/java/net/hellheim/spongetools/proxy/solid/entity/EntityTypeProxy.java diff --git a/src/main/java/net/hellheim/spongetools/proxy/solid/entity/IEntityProxy.java b/api/src/main/java/net/hellheim/spongetools/proxy/solid/entity/IEntityProxy.java similarity index 100% rename from src/main/java/net/hellheim/spongetools/proxy/solid/entity/IEntityProxy.java rename to api/src/main/java/net/hellheim/spongetools/proxy/solid/entity/IEntityProxy.java diff --git a/api/src/main/java/net/hellheim/spongetools/proxy/solid/item/DirectItemStackProxy.java b/api/src/main/java/net/hellheim/spongetools/proxy/solid/item/DirectItemStackProxy.java new file mode 100644 index 0000000..53d3a3a --- /dev/null +++ b/api/src/main/java/net/hellheim/spongetools/proxy/solid/item/DirectItemStackProxy.java @@ -0,0 +1,37 @@ +package net.hellheim.spongetools.proxy.solid.item; + +import org.spongepowered.api.item.ItemType; +import org.spongepowered.api.item.inventory.ItemStack; +import org.spongepowered.api.item.inventory.ItemStackLike; +import org.spongepowered.api.item.inventory.ItemStackSnapshot; + +import net.hellheim.spongetools.object.ItemBuilder; + +public interface DirectItemStackProxy extends IItemProxy { + + ItemStack getAsDirectItemStack(); + + @Override + default ItemType getAsItemType() { + return this.getAsDirectItemStack().type(); + } + + @Override + default ItemStackSnapshot getAsItemStackSnapshot() { + return this.getAsDirectItemStack().asImmutable(); + } + + @Override + default ItemStack getAsItemStack() { + return this.getAsDirectItemStack().copy(); + } + + @Override + default ItemStackLike getAsItemStackLike() { + return this.getAsDirectItemStack(); + } + + default ItemBuilder builderOfDirectStack() { + return ItemBuilder.of(this.getAsDirectItemStack()); + } +} diff --git a/src/main/java/net/hellheim/spongetools/proxy/solid/item/IItemProxy.java b/api/src/main/java/net/hellheim/spongetools/proxy/solid/item/IItemProxy.java similarity index 100% rename from src/main/java/net/hellheim/spongetools/proxy/solid/item/IItemProxy.java rename to api/src/main/java/net/hellheim/spongetools/proxy/solid/item/IItemProxy.java diff --git a/src/main/java/net/hellheim/spongetools/proxy/solid/item/ItemStackProxy.java b/api/src/main/java/net/hellheim/spongetools/proxy/solid/item/ItemStackProxy.java similarity index 100% rename from src/main/java/net/hellheim/spongetools/proxy/solid/item/ItemStackProxy.java rename to api/src/main/java/net/hellheim/spongetools/proxy/solid/item/ItemStackProxy.java diff --git a/src/main/java/net/hellheim/spongetools/proxy/solid/item/ItemStackSnapshotProxy.java b/api/src/main/java/net/hellheim/spongetools/proxy/solid/item/ItemStackSnapshotProxy.java similarity index 73% rename from src/main/java/net/hellheim/spongetools/proxy/solid/item/ItemStackSnapshotProxy.java rename to api/src/main/java/net/hellheim/spongetools/proxy/solid/item/ItemStackSnapshotProxy.java index 49dfca4..1d092aa 100644 --- a/src/main/java/net/hellheim/spongetools/proxy/solid/item/ItemStackSnapshotProxy.java +++ b/api/src/main/java/net/hellheim/spongetools/proxy/solid/item/ItemStackSnapshotProxy.java @@ -5,6 +5,9 @@ import org.spongepowered.api.item.ItemType; import org.spongepowered.api.item.inventory.ItemStack; import org.spongepowered.api.item.inventory.ItemStackLike; +import org.spongepowered.api.item.inventory.ItemStackSnapshot; + +import com.google.common.base.Suppliers; import net.hellheim.spongetools.object.CachedSupplier; import net.hellheim.spongetools.object.ItemBuilder; @@ -20,6 +23,15 @@ static ItemStackSnapshotProxy wrapTypeCached(final Supplier typeSuppli return CachedSupplier.of(() -> ItemUtil.snapshotOf(typeSupplier))::get; } + static ItemStackSnapshotProxy of(final ItemStackLike stack) { + final ItemStackSnapshot snapshot = stack.asImmutable(); + return () -> snapshot; + } + + static ItemStackSnapshotProxy of(final Supplier stack) { + return Suppliers.memoize(() -> stack.get().asImmutable())::get; + } + @Override default ItemType getAsItemType() { return this.getAsItemStackSnapshot().type(); diff --git a/src/main/java/net/hellheim/spongetools/proxy/solid/item/ItemTypeProxy.java b/api/src/main/java/net/hellheim/spongetools/proxy/solid/item/ItemTypeProxy.java similarity index 100% rename from src/main/java/net/hellheim/spongetools/proxy/solid/item/ItemTypeProxy.java rename to api/src/main/java/net/hellheim/spongetools/proxy/solid/item/ItemTypeProxy.java diff --git a/api/src/main/java/net/hellheim/spongetools/resourcepack/ItemTransform.java b/api/src/main/java/net/hellheim/spongetools/resourcepack/ItemTransform.java new file mode 100644 index 0000000..c270369 --- /dev/null +++ b/api/src/main/java/net/hellheim/spongetools/resourcepack/ItemTransform.java @@ -0,0 +1,106 @@ +package net.hellheim.spongetools.resourcepack; + +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; +import java.util.function.Supplier; + +import org.spongepowered.api.entity.display.ItemDisplayType; +import org.spongepowered.api.util.CopyableBuilder; +import org.spongepowered.api.util.Transform; + +import com.mojang.serialization.Codec; + +import net.hellheim.spongetools.codec.list.MathCodecs; +import net.hellheim.spongetools.codec.list.StringRepresentableCodecs; + +public record ItemTransform(Map transforms) { + + public static final ItemTransform DEFAULT = new ItemTransform(Map.of()); + + public static final Codec CODEC = + Codec.unboundedMap(StringRepresentableCodecs.ITEM_DISPLAY_TYPE, MathCodecs.TRANSFORM) + .xmap(ItemTransform::new, ItemTransform::transforms); + + public ItemTransform(final Map transforms) { + this.transforms = Map.copyOf(transforms); + } + + public static Builder builder() { + return new Builder(); + } + + public static class Builder implements + org.spongepowered.api.util.Builder, + CopyableBuilder { + + private final Map transforms = new HashMap<>(); + + public Builder put(final ItemDisplayType display, final Transform transform) { + this.transforms.put( + Objects.requireNonNull(display, "display"), + Objects.requireNonNull(transform, "transform")); + return this; + } + + public Builder put(final Supplier display, final Transform transform) { + return this.put(display.get(), transform); + } + + public Builder putIfAbsent(final ItemDisplayType display, final Transform transform) { + this.transforms.putIfAbsent( + Objects.requireNonNull(display, "display"), + Objects.requireNonNull(transform, "transform")); + return this; + } + + public Builder putIfAbsent(final Supplier display, final Transform transform) { + return this.putIfAbsent(display.get(), transform); + } + + public Builder putAll(final Map transforms) { + Objects.requireNonNull(transforms, "transforms").forEach(this::put); + return this; + } + + public Builder putAll(final Transform transform, final ItemDisplayType... displays) { + for (final ItemDisplayType display : Objects.requireNonNull(displays, "displays")) { + this.put(display, transform); + } + return this; + } + + @SuppressWarnings("unchecked") + public Builder putAll(final Transform transform, final Supplier... displays) { + for (final Supplier display : Objects.requireNonNull(displays, "displays")) { + this.put(display, transform); + } + return this; + } + + public Builder putAll(final Transform transform, final Iterable displays) { + for (final ItemDisplayType display : Objects.requireNonNull(displays, "displays")) { + this.put(display, transform); + } + return this; + } + + @Override + public Builder from(final ItemTransform value) { + this.reset(); + this.transforms.putAll(value.transforms); + return this; + } + + @Override + public Builder reset() { + this.transforms.clear(); + return this; + } + + @Override + public ItemTransform build() { + return new ItemTransform(this.transforms); + } + } +} diff --git a/api/src/main/java/net/hellheim/spongetools/resourcepack/Model.java b/api/src/main/java/net/hellheim/spongetools/resourcepack/Model.java new file mode 100644 index 0000000..fa1a18a --- /dev/null +++ b/api/src/main/java/net/hellheim/spongetools/resourcepack/Model.java @@ -0,0 +1,209 @@ +package net.hellheim.spongetools.resourcepack; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; +import java.util.Objects; +import java.util.function.Consumer; +import java.util.stream.Stream; + +import org.checkerframework.checker.nullness.qual.Nullable; +import org.spongepowered.api.registry.DefaultedRegistryType; +import org.spongepowered.api.util.CopyableBuilder; + +import com.mojang.serialization.Codec; +import com.mojang.serialization.codecs.RecordCodecBuilder; + +import net.hellheim.spongetools.SpongeTools; +import net.hellheim.spongetools.codec.list.RegistryCodecs; +import net.hellheim.spongetools.resourcepack.util.GuiLight; + +// TODO make ModelTemplate optional (?) +/** + * @see
Minecraft Wiki + */ +public record Model(TexturedModel model, ItemTransform display, List parts, GuiLight guiLight, boolean ambientOcclusion) + implements ModelLike { + + public static final boolean DEFAULT_AMBIENT_OCCLUSION = true; + + public static final Codec CODEC = RecordCodecBuilder.create( + instance -> instance.group( + TexturedModel.MAP_CODEC.forGetter(Model::model), + ItemTransform.CODEC.optionalFieldOf("display", ItemTransform.DEFAULT).forGetter(Model::display), + ModelPart.CODEC.listOf().optionalFieldOf("elements", List.of()).forGetter(Model::parts), + GuiLight.CODEC.optionalFieldOf("gui_light", GuiLight.DEFAULT).forGetter(Model::guiLight), + Codec.BOOL.optionalFieldOf("ambientocclusion", Model.DEFAULT_AMBIENT_OCCLUSION).forGetter(Model::ambientOcclusion) + ).apply(instance, Model::new)); + + public Model( + final TexturedModel model, + final ItemTransform display, final List parts, + final GuiLight guiLight, final boolean ambientOcclusion + ) { + this.model = Objects.requireNonNull(model, "model"); + this.parts = Objects.requireNonNull(parts, "parts"); + this.display = Objects.requireNonNull(display, "display"); + this.guiLight = Objects.requireNonNull(guiLight, "guiLight"); + this.ambientOcclusion = ambientOcclusion; + } + + public static DefaultedRegistryType registry() { + return SpongeTools.Registries.MODEL; + } + + public static Codec registryCodec() { + return RegistryCodecs.MODEL; + } + + public static Model of(final ModelTemplate parent, final Textures textures, final ModelPart... parts) { + return Model.of(TexturedModel.of(parent, textures), parts); + } + + public static Model of(final ModelTemplate parent, final Textures textures, final ItemTransform display, final ModelPart... parts) { + return Model.of(TexturedModel.of(parent, textures), display, parts); + } + + public static Model of(final TexturedModel model, final ModelPart... parts) { + return Model.of(model, ItemTransform.DEFAULT, parts); + } + + public static Model of(final TexturedModel model, final ItemTransform display, final ModelPart... parts) { + return Model.builder().model(model).display(display).parts(parts).build(); + } + + public static Builder builder() { + return new Builder(); + } + + public ModelTemplate parent() { + return this.model().parent(); + } + + public Textures textures() { + return this.model().textures(); + } + + @Override + public Model asModel() { + return this; + } + + public static final class Builder implements + org.spongepowered.api.util.Builder, + CopyableBuilder { + + private @Nullable ModelTemplate parent; + private @Nullable Textures textures; + private ItemTransform display; + private GuiLight guiLight; + private boolean ambientOcclusion; + private final List parts = new ArrayList<>(); + + private Builder() { + this.reset(); + } + + public Builder display(final ItemTransform display) { + this.display = Objects.requireNonNull(display, "display"); + return this; + } + + public Builder display(final ItemTransform.Builder builder) { + return this.display(Objects.requireNonNull(builder, "builder").build()); + } + + public Builder display(final Consumer configurator) { + final ItemTransform.Builder builder = ItemTransform.builder(); + Objects.requireNonNull(configurator, "configurator").accept(builder); + return this.display(builder); + } + + public Builder model(final TexturedModel model) { + Objects.requireNonNull(model, "model"); + return this.parent(model.parent()).textures(model.textures()); + } + + public Builder parent(final ModelTemplate parent) { + this.parent = Objects.requireNonNull(parent, "parent"); + return this; + } + + public Builder textures(final Textures textures) { + this.textures = Objects.requireNonNull(textures, "textures"); + return this; + } + + public Builder textures(final Textures.Builder builder) { + return this.textures(Objects.requireNonNull(builder, "builder").build()); + } + + public Builder textures(final Consumer configurator) { + final Textures.Builder builder = Textures.builder(); + Objects.requireNonNull(configurator, "configurator").accept(builder); + return this.textures(builder); + } + + public Builder light(final GuiLight guiLight) { + this.guiLight = Objects.requireNonNull(guiLight, "guiLight"); + return this; + } + + public Builder occlusion(final boolean ambientOcclusion) { + this.ambientOcclusion = ambientOcclusion; + return this; + } + + public Builder parts(final ModelPart.Builder... builders) { + return this.parts(Arrays.stream(Objects.requireNonNull(builders, "builders")).map(ModelPart.Builder::build)); + } + + public Builder parts(final ModelPart... parts) { + return this.parts(Arrays.stream(Objects.requireNonNull(parts, "parts"))); + } + + public Builder parts(final Collection parts) { + return this.parts(Objects.requireNonNull(parts, "parts").stream()); + } + + private Builder parts(final Stream parts) { + parts.forEach(part -> this.parts.add(Objects.requireNonNull(part, "part"))); + return this; + } + + @Override + public Builder from(final Model model) { + return this.model(model.model()) + .display(model.display()) + .light(model.guiLight()) + .occlusion(model.ambientOcclusion()) + .parts(model.parts()); + } + + @Override + public Builder reset() { + this.parent = null; + this.textures = null; + this.display = ItemTransform.DEFAULT; + this.guiLight = GuiLight.DEFAULT; + this.ambientOcclusion = Model.DEFAULT_AMBIENT_OCCLUSION; + this.parts.clear(); + return this; + } + + @Override + public Model build() { + if (this.parent == null) { + throw new IllegalStateException("parent must be set"); + } else if (this.textures == null) { + throw new IllegalStateException("textures must be set"); + } + + return new Model( + TexturedModel.of(this.parent, this.textures), + this.display, this.parts, + this.guiLight, this.ambientOcclusion); + } + } +} diff --git a/api/src/main/java/net/hellheim/spongetools/resourcepack/ModelLike.java b/api/src/main/java/net/hellheim/spongetools/resourcepack/ModelLike.java new file mode 100644 index 0000000..d1bbdaf --- /dev/null +++ b/api/src/main/java/net/hellheim/spongetools/resourcepack/ModelLike.java @@ -0,0 +1,6 @@ +package net.hellheim.spongetools.resourcepack; + +public interface ModelLike { + + Model asModel(); +} diff --git a/api/src/main/java/net/hellheim/spongetools/resourcepack/ModelPart.java b/api/src/main/java/net/hellheim/spongetools/resourcepack/ModelPart.java new file mode 100644 index 0000000..b4fc1be --- /dev/null +++ b/api/src/main/java/net/hellheim/spongetools/resourcepack/ModelPart.java @@ -0,0 +1,199 @@ +package net.hellheim.spongetools.resourcepack; + +import java.util.EnumMap; +import java.util.Map; +import java.util.Objects; + +import org.checkerframework.checker.nullness.qual.Nullable; +import org.spongepowered.api.util.CopyableBuilder; +import org.spongepowered.api.util.Direction; +import org.spongepowered.math.vector.Vector3d; + +import com.mojang.serialization.Codec; +import com.mojang.serialization.DataResult; +import com.mojang.serialization.codecs.RecordCodecBuilder; + +import net.hellheim.spongetools.codec.list.MathCodecs; +import net.hellheim.spongetools.codec.list.SpongeCodecs; + +public final class ModelPart { + + public static final boolean DEFAULT_SHADE = true; + public static final int DEFAULT_LIGHT_EMISSION = 0; + public static final Codec CODEC = RecordCodecBuilder.create( + instance -> instance.group( + MathCodecs.VECTOR3D.validate(ModelPart::validatePoint).fieldOf("from").forGetter(ModelPart::from), + MathCodecs.VECTOR3D.validate(ModelPart::validatePoint).fieldOf("to").forGetter(ModelPart::from), + ModelPartRotation.CODEC.fieldOf("rotation").forGetter(ModelPart::rotation), + Codec.BOOL.optionalFieldOf("shade", ModelPart.DEFAULT_SHADE).forGetter(ModelPart::shade), + Codec.INT.validate(ModelPart::validateLight).optionalFieldOf("light_emission", ModelPart.DEFAULT_LIGHT_EMISSION).forGetter(ModelPart::lightEmission), + Codec.unboundedMap(SpongeCodecs.CARDINAL_DIRECTION, ModelPartFace.CODEC).optionalFieldOf("faces", Map.of()).forGetter(ModelPart::faces) + ).apply(instance, ModelPart::new)); + + private final Vector3d from; + private final Vector3d to; + private final ModelPartRotation rotation; + private final boolean shade; + private final int lightEmission; + private final Map faces; + + private ModelPart( + final Vector3d from, final Vector3d to, final ModelPartRotation rotation, + final boolean shade, final int lightEmission, final Map faces + ) { + this.from = from; + this.to = to; + this.rotation = rotation; + this.shade = shade; + this.lightEmission = lightEmission; + this.faces = Map.copyOf(faces); + } + + public static Builder builder() { + return new Builder(); + } + + private static DataResult validatePoint(final Vector3d vec) { + return ModelPart.validatePointValue(vec, vec.x()) + .flatMap($ -> ModelPart.validatePointValue(vec, vec.y())) + .flatMap($ -> ModelPart.validatePointValue(vec, vec.z())); + } + + private static DataResult validatePointValue(final Vector3d vec, final double value) { + return value < -16.0D || value > 32.0D + ? DataResult.error(() -> "Point values must be in [-16;32] range: " + vec.toString()) + : DataResult.success(vec); + } + + private static DataResult validateLight(final int lightEmission) { + return lightEmission < 0 || lightEmission > 15 + ? DataResult.error(() -> "Light emission must be between 0 and 15: " + lightEmission) + : DataResult.success(lightEmission); + } + + public Vector3d from() { + return this.from; + } + + public Vector3d to() { + return this.to; + } + + public ModelPartRotation rotation() { + return this.rotation; + } + + public boolean shade() { + return this.shade; + } + + public int lightEmission() { + return this.lightEmission; + } + + public Map faces() { + return this.faces; + } + + public static final class Builder implements + org.spongepowered.api.util.Builder, + CopyableBuilder { + + private @Nullable Vector3d from; + private @Nullable Vector3d to; + private @Nullable ModelPartRotation rotation; + private boolean shade; + private int lightEmission; + private final Map faces = new EnumMap<>(Direction.class); + + private Builder() { + this.reset(); + } + + private Vector3d validatePoint(final Vector3d vec) { + return ModelPart.validatePoint(vec).getOrThrow(IllegalArgumentException::new); + } + + private int validateLight(final int lightEmission) { + return ModelPart.validateLight(lightEmission).getOrThrow(IllegalArgumentException::new); + } + + public Builder from(final Vector3d from) { + this.from = this.validatePoint(Objects.requireNonNull(from, "from")); + return this; + } + + public Builder to(final Vector3d to) { + this.to = this.validatePoint(Objects.requireNonNull(to, "to")); + return this; + } + + public Builder rotation(final ModelPartRotation rotation) { + this.rotation = Objects.requireNonNull(rotation, "rotation"); + return this; + } + + public Builder shade(final boolean shade) { + this.shade = shade; + return this; + } + + public Builder lightEmission(final int lightEmission) { + this.lightEmission = this.validateLight(lightEmission); + return this; + } + + public Builder face(final Direction direction, final ModelPartFace face) { + Objects.requireNonNull(direction, "direction"); + Objects.requireNonNull(face, "face"); + if (!direction.isCardinal()) { + throw new IllegalArgumentException("direction must be cardinal"); + } + + this.faces.put(direction, face); + return this; + } + + public Builder faces(final Map faces) { + Objects.requireNonNull(faces, "faces").forEach(this::face); + return this; + } + + @Override + public Builder from(final ModelPart element) { + Objects.requireNonNull(element, "element"); + return this + .from(element.from()) + .to(element.to()) + .rotation(element.rotation()) + .shade(element.shade()) + .lightEmission(element.lightEmission()) + .faces(element.faces()); + } + + @Override + public Builder reset() { + this.from = null; + this.to = null; + this.rotation = null; + this.shade = ModelPart.DEFAULT_SHADE; + this.lightEmission = ModelPart.DEFAULT_LIGHT_EMISSION; + this.faces.clear(); + return this; + } + + @Override + public ModelPart build() { + if (this.from == null) { + throw new IllegalStateException("from must be set"); + } else if (this.to == null) { + throw new IllegalStateException("to must be set"); + } else if (this.rotation == null) { + throw new IllegalStateException("rotation must be set"); + } + + return new ModelPart( + this.from, this.to, this.rotation, this.shade, this.lightEmission, this.faces); + } + } +} diff --git a/api/src/main/java/net/hellheim/spongetools/resourcepack/ModelPartFace.java b/api/src/main/java/net/hellheim/spongetools/resourcepack/ModelPartFace.java new file mode 100644 index 0000000..165e17e --- /dev/null +++ b/api/src/main/java/net/hellheim/spongetools/resourcepack/ModelPartFace.java @@ -0,0 +1,166 @@ +package net.hellheim.spongetools.resourcepack; + +import java.util.Objects; +import java.util.Optional; +import java.util.function.Supplier; + +import org.checkerframework.checker.nullness.qual.Nullable; +import org.spongepowered.api.util.CopyableBuilder; +import org.spongepowered.api.util.Direction; +import org.spongepowered.api.util.rotation.Rotation; +import org.spongepowered.math.matrix.Matrix2d; +import org.spongepowered.math.vector.Vector2d; + +import com.mojang.serialization.Codec; +import com.mojang.serialization.codecs.RecordCodecBuilder; + +import net.hellheim.spongetools.codec.list.MathCodecs; +import net.hellheim.spongetools.codec.list.SpongeCodecs; +import net.hellheim.spongetools.util.GeomUtil; + +public final class ModelPartFace { + + public static final int DEFAULT_TINT = -1; + public static final Supplier DEFAULT_ROTATION = GeomUtil.ROT_0; + public static final Codec CODEC = Codec.lazyInitialized(() -> RecordCodecBuilder.create( + instance -> instance.group( + TextureSlot.CODEC_HASHED.fieldOf("texture").forGetter(ModelPartFace::texture), + MathCodecs.MATRIX2D.optionalFieldOf("uv").forGetter(ModelPartFace::uv), + SpongeCodecs.CARDINAL_DIRECTION.optionalFieldOf("cullface").forGetter(ModelPartFace::cullface), + SpongeCodecs.ROTATION_BY_ANGLE.optionalFieldOf("rotation", ModelPartFace.DEFAULT_ROTATION.get()).forGetter(ModelPartFace::rotation), + Codec.INT.optionalFieldOf("tintindex", ModelPartFace.DEFAULT_TINT).forGetter(ModelPartFace::tintIndex) + ).apply(instance, ModelPartFace::new))); + + private final TextureSlot texture; + private final Optional uv; + private final Optional cullface; + private final Rotation rotation; + private final int tintIndex; + + private ModelPartFace( + final TextureSlot texture, + final Optional uv, final Optional cullface, + final Rotation rotation, final int tintIndex + ) { + this.texture = texture; + this.uv = uv; + this.cullface = cullface; + this.rotation = rotation; + this.tintIndex = tintIndex; + } + + public static Builder builder() { + return new Builder(); + } + + public TextureSlot texture() { + return this.texture; + } + + public Optional uv() { + return this.uv; + } + + public Optional cullface() { + return this.cullface; + } + + public Rotation rotation() { + return this.rotation; + } + + public int tintIndex() { + return this.tintIndex; + } + + public static final class Builder implements + org.spongepowered.api.util.Builder, + CopyableBuilder { + + private @Nullable Matrix2d uv; + private @Nullable TextureSlot texture; + private @Nullable Direction cullface; + private @Nullable Rotation rotation; + private int tintIndex; + + private Builder() { + this.reset(); + } + + public Builder texture(final String textureId) { + return this.texture(TextureSlot.of(textureId)); + } + + public Builder texture(final TextureSlot texture) { + this.texture = Objects.requireNonNull(texture, "texture"); + return this; + } + + public Builder uv(final double x1, final double y1, final double x2, final double y2) { + return this.uv(new Matrix2d(x1, y1, x2, y2)); + } + + public Builder uv(final Vector2d u, final Vector2d v) { + return this.uv(new Matrix2d(u.x(), u.y(), v.x(), v.y())); + } + + public Builder uv(final Matrix2d uv) { + this.uv = Objects.requireNonNull(uv, "uv"); + return this; + } + + public Builder cullface(final Direction cullface) { + Objects.requireNonNull(cullface, "cullface"); + if (!cullface.isCardinal()) { + throw new IllegalArgumentException("cullface must be cardinal"); + } + + this.cullface = cullface; + return this; + } + + public Builder rotation(final Rotation rotation) { + this.rotation = Objects.requireNonNull(rotation, "rotation"); + return this; + } + + public Builder rotation(final Supplier rotationSupplier) { + return this.rotation(Objects.requireNonNull(rotationSupplier, "rotationSupplier").get()); + } + + public Builder tint(final int tintIndex) { + this.tintIndex = tintIndex; + return this; + } + + @Override + public Builder from(final ModelPartFace face) { + Objects.requireNonNull(face, "face"); + this.reset(); + face.uv().ifPresent(this::uv); + face.cullface.ifPresent(this::cullface); + return this + .texture(face.texture()) + .rotation(face.rotation()) + .tint(face.tintIndex()); + } + + @Override + public Builder reset() { + this.texture = null; + this.uv = null; + this.cullface = null; + this.rotation = ModelPartFace.DEFAULT_ROTATION.get(); + this.tintIndex = ModelPartFace.DEFAULT_TINT; + return this; + } + + @Override + public ModelPartFace build() { + return new ModelPartFace( + this.texture, + Optional.ofNullable(this.uv), Optional.ofNullable(this.cullface), + this.rotation, this.tintIndex); + } + } +} diff --git a/api/src/main/java/net/hellheim/spongetools/resourcepack/ModelPartRotation.java b/api/src/main/java/net/hellheim/spongetools/resourcepack/ModelPartRotation.java new file mode 100644 index 0000000..7ed4061 --- /dev/null +++ b/api/src/main/java/net/hellheim/spongetools/resourcepack/ModelPartRotation.java @@ -0,0 +1,92 @@ +package net.hellheim.spongetools.resourcepack; + +import java.util.Objects; + +import org.spongepowered.api.util.Axis; +import org.spongepowered.math.vector.Vector3d; + +import com.mojang.serialization.Codec; +import com.mojang.serialization.DataResult; +import com.mojang.serialization.codecs.RecordCodecBuilder; + +import net.hellheim.spongetools.codec.list.MathCodecs; +import net.hellheim.spongetools.codec.list.SpongeCodecs; + +public final class ModelPartRotation { + + public static final Codec CODEC = RecordCodecBuilder.create( + instance -> instance.group( + MathCodecs.VECTOR3D.fieldOf("origin").forGetter(ModelPartRotation::origin), + SpongeCodecs.AXIS.fieldOf("axis").forGetter(ModelPartRotation::axis), + Codec.DOUBLE.fieldOf("angle").forGetter(ModelPartRotation::angle), + Codec.BOOL.fieldOf("rescale").forGetter(ModelPartRotation::rescale) + ).apply(instance, ModelPartRotation::new)) + .validate(ModelPartRotation::validate); + + private final Vector3d origin; + private final Axis axis; + private final double angle; + private final boolean rescale; + + private ModelPartRotation( + final Vector3d origin, final Axis axis, final double angle, final boolean rescale + ) { + this.origin = origin; + this.axis = axis; + this.angle = angle; + this.rescale = rescale; + } + + public static ModelPartRotation of( + final Vector3d origin, final Axis axis, final double angle, final boolean rescale + ) { + Objects.requireNonNull(origin, "origin"); + Objects.requireNonNull(axis, "axis"); + final ModelPartRotation rotation = new ModelPartRotation(origin, axis, angle, rescale); + rotation.validate().ifError(error -> { + throw new IllegalArgumentException(error.message()); + }); + return rotation; + } + + private DataResult validate() { + if (this.angle != 0.0F && Math.abs(this.angle) != 22.5D && Math.abs(this.angle) != 45.0D) { + return DataResult.error(() -> + "Invalid angle " + this.angle + ", only -45/-22.5/0/22.5/45 allowed"); + } else { + return DataResult.success(this); + } + } + + public Vector3d origin() { + return this.origin; + } + + public Axis axis() { + return this.axis; + } + + public double angle() { + return this.angle; + } + + public boolean rescale() { + return this.rescale; + } + + public ModelPartRotation withOrigin(final Vector3d origin) { + return ModelPartRotation.of(origin, this.axis, this.angle, this.rescale); + } + + public ModelPartRotation withAxis(final Axis axis) { + return ModelPartRotation.of(this.origin, axis, this.angle, this.rescale); + } + + public ModelPartRotation withAngle(final double angle) { + return ModelPartRotation.of(this.origin, this.axis, angle, this.rescale); + } + + public ModelPartRotation withRescale(final boolean rescale) { + return ModelPartRotation.of(this.origin, this.axis, this.angle, rescale); + } +} diff --git a/api/src/main/java/net/hellheim/spongetools/resourcepack/ModelTemplate.java b/api/src/main/java/net/hellheim/spongetools/resourcepack/ModelTemplate.java new file mode 100644 index 0000000..2da4676 --- /dev/null +++ b/api/src/main/java/net/hellheim/spongetools/resourcepack/ModelTemplate.java @@ -0,0 +1,35 @@ +package net.hellheim.spongetools.resourcepack; + +import java.util.Collection; +import java.util.List; +import java.util.Objects; +import java.util.Optional; +import java.util.stream.Collectors; + +import org.spongepowered.api.ResourceKey; +import org.spongepowered.api.util.annotation.CatalogedBy; + +import com.google.common.collect.LinkedHashMultimap; +import com.google.common.collect.Multimap; + +@CatalogedBy(ModelTemplates.class) +public record ModelTemplate(Optional key, Optional suffix, List slots) { + + private static final Multimap MULTIMAP = LinkedHashMultimap.create(); + + public ModelTemplate( + final Optional key, + final Optional suffix, + final List slots + ) { + this.key = Objects.requireNonNull(key, "key"); + this.suffix = Objects.requireNonNull(suffix, "suffix"); + this.slots = slots.stream().distinct().collect(Collectors.toUnmodifiableList()); + + this.key.ifPresent(k -> ModelTemplate.MULTIMAP.put(k, this)); + } + + public static Collection get(final ResourceKey key) { + return ModelTemplate.MULTIMAP.get(Objects.requireNonNull(key, "key")); + } +} diff --git a/api/src/main/java/net/hellheim/spongetools/resourcepack/ModelTemplateProvider.java b/api/src/main/java/net/hellheim/spongetools/resourcepack/ModelTemplateProvider.java new file mode 100644 index 0000000..eef11ee --- /dev/null +++ b/api/src/main/java/net/hellheim/spongetools/resourcepack/ModelTemplateProvider.java @@ -0,0 +1,834 @@ +package net.hellheim.spongetools.resourcepack; + +import java.util.List; +import java.util.Objects; +import java.util.Optional; +import java.util.function.Supplier; + +import org.spongepowered.api.ResourceKey; +import org.spongepowered.api.util.annotation.CatalogedBy; + +@CatalogedBy(ModelTemplates.class) +public interface ModelTemplateProvider extends Supplier { + + private static ModelTemplate validate(final ModelTemplate template, final int size) { + final int slots = Objects.requireNonNull(template, "template").slots().size(); + if (slots != size) { + throw new IllegalArgumentException("Template must have " + size + " slots, " + slots + " provided."); + } + + return template; + } + + static ModelTemplateProvider.T1 t1(final ModelTemplate template) { + ModelTemplateProvider.validate(template, 1); + return () -> template; + } + + static ModelTemplateProvider.T2 t2(final ModelTemplate template) { + ModelTemplateProvider.validate(template, 2); + return () -> template; + } + + static ModelTemplateProvider.T3 t3(final ModelTemplate template) { + ModelTemplateProvider.validate(template, 3); + return () -> template; + } + + static ModelTemplateProvider.T4 t4(final ModelTemplate template) { + ModelTemplateProvider.validate(template, 4); + return () -> template; + } + + static ModelTemplateProvider.T5 t5(final ModelTemplate template) { + ModelTemplateProvider.validate(template, 5); + return () -> template; + } + + static ModelTemplateProvider.T6 t6(final ModelTemplate template) { + ModelTemplateProvider.validate(template, 6); + return () -> template; + } + + static ModelTemplateProvider.T7 t7(final ModelTemplate template) { + ModelTemplateProvider.validate(template, 7); + return () -> template; + } + + + + default Optional key() { + return this.get().key(); + } + + default Optional suffix() { + return this.get().suffix(); + } + + default List slots() { + return this.get().slots(); + } + + public interface T1 extends ModelTemplateProvider { + + default TextureSlot slot() { + return this.slots().get(0); + } + + default TexturedModel textured( + final ResourceKey texture + ) { + return TexturedModel.of(this, Textures.of( + this.slot(), texture + )); + } + + default TexturedModel textured( + final ResourceKey texture, + final String suffix + ) { + return TexturedModel.of(this, Textures.of( + texture, + this.slot(), suffix + )); + } + + default TexturedModel texturedBlock( + final ResourceKey texture + ) { + return TexturedModel.of(this, Textures.block( + this.slot(), texture + )); + } + + default TexturedModel texturedBlock( + final ResourceKey texture, + final String suffix + ) { + return TexturedModel.of(this, Textures.block( + texture, + this.slot(), suffix + )); + } + + default TexturedModel texturedItem( + final ResourceKey texture + ) { + return TexturedModel.of(this, Textures.item( + this.slot(), texture + )); + } + + default TexturedModel texturedItem( + final ResourceKey texture, + final String suffix + ) { + return TexturedModel.of(this, Textures.item( + texture, + this.slot(), suffix + )); + } + } + + public interface T2 extends ModelTemplateProvider { + + default TextureSlot slot1() { + return this.slots().get(0); + } + + default TextureSlot slot2() { + return this.slots().get(1); + } + + default TexturedModel textured( + final ResourceKey texture1, + final ResourceKey texture2 + ) { + return TexturedModel.of(this, Textures.of( + this.slot1(), texture1, + this.slot2(), texture2 + )); + } + + default TexturedModel textured( + final ResourceKey texture, + final String suffix1, + final String suffix2 + ) { + return TexturedModel.of(this, Textures.of( + texture, + this.slot1(), suffix1, + this.slot2(), suffix2 + )); + } + + default TexturedModel texturedBlock( + final ResourceKey texture1, + final ResourceKey texture2 + ) { + return TexturedModel.of(this, Textures.block( + this.slot1(), texture1, + this.slot2(), texture2 + )); + } + + default TexturedModel texturedBlock( + final ResourceKey texture, + final String suffix1, + final String suffix2 + ) { + return TexturedModel.of(this, Textures.block( + texture, + this.slot1(), suffix1, + this.slot2(), suffix2 + )); + } + + default TexturedModel texturedItem( + final ResourceKey texture1, + final ResourceKey texture2 + ) { + return TexturedModel.of(this, Textures.item( + this.slot1(), texture1, + this.slot2(), texture2 + )); + } + + default TexturedModel texturedItem( + final ResourceKey texture, + final String suffix1, + final String suffix2 + ) { + return TexturedModel.of(this, Textures.item( + texture, + this.slot1(), suffix1, + this.slot2(), suffix2 + )); + } + } + + public interface T3 extends ModelTemplateProvider { + + default TextureSlot slot1() { + return this.slots().get(0); + } + + default TextureSlot slot2() { + return this.slots().get(1); + } + + default TextureSlot slot3() { + return this.slots().get(2); + } + + default TexturedModel textured( + final ResourceKey texture1, + final ResourceKey texture2, + final ResourceKey texture3 + ) { + return TexturedModel.of(this, Textures.of( + this.slot1(), texture1, + this.slot2(), texture2, + this.slot3(), texture3 + )); + } + + default TexturedModel textured( + final ResourceKey texture, + final String suffix1, + final String suffix2, + final String suffix3 + ) { + return TexturedModel.of(this, Textures.of( + texture, + this.slot1(), suffix1, + this.slot2(), suffix2, + this.slot3(), suffix3 + )); + } + + default TexturedModel texturedBlock( + final ResourceKey texture1, + final ResourceKey texture2, + final ResourceKey texture3 + ) { + return TexturedModel.of(this, Textures.block( + this.slot1(), texture1, + this.slot2(), texture2, + this.slot3(), texture3 + )); + } + + default TexturedModel texturedBlock( + final ResourceKey texture, + final String suffix1, + final String suffix2, + final String suffix3 + ) { + return TexturedModel.of(this, Textures.block( + texture, + this.slot1(), suffix1, + this.slot2(), suffix2, + this.slot3(), suffix3 + )); + } + + default TexturedModel texturedItem( + final ResourceKey texture1, + final ResourceKey texture2, + final ResourceKey texture3 + ) { + return TexturedModel.of(this, Textures.item( + this.slot1(), texture1, + this.slot2(), texture2, + this.slot3(), texture3 + )); + } + + default TexturedModel texturedItem( + final ResourceKey texture, + final String suffix1, + final String suffix2, + final String suffix3 + ) { + return TexturedModel.of(this, Textures.item( + texture, + this.slot1(), suffix1, + this.slot2(), suffix2, + this.slot3(), suffix3 + )); + } + } + + public interface T4 extends ModelTemplateProvider { + + default TextureSlot slot1() { + return this.slots().get(0); + } + + default TextureSlot slot2() { + return this.slots().get(1); + } + + default TextureSlot slot3() { + return this.slots().get(2); + } + + default TextureSlot slot4() { + return this.slots().get(3); + } + + default TexturedModel textured( + final ResourceKey texture1, + final ResourceKey texture2, + final ResourceKey texture3, + final ResourceKey texture4 + ) { + return TexturedModel.of(this, Textures.of( + this.slot1(), texture1, + this.slot2(), texture2, + this.slot3(), texture3, + this.slot4(), texture4 + )); + } + + default TexturedModel textured( + final ResourceKey texture, + final String suffix1, + final String suffix2, + final String suffix3, + final String suffix4 + ) { + return TexturedModel.of(this, Textures.of( + texture, + this.slot1(), suffix1, + this.slot2(), suffix2, + this.slot3(), suffix3, + this.slot4(), suffix4 + )); + } + + default TexturedModel texturedBlock( + final ResourceKey texture1, + final ResourceKey texture2, + final ResourceKey texture3, + final ResourceKey texture4 + ) { + return TexturedModel.of(this, Textures.block( + this.slot1(), texture1, + this.slot2(), texture2, + this.slot3(), texture3, + this.slot4(), texture4 + )); + } + + default TexturedModel texturedBlock( + final ResourceKey texture, + final String suffix1, + final String suffix2, + final String suffix3, + final String suffix4 + ) { + return TexturedModel.of(this, Textures.block( + texture, + this.slot1(), suffix1, + this.slot2(), suffix2, + this.slot3(), suffix3, + this.slot4(), suffix4 + )); + } + + default TexturedModel texturedItem( + final ResourceKey texture1, + final ResourceKey texture2, + final ResourceKey texture3, + final ResourceKey texture4 + ) { + return TexturedModel.of(this, Textures.item( + this.slot1(), texture1, + this.slot2(), texture2, + this.slot3(), texture3, + this.slot4(), texture4 + )); + } + + default TexturedModel texturedItem( + final ResourceKey texture, + final String suffix1, + final String suffix2, + final String suffix3, + final String suffix4 + ) { + return TexturedModel.of(this, Textures.item( + texture, + this.slot1(), suffix1, + this.slot2(), suffix2, + this.slot3(), suffix3, + this.slot4(), suffix4 + )); + } + } + + public interface T5 extends ModelTemplateProvider { + + default TextureSlot slot1() { + return this.slots().get(0); + } + + default TextureSlot slot2() { + return this.slots().get(1); + } + + default TextureSlot slot3() { + return this.slots().get(2); + } + + default TextureSlot slot4() { + return this.slots().get(3); + } + + default TextureSlot slot5() { + return this.slots().get(4); + } + + default TexturedModel textured( + final ResourceKey texture1, + final ResourceKey texture2, + final ResourceKey texture3, + final ResourceKey texture4, + final ResourceKey texture5 + ) { + return TexturedModel.of(this, Textures.of( + this.slot1(), texture1, + this.slot2(), texture2, + this.slot3(), texture3, + this.slot4(), texture4, + this.slot5(), texture5 + )); + } + + default TexturedModel textured( + final ResourceKey texture, + final String suffix1, + final String suffix2, + final String suffix3, + final String suffix4, + final String suffix5 + ) { + return TexturedModel.of(this, Textures.of( + texture, + this.slot1(), suffix1, + this.slot2(), suffix2, + this.slot3(), suffix3, + this.slot4(), suffix4, + this.slot5(), suffix5 + )); + } + + default TexturedModel texturedBlock( + final ResourceKey texture1, + final ResourceKey texture2, + final ResourceKey texture3, + final ResourceKey texture4, + final ResourceKey texture5 + ) { + return TexturedModel.of(this, Textures.block( + this.slot1(), texture1, + this.slot2(), texture2, + this.slot3(), texture3, + this.slot4(), texture4, + this.slot5(), texture5 + )); + } + + default TexturedModel texturedBlock( + final ResourceKey texture, + final String suffix1, + final String suffix2, + final String suffix3, + final String suffix4, + final String suffix5 + ) { + return TexturedModel.of(this, Textures.block( + texture, + this.slot1(), suffix1, + this.slot2(), suffix2, + this.slot3(), suffix3, + this.slot4(), suffix4, + this.slot5(), suffix5 + )); + } + + default TexturedModel texturedItem( + final ResourceKey texture1, + final ResourceKey texture2, + final ResourceKey texture3, + final ResourceKey texture4, + final ResourceKey texture5 + ) { + return TexturedModel.of(this, Textures.item( + this.slot1(), texture1, + this.slot2(), texture2, + this.slot3(), texture3, + this.slot4(), texture4, + this.slot5(), texture5 + )); + } + + default TexturedModel texturedItem( + final ResourceKey texture, + final String suffix1, + final String suffix2, + final String suffix3, + final String suffix4, + final String suffix5 + ) { + return TexturedModel.of(this, Textures.item( + texture, + this.slot1(), suffix1, + this.slot2(), suffix2, + this.slot3(), suffix3, + this.slot4(), suffix4, + this.slot5(), suffix5 + )); + } + } + + public interface T6 extends ModelTemplateProvider { + + default TextureSlot slot1() { + return this.slots().get(0); + } + + default TextureSlot slot2() { + return this.slots().get(1); + } + + default TextureSlot slot3() { + return this.slots().get(2); + } + + default TextureSlot slot4() { + return this.slots().get(3); + } + + default TextureSlot slot5() { + return this.slots().get(4); + } + + default TextureSlot slot6() { + return this.slots().get(5); + } + + default TexturedModel textured( + final ResourceKey texture1, + final ResourceKey texture2, + final ResourceKey texture3, + final ResourceKey texture4, + final ResourceKey texture5, + final ResourceKey texture6 + ) { + return TexturedModel.of(this, Textures.of( + this.slot1(), texture1, + this.slot2(), texture2, + this.slot3(), texture3, + this.slot4(), texture4, + this.slot5(), texture5, + this.slot6(), texture6 + )); + } + + default TexturedModel textured( + final ResourceKey texture, + final String suffix1, + final String suffix2, + final String suffix3, + final String suffix4, + final String suffix5, + final String suffix6 + ) { + return TexturedModel.of(this, Textures.of( + texture, + this.slot1(), suffix1, + this.slot2(), suffix2, + this.slot3(), suffix3, + this.slot4(), suffix4, + this.slot5(), suffix5, + this.slot6(), suffix6 + )); + } + + default TexturedModel texturedBlock( + final ResourceKey texture1, + final ResourceKey texture2, + final ResourceKey texture3, + final ResourceKey texture4, + final ResourceKey texture5, + final ResourceKey texture6 + ) { + return TexturedModel.of(this, Textures.block( + this.slot1(), texture1, + this.slot2(), texture2, + this.slot3(), texture3, + this.slot4(), texture4, + this.slot5(), texture5, + this.slot6(), texture6 + )); + } + + default TexturedModel texturedBlock( + final ResourceKey texture, + final String suffix1, + final String suffix2, + final String suffix3, + final String suffix4, + final String suffix5, + final String suffix6 + ) { + return TexturedModel.of(this, Textures.block( + texture, + this.slot1(), suffix1, + this.slot2(), suffix2, + this.slot3(), suffix3, + this.slot4(), suffix4, + this.slot5(), suffix5, + this.slot6(), suffix6 + )); + } + + default TexturedModel texturedItem( + final ResourceKey texture1, + final ResourceKey texture2, + final ResourceKey texture3, + final ResourceKey texture4, + final ResourceKey texture5, + final ResourceKey texture6 + ) { + return TexturedModel.of(this, Textures.item( + this.slot1(), texture1, + this.slot2(), texture2, + this.slot3(), texture3, + this.slot4(), texture4, + this.slot5(), texture5, + this.slot6(), texture6 + )); + } + + default TexturedModel texturedItem( + final ResourceKey texture, + final String suffix1, + final String suffix2, + final String suffix3, + final String suffix4, + final String suffix5, + final String suffix6 + ) { + return TexturedModel.of(this, Textures.item( + texture, + this.slot1(), suffix1, + this.slot2(), suffix2, + this.slot3(), suffix3, + this.slot4(), suffix4, + this.slot5(), suffix5, + this.slot6(), suffix6 + )); + } + } + + public interface T7 extends ModelTemplateProvider { + + default TextureSlot slot1() { + return this.slots().get(0); + } + + default TextureSlot slot2() { + return this.slots().get(1); + } + + default TextureSlot slot3() { + return this.slots().get(2); + } + + default TextureSlot slot4() { + return this.slots().get(3); + } + + default TextureSlot slot5() { + return this.slots().get(4); + } + + default TextureSlot slot6() { + return this.slots().get(5); + } + + default TextureSlot slot7() { + return this.slots().get(6); + } + + default TexturedModel textured( + final ResourceKey texture1, + final ResourceKey texture2, + final ResourceKey texture3, + final ResourceKey texture4, + final ResourceKey texture5, + final ResourceKey texture6, + final ResourceKey texture7 + ) { + return TexturedModel.of(this, Textures.of( + this.slot1(), texture1, + this.slot2(), texture2, + this.slot3(), texture3, + this.slot4(), texture4, + this.slot5(), texture5, + this.slot6(), texture6, + this.slot7(), texture7 + )); + } + + default TexturedModel textured( + final ResourceKey texture, + final String suffix1, + final String suffix2, + final String suffix3, + final String suffix4, + final String suffix5, + final String suffix6, + final String suffix7 + ) { + return TexturedModel.of(this, Textures.of( + texture, + this.slot1(), suffix1, + this.slot2(), suffix2, + this.slot3(), suffix3, + this.slot4(), suffix4, + this.slot5(), suffix5, + this.slot6(), suffix6, + this.slot7(), suffix7 + )); + } + + default TexturedModel texturedBlock( + final ResourceKey texture1, + final ResourceKey texture2, + final ResourceKey texture3, + final ResourceKey texture4, + final ResourceKey texture5, + final ResourceKey texture6, + final ResourceKey texture7 + ) { + return TexturedModel.of(this, Textures.block( + this.slot1(), texture1, + this.slot2(), texture2, + this.slot3(), texture3, + this.slot4(), texture4, + this.slot5(), texture5, + this.slot6(), texture6, + this.slot7(), texture7 + )); + } + + default TexturedModel texturedBlock( + final ResourceKey texture, + final String suffix1, + final String suffix2, + final String suffix3, + final String suffix4, + final String suffix5, + final String suffix6, + final String suffix7 + ) { + return TexturedModel.of(this, Textures.block( + texture, + this.slot1(), suffix1, + this.slot2(), suffix2, + this.slot3(), suffix3, + this.slot4(), suffix4, + this.slot5(), suffix5, + this.slot6(), suffix6, + this.slot7(), suffix7 + )); + } + + default TexturedModel texturedItem( + final ResourceKey texture1, + final ResourceKey texture2, + final ResourceKey texture3, + final ResourceKey texture4, + final ResourceKey texture5, + final ResourceKey texture6, + final ResourceKey texture7 + ) { + return TexturedModel.of(this, Textures.item( + this.slot1(), texture1, + this.slot2(), texture2, + this.slot3(), texture3, + this.slot4(), texture4, + this.slot5(), texture5, + this.slot6(), texture6, + this.slot7(), texture7 + )); + } + + default TexturedModel texturedItem( + final ResourceKey texture, + final String suffix1, + final String suffix2, + final String suffix3, + final String suffix4, + final String suffix5, + final String suffix6, + final String suffix7 + ) { + return TexturedModel.of(this, Textures.item( + texture, + this.slot1(), suffix1, + this.slot2(), suffix2, + this.slot3(), suffix3, + this.slot4(), suffix4, + this.slot5(), suffix5, + this.slot6(), suffix6, + this.slot7(), suffix7 + )); + } + } +} diff --git a/api/src/main/java/net/hellheim/spongetools/resourcepack/ModelTemplates.java b/api/src/main/java/net/hellheim/spongetools/resourcepack/ModelTemplates.java new file mode 100644 index 0000000..7ace67d --- /dev/null +++ b/api/src/main/java/net/hellheim/spongetools/resourcepack/ModelTemplates.java @@ -0,0 +1,525 @@ +package net.hellheim.spongetools.resourcepack; + +import java.util.List; +import java.util.Optional; +import java.util.stream.IntStream; + +import org.spongepowered.api.ResourceKey; + +import net.hellheim.spongetools.util.ModelUtil; + +public final class ModelTemplates { + + public static final ModelTemplateProvider.T1 PARTICLE_ONLY = ModelTemplateProvider.t1(of(TextureSlots.PARTICLE)); + + + + public static final ModelTemplateProvider.T1 FLAT_ITEM = ModelTemplateProvider.t1( + item("generated", TextureSlots.LAYER0)); + + public static final ModelTemplateProvider.T2 TWO_LAYERED_ITEM = ModelTemplateProvider.t2( + item("generated", TextureSlots.LAYER0, TextureSlots.LAYER1)); + + public static final ModelTemplateProvider.T3 THREE_LAYERED_ITEM = ModelTemplateProvider.t3( + item("generated", TextureSlots.LAYER0, TextureSlots.LAYER1, TextureSlots.LAYER2)); + + public static final ModelTemplateProvider.T1 BOW = ModelTemplateProvider.t1( + item("bow", TextureSlots.LAYER0)); + + public static final ModelTemplateProvider.T1 CROSSBOW = ModelTemplateProvider.t1( + item("crossbow", TextureSlots.LAYER0)); + + public static final ModelTemplateProvider.T1 HANDHELD = ModelTemplateProvider.t1( + item("handheld", TextureSlots.LAYER0)); + + public static final ModelTemplateProvider.T1 HANDHELD_ROD = ModelTemplateProvider.t1( + item("handheld_rod", TextureSlots.LAYER0)); + + public static final ModelTemplateProvider.T1 HANDHELD_MACE = ModelTemplateProvider.t1( + item("handheld_mace", TextureSlots.LAYER0)); + + public static final ModelTemplateProvider.T1 TEMPLATE_MUSIC_DISC = ModelTemplateProvider.t1( + item("template_music_disc", TextureSlots.LAYER0)); + + public static final ModelTemplateProvider.T1 TEMPLATE_SHULKER_BOX = ModelTemplateProvider.t1( + item("template_shulker_box", TextureSlots.PARTICLE)); + + public static final ModelTemplateProvider.T1 TEMPLATE_BED = ModelTemplateProvider.t1( + item("template_bed", TextureSlots.PARTICLE)); + + public static final ModelTemplateProvider.T1 TEMPLATE_CHEST = ModelTemplateProvider.t1( + item("template_chest", TextureSlots.PARTICLE)); + + public static final ModelTemplateProvider.T1 TEMPLATE_BUNDLE_OPEN_FRONT = ModelTemplateProvider.t1( + item("template_bundle_open_front", "_open_front", TextureSlots.LAYER0)); + + public static final ModelTemplateProvider.T1 TEMPLATE_BUNDLE_OPEN_BACK = ModelTemplateProvider.t1( + item("template_bundle_open_back", "_open_back", TextureSlots.LAYER0)); + + + + public static final ModelTemplateProvider.T7 CUBE = ModelTemplateProvider.t7( + block("cube", TextureSlots.PARTICLE, TextureSlots.NORTH, TextureSlots.SOUTH, TextureSlots.EAST, TextureSlots.WEST, TextureSlots.UP, TextureSlots.DOWN)); + + public static final ModelTemplateProvider.T7 CUBE_DIRECTIONAL = ModelTemplateProvider.t7( + block("cube_directional", TextureSlots.PARTICLE, TextureSlots.NORTH, TextureSlots.SOUTH, TextureSlots.EAST, TextureSlots.WEST, TextureSlots.UP, TextureSlots.DOWN)); + + public static final ModelTemplateProvider.T1 CUBE_ALL = ModelTemplateProvider.t1( + block("cube_all", TextureSlots.ALL)); + + public static final ModelTemplateProvider.T1 CUBE_ALL_INNER_FACES = ModelTemplateProvider.t1( + block("cube_all_inner_faces", TextureSlots.ALL)); + + public static final ModelTemplateProvider.T1 CUBE_MIRRORED_ALL = ModelTemplateProvider.t1( + block("cube_mirrored_all", "_mirrored", TextureSlots.ALL)); + + public static final ModelTemplateProvider.T1 CUBE_NORTH_WEST_MIRRORED_ALL = ModelTemplateProvider.t1( + block("cube_north_west_mirrored_all", "_north_west_mirrored", TextureSlots.ALL)); + + public static final ModelTemplateProvider.T2 CUBE_COLUMN_UV_LOCKED_X = ModelTemplateProvider.t2( + block("cube_column_uv_locked_x", "_x", TextureSlots.END, TextureSlots.SIDE)); + + public static final ModelTemplateProvider.T2 CUBE_COLUMN_UV_LOCKED_Y = ModelTemplateProvider.t2( + block("cube_column_uv_locked_y", "_y", TextureSlots.END, TextureSlots.SIDE)); + + public static final ModelTemplateProvider.T2 CUBE_COLUMN_UV_LOCKED_Z = ModelTemplateProvider.t2( + block("cube_column_uv_locked_z", "_z", TextureSlots.END, TextureSlots.SIDE)); + + public static final ModelTemplateProvider.T2 CUBE_COLUMN = ModelTemplateProvider.t2( + block("cube_column", TextureSlots.END, TextureSlots.SIDE)); + + public static final ModelTemplateProvider.T2 CUBE_COLUMN_HORIZONTAL = ModelTemplateProvider.t2( + block("cube_column_horizontal", "_horizontal", TextureSlots.END, TextureSlots.SIDE)); + + public static final ModelTemplateProvider.T2 CUBE_COLUMN_MIRRORED = ModelTemplateProvider.t2( + block("cube_column_mirrored", "_mirrored", TextureSlots.END, TextureSlots.SIDE)); + + public static final ModelTemplateProvider.T2 CUBE_TOP = ModelTemplateProvider.t2( + block("cube_top", TextureSlots.TOP, TextureSlots.SIDE)); + + public static final ModelTemplateProvider.T3 CUBE_BOTTOM_TOP = ModelTemplateProvider.t3( + block("cube_bottom_top", TextureSlots.TOP, TextureSlots.BOTTOM, TextureSlots.SIDE)); + + public static final ModelTemplateProvider.T3 CUBE_BOTTOM_TOP_INNER_FACES = ModelTemplateProvider.t3( + block("cube_bottom_top_inner_faces", TextureSlots.TOP, TextureSlots.BOTTOM, TextureSlots.SIDE)); + + public static final ModelTemplateProvider.T3 CUBE_ORIENTABLE = ModelTemplateProvider.t3( + block("orientable", TextureSlots.TOP, TextureSlots.FRONT, TextureSlots.SIDE)); + + public static final ModelTemplateProvider.T4 CUBE_ORIENTABLE_TOP_BOTTOM = ModelTemplateProvider.t4( + block("orientable_with_bottom", TextureSlots.TOP, TextureSlots.BOTTOM, TextureSlots.SIDE, TextureSlots.FRONT)); + + public static final ModelTemplateProvider.T2 CUBE_ORIENTABLE_VERTICAL = ModelTemplateProvider.t2( + block("orientable_vertical", "_vertical", TextureSlots.FRONT, TextureSlots.SIDE)); + + public static final ModelTemplateProvider.T1 BUTTON = ModelTemplateProvider.t1( + block("button", TextureSlots.TEXTURE)); + + public static final ModelTemplateProvider.T1 BUTTON_PRESSED = ModelTemplateProvider.t1( + block("button_pressed", "_pressed", TextureSlots.TEXTURE)); + + public static final ModelTemplateProvider.T1 BUTTON_INVENTORY = ModelTemplateProvider.t1( + block("button_inventory", "_inventory", TextureSlots.TEXTURE)); + + public static final ModelTemplateProvider.T2 DOOR_BOTTOM_LEFT = ModelTemplateProvider.t2( + block("door_bottom_left", "_bottom_left", TextureSlots.TOP, TextureSlots.BOTTOM)); + + public static final ModelTemplateProvider.T2 DOOR_BOTTOM_LEFT_OPEN = ModelTemplateProvider.t2( + block("door_bottom_left_open", "_bottom_left_open", TextureSlots.TOP, TextureSlots.BOTTOM)); + + public static final ModelTemplateProvider.T2 DOOR_BOTTOM_RIGHT = ModelTemplateProvider.t2( + block("door_bottom_right", "_bottom_right", TextureSlots.TOP, TextureSlots.BOTTOM)); + + public static final ModelTemplateProvider.T2 DOOR_BOTTOM_RIGHT_OPEN = ModelTemplateProvider.t2( + block("door_bottom_right_open", "_bottom_right_open", TextureSlots.TOP, TextureSlots.BOTTOM)); + + public static final ModelTemplateProvider.T2 DOOR_TOP_LEFT = ModelTemplateProvider.t2( + block("door_top_left", "_top_left", TextureSlots.TOP, TextureSlots.BOTTOM)); + + public static final ModelTemplateProvider.T2 DOOR_TOP_LEFT_OPEN = ModelTemplateProvider.t2( + block("door_top_left_open", "_top_left_open", TextureSlots.TOP, TextureSlots.BOTTOM)); + + public static final ModelTemplateProvider.T2 DOOR_TOP_RIGHT = ModelTemplateProvider.t2( + block("door_top_right", "_top_right", TextureSlots.TOP, TextureSlots.BOTTOM)); + + public static final ModelTemplateProvider.T2 DOOR_TOP_RIGHT_OPEN = ModelTemplateProvider.t2( + block("door_top_right_open", "_top_right_open", TextureSlots.TOP, TextureSlots.BOTTOM)); + + public static final ModelTemplateProvider.T2 CUSTOM_FENCE_POST = ModelTemplateProvider.t2( + block("custom_fence_post", "_post", TextureSlots.TEXTURE, TextureSlots.PARTICLE)); + + public static final ModelTemplateProvider.T1 CUSTOM_FENCE_SIDE_NORTH = ModelTemplateProvider.t1( + block("custom_fence_side_north", "_side_north", TextureSlots.TEXTURE)); + + public static final ModelTemplateProvider.T1 CUSTOM_FENCE_SIDE_EAST = ModelTemplateProvider.t1( + block("custom_fence_side_east", "_side_east", TextureSlots.TEXTURE)); + + public static final ModelTemplateProvider.T1 CUSTOM_FENCE_SIDE_SOUTH = ModelTemplateProvider.t1( + block("custom_fence_side_south", "_side_south", TextureSlots.TEXTURE)); + + public static final ModelTemplateProvider.T1 CUSTOM_FENCE_SIDE_WEST = ModelTemplateProvider.t1( + block("custom_fence_side_west", "_side_west", TextureSlots.TEXTURE)); + + public static final ModelTemplateProvider.T1 CUSTOM_FENCE_INVENTORY = ModelTemplateProvider.t1( + block("custom_fence_inventory", "_inventory", TextureSlots.TEXTURE)); + + public static final ModelTemplateProvider.T1 FENCE_POST = ModelTemplateProvider.t1( + block("fence_post", "_post", TextureSlots.TEXTURE)); + + public static final ModelTemplateProvider.T1 FENCE_SIDE = ModelTemplateProvider.t1( + block("fence_side", "_side", TextureSlots.TEXTURE)); + + public static final ModelTemplateProvider.T1 FENCE_INVENTORY = ModelTemplateProvider.t1( + block("fence_inventory", "_inventory", TextureSlots.TEXTURE)); + + public static final ModelTemplateProvider.T1 WALL_POST = ModelTemplateProvider.t1( + block("template_wall_post", "_post", TextureSlots.WALL)); + + public static final ModelTemplateProvider.T1 WALL_LOW_SIDE = ModelTemplateProvider.t1( + block("template_wall_side", "_side", TextureSlots.WALL)); + + public static final ModelTemplateProvider.T1 WALL_TALL_SIDE = ModelTemplateProvider.t1( + block("template_wall_side_tall", "_side_tall", TextureSlots.WALL)); + + public static final ModelTemplateProvider.T1 WALL_INVENTORY = ModelTemplateProvider.t1( + block("wall_inventory", "_inventory", TextureSlots.WALL)); + + public static final ModelTemplateProvider.T2 CUSTOM_FENCE_GATE_CLOSED = ModelTemplateProvider.t2( + block("template_custom_fence_gate", TextureSlots.TEXTURE, TextureSlots.PARTICLE)); + + public static final ModelTemplateProvider.T2 CUSTOM_FENCE_GATE_OPEN = ModelTemplateProvider.t2( + block("template_custom_fence_gate_open", "_open", TextureSlots.TEXTURE, TextureSlots.PARTICLE)); + + public static final ModelTemplateProvider.T2 CUSTOM_FENCE_GATE_WALL_CLOSED = ModelTemplateProvider.t2( + block("template_custom_fence_gate_wall", "_wall", TextureSlots.TEXTURE, TextureSlots.PARTICLE)); + + public static final ModelTemplateProvider.T2 CUSTOM_FENCE_GATE_WALL_OPEN = ModelTemplateProvider.t2( + block("template_custom_fence_gate_wall_open", "_wall_open", TextureSlots.TEXTURE, TextureSlots.PARTICLE)); + + public static final ModelTemplateProvider.T1 FENCE_GATE_CLOSED = ModelTemplateProvider.t1( + block("template_fence_gate", TextureSlots.TEXTURE)); + + public static final ModelTemplateProvider.T1 FENCE_GATE_OPEN = ModelTemplateProvider.t1( + block("template_fence_gate_open", "_open", TextureSlots.TEXTURE)); + + public static final ModelTemplateProvider.T1 FENCE_GATE_WALL_CLOSED = ModelTemplateProvider.t1( + block("template_fence_gate_wall", "_wall", TextureSlots.TEXTURE)); + + public static final ModelTemplateProvider.T1 FENCE_GATE_WALL_OPEN = ModelTemplateProvider.t1( + block("template_fence_gate_wall_open", "_wall_open", TextureSlots.TEXTURE)); + + public static final ModelTemplateProvider.T1 PRESSURE_PLATE_UP = ModelTemplateProvider.t1( + block("pressure_plate_up", TextureSlots.TEXTURE)); + + public static final ModelTemplateProvider.T1 PRESSURE_PLATE_DOWN = ModelTemplateProvider.t1( + block("pressure_plate_down", "_down", TextureSlots.TEXTURE)); + + public static final ModelTemplateProvider.T3 SLAB_BOTTOM = ModelTemplateProvider.t3( + block("slab", TextureSlots.BOTTOM, TextureSlots.TOP, TextureSlots.SIDE)); + + public static final ModelTemplateProvider.T3 SLAB_TOP = ModelTemplateProvider.t3( + block("slab_top", "_top", TextureSlots.BOTTOM, TextureSlots.TOP, TextureSlots.SIDE)); + + public static final ModelTemplateProvider.T1 LEAVES = ModelTemplateProvider.t1( + block("leaves", TextureSlots.ALL)); + + public static final ModelTemplateProvider.T3 STAIRS_STRAIGHT = ModelTemplateProvider.t3( + block("stairs", TextureSlots.BOTTOM, TextureSlots.TOP, TextureSlots.SIDE)); + + public static final ModelTemplateProvider.T3 STAIRS_INNER = ModelTemplateProvider.t3( + block("inner_stairs", "_inner", TextureSlots.BOTTOM, TextureSlots.TOP, TextureSlots.SIDE)); + + public static final ModelTemplateProvider.T3 STAIRS_OUTER = ModelTemplateProvider.t3( + block("outer_stairs", "_outer", TextureSlots.BOTTOM, TextureSlots.TOP, TextureSlots.SIDE)); + + public static final ModelTemplateProvider.T1 TRAPDOOR_TOP = ModelTemplateProvider.t1( + block("template_trapdoor_top", "_top", TextureSlots.TEXTURE)); + + public static final ModelTemplateProvider.T1 TRAPDOOR_BOTTOM = ModelTemplateProvider.t1( + block("template_trapdoor_bottom", "_bottom", TextureSlots.TEXTURE)); + + public static final ModelTemplateProvider.T1 TRAPDOOR_OPEN = ModelTemplateProvider.t1( + block("template_trapdoor_open", "_open", TextureSlots.TEXTURE)); + + public static final ModelTemplateProvider.T1 ORIENTABLE_TRAPDOOR_TOP = ModelTemplateProvider.t1( + block("template_orientable_trapdoor_top", "_top", TextureSlots.TEXTURE)); + + public static final ModelTemplateProvider.T1 ORIENTABLE_TRAPDOOR_BOTTOM = ModelTemplateProvider.t1( + block("template_orientable_trapdoor_bottom", "_bottom", TextureSlots.TEXTURE)); + + public static final ModelTemplateProvider.T1 ORIENTABLE_TRAPDOOR_OPEN = ModelTemplateProvider.t1( + block("template_orientable_trapdoor_open", "_open", TextureSlots.TEXTURE)); + + public static final ModelTemplateProvider.T1 POINTED_DRIPSTONE = ModelTemplateProvider.t1( + block("pointed_dripstone", TextureSlots.CROSS)); + + public static final ModelTemplateProvider.T1 CROSS = ModelTemplateProvider.t1( + block("cross", TextureSlots.CROSS)); + + public static final ModelTemplateProvider.T1 TINTED_CROSS = ModelTemplateProvider.t1( + block("tinted_cross", TextureSlots.CROSS)); + + public static final ModelTemplateProvider.T2 CROSS_EMISSIVE = ModelTemplateProvider.t2( + block("cross_emissive", TextureSlots.CROSS, TextureSlots.CROSS_EMISSIVE)); + + public static final ModelTemplateProvider.T1 FLOWER_POT_CROSS = ModelTemplateProvider.t1( + block("flower_pot_cross", TextureSlots.PLANT)); + + public static final ModelTemplateProvider.T1 TINTED_FLOWER_POT_CROSS = ModelTemplateProvider.t1( + block("tinted_flower_pot_cross", TextureSlots.PLANT)); + + public static final ModelTemplateProvider.T2 FLOWER_POT_CROSS_EMISSIVE = ModelTemplateProvider.t2( + block("flower_pot_cross_emissive", TextureSlots.PLANT, TextureSlots.CROSS_EMISSIVE)); + + public static final ModelTemplateProvider.T1 RAIL_FLAT = ModelTemplateProvider.t1( + block("rail_flat", TextureSlots.RAIL)); + + public static final ModelTemplateProvider.T1 RAIL_CURVED = ModelTemplateProvider.t1( + block("rail_curved", "_corner", TextureSlots.RAIL)); + + public static final ModelTemplateProvider.T1 RAIL_RAISED_NE = ModelTemplateProvider.t1( + block("template_rail_raised_ne", "_raised_ne", TextureSlots.RAIL)); + + public static final ModelTemplateProvider.T1 RAIL_RAISED_SW = ModelTemplateProvider.t1( + block("template_rail_raised_sw", "_raised_sw", TextureSlots.RAIL)); + + public static final ModelTemplateProvider.T1 CARPET = ModelTemplateProvider.t1( + block("carpet", TextureSlots.WOOL)); + + public static final ModelTemplateProvider.T1 MOSSY_CARPET_SIDE = ModelTemplateProvider.t1( + block("mossy_carpet_side", TextureSlots.SIDE)); + + public static final ModelTemplateProvider.T2 FLOWERBED_1 = ModelTemplateProvider.t2( + block("flowerbed_1", "_1", TextureSlots.FLOWERBED, TextureSlots.STEM)); + + public static final ModelTemplateProvider.T2 FLOWERBED_2 = ModelTemplateProvider.t2( + block("flowerbed_2", "_2", TextureSlots.FLOWERBED, TextureSlots.STEM)); + + public static final ModelTemplateProvider.T2 FLOWERBED_3 = ModelTemplateProvider.t2( + block("flowerbed_3", "_3", TextureSlots.FLOWERBED, TextureSlots.STEM)); + + public static final ModelTemplateProvider.T2 FLOWERBED_4 = ModelTemplateProvider.t2( + block("flowerbed_4", "_4", TextureSlots.FLOWERBED, TextureSlots.STEM)); + + public static final ModelTemplateProvider.T1 CORAL_FAN = ModelTemplateProvider.t1( + block("coral_fan", TextureSlots.FAN)); + + public static final ModelTemplateProvider.T1 CORAL_WALL_FAN = ModelTemplateProvider.t1( + block("coral_wall_fan", TextureSlots.FAN)); + + public static final ModelTemplateProvider.T1 GLAZED_TERRACOTTA = ModelTemplateProvider.t1( + block("template_glazed_terracotta", TextureSlots.PATTERN)); + + public static final ModelTemplateProvider.T1 CHORUS_FLOWER = ModelTemplateProvider.t1( + block("template_chorus_flower", TextureSlots.TEXTURE)); + + public static final ModelTemplateProvider.T2 DAYLIGHT_DETECTOR = ModelTemplateProvider.t2( + block("template_daylight_detector", TextureSlots.TOP, TextureSlots.SIDE)); + + public static final ModelTemplateProvider.T1 STAINED_GLASS_PANE_NOSIDE = ModelTemplateProvider.t1( + block("template_glass_pane_noside", "_noside", TextureSlots.PANE)); + + public static final ModelTemplateProvider.T1 STAINED_GLASS_PANE_NOSIDE_ALT = ModelTemplateProvider.t1( + block("template_glass_pane_noside_alt", "_noside_alt", TextureSlots.PANE)); + + public static final ModelTemplateProvider.T2 STAINED_GLASS_PANE_POST = ModelTemplateProvider.t2( + block("template_glass_pane_post", "_post", TextureSlots.PANE, TextureSlots.EDGE)); + + public static final ModelTemplateProvider.T2 STAINED_GLASS_PANE_SIDE = ModelTemplateProvider.t2( + block("template_glass_pane_side", "_side", TextureSlots.PANE, TextureSlots.EDGE)); + + public static final ModelTemplateProvider.T2 STAINED_GLASS_PANE_SIDE_ALT = ModelTemplateProvider.t2( + block("template_glass_pane_side_alt", "_side_alt", TextureSlots.PANE, TextureSlots.EDGE)); + + public static final ModelTemplateProvider.T3 COMMAND_BLOCK = ModelTemplateProvider.t3( + block("template_command_block", TextureSlots.FRONT, TextureSlots.BACK, TextureSlots.SIDE)); + + public static final ModelTemplateProvider.T1 CHISELED_BOOKSHELF_SLOT_TOP_LEFT = ModelTemplateProvider.t1( + block("template_chiseled_bookshelf_slot_top_left", "_slot_top_left", TextureSlots.TEXTURE)); + + public static final ModelTemplateProvider.T1 CHISELED_BOOKSHELF_SLOT_TOP_MID = ModelTemplateProvider.t1( + block("template_chiseled_bookshelf_slot_top_mid", "_slot_top_mid", TextureSlots.TEXTURE)); + + public static final ModelTemplateProvider.T1 CHISELED_BOOKSHELF_SLOT_TOP_RIGHT = ModelTemplateProvider.t1( + block("template_chiseled_bookshelf_slot_top_right", "_slot_top_right", TextureSlots.TEXTURE)); + + public static final ModelTemplateProvider.T1 CHISELED_BOOKSHELF_SLOT_BOTTOM_LEFT = ModelTemplateProvider.t1( + block("template_chiseled_bookshelf_slot_bottom_left", "_slot_bottom_left", TextureSlots.TEXTURE)); + + public static final ModelTemplateProvider.T1 CHISELED_BOOKSHELF_SLOT_BOTTOM_MID = ModelTemplateProvider.t1( + block("template_chiseled_bookshelf_slot_bottom_mid", "_slot_bottom_mid", TextureSlots.TEXTURE)); + + public static final ModelTemplateProvider.T1 CHISELED_BOOKSHELF_SLOT_BOTTOM_RIGHT = ModelTemplateProvider.t1( + block("template_chiseled_bookshelf_slot_bottom_right", "_slot_bottom_right", TextureSlots.TEXTURE)); + + public static final ModelTemplateProvider.T1 ANVIL = ModelTemplateProvider.t1( + block("template_anvil", TextureSlots.TOP)); + + public static final ModelTemplateProvider.T2 ATTACHED_STEM = ModelTemplateProvider.t2( + block("stem_fruit", TextureSlots.STEM, TextureSlots.UPPER_STEM)); + + public static final ModelTemplateProvider.T1 CROP = ModelTemplateProvider.t1( + block("crop", TextureSlots.CROP)); + + public static final ModelTemplateProvider.T2 FARMLAND = ModelTemplateProvider.t2( + block("template_farmland", TextureSlots.DIRT, TextureSlots.TOP)); + + public static final ModelTemplateProvider.T1 FIRE_FLOOR = ModelTemplateProvider.t1( + block("template_fire_floor", TextureSlots.FIRE)); + + public static final ModelTemplateProvider.T1 FIRE_SIDE = ModelTemplateProvider.t1( + block("template_fire_side", TextureSlots.FIRE)); + + public static final ModelTemplateProvider.T1 FIRE_SIDE_ALT = ModelTemplateProvider.t1( + block("template_fire_side_alt", TextureSlots.FIRE)); + + public static final ModelTemplateProvider.T1 FIRE_UP = ModelTemplateProvider.t1( + block("template_fire_up", TextureSlots.FIRE)); + + public static final ModelTemplateProvider.T1 FIRE_UP_ALT = ModelTemplateProvider.t1( + block("template_fire_up_alt", TextureSlots.FIRE)); + + public static final ModelTemplateProvider.T2 CAMPFIRE = ModelTemplateProvider.t2( + block("template_campfire", TextureSlots.FIRE, TextureSlots.LIT_LOG)); + + public static final ModelTemplateProvider.T1 LANTERN = ModelTemplateProvider.t1( + block("template_lantern", TextureSlots.LANTERN)); + + public static final ModelTemplateProvider.T1 HANGING_LANTERN = ModelTemplateProvider.t1( + block("template_hanging_lantern", "_hanging", TextureSlots.LANTERN)); + + public static final ModelTemplateProvider.T1 TORCH = ModelTemplateProvider.t1( + block("template_torch", TextureSlots.TORCH)); + + public static final ModelTemplateProvider.T1 TORCH_UNLIT = ModelTemplateProvider.t1( + block("template_torch_unlit", TextureSlots.TORCH)); + + public static final ModelTemplateProvider.T1 WALL_TORCH = ModelTemplateProvider.t1( + block("template_torch_wall", TextureSlots.TORCH)); + + public static final ModelTemplateProvider.T1 WALL_TORCH_UNLIT = ModelTemplateProvider.t1( + block("template_torch_wall_unlit", TextureSlots.TORCH)); + + public static final ModelTemplateProvider.T1 REDSTONE_TORCH = ModelTemplateProvider.t1( + block("template_redstone_torch", TextureSlots.TORCH)); + + public static final ModelTemplateProvider.T1 REDSTONE_WALL_TORCH = ModelTemplateProvider.t1( + block("template_redstone_torch_wall", TextureSlots.TORCH)); + + public static final ModelTemplateProvider.T3 PISTON = ModelTemplateProvider.t3( + block("template_piston", TextureSlots.PLATFORM, TextureSlots.BOTTOM, TextureSlots.SIDE)); + + public static final ModelTemplateProvider.T3 PISTON_HEAD = ModelTemplateProvider.t3( + block("template_piston_head", TextureSlots.PLATFORM, TextureSlots.SIDE, TextureSlots.UNSTICKY)); + + public static final ModelTemplateProvider.T3 PISTON_HEAD_SHORT = ModelTemplateProvider.t3( + block("template_piston_head_short", TextureSlots.PLATFORM, TextureSlots.SIDE, TextureSlots.UNSTICKY)); + + public static final ModelTemplateProvider.T1 SEAGRASS = ModelTemplateProvider.t1( + block("template_seagrass", TextureSlots.TEXTURE)); + + public static final ModelTemplateProvider.T1 TURTLE_EGG = ModelTemplateProvider.t1( + block("template_turtle_egg", TextureSlots.ALL)); + + public static final ModelTemplateProvider.T1 TWO_TURTLE_EGGS = ModelTemplateProvider.t1( + block("template_two_turtle_eggs", TextureSlots.ALL)); + + public static final ModelTemplateProvider.T1 THREE_TURTLE_EGGS = ModelTemplateProvider.t1( + block("template_three_turtle_eggs", TextureSlots.ALL)); + + public static final ModelTemplateProvider.T1 FOUR_TURTLE_EGGS = ModelTemplateProvider.t1( + block("template_four_turtle_eggs", TextureSlots.ALL)); + + public static final ModelTemplateProvider.T1 SINGLE_FACE = ModelTemplateProvider.t1( + block("template_single_face", TextureSlots.TEXTURE)); + + public static final ModelTemplateProvider.T6 CAULDRON_LEVEL1 = ModelTemplateProvider.t6( + block("template_cauldron_level1", TextureSlots.CONTENT, TextureSlots.INSIDE, TextureSlots.PARTICLE, TextureSlots.TOP, TextureSlots.BOTTOM, TextureSlots.SIDE)); + + public static final ModelTemplateProvider.T6 CAULDRON_LEVEL2 = ModelTemplateProvider.t6( + block("template_cauldron_level2", TextureSlots.CONTENT, TextureSlots.INSIDE, TextureSlots.PARTICLE, TextureSlots.TOP, TextureSlots.BOTTOM, TextureSlots.SIDE)); + + public static final ModelTemplateProvider.T6 CAULDRON_FULL = ModelTemplateProvider.t6( + block("template_cauldron_full", TextureSlots.CONTENT, TextureSlots.INSIDE, TextureSlots.PARTICLE, TextureSlots.TOP, TextureSlots.BOTTOM, TextureSlots.SIDE)); + + public static final ModelTemplateProvider.T2 AZALEA = ModelTemplateProvider.t2( + block("template_azalea", TextureSlots.TOP, TextureSlots.SIDE)); + + public static final ModelTemplateProvider.T3 POTTED_AZALEA = ModelTemplateProvider.t3( + block("template_potted_azalea_bush", TextureSlots.PLANT, TextureSlots.TOP, TextureSlots.SIDE)); + + public static final ModelTemplateProvider.T3 POTTED_FLOWERING_AZALEA = ModelTemplateProvider.t3( + block("template_potted_azalea_bush", TextureSlots.PLANT, TextureSlots.TOP, TextureSlots.SIDE)); + + public static final ModelTemplateProvider.T6 SNIFFER_EGG = ModelTemplateProvider.t6( + block("sniffer_egg", TextureSlots.TOP, TextureSlots.BOTTOM, TextureSlots.NORTH, TextureSlots.SOUTH, TextureSlots.EAST, TextureSlots.WEST)); + + public static final ModelTemplateProvider.T2 CANDLE = ModelTemplateProvider.t2( + block("template_candle", TextureSlots.ALL, TextureSlots.PARTICLE)); + + public static final ModelTemplateProvider.T2 TWO_CANDLES = ModelTemplateProvider.t2( + block("template_two_candles", TextureSlots.ALL, TextureSlots.PARTICLE)); + + public static final ModelTemplateProvider.T2 THREE_CANDLES = ModelTemplateProvider.t2( + block("template_three_candles", TextureSlots.ALL, TextureSlots.PARTICLE)); + + public static final ModelTemplateProvider.T2 FOUR_CANDLES = ModelTemplateProvider.t2( + block("template_four_candles", TextureSlots.ALL, TextureSlots.PARTICLE)); + + public static final ModelTemplateProvider.T5 CANDLE_CAKE = ModelTemplateProvider.t5( + block("template_cake_with_candle", TextureSlots.CANDLE, TextureSlots.BOTTOM, TextureSlots.SIDE, TextureSlots.TOP, TextureSlots.PARTICLE)); + + public static final ModelTemplateProvider.T5 SCULK_SHRIEKER = ModelTemplateProvider.t5( + block("template_sculk_shrieker", TextureSlots.BOTTOM, TextureSlots.SIDE, TextureSlots.TOP, TextureSlots.PARTICLE, TextureSlots.INNER_TOP)); + + public static final ModelTemplateProvider.T4 VAULT = ModelTemplateProvider.t4( + block("template_vault", TextureSlots.TOP, TextureSlots.BOTTOM, TextureSlots.SIDE, TextureSlots.FRONT)); + + public static final ModelTemplateProvider.T1[] STEMS = IntStream.range(0, 8) + .mapToObj(i -> ModelTemplateProvider.t1( + block("stem_growth" + i, "_stage" + i, TextureSlots.STEM))) + .toArray(ModelTemplateProvider.T1[]::new); + + + + public static ModelTemplate of( + final Optional key, final Optional suffix, final List slots + ) { + return new ModelTemplate(key, suffix, slots); + } + + public static ModelTemplate of( + final Optional key, final Optional suffix, final TextureSlot... slots + ) { + return ModelTemplates.of(key, suffix, List.of(slots)); + } + + public static ModelTemplate of( + final ResourceKey key, final Optional suffix, final TextureSlot... slots + ) { + return ModelTemplates.of(Optional.of(key), suffix, slots); + } + + protected static ModelTemplate of(final TextureSlot... slots) { + return ModelTemplates.of(Optional.empty(), Optional.empty(), slots); + } + + public static ModelTemplate block(final String name, final TextureSlot... slots) { + return ModelTemplates.of(ModelUtil.withBlockPrefix(name), Optional.empty(), slots); + } + + public static ModelTemplate block(final String name, final String suffix, final TextureSlot... slots) { + return ModelTemplates.of(ModelUtil.withBlockPrefix(name), Optional.of(suffix), slots); + } + + public static ModelTemplate item(final String name, final TextureSlot... slots) { + return ModelTemplates.of(ModelUtil.withItemPrefix(name), Optional.empty(), slots); + } + + public static ModelTemplate item(final String name, final String suffix, final TextureSlot... slots) { + return ModelTemplates.of(ModelUtil.withItemPrefix(name), Optional.of(suffix), slots); + } + + private ModelTemplates() { + } +} diff --git a/api/src/main/java/net/hellheim/spongetools/resourcepack/TextureSlot.java b/api/src/main/java/net/hellheim/spongetools/resourcepack/TextureSlot.java new file mode 100644 index 0000000..489ce6f --- /dev/null +++ b/api/src/main/java/net/hellheim/spongetools/resourcepack/TextureSlot.java @@ -0,0 +1,68 @@ +package net.hellheim.spongetools.resourcepack; + +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; + +import org.spongepowered.api.data.type.StringRepresentable; +import org.spongepowered.api.util.annotation.CatalogedBy; + +import com.mojang.serialization.Codec; +import com.mojang.serialization.DataResult; + +import net.hellheim.spongetools.codec.LateBoundIdMapper; + +/** + * TextureSlot will pick textures from parent slots if its own texture is absent. + */ +@CatalogedBy(TextureSlots.class) +public final class TextureSlot implements StringRepresentable { + + private static final Map VALUES = new HashMap<>(); + private static final LateBoundIdMapper ID_MAPPER = new LateBoundIdMapper<>(); + + public static final Codec CODEC = TextureSlot.ID_MAPPER.codec(Codec.STRING); + public static final Codec CODEC_HASHED = TextureSlot.ID_MAPPER.codec(Codec.STRING + .flatComapMap( + id -> "#" + id, + id -> !id.isEmpty() && id.charAt(0) == '#' + ? DataResult.success(id.substring(1)) + : DataResult.error(() -> "id must have # in the begining"))); + + private final String id; + private final Optional parent; + + private TextureSlot(final String id, final Optional parent) { + this.id = id; + this.parent = parent; + TextureSlot.ID_MAPPER.put(id, this); + } + + public static TextureSlot of(final String id) { + return TextureSlot.of(id, Optional.empty()); + } + + public static TextureSlot of(final String id, final TextureSlot parent) { + return TextureSlot.of(id, Optional.of(parent)); + } + + public static TextureSlot of(final String id, final Optional parent) { + Objects.requireNonNull(id, "id"); + Objects.requireNonNull(parent, "parent"); + return TextureSlot.VALUES.computeIfAbsent(id, $ -> new TextureSlot(id, parent)); + } + + public String id() { + return this.id; + } + + public Optional parent() { + return this.parent; + } + + @Override + public String serializationString() { + return this.id; + } +} diff --git a/api/src/main/java/net/hellheim/spongetools/resourcepack/TextureSlots.java b/api/src/main/java/net/hellheim/spongetools/resourcepack/TextureSlots.java new file mode 100644 index 0000000..dfe3c85 --- /dev/null +++ b/api/src/main/java/net/hellheim/spongetools/resourcepack/TextureSlots.java @@ -0,0 +1,54 @@ +package net.hellheim.spongetools.resourcepack; + +public final class TextureSlots { + + public static final TextureSlot + + ALL = TextureSlot.of("all"), + TEXTURE = TextureSlot.of("texture", ALL), + PARTICLE = TextureSlot.of("particle", TEXTURE), + END = TextureSlot.of("end", ALL), + BOTTOM = TextureSlot.of("bottom", END), + TOP = TextureSlot.of("top", END), + FRONT = TextureSlot.of("front", ALL), + BACK = TextureSlot.of("back", ALL), + SIDE = TextureSlot.of("side", ALL), + NORTH = TextureSlot.of("north", SIDE), + SOUTH = TextureSlot.of("south", SIDE), + EAST = TextureSlot.of("east", SIDE), + WEST = TextureSlot.of("west", SIDE), + UP = TextureSlot.of("up"), + DOWN = TextureSlot.of("down"), + CROSS = TextureSlot.of("cross"), + CROSS_EMISSIVE = TextureSlot.of("cross_emissive"), + PLANT = TextureSlot.of("plant"), + WALL = TextureSlot.of("wall", ALL), + RAIL = TextureSlot.of("rail"), + WOOL = TextureSlot.of("wool"), + PATTERN = TextureSlot.of("pattern"), + PANE = TextureSlot.of("pane"), + EDGE = TextureSlot.of("edge"), + FAN = TextureSlot.of("fan"), + STEM = TextureSlot.of("stem"), + UPPER_STEM = TextureSlot.of("upperstem"), + CROP = TextureSlot.of("crop"), + DIRT = TextureSlot.of("dirt"), + FIRE = TextureSlot.of("fire"), + LANTERN = TextureSlot.of("lantern"), + PLATFORM = TextureSlot.of("platform"), + UNSTICKY = TextureSlot.of("unsticky"), + TORCH = TextureSlot.of("torch"), + LAYER0 = TextureSlot.of("layer0"), + LAYER1 = TextureSlot.of("layer1"), + LAYER2 = TextureSlot.of("layer2"), + LIT_LOG = TextureSlot.of("lit_log"), + CANDLE = TextureSlot.of("candle"), + INSIDE = TextureSlot.of("inside"), + CONTENT = TextureSlot.of("content"), + INNER_TOP = TextureSlot.of("inner_top"), + FLOWERBED = TextureSlot.of("flowerbed") + ; + + private TextureSlots() { + } +} diff --git a/api/src/main/java/net/hellheim/spongetools/resourcepack/TexturedModel.java b/api/src/main/java/net/hellheim/spongetools/resourcepack/TexturedModel.java new file mode 100644 index 0000000..19213e0 --- /dev/null +++ b/api/src/main/java/net/hellheim/spongetools/resourcepack/TexturedModel.java @@ -0,0 +1,143 @@ +package net.hellheim.spongetools.resourcepack; + +import java.util.Collection; +import java.util.List; +import java.util.Objects; +import java.util.Optional; +import java.util.Set; +import java.util.function.Supplier; + +import org.spongepowered.api.ResourceKey; +import org.spongepowered.api.registry.RegistryKey; + +import com.google.common.collect.Sets; +import com.mojang.serialization.DataResult; +import com.mojang.serialization.MapCodec; +import com.mojang.serialization.codecs.RecordCodecBuilder; + +import net.hellheim.spongetools.codec.list.SpongeCodecs; +import net.hellheim.spongetools.util.ModelUtil; + +public record TexturedModel(ModelTemplate parent, Textures textures) implements ModelLike { + + public static final MapCodec MAP_CODEC = RecordCodecBuilder.mapCodec( + instance -> instance.group( + SpongeCodecs.RESOURCE_KEY.optionalFieldOf("parent").forGetter(TexturedModel::key), + Textures.CODEC.fieldOf("textures").forGetter(TexturedModel::textures) + ).apply(instance, TexturedModel::of) + ).validate(model -> { + final Optional exception = TexturedModel.validate(model); + return exception.isEmpty() + ? DataResult.success(model) + : DataResult.error(exception::get); + }); + + public TexturedModel(final ModelTemplate parent, final Textures textures) { + this.parent = Objects.requireNonNull(parent, "parent"); + this.textures = Objects.requireNonNull(textures, "textures"); + } + + public Optional key() { + return this.parent.key(); + } + + @Override + public Model asModel() { + return Model.of(this); + } + + public Model asModel(final ModelPart... parts) { + return Model.of(this, parts); + } + + public Model asModel(final ItemTransform display, ModelPart... parts) { + return Model.of(this, display, parts); + } + + public static TexturedModel of(final Supplier parent, final Textures textures) { + return TexturedModel.of(Objects.requireNonNull(parent, "parent").get(), textures); + } + + public static TexturedModel of(final ModelTemplate parent, final Textures textures) { + final TexturedModel model = new TexturedModel(parent, textures); + final Optional exception = TexturedModel.validate(model); + if (exception.isPresent()) { + throw new IllegalArgumentException(exception.get()); + } + return model; + } + + public static Optional validate(final TexturedModel model) { + final Set required = Set.copyOf(model.parent.slots()); + final Set provided = model.textures.slots(); + + if (required.equals(provided)) { + return Optional.empty(); + } + + String message = "\n"; + + for (final TextureSlot slot : Sets.difference(required, provided)) { + message += "TextureSlot required but not provided: " + slot.id() + "\n"; + } + + for (final TextureSlot slot : Sets.difference(provided, required)) { + message += "TextureSlot provided but not required: " + slot.id() + "\n"; + } + + return Optional.of(message); + } + + private static TexturedModel of(final Optional optionalKey, final Textures textures) { + if (optionalKey.isEmpty()) { + return new TexturedModel(ModelTemplates.PARTICLE_ONLY.get(), textures); + } + + final ResourceKey key = optionalKey.get(); + final Collection templates = ModelTemplate.get(key); + if (templates.isEmpty()) { + final ModelTemplate template = new ModelTemplate(optionalKey, Optional.empty(), List.of()); + return new TexturedModel(template, textures); + } + + if (templates.size() == 1) { + return new TexturedModel(templates.iterator().next(), textures); + } + + for (final ModelTemplate template : templates) { + if (textures.slots().size() == template.slots().size() + && textures.slots().containsAll(template.slots())) { + return new TexturedModel(template, textures); + } + } + + // TODO What to do here? + return new TexturedModel(templates.iterator().next(), textures); + } + + // TODO remove? + public static interface Provider { + + TexturedModel get(ResourceKey key); + + default TexturedModel get(final RegistryKey key) { + return this.get(key.location()); + } + + default TexturedModel block(final ResourceKey key) { + return this.get(ModelUtil.withBlockPrefix(key)); + } + + default TexturedModel block(final RegistryKey key) { + return this.block(key.location()); + } + + default TexturedModel item(final ResourceKey key) { + return this.get(ModelUtil.withItemPrefix(key)); + } + + default TexturedModel item(final RegistryKey key) { + return this.item(key.location()); + } + } +} diff --git a/api/src/main/java/net/hellheim/spongetools/resourcepack/Textures.java b/api/src/main/java/net/hellheim/spongetools/resourcepack/Textures.java new file mode 100644 index 0000000..6317ed4 --- /dev/null +++ b/api/src/main/java/net/hellheim/spongetools/resourcepack/Textures.java @@ -0,0 +1,723 @@ +package net.hellheim.spongetools.resourcepack; + +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; +import java.util.Set; + +import org.spongepowered.api.ResourceKey; +import org.spongepowered.api.util.CopyableBuilder; + +import com.mojang.serialization.Codec; + +import net.hellheim.spongetools.codec.list.SpongeCodecs; +import net.hellheim.spongetools.util.ModelUtil; + +public record Textures(Map textures) { + + public static final Codec CODEC = + Codec.unboundedMap(TextureSlot.CODEC, SpongeCodecs.RESOURCE_KEY) + .xmap(Textures::new, Textures::textures); + + public Textures(final Map textures) { + this.textures = Map.copyOf(textures); + } + + public Set slots() { + return this.textures.keySet(); + } + + public static Builder builder() { + return new Builder(); + } + + // Generic builders + + public static Textures of(final Map textures) { + return new Textures(textures); + } + + public static Textures of( + final TextureSlot slot, final ResourceKey texture + ) { + return Textures.of(Map.of( + slot, texture + )); + } + + public static Textures of( + final TextureSlot slot1, final ResourceKey texture1, + final TextureSlot slot2, final ResourceKey texture2 + ) { + return Textures.of(Map.of( + slot1, texture1, + slot2, texture2 + )); + } + + public static Textures of( + final TextureSlot slot1, final ResourceKey texture1, + final TextureSlot slot2, final ResourceKey texture2, + final TextureSlot slot3, final ResourceKey texture3 + ) { + return Textures.of(Map.of( + slot1, texture1, + slot2, texture2, + slot3, texture3 + )); + } + + public static Textures of( + final TextureSlot slot1, final ResourceKey texture1, + final TextureSlot slot2, final ResourceKey texture2, + final TextureSlot slot3, final ResourceKey texture3, + final TextureSlot slot4, final ResourceKey texture4 + ) { + return Textures.of(Map.of( + slot1, texture1, + slot2, texture2, + slot3, texture3, + slot4, texture4 + )); + } + + public static Textures of( + final TextureSlot slot1, final ResourceKey texture1, + final TextureSlot slot2, final ResourceKey texture2, + final TextureSlot slot3, final ResourceKey texture3, + final TextureSlot slot4, final ResourceKey texture4, + final TextureSlot slot5, final ResourceKey texture5 + ) { + return Textures.of(Map.of( + slot1, texture1, + slot2, texture2, + slot3, texture3, + slot4, texture4, + slot5, texture5 + )); + } + + public static Textures of( + final TextureSlot slot1, final ResourceKey texture1, + final TextureSlot slot2, final ResourceKey texture2, + final TextureSlot slot3, final ResourceKey texture3, + final TextureSlot slot4, final ResourceKey texture4, + final TextureSlot slot5, final ResourceKey texture5, + final TextureSlot slot6, final ResourceKey texture6 + ) { + return Textures.of(Map.of( + slot1, texture1, + slot2, texture2, + slot3, texture3, + slot4, texture4, + slot5, texture5, + slot6, texture6 + )); + } + + public static Textures of( + final TextureSlot slot1, final ResourceKey texture1, + final TextureSlot slot2, final ResourceKey texture2, + final TextureSlot slot3, final ResourceKey texture3, + final TextureSlot slot4, final ResourceKey texture4, + final TextureSlot slot5, final ResourceKey texture5, + final TextureSlot slot6, final ResourceKey texture6, + final TextureSlot slot7, final ResourceKey texture7 + ) { + return Textures.of(Map.of( + slot1, texture1, + slot2, texture2, + slot3, texture3, + slot4, texture4, + slot5, texture5, + slot6, texture6, + slot7, texture7 + )); + } + + public static Textures of( + final ResourceKey texture, + final TextureSlot slot, final String suffix + ) { + return Textures.of( + slot, ModelUtil.withSuffix(texture, suffix) + ); + } + + public static Textures of( + final ResourceKey texture, + final TextureSlot slot1, final String suffix1, + final TextureSlot slot2, final String suffix2 + ) { + return Textures.of( + slot1, ModelUtil.withSuffix(texture, suffix1), + slot2, ModelUtil.withSuffix(texture, suffix2) + ); + } + + public static Textures of( + final ResourceKey texture, + final TextureSlot slot1, final String suffix1, + final TextureSlot slot2, final String suffix2, + final TextureSlot slot3, final String suffix3 + ) { + return Textures.of( + slot1, ModelUtil.withSuffix(texture, suffix1), + slot2, ModelUtil.withSuffix(texture, suffix2), + slot3, ModelUtil.withSuffix(texture, suffix3) + ); + } + + public static Textures of( + final ResourceKey texture, + final TextureSlot slot1, final String suffix1, + final TextureSlot slot2, final String suffix2, + final TextureSlot slot3, final String suffix3, + final TextureSlot slot4, final String suffix4 + ) { + return Textures.of( + slot1, ModelUtil.withSuffix(texture, suffix1), + slot2, ModelUtil.withSuffix(texture, suffix2), + slot3, ModelUtil.withSuffix(texture, suffix3), + slot4, ModelUtil.withSuffix(texture, suffix4) + ); + } + + public static Textures of( + final ResourceKey texture, + final TextureSlot slot1, final String suffix1, + final TextureSlot slot2, final String suffix2, + final TextureSlot slot3, final String suffix3, + final TextureSlot slot4, final String suffix4, + final TextureSlot slot5, final String suffix5 + ) { + return Textures.of( + slot1, ModelUtil.withSuffix(texture, suffix1), + slot2, ModelUtil.withSuffix(texture, suffix2), + slot3, ModelUtil.withSuffix(texture, suffix3), + slot4, ModelUtil.withSuffix(texture, suffix4), + slot5, ModelUtil.withSuffix(texture, suffix5) + ); + } + + public static Textures of( + final ResourceKey texture, + final TextureSlot slot1, final String suffix1, + final TextureSlot slot2, final String suffix2, + final TextureSlot slot3, final String suffix3, + final TextureSlot slot4, final String suffix4, + final TextureSlot slot5, final String suffix5, + final TextureSlot slot6, final String suffix6 + ) { + return Textures.of( + slot1, ModelUtil.withSuffix(texture, suffix1), + slot2, ModelUtil.withSuffix(texture, suffix2), + slot3, ModelUtil.withSuffix(texture, suffix3), + slot4, ModelUtil.withSuffix(texture, suffix4), + slot5, ModelUtil.withSuffix(texture, suffix5), + slot6, ModelUtil.withSuffix(texture, suffix6) + ); + } + + public static Textures of( + final ResourceKey texture, + final TextureSlot slot1, final String suffix1, + final TextureSlot slot2, final String suffix2, + final TextureSlot slot3, final String suffix3, + final TextureSlot slot4, final String suffix4, + final TextureSlot slot5, final String suffix5, + final TextureSlot slot6, final String suffix6, + final TextureSlot slot7, final String suffix7 + ) { + return Textures.of( + slot1, ModelUtil.withSuffix(texture, suffix1), + slot2, ModelUtil.withSuffix(texture, suffix2), + slot3, ModelUtil.withSuffix(texture, suffix3), + slot4, ModelUtil.withSuffix(texture, suffix4), + slot5, ModelUtil.withSuffix(texture, suffix5), + slot6, ModelUtil.withSuffix(texture, suffix6), + slot7, ModelUtil.withSuffix(texture, suffix7) + ); + } + + // Block-specific builders + + public static Textures block( + final TextureSlot slot, final ResourceKey texture + ) { + return Textures.of( + slot, ModelUtil.withBlockPrefix(texture) + ); + } + + public static Textures block( + final TextureSlot slot1, final ResourceKey texture1, + final TextureSlot slot2, final ResourceKey texture2 + ) { + return Textures.of( + slot1, ModelUtil.withBlockPrefix(texture1), + slot2, ModelUtil.withBlockPrefix(texture2) + ); + } + + public static Textures block( + final TextureSlot slot1, final ResourceKey texture1, + final TextureSlot slot2, final ResourceKey texture2, + final TextureSlot slot3, final ResourceKey texture3 + ) { + return Textures.of( + slot1, ModelUtil.withBlockPrefix(texture1), + slot2, ModelUtil.withBlockPrefix(texture2), + slot3, ModelUtil.withBlockPrefix(texture3) + ); + } + + public static Textures block( + final TextureSlot slot1, final ResourceKey texture1, + final TextureSlot slot2, final ResourceKey texture2, + final TextureSlot slot3, final ResourceKey texture3, + final TextureSlot slot4, final ResourceKey texture4 + ) { + return Textures.of( + slot1, ModelUtil.withBlockPrefix(texture1), + slot2, ModelUtil.withBlockPrefix(texture2), + slot3, ModelUtil.withBlockPrefix(texture3), + slot4, ModelUtil.withBlockPrefix(texture4) + ); + } + + public static Textures block( + final TextureSlot slot1, final ResourceKey texture1, + final TextureSlot slot2, final ResourceKey texture2, + final TextureSlot slot3, final ResourceKey texture3, + final TextureSlot slot4, final ResourceKey texture4, + final TextureSlot slot5, final ResourceKey texture5 + ) { + return Textures.of( + slot1, ModelUtil.withBlockPrefix(texture1), + slot2, ModelUtil.withBlockPrefix(texture2), + slot3, ModelUtil.withBlockPrefix(texture3), + slot4, ModelUtil.withBlockPrefix(texture4), + slot5, ModelUtil.withBlockPrefix(texture5) + ); + } + + public static Textures block( + final TextureSlot slot1, final ResourceKey texture1, + final TextureSlot slot2, final ResourceKey texture2, + final TextureSlot slot3, final ResourceKey texture3, + final TextureSlot slot4, final ResourceKey texture4, + final TextureSlot slot5, final ResourceKey texture5, + final TextureSlot slot6, final ResourceKey texture6 + ) { + return Textures.of( + slot1, ModelUtil.withBlockPrefix(texture1), + slot2, ModelUtil.withBlockPrefix(texture2), + slot3, ModelUtil.withBlockPrefix(texture3), + slot4, ModelUtil.withBlockPrefix(texture4), + slot5, ModelUtil.withBlockPrefix(texture5), + slot6, ModelUtil.withBlockPrefix(texture6) + ); + } + + public static Textures block( + final TextureSlot slot1, final ResourceKey texture1, + final TextureSlot slot2, final ResourceKey texture2, + final TextureSlot slot3, final ResourceKey texture3, + final TextureSlot slot4, final ResourceKey texture4, + final TextureSlot slot5, final ResourceKey texture5, + final TextureSlot slot6, final ResourceKey texture6, + final TextureSlot slot7, final ResourceKey texture7 + ) { + return Textures.of( + slot1, ModelUtil.withBlockPrefix(texture1), + slot2, ModelUtil.withBlockPrefix(texture2), + slot3, ModelUtil.withBlockPrefix(texture3), + slot4, ModelUtil.withBlockPrefix(texture4), + slot5, ModelUtil.withBlockPrefix(texture5), + slot6, ModelUtil.withBlockPrefix(texture6), + slot7, ModelUtil.withBlockPrefix(texture7) + ); + } + + public static Textures block( + final ResourceKey texture, + final TextureSlot slot, final String suffix + ) { + return Textures.of(ModelUtil.withBlockPrefix(texture), + slot, suffix + ); + } + + public static Textures block( + final ResourceKey texture, + final TextureSlot slot1, final String suffix1, + final TextureSlot slot2, final String suffix2 + ) { + return Textures.of(ModelUtil.withBlockPrefix(texture), + slot1, suffix1, + slot2, suffix2 + ); + } + + public static Textures block( + final ResourceKey texture, + final TextureSlot slot1, final String suffix1, + final TextureSlot slot2, final String suffix2, + final TextureSlot slot3, final String suffix3 + ) { + return Textures.of(ModelUtil.withBlockPrefix(texture), + slot1, suffix1, + slot2, suffix2, + slot3, suffix3 + ); + } + + public static Textures block( + final ResourceKey texture, + final TextureSlot slot1, final String suffix1, + final TextureSlot slot2, final String suffix2, + final TextureSlot slot3, final String suffix3, + final TextureSlot slot4, final String suffix4 + ) { + return Textures.of(ModelUtil.withBlockPrefix(texture), + slot1, suffix1, + slot2, suffix2, + slot3, suffix3, + slot4, suffix4 + ); + } + + public static Textures block( + final ResourceKey texture, + final TextureSlot slot1, final String suffix1, + final TextureSlot slot2, final String suffix2, + final TextureSlot slot3, final String suffix3, + final TextureSlot slot4, final String suffix4, + final TextureSlot slot5, final String suffix5 + ) { + return Textures.of(ModelUtil.withBlockPrefix(texture), + slot1, suffix1, + slot2, suffix2, + slot3, suffix3, + slot4, suffix4, + slot5, suffix5 + ); + } + + public static Textures block( + final ResourceKey texture, + final TextureSlot slot1, final String suffix1, + final TextureSlot slot2, final String suffix2, + final TextureSlot slot3, final String suffix3, + final TextureSlot slot4, final String suffix4, + final TextureSlot slot5, final String suffix5, + final TextureSlot slot6, final String suffix6 + ) { + return Textures.of(ModelUtil.withBlockPrefix(texture), + slot1, suffix1, + slot2, suffix2, + slot3, suffix3, + slot4, suffix4, + slot5, suffix5, + slot6, suffix6 + ); + } + + public static Textures block( + final ResourceKey texture, + final TextureSlot slot1, final String suffix1, + final TextureSlot slot2, final String suffix2, + final TextureSlot slot3, final String suffix3, + final TextureSlot slot4, final String suffix4, + final TextureSlot slot5, final String suffix5, + final TextureSlot slot6, final String suffix6, + final TextureSlot slot7, final String suffix7 + ) { + return Textures.of(ModelUtil.withBlockPrefix(texture), + slot1, suffix1, + slot2, suffix2, + slot3, suffix3, + slot4, suffix4, + slot5, suffix5, + slot6, suffix6, + slot7, suffix7 + ); + } + + // Item-specific builders + + public static Textures item( + final TextureSlot slot, final ResourceKey texture + ) { + return Textures.of( + slot, ModelUtil.withItemPrefix(texture) + ); + } + + public static Textures item( + final TextureSlot slot1, final ResourceKey texture1, + final TextureSlot slot2, final ResourceKey texture2 + ) { + return Textures.of( + slot1, ModelUtil.withItemPrefix(texture1), + slot2, ModelUtil.withItemPrefix(texture2) + ); + } + + public static Textures item( + final TextureSlot slot1, final ResourceKey texture1, + final TextureSlot slot2, final ResourceKey texture2, + final TextureSlot slot3, final ResourceKey texture3 + ) { + return Textures.of( + slot1, ModelUtil.withItemPrefix(texture1), + slot2, ModelUtil.withItemPrefix(texture2), + slot3, ModelUtil.withItemPrefix(texture3) + ); + } + + public static Textures item( + final TextureSlot slot1, final ResourceKey texture1, + final TextureSlot slot2, final ResourceKey texture2, + final TextureSlot slot3, final ResourceKey texture3, + final TextureSlot slot4, final ResourceKey texture4 + ) { + return Textures.of( + slot1, ModelUtil.withItemPrefix(texture1), + slot2, ModelUtil.withItemPrefix(texture2), + slot3, ModelUtil.withItemPrefix(texture3), + slot4, ModelUtil.withItemPrefix(texture4) + ); + } + + public static Textures item( + final TextureSlot slot1, final ResourceKey texture1, + final TextureSlot slot2, final ResourceKey texture2, + final TextureSlot slot3, final ResourceKey texture3, + final TextureSlot slot4, final ResourceKey texture4, + final TextureSlot slot5, final ResourceKey texture5 + ) { + return Textures.of( + slot1, ModelUtil.withItemPrefix(texture1), + slot2, ModelUtil.withItemPrefix(texture2), + slot3, ModelUtil.withItemPrefix(texture3), + slot4, ModelUtil.withItemPrefix(texture4), + slot5, ModelUtil.withItemPrefix(texture5) + ); + } + + public static Textures item( + final TextureSlot slot1, final ResourceKey texture1, + final TextureSlot slot2, final ResourceKey texture2, + final TextureSlot slot3, final ResourceKey texture3, + final TextureSlot slot4, final ResourceKey texture4, + final TextureSlot slot5, final ResourceKey texture5, + final TextureSlot slot6, final ResourceKey texture6 + ) { + return Textures.of( + slot1, ModelUtil.withItemPrefix(texture1), + slot2, ModelUtil.withItemPrefix(texture2), + slot3, ModelUtil.withItemPrefix(texture3), + slot4, ModelUtil.withItemPrefix(texture4), + slot5, ModelUtil.withItemPrefix(texture5), + slot6, ModelUtil.withItemPrefix(texture6) + ); + } + + public static Textures item( + final TextureSlot slot1, final ResourceKey texture1, + final TextureSlot slot2, final ResourceKey texture2, + final TextureSlot slot3, final ResourceKey texture3, + final TextureSlot slot4, final ResourceKey texture4, + final TextureSlot slot5, final ResourceKey texture5, + final TextureSlot slot6, final ResourceKey texture6, + final TextureSlot slot7, final ResourceKey texture7 + ) { + return Textures.of( + slot1, ModelUtil.withItemPrefix(texture1), + slot2, ModelUtil.withItemPrefix(texture2), + slot3, ModelUtil.withItemPrefix(texture3), + slot4, ModelUtil.withItemPrefix(texture4), + slot5, ModelUtil.withItemPrefix(texture5), + slot6, ModelUtil.withItemPrefix(texture6), + slot7, ModelUtil.withItemPrefix(texture7) + ); + } + + public static Textures item( + final ResourceKey texture, + final TextureSlot slot, final String suffix + ) { + return Textures.of(ModelUtil.withItemPrefix(texture), + slot, suffix + ); + } + + public static Textures item( + final ResourceKey texture, + final TextureSlot slot1, final String suffix1, + final TextureSlot slot2, final String suffix2 + ) { + return Textures.of(ModelUtil.withItemPrefix(texture), + slot1, suffix1, + slot2, suffix2 + ); + } + + public static Textures item( + final ResourceKey texture, + final TextureSlot slot1, final String suffix1, + final TextureSlot slot2, final String suffix2, + final TextureSlot slot3, final String suffix3 + ) { + return Textures.of(ModelUtil.withItemPrefix(texture), + slot1, suffix1, + slot2, suffix2, + slot3, suffix3 + ); + } + + public static Textures item( + final ResourceKey texture, + final TextureSlot slot1, final String suffix1, + final TextureSlot slot2, final String suffix2, + final TextureSlot slot3, final String suffix3, + final TextureSlot slot4, final String suffix4 + ) { + return Textures.of(ModelUtil.withItemPrefix(texture), + slot1, suffix1, + slot2, suffix2, + slot3, suffix3, + slot4, suffix4 + ); + } + + public static Textures item( + final ResourceKey texture, + final TextureSlot slot1, final String suffix1, + final TextureSlot slot2, final String suffix2, + final TextureSlot slot3, final String suffix3, + final TextureSlot slot4, final String suffix4, + final TextureSlot slot5, final String suffix5 + ) { + return Textures.of(ModelUtil.withItemPrefix(texture), + slot1, suffix1, + slot2, suffix2, + slot3, suffix3, + slot4, suffix4, + slot5, suffix5 + ); + } + + public static Textures item( + final ResourceKey texture, + final TextureSlot slot1, final String suffix1, + final TextureSlot slot2, final String suffix2, + final TextureSlot slot3, final String suffix3, + final TextureSlot slot4, final String suffix4, + final TextureSlot slot5, final String suffix5, + final TextureSlot slot6, final String suffix6 + ) { + return Textures.of(ModelUtil.withItemPrefix(texture), + slot1, suffix1, + slot2, suffix2, + slot3, suffix3, + slot4, suffix4, + slot5, suffix5, + slot6, suffix6 + ); + } + + public static Textures item( + final ResourceKey texture, + final TextureSlot slot1, final String suffix1, + final TextureSlot slot2, final String suffix2, + final TextureSlot slot3, final String suffix3, + final TextureSlot slot4, final String suffix4, + final TextureSlot slot5, final String suffix5, + final TextureSlot slot6, final String suffix6, + final TextureSlot slot7, final String suffix7 + ) { + return Textures.of(ModelUtil.withItemPrefix(texture), + slot1, suffix1, + slot2, suffix2, + slot3, suffix3, + slot4, suffix4, + slot5, suffix5, + slot6, suffix6, + slot7, suffix7 + ); + } + + // Other builders + // TODO remove? + + public static Textures all(final ResourceKey texture) { + return Textures.of(TextureSlots.ALL, texture); + } + + public static Textures column(final ResourceKey texture) { + return Textures.of(texture, + TextureSlots.SIDE, "_side", + TextureSlots.END, "_top"); + } + + public static class Builder implements + org.spongepowered.api.util.Builder, + CopyableBuilder { + + private final Map textures = new HashMap<>(); + + public Builder put(final TextureSlot slot, final ResourceKey texture) { + this.textures.put( + Objects.requireNonNull(slot, "slot"), + Objects.requireNonNull(texture, "texture")); + return this; + } + + public Builder putIfAbsent(final TextureSlot slot, final ResourceKey texture) { + this.textures.putIfAbsent( + Objects.requireNonNull(slot, "slot"), + Objects.requireNonNull(texture, "texture")); + return this; + } + + public Builder putAll(final Map textures) { + Objects.requireNonNull(textures, "textures").forEach(this::put); + return this; + } + + public Builder putAll(final ResourceKey texture, final TextureSlot... slots) { + for (final TextureSlot slot : Objects.requireNonNull(slots, "slots")) { + this.put(slot, texture); + } + return this; + } + + public Builder putAll(final ResourceKey texture, final Iterable slots) { + for (final TextureSlot slot : Objects.requireNonNull(slots, "slots")) { + this.put(slot, texture); + } + return this; + } + + @Override + public Builder from(final Textures value) { + this.reset(); + this.textures.putAll(value.textures); + return this; + } + + @Override + public Builder reset() { + this.textures.clear(); + return this; + } + + @Override + public Textures build() { + return new Textures(this.textures); + } + } +} diff --git a/api/src/main/java/net/hellheim/spongetools/resourcepack/block/BlockDefinition.java b/api/src/main/java/net/hellheim/spongetools/resourcepack/block/BlockDefinition.java new file mode 100644 index 0000000..e0e65f6 --- /dev/null +++ b/api/src/main/java/net/hellheim/spongetools/resourcepack/block/BlockDefinition.java @@ -0,0 +1,305 @@ +package net.hellheim.spongetools.resourcepack.block; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.Set; +import java.util.function.Function; +import java.util.function.Supplier; +import java.util.function.UnaryOperator; +import java.util.stream.Stream; + +import org.spongepowered.api.block.BlockState; +import org.spongepowered.api.block.BlockType; +import org.spongepowered.api.registry.DefaultedRegistryType; +import org.spongepowered.api.state.StateProperty; +import org.spongepowered.api.util.CopyableBuilder; + +import com.mojang.datafixers.util.Pair; +import com.mojang.serialization.Codec; + +import net.hellheim.spongetools.SpongeTools; +import net.hellheim.spongetools.codec.list.RegistryCodecs; + +/** + * @see Minecraft Wiki + */ +public interface BlockDefinition { + + Codec CODEC = StateCodec.BLOCK_DEFINITION; + + public static DefaultedRegistryType registry() { + return SpongeTools.Registries.BLOCK_DEFINITION; + } + + public static Codec registryCodec() { + return RegistryCodecs.BLOCK_DEFINITION; + } + + static MultiVariant.Builder variant(final Supplier blockSupplier) { + return BlockDefinition.variant(Objects.requireNonNull(blockSupplier, "blockSupplier").get()); + } + + static MultiVariant.Builder variant(final BlockType block) { + return new MultiVariant.Builder(block); + } + + static MultiPart.Builder part(final Supplier blockSupplier) { + return BlockDefinition.part(Objects.requireNonNull(blockSupplier, "blockSupplier").get()); + } + + static MultiPart.Builder part(final BlockType block) { + return new MultiPart.Builder(block); + } + + BlockType block(); + + Builder toBuilder(); + + BlockDefinition expandWith(BlockState state, VariantListLike variants); + + final class MultiVariant implements BlockDefinition { + + public static final Codec CODEC = StateCodec.MULTI_VARIANT; + + private final BlockType block; + private final StateDispatch dispatch; + + private MultiVariant(final BlockType block, final StateDispatch dispatch) { + this.block = block; + this.dispatch = dispatch.map(VariantListLike::asVariantList); + } + + @Override + public BlockType block() { + return this.block; + } + + public StateDispatch dispatch() { + return this.dispatch; + } + + @Override + public Builder toBuilder() { + return BlockDefinition.variant(this.block()).from(this); + } + + @Override + public MultiVariant expandWith(final BlockState state, final VariantListLike variants) { + return this.withDispatchBuilder(b -> b.expand(state, variants)); + } + + public MultiVariant withDispatchBuilder(final UnaryOperator> dispatchBuilderOperator) { + Objects.requireNonNull(dispatchBuilderOperator, "dispatchBuilderOperator"); + return this.withDispatch(dispatch -> dispatchBuilderOperator.apply(dispatch.toBuilder()).build()); + } + + public MultiVariant withDispatch(final UnaryOperator> dispatchOperator) { + Objects.requireNonNull(dispatchOperator, "dispatchOperator"); + return new MultiVariant(this.block, dispatchOperator.apply(this.dispatch.map(Function.identity()))); + } + + public static final class Builder implements BlockDefinition.Builder { + + private final BlockType block; + private Variant baseVariant; + private final List> dispatches = new ArrayList<>(); + private final Set> seenProperties = new HashSet<>(); + + private Builder(BlockType block) { + this.block = Objects.requireNonNull(block, "block"); + this.reset(); + } + + public final Builder base(final Variant variant) { + this.baseVariant = Objects.requireNonNull(variant, "variant"); + return this; + } + + @SafeVarargs + public final Builder dispatch(final StateDispatch.Builder... builders) { + for (final StateDispatch.Builder builder : Objects.requireNonNull(builders, "builders")) { + this.tryDispatch(Objects.requireNonNull(builder, "builder").build()); + } + return this; + } + + @SafeVarargs + public final Builder dispatch(final StateDispatch... dispatches) { + for (final StateDispatch dispatch : Objects.requireNonNull(dispatches, "dispatches")) { + this.tryDispatch(dispatch); + } + return this; + } + + public final Builder dispatch(final Iterable> dispatches) { + for (final StateDispatch dispatch : Objects.requireNonNull(dispatches, "dispatches")) { + this.tryDispatch(dispatch); + } + return this; + } + + private final void tryDispatch(final StateDispatch dispatch) { + Objects.requireNonNull(dispatch, "dispatch"); + for (final StateProperty property : dispatch.properties()) { + if (!this.block.stateProperties().contains(property)) { + throw new IllegalStateException("Property " + property + " is not defined for block " + this.block); + } else if (!this.seenProperties.add(property)) { + throw new IllegalStateException("Values of property " + property + " already defined for block " + this.block); + } + } + + this.dispatches.add(dispatch); + } + + @Override + public final Builder from(final MultiVariant definition) { + return this.reset().dispatch(Objects.requireNonNull(definition, "definition").dispatch()); + } + + @Override + public final Builder reset() { + this.baseVariant = Variant.empty(); + this.dispatches.clear(); + this.seenProperties.clear(); + return this; + } + + @Override + public final MultiVariant build() { + Stream> stream = Stream.of(Pair.of(StateSelector.empty(), this.baseVariant)); + for (final StateDispatch dispatch : this.dispatches) { + final Map values = dispatch.values(); + stream = stream.flatMap(pair -> { + return values.entrySet().stream().map(entry -> { + final StateSelector selector = pair.getFirst().with(entry.getKey()); + final VariantList variant = pair.getSecond().merge(entry.getValue()); + return Pair.of(selector, variant); + }); + }); + } + + final StateDispatch.Builder builder = StateDispatch.raw(); + stream.forEach(pair -> builder.add(pair.getFirst(), pair.getSecond())); + return new MultiVariant(this.block, builder.build()); + } + } + } + + final class MultiPart implements BlockDefinition { + + private final BlockType block; + private final List parts; + + private MultiPart(final BlockType block, final List parts) { + this.block = block; + this.parts = List.copyOf(parts); + } + + @Override + public BlockType block() { + return this.block; + } + + public List parts() { + return this.parts; + } + + @Override + public Builder toBuilder() { + return BlockDefinition.part(this.block()).from(this); + } + + @Override + public BlockDefinition expandWith(final BlockState state, final VariantListLike variant) { + final Builder builder = BlockDefinition.part(this.block); + final StateCondition isState = StateCondition.is(state); + builder.add(StatePart.of(isState, variant)); + + final StateCondition notState = isState.negate(); + this.parts.forEach(part -> { + final Optional oldCondition = part.condition(); + final StateCondition newCondition = oldCondition.isEmpty() + ? notState + : StateCondition.and(oldCondition.get(), notState); + builder.add(StatePart.of(newCondition, part.variants())); + }); + + return builder.build(); + } + + public static final class Builder implements BlockDefinition.Builder { + + private final BlockType block; + private final List parts = new ArrayList<>(); + private final Set> seenProperties = new HashSet<>(); + + private Builder(final BlockType block) { + this.block = Objects.requireNonNull(block, "block"); + } + + public Builder add(final StatePart... parts) { + for (final StatePart part : Objects.requireNonNull(parts, "parts")) { + this.addPart(part); + } + + return this; + } + + public Builder add(final Iterable parts) { + for (final StatePart part : Objects.requireNonNull(parts, "parts")) { + this.addPart(part); + } + + return this; + } + + private void addPart(final StatePart part) { + Objects.requireNonNull(part, "part"); + if (part.condition().orElse(null) == StateCondition.alwaysFalse()) { + // @see #build + // return; + } + + for (final StateProperty property : part.properties()) { + if (!this.block.stateProperties().contains(property)) { + throw new IllegalStateException("Property " + property + " is not defined for block " + this.block); + } + } + } + + @Override + public Builder from(final MultiPart definition) { + return this.reset().add(Objects.requireNonNull(definition, "definition").parts()); + } + + @Override + public Builder reset() { + this.parts.clear(); + this.seenProperties.clear(); + return this; + } + + @Override + public MultiPart build() { + if (this.parts.isEmpty()) { + // Client will throw exception if there is no + // at least one StatePart with at least one Variant + // even if this part will never apply (e.g. with StateCondition#alwaysFalse). + // Maybe it's fine to just add always-false part if no parts are provided. + throw new IllegalStateException("At least one StatePart must be provided"); + } + + return new MultiPart(this.block, this.parts); + } + } + } + + interface Builder> extends + org.spongepowered.api.util.Builder, + CopyableBuilder> { + } +} diff --git a/api/src/main/java/net/hellheim/spongetools/resourcepack/block/StateCodec.java b/api/src/main/java/net/hellheim/spongetools/resourcepack/block/StateCodec.java new file mode 100644 index 0000000..3f70c66 --- /dev/null +++ b/api/src/main/java/net/hellheim/spongetools/resourcepack/block/StateCodec.java @@ -0,0 +1,335 @@ +package net.hellheim.spongetools.resourcepack.block; + +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Set; + +import org.checkerframework.checker.nullness.qual.Nullable; +import org.spongepowered.api.block.BlockType; +import org.spongepowered.api.state.StateContainer; +import org.spongepowered.api.state.StateProperty; + +import com.google.common.base.Splitter; +import com.mojang.datafixers.util.Pair; +import com.mojang.serialization.Codec; +import com.mojang.serialization.DataResult; +import com.mojang.serialization.DynamicOps; + +interface StateCodec extends Codec { + + Codec> STATE_PROPERTY_VALUE = new StateCodec<>() { + + @Override + public DataResult encode( + final StatePropertyValue input, final DynamicOps ops, final T prefix + ) { + return DataResult.success(ops.createString(input.serializationString())); + } + + @Override + public DataResult, T>> read( + final StateOps ops, final T input + ) { + return ops.getStringValue(input).flatMap(fullProperty -> + StateCodec.decodePropertyValue(ops, fullProperty) + .map(value -> Pair.of(value, ops.empty()))); + } + }; + + Codec STATE_SELECTOR = new StateCodec<>() { + + @Override + public DataResult encode( + final StateSelector input, final DynamicOps ops, final T prefix + ) { + return DataResult.success(ops.createString(input.serializationString())); + } + + @Override + public DataResult> read( + final StateOps ops, final T input + ) { + return ops.getStringValue(input).flatMap(selector -> + StateCodec.decodeSelector(ops, selector) + .map(value -> Pair.of(value, ops.empty()))); + } + }; + + // TODO TEST THIS TEST THIS TEST THIS + Codec STATE_CONDITION = new StateCodec<>() { + + @Override + public DataResult encode( + final StateCondition input, final DynamicOps ops, final T prefix + ) { + return switch (input) { + case final StateCondition.OrCondition condition -> + StateCondition.LIST_CODEC.encode(condition.conditions(), ops, prefix) + .map(conditions -> ops.set(ops.emptyMap(), "OR", conditions)); + case final StateCondition.AndCondition condition -> + StateCondition.LIST_CODEC.encode(condition.conditions(), ops, prefix); + case final StateCondition.PropertyCondition condition -> + ops.mergeToMap(prefix, + ops.createString(condition.property().name()), + ops.createString(condition.valuesString())); + default -> DataResult.error(() -> "Unknown StateCondition: " + input); + }; + } + + @Override + public DataResult> read( + final StateOps ops, final T input + ) { + return ops.getMap(input).flatMap(map -> { + final long amount = map.entries().count(); + if (amount == 0) { + return DataResult.error(() -> "No conditions found"); + } else if (amount == 1) { + final @Nullable T or = map.get("OR"); + if (or != null) { + return StateCondition.LIST_CODEC.decode(ops, or) + .map(pair -> Pair.of(StateCondition.or(pair.getFirst()), pair.getSecond())); + } + + final @Nullable T and = map.get("AND"); + if (and != null) { + return StateCondition.LIST_CODEC.decode(ops, and) + .map(pair -> Pair.of(StateCondition.and(pair.getFirst()), pair.getSecond())); + } + + return StateCodec.decodePropertyCondition(ops, map.entries().findAny().get()) + .map(condition -> Pair.of(condition, ops.empty())); + } else { + final List conditions = map.entries() + .map(entry -> StateCodec.decodePropertyCondition(ops, entry)) + .filter(DataResult::isSuccess) + .map(DataResult::getOrThrow) + .toList(); + + return conditions.isEmpty() + ? DataResult.error(() -> "No conditions found") + : DataResult.success(Pair.of(StateCondition.and(conditions), ops.empty())); + } + }); + } + }; + + Codec MULTI_VARIANT = new StateCodec<>() { + + @Override + public DataResult encode( + final BlockDefinition.MultiVariant input, final DynamicOps ops, final T prefix + ) { + return StateDispatch.CODEC.encode(input.dispatch(), ops, prefix) + .map(dispatch -> ops.set(ops.emptyMap(), StateCodec.KEY_VARIANT, dispatch)); + } + + @Override + public DataResult> read( + final StateOps ops, final T input + ) { + if (!(ops.getAsStateContainer() instanceof final BlockType block)) { + return DataResult.error(() -> "StateOps container not a BlockType"); + } + + return ops.get(input, StateCodec.KEY_VARIANT) + .flatMap(dispatch -> StateDispatch.CODEC.decode(ops, dispatch)) + .map(pair -> Pair.of(BlockDefinition.variant(block).dispatch(pair.getFirst()).build(), pair.getSecond())); + } + }; + + Codec MULTI_PART = new StateCodec<>() { + + @Override + public DataResult encode( + final BlockDefinition.MultiPart input, final DynamicOps ops, final T prefix + ) { + return StatePart.LIST_CODEC.encode(input.parts(), ops, prefix) + .map(parts -> ops.set(ops.emptyMap(), StateCodec.KEY_PART, parts)); + } + + @Override + public DataResult> read( + final StateOps ops, final T input + ) { + if (!(ops.getAsStateContainer() instanceof final BlockType block)) { + return DataResult.error(() -> "StateOps container not a BlockType"); + } + + return ops.get(input, StateCodec.KEY_PART) + .flatMap(parts -> StatePart.LIST_CODEC.decode(ops, parts)) + .map(pair -> Pair.of(BlockDefinition.part(block).add(pair.getFirst()).build(), pair.getSecond())); + } + }; + + Codec BLOCK_DEFINITION = new Codec<>() { + + @Override + public DataResult encode( + final BlockDefinition input, final DynamicOps ops, final T prefix + ) { + return switch (input) { + case BlockDefinition.MultiVariant definition -> + BlockDefinition.MultiVariant.CODEC.encode(definition, ops, prefix); + case BlockDefinition.MultiPart definition -> + BlockDefinition.MultiPart.CODEC.encode(definition, ops, prefix); + default -> DataResult.error(() -> "Unknown BlockDefinition: " + input); + }; + } + + @SuppressWarnings("unchecked") + @Override + public DataResult> decode( + final DynamicOps ops, final T input + ) { + final @Nullable T variant = ops.get(input, StateCodec.KEY_VARIANT).result().orElse(null); + if (variant != null) { + return (DataResult>) (Object) + BlockDefinition.MultiVariant.CODEC.decode(ops, input); + } + + final @Nullable T part = ops.get(input, StateCodec.KEY_PART).result().orElse(null); + if (part != null) { + return (DataResult>) (Object) + BlockDefinition.MultiPart.CODEC.decode(ops, input); + } + + return DataResult.error(() -> "Unknown BlockDefinition"); + } + }; + + String KEY_VARIANT = "variants"; + String KEY_PART = "multipart"; + + Splitter COMMA_SPLITTER = Splitter.on(','); + Splitter EQUAL_SPLITTER = Splitter.on('=').limit(2); + Splitter PIPE_SPLITTER = Splitter.on('|').omitEmptyStrings(); + + DataResult> read(StateOps ops, T input); + + @Override + default DataResult> decode(final DynamicOps ops, final T input) { + return ops instanceof final StateOps stateOps + ? this.read(stateOps, input) + : DataResult.error(() -> "Not a StateOps"); + } + + static DataResult decodeSelector(final StateContainer container, final String selector) { + if (selector.isEmpty()) { + return DataResult.success(StateSelector.empty()); + } + + final StateSelector.Builder builder = StateSelector.builder(); + for (final String property : COMMA_SPLITTER.split(selector)) { + final var value = StateCodec.decodePropertyValue(container, property); + if (value.isError()) { + return DataResult.error(value.error().get().messageSupplier(), builder.build()); + } + + builder.add(value.result().get()); + } + + return DataResult.success(builder.build()); + } + + static DataResult> decodePropertyValue( + final StateContainer container, final String fullProperty + ) { + final Iterator propertyData = EQUAL_SPLITTER.split(fullProperty).iterator(); + + final String propertyString = propertyData.next(); + if (propertyString.isEmpty()) { + return DataResult.error(() -> "Empty state property"); + } + + final @Nullable StateProperty property = container.findStateProperty(propertyString).orElse(null); + if (property == null) { + return DataResult.error(() -> String.format( + "Unknown state property: '%s'", + propertyString + )); + } + + if (!propertyData.hasNext()) { + return DataResult.error(() -> String.format( + "State property without value: '%s'", + propertyString + )); + } + + final String valueString = propertyData.next(); + if (valueString.isEmpty()) { + return DataResult.error(() -> "Empty state property value"); + } + + final @Nullable Comparable value = property.parseValue(valueString).orElse(null); + if (value == null) { + return DataResult.error(() -> String.format( + "Unknown value: '%s' for state property: '%s' %s", + valueString, propertyString, property.possibleValues() + )); + } + + return DataResult.success(StatePropertyValue.ofRaw(property, value)); + } + + static DataResult decodePropertyCondition( + final StateOps ops, final Pair entry + ) { + final DataResult key = ops.getStringValue(entry.getFirst()); + if (key.isError()) { + return DataResult.error(key.error().get().messageSupplier()); + } + + final String propertyName = key.getOrThrow(); + final @Nullable StateProperty property = ops.findStateProperty(propertyName).orElse(null); + if (property == null) { + return DataResult.error(() -> String.format( + "Unknown state property '%' on container '%'", + propertyName, ops.getAsStateContainer() + )); + } + + final DataResult value = ops.getStringValue(entry.getSecond()); + if (value.isError()) { + return DataResult.error(value.error().get().messageSupplier()); + } + + String values = value.getOrThrow(); + final boolean negate = !values.isEmpty() && values.charAt(0) == '!'; + if (negate) { + values = values.substring(1); + } + + if (values.isEmpty()) { + return DataResult.error(() -> String.format( + "Empty property values for property '%s' on container '%'", + propertyName, ops.getAsStateContainer() + )); + } + + return StateCodec.decodePropertyConditionValues(ops, property, negate, values); + } + + static > DataResult decodePropertyConditionValues( + final StateOps ops, + final StateProperty property, final boolean negate, final String valueNames + ) { + final Set values = new HashSet<>(); + for (final String valueName : StateCodec.PIPE_SPLITTER.split(valueNames)) { + final @Nullable T value = property.parseValue(valueName).orElse(null); + if (value == null) { + return DataResult.error(() -> String.format( + "Unknown value '%' for property '%' in '%' on container '%'", + valueName, property.name(), valueNames, ops.getAsStateContainer() + ), + StateCondition.property(property, negate, values)); + } + + values.add(value); + } + + return DataResult.success(StateCondition.property(property, negate, values)); + } +} diff --git a/api/src/main/java/net/hellheim/spongetools/resourcepack/block/StateCondition.java b/api/src/main/java/net/hellheim/spongetools/resourcepack/block/StateCondition.java new file mode 100644 index 0000000..e4a22c8 --- /dev/null +++ b/api/src/main/java/net/hellheim/spongetools/resourcepack/block/StateCondition.java @@ -0,0 +1,543 @@ +package net.hellheim.spongetools.resourcepack.block; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Iterator; +import java.util.List; +import java.util.Objects; +import java.util.Optional; +import java.util.Set; +import java.util.function.Consumer; +import java.util.function.Function; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import org.checkerframework.checker.nullness.qual.Nullable; +import org.spongepowered.api.state.State; +import org.spongepowered.api.state.StateProperty; + +import com.google.common.collect.ImmutableSortedSet; +import com.google.common.collect.Sets; +import com.google.common.collect.Streams; +import com.mojang.serialization.Codec; + +public interface StateCondition extends StatePredicate { + + Codec CODEC = StateCodec.STATE_CONDITION; + + Codec> LIST_CODEC = StateCondition.CODEC.listOf(); + + static StateCondition alwaysTrue() { + return CompositeCondition.ALWAYS_TRUE; + } + + static StateCondition alwaysFalse() { + return CompositeCondition.ALWAYS_FALSE; + } + + static StateCondition always(final boolean value) { + return value ? StateCondition.alwaysTrue() : StateCondition.alwaysFalse(); + } + + static AndCondition.Builder and() { + return new AndCondition.Builder(); + } + + static StateCondition and(final Consumer configurator) { + final AndCondition.Builder builder = StateCondition.and(); + Objects.requireNonNull(configurator, "configurator").accept(builder); + return builder.build(); + } + + static StateCondition and(final StateCondition... conditions) { + return StateCondition.and().add(conditions).build(); + } + + static StateCondition and(final Iterable conditions) { + return StateCondition.and().add(conditions).build(); + } + + static StateCondition and(final Stream conditions) { + return StateCondition.and().add(conditions).build(); + } + + static OrCondition.Builder or() { + return new OrCondition.Builder(); + } + + static StateCondition or(final Consumer configurator) { + final OrCondition.Builder builder = StateCondition.or(); + Objects.requireNonNull(configurator, "configurator").accept(builder); + return builder.build(); + } + + static StateCondition or(final StateCondition... conditions) { + return StateCondition.or().add(conditions).build(); + } + + static StateCondition or(final Iterable conditions) { + return StateCondition.or().add(conditions).build(); + } + + static StateCondition or(final Stream conditions) { + return StateCondition.or().add(conditions).build(); + } + + static > StateCondition property( + final StateProperty property, final boolean inverse, final Collection values + ) { + return PropertyCondition.of(property, inverse, Set.copyOf(values)); + } + + @SafeVarargs + static > StateCondition property( + final StateProperty property, final boolean inverse, final T... values + ) { + return StateCondition.property(property, inverse, Set.of(values)); + } + + @SafeVarargs + static > StateCondition is( + final StateProperty property, final T... values + ) { + return StateCondition.property(property, false, values); + } + + static > StateCondition is( + final StateProperty property, final Collection values + ) { + return StateCondition.property(property, false, values); + } + + static > StateCondition is( + final StatePropertyValue value + ) { + return StateCondition.is(value.property(), value.propertyValue()); + } + + static StateCondition is(final State state) { + return StateCondition.and(StatePropertyValue.allOf(state).map(StateCondition::is)); + } + + @SafeVarargs + static > StateCondition not( + final StateProperty property, final T... values + ) { + return StateCondition.property(property, true, values); + } + + static > StateCondition not( + final StateProperty property, final Collection values + ) { + return StateCondition.property(property, true, values); + } + + static > StateCondition not( + final StatePropertyValue value + ) { + return StateCondition.not(value.property(), value.propertyValue()); + } + + static StateCondition not(final State state) { + return StateCondition.or(StatePropertyValue.allOf(state).map(StateCondition::not)); + } + + Set> properties(); + + StateCondition negate(); + + @Override + String toString(); + + abstract class CompositeCondition implements StateCondition { + + private static final AndCondition ALWAYS_TRUE = new AndCondition(List.of(), Set.of()); + private static final OrCondition ALWAYS_FALSE = new OrCondition(List.of(), Set.of()); + + private final List conditions; + private final Set> properties; + + private CompositeCondition( + final List conditions, final Set> properties + ) { + this.conditions = List.copyOf(conditions); + this.properties = Set.copyOf(properties); + } + + protected abstract StateCondition negate0(); + + protected abstract String toString0(); + + public List conditions() { + return this.conditions; + } + + @Override + public Set> properties() { + return this.properties; + } + + @Override + public StateCondition negate() { + if (this == StateCondition.alwaysFalse()) { + return StateCondition.alwaysTrue(); + } else if (this == StateCondition.alwaysTrue()) { + return StateCondition.alwaysFalse(); + } else { + return this.negate0(); + } + } + + @Override + public String toString() { + return String.format("%sCondition[%s]", + this.toString0(), + this.conditions.stream().map(StateCondition::toString).collect(Collectors.joining(","))); + } + + public static abstract class Builder> + implements org.spongepowered.api.util.Builder { + + private final boolean bias; + protected final List conditions = new ArrayList<>(); + protected boolean always; + + private Builder(final boolean bias) { + this.bias = bias; + this.reset(); + } + + @SuppressWarnings("unchecked") + private B cast() { + return (B) this; + } + + protected abstract void tryAdd(StateCondition condition); + + protected abstract > StateCondition merge( + PropertyCondition first, PropertyCondition second + ); + + protected abstract CompositeCondition build0(Set> properties); + + public B add(final StateCondition... conditions) { + return this.add(Arrays.stream(conditions)); + } + + public B add(final Iterable conditions) { + return this.add(Streams.stream(conditions)); + } + + public B add(final Stream conditions) { + Objects.requireNonNull(conditions, "conditions").forEach(condition -> { + this.addSingle(Objects.requireNonNull(condition, "condition")); + }); + + return this.cast(); + } + + private void addSingle(final StateCondition condition) { + if (this.always) { + return; + } else if (condition instanceof final PropertyCondition property) { + this.merge(property); + } else { + this.tryAdd(condition); + } + } + + private > void merge(final PropertyCondition condition) { + @SuppressWarnings("unchecked") + final @Nullable PropertyCondition current = this.conditions.stream() + .filter(PropertyCondition.class::isInstance) + .map(PropertyCondition.class::cast) + .filter(c -> c.property().equals(condition.property())) + .map(c -> (PropertyCondition) c) + .findAny() + .orElse(null); + + if (current == null) { + this.tryAdd(condition); + } else { + this.conditions.remove(current); + this.tryAdd(this.merge(current, condition)); + } + } + + @Override + public B reset() { + this.conditions.clear(); + this.always = false; + return this.cast(); + } + + @Override + public StateCondition build() { + if (this.conditions.isEmpty()) { + return StateCondition.always(this.bias); + } else if (this.conditions.size() == 1) { + return this.conditions.get(0); + } + + final Set> properties = this.conditions.stream() + .flatMap(condition -> condition.properties().stream()) + .collect(Collectors.toSet()); + final CompositeCondition condition = this.build0(properties); + + final Iterator selectors = StateSelector.populate(properties).iterator(); + int positive = 0, negative = 0; + while (selectors.hasNext()) { + final StateSelector selector = selectors.next(); + if (condition.test(selector)) { + ++positive; + } else { + ++negative; + } + } + + if (positive == 0) { + return StateCondition.alwaysFalse(); + } else if (negative == 0) { + return StateCondition.alwaysTrue(); + } else { + return condition; + } + } + } + } + + final class AndCondition extends CompositeCondition { + + private AndCondition( + final List conditions, final Set> properties + ) { + super(conditions, properties); + } + + @Override + public boolean test( + final Function, Optional>> propertyLookup + ) { + for (final StateCondition condition : this.conditions()) { + if (!condition.test(propertyLookup)) { + return false; + } + } + + return true; + } + + @Override + protected StateCondition negate0() { + return new OrCondition( + this.conditions().stream().map(StateCondition::negate).toList(), + this.properties()); + } + + @Override + protected String toString0() { + return "And"; + } + + public static final class Builder extends CompositeCondition.Builder { + + private Builder() { + super(true); + } + + @Override + protected void tryAdd(final StateCondition condition) { + if (condition instanceof final AndCondition and) { + this.add(and.conditions()); + } else if (condition == StateCondition.alwaysFalse()) { + this.conditions.clear(); + this.always = true; + } else if (condition != StateCondition.alwaysTrue()) { + this.conditions.add(condition); + } + } + + @Override + protected > @Nullable StateCondition merge( + final PropertyCondition first, final PropertyCondition second + ) { + final StateProperty property = first.property(); + final boolean inverse = first.inverse() && second.inverse(); + final Set values = first.inverse() == second.inverse() + ? first.inverse() + ? Sets.union(first.values(), second.values()) + : Sets.intersection(first.values(), second.values()) + : first.inverse() + ? Sets.difference(second.values(), first.values()) + : Sets.difference(first.values(), second.values()); + + return StateCondition.property(property, inverse, values); + }; + + @Override + protected CompositeCondition build0(final Set> properties) { + return new AndCondition(this.conditions, properties); + } + } + } + + final class OrCondition extends CompositeCondition { + + private OrCondition( + final List conditions, final Set> properties + ) { + super(conditions, properties); + } + + @Override + public boolean test( + final Function, Optional>> propertyLookup + ) { + for (final StateCondition condition : this.conditions()) { + if (condition.test(propertyLookup)) { + return true; + } + } + + return false; + } + + @Override + protected StateCondition negate0() { + return new AndCondition( + this.conditions().stream().map(StateCondition::negate).toList(), + this.properties()); + } + + @Override + protected String toString0() { + return "Or"; + } + + public static final class Builder extends CompositeCondition.Builder { + + private Builder() { + super(false); + } + + @Override + protected void tryAdd(final StateCondition condition) { + if (condition instanceof final OrCondition or) { + this.add(or.conditions()); + } else if (condition == StateCondition.alwaysTrue()) { + this.conditions.clear(); + this.always = true; + } else if (condition != StateCondition.alwaysFalse()) { + this.conditions.add(condition); + } + } + + @Override + protected > StateCondition merge( + final PropertyCondition first, final PropertyCondition second + ) { + final StateProperty property = first.property(); + final boolean inverse = !first.inverse() && !second.inverse(); + final Set values = first.inverse() == second.inverse() + ? first.inverse() + ? Sets.intersection(first.values(), second.values()) + : Sets.union(first.values(), second.values()) + : first.inverse() + ? Sets.difference(first.values(), second.values()) + : Sets.difference(second.values(), first.values()); + + return StateCondition.property(property, inverse, values); + }; + + @Override + protected CompositeCondition build0(final Set> properties) { + return new OrCondition(this.conditions, properties); + } + } + } + + final class PropertyCondition> implements StateCondition { + + private final StateProperty property; + private final boolean inverse; + private final Set values; + + private PropertyCondition( + final StateProperty property, final boolean inverse, final Set values + ) { + this.property = property; + this.inverse = inverse; + this.values = ImmutableSortedSet.copyOf(values); + } + + private static > StateCondition of( + final StateProperty property, final boolean inverse, final Set values + ) { + Objects.requireNonNull(property, "property"); + Objects.requireNonNull(values, "values"); + if (values.isEmpty()) { + return StateCondition.always(inverse); + } + + final Set possibleValues = Set.copyOf(property.possibleValues()); + final Set illegalValues = Sets.difference(values, possibleValues); + if (!illegalValues.isEmpty()) { + throw new IllegalArgumentException(String.format( + "Property '%s' does not support values '%'", + property.name(), illegalValues + )); + } + + if (values.size() == possibleValues.size()) { + return StateCondition.always(!inverse); + } + + return new PropertyCondition<>(property, inverse, values); + } + + public StateProperty property() { + return this.property; + } + + public boolean inverse() { + return this.inverse; + } + + public Set values() { + return this.values; + } + + public String valuesString() { + final String values = this.values.stream() + .map(value -> StatePropertyValue.of(this.property, value).valueName()) + .collect(Collectors.joining("|")); + return this.inverse ? "!" + values : values; + } + + @Override + public boolean test( + final Function, Optional>> propertyLookup + ) { + return propertyLookup.apply(this.property) + .map(value -> this.inverse ^ this.values.contains(value)) + .orElse(false); + } + + @Override + public Set> properties() { + return Set.of(this.property); + } + + @Override + public StateCondition.PropertyCondition negate() { + return new PropertyCondition<>(this.property, !this.inverse, this.values); + } + + @Override + public String toString() { + return String.format("PropertyCondition[property=%s;inverse=%s;values=%s]", + this.property.name(), this.inverse, this.valuesString()); + } + } +} diff --git a/api/src/main/java/net/hellheim/spongetools/resourcepack/block/StateDispatch.java b/api/src/main/java/net/hellheim/spongetools/resourcepack/block/StateDispatch.java new file mode 100644 index 0000000..7fc2975 --- /dev/null +++ b/api/src/main/java/net/hellheim/spongetools/resourcepack/block/StateDispatch.java @@ -0,0 +1,746 @@ +package net.hellheim.spongetools.resourcepack.block; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.Set; +import java.util.function.BiFunction; +import java.util.function.Function; +import java.util.function.Predicate; +import java.util.function.Supplier; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import org.spongepowered.api.state.State; +import org.spongepowered.api.state.StateProperty; +import org.spongepowered.api.util.CopyableBuilder; + +import com.google.common.collect.Maps; +import com.google.common.collect.Streams; +import com.mojang.serialization.Codec; +import com.mojang.serialization.DataResult; + +import net.hellheim.spongetools.function.QuadFunction; +import net.hellheim.spongetools.function.QuinFunction; +import net.hellheim.spongetools.function.TriFunction; + +public class StateDispatch { + + public static final Codec> CODEC = StateDispatch.codec(VariantList.CODEC); + + private final Set> properties; + private final Map values; + + protected StateDispatch(final Map values) { + this(values, values.keySet().stream() + .flatMap(selector -> selector.properties().stream()) + .collect(Collectors.toSet())); + } + + protected StateDispatch(final Map values, final Set> properties) { + this.values = Map.copyOf(values); + this.properties = Set.copyOf(properties); + } + + public static Codec> codec(final Codec valueCodec) { + return Codec + .unboundedMap(StateSelector.CODEC, valueCodec) + .xmap(StateDispatch::new, StateDispatch::values) + .validate(StateDispatch::validate); + } + + public static StateDispatch of(final V value) { + return StateDispatch.raw().add(StateSelector.empty(), value).build(); + } + + public static Builder raw() { + return new Builder<>(); + } + + public static < + V, + T1 extends Comparable> + P1.Builder of( + final StateProperty p1 + ) { + return new P1.Builder<>(p1); + } + + public static < + V, + T1 extends Comparable, + T2 extends Comparable> + P2.Builder of( + final StateProperty p1, + final StateProperty p2 + ) { + return new P2.Builder<>(p1, p2); + } + + public static < + V, + T1 extends Comparable, + T2 extends Comparable, + T3 extends Comparable> + P3.Builder of( + final StateProperty p1, + final StateProperty p2, + final StateProperty p3 + ) { + return new P3.Builder<>(p1, p2, p3); + } + + public static < + V, + T1 extends Comparable, + T2 extends Comparable, + T3 extends Comparable, + T4 extends Comparable> + P4.Builder of( + final StateProperty p1, + final StateProperty p2, + final StateProperty p3, + final StateProperty p4 + ) { + return new P4.Builder<>(p1, p2, p3, p4); + } + + public static < + V, + T1 extends Comparable, + T2 extends Comparable, + T3 extends Comparable, + T4 extends Comparable, + T5 extends Comparable> + P5.Builder of( + final StateProperty p1, + final StateProperty p2, + final StateProperty p3, + final StateProperty p4, + final StateProperty p5 + ) { + return new P5.Builder<>(p1, p2, p3, p4, p5); + } + + private static DataResult> validate(final StateDispatch dispatch) { + return StateDispatch.validate(dispatch.values, dispatch.properties) + .>>map(msg -> DataResult.error(() -> msg)) + .orElse(DataResult.success(dispatch)); + } + + private static Optional validate( + final Map values, + final Set> properties + ) { + if (values.isEmpty()) { + return Optional.of("Dispatch must contain at least one selector"); + } + + final List missingSelectors = StateSelector.populate(properties) + .filter(fullSelector -> { + for (final StateSelector selector : values.keySet()) { + if (selector.test(fullSelector)) { + return false; + } + } + return true; + }) + .toList(); + + if (!missingSelectors.isEmpty()) { + String message = "Missing selectors for property sets:"; + for (final StateSelector selector : missingSelectors) { + message += "\n\t\t" + selector.serializationString(); + } + return Optional.of(message); + } + + return Optional.empty(); + } + + public Set> properties() { + return this.properties; + } + + public Set selectors() { + return this.values.keySet(); + } + + public Map values() { + return this.values; + } + + public Builder toBuilder() { + return StateDispatch.raw().from(this); + } + + public StateDispatch map(final Function mapper) { + Objects.requireNonNull(mapper, "mapper"); + return new StateDispatch<>(Maps.transformValues(this.values, mapper::apply), this.properties); + } + + public V getForAnyState(final Iterable> states) { + return this.getFor(s -> s.matchAnyState(states), () -> Streams.stream(states) + .map(State::toString).collect(Collectors.joining("; ", "States[", "]"))); + } + + public V getForAnyState(final State... states) { + return this.getFor(s -> s.matchAnyState(states), () -> Arrays.stream(states) + .map(State::toString).collect(Collectors.joining("; ", "States[", "]"))); + } + + public V getForAnySelector(final Iterable selectors) { + return this.getFor(s -> s.matchAnySelector(selectors), () -> Streams.stream(selectors) + .map(StateSelector::toString).collect(Collectors.joining("; ", "Selectors[", "]"))); + } + + public V getForAnySelector(final StateSelector... selectors) { + return this.getFor(s -> s.matchAnySelector(selectors), () -> Arrays.stream(selectors) + .map(StateSelector::toString).collect(Collectors.joining("; ", "Selectors[", "]"))); + } + + public V getFor(final State state) { + return this.getFor(s -> s.test(state), state::toString); + } + + public V getFor(final StateSelector selector) { + return this.getFor(s -> s.test(selector), selector::toString); + } + + private V getFor(final Predicate selectorPredicate, final Supplier name) { + for (final var entry : this.values.entrySet()) { + if (selectorPredicate.test(entry.getKey())) { + return entry.getValue(); + } + } + + throw new IllegalArgumentException(String.format( + "Provided argument (%s) does not match any selector in the dispatch (%s)", + name.get(), this.toString())); + } + + @Override + public int hashCode() { + return this.values.hashCode(); + } + + @Override + public boolean equals(final Object obj) { + if (this == obj) { + return true; + } else if (!(obj instanceof StateDispatch dispatch)) { + return false; + } else { + return this.values.equals(dispatch.values); + } + } + + @Override + public String toString() { + return this.values.entrySet().stream() + .map(entry -> entry.getKey().serializationString() + ": " + entry.getValue().toString()) + .collect(Collectors.joining("; ", "StateDispatch[", "]")); + } + + public static final class P1< + V, + T1 extends Comparable> + extends StateDispatch { + + private final StateProperty p1; + + private P1( + final Map values, + final Set> properties, + final StateProperty p1 + ) { + super(values, properties); + this.p1 = p1; + } + + @Override + public Builder toBuilder() { + return StateDispatch.of(this.p1).from(this); + } + + public static final class Builder< + V, + T1 extends Comparable> + extends StateDispatch.Builder> { + + private final StateProperty p1; + + private Builder( + final StateProperty p1 + ) { + this.p1 = Objects.requireNonNull(p1, "property"); + } + + public Builder add(final T1 v1, final V value) { + return this.add(StateSelector.builder().add(this.p1, v1), value); + } + + public Builder generate(final Function generator) { + Objects.requireNonNull(generator, "generator"); + this.p1.possibleValues().forEach(v1 -> + this.add(v1, generator.apply(v1))); + return this; + } + + @Override + public P1 build() { + this.validate(); + return new P1<>(this.values, this.properties, this.p1); + } + } + } + + public static final class P2< + V, + T1 extends Comparable, + T2 extends Comparable> + extends StateDispatch { + + private final StateProperty p1; + private final StateProperty p2; + + private P2( + final Map values, + final Set> properties, + final StateProperty p1, + final StateProperty p2 + ) { + super(values, properties); + this.p1 = p1; + this.p2 = p2; + } + + @Override + public Builder toBuilder() { + return StateDispatch.of(this.p1, this.p2).from(this); + } + + public static final class Builder< + V, + T1 extends Comparable, + T2 extends Comparable> + extends StateDispatch.Builder> { + + private final StateProperty p1; + private final StateProperty p2; + + private Builder( + final StateProperty p1, + final StateProperty p2 + ) { + this.p1 = Objects.requireNonNull(p1, "property1"); + this.p2 = Objects.requireNonNull(p2, "property2"); + } + + public Builder add(final T1 v1, final T2 v2, final V value) { + return this.add(StateSelector.builder().add(this.p1, v1).add(this.p2, v2), value); + } + + public Builder generate(final BiFunction generator) { + Objects.requireNonNull(generator, "generator"); + this.p1.possibleValues().forEach(v1 -> + this.p2.possibleValues().forEach(v2 -> + this.add(v1, v2, generator.apply(v1, v2)))); + return this; + } + + @Override + public P2 build() { + this.validate(); + return new P2<>(this.values, this.properties, this.p1, this.p2); + } + } + } + + public static final class P3< + V, + T1 extends Comparable, + T2 extends Comparable, + T3 extends Comparable> + extends StateDispatch { + + private final StateProperty p1; + private final StateProperty p2; + private final StateProperty p3; + + private P3( + final Map values, + final Set> properties, + final StateProperty p1, + final StateProperty p2, + final StateProperty p3 + ) { + super(values, properties); + this.p1 = p1; + this.p2 = p2; + this.p3 = p3; + } + + @Override + public Builder toBuilder() { + return StateDispatch.of(this.p1, this.p2, this.p3).from(this); + } + + public static final class Builder< + V, + T1 extends Comparable, + T2 extends Comparable, + T3 extends Comparable> + extends StateDispatch.Builder> { + + private final StateProperty p1; + private final StateProperty p2; + private final StateProperty p3; + + private Builder( + final StateProperty p1, + final StateProperty p2, + final StateProperty p3 + ) { + this.p1 = Objects.requireNonNull(p1, "property1"); + this.p2 = Objects.requireNonNull(p2, "property2"); + this.p3 = Objects.requireNonNull(p3, "property3"); + } + + public Builder add(final T1 v1, final T2 v2, final T3 v3, final V value) { + return this.add(StateSelector.builder().add(this.p1, v1).add(this.p2, v2).add(this.p3, v3), value); + } + + public Builder generate(final TriFunction generator) { + Objects.requireNonNull(generator, "generator"); + this.p1.possibleValues().forEach(v1 -> + this.p2.possibleValues().forEach(v2 -> + this.p3.possibleValues().forEach(v3 -> + this.add(v1, v2, v3, generator.apply(v1, v2, v3))))); + return this; + } + + @Override + public P3 build() { + this.validate(); + return new P3<>(this.values, this.properties, this.p1, this.p2, this.p3); + } + } + } + + public static final class P4< + V, + T1 extends Comparable, + T2 extends Comparable, + T3 extends Comparable, + T4 extends Comparable> + extends StateDispatch { + + private final StateProperty p1; + private final StateProperty p2; + private final StateProperty p3; + private final StateProperty p4; + + private P4( + final Map values, + final Set> properties, + final StateProperty p1, + final StateProperty p2, + final StateProperty p3, + final StateProperty p4 + ) { + super(values, properties); + this.p1 = p1; + this.p2 = p2; + this.p3 = p3; + this.p4 = p4; + } + + @Override + public Builder toBuilder() { + return StateDispatch.of(this.p1, this.p2, this.p3, this.p4).from(this); + } + + public static final class Builder< + V, + T1 extends Comparable, + T2 extends Comparable, + T3 extends Comparable, + T4 extends Comparable> + extends StateDispatch.Builder> { + + private final StateProperty p1; + private final StateProperty p2; + private final StateProperty p3; + private final StateProperty p4; + + private Builder( + final StateProperty p1, + final StateProperty p2, + final StateProperty p3, + final StateProperty p4 + ) { + this.p1 = Objects.requireNonNull(p1, "property1"); + this.p2 = Objects.requireNonNull(p2, "property2"); + this.p3 = Objects.requireNonNull(p3, "property3"); + this.p4 = Objects.requireNonNull(p4, "property4"); + } + + public Builder add(final T1 v1, final T2 v2, final T3 v3, final T4 v4, final V value) { + return this.add(StateSelector.builder().add(this.p1, v1).add(this.p2, v2).add(this.p3, v3).add(this.p4, v4), value); + } + + public Builder generate(final QuadFunction generator) { + Objects.requireNonNull(generator, "generator"); + this.p1.possibleValues().forEach(v1 -> + this.p2.possibleValues().forEach(v2 -> + this.p3.possibleValues().forEach(v3 -> + this.p4.possibleValues().forEach(v4 -> + this.add(v1, v2, v3, v4, generator.apply(v1, v2, v3, v4)))))); + return this; + } + + @Override + public P4 build() { + this.validate(); + return new P4<>(this.values, this.properties, this.p1, this.p2, this.p3, this.p4); + } + } + } + + public static final class P5< + V, + T1 extends Comparable, + T2 extends Comparable, + T3 extends Comparable, + T4 extends Comparable, + T5 extends Comparable> + extends StateDispatch { + + private final StateProperty p1; + private final StateProperty p2; + private final StateProperty p3; + private final StateProperty p4; + private final StateProperty p5; + + private P5( + final Map values, + final Set> properties, + final StateProperty p1, + final StateProperty p2, + final StateProperty p3, + final StateProperty p4, + final StateProperty p5 + ) { + super(values, properties); + this.p1 = p1; + this.p2 = p2; + this.p3 = p3; + this.p4 = p4; + this.p5 = p5; + } + + @Override + public Builder toBuilder() { + return StateDispatch.of(this.p1, this.p2, this.p3, this.p4, this.p5).from(this); + } + + public static final class Builder< + V, + T1 extends Comparable, + T2 extends Comparable, + T3 extends Comparable, + T4 extends Comparable, + T5 extends Comparable> + extends StateDispatch.Builder> { + + private final StateProperty p1; + private final StateProperty p2; + private final StateProperty p3; + private final StateProperty p4; + private final StateProperty p5; + + private Builder( + final StateProperty p1, + final StateProperty p2, + final StateProperty p3, + final StateProperty p4, + final StateProperty p5 + ) { + this.p1 = Objects.requireNonNull(p1, "property1"); + this.p2 = Objects.requireNonNull(p2, "property2"); + this.p3 = Objects.requireNonNull(p3, "property3"); + this.p4 = Objects.requireNonNull(p4, "property4"); + this.p5 = Objects.requireNonNull(p5, "property5"); + } + + public Builder add(final T1 v1, final T2 v2, final T3 v3, final T4 v4, final T5 v5, final V value) { + return this.add(StateSelector.builder().add(this.p1, v1).add(this.p2, v2).add(this.p3, v3).add(this.p4, v4).add(this.p5, v5), value); + } + + public Builder generate(final QuinFunction generator) { + Objects.requireNonNull(generator, "generator"); + this.p1.possibleValues().forEach(v1 -> + this.p2.possibleValues().forEach(v2 -> + this.p3.possibleValues().forEach(v3 -> + this.p4.possibleValues().forEach(v4 -> + this.p5.possibleValues().forEach(v5 -> + this.add(v1, v2, v3, v4, v5, generator.apply(v1, v2, v3, v4, v5))))))); + return this; + } + + @Override + public P5 build() { + this.validate(); + return new P5<>(this.values, this.properties, this.p1, this.p2, this.p3, this.p4, this.p5); + } + } + } + + public static class Builder> implements + org.spongepowered.api.util.Builder, B>, + CopyableBuilder, B> { + + protected final Map values = new HashMap<>(); + protected final Set> properties = new HashSet<>(); + private final Set propertyNames = new HashSet<>(); + + protected Builder() { + this.reset(); + } + + @SuppressWarnings("unchecked") + private B cast() { + return (B) this; + } + + public B addAll(final Map values) { + Objects.requireNonNull(values, "values").forEach(this::add); + return this.cast(); + } + + public B add(final State state, final V value) { + return this.add(StateSelector.of(state), value); + } + + public B add(final StateSelector.Builder selector, final V value) { + return this.add(selector.build(), value); + } + + public B add(final StateSelector selector, final V value) { + this.validateInput(selector, value); + if (this.values.containsKey(selector)) { + this.values.put(selector, value); + return this.cast(); + } + + for (final StateProperty property : selector.properties()) { + final String propertyName = property.name(); + if (this.propertyNames.contains(propertyName) && !this.properties.contains(property)) { + throw new IllegalArgumentException(String.format( + "Given selector has property with name '%s', " + + "but this dispatch already contains another property with the same name", + propertyName + )); + } + } + + for (final StateSelector current : this.values.keySet()) { + if (current.overlaps(selector)) { + throw this.overlap(selector, current); + } + } + + this.putEntry(selector, value); + return this.cast(); + } + + public B expand(final State state, final V value) { + return this.expand(StateSelector.of(state), value); + } + + public B expand(final StateSelector.Builder selector, final V value) { + return this.expand(selector.build(), value); + } + + public B expand(final StateSelector selector, final V value) { + this.validateInput(selector, value); + if (this.values.containsKey(selector)) { + this.values.put(selector, value); + return this.cast(); + } + + final var values = this.values.entrySet().iterator(); + final Map valuesToAdd = new HashMap<>(); + while (values.hasNext()) { + final var entry = values.next(); + final StateSelector oldSelector = entry.getKey(); + final V oldValue = entry.getValue(); + + if (oldSelector.test(selector)) { + values.remove(); + final Stream> newProperties = selector.properties().stream() + .filter(property -> !oldSelector.properties().contains(property)); + + StateSelector.populate(newProperties::iterator) + .map(oldSelector::with) + .forEach(newSelector -> valuesToAdd.put(newSelector, oldValue)); + + valuesToAdd.put(selector, value); + + } else if (oldSelector.overlaps(selector)) { + throw this.overlap(selector, oldSelector); + } + } + + valuesToAdd.forEach(this::putEntry); + return this.cast(); + } + + @Override + public B reset() { + this.values.clear(); + this.properties.clear(); + this.propertyNames.clear(); + return this.cast(); + } + + @Override + public B from(final StateDispatch dispatch) { + return this.reset().addAll(Objects.requireNonNull(dispatch, "dispatch").values()); + } + + @Override + public StateDispatch build() { + this.validate(); + return new StateDispatch(this.values, this.properties); + } + + private void validateInput(final StateSelector selector, final V value) { + Objects.requireNonNull(selector, "selector"); + } + + private RuntimeException overlap(final StateSelector given, final StateSelector current) { + return new IllegalArgumentException(String.format( + "Given selector (%s) overlaps with the selector in this dispatch (%s)", + given.serializationString(), current.serializationString() + )); + } + + private void putEntry(final StateSelector selector, final V value) { + this.values.put(selector, value); + this.properties.addAll(selector.properties()); + selector.properties().forEach(property -> this.propertyNames.add(property.name())); + } + + protected void validate() { + StateDispatch.validate(this.values, this.properties).ifPresent(msg -> { + throw new IllegalStateException(msg); + }); + } + } +} diff --git a/api/src/main/java/net/hellheim/spongetools/resourcepack/block/StateOps.java b/api/src/main/java/net/hellheim/spongetools/resourcepack/block/StateOps.java new file mode 100644 index 0000000..9241ccb --- /dev/null +++ b/api/src/main/java/net/hellheim/spongetools/resourcepack/block/StateOps.java @@ -0,0 +1,45 @@ +package net.hellheim.spongetools.resourcepack.block; + +import java.util.Objects; +import java.util.function.Supplier; + +import org.spongepowered.api.state.State; +import org.spongepowered.api.state.StateContainer; + +import com.mojang.serialization.DynamicOps; + +import net.hellheim.spongetools.proxy.solid.block.StateContainerProxy; +import net.hellheim.spongetools.proxy.solid.codec.DynamicOpsProxy; + +public final class StateOps> implements DynamicOpsProxy, StateContainerProxy { + + private final DynamicOps ops; + private final StateContainer container; + + private StateOps(final DynamicOps ops, final StateContainer container) { + this.ops = Objects.requireNonNull(ops, "ops"); + this.container = Objects.requireNonNull(container, "contaier"); + } + + public static > StateOps of( + final DynamicOps ops, final StateContainer container + ) { + return new StateOps<>(ops, container); + } + + public static > StateOps of( + final DynamicOps ops, final Supplier> containerSupplier + ) { + return new StateOps<>(ops, Objects.requireNonNull(containerSupplier, "containerSupplier").get()); + } + + @Override + public DynamicOps getAsOps() { + return this.ops; + } + + @Override + public StateContainer getAsStateContainer() { + return this.container; + } +} diff --git a/api/src/main/java/net/hellheim/spongetools/resourcepack/block/StatePart.java b/api/src/main/java/net/hellheim/spongetools/resourcepack/block/StatePart.java new file mode 100644 index 0000000..b2ddc40 --- /dev/null +++ b/api/src/main/java/net/hellheim/spongetools/resourcepack/block/StatePart.java @@ -0,0 +1,44 @@ +package net.hellheim.spongetools.resourcepack.block; + +import java.util.List; +import java.util.Objects; +import java.util.Optional; +import java.util.Set; + +import org.spongepowered.api.state.StateProperty; + +import com.mojang.serialization.Codec; +import com.mojang.serialization.codecs.RecordCodecBuilder; + +public record StatePart(Optional condition, VariantList variants) { + + public static final Codec CODEC = RecordCodecBuilder.create( + instance -> instance.group( + StateCondition.CODEC.optionalFieldOf("when").forGetter(StatePart::condition), + VariantList.CODEC.fieldOf("apply").forGetter(StatePart::variants) + ).apply(instance, StatePart::new)); + + public static final Codec> LIST_CODEC = StatePart.CODEC.listOf(); + + public StatePart(final Optional condition, final VariantList variants) { + this.condition = Objects.requireNonNull(condition, "condition") + .filter(c -> c != StateCondition.alwaysTrue()); + this.variants = variants; + } + + public static StatePart of(final Optional condition, final VariantListLike variants) { + return new StatePart(condition, variants.asVariantList()); + } + + public static StatePart of(final StateCondition condition, final VariantListLike variants) { + return StatePart.of(Optional.of(condition), variants); + } + + public static StatePart of(final VariantListLike variants) { + return StatePart.of(Optional.empty(), variants); + } + + public Set> properties() { + return this.condition.map(StateCondition::properties).orElse(Set.of()); + } +} diff --git a/api/src/main/java/net/hellheim/spongetools/resourcepack/block/StatePredicate.java b/api/src/main/java/net/hellheim/spongetools/resourcepack/block/StatePredicate.java new file mode 100644 index 0000000..d767dc2 --- /dev/null +++ b/api/src/main/java/net/hellheim/spongetools/resourcepack/block/StatePredicate.java @@ -0,0 +1,58 @@ +package net.hellheim.spongetools.resourcepack.block; + +import java.util.Objects; +import java.util.Optional; +import java.util.function.Function; + +import org.spongepowered.api.state.State; +import org.spongepowered.api.state.StateProperty; + +@FunctionalInterface +public interface StatePredicate { + + default boolean matchAnyState(final Iterable> states) { + for (final State state : Objects.requireNonNull(states, "states")) { + if (this.test(state)) { + return true; + } + } + return false; + } + + default boolean matchAnyState(final State... states) { + for (final State state : Objects.requireNonNull(states, "states")) { + if (this.test(state)) { + return true; + } + } + return false; + } + + default boolean matchAnySelector(final Iterable selectors) { + for (final StateSelector selector : Objects.requireNonNull(selectors, "selectors")) { + if (this.test(selector)) { + return true; + } + } + return false; + } + + default boolean matchAnySelector(final StateSelector... selectors) { + for (final StateSelector selector : Objects.requireNonNull(selectors, "selectors")) { + if (this.test(selector)) { + return true; + } + } + return false; + } + + default boolean test(final State state) { + return this.test(Objects.requireNonNull(state, "state")::stateProperty); + } + + default boolean test(final StateSelector selector) { + return this.test(Objects.requireNonNull(selector, "selector")::get); + } + + boolean test(Function, Optional>> propertyLookup); +} diff --git a/api/src/main/java/net/hellheim/spongetools/resourcepack/block/StatePropertyValue.java b/api/src/main/java/net/hellheim/spongetools/resourcepack/block/StatePropertyValue.java new file mode 100644 index 0000000..6019c3e --- /dev/null +++ b/api/src/main/java/net/hellheim/spongetools/resourcepack/block/StatePropertyValue.java @@ -0,0 +1,52 @@ +package net.hellheim.spongetools.resourcepack.block; + +import java.util.Objects; +import java.util.function.Supplier; +import java.util.stream.Stream; + +import org.spongepowered.api.Sponge; +import org.spongepowered.api.data.type.StringRepresentable; +import org.spongepowered.api.state.State; +import org.spongepowered.api.state.StateProperty; + +import com.mojang.serialization.Codec; + +public interface StatePropertyValue> extends StringRepresentable { + + Codec> CODEC = StateCodec.STATE_PROPERTY_VALUE; + + static > StatePropertyValue of( + final StateProperty property, final T value + ) { + return Sponge.game().factoryProvider().provide(Factory.class).of(property, value); + } + + static > StatePropertyValue of( + final StateProperty property, final Supplier valueSupplier + ) { + return StatePropertyValue.of(property, Objects.requireNonNull(valueSupplier, "valueSupplier").get()); + } + + @SuppressWarnings("unchecked") + static > StatePropertyValue ofRaw( + final StateProperty property, final Object value + ) { + return StatePropertyValue.of(property, (T) value); + } + + static Stream> allOf(final State state) { + return state.statePropertyMap().entrySet().stream() + .map(e -> StatePropertyValue.ofRaw(e.getKey(), e.getValue())); + } + + StateProperty property(); + + T propertyValue(); + + String valueName(); + + interface Factory { + + > StatePropertyValue of(StateProperty property, T value); + } +} diff --git a/api/src/main/java/net/hellheim/spongetools/resourcepack/block/StateSelector.java b/api/src/main/java/net/hellheim/spongetools/resourcepack/block/StateSelector.java new file mode 100644 index 0000000..1b608b7 --- /dev/null +++ b/api/src/main/java/net/hellheim/spongetools/resourcepack/block/StateSelector.java @@ -0,0 +1,228 @@ +package net.hellheim.spongetools.resourcepack.block; + +import java.util.Collection; +import java.util.Comparator; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.Set; +import java.util.function.Function; +import java.util.function.Supplier; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import org.checkerframework.checker.nullness.qual.Nullable; +import org.spongepowered.api.data.type.StringRepresentable; +import org.spongepowered.api.state.State; +import org.spongepowered.api.state.StateProperty; +import org.spongepowered.api.util.CopyableBuilder; + +import com.google.common.collect.Sets; +import com.mojang.serialization.Codec; + +public final class StateSelector implements StringRepresentable, StatePredicate { + + public static final Codec CODEC = StateCodec.STATE_SELECTOR; + + private static final StateSelector EMPTY = new StateSelector(Map.of()); + + private static final Comparator> COMPARE_BY_NAME = + Comparator.comparing(value -> value.property().name()); + + private final Map, StatePropertyValue> values; + + private StateSelector(final Map, StatePropertyValue> values) { + this.values = Map.copyOf(values); + } + + public static Builder builder() { + return new Builder(); + } + + public static StateSelector empty() { + return StateSelector.EMPTY; + } + + public static StateSelector of(final State state) { + final Builder builder = StateSelector.builder(); + Objects.requireNonNull(state, "state").statePropertyMap().forEach((property, value) -> + builder.add(StatePropertyValue.ofRaw(property, value))); + return builder.build(); + } + + public static Stream populate(final StateProperty... properties) { + return StateSelector.populate(List.of(properties)); + } + + public static Stream populate(final Iterable> properties) { + Objects.requireNonNull(properties, "properties"); + Stream selectors = Stream.of(StateSelector.empty()); + for (final StateProperty property : properties) { + selectors = selectors.flatMap(selector -> + property.possibleValues().stream().map(value -> + selector.with(StatePropertyValue.ofRaw(property, value)))); + } + return selectors; + } + + public Set> properties() { + return this.values.keySet(); + } + + public Collection> values() { + return this.values.values(); + } + + public > Optional get(final StateProperty property) { + @SuppressWarnings("unchecked") + final @Nullable StatePropertyValue value = (StatePropertyValue) this.values.get(property); + return value == null ? Optional.empty() : Optional.of(value.propertyValue()); + } + + public boolean overlaps(final StateSelector selector) { + final var commonProperties = Sets.intersection(this.properties(), selector.properties()); + for (final StateProperty property : commonProperties) { + final var thisValue = this.values.get(property).propertyValue(); + final var thatValue = selector.values.get(property).propertyValue(); + if (!Objects.equals(thisValue, thatValue)) { + return false; + } + } + + return true; + } + + @Override + public boolean test(final Function, Optional>> propertyLookup) { + for (final StatePropertyValue value : this.values()) { + if (propertyLookup.apply(value.property()).map(v -> !value.propertyValue().equals(v)).orElse(true)) { + return false; + } + } + + return true; + } + + public Builder toBuilder() { + return StateSelector.builder().from(this); + } + + public > StateSelector with( + final StateProperty property, final T value + ) { + return this.toBuilder().add(property, value).build(); + } + + public > StateSelector with( + final StateProperty property, final Supplier valueSupplier + ) { + return this.toBuilder().add(property, valueSupplier).build(); + } + + public StateSelector with(final StatePropertyValue value) { + return this.toBuilder().add(value).build(); + } + + public StateSelector with(final StateSelector selector) { + return this.toBuilder().addAll(selector).build(); + } + + @Override + public String serializationString() { + return this.values().stream() + .sorted(StateSelector.COMPARE_BY_NAME) + .map(StatePropertyValue::serializationString) + .collect(Collectors.joining(",")); + } + + @Override + public int hashCode() { + return this.values.hashCode(); + } + + @Override + public boolean equals(final Object obj) { + if (this == obj) { + return true; + } else if (this.getClass() != obj.getClass()) { + return false; + } else { + return this.values.equals(((StateSelector) obj).values); + } + } + + @Override + public String toString() { + return "StateSelector[" + this.serializationString() + "]"; + } + + public static class Builder implements + org.spongepowered.api.util.Builder, + CopyableBuilder { + + private final Map, StatePropertyValue> values = new HashMap<>(); + private final Set propertyNames = new HashSet<>(); + + public > Builder add(final StateProperty property, final T value) { + return this.add(StatePropertyValue.of(property, value)); + } + + public > Builder add(final StateProperty property, final Supplier valueSupplier) { + return this.add(StatePropertyValue.of(property, valueSupplier)); + } + + public Builder add(final StatePropertyValue value) { + final StateProperty property = value.property(); + final String propertyName = property.name(); + if (this.propertyNames.contains(propertyName) && !this.values.containsKey(property)) { + throw new IllegalArgumentException(String.format( + "Given selector has property with name '%s', " + + "but this selector already contains another property with the same name", + propertyName + )); + } + + this.values.put(property, value); + this.propertyNames.add(propertyName); + return this; + } + + public Builder addAll(final StatePropertyValue... values) { + for (final StatePropertyValue value : Objects.requireNonNull(values, "values")) { + this.add(value); + } + return this; + } + + public Builder addAll(final Iterable> values) { + for (final StatePropertyValue value : Objects.requireNonNull(values, "values")) { + this.add(value); + } + return this; + } + + public Builder addAll(final StateSelector selector) { + return this.addAll(Objects.requireNonNull(selector, "selector").values()); + } + + @Override + public Builder from(final StateSelector selector) { + return this.reset().addAll(selector); + } + + @Override + public Builder reset() { + this.values.clear(); + this.propertyNames.clear(); + return this; + } + + @Override + public StateSelector build() { + return this.values.isEmpty() ? StateSelector.empty() : new StateSelector(this.values); + } + } +} diff --git a/api/src/main/java/net/hellheim/spongetools/resourcepack/block/Variant.java b/api/src/main/java/net/hellheim/spongetools/resourcepack/block/Variant.java new file mode 100644 index 0000000..a53819d --- /dev/null +++ b/api/src/main/java/net/hellheim/spongetools/resourcepack/block/Variant.java @@ -0,0 +1,171 @@ +package net.hellheim.spongetools.resourcepack.block; + +import java.util.Collection; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Set; +import java.util.function.Supplier; +import java.util.stream.Collectors; + +import org.spongepowered.api.ResourceKey; +import org.spongepowered.api.util.CopyableBuilder; + +import com.google.common.collect.Iterators; +import com.mojang.serialization.Codec; + +import net.hellheim.spongetools.util.ModelUtil; + +public final class Variant implements VariantListLike { + + public static final Codec CODEC = VariantPropertyValue.LIST_CODEC.xmap( + list -> Variant.builder().addAll(list).build(), + variant -> List.copyOf(variant.values()) + ); + + private static final Variant EMPTY = new Variant(Map.of()); + + private final Map, VariantPropertyValue> values; + + private Variant(final Map, VariantPropertyValue> values) { + this.values = Map.copyOf(values); + } + + public static Builder builder() { + return new Builder(); + } + + public static Variant empty() { + return Variant.EMPTY; + } + + public static Variant model(final ResourceKey model) { + return Variant.builder().add(VariantProperties.MODEL, model).build(); + } + + public static Variant prefixedModel(final ResourceKey model) { + return Variant.model(ModelUtil.withBlockPrefix(model)); + } + + public Set> properties() { + return this.values.keySet(); + } + + public Collection> values() { + return this.values.values(); + } + + public Builder toBuilder() { + return Variant.builder().from(this); + } + + public Variant with(final VariantProperty property, final T value) { + return this.toBuilder().add(property, value).build(); + } + + public Variant with(final VariantProperty property, final Supplier valueSupplier) { + return this.toBuilder().add(property, valueSupplier).build(); + } + + public Variant with(final VariantPropertyValue value) { + return this.toBuilder().add(value).build(); + } + + public Variant with(final Variant variant) { + return this.toBuilder().addAll(variant).build(); + } + + @Override + public VariantList asVariantList() { + return VariantList.of(this); + } + + @Override + public Iterator iterator() { + return Iterators.singletonIterator(this); + } + + @Override + public int hashCode() { + return this.values.hashCode(); + } + + @Override + public boolean equals(final Object obj) { + if (this == obj) { + return true; + } else if (obj.getClass() != this.getClass()) { + return false; + } else { + return this.values.equals(((Variant) obj).values); + } + } + + @Override + public String toString() { + return this.values.values().stream() + .map(VariantPropertyValue::toString) + .collect(Collectors.joining(",", "Variant[", "]")); + } + + public static class Builder implements + org.spongepowered.api.util.Builder, + CopyableBuilder { + + private final Map, VariantPropertyValue> values = new HashMap<>(); + + public Builder() { + this.reset(); + } + + public Builder add(final VariantProperty property, final T value) { + return this.add(VariantPropertyValue.of(property, value)); + } + + public Builder add(final VariantProperty property, final Supplier valueSupplier) { + return this.add(VariantPropertyValue.of(property, valueSupplier)); + } + + public Builder add(final VariantPropertyValue value) { + Objects.requireNonNull(value, "value"); + this.values.put(value.property(), value); + return this; + } + + public Builder addAll(final VariantPropertyValue... values) { + for (final VariantPropertyValue value : Objects.requireNonNull(values, "values")) { + this.add(value); + } + return this; + } + + public Builder addAll(final Iterable> values) { + for (final VariantPropertyValue value : Objects.requireNonNull(values, "values")) { + this.add(value); + } + return this; + } + + public Builder addAll(final Variant variant) { + return this.addAll(Objects.requireNonNull(variant, "variant").values()); + } + + @Override + public Builder from(final Variant variant) { + return this.reset().addAll(variant); + } + + @Override + public Builder reset() { + this.values.clear(); + return this; + } + + @Override + public Variant build() { + return new Variant(this.values); + } + } +} diff --git a/api/src/main/java/net/hellheim/spongetools/resourcepack/block/VariantList.java b/api/src/main/java/net/hellheim/spongetools/resourcepack/block/VariantList.java new file mode 100644 index 0000000..bcc8d8d --- /dev/null +++ b/api/src/main/java/net/hellheim/spongetools/resourcepack/block/VariantList.java @@ -0,0 +1,56 @@ +package net.hellheim.spongetools.resourcepack.block; + +import java.util.Arrays; +import java.util.Iterator; +import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import com.google.common.collect.Streams; +import com.mojang.serialization.Codec; +import com.mojang.serialization.DataResult; + +import net.hellheim.spongetools.codec.list.ExtraCodecs; + +public record VariantList(Set variants) implements VariantListLike { + + private static final Codec SINGLE_CODEC = Variant.CODEC + .xmap(v -> new VariantList(Set.of(v)), list -> list.variants().iterator().next()); + private static final Codec LIST_CODEC = ExtraCodecs.setOf(Variant.CODEC) + .xmap(VariantList::new, VariantList::variants) + .validate(VariantList::validate); + public static final Codec CODEC = Codec.withAlternative(SINGLE_CODEC, LIST_CODEC); + + public VariantList(final Set variants) { + this.variants = Set.copyOf(variants); + } + + public static VariantList of(final Stream variants) { + return VariantList.validate(new VariantList(variants.collect(Collectors.toSet()))) + .getOrThrow(IllegalArgumentException::new); + } + + public static VariantList of(final Iterable variants) { + return VariantList.of(Streams.stream(variants)); + } + + public static VariantList of(final Variant... variants) { + return VariantList.of(Arrays.stream(variants)); + } + + private static DataResult validate(final VariantList variants) { + return variants.variants().isEmpty() + ? DataResult.error(() -> "At least one variant must be provided") + : DataResult.success(variants); + } + + @Override + public VariantList asVariantList() { + return this; + } + + @Override + public Iterator iterator() { + return this.variants.iterator(); + } +} diff --git a/api/src/main/java/net/hellheim/spongetools/resourcepack/block/VariantListLike.java b/api/src/main/java/net/hellheim/spongetools/resourcepack/block/VariantListLike.java new file mode 100644 index 0000000..f055138 --- /dev/null +++ b/api/src/main/java/net/hellheim/spongetools/resourcepack/block/VariantListLike.java @@ -0,0 +1,25 @@ +package net.hellheim.spongetools.resourcepack.block; + +import java.util.ArrayList; +import java.util.List; + +/** + * Something that can be represented as a {@link VariantList}. + */ +public interface VariantListLike extends Iterable { + + /** + * Gets the {@link VariantList} representation. + * + * @return The variant list + */ + VariantList asVariantList(); + + default VariantList merge(final VariantListLike variants) { + final List result = new ArrayList<>(); + this.forEach(first -> + variants.forEach(second -> + result.add(first.with(second)))); + return VariantList.of(result); + } +} diff --git a/api/src/main/java/net/hellheim/spongetools/resourcepack/block/VariantProperties.java b/api/src/main/java/net/hellheim/spongetools/resourcepack/block/VariantProperties.java new file mode 100644 index 0000000..e58a196 --- /dev/null +++ b/api/src/main/java/net/hellheim/spongetools/resourcepack/block/VariantProperties.java @@ -0,0 +1,33 @@ +package net.hellheim.spongetools.resourcepack.block; + +import java.util.Optional; + +import org.spongepowered.api.ResourceKey; +import org.spongepowered.api.util.rotation.Rotation; + +import com.mojang.serialization.Codec; + +import net.hellheim.spongetools.codec.list.SpongeCodecs; +import net.hellheim.spongetools.util.GeomUtil; + +public final class VariantProperties { + + public static final VariantProperty MODEL = property("model", SpongeCodecs.RESOURCE_KEY, Optional.empty()); + + public static final VariantProperty X_ROT = property("x", SpongeCodecs.ROTATION_BY_ANGLE, Optional.of(GeomUtil.ROT_0.get())); + + public static final VariantProperty Y_ROT = property("y", SpongeCodecs.ROTATION_BY_ANGLE, Optional.of(GeomUtil.ROT_0.get())); + + public static final VariantProperty UV_LOCK = property("uvlock", Codec.BOOL, Optional.of(false)); + + public static final VariantProperty WEIGHT = property("weight", Codec.INT, Optional.of(1)); + + private static VariantProperty property( + final String name, final Codec valueCodec, final Optional defaultValue + ) { + return new VariantProperty<>(name, valueCodec, defaultValue); + } + + private VariantProperties() { + } +} diff --git a/api/src/main/java/net/hellheim/spongetools/resourcepack/block/VariantProperty.java b/api/src/main/java/net/hellheim/spongetools/resourcepack/block/VariantProperty.java new file mode 100644 index 0000000..c4ca9f3 --- /dev/null +++ b/api/src/main/java/net/hellheim/spongetools/resourcepack/block/VariantProperty.java @@ -0,0 +1,41 @@ +package net.hellheim.spongetools.resourcepack.block; + +import java.util.Objects; +import java.util.Optional; +import java.util.function.Supplier; + +import org.spongepowered.api.data.type.StringRepresentable; +import org.spongepowered.api.util.annotation.CatalogedBy; + +import com.mojang.serialization.Codec; + +import net.hellheim.spongetools.codec.LateBoundIdMapper; + +@CatalogedBy(VariantProperties.class) +public record VariantProperty(String name, Codec valueCodec, Optional defaultValue) + implements StringRepresentable { + + private static final LateBoundIdMapper> ID_MAPPER = new LateBoundIdMapper<>(); + + public static final Codec> CODEC = VariantProperty.ID_MAPPER.codec(Codec.STRING); + + public VariantProperty(final String name, final Codec valueCodec, final Optional defaultValue) { + this.name = Objects.requireNonNull(name, "name").toLowerCase(); + this.valueCodec = Objects.requireNonNull(valueCodec, "valueCodec"); + this.defaultValue = Objects.requireNonNull(defaultValue, "defaultValue"); + VariantProperty.ID_MAPPER.put(this.name, this); + } + + public VariantPropertyValue with(final T value) { + return VariantPropertyValue.of(this, value); + } + + public VariantPropertyValue with(final Supplier valueSupplier) { + return VariantPropertyValue.of(this, valueSupplier); + } + + @Override + public String serializationString() { + return this.name; + } +} diff --git a/api/src/main/java/net/hellheim/spongetools/resourcepack/block/VariantPropertyValue.java b/api/src/main/java/net/hellheim/spongetools/resourcepack/block/VariantPropertyValue.java new file mode 100644 index 0000000..5d4986a --- /dev/null +++ b/api/src/main/java/net/hellheim/spongetools/resourcepack/block/VariantPropertyValue.java @@ -0,0 +1,52 @@ +package net.hellheim.spongetools.resourcepack.block; + +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.function.Supplier; +import java.util.stream.Collectors; + +import com.mojang.serialization.Codec; + +public record VariantPropertyValue(VariantProperty property, T value) { + + private static final Codec, Object>> MAP_CODEC = Codec.dispatchedMap(VariantProperty.CODEC, VariantProperty::valueCodec); + + public static final Codec>> LIST_CODEC = VariantPropertyValue.MAP_CODEC.xmap( + map -> map.entrySet().stream() + .map(e -> { + @SuppressWarnings("unchecked") + final var property = (VariantProperty) e.getKey(); + return VariantPropertyValue.of(property, e.getValue()); + }) + .collect(Collectors.toUnmodifiableList()), + list -> list.stream() + .collect(Collectors.toUnmodifiableMap(VariantPropertyValue::property, VariantPropertyValue::value)) + ); + + public VariantPropertyValue(final VariantProperty property, final T value) { + this.property = Objects.requireNonNull(property, "property"); + this.value = Objects.requireNonNull(value, "value"); + } + + public static VariantPropertyValue of( + final VariantProperty property, final T value + ) { + return new VariantPropertyValue<>(property, value); + } + + public static VariantPropertyValue of( + final VariantProperty property, final Supplier valueSupplier + ) { + return VariantPropertyValue.of(property, Objects.requireNonNull(valueSupplier, "valueSupplier").get()); + } + + public boolean isDefault() { + return this.property.defaultValue().map(this.value::equals).orElse(false); + } + + @Override + public final String toString() { + return this.property.name() + "=" + this.value; + } +} diff --git a/api/src/main/java/net/hellheim/spongetools/resourcepack/item/ConditionalProperty.java b/api/src/main/java/net/hellheim/spongetools/resourcepack/item/ConditionalProperty.java new file mode 100644 index 0000000..3abfd0d --- /dev/null +++ b/api/src/main/java/net/hellheim/spongetools/resourcepack/item/ConditionalProperty.java @@ -0,0 +1,184 @@ +package net.hellheim.spongetools.resourcepack.item; + +import java.util.function.Function; + +import org.spongepowered.api.ResourceKey; + +import com.google.common.base.Preconditions; +import com.mojang.serialization.MapCodec; +import com.mojang.serialization.codecs.RecordCodecBuilder; + +import net.hellheim.spongetools.codec.LateBoundIdMapper; +import net.hellheim.spongetools.codec.list.ExtraCodecs; +import net.hellheim.spongetools.codec.list.SpongeCodecs; +import net.hellheim.spongetools.proxy.solid.codec.MapCodecProxy; + +public interface ConditionalProperty extends MapCodecProxy { + + LateBoundIdMapper> ID_MAPPER = new LateBoundIdMapper<>(); + + MapCodec CODEC = ConditionalProperty.ID_MAPPER.codec(SpongeCodecs.RESOURCE_KEY) + .dispatchMap("property", ConditionalProperty::mapCodec, Function.identity()); + + static Broken broken() { + return Broken.INSTANCE; + } + + static Damaged damaged() { + return Damaged.INSTANCE; + } + + static Carried carried() { + return Carried.INSTANCE; + } + + static Selected selected() { + return Selected.INSTANCE; + } + + static ExtendedView extendedView() { + return ExtendedView.INSTANCE; + } + + static FishingRodCast rodCast() { + return FishingRodCast.INSTANCE; + } + + static BundleHasSelectedItem bundleHasSelectedItem() { + return BundleHasSelectedItem.INSTANCE; + } + + static Using using() { + return Using.INSTANCE; + } + + static ViewingEntity viewingEntity() { + return ViewingEntity.INSTANCE; + } + + static CustomModelData custom() { + return ConditionalProperty.custom(CustomModelData.DEFAULT_INDEX); + } + + static CustomModelData custom(final int index) { + return new CustomModelData(index); + } + + record Broken() implements ConditionalProperty { + public static final Broken INSTANCE = new Broken(); + public static final MapCodec CODEC = MapCodec.unit(INSTANCE); + + @Override + public MapCodec mapCodec() { + return CODEC; + } + } + + record Damaged() implements ConditionalProperty { + public static final Damaged INSTANCE = new Damaged(); + public static final MapCodec CODEC = MapCodec.unit(INSTANCE); + + @Override + public MapCodec mapCodec() { + return CODEC; + } + } + + record Carried() implements ConditionalProperty { + public static final Carried INSTANCE = new Carried(); + public static final MapCodec CODEC = MapCodec.unit(INSTANCE); + + @Override + public MapCodec mapCodec() { + return CODEC; + } + } + + record Selected() implements ConditionalProperty { + public static final Selected INSTANCE = new Selected(); + public static final MapCodec CODEC = MapCodec.unit(INSTANCE); + + @Override + public MapCodec mapCodec() { + return CODEC; + } + } + + record ExtendedView() implements ConditionalProperty { + public static final ExtendedView INSTANCE = new ExtendedView(); + public static final MapCodec CODEC = MapCodec.unit(INSTANCE); + + @Override + public MapCodec mapCodec() { + return CODEC; + } + } + + record FishingRodCast() implements ConditionalProperty { + public static final FishingRodCast INSTANCE = new FishingRodCast(); + public static final MapCodec CODEC = MapCodec.unit(INSTANCE); + + @Override + public MapCodec mapCodec() { + return CODEC; + } + } + + record BundleHasSelectedItem() implements ConditionalProperty { + public static final BundleHasSelectedItem INSTANCE = new BundleHasSelectedItem(); + public static final MapCodec CODEC = MapCodec.unit(INSTANCE); + + @Override + public MapCodec mapCodec() { + return CODEC; + } + } + + record Using() implements ConditionalProperty { + public static final Using INSTANCE = new Using(); + public static final MapCodec CODEC = MapCodec.unit(INSTANCE); + + @Override + public MapCodec mapCodec() { + return CODEC; + } + } + + record ViewingEntity() implements ConditionalProperty { + public static final ViewingEntity INSTANCE = new ViewingEntity(); + public static final MapCodec CODEC = MapCodec.unit(INSTANCE); + + @Override + public MapCodec mapCodec() { + return CODEC; + } + } + + record CustomModelData(int index) implements ConditionalProperty { + public static final int DEFAULT_INDEX = 0; + public static final MapCodec CODEC = RecordCodecBuilder.mapCodec( + instance -> instance.group( + ExtraCodecs.NON_NEGATIVE_INT.optionalFieldOf("index", CustomModelData.DEFAULT_INDEX).forGetter(CustomModelData::index) + ).apply(instance, CustomModelData::new)); + + public CustomModelData(final int index) { + Preconditions.checkArgument(index >= 0, "Index must not be negative: " + index); + this.index = index; + } + + @Override + public MapCodec mapCodec() { + return CODEC; + } + } + + // TODO expose DataComponentType + /*record HasComponent() implements ConditionalProperty { + + }*/ + + // TODO + /*record KeybindDown() implements ConditionalProperty { + + }*/ +} diff --git a/api/src/main/java/net/hellheim/spongetools/resourcepack/item/ItemDefinition.java b/api/src/main/java/net/hellheim/spongetools/resourcepack/item/ItemDefinition.java new file mode 100644 index 0000000..23397af --- /dev/null +++ b/api/src/main/java/net/hellheim/spongetools/resourcepack/item/ItemDefinition.java @@ -0,0 +1,51 @@ +package net.hellheim.spongetools.resourcepack.item; + +import java.util.Objects; + +import org.spongepowered.api.registry.DefaultedRegistryType; + +import com.mojang.serialization.Codec; +import com.mojang.serialization.codecs.RecordCodecBuilder; + +import net.hellheim.spongetools.SpongeTools; +import net.hellheim.spongetools.codec.list.RegistryCodecs; + +/** + * @see Minecraft Wiki + */ +public record ItemDefinition(ItemModel model, boolean handAnimationOnSwap) implements ItemDefinitionLike { + + public static final boolean DEFAULT_HAND_ANIMATION_ON_SWAP = true; + + public static final Codec CODEC = RecordCodecBuilder.create( + instance -> instance.group( + ItemModel.CODEC.fieldOf("model").forGetter(ItemDefinition::model), + Codec.BOOL.optionalFieldOf("hand_animation_on_swap", ItemDefinition.DEFAULT_HAND_ANIMATION_ON_SWAP).forGetter(ItemDefinition::handAnimationOnSwap) + ).apply(instance, ItemDefinition::new)); + + public ItemDefinition(final ItemModel model, final boolean handAnimationOnSwap) { + this.model = Objects.requireNonNull(model, "model"); + this.handAnimationOnSwap = handAnimationOnSwap; + } + + public static DefaultedRegistryType registry() { + return SpongeTools.Registries.ITEM_DEFINITION; + } + + public static Codec registryCodec() { + return RegistryCodecs.ITEM_DEFINITION; + } + + public static ItemDefinition of(final ItemModel model, final boolean handAnimationOnSwap) { + return new ItemDefinition(model, handAnimationOnSwap); + } + + public static ItemDefinition of(final ItemModel model) { + return ItemDefinition.of(model, ItemDefinition.DEFAULT_HAND_ANIMATION_ON_SWAP); + } + + @Override + public ItemDefinition asDefinition() { + return this; + } +} diff --git a/api/src/main/java/net/hellheim/spongetools/resourcepack/item/ItemDefinitionLike.java b/api/src/main/java/net/hellheim/spongetools/resourcepack/item/ItemDefinitionLike.java new file mode 100644 index 0000000..76b80cf --- /dev/null +++ b/api/src/main/java/net/hellheim/spongetools/resourcepack/item/ItemDefinitionLike.java @@ -0,0 +1,6 @@ +package net.hellheim.spongetools.resourcepack.item; + +public interface ItemDefinitionLike { + + ItemDefinition asDefinition(); +} diff --git a/api/src/main/java/net/hellheim/spongetools/resourcepack/item/ItemModel.java b/api/src/main/java/net/hellheim/spongetools/resourcepack/item/ItemModel.java new file mode 100644 index 0000000..f9cad30 --- /dev/null +++ b/api/src/main/java/net/hellheim/spongetools/resourcepack/item/ItemModel.java @@ -0,0 +1,261 @@ +package net.hellheim.spongetools.resourcepack.item; + +import java.util.Collection; +import java.util.List; +import java.util.Objects; +import java.util.Optional; +import java.util.function.Function; + +import org.spongepowered.api.ResourceKey; + +import com.mojang.serialization.Codec; +import com.mojang.serialization.MapCodec; +import com.mojang.serialization.codecs.RecordCodecBuilder; + +import net.hellheim.spongetools.codec.LateBoundIdMapper; +import net.hellheim.spongetools.codec.list.SpongeCodecs; +import net.hellheim.spongetools.proxy.solid.codec.MapCodecProxy; + +public interface ItemModel extends ItemDefinitionLike, MapCodecProxy { + + LateBoundIdMapper> ID_MAPPER = new LateBoundIdMapper<>(); + + Codec CODEC = ItemModel.ID_MAPPER.codec(SpongeCodecs.RESOURCE_KEY) + .dispatch(ItemModel::mapCodec, Function.identity()); + + static Empty empty() { + return Empty.INSTANCE; + } + + static BundleSelectedItem bundleSelectedItem() { + return BundleSelectedItem.INSTANCE; + } + + static Simple simple(final ResourceKey model, final TintSource... tints) { + return new Simple(model, List.of(tints)); + } + + static Simple simple(final ResourceKey model, final Collection tints) { + return new Simple(model, List.copyOf(tints)); + } + + static Composite composite(final ItemModel... models) { + return new Composite(List.of(models)); + } + + static Composite composote(final Collection models) { + return new Composite(List.copyOf(models)); + } + + static Conditional conditional(final ConditionalProperty property, final ItemModel onTrue, final ItemModel onFalse) { + return new Conditional(property, onTrue, onFalse); + } + + @SafeVarargs + static Select select(final SelectProperty property, final SelectSwitchCase... cases) { + return ItemModel.select(property, Optional.empty(), cases); + } + + @SafeVarargs + static Select select(final SelectProperty property, final ItemModel fallback, final SelectSwitchCase... cases) { + return ItemModel.select(property, Optional.of(fallback), cases); + } + + @SafeVarargs + static Select select(final SelectProperty property, final Optional fallback, final SelectSwitchCase... cases) { + return ItemModel.select(SelectSwitch.of(property, List.of(cases)), fallback); + } + + static Select select(final SelectProperty property, final Collection> cases) { + return ItemModel.select(property, Optional.empty(), cases); + } + + static Select select(final SelectProperty property, final ItemModel fallback, final Collection> cases) { + return ItemModel.select(property, Optional.of(fallback), cases); + } + + static Select select(final SelectProperty property, final Optional fallback, final Collection> cases) { + return ItemModel.select(SelectSwitch.of(property, List.copyOf(cases)), fallback); + } + + static Select select(final SelectSwitch body) { + return ItemModel.select(body, Optional.empty()); + } + + static Select select(final SelectSwitch body, final ItemModel fallback) { + return ItemModel.select(body, Optional.of(fallback)); + } + + static Select select(final SelectSwitch body, final Optional fallback) { + return new Select(body, fallback); + } + + static RangeSelect rangeSelect(final RangeSelectProperty property, final float scale, final RangeSelectEntry... entries) { + return ItemModel.rangeSelect(property, scale, Optional.empty(), entries); + } + + static RangeSelect rangeSelect(final RangeSelectProperty property, final float scale, final ItemModel fallback, final RangeSelectEntry... entries) { + return ItemModel.rangeSelect(property, scale, Optional.of(fallback), entries); + } + + static RangeSelect rangeSelect(final RangeSelectProperty property, final float scale, final Optional fallback, final RangeSelectEntry... entries) { + return new RangeSelect(property, scale, fallback, List.of(entries)); + } + + static RangeSelect rangeSelect(final RangeSelectProperty property, final float scale, final Collection entries) { + return ItemModel.rangeSelect(property, scale, Optional.empty(), entries); + } + + static RangeSelect rangeSelect(final RangeSelectProperty property, final float scale, final ItemModel fallback, final Collection entries) { + return ItemModel.rangeSelect(property, scale, Optional.of(fallback), entries); + } + + static RangeSelect rangeSelect(final RangeSelectProperty property, final float scale, final Optional fallback, final Collection entries) { + return new RangeSelect(property, scale, fallback, List.copyOf(entries)); + } + + static Special special(final ResourceKey base, final SpecialModel model) { + return new Special(base, model); + } + + default ItemDefinition asDefinition(final boolean handAnimationOnSwap) { + return ItemDefinition.of(this, handAnimationOnSwap); + } + + @Override + default ItemDefinition asDefinition() { + return ItemDefinition.of(this); + } + + record Empty() implements ItemModel { + public static final Empty INSTANCE = new Empty(); + public static final MapCodec CODEC = MapCodec.unit(INSTANCE); + + @Override + public MapCodec mapCodec() { + return CODEC; + } + } + + record BundleSelectedItem() implements ItemModel { + public static final BundleSelectedItem INSTANCE = new BundleSelectedItem(); + public static final MapCodec CODEC = MapCodec.unit(INSTANCE); + + @Override + public MapCodec mapCodec() { + return CODEC; + } + } + + record Simple(ResourceKey model, List tints) implements ItemModel { + public static final MapCodec CODEC = RecordCodecBuilder.mapCodec( + instance -> instance.group( + SpongeCodecs.RESOURCE_KEY.fieldOf("model").forGetter(Simple::model), + TintSource.CODEC.listOf().optionalFieldOf("tints", List.of()).forGetter(Simple::tints) + ).apply(instance, Simple::new)); + + public Simple(final ResourceKey model, final List tints) { + this.model = Objects.requireNonNull(model, "model"); + this.tints = Objects.requireNonNull(tints, "tints"); + } + + @Override + public MapCodec mapCodec() { + return CODEC; + } + } + + record Composite(List models) implements ItemModel { + public static final MapCodec CODEC = RecordCodecBuilder.mapCodec( + instance -> instance.group( + ItemModel.CODEC.listOf().fieldOf("models").forGetter(Composite::models) + ).apply(instance, Composite::new)); + + public Composite(final List models) { + this.models = Objects.requireNonNull(models, "models"); + } + + @Override + public MapCodec mapCodec() { + return CODEC; + } + } + + record Conditional(ConditionalProperty property, ItemModel onTrue, ItemModel onFalse) implements ItemModel { + public static final MapCodec CODEC = RecordCodecBuilder.mapCodec( + instance -> instance.group( + ConditionalProperty.CODEC.forGetter(Conditional::property), + ItemModel.CODEC.fieldOf("on_true").forGetter(Conditional::onTrue), + ItemModel.CODEC.fieldOf("on_false").forGetter(Conditional::onFalse)) + .apply(instance, Conditional::new)); + + public Conditional(final ConditionalProperty property, final ItemModel onTrue, final ItemModel onFalse) { + this.property = Objects.requireNonNull(property, "property"); + this.onTrue = Objects.requireNonNull(onTrue, "onTrue"); + this.onFalse = Objects.requireNonNull(onFalse, "onFalse"); + } + + @Override + public MapCodec mapCodec() { + return CODEC; + } + } + + record Select(SelectSwitch body, Optional fallback) implements ItemModel { + public static final MapCodec