9
0
mirror of https://github.com/BX-Team/DivineMC.git synced 2025-12-22 16:29:23 +00:00

implement new patches

This commit is contained in:
NONPLAYT
2025-02-22 15:13:16 +03:00
parent b90c03f48d
commit a67dc0f576
184 changed files with 21942 additions and 1357 deletions

View File

@@ -0,0 +1,89 @@
package net.caffeinemc.mods.lithium.common.util.math;
import net.minecraft.util.Mth;
/**
* A replacement for the sine angle lookup table used in {@link Mth}, both reducing the size of LUT and improving
* the access patterns for common paired sin/cos operations.
* <p>
* sin(-x) = -sin(x)
* ... to eliminate negative angles from the LUT.
* <p>
* sin(x) = sin(pi/2 - x)
* ... to eliminate supplementary angles from the LUT.
* <p>
* Using these identities allows us to reduce the LUT from 64K entries (256 KB) to just 16K entries (64 KB), enabling
* it to better fit into the CPU's caches at the expense of some cycles on the fast path. The implementation has been
* tightly optimized to avoid branching where possible and to use very quick integer operations.
* <p>
* Generally speaking, reducing the size of a lookup table is always a good optimization, but since we need to spend
* extra CPU cycles trying to maintain parity with vanilla, there is the potential risk that this implementation ends
* up being slower than vanilla when the lookup table is able to be kept in cache memory.
* <p>
* Unlike other "fast math" implementations, the values returned by this class are *bit-for-bit identical* with those
* from {@link Mth}. Validation is performed during runtime to ensure that the table is correct.
*
* @author coderbot16 Author of the original (and very clever) implementation in <a href="https://gitlab.com/coderbot16/i73/-/tree/master/i73-trig/src">Rust</a>
* @author jellysquid3 Additional optimizations, port to Java
*/
public class CompactSineLUT {
private static final int[] SINE_TABLE_INT = new int[16384 + 1];
private static final float SINE_TABLE_MIDPOINT;
static {
final float[] SINE_TABLE = Mth.SIN;
// Copy the sine table, covering to raw int bits
for (int i = 0; i < SINE_TABLE_INT.length; i++) {
SINE_TABLE_INT[i] = Float.floatToRawIntBits(SINE_TABLE[i]);
}
SINE_TABLE_MIDPOINT = SINE_TABLE[SINE_TABLE.length / 2];
// Test that the lookup table is correct during runtime
for (int i = 0; i < SINE_TABLE.length; i++) {
float expected = SINE_TABLE[i];
float value = lookup(i);
if (expected != value) {
throw new IllegalArgumentException(String.format("LUT error at index %d (expected: %s, found: %s)", i, expected, value));
}
}
}
// [VanillaCopy] MathHelper#sin(float)
public static float sin(float f) {
return lookup((int) (f * 10430.378f) & 0xFFFF);
}
// [VanillaCopy] MathHelper#cos(float)
public static float cos(float f) {
return lookup((int) (f * 10430.378f + 16384.0f) & 0xFFFF);
}
private static float lookup(int index) {
// A special case... Is there some way to eliminate this?
if (index == 32768) {
return SINE_TABLE_MIDPOINT;
}
// Trigonometric identity: sin(-x) = -sin(x)
// Given a domain of 0 <= x <= 2*pi, just negate the value if x > pi.
// This allows the sin table size to be halved.
int neg = (index & 0x8000) << 16;
// All bits set if (pi/2 <= x), none set otherwise
// Extracts the 15th bit from 'half'
int mask = (index << 17) >> 31;
// Trigonometric identity: sin(x) = sin(pi/2 - x)
int pos = (0x8001 & mask) + (index ^ mask);
// Wrap the position in the table. Moving this down to immediately before the array access
// seems to help the Hotspot compiler optimize the bit math better.
pos &= 0x7fff;
// Fetch the corresponding value from the LUT and invert the sign bit as needed
// This directly manipulate the sign bit on the float bits to simplify logic
return Float.intBitsToFloat(SINE_TABLE_INT[pos] ^ neg);
}
}

View File

@@ -0,0 +1,372 @@
package org.bxteam.divinemc;
import com.google.common.base.Throwables;
import com.google.common.collect.ImmutableMap;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.state.BlockBehaviour;
import org.bukkit.Bukkit;
import org.bukkit.command.Command;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.configuration.InvalidConfigurationException;
import org.bukkit.configuration.file.YamlConfiguration;
import org.bxteam.divinemc.server.chunk.ChunkSystemAlgorithms;
import org.jetbrains.annotations.Nullable;
import org.jspecify.annotations.NullMarked;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
@SuppressWarnings("unused")
@NullMarked
public final class DivineConfig {
private DivineConfig() {
throw new IllegalStateException("Utility class");
}
private static final String HEADER = "This is the main configuration file for DivineMC.\n"
+ "If you need help with the configuration or have any questions related to DivineMC,\n"
+ "join us in our Discord server.\n"
+ "\n"
+ "Discord: https://discord.gg/p7cxhw7E2M \n"
+ "Docs: https://bxteam.org/docs/divinemc \n"
+ "New builds: https://github.com/BX-Team/DivineMC/releases/latest";
private static File configFile;
public static YamlConfiguration config;
private static Map<String, Command> commands;
public static int version;
static boolean verbose;
public static void init(File configFile) {
DivineConfig.configFile = configFile;
config = new YamlConfiguration();
try {
config.load(configFile);
} catch (IOException ignored) {
} catch (InvalidConfigurationException ex) {
Bukkit.getLogger().log(Level.SEVERE, "Could not load divinemc.yml, please correct your syntax errors", ex);
throw Throwables.propagate(ex);
}
config.options().header(HEADER);
config.options().copyDefaults(true);
verbose = getBoolean(config, "verbose", false);
version = getInt(config, "config-version", 5);
set(config, "config-version", 5);
readConfig(DivineConfig.class, null);
}
public static void log(String s) {
if (verbose) {
log(Level.INFO, s);
}
}
public static void log(Level level, String s) {
Bukkit.getLogger().log(level, s);
}
static void readConfig(Class<?> clazz, @Nullable Object instance) {
readConfig(configFile, config, clazz, instance);
}
public static void readConfig(File configFile, YamlConfiguration config, Class<?> clazz, @Nullable Object instance) {
for (Method method : clazz.getDeclaredMethods()) {
if (!Modifier.isPrivate(method.getModifiers())) continue;
if (method.getParameterTypes().length != 0) continue;
if (method.getReturnType() != Void.TYPE) continue;
try {
method.setAccessible(true);
method.invoke(instance);
} catch (InvocationTargetException ex) {
throw Throwables.propagate(ex.getCause());
} catch (Exception ex) {
Bukkit.getLogger().log(Level.SEVERE, "Error invoking " + method, ex);
}
}
try {
config.save(configFile);
} catch (IOException ex) {
Bukkit.getLogger().log(Level.SEVERE, "Could not save " + configFile, ex);
}
}
private static void set(YamlConfiguration config, String path, Object val, String... comments) {
config.addDefault(path, val);
config.set(path, val);
if (comments.length > 0) {
config.setComments(path, List.of(comments));
}
}
private static String getString(YamlConfiguration config, String path, String def, String... comments) {
config.addDefault(path, def);
if (comments.length > 0) {
config.setComments(path, List.of(comments));
}
return config.getString(path, config.getString(path));
}
private static boolean getBoolean(YamlConfiguration config, String path, boolean def, String... comments) {
config.addDefault(path, def);
if (comments.length > 0) {
config.setComments(path, List.of(comments));
}
return config.getBoolean(path, config.getBoolean(path));
}
private static double getDouble(YamlConfiguration config, String path, double def, String... comments) {
config.addDefault(path, def);
if (comments.length > 0) {
config.setComments(path, List.of(comments));
}
return config.getDouble(path, config.getDouble(path));
}
private static int getInt(YamlConfiguration config, String path, int def, String... comments) {
config.addDefault(path, def);
if (comments.length > 0) {
config.setComments(path, List.of(comments));
}
return config.getInt(path, config.getInt(path));
}
private static long getLong(YamlConfiguration config, String path, long def, String... comments) {
config.addDefault(path, def);
if (comments.length > 0) {
config.setComments(path, List.of(comments));
}
return config.getLong(path, config.getLong(path));
}
private static <T> List<T> getList(YamlConfiguration config, String path, List<T> def, String... comments) {
config.addDefault(path, def);
if (comments.length > 0) {
config.setComments(path, List.of(comments));
}
return (List<T>) config.getList(path, def);
}
static Map<String, Object> getMap(YamlConfiguration config, String path, Map<String, Object> def, String... comments) {
if (def != null && config.getConfigurationSection(path) == null) {
config.addDefault(path, def);
return def;
}
if (comments.length > 0) {
config.setComments(path, List.of(comments));
}
return toMap(config.getConfigurationSection(path));
}
private static Map<String, Object> toMap(ConfigurationSection section) {
ImmutableMap.Builder<String, Object> builder = ImmutableMap.builder();
if (section != null) {
for (String key : section.getKeys(false)) {
Object obj = section.get(key);
if (obj != null) {
builder.put(key, obj instanceof ConfigurationSection val ? toMap(val) : obj);
}
}
}
return builder.build();
}
public static int parallelThreadCount = 4;
public static boolean logContainerCreationStacktraces = false;
private static void parallelWorldTicking() {
parallelThreadCount = getInt(config, "settings.parallel-world-ticking.thread-count", parallelThreadCount);
logContainerCreationStacktraces = getBoolean(config, "settings.parallel-world-ticking.log-container-creation-stacktraces", logContainerCreationStacktraces);
}
public static boolean nativeAccelerationEnabled = true;
public static boolean allowAVX512 = false;
public static int isaTargetLevelOverride = -1;
public static long chunkDataCacheSoftLimit = 8192L;
public static long chunkDataCacheLimit = 32678L;
public static int maxViewDistance = 32;
public static ChunkSystemAlgorithms chunkWorkerAlgorithm = ChunkSystemAlgorithms.C2ME;
public static boolean enableSecureSeed = false;
public static boolean enableDensityFunctionCompiler = false;
public static boolean enableStructureLayoutOptimizer = true;
public static boolean deduplicateShuffledTemplatePoolElementList = false;
private static void chunkGeneration() {
nativeAccelerationEnabled = getBoolean(config, "settings.chunk-generation.native-acceleration-enabled", nativeAccelerationEnabled);
allowAVX512 = getBoolean(config, "settings.chunk-generation.allow-avx512", allowAVX512,
"Enables AVX512 support for natives-math optimizations",
"",
"Read more about AVX512: https://en.wikipedia.org/wiki/AVX-512");
isaTargetLevelOverride = getInt(config, "settings.chunk-generation.isa-target-level-override", isaTargetLevelOverride,
"Overrides the ISA target located by the native loader, which allows forcing AVX512 (must be a value between 6-9 for AVX512 support).",
"Value must be between 1-9, and -1 to disable override");
if (isaTargetLevelOverride < -1 || isaTargetLevelOverride > 9) {
log(Level.WARNING, "Invalid ISA target level override: " + isaTargetLevelOverride + ", resetting to -1");
isaTargetLevelOverride = -1;
}
chunkDataCacheSoftLimit = getLong(config, "settings.chunk-generation.chunk-data-cache-soft-limit", chunkDataCacheSoftLimit);
chunkDataCacheLimit = getLong(config, "settings.chunk-generation.chunk-data-cache-limit", chunkDataCacheLimit);
maxViewDistance = getInt(config, "settings.chunk-generation.max-view-distance", maxViewDistance,
"Changes the maximum view distance for the server, allowing clients to have render distances higher than 32");
chunkWorkerAlgorithm = ChunkSystemAlgorithms.valueOf(getString(config, "settings.chunk-generation.chunk-worker-algorithm", chunkWorkerAlgorithm.name(),
"Modifies what algorithm the chunk system will use to define thread counts. values: MOONRISE, C2ME, C2ME_AGGRESSIVE"));
enableSecureSeed = getBoolean(config, "settings.misc.enable-secure-seed", enableSecureSeed,
"This feature is based on Secure Seed mod by Earthcomputer.",
"",
"Terrain and biome generation remains the same, but all the ores and structures are generated with 1024-bit seed, instead of the usual 64-bit seed.",
"This seed is almost impossible to crack, and there are no weird links between structures.");
enableDensityFunctionCompiler = getBoolean(config, "settings.chunk-generation.experimental.enable-density-function-compiler", enableDensityFunctionCompiler,
"Whether to use density function compiler to accelerate world generation",
"",
"Density function: https://minecraft.wiki/w/Density_function",
"",
"This functionality compiles density functions from world generation",
"datapacks (including vanilla generation) to JVM bytecode to increase",
"performance by allowing JVM JIT to better optimize the code.",
"All functions provided by vanilla are implemented.",
"",
"Please test if this optimization actually benefits your server, as",
"it can sometimes slow down chunk performance than speed it up.");
enableStructureLayoutOptimizer = getBoolean(config, "settings.chunk-generation.experimental.enable-structure-layout-optimizer", enableStructureLayoutOptimizer,
"Enables a port of the mod StructureLayoutOptimizer, which optimizes general Jigsaw structure generation");
deduplicateShuffledTemplatePoolElementList = getBoolean(config, "settings.chunk-generation.experimental.deduplicate-shuffled-template-pool-element-list", deduplicateShuffledTemplatePoolElementList,
"Whether to use an alternative strategy to make structure layouts generate slightly even faster than",
"the default optimization this mod has for template pool weights. This alternative strategy works by",
"changing the list of pieces that structures collect from the template pool to not have duplicate entries.",
"",
"This will not break the structure generation, but it will make the structure layout different than",
"if this config was off (breaking vanilla seed parity). The cost of speed may be worth it in large",
"modpacks where many structure mods are using very high weight values in their template pools.");
}
public static boolean skipUselessSecondaryPoiSensor = true;
public static boolean clumpOrbs = true;
public static boolean ignoreMovedTooQuicklyWhenLagging = true;
public static boolean alwaysAllowWeirdMovement = true;
private static void miscSettings() {
skipUselessSecondaryPoiSensor = getBoolean(config, "settings.misc.skip-useless-secondary-poi-sensor", skipUselessSecondaryPoiSensor);
clumpOrbs = getBoolean(config, "settings.misc.clump-orbs", clumpOrbs,
"Clumps experience orbs together to reduce entity count");
ignoreMovedTooQuicklyWhenLagging = getBoolean(config, "settings.misc.ignore-moved-too-quickly-when-lagging", ignoreMovedTooQuicklyWhenLagging,
"Improves general gameplay experience of the player when the server is lagging, as they won't get lagged back (message 'moved too quickly')");
alwaysAllowWeirdMovement = getBoolean(config, "settings.misc.always-allow-weird-movement", alwaysAllowWeirdMovement,
"Means ignoring messages like 'moved too quickly' and 'moved wrongly'");
}
public static boolean enableFasterTntOptimization = true;
public static boolean explosionNoBlockDamage = false;
public static double tntRandomRange = -1;
private static void tntOptimization() {
enableFasterTntOptimization = getBoolean(config, "settings.tnt-optimization.enable-faster-tnt-optimization", enableFasterTntOptimization);
explosionNoBlockDamage = getBoolean(config, "settings.tnt-optimization.explosion-no-block-damage", explosionNoBlockDamage);
tntRandomRange = getDouble(config, "settings.tnt-optimization.tnt-random-range", tntRandomRange);
}
public static boolean lagCompensationEnabled = true;
public static boolean blockEntityAcceleration = false;
public static boolean blockBreakingAcceleration = true;
public static boolean eatingAcceleration = true;
public static boolean potionEffectAcceleration = true;
public static boolean fluidAcceleration = true;
public static boolean pickupAcceleration = true;
public static boolean portalAcceleration = true;
public static boolean timeAcceleration = true;
public static boolean randomTickSpeedAcceleration = true;
private static void lagCompensation() {
lagCompensationEnabled = getBoolean(config, "settings.lag-compensation.enabled", lagCompensationEnabled, "Improves the player experience when TPS is low");
blockEntityAcceleration = getBoolean(config, "settings.lag-compensation.block-entity-acceleration", blockEntityAcceleration);
blockBreakingAcceleration = getBoolean(config, "settings.lag-compensation.block-breaking-acceleration", blockBreakingAcceleration);
eatingAcceleration = getBoolean(config, "settings.lag-compensation.eating-acceleration", eatingAcceleration);
potionEffectAcceleration = getBoolean(config, "settings.lag-compensation.potion-effect-acceleration", potionEffectAcceleration);
fluidAcceleration = getBoolean(config, "settings.lag-compensation.fluid-acceleration", fluidAcceleration);
pickupAcceleration = getBoolean(config, "settings.lag-compensation.pickup-acceleration", pickupAcceleration);
portalAcceleration = getBoolean(config, "settings.lag-compensation.portal-acceleration", portalAcceleration);
timeAcceleration = getBoolean(config, "settings.lag-compensation.time-acceleration", timeAcceleration);
randomTickSpeedAcceleration = getBoolean(config, "settings.lag-compensation.random-tick-speed-acceleration", randomTickSpeedAcceleration);
}
public static boolean noChatReportsEnabled = false;
public static boolean noChatReportsAddQueryData = true;
public static boolean noChatReportsConvertToGameMessage = true;
public static boolean noChatReportsDebugLog = false;
public static boolean noChatReportsDemandOnClient = false;
public static String noChatReportsDisconnectDemandOnClientMessage = "You do not have No Chat Reports, and this server is configured to require it on client!";
private static void noChatReports() {
noChatReportsEnabled = getBoolean(config, "settings.no-chat-reports.enabled", noChatReportsEnabled,
"Enables or disables the No Chat Reports feature");
noChatReportsAddQueryData = getBoolean(config, "settings.no-chat-reports.add-query-data", noChatReportsAddQueryData,
"Should server include extra query data to help clients know that your server is secure");
noChatReportsConvertToGameMessage = getBoolean(config, "settings.no-chat-reports.convert-to-game-message", noChatReportsConvertToGameMessage,
"Should the server convert all player messages to system messages");
noChatReportsDebugLog = getBoolean(config, "settings.no-chat-reports.debug-log", noChatReportsDebugLog);
noChatReportsDemandOnClient = getBoolean(config, "settings.no-chat-reports.demand-on-client", noChatReportsDemandOnClient,
"Should the server require No Chat Reports on the client side");
noChatReportsDisconnectDemandOnClientMessage = getString(config, "settings.no-chat-reports.disconnect-demand-on-client-message", noChatReportsDisconnectDemandOnClientMessage,
"Message to send to the client when they are disconnected for not having No Chat Reports");
}
public static boolean asyncPathfinding = true;
public static int asyncPathfindingMaxThreads = 2;
public static int asyncPathfindingKeepalive = 60;
private static void asyncPathfinding() {
asyncPathfinding = getBoolean(config, "settings.async-pathfinding.enable", asyncPathfinding);
asyncPathfindingMaxThreads = getInt(config, "settings.async-pathfinding.max-threads", asyncPathfindingMaxThreads);
asyncPathfindingKeepalive = getInt(config, "settings.async-pathfinding.keepalive", asyncPathfindingKeepalive);
if (asyncPathfindingMaxThreads < 0) {
asyncPathfindingMaxThreads = Math.max(Runtime.getRuntime().availableProcessors() + asyncPathfindingMaxThreads, 1);
} else if (asyncPathfindingMaxThreads == 0) {
asyncPathfindingMaxThreads = Math.max(Runtime.getRuntime().availableProcessors() / 4, 1);
}
if (!asyncPathfinding) {
asyncPathfindingMaxThreads = 0;
} else {
Bukkit.getLogger().log(Level.INFO, "Using " + asyncPathfindingMaxThreads + " threads for Async Pathfinding");
}
}
public static boolean multithreadedEnabled = true;
public static boolean multithreadedCompatModeEnabled = false;
public static int asyncEntityTrackerMaxThreads = 1;
public static int asyncEntityTrackerKeepalive = 60;
private static void multithreadedTracker() {
multithreadedEnabled = getBoolean(config, "settings.multithreaded-tracker.enable", multithreadedEnabled);
multithreadedCompatModeEnabled = getBoolean(config, "settings.multithreaded-tracker.compat-mode", multithreadedCompatModeEnabled);
asyncEntityTrackerMaxThreads = getInt(config, "settings.multithreaded-tracker.max-threads", asyncEntityTrackerMaxThreads);
asyncEntityTrackerKeepalive = getInt(config, "settings.multithreaded-tracker.keepalive", asyncEntityTrackerKeepalive);
if (asyncEntityTrackerMaxThreads < 0) {
asyncEntityTrackerMaxThreads = Math.max(Runtime.getRuntime().availableProcessors() + asyncEntityTrackerMaxThreads, 1);
} else if (asyncEntityTrackerMaxThreads == 0) {
asyncEntityTrackerMaxThreads = Math.max(Runtime.getRuntime().availableProcessors() / 4, 1);
}
if (!multithreadedEnabled) {
asyncEntityTrackerMaxThreads = 0;
} else {
Bukkit.getLogger().log(Level.INFO, "Using " + asyncEntityTrackerMaxThreads + " threads for Async Entity Tracker");
}
}
}

View File

@@ -0,0 +1,82 @@
package org.bxteam.divinemc;
import org.bukkit.World;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.configuration.file.YamlConfiguration;
import org.jspecify.annotations.NullMarked;
import java.util.List;
import java.util.Map;
import static org.bxteam.divinemc.DivineConfig.log;
@NullMarked
public final class DivineWorldConfig {
private final YamlConfiguration config;
private final String worldName;
private final World.Environment environment;
public DivineWorldConfig(String worldName, World.Environment environment) {
this.config = DivineConfig.config;
this.worldName = worldName;
this.environment = environment;
init();
}
public void init() {
log("-------- World Settings For [" + worldName + "] --------");
DivineConfig.readConfig(DivineWorldConfig.class, this);
}
private void set(String path, Object val) {
this.config.addDefault("world-settings.default." + path, val);
this.config.set("world-settings.default." + path, val);
if (this.config.get("world-settings." + worldName + "." + path) != null) {
this.config.addDefault("world-settings." + worldName + "." + path, val);
this.config.set("world-settings." + worldName + "." + path, val);
}
}
private ConfigurationSection getConfigurationSection(String path) {
ConfigurationSection section = this.config.getConfigurationSection("world-settings." + worldName + "." + path);
return section != null ? section : this.config.getConfigurationSection("world-settings.default." + path);
}
private String getString(String path, String def) {
this.config.addDefault("world-settings.default." + path, def);
return this.config.getString("world-settings." + worldName + "." + path, this.config.getString("world-settings.default." + path));
}
private boolean getBoolean(String path, boolean def) {
this.config.addDefault("world-settings.default." + path, def);
return this.config.getBoolean("world-settings." + worldName + "." + path, this.config.getBoolean("world-settings.default." + path));
}
private double getDouble(String path, double def) {
this.config.addDefault("world-settings.default." + path, def);
return this.config.getDouble("world-settings." + worldName + "." + path, this.config.getDouble("world-settings.default." + path));
}
private int getInt(String path, int def) {
this.config.addDefault("world-settings.default." + path, def);
return this.config.getInt("world-settings." + worldName + "." + path, this.config.getInt("world-settings.default." + path));
}
private <T> List<?> getList(String path, T def) {
this.config.addDefault("world-settings.default." + path, def);
return this.config.getList("world-settings." + worldName + "." + path, this.config.getList("world-settings.default." + path));
}
private Map<String, Object> getMap(String path, Map<String, Object> def) {
final Map<String, Object> fallback = this.getMap("world-settings.default." + path, def);
final Map<String, Object> value = this.getMap("world-settings." + worldName + "." + path, null);
return value.isEmpty() ? fallback : value;
}
public boolean snowballCanKnockback = true;
public boolean eggCanKnockback = true;
private void setSnowballAndEggKnockback() {
snowballCanKnockback = getBoolean("gameplay-mechanics.projectiles.snowball.knockback", snowballCanKnockback);
eggCanKnockback = getBoolean("gameplay-mechanics.projectiles.egg.knockback", eggCanKnockback);
}
}

View File

@@ -2,7 +2,6 @@ package org.bxteam.divinemc.command;
import net.minecraft.server.MinecraftServer;
import org.bukkit.command.Command;
import org.bukkit.craftbukkit.util.permissions.CraftDefaultPermissions;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.framework.qual.DefaultQualifier;
@@ -11,7 +10,7 @@ import java.util.Map;
@DefaultQualifier(NonNull.class)
public final class DivineCommands {
public static final String COMMAND_BASE_PERM = CraftDefaultPermissions.DIVINEMC_ROOT + ".command";
public static final String COMMAND_BASE_PERM = "divinemc.command";
private DivineCommands() {}

View File

@@ -8,7 +8,7 @@ import org.bukkit.craftbukkit.CraftServer;
import org.bukkit.permissions.PermissionDefault;
import org.bxteam.divinemc.command.DivineCommand;
import org.bxteam.divinemc.command.DivineSubCommandPermission;
import org.bxteam.divinemc.configuration.DivineConfig;
import org.bxteam.divinemc.DivineConfig;
import java.io.File;
@@ -37,7 +37,7 @@ public final class ReloadCommand extends DivineSubCommandPermission {
MinecraftServer server = ((CraftServer) sender.getServer()).getServer();
DivineConfig.init((File) server.options.valueOf("divinemc-settings"));
for (ServerLevel level : server.getAllLevels()) {
level.divinemcConfig.init();
level.divineConfig.init();
level.resetBreedingCooldowns();
}
server.server.reloadCount++;

View File

@@ -1,218 +0,0 @@
package org.bxteam.divinemc.configuration;
import com.google.common.base.Throwables;
import com.google.common.collect.ImmutableMap;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.state.BlockBehaviour;
import org.bukkit.Bukkit;
import org.bukkit.command.Command;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.configuration.InvalidConfigurationException;
import org.bukkit.configuration.file.YamlConfiguration;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
@SuppressWarnings("unused")
public class DivineConfig {
private static final String HEADER = "This is the main configuration file for DivineMC.\n"
+ "If you need help with the configuration or have any questions related to DivineMC,\n"
+ "join us in our Discord server.\n"
+ "\n"
+ "Discord: https://discord.gg/p7cxhw7E2M \n"
+ "Docs: https://bxteam.org/docs/divinemc \n"
+ "New builds: https://github.com/BX-Team/DivineMC/releases/latest";
private static File CONFIG_FILE;
public static YamlConfiguration config;
private static Map<String, Command> commands;
public static int version;
static boolean verbose;
public static void init(File configFile) {
CONFIG_FILE = configFile;
config = new YamlConfiguration();
try {
config.load(CONFIG_FILE);
} catch (IOException ignore) {
} catch (InvalidConfigurationException ex) {
Bukkit.getLogger().log(Level.SEVERE, "Could not load divinemc.yml, please correct your syntax errors", ex);
throw Throwables.propagate(ex);
}
config.options().header(HEADER);
config.options().copyDefaults(true);
verbose = getBoolean("verbose", false);
version = getInt("config-version", 4);
set("config-version", 4);
readConfig(DivineConfig.class, null);
Block.BLOCK_STATE_REGISTRY.forEach(BlockBehaviour.BlockStateBase::initCache);
}
protected static void log(String s) {
if (verbose) {
log(Level.INFO, s);
}
}
protected static void log(Level level, String s) {
Bukkit.getLogger().log(level, s);
}
static void readConfig(Class<?> clazz, Object instance) {
for (Method method : clazz.getDeclaredMethods()) {
if (Modifier.isPrivate(method.getModifiers())) {
if (method.getParameterTypes().length == 0 && method.getReturnType() == Void.TYPE) {
try {
method.setAccessible(true);
method.invoke(instance);
} catch (InvocationTargetException ex) {
throw Throwables.propagate(ex.getCause());
} catch (Exception ex) {
Bukkit.getLogger().log(Level.SEVERE, "Error invoking " + method, ex);
}
}
}
}
try {
config.save(CONFIG_FILE);
} catch (IOException ex) {
Bukkit.getLogger().log(Level.SEVERE, "Could not save " + CONFIG_FILE, ex);
}
}
private static void set(String path, Object val) {
config.addDefault(path, val);
config.set(path, val);
}
private static String getString(String path, String def) {
config.addDefault(path, def);
return config.getString(path, config.getString(path));
}
private static boolean getBoolean(String path, boolean def) {
config.addDefault(path, def);
return config.getBoolean(path, config.getBoolean(path));
}
private static double getDouble(String path, double def) {
config.addDefault(path, def);
return config.getDouble(path, config.getDouble(path));
}
private static int getInt(String path, int def) {
config.addDefault(path, def);
return config.getInt(path, config.getInt(path));
}
private static <T> List getList(String path, T def) {
config.addDefault(path, def);
return config.getList(path, config.getList(path));
}
static Map<String, Object> getMap(String path, Map<String, Object> def) {
if (def != null && config.getConfigurationSection(path) == null) {
config.addDefault(path, def);
return def;
}
return toMap(config.getConfigurationSection(path));
}
private static Map<String, Object> toMap(ConfigurationSection section) {
ImmutableMap.Builder<String, Object> builder = ImmutableMap.builder();
if (section != null) {
for (String key : section.getKeys(false)) {
Object obj = section.get(key);
if (obj != null) {
builder.put(key, obj instanceof ConfigurationSection val ? toMap(val) : obj);
}
}
}
return builder.build();
}
public static boolean noChatSign = true;
private static void chatMessageSignatures() {
noChatSign = getBoolean("settings.no-chat-sign", noChatSign);
}
public static boolean optimizedDragonRespawn = true;
public static boolean lagCompensationEnabled = false;
public static boolean lagCompensationEnableForWater = false;
public static boolean lagCompensationEnableForLava = false;
private static void optimizations() {
optimizedDragonRespawn = getBoolean("settings.optimizations.optimized-dragon-respawn", optimizedDragonRespawn);
lagCompensationEnabled = getBoolean("settings.optimizations.lag-compensation.enabled", lagCompensationEnabled);
lagCompensationEnableForWater = getBoolean("settings.optimizations.lag-compensation.enable-for-water", lagCompensationEnableForWater);
lagCompensationEnableForLava = getBoolean("settings.optimizations.lag-compensation.enable-for-lava", lagCompensationEnableForLava);
}
public static boolean disableNonEditableSignWarning = true;
public static boolean removeVanillaUsernameCheck = false;
public static boolean disableMovedWronglyThreshold = false;
public static boolean enableSecureSeed = false;
public static boolean asyncPlayerDataSaveEnabled = false;
private static void miscSettings() {
disableNonEditableSignWarning = getBoolean("settings.misc.disable-non-editable-sign-warning", disableNonEditableSignWarning);
removeVanillaUsernameCheck = getBoolean("settings.misc.remove-vanilla-username-check", removeVanillaUsernameCheck);
disableMovedWronglyThreshold = getBoolean("settings.misc.disable-moved-wrongly-threshold", disableMovedWronglyThreshold);
enableSecureSeed = getBoolean("settings.misc.enable-secure-seed", enableSecureSeed);
asyncPlayerDataSaveEnabled = getBoolean("settings.misc.experimental.async-player-data-save-enabled", asyncPlayerDataSaveEnabled);
}
public static int linearFlushFrequency = 5;
public static boolean throwOnUnknownExtension = false;
private static void linearSettings() {
linearFlushFrequency = getInt("settings.region-format.linear.flush-frequency", linearFlushFrequency);
throwOnUnknownExtension = getBoolean("settings.region-format.linear.throw-on-unknown-extension", throwOnUnknownExtension);
}
public static boolean asyncPathfinding = true;
public static int asyncPathfindingMaxThreads = 0;
public static int asyncPathfindingKeepalive = 60;
private static void asyncPathfinding() {
asyncPathfinding = getBoolean("settings.async-pathfinding.enable", asyncPathfinding);
asyncPathfindingMaxThreads = getInt("settings.async-pathfinding.max-threads", asyncPathfindingMaxThreads);
asyncPathfindingKeepalive = getInt("settings.async-pathfinding.keepalive", asyncPathfindingKeepalive);
if (asyncPathfindingMaxThreads < 0)
asyncPathfindingMaxThreads = Math.max(Runtime.getRuntime().availableProcessors() + asyncPathfindingMaxThreads, 1);
else if (asyncPathfindingMaxThreads == 0)
asyncPathfindingMaxThreads = Math.max(Runtime.getRuntime().availableProcessors() / 4, 1);
if (!asyncPathfinding)
asyncPathfindingMaxThreads = 0;
else
Bukkit.getLogger().log(Level.INFO, "Using " + asyncPathfindingMaxThreads + " threads for Async Pathfinding");
}
public static boolean multithreadedEnabled = false;
public static boolean multithreadedCompatModeEnabled = false;
public static int asyncEntityTrackerMaxThreads = 0;
public static int asyncEntityTrackerKeepalive = 60;
private static void multithreadedTracker() {
multithreadedEnabled = getBoolean("settings.multithreaded-tracker.enable", multithreadedEnabled);
multithreadedCompatModeEnabled = getBoolean("settings.multithreaded-tracker.compat-mode", multithreadedCompatModeEnabled);
asyncEntityTrackerMaxThreads = getInt("settings.multithreaded-tracker.max-threads", asyncEntityTrackerMaxThreads);
asyncEntityTrackerKeepalive = getInt("settings.multithreaded-tracker.keepalive", asyncEntityTrackerKeepalive);
if (asyncEntityTrackerMaxThreads < 0)
asyncEntityTrackerMaxThreads = Math.max(Runtime.getRuntime().availableProcessors() + asyncEntityTrackerMaxThreads, 1);
else if (asyncEntityTrackerMaxThreads == 0)
asyncEntityTrackerMaxThreads = Math.max(Runtime.getRuntime().availableProcessors() / 4, 1);
if (!multithreadedEnabled)
asyncEntityTrackerMaxThreads = 0;
else
Bukkit.getLogger().log(Level.INFO, "Using " + asyncEntityTrackerMaxThreads + " threads for Async Entity Tracker");
}
}

View File

@@ -1,121 +0,0 @@
package org.bxteam.divinemc.configuration;
import org.apache.commons.lang.BooleanUtils;
import org.bukkit.World;
import org.bukkit.configuration.ConfigurationSection;
import org.bxteam.divinemc.region.RegionFileFormat;
import java.util.List;
import java.util.Map;
import java.util.function.Predicate;
import java.util.logging.Level;
import static org.bxteam.divinemc.configuration.DivineConfig.log;
@SuppressWarnings("unused")
public class DivineWorldConfig {
private final String worldName;
private final World.Environment environment;
public DivineWorldConfig(String worldName, World.Environment environment) {
this.worldName = worldName;
this.environment = environment;
init();
}
public void init() {
log("-------- World Settings For [" + worldName + "] --------");
DivineConfig.readConfig(DivineWorldConfig.class, this);
}
private void set(String path, Object val) {
DivineConfig.config.addDefault("world-settings.default." + path, val);
DivineConfig.config.set("world-settings.default." + path, val);
if (DivineConfig.config.get("world-settings." + worldName + "." + path) != null) {
DivineConfig.config.addDefault("world-settings." + worldName + "." + path, val);
DivineConfig.config.set("world-settings." + worldName + "." + path, val);
}
}
private ConfigurationSection getConfigurationSection(String path) {
ConfigurationSection section = DivineConfig.config.getConfigurationSection("world-settings." + worldName + "." + path);
return section != null ? section : DivineConfig.config.getConfigurationSection("world-settings.default." + path);
}
private String getString(String path, String def) {
DivineConfig.config.addDefault("world-settings.default." + path, def);
return DivineConfig.config.getString("world-settings." + worldName + "." + path, DivineConfig.config.getString("world-settings.default." + path));
}
private boolean getBoolean(String path, boolean def) {
DivineConfig.config.addDefault("world-settings.default." + path, def);
return DivineConfig.config.getBoolean("world-settings." + worldName + "." + path, DivineConfig.config.getBoolean("world-settings.default." + path));
}
private boolean getBoolean(String path, Predicate<Boolean> predicate) {
String val = getString(path, "default").toLowerCase();
Boolean bool = BooleanUtils.toBooleanObject(val, "true", "false", "default");
return predicate.test(bool);
}
private double getDouble(String path, double def) {
DivineConfig.config.addDefault("world-settings.default." + path, def);
return DivineConfig.config.getDouble("world-settings." + worldName + "." + path, DivineConfig.config.getDouble("world-settings.default." + path));
}
private int getInt(String path, int def) {
DivineConfig.config.addDefault("world-settings.default." + path, def);
return DivineConfig.config.getInt("world-settings." + worldName + "." + path, DivineConfig.config.getInt("world-settings.default." + path));
}
private <T> List<?> getList(String path, T def) {
DivineConfig.config.addDefault("world-settings.default." + path, def);
return DivineConfig.config.getList("world-settings." + worldName + "." + path, DivineConfig.config.getList("world-settings.default." + path));
}
private Map<String, Object> getMap(String path, Map<String, Object> def) {
final Map<String, Object> fallback = DivineConfig.getMap("world-settings.default." + path, def);
final Map<String, Object> value = DivineConfig.getMap("world-settings." + worldName + "." + path, null);
return value.isEmpty() ? fallback : value;
}
public boolean despawnShulkerBulletsOnOwnerDeath = true;
private void despawnShulkerBulletsOnOwnerDeath() {
despawnShulkerBulletsOnOwnerDeath = getBoolean("gameplay-mechanics.mob.shulker.despawn-bullets-on-player-death", despawnShulkerBulletsOnOwnerDeath);
}
public boolean saveFireworks = false;
private void projectiles() {
saveFireworks = getBoolean("gameplay-mechanics.should-save-fireworks", saveFireworks);
}
public boolean suppressErrorsFromDirtyAttributes = true;
private void suppressErrorsFromDirtyAttributes() {
suppressErrorsFromDirtyAttributes = getBoolean("suppress-errors-from-dirty-attributes", suppressErrorsFromDirtyAttributes);
}
public boolean snowballCanKnockback = true;
public boolean eggCanKnockback = true;
private void setSnowballAndEggKnockback() {
snowballCanKnockback = getBoolean("gameplay-mechanics.projectiles.snowball.knockback", snowballCanKnockback);
eggCanKnockback = getBoolean("gameplay-mechanics.projectiles.egg.knockback", eggCanKnockback);
}
public RegionFileFormat regionFormatName = RegionFileFormat.MCA;
public int linearCompressionLevel = 1;
private void regionFormatSettings() {
regionFormatName = RegionFileFormat.fromExtension(getString("region-format.format", regionFormatName.name()));
if (regionFormatName.equals(RegionFileFormat.UNKNOWN)) {
log(Level.SEVERE, "Unknown region file type!");
log(Level.SEVERE, "Falling back to ANVIL region file format.");
regionFormatName = RegionFileFormat.MCA;
}
linearCompressionLevel = getInt("region-format.linear.compression-level", linearCompressionLevel);
if (linearCompressionLevel > 23 || linearCompressionLevel < 1) {
log(Level.SEVERE, "Linear region compression level should be between 1 and 22 in config: " + linearCompressionLevel);
log(Level.SEVERE, "Falling back to compression level 1.");
linearCompressionLevel = 1;
}
}
}

View File

@@ -0,0 +1,13 @@
package org.bxteam.divinemc.dfc.common;
import net.minecraft.world.level.levelgen.NoiseRouterData;
public interface IDensityFunctionsCaveScaler {
static double invokeScaleCaves(double value) {
return NoiseRouterData.QuantizedSpaghettiRarity.getSphaghettiRarity2D(value);
}
static double invokeScaleTunnels(double value) {
return NoiseRouterData.QuantizedSpaghettiRarity.getSpaghettiRarity3D(value);
}
}

View File

@@ -0,0 +1,22 @@
package org.bxteam.divinemc.dfc.common.ast;
import org.bxteam.divinemc.dfc.common.gen.BytecodeGen;
import org.objectweb.asm.commons.InstructionAdapter;
public interface AstNode {
double evalSingle(int var1, int var2, int var3, EvalType var4);
void evalMulti(double[] var1, int[] var2, int[] var3, int[] var4, EvalType var5);
AstNode[] getChildren();
AstNode transform(AstTransformer var1);
void doBytecodeGenSingle(BytecodeGen.Context var1, InstructionAdapter var2, BytecodeGen.Context.LocalVarConsumer var3);
void doBytecodeGenMulti(BytecodeGen.Context var1, InstructionAdapter var2, BytecodeGen.Context.LocalVarConsumer var3);
boolean relaxedEquals(AstNode var1);
int relaxedHashCode();
}

View File

@@ -0,0 +1,5 @@
package org.bxteam.divinemc.dfc.common.ast;
public interface AstTransformer {
AstNode transform(AstNode var1);
}

View File

@@ -0,0 +1,25 @@
package org.bxteam.divinemc.dfc.common.ast;
import org.bxteam.divinemc.dfc.common.vif.EachApplierVanillaInterface;
import net.minecraft.world.level.levelgen.DensityFunction;
import net.minecraft.world.level.levelgen.NoiseChunk;
public enum EvalType {
NORMAL,
INTERPOLATION;
private EvalType() {
}
public static EvalType from(DensityFunction.FunctionContext pos) {
return pos instanceof NoiseChunk ? INTERPOLATION : NORMAL;
}
public static EvalType from(DensityFunction.ContextProvider applier) {
if (applier instanceof EachApplierVanillaInterface vif) {
return vif.getType();
} else {
return applier instanceof NoiseChunk ? INTERPOLATION : NORMAL;
}
}
}

View File

@@ -0,0 +1,106 @@
package org.bxteam.divinemc.dfc.common.ast;
import org.bxteam.divinemc.dfc.common.ast.binary.AddNode;
import org.bxteam.divinemc.dfc.common.ast.binary.MaxNode;
import org.bxteam.divinemc.dfc.common.ast.binary.MaxShortNode;
import org.bxteam.divinemc.dfc.common.ast.binary.MinNode;
import org.bxteam.divinemc.dfc.common.ast.binary.MinShortNode;
import org.bxteam.divinemc.dfc.common.ast.binary.MulNode;
import org.bxteam.divinemc.dfc.common.ast.misc.CacheLikeNode;
import org.bxteam.divinemc.dfc.common.ast.misc.ConstantNode;
import org.bxteam.divinemc.dfc.common.ast.misc.DelegateNode;
import org.bxteam.divinemc.dfc.common.ast.misc.RangeChoiceNode;
import org.bxteam.divinemc.dfc.common.ast.misc.YClampedGradientNode;
import org.bxteam.divinemc.dfc.common.ast.noise.DFTNoiseNode;
import org.bxteam.divinemc.dfc.common.ast.noise.DFTShiftANode;
import org.bxteam.divinemc.dfc.common.ast.noise.DFTShiftBNode;
import org.bxteam.divinemc.dfc.common.ast.noise.DFTShiftNode;
import org.bxteam.divinemc.dfc.common.ast.noise.DFTWeirdScaledSamplerNode;
import org.bxteam.divinemc.dfc.common.ast.noise.ShiftedNoiseNode;
import org.bxteam.divinemc.dfc.common.ast.spline.SplineAstNode;
import org.bxteam.divinemc.dfc.common.ast.unary.AbsNode;
import org.bxteam.divinemc.dfc.common.ast.unary.CubeNode;
import org.bxteam.divinemc.dfc.common.ast.unary.NegMulNode;
import org.bxteam.divinemc.dfc.common.ast.unary.SquareNode;
import org.bxteam.divinemc.dfc.common.ast.unary.SqueezeNode;
import org.bxteam.divinemc.dfc.common.ducks.IEqualityOverriding;
import org.bxteam.divinemc.dfc.common.ducks.IFastCacheLike;
import org.bxteam.divinemc.dfc.common.vif.AstVanillaInterface;
import java.util.Objects;
import net.minecraft.world.level.levelgen.DensityFunction;
import net.minecraft.world.level.levelgen.DensityFunctions;
import net.minecraft.world.level.levelgen.NoiseChunk;
import org.jetbrains.annotations.NotNull;
public class McToAst {
public McToAst() {
}
public static AstNode toAst(DensityFunction df) {
Objects.requireNonNull(df);
return switch (df) {
case AstVanillaInterface f -> f.getAstNode();
case NoiseChunk.BlendAlpha f -> new ConstantNode(1.0);
case NoiseChunk.BlendOffset f -> new ConstantNode(0.0);
case DensityFunctions.BlendAlpha f -> new ConstantNode(1.0);
case DensityFunctions.BlendOffset f -> new ConstantNode(0.0);
case DensityFunctions.TwoArgumentSimpleFunction f -> switch (f.type()) {
case ADD -> new AddNode(toAst(f.argument1()), toAst(f.argument2()));
case MUL -> new MulNode(toAst(f.argument1()), toAst(f.argument2()));
case MIN -> {
double rightMin = f.argument2().minValue();
if (f.argument1().minValue() < rightMin) {
yield new MinShortNode(toAst(f.argument1()), toAst(f.argument2()), rightMin);
} else {
yield new MinNode(toAst(f.argument1()), toAst(f.argument2()));
}
}
case MAX -> {
double rightMax = f.argument2().maxValue();
if (f.argument1().maxValue() > rightMax) {
yield new MaxShortNode(toAst(f.argument1()), toAst(f.argument2()), rightMax);
} else {
yield new MaxNode(toAst(f.argument1()), toAst(f.argument2()));
}
}
};
case DensityFunctions.BlendDensity f -> toAst(f.input());
case DensityFunctions.Clamp f -> new MaxNode(new ConstantNode(f.minValue()), new MinNode(new ConstantNode(f.maxValue()), toAst(f.input())));
case DensityFunctions.Constant f -> new ConstantNode(f.value());
case DensityFunctions.HolderHolder f -> toAst(f.function().value());
case DensityFunctions.Mapped f -> switch (f.type()) {
case ABS -> new AbsNode(toAst(f.input()));
case SQUARE -> new SquareNode(toAst(f.input()));
case CUBE -> new CubeNode(toAst(f.input()));
case HALF_NEGATIVE -> new NegMulNode(toAst(f.input()), 0.5);
case QUARTER_NEGATIVE -> new NegMulNode(toAst(f.input()), 0.25);
case SQUEEZE -> new SqueezeNode(toAst(f.input()));
};
case DensityFunctions.RangeChoice f -> new RangeChoiceNode(toAst(f.input()), f.minInclusive(), f.maxExclusive(), toAst(f.whenInRange()), toAst(f.whenOutOfRange()));
case DensityFunctions.Marker f -> {
DensityFunctions.Marker wrapping = new DensityFunctions.Marker(f.type(), new AstVanillaInterface(toAst(f.wrapped()), null));
((IEqualityOverriding) (Object) wrapping).c2me$overrideEquality(wrapping);
yield new DelegateNode(wrapping);
}
case IFastCacheLike f -> new CacheLikeNode(f, toAst(f.c2me$getDelegate()));
case DensityFunctions.ShiftedNoise f -> new ShiftedNoiseNode(toAst(f.shiftX()), toAst(f.shiftY()), toAst(f.shiftZ()), f.xzScale(), f.yScale(), f.noise());
case DensityFunctions.Noise f -> new DFTNoiseNode(f.noise(), f.xzScale(), f.yScale());
case DensityFunctions.Shift f -> new DFTShiftNode(f.offsetNoise());
case DensityFunctions.ShiftA f -> new DFTShiftANode(f.offsetNoise());
case DensityFunctions.ShiftB f -> new DFTShiftBNode(f.offsetNoise());
case DensityFunctions.YClampedGradient f -> new YClampedGradientNode(f.fromY(), f.toY(), f.fromValue(), f.toValue());
case DensityFunctions.WeirdScaledSampler f -> new DFTWeirdScaledSamplerNode(toAst(f.input()), f.noise(), f.rarityValueMapper());
case DensityFunctions.Spline f -> new SplineAstNode(f.spline());
default -> {
// delegateStatistics.computeIfAbsent(df.getClass(), unused -> new LongAdder()).increment();;
yield new DelegateNode(df);
}
};
}
public static @NotNull DensityFunction wrapVanilla(DensityFunction densityFunction) {
return new AstVanillaInterface(toAst(densityFunction), densityFunction);
}
}

View File

@@ -0,0 +1,106 @@
package org.bxteam.divinemc.dfc.common.ast.binary;
import org.bxteam.divinemc.dfc.common.ast.AstNode;
import org.bxteam.divinemc.dfc.common.ast.AstTransformer;
import org.bxteam.divinemc.dfc.common.gen.BytecodeGen.Context;
import org.bxteam.divinemc.dfc.common.util.ArrayCache;
import java.util.Objects;
import org.objectweb.asm.Type;
import org.objectweb.asm.commons.InstructionAdapter;
public abstract class AbstractBinaryNode implements AstNode {
protected final AstNode left;
protected final AstNode right;
public AbstractBinaryNode(AstNode left, AstNode right) {
this.left = (AstNode)Objects.requireNonNull(left);
this.right = (AstNode)Objects.requireNonNull(right);
}
public AstNode[] getChildren() {
return new AstNode[]{this.left, this.right};
}
public boolean equals(Object o) {
if (this == o) {
return true;
} else if (o != null && this.getClass() == o.getClass()) {
AbstractBinaryNode that = (AbstractBinaryNode)o;
return Objects.equals(this.left, that.left) && Objects.equals(this.right, that.right);
} else {
return false;
}
}
public int hashCode() {
int result = 1;
result = 31 * result + this.getClass().hashCode();
result = 31 * result + this.left.hashCode();
result = 31 * result + this.right.hashCode();
return result;
}
public boolean relaxedEquals(AstNode o) {
if (this == o) {
return true;
} else if (o != null && this.getClass() == o.getClass()) {
AbstractBinaryNode that = (AbstractBinaryNode)o;
return this.left.relaxedEquals(that.left) && this.right.relaxedEquals(that.right);
} else {
return false;
}
}
public int relaxedHashCode() {
int result = 1;
result = 31 * result + this.getClass().hashCode();
result = 31 * result + this.left.relaxedHashCode();
result = 31 * result + this.right.relaxedHashCode();
return result;
}
protected abstract AstNode newInstance(AstNode var1, AstNode var2);
public AstNode transform(AstTransformer transformer) {
AstNode left = this.left.transform(transformer);
AstNode right = this.right.transform(transformer);
return left == this.left && right == this.right ? transformer.transform(this) : transformer.transform(this.newInstance(left, right));
}
public void doBytecodeGenSingle(Context context, InstructionAdapter m, Context.LocalVarConsumer localVarConsumer) {
String leftMethod = context.newSingleMethod(this.left);
String rightMethod = context.newSingleMethod(this.right);
context.callDelegateSingle(m, leftMethod);
context.callDelegateSingle(m, rightMethod);
}
public void doBytecodeGenMulti(Context context, InstructionAdapter m, Context.LocalVarConsumer localVarConsumer) {
String leftMethod = context.newMultiMethod(this.left);
String rightMethod = context.newMultiMethod(this.right);
int res1 = localVarConsumer.createLocalVariable("res1", Type.getDescriptor(double[].class));
m.load(6, InstructionAdapter.OBJECT_TYPE);
m.load(1, InstructionAdapter.OBJECT_TYPE);
m.arraylength();
m.iconst(0);
m.invokevirtual(Type.getInternalName(ArrayCache.class), "getDoubleArray", Type.getMethodDescriptor(Type.getType(double[].class), new Type[]{Type.INT_TYPE, Type.BOOLEAN_TYPE}), false);
m.store(res1, InstructionAdapter.OBJECT_TYPE);
context.callDelegateMulti(m, leftMethod);
m.load(0, InstructionAdapter.OBJECT_TYPE);
m.load(res1, InstructionAdapter.OBJECT_TYPE);
m.load(2, InstructionAdapter.OBJECT_TYPE);
m.load(3, InstructionAdapter.OBJECT_TYPE);
m.load(4, InstructionAdapter.OBJECT_TYPE);
m.load(5, InstructionAdapter.OBJECT_TYPE);
m.load(6, InstructionAdapter.OBJECT_TYPE);
m.invokevirtual(context.className, rightMethod, Context.MULTI_DESC, false);
context.doCountedLoop(m, localVarConsumer, (idx) -> {
this.bytecodeGenMultiBody(m, idx, res1);
});
m.load(6, InstructionAdapter.OBJECT_TYPE);
m.load(res1, InstructionAdapter.OBJECT_TYPE);
m.invokevirtual(Type.getInternalName(ArrayCache.class), "recycle", Type.getMethodDescriptor(Type.VOID_TYPE, new Type[]{Type.getType(double[].class)}), false);
m.areturn(Type.VOID_TYPE);
}
protected abstract void bytecodeGenMultiBody(InstructionAdapter var1, int var2, int var3);
}

View File

@@ -0,0 +1,50 @@
package org.bxteam.divinemc.dfc.common.ast.binary;
import org.bxteam.divinemc.dfc.common.ast.AstNode;
import org.bxteam.divinemc.dfc.common.ast.EvalType;
import org.bxteam.divinemc.dfc.common.gen.BytecodeGen;
import org.objectweb.asm.Type;
import org.objectweb.asm.commons.InstructionAdapter;
public class AddNode extends AbstractBinaryNode {
public AddNode(AstNode left, AstNode right) {
super(left, right);
}
protected AstNode newInstance(AstNode left, AstNode right) {
return new AddNode(left, right);
}
public double evalSingle(int x, int y, int z, EvalType type) {
return this.left.evalSingle(x, y, z, type) + this.right.evalSingle(x, y, z, type);
}
public void evalMulti(double[] res, int[] x, int[] y, int[] z, EvalType type) {
double[] res1 = new double[res.length];
this.left.evalMulti(res, x, y, z, type);
this.right.evalMulti(res1, x, y, z, type);
for(int i = 0; i < res1.length; ++i) {
res[i] += res1[i];
}
}
public void doBytecodeGenSingle(BytecodeGen.Context context, InstructionAdapter m, BytecodeGen.Context.LocalVarConsumer localVarConsumer) {
super.doBytecodeGenSingle(context, m, localVarConsumer);
m.add(Type.DOUBLE_TYPE);
m.areturn(Type.DOUBLE_TYPE);
}
protected void bytecodeGenMultiBody(InstructionAdapter m, int idx, int res1) {
m.load(1, InstructionAdapter.OBJECT_TYPE);
m.load(idx, Type.INT_TYPE);
m.dup2();
m.aload(Type.DOUBLE_TYPE);
m.load(res1, InstructionAdapter.OBJECT_TYPE);
m.load(idx, Type.INT_TYPE);
m.aload(Type.DOUBLE_TYPE);
m.add(Type.DOUBLE_TYPE);
m.astore(Type.DOUBLE_TYPE);
}
}

View File

@@ -0,0 +1,50 @@
package org.bxteam.divinemc.dfc.common.ast.binary;
import org.bxteam.divinemc.dfc.common.ast.AstNode;
import org.bxteam.divinemc.dfc.common.ast.EvalType;
import org.bxteam.divinemc.dfc.common.gen.BytecodeGen;
import org.objectweb.asm.Type;
import org.objectweb.asm.commons.InstructionAdapter;
public class MaxNode extends AbstractBinaryNode {
public MaxNode(AstNode left, AstNode right) {
super(left, right);
}
protected AstNode newInstance(AstNode left, AstNode right) {
return new MaxNode(left, right);
}
public double evalSingle(int x, int y, int z, EvalType type) {
return Math.max(this.left.evalSingle(x, y, z, type), this.right.evalSingle(x, y, z, type));
}
public void evalMulti(double[] res, int[] x, int[] y, int[] z, EvalType type) {
double[] res1 = new double[res.length];
this.left.evalMulti(res, x, y, z, type);
this.right.evalMulti(res1, x, y, z, type);
for(int i = 0; i < res1.length; ++i) {
res[i] = Math.max(res[i], res1[i]);
}
}
public void doBytecodeGenSingle(BytecodeGen.Context context, InstructionAdapter m, BytecodeGen.Context.LocalVarConsumer localVarConsumer) {
super.doBytecodeGenSingle(context, m, localVarConsumer);
m.invokestatic(Type.getInternalName(Math.class), "max", Type.getMethodDescriptor(Type.DOUBLE_TYPE, new Type[]{Type.DOUBLE_TYPE, Type.DOUBLE_TYPE}), false);
m.areturn(Type.DOUBLE_TYPE);
}
protected void bytecodeGenMultiBody(InstructionAdapter m, int idx, int res1) {
m.load(1, InstructionAdapter.OBJECT_TYPE);
m.load(idx, Type.INT_TYPE);
m.dup2();
m.aload(Type.DOUBLE_TYPE);
m.load(res1, InstructionAdapter.OBJECT_TYPE);
m.load(idx, Type.INT_TYPE);
m.aload(Type.DOUBLE_TYPE);
m.invokestatic(Type.getInternalName(Math.class), "max", Type.getMethodDescriptor(Type.DOUBLE_TYPE, new Type[]{Type.DOUBLE_TYPE, Type.DOUBLE_TYPE}), false);
m.astore(Type.DOUBLE_TYPE);
}
}

View File

@@ -0,0 +1,92 @@
package org.bxteam.divinemc.dfc.common.ast.binary;
import org.bxteam.divinemc.dfc.common.ast.AstNode;
import org.bxteam.divinemc.dfc.common.ast.EvalType;
import org.bxteam.divinemc.dfc.common.gen.BytecodeGen.Context;
import org.objectweb.asm.Label;
import org.objectweb.asm.Type;
import org.objectweb.asm.commons.InstructionAdapter;
public class MaxShortNode extends AbstractBinaryNode {
private final double rightMax;
public MaxShortNode(AstNode left, AstNode right, double rightMax) {
super(left, right);
this.rightMax = rightMax;
}
protected AstNode newInstance(AstNode left, AstNode right) {
return new MaxShortNode(left, right, this.rightMax);
}
public double evalSingle(int x, int y, int z, EvalType type) {
double evaled = this.left.evalSingle(x, y, z, type);
return evaled >= this.rightMax ? evaled : Math.max(evaled, this.right.evalSingle(x, y, z, type));
}
public void evalMulti(double[] res, int[] x, int[] y, int[] z, EvalType type) {
this.left.evalMulti(res, x, y, z, type);
for(int i = 0; i < res.length; ++i) {
res[i] = res[i] >= this.rightMax ? res[i] : Math.max(res[i], this.right.evalSingle(x[i], y[i], z[i], type));
}
}
public void doBytecodeGenSingle(Context context, InstructionAdapter m, Context.LocalVarConsumer localVarConsumer) {
String leftMethod = context.newSingleMethod(this.left);
String rightMethod = context.newSingleMethod(this.right);
Label minLabel = new Label();
context.callDelegateSingle(m, leftMethod);
m.dup2();
m.dconst(this.rightMax);
m.cmpl(Type.DOUBLE_TYPE);
m.iflt(minLabel);
m.areturn(Type.DOUBLE_TYPE);
m.visitLabel(minLabel);
context.callDelegateSingle(m, rightMethod);
m.invokestatic(Type.getInternalName(Math.class), "max", Type.getMethodDescriptor(Type.DOUBLE_TYPE, new Type[]{Type.DOUBLE_TYPE, Type.DOUBLE_TYPE}), false);
m.areturn(Type.DOUBLE_TYPE);
}
public void doBytecodeGenMulti(Context context, InstructionAdapter m, Context.LocalVarConsumer localVarConsumer) {
String leftMethod = context.newMultiMethod(this.left);
String rightMethodSingle = context.newSingleMethod(this.right);
context.callDelegateMulti(m, leftMethod);
context.doCountedLoop(m, localVarConsumer, (idx) -> {
Label minLabel = new Label();
Label end = new Label();
m.load(1, InstructionAdapter.OBJECT_TYPE);
m.load(idx, Type.INT_TYPE);
m.load(1, InstructionAdapter.OBJECT_TYPE);
m.load(idx, Type.INT_TYPE);
m.aload(Type.DOUBLE_TYPE);
m.dup2();
m.dconst(this.rightMax);
m.cmpl(Type.DOUBLE_TYPE);
m.iflt(minLabel);
m.goTo(end);
m.visitLabel(minLabel);
m.load(0, InstructionAdapter.OBJECT_TYPE);
m.load(2, InstructionAdapter.OBJECT_TYPE);
m.load(idx, Type.INT_TYPE);
m.aload(Type.INT_TYPE);
m.load(3, InstructionAdapter.OBJECT_TYPE);
m.load(idx, Type.INT_TYPE);
m.aload(Type.INT_TYPE);
m.load(4, InstructionAdapter.OBJECT_TYPE);
m.load(idx, Type.INT_TYPE);
m.aload(Type.INT_TYPE);
m.load(5, InstructionAdapter.OBJECT_TYPE);
m.invokevirtual(context.className, rightMethodSingle, Context.SINGLE_DESC, false);
m.invokestatic(Type.getInternalName(Math.class), "max", Type.getMethodDescriptor(Type.DOUBLE_TYPE, new Type[]{Type.DOUBLE_TYPE, Type.DOUBLE_TYPE}), false);
m.visitLabel(end);
m.astore(Type.DOUBLE_TYPE);
});
m.areturn(Type.VOID_TYPE);
}
protected void bytecodeGenMultiBody(InstructionAdapter m, int idx, int res1) {
throw new UnsupportedOperationException();
}
}

View File

@@ -0,0 +1,50 @@
package org.bxteam.divinemc.dfc.common.ast.binary;
import org.bxteam.divinemc.dfc.common.ast.AstNode;
import org.bxteam.divinemc.dfc.common.ast.EvalType;
import org.bxteam.divinemc.dfc.common.gen.BytecodeGen;
import org.objectweb.asm.Type;
import org.objectweb.asm.commons.InstructionAdapter;
public class MinNode extends AbstractBinaryNode {
public MinNode(AstNode left, AstNode right) {
super(left, right);
}
protected AstNode newInstance(AstNode left, AstNode right) {
return new MinNode(left, right);
}
public double evalSingle(int x, int y, int z, EvalType type) {
return Math.min(this.left.evalSingle(x, y, z, type), this.right.evalSingle(x, y, z, type));
}
public void evalMulti(double[] res, int[] x, int[] y, int[] z, EvalType type) {
double[] res1 = new double[res.length];
this.left.evalMulti(res, x, y, z, type);
this.right.evalMulti(res1, x, y, z, type);
for(int i = 0; i < res1.length; ++i) {
res[i] = Math.min(res[i], res1[i]);
}
}
public void doBytecodeGenSingle(BytecodeGen.Context context, InstructionAdapter m, BytecodeGen.Context.LocalVarConsumer localVarConsumer) {
super.doBytecodeGenSingle(context, m, localVarConsumer);
m.invokestatic(Type.getInternalName(Math.class), "min", Type.getMethodDescriptor(Type.DOUBLE_TYPE, new Type[]{Type.DOUBLE_TYPE, Type.DOUBLE_TYPE}), false);
m.areturn(Type.DOUBLE_TYPE);
}
protected void bytecodeGenMultiBody(InstructionAdapter m, int idx, int res1) {
m.load(1, InstructionAdapter.OBJECT_TYPE);
m.load(idx, Type.INT_TYPE);
m.dup2();
m.aload(Type.DOUBLE_TYPE);
m.load(res1, InstructionAdapter.OBJECT_TYPE);
m.load(idx, Type.INT_TYPE);
m.aload(Type.DOUBLE_TYPE);
m.invokestatic(Type.getInternalName(Math.class), "min", Type.getMethodDescriptor(Type.DOUBLE_TYPE, new Type[]{Type.DOUBLE_TYPE, Type.DOUBLE_TYPE}), false);
m.astore(Type.DOUBLE_TYPE);
}
}

View File

@@ -0,0 +1,92 @@
package org.bxteam.divinemc.dfc.common.ast.binary;
import org.bxteam.divinemc.dfc.common.ast.AstNode;
import org.bxteam.divinemc.dfc.common.ast.EvalType;
import org.bxteam.divinemc.dfc.common.gen.BytecodeGen.Context;
import org.objectweb.asm.Label;
import org.objectweb.asm.Type;
import org.objectweb.asm.commons.InstructionAdapter;
public class MinShortNode extends AbstractBinaryNode {
private final double rightMin;
public MinShortNode(AstNode left, AstNode right, double rightMin) {
super(left, right);
this.rightMin = rightMin;
}
protected AstNode newInstance(AstNode left, AstNode right) {
return new MinShortNode(left, right, this.rightMin);
}
public double evalSingle(int x, int y, int z, EvalType type) {
double evaled = this.left.evalSingle(x, y, z, type);
return evaled <= this.rightMin ? evaled : Math.min(evaled, this.right.evalSingle(x, y, z, type));
}
public void evalMulti(double[] res, int[] x, int[] y, int[] z, EvalType type) {
this.left.evalMulti(res, x, y, z, type);
for(int i = 0; i < res.length; ++i) {
res[i] = res[i] <= this.rightMin ? res[i] : Math.min(res[i], this.right.evalSingle(x[i], y[i], z[i], type));
}
}
public void doBytecodeGenSingle(Context context, InstructionAdapter m, Context.LocalVarConsumer localVarConsumer) {
String leftMethod = context.newSingleMethod(this.left);
String rightMethod = context.newSingleMethod(this.right);
Label minLabel = new Label();
context.callDelegateSingle(m, leftMethod);
m.dup2();
m.dconst(this.rightMin);
m.cmpg(Type.DOUBLE_TYPE);
m.ifgt(minLabel);
m.areturn(Type.DOUBLE_TYPE);
m.visitLabel(minLabel);
context.callDelegateSingle(m, rightMethod);
m.invokestatic(Type.getInternalName(Math.class), "min", Type.getMethodDescriptor(Type.DOUBLE_TYPE, new Type[]{Type.DOUBLE_TYPE, Type.DOUBLE_TYPE}), false);
m.areturn(Type.DOUBLE_TYPE);
}
public void doBytecodeGenMulti(Context context, InstructionAdapter m, Context.LocalVarConsumer localVarConsumer) {
String leftMethod = context.newMultiMethod(this.left);
String rightMethodSingle = context.newSingleMethod(this.right);
context.callDelegateMulti(m, leftMethod);
context.doCountedLoop(m, localVarConsumer, (idx) -> {
Label minLabel = new Label();
Label end = new Label();
m.load(1, InstructionAdapter.OBJECT_TYPE);
m.load(idx, Type.INT_TYPE);
m.load(1, InstructionAdapter.OBJECT_TYPE);
m.load(idx, Type.INT_TYPE);
m.aload(Type.DOUBLE_TYPE);
m.dup2();
m.dconst(this.rightMin);
m.cmpg(Type.DOUBLE_TYPE);
m.ifgt(minLabel);
m.goTo(end);
m.visitLabel(minLabel);
m.load(0, InstructionAdapter.OBJECT_TYPE);
m.load(2, InstructionAdapter.OBJECT_TYPE);
m.load(idx, Type.INT_TYPE);
m.aload(Type.INT_TYPE);
m.load(3, InstructionAdapter.OBJECT_TYPE);
m.load(idx, Type.INT_TYPE);
m.aload(Type.INT_TYPE);
m.load(4, InstructionAdapter.OBJECT_TYPE);
m.load(idx, Type.INT_TYPE);
m.aload(Type.INT_TYPE);
m.load(5, InstructionAdapter.OBJECT_TYPE);
m.invokevirtual(context.className, rightMethodSingle, Context.SINGLE_DESC, false);
m.invokestatic(Type.getInternalName(Math.class), "min", Type.getMethodDescriptor(Type.DOUBLE_TYPE, new Type[]{Type.DOUBLE_TYPE, Type.DOUBLE_TYPE}), false);
m.visitLabel(end);
m.astore(Type.DOUBLE_TYPE);
});
m.areturn(Type.VOID_TYPE);
}
protected void bytecodeGenMultiBody(InstructionAdapter m, int idx, int res1) {
throw new UnsupportedOperationException();
}
}

View File

@@ -0,0 +1,92 @@
package org.bxteam.divinemc.dfc.common.ast.binary;
import org.bxteam.divinemc.dfc.common.ast.AstNode;
import org.bxteam.divinemc.dfc.common.ast.EvalType;
import org.bxteam.divinemc.dfc.common.gen.BytecodeGen.Context;
import org.objectweb.asm.Label;
import org.objectweb.asm.Type;
import org.objectweb.asm.commons.InstructionAdapter;
public class MulNode extends AbstractBinaryNode {
public MulNode(AstNode left, AstNode right) {
super(left, right);
}
protected AstNode newInstance(AstNode left, AstNode right) {
return new MulNode(left, right);
}
public double evalSingle(int x, int y, int z, EvalType type) {
double evaled = this.left.evalSingle(x, y, z, type);
return evaled == 0.0 ? 0.0 : evaled * this.right.evalSingle(x, y, z, type);
}
public void evalMulti(double[] res, int[] x, int[] y, int[] z, EvalType type) {
this.left.evalMulti(res, x, y, z, type);
for(int i = 0; i < res.length; ++i) {
res[i] = res[i] == 0.0 ? 0.0 : res[i] * this.right.evalSingle(x[i], y[i], z[i], type);
}
}
public void doBytecodeGenSingle(Context context, InstructionAdapter m, Context.LocalVarConsumer localVarConsumer) {
String leftMethod = context.newSingleMethod(this.left);
String rightMethod = context.newSingleMethod(this.right);
Label notZero = new Label();
context.callDelegateSingle(m, leftMethod);
m.dup2();
m.dconst(0.0);
m.cmpl(Type.DOUBLE_TYPE);
m.ifne(notZero);
m.dconst(0.0);
m.areturn(Type.DOUBLE_TYPE);
m.visitLabel(notZero);
context.callDelegateSingle(m, rightMethod);
m.mul(Type.DOUBLE_TYPE);
m.areturn(Type.DOUBLE_TYPE);
}
public void doBytecodeGenMulti(Context context, InstructionAdapter m, Context.LocalVarConsumer localVarConsumer) {
String leftMethod = context.newMultiMethod(this.left);
String rightMethodSingle = context.newSingleMethod(this.right);
context.callDelegateMulti(m, leftMethod);
context.doCountedLoop(m, localVarConsumer, (idx) -> {
Label minLabel = new Label();
Label end = new Label();
m.load(1, InstructionAdapter.OBJECT_TYPE);
m.load(idx, Type.INT_TYPE);
m.load(1, InstructionAdapter.OBJECT_TYPE);
m.load(idx, Type.INT_TYPE);
m.aload(Type.DOUBLE_TYPE);
m.dup2();
m.dconst(0.0);
m.cmpl(Type.DOUBLE_TYPE);
m.ifne(minLabel);
m.pop2();
m.dconst(0.0);
m.goTo(end);
m.visitLabel(minLabel);
m.load(0, InstructionAdapter.OBJECT_TYPE);
m.load(2, InstructionAdapter.OBJECT_TYPE);
m.load(idx, Type.INT_TYPE);
m.aload(Type.INT_TYPE);
m.load(3, InstructionAdapter.OBJECT_TYPE);
m.load(idx, Type.INT_TYPE);
m.aload(Type.INT_TYPE);
m.load(4, InstructionAdapter.OBJECT_TYPE);
m.load(idx, Type.INT_TYPE);
m.aload(Type.INT_TYPE);
m.load(5, InstructionAdapter.OBJECT_TYPE);
m.invokevirtual(context.className, rightMethodSingle, Context.SINGLE_DESC, false);
m.mul(Type.DOUBLE_TYPE);
m.visitLabel(end);
m.astore(Type.DOUBLE_TYPE);
});
m.areturn(Type.VOID_TYPE);
}
protected void bytecodeGenMultiBody(InstructionAdapter m, int idx, int res1) {
throw new UnsupportedOperationException();
}
}

View File

@@ -0,0 +1,23 @@
package org.bxteam.divinemc.dfc.common.ast.dfvisitor;
import net.minecraft.world.level.levelgen.DensityFunction;
import net.minecraft.world.level.levelgen.DensityFunctions;
import net.minecraft.world.level.levelgen.NoiseChunk;
import org.jetbrains.annotations.NotNull;
public class StripBlending implements DensityFunction.Visitor {
public static final StripBlending INSTANCE = new StripBlending();
private StripBlending() {
}
public @NotNull DensityFunction apply(@NotNull DensityFunction densityFunction) {
return switch (densityFunction) {
case NoiseChunk.BlendAlpha _ -> DensityFunctions.constant(1.0);
case NoiseChunk.BlendOffset _ -> DensityFunctions.constant(0.0);
case DensityFunctions.BlendAlpha _ -> DensityFunctions.constant(1.0);
case DensityFunctions.BlendOffset _ -> DensityFunctions.constant(0.0);
default -> densityFunction;
};
}
}

View File

@@ -0,0 +1,256 @@
package org.bxteam.divinemc.dfc.common.ast.misc;
import org.bxteam.divinemc.dfc.common.ast.AstNode;
import org.bxteam.divinemc.dfc.common.ast.AstTransformer;
import org.bxteam.divinemc.dfc.common.ast.EvalType;
import org.bxteam.divinemc.dfc.common.ducks.IFastCacheLike;
import org.bxteam.divinemc.dfc.common.gen.BytecodeGen.Context;
import org.bxteam.divinemc.dfc.common.gen.IMultiMethod;
import org.bxteam.divinemc.dfc.common.gen.ISingleMethod;
import org.bxteam.divinemc.dfc.common.gen.SubCompiledDensityFunction;
import java.util.Objects;
import net.minecraft.world.level.levelgen.DensityFunction;
import net.minecraft.world.level.levelgen.DensityFunctions;
import org.jetbrains.annotations.NotNull;
import org.objectweb.asm.Handle;
import org.objectweb.asm.Label;
import org.objectweb.asm.Type;
import org.objectweb.asm.commons.InstructionAdapter;
public class CacheLikeNode implements AstNode {
private final IFastCacheLike cacheLike;
private final AstNode delegate;
public CacheLikeNode(IFastCacheLike cacheLike, AstNode delegate) {
this.cacheLike = cacheLike;
this.delegate = (AstNode)Objects.requireNonNull(delegate);
}
public double evalSingle(int x, int y, int z, EvalType type) {
if (this.cacheLike == null) {
return this.delegate.evalSingle(x, y, z, type);
} else {
double cached = this.cacheLike.c2me$getCached(x, y, z, type);
if (Double.doubleToRawLongBits(cached) != 9222769054270909007L) {
return cached;
} else {
double eval = this.delegate.evalSingle(x, y, z, type);
this.cacheLike.c2me$cache(x, y, z, type, eval);
return eval;
}
}
}
public void evalMulti(double[] res, int[] x, int[] y, int[] z, EvalType type) {
if (this.cacheLike == null) {
this.delegate.evalMulti(res, x, y, z, type);
} else {
boolean cached = this.cacheLike.c2me$getCached(res, x, y, z, type);
if (!cached) {
this.delegate.evalMulti(res, x, y, z, type);
this.cacheLike.c2me$cache(res, x, y, z, type);
}
}
}
public AstNode[] getChildren() {
return new AstNode[]{this.delegate};
}
public AstNode transform(AstTransformer transformer) {
AstNode delegate = this.delegate.transform(transformer);
return this.delegate == delegate ? transformer.transform(this) : transformer.transform(new CacheLikeNode(this.cacheLike, delegate));
}
public void doBytecodeGenSingle(@NotNull Context context, @NotNull InstructionAdapter m, Context.@NotNull LocalVarConsumer localVarConsumer) {
String delegateMethod = context.newSingleMethod(this.delegate);
String cacheLikeField = context.newField(IFastCacheLike.class, this.cacheLike);
this.genPostprocessingMethod(context, cacheLikeField);
int eval = localVarConsumer.createLocalVariable("eval", Type.DOUBLE_TYPE.getDescriptor());
Label cacheExists = new Label();
Label cacheMiss = new Label();
m.load(0, InstructionAdapter.OBJECT_TYPE);
m.getfield(context.className, cacheLikeField, Type.getDescriptor(IFastCacheLike.class));
m.ifnonnull(cacheExists);
context.callDelegateSingle(m, delegateMethod);
m.areturn(Type.DOUBLE_TYPE);
m.visitLabel(cacheExists);
m.load(0, InstructionAdapter.OBJECT_TYPE);
m.getfield(context.className, cacheLikeField, Type.getDescriptor(IFastCacheLike.class));
m.load(1, Type.INT_TYPE);
m.load(2, Type.INT_TYPE);
m.load(3, Type.INT_TYPE);
m.load(4, InstructionAdapter.OBJECT_TYPE);
m.invokeinterface(Type.getInternalName(IFastCacheLike.class), "c2me$getCached", Type.getMethodDescriptor(Type.DOUBLE_TYPE, new Type[]{Type.INT_TYPE, Type.INT_TYPE, Type.INT_TYPE, Type.getType(EvalType.class)}));
m.dup2();
m.invokestatic(Type.getInternalName(Double.class), "doubleToRawLongBits", Type.getMethodDescriptor(Type.LONG_TYPE, new Type[]{Type.DOUBLE_TYPE}), false);
m.lconst(9222769054270909007L);
m.lcmp();
m.ifeq(cacheMiss);
m.areturn(Type.DOUBLE_TYPE);
m.visitLabel(cacheMiss);
m.pop2();
context.callDelegateSingle(m, delegateMethod);
m.store(eval, Type.DOUBLE_TYPE);
m.load(0, InstructionAdapter.OBJECT_TYPE);
m.getfield(context.className, cacheLikeField, Type.getDescriptor(IFastCacheLike.class));
m.load(1, Type.INT_TYPE);
m.load(2, Type.INT_TYPE);
m.load(3, Type.INT_TYPE);
m.load(4, InstructionAdapter.OBJECT_TYPE);
m.load(eval, Type.DOUBLE_TYPE);
m.invokeinterface(Type.getInternalName(IFastCacheLike.class), "c2me$cache", Type.getMethodDescriptor(Type.VOID_TYPE, new Type[]{Type.INT_TYPE, Type.INT_TYPE, Type.INT_TYPE, Type.getType(EvalType.class), Type.DOUBLE_TYPE}));
m.load(eval, Type.DOUBLE_TYPE);
m.areturn(Type.DOUBLE_TYPE);
}
public void doBytecodeGenMulti(@NotNull Context context, @NotNull InstructionAdapter m, Context.LocalVarConsumer localVarConsumer) {
String delegateMethod = context.newMultiMethod(this.delegate);
String cacheLikeField = context.newField(IFastCacheLike.class, this.cacheLike);
this.genPostprocessingMethod(context, cacheLikeField);
Label cacheExists = new Label();
Label cacheMiss = new Label();
m.load(0, InstructionAdapter.OBJECT_TYPE);
m.getfield(context.className, cacheLikeField, Type.getDescriptor(IFastCacheLike.class));
m.ifnonnull(cacheExists);
context.callDelegateMulti(m, delegateMethod);
m.areturn(Type.VOID_TYPE);
m.visitLabel(cacheExists);
m.load(0, InstructionAdapter.OBJECT_TYPE);
m.getfield(context.className, cacheLikeField, Type.getDescriptor(IFastCacheLike.class));
m.load(1, InstructionAdapter.OBJECT_TYPE);
m.load(2, InstructionAdapter.OBJECT_TYPE);
m.load(3, InstructionAdapter.OBJECT_TYPE);
m.load(4, InstructionAdapter.OBJECT_TYPE);
m.load(5, InstructionAdapter.OBJECT_TYPE);
m.invokeinterface(Type.getInternalName(IFastCacheLike.class), "c2me$getCached", Type.getMethodDescriptor(Type.BOOLEAN_TYPE, new Type[]{Type.getType(double[].class), Type.getType(int[].class), Type.getType(int[].class), Type.getType(int[].class), Type.getType(EvalType.class)}));
m.ifeq(cacheMiss);
m.areturn(Type.VOID_TYPE);
m.visitLabel(cacheMiss);
context.callDelegateMulti(m, delegateMethod);
m.load(0, InstructionAdapter.OBJECT_TYPE);
m.getfield(context.className, cacheLikeField, Type.getDescriptor(IFastCacheLike.class));
m.load(1, InstructionAdapter.OBJECT_TYPE);
m.load(2, InstructionAdapter.OBJECT_TYPE);
m.load(3, InstructionAdapter.OBJECT_TYPE);
m.load(4, InstructionAdapter.OBJECT_TYPE);
m.load(5, InstructionAdapter.OBJECT_TYPE);
m.invokeinterface(Type.getInternalName(IFastCacheLike.class), "c2me$cache", Type.getMethodDescriptor(Type.VOID_TYPE, new Type[]{Type.getType(double[].class), Type.getType(int[].class), Type.getType(int[].class), Type.getType(int[].class), Type.getType(EvalType.class)}));
m.areturn(Type.VOID_TYPE);
}
private void genPostprocessingMethod(@NotNull Context context, String cacheLikeField) {
String methodName = String.format("postProcessing_%s", cacheLikeField);
String delegateSingle = context.newSingleMethod(this.delegate);
String delegateMulti = context.newMultiMethod(this.delegate);
context.genPostprocessingMethod(methodName, (m) -> {
Label cacheExists = new Label();
m.load(0, InstructionAdapter.OBJECT_TYPE);
m.load(0, InstructionAdapter.OBJECT_TYPE);
m.getfield(context.className, cacheLikeField, Type.getDescriptor(IFastCacheLike.class));
m.dup();
m.ifnonnull(cacheExists);
m.pop();
m.pop();
m.areturn(Type.VOID_TYPE);
m.visitLabel(cacheExists);
m.anew(Type.getType(SubCompiledDensityFunction.class));
m.dup();
m.load(0, InstructionAdapter.OBJECT_TYPE);
m.invokedynamic("evalSingle", Type.getMethodDescriptor(Type.getType(ISingleMethod.class), new Type[]{Type.getType(context.classDesc)}), new Handle(6, "java/lang/invoke/LambdaMetafactory", "metafactory", "(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;", false), new Object[]{Type.getMethodType(Context.SINGLE_DESC), new Handle(5, context.className, delegateSingle, Context.SINGLE_DESC, false), Type.getMethodType(Context.SINGLE_DESC)});
m.load(0, InstructionAdapter.OBJECT_TYPE);
m.invokedynamic("evalMulti", Type.getMethodDescriptor(Type.getType(IMultiMethod.class), new Type[]{Type.getType(context.classDesc)}), new Handle(6, "java/lang/invoke/LambdaMetafactory", "metafactory", "(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;", false), new Object[]{Type.getMethodType(Context.MULTI_DESC), new Handle(5, context.className, delegateMulti, Context.MULTI_DESC, false), Type.getMethodType(Context.MULTI_DESC)});
m.load(0, InstructionAdapter.OBJECT_TYPE);
m.getfield(context.className, cacheLikeField, Type.getDescriptor(IFastCacheLike.class));
m.checkcast(Type.getType(DensityFunction.class));
m.invokespecial(Type.getInternalName(SubCompiledDensityFunction.class), "<init>", Type.getMethodDescriptor(Type.VOID_TYPE, new Type[]{Type.getType(ISingleMethod.class), Type.getType(IMultiMethod.class), Type.getType(DensityFunction.class)}), false);
m.checkcast(Type.getType(DensityFunction.class));
m.invokeinterface(Type.getInternalName(IFastCacheLike.class), "c2me$withDelegate", Type.getMethodDescriptor(Type.getType(DensityFunction.class), new Type[]{Type.getType(DensityFunction.class)}));
m.putfield(context.className, cacheLikeField, Type.getDescriptor(IFastCacheLike.class));
m.areturn(Type.VOID_TYPE);
});
}
public IFastCacheLike getCacheLike() {
return this.cacheLike;
}
public AstNode getDelegate() {
return this.delegate;
}
public boolean equals(Object o) {
if (this == o) {
return true;
} else if (o != null && this.getClass() == o.getClass()) {
CacheLikeNode that = (CacheLikeNode)o;
return equals(this.cacheLike, that.cacheLike) && Objects.equals(this.delegate, that.delegate);
} else {
return false;
}
}
private static boolean equals(IFastCacheLike a, IFastCacheLike b) {
if (a instanceof DensityFunctions.Marker wrappingA) {
if (b instanceof DensityFunctions.Marker wrappingB) {
return wrappingA.type() == wrappingB.type();
}
}
return a.equals(b);
}
public int hashCode() {
int result = 1;
result = 31 * result + this.getClass().hashCode();
result = 31 * result + hashCode(this.cacheLike);
result = 31 * result + this.delegate.hashCode();
return result;
}
private static int hashCode(IFastCacheLike o) {
if (o instanceof DensityFunctions.Marker wrapping) {
return wrapping.type().hashCode();
} else {
return o.hashCode();
}
}
public boolean relaxedEquals(AstNode o) {
if (this == o) {
return true;
} else if (o != null && this.getClass() == o.getClass()) {
CacheLikeNode that = (CacheLikeNode)o;
return relaxedEquals(this.cacheLike, that.cacheLike) && this.delegate.relaxedEquals(that.delegate);
} else {
return false;
}
}
private static boolean relaxedEquals(IFastCacheLike a, IFastCacheLike b) {
if (a instanceof DensityFunctions.Marker wrappingA) {
if (b instanceof DensityFunctions.Marker wrappingB) {
return wrappingA.type() == wrappingB.type();
}
}
return a.getClass() == b.getClass();
}
public int relaxedHashCode() {
int result = 1;
result = 31 * result + this.getClass().hashCode();
result = 31 * result + relaxedHashCode(this.cacheLike);
result = 31 * result + this.delegate.relaxedHashCode();
return result;
}
private static int relaxedHashCode(IFastCacheLike o) {
if (o instanceof DensityFunctions.Marker wrapping) {
return wrapping.type().hashCode();
} else {
return o.getClass().hashCode();
}
}
}

View File

@@ -0,0 +1,72 @@
package org.bxteam.divinemc.dfc.common.ast.misc;
import org.bxteam.divinemc.dfc.common.ast.AstNode;
import org.bxteam.divinemc.dfc.common.ast.AstTransformer;
import org.bxteam.divinemc.dfc.common.ast.EvalType;
import org.bxteam.divinemc.dfc.common.gen.BytecodeGen;
import java.util.Arrays;
import org.objectweb.asm.Type;
import org.objectweb.asm.commons.InstructionAdapter;
public class ConstantNode implements AstNode {
private final double value;
public ConstantNode(double value) {
this.value = value;
}
public double evalSingle(int x, int y, int z, EvalType type) {
return this.value;
}
public void evalMulti(double[] res, int[] x, int[] y, int[] z, EvalType type) {
Arrays.fill(res, this.value);
}
public AstNode[] getChildren() {
return new AstNode[0];
}
public AstNode transform(AstTransformer transformer) {
return transformer.transform(this);
}
public void doBytecodeGenSingle(BytecodeGen.Context context, InstructionAdapter m, BytecodeGen.Context.LocalVarConsumer localVarConsumer) {
m.dconst(this.value);
m.areturn(Type.DOUBLE_TYPE);
}
public void doBytecodeGenMulti(BytecodeGen.Context context, InstructionAdapter m, BytecodeGen.Context.LocalVarConsumer localVarConsumer) {
m.load(1, InstructionAdapter.OBJECT_TYPE);
m.dconst(this.value);
m.invokestatic(Type.getInternalName(Arrays.class), "fill", Type.getMethodDescriptor(Type.VOID_TYPE, new Type[]{Type.getType(double[].class), Type.DOUBLE_TYPE}), false);
m.areturn(Type.VOID_TYPE);
}
public double getValue() {
return this.value;
}
public boolean equals(Object o) {
if (this == o) {
return true;
} else if (o != null && this.getClass() == o.getClass()) {
ConstantNode that = (ConstantNode)o;
return Double.compare(this.value, that.value) == 0;
} else {
return false;
}
}
public int hashCode() {
return Double.hashCode(this.value);
}
public boolean relaxedEquals(AstNode o) {
return this.equals(o);
}
public int relaxedHashCode() {
return this.hashCode();
}
}

View File

@@ -0,0 +1,140 @@
package org.bxteam.divinemc.dfc.common.ast.misc;
import org.bxteam.divinemc.dfc.common.ast.AstNode;
import org.bxteam.divinemc.dfc.common.ast.AstTransformer;
import org.bxteam.divinemc.dfc.common.ast.EvalType;
import org.bxteam.divinemc.dfc.common.gen.BytecodeGen;
import org.bxteam.divinemc.dfc.common.util.ArrayCache;
import org.bxteam.divinemc.dfc.common.vif.EachApplierVanillaInterface;
import org.bxteam.divinemc.dfc.common.vif.NoisePosVanillaInterface;
import java.util.Objects;
import net.minecraft.world.level.levelgen.DensityFunction;
import org.objectweb.asm.Label;
import org.objectweb.asm.Type;
import org.objectweb.asm.commons.InstructionAdapter;
public class DelegateNode implements AstNode {
private final DensityFunction densityFunction;
public DelegateNode(DensityFunction densityFunction) {
this.densityFunction = Objects.requireNonNull(densityFunction);
}
public double evalSingle(int x, int y, int z, EvalType type) {
return this.densityFunction.compute(new NoisePosVanillaInterface(x, y, z, type));
}
public void evalMulti(double[] res, int[] x, int[] y, int[] z, EvalType type) {
if (res.length == 1) {
res[0] = this.evalSingle(x[0], y[0], z[0], type);
} else {
this.densityFunction.fillArray(res, new EachApplierVanillaInterface(x, y, z, type));
}
}
public AstNode[] getChildren() {
return new AstNode[0];
}
public AstNode transform(AstTransformer transformer) {
return transformer.transform(this);
}
public void doBytecodeGenSingle(BytecodeGen.Context context, InstructionAdapter m, BytecodeGen.Context.LocalVarConsumer localVarConsumer) {
String newField = context.newField(DensityFunction.class, this.densityFunction);
m.load(0, InstructionAdapter.OBJECT_TYPE);
m.getfield(context.className, newField, Type.getDescriptor(DensityFunction.class));
m.anew(Type.getType(NoisePosVanillaInterface.class));
m.dup();
m.load(1, Type.INT_TYPE);
m.load(2, Type.INT_TYPE);
m.load(3, Type.INT_TYPE);
m.load(4, InstructionAdapter.OBJECT_TYPE);
m.invokespecial(Type.getInternalName(NoisePosVanillaInterface.class), "<init>", Type.getMethodDescriptor(Type.VOID_TYPE, Type.INT_TYPE, Type.INT_TYPE, Type.INT_TYPE, Type.getType(EvalType.class)), false);
m.invokeinterface(Type.getInternalName(DensityFunction.class), "compute", Type.getMethodDescriptor(Type.DOUBLE_TYPE, Type.getType(DensityFunction.FunctionContext.class)));
m.areturn(Type.DOUBLE_TYPE);
}
public void doBytecodeGenMulti(BytecodeGen.Context context, InstructionAdapter m, BytecodeGen.Context.LocalVarConsumer localVarConsumer) {
String newField = context.newField(DensityFunction.class, this.densityFunction);
Label moreThanTwoLabel = new Label();
m.load(1, InstructionAdapter.OBJECT_TYPE);
m.arraylength();
m.iconst(1);
m.ificmpgt(moreThanTwoLabel);
m.load(1, InstructionAdapter.OBJECT_TYPE);
m.iconst(0);
m.load(0, InstructionAdapter.OBJECT_TYPE);
m.getfield(context.className, newField, Type.getDescriptor(DensityFunction.class));
m.anew(Type.getType(NoisePosVanillaInterface.class));
m.dup();
m.load(2, InstructionAdapter.OBJECT_TYPE);
m.iconst(0);
m.aload(Type.INT_TYPE);
m.load(3, InstructionAdapter.OBJECT_TYPE);
m.iconst(0);
m.aload(Type.INT_TYPE);
m.load(4, InstructionAdapter.OBJECT_TYPE);
m.iconst(0);
m.aload(Type.INT_TYPE);
m.load(5, InstructionAdapter.OBJECT_TYPE);
m.invokespecial(Type.getInternalName(NoisePosVanillaInterface.class), "<init>", Type.getMethodDescriptor(Type.VOID_TYPE, Type.INT_TYPE, Type.INT_TYPE, Type.INT_TYPE, Type.getType(EvalType.class)), false);
m.invokeinterface(Type.getInternalName(DensityFunction.class), "compute", Type.getMethodDescriptor(Type.DOUBLE_TYPE, Type.getType(DensityFunction.FunctionContext.class)));
m.astore(Type.DOUBLE_TYPE);
m.areturn(Type.VOID_TYPE);
m.visitLabel(moreThanTwoLabel);
m.load(0, InstructionAdapter.OBJECT_TYPE);
m.getfield(context.className, newField, Type.getDescriptor(DensityFunction.class));
m.load(1, InstructionAdapter.OBJECT_TYPE);
m.anew(Type.getType(EachApplierVanillaInterface.class));
m.dup();
m.load(2, InstructionAdapter.OBJECT_TYPE);
m.load(3, InstructionAdapter.OBJECT_TYPE);
m.load(4, InstructionAdapter.OBJECT_TYPE);
m.load(5, InstructionAdapter.OBJECT_TYPE);
m.load(6, InstructionAdapter.OBJECT_TYPE);
m.invokespecial(Type.getInternalName(EachApplierVanillaInterface.class), "<init>", Type.getMethodDescriptor(Type.VOID_TYPE, Type.getType(int[].class), Type.getType(int[].class), Type.getType(int[].class), Type.getType(EvalType.class), Type.getType(ArrayCache.class)), false);
m.invokeinterface(Type.getInternalName(DensityFunction.class), "fillArray", Type.getMethodDescriptor(Type.VOID_TYPE, Type.getType(double[].class), Type.getType(DensityFunction.ContextProvider.class)));
m.areturn(Type.VOID_TYPE);
}
public DensityFunction getDelegate() {
return this.densityFunction;
}
public boolean equals(Object o) {
if (this == o) {
return true;
} else if (o != null && this.getClass() == o.getClass()) {
DelegateNode that = (DelegateNode)o;
return Objects.equals(this.densityFunction, that.densityFunction);
} else {
return false;
}
}
public int hashCode() {
int result = 1;
result = 31 * result + Objects.hashCode(this.getClass());
result = 31 * result + Objects.hashCode(this.densityFunction);
return result;
}
public boolean relaxedEquals(AstNode o) {
if (this == o) {
return true;
} else if (o != null && this.getClass() == o.getClass()) {
DelegateNode that = (DelegateNode)o;
return this.densityFunction.getClass() == that.densityFunction.getClass();
} else {
return false;
}
}
public int relaxedHashCode() {
int result = 1;
result = 31 * result + Objects.hashCode(this.getClass());
result = 31 * result + Objects.hashCode(this.densityFunction.getClass());
return result;
}
}

View File

@@ -0,0 +1,337 @@
package org.bxteam.divinemc.dfc.common.ast.misc;
import org.bxteam.divinemc.dfc.common.ast.AstNode;
import org.bxteam.divinemc.dfc.common.ast.AstTransformer;
import org.bxteam.divinemc.dfc.common.ast.EvalType;
import org.bxteam.divinemc.dfc.common.gen.BytecodeGen;
import org.bxteam.divinemc.dfc.common.gen.BytecodeGen.Context;
import java.util.Objects;
import org.objectweb.asm.Label;
import org.objectweb.asm.Type;
import org.objectweb.asm.commons.InstructionAdapter;
public class RangeChoiceNode implements AstNode {
private final AstNode input;
private final double minInclusive;
private final double maxExclusive;
private final AstNode whenInRange;
private final AstNode whenOutOfRange;
public RangeChoiceNode(AstNode input, double minInclusive, double maxExclusive, AstNode whenInRange, AstNode whenOutOfRange) {
this.input = (AstNode)Objects.requireNonNull(input);
this.minInclusive = minInclusive;
this.maxExclusive = maxExclusive;
this.whenInRange = (AstNode)Objects.requireNonNull(whenInRange);
this.whenOutOfRange = (AstNode)Objects.requireNonNull(whenOutOfRange);
}
public double evalSingle(int x, int y, int z, EvalType type) {
double v = this.input.evalSingle(x, y, z, type);
return v >= this.minInclusive && v < this.maxExclusive ? this.whenInRange.evalSingle(x, y, z, type) : this.whenOutOfRange.evalSingle(x, y, z, type);
}
public void evalMulti(double[] res, int[] x, int[] y, int[] z, EvalType type) {
this.input.evalMulti(res, x, y, z, type);
int numInRange = 0;
int numOutOfRange;
for(numOutOfRange = 0; numOutOfRange < x.length; ++numOutOfRange) {
double v = res[numOutOfRange];
if (v >= this.minInclusive && v < this.maxExclusive) {
++numInRange;
}
}
numOutOfRange = res.length - numInRange;
if (numInRange == 0) {
this.evalChildMulti(this.whenOutOfRange, res, x, y, z, type);
} else if (numInRange == res.length) {
this.evalChildMulti(this.whenInRange, res, x, y, z, type);
} else {
int idx1 = 0;
int[] i1 = new int[numInRange];
double[] res1 = new double[numInRange];
int[] x1 = new int[numInRange];
int[] y1 = new int[numInRange];
int[] z1 = new int[numInRange];
int idx2 = 0;
int[] i2 = new int[numOutOfRange];
double[] res2 = new double[numOutOfRange];
int[] x2 = new int[numOutOfRange];
int[] y2 = new int[numOutOfRange];
int[] z2 = new int[numOutOfRange];
int i;
for(i = 0; i < res.length; ++i) {
double v = res[i];
int index;
if (v >= this.minInclusive && v < this.maxExclusive) {
index = idx1++;
i1[index] = i;
x1[index] = x[i];
y1[index] = y[i];
z1[index] = z[i];
} else {
index = idx2++;
i2[index] = i;
x2[index] = x[i];
y2[index] = y[i];
z2[index] = z[i];
}
}
this.evalChildMulti(this.whenInRange, res1, x1, y1, z1, type);
this.evalChildMulti(this.whenOutOfRange, res2, x2, y2, z2, type);
for(i = 0; i < numInRange; ++i) {
res[i1[i]] = res1[i];
}
for(i = 0; i < numOutOfRange; ++i) {
res[i2[i]] = res2[i];
}
}
}
public static void evalMultiStatic(double[] res, int[] x, int[] y, int[] z, EvalType type, double minInclusive, double maxExclusive, BytecodeGen.EvalSingleInterface whenInRangeSingle, BytecodeGen.EvalSingleInterface whenOutOfRangeSingle, BytecodeGen.EvalMultiInterface inputMulti, BytecodeGen.EvalMultiInterface whenInRangeMulti, BytecodeGen.EvalMultiInterface whenOutOfRangeMulti) {
inputMulti.evalMulti(res, x, y, z, type);
int numInRange = 0;
int numOutOfRange;
for(numOutOfRange = 0; numOutOfRange < x.length; ++numOutOfRange) {
double v = res[numOutOfRange];
if (v >= minInclusive && v < maxExclusive) {
++numInRange;
}
}
numOutOfRange = res.length - numInRange;
if (numInRange == 0) {
evalChildMulti(whenOutOfRangeSingle, whenOutOfRangeMulti, res, x, y, z, type);
} else if (numInRange == res.length) {
evalChildMulti(whenInRangeSingle, whenInRangeMulti, res, x, y, z, type);
} else {
int idx1 = 0;
int[] i1 = new int[numInRange];
double[] res1 = new double[numInRange];
int[] x1 = new int[numInRange];
int[] y1 = new int[numInRange];
int[] z1 = new int[numInRange];
int idx2 = 0;
int[] i2 = new int[numOutOfRange];
double[] res2 = new double[numOutOfRange];
int[] x2 = new int[numOutOfRange];
int[] y2 = new int[numOutOfRange];
int[] z2 = new int[numOutOfRange];
int i;
for(i = 0; i < res.length; ++i) {
double v = res[i];
int index;
if (v >= minInclusive && v < maxExclusive) {
index = idx1++;
i1[index] = i;
x1[index] = x[i];
y1[index] = y[i];
z1[index] = z[i];
} else {
index = idx2++;
i2[index] = i;
x2[index] = x[i];
y2[index] = y[i];
z2[index] = z[i];
}
}
evalChildMulti(whenInRangeSingle, whenInRangeMulti, res1, x1, y1, z1, type);
evalChildMulti(whenOutOfRangeSingle, whenOutOfRangeMulti, res2, x2, y2, z2, type);
for(i = 0; i < numInRange; ++i) {
res[i1[i]] = res1[i];
}
for(i = 0; i < numOutOfRange; ++i) {
res[i2[i]] = res2[i];
}
}
}
private static void evalChildMulti(BytecodeGen.EvalSingleInterface single, BytecodeGen.EvalMultiInterface multi, double[] res, int[] x, int[] y, int[] z, EvalType type) {
if (res.length == 1) {
res[0] = single.evalSingle(x[0], y[0], z[0], type);
} else {
multi.evalMulti(res, x, y, z, type);
}
}
private void evalChildMulti(AstNode child, double[] res, int[] x, int[] y, int[] z, EvalType type) {
if (res.length == 1) {
res[0] = child.evalSingle(x[0], y[0], z[0], type);
} else {
child.evalMulti(res, x, y, z, type);
}
}
public AstNode[] getChildren() {
return new AstNode[]{this.input, this.whenInRange, this.whenOutOfRange};
}
public AstNode transform(AstTransformer transformer) {
AstNode input = this.input.transform(transformer);
AstNode whenInRange = this.whenInRange.transform(transformer);
AstNode whenOutOfRange = this.whenOutOfRange.transform(transformer);
return this.input == input && this.whenInRange == whenInRange && this.whenOutOfRange == whenOutOfRange ? transformer.transform(this) : transformer.transform(new RangeChoiceNode(input, this.minInclusive, this.maxExclusive, whenInRange, whenOutOfRange));
}
public void doBytecodeGenSingle(Context context, InstructionAdapter m, Context.LocalVarConsumer localVarConsumer) {
String inputMethod = context.newSingleMethod(this.input);
String whenInRangeMethod = context.newSingleMethod(this.whenInRange);
String whenOutOfRangeMethod = context.newSingleMethod(this.whenOutOfRange);
int inputValue = localVarConsumer.createLocalVariable("inputValue", Type.DOUBLE_TYPE.getDescriptor());
context.callDelegateSingle(m, inputMethod);
m.store(inputValue, Type.DOUBLE_TYPE);
Label whenOutOfRangeLabel = new Label();
Label end = new Label();
m.load(inputValue, Type.DOUBLE_TYPE);
m.dconst(this.minInclusive);
m.cmpl(Type.DOUBLE_TYPE);
m.iflt(whenOutOfRangeLabel);
m.load(inputValue, Type.DOUBLE_TYPE);
m.dconst(this.maxExclusive);
m.cmpg(Type.DOUBLE_TYPE);
m.ifge(whenOutOfRangeLabel);
if (whenInRangeMethod.equals(inputMethod)) {
m.load(inputValue, Type.DOUBLE_TYPE);
} else {
context.callDelegateSingle(m, whenInRangeMethod);
}
m.goTo(end);
m.visitLabel(whenOutOfRangeLabel);
if (whenOutOfRangeMethod.equals(inputMethod)) {
m.load(inputValue, Type.DOUBLE_TYPE);
} else {
context.callDelegateSingle(m, whenOutOfRangeMethod);
}
m.visitLabel(end);
m.areturn(Type.DOUBLE_TYPE);
}
public void doBytecodeGenMulti(Context context, InstructionAdapter m, Context.LocalVarConsumer localVarConsumer) {
String inputSingle = context.newSingleMethod(this.input);
String whenInRangeSingle = context.newSingleMethod(this.whenInRange);
String whenOutOfRangeSingle = context.newSingleMethod(this.whenOutOfRange);
String inputMulti = context.newMultiMethod(this.input);
context.callDelegateMulti(m, inputMulti);
context.doCountedLoop(m, localVarConsumer, (idx) -> {
Label whenOutOfRangeLabel = new Label();
Label end = new Label();
m.load(1, InstructionAdapter.OBJECT_TYPE);
m.load(idx, Type.INT_TYPE);
m.load(1, InstructionAdapter.OBJECT_TYPE);
m.load(idx, Type.INT_TYPE);
m.aload(Type.DOUBLE_TYPE);
m.dconst(this.minInclusive);
m.cmpl(Type.DOUBLE_TYPE);
m.iflt(whenOutOfRangeLabel);
m.load(1, InstructionAdapter.OBJECT_TYPE);
m.load(idx, Type.INT_TYPE);
m.aload(Type.DOUBLE_TYPE);
m.dconst(this.maxExclusive);
m.cmpg(Type.DOUBLE_TYPE);
m.ifge(whenOutOfRangeLabel);
if (whenInRangeSingle.equals(inputSingle)) {
m.load(1, InstructionAdapter.OBJECT_TYPE);
m.load(idx, Type.INT_TYPE);
m.aload(Type.DOUBLE_TYPE);
} else {
m.load(0, InstructionAdapter.OBJECT_TYPE);
m.load(2, InstructionAdapter.OBJECT_TYPE);
m.load(idx, Type.INT_TYPE);
m.aload(Type.INT_TYPE);
m.load(3, InstructionAdapter.OBJECT_TYPE);
m.load(idx, Type.INT_TYPE);
m.aload(Type.INT_TYPE);
m.load(4, InstructionAdapter.OBJECT_TYPE);
m.load(idx, Type.INT_TYPE);
m.aload(Type.INT_TYPE);
m.load(5, InstructionAdapter.OBJECT_TYPE);
m.invokevirtual(context.className, whenInRangeSingle, Context.SINGLE_DESC, false);
}
m.goTo(end);
m.visitLabel(whenOutOfRangeLabel);
if (whenOutOfRangeSingle.equals(inputSingle)) {
m.load(1, InstructionAdapter.OBJECT_TYPE);
m.load(idx, Type.INT_TYPE);
m.aload(Type.DOUBLE_TYPE);
} else {
m.load(0, InstructionAdapter.OBJECT_TYPE);
m.load(2, InstructionAdapter.OBJECT_TYPE);
m.load(idx, Type.INT_TYPE);
m.aload(Type.INT_TYPE);
m.load(3, InstructionAdapter.OBJECT_TYPE);
m.load(idx, Type.INT_TYPE);
m.aload(Type.INT_TYPE);
m.load(4, InstructionAdapter.OBJECT_TYPE);
m.load(idx, Type.INT_TYPE);
m.aload(Type.INT_TYPE);
m.load(5, InstructionAdapter.OBJECT_TYPE);
m.invokevirtual(context.className, whenOutOfRangeSingle, Context.SINGLE_DESC, false);
}
m.visitLabel(end);
m.astore(Type.DOUBLE_TYPE);
});
m.areturn(Type.VOID_TYPE);
}
public boolean equals(Object o) {
if (this == o) {
return true;
} else if (o != null && this.getClass() == o.getClass()) {
RangeChoiceNode that = (RangeChoiceNode)o;
return Double.compare(this.minInclusive, that.minInclusive) == 0 && Double.compare(this.maxExclusive, that.maxExclusive) == 0 && Objects.equals(this.input, that.input) && Objects.equals(this.whenInRange, that.whenInRange) && Objects.equals(this.whenOutOfRange, that.whenOutOfRange);
} else {
return false;
}
}
public int hashCode() {
int result = 1;
result = 31 * result + this.getClass().hashCode();
result = 31 * result + this.input.hashCode();
result = 31 * result + Double.hashCode(this.minInclusive);
result = 31 * result + Double.hashCode(this.maxExclusive);
result = 31 * result + this.whenInRange.hashCode();
result = 31 * result + this.whenOutOfRange.hashCode();
return result;
}
public boolean relaxedEquals(AstNode o) {
if (this == o) {
return true;
} else if (o != null && this.getClass() == o.getClass()) {
RangeChoiceNode that = (RangeChoiceNode)o;
return Double.compare(this.minInclusive, that.minInclusive) == 0 && Double.compare(this.maxExclusive, that.maxExclusive) == 0 && this.input.relaxedEquals(that.input) && this.whenInRange.relaxedEquals(that.whenInRange) && this.whenOutOfRange.relaxedEquals(that.whenOutOfRange);
} else {
return false;
}
}
public int relaxedHashCode() {
int result = 1;
result = 31 * result + this.getClass().hashCode();
result = 31 * result + this.input.relaxedHashCode();
result = 31 * result + Double.hashCode(this.minInclusive);
result = 31 * result + Double.hashCode(this.maxExclusive);
result = 31 * result + this.whenInRange.relaxedHashCode();
result = 31 * result + this.whenOutOfRange.relaxedHashCode();
return result;
}
}

View File

@@ -0,0 +1,82 @@
package org.bxteam.divinemc.dfc.common.ast.misc;
import org.bxteam.divinemc.dfc.common.ast.AstNode;
import org.bxteam.divinemc.dfc.common.ast.AstTransformer;
import org.bxteam.divinemc.dfc.common.ast.EvalType;
import org.bxteam.divinemc.dfc.common.gen.BytecodeGen;
import java.util.Objects;
import org.objectweb.asm.Type;
import org.objectweb.asm.commons.InstructionAdapter;
public class RootNode implements AstNode {
private final AstNode next;
public RootNode(AstNode next) {
this.next = (AstNode)Objects.requireNonNull(next);
}
public double evalSingle(int x, int y, int z, EvalType type) {
return this.next.evalSingle(x, y, z, type);
}
public void evalMulti(double[] res, int[] x, int[] y, int[] z, EvalType type) {
this.next.evalMulti(res, x, y, z, type);
}
public AstNode[] getChildren() {
return new AstNode[]{this.next};
}
public AstNode transform(AstTransformer transformer) {
AstNode next = this.next.transform(transformer);
return next == this.next ? transformer.transform(this) : transformer.transform(new RootNode(next));
}
public void doBytecodeGenSingle(BytecodeGen.Context context, InstructionAdapter m, BytecodeGen.Context.LocalVarConsumer localVarConsumer) {
String nextMethod = context.newSingleMethod(this.next);
context.callDelegateSingle(m, nextMethod);
m.areturn(Type.DOUBLE_TYPE);
}
public void doBytecodeGenMulti(BytecodeGen.Context context, InstructionAdapter m, BytecodeGen.Context.LocalVarConsumer localVarConsumer) {
String nextMethod = context.newMultiMethod(this.next);
context.callDelegateMulti(m, nextMethod);
m.areturn(Type.VOID_TYPE);
}
public boolean equals(Object o) {
if (this == o) {
return true;
} else if (o != null && this.getClass() == o.getClass()) {
RootNode that = (RootNode)o;
return Objects.equals(this.next, that.next);
} else {
return false;
}
}
public int hashCode() {
int result = 1;
result = 31 * result + this.getClass().hashCode();
result = 31 * result + this.next.hashCode();
return result;
}
public boolean relaxedEquals(AstNode o) {
if (this == o) {
return true;
} else if (o != null && this.getClass() == o.getClass()) {
RootNode that = (RootNode)o;
return this.next.relaxedEquals(that.next);
} else {
return false;
}
}
public int relaxedHashCode() {
int result = 1;
result = 31 * result + this.getClass().hashCode();
result = 31 * result + this.next.relaxedHashCode();
return result;
}
}

View File

@@ -0,0 +1,100 @@
package org.bxteam.divinemc.dfc.common.ast.misc;
import org.bxteam.divinemc.dfc.common.ast.AstNode;
import org.bxteam.divinemc.dfc.common.ast.AstTransformer;
import org.bxteam.divinemc.dfc.common.ast.EvalType;
import org.bxteam.divinemc.dfc.common.gen.BytecodeGen;
import net.minecraft.util.Mth;
import org.objectweb.asm.Type;
import org.objectweb.asm.commons.InstructionAdapter;
public class YClampedGradientNode implements AstNode {
private final double fromY;
private final double toY;
private final double fromValue;
private final double toValue;
public YClampedGradientNode(double fromY, double toY, double fromValue, double toValue) {
this.fromY = fromY;
this.toY = toY;
this.fromValue = fromValue;
this.toValue = toValue;
}
public double evalSingle(int x, int y, int z, EvalType type) {
return Mth.clampedMap((double)y, this.fromY, this.toY, this.fromValue, this.toValue);
}
public void evalMulti(double[] res, int[] x, int[] y, int[] z, EvalType type) {
for(int i = 0; i < res.length; ++i) {
res[i] = Mth.clampedMap((double)y[i], this.fromY, this.toY, this.fromValue, this.toValue);
}
}
public AstNode[] getChildren() {
return new AstNode[0];
}
public AstNode transform(AstTransformer transformer) {
return transformer.transform(this);
}
public void doBytecodeGenSingle(BytecodeGen.Context context, InstructionAdapter m, BytecodeGen.Context.LocalVarConsumer localVarConsumer) {
m.load(2, Type.INT_TYPE);
m.cast(Type.INT_TYPE, Type.DOUBLE_TYPE);
m.dconst(this.fromY);
m.dconst(this.toY);
m.dconst(this.fromValue);
m.dconst(this.toValue);
m.invokestatic(Type.getInternalName(Mth.class), "clampedMap", "(DDDDD)D", false);
m.areturn(Type.DOUBLE_TYPE);
}
public void doBytecodeGenMulti(BytecodeGen.Context context, InstructionAdapter m, BytecodeGen.Context.LocalVarConsumer localVarConsumer) {
context.doCountedLoop(m, localVarConsumer, (idx) -> {
m.load(1, InstructionAdapter.OBJECT_TYPE);
m.load(idx, Type.INT_TYPE);
m.load(3, InstructionAdapter.OBJECT_TYPE);
m.load(idx, Type.INT_TYPE);
m.aload(Type.INT_TYPE);
m.cast(Type.INT_TYPE, Type.DOUBLE_TYPE);
m.dconst(this.fromY);
m.dconst(this.toY);
m.dconst(this.fromValue);
m.dconst(this.toValue);
m.invokestatic(Type.getInternalName(Mth.class), "clampedMap", "(DDDDD)D", false);
m.astore(Type.DOUBLE_TYPE);
});
m.areturn(Type.VOID_TYPE);
}
public boolean equals(Object o) {
if (this == o) {
return true;
} else if (o != null && this.getClass() == o.getClass()) {
YClampedGradientNode that = (YClampedGradientNode)o;
return Double.compare(this.fromY, that.fromY) == 0 && Double.compare(this.toY, that.toY) == 0 && Double.compare(this.fromValue, that.fromValue) == 0 && Double.compare(this.toValue, that.toValue) == 0;
} else {
return false;
}
}
public int hashCode() {
int result = 1;
result = 31 * result + this.getClass().hashCode();
result = 31 * result + Double.hashCode(this.fromY);
result = 31 * result + Double.hashCode(this.toY);
result = 31 * result + Double.hashCode(this.fromValue);
result = 31 * result + Double.hashCode(this.toValue);
return result;
}
public boolean relaxedEquals(AstNode o) {
return this.equals(o);
}
public int relaxedHashCode() {
return this.hashCode();
}
}

View File

@@ -0,0 +1,131 @@
package org.bxteam.divinemc.dfc.common.ast.noise;
import org.bxteam.divinemc.dfc.common.ast.AstNode;
import org.bxteam.divinemc.dfc.common.ast.AstTransformer;
import org.bxteam.divinemc.dfc.common.ast.EvalType;
import org.bxteam.divinemc.dfc.common.gen.BytecodeGen;
import java.util.Objects;
import net.minecraft.world.level.levelgen.DensityFunction;
import org.objectweb.asm.Type;
import org.objectweb.asm.commons.InstructionAdapter;
public class DFTNoiseNode implements AstNode {
private final DensityFunction.NoiseHolder noise;
private final double xzScale;
private final double yScale;
public DFTNoiseNode(DensityFunction.NoiseHolder noise, double xzScale, double yScale) {
this.noise = (DensityFunction.NoiseHolder)Objects.requireNonNull(noise);
this.xzScale = xzScale;
this.yScale = yScale;
}
public double evalSingle(int x, int y, int z, EvalType type) {
return this.noise.getValue((double)x * this.xzScale, (double)y * this.yScale, (double)z * this.xzScale);
}
public void evalMulti(double[] res, int[] x, int[] y, int[] z, EvalType type) {
for(int i = 0; i < res.length; ++i) {
res[i] = this.noise.getValue((double)x[i] * this.xzScale, (double)y[i] * this.yScale, (double)z[i] * this.xzScale);
}
}
public AstNode[] getChildren() {
return new AstNode[0];
}
public AstNode transform(AstTransformer transformer) {
return transformer.transform(this);
}
public void doBytecodeGenSingle(BytecodeGen.Context context, InstructionAdapter m, BytecodeGen.Context.LocalVarConsumer localVarConsumer) {
String noiseField = context.newField(DensityFunction.NoiseHolder.class, this.noise);
m.load(0, InstructionAdapter.OBJECT_TYPE);
m.getfield(context.className, noiseField, Type.getDescriptor(DensityFunction.NoiseHolder.class));
m.load(1, Type.INT_TYPE);
m.cast(Type.INT_TYPE, Type.DOUBLE_TYPE);
m.dconst(this.xzScale);
m.mul(Type.DOUBLE_TYPE);
m.load(2, Type.INT_TYPE);
m.cast(Type.INT_TYPE, Type.DOUBLE_TYPE);
m.dconst(this.yScale);
m.mul(Type.DOUBLE_TYPE);
m.load(3, Type.INT_TYPE);
m.cast(Type.INT_TYPE, Type.DOUBLE_TYPE);
m.dconst(this.xzScale);
m.mul(Type.DOUBLE_TYPE);
m.invokevirtual(Type.getInternalName(DensityFunction.NoiseHolder.class), "getValue", "(DDD)D", false);
m.areturn(Type.DOUBLE_TYPE);
}
public void doBytecodeGenMulti(BytecodeGen.Context context, InstructionAdapter m, BytecodeGen.Context.LocalVarConsumer localVarConsumer) {
String noiseField = context.newField(DensityFunction.NoiseHolder.class, this.noise);
context.doCountedLoop(m, localVarConsumer, (idx) -> {
m.load(1, InstructionAdapter.OBJECT_TYPE);
m.load(idx, Type.INT_TYPE);
m.load(0, InstructionAdapter.OBJECT_TYPE);
m.getfield(context.className, noiseField, Type.getDescriptor(DensityFunction.NoiseHolder.class));
m.load(2, InstructionAdapter.OBJECT_TYPE);
m.load(idx, Type.INT_TYPE);
m.aload(Type.INT_TYPE);
m.cast(Type.INT_TYPE, Type.DOUBLE_TYPE);
m.dconst(this.xzScale);
m.mul(Type.DOUBLE_TYPE);
m.load(3, InstructionAdapter.OBJECT_TYPE);
m.load(idx, Type.INT_TYPE);
m.aload(Type.INT_TYPE);
m.cast(Type.INT_TYPE, Type.DOUBLE_TYPE);
m.dconst(this.yScale);
m.mul(Type.DOUBLE_TYPE);
m.load(4, InstructionAdapter.OBJECT_TYPE);
m.load(idx, Type.INT_TYPE);
m.aload(Type.INT_TYPE);
m.cast(Type.INT_TYPE, Type.DOUBLE_TYPE);
m.dconst(this.xzScale);
m.mul(Type.DOUBLE_TYPE);
m.invokevirtual(Type.getInternalName(DensityFunction.NoiseHolder.class), "getValue", "(DDD)D", false);
m.astore(Type.DOUBLE_TYPE);
});
m.areturn(Type.VOID_TYPE);
}
public boolean equals(Object o) {
if (this == o) {
return true;
} else if (o != null && this.getClass() == o.getClass()) {
DFTNoiseNode that = (DFTNoiseNode)o;
return Double.compare(this.xzScale, that.xzScale) == 0 && Double.compare(this.yScale, that.yScale) == 0 && Objects.equals(this.noise, that.noise);
} else {
return false;
}
}
public int hashCode() {
int result = 1;
result = 31 * result + this.getClass().hashCode();
result = 31 * result + this.noise.hashCode();
result = 31 * result + Double.hashCode(this.xzScale);
result = 31 * result + Double.hashCode(this.yScale);
return result;
}
public boolean relaxedEquals(AstNode o) {
if (this == o) {
return true;
} else if (o != null && this.getClass() == o.getClass()) {
DFTNoiseNode that = (DFTNoiseNode)o;
return Double.compare(this.xzScale, that.xzScale) == 0 && Double.compare(this.yScale, that.yScale) == 0;
} else {
return false;
}
}
public int relaxedHashCode() {
int result = 1;
result = 31 * result + this.getClass().hashCode();
result = 31 * result + Double.hashCode(this.xzScale);
result = 31 * result + Double.hashCode(this.yScale);
return result;
}
}

View File

@@ -0,0 +1,115 @@
package org.bxteam.divinemc.dfc.common.ast.noise;
import org.bxteam.divinemc.dfc.common.ast.AstNode;
import org.bxteam.divinemc.dfc.common.ast.AstTransformer;
import org.bxteam.divinemc.dfc.common.ast.EvalType;
import org.bxteam.divinemc.dfc.common.gen.BytecodeGen;
import java.util.Objects;
import net.minecraft.world.level.levelgen.DensityFunction;
import org.objectweb.asm.Type;
import org.objectweb.asm.commons.InstructionAdapter;
public class DFTShiftANode implements AstNode {
private final DensityFunction.NoiseHolder offsetNoise;
public DFTShiftANode(DensityFunction.NoiseHolder offsetNoise) {
this.offsetNoise = (DensityFunction.NoiseHolder)Objects.requireNonNull(offsetNoise);
}
public double evalSingle(int x, int y, int z, EvalType type) {
return this.offsetNoise.getValue((double)x * 0.25, 0.0, (double)z * 0.25) * 4.0;
}
public void evalMulti(double[] res, int[] x, int[] y, int[] z, EvalType type) {
for(int i = 0; i < res.length; ++i) {
res[i] = this.offsetNoise.getValue((double)x[i] * 0.25, 0.0, (double)z[i] * 0.25) * 4.0;
}
}
public AstNode[] getChildren() {
return new AstNode[0];
}
public AstNode transform(AstTransformer transformer) {
return transformer.transform(this);
}
public void doBytecodeGenSingle(BytecodeGen.Context context, InstructionAdapter m, BytecodeGen.Context.LocalVarConsumer localVarConsumer) {
String noiseField = context.newField(DensityFunction.NoiseHolder.class, this.offsetNoise);
m.load(0, InstructionAdapter.OBJECT_TYPE);
m.getfield(context.className, noiseField, Type.getDescriptor(DensityFunction.NoiseHolder.class));
m.load(1, Type.INT_TYPE);
m.cast(Type.INT_TYPE, Type.DOUBLE_TYPE);
m.dconst(0.25);
m.mul(Type.DOUBLE_TYPE);
m.dconst(0.0);
m.load(3, Type.INT_TYPE);
m.cast(Type.INT_TYPE, Type.DOUBLE_TYPE);
m.dconst(0.25);
m.mul(Type.DOUBLE_TYPE);
m.invokevirtual(Type.getInternalName(DensityFunction.NoiseHolder.class), "getValue", "(DDD)D", false);
m.dconst(4.0);
m.mul(Type.DOUBLE_TYPE);
m.areturn(Type.DOUBLE_TYPE);
}
public void doBytecodeGenMulti(BytecodeGen.Context context, InstructionAdapter m, BytecodeGen.Context.LocalVarConsumer localVarConsumer) {
String noiseField = context.newField(DensityFunction.NoiseHolder.class, this.offsetNoise);
context.doCountedLoop(m, localVarConsumer, (idx) -> {
m.load(1, InstructionAdapter.OBJECT_TYPE);
m.load(idx, Type.INT_TYPE);
m.load(0, InstructionAdapter.OBJECT_TYPE);
m.getfield(context.className, noiseField, Type.getDescriptor(DensityFunction.NoiseHolder.class));
m.load(2, InstructionAdapter.OBJECT_TYPE);
m.load(idx, Type.INT_TYPE);
m.aload(Type.INT_TYPE);
m.cast(Type.INT_TYPE, Type.DOUBLE_TYPE);
m.dconst(0.25);
m.mul(Type.DOUBLE_TYPE);
m.dconst(0.0);
m.load(4, InstructionAdapter.OBJECT_TYPE);
m.load(idx, Type.INT_TYPE);
m.aload(Type.INT_TYPE);
m.cast(Type.INT_TYPE, Type.DOUBLE_TYPE);
m.dconst(0.25);
m.mul(Type.DOUBLE_TYPE);
m.invokevirtual(Type.getInternalName(DensityFunction.NoiseHolder.class), "getValue", "(DDD)D", false);
m.dconst(4.0);
m.mul(Type.DOUBLE_TYPE);
m.astore(Type.DOUBLE_TYPE);
});
m.areturn(Type.VOID_TYPE);
}
public boolean equals(Object o) {
if (this == o) {
return true;
} else if (o != null && this.getClass() == o.getClass()) {
DFTShiftANode that = (DFTShiftANode)o;
return Objects.equals(this.offsetNoise, that.offsetNoise);
} else {
return false;
}
}
public int hashCode() {
int result = 1;
Object o = this.getClass();
result = 31 * result + o.hashCode();
result = 31 * result + this.offsetNoise.hashCode();
return result;
}
public boolean relaxedEquals(AstNode o) {
if (this == o) {
return true;
} else {
return o != null && this.getClass() == o.getClass();
}
}
public int relaxedHashCode() {
return this.getClass().hashCode();
}
}

View File

@@ -0,0 +1,115 @@
package org.bxteam.divinemc.dfc.common.ast.noise;
import org.bxteam.divinemc.dfc.common.ast.AstNode;
import org.bxteam.divinemc.dfc.common.ast.AstTransformer;
import org.bxteam.divinemc.dfc.common.ast.EvalType;
import org.bxteam.divinemc.dfc.common.gen.BytecodeGen;
import java.util.Objects;
import net.minecraft.world.level.levelgen.DensityFunction;
import org.objectweb.asm.Type;
import org.objectweb.asm.commons.InstructionAdapter;
public class DFTShiftBNode implements AstNode {
private final DensityFunction.NoiseHolder offsetNoise;
public DFTShiftBNode(DensityFunction.NoiseHolder offsetNoise) {
this.offsetNoise = (DensityFunction.NoiseHolder)Objects.requireNonNull(offsetNoise);
}
public double evalSingle(int x, int y, int z, EvalType type) {
return this.offsetNoise.getValue((double)z * 0.25, (double)x * 0.25, 0.0) * 4.0;
}
public void evalMulti(double[] res, int[] x, int[] y, int[] z, EvalType type) {
for(int i = 0; i < res.length; ++i) {
res[i] = this.offsetNoise.getValue((double)z[i] * 0.25, (double)x[i] * 0.25, 0.0) * 4.0;
}
}
public AstNode[] getChildren() {
return new AstNode[0];
}
public AstNode transform(AstTransformer transformer) {
return transformer.transform(this);
}
public void doBytecodeGenSingle(BytecodeGen.Context context, InstructionAdapter m, BytecodeGen.Context.LocalVarConsumer localVarConsumer) {
String noiseField = context.newField(DensityFunction.NoiseHolder.class, this.offsetNoise);
m.load(0, InstructionAdapter.OBJECT_TYPE);
m.getfield(context.className, noiseField, Type.getDescriptor(DensityFunction.NoiseHolder.class));
m.load(3, Type.INT_TYPE);
m.cast(Type.INT_TYPE, Type.DOUBLE_TYPE);
m.dconst(0.25);
m.mul(Type.DOUBLE_TYPE);
m.load(1, Type.INT_TYPE);
m.cast(Type.INT_TYPE, Type.DOUBLE_TYPE);
m.dconst(0.25);
m.mul(Type.DOUBLE_TYPE);
m.dconst(0.0);
m.invokevirtual(Type.getInternalName(DensityFunction.NoiseHolder.class), "getValue", "(DDD)D", false);
m.dconst(4.0);
m.mul(Type.DOUBLE_TYPE);
m.areturn(Type.DOUBLE_TYPE);
}
public void doBytecodeGenMulti(BytecodeGen.Context context, InstructionAdapter m, BytecodeGen.Context.LocalVarConsumer localVarConsumer) {
String noiseField = context.newField(DensityFunction.NoiseHolder.class, this.offsetNoise);
context.doCountedLoop(m, localVarConsumer, (idx) -> {
m.load(1, InstructionAdapter.OBJECT_TYPE);
m.load(idx, Type.INT_TYPE);
m.load(0, InstructionAdapter.OBJECT_TYPE);
m.getfield(context.className, noiseField, Type.getDescriptor(DensityFunction.NoiseHolder.class));
m.load(4, InstructionAdapter.OBJECT_TYPE);
m.load(idx, Type.INT_TYPE);
m.aload(Type.INT_TYPE);
m.cast(Type.INT_TYPE, Type.DOUBLE_TYPE);
m.dconst(0.25);
m.mul(Type.DOUBLE_TYPE);
m.load(2, InstructionAdapter.OBJECT_TYPE);
m.load(idx, Type.INT_TYPE);
m.aload(Type.INT_TYPE);
m.cast(Type.INT_TYPE, Type.DOUBLE_TYPE);
m.dconst(0.25);
m.mul(Type.DOUBLE_TYPE);
m.dconst(0.0);
m.invokevirtual(Type.getInternalName(DensityFunction.NoiseHolder.class), "getValue", "(DDD)D", false);
m.dconst(4.0);
m.mul(Type.DOUBLE_TYPE);
m.astore(Type.DOUBLE_TYPE);
});
m.areturn(Type.VOID_TYPE);
}
public boolean equals(Object o) {
if (this == o) {
return true;
} else if (o != null && this.getClass() == o.getClass()) {
DFTShiftBNode that = (DFTShiftBNode)o;
return Objects.equals(this.offsetNoise, that.offsetNoise);
} else {
return false;
}
}
public int hashCode() {
int result = 1;
Object o = this.getClass();
result = 31 * result + o.hashCode();
result = 31 * result + this.offsetNoise.hashCode();
return result;
}
public boolean relaxedEquals(AstNode o) {
if (this == o) {
return true;
} else {
return o != null && this.getClass() == o.getClass();
}
}
public int relaxedHashCode() {
return this.getClass().hashCode();
}
}

View File

@@ -0,0 +1,123 @@
package org.bxteam.divinemc.dfc.common.ast.noise;
import org.bxteam.divinemc.dfc.common.ast.AstNode;
import org.bxteam.divinemc.dfc.common.ast.AstTransformer;
import org.bxteam.divinemc.dfc.common.ast.EvalType;
import org.bxteam.divinemc.dfc.common.gen.BytecodeGen;
import java.util.Objects;
import net.minecraft.world.level.levelgen.DensityFunction;
import org.objectweb.asm.Type;
import org.objectweb.asm.commons.InstructionAdapter;
public class DFTShiftNode implements AstNode {
private final DensityFunction.NoiseHolder offsetNoise;
public DFTShiftNode(DensityFunction.NoiseHolder offsetNoise) {
this.offsetNoise = (DensityFunction.NoiseHolder)Objects.requireNonNull(offsetNoise);
}
public double evalSingle(int x, int y, int z, EvalType type) {
return this.offsetNoise.getValue((double)x * 0.25, (double)y * 0.25, (double)z * 0.25) * 4.0;
}
public void evalMulti(double[] res, int[] x, int[] y, int[] z, EvalType type) {
for(int i = 0; i < res.length; ++i) {
res[i] = this.offsetNoise.getValue((double)x[i] * 0.25, (double)y[i] * 0.25, (double)z[i] * 0.25) * 4.0;
}
}
public AstNode[] getChildren() {
return new AstNode[0];
}
public AstNode transform(AstTransformer transformer) {
return transformer.transform(this);
}
public void doBytecodeGenSingle(BytecodeGen.Context context, InstructionAdapter m, BytecodeGen.Context.LocalVarConsumer localVarConsumer) {
String noiseField = context.newField(DensityFunction.NoiseHolder.class, this.offsetNoise);
m.load(0, InstructionAdapter.OBJECT_TYPE);
m.getfield(context.className, noiseField, Type.getDescriptor(DensityFunction.NoiseHolder.class));
m.load(1, Type.INT_TYPE);
m.cast(Type.INT_TYPE, Type.DOUBLE_TYPE);
m.dconst(0.25);
m.mul(Type.DOUBLE_TYPE);
m.load(2, Type.INT_TYPE);
m.cast(Type.INT_TYPE, Type.DOUBLE_TYPE);
m.dconst(0.25);
m.mul(Type.DOUBLE_TYPE);
m.load(3, Type.INT_TYPE);
m.cast(Type.INT_TYPE, Type.DOUBLE_TYPE);
m.dconst(0.25);
m.mul(Type.DOUBLE_TYPE);
m.invokevirtual(Type.getInternalName(DensityFunction.NoiseHolder.class), "getValue", "(DDD)D", false);
m.dconst(4.0);
m.mul(Type.DOUBLE_TYPE);
m.areturn(Type.DOUBLE_TYPE);
}
public void doBytecodeGenMulti(BytecodeGen.Context context, InstructionAdapter m, BytecodeGen.Context.LocalVarConsumer localVarConsumer) {
String noiseField = context.newField(DensityFunction.NoiseHolder.class, this.offsetNoise);
context.doCountedLoop(m, localVarConsumer, (idx) -> {
m.load(1, InstructionAdapter.OBJECT_TYPE);
m.load(idx, Type.INT_TYPE);
m.load(0, InstructionAdapter.OBJECT_TYPE);
m.getfield(context.className, noiseField, Type.getDescriptor(DensityFunction.NoiseHolder.class));
m.load(2, InstructionAdapter.OBJECT_TYPE);
m.load(idx, Type.INT_TYPE);
m.aload(Type.INT_TYPE);
m.cast(Type.INT_TYPE, Type.DOUBLE_TYPE);
m.dconst(0.25);
m.mul(Type.DOUBLE_TYPE);
m.load(3, InstructionAdapter.OBJECT_TYPE);
m.load(idx, Type.INT_TYPE);
m.aload(Type.INT_TYPE);
m.cast(Type.INT_TYPE, Type.DOUBLE_TYPE);
m.dconst(0.25);
m.mul(Type.DOUBLE_TYPE);
m.load(4, InstructionAdapter.OBJECT_TYPE);
m.load(idx, Type.INT_TYPE);
m.aload(Type.INT_TYPE);
m.cast(Type.INT_TYPE, Type.DOUBLE_TYPE);
m.dconst(0.25);
m.mul(Type.DOUBLE_TYPE);
m.invokevirtual(Type.getInternalName(DensityFunction.NoiseHolder.class), "getValue", "(DDD)D", false);
m.dconst(4.0);
m.mul(Type.DOUBLE_TYPE);
m.astore(Type.DOUBLE_TYPE);
});
m.areturn(Type.VOID_TYPE);
}
public boolean equals(Object o) {
if (this == o) {
return true;
} else if (o != null && this.getClass() == o.getClass()) {
DFTShiftNode that = (DFTShiftNode)o;
return Objects.equals(this.offsetNoise, that.offsetNoise);
} else {
return false;
}
}
public int hashCode() {
int result = 1;
Object o = this.getClass();
result = 31 * result + o.hashCode();
result = 31 * result + this.offsetNoise.hashCode();
return result;
}
public boolean relaxedEquals(AstNode o) {
if (this == o) {
return true;
} else {
return o != null && this.getClass() == o.getClass();
}
}
public int relaxedHashCode() {
return this.getClass().hashCode();
}
}

View File

@@ -0,0 +1,169 @@
package org.bxteam.divinemc.dfc.common.ast.noise;
import org.bxteam.divinemc.dfc.common.IDensityFunctionsCaveScaler;
import org.bxteam.divinemc.dfc.common.ast.AstNode;
import org.bxteam.divinemc.dfc.common.ast.AstTransformer;
import org.bxteam.divinemc.dfc.common.ast.EvalType;
import org.bxteam.divinemc.dfc.common.gen.BytecodeGen;
import java.util.Objects;
import net.minecraft.world.level.levelgen.DensityFunction;
import net.minecraft.world.level.levelgen.DensityFunctions;
import org.jetbrains.annotations.NotNull;
import org.objectweb.asm.Type;
import org.objectweb.asm.commons.InstructionAdapter;
public class DFTWeirdScaledSamplerNode implements AstNode {
private final AstNode input;
private final DensityFunction.NoiseHolder noise;
private final DensityFunctions.WeirdScaledSampler.RarityValueMapper mapper;
public DFTWeirdScaledSamplerNode(AstNode input, DensityFunction.NoiseHolder noise, DensityFunctions.WeirdScaledSampler.RarityValueMapper mapper) {
this.input = Objects.requireNonNull(input);
this.noise = Objects.requireNonNull(noise);
this.mapper = Objects.requireNonNull(mapper);
}
public double evalSingle(int x, int y, int z, EvalType type) {
double v = this.input.evalSingle(x, y, z, type);
double d = (this.mapper.mapper).get(v);
return d * Math.abs(this.noise.getValue((double)x / d, (double)y / d, (double)z / d));
}
public void evalMulti(double[] res, int[] x, int[] y, int[] z, EvalType type) {
this.input.evalMulti(res, x, y, z, type);
for(int i = 0; i < res.length; ++i) {
double d = (this.mapper.mapper).get(res[i]);
res[i] = d * Math.abs(this.noise.getValue((double)x[i] / d, (double)y[i] / d, (double)z[i] / d));
}
}
public AstNode[] getChildren() {
return new AstNode[]{this.input};
}
public AstNode transform(AstTransformer transformer) {
AstNode input = this.input.transform(transformer);
return input == this.input ? transformer.transform(this) : transformer.transform(new DFTWeirdScaledSamplerNode(input, this.noise, this.mapper));
}
public void doBytecodeGenSingle(BytecodeGen.@NotNull Context context, InstructionAdapter m, BytecodeGen.Context.@NotNull LocalVarConsumer localVarConsumer) {
String inputMethod = context.newSingleMethod(this.input);
String noiseField = context.newField(DensityFunction.NoiseHolder.class, this.noise);
int scale = localVarConsumer.createLocalVariable("scale", Type.DOUBLE_TYPE.getDescriptor());
context.callDelegateSingle(m, inputMethod);
switch (this.mapper) {
case TYPE1 -> m.invokestatic(Type.getInternalName(IDensityFunctionsCaveScaler.class), "invokeScaleTunnels", Type.getMethodDescriptor(Type.DOUBLE_TYPE, Type.DOUBLE_TYPE), true);
case TYPE2 -> m.invokestatic(Type.getInternalName(IDensityFunctionsCaveScaler.class), "invokeScaleCaves", Type.getMethodDescriptor(Type.DOUBLE_TYPE, Type.DOUBLE_TYPE), true);
default -> throw new UnsupportedOperationException(String.format("Unknown mapper %s", this.mapper));
}
m.store(scale, Type.DOUBLE_TYPE);
m.load(0, InstructionAdapter.OBJECT_TYPE);
m.getfield(context.className, noiseField, Type.getDescriptor(DensityFunction.NoiseHolder.class));
m.load(1, Type.INT_TYPE);
m.cast(Type.INT_TYPE, Type.DOUBLE_TYPE);
m.load(scale, Type.DOUBLE_TYPE);
m.div(Type.DOUBLE_TYPE);
m.load(2, Type.INT_TYPE);
m.cast(Type.INT_TYPE, Type.DOUBLE_TYPE);
m.load(scale, Type.DOUBLE_TYPE);
m.div(Type.DOUBLE_TYPE);
m.load(3, Type.INT_TYPE);
m.cast(Type.INT_TYPE, Type.DOUBLE_TYPE);
m.load(scale, Type.DOUBLE_TYPE);
m.div(Type.DOUBLE_TYPE);
m.invokevirtual(Type.getInternalName(DensityFunction.NoiseHolder.class), "getValue", "(DDD)D", false);
m.invokestatic(Type.getInternalName(Math.class), "abs", "(D)D", false);
m.load(scale, Type.DOUBLE_TYPE);
m.mul(Type.DOUBLE_TYPE);
m.areturn(Type.DOUBLE_TYPE);
}
public void doBytecodeGenMulti(BytecodeGen.Context context, InstructionAdapter m, BytecodeGen.Context.LocalVarConsumer localVarConsumer) {
String inputMethod = context.newMultiMethod(this.input);
String noiseField = context.newField(DensityFunction.NoiseHolder.class, this.noise);
context.callDelegateMulti(m, inputMethod);
context.doCountedLoop(m, localVarConsumer, (idx) -> {
int scale = localVarConsumer.createLocalVariable("scale", Type.DOUBLE_TYPE.getDescriptor());
m.load(1, InstructionAdapter.OBJECT_TYPE);
m.load(idx, Type.INT_TYPE);
m.load(1, InstructionAdapter.OBJECT_TYPE);
m.load(idx, Type.INT_TYPE);
m.aload(Type.DOUBLE_TYPE);
switch (this.mapper) {
case TYPE1 -> m.invokestatic(Type.getInternalName(IDensityFunctionsCaveScaler.class), "invokeScaleTunnels", Type.getMethodDescriptor(Type.DOUBLE_TYPE, Type.DOUBLE_TYPE), true);
case TYPE2 -> m.invokestatic(Type.getInternalName(IDensityFunctionsCaveScaler.class), "invokeScaleCaves", Type.getMethodDescriptor(Type.DOUBLE_TYPE, Type.DOUBLE_TYPE), true);
default -> throw new UnsupportedOperationException(String.format("Unknown mapper %s", this.mapper));
}
m.store(scale, Type.DOUBLE_TYPE);
m.load(0, InstructionAdapter.OBJECT_TYPE);
m.getfield(context.className, noiseField, Type.getDescriptor(DensityFunction.NoiseHolder.class));
m.load(2, InstructionAdapter.OBJECT_TYPE);
m.load(idx, Type.INT_TYPE);
m.aload(Type.INT_TYPE);
m.cast(Type.INT_TYPE, Type.DOUBLE_TYPE);
m.load(scale, Type.DOUBLE_TYPE);
m.div(Type.DOUBLE_TYPE);
m.load(3, InstructionAdapter.OBJECT_TYPE);
m.load(idx, Type.INT_TYPE);
m.aload(Type.INT_TYPE);
m.cast(Type.INT_TYPE, Type.DOUBLE_TYPE);
m.load(scale, Type.DOUBLE_TYPE);
m.div(Type.DOUBLE_TYPE);
m.load(4, InstructionAdapter.OBJECT_TYPE);
m.load(idx, Type.INT_TYPE);
m.aload(Type.INT_TYPE);
m.cast(Type.INT_TYPE, Type.DOUBLE_TYPE);
m.load(scale, Type.DOUBLE_TYPE);
m.div(Type.DOUBLE_TYPE);
m.invokevirtual(Type.getInternalName(DensityFunction.NoiseHolder.class), "getValue", "(DDD)D", false);
m.invokestatic(Type.getInternalName(Math.class), "abs", "(D)D", false);
m.load(scale, Type.DOUBLE_TYPE);
m.mul(Type.DOUBLE_TYPE);
m.astore(Type.DOUBLE_TYPE);
});
m.areturn(Type.VOID_TYPE);
}
public boolean equals(Object o) {
if (this == o) {
return true;
} else if (o != null && this.getClass() == o.getClass()) {
DFTWeirdScaledSamplerNode that = (DFTWeirdScaledSamplerNode)o;
return Objects.equals(this.input, that.input) && Objects.equals(this.noise, that.noise) && this.mapper == that.mapper;
} else {
return false;
}
}
public int hashCode() {
int result = 1;
result = 31 * result + this.getClass().hashCode();
result = 31 * result + this.input.hashCode();
result = 31 * result + this.noise.hashCode();
result = 31 * result + this.mapper.hashCode();
return result;
}
public boolean relaxedEquals(AstNode o) {
if (this == o) {
return true;
} else if (o != null && this.getClass() == o.getClass()) {
DFTWeirdScaledSamplerNode that = (DFTWeirdScaledSamplerNode)o;
return this.input.relaxedEquals(that.input) && this.mapper == that.mapper;
} else {
return false;
}
}
public int relaxedHashCode() {
int result = 1;
result = 31 * result + this.getClass().hashCode();
result = 31 * result + this.input.relaxedHashCode();
result = 31 * result + this.mapper.hashCode();
return result;
}
}

View File

@@ -0,0 +1,215 @@
package org.bxteam.divinemc.dfc.common.ast.noise;
import org.bxteam.divinemc.dfc.common.ast.AstNode;
import org.bxteam.divinemc.dfc.common.ast.AstTransformer;
import org.bxteam.divinemc.dfc.common.ast.EvalType;
import org.bxteam.divinemc.dfc.common.gen.BytecodeGen.Context;
import org.bxteam.divinemc.dfc.common.util.ArrayCache;
import java.util.Objects;
import net.minecraft.world.level.levelgen.DensityFunction;
import org.objectweb.asm.Type;
import org.objectweb.asm.commons.InstructionAdapter;
public class ShiftedNoiseNode implements AstNode {
private final AstNode shiftX;
private final AstNode shiftY;
private final AstNode shiftZ;
private final double xzScale;
private final double yScale;
private final DensityFunction.NoiseHolder noise;
public ShiftedNoiseNode(AstNode shiftX, AstNode shiftY, AstNode shiftZ, double xzScale, double yScale, DensityFunction.NoiseHolder noise) {
this.shiftX = (AstNode)Objects.requireNonNull(shiftX);
this.shiftY = (AstNode)Objects.requireNonNull(shiftY);
this.shiftZ = (AstNode)Objects.requireNonNull(shiftZ);
this.xzScale = xzScale;
this.yScale = yScale;
this.noise = (DensityFunction.NoiseHolder)Objects.requireNonNull(noise);
}
public double evalSingle(int x, int y, int z, EvalType type) {
double d = (double)x * this.xzScale + this.shiftX.evalSingle(x, y, z, type);
double e = (double)y * this.yScale + this.shiftY.evalSingle(x, y, z, type);
double f = (double)z * this.xzScale + this.shiftZ.evalSingle(x, y, z, type);
return this.noise.getValue(d, e, f);
}
public void evalMulti(double[] res, int[] x, int[] y, int[] z, EvalType type) {
double[] res1 = new double[res.length];
double[] res2 = new double[res.length];
this.shiftX.evalMulti(res, x, y, z, type);
this.shiftY.evalMulti(res1, x, y, z, type);
this.shiftZ.evalMulti(res2, x, y, z, type);
for(int i = 0; i < res.length; ++i) {
res[i] = this.noise.getValue((double)x[i] * this.xzScale + res[i], (double)y[i] * this.yScale + res1[i], (double)z[i] * this.xzScale + res2[i]);
}
}
public AstNode[] getChildren() {
return new AstNode[]{this.shiftX, this.shiftY, this.shiftZ};
}
public AstNode transform(AstTransformer transformer) {
AstNode shiftX = this.shiftX.transform(transformer);
AstNode shiftY = this.shiftY.transform(transformer);
AstNode shiftZ = this.shiftZ.transform(transformer);
return shiftX == this.shiftX && shiftY == this.shiftY && shiftZ == this.shiftZ ? transformer.transform(this) : transformer.transform(new ShiftedNoiseNode(shiftX, shiftY, shiftZ, this.xzScale, this.yScale, this.noise));
}
public void doBytecodeGenSingle(Context context, InstructionAdapter m, Context.LocalVarConsumer localVarConsumer) {
String noiseField = context.newField(DensityFunction.NoiseHolder.class, this.noise);
String shiftXMethod = context.newSingleMethod(this.shiftX);
String shiftYMethod = context.newSingleMethod(this.shiftY);
String shiftZMethod = context.newSingleMethod(this.shiftZ);
m.load(0, InstructionAdapter.OBJECT_TYPE);
m.getfield(context.className, noiseField, Type.getDescriptor(DensityFunction.NoiseHolder.class));
m.load(1, Type.INT_TYPE);
m.cast(Type.INT_TYPE, Type.DOUBLE_TYPE);
m.dconst(this.xzScale);
m.mul(Type.DOUBLE_TYPE);
context.callDelegateSingle(m, shiftXMethod);
m.add(Type.DOUBLE_TYPE);
m.load(2, Type.INT_TYPE);
m.cast(Type.INT_TYPE, Type.DOUBLE_TYPE);
m.dconst(this.yScale);
m.mul(Type.DOUBLE_TYPE);
context.callDelegateSingle(m, shiftYMethod);
m.add(Type.DOUBLE_TYPE);
m.load(3, Type.INT_TYPE);
m.cast(Type.INT_TYPE, Type.DOUBLE_TYPE);
m.dconst(this.xzScale);
m.mul(Type.DOUBLE_TYPE);
context.callDelegateSingle(m, shiftZMethod);
m.add(Type.DOUBLE_TYPE);
m.invokevirtual(Type.getInternalName(DensityFunction.NoiseHolder.class), "getValue", "(DDD)D", false);
m.areturn(Type.DOUBLE_TYPE);
}
public void doBytecodeGenMulti(Context context, InstructionAdapter m, Context.LocalVarConsumer localVarConsumer) {
String noiseField = context.newField(DensityFunction.NoiseHolder.class, this.noise);
String shiftXMethod = context.newMultiMethod(this.shiftX);
String shiftYMethod = context.newMultiMethod(this.shiftY);
String shiftZMethod = context.newMultiMethod(this.shiftZ);
int res1 = localVarConsumer.createLocalVariable("res1", Type.getDescriptor(double[].class));
int res2 = localVarConsumer.createLocalVariable("res2", Type.getDescriptor(double[].class));
m.load(6, InstructionAdapter.OBJECT_TYPE);
m.load(1, InstructionAdapter.OBJECT_TYPE);
m.arraylength();
m.iconst(0);
m.invokevirtual(Type.getInternalName(ArrayCache.class), "getDoubleArray", Type.getMethodDescriptor(Type.getType(double[].class), new Type[]{Type.INT_TYPE, Type.BOOLEAN_TYPE}), false);
m.store(res1, InstructionAdapter.OBJECT_TYPE);
m.load(6, InstructionAdapter.OBJECT_TYPE);
m.load(1, InstructionAdapter.OBJECT_TYPE);
m.arraylength();
m.iconst(0);
m.invokevirtual(Type.getInternalName(ArrayCache.class), "getDoubleArray", Type.getMethodDescriptor(Type.getType(double[].class), new Type[]{Type.INT_TYPE, Type.BOOLEAN_TYPE}), false);
m.store(res2, InstructionAdapter.OBJECT_TYPE);
context.callDelegateMulti(m, shiftXMethod);
m.load(0, InstructionAdapter.OBJECT_TYPE);
m.load(res1, InstructionAdapter.OBJECT_TYPE);
m.load(2, InstructionAdapter.OBJECT_TYPE);
m.load(3, InstructionAdapter.OBJECT_TYPE);
m.load(4, InstructionAdapter.OBJECT_TYPE);
m.load(5, InstructionAdapter.OBJECT_TYPE);
m.load(6, InstructionAdapter.OBJECT_TYPE);
m.invokevirtual(context.className, shiftYMethod, Context.MULTI_DESC, false);
m.load(0, InstructionAdapter.OBJECT_TYPE);
m.load(res2, InstructionAdapter.OBJECT_TYPE);
m.load(2, InstructionAdapter.OBJECT_TYPE);
m.load(3, InstructionAdapter.OBJECT_TYPE);
m.load(4, InstructionAdapter.OBJECT_TYPE);
m.load(5, InstructionAdapter.OBJECT_TYPE);
m.load(6, InstructionAdapter.OBJECT_TYPE);
m.invokevirtual(context.className, shiftZMethod, Context.MULTI_DESC, false);
context.doCountedLoop(m, localVarConsumer, (idx) -> {
m.load(1, InstructionAdapter.OBJECT_TYPE);
m.load(idx, Type.INT_TYPE);
m.load(0, InstructionAdapter.OBJECT_TYPE);
m.getfield(context.className, noiseField, Type.getDescriptor(DensityFunction.NoiseHolder.class));
m.load(2, InstructionAdapter.OBJECT_TYPE);
m.load(idx, Type.INT_TYPE);
m.aload(Type.INT_TYPE);
m.cast(Type.INT_TYPE, Type.DOUBLE_TYPE);
m.dconst(this.xzScale);
m.mul(Type.DOUBLE_TYPE);
m.load(1, InstructionAdapter.OBJECT_TYPE);
m.load(idx, Type.INT_TYPE);
m.aload(Type.DOUBLE_TYPE);
m.add(Type.DOUBLE_TYPE);
m.load(3, InstructionAdapter.OBJECT_TYPE);
m.load(idx, Type.INT_TYPE);
m.aload(Type.INT_TYPE);
m.cast(Type.INT_TYPE, Type.DOUBLE_TYPE);
m.dconst(this.yScale);
m.mul(Type.DOUBLE_TYPE);
m.load(res1, InstructionAdapter.OBJECT_TYPE);
m.load(idx, Type.INT_TYPE);
m.aload(Type.DOUBLE_TYPE);
m.add(Type.DOUBLE_TYPE);
m.load(4, InstructionAdapter.OBJECT_TYPE);
m.load(idx, Type.INT_TYPE);
m.aload(Type.INT_TYPE);
m.cast(Type.INT_TYPE, Type.DOUBLE_TYPE);
m.dconst(this.xzScale);
m.mul(Type.DOUBLE_TYPE);
m.load(res2, InstructionAdapter.OBJECT_TYPE);
m.load(idx, Type.INT_TYPE);
m.aload(Type.DOUBLE_TYPE);
m.add(Type.DOUBLE_TYPE);
m.invokevirtual(Type.getInternalName(DensityFunction.NoiseHolder.class), "getValue", "(DDD)D", false);
m.astore(Type.DOUBLE_TYPE);
});
m.load(6, InstructionAdapter.OBJECT_TYPE);
m.load(res1, InstructionAdapter.OBJECT_TYPE);
m.invokevirtual(Type.getInternalName(ArrayCache.class), "recycle", Type.getMethodDescriptor(Type.VOID_TYPE, new Type[]{Type.getType(double[].class)}), false);
m.load(6, InstructionAdapter.OBJECT_TYPE);
m.load(res2, InstructionAdapter.OBJECT_TYPE);
m.invokevirtual(Type.getInternalName(ArrayCache.class), "recycle", Type.getMethodDescriptor(Type.VOID_TYPE, new Type[]{Type.getType(double[].class)}), false);
m.areturn(Type.VOID_TYPE);
}
public boolean equals(Object o) {
if (this == o) {
return true;
} else if (o != null && this.getClass() == o.getClass()) {
ShiftedNoiseNode that = (ShiftedNoiseNode)o;
return Double.compare(this.xzScale, that.xzScale) == 0 && Double.compare(this.yScale, that.yScale) == 0 && Objects.equals(this.shiftX, that.shiftX) && Objects.equals(this.shiftY, that.shiftY) && Objects.equals(this.shiftZ, that.shiftZ) && Objects.equals(this.noise, that.noise);
} else {
return false;
}
}
public int hashCode() {
int result = 1;
result = 31 * result + this.shiftX.hashCode();
result = 31 * result + this.shiftY.hashCode();
result = 31 * result + this.shiftZ.hashCode();
result = 31 * result + Double.hashCode(this.xzScale);
result = 31 * result + Double.hashCode(this.yScale);
result = 31 * result + this.noise.hashCode();
return result;
}
public boolean relaxedEquals(AstNode o) {
if (this == o) {
return true;
} else if (o != null && this.getClass() == o.getClass()) {
ShiftedNoiseNode that = (ShiftedNoiseNode)o;
return Double.compare(this.xzScale, that.xzScale) == 0 && Double.compare(this.yScale, that.yScale) == 0 && this.shiftX.relaxedEquals(that.shiftX) && this.shiftY.relaxedEquals(that.shiftY) && this.shiftZ.relaxedEquals(that.shiftZ);
} else {
return false;
}
}
public int relaxedHashCode() {
int result = 1;
result = 31 * result + this.shiftX.relaxedHashCode();
result = 31 * result + this.shiftY.relaxedHashCode();
result = 31 * result + this.shiftZ.relaxedHashCode();
result = 31 * result + Double.hashCode(this.xzScale);
result = 31 * result + Double.hashCode(this.yScale);
return result;
}
}

View File

@@ -0,0 +1,453 @@
package org.bxteam.divinemc.dfc.common.ast.spline;
import org.bxteam.divinemc.dfc.common.ast.AstNode;
import org.bxteam.divinemc.dfc.common.ast.AstTransformer;
import org.bxteam.divinemc.dfc.common.ast.EvalType;
import org.bxteam.divinemc.dfc.common.ast.McToAst;
import org.bxteam.divinemc.dfc.common.gen.BytecodeGen;
import org.bxteam.divinemc.dfc.common.vif.NoisePosVanillaInterface;
import it.unimi.dsi.fastutil.Pair;
import it.unimi.dsi.fastutil.ints.IntObjectPair;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import net.minecraft.util.CubicSpline;
import net.minecraft.util.Mth;
import net.minecraft.world.level.levelgen.DensityFunction;
import net.minecraft.world.level.levelgen.DensityFunctions;
import org.bxteam.divinemc.util.Assertions;
import org.objectweb.asm.Label;
import org.objectweb.asm.Type;
import org.objectweb.asm.commons.AnalyzerAdapter;
import org.objectweb.asm.commons.InstructionAdapter;
public class SplineAstNode implements AstNode {
public static final String SPLINE_METHOD_DESC;
private final CubicSpline<DensityFunctions.Spline.Point, DensityFunctions.Spline.Coordinate> spline;
public SplineAstNode(CubicSpline<DensityFunctions.Spline.Point, DensityFunctions.Spline.Coordinate> spline) {
this.spline = spline;
}
public double evalSingle(int x, int y, int z, EvalType type) {
return (double)this.spline.apply(new DensityFunctions.Spline.Point(new NoisePosVanillaInterface(x, y, z, type)));
}
public void evalMulti(double[] res, int[] x, int[] y, int[] z, EvalType type) {
for(int i = 0; i < res.length; ++i) {
res[i] = this.evalSingle(x[i], y[i], z[i], type);
}
}
public AstNode[] getChildren() {
return new AstNode[0];
}
public AstNode transform(AstTransformer transformer) {
return transformer.transform(this);
}
public void doBytecodeGenSingle(BytecodeGen.Context context, InstructionAdapter m, BytecodeGen.Context.LocalVarConsumer localVarConsumer) {
ValuesMethodDef splineMethod = doBytecodeGenSpline(context, this.spline);
callSplineSingle(context, m, splineMethod);
m.cast(Type.FLOAT_TYPE, Type.DOUBLE_TYPE);
m.areturn(Type.DOUBLE_TYPE);
}
private static ValuesMethodDef doBytecodeGenSpline(BytecodeGen.Context context, CubicSpline<DensityFunctions.Spline.Point, DensityFunctions.Spline.Coordinate> spline) {
String name = context.getCachedSplineMethod(spline);
if (name != null) {
return new ValuesMethodDef(false, name, 0.0F);
} else if (spline instanceof CubicSpline.Constant) {
CubicSpline.Constant<DensityFunctions.Spline.Point, DensityFunctions.Spline.Coordinate> spline1 = (CubicSpline.Constant)spline;
return new ValuesMethodDef(true, (String)null, spline1.value());
} else {
name = context.nextMethodName("Spline");
InstructionAdapter m = new InstructionAdapter(new AnalyzerAdapter(context.className, 18, name, SPLINE_METHOD_DESC, context.classWriter.visitMethod(18, name, SPLINE_METHOD_DESC, (String)null, (String[])null)));
List<IntObjectPair<Pair<String, String>>> extraLocals = new ArrayList();
Label start = new Label();
Label end = new Label();
m.visitLabel(start);
BytecodeGen.Context.LocalVarConsumer localVarConsumer = (localName, localDesc) -> {
int ordinal = extraLocals.size() + 5;
extraLocals.add(IntObjectPair.of(ordinal, Pair.of(localName, localDesc)));
return ordinal;
};
if (spline instanceof CubicSpline.Multipoint) {
CubicSpline.Multipoint<DensityFunctions.Spline.Point, DensityFunctions.Spline.Coordinate> impl = (CubicSpline.Multipoint)spline;
ValuesMethodDef[] valuesMethods = (ValuesMethodDef[])impl.values().stream().map((spline1x) -> {
return doBytecodeGenSpline(context, spline1x);
}).toArray((x$0) -> {
return new ValuesMethodDef[x$0];
});
String locations = context.newField(float[].class, impl.locations());
String derivatives = context.newField(float[].class, impl.derivatives());
int point = localVarConsumer.createLocalVariable("point", Type.FLOAT_TYPE.getDescriptor());
int rangeForLocation = localVarConsumer.createLocalVariable("rangeForLocation", Type.INT_TYPE.getDescriptor());
int lastConst = impl.locations().length - 1;
String locationFunction = context.newSingleMethod(McToAst.toAst((DensityFunction)((DensityFunctions.Spline.Coordinate)impl.coordinate()).function().value()));
context.callDelegateSingle(m, locationFunction);
m.cast(Type.DOUBLE_TYPE, Type.FLOAT_TYPE);
m.store(point, Type.FLOAT_TYPE);
if (valuesMethods.length == 1) {
m.load(point, Type.FLOAT_TYPE);
m.load(0, InstructionAdapter.OBJECT_TYPE);
m.getfield(context.className, locations, Type.getDescriptor(float[].class));
callSplineSingle(context, m, valuesMethods[0]);
m.load(0, InstructionAdapter.OBJECT_TYPE);
m.getfield(context.className, derivatives, Type.getDescriptor(float[].class));
m.iconst(0);
m.invokestatic(Type.getInternalName(SplineSupport.class), "sampleOutsideRange", Type.getMethodDescriptor(Type.FLOAT_TYPE, new Type[]{Type.FLOAT_TYPE, Type.getType(float[].class), Type.FLOAT_TYPE, Type.getType(float[].class), Type.INT_TYPE}), false);
m.areturn(Type.FLOAT_TYPE);
} else {
m.load(0, InstructionAdapter.OBJECT_TYPE);
m.getfield(context.className, locations, Type.getDescriptor(float[].class));
m.load(point, Type.FLOAT_TYPE);
m.invokestatic(Type.getInternalName(SplineSupport.class), "findRangeForLocation", Type.getMethodDescriptor(Type.INT_TYPE, new Type[]{Type.getType(float[].class), Type.FLOAT_TYPE}), false);
m.store(rangeForLocation, Type.INT_TYPE);
Label label1 = new Label();
Label label2 = new Label();
m.load(rangeForLocation, Type.INT_TYPE);
m.ifge(label1);
m.load(point, Type.FLOAT_TYPE);
m.load(0, InstructionAdapter.OBJECT_TYPE);
m.getfield(context.className, locations, Type.getDescriptor(float[].class));
callSplineSingle(context, m, valuesMethods[0]);
m.load(0, InstructionAdapter.OBJECT_TYPE);
m.getfield(context.className, derivatives, Type.getDescriptor(float[].class));
m.iconst(0);
m.invokestatic(Type.getInternalName(SplineSupport.class), "sampleOutsideRange", Type.getMethodDescriptor(Type.FLOAT_TYPE, new Type[]{Type.FLOAT_TYPE, Type.getType(float[].class), Type.FLOAT_TYPE, Type.getType(float[].class), Type.INT_TYPE}), false);
m.areturn(Type.FLOAT_TYPE);
m.visitLabel(label1);
m.load(rangeForLocation, Type.INT_TYPE);
m.iconst(lastConst);
m.ificmpne(label2);
m.load(point, Type.FLOAT_TYPE);
m.load(0, InstructionAdapter.OBJECT_TYPE);
m.getfield(context.className, locations, Type.getDescriptor(float[].class));
callSplineSingle(context, m, valuesMethods[lastConst]);
m.load(0, InstructionAdapter.OBJECT_TYPE);
m.getfield(context.className, derivatives, Type.getDescriptor(float[].class));
m.iconst(lastConst);
m.invokestatic(Type.getInternalName(SplineSupport.class), "sampleOutsideRange", Type.getMethodDescriptor(Type.FLOAT_TYPE, new Type[]{Type.FLOAT_TYPE, Type.getType(float[].class), Type.FLOAT_TYPE, Type.getType(float[].class), Type.INT_TYPE}), false);
m.areturn(Type.FLOAT_TYPE);
m.visitLabel(label2);
int loc0 = localVarConsumer.createLocalVariable("loc0", Type.FLOAT_TYPE.getDescriptor());
int loc1 = localVarConsumer.createLocalVariable("loc1", Type.FLOAT_TYPE.getDescriptor());
int locDist = localVarConsumer.createLocalVariable("locDist", Type.FLOAT_TYPE.getDescriptor());
int k = localVarConsumer.createLocalVariable("k", Type.FLOAT_TYPE.getDescriptor());
int n = localVarConsumer.createLocalVariable("n", Type.FLOAT_TYPE.getDescriptor());
int o = localVarConsumer.createLocalVariable("o", Type.FLOAT_TYPE.getDescriptor());
int onDist = localVarConsumer.createLocalVariable("onDist", Type.FLOAT_TYPE.getDescriptor());
int p = localVarConsumer.createLocalVariable("p", Type.FLOAT_TYPE.getDescriptor());
int q = localVarConsumer.createLocalVariable("q", Type.FLOAT_TYPE.getDescriptor());
m.load(0, InstructionAdapter.OBJECT_TYPE);
m.getfield(context.className, locations, Type.getDescriptor(float[].class));
m.load(rangeForLocation, Type.INT_TYPE);
m.aload(Type.FLOAT_TYPE);
m.store(loc0, Type.FLOAT_TYPE);
m.load(0, InstructionAdapter.OBJECT_TYPE);
m.getfield(context.className, locations, Type.getDescriptor(float[].class));
m.load(rangeForLocation, Type.INT_TYPE);
m.iconst(1);
m.add(Type.INT_TYPE);
m.aload(Type.FLOAT_TYPE);
m.store(loc1, Type.FLOAT_TYPE);
m.load(loc1, Type.FLOAT_TYPE);
m.load(loc0, Type.FLOAT_TYPE);
m.sub(Type.FLOAT_TYPE);
m.store(locDist, Type.FLOAT_TYPE);
m.load(point, Type.FLOAT_TYPE);
m.load(loc0, Type.FLOAT_TYPE);
m.sub(Type.FLOAT_TYPE);
m.load(locDist, Type.FLOAT_TYPE);
m.div(Type.FLOAT_TYPE);
m.store(k, Type.FLOAT_TYPE);
Label[] jumpLabels = new Label[valuesMethods.length - 1];
boolean[] jumpGenerated = new boolean[valuesMethods.length - 1];
for(int i = 0; i < valuesMethods.length - 1; ++i) {
jumpLabels[i] = new Label();
}
Label defaultLabel = new Label();
Label label3 = new Label();
m.load(rangeForLocation, Type.INT_TYPE);
m.tableswitch(0, valuesMethods.length - 2, defaultLabel, jumpLabels);
for(int i = 0; i < valuesMethods.length - 1; ++i) {
if (!jumpGenerated[i]) {
m.visitLabel(jumpLabels[i]);
jumpGenerated[i] = true;
for(int j = i + 1; j < valuesMethods.length - 1; ++j) {
if (valuesMethods[i].equals(valuesMethods[j]) && valuesMethods[i + 1].equals(valuesMethods[j + 1])) {
m.visitLabel(jumpLabels[j]);
jumpGenerated[j] = true;
}
}
callSplineSingle(context, m, valuesMethods[i]);
if (valuesMethods[i].equals(valuesMethods[i + 1])) {
m.dup();
m.store(n, Type.FLOAT_TYPE);
m.store(o, Type.FLOAT_TYPE);
} else {
m.store(n, Type.FLOAT_TYPE);
callSplineSingle(context, m, valuesMethods[i + 1]);
m.store(o, Type.FLOAT_TYPE);
}
m.goTo(label3);
}
}
m.visitLabel(defaultLabel);
m.iconst(0);
m.aconst("boom");
m.invokestatic(Type.getInternalName(Assertions.class), "assertTrue", Type.getMethodDescriptor(Type.VOID_TYPE, new Type[]{Type.BOOLEAN_TYPE, Type.getType(String.class)}), false);
m.fconst(Float.NaN);
m.areturn(Type.FLOAT_TYPE);
m.visitLabel(label3);
m.load(o, Type.FLOAT_TYPE);
m.load(n, Type.FLOAT_TYPE);
m.sub(Type.FLOAT_TYPE);
m.store(onDist, Type.FLOAT_TYPE);
m.load(0, InstructionAdapter.OBJECT_TYPE);
m.getfield(context.className, derivatives, Type.getDescriptor(float[].class));
m.load(rangeForLocation, Type.INT_TYPE);
m.aload(Type.FLOAT_TYPE);
m.load(locDist, Type.FLOAT_TYPE);
m.mul(Type.FLOAT_TYPE);
m.load(onDist, Type.FLOAT_TYPE);
m.sub(Type.FLOAT_TYPE);
m.store(p, Type.FLOAT_TYPE);
m.load(0, InstructionAdapter.OBJECT_TYPE);
m.getfield(context.className, derivatives, Type.getDescriptor(float[].class));
m.load(rangeForLocation, Type.INT_TYPE);
m.iconst(1);
m.add(Type.INT_TYPE);
m.aload(Type.FLOAT_TYPE);
m.neg(Type.FLOAT_TYPE);
m.load(locDist, Type.FLOAT_TYPE);
m.mul(Type.FLOAT_TYPE);
m.load(onDist, Type.FLOAT_TYPE);
m.add(Type.FLOAT_TYPE);
m.store(q, Type.FLOAT_TYPE);
m.load(k, Type.FLOAT_TYPE);
m.load(n, Type.FLOAT_TYPE);
m.load(o, Type.FLOAT_TYPE);
m.invokestatic(Type.getInternalName(Mth.class), "lerp", "(FFF)F", false);
m.load(k, Type.FLOAT_TYPE);
m.fconst(1.0F);
m.load(k, Type.FLOAT_TYPE);
m.sub(Type.FLOAT_TYPE);
m.mul(Type.FLOAT_TYPE);
m.load(k, Type.FLOAT_TYPE);
m.load(p, Type.FLOAT_TYPE);
m.load(q, Type.FLOAT_TYPE);
m.invokestatic(Type.getInternalName(Mth.class), "lerp", "(FFF)F", false);
m.mul(Type.FLOAT_TYPE);
m.add(Type.FLOAT_TYPE);
m.areturn(Type.FLOAT_TYPE);
}
} else {
if (!(spline instanceof CubicSpline.Constant)) {
throw new UnsupportedOperationException(String.format("Unsupported spline implementation: %s", spline.getClass().getName()));
}
CubicSpline.Constant<DensityFunctions.Spline.Point, DensityFunctions.Spline.Coordinate> floatFunction = (CubicSpline.Constant)spline;
m.fconst(floatFunction.value());
m.areturn(Type.FLOAT_TYPE);
}
m.visitLabel(end);
m.visitLocalVariable("this", context.classDesc, (String)null, start, end, 0);
m.visitLocalVariable("x", Type.INT_TYPE.getDescriptor(), (String)null, start, end, 1);
m.visitLocalVariable("y", Type.INT_TYPE.getDescriptor(), (String)null, start, end, 2);
m.visitLocalVariable("z", Type.INT_TYPE.getDescriptor(), (String)null, start, end, 3);
m.visitLocalVariable("evalType", Type.getType(EvalType.class).getDescriptor(), (String)null, start, end, 4);
Iterator var35 = extraLocals.iterator();
while(var35.hasNext()) {
IntObjectPair<Pair<String, String>> local = (IntObjectPair)var35.next();
m.visitLocalVariable((String)((Pair)local.right()).left(), (String)((Pair)local.right()).right(), (String)null, start, end, local.leftInt());
}
m.visitMaxs(0, 0);
context.cacheSplineMethod(spline, name);
return new ValuesMethodDef(false, name, 0.0F);
}
}
private static void callSplineSingle(BytecodeGen.Context context, InstructionAdapter m, ValuesMethodDef target) {
if (target.isConst()) {
m.fconst(target.constValue());
} else {
m.load(0, InstructionAdapter.OBJECT_TYPE);
m.load(1, Type.INT_TYPE);
m.load(2, Type.INT_TYPE);
m.load(3, Type.INT_TYPE);
m.load(4, InstructionAdapter.OBJECT_TYPE);
m.invokevirtual(context.className, target.generatedMethod(), SPLINE_METHOD_DESC, false);
}
}
public void doBytecodeGenMulti(BytecodeGen.Context context, InstructionAdapter m, BytecodeGen.Context.LocalVarConsumer localVarConsumer) {
context.delegateToSingle(m, localVarConsumer, this);
m.areturn(Type.VOID_TYPE);
}
private static boolean deepEquals(CubicSpline<DensityFunctions.Spline.Point, DensityFunctions.Spline.Coordinate> a, CubicSpline<DensityFunctions.Spline.Point, DensityFunctions.Spline.Coordinate> b) {
if (a instanceof CubicSpline.Constant<DensityFunctions.Spline.Point, DensityFunctions.Spline.Coordinate> a1) {
if (b instanceof CubicSpline.Constant<DensityFunctions.Spline.Point, DensityFunctions.Spline.Coordinate> b1) {
return a1.value() == b1.value();
}
}
if (a instanceof CubicSpline.Multipoint<DensityFunctions.Spline.Point, DensityFunctions.Spline.Coordinate> a1) {
if (b instanceof CubicSpline.Multipoint<DensityFunctions.Spline.Point, DensityFunctions.Spline.Coordinate> b1) {
boolean equals1 = Arrays.equals(a1.derivatives(), b1.derivatives()) && Arrays.equals(a1.locations(), b1.locations()) && a1.values().size() == b1.values().size() && McToAst.toAst((DensityFunction)((DensityFunctions.Spline.Coordinate)a1.coordinate()).function().value()).equals(McToAst.toAst((DensityFunction)((DensityFunctions.Spline.Coordinate)b1.coordinate()).function().value()));
if (!equals1) {
return false;
}
int size = a1.values().size();
for(int i = 0; i < size; ++i) {
if (!deepEquals((CubicSpline)a1.values().get(i), (CubicSpline)b1.values().get(i))) {
return false;
}
}
return true;
}
}
return false;
}
private static boolean deepRelaxedEquals(CubicSpline<DensityFunctions.Spline.Point, DensityFunctions.Spline.Coordinate> a, CubicSpline<DensityFunctions.Spline.Point, DensityFunctions.Spline.Coordinate> b) {
if (a instanceof CubicSpline.Constant<DensityFunctions.Spline.Point, DensityFunctions.Spline.Coordinate> a1) {
if (b instanceof CubicSpline.Constant<DensityFunctions.Spline.Point, DensityFunctions.Spline.Coordinate> b1) {
return a1.value() == b1.value();
}
}
if (a instanceof CubicSpline.Multipoint<DensityFunctions.Spline.Point, DensityFunctions.Spline.Coordinate> a1) {
if (b instanceof CubicSpline.Multipoint<DensityFunctions.Spline.Point, DensityFunctions.Spline.Coordinate> b1) {
boolean equals1 = a1.values().size() == b1.values().size() && McToAst.toAst((DensityFunction)((DensityFunctions.Spline.Coordinate)a1.coordinate()).function().value()).relaxedEquals(McToAst.toAst((DensityFunction)((DensityFunctions.Spline.Coordinate)b1.coordinate()).function().value()));
if (!equals1) {
return false;
}
int size = a1.values().size();
for(int i = 0; i < size; ++i) {
if (!deepRelaxedEquals((CubicSpline)a1.values().get(i), (CubicSpline)b1.values().get(i))) {
return false;
}
}
return true;
}
}
return false;
}
private static int deepHashcode(CubicSpline<DensityFunctions.Spline.Point, DensityFunctions.Spline.Coordinate> a) {
if (a instanceof CubicSpline.Constant<DensityFunctions.Spline.Point, DensityFunctions.Spline.Coordinate> a1) {
return Float.hashCode(a1.value());
} else if (!(a instanceof CubicSpline.Multipoint<DensityFunctions.Spline.Point, DensityFunctions.Spline.Coordinate> a1)) {
return a.hashCode();
} else {
int result = 1;
result = 31 * result + Arrays.hashCode(a1.derivatives());
result = 31 * result + Arrays.hashCode(a1.locations());
CubicSpline spline;
for(Iterator var4 = a1.values().iterator(); var4.hasNext(); result = 31 * result + deepHashcode(spline)) {
spline = (CubicSpline)var4.next();
}
result = 31 * result + McToAst.toAst((DensityFunction)((DensityFunctions.Spline.Coordinate)a1.coordinate()).function().value()).hashCode();
return result;
}
}
private static int deepRelaxedHashcode(CubicSpline<DensityFunctions.Spline.Point, DensityFunctions.Spline.Coordinate> a) {
if (a instanceof CubicSpline.Constant<DensityFunctions.Spline.Point, DensityFunctions.Spline.Coordinate> a1) {
return Float.hashCode(a1.value());
} else if (!(a instanceof CubicSpline.Multipoint<DensityFunctions.Spline.Point, DensityFunctions.Spline.Coordinate> a1)) {
return a.hashCode();
} else {
int result = 1;
CubicSpline spline;
for(Iterator var4 = a1.values().iterator(); var4.hasNext(); result = 31 * result + deepRelaxedHashcode(spline)) {
spline = (CubicSpline)var4.next();
}
result = 31 * result + McToAst.toAst((DensityFunction)((DensityFunctions.Spline.Coordinate)a1.coordinate()).function().value()).relaxedHashCode();
return result;
}
}
public boolean equals(Object o) {
if (this == o) {
return true;
} else if (o != null && this.getClass() == o.getClass()) {
SplineAstNode that = (SplineAstNode)o;
return deepEquals(this.spline, that.spline);
} else {
return false;
}
}
public int hashCode() {
return deepHashcode(this.spline);
}
public boolean relaxedEquals(AstNode o) {
if (this == o) {
return true;
} else if (o != null && this.getClass() == o.getClass()) {
SplineAstNode that = (SplineAstNode)o;
return deepRelaxedEquals(this.spline, that.spline);
} else {
return false;
}
}
public int relaxedHashCode() {
return deepRelaxedHashcode(this.spline);
}
static {
SPLINE_METHOD_DESC = Type.getMethodDescriptor(Type.getType(Float.TYPE), new Type[]{Type.getType(Integer.TYPE), Type.getType(Integer.TYPE), Type.getType(Integer.TYPE), Type.getType(EvalType.class)});
}
private static record ValuesMethodDef(boolean isConst, String generatedMethod, float constValue) {
private ValuesMethodDef(boolean isConst, String generatedMethod, float constValue) {
this.isConst = isConst;
this.generatedMethod = generatedMethod;
this.constValue = constValue;
}
public boolean isConst() {
return this.isConst;
}
public String generatedMethod() {
return this.generatedMethod;
}
public float constValue() {
return this.constValue;
}
}
}

View File

@@ -0,0 +1,29 @@
package org.bxteam.divinemc.dfc.common.ast.spline;
public class SplineSupport {
public SplineSupport() {
}
public static int findRangeForLocation(float[] locations, float x) {
int min = 0;
int i = locations.length;
while(i > 0) {
int j = i / 2;
int k = min + j;
if (x < locations[k]) {
i = j;
} else {
min = k + 1;
i -= j + 1;
}
}
return min - 1;
}
public static float sampleOutsideRange(float point, float[] locations, float value, float[] derivatives, int i) {
float f = derivatives[i];
return f == 0.0F ? value : value + f * (point - locations[i]);
}
}

View File

@@ -0,0 +1,49 @@
package org.bxteam.divinemc.dfc.common.ast.unary;
import org.bxteam.divinemc.dfc.common.ast.AstNode;
import org.bxteam.divinemc.dfc.common.ast.EvalType;
import org.bxteam.divinemc.dfc.common.gen.BytecodeGen;
import org.objectweb.asm.Type;
import org.objectweb.asm.commons.InstructionAdapter;
public class AbsNode extends AbstractUnaryNode {
public AbsNode(AstNode operand) {
super(operand);
}
protected AstNode newInstance(AstNode operand) {
return new AbsNode(operand);
}
public double evalSingle(int x, int y, int z, EvalType type) {
return Math.abs(this.operand.evalSingle(x, y, z, type));
}
public void evalMulti(double[] res, int[] x, int[] y, int[] z, EvalType type) {
this.operand.evalMulti(res, x, y, z, type);
for(int i = 0; i < res.length; ++i) {
res[i] = Math.abs(res[i]);
}
}
public void doBytecodeGenSingle(BytecodeGen.Context context, InstructionAdapter m, BytecodeGen.Context.LocalVarConsumer localVarConsumer) {
super.doBytecodeGenSingle(context, m, localVarConsumer);
m.invokestatic(Type.getInternalName(Math.class), "abs", Type.getMethodDescriptor(Type.DOUBLE_TYPE, new Type[]{Type.DOUBLE_TYPE}), false);
m.areturn(Type.DOUBLE_TYPE);
}
public void doBytecodeGenMulti(BytecodeGen.Context context, InstructionAdapter m, BytecodeGen.Context.LocalVarConsumer localVarConsumer) {
super.doBytecodeGenMulti(context, m, localVarConsumer);
context.doCountedLoop(m, localVarConsumer, (idx) -> {
m.load(1, InstructionAdapter.OBJECT_TYPE);
m.load(idx, Type.INT_TYPE);
m.dup2();
m.aload(Type.DOUBLE_TYPE);
m.invokestatic(Type.getInternalName(Math.class), "abs", Type.getMethodDescriptor(Type.DOUBLE_TYPE, new Type[]{Type.DOUBLE_TYPE}), false);
m.astore(Type.DOUBLE_TYPE);
});
m.areturn(Type.VOID_TYPE);
}
}

View File

@@ -0,0 +1,72 @@
package org.bxteam.divinemc.dfc.common.ast.unary;
import org.bxteam.divinemc.dfc.common.ast.AstNode;
import org.bxteam.divinemc.dfc.common.ast.AstTransformer;
import org.bxteam.divinemc.dfc.common.gen.BytecodeGen;
import java.util.Objects;
import org.objectweb.asm.commons.InstructionAdapter;
public abstract class AbstractUnaryNode implements AstNode {
protected final AstNode operand;
public AbstractUnaryNode(AstNode operand) {
this.operand = (AstNode)Objects.requireNonNull(operand);
}
public AstNode[] getChildren() {
return new AstNode[]{this.operand};
}
public boolean equals(Object o) {
if (this == o) {
return true;
} else if (o != null && this.getClass() == o.getClass()) {
AbstractUnaryNode that = (AbstractUnaryNode)o;
return Objects.equals(this.operand, that.operand);
} else {
return false;
}
}
public int hashCode() {
int result = 1;
result = 31 * result + this.getClass().hashCode();
result = 31 * result + this.operand.hashCode();
return result;
}
public boolean relaxedEquals(AstNode o) {
if (this == o) {
return true;
} else if (o != null && this.getClass() == o.getClass()) {
AbstractUnaryNode that = (AbstractUnaryNode)o;
return this.operand.relaxedEquals(that.operand);
} else {
return false;
}
}
public int relaxedHashCode() {
int result = 1;
result = 31 * result + this.getClass().hashCode();
result = 31 * result + this.operand.relaxedHashCode();
return result;
}
protected abstract AstNode newInstance(AstNode var1);
public AstNode transform(AstTransformer transformer) {
AstNode operand = this.operand.transform(transformer);
return this.operand == operand ? transformer.transform(this) : transformer.transform(this.newInstance(operand));
}
public void doBytecodeGenSingle(BytecodeGen.Context context, InstructionAdapter m, BytecodeGen.Context.LocalVarConsumer localVarConsumer) {
String operandMethod = context.newSingleMethod(this.operand);
context.callDelegateSingle(m, operandMethod);
}
public void doBytecodeGenMulti(BytecodeGen.Context context, InstructionAdapter m, BytecodeGen.Context.LocalVarConsumer localVarConsumer) {
String operandMethod = context.newMultiMethod(this.operand);
context.callDelegateMulti(m, operandMethod);
}
}

View File

@@ -0,0 +1,56 @@
package org.bxteam.divinemc.dfc.common.ast.unary;
import org.bxteam.divinemc.dfc.common.ast.AstNode;
import org.bxteam.divinemc.dfc.common.ast.EvalType;
import org.bxteam.divinemc.dfc.common.gen.BytecodeGen;
import org.objectweb.asm.Type;
import org.objectweb.asm.commons.InstructionAdapter;
public class CubeNode extends AbstractUnaryNode {
public CubeNode(AstNode operand) {
super(operand);
}
protected AstNode newInstance(AstNode operand) {
return new CubeNode(operand);
}
public double evalSingle(int x, int y, int z, EvalType type) {
double v = this.operand.evalSingle(x, y, z, type);
return v * v * v;
}
public void evalMulti(double[] res, int[] x, int[] y, int[] z, EvalType type) {
this.operand.evalMulti(res, x, y, z, type);
for(int i = 0; i < res.length; ++i) {
res[i] = res[i] * res[i] * res[i];
}
}
public void doBytecodeGenSingle(BytecodeGen.Context context, InstructionAdapter m, BytecodeGen.Context.LocalVarConsumer localVarConsumer) {
super.doBytecodeGenSingle(context, m, localVarConsumer);
m.dup2();
m.dup2();
m.mul(Type.DOUBLE_TYPE);
m.mul(Type.DOUBLE_TYPE);
m.areturn(Type.DOUBLE_TYPE);
}
public void doBytecodeGenMulti(BytecodeGen.Context context, InstructionAdapter m, BytecodeGen.Context.LocalVarConsumer localVarConsumer) {
super.doBytecodeGenMulti(context, m, localVarConsumer);
context.doCountedLoop(m, localVarConsumer, (idx) -> {
m.load(1, InstructionAdapter.OBJECT_TYPE);
m.load(idx, Type.INT_TYPE);
m.dup2();
m.aload(Type.DOUBLE_TYPE);
m.dup2();
m.dup2();
m.mul(Type.DOUBLE_TYPE);
m.mul(Type.DOUBLE_TYPE);
m.astore(Type.DOUBLE_TYPE);
});
m.areturn(Type.VOID_TYPE);
}
}

View File

@@ -0,0 +1,83 @@
package org.bxteam.divinemc.dfc.common.ast.unary;
import org.bxteam.divinemc.dfc.common.ast.AstNode;
import org.bxteam.divinemc.dfc.common.ast.EvalType;
import org.bxteam.divinemc.dfc.common.gen.BytecodeGen;
import org.objectweb.asm.Label;
import org.objectweb.asm.Type;
import org.objectweb.asm.commons.InstructionAdapter;
public class NegMulNode extends AbstractUnaryNode {
private final double negMul;
public NegMulNode(AstNode operand, double negMul) {
super(operand);
this.negMul = negMul;
}
protected AstNode newInstance(AstNode operand) {
return new NegMulNode(operand, this.negMul);
}
public double evalSingle(int x, int y, int z, EvalType type) {
double v = this.operand.evalSingle(x, y, z, type);
return v > 0.0 ? v : v * this.negMul;
}
public void evalMulti(double[] res, int[] x, int[] y, int[] z, EvalType type) {
this.operand.evalMulti(res, x, y, z, type);
for(int i = 0; i < res.length; ++i) {
double v = res[i];
res[i] = v > 0.0 ? v : v * this.negMul;
}
}
public void doBytecodeGenSingle(BytecodeGen.Context context, InstructionAdapter m, BytecodeGen.Context.LocalVarConsumer localVarConsumer) {
super.doBytecodeGenSingle(context, m, localVarConsumer);
int v = localVarConsumer.createLocalVariable("v", Type.DOUBLE_TYPE.getDescriptor());
m.store(v, Type.DOUBLE_TYPE);
Label negMulLabel = new Label();
Label end = new Label();
m.load(v, Type.DOUBLE_TYPE);
m.dconst(0.0);
m.cmpl(Type.DOUBLE_TYPE);
m.ifle(negMulLabel);
m.load(v, Type.DOUBLE_TYPE);
m.goTo(end);
m.visitLabel(negMulLabel);
m.load(v, Type.DOUBLE_TYPE);
m.dconst(this.negMul);
m.mul(Type.DOUBLE_TYPE);
m.visitLabel(end);
m.areturn(Type.DOUBLE_TYPE);
}
public void doBytecodeGenMulti(BytecodeGen.Context context, InstructionAdapter m, BytecodeGen.Context.LocalVarConsumer localVarConsumer) {
super.doBytecodeGenMulti(context, m, localVarConsumer);
context.doCountedLoop(m, localVarConsumer, (idx) -> {
int v = localVarConsumer.createLocalVariable("v", Type.DOUBLE_TYPE.getDescriptor());
m.load(1, InstructionAdapter.OBJECT_TYPE);
m.load(idx, Type.INT_TYPE);
m.dup2();
m.aload(Type.DOUBLE_TYPE);
m.store(v, Type.DOUBLE_TYPE);
Label negMulLabel = new Label();
Label end = new Label();
m.load(v, Type.DOUBLE_TYPE);
m.dconst(0.0);
m.cmpl(Type.DOUBLE_TYPE);
m.ifle(negMulLabel);
m.load(v, Type.DOUBLE_TYPE);
m.goTo(end);
m.visitLabel(negMulLabel);
m.load(v, Type.DOUBLE_TYPE);
m.dconst(this.negMul);
m.mul(Type.DOUBLE_TYPE);
m.visitLabel(end);
m.astore(Type.DOUBLE_TYPE);
});
m.areturn(Type.VOID_TYPE);
}
}

View File

@@ -0,0 +1,52 @@
package org.bxteam.divinemc.dfc.common.ast.unary;
import org.bxteam.divinemc.dfc.common.ast.AstNode;
import org.bxteam.divinemc.dfc.common.ast.EvalType;
import org.bxteam.divinemc.dfc.common.gen.BytecodeGen;
import org.objectweb.asm.Type;
import org.objectweb.asm.commons.InstructionAdapter;
public class SquareNode extends AbstractUnaryNode {
public SquareNode(AstNode operand) {
super(operand);
}
protected AstNode newInstance(AstNode operand) {
return new SquareNode(operand);
}
public double evalSingle(int x, int y, int z, EvalType type) {
double v = this.operand.evalSingle(x, y, z, type);
return v * v;
}
public void evalMulti(double[] res, int[] x, int[] y, int[] z, EvalType type) {
this.operand.evalMulti(res, x, y, z, type);
for(int i = 0; i < res.length; ++i) {
res[i] *= res[i];
}
}
public void doBytecodeGenSingle(BytecodeGen.Context context, InstructionAdapter m, BytecodeGen.Context.LocalVarConsumer localVarConsumer) {
super.doBytecodeGenSingle(context, m, localVarConsumer);
m.dup2();
m.mul(Type.DOUBLE_TYPE);
m.areturn(Type.DOUBLE_TYPE);
}
public void doBytecodeGenMulti(BytecodeGen.Context context, InstructionAdapter m, BytecodeGen.Context.LocalVarConsumer localVarConsumer) {
super.doBytecodeGenMulti(context, m, localVarConsumer);
context.doCountedLoop(m, localVarConsumer, (idx) -> {
m.load(1, InstructionAdapter.OBJECT_TYPE);
m.load(idx, Type.INT_TYPE);
m.dup2();
m.aload(Type.DOUBLE_TYPE);
m.dup2();
m.mul(Type.DOUBLE_TYPE);
m.astore(Type.DOUBLE_TYPE);
});
m.areturn(Type.VOID_TYPE);
}
}

View File

@@ -0,0 +1,84 @@
package org.bxteam.divinemc.dfc.common.ast.unary;
import org.bxteam.divinemc.dfc.common.ast.AstNode;
import org.bxteam.divinemc.dfc.common.ast.EvalType;
import org.bxteam.divinemc.dfc.common.gen.BytecodeGen;
import net.minecraft.util.Mth;
import org.objectweb.asm.Type;
import org.objectweb.asm.commons.InstructionAdapter;
public class SqueezeNode extends AbstractUnaryNode {
public SqueezeNode(AstNode operand) {
super(operand);
}
protected AstNode newInstance(AstNode operand) {
return new SqueezeNode(operand);
}
public double evalSingle(int x, int y, int z, EvalType type) {
double v = Mth.clamp(this.operand.evalSingle(x, y, z, type), -1.0, 1.0);
return v / 2.0 - v * v * v / 24.0;
}
public void evalMulti(double[] res, int[] x, int[] y, int[] z, EvalType type) {
this.operand.evalMulti(res, x, y, z, type);
for(int i = 0; i < res.length; ++i) {
double v = Mth.clamp(res[i], -1.0, 1.0);
res[i] = v / 2.0 - v * v * v / 24.0;
}
}
public void doBytecodeGenSingle(BytecodeGen.Context context, InstructionAdapter m, BytecodeGen.Context.LocalVarConsumer localVarConsumer) {
super.doBytecodeGenSingle(context, m, localVarConsumer);
m.dconst(1.0);
m.invokestatic(Type.getInternalName(Math.class), "min", Type.getMethodDescriptor(Type.DOUBLE_TYPE, new Type[]{Type.DOUBLE_TYPE, Type.DOUBLE_TYPE}), false);
m.dconst(-1.0);
m.invokestatic(Type.getInternalName(Math.class), "max", Type.getMethodDescriptor(Type.DOUBLE_TYPE, new Type[]{Type.DOUBLE_TYPE, Type.DOUBLE_TYPE}), false);
int v = localVarConsumer.createLocalVariable("v", Type.DOUBLE_TYPE.getDescriptor());
m.store(v, Type.DOUBLE_TYPE);
m.load(v, Type.DOUBLE_TYPE);
m.dconst(2.0);
m.div(Type.DOUBLE_TYPE);
m.load(v, Type.DOUBLE_TYPE);
m.dup2();
m.dup2();
m.mul(Type.DOUBLE_TYPE);
m.mul(Type.DOUBLE_TYPE);
m.dconst(24.0);
m.div(Type.DOUBLE_TYPE);
m.sub(Type.DOUBLE_TYPE);
m.areturn(Type.DOUBLE_TYPE);
}
public void doBytecodeGenMulti(BytecodeGen.Context context, InstructionAdapter m, BytecodeGen.Context.LocalVarConsumer localVarConsumer) {
super.doBytecodeGenMulti(context, m, localVarConsumer);
context.doCountedLoop(m, localVarConsumer, (idx) -> {
int v = localVarConsumer.createLocalVariable("v", Type.DOUBLE_TYPE.getDescriptor());
m.load(1, InstructionAdapter.OBJECT_TYPE);
m.load(idx, Type.INT_TYPE);
m.dup2();
m.aload(Type.DOUBLE_TYPE);
m.dconst(1.0);
m.invokestatic(Type.getInternalName(Math.class), "min", Type.getMethodDescriptor(Type.DOUBLE_TYPE, new Type[]{Type.DOUBLE_TYPE, Type.DOUBLE_TYPE}), false);
m.dconst(-1.0);
m.invokestatic(Type.getInternalName(Math.class), "max", Type.getMethodDescriptor(Type.DOUBLE_TYPE, new Type[]{Type.DOUBLE_TYPE, Type.DOUBLE_TYPE}), false);
m.store(v, Type.DOUBLE_TYPE);
m.load(v, Type.DOUBLE_TYPE);
m.dconst(2.0);
m.div(Type.DOUBLE_TYPE);
m.load(v, Type.DOUBLE_TYPE);
m.dup2();
m.dup2();
m.mul(Type.DOUBLE_TYPE);
m.mul(Type.DOUBLE_TYPE);
m.dconst(24.0);
m.div(Type.DOUBLE_TYPE);
m.sub(Type.DOUBLE_TYPE);
m.astore(Type.DOUBLE_TYPE);
});
m.areturn(Type.VOID_TYPE);
}
}

View File

@@ -0,0 +1,7 @@
package org.bxteam.divinemc.dfc.common.ducks;
import org.bxteam.divinemc.dfc.common.util.ArrayCache;
public interface IArrayCacheCapable {
ArrayCache c2me$getArrayCache();
}

View File

@@ -0,0 +1,5 @@
package org.bxteam.divinemc.dfc.common.ducks;
public interface IBlendingAwareVisitor {
boolean c2me$isBlendingEnabled();
}

View File

@@ -0,0 +1,5 @@
package org.bxteam.divinemc.dfc.common.ducks;
public interface ICoordinatesFilling {
void c2me$fillCoordinates(int[] var1, int[] var2, int[] var3);
}

View File

@@ -0,0 +1,7 @@
package org.bxteam.divinemc.dfc.common.ducks;
public interface IEqualityOverriding {
void c2me$overrideEquality(Object var1);
Object c2me$getOverriddenEquality();
}

View File

@@ -0,0 +1,20 @@
package org.bxteam.divinemc.dfc.common.ducks;
import org.bxteam.divinemc.dfc.common.ast.EvalType;
import net.minecraft.world.level.levelgen.DensityFunction;
public interface IFastCacheLike extends DensityFunction {
long CACHE_MISS_NAN_BITS = 9222769054270909007L;
double c2me$getCached(int var1, int var2, int var3, EvalType var4);
boolean c2me$getCached(double[] var1, int[] var2, int[] var3, int[] var4, EvalType var5);
void c2me$cache(int var1, int var2, int var3, EvalType var4, double var5);
void c2me$cache(double[] var1, int[] var2, int[] var3, int[] var4, EvalType var5);
DensityFunction c2me$getDelegate();
DensityFunction c2me$withDelegate(DensityFunction var1);
}

View File

@@ -0,0 +1,499 @@
package org.bxteam.divinemc.dfc.common.gen;
import com.google.common.io.Files;
import org.bxteam.divinemc.dfc.common.ast.AstNode;
import org.bxteam.divinemc.dfc.common.ast.EvalType;
import org.bxteam.divinemc.dfc.common.ast.McToAst;
import org.bxteam.divinemc.dfc.common.ast.dfvisitor.StripBlending;
import org.bxteam.divinemc.dfc.common.ast.misc.ConstantNode;
import org.bxteam.divinemc.dfc.common.ast.misc.RootNode;
import org.bxteam.divinemc.dfc.common.util.ArrayCache;
import org.bxteam.divinemc.dfc.common.vif.AstVanillaInterface;
import it.unimi.dsi.fastutil.Hash;
import it.unimi.dsi.fastutil.Pair;
import it.unimi.dsi.fastutil.ints.IntObjectPair;
import it.unimi.dsi.fastutil.objects.Object2ReferenceMap;
import it.unimi.dsi.fastutil.objects.Object2ReferenceMaps;
import it.unimi.dsi.fastutil.objects.Object2ReferenceOpenCustomHashMap;
import it.unimi.dsi.fastutil.objects.Object2ReferenceOpenHashMap;
import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet;
import it.unimi.dsi.fastutil.objects.Reference2ObjectOpenHashMap;
import it.unimi.dsi.fastutil.objects.Reference2ReferenceMap;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicLong;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.function.IntConsumer;
import java.util.stream.Collectors;
import net.minecraft.util.CubicSpline;
import net.minecraft.world.level.levelgen.DensityFunction;
import net.minecraft.world.level.levelgen.DensityFunctions;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.Label;
import org.objectweb.asm.Type;
import org.objectweb.asm.commons.AnalyzerAdapter;
import org.objectweb.asm.commons.InstructionAdapter;
public class BytecodeGen {
private static final File exportDir = new File("./cache/c2me-dfc");
private static final AtomicLong ordinal = new AtomicLong();
public static final Hash.Strategy<AstNode> RELAXED_STRATEGY;
private static final Object2ReferenceMap<AstNode, Class<?>> compilationCache;
public BytecodeGen() {
}
public static DensityFunction compile(DensityFunction densityFunction, Reference2ReferenceMap<DensityFunction, DensityFunction> tempCache) {
DensityFunction cached = (DensityFunction)tempCache.get(densityFunction);
if (cached != null) {
return cached;
} else if (densityFunction instanceof AstVanillaInterface) {
AstVanillaInterface vif = (AstVanillaInterface)densityFunction;
AstNode ast = vif.getAstNode();
return new CompiledDensityFunction(compile0(ast), vif.getBlendingFallback());
} else {
AstNode ast = McToAst.toAst(densityFunction.mapAll(StripBlending.INSTANCE));
if (ast instanceof ConstantNode) {
ConstantNode constantNode = (ConstantNode)ast;
return DensityFunctions.constant(constantNode.getValue());
} else {
CompiledDensityFunction compiled = new CompiledDensityFunction(compile0(ast), densityFunction);
tempCache.put(densityFunction, compiled);
return compiled;
}
}
}
public static synchronized CompiledEntry compile0(AstNode node) {
Class<?> cached = (Class)compilationCache.get(node);
ClassWriter writer = new ClassWriter(3);
String name = cached != null ? String.format("DfcCompiled_discarded") : String.format("DfcCompiled_%d", ordinal.getAndIncrement());
writer.visit(65, 17, name, (String)null, Type.getInternalName(Object.class), new String[]{Type.getInternalName(CompiledEntry.class)});
RootNode rootNode = new RootNode(node);
Context genContext = new Context(writer, name);
genContext.newSingleMethod0((adapter, localVarConsumer) -> {
rootNode.doBytecodeGenSingle(genContext, adapter, localVarConsumer);
}, "evalSingle", true);
genContext.newMultiMethod0((adapter, localVarConsumer) -> {
rootNode.doBytecodeGenMulti(genContext, adapter, localVarConsumer);
}, "evalMulti", true);
List<Object> args = (List)genContext.args.entrySet().stream().sorted(Comparator.comparingInt((o) -> {
return ((Context.FieldRecord)o.getValue()).ordinal();
})).map(Map.Entry::getKey).collect(Collectors.toCollection(ArrayList::new));
if (cached != null) {
try {
return (CompiledEntry)cached.getConstructor(List.class).newInstance(args);
} catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException | InstantiationException var11) {
ReflectiveOperationException e = var11;
throw new RuntimeException(e);
}
} else {
genConstructor(genContext);
genGetArgs(genContext);
genNewInstance(genContext);
Object var8;
for(ListIterator<Object> iterator = args.listIterator(); iterator.hasNext(); var8 = iterator.next()) {
}
byte[] bytes = writer.toByteArray();
dumpClass(genContext.className, bytes);
Class<?> defined = defineClass(genContext.className, bytes);
compilationCache.put(node, defined);
try {
return (CompiledEntry)defined.getConstructor(List.class).newInstance(args);
} catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException | InstantiationException var12) {
ReflectiveOperationException e = var12;
throw new RuntimeException(e);
}
}
}
private static void genConstructor(Context context) {
InstructionAdapter m = new InstructionAdapter(new AnalyzerAdapter(context.className, 1, "<init>", Type.getMethodDescriptor(Type.VOID_TYPE, new Type[]{Type.getType(List.class)}), context.classWriter.visitMethod(1, "<init>", Type.getMethodDescriptor(Type.VOID_TYPE, new Type[]{Type.getType(List.class)}), (String)null, (String[])null)));
Label start = new Label();
Label end = new Label();
m.visitLabel(start);
m.load(0, InstructionAdapter.OBJECT_TYPE);
m.invokespecial(Type.getInternalName(Object.class), "<init>", Type.getMethodDescriptor(Type.VOID_TYPE, new Type[0]), false);
Iterator var4 = context.args.entrySet().stream().sorted(Comparator.comparingInt((o) -> {
return ((Context.FieldRecord)o.getValue()).ordinal();
})).toList().iterator();
while(var4.hasNext()) {
Map.Entry<Object, Context.FieldRecord> entry = (Map.Entry)var4.next();
String name = ((Context.FieldRecord)entry.getValue()).name();
Class<?> type = ((Context.FieldRecord)entry.getValue()).type();
int ordinal = ((Context.FieldRecord)entry.getValue()).ordinal();
m.load(0, InstructionAdapter.OBJECT_TYPE);
m.load(1, InstructionAdapter.OBJECT_TYPE);
m.iconst(ordinal);
m.invokeinterface(Type.getInternalName(List.class), "get", Type.getMethodDescriptor(InstructionAdapter.OBJECT_TYPE, new Type[]{Type.INT_TYPE}));
m.checkcast(Type.getType(type));
m.putfield(context.className, name, Type.getDescriptor(type));
}
var4 = context.postProcessMethods.stream().sorted().toList().iterator();
while(var4.hasNext()) {
String postProcessingMethod = (String)var4.next();
m.load(0, InstructionAdapter.OBJECT_TYPE);
m.invokevirtual(context.className, postProcessingMethod, "()V", false);
}
m.areturn(Type.VOID_TYPE);
m.visitLabel(end);
m.visitLocalVariable("this", context.classDesc, (String)null, start, end, 0);
m.visitLocalVariable("list", Type.getDescriptor(List.class), (String)null, start, end, 1);
m.visitMaxs(0, 0);
}
private static void genGetArgs(Context context) {
InstructionAdapter m = new InstructionAdapter(new AnalyzerAdapter(context.className, 17, "getArgs", Type.getMethodDescriptor(Type.getType(List.class), new Type[0]), context.classWriter.visitMethod(17, "getArgs", Type.getMethodDescriptor(Type.getType(List.class), new Type[0]), (String)null, (String[])null)));
Label start = new Label();
Label end = new Label();
m.visitLabel(start);
m.anew(Type.getType(ArrayList.class));
m.dup();
m.iconst(context.args.size());
m.invokespecial(Type.getInternalName(ArrayList.class), "<init>", Type.getMethodDescriptor(Type.VOID_TYPE, new Type[]{Type.INT_TYPE}), false);
m.store(1, InstructionAdapter.OBJECT_TYPE);
Iterator var4 = context.args.entrySet().stream().sorted(Comparator.comparingInt((o) -> {
return ((Context.FieldRecord)o.getValue()).ordinal();
})).toList().iterator();
while(var4.hasNext()) {
Map.Entry<Object, Context.FieldRecord> entry = (Map.Entry)var4.next();
String name = ((Context.FieldRecord)entry.getValue()).name();
Class<?> type = ((Context.FieldRecord)entry.getValue()).type();
m.load(1, InstructionAdapter.OBJECT_TYPE);
m.load(0, InstructionAdapter.OBJECT_TYPE);
m.getfield(context.className, name, Type.getDescriptor(type));
m.invokeinterface(Type.getInternalName(List.class), "add", Type.getMethodDescriptor(Type.BOOLEAN_TYPE, new Type[]{InstructionAdapter.OBJECT_TYPE}));
m.pop();
}
m.load(1, InstructionAdapter.OBJECT_TYPE);
m.areturn(InstructionAdapter.OBJECT_TYPE);
m.visitLabel(end);
m.visitLocalVariable("this", context.classDesc, (String)null, start, end, 0);
m.visitLocalVariable("list", Type.getDescriptor(List.class), (String)null, start, end, 1);
m.visitMaxs(0, 0);
}
private static void genNewInstance(Context context) {
InstructionAdapter m = new InstructionAdapter(new AnalyzerAdapter(context.className, 17, "newInstance", Type.getMethodDescriptor(Type.getType(CompiledEntry.class), new Type[]{Type.getType(List.class)}), context.classWriter.visitMethod(17, "newInstance", Type.getMethodDescriptor(Type.getType(CompiledEntry.class), new Type[]{Type.getType(List.class)}), (String)null, (String[])null)));
Label start = new Label();
Label end = new Label();
m.visitLabel(start);
m.anew(Type.getType(context.classDesc));
m.dup();
m.load(1, InstructionAdapter.OBJECT_TYPE);
m.invokespecial(context.className, "<init>", Type.getMethodDescriptor(Type.VOID_TYPE, new Type[]{Type.getType(List.class)}), false);
m.areturn(InstructionAdapter.OBJECT_TYPE);
m.visitLabel(end);
m.visitLocalVariable("this", context.classDesc, (String)null, start, end, 0);
m.visitLocalVariable("list", Type.getDescriptor(List.class), (String)null, start, end, 1);
m.visitMaxs(0, 0);
}
private static void dumpClass(String className, byte[] bytes) {
File outputFile = new File(exportDir, className + ".class");
outputFile.getParentFile().mkdirs();
try {
Files.write(bytes, outputFile);
} catch (IOException var4) {
IOException e = var4;
e.printStackTrace();
}
}
private static Class<?> defineClass(final String className, final byte[] bytes) {
ClassLoader classLoader = new ClassLoader(BytecodeGen.class.getClassLoader()) {
public Class<?> loadClass(String name) throws ClassNotFoundException {
return name.equals(className) ? super.defineClass(name, bytes, 0, bytes.length) : super.loadClass(name);
}
};
try {
return classLoader.loadClass(className);
} catch (ClassNotFoundException var4) {
ClassNotFoundException e = var4;
throw new RuntimeException(e);
}
}
static {
try {
org.bxteam.divinemc.util.Files.deleteRecursively(exportDir);
} catch (IOException var1) {
IOException e = var1;
e.printStackTrace();
}
RELAXED_STRATEGY = new Hash.Strategy<AstNode>() {
public int hashCode(AstNode o) {
return o.relaxedHashCode();
}
public boolean equals(AstNode a, AstNode b) {
return a.relaxedEquals(b);
}
};
compilationCache = Object2ReferenceMaps.synchronize(new Object2ReferenceOpenCustomHashMap<>(RELAXED_STRATEGY));
}
public static class Context {
public static final String SINGLE_DESC;
public static final String MULTI_DESC;
public final ClassWriter classWriter;
public final String className;
public final String classDesc;
private int methodIdx = 0;
private final Object2ReferenceOpenHashMap<AstNode, String> singleMethods = new Object2ReferenceOpenHashMap<>();
private final Object2ReferenceOpenHashMap<AstNode, String> multiMethods = new Object2ReferenceOpenHashMap<>();
private final Object2ReferenceOpenHashMap<CubicSpline<DensityFunctions.Spline.Point, DensityFunctions.Spline.Coordinate>, String> splineMethods = new Object2ReferenceOpenHashMap<>();
private final ObjectOpenHashSet<String> postProcessMethods = new ObjectOpenHashSet<>();
private final Reference2ObjectOpenHashMap<Object, FieldRecord> args = new Reference2ObjectOpenHashMap<>();
public Context(ClassWriter classWriter, String className) {
this.classWriter = (ClassWriter)Objects.requireNonNull(classWriter);
this.className = (String)Objects.requireNonNull(className);
this.classDesc = String.format("L%s;", this.className);
}
public String nextMethodName() {
return String.format("method_%d", this.methodIdx++);
}
public String nextMethodName(String suffix) {
return String.format("method_%d_%s", this.methodIdx++, suffix);
}
public String newSingleMethod(AstNode node) {
return this.singleMethods.computeIfAbsent(node, (AstNode node1) -> this.newSingleMethod((adapter, localVarConsumer) -> node1.doBytecodeGenSingle(this, adapter, localVarConsumer), nextMethodName(node.getClass().getSimpleName())));
}
public String newSingleMethod(BiConsumer<InstructionAdapter, LocalVarConsumer> generator) {
return this.newSingleMethod(generator, this.nextMethodName());
}
public String newSingleMethod(BiConsumer<InstructionAdapter, LocalVarConsumer> generator, String name) {
this.newSingleMethod0(generator, name, false);
return name;
}
private void newSingleMethod0(BiConsumer<InstructionAdapter, LocalVarConsumer> generator, String name, boolean isPublic) {
InstructionAdapter adapter = new InstructionAdapter(new AnalyzerAdapter(this.className, (isPublic ? 1 : 2) | 16, name, SINGLE_DESC, this.classWriter.visitMethod((isPublic ? 1 : 2) | 16, name, SINGLE_DESC, (String)null, (String[])null)));
List<IntObjectPair<Pair<String, String>>> extraLocals = new ArrayList<>();
Label start = new Label();
Label end = new Label();
adapter.visitLabel(start);
generator.accept(adapter, (localName, localDesc) -> {
int ordinal = extraLocals.size() + 5;
extraLocals.add(IntObjectPair.of(ordinal, Pair.of(localName, localDesc)));
return ordinal;
});
adapter.visitLabel(end);
adapter.visitLocalVariable("this", this.classDesc, (String)null, start, end, 0);
adapter.visitLocalVariable("x", Type.INT_TYPE.getDescriptor(), (String)null, start, end, 1);
adapter.visitLocalVariable("y", Type.INT_TYPE.getDescriptor(), (String)null, start, end, 2);
adapter.visitLocalVariable("z", Type.INT_TYPE.getDescriptor(), (String)null, start, end, 3);
adapter.visitLocalVariable("evalType", Type.getType(EvalType.class).getDescriptor(), (String)null, start, end, 4);
Iterator var8 = extraLocals.iterator();
while(var8.hasNext()) {
IntObjectPair<Pair<String, String>> local = (IntObjectPair)var8.next();
adapter.visitLocalVariable((String)((Pair)local.right()).left(), (String)((Pair)local.right()).right(), (String)null, start, end, local.leftInt());
}
adapter.visitMaxs(0, 0);
}
public String newMultiMethod(AstNode node) {
return this.multiMethods.computeIfAbsent(node, (AstNode node1) -> this.newMultiMethod((adapter, localVarConsumer) -> node1.doBytecodeGenMulti(this, adapter, localVarConsumer), nextMethodName(node.getClass().getSimpleName())));
}
public String newMultiMethod(BiConsumer<InstructionAdapter, LocalVarConsumer> generator) {
return this.newMultiMethod(generator, this.nextMethodName());
}
public String newMultiMethod(BiConsumer<InstructionAdapter, LocalVarConsumer> generator, String name) {
this.newMultiMethod0(generator, name, false);
return name;
}
private void newMultiMethod0(BiConsumer<InstructionAdapter, LocalVarConsumer> generator, String name, boolean isPublic) {
InstructionAdapter adapter = new InstructionAdapter(new AnalyzerAdapter(this.className, (isPublic ? 1 : 2) | 16, name, MULTI_DESC, this.classWriter.visitMethod((isPublic ? 1 : 2) | 16, name, MULTI_DESC, (String)null, (String[])null)));
List<IntObjectPair<Pair<String, String>>> extraLocals = new ArrayList();
Label start = new Label();
Label end = new Label();
adapter.visitLabel(start);
generator.accept(adapter, (localName, localDesc) -> {
int ordinal = extraLocals.size() + 7;
extraLocals.add(IntObjectPair.of(ordinal, Pair.of(localName, localDesc)));
return ordinal;
});
adapter.visitLabel(end);
adapter.visitLocalVariable("this", this.classDesc, (String)null, start, end, 0);
adapter.visitLocalVariable("res", Type.getType(double[].class).getDescriptor(), (String)null, start, end, 1);
adapter.visitLocalVariable("x", Type.getType(double[].class).getDescriptor(), (String)null, start, end, 2);
adapter.visitLocalVariable("y", Type.getType(double[].class).getDescriptor(), (String)null, start, end, 3);
adapter.visitLocalVariable("z", Type.getType(double[].class).getDescriptor(), (String)null, start, end, 4);
adapter.visitLocalVariable("evalType", Type.getType(EvalType.class).getDescriptor(), (String)null, start, end, 5);
adapter.visitLocalVariable("arrayCache", Type.getType(ArrayCache.class).getDescriptor(), (String)null, start, end, 6);
Iterator var8 = extraLocals.iterator();
while(var8.hasNext()) {
IntObjectPair<Pair<String, String>> local = (IntObjectPair)var8.next();
adapter.visitLocalVariable((String)((Pair)local.right()).left(), (String)((Pair)local.right()).right(), (String)null, start, end, local.leftInt());
}
adapter.visitMaxs(0, 0);
}
public String getCachedSplineMethod(CubicSpline<DensityFunctions.Spline.Point, DensityFunctions.Spline.Coordinate> spline) {
return (String)this.splineMethods.get(spline);
}
public void cacheSplineMethod(CubicSpline<DensityFunctions.Spline.Point, DensityFunctions.Spline.Coordinate> spline, String method) {
this.splineMethods.put(spline, method);
}
public void callDelegateSingle(InstructionAdapter m, String target) {
m.load(0, InstructionAdapter.OBJECT_TYPE);
m.load(1, Type.INT_TYPE);
m.load(2, Type.INT_TYPE);
m.load(3, Type.INT_TYPE);
m.load(4, InstructionAdapter.OBJECT_TYPE);
m.invokevirtual(this.className, target, SINGLE_DESC, false);
}
public void callDelegateMulti(InstructionAdapter m, String target) {
m.load(0, InstructionAdapter.OBJECT_TYPE);
m.load(1, InstructionAdapter.OBJECT_TYPE);
m.load(2, InstructionAdapter.OBJECT_TYPE);
m.load(3, InstructionAdapter.OBJECT_TYPE);
m.load(4, InstructionAdapter.OBJECT_TYPE);
m.load(5, InstructionAdapter.OBJECT_TYPE);
m.load(6, InstructionAdapter.OBJECT_TYPE);
m.invokevirtual(this.className, target, MULTI_DESC, false);
}
public <T> String newField(Class<T> type, T data) {
FieldRecord existing = (FieldRecord)this.args.get(data);
if (existing != null) {
return existing.name();
} else {
int size = this.args.size();
String name = String.format("field_%d", size);
this.classWriter.visitField(2, name, Type.getDescriptor(type), (String)null, (Object)null);
this.args.put(data, new FieldRecord(name, size, type));
return name;
}
}
public void doCountedLoop(InstructionAdapter m, LocalVarConsumer localVarConsumer, IntConsumer bodyGenerator) {
int loopIdx = localVarConsumer.createLocalVariable("loopIdx", Type.INT_TYPE.getDescriptor());
m.iconst(0);
m.store(loopIdx, Type.INT_TYPE);
Label start = new Label();
Label end = new Label();
m.visitLabel(start);
m.load(loopIdx, Type.INT_TYPE);
m.load(1, InstructionAdapter.OBJECT_TYPE);
m.arraylength();
m.ificmpge(end);
bodyGenerator.accept(loopIdx);
m.iinc(loopIdx, 1);
m.goTo(start);
m.visitLabel(end);
}
public void delegateToSingle(InstructionAdapter m, LocalVarConsumer localVarConsumer, AstNode current) {
String singleMethod = this.newSingleMethod(current);
this.doCountedLoop(m, localVarConsumer, (idx) -> {
m.load(1, InstructionAdapter.OBJECT_TYPE);
m.load(idx, Type.INT_TYPE);
m.load(0, InstructionAdapter.OBJECT_TYPE);
m.load(2, InstructionAdapter.OBJECT_TYPE);
m.load(idx, Type.INT_TYPE);
m.aload(Type.INT_TYPE);
m.load(3, InstructionAdapter.OBJECT_TYPE);
m.load(idx, Type.INT_TYPE);
m.aload(Type.INT_TYPE);
m.load(4, InstructionAdapter.OBJECT_TYPE);
m.load(idx, Type.INT_TYPE);
m.aload(Type.INT_TYPE);
m.load(5, InstructionAdapter.OBJECT_TYPE);
m.invokevirtual(this.className, singleMethod, SINGLE_DESC, false);
m.astore(Type.DOUBLE_TYPE);
});
}
public void genPostprocessingMethod(String name, Consumer<InstructionAdapter> generator) {
if (!this.postProcessMethods.contains(name)) {
InstructionAdapter adapter = new InstructionAdapter(new AnalyzerAdapter(this.className, 18, name, "()V", this.classWriter.visitMethod(18, name, "()V", (String)null, (String[])null)));
Label start = new Label();
Label end = new Label();
adapter.visitLabel(start);
generator.accept(adapter);
adapter.visitLabel(end);
adapter.visitMaxs(0, 0);
adapter.visitLocalVariable("this", this.classDesc, (String)null, start, end, 0);
this.postProcessMethods.add(name);
}
}
static {
SINGLE_DESC = Type.getMethodDescriptor(Type.getType(Double.TYPE), new Type[]{Type.getType(Integer.TYPE), Type.getType(Integer.TYPE), Type.getType(Integer.TYPE), Type.getType(EvalType.class)});
MULTI_DESC = Type.getMethodDescriptor(Type.VOID_TYPE, new Type[]{Type.getType(double[].class), Type.getType(int[].class), Type.getType(int[].class), Type.getType(int[].class), Type.getType(EvalType.class), Type.getType(ArrayCache.class)});
}
public interface LocalVarConsumer {
int createLocalVariable(String var1, String var2);
}
private static record FieldRecord(String name, int ordinal, Class<?> type) {
private FieldRecord(String name, int ordinal, Class<?> type) {
this.name = name;
this.ordinal = ordinal;
this.type = type;
}
public String name() {
return this.name;
}
public int ordinal() {
return this.ordinal;
}
public Class<?> type() {
return this.type;
}
}
}
@FunctionalInterface
public interface EvalMultiInterface {
void evalMulti(double[] var1, int[] var2, int[] var3, int[] var4, EvalType var5);
}
@FunctionalInterface
public interface EvalSingleInterface {
double evalSingle(int var1, int var2, int var3, EvalType var4);
}
}

View File

@@ -0,0 +1,98 @@
package org.bxteam.divinemc.dfc.common.gen;
import com.google.common.base.Suppliers;
import org.bxteam.divinemc.dfc.common.ducks.IBlendingAwareVisitor;
import org.bxteam.divinemc.dfc.common.ducks.IFastCacheLike;
import java.util.List;
import java.util.ListIterator;
import java.util.Objects;
import java.util.function.Supplier;
import net.minecraft.world.level.levelgen.DensityFunction;
public class CompiledDensityFunction extends SubCompiledDensityFunction {
private final CompiledEntry compiledEntry;
public CompiledDensityFunction(CompiledEntry compiledEntry, DensityFunction blendingFallback) {
super(compiledEntry, compiledEntry, blendingFallback);
this.compiledEntry = (CompiledEntry)Objects.requireNonNull(compiledEntry);
}
private CompiledDensityFunction(CompiledEntry compiledEntry, Supplier<DensityFunction> blendingFallback) {
super(compiledEntry, compiledEntry, blendingFallback);
this.compiledEntry = (CompiledEntry)Objects.requireNonNull(compiledEntry);
}
public DensityFunction mapAll(Visitor visitor) {
if (visitor instanceof IBlendingAwareVisitor blendingAwareVisitor) {
if (blendingAwareVisitor.c2me$isBlendingEnabled()) {
DensityFunction fallback1 = this.getFallback();
if (fallback1 == null) {
throw new IllegalStateException("blendingFallback is no more");
}
return fallback1.mapAll(visitor);
}
}
boolean modified = false;
List<Object> args = this.compiledEntry.getArgs();
ListIterator<Object> iterator = args.listIterator();
Object next;
while(iterator.hasNext()) {
next = iterator.next();
if (next instanceof DensityFunction df) {
if (!(df instanceof IFastCacheLike)) {
DensityFunction applied = df.mapAll(visitor);
if (df != applied) {
iterator.set(applied);
modified = true;
}
}
}
if (next instanceof NoiseHolder noise) {
NoiseHolder applied = visitor.visitNoise(noise);
if (noise != applied) {
iterator.set(applied);
modified = true;
}
}
}
iterator = args.listIterator();
while(iterator.hasNext()) {
next = iterator.next();
if (next instanceof IFastCacheLike cacheLike) {
DensityFunction applied = visitor.apply(cacheLike);
if (applied == cacheLike.c2me$getDelegate()) {
iterator.set((Object)null);
modified = true;
} else {
if (!(applied instanceof IFastCacheLike)) {
throw new UnsupportedOperationException("Unsupported transformation on Wrapping node");
}
IFastCacheLike newCacheLike = (IFastCacheLike)applied;
iterator.set(newCacheLike);
modified = true;
}
}
}
Supplier<DensityFunction> fallback = this.blendingFallback != null ? Suppliers.memoize(() -> {
DensityFunction densityFunction = (DensityFunction)this.blendingFallback.get();
return densityFunction != null ? densityFunction.mapAll(visitor) : null;
}) : null;
if (fallback != this.blendingFallback) {
modified = true;
}
if (modified) {
return new CompiledDensityFunction(this.compiledEntry.newInstance(args), fallback);
} else {
return this;
}
}
}

View File

@@ -0,0 +1,15 @@
package org.bxteam.divinemc.dfc.common.gen;
import org.bxteam.divinemc.dfc.common.ast.EvalType;
import org.bxteam.divinemc.dfc.common.util.ArrayCache;
import java.util.List;
public interface CompiledEntry extends ISingleMethod, IMultiMethod {
double evalSingle(int var1, int var2, int var3, EvalType var4);
void evalMulti(double[] var1, int[] var2, int[] var3, int[] var4, EvalType var5, ArrayCache var6);
CompiledEntry newInstance(List<?> var1);
List<Object> getArgs();
}

View File

@@ -0,0 +1,28 @@
package org.bxteam.divinemc.dfc.common.gen;
import org.bxteam.divinemc.dfc.common.ducks.IBlendingAwareVisitor;
import java.util.Objects;
import net.minecraft.world.level.levelgen.DensityFunction;
import org.jetbrains.annotations.NotNull;
public class DelegatingBlendingAwareVisitor implements IBlendingAwareVisitor, DensityFunction.Visitor {
private final DensityFunction.Visitor delegate;
private final boolean blendingEnabled;
public DelegatingBlendingAwareVisitor(DensityFunction.Visitor delegate, boolean blendingEnabled) {
this.delegate = Objects.requireNonNull(delegate);
this.blendingEnabled = blendingEnabled;
}
public @NotNull DensityFunction apply(@NotNull DensityFunction densityFunction) {
return this.delegate.apply(densityFunction);
}
public DensityFunction.@NotNull NoiseHolder visitNoise(DensityFunction.@NotNull NoiseHolder noiseDensityFunction) {
return this.delegate.visitNoise(noiseDensityFunction);
}
public boolean c2me$isBlendingEnabled() {
return this.blendingEnabled;
}
}

View File

@@ -0,0 +1,9 @@
package org.bxteam.divinemc.dfc.common.gen;
import org.bxteam.divinemc.dfc.common.ast.EvalType;
import org.bxteam.divinemc.dfc.common.util.ArrayCache;
@FunctionalInterface
public interface IMultiMethod {
void evalMulti(double[] var1, int[] var2, int[] var3, int[] var4, EvalType var5, ArrayCache var6);
}

View File

@@ -0,0 +1,8 @@
package org.bxteam.divinemc.dfc.common.gen;
import org.bxteam.divinemc.dfc.common.ast.EvalType;
@FunctionalInterface
public interface ISingleMethod {
double evalSingle(int var1, int var2, int var3, EvalType var4);
}

View File

@@ -0,0 +1,142 @@
package org.bxteam.divinemc.dfc.common.gen;
import com.google.common.base.Suppliers;
import org.bxteam.divinemc.dfc.common.ast.EvalType;
import org.bxteam.divinemc.dfc.common.ducks.IArrayCacheCapable;
import org.bxteam.divinemc.dfc.common.ducks.IBlendingAwareVisitor;
import org.bxteam.divinemc.dfc.common.ducks.ICoordinatesFilling;
import org.bxteam.divinemc.dfc.common.util.ArrayCache;
import org.bxteam.divinemc.dfc.common.vif.EachApplierVanillaInterface;
import java.util.Objects;
import java.util.function.Supplier;
import net.minecraft.util.KeyDispatchDataCodec;
import net.minecraft.world.level.levelgen.DensityFunction;
import net.minecraft.world.level.levelgen.NoiseChunk;
import net.minecraft.world.level.levelgen.blending.Blender;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class SubCompiledDensityFunction implements DensityFunction {
private static final Logger LOGGER = LoggerFactory.getLogger(SubCompiledDensityFunction.class);
private final ISingleMethod singleMethod;
private final IMultiMethod multiMethod;
protected final Supplier<DensityFunction> blendingFallback;
public SubCompiledDensityFunction(ISingleMethod singleMethod, IMultiMethod multiMethod, DensityFunction blendingFallback) {
this(singleMethod, multiMethod, unwrap(blendingFallback));
}
protected SubCompiledDensityFunction(ISingleMethod singleMethod, IMultiMethod multiMethod, Supplier<DensityFunction> blendingFallback) {
this.singleMethod = (ISingleMethod)Objects.requireNonNull(singleMethod);
this.multiMethod = (IMultiMethod)Objects.requireNonNull(multiMethod);
this.blendingFallback = blendingFallback;
}
private static Supplier<DensityFunction> unwrap(DensityFunction densityFunction) {
if (densityFunction instanceof SubCompiledDensityFunction scdf) {
return scdf.blendingFallback;
} else {
return densityFunction != null ? Suppliers.ofInstance(densityFunction) : null;
}
}
public double compute(FunctionContext pos) {
if (pos.getBlender() != Blender.empty()) {
DensityFunction fallback = this.getFallback();
if (fallback == null) {
throw new IllegalStateException("blendingFallback is no more");
} else {
return fallback.compute(pos);
}
} else {
return this.singleMethod.evalSingle(pos.blockX(), pos.blockY(), pos.blockZ(), EvalType.from(pos));
}
}
public void fillArray(double[] densities, ContextProvider applier) {
if (applier instanceof NoiseChunk sampler) {
if (sampler.getBlender() != Blender.empty()) {
DensityFunction fallback = this.getFallback();
if (fallback == null) {
throw new IllegalStateException("blendingFallback is no more");
}
fallback.fillArray(densities, applier);
return;
}
}
if (applier instanceof EachApplierVanillaInterface vanillaInterface) {
this.multiMethod.evalMulti(densities, vanillaInterface.getX(), vanillaInterface.getY(), vanillaInterface.getZ(), EvalType.from(applier), vanillaInterface.c2me$getArrayCache());
} else {
ArrayCache var10000;
if (applier instanceof IArrayCacheCapable cacheCapable) {
var10000 = cacheCapable.c2me$getArrayCache();
} else {
var10000 = new ArrayCache();
}
ArrayCache cache = var10000;
int[] x = cache.getIntArray(densities.length, false);
int[] y = cache.getIntArray(densities.length, false);
int[] z = cache.getIntArray(densities.length, false);
if (applier instanceof ICoordinatesFilling coordinatesFilling) {
coordinatesFilling.c2me$fillCoordinates(x, y, z);
} else {
for(int i = 0; i < densities.length; ++i) {
FunctionContext pos = applier.forIndex(i);
x[i] = pos.blockX();
y[i] = pos.blockY();
z[i] = pos.blockZ();
}
}
this.multiMethod.evalMulti(densities, x, y, z, EvalType.from(applier), cache);
}
}
public DensityFunction mapAll(Visitor visitor) {
if (this.getClass() != SubCompiledDensityFunction.class) {
throw new AbstractMethodError();
} else {
if (visitor instanceof IBlendingAwareVisitor) {
IBlendingAwareVisitor blendingAwareVisitor = (IBlendingAwareVisitor)visitor;
if (blendingAwareVisitor.c2me$isBlendingEnabled()) {
DensityFunction fallback1 = this.getFallback();
if (fallback1 == null) {
throw new IllegalStateException("blendingFallback is no more");
}
return fallback1.mapAll(visitor);
}
}
boolean modified = false;
Supplier<DensityFunction> fallback = this.blendingFallback != null ? Suppliers.memoize(() -> {
DensityFunction densityFunction = (DensityFunction)this.blendingFallback.get();
return densityFunction != null ? densityFunction.mapAll(visitor) : null;
}) : null;
if (fallback != this.blendingFallback) {
modified = true;
}
return modified ? new SubCompiledDensityFunction(this.singleMethod, this.multiMethod, fallback) : this;
}
}
public double minValue() {
return Double.MIN_VALUE;
}
public double maxValue() {
return Double.MAX_VALUE;
}
public KeyDispatchDataCodec<? extends DensityFunction> codec() {
throw new UnsupportedOperationException();
}
protected DensityFunction getFallback() {
return this.blendingFallback != null ? (DensityFunction)this.blendingFallback.get() : null;
}
}

View File

@@ -0,0 +1,57 @@
package org.bxteam.divinemc.dfc.common.util;
import it.unimi.dsi.fastutil.ints.Int2ReferenceArrayMap;
import it.unimi.dsi.fastutil.objects.ReferenceArrayList;
import java.util.Arrays;
public class ArrayCache {
private final Int2ReferenceArrayMap<ReferenceArrayList<double[]>> doubleArrayCache = new Int2ReferenceArrayMap<>();
private final Int2ReferenceArrayMap<ReferenceArrayList<int[]>> intArrayCache = new Int2ReferenceArrayMap<>();
public ArrayCache() {
}
public double[] getDoubleArray(int size, boolean zero) {
ReferenceArrayList<double[]> list = this.doubleArrayCache.computeIfAbsent(size, (k) -> {
return new ReferenceArrayList<>();
});
if (list.isEmpty()) {
return new double[size];
} else {
double[] popped = list.pop();
if (zero) {
Arrays.fill(popped, 0.0);
}
return popped;
}
}
public int[] getIntArray(int size, boolean zero) {
ReferenceArrayList<int[]> list = this.intArrayCache.computeIfAbsent(size, (k) -> {
return new ReferenceArrayList<>();
});
if (list.isEmpty()) {
return new int[size];
} else {
int[] popped = list.pop();
if (zero) {
Arrays.fill(popped, 0);
}
return popped;
}
}
public void recycle(double[] array) {
this.doubleArrayCache.computeIfAbsent(array.length, (k) -> {
return new ReferenceArrayList<>();
}).add(array);
}
public void recycle(int[] array) {
this.intArrayCache.computeIfAbsent(array.length, (k) -> {
return new ReferenceArrayList<>();
}).add(array);
}
}

View File

@@ -0,0 +1,98 @@
package org.bxteam.divinemc.dfc.common.vif;
import org.bxteam.divinemc.dfc.common.ast.AstNode;
import org.bxteam.divinemc.dfc.common.ast.EvalType;
import org.bxteam.divinemc.dfc.common.ast.misc.CacheLikeNode;
import org.bxteam.divinemc.dfc.common.ast.misc.DelegateNode;
import org.bxteam.divinemc.dfc.common.ducks.IFastCacheLike;
import java.util.Objects;
import net.minecraft.util.KeyDispatchDataCodec;
import net.minecraft.world.level.levelgen.DensityFunction;
import net.minecraft.world.level.levelgen.NoiseChunk;
import net.minecraft.world.level.levelgen.blending.Blender;
public class AstVanillaInterface implements DensityFunction {
private final AstNode astNode;
private final DensityFunction blendingFallback;
public AstVanillaInterface(AstNode astNode, DensityFunction blendingFallback) {
this.astNode = (AstNode)Objects.requireNonNull(astNode);
this.blendingFallback = blendingFallback;
}
public double compute(FunctionContext pos) {
if (pos.getBlender() != Blender.empty()) {
if (this.blendingFallback == null) {
throw new IllegalStateException("blendingFallback is no more");
} else {
return this.blendingFallback.compute(pos);
}
} else {
return this.astNode.evalSingle(pos.blockX(), pos.blockY(), pos.blockZ(), EvalType.from(pos));
}
}
public void fillArray(double[] densities, ContextProvider applier) {
if (applier instanceof NoiseChunk sampler) {
if (sampler.getBlender() != Blender.empty()) {
if (this.blendingFallback == null) {
throw new IllegalStateException("blendingFallback is no more");
}
this.blendingFallback.fillArray(densities, applier);
return;
}
}
if (applier instanceof EachApplierVanillaInterface vanillaInterface) {
this.astNode.evalMulti(densities, vanillaInterface.getX(), vanillaInterface.getY(), vanillaInterface.getZ(), EvalType.from(applier));
} else {
int[] x = new int[densities.length];
int[] y = new int[densities.length];
int[] z = new int[densities.length];
for(int i = 0; i < densities.length; ++i) {
FunctionContext pos = applier.forIndex(i);
x[i] = pos.blockX();
y[i] = pos.blockY();
z[i] = pos.blockZ();
}
this.astNode.evalMulti(densities, x, y, z, EvalType.from(applier));
}
}
public DensityFunction mapAll(Visitor visitor) {
AstNode transformed = this.astNode.transform((astNode) -> {
if (astNode instanceof DelegateNode delegateNode) {
return new DelegateNode(delegateNode.getDelegate().mapAll(visitor));
} else if (astNode instanceof CacheLikeNode cacheLikeNode) {
return new CacheLikeNode((IFastCacheLike)cacheLikeNode.getCacheLike().mapAll(visitor), cacheLikeNode.getDelegate());
} else {
return astNode;
}
});
DensityFunction blendingFallback1 = this.blendingFallback != null ? this.blendingFallback.mapAll(visitor) : null;
return transformed == this.astNode && blendingFallback1 == this.blendingFallback ? this : new AstVanillaInterface(transformed, blendingFallback1);
}
public double minValue() {
return this.blendingFallback.minValue();
}
public double maxValue() {
return this.blendingFallback.maxValue();
}
public KeyDispatchDataCodec<? extends DensityFunction> codec() {
throw new UnsupportedOperationException();
}
public AstNode getAstNode() {
return this.astNode;
}
public DensityFunction getBlendingFallback() {
return this.blendingFallback;
}
}

View File

@@ -0,0 +1,58 @@
package org.bxteam.divinemc.dfc.common.vif;
import org.bxteam.divinemc.dfc.common.ast.EvalType;
import org.bxteam.divinemc.dfc.common.ducks.IArrayCacheCapable;
import org.bxteam.divinemc.dfc.common.util.ArrayCache;
import java.util.Objects;
import net.minecraft.world.level.levelgen.DensityFunction;
public class EachApplierVanillaInterface implements DensityFunction.ContextProvider, IArrayCacheCapable {
private final int[] x;
private final int[] y;
private final int[] z;
private final EvalType type;
private final ArrayCache cache;
public EachApplierVanillaInterface(int[] x, int[] y, int[] z, EvalType type) {
this(x, y, z, type, new ArrayCache());
}
public EachApplierVanillaInterface(int[] x, int[] y, int[] z, EvalType type, ArrayCache cache) {
this.x = (int[])Objects.requireNonNull(x);
this.y = (int[])Objects.requireNonNull(y);
this.z = (int[])Objects.requireNonNull(z);
this.type = (EvalType)Objects.requireNonNull(type);
this.cache = (ArrayCache)Objects.requireNonNull(cache);
}
public DensityFunction.FunctionContext forIndex(int index) {
return new NoisePosVanillaInterface(this.x[index], this.y[index], this.z[index], this.type);
}
public void fillAllDirectly(double[] densities, DensityFunction densityFunction) {
for(int i = 0; i < this.x.length; ++i) {
densities[i] = densityFunction.compute(this.forIndex(i));
}
}
public int[] getX() {
return this.x;
}
public int[] getY() {
return this.y;
}
public int[] getZ() {
return this.z;
}
public EvalType getType() {
return this.type;
}
public ArrayCache c2me$getArrayCache() {
return this.cache;
}
}

View File

@@ -0,0 +1,35 @@
package org.bxteam.divinemc.dfc.common.vif;
import org.bxteam.divinemc.dfc.common.ast.EvalType;
import java.util.Objects;
import net.minecraft.world.level.levelgen.DensityFunction;
public class NoisePosVanillaInterface implements DensityFunction.FunctionContext {
private final int x;
private final int y;
private final int z;
private final EvalType type;
public NoisePosVanillaInterface(int x, int y, int z, EvalType type) {
this.x = x;
this.y = y;
this.z = z;
this.type = (EvalType)Objects.requireNonNull(type);
}
public int blockX() {
return this.x;
}
public int blockY() {
return this.y;
}
public int blockZ() {
return this.z;
}
public EvalType getType() {
return this.type;
}
}

View File

@@ -0,0 +1,284 @@
package org.bxteam.divinemc.entity.pathfinding;
import net.minecraft.core.BlockPos;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.level.pathfinder.Node;
import net.minecraft.world.level.pathfinder.Path;
import net.minecraft.world.phys.Vec3;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.function.Supplier;
public class AsyncPath extends Path {
/**
* marks whether this async path has been processed
*/
private volatile PathProcessState processState = PathProcessState.WAITING;
/**
* runnables waiting for this to be processed
*/
private final List<Runnable> postProcessing = new ArrayList<>(0);
/**
* a list of positions that this path could path towards
*/
private final Set<BlockPos> positions;
/**
* the supplier of the real processed path
*/
private final Supplier<Path> pathSupplier;
/*
* Processed values
*/
/**
* this is a reference to the nodes list in the parent `Path` object
*/
private final List<Node> nodes;
/**
* the block we're trying to path to
* <p>
* while processing, we have no idea where this is so consumers of `Path` should check that the path is processed before checking the target block
*/
private @Nullable BlockPos target;
/**
* how far we are to the target
* <p>
* while processing, the target could be anywhere but theoretically we're always "close" to a theoretical target so default is 0
*/
private float distToTarget = 0;
/**
* whether we can reach the target
* <p>
* while processing, we can always theoretically reach the target so default is true
*/
private boolean canReach = true;
public AsyncPath(@NotNull List<Node> emptyNodeList, @NotNull Set<BlockPos> positions, @NotNull Supplier<Path> pathSupplier) {
//noinspection ConstantConditions
super(emptyNodeList, null, false);
this.nodes = emptyNodeList;
this.positions = positions;
this.pathSupplier = pathSupplier;
AsyncPathProcessor.queue(this);
}
@Override
public boolean isProcessed() {
return this.processState == PathProcessState.COMPLETED;
}
/**
* returns the future representing the processing state of this path
*/
public synchronized void postProcessing(@NotNull Runnable runnable) {
if (isProcessed()) {
runnable.run();
} else {
this.postProcessing.add(runnable);
}
}
/**
* an easy way to check if this processing path is the same as an attempted new path
*
* @param positions - the positions to compare against
* @return true if we are processing the same positions
*/
public boolean hasSameProcessingPositions(final Set<BlockPos> positions) {
if (this.positions.size() != positions.size()) {
return false;
}
return this.positions.containsAll(positions);
}
/**
* starts processing this path
*/
public synchronized void process() {
if (this.processState == PathProcessState.COMPLETED ||
this.processState == PathProcessState.PROCESSING) {
return;
}
processState = PathProcessState.PROCESSING;
final Path bestPath = this.pathSupplier.get();
this.nodes.addAll(bestPath.nodes); // we mutate this list to reuse the logic in Path
this.target = bestPath.getTarget();
this.distToTarget = bestPath.getDistToTarget();
this.canReach = bestPath.canReach();
processState = PathProcessState.COMPLETED;
for (Runnable runnable : this.postProcessing) {
runnable.run();
} // Run tasks after processing
}
/**
* if this path is accessed while it hasn't processed, just process it in-place
*/
private void checkProcessed() {
if (this.processState == PathProcessState.WAITING ||
this.processState == PathProcessState.PROCESSING) { // Block if we are on processing
this.process();
}
}
/*
* overrides we need for final fields that we cannot modify after processing
*/
@Override
public @NotNull BlockPos getTarget() {
this.checkProcessed();
return this.target;
}
@Override
public float getDistToTarget() {
this.checkProcessed();
return this.distToTarget;
}
@Override
public boolean canReach() {
this.checkProcessed();
return this.canReach;
}
/*
* overrides to ensure we're processed first
*/
@Override
public boolean isDone() {
return this.processState == PathProcessState.COMPLETED && super.isDone();
}
@Override
public void advance() {
this.checkProcessed();
super.advance();
}
@Override
public boolean notStarted() {
this.checkProcessed();
return super.notStarted();
}
@Nullable
@Override
public Node getEndNode() {
this.checkProcessed();
return super.getEndNode();
}
@Override
public Node getNode(int index) {
this.checkProcessed();
return super.getNode(index);
}
@Override
public void truncateNodes(int length) {
this.checkProcessed();
super.truncateNodes(length);
}
@Override
public void replaceNode(int index, Node node) {
this.checkProcessed();
super.replaceNode(index, node);
}
@Override
public int getNodeCount() {
this.checkProcessed();
return super.getNodeCount();
}
@Override
public int getNextNodeIndex() {
this.checkProcessed();
return super.getNextNodeIndex();
}
@Override
public void setNextNodeIndex(int nodeIndex) {
this.checkProcessed();
super.setNextNodeIndex(nodeIndex);
}
@Override
public Vec3 getEntityPosAtNode(Entity entity, int index) {
this.checkProcessed();
return super.getEntityPosAtNode(entity, index);
}
@Override
public BlockPos getNodePos(int index) {
this.checkProcessed();
return super.getNodePos(index);
}
@Override
public Vec3 getNextEntityPos(Entity entity) {
this.checkProcessed();
return super.getNextEntityPos(entity);
}
@Override
public BlockPos getNextNodePos() {
this.checkProcessed();
return super.getNextNodePos();
}
@Override
public Node getNextNode() {
this.checkProcessed();
return super.getNextNode();
}
@Nullable
@Override
public Node getPreviousNode() {
this.checkProcessed();
return super.getPreviousNode();
}
public PathProcessState getProcessState() {
return processState;
}
}

View File

@@ -0,0 +1,48 @@
package org.bxteam.divinemc.entity.pathfinding;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import net.minecraft.server.MinecraftServer;
import net.minecraft.world.level.pathfinder.Path;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.concurrent.*;
import java.util.function.Consumer;
/**
* used to handle the scheduling of async path processing
*/
public class AsyncPathProcessor {
private static final Executor pathProcessingExecutor = new ThreadPoolExecutor(
1,
org.bxteam.divinemc.DivineConfig.asyncPathfindingMaxThreads,
org.bxteam.divinemc.DivineConfig.asyncPathfindingKeepalive, TimeUnit.SECONDS,
new LinkedBlockingQueue<>(),
new ThreadFactoryBuilder()
.setNameFormat("DivineMC Async Pathfinding Thread - %d")
.setPriority(Thread.NORM_PRIORITY - 2)
.build()
);
protected static CompletableFuture<Void> queue(@NotNull AsyncPath path) {
return CompletableFuture.runAsync(path::process, pathProcessingExecutor);
}
/**
* takes a possibly unprocessed path, and waits until it is completed
* the consumer will be immediately invoked if the path is already processed
* the consumer will always be called on the main thread
*
* @param path a path to wait on
* @param afterProcessing a consumer to be called
*/
public static void awaitProcessing(@Nullable Path path, Consumer<@Nullable Path> afterProcessing) {
if (path != null && !path.isProcessed() && path instanceof AsyncPath asyncPath) {
asyncPath.postProcessing(() ->
MinecraftServer.getServer().scheduleOnMain(() -> afterProcessing.accept(path))
);
} else {
afterProcessing.accept(path);
}
}
}

View File

@@ -0,0 +1,44 @@
package org.bxteam.divinemc.entity.pathfinding;
import net.minecraft.world.level.pathfinder.NodeEvaluator;
import org.apache.commons.lang.Validate;
import org.jetbrains.annotations.NotNull;
import java.util.Map;
import java.util.Queue;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedQueue;
public class NodeEvaluatorCache {
private static final Map<NodeEvaluatorFeatures, ConcurrentLinkedQueue<NodeEvaluator>> threadLocalNodeEvaluators = new ConcurrentHashMap<>();
private static final Map<NodeEvaluator, NodeEvaluatorGenerator> nodeEvaluatorToGenerator = new ConcurrentHashMap<>();
private static @NotNull Queue<NodeEvaluator> getQueueForFeatures(@NotNull NodeEvaluatorFeatures nodeEvaluatorFeatures) {
return threadLocalNodeEvaluators.computeIfAbsent(nodeEvaluatorFeatures, key -> new ConcurrentLinkedQueue<>());
}
public static @NotNull NodeEvaluator takeNodeEvaluator(@NotNull NodeEvaluatorGenerator generator, @NotNull NodeEvaluator localNodeEvaluator) {
final NodeEvaluatorFeatures nodeEvaluatorFeatures = NodeEvaluatorFeatures.fromNodeEvaluator(localNodeEvaluator);
NodeEvaluator nodeEvaluator = getQueueForFeatures(nodeEvaluatorFeatures).poll();
if (nodeEvaluator == null) {
nodeEvaluator = generator.generate(nodeEvaluatorFeatures);
}
nodeEvaluatorToGenerator.put(nodeEvaluator, generator);
return nodeEvaluator;
}
public static void returnNodeEvaluator(@NotNull NodeEvaluator nodeEvaluator) {
final NodeEvaluatorGenerator generator = nodeEvaluatorToGenerator.remove(nodeEvaluator);
Validate.notNull(generator, "NodeEvaluator already returned");
final NodeEvaluatorFeatures nodeEvaluatorFeatures = NodeEvaluatorFeatures.fromNodeEvaluator(nodeEvaluator);
getQueueForFeatures(nodeEvaluatorFeatures).offer(nodeEvaluator);
}
public static void removeNodeEvaluator(@NotNull NodeEvaluator nodeEvaluator) {
nodeEvaluatorToGenerator.remove(nodeEvaluator);
}
}

View File

@@ -0,0 +1,23 @@
package org.bxteam.divinemc.entity.pathfinding;
import net.minecraft.world.level.pathfinder.NodeEvaluator;
import net.minecraft.world.level.pathfinder.SwimNodeEvaluator;
public record NodeEvaluatorFeatures(
NodeEvaluatorType type,
boolean canPassDoors,
boolean canFloat,
boolean canWalkOverFences,
boolean canOpenDoors,
boolean allowBreaching
) {
public static NodeEvaluatorFeatures fromNodeEvaluator(NodeEvaluator nodeEvaluator) {
NodeEvaluatorType type = NodeEvaluatorType.fromNodeEvaluator(nodeEvaluator);
boolean canPassDoors = nodeEvaluator.canPassDoors();
boolean canFloat = nodeEvaluator.canFloat();
boolean canWalkOverFences = nodeEvaluator.canWalkOverFences();
boolean canOpenDoors = nodeEvaluator.canOpenDoors();
boolean allowBreaching = nodeEvaluator instanceof SwimNodeEvaluator swimNodeEvaluator && swimNodeEvaluator.allowBreaching;
return new NodeEvaluatorFeatures(type, canPassDoors, canFloat, canWalkOverFences, canOpenDoors, allowBreaching);
}
}

View File

@@ -0,0 +1,8 @@
package org.bxteam.divinemc.entity.pathfinding;
import net.minecraft.world.level.pathfinder.NodeEvaluator;
import org.jetbrains.annotations.NotNull;
public interface NodeEvaluatorGenerator {
@NotNull NodeEvaluator generate(NodeEvaluatorFeatures nodeEvaluatorFeatures);
}

View File

@@ -0,0 +1,17 @@
package org.bxteam.divinemc.entity.pathfinding;
import net.minecraft.world.level.pathfinder.*;
public enum NodeEvaluatorType {
WALK,
SWIM,
AMPHIBIOUS,
FLY;
public static NodeEvaluatorType fromNodeEvaluator(NodeEvaluator nodeEvaluator) {
if (nodeEvaluator instanceof SwimNodeEvaluator) return SWIM;
if (nodeEvaluator instanceof FlyNodeEvaluator) return FLY;
if (nodeEvaluator instanceof AmphibiousNodeEvaluator) return AMPHIBIOUS;
return WALK;
}
}

View File

@@ -0,0 +1,7 @@
package org.bxteam.divinemc.entity.pathfinding;
public enum PathProcessState {
WAITING,
PROCESSING,
COMPLETED
}

View File

@@ -0,0 +1,141 @@
package org.bxteam.divinemc.entity.tracking;
import ca.spottedleaf.moonrise.common.list.ReferenceList;
import ca.spottedleaf.moonrise.common.misc.NearbyPlayers;
import ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemServerLevel;
import ca.spottedleaf.moonrise.patches.chunk_system.level.entity.server.ServerEntityLookup;
import ca.spottedleaf.moonrise.patches.entity_tracker.EntityTrackerEntity;
import ca.spottedleaf.moonrise.patches.entity_tracker.EntityTrackerTrackedEntity;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import net.minecraft.server.level.ChunkMap;
import net.minecraft.server.level.FullChunkStatus;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.entity.Entity;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.util.concurrent.Executor;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public class MultithreadedTracker {
private static final Logger LOGGER = LogManager.getLogger("MultithreadedTracker");
public static class MultithreadedTrackerThread extends Thread {
@Override
public void run() {
super.run();
}
}
private static final Executor trackerExecutor = new ThreadPoolExecutor(
1,
org.bxteam.divinemc.DivineConfig.asyncEntityTrackerMaxThreads,
org.bxteam.divinemc.DivineConfig.asyncEntityTrackerKeepalive, TimeUnit.SECONDS,
new LinkedBlockingQueue<>(),
new ThreadFactoryBuilder()
.setThreadFactory(
r -> new MultithreadedTrackerThread() {
@Override
public void run() {
r.run();
}
}
)
.setNameFormat("DivineMC Async Tracker Thread - %d")
.setPriority(Thread.NORM_PRIORITY - 2)
.build());
private MultithreadedTracker() { }
public static Executor getTrackerExecutor() {
return trackerExecutor;
}
public static void tick(ChunkSystemServerLevel level) {
try {
if (!org.bxteam.divinemc.DivineConfig.multithreadedCompatModeEnabled) {
tickAsync(level);
} else {
tickAsyncWithCompatMode(level);
}
} catch (Exception e) {
LOGGER.error("Error occurred while executing async task.", e);
}
}
private static void tickAsync(ChunkSystemServerLevel level) {
final NearbyPlayers nearbyPlayers = level.moonrise$getNearbyPlayers();
final ServerEntityLookup entityLookup = (ServerEntityLookup) level.moonrise$getEntityLookup();
final ReferenceList<Entity> trackerEntities = entityLookup.trackerEntities;
final Entity[] trackerEntitiesRaw = trackerEntities.getRawDataUnchecked();
// Move tracking to off-main
trackerExecutor.execute(() -> {
for (final Entity entity : trackerEntitiesRaw) {
if (entity == null) continue;
final ChunkMap.TrackedEntity tracker = ((EntityTrackerEntity) entity).moonrise$getTrackedEntity();
if (tracker == null) continue;
((EntityTrackerTrackedEntity) tracker).moonrise$tick(nearbyPlayers.getChunk(entity.chunkPosition()));
tracker.serverEntity.sendChanges();
}
});
}
private static void tickAsyncWithCompatMode(ChunkSystemServerLevel level) {
final NearbyPlayers nearbyPlayers = level.moonrise$getNearbyPlayers();
final ServerEntityLookup entityLookup = (ServerEntityLookup) level.moonrise$getEntityLookup();
final ReferenceList<Entity> trackerEntities = entityLookup.trackerEntities;
final Entity[] trackerEntitiesRaw = trackerEntities.getRawDataUnchecked();
final Runnable[] sendChangesTasks = new Runnable[trackerEntitiesRaw.length];
int index = 0;
for (final Entity entity : trackerEntitiesRaw) {
if (entity == null) continue;
final ChunkMap.TrackedEntity tracker = ((EntityTrackerEntity) entity).moonrise$getTrackedEntity();
if (tracker == null) continue;
((EntityTrackerTrackedEntity) tracker).moonrise$tick(nearbyPlayers.getChunk(entity.chunkPosition()));
sendChangesTasks[index++] = () -> tracker.serverEntity.sendChanges(); // Collect send changes to task array
}
// batch submit tasks
trackerExecutor.execute(() -> {
for (final Runnable sendChanges : sendChangesTasks) {
if (sendChanges == null) continue;
sendChanges.run();
}
});
}
// Original ChunkMap#newTrackerTick of Paper
// Just for diff usage for future update
@SuppressWarnings("DuplicatedCode")
private static void tickOriginal(ServerLevel level) {
final ca.spottedleaf.moonrise.patches.chunk_system.level.entity.server.ServerEntityLookup entityLookup = (ca.spottedleaf.moonrise.patches.chunk_system.level.entity.server.ServerEntityLookup) ((ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemServerLevel) level).moonrise$getEntityLookup();
final ca.spottedleaf.moonrise.common.list.ReferenceList<net.minecraft.world.entity.Entity> trackerEntities = entityLookup.trackerEntities;
final Entity[] trackerEntitiesRaw = trackerEntities.getRawDataUnchecked();
for (int i = 0, len = trackerEntities.size(); i < len; ++i) {
final Entity entity = trackerEntitiesRaw[i];
final ChunkMap.TrackedEntity tracker = ((ca.spottedleaf.moonrise.patches.entity_tracker.EntityTrackerEntity) entity).moonrise$getTrackedEntity();
if (tracker == null) {
continue;
}
((ca.spottedleaf.moonrise.patches.entity_tracker.EntityTrackerTrackedEntity) tracker).moonrise$tick(((ca.spottedleaf.moonrise.patches.chunk_system.entity.ChunkSystemEntity) entity).moonrise$getChunkData().nearbyPlayers);
if (((ca.spottedleaf.moonrise.patches.entity_tracker.EntityTrackerTrackedEntity) tracker).moonrise$hasPlayers()
|| ((ca.spottedleaf.moonrise.patches.chunk_system.entity.ChunkSystemEntity) entity).moonrise$getChunkStatus().isOrAfter(FullChunkStatus.ENTITY_TICKING)) {
tracker.serverEntity.sendChanges();
}
}
}
}

View File

@@ -0,0 +1,105 @@
package org.bxteam.divinemc.math;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.lang.foreign.MemorySegment;
import java.lang.invoke.MethodHandle;
public class Bindings {
private static final Logger LOGGER = LoggerFactory.getLogger(Bindings.class);
private static final MethodHandle MH_c2me_natives_noise_perlin_double = bind(BindingsTemplate.c2me_natives_noise_perlin_double, "c2me_natives_noise_perlin_double");
private static final MethodHandle MH_c2me_natives_noise_perlin_double_ptr = bind(BindingsTemplate.c2me_natives_noise_perlin_double_ptr, "c2me_natives_noise_perlin_double");
private static final MethodHandle MH_c2me_natives_noise_perlin_double_batch = bind(BindingsTemplate.c2me_natives_noise_perlin_double_batch, "c2me_natives_noise_perlin_double_batch");
private static final MethodHandle MH_c2me_natives_noise_perlin_double_batch_partial_ptr = bind(BindingsTemplate.c2me_natives_noise_perlin_double_batch_ptr, "c2me_natives_noise_perlin_double_batch");
private static final MethodHandle MH_c2me_natives_noise_interpolated = bind(BindingsTemplate.c2me_natives_noise_interpolated, "c2me_natives_noise_interpolated");
private static final MethodHandle MH_c2me_natives_noise_interpolated_ptr = bind(BindingsTemplate.c2me_natives_noise_interpolated_ptr, "c2me_natives_noise_interpolated");
private static final MethodHandle MH_c2me_natives_end_islands_sample = bind(BindingsTemplate.c2me_natives_end_islands_sample, "c2me_natives_end_islands_sample");
private static final MethodHandle MH_c2me_natives_end_islands_sample_ptr = bind(BindingsTemplate.c2me_natives_end_islands_sample_ptr, "c2me_natives_end_islands_sample");
private static final MethodHandle MH_c2me_natives_biome_access_sample = bind(BindingsTemplate.c2me_natives_biome_access_sample, "c2me_natives_biome_access_sample");
private static @Nullable MethodHandle bind(@NotNull MethodHandle template, String prefix) {
if (NativeLoader.currentMachineTarget == null) {
LOGGER.warn("Call to bindings was found! Please disable native acceleration in config, as your system may be incompatible");
return null;
}
return template.bindTo(NativeLoader.lookup.find(prefix + NativeLoader.currentMachineTarget.getSuffix()).get());
}
public static double c2me_natives_noise_perlin_double(MemorySegment data, double x, double y, double z) {
try {
return (double) MH_c2me_natives_noise_perlin_double.invokeExact(data, x, y, z);
} catch (Throwable e) {
throw new RuntimeException(e);
}
}
public static double c2me_natives_noise_perlin_double(long data_ptr, double x, double y, double z) {
try {
return (double) MH_c2me_natives_noise_perlin_double_ptr.invokeExact(data_ptr, x, y, z);
} catch (Throwable e) {
throw new RuntimeException(e);
}
}
public static void c2me_natives_noise_perlin_double_batch(MemorySegment data, MemorySegment res, MemorySegment x, MemorySegment y, MemorySegment z, int length) {
try {
MH_c2me_natives_noise_perlin_double_batch.invokeExact(data, res, x, y, z, length);
} catch (Throwable e) {
throw new RuntimeException(e);
}
}
public static void c2me_natives_noise_perlin_double_batch(long data_ptr, MemorySegment res, MemorySegment x, MemorySegment y, MemorySegment z, int length) {
try {
MH_c2me_natives_noise_perlin_double_batch_partial_ptr.invokeExact(data_ptr, res, x, y, z, length);
} catch (Throwable e) {
throw new RuntimeException(e);
}
}
public static double c2me_natives_noise_interpolated(MemorySegment data, double x, double y, double z) {
try {
return (double) MH_c2me_natives_noise_interpolated.invokeExact(data, x, y, z);
} catch (Throwable e) {
throw new RuntimeException(e);
}
}
public static double c2me_natives_noise_interpolated(long data_ptr, double x, double y, double z) {
try {
return (double) MH_c2me_natives_noise_interpolated_ptr.invokeExact(data_ptr, x, y, z);
} catch (Throwable e) {
throw new RuntimeException(e);
}
}
public static float c2me_natives_end_islands_sample(MemorySegment data, int x, int z) {
try {
return (float) MH_c2me_natives_end_islands_sample.invokeExact(data, x, z);
} catch (Throwable e) {
throw new RuntimeException(e);
}
}
public static float c2me_natives_end_islands_sample(long data_ptr, int x, int z) {
if ((x * x + z * z) < 0) { // workaround some compiler bugs
return Float.NaN;
}
try {
return (float) MH_c2me_natives_end_islands_sample_ptr.invokeExact(data_ptr, x, z);
} catch (Throwable e) {
throw new RuntimeException(e);
}
}
public static int c2me_natives_biome_access_sample(long seed, int x, int y, int z) {
try {
return (int) MH_c2me_natives_biome_access_sample.invokeExact(seed, x, y, z);
} catch (Throwable e) {
throw new RuntimeException(e);
}
}
}

View File

@@ -0,0 +1,407 @@
package org.bxteam.divinemc.math;
import net.minecraft.world.level.levelgen.synth.BlendedNoise;
import net.minecraft.world.level.levelgen.synth.ImprovedNoise;
import net.minecraft.world.level.levelgen.synth.PerlinNoise;
import org.jetbrains.annotations.NotNull;
import org.bxteam.divinemc.util.MemoryUtil;
import java.lang.foreign.*;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.VarHandle;
import java.util.Objects;
import java.util.stream.IntStream;
public class BindingsTemplate {
// double c2me_natives_noise_perlin_sample (const uint8_t *permutations, double originX, double originY, double originZ, double x, double y, double z, double yScale, double yMax)
public static final MethodHandle c2me_natives_noise_perlin_sample = NativeLoader.linker.downcallHandle(
FunctionDescriptor.of(
ValueLayout.JAVA_DOUBLE,
ValueLayout.ADDRESS,
ValueLayout.JAVA_DOUBLE,
ValueLayout.JAVA_DOUBLE,
ValueLayout.JAVA_DOUBLE,
ValueLayout.JAVA_DOUBLE,
ValueLayout.JAVA_DOUBLE,
ValueLayout.JAVA_DOUBLE,
ValueLayout.JAVA_DOUBLE,
ValueLayout.JAVA_DOUBLE
),
Linker.Option.critical(true)
);
// c2me_natives_noise_perlin_double, double, (const double_octave_sampler_data_t *data, double x, double y, double z)
public static final MethodHandle c2me_natives_noise_perlin_double = NativeLoader.linker.downcallHandle(
FunctionDescriptor.of(
ValueLayout.JAVA_DOUBLE,
ValueLayout.ADDRESS,
ValueLayout.JAVA_DOUBLE,
ValueLayout.JAVA_DOUBLE,
ValueLayout.JAVA_DOUBLE
),
Linker.Option.critical(false)
);
public static final MethodHandle c2me_natives_noise_perlin_double_ptr = NativeLoader.linker.downcallHandle(
FunctionDescriptor.of(
ValueLayout.JAVA_DOUBLE,
ValueLayout.JAVA_LONG,
ValueLayout.JAVA_DOUBLE,
ValueLayout.JAVA_DOUBLE,
ValueLayout.JAVA_DOUBLE
),
Linker.Option.critical(false)
);
// c2me_natives_noise_perlin_double_batch, void, (const double_octave_sampler_data_t *const data,
// double *const res, const double *const x,
// const double *const y, const double *const z,
// const uint32_t length)
public static final MethodHandle c2me_natives_noise_perlin_double_batch = NativeLoader.linker.downcallHandle(
FunctionDescriptor.ofVoid(
ValueLayout.ADDRESS,
ValueLayout.ADDRESS,
ValueLayout.ADDRESS,
ValueLayout.ADDRESS,
ValueLayout.ADDRESS,
ValueLayout.JAVA_INT
),
Linker.Option.critical(true)
);
public static final MethodHandle c2me_natives_noise_perlin_double_batch_ptr = NativeLoader.linker.downcallHandle(
FunctionDescriptor.ofVoid(
ValueLayout.JAVA_LONG,
ValueLayout.ADDRESS,
ValueLayout.ADDRESS,
ValueLayout.ADDRESS,
ValueLayout.ADDRESS,
ValueLayout.JAVA_INT
),
Linker.Option.critical(true)
);
public static final StructLayout double_octave_sampler_data = MemoryLayout.structLayout(
ValueLayout.JAVA_LONG.withName("length"),
ValueLayout.JAVA_DOUBLE.withName("amplitude"),
ValueLayout.ADDRESS.withName("need_shift"),
ValueLayout.ADDRESS.withName("lacunarity_powd"),
ValueLayout.ADDRESS.withName("persistence_powd"),
ValueLayout.ADDRESS.withName("sampler_permutations"),
ValueLayout.ADDRESS.withName("sampler_originX"),
ValueLayout.ADDRESS.withName("sampler_originY"),
ValueLayout.ADDRESS.withName("sampler_originZ"),
ValueLayout.ADDRESS.withName("amplitudes")
).withByteAlignment(32).withName("double_double_octave_sampler_data");
public static final VarHandle double_octave_sampler_data$length = double_octave_sampler_data.varHandle(MemoryLayout.PathElement.groupElement("length"));
public static final VarHandle double_octave_sampler_data$amplitude = double_octave_sampler_data.varHandle(MemoryLayout.PathElement.groupElement("amplitude"));
public static final VarHandle double_octave_sampler_data$need_shift = double_octave_sampler_data.varHandle(MemoryLayout.PathElement.groupElement("need_shift"));
public static final VarHandle double_octave_sampler_data$lacunarity_powd = double_octave_sampler_data.varHandle(MemoryLayout.PathElement.groupElement("lacunarity_powd"));
public static final VarHandle double_octave_sampler_data$persistence_powd = double_octave_sampler_data.varHandle(MemoryLayout.PathElement.groupElement("persistence_powd"));
public static final VarHandle double_octave_sampler_data$sampler_permutations = double_octave_sampler_data.varHandle(MemoryLayout.PathElement.groupElement("sampler_permutations"));
public static final VarHandle double_octave_sampler_data$sampler_originX = double_octave_sampler_data.varHandle(MemoryLayout.PathElement.groupElement("sampler_originX"));
public static final VarHandle double_octave_sampler_data$sampler_originY = double_octave_sampler_data.varHandle(MemoryLayout.PathElement.groupElement("sampler_originY"));
public static final VarHandle double_octave_sampler_data$sampler_originZ = double_octave_sampler_data.varHandle(MemoryLayout.PathElement.groupElement("sampler_originZ"));
public static final VarHandle double_octave_sampler_data$amplitudes = double_octave_sampler_data.varHandle(MemoryLayout.PathElement.groupElement("amplitudes"));
public static MemorySegment double_octave_sampler_data$create(Arena arena, @NotNull PerlinNoise firstSampler, PerlinNoise secondSampler, double amplitude) {
long nonNullSamplerCount = 0;
for (ImprovedNoise sampler : (firstSampler.noiseLevels)) {
if (sampler != null) {
nonNullSamplerCount++;
}
}
for (ImprovedNoise sampler : (secondSampler.noiseLevels)) {
if (sampler != null) {
nonNullSamplerCount++;
}
}
final MemorySegment data = arena.allocate(double_octave_sampler_data.byteSize(), 64);
final MemorySegment need_shift = arena.allocate(nonNullSamplerCount, 64);
final MemorySegment lacunarity_powd = arena.allocate(nonNullSamplerCount * 8, 64);
final MemorySegment persistence_powd = arena.allocate(nonNullSamplerCount * 8, 64);
final MemorySegment sampler_permutations = arena.allocate(nonNullSamplerCount * 256 * 4, 64);
final MemorySegment sampler_originX = arena.allocate(nonNullSamplerCount * 8, 64);
final MemorySegment sampler_originY = arena.allocate(nonNullSamplerCount * 8, 64);
final MemorySegment sampler_originZ = arena.allocate(nonNullSamplerCount * 8, 64);
final MemorySegment amplitudes = arena.allocate(nonNullSamplerCount * 8, 64);
double_octave_sampler_data$length.set(data, 0L, nonNullSamplerCount);
double_octave_sampler_data$amplitude.set(data, 0L, amplitude);
double_octave_sampler_data$need_shift.set(data, 0L, need_shift);
double_octave_sampler_data$lacunarity_powd.set(data, 0L, lacunarity_powd);
double_octave_sampler_data$persistence_powd.set(data, 0L, persistence_powd);
double_octave_sampler_data$sampler_permutations.set(data, 0L, sampler_permutations);
double_octave_sampler_data$sampler_originX.set(data, 0L, sampler_originX);
double_octave_sampler_data$sampler_originY.set(data, 0L, sampler_originY);
double_octave_sampler_data$sampler_originZ.set(data, 0L, sampler_originZ);
double_octave_sampler_data$amplitudes.set(data, 0L, amplitudes);
long index = 0;
{
ImprovedNoise[] octaveSamplers = (firstSampler.noiseLevels);
for (int i = 0, octaveSamplersLength = octaveSamplers.length; i < octaveSamplersLength; i++) {
ImprovedNoise sampler = octaveSamplers[i];
if (sampler != null) {
need_shift.set(ValueLayout.JAVA_BOOLEAN, index, false);
lacunarity_powd.set(ValueLayout.JAVA_DOUBLE, index * 8, (firstSampler.lowestFreqInputFactor) * Math.pow(2.0, i));
persistence_powd.set(ValueLayout.JAVA_DOUBLE, index * 8, (firstSampler.lowestFreqValueFactor) * Math.pow(2.0, -i));
MemorySegment.copy(MemorySegment.ofArray(MemoryUtil.byte2int(sampler.p)), 0, sampler_permutations, index * 256L * 4L, 256 * 4);
sampler_originX.set(ValueLayout.JAVA_DOUBLE, index * 8, sampler.xo);
sampler_originY.set(ValueLayout.JAVA_DOUBLE, index * 8, sampler.yo);
sampler_originZ.set(ValueLayout.JAVA_DOUBLE, index * 8, sampler.zo);
amplitudes.set(ValueLayout.JAVA_DOUBLE, index * 8, (firstSampler.amplitudes).getDouble(i));
index++;
}
}
}
{
ImprovedNoise[] octaveSamplers = (secondSampler.noiseLevels);
for (int i = 0, octaveSamplersLength = octaveSamplers.length; i < octaveSamplersLength; i++) {
ImprovedNoise sampler = octaveSamplers[i];
if (sampler != null) {
need_shift.set(ValueLayout.JAVA_BOOLEAN, index, true);
lacunarity_powd.set(ValueLayout.JAVA_DOUBLE, index * 8, (secondSampler.lowestFreqInputFactor) * Math.pow(2.0, i));
persistence_powd.set(ValueLayout.JAVA_DOUBLE, index * 8, (secondSampler.lowestFreqValueFactor) * Math.pow(2.0, -i));
MemorySegment.copy(MemorySegment.ofArray(MemoryUtil.byte2int(sampler.p)), 0, sampler_permutations, index * 256L * 4L, 256 * 4);
sampler_originX.set(ValueLayout.JAVA_DOUBLE, index * 8, sampler.xo);
sampler_originY.set(ValueLayout.JAVA_DOUBLE, index * 8, sampler.yo);
sampler_originZ.set(ValueLayout.JAVA_DOUBLE, index * 8, sampler.zo);
amplitudes.set(ValueLayout.JAVA_DOUBLE, index * 8, (secondSampler.amplitudes).getDouble(i));
index++;
}
}
}
VarHandle.fullFence();
return data;
}
// c2me_natives_noise_interpolated, double, (const interpolated_noise_sampler_t *const data, const double x, const double y, const double z)
public static final MethodHandle c2me_natives_noise_interpolated = NativeLoader.linker.downcallHandle(
FunctionDescriptor.of(
ValueLayout.JAVA_DOUBLE,
ValueLayout.ADDRESS,
ValueLayout.JAVA_DOUBLE,
ValueLayout.JAVA_DOUBLE,
ValueLayout.JAVA_DOUBLE
),
Linker.Option.critical(false)
);
public static final MethodHandle c2me_natives_noise_interpolated_ptr = NativeLoader.linker.downcallHandle(
FunctionDescriptor.of(
ValueLayout.JAVA_DOUBLE,
ValueLayout.JAVA_LONG,
ValueLayout.JAVA_DOUBLE,
ValueLayout.JAVA_DOUBLE,
ValueLayout.JAVA_DOUBLE
),
Linker.Option.critical(false)
);
// typedef const struct interpolated_noise_sub_sampler {
// const aligned_uint8_ptr sampler_permutations;
// const aligned_double_ptr sampler_originX;
// const aligned_double_ptr sampler_originY;
// const aligned_double_ptr sampler_originZ;
// const aligned_double_ptr sampler_mulFactor;
// const uint32_t length;
// } interpolated_noise_sub_sampler_t;
public static final StructLayout interpolated_noise_sub_sampler = MemoryLayout.structLayout(
ValueLayout.ADDRESS.withName("sampler_permutations"),
ValueLayout.ADDRESS.withName("sampler_originX"),
ValueLayout.ADDRESS.withName("sampler_originY"),
ValueLayout.ADDRESS.withName("sampler_originZ"),
ValueLayout.ADDRESS.withName("sampler_mulFactor"),
ValueLayout.JAVA_INT.withName("length"),
MemoryLayout.paddingLayout(4)
).withName("interpolated_noise_sub_sampler_t");
// typedef const struct interpolated_noise_sampler {
// const double scaledXzScale;
// const double scaledYScale;
// const double xzFactor;
// const double yFactor;
// const double smearScaleMultiplier;
// const double xzScale;
// const double yScale;
//
// const interpolated_noise_sub_sampler_t lower;
// const interpolated_noise_sub_sampler_t upper;
// const interpolated_noise_sub_sampler_t normal;
// } interpolated_noise_sampler_t;
public static final StructLayout interpolated_noise_sampler = MemoryLayout.structLayout(
ValueLayout.JAVA_DOUBLE.withName("scaledXzScale"),
ValueLayout.JAVA_DOUBLE.withName("scaledYScale"),
ValueLayout.JAVA_DOUBLE.withName("xzFactor"),
ValueLayout.JAVA_DOUBLE.withName("yFactor"),
ValueLayout.JAVA_DOUBLE.withName("smearScaleMultiplier"),
ValueLayout.JAVA_DOUBLE.withName("xzScale"),
ValueLayout.JAVA_DOUBLE.withName("yScale"),
interpolated_noise_sub_sampler.withName("lower"),
interpolated_noise_sub_sampler.withName("upper"),
interpolated_noise_sub_sampler.withName("normal")
).withByteAlignment(32).withName("interpolated_noise_sampler_t");
public static final VarHandle interpolated_noise_sampler$scaledXzScale = interpolated_noise_sampler.varHandle(MemoryLayout.PathElement.groupElement("scaledXzScale"));
public static final VarHandle interpolated_noise_sampler$scaledYScale = interpolated_noise_sampler.varHandle(MemoryLayout.PathElement.groupElement("scaledYScale"));
public static final VarHandle interpolated_noise_sampler$xzFactor = interpolated_noise_sampler.varHandle(MemoryLayout.PathElement.groupElement("xzFactor"));
public static final VarHandle interpolated_noise_sampler$yFactor = interpolated_noise_sampler.varHandle(MemoryLayout.PathElement.groupElement("yFactor"));
public static final VarHandle interpolated_noise_sampler$smearScaleMultiplier = interpolated_noise_sampler.varHandle(MemoryLayout.PathElement.groupElement("smearScaleMultiplier"));
public static final VarHandle interpolated_noise_sampler$xzScale = interpolated_noise_sampler.varHandle(MemoryLayout.PathElement.groupElement("xzScale"));
public static final VarHandle interpolated_noise_sampler$yScale = interpolated_noise_sampler.varHandle(MemoryLayout.PathElement.groupElement("yScale"));
public static final VarHandle interpolated_noise_sampler$lower$sampler_permutations = interpolated_noise_sampler.varHandle(MemoryLayout.PathElement.groupElement("lower"), MemoryLayout.PathElement.groupElement("sampler_permutations"));
public static final VarHandle interpolated_noise_sampler$lower$sampler_originX = interpolated_noise_sampler.varHandle(MemoryLayout.PathElement.groupElement("lower"), MemoryLayout.PathElement.groupElement("sampler_originX"));
public static final VarHandle interpolated_noise_sampler$lower$sampler_originY = interpolated_noise_sampler.varHandle(MemoryLayout.PathElement.groupElement("lower"), MemoryLayout.PathElement.groupElement("sampler_originY"));
public static final VarHandle interpolated_noise_sampler$lower$sampler_originZ = interpolated_noise_sampler.varHandle(MemoryLayout.PathElement.groupElement("lower"), MemoryLayout.PathElement.groupElement("sampler_originZ"));
public static final VarHandle interpolated_noise_sampler$lower$sampler_mulFactor = interpolated_noise_sampler.varHandle(MemoryLayout.PathElement.groupElement("lower"), MemoryLayout.PathElement.groupElement("sampler_mulFactor"));
public static final VarHandle interpolated_noise_sampler$lower$length = interpolated_noise_sampler.varHandle(MemoryLayout.PathElement.groupElement("lower"), MemoryLayout.PathElement.groupElement("length"));
public static final VarHandle interpolated_noise_sampler$upper$sampler_permutations = interpolated_noise_sampler.varHandle(MemoryLayout.PathElement.groupElement("upper"), MemoryLayout.PathElement.groupElement("sampler_permutations"));
public static final VarHandle interpolated_noise_sampler$upper$sampler_originX = interpolated_noise_sampler.varHandle(MemoryLayout.PathElement.groupElement("upper"), MemoryLayout.PathElement.groupElement("sampler_originX"));
public static final VarHandle interpolated_noise_sampler$upper$sampler_originY = interpolated_noise_sampler.varHandle(MemoryLayout.PathElement.groupElement("upper"), MemoryLayout.PathElement.groupElement("sampler_originY"));
public static final VarHandle interpolated_noise_sampler$upper$sampler_originZ = interpolated_noise_sampler.varHandle(MemoryLayout.PathElement.groupElement("upper"), MemoryLayout.PathElement.groupElement("sampler_originZ"));
public static final VarHandle interpolated_noise_sampler$upper$sampler_mulFactor = interpolated_noise_sampler.varHandle(MemoryLayout.PathElement.groupElement("upper"), MemoryLayout.PathElement.groupElement("sampler_mulFactor"));
public static final VarHandle interpolated_noise_sampler$upper$length = interpolated_noise_sampler.varHandle(MemoryLayout.PathElement.groupElement("upper"), MemoryLayout.PathElement.groupElement("length"));
public static final VarHandle interpolated_noise_sampler$normal$sampler_permutations = interpolated_noise_sampler.varHandle(MemoryLayout.PathElement.groupElement("normal"), MemoryLayout.PathElement.groupElement("sampler_permutations"));
public static final VarHandle interpolated_noise_sampler$normal$sampler_originX = interpolated_noise_sampler.varHandle(MemoryLayout.PathElement.groupElement("normal"), MemoryLayout.PathElement.groupElement("sampler_originX"));
public static final VarHandle interpolated_noise_sampler$normal$sampler_originY = interpolated_noise_sampler.varHandle(MemoryLayout.PathElement.groupElement("normal"), MemoryLayout.PathElement.groupElement("sampler_originY"));
public static final VarHandle interpolated_noise_sampler$normal$sampler_originZ = interpolated_noise_sampler.varHandle(MemoryLayout.PathElement.groupElement("normal"), MemoryLayout.PathElement.groupElement("sampler_originZ"));
public static final VarHandle interpolated_noise_sampler$normal$sampler_mulFactor = interpolated_noise_sampler.varHandle(MemoryLayout.PathElement.groupElement("normal"), MemoryLayout.PathElement.groupElement("sampler_mulFactor"));
public static final VarHandle interpolated_noise_sampler$normal$length = interpolated_noise_sampler.varHandle(MemoryLayout.PathElement.groupElement("normal"), MemoryLayout.PathElement.groupElement("length"));
public static boolean interpolated_noise_sampler$isSpecializedBase3dNoiseFunction(BlendedNoise interpolated) {
return IntStream.range(0, 16).mapToObj((interpolated).minLimitNoise::getOctaveNoise).filter(Objects::nonNull).count() == 16 &&
IntStream.range(0, 16).mapToObj((interpolated).maxLimitNoise::getOctaveNoise).filter(Objects::nonNull).count() == 16 &&
IntStream.range(0, 8).mapToObj((interpolated).mainNoise::getOctaveNoise).filter(Objects::nonNull).count() == 8;
}
public static MemorySegment interpolated_noise_sampler$create(@NotNull Arena arena, BlendedNoise interpolated) {
final MemorySegment data = arena.allocate(interpolated_noise_sampler.byteSize(), 64);
interpolated_noise_sampler$scaledXzScale.set(data, 0L, (interpolated).xzMultiplier);
interpolated_noise_sampler$scaledYScale.set(data, 0L, (interpolated).yMultiplier);
interpolated_noise_sampler$xzFactor.set(data, 0L, (interpolated).xzFactor);
interpolated_noise_sampler$yFactor.set(data, 0L, (interpolated).yFactor);
interpolated_noise_sampler$smearScaleMultiplier.set(data, 0L, (interpolated).smearScaleMultiplier);
interpolated_noise_sampler$xzScale.set(data, 0L, (interpolated).xzScale);
interpolated_noise_sampler$yScale.set(data, 0L, (interpolated).yScale);
// if (true) {
// System.out.println(String.format("Interpolated total: %d", countNonNull));
// System.out.println(String.format("lower: %d", IntStream.range(0, 16).mapToObj(((IInterpolatedNoiseSampler) interpolated).getLowerInterpolatedNoise()::getOctave).filter(Objects::nonNull).count()));
// System.out.println(String.format("upper: %d", IntStream.range(0, 16).mapToObj(((IInterpolatedNoiseSampler) interpolated).getUpperInterpolatedNoise()::getOctave).filter(Objects::nonNull).count()));
// System.out.println(String.format("normal: %d", IntStream.range(0, 8).mapToObj(((IInterpolatedNoiseSampler) interpolated).getInterpolationNoise()::getOctave).filter(Objects::nonNull).count()));
// }
final MemorySegment sampler_permutations = arena.allocate(40 * 256L * 4L, 64);
final MemorySegment sampler_originX = arena.allocate(40 * 8L, 64);
final MemorySegment sampler_originY = arena.allocate(40 * 8L, 64);
final MemorySegment sampler_originZ = arena.allocate(40 * 8L, 64);
final MemorySegment sampler_mulFactor = arena.allocate(40 * 8L, 64);
int index = 0;
{
int startIndex = index;
for (int i = 0; i < 8; i++) {
ImprovedNoise sampler = (interpolated.mainNoise).getOctaveNoise(i);
if (sampler != null) {
MemorySegment.copy(MemorySegment.ofArray(MemoryUtil.byte2int(sampler.p)), 0, sampler_permutations, index * 256L * 4L, 256 * 4);
sampler_originX.set(ValueLayout.JAVA_DOUBLE, index * 8L, sampler.xo);
sampler_originY.set(ValueLayout.JAVA_DOUBLE, index * 8L, sampler.yo);
sampler_originZ.set(ValueLayout.JAVA_DOUBLE, index * 8L, sampler.zo);
sampler_mulFactor.set(ValueLayout.JAVA_DOUBLE, index * 8L, Math.pow(2, -i));
index ++;
}
}
BindingsTemplate.interpolated_noise_sampler$normal$sampler_permutations.set(data, 0L, sampler_permutations.asSlice(startIndex * 256L * 4L));
BindingsTemplate.interpolated_noise_sampler$normal$sampler_originX.set(data, 0L, sampler_originX.asSlice(startIndex * 8L));
BindingsTemplate.interpolated_noise_sampler$normal$sampler_originY.set(data, 0L, sampler_originY.asSlice(startIndex * 8L));
BindingsTemplate.interpolated_noise_sampler$normal$sampler_originZ.set(data, 0L, sampler_originZ.asSlice(startIndex * 8L));
BindingsTemplate.interpolated_noise_sampler$normal$sampler_mulFactor.set(data, 0L, sampler_mulFactor.asSlice(startIndex * 8L));
BindingsTemplate.interpolated_noise_sampler$normal$length.set(data, 0L, index - startIndex);
}
{
int startIndex = index = 8;
for (int i = 0; i < 16; i++) {
ImprovedNoise sampler = (interpolated.minLimitNoise).getOctaveNoise(i);
if (sampler != null) {
MemorySegment.copy(MemorySegment.ofArray(MemoryUtil.byte2int(sampler.p)), 0, sampler_permutations, index * 256L * 4L, 256 * 4);
sampler_originX.set(ValueLayout.JAVA_DOUBLE, index * 8L, sampler.xo);
sampler_originY.set(ValueLayout.JAVA_DOUBLE, index * 8L, sampler.yo);
sampler_originZ.set(ValueLayout.JAVA_DOUBLE, index * 8L, sampler.zo);
sampler_mulFactor.set(ValueLayout.JAVA_DOUBLE, index * 8L, Math.pow(2, -i));
index ++;
}
}
BindingsTemplate.interpolated_noise_sampler$lower$sampler_permutations.set(data, 0L, sampler_permutations.asSlice(startIndex * 256L * 4L));
BindingsTemplate.interpolated_noise_sampler$lower$sampler_originX.set(data, 0L, sampler_originX.asSlice(startIndex * 8L));
BindingsTemplate.interpolated_noise_sampler$lower$sampler_originY.set(data, 0L, sampler_originY.asSlice(startIndex * 8L));
BindingsTemplate.interpolated_noise_sampler$lower$sampler_originZ.set(data, 0L, sampler_originZ.asSlice(startIndex * 8L));
BindingsTemplate.interpolated_noise_sampler$lower$sampler_mulFactor.set(data, 0L, sampler_mulFactor.asSlice(startIndex * 8L));
BindingsTemplate.interpolated_noise_sampler$lower$length.set(data, 0L, index - startIndex);
}
{
int startIndex = index = 8 + 16;
for (int i = 0; i < 16; i++) {
ImprovedNoise sampler = (interpolated.maxLimitNoise).getOctaveNoise(i);
if (sampler != null) {
MemorySegment.copy(MemorySegment.ofArray(MemoryUtil.byte2int(sampler.p)), 0, sampler_permutations, index * 256L * 4L, 256 * 4);
sampler_originX.set(ValueLayout.JAVA_DOUBLE, index * 8L, sampler.xo);
sampler_originY.set(ValueLayout.JAVA_DOUBLE, index * 8L, sampler.yo);
sampler_originZ.set(ValueLayout.JAVA_DOUBLE, index * 8L, sampler.zo);
sampler_mulFactor.set(ValueLayout.JAVA_DOUBLE, index * 8L, Math.pow(2, -i));
index ++;
}
}
BindingsTemplate.interpolated_noise_sampler$upper$sampler_permutations.set(data, 0L, sampler_permutations.asSlice(startIndex * 256L * 4L));
BindingsTemplate.interpolated_noise_sampler$upper$sampler_originX.set(data, 0L, sampler_originX.asSlice(startIndex * 8L));
BindingsTemplate.interpolated_noise_sampler$upper$sampler_originY.set(data, 0L, sampler_originY.asSlice(startIndex * 8L));
BindingsTemplate.interpolated_noise_sampler$upper$sampler_originZ.set(data, 0L, sampler_originZ.asSlice(startIndex * 8L));
BindingsTemplate.interpolated_noise_sampler$upper$sampler_mulFactor.set(data, 0L, sampler_mulFactor.asSlice(startIndex * 8L));
BindingsTemplate.interpolated_noise_sampler$upper$length.set(data, 0L, index - startIndex);
}
VarHandle.fullFence();
return data;
}
// c2me_natives_end_islands_sample, float, (const int32_t *const simplex_permutations, const int32_t x, const int32_t z)
public static final MethodHandle c2me_natives_end_islands_sample = NativeLoader.linker.downcallHandle(
FunctionDescriptor.of(
ValueLayout.JAVA_FLOAT,
ValueLayout.ADDRESS,
ValueLayout.JAVA_INT,
ValueLayout.JAVA_INT
),
Linker.Option.critical(true)
);
public static final MethodHandle c2me_natives_end_islands_sample_ptr = NativeLoader.linker.downcallHandle(
FunctionDescriptor.of(
ValueLayout.JAVA_FLOAT,
ValueLayout.JAVA_LONG,
ValueLayout.JAVA_INT,
ValueLayout.JAVA_INT
),
Linker.Option.critical(false)
);
// c2me_natives_biome_access_sample, uint32_t, (const int64_t theSeed, const int32_t x, const int32_t y, const int32_t z)
public static final MethodHandle c2me_natives_biome_access_sample = NativeLoader.linker.downcallHandle(
FunctionDescriptor.of(
ValueLayout.JAVA_INT,
ValueLayout.JAVA_LONG,
ValueLayout.JAVA_INT,
ValueLayout.JAVA_INT,
ValueLayout.JAVA_INT
),
Linker.Option.critical(false)
);
}

View File

@@ -0,0 +1,20 @@
package org.bxteam.divinemc.math;
import org.bxteam.divinemc.math.isa.ISA_aarch64;
import org.bxteam.divinemc.math.isa.ISA_x86_64;
public interface ISATarget {
int ordinal();
String getSuffix();
boolean isNativelySupported();
static Class<? extends Enum<? extends ISATarget>> getInstance() {
return switch (NativeLoader.NORMALIZED_ARCH) {
case "x86_64" -> ISA_x86_64.class;
case "aarch_64" -> ISA_aarch64.class;
default -> null;
};
}
}

View File

@@ -0,0 +1,179 @@
package org.bxteam.divinemc.math;
import io.netty.util.internal.SystemPropertyUtil;
import org.bxteam.divinemc.DivineConfig;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.InputStream;
import java.lang.foreign.*;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardCopyOption;
import java.util.Locale;
public class NativeLoader {
private static final Logger LOGGER = LoggerFactory.getLogger(NativeLoader.class);
public static final String NORMALIZED_ARCH = normalizeArch(SystemPropertyUtil.get("os.arch", ""));
public static final String NORMALIZED_OS = normalizeOs(SystemPropertyUtil.get("os.name", ""));
private static final Arena arena = Arena.ofAuto();
public static final SymbolLookup lookup;
public static final Linker linker = Linker.nativeLinker();
public static final ISATarget currentMachineTarget;
static {
String libName = String.format("%s-%s-%s", NORMALIZED_OS, NORMALIZED_ARCH, System.mapLibraryName("c2me-opts-natives-math"));
lookup = load0(libName);
if (lookup == null) {
currentMachineTarget = null;
DivineConfig.nativeAccelerationEnabled = false;
LOGGER.warn("Disabling native math optimization due to unsupported platform.");
} else {
try {
LOGGER.info("Attempting to call native library. If your game crashes right after this point, native acceleration may not be available for your system.");
int level = (int) linker.downcallHandle(
lookup.find("c2me_natives_get_system_isa").get(),
FunctionDescriptor.of(
ValueLayout.JAVA_INT,
ValueLayout.JAVA_BOOLEAN
)
).invokeExact(DivineConfig.allowAVX512);
ISATarget target;
if (DivineConfig.isaTargetLevelOverride != -1) {
target = (ISATarget) ISATarget.getInstance().getEnumConstants()[DivineConfig.isaTargetLevelOverride];
} else {
target = (ISATarget) ISATarget.getInstance().getEnumConstants()[level];
while (!target.isNativelySupported()) target = (ISATarget) ISATarget.getInstance().getEnumConstants()[target.ordinal() - 1];
}
currentMachineTarget = target;
LOGGER.info("Detected maximum supported ISA target: {}", currentMachineTarget);
} catch (Throwable e) {
throw new RuntimeException(e);
}
}
}
@Contract(pure = true)
public static @NotNull String getAvailabilityString() {
if (lookup != null) {
return String.format("Available, with ISA target %s", currentMachineTarget);
} else {
return "Unavailable";
}
}
private static @Nullable SymbolLookup load0(String libName) {
// load from resources
try (final InputStream in = NativeLoader.class.getClassLoader().getResourceAsStream(libName)) {
if (in == null) {
LOGGER.warn("Cannot find native library {}, possibly unsupported platform for native acceleration", libName);
return null;
}
final Path tempFile;
if (Boolean.getBoolean("vectorizedgen.preserveNative")) {
tempFile = Path.of(".", libName);
} else {
tempFile = Files.createTempFile(null, libName);
tempFile.toFile().deleteOnExit();
}
Files.copy(in, tempFile, StandardCopyOption.REPLACE_EXISTING);
return SymbolLookup.libraryLookup(tempFile, arena);
} catch (Throwable e) {
LOGGER.warn("Failed to load native library", e);
return null;
}
}
private static @NotNull String normalize(@NotNull String value) {
return value.toLowerCase(Locale.US).replaceAll("[^a-z0-9]+", "");
}
private static @NotNull String normalizeArch(String value) {
value = normalize(value);
if (value.matches("^(x8664|amd64|ia32e|em64t|x64)$")) {
return "x86_64";
}
if (value.matches("^(x8632|x86|i[3-6]86|ia32|x32)$")) {
return "x86_32";
}
if (value.matches("^(ia64|itanium64)$")) {
return "itanium_64";
}
if (value.matches("^(sparc|sparc32)$")) {
return "sparc_32";
}
if (value.matches("^(sparcv9|sparc64)$")) {
return "sparc_64";
}
if (value.matches("^(arm|arm32)$")) {
return "arm_32";
}
if ("aarch64".equals(value)) {
return "aarch_64";
}
if (value.matches("^(ppc|ppc32)$")) {
return "ppc_32";
}
if ("ppc64".equals(value)) {
return "ppc_64";
}
if ("ppc64le".equals(value)) {
return "ppcle_64";
}
if ("s390".equals(value)) {
return "s390_32";
}
if ("s390x".equals(value)) {
return "s390_64";
}
if ("loongarch64".equals(value)) {
return "loongarch_64";
}
return "unknown";
}
private static @NotNull String normalizeOs(String value) {
value = normalize(value);
if (value.startsWith("aix")) {
return "aix";
}
if (value.startsWith("hpux")) {
return "hpux";
}
if (value.startsWith("os400")) {
// Avoid the names such as os4000
if (value.length() <= 5 || !Character.isDigit(value.charAt(5))) {
return "os400";
}
}
if (value.startsWith("linux")) {
return "linux";
}
if (value.startsWith("macosx") || value.startsWith("osx") || value.startsWith("darwin")) {
return "osx";
}
if (value.startsWith("freebsd")) {
return "freebsd";
}
if (value.startsWith("openbsd")) {
return "openbsd";
}
if (value.startsWith("netbsd")) {
return "netbsd";
}
if (value.startsWith("solaris") || value.startsWith("sunos")) {
return "sunos";
}
if (value.startsWith("windows")) {
return "windows";
}
return "unknown";
}
}

View File

@@ -0,0 +1,25 @@
package org.bxteam.divinemc.math.isa;
import org.bxteam.divinemc.math.ISATarget;
public enum ISA_aarch64 implements ISATarget {
GENERIC("_generic", true);
private final String suffix;
private final boolean nativelySupported;
ISA_aarch64(String suffix, boolean nativelySupported) {
this.suffix = suffix;
this.nativelySupported = nativelySupported;
}
@Override
public String getSuffix() {
return this.suffix;
}
@Override
public boolean isNativelySupported() {
return this.nativelySupported;
}
}

View File

@@ -0,0 +1,34 @@
package org.bxteam.divinemc.math.isa;
import org.bxteam.divinemc.math.ISATarget;
public enum ISA_x86_64 implements ISATarget {
SSE2("_sse2", true), // 0
SSE4_1("_sse2", false), // 1, not implemented
SSE4_2("_sse4_2", true), // 2
AVX("_avx", true), // 3
AVX2("_avx2", true), // 4
AVX2ADL("_avx2adl", true), // 5
AVX512KNL("_avx2", false), // 6, not implemented
AVX512SKX("_avx512skx", true), // 7
AVX512ICL("_avx512icl", true), // 8
AVX512SPR("_avx512spr", true); // 9
private final String suffix;
private final boolean nativelySupported;
ISA_x86_64(String suffix, boolean nativelySupported) {
this.suffix = suffix;
this.nativelySupported = nativelySupported;
}
@Override
public String getSuffix() {
return this.suffix;
}
@Override
public boolean isNativelySupported() {
return this.nativelySupported;
}
}

View File

@@ -0,0 +1,27 @@
package org.bxteam.divinemc.server;
import ca.spottedleaf.moonrise.common.util.TickThread;
import java.util.concurrent.ThreadFactory;
public class ServerLevelTickExecutorThreadFactory implements ThreadFactory {
private final String worldName;
public ServerLevelTickExecutorThreadFactory(String worldName) {
this.worldName = worldName;
}
@Override
public Thread newThread(Runnable runnable) {
TickThread.ServerLevelTickThread tickThread = new TickThread.ServerLevelTickThread(runnable, "serverlevel-tick-worker [" + worldName + "]");
if (tickThread.isDaemon()) {
tickThread.setDaemon(false);
}
if (tickThread.getPriority() != 5) {
tickThread.setPriority(5);
}
return tickThread;
}
}

View File

@@ -0,0 +1,116 @@
package org.bxteam.divinemc.server.chunk;
import ca.spottedleaf.moonrise.common.PlatformHooks;
import io.netty.util.internal.PlatformDependent;
import net.objecthunter.exp4j.ExpressionBuilder;
import net.objecthunter.exp4j.function.Function;
import org.jetbrains.annotations.NotNull;
import oshi.util.tuples.Pair;
import java.util.function.BiFunction;
public enum ChunkSystemAlgorithms {
MOONRISE((configWorkerThreads, configIoThreads) -> {
int defaultWorkerThreads = Runtime.getRuntime().availableProcessors() / 2;
if (defaultWorkerThreads <= 4) {
defaultWorkerThreads = defaultWorkerThreads <= 3 ? 1 : 2;
} else {
defaultWorkerThreads = defaultWorkerThreads / 2;
}
defaultWorkerThreads = Integer.getInteger(PlatformHooks.get().getBrand() + ".WorkerThreadCount", Integer.valueOf(defaultWorkerThreads));
int workerThreads = configWorkerThreads;
if (workerThreads <= 0) {
workerThreads = defaultWorkerThreads;
}
final int ioThreads = Math.max(1, configIoThreads);
return new Pair<>(workerThreads, ioThreads);
}),
C2ME_AGGRESSIVE((configWorkerThreads, configIoThreads) -> {
String expression = """
max(
1,
min(
if( is_windows,
(cpus / 1.6),
(cpus / 1.3)
) - if(is_client, 1, 0),
( ( mem_gb - (if(is_client, 1.0, 0.5)) ) / 0.6 )
)
)
\040""";
int eval = configWorkerThreads <= 0 ? tryEvaluateExpression(expression) : configWorkerThreads;
return new Pair<>(eval, Math.max(1, configIoThreads));
}),
C2ME((configWorkerThreads, configIoThreads) -> {
String expression = """
max(
1,
min(
if( is_windows,
(cpus / 1.6 - 2),
(cpus / 1.2 - 2)
) - if(is_client, 2, 0),
if( is_j9vm,
( ( mem_gb - (if(is_client, 0.6, 0.2)) ) / 0.4 ),
( ( mem_gb - (if(is_client, 1.2, 0.6)) ) / 0.6 )
)
)
)
\040""";
int eval = configWorkerThreads <= 0 ? tryEvaluateExpression(expression) : configWorkerThreads;
return new Pair<>(eval, Math.max(1, configIoThreads));
});
private final BiFunction<Integer, Integer, Pair<Integer, Integer>> eval;
ChunkSystemAlgorithms(BiFunction<Integer, Integer, Pair<Integer, Integer>> eval) {
this.eval = eval;
}
private static int tryEvaluateExpression(String expression) {
return (int) Math.max(1,
new ExpressionBuilder(expression)
.variables("is_windows", "is_j9vm", "is_client", "cpus", "mem_gb")
.function(new Function("max", 2) {
@Override
public double apply(double... args) {
return Math.max(args[0], args[1]);
}
})
.function(new Function("min", 2) {
@Override
public double apply(double... args) {
return Math.min(args[0], args[1]);
}
})
.function(new Function("if", 3) {
@Override
public double apply(double... args) {
return args[0] != 0 ? args[1] : args[2];
}
})
.build()
.setVariable("is_windows", PlatformDependent.isWindows() ? 1 : 0)
.setVariable("is_j9vm", PlatformDependent.isJ9Jvm() ? 1 : 0)
.setVariable("is_client", 0)
.setVariable("cpus", Runtime.getRuntime().availableProcessors())
.setVariable("mem_gb", Runtime.getRuntime().maxMemory() / 1024.0 / 1024.0 / 1024.0)
.evaluate()
);
}
public int evalWorkers(final int configWorkerThreads, final int configIoThreads) {
return eval.apply(configWorkerThreads, configIoThreads).getA();
}
public int evalIO(final int configWorkerThreads, final int configIoThreads) {
return eval.apply(configWorkerThreads, configIoThreads).getB();
}
public @NotNull String asDebugString() {
return this + "(" + evalWorkers(-1, -1) + ")";
}
}

View File

@@ -0,0 +1,421 @@
package org.bxteam.divinemc.util;
import java.util.Collection;
import java.util.function.Supplier;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import org.jspecify.annotations.Nullable;
/**
* Assertion utility class that assists in validating arguments.
*
* <p>Useful for identifying programmer errors early and clearly at runtime.
*
* <p>For example, if the contract of a public method states it does not
* allow {@code null} arguments, {@code Assert} can be used to validate that
* contract. Doing this clearly indicates a contract violation when it
* occurs and protects the class's invariants.
*
* <p>Typically used to validate method arguments rather than configuration
* properties, to check for cases that are usually programmer errors rather
* than configuration errors. In contrast to configuration initialization
* code, there is usually no point in falling back to defaults in such methods.
*
* <p>This class is similar to JUnit's assertion library. If an argument value is
* deemed invalid, an {@link IllegalArgumentException} is thrown (typically).
* For example:
*
* <pre class="code">
* Assert.notNull(clazz, "The class must not be null");
* Assert.isTrue(i &gt; 0, "The value must be greater than zero");</pre>
*
* <p>Mainly for internal use within the framework; for a more comprehensive suite
* of assertion utilities consider {@code org.apache.commons.lang3.Validate} from
* <a href="https://commons.apache.org/proper/commons-lang/">Apache Commons Lang</a>,
* Google Guava's
* <a href="https://github.com/google/guava/wiki/PreconditionsExplained">Preconditions</a>,
* or similar third-party libraries.
*
* @author Keith Donald
* @author Juergen Hoeller
* @author Sam Brannen
* @author Colin Sampaleanu
* @author Rob Harrop
* @author Sebastien Deleuze
*/
public abstract class Assert {
/**
* Assert a boolean expression, throwing an {@code IllegalStateException}
* if the expression evaluates to {@code false}.
* <p>Call {@link #isTrue} if you wish to throw an {@code IllegalArgumentException}
* on an assertion failure.
* <pre class="code">Assert.state(id == null, "The id property must not already be initialized");</pre>
* @param expression a boolean expression
* @param message the exception message to use if the assertion fails
* @throws IllegalStateException if {@code expression} is {@code false}
*/
@Contract("false, _ -> fail")
public static void state(boolean expression, String message) {
if (!expression) {
throw new IllegalStateException(message);
}
}
/**
* Assert a boolean expression, throwing an {@code IllegalStateException}
* if the expression evaluates to {@code false}.
* <p>Call {@link #isTrue} if you wish to throw an {@code IllegalArgumentException}
* on an assertion failure.
* <pre class="code">
* Assert.state(entity.getId() == null,
* () -&gt; "ID for entity " + entity.getName() + " must not already be initialized");
* </pre>
* @param expression a boolean expression
* @param messageSupplier a supplier for the exception message to use if the
* assertion fails
* @throws IllegalStateException if {@code expression} is {@code false}
* @since 5.0
*/
@Contract("false, _ -> fail")
public static void state(boolean expression, Supplier<String> messageSupplier) {
if (!expression) {
throw new IllegalStateException(nullSafeGet(messageSupplier));
}
}
/**
* Assert a boolean expression, throwing an {@code IllegalArgumentException}
* if the expression evaluates to {@code false}.
* <pre class="code">Assert.isTrue(i &gt; 0, "The value must be greater than zero");</pre>
* @param expression a boolean expression
* @param message the exception message to use if the assertion fails
* @throws IllegalArgumentException if {@code expression} is {@code false}
*/
@Contract("false, _ -> fail")
public static void isTrue(boolean expression, String message) {
if (!expression) {
throw new IllegalArgumentException(message);
}
}
/**
* Assert a boolean expression, throwing an {@code IllegalArgumentException}
* if the expression evaluates to {@code false}.
* <pre class="code">
* Assert.isTrue(i &gt; 0, () -&gt; "The value '" + i + "' must be greater than zero");
* </pre>
* @param expression a boolean expression
* @param messageSupplier a supplier for the exception message to use if the
* assertion fails
* @throws IllegalArgumentException if {@code expression} is {@code false}
* @since 5.0
*/
@Contract("false, _ -> fail")
public static void isTrue(boolean expression, Supplier<String> messageSupplier) {
if (!expression) {
throw new IllegalArgumentException(nullSafeGet(messageSupplier));
}
}
/**
* Assert that an object is {@code null}.
* <pre class="code">Assert.isNull(value, "The value must be null");</pre>
* @param object the object to check
* @param message the exception message to use if the assertion fails
* @throws IllegalArgumentException if the object is not {@code null}
*/
@Contract("!null, _ -> fail")
public static void isNull(@Nullable Object object, String message) {
if (object != null) {
throw new IllegalArgumentException(message);
}
}
/**
* Assert that an object is {@code null}.
* <pre class="code">
* Assert.isNull(value, () -&gt; "The value '" + value + "' must be null");
* </pre>
* @param object the object to check
* @param messageSupplier a supplier for the exception message to use if the
* assertion fails
* @throws IllegalArgumentException if the object is not {@code null}
* @since 5.0
*/
@Contract("!null, _ -> fail")
public static void isNull(@Nullable Object object, Supplier<String> messageSupplier) {
if (object != null) {
throw new IllegalArgumentException(nullSafeGet(messageSupplier));
}
}
/**
* Assert that an object is not {@code null}.
* <pre class="code">Assert.notNull(clazz, "The class must not be null");</pre>
* @param object the object to check
* @param message the exception message to use if the assertion fails
* @throws IllegalArgumentException if the object is {@code null}
*/
@Contract("null, _ -> fail")
public static void notNull(@Nullable Object object, String message) {
if (object == null) {
throw new IllegalArgumentException(message);
}
}
/**
* Assert that an object is not {@code null}.
* <pre class="code">
* Assert.notNull(entity.getId(),
* () -&gt; "ID for entity " + entity.getName() + " must not be null");
* </pre>
* @param object the object to check
* @param messageSupplier a supplier for the exception message to use if the
* assertion fails
* @throws IllegalArgumentException if the object is {@code null}
* @since 5.0
*/
@Contract("null, _ -> fail")
public static void notNull(@Nullable Object object, Supplier<String> messageSupplier) {
if (object == null) {
throw new IllegalArgumentException(nullSafeGet(messageSupplier));
}
}
/**
* Assert that an array contains no {@code null} elements.
* <p>Note: Does not complain if the array is empty!
* <pre class="code">Assert.noNullElements(array, "The array must contain non-null elements");</pre>
* @param array the array to check
* @param message the exception message to use if the assertion fails
* @throws IllegalArgumentException if the object array contains a {@code null} element
*/
public static void noNullElements(Object @Nullable [] array, String message) {
if (array != null) {
for (Object element : array) {
if (element == null) {
throw new IllegalArgumentException(message);
}
}
}
}
/**
* Assert that an array contains no {@code null} elements.
* <p>Note: Does not complain if the array is empty!
* <pre class="code">
* Assert.noNullElements(array, () -&gt; "The " + arrayType + " array must contain non-null elements");
* </pre>
* @param array the array to check
* @param messageSupplier a supplier for the exception message to use if the
* assertion fails
* @throws IllegalArgumentException if the object array contains a {@code null} element
* @since 5.0
*/
public static void noNullElements(Object @Nullable [] array, Supplier<String> messageSupplier) {
if (array != null) {
for (Object element : array) {
if (element == null) {
throw new IllegalArgumentException(nullSafeGet(messageSupplier));
}
}
}
}
/**
* Assert that a collection contains no {@code null} elements.
* <p>Note: Does not complain if the collection is empty!
* <pre class="code">Assert.noNullElements(collection, "Collection must contain non-null elements");</pre>
* @param collection the collection to check
* @param message the exception message to use if the assertion fails
* @throws IllegalArgumentException if the collection contains a {@code null} element
* @since 5.2
*/
public static void noNullElements(@Nullable Collection<?> collection, String message) {
if (collection != null) {
for (Object element : collection) {
if (element == null) {
throw new IllegalArgumentException(message);
}
}
}
}
/**
* Assert that a collection contains no {@code null} elements.
* <p>Note: Does not complain if the collection is empty!
* <pre class="code">
* Assert.noNullElements(collection, () -&gt; "Collection " + collectionName + " must contain non-null elements");
* </pre>
* @param collection the collection to check
* @param messageSupplier a supplier for the exception message to use if the
* assertion fails
* @throws IllegalArgumentException if the collection contains a {@code null} element
* @since 5.2
*/
public static void noNullElements(@Nullable Collection<?> collection, Supplier<String> messageSupplier) {
if (collection != null) {
for (Object element : collection) {
if (element == null) {
throw new IllegalArgumentException(nullSafeGet(messageSupplier));
}
}
}
}
/**
* Assert that the provided object is an instance of the provided class.
* <pre class="code">Assert.instanceOf(Foo.class, foo, "Foo expected");</pre>
* @param type the type to check against
* @param obj the object to check
* @param message a message which will be prepended to provide further context.
* If it is empty or ends in ":" or ";" or "," or ".", a full exception message
* will be appended. If it ends in a space, the name of the offending object's
* type will be appended. In any other case, a ":" with a space and the name
* of the offending object's type will be appended.
* @throws IllegalArgumentException if the object is not an instance of type
*/
@Contract("_, null, _ -> fail")
public static void isInstanceOf(Class<?> type, @Nullable Object obj, String message) {
notNull(type, "Type to check against must not be null");
if (!type.isInstance(obj)) {
instanceCheckFailed(type, obj, message);
}
}
/**
* Assert that the provided object is an instance of the provided class.
* <pre class="code">
* Assert.instanceOf(Foo.class, foo, () -&gt; "Processing " + Foo.class.getSimpleName() + ":");
* </pre>
* @param type the type to check against
* @param obj the object to check
* @param messageSupplier a supplier for the exception message to use if the
* assertion fails. See {@link #isInstanceOf(Class, Object, String)} for details.
* @throws IllegalArgumentException if the object is not an instance of type
* @since 5.0
*/
@Contract("_, null, _ -> fail")
public static void isInstanceOf(Class<?> type, @Nullable Object obj, Supplier<String> messageSupplier) {
notNull(type, "Type to check against must not be null");
if (!type.isInstance(obj)) {
instanceCheckFailed(type, obj, nullSafeGet(messageSupplier));
}
}
/**
* Assert that the provided object is an instance of the provided class.
* <pre class="code">Assert.instanceOf(Foo.class, foo);</pre>
* @param type the type to check against
* @param obj the object to check
* @throws IllegalArgumentException if the object is not an instance of type
*/
@Contract("_, null -> fail")
public static void isInstanceOf(Class<?> type, @Nullable Object obj) {
isInstanceOf(type, obj, "");
}
/**
* Assert that {@code superType.isAssignableFrom(subType)} is {@code true}.
* <pre class="code">Assert.isAssignable(Number.class, myClass, "Number expected");</pre>
* @param superType the supertype to check against
* @param subType the subtype to check
* @param message a message which will be prepended to provide further context.
* If it is empty or ends in ":" or ";" or "," or ".", a full exception message
* will be appended. If it ends in a space, the name of the offending subtype
* will be appended. In any other case, a ":" with a space and the name of the
* offending subtype will be appended.
* @throws IllegalArgumentException if the classes are not assignable
*/
@Contract("_, null, _ -> fail")
public static void isAssignable(Class<?> superType, @Nullable Class<?> subType, String message) {
notNull(superType, "Supertype to check against must not be null");
if (subType == null || !superType.isAssignableFrom(subType)) {
assignableCheckFailed(superType, subType, message);
}
}
/**
* Assert that {@code superType.isAssignableFrom(subType)} is {@code true}.
* <pre class="code">
* Assert.isAssignable(Number.class, myClass, () -&gt; "Processing " + myAttributeName + ":");
* </pre>
* @param superType the supertype to check against
* @param subType the subtype to check
* @param messageSupplier a supplier for the exception message to use if the
* assertion fails. See {@link #isAssignable(Class, Class, String)} for details.
* @throws IllegalArgumentException if the classes are not assignable
* @since 5.0
*/
@Contract("_, null, _ -> fail")
public static void isAssignable(Class<?> superType, @Nullable Class<?> subType, Supplier<String> messageSupplier) {
notNull(superType, "Supertype to check against must not be null");
if (subType == null || !superType.isAssignableFrom(subType)) {
assignableCheckFailed(superType, subType, nullSafeGet(messageSupplier));
}
}
/**
* Assert that {@code superType.isAssignableFrom(subType)} is {@code true}.
* <pre class="code">Assert.isAssignable(Number.class, myClass);</pre>
* @param superType the supertype to check
* @param subType the subtype to check
* @throws IllegalArgumentException if the classes are not assignable
*/
@Contract("_, null -> fail")
public static void isAssignable(Class<?> superType, @Nullable Class<?> subType) {
isAssignable(superType, subType, "");
}
private static void instanceCheckFailed(Class<?> type, @Nullable Object obj, @Nullable String msg) {
String className = (obj != null ? obj.getClass().getName() : "null");
String result = "";
boolean defaultMessage = true;
if (StringUtil.hasLength(msg)) {
if (endsWithSeparator(msg)) {
result = msg + " ";
}
else {
result = messageWithTypeName(msg, className);
defaultMessage = false;
}
}
if (defaultMessage) {
result = result + ("Object of class [" + className + "] must be an instance of " + type);
}
throw new IllegalArgumentException(result);
}
private static void assignableCheckFailed(Class<?> superType, @Nullable Class<?> subType, @Nullable String msg) {
String result = "";
boolean defaultMessage = true;
if (StringUtil.hasLength(msg)) {
if (endsWithSeparator(msg)) {
result = msg + " ";
}
else {
result = messageWithTypeName(msg, subType);
defaultMessage = false;
}
}
if (defaultMessage) {
result = result + (subType + " is not assignable to " + superType);
}
throw new IllegalArgumentException(result);
}
private static boolean endsWithSeparator(@NotNull String msg) {
return (msg.endsWith(":") || msg.endsWith(";") || msg.endsWith(",") || msg.endsWith("."));
}
@Contract(pure = true)
private static @NotNull String messageWithTypeName(@NotNull String msg, @Nullable Object typeName) {
return msg + (msg.endsWith(" ") ? "" : ": ") + typeName;
}
private static @Nullable String nullSafeGet(@Nullable Supplier<String> messageSupplier) {
return (messageSupplier != null ? messageSupplier.get() : null);
}
}

View File

@@ -0,0 +1,27 @@
package org.bxteam.divinemc.util;
public final class Assertions {
public static void assertTrue(boolean value, String message) {
if (!value) {
final AssertionError error = new AssertionError(message);
error.printStackTrace();
throw error;
}
}
public static void assertTrue(boolean state, String format, Object... args) {
if (!state) {
final AssertionError error = new AssertionError(String.format(format, args));
error.printStackTrace();
throw error;
}
}
public static void assertTrue(boolean value) {
if (!value) {
final AssertionError error = new AssertionError();
error.printStackTrace();
throw error;
}
}
}

View File

@@ -0,0 +1,98 @@
package org.bxteam.divinemc.util;
import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
import net.minecraft.world.level.block.entity.TickingBlockEntity;
import java.util.Arrays;
import java.util.Collection;
/**
* A list for ServerLevel's blockEntityTickers
* <p>
* This list is behaving identically to ObjectArrayList, but it has an additional method, `removeAllByIndex`, that allows a list of integers to be passed indicating what
* indexes should be deleted from the list
* <p>
* This is faster than using removeAll, since we don't need to compare the identity of each block entity, and faster than looping thru each index manually and deleting with remove,
* since we don't need to resize the array every single remove.
*/
public final class BlockEntityTickersList extends ObjectArrayList<TickingBlockEntity> {
private final IntOpenHashSet toRemove = new IntOpenHashSet();
private int startSearchFromIndex = -1;
/** Creates a new array list with {@link #DEFAULT_INITIAL_CAPACITY} capacity. */
public BlockEntityTickersList() {
super();
}
/**
* Creates a new array list and fills it with a given collection.
*
* @param c a collection that will be used to fill the array list.
*/
public BlockEntityTickersList(final Collection<? extends TickingBlockEntity> c) {
super(c);
}
/**
* Marks an entry as removed
*
* @param index the index of the item on the list to be marked as removed
*/
public void markAsRemoved(final int index) {
// The block entities list always loop starting from 0, so we only need to check if the startSearchFromIndex is -1 and that's it
if (this.startSearchFromIndex == -1)
this.startSearchFromIndex = index;
this.toRemove.add(index);
}
/**
* Removes elements that have been marked as removed.
*/
public void removeMarkedEntries() {
if (this.startSearchFromIndex == -1) // No entries in the list, skip
return;
removeAllByIndex(startSearchFromIndex, toRemove);
toRemove.clear();
this.startSearchFromIndex = -1; // Reset the start search index
}
/**
* Removes elements by their index.
*/
private void removeAllByIndex(final int startSearchFromIndex, final IntOpenHashSet c) { // can't use Set<Integer> because we want to avoid autoboxing when using contains
final int requiredMatches = c.size();
if (requiredMatches == 0)
return; // exit early, we don't need to do anything
final Object[] a = this.a;
int j = startSearchFromIndex;
int matches = 0;
for (int i = startSearchFromIndex; i < size; i++) { // If the user knows the first index to be removed, we can skip a lot of unnecessary comparsions
if (!c.contains(i)) {
// TODO: It can be possible to optimize this loop by tracking the start/finish and then using arraycopy to "skip" the elements,
// this would optimize cases where the index to be removed are far apart, HOWEVER it does have a big performance impact if you are doing
// "arraycopy" for each element
a[j++] = a[i];
} else {
matches++;
}
if (matches == requiredMatches) { // Exit the loop if we already removed everything, we don't need to check anything else
// We need to update the final size here, because we know that we already found everything!
// Because we know that the size must be currentSize - requiredMatches (because we have matched everything), let's update the value
// However, we need to copy the rest of the stuff over
if (i != (size - 1)) { // If it isn't the last index...
// i + 1 because we want to copy the *next* element over
// and the size - i - 1 is because we want to get the current size, minus the current index (which is i), and then - 1 because we want to copy -1 ahead (remember, we are adding +1 to copy the *next* element)
System.arraycopy(a, i + 1, a, j, size - i - 1);
}
j = size - requiredMatches;
break;
}
}
Arrays.fill(a, j, size, null);
size = j;
}
}

View File

@@ -0,0 +1,35 @@
package org.bxteam.divinemc.util;
import java.io.File;
import java.io.IOException;
public final class Files {
public static void deleteRecursively(File dir) throws IOException {
if (dir == null || !dir.isDirectory()) {
return;
}
try {
File[] files = dir.listFiles();
if (files == null) {
throw new IOException("Error enumerating directory during recursive delete operation: " + dir.getAbsolutePath());
}
for (File child : files) {
if (child.isDirectory()) {
Files.deleteRecursively(child);
} else if (child.isFile()) {
if (!child.delete()) {
throw new IOException("Error deleting file during recursive delete operation: " + child.getAbsolutePath());
}
}
}
if (!dir.delete()) {
throw new IOException("Error deleting directory during recursive delete operation: " + dir.getAbsolutePath());
}
} catch (SecurityException ex) {
throw new IOException("Security error during recursive delete operation", ex);
}
}
}

View File

@@ -0,0 +1,12 @@
package org.bxteam.divinemc.util;
public final class MemoryUtil {
public static int[] byte2int(byte[] data) {
if (data == null) return null;
int[] ints = new int[data.length];
for (int i = 0; i < data.length; i++) {
ints[i] = data[i] & 0xff;
}
return ints;
}
}

View File

@@ -0,0 +1,108 @@
package org.bxteam.divinemc.util;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.Nullable;
import java.util.Arrays;
public class ObjectUtil {
@Contract("null, null -> true; null, _ -> false; _, null -> false")
public static boolean nullSafeEquals(@Nullable Object o1, @Nullable Object o2) {
if (o1 == o2) {
return true;
}
if (o1 == null || o2 == null) {
return false;
}
if (o1.equals(o2)) {
return true;
}
if (o1.getClass().isArray() && o2.getClass().isArray()) {
return arrayEquals(o1, o2);
}
return false;
}
/**
* Compare the given arrays with {@code Arrays.equals}, performing an equality
* check based on the array elements rather than the array reference.
* @param o1 first array to compare
* @param o2 second array to compare
* @return whether the given objects are equal
* @see #nullSafeEquals(Object, Object)
* @see java.util.Arrays#equals
*/
private static boolean arrayEquals(Object o1, Object o2) {
if (o1 instanceof Object[] objects1 && o2 instanceof Object[] objects2) {
return Arrays.equals(objects1, objects2);
}
if (o1 instanceof boolean[] booleans1 && o2 instanceof boolean[] booleans2) {
return Arrays.equals(booleans1, booleans2);
}
if (o1 instanceof byte[] bytes1 && o2 instanceof byte[] bytes2) {
return Arrays.equals(bytes1, bytes2);
}
if (o1 instanceof char[] chars1 && o2 instanceof char[] chars2) {
return Arrays.equals(chars1, chars2);
}
if (o1 instanceof double[] doubles1 && o2 instanceof double[] doubles2) {
return Arrays.equals(doubles1, doubles2);
}
if (o1 instanceof float[] floats1 && o2 instanceof float[] floats2) {
return Arrays.equals(floats1, floats2);
}
if (o1 instanceof int[] ints1 && o2 instanceof int[] ints2) {
return Arrays.equals(ints1, ints2);
}
if (o1 instanceof long[] longs1 && o2 instanceof long[] longs2) {
return Arrays.equals(longs1, longs2);
}
if (o1 instanceof short[] shorts1 && o2 instanceof short[] shorts2) {
return Arrays.equals(shorts1, shorts2);
}
return false;
}
/**
* Return a hash code for the given object; typically the value of
* {@code Object#hashCode()}}. If the object is an array,
* this method will delegate to any of the {@code Arrays.hashCode}
* methods. If the object is {@code null}, this method returns 0.
* @see Object#hashCode()
* @see Arrays
*/
public static int nullSafeHashCode(@Nullable Object obj) {
if (obj == null) {
return 0;
}
if (obj.getClass().isArray()) {
if (obj instanceof Object[] objects) {
return Arrays.hashCode(objects);
}
if (obj instanceof boolean[] booleans) {
return Arrays.hashCode(booleans);
}
if (obj instanceof byte[] bytes) {
return Arrays.hashCode(bytes);
}
if (obj instanceof char[] chars) {
return Arrays.hashCode(chars);
}
if (obj instanceof double[] doubles) {
return Arrays.hashCode(doubles);
}
if (obj instanceof float[] floats) {
return Arrays.hashCode(floats);
}
if (obj instanceof int[] ints) {
return Arrays.hashCode(ints);
}
if (obj instanceof long[] longs) {
return Arrays.hashCode(longs);
}
if (obj instanceof short[] shorts) {
return Arrays.hashCode(shorts);
}
}
return obj.hashCode();
}
}

View File

@@ -0,0 +1,40 @@
package org.bxteam.divinemc.util;
import net.minecraft.util.Mth;
import net.minecraft.util.RandomSource;
import net.minecraft.world.level.levelgen.LegacyRandomSource;
import net.minecraft.world.level.levelgen.PositionalRandomFactory;
import net.minecraft.world.level.levelgen.SingleThreadedRandomSource;
import net.minecraft.world.level.levelgen.Xoroshiro128PlusPlus;
import net.minecraft.world.level.levelgen.XoroshiroRandomSource;
import org.jetbrains.annotations.NotNull;
public final class RandomUtil {
public static @NotNull RandomSource getRandom(PositionalRandomFactory deriver) {
if (deriver instanceof XoroshiroRandomSource.XoroshiroPositionalRandomFactory) {
return new XoroshiroRandomSource(0L, 0L);
}
if (deriver instanceof LegacyRandomSource.LegacyPositionalRandomFactory) {
return new SingleThreadedRandomSource(0L);
}
throw new IllegalArgumentException();
}
private static final ThreadLocal<XoroshiroRandomSource> xoroshiro = ThreadLocal.withInitial(() -> new XoroshiroRandomSource(0L, 0L));
private static final ThreadLocal<SingleThreadedRandomSource> simple = ThreadLocal.withInitial(() -> new SingleThreadedRandomSource(0L));
public static void derive(PositionalRandomFactory deriver, RandomSource random, int x, int y, int z) {
if (deriver instanceof final XoroshiroRandomSource.XoroshiroPositionalRandomFactory deriver1) {
final Xoroshiro128PlusPlus implementation = ((XoroshiroRandomSource) random).randomNumberGenerator;
implementation.seedLo = (Mth.getSeed(x, y, z) ^ deriver1.seedLo());
implementation.seedHi = (deriver1.seedHi());
return;
}
if (deriver instanceof LegacyRandomSource.LegacyPositionalRandomFactory(long seed)) {
final SingleThreadedRandomSource random1 = (SingleThreadedRandomSource) random;
random1.setSeed(Mth.getSeed(x, y, z) ^ seed);
return;
}
throw new IllegalArgumentException();
}
}

View File

@@ -0,0 +1,9 @@
package org.bxteam.divinemc.util;
import org.jetbrains.annotations.Nullable;
public final class StringUtil {
public static boolean hasLength(@Nullable String str) {
return (str != null && !str.isEmpty());
}
}

View File

@@ -0,0 +1,217 @@
package org.bxteam.divinemc.util.collections;
import it.unimi.dsi.fastutil.bytes.ByteBytePair;
import it.unimi.dsi.fastutil.ints.IntArrayList;
import net.minecraft.core.BlockPos;
import net.minecraft.util.RandomSource;
import net.minecraft.world.entity.ai.behavior.LongJumpToRandomPos;
import java.util.AbstractList;
import java.util.Arrays;
import java.util.concurrent.ConcurrentHashMap;
public class LongJumpChoiceList extends AbstractList<LongJumpToRandomPos.PossibleJump> {
/**
* A cache of choice lists for different ranges. The elements must not be mutated, but copied instead.
* In vanilla minecraft there should be two elements, one for frog jumps and one for goat jumps.
*/
private static final ConcurrentHashMap<ByteBytePair, LongJumpChoiceList> CHOICE_LISTS = new ConcurrentHashMap<>();
/**
* The choice list for frog jumps. Skipping the hash map access. Must not be mutated, but copied instead.
*/
private static final LongJumpChoiceList FROG_JUMP = new LongJumpChoiceList((byte) 4, (byte) 2);
/**
* The choice list for goat jumps. Skipping the hash map access. Must not be mutated, but copied instead.
*/
private static final LongJumpChoiceList GOAT_JUMP = new LongJumpChoiceList((byte) 5, (byte) 5);
private final BlockPos origin;
private final IntArrayList[] packedOffsetsByDistanceSq;
private final int[] weightByDistanceSq;
private int totalWeight;
/**
* Constructs a new LongJumpChoiceList with the given horizontal and vertical range.
* We avoid creating too many objects here, e.g. LongJumpTask.Target is not created yet.
* @param horizontalRange the horizontal range
* @param verticalRange the vertical range
*/
public LongJumpChoiceList(byte horizontalRange, byte verticalRange) {
if (horizontalRange < 0 || verticalRange < 0) {
throw new IllegalArgumentException("The ranges must be within 0..127!");
}
this.origin = BlockPos.ZERO;
int maxSqDistance = horizontalRange*horizontalRange * 2 + verticalRange*verticalRange;
this.packedOffsetsByDistanceSq = new IntArrayList[maxSqDistance];
this.weightByDistanceSq = new int[maxSqDistance];
for (int x = -horizontalRange; x <= horizontalRange; x++) {
for (int y = -verticalRange; y <= verticalRange; y++) {
for (int z = -horizontalRange; z <= horizontalRange; z++) {
int squaredDistance = x * x + y * y + z * z;
int index = squaredDistance - 1;
if (index >= 0) { //exclude origin (distance 0)
int packedOffset = this.packOffset(x, y, z);
IntArrayList offsets = this.packedOffsetsByDistanceSq[index];
if (offsets == null) {
this.packedOffsetsByDistanceSq[index] = offsets = new IntArrayList();
}
offsets.add(packedOffset);
this.weightByDistanceSq[index] += squaredDistance;
this.totalWeight += squaredDistance;
}
}
}
}
}
public LongJumpChoiceList(BlockPos origin, IntArrayList[] packedOffsetsByDistanceSq, int[] weightByDistanceSq, int totalWeight) {
this.origin = origin;
this.packedOffsetsByDistanceSq = packedOffsetsByDistanceSq;
this.weightByDistanceSq = weightByDistanceSq;
this.totalWeight = totalWeight;
}
private int packOffset(int x, int y, int z) {
return (x + 128) | ((y + 128) << 8) | ((z + 128) << 16);
}
private int unpackX(int packedOffset) {
return (packedOffset & 0xFF) - 128;
}
private int unpackY(int packedOffset) {
return ((packedOffset >>> 8) & 0xFF) - 128;
}
private int unpackZ(int packedOffset) {
return ((packedOffset >>> 16) & 0xFF) - 128;
}
/**
* Returns a LongJumpChoiceList for the given center position and ranges.
* Quickly creates the list by copying an existing, memoized list.
* @param centerPos the center position
* @param horizontalRange the horizontal range
* @param verticalRange the vertical range
* @return a LongJumpChoiceList for the given parameters
*/
public static LongJumpChoiceList forCenter(BlockPos centerPos, byte horizontalRange, byte verticalRange) {
if (horizontalRange < 0 || verticalRange < 0) {
throw new IllegalArgumentException("The ranges must be within 0..127!");
}
LongJumpChoiceList jumpDestinationsList;
short range = (short) ((horizontalRange << 8) | verticalRange);
if (range == ((4 << 8) | 2)) {
//Frog jump
jumpDestinationsList = LongJumpChoiceList.FROG_JUMP;
} else if (range == ((5 << 8) | 5)) {
//Goat jump
jumpDestinationsList = LongJumpChoiceList.GOAT_JUMP;
} else {
jumpDestinationsList = LongJumpChoiceList.CHOICE_LISTS.computeIfAbsent(
ByteBytePair.of(horizontalRange, verticalRange),
key -> new LongJumpChoiceList(key.leftByte(), key.rightByte())
);
}
return jumpDestinationsList.offsetCopy(centerPos);
}
private LongJumpChoiceList offsetCopy(BlockPos offset) {
IntArrayList[] packedOffsetsByDistanceSq = new IntArrayList[this.packedOffsetsByDistanceSq.length];
for (int i = 0; i < packedOffsetsByDistanceSq.length; i++) {
IntArrayList packedOffsets = this.packedOffsetsByDistanceSq[i];
if (packedOffsets != null) {
packedOffsetsByDistanceSq[i] = packedOffsets.clone();
}
}
return new LongJumpChoiceList(
this.origin.offset(offset),
packedOffsetsByDistanceSq,
Arrays.copyOf(this.weightByDistanceSq, this.weightByDistanceSq.length), this.totalWeight);
}
/**
* Removes and returns a random target from the list, weighted by squared distance.
* @param random the random number generator
* @return a random target
*/
public LongJumpToRandomPos.PossibleJump removeRandomWeightedByDistanceSq(RandomSource random) {
int targetWeight = random.nextInt(this.totalWeight);
for (int index = 0; targetWeight >= 0 && index < this.weightByDistanceSq.length; index++) {
targetWeight -= this.weightByDistanceSq[index];
if (targetWeight < 0) {
int distanceSq = index + 1;
IntArrayList elementsOfDistance = this.packedOffsetsByDistanceSq[index];
int elementIndex = random.nextInt(elementsOfDistance.size());
//fast remove by swapping to end and removing, order does not matter
elementsOfDistance.set(elementIndex, elementsOfDistance.set(elementsOfDistance.size() - 1, elementsOfDistance.getInt(elementIndex)));
int packedOffset = elementsOfDistance.removeInt(elementsOfDistance.size() - 1);
this.weightByDistanceSq[index] -= distanceSq;
this.totalWeight -= distanceSq;
return new LongJumpToRandomPos.PossibleJump(this.origin.offset(this.unpackX(packedOffset), this.unpackY(packedOffset), this.unpackZ(packedOffset)), distanceSq);
}
}
return null;
}
@Override
public LongJumpToRandomPos.PossibleJump get(int index) {
int elementIndex = index;
IntArrayList[] offsetsByDistanceSq = this.packedOffsetsByDistanceSq;
for (int distanceSq = 0; distanceSq < offsetsByDistanceSq.length; distanceSq++) {
IntArrayList packedOffsets = offsetsByDistanceSq[distanceSq];
if (packedOffsets != null) {
if (elementIndex < packedOffsets.size()) {
int packedOffset = packedOffsets.getInt(elementIndex);
return new LongJumpToRandomPos.PossibleJump(this.origin.offset(this.unpackX(packedOffset), this.unpackY(packedOffset), this.unpackZ(packedOffset)), distanceSq);
}
elementIndex -= packedOffsets.size();
}
}
throw new IndexOutOfBoundsException();
}
@Override
public boolean isEmpty() {
return this.totalWeight == 0;
}
@Override
public int size() {
int size = 0;
for (IntArrayList packedOffsets : this.packedOffsetsByDistanceSq) {
if (packedOffsets != null) {
size += packedOffsets.size();
}
}
return size;
}
@Override
public LongJumpToRandomPos.PossibleJump remove(int index) {
int elementIndex = index;
IntArrayList[] offsetsByDistanceSq = this.packedOffsetsByDistanceSq;
for (int distanceSq = 0; distanceSq < offsetsByDistanceSq.length; distanceSq++) {
IntArrayList packedOffsets = offsetsByDistanceSq[distanceSq];
if (packedOffsets != null) {
if (elementIndex < packedOffsets.size()) {
int packedOffset = packedOffsets.getInt(elementIndex);
packedOffsets.set(elementIndex, packedOffsets.set(packedOffsets.size() - 1, packedOffsets.getInt(elementIndex)));
packedOffsets.removeInt(packedOffsets.size() - 1);
this.weightByDistanceSq[distanceSq] -= distanceSq;
this.totalWeight -= distanceSq;
return new LongJumpToRandomPos.PossibleJump(this.origin.offset(this.unpackX(packedOffset), this.unpackY(packedOffset), this.unpackZ(packedOffset)), distanceSq);
}
elementIndex -= packedOffsets.size();
}
}
throw new IndexOutOfBoundsException();
}
}

View File

@@ -0,0 +1,150 @@
package org.bxteam.divinemc.util.collections;
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
import java.util.AbstractList;
import java.util.BitSet;
import java.util.Iterator;
import java.util.Spliterator;
import java.util.Spliterators;
import java.util.function.Consumer;
public class MaskedList<E> extends AbstractList<E> {
private final ObjectArrayList<E> allElements;
private final BitSet visibleMask;
private final Object2IntOpenHashMap<E> element2Index;
private final boolean defaultVisibility;
private int numCleared;
public MaskedList(ObjectArrayList<E> allElements, boolean defaultVisibility) {
this.allElements = new ObjectArrayList<>();
this.visibleMask = new BitSet();
this.defaultVisibility = defaultVisibility;
this.element2Index = new Object2IntOpenHashMap<>();
this.element2Index.defaultReturnValue(-1);
this.addAll(allElements);
}
public MaskedList() {
this(new ObjectArrayList<>(), true);
}
public int totalSize() {
return this.allElements.size();
}
public void addOrSet(E element, boolean visible) {
int index = this.element2Index.getInt(element);
if (index != -1) {
this.visibleMask.set(index, visible);
} else {
this.add(element);
this.setVisible(element, visible);
}
}
public void setVisible(E element, final boolean visible) {
int index = this.element2Index.getInt(element);
if (index != -1) {
this.visibleMask.set(index, visible);
}
}
@Override
public Iterator<E> iterator() {
return new Iterator<>() {
int nextIndex = 0;
int cachedNext = -1;
@Override
public boolean hasNext() {
return (this.cachedNext = MaskedList.this.visibleMask.nextSetBit(this.nextIndex)) != -1;
}
@Override
public E next() {
int index = this.cachedNext;
this.cachedNext = -1;
this.nextIndex = index + 1;
return MaskedList.this.allElements.get(index);
}
};
}
@Override
public Spliterator<E> spliterator() {
return new Spliterators.AbstractSpliterator<E>(Long.MAX_VALUE, Spliterator.ORDERED | Spliterator.NONNULL) {
int nextIndex = 0;
@Override
public boolean tryAdvance(Consumer<? super E> action) {
int index = MaskedList.this.visibleMask.nextSetBit(this.nextIndex);
if (index == -1) {
return false;
}
this.nextIndex = index + 1;
action.accept(MaskedList.this.allElements.get(index));
return true;
}
};
}
@Override
public boolean add(E e) {
int oldIndex = this.element2Index.put(e, this.allElements.size());
if (oldIndex != -1) {
throw new IllegalStateException("MaskedList must not contain duplicates! Trying to add " + e + " but it is already present at index " + oldIndex + ". Current size: " + this.allElements.size());
}
this.visibleMask.set(this.allElements.size(), this.defaultVisibility);
return this.allElements.add(e);
}
@Override
public boolean remove(Object o) {
int index = this.element2Index.removeInt(o);
if (index == -1) {
return false;
}
this.visibleMask.clear(index);
this.allElements.set(index, null);
this.numCleared++;
if (this.numCleared * 2 > this.allElements.size()) {
ObjectArrayList<E> clonedElements = this.allElements.clone();
BitSet clonedVisibleMask = (BitSet) this.visibleMask.clone();
this.allElements.clear();
this.visibleMask.clear();
this.element2Index.clear();
for (int i = 0; i < clonedElements.size(); i++) {
E element = clonedElements.get(i);
int newIndex = this.allElements.size();
this.allElements.add(element);
this.visibleMask.set(newIndex, clonedVisibleMask.get(i));
this.element2Index.put(element, newIndex);
}
this.numCleared = 0;
}
return true;
}
@Override
public E get(int index) {
if (index < 0 || index >= this.size()) {
throw new IndexOutOfBoundsException(index);
}
int i = 0;
while (index >= 0) {
index--;
i = this.visibleMask.nextSetBit(i + 1);
}
return this.allElements.get(i);
}
@Override
public int size() {
return this.visibleMask.cardinality();
}
}

View File

@@ -0,0 +1,53 @@
package org.bxteam.divinemc.util.entity;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.entity.ai.Brain;
import net.minecraft.world.entity.ai.sensing.Sensor;
import net.minecraft.world.entity.ai.sensing.SensorType;
public class SensorHelper {
public static void disableSensor(LivingEntity brainedEntity, SensorType<?> sensorType) {
if (brainedEntity.level().isClientSide()) {
return;
}
Brain<?> brain = brainedEntity.getBrain();
Sensor<?> sensor = (brain).sensors.get(sensorType);
if (sensor != null) {
long lastSenseTime = sensor.timeToTick;
int senseInterval = sensor.scanRate;
long maxMultipleOfSenseInterval = Long.MAX_VALUE - (Long.MAX_VALUE % senseInterval);
maxMultipleOfSenseInterval -= senseInterval;
maxMultipleOfSenseInterval += lastSenseTime;
sensor.timeToTick = (maxMultipleOfSenseInterval);
}
}
public static <T extends LivingEntity, U extends Sensor<T>> void enableSensor(T brainedEntity, SensorType<U> sensorType) {
enableSensor(brainedEntity, sensorType, false);
}
public static <T extends LivingEntity, U extends Sensor<T>> void enableSensor(T brainedEntity, SensorType<U> sensorType, boolean extraTick) {
if (brainedEntity.level().isClientSide()) {
return;
}
Brain<?> brain = brainedEntity.getBrain();
U sensor = (U) (brain).sensors.get(sensorType);
if (sensor != null) {
long lastSenseTime = sensor.timeToTick;
int senseInterval = sensor.scanRate;
if (lastSenseTime > senseInterval) {
lastSenseTime = lastSenseTime % senseInterval;
if (extraTick) {
(sensor).timeToTick = (0L);
sensor.tick((ServerLevel) brainedEntity.level(), brainedEntity);
}
}
sensor.timeToTick = (lastSenseTime);
}
}
}

View File

@@ -0,0 +1,165 @@
package org.bxteam.divinemc.util.structure;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Vec3i;
import net.minecraft.world.phys.AABB;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.List;
public class BoxOctree {
private static final int subdivideThreshold = 10;
private static final int maximumDepth = 3;
private final AABB boundary;
private final Vec3i size;
private final int depth;
private final List<AABB> innerBoxes = new ArrayList<>();
private final List<BoxOctree> childrenOctants = new ArrayList<>();
public BoxOctree(AABB axisAlignedBB) {
this(axisAlignedBB, 0);
}
private BoxOctree(@NotNull AABB axisAlignedBB, int parentDepth) {
boundary = axisAlignedBB.move(0, 0, 0); // deep copy
size = new Vec3i(roundAwayFromZero(boundary.getXsize()), roundAwayFromZero(boundary.getYsize()), roundAwayFromZero(boundary.getZsize()));
depth = parentDepth + 1;
}
private int roundAwayFromZero(double value) {
return (value >= 0) ? (int)Math.ceil(value) : (int)Math.floor(value);
}
private void subdivide() {
if (!childrenOctants.isEmpty()) {
throw new UnsupportedOperationException("Tried to subdivide when there are already children octants.");
}
int halfXSize = size.getX()/2;
int halfYSize = size.getY()/2;
int halfZSize = size.getZ()/2;
// Lower Left Back Corner
childrenOctants.add(new BoxOctree(new AABB(
boundary.minX, boundary.minY, boundary.minZ,
boundary.minX + halfXSize, boundary.minY + halfYSize, boundary.minZ + halfZSize),
depth));
// Lower Left Front Corner
childrenOctants.add(new BoxOctree(new AABB(
boundary.minX, boundary.minY, boundary.minZ + halfZSize,
boundary.minX + halfXSize, boundary.minY + halfYSize, boundary.maxZ),
depth));
// Lower Right Back Corner
childrenOctants.add(new BoxOctree(new AABB(
boundary.minX + halfXSize, boundary.minY, boundary.minZ,
boundary.maxX, boundary.minY + halfYSize, boundary.minZ + halfZSize),
depth));
// Lower Right Front Corner
childrenOctants.add(new BoxOctree(new AABB(
boundary.minX + halfXSize, boundary.minY, boundary.minZ + halfZSize,
boundary.maxX, boundary.minY + halfYSize, boundary.maxZ),
depth));
// Upper Left Back Corner
childrenOctants.add(new BoxOctree(new AABB(
boundary.minX, boundary.minY + halfYSize, boundary.minZ,
boundary.minX + halfXSize, boundary.maxY, boundary.minZ + halfZSize),
depth));
// Upper Left Front Corner
childrenOctants.add(new BoxOctree(new AABB(
boundary.minX, boundary.minY + halfYSize, boundary.minZ + halfZSize,
boundary.minX + halfXSize, boundary.maxY, boundary.maxZ),
depth));
// Upper Right Back Corner
childrenOctants.add(new BoxOctree(new AABB(
boundary.minX + halfXSize, boundary.minY + halfYSize, boundary.minZ,
boundary.maxX, boundary.maxY, boundary.minZ + halfZSize),
depth));
// Upper Right Front Corner
childrenOctants.add(new BoxOctree(new AABB(
boundary.minX + halfXSize, boundary.minY + halfYSize, boundary.minZ + halfZSize,
boundary.maxX, boundary.maxY, boundary.maxZ),
depth));
for (AABB parentInnerBox : innerBoxes) {
for (BoxOctree octree : childrenOctants) {
if (octree.boundaryIntersects(parentInnerBox)) {
octree.addBox(parentInnerBox);
}
}
}
innerBoxes.clear();
}
public void addBox(AABB axisAlignedBB) {
if (depth < maximumDepth && innerBoxes.size() > subdivideThreshold) {
subdivide();
} if (!childrenOctants.isEmpty()) {
for (BoxOctree octree : childrenOctants) {
if (octree.boundaryIntersects(axisAlignedBB)) {
octree.addBox(axisAlignedBB);
}
}
} else {
// Prevent re-adding the same box if it already exists
for (AABB parentInnerBox : innerBoxes) {
if (parentInnerBox.equals(axisAlignedBB)) {
return;
}
}
innerBoxes.add(axisAlignedBB);
}
}
public boolean boundaryEntirelyContains(@NotNull AABB axisAlignedBB) {
return boundary.contains(axisAlignedBB.minX, axisAlignedBB.minY, axisAlignedBB.minZ) &&
boundary.contains(axisAlignedBB.maxX, axisAlignedBB.maxY, axisAlignedBB.maxZ);
}
public boolean boundaryIntersects(AABB axisAlignedBB) {
return boundary.intersects(axisAlignedBB);
}
public boolean withinBoundsButNotIntersectingChildren(AABB axisAlignedBB) {
return this.boundaryEntirelyContains(axisAlignedBB) && !this.intersectsAnyBox(axisAlignedBB);
}
public boolean intersectsAnyBox(AABB axisAlignedBB) {
if (!childrenOctants.isEmpty()) {
for (BoxOctree octree : childrenOctants) {
if (octree.boundaryIntersects(axisAlignedBB) && octree.intersectsAnyBox(axisAlignedBB)) {
return true;
}
}
}
else {
for (AABB innerBox : innerBoxes) {
if (innerBox.intersects(axisAlignedBB)) {
return true;
}
}
}
return false;
}
public boolean boundaryContains(@NotNull BlockPos position) {
return boundary.contains(position.getX(), position.getY(), position.getZ());
}
public boolean withinAnyBox(BlockPos position) {
if (!childrenOctants.isEmpty()) {
for (BoxOctree octree : childrenOctants) {
if (octree.boundaryContains(position) && octree.withinAnyBox(position)) {
return true;
}
}
}
else {
for (AABB innerBox : innerBoxes) {
if (innerBox.contains(position.getX(), position.getY(), position.getZ())) {
return true;
}
}
}
return false;
}
}

View File

@@ -0,0 +1,90 @@
package org.bxteam.divinemc.util.structure;
import it.unimi.dsi.fastutil.ints.Int2ObjectArrayMap;
import it.unimi.dsi.fastutil.ints.IntArrayList;
import it.unimi.dsi.fastutil.ints.IntComparators;
import net.minecraft.Util;
import net.minecraft.core.FrontAndTop;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.NumericTag;
import net.minecraft.nbt.StringTag;
import net.minecraft.util.RandomSource;
import net.minecraft.world.level.block.JigsawBlock;
import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplate;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.List;
public final class GeneralUtils {
private GeneralUtils() {}
// More optimized with checking if the jigsaw blocks can connect
public static boolean canJigsawsAttach(StructureTemplate.@NotNull JigsawBlockInfo jigsaw1, StructureTemplate.@NotNull JigsawBlockInfo jigsaw2) {
FrontAndTop prop1 = jigsaw1.info().state().getValue(JigsawBlock.ORIENTATION);
FrontAndTop prop2 = jigsaw2.info().state().getValue(JigsawBlock.ORIENTATION);
return prop1.front() == prop2.front().getOpposite() &&
(prop1.top() == prop2.top() || isRollableJoint(jigsaw1, prop1)) &&
getStringMicroOptimised(jigsaw1.info().nbt(), "target").equals(getStringMicroOptimised(jigsaw2.info().nbt(), "name"));
}
private static boolean isRollableJoint(StructureTemplate.@NotNull JigsawBlockInfo jigsaw1, FrontAndTop prop1) {
String joint = getStringMicroOptimised(jigsaw1.info().nbt(), "joint");
if(!joint.equals("rollable") && !joint.equals("aligned")) {
return !prop1.front().getAxis().isHorizontal();
}
else {
return joint.equals("rollable");
}
}
public static void shuffleAndPrioritize(@NotNull List<StructureTemplate.JigsawBlockInfo> list, RandomSource random) {
Int2ObjectArrayMap<List<StructureTemplate.JigsawBlockInfo>> buckets = new Int2ObjectArrayMap<>();
// Add entries to the bucket
for (StructureTemplate.JigsawBlockInfo structureBlockInfo : list) {
int key = 0;
if (structureBlockInfo.info().nbt() != null) {
key = getIntMicroOptimised(structureBlockInfo.info().nbt(), "selection_priority");
}
buckets.computeIfAbsent(key, k -> new ArrayList<>()).add(structureBlockInfo);
}
// Shuffle the entries in the bucket
for (List<StructureTemplate.JigsawBlockInfo> bucketList : buckets.values()) {
Util.shuffle(bucketList, random);
}
if (buckets.size() == 1) {
list.clear();
copyAll(buckets.int2ObjectEntrySet().fastIterator().next().getValue(), list);
}
else if (buckets.size() > 1) {
// Priorities found. Concat them into a single new master list in reverse order to match vanilla behavior
list.clear();
IntArrayList keys = new IntArrayList(buckets.keySet());
keys.sort(IntComparators.OPPOSITE_COMPARATOR);
for (int i = 0; i < keys.size(); i++) {
copyAll(buckets.get(keys.getInt(i)), list);
}
}
}
public static int getIntMicroOptimised(@NotNull CompoundTag tag, String key) {
return tag.get(key) instanceof NumericTag numericTag ? numericTag.getAsInt() : 0;
}
public static @NotNull String getStringMicroOptimised(@NotNull CompoundTag tag, String key) {
return tag.get(key) instanceof StringTag stringTag ? stringTag.getAsString() : "";
}
public static <T> void copyAll(@NotNull List<T> src, List<T> dest) {
// Do not listen to IDE. This is faster than addAll
for (int i = 0; i < src.size(); i++) {
dest.add(src.get(i));
}
}
}

View File

@@ -0,0 +1,274 @@
package org.bxteam.divinemc.util.structure;
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplate;
import org.jetbrains.annotations.NotNull;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import java.util.function.Predicate;
public class PalettedStructureBlockInfoList implements List<StructureTemplate.StructureBlockInfo> {
private static final long[] EMPTY_DATA = new long[0];
private static final CompoundTag[] NULL_TAGS = new CompoundTag[]{null};
private static final String UNSUPPORTED_OPERATION_ERROR_MESSAGE = "Structure Layout Optimizer: No mod should be modifying a StructureTemplate's Palette itself. Please reach out to Structure Layout Optimizer dev for this crash to investigate this mod compat issue.";
protected final long[] data;
protected final BlockState[] states;
protected final CompoundTag[] nbts;
protected final int xBits, yBits, zBits;
protected final int stateBits, nbtBits;
protected final int bitsPerEntry;
protected final int size;
private WeakReference<List<StructureTemplate.StructureBlockInfo>> cachedStructureBlockInfoList = new WeakReference<>(null);
public PalettedStructureBlockInfoList(List<StructureTemplate.StructureBlockInfo> infos) {
this(infos, null);
}
public PalettedStructureBlockInfoList(List<StructureTemplate.StructureBlockInfo> infos, Predicate<StructureTemplate.StructureBlockInfo> predicate) {
List<Entry> entries = new ArrayList<>();
List<BlockState> states = new ArrayList<>();
List<CompoundTag> tags = new ArrayList<>();
int maxX = 0;
int maxY = 0;
int maxZ = 0;
for (StructureTemplate.StructureBlockInfo info : infos) {
if (predicate != null && !predicate.test(info)) {
continue;
}
int state = states.indexOf(info.state());
if (state == -1) {
state = states.size();
states.add(info.state());
}
int tag = indexOf(tags, info.nbt());
if (tag == -1) {
tag = tags.size();
tags.add(info.nbt());
}
int x = info.pos().getX();
int y = info.pos().getY();
int z = info.pos().getZ();
if (x < 0 || y < 0 || z < 0) {
throw new RuntimeException("StructureLayoutOptimizer: Invalid StructureBlockInfo position: " + info.pos());
}
if (x > maxX) {
maxX = x;
}
if (y > maxY) {
maxY = y;
}
if (z > maxZ) {
maxZ = z;
}
entries.add(new Entry(x, y, z, state, tag));
}
this.xBits = bits(maxX);
this.yBits = bits(maxY);
this.zBits = bits(maxZ);
this.stateBits = bits(states.size() - 1);
this.nbtBits = bits(tags.size() - 1);
this.bitsPerEntry = this.xBits + this.yBits + this.zBits + this.stateBits + this.nbtBits;
if (this.bitsPerEntry > 64) {
throw new RuntimeException("StructureLayoutOptimizer: Too many bits per entry: " + this.bitsPerEntry);
}
this.size = entries.size();
if (this.bitsPerEntry != 0) {
int entriesPerLong = 64 / this.bitsPerEntry;
this.data = new long[(this.size + entriesPerLong - 1) / entriesPerLong];
for (int i = 0; i < this.size; i++) {
this.data[i / entriesPerLong] |= entries.get(i).compress(this.xBits, this.yBits, this.zBits, this.stateBits) << ((i % entriesPerLong) * this.bitsPerEntry);
}
} else {
this.data = EMPTY_DATA;
}
this.states = states.toArray(new BlockState[0]);
this.nbts = tags.size() == 1 && tags.get(0) == null ? NULL_TAGS : tags.toArray(new CompoundTag[0]);
}
private static int bits(int i) {
int bits = 0;
while (i >= 1 << bits) {
bits++;
}
return bits;
}
private static <T> int indexOf(@NotNull List<T> list, T o) {
for (int i = 0; i < list.size(); i++) {
if (list.get(i) == o) {
return i;
}
}
return -1;
}
private @NotNull List<StructureTemplate.StructureBlockInfo> convertBackToStructureBlockInfoListAndCache() {
synchronized(data) {
List<StructureTemplate.StructureBlockInfo> structureBlockInfos = cachedStructureBlockInfoList.get();
if (structureBlockInfos != null) {
return structureBlockInfos;
}
structureBlockInfos = new ObjectArrayList<>(new PalettedStructureBlockInfoListIterator(this));
cachedStructureBlockInfoList = new WeakReference<>(structureBlockInfos);
return structureBlockInfos;
}
}
@Override
public int size() {
return this.size;
}
@Override
public boolean isEmpty() {
return this.size == 0;
}
@NotNull
@Override
public Iterator<StructureTemplate.StructureBlockInfo> iterator() {
return convertBackToStructureBlockInfoListAndCache().iterator();
}
@NotNull
@Override
public ListIterator<StructureTemplate.StructureBlockInfo> listIterator() {
return convertBackToStructureBlockInfoListAndCache().listIterator();
}
@NotNull
@Override
public ListIterator<StructureTemplate.StructureBlockInfo> listIterator(int index) {
return convertBackToStructureBlockInfoListAndCache().listIterator(index);
}
@Override
public boolean contains(Object o) {
return convertBackToStructureBlockInfoListAndCache().contains(o);
}
@Override
public boolean containsAll(@NotNull Collection<?> c) {
return new HashSet<>(convertBackToStructureBlockInfoListAndCache()).containsAll(c);
}
@NotNull
@Override
public Object @NotNull [] toArray() {
return convertBackToStructureBlockInfoListAndCache().toArray();
}
@NotNull
@Override
public <T> T @NotNull [] toArray(@NotNull T @NotNull [] a) {
return convertBackToStructureBlockInfoListAndCache().toArray(a);
}
@Override
public StructureTemplate.StructureBlockInfo get(int index) {
return convertBackToStructureBlockInfoListAndCache().get(index);
}
@Override
public int indexOf(Object o) {
return convertBackToStructureBlockInfoListAndCache().indexOf(o);
}
@Override
public int lastIndexOf(Object o) {
return convertBackToStructureBlockInfoListAndCache().lastIndexOf(o);
}
@NotNull
@Override
public List<StructureTemplate.StructureBlockInfo> subList(int fromIndex, int toIndex) {
return convertBackToStructureBlockInfoListAndCache().subList(fromIndex, toIndex);
}
@Override
public StructureTemplate.StructureBlockInfo set(int index, StructureTemplate.StructureBlockInfo element) {
throw new UnsupportedOperationException(UNSUPPORTED_OPERATION_ERROR_MESSAGE);
}
@Override
public void add(int index, StructureTemplate.StructureBlockInfo element) {
throw new UnsupportedOperationException(UNSUPPORTED_OPERATION_ERROR_MESSAGE);
}
@Override
public boolean add(StructureTemplate.StructureBlockInfo info) {
throw new UnsupportedOperationException(UNSUPPORTED_OPERATION_ERROR_MESSAGE);
}
@Override
public boolean remove(Object o) {
throw new UnsupportedOperationException(UNSUPPORTED_OPERATION_ERROR_MESSAGE);
}
@Override
public StructureTemplate.StructureBlockInfo remove(int index) {
throw new UnsupportedOperationException(UNSUPPORTED_OPERATION_ERROR_MESSAGE);
}
@Override
public boolean addAll(@NotNull Collection<? extends StructureTemplate.StructureBlockInfo> c) {
throw new UnsupportedOperationException(UNSUPPORTED_OPERATION_ERROR_MESSAGE);
}
@Override
public boolean addAll(int index, @NotNull Collection<? extends StructureTemplate.StructureBlockInfo> c) {
throw new UnsupportedOperationException(UNSUPPORTED_OPERATION_ERROR_MESSAGE);
}
@Override
public boolean removeAll(@NotNull Collection<?> c) {
throw new UnsupportedOperationException(UNSUPPORTED_OPERATION_ERROR_MESSAGE);
}
@Override
public boolean retainAll(@NotNull Collection<?> c) {
throw new UnsupportedOperationException(UNSUPPORTED_OPERATION_ERROR_MESSAGE);
}
@Override
public void clear() {
throw new UnsupportedOperationException(UNSUPPORTED_OPERATION_ERROR_MESSAGE);
}
private static class Entry {
private final int x, y, z;
private final int state, nbt;
private Entry(int x, int y, int z, int state, int nbt) {
this.x = x;
this.y = y;
this.z = z;
this.state = state;
this.nbt = nbt;
}
private long compress(int xBits, int yBits, int zBits, int stateBits) {
return this.x + ((this.y + ((this.z + ((this.state + ((long) this.nbt << stateBits)) << zBits)) << yBits)) << xBits);
}
}
}

View File

@@ -0,0 +1,65 @@
package org.bxteam.divinemc.util.structure;
import net.minecraft.core.BlockPos;
import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplate;
import org.jetbrains.annotations.NotNull;
import java.util.Iterator;
public class PalettedStructureBlockInfoListIterator implements Iterator<StructureTemplate.StructureBlockInfo> {
private final PalettedStructureBlockInfoList infos;
private final int xOffset, yOffset, zOffset, stateOffset;
private final int xMask, yMask, zMask, stateMask, tagMask;
private int index = 0;
public PalettedStructureBlockInfoListIterator(@NotNull PalettedStructureBlockInfoList infos) {
this.infos = infos;
this.xOffset = infos.xBits;
this.yOffset = this.xOffset + infos.yBits;
this.zOffset = this.yOffset + infos.zBits;
this.stateOffset = this.zOffset + infos.stateBits;
this.xMask = (1 << infos.xBits) - 1;
this.yMask = (1 << infos.yBits) - 1;
this.zMask = (1 << infos.zBits) - 1;
this.stateMask = (1 << infos.stateBits) - 1;
this.tagMask = (1 << infos.nbtBits) - 1;
}
@Override
public boolean hasNext() {
return this.index < this.size();
}
@Override
public StructureTemplate.StructureBlockInfo next() {
int index = this.toIndex(this.index++);
if (index >= this.infos.size) {
throw new IndexOutOfBoundsException();
}
int bitsPerEntry = this.infos.bitsPerEntry;
if (bitsPerEntry == 0) {
return new StructureTemplate.StructureBlockInfo(new BlockPos(0, 0, 0), this.infos.states[0], this.infos.nbts[0]);
}
int entriesPerLong = 64 / bitsPerEntry;
long entry = this.infos.data[index / entriesPerLong] >>> ((index % entriesPerLong) * bitsPerEntry);
int x = (int) (entry & this.xMask);
int y = (int) (entry >> this.xOffset & this.yMask);
int z = (int) (entry >> this.yOffset & this.zMask);
int state = (int) (entry >> this.zOffset & this.stateMask);
int tag = (int) (entry >> this.stateOffset & this.tagMask);
return new StructureTemplate.StructureBlockInfo(new BlockPos(x, y, z), this.infos.states[state], this.infos.nbts[tag]);
}
protected int toIndex(int index) {
return index;
}
protected int size() {
return this.infos.size;
}
}

View File

@@ -0,0 +1,105 @@
package org.bxteam.divinemc.util.structure;
import it.unimi.dsi.fastutil.objects.Object2BooleanMaps;
import it.unimi.dsi.fastutil.objects.Object2BooleanOpenHashMap;
import net.minecraft.core.BlockPos;
import net.minecraft.world.level.ServerLevelAccessor;
import net.minecraft.world.level.block.Mirror;
import net.minecraft.world.level.block.Rotation;
import net.minecraft.world.level.levelgen.structure.BoundingBox;
import net.minecraft.world.level.levelgen.structure.templatesystem.StructurePlaceSettings;
import net.minecraft.world.level.levelgen.structure.templatesystem.StructureProcessor;
import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplate;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
public class StructureTemplateOptimizer {
private static final Map<StructureProcessor, Boolean> FINALIZE_PROCESSING_PROCESSORS = Object2BooleanMaps.synchronize(new Object2BooleanOpenHashMap<>());
public static @NotNull List<StructureTemplate.StructureBlockInfo> getStructureBlockInfosInBounds(StructureTemplate.@NotNull Palette palette, BlockPos offset, @NotNull StructurePlaceSettings structurePlaceSettings) {
BoundingBox boundingBox = structurePlaceSettings.getBoundingBox();
List<StructureTemplate.StructureBlockInfo> originalPositions = palette.blocks();
if (boundingBox == null) {
return originalPositions;
}
// Capped processor needs full nbt block lists
for (StructureProcessor processor : structurePlaceSettings.getProcessors()) {
if (FINALIZE_PROCESSING_PROCESSORS.computeIfAbsent(processor, StructureTemplateOptimizer::isFinalizeProcessor)) {
return palette.blocks();
}
}
Mirror mirror = structurePlaceSettings.getMirror();
Rotation rotation = structurePlaceSettings.getRotation();
BlockPos pivot = structurePlaceSettings.getRotationPivot();
List<StructureTemplate.StructureBlockInfo> listOfInBoundsRelativePositions = new ArrayList<>();
BlockPos.MutableBlockPos mutableBlockPos = new BlockPos.MutableBlockPos();
for (StructureTemplate.StructureBlockInfo blockInfo : originalPositions) {
mutableBlockPos.set(blockInfo.pos());
transform(mutableBlockPos, mirror, rotation, pivot);
mutableBlockPos.move(offset);
if (boundingBox.isInside(mutableBlockPos)) {
listOfInBoundsRelativePositions.add(blockInfo);
}
}
// DO NOT REMOVE. This is required because the Template will return false for an entirely empty list and then remove the structure piece
// out of the structure start, preventing it from placing blocks into any other side chunks that the piece was supposed to place blocks in.
if (listOfInBoundsRelativePositions.isEmpty() && !originalPositions.isEmpty()) {
listOfInBoundsRelativePositions.add(originalPositions.get(0));
}
return listOfInBoundsRelativePositions;
}
private static @NotNull Boolean isFinalizeProcessor(@NotNull StructureProcessor structureProcessor) {
try {
var method = structureProcessor.getClass().getMethod(
"finalizeProcessing", ServerLevelAccessor.class, BlockPos.class, BlockPos.class, List.class, List.class, StructurePlaceSettings.class);
return method.getDeclaringClass() != StructureProcessor.class;
}
catch (NoSuchMethodException e) {
throw new RuntimeException("StructureProcessor does not have finalizeProcessing method", e);
}
}
private static void transform(BlockPos.@NotNull MutableBlockPos mutableBlockPos, @NotNull Mirror mirror, Rotation rotation, BlockPos pivot) {
int i = mutableBlockPos.getX();
int j = mutableBlockPos.getY();
int k = mutableBlockPos.getZ();
boolean flag = true;
switch (mirror) {
case LEFT_RIGHT:
k = -k;
break;
case FRONT_BACK:
i = -i;
break;
default:
flag = false;
}
int l = pivot.getX();
int i1 = pivot.getZ();
switch (rotation) {
case COUNTERCLOCKWISE_90:
mutableBlockPos.set(l - i1 + k, j, l + i1 - i);
return;
case CLOCKWISE_90:
mutableBlockPos.set(l + i1 - k, j, i1 - l + i);
return;
case CLOCKWISE_180:
mutableBlockPos.set(l + l - i, j, i1 + i1 - k);
return;
default:
if (flag) mutableBlockPos.set(i, j, k);
}
}
}

View File

@@ -0,0 +1,11 @@
package org.bxteam.divinemc.util.structure;
import net.minecraft.world.level.levelgen.structure.pools.StructurePoolElement;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Set;
public class TrojanArrayList<E extends @Nullable Object> extends ArrayList<E> {
public final Set<StructurePoolElement> elementsAlreadyParsed = new HashSet<>();
}

View File

@@ -0,0 +1,21 @@
package org.bxteam.divinemc.util.structure;
import it.unimi.dsi.fastutil.doubles.DoubleList;
import net.minecraft.core.Direction;
import net.minecraft.world.phys.shapes.BitSetDiscreteVoxelShape;
import net.minecraft.world.phys.shapes.VoxelShape;
import org.jetbrains.annotations.NotNull;
public class TrojanVoxelShape extends VoxelShape {
public final BoxOctree boxOctree;
public TrojanVoxelShape(BoxOctree boxOctree) {
super(BitSetDiscreteVoxelShape.withFilledBounds(0 ,0, 0, 0, 0, 0, 0, 0, 0));
this.boxOctree = boxOctree;
}
@Override
public DoubleList getCoords(Direction.@NotNull Axis axis) {
return null;
}
}

View File

@@ -0,0 +1,5 @@
/**
* This package was ported from the Structure Layout Optimizer mod by TelepathicGrunt. <br>
* Original source code - https://github.com/TelepathicGrunt/StructureLayoutOptimizer (MIT License)
*/
package org.bxteam.divinemc.util.structure;

View File

@@ -0,0 +1,83 @@
package org.bxteam.divinemc.util.tps;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
public class TPSCalculator {
public Long lastTick;
public Long currentTick;
private double allMissedTicks = 0;
private final List<Double> tpsHistory = new CopyOnWriteArrayList<>();
private static final int historyLimit = 40;
public static final int MAX_TPS = 20;
public static final int FULL_TICK = 50;
public TPSCalculator() {}
public void doTick() {
if (currentTick != null) {
lastTick = currentTick;
}
currentTick = System.currentTimeMillis();
addToHistory(getTPS());
clearMissedTicks();
missedTick();
}
private void addToHistory(double tps) {
if (tpsHistory.size() >= historyLimit) {
tpsHistory.remove(0);
}
tpsHistory.add(tps);
}
public long getMSPT() {
return currentTick - lastTick;
}
public double getAverageTPS() {
return tpsHistory.stream()
.mapToDouble(Double::doubleValue)
.average()
.orElse(0.1);
}
public double getTPS() {
if (lastTick == null) return -1;
if (getMSPT() <= 0) return 0.1;
double tps = 1000 / (double) getMSPT();
return tps > MAX_TPS ? MAX_TPS : tps;
}
public void missedTick() {
if (lastTick == null) return;
long mspt = getMSPT() <= 0 ? 50 : getMSPT();
double missedTicks = (mspt / (double) FULL_TICK) - 1;
allMissedTicks += missedTicks <= 0 ? 0 : missedTicks;
}
public double getMostAccurateTPS() {
return getTPS() > getAverageTPS() ? getAverageTPS() : getTPS();
}
public double getAllMissedTicks() {
return allMissedTicks;
}
public int applicableMissedTicks() {
return (int) Math.floor(allMissedTicks);
}
public void clearMissedTicks() {
allMissedTicks -= applicableMissedTicks();
}
public void resetMissedTicks() {
allMissedTicks = 0;
}
}

View File

@@ -0,0 +1,35 @@
package org.bxteam.divinemc.util.tps;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.level.ServerLevel;
import org.jetbrains.annotations.Nullable;
public class TPSUtil {
public static final int MAX_TPS = 20;
public static final int FULL_TICK = 50;
public static float tt20(float ticks, boolean limitZero, @Nullable ServerLevel level) {
float newTicks = (float) rawTT20(ticks, level);
if (limitZero) return newTicks > 0 ? newTicks : 1;
else return newTicks;
}
public static int tt20(int ticks, boolean limitZero, @Nullable ServerLevel level) {
int newTicks = (int) Math.ceil(rawTT20(ticks, level));
if (limitZero) return newTicks > 0 ? newTicks : 1;
else return newTicks;
}
public static double tt20(double ticks, boolean limitZero, @Nullable ServerLevel level) {
double newTicks = (double) rawTT20(ticks, level);
if (limitZero) return newTicks > 0 ? newTicks : 1;
else return newTicks;
}
public static double rawTT20(double ticks, @Nullable ServerLevel level) {
return ticks == 0 ? 0 : ticks * (level == null ? MinecraftServer.getServer().tpsCalculator.getMostAccurateTPS() : level.tpsCalculator.getMostAccurateTPS()) / MAX_TPS;
}
}

View File

@@ -0,0 +1,95 @@
package su.plo.matter;
import com.google.common.collect.Iterables;
import net.minecraft.server.level.ServerLevel;
import org.bxteam.divinemc.DivineConfig;
import java.math.BigInteger;
import java.security.SecureRandom;
import java.util.Optional;
public class Globals {
public static final int WORLD_SEED_LONGS = 16;
public static final int WORLD_SEED_BITS = WORLD_SEED_LONGS * 64;
public static final long[] worldSeed = new long[WORLD_SEED_LONGS];
public static final ThreadLocal<Integer> dimension = ThreadLocal.withInitial(() -> 0);
public enum Salt {
UNDEFINED,
BASTION_FEATURE,
WOODLAND_MANSION_FEATURE,
MINESHAFT_FEATURE,
BURIED_TREASURE_FEATURE,
NETHER_FORTRESS_FEATURE,
PILLAGER_OUTPOST_FEATURE,
GEODE_FEATURE,
NETHER_FOSSIL_FEATURE,
OCEAN_MONUMENT_FEATURE,
RUINED_PORTAL_FEATURE,
POTENTIONAL_FEATURE,
GENERATE_FEATURE,
JIGSAW_PLACEMENT,
STRONGHOLDS,
POPULATION,
DECORATION,
SLIME_CHUNK
}
public static void setupGlobals(ServerLevel world) {
if (!DivineConfig.enableSecureSeed) return;
long[] seed = world.getServer().getWorldData().worldGenOptions().featureSeed();
System.arraycopy(seed, 0, worldSeed, 0, WORLD_SEED_LONGS);
int worldIndex = Iterables.indexOf(world.getServer().levelKeys(), it -> it == world.dimension());
if (worldIndex == -1)
worldIndex = world.getServer().levelKeys().size(); // if we are in world construction it may not have been added to the map yet
dimension.set(worldIndex);
}
public static long[] createRandomWorldSeed() {
long[] seed = new long[WORLD_SEED_LONGS];
SecureRandom rand = new SecureRandom();
for (int i = 0; i < WORLD_SEED_LONGS; i++) {
seed[i] = rand.nextLong();
}
return seed;
}
// 1024-bit string -> 16 * 64 long[]
public static Optional<long[]> parseSeed(String seedStr) {
if (seedStr.isEmpty()) return Optional.empty();
if (seedStr.length() != WORLD_SEED_BITS) {
throw new IllegalArgumentException("Secure seed length must be " + WORLD_SEED_BITS + "-bit but found " + seedStr.length() + "-bit.");
}
long[] seed = new long[WORLD_SEED_LONGS];
for (int i = 0; i < WORLD_SEED_LONGS; i++) {
int start = i * 64;
int end = start + 64;
String seedSection = seedStr.substring(start, end);
BigInteger seedInDecimal = new BigInteger(seedSection, 2);
seed[i] = seedInDecimal.longValue();
}
return Optional.of(seed);
}
// 16 * 64 long[] -> 1024-bit string
public static String seedToString(long[] seed) {
StringBuilder sb = new StringBuilder();
for (long longV : seed) {
// Convert to 64-bit binary string per long
// Use format to keep 64-bit length, and use 0 to complete space
String binaryStr = String.format("%64s", Long.toBinaryString(longV)).replace(' ', '0');
sb.append(binaryStr);
}
return sb.toString();
}
}

View File

@@ -0,0 +1,73 @@
package su.plo.matter;
public class Hashing {
// https://en.wikipedia.org/wiki/BLAKE_(hash_function)
// https://github.com/bcgit/bc-java/blob/master/core/src/main/java/org/bouncycastle/crypto/digests/Blake2bDigest.java
private final static long[] blake2b_IV = {
0x6a09e667f3bcc908L, 0xbb67ae8584caa73bL, 0x3c6ef372fe94f82bL,
0xa54ff53a5f1d36f1L, 0x510e527fade682d1L, 0x9b05688c2b3e6c1fL,
0x1f83d9abfb41bd6bL, 0x5be0cd19137e2179L
};
private final static byte[][] blake2b_sigma = {
{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15},
{14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3},
{11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4},
{7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8},
{9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13},
{2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9},
{12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11},
{13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10},
{6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5},
{10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0},
{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15},
{14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3}
};
public static long[] hashWorldSeed(long[] worldSeed) {
long[] result = blake2b_IV.clone();
result[0] ^= 0x01010040;
hash(worldSeed, result, new long[16], 0, false);
return result;
}
public static void hash(long[] message, long[] chainValue, long[] internalState, long messageOffset, boolean isFinal) {
assert message.length == 16;
assert chainValue.length == 8;
assert internalState.length == 16;
System.arraycopy(chainValue, 0, internalState, 0, chainValue.length);
System.arraycopy(blake2b_IV, 0, internalState, chainValue.length, 4);
internalState[12] = messageOffset ^ blake2b_IV[4];
internalState[13] = blake2b_IV[5];
if (isFinal) internalState[14] = ~blake2b_IV[6];
internalState[15] = blake2b_IV[7];
for (int round = 0; round < 12; round++) {
G(message[blake2b_sigma[round][0]], message[blake2b_sigma[round][1]], 0, 4, 8, 12, internalState);
G(message[blake2b_sigma[round][2]], message[blake2b_sigma[round][3]], 1, 5, 9, 13, internalState);
G(message[blake2b_sigma[round][4]], message[blake2b_sigma[round][5]], 2, 6, 10, 14, internalState);
G(message[blake2b_sigma[round][6]], message[blake2b_sigma[round][7]], 3, 7, 11, 15, internalState);
G(message[blake2b_sigma[round][8]], message[blake2b_sigma[round][9]], 0, 5, 10, 15, internalState);
G(message[blake2b_sigma[round][10]], message[blake2b_sigma[round][11]], 1, 6, 11, 12, internalState);
G(message[blake2b_sigma[round][12]], message[blake2b_sigma[round][13]], 2, 7, 8, 13, internalState);
G(message[blake2b_sigma[round][14]], message[blake2b_sigma[round][15]], 3, 4, 9, 14, internalState);
}
for (int i = 0; i < 8; i++) {
chainValue[i] ^= internalState[i] ^ internalState[i + 8];
}
}
private static void G(long m1, long m2, int posA, int posB, int posC, int posD, long[] internalState) {
internalState[posA] = internalState[posA] + internalState[posB] + m1;
internalState[posD] = Long.rotateRight(internalState[posD] ^ internalState[posA], 32);
internalState[posC] = internalState[posC] + internalState[posD];
internalState[posB] = Long.rotateRight(internalState[posB] ^ internalState[posC], 24); // replaces 25 of BLAKE
internalState[posA] = internalState[posA] + internalState[posB] + m2;
internalState[posD] = Long.rotateRight(internalState[posD] ^ internalState[posA], 16);
internalState[posC] = internalState[posC] + internalState[posD];
internalState[posB] = Long.rotateRight(internalState[posB] ^ internalState[posC], 63); // replaces 11 of BLAKE
}
}

View File

@@ -0,0 +1,159 @@
package su.plo.matter;
import net.minecraft.util.Mth;
import net.minecraft.util.RandomSource;
import net.minecraft.world.level.levelgen.LegacyRandomSource;
import net.minecraft.world.level.levelgen.WorldgenRandom;
import org.jetbrains.annotations.NotNull;
import java.util.Arrays;
public class WorldgenCryptoRandom extends WorldgenRandom {
// hash the world seed to guard against badly chosen world seeds
private static final long[] HASHED_ZERO_SEED = Hashing.hashWorldSeed(new long[Globals.WORLD_SEED_LONGS]);
private static final ThreadLocal<long[]> LAST_SEEN_WORLD_SEED = ThreadLocal.withInitial(() -> new long[Globals.WORLD_SEED_LONGS]);
private static final ThreadLocal<long[]> HASHED_WORLD_SEED = ThreadLocal.withInitial(() -> HASHED_ZERO_SEED);
private final long[] worldSeed = new long[Globals.WORLD_SEED_LONGS];
private final long[] randomBits = new long[8];
private int randomBitIndex;
private static final int MAX_RANDOM_BIT_INDEX = 64 * 8;
private static final int LOG2_MAX_RANDOM_BIT_INDEX = 9;
private long counter;
private final long[] message = new long[16];
private final long[] cachedInternalState = new long[16];
public WorldgenCryptoRandom(int x, int z, Globals.Salt typeSalt, long salt) {
super(new LegacyRandomSource(0L));
if (typeSalt != null) {
this.setSecureSeed(x, z, typeSalt, salt);
}
}
public void setSecureSeed(int x, int z, Globals.Salt typeSalt, long salt) {
System.arraycopy(Globals.worldSeed, 0, this.worldSeed, 0, Globals.WORLD_SEED_LONGS);
message[0] = ((long) x << 32) | ((long) z & 0xffffffffL);
message[1] = ((long) Globals.dimension.get() << 32) | ((long) salt & 0xffffffffL);
message[2] = typeSalt.ordinal();
message[3] = counter = 0;
randomBitIndex = MAX_RANDOM_BIT_INDEX;
}
private long[] getHashedWorldSeed() {
if (!Arrays.equals(worldSeed, LAST_SEEN_WORLD_SEED.get())) {
HASHED_WORLD_SEED.set(Hashing.hashWorldSeed(worldSeed));
System.arraycopy(worldSeed, 0, LAST_SEEN_WORLD_SEED.get(), 0, Globals.WORLD_SEED_LONGS);
}
return HASHED_WORLD_SEED.get();
}
private void moreRandomBits() {
message[3] = counter++;
System.arraycopy(getHashedWorldSeed(), 0, randomBits, 0, 8);
Hashing.hash(message, randomBits, cachedInternalState, 64, true);
}
private long getBits(int count) {
if (randomBitIndex >= MAX_RANDOM_BIT_INDEX) {
moreRandomBits();
randomBitIndex -= MAX_RANDOM_BIT_INDEX;
}
int alignment = randomBitIndex & 63;
if ((randomBitIndex >>> 6) == ((randomBitIndex + count) >>> 6)) {
long result = (randomBits[randomBitIndex >>> 6] >>> alignment) & ((1L << count) - 1);
randomBitIndex += count;
return result;
} else {
long result = (randomBits[randomBitIndex >>> 6] >>> alignment) & ((1L << (64 - alignment)) - 1);
randomBitIndex += count;
if (randomBitIndex >= MAX_RANDOM_BIT_INDEX) {
moreRandomBits();
randomBitIndex -= MAX_RANDOM_BIT_INDEX;
}
alignment = randomBitIndex & 63;
result <<= alignment;
result |= (randomBits[randomBitIndex >>> 6] >>> (64 - alignment)) & ((1L << alignment) - 1);
return result;
}
}
@Override
public @NotNull RandomSource fork() {
WorldgenCryptoRandom fork = new WorldgenCryptoRandom(0, 0, null, 0);
System.arraycopy(Globals.worldSeed, 0, fork.worldSeed, 0, Globals.WORLD_SEED_LONGS);
fork.message[0] = this.message[0];
fork.message[1] = this.message[1];
fork.message[2] = this.message[2];
fork.message[3] = this.message[3];
fork.randomBitIndex = this.randomBitIndex;
fork.counter = this.counter;
fork.nextLong();
return fork;
}
@Override
public int next(int bits) {
return (int) getBits(bits);
}
@Override
public void consumeCount(int count) {
randomBitIndex += count;
if (randomBitIndex >= MAX_RANDOM_BIT_INDEX * 2) {
randomBitIndex -= MAX_RANDOM_BIT_INDEX;
counter += randomBitIndex >>> LOG2_MAX_RANDOM_BIT_INDEX;
randomBitIndex &= MAX_RANDOM_BIT_INDEX - 1;
randomBitIndex += MAX_RANDOM_BIT_INDEX;
}
}
@Override
public int nextInt(int bound) {
int bits = Mth.ceillog2(bound);
int result;
do {
result = (int) getBits(bits);
} while (result >= bound);
return result;
}
@Override
public long nextLong() {
return getBits(64);
}
@Override
public double nextDouble() {
return getBits(53) * 0x1.0p-53;
}
@Override
public long setDecorationSeed(long worldSeed, int blockX, int blockZ) {
setSecureSeed(blockX, blockZ, Globals.Salt.POPULATION, 0);
return ((long) blockX << 32) | ((long) blockZ & 0xffffffffL);
}
@Override
public void setFeatureSeed(long populationSeed, int index, int step) {
setSecureSeed((int) (populationSeed >> 32), (int) populationSeed, Globals.Salt.DECORATION, index + 10000L * step);
}
@Override
public void setLargeFeatureSeed(long worldSeed, int chunkX, int chunkZ) {
super.setLargeFeatureSeed(worldSeed, chunkX, chunkZ);
}
@Override
public void setLargeFeatureWithSalt(long worldSeed, int regionX, int regionZ, int salt) {
super.setLargeFeatureWithSalt(worldSeed, regionX, regionZ, salt);
}
public static RandomSource seedSlimeChunk(int chunkX, int chunkZ) {
return new WorldgenCryptoRandom(chunkX, chunkZ, Globals.Salt.SLIME_CHUNK, 0);
}
}