diff --git a/build.gradle b/build.gradle index fcbf46b04..b3cb01597 100644 --- a/build.gradle +++ b/build.gradle @@ -24,8 +24,8 @@ allprojects { compileOnly 'org.projectlombok:lombok:1.18.24' annotationProcessor 'org.projectlombok:lombok:1.18.24' implementation 'art.arcane.source:Source:22.6.9' - implementation 'art.arcane:Amulet:22.6.9' - implementation 'com.github.ben-manes.caffeine:caffeine:3.0.6' + implementation 'art.arcane:Amulet:22.7.1' + implementation 'com.github.ben-manes.caffeine:caffeine:3.1.1' annotationProcessor 'systems.manifold:manifold-ext:2022.1.18' testAnnotationProcessor 'systems.manifold:manifold-ext:2022.1.18' implementation 'systems.manifold:manifold-rt:2022.1.18' diff --git a/bukkit/src/main/resources/plugin.yml b/bukkit/src/main/resources/plugin.yml index 116e58960..787fb1077 100644 --- a/bukkit/src/main/resources/plugin.yml +++ b/bukkit/src/main/resources/plugin.yml @@ -5,4 +5,4 @@ api-version: 1.19 libraries: - org.apache-extras.beanshell:bsh:2.0b6 - com.google.code.gson:gson:2.8.9 -- com.github.ben-manes.caffeine:caffeine:3.0.6 \ No newline at end of file +- com.github.ben-manes.caffeine:caffeine:3.1.1 \ No newline at end of file diff --git a/engine/src/main/java/com/volmit/iris/engine/EngineEditor.java b/engine/src/main/java/com/volmit/iris/engine/EngineEditor.java index 8933a663e..39bbfe5b8 100644 --- a/engine/src/main/java/com/volmit/iris/engine/EngineEditor.java +++ b/engine/src/main/java/com/volmit/iris/engine/EngineEditor.java @@ -5,11 +5,25 @@ import art.arcane.amulet.io.JarLoader; import com.google.gson.Gson; import com.google.gson.GsonBuilder; import com.google.gson.TypeAdapterFactory; +import com.volmit.iris.engine.dimension.IrisAuthor; +import com.volmit.iris.engine.dimension.IrisBiome; +import com.volmit.iris.engine.dimension.IrisChance; +import com.volmit.iris.engine.dimension.IrisDecorator; +import com.volmit.iris.engine.dimension.IrisDimension; +import com.volmit.iris.engine.dimension.IrisDimensionMeta; import com.volmit.iris.engine.dimension.IrisGenerator; +import com.volmit.iris.engine.dimension.IrisPalette; +import com.volmit.iris.engine.dimension.IrisRange; +import com.volmit.iris.engine.dimension.IrisResolvable; +import com.volmit.iris.engine.dimension.IrisSeed; +import com.volmit.iris.engine.dimension.IrisSeedSetMode; +import com.volmit.iris.engine.dimension.IrisSurface; +import com.volmit.iris.engine.dimension.IrisSurfaceLayer; import com.volmit.iris.engine.editor.Resolvable; import lombok.Data; import java.util.List; +import java.util.Map; import java.util.Objects; @Data @@ -25,6 +39,7 @@ public class EngineEditor { .filter(Objects::nonNull) .filter(i -> !i.isInterface() && !i.isEnum()) .filter(i -> i.isAssignableFrom(Resolvable.class) || Resolvable.class.isAssignableFrom(i)) + .filter(i -> !i.equals(IrisResolvable.class)) .map(i -> J.attempt(() -> (Resolvable) i.getDeclaredConstructor().newInstance(), null)).toList(), List.of()); GsonBuilder gsonBuilder = new GsonBuilder(); resolvableTypes.forEach(i -> i.apply(gsonBuilder)); @@ -32,5 +47,69 @@ public class EngineEditor { i("Registered " + resolvableTypes.size() + " Mutators with " + resolvableTypes.stream().filter(i -> i instanceof TypeAdapterFactory).count() + " Type Adapter Factories"); i(gson.toJson(gson.fromJson("Noise.simplex(seed)", IrisGenerator.class))); + + System.out.println(gson.toJson(IrisDimension.builder() + .biome(IrisBiome.builder() + .name("Plains") + .surface(IrisSurface.builder() + .decorator(IrisDecorator.builder() + .palette(IrisPalette.flat("minecraft:grass")) + .chance(IrisChance.half(IrisGenerator.builder() + .java("Noise.static(seed)") + .seed(IrisSeed.builder() + .offset(67) + .build()) + .build())) + .build()) + .layer(IrisSurfaceLayer.builder() + .palette(IrisPalette.flat("minecraft:grass_block")) + .thickness(IrisRange.flat(1)) + .build()) + .layer(IrisSurfaceLayer.builder() + .palette(IrisPalette.builder() + .block("minecraft:dirt") + .block("minecraft:coarse_dirt") + .build()) + .thickness(IrisRange.builder() + .min(3) + .max(5) + .generator(IrisGenerator.builder() + .java("Noise.simplex(seed).warp(Noise.perlin(seed+1), 0.5, 9)") + .seed(IrisSeed.builder() + .offset(446) + .mode(IrisSeedSetMode.WORLD_OFFSET) + .build()) + .build()) + .build()) + .build()) + .layer(IrisSurfaceLayer.builder() + .palette(IrisPalette.builder() + .block("minecraft:stone") + .block("minecraft:granite") + .build()) + .thickness(IrisRange.builder() + .min(3) + .max(7) + .generator(IrisGenerator.builder() + .java("Noise.simplex(seed).warp(Noise.perlin(seed+1), 0.5, 9)") + .seed(IrisSeed.builder() + .offset(123) + .mode(IrisSeedSetMode.WORLD_OFFSET) + .build()) + .build()) + .build()) + .build()) + .build()) + .build()) + .meta(IrisDimensionMeta.builder() + .name("Overworld") + .description("The overworld generates stuff") + .version("3.0.0") + .author(IrisAuthor.builder() + .name("cyberpwn") + .social(Map.of("discord", "cyberpwn#1337")) + .build()) + .build()) + .build())); } } diff --git a/engine/src/main/java/com/volmit/iris/engine/dimension/IrisDimensionAuthor.java b/engine/src/main/java/com/volmit/iris/engine/dimension/IrisAuthor.java similarity index 73% rename from engine/src/main/java/com/volmit/iris/engine/dimension/IrisDimensionAuthor.java rename to engine/src/main/java/com/volmit/iris/engine/dimension/IrisAuthor.java index 617bd2741..219f13081 100644 --- a/engine/src/main/java/com/volmit/iris/engine/dimension/IrisDimensionAuthor.java +++ b/engine/src/main/java/com/volmit/iris/engine/dimension/IrisAuthor.java @@ -4,6 +4,7 @@ import com.volmit.iris.engine.editor.Resolvable; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; +import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; import lombok.experimental.Accessors; @@ -13,8 +14,10 @@ import java.util.Map; @NoArgsConstructor @Builder @AllArgsConstructor +@EqualsAndHashCode(callSuper=false) @Accessors(fluent = true, chain = true) -public class IrisDimensionAuthor implements Resolvable +@Resolvable.Entity(id = "author") +public class IrisAuthor extends IrisResolvable { private String name; private Map social; diff --git a/engine/src/main/java/com/volmit/iris/engine/dimension/IrisBiome.java b/engine/src/main/java/com/volmit/iris/engine/dimension/IrisBiome.java index 637228585..72980f83a 100644 --- a/engine/src/main/java/com/volmit/iris/engine/dimension/IrisBiome.java +++ b/engine/src/main/java/com/volmit/iris/engine/dimension/IrisBiome.java @@ -1,17 +1,27 @@ package com.volmit.iris.engine.dimension; +import com.google.gson.stream.JsonToken; import com.volmit.iris.engine.editor.Resolvable; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; +import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; import lombok.experimental.Accessors; +import java.util.ArrayList; +import java.util.List; + @Builder @Data @AllArgsConstructor @NoArgsConstructor +@EqualsAndHashCode(callSuper=false) @Accessors(fluent = true, chain = true) -public class IrisBiome implements Resolvable { +@Resolvable.Entity(id = "biome") +public class IrisBiome extends IrisResolvable { private String name; + + @Builder.Default + private IrisSurface surface = new IrisSurface(); } diff --git a/engine/src/main/java/com/volmit/iris/engine/dimension/IrisChance.java b/engine/src/main/java/com/volmit/iris/engine/dimension/IrisChance.java new file mode 100644 index 000000000..0724d7e46 --- /dev/null +++ b/engine/src/main/java/com/volmit/iris/engine/dimension/IrisChance.java @@ -0,0 +1,73 @@ +package com.volmit.iris.engine.dimension; + +import com.google.gson.Gson; +import com.google.gson.TypeAdapter; +import com.google.gson.TypeAdapterFactory; +import com.google.gson.reflect.TypeToken; +import com.google.gson.stream.JsonReader; +import com.google.gson.stream.JsonToken; +import com.google.gson.stream.JsonWriter; +import com.volmit.iris.engine.editor.Resolvable; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +import java.io.IOException; + +@Builder +@Data +@AllArgsConstructor +@NoArgsConstructor +@EqualsAndHashCode(callSuper=false) +@Accessors(fluent = true, chain = true) +@Resolvable.Entity(id = "chance", jsonTypes = {JsonToken.STRING, JsonToken.BEGIN_OBJECT}) +public class IrisChance extends IrisResolvable implements TypeAdapterFactory { + @Builder.Default + @TokenConstructor(JsonToken.NUMBER) + private double threshold = 0.5; + + @TokenConstructor(JsonToken.STRING) + @Builder.Default + private IrisGenerator generator = IrisGenerator.WHITE; + + @Override + public TypeAdapter create(Gson gson, TypeToken type) { + final TypeAdapter delegate = gson.getDelegateAdapter(this, type); + + if(!type.getRawType().equals(getClass())) { + return null; + } + + return new TypeAdapter<>() { + public void write(JsonWriter out, T value) {writeSafeJson(delegate, out, value);} + + @SuppressWarnings("unchecked") + public T read(JsonReader in) throws IOException { + JsonToken token = in.peek(); + + if(token == JsonToken.STRING) { + return (T) IrisChance.half(gson.fromJson(in.nextString(), IrisGenerator.class)); + } + + return delegate.read(in); + } + }; + } + + public static IrisChance half(IrisGenerator generator) { + return IrisChance.builder() + .threshold(0.5) + .generator(generator) + .build(); + } + + public static IrisChance white(double chance) { + return IrisChance.builder() + .threshold(chance) + .generator(IrisGenerator.WHITE) + .build(); + } +} \ No newline at end of file diff --git a/engine/src/main/java/com/volmit/iris/engine/dimension/IrisDecorator.java b/engine/src/main/java/com/volmit/iris/engine/dimension/IrisDecorator.java new file mode 100644 index 000000000..d9924e9d5 --- /dev/null +++ b/engine/src/main/java/com/volmit/iris/engine/dimension/IrisDecorator.java @@ -0,0 +1,28 @@ +package com.volmit.iris.engine.dimension; + +import com.volmit.iris.engine.editor.Resolvable; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import lombok.Singular; +import lombok.experimental.Accessors; + +import java.util.ArrayList; +import java.util.List; + +@Builder +@Data +@AllArgsConstructor +@NoArgsConstructor +@EqualsAndHashCode(callSuper=false) +@Accessors(fluent = true, chain = true) +@Resolvable.Entity(id = "decorator") +public class IrisDecorator extends IrisResolvable { + @Builder.Default + private IrisPalette palette = IrisPalette.flat("minecraft:grass"); + + @Builder.Default + private IrisChance chance = IrisChance.white(0.25); +} diff --git a/engine/src/main/java/com/volmit/iris/engine/dimension/IrisDimension.java b/engine/src/main/java/com/volmit/iris/engine/dimension/IrisDimension.java index 8a4467aeb..b948501d3 100644 --- a/engine/src/main/java/com/volmit/iris/engine/dimension/IrisDimension.java +++ b/engine/src/main/java/com/volmit/iris/engine/dimension/IrisDimension.java @@ -4,16 +4,26 @@ import com.volmit.iris.engine.editor.Resolvable; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; +import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; +import lombok.Singular; import lombok.experimental.Accessors; +import java.util.ArrayList; +import java.util.List; + @Data @NoArgsConstructor @Builder @AllArgsConstructor +@EqualsAndHashCode(callSuper=false) @Accessors(fluent = true, chain = true) -public class IrisDimension implements Resolvable -{ +@Resolvable.Entity(id = "dimension") +public class IrisDimension extends IrisResolvable { @Builder.Default private IrisDimensionMeta meta = new IrisDimensionMeta(); + + @Singular + @Type(IrisBiome.class) + private List biomes = new ArrayList<>(); } diff --git a/engine/src/main/java/com/volmit/iris/engine/dimension/IrisDimensionMeta.java b/engine/src/main/java/com/volmit/iris/engine/dimension/IrisDimensionMeta.java index 4105606c8..9848dce98 100644 --- a/engine/src/main/java/com/volmit/iris/engine/dimension/IrisDimensionMeta.java +++ b/engine/src/main/java/com/volmit/iris/engine/dimension/IrisDimensionMeta.java @@ -4,6 +4,7 @@ import com.volmit.iris.engine.editor.Resolvable; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; +import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; import lombok.Singular; import lombok.experimental.Accessors; @@ -15,12 +16,15 @@ import java.util.List; @NoArgsConstructor @Builder @AllArgsConstructor +@EqualsAndHashCode(callSuper=false) @Accessors(fluent = true, chain = true) -public class IrisDimensionMeta implements Resolvable +@Resolvable.Entity(id = "dimension-meta") +public class IrisDimensionMeta extends IrisResolvable { private String name; private String description; private String version; @Singular - private List authors = new ArrayList<>(); + @Resolvable.Type(IrisAuthor.class) + private List authors = new ArrayList<>(); } \ No newline at end of file diff --git a/engine/src/main/java/com/volmit/iris/engine/dimension/IrisGenerator.java b/engine/src/main/java/com/volmit/iris/engine/dimension/IrisGenerator.java index 047a0b3ca..be8dad1ec 100644 --- a/engine/src/main/java/com/volmit/iris/engine/dimension/IrisGenerator.java +++ b/engine/src/main/java/com/volmit/iris/engine/dimension/IrisGenerator.java @@ -14,6 +14,7 @@ import com.volmit.iris.engine.editor.Resolvable; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; +import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; import lombok.experimental.Accessors; @@ -24,13 +25,19 @@ import java.io.IOException; @NoArgsConstructor @Builder @AllArgsConstructor +@EqualsAndHashCode(callSuper=false) @Accessors(fluent = true, chain = true) -public class IrisGenerator implements Resolvable, TypeAdapterFactory { +@Resolvable.Entity(id = "generator", jsonTypes = {JsonToken.STRING, JsonToken.BEGIN_OBJECT}) +public class IrisGenerator extends IrisResolvable implements TypeAdapterFactory { + public static final IrisGenerator NATURAL = IrisGenerator.builder().java("art.arcane.source.api.util.NoisePreset.NATURAL.create(seed)").build(); + public static final IrisGenerator WHITE = IrisGenerator.builder().java("Noise.white(seed)").build(); + public static final IrisGenerator FLAT = IrisGenerator.builder().java("Noise.flat(seed)").build(); + @Builder.Default private String java = "art.arcane.source.api.util.NoisePreset.NATURAL.create(seed)"; @Builder.Default - private IrisSeedSet seed = new IrisSeedSet(); + private IrisSeed seed = new IrisSeed(); public NoisePlane getNoisePlane(long seed) { @@ -53,11 +60,11 @@ public class IrisGenerator implements Resolvable, TypeAdapterFactory { return new TypeAdapter<>() { public void write(JsonWriter out, T value) {writeSafeJson(delegate, out, value);} + @SuppressWarnings("unchecked") public T read(JsonReader in) throws IOException { JsonToken token = in.peek(); - if(token == JsonToken.STRING) - { + if(token == JsonToken.STRING) { return (T) IrisGenerator.builder().java(in.nextString()).build(); } diff --git a/engine/src/main/java/com/volmit/iris/engine/dimension/IrisPalette.java b/engine/src/main/java/com/volmit/iris/engine/dimension/IrisPalette.java new file mode 100644 index 000000000..f55b71b69 --- /dev/null +++ b/engine/src/main/java/com/volmit/iris/engine/dimension/IrisPalette.java @@ -0,0 +1,74 @@ +package com.volmit.iris.engine.dimension; + +import com.google.gson.Gson; +import com.google.gson.TypeAdapter; +import com.google.gson.TypeAdapterFactory; +import com.google.gson.reflect.TypeToken; +import com.google.gson.stream.JsonReader; +import com.google.gson.stream.JsonToken; +import com.google.gson.stream.JsonWriter; +import com.volmit.iris.engine.editor.Resolvable; +import com.volmit.iris.platform.PlatformBlock; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import lombok.Singular; +import lombok.ToString; +import lombok.experimental.Accessors; +import org.apache.commons.lang3.builder.ToStringExclude; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +@Builder +@Data +@AllArgsConstructor +@NoArgsConstructor +@EqualsAndHashCode(callSuper=false) +@Accessors(fluent = true, chain = true) +@Resolvable.Entity(id = "palette") +public class IrisPalette extends IrisResolvable implements TypeAdapterFactory { + @Singular + @PlatformType(PlatformBlock.class) + @TokenConstructor(JsonToken.STRING) + private List blocks = new ArrayList<>(); + + @Builder.Default + private IrisGenerator generator = IrisGenerator.WHITE; + + @Override + public TypeAdapter create(Gson gson, TypeToken type) { + final TypeAdapter delegate = gson.getDelegateAdapter(this, type); + + if(!type.getRawType().equals(getClass())) { + return null; + } + + return new TypeAdapter<>() { + public void write(JsonWriter out, T value) { + writeSafeJson(delegate, out, value); + } + + @SuppressWarnings("unchecked") + public T read(JsonReader in) throws IOException { + JsonToken token = in.peek(); + + if(token == JsonToken.STRING) { + return (T) IrisPalette.flat(in.nextString()); + } + + return delegate.read(in); + } + }; + } + + public static IrisPalette flat(String block){ + return IrisPalette.builder() + .block(block) + .generator(IrisGenerator.FLAT) + .build(); + } +} diff --git a/engine/src/main/java/com/volmit/iris/engine/dimension/IrisRange.java b/engine/src/main/java/com/volmit/iris/engine/dimension/IrisRange.java new file mode 100644 index 000000000..bc7b5c313 --- /dev/null +++ b/engine/src/main/java/com/volmit/iris/engine/dimension/IrisRange.java @@ -0,0 +1,70 @@ +package com.volmit.iris.engine.dimension; + +import com.google.gson.Gson; +import com.google.gson.TypeAdapter; +import com.google.gson.TypeAdapterFactory; +import com.google.gson.reflect.TypeToken; +import com.google.gson.stream.JsonReader; +import com.google.gson.stream.JsonToken; +import com.google.gson.stream.JsonWriter; +import com.volmit.iris.engine.editor.Resolvable; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +import java.io.IOException; + +@Builder +@Data +@AllArgsConstructor +@NoArgsConstructor +@EqualsAndHashCode(callSuper=false) +@Accessors(fluent = true, chain = true) +@Resolvable.Entity(id = "range", jsonTypes = {JsonToken.NUMBER, JsonToken.BEGIN_OBJECT}) +public class IrisRange extends IrisResolvable implements TypeAdapterFactory { + @Builder.Default + @TokenConstructor(JsonToken.NUMBER) + private double max = 1; + + @Builder.Default + private double min = 1; + + @Builder.Default + private IrisGenerator generator = IrisGenerator.NATURAL; + + @Override + public TypeAdapter create(Gson gson, TypeToken type) { + final TypeAdapter delegate = gson.getDelegateAdapter(this, type); + + if(!type.getRawType().equals(getClass())) { + return null; + } + + return new TypeAdapter<>() { + public void write(JsonWriter out, T value) {writeSafeJson(delegate, out, value);} + + @SuppressWarnings("unchecked") + public T read(JsonReader in) throws IOException { + JsonToken token = in.peek(); + + if(token == JsonToken.NUMBER) { + double d = in.nextDouble(); + return (T) IrisRange.builder().min(d).max(d).generator(IrisGenerator.FLAT).build(); + } + + return delegate.read(in); + } + }; + } + + public static IrisRange flat(double v) { + return IrisRange.builder() + .max(v) + .min(v) + .generator(IrisGenerator.FLAT) + .build(); + } +} \ No newline at end of file diff --git a/engine/src/main/java/com/volmit/iris/engine/dimension/IrisResolvable.java b/engine/src/main/java/com/volmit/iris/engine/dimension/IrisResolvable.java new file mode 100644 index 000000000..8806c4fb7 --- /dev/null +++ b/engine/src/main/java/com/volmit/iris/engine/dimension/IrisResolvable.java @@ -0,0 +1,15 @@ +package com.volmit.iris.engine.dimension; + +import com.volmit.iris.engine.editor.Resolvable; +import com.volmit.iris.platform.PlatformNamespaceKey; +import lombok.Data; + +@Data +public class IrisResolvable implements Resolvable { + private PlatformNamespaceKey key; + + @Override + public PlatformNamespaceKey getKey() { + return null; + } +} diff --git a/engine/src/main/java/com/volmit/iris/engine/dimension/IrisSeed.java b/engine/src/main/java/com/volmit/iris/engine/dimension/IrisSeed.java new file mode 100644 index 000000000..00c179122 --- /dev/null +++ b/engine/src/main/java/com/volmit/iris/engine/dimension/IrisSeed.java @@ -0,0 +1,86 @@ +package com.volmit.iris.engine.dimension; + +import com.google.gson.Gson; +import com.google.gson.TypeAdapter; +import com.google.gson.TypeAdapterFactory; +import com.google.gson.reflect.TypeToken; +import com.google.gson.stream.JsonReader; +import com.google.gson.stream.JsonToken; +import com.google.gson.stream.JsonWriter; +import com.volmit.iris.engine.Engine; +import com.volmit.iris.engine.editor.Resolvable; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +import java.io.IOException; + +@Data +@NoArgsConstructor +@Builder +@AllArgsConstructor +@EqualsAndHashCode(callSuper=false) +@Accessors(fluent = true, chain = true) +@Resolvable.Entity(id = "seed", jsonTypes = {JsonToken.NUMBER, JsonToken.STRING, JsonToken.BEGIN_OBJECT}) +public class IrisSeed extends IrisResolvable implements TypeAdapterFactory { + @Builder.Default + private IrisSeedSetMode mode = IrisSeedSetMode.LOCAL_OFFSET; + + @Builder.Default + @TokenConstructor(JsonToken.NUMBER) + private long offset = 0; + + @TokenConstructor(JsonToken.STRING) + private String hashOffset; + + public long getOffset() { + if(hashOffset != null && hashOffset.isNotEmpty()) { + return hashOffset.hashCode() + offset; + } + + return offset; + } + + public double getSeed(Engine engine, long localSeed) { + return switch(mode) + { + case WORLD -> engine.getSeedManager().getWorldSeed(); + case LOCAL -> localSeed; + case LOCAL_OFFSET -> localSeed + getOffset(); + case RAW -> getOffset(); + case WORLD_OFFSET -> engine.getSeedManager().getWorldSeed() + getOffset(); + case RANDOM -> (Math.random() * Long.MAX_VALUE) + (Math.random() * Long.MAX_VALUE); + }; + } + + @Override + public TypeAdapter create(Gson gson, TypeToken type) { + final TypeAdapter delegate = gson.getDelegateAdapter(this, type); + + if(!type.getRawType().equals(getClass())) { + return null; + } + + return new TypeAdapter<>() { + public void write(JsonWriter out, T value) {writeSafeJson(delegate, out, value);} + + @SuppressWarnings("unchecked") + public T read(JsonReader in) throws IOException { + JsonToken token = in.peek(); + + if(token == JsonToken.STRING) { + return (T) IrisSeed.builder().hashOffset(in.nextString()).build(); + } + + if(token == JsonToken.NUMBER) { + return (T) IrisSeed.builder().offset(Double.doubleToLongBits(in.nextDouble())).build(); + } + + return delegate.read(in); + } + }; + } +} diff --git a/engine/src/main/java/com/volmit/iris/engine/dimension/IrisSeedSet.java b/engine/src/main/java/com/volmit/iris/engine/dimension/IrisSeedSet.java deleted file mode 100644 index 806de23de..000000000 --- a/engine/src/main/java/com/volmit/iris/engine/dimension/IrisSeedSet.java +++ /dev/null @@ -1,30 +0,0 @@ -package com.volmit.iris.engine.dimension; - -import com.volmit.iris.engine.Engine; -import com.volmit.iris.engine.editor.Resolvable; -import lombok.AllArgsConstructor; -import lombok.Data; -import lombok.NoArgsConstructor; -import lombok.experimental.Accessors; - -@Data -@NoArgsConstructor -@AllArgsConstructor -@Accessors(fluent = true, chain = true) -public class IrisSeedSet implements Resolvable { - private IrisSeedSetMode mode = IrisSeedSetMode.LOCAL_OFFSET; - private long offset = 1337; - - public double getSeed(Engine engine, long localSeed) - { - return switch(mode) - { - case WORLD -> engine.getSeedManager().getWorldSeed(); - case LOCAL -> localSeed; - case LOCAL_OFFSET -> localSeed + offset; - case RAW -> offset; - case WORLD_OFFSET -> engine.getSeedManager().getWorldSeed() + offset; - case RANDOM -> (Math.random() * Long.MAX_VALUE) + (Math.random() * Long.MAX_VALUE); - }; - } -} diff --git a/engine/src/main/java/com/volmit/iris/engine/dimension/IrisSurface.java b/engine/src/main/java/com/volmit/iris/engine/dimension/IrisSurface.java new file mode 100644 index 000000000..2e6646f7f --- /dev/null +++ b/engine/src/main/java/com/volmit/iris/engine/dimension/IrisSurface.java @@ -0,0 +1,39 @@ +package com.volmit.iris.engine.dimension; + +import com.google.gson.Gson; +import com.google.gson.TypeAdapter; +import com.google.gson.TypeAdapterFactory; +import com.google.gson.reflect.TypeToken; +import com.google.gson.stream.JsonReader; +import com.google.gson.stream.JsonToken; +import com.google.gson.stream.JsonWriter; +import com.volmit.iris.engine.editor.Resolvable; +import com.volmit.iris.platform.PlatformBlock; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import lombok.Singular; +import lombok.experimental.Accessors; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +@Builder +@Data +@AllArgsConstructor +@NoArgsConstructor +@EqualsAndHashCode(callSuper=false) +@Accessors(fluent = true, chain = true) +@Resolvable.Entity(id = "surface-layer") +public class IrisSurface extends IrisResolvable { + @Singular + @Type(IrisSurfaceLayer.class) + private List layers = new ArrayList<>(); + + @Singular + @Type(IrisDecorator.class) + private List decorators = new ArrayList<>(); +} diff --git a/engine/src/main/java/com/volmit/iris/engine/dimension/IrisSurfaceLayer.java b/engine/src/main/java/com/volmit/iris/engine/dimension/IrisSurfaceLayer.java new file mode 100644 index 000000000..59df1938e --- /dev/null +++ b/engine/src/main/java/com/volmit/iris/engine/dimension/IrisSurfaceLayer.java @@ -0,0 +1,27 @@ +package com.volmit.iris.engine.dimension; + +import com.google.gson.stream.JsonToken; +import com.volmit.iris.engine.editor.Resolvable; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +import java.util.List; + +@Builder +@Data +@AllArgsConstructor +@NoArgsConstructor +@EqualsAndHashCode(callSuper=false) +@Accessors(fluent = true, chain = true) +@Resolvable.Entity(id = "surface-layer") +public class IrisSurfaceLayer extends IrisResolvable { + @Builder.Default + private IrisPalette palette = IrisPalette.flat("minecraft:stone"); + + @Builder.Default + private IrisRange thickness = IrisRange.flat(1); +} diff --git a/engine/src/main/java/com/volmit/iris/engine/editor/Resolvable.java b/engine/src/main/java/com/volmit/iris/engine/editor/Resolvable.java index dad8476b4..d038ce717 100644 --- a/engine/src/main/java/com/volmit/iris/engine/editor/Resolvable.java +++ b/engine/src/main/java/com/volmit/iris/engine/editor/Resolvable.java @@ -1,13 +1,21 @@ package com.volmit.iris.engine.editor; +import art.arcane.amulet.format.Form; import com.google.gson.GsonBuilder; import com.google.gson.TypeAdapter; import com.google.gson.TypeAdapterFactory; +import com.google.gson.stream.JsonToken; import com.google.gson.stream.JsonWriter; +import com.volmit.iris.platform.PlatformNamespaced; +import lombok.AllArgsConstructor; import java.io.IOException; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; -public interface Resolvable { +public interface Resolvable extends PlatformNamespaced { default void apply(GsonBuilder builder) { if(this instanceof TypeAdapterFactory f) { builder.registerTypeAdapterFactory(f); @@ -25,4 +33,50 @@ public interface Resolvable { } } } + + @Target({ElementType.FIELD, ElementType.TYPE}) + @Retention(RetentionPolicy.RUNTIME) + @interface Entity { + String id(); + String name() default ""; + String namePlural() default ""; + JsonToken[] jsonTypes() default JsonToken.BEGIN_OBJECT; + + @AllArgsConstructor + @lombok.Data + class ResolverEntityData + { + private final Entity annotation; + + public String getId() { + return annotation.id(); + } + + public String getName() { + return annotation.name().isEmpty() ? Form.capitalizeWords(getId().replaceAll("\\Q-\\E", " ")) : annotation.name(); + } + + public String getNamePlural() { + return annotation.namePlural().isEmpty() ? getName() + "s" : annotation.namePlural(); + } + } + } + + @Target({ElementType.FIELD, ElementType.TYPE}) + @Retention(RetentionPolicy.RUNTIME) + @interface TokenConstructor { + JsonToken[] value(); + } + + @Target({ElementType.FIELD, ElementType.TYPE}) + @Retention(RetentionPolicy.RUNTIME) + @interface Type { + Class value(); + } + + @Target({ElementType.FIELD, ElementType.TYPE}) + @Retention(RetentionPolicy.RUNTIME) + @interface PlatformType { + Class value(); + } } diff --git a/engine/src/main/java/com/volmit/iris/engine/editor/pak/PakInputStream.java b/engine/src/main/java/com/volmit/iris/engine/editor/pak/PakInputStream.java new file mode 100644 index 000000000..8575b478f --- /dev/null +++ b/engine/src/main/java/com/volmit/iris/engine/editor/pak/PakInputStream.java @@ -0,0 +1,62 @@ +package com.volmit.iris.engine.editor.pak; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; + +public class PakInputStream extends InputStream { + private final File folder; + private final String name; + private final int pakSize; + private int read; + private int currentPakNumber; + private InputStream currentPakStream; + + public PakInputStream(File folder, String name, int pakSize) throws IOException { + this.pakSize = pakSize; + this.folder = folder; + this.name = name; + this.read = 0; + this.currentPakNumber = 0; + this.currentPakStream = readPakFile(currentPakNumber); + } + + private InputStream readPakFile(int number) throws IOException { + File f = new File(folder, name + number + ".pak"); + + if(!f.exists()) { + return null; + } + + return new FileInputStream(f); + } + + @Override + public int read() throws IOException { + if(currentPakStream == null) { + return -1; + } + + if(read++ == pakSize) { + currentPakStream.close(); + currentPakStream = readPakFile(++currentPakNumber); + + if(currentPakStream == null) + { + return -1; + } + + read = 1; + } + + return currentPakStream.read(); + } + + public void close() throws IOException { + if(currentPakStream != null) { + currentPakStream.close(); + } + } +} diff --git a/engine/src/main/java/com/volmit/iris/engine/editor/pak/PakMetadata.java b/engine/src/main/java/com/volmit/iris/engine/editor/pak/PakMetadata.java new file mode 100644 index 000000000..25dd8fbc6 --- /dev/null +++ b/engine/src/main/java/com/volmit/iris/engine/editor/pak/PakMetadata.java @@ -0,0 +1,22 @@ +package com.volmit.iris.engine.editor.pak; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.Singular; +import lombok.experimental.Accessors; + +import java.util.List; + +@NoArgsConstructor +@Data +@AllArgsConstructor +@Builder +@Accessors(chain = true, fluent = true) +public class PakMetadata { + private String namespace; + @Singular + private List resources; + private long pakSize; +} diff --git a/engine/src/main/java/com/volmit/iris/engine/editor/pak/PakOutputStream.java b/engine/src/main/java/com/volmit/iris/engine/editor/pak/PakOutputStream.java new file mode 100644 index 000000000..b42560278 --- /dev/null +++ b/engine/src/main/java/com/volmit/iris/engine/editor/pak/PakOutputStream.java @@ -0,0 +1,57 @@ +package com.volmit.iris.engine.editor.pak; + +import lombok.Getter; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.List; + +public class PakOutputStream extends OutputStream { + @Getter + private final File folder; + @Getter + private final String name; + private OutputStream currentPakOutput; + private int currentPakNumber; + @Getter + private final long pakSize; + @Getter + private int written; + @Getter + private final List writtenFiles; + + public PakOutputStream(File folder, String name, long pakSize) throws IOException { + folder.mkdirs(); + this.writtenFiles = new ArrayList<>(); + this.written = 0; + this.name = name; + this.currentPakNumber = 0; + this.currentPakOutput = writePakFile(0); + this.pakSize = pakSize; + this.folder = folder; + } + + private OutputStream writePakFile(int number) throws IOException { + File f = new File(folder, name + number + ".pak"); + writtenFiles.add(f); + return new FileOutputStream(f); + } + + @Override + public void write(int b) throws IOException { + if(written++ == pakSize) { + currentPakOutput.close(); + currentPakOutput = writePakFile(++currentPakNumber); + written = 1; + } + + currentPakOutput.write(b); + } + + public void close() throws IOException { + currentPakOutput.close(); + } +} diff --git a/engine/src/main/java/com/volmit/iris/engine/editor/pak/PakResourceInput.java b/engine/src/main/java/com/volmit/iris/engine/editor/pak/PakResourceInput.java new file mode 100644 index 000000000..34a5b5e09 --- /dev/null +++ b/engine/src/main/java/com/volmit/iris/engine/editor/pak/PakResourceInput.java @@ -0,0 +1,51 @@ +package com.volmit.iris.engine.editor.pak; + +import com.volmit.iris.engine.editor.Resolvable; +import com.volmit.iris.platform.PlatformNamespaceKey; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.DataOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.function.Consumer; +import java.util.function.Supplier; + +@NoArgsConstructor +@Builder +@Data +@AllArgsConstructor +public class PakResourceInput { + private Class type; + private PlatformNamespaceKey key; + private Supplier reader; + private long size; + + public long write(OutputStream out) throws IOException { + InputStream in = reader.get(); + long w = in.transferTo(out); + in.close(); + return w; + } + + public static PakResourceInput file(PlatformNamespaceKey key, Class type, File f) { + return PakResourceInput.builder() + .size(f.length()) + .key(key) + .type(type) + .reader(() -> { + try { + return new FileInputStream(f); + } catch(FileNotFoundException e) { + throw new RuntimeException(e); + } + }) + .build(); + } +} diff --git a/engine/src/main/java/com/volmit/iris/engine/editor/pak/PakResourceMetadata.java b/engine/src/main/java/com/volmit/iris/engine/editor/pak/PakResourceMetadata.java new file mode 100644 index 000000000..271affa7f --- /dev/null +++ b/engine/src/main/java/com/volmit/iris/engine/editor/pak/PakResourceMetadata.java @@ -0,0 +1,22 @@ +package com.volmit.iris.engine.editor.pak; + +import com.volmit.iris.engine.editor.Resolvable; +import com.volmit.iris.platform.PlatformNamespaceKey; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +@NoArgsConstructor +@Data +@AllArgsConstructor +@Builder +@Accessors(chain = true, fluent = true) +public class PakResourceMetadata +{ + private String type; + private String key; + private long start; + private long length; +} diff --git a/engine/src/main/java/com/volmit/iris/engine/editor/pak/PakWriter.java b/engine/src/main/java/com/volmit/iris/engine/editor/pak/PakWriter.java new file mode 100644 index 000000000..e7ae59ce0 --- /dev/null +++ b/engine/src/main/java/com/volmit/iris/engine/editor/pak/PakWriter.java @@ -0,0 +1,62 @@ +package com.volmit.iris.engine.editor.pak; + +import art.arcane.amulet.io.nbt.nbt.io.NBTUtil; +import art.arcane.amulet.io.nbt.nbt.io.NamedTag; +import art.arcane.amulet.io.nbt.objects.NBTObjectSerializer; +import art.arcane.amulet.io.nbt.objects.UnserializableClassException; + +import java.io.DataOutputStream; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.List; +import static art.arcane.amulet.MagicalSugar.*; + +public class PakWriter { + private final List resources; + private final File folder; + private final PakOutputStream output; + private final String name; + private final long pakSize; + + public PakWriter(File folder, String name, long pakSize) throws IOException { + folder.mkdirs(); + this.name = name; + this.folder = folder; + this.pakSize = pakSize; + output = new PakOutputStream(folder, name, pakSize); + resources = new ArrayList<>(); + } + + public PakWriter(File folder, String name) throws IOException { + this(folder, name, 1MB); + } + + public void write() throws IOException, UnserializableClassException, IllegalAccessException { + PakMetadata.PakMetadataBuilder meta = PakMetadata.builder().namespace(name).pakSize(pakSize); + long totalWritten = 0; + + for(PakResourceInput i : resources) { + long written = i.write(output); + meta.resource(PakResourceMetadata.builder() + .key(i.getKey().toString()) + .type(i.getType().getCanonicalName()) + .start(totalWritten) + .length(written) + .build()); + totalWritten += written; + } + + NBTUtil.write(new NamedTag("Package " + name, NBTObjectSerializer.serialize(meta.build())), new File(folder, name + ".dat"), true); + output.close(); + } + + public PakWriter resource(PakResourceInput input) + { + resources.add(input); + return this; + } +}