9
0
mirror of https://github.com/BX-Team/DivineMC.git synced 2026-01-03 22:16:22 +00:00

implement new patches

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

View File

@@ -0,0 +1,32 @@
cmake_minimum_required(VERSION 3.25)
set(CMAKE_TRY_COMPILE_TARGET_TYPE "STATIC_LIBRARY")
project(c2me-opts-natives-math C)
set(CMAKE_C_STANDARD 11)
set(CMAKE_C_COMPILER clang)
SET(CMAKE_C_FLAGS_RELWITHDEBINFO "-O3 -g")
set(CMAKE_C_STANDARD_LIBRARIES "")
set(CMAKE_CXX_STANDARD_LIBRARIES "")
add_library(c2me-opts-natives-math SHARED
exports.c
system_isa_x86_64.c
exports_x86_64_nogather.c
system_isa_aarch64.c
flibc.c
)
if(CMAKE_SYSTEM_PROCESSOR MATCHES "(x86)|(X86)|(amd64)|(AMD64)")
set_source_files_properties(exports_x86_64_nogather.c PROPERTIES COMPILE_FLAGS "-mno-gather -mno-scatter")
endif()
execute_process(COMMAND llvm-config --prefix OUTPUT_VARIABLE LLVM_PREFIX OUTPUT_STRIP_TRAILING_WHITESPACE)
target_include_directories(c2me-opts-natives-math PRIVATE includes/)
target_compile_options(c2me-opts-natives-math PRIVATE $<$<COMPILE_LANGUAGE:C>:-Wall -Wextra -Wpedantic -ffreestanding -ffile-prefix-map=${CMAKE_SOURCE_DIR}=. -fdebug-compilation-dir=. -fdebug-prefix-map=${CMAKE_SOURCE_DIR}=. -fdebug-prefix-map=${LLVM_PREFIX}=.../llvm-prefix -fno-math-errno -mprefer-vector-width=512 -ffp-contract=off -Rpass-analysis=loop-vectorize -mno-stack-arg-probe -fsave-optimization-record "SHELL:-mllvm -extra-vectorizer-passes" "SHELL:-mllvm -slp-vectorize-hor-store" "SHELL:-mllvm -slp-min-tree-size=1" "SHELL:-mllvm -slp-min-reg-size=64" "SHELL:-mllvm -slp-threshold=-1" "SHELL:-mllvm -enable-epilogue-vectorization">)
target_link_options(c2me-opts-natives-math PRIVATE -v -nostdlib -fuse-ld=lld -ffile-prefix-map=${CMAKE_SOURCE_DIR}=. -fdebug-compilation-dir=. -fdebug-prefix-map=${CMAKE_SOURCE_DIR}=. -fdebug-prefix-map=${LLVM_PREFIX}=.../llvm-prefix)

View File

@@ -0,0 +1,34 @@
#include <ext_math.h>
#include <target_macros.h>
TARGET_IMPL(c2me_natives_noise_perlin_sample, double, (const aligned_uint32_ptr permutations, const double originX,
const double originY, const double originZ, const double x,
const double y, const double z, const double yScale,
const double yMax) {
return math_noise_perlin_sample(permutations, originX, originY, originZ, x, y, z, yScale, yMax);
})
TARGET_IMPL(c2me_natives_noise_perlin_double, double, (const double_octave_sampler_data_t *const data,
const double x, const double y, const double z) {
return math_noise_perlin_double_octave_sample(data, x, y, z);
})
TARGET_IMPL(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) {
math_noise_perlin_double_octave_sample_batch(data, res, x, y, z, length);
})
TARGET_IMPL(c2me_natives_noise_interpolated, double, (const interpolated_noise_sampler_t *const data,
const double x, const double y, const double z) {
return math_noise_perlin_interpolated_sample(data, x, y, z);
})
TARGET_IMPL(c2me_natives_end_islands_sample, float, (const aligned_uint32_ptr simplex_permutations, const int32_t x, const int32_t z) {
return math_end_islands_sample(simplex_permutations, x, z);
})
TARGET_IMPL(c2me_natives_biome_access_sample, uint32_t, (const int64_t theSeed, const int32_t x, const int32_t y, const int32_t z) {
return math_biome_access_sample(theSeed, x, y, z);
})

View File

@@ -0,0 +1,9 @@
#ifdef __x86_64__
#define GATHER_DISABLED 1
#include "exports.c"
#endif
typedef int make_iso_compiler_happy;

View File

@@ -0,0 +1,672 @@
#include <stddef.h>
#include <stdint.h>
#include <float.h>
typedef int make_iso_compilers_happy;
#ifdef WIN32
// ld.lld: error: <root>: undefined symbol: DllMainCRTStartup
int __stdcall DllMainCRTStartup(void* instance, unsigned reason, void* reserved)
{
(void) instance;
(void) reason;
(void) reserved;
return 1;
}
// ld.lld: error: undefined symbol: _fltused
int _fltused = 0;
// ld.lld: error: undefined symbol: abort
void abort(void)
{
__builtin_trap();
}
#endif // WIN32
/*
The following code is from musl, original license below:
musl as a whole is licensed under the following standard MIT license:
----------------------------------------------------------------------
Copyright © 2005-2020 Rich Felker, et al.
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
----------------------------------------------------------------------
*/
// src/internal/libm.h
#if LDBL_MANT_DIG == 53 && LDBL_MAX_EXP == 1024
#elif LDBL_MANT_DIG == 64 && LDBL_MAX_EXP == 16384 && __BYTE_ORDER == __LITTLE_ENDIAN
union ldshape {
long double f;
struct {
uint64_t m;
uint16_t se;
} i;
};
#elif LDBL_MANT_DIG == 64 && LDBL_MAX_EXP == 16384 && __BYTE_ORDER == __BIG_ENDIAN
/* This is the m68k variant of 80-bit long double, and this definition only works
* on archs where the alignment requirement of uint64_t is <= 4. */
union ldshape {
long double f;
struct {
uint16_t se;
uint16_t pad;
uint64_t m;
} i;
};
#elif LDBL_MANT_DIG == 113 && LDBL_MAX_EXP == 16384 && __BYTE_ORDER == __LITTLE_ENDIAN
union ldshape {
long double f;
struct {
uint64_t lo;
uint32_t mid;
uint16_t top;
uint16_t se;
} i;
struct {
uint64_t lo;
uint64_t hi;
} i2;
};
#elif LDBL_MANT_DIG == 113 && LDBL_MAX_EXP == 16384 && __BYTE_ORDER == __BIG_ENDIAN
union ldshape {
long double f;
struct {
uint16_t se;
uint16_t top;
uint32_t mid;
uint64_t lo;
} i;
struct {
uint64_t hi;
uint64_t lo;
} i2;
};
#else
#error Unsupported long double representation
#endif
/* Support non-nearest rounding mode. */
#define WANT_ROUNDING 1
/* Support signaling NaNs. */
#define WANT_SNAN 0
#if WANT_SNAN
#error SNaN is unsupported
#else
#define issignalingf_inline(x) 0
#define issignaling_inline(x) 0
#endif
#ifndef TOINT_INTRINSICS
#define TOINT_INTRINSICS 0
#endif
#if TOINT_INTRINSICS
/* Round x to nearest int in all rounding modes, ties have to be rounded
consistently with converttoint so the results match. If the result
would be outside of [-2^31, 2^31-1] then the semantics is unspecified. */
static double_t roundtoint(double_t);
/* Convert x to nearest int in all rounding modes, ties have to be rounded
consistently with roundtoint. If the result is not representible in an
int32_t then the semantics is unspecified. */
static int32_t converttoint(double_t);
#endif
/* Helps static branch prediction so hot path can be better optimized. */
#ifdef __GNUC__
#define predict_true(x) __builtin_expect(!!(x), 1)
#define predict_false(x) __builtin_expect(x, 0)
#else
#define predict_true(x) (x)
#define predict_false(x) (x)
#endif
/* Evaluate an expression as the specified type. With standard excess
precision handling a type cast or assignment is enough (with
-ffloat-store an assignment is required, in old compilers argument
passing and return statement may not drop excess precision). */
static inline float eval_as_float(float x) {
float y = x;
return y;
}
static inline double eval_as_double(double x) {
double y = x;
return y;
}
/* fp_barrier returns its input, but limits code transformations
as if it had a side-effect (e.g. observable io) and returned
an arbitrary value. */
#ifndef fp_barrierf
#define fp_barrierf fp_barrierf
static inline float fp_barrierf(float x) {
volatile float y = x;
return y;
}
#endif
#ifndef fp_barrier
#define fp_barrier fp_barrier
static inline double fp_barrier(double x) {
volatile double y = x;
return y;
}
#endif
#ifndef fp_barrierl
#define fp_barrierl fp_barrierl
static inline long double fp_barrierl(long double x) {
volatile long double y = x;
return y;
}
#endif
/* fp_force_eval ensures that the input value is computed when that's
otherwise unused. To prevent the constant folding of the input
expression, an additional fp_barrier may be needed or a compilation
mode that does so (e.g. -frounding-math in gcc). Then it can be
used to evaluate an expression for its fenv side-effects only. */
#ifndef fp_force_evalf
#define fp_force_evalf fp_force_evalf
static inline void fp_force_evalf(float x) {
volatile float y;
y = x;
}
#endif
#ifndef fp_force_eval
#define fp_force_eval fp_force_eval
static inline void fp_force_eval(double x) {
volatile double y;
y = x;
}
#endif
#ifndef fp_force_evall
#define fp_force_evall fp_force_evall
static inline void fp_force_evall(long double x) {
volatile long double y;
y = x;
}
#endif
#define FORCE_EVAL(x) do { \
if (sizeof(x) == sizeof(float)) { \
fp_force_evalf(x); \
} else if (sizeof(x) == sizeof(double)) { \
fp_force_eval(x); \
} else { \
fp_force_evall(x); \
} \
} while(0)
#define asuint(f) ((union{float _f; uint32_t _i;}){f})._i
#define asfloat(i) ((union{uint32_t _i; float _f;}){i})._f
#define asuint64(f) ((union{double _f; uint64_t _i;}){f})._i
#define asdouble(i) ((union{uint64_t _i; double _f;}){i})._f
#define EXTRACT_WORDS(hi, lo, d) \
do { \
uint64_t __u = asuint64(d); \
(hi) = __u >> 32; \
(lo) = (uint32_t)__u; \
} while (0)
#define GET_HIGH_WORD(hi, d) \
do { \
(hi) = asuint64(d) >> 32; \
} while (0)
#define GET_LOW_WORD(lo, d) \
do { \
(lo) = (uint32_t)asuint64(d); \
} while (0)
#define INSERT_WORDS(d, hi, lo) \
do { \
(d) = asdouble(((uint64_t)(hi)<<32) | (uint32_t)(lo)); \
} while (0)
#define SET_HIGH_WORD(d, hi) \
INSERT_WORDS(d, hi, (uint32_t)asuint64(d))
#define SET_LOW_WORD(d, lo) \
INSERT_WORDS(d, asuint64(d)>>32, lo)
#define GET_FLOAT_WORD(w, d) \
do { \
(w) = asuint(d); \
} while (0)
#define SET_FLOAT_WORD(d, w) \
do { \
(d) = asfloat(w); \
} while (0)
static int __rem_pio2_large(double *, double *, int, int, int);
static int __rem_pio2(double, double *);
static double __sin(double, double, int);
static double __cos(double, double);
static double __tan(double, double, int);
static double __expo2(double, double);
static int __rem_pio2f(float, double *);
static float __sindf(double);
static float __cosdf(double);
static float __tandf(double, int);
static float __expo2f(float, float);
static int __rem_pio2l(long double, long double *);
static long double __sinl(long double, long double, int);
static long double __cosl(long double, long double);
static long double __tanl(long double, long double, int);
static long double __polevll(long double, const long double *, int);
static long double __p1evll(long double, const long double *, int);
//extern int __signgam;
static double __lgamma_r(double, int *);
static float __lgammaf_r(float, int *);
/* error handling functions */
static float __math_xflowf(uint32_t, float);
static float __math_uflowf(uint32_t);
static float __math_oflowf(uint32_t);
static float __math_divzerof(uint32_t);
static float __math_invalidf(float);
static double __math_xflow(uint32_t, double);
static double __math_uflow(uint32_t);
static double __math_oflow(uint32_t);
static double __math_divzero(uint32_t);
static double __math_invalid(double);
#if LDBL_MANT_DIG != DBL_MANT_DIG
static long double __math_invalidl(long double);
#endif
// src/math/__math_invalidf.c
static float __math_invalidf(float x)
{
return (x - x) / (x - x);
}
// src/math/truncf.c
float truncf(float x) {
union {
float f;
uint32_t i;
} u = {x};
int e = (int) (u.i >> 23 & 0xff) - 0x7f + 9;
uint32_t m;
if (e >= 23 + 9)
return x;
if (e < 9)
e = 1;
m = -1U >> e;
if ((u.i & m) == 0)
return x;
FORCE_EVAL(x + 0x1p120f);
u.i &= ~m;
return u.f;
}
// src/math/floor.c
#if FLT_EVAL_METHOD == 0 || FLT_EVAL_METHOD == 1
#define EPS DBL_EPSILON
#elif FLT_EVAL_METHOD == 2
#define EPS LDBL_EPSILON
#endif
static const double toint = 1 / EPS;
double floor(double x) {
union {
double f;
uint64_t i;
} u = {x};
int e = u.i >> 52 & 0x7ff;
double y;
if (e >= 0x3ff + 52 || x == 0)
return x;
/* y = int(x) - x, where int(x) is an integer neighbor of x */
if (u.i >> 63)
y = x - toint + toint - x;
else
y = x + toint - toint - x;
/* special case because of non-nearest rounding modes */
if (e <= 0x3ff - 1) {
FORCE_EVAL(y);
return u.i >> 63 ? -1 : 0;
}
if (y > 0)
return x + y - 1;
return x + y;
}
// src/math/fmodf.c
float fmodf(float x, float y) {
union {
float f;
uint32_t i;
} ux = {x}, uy = {y};
int ex = ux.i >> 23 & 0xff;
int ey = uy.i >> 23 & 0xff;
uint32_t sx = ux.i & 0x80000000;
uint32_t i;
uint32_t uxi = ux.i;
if (uy.i << 1 == 0 || __builtin_isnan(y) || ex == 0xff)
return (x * y) / (x * y);
if (uxi << 1 <= uy.i << 1) {
if (uxi << 1 == uy.i << 1)
return 0 * x;
return x;
}
/* normalize x and y */
if (!ex) {
for (i = uxi << 9; i >> 31 == 0; ex--, i <<= 1);
uxi <<= -ex + 1;
} else {
uxi &= -1U >> 9;
uxi |= 1U << 23;
}
if (!ey) {
for (i = uy.i << 9; i >> 31 == 0; ey--, i <<= 1);
uy.i <<= -ey + 1;
} else {
uy.i &= -1U >> 9;
uy.i |= 1U << 23;
}
/* x mod y */
for (; ex > ey; ex--) {
i = uxi - uy.i;
if (i >> 31 == 0) {
if (i == 0)
return 0 * x;
uxi = i;
}
uxi <<= 1;
}
i = uxi - uy.i;
if (i >> 31 == 0) {
if (i == 0)
return 0 * x;
uxi = i;
}
for (; uxi >> 23 == 0; uxi <<= 1, ex--);
/* scale result up */
if (ex > 0) {
uxi -= 1U << 23;
uxi |= (uint32_t) ex << 23;
} else {
uxi >>= -ex + 1;
}
uxi |= sx;
ux.i = uxi;
return ux.f;
}
// src/string/memset.c
void *memset(void *dest, int c, size_t n) {
unsigned char *s = dest;
size_t k;
/* Fill head and tail with minimal branching. Each
* conditional ensures that all the subsequently used
* offsets are well-defined and in the dest region. */
if (!n) return dest;
s[0] = c;
s[n - 1] = c;
if (n <= 2) return dest;
s[1] = c;
s[2] = c;
s[n - 2] = c;
s[n - 3] = c;
if (n <= 6) return dest;
s[3] = c;
s[n - 4] = c;
if (n <= 8) return dest;
/* Advance pointer to align it at a 4-byte boundary,
* and truncate n to a multiple of 4. The previous code
* already took care of any head/tail that get cut off
* by the alignment. */
k = -(uintptr_t) s & 3;
s += k;
n -= k;
n &= -4;
#ifdef __GNUC__
typedef uint32_t __attribute__((__may_alias__)) u32;
typedef uint64_t __attribute__((__may_alias__)) u64;
u32 c32 = ((u32) -1) / 255 * (unsigned char) c;
/* In preparation to copy 32 bytes at a time, aligned on
* an 8-byte bounary, fill head/tail up to 28 bytes each.
* As in the initial byte-based head/tail fill, each
* conditional below ensures that the subsequent offsets
* are valid (e.g. !(n<=24) implies n>=28). */
*(u32 *) (s + 0) = c32;
*(u32 *) (s + n - 4) = c32;
if (n <= 8) return dest;
*(u32 *) (s + 4) = c32;
*(u32 *) (s + 8) = c32;
*(u32 *) (s + n - 12) = c32;
*(u32 *) (s + n - 8) = c32;
if (n <= 24) return dest;
*(u32 *) (s + 12) = c32;
*(u32 *) (s + 16) = c32;
*(u32 *) (s + 20) = c32;
*(u32 *) (s + 24) = c32;
*(u32 *) (s + n - 28) = c32;
*(u32 *) (s + n - 24) = c32;
*(u32 *) (s + n - 20) = c32;
*(u32 *) (s + n - 16) = c32;
/* Align to a multiple of 8 so we can fill 64 bits at a time,
* and avoid writing the same bytes twice as much as is
* practical without introducing additional branching. */
k = 24 + ((uintptr_t) s & 4);
s += k;
n -= k;
/* If this loop is reached, 28 tail bytes have already been
* filled, so any remainder when n drops below 32 can be
* safely ignored. */
u64 c64 = c32 | ((u64) c32 << 32);
for (; n >= 32; n -= 32, s += 32) {
*(u64 *) (s + 0) = c64;
*(u64 *) (s + 8) = c64;
*(u64 *) (s + 16) = c64;
*(u64 *) (s + 24) = c64;
}
#else
/* Pure C fallback with no aliasing violations. */
for (; n; n--, s++) *s = c;
#endif
return dest;
}
// src/math/sqrt_data.[c|h]
/* if x in [1,2): i = (int)(64*x);
if x in [2,4): i = (int)(32*x-64);
__rsqrt_tab[i]*2^-16 is estimating 1/sqrt(x) with small relative error:
|__rsqrt_tab[i]*0x1p-16*sqrt(x) - 1| < -0x1.fdp-9 < 2^-8 */
extern const uint16_t __rsqrt_tab[128] = {
0xb451, 0xb2f0, 0xb196, 0xb044, 0xaef9, 0xadb6, 0xac79, 0xab43,
0xaa14, 0xa8eb, 0xa7c8, 0xa6aa, 0xa592, 0xa480, 0xa373, 0xa26b,
0xa168, 0xa06a, 0x9f70, 0x9e7b, 0x9d8a, 0x9c9d, 0x9bb5, 0x9ad1,
0x99f0, 0x9913, 0x983a, 0x9765, 0x9693, 0x95c4, 0x94f8, 0x9430,
0x936b, 0x92a9, 0x91ea, 0x912e, 0x9075, 0x8fbe, 0x8f0a, 0x8e59,
0x8daa, 0x8cfe, 0x8c54, 0x8bac, 0x8b07, 0x8a64, 0x89c4, 0x8925,
0x8889, 0x87ee, 0x8756, 0x86c0, 0x862b, 0x8599, 0x8508, 0x8479,
0x83ec, 0x8361, 0x82d8, 0x8250, 0x81c9, 0x8145, 0x80c2, 0x8040,
0xff02, 0xfd0e, 0xfb25, 0xf947, 0xf773, 0xf5aa, 0xf3ea, 0xf234,
0xf087, 0xeee3, 0xed47, 0xebb3, 0xea27, 0xe8a3, 0xe727, 0xe5b2,
0xe443, 0xe2dc, 0xe17a, 0xe020, 0xdecb, 0xdd7d, 0xdc34, 0xdaf1,
0xd9b3, 0xd87b, 0xd748, 0xd61a, 0xd4f1, 0xd3cd, 0xd2ad, 0xd192,
0xd07b, 0xcf69, 0xce5b, 0xcd51, 0xcc4a, 0xcb48, 0xca4a, 0xc94f,
0xc858, 0xc764, 0xc674, 0xc587, 0xc49d, 0xc3b7, 0xc2d4, 0xc1f4,
0xc116, 0xc03c, 0xbf65, 0xbe90, 0xbdbe, 0xbcef, 0xbc23, 0xbb59,
0xba91, 0xb9cc, 0xb90a, 0xb84a, 0xb78c, 0xb6d0, 0xb617, 0xb560,
};
// src/math/sqrtf.c
#define FENV_SUPPORT 1
static inline uint32_t mul32(uint32_t a, uint32_t b) {
return (uint64_t) a * b >> 32;
}
/* see sqrt.c for more detailed comments. */
float sqrtf(float x) {
uint32_t ix, m, m1, m0, even, ey;
ix = asuint(x);
if (predict_false(ix - 0x00800000 >= 0x7f800000 - 0x00800000)) {
/* x < 0x1p-126 or inf or nan. */
if (ix * 2 == 0)
return x;
if (ix == 0x7f800000)
return x;
if (ix > 0x7f800000)
return __math_invalidf(x);
/* x is subnormal, normalize it. */
ix = asuint(x * 0x1p23f);
ix -= 23 << 23;
}
/* x = 4^e m; with int e and m in [1, 4). */
even = ix & 0x00800000;
m1 = (ix << 8) | 0x80000000;
m0 = (ix << 7) & 0x7fffffff;
m = even ? m0 : m1;
/* 2^e is the exponent part of the return value. */
ey = ix >> 1;
ey += 0x3f800000 >> 1;
ey &= 0x7f800000;
/* compute r ~ 1/sqrt(m), s ~ sqrt(m) with 2 goldschmidt iterations. */
static const uint32_t three = 0xc0000000;
uint32_t r, s, d, u, i;
i = (ix >> 17) % 128;
r = (uint32_t) __rsqrt_tab[i] << 16;
/* |r*sqrt(m) - 1| < 0x1p-8 */
s = mul32(m, r);
/* |s/sqrt(m) - 1| < 0x1p-8 */
d = mul32(s, r);
u = three - d;
r = mul32(r, u) << 1;
/* |r*sqrt(m) - 1| < 0x1.7bp-16 */
s = mul32(s, u) << 1;
/* |s/sqrt(m) - 1| < 0x1.7bp-16 */
d = mul32(s, r);
u = three - d;
s = mul32(s, u);
/* -0x1.03p-28 < s/sqrt(m) - 1 < 0x1.fp-31 */
s = (s - 1) >> 6;
/* s < sqrt(m) < s + 0x1.08p-23 */
/* compute nearest rounded result. */
uint32_t d0, d1, d2;
float y, t;
d0 = (m << 16) - s * s;
d1 = s - d0;
d2 = d1 + s + 1;
s += d1 >> 31;
s &= 0x007fffff;
s |= ey;
y = asfloat(s);
if (FENV_SUPPORT) {
/* handle rounding and inexact exception. */
uint32_t tiny = predict_false(d2 == 0) ? 0 : 0x01000000;
tiny |= (d1 ^ d2) & 0x80000000;
t = asfloat(tiny);
y = eval_as_float(y + t);
}
return y;
}

View File

@@ -0,0 +1,744 @@
#pragma once
#include <stdbool.h>
#include <stdint.h>
#include <stddef.h>
#include <float.h>
__attribute__((aligned(64))) static const double FLAT_SIMPLEX_GRAD[] = {
1, 1, 0, 0,
-1, 1, 0, 0,
1, -1, 0, 0,
-1, -1, 0, 0,
1, 0, 1, 0,
-1, 0, 1, 0,
1, 0, -1, 0,
-1, 0, -1, 0,
0, 1, 1, 0,
0, -1, 1, 0,
0, 1, -1, 0,
0, -1, -1, 0,
1, 1, 0, 0,
0, -1, 1, 0,
-1, 1, 0, 0,
0, -1, -1, 0,
};
static const double SQRT_3 = 1.7320508075688772;
// 0.5 * (SQRT_3 - 1.0)
static const double SKEW_FACTOR_2D = 0.3660254037844386;
// (3.0 - SQRT_3) / 6.0
static const double UNSKEW_FACTOR_2D = 0.21132486540518713;
typedef const double *aligned_double_ptr __attribute__((align_value(64)));
typedef const uint8_t *aligned_uint8_ptr __attribute__((align_value(64)));
typedef const uint32_t *aligned_uint32_ptr __attribute__((align_value(64)));
#pragma clang attribute push (__attribute__((always_inline)), apply_to = function)
static inline __attribute__((const)) float fminf(const float x, const float y) {
return __builtin_fminf(x, y);
}
static inline __attribute__((const)) float fmaxf(const float x, const float y) {
return __builtin_fmaxf(x, y);
}
static inline __attribute__((const)) float fabsf(const float x) {
union {
float f;
uint32_t i;
} u = {x};
u.i &= 0x7fffffff;
return u.f;
}
static inline __attribute__((const)) int64_t labs(const int64_t x) {
return __builtin_labs(x);
}
static inline __attribute__((const)) double floor(double x) {
return __builtin_floor(x);
}
static inline __attribute__((const)) float sqrtf(float x) {
return __builtin_sqrtf(x);
}
static inline __attribute__((const)) float fmodf(float x, float y) {
return __builtin_fmodf(x, y);
}
static inline __attribute__((const)) int32_t math_floorDiv(const int32_t x, const int32_t y) {
int r = x / y;
// if the signs are different and modulo not zero, round down
if ((x ^ y) < 0 && (r * y != x)) {
r--;
}
return r;
}
static inline __attribute__((const)) float clampf(const float value, const float min, const float max) {
return fminf(fmaxf(value, min), max);
}
static inline __attribute__((const)) double math_octave_maintainPrecision(const double value) {
return value - floor(value / 3.3554432E7 + 0.5) * 3.3554432E7;
}
static inline __attribute__((const)) double math_simplex_grad(const int32_t hash, const double x, const double y,
const double z, const double distance) {
double d = distance - x * x - y * y - z * z;
if (d < 0.0) {
return 0.0;
} else {
int32_t i = hash << 2;
double var0 = FLAT_SIMPLEX_GRAD[i | 0] * x;
double var1 = FLAT_SIMPLEX_GRAD[i | 1] * y;
double var2 = FLAT_SIMPLEX_GRAD[i | 2] * z;
return d * d * d * d * (var0 + var1 + var2);
}
}
static inline __attribute__((const)) double math_lerp(const double delta, const double start, const double end) {
return start + delta * (end - start);
}
static inline __attribute__((const)) float math_lerpf(const float delta, const float start, const float end) {
return start + delta * (end - start);
}
static inline __attribute__((const)) double math_clampedLerp(const double start, const double end, const double delta) {
if (delta < 0.0) {
return start;
} else {
return delta > 1.0 ? end : math_lerp(delta, start, end);
}
}
static inline __attribute__((const)) double math_square(const double operand) {
return operand * operand;
}
static inline __attribute__((const)) double math_lerp2(const double deltaX, const double deltaY, const double x0y0,
const double x1y0, const double x0y1, const double x1y1) {
return math_lerp(deltaY, math_lerp(deltaX, x0y0, x1y0), math_lerp(deltaX, x0y1, x1y1));
}
static inline __attribute__((const)) double math_lerp3(
const double deltaX,
const double deltaY,
const double deltaZ,
const double x0y0z0,
const double x1y0z0,
const double x0y1z0,
const double x1y1z0,
const double x0y0z1,
const double x1y0z1,
const double x0y1z1,
const double x1y1z1
) {
return math_lerp(deltaZ, math_lerp2(deltaX, deltaY, x0y0z0, x1y0z0, x0y1z0, x1y1z0),
math_lerp2(deltaX, deltaY, x0y0z1, x1y0z1, x0y1z1, x1y1z1));
}
static inline __attribute__((const)) double math_getLerpProgress(const double value, const double start,
const double end) {
return (value - start) / (end - start);
}
static inline __attribute__((const)) double
math_clampedLerpFromProgress(const double lerpValue, const double lerpStart, const double lerpEnd, const double start,
const double end) {
return math_clampedLerp(start, end, math_getLerpProgress(lerpValue, lerpStart, lerpEnd));
}
static inline __attribute__((const)) int32_t math_floorMod(const int32_t x, const int32_t y) {
int32_t mod = x % y;
// if the signs are different and modulo not zero, adjust result
if ((mod ^ y) < 0 && mod != 0) {
mod += y;
}
return mod;
}
static inline __attribute__((const)) int32_t math_biome2block(const int32_t biomeCoord) {
return biomeCoord << 2;
}
static inline __attribute__((const)) int32_t math_block2biome(const int32_t blockCoord) {
return blockCoord >> 2;
}
static inline __attribute__((const)) uint32_t
__math_simplex_map(const aligned_uint32_ptr permutations, const int32_t input) {
return permutations[input & 0xFF];
}
static inline __attribute__((const)) double math_simplex_dot(const int32_t hash, const double x, const double y,
const double z) {
const int32_t loc = hash << 2;
return FLAT_SIMPLEX_GRAD[loc + 0] * x + FLAT_SIMPLEX_GRAD[loc + 1] * y + FLAT_SIMPLEX_GRAD[loc + 2] * z;
}
static inline __attribute__((const)) double __math_simplex_grad(const int32_t hash, const double x, const double y,
const double z, const double distance) {
double d = distance - x * x - y * y - z * z;
double e;
if (d < 0.0) {
e = 0.0;
} else {
d *= d;
e = d * d * math_simplex_dot(hash, x, y, z);
}
return e;
// double tmp = d * d; // speculative execution
// return d < 0.0 ? 0.0 : tmp * tmp * math_simplex_dot(hash, x, y, z);
}
static inline double __attribute__((const))
math_noise_simplex_sample2d(const aligned_uint32_ptr permutations, const double x, const double y) {
const double d = (x + y) * SKEW_FACTOR_2D;
const double i = floor(x + d);
const double j = floor(y + d);
const double e = (i + j) * UNSKEW_FACTOR_2D;
const double f = i - e;
const double g = j - e;
const double h = x - f;
const double k = y - g;
double l;
int32_t li;
double m;
int32_t mi;
if (h > k) {
l = 1;
li = 1;
m = 0;
mi = 0;
} else {
l = 0;
li = 1;
m = 1;
mi = 1;
}
const double n = h - (double) l + UNSKEW_FACTOR_2D;
const double o = k - (double) m + UNSKEW_FACTOR_2D;
const double p = h - 1.0 + 2.0 * UNSKEW_FACTOR_2D;
const double q = k - 1.0 + 2.0 * UNSKEW_FACTOR_2D;
const int32_t r = (int32_t) i & 0xFF;
const int32_t s = (int32_t) j & 0xFF;
const int32_t t = __math_simplex_map(permutations, r + __math_simplex_map(permutations, s)) % 12;
const int32_t u = __math_simplex_map(permutations, r + li + __math_simplex_map(permutations, s + mi)) % 12;
const int32_t v = __math_simplex_map(permutations, r + 1 + __math_simplex_map(permutations, s + 1)) % 12;
const double w = __math_simplex_grad(t, h, k, 0.0, 0.5);
const double z = __math_simplex_grad(u, n, o, 0.0, 0.5);
const double aa = __math_simplex_grad(v, p, q, 0.0, 0.5);
return 70.0 * (w + z + aa);
}
static inline __attribute__((const)) double math_perlinFade(const double value) {
return value * value * value * (value * (value * 6.0 - 15.0) + 10.0);
}
static inline __attribute__((const)) double __math_perlin_grad(const aligned_uint32_ptr permutations, const int32_t px,
const int32_t py, const int32_t pz, const double fx,
const double fy, const double fz) {
const double f[3] = {fx, fy, fz};
const int32_t p[3] = {px, py, pz};
const uint32_t q[3] = {p[0] & 0xFF, p[1] & 0xFF, p[2] & 0xFF};
const uint32_t hash = permutations[(permutations[(permutations[q[0]] + q[1]) & 0xFF] + q[2]) & 0xFF] & 0xF;
const double *const grad = FLAT_SIMPLEX_GRAD + (hash << 2);
return grad[0] * f[0] + grad[1] * f[1] + grad[2] * f[2];
}
static inline __attribute__((const)) double
math_noise_perlin_sampleScalar(const aligned_uint32_ptr permutations,
const int32_t px0, const int32_t py0, const int32_t pz0,
const double fx0, const double fy0, const double fz0, const double fadeLocalY) {
const int32_t px1 = px0 + 1;
const int32_t py1 = py0 + 1;
const int32_t pz1 = pz0 + 1;
const double fx1 = fx0 - 1;
const double fy1 = fy0 - 1;
const double fz1 = fz0 - 1;
const double f000 = __math_perlin_grad(permutations, px0, py0, pz0, fx0, fy0, fz0);
const double f100 = __math_perlin_grad(permutations, px1, py0, pz0, fx1, fy0, fz0);
const double f010 = __math_perlin_grad(permutations, px0, py1, pz0, fx0, fy1, fz0);
const double f110 = __math_perlin_grad(permutations, px1, py1, pz0, fx1, fy1, fz0);
const double f001 = __math_perlin_grad(permutations, px0, py0, pz1, fx0, fy0, fz1);
const double f101 = __math_perlin_grad(permutations, px1, py0, pz1, fx1, fy0, fz1);
const double f011 = __math_perlin_grad(permutations, px0, py1, pz1, fx0, fy1, fz1);
const double f111 = __math_perlin_grad(permutations, px1, py1, pz1, fx1, fy1, fz1);
const double dx = math_perlinFade(fx0);
const double dy = math_perlinFade(fadeLocalY);
const double dz = math_perlinFade(fz0);
return math_lerp3(dx, dy, dz, f000, f100, f010, f110, f001, f101, f011, f111);
}
static inline __attribute__((const)) double
math_noise_perlin_sample(const aligned_uint32_ptr permutations,
const double originX, const double originY, const double originZ,
const double x, const double y, const double z,
const double yScale, const double yMax) {
const double d = x + originX;
const double e = y + originY;
const double f = z + originZ;
const double i = floor(d);
const double j = floor(e);
const double k = floor(f);
const double g = d - i;
const double h = e - j;
const double l = f - k;
const double o = yScale != 0 ? floor(((yMax >= 0.0 && yMax < h) ? yMax : h) / yScale + 1.0E-7) * yScale : 0;
return math_noise_perlin_sampleScalar(permutations, (int32_t) i, (int32_t) j, (int32_t) k, g, h - o, l, h);
}
typedef const struct double_octave_sampler_data {
const uint64_t length;
const double amplitude;
const bool *const need_shift;
const aligned_double_ptr lacunarity_powd;
const aligned_double_ptr persistence_powd;
const aligned_uint32_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 amplitudes;
} double_octave_sampler_data_t;
static inline __attribute__((const)) double
math_noise_perlin_double_octave_sample_impl(const double_octave_sampler_data_t *const data,
const double x, const double y, const double z,
const double yScale, const double yMax, const uint8_t useOrigin) {
double ds[data->length];
#pragma clang loop vectorize(enable) interleave(enable) interleave_count(2)
for (uint32_t i = 0; i < data->length; i++) {
const double e = data->lacunarity_powd[i];
const double f = data->persistence_powd[i];
const aligned_uint32_ptr permutations = data->sampler_permutations + 256 * i;
const double sampleX = data->need_shift[i] ? x * 1.0181268882175227 : x;
const double sampleY = data->need_shift[i] ? y * 1.0181268882175227 : y;
const double sampleZ = data->need_shift[i] ? z * 1.0181268882175227 : z;
const double g = math_noise_perlin_sample(
permutations,
data->sampler_originX[i],
data->sampler_originY[i],
data->sampler_originZ[i],
math_octave_maintainPrecision(sampleX * e),
useOrigin ? -(data->sampler_originY[i]) : math_octave_maintainPrecision(sampleY * e),
math_octave_maintainPrecision(sampleZ * e),
yScale * e,
yMax * e);
ds[i] = data->amplitudes[i] * g * f;
}
double d1 = 0.0;
double d2 = 0.0;
for (uint32_t i = 0; i < data->length; i++) {
if (!data->need_shift[i]) {
d1 += ds[i];
} else {
d2 += ds[i];
}
}
return (d1 + d2) * data->amplitude;
}
//static inline void
//math_noise_perlin_double_octave_sample_impl_batch(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) {
// double ds[data->length][length];
//
// for (uint32_t si = 0; si < data->length; si ++) {
//#pragma clang loop vectorize(enable) interleave(enable) interleave_count(2)
// for (uint32_t bi = 0; bi < length; bi++) {
// const double e = data->lacunarity_powd[si];
// const double f = data->persistence_powd[si];
// const aligned_uint32_ptr permutations = data->sampler_permutations + 256 * si;
// const double sampleX = data->need_shift[si] ? x[bi] * 1.0181268882175227 : x[bi];
// const double sampleY = data->need_shift[si] ? y[bi] * 1.0181268882175227 : y[bi];
// const double sampleZ = data->need_shift[si] ? z[bi] * 1.0181268882175227 : z[bi];
// const double g = math_noise_perlin_sample(
// permutations,
// data->sampler_originX[si],
// data->sampler_originY[si],
// data->sampler_originZ[si],
// math_octave_maintainPrecision(sampleX * e),
// math_octave_maintainPrecision(sampleY * e),
// math_octave_maintainPrecision(sampleZ * e),
// 0.0,
// 0.0);
// ds[si][bi] = data->amplitudes[si] * g * f;
// }
// }
//
// double d1[length];
// double d2[length];
// for (uint32_t i = 0; i < length; i ++) {
// d1[i] = 0.0;
// d2[i] = 0.0;
// }
// for (uint32_t bi = 0; bi < length; bi++) {
// for (uint32_t si = 0; si < data->length; si ++) {
// if (!data->need_shift[si]) {
// d1[bi] += ds[si][bi];
// } else {
// d2[bi] += ds[si][bi];
// }
// }
// }
// for (uint32_t bi = 0; bi < length; bi++) {
// res[bi] = (d1[bi] + d2[bi]) * data->amplitude;
// }
//}
//static inline void
//math_noise_perlin_double_octave_sample_impl_batch(const double_octave_sampler_data_t *restrict const data,
// double *restrict const res, const double *restrict const x,
// const double *restrict const y, const double *restrict const z,
// const uint32_t length) {
// const uint32_t total_len = data->length * length;
//
// double ds[total_len];
// uint32_t sia[total_len]; // sampler index array
// uint32_t bia[total_len]; // batch index array
// double xa[total_len]; // x array
// double ya[total_len]; // y array
// double za[total_len]; // z array
//
// double lacunarity_powd[total_len];
// double persistence_powd[total_len];
// bool need_shift[total_len];
// double sampler_originX[total_len];
// double sampler_originY[total_len];
// double sampler_originZ[total_len];
// double amplitudes[total_len];
//
// {
// uint32_t idx = 0;
// for (uint32_t si = 0; si < data->length; si++) {
// for (uint32_t bi = 0; bi < length; bi++) {
// sia[idx] = si;
// bia[idx] = bi;
// xa[idx] = x[bi];
// ya[idx] = y[bi];
// za[idx] = z[bi];
// lacunarity_powd[idx] = data->lacunarity_powd[si];
// persistence_powd[idx] = data->persistence_powd[si];
// need_shift[idx] = data->need_shift[si];
// sampler_originX[idx] = data->sampler_originX[si];
// sampler_originY[idx] = data->sampler_originY[si];
// sampler_originZ[idx] = data->sampler_originZ[si];
// amplitudes[idx] = data->amplitudes[si];
// idx++;
// }
// }
// }
//
//#pragma clang loop vectorize(enable) interleave(enable) interleave_count(2)
// for (uint32_t idx = 0; idx < total_len; idx++) {
// const uint32_t si = sia[idx];
// const double xi = xa[idx];
// const double yi = ya[idx];
// const double zi = za[idx];
// const double e = lacunarity_powd[idx];
// const double f = persistence_powd[idx];
// const aligned_uint32_ptr permutations = data->sampler_permutations + 256 * si;
// const double sampleX = need_shift[idx] ? xi * 1.0181268882175227 : xi;
// const double sampleY = need_shift[idx] ? yi * 1.0181268882175227 : yi;
// const double sampleZ = need_shift[idx] ? zi * 1.0181268882175227 : zi;
// const double g = math_noise_perlin_sample(
// permutations,
// sampler_originX[idx],
// sampler_originY[idx],
// sampler_originZ[idx],
// math_octave_maintainPrecision(sampleX * e),
// math_octave_maintainPrecision(sampleY * e),
// math_octave_maintainPrecision(sampleZ * e),
// 0.0,
// 0.0);
// ds[idx] = amplitudes[idx] * g * f;
// }
//
// double d1[length];
// double d2[length];
// for (uint32_t i = 0; i < length; i++) {
// d1[i] = 0.0;
// d2[i] = 0.0;
// }
// for (uint32_t idx = 0; idx < total_len; idx++) {
// const uint32_t si = sia[idx];
// const uint32_t bi = bia[idx];
// if (!data->need_shift[si]) {
// d1[bi] += ds[idx];
// } else {
// d2[bi] += ds[idx];
// }
// }
// for (uint32_t bi = 0; bi < length; bi++) {
// res[bi] = (d1[bi] + d2[bi]) * data->amplitude;
// }
//}
static inline __attribute__((const)) double
math_noise_perlin_double_octave_sample(const double_octave_sampler_data_t *const data,
const double x, const double y, const double z) {
return math_noise_perlin_double_octave_sample_impl(data, x, y, z, 0.0, 0.0, 0);
}
static inline void
math_noise_perlin_double_octave_sample_batch(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) {
// math_noise_perlin_double_octave_sample_impl_batch(data, res, x, y, z, length);
for (uint32_t i = 0; i < length; i ++) {
res[i] = math_noise_perlin_double_octave_sample_impl(data, x[i], y[i], z[i], 0.0, 0.0, 0);
}
}
typedef const struct interpolated_noise_sub_sampler {
const aligned_uint32_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;
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;
static inline __attribute__((const)) double
math_noise_perlin_interpolated_sample(const interpolated_noise_sampler_t *const data,
const double x, const double y, const double z) {
const double d = x * data->scaledXzScale;
const double e = y * data->scaledYScale;
const double f = z * data->scaledXzScale;
const double g = d / data->xzFactor;
const double h = e / data->yFactor;
const double i = f / data->xzFactor;
const double j = data->scaledYScale * data->smearScaleMultiplier;
const double k = j / data->yFactor;
double l = 0.0;
double m = 0.0;
double n = 0.0;
double ns[data->normal.length];
#pragma clang loop vectorize(enable)
for (uint32_t offset = 0; offset < data->normal.length; offset++) {
ns[offset] = math_noise_perlin_sample(
data->normal.sampler_permutations + 256 * offset,
data->normal.sampler_originX[offset],
data->normal.sampler_originY[offset],
data->normal.sampler_originZ[offset],
math_octave_maintainPrecision(g * data->normal.sampler_mulFactor[offset]),
math_octave_maintainPrecision(h * data->normal.sampler_mulFactor[offset]),
math_octave_maintainPrecision(i * data->normal.sampler_mulFactor[offset]),
k * data->normal.sampler_mulFactor[offset],
h * data->normal.sampler_mulFactor[offset]
) / data->normal.sampler_mulFactor[offset];
}
for (uint32_t offset = 0; offset < data->normal.length; offset++) {
n += ns[offset];
}
const double q = (n / 10.0 + 1.0) / 2.0;
const uint8_t bl2 = q >= 1.0;
const uint8_t bl3 = q <= 0.0;
if (!bl2) {
double ls[data->lower.length];
#pragma clang loop vectorize(enable) interleave_count(2)
for (uint32_t offset = 0; offset < data->lower.length; offset++) {
ls[offset] = math_noise_perlin_sample(
data->lower.sampler_permutations + 256 * offset,
data->lower.sampler_originX[offset],
data->lower.sampler_originY[offset],
data->lower.sampler_originZ[offset],
math_octave_maintainPrecision(d * data->lower.sampler_mulFactor[offset]),
math_octave_maintainPrecision(e * data->lower.sampler_mulFactor[offset]),
math_octave_maintainPrecision(f * data->lower.sampler_mulFactor[offset]),
j * data->lower.sampler_mulFactor[offset],
e * data->lower.sampler_mulFactor[offset]
) / data->lower.sampler_mulFactor[offset];
}
for (uint32_t offset = 0; offset < data->lower.length; offset++) {
l += ls[offset];
}
}
if (!bl3) {
double ms[data->upper.length];
#pragma clang loop vectorize(enable) interleave_count(2)
for (uint32_t offset = 0; offset < data->upper.length; offset++) {
ms[offset] = math_noise_perlin_sample(
data->upper.sampler_permutations + 256 * offset,
data->upper.sampler_originX[offset],
data->upper.sampler_originY[offset],
data->upper.sampler_originZ[offset],
math_octave_maintainPrecision(d * data->upper.sampler_mulFactor[offset]),
math_octave_maintainPrecision(e * data->upper.sampler_mulFactor[offset]),
math_octave_maintainPrecision(f * data->upper.sampler_mulFactor[offset]),
j * data->upper.sampler_mulFactor[offset],
e * data->upper.sampler_mulFactor[offset]
) / data->upper.sampler_mulFactor[offset];
}
for (uint32_t offset = 0; offset < data->upper.length; offset++) {
m += ms[offset];
}
}
return math_clampedLerp(l / 512.0, m / 512.0, q) / 128.0;
}
static inline __attribute__((const)) float
math_end_islands_sample(const aligned_uint32_ptr simplex_permutations, const int32_t x, const int32_t z) {
const int32_t i = x / 2;
const int32_t j = z / 2;
const int32_t k = x % 2;
const int32_t l = z % 2;
const int32_t muld = x * x + z * z; // int32_t intentionally
if (muld < 0) {
return __builtin_nanf("");
}
float f = 100.0F - sqrtf((float) muld) * 8.0F;
f = clampf(f, -100.0F, 80.0F);
int8_t ms[25 * 25], ns[25 * 25], hit[25 * 25];
const int64_t omin = labs(i) - 12LL;
const int64_t pmin = labs(j) - 12LL;
const int64_t omax = labs(i) + 12LL;
const int64_t pmax = labs(j) + 12LL;
{
uint32_t idx = 0;
#pragma clang loop vectorize(enable)
for (int8_t m = -12; m < 13; m++) {
for (int8_t n = -12; n < 13; n++) {
ms[idx] = m;
ns[idx] = n;
idx++;
}
}
if (idx != 25 * 25) {
__builtin_trap();
}
}
if (omin * omin + pmin * pmin > 4096LL) {
for (uint32_t idx = 0; idx < 25 * 25; idx++) {
const int64_t o = (int64_t) i + (int64_t) ms[idx];
const int64_t p = (int64_t) j + (int64_t) ns[idx];
hit[idx] = math_noise_simplex_sample2d(simplex_permutations, (double) o, (double) p) < -0.9F;
}
} else {
for (uint32_t idx = 0; idx < 25 * 25; idx++) {
const int64_t o = (int64_t) i + (int64_t) ms[idx];
const int64_t p = (int64_t) j + (int64_t) ns[idx];
hit[idx] = (o * o + p * p > 4096LL) && math_noise_simplex_sample2d(
simplex_permutations, (double) o, (double) p) < -0.9F;
}
}
#pragma clang loop vectorize(enable) interleave(enable)
for (uint32_t idx = 0; idx < 25 * 25; idx++) {
if (hit[idx]) {
const int32_t m = ms[idx];
const int32_t n = ns[idx];
const int64_t o = (int64_t) i + (int64_t) m;
const int64_t p = (int64_t) j + (int64_t) n;
const float g1 = fabsf((float) o) * 3439.0F;
const float g2 = fabsf((float) p) * 147.0F;
const float g = fmodf((g1 + g2), 13.0F) + 9.0F;
const float h = (float) (k - m * 2);
const float q = (float) (l - n * 2);
float r = 100.0F - sqrtf(h * h + q * q) * g;
r = clampf(r, -100.0F, 80.0F);
f = fmaxf(f, r);
}
}
return f;
}
static inline __attribute__((const)) uint32_t
math_biome_access_sample(const int64_t theSeed, const int32_t x, const int32_t y, const int32_t z) {
const int32_t var0 = x - 2;
const int32_t var1 = y - 2;
const int32_t var2 = z - 2;
const int32_t var3 = var0 >> 2;
const int32_t var4 = var1 >> 2;
const int32_t var5 = var2 >> 2;
const double var6 = (double) (var0 & 3) / 4.0;
const double var7 = (double) (var1 & 3) / 4.0;
const double var8 = (double) (var2 & 3) / 4.0;
uint32_t var9 = 0;
double var10 = DBL_MAX;
double var28s[8];
#pragma clang loop interleave_count(2)
for (uint32_t var11 = 0; var11 < 8; ++var11) {
uint32_t var12 = var11 & 4;
uint32_t var13 = var11 & 2;
uint32_t var14 = var11 & 1;
int64_t var15 = var12 ? var3 + 1 : var3;
int64_t var16 = var13 ? var4 + 1 : var4;
int64_t var17 = var14 ? var5 + 1 : var5;
double var18 = var12 ? var6 - 1.0 : var6;
double var19 = var13 ? var7 - 1.0 : var7;
double var20 = var14 ? var8 - 1.0 : var8;
int64_t var21 = theSeed * (theSeed * 6364136223846793005L + 1442695040888963407L) + var15;
var21 = var21 * (var21 * 6364136223846793005L + 1442695040888963407L) + var16;
var21 = var21 * (var21 * 6364136223846793005L + 1442695040888963407L) + var17;
var21 = var21 * (var21 * 6364136223846793005L + 1442695040888963407L) + var15;
var21 = var21 * (var21 * 6364136223846793005L + 1442695040888963407L) + var16;
var21 = var21 * (var21 * 6364136223846793005L + 1442695040888963407L) + var17;
double var22 = (double) ((var21 >> 24) & 1023) / 1024.0;
double var23 = (var22 - 0.5) * 0.9;
var21 = var21 * (var21 * 6364136223846793005L + 1442695040888963407L) + theSeed;
double var24 = (double) ((var21 >> 24) & 1023) / 1024.0;
double var25 = (var24 - 0.5) * 0.9;
var21 = var21 * (var21 * 6364136223846793005L + 1442695040888963407L) + theSeed;
double var26 = (double) ((var21 >> 24) & 1023) / 1024.0;
double var27 = (var26 - 0.5) * 0.9;
double var28 = math_square(var20 + var27) + math_square(var19 + var25) + math_square(var18 + var23);
var28s[var11] = var28;
}
for (int i = 0; i < 8; ++i) {
if (var10 > var28s[i]) {
var9 = i;
var10 = var28s[i];
}
}
return var9;
}
#pragma clang attribute pop

View File

@@ -0,0 +1,28 @@
#pragma once
#define TARGET_IMPL_ARCH(suffix, func_prefix, func_ret, func_call) \
func_ret func_prefix##_##suffix func_call
#ifdef __x86_64__
#ifdef GATHER_DISABLED
#define TARGET_IMPL(func_prefix, func_ret, func_call) \
__attribute__((pure, target("arch=haswell"))) TARGET_IMPL_ARCH(avx2, func_prefix, func_ret, func_call) \
__attribute__((pure, target("arch=skylake-avx512"))) TARGET_IMPL_ARCH(avx512skx, func_prefix, func_ret, func_call) \
__attribute__((pure, target("arch=icelake-server"))) TARGET_IMPL_ARCH(avx512icl, func_prefix, func_ret, func_call)
#else
#define TARGET_IMPL(func_prefix, func_ret, func_call) \
__attribute__((pure, target("arch=x86-64"))) TARGET_IMPL_ARCH(sse2, func_prefix, func_ret, func_call) \
__attribute__((pure, target("arch=x86-64-v2"))) TARGET_IMPL_ARCH(sse4_2, func_prefix, func_ret, func_call) \
__attribute__((pure, target("arch=sandybridge"))) TARGET_IMPL_ARCH(avx, func_prefix, func_ret, func_call) \
__attribute__((pure, target("arch=alderlake"))) TARGET_IMPL_ARCH(avx2adl, func_prefix, func_ret, func_call) \
__attribute__((pure, target("arch=sapphirerapids"))) TARGET_IMPL_ARCH(avx512spr, func_prefix, func_ret, func_call)
#endif
#else
#define TARGET_IMPL(func_prefix, func_ret, func_call) \
__attribute__((pure)) TARGET_IMPL_ARCH(generic, func_prefix, func_ret, func_call)
#endif

View File

@@ -0,0 +1,11 @@
#ifdef __aarch64__
#include <stdint.h>
int32_t c2me_natives_get_system_isa(_Bool allowAVX512) {
return 0;
}
#endif
typedef int make_iso_compiler_happy;

View File

@@ -0,0 +1,154 @@
#ifdef __x86_64__
#include <stdint.h>
static void __cpuid(int info[4], int infoType) {
__asm__ __volatile__("cpuid" : "=a"(info[0]), "=b"(info[1]), "=c"(info[2]), "=d"(info[3]) : "0"(infoType));
}
static void __cpuidex(int info[4], int level, int count) {
__asm__ __volatile__("cpuid" : "=a"(info[0]), "=b"(info[1]), "=c"(info[2]), "=d"(info[3]) : "0"(level), "2"(count));
}
static int __os_has_avx_support(void) {
// Check xgetbv; this uses a .byte sequence instead of the instruction
// directly because older assemblers do not include support for xgetbv and
// there is no easy way to conditionally compile based on the assembler used.
int rEAX, rEDX;
__asm__ __volatile__(".byte 0x0f, 0x01, 0xd0" : "=a"(rEAX), "=d"(rEDX) : "c"(0));
return (rEAX & 6) == 6;
}
#if !defined(__APPLE__)
static int __os_has_avx512_support(void) {
// Check if the OS saves the XMM, YMM and ZMM registers, i.e. it supports AVX2 and AVX512.
// See section 2.1 of software.intel.com/sites/default/files/managed/0d/53/319433-022.pdf
// Check xgetbv; this uses a .byte sequence instead of the instruction
// directly because older assemblers do not include support for xgetbv and
// there is no easy way to conditionally compile based on the assembler used.
int rEAX, rEDX;
__asm__ __volatile__(".byte 0x0f, 0x01, 0xd0" : "=a"(rEAX), "=d"(rEDX) : "c"(0));
return (rEAX & 0xE6) == 0xE6;
}
#endif // !__APPLE__
// __get_system_isa should return a value corresponding to one of the
// Target::ISA enumerant values that gives the most capable ISA that the
// current system can run.
int32_t c2me_natives_get_system_isa(_Bool allowAVX512) {
int info[4];
__cpuid(info, 1);
// Call cpuid with eax=7, ecx=0
int info2[4];
__cpuidex(info2, 7, 0);
int info3[4] = {0, 0, 0, 0};
int max_subleaf = info2[0];
// Call cpuid with eax=7, ecx=1
if (max_subleaf >= 1)
__cpuidex(info3, 7, 1);
// clang-format off
_Bool sse2 = (info[3] & (1 << 26)) != 0;
_Bool sse41 = (info[2] & (1 << 19)) != 0;
_Bool sse42 = (info[2] & (1 << 20)) != 0;
_Bool avx = (info[2] & (1 << 28)) != 0;
_Bool avx2 = (info2[1] & (1 << 5)) != 0;
_Bool avx_vnni = (info3[0] & (1 << 4)) != 0;
_Bool avx_f16c = (info[2] & (1 << 29)) != 0;
_Bool avx_rdrand = (info[2] & (1 << 30)) != 0;
_Bool osxsave = (info[2] & (1 << 27)) != 0;
_Bool avx512_f = (info2[1] & (1 << 16)) != 0;
// clang-format on
// NOTE: the values returned below must be the same as the
// corresponding enumerant values in Target::ISA.
if (allowAVX512 && osxsave && avx2 && avx512_f
#if !defined(__APPLE__)
&& __os_has_avx512_support()
#endif // !__APPLE__
) {
// We need to verify that AVX2 is also available,
// as well as AVX512, because our targets are supposed
// to use both.
// clang-format off
_Bool avx512_dq = (info2[1] & (1 << 17)) != 0;
_Bool avx512_pf = (info2[1] & (1 << 26)) != 0;
_Bool avx512_er = (info2[1] & (1 << 27)) != 0;
_Bool avx512_cd = (info2[1] & (1 << 28)) != 0;
_Bool avx512_bw = (info2[1] & (1 << 30)) != 0;
_Bool avx512_vl = (info2[1] & (1 << 31)) != 0;
#if !defined(__APPLE__)
_Bool avx512_vbmi2 = (info2[2] & (1 << 6)) != 0;
_Bool avx512_gfni = (info2[2] & (1 << 8)) != 0;
_Bool avx512_vaes = (info2[2] & (1 << 9)) != 0;
_Bool avx512_vpclmulqdq = (info2[2] & (1 << 10)) != 0;
_Bool avx512_vnni = (info2[2] & (1 << 11)) != 0;
_Bool avx512_bitalg = (info2[2] & (1 << 12)) != 0;
_Bool avx512_vpopcntdq = (info2[2] & (1 << 14)) != 0;
_Bool avx512_bf16 = (info3[0] & (1 << 5)) != 0;
_Bool avx512_vp2intersect = (info2[3] & (1 << 8)) != 0;
_Bool avx512_amx_bf16 = (info2[3] & (1 << 22)) != 0;
_Bool avx512_amx_tile = (info2[3] & (1 << 24)) != 0;
_Bool avx512_amx_int8 = (info2[3] & (1 << 25)) != 0;
_Bool avx512_fp16 = (info2[3] & (1 << 23)) != 0;
#endif // !__APPLE__
// clang-format on
// Knights Landing: KNL = F + PF + ER + CD
// Skylake server: SKX = F + DQ + CD + BW + VL
// Cascade Lake server: CLX = SKX + VNNI
// Cooper Lake server: CPX = CLX + BF16
// Ice Lake client & server: ICL = CLX + VBMI2 + GFNI + VAES + VPCLMULQDQ + BITALG + VPOPCNTDQ
// Tiger Lake: TGL = ICL + VP2INTERSECT
// Sapphire Rapids: SPR = ICL + BF16 + AMX_BF16 + AMX_TILE + AMX_INT8 + AVX_VNNI + FP16
_Bool knl = avx512_pf && avx512_er && avx512_cd;
_Bool skx = avx512_dq && avx512_cd && avx512_bw && avx512_vl;
#if !defined(__APPLE__)
_Bool clx = skx && avx512_vnni;
_Bool cpx = clx && avx512_bf16;
_Bool icl =
clx && avx512_vbmi2 && avx512_gfni && avx512_vaes && avx512_vpclmulqdq && avx512_bitalg && avx512_vpopcntdq;
_Bool tgl = icl && avx512_vp2intersect;
_Bool spr =
icl && avx512_bf16 && avx512_amx_bf16 && avx512_amx_tile && avx512_amx_int8 && avx_vnni && avx512_fp16;
if (spr) {
return 9; // SPR
} else if (icl) {
return 8; // ICL
}
#endif // !__APPLE__
if (skx) {
return 7; // SKX
} else if (knl) {
return 6; // KNL
}
// If it's unknown AVX512 target, fall through and use AVX2
// or whatever is available in the machine.
}
if (osxsave && avx && __os_has_avx_support()) {
// if (avx_vnni) {
// return 5; // ADL
// }
if (avx_f16c && avx_rdrand && avx2) {
return 4;
}
// Regular AVX
return 3;
} else if (sse42) {
return 2; // SSE4.2
} else if (sse41) {
return 1; // SSE4.1
} else if (sse2) {
return 0; // SSE2
} else {
__builtin_trap();
}
}
#endif
typedef int make_iso_compiler_happy;

View File

@@ -0,0 +1,11 @@
set(CMAKE_SYSTEM_NAME Darwin)
set(CMAKE_SYSTEM_PROCESSOR x86_64)
set(triple x86_64-apple-darwin)
set(CMAKE_C_COMPILER clang)
set(CMAKE_C_COMPILER_TARGET ${triple})
set(CMAKE_CXX_COMPILER clang++)
set(CMAKE_CXX_COMPILER_TARGET ${triple})
set(CMAKE_C_FLAGS "-march=x86-64 -Wl,-no_uuid")

View File

@@ -0,0 +1,10 @@
set(CMAKE_SYSTEM_NAME Linux)
set(CMAKE_SYSTEM_PROCESSOR arm64)
set(triple aarch64-unknown-linux-musl)
set(CMAKE_C_COMPILER clang)
set(CMAKE_C_COMPILER_TARGET ${triple})
set(CMAKE_CXX_COMPILER clang++)
set(CMAKE_CXX_COMPILER_TARGET ${triple})

View File

@@ -0,0 +1,11 @@
set(CMAKE_SYSTEM_NAME Linux)
set(CMAKE_SYSTEM_PROCESSOR x86_64)
set(triple x86_64-unknown-linux-musl)
set(CMAKE_C_COMPILER clang)
set(CMAKE_C_COMPILER_TARGET ${triple})
set(CMAKE_CXX_COMPILER clang++)
set(CMAKE_CXX_COMPILER_TARGET ${triple})
set(CMAKE_C_FLAGS -march=x86-64)

View File

@@ -0,0 +1,11 @@
set(CMAKE_SYSTEM_NAME Windows)
set(CMAKE_SYSTEM_PROCESSOR AMD64)
set(triple x86_64-pc-windows-gnu)
set(CMAKE_C_COMPILER clang)
set(CMAKE_C_COMPILER_TARGET ${triple})
set(CMAKE_CXX_COMPILER clang++)
set(CMAKE_CXX_COMPILER_TARGET ${triple})
set(CMAKE_C_FLAGS "-march=x86-64 -Wl,--no-insert-timestamp")

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

Some files were not shown because too many files have changed in this diff Show More