mirror of
https://github.com/VolmitSoftware/Iris.git
synced 2025-07-01 23:47:21 +00:00
commit
b220b1bffa
@ -61,9 +61,6 @@ def NMS_BINDINGS = Map.of(
|
|||||||
"v1_20_R3", "1.20.4-R0.1-SNAPSHOT",
|
"v1_20_R3", "1.20.4-R0.1-SNAPSHOT",
|
||||||
"v1_20_R2", "1.20.2-R0.1-SNAPSHOT",
|
"v1_20_R2", "1.20.2-R0.1-SNAPSHOT",
|
||||||
"v1_20_R1", "1.20.1-R0.1-SNAPSHOT",
|
"v1_20_R1", "1.20.1-R0.1-SNAPSHOT",
|
||||||
"v1_19_R3", "1.19.4-R0.1-SNAPSHOT",
|
|
||||||
"v1_19_R2", "1.19.3-R0.1-SNAPSHOT",
|
|
||||||
"v1_19_R1", "1.19.2-R0.1-SNAPSHOT"
|
|
||||||
)
|
)
|
||||||
def JVM_VERSION = Map.of()
|
def JVM_VERSION = Map.of()
|
||||||
NMS_BINDINGS.each { nms ->
|
NMS_BINDINGS.each { nms ->
|
||||||
@ -131,7 +128,7 @@ allprojects {
|
|||||||
annotationProcessor 'org.projectlombok:lombok:1.18.36'
|
annotationProcessor 'org.projectlombok:lombok:1.18.36'
|
||||||
|
|
||||||
// Shaded
|
// Shaded
|
||||||
implementation 'com.dfsek:Paralithic:0.4.0'
|
implementation 'com.dfsek:paralithic:0.8.1'
|
||||||
implementation 'io.papermc:paperlib:1.0.5'
|
implementation 'io.papermc:paperlib:1.0.5'
|
||||||
implementation "net.kyori:adventure-text-minimessage:4.17.0"
|
implementation "net.kyori:adventure-text-minimessage:4.17.0"
|
||||||
implementation 'net.kyori:adventure-platform-bukkit:4.3.4'
|
implementation 'net.kyori:adventure-platform-bukkit:4.3.4'
|
||||||
|
@ -265,6 +265,17 @@ public class CommandIris implements DecreeExecutor {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
sender().sendMessage(C.GREEN + "Removing world: " + world.getName());
|
sender().sendMessage(C.GREEN + "Removing world: " + world.getName());
|
||||||
|
|
||||||
|
if (!IrisToolbelt.evacuate(world)) {
|
||||||
|
sender().sendMessage(C.RED + "Failed to evacuate world: " + world.getName());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!Bukkit.unloadWorld(world, false)) {
|
||||||
|
sender().sendMessage(C.RED + "Failed to unload world: " + world.getName());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (IrisToolbelt.removeWorld(world)) {
|
if (IrisToolbelt.removeWorld(world)) {
|
||||||
sender().sendMessage(C.GREEN + "Successfully removed " + world.getName() + " from bukkit.yml");
|
sender().sendMessage(C.GREEN + "Successfully removed " + world.getName() + " from bukkit.yml");
|
||||||
@ -277,27 +288,32 @@ public class CommandIris implements DecreeExecutor {
|
|||||||
}
|
}
|
||||||
IrisToolbelt.evacuate(world, "Deleting world");
|
IrisToolbelt.evacuate(world, "Deleting world");
|
||||||
deletingWorld = true;
|
deletingWorld = true;
|
||||||
Bukkit.unloadWorld(world, false);
|
if (!delete) {
|
||||||
int retries = 12;
|
deletingWorld = false;
|
||||||
if (delete) {
|
return;
|
||||||
|
}
|
||||||
|
VolmitSender sender = sender();
|
||||||
|
J.a(() -> {
|
||||||
|
int retries = 12;
|
||||||
|
|
||||||
if (deleteDirectory(world.getWorldFolder())) {
|
if (deleteDirectory(world.getWorldFolder())) {
|
||||||
sender().sendMessage(C.GREEN + "Successfully removed world folder");
|
sender.sendMessage(C.GREEN + "Successfully removed world folder");
|
||||||
} else {
|
} else {
|
||||||
while(true){
|
while(true){
|
||||||
if (deleteDirectory(world.getWorldFolder())){
|
if (deleteDirectory(world.getWorldFolder())){
|
||||||
sender().sendMessage(C.GREEN + "Successfully removed world folder");
|
sender.sendMessage(C.GREEN + "Successfully removed world folder");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
retries--;
|
retries--;
|
||||||
if (retries == 0){
|
if (retries == 0){
|
||||||
sender().sendMessage(C.RED + "Failed to remove world folder");
|
sender.sendMessage(C.RED + "Failed to remove world folder");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
J.sleep(3000);
|
J.sleep(3000);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
deletingWorld = false;
|
||||||
deletingWorld = false;
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean deleteDirectory(File dir) {
|
public static boolean deleteDirectory(File dir) {
|
||||||
|
@ -37,7 +37,7 @@ public class UtilsSFG {
|
|||||||
}
|
}
|
||||||
if (ServerBootSFG.unsuportedversion) {
|
if (ServerBootSFG.unsuportedversion) {
|
||||||
Iris.safeguard(C.RED + "Server Version");
|
Iris.safeguard(C.RED + "Server Version");
|
||||||
Iris.safeguard(C.RED + "- Iris only supports 1.19.2 > 1.21.3");
|
Iris.safeguard(C.RED + "- Iris only supports 1.20.1 > 1.21.4");
|
||||||
}
|
}
|
||||||
if (!ServerBootSFG.passedserversoftware) {
|
if (!ServerBootSFG.passedserversoftware) {
|
||||||
Iris.safeguard(C.YELLOW + "Unsupported Server Software");
|
Iris.safeguard(C.YELLOW + "Unsupported Server Software");
|
||||||
|
@ -90,8 +90,10 @@ public class MantleJigsawComponent extends IrisMantleComponent {
|
|||||||
private boolean placeStructures(MantleWriter writer, long seed, int x, int z, KList<IrisJigsawStructurePlacement> structures,
|
private boolean placeStructures(MantleWriter writer, long seed, int x, int z, KList<IrisJigsawStructurePlacement> structures,
|
||||||
KSet<Position2> cachedRegions, KMap<String, KSet<Position2>> cache, KMap<Position2, Double> distanceCache) {
|
KSet<Position2> cachedRegions, KMap<String, KSet<Position2>> cache, KMap<Position2, Double> distanceCache) {
|
||||||
IrisJigsawStructurePlacement i = pick(structures, seed, x, z);
|
IrisJigsawStructurePlacement i = pick(structures, seed, x, z);
|
||||||
if (i == null || checkMinDistances(i.collectMinDistances(), x, z, cachedRegions, cache, distanceCache))
|
try {
|
||||||
return false;
|
if (i == null || checkMinDistances(i.collectMinDistances(), x, z, cachedRegions, cache, distanceCache))
|
||||||
|
return false;
|
||||||
|
} catch (Throwable ignored) {}
|
||||||
RNG rng = new RNG(seed);
|
RNG rng = new RNG(seed);
|
||||||
IrisPosition position = new IrisPosition((x << 4) + rng.nextInt(15), 0, (z << 4) + rng.nextInt(15));
|
IrisPosition position = new IrisPosition((x << 4) + rng.nextInt(15), 0, (z << 4) + rng.nextInt(15));
|
||||||
IrisJigsawStructure structure = getData().getJigsawStructureLoader().load(i.getStructure());
|
IrisJigsawStructure structure = getData().getJigsawStructureLoader().load(i.getStructure());
|
||||||
@ -159,7 +161,7 @@ public class MantleJigsawComponent extends IrisMantleComponent {
|
|||||||
@ChunkCoordinates
|
@ChunkCoordinates
|
||||||
private IrisJigsawStructurePlacement pick(List<IrisJigsawStructurePlacement> structures, long seed, int x, int z) {
|
private IrisJigsawStructurePlacement pick(List<IrisJigsawStructurePlacement> structures, long seed, int x, int z) {
|
||||||
return IRare.pick(structures.stream()
|
return IRare.pick(structures.stream()
|
||||||
.filter(p -> p.shouldPlace(getDimension().getJigsawStructureDivisor(), jigsaw(), x, z))
|
.filter(p -> p.shouldPlace(getData(), getDimension().getJigsawStructureDivisor(), jigsaw(), x, z))
|
||||||
.toList(), new RNG(seed).nextDouble());
|
.toList(), new RNG(seed).nextDouble());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -24,6 +24,7 @@ import com.dfsek.paralithic.eval.parser.Scope;
|
|||||||
import com.volmit.iris.Iris;
|
import com.volmit.iris.Iris;
|
||||||
import com.volmit.iris.core.loader.IrisRegistrant;
|
import com.volmit.iris.core.loader.IrisRegistrant;
|
||||||
import com.volmit.iris.engine.data.cache.AtomicCache;
|
import com.volmit.iris.engine.data.cache.AtomicCache;
|
||||||
|
import com.volmit.iris.engine.object.IrisExpressionFunction.FunctionContext;
|
||||||
import com.volmit.iris.engine.object.annotations.ArrayType;
|
import com.volmit.iris.engine.object.annotations.ArrayType;
|
||||||
import com.volmit.iris.engine.object.annotations.Desc;
|
import com.volmit.iris.engine.object.annotations.Desc;
|
||||||
import com.volmit.iris.engine.object.annotations.Required;
|
import com.volmit.iris.engine.object.annotations.Required;
|
||||||
@ -46,12 +47,14 @@ import lombok.experimental.Accessors;
|
|||||||
@Data
|
@Data
|
||||||
@EqualsAndHashCode(callSuper = false)
|
@EqualsAndHashCode(callSuper = false)
|
||||||
public class IrisExpression extends IrisRegistrant {
|
public class IrisExpression extends IrisRegistrant {
|
||||||
private static final Parser parser = new Parser();
|
|
||||||
|
|
||||||
@ArrayType(type = IrisExpressionLoad.class, min = 1)
|
@ArrayType(type = IrisExpressionLoad.class, min = 1)
|
||||||
@Desc("Variables to use in this expression")
|
@Desc("Variables to use in this expression")
|
||||||
private KList<IrisExpressionLoad> variables = new KList<>();
|
private KList<IrisExpressionLoad> variables = new KList<>();
|
||||||
|
|
||||||
|
@ArrayType(type = IrisExpressionFunction.class, min = 1)
|
||||||
|
@Desc("Functions to use in this expression")
|
||||||
|
private KList<IrisExpressionFunction> functions = new KList<>();
|
||||||
|
|
||||||
@Required
|
@Required
|
||||||
@Desc("The expression. Inherited variables are x, y and z. Avoid using those variable names.")
|
@Desc("The expression. Inherited variables are x, y and z. Avoid using those variable names.")
|
||||||
private String expression;
|
private String expression;
|
||||||
@ -62,6 +65,7 @@ public class IrisExpression extends IrisRegistrant {
|
|||||||
private Expression expression() {
|
private Expression expression() {
|
||||||
return expressionCache.aquire(() -> {
|
return expressionCache.aquire(() -> {
|
||||||
Scope scope = new Scope(); // Create variable scope. This scope can hold both constants and invocation variables.
|
Scope scope = new Scope(); // Create variable scope. This scope can hold both constants and invocation variables.
|
||||||
|
Parser parser = new Parser();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
for (IrisExpressionLoad i : variables) {
|
for (IrisExpressionLoad i : variables) {
|
||||||
@ -76,6 +80,12 @@ public class IrisExpression extends IrisRegistrant {
|
|||||||
Iris.error("Script Variable load error in " + getLoadFile().getPath());
|
Iris.error("Script Variable load error in " + getLoadFile().getPath());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (IrisExpressionFunction f : functions) {
|
||||||
|
if (!f.isValid()) continue;
|
||||||
|
f.setData(getLoader());
|
||||||
|
parser.registerFunction(f.getName(), f);
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
return parser.parse(getExpression(), scope);
|
return parser.parse(getExpression(), scope);
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
@ -103,7 +113,7 @@ public class IrisExpression extends IrisRegistrant {
|
|||||||
g[m++] = z;
|
g[m++] = z;
|
||||||
g[m] = -1;
|
g[m] = -1;
|
||||||
|
|
||||||
return expression().evaluate(g);
|
return expression().evaluate(new FunctionContext(rng), g);
|
||||||
}
|
}
|
||||||
|
|
||||||
public double evaluate(RNG rng, double x, double y, double z) {
|
public double evaluate(RNG rng, double x, double y, double z) {
|
||||||
@ -117,7 +127,7 @@ public class IrisExpression extends IrisRegistrant {
|
|||||||
g[m++] = y;
|
g[m++] = y;
|
||||||
g[m] = z;
|
g[m] = z;
|
||||||
|
|
||||||
return expression().evaluate(g);
|
return expression().evaluate(new FunctionContext(rng), g);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -0,0 +1,101 @@
|
|||||||
|
package com.volmit.iris.engine.object;
|
||||||
|
|
||||||
|
import com.dfsek.paralithic.functions.dynamic.Context;
|
||||||
|
import com.dfsek.paralithic.functions.dynamic.DynamicFunction;
|
||||||
|
import com.dfsek.paralithic.node.Statefulness;
|
||||||
|
import com.volmit.iris.core.loader.IrisData;
|
||||||
|
import com.volmit.iris.engine.object.annotations.Desc;
|
||||||
|
import com.volmit.iris.engine.object.annotations.MinNumber;
|
||||||
|
import com.volmit.iris.engine.object.annotations.Required;
|
||||||
|
import com.volmit.iris.engine.object.annotations.Snippet;
|
||||||
|
import com.volmit.iris.util.collection.KMap;
|
||||||
|
import com.volmit.iris.util.math.RNG;
|
||||||
|
import lombok.*;
|
||||||
|
import lombok.experimental.Accessors;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
@Snippet("expression-function")
|
||||||
|
@Accessors(chain = true)
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
@Desc("Represents a function to use in your expression. Do not set the name to x, y, or z, also don't duplicate names.")
|
||||||
|
@Data
|
||||||
|
@EqualsAndHashCode(callSuper = false)
|
||||||
|
public class IrisExpressionFunction implements DynamicFunction {
|
||||||
|
@Required
|
||||||
|
@Desc("The function to assign this value to. Do not set the name to x, y, or z")
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
@Desc("If defined, this variable will use a generator style as it's value")
|
||||||
|
private IrisGeneratorStyle styleValue = null;
|
||||||
|
|
||||||
|
@Desc("If defined, iris will use an internal stream from the engine as it's value")
|
||||||
|
private IrisEngineStreamType engineStreamValue = null;
|
||||||
|
|
||||||
|
@MinNumber(2)
|
||||||
|
@Desc("Number of arguments for the function")
|
||||||
|
private int args = 2;
|
||||||
|
|
||||||
|
@Getter(AccessLevel.NONE)
|
||||||
|
@Setter(AccessLevel.NONE)
|
||||||
|
private transient final KMap<FunctionContext, Provider> cache = new KMap<>();
|
||||||
|
private transient IrisData data;
|
||||||
|
|
||||||
|
public boolean isValid() {
|
||||||
|
return styleValue != null || engineStreamValue != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getArgNumber() {
|
||||||
|
if (engineStreamValue != null) return 2;
|
||||||
|
return Math.max(args, 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Override
|
||||||
|
public Statefulness statefulness() {
|
||||||
|
return Statefulness.STATEFUL;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public double eval(double... doubles) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public double eval(@Nullable Context raw, double... args) {
|
||||||
|
return cache.computeIfAbsent((FunctionContext) raw, context -> {
|
||||||
|
assert context != null;
|
||||||
|
if (engineStreamValue != null) {
|
||||||
|
var stream = engineStreamValue.get(data.getEngine());
|
||||||
|
return d -> stream.get(d[0], d[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (styleValue != null) {
|
||||||
|
return styleValue.createNoCache(context.rng, data)::noise;
|
||||||
|
}
|
||||||
|
|
||||||
|
return d -> Double.NaN;
|
||||||
|
}).eval(args);
|
||||||
|
}
|
||||||
|
|
||||||
|
public record FunctionContext(@NonNull RNG rng) implements Context {
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if (o == null || getClass() != o.getClass()) return false;
|
||||||
|
FunctionContext that = (FunctionContext) o;
|
||||||
|
return rng.getSeed() == that.rng.getSeed();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return Long.hashCode(rng.getSeed());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@FunctionalInterface
|
||||||
|
private interface Provider {
|
||||||
|
double eval(double... args);
|
||||||
|
}
|
||||||
|
}
|
@ -23,12 +23,11 @@ import com.volmit.iris.engine.data.cache.AtomicCache;
|
|||||||
import com.volmit.iris.engine.object.annotations.Desc;
|
import com.volmit.iris.engine.object.annotations.Desc;
|
||||||
import com.volmit.iris.engine.object.annotations.Required;
|
import com.volmit.iris.engine.object.annotations.Required;
|
||||||
import com.volmit.iris.engine.object.annotations.Snippet;
|
import com.volmit.iris.engine.object.annotations.Snippet;
|
||||||
|
import com.volmit.iris.util.collection.KMap;
|
||||||
import com.volmit.iris.util.math.RNG;
|
import com.volmit.iris.util.math.RNG;
|
||||||
|
import com.volmit.iris.util.noise.CNG;
|
||||||
import com.volmit.iris.util.stream.ProceduralStream;
|
import com.volmit.iris.util.stream.ProceduralStream;
|
||||||
import lombok.AllArgsConstructor;
|
import lombok.*;
|
||||||
import lombok.Data;
|
|
||||||
import lombok.EqualsAndHashCode;
|
|
||||||
import lombok.NoArgsConstructor;
|
|
||||||
import lombok.experimental.Accessors;
|
import lombok.experimental.Accessors;
|
||||||
|
|
||||||
@Snippet("expression-load")
|
@Snippet("expression-load")
|
||||||
@ -57,6 +56,9 @@ public class IrisExpressionLoad {
|
|||||||
|
|
||||||
private transient AtomicCache<ProceduralStream<Double>> streamCache = new AtomicCache<>();
|
private transient AtomicCache<ProceduralStream<Double>> streamCache = new AtomicCache<>();
|
||||||
private transient AtomicCache<Double> valueCache = new AtomicCache<>();
|
private transient AtomicCache<Double> valueCache = new AtomicCache<>();
|
||||||
|
@Getter(AccessLevel.NONE)
|
||||||
|
@Setter(AccessLevel.NONE)
|
||||||
|
private transient final KMap<Long, CNG> styleCache = new KMap<>();
|
||||||
|
|
||||||
public double getValue(RNG rng, IrisData data, double x, double z) {
|
public double getValue(RNG rng, IrisData data, double x, double z) {
|
||||||
if (engineValue != null) {
|
if (engineValue != null) {
|
||||||
@ -68,7 +70,8 @@ public class IrisExpressionLoad {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (styleValue != null) {
|
if (styleValue != null) {
|
||||||
return styleValue.create(rng, data).noise(x, z);
|
return styleCache.computeIfAbsent(rng.getSeed(), k -> styleValue.createNoCache(new RNG(k), data))
|
||||||
|
.noise(x, z);
|
||||||
}
|
}
|
||||||
|
|
||||||
return staticValue;
|
return staticValue;
|
||||||
@ -84,7 +87,8 @@ public class IrisExpressionLoad {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (styleValue != null) {
|
if (styleValue != null) {
|
||||||
return styleValue.create(rng, data).noise(x, y, z);
|
return styleCache.computeIfAbsent(rng.getSeed(), k -> styleValue.createNoCache(new RNG(k), data))
|
||||||
|
.noise(x, y, z);
|
||||||
}
|
}
|
||||||
|
|
||||||
return staticValue;
|
return staticValue;
|
||||||
|
@ -19,13 +19,8 @@
|
|||||||
package com.volmit.iris.engine.object;
|
package com.volmit.iris.engine.object;
|
||||||
|
|
||||||
import com.volmit.iris.Iris;
|
import com.volmit.iris.Iris;
|
||||||
import com.volmit.iris.engine.object.annotations.ArrayType;
|
import com.volmit.iris.core.loader.IrisData;
|
||||||
import com.volmit.iris.engine.object.annotations.Desc;
|
import com.volmit.iris.engine.object.annotations.*;
|
||||||
import com.volmit.iris.engine.object.annotations.MaxNumber;
|
|
||||||
import com.volmit.iris.engine.object.annotations.MinNumber;
|
|
||||||
import com.volmit.iris.engine.object.annotations.RegistryListResource;
|
|
||||||
import com.volmit.iris.engine.object.annotations.Required;
|
|
||||||
import com.volmit.iris.engine.object.annotations.Snippet;
|
|
||||||
import com.volmit.iris.util.collection.KList;
|
import com.volmit.iris.util.collection.KList;
|
||||||
import com.volmit.iris.util.collection.KMap;
|
import com.volmit.iris.util.collection.KMap;
|
||||||
import com.volmit.iris.util.documentation.ChunkCoordinates;
|
import com.volmit.iris.util.documentation.ChunkCoordinates;
|
||||||
@ -54,6 +49,7 @@ public class IrisJigsawStructurePlacement implements IRare {
|
|||||||
private int rarity = 100;
|
private int rarity = 100;
|
||||||
|
|
||||||
@Required
|
@Required
|
||||||
|
@DependsOn({"spacing", "separation"})
|
||||||
@Desc("The salt to use when generating the structure (to differentiate structures)")
|
@Desc("The salt to use when generating the structure (to differentiate structures)")
|
||||||
@MinNumber(Long.MIN_VALUE)
|
@MinNumber(Long.MIN_VALUE)
|
||||||
@MaxNumber(Long.MAX_VALUE)
|
@MaxNumber(Long.MAX_VALUE)
|
||||||
@ -61,16 +57,26 @@ public class IrisJigsawStructurePlacement implements IRare {
|
|||||||
|
|
||||||
@Required
|
@Required
|
||||||
@MinNumber(0)
|
@MinNumber(0)
|
||||||
|
@DependsOn({"salt", "separation"})
|
||||||
@Desc("Average distance in chunks between two neighboring generation attempts")
|
@Desc("Average distance in chunks between two neighboring generation attempts")
|
||||||
private int spacing = -1;
|
private int spacing = -1;
|
||||||
|
|
||||||
@Required
|
@Required
|
||||||
@MinNumber(0)
|
@MinNumber(0)
|
||||||
|
@DependsOn({"salt", "spacing"})
|
||||||
@Desc("Minimum distance in chunks between two neighboring generation attempts\nThe maximum distance of two neighboring generation attempts is 2*spacing - separation")
|
@Desc("Minimum distance in chunks between two neighboring generation attempts\nThe maximum distance of two neighboring generation attempts is 2*spacing - separation")
|
||||||
private int separation = -1;
|
private int separation = -1;
|
||||||
|
|
||||||
@Desc("The method used to spread the structure")
|
@Desc("The method used to spread the structure")
|
||||||
private SpreadType spreadType = SpreadType.TRIANGULAR;
|
private SpreadType spreadType = SpreadType.LINEAR;
|
||||||
|
|
||||||
|
@DependsOn({"spreadType"})
|
||||||
|
@Desc("The noise style to use when spreadType is set to 'NOISE'\nThis ignores the spacing and separation parameters")
|
||||||
|
private IrisGeneratorStyle style = new IrisGeneratorStyle();
|
||||||
|
|
||||||
|
@DependsOn({"spreadType", "style"})
|
||||||
|
@Desc("Threshold for noise style")
|
||||||
|
private double threshold = 0.5;
|
||||||
|
|
||||||
@ArrayType(type = IrisJigsawMinDistance.class)
|
@ArrayType(type = IrisJigsawMinDistance.class)
|
||||||
@Desc("List of minimum distances to check for")
|
@Desc("List of minimum distances to check for")
|
||||||
@ -89,20 +95,29 @@ public class IrisJigsawStructurePlacement implements IRare {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void calculateMissing(double divisor, long seed) {
|
private void calculateMissing(double divisor, long seed) {
|
||||||
seed = seed + hashCode();
|
if (salt != 0 && separation > 0 && spacing > 0)
|
||||||
|
return;
|
||||||
|
seed *= (long) structure.hashCode() * rarity;
|
||||||
if (salt == 0) {
|
if (salt == 0) {
|
||||||
salt = new RNG(seed).nextLong(Integer.MIN_VALUE, Integer.MAX_VALUE);
|
salt = new RNG(seed).l(Integer.MIN_VALUE, Integer.MAX_VALUE);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (separation == -1 || spacing == -1) {
|
if (separation == -1 || spacing == -1) {
|
||||||
separation = (int) Math.round(rarity / divisor);
|
separation = (int) Math.round(rarity / divisor);
|
||||||
spacing = new RNG(seed).nextInt(separation, separation * 2);
|
spacing = new RNG(seed).i(separation, separation * 2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ChunkCoordinates
|
@ChunkCoordinates
|
||||||
public boolean shouldPlace(double divisor, long seed, int x, int z) {
|
public boolean shouldPlace(IrisData data, double divisor, long seed, int x, int z) {
|
||||||
calculateMissing(divisor, seed);
|
calculateMissing(divisor, seed);
|
||||||
|
if (spreadType != SpreadType.NOISE)
|
||||||
|
return shouldPlaceSpread(seed, x, z);
|
||||||
|
|
||||||
|
return style.create(new RNG(seed + salt), data).noise(x, z) > threshold;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean shouldPlaceSpread(long seed, int x, int z) {
|
||||||
if (separation > spacing) {
|
if (separation > spacing) {
|
||||||
separation = spacing;
|
separation = spacing;
|
||||||
Iris.warn("JigsawStructurePlacement: separation must be less than or equal to spacing");
|
Iris.warn("JigsawStructurePlacement: separation must be less than or equal to spacing");
|
||||||
@ -123,7 +138,9 @@ public class IrisJigsawStructurePlacement implements IRare {
|
|||||||
@Desc("Linear spread")
|
@Desc("Linear spread")
|
||||||
LINEAR(RNG::i),
|
LINEAR(RNG::i),
|
||||||
@Desc("Triangular spread")
|
@Desc("Triangular spread")
|
||||||
TRIANGULAR((rng, bound) -> (rng.i(bound) + rng.i(bound)) / 2);
|
TRIANGULAR((rng, bound) -> (rng.i(bound) + rng.i(bound)) / 2),
|
||||||
|
@Desc("Noise based spread\nThis ignores the spacing and separation parameters")
|
||||||
|
NOISE((rng, bound) -> 0);
|
||||||
private final SpreadMethod method;
|
private final SpreadMethod method;
|
||||||
|
|
||||||
SpreadType(SpreadMethod method) {
|
SpreadType(SpreadMethod method) {
|
||||||
|
@ -35,7 +35,6 @@ import java.io.IOException;
|
|||||||
|
|
||||||
@SuppressWarnings("ALL")
|
@SuppressWarnings("ALL")
|
||||||
@Getter
|
@Getter
|
||||||
@ToString
|
|
||||||
@EqualsAndHashCode
|
@EqualsAndHashCode
|
||||||
@AllArgsConstructor
|
@AllArgsConstructor
|
||||||
@NoArgsConstructor(access = AccessLevel.PROTECTED)
|
@NoArgsConstructor(access = AccessLevel.PROTECTED)
|
||||||
@ -137,4 +136,9 @@ public class TileData implements Cloneable {
|
|||||||
clone.properties = properties.copy(); //TODO make a deep copy
|
clone.properties = properties.copy(); //TODO make a deep copy
|
||||||
return clone;
|
return clone;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return material.getKey() + gson.toJson(properties);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,15 +1,18 @@
|
|||||||
package com.volmit.iris.util.data.registry;
|
package com.volmit.iris.util.data.registry;
|
||||||
|
|
||||||
import com.volmit.iris.core.nms.container.Pair;
|
import com.volmit.iris.core.nms.container.Pair;
|
||||||
|
import com.volmit.iris.engine.data.cache.AtomicCache;
|
||||||
import lombok.NonNull;
|
import lombok.NonNull;
|
||||||
import org.bukkit.Bukkit;
|
import org.bukkit.Bukkit;
|
||||||
import org.bukkit.Keyed;
|
import org.bukkit.Keyed;
|
||||||
import org.bukkit.NamespacedKey;
|
import org.bukkit.NamespacedKey;
|
||||||
import org.bukkit.Registry;
|
import org.bukkit.Registry;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
import java.lang.reflect.Modifier;
|
import java.lang.reflect.Modifier;
|
||||||
import java.lang.reflect.ParameterizedType;
|
import java.lang.reflect.ParameterizedType;
|
||||||
|
import java.lang.reflect.Type;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
@ -20,6 +23,7 @@ import java.util.stream.Collectors;
|
|||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
public class RegistryUtil {
|
public class RegistryUtil {
|
||||||
|
private static final AtomicCache<RegistryLookup> registryLookup = new AtomicCache<>();
|
||||||
private static final Map<Class<?>, Map<NamespacedKey, Keyed>> KEYED_REGISTRY = new HashMap<>();
|
private static final Map<Class<?>, Map<NamespacedKey, Keyed>> KEYED_REGISTRY = new HashMap<>();
|
||||||
private static final Map<Class<?>, Map<NamespacedKey, Object>> ENUM_REGISTRY = new HashMap<>();
|
private static final Map<Class<?>, Map<NamespacedKey, Object>> ENUM_REGISTRY = new HashMap<>();
|
||||||
private static final Map<Class<?>, Registry<Keyed>> REGISTRY = new HashMap<>();
|
private static final Map<Class<?>, Registry<Keyed>> REGISTRY = new HashMap<>();
|
||||||
@ -43,7 +47,7 @@ public class RegistryUtil {
|
|||||||
if (keys.length == 0) throw new IllegalArgumentException("Need at least one key");
|
if (keys.length == 0) throw new IllegalArgumentException("Need at least one key");
|
||||||
Registry<Keyed> registry = null;
|
Registry<Keyed> registry = null;
|
||||||
if (Keyed.class.isAssignableFrom(typeClass)) {
|
if (Keyed.class.isAssignableFrom(typeClass)) {
|
||||||
registry = Bukkit.getRegistry(typeClass.asSubclass(Keyed.class));
|
registry = getRegistry(typeClass.asSubclass(Keyed.class));
|
||||||
}
|
}
|
||||||
if (registry == null) {
|
if (registry == null) {
|
||||||
registry = REGISTRY.computeIfAbsent(typeClass, t -> Arrays.stream(Registry.class.getDeclaredFields())
|
registry = REGISTRY.computeIfAbsent(typeClass, t -> Arrays.stream(Registry.class.getDeclaredFields())
|
||||||
@ -150,4 +154,56 @@ public class RegistryUtil {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
private static <T extends Keyed> Registry<T> getRegistry(@NotNull Class<T> type) {
|
||||||
|
RegistryLookup lookup = registryLookup.aquire(() -> {
|
||||||
|
RegistryLookup bukkit;
|
||||||
|
try {
|
||||||
|
bukkit = Bukkit::getRegistry;
|
||||||
|
} catch (Throwable ignored) {
|
||||||
|
bukkit = null;
|
||||||
|
}
|
||||||
|
return new DefaultRegistryLookup(bukkit);
|
||||||
|
});
|
||||||
|
return lookup.find(type);
|
||||||
|
}
|
||||||
|
|
||||||
|
private interface RegistryLookup {
|
||||||
|
@Nullable
|
||||||
|
<T extends Keyed> Registry<T> find(@NonNull Class<T> type);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class DefaultRegistryLookup implements RegistryLookup {
|
||||||
|
private final RegistryLookup bukkit;
|
||||||
|
private final Map<Type, Object> registries;
|
||||||
|
|
||||||
|
private DefaultRegistryLookup(RegistryLookup bukkit) {
|
||||||
|
this.bukkit = bukkit;
|
||||||
|
registries = Arrays.stream(Registry.class.getDeclaredFields())
|
||||||
|
.filter(field -> Modifier.isPublic(field.getModifiers()) && Modifier.isStatic(field.getModifiers()))
|
||||||
|
.filter(field -> Registry.class.isAssignableFrom(field.getType()))
|
||||||
|
.map(field -> {
|
||||||
|
var type = ((ParameterizedType) field.getGenericType()).getActualTypeArguments()[0];
|
||||||
|
try {
|
||||||
|
return new Pair<>(type, field.get(null));
|
||||||
|
} catch (Throwable e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.filter(Objects::nonNull)
|
||||||
|
.collect(Collectors.toMap(Pair::getA, Pair::getB, (a, b) -> a));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public <T extends Keyed> Registry<T> find(@NonNull Class<T> type) {
|
||||||
|
if (bukkit == null) return (Registry<T>) registries.get(type);
|
||||||
|
try {
|
||||||
|
return bukkit.find(type);
|
||||||
|
} catch (Throwable e) {
|
||||||
|
return (Registry<T>) registries.get(type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,150 +0,0 @@
|
|||||||
package com.volmit.iris.core.nms.v1_19_R1;
|
|
||||||
|
|
||||||
import com.mojang.serialization.Codec;
|
|
||||||
import com.volmit.iris.Iris;
|
|
||||||
import com.volmit.iris.engine.data.cache.AtomicCache;
|
|
||||||
import com.volmit.iris.engine.framework.Engine;
|
|
||||||
import com.volmit.iris.engine.object.IrisBiome;
|
|
||||||
import com.volmit.iris.engine.object.IrisBiomeCustom;
|
|
||||||
import com.volmit.iris.util.collection.KMap;
|
|
||||||
import com.volmit.iris.util.math.RNG;
|
|
||||||
import net.minecraft.core.Holder;
|
|
||||||
import net.minecraft.core.Registry;
|
|
||||||
import net.minecraft.core.RegistryAccess;
|
|
||||||
import net.minecraft.resources.ResourceLocation;
|
|
||||||
import net.minecraft.world.level.biome.Biome;
|
|
||||||
import net.minecraft.world.level.biome.BiomeSource;
|
|
||||||
import net.minecraft.world.level.biome.Climate;
|
|
||||||
import org.bukkit.Bukkit;
|
|
||||||
import org.bukkit.World;
|
|
||||||
import org.bukkit.craftbukkit.v1_19_R1.CraftServer;
|
|
||||||
import org.bukkit.craftbukkit.v1_19_R1.CraftWorld;
|
|
||||||
import org.bukkit.craftbukkit.v1_19_R1.block.CraftBlock;
|
|
||||||
|
|
||||||
import java.lang.reflect.Field;
|
|
||||||
import java.lang.reflect.Method;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public class CustomBiomeSource extends BiomeSource {
|
|
||||||
private final long seed;
|
|
||||||
private final Engine engine;
|
|
||||||
private final Registry<Biome> biomeCustomRegistry;
|
|
||||||
private final Registry<Biome> biomeRegistry;
|
|
||||||
private final AtomicCache<RegistryAccess> registryAccess = new AtomicCache<>();
|
|
||||||
private final RNG rng;
|
|
||||||
private final KMap<String, Holder<Biome>> customBiomes;
|
|
||||||
|
|
||||||
public CustomBiomeSource(long seed, Engine engine, World world) {
|
|
||||||
super(getAllBiomes(
|
|
||||||
((RegistryAccess) getFor(RegistryAccess.Frozen.class, ((CraftServer) Bukkit.getServer()).getHandle().getServer()))
|
|
||||||
.registry(Registry.BIOME_REGISTRY).orElse(null),
|
|
||||||
((CraftWorld) world).getHandle().registryAccess().registry(Registry.BIOME_REGISTRY).orElse(null),
|
|
||||||
engine));
|
|
||||||
this.engine = engine;
|
|
||||||
this.seed = seed;
|
|
||||||
this.biomeCustomRegistry = registry().registry(Registry.BIOME_REGISTRY).orElse(null);
|
|
||||||
this.biomeRegistry = ((CraftWorld) world).getHandle().registryAccess().registry(Registry.BIOME_REGISTRY).orElse(null);
|
|
||||||
this.rng = new RNG(engine.getSeedManager().getBiome());
|
|
||||||
this.customBiomes = fillCustomBiomes(biomeCustomRegistry, engine);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static List<Holder<Biome>> getAllBiomes(Registry<Biome> customRegistry, Registry<Biome> registry, Engine engine) {
|
|
||||||
List<Holder<Biome>> b = new ArrayList<>();
|
|
||||||
|
|
||||||
for (IrisBiome i : engine.getAllBiomes()) {
|
|
||||||
if (i.isCustom()) {
|
|
||||||
for (IrisBiomeCustom j : i.getCustomDerivitives()) {
|
|
||||||
b.add(customRegistry.getHolder(customRegistry.getResourceKey(customRegistry
|
|
||||||
.get(new ResourceLocation(engine.getDimension().getLoadKey() + ":" + j.getId()))).get()).get());
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
b.add(CraftBlock.biomeToBiomeBase(registry, i.getVanillaDerivative()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return b;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Object getFor(Class<?> type, Object source) {
|
|
||||||
Object o = fieldFor(type, source);
|
|
||||||
|
|
||||||
if (o != null) {
|
|
||||||
return o;
|
|
||||||
}
|
|
||||||
|
|
||||||
return invokeFor(type, source);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Object fieldFor(Class<?> returns, Object in) {
|
|
||||||
return fieldForClass(returns, in.getClass(), in);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Object invokeFor(Class<?> returns, Object in) {
|
|
||||||
for (Method i : in.getClass().getMethods()) {
|
|
||||||
if (i.getReturnType().equals(returns)) {
|
|
||||||
i.setAccessible(true);
|
|
||||||
try {
|
|
||||||
Iris.debug("[NMS] Found " + returns.getSimpleName() + " in " + in.getClass().getSimpleName() + "." + i.getName() + "()");
|
|
||||||
return i.invoke(in);
|
|
||||||
} catch (Throwable e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
private static <T> T fieldForClass(Class<T> returnType, Class<?> sourceType, Object in) {
|
|
||||||
for (Field i : sourceType.getDeclaredFields()) {
|
|
||||||
if (i.getType().equals(returnType)) {
|
|
||||||
i.setAccessible(true);
|
|
||||||
try {
|
|
||||||
Iris.debug("[NMS] Found " + returnType.getSimpleName() + " in " + sourceType.getSimpleName() + "." + i.getName());
|
|
||||||
return (T) i.get(in);
|
|
||||||
} catch (IllegalAccessException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private KMap<String, Holder<Biome>> fillCustomBiomes(Registry<Biome> customRegistry, Engine engine) {
|
|
||||||
KMap<String, Holder<Biome>> m = new KMap<>();
|
|
||||||
|
|
||||||
for (IrisBiome i : engine.getAllBiomes()) {
|
|
||||||
if (i.isCustom()) {
|
|
||||||
for (IrisBiomeCustom j : i.getCustomDerivitives()) {
|
|
||||||
m.put(j.getId(), customRegistry.getHolder(customRegistry.getResourceKey(customRegistry
|
|
||||||
.get(new ResourceLocation(engine.getDimension().getLoadKey() + ":" + j.getId()))).get()).get());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return m;
|
|
||||||
}
|
|
||||||
|
|
||||||
private RegistryAccess registry() {
|
|
||||||
return registryAccess.aquire(() -> (RegistryAccess) getFor(RegistryAccess.Frozen.class, ((CraftServer) Bukkit.getServer()).getHandle().getServer()));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected Codec<? extends BiomeSource> codec() {
|
|
||||||
throw new UnsupportedOperationException("Not supported");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Holder<Biome> getNoiseBiome(int x, int y, int z, Climate.Sampler sampler) {
|
|
||||||
int m = (y - engine.getMinHeight()) << 2;
|
|
||||||
IrisBiome ib = engine.getComplex().getTrueBiomeStream().get(x << 2, z << 2);
|
|
||||||
if (ib.isCustom()) {
|
|
||||||
return customBiomes.get(ib.getCustomBiome(rng, x << 2, m, z << 2).getId());
|
|
||||||
} else {
|
|
||||||
org.bukkit.block.Biome v = ib.getSkyBiome(rng, x << 2, m, z << 2);
|
|
||||||
return CraftBlock.biomeToBiomeBase(biomeRegistry, v);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,257 +0,0 @@
|
|||||||
package com.volmit.iris.core.nms.v1_19_R1;
|
|
||||||
|
|
||||||
import com.mojang.datafixers.util.Pair;
|
|
||||||
import com.mojang.serialization.Codec;
|
|
||||||
import com.volmit.iris.Iris;
|
|
||||||
import com.volmit.iris.engine.framework.Engine;
|
|
||||||
import com.volmit.iris.engine.framework.ResultLocator;
|
|
||||||
import com.volmit.iris.engine.framework.WrongEngineBroException;
|
|
||||||
import com.volmit.iris.engine.object.IrisJigsawStructure;
|
|
||||||
import com.volmit.iris.engine.object.IrisJigsawStructurePlacement;
|
|
||||||
import com.volmit.iris.util.collection.KList;
|
|
||||||
import com.volmit.iris.util.collection.KMap;
|
|
||||||
import com.volmit.iris.util.collection.KSet;
|
|
||||||
import com.volmit.iris.util.mantle.MantleFlag;
|
|
||||||
import com.volmit.iris.util.math.Position2;
|
|
||||||
import com.volmit.iris.util.reflect.WrappedField;
|
|
||||||
import net.minecraft.core.*;
|
|
||||||
import net.minecraft.resources.ResourceKey;
|
|
||||||
import net.minecraft.resources.ResourceLocation;
|
|
||||||
import net.minecraft.server.level.ServerLevel;
|
|
||||||
import net.minecraft.server.level.WorldGenRegion;
|
|
||||||
import net.minecraft.tags.TagKey;
|
|
||||||
import net.minecraft.util.random.WeightedRandomList;
|
|
||||||
import net.minecraft.world.entity.MobCategory;
|
|
||||||
import net.minecraft.world.level.LevelHeightAccessor;
|
|
||||||
import net.minecraft.world.level.NoiseColumn;
|
|
||||||
import net.minecraft.world.level.StructureManager;
|
|
||||||
import net.minecraft.world.level.WorldGenLevel;
|
|
||||||
import net.minecraft.world.level.biome.Biome;
|
|
||||||
import net.minecraft.world.level.biome.BiomeManager;
|
|
||||||
import net.minecraft.world.level.biome.BiomeSource;
|
|
||||||
import net.minecraft.world.level.biome.MobSpawnSettings;
|
|
||||||
import net.minecraft.world.level.chunk.ChunkAccess;
|
|
||||||
import net.minecraft.world.level.chunk.ChunkGenerator;
|
|
||||||
import net.minecraft.world.level.levelgen.GenerationStep;
|
|
||||||
import net.minecraft.world.level.levelgen.Heightmap;
|
|
||||||
import net.minecraft.world.level.levelgen.RandomState;
|
|
||||||
import net.minecraft.world.level.levelgen.blending.Blender;
|
|
||||||
import net.minecraft.world.level.levelgen.structure.Structure;
|
|
||||||
import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplateManager;
|
|
||||||
import org.bukkit.World;
|
|
||||||
import org.bukkit.craftbukkit.v1_19_R1.CraftWorld;
|
|
||||||
import org.bukkit.craftbukkit.v1_19_R1.generator.CustomChunkGenerator;
|
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
|
||||||
import java.lang.reflect.Field;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Objects;
|
|
||||||
import java.util.concurrent.CompletableFuture;
|
|
||||||
import java.util.concurrent.ExecutionException;
|
|
||||||
import java.util.concurrent.Executor;
|
|
||||||
|
|
||||||
public class IrisChunkGenerator extends CustomChunkGenerator {
|
|
||||||
private static final WrappedField<ChunkGenerator, BiomeSource> BIOME_SOURCE;
|
|
||||||
private final ChunkGenerator delegate;
|
|
||||||
private final Engine engine;
|
|
||||||
private final KMap<ResourceKey<Structure>, KSet<String>> structures = new KMap<>();
|
|
||||||
|
|
||||||
public IrisChunkGenerator(ChunkGenerator delegate, long seed, Engine engine, World world) {
|
|
||||||
super(((CraftWorld) world).getHandle(), edit(delegate, new CustomBiomeSource(seed, engine, world)), null);
|
|
||||||
this.delegate = delegate;
|
|
||||||
this.engine = engine;
|
|
||||||
var dimension = engine.getDimension();
|
|
||||||
|
|
||||||
KSet<IrisJigsawStructure> placements = new KSet<>();
|
|
||||||
addAll(dimension.getJigsawStructures(), placements);
|
|
||||||
for (var region : dimension.getAllRegions(engine)) {
|
|
||||||
addAll(region.getJigsawStructures(), placements);
|
|
||||||
for (var biome : region.getAllBiomes(engine))
|
|
||||||
addAll(biome.getJigsawStructures(), placements);
|
|
||||||
}
|
|
||||||
var stronghold = dimension.getStronghold();
|
|
||||||
if (stronghold != null)
|
|
||||||
placements.add(engine.getData().getJigsawStructureLoader().load(stronghold));
|
|
||||||
placements.removeIf(Objects::isNull);
|
|
||||||
|
|
||||||
var registry = ((CraftWorld) world).getHandle().registryAccess().registry(Registry.STRUCTURE_REGISTRY).orElseThrow();
|
|
||||||
for (var s : placements) {
|
|
||||||
try {
|
|
||||||
String raw = s.getStructureKey();
|
|
||||||
if (raw == null) continue;
|
|
||||||
boolean tag = raw.startsWith("#");
|
|
||||||
if (tag) raw = raw.substring(1);
|
|
||||||
|
|
||||||
var location = new ResourceLocation(raw);
|
|
||||||
if (!tag) {
|
|
||||||
structures.computeIfAbsent(ResourceKey.create(Registry.STRUCTURE_REGISTRY, location), k -> new KSet<>()).add(s.getLoadKey());
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
var key = TagKey.create(Registry.STRUCTURE_REGISTRY, location);
|
|
||||||
var set = registry.getTag(key).orElse(null);
|
|
||||||
if (set == null) {
|
|
||||||
Iris.error("Could not find structure tag: " + raw);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
for (var holder : set) {
|
|
||||||
var resourceKey = holder.unwrapKey().orElse(null);
|
|
||||||
if (resourceKey == null) continue;
|
|
||||||
structures.computeIfAbsent(resourceKey, k -> new KSet<>()).add(s.getLoadKey());
|
|
||||||
}
|
|
||||||
} catch (Throwable e) {
|
|
||||||
Iris.error("Failed to load structure: " + s.getLoadKey());
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void addAll(KList<IrisJigsawStructurePlacement> placements, KSet<IrisJigsawStructure> structures) {
|
|
||||||
if (placements == null) return;
|
|
||||||
placements.stream()
|
|
||||||
.map(IrisJigsawStructurePlacement::getStructure)
|
|
||||||
.map(engine.getData().getJigsawStructureLoader()::load)
|
|
||||||
.filter(Objects::nonNull)
|
|
||||||
.forEach(structures::add);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public @Nullable Pair<BlockPos, Holder<Structure>> findNearestMapStructure(ServerLevel level, HolderSet<Structure> holders, BlockPos pos, int radius, boolean findUnexplored) {
|
|
||||||
if (engine.getDimension().isDisableExplorerMaps())
|
|
||||||
return null;
|
|
||||||
|
|
||||||
KMap<String, Holder<Structure>> structures = new KMap<>();
|
|
||||||
for (var holder : holders) {
|
|
||||||
if (holder == null) continue;
|
|
||||||
var key = holder.unwrapKey().orElse(null);
|
|
||||||
var set = this.structures.get(key);
|
|
||||||
if (set == null) continue;
|
|
||||||
for (var structure : set) {
|
|
||||||
structures.put(structure, holder);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (structures.isEmpty())
|
|
||||||
return null;
|
|
||||||
|
|
||||||
var locator = ResultLocator.locateStructure(structures.keySet())
|
|
||||||
.then((e, p , s) -> structures.get(s.getLoadKey()));
|
|
||||||
if (findUnexplored)
|
|
||||||
locator = locator.then((e, p, s) -> e.getMantle().getMantle().getChunk(p.getX(), p.getZ()).isFlagged(MantleFlag.DISCOVERED) ? null : s);
|
|
||||||
|
|
||||||
try {
|
|
||||||
var result = locator.find(engine, new Position2(pos.getX() >> 4, pos.getZ() >> 4), radius * 10L, i -> {}, false).get();
|
|
||||||
if (result == null) return null;
|
|
||||||
var blockPos = new BlockPos(result.getBlockX(), 0, result.getBlockZ());
|
|
||||||
return Pair.of(blockPos, result.obj());
|
|
||||||
} catch (WrongEngineBroException | ExecutionException | InterruptedException e) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected Codec<? extends ChunkGenerator> codec() {
|
|
||||||
return Codec.unit(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public ChunkGenerator getDelegate() {
|
|
||||||
if (delegate instanceof CustomChunkGenerator chunkGenerator)
|
|
||||||
return chunkGenerator.getDelegate();
|
|
||||||
return delegate;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getMinY() {
|
|
||||||
return delegate.getMinY();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getSeaLevel() {
|
|
||||||
return delegate.getSeaLevel();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void createStructures(RegistryAccess iregistrycustom, RandomState randomstate, StructureManager structuremanager, ChunkAccess ichunkaccess, StructureTemplateManager structuretemplatemanager, long i) {
|
|
||||||
delegate.createStructures(iregistrycustom, randomstate, structuremanager, ichunkaccess, structuretemplatemanager, i);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void buildSurface(WorldGenRegion regionlimitedworldaccess, StructureManager structuremanager, RandomState randomstate, ChunkAccess ichunkaccess) {
|
|
||||||
delegate.buildSurface(regionlimitedworldaccess, structuremanager, randomstate, ichunkaccess);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void applyCarvers(WorldGenRegion regionlimitedworldaccess, long seed, RandomState randomstate, BiomeManager biomemanager, StructureManager structuremanager, ChunkAccess ichunkaccess, GenerationStep.Carving worldgenstage_features) {
|
|
||||||
delegate.applyCarvers(regionlimitedworldaccess, seed, randomstate, biomemanager, structuremanager, ichunkaccess, worldgenstage_features);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public CompletableFuture<ChunkAccess> fillFromNoise(Executor executor, Blender blender, RandomState randomstate, StructureManager structuremanager, ChunkAccess ichunkaccess) {
|
|
||||||
return delegate.fillFromNoise(executor, blender, randomstate, structuremanager, ichunkaccess);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getBaseHeight(int i, int j, Heightmap.Types heightmap_type, LevelHeightAccessor levelheightaccessor, RandomState randomstate) {
|
|
||||||
return delegate.getBaseHeight(i, j, heightmap_type, levelheightaccessor, randomstate);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public WeightedRandomList<MobSpawnSettings.SpawnerData> getMobsAt(Holder<Biome> holder, StructureManager structuremanager, MobCategory enumcreaturetype, BlockPos blockposition) {
|
|
||||||
return delegate.getMobsAt(holder, structuremanager, enumcreaturetype, blockposition);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void applyBiomeDecoration(WorldGenLevel generatoraccessseed, ChunkAccess ichunkaccess, StructureManager structuremanager) {
|
|
||||||
delegate.applyBiomeDecoration(generatoraccessseed, ichunkaccess, structuremanager);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void addDebugScreenInfo(List<String> list, RandomState randomstate, BlockPos blockposition) {
|
|
||||||
delegate.addDebugScreenInfo(list, randomstate, blockposition);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void spawnOriginalMobs(WorldGenRegion regionlimitedworldaccess) {
|
|
||||||
delegate.spawnOriginalMobs(regionlimitedworldaccess);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getSpawnHeight(LevelHeightAccessor levelheightaccessor) {
|
|
||||||
return delegate.getSpawnHeight(levelheightaccessor);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getGenDepth() {
|
|
||||||
return delegate.getGenDepth();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public NoiseColumn getBaseColumn(int i, int j, LevelHeightAccessor levelheightaccessor, RandomState randomstate) {
|
|
||||||
return delegate.getBaseColumn(i, j, levelheightaccessor, randomstate);
|
|
||||||
}
|
|
||||||
|
|
||||||
static {
|
|
||||||
Field biomeSource = null;
|
|
||||||
for (Field field : ChunkGenerator.class.getDeclaredFields()) {
|
|
||||||
if (!field.getType().equals(BiomeSource.class))
|
|
||||||
continue;
|
|
||||||
biomeSource = field;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (biomeSource == null)
|
|
||||||
throw new RuntimeException("Could not find biomeSource field in ChunkGenerator!");
|
|
||||||
BIOME_SOURCE = new WrappedField<>(ChunkGenerator.class, biomeSource.getName());
|
|
||||||
}
|
|
||||||
|
|
||||||
private static ChunkGenerator edit(ChunkGenerator generator, BiomeSource source) {
|
|
||||||
try {
|
|
||||||
BIOME_SOURCE.set(generator, source);
|
|
||||||
if (generator instanceof CustomChunkGenerator custom)
|
|
||||||
BIOME_SOURCE.set(custom.getDelegate(), source);
|
|
||||||
|
|
||||||
return generator;
|
|
||||||
} catch (IllegalAccessException e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,626 +0,0 @@
|
|||||||
package com.volmit.iris.core.nms.v1_19_R1;
|
|
||||||
|
|
||||||
import java.awt.Color;
|
|
||||||
import java.io.ByteArrayInputStream;
|
|
||||||
import java.io.ByteArrayOutputStream;
|
|
||||||
import java.io.DataInputStream;
|
|
||||||
import java.io.DataOutputStream;
|
|
||||||
import java.lang.reflect.Field;
|
|
||||||
import java.lang.reflect.Method;
|
|
||||||
import java.lang.reflect.Modifier;
|
|
||||||
import java.util.Iterator;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Vector;
|
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
|
||||||
|
|
||||||
import com.mojang.datafixers.util.Pair;
|
|
||||||
import com.volmit.iris.core.nms.container.BiomeColor;
|
|
||||||
import com.volmit.iris.util.scheduling.J;
|
|
||||||
import net.minecraft.nbt.*;
|
|
||||||
import net.minecraft.nbt.Tag;
|
|
||||||
import net.minecraft.server.commands.data.BlockDataAccessor;
|
|
||||||
import net.minecraft.tags.TagKey;
|
|
||||||
import net.minecraft.world.level.LevelReader;
|
|
||||||
import net.minecraft.world.level.block.EntityBlock;
|
|
||||||
import org.bukkit.*;
|
|
||||||
import org.bukkit.block.Biome;
|
|
||||||
import org.bukkit.block.data.BlockData;
|
|
||||||
import org.bukkit.craftbukkit.v1_19_R1.CraftChunk;
|
|
||||||
import org.bukkit.craftbukkit.v1_19_R1.CraftServer;
|
|
||||||
import org.bukkit.craftbukkit.v1_19_R1.CraftWorld;
|
|
||||||
import org.bukkit.craftbukkit.v1_19_R1.block.CraftBlock;
|
|
||||||
import org.bukkit.craftbukkit.v1_19_R1.block.CraftBlockState;
|
|
||||||
import org.bukkit.craftbukkit.v1_19_R1.block.CraftBlockStates;
|
|
||||||
import org.bukkit.craftbukkit.v1_19_R1.block.data.CraftBlockData;
|
|
||||||
import org.bukkit.craftbukkit.v1_19_R1.entity.CraftDolphin;
|
|
||||||
import org.bukkit.craftbukkit.v1_19_R1.inventory.CraftItemStack;
|
|
||||||
import org.bukkit.entity.Dolphin;
|
|
||||||
import org.bukkit.entity.Entity;
|
|
||||||
import org.bukkit.event.entity.CreatureSpawnEvent;
|
|
||||||
import org.bukkit.generator.ChunkGenerator;
|
|
||||||
import org.bukkit.inventory.ItemStack;
|
|
||||||
import org.jetbrains.annotations.Contract;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
|
|
||||||
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
|
||||||
import com.volmit.iris.Iris;
|
|
||||||
import com.volmit.iris.core.nms.INMSBinding;
|
|
||||||
import com.volmit.iris.engine.data.cache.AtomicCache;
|
|
||||||
import com.volmit.iris.engine.framework.Engine;
|
|
||||||
import com.volmit.iris.util.collection.KList;
|
|
||||||
import com.volmit.iris.util.collection.KMap;
|
|
||||||
import com.volmit.iris.util.hunk.Hunk;
|
|
||||||
import com.volmit.iris.util.json.JSONObject;
|
|
||||||
import com.volmit.iris.util.mantle.Mantle;
|
|
||||||
import com.volmit.iris.util.math.Vector3d;
|
|
||||||
import com.volmit.iris.util.matter.MatterBiomeInject;
|
|
||||||
import com.volmit.iris.util.nbt.io.NBTUtil;
|
|
||||||
import com.volmit.iris.util.nbt.mca.NBTWorld;
|
|
||||||
import com.volmit.iris.util.nbt.mca.palette.*;
|
|
||||||
import com.volmit.iris.util.nbt.tag.CompoundTag;
|
|
||||||
|
|
||||||
import it.unimi.dsi.fastutil.objects.Object2IntMap;
|
|
||||||
import net.minecraft.core.BlockPos;
|
|
||||||
import net.minecraft.core.Holder;
|
|
||||||
import net.minecraft.core.Registry;
|
|
||||||
import net.minecraft.core.RegistryAccess;
|
|
||||||
import net.minecraft.resources.ResourceLocation;
|
|
||||||
import net.minecraft.server.level.ServerLevel;
|
|
||||||
import net.minecraft.world.entity.EntityType;
|
|
||||||
import net.minecraft.world.level.biome.BiomeSource;
|
|
||||||
import net.minecraft.world.level.block.Block;
|
|
||||||
import net.minecraft.world.level.block.entity.BlockEntity;
|
|
||||||
import net.minecraft.world.level.block.state.BlockState;
|
|
||||||
import net.minecraft.world.level.chunk.ChunkAccess;
|
|
||||||
import net.minecraft.world.level.chunk.LevelChunk;
|
|
||||||
import sun.misc.Unsafe;
|
|
||||||
|
|
||||||
public class NMSBinding implements INMSBinding {
|
|
||||||
private final KMap<Biome, Object> baseBiomeCache = new KMap<>();
|
|
||||||
private final BlockData AIR = Material.AIR.createBlockData();
|
|
||||||
private final AtomicCache<MCAIdMap<net.minecraft.world.level.biome.Biome>> biomeMapCache = new AtomicCache<>();
|
|
||||||
private final AtomicCache<MCAIdMapper<BlockState>> registryCache = new AtomicCache<>();
|
|
||||||
private final AtomicCache<MCAPalette<BlockState>> globalCache = new AtomicCache<>();
|
|
||||||
private final AtomicCache<RegistryAccess> registryAccess = new AtomicCache<>();
|
|
||||||
private final AtomicCache<Method> byIdRef = new AtomicCache<>();
|
|
||||||
private Field biomeStorageCache = null;
|
|
||||||
|
|
||||||
private static Object getFor(Class<?> type, Object source) {
|
|
||||||
Object o = fieldFor(type, source);
|
|
||||||
|
|
||||||
if (o != null) {
|
|
||||||
return o;
|
|
||||||
}
|
|
||||||
|
|
||||||
return invokeFor(type, source);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Object invokeFor(Class<?> returns, Object in) {
|
|
||||||
for (Method i : in.getClass().getMethods()) {
|
|
||||||
if (i.getReturnType().equals(returns)) {
|
|
||||||
i.setAccessible(true);
|
|
||||||
try {
|
|
||||||
Iris.debug("[NMS] Found " + returns.getSimpleName() + " in " + in.getClass().getSimpleName() + "." + i.getName() + "()");
|
|
||||||
return i.invoke(in);
|
|
||||||
} catch (Throwable e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Object fieldFor(Class<?> returns, Object in) {
|
|
||||||
return fieldForClass(returns, in.getClass(), in);
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
private static <T> T fieldForClass(Class<T> returnType, Class<?> sourceType, Object in) {
|
|
||||||
for (Field i : sourceType.getDeclaredFields()) {
|
|
||||||
if (i.getType().equals(returnType)) {
|
|
||||||
i.setAccessible(true);
|
|
||||||
try {
|
|
||||||
Iris.debug("[NMS] Found " + returnType.getSimpleName() + " in " + sourceType.getSimpleName() + "." + i.getName());
|
|
||||||
return (T) i.get(in);
|
|
||||||
} catch (IllegalAccessException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Class<?> getClassType(Class<?> type, int ordinal) {
|
|
||||||
return type.getDeclaredClasses()[ordinal];
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean hasTile(Material material) {
|
|
||||||
return !CraftBlockState.class.equals(CraftBlockStates.getBlockStateType(material));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean hasTile(Location l) {
|
|
||||||
return ((CraftWorld) l.getWorld()).getHandle().getBlockEntity(new BlockPos(l.getBlockX(), l.getBlockY(), l.getBlockZ()), false) != null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
public KMap<String, Object> serializeTile(Location location) {
|
|
||||||
BlockEntity e = ((CraftWorld) location.getWorld()).getHandle().getBlockEntity(new BlockPos(location.getBlockX(), location.getBlockY(), location.getBlockZ()), false);
|
|
||||||
|
|
||||||
if (e == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
net.minecraft.nbt.CompoundTag tag = e.saveWithoutMetadata();
|
|
||||||
return (KMap<String, Object>) convertFromTag(tag, 0, 64);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Contract(value = "null, _, _ -> null", pure = true)
|
|
||||||
private Object convertFromTag(net.minecraft.nbt.Tag tag, int depth, int maxDepth) {
|
|
||||||
if (tag == null || depth > maxDepth) return null;
|
|
||||||
if (tag instanceof CollectionTag<?> collection) {
|
|
||||||
KList<Object> list = new KList<>();
|
|
||||||
|
|
||||||
for (Object i : collection) {
|
|
||||||
if (i instanceof net.minecraft.nbt.Tag t)
|
|
||||||
list.add(convertFromTag(t, depth + 1, maxDepth));
|
|
||||||
else list.add(i);
|
|
||||||
}
|
|
||||||
return list;
|
|
||||||
}
|
|
||||||
if (tag instanceof net.minecraft.nbt.CompoundTag compound) {
|
|
||||||
KMap<String, Object> map = new KMap<>();
|
|
||||||
|
|
||||||
for (String key : compound.getAllKeys()) {
|
|
||||||
var child = compound.get(key);
|
|
||||||
if (child == null) continue;
|
|
||||||
var value = convertFromTag(child, depth + 1, maxDepth);
|
|
||||||
if (value == null) continue;
|
|
||||||
map.put(key, value);
|
|
||||||
}
|
|
||||||
return map;
|
|
||||||
}
|
|
||||||
if (tag instanceof NumericTag numeric)
|
|
||||||
return numeric.getAsNumber();
|
|
||||||
return tag.getAsString();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void deserializeTile(KMap<String, Object> map, Location pos) {
|
|
||||||
net.minecraft.nbt.CompoundTag tag = (net.minecraft.nbt.CompoundTag) convertToTag(map, 0, 64);
|
|
||||||
var level = ((CraftWorld) pos.getWorld()).getHandle();
|
|
||||||
var blockPos = new BlockPos(pos.getBlockX(), pos.getBlockY(), pos.getBlockZ());
|
|
||||||
J.s(() -> merge(level, blockPos, tag));
|
|
||||||
}
|
|
||||||
|
|
||||||
private void merge(ServerLevel level, BlockPos blockPos, net.minecraft.nbt.CompoundTag tag) {
|
|
||||||
var blockEntity = level.getBlockEntity(blockPos);
|
|
||||||
if (blockEntity == null) {
|
|
||||||
Iris.warn("[NMS] BlockEntity not found at " + blockPos);
|
|
||||||
var state = level.getBlockState(blockPos);
|
|
||||||
if (!state.hasBlockEntity())
|
|
||||||
return;
|
|
||||||
|
|
||||||
blockEntity = ((EntityBlock) state.getBlock())
|
|
||||||
.newBlockEntity(blockPos, state);
|
|
||||||
}
|
|
||||||
var accessor = new BlockDataAccessor(blockEntity, blockPos);
|
|
||||||
accessor.setData(tag.merge(accessor.getData()));
|
|
||||||
}
|
|
||||||
|
|
||||||
private Tag convertToTag(Object object, int depth, int maxDepth) {
|
|
||||||
if (object == null || depth > maxDepth) return EndTag.INSTANCE;
|
|
||||||
if (object instanceof Map<?,?> map) {
|
|
||||||
var tag = new net.minecraft.nbt.CompoundTag();
|
|
||||||
for (var i : map.entrySet()) {
|
|
||||||
tag.put(i.getKey().toString(), convertToTag(i.getValue(), depth + 1, maxDepth));
|
|
||||||
}
|
|
||||||
return tag;
|
|
||||||
}
|
|
||||||
if (object instanceof List<?> list) {
|
|
||||||
var tag = new net.minecraft.nbt.ListTag();
|
|
||||||
for (var i : list) {
|
|
||||||
tag.add(convertToTag(i, depth + 1, maxDepth));
|
|
||||||
}
|
|
||||||
return tag;
|
|
||||||
}
|
|
||||||
if (object instanceof Byte number) return ByteTag.valueOf(number);
|
|
||||||
if (object instanceof Short number) return ShortTag.valueOf(number);
|
|
||||||
if (object instanceof Integer number) return IntTag.valueOf(number);
|
|
||||||
if (object instanceof Long number) return LongTag.valueOf(number);
|
|
||||||
if (object instanceof Float number) return FloatTag.valueOf(number);
|
|
||||||
if (object instanceof Double number) return DoubleTag.valueOf(number);
|
|
||||||
if (object instanceof String string) return StringTag.valueOf(string);
|
|
||||||
return EndTag.INSTANCE;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public CompoundTag serializeEntity(Entity location) {
|
|
||||||
return null;// TODO:
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Entity deserializeEntity(CompoundTag s, Location newPosition) {
|
|
||||||
return null;// TODO:
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean supportsCustomHeight() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private RegistryAccess registry() {
|
|
||||||
return registryAccess.aquire(() -> (RegistryAccess) getFor(RegistryAccess.Frozen.class, ((CraftServer) Bukkit.getServer()).getHandle().getServer()));
|
|
||||||
}
|
|
||||||
|
|
||||||
private Registry<net.minecraft.world.level.biome.Biome> getCustomBiomeRegistry() {
|
|
||||||
return registry().registry(Registry.BIOME_REGISTRY).orElse(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
private Registry<Block> getBlockRegistry() {
|
|
||||||
return registry().registry(Registry.BLOCK_REGISTRY).orElse(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Object getBiomeBaseFromId(int id) {
|
|
||||||
return getCustomBiomeRegistry().getHolder(id);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getMinHeight(World world) {
|
|
||||||
return world.getMinHeight();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean supportsCustomBiomes() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getTrueBiomeBaseId(Object biomeBase) {
|
|
||||||
return getCustomBiomeRegistry().getId(((Holder<net.minecraft.world.level.biome.Biome>) biomeBase).value());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Object getTrueBiomeBase(Location location) {
|
|
||||||
return ((CraftWorld) location.getWorld()).getHandle().getBiome(new BlockPos(location.getBlockX(), location.getBlockY(), location.getBlockZ()));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getTrueBiomeBaseKey(Location location) {
|
|
||||||
return getKeyForBiomeBase(getTrueBiomeBase(location));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Object getCustomBiomeBaseFor(String mckey) {
|
|
||||||
return getCustomBiomeRegistry().get(new ResourceLocation(mckey));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Object getCustomBiomeBaseHolderFor(String mckey) {
|
|
||||||
return getCustomBiomeRegistry().getHolder(getTrueBiomeBaseId(getCustomBiomeRegistry().get(new ResourceLocation(mckey)))).get();
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getBiomeBaseIdForKey(String key) {
|
|
||||||
return getCustomBiomeRegistry().getId(getCustomBiomeRegistry().get(new ResourceLocation(key)));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getKeyForBiomeBase(Object biomeBase) {
|
|
||||||
return getCustomBiomeRegistry().getKey((net.minecraft.world.level.biome.Biome) biomeBase).getPath(); // something, not something:something
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Object getBiomeBase(World world, Biome biome) {
|
|
||||||
return CraftBlock.biomeToBiomeBase(((CraftWorld) world).getHandle()
|
|
||||||
.registryAccess().registry(Registry.BIOME_REGISTRY).orElse(null), biome);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Object getBiomeBase(Object registry, Biome biome) {
|
|
||||||
Object v = baseBiomeCache.get(biome);
|
|
||||||
|
|
||||||
if (v != null) {
|
|
||||||
return v;
|
|
||||||
}
|
|
||||||
//noinspection unchecked
|
|
||||||
v = CraftBlock.biomeToBiomeBase((Registry<net.minecraft.world.level.biome.Biome>) registry, biome);
|
|
||||||
if (v == null) {
|
|
||||||
// Ok so there is this new biome name called "CUSTOM" in Paper's new releases.
|
|
||||||
// But, this does NOT exist within CraftBukkit which makes it return an error.
|
|
||||||
// So, we will just return the ID that the plains biome returns instead.
|
|
||||||
//noinspection unchecked
|
|
||||||
return CraftBlock.biomeToBiomeBase((Registry<net.minecraft.world.level.biome.Biome>) registry, Biome.PLAINS);
|
|
||||||
}
|
|
||||||
baseBiomeCache.put(biome, v);
|
|
||||||
return v;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public KList<Biome> getBiomes() {
|
|
||||||
return new KList<>(Biome.values()).qdel(Biome.CUSTOM);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isBukkit() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getBiomeId(Biome biome) {
|
|
||||||
for (World i : Bukkit.getWorlds()) {
|
|
||||||
if (i.getEnvironment().equals(World.Environment.NORMAL)) {
|
|
||||||
Registry<net.minecraft.world.level.biome.Biome> registry = ((CraftWorld) i).getHandle().registryAccess().registry(Registry.BIOME_REGISTRY).orElse(null);
|
|
||||||
return registry.getId((net.minecraft.world.level.biome.Biome) getBiomeBase(registry, biome));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return biome.ordinal();
|
|
||||||
}
|
|
||||||
|
|
||||||
private MCAIdMap<net.minecraft.world.level.biome.Biome> getBiomeMapping() {
|
|
||||||
return biomeMapCache.aquire(() -> new MCAIdMap<>() {
|
|
||||||
@NotNull
|
|
||||||
@Override
|
|
||||||
public Iterator<net.minecraft.world.level.biome.Biome> iterator() {
|
|
||||||
return getCustomBiomeRegistry().iterator();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getId(net.minecraft.world.level.biome.Biome paramT) {
|
|
||||||
return getCustomBiomeRegistry().getId(paramT);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public net.minecraft.world.level.biome.Biome byId(int paramInt) {
|
|
||||||
return (net.minecraft.world.level.biome.Biome) getBiomeBaseFromId(paramInt);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
private MCABiomeContainer getBiomeContainerInterface(MCAIdMap<net.minecraft.world.level.biome.Biome> biomeMapping, MCAChunkBiomeContainer<net.minecraft.world.level.biome.Biome> base) {
|
|
||||||
return new MCABiomeContainer() {
|
|
||||||
@Override
|
|
||||||
public int[] getData() {
|
|
||||||
return base.writeBiomes();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setBiome(int x, int y, int z, int id) {
|
|
||||||
base.setBiome(x, y, z, biomeMapping.byId(id));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getBiome(int x, int y, int z) {
|
|
||||||
return biomeMapping.getId(base.getBiome(x, y, z));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public MCABiomeContainer newBiomeContainer(int min, int max) {
|
|
||||||
MCAChunkBiomeContainer<net.minecraft.world.level.biome.Biome> base = new MCAChunkBiomeContainer<>(getBiomeMapping(), min, max);
|
|
||||||
return getBiomeContainerInterface(getBiomeMapping(), base);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public MCABiomeContainer newBiomeContainer(int min, int max, int[] data) {
|
|
||||||
MCAChunkBiomeContainer<net.minecraft.world.level.biome.Biome> base = new MCAChunkBiomeContainer<>(getBiomeMapping(), min, max, data);
|
|
||||||
return getBiomeContainerInterface(getBiomeMapping(), base);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int countCustomBiomes() {
|
|
||||||
AtomicInteger a = new AtomicInteger(0);
|
|
||||||
|
|
||||||
getCustomBiomeRegistry().keySet().forEach((i) -> {
|
|
||||||
if (i.getNamespace().equals("minecraft")) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
a.incrementAndGet();
|
|
||||||
Iris.debug("Custom Biome: " + i);
|
|
||||||
});
|
|
||||||
|
|
||||||
return a.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean supportsDataPacks() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setBiomes(int cx, int cz, World world, Hunk<Object> biomes) {
|
|
||||||
LevelChunk c = ((CraftWorld) world).getHandle().getChunk(cx, cz);
|
|
||||||
biomes.iterateSync((x, y, z, b) -> c.setBiome(x, y, z, (Holder<net.minecraft.world.level.biome.Biome>) b));
|
|
||||||
c.setUnsaved(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void forceBiomeInto(int x, int y, int z, Object somethingVeryDirty, ChunkGenerator.BiomeGrid chunk) {
|
|
||||||
try {
|
|
||||||
ChunkAccess s = (ChunkAccess) getFieldForBiomeStorage(chunk).get(chunk);
|
|
||||||
Holder<net.minecraft.world.level.biome.Biome> biome = (Holder<net.minecraft.world.level.biome.Biome>) somethingVeryDirty;
|
|
||||||
s.setBiome(x, y, z, biome);
|
|
||||||
} catch (IllegalAccessException e) {
|
|
||||||
Iris.reportError(e);
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private Field getFieldForBiomeStorage(Object storage) {
|
|
||||||
Field f = biomeStorageCache;
|
|
||||||
|
|
||||||
if (f != null) {
|
|
||||||
return f;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
f = storage.getClass().getDeclaredField("biome");
|
|
||||||
f.setAccessible(true);
|
|
||||||
return f;
|
|
||||||
} catch (Throwable e) {
|
|
||||||
Iris.reportError(e);
|
|
||||||
e.printStackTrace();
|
|
||||||
Iris.error(storage.getClass().getCanonicalName());
|
|
||||||
}
|
|
||||||
|
|
||||||
biomeStorageCache = f;
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public MCAPaletteAccess createPalette() {
|
|
||||||
MCAIdMapper<BlockState> registry = registryCache.aquireNasty(() -> {
|
|
||||||
Field cf = net.minecraft.core.IdMapper.class.getDeclaredField("tToId");
|
|
||||||
Field df = net.minecraft.core.IdMapper.class.getDeclaredField("idToT");
|
|
||||||
Field bf = net.minecraft.core.IdMapper.class.getDeclaredField("nextId");
|
|
||||||
cf.setAccessible(true);
|
|
||||||
df.setAccessible(true);
|
|
||||||
bf.setAccessible(true);
|
|
||||||
net.minecraft.core.IdMapper<BlockState> blockData = Block.BLOCK_STATE_REGISTRY;
|
|
||||||
int b = bf.getInt(blockData);
|
|
||||||
Object2IntMap<BlockState> c = (Object2IntMap<BlockState>) cf.get(blockData);
|
|
||||||
List<BlockState> d = (List<BlockState>) df.get(blockData);
|
|
||||||
return new MCAIdMapper<BlockState>(c, d, b);
|
|
||||||
});
|
|
||||||
MCAPalette<BlockState> global = globalCache.aquireNasty(() -> new MCAGlobalPalette<>(registry, ((CraftBlockData) AIR).getState()));
|
|
||||||
MCAPalettedContainer<BlockState> container = new MCAPalettedContainer<>(global, registry,
|
|
||||||
i -> ((CraftBlockData) NBTWorld.getBlockData(i)).getState(),
|
|
||||||
i -> NBTWorld.getCompound(CraftBlockData.fromData(i)),
|
|
||||||
((CraftBlockData) AIR).getState());
|
|
||||||
return new MCAWrappedPalettedContainer<>(container,
|
|
||||||
i -> NBTWorld.getCompound(CraftBlockData.fromData(i)),
|
|
||||||
i -> ((CraftBlockData) NBTWorld.getBlockData(i)).getState());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void injectBiomesFromMantle(Chunk e, Mantle mantle) {
|
|
||||||
ChunkAccess chunk = ((CraftChunk) e).getHandle();
|
|
||||||
AtomicInteger c = new AtomicInteger();
|
|
||||||
AtomicInteger r = new AtomicInteger();
|
|
||||||
mantle.iterateChunk(e.getX(), e.getZ(), MatterBiomeInject.class, (x, y, z, b) -> {
|
|
||||||
if (b != null) {
|
|
||||||
if (b.isCustom()) {
|
|
||||||
chunk.setBiome(x, y, z, getCustomBiomeRegistry().getHolder(b.getBiomeId()).get());
|
|
||||||
c.getAndIncrement();
|
|
||||||
} else {
|
|
||||||
chunk.setBiome(x, y, z, (Holder<net.minecraft.world.level.biome.Biome>) getBiomeBase(e.getWorld(), b.getBiome()));
|
|
||||||
r.getAndIncrement();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public ItemStack applyCustomNbt(ItemStack itemStack, KMap<String, Object> customNbt) throws IllegalArgumentException {
|
|
||||||
if (customNbt != null && !customNbt.isEmpty()) {
|
|
||||||
net.minecraft.world.item.ItemStack s = CraftItemStack.asNMSCopy(itemStack);
|
|
||||||
|
|
||||||
try {
|
|
||||||
net.minecraft.nbt.CompoundTag tag = TagParser.parseTag((new JSONObject(customNbt)).toString());
|
|
||||||
tag.merge(s.getOrCreateTag());
|
|
||||||
s.setTag(tag);
|
|
||||||
} catch (CommandSyntaxException var5) {
|
|
||||||
throw new IllegalArgumentException(var5);
|
|
||||||
}
|
|
||||||
|
|
||||||
return CraftItemStack.asBukkitCopy(s);
|
|
||||||
} else {
|
|
||||||
return itemStack;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void inject(long seed, Engine engine, World world) {
|
|
||||||
var chunkMap = ((CraftWorld)world).getHandle().getChunkSource().chunkMap;
|
|
||||||
chunkMap.generator = new IrisChunkGenerator(chunkMap.generator, seed, engine, world);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Vector3d getBoundingbox(org.bukkit.entity.EntityType entity) {
|
|
||||||
Field[] fields = EntityType.class.getDeclaredFields();
|
|
||||||
for (Field field : fields) {
|
|
||||||
if (Modifier.isStatic(field.getModifiers()) && field.getType().equals(EntityType.class)) {
|
|
||||||
try {
|
|
||||||
EntityType entityType = (EntityType) field.get(null);
|
|
||||||
if (entityType.getDescriptionId().equals("entity.minecraft." + entity.name().toLowerCase())) {
|
|
||||||
Vector<Float> v1 = new Vector<>();
|
|
||||||
v1.add(entityType.getHeight());
|
|
||||||
entityType.getDimensions();
|
|
||||||
Vector3d box = new Vector3d( entityType.getWidth(), entityType.getHeight(), entityType.getWidth());
|
|
||||||
//System.out.println("Entity Type: " + entityType.getDescriptionId() + ", " + "Height: " + height + ", Width: " + width);
|
|
||||||
return box;
|
|
||||||
}
|
|
||||||
} catch (IllegalAccessException e) {
|
|
||||||
Iris.error("Unable to get entity dimensions!");
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Entity spawnEntity(Location location, org.bukkit.entity.EntityType type, CreatureSpawnEvent.SpawnReason reason) {
|
|
||||||
return ((CraftWorld) location.getWorld()).spawn(location, type.getEntityClass(), null, reason);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Color getBiomeColor(Location location, BiomeColor type) {
|
|
||||||
LevelReader reader = ((CraftWorld) location.getWorld()).getHandle();
|
|
||||||
var holder = reader.getBiome(new BlockPos(location.getBlockX(), location.getBlockY(), location.getBlockZ()));
|
|
||||||
var biome = holder.value();
|
|
||||||
if (biome == null) throw new IllegalArgumentException("Invalid biome: " + holder.unwrapKey().orElse(null));
|
|
||||||
|
|
||||||
int rgba = switch (type) {
|
|
||||||
case FOG -> biome.getFogColor();
|
|
||||||
case WATER -> biome.getWaterColor();
|
|
||||||
case WATER_FOG -> biome.getWaterFogColor();
|
|
||||||
case SKY -> biome.getSkyColor();
|
|
||||||
case FOLIAGE -> biome.getFoliageColor();
|
|
||||||
case GRASS -> biome.getGrassColor(location.getBlockX(), location.getBlockZ());
|
|
||||||
};
|
|
||||||
if (rgba == 0) {
|
|
||||||
if (BiomeColor.FOLIAGE == type && biome.getSpecialEffects().getFoliageColorOverride().isEmpty())
|
|
||||||
return null;
|
|
||||||
if (BiomeColor.GRASS == type && biome.getSpecialEffects().getGrassColorOverride().isEmpty())
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return new Color(rgba, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public KList<String> getStructureKeys() {
|
|
||||||
KList<String> keys = new KList<>();
|
|
||||||
|
|
||||||
var registry = registry().registry(Registry.STRUCTURE_REGISTRY).orElse(null);
|
|
||||||
if (registry == null) return keys;
|
|
||||||
registry.keySet().stream().map(ResourceLocation::toString).forEach(keys::add);
|
|
||||||
registry.getTags()
|
|
||||||
.map(Pair::getFirst)
|
|
||||||
.map(TagKey::location)
|
|
||||||
.map(ResourceLocation::toString)
|
|
||||||
.map(s -> "#" + s)
|
|
||||||
.forEach(keys::add);
|
|
||||||
|
|
||||||
return keys;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Field getField(Class<?> clazz, Class<?> fieldType) throws NoSuchFieldException {
|
|
||||||
try {
|
|
||||||
for (Field f : clazz.getDeclaredFields()) {
|
|
||||||
if (f.getType().equals(fieldType))
|
|
||||||
return f;
|
|
||||||
}
|
|
||||||
throw new NoSuchFieldException(fieldType.getName());
|
|
||||||
} catch (NoSuchFieldException var4) {
|
|
||||||
Class<?> superClass = clazz.getSuperclass();
|
|
||||||
if (superClass == null) {
|
|
||||||
throw var4;
|
|
||||||
} else {
|
|
||||||
return getField(superClass, fieldType);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,152 +0,0 @@
|
|||||||
package com.volmit.iris.core.nms.v1_19_R2;
|
|
||||||
|
|
||||||
import com.mojang.serialization.Codec;
|
|
||||||
import com.volmit.iris.Iris;
|
|
||||||
import com.volmit.iris.engine.data.cache.AtomicCache;
|
|
||||||
import com.volmit.iris.engine.framework.Engine;
|
|
||||||
import com.volmit.iris.engine.object.IrisBiome;
|
|
||||||
import com.volmit.iris.engine.object.IrisBiomeCustom;
|
|
||||||
import com.volmit.iris.util.collection.KMap;
|
|
||||||
import com.volmit.iris.util.math.RNG;
|
|
||||||
import net.minecraft.core.Holder;
|
|
||||||
import net.minecraft.core.Registry;
|
|
||||||
import net.minecraft.core.RegistryAccess;
|
|
||||||
import net.minecraft.core.registries.Registries;
|
|
||||||
import net.minecraft.resources.ResourceLocation;
|
|
||||||
import net.minecraft.world.level.biome.Biome;
|
|
||||||
import net.minecraft.world.level.biome.BiomeSource;
|
|
||||||
import net.minecraft.world.level.biome.Climate;
|
|
||||||
import org.bukkit.Bukkit;
|
|
||||||
import org.bukkit.World;
|
|
||||||
import org.bukkit.craftbukkit.v1_19_R2.CraftServer;
|
|
||||||
import org.bukkit.craftbukkit.v1_19_R2.CraftWorld;
|
|
||||||
import org.bukkit.craftbukkit.v1_19_R2.block.CraftBlock;
|
|
||||||
|
|
||||||
import java.lang.reflect.Field;
|
|
||||||
import java.lang.reflect.Method;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public class CustomBiomeSource extends BiomeSource {
|
|
||||||
|
|
||||||
private final long seed;
|
|
||||||
private final Engine engine;
|
|
||||||
private final Registry<Biome> biomeCustomRegistry;
|
|
||||||
private final Registry<Biome> biomeRegistry;
|
|
||||||
private final AtomicCache<RegistryAccess> registryAccess = new AtomicCache<>();
|
|
||||||
private final RNG rng;
|
|
||||||
private final KMap<String, Holder<Biome>> customBiomes;
|
|
||||||
|
|
||||||
public CustomBiomeSource(long seed, Engine engine, World world) {
|
|
||||||
super(getAllBiomes(
|
|
||||||
((RegistryAccess) getFor(RegistryAccess.Frozen.class, ((CraftServer) Bukkit.getServer()).getHandle().getServer()))
|
|
||||||
.registry(Registries.BIOME).orElse(null),
|
|
||||||
((CraftWorld) world).getHandle().registryAccess().registry(Registries.BIOME).orElse(null),
|
|
||||||
engine));
|
|
||||||
this.engine = engine;
|
|
||||||
this.seed = seed;
|
|
||||||
this.biomeCustomRegistry = registry().registry(Registries.BIOME).orElse(null);
|
|
||||||
this.biomeRegistry = ((CraftWorld) world).getHandle().registryAccess().registry(Registries.BIOME).orElse(null);
|
|
||||||
this.rng = new RNG(engine.getSeedManager().getBiome());
|
|
||||||
this.customBiomes = fillCustomBiomes(biomeCustomRegistry, engine);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static List<Holder<Biome>> getAllBiomes(Registry<Biome> customRegistry, Registry<Biome> registry, Engine engine) {
|
|
||||||
List<Holder<Biome>> b = new ArrayList<>();
|
|
||||||
|
|
||||||
for (IrisBiome i : engine.getAllBiomes()) {
|
|
||||||
if (i.isCustom()) {
|
|
||||||
for (IrisBiomeCustom j : i.getCustomDerivitives()) {
|
|
||||||
b.add(customRegistry.getHolder(customRegistry.getResourceKey(customRegistry
|
|
||||||
.get(new ResourceLocation(engine.getDimension().getLoadKey() + ":" + j.getId()))).get()).get());
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
b.add(CraftBlock.biomeToBiomeBase(registry, i.getVanillaDerivative()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return b;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Object getFor(Class<?> type, Object source) {
|
|
||||||
Object o = fieldFor(type, source);
|
|
||||||
|
|
||||||
if (o != null) {
|
|
||||||
return o;
|
|
||||||
}
|
|
||||||
|
|
||||||
return invokeFor(type, source);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Object fieldFor(Class<?> returns, Object in) {
|
|
||||||
return fieldForClass(returns, in.getClass(), in);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Object invokeFor(Class<?> returns, Object in) {
|
|
||||||
for (Method i : in.getClass().getMethods()) {
|
|
||||||
if (i.getReturnType().equals(returns)) {
|
|
||||||
i.setAccessible(true);
|
|
||||||
try {
|
|
||||||
Iris.debug("[NMS] Found " + returns.getSimpleName() + " in " + in.getClass().getSimpleName() + "." + i.getName() + "()");
|
|
||||||
return i.invoke(in);
|
|
||||||
} catch (Throwable e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
private static <T> T fieldForClass(Class<T> returnType, Class<?> sourceType, Object in) {
|
|
||||||
for (Field i : sourceType.getDeclaredFields()) {
|
|
||||||
if (i.getType().equals(returnType)) {
|
|
||||||
i.setAccessible(true);
|
|
||||||
try {
|
|
||||||
Iris.debug("[NMS] Found " + returnType.getSimpleName() + " in " + sourceType.getSimpleName() + "." + i.getName());
|
|
||||||
return (T) i.get(in);
|
|
||||||
} catch (IllegalAccessException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private KMap<String, Holder<Biome>> fillCustomBiomes(Registry<Biome> customRegistry, Engine engine) {
|
|
||||||
KMap<String, Holder<Biome>> m = new KMap<>();
|
|
||||||
|
|
||||||
for (IrisBiome i : engine.getAllBiomes()) {
|
|
||||||
if (i.isCustom()) {
|
|
||||||
for (IrisBiomeCustom j : i.getCustomDerivitives()) {
|
|
||||||
m.put(j.getId(), customRegistry.getHolder(customRegistry.getResourceKey(customRegistry
|
|
||||||
.get(new ResourceLocation(engine.getDimension().getLoadKey() + ":" + j.getId()))).get()).get());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return m;
|
|
||||||
}
|
|
||||||
|
|
||||||
private RegistryAccess registry() {
|
|
||||||
return registryAccess.aquire(() -> (RegistryAccess) getFor(RegistryAccess.Frozen.class, ((CraftServer) Bukkit.getServer()).getHandle().getServer()));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected Codec<? extends BiomeSource> codec() {
|
|
||||||
throw new UnsupportedOperationException("Not supported");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Holder<Biome> getNoiseBiome(int x, int y, int z, Climate.Sampler sampler) {
|
|
||||||
int m = (y - engine.getMinHeight()) << 2;
|
|
||||||
IrisBiome ib = engine.getComplex().getTrueBiomeStream().get(x << 2, z << 2);
|
|
||||||
if (ib.isCustom()) {
|
|
||||||
return customBiomes.get(ib.getCustomBiome(rng, x << 2, m, z << 2).getId());
|
|
||||||
} else {
|
|
||||||
org.bukkit.block.Biome v = ib.getSkyBiome(rng, x << 2, m, z << 2);
|
|
||||||
return CraftBlock.biomeToBiomeBase(biomeRegistry, v);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,260 +0,0 @@
|
|||||||
package com.volmit.iris.core.nms.v1_19_R2;
|
|
||||||
|
|
||||||
import com.mojang.datafixers.util.Pair;
|
|
||||||
import com.mojang.serialization.Codec;
|
|
||||||
import com.volmit.iris.Iris;
|
|
||||||
import com.volmit.iris.engine.framework.Engine;
|
|
||||||
import com.volmit.iris.engine.framework.ResultLocator;
|
|
||||||
import com.volmit.iris.engine.framework.WrongEngineBroException;
|
|
||||||
import com.volmit.iris.engine.object.IrisJigsawStructure;
|
|
||||||
import com.volmit.iris.engine.object.IrisJigsawStructurePlacement;
|
|
||||||
import com.volmit.iris.util.collection.KList;
|
|
||||||
import com.volmit.iris.util.collection.KMap;
|
|
||||||
import com.volmit.iris.util.collection.KSet;
|
|
||||||
import com.volmit.iris.util.mantle.MantleFlag;
|
|
||||||
import com.volmit.iris.util.math.Position2;
|
|
||||||
import com.volmit.iris.util.reflect.WrappedField;
|
|
||||||
import net.minecraft.core.BlockPos;
|
|
||||||
import net.minecraft.core.Holder;
|
|
||||||
import net.minecraft.core.HolderSet;
|
|
||||||
import net.minecraft.core.RegistryAccess;
|
|
||||||
import net.minecraft.core.registries.Registries;
|
|
||||||
import net.minecraft.resources.ResourceKey;
|
|
||||||
import net.minecraft.resources.ResourceLocation;
|
|
||||||
import net.minecraft.server.level.ServerLevel;
|
|
||||||
import net.minecraft.server.level.WorldGenRegion;
|
|
||||||
import net.minecraft.tags.TagKey;
|
|
||||||
import net.minecraft.util.random.WeightedRandomList;
|
|
||||||
import net.minecraft.world.entity.MobCategory;
|
|
||||||
import net.minecraft.world.level.LevelHeightAccessor;
|
|
||||||
import net.minecraft.world.level.NoiseColumn;
|
|
||||||
import net.minecraft.world.level.StructureManager;
|
|
||||||
import net.minecraft.world.level.WorldGenLevel;
|
|
||||||
import net.minecraft.world.level.biome.Biome;
|
|
||||||
import net.minecraft.world.level.biome.BiomeManager;
|
|
||||||
import net.minecraft.world.level.biome.BiomeSource;
|
|
||||||
import net.minecraft.world.level.biome.MobSpawnSettings;
|
|
||||||
import net.minecraft.world.level.chunk.ChunkAccess;
|
|
||||||
import net.minecraft.world.level.chunk.ChunkGenerator;
|
|
||||||
import net.minecraft.world.level.chunk.ChunkGeneratorStructureState;
|
|
||||||
import net.minecraft.world.level.levelgen.GenerationStep;
|
|
||||||
import net.minecraft.world.level.levelgen.Heightmap;
|
|
||||||
import net.minecraft.world.level.levelgen.RandomState;
|
|
||||||
import net.minecraft.world.level.levelgen.blending.Blender;
|
|
||||||
import net.minecraft.world.level.levelgen.structure.Structure;
|
|
||||||
import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplateManager;
|
|
||||||
import org.bukkit.World;
|
|
||||||
import org.bukkit.craftbukkit.v1_19_R2.CraftWorld;
|
|
||||||
import org.bukkit.craftbukkit.v1_19_R2.generator.CustomChunkGenerator;
|
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
|
||||||
import java.lang.reflect.Field;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Objects;
|
|
||||||
import java.util.concurrent.*;
|
|
||||||
|
|
||||||
public class IrisChunkGenerator extends CustomChunkGenerator {
|
|
||||||
private static final WrappedField<ChunkGenerator, BiomeSource> BIOME_SOURCE;
|
|
||||||
private final ChunkGenerator delegate;
|
|
||||||
private final Engine engine;
|
|
||||||
private final KMap<ResourceKey<Structure>, KSet<String>> structures = new KMap<>();
|
|
||||||
|
|
||||||
public IrisChunkGenerator(ChunkGenerator delegate, long seed, Engine engine, World world) {
|
|
||||||
super(((CraftWorld) world).getHandle(), edit(delegate, new CustomBiomeSource(seed, engine, world)), null);
|
|
||||||
this.delegate = delegate;
|
|
||||||
this.engine = engine;
|
|
||||||
var dimension = engine.getDimension();
|
|
||||||
|
|
||||||
KSet<IrisJigsawStructure> placements = new KSet<>();
|
|
||||||
addAll(dimension.getJigsawStructures(), placements);
|
|
||||||
for (var region : dimension.getAllRegions(engine)) {
|
|
||||||
addAll(region.getJigsawStructures(), placements);
|
|
||||||
for (var biome : region.getAllBiomes(engine))
|
|
||||||
addAll(biome.getJigsawStructures(), placements);
|
|
||||||
}
|
|
||||||
var stronghold = dimension.getStronghold();
|
|
||||||
if (stronghold != null)
|
|
||||||
placements.add(engine.getData().getJigsawStructureLoader().load(stronghold));
|
|
||||||
placements.removeIf(Objects::isNull);
|
|
||||||
|
|
||||||
var registry = ((CraftWorld) world).getHandle().registryAccess().registry(Registries.STRUCTURE).orElseThrow();
|
|
||||||
for (var s : placements) {
|
|
||||||
try {
|
|
||||||
String raw = s.getStructureKey();
|
|
||||||
if (raw == null) continue;
|
|
||||||
boolean tag = raw.startsWith("#");
|
|
||||||
if (tag) raw = raw.substring(1);
|
|
||||||
|
|
||||||
var location = new ResourceLocation(raw);
|
|
||||||
if (!tag) {
|
|
||||||
structures.computeIfAbsent(ResourceKey.create(Registries.STRUCTURE, location), k -> new KSet<>()).add(s.getLoadKey());
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
var key = TagKey.create(Registries.STRUCTURE, location);
|
|
||||||
var set = registry.getTag(key).orElse(null);
|
|
||||||
if (set == null) {
|
|
||||||
Iris.error("Could not find structure tag: " + raw);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
for (var holder : set) {
|
|
||||||
var resourceKey = holder.unwrapKey().orElse(null);
|
|
||||||
if (resourceKey == null) continue;
|
|
||||||
structures.computeIfAbsent(resourceKey, k -> new KSet<>()).add(s.getLoadKey());
|
|
||||||
}
|
|
||||||
} catch (Throwable e) {
|
|
||||||
Iris.error("Failed to load structure: " + s.getLoadKey());
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void addAll(KList<IrisJigsawStructurePlacement> placements, KSet<IrisJigsawStructure> structures) {
|
|
||||||
if (placements == null) return;
|
|
||||||
placements.stream()
|
|
||||||
.map(IrisJigsawStructurePlacement::getStructure)
|
|
||||||
.map(engine.getData().getJigsawStructureLoader()::load)
|
|
||||||
.filter(Objects::nonNull)
|
|
||||||
.forEach(structures::add);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public @Nullable Pair<BlockPos, Holder<Structure>> findNearestMapStructure(ServerLevel level, HolderSet<Structure> holders, BlockPos pos, int radius, boolean findUnexplored) {
|
|
||||||
if (engine.getDimension().isDisableExplorerMaps())
|
|
||||||
return null;
|
|
||||||
|
|
||||||
KMap<String, Holder<Structure>> structures = new KMap<>();
|
|
||||||
for (var holder : holders) {
|
|
||||||
if (holder == null) continue;
|
|
||||||
var key = holder.unwrapKey().orElse(null);
|
|
||||||
var set = this.structures.get(key);
|
|
||||||
if (set == null) continue;
|
|
||||||
for (var structure : set) {
|
|
||||||
structures.put(structure, holder);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (structures.isEmpty())
|
|
||||||
return null;
|
|
||||||
|
|
||||||
var locator = ResultLocator.locateStructure(structures.keySet())
|
|
||||||
.then((e, p , s) -> structures.get(s.getLoadKey()));
|
|
||||||
if (findUnexplored)
|
|
||||||
locator = locator.then((e, p, s) -> e.getMantle().getMantle().getChunk(p.getX(), p.getZ()).isFlagged(MantleFlag.DISCOVERED) ? null : s);
|
|
||||||
|
|
||||||
try {
|
|
||||||
var result = locator.find(engine, new Position2(pos.getX() >> 4, pos.getZ() >> 4), radius * 10L, i -> {}, false).get();
|
|
||||||
if (result == null) return null;
|
|
||||||
var blockPos = new BlockPos(result.getBlockX(), 0, result.getBlockZ());
|
|
||||||
return Pair.of(blockPos, result.obj());
|
|
||||||
} catch (WrongEngineBroException | ExecutionException | InterruptedException e) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected Codec<? extends ChunkGenerator> codec() {
|
|
||||||
return Codec.unit(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public ChunkGenerator getDelegate() {
|
|
||||||
if (delegate instanceof CustomChunkGenerator chunkGenerator)
|
|
||||||
return chunkGenerator.getDelegate();
|
|
||||||
return delegate;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getMinY() {
|
|
||||||
return delegate.getMinY();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getSeaLevel() {
|
|
||||||
return delegate.getSeaLevel();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void createStructures(RegistryAccess iregistrycustom, ChunkGeneratorStructureState chunkgeneratorstructurestate, StructureManager structuremanager, ChunkAccess ichunkaccess, StructureTemplateManager structuretemplatemanager) {
|
|
||||||
delegate.createStructures(iregistrycustom, chunkgeneratorstructurestate, structuremanager, ichunkaccess, structuretemplatemanager);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void buildSurface(WorldGenRegion regionlimitedworldaccess, StructureManager structuremanager, RandomState randomstate, ChunkAccess ichunkaccess) {
|
|
||||||
delegate.buildSurface(regionlimitedworldaccess, structuremanager, randomstate, ichunkaccess);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void applyCarvers(WorldGenRegion regionlimitedworldaccess, long seed, RandomState randomstate, BiomeManager biomemanager, StructureManager structuremanager, ChunkAccess ichunkaccess, GenerationStep.Carving worldgenstage_features) {
|
|
||||||
delegate.applyCarvers(regionlimitedworldaccess, seed, randomstate, biomemanager, structuremanager, ichunkaccess, worldgenstage_features);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public CompletableFuture<ChunkAccess> fillFromNoise(Executor executor, Blender blender, RandomState randomstate, StructureManager structuremanager, ChunkAccess ichunkaccess) {
|
|
||||||
return delegate.fillFromNoise(executor, blender, randomstate, structuremanager, ichunkaccess);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getBaseHeight(int i, int j, Heightmap.Types heightmap_type, LevelHeightAccessor levelheightaccessor, RandomState randomstate) {
|
|
||||||
return delegate.getBaseHeight(i, j, heightmap_type, levelheightaccessor, randomstate);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public WeightedRandomList<MobSpawnSettings.SpawnerData> getMobsAt(Holder<Biome> holder, StructureManager structuremanager, MobCategory enumcreaturetype, BlockPos blockposition) {
|
|
||||||
return delegate.getMobsAt(holder, structuremanager, enumcreaturetype, blockposition);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void applyBiomeDecoration(WorldGenLevel generatoraccessseed, ChunkAccess ichunkaccess, StructureManager structuremanager) {
|
|
||||||
delegate.applyBiomeDecoration(generatoraccessseed, ichunkaccess, structuremanager);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void addDebugScreenInfo(List<String> list, RandomState randomstate, BlockPos blockposition) {
|
|
||||||
delegate.addDebugScreenInfo(list, randomstate, blockposition);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void spawnOriginalMobs(WorldGenRegion regionlimitedworldaccess) {
|
|
||||||
delegate.spawnOriginalMobs(regionlimitedworldaccess);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getSpawnHeight(LevelHeightAccessor levelheightaccessor) {
|
|
||||||
return delegate.getSpawnHeight(levelheightaccessor);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getGenDepth() {
|
|
||||||
return delegate.getGenDepth();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public NoiseColumn getBaseColumn(int i, int j, LevelHeightAccessor levelheightaccessor, RandomState randomstate) {
|
|
||||||
return delegate.getBaseColumn(i, j, levelheightaccessor, randomstate);
|
|
||||||
}
|
|
||||||
|
|
||||||
static {
|
|
||||||
Field biomeSource = null;
|
|
||||||
for (Field field : ChunkGenerator.class.getDeclaredFields()) {
|
|
||||||
if (!field.getType().equals(BiomeSource.class))
|
|
||||||
continue;
|
|
||||||
biomeSource = field;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (biomeSource == null)
|
|
||||||
throw new RuntimeException("Could not find biomeSource field in ChunkGenerator!");
|
|
||||||
BIOME_SOURCE = new WrappedField<>(ChunkGenerator.class, biomeSource.getName());
|
|
||||||
}
|
|
||||||
|
|
||||||
private static ChunkGenerator edit(ChunkGenerator generator, BiomeSource source) {
|
|
||||||
try {
|
|
||||||
BIOME_SOURCE.set(generator, source);
|
|
||||||
if (generator instanceof CustomChunkGenerator custom)
|
|
||||||
BIOME_SOURCE.set(custom.getDelegate(), source);
|
|
||||||
|
|
||||||
return generator;
|
|
||||||
} catch (IllegalAccessException e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,628 +0,0 @@
|
|||||||
package com.volmit.iris.core.nms.v1_19_R2;
|
|
||||||
|
|
||||||
import java.awt.Color;
|
|
||||||
import java.io.ByteArrayInputStream;
|
|
||||||
import java.io.ByteArrayOutputStream;
|
|
||||||
import java.io.DataInputStream;
|
|
||||||
import java.io.DataOutputStream;
|
|
||||||
import java.lang.reflect.Field;
|
|
||||||
import java.lang.reflect.Method;
|
|
||||||
import java.lang.reflect.Modifier;
|
|
||||||
import java.util.Iterator;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Vector;
|
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
|
||||||
|
|
||||||
import com.mojang.datafixers.util.Pair;
|
|
||||||
import com.volmit.iris.core.nms.container.BiomeColor;
|
|
||||||
import com.volmit.iris.util.scheduling.J;
|
|
||||||
import net.minecraft.nbt.*;
|
|
||||||
import net.minecraft.nbt.Tag;
|
|
||||||
import net.minecraft.server.commands.data.BlockDataAccessor;
|
|
||||||
import net.minecraft.tags.TagKey;
|
|
||||||
import net.minecraft.world.level.LevelReader;
|
|
||||||
import net.minecraft.world.level.block.EntityBlock;
|
|
||||||
import org.bukkit.*;
|
|
||||||
import org.bukkit.block.Biome;
|
|
||||||
import org.bukkit.block.data.BlockData;
|
|
||||||
import org.bukkit.craftbukkit.v1_19_R2.CraftChunk;
|
|
||||||
import org.bukkit.craftbukkit.v1_19_R2.CraftServer;
|
|
||||||
import org.bukkit.craftbukkit.v1_19_R2.CraftWorld;
|
|
||||||
import org.bukkit.craftbukkit.v1_19_R2.block.CraftBlock;
|
|
||||||
import org.bukkit.craftbukkit.v1_19_R2.block.CraftBlockState;
|
|
||||||
import org.bukkit.craftbukkit.v1_19_R2.block.CraftBlockStates;
|
|
||||||
import org.bukkit.craftbukkit.v1_19_R2.block.data.CraftBlockData;
|
|
||||||
import org.bukkit.craftbukkit.v1_19_R2.entity.CraftDolphin;
|
|
||||||
import org.bukkit.craftbukkit.v1_19_R2.inventory.CraftItemStack;
|
|
||||||
import org.bukkit.entity.Dolphin;
|
|
||||||
import org.bukkit.entity.Entity;
|
|
||||||
import org.bukkit.event.entity.CreatureSpawnEvent;
|
|
||||||
import org.bukkit.generator.ChunkGenerator;
|
|
||||||
import org.bukkit.inventory.ItemStack;
|
|
||||||
import org.jetbrains.annotations.Contract;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
|
|
||||||
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
|
||||||
import com.volmit.iris.Iris;
|
|
||||||
import com.volmit.iris.core.nms.INMSBinding;
|
|
||||||
import com.volmit.iris.engine.data.cache.AtomicCache;
|
|
||||||
import com.volmit.iris.engine.framework.Engine;
|
|
||||||
import com.volmit.iris.util.collection.KList;
|
|
||||||
import com.volmit.iris.util.collection.KMap;
|
|
||||||
import com.volmit.iris.util.hunk.Hunk;
|
|
||||||
import com.volmit.iris.util.json.JSONObject;
|
|
||||||
import com.volmit.iris.util.mantle.Mantle;
|
|
||||||
import com.volmit.iris.util.math.Vector3d;
|
|
||||||
import com.volmit.iris.util.matter.MatterBiomeInject;
|
|
||||||
import com.volmit.iris.util.nbt.io.NBTUtil;
|
|
||||||
import com.volmit.iris.util.nbt.mca.NBTWorld;
|
|
||||||
import com.volmit.iris.util.nbt.mca.palette.*;
|
|
||||||
import com.volmit.iris.util.nbt.tag.CompoundTag;
|
|
||||||
|
|
||||||
import it.unimi.dsi.fastutil.objects.Object2IntMap;
|
|
||||||
import net.minecraft.core.BlockPos;
|
|
||||||
import net.minecraft.core.Holder;
|
|
||||||
import net.minecraft.core.Registry;
|
|
||||||
import net.minecraft.core.RegistryAccess;
|
|
||||||
import net.minecraft.core.registries.Registries;
|
|
||||||
import net.minecraft.resources.ResourceLocation;
|
|
||||||
import net.minecraft.server.level.ServerLevel;
|
|
||||||
import net.minecraft.world.entity.EntityType;
|
|
||||||
import net.minecraft.world.level.biome.BiomeSource;
|
|
||||||
import net.minecraft.world.level.block.Block;
|
|
||||||
import net.minecraft.world.level.block.entity.BlockEntity;
|
|
||||||
import net.minecraft.world.level.block.state.BlockState;
|
|
||||||
import net.minecraft.world.level.chunk.ChunkAccess;
|
|
||||||
import net.minecraft.world.level.chunk.LevelChunk;
|
|
||||||
import sun.misc.Unsafe;
|
|
||||||
|
|
||||||
public class NMSBinding implements INMSBinding {
|
|
||||||
private final KMap<Biome, Object> baseBiomeCache = new KMap<>();
|
|
||||||
private final BlockData AIR = Material.AIR.createBlockData();
|
|
||||||
private final AtomicCache<MCAIdMap<net.minecraft.world.level.biome.Biome>> biomeMapCache = new AtomicCache<>();
|
|
||||||
private final AtomicCache<MCAIdMapper<BlockState>> registryCache = new AtomicCache<>();
|
|
||||||
private final AtomicCache<MCAPalette<BlockState>> globalCache = new AtomicCache<>();
|
|
||||||
private final AtomicCache<RegistryAccess> registryAccess = new AtomicCache<>();
|
|
||||||
private final AtomicCache<Method> byIdRef = new AtomicCache<>();
|
|
||||||
private Field biomeStorageCache = null;
|
|
||||||
|
|
||||||
private static Object getFor(Class<?> type, Object source) {
|
|
||||||
Object o = fieldFor(type, source);
|
|
||||||
|
|
||||||
if (o != null) {
|
|
||||||
return o;
|
|
||||||
}
|
|
||||||
|
|
||||||
return invokeFor(type, source);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Object invokeFor(Class<?> returns, Object in) {
|
|
||||||
for (Method i : in.getClass().getMethods()) {
|
|
||||||
if (i.getReturnType().equals(returns)) {
|
|
||||||
i.setAccessible(true);
|
|
||||||
try {
|
|
||||||
Iris.debug("[NMS] Found " + returns.getSimpleName() + " in " + in.getClass().getSimpleName() + "." + i.getName() + "()");
|
|
||||||
return i.invoke(in);
|
|
||||||
} catch (Throwable e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Object fieldFor(Class<?> returns, Object in) {
|
|
||||||
return fieldForClass(returns, in.getClass(), in);
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
private static <T> T fieldForClass(Class<T> returnType, Class<?> sourceType, Object in) {
|
|
||||||
for (Field i : sourceType.getDeclaredFields()) {
|
|
||||||
if (i.getType().equals(returnType)) {
|
|
||||||
i.setAccessible(true);
|
|
||||||
try {
|
|
||||||
Iris.debug("[NMS] Found " + returnType.getSimpleName() + " in " + sourceType.getSimpleName() + "." + i.getName());
|
|
||||||
return (T) i.get(in);
|
|
||||||
} catch (IllegalAccessException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Class<?> getClassType(Class<?> type, int ordinal) {
|
|
||||||
return type.getDeclaredClasses()[ordinal];
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean hasTile(Material material) {
|
|
||||||
return !CraftBlockState.class.equals(CraftBlockStates.getBlockStateType(material));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean hasTile(Location l) {
|
|
||||||
return ((CraftWorld) l.getWorld()).getHandle().getBlockEntity(new BlockPos(l.getBlockX(), l.getBlockY(), l.getBlockZ()), false) != null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
public KMap<String, Object> serializeTile(Location location) {
|
|
||||||
BlockEntity e = ((CraftWorld) location.getWorld()).getHandle().getBlockEntity(new BlockPos(location.getBlockX(), location.getBlockY(), location.getBlockZ()), false);
|
|
||||||
|
|
||||||
if (e == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
net.minecraft.nbt.CompoundTag tag = e.saveWithoutMetadata();
|
|
||||||
return (KMap<String, Object>) convertFromTag(tag, 0, 64);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Contract(value = "null, _, _ -> null", pure = true)
|
|
||||||
private Object convertFromTag(net.minecraft.nbt.Tag tag, int depth, int maxDepth) {
|
|
||||||
if (tag == null || depth > maxDepth) return null;
|
|
||||||
if (tag instanceof CollectionTag<?> collection) {
|
|
||||||
KList<Object> list = new KList<>();
|
|
||||||
|
|
||||||
for (Object i : collection) {
|
|
||||||
if (i instanceof net.minecraft.nbt.Tag t)
|
|
||||||
list.add(convertFromTag(t, depth + 1, maxDepth));
|
|
||||||
else list.add(i);
|
|
||||||
}
|
|
||||||
return list;
|
|
||||||
}
|
|
||||||
if (tag instanceof net.minecraft.nbt.CompoundTag compound) {
|
|
||||||
KMap<String, Object> map = new KMap<>();
|
|
||||||
|
|
||||||
for (String key : compound.getAllKeys()) {
|
|
||||||
var child = compound.get(key);
|
|
||||||
if (child == null) continue;
|
|
||||||
var value = convertFromTag(child, depth + 1, maxDepth);
|
|
||||||
if (value == null) continue;
|
|
||||||
map.put(key, value);
|
|
||||||
}
|
|
||||||
return map;
|
|
||||||
}
|
|
||||||
if (tag instanceof NumericTag numeric)
|
|
||||||
return numeric.getAsNumber();
|
|
||||||
return tag.getAsString();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void deserializeTile(KMap<String, Object> map, Location pos) {
|
|
||||||
net.minecraft.nbt.CompoundTag tag = (net.minecraft.nbt.CompoundTag) convertToTag(map, 0, 64);
|
|
||||||
var level = ((CraftWorld) pos.getWorld()).getHandle();
|
|
||||||
var blockPos = new BlockPos(pos.getBlockX(), pos.getBlockY(), pos.getBlockZ());
|
|
||||||
J.s(() -> merge(level, blockPos, tag));
|
|
||||||
}
|
|
||||||
|
|
||||||
private void merge(ServerLevel level, BlockPos blockPos, net.minecraft.nbt.CompoundTag tag) {
|
|
||||||
var blockEntity = level.getBlockEntity(blockPos);
|
|
||||||
if (blockEntity == null) {
|
|
||||||
Iris.warn("[NMS] BlockEntity not found at " + blockPos);
|
|
||||||
var state = level.getBlockState(blockPos);
|
|
||||||
if (!state.hasBlockEntity())
|
|
||||||
return;
|
|
||||||
|
|
||||||
blockEntity = ((EntityBlock) state.getBlock())
|
|
||||||
.newBlockEntity(blockPos, state);
|
|
||||||
}
|
|
||||||
var accessor = new BlockDataAccessor(blockEntity, blockPos);
|
|
||||||
accessor.setData(tag.merge(accessor.getData()));
|
|
||||||
}
|
|
||||||
|
|
||||||
private Tag convertToTag(Object object, int depth, int maxDepth) {
|
|
||||||
if (object == null || depth > maxDepth) return EndTag.INSTANCE;
|
|
||||||
if (object instanceof Map<?,?> map) {
|
|
||||||
var tag = new net.minecraft.nbt.CompoundTag();
|
|
||||||
for (var i : map.entrySet()) {
|
|
||||||
tag.put(i.getKey().toString(), convertToTag(i.getValue(), depth + 1, maxDepth));
|
|
||||||
}
|
|
||||||
return tag;
|
|
||||||
}
|
|
||||||
if (object instanceof List<?> list) {
|
|
||||||
var tag = new net.minecraft.nbt.ListTag();
|
|
||||||
for (var i : list) {
|
|
||||||
tag.add(convertToTag(i, depth + 1, maxDepth));
|
|
||||||
}
|
|
||||||
return tag;
|
|
||||||
}
|
|
||||||
if (object instanceof Byte number) return ByteTag.valueOf(number);
|
|
||||||
if (object instanceof Short number) return ShortTag.valueOf(number);
|
|
||||||
if (object instanceof Integer number) return IntTag.valueOf(number);
|
|
||||||
if (object instanceof Long number) return LongTag.valueOf(number);
|
|
||||||
if (object instanceof Float number) return FloatTag.valueOf(number);
|
|
||||||
if (object instanceof Double number) return DoubleTag.valueOf(number);
|
|
||||||
if (object instanceof String string) return StringTag.valueOf(string);
|
|
||||||
return EndTag.INSTANCE;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public CompoundTag serializeEntity(Entity location) {
|
|
||||||
return null;// TODO:
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Entity deserializeEntity(CompoundTag s, Location newPosition) {
|
|
||||||
return null;// TODO:
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean supportsCustomHeight() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private RegistryAccess registry() {
|
|
||||||
return registryAccess.aquire(() -> (RegistryAccess) getFor(RegistryAccess.Frozen.class, ((CraftServer) Bukkit.getServer()).getHandle().getServer()));
|
|
||||||
}
|
|
||||||
|
|
||||||
private Registry<net.minecraft.world.level.biome.Biome> getCustomBiomeRegistry() {
|
|
||||||
return registry().registry(Registries.BIOME).orElse(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
private Registry<Block> getBlockRegistry() {
|
|
||||||
return registry().registry(Registries.BLOCK).orElse(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Object getBiomeBaseFromId(int id) {
|
|
||||||
return getCustomBiomeRegistry().getHolder(id);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getMinHeight(World world) {
|
|
||||||
return world.getMinHeight();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean supportsCustomBiomes() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getTrueBiomeBaseId(Object biomeBase) {
|
|
||||||
return getCustomBiomeRegistry().getId(((Holder<net.minecraft.world.level.biome.Biome>) biomeBase).value());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Object getTrueBiomeBase(Location location) {
|
|
||||||
return ((CraftWorld) location.getWorld()).getHandle().getBiome(new BlockPos(location.getBlockX(), location.getBlockY(), location.getBlockZ()));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getTrueBiomeBaseKey(Location location) {
|
|
||||||
return getKeyForBiomeBase(getTrueBiomeBase(location));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Object getCustomBiomeBaseFor(String mckey) {
|
|
||||||
return getCustomBiomeRegistry().get(new ResourceLocation(mckey));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Object getCustomBiomeBaseHolderFor(String mckey) {
|
|
||||||
return getCustomBiomeRegistry().getHolder(getTrueBiomeBaseId(getCustomBiomeRegistry().get(new ResourceLocation(mckey)))).get();
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getBiomeBaseIdForKey(String key) {
|
|
||||||
return getCustomBiomeRegistry().getId(getCustomBiomeRegistry().get(new ResourceLocation(key)));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getKeyForBiomeBase(Object biomeBase) {
|
|
||||||
return getCustomBiomeRegistry().getKey((net.minecraft.world.level.biome.Biome) biomeBase).getPath(); // something, not something:something
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Object getBiomeBase(World world, Biome biome) {
|
|
||||||
return CraftBlock.biomeToBiomeBase(((CraftWorld) world).getHandle()
|
|
||||||
.registryAccess().registry(Registries.BIOME).orElse(null), biome);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Object getBiomeBase(Object registry, Biome biome) {
|
|
||||||
Object v = baseBiomeCache.get(biome);
|
|
||||||
|
|
||||||
if (v != null) {
|
|
||||||
return v;
|
|
||||||
}
|
|
||||||
//noinspection unchecked
|
|
||||||
v = CraftBlock.biomeToBiomeBase((Registry<net.minecraft.world.level.biome.Biome>) registry, biome);
|
|
||||||
if (v == null) {
|
|
||||||
// Ok so there is this new biome name called "CUSTOM" in Paper's new releases.
|
|
||||||
// But, this does NOT exist within CraftBukkit which makes it return an error.
|
|
||||||
// So, we will just return the ID that the plains biome returns instead.
|
|
||||||
//noinspection unchecked
|
|
||||||
return CraftBlock.biomeToBiomeBase((Registry<net.minecraft.world.level.biome.Biome>) registry, Biome.PLAINS);
|
|
||||||
}
|
|
||||||
baseBiomeCache.put(biome, v);
|
|
||||||
return v;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public KList<Biome> getBiomes() {
|
|
||||||
return new KList<>(Biome.values()).qdel(Biome.CUSTOM);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isBukkit() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getBiomeId(Biome biome) {
|
|
||||||
for (World i : Bukkit.getWorlds()) {
|
|
||||||
if (i.getEnvironment().equals(World.Environment.NORMAL)) {
|
|
||||||
Registry<net.minecraft.world.level.biome.Biome> registry = ((CraftWorld) i).getHandle().registryAccess().registry(Registries.BIOME).orElse(null);
|
|
||||||
return registry.getId((net.minecraft.world.level.biome.Biome) getBiomeBase(registry, biome));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return biome.ordinal();
|
|
||||||
}
|
|
||||||
|
|
||||||
private MCAIdMap<net.minecraft.world.level.biome.Biome> getBiomeMapping() {
|
|
||||||
return biomeMapCache.aquire(() -> new MCAIdMap<>() {
|
|
||||||
@NotNull
|
|
||||||
@Override
|
|
||||||
public Iterator<net.minecraft.world.level.biome.Biome> iterator() {
|
|
||||||
return getCustomBiomeRegistry().iterator();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getId(net.minecraft.world.level.biome.Biome paramT) {
|
|
||||||
return getCustomBiomeRegistry().getId(paramT);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public net.minecraft.world.level.biome.Biome byId(int paramInt) {
|
|
||||||
return (net.minecraft.world.level.biome.Biome) getBiomeBaseFromId(paramInt);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
private MCABiomeContainer getBiomeContainerInterface(MCAIdMap<net.minecraft.world.level.biome.Biome> biomeMapping, MCAChunkBiomeContainer<net.minecraft.world.level.biome.Biome> base) {
|
|
||||||
return new MCABiomeContainer() {
|
|
||||||
@Override
|
|
||||||
public int[] getData() {
|
|
||||||
return base.writeBiomes();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setBiome(int x, int y, int z, int id) {
|
|
||||||
base.setBiome(x, y, z, biomeMapping.byId(id));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getBiome(int x, int y, int z) {
|
|
||||||
return biomeMapping.getId(base.getBiome(x, y, z));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public MCABiomeContainer newBiomeContainer(int min, int max) {
|
|
||||||
MCAChunkBiomeContainer<net.minecraft.world.level.biome.Biome> base = new MCAChunkBiomeContainer<>(getBiomeMapping(), min, max);
|
|
||||||
return getBiomeContainerInterface(getBiomeMapping(), base);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public MCABiomeContainer newBiomeContainer(int min, int max, int[] data) {
|
|
||||||
MCAChunkBiomeContainer<net.minecraft.world.level.biome.Biome> base = new MCAChunkBiomeContainer<>(getBiomeMapping(), min, max, data);
|
|
||||||
return getBiomeContainerInterface(getBiomeMapping(), base);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int countCustomBiomes() {
|
|
||||||
AtomicInteger a = new AtomicInteger(0);
|
|
||||||
|
|
||||||
getCustomBiomeRegistry().keySet().forEach((i) -> {
|
|
||||||
if (i.getNamespace().equals("minecraft")) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
a.incrementAndGet();
|
|
||||||
Iris.debug("Custom Biome: " + i);
|
|
||||||
});
|
|
||||||
|
|
||||||
return a.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean supportsDataPacks() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setBiomes(int cx, int cz, World world, Hunk<Object> biomes) {
|
|
||||||
LevelChunk c = ((CraftWorld) world).getHandle().getChunk(cx, cz);
|
|
||||||
biomes.iterateSync((x, y, z, b) -> c.setBiome(x, y, z, (Holder<net.minecraft.world.level.biome.Biome>) b));
|
|
||||||
c.setUnsaved(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void forceBiomeInto(int x, int y, int z, Object somethingVeryDirty, ChunkGenerator.BiomeGrid chunk) {
|
|
||||||
try {
|
|
||||||
ChunkAccess s = (ChunkAccess) getFieldForBiomeStorage(chunk).get(chunk);
|
|
||||||
Holder<net.minecraft.world.level.biome.Biome> biome = (Holder<net.minecraft.world.level.biome.Biome>) somethingVeryDirty;
|
|
||||||
s.setBiome(x, y, z, biome);
|
|
||||||
} catch (IllegalAccessException e) {
|
|
||||||
Iris.reportError(e);
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private Field getFieldForBiomeStorage(Object storage) {
|
|
||||||
Field f = biomeStorageCache;
|
|
||||||
|
|
||||||
if (f != null) {
|
|
||||||
return f;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
f = storage.getClass().getDeclaredField("biome");
|
|
||||||
f.setAccessible(true);
|
|
||||||
return f;
|
|
||||||
} catch (Throwable e) {
|
|
||||||
Iris.reportError(e);
|
|
||||||
e.printStackTrace();
|
|
||||||
Iris.error(storage.getClass().getCanonicalName());
|
|
||||||
}
|
|
||||||
|
|
||||||
biomeStorageCache = f;
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public MCAPaletteAccess createPalette() {
|
|
||||||
MCAIdMapper<BlockState> registry = registryCache.aquireNasty(() -> {
|
|
||||||
Field cf = net.minecraft.core.IdMapper.class.getDeclaredField("tToId");
|
|
||||||
Field df = net.minecraft.core.IdMapper.class.getDeclaredField("idToT");
|
|
||||||
Field bf = net.minecraft.core.IdMapper.class.getDeclaredField("nextId");
|
|
||||||
cf.setAccessible(true);
|
|
||||||
df.setAccessible(true);
|
|
||||||
bf.setAccessible(true);
|
|
||||||
net.minecraft.core.IdMapper<BlockState> blockData = Block.BLOCK_STATE_REGISTRY;
|
|
||||||
int b = bf.getInt(blockData);
|
|
||||||
Object2IntMap<BlockState> c = (Object2IntMap<BlockState>) cf.get(blockData);
|
|
||||||
List<BlockState> d = (List<BlockState>) df.get(blockData);
|
|
||||||
return new MCAIdMapper<BlockState>(c, d, b);
|
|
||||||
});
|
|
||||||
MCAPalette<BlockState> global = globalCache.aquireNasty(() -> new MCAGlobalPalette<>(registry, ((CraftBlockData) AIR).getState()));
|
|
||||||
MCAPalettedContainer<BlockState> container = new MCAPalettedContainer<>(global, registry,
|
|
||||||
i -> ((CraftBlockData) NBTWorld.getBlockData(i)).getState(),
|
|
||||||
i -> NBTWorld.getCompound(CraftBlockData.fromData(i)),
|
|
||||||
((CraftBlockData) AIR).getState());
|
|
||||||
return new MCAWrappedPalettedContainer<>(container,
|
|
||||||
i -> NBTWorld.getCompound(CraftBlockData.fromData(i)),
|
|
||||||
i -> ((CraftBlockData) NBTWorld.getBlockData(i)).getState());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void injectBiomesFromMantle(Chunk e, Mantle mantle) {
|
|
||||||
ChunkAccess chunk = ((CraftChunk) e).getHandle();
|
|
||||||
AtomicInteger c = new AtomicInteger();
|
|
||||||
AtomicInteger r = new AtomicInteger();
|
|
||||||
mantle.iterateChunk(e.getX(), e.getZ(), MatterBiomeInject.class, (x, y, z, b) -> {
|
|
||||||
if (b != null) {
|
|
||||||
if (b.isCustom()) {
|
|
||||||
chunk.setBiome(x, y, z, getCustomBiomeRegistry().getHolder(b.getBiomeId()).get());
|
|
||||||
c.getAndIncrement();
|
|
||||||
} else {
|
|
||||||
chunk.setBiome(x, y, z, (Holder<net.minecraft.world.level.biome.Biome>) getBiomeBase(e.getWorld(), b.getBiome()));
|
|
||||||
r.getAndIncrement();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public ItemStack applyCustomNbt(ItemStack itemStack, KMap<String, Object> customNbt) throws IllegalArgumentException {
|
|
||||||
if (customNbt != null && !customNbt.isEmpty()) {
|
|
||||||
net.minecraft.world.item.ItemStack s = CraftItemStack.asNMSCopy(itemStack);
|
|
||||||
|
|
||||||
try {
|
|
||||||
net.minecraft.nbt.CompoundTag tag = TagParser.parseTag((new JSONObject(customNbt)).toString());
|
|
||||||
tag.merge(s.getOrCreateTag());
|
|
||||||
s.setTag(tag);
|
|
||||||
} catch (CommandSyntaxException var5) {
|
|
||||||
throw new IllegalArgumentException(var5);
|
|
||||||
}
|
|
||||||
|
|
||||||
return CraftItemStack.asBukkitCopy(s);
|
|
||||||
} else {
|
|
||||||
return itemStack;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void inject(long seed, Engine engine, World world) {
|
|
||||||
var chunkMap = ((CraftWorld)world).getHandle().getChunkSource().chunkMap;
|
|
||||||
chunkMap.generator = new IrisChunkGenerator(chunkMap.generator, seed, engine, world);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public Vector3d getBoundingbox(org.bukkit.entity.EntityType entity) {
|
|
||||||
Field[] fields = EntityType.class.getDeclaredFields();
|
|
||||||
for (Field field : fields) {
|
|
||||||
if (Modifier.isStatic(field.getModifiers()) && field.getType().equals(EntityType.class)) {
|
|
||||||
try {
|
|
||||||
EntityType entityType = (EntityType) field.get(null);
|
|
||||||
if (entityType.getDescriptionId().equals("entity.minecraft." + entity.name().toLowerCase())) {
|
|
||||||
Vector<Float> v1 = new Vector<>();
|
|
||||||
v1.add(entityType.getHeight());
|
|
||||||
entityType.getDimensions();
|
|
||||||
Vector3d box = new Vector3d( entityType.getWidth(), entityType.getHeight(), entityType.getWidth());
|
|
||||||
//System.out.println("Entity Type: " + entityType.getDescriptionId() + ", " + "Height: " + height + ", Width: " + width);
|
|
||||||
return box;
|
|
||||||
}
|
|
||||||
} catch (IllegalAccessException e) {
|
|
||||||
Iris.error("Unable to get entity dimensions!");
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Entity spawnEntity(Location location, org.bukkit.entity.EntityType type, CreatureSpawnEvent.SpawnReason reason) {
|
|
||||||
return ((CraftWorld) location.getWorld()).spawn(location, type.getEntityClass(), null, reason);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Color getBiomeColor(Location location, BiomeColor type) {
|
|
||||||
LevelReader reader = ((CraftWorld) location.getWorld()).getHandle();
|
|
||||||
var holder = reader.getBiome(new BlockPos(location.getBlockX(), location.getBlockY(), location.getBlockZ()));
|
|
||||||
var biome = holder.value();
|
|
||||||
if (biome == null) throw new IllegalArgumentException("Invalid biome: " + holder.unwrapKey().orElse(null));
|
|
||||||
|
|
||||||
int rgba = switch (type) {
|
|
||||||
case FOG -> biome.getFogColor();
|
|
||||||
case WATER -> biome.getWaterColor();
|
|
||||||
case WATER_FOG -> biome.getWaterFogColor();
|
|
||||||
case SKY -> biome.getSkyColor();
|
|
||||||
case FOLIAGE -> biome.getFoliageColor();
|
|
||||||
case GRASS -> biome.getGrassColor(location.getBlockX(), location.getBlockZ());
|
|
||||||
};
|
|
||||||
if (rgba == 0) {
|
|
||||||
if (BiomeColor.FOLIAGE == type && biome.getSpecialEffects().getFoliageColorOverride().isEmpty())
|
|
||||||
return null;
|
|
||||||
if (BiomeColor.GRASS == type && biome.getSpecialEffects().getGrassColorOverride().isEmpty())
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return new Color(rgba, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public KList<String> getStructureKeys() {
|
|
||||||
KList<String> keys = new KList<>();
|
|
||||||
|
|
||||||
var registry = registry().registry(Registries.STRUCTURE).orElse(null);
|
|
||||||
if (registry == null) return keys;
|
|
||||||
registry.keySet().stream().map(ResourceLocation::toString).forEach(keys::add);
|
|
||||||
registry.getTags()
|
|
||||||
.map(Pair::getFirst)
|
|
||||||
.map(TagKey::location)
|
|
||||||
.map(ResourceLocation::toString)
|
|
||||||
.map(s -> "#" + s)
|
|
||||||
.forEach(keys::add);
|
|
||||||
|
|
||||||
return keys;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Field getField(Class<?> clazz, Class<?> fieldType) throws NoSuchFieldException {
|
|
||||||
try {
|
|
||||||
for (Field f : clazz.getDeclaredFields()) {
|
|
||||||
if (f.getType().equals(fieldType))
|
|
||||||
return f;
|
|
||||||
}
|
|
||||||
throw new NoSuchFieldException(fieldType.getName());
|
|
||||||
} catch (NoSuchFieldException var4) {
|
|
||||||
Class<?> superClass = clazz.getSuperclass();
|
|
||||||
if (superClass == null) {
|
|
||||||
throw var4;
|
|
||||||
} else {
|
|
||||||
return getField(superClass, fieldType);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,169 +0,0 @@
|
|||||||
package com.volmit.iris.core.nms.v1_19_R3;
|
|
||||||
|
|
||||||
import com.mojang.serialization.Codec;
|
|
||||||
import com.volmit.iris.Iris;
|
|
||||||
import com.volmit.iris.engine.data.cache.AtomicCache;
|
|
||||||
import com.volmit.iris.engine.framework.Engine;
|
|
||||||
import com.volmit.iris.engine.object.IrisBiome;
|
|
||||||
import com.volmit.iris.engine.object.IrisBiomeCustom;
|
|
||||||
import com.volmit.iris.util.collection.KMap;
|
|
||||||
import com.volmit.iris.util.math.RNG;
|
|
||||||
import net.minecraft.core.Holder;
|
|
||||||
import net.minecraft.core.Registry;
|
|
||||||
import net.minecraft.core.RegistryAccess;
|
|
||||||
import net.minecraft.core.registries.Registries;
|
|
||||||
import net.minecraft.resources.ResourceKey;
|
|
||||||
import net.minecraft.resources.ResourceLocation;
|
|
||||||
import net.minecraft.world.level.biome.Biome;
|
|
||||||
import net.minecraft.world.level.biome.BiomeSource;
|
|
||||||
import net.minecraft.world.level.biome.Climate;
|
|
||||||
import org.bukkit.Bukkit;
|
|
||||||
import org.bukkit.World;
|
|
||||||
import org.bukkit.craftbukkit.v1_19_R3.CraftServer;
|
|
||||||
import org.bukkit.craftbukkit.v1_19_R3.CraftWorld;
|
|
||||||
import org.bukkit.craftbukkit.v1_19_R3.block.CraftBlock;
|
|
||||||
import java.lang.reflect.Field;
|
|
||||||
import java.lang.reflect.Method;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Optional;
|
|
||||||
import java.util.stream.Stream;
|
|
||||||
|
|
||||||
public class CustomBiomeSource extends BiomeSource {
|
|
||||||
|
|
||||||
private final long seed;
|
|
||||||
private final Engine engine;
|
|
||||||
private final Registry<Biome> biomeCustomRegistry;
|
|
||||||
private final Registry<Biome> biomeRegistry;
|
|
||||||
private final AtomicCache<RegistryAccess> registryAccess = new AtomicCache<>();
|
|
||||||
private final RNG rng;
|
|
||||||
private final KMap<String, Holder<Biome>> customBiomes;
|
|
||||||
|
|
||||||
public CustomBiomeSource(long seed, Engine engine, World world) {
|
|
||||||
this.engine = engine;
|
|
||||||
this.seed = seed;
|
|
||||||
this.biomeCustomRegistry = registry().registry(Registries.BIOME).orElse(null);
|
|
||||||
this.biomeRegistry = ((RegistryAccess) getFor(RegistryAccess.Frozen.class, ((CraftServer) Bukkit.getServer()).getHandle().getServer())).registry(Registries.BIOME).orElse(null);
|
|
||||||
this.rng = new RNG(engine.getSeedManager().getBiome());
|
|
||||||
this.customBiomes = fillCustomBiomes(biomeCustomRegistry, engine);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static List<Holder<Biome>> getAllBiomes(Registry<Biome> customRegistry, Registry<Biome> registry, Engine engine) {
|
|
||||||
List<Holder<Biome>> b = new ArrayList<>();
|
|
||||||
|
|
||||||
for (IrisBiome i : engine.getAllBiomes()) {
|
|
||||||
if (i.isCustom()) {
|
|
||||||
for (IrisBiomeCustom j : i.getCustomDerivitives()) {
|
|
||||||
b.add(customRegistry.getHolder(customRegistry.getResourceKey(customRegistry
|
|
||||||
.get(new ResourceLocation(engine.getDimension().getLoadKey() + ":" + j.getId()))).get()).get());
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
b.add(CraftBlock.biomeToBiomeBase(registry, i.getVanillaDerivative()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return b;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Object getFor(Class<?> type, Object source) {
|
|
||||||
Object o = fieldFor(type, source);
|
|
||||||
|
|
||||||
if (o != null) {
|
|
||||||
return o;
|
|
||||||
}
|
|
||||||
|
|
||||||
return invokeFor(type, source);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Object fieldFor(Class<?> returns, Object in) {
|
|
||||||
return fieldForClass(returns, in.getClass(), in);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Object invokeFor(Class<?> returns, Object in) {
|
|
||||||
for (Method i : in.getClass().getMethods()) {
|
|
||||||
if (i.getReturnType().equals(returns)) {
|
|
||||||
i.setAccessible(true);
|
|
||||||
try {
|
|
||||||
Iris.debug("[NMS] Found " + returns.getSimpleName() + " in " + in.getClass().getSimpleName() + "." + i.getName() + "()");
|
|
||||||
return i.invoke(in);
|
|
||||||
} catch (Throwable e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
private static <T> T fieldForClass(Class<T> returnType, Class<?> sourceType, Object in) {
|
|
||||||
for (Field i : sourceType.getDeclaredFields()) {
|
|
||||||
if (i.getType().equals(returnType)) {
|
|
||||||
i.setAccessible(true);
|
|
||||||
try {
|
|
||||||
Iris.debug("[NMS] Found " + returnType.getSimpleName() + " in " + sourceType.getSimpleName() + "." + i.getName());
|
|
||||||
return (T) i.get(in);
|
|
||||||
} catch (IllegalAccessException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected Stream<Holder<Biome>> collectPossibleBiomes() {
|
|
||||||
return getAllBiomes(
|
|
||||||
((RegistryAccess) getFor(RegistryAccess.Frozen.class, ((CraftServer) Bukkit.getServer()).getHandle().getServer()))
|
|
||||||
.registry(Registries.BIOME).orElse(null),
|
|
||||||
((CraftWorld) engine.getWorld().realWorld()).getHandle().registryAccess().registry(Registries.BIOME).orElse(null),
|
|
||||||
engine).stream();
|
|
||||||
}
|
|
||||||
private KMap<String, Holder<Biome>> fillCustomBiomes(Registry<Biome> customRegistry, Engine engine) {
|
|
||||||
KMap<String, Holder<Biome>> m = new KMap<>();
|
|
||||||
|
|
||||||
for (IrisBiome i : engine.getAllBiomes()) {
|
|
||||||
if (i.isCustom()) {
|
|
||||||
for (IrisBiomeCustom j : i.getCustomDerivitives()) {
|
|
||||||
ResourceLocation resourceLocation = new ResourceLocation(engine.getDimension().getLoadKey() + ":" + j.getId());
|
|
||||||
Biome biome = customRegistry.get(resourceLocation);
|
|
||||||
Optional<ResourceKey<Biome>> optionalBiomeKey = customRegistry.getResourceKey(biome);
|
|
||||||
if (optionalBiomeKey.isEmpty()) {
|
|
||||||
Iris.error("Cannot find biome for IrisBiomeCustom " + j.getId() + " from engine " + engine.getName());
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
ResourceKey<Biome> biomeKey = optionalBiomeKey.get();
|
|
||||||
Optional<Holder.Reference<Biome>> optionalReferenceHolder = customRegistry.getHolder(biomeKey);
|
|
||||||
if (optionalReferenceHolder.isEmpty()) {
|
|
||||||
Iris.error("Cannot find reference to biome " + biomeKey + " for engine " + engine.getName());
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
m.put(j.getId(), optionalReferenceHolder.get());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return m;
|
|
||||||
}
|
|
||||||
|
|
||||||
private RegistryAccess registry() {
|
|
||||||
return registryAccess.aquire(() -> (RegistryAccess) getFor(RegistryAccess.Frozen.class, ((CraftServer) Bukkit.getServer()).getHandle().getServer()));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected Codec<? extends BiomeSource> codec() {
|
|
||||||
throw new UnsupportedOperationException("Not supported");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Holder<Biome> getNoiseBiome(int x, int y, int z, Climate.Sampler sampler) {
|
|
||||||
int m = (y - engine.getMinHeight()) << 2;
|
|
||||||
IrisBiome ib = engine.getComplex().getTrueBiomeStream().get(x << 2, z << 2);
|
|
||||||
if (ib.isCustom()) {
|
|
||||||
return customBiomes.get(ib.getCustomBiome(rng, x << 2, m, z << 2).getId());
|
|
||||||
} else {
|
|
||||||
org.bukkit.block.Biome v = ib.getSkyBiome(rng, x << 2, m, z << 2);
|
|
||||||
return CraftBlock.biomeToBiomeBase(biomeRegistry, v);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,262 +0,0 @@
|
|||||||
package com.volmit.iris.core.nms.v1_19_R3;
|
|
||||||
|
|
||||||
import com.mojang.datafixers.util.Pair;
|
|
||||||
import com.mojang.serialization.Codec;
|
|
||||||
import com.volmit.iris.Iris;
|
|
||||||
import com.volmit.iris.engine.framework.Engine;
|
|
||||||
import com.volmit.iris.engine.framework.ResultLocator;
|
|
||||||
import com.volmit.iris.engine.framework.WrongEngineBroException;
|
|
||||||
import com.volmit.iris.engine.object.IrisJigsawStructure;
|
|
||||||
import com.volmit.iris.engine.object.IrisJigsawStructurePlacement;
|
|
||||||
import com.volmit.iris.util.collection.KList;
|
|
||||||
import com.volmit.iris.util.collection.KMap;
|
|
||||||
import com.volmit.iris.util.collection.KSet;
|
|
||||||
import com.volmit.iris.util.mantle.MantleFlag;
|
|
||||||
import com.volmit.iris.util.math.Position2;
|
|
||||||
import com.volmit.iris.util.reflect.WrappedField;
|
|
||||||
import net.minecraft.core.BlockPos;
|
|
||||||
import net.minecraft.core.Holder;
|
|
||||||
import net.minecraft.core.HolderSet;
|
|
||||||
import net.minecraft.core.RegistryAccess;
|
|
||||||
import net.minecraft.core.registries.Registries;
|
|
||||||
import net.minecraft.resources.ResourceKey;
|
|
||||||
import net.minecraft.resources.ResourceLocation;
|
|
||||||
import net.minecraft.server.level.ServerLevel;
|
|
||||||
import net.minecraft.server.level.WorldGenRegion;
|
|
||||||
import net.minecraft.tags.TagKey;
|
|
||||||
import net.minecraft.util.random.WeightedRandomList;
|
|
||||||
import net.minecraft.world.entity.MobCategory;
|
|
||||||
import net.minecraft.world.level.LevelHeightAccessor;
|
|
||||||
import net.minecraft.world.level.NoiseColumn;
|
|
||||||
import net.minecraft.world.level.StructureManager;
|
|
||||||
import net.minecraft.world.level.WorldGenLevel;
|
|
||||||
import net.minecraft.world.level.biome.Biome;
|
|
||||||
import net.minecraft.world.level.biome.BiomeManager;
|
|
||||||
import net.minecraft.world.level.biome.BiomeSource;
|
|
||||||
import net.minecraft.world.level.biome.MobSpawnSettings;
|
|
||||||
import net.minecraft.world.level.chunk.ChunkAccess;
|
|
||||||
import net.minecraft.world.level.chunk.ChunkGenerator;
|
|
||||||
import net.minecraft.world.level.chunk.ChunkGeneratorStructureState;
|
|
||||||
import net.minecraft.world.level.levelgen.GenerationStep;
|
|
||||||
import net.minecraft.world.level.levelgen.Heightmap;
|
|
||||||
import net.minecraft.world.level.levelgen.RandomState;
|
|
||||||
import net.minecraft.world.level.levelgen.blending.Blender;
|
|
||||||
import net.minecraft.world.level.levelgen.structure.Structure;
|
|
||||||
import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplateManager;
|
|
||||||
import org.bukkit.World;
|
|
||||||
import org.bukkit.craftbukkit.v1_19_R3.CraftWorld;
|
|
||||||
import org.bukkit.craftbukkit.v1_19_R3.generator.CustomChunkGenerator;
|
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
|
||||||
import java.lang.reflect.Field;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Objects;
|
|
||||||
import java.util.concurrent.CompletableFuture;
|
|
||||||
import java.util.concurrent.ExecutionException;
|
|
||||||
import java.util.concurrent.Executor;
|
|
||||||
|
|
||||||
public class IrisChunkGenerator extends CustomChunkGenerator {
|
|
||||||
private static final WrappedField<ChunkGenerator, BiomeSource> BIOME_SOURCE;
|
|
||||||
private final ChunkGenerator delegate;
|
|
||||||
private final Engine engine;
|
|
||||||
private final KMap<ResourceKey<Structure>, KSet<String>> structures = new KMap<>();
|
|
||||||
|
|
||||||
public IrisChunkGenerator(ChunkGenerator delegate, long seed, Engine engine, World world) {
|
|
||||||
super(((CraftWorld) world).getHandle(), edit(delegate, new CustomBiomeSource(seed, engine, world)), null);
|
|
||||||
this.delegate = delegate;
|
|
||||||
this.engine = engine;
|
|
||||||
var dimension = engine.getDimension();
|
|
||||||
|
|
||||||
KSet<IrisJigsawStructure> placements = new KSet<>();
|
|
||||||
addAll(dimension.getJigsawStructures(), placements);
|
|
||||||
for (var region : dimension.getAllRegions(engine)) {
|
|
||||||
addAll(region.getJigsawStructures(), placements);
|
|
||||||
for (var biome : region.getAllBiomes(engine))
|
|
||||||
addAll(biome.getJigsawStructures(), placements);
|
|
||||||
}
|
|
||||||
var stronghold = dimension.getStronghold();
|
|
||||||
if (stronghold != null)
|
|
||||||
placements.add(engine.getData().getJigsawStructureLoader().load(stronghold));
|
|
||||||
placements.removeIf(Objects::isNull);
|
|
||||||
|
|
||||||
var registry = ((CraftWorld) world).getHandle().registryAccess().registry(Registries.STRUCTURE).orElseThrow();
|
|
||||||
for (var s : placements) {
|
|
||||||
try {
|
|
||||||
String raw = s.getStructureKey();
|
|
||||||
if (raw == null) continue;
|
|
||||||
boolean tag = raw.startsWith("#");
|
|
||||||
if (tag) raw = raw.substring(1);
|
|
||||||
|
|
||||||
var location = new ResourceLocation(raw);
|
|
||||||
if (!tag) {
|
|
||||||
structures.computeIfAbsent(ResourceKey.create(Registries.STRUCTURE, location), k -> new KSet<>()).add(s.getLoadKey());
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
var key = TagKey.create(Registries.STRUCTURE, location);
|
|
||||||
var set = registry.getTag(key).orElse(null);
|
|
||||||
if (set == null) {
|
|
||||||
Iris.error("Could not find structure tag: " + raw);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
for (var holder : set) {
|
|
||||||
var resourceKey = holder.unwrapKey().orElse(null);
|
|
||||||
if (resourceKey == null) continue;
|
|
||||||
structures.computeIfAbsent(resourceKey, k -> new KSet<>()).add(s.getLoadKey());
|
|
||||||
}
|
|
||||||
} catch (Throwable e) {
|
|
||||||
Iris.error("Failed to load structure: " + s.getLoadKey());
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void addAll(KList<IrisJigsawStructurePlacement> placements, KSet<IrisJigsawStructure> structures) {
|
|
||||||
if (placements == null) return;
|
|
||||||
placements.stream()
|
|
||||||
.map(IrisJigsawStructurePlacement::getStructure)
|
|
||||||
.map(engine.getData().getJigsawStructureLoader()::load)
|
|
||||||
.filter(Objects::nonNull)
|
|
||||||
.forEach(structures::add);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public @Nullable Pair<BlockPos, Holder<Structure>> findNearestMapStructure(ServerLevel level, HolderSet<Structure> holders, BlockPos pos, int radius, boolean findUnexplored) {
|
|
||||||
if (engine.getDimension().isDisableExplorerMaps())
|
|
||||||
return null;
|
|
||||||
|
|
||||||
KMap<String, Holder<Structure>> structures = new KMap<>();
|
|
||||||
for (var holder : holders) {
|
|
||||||
if (holder == null) continue;
|
|
||||||
var key = holder.unwrapKey().orElse(null);
|
|
||||||
var set = this.structures.get(key);
|
|
||||||
if (set == null) continue;
|
|
||||||
for (var structure : set) {
|
|
||||||
structures.put(structure, holder);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (structures.isEmpty())
|
|
||||||
return null;
|
|
||||||
|
|
||||||
var locator = ResultLocator.locateStructure(structures.keySet())
|
|
||||||
.then((e, p , s) -> structures.get(s.getLoadKey()));
|
|
||||||
if (findUnexplored)
|
|
||||||
locator = locator.then((e, p, s) -> e.getMantle().getMantle().getChunk(p.getX(), p.getZ()).isFlagged(MantleFlag.DISCOVERED) ? null : s);
|
|
||||||
|
|
||||||
try {
|
|
||||||
var result = locator.find(engine, new Position2(pos.getX() >> 4, pos.getZ() >> 4), radius * 10L, i -> {}, false).get();
|
|
||||||
if (result == null) return null;
|
|
||||||
var blockPos = new BlockPos(result.getBlockX(), 0, result.getBlockZ());
|
|
||||||
return Pair.of(blockPos, result.obj());
|
|
||||||
} catch (WrongEngineBroException | ExecutionException | InterruptedException e) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected Codec<? extends ChunkGenerator> codec() {
|
|
||||||
return Codec.unit(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public ChunkGenerator getDelegate() {
|
|
||||||
if (delegate instanceof CustomChunkGenerator chunkGenerator)
|
|
||||||
return chunkGenerator.getDelegate();
|
|
||||||
return delegate;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getMinY() {
|
|
||||||
return delegate.getMinY();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getSeaLevel() {
|
|
||||||
return delegate.getSeaLevel();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void createStructures(RegistryAccess iregistrycustom, ChunkGeneratorStructureState chunkgeneratorstructurestate, StructureManager structuremanager, ChunkAccess ichunkaccess, StructureTemplateManager structuretemplatemanager) {
|
|
||||||
delegate.createStructures(iregistrycustom, chunkgeneratorstructurestate, structuremanager, ichunkaccess, structuretemplatemanager);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void buildSurface(WorldGenRegion regionlimitedworldaccess, StructureManager structuremanager, RandomState randomstate, ChunkAccess ichunkaccess) {
|
|
||||||
delegate.buildSurface(regionlimitedworldaccess, structuremanager, randomstate, ichunkaccess);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void applyCarvers(WorldGenRegion regionlimitedworldaccess, long seed, RandomState randomstate, BiomeManager biomemanager, StructureManager structuremanager, ChunkAccess ichunkaccess, GenerationStep.Carving worldgenstage_features) {
|
|
||||||
delegate.applyCarvers(regionlimitedworldaccess, seed, randomstate, biomemanager, structuremanager, ichunkaccess, worldgenstage_features);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public CompletableFuture<ChunkAccess> fillFromNoise(Executor executor, Blender blender, RandomState randomstate, StructureManager structuremanager, ChunkAccess ichunkaccess) {
|
|
||||||
return delegate.fillFromNoise(executor, blender, randomstate, structuremanager, ichunkaccess);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getBaseHeight(int i, int j, Heightmap.Types heightmap_type, LevelHeightAccessor levelheightaccessor, RandomState randomstate) {
|
|
||||||
return delegate.getBaseHeight(i, j, heightmap_type, levelheightaccessor, randomstate);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public WeightedRandomList<MobSpawnSettings.SpawnerData> getMobsAt(Holder<Biome> holder, StructureManager structuremanager, MobCategory enumcreaturetype, BlockPos blockposition) {
|
|
||||||
return delegate.getMobsAt(holder, structuremanager, enumcreaturetype, blockposition);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void applyBiomeDecoration(WorldGenLevel generatoraccessseed, ChunkAccess ichunkaccess, StructureManager structuremanager) {
|
|
||||||
delegate.applyBiomeDecoration(generatoraccessseed, ichunkaccess, structuremanager);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void addDebugScreenInfo(List<String> list, RandomState randomstate, BlockPos blockposition) {
|
|
||||||
delegate.addDebugScreenInfo(list, randomstate, blockposition);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void spawnOriginalMobs(WorldGenRegion regionlimitedworldaccess) {
|
|
||||||
delegate.spawnOriginalMobs(regionlimitedworldaccess);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getSpawnHeight(LevelHeightAccessor levelheightaccessor) {
|
|
||||||
return delegate.getSpawnHeight(levelheightaccessor);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getGenDepth() {
|
|
||||||
return delegate.getGenDepth();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public NoiseColumn getBaseColumn(int i, int j, LevelHeightAccessor levelheightaccessor, RandomState randomstate) {
|
|
||||||
return delegate.getBaseColumn(i, j, levelheightaccessor, randomstate);
|
|
||||||
}
|
|
||||||
|
|
||||||
static {
|
|
||||||
Field biomeSource = null;
|
|
||||||
for (Field field : ChunkGenerator.class.getDeclaredFields()) {
|
|
||||||
if (!field.getType().equals(BiomeSource.class))
|
|
||||||
continue;
|
|
||||||
biomeSource = field;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (biomeSource == null)
|
|
||||||
throw new RuntimeException("Could not find biomeSource field in ChunkGenerator!");
|
|
||||||
BIOME_SOURCE = new WrappedField<>(ChunkGenerator.class, biomeSource.getName());
|
|
||||||
}
|
|
||||||
|
|
||||||
private static ChunkGenerator edit(ChunkGenerator generator, BiomeSource source) {
|
|
||||||
try {
|
|
||||||
BIOME_SOURCE.set(generator, source);
|
|
||||||
if (generator instanceof CustomChunkGenerator custom)
|
|
||||||
BIOME_SOURCE.set(custom.getDelegate(), source);
|
|
||||||
|
|
||||||
return generator;
|
|
||||||
} catch (IllegalAccessException e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,638 +0,0 @@
|
|||||||
package com.volmit.iris.core.nms.v1_19_R3;
|
|
||||||
|
|
||||||
import java.awt.Color;
|
|
||||||
import java.io.ByteArrayInputStream;
|
|
||||||
import java.io.ByteArrayOutputStream;
|
|
||||||
import java.io.DataInputStream;
|
|
||||||
import java.io.DataOutputStream;
|
|
||||||
import java.lang.reflect.Field;
|
|
||||||
import java.lang.reflect.Method;
|
|
||||||
import java.lang.reflect.Modifier;
|
|
||||||
import java.util.Iterator;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Vector;
|
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
|
||||||
|
|
||||||
import com.mojang.datafixers.util.Pair;
|
|
||||||
import com.volmit.iris.core.nms.container.BiomeColor;
|
|
||||||
import com.volmit.iris.util.scheduling.J;
|
|
||||||
import net.minecraft.nbt.*;
|
|
||||||
import net.minecraft.nbt.Tag;
|
|
||||||
import net.minecraft.server.commands.data.BlockDataAccessor;
|
|
||||||
import net.minecraft.tags.TagKey;
|
|
||||||
import net.minecraft.world.level.LevelReader;
|
|
||||||
import net.minecraft.world.level.block.EntityBlock;
|
|
||||||
import org.bukkit.*;
|
|
||||||
import org.bukkit.block.Biome;
|
|
||||||
import org.bukkit.block.data.BlockData;
|
|
||||||
import org.bukkit.craftbukkit.v1_19_R3.CraftChunk;
|
|
||||||
import org.bukkit.craftbukkit.v1_19_R3.CraftServer;
|
|
||||||
import org.bukkit.craftbukkit.v1_19_R3.CraftWorld;
|
|
||||||
import org.bukkit.craftbukkit.v1_19_R3.block.CraftBlock;
|
|
||||||
import org.bukkit.craftbukkit.v1_19_R3.block.CraftBlockState;
|
|
||||||
import org.bukkit.craftbukkit.v1_19_R3.block.CraftBlockStates;
|
|
||||||
import org.bukkit.craftbukkit.v1_19_R3.block.data.CraftBlockData;
|
|
||||||
import org.bukkit.craftbukkit.v1_19_R3.entity.CraftDolphin;
|
|
||||||
import org.bukkit.craftbukkit.v1_19_R3.inventory.CraftItemStack;
|
|
||||||
import org.bukkit.entity.Dolphin;
|
|
||||||
import org.bukkit.entity.Entity;
|
|
||||||
import org.bukkit.event.entity.CreatureSpawnEvent;
|
|
||||||
import org.bukkit.generator.ChunkGenerator;
|
|
||||||
import org.bukkit.inventory.ItemStack;
|
|
||||||
import org.jetbrains.annotations.Contract;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
|
|
||||||
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
|
||||||
import com.volmit.iris.Iris;
|
|
||||||
import com.volmit.iris.core.nms.INMSBinding;
|
|
||||||
import com.volmit.iris.engine.data.cache.AtomicCache;
|
|
||||||
import com.volmit.iris.engine.framework.Engine;
|
|
||||||
import com.volmit.iris.util.collection.KList;
|
|
||||||
import com.volmit.iris.util.collection.KMap;
|
|
||||||
import com.volmit.iris.util.hunk.Hunk;
|
|
||||||
import com.volmit.iris.util.json.JSONObject;
|
|
||||||
import com.volmit.iris.util.mantle.Mantle;
|
|
||||||
import com.volmit.iris.util.math.Vector3d;
|
|
||||||
import com.volmit.iris.util.matter.MatterBiomeInject;
|
|
||||||
import com.volmit.iris.util.nbt.io.NBTUtil;
|
|
||||||
import com.volmit.iris.util.nbt.mca.NBTWorld;
|
|
||||||
import com.volmit.iris.util.nbt.mca.palette.*;
|
|
||||||
import com.volmit.iris.util.nbt.tag.CompoundTag;
|
|
||||||
|
|
||||||
import it.unimi.dsi.fastutil.objects.Object2IntMap;
|
|
||||||
import net.minecraft.core.BlockPos;
|
|
||||||
import net.minecraft.core.Holder;
|
|
||||||
import net.minecraft.core.Registry;
|
|
||||||
import net.minecraft.core.RegistryAccess;
|
|
||||||
import net.minecraft.core.registries.Registries;
|
|
||||||
import net.minecraft.resources.ResourceLocation;
|
|
||||||
import net.minecraft.server.level.ServerLevel;
|
|
||||||
import net.minecraft.world.entity.EntityType;
|
|
||||||
import net.minecraft.world.level.biome.BiomeSource;
|
|
||||||
import net.minecraft.world.level.block.Block;
|
|
||||||
import net.minecraft.world.level.block.entity.BlockEntity;
|
|
||||||
import net.minecraft.world.level.block.state.BlockState;
|
|
||||||
import net.minecraft.world.level.chunk.ChunkAccess;
|
|
||||||
import net.minecraft.world.level.chunk.ChunkStatus;
|
|
||||||
import net.minecraft.world.level.chunk.LevelChunk;
|
|
||||||
import sun.misc.Unsafe;
|
|
||||||
|
|
||||||
public class NMSBinding implements INMSBinding {
|
|
||||||
private final KMap<Biome, Object> baseBiomeCache = new KMap<>();
|
|
||||||
private final BlockData AIR = Material.AIR.createBlockData();
|
|
||||||
private final AtomicCache<MCAIdMap<net.minecraft.world.level.biome.Biome>> biomeMapCache = new AtomicCache<>();
|
|
||||||
private final AtomicCache<MCAIdMapper<BlockState>> registryCache = new AtomicCache<>();
|
|
||||||
private final AtomicCache<MCAPalette<BlockState>> globalCache = new AtomicCache<>();
|
|
||||||
private final AtomicCache<RegistryAccess> registryAccess = new AtomicCache<>();
|
|
||||||
private final AtomicCache<Method> byIdRef = new AtomicCache<>();
|
|
||||||
private Field biomeStorageCache = null;
|
|
||||||
|
|
||||||
private static Object getFor(Class<?> type, Object source) {
|
|
||||||
Object o = fieldFor(type, source);
|
|
||||||
|
|
||||||
if (o != null) {
|
|
||||||
return o;
|
|
||||||
}
|
|
||||||
|
|
||||||
return invokeFor(type, source);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Object invokeFor(Class<?> returns, Object in) {
|
|
||||||
for (Method i : in.getClass().getMethods()) {
|
|
||||||
if (i.getReturnType().equals(returns)) {
|
|
||||||
i.setAccessible(true);
|
|
||||||
try {
|
|
||||||
Iris.debug("[NMS] Found " + returns.getSimpleName() + " in " + in.getClass().getSimpleName() + "." + i.getName() + "()");
|
|
||||||
return i.invoke(in);
|
|
||||||
} catch (Throwable e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Object fieldFor(Class<?> returns, Object in) {
|
|
||||||
return fieldForClass(returns, in.getClass(), in);
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
private static <T> T fieldForClass(Class<T> returnType, Class<?> sourceType, Object in) {
|
|
||||||
for (Field i : sourceType.getDeclaredFields()) {
|
|
||||||
if (i.getType().equals(returnType)) {
|
|
||||||
i.setAccessible(true);
|
|
||||||
try {
|
|
||||||
Iris.debug("[NMS] Found " + returnType.getSimpleName() + " in " + sourceType.getSimpleName() + "." + i.getName());
|
|
||||||
return (T) i.get(in);
|
|
||||||
} catch (IllegalAccessException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Class<?> getClassType(Class<?> type, int ordinal) {
|
|
||||||
return type.getDeclaredClasses()[ordinal];
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean hasTile(Material material) {
|
|
||||||
return !CraftBlockState.class.equals(CraftBlockStates.getBlockStateType(material));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean hasTile(Location l) {
|
|
||||||
return ((CraftWorld) l.getWorld()).getHandle().getBlockEntity(new BlockPos(l.getBlockX(), l.getBlockY(), l.getBlockZ()), false) != null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
public KMap<String, Object> serializeTile(Location location) {
|
|
||||||
BlockEntity e = ((CraftWorld) location.getWorld()).getHandle().getBlockEntity(new BlockPos(location.getBlockX(), location.getBlockY(), location.getBlockZ()), false);
|
|
||||||
|
|
||||||
if (e == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
net.minecraft.nbt.CompoundTag tag = e.saveWithoutMetadata();
|
|
||||||
return (KMap<String, Object>) convertFromTag(tag, 0, 64);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Contract(value = "null, _, _ -> null", pure = true)
|
|
||||||
private Object convertFromTag(net.minecraft.nbt.Tag tag, int depth, int maxDepth) {
|
|
||||||
if (tag == null || depth > maxDepth) return null;
|
|
||||||
if (tag instanceof CollectionTag<?> collection) {
|
|
||||||
KList<Object> list = new KList<>();
|
|
||||||
|
|
||||||
for (Object i : collection) {
|
|
||||||
if (i instanceof net.minecraft.nbt.Tag t)
|
|
||||||
list.add(convertFromTag(t, depth + 1, maxDepth));
|
|
||||||
else list.add(i);
|
|
||||||
}
|
|
||||||
return list;
|
|
||||||
}
|
|
||||||
if (tag instanceof net.minecraft.nbt.CompoundTag compound) {
|
|
||||||
KMap<String, Object> map = new KMap<>();
|
|
||||||
|
|
||||||
for (String key : compound.getAllKeys()) {
|
|
||||||
var child = compound.get(key);
|
|
||||||
if (child == null) continue;
|
|
||||||
var value = convertFromTag(child, depth + 1, maxDepth);
|
|
||||||
if (value == null) continue;
|
|
||||||
map.put(key, value);
|
|
||||||
}
|
|
||||||
return map;
|
|
||||||
}
|
|
||||||
if (tag instanceof NumericTag numeric)
|
|
||||||
return numeric.getAsNumber();
|
|
||||||
return tag.getAsString();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void deserializeTile(KMap<String, Object> map, Location pos) {
|
|
||||||
net.minecraft.nbt.CompoundTag tag = (net.minecraft.nbt.CompoundTag) convertToTag(map, 0, 64);
|
|
||||||
var level = ((CraftWorld) pos.getWorld()).getHandle();
|
|
||||||
var blockPos = new BlockPos(pos.getBlockX(), pos.getBlockY(), pos.getBlockZ());
|
|
||||||
J.s(() -> merge(level, blockPos, tag));
|
|
||||||
}
|
|
||||||
|
|
||||||
private void merge(ServerLevel level, BlockPos blockPos, net.minecraft.nbt.CompoundTag tag) {
|
|
||||||
var blockEntity = level.getBlockEntity(blockPos);
|
|
||||||
if (blockEntity == null) {
|
|
||||||
Iris.warn("[NMS] BlockEntity not found at " + blockPos);
|
|
||||||
var state = level.getBlockState(blockPos);
|
|
||||||
if (!state.hasBlockEntity())
|
|
||||||
return;
|
|
||||||
|
|
||||||
blockEntity = ((EntityBlock) state.getBlock())
|
|
||||||
.newBlockEntity(blockPos, state);
|
|
||||||
}
|
|
||||||
var accessor = new BlockDataAccessor(blockEntity, blockPos);
|
|
||||||
accessor.setData(tag.merge(accessor.getData()));
|
|
||||||
}
|
|
||||||
|
|
||||||
private Tag convertToTag(Object object, int depth, int maxDepth) {
|
|
||||||
if (object == null || depth > maxDepth) return EndTag.INSTANCE;
|
|
||||||
if (object instanceof Map<?,?> map) {
|
|
||||||
var tag = new net.minecraft.nbt.CompoundTag();
|
|
||||||
for (var i : map.entrySet()) {
|
|
||||||
tag.put(i.getKey().toString(), convertToTag(i.getValue(), depth + 1, maxDepth));
|
|
||||||
}
|
|
||||||
return tag;
|
|
||||||
}
|
|
||||||
if (object instanceof List<?> list) {
|
|
||||||
var tag = new net.minecraft.nbt.ListTag();
|
|
||||||
for (var i : list) {
|
|
||||||
tag.add(convertToTag(i, depth + 1, maxDepth));
|
|
||||||
}
|
|
||||||
return tag;
|
|
||||||
}
|
|
||||||
if (object instanceof Byte number) return ByteTag.valueOf(number);
|
|
||||||
if (object instanceof Short number) return ShortTag.valueOf(number);
|
|
||||||
if (object instanceof Integer number) return IntTag.valueOf(number);
|
|
||||||
if (object instanceof Long number) return LongTag.valueOf(number);
|
|
||||||
if (object instanceof Float number) return FloatTag.valueOf(number);
|
|
||||||
if (object instanceof Double number) return DoubleTag.valueOf(number);
|
|
||||||
if (object instanceof String string) return StringTag.valueOf(string);
|
|
||||||
return EndTag.INSTANCE;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public CompoundTag serializeEntity(Entity location) {
|
|
||||||
return null;// TODO:
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Entity deserializeEntity(CompoundTag s, Location newPosition) {
|
|
||||||
return null;// TODO:
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean supportsCustomHeight() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private RegistryAccess registry() {
|
|
||||||
return registryAccess.aquire(() -> (RegistryAccess) getFor(RegistryAccess.Frozen.class, ((CraftServer) Bukkit.getServer()).getHandle().getServer()));
|
|
||||||
}
|
|
||||||
|
|
||||||
private Registry<net.minecraft.world.level.biome.Biome> getCustomBiomeRegistry() {
|
|
||||||
return registry().registry(Registries.BIOME).orElse(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
private Registry<Block> getBlockRegistry() {
|
|
||||||
return registry().registry(Registries.BLOCK).orElse(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Object getBiomeBaseFromId(int id) {
|
|
||||||
return getCustomBiomeRegistry().getHolder(id);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getMinHeight(World world) {
|
|
||||||
return world.getMinHeight();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean supportsCustomBiomes() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getTrueBiomeBaseId(Object biomeBase) {
|
|
||||||
return getCustomBiomeRegistry().getId(((Holder<net.minecraft.world.level.biome.Biome>) biomeBase).value());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Object getTrueBiomeBase(Location location) {
|
|
||||||
return ((CraftWorld) location.getWorld()).getHandle().getBiome(new BlockPos(location.getBlockX(), location.getBlockY(), location.getBlockZ()));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getTrueBiomeBaseKey(Location location) {
|
|
||||||
return getKeyForBiomeBase(getTrueBiomeBase(location));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Object getCustomBiomeBaseFor(String mckey) {
|
|
||||||
return getCustomBiomeRegistry().get(new ResourceLocation(mckey));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Object getCustomBiomeBaseHolderFor(String mckey) {
|
|
||||||
return getCustomBiomeRegistry().getHolder(getTrueBiomeBaseId(getCustomBiomeRegistry().get(new ResourceLocation(mckey)))).get();
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getBiomeBaseIdForKey(String key) {
|
|
||||||
return getCustomBiomeRegistry().getId(getCustomBiomeRegistry().get(new ResourceLocation(key)));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getKeyForBiomeBase(Object biomeBase) {
|
|
||||||
return getCustomBiomeRegistry().getKey((net.minecraft.world.level.biome.Biome) biomeBase).getPath(); // something, not something:something
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Object getBiomeBase(World world, Biome biome) {
|
|
||||||
return CraftBlock.biomeToBiomeBase(((CraftWorld) world).getHandle()
|
|
||||||
.registryAccess().registry(Registries.BIOME).orElse(null), biome);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Object getBiomeBase(Object registry, Biome biome) {
|
|
||||||
Object v = baseBiomeCache.get(biome);
|
|
||||||
|
|
||||||
if (v != null) {
|
|
||||||
return v;
|
|
||||||
}
|
|
||||||
//noinspection unchecked
|
|
||||||
v = CraftBlock.biomeToBiomeBase((Registry<net.minecraft.world.level.biome.Biome>) registry, biome);
|
|
||||||
if (v == null) {
|
|
||||||
// Ok so there is this new biome name called "CUSTOM" in Paper's new releases.
|
|
||||||
// But, this does NOT exist within CraftBukkit which makes it return an error.
|
|
||||||
// So, we will just return the ID that the plains biome returns instead.
|
|
||||||
//noinspection unchecked
|
|
||||||
return CraftBlock.biomeToBiomeBase((Registry<net.minecraft.world.level.biome.Biome>) registry, Biome.PLAINS);
|
|
||||||
}
|
|
||||||
baseBiomeCache.put(biome, v);
|
|
||||||
return v;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public KList<Biome> getBiomes() {
|
|
||||||
return new KList<>(Biome.values()).qdel(Biome.CHERRY_GROVE).qdel(Biome.CUSTOM);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isBukkit() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getBiomeId(Biome biome) {
|
|
||||||
for (World i : Bukkit.getWorlds()) {
|
|
||||||
if (i.getEnvironment().equals(World.Environment.NORMAL)) {
|
|
||||||
Registry<net.minecraft.world.level.biome.Biome> registry = ((CraftWorld) i).getHandle().registryAccess().registry(Registries.BIOME).orElse(null);
|
|
||||||
return registry.getId((net.minecraft.world.level.biome.Biome) getBiomeBase(registry, biome));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return biome.ordinal();
|
|
||||||
}
|
|
||||||
|
|
||||||
private MCAIdMap<net.minecraft.world.level.biome.Biome> getBiomeMapping() {
|
|
||||||
return biomeMapCache.aquire(() -> new MCAIdMap<>() {
|
|
||||||
@NotNull
|
|
||||||
@Override
|
|
||||||
public Iterator<net.minecraft.world.level.biome.Biome> iterator() {
|
|
||||||
return getCustomBiomeRegistry().iterator();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getId(net.minecraft.world.level.biome.Biome paramT) {
|
|
||||||
return getCustomBiomeRegistry().getId(paramT);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public net.minecraft.world.level.biome.Biome byId(int paramInt) {
|
|
||||||
return (net.minecraft.world.level.biome.Biome) getBiomeBaseFromId(paramInt);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
private MCABiomeContainer getBiomeContainerInterface(MCAIdMap<net.minecraft.world.level.biome.Biome> biomeMapping, MCAChunkBiomeContainer<net.minecraft.world.level.biome.Biome> base) {
|
|
||||||
return new MCABiomeContainer() {
|
|
||||||
@Override
|
|
||||||
public int[] getData() {
|
|
||||||
return base.writeBiomes();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setBiome(int x, int y, int z, int id) {
|
|
||||||
base.setBiome(x, y, z, biomeMapping.byId(id));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getBiome(int x, int y, int z) {
|
|
||||||
return biomeMapping.getId(base.getBiome(x, y, z));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public MCABiomeContainer newBiomeContainer(int min, int max) {
|
|
||||||
MCAChunkBiomeContainer<net.minecraft.world.level.biome.Biome> base = new MCAChunkBiomeContainer<>(getBiomeMapping(), min, max);
|
|
||||||
return getBiomeContainerInterface(getBiomeMapping(), base);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public MCABiomeContainer newBiomeContainer(int min, int max, int[] data) {
|
|
||||||
MCAChunkBiomeContainer<net.minecraft.world.level.biome.Biome> base = new MCAChunkBiomeContainer<>(getBiomeMapping(), min, max, data);
|
|
||||||
return getBiomeContainerInterface(getBiomeMapping(), base);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int countCustomBiomes() {
|
|
||||||
AtomicInteger a = new AtomicInteger(0);
|
|
||||||
|
|
||||||
getCustomBiomeRegistry().keySet().forEach((i) -> {
|
|
||||||
if (i.getNamespace().equals("minecraft")) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
a.incrementAndGet();
|
|
||||||
Iris.debug("Custom Biome: " + i);
|
|
||||||
});
|
|
||||||
|
|
||||||
return a.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean supportsDataPacks() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setBiomes(int cx, int cz, World world, Hunk<Object> biomes) {
|
|
||||||
LevelChunk c = ((CraftWorld) world).getHandle().getChunk(cx, cz);
|
|
||||||
biomes.iterateSync((x, y, z, b) -> c.setBiome(x, y, z, (Holder<net.minecraft.world.level.biome.Biome>) b));
|
|
||||||
c.setUnsaved(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void forceBiomeInto(int x, int y, int z, Object somethingVeryDirty, ChunkGenerator.BiomeGrid chunk) {
|
|
||||||
try {
|
|
||||||
ChunkAccess s = (ChunkAccess) getFieldForBiomeStorage(chunk).get(chunk);
|
|
||||||
Holder<net.minecraft.world.level.biome.Biome> biome = (Holder<net.minecraft.world.level.biome.Biome>) somethingVeryDirty;
|
|
||||||
s.setBiome(x, y, z, biome);
|
|
||||||
} catch (IllegalAccessException e) {
|
|
||||||
Iris.reportError(e);
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private Field getFieldForBiomeStorage(Object storage) {
|
|
||||||
Field f = biomeStorageCache;
|
|
||||||
|
|
||||||
if (f != null) {
|
|
||||||
return f;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
f = storage.getClass().getDeclaredField("biome");
|
|
||||||
f.setAccessible(true);
|
|
||||||
return f;
|
|
||||||
} catch (Throwable e) {
|
|
||||||
Iris.reportError(e);
|
|
||||||
e.printStackTrace();
|
|
||||||
Iris.error(storage.getClass().getCanonicalName());
|
|
||||||
}
|
|
||||||
|
|
||||||
biomeStorageCache = f;
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public MCAPaletteAccess createPalette() {
|
|
||||||
MCAIdMapper<BlockState> registry = registryCache.aquireNasty(() -> {
|
|
||||||
Field cf = net.minecraft.core.IdMapper.class.getDeclaredField("tToId");
|
|
||||||
Field df = net.minecraft.core.IdMapper.class.getDeclaredField("idToT");
|
|
||||||
Field bf = net.minecraft.core.IdMapper.class.getDeclaredField("nextId");
|
|
||||||
cf.setAccessible(true);
|
|
||||||
df.setAccessible(true);
|
|
||||||
bf.setAccessible(true);
|
|
||||||
net.minecraft.core.IdMapper<BlockState> blockData = Block.BLOCK_STATE_REGISTRY;
|
|
||||||
int b = bf.getInt(blockData);
|
|
||||||
Object2IntMap<BlockState> c = (Object2IntMap<BlockState>) cf.get(blockData);
|
|
||||||
List<BlockState> d = (List<BlockState>) df.get(blockData);
|
|
||||||
return new MCAIdMapper<BlockState>(c, d, b);
|
|
||||||
});
|
|
||||||
MCAPalette<BlockState> global = globalCache.aquireNasty(() -> new MCAGlobalPalette<>(registry, ((CraftBlockData) AIR).getState()));
|
|
||||||
MCAPalettedContainer<BlockState> container = new MCAPalettedContainer<>(global, registry,
|
|
||||||
i -> ((CraftBlockData) NBTWorld.getBlockData(i)).getState(),
|
|
||||||
i -> NBTWorld.getCompound(CraftBlockData.fromData(i)),
|
|
||||||
((CraftBlockData) AIR).getState());
|
|
||||||
return new MCAWrappedPalettedContainer<>(container,
|
|
||||||
i -> NBTWorld.getCompound(CraftBlockData.fromData(i)),
|
|
||||||
i -> ((CraftBlockData) NBTWorld.getBlockData(i)).getState());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void injectBiomesFromMantle(Chunk e, Mantle mantle) {
|
|
||||||
ChunkAccess chunk = ((CraftChunk) e).getHandle(ChunkStatus.FULL);
|
|
||||||
AtomicInteger c = new AtomicInteger();
|
|
||||||
AtomicInteger r = new AtomicInteger();
|
|
||||||
mantle.iterateChunk(e.getX(), e.getZ(), MatterBiomeInject.class, (x, y, z, b) -> {
|
|
||||||
if (b != null) {
|
|
||||||
if (b.isCustom()) {
|
|
||||||
chunk.setBiome(x, y, z, getCustomBiomeRegistry().getHolder(b.getBiomeId()).get());
|
|
||||||
c.getAndIncrement();
|
|
||||||
} else {
|
|
||||||
chunk.setBiome(x, y, z, (Holder<net.minecraft.world.level.biome.Biome>) getBiomeBase(e.getWorld(), b.getBiome()));
|
|
||||||
r.getAndIncrement();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public ItemStack applyCustomNbt(ItemStack itemStack, KMap<String, Object> customNbt) throws IllegalArgumentException {
|
|
||||||
if (customNbt != null && !customNbt.isEmpty()) {
|
|
||||||
net.minecraft.world.item.ItemStack s = CraftItemStack.asNMSCopy(itemStack);
|
|
||||||
|
|
||||||
try {
|
|
||||||
net.minecraft.nbt.CompoundTag tag = TagParser.parseTag((new JSONObject(customNbt)).toString());
|
|
||||||
tag.merge(s.getOrCreateTag());
|
|
||||||
s.setTag(tag);
|
|
||||||
} catch (CommandSyntaxException var5) {
|
|
||||||
throw new IllegalArgumentException(var5);
|
|
||||||
}
|
|
||||||
|
|
||||||
return CraftItemStack.asBukkitCopy(s);
|
|
||||||
} else {
|
|
||||||
return itemStack;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setTreasurePos(Dolphin dolphin, com.volmit.iris.core.nms.container.BlockPos pos) {
|
|
||||||
CraftDolphin cd = (CraftDolphin)dolphin;
|
|
||||||
cd.getHandle().setTreasurePos(new BlockPos(pos.getX(), pos.getY(), pos.getZ()));
|
|
||||||
cd.getHandle().setGotFish(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void inject(long seed, Engine engine, World world) {
|
|
||||||
var chunkMap = ((CraftWorld)world).getHandle().getChunkSource().chunkMap;
|
|
||||||
chunkMap.generator = new IrisChunkGenerator(chunkMap.generator, seed, engine, world);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Vector3d getBoundingbox(org.bukkit.entity.EntityType entity) {
|
|
||||||
Field[] fields = EntityType.class.getDeclaredFields();
|
|
||||||
for (Field field : fields) {
|
|
||||||
if (Modifier.isStatic(field.getModifiers()) && field.getType().equals(EntityType.class)) {
|
|
||||||
try {
|
|
||||||
EntityType entityType = (EntityType) field.get(null);
|
|
||||||
if (entityType.getDescriptionId().equals("entity.minecraft." + entity.name().toLowerCase())) {
|
|
||||||
Vector<Float> v1 = new Vector<>();
|
|
||||||
v1.add(entityType.getHeight());
|
|
||||||
entityType.getDimensions();
|
|
||||||
Vector3d box = new Vector3d( entityType.getWidth(), entityType.getHeight(), entityType.getWidth());
|
|
||||||
//System.out.println("Entity Type: " + entityType.getDescriptionId() + ", " + "Height: " + height + ", Width: " + width);
|
|
||||||
return box;
|
|
||||||
}
|
|
||||||
} catch (IllegalAccessException e) {
|
|
||||||
Iris.error("Unable to get entity dimensions!");
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Entity spawnEntity(Location location, org.bukkit.entity.EntityType type, CreatureSpawnEvent.SpawnReason reason) {
|
|
||||||
if (type == org.bukkit.entity.EntityType.CAMEL) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return ((CraftWorld) location.getWorld()).spawn(location, type.getEntityClass(), null, reason);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Color getBiomeColor(Location location, BiomeColor type) {
|
|
||||||
LevelReader reader = ((CraftWorld) location.getWorld()).getHandle();
|
|
||||||
var holder = reader.getBiome(new BlockPos(location.getBlockX(), location.getBlockY(), location.getBlockZ()));
|
|
||||||
var biome = holder.value();
|
|
||||||
if (biome == null) throw new IllegalArgumentException("Invalid biome: " + holder.unwrapKey().orElse(null));
|
|
||||||
|
|
||||||
int rgba = switch (type) {
|
|
||||||
case FOG -> biome.getFogColor();
|
|
||||||
case WATER -> biome.getWaterColor();
|
|
||||||
case WATER_FOG -> biome.getWaterFogColor();
|
|
||||||
case SKY -> biome.getSkyColor();
|
|
||||||
case FOLIAGE -> biome.getFoliageColor();
|
|
||||||
case GRASS -> biome.getGrassColor(location.getBlockX(), location.getBlockZ());
|
|
||||||
};
|
|
||||||
if (rgba == 0) {
|
|
||||||
if (BiomeColor.FOLIAGE == type && biome.getSpecialEffects().getFoliageColorOverride().isEmpty())
|
|
||||||
return null;
|
|
||||||
if (BiomeColor.GRASS == type && biome.getSpecialEffects().getGrassColorOverride().isEmpty())
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return new Color(rgba, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public KList<String> getStructureKeys() {
|
|
||||||
KList<String> keys = new KList<>();
|
|
||||||
|
|
||||||
var registry = registry().registry(Registries.STRUCTURE).orElse(null);
|
|
||||||
if (registry == null) return keys;
|
|
||||||
registry.keySet().stream().map(ResourceLocation::toString).forEach(keys::add);
|
|
||||||
registry.getTags()
|
|
||||||
.map(Pair::getFirst)
|
|
||||||
.map(TagKey::location)
|
|
||||||
.map(ResourceLocation::toString)
|
|
||||||
.map(s -> "#" + s)
|
|
||||||
.forEach(keys::add);
|
|
||||||
|
|
||||||
return keys;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Field getField(Class<?> clazz, Class<?> fieldType) throws NoSuchFieldException {
|
|
||||||
try {
|
|
||||||
for (Field f : clazz.getDeclaredFields()) {
|
|
||||||
if (f.getType().equals(fieldType))
|
|
||||||
return f;
|
|
||||||
}
|
|
||||||
throw new NoSuchFieldException(fieldType.getName());
|
|
||||||
} catch (NoSuchFieldException var4) {
|
|
||||||
Class<?> superClass = clazz.getSuperclass();
|
|
||||||
if (superClass == null) {
|
|
||||||
throw var4;
|
|
||||||
} else {
|
|
||||||
return getField(superClass, fieldType);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -214,7 +214,7 @@ public class NMSBinding implements INMSBinding {
|
|||||||
.newBlockEntity(blockPos, state);
|
.newBlockEntity(blockPos, state);
|
||||||
}
|
}
|
||||||
var accessor = new BlockDataAccessor(blockEntity, blockPos);
|
var accessor = new BlockDataAccessor(blockEntity, blockPos);
|
||||||
accessor.setData(tag.merge(accessor.getData()));
|
accessor.setData(accessor.getData().merge(tag));
|
||||||
}
|
}
|
||||||
|
|
||||||
private Tag convertToTag(Object object, int depth, int maxDepth) {
|
private Tag convertToTag(Object object, int depth, int maxDepth) {
|
||||||
|
@ -212,7 +212,7 @@ public class NMSBinding implements INMSBinding {
|
|||||||
.newBlockEntity(blockPos, state);
|
.newBlockEntity(blockPos, state);
|
||||||
}
|
}
|
||||||
var accessor = new BlockDataAccessor(blockEntity, blockPos);
|
var accessor = new BlockDataAccessor(blockEntity, blockPos);
|
||||||
accessor.setData(tag.merge(accessor.getData()));
|
accessor.setData(accessor.getData().merge(tag));
|
||||||
}
|
}
|
||||||
|
|
||||||
private Tag convertToTag(Object object, int depth, int maxDepth) {
|
private Tag convertToTag(Object object, int depth, int maxDepth) {
|
||||||
|
@ -212,7 +212,7 @@ public class NMSBinding implements INMSBinding {
|
|||||||
.newBlockEntity(blockPos, state);
|
.newBlockEntity(blockPos, state);
|
||||||
}
|
}
|
||||||
var accessor = new BlockDataAccessor(blockEntity, blockPos);
|
var accessor = new BlockDataAccessor(blockEntity, blockPos);
|
||||||
accessor.setData(tag.merge(accessor.getData()));
|
accessor.setData(accessor.getData().merge(tag));
|
||||||
}
|
}
|
||||||
|
|
||||||
private Tag convertToTag(Object object, int depth, int maxDepth) {
|
private Tag convertToTag(Object object, int depth, int maxDepth) {
|
||||||
|
@ -216,7 +216,7 @@ public class NMSBinding implements INMSBinding {
|
|||||||
.newBlockEntity(blockPos, state);
|
.newBlockEntity(blockPos, state);
|
||||||
}
|
}
|
||||||
var accessor = new BlockDataAccessor(blockEntity, blockPos);
|
var accessor = new BlockDataAccessor(blockEntity, blockPos);
|
||||||
accessor.setData(tag.merge(accessor.getData()));
|
accessor.setData(accessor.getData().merge(tag));
|
||||||
}
|
}
|
||||||
|
|
||||||
private Tag convertToTag(Object object, int depth, int maxDepth) {
|
private Tag convertToTag(Object object, int depth, int maxDepth) {
|
||||||
|
@ -215,7 +215,7 @@ public class NMSBinding implements INMSBinding {
|
|||||||
.newBlockEntity(blockPos, state);
|
.newBlockEntity(blockPos, state);
|
||||||
}
|
}
|
||||||
var accessor = new BlockDataAccessor(blockEntity, blockPos);
|
var accessor = new BlockDataAccessor(blockEntity, blockPos);
|
||||||
accessor.setData(tag.merge(accessor.getData()));
|
accessor.setData(accessor.getData().merge(tag));
|
||||||
}
|
}
|
||||||
|
|
||||||
private Tag convertToTag(Object object, int depth, int maxDepth) {
|
private Tag convertToTag(Object object, int depth, int maxDepth) {
|
||||||
|
@ -202,7 +202,7 @@ public class NMSBinding implements INMSBinding {
|
|||||||
.newBlockEntity(blockPos, state);
|
.newBlockEntity(blockPos, state);
|
||||||
}
|
}
|
||||||
var accessor = new BlockDataAccessor(blockEntity, blockPos);
|
var accessor = new BlockDataAccessor(blockEntity, blockPos);
|
||||||
accessor.setData(tag.merge(accessor.getData()));
|
accessor.setData(accessor.getData().merge(tag));
|
||||||
}
|
}
|
||||||
|
|
||||||
private Tag convertToTag(Object object, int depth, int maxDepth) {
|
private Tag convertToTag(Object object, int depth, int maxDepth) {
|
||||||
|
@ -201,7 +201,7 @@ public class NMSBinding implements INMSBinding {
|
|||||||
.newBlockEntity(blockPos, state);
|
.newBlockEntity(blockPos, state);
|
||||||
}
|
}
|
||||||
var accessor = new BlockDataAccessor(blockEntity, blockPos);
|
var accessor = new BlockDataAccessor(blockEntity, blockPos);
|
||||||
accessor.setData(tag.merge(accessor.getData()));
|
accessor.setData(accessor.getData().merge(tag));
|
||||||
}
|
}
|
||||||
|
|
||||||
private Tag convertToTag(Object object, int depth, int maxDepth) {
|
private Tag convertToTag(Object object, int depth, int maxDepth) {
|
||||||
|
@ -37,7 +37,4 @@ include(
|
|||||||
':nms:v1_20_R3',
|
':nms:v1_20_R3',
|
||||||
':nms:v1_20_R2',
|
':nms:v1_20_R2',
|
||||||
':nms:v1_20_R1',
|
':nms:v1_20_R1',
|
||||||
':nms:v1_19_R3',
|
|
||||||
':nms:v1_19_R2',
|
|
||||||
':nms:v1_19_R1'
|
|
||||||
)
|
)
|
Loading…
x
Reference in New Issue
Block a user