9
0
mirror of https://github.com/VolmitSoftware/Iris.git synced 2025-12-19 23:19:21 +00:00

Compare commits

...

73 Commits

Author SHA1 Message Date
RePixelatedMC
b279bb7716 wee 2024-11-18 20:22:57 +01:00
RePixelatedMC
2976390e78 Merge branch 'v3.4.3' of https://github.com/VolmitSoftware/Iris into v3.4.3-dev 2024-11-15 11:19:15 +01:00
RePixelatedMC
578070dffe changes 2024-11-13 16:58:36 +01:00
RePixelatedMC
a0d91dbc74 sync and fixes 2024-11-05 10:14:32 +01:00
RePixelatedMC
eb80ae62b1 Fix mem leak and further improvements 2024-11-01 20:47:19 +01:00
RePixelatedMC
a8751c80ab Should fix? 2024-10-29 20:53:16 +01:00
RePixelatedMC
fd38f0d127 better 2024-10-29 13:36:32 +01:00
RePixelatedMC
537b0f8dd6 Should work 2024-10-28 15:44:44 +01:00
RePixelatedMC
560f2f4fdc lol 2024-10-28 15:37:14 +01:00
RePixelatedMC
7c725a9436 Slices + Huge performance increase 2024-10-28 15:30:51 +01:00
RePixelatedMC
6532715490 r 2024-10-28 13:30:31 +01:00
RePixelatedMC
b1bb01665a Stability 2024-10-28 11:12:24 +01:00
Julian Krings
6c69c69868 Merge branch 'v3.4.3' into v3.4.3-dev 2024-10-27 14:06:17 +01:00
RePixelatedMC
9cd7ad6770 Merge branch 'v3.4.3-dev' of https://github.com/VolmitSoftware/Iris into v3.4.3 2024-10-23 20:27:41 +02:00
RePixelatedMC
b70c9fd4d8 deepslate variant support 2024-10-23 13:50:24 +02:00
RePixelatedMC
412f592e1c removed comment 2024-10-22 21:22:01 +02:00
RePixelatedMC
6c0fa9361f Merge remote-tracking branch 'origin/v3.4.3' into v3.4.3
# Conflicts:
#	core/src/main/java/com/volmit/iris/engine/actuator/IrisTerrainNormalActuator.java
2024-10-22 21:20:45 +02:00
RePixelatedMC
b7dcdd2921 yey 2024-10-22 21:20:29 +02:00
RePixelatedMC
1d8acd2d62 Merge remote-tracking branch 'origin/v3.4.3' into v3.4.3 2024-10-22 15:29:05 +02:00
RePixelatedMC
05bf92ca50 huh 2024-10-22 15:28:39 +02:00
RePixelatedMC
dd598d18bf Merge remote-tracking branch 'origin/v3.4.3' into v3.4.3
# Conflicts:
#	core/src/main/java/com/volmit/iris/engine/actuator/IrisTerrainNormalActuator.java
2024-10-22 15:24:10 +02:00
RePixelatedMC
cc6de4a46d oh well back to scratch 2024-10-22 15:23:50 +02:00
RePixelatedMC
9950551ecb Help 2024-10-22 14:13:30 +02:00
RePixelatedMC
dfd27ecbff sync 2024-10-22 10:01:48 +02:00
RePixelatedMC
ac5d5793ab cleanup 2 2024-10-19 17:42:35 +02:00
RePixelatedMC
2133a03c21 cleanup 2024-10-19 17:16:29 +02:00
RePixelatedMC
8755449c13 Untested but should work though 2024-10-18 15:36:09 +02:00
Julian Krings
86ce02789e woops 2024-10-17 17:49:23 +02:00
Julian Krings
773c8129c9 cleanup and improve merger 2024-10-17 17:44:44 +02:00
RePixelatedMC
dbe9f81091 fixes seed inconsistency and a huge performance bump 2024-10-17 16:03:49 +02:00
RePixelatedMC
288e556f2a weee 2024-10-17 13:53:23 +02:00
RePixelatedMC
70822e37de merger! 2024-10-17 13:30:35 +02:00
RePixelatedMC
3f994c18ff sync 2024-10-14 12:24:10 +02:00
RePixelatedMC
36b99ae4b0 sync 2024-10-13 21:54:37 +02:00
RePixelatedMC
25dbc4dfd6 sync 2024-10-13 21:35:58 +02:00
RePixelatedMC
c35366caa4 mid save 2024-10-11 20:21:30 +02:00
Julian Krings
76365e7875 woops 2024-10-09 13:01:31 +02:00
RePixelatedMC
57649a9ec5 sync 2024-10-09 12:57:49 +02:00
Julian Krings
03582751c5 improve CraftWorldInfo creation 2024-10-09 12:24:52 +02:00
RePixelatedMC
13369bbf32 Merge remote-tracking branch 'origin/v3.4.3' into v3.4.3 2024-10-08 13:42:39 +02:00
Julian Krings
f3b1109804 improve error handling 2024-10-07 20:46:00 +02:00
RePixelatedMC
7f530a4b32 sync 2024-10-07 20:21:14 +02:00
Julian Krings
eda44a8ace fix memory worlds 2024-10-07 16:58:59 +02:00
Julian Krings
f6a354b890 fix missing ArrayType and add tabcompletion to vanilla loot tables 2024-10-06 18:27:48 +02:00
RePixelatedMC
db14861b40 mid save 2024-10-05 20:41:29 +02:00
Julian Krings
457b691add whops 2024-10-04 15:03:09 +02:00
Brian Fopiano
d651f204f8 Merge pull request #1117 from hUwUtao/master
make javadoc buildable...
2024-10-02 00:43:56 -04:00
Julian Krings
a61787ecfe woops 2024-10-01 19:31:31 +02:00
Julian Krings
093d77bf8a implement MemoryWorlds 2024-10-01 19:29:29 +02:00
RePixelatedMC
0101130d7a Weee 2024-09-30 20:21:01 +02:00
RePixelatedMC
a3b2a17e2d Merge remote-tracking branch 'mcwg/v3.4.3' into v3.4.3 2024-09-30 08:19:46 +02:00
RePixelatedMC
ff0a68c4f6 Mid changes - SYNC 2024-09-30 08:19:00 +02:00
Julian Krings
e0034dd718 implement vanilla loottables 2024-09-29 20:44:49 +02:00
RePixelatedMC
7c6df58c15 Saving chunks still is a problem though 2024-09-29 19:00:52 +02:00
RePixelatedMC
5d5c5ba9f4 Oh well 2024-09-28 18:43:05 +02:00
RePixelatedMC
85aefcd206 mid save 2024-09-28 18:03:07 +02:00
RePixelatedMC
34a67dc781 Merge remote-tracking branch 'mcwg/v3.4.3' into v3.4.3 2024-09-28 14:35:37 +02:00
RePixelatedMC
1a96128321 backup 2024-09-28 14:24:14 +02:00
RePixelatedMC
1b0f4e6af6 Merge remote-tracking branch 'mcwg/v3.4.3' into v3.4.3 2024-09-28 14:22:44 +02:00
RePixelatedMC
9eb01dd6ca Merge remote-tracking branch 'mcwg/v3.4.3' into v3.4.3 2024-09-28 14:22:05 +02:00
RePixelatedMC
bb6778dc63 Merge remote-tracking branch 'mcwg/v3.4.3' into v3.4.3 2024-09-28 14:20:38 +02:00
RePixelatedMC
9a24627ceb backup 2024-09-27 17:07:01 +02:00
RePixelatedMC
7583b91d46 Merge remote-tracking branch 'newIris/v3.4.3' into v3.4.3 2024-09-27 14:53:07 +02:00
Julian Krings
c418962411 Merge remote-tracking branch 'origin/v3.4.3' into v3.4.3 2024-09-26 21:23:07 +02:00
Julian Krings
8e8366b318 add jigsaw structure marker 2024-09-26 20:56:56 +02:00
RePixelatedMC
a4ad83d462 stuff 2024-09-25 19:21:43 +02:00
RePixelatedMC
ddf0e79a7e few new options!
- Disable caves
- Basic deepslate layer
2024-09-25 19:19:52 +02:00
stdpi
2bd3ac7a9b Merge branch 'VolmitSoftware:master' into master 2024-09-25 02:00:31 +07:00
Brian Neumann-Fopiano
5e437b34e3 Revert "Update LICENSE.md"
This reverts commit 6c6c9654c1.
2024-09-24 14:58:10 -04:00
stdpi
a5ef89a128 add irisDev task to copy javadocs and sources jar 2024-09-22 17:35:00 +07:00
stdpi
558b8e4eca add more jar package (javadocJar sourcesJar) 2024-09-22 16:09:48 +07:00
stdpi
0a001b8a63 fix: javadoc tolerate bad html
and match encoding, ugh
2024-09-22 04:48:18 +07:00
Brian Fopiano
6c6c9654c1 Update LICENSE.md 2024-09-09 16:54:57 -04:00
64 changed files with 4611 additions and 1243 deletions

View File

@@ -43,7 +43,8 @@ registerCustomOutputTask('Coco', 'D://mcsm/plugins')
registerCustomOutputTask('Strange', 'D://Servers/1.17 Test Server/plugins')
registerCustomOutputTask('Vatuu', 'D://Minecraft/Servers/1.19.4/plugins')
registerCustomOutputTask('CrazyDev22', 'C://Users/Julian/Desktop/server/plugins')
registerCustomOutputTask('Pixel', 'C://Users/repix/Iris Dimension Engine/1.20.4 - Development/plugins')
registerCustomOutputTask('Pixel', 'D://Iris Dimension Engine/1.20.4 - Development/plugins')
registerCustomOutputTask('PixelFury', 'C://Users/RePixelatedMC/Iris/1.21 - Development-v3/plugins')
// ========================== UNIX ==============================
registerCustomOutputTaskUnix('CyberpwnLT', '/Users/danielmills/development/server/plugins')
registerCustomOutputTaskUnix('PsychoLT', '/Users/brianfopiano/Developer/RemoteGit/Server/plugins')
@@ -109,10 +110,10 @@ allprojects {
repositories {
mavenCentral()
maven { url "https://repo.papermc.io/repository/maven-public/"}
maven { url "https://repo.papermc.io/repository/maven-public/" }
maven { url "https://repo.codemc.org/repository/maven-public" }
maven { url "https://mvn.lumine.io/repository/maven-public/" }
maven { url "https://jitpack.io"}
maven { url "https://jitpack.io" }
maven { url "https://s01.oss.sonatype.org/content/repositories/snapshots" }
maven { url "https://mvn.lumine.io/repository/maven/" }
@@ -159,6 +160,21 @@ allprojects {
options.compilerArgs << '-parameters'
options.encoding = "UTF-8"
}
javadoc {
options.encoding = "UTF-8"
options.addStringOption('Xdoclint:none', '-quiet')
}
task sourcesJar(type: Jar, dependsOn: classes) {
archiveClassifier.set('sources')
from sourceSets.main.allSource
}
task javadocJar(type: Jar, dependsOn: javadoc) {
archiveClassifier.set('javadoc')
from javadoc.destinationDir
}
}
if (JavaVersion.current().toString() != "17") {
@@ -186,6 +202,20 @@ task iris(type: Copy) {
dependsOn(build)
}
// with classifier: 'javadoc' and 'sources'
task irisDev(type: Copy) {
group "iris"
from("core/build/libs/core-javadoc.jar", "core/build/libs/core-sources.jar")
rename { String fileName ->
fileName.replace("core", "Iris-${version}")
}
into layout.buildDirectory.asFile.get()
dependsOn(iris)
dependsOn("core:sourcesJar")
dependsOn("core:javadocJar")
}
def registerCustomOutputTask(name, path) {
if (!System.properties['os.name'].toLowerCase().contains('windows')) {
return;

View File

@@ -55,7 +55,7 @@ dependencies {
compileOnly 'org.spigotmc:spigot-api:1.20.1-R0.1-SNAPSHOT'
compileOnly 'org.apache.logging.log4j:log4j-api:2.19.0'
compileOnly 'org.apache.logging.log4j:log4j-core:2.19.0'
compileOnly 'commons-io:commons-io:2.13.0'
compileOnly 'commons-io:commons-io:2.14.0'
compileOnly 'commons-lang:commons-lang:2.6'
compileOnly 'com.github.oshi:oshi-core:5.8.5'
compileOnly 'org.lz4:lz4-java:1.8.0'

View File

@@ -91,6 +91,7 @@ import java.io.*;
import java.lang.annotation.Annotation;
import java.lang.management.ManagementFactory;
import java.lang.management.OperatingSystemMXBean;
import java.lang.management.ThreadMXBean;
import java.net.URL;
import java.util.Date;
import java.util.Map;
@@ -811,6 +812,22 @@ public class Iris extends VolmitPlugin implements Listener {
Iris.info("Server type & version: " + C.RED + Bukkit.getVersion());
} else { Iris.info("Server type & version: " + Bukkit.getVersion()); }
Iris.info("Java: " + getJava());
try {
if (getCPUModel().contains("Intel")) {
Iris.info("Server Cpu: " + C.BLUE + getCPUModel());
}
if (getCPUModel().contains("Ryzen")) {
Iris.info("Server Cpu: " + C.RED + getCPUModel());
}
if (!getCPUModel().contains("Ryzen") && !getCPUModel().contains("Intel")) {
Iris.info("Server Cpu: " + C.GRAY + getCPUModel());
}
} catch (Exception e){
Iris.info("Server Cpu: " + C.DARK_RED + "Failed");
}
Iris.info("Threads: " + C.GRAY + Runtime.getRuntime().availableProcessors());
if (!instance.getServer().getVersion().contains("Purpur")) {
if (instance.getServer().getVersion().contains("Spigot") && instance.getServer().getVersion().contains("Bukkit")) {
Iris.info(C.RED + " Iris requires paper or above to function properly..");

View File

@@ -47,6 +47,8 @@ public class IrisSettings {
private IrisSettingsUpdater updater = new IrisSettingsUpdater();
public static int getThreadCount(int c) {
if (System.getProperty("os.name").toLowerCase().contains("win"))
return Runtime.getRuntime().availableProcessors();
return switch (c) {
case -1, -2, -4 -> Runtime.getRuntime().availableProcessors() / -c;
case 0, 1, 2 -> 1;
@@ -135,6 +137,7 @@ public class IrisSettings {
@Data
public static class IrisSettingsConcurrency {
public int parallelism = -1;
public boolean windowsFullPerformance = true;
}
@Data

View File

@@ -1,134 +0,0 @@
/*
* Iris is a World Generator for Minecraft Bukkit Servers
* Copyright (c) 2022 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.commands;
import com.volmit.iris.Iris;
import com.volmit.iris.core.pregenerator.DeepSearchPregenerator;
import com.volmit.iris.core.pregenerator.PregenTask;
import com.volmit.iris.core.pregenerator.TurboPregenerator;
import com.volmit.iris.core.tools.IrisToolbelt;
import com.volmit.iris.util.data.Dimension;
import com.volmit.iris.util.decree.DecreeExecutor;
import com.volmit.iris.util.decree.annotations.Decree;
import com.volmit.iris.util.decree.annotations.Param;
import com.volmit.iris.util.format.C;
import com.volmit.iris.util.math.Position2;
import org.bukkit.Bukkit;
import org.bukkit.World;
import org.bukkit.util.Vector;
import java.io.File;
import java.io.IOException;
@Decree(name = "DeepSearch", aliases = "search", description = "Pregenerate your Iris worlds!")
public class CommandDeepSearch implements DecreeExecutor {
public String worldName;
@Decree(description = "DeepSearch a world")
public void start(
@Param(description = "The radius of the pregen in blocks", aliases = "size")
int radius,
@Param(description = "The world to pregen", contextual = true)
World world,
@Param(aliases = "middle", description = "The center location of the pregen. Use \"me\" for your current location", defaultValue = "0,0")
Vector center
) {
worldName = world.getName();
File worldDirectory = new File(Bukkit.getWorldContainer(), world.getName());
File TurboFile = new File(worldDirectory, "DeepSearch.json");
if (TurboFile.exists()) {
if (DeepSearchPregenerator.getInstance() != null) {
sender().sendMessage(C.BLUE + "DeepSearch is already in progress");
Iris.info(C.YELLOW + "DeepSearch is already in progress");
return;
} else {
try {
TurboFile.delete();
} catch (Exception e){
Iris.error("Failed to delete the old instance file of DeepSearch!");
return;
}
}
}
try {
if (sender().isPlayer() && access() == null) {
sender().sendMessage(C.RED + "The engine access for this world is null!");
sender().sendMessage(C.RED + "Please make sure the world is loaded & the engine is initialized. Generate a new chunk, for example.");
}
DeepSearchPregenerator.DeepSearchJob DeepSearchJob = DeepSearchPregenerator.DeepSearchJob.builder()
.world(world)
.radiusBlocks(radius)
.position(0)
.build();
File SearchGenFile = new File(worldDirectory, "DeepSearch.json");
DeepSearchPregenerator pregenerator = new DeepSearchPregenerator(DeepSearchJob, SearchGenFile);
pregenerator.start();
String msg = C.GREEN + "DeepSearch started in " + C.GOLD + worldName + C.GREEN + " of " + C.GOLD + (radius * 2) + C.GREEN + " by " + C.GOLD + (radius * 2) + C.GREEN + " blocks from " + C.GOLD + center.getX() + "," + center.getZ();
sender().sendMessage(msg);
Iris.info(msg);
} catch (Throwable e) {
sender().sendMessage(C.RED + "Epic fail. See console.");
Iris.reportError(e);
e.printStackTrace();
}
}
@Decree(description = "Stop the active DeepSearch task", aliases = "x")
public void stop(@Param(aliases = "world", description = "The world to pause") World world) throws IOException {
DeepSearchPregenerator DeepSearchInstance = DeepSearchPregenerator.getInstance();
File worldDirectory = new File(Bukkit.getWorldContainer(), world.getName());
File turboFile = new File(worldDirectory, "DeepSearch.json");
if (DeepSearchInstance != null) {
DeepSearchInstance.shutdownInstance(world);
sender().sendMessage(C.LIGHT_PURPLE + "Closed Turbogen instance for " + world.getName());
} else if (turboFile.exists() && turboFile.delete()) {
sender().sendMessage(C.LIGHT_PURPLE + "Closed Turbogen instance for " + world.getName());
} else if (turboFile.exists()) {
Iris.error("Failed to delete the old instance file of Turbo Pregen!");
} else {
sender().sendMessage(C.YELLOW + "No active pregeneration tasks to stop");
}
}
@Decree(description = "Pause / continue the active pregeneration task", aliases = {"t", "resume", "unpause"})
public void pause(
@Param(aliases = "world", description = "The world to pause")
World world
) {
if (TurboPregenerator.getInstance() != null) {
TurboPregenerator.setPausedTurbo(world);
sender().sendMessage(C.GREEN + "Paused/unpaused Turbo Pregen, now: " + (TurboPregenerator.isPausedTurbo(world) ? "Paused" : "Running") + ".");
} else {
File worldDirectory = new File(Bukkit.getWorldContainer(), world.getName());
File TurboFile = new File(worldDirectory, "DeepSearch.json");
if (TurboFile.exists()){
TurboPregenerator.loadTurboGenerator(world.getName());
sender().sendMessage(C.YELLOW + "Started DeepSearch back up!");
} else {
sender().sendMessage(C.YELLOW + "No active DeepSearch tasks to pause/unpause.");
}
}
}
}

View File

@@ -23,19 +23,12 @@ import com.volmit.iris.core.ServerConfigurator;
import com.volmit.iris.core.loader.IrisData;
import com.volmit.iris.core.nms.INMS;
import com.volmit.iris.core.nms.datapack.DataVersion;
import com.volmit.iris.core.nms.v1X.NMSBinding1X;
import com.volmit.iris.core.pregenerator.ChunkUpdater;
import com.volmit.iris.core.service.IrisEngineSVC;
import com.volmit.iris.core.tools.IrisConverter;
import com.volmit.iris.core.tools.IrisPackBenchmarking;
import com.volmit.iris.core.tools.IrisToolbelt;
import com.volmit.iris.core.tools.IrisWorldAnalytics;
import com.volmit.iris.engine.framework.Engine;
import com.volmit.iris.engine.mantle.components.MantleObjectComponent;
import com.volmit.iris.engine.object.IrisBiome;
import com.volmit.iris.engine.object.IrisCave;
import com.volmit.iris.engine.object.IrisDimension;
import com.volmit.iris.engine.object.IrisEntity;
import com.volmit.iris.util.data.Dimension;
import com.volmit.iris.util.decree.DecreeExecutor;
import com.volmit.iris.util.decree.DecreeOrigin;
import com.volmit.iris.util.decree.annotations.Decree;
@@ -44,24 +37,16 @@ import com.volmit.iris.util.format.C;
import com.volmit.iris.util.format.Form;
import com.volmit.iris.util.io.IO;
import com.volmit.iris.util.mantle.TectonicPlate;
import com.volmit.iris.util.math.Spiraler;
import com.volmit.iris.util.math.Vector3d;
import com.volmit.iris.util.nbt.mca.MCAFile;
import com.volmit.iris.util.nbt.mca.MCAUtil;
import com.volmit.iris.util.parallel.MultiBurst;
import com.volmit.iris.util.plugin.VolmitSender;
import io.lumine.mythic.bukkit.adapters.BukkitEntity;
import net.jpountz.lz4.LZ4BlockInputStream;
import net.jpountz.lz4.LZ4BlockOutputStream;
import net.jpountz.lz4.LZ4FrameInputStream;
import net.jpountz.lz4.LZ4FrameOutputStream;
import org.apache.commons.lang.RandomStringUtils;
import org.bukkit.Bukkit;
import org.bukkit.Chunk;
import org.bukkit.Location;
import org.bukkit.World;
import org.bukkit.entity.Creeper;
import org.bukkit.entity.EntityType;
import org.bukkit.*;
import java.io.*;
import java.net.InetAddress;
@@ -75,7 +60,6 @@ import java.util.zip.GZIPOutputStream;
@Decree(name = "Developer", origin = DecreeOrigin.BOTH, description = "Iris World Manager", aliases = {"dev"})
public class CommandDeveloper implements DecreeExecutor {
private CommandTurboPregen turboPregen;
private CommandUpdater updater;
@Decree(description = "Get Loaded TectonicPlates Count", origin = DecreeOrigin.BOTH, sync = true)
@@ -160,6 +144,17 @@ public class CommandDeveloper implements DecreeExecutor {
}
@Decree(description = "gets wg height")
public void whatHeight() {
Iris.info("test");
sender().sendMessage("Height: " + player().getWorld().getHighestBlockAt(player().getLocation(), HeightMap.MOTION_BLOCKING).getY());
}
@Decree(description = "check", aliases = {"ck"} )
public void check() {
sender().sendMessage("Data Pack Biome: " + INMS.get().getTrueBiomeBaseKey(player().getLocation()) + " (ID: " + INMS.get().getTrueBiomeBaseId(INMS.get().getTrueBiomeBase(player().getLocation())) + ")");
}
@Decree(description = "Upgrade to another Minecraft version")
public void upgrade(
@Param(description = "The version to upgrade to", defaultValue = "latest") DataVersion version) {
@@ -175,6 +170,7 @@ public class CommandDeveloper implements DecreeExecutor {
File[] McaFiles = new File(world, "region").listFiles((dir, name) -> name.endsWith(".mca"));
for (File mca : McaFiles) {
MCAFile MCARegion = MCAUtil.read(mca);
int i = 0;
}
} catch (Exception e) {
e.printStackTrace();
@@ -182,6 +178,19 @@ public class CommandDeveloper implements DecreeExecutor {
}
@Decree(description = "test")
public void anl (
@Param(description = "String") String world) {
try {
IrisWorldAnalytics a = new IrisWorldAnalytics(world);
a.execute();
} catch (Exception e) {
e.printStackTrace();
}
}
@Decree(description = "UnloadChunks for good reasons.")
public void unloadchunks() {
List<World> IrisWorlds = new ArrayList<>();

View File

@@ -73,14 +73,12 @@ import static org.bukkit.Bukkit.getServer;
public class CommandIris implements DecreeExecutor {
private CommandStudio studio;
private CommandPregen pregen;
private CommandLazyPregen lazyPregen;
private CommandSettings settings;
private CommandObject object;
private CommandJigsaw jigsaw;
private CommandWhat what;
private CommandEdit edit;
private CommandFind find;
private CommandSupport support;
private CommandDeveloper developer;
public static boolean worldCreation = false;
String WorldEngine;

View File

@@ -1,121 +0,0 @@
/*
* Iris is a World Generator for Minecraft Bukkit Servers
* Copyright (c) 2022 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.commands;
import com.volmit.iris.Iris;
import com.volmit.iris.core.IrisSettings;
import com.volmit.iris.core.gui.PregeneratorJob;
import com.volmit.iris.core.pregenerator.LazyPregenerator;
import com.volmit.iris.core.pregenerator.PregenTask;
import com.volmit.iris.core.tools.IrisToolbelt;
import com.volmit.iris.util.decree.DecreeExecutor;
import com.volmit.iris.util.decree.annotations.Decree;
import com.volmit.iris.util.decree.annotations.Param;
import com.volmit.iris.util.format.C;
import com.volmit.iris.util.math.Position2;
import org.bukkit.Bukkit;
import org.bukkit.World;
import org.bukkit.util.Vector;
import java.io.File;
import java.io.IOException;
@Decree(name = "lazypregen", aliases = "lazy", description = "Pregenerate your Iris worlds!")
public class CommandLazyPregen implements DecreeExecutor {
public String worldName;
@Decree(description = "Pregenerate a world")
public void start(
@Param(description = "The radius of the pregen in blocks", aliases = "size")
int radius,
@Param(description = "The world to pregen", contextual = true)
World world,
@Param(aliases = "middle", description = "The center location of the pregen. Use \"me\" for your current location", defaultValue = "0,0")
Vector center,
@Param(aliases = "maxcpm", description = "Limit the chunks per minute the pregen will generate", defaultValue = "999999999")
int cpm,
@Param(aliases = "silent", description = "Silent generation", defaultValue = "false")
boolean silent
) {
worldName = world.getName();
File worldDirectory = new File(Bukkit.getWorldContainer(), world.getName());
File lazyFile = new File(worldDirectory, "lazygen.json");
if (lazyFile.exists()) {
sender().sendMessage(C.BLUE + "Lazy pregen is already in progress");
Iris.info(C.YELLOW + "Lazy pregen is already in progress");
return;
}
try {
if (sender().isPlayer() && access() == null) {
sender().sendMessage(C.RED + "The engine access for this world is null!");
sender().sendMessage(C.RED + "Please make sure the world is loaded & the engine is initialized. Generate a new chunk, for example.");
}
LazyPregenerator.LazyPregenJob pregenJob = LazyPregenerator.LazyPregenJob.builder()
.world(worldName)
.healingPosition(0)
.healing(false)
.chunksPerMinute(cpm)
.radiusBlocks(radius)
.position(0)
.silent(silent)
.build();
File lazyGenFile = new File(worldDirectory, "lazygen.json");
LazyPregenerator pregenerator = new LazyPregenerator(pregenJob, lazyGenFile);
pregenerator.start();
String msg = C.GREEN + "LazyPregen started in " + C.GOLD + worldName + C.GREEN + " of " + C.GOLD + (radius * 2) + C.GREEN + " by " + C.GOLD + (radius * 2) + C.GREEN + " blocks from " + C.GOLD + center.getX() + "," + center.getZ();
sender().sendMessage(msg);
Iris.info(msg);
} catch (Throwable e) {
sender().sendMessage(C.RED + "Epic fail. See console.");
Iris.reportError(e);
e.printStackTrace();
}
}
@Decree(description = "Stop the active pregeneration task", aliases = "x")
public void stop(
@Param(aliases = "world", description = "The world to pause")
World world
) throws IOException {
if (LazyPregenerator.getInstance() != null) {
LazyPregenerator.getInstance().shutdownInstance(world);
sender().sendMessage(C.LIGHT_PURPLE + "Closed lazygen instance for " + world.getName());
} else {
sender().sendMessage(C.YELLOW + "No active pregeneration tasks to stop");
}
}
@Decree(description = "Pause / continue the active pregeneration task", aliases = {"t", "resume", "unpause"})
public void pause(
@Param(aliases = "world", description = "The world to pause")
World world
) {
if (LazyPregenerator.getInstance() != null) {
LazyPregenerator.getInstance().setPausedLazy(world);
sender().sendMessage(C.GREEN + "Paused/unpaused Lazy Pregen, now: " + (LazyPregenerator.getInstance().isPausedLazy(world) ? "Paused" : "Running") + ".");
} else {
sender().sendMessage(C.YELLOW + "No active Lazy Pregen tasks to pause/unpause.");
}
}
}

View File

@@ -306,7 +306,7 @@ public class CommandStudio implements DecreeExecutor {
Inventory inv = Bukkit.createInventory(null, 27 * 2);
try {
engine().addItems(true, inv, RNG.r, tables, InventorySlotType.STORAGE, player().getLocation().getBlockX(), player().getLocation().getBlockY(), player().getLocation().getBlockZ(), 1);
engine().addItems(true, inv, RNG.r, tables, InventorySlotType.STORAGE, player().getWorld(), player().getLocation().getBlockX(), player().getLocation().getBlockY(), player().getLocation().getBlockZ(), 1);
} catch (Throwable e) {
Iris.reportError(e);
sender().sendMessage(C.RED + "Cannot add items to virtual inventory because of: " + e.getMessage());
@@ -329,7 +329,7 @@ public class CommandStudio implements DecreeExecutor {
inv.clear();
}
engine().addItems(true, inv, new RNG(RNG.r.imax()), tables, InventorySlotType.STORAGE, player().getLocation().getBlockX(), player().getLocation().getBlockY(), player().getLocation().getBlockZ(), 1);
engine().addItems(true, inv, new RNG(RNG.r.imax()), tables, InventorySlotType.STORAGE, player().getWorld(), player().getLocation().getBlockX(), player().getLocation().getBlockY(), player().getLocation().getBlockZ(), 1);
}, 0, fast ? 5 : 35));
sender().sendMessage(C.GREEN + "Opening inventory now!");

View File

@@ -1,82 +0,0 @@
/*
* Iris is a World Generator for Minecraft Bukkit Servers
* Copyright (c) 2022 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.commands;
import com.volmit.iris.Iris;
import com.volmit.iris.core.loader.IrisData;
import com.volmit.iris.core.pregenerator.ChunkUpdater;
import com.volmit.iris.core.service.IrisEngineSVC;
import com.volmit.iris.core.tools.IrisPackBenchmarking;
import com.volmit.iris.core.tools.IrisToolbelt;
import com.volmit.iris.engine.framework.Engine;
import com.volmit.iris.engine.object.IrisDimension;
import com.volmit.iris.util.collection.KList;
import com.volmit.iris.util.decree.DecreeExecutor;
import com.volmit.iris.util.decree.DecreeOrigin;
import com.volmit.iris.util.decree.annotations.Decree;
import com.volmit.iris.util.decree.annotations.Param;
import com.volmit.iris.util.format.C;
import com.volmit.iris.util.format.Form;
import com.volmit.iris.util.io.IO;
import com.volmit.iris.util.mantle.TectonicPlate;
import com.volmit.iris.util.misc.Hastebin;
import com.volmit.iris.util.misc.Platform;
import com.volmit.iris.util.misc.getHardware;
import com.volmit.iris.util.nbt.mca.MCAFile;
import com.volmit.iris.util.nbt.mca.MCAUtil;
import com.volmit.iris.util.plugin.VolmitSender;
import net.jpountz.lz4.LZ4BlockInputStream;
import net.jpountz.lz4.LZ4BlockOutputStream;
import net.jpountz.lz4.LZ4FrameInputStream;
import net.jpountz.lz4.LZ4FrameOutputStream;
import org.apache.commons.lang.RandomStringUtils;
import org.bukkit.Bukkit;
import org.bukkit.Chunk;
import org.bukkit.World;
import oshi.SystemInfo;
import java.io.*;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.util.*;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;
@Decree(name = "Support", origin = DecreeOrigin.BOTH, description = "Iris World Manager", aliases = {"support"})
public class CommandSupport implements DecreeExecutor {
@Decree(description = "report")
public void report() {
try {
if (sender().isPlayer()) sender().sendMessage(C.GOLD + "Creating report..");
if (!sender().isPlayer()) Iris.info(C.GOLD + "Creating report..");
Hastebin.enviornment(sender());
} catch (Exception e) {
Iris.info(C.RED + "Something went wrong: ");
e.printStackTrace();
}
}
}

View File

@@ -1,131 +0,0 @@
/*
* Iris is a World Generator for Minecraft Bukkit Servers
* Copyright (c) 2022 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.commands;
import com.volmit.iris.Iris;
import com.volmit.iris.core.pregenerator.LazyPregenerator;
import com.volmit.iris.core.pregenerator.TurboPregenerator;
import com.volmit.iris.core.pregenerator.TurboPregenerator;
import com.volmit.iris.util.decree.DecreeExecutor;
import com.volmit.iris.util.decree.annotations.Decree;
import com.volmit.iris.util.decree.annotations.Param;
import com.volmit.iris.util.format.C;
import org.bukkit.Bukkit;
import org.bukkit.World;
import org.bukkit.util.Vector;
import java.io.File;
import java.io.IOException;
@Decree(name = "turbopregen", aliases = "turbo", description = "Pregenerate your Iris worlds!")
public class CommandTurboPregen implements DecreeExecutor {
public String worldName;
@Decree(description = "Pregenerate a world")
public void start(
@Param(description = "The radius of the pregen in blocks", aliases = "size")
int radius,
@Param(description = "The world to pregen", contextual = true)
World world,
@Param(aliases = "middle", description = "The center location of the pregen. Use \"me\" for your current location", defaultValue = "0,0")
Vector center
) {
worldName = world.getName();
File worldDirectory = new File(Bukkit.getWorldContainer(), world.getName());
File TurboFile = new File(worldDirectory, "turbogen.json");
if (TurboFile.exists()) {
if (TurboPregenerator.getInstance() != null) {
sender().sendMessage(C.BLUE + "Turbo pregen is already in progress");
Iris.info(C.YELLOW + "Turbo pregen is already in progress");
return;
} else {
try {
TurboFile.delete();
} catch (Exception e){
Iris.error("Failed to delete the old instance file of Turbo Pregen!");
return;
}
}
}
try {
if (sender().isPlayer() && access() == null) {
sender().sendMessage(C.RED + "The engine access for this world is null!");
sender().sendMessage(C.RED + "Please make sure the world is loaded & the engine is initialized. Generate a new chunk, for example.");
}
TurboPregenerator.TurboPregenJob pregenJob = TurboPregenerator.TurboPregenJob.builder()
.world(worldName)
.radiusBlocks(radius)
.position(0)
.build();
File TurboGenFile = new File(worldDirectory, "turbogen.json");
TurboPregenerator pregenerator = new TurboPregenerator(pregenJob, TurboGenFile);
pregenerator.start();
String msg = C.GREEN + "TurboPregen started in " + C.GOLD + worldName + C.GREEN + " of " + C.GOLD + (radius * 2) + C.GREEN + " by " + C.GOLD + (radius * 2) + C.GREEN + " blocks from " + C.GOLD + center.getX() + "," + center.getZ();
sender().sendMessage(msg);
Iris.info(msg);
} catch (Throwable e) {
sender().sendMessage(C.RED + "Epic fail. See console.");
Iris.reportError(e);
e.printStackTrace();
}
}
@Decree(description = "Stop the active pregeneration task", aliases = "x")
public void stop(@Param(aliases = "world", description = "The world to pause") World world) throws IOException {
TurboPregenerator turboPregenInstance = TurboPregenerator.getInstance();
File worldDirectory = new File(Bukkit.getWorldContainer(), world.getName());
File turboFile = new File(worldDirectory, "turbogen.json");
if (turboPregenInstance != null) {
turboPregenInstance.shutdownInstance(world);
sender().sendMessage(C.LIGHT_PURPLE + "Closed Turbogen instance for " + world.getName());
} else if (turboFile.exists() && turboFile.delete()) {
sender().sendMessage(C.LIGHT_PURPLE + "Closed Turbogen instance for " + world.getName());
} else if (turboFile.exists()) {
Iris.error("Failed to delete the old instance file of Turbo Pregen!");
} else {
sender().sendMessage(C.YELLOW + "No active pregeneration tasks to stop");
}
}
@Decree(description = "Pause / continue the active pregeneration task", aliases = {"t", "resume", "unpause"})
public void pause(
@Param(aliases = "world", description = "The world to pause")
World world
) {
if (TurboPregenerator.getInstance() != null) {
TurboPregenerator.setPausedTurbo(world);
sender().sendMessage(C.GREEN + "Paused/unpaused Turbo Pregen, now: " + (TurboPregenerator.isPausedTurbo(world) ? "Paused" : "Running") + ".");
} else {
File worldDirectory = new File(Bukkit.getWorldContainer(), world.getName());
File TurboFile = new File(worldDirectory, "turbogen.json");
if (TurboFile.exists()){
TurboPregenerator.loadTurboGenerator(world.getName());
sender().sendMessage(C.YELLOW + "Started Turbo Pregen back up!");
} else {
sender().sendMessage(C.YELLOW + "No active Turbo Pregen tasks to pause/unpause.");
}
}
}
}

View File

@@ -275,9 +275,9 @@ public class NoiseExplorerGUI extends JPanel implements MouseWheelListener, List
n = n > 1 ? 1 : n < 0 ? 0 : n;
try {
//Color color = colorMode ? Color.getHSBColor((float) (n), 1f - (float) (n * n * n * n * n * n), 1f - (float) n) : Color.getHSBColor(0f, 0f, (float) n);
Color color = colorMode ? Color.getHSBColor((float) (n), 1f - (float) (n * n * n * n * n * n), 1f - (float) n) : Color.getHSBColor(0f, 0f, (float) n);
//Color color = colorMode ? Color.getHSBColor((float) (n), (float) (n * n * n * n * n * n), (float) n) : Color.getHSBColor(0f, 0f, (float) n);
Color color = colorMode ? Color.getHSBColor((float) n, (float) (n * n * n * n * n * n), (float) n) : Color.getHSBColor(0f, 0f, (float) n);
//Color color = colorMode ? Color.getHSBColor((float) n, (float) (n * n * n * n * n * n), (float) n) : Color.getHSBColor(0f, 0f, (float) n);
int rgb = color.getRGB();
img.setRGB(xx, z, rgb);

View File

@@ -0,0 +1,15 @@
package com.volmit.iris.core.nms;
import org.bukkit.Chunk;
import org.bukkit.World;
import org.bukkit.event.Listener;
import org.bukkit.generator.ChunkGenerator;
public interface IMemoryWorld extends Listener, AutoCloseable {
World getBukkit();
Chunk getChunk(int x, int z);
ChunkGenerator.ChunkData getChunkData(int x, int z);
}

View File

@@ -18,6 +18,7 @@
package com.volmit.iris.core.nms;
import com.volmit.iris.Iris;
import com.volmit.iris.core.nms.container.BiomeColor;
import com.volmit.iris.core.nms.datapack.DataVersion;
import com.volmit.iris.engine.framework.Engine;
@@ -30,6 +31,7 @@ import com.volmit.iris.util.nbt.mca.palette.MCAPaletteAccess;
import com.volmit.iris.util.nbt.tag.CompoundTag;
import org.bukkit.*;
import org.bukkit.block.Biome;
import org.bukkit.block.data.BlockData;
import org.bukkit.entity.Dolphin;
import org.bukkit.entity.Entity;
import org.bukkit.entity.EntityType;
@@ -40,6 +42,7 @@ import org.bukkit.inventory.ItemStack;
import java.awt.*;
import java.awt.Color;
import java.io.IOException;
public interface INMSBinding {
boolean hasTile(Material material);
@@ -96,6 +99,10 @@ public interface INMSBinding {
int countCustomBiomes();
default boolean setBlock(World world, int x, int y, int z, BlockData data, int flag, int updateDepth) {
throw new UnsupportedOperationException();
}
void forceBiomeInto(int x, int y, int z, Object somethingVeryDirty, ChunkGenerator.BiomeGrid chunk);
default boolean supportsDataPacks() {
@@ -125,4 +132,20 @@ public interface INMSBinding {
}
KList<String> getStructureKeys();
default BlockData getBlockData(CompoundTag tag) {
Iris.error("Unsupported version!");
return null;
};
default IMemoryWorld createMemoryWorld(WorldCreator creator) throws IOException {
return createMemoryWorld(switch (creator.environment()) {
case NORMAL -> NamespacedKey.minecraft("overworld");
case NETHER -> NamespacedKey.minecraft("the_nether");
case THE_END -> NamespacedKey.minecraft("the_end");
default -> throw new IllegalArgumentException("Illegal dimension (" + creator.environment() + ")");
}, creator);
}
IMemoryWorld createMemoryWorld(NamespacedKey levelType, WorldCreator creator) throws IOException;
}

View File

@@ -19,6 +19,7 @@
package com.volmit.iris.core.nms.v1X;
import com.volmit.iris.Iris;
import com.volmit.iris.core.nms.IMemoryWorld;
import com.volmit.iris.core.nms.INMSBinding;
import com.volmit.iris.core.nms.container.BiomeColor;
import com.volmit.iris.core.nms.container.BlockPos;
@@ -32,6 +33,7 @@ import com.volmit.iris.util.nbt.mca.palette.MCAPaletteAccess;
import com.volmit.iris.util.nbt.tag.CompoundTag;
import org.bukkit.*;
import org.bukkit.block.Biome;
import org.bukkit.block.data.BlockData;
import org.bukkit.entity.Dolphin;
import org.bukkit.entity.Entity;
import org.bukkit.entity.EntityType;
@@ -42,6 +44,7 @@ import org.bukkit.inventory.ItemStack;
import java.awt.*;
import java.awt.Color;
import java.io.IOException;
public class NMSBinding1X implements INMSBinding {
private static final boolean supportsCustomHeight = testCustomHeight();
@@ -120,6 +123,11 @@ public class NMSBinding1X implements INMSBinding {
return new KList<>(list);
}
@Override
public IMemoryWorld createMemoryWorld(NamespacedKey levelType, WorldCreator creator) throws IOException {
throw new IOException("Unsupported version!");
}
@Override
public CompoundTag serializeEntity(Entity location) {
return null;

View File

@@ -1,275 +0,0 @@
package com.volmit.iris.core.pregenerator;
import com.google.gson.Gson;
import com.volmit.iris.Iris;
import com.volmit.iris.core.tools.IrisToolbelt;
import com.volmit.iris.engine.framework.Engine;
import com.volmit.iris.engine.object.IrisBiome;
import com.volmit.iris.util.collection.KList;
import com.volmit.iris.util.format.C;
import com.volmit.iris.util.format.Form;
import com.volmit.iris.util.io.IO;
import com.volmit.iris.util.math.M;
import com.volmit.iris.util.math.Position2;
import com.volmit.iris.util.math.RollingSequence;
import com.volmit.iris.util.math.Spiraler;
import com.volmit.iris.util.scheduling.ChronoLatch;
import com.volmit.iris.util.scheduling.J;
import lombok.Builder;
import lombok.Data;
import lombok.Getter;
import org.bukkit.Bukkit;
import org.bukkit.World;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.world.WorldUnloadEvent;
import org.bukkit.scheduler.BukkitRunnable;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.locks.ReentrantLock;
public class DeepSearchPregenerator extends Thread implements Listener {
@Getter
private static DeepSearchPregenerator instance;
private final DeepSearchJob job;
private final File destination;
private final int maxPosition;
private World world;
private final ChronoLatch latch;
private static AtomicInteger foundChunks;
private final AtomicInteger foundLast;
private final AtomicInteger foundTotalChunks;
private final AtomicLong startTime;
private final RollingSequence chunksPerSecond;
private final RollingSequence chunksPerMinute;
private final AtomicInteger chunkCachePos;
private final AtomicInteger chunkCacheSize;
private int pos;
private final AtomicInteger foundCacheLast;
private final AtomicInteger foundCache;
private LinkedHashMap<Integer, Position2> chunkCache;
private KList<Position2> chunkQueue;
private final ReentrantLock cacheLock;
private static final Map<String, DeepSearchJob> jobs = new HashMap<>();
public DeepSearchPregenerator(DeepSearchJob job, File destination) {
this.job = job;
this.chunkCacheSize = new AtomicInteger(); // todo
this.chunkCachePos = new AtomicInteger(1000);
this.foundCacheLast = new AtomicInteger();
this.foundCache = new AtomicInteger();
this.cacheLock = new ReentrantLock();
this.destination = destination;
this.chunkCache = new LinkedHashMap<>();
this.maxPosition = new Spiraler(job.getRadiusBlocks() * 2, job.getRadiusBlocks() * 2, (x, z) -> {
}).count();
this.world = Bukkit.getWorld(job.getWorld().getUID());
this.chunkQueue = new KList<>();
this.latch = new ChronoLatch(3000);
this.startTime = new AtomicLong(M.ms());
this.chunksPerSecond = new RollingSequence(10);
this.chunksPerMinute = new RollingSequence(10);
foundChunks = new AtomicInteger(0);
this.foundLast = new AtomicInteger(0);
this.foundTotalChunks = new AtomicInteger((int) Math.ceil(Math.pow((2.0 * job.getRadiusBlocks()) / 16, 2)));
this.pos = 0;
jobs.put(job.getWorld().getName(), job);
DeepSearchPregenerator.instance = this;
}
@EventHandler
public void on(WorldUnloadEvent e) {
if (e.getWorld().equals(world)) {
interrupt();
}
}
public void run() {
while (!interrupted()) {
tick();
}
try {
saveNow();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
public void tick() {
DeepSearchJob job = jobs.get(world.getName());
// chunkCache(); //todo finish this
if (latch.flip() && !job.paused) {
if (cacheLock.isLocked()) {
Iris.info("DeepFinder: Caching: " + chunkCachePos.get() + " Of " + chunkCacheSize.get());
} else {
long eta = computeETA();
save();
int secondGenerated = foundChunks.get() - foundLast.get();
foundLast.set(foundChunks.get());
secondGenerated = secondGenerated / 3;
chunksPerSecond.put(secondGenerated);
chunksPerMinute.put(secondGenerated * 60);
Iris.info("DeepFinder: " + C.IRIS + world.getName() + C.RESET + " Searching: " + Form.f(foundChunks.get()) + " of " + Form.f(foundTotalChunks.get()) + " " + Form.f((int) chunksPerSecond.getAverage()) + "/s ETA: " + Form.duration((double) eta, 2));
}
}
if (foundChunks.get() >= foundTotalChunks.get()) {
Iris.info("Completed DeepSearch!");
interrupt();
}
}
private long computeETA() {
return (long) ((foundTotalChunks.get() - foundChunks.get()) / chunksPerSecond.getAverage()) * 1000;
// todo broken
}
private final ExecutorService executorService = Executors.newSingleThreadExecutor();
private void queueSystem(Position2 chunk) {
if (chunkQueue.isEmpty()) {
for (int limit = 512; limit != 0; limit--) {
pos = job.getPosition() + 1;
chunkQueue.add(getChunk(pos));
}
} else {
//MCAUtil.read();
}
}
private void findInChunk(World world, int x, int z) throws IOException {
int xx = x * 16;
int zz = z * 16;
Engine engine = IrisToolbelt.access(world).getEngine();
for (int i = 0; i < 16; i++) {
for (int j = 0; j < 16; j++) {
int height = engine.getHeight(xx + i, zz + j);
if (height > 300) {
File found = new File("plugins" + "iris" + "found.txt");
FileWriter writer = new FileWriter(found);
if (!found.exists()) {
found.createNewFile();
}
IrisBiome biome = engine.getBiome(xx, engine.getHeight(), zz);
Iris.info("Found at! " + xx + ", " + zz + "Biome ID: " + biome.getName() + ", ");
writer.write("Biome at: X: " + xx + " Z: " + zz + "Biome ID: " + biome.getName() + ", ");
return;
}
}
}
}
public Position2 getChunk(int position) {
int p = -1;
AtomicInteger xx = new AtomicInteger();
AtomicInteger zz = new AtomicInteger();
Spiraler s = new Spiraler(job.getRadiusBlocks() * 2, job.getRadiusBlocks() * 2, (x, z) -> {
xx.set(x);
zz.set(z);
});
while (s.hasNext() && p++ < position) {
s.next();
}
return new Position2(xx.get(), zz.get());
}
public void save() {
J.a(() -> {
try {
saveNow();
} catch (Throwable e) {
e.printStackTrace();
}
});
}
public static void setPausedDeep(World world) {
DeepSearchJob job = jobs.get(world.getName());
if (isPausedDeep(world)){
job.paused = false;
} else {
job.paused = true;
}
if ( job.paused) {
Iris.info(C.BLUE + "DeepSearch: " + C.IRIS + world.getName() + C.BLUE + " Paused");
} else {
Iris.info(C.BLUE + "DeepSearch: " + C.IRIS + world.getName() + C.BLUE + " Resumed");
}
}
public static boolean isPausedDeep(World world) {
DeepSearchJob job = jobs.get(world.getName());
return job != null && job.isPaused();
}
public void shutdownInstance(World world) throws IOException {
Iris.info("DeepSearch: " + C.IRIS + world.getName() + C.BLUE + " Shutting down..");
DeepSearchJob job = jobs.get(world.getName());
File worldDirectory = new File(Bukkit.getWorldContainer(), world.getName());
File deepFile = new File(worldDirectory, "DeepSearch.json");
if (job == null) {
Iris.error("No DeepSearch job found for world: " + world.getName());
return;
}
try {
if (!job.isPaused()) {
job.setPaused(true);
}
save();
jobs.remove(world.getName());
new BukkitRunnable() {
@Override
public void run() {
while (deepFile.exists()){
deepFile.delete();
J.sleep(1000);
}
Iris.info("DeepSearch: " + C.IRIS + world.getName() + C.BLUE + " File deleted and instance closed.");
}
}.runTaskLater(Iris.instance, 20L);
} catch (Exception e) {
Iris.error("Failed to shutdown DeepSearch for " + world.getName());
e.printStackTrace();
} finally {
saveNow();
interrupt();
}
}
public void saveNow() throws IOException {
IO.writeAll(this.destination, new Gson().toJson(job));
}
@Data
@Builder
public static class DeepSearchJob {
private World world;
@Builder.Default
private int radiusBlocks = 5000;
@Builder.Default
private int position = 0;
@Builder.Default
boolean paused = false;
}
}

View File

@@ -1,342 +0,0 @@
package com.volmit.iris.core.pregenerator;
import com.google.gson.Gson;
import com.volmit.iris.Iris;
import com.volmit.iris.core.IrisSettings;
import com.volmit.iris.util.collection.KList;
import com.volmit.iris.util.format.C;
import com.volmit.iris.util.format.Form;
import com.volmit.iris.util.io.IO;
import com.volmit.iris.util.math.M;
import com.volmit.iris.util.math.Position2;
import com.volmit.iris.util.math.RollingSequence;
import com.volmit.iris.util.math.Spiraler;
import com.volmit.iris.util.parallel.BurstExecutor;
import com.volmit.iris.util.parallel.HyperLock;
import com.volmit.iris.util.parallel.MultiBurst;
import com.volmit.iris.util.scheduling.ChronoLatch;
import com.volmit.iris.util.scheduling.J;
import com.volmit.iris.util.scheduling.PrecisionStopwatch;
import io.papermc.lib.PaperLib;
import lombok.Builder;
import lombok.Data;
import lombok.Getter;
import org.apache.logging.log4j.core.util.ExecutorServices;
import org.bukkit.Bukkit;
import org.bukkit.World;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.world.WorldUnloadEvent;
import org.bukkit.scheduler.BukkitRunnable;
import org.checkerframework.checker.units.qual.N;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Array;
import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.locks.ReentrantLock;
import java.util.stream.IntStream;
public class TurboPregenerator extends Thread implements Listener {
@Getter
private static TurboPregenerator instance;
private final TurboPregenJob job;
private final File destination;
private final int maxPosition;
private World world;
private final ChronoLatch latch;
private static AtomicInteger turboGeneratedChunks;
private final AtomicInteger generatedLast;
private final AtomicLong cachedLast;
private final RollingSequence cachePerSecond;
private final AtomicInteger turboTotalChunks;
private final AtomicLong startTime;
private final RollingSequence chunksPerSecond;
private final RollingSequence chunksPerMinute;
private KList<Position2> queue;
private ConcurrentHashMap<Integer, Position2> cache;
private AtomicInteger maxWaiting;
private ReentrantLock cachinglock;
private AtomicBoolean caching;
private final HyperLock hyperLock;
private MultiBurst burst;
private static final Map<String, TurboPregenJob> jobs = new HashMap<>();
public TurboPregenerator(TurboPregenJob job, File destination) {
this.job = job;
queue = new KList<>(512);
this.maxWaiting = new AtomicInteger(128);
this.destination = destination;
this.maxPosition = new Spiraler(job.getRadiusBlocks() * 2, job.getRadiusBlocks() * 2, (x, z) -> {
}).count();
this.world = Bukkit.getWorld(job.getWorld());
this.latch = new ChronoLatch(3000);
this.burst = MultiBurst.burst;
this.hyperLock = new HyperLock();
this.startTime = new AtomicLong(M.ms());
this.cachePerSecond = new RollingSequence(10);
this.chunksPerSecond = new RollingSequence(10);
this.chunksPerMinute = new RollingSequence(10);
turboGeneratedChunks = new AtomicInteger(0);
this.generatedLast = new AtomicInteger(0);
this.cachedLast = new AtomicLong(0);
this.caching = new AtomicBoolean(false);
this.turboTotalChunks = new AtomicInteger((int) Math.ceil(Math.pow((2.0 * job.getRadiusBlocks()) / 16, 2)));
cache = new ConcurrentHashMap<>(turboTotalChunks.get());
this.cachinglock = new ReentrantLock();
jobs.put(job.getWorld(), job);
TurboPregenerator.instance = this;
}
public TurboPregenerator(File file) throws IOException {
this(new Gson().fromJson(IO.readAll(file), TurboPregenerator.TurboPregenJob.class), file);
}
public static void loadTurboGenerator(String i) {
World x = Bukkit.getWorld(i);
File turbogen = new File(x.getWorldFolder(), "turbogen.json");
if (turbogen.exists()) {
try {
TurboPregenerator p = new TurboPregenerator(turbogen);
p.start();
Iris.info("Started Turbo Pregenerator: " + p.job);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
@EventHandler
public void on(WorldUnloadEvent e) {
if (e.getWorld().equals(world)) {
interrupt();
}
}
public void run() {
while (!interrupted()) {
tick();
}
try {
saveNow();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
public void tick() {
TurboPregenJob job = jobs.get(world.getName());
if (!cachinglock.isLocked() && cache.isEmpty() && !caching.get()) {
ExecutorService cache = Executors.newFixedThreadPool(1);
cache.submit(this::cache);
}
if (latch.flip() && caching.get()) {
long secondCached = cache.mappingCount() - cachedLast.get();
cachedLast.set(cache.mappingCount());
secondCached = secondCached / 3;
cachePerSecond.put(secondCached);
Iris.info("TurboGen: " + C.IRIS + world.getName() + C.RESET + C.BLUE + " Caching: " + Form.f(cache.mappingCount()) + " of " + Form.f(turboTotalChunks.get()) + " " + Form.f((int) cachePerSecond.getAverage()) + "/s");
}
if (latch.flip() && !job.paused && !cachinglock.isLocked()) {
long eta = computeETA();
save();
int secondGenerated = turboGeneratedChunks.get() - generatedLast.get();
generatedLast.set(turboGeneratedChunks.get());
secondGenerated = secondGenerated / 3;
chunksPerSecond.put(secondGenerated);
chunksPerMinute.put(secondGenerated * 60);
Iris.info("TurboGen: " + C.IRIS + world.getName() + C.RESET + " RTT: " + Form.f(turboGeneratedChunks.get()) + " of " + Form.f(turboTotalChunks.get()) + " " + Form.f((int) chunksPerSecond.getAverage()) + "/s ETA: " + Form.duration((double) eta, 2));
}
if (turboGeneratedChunks.get() >= turboTotalChunks.get()) {
Iris.info("Completed Turbo Gen!");
interrupt();
} else {
if (!cachinglock.isLocked()) {
int pos = job.getPosition() + 1;
job.setPosition(pos);
if (!job.paused) {
if (queue.size() < maxWaiting.get()) {
Position2 chunk = cache.get(pos);
queue.add(chunk);
}
waitForChunksPartial();
}
}
}
}
private void cache() {
if (!cachinglock.isLocked()) {
cachinglock.lock();
caching.set(true);
PrecisionStopwatch p = PrecisionStopwatch.start();
BurstExecutor b = MultiBurst.burst.burst(turboTotalChunks.get());
b.setMulticore(true);
int[] list = IntStream.rangeClosed(0, turboTotalChunks.get()).toArray();
AtomicInteger order = new AtomicInteger(turboTotalChunks.get());
int threads = Runtime.getRuntime().availableProcessors();
if (threads > 1) threads--;
ExecutorService process = Executors.newFixedThreadPool(threads);
for (int id : list) {
b.queue(() -> {
cache.put(id, getChunk(id));
order.addAndGet(-1);
});
}
b.complete();
if (order.get() < 0) {
cachinglock.unlock();
caching.set(false);
Iris.info("Completed Caching in: " + Form.duration(p.getMilliseconds(), 2));
}
} else {
Iris.error("TurboCache is locked!");
}
}
private void waitForChunksPartial() {
while (!queue.isEmpty() && maxWaiting.get() > queue.size()) {
try {
for (Position2 c : new KList<>(queue)) {
tickGenerate(c);
queue.remove(c);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
private long computeETA() {
return (long) ((turboTotalChunks.get() - turboGeneratedChunks.get()) / chunksPerMinute.getAverage()) * 1000;
// todo broken
}
private final ExecutorService executorService = Executors.newFixedThreadPool(10);
private void tickGenerate(Position2 chunk) {
executorService.submit(() -> {
CountDownLatch latch = new CountDownLatch(1);
PaperLib.getChunkAtAsync(world, chunk.getX(), chunk.getZ(), true)
.thenAccept((i) -> {
latch.countDown();
});
try {
latch.await();
} catch (InterruptedException ignored) {
}
turboGeneratedChunks.addAndGet(1);
});
}
public Position2 getChunk(int position) {
int p = -1;
AtomicInteger xx = new AtomicInteger();
AtomicInteger zz = new AtomicInteger();
Spiraler s = new Spiraler(job.getRadiusBlocks() * 2, job.getRadiusBlocks() * 2, (x, z) -> {
xx.set(x);
zz.set(z);
});
while (s.hasNext() && p++ < position) {
s.next();
}
return new Position2(xx.get(), zz.get());
}
public void save() {
J.a(() -> {
try {
saveNow();
} catch (Throwable e) {
e.printStackTrace();
}
});
}
public static void setPausedTurbo(World world) {
TurboPregenJob job = jobs.get(world.getName());
if (isPausedTurbo(world)) {
job.paused = false;
} else {
job.paused = true;
}
if (job.paused) {
Iris.info(C.BLUE + "TurboGen: " + C.IRIS + world.getName() + C.BLUE + " Paused");
} else {
Iris.info(C.BLUE + "TurboGen: " + C.IRIS + world.getName() + C.BLUE + " Resumed");
}
}
public static boolean isPausedTurbo(World world) {
TurboPregenJob job = jobs.get(world.getName());
return job != null && job.isPaused();
}
public void shutdownInstance(World world) throws IOException {
Iris.info("turboGen: " + C.IRIS + world.getName() + C.BLUE + " Shutting down..");
TurboPregenJob job = jobs.get(world.getName());
File worldDirectory = new File(Bukkit.getWorldContainer(), world.getName());
File turboFile = new File(worldDirectory, "turbogen.json");
if (job == null) {
Iris.error("No turbogen job found for world: " + world.getName());
return;
}
try {
if (!job.isPaused()) {
job.setPaused(true);
}
save();
jobs.remove(world.getName());
new BukkitRunnable() {
@Override
public void run() {
while (turboFile.exists()) {
turboFile.delete();
J.sleep(1000);
}
Iris.info("turboGen: " + C.IRIS + world.getName() + C.BLUE + " File deleted and instance closed.");
}
}.runTaskLater(Iris.instance, 20L);
} catch (Exception e) {
Iris.error("Failed to shutdown turbogen for " + world.getName());
e.printStackTrace();
} finally {
saveNow();
interrupt();
}
}
public void saveNow() throws IOException {
IO.writeAll(this.destination, new Gson().toJson(job));
}
@Data
@Builder
public static class TurboPregenJob {
private String world;
@Builder.Default
private int radiusBlocks = 5000;
@Builder.Default
private int position = 0;
@Builder.Default
boolean paused = false;
}
}

View File

@@ -397,6 +397,10 @@ public class SchemaBuilder {
description.add(SYMBOL_LIMIT__N + " Requires at least " + t.min() + " entries.");
}
}
if (t.max() > 0) {
prop.put("maxItems", t.max());
description.add(SYMBOL_LIMIT__N + " Maximum allowed entries are " + t.max() + ".");
}
String arrayType = getType(t.type());

View File

@@ -125,20 +125,7 @@ public class BoardSVC implements IrisService, BoardProvider {
int y = player.getLocation().getBlockY() - player.getWorld().getMinHeight();
int z = player.getLocation().getBlockZ();
if(IrisSettings.get().getGeneral().debug){
lines.add("&7&m ");
lines.add(C.GREEN + "Speed" + C.GRAY + ": " + Form.f(engine.getGeneratedPerSecond(), 0) + "/s " + Form.duration(1000D / engine.getGeneratedPerSecond(), 0));
lines.add(C.AQUA + "Cache" + C.GRAY + ": " + Form.f(IrisData.cacheSize()));
lines.add(C.AQUA + "Mantle" + C.GRAY + ": " + engine.getMantle().getLoadedRegionCount());
lines.add(C.LIGHT_PURPLE + "Carving" + C.GRAY + ": " + engine.getMantle().isCarved(x,y,z));
lines.add("&7&m ");
lines.add(C.AQUA + "Region" + C.GRAY + ": " + engine.getRegion(x, z).getName());
lines.add(C.AQUA + "Biome" + C.GRAY + ": " + engine.getBiomeOrMantle(x, y, z).getName());
lines.add(C.AQUA + "Height" + C.GRAY + ": " + Math.round(engine.getHeight(x, z)));
lines.add(C.AQUA + "Slope" + C.GRAY + ": " + Form.f(engine.getComplex().getSlopeStream().get(x, z), 2));
lines.add(C.AQUA + "BUD/s" + C.GRAY + ": " + Form.f(engine.getBlockUpdatesPerSecond()));
lines.add("&7&m ");
} else {
lines.add("&7&m ");
lines.add(C.GREEN + "Speed" + C.GRAY + ": " + Form.f(engine.getGeneratedPerSecond(), 0) + "/s " + Form.duration(1000D / engine.getGeneratedPerSecond(), 0));
lines.add(C.AQUA + "Cache" + C.GRAY + ": " + Form.f(IrisData.cacheSize()));
@@ -150,7 +137,7 @@ public class BoardSVC implements IrisService, BoardProvider {
lines.add(C.AQUA + "Slope" + C.GRAY + ": " + Form.f(engine.getComplex().getSlopeStream().get(x, z), 2));
lines.add(C.AQUA + "BUD/s" + C.GRAY + ": " + Form.f(engine.getBlockUpdatesPerSecond()));
lines.add("&7&m ");
}
}
}
}

View File

@@ -20,9 +20,12 @@ package com.volmit.iris.core.service;
import com.google.gson.Gson;
import com.volmit.iris.Iris;
import com.volmit.iris.core.nms.INMS;
import com.volmit.iris.core.nms.v1X.NMSBinding1X;
import com.volmit.iris.engine.object.*;
import com.volmit.iris.util.collection.KList;
import com.volmit.iris.util.collection.KMap;
import com.volmit.iris.util.format.C;
import com.volmit.iris.util.format.Form;
import com.volmit.iris.util.function.Consumer2;
import com.volmit.iris.util.io.Converter;
@@ -37,13 +40,16 @@ import com.volmit.iris.util.nbt.tag.ListTag;
import com.volmit.iris.util.plugin.IrisService;
import com.volmit.iris.util.plugin.VolmitSender;
import com.volmit.iris.util.scheduling.J;
import org.apache.commons.io.FileUtils;
import org.bukkit.Material;
import org.bukkit.block.data.BlockData;
import org.bukkit.block.data.type.Jigsaw;
import java.io.File;
import java.io.IOException;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;
public class ConversionSVC implements IrisService {
private KList<Converter> converters;
@@ -122,7 +128,7 @@ public class ConversionSVC implements IrisService {
@SuppressWarnings("unchecked") ListTag<CompoundTag> paletteList = (ListTag<CompoundTag>) compound.getListTag("palette");
for (int i = 0; i < paletteList.size(); i++) {
CompoundTag cp = paletteList.get(i);
palette.add(NBTWorld.getBlockData(cp));
palette.add(INMS.get().getBlockData(cp));
}
IrisJigsawPiece piece = new IrisJigsawPiece();
IrisObject object = new IrisObject(w, h, d);
@@ -135,20 +141,37 @@ public class ConversionSVC implements IrisService {
int z = pos.get(2).asInt();
BlockData bd = palette.get(cp.getInt("state")).clone();
if (bd.getMaterial().equals(Material.JIGSAW) && cp.containsKey("nbt")) {
piece.setObject(in.toURI().relativize(folder.toURI()).getPath() + file.getName().split("\\Q.\\E")[0]);
if (bd.getMaterial().equals(Material.JIGSAW) && cp.containsKey("nbt")) {
//.setObject(in.toURI().relativize(folder.toURI()).getPath() + file.getName().split("\\Q.\\E")[0]);
IrisPosition spos = new IrisPosition(object.getSigned(x, y, z));
CompoundTag nbt = cp.getCompoundTag("nbt");
CompoundTag finalState = new CompoundTag();
finalState.putString("Name", nbt.getString("final_state"));
BlockData jd = bd.clone();
bd = NBTWorld.getBlockData(finalState);
bd = INMS.get().getBlockData(finalState);
String joint = nbt.getString("joint");
String pool = nbt.getString("pool");
String poolId = toPoolName(pool);
String name = nbt.getString("name");
String target = nbt.getString("target");
pools.computeIfAbsent(poolId, (k) -> new IrisJigsawPool());
pools.computeIfAbsent(poolId, (k) -> {
IrisJigsawPool irisPool = new IrisJigsawPool();
String basePath = in.toURI().relativize(folder.toURI()).getPath();
File baseFolder = new File(in.toURI().relativize(folder.toURI()).toString());
String[] paths = FileUtils.listFiles(folder, null, true)
.stream()
.map(path -> path.getPath().replaceAll("\\.nbt$", "")).toArray(String[]::new);
KList<String> poolList = new KList<>();
for (int ii = 0; ii < Objects.requireNonNull(paths).length; ii++) {
String lastSegment = paths[ii].substring(paths[ii].lastIndexOf("\\") + 1);
poolList.add(basePath + lastSegment);
}
irisPool.setPieces(poolList);
return irisPool;
});
IrisJigsawPieceConnector connector = new IrisJigsawPieceConnector();
connector.setName(name);
connector.setTargetName(target);
@@ -169,10 +192,14 @@ public class ConversionSVC implements IrisService {
}
}
if (piece.getObject().isBlank() || piece.getObject().isEmpty()) {
Iris.info(C.RED + "Failed Setting object with path: " + in.toURI().relativize(folder.toURI()).getPath() + file.getName().split("\\Q.\\E")[0]);
}
jpool.getPieces().addIfMissing(id);
object.write(new File(destObjects, file.getName().split("\\Q.\\E")[0] + ".iob"));
IO.writeAll(new File(destPieces, file.getName().split("\\Q.\\E")[0] + ".json"), new JSONObject(new Gson().toJson(piece)).toString(4));
Iris.info("[Jigsaw]: (" + Form.pc((double) at.get() / (double) total.get(), 0) + ") Exported Piece: " + id);
Iris.info("[Jigsaw]: (" + Form.pc((double) at.get() / (double) total.get(), 0).replace("%", "%%") + ") Exported Piece: " + id);
}
} catch (Throwable e) {
e.printStackTrace();

View File

@@ -2,8 +2,12 @@ package com.volmit.iris.core.tools;
import com.volmit.iris.Iris;
import com.volmit.iris.engine.object.*;
import com.volmit.iris.util.collection.KList;
import com.volmit.iris.util.collection.KMap;
import com.volmit.iris.util.format.C;
import com.volmit.iris.util.format.Form;
import com.volmit.iris.util.function.Consumer2;
import com.volmit.iris.util.misc.E;
import com.volmit.iris.util.nbt.io.NBTUtil;
import com.volmit.iris.util.nbt.io.NamedTag;
import com.volmit.iris.util.nbt.tag.*;
@@ -30,6 +34,10 @@ import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicInteger;
public class IrisConverter {
/**
* Converts all schematics in the convert folder
* @param sender
*/
public static void convertSchematics(VolmitSender sender) {
File folder = Iris.instance.getDataFolder("convert");
@@ -132,6 +140,62 @@ public class IrisConverter {
});
}
// /**
// *
// * @param sender
// */
// public static void convertJigsawStructure(File in, File out, VolmitSender sender) {
// File dataFolder = Iris.instance.getDataFolder("convert");
// try {
// KMap<String, IrisJigsawPool> pools = new KMap<>();
// KList<File> roots = new KList<>();
// AtomicInteger total = new AtomicInteger(0);
// AtomicInteger at = new AtomicInteger(0);
// File destPools = new File(out.getAbsolutePath() + "/jigsaw-pools");
// destPools.mkdirs();
// findAllNBT(in, (folder, file) -> {
// total.getAndIncrement();
// if (roots.addIfMissing(folder)) {
// String b = in.toURI().relativize(folder.toURI()).getPath();
// if (b.startsWith("/")) {
// b = b.substring(1);
// }
//
// if (b.endsWith("/")) {
// b = b.substring(0, b.length() - 1);
// }
//
// pools.put(b, new IrisJigsawPool());
// }
// });
//
// } catch (Exception e) {
// Iris.error(C.RED + "Failed to convert: " + in.getPath());
// e.printStackTrace();
// }
//
//
//
// }
private static void findAllNBT(File path, Consumer2<File, File> inFile) {
if (path == null) {
return;
}
if (path.isFile() && path.getName().endsWith(".nbt")) {
inFile.accept(path.getParentFile(), path);
return;
}
for (File i : path.listFiles()) {
if (i.isDirectory()) {
findAllNBT(i, inFile);
} else if (i.isFile() && i.getName().endsWith(".nbt")) {
inFile.accept(path, i);
}
}
}
}

View File

@@ -0,0 +1,111 @@
package com.volmit.iris.core.tools;
import com.volmit.iris.Iris;
import com.volmit.iris.util.format.Form;
import com.volmit.iris.util.math.M;
import com.volmit.iris.util.math.RollingSequence;
import com.volmit.iris.util.nbt.mca.MCAFile;
import com.volmit.iris.util.nbt.mca.MCAUtil;
import com.volmit.iris.util.parallel.BurstExecutor;
import com.volmit.iris.util.parallel.MultiBurst;
import com.volmit.iris.util.scheduling.ChronoLatch;
import com.volmit.iris.util.scheduling.Looper;
import java.io.File;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
import java.util.function.Supplier;
public class IrisWorldAnalytics {
private final ChronoLatch latch;
private final String world;
private final AtomicInteger totalChunks;
private final AtomicInteger processed;
private final RollingSequence chunksPerSecond;
private final AtomicLong startTime;
private final Looper ticker;
public IrisWorldAnalytics(String world) {
this.world = world;
totalChunks = new AtomicInteger();
processed = new AtomicInteger(0);
latch = new ChronoLatch(3000);
chunksPerSecond = new RollingSequence(3000);
startTime = new AtomicLong(M.ms());
index();
ticker = new Looper() {
@Override
protected long loop() {
return 1000;
}
};
}
public void execute() {
Iris.info("Starting world analyser..");
long startTime = System.currentTimeMillis();
}
private long computeETA() {
return (long) (totalChunks.get() > 1024 ? // Generated chunks exceed 1/8th of total?
// If yes, use smooth function (which gets more accurate over time since its less sensitive to outliers)
((totalChunks.get() - processed.get()) * ((double) (M.ms() - startTime.get()) / (double) processed.get())) :
// If no, use quick function (which is less accurate over time but responds better to the initial delay)
((totalChunks.get() - processed.get()) / chunksPerSecond.getAverage()) * 1000
);
}
private void index() {
try {
AtomicInteger chunks = new AtomicInteger();
AtomicInteger pr = new AtomicInteger();
AtomicInteger pl = new AtomicInteger(0);
RollingSequence rps = new RollingSequence(5);
ChronoLatch cl = new ChronoLatch(3000);
File[] McaFiles = new File(world, "region").listFiles((dir, name) -> name.endsWith(".mca"));
Supplier<Long> eta = () -> (long) ((McaFiles.length - pr.get()) / rps.getAverage()) * 1000;
ScheduledFuture<?> sc = Executors.newSingleThreadScheduledExecutor().scheduleAtFixedRate(() -> {
int sp = pr.get() - pl.get();
pl.set(pr.get());
rps.put(sp);
if (cl.flip()) {
double pc = ((double) pr.get() / (double) McaFiles.length) * 100;
Iris.info("Indexing: " + Form.f(pr.get()) + " of " + Form.f(McaFiles.length) + " (%.0f%%) " + Form.f((int) rps.getAverage()) + "/s ETA: " + Form.duration(eta.get(), 2), pc);
}
}, 3,1, TimeUnit.SECONDS);
BurstExecutor b = MultiBurst.burst.burst(McaFiles.length);
for (File mca : McaFiles) {
b.queue(() -> {
try {
MCAFile region = MCAUtil.read(mca, 0);
var array = region.getChunks();
for (int i = 0; i < array.length(); i++) {
if (array.get(i) != null) {
chunks.incrementAndGet();
}
}
pr.incrementAndGet();
} catch (Exception e) {
e.printStackTrace();
}
});
}
b.complete();
sc.cancel(true);
totalChunks.set(chunks.get());
Iris.info("Indexing completed!");
} catch (Exception e) {
e.printStackTrace();
}
}
}

View File

@@ -0,0 +1,73 @@
package com.volmit.iris.core.tools;
import com.volmit.iris.engine.framework.Engine;
import com.volmit.iris.util.math.Position2;
import com.volmit.iris.util.math.Spiraler;
import org.bukkit.Chunk;
import org.bukkit.Material;
import org.bukkit.World;
import org.bukkit.block.Block;
public class IrisWorldMerger {
private Engine engine;
private World world;
private World selectedWorld;
/**
* @param world > The selected world to get the caves from
* @param engine > The engine of the iris world
*/
public IrisWorldMerger(Engine engine, World world) {
this.engine = engine;
this.world = this.engine.getWorld().realWorld();
this.selectedWorld = world;
}
/**
* Merges caves from a selected chunk into the corresponding chunk in the outcome world.
*
* @param selectedChunk The chunk from the selected world.
* @param targetChunk The corresponding chunk in the outcome world.
*/
private void mergeCavesInChunk(Chunk selectedChunk, Chunk targetChunk) {
int baseX = selectedChunk.getX() << 4;
int baseZ = selectedChunk.getZ() << 4;
for (int x = 0; x < 16; x++) {
int worldX = baseX + x;
for (int z = 0; z < 16; z++) {
int worldZ = baseZ + z;
int surfaceY = engine.getHeight(worldX, worldZ);
for (int y = 0; y <= surfaceY; y++) {
Block selectedBlock = selectedChunk.getBlock(x, y, z);
if (selectedBlock.getType() == Material.AIR) {
Block targetBlock = targetChunk.getBlock(x, y, z);
targetBlock.setType(Material.AIR);
}
}
}
}
}
/**
* Irritates (merges) caves in a spiral pattern around the specified center chunk coordinates.
*
* @param centerX The X coordinate of the center chunk.
* @param centerZ The Z coordinate of the center chunk.
* @param radius The radius (in chunks) to merge caves around.
*/
public void irritateSpiral(int centerX, int centerZ, int radius) {
Spiraler spiraler = new Spiraler(radius * 2, radius * 2, (x, z) -> {
int chunkX = centerX + x;
int chunkZ = centerZ + z;
Chunk selectedChunk = selectedWorld.getChunkAt(chunkX, chunkZ);
Chunk targetChunk = world.getChunkAt(chunkX, chunkZ);
mergeCavesInChunk(selectedChunk, targetChunk);
});
// Execute the spiral iteration
while (spiraler.hasNext()) {
spiraler.next(); // The spiraler itself runs the callback defined in its constructor
}
}
}

View File

@@ -21,10 +21,11 @@ package com.volmit.iris.engine;
import com.google.common.util.concurrent.AtomicDouble;
import com.google.gson.Gson;
import com.volmit.iris.Iris;
import com.volmit.iris.core.IrisSettings;
import com.volmit.iris.core.ServerConfigurator;
import com.volmit.iris.core.events.IrisEngineHotloadEvent;
import com.volmit.iris.core.gui.PregeneratorJob;
import com.volmit.iris.core.nms.IMemoryWorld;
import com.volmit.iris.core.nms.INMS;
import com.volmit.iris.core.nms.container.BlockPos;
import com.volmit.iris.core.nms.container.Pair;
import com.volmit.iris.core.project.IrisProject;
@@ -53,21 +54,24 @@ import com.volmit.iris.util.scheduling.PrecisionStopwatch;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import org.bukkit.Bukkit;
import org.apache.commons.lang3.function.Failable;
import org.bukkit.Material;
import org.bukkit.NamespacedKey;
import org.bukkit.WorldCreator;
import org.bukkit.block.Biome;
import org.bukkit.block.data.BlockData;
import org.bukkit.command.CommandSender;
import org.bukkit.persistence.PersistentDataContainer;
import org.bukkit.persistence.PersistentDataType;
import java.io.File;
import java.io.IOException;
import java.util.HashSet;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@Data
@EqualsAndHashCode(exclude = "context")
@@ -96,6 +100,8 @@ public class IrisEngine implements Engine {
private EngineEffects effects;
private EngineExecutionEnvironment execution;
private EngineWorldManager worldManager;
private IMemoryWorld memoryWorld;
private IrisMerger merger;
private volatile int parallelism;
private volatile int minHeight;
private boolean failing;
@@ -127,6 +133,9 @@ public class IrisEngine implements Engine {
context = new IrisContext(this);
cleaning = new AtomicBoolean(false);
context.touch();
merger = getDimension().getMerger();
merger.loadWorld(this);
updateMemoryWorld();
getData().setEngine(this);
getData().loadPrefetch(this);
Iris.info("Initializing Engine: " + target.getWorld().name() + "/" + target.getDimension().getLoadKey() + " (" + target.getDimension().getDimensionHeight() + " height) Seed: " + getSeedManager().getSeed());
@@ -192,6 +201,34 @@ public class IrisEngine implements Engine {
mode = getDimension().getMode().getType().create(this);
}
private void updateMemoryWorld() {
try {
if(!merger.isUseMemoryWorld() || merger.getGenerator().isEmpty())
return;
merger = getDimension().getMerger();
if (!getDimension().isEnableExperimentalMerger()) return;
if (getMerger().getGenerator().isBlank()) return;
NamespacedKey dk = NamespacedKey.minecraft("memory_current_creator");
PersistentDataContainer per;
if (memoryWorld != null) {
per = memoryWorld.getBukkit().getPersistentDataContainer();
if (Objects.equals(per.get(dk, PersistentDataType.STRING), getMerger().getGenerator()))
return;
if (memoryWorld != null)
memoryWorld.close();
}
memoryWorld = getMerger().isDatapackMode() ? Failable.get(() ->
getMerger().getGenerator() == null ?
INMS.get().createMemoryWorld(new WorldCreator("memoryworld").seed(getSeedManager().getSeed())) :
INMS.get().createMemoryWorld(NamespacedKey.minecraft(getMerger().getGenerator()), new WorldCreator("memoryworld").seed(getSeedManager().getSeed()))
) : null; // todo: experimental
per = memoryWorld.getBukkit().getPersistentDataContainer();
per.set(dk, PersistentDataType.STRING, getMerger().getGenerator());
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public void generateMatter(int x, int z, boolean multicore, ChunkContext context) {
getMantle().generateMatter(x, z, multicore, context);
@@ -241,6 +278,7 @@ public class IrisEngine implements Engine {
getData().clearLists();
getTarget().setDimension(getData().getDimensionLoader().load(getDimension().getLoadKey()));
prehotload();
updateMemoryWorld();
setupEngine();
J.a(() -> {
synchronized (ServerConfigurator.class) {

View File

@@ -18,6 +18,7 @@
package com.volmit.iris.engine.actuator;
import com.volmit.iris.core.nms.IMemoryWorld;
import com.volmit.iris.engine.framework.Engine;
import com.volmit.iris.engine.framework.EngineAssignedActuator;
import com.volmit.iris.engine.object.IrisBiome;
@@ -29,15 +30,18 @@ import com.volmit.iris.util.hunk.Hunk;
import com.volmit.iris.util.math.RNG;
import com.volmit.iris.util.scheduling.PrecisionStopwatch;
import lombok.Getter;
import org.bukkit.Material;
import org.bukkit.*;
import org.bukkit.block.data.BlockData;
public class IrisTerrainNormalActuator extends EngineAssignedActuator<BlockData> {
private static final BlockData AIR = Material.AIR.createBlockData();
private static final BlockData BEDROCK = Material.BEDROCK.createBlockData();
private static final BlockData DEEPSLATE = Material.DEEPSLATE.createBlockData();
private static final BlockData LAVA = Material.LAVA.createBlockData();
private static final BlockData GLASS = Material.GLASS.createBlockData();
private static final BlockData CAVE_AIR = Material.CAVE_AIR.createBlockData();
private static final BlockData FILLER = Material.STONE.createBlockData();
private IMemoryWorld memoryWorld;
@Getter
private final RNG rng;
@Getter
@@ -51,6 +55,7 @@ public class IrisTerrainNormalActuator extends EngineAssignedActuator<BlockData>
@BlockCoordinates
@Override
public void onActuate(int x, int z, Hunk<BlockData> h, boolean multicore, ChunkContext context) {
try {
PrecisionStopwatch p = PrecisionStopwatch.start();
for (int xf = 0; xf < h.getWidth(); xf++) {
@@ -58,7 +63,12 @@ public class IrisTerrainNormalActuator extends EngineAssignedActuator<BlockData>
}
getEngine().getMetrics().getTerrain().put(p.getMilliseconds());
} catch (Exception e) {
e.printStackTrace();
//Iris.error("Fatal Error!", e);
}
}
private int fluidOrHeight(int height) {
return Math.max(getDimension().getFluidHeight(), height);
@@ -138,12 +148,20 @@ public class IrisTerrainNormalActuator extends EngineAssignedActuator<BlockData>
continue;
}
if (getDimension().isEnableExperimentalMerger()) {
h.set(xf, i, zf, FILLER);
continue;
}
BlockData ore = biome.generateOres(realX, i, realZ, rng, getData());
ore = ore == null ? region.generateOres(realX, i, realZ, rng, getData()) : ore;
ore = ore == null ? getDimension().generateOres(realX, i, realZ, rng, getData()) : ore;
if (ore != null) {
h.set(xf, i, zf, ore);
} else {
if (getDimension().isDeepslateLayer() && i < 64) {
h.set(xf, i, zf, DEEPSLATE);
} else {
h.set(xf, i, zf, context.getRock().get(xf, zf));
}
@@ -151,4 +169,5 @@ public class IrisTerrainNormalActuator extends EngineAssignedActuator<BlockData>
}
}
}
}
}

View File

@@ -25,6 +25,7 @@ import com.volmit.iris.core.gui.components.Renderer;
import com.volmit.iris.core.link.Identifier;
import com.volmit.iris.core.loader.IrisData;
import com.volmit.iris.core.loader.IrisRegistrant;
import com.volmit.iris.core.nms.IMemoryWorld;
import com.volmit.iris.core.nms.container.BlockPos;
import com.volmit.iris.core.nms.container.Pair;
import com.volmit.iris.core.pregenerator.ChunkUpdater;
@@ -64,10 +65,7 @@ import com.volmit.iris.util.scheduling.J;
import com.volmit.iris.util.scheduling.PrecisionStopwatch;
import com.volmit.iris.util.stream.ProceduralStream;
import io.papermc.lib.PaperLib;
import org.bukkit.Bukkit;
import org.bukkit.Chunk;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.*;
import org.bukkit.block.Biome;
import org.bukkit.block.Block;
import org.bukkit.block.BlockFace;
@@ -79,6 +77,7 @@ import org.bukkit.inventory.InventoryHolder;
import org.bukkit.inventory.ItemStack;
import java.awt.*;
import java.awt.Color;
import java.util.Arrays;
import java.util.Set;
import java.util.UUID;
@@ -113,6 +112,10 @@ public interface Engine extends DataProvider, Fallible, LootProvider, BlockUpdat
EngineExecutionEnvironment getExecution();
IMemoryWorld getMemoryWorld();
IrisMerger getMerger();
double getMaxBiomeObjectDensity();
double getMaxBiomeDecoratorDensity();
@@ -417,7 +420,7 @@ public interface Engine extends DataProvider, Fallible, LootProvider, BlockUpdat
if (tables.isEmpty())
return;
InventoryHolder m = (InventoryHolder) block.getState();
addItems(false, m.getInventory(), rx, tables, slot, x, y, z, 15);
addItems(false, m.getInventory(), rx, tables, slot, c.getWorld(), x, y, z, 15);
} catch (Throwable e) {
Iris.reportError(e);
@@ -526,7 +529,7 @@ public interface Engine extends DataProvider, Fallible, LootProvider, BlockUpdat
}
@Override
default void addItems(boolean debug, Inventory inv, RNG rng, KList<IrisLootTable> tables, InventorySlotType slot, int x, int y, int z, int mgf) {
default void addItems(boolean debug, Inventory inv, RNG rng, KList<IrisLootTable> tables, InventorySlotType slot, World world, int x, int y, int z, int mgf) {
KList<ItemStack> items = new KList<>();
int b = 4;
@@ -534,7 +537,7 @@ public interface Engine extends DataProvider, Fallible, LootProvider, BlockUpdat
if (i == null)
continue;
b++;
items.addAll(i.getLoot(debug, rng, slot, x, y, z));
items.addAll(i.getLoot(debug, rng, slot, world, x, y, z));
}
if (PaperLib.isPaper() && getWorld().hasRealWorld()) {

View File

@@ -3,29 +3,106 @@ package com.volmit.iris.engine.framework;
import com.volmit.iris.engine.object.InventorySlotType;
import com.volmit.iris.engine.object.IrisLootTable;
import com.volmit.iris.util.collection.KList;
import com.volmit.iris.util.math.RNG;
import lombok.Getter;
import org.bukkit.Bukkit;
import org.bukkit.Material;
import org.bukkit.NamespacedKey;
import org.bukkit.World;
import org.bukkit.block.Block;
import org.bukkit.event.Event;
import org.bukkit.event.HandlerList;
import org.bukkit.event.world.LootGenerateEvent;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.InventoryHolder;
import org.bukkit.inventory.ItemStack;
import org.bukkit.loot.LootContext;
import org.bukkit.loot.LootTable;
import org.bukkit.loot.LootTables;
import javax.annotation.Nullable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
@Getter
public class IrisLootEvent extends Event {
private static final HandlerList handlers = new HandlerList();
private final Engine engine;
private final Block block;
private final InventorySlotType slot;
private final KList<IrisLootTable> tables;
private final Mode mode; // New field to represent the mode
// Define the different modes for the event
public enum Mode {
NORMAL,
BUKKIT_LOOT
}
/**
* Constructor for IrisLootEvent with mode selection.
*
* @param engine The engine instance.
* @param block The block associated with the event.
* @param slot The inventory slot type.
* @param tables The list of IrisLootTables. (mutable*)
*/
public IrisLootEvent(Engine engine, Block block, InventorySlotType slot, KList<IrisLootTable> tables) {
this.engine = engine;
this.block = block;
this.slot = slot;
this.tables = tables;
this.mode = Mode.BUKKIT_LOOT;
if (this.mode == Mode.BUKKIT_LOOT) {
triggerBukkitLootEvent();
}
}
/**
* Triggers the corresponding Bukkit loot event.
* This method integrates your custom IrisLootTables with Bukkit's LootGenerateEvent,
* allowing other plugins to modify or cancel the loot generation.
*/
private Inventory triggerBukkitLootEvent() {
if (block.getState() instanceof InventoryHolder holder) {
Inventory inventory = holder.getInventory();
inventory.clear();
List<ItemStack> loot = new ArrayList<>();
RNG rng = new RNG();
int x = block.getX(), y = block.getY(), z = block.getZ();
for (IrisLootTable table : tables)
loot.addAll(table.getLoot(false, rng, slot, block.getWorld(), x, y, z));
LootContext context = new LootContext.Builder(block.getLocation()).build();
LootTable lootTable = LootTables.EMPTY.getLootTable(); // todo: Correct structure
LootGenerateEvent bukkitEvent = new LootGenerateEvent(engine.getWorld().realWorld(), null, holder, lootTable, context, loot, true); // todo: Use the iris loottable
Bukkit.getServer().getPluginManager().callEvent(bukkitEvent);
if (!bukkitEvent.isCancelled())
inventory.setContents(bukkitEvent.getLoot().toArray(new ItemStack[0]));
return inventory;
}
return null;
}
@Override
public HandlerList getHandlers() {
return handlers;
}
/**
* Required method to get the HandlerList for this event.
*
* @return The HandlerList.
*/
public static HandlerList getHandlerList() {
return handlers;
}

View File

@@ -23,6 +23,7 @@ import com.volmit.iris.engine.object.IrisLootReference;
import com.volmit.iris.engine.object.IrisLootTable;
import com.volmit.iris.util.collection.KList;
import com.volmit.iris.util.math.RNG;
import org.bukkit.World;
import org.bukkit.block.Block;
import org.bukkit.inventory.Inventory;
@@ -33,5 +34,5 @@ public interface LootProvider {
KList<IrisLootTable> getLootTables(RNG rng, Block b);
void addItems(boolean debug, Inventory inv, RNG rng, KList<IrisLootTable> tables, InventorySlotType slot, int x, int y, int z, int mgf);
void addItems(boolean debug, Inventory inv, RNG rng, KList<IrisLootTable> tables, InventorySlotType slot, World world, int x, int y, int z, int mgf);
}

View File

@@ -23,6 +23,7 @@ import org.bukkit.block.Block;
import org.bukkit.block.BlockState;
import org.bukkit.block.TileState;
import org.bukkit.block.data.BlockData;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.InventoryHolder;
@Getter
@@ -64,6 +65,8 @@ public class WorldObjectPlacer implements IObjectPlacer {
RNG rx = new RNG(Cache.key(x, z));
KList<IrisLootTable> tables = engine.getLootTables(rx, block);
Inventory inventory = null;
try {
Bukkit.getPluginManager().callEvent(new IrisLootEvent(engine, block, slot, tables));
@@ -74,7 +77,7 @@ public class WorldObjectPlacer implements IObjectPlacer {
if (tables.isEmpty())
return;
InventoryHolder m = (InventoryHolder) block.getState();
engine.addItems(false, m.getInventory(), rx, tables, slot, x, y, z, 15);
engine.addItems(false, m.getInventory(), rx, tables, slot, world, x, y, z, 15);
} catch (Throwable e) {
Iris.reportError(e);
}

View File

@@ -30,6 +30,7 @@ import com.volmit.iris.util.mantle.Mantle;
import com.volmit.iris.util.math.Position2;
import com.volmit.iris.util.math.RNG;
import com.volmit.iris.util.matter.slices.container.JigsawPieceContainer;
import com.volmit.iris.util.matter.slices.container.JigsawStructureContainer;
import com.volmit.iris.util.matter.slices.container.JigsawStructuresContainer;
import com.volmit.iris.util.scheduling.J;
import lombok.Data;
@@ -151,10 +152,12 @@ public class PlannedStructure {
int id = rng.i(0, Integer.MAX_VALUE);
JigsawPieceContainer container = JigsawPieceContainer.toContainer(i.getPiece());
JigsawStructureContainer structureContainer = JigsawStructureContainer.toContainer(structure);
i.setRealPositions(xx, height, zz, placer);
return v.place(xx, height, zz, placer, options, rng, (b, data) -> {
e.set(b.getX(), b.getY(), b.getZ(), v.getLoadKey() + "@" + id);
e.set(b.getX(), b.getY(), b.getZ(), container);
e.set(b.getX(), b.getY(), b.getZ(), structureContainer);
if (data instanceof IrisBlockData d) {
e.set(b.getX(), b.getY(), b.getZ(), d.getCustom());
}

View File

@@ -67,6 +67,10 @@ public class IrisCarving {
@BlockCoordinates
public void doCarving(MantleWriter writer, RNG rng, Engine engine, int x, int y, int z, int waterHint) {
if (!engine.getDimension().isDoCaves()) {
return;
}
if (caves.isNotEmpty()) {
for (IrisCavePlacer i : caves) {
i.generateCave(writer, rng, engine, x, y, z, waterHint);

View File

@@ -144,6 +144,10 @@ public class IrisDimension extends IrisRegistrant {
@RegistryListResource(IrisJigsawStructure.class)
@Desc("If defined, Iris will place the given jigsaw structure where minecraft should place the overworld stronghold.")
private String stronghold;
@Desc("Iris merger [Experimental] ( Deprecated for v3 )")
private IrisMerger merger = new IrisMerger();
@Desc("Cheap temp solution till v4 arrives [ Enables the experimental merger ] Requires studio restart to take effect!")
private boolean EnableExperimentalMerger = false;
@Desc("If set to true, Iris will remove chunks to allow visualizing cross sections of chunks easily")
private boolean debugChunkCrossSections = false;
@Desc("Vertically split up the biome palettes with 3 air blocks in between to visualize them")
@@ -178,6 +182,10 @@ public class IrisDimension extends IrisRegistrant {
private KList<IrisBlockDrops> blockDrops = new KList<>();
@Desc("Should bedrock be generated or not.")
private boolean bedrock = true;
@Desc("If under 0 deepslate will be placed instead of the rockPalette")
private boolean deepslateLayer = true;
@Desc("If true caves get made")
private boolean doCaves = true;
@MinNumber(0)
@MaxNumber(1)
@Desc("The land chance. Up to 1.0 for total land or 0.0 for total sea")

View File

@@ -271,7 +271,7 @@ public class IrisEntity extends IrisRegistrant {
for (String fi : getLoot().getTables()) {
IrisLootTable i = gen.getData().getLootLoader().load(fi);
items.addAll(i.getLoot(gen.isStudio(), rng.nextParallelRNG(345911), InventorySlotType.STORAGE, finalAt.getBlockX(), finalAt.getBlockY(), finalAt.getBlockZ()));
items.addAll(i.getLoot(gen.isStudio(), rng.nextParallelRNG(345911), InventorySlotType.STORAGE, finalAt.getWorld(), finalAt.getBlockX(), finalAt.getBlockY(), finalAt.getBlockZ()));
}
return items;

View File

@@ -22,6 +22,7 @@ import com.volmit.iris.Iris;
import com.volmit.iris.core.loader.IrisRegistrant;
import com.volmit.iris.engine.data.cache.AtomicCache;
import com.volmit.iris.engine.object.annotations.*;
import com.volmit.iris.engine.object.annotations.functions.StructureKeyFunction;
import com.volmit.iris.util.collection.KList;
import com.volmit.iris.util.json.JSONObject;
import com.volmit.iris.util.plugin.VolmitSender;

View File

@@ -33,6 +33,7 @@ import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
import org.bukkit.Material;
import org.bukkit.World;
import org.bukkit.inventory.ItemStack;
@Accessors(chain = true)
@@ -67,7 +68,7 @@ public class IrisLootTable extends IrisRegistrant {
@ArrayType(min = 1, type = IrisLoot.class)
private KList<IrisLoot> loot = new KList<>();
public KList<ItemStack> getLoot(boolean debug, RNG rng, InventorySlotType slot, int x, int y, int z) {
public KList<ItemStack> getLoot(boolean debug, RNG rng, InventorySlotType slot, World world, int x, int y, int z) {
KList<ItemStack> lootf = new KList<>();
int m = 0;

View File

@@ -0,0 +1,13 @@
package com.volmit.iris.engine.object;
import com.volmit.iris.engine.object.annotations.Desc;
@Desc("Modes for generator merging")
public enum IrisMergeStrategies {
@Desc("Splits the world in height. Use the split settings to customize this option")
SPLIT,
@Desc("Merge from of the engine height")
SPLIT_ENGINE_HEIGHT,
}

View File

@@ -0,0 +1,391 @@
package com.volmit.iris.engine.object;
import com.volmit.iris.Iris;
import com.volmit.iris.core.nms.IMemoryWorld;
import com.volmit.iris.core.nms.INMS;
import com.volmit.iris.engine.framework.Engine;
import com.volmit.iris.engine.object.annotations.Desc;
import com.volmit.iris.util.context.ChunkedDataCache;
import com.volmit.iris.util.format.Form;
import com.volmit.iris.util.hunk.Hunk;
import com.volmit.iris.util.math.RollingSequence;
import com.volmit.iris.util.parallel.BurstExecutor;
import com.volmit.iris.util.parallel.MultiBurst;
import com.volmit.iris.util.scheduling.J;
import com.volmit.iris.util.scheduling.PrecisionStopwatch;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.bukkit.*;
import org.bukkit.block.Biome;
import org.bukkit.block.data.BlockData;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.world.WorldLoadEvent;
import org.bukkit.generator.ChunkGenerator;
import java.io.File;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.ReentrantLock;
import java.util.function.Function;
@AllArgsConstructor
@NoArgsConstructor
@Desc("Dimension Merging only supports 1 for now.")
@Data
public class IrisMerger {
private transient RollingSequence mergeDuration = new RollingSequence(20);
private transient World worldsave;
private transient ReentrantLock lock = new ReentrantLock();
private transient ChunkGenerator chunkGenerator;
private static final BlockData FILLER = Material.STONE.createBlockData();
@Desc("Selected Generator")
private String generator;
@Desc("Use Generator")
private boolean useMemoryWorld = false;
@Desc("Allows to gen chunks on the mergers world")
private boolean allowGenChunks = true;
@Desc("Uses a world instead of a generator")
private String world;
@Desc("Uses the generator as a datapack key")
private boolean datapackMode;
@Desc("How deep till it should use vanilla terrain")
private int depth = 30;
@Desc("Gets the terrain x,z height as the limit")
private IrisMergeStrategies mode = null;
@Desc("If it should put the selected generator above or under the split")
private boolean splitUnder = true;
@Desc("Splits in the engine height")
private int split = 0;
@Desc("If it should translate iris deposits/ores to their deepslate variant")
private boolean deepslateTranslator = true;
private final Map<Chunk, Long> lastUse = new ConcurrentHashMap<>();
/**
* Merges underground from a selected chunk into the corresponding chunk in the outcome world.
*/
@Deprecated
public void generateVanillaUnderground(int cx, int cz, Chunk ichunk, Engine engine) {
if (engine.getMemoryWorld() == null && useMemoryWorld)
throw new IllegalStateException("MemoryWorld is null. Ensure that it has been initialized.");
if (engine.getWorld().realWorld() == null)
return;
if (world == null) {
Iris.error("World merger is null! cant generate chunks FALLBACK!");
return;
}
try {
PrecisionStopwatch p = PrecisionStopwatch.start();
IMemoryWorld memoryWorld;
World bukkit;
if (world.isBlank()) {
memoryWorld = engine.getMemoryWorld();
bukkit = memoryWorld.getBukkit();
} else {
bukkit = Bukkit.getWorld(world);
if (bukkit == null) {
Iris.info("World " + world + " not loaded yet, cannot generate chunk at (" + cx + ", " + cz + ")");
return;
}
}
Chunk chunk;
if (allowGenChunks) {
chunk = bukkit.getChunkAt(cx, cz, true);
} else {
chunk = bukkit.getChunkAt(cx, cz, false);
if (!chunk.isGenerated()) {
throw new IllegalStateException("Chunk " + cx + ", " + cz + " not found. OUT OF BOUNDS");
}
}
// Chunk ichunk = engine.getWorld().realWorld().getChunkAt(cx, cz);
if (!chunk.isLoaded())
J.s(chunk::load);
int totalHeight = bukkit.getMaxHeight() - bukkit.getMinHeight();
int minHeight = Math.abs(bukkit.getMinHeight());
var world = engine.getWorld().realWorld();
int wX = cx << 4;
int wZ = cz << 4;
BurstExecutor b = MultiBurst.burst.burst();
var cache = new ChunkedDataCache<>(b, engine.getComplex().getHeightStream(), wX, wZ);
b.complete();
Set<Biome> caveBiomes = new HashSet<>(Arrays.asList(
Biome.DRIPSTONE_CAVES,
Biome.LUSH_CAVES,
Biome.DEEP_DARK
));
var nms = INMS.get();
var flag = new Flags(false, false, true, false, false).value();
for (int xx = 0; xx < 16; xx += 4) {
for (int zz = 0; zz < 16; zz += 4) {
int maxHeightInSection = 0;
for (int x = 0; x < 4; x++) {
for (int z = 0; z < 4; z++) {
int globalX = xx + x;
int globalZ = zz + z;
int height = (int) Math.ceil(cache.get(globalX, globalZ) - depth);
if (height > maxHeightInSection) {
maxHeightInSection = height;
}
}
}
Hunk<BlockData> vh = getHunkSlice(chunk, xx, zz, maxHeightInSection);
Hunk<BlockData> ih = getHunkSlice(ichunk, xx, zz, maxHeightInSection);
for (int x = 0; x < 4; x++) {
for (int z = 0; z < 4; z++) {
int globalX = xx + x;
int globalZ = zz + z;
int height = (int) Math.ceil(cache.get(globalX, globalZ) - depth);
for (int y = 0; y < totalHeight; y++) {
if (shouldSkip(y, height))
continue;
BlockData blockData = vh.get(x, y, z);
if (!blockData.getMaterial().isAir() && deepslateTranslator) {
if (ih.get(x, y, z).getMaterial() != FILLER.getMaterial() && blockData.getMaterial().isOccluding()) {
try {
BlockData newBlockData = ih.get(x, y, z);
if (hasAround(vh, x, y, z, Material.DEEPSLATE)) {
String id = newBlockData.getMaterial().getItemTranslationKey().replaceFirst("^block\\.[^.]+\\.", "").toUpperCase();
id = "DEEPSLATE_" + id;
Material dps = Material.getMaterial(id);
if (dps != null)
blockData = dps.createBlockData();
}
} catch (Exception e) {
// no Handle exception
}
}
}
nms.setBlock(
world,
wX + globalX,
y - minHeight,
wZ + globalZ,
blockData,
flag,
0
);
if (nms.hasTile(blockData.getMaterial())) {
var tile = nms.serializeTile(new Location(bukkit, wX + globalX, y - minHeight, wZ + globalZ));
if (tile != null) {
nms.deserializeTile(tile, new Location(world, wX + globalX, y - minHeight, wZ + globalZ));
}
}
if (globalX % 4 == 0 && globalZ % 4 == 0 && y % 4 == 0) {
Biome biome;
biome = bukkit.getBiome(wX + globalX, y, wZ + globalZ);
if (caveBiomes.contains(biome)) {
world.setBiome(wX + globalX, y - minHeight, wZ + globalZ, biome);
}
}
}
}
}
}
}
lastUse.put(chunk, System.currentTimeMillis());
mergeDuration.put(p.getMilliseconds());
Iris.info("Vanilla merge average in: " + Form.duration(mergeDuration.getAverage(), 8));
} catch (Exception e) {
e.printStackTrace();
}
}
private boolean shouldSkip(int y, int ht) {
int threshold;
switch (mode) {
case SPLIT_ENGINE_HEIGHT:
threshold = ht;
break;
case SPLIT:
threshold = split;
break;
default:
return false;
}
return splitUnder ? y > threshold : y < threshold;
}
public record Flags(boolean listener, boolean flag, boolean client, boolean update, boolean physics) {
public static Flags fromValue(int value) {
return new Flags(
(value & 1024) != 0,
(value & 64) != 0,
(value & 2) != 0,
(value & 1) != 0,
(value & 16) == 0
);
}
public int value() {
int value = 0;
if (!listener) value |= 1024;
if (flag) value |= 64;
if (client) value |= 2;
if (update) value |= 1;
if (!physics) value |= 16;
return value;
}
}
/**
* Retrieves a 4x4 hunk slice starting at (sx, sz) up to the specified height.
*
* @param chunk The Bukkit chunk
* @param sx Chunk Slice X (must be multiple of 4)
* @param sz Chunk Slice Z (must be multiple of 4)
* @param height The maximum height to process
* @return A hunk of size 4x(totalHeight)x4
*/
private Hunk<BlockData> getHunkSlice(Chunk chunk, int sx, int sz, int height) {
if (!chunk.isGenerated())
throw new IllegalStateException("Chunk is not generated!");
int minHeight = chunk.getWorld().getMinHeight();
int maxHeight = chunk.getWorld().getMaxHeight();
int totalHeight = Math.abs(minHeight) + maxHeight;
Hunk<BlockData> h = Hunk.newHunk(4, totalHeight, 4);
for (int x = 0; x < 4; x++) {
for (int z = 0; z < 4; z++) {
for (int y = 0; y < totalHeight; y++) {
if (shouldSkip(y, height))
continue;
BlockData data = chunk.getBlock(sx + x, y + minHeight, sz + z).getBlockData();
h.set(x, y, z, data);
}
}
}
return h;
}
private boolean hasAround(Hunk<BlockData> hunk, int x, int y, int z, Material material) {
int[] d = {-1, 0, 1};
for (int dx : d) {
for (int dy : d) {
for (int dz : d) {
if (dx == 0 && dy == 0 && dz == 0) continue;
int nx = x + dx;
int ny = y + dy;
int nz = z + dz;
if (nx >= 0 && nx < hunk.getWidth() && nz >= 0 && nz < hunk.getDepth() && ny >= 0 && ny < hunk.getHeight()) {
BlockData neighborBlock = hunk.get(nx, ny, nz);
if (neighborBlock.getMaterial() == material) {
return true;
}
}
}
}
}
return false;
}
public static <T> Hunk<T> copyHunkParallel(Hunk<T> original, Function<T, T> elementCopier) {
Hunk<T> copy = Hunk.newHunk(original.getWidth(), original.getHeight(), original.getDepth());
original.compute3D((ox, oy, oz, section) -> {
Hunk<T> copySection = copy.croppedView(ox, oy, oz, ox + section.getWidth(), oy + section.getHeight(), oz + section.getDepth());
section.iterate((x, y, z, value) -> {
T copiedValue = value != null ? elementCopier.apply(value) : null;
copySection.set(x, y, z, copiedValue);
});
});
return copy;
}
public void loadWorld(Engine engine) {
if (!engine.getDimension().isEnableExperimentalMerger())
return;
World bukkitWorld = Bukkit.getWorld(world);
if (!new File(Bukkit.getWorldContainer(), world).exists())
Iris.warn("World does not exist disabled merger generation for: " + engine.getWorld().name());
//throw new IllegalStateException("World does not exist!");
if (bukkitWorld == null) {
Iris.info("World " + world + " is not loaded yet, creating it.");
Bukkit.getPluginManager().registerEvents(new Listener() {
@EventHandler
public void onWorldLoad(WorldLoadEvent event) {
if (event.getWorld().getName().equals(world)) {
worldsave = event.getWorld();
Iris.info("World " + world + " has been loaded.");
}
}
}, Iris.instance);
WorldCreator worldCreator = new WorldCreator(world);
Bukkit.createWorld(worldCreator);
} else {
worldsave = bukkitWorld;
}
init();
}
public void init() {
Bukkit.getScheduler().runTaskTimer(Iris.instance, this::unloadAndSaveAllChunks, 200L, 20L); // Runs every 10 seconds
}
private void unloadAndSaveAllChunks() {
try {
if (worldsave == null) {
Iris.warn("World was null somehow...");
return;
}
for (Chunk chunk : new ArrayList<>(lastUse.keySet())) {
Long lastUsed = lastUse.get(chunk);
if (lastUsed != null && System.currentTimeMillis() - lastUsed >= 10000) { // 10 seconds
if (chunk.isLoaded()) {
chunk.unload();
}
lastUse.remove(chunk);
}
}
worldsave.save();
} catch (Exception e) {
e.printStackTrace();
}
}
}

View File

@@ -34,9 +34,15 @@ import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
import org.bukkit.Bukkit;
import org.bukkit.Material;
import org.bukkit.NamespacedKey;
import org.bukkit.TreeType;
import org.bukkit.block.data.BlockData;
import org.bukkit.loot.LootTable;
import org.jetbrains.annotations.Nullable;
import java.util.function.Function;
@Snippet("object-placer")
@EqualsAndHashCode()
@@ -123,6 +129,9 @@ public class IrisObjectPlacement {
@ArrayType(min = 1, type = IrisObjectLoot.class)
@Desc("The loot tables to apply to these objects")
private KList<IrisObjectLoot> loot = new KList<>();
@ArrayType(min = 1, type = IrisObjectVanillaLoot.class)
@Desc("The vanilla loot tables to apply to these objects")
private KList<IrisObjectVanillaLoot> vanillaLoot = new KList<>();
@Desc("Whether the given loot tables override any and all other loot tables available in the dimension, region or biome.")
private boolean overrideGlobalLoot = false;
@Desc("This object / these objects override the following trees when they grow...")
@@ -138,7 +147,8 @@ public class IrisObjectPlacement {
private KList<String> forbiddenCollisions = new KList<>();
@Desc("Ignore any placement restrictions for this object")
private boolean forcePlace = false;
private transient AtomicCache<TableCache> cache = new AtomicCache<>();
private transient AtomicCache<TableCache<IrisLootTable>> cache = new AtomicCache<>();
private transient AtomicCache<TableCache<IrisVanillaLootTable>> vanillaCache = new AtomicCache<>();
public IrisObjectPlacement toPlacement(String... place) {
IrisObjectPlacement p = new IrisObjectPlacement();
@@ -209,14 +219,21 @@ public class IrisObjectPlacement {
return (int) Math.round(densityStyle.get(rng, x, z, data));
}
private TableCache getCache(IrisData manager) {
return cache.aquire(() -> {
TableCache tc = new TableCache();
private TableCache<IrisLootTable> getCache(IrisData manager) {
return cache.aquire(() -> getCache(manager, manager.getLootLoader()::load));
}
private TableCache<IrisVanillaLootTable> getVanillaCache(IrisData manager) {
return vanillaCache.aquire(() -> getCache(manager, IrisObjectPlacement::getVanillaTable));
}
private <T> TableCache<T> getCache(IrisData manager, Function<String, T> loader) {
TableCache<T> tc = new TableCache<>();
for (IrisObjectLoot loot : getLoot()) {
if (loot == null)
continue;
IrisLootTable table = manager.getLootLoader().load(loot.getName());
T table = loader.apply(loot.getName());
if (table == null) {
Iris.warn("Couldn't find loot table " + loot.getName());
continue;
@@ -250,7 +267,14 @@ public class IrisObjectPlacement {
}
}
return tc;
});
}
@Nullable
private static IrisVanillaLootTable getVanillaTable(String name) {
NamespacedKey key = NamespacedKey.fromString(name);
if (key == null)
return null;
return new IrisVanillaLootTable(Bukkit.getLootTable(key));
}
/**
@@ -261,10 +285,16 @@ public class IrisObjectPlacement {
* @return The loot table it should use.
*/
public IrisLootTable getTable(BlockData data, IrisData dataManager) {
TableCache cache = getCache(dataManager);
IrisLootTable table = pickTable(data, getVanillaCache(dataManager));
if (table == null) {
table = pickTable(data, getCache(dataManager));
}
return table;
}
private <T> T pickTable(BlockData data, TableCache<T> cache) {
if (B.isStorageChest(data)) {
IrisLootTable picked = null;
T picked = null;
if (cache.exact.containsKey(data.getMaterial()) && cache.exact.get(data.getMaterial()).containsKey(data)) {
picked = cache.exact.get(data.getMaterial()).get(data).pullRandom();
} else if (cache.basic.containsKey(data.getMaterial())) {
@@ -279,9 +309,9 @@ public class IrisObjectPlacement {
return null;
}
private static class TableCache {
final transient WeightedRandom<IrisLootTable> global = new WeightedRandom<>();
final transient KMap<Material, WeightedRandom<IrisLootTable>> basic = new KMap<>();
final transient KMap<Material, KMap<BlockData, WeightedRandom<IrisLootTable>>> exact = new KMap<>();
private static class TableCache<T> {
final transient WeightedRandom<T> global = new WeightedRandom<>();
final transient KMap<Material, WeightedRandom<T>> basic = new KMap<>();
final transient KMap<Material, KMap<BlockData, WeightedRandom<T>>> exact = new KMap<>();
}
}

View File

@@ -0,0 +1,50 @@
package com.volmit.iris.engine.object;
import com.volmit.iris.core.loader.IrisData;
import com.volmit.iris.engine.data.cache.AtomicCache;
import com.volmit.iris.engine.object.annotations.*;
import com.volmit.iris.engine.object.annotations.functions.LootTableKeyFunction;
import com.volmit.iris.util.collection.KList;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
import org.bukkit.block.data.BlockData;
@Snippet("object-vanilla-loot")
@Accessors(chain = true)
@NoArgsConstructor
@AllArgsConstructor
@Desc("Represents vanilla loot within this object or jigsaw piece")
@Data
public class IrisObjectVanillaLoot {
private final transient AtomicCache<KList<BlockData>> filterCache = new AtomicCache<>();
@ArrayType(min = 1, type = IrisBlockData.class)
@Desc("The list of blocks this loot table should apply to")
private KList<IrisBlockData> filter = new KList<>();
@Desc("Exactly match the block data or not")
private boolean exact = false;
@RegistryListFunction(LootTableKeyFunction.class)
@Desc("The vanilla loot table key")
@Required
private String name;
@Desc("The weight of this loot table being chosen")
private int weight = 1;
public KList<BlockData> getFilter(IrisData rdata) {
return filterCache.aquire(() ->
{
KList<BlockData> b = new KList<>();
for (IrisBlockData i : filter) {
BlockData bx = i.getBlockData(rdata);
if (bx != null) {
b.add(bx);
}
}
return b;
});
}
}

View File

@@ -0,0 +1,84 @@
package com.volmit.iris.engine.object;
import com.volmit.iris.core.loader.IrisData;
import com.volmit.iris.util.collection.KList;
import com.volmit.iris.util.math.RNG;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.bukkit.Location;
import org.bukkit.World;
import org.bukkit.inventory.ItemStack;
import org.bukkit.loot.LootContext;
import org.bukkit.loot.LootTable;
import java.io.File;
@Data
@EqualsAndHashCode(callSuper = false)
public class IrisVanillaLootTable extends IrisLootTable {
private final LootTable lootTable;
public KList<ItemStack> getLoot(RNG rng, World world, int x, int y, int z) {
return new KList<>(lootTable.populateLoot(rng, new LootContext.Builder(new Location(world, x, y, z)).build()));
}
@Override
public String getName() {
throw new IllegalStateException("Vanilla loot tables should not be used in Iris");
}
@Override
public int getRarity() {
throw new IllegalStateException("Vanilla loot tables should not be used in Iris");
}
@Override
public int getMaxPicked() {
throw new IllegalStateException("Vanilla loot tables should not be used in Iris");
}
@Override
public int getMinPicked() {
throw new IllegalStateException("Vanilla loot tables should not be used in Iris");
}
@Override
public int getMaxTries() {
throw new IllegalStateException("Vanilla loot tables should not be used in Iris");
}
@Override
public KList<IrisLoot> getLoot() {
throw new IllegalStateException("Vanilla loot tables should not be used in Iris");
}
@Override
public KList<ItemStack> getLoot(boolean debug, RNG rng, InventorySlotType slot, World world, int x, int y, int z) {
return new KList<>(lootTable.populateLoot(rng, new LootContext.Builder(new Location(world, x, y, z)).build()));
}
@Override
public String getFolderName() {
throw new IllegalStateException("Vanilla loot tables should not be used in Iris");
}
@Override
public String getTypeName() {
throw new IllegalStateException("Vanilla loot tables should not be used in Iris");
}
@Override
public File getLoadFile() {
throw new IllegalStateException("Vanilla loot tables should not be used in Iris");
}
@Override
public IrisData getLoader() {
throw new IllegalStateException("Vanilla loot tables should not be used in Iris");
}
@Override
public KList<String> getPreprocessors() {
throw new IllegalStateException("Vanilla loot tables should not be used in Iris");
}
}

View File

@@ -30,4 +30,6 @@ public @interface ArrayType {
Class<?> type();
int min() default 0;
int max() default Integer.MAX_VALUE;
}

View File

@@ -0,0 +1,30 @@
package com.volmit.iris.engine.object.annotations.functions;
import com.volmit.iris.core.loader.IrisData;
import com.volmit.iris.engine.framework.ListFunction;
import com.volmit.iris.util.collection.KList;
import org.bukkit.NamespacedKey;
import org.bukkit.loot.LootTables;
import java.util.Arrays;
import java.util.stream.Collectors;
public class LootTableKeyFunction implements ListFunction<IrisData, KList<String>> {
@Override
public String key() {
return "loot-table-key";
}
@Override
public String fancyName() {
return "LootTable Key";
}
@Override
public KList<String> apply(IrisData data) {
return Arrays.stream(LootTables.values())
.map(LootTables::getKey)
.map(NamespacedKey::toString)
.collect(Collectors.toCollection(KList::new));
}
}

View File

@@ -1,4 +1,4 @@
package com.volmit.iris.engine.object.annotations;
package com.volmit.iris.engine.object.annotations.functions;
import com.volmit.iris.core.loader.IrisData;
import com.volmit.iris.core.nms.INMS;

View File

@@ -36,6 +36,7 @@ import com.volmit.iris.util.hunk.Hunk;
import com.volmit.iris.util.hunk.view.BiomeGridHunkHolder;
import com.volmit.iris.util.hunk.view.ChunkDataHunkHolder;
import com.volmit.iris.util.io.ReactiveFolder;
import com.volmit.iris.util.math.RollingSequence;
import com.volmit.iris.util.scheduling.ChronoLatch;
import com.volmit.iris.util.scheduling.J;
import com.volmit.iris.util.scheduling.Looper;
@@ -49,6 +50,7 @@ import org.bukkit.entity.Entity;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.world.ChunkLoadEvent;
import org.bukkit.event.world.WorldInitEvent;
import org.bukkit.generator.BiomeProvider;
import org.bukkit.generator.BlockPopulator;
@@ -87,6 +89,7 @@ public class BukkitChunkGenerator extends ChunkGenerator implements PlatformChun
private final AtomicInteger a = new AtomicInteger(0);
private final CompletableFuture<Integer> spawnChunks = new CompletableFuture<>();
private final boolean smartVanillaHeight;
private RollingSequence mergeDuration;
private Engine engine;
private Looper hotloader;
private StudioMode lastMode;
@@ -103,6 +106,7 @@ public class BukkitChunkGenerator extends ChunkGenerator implements PlatformChun
populators = new KList<>();
loadLock = new Semaphore(LOAD_LOCKS);
this.world = world;
this.mergeDuration = new RollingSequence(20);
this.hotloadChecker = new ChronoLatch(1000, false);
this.studio = studio;
this.dataLocation = dataLocation;
@@ -379,6 +383,13 @@ public class BukkitChunkGenerator extends ChunkGenerator implements PlatformChun
}
}
@EventHandler
private void onChunkGeneration(ChunkLoadEvent event) {
if (engine == null ||!event.isNewChunk() || !engine.getWorld().realWorld().equals(event.getWorld()) || !engine.getDimension().isEnableExperimentalMerger())
return;
engine.getMerger().generateVanillaUnderground(event.getChunk().getX(), event.getChunk().getZ(), event.getChunk(), engine);
}
@Override
public int getBaseHeight(@NotNull WorldInfo worldInfo, @NotNull Random random, int x, int z, @NotNull HeightMap heightMap) {
return 4;

View File

@@ -0,0 +1,36 @@
package com.volmit.iris.util.matter.slices;
import com.volmit.iris.util.data.palette.Palette;
import com.volmit.iris.util.matter.Sliced;
import com.volmit.iris.util.matter.slices.container.JigsawStructureContainer;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
@Sliced
public class JigsawStructureMatter extends RawMatter<JigsawStructureContainer> {
public JigsawStructureMatter() {
this(1, 1, 1);
}
public JigsawStructureMatter(int width, int height, int depth) {
super(width, height, depth, JigsawStructureContainer.class);
}
@Override
public Palette<JigsawStructureContainer> getGlobalPalette() {
return null;
}
@Override
public void writeNode(JigsawStructureContainer b, DataOutputStream dos) throws IOException {
dos.writeUTF(b.getLoadKey());
}
@Override
public JigsawStructureContainer readNode(DataInputStream din) throws IOException {
return new JigsawStructureContainer(din.readUTF());
}
}

View File

@@ -0,0 +1,14 @@
package com.volmit.iris.util.matter.slices.container;
import com.volmit.iris.engine.object.IrisJigsawStructure;
public class JigsawStructureContainer extends RegistrantContainer<IrisJigsawStructure> {
public JigsawStructureContainer(String loadKey) {
super(IrisJigsawStructure.class, loadKey);
}
public static JigsawStructureContainer toContainer(IrisJigsawStructure structure) {
return new JigsawStructureContainer(structure.getLoadKey());
}
}

View File

@@ -41,7 +41,6 @@ public class Chunk {
private int lastMCAUpdate;
private CompoundTag data;
private int dataVersion;
private int nativeIrisVersion;
private long lastUpdate;
private long inhabitedTime;
private MCABiomeContainer biomes;
@@ -150,8 +149,8 @@ public class Chunk {
if ((loadFlags & STRUCTURES) != 0) {
structures = level.getCompoundTag("Structures");
}
if ((loadFlags & (BLOCK_LIGHTS | BLOCK_STATES | SKY_LIGHT)) != 0 && level.containsKey("Sections")) {
for (CompoundTag section : level.getListTag("Sections").asCompoundTagList()) {
if ((loadFlags & (BLOCK_LIGHTS | BLOCK_STATES | SKY_LIGHT)) != 0 && level.containsKey("sections")) {
for (CompoundTag section : level.getListTag("sections").asCompoundTagList()) {
int sectionIndex = section.getByte("Y");
if (sectionIndex > 15 || sectionIndex < 0) {
continue;

View File

@@ -0,0 +1,42 @@
package com.volmit.iris.util.reflect;
import com.volmit.iris.core.nms.container.Pair;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
public class Reflect {
public static <T> T newInstance(Class<T> type, Object... initArgs) throws NoSuchMethodException, InvocationTargetException {
var list = Arrays.stream(initArgs)
.map(arg -> new Pair<Class<?>, Object>(arg.getClass(), arg))
.toList();
return newInstance(type, list);
}
public static <T> T newInstance(Class<T> type, List<Pair<Class<?>, Object>> initArgs) throws NoSuchMethodException, InvocationTargetException{
constructors:
for (var c : type.getDeclaredConstructors()) {
var types = c.getParameterTypes();
for (int i = 0; i < types.length; i++) {
if (!types[i].isAssignableFrom(initArgs.get(i).getA()))
continue constructors;
}
c.setAccessible(true);
try {
return (T) c.newInstance(initArgs.stream().map(Pair::getB).toArray());
} catch (InstantiationException | IllegalAccessException e) {
throw new InvocationTargetException(e);
}
}
var constructors = Arrays.stream(type.getDeclaredConstructors())
.map(Constructor::toGenericString)
.collect(Collectors.joining("\n"));
throw new NoSuchMethodException("No matching constructor found in:\n" + constructors);
}
}

View File

@@ -0,0 +1,386 @@
package com.volmit.iris.core.nms.v1_19_R1;
import com.google.common.collect.ImmutableList;
import com.mojang.serialization.Lifecycle;
import com.volmit.iris.Iris;
import com.volmit.iris.core.nms.IMemoryWorld;
import com.volmit.iris.util.reflect.Reflect;
import com.volmit.iris.util.scheduling.J;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Registry;
import net.minecraft.core.RegistryAccess;
import net.minecraft.resources.ResourceKey;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.dedicated.DedicatedServerProperties;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.util.GsonHelper;
import net.minecraft.world.Difficulty;
import net.minecraft.world.entity.ai.village.VillageSiege;
import net.minecraft.world.entity.npc.CatSpawner;
import net.minecraft.world.entity.npc.WanderingTraderSpawner;
import net.minecraft.world.level.*;
import net.minecraft.world.level.biome.BiomeManager;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.block.EntityBlock;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.chunk.ChunkAccess;
import net.minecraft.world.level.chunk.LevelChunk;
import net.minecraft.world.level.dimension.DimensionType;
import net.minecraft.world.level.dimension.LevelStem;
import net.minecraft.world.level.levelgen.PatrolSpawner;
import net.minecraft.world.level.levelgen.PhantomSpawner;
import net.minecraft.world.level.storage.LevelStorageSource;
import net.minecraft.world.level.storage.PrimaryLevelData;
import net.minecraft.world.level.storage.ServerLevelData;
import org.bukkit.*;
import org.bukkit.block.Biome;
import org.bukkit.block.data.BlockData;
import org.bukkit.craftbukkit.v1_19_R1.CraftChunk;
import org.bukkit.craftbukkit.v1_19_R1.CraftServer;
import org.bukkit.craftbukkit.v1_19_R1.block.CraftBlock;
import org.bukkit.craftbukkit.v1_19_R1.block.data.CraftBlockData;
import org.bukkit.craftbukkit.v1_19_R1.generator.CraftWorldInfo;
import org.bukkit.craftbukkit.v1_19_R1.util.CraftMagicNumbers;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.HandlerList;
import org.bukkit.event.world.WorldUnloadEvent;
import org.bukkit.generator.ChunkGenerator;
import org.bukkit.material.MaterialData;
import org.jetbrains.annotations.NotNull;
import java.io.IOException;
import java.lang.ref.WeakReference;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.nio.file.Files;
import java.util.Arrays;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;
public class MemoryWorld implements IMemoryWorld {
private static final AtomicLong C = new AtomicLong();
private static final Field WORLDS_FIELD;
private final AtomicReference<ServerLevel> level = new AtomicReference<>();
public MemoryWorld(NamespacedKey levelType, WorldCreator creator) throws IOException {
var name = "memory_world"+C.getAndIncrement();
while (Bukkit.getWorld(name) != null) {
name = "memory_world"+C.getAndIncrement();
}
var generator = creator.generator();
var biomeProvider = creator.biomeProvider();
var hardcore = creator.hardcore();
var server = getServer();
var tempDir = Files.createTempDirectory("MemoryGenerator");
LevelStorageSource source = LevelStorageSource.createDefault(tempDir);
ResourceKey<LevelStem> stemKey = ResourceKey.create(Registry.LEVEL_STEM_REGISTRY, new ResourceLocation(levelType.getNamespace(), levelType.getKey()));
var access = source.createAccess(name, stemKey);
var registry = server.registryAccess().registryOrThrow(Registry.LEVEL_STEM_REGISTRY);
var properties = new DedicatedServerProperties.WorldGenProperties(Objects.toString(creator.seed()), GsonHelper.parse(creator.generatorSettings().isEmpty() ? "{}" : creator.generatorSettings()), creator.generateStructures(), creator.type().name().toLowerCase(Locale.ROOT));
var settings = properties.create(server.registryAccess());
var worldSettings = new LevelSettings(name, GameType.byId(Bukkit.getDefaultGameMode().getValue()), hardcore, Difficulty.EASY, false, new GameRules(), server.datapackconfiguration);
var worldData = new PrimaryLevelData(worldSettings, settings, Lifecycle.stable());
long obfSeed = BiomeManager.obfuscateSeed(creator.seed());
var list = ImmutableList.of(new PhantomSpawner(), new PatrolSpawner(), new CatSpawner(), new VillageSiege(), new WanderingTraderSpawner(worldData));
var levelStem = registry.get(stemKey);
if (levelStem == null)
throw new IllegalStateException("Unknown dimension type: " + stemKey);
CraftWorldInfo worldInfo;
try {
worldInfo = new CraftWorldInfo(worldData, access, creator.environment(), levelStem.typeHolder().value());
} catch (Throwable e) {
try {
worldInfo = Reflect.newInstance(CraftWorldInfo.class, worldData, access, creator.environment(), levelStem.typeHolder().value(), levelStem.generator(), server.registryAccess());
} catch (NoSuchMethodException | InvocationTargetException ex) {
throw new IOException("Failed to create CraftWorldInfo", ex);
}
}
if (biomeProvider == null && generator != null) {
biomeProvider = generator.getDefaultBiomeProvider(worldInfo);
}
var levelKey = ResourceKey.create(Registry.DIMENSION_REGISTRY, new ResourceLocation(name));
var level = new ServerLevel(
server,
server.executor,
access,
worldData,
levelKey,
levelStem,
server.progressListenerFactory.create(0),
worldData.worldGenSettings().isDebug(),
obfSeed,
creator.environment() == World.Environment.NORMAL ? list : ImmutableList.of(),
true,
creator.environment(),
generator,
biomeProvider
);
level.keepSpawnInMemory = false;
Iris.instance.registerListener(this);
this.level.set(level);
}
public World getBukkit() {
var level = this.level.get();
if (level == null)
throw new IllegalStateException("World is not loaded");
return level.getWorld();
}
public Chunk getChunk(int x, int z) {
var level = this.level.get();
if (level == null)
throw new IllegalStateException("World is not loaded");
return new CraftChunk(level, x, z);
}
public ChunkGenerator.ChunkData getChunkData(int x, int z) {
var level = this.level.get();
if (level == null)
throw new IllegalStateException("World is not loaded");
return new MemoryChunkData(x, z);
}
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
public void onWorldUnload(WorldUnloadEvent event) {
var level = this.level.get();
if (level == null || event.getWorld() != level.getWorld())
return;
this.level.set(null);
}
public boolean isClosed() {
return this.level.get() == null;
}
@Override
public void close() throws Exception {
if (!Bukkit.isPrimaryThread()) {
var future = new CompletableFuture<Void>();
J.s(() -> {
try {
close();
future.complete(null);
} catch (Exception e) {
future.completeExceptionally(e);
}
});
future.join();
return;
}
var level = this.level.get();
if (level == null || !this.level.compareAndSet(level, null))
return;
level.getChunkSource().close(false);
level.entityManager.close(false);
level.convertable.deleteLevel();
level.convertable.close();
var map = (Map<String, World>) WORLDS_FIELD.get(Bukkit.getServer());
map.remove(level.dimension().location().getPath());
getServer().removeLevel(level);
HandlerList.unregisterAll(this);
}
private static MinecraftServer getServer() {
return ((CraftServer) Bukkit.getServer()).getServer();
}
static {
try {
WORLDS_FIELD = CraftServer.class.getDeclaredField("worlds");
WORLDS_FIELD.setAccessible(true);
} catch (NoSuchFieldException e) {
throw new RuntimeException(e);
}
}
private class MemoryChunkData implements ChunkGenerator.ChunkData {
private final int maxHeight;
private final int minHeight;
private final ChunkPos pos;
private WeakReference<LevelChunk> chunk;
private MemoryChunkData(int x, int z) {
var level = MemoryWorld.this.level.get();
if (level == null)
throw new IllegalStateException("World is not loaded");
var chunk = level.getChunk(x, z);
this.minHeight = chunk.getMinBuildHeight();
this.maxHeight = chunk.getMaxBuildHeight();
this.pos = new ChunkPos(x, z);
this.chunk = new WeakReference<>(chunk);
}
public LevelChunk getHandle() {
LevelChunk chunk = this.chunk.get();
if (chunk == null) {
var level = MemoryWorld.this.level.get();
if (level == null)
throw new IllegalStateException("World is not loaded");
chunk = level.getChunk(this.pos.x, this.pos.z);
this.chunk = new WeakReference<>(chunk);
}
return chunk;
}
@Override
public int getMinHeight() {
return minHeight;
}
@Override
public int getMaxHeight() {
return maxHeight;
}
@NotNull
@Override
public Biome getBiome(int x, int y, int z) {
return CraftBlock.biomeBaseToBiome(this.getHandle().biomeRegistry, this.getHandle().getNoiseBiome(x >> 2, y >> 2, z >> 2));
}
@Override
public void setBlock(int x, int y, int z, @NotNull Material material) {
setBlock(x, y, z, material.createBlockData());
}
@Override
public void setBlock(int x, int y, int z, @NotNull MaterialData material) {
setBlock(x, y, z, CraftMagicNumbers.getBlock(material));
}
@Override
public void setBlock(int x, int y, int z, @NotNull BlockData blockData) {
setBlock(x, y, z, ((CraftBlockData) blockData).getState());
}
@Override
public void setRegion(int xMin, int yMin, int zMin, int xMax, int yMax, int zMax, Material material) {
this.setRegion(xMin, yMin, zMin, xMax, yMax, zMax, material.createBlockData());
}
@Override
public void setRegion(int xMin, int yMin, int zMin, int xMax, int yMax, int zMax, MaterialData material) {
this.setRegion(xMin, yMin, zMin, xMax, yMax, zMax, CraftMagicNumbers.getBlock(material));
}
@Override
public void setRegion(int xMin, int yMin, int zMin, int xMax, int yMax, int zMax, BlockData blockData) {
this.setRegion(xMin, yMin, zMin, xMax, yMax, zMax, ((CraftBlockData)blockData).getState());
}
@NotNull
@Override
public Material getType(int x, int y, int z) {
return CraftMagicNumbers.getMaterial(this.getTypeId(x, y, z).getBlock());
}
@NotNull
@Override
public MaterialData getTypeAndData(int x, int y, int z) {
return CraftMagicNumbers.getMaterial(this.getTypeId(x, y, z));
}
@NotNull
@Override
public BlockData getBlockData(int x, int y, int z) {
return CraftBlockData.fromData(this.getTypeId(x, y, z));
}
@Deprecated
@Override
public byte getData(int x, int y, int z) {
return CraftMagicNumbers.toLegacyData(this.getTypeId(x, y, z));
}
private void setRegion(int xMin, int yMin, int zMin, int xMax, int yMax, int zMax, BlockState type) {
if (xMin <= 15 && yMin < this.maxHeight && zMin <= 15) {
if (xMin < 0) {
xMin = 0;
}
if (yMin < this.minHeight) {
yMin = this.minHeight;
}
if (zMin < 0) {
zMin = 0;
}
if (xMax > 16) {
xMax = 16;
}
if (yMax > this.maxHeight) {
yMax = this.maxHeight;
}
if (zMax > 16) {
zMax = 16;
}
if (xMin < xMax && yMin < yMax && zMin < zMax) {
for(int y = yMin; y < yMax; ++y) {
for(int x = xMin; x < xMax; ++x) {
for(int z = zMin; z < zMax; ++z) {
this.setBlock(x, y, z, type);
}
}
}
}
}
}
private BlockState getTypeId(int x, int y, int z) {
if (x == (x & 15) && y >= this.minHeight && y < this.maxHeight && z == (z & 15)) {
ChunkAccess access = this.getHandle();
return access.getBlockState(new BlockPos(access.getPos().getMinBlockX() + x, y, access.getPos().getMinBlockZ() + z));
} else {
return Blocks.AIR.defaultBlockState();
}
}
private void setBlock(int x, int y, int z, BlockState type) {
if (x == (x & 15) && y >= this.minHeight && y < this.maxHeight && z == (z & 15)) {
ChunkAccess access = this.getHandle();
BlockPos blockPosition = new BlockPos(access.getPos().getMinBlockX() + x, y, access.getPos().getMinBlockZ() + z);
BlockState oldBlockData = access.setBlockState(blockPosition, type, false);
if (type.hasBlockEntity()) {
BlockEntity tileEntity = ((EntityBlock)type.getBlock()).newBlockEntity(blockPosition, type);
if (tileEntity == null) {
access.removeBlockEntity(blockPosition);
} else {
access.setBlockEntity(tileEntity);
}
} else if (oldBlockData != null && oldBlockData.hasBlockEntity()) {
access.removeBlockEntity(blockPosition);
}
}
}
}
}

View File

@@ -1,10 +1,7 @@
package com.volmit.iris.core.nms.v1_19_R1;
import java.awt.Color;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.*;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
@@ -15,6 +12,7 @@ import java.util.Vector;
import java.util.concurrent.atomic.AtomicInteger;
import com.mojang.datafixers.util.Pair;
import com.volmit.iris.core.nms.IMemoryWorld;
import com.volmit.iris.core.nms.container.BiomeColor;
import com.volmit.iris.util.scheduling.J;
import net.minecraft.nbt.*;
@@ -607,6 +605,11 @@ public class NMSBinding implements INMSBinding {
return keys;
}
@Override
public IMemoryWorld createMemoryWorld(NamespacedKey levelType, WorldCreator creator) throws IOException {
return new MemoryWorld(levelType, creator);
}
private static Field getField(Class<?> clazz, Class<?> fieldType) throws NoSuchFieldException {
try {
for (Field f : clazz.getDeclaredFields()) {

View File

@@ -0,0 +1,379 @@
package com.volmit.iris.core.nms.v1_19_R2;
import com.google.common.collect.ImmutableList;
import com.mojang.serialization.Lifecycle;
import com.volmit.iris.Iris;
import com.volmit.iris.core.nms.IMemoryWorld;
import com.volmit.iris.util.reflect.Reflect;
import net.minecraft.core.BlockPos;
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.MinecraftServer;
import net.minecraft.server.dedicated.DedicatedServerProperties;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.util.GsonHelper;
import net.minecraft.world.Difficulty;
import net.minecraft.world.entity.ai.village.VillageSiege;
import net.minecraft.world.entity.npc.CatSpawner;
import net.minecraft.world.entity.npc.WanderingTraderSpawner;
import net.minecraft.world.level.ChunkPos;
import net.minecraft.world.level.GameRules;
import net.minecraft.world.level.GameType;
import net.minecraft.world.level.LevelSettings;
import net.minecraft.world.level.biome.BiomeManager;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.block.EntityBlock;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.chunk.ChunkAccess;
import net.minecraft.world.level.chunk.LevelChunk;
import net.minecraft.world.level.dimension.DimensionType;
import net.minecraft.world.level.dimension.LevelStem;
import net.minecraft.world.level.levelgen.PatrolSpawner;
import net.minecraft.world.level.levelgen.PhantomSpawner;
import net.minecraft.world.level.levelgen.WorldOptions;
import net.minecraft.world.level.storage.LevelStorageSource;
import net.minecraft.world.level.storage.PrimaryLevelData;
import net.minecraft.world.level.storage.ServerLevelData;
import org.bukkit.*;
import org.bukkit.block.Biome;
import org.bukkit.block.data.BlockData;
import org.bukkit.craftbukkit.v1_19_R2.CraftChunk;
import org.bukkit.craftbukkit.v1_19_R2.CraftServer;
import org.bukkit.craftbukkit.v1_19_R2.block.CraftBlock;
import org.bukkit.craftbukkit.v1_19_R2.block.data.CraftBlockData;
import org.bukkit.craftbukkit.v1_19_R2.generator.CraftWorldInfo;
import org.bukkit.craftbukkit.v1_19_R2.util.CraftMagicNumbers;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.HandlerList;
import org.bukkit.event.world.WorldUnloadEvent;
import org.bukkit.generator.ChunkGenerator;
import org.bukkit.material.MaterialData;
import org.jetbrains.annotations.NotNull;
import java.io.IOException;
import java.lang.ref.WeakReference;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.nio.file.Files;
import java.util.Arrays;
import java.util.Locale;
import java.util.Map;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;
public class MemoryWorld implements IMemoryWorld {
private static final AtomicLong C = new AtomicLong();
private static final Field WORLDS_FIELD;
private final AtomicReference<ServerLevel> level = new AtomicReference<>();
public MemoryWorld(NamespacedKey levelType, WorldCreator creator) throws IOException {
var name = "memory_world"+C.getAndIncrement();
while (Bukkit.getWorld(name) != null) {
name = "memory_world"+C.getAndIncrement();
}
var generator = creator.generator();
var biomeProvider = creator.biomeProvider();
var hardcore = creator.hardcore();
var server = getServer();
var tempDir = Files.createTempDirectory("MemoryGenerator");
LevelStorageSource source = LevelStorageSource.createDefault(tempDir);
ResourceKey<LevelStem> stemKey = ResourceKey.create(Registries.LEVEL_STEM, new ResourceLocation(levelType.getNamespace(), levelType.getKey()));
var access = source.createAccess(name, stemKey);
var worldLoader = server.worldLoader;
var registry = server.registryAccess().registryOrThrow(Registries.LEVEL_STEM);
var options = new WorldOptions(creator.seed(), creator.generateStructures(), false);
var properties = new DedicatedServerProperties.WorldDimensionData(GsonHelper.parse(creator.generatorSettings().isEmpty() ? "{}" : creator.generatorSettings()), creator.type().name().toLowerCase(Locale.ROOT));
var settings = new LevelSettings(name, GameType.byId(Bukkit.getDefaultGameMode().getValue()), hardcore, Difficulty.EASY, false, new GameRules(), worldLoader.dataConfiguration());
var dimension = properties.create(worldLoader.datapackWorldgen()).bake(registry);
Lifecycle lifecycle = dimension.lifecycle().add(worldLoader.datapackWorldgen().allRegistriesLifecycle());
var worldData = new PrimaryLevelData(settings, options, dimension.specialWorldProperty(), lifecycle);
worldData.customDimensions = registry;
long obfSeed = BiomeManager.obfuscateSeed(creator.seed());
var list = ImmutableList.of(new PhantomSpawner(), new PatrolSpawner(), new CatSpawner(), new VillageSiege(), new WanderingTraderSpawner(worldData));
var levelStem = registry.get(stemKey);
if (levelStem == null)
throw new IllegalStateException("Unknown dimension type: " + stemKey);
CraftWorldInfo worldInfo;
try {
worldInfo = new CraftWorldInfo(worldData, access, creator.environment(), levelStem.type().value());
} catch (Throwable e) {
try {
worldInfo = Reflect.newInstance(CraftWorldInfo.class, worldData, access, creator.environment(), levelStem.type().value(), levelStem.generator(), server.registryAccess());
} catch (NoSuchMethodException | InvocationTargetException ex) {
throw new IOException("Failed to create CraftWorldInfo", ex);
}
}
if (biomeProvider == null && generator != null) {
biomeProvider = generator.getDefaultBiomeProvider(worldInfo);
}
var levelKey = ResourceKey.create(Registries.DIMENSION, new ResourceLocation(name));
var level = new ServerLevel(
server,
server.executor,
access,
worldData,
levelKey,
levelStem,
server.progressListenerFactory.create(0),
worldData.isDebugWorld(),
obfSeed,
creator.environment() == World.Environment.NORMAL ? list : ImmutableList.of(),
true,
creator.environment(),
generator,
biomeProvider
);
level.keepSpawnInMemory = false;
Iris.instance.registerListener(this);
this.level.set(level);
}
public World getBukkit() {
var level = this.level.get();
if (level == null)
throw new IllegalStateException("World is not loaded");
return level.getWorld();
}
public Chunk getChunk(int x, int z) {
var level = this.level.get();
if (level == null)
throw new IllegalStateException("World is not loaded");
return new CraftChunk(level, x, z);
}
public ChunkGenerator.ChunkData getChunkData(int x, int z) {
var level = this.level.get();
if (level == null)
throw new IllegalStateException("World is not loaded");
return new MemoryChunkData(x, z);
}
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
public void onWorldUnload(WorldUnloadEvent event) {
var level = this.level.get();
if (level == null || event.getWorld() != level.getWorld())
return;
this.level.set(null);
}
public boolean isClosed() {
return this.level.get() == null;
}
@Override
public void close() throws Exception {
var level = this.level.get();
if (level == null || !this.level.compareAndSet(level, null))
return;
level.getChunkSource().close(false);
level.entityManager.close(false);
level.convertable.deleteLevel();
level.convertable.close();
var map = (Map<String, World>) WORLDS_FIELD.get(Bukkit.getServer());
map.remove(level.dimension().location().getPath());
getServer().removeLevel(level);
HandlerList.unregisterAll(this);
}
private static MinecraftServer getServer() {
return ((CraftServer) Bukkit.getServer()).getServer();
}
static {
try {
WORLDS_FIELD = CraftServer.class.getDeclaredField("worlds");
WORLDS_FIELD.setAccessible(true);
} catch (NoSuchFieldException e) {
throw new RuntimeException(e);
}
}
private class MemoryChunkData implements ChunkGenerator.ChunkData {
private final int maxHeight;
private final int minHeight;
private final ChunkPos pos;
private WeakReference<LevelChunk> chunk;
private MemoryChunkData(int x, int z) {
var level = MemoryWorld.this.level.get();
if (level == null)
throw new IllegalStateException("World is not loaded");
var chunk = level.getChunk(x, z);
this.minHeight = chunk.getMinBuildHeight();
this.maxHeight = chunk.getMaxBuildHeight();
this.pos = new ChunkPos(x, z);
this.chunk = new WeakReference<>(chunk);
}
public LevelChunk getHandle() {
LevelChunk chunk = this.chunk.get();
if (chunk == null) {
var level = MemoryWorld.this.level.get();
if (level == null)
throw new IllegalStateException("World is not loaded");
chunk = level.getChunk(this.pos.x, this.pos.z);
this.chunk = new WeakReference<>(chunk);
}
return chunk;
}
@Override
public int getMinHeight() {
return minHeight;
}
@Override
public int getMaxHeight() {
return maxHeight;
}
@NotNull
@Override
public Biome getBiome(int x, int y, int z) {
return CraftBlock.biomeBaseToBiome(this.getHandle().biomeRegistry, this.getHandle().getNoiseBiome(x >> 2, y >> 2, z >> 2));
}
@Override
public void setBlock(int x, int y, int z, @NotNull Material material) {
setBlock(x, y, z, material.createBlockData());
}
@Override
public void setBlock(int x, int y, int z, @NotNull MaterialData material) {
setBlock(x, y, z, CraftMagicNumbers.getBlock(material));
}
@Override
public void setBlock(int x, int y, int z, @NotNull BlockData blockData) {
setBlock(x, y, z, ((CraftBlockData) blockData).getState());
}
@Override
public void setRegion(int xMin, int yMin, int zMin, int xMax, int yMax, int zMax, Material material) {
this.setRegion(xMin, yMin, zMin, xMax, yMax, zMax, material.createBlockData());
}
@Override
public void setRegion(int xMin, int yMin, int zMin, int xMax, int yMax, int zMax, MaterialData material) {
this.setRegion(xMin, yMin, zMin, xMax, yMax, zMax, CraftMagicNumbers.getBlock(material));
}
@Override
public void setRegion(int xMin, int yMin, int zMin, int xMax, int yMax, int zMax, BlockData blockData) {
this.setRegion(xMin, yMin, zMin, xMax, yMax, zMax, ((CraftBlockData)blockData).getState());
}
@NotNull
@Override
public Material getType(int x, int y, int z) {
return CraftMagicNumbers.getMaterial(this.getTypeId(x, y, z).getBlock());
}
@NotNull
@Override
public MaterialData getTypeAndData(int x, int y, int z) {
return CraftMagicNumbers.getMaterial(this.getTypeId(x, y, z));
}
@NotNull
@Override
public BlockData getBlockData(int x, int y, int z) {
return CraftBlockData.fromData(this.getTypeId(x, y, z));
}
@Deprecated
@Override
public byte getData(int x, int y, int z) {
return CraftMagicNumbers.toLegacyData(this.getTypeId(x, y, z));
}
private void setRegion(int xMin, int yMin, int zMin, int xMax, int yMax, int zMax, BlockState type) {
if (xMin <= 15 && yMin < this.maxHeight && zMin <= 15) {
if (xMin < 0) {
xMin = 0;
}
if (yMin < this.minHeight) {
yMin = this.minHeight;
}
if (zMin < 0) {
zMin = 0;
}
if (xMax > 16) {
xMax = 16;
}
if (yMax > this.maxHeight) {
yMax = this.maxHeight;
}
if (zMax > 16) {
zMax = 16;
}
if (xMin < xMax && yMin < yMax && zMin < zMax) {
for(int y = yMin; y < yMax; ++y) {
for(int x = xMin; x < xMax; ++x) {
for(int z = zMin; z < zMax; ++z) {
this.setBlock(x, y, z, type);
}
}
}
}
}
}
private BlockState getTypeId(int x, int y, int z) {
if (x == (x & 15) && y >= this.minHeight && y < this.maxHeight && z == (z & 15)) {
ChunkAccess access = this.getHandle();
return access.getBlockState(new BlockPos(access.getPos().getMinBlockX() + x, y, access.getPos().getMinBlockZ() + z));
} else {
return Blocks.AIR.defaultBlockState();
}
}
private void setBlock(int x, int y, int z, BlockState type) {
if (x == (x & 15) && y >= this.minHeight && y < this.maxHeight && z == (z & 15)) {
ChunkAccess access = this.getHandle();
BlockPos blockPosition = new BlockPos(access.getPos().getMinBlockX() + x, y, access.getPos().getMinBlockZ() + z);
BlockState oldBlockData = access.setBlockState(blockPosition, type, false);
if (type.hasBlockEntity()) {
BlockEntity tileEntity = ((EntityBlock)type.getBlock()).newBlockEntity(blockPosition, type);
if (tileEntity == null) {
access.removeBlockEntity(blockPosition);
} else {
access.setBlockEntity(tileEntity);
}
} else if (oldBlockData != null && oldBlockData.hasBlockEntity()) {
access.removeBlockEntity(blockPosition);
}
}
}
}
}

View File

@@ -1,10 +1,7 @@
package com.volmit.iris.core.nms.v1_19_R2;
import java.awt.Color;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.*;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
@@ -15,6 +12,7 @@ import java.util.Vector;
import java.util.concurrent.atomic.AtomicInteger;
import com.mojang.datafixers.util.Pair;
import com.volmit.iris.core.nms.IMemoryWorld;
import com.volmit.iris.core.nms.container.BiomeColor;
import com.volmit.iris.util.scheduling.J;
import net.minecraft.nbt.*;
@@ -609,6 +607,11 @@ public class NMSBinding implements INMSBinding {
return keys;
}
@Override
public IMemoryWorld createMemoryWorld(NamespacedKey levelType, WorldCreator creator) throws IOException {
return new MemoryWorld(levelType, creator);
}
private static Field getField(Class<?> clazz, Class<?> fieldType) throws NoSuchFieldException {
try {
for (Field f : clazz.getDeclaredFields()) {

View File

@@ -0,0 +1,394 @@
package com.volmit.iris.core.nms.v1_19_R3;
import com.google.common.collect.ImmutableList;
import com.mojang.serialization.Lifecycle;
import com.volmit.iris.Iris;
import com.volmit.iris.core.nms.IMemoryWorld;
import com.volmit.iris.util.reflect.Reflect;
import com.volmit.iris.util.scheduling.J;
import net.minecraft.core.BlockPos;
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.MinecraftServer;
import net.minecraft.server.dedicated.DedicatedServerProperties;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.util.GsonHelper;
import net.minecraft.world.Difficulty;
import net.minecraft.world.entity.ai.village.VillageSiege;
import net.minecraft.world.entity.npc.CatSpawner;
import net.minecraft.world.entity.npc.WanderingTraderSpawner;
import net.minecraft.world.level.ChunkPos;
import net.minecraft.world.level.GameRules;
import net.minecraft.world.level.GameType;
import net.minecraft.world.level.LevelSettings;
import net.minecraft.world.level.biome.BiomeManager;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.block.EntityBlock;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.chunk.ChunkAccess;
import net.minecraft.world.level.chunk.LevelChunk;
import net.minecraft.world.level.dimension.DimensionType;
import net.minecraft.world.level.dimension.LevelStem;
import net.minecraft.world.level.levelgen.PatrolSpawner;
import net.minecraft.world.level.levelgen.PhantomSpawner;
import net.minecraft.world.level.levelgen.WorldOptions;
import net.minecraft.world.level.storage.LevelStorageSource;
import net.minecraft.world.level.storage.PrimaryLevelData;
import net.minecraft.world.level.storage.ServerLevelData;
import org.bukkit.*;
import org.bukkit.block.Biome;
import org.bukkit.block.data.BlockData;
import org.bukkit.craftbukkit.v1_19_R3.CraftChunk;
import org.bukkit.craftbukkit.v1_19_R3.CraftServer;
import org.bukkit.craftbukkit.v1_19_R3.block.CraftBlock;
import org.bukkit.craftbukkit.v1_19_R3.block.data.CraftBlockData;
import org.bukkit.craftbukkit.v1_19_R3.generator.CraftWorldInfo;
import org.bukkit.craftbukkit.v1_19_R3.util.CraftMagicNumbers;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.HandlerList;
import org.bukkit.event.world.WorldUnloadEvent;
import org.bukkit.generator.ChunkGenerator;
import org.bukkit.material.MaterialData;
import org.jetbrains.annotations.NotNull;
import java.io.IOException;
import java.lang.ref.WeakReference;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.nio.file.Files;
import java.util.Arrays;
import java.util.Locale;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;
public class MemoryWorld implements IMemoryWorld {
private static final AtomicLong C = new AtomicLong();
private static final Field WORLDS_FIELD;
private final AtomicReference<ServerLevel> level = new AtomicReference<>();
public MemoryWorld(NamespacedKey levelType, WorldCreator creator) throws IOException {
var name = "memory_world"+C.getAndIncrement();
while (Bukkit.getWorld(name) != null) {
name = "memory_world"+C.getAndIncrement();
}
var generator = creator.generator();
var biomeProvider = creator.biomeProvider();
var hardcore = creator.hardcore();
var server = getServer();
var tempDir = Files.createTempDirectory("MemoryGenerator");
LevelStorageSource source = LevelStorageSource.createDefault(tempDir);
ResourceKey<LevelStem> stemKey = ResourceKey.create(Registries.LEVEL_STEM, new ResourceLocation(levelType.getNamespace(), levelType.getKey()));
var access = source.createAccess(name, stemKey);
var worldLoader = server.worldLoader;
var registry = server.registryAccess().registryOrThrow(Registries.LEVEL_STEM);
var options = new WorldOptions(creator.seed(), creator.generateStructures(), false);
var properties = new DedicatedServerProperties.WorldDimensionData(GsonHelper.parse(creator.generatorSettings().isEmpty() ? "{}" : creator.generatorSettings()), creator.type().name().toLowerCase(Locale.ROOT));
var settings = new LevelSettings(name, GameType.byId(Bukkit.getDefaultGameMode().getValue()), hardcore, Difficulty.EASY, false, new GameRules(), worldLoader.dataConfiguration());
var dimension = properties.create(worldLoader.datapackWorldgen()).bake(registry);
Lifecycle lifecycle = dimension.lifecycle().add(worldLoader.datapackWorldgen().allRegistriesLifecycle());
var worldData = new PrimaryLevelData(settings, options, dimension.specialWorldProperty(), lifecycle);
worldData.customDimensions = registry;
long obfSeed = BiomeManager.obfuscateSeed(creator.seed());
var list = ImmutableList.of(new PhantomSpawner(), new PatrolSpawner(), new CatSpawner(), new VillageSiege(), new WanderingTraderSpawner(worldData));
var levelStem = registry.get(stemKey);
if (levelStem == null)
throw new IllegalStateException("Unknown dimension type: " + stemKey);
CraftWorldInfo worldInfo;
try {
worldInfo = new CraftWorldInfo(worldData, access, creator.environment(), levelStem.type().value());
} catch (Throwable e) {
try {
worldInfo = Reflect.newInstance(CraftWorldInfo.class, worldData, access, creator.environment(), levelStem.type().value(), levelStem.generator(), server.registryAccess());
} catch (NoSuchMethodException | InvocationTargetException ex) {
throw new IOException("Failed to create CraftWorldInfo", ex);
}
}
if (biomeProvider == null && generator != null) {
biomeProvider = generator.getDefaultBiomeProvider(worldInfo);
}
var levelKey = ResourceKey.create(Registries.DIMENSION, new ResourceLocation(name));
var level = new ServerLevel(
server,
server.executor,
access,
worldData,
levelKey,
levelStem,
server.progressListenerFactory.create(0),
worldData.isDebugWorld(),
obfSeed,
creator.environment() == World.Environment.NORMAL ? list : ImmutableList.of(),
true,
creator.environment(),
generator,
biomeProvider
);
level.keepSpawnInMemory = false;
Iris.instance.registerListener(this);
this.level.set(level);
}
public World getBukkit() {
var level = this.level.get();
if (level == null)
throw new IllegalStateException("World is not loaded");
return level.getWorld();
}
public Chunk getChunk(int x, int z) {
var level = this.level.get();
if (level == null)
throw new IllegalStateException("World is not loaded");
return new CraftChunk(level, x, z);
}
public ChunkGenerator.ChunkData getChunkData(int x, int z) {
var level = this.level.get();
if (level == null)
throw new IllegalStateException("World is not loaded");
return new MemoryChunkData(x, z);
}
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
public void onWorldUnload(WorldUnloadEvent event) {
var level = this.level.get();
if (level == null || event.getWorld() != level.getWorld())
return;
this.level.set(null);
}
public boolean isClosed() {
return this.level.get() == null;
}
@Override
public void close() throws Exception {
if (!Bukkit.isPrimaryThread()) {
var future = new CompletableFuture<Void>();
J.s(() -> {
try {
close();
future.complete(null);
} catch (Exception e) {
future.completeExceptionally(e);
}
});
future.join();
return;
}
var level = this.level.get();
if (level == null || !this.level.compareAndSet(level, null))
return;
level.close();
level.convertable.deleteLevel();
level.convertable.close();
var map = (Map<String, World>) WORLDS_FIELD.get(Bukkit.getServer());
map.remove(level.dimension().location().getPath());
getServer().removeLevel(level);
HandlerList.unregisterAll(this);
}
private static MinecraftServer getServer() {
return ((CraftServer) Bukkit.getServer()).getServer();
}
static {
try {
WORLDS_FIELD = CraftServer.class.getDeclaredField("worlds");
WORLDS_FIELD.setAccessible(true);
} catch (NoSuchFieldException e) {
throw new RuntimeException(e);
}
}
private class MemoryChunkData implements ChunkGenerator.ChunkData {
private final int maxHeight;
private final int minHeight;
private final ChunkPos pos;
private WeakReference<LevelChunk> chunk;
private MemoryChunkData(int x, int z) {
var level = MemoryWorld.this.level.get();
if (level == null)
throw new IllegalStateException("World is not loaded");
var chunk = level.getChunk(x, z);
this.minHeight = chunk.getMinBuildHeight();
this.maxHeight = chunk.getMaxBuildHeight();
this.pos = new ChunkPos(x, z);
this.chunk = new WeakReference<>(chunk);
}
public LevelChunk getHandle() {
LevelChunk chunk = this.chunk.get();
if (chunk == null) {
var level = MemoryWorld.this.level.get();
if (level == null)
throw new IllegalStateException("World is not loaded");
chunk = level.getChunk(this.pos.x, this.pos.z);
this.chunk = new WeakReference<>(chunk);
}
return chunk;
}
@Override
public int getMinHeight() {
return minHeight;
}
@Override
public int getMaxHeight() {
return maxHeight;
}
@NotNull
@Override
public Biome getBiome(int x, int y, int z) {
return CraftBlock.biomeBaseToBiome(this.getHandle().biomeRegistry, this.getHandle().getNoiseBiome(x >> 2, y >> 2, z >> 2));
}
@Override
public void setBlock(int x, int y, int z, @NotNull Material material) {
setBlock(x, y, z, material.createBlockData());
}
@Override
public void setBlock(int x, int y, int z, @NotNull MaterialData material) {
setBlock(x, y, z, CraftMagicNumbers.getBlock(material));
}
@Override
public void setBlock(int x, int y, int z, @NotNull BlockData blockData) {
setBlock(x, y, z, ((CraftBlockData) blockData).getState());
}
@Override
public void setRegion(int xMin, int yMin, int zMin, int xMax, int yMax, int zMax, Material material) {
this.setRegion(xMin, yMin, zMin, xMax, yMax, zMax, material.createBlockData());
}
@Override
public void setRegion(int xMin, int yMin, int zMin, int xMax, int yMax, int zMax, MaterialData material) {
this.setRegion(xMin, yMin, zMin, xMax, yMax, zMax, CraftMagicNumbers.getBlock(material));
}
@Override
public void setRegion(int xMin, int yMin, int zMin, int xMax, int yMax, int zMax, BlockData blockData) {
this.setRegion(xMin, yMin, zMin, xMax, yMax, zMax, ((CraftBlockData)blockData).getState());
}
@NotNull
@Override
public Material getType(int x, int y, int z) {
return CraftMagicNumbers.getMaterial(this.getTypeId(x, y, z).getBlock());
}
@NotNull
@Override
public MaterialData getTypeAndData(int x, int y, int z) {
return CraftMagicNumbers.getMaterial(this.getTypeId(x, y, z));
}
@NotNull
@Override
public BlockData getBlockData(int x, int y, int z) {
return CraftBlockData.fromData(this.getTypeId(x, y, z));
}
@Deprecated
@Override
public byte getData(int x, int y, int z) {
return CraftMagicNumbers.toLegacyData(this.getTypeId(x, y, z));
}
private void setRegion(int xMin, int yMin, int zMin, int xMax, int yMax, int zMax, BlockState type) {
if (xMin <= 15 && yMin < this.maxHeight && zMin <= 15) {
if (xMin < 0) {
xMin = 0;
}
if (yMin < this.minHeight) {
yMin = this.minHeight;
}
if (zMin < 0) {
zMin = 0;
}
if (xMax > 16) {
xMax = 16;
}
if (yMax > this.maxHeight) {
yMax = this.maxHeight;
}
if (zMax > 16) {
zMax = 16;
}
if (xMin < xMax && yMin < yMax && zMin < zMax) {
for(int y = yMin; y < yMax; ++y) {
for(int x = xMin; x < xMax; ++x) {
for(int z = zMin; z < zMax; ++z) {
this.setBlock(x, y, z, type);
}
}
}
}
}
}
private BlockState getTypeId(int x, int y, int z) {
if (x == (x & 15) && y >= this.minHeight && y < this.maxHeight && z == (z & 15)) {
ChunkAccess access = this.getHandle();
return access.getBlockState(new BlockPos(access.getPos().getMinBlockX() + x, y, access.getPos().getMinBlockZ() + z));
} else {
return Blocks.AIR.defaultBlockState();
}
}
private void setBlock(int x, int y, int z, BlockState type) {
if (x == (x & 15) && y >= this.minHeight && y < this.maxHeight && z == (z & 15)) {
ChunkAccess access = this.getHandle();
BlockPos blockPosition = new BlockPos(access.getPos().getMinBlockX() + x, y, access.getPos().getMinBlockZ() + z);
BlockState oldBlockData = access.setBlockState(blockPosition, type, false);
if (type.hasBlockEntity()) {
BlockEntity tileEntity = ((EntityBlock)type.getBlock()).newBlockEntity(blockPosition, type);
if (tileEntity == null) {
access.removeBlockEntity(blockPosition);
} else {
access.setBlockEntity(tileEntity);
}
} else if (oldBlockData != null && oldBlockData.hasBlockEntity()) {
access.removeBlockEntity(blockPosition);
}
}
}
}
}

View File

@@ -1,10 +1,7 @@
package com.volmit.iris.core.nms.v1_19_R3;
import java.awt.Color;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.*;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
@@ -15,6 +12,7 @@ import java.util.Vector;
import java.util.concurrent.atomic.AtomicInteger;
import com.mojang.datafixers.util.Pair;
import com.volmit.iris.core.nms.IMemoryWorld;
import com.volmit.iris.core.nms.container.BiomeColor;
import com.volmit.iris.util.scheduling.J;
import net.minecraft.nbt.*;
@@ -619,6 +617,11 @@ public class NMSBinding implements INMSBinding {
return keys;
}
@Override
public IMemoryWorld createMemoryWorld(NamespacedKey levelType, WorldCreator creator) throws IOException {
return new MemoryWorld(levelType, creator);
}
private static Field getField(Class<?> clazz, Class<?> fieldType) throws NoSuchFieldException {
try {
for (Field f : clazz.getDeclaredFields()) {

View File

@@ -0,0 +1,396 @@
package com.volmit.iris.core.nms.v1_20_R1;
import com.google.common.collect.ImmutableList;
import com.mojang.serialization.Lifecycle;
import com.volmit.iris.Iris;
import com.volmit.iris.core.nms.IMemoryWorld;
import com.volmit.iris.util.reflect.Reflect;
import com.volmit.iris.util.scheduling.J;
import net.minecraft.core.BlockPos;
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.MinecraftServer;
import net.minecraft.server.dedicated.DedicatedServerProperties;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.util.GsonHelper;
import net.minecraft.world.Difficulty;
import net.minecraft.world.entity.ai.village.VillageSiege;
import net.minecraft.world.entity.npc.CatSpawner;
import net.minecraft.world.entity.npc.WanderingTraderSpawner;
import net.minecraft.world.level.ChunkPos;
import net.minecraft.world.level.GameRules;
import net.minecraft.world.level.GameType;
import net.minecraft.world.level.LevelSettings;
import net.minecraft.world.level.biome.BiomeManager;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.block.EntityBlock;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.chunk.ChunkAccess;
import net.minecraft.world.level.chunk.LevelChunk;
import net.minecraft.world.level.dimension.DimensionType;
import net.minecraft.world.level.dimension.LevelStem;
import net.minecraft.world.level.levelgen.PatrolSpawner;
import net.minecraft.world.level.levelgen.PhantomSpawner;
import net.minecraft.world.level.levelgen.WorldOptions;
import net.minecraft.world.level.storage.LevelStorageSource;
import net.minecraft.world.level.storage.PrimaryLevelData;
import net.minecraft.world.level.storage.ServerLevelData;
import org.bukkit.*;
import org.bukkit.block.Biome;
import org.bukkit.block.data.BlockData;
import org.bukkit.craftbukkit.v1_20_R1.CraftChunk;
import org.bukkit.craftbukkit.v1_20_R1.CraftServer;
import org.bukkit.craftbukkit.v1_20_R1.attribute.CraftAttributeInstance;
import org.bukkit.craftbukkit.v1_20_R1.block.CraftBlock;
import org.bukkit.craftbukkit.v1_20_R1.block.data.CraftBlockData;
import org.bukkit.craftbukkit.v1_20_R1.generator.CraftWorldInfo;
import org.bukkit.craftbukkit.v1_20_R1.util.CraftMagicNumbers;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.HandlerList;
import org.bukkit.event.world.WorldUnloadEvent;
import org.bukkit.generator.ChunkGenerator;
import org.bukkit.material.MaterialData;
import org.jetbrains.annotations.NotNull;
import java.io.IOException;
import java.lang.ref.WeakReference;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.nio.file.Files;
import java.util.Arrays;
import java.util.Locale;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;
public class MemoryWorld implements IMemoryWorld {
private static final AtomicLong C = new AtomicLong();
private static final Field WORLDS_FIELD;
private final AtomicReference<ServerLevel> level = new AtomicReference<>();
public MemoryWorld(NamespacedKey levelType, WorldCreator creator) throws IOException {
var name = "memory_world"+C.getAndIncrement();
while (Bukkit.getWorld(name) != null) {
name = "memory_world"+C.getAndIncrement();
}
var generator = creator.generator();
var biomeProvider = creator.biomeProvider();
var hardcore = creator.hardcore();
var server = getServer();
var tempDir = Files.createTempDirectory("MemoryGenerator");
LevelStorageSource source = LevelStorageSource.createDefault(tempDir);
ResourceKey<LevelStem> stemKey = ResourceKey.create(Registries.LEVEL_STEM, new ResourceLocation(levelType.getNamespace(), levelType.getKey()));
var access = source.createAccess(name, stemKey);
var worldLoader = server.worldLoader;
var registry = server.registryAccess().registryOrThrow(Registries.LEVEL_STEM);
var options = new WorldOptions(creator.seed(), creator.generateStructures(), false);
var properties = new DedicatedServerProperties.WorldDimensionData(GsonHelper.parse(creator.generatorSettings().isEmpty() ? "{}" : creator.generatorSettings()), creator.type().name().toLowerCase(Locale.ROOT));
var settings = new LevelSettings(name, GameType.byId(Bukkit.getDefaultGameMode().getValue()), hardcore, Difficulty.EASY, false, new GameRules(), worldLoader.dataConfiguration());
var dimension = properties.create(worldLoader.datapackWorldgen()).bake(registry);
Lifecycle lifecycle = dimension.lifecycle().add(worldLoader.datapackWorldgen().allRegistriesLifecycle());
var worldData = new PrimaryLevelData(settings, options, dimension.specialWorldProperty(), lifecycle);
worldData.customDimensions = registry;
long obfSeed = BiomeManager.obfuscateSeed(creator.seed());
var list = ImmutableList.of(new PhantomSpawner(), new PatrolSpawner(), new CatSpawner(), new VillageSiege(), new WanderingTraderSpawner(worldData));
var levelStem = registry.get(stemKey);
if (levelStem == null)
throw new IllegalStateException("Unknown dimension type: " + stemKey);
CraftWorldInfo worldInfo;
try {
worldInfo = new CraftWorldInfo(worldData, access, creator.environment(), levelStem.type().value());
} catch (Throwable e) {
try {
worldInfo = Reflect.newInstance(CraftWorldInfo.class, worldData, access, creator.environment(), levelStem.type().value(), levelStem.generator(), server.registryAccess());
} catch (NoSuchMethodException | InvocationTargetException ex) {
throw new IOException("Failed to create CraftWorldInfo", ex);
}
}
if (biomeProvider == null && generator != null) {
biomeProvider = generator.getDefaultBiomeProvider(worldInfo);
}
var levelKey = ResourceKey.create(Registries.DIMENSION, new ResourceLocation(name));
var level = new ServerLevel(
server,
server.executor,
access,
worldData,
levelKey,
levelStem,
server.progressListenerFactory.create(0),
worldData.isDebugWorld(),
obfSeed,
creator.environment() == World.Environment.NORMAL ? list : ImmutableList.of(),
true,
server.overworld().getRandomSequences(),
creator.environment(),
generator,
biomeProvider
);
level.keepSpawnInMemory = false;
Iris.instance.registerListener(this);
this.level.set(level);
}
public World getBukkit() {
var level = this.level.get();
if (level == null)
throw new IllegalStateException("World is not loaded");
return level.getWorld();
}
public Chunk getChunk(int x, int z) {
var level = this.level.get();
if (level == null)
throw new IllegalStateException("World is not loaded");
return new CraftChunk(level, x, z);
}
public ChunkGenerator.ChunkData getChunkData(int x, int z) {
var level = this.level.get();
if (level == null)
throw new IllegalStateException("World is not loaded");
return new MemoryChunkData(x, z);
}
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
public void onWorldUnload(WorldUnloadEvent event) {
var level = this.level.get();
if (level == null || event.getWorld() != level.getWorld())
return;
this.level.set(null);
}
public boolean isClosed() {
return this.level.get() == null;
}
@Override
public void close() throws Exception {
if (!Bukkit.isPrimaryThread()) {
var future = new CompletableFuture<Void>();
J.s(() -> {
try {
close();
future.complete(null);
} catch (Exception e) {
future.completeExceptionally(e);
}
});
future.join();
return;
}
var level = this.level.get();
if (level == null || !this.level.compareAndSet(level, null))
return;
level.close();
level.convertable.deleteLevel();
level.convertable.close();
var map = (Map<String, World>) WORLDS_FIELD.get(Bukkit.getServer());
map.remove(level.dimension().location().getPath());
getServer().removeLevel(level);
HandlerList.unregisterAll(this);
}
private static MinecraftServer getServer() {
return ((CraftServer) Bukkit.getServer()).getServer();
}
static {
try {
WORLDS_FIELD = CraftServer.class.getDeclaredField("worlds");
WORLDS_FIELD.setAccessible(true);
} catch (NoSuchFieldException e) {
throw new RuntimeException(e);
}
}
private class MemoryChunkData implements ChunkGenerator.ChunkData {
private final int maxHeight;
private final int minHeight;
private final ChunkPos pos;
private WeakReference<LevelChunk> chunk;
private MemoryChunkData(int x, int z) {
var level = MemoryWorld.this.level.get();
if (level == null)
throw new IllegalStateException("World is not loaded");
var chunk = level.getChunk(x, z);
this.minHeight = chunk.getMinBuildHeight();
this.maxHeight = chunk.getMaxBuildHeight();
this.pos = new ChunkPos(x, z);
this.chunk = new WeakReference<>(chunk);
}
public LevelChunk getHandle() {
LevelChunk chunk = this.chunk.get();
if (chunk == null) {
var level = MemoryWorld.this.level.get();
if (level == null)
throw new IllegalStateException("World is not loaded");
chunk = level.getChunk(this.pos.x, this.pos.z);
this.chunk = new WeakReference<>(chunk);
}
return chunk;
}
@Override
public int getMinHeight() {
return minHeight;
}
@Override
public int getMaxHeight() {
return maxHeight;
}
@NotNull
@Override
public Biome getBiome(int x, int y, int z) {
return CraftBlock.biomeBaseToBiome(this.getHandle().biomeRegistry, this.getHandle().getNoiseBiome(x >> 2, y >> 2, z >> 2));
}
@Override
public void setBlock(int x, int y, int z, @NotNull Material material) {
setBlock(x, y, z, material.createBlockData());
}
@Override
public void setBlock(int x, int y, int z, @NotNull MaterialData material) {
setBlock(x, y, z, CraftMagicNumbers.getBlock(material));
}
@Override
public void setBlock(int x, int y, int z, @NotNull BlockData blockData) {
setBlock(x, y, z, ((CraftBlockData) blockData).getState());
}
@Override
public void setRegion(int xMin, int yMin, int zMin, int xMax, int yMax, int zMax, Material material) {
this.setRegion(xMin, yMin, zMin, xMax, yMax, zMax, material.createBlockData());
}
@Override
public void setRegion(int xMin, int yMin, int zMin, int xMax, int yMax, int zMax, MaterialData material) {
this.setRegion(xMin, yMin, zMin, xMax, yMax, zMax, CraftMagicNumbers.getBlock(material));
}
@Override
public void setRegion(int xMin, int yMin, int zMin, int xMax, int yMax, int zMax, BlockData blockData) {
this.setRegion(xMin, yMin, zMin, xMax, yMax, zMax, ((CraftBlockData)blockData).getState());
}
@NotNull
@Override
public Material getType(int x, int y, int z) {
return CraftMagicNumbers.getMaterial(this.getTypeId(x, y, z).getBlock());
}
@NotNull
@Override
public MaterialData getTypeAndData(int x, int y, int z) {
return CraftMagicNumbers.getMaterial(this.getTypeId(x, y, z));
}
@NotNull
@Override
public BlockData getBlockData(int x, int y, int z) {
return CraftBlockData.fromData(this.getTypeId(x, y, z));
}
@Deprecated
@Override
public byte getData(int x, int y, int z) {
return CraftMagicNumbers.toLegacyData(this.getTypeId(x, y, z));
}
private void setRegion(int xMin, int yMin, int zMin, int xMax, int yMax, int zMax, BlockState type) {
if (xMin <= 15 && yMin < this.maxHeight && zMin <= 15) {
if (xMin < 0) {
xMin = 0;
}
if (yMin < this.minHeight) {
yMin = this.minHeight;
}
if (zMin < 0) {
zMin = 0;
}
if (xMax > 16) {
xMax = 16;
}
if (yMax > this.maxHeight) {
yMax = this.maxHeight;
}
if (zMax > 16) {
zMax = 16;
}
if (xMin < xMax && yMin < yMax && zMin < zMax) {
for(int y = yMin; y < yMax; ++y) {
for(int x = xMin; x < xMax; ++x) {
for(int z = zMin; z < zMax; ++z) {
this.setBlock(x, y, z, type);
}
}
}
}
}
}
private BlockState getTypeId(int x, int y, int z) {
if (x == (x & 15) && y >= this.minHeight && y < this.maxHeight && z == (z & 15)) {
ChunkAccess access = this.getHandle();
return access.getBlockState(new BlockPos(access.getPos().getMinBlockX() + x, y, access.getPos().getMinBlockZ() + z));
} else {
return Blocks.AIR.defaultBlockState();
}
}
private void setBlock(int x, int y, int z, BlockState type) {
if (x == (x & 15) && y >= this.minHeight && y < this.maxHeight && z == (z & 15)) {
ChunkAccess access = this.getHandle();
BlockPos blockPosition = new BlockPos(access.getPos().getMinBlockX() + x, y, access.getPos().getMinBlockZ() + z);
BlockState oldBlockData = access.setBlockState(blockPosition, type, false);
if (type.hasBlockEntity()) {
BlockEntity tileEntity = ((EntityBlock)type.getBlock()).newBlockEntity(blockPosition, type);
if (tileEntity == null) {
access.removeBlockEntity(blockPosition);
} else {
access.setBlockEntity(tileEntity);
}
} else if (oldBlockData != null && oldBlockData.hasBlockEntity()) {
access.removeBlockEntity(blockPosition);
}
}
}
}
}

View File

@@ -3,6 +3,7 @@ package com.volmit.iris.core.nms.v1_20_R1;
import com.mojang.brigadier.exceptions.CommandSyntaxException;
import com.mojang.datafixers.util.Pair;
import com.volmit.iris.Iris;
import com.volmit.iris.core.nms.IMemoryWorld;
import com.volmit.iris.core.nms.INMSBinding;
import com.volmit.iris.core.nms.container.BiomeColor;
import com.volmit.iris.engine.data.cache.AtomicCache;
@@ -66,10 +67,7 @@ import org.jetbrains.annotations.NotNull;
import sun.misc.Unsafe;
import java.awt.Color;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.*;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
@@ -612,6 +610,11 @@ public class NMSBinding implements INMSBinding {
return keys;
}
@Override
public IMemoryWorld createMemoryWorld(NamespacedKey levelType, WorldCreator creator) throws IOException {
return new MemoryWorld(levelType, creator);
}
private static Field getField(Class<?> clazz, Class<?> fieldType) throws NoSuchFieldException {
try {
for (Field f : clazz.getDeclaredFields()) {

View File

@@ -0,0 +1,395 @@
package com.volmit.iris.core.nms.v1_20_R2;
import com.google.common.collect.ImmutableList;
import com.mojang.serialization.Lifecycle;
import com.volmit.iris.Iris;
import com.volmit.iris.core.nms.IMemoryWorld;
import com.volmit.iris.util.reflect.Reflect;
import com.volmit.iris.util.scheduling.J;
import net.minecraft.core.BlockPos;
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.MinecraftServer;
import net.minecraft.server.dedicated.DedicatedServerProperties;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.util.GsonHelper;
import net.minecraft.world.Difficulty;
import net.minecraft.world.entity.ai.village.VillageSiege;
import net.minecraft.world.entity.npc.CatSpawner;
import net.minecraft.world.entity.npc.WanderingTraderSpawner;
import net.minecraft.world.level.ChunkPos;
import net.minecraft.world.level.GameRules;
import net.minecraft.world.level.GameType;
import net.minecraft.world.level.LevelSettings;
import net.minecraft.world.level.biome.BiomeManager;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.block.EntityBlock;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.chunk.ChunkAccess;
import net.minecraft.world.level.chunk.LevelChunk;
import net.minecraft.world.level.dimension.DimensionType;
import net.minecraft.world.level.dimension.LevelStem;
import net.minecraft.world.level.levelgen.PatrolSpawner;
import net.minecraft.world.level.levelgen.PhantomSpawner;
import net.minecraft.world.level.levelgen.WorldOptions;
import net.minecraft.world.level.storage.LevelStorageSource;
import net.minecraft.world.level.storage.PrimaryLevelData;
import net.minecraft.world.level.storage.ServerLevelData;
import org.bukkit.*;
import org.bukkit.block.Biome;
import org.bukkit.block.data.BlockData;
import org.bukkit.craftbukkit.v1_20_R2.CraftChunk;
import org.bukkit.craftbukkit.v1_20_R2.CraftServer;
import org.bukkit.craftbukkit.v1_20_R2.block.CraftBiome;
import org.bukkit.craftbukkit.v1_20_R2.block.data.CraftBlockData;
import org.bukkit.craftbukkit.v1_20_R2.generator.CraftWorldInfo;
import org.bukkit.craftbukkit.v1_20_R2.util.CraftMagicNumbers;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.HandlerList;
import org.bukkit.event.world.WorldUnloadEvent;
import org.bukkit.generator.ChunkGenerator;
import org.bukkit.material.MaterialData;
import org.jetbrains.annotations.NotNull;
import java.io.IOException;
import java.lang.ref.WeakReference;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.nio.file.Files;
import java.util.Arrays;
import java.util.Locale;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;
public class MemoryWorld implements IMemoryWorld {
private static final AtomicLong C = new AtomicLong();
private static final Field WORLDS_FIELD;
private final AtomicReference<ServerLevel> level = new AtomicReference<>();
public MemoryWorld(NamespacedKey levelType, WorldCreator creator) throws IOException {
var name = "memory_world"+C.getAndIncrement();
while (Bukkit.getWorld(name) != null) {
name = "memory_world"+C.getAndIncrement();
}
var generator = creator.generator();
var biomeProvider = creator.biomeProvider();
var hardcore = creator.hardcore();
var server = getServer();
var tempDir = Files.createTempDirectory("MemoryGenerator");
LevelStorageSource source = LevelStorageSource.createDefault(tempDir);
ResourceKey<LevelStem> stemKey = ResourceKey.create(Registries.LEVEL_STEM, new ResourceLocation(levelType.getNamespace(), levelType.getKey()));
var access = source.createAccess(name, stemKey);
var worldLoader = server.worldLoader;
var registry = server.registryAccess().registryOrThrow(Registries.LEVEL_STEM);
var options = new WorldOptions(creator.seed(), creator.generateStructures(), false);
var properties = new DedicatedServerProperties.WorldDimensionData(GsonHelper.parse(creator.generatorSettings().isEmpty() ? "{}" : creator.generatorSettings()), creator.type().name().toLowerCase(Locale.ROOT));
var settings = new LevelSettings(name, GameType.byId(Bukkit.getDefaultGameMode().getValue()), hardcore, Difficulty.EASY, false, new GameRules(), worldLoader.dataConfiguration());
var dimension = properties.create(worldLoader.datapackWorldgen()).bake(registry);
Lifecycle lifecycle = dimension.lifecycle().add(worldLoader.datapackWorldgen().allRegistriesLifecycle());
var worldData = new PrimaryLevelData(settings, options, dimension.specialWorldProperty(), lifecycle);
worldData.customDimensions = registry;
long obfSeed = BiomeManager.obfuscateSeed(creator.seed());
var list = ImmutableList.of(new PhantomSpawner(), new PatrolSpawner(), new CatSpawner(), new VillageSiege(), new WanderingTraderSpawner(worldData));
var levelStem = registry.get(stemKey);
if (levelStem == null)
throw new IllegalStateException("Unknown dimension type: " + stemKey);
CraftWorldInfo worldInfo;
try {
worldInfo = new CraftWorldInfo(worldData, access, creator.environment(), levelStem.type().value());
} catch (Throwable e) {
try {
worldInfo = Reflect.newInstance(CraftWorldInfo.class, worldData, access, creator.environment(), levelStem.type().value(), levelStem.generator(), server.registryAccess());
} catch (NoSuchMethodException | InvocationTargetException ex) {
throw new IOException("Failed to create CraftWorldInfo", ex);
}
}
if (biomeProvider == null && generator != null) {
biomeProvider = generator.getDefaultBiomeProvider(worldInfo);
}
var levelKey = ResourceKey.create(Registries.DIMENSION, new ResourceLocation(name));
var level = new ServerLevel(
server,
server.executor,
access,
worldData,
levelKey,
levelStem,
server.progressListenerFactory.create(0),
worldData.isDebugWorld(),
obfSeed,
creator.environment() == World.Environment.NORMAL ? list : ImmutableList.of(),
true,
server.overworld().getRandomSequences(),
creator.environment(),
generator,
biomeProvider
);
level.keepSpawnInMemory = false;
Iris.instance.registerListener(this);
this.level.set(level);
}
public World getBukkit() {
var level = this.level.get();
if (level == null)
throw new IllegalStateException("World is not loaded");
return level.getWorld();
}
public Chunk getChunk(int x, int z) {
var level = this.level.get();
if (level == null)
throw new IllegalStateException("World is not loaded");
return new CraftChunk(level, x, z);
}
public ChunkGenerator.ChunkData getChunkData(int x, int z) {
var level = this.level.get();
if (level == null)
throw new IllegalStateException("World is not loaded");
return new MemoryChunkData(x, z);
}
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
public void onWorldUnload(WorldUnloadEvent event) {
var level = this.level.get();
if (level == null || event.getWorld() != level.getWorld())
return;
this.level.set(null);
}
public boolean isClosed() {
return this.level.get() == null;
}
@Override
public void close() throws Exception {
if (!Bukkit.isPrimaryThread()) {
var future = new CompletableFuture<Void>();
J.s(() -> {
try {
close();
future.complete(null);
} catch (Exception e) {
future.completeExceptionally(e);
}
});
future.join();
return;
}
var level = this.level.get();
if (level == null || !this.level.compareAndSet(level, null))
return;
level.close();
level.convertable.deleteLevel();
level.convertable.close();
var map = (Map<String, World>) WORLDS_FIELD.get(Bukkit.getServer());
map.remove(level.dimension().location().getPath());
getServer().removeLevel(level);
HandlerList.unregisterAll(this);
}
private static MinecraftServer getServer() {
return ((CraftServer) Bukkit.getServer()).getServer();
}
static {
try {
WORLDS_FIELD = CraftServer.class.getDeclaredField("worlds");
WORLDS_FIELD.setAccessible(true);
} catch (NoSuchFieldException e) {
throw new RuntimeException(e);
}
}
private class MemoryChunkData implements ChunkGenerator.ChunkData {
private final int maxHeight;
private final int minHeight;
private final ChunkPos pos;
private WeakReference<LevelChunk> chunk;
private MemoryChunkData(int x, int z) {
var level = MemoryWorld.this.level.get();
if (level == null)
throw new IllegalStateException("World is not loaded");
var chunk = level.getChunk(x, z);
this.minHeight = chunk.getMinBuildHeight();
this.maxHeight = chunk.getMaxBuildHeight();
this.pos = new ChunkPos(x, z);
this.chunk = new WeakReference<>(chunk);
}
public LevelChunk getHandle() {
LevelChunk chunk = this.chunk.get();
if (chunk == null) {
var level = MemoryWorld.this.level.get();
if (level == null)
throw new IllegalStateException("World is not loaded");
chunk = level.getChunk(this.pos.x, this.pos.z);
this.chunk = new WeakReference<>(chunk);
}
return chunk;
}
@Override
public int getMinHeight() {
return minHeight;
}
@Override
public int getMaxHeight() {
return maxHeight;
}
@NotNull
@Override
public Biome getBiome(int x, int y, int z) {
return CraftBiome.minecraftHolderToBukkit(getHandle().getNoiseBiome(x >> 2, y >> 2, z >> 2));
}
@Override
public void setBlock(int x, int y, int z, @NotNull Material material) {
setBlock(x, y, z, material.createBlockData());
}
@Override
public void setBlock(int x, int y, int z, @NotNull MaterialData material) {
setBlock(x, y, z, CraftMagicNumbers.getBlock(material));
}
@Override
public void setBlock(int x, int y, int z, @NotNull BlockData blockData) {
setBlock(x, y, z, ((CraftBlockData) blockData).getState());
}
@Override
public void setRegion(int xMin, int yMin, int zMin, int xMax, int yMax, int zMax, Material material) {
this.setRegion(xMin, yMin, zMin, xMax, yMax, zMax, material.createBlockData());
}
@Override
public void setRegion(int xMin, int yMin, int zMin, int xMax, int yMax, int zMax, MaterialData material) {
this.setRegion(xMin, yMin, zMin, xMax, yMax, zMax, CraftMagicNumbers.getBlock(material));
}
@Override
public void setRegion(int xMin, int yMin, int zMin, int xMax, int yMax, int zMax, BlockData blockData) {
this.setRegion(xMin, yMin, zMin, xMax, yMax, zMax, ((CraftBlockData)blockData).getState());
}
@NotNull
@Override
public Material getType(int x, int y, int z) {
return CraftMagicNumbers.getMaterial(this.getTypeId(x, y, z).getBlock());
}
@NotNull
@Override
public MaterialData getTypeAndData(int x, int y, int z) {
return CraftMagicNumbers.getMaterial(this.getTypeId(x, y, z));
}
@NotNull
@Override
public BlockData getBlockData(int x, int y, int z) {
return CraftBlockData.fromData(this.getTypeId(x, y, z));
}
@Deprecated
@Override
public byte getData(int x, int y, int z) {
return CraftMagicNumbers.toLegacyData(this.getTypeId(x, y, z));
}
private void setRegion(int xMin, int yMin, int zMin, int xMax, int yMax, int zMax, BlockState type) {
if (xMin <= 15 && yMin < this.maxHeight && zMin <= 15) {
if (xMin < 0) {
xMin = 0;
}
if (yMin < this.minHeight) {
yMin = this.minHeight;
}
if (zMin < 0) {
zMin = 0;
}
if (xMax > 16) {
xMax = 16;
}
if (yMax > this.maxHeight) {
yMax = this.maxHeight;
}
if (zMax > 16) {
zMax = 16;
}
if (xMin < xMax && yMin < yMax && zMin < zMax) {
for(int y = yMin; y < yMax; ++y) {
for(int x = xMin; x < xMax; ++x) {
for(int z = zMin; z < zMax; ++z) {
this.setBlock(x, y, z, type);
}
}
}
}
}
}
private BlockState getTypeId(int x, int y, int z) {
if (x == (x & 15) && y >= this.minHeight && y < this.maxHeight && z == (z & 15)) {
ChunkAccess access = this.getHandle();
return access.getBlockState(new BlockPos(access.getPos().getMinBlockX() + x, y, access.getPos().getMinBlockZ() + z));
} else {
return Blocks.AIR.defaultBlockState();
}
}
private void setBlock(int x, int y, int z, BlockState type) {
if (x == (x & 15) && y >= this.minHeight && y < this.maxHeight && z == (z & 15)) {
ChunkAccess access = this.getHandle();
BlockPos blockPosition = new BlockPos(access.getPos().getMinBlockX() + x, y, access.getPos().getMinBlockZ() + z);
BlockState oldBlockData = access.setBlockState(blockPosition, type, false);
if (type.hasBlockEntity()) {
BlockEntity tileEntity = ((EntityBlock)type.getBlock()).newBlockEntity(blockPosition, type);
if (tileEntity == null) {
access.removeBlockEntity(blockPosition);
} else {
access.setBlockEntity(tileEntity);
}
} else if (oldBlockData != null && oldBlockData.hasBlockEntity()) {
access.removeBlockEntity(blockPosition);
}
}
}
}
}

View File

@@ -1,10 +1,7 @@
package com.volmit.iris.core.nms.v1_20_R2;
import java.awt.Color;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.*;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
@@ -15,6 +12,7 @@ import java.util.Vector;
import java.util.concurrent.atomic.AtomicInteger;
import com.mojang.datafixers.util.Pair;
import com.volmit.iris.core.nms.IMemoryWorld;
import com.volmit.iris.core.nms.container.BiomeColor;
import com.volmit.iris.util.scheduling.J;
import net.minecraft.nbt.*;
@@ -610,6 +608,11 @@ public class NMSBinding implements INMSBinding {
return keys;
}
@Override
public IMemoryWorld createMemoryWorld(NamespacedKey levelType, WorldCreator creator) throws IOException {
return new MemoryWorld(levelType, creator);
}
private static Field getField(Class<?> clazz, Class<?> fieldType) throws NoSuchFieldException {
try {
for (Field f : clazz.getDeclaredFields()) {

View File

@@ -0,0 +1,400 @@
package com.volmit.iris.core.nms.v1_20_R3;
import com.google.common.collect.ImmutableList;
import com.mojang.serialization.Lifecycle;
import com.volmit.iris.Iris;
import com.volmit.iris.core.nms.IMemoryWorld;
import com.volmit.iris.util.reflect.Reflect;
import com.volmit.iris.util.scheduling.J;
import net.minecraft.core.BlockPos;
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.MinecraftServer;
import net.minecraft.server.dedicated.DedicatedServerProperties;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.util.GsonHelper;
import net.minecraft.world.Difficulty;
import net.minecraft.world.entity.ai.village.VillageSiege;
import net.minecraft.world.entity.npc.CatSpawner;
import net.minecraft.world.entity.npc.WanderingTraderSpawner;
import net.minecraft.world.level.ChunkPos;
import net.minecraft.world.level.GameRules;
import net.minecraft.world.level.GameType;
import net.minecraft.world.level.LevelSettings;
import net.minecraft.world.level.biome.BiomeManager;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.block.EntityBlock;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.chunk.ChunkAccess;
import net.minecraft.world.level.chunk.LevelChunk;
import net.minecraft.world.level.dimension.DimensionType;
import net.minecraft.world.level.dimension.LevelStem;
import net.minecraft.world.level.levelgen.PatrolSpawner;
import net.minecraft.world.level.levelgen.PhantomSpawner;
import net.minecraft.world.level.levelgen.WorldOptions;
import net.minecraft.world.level.storage.LevelStorageSource;
import net.minecraft.world.level.storage.PrimaryLevelData;
import net.minecraft.world.level.storage.ServerLevelData;
import org.bukkit.*;
import org.bukkit.block.Biome;
import org.bukkit.block.data.BlockData;
import org.bukkit.craftbukkit.v1_20_R3.CraftChunk;
import org.bukkit.craftbukkit.v1_20_R3.CraftServer;
import org.bukkit.craftbukkit.v1_20_R3.block.CraftBiome;
import org.bukkit.craftbukkit.v1_20_R3.block.CraftBlockType;
import org.bukkit.craftbukkit.v1_20_R3.block.data.CraftBlockData;
import org.bukkit.craftbukkit.v1_20_R3.generator.CraftChunkData;
import org.bukkit.craftbukkit.v1_20_R3.generator.CraftWorldInfo;
import org.bukkit.craftbukkit.v1_20_R3.util.CraftMagicNumbers;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.HandlerList;
import org.bukkit.event.Listener;
import org.bukkit.event.world.WorldUnloadEvent;
import org.bukkit.generator.ChunkGenerator;
import org.bukkit.material.MaterialData;
import org.bukkit.plugin.Plugin;
import org.jetbrains.annotations.NotNull;
import java.io.IOException;
import java.lang.ref.WeakReference;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.nio.file.Files;
import java.util.Arrays;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;
public class MemoryWorld implements IMemoryWorld {
private static final AtomicLong C = new AtomicLong();
private static final Field WORLDS_FIELD;
private final AtomicReference<ServerLevel> level = new AtomicReference<>();
public MemoryWorld(NamespacedKey levelType, WorldCreator creator) throws IOException {
var name = "memory_world"+C.getAndIncrement();
while (Bukkit.getWorld(name) != null) {
name = "memory_world"+C.getAndIncrement();
}
var generator = creator.generator();
var biomeProvider = creator.biomeProvider();
var hardcore = creator.hardcore();
var server = getServer();
var tempDir = Files.createTempDirectory("MemoryGenerator");
LevelStorageSource source = LevelStorageSource.createDefault(tempDir);
ResourceKey<LevelStem> stemKey = ResourceKey.create(Registries.LEVEL_STEM, new ResourceLocation(levelType.getNamespace(), levelType.getKey()));
var access = source.createAccess(name, stemKey);
var worldLoader = server.worldLoader;
var registry = server.registryAccess().registryOrThrow(Registries.LEVEL_STEM);
var options = new WorldOptions(creator.seed(), creator.generateStructures(), false);
var properties = new DedicatedServerProperties.WorldDimensionData(GsonHelper.parse(creator.generatorSettings().isEmpty() ? "{}" : creator.generatorSettings()), creator.type().name().toLowerCase(Locale.ROOT));
var settings = new LevelSettings(name, GameType.byId(Bukkit.getDefaultGameMode().getValue()), hardcore, Difficulty.EASY, false, new GameRules(), worldLoader.dataConfiguration());
var dimension = properties.create(worldLoader.datapackWorldgen()).bake(registry);
Lifecycle lifecycle = dimension.lifecycle().add(worldLoader.datapackWorldgen().allRegistriesLifecycle());
var worldData = new PrimaryLevelData(settings, options, dimension.specialWorldProperty(), lifecycle);
worldData.customDimensions = registry;
long obfSeed = BiomeManager.obfuscateSeed(creator.seed());
var list = ImmutableList.of(new PhantomSpawner(), new PatrolSpawner(), new CatSpawner(), new VillageSiege(), new WanderingTraderSpawner(worldData));
var levelStem = registry.get(stemKey);
if (levelStem == null)
throw new IllegalStateException("Unknown dimension type: " + stemKey);
CraftWorldInfo worldInfo;
try {
worldInfo = new CraftWorldInfo(worldData, access, creator.environment(), levelStem.type().value());
} catch (Throwable e) {
try {
worldInfo = Reflect.newInstance(CraftWorldInfo.class, worldData, access, creator.environment(), levelStem.type().value(), levelStem.generator(), server.registryAccess());
} catch (NoSuchMethodException | InvocationTargetException ex) {
throw new IOException("Failed to create CraftWorldInfo", ex);
}
}
if (biomeProvider == null && generator != null) {
biomeProvider = generator.getDefaultBiomeProvider(worldInfo);
}
var levelKey = ResourceKey.create(Registries.DIMENSION, new ResourceLocation(name));
var level = new ServerLevel(
server,
server.executor,
access,
worldData,
levelKey,
levelStem,
server.progressListenerFactory.create(0),
worldData.isDebugWorld(),
obfSeed,
creator.environment() == World.Environment.NORMAL ? list : ImmutableList.of(),
true,
server.overworld().getRandomSequences(),
creator.environment(),
generator,
biomeProvider
);
level.keepSpawnInMemory = false;
Iris.instance.registerListener(this);
this.level.set(level);
}
public World getBukkit() {
var level = this.level.get();
if (level == null)
throw new IllegalStateException("World is not loaded");
return level.getWorld();
}
public Chunk getChunk(int x, int z) {
var level = this.level.get();
if (level == null)
throw new IllegalStateException("World is not loaded");
return new CraftChunk(level, x, z);
}
public ChunkGenerator.ChunkData getChunkData(int x, int z) {
var level = this.level.get();
if (level == null)
throw new IllegalStateException("World is not loaded");
return new MemoryChunkData(x, z);
}
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
public void onWorldUnload(WorldUnloadEvent event) {
var level = this.level.get();
if (level == null || event.getWorld() != level.getWorld())
return;
this.level.set(null);
}
public boolean isClosed() {
return this.level.get() == null;
}
@Override
public void close() throws Exception {
if (!Bukkit.isPrimaryThread()) {
var future = new CompletableFuture<Void>();
J.s(() -> {
try {
close();
future.complete(null);
} catch (Exception e) {
future.completeExceptionally(e);
}
});
future.join();
return;
}
var level = this.level.get();
if (level == null || !this.level.compareAndSet(level, null))
return;
level.close();
level.convertable.deleteLevel();
level.convertable.close();
var map = (Map<String, World>) WORLDS_FIELD.get(Bukkit.getServer());
map.remove(level.dimension().location().getPath());
getServer().removeLevel(level);
HandlerList.unregisterAll(this);
}
private static MinecraftServer getServer() {
return ((CraftServer) Bukkit.getServer()).getServer();
}
static {
try {
WORLDS_FIELD = CraftServer.class.getDeclaredField("worlds");
WORLDS_FIELD.setAccessible(true);
} catch (NoSuchFieldException e) {
throw new RuntimeException(e);
}
}
private class MemoryChunkData implements ChunkGenerator.ChunkData {
private final int maxHeight;
private final int minHeight;
private final ChunkPos pos;
private WeakReference<LevelChunk> chunk;
private MemoryChunkData(int x, int z) {
var level = MemoryWorld.this.level.get();
if (level == null)
throw new IllegalStateException("World is not loaded");
var chunk = level.getChunk(x, z);
this.minHeight = chunk.getMinBuildHeight();
this.maxHeight = chunk.getMaxBuildHeight();
this.pos = new ChunkPos(x, z);
this.chunk = new WeakReference<>(chunk);
}
public LevelChunk getHandle() {
LevelChunk chunk = this.chunk.get();
if (chunk == null) {
var level = MemoryWorld.this.level.get();
if (level == null)
throw new IllegalStateException("World is not loaded");
chunk = level.getChunk(this.pos.x, this.pos.z);
this.chunk = new WeakReference<>(chunk);
}
return chunk;
}
@Override
public int getMinHeight() {
return minHeight;
}
@Override
public int getMaxHeight() {
return maxHeight;
}
@NotNull
@Override
public Biome getBiome(int x, int y, int z) {
return CraftBiome.minecraftHolderToBukkit(getHandle().getNoiseBiome(x >> 2, y >> 2, z >> 2));
}
@Override
public void setBlock(int x, int y, int z, @NotNull Material material) {
setBlock(x, y, z, material.createBlockData());
}
@Override
public void setBlock(int x, int y, int z, @NotNull MaterialData material) {
setBlock(x, y, z, CraftMagicNumbers.getBlock(material));
}
@Override
public void setBlock(int x, int y, int z, @NotNull BlockData blockData) {
setBlock(x, y, z, ((CraftBlockData) blockData).getState());
}
@Override
public void setRegion(int xMin, int yMin, int zMin, int xMax, int yMax, int zMax, Material material) {
this.setRegion(xMin, yMin, zMin, xMax, yMax, zMax, material.createBlockData());
}
@Override
public void setRegion(int xMin, int yMin, int zMin, int xMax, int yMax, int zMax, MaterialData material) {
this.setRegion(xMin, yMin, zMin, xMax, yMax, zMax, CraftMagicNumbers.getBlock(material));
}
@Override
public void setRegion(int xMin, int yMin, int zMin, int xMax, int yMax, int zMax, BlockData blockData) {
this.setRegion(xMin, yMin, zMin, xMax, yMax, zMax, ((CraftBlockData)blockData).getState());
}
@NotNull
@Override
public Material getType(int x, int y, int z) {
return CraftBlockType.minecraftToBukkit(this.getTypeId(x, y, z).getBlock());
}
@NotNull
@Override
public MaterialData getTypeAndData(int x, int y, int z) {
return CraftMagicNumbers.getMaterial(this.getTypeId(x, y, z));
}
@NotNull
@Override
public BlockData getBlockData(int x, int y, int z) {
return CraftBlockData.fromData(this.getTypeId(x, y, z));
}
@Deprecated
@Override
public byte getData(int x, int y, int z) {
return CraftMagicNumbers.toLegacyData(this.getTypeId(x, y, z));
}
private void setRegion(int xMin, int yMin, int zMin, int xMax, int yMax, int zMax, BlockState type) {
if (xMin <= 15 && yMin < this.maxHeight && zMin <= 15) {
if (xMin < 0) {
xMin = 0;
}
if (yMin < this.minHeight) {
yMin = this.minHeight;
}
if (zMin < 0) {
zMin = 0;
}
if (xMax > 16) {
xMax = 16;
}
if (yMax > this.maxHeight) {
yMax = this.maxHeight;
}
if (zMax > 16) {
zMax = 16;
}
if (xMin < xMax && yMin < yMax && zMin < zMax) {
for(int y = yMin; y < yMax; ++y) {
for(int x = xMin; x < xMax; ++x) {
for(int z = zMin; z < zMax; ++z) {
this.setBlock(x, y, z, type);
}
}
}
}
}
}
private BlockState getTypeId(int x, int y, int z) {
if (x == (x & 15) && y >= this.minHeight && y < this.maxHeight && z == (z & 15)) {
ChunkAccess access = this.getHandle();
return access.getBlockState(new BlockPos(access.getPos().getMinBlockX() + x, y, access.getPos().getMinBlockZ() + z));
} else {
return Blocks.AIR.defaultBlockState();
}
}
private void setBlock(int x, int y, int z, BlockState type) {
if (x == (x & 15) && y >= this.minHeight && y < this.maxHeight && z == (z & 15)) {
ChunkAccess access = this.getHandle();
BlockPos blockPosition = new BlockPos(access.getPos().getMinBlockX() + x, y, access.getPos().getMinBlockZ() + z);
BlockState oldBlockData = access.setBlockState(blockPosition, type, false);
if (type.hasBlockEntity()) {
BlockEntity tileEntity = ((EntityBlock)type.getBlock()).newBlockEntity(blockPosition, type);
if (tileEntity == null) {
access.removeBlockEntity(blockPosition);
} else {
access.setBlockEntity(tileEntity);
}
} else if (oldBlockData != null && oldBlockData.hasBlockEntity()) {
access.removeBlockEntity(blockPosition);
}
}
}
}
}

View File

@@ -1,10 +1,7 @@
package com.volmit.iris.core.nms.v1_20_R3;
import java.awt.Color;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.*;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
@@ -15,14 +12,19 @@ import java.util.Vector;
import java.util.concurrent.atomic.AtomicInteger;
import com.mojang.datafixers.util.Pair;
import com.volmit.iris.core.nms.IMemoryWorld;
import com.volmit.iris.core.nms.container.BiomeColor;
import com.volmit.iris.util.data.B;
import com.volmit.iris.util.scheduling.J;
import net.minecraft.nbt.*;
import net.minecraft.nbt.Tag;
import net.minecraft.server.commands.data.BlockDataAccessor;
import net.minecraft.tags.TagKey;
import net.minecraft.util.datafix.fixes.BlockStateData;
import net.minecraft.world.level.DataPackConfig;
import net.minecraft.world.level.LevelReader;
import net.minecraft.world.level.block.EntityBlock;
import net.minecraft.world.level.chunk.LevelChunkSection;
import org.bukkit.*;
import org.bukkit.block.Biome;
import org.bukkit.block.data.BlockData;
@@ -40,6 +42,8 @@ import org.bukkit.entity.Entity;
import org.bukkit.event.entity.CreatureSpawnEvent;
import org.bukkit.generator.ChunkGenerator;
import org.bukkit.inventory.ItemStack;
import org.bukkit.packs.DataPack;
import org.bukkit.packs.DataPackManager;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
@@ -631,4 +635,44 @@ public class NMSBinding implements INMSBinding {
public static Holder<net.minecraft.world.level.biome.Biome> biomeToBiomeBase(Registry<net.minecraft.world.level.biome.Biome> registry, Biome biome) {
return registry.getHolderOrThrow(ResourceKey.create(Registries.BIOME, CraftNamespacedKey.toMinecraft(biome.getKey())));
}
@Override
public boolean setBlock(World world, int x, int y, int z, BlockData data, int flag, int updateDepth) {
var level = ((CraftWorld) world).getHandle();
var blockData = ((CraftBlockData) data).getState();
return level.setBlock(new BlockPos(x, y, z), blockData, flag, updateDepth);
}
@Override
public BlockData getBlockData(CompoundTag tag) {
if (tag == null) {
return B.getAir();
}
StringBuilder p = new StringBuilder(tag.getString("Name"));
if (tag.containsKey("Properties")) {
CompoundTag props = tag.getCompoundTag("Properties");
p.append('[');
for (String i : props.keySet()) {
p.append(i).append('=').append(props.getString(i)).append(',');
}
p.deleteCharAt(p.length() - 1).append(']');
}
BlockData b = B.get(String.valueOf(p));
if (b == null) {
return B.getAir();
}
return b;
}
@Override
public IMemoryWorld createMemoryWorld(NamespacedKey levelType, WorldCreator creator) throws IOException {
return new MemoryWorld(levelType, creator);
}
}

View File

@@ -0,0 +1,396 @@
package com.volmit.iris.core.nms.v1_20_R4;
import com.google.common.collect.ImmutableList;
import com.mojang.serialization.Lifecycle;
import com.volmit.iris.Iris;
import com.volmit.iris.core.nms.IMemoryWorld;
import com.volmit.iris.util.reflect.Reflect;
import com.volmit.iris.util.scheduling.J;
import net.minecraft.core.BlockPos;
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.MinecraftServer;
import net.minecraft.server.dedicated.DedicatedServerProperties;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.util.GsonHelper;
import net.minecraft.world.Difficulty;
import net.minecraft.world.entity.ai.village.VillageSiege;
import net.minecraft.world.entity.npc.CatSpawner;
import net.minecraft.world.entity.npc.WanderingTraderSpawner;
import net.minecraft.world.level.ChunkPos;
import net.minecraft.world.level.GameRules;
import net.minecraft.world.level.GameType;
import net.minecraft.world.level.LevelSettings;
import net.minecraft.world.level.biome.BiomeManager;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.block.EntityBlock;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.chunk.ChunkAccess;
import net.minecraft.world.level.chunk.LevelChunk;
import net.minecraft.world.level.dimension.DimensionType;
import net.minecraft.world.level.dimension.LevelStem;
import net.minecraft.world.level.levelgen.PatrolSpawner;
import net.minecraft.world.level.levelgen.PhantomSpawner;
import net.minecraft.world.level.levelgen.WorldOptions;
import net.minecraft.world.level.storage.LevelStorageSource;
import net.minecraft.world.level.storage.PrimaryLevelData;
import net.minecraft.world.level.storage.ServerLevelData;
import org.bukkit.*;
import org.bukkit.block.Biome;
import org.bukkit.block.data.BlockData;
import org.bukkit.craftbukkit.v1_20_R4.CraftChunk;
import org.bukkit.craftbukkit.v1_20_R4.CraftServer;
import org.bukkit.craftbukkit.v1_20_R4.block.CraftBiome;
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.generator.CraftWorldInfo;
import org.bukkit.craftbukkit.v1_20_R4.util.CraftMagicNumbers;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.HandlerList;
import org.bukkit.event.world.WorldUnloadEvent;
import org.bukkit.generator.ChunkGenerator;
import org.bukkit.material.MaterialData;
import org.jetbrains.annotations.NotNull;
import java.io.IOException;
import java.lang.ref.WeakReference;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.nio.file.Files;
import java.util.Arrays;
import java.util.Locale;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;
public class MemoryWorld implements IMemoryWorld {
private static final AtomicLong C = new AtomicLong();
private static final Field WORLDS_FIELD;
private final AtomicReference<ServerLevel> level = new AtomicReference<>();
public MemoryWorld(NamespacedKey levelType, WorldCreator creator) throws IOException {
var name = "memory_world"+C.getAndIncrement();
while (Bukkit.getWorld(name) != null) {
name = "memory_world"+C.getAndIncrement();
}
var generator = creator.generator();
var biomeProvider = creator.biomeProvider();
var hardcore = creator.hardcore();
var server = getServer();
var tempDir = Files.createTempDirectory("MemoryGenerator");
LevelStorageSource source = LevelStorageSource.createDefault(tempDir);
ResourceKey<LevelStem> stemKey = ResourceKey.create(Registries.LEVEL_STEM, new ResourceLocation(levelType.getNamespace(), levelType.getKey()));
var access = source.createAccess(name, stemKey);
var worldLoader = server.worldLoader;
var registry = server.registryAccess().registryOrThrow(Registries.LEVEL_STEM);
var options = new WorldOptions(creator.seed(), creator.generateStructures(), false);
var properties = new DedicatedServerProperties.WorldDimensionData(GsonHelper.parse(creator.generatorSettings().isEmpty() ? "{}" : creator.generatorSettings()), creator.type().name().toLowerCase(Locale.ROOT));
var settings = new LevelSettings(name, GameType.byId(Bukkit.getDefaultGameMode().getValue()), hardcore, Difficulty.EASY, false, new GameRules(), worldLoader.dataConfiguration());
var dimension = properties.create(worldLoader.datapackWorldgen()).bake(registry);
Lifecycle lifecycle = dimension.lifecycle().add(worldLoader.datapackWorldgen().allRegistriesLifecycle());
var worldData = new PrimaryLevelData(settings, options, dimension.specialWorldProperty(), lifecycle);
worldData.customDimensions = registry;
worldData.getGameRules().getRule(GameRules.RULE_SPAWN_CHUNK_RADIUS).set(0, null);
long obfSeed = BiomeManager.obfuscateSeed(creator.seed());
var list = ImmutableList.of(new PhantomSpawner(), new PatrolSpawner(), new CatSpawner(), new VillageSiege(), new WanderingTraderSpawner(worldData));
var levelStem = registry.get(stemKey);
if (levelStem == null)
throw new IllegalStateException("Unknown dimension type: " + stemKey);
CraftWorldInfo worldInfo;
try {
worldInfo = new CraftWorldInfo(worldData, access, creator.environment(), levelStem.type().value());
} catch (Throwable e) {
try {
worldInfo = Reflect.newInstance(CraftWorldInfo.class, worldData, access, creator.environment(), levelStem.type().value(), levelStem.generator(), server.registryAccess());
} catch (NoSuchMethodException | InvocationTargetException ex) {
throw new IOException("Failed to create CraftWorldInfo", ex);
}
}
if (biomeProvider == null && generator != null) {
biomeProvider = generator.getDefaultBiomeProvider(worldInfo);
}
var levelKey = ResourceKey.create(Registries.DIMENSION, new ResourceLocation(name));
var level = new ServerLevel(
server,
server.executor,
access,
worldData,
levelKey,
levelStem,
server.progressListenerFactory.create(0),
worldData.isDebugWorld(),
obfSeed,
creator.environment() == World.Environment.NORMAL ? list : ImmutableList.of(),
true,
server.overworld().getRandomSequences(),
creator.environment(),
generator,
biomeProvider
);
Iris.instance.registerListener(this);
this.level.set(level);
}
public World getBukkit() {
var level = this.level.get();
if (level == null)
throw new IllegalStateException("World is not loaded");
return level.getWorld();
}
public Chunk getChunk(int x, int z) {
var level = this.level.get();
if (level == null)
throw new IllegalStateException("World is not loaded");
return new CraftChunk(level, x, z);
}
public ChunkGenerator.ChunkData getChunkData(int x, int z) {
var level = this.level.get();
if (level == null)
throw new IllegalStateException("World is not loaded");
return new MemoryChunkData(x, z);
}
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
public void onWorldUnload(WorldUnloadEvent event) {
var level = this.level.get();
if (level == null || event.getWorld() != level.getWorld())
return;
this.level.set(null);
}
public boolean isClosed() {
return this.level.get() == null;
}
@Override
public void close() throws Exception {
if (!Bukkit.isPrimaryThread()) {
var future = new CompletableFuture<Void>();
J.s(() -> {
try {
close();
future.complete(null);
} catch (Exception e) {
future.completeExceptionally(e);
}
});
future.join();
return;
}
var level = this.level.get();
if (level == null || !this.level.compareAndSet(level, null))
return;
level.close();
level.convertable.deleteLevel();
level.convertable.close();
var map = (Map<String, World>) WORLDS_FIELD.get(Bukkit.getServer());
map.remove(level.dimension().location().getPath());
getServer().removeLevel(level);
HandlerList.unregisterAll(this);
}
private static MinecraftServer getServer() {
return ((CraftServer) Bukkit.getServer()).getServer();
}
static {
try {
WORLDS_FIELD = CraftServer.class.getDeclaredField("worlds");
WORLDS_FIELD.setAccessible(true);
} catch (NoSuchFieldException e) {
throw new RuntimeException(e);
}
}
private class MemoryChunkData implements ChunkGenerator.ChunkData {
private final int maxHeight;
private final int minHeight;
private final ChunkPos pos;
private WeakReference<LevelChunk> chunk;
private MemoryChunkData(int x, int z) {
var level = MemoryWorld.this.level.get();
if (level == null)
throw new IllegalStateException("World is not loaded");
var chunk = level.getChunk(x, z);
this.minHeight = chunk.getMinBuildHeight();
this.maxHeight = chunk.getMaxBuildHeight();
this.pos = new ChunkPos(x, z);
this.chunk = new WeakReference<>(chunk);
}
public LevelChunk getHandle() {
LevelChunk chunk = this.chunk.get();
if (chunk == null) {
var level = MemoryWorld.this.level.get();
if (level == null)
throw new IllegalStateException("World is not loaded");
chunk = level.getChunk(this.pos.x, this.pos.z);
this.chunk = new WeakReference<>(chunk);
}
return chunk;
}
@Override
public int getMinHeight() {
return minHeight;
}
@Override
public int getMaxHeight() {
return maxHeight;
}
@NotNull
@Override
public Biome getBiome(int x, int y, int z) {
return CraftBiome.minecraftHolderToBukkit(getHandle().getNoiseBiome(x >> 2, y >> 2, z >> 2));
}
@Override
public void setBlock(int x, int y, int z, @NotNull Material material) {
setBlock(x, y, z, material.createBlockData());
}
@Override
public void setBlock(int x, int y, int z, @NotNull MaterialData material) {
setBlock(x, y, z, CraftMagicNumbers.getBlock(material));
}
@Override
public void setBlock(int x, int y, int z, @NotNull BlockData blockData) {
setBlock(x, y, z, ((CraftBlockData) blockData).getState());
}
@Override
public void setRegion(int xMin, int yMin, int zMin, int xMax, int yMax, int zMax, Material material) {
this.setRegion(xMin, yMin, zMin, xMax, yMax, zMax, material.createBlockData());
}
@Override
public void setRegion(int xMin, int yMin, int zMin, int xMax, int yMax, int zMax, MaterialData material) {
this.setRegion(xMin, yMin, zMin, xMax, yMax, zMax, CraftMagicNumbers.getBlock(material));
}
@Override
public void setRegion(int xMin, int yMin, int zMin, int xMax, int yMax, int zMax, BlockData blockData) {
this.setRegion(xMin, yMin, zMin, xMax, yMax, zMax, ((CraftBlockData)blockData).getState());
}
@NotNull
@Override
public Material getType(int x, int y, int z) {
return CraftBlockType.minecraftToBukkit(this.getTypeId(x, y, z).getBlock());
}
@NotNull
@Override
public MaterialData getTypeAndData(int x, int y, int z) {
return CraftMagicNumbers.getMaterial(this.getTypeId(x, y, z));
}
@NotNull
@Override
public BlockData getBlockData(int x, int y, int z) {
return CraftBlockData.fromData(this.getTypeId(x, y, z));
}
@Deprecated
@Override
public byte getData(int x, int y, int z) {
return CraftMagicNumbers.toLegacyData(this.getTypeId(x, y, z));
}
private void setRegion(int xMin, int yMin, int zMin, int xMax, int yMax, int zMax, BlockState type) {
if (xMin <= 15 && yMin < this.maxHeight && zMin <= 15) {
if (xMin < 0) {
xMin = 0;
}
if (yMin < this.minHeight) {
yMin = this.minHeight;
}
if (zMin < 0) {
zMin = 0;
}
if (xMax > 16) {
xMax = 16;
}
if (yMax > this.maxHeight) {
yMax = this.maxHeight;
}
if (zMax > 16) {
zMax = 16;
}
if (xMin < xMax && yMin < yMax && zMin < zMax) {
for(int y = yMin; y < yMax; ++y) {
for(int x = xMin; x < xMax; ++x) {
for(int z = zMin; z < zMax; ++z) {
this.setBlock(x, y, z, type);
}
}
}
}
}
}
private BlockState getTypeId(int x, int y, int z) {
if (x == (x & 15) && y >= this.minHeight && y < this.maxHeight && z == (z & 15)) {
ChunkAccess access = this.getHandle();
return access.getBlockState(new BlockPos(access.getPos().getMinBlockX() + x, y, access.getPos().getMinBlockZ() + z));
} else {
return Blocks.AIR.defaultBlockState();
}
}
private void setBlock(int x, int y, int z, BlockState type) {
if (x == (x & 15) && y >= this.minHeight && y < this.maxHeight && z == (z & 15)) {
ChunkAccess access = this.getHandle();
BlockPos blockPosition = new BlockPos(access.getPos().getMinBlockX() + x, y, access.getPos().getMinBlockZ() + z);
BlockState oldBlockData = access.setBlockState(blockPosition, type, false);
if (type.hasBlockEntity()) {
BlockEntity tileEntity = ((EntityBlock)type.getBlock()).newBlockEntity(blockPosition, type);
if (tileEntity == null) {
access.removeBlockEntity(blockPosition);
} else {
access.setBlockEntity(tileEntity);
}
} else if (oldBlockData != null && oldBlockData.hasBlockEntity()) {
access.removeBlockEntity(blockPosition);
}
}
}
}
}

View File

@@ -1,6 +1,7 @@
package com.volmit.iris.core.nms.v1_20_R4;
import java.awt.Color;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
@@ -8,6 +9,7 @@ import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import com.mojang.datafixers.util.Pair;
import com.volmit.iris.core.nms.IMemoryWorld;
import com.volmit.iris.core.nms.container.BiomeColor;
import com.volmit.iris.core.nms.datapack.DataVersion;
import com.volmit.iris.util.nbt.tag.CompoundTag;
@@ -570,12 +572,18 @@ public class NMSBinding implements INMSBinding {
return null;
}
@Override
public Entity spawnEntity(Location location, org.bukkit.entity.EntityType type, CreatureSpawnEvent.SpawnReason reason) {
return ((CraftWorld) location.getWorld()).spawn(location, type.getEntityClass(), null, reason);
}
@Override
public boolean setBlock(World world, int x, int y, int z, BlockData data, int flag, int updateDepth) {
var level = ((CraftWorld) world).getHandle();
var blockData = ((CraftBlockData) data).getState();
return level.setBlock(new BlockPos(x, y, z), blockData, flag, updateDepth);
}
@Override
public Color getBiomeColor(Location location, BiomeColor type) {
LevelReader reader = ((CraftWorld) location.getWorld()).getHandle();
@@ -650,4 +658,9 @@ public class NMSBinding implements INMSBinding {
return keys;
}
@Override
public IMemoryWorld createMemoryWorld(NamespacedKey levelType, WorldCreator creator) throws IOException {
return new MemoryWorld(levelType, creator);
}
}

View File

@@ -0,0 +1,397 @@
package com.volmit.iris.core.nms.v1_21_R1;
import com.google.common.collect.ImmutableList;
import com.mojang.serialization.Lifecycle;
import com.volmit.iris.Iris;
import com.volmit.iris.core.nms.IMemoryWorld;
import com.volmit.iris.util.reflect.Reflect;
import com.volmit.iris.util.scheduling.J;
import net.minecraft.core.BlockPos;
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.MinecraftServer;
import net.minecraft.server.dedicated.DedicatedServerProperties;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.util.GsonHelper;
import net.minecraft.world.Difficulty;
import net.minecraft.world.entity.ai.village.VillageSiege;
import net.minecraft.world.entity.npc.CatSpawner;
import net.minecraft.world.entity.npc.WanderingTraderSpawner;
import net.minecraft.world.level.ChunkPos;
import net.minecraft.world.level.GameRules;
import net.minecraft.world.level.GameType;
import net.minecraft.world.level.LevelSettings;
import net.minecraft.world.level.biome.BiomeManager;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.block.EntityBlock;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.chunk.ChunkAccess;
import net.minecraft.world.level.chunk.LevelChunk;
import net.minecraft.world.level.dimension.DimensionType;
import net.minecraft.world.level.dimension.LevelStem;
import net.minecraft.world.level.levelgen.PatrolSpawner;
import net.minecraft.world.level.levelgen.PhantomSpawner;
import net.minecraft.world.level.levelgen.WorldOptions;
import net.minecraft.world.level.storage.LevelStorageSource;
import net.minecraft.world.level.storage.PrimaryLevelData;
import net.minecraft.world.level.storage.ServerLevelData;
import org.bukkit.*;
import org.bukkit.block.Biome;
import org.bukkit.block.data.BlockData;
import org.bukkit.craftbukkit.v1_21_R1.CraftChunk;
import org.bukkit.craftbukkit.v1_21_R1.CraftServer;
import org.bukkit.craftbukkit.v1_21_R1.block.CraftBiome;
import org.bukkit.craftbukkit.v1_21_R1.block.CraftBlockType;
import org.bukkit.craftbukkit.v1_21_R1.block.data.CraftBlockData;
import org.bukkit.craftbukkit.v1_21_R1.generator.CraftWorldInfo;
import org.bukkit.craftbukkit.v1_21_R1.util.CraftMagicNumbers;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.HandlerList;
import org.bukkit.event.world.WorldUnloadEvent;
import org.bukkit.generator.ChunkGenerator;
import org.bukkit.material.MaterialData;
import org.jetbrains.annotations.NotNull;
import java.io.IOException;
import java.lang.ref.WeakReference;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Modifier;
import java.nio.file.Files;
import java.util.Arrays;
import java.util.Locale;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;
public class MemoryWorld implements IMemoryWorld {
private static final AtomicLong C = new AtomicLong();
private static final Field WORLDS_FIELD;
private final AtomicReference<ServerLevel> level = new AtomicReference<>();
public MemoryWorld(NamespacedKey levelType, WorldCreator creator) throws IOException {
var name = "memory_world"+C.getAndIncrement();
while (Bukkit.getWorld(name) != null) {
name = "memory_world"+C.getAndIncrement();
}
var generator = creator.generator();
var biomeProvider = creator.biomeProvider();
var hardcore = creator.hardcore();
var server = getServer();
var tempDir = Files.createTempDirectory("MemoryGenerator");
LevelStorageSource source = LevelStorageSource.createDefault(tempDir);
ResourceKey<LevelStem> stemKey = ResourceKey.create(Registries.LEVEL_STEM, ResourceLocation.fromNamespaceAndPath(levelType.getNamespace(), levelType.getKey()));
var access = source.createAccess(name, stemKey);
var worldLoader = server.worldLoader;
var registry = server.registryAccess().registryOrThrow(Registries.LEVEL_STEM);
var options = new WorldOptions(creator.seed(), creator.generateStructures(), false);
var properties = new DedicatedServerProperties.WorldDimensionData(GsonHelper.parse(creator.generatorSettings().isEmpty() ? "{}" : creator.generatorSettings()), creator.type().name().toLowerCase(Locale.ROOT));
var settings = new LevelSettings(name, GameType.byId(Bukkit.getDefaultGameMode().getValue()), hardcore, Difficulty.EASY, false, new GameRules(), worldLoader.dataConfiguration());
var dimension = properties.create(worldLoader.datapackWorldgen()).bake(registry);
Lifecycle lifecycle = dimension.lifecycle().add(worldLoader.datapackWorldgen().allRegistriesLifecycle());
var worldData = new PrimaryLevelData(settings, options, dimension.specialWorldProperty(), lifecycle);
worldData.customDimensions = registry;
worldData.getGameRules().getRule(GameRules.RULE_SPAWN_CHUNK_RADIUS).set(0, null);
long obfSeed = BiomeManager.obfuscateSeed(creator.seed());
var list = ImmutableList.of(new PhantomSpawner(), new PatrolSpawner(), new CatSpawner(), new VillageSiege(), new WanderingTraderSpawner(worldData));
var levelStem = registry.get(stemKey);
if (levelStem == null)
throw new IllegalStateException("Unknown dimension type: " + stemKey);
CraftWorldInfo worldInfo;
try {
worldInfo = new CraftWorldInfo(worldData, access, creator.environment(), levelStem.type().value());
} catch (Throwable e) {
try {
worldInfo = Reflect.newInstance(CraftWorldInfo.class, worldData, access, creator.environment(), levelStem.type().value(), levelStem.generator(), server.registryAccess());
} catch (NoSuchMethodException | InvocationTargetException ex) {
throw new IOException("Failed to create CraftWorldInfo", ex);
}
}
if (biomeProvider == null && generator != null) {
biomeProvider = generator.getDefaultBiomeProvider(worldInfo);
}
var levelKey = ResourceKey.create(Registries.DIMENSION, ResourceLocation.withDefaultNamespace(name));
var level = new ServerLevel(
server,
server.executor,
access,
worldData,
levelKey,
levelStem,
server.progressListenerFactory.create(0),
worldData.isDebugWorld(),
obfSeed,
creator.environment() == World.Environment.NORMAL ? list : ImmutableList.of(),
true,
server.overworld().getRandomSequences(),
creator.environment(),
generator,
biomeProvider
);
Iris.instance.registerListener(this);
this.level.set(level);
}
public World getBukkit() {
var level = this.level.get();
if (level == null)
throw new IllegalStateException("World is not loaded");
return level.getWorld();
}
public Chunk getChunk(int x, int z) {
var level = this.level.get();
if (level == null)
throw new IllegalStateException("World is not loaded");
return new CraftChunk(level, x, z);
}
public ChunkGenerator.ChunkData getChunkData(int x, int z) {
var level = this.level.get();
if (level == null)
throw new IllegalStateException("World is not loaded");
return new MemoryChunkData(x, z);
}
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
public void onWorldUnload(WorldUnloadEvent event) {
var level = this.level.get();
if (level == null || event.getWorld() != level.getWorld())
return;
this.level.set(null);
}
public boolean isClosed() {
return this.level.get() == null;
}
@Override
public void close() throws Exception {
if (!Bukkit.isPrimaryThread()) {
var future = new CompletableFuture<Void>();
J.s(() -> {
try {
close();
future.complete(null);
} catch (Exception e) {
future.completeExceptionally(e);
}
});
future.join();
return;
}
var level = this.level.get();
if (level == null || !this.level.compareAndSet(level, null))
return;
level.close();
level.convertable.deleteLevel();
level.convertable.close();
var map = (Map<String, World>) WORLDS_FIELD.get(Bukkit.getServer());
map.remove(level.dimension().location().getPath());
getServer().removeLevel(level);
HandlerList.unregisterAll(this);
}
private static MinecraftServer getServer() {
return ((CraftServer) Bukkit.getServer()).getServer();
}
static {
try {
WORLDS_FIELD = CraftServer.class.getDeclaredField("worlds");
WORLDS_FIELD.setAccessible(true);
} catch (NoSuchFieldException e) {
throw new RuntimeException(e);
}
}
private class MemoryChunkData implements ChunkGenerator.ChunkData {
private final int maxHeight;
private final int minHeight;
private final ChunkPos pos;
private WeakReference<LevelChunk> chunk;
private MemoryChunkData(int x, int z) {
var level = MemoryWorld.this.level.get();
if (level == null)
throw new IllegalStateException("World is not loaded");
var chunk = level.getChunk(x, z);
this.minHeight = chunk.getMinBuildHeight();
this.maxHeight = chunk.getMaxBuildHeight();
this.pos = new ChunkPos(x, z);
this.chunk = new WeakReference<>(chunk);
}
public LevelChunk getHandle() {
LevelChunk chunk = this.chunk.get();
if (chunk == null) {
var level = MemoryWorld.this.level.get();
if (level == null)
throw new IllegalStateException("World is not loaded");
chunk = level.getChunk(this.pos.x, this.pos.z);
this.chunk = new WeakReference<>(chunk);
}
return chunk;
}
@Override
public int getMinHeight() {
return minHeight;
}
@Override
public int getMaxHeight() {
return maxHeight;
}
@NotNull
@Override
public Biome getBiome(int x, int y, int z) {
return CraftBiome.minecraftHolderToBukkit(getHandle().getNoiseBiome(x >> 2, y >> 2, z >> 2));
}
@Override
public void setBlock(int x, int y, int z, @NotNull Material material) {
setBlock(x, y, z, material.createBlockData());
}
@Override
public void setBlock(int x, int y, int z, @NotNull MaterialData material) {
setBlock(x, y, z, CraftMagicNumbers.getBlock(material));
}
@Override
public void setBlock(int x, int y, int z, @NotNull BlockData blockData) {
setBlock(x, y, z, ((CraftBlockData) blockData).getState());
}
@Override
public void setRegion(int xMin, int yMin, int zMin, int xMax, int yMax, int zMax, Material material) {
this.setRegion(xMin, yMin, zMin, xMax, yMax, zMax, material.createBlockData());
}
@Override
public void setRegion(int xMin, int yMin, int zMin, int xMax, int yMax, int zMax, MaterialData material) {
this.setRegion(xMin, yMin, zMin, xMax, yMax, zMax, CraftMagicNumbers.getBlock(material));
}
@Override
public void setRegion(int xMin, int yMin, int zMin, int xMax, int yMax, int zMax, BlockData blockData) {
this.setRegion(xMin, yMin, zMin, xMax, yMax, zMax, ((CraftBlockData)blockData).getState());
}
@NotNull
@Override
public Material getType(int x, int y, int z) {
return CraftBlockType.minecraftToBukkit(this.getTypeId(x, y, z).getBlock());
}
@NotNull
@Override
public MaterialData getTypeAndData(int x, int y, int z) {
return CraftMagicNumbers.getMaterial(this.getTypeId(x, y, z));
}
@NotNull
@Override
public BlockData getBlockData(int x, int y, int z) {
return CraftBlockData.fromData(this.getTypeId(x, y, z));
}
@Deprecated
@Override
public byte getData(int x, int y, int z) {
return CraftMagicNumbers.toLegacyData(this.getTypeId(x, y, z));
}
private void setRegion(int xMin, int yMin, int zMin, int xMax, int yMax, int zMax, BlockState type) {
if (xMin <= 15 && yMin < this.maxHeight && zMin <= 15) {
if (xMin < 0) {
xMin = 0;
}
if (yMin < this.minHeight) {
yMin = this.minHeight;
}
if (zMin < 0) {
zMin = 0;
}
if (xMax > 16) {
xMax = 16;
}
if (yMax > this.maxHeight) {
yMax = this.maxHeight;
}
if (zMax > 16) {
zMax = 16;
}
if (xMin < xMax && yMin < yMax && zMin < zMax) {
for(int y = yMin; y < yMax; ++y) {
for(int x = xMin; x < xMax; ++x) {
for(int z = zMin; z < zMax; ++z) {
this.setBlock(x, y, z, type);
}
}
}
}
}
}
private BlockState getTypeId(int x, int y, int z) {
if (x == (x & 15) && y >= this.minHeight && y < this.maxHeight && z == (z & 15)) {
ChunkAccess access = this.getHandle();
return access.getBlockState(new BlockPos(access.getPos().getMinBlockX() + x, y, access.getPos().getMinBlockZ() + z));
} else {
return Blocks.AIR.defaultBlockState();
}
}
private void setBlock(int x, int y, int z, BlockState type) {
if (x == (x & 15) && y >= this.minHeight && y < this.maxHeight && z == (z & 15)) {
ChunkAccess access = this.getHandle();
BlockPos blockPosition = new BlockPos(access.getPos().getMinBlockX() + x, y, access.getPos().getMinBlockZ() + z);
BlockState oldBlockData = access.setBlockState(blockPosition, type, false);
if (type.hasBlockEntity()) {
BlockEntity tileEntity = ((EntityBlock)type.getBlock()).newBlockEntity(blockPosition, type);
if (tileEntity == null) {
access.removeBlockEntity(blockPosition);
} else {
access.setBlockEntity(tileEntity);
}
} else if (oldBlockData != null && oldBlockData.hasBlockEntity()) {
access.removeBlockEntity(blockPosition);
}
}
}
}
}

View File

@@ -1,10 +1,7 @@
package com.volmit.iris.core.nms.v1_21_R1;
import java.awt.Color;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.*;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
@@ -12,6 +9,7 @@ import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import com.mojang.datafixers.util.Pair;
import com.volmit.iris.core.nms.IMemoryWorld;
import com.volmit.iris.core.nms.container.BiomeColor;
import com.volmit.iris.core.nms.datapack.DataVersion;
import com.volmit.iris.util.scheduling.J;
@@ -577,6 +575,12 @@ public class NMSBinding implements INMSBinding {
return null;
}
@Override
public boolean setBlock(World world, int x, int y, int z, BlockData data, int flag, int updateDepth) {
var level = ((CraftWorld) world).getHandle();
var blockData = ((CraftBlockData) data).getState();
return level.setBlock(new BlockPos(x, y, z), blockData, flag, updateDepth);
}
@Override
public Entity spawnEntity(Location location, org.bukkit.entity.EntityType type, CreatureSpawnEvent.SpawnReason reason) {
@@ -657,4 +661,9 @@ public class NMSBinding implements INMSBinding {
return keys;
}
@Override
public IMemoryWorld createMemoryWorld(NamespacedKey levelType, WorldCreator creator) throws IOException {
return new MemoryWorld(levelType, creator);
}
}