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:
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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() {}
|
||||
|
||||
|
||||
@@ -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++;
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
package org.bxteam.divinemc.dfc.common.ast;
|
||||
|
||||
public interface AstTransformer {
|
||||
AstNode transform(AstNode var1);
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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]);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
package org.bxteam.divinemc.dfc.common.ducks;
|
||||
|
||||
public interface IBlendingAwareVisitor {
|
||||
boolean c2me$isBlendingEnabled();
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
package org.bxteam.divinemc.dfc.common.ducks;
|
||||
|
||||
public interface ICoordinatesFilling {
|
||||
void c2me$fillCoordinates(int[] var1, int[] var2, int[] var3);
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package org.bxteam.divinemc.dfc.common.ducks;
|
||||
|
||||
public interface IEqualityOverriding {
|
||||
void c2me$overrideEquality(Object var1);
|
||||
|
||||
Object c2me$getOverriddenEquality();
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package org.bxteam.divinemc.entity.pathfinding;
|
||||
|
||||
public enum PathProcessState {
|
||||
WAITING,
|
||||
PROCESSING,
|
||||
COMPLETED
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
);
|
||||
}
|
||||
@@ -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;
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -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";
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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) + ")";
|
||||
}
|
||||
}
|
||||
@@ -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 > 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,
|
||||
* () -> "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 > 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 > 0, () -> "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, () -> "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(),
|
||||
* () -> "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, () -> "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, () -> "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, () -> "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, () -> "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);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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<>();
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
95
divinemc-server/src/main/java/su/plo/matter/Globals.java
Normal file
95
divinemc-server/src/main/java/su/plo/matter/Globals.java
Normal 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();
|
||||
}
|
||||
}
|
||||
73
divinemc-server/src/main/java/su/plo/matter/Hashing.java
Normal file
73
divinemc-server/src/main/java/su/plo/matter/Hashing.java
Normal 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
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user