Merge remote-tracking branch 'origin/iris4' into iris4

This commit is contained in:
repixelatedmc
2024-08-18 12:32:03 +02:00
50 changed files with 2618 additions and 449 deletions
+1 -5
View File
@@ -16,10 +16,6 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
import java.util.function.Consumer
buildscript() { buildscript() {
repositories { repositories {
maven { url 'https://jitpack.io'} maven { url 'https://jitpack.io'}
@@ -36,7 +32,7 @@ plugins {
id "de.undercouch.download" version "5.0.1" id "de.undercouch.download" version "5.0.1"
} }
version '4.0-1.19.2-1.21' version '4.0-1.19.2-1.21.1'
// ADD YOURSELF AS A NEW LINE IF YOU WANT YOUR OWN BUILD TASK GENERATED // ADD YOURSELF AS A NEW LINE IF YOU WANT YOUR OWN BUILD TASK GENERATED
// ======================== WINDOWS ============================= // ======================== WINDOWS =============================
@@ -36,6 +36,7 @@ import com.volmit.iris.util.io.IO;
import com.volmit.iris.util.mantle.TectonicPlate; import com.volmit.iris.util.mantle.TectonicPlate;
import com.volmit.iris.util.nbt.mca.MCAFile; import com.volmit.iris.util.nbt.mca.MCAFile;
import com.volmit.iris.util.nbt.mca.MCAUtil; import com.volmit.iris.util.nbt.mca.MCAUtil;
import com.volmit.iris.util.parallel.MultiBurst;
import com.volmit.iris.util.plugin.VolmitSender; import com.volmit.iris.util.plugin.VolmitSender;
import net.jpountz.lz4.LZ4BlockInputStream; import net.jpountz.lz4.LZ4BlockInputStream;
import net.jpountz.lz4.LZ4BlockOutputStream; import net.jpountz.lz4.LZ4BlockOutputStream;
@@ -50,6 +51,7 @@ import java.io.*;
import java.net.InetAddress; import java.net.InetAddress;
import java.net.NetworkInterface; import java.net.NetworkInterface;
import java.util.*; import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.zip.GZIPInputStream; import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream; import java.util.zip.GZIPOutputStream;
@@ -169,6 +171,23 @@ public class CommandDeveloper implements DecreeExecutor {
} }
@Decree
public void objects(@Param(defaultValue = "overworld") IrisDimension dimension) {
var loader = dimension.getLoader().getObjectLoader();
var sender = sender();
var keys = loader.getPossibleKeys();
var burst = MultiBurst.burst.burst(keys.length);
AtomicInteger failed = new AtomicInteger();
for (String key : keys) {
burst.queue(() -> {
if (loader.load(key) == null)
failed.incrementAndGet();
});
}
burst.complete();
sender.sendMessage(C.RED + "Failed to load " + failed.get() + " of " + keys.length + " objects");
}
@Decree(description = "Test", aliases = {"ip"}) @Decree(description = "Test", aliases = {"ip"})
public void network() { public void network() {
try { try {
@@ -50,10 +50,10 @@ public class ObjectResourceLoader extends ResourceLoader<IrisObject> {
try { try {
PrecisionStopwatch p = PrecisionStopwatch.start(); PrecisionStopwatch p = PrecisionStopwatch.start();
IrisObject t = new IrisObject(0, 0, 0); IrisObject t = new IrisObject(0, 0, 0);
t.read(j);
t.setLoadKey(name); t.setLoadKey(name);
t.setLoader(manager); t.setLoader(manager);
t.setLoadFile(j); t.setLoadFile(j);
t.read(j);
logLoad(j, t); logLoad(j, t);
tlt.addAndGet(p.getMilliseconds()); tlt.addAndGet(p.getMilliseconds());
return t; return t;
@@ -29,7 +29,8 @@ public class INMS {
private static final Map<String, String> REVISION = Map.of( private static final Map<String, String> REVISION = Map.of(
"1.20.5", "v1_20_R4", "1.20.5", "v1_20_R4",
"1.20.6", "v1_20_R4", "1.20.6", "v1_20_R4",
"1.21", "v1_21_R1" "1.21", "v1_21_R1",
"1.21.1", "v1_21_R1"
); );
//@done //@done
private static final INMSBinding binding = bind(); private static final INMSBinding binding = bind();
@@ -38,6 +38,7 @@ import org.bukkit.entity.Entity;
import org.bukkit.entity.EntityType; import org.bukkit.entity.EntityType;
import org.bukkit.event.entity.CreatureSpawnEvent; import org.bukkit.event.entity.CreatureSpawnEvent;
import org.bukkit.generator.ChunkGenerator; import org.bukkit.generator.ChunkGenerator;
import org.bukkit.generator.structure.Structure;
import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.ItemStack;
import java.io.File; import java.io.File;
@@ -110,8 +111,6 @@ public interface INMSBinding {
ItemStack applyCustomNbt(ItemStack itemStack, KMap<String, Object> customNbt) throws IllegalArgumentException; ItemStack applyCustomNbt(ItemStack itemStack, KMap<String, Object> customNbt) throws IllegalArgumentException;
void setTreasurePos(Dolphin dolphin, com.volmit.iris.core.nms.container.BlockPos pos);
void inject(long seed, Engine engine, World world) throws NoSuchFieldException, IllegalAccessException; void inject(long seed, Engine engine, World world) throws NoSuchFieldException, IllegalAccessException;
Vector3d getBoundingbox(org.bukkit.entity.EntityType entity); Vector3d getBoundingbox(org.bukkit.entity.EntityType entity);
@@ -141,4 +140,6 @@ public interface INMSBinding {
} }
IPackRepository getPackRepository(); IPackRepository getPackRepository();
KList<String> getStructureKeys();
} }
@@ -39,10 +39,7 @@ import net.bytebuddy.ByteBuddy;
import net.bytebuddy.asm.Advice; import net.bytebuddy.asm.Advice;
import net.bytebuddy.dynamic.loading.ClassReloadingStrategy; import net.bytebuddy.dynamic.loading.ClassReloadingStrategy;
import net.bytebuddy.matcher.ElementMatchers; import net.bytebuddy.matcher.ElementMatchers;
import org.bukkit.Chunk; import org.bukkit.*;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.World;
import org.bukkit.WorldCreator; import org.bukkit.WorldCreator;
import org.bukkit.block.Biome; import org.bukkit.block.Biome;
import org.bukkit.entity.Dolphin; import org.bukkit.entity.Dolphin;
@@ -50,9 +47,10 @@ import org.bukkit.entity.Entity;
import org.bukkit.entity.EntityType; import org.bukkit.entity.EntityType;
import org.bukkit.event.entity.CreatureSpawnEvent; import org.bukkit.event.entity.CreatureSpawnEvent;
import org.bukkit.generator.ChunkGenerator; import org.bukkit.generator.ChunkGenerator;
import org.bukkit.generator.structure.Structure;
import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.ItemStack;
import java.awt.*; import java.awt.Color;
import java.io.File; import java.io.File;
public class NMSBinding1X implements INMSBinding { public class NMSBinding1X implements INMSBinding {
@@ -104,11 +102,6 @@ public class NMSBinding1X implements INMSBinding {
return itemStack; return itemStack;
} }
@Override
public void setTreasurePos(Dolphin dolphin, BlockPos pos) {
}
@Override @Override
public void inject(long seed, Engine engine, World world) throws NoSuchFieldException, IllegalAccessException { public void inject(long seed, Engine engine, World world) throws NoSuchFieldException, IllegalAccessException {
@@ -143,6 +136,15 @@ public class NMSBinding1X implements INMSBinding {
return Color.GREEN; return Color.GREEN;
} }
@Override
public KList<String> getStructureKeys() {
var list = Registry.STRUCTURE.stream()
.map(Structure::getKey)
.map(NamespacedKey::toString)
.toList();
return new KList<>(list);
}
@Override @Override
public CompoundTag serializeEntity(Entity location) { public CompoundTag serializeEntity(Entity location) {
return null; return null;
@@ -177,7 +177,7 @@ public class IrisPregenerator {
// updater.start(); // updater.start();
// } // }
} else { } else {
IrisPackBenchmarking.instance.finishedBenchmark(chunksPerSecondHistory); IrisPackBenchmarking.getInstance().finishedBenchmark(chunksPerSecondHistory);
} }
} }
@@ -22,6 +22,7 @@ import com.volmit.iris.Iris;
import com.volmit.iris.core.loader.IrisData; import com.volmit.iris.core.loader.IrisData;
import com.volmit.iris.core.loader.IrisRegistrant; import com.volmit.iris.core.loader.IrisRegistrant;
import com.volmit.iris.core.loader.ResourceLoader; import com.volmit.iris.core.loader.ResourceLoader;
import com.volmit.iris.engine.framework.ListFunction;
import com.volmit.iris.engine.object.annotations.*; import com.volmit.iris.engine.object.annotations.*;
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;
@@ -309,6 +310,24 @@ public class SchemaBuilder {
fancyType = "Enchantment Type"; fancyType = "Enchantment Type";
prop.put("$ref", "#/definitions/" + key); prop.put("$ref", "#/definitions/" + key);
description.add(SYMBOL_TYPE__N + " Must be a valid Enchantment Type (use ctrl+space for auto complete!)"); description.add(SYMBOL_TYPE__N + " Must be a valid Enchantment Type (use ctrl+space for auto complete!)");
} else if (k.isAnnotationPresent(RegistryListFunction.class)) {
var functionClass = k.getDeclaredAnnotation(RegistryListFunction.class).value();
try {
var instance = functionClass.getDeclaredConstructor().newInstance();
String key = instance.key();
fancyType = instance.fancyName();
if (!definitions.containsKey(key)) {
JSONObject j = new JSONObject();
j.put("enum", instance.apply(data));
definitions.put(key, j);
}
prop.put("$ref", "#/definitions/" + key);
description.add(SYMBOL_TYPE__N + " Must be a valid " + fancyType + " (use ctrl+space for auto complete!)");
} catch (Throwable e) {
Iris.error("Could not execute apply method in " + functionClass.getName());
}
} else if (k.getType().equals(PotionEffectType.class)) { } else if (k.getType().equals(PotionEffectType.class)) {
String key = "enum-potion-effect-type"; String key = "enum-potion-effect-type";
@@ -55,7 +55,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.20.6"); Iris.safeguard(C.RED + "- Iris only supports 1.19.2 > 1.21.1");
} }
if (!ServerBootSFG.passedserversoftware) { if (!ServerBootSFG.passedserversoftware) {
Iris.safeguard(C.YELLOW + "Unsupported Server Software"); Iris.safeguard(C.YELLOW + "Unsupported Server Software");
@@ -1,93 +0,0 @@
/*
* Iris is a World Generator for Minecraft Bukkit Servers
* Copyright (c) 2024 Arcane Arts (Volmit Software)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.volmit.iris.core.service;
import com.volmit.iris.core.nms.INMS;
import com.volmit.iris.core.nms.container.BlockPos;
import com.volmit.iris.core.tools.IrisToolbelt;
import com.volmit.iris.engine.framework.Engine;
import com.volmit.iris.util.documentation.ChunkCoordinates;
import com.volmit.iris.util.function.Consumer4;
import com.volmit.iris.util.math.Spiraler;
import com.volmit.iris.util.matter.MatterStructurePOI;
import com.volmit.iris.util.plugin.IrisService;
import org.bukkit.Material;
import org.bukkit.Sound;
import org.bukkit.SoundCategory;
import org.bukkit.entity.Dolphin;
import org.bukkit.entity.EntityType;
import org.bukkit.event.EventHandler;
import org.bukkit.event.player.PlayerInteractEntityEvent;
import org.bukkit.generator.structure.StructureType;
import java.util.concurrent.atomic.AtomicReference;
public class DolphinSVC implements IrisService {
@Override
public void onEnable() {
}
@Override
public void onDisable() {
}
@EventHandler
public void on(PlayerInteractEntityEvent event) {
if (!IrisToolbelt.isIrisWorld(event.getPlayer().getWorld())) {
return;
}
Material hand = event.getPlayer().getInventory().getItem(event.getHand()).getType();
if (event.getRightClicked().getType().equals(EntityType.DOLPHIN) && (hand.equals(Material.TROPICAL_FISH) || hand.equals(Material.PUFFERFISH) || hand.equals(Material.COD) || hand.equals(Material.SALMON))) {
Engine e = IrisToolbelt.access(event.getPlayer().getWorld()).getEngine();
searchNearestTreasure(e, event.getPlayer().getLocation().getBlockX() >> 4, event.getPlayer().getLocation().getBlockZ() >> 4, e.getMantle().getRadius() - 1, StructureType.BURIED_TREASURE, (x, y, z, p) -> {
event.setCancelled(true);
Dolphin d = (Dolphin) event.getRightClicked();
INMS.get().setTreasurePos(d, new BlockPos(x, y, z));
d.getWorld().playSound(d, Sound.ENTITY_DOLPHIN_EAT, SoundCategory.NEUTRAL, 1, 1);
});
}
}
@ChunkCoordinates
public void findTreasure(Engine engine, int chunkX, int chunkY, StructureType type, Consumer4<Integer, Integer, Integer, MatterStructurePOI> consumer) {
AtomicReference<MatterStructurePOI> ref = new AtomicReference<>();
engine.getMantle().getMantle().iterateChunk(chunkX, chunkY, MatterStructurePOI.class, ref.get() == null ? (x, y, z, d) -> {
if (d.getType().equals(type.getKey().getKey())) {
ref.set(d);
consumer.accept(x, y, z, d);
}
} : (x, y, z, d) -> {
});
}
@ChunkCoordinates
public void searchNearestTreasure(Engine engine, int chunkX, int chunkY, int radius, StructureType type, Consumer4<Integer, Integer, Integer, MatterStructurePOI> consumer) {
AtomicReference<MatterStructurePOI> ref = new AtomicReference<>();
new Spiraler(radius * 2, radius * 2, (x, z) -> findTreasure(engine, x, z, type, ref.get() == null ? (i, d, g, a) -> {
ref.set(a);
consumer.accept(i, d, g, a);
} : (i, d, g, a) -> {
})).setOffset(chunkX, chunkY).drain();
}
}
@@ -1,127 +0,0 @@
/*
* Iris is a World Generator for Minecraft Bukkit Servers
* Copyright (c) 2024 Arcane Arts (Volmit Software)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.volmit.iris.core.service;
import com.volmit.iris.Iris;
import com.volmit.iris.core.IrisSettings;
import com.volmit.iris.core.tools.IrisToolbelt;
import com.volmit.iris.engine.object.IrisDimension;
import com.volmit.iris.util.format.C;
import com.volmit.iris.util.plugin.IrisService;
import com.volmit.iris.util.plugin.VolmitSender;
import org.bukkit.Location;
import org.bukkit.entity.Player;
import org.bukkit.entity.Villager;
import org.bukkit.event.EventHandler;
import org.bukkit.event.entity.VillagerCareerChangeEvent;
import java.util.List;
public class VillageSVC implements IrisService {
@Override
public void onEnable() {
}
@Override
public void onDisable() {
}
@EventHandler
public void on(VillagerCareerChangeEvent event) {
if (!IrisToolbelt.isIrisWorld(event.getEntity().getWorld())) {
return;
}
IrisDimension dim = IrisToolbelt.access(event.getEntity().getWorld())
.getEngine().getDimension();
if (!dim.isRemoveCartographersDueToCrash()) {
return;
}
if (event.getProfession().equals(Villager.Profession.CARTOGRAPHER)) {
event.setCancelled(true);
Location eventLocation = event.getEntity().getLocation();
int radius = dim.getNotifyPlayersOfCartographerCancelledRadius();
if (radius == -1) {
return;
}
List<Player> playersInWorld = event.getEntity().getWorld().getPlayers();
String message = C.GOLD + IrisSettings.get().getGeneral().cartographerMessage;
Iris.info("Cancelled Cartographer Villager to prevent server crash at " + eventLocation + "!");
if (radius == -2) {
playersInWorld.stream().map(VolmitSender::new).forEach(v -> v.sendMessage(message));
} else {
playersInWorld.forEach(p -> {
if (p.getLocation().distance(eventLocation) < radius) {
new VolmitSender(p).sendMessage(message);
}
});
}
}
}
/*
* Replace or disable villager trade add event to prevent explorer map
*/
/* Removed due to MC breaking stuff again. This event is now called after the cartographer maps are made,
so it can fuck right off.
@EventHandler
public void on(VillagerAcquireTradeEvent event) {
if(!IrisToolbelt.isIrisWorld((event.getEntity().getWorld()))) {
return;
}
// Iris.info("Trade event: type " + event.getRecipe().getResult().getType() + " / meta " + event.getRecipe().getResult().getItemMeta() + " / data " + event.getRecipe().getResult().getData());
if(!event.getRecipe().getResult().getType().equals(Material.FILLED_MAP)) {
return;
}
IrisVillagerOverride override = IrisToolbelt.access(event.getEntity().getWorld()).getEngine()
.getDimension().getPatchCartographers();
if(override.isDisableTrade()) {
event.setCancelled(true);
Iris.debug("Cancelled cartographer trade @ " + event.getEntity().getLocation());
return;
}
if(override.getValidItems() == null) {
event.setCancelled(true);
Iris.debug("Cancelled cartographer trade because no override items are valid @ " + event.getEntity().getLocation());
return;
}
IrisVillagerTrade trade = override.getValidItems().getRandom();
event.setRecipe(trade.convert());
Iris.debug("Overrode cartographer trade with: " + trade + " to prevent allowing cartography map trades");
}
*/
}
@@ -299,39 +299,44 @@ public class IrisEngine implements Engine {
@Override @Override
public IrisEngineData getEngineData() { public IrisEngineData getEngineData() {
var ret = engineData.aquire(() -> { return engineData.aquire(() -> {
//TODO: Method this file //TODO: Method this file
File f = new File(getWorld().worldFolder(), "iris/engine-data/" + getDimension().getLoadKey() + ".json"); File f = new File(getWorld().worldFolder(), "iris/engine-data/" + getDimension().getLoadKey() + ".json");
IrisEngineData data = null;
if (!f.exists()) { if (f.exists()) {
try { try {
f.getParentFile().mkdirs(); data = new Gson().fromJson(IO.readAll(f), IrisEngineData.class);
IrisEngineData data = new IrisEngineData(); if (data == null) {
Iris.error("Failed to read Engine Data! Corrupted File? recreating...");
}
} catch (IOException e) {
e.printStackTrace();
}
}
if (data == null) {
data = new IrisEngineData();
data.getStatistics().setIrisCreationVersion(Iris.instance.getIrisVersion()); data.getStatistics().setIrisCreationVersion(Iris.instance.getIrisVersion());
data.getStatistics().setMCVersion(Iris.instance.getMCVersion()); data.getStatistics().setMCVersion(Iris.instance.getMCVersion());
data.getStatistics().setIrisToUpgradedVersion(Iris.instance.getIrisVersion()); data.getStatistics().setIrisToUpgradedVersion(Iris.instance.getIrisVersion());
if (data.getStatistics().getIrisCreationVersion() == -1 || data.getStatistics().getMCVersion() == -1) { if (data.getStatistics().getIrisCreationVersion() == -1 || data.getStatistics().getMCVersion() == -1) {
Iris.error("Failed to setup Engine Data!"); Iris.error("Failed to setup Engine Data!");
} }
if (f.getParentFile().exists() || f.getParentFile().mkdirs()) {
try {
IO.writeAll(f, new Gson().toJson(data)); IO.writeAll(f, new Gson().toJson(data));
} catch (IOException e) { } catch (IOException e) {
e.printStackTrace(); e.printStackTrace();
} }
} else {
Iris.error("Failed to setup Engine Data!");
}
} }
try { return data;
return new Gson().fromJson(IO.readAll(f), IrisEngineData.class);
} catch (Throwable e) {
e.printStackTrace();
}
return new IrisEngineData();
}); });
if (ret == null) {
Iris.error("Failed to load Engine Data! (How did this happen?)");
return new IrisEngineData();
}
return ret;
} }
@Override @Override
@@ -76,6 +76,7 @@ public class IrisWorldManager extends EngineAssignedWorldManager {
private final ChronoLatch ecl; private final ChronoLatch ecl;
private final ChronoLatch cln; private final ChronoLatch cln;
private final ChronoLatch chunkUpdater; private final ChronoLatch chunkUpdater;
private final ChronoLatch chunkDiscovery;
private double energy = 25; private double energy = 25;
private int entityCount = 0; private int entityCount = 0;
private long charge = 0; private long charge = 0;
@@ -92,12 +93,14 @@ public class IrisWorldManager extends EngineAssignedWorldManager {
clw = null; clw = null;
looper = null; looper = null;
chunkUpdater = null; chunkUpdater = null;
chunkDiscovery = null;
id = -1; id = -1;
} }
public IrisWorldManager(Engine engine) { public IrisWorldManager(Engine engine) {
super(engine); super(engine);
chunkUpdater = new ChronoLatch(3000); chunkUpdater = new ChronoLatch(3000);
chunkDiscovery = new ChronoLatch(5000);
cln = new ChronoLatch(60000); cln = new ChronoLatch(60000);
cl = new ChronoLatch(3000); cl = new ChronoLatch(3000);
ecl = new ChronoLatch(250); ecl = new ChronoLatch(250);
@@ -128,6 +131,10 @@ public class IrisWorldManager extends EngineAssignedWorldManager {
updateChunks(); updateChunks();
} }
if (chunkDiscovery.flip()) {
discoverChunks();
}
if (getDimension().isInfiniteEnergy()) { if (getDimension().isInfiniteEnergy()) {
energy += 1000; energy += 1000;
@@ -174,6 +181,19 @@ public class IrisWorldManager extends EngineAssignedWorldManager {
looper.start(); looper.start();
} }
private void discoverChunks() {
var mantle = getEngine().getMantle().getMantle();
for (Player i : getEngine().getWorld().realWorld().getPlayers()) {
int r = 1;
for (int x = -r; x <= r; x++) {
for (int z = -r; z <= r; z++) {
mantle.getChunk(i.getLocation().getChunk()).flag(MantleFlag.DISCOVERED, true);
}
}
}
}
private void updateChunks() { private void updateChunks() {
for (Player i : getEngine().getWorld().realWorld().getPlayers()) { for (Player i : getEngine().getWorld().realWorld().getPlayers()) {
int r = 1; int r = 1;
@@ -840,7 +840,7 @@ public interface Engine extends DataProvider, Fallible, LootProvider, BlockUpdat
default void gotoJigsaw(IrisJigsawStructure s, Player player, boolean teleport) { default void gotoJigsaw(IrisJigsawStructure s, Player player, boolean teleport) {
if (s.getLoadKey().equals(getDimension().getStronghold())) { if (s.getLoadKey().equals(getDimension().getStronghold())) {
KList<Position2> p = getDimension().getStrongholds(getSeedManager().getSpawn()); KList<Position2> p = getDimension().getStrongholds(getSeedManager().getMantle());
if (p.isEmpty()) { if (p.isEmpty()) {
player.sendMessage(C.GOLD + "No strongholds in world."); player.sendMessage(C.GOLD + "No strongholds in world.");
@@ -110,7 +110,7 @@ public abstract class EngineAssignedWorldManager extends EngineAssignedComponent
return; return;
} }
KList<Position2> positions = getEngine().getDimension().getStrongholds(getEngine().getSeedManager().getSpawn()); KList<Position2> positions = getEngine().getDimension().getStrongholds(getEngine().getSeedManager().getMantle());
if (positions.isEmpty()) { if (positions.isEmpty()) {
return; return;
} }
@@ -0,0 +1,8 @@
package com.volmit.iris.engine.framework;
import java.util.function.Function;
public interface ListFunction<T, R> extends Function<T, R> {
String key();
String fancyName();
}
@@ -0,0 +1,121 @@
package com.volmit.iris.engine.framework;
import com.volmit.iris.core.IrisSettings;
import com.volmit.iris.engine.object.IrisJigsawStructure;
import com.volmit.iris.engine.object.IrisObject;
import com.volmit.iris.util.collection.KList;
import com.volmit.iris.util.math.Position2;
import com.volmit.iris.util.math.Spiraler;
import com.volmit.iris.util.parallel.BurstExecutor;
import com.volmit.iris.util.parallel.MultiBurst;
import com.volmit.iris.util.scheduling.PrecisionStopwatch;
import org.apache.commons.lang3.function.TriFunction;
import java.util.Collection;
import java.util.Set;
import java.util.concurrent.Future;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Consumer;
@FunctionalInterface
public interface ResultLocator<T> {
static void cancelSearch() {
if (LocatorCanceller.cancel != null) {
LocatorCanceller.cancel.run();
LocatorCanceller.cancel = null;
}
}
static ResultLocator<IrisJigsawStructure> locateStructure(Collection<String> keys) {
return (e, pos) -> {
var structure = e.getStructureAt(pos.getX(), pos.getZ());
return structure != null && keys.contains(structure.getLoadKey()) ? structure : null;
};
}
static ResultLocator<IrisObject> locateObject(Collection<String> keys) {
return (e, pos) -> {
Set<String> objects = e.getObjectsAt(pos.getX(), pos.getZ());
for (String object : objects) {
if (!keys.contains(object)) continue;
return e.getData().getObjectLoader().load(object);
}
return null;
};
}
T find(Engine e, Position2 chunkPos);
default <R> ResultLocator<R> then(TriFunction<Engine, Position2, T, R> filter) {
return (e, pos) -> {
var t = find(e, pos);
return t != null ? filter.apply(e, pos, t) : null;
};
}
default Future<Result<T>> find(Engine engine, Position2 pos, long timeout, Consumer<Integer> checks, boolean cancelable) throws WrongEngineBroException {
if (engine.isClosed()) {
throw new WrongEngineBroException();
}
cancelSearch();
return MultiBurst.burst.completeValue(() -> {
int tc = IrisSettings.getThreadCount(IrisSettings.get().getConcurrency().getParallelism()) * 17;
MultiBurst burst = MultiBurst.burst;
AtomicBoolean found = new AtomicBoolean(false);
AtomicReference<Result<T>> foundObj = new AtomicReference<>();
Position2 cursor = pos;
AtomicInteger searched = new AtomicInteger();
AtomicBoolean stop = new AtomicBoolean(false);
PrecisionStopwatch px = PrecisionStopwatch.start();
if (cancelable) LocatorCanceller.cancel = () -> stop.set(true);
AtomicReference<Position2> next = new AtomicReference<>(cursor);
Spiraler s = new Spiraler(100000, 100000, (x, z) -> next.set(new Position2(x, z)));
s.setOffset(cursor.getX(), cursor.getZ());
s.next();
while (!found.get() && !stop.get() && px.getMilliseconds() < timeout) {
BurstExecutor e = burst.burst(tc);
for (int i = 0; i < tc; i++) {
Position2 p = next.get();
s.next();
e.queue(() -> {
var o = find(engine, p);
if (o != null) {
if (foundObj.get() == null) {
foundObj.set(new Result<>(o, p));
}
found.set(true);
}
searched.incrementAndGet();
});
}
e.complete();
checks.accept(searched.get());
}
LocatorCanceller.cancel = null;
if (found.get() && foundObj.get() != null) {
return foundObj.get();
}
return null;
});
}
record Result<T>(T obj, Position2 pos) {
public int getBlockX() {
return (pos.getX() << 4) + 8;
}
public int getBlockZ() {
return (pos.getZ() << 4) + 8;
}
}
}
@@ -107,7 +107,7 @@ public interface EngineMantle extends IObjectPlacer {
@Override @Override
default void setTile(int x, int y, int z, TileData d) { default void setTile(int x, int y, int z, TileData d) {
getMantle().set(x, y, z, d); getMantle().set(x, y, z, new TileWrapper(d));
} }
@Override @Override
@@ -62,6 +62,8 @@ public class IrisBlockData extends IrisRegistrant {
private IrisBlockData backup = null; private IrisBlockData backup = null;
@Desc("Optional properties for this block data such as 'waterlogged': true") @Desc("Optional properties for this block data such as 'waterlogged': true")
private KMap<String, Object> data = new KMap<>(); private KMap<String, Object> data = new KMap<>();
@Desc("Optional tile data for this block data")
private KMap<String, Object> tileData = new KMap<>();
public IrisBlockData(String b) { public IrisBlockData(String b) {
this.block = b; this.block = b;
@@ -200,9 +202,9 @@ public class IrisBlockData extends IrisRegistrant {
public TileData tryGetTile(IrisData data) { public TileData tryGetTile(IrisData data) {
//TODO Do like a registry thing with the tile data registry. Also update the parsing of data to include **block** entities. //TODO Do like a registry thing with the tile data registry. Also update the parsing of data to include **block** entities.
var type = getBlockData(data).getMaterial(); var type = getBlockData(data).getMaterial();
if (!INMS.get().hasTile(type)) if (!INMS.get().hasTile(type) || tileData == null || tileData.isEmpty())
return null; return null;
return new TileData().setMaterial(type).setProperties(this.data); return new TileData(type, this.tileData);
} }
private String keyify(String dat) { private String keyify(String dat) {
@@ -219,10 +219,8 @@ public class IrisDimension extends IrisRegistrant {
private IrisMaterialPalette rockPalette = new IrisMaterialPalette().qclear().qadd("stone"); private IrisMaterialPalette rockPalette = new IrisMaterialPalette().qclear().qadd("stone");
@Desc("The palette of blocks for 'water'") @Desc("The palette of blocks for 'water'")
private IrisMaterialPalette fluidPalette = new IrisMaterialPalette().qclear().qadd("water"); private IrisMaterialPalette fluidPalette = new IrisMaterialPalette().qclear().qadd("water");
@Desc("Remove cartographers so they do not crash the server (Iris worlds only)") @Desc("Prevent cartographers to generate explorer maps (Iris worlds only)\nONLY TOUCH IF YOUR SERVER CRASHES WHILE GENERATING EXPLORER MAPS")
private boolean removeCartographersDueToCrash = true; private boolean disableExplorerMaps = false;
@Desc("Notify players of cancelled cartographer villager in this radius in blocks (set to -1 to disable, -2 for everyone)")
private int notifyPlayersOfCartographerCancelledRadius = 30;
@Desc("Collection of ores to be generated") @Desc("Collection of ores to be generated")
@ArrayType(type = IrisOreGenerator.class, min = 1) @ArrayType(type = IrisOreGenerator.class, min = 1)
private KList<IrisOreGenerator> ores = new KList<>(); private KList<IrisOreGenerator> ores = new KList<>();
@@ -69,6 +69,10 @@ public class IrisJigsawStructure extends IrisRegistrant {
@Desc("Set to true to prevent rotating the initial structure piece") @Desc("Set to true to prevent rotating the initial structure piece")
private boolean disableInitialRotation = false; private boolean disableInitialRotation = false;
@RegistryListFunction(StructureKeyFunction.class)
@Desc("The minecraft key to use when creating treasure maps")
private String structureKey = null;
private transient AtomicCache<Integer> maxDimension = new AtomicCache<>(); private transient AtomicCache<Integer> maxDimension = new AtomicCache<>();
private void loadPool(String p, KList<String> pools, KList<String> pieces) { private void loadPool(String p, KList<String> pools, KList<String> pieces) {
@@ -64,7 +64,7 @@ public class IrisJigsawStructurePlacement implements IRare {
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;
@ArrayType(type = IrisJigsawMinDistance.class) @ArrayType(type = IrisJigsawMinDistance.class)
@Desc("List of minimum distances to check for") @Desc("List of minimum distances to check for")
@@ -385,14 +385,14 @@ public class IrisObject extends IrisRegistrant {
} }
public void read(File file) throws IOException { public void read(File file) throws IOException {
FileInputStream fin = new FileInputStream(file); var fin = new BufferedInputStream(new FileInputStream(file));
try { try {
read(fin); read(fin);
fin.close(); fin.close();
} catch (Throwable e) { } catch (Throwable e) {
Iris.reportError(e); Iris.reportError(e);
fin.close(); fin.close();
fin = new FileInputStream(file); fin = new BufferedInputStream(new FileInputStream(file));
readLegacy(fin); readLegacy(fin);
fin.close(); fin.close();
} }
@@ -0,0 +1,151 @@
package com.volmit.iris.engine.object;
import com.volmit.iris.util.collection.KList;
import com.volmit.iris.util.scheduling.J;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import org.apache.commons.io.function.IOFunction;
import org.bukkit.DyeColor;
import org.bukkit.block.*;
import org.bukkit.block.banner.Pattern;
import org.bukkit.block.banner.PatternType;
import org.bukkit.entity.EntityType;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.util.Map;
@ToString
@EqualsAndHashCode(callSuper = false)
public class LegacyTileData extends TileData {
private static final Map<Integer, IOFunction<DataInputStream, Handler>> legacy = Map.of(
0, SignHandler::new,
1, SpawnerHandler::new,
2, BannerHandler::new);
private final int id;
private final Handler handler;
public LegacyTileData(DataInputStream in) throws IOException {
id = in.readShort();
var factory = legacy.get(id);
if (factory == null)
throw new IOException("Unknown tile type: " + id);
handler = factory.apply(in);
}
@Override
public void toBukkit(Block block) {
J.s(() -> handler.toBukkit(block));
}
@Override
public void toBinary(DataOutputStream out) throws IOException {
out.writeShort(id);
handler.toBinary(out);
}
@Override
public TileData clone() {
return this;
}
private interface Handler {
void toBinary(DataOutputStream out) throws IOException;
void toBukkit(Block block);
}
@ToString
@EqualsAndHashCode
private static class SignHandler implements Handler {
private final String line1;
private final String line2;
private final String line3;
private final String line4;
private final DyeColor dyeColor;
private SignHandler(DataInputStream in) throws IOException {
line1 = in.readUTF();
line2 = in.readUTF();
line3 = in.readUTF();
line4 = in.readUTF();
dyeColor = DyeColor.values()[in.readByte()];
}
@Override
public void toBinary(DataOutputStream out) throws IOException {
out.writeUTF(line1);
out.writeUTF(line2);
out.writeUTF(line3);
out.writeUTF(line4);
out.writeByte(dyeColor.ordinal());
}
@Override
public void toBukkit(Block block) {
Sign sign = (Sign) block.getState();
sign.setLine(0, line1);
sign.setLine(1, line2);
sign.setLine(2, line3);
sign.setLine(3, line4);
sign.setColor(dyeColor);
sign.update();
}
}
@ToString
@EqualsAndHashCode
private static class SpawnerHandler implements Handler {
private final EntityType type;
private SpawnerHandler(DataInputStream in) throws IOException {
type = EntityType.values()[in.readShort()];
}
@Override
public void toBinary(DataOutputStream out) throws IOException {
out.writeShort(type.ordinal());
}
@Override
public void toBukkit(Block block) {
CreatureSpawner spawner = (CreatureSpawner) block.getState();
spawner.setSpawnedType(type);
spawner.update();
}
}
@ToString
@EqualsAndHashCode
private static class BannerHandler implements Handler {
private final KList<Pattern> patterns;
private final DyeColor baseColor;
private BannerHandler(DataInputStream in) throws IOException {
baseColor = DyeColor.values()[in.readByte()];
patterns = new KList<>();
int listSize = in.readByte();
for (int i = 0; i < listSize; i++) {
DyeColor color = DyeColor.values()[in.readByte()];
PatternType pattern = PatternType.values()[in.readByte()];
patterns.add(new Pattern(color, pattern));
}
}
@Override
public void toBinary(DataOutputStream out) throws IOException {
out.writeByte(baseColor.ordinal());
out.writeByte(patterns.size());
for (Pattern i : patterns) {
out.writeByte(i.getColor().ordinal());
out.writeByte(i.getPattern().ordinal());
}
}
@Override
public void toBukkit(Block block) {
Banner banner = (Banner) block.getState();
banner.setBaseColor(baseColor);
banner.setPatterns(patterns);
banner.update();
}
}
}
@@ -23,11 +23,9 @@ import com.google.gson.GsonBuilder;
import com.volmit.iris.Iris; import com.volmit.iris.Iris;
import com.volmit.iris.core.nms.INMS; import com.volmit.iris.core.nms.INMS;
import com.volmit.iris.util.collection.KMap; import com.volmit.iris.util.collection.KMap;
import lombok.Data; import lombok.*;
import lombok.experimental.Accessors;
import org.bukkit.Material; import org.bukkit.Material;
import org.bukkit.block.Block; import org.bukkit.block.Block;
import org.bukkit.block.BlockState;
import org.bukkit.block.TileState; import org.bukkit.block.TileState;
import org.bukkit.block.data.BlockData; import org.bukkit.block.data.BlockData;
@@ -35,14 +33,19 @@ import java.io.DataInputStream;
import java.io.DataOutputStream; import java.io.DataOutputStream;
import java.io.IOException; import java.io.IOException;
@Data
@SuppressWarnings("ALL") @SuppressWarnings("ALL")
@Accessors(chain = true) @Getter
@ToString
@EqualsAndHashCode
@AllArgsConstructor
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class TileData implements Cloneable { public class TileData implements Cloneable {
private static final Gson gson = new GsonBuilder().disableHtmlEscaping().setLenient().create(); private static final Gson gson = new GsonBuilder().disableHtmlEscaping().setLenient().create();
private Material material = null; @NonNull
private KMap<String, Object> properties = new KMap<>(); private Material material;
@NonNull
private KMap<String, Object> properties;
public static boolean setTileState(Block block, TileData data) { public static boolean setTileState(Block block, TileData data) {
if (block.getState() instanceof TileState && data.isApplicable(block.getBlockData())) if (block.getState() instanceof TileState && data.isApplicable(block.getBlockData()))
@@ -57,10 +60,19 @@ public class TileData implements Cloneable {
} }
public static TileData read(DataInputStream in) throws IOException { public static TileData read(DataInputStream in) throws IOException {
TileData d = new TileData(); if (!in.markSupported())
d.material = Material.matchMaterial(in.readUTF()); throw new IOException("Mark not supported");
d.properties = gson.fromJson(in.readUTF(), KMap.class); in.mark(Integer.MAX_VALUE);
return d; try {
return new TileData(
Material.matchMaterial(in.readUTF()),
gson.fromJson(in.readUTF(), KMap.class));
} catch (Throwable e) {
in.reset();
return new LegacyTileData(in);
} finally {
in.mark(0);
}
} }
public boolean isApplicable(BlockData data) { public boolean isApplicable(BlockData data) {
@@ -0,0 +1,17 @@
package com.volmit.iris.engine.object.annotations;
import com.volmit.iris.core.loader.IrisData;
import com.volmit.iris.engine.framework.ListFunction;
import com.volmit.iris.util.collection.KList;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import static java.lang.annotation.ElementType.*;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
@Retention(RUNTIME)
@Target({PARAMETER, TYPE, FIELD})
public @interface RegistryListFunction {
Class<? extends ListFunction<IrisData, KList<String>>> value();
}
@@ -0,0 +1,23 @@
package com.volmit.iris.engine.object.annotations;
import com.volmit.iris.core.loader.IrisData;
import com.volmit.iris.core.nms.INMS;
import com.volmit.iris.engine.framework.ListFunction;
import com.volmit.iris.util.collection.KList;
public class StructureKeyFunction implements ListFunction<IrisData, KList<String>> {
@Override
public String key() {
return "structure-key";
}
@Override
public String fancyName() {
return "Structure Key";
}
@Override
public KList<String> apply(IrisData irisData) {
return INMS.get().getStructureKeys();
}
}
@@ -35,7 +35,8 @@ public enum MantleFlag {
ETCHED, ETCHED,
TILE, TILE,
CUSTOM, CUSTOM,
CUSTOM_ACTIVE; CUSTOM_ACTIVE,
DISCOVERED;
static StateList getStateList() { static StateList getStateList() {
return new StateList(MantleFlag.values()); return new StateList(MantleFlag.values());
@@ -84,10 +84,10 @@ public class TectonicPlate {
DataInputStream din; DataInputStream din;
if (file.getName().endsWith("ttp.lz4b")) { if (file.getName().endsWith("ttp.lz4b")) {
LZ4BlockInputStream lz4 = new LZ4BlockInputStream(fin); LZ4BlockInputStream lz4 = new LZ4BlockInputStream(fin);
din = new DataInputStream(lz4); din = new DataInputStream(new BufferedInputStream(lz4));
} else { } else {
GZIPInputStream gzi = new GZIPInputStream(fin); GZIPInputStream gzi = new GZIPInputStream(fin);
din = new DataInputStream(gzi); din = new DataInputStream(new BufferedInputStream(gzi));
} }
TectonicPlate p = new TectonicPlate(worldHeight, din); TectonicPlate p = new TectonicPlate(worldHeight, din);
din.close(); din.close();
@@ -0,0 +1,27 @@
/*
* Iris is a World Generator for Minecraft Bukkit Servers
* Copyright (c) 2024 Arcane Arts (Volmit Software)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.volmit.iris.util.matter;
import com.volmit.iris.engine.object.TileData;
import lombok.Data;
@Data
public class TileWrapper {
private final TileData data;
}
@@ -34,8 +34,8 @@ public class WorldMatter {
matter.slice(MatterEntityGroup.class).writeInto(at); matter.slice(MatterEntityGroup.class).writeInto(at);
} }
if (matter.hasSlice(TileData.class)) { if (matter.hasSlice(TileWrapper.class)) {
matter.slice(TileData.class).writeInto(at); matter.slice(TileWrapper.class).writeInto(at);
} }
} }
@@ -46,7 +46,7 @@ public class WorldMatter {
s.getHeader().setAuthor(author); s.getHeader().setAuthor(author);
s.slice(BlockData.class).readFrom(c.getLowerNE()); s.slice(BlockData.class).readFrom(c.getLowerNE());
s.slice(MatterEntityGroup.class).readFrom(c.getLowerNE()); s.slice(MatterEntityGroup.class).readFrom(c.getLowerNE());
s.slice(TileData.class).readFrom(c.getLowerNE()); s.slice(TileWrapper.class).readFrom(c.getLowerNE());
s.trimSlices(); s.trimSlices();
return s; return s;
@@ -21,6 +21,7 @@ package com.volmit.iris.util.matter.slices;
import com.volmit.iris.engine.object.TileData; import com.volmit.iris.engine.object.TileData;
import com.volmit.iris.util.data.palette.Palette; import com.volmit.iris.util.data.palette.Palette;
import com.volmit.iris.util.matter.Sliced; import com.volmit.iris.util.matter.Sliced;
import com.volmit.iris.util.matter.TileWrapper;
import org.bukkit.Location; import org.bukkit.Location;
import org.bukkit.World; import org.bukkit.World;
@@ -30,28 +31,28 @@ import java.io.IOException;
@SuppressWarnings("rawtypes") @SuppressWarnings("rawtypes")
@Sliced @Sliced
public class TileMatter extends RawMatter<TileData> { public class TileMatter extends RawMatter<TileWrapper> {
public TileMatter() { public TileMatter() {
this(1, 1, 1); this(1, 1, 1);
} }
public TileMatter(int width, int height, int depth) { public TileMatter(int width, int height, int depth) {
super(width, height, depth, TileData.class); super(width, height, depth, TileWrapper.class);
registerWriter(World.class, (w, d, x, y, z) -> TileData.setTileState(w.getBlockAt(new Location(w, x, y, z)), d)); registerWriter(World.class, (w, d, x, y, z) -> TileData.setTileState(w.getBlockAt(new Location(w, x, y, z)), d.getData()));
registerReader(World.class, (w, x, y, z) -> TileData.getTileState(w.getBlockAt(new Location(w, x, y, z)))); registerReader(World.class, (w, x, y, z) -> new TileWrapper(TileData.getTileState(w.getBlockAt(new Location(w, x, y, z)))));
} }
@Override @Override
public Palette<TileData> getGlobalPalette() { public Palette<TileWrapper> getGlobalPalette() {
return null; return null;
} }
public void writeNode(TileData b, DataOutputStream dos) throws IOException { public void writeNode(TileWrapper b, DataOutputStream dos) throws IOException {
b.toBinary(dos); b.getData().toBinary(dos);
} }
public TileData readNode(DataInputStream din) throws IOException { public TileWrapper readNode(DataInputStream din) throws IOException {
return TileData.read(din); return new TileWrapper(TileData.read(din));
} }
} }
@@ -0,0 +1,236 @@
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 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.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 final ChunkGenerator delegate;
private final Engine engine;
private final BiomeSource biomeSource;
private final KMap<ResourceKey<Structure>, KSet<String>> structures = new KMap<>();
public IrisChunkGenerator(ChunkGenerator delegate, long seed, Engine engine, World world) {
super(((CraftWorld) world).getHandle(), delegate, null);
this.delegate = delegate;
this.engine = engine;
this.biomeSource = new CustomBiomeSource(seed, engine, world);
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 BiomeSource getBiomeSource() {
return biomeSource;
}
@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);
}
}
@@ -34,6 +34,7 @@ import java.util.logging.Logger;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import com.google.gson.JsonNull; import com.google.gson.JsonNull;
import com.mojang.datafixers.util.Pair;
import com.volmit.iris.core.nms.container.BiomeColor; import com.volmit.iris.core.nms.container.BiomeColor;
import com.volmit.iris.util.scheduling.J; import com.volmit.iris.util.scheduling.J;
import net.minecraft.nbt.*; import net.minecraft.nbt.*;
@@ -41,6 +42,7 @@ import net.minecraft.nbt.Tag;
import net.minecraft.server.commands.data.BlockDataAccessor; import net.minecraft.server.commands.data.BlockDataAccessor;
import com.volmit.iris.core.nms.container.IPackRepository; import com.volmit.iris.core.nms.container.IPackRepository;
import com.volmit.iris.util.io.IO; import com.volmit.iris.util.io.IO;
import net.minecraft.tags.TagKey;
import net.minecraft.world.level.LevelReader; import net.minecraft.world.level.LevelReader;
import net.minecraft.world.level.block.EntityBlock; import net.minecraft.world.level.block.EntityBlock;
import com.google.common.base.Preconditions; import com.google.common.base.Preconditions;
@@ -594,23 +596,9 @@ public class NMSBinding implements INMSBinding {
} }
} }
public void setTreasurePos(Dolphin dolphin, com.volmit.iris.core.nms.container.BlockPos pos) { public void inject(long seed, Engine engine, World world) {
CraftDolphin cd = (CraftDolphin)dolphin; var chunkMap = ((CraftWorld)world).getHandle().getChunkSource().chunkMap;
cd.getHandle().setTreasurePos(new BlockPos(pos.getX(), pos.getY(), pos.getZ())); chunkMap.generator = new IrisChunkGenerator(chunkMap.generator, seed, engine, world);
cd.getHandle().setGotFish(true);
}
public void inject(long seed, Engine engine, World world) throws NoSuchFieldException, IllegalAccessException {
ServerLevel serverLevel = ((CraftWorld)world).getHandle();
Class<?> clazz = serverLevel.getChunkSource().chunkMap.generator.getClass();
Field biomeSource = getField(clazz, BiomeSource.class);
biomeSource.setAccessible(true);
Field unsafeField = Unsafe.class.getDeclaredField("theUnsafe");
unsafeField.setAccessible(true);
Unsafe unsafe = (Unsafe)unsafeField.get(null);
CustomBiomeSource customBiomeSource = new CustomBiomeSource(seed, engine, world);
unsafe.putObject(biomeSource.get(serverLevel.getChunkSource().chunkMap.generator), unsafe.objectFieldOffset(biomeSource), customBiomeSource);
biomeSource.set(serverLevel.getChunkSource().chunkMap.generator, customBiomeSource);
} }
public Vector3d getBoundingbox(org.bukkit.entity.EntityType entity) { public Vector3d getBoundingbox(org.bukkit.entity.EntityType entity) {
@@ -865,6 +853,23 @@ public class NMSBinding implements INMSBinding {
return new Color(rgba, true); 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 { private static Field getField(Class<?> clazz, Class<?> fieldType) throws NoSuchFieldException {
try { try {
for (Field f : clazz.getDeclaredFields()) { for (Field f : clazz.getDeclaredFields()) {
@@ -0,0 +1,241 @@
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 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.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 final ChunkGenerator delegate;
private final Engine engine;
private final BiomeSource biomeSource;
private final KMap<ResourceKey<Structure>, KSet<String>> structures = new KMap<>();
public IrisChunkGenerator(ChunkGenerator delegate, long seed, Engine engine, World world) {
super(((CraftWorld) world).getHandle(), delegate, null);
this.delegate = delegate;
this.engine = engine;
this.biomeSource = new CustomBiomeSource(seed, engine, world);
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 BiomeSource getBiomeSource() {
return biomeSource;
}
@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);
}
}
@@ -35,6 +35,7 @@ import java.util.logging.Logger;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import com.google.gson.JsonNull; import com.google.gson.JsonNull;
import com.mojang.datafixers.util.Pair;
import com.volmit.iris.core.nms.container.BiomeColor; import com.volmit.iris.core.nms.container.BiomeColor;
import com.volmit.iris.core.nms.container.IPackRepository; import com.volmit.iris.core.nms.container.IPackRepository;
import com.volmit.iris.util.io.IO; import com.volmit.iris.util.io.IO;
@@ -42,6 +43,7 @@ import com.volmit.iris.util.scheduling.J;
import net.minecraft.nbt.*; import net.minecraft.nbt.*;
import net.minecraft.nbt.Tag; import net.minecraft.nbt.Tag;
import net.minecraft.server.commands.data.BlockDataAccessor; import net.minecraft.server.commands.data.BlockDataAccessor;
import net.minecraft.tags.TagKey;
import net.minecraft.world.level.LevelReader; import net.minecraft.world.level.LevelReader;
import net.minecraft.world.level.block.EntityBlock; import net.minecraft.world.level.block.EntityBlock;
import com.google.common.base.Preconditions; import com.google.common.base.Preconditions;
@@ -596,23 +598,9 @@ public class NMSBinding implements INMSBinding {
} }
} }
public void setTreasurePos(Dolphin dolphin, com.volmit.iris.core.nms.container.BlockPos pos) { public void inject(long seed, Engine engine, World world) {
CraftDolphin cd = (CraftDolphin)dolphin; var chunkMap = ((CraftWorld)world).getHandle().getChunkSource().chunkMap;
cd.getHandle().setTreasurePos(new BlockPos(pos.getX(), pos.getY(), pos.getZ())); chunkMap.generator = new IrisChunkGenerator(chunkMap.generator, seed, engine, world);
cd.getHandle().setGotFish(true);
}
public void inject(long seed, Engine engine, World world) throws NoSuchFieldException, IllegalAccessException {
ServerLevel serverLevel = ((CraftWorld)world).getHandle();
Class<?> clazz = serverLevel.getChunkSource().chunkMap.generator.getClass();
Field biomeSource = getField(clazz, BiomeSource.class);
biomeSource.setAccessible(true);
Field unsafeField = Unsafe.class.getDeclaredField("theUnsafe");
unsafeField.setAccessible(true);
Unsafe unsafe = (Unsafe)unsafeField.get(null);
CustomBiomeSource customBiomeSource = new CustomBiomeSource(seed, engine, world);
unsafe.putObject(biomeSource.get(serverLevel.getChunkSource().chunkMap.generator), unsafe.objectFieldOffset(biomeSource), customBiomeSource);
biomeSource.set(serverLevel.getChunkSource().chunkMap.generator, customBiomeSource);
} }
@@ -802,6 +790,23 @@ public class NMSBinding implements INMSBinding {
return new Color(rgba, true); 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 { private static Field getField(Class<?> clazz, Class<?> fieldType) throws NoSuchFieldException {
try { try {
for (Field f : clazz.getDeclaredFields()) { for (Field f : clazz.getDeclaredFields()) {
@@ -0,0 +1,241 @@
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 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.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 final ChunkGenerator delegate;
private final Engine engine;
private final BiomeSource biomeSource;
private final KMap<ResourceKey<Structure>, KSet<String>> structures = new KMap<>();
public IrisChunkGenerator(ChunkGenerator delegate, long seed, Engine engine, World world) {
super(((CraftWorld) world).getHandle(), delegate, null);
this.delegate = delegate;
this.engine = engine;
this.biomeSource = new CustomBiomeSource(seed, engine, world);
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 BiomeSource getBiomeSource() {
return biomeSource;
}
@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);
}
}
@@ -34,6 +34,7 @@ import java.util.logging.Logger;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import com.google.gson.JsonNull; import com.google.gson.JsonNull;
import com.mojang.datafixers.util.Pair;
import com.volmit.iris.core.nms.container.BiomeColor; import com.volmit.iris.core.nms.container.BiomeColor;
import com.volmit.iris.util.scheduling.J; import com.volmit.iris.util.scheduling.J;
import net.minecraft.nbt.*; import net.minecraft.nbt.*;
@@ -41,6 +42,7 @@ import net.minecraft.nbt.Tag;
import net.minecraft.server.commands.data.BlockDataAccessor; import net.minecraft.server.commands.data.BlockDataAccessor;
import com.volmit.iris.core.nms.container.IPackRepository; import com.volmit.iris.core.nms.container.IPackRepository;
import com.volmit.iris.util.io.IO; import com.volmit.iris.util.io.IO;
import net.minecraft.tags.TagKey;
import net.minecraft.world.level.LevelReader; import net.minecraft.world.level.LevelReader;
import net.minecraft.world.level.block.EntityBlock; import net.minecraft.world.level.block.EntityBlock;
import com.google.common.base.Preconditions; import com.google.common.base.Preconditions;
@@ -603,17 +605,9 @@ public class NMSBinding implements INMSBinding {
cd.getHandle().setGotFish(true); cd.getHandle().setGotFish(true);
} }
public void inject(long seed, Engine engine, World world) throws NoSuchFieldException, IllegalAccessException { public void inject(long seed, Engine engine, World world) {
ServerLevel serverLevel = ((CraftWorld)world).getHandle(); var chunkMap = ((CraftWorld)world).getHandle().getChunkSource().chunkMap;
Class<?> clazz = serverLevel.getChunkSource().chunkMap.generator.getClass(); chunkMap.generator = new IrisChunkGenerator(chunkMap.generator, seed, engine, world);
Field biomeSource = getField(clazz, BiomeSource.class);
biomeSource.setAccessible(true);
Field unsafeField = Unsafe.class.getDeclaredField("theUnsafe");
unsafeField.setAccessible(true);
Unsafe unsafe = (Unsafe)unsafeField.get(null);
CustomBiomeSource customBiomeSource = new CustomBiomeSource(seed, engine, world);
unsafe.putObject(biomeSource.get(serverLevel.getChunkSource().chunkMap.generator), unsafe.objectFieldOffset(biomeSource), customBiomeSource);
biomeSource.set(serverLevel.getChunkSource().chunkMap.generator, customBiomeSource);
} }
public Vector3d getBoundingbox(org.bukkit.entity.EntityType entity) { public Vector3d getBoundingbox(org.bukkit.entity.EntityType entity) {
@@ -805,6 +799,23 @@ public class NMSBinding implements INMSBinding {
return new Color(rgba, true); 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 { private static Field getField(Class<?> clazz, Class<?> fieldType) throws NoSuchFieldException {
try { try {
for (Field f : clazz.getDeclaredFields()) { for (Field f : clazz.getDeclaredFields()) {
@@ -0,0 +1,241 @@
package com.volmit.iris.core.nms.v1_20_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 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_20_R1.CraftWorld;
import org.bukkit.craftbukkit.v1_20_R1.generator.CustomChunkGenerator;
import javax.annotation.Nullable;
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 final ChunkGenerator delegate;
private final Engine engine;
private final BiomeSource biomeSource;
private final KMap<ResourceKey<Structure>, KSet<String>> structures = new KMap<>();
public IrisChunkGenerator(ChunkGenerator delegate, long seed, Engine engine, World world) {
super(((CraftWorld) world).getHandle(), delegate, null);
this.delegate = delegate;
this.engine = engine;
this.biomeSource = new CustomBiomeSource(seed, engine, world);
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 BiomeSource getBiomeSource() {
return biomeSource;
}
@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);
}
}
@@ -27,6 +27,7 @@ import com.mojang.datafixers.util.Pair;
import com.mojang.serialization.Codec; import com.mojang.serialization.Codec;
import com.mojang.serialization.JsonOps; import com.mojang.serialization.JsonOps;
import com.mojang.serialization.Lifecycle; import com.mojang.serialization.Lifecycle;
import com.mojang.datafixers.util.Pair;
import com.volmit.iris.Iris; import com.volmit.iris.Iris;
import com.volmit.iris.core.nms.INMSBinding; import com.volmit.iris.core.nms.INMSBinding;
import com.volmit.iris.core.nms.container.BiomeColor; import com.volmit.iris.core.nms.container.BiomeColor;
@@ -68,6 +69,7 @@ import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.MinecraftServer; import net.minecraft.server.MinecraftServer;
import net.minecraft.server.commands.data.BlockDataAccessor; import net.minecraft.server.commands.data.BlockDataAccessor;
import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerLevel;
import net.minecraft.tags.TagKey;
import net.minecraft.world.entity.EntityDimensions; import net.minecraft.world.entity.EntityDimensions;
import net.minecraft.world.level.LevelReader; import net.minecraft.world.level.LevelReader;
import net.minecraft.server.level.progress.ChunkProgressListener; import net.minecraft.server.level.progress.ChunkProgressListener;
@@ -598,12 +600,6 @@ public class NMSBinding implements INMSBinding {
} }
} }
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 Vector3d getBoundingbox(org.bukkit.entity.EntityType entity) { public Vector3d getBoundingbox(org.bukkit.entity.EntityType entity) {
Field[] fields = net.minecraft.world.entity.EntityType.class.getDeclaredFields(); Field[] fields = net.minecraft.world.entity.EntityType.class.getDeclaredFields();
for (Field field : fields) { for (Field field : fields) {
@@ -766,17 +762,9 @@ public class NMSBinding implements INMSBinding {
} }
} }
public void inject(long seed, Engine engine, World world) throws NoSuchFieldException, IllegalAccessException { public void inject(long seed, Engine engine, World world) {
ServerLevel serverLevel = ((CraftWorld)world).getHandle(); var chunkMap = ((CraftWorld)world).getHandle().getChunkSource().chunkMap;
Class<?> clazz = serverLevel.getChunkSource().chunkMap.generator.getClass(); chunkMap.generator = new IrisChunkGenerator(chunkMap.generator, seed, engine, world);
Field biomeSource = getField(clazz, BiomeSource.class);
biomeSource.setAccessible(true);
Field unsafeField = Unsafe.class.getDeclaredField("theUnsafe");
unsafeField.setAccessible(true);
Unsafe unsafe = (Unsafe)unsafeField.get(null);
CustomBiomeSource customBiomeSource = new CustomBiomeSource(seed, engine, world);
unsafe.putObject(biomeSource.get(serverLevel.getChunkSource().chunkMap.generator), unsafe.objectFieldOffset(biomeSource), customBiomeSource);
biomeSource.set(serverLevel.getChunkSource().chunkMap.generator, customBiomeSource);
} }
@Override @Override
@@ -803,6 +791,23 @@ public class NMSBinding implements INMSBinding {
return new Color(rgba, true); 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 { private static Field getField(Class<?> clazz, Class<?> fieldType) throws NoSuchFieldException {
try { try {
for (Field f : clazz.getDeclaredFields()) { for (Field f : clazz.getDeclaredFields()) {
@@ -0,0 +1,241 @@
package com.volmit.iris.core.nms.v1_20_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 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_20_R2.CraftWorld;
import org.bukkit.craftbukkit.v1_20_R2.generator.CustomChunkGenerator;
import javax.annotation.Nullable;
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 final ChunkGenerator delegate;
private final Engine engine;
private final BiomeSource biomeSource;
private final KMap<ResourceKey<Structure>, KSet<String>> structures = new KMap<>();
public IrisChunkGenerator(ChunkGenerator delegate, long seed, Engine engine, World world) {
super(((CraftWorld) world).getHandle(), delegate, null);
this.delegate = delegate;
this.engine = engine;
this.biomeSource = new CustomBiomeSource(seed, engine, world);
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 BiomeSource getBiomeSource() {
return biomeSource;
}
@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);
}
}
@@ -34,11 +34,13 @@ import java.util.logging.Logger;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import com.google.gson.JsonNull; import com.google.gson.JsonNull;
import com.mojang.datafixers.util.Pair;
import com.volmit.iris.core.nms.container.BiomeColor; import com.volmit.iris.core.nms.container.BiomeColor;
import com.volmit.iris.util.scheduling.J; import com.volmit.iris.util.scheduling.J;
import net.minecraft.nbt.*; import net.minecraft.nbt.*;
import net.minecraft.nbt.Tag; import net.minecraft.nbt.Tag;
import net.minecraft.server.commands.data.BlockDataAccessor; import net.minecraft.server.commands.data.BlockDataAccessor;
import net.minecraft.tags.TagKey;
import com.volmit.iris.core.nms.container.IPackRepository; import com.volmit.iris.core.nms.container.IPackRepository;
import com.volmit.iris.util.io.IO; import com.volmit.iris.util.io.IO;
import net.minecraft.server.commands.DataPackCommand; import net.minecraft.server.commands.DataPackCommand;
@@ -599,23 +601,9 @@ public class NMSBinding implements INMSBinding {
} }
} }
public void setTreasurePos(Dolphin dolphin, com.volmit.iris.core.nms.container.BlockPos pos) { public void inject(long seed, Engine engine, World world) {
CraftDolphin cd = (CraftDolphin)dolphin; var chunkMap = ((CraftWorld)world).getHandle().getChunkSource().chunkMap;
cd.getHandle().setTreasurePos(new BlockPos(pos.getX(), pos.getY(), pos.getZ())); chunkMap.generator = new IrisChunkGenerator(chunkMap.generator, seed, engine, world);
cd.getHandle().setGotFish(true);
}
public void inject(long seed, Engine engine, World world) throws NoSuchFieldException, IllegalAccessException {
ServerLevel serverLevel = ((CraftWorld)world).getHandle();
Class<?> clazz = serverLevel.getChunkSource().chunkMap.generator.getClass();
Field biomeSource = getField(clazz, BiomeSource.class);
biomeSource.setAccessible(true);
Field unsafeField = Unsafe.class.getDeclaredField("theUnsafe");
unsafeField.setAccessible(true);
Unsafe unsafe = (Unsafe)unsafeField.get(null);
CustomBiomeSource customBiomeSource = new CustomBiomeSource(seed, engine, world);
unsafe.putObject(biomeSource.get(serverLevel.getChunkSource().chunkMap.generator), unsafe.objectFieldOffset(biomeSource), customBiomeSource);
biomeSource.set(serverLevel.getChunkSource().chunkMap.generator, customBiomeSource);
} }
public Vector3d getBoundingbox(org.bukkit.entity.EntityType entity) { public Vector3d getBoundingbox(org.bukkit.entity.EntityType entity) {
@@ -805,6 +793,23 @@ public class NMSBinding implements INMSBinding {
return new Color(rgba, true); 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 { private static Field getField(Class<?> clazz, Class<?> fieldType) throws NoSuchFieldException {
try { try {
for (Field f : clazz.getDeclaredFields()) { for (Field f : clazz.getDeclaredFields()) {
@@ -40,6 +40,7 @@ import com.volmit.iris.util.mantle.MantleFlag;
import com.volmit.iris.util.math.RNG; import com.volmit.iris.util.math.RNG;
import com.volmit.iris.util.parallel.MultiBurst; import com.volmit.iris.util.parallel.MultiBurst;
import com.volmit.iris.util.scheduling.PrecisionStopwatch; import com.volmit.iris.util.scheduling.PrecisionStopwatch;
import lombok.Getter;
import net.minecraft.core.Holder; import net.minecraft.core.Holder;
import net.minecraft.core.QuartPos; import net.minecraft.core.QuartPos;
import net.minecraft.nbt.CompoundTag; import net.minecraft.nbt.CompoundTag;
@@ -68,6 +69,8 @@ public class Headless implements IHeadless, LevelHeightAccessor {
private final KMap<String, Holder<Biome>> customBiomes = new KMap<>(); private final KMap<String, Holder<Biome>> customBiomes = new KMap<>();
private final KMap<org.bukkit.block.Biome, Holder<Biome>> minecraftBiomes = new KMap<>(); private final KMap<org.bukkit.block.Biome, Holder<Biome>> minecraftBiomes = new KMap<>();
private final RNG BIOME_RNG; private final RNG BIOME_RNG;
private final @Getter int minBuildHeight;
private final @Getter int height;
private boolean closed = false; private boolean closed = false;
public Headless(NMSBinding binding, Engine engine) { public Headless(NMSBinding binding, Engine engine) {
@@ -75,6 +78,8 @@ public class Headless implements IHeadless, LevelHeightAccessor {
this.engine = engine; this.engine = engine;
this.storage = new RegionFileStorage(new File(engine.getWorld().worldFolder(), "region").toPath(), true); this.storage = new RegionFileStorage(new File(engine.getWorld().worldFolder(), "region").toPath(), true);
this.BIOME_RNG = new RNG(engine.getSeedManager().getBiome()); this.BIOME_RNG = new RNG(engine.getSeedManager().getBiome());
this.minBuildHeight = engine.getDimension().getMinHeight();
this.height = engine.getDimension().getMaxHeight() - minBuildHeight;
engine.getWorld().headless(this); engine.getWorld().headless(this);
var dimKey = engine.getDimension().getLoadKey(); var dimKey = engine.getDimension().getLoadKey();
@@ -107,6 +112,8 @@ public class Headless implements IHeadless, LevelHeightAccessor {
@Override @Override
public boolean exists(int x, int z) { public boolean exists(int x, int z) {
if (closed) return false; if (closed) return false;
if (engine.getWorld().hasRealWorld() && engine.getWorld().realWorld().isChunkLoaded(x, z))
return true;
try { try {
CompoundTag tag = storage.read(new ChunkPos(x, z)); CompoundTag tag = storage.read(new ChunkPos(x, z));
return tag != null && !"empty".equals(tag.getString("Status")); return tag != null && !"empty".equals(tag.getString("Status"));
@@ -270,14 +277,4 @@ public class Headless implements IHeadless, LevelHeightAccessor {
minecraftBiomes.clear(); minecraftBiomes.clear();
} }
} }
@Override
public int getHeight() {
return engine.getHeight();
}
@Override
public int getMinBuildHeight() {
return engine.getMinHeight();
}
} }
@@ -0,0 +1,244 @@
package com.volmit.iris.core.nms.v1_20_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.scheduling.S;
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.StructureTags;
import net.minecraft.tags.TagKey;
import net.minecraft.util.random.WeightedRandomList;
import net.minecraft.world.entity.MobCategory;
import net.minecraft.world.item.EnderEyeItem;
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_20_R3.CraftWorld;
import org.bukkit.craftbukkit.v1_20_R3.generator.CustomChunkGenerator;
import javax.annotation.Nullable;
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 final ChunkGenerator delegate;
private final Engine engine;
private final BiomeSource biomeSource;
private final KMap<ResourceKey<Structure>, KSet<String>> structures = new KMap<>();
public IrisChunkGenerator(ChunkGenerator delegate, long seed, Engine engine, World world) {
super(((CraftWorld) world).getHandle(), delegate, null);
this.delegate = delegate;
this.engine = engine;
this.biomeSource = new CustomBiomeSource(seed, engine, world);
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 BiomeSource getBiomeSource() {
return biomeSource;
}
@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);
}
}
@@ -70,11 +70,13 @@ import net.minecraft.world.level.dimension.DimensionType;
import net.minecraft.world.level.dimension.LevelStem; import net.minecraft.world.level.dimension.LevelStem;
import net.minecraft.world.level.storage.LevelStorageSource; import net.minecraft.world.level.storage.LevelStorageSource;
import net.minecraft.world.level.storage.PrimaryLevelData; import net.minecraft.world.level.storage.PrimaryLevelData;
import com.mojang.datafixers.util.Pair;
import com.volmit.iris.core.nms.container.BiomeColor; import com.volmit.iris.core.nms.container.BiomeColor;
import com.volmit.iris.util.scheduling.J; import com.volmit.iris.util.scheduling.J;
import net.minecraft.nbt.*; import net.minecraft.nbt.*;
import net.minecraft.nbt.Tag; import net.minecraft.nbt.Tag;
import net.minecraft.server.commands.data.BlockDataAccessor; import net.minecraft.server.commands.data.BlockDataAccessor;
import net.minecraft.tags.TagKey;
import net.minecraft.world.level.LevelReader; import net.minecraft.world.level.LevelReader;
import net.minecraft.world.level.block.EntityBlock; import net.minecraft.world.level.block.EntityBlock;
import org.bukkit.*; import org.bukkit.*;
@@ -608,23 +610,9 @@ public class NMSBinding implements INMSBinding {
} }
} }
public void setTreasurePos(Dolphin dolphin, com.volmit.iris.core.nms.container.BlockPos pos) { public void inject(long seed, Engine engine, World world) {
CraftDolphin cd = (CraftDolphin)dolphin; var chunkMap = ((CraftWorld)world).getHandle().getChunkSource().chunkMap;
cd.getHandle().setTreasurePos(new BlockPos(pos.getX(), pos.getY(), pos.getZ())); chunkMap.generator = new IrisChunkGenerator(chunkMap.generator, seed, engine, world);
cd.getHandle().setGotFish(true);
}
public void inject(long seed, Engine engine, World world) throws NoSuchFieldException, IllegalAccessException {
ServerLevel serverLevel = ((CraftWorld)world).getHandle();
Class<?> clazz = serverLevel.getChunkSource().chunkMap.generator.getClass();
Field biomeSource = getField(clazz, BiomeSource.class);
biomeSource.setAccessible(true);
Field unsafeField = Unsafe.class.getDeclaredField("theUnsafe");
unsafeField.setAccessible(true);
Unsafe unsafe = (Unsafe)unsafeField.get(null);
CustomBiomeSource customBiomeSource = new CustomBiomeSource(seed, engine, world);
unsafe.putObject(biomeSource.get(serverLevel.getChunkSource().chunkMap.generator), unsafe.objectFieldOffset(biomeSource), customBiomeSource);
biomeSource.set(serverLevel.getChunkSource().chunkMap.generator, customBiomeSource);
} }
public Vector3d getBoundingbox(org.bukkit.entity.EntityType entity) { public Vector3d getBoundingbox(org.bukkit.entity.EntityType entity) {
@@ -813,6 +801,23 @@ public class NMSBinding implements INMSBinding {
return new Color(rgba, true); 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 { private static Field getField(Class<?> clazz, Class<?> fieldType) throws NoSuchFieldException {
try { try {
for (Field f : clazz.getDeclaredFields()) { for (Field f : clazz.getDeclaredFields()) {
@@ -60,8 +60,6 @@ public record MCATerrainChunk(ChunkAccess chunk) implements TerrainChunk {
@Override @Override
public void setBiome(int x, int y, int z, Biome bio) { public void setBiome(int x, int y, int z, Biome bio) {
if (y < 0) return;
y += getMinHeight();
if (y > getMaxHeight()) return; if (y > getMaxHeight()) return;
chunk.setBiome(x & 15, y, z & 15, CraftBiome.bukkitToMinecraftHolder(bio)); chunk.setBiome(x & 15, y, z & 15, CraftBiome.bukkitToMinecraftHolder(bio));
} }
@@ -82,10 +80,6 @@ public record MCATerrainChunk(ChunkAccess chunk) implements TerrainChunk {
@Override @Override
public void setBlock(int x, int y, int z, BlockData blockData) { public void setBlock(int x, int y, int z, BlockData blockData) {
if (y < 0) return;
y += getMinHeight();
if (y > getMaxHeight()) return;
if (blockData == null) { if (blockData == null) {
Iris.error("NULL BD"); Iris.error("NULL BD");
} }
@@ -97,9 +91,6 @@ public record MCATerrainChunk(ChunkAccess chunk) implements TerrainChunk {
} }
private BlockState getBlockState(int x, int y, int z) { private BlockState getBlockState(int x, int y, int z) {
if (y < 0) {
y = 0;
}
y += getMinHeight(); y += getMinHeight();
if (y > getMaxHeight()) { if (y > getMaxHeight()) {
y = getMaxHeight(); y = getMaxHeight();
@@ -0,0 +1,239 @@
package com.volmit.iris.core.nms.v1_20_R4;
import com.mojang.datafixers.util.Pair;
import com.mojang.serialization.MapCodec;
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 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_20_R4.CraftWorld;
import org.bukkit.craftbukkit.v1_20_R4.generator.CustomChunkGenerator;
import javax.annotation.Nullable;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.*;
public class IrisChunkGenerator extends CustomChunkGenerator {
private final ChunkGenerator delegate;
private final Engine engine;
private final BiomeSource biomeSource;
private final KMap<ResourceKey<Structure>, KSet<String>> structures = new KMap<>();
public IrisChunkGenerator(ChunkGenerator delegate, long seed, Engine engine, World world) {
super(((CraftWorld) world).getHandle(), delegate, null);
this.delegate = delegate;
this.engine = engine;
this.biomeSource = new CustomBiomeSource(seed, engine, world);
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 MapCodec<? extends ChunkGenerator> codec() {
return MapCodec.unit(null);
}
@Override
public ChunkGenerator getDelegate() {
if (delegate instanceof CustomChunkGenerator chunkGenerator)
return chunkGenerator.getDelegate();
return delegate;
}
@Override
public BiomeSource getBiomeSource() {
return biomeSource;
}
@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);
}
}
@@ -34,6 +34,7 @@ import java.util.logging.Logger;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import com.google.gson.JsonNull; import com.google.gson.JsonNull;
import com.mojang.datafixers.util.Pair;
import com.volmit.iris.core.nms.container.BiomeColor; import com.volmit.iris.core.nms.container.BiomeColor;
import com.google.common.base.Preconditions; import com.google.common.base.Preconditions;
import com.google.gson.JsonElement; import com.google.gson.JsonElement;
@@ -75,6 +76,8 @@ import net.minecraft.nbt.StringTag;
import net.minecraft.nbt.Tag; import net.minecraft.nbt.Tag;
import net.minecraft.server.commands.data.BlockDataAccessor; import net.minecraft.server.commands.data.BlockDataAccessor;
import net.minecraft.server.commands.data.DataCommands; import net.minecraft.server.commands.data.DataCommands;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.tags.TagKey;
import net.minecraft.world.item.component.CustomData; import net.minecraft.world.item.component.CustomData;
import net.minecraft.world.level.LevelReader; import net.minecraft.world.level.LevelReader;
import net.minecraft.world.level.Level; import net.minecraft.world.level.Level;
@@ -96,6 +99,7 @@ import org.bukkit.craftbukkit.v1_20_R4.block.CraftBlockStates;
import org.bukkit.craftbukkit.v1_20_R4.block.CraftBlockType; import org.bukkit.craftbukkit.v1_20_R4.block.CraftBlockType;
import org.bukkit.craftbukkit.v1_20_R4.block.data.CraftBlockData; import org.bukkit.craftbukkit.v1_20_R4.block.data.CraftBlockData;
import org.bukkit.craftbukkit.v1_20_R4.entity.CraftDolphin; import org.bukkit.craftbukkit.v1_20_R4.entity.CraftDolphin;
import org.bukkit.craftbukkit.v1_20_R4.generator.CustomChunkGenerator;
import org.bukkit.craftbukkit.v1_20_R4.inventory.CraftItemStack; import org.bukkit.craftbukkit.v1_20_R4.inventory.CraftItemStack;
import org.bukkit.craftbukkit.v1_20_R4.util.CraftNamespacedKey; import org.bukkit.craftbukkit.v1_20_R4.util.CraftNamespacedKey;
import org.bukkit.entity.Dolphin; import org.bukkit.entity.Dolphin;
@@ -126,15 +130,12 @@ import it.unimi.dsi.fastutil.objects.Object2IntMap;
import net.minecraft.core.registries.Registries; import net.minecraft.core.registries.Registries;
import net.minecraft.resources.ResourceKey; import net.minecraft.resources.ResourceKey;
import net.minecraft.resources.ResourceLocation; import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.entity.EntityType; 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.Block;
import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.chunk.ChunkAccess; import net.minecraft.world.level.chunk.ChunkAccess;
import net.minecraft.world.level.chunk.LevelChunk; import net.minecraft.world.level.chunk.LevelChunk;
import sun.misc.Unsafe;
public class NMSBinding implements INMSBinding { public class NMSBinding implements INMSBinding {
private final KMap<Biome, Object> baseBiomeCache = new KMap<>(); private final KMap<Biome, Object> baseBiomeCache = new KMap<>();
@@ -598,23 +599,9 @@ public class NMSBinding implements INMSBinding {
} }
} }
public void setTreasurePos(Dolphin dolphin, com.volmit.iris.core.nms.container.BlockPos pos) { public void inject(long seed, Engine engine, World world) {
CraftDolphin cd = (CraftDolphin)dolphin; var chunkMap = ((CraftWorld)world).getHandle().getChunkSource().chunkMap;
cd.getHandle().setTreasurePos(new BlockPos(pos.getX(), pos.getY(), pos.getZ())); chunkMap.generator = new IrisChunkGenerator(chunkMap.generator, seed, engine, world);
cd.getHandle().setGotFish(true);
}
public void inject(long seed, Engine engine, World world) throws NoSuchFieldException, IllegalAccessException {
ServerLevel serverLevel = ((CraftWorld)world).getHandle();
Class<?> clazz = serverLevel.getChunkSource().chunkMap.generator.getClass();
Field biomeSource = getField(clazz, BiomeSource.class);
biomeSource.setAccessible(true);
Field unsafeField = Unsafe.class.getDeclaredField("theUnsafe");
unsafeField.setAccessible(true);
Unsafe unsafe = (Unsafe)unsafeField.get(null);
CustomBiomeSource customBiomeSource = new CustomBiomeSource(seed, engine, world);
unsafe.putObject(biomeSource.get(serverLevel.getChunkSource().chunkMap.generator), unsafe.objectFieldOffset(biomeSource), customBiomeSource);
biomeSource.set(serverLevel.getChunkSource().chunkMap.generator, customBiomeSource);
} }
public Vector3d getBoundingbox(org.bukkit.entity.EntityType entity) { public Vector3d getBoundingbox(org.bukkit.entity.EntityType entity) {
@@ -900,6 +887,23 @@ public class NMSBinding implements INMSBinding {
return (int) Math.pow(2 * radius + 1, 2); return (int) Math.pow(2 * radius + 1, 2);
} }
@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;
}
@Override @Override
public IPackRepository getPackRepository() { public IPackRepository getPackRepository() {
return packRepository; return packRepository;
@@ -0,0 +1,240 @@
package com.volmit.iris.core.nms.v1_21_R1;
import com.mojang.datafixers.util.Pair;
import com.mojang.serialization.MapCodec;
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 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_21_R1.CraftWorld;
import org.bukkit.craftbukkit.v1_21_R1.generator.CustomChunkGenerator;
import javax.annotation.Nullable;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
public class IrisChunkGenerator extends CustomChunkGenerator {
private final ChunkGenerator delegate;
private final Engine engine;
private final BiomeSource biomeSource;
private final KMap<ResourceKey<Structure>, KSet<String>> structures = new KMap<>();
public IrisChunkGenerator(ChunkGenerator delegate, long seed, Engine engine, World world) {
super(((CraftWorld) world).getHandle(), delegate, null);
this.delegate = delegate;
this.engine = engine;
this.biomeSource = new CustomBiomeSource(seed, engine, world);
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 = ResourceLocation.parse(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 MapCodec<? extends ChunkGenerator> codec() {
return MapCodec.unit(null);
}
@Override
public ChunkGenerator getDelegate() {
if (delegate instanceof CustomChunkGenerator chunkGenerator)
return chunkGenerator.getDelegate();
return delegate;
}
@Override
public BiomeSource getBiomeSource() {
return biomeSource;
}
@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(Blender blender, RandomState randomstate, StructureManager structuremanager, ChunkAccess ichunkaccess) {
return delegate.fillFromNoise(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);
}
}
@@ -36,6 +36,7 @@ import com.google.gson.JsonObject;
import com.mojang.datafixers.util.Pair; import com.mojang.datafixers.util.Pair;
import com.mojang.serialization.Codec; import com.mojang.serialization.Codec;
import com.mojang.serialization.JsonOps; import com.mojang.serialization.JsonOps;
import com.mojang.datafixers.util.Pair;
import com.volmit.iris.core.nms.container.BiomeColor; import com.volmit.iris.core.nms.container.BiomeColor;
import com.volmit.iris.core.nms.container.IPackRepository; import com.volmit.iris.core.nms.container.IPackRepository;
import com.volmit.iris.core.nms.datapack.DataVersion; import com.volmit.iris.core.nms.datapack.DataVersion;
@@ -57,6 +58,7 @@ import net.minecraft.nbt.Tag;
import net.minecraft.server.commands.data.BlockDataAccessor; import net.minecraft.server.commands.data.BlockDataAccessor;
import net.minecraft.server.MinecraftServer; import net.minecraft.server.MinecraftServer;
import net.minecraft.server.level.ChunkMap; import net.minecraft.server.level.ChunkMap;
import net.minecraft.tags.TagKey;
import net.minecraft.server.level.progress.ChunkProgressListener; import net.minecraft.server.level.progress.ChunkProgressListener;
import net.minecraft.util.GsonHelper; import net.minecraft.util.GsonHelper;
import net.minecraft.world.RandomSequences; import net.minecraft.world.RandomSequences;
@@ -584,26 +586,17 @@ public class NMSBinding implements INMSBinding {
} }
} }
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) throws NoSuchFieldException, IllegalAccessException { public void inject(long seed, Engine engine, World world) throws NoSuchFieldException, IllegalAccessException {
var chunkMap = ((CraftWorld)world).getHandle().getChunkSource().chunkMap; var chunkMap = ((CraftWorld)world).getHandle().getChunkSource().chunkMap;
var worldGenContextField = getField(chunkMap.getClass(), WorldGenContext.class); var worldGenContextField = getField(chunkMap.getClass(), WorldGenContext.class);
worldGenContextField.setAccessible(true); worldGenContextField.setAccessible(true);
var worldGenContext = (WorldGenContext) worldGenContextField.get(chunkMap); var worldGenContext = (WorldGenContext) worldGenContextField.get(chunkMap);
Class<?> clazz = worldGenContext.generator().getClass();
Field biomeSource = getField(clazz, BiomeSource.class); var newContext = new WorldGenContext(
biomeSource.setAccessible(true); worldGenContext.level(), new IrisChunkGenerator(worldGenContext.generator(), seed, engine, world),
Field unsafeField = Unsafe.class.getDeclaredField("theUnsafe"); worldGenContext.structureManager(), worldGenContext.lightEngine(), worldGenContext.mainThreadMailBox());
unsafeField.setAccessible(true);
Unsafe unsafe = (Unsafe)unsafeField.get(null); worldGenContextField.set(chunkMap, newContext);
CustomBiomeSource customBiomeSource = new CustomBiomeSource(seed, engine, world);
unsafe.putObject(biomeSource.get(worldGenContext.generator()), unsafe.objectFieldOffset(biomeSource), customBiomeSource);
biomeSource.set(worldGenContext.generator(), customBiomeSource);
} }
public Vector3d getBoundingbox(org.bukkit.entity.EntityType entity) { public Vector3d getBoundingbox(org.bukkit.entity.EntityType entity) {
@@ -889,6 +882,23 @@ public class NMSBinding implements INMSBinding {
return (int) Math.pow(2 * radius + 1, 2); return (int) Math.pow(2 * radius + 1, 2);
} }
@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;
}
@Override @Override
public IPackRepository getPackRepository() { public IPackRepository getPackRepository() {
return packRepository; return packRepository;