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:
32
divinemc-server/src/c/CMakeLists.txt
Normal file
32
divinemc-server/src/c/CMakeLists.txt
Normal 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)
|
||||
|
||||
34
divinemc-server/src/c/exports.c
Normal file
34
divinemc-server/src/c/exports.c
Normal 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);
|
||||
})
|
||||
9
divinemc-server/src/c/exports_x86_64_nogather.c
Normal file
9
divinemc-server/src/c/exports_x86_64_nogather.c
Normal file
@@ -0,0 +1,9 @@
|
||||
#ifdef __x86_64__
|
||||
|
||||
#define GATHER_DISABLED 1
|
||||
|
||||
#include "exports.c"
|
||||
|
||||
#endif
|
||||
|
||||
typedef int make_iso_compiler_happy;
|
||||
672
divinemc-server/src/c/flibc.c
Normal file
672
divinemc-server/src/c/flibc.c
Normal 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;
|
||||
}
|
||||
|
||||
744
divinemc-server/src/c/includes/ext_math.h
Normal file
744
divinemc-server/src/c/includes/ext_math.h
Normal 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
|
||||
|
||||
28
divinemc-server/src/c/includes/target_macros.h
Normal file
28
divinemc-server/src/c/includes/target_macros.h
Normal 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
|
||||
|
||||
11
divinemc-server/src/c/system_isa_aarch64.c
Normal file
11
divinemc-server/src/c/system_isa_aarch64.c
Normal 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;
|
||||
154
divinemc-server/src/c/system_isa_x86_64.c
Normal file
154
divinemc-server/src/c/system_isa_x86_64.c
Normal 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;
|
||||
11
divinemc-server/src/c/targets/darwin-x86_64.cmake
Normal file
11
divinemc-server/src/c/targets/darwin-x86_64.cmake
Normal 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")
|
||||
10
divinemc-server/src/c/targets/linux-aarch_64.cmake
Normal file
10
divinemc-server/src/c/targets/linux-aarch_64.cmake
Normal 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})
|
||||
|
||||
11
divinemc-server/src/c/targets/linux-x86_64.cmake
Normal file
11
divinemc-server/src/c/targets/linux-x86_64.cmake
Normal 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)
|
||||
11
divinemc-server/src/c/targets/windows-x86_64.cmake
Normal file
11
divinemc-server/src/c/targets/windows-x86_64.cmake
Normal 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")
|
||||
@@ -0,0 +1,89 @@
|
||||
package net.caffeinemc.mods.lithium.common.util.math;
|
||||
|
||||
import net.minecraft.util.Mth;
|
||||
|
||||
/**
|
||||
* A replacement for the sine angle lookup table used in {@link Mth}, both reducing the size of LUT and improving
|
||||
* the access patterns for common paired sin/cos operations.
|
||||
* <p>
|
||||
* sin(-x) = -sin(x)
|
||||
* ... to eliminate negative angles from the LUT.
|
||||
* <p>
|
||||
* sin(x) = sin(pi/2 - x)
|
||||
* ... to eliminate supplementary angles from the LUT.
|
||||
* <p>
|
||||
* Using these identities allows us to reduce the LUT from 64K entries (256 KB) to just 16K entries (64 KB), enabling
|
||||
* it to better fit into the CPU's caches at the expense of some cycles on the fast path. The implementation has been
|
||||
* tightly optimized to avoid branching where possible and to use very quick integer operations.
|
||||
* <p>
|
||||
* Generally speaking, reducing the size of a lookup table is always a good optimization, but since we need to spend
|
||||
* extra CPU cycles trying to maintain parity with vanilla, there is the potential risk that this implementation ends
|
||||
* up being slower than vanilla when the lookup table is able to be kept in cache memory.
|
||||
* <p>
|
||||
* Unlike other "fast math" implementations, the values returned by this class are *bit-for-bit identical* with those
|
||||
* from {@link Mth}. Validation is performed during runtime to ensure that the table is correct.
|
||||
*
|
||||
* @author coderbot16 Author of the original (and very clever) implementation in <a href="https://gitlab.com/coderbot16/i73/-/tree/master/i73-trig/src">Rust</a>
|
||||
* @author jellysquid3 Additional optimizations, port to Java
|
||||
*/
|
||||
public class CompactSineLUT {
|
||||
private static final int[] SINE_TABLE_INT = new int[16384 + 1];
|
||||
private static final float SINE_TABLE_MIDPOINT;
|
||||
|
||||
static {
|
||||
final float[] SINE_TABLE = Mth.SIN;
|
||||
// Copy the sine table, covering to raw int bits
|
||||
for (int i = 0; i < SINE_TABLE_INT.length; i++) {
|
||||
SINE_TABLE_INT[i] = Float.floatToRawIntBits(SINE_TABLE[i]);
|
||||
}
|
||||
|
||||
SINE_TABLE_MIDPOINT = SINE_TABLE[SINE_TABLE.length / 2];
|
||||
|
||||
// Test that the lookup table is correct during runtime
|
||||
for (int i = 0; i < SINE_TABLE.length; i++) {
|
||||
float expected = SINE_TABLE[i];
|
||||
float value = lookup(i);
|
||||
|
||||
if (expected != value) {
|
||||
throw new IllegalArgumentException(String.format("LUT error at index %d (expected: %s, found: %s)", i, expected, value));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// [VanillaCopy] MathHelper#sin(float)
|
||||
public static float sin(float f) {
|
||||
return lookup((int) (f * 10430.378f) & 0xFFFF);
|
||||
}
|
||||
|
||||
// [VanillaCopy] MathHelper#cos(float)
|
||||
public static float cos(float f) {
|
||||
return lookup((int) (f * 10430.378f + 16384.0f) & 0xFFFF);
|
||||
}
|
||||
|
||||
private static float lookup(int index) {
|
||||
// A special case... Is there some way to eliminate this?
|
||||
if (index == 32768) {
|
||||
return SINE_TABLE_MIDPOINT;
|
||||
}
|
||||
|
||||
// Trigonometric identity: sin(-x) = -sin(x)
|
||||
// Given a domain of 0 <= x <= 2*pi, just negate the value if x > pi.
|
||||
// This allows the sin table size to be halved.
|
||||
int neg = (index & 0x8000) << 16;
|
||||
|
||||
// All bits set if (pi/2 <= x), none set otherwise
|
||||
// Extracts the 15th bit from 'half'
|
||||
int mask = (index << 17) >> 31;
|
||||
|
||||
// Trigonometric identity: sin(x) = sin(pi/2 - x)
|
||||
int pos = (0x8001 & mask) + (index ^ mask);
|
||||
|
||||
// Wrap the position in the table. Moving this down to immediately before the array access
|
||||
// seems to help the Hotspot compiler optimize the bit math better.
|
||||
pos &= 0x7fff;
|
||||
|
||||
// Fetch the corresponding value from the LUT and invert the sign bit as needed
|
||||
// This directly manipulate the sign bit on the float bits to simplify logic
|
||||
return Float.intBitsToFloat(SINE_TABLE_INT[pos] ^ neg);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,372 @@
|
||||
package org.bxteam.divinemc;
|
||||
|
||||
import com.google.common.base.Throwables;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import net.minecraft.world.level.block.Block;
|
||||
import net.minecraft.world.level.block.state.BlockBehaviour;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.configuration.ConfigurationSection;
|
||||
import org.bukkit.configuration.InvalidConfigurationException;
|
||||
import org.bukkit.configuration.file.YamlConfiguration;
|
||||
import org.bxteam.divinemc.server.chunk.ChunkSystemAlgorithms;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.logging.Level;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@NullMarked
|
||||
public final class DivineConfig {
|
||||
private DivineConfig() {
|
||||
throw new IllegalStateException("Utility class");
|
||||
}
|
||||
|
||||
private static final String HEADER = "This is the main configuration file for DivineMC.\n"
|
||||
+ "If you need help with the configuration or have any questions related to DivineMC,\n"
|
||||
+ "join us in our Discord server.\n"
|
||||
+ "\n"
|
||||
+ "Discord: https://discord.gg/p7cxhw7E2M \n"
|
||||
+ "Docs: https://bxteam.org/docs/divinemc \n"
|
||||
+ "New builds: https://github.com/BX-Team/DivineMC/releases/latest";
|
||||
private static File configFile;
|
||||
public static YamlConfiguration config;
|
||||
|
||||
private static Map<String, Command> commands;
|
||||
|
||||
public static int version;
|
||||
static boolean verbose;
|
||||
|
||||
public static void init(File configFile) {
|
||||
DivineConfig.configFile = configFile;
|
||||
config = new YamlConfiguration();
|
||||
try {
|
||||
config.load(configFile);
|
||||
} catch (IOException ignored) {
|
||||
} catch (InvalidConfigurationException ex) {
|
||||
Bukkit.getLogger().log(Level.SEVERE, "Could not load divinemc.yml, please correct your syntax errors", ex);
|
||||
throw Throwables.propagate(ex);
|
||||
}
|
||||
config.options().header(HEADER);
|
||||
config.options().copyDefaults(true);
|
||||
verbose = getBoolean(config, "verbose", false);
|
||||
|
||||
version = getInt(config, "config-version", 5);
|
||||
set(config, "config-version", 5);
|
||||
|
||||
readConfig(DivineConfig.class, null);
|
||||
}
|
||||
|
||||
public static void log(String s) {
|
||||
if (verbose) {
|
||||
log(Level.INFO, s);
|
||||
}
|
||||
}
|
||||
|
||||
public static void log(Level level, String s) {
|
||||
Bukkit.getLogger().log(level, s);
|
||||
}
|
||||
|
||||
static void readConfig(Class<?> clazz, @Nullable Object instance) {
|
||||
readConfig(configFile, config, clazz, instance);
|
||||
}
|
||||
|
||||
public static void readConfig(File configFile, YamlConfiguration config, Class<?> clazz, @Nullable Object instance) {
|
||||
for (Method method : clazz.getDeclaredMethods()) {
|
||||
if (!Modifier.isPrivate(method.getModifiers())) continue;
|
||||
if (method.getParameterTypes().length != 0) continue;
|
||||
if (method.getReturnType() != Void.TYPE) continue;
|
||||
|
||||
try {
|
||||
method.setAccessible(true);
|
||||
method.invoke(instance);
|
||||
} catch (InvocationTargetException ex) {
|
||||
throw Throwables.propagate(ex.getCause());
|
||||
} catch (Exception ex) {
|
||||
Bukkit.getLogger().log(Level.SEVERE, "Error invoking " + method, ex);
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
config.save(configFile);
|
||||
} catch (IOException ex) {
|
||||
Bukkit.getLogger().log(Level.SEVERE, "Could not save " + configFile, ex);
|
||||
}
|
||||
}
|
||||
|
||||
private static void set(YamlConfiguration config, String path, Object val, String... comments) {
|
||||
config.addDefault(path, val);
|
||||
config.set(path, val);
|
||||
if (comments.length > 0) {
|
||||
config.setComments(path, List.of(comments));
|
||||
}
|
||||
}
|
||||
|
||||
private static String getString(YamlConfiguration config, String path, String def, String... comments) {
|
||||
config.addDefault(path, def);
|
||||
if (comments.length > 0) {
|
||||
config.setComments(path, List.of(comments));
|
||||
}
|
||||
return config.getString(path, config.getString(path));
|
||||
}
|
||||
|
||||
private static boolean getBoolean(YamlConfiguration config, String path, boolean def, String... comments) {
|
||||
config.addDefault(path, def);
|
||||
if (comments.length > 0) {
|
||||
config.setComments(path, List.of(comments));
|
||||
}
|
||||
return config.getBoolean(path, config.getBoolean(path));
|
||||
}
|
||||
|
||||
private static double getDouble(YamlConfiguration config, String path, double def, String... comments) {
|
||||
config.addDefault(path, def);
|
||||
if (comments.length > 0) {
|
||||
config.setComments(path, List.of(comments));
|
||||
}
|
||||
return config.getDouble(path, config.getDouble(path));
|
||||
}
|
||||
|
||||
private static int getInt(YamlConfiguration config, String path, int def, String... comments) {
|
||||
config.addDefault(path, def);
|
||||
if (comments.length > 0) {
|
||||
config.setComments(path, List.of(comments));
|
||||
}
|
||||
return config.getInt(path, config.getInt(path));
|
||||
}
|
||||
|
||||
private static long getLong(YamlConfiguration config, String path, long def, String... comments) {
|
||||
config.addDefault(path, def);
|
||||
if (comments.length > 0) {
|
||||
config.setComments(path, List.of(comments));
|
||||
}
|
||||
return config.getLong(path, config.getLong(path));
|
||||
}
|
||||
|
||||
private static <T> List<T> getList(YamlConfiguration config, String path, List<T> def, String... comments) {
|
||||
config.addDefault(path, def);
|
||||
if (comments.length > 0) {
|
||||
config.setComments(path, List.of(comments));
|
||||
}
|
||||
return (List<T>) config.getList(path, def);
|
||||
}
|
||||
|
||||
static Map<String, Object> getMap(YamlConfiguration config, String path, Map<String, Object> def, String... comments) {
|
||||
if (def != null && config.getConfigurationSection(path) == null) {
|
||||
config.addDefault(path, def);
|
||||
return def;
|
||||
}
|
||||
if (comments.length > 0) {
|
||||
config.setComments(path, List.of(comments));
|
||||
}
|
||||
return toMap(config.getConfigurationSection(path));
|
||||
}
|
||||
|
||||
private static Map<String, Object> toMap(ConfigurationSection section) {
|
||||
ImmutableMap.Builder<String, Object> builder = ImmutableMap.builder();
|
||||
if (section != null) {
|
||||
for (String key : section.getKeys(false)) {
|
||||
Object obj = section.get(key);
|
||||
if (obj != null) {
|
||||
builder.put(key, obj instanceof ConfigurationSection val ? toMap(val) : obj);
|
||||
}
|
||||
}
|
||||
}
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
public static int parallelThreadCount = 4;
|
||||
public static boolean logContainerCreationStacktraces = false;
|
||||
private static void parallelWorldTicking() {
|
||||
parallelThreadCount = getInt(config, "settings.parallel-world-ticking.thread-count", parallelThreadCount);
|
||||
logContainerCreationStacktraces = getBoolean(config, "settings.parallel-world-ticking.log-container-creation-stacktraces", logContainerCreationStacktraces);
|
||||
}
|
||||
|
||||
public static boolean nativeAccelerationEnabled = true;
|
||||
public static boolean allowAVX512 = false;
|
||||
public static int isaTargetLevelOverride = -1;
|
||||
public static long chunkDataCacheSoftLimit = 8192L;
|
||||
public static long chunkDataCacheLimit = 32678L;
|
||||
public static int maxViewDistance = 32;
|
||||
public static ChunkSystemAlgorithms chunkWorkerAlgorithm = ChunkSystemAlgorithms.C2ME;
|
||||
public static boolean enableSecureSeed = false;
|
||||
public static boolean enableDensityFunctionCompiler = false;
|
||||
public static boolean enableStructureLayoutOptimizer = true;
|
||||
public static boolean deduplicateShuffledTemplatePoolElementList = false;
|
||||
|
||||
private static void chunkGeneration() {
|
||||
nativeAccelerationEnabled = getBoolean(config, "settings.chunk-generation.native-acceleration-enabled", nativeAccelerationEnabled);
|
||||
|
||||
allowAVX512 = getBoolean(config, "settings.chunk-generation.allow-avx512", allowAVX512,
|
||||
"Enables AVX512 support for natives-math optimizations",
|
||||
"",
|
||||
"Read more about AVX512: https://en.wikipedia.org/wiki/AVX-512");
|
||||
isaTargetLevelOverride = getInt(config, "settings.chunk-generation.isa-target-level-override", isaTargetLevelOverride,
|
||||
"Overrides the ISA target located by the native loader, which allows forcing AVX512 (must be a value between 6-9 for AVX512 support).",
|
||||
"Value must be between 1-9, and -1 to disable override");
|
||||
|
||||
if (isaTargetLevelOverride < -1 || isaTargetLevelOverride > 9) {
|
||||
log(Level.WARNING, "Invalid ISA target level override: " + isaTargetLevelOverride + ", resetting to -1");
|
||||
isaTargetLevelOverride = -1;
|
||||
}
|
||||
|
||||
chunkDataCacheSoftLimit = getLong(config, "settings.chunk-generation.chunk-data-cache-soft-limit", chunkDataCacheSoftLimit);
|
||||
chunkDataCacheLimit = getLong(config, "settings.chunk-generation.chunk-data-cache-limit", chunkDataCacheLimit);
|
||||
maxViewDistance = getInt(config, "settings.chunk-generation.max-view-distance", maxViewDistance,
|
||||
"Changes the maximum view distance for the server, allowing clients to have render distances higher than 32");
|
||||
|
||||
chunkWorkerAlgorithm = ChunkSystemAlgorithms.valueOf(getString(config, "settings.chunk-generation.chunk-worker-algorithm", chunkWorkerAlgorithm.name(),
|
||||
"Modifies what algorithm the chunk system will use to define thread counts. values: MOONRISE, C2ME, C2ME_AGGRESSIVE"));
|
||||
|
||||
enableSecureSeed = getBoolean(config, "settings.misc.enable-secure-seed", enableSecureSeed,
|
||||
"This feature is based on Secure Seed mod by Earthcomputer.",
|
||||
"",
|
||||
"Terrain and biome generation remains the same, but all the ores and structures are generated with 1024-bit seed, instead of the usual 64-bit seed.",
|
||||
"This seed is almost impossible to crack, and there are no weird links between structures.");
|
||||
|
||||
enableDensityFunctionCompiler = getBoolean(config, "settings.chunk-generation.experimental.enable-density-function-compiler", enableDensityFunctionCompiler,
|
||||
"Whether to use density function compiler to accelerate world generation",
|
||||
"",
|
||||
"Density function: https://minecraft.wiki/w/Density_function",
|
||||
"",
|
||||
"This functionality compiles density functions from world generation",
|
||||
"datapacks (including vanilla generation) to JVM bytecode to increase",
|
||||
"performance by allowing JVM JIT to better optimize the code.",
|
||||
"All functions provided by vanilla are implemented.",
|
||||
"",
|
||||
"Please test if this optimization actually benefits your server, as",
|
||||
"it can sometimes slow down chunk performance than speed it up.");
|
||||
|
||||
enableStructureLayoutOptimizer = getBoolean(config, "settings.chunk-generation.experimental.enable-structure-layout-optimizer", enableStructureLayoutOptimizer,
|
||||
"Enables a port of the mod StructureLayoutOptimizer, which optimizes general Jigsaw structure generation");
|
||||
deduplicateShuffledTemplatePoolElementList = getBoolean(config, "settings.chunk-generation.experimental.deduplicate-shuffled-template-pool-element-list", deduplicateShuffledTemplatePoolElementList,
|
||||
"Whether to use an alternative strategy to make structure layouts generate slightly even faster than",
|
||||
"the default optimization this mod has for template pool weights. This alternative strategy works by",
|
||||
"changing the list of pieces that structures collect from the template pool to not have duplicate entries.",
|
||||
"",
|
||||
"This will not break the structure generation, but it will make the structure layout different than",
|
||||
"if this config was off (breaking vanilla seed parity). The cost of speed may be worth it in large",
|
||||
"modpacks where many structure mods are using very high weight values in their template pools.");
|
||||
}
|
||||
|
||||
public static boolean skipUselessSecondaryPoiSensor = true;
|
||||
public static boolean clumpOrbs = true;
|
||||
public static boolean ignoreMovedTooQuicklyWhenLagging = true;
|
||||
public static boolean alwaysAllowWeirdMovement = true;
|
||||
|
||||
private static void miscSettings() {
|
||||
skipUselessSecondaryPoiSensor = getBoolean(config, "settings.misc.skip-useless-secondary-poi-sensor", skipUselessSecondaryPoiSensor);
|
||||
clumpOrbs = getBoolean(config, "settings.misc.clump-orbs", clumpOrbs,
|
||||
"Clumps experience orbs together to reduce entity count");
|
||||
ignoreMovedTooQuicklyWhenLagging = getBoolean(config, "settings.misc.ignore-moved-too-quickly-when-lagging", ignoreMovedTooQuicklyWhenLagging,
|
||||
"Improves general gameplay experience of the player when the server is lagging, as they won't get lagged back (message 'moved too quickly')");
|
||||
alwaysAllowWeirdMovement = getBoolean(config, "settings.misc.always-allow-weird-movement", alwaysAllowWeirdMovement,
|
||||
"Means ignoring messages like 'moved too quickly' and 'moved wrongly'");
|
||||
}
|
||||
|
||||
public static boolean enableFasterTntOptimization = true;
|
||||
public static boolean explosionNoBlockDamage = false;
|
||||
public static double tntRandomRange = -1;
|
||||
|
||||
private static void tntOptimization() {
|
||||
enableFasterTntOptimization = getBoolean(config, "settings.tnt-optimization.enable-faster-tnt-optimization", enableFasterTntOptimization);
|
||||
explosionNoBlockDamage = getBoolean(config, "settings.tnt-optimization.explosion-no-block-damage", explosionNoBlockDamage);
|
||||
tntRandomRange = getDouble(config, "settings.tnt-optimization.tnt-random-range", tntRandomRange);
|
||||
}
|
||||
|
||||
public static boolean lagCompensationEnabled = true;
|
||||
public static boolean blockEntityAcceleration = false;
|
||||
public static boolean blockBreakingAcceleration = true;
|
||||
public static boolean eatingAcceleration = true;
|
||||
public static boolean potionEffectAcceleration = true;
|
||||
public static boolean fluidAcceleration = true;
|
||||
public static boolean pickupAcceleration = true;
|
||||
public static boolean portalAcceleration = true;
|
||||
public static boolean timeAcceleration = true;
|
||||
public static boolean randomTickSpeedAcceleration = true;
|
||||
|
||||
private static void lagCompensation() {
|
||||
lagCompensationEnabled = getBoolean(config, "settings.lag-compensation.enabled", lagCompensationEnabled, "Improves the player experience when TPS is low");
|
||||
blockEntityAcceleration = getBoolean(config, "settings.lag-compensation.block-entity-acceleration", blockEntityAcceleration);
|
||||
blockBreakingAcceleration = getBoolean(config, "settings.lag-compensation.block-breaking-acceleration", blockBreakingAcceleration);
|
||||
eatingAcceleration = getBoolean(config, "settings.lag-compensation.eating-acceleration", eatingAcceleration);
|
||||
potionEffectAcceleration = getBoolean(config, "settings.lag-compensation.potion-effect-acceleration", potionEffectAcceleration);
|
||||
fluidAcceleration = getBoolean(config, "settings.lag-compensation.fluid-acceleration", fluidAcceleration);
|
||||
pickupAcceleration = getBoolean(config, "settings.lag-compensation.pickup-acceleration", pickupAcceleration);
|
||||
portalAcceleration = getBoolean(config, "settings.lag-compensation.portal-acceleration", portalAcceleration);
|
||||
timeAcceleration = getBoolean(config, "settings.lag-compensation.time-acceleration", timeAcceleration);
|
||||
randomTickSpeedAcceleration = getBoolean(config, "settings.lag-compensation.random-tick-speed-acceleration", randomTickSpeedAcceleration);
|
||||
}
|
||||
|
||||
public static boolean noChatReportsEnabled = false;
|
||||
public static boolean noChatReportsAddQueryData = true;
|
||||
public static boolean noChatReportsConvertToGameMessage = true;
|
||||
public static boolean noChatReportsDebugLog = false;
|
||||
public static boolean noChatReportsDemandOnClient = false;
|
||||
public static String noChatReportsDisconnectDemandOnClientMessage = "You do not have No Chat Reports, and this server is configured to require it on client!";
|
||||
|
||||
private static void noChatReports() {
|
||||
noChatReportsEnabled = getBoolean(config, "settings.no-chat-reports.enabled", noChatReportsEnabled,
|
||||
"Enables or disables the No Chat Reports feature");
|
||||
noChatReportsAddQueryData = getBoolean(config, "settings.no-chat-reports.add-query-data", noChatReportsAddQueryData,
|
||||
"Should server include extra query data to help clients know that your server is secure");
|
||||
noChatReportsConvertToGameMessage = getBoolean(config, "settings.no-chat-reports.convert-to-game-message", noChatReportsConvertToGameMessage,
|
||||
"Should the server convert all player messages to system messages");
|
||||
noChatReportsDebugLog = getBoolean(config, "settings.no-chat-reports.debug-log", noChatReportsDebugLog);
|
||||
noChatReportsDemandOnClient = getBoolean(config, "settings.no-chat-reports.demand-on-client", noChatReportsDemandOnClient,
|
||||
"Should the server require No Chat Reports on the client side");
|
||||
noChatReportsDisconnectDemandOnClientMessage = getString(config, "settings.no-chat-reports.disconnect-demand-on-client-message", noChatReportsDisconnectDemandOnClientMessage,
|
||||
"Message to send to the client when they are disconnected for not having No Chat Reports");
|
||||
}
|
||||
|
||||
public static boolean asyncPathfinding = true;
|
||||
public static int asyncPathfindingMaxThreads = 2;
|
||||
public static int asyncPathfindingKeepalive = 60;
|
||||
|
||||
private static void asyncPathfinding() {
|
||||
asyncPathfinding = getBoolean(config, "settings.async-pathfinding.enable", asyncPathfinding);
|
||||
asyncPathfindingMaxThreads = getInt(config, "settings.async-pathfinding.max-threads", asyncPathfindingMaxThreads);
|
||||
asyncPathfindingKeepalive = getInt(config, "settings.async-pathfinding.keepalive", asyncPathfindingKeepalive);
|
||||
|
||||
if (asyncPathfindingMaxThreads < 0) {
|
||||
asyncPathfindingMaxThreads = Math.max(Runtime.getRuntime().availableProcessors() + asyncPathfindingMaxThreads, 1);
|
||||
} else if (asyncPathfindingMaxThreads == 0) {
|
||||
asyncPathfindingMaxThreads = Math.max(Runtime.getRuntime().availableProcessors() / 4, 1);
|
||||
}
|
||||
|
||||
if (!asyncPathfinding) {
|
||||
asyncPathfindingMaxThreads = 0;
|
||||
} else {
|
||||
Bukkit.getLogger().log(Level.INFO, "Using " + asyncPathfindingMaxThreads + " threads for Async Pathfinding");
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean multithreadedEnabled = true;
|
||||
public static boolean multithreadedCompatModeEnabled = false;
|
||||
public static int asyncEntityTrackerMaxThreads = 1;
|
||||
public static int asyncEntityTrackerKeepalive = 60;
|
||||
|
||||
private static void multithreadedTracker() {
|
||||
multithreadedEnabled = getBoolean(config, "settings.multithreaded-tracker.enable", multithreadedEnabled);
|
||||
multithreadedCompatModeEnabled = getBoolean(config, "settings.multithreaded-tracker.compat-mode", multithreadedCompatModeEnabled);
|
||||
asyncEntityTrackerMaxThreads = getInt(config, "settings.multithreaded-tracker.max-threads", asyncEntityTrackerMaxThreads);
|
||||
asyncEntityTrackerKeepalive = getInt(config, "settings.multithreaded-tracker.keepalive", asyncEntityTrackerKeepalive);
|
||||
|
||||
if (asyncEntityTrackerMaxThreads < 0) {
|
||||
asyncEntityTrackerMaxThreads = Math.max(Runtime.getRuntime().availableProcessors() + asyncEntityTrackerMaxThreads, 1);
|
||||
} else if (asyncEntityTrackerMaxThreads == 0) {
|
||||
asyncEntityTrackerMaxThreads = Math.max(Runtime.getRuntime().availableProcessors() / 4, 1);
|
||||
}
|
||||
|
||||
if (!multithreadedEnabled) {
|
||||
asyncEntityTrackerMaxThreads = 0;
|
||||
} else {
|
||||
Bukkit.getLogger().log(Level.INFO, "Using " + asyncEntityTrackerMaxThreads + " threads for Async Entity Tracker");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
package org.bxteam.divinemc;
|
||||
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.configuration.ConfigurationSection;
|
||||
import org.bukkit.configuration.file.YamlConfiguration;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.bxteam.divinemc.DivineConfig.log;
|
||||
|
||||
@NullMarked
|
||||
public final class DivineWorldConfig {
|
||||
private final YamlConfiguration config;
|
||||
private final String worldName;
|
||||
private final World.Environment environment;
|
||||
|
||||
public DivineWorldConfig(String worldName, World.Environment environment) {
|
||||
this.config = DivineConfig.config;
|
||||
this.worldName = worldName;
|
||||
this.environment = environment;
|
||||
init();
|
||||
}
|
||||
|
||||
public void init() {
|
||||
log("-------- World Settings For [" + worldName + "] --------");
|
||||
DivineConfig.readConfig(DivineWorldConfig.class, this);
|
||||
}
|
||||
|
||||
private void set(String path, Object val) {
|
||||
this.config.addDefault("world-settings.default." + path, val);
|
||||
this.config.set("world-settings.default." + path, val);
|
||||
if (this.config.get("world-settings." + worldName + "." + path) != null) {
|
||||
this.config.addDefault("world-settings." + worldName + "." + path, val);
|
||||
this.config.set("world-settings." + worldName + "." + path, val);
|
||||
}
|
||||
}
|
||||
|
||||
private ConfigurationSection getConfigurationSection(String path) {
|
||||
ConfigurationSection section = this.config.getConfigurationSection("world-settings." + worldName + "." + path);
|
||||
return section != null ? section : this.config.getConfigurationSection("world-settings.default." + path);
|
||||
}
|
||||
|
||||
private String getString(String path, String def) {
|
||||
this.config.addDefault("world-settings.default." + path, def);
|
||||
return this.config.getString("world-settings." + worldName + "." + path, this.config.getString("world-settings.default." + path));
|
||||
}
|
||||
|
||||
private boolean getBoolean(String path, boolean def) {
|
||||
this.config.addDefault("world-settings.default." + path, def);
|
||||
return this.config.getBoolean("world-settings." + worldName + "." + path, this.config.getBoolean("world-settings.default." + path));
|
||||
}
|
||||
|
||||
private double getDouble(String path, double def) {
|
||||
this.config.addDefault("world-settings.default." + path, def);
|
||||
return this.config.getDouble("world-settings." + worldName + "." + path, this.config.getDouble("world-settings.default." + path));
|
||||
}
|
||||
|
||||
private int getInt(String path, int def) {
|
||||
this.config.addDefault("world-settings.default." + path, def);
|
||||
return this.config.getInt("world-settings." + worldName + "." + path, this.config.getInt("world-settings.default." + path));
|
||||
}
|
||||
|
||||
private <T> List<?> getList(String path, T def) {
|
||||
this.config.addDefault("world-settings.default." + path, def);
|
||||
return this.config.getList("world-settings." + worldName + "." + path, this.config.getList("world-settings.default." + path));
|
||||
}
|
||||
|
||||
private Map<String, Object> getMap(String path, Map<String, Object> def) {
|
||||
final Map<String, Object> fallback = this.getMap("world-settings.default." + path, def);
|
||||
final Map<String, Object> value = this.getMap("world-settings." + worldName + "." + path, null);
|
||||
return value.isEmpty() ? fallback : value;
|
||||
}
|
||||
|
||||
public boolean snowballCanKnockback = true;
|
||||
public boolean eggCanKnockback = true;
|
||||
private void setSnowballAndEggKnockback() {
|
||||
snowballCanKnockback = getBoolean("gameplay-mechanics.projectiles.snowball.knockback", snowballCanKnockback);
|
||||
eggCanKnockback = getBoolean("gameplay-mechanics.projectiles.egg.knockback", eggCanKnockback);
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,6 @@ package org.bxteam.divinemc.command;
|
||||
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.craftbukkit.util.permissions.CraftDefaultPermissions;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.checkerframework.framework.qual.DefaultQualifier;
|
||||
|
||||
@@ -11,7 +10,7 @@ import java.util.Map;
|
||||
|
||||
@DefaultQualifier(NonNull.class)
|
||||
public final class DivineCommands {
|
||||
public static final String COMMAND_BASE_PERM = CraftDefaultPermissions.DIVINEMC_ROOT + ".command";
|
||||
public static final String COMMAND_BASE_PERM = "divinemc.command";
|
||||
|
||||
private DivineCommands() {}
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ import org.bukkit.craftbukkit.CraftServer;
|
||||
import org.bukkit.permissions.PermissionDefault;
|
||||
import org.bxteam.divinemc.command.DivineCommand;
|
||||
import org.bxteam.divinemc.command.DivineSubCommandPermission;
|
||||
import org.bxteam.divinemc.configuration.DivineConfig;
|
||||
import org.bxteam.divinemc.DivineConfig;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
@@ -37,7 +37,7 @@ public final class ReloadCommand extends DivineSubCommandPermission {
|
||||
MinecraftServer server = ((CraftServer) sender.getServer()).getServer();
|
||||
DivineConfig.init((File) server.options.valueOf("divinemc-settings"));
|
||||
for (ServerLevel level : server.getAllLevels()) {
|
||||
level.divinemcConfig.init();
|
||||
level.divineConfig.init();
|
||||
level.resetBreedingCooldowns();
|
||||
}
|
||||
server.server.reloadCount++;
|
||||
|
||||
@@ -1,218 +0,0 @@
|
||||
package org.bxteam.divinemc.configuration;
|
||||
|
||||
import com.google.common.base.Throwables;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import net.minecraft.world.level.block.Block;
|
||||
import net.minecraft.world.level.block.state.BlockBehaviour;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.configuration.ConfigurationSection;
|
||||
import org.bukkit.configuration.InvalidConfigurationException;
|
||||
import org.bukkit.configuration.file.YamlConfiguration;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.logging.Level;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public class DivineConfig {
|
||||
private static final String HEADER = "This is the main configuration file for DivineMC.\n"
|
||||
+ "If you need help with the configuration or have any questions related to DivineMC,\n"
|
||||
+ "join us in our Discord server.\n"
|
||||
+ "\n"
|
||||
+ "Discord: https://discord.gg/p7cxhw7E2M \n"
|
||||
+ "Docs: https://bxteam.org/docs/divinemc \n"
|
||||
+ "New builds: https://github.com/BX-Team/DivineMC/releases/latest";
|
||||
private static File CONFIG_FILE;
|
||||
public static YamlConfiguration config;
|
||||
|
||||
private static Map<String, Command> commands;
|
||||
|
||||
public static int version;
|
||||
static boolean verbose;
|
||||
|
||||
public static void init(File configFile) {
|
||||
CONFIG_FILE = configFile;
|
||||
config = new YamlConfiguration();
|
||||
try {
|
||||
config.load(CONFIG_FILE);
|
||||
} catch (IOException ignore) {
|
||||
} catch (InvalidConfigurationException ex) {
|
||||
Bukkit.getLogger().log(Level.SEVERE, "Could not load divinemc.yml, please correct your syntax errors", ex);
|
||||
throw Throwables.propagate(ex);
|
||||
}
|
||||
config.options().header(HEADER);
|
||||
config.options().copyDefaults(true);
|
||||
verbose = getBoolean("verbose", false);
|
||||
|
||||
version = getInt("config-version", 4);
|
||||
set("config-version", 4);
|
||||
|
||||
readConfig(DivineConfig.class, null);
|
||||
|
||||
Block.BLOCK_STATE_REGISTRY.forEach(BlockBehaviour.BlockStateBase::initCache);
|
||||
}
|
||||
|
||||
protected static void log(String s) {
|
||||
if (verbose) {
|
||||
log(Level.INFO, s);
|
||||
}
|
||||
}
|
||||
|
||||
protected static void log(Level level, String s) {
|
||||
Bukkit.getLogger().log(level, s);
|
||||
}
|
||||
|
||||
static void readConfig(Class<?> clazz, Object instance) {
|
||||
for (Method method : clazz.getDeclaredMethods()) {
|
||||
if (Modifier.isPrivate(method.getModifiers())) {
|
||||
if (method.getParameterTypes().length == 0 && method.getReturnType() == Void.TYPE) {
|
||||
try {
|
||||
method.setAccessible(true);
|
||||
method.invoke(instance);
|
||||
} catch (InvocationTargetException ex) {
|
||||
throw Throwables.propagate(ex.getCause());
|
||||
} catch (Exception ex) {
|
||||
Bukkit.getLogger().log(Level.SEVERE, "Error invoking " + method, ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
config.save(CONFIG_FILE);
|
||||
} catch (IOException ex) {
|
||||
Bukkit.getLogger().log(Level.SEVERE, "Could not save " + CONFIG_FILE, ex);
|
||||
}
|
||||
}
|
||||
|
||||
private static void set(String path, Object val) {
|
||||
config.addDefault(path, val);
|
||||
config.set(path, val);
|
||||
}
|
||||
|
||||
private static String getString(String path, String def) {
|
||||
config.addDefault(path, def);
|
||||
return config.getString(path, config.getString(path));
|
||||
}
|
||||
|
||||
private static boolean getBoolean(String path, boolean def) {
|
||||
config.addDefault(path, def);
|
||||
return config.getBoolean(path, config.getBoolean(path));
|
||||
}
|
||||
|
||||
private static double getDouble(String path, double def) {
|
||||
config.addDefault(path, def);
|
||||
return config.getDouble(path, config.getDouble(path));
|
||||
}
|
||||
|
||||
private static int getInt(String path, int def) {
|
||||
config.addDefault(path, def);
|
||||
return config.getInt(path, config.getInt(path));
|
||||
}
|
||||
|
||||
private static <T> List getList(String path, T def) {
|
||||
config.addDefault(path, def);
|
||||
return config.getList(path, config.getList(path));
|
||||
}
|
||||
|
||||
static Map<String, Object> getMap(String path, Map<String, Object> def) {
|
||||
if (def != null && config.getConfigurationSection(path) == null) {
|
||||
config.addDefault(path, def);
|
||||
return def;
|
||||
}
|
||||
return toMap(config.getConfigurationSection(path));
|
||||
}
|
||||
|
||||
private static Map<String, Object> toMap(ConfigurationSection section) {
|
||||
ImmutableMap.Builder<String, Object> builder = ImmutableMap.builder();
|
||||
if (section != null) {
|
||||
for (String key : section.getKeys(false)) {
|
||||
Object obj = section.get(key);
|
||||
if (obj != null) {
|
||||
builder.put(key, obj instanceof ConfigurationSection val ? toMap(val) : obj);
|
||||
}
|
||||
}
|
||||
}
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
public static boolean noChatSign = true;
|
||||
private static void chatMessageSignatures() {
|
||||
noChatSign = getBoolean("settings.no-chat-sign", noChatSign);
|
||||
}
|
||||
|
||||
public static boolean optimizedDragonRespawn = true;
|
||||
public static boolean lagCompensationEnabled = false;
|
||||
public static boolean lagCompensationEnableForWater = false;
|
||||
public static boolean lagCompensationEnableForLava = false;
|
||||
private static void optimizations() {
|
||||
optimizedDragonRespawn = getBoolean("settings.optimizations.optimized-dragon-respawn", optimizedDragonRespawn);
|
||||
lagCompensationEnabled = getBoolean("settings.optimizations.lag-compensation.enabled", lagCompensationEnabled);
|
||||
lagCompensationEnableForWater = getBoolean("settings.optimizations.lag-compensation.enable-for-water", lagCompensationEnableForWater);
|
||||
lagCompensationEnableForLava = getBoolean("settings.optimizations.lag-compensation.enable-for-lava", lagCompensationEnableForLava);
|
||||
}
|
||||
|
||||
public static boolean disableNonEditableSignWarning = true;
|
||||
public static boolean removeVanillaUsernameCheck = false;
|
||||
public static boolean disableMovedWronglyThreshold = false;
|
||||
public static boolean enableSecureSeed = false;
|
||||
public static boolean asyncPlayerDataSaveEnabled = false;
|
||||
private static void miscSettings() {
|
||||
disableNonEditableSignWarning = getBoolean("settings.misc.disable-non-editable-sign-warning", disableNonEditableSignWarning);
|
||||
removeVanillaUsernameCheck = getBoolean("settings.misc.remove-vanilla-username-check", removeVanillaUsernameCheck);
|
||||
disableMovedWronglyThreshold = getBoolean("settings.misc.disable-moved-wrongly-threshold", disableMovedWronglyThreshold);
|
||||
enableSecureSeed = getBoolean("settings.misc.enable-secure-seed", enableSecureSeed);
|
||||
asyncPlayerDataSaveEnabled = getBoolean("settings.misc.experimental.async-player-data-save-enabled", asyncPlayerDataSaveEnabled);
|
||||
}
|
||||
|
||||
public static int linearFlushFrequency = 5;
|
||||
public static boolean throwOnUnknownExtension = false;
|
||||
private static void linearSettings() {
|
||||
linearFlushFrequency = getInt("settings.region-format.linear.flush-frequency", linearFlushFrequency);
|
||||
throwOnUnknownExtension = getBoolean("settings.region-format.linear.throw-on-unknown-extension", throwOnUnknownExtension);
|
||||
}
|
||||
|
||||
public static boolean asyncPathfinding = true;
|
||||
public static int asyncPathfindingMaxThreads = 0;
|
||||
public static int asyncPathfindingKeepalive = 60;
|
||||
private static void asyncPathfinding() {
|
||||
asyncPathfinding = getBoolean("settings.async-pathfinding.enable", asyncPathfinding);
|
||||
asyncPathfindingMaxThreads = getInt("settings.async-pathfinding.max-threads", asyncPathfindingMaxThreads);
|
||||
asyncPathfindingKeepalive = getInt("settings.async-pathfinding.keepalive", asyncPathfindingKeepalive);
|
||||
if (asyncPathfindingMaxThreads < 0)
|
||||
asyncPathfindingMaxThreads = Math.max(Runtime.getRuntime().availableProcessors() + asyncPathfindingMaxThreads, 1);
|
||||
else if (asyncPathfindingMaxThreads == 0)
|
||||
asyncPathfindingMaxThreads = Math.max(Runtime.getRuntime().availableProcessors() / 4, 1);
|
||||
if (!asyncPathfinding)
|
||||
asyncPathfindingMaxThreads = 0;
|
||||
else
|
||||
Bukkit.getLogger().log(Level.INFO, "Using " + asyncPathfindingMaxThreads + " threads for Async Pathfinding");
|
||||
}
|
||||
|
||||
public static boolean multithreadedEnabled = false;
|
||||
public static boolean multithreadedCompatModeEnabled = false;
|
||||
public static int asyncEntityTrackerMaxThreads = 0;
|
||||
public static int asyncEntityTrackerKeepalive = 60;
|
||||
private static void multithreadedTracker() {
|
||||
multithreadedEnabled = getBoolean("settings.multithreaded-tracker.enable", multithreadedEnabled);
|
||||
multithreadedCompatModeEnabled = getBoolean("settings.multithreaded-tracker.compat-mode", multithreadedCompatModeEnabled);
|
||||
asyncEntityTrackerMaxThreads = getInt("settings.multithreaded-tracker.max-threads", asyncEntityTrackerMaxThreads);
|
||||
asyncEntityTrackerKeepalive = getInt("settings.multithreaded-tracker.keepalive", asyncEntityTrackerKeepalive);
|
||||
|
||||
if (asyncEntityTrackerMaxThreads < 0)
|
||||
asyncEntityTrackerMaxThreads = Math.max(Runtime.getRuntime().availableProcessors() + asyncEntityTrackerMaxThreads, 1);
|
||||
else if (asyncEntityTrackerMaxThreads == 0)
|
||||
asyncEntityTrackerMaxThreads = Math.max(Runtime.getRuntime().availableProcessors() / 4, 1);
|
||||
|
||||
if (!multithreadedEnabled)
|
||||
asyncEntityTrackerMaxThreads = 0;
|
||||
else
|
||||
Bukkit.getLogger().log(Level.INFO, "Using " + asyncEntityTrackerMaxThreads + " threads for Async Entity Tracker");
|
||||
}
|
||||
}
|
||||
@@ -1,121 +0,0 @@
|
||||
package org.bxteam.divinemc.configuration;
|
||||
|
||||
import org.apache.commons.lang.BooleanUtils;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.configuration.ConfigurationSection;
|
||||
import org.bxteam.divinemc.region.RegionFileFormat;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.logging.Level;
|
||||
|
||||
import static org.bxteam.divinemc.configuration.DivineConfig.log;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public class DivineWorldConfig {
|
||||
private final String worldName;
|
||||
private final World.Environment environment;
|
||||
|
||||
public DivineWorldConfig(String worldName, World.Environment environment) {
|
||||
this.worldName = worldName;
|
||||
this.environment = environment;
|
||||
init();
|
||||
}
|
||||
|
||||
public void init() {
|
||||
log("-------- World Settings For [" + worldName + "] --------");
|
||||
DivineConfig.readConfig(DivineWorldConfig.class, this);
|
||||
}
|
||||
|
||||
private void set(String path, Object val) {
|
||||
DivineConfig.config.addDefault("world-settings.default." + path, val);
|
||||
DivineConfig.config.set("world-settings.default." + path, val);
|
||||
if (DivineConfig.config.get("world-settings." + worldName + "." + path) != null) {
|
||||
DivineConfig.config.addDefault("world-settings." + worldName + "." + path, val);
|
||||
DivineConfig.config.set("world-settings." + worldName + "." + path, val);
|
||||
}
|
||||
}
|
||||
|
||||
private ConfigurationSection getConfigurationSection(String path) {
|
||||
ConfigurationSection section = DivineConfig.config.getConfigurationSection("world-settings." + worldName + "." + path);
|
||||
return section != null ? section : DivineConfig.config.getConfigurationSection("world-settings.default." + path);
|
||||
}
|
||||
|
||||
private String getString(String path, String def) {
|
||||
DivineConfig.config.addDefault("world-settings.default." + path, def);
|
||||
return DivineConfig.config.getString("world-settings." + worldName + "." + path, DivineConfig.config.getString("world-settings.default." + path));
|
||||
}
|
||||
|
||||
private boolean getBoolean(String path, boolean def) {
|
||||
DivineConfig.config.addDefault("world-settings.default." + path, def);
|
||||
return DivineConfig.config.getBoolean("world-settings." + worldName + "." + path, DivineConfig.config.getBoolean("world-settings.default." + path));
|
||||
}
|
||||
|
||||
private boolean getBoolean(String path, Predicate<Boolean> predicate) {
|
||||
String val = getString(path, "default").toLowerCase();
|
||||
Boolean bool = BooleanUtils.toBooleanObject(val, "true", "false", "default");
|
||||
return predicate.test(bool);
|
||||
}
|
||||
|
||||
private double getDouble(String path, double def) {
|
||||
DivineConfig.config.addDefault("world-settings.default." + path, def);
|
||||
return DivineConfig.config.getDouble("world-settings." + worldName + "." + path, DivineConfig.config.getDouble("world-settings.default." + path));
|
||||
}
|
||||
|
||||
private int getInt(String path, int def) {
|
||||
DivineConfig.config.addDefault("world-settings.default." + path, def);
|
||||
return DivineConfig.config.getInt("world-settings." + worldName + "." + path, DivineConfig.config.getInt("world-settings.default." + path));
|
||||
}
|
||||
|
||||
private <T> List<?> getList(String path, T def) {
|
||||
DivineConfig.config.addDefault("world-settings.default." + path, def);
|
||||
return DivineConfig.config.getList("world-settings." + worldName + "." + path, DivineConfig.config.getList("world-settings.default." + path));
|
||||
}
|
||||
|
||||
private Map<String, Object> getMap(String path, Map<String, Object> def) {
|
||||
final Map<String, Object> fallback = DivineConfig.getMap("world-settings.default." + path, def);
|
||||
final Map<String, Object> value = DivineConfig.getMap("world-settings." + worldName + "." + path, null);
|
||||
return value.isEmpty() ? fallback : value;
|
||||
}
|
||||
|
||||
public boolean despawnShulkerBulletsOnOwnerDeath = true;
|
||||
private void despawnShulkerBulletsOnOwnerDeath() {
|
||||
despawnShulkerBulletsOnOwnerDeath = getBoolean("gameplay-mechanics.mob.shulker.despawn-bullets-on-player-death", despawnShulkerBulletsOnOwnerDeath);
|
||||
}
|
||||
|
||||
public boolean saveFireworks = false;
|
||||
private void projectiles() {
|
||||
saveFireworks = getBoolean("gameplay-mechanics.should-save-fireworks", saveFireworks);
|
||||
}
|
||||
|
||||
public boolean suppressErrorsFromDirtyAttributes = true;
|
||||
private void suppressErrorsFromDirtyAttributes() {
|
||||
suppressErrorsFromDirtyAttributes = getBoolean("suppress-errors-from-dirty-attributes", suppressErrorsFromDirtyAttributes);
|
||||
}
|
||||
|
||||
public boolean snowballCanKnockback = true;
|
||||
public boolean eggCanKnockback = true;
|
||||
private void setSnowballAndEggKnockback() {
|
||||
snowballCanKnockback = getBoolean("gameplay-mechanics.projectiles.snowball.knockback", snowballCanKnockback);
|
||||
eggCanKnockback = getBoolean("gameplay-mechanics.projectiles.egg.knockback", eggCanKnockback);
|
||||
}
|
||||
|
||||
public RegionFileFormat regionFormatName = RegionFileFormat.MCA;
|
||||
public int linearCompressionLevel = 1;
|
||||
private void regionFormatSettings() {
|
||||
regionFormatName = RegionFileFormat.fromExtension(getString("region-format.format", regionFormatName.name()));
|
||||
if (regionFormatName.equals(RegionFileFormat.UNKNOWN)) {
|
||||
log(Level.SEVERE, "Unknown region file type!");
|
||||
log(Level.SEVERE, "Falling back to ANVIL region file format.");
|
||||
regionFormatName = RegionFileFormat.MCA;
|
||||
}
|
||||
|
||||
linearCompressionLevel = getInt("region-format.linear.compression-level", linearCompressionLevel);
|
||||
if (linearCompressionLevel > 23 || linearCompressionLevel < 1) {
|
||||
log(Level.SEVERE, "Linear region compression level should be between 1 and 22 in config: " + linearCompressionLevel);
|
||||
log(Level.SEVERE, "Falling back to compression level 1.");
|
||||
linearCompressionLevel = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package org.bxteam.divinemc.dfc.common;
|
||||
|
||||
import net.minecraft.world.level.levelgen.NoiseRouterData;
|
||||
|
||||
public interface IDensityFunctionsCaveScaler {
|
||||
static double invokeScaleCaves(double value) {
|
||||
return NoiseRouterData.QuantizedSpaghettiRarity.getSphaghettiRarity2D(value);
|
||||
}
|
||||
|
||||
static double invokeScaleTunnels(double value) {
|
||||
return NoiseRouterData.QuantizedSpaghettiRarity.getSpaghettiRarity3D(value);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package org.bxteam.divinemc.dfc.common.ast;
|
||||
|
||||
import org.bxteam.divinemc.dfc.common.gen.BytecodeGen;
|
||||
import org.objectweb.asm.commons.InstructionAdapter;
|
||||
|
||||
public interface AstNode {
|
||||
double evalSingle(int var1, int var2, int var3, EvalType var4);
|
||||
|
||||
void evalMulti(double[] var1, int[] var2, int[] var3, int[] var4, EvalType var5);
|
||||
|
||||
AstNode[] getChildren();
|
||||
|
||||
AstNode transform(AstTransformer var1);
|
||||
|
||||
void doBytecodeGenSingle(BytecodeGen.Context var1, InstructionAdapter var2, BytecodeGen.Context.LocalVarConsumer var3);
|
||||
|
||||
void doBytecodeGenMulti(BytecodeGen.Context var1, InstructionAdapter var2, BytecodeGen.Context.LocalVarConsumer var3);
|
||||
|
||||
boolean relaxedEquals(AstNode var1);
|
||||
|
||||
int relaxedHashCode();
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
package org.bxteam.divinemc.dfc.common.ast;
|
||||
|
||||
public interface AstTransformer {
|
||||
AstNode transform(AstNode var1);
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package org.bxteam.divinemc.dfc.common.ast;
|
||||
|
||||
import org.bxteam.divinemc.dfc.common.vif.EachApplierVanillaInterface;
|
||||
import net.minecraft.world.level.levelgen.DensityFunction;
|
||||
import net.minecraft.world.level.levelgen.NoiseChunk;
|
||||
|
||||
public enum EvalType {
|
||||
NORMAL,
|
||||
INTERPOLATION;
|
||||
|
||||
private EvalType() {
|
||||
}
|
||||
|
||||
public static EvalType from(DensityFunction.FunctionContext pos) {
|
||||
return pos instanceof NoiseChunk ? INTERPOLATION : NORMAL;
|
||||
}
|
||||
|
||||
public static EvalType from(DensityFunction.ContextProvider applier) {
|
||||
if (applier instanceof EachApplierVanillaInterface vif) {
|
||||
return vif.getType();
|
||||
} else {
|
||||
return applier instanceof NoiseChunk ? INTERPOLATION : NORMAL;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
package org.bxteam.divinemc.dfc.common.ast;
|
||||
|
||||
import org.bxteam.divinemc.dfc.common.ast.binary.AddNode;
|
||||
import org.bxteam.divinemc.dfc.common.ast.binary.MaxNode;
|
||||
import org.bxteam.divinemc.dfc.common.ast.binary.MaxShortNode;
|
||||
import org.bxteam.divinemc.dfc.common.ast.binary.MinNode;
|
||||
import org.bxteam.divinemc.dfc.common.ast.binary.MinShortNode;
|
||||
import org.bxteam.divinemc.dfc.common.ast.binary.MulNode;
|
||||
import org.bxteam.divinemc.dfc.common.ast.misc.CacheLikeNode;
|
||||
import org.bxteam.divinemc.dfc.common.ast.misc.ConstantNode;
|
||||
import org.bxteam.divinemc.dfc.common.ast.misc.DelegateNode;
|
||||
import org.bxteam.divinemc.dfc.common.ast.misc.RangeChoiceNode;
|
||||
import org.bxteam.divinemc.dfc.common.ast.misc.YClampedGradientNode;
|
||||
import org.bxteam.divinemc.dfc.common.ast.noise.DFTNoiseNode;
|
||||
import org.bxteam.divinemc.dfc.common.ast.noise.DFTShiftANode;
|
||||
import org.bxteam.divinemc.dfc.common.ast.noise.DFTShiftBNode;
|
||||
import org.bxteam.divinemc.dfc.common.ast.noise.DFTShiftNode;
|
||||
import org.bxteam.divinemc.dfc.common.ast.noise.DFTWeirdScaledSamplerNode;
|
||||
import org.bxteam.divinemc.dfc.common.ast.noise.ShiftedNoiseNode;
|
||||
import org.bxteam.divinemc.dfc.common.ast.spline.SplineAstNode;
|
||||
import org.bxteam.divinemc.dfc.common.ast.unary.AbsNode;
|
||||
import org.bxteam.divinemc.dfc.common.ast.unary.CubeNode;
|
||||
import org.bxteam.divinemc.dfc.common.ast.unary.NegMulNode;
|
||||
import org.bxteam.divinemc.dfc.common.ast.unary.SquareNode;
|
||||
import org.bxteam.divinemc.dfc.common.ast.unary.SqueezeNode;
|
||||
import org.bxteam.divinemc.dfc.common.ducks.IEqualityOverriding;
|
||||
import org.bxteam.divinemc.dfc.common.ducks.IFastCacheLike;
|
||||
import org.bxteam.divinemc.dfc.common.vif.AstVanillaInterface;
|
||||
import java.util.Objects;
|
||||
import net.minecraft.world.level.levelgen.DensityFunction;
|
||||
import net.minecraft.world.level.levelgen.DensityFunctions;
|
||||
import net.minecraft.world.level.levelgen.NoiseChunk;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class McToAst {
|
||||
public McToAst() {
|
||||
}
|
||||
|
||||
public static AstNode toAst(DensityFunction df) {
|
||||
Objects.requireNonNull(df);
|
||||
return switch (df) {
|
||||
case AstVanillaInterface f -> f.getAstNode();
|
||||
|
||||
case NoiseChunk.BlendAlpha f -> new ConstantNode(1.0);
|
||||
case NoiseChunk.BlendOffset f -> new ConstantNode(0.0);
|
||||
case DensityFunctions.BlendAlpha f -> new ConstantNode(1.0);
|
||||
case DensityFunctions.BlendOffset f -> new ConstantNode(0.0);
|
||||
case DensityFunctions.TwoArgumentSimpleFunction f -> switch (f.type()) {
|
||||
case ADD -> new AddNode(toAst(f.argument1()), toAst(f.argument2()));
|
||||
case MUL -> new MulNode(toAst(f.argument1()), toAst(f.argument2()));
|
||||
case MIN -> {
|
||||
double rightMin = f.argument2().minValue();
|
||||
if (f.argument1().minValue() < rightMin) {
|
||||
yield new MinShortNode(toAst(f.argument1()), toAst(f.argument2()), rightMin);
|
||||
} else {
|
||||
yield new MinNode(toAst(f.argument1()), toAst(f.argument2()));
|
||||
}
|
||||
}
|
||||
case MAX -> {
|
||||
double rightMax = f.argument2().maxValue();
|
||||
if (f.argument1().maxValue() > rightMax) {
|
||||
yield new MaxShortNode(toAst(f.argument1()), toAst(f.argument2()), rightMax);
|
||||
} else {
|
||||
yield new MaxNode(toAst(f.argument1()), toAst(f.argument2()));
|
||||
}
|
||||
}
|
||||
};
|
||||
case DensityFunctions.BlendDensity f -> toAst(f.input());
|
||||
case DensityFunctions.Clamp f -> new MaxNode(new ConstantNode(f.minValue()), new MinNode(new ConstantNode(f.maxValue()), toAst(f.input())));
|
||||
case DensityFunctions.Constant f -> new ConstantNode(f.value());
|
||||
case DensityFunctions.HolderHolder f -> toAst(f.function().value());
|
||||
case DensityFunctions.Mapped f -> switch (f.type()) {
|
||||
case ABS -> new AbsNode(toAst(f.input()));
|
||||
case SQUARE -> new SquareNode(toAst(f.input()));
|
||||
case CUBE -> new CubeNode(toAst(f.input()));
|
||||
case HALF_NEGATIVE -> new NegMulNode(toAst(f.input()), 0.5);
|
||||
case QUARTER_NEGATIVE -> new NegMulNode(toAst(f.input()), 0.25);
|
||||
case SQUEEZE -> new SqueezeNode(toAst(f.input()));
|
||||
};
|
||||
case DensityFunctions.RangeChoice f -> new RangeChoiceNode(toAst(f.input()), f.minInclusive(), f.maxExclusive(), toAst(f.whenInRange()), toAst(f.whenOutOfRange()));
|
||||
case DensityFunctions.Marker f -> {
|
||||
DensityFunctions.Marker wrapping = new DensityFunctions.Marker(f.type(), new AstVanillaInterface(toAst(f.wrapped()), null));
|
||||
((IEqualityOverriding) (Object) wrapping).c2me$overrideEquality(wrapping);
|
||||
yield new DelegateNode(wrapping);
|
||||
}
|
||||
case IFastCacheLike f -> new CacheLikeNode(f, toAst(f.c2me$getDelegate()));
|
||||
case DensityFunctions.ShiftedNoise f -> new ShiftedNoiseNode(toAst(f.shiftX()), toAst(f.shiftY()), toAst(f.shiftZ()), f.xzScale(), f.yScale(), f.noise());
|
||||
case DensityFunctions.Noise f -> new DFTNoiseNode(f.noise(), f.xzScale(), f.yScale());
|
||||
case DensityFunctions.Shift f -> new DFTShiftNode(f.offsetNoise());
|
||||
case DensityFunctions.ShiftA f -> new DFTShiftANode(f.offsetNoise());
|
||||
case DensityFunctions.ShiftB f -> new DFTShiftBNode(f.offsetNoise());
|
||||
case DensityFunctions.YClampedGradient f -> new YClampedGradientNode(f.fromY(), f.toY(), f.fromValue(), f.toValue());
|
||||
case DensityFunctions.WeirdScaledSampler f -> new DFTWeirdScaledSamplerNode(toAst(f.input()), f.noise(), f.rarityValueMapper());
|
||||
case DensityFunctions.Spline f -> new SplineAstNode(f.spline());
|
||||
|
||||
default -> {
|
||||
// delegateStatistics.computeIfAbsent(df.getClass(), unused -> new LongAdder()).increment();;
|
||||
yield new DelegateNode(df);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public static @NotNull DensityFunction wrapVanilla(DensityFunction densityFunction) {
|
||||
return new AstVanillaInterface(toAst(densityFunction), densityFunction);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
package org.bxteam.divinemc.dfc.common.ast.binary;
|
||||
|
||||
import org.bxteam.divinemc.dfc.common.ast.AstNode;
|
||||
import org.bxteam.divinemc.dfc.common.ast.AstTransformer;
|
||||
import org.bxteam.divinemc.dfc.common.gen.BytecodeGen.Context;
|
||||
import org.bxteam.divinemc.dfc.common.util.ArrayCache;
|
||||
import java.util.Objects;
|
||||
import org.objectweb.asm.Type;
|
||||
import org.objectweb.asm.commons.InstructionAdapter;
|
||||
|
||||
public abstract class AbstractBinaryNode implements AstNode {
|
||||
protected final AstNode left;
|
||||
protected final AstNode right;
|
||||
|
||||
public AbstractBinaryNode(AstNode left, AstNode right) {
|
||||
this.left = (AstNode)Objects.requireNonNull(left);
|
||||
this.right = (AstNode)Objects.requireNonNull(right);
|
||||
}
|
||||
|
||||
public AstNode[] getChildren() {
|
||||
return new AstNode[]{this.left, this.right};
|
||||
}
|
||||
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
} else if (o != null && this.getClass() == o.getClass()) {
|
||||
AbstractBinaryNode that = (AbstractBinaryNode)o;
|
||||
return Objects.equals(this.left, that.left) && Objects.equals(this.right, that.right);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
int result = 1;
|
||||
result = 31 * result + this.getClass().hashCode();
|
||||
result = 31 * result + this.left.hashCode();
|
||||
result = 31 * result + this.right.hashCode();
|
||||
return result;
|
||||
}
|
||||
|
||||
public boolean relaxedEquals(AstNode o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
} else if (o != null && this.getClass() == o.getClass()) {
|
||||
AbstractBinaryNode that = (AbstractBinaryNode)o;
|
||||
return this.left.relaxedEquals(that.left) && this.right.relaxedEquals(that.right);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public int relaxedHashCode() {
|
||||
int result = 1;
|
||||
result = 31 * result + this.getClass().hashCode();
|
||||
result = 31 * result + this.left.relaxedHashCode();
|
||||
result = 31 * result + this.right.relaxedHashCode();
|
||||
return result;
|
||||
}
|
||||
|
||||
protected abstract AstNode newInstance(AstNode var1, AstNode var2);
|
||||
|
||||
public AstNode transform(AstTransformer transformer) {
|
||||
AstNode left = this.left.transform(transformer);
|
||||
AstNode right = this.right.transform(transformer);
|
||||
return left == this.left && right == this.right ? transformer.transform(this) : transformer.transform(this.newInstance(left, right));
|
||||
}
|
||||
|
||||
public void doBytecodeGenSingle(Context context, InstructionAdapter m, Context.LocalVarConsumer localVarConsumer) {
|
||||
String leftMethod = context.newSingleMethod(this.left);
|
||||
String rightMethod = context.newSingleMethod(this.right);
|
||||
context.callDelegateSingle(m, leftMethod);
|
||||
context.callDelegateSingle(m, rightMethod);
|
||||
}
|
||||
|
||||
public void doBytecodeGenMulti(Context context, InstructionAdapter m, Context.LocalVarConsumer localVarConsumer) {
|
||||
String leftMethod = context.newMultiMethod(this.left);
|
||||
String rightMethod = context.newMultiMethod(this.right);
|
||||
int res1 = localVarConsumer.createLocalVariable("res1", Type.getDescriptor(double[].class));
|
||||
m.load(6, InstructionAdapter.OBJECT_TYPE);
|
||||
m.load(1, InstructionAdapter.OBJECT_TYPE);
|
||||
m.arraylength();
|
||||
m.iconst(0);
|
||||
m.invokevirtual(Type.getInternalName(ArrayCache.class), "getDoubleArray", Type.getMethodDescriptor(Type.getType(double[].class), new Type[]{Type.INT_TYPE, Type.BOOLEAN_TYPE}), false);
|
||||
m.store(res1, InstructionAdapter.OBJECT_TYPE);
|
||||
context.callDelegateMulti(m, leftMethod);
|
||||
m.load(0, InstructionAdapter.OBJECT_TYPE);
|
||||
m.load(res1, InstructionAdapter.OBJECT_TYPE);
|
||||
m.load(2, InstructionAdapter.OBJECT_TYPE);
|
||||
m.load(3, InstructionAdapter.OBJECT_TYPE);
|
||||
m.load(4, InstructionAdapter.OBJECT_TYPE);
|
||||
m.load(5, InstructionAdapter.OBJECT_TYPE);
|
||||
m.load(6, InstructionAdapter.OBJECT_TYPE);
|
||||
m.invokevirtual(context.className, rightMethod, Context.MULTI_DESC, false);
|
||||
context.doCountedLoop(m, localVarConsumer, (idx) -> {
|
||||
this.bytecodeGenMultiBody(m, idx, res1);
|
||||
});
|
||||
m.load(6, InstructionAdapter.OBJECT_TYPE);
|
||||
m.load(res1, InstructionAdapter.OBJECT_TYPE);
|
||||
m.invokevirtual(Type.getInternalName(ArrayCache.class), "recycle", Type.getMethodDescriptor(Type.VOID_TYPE, new Type[]{Type.getType(double[].class)}), false);
|
||||
m.areturn(Type.VOID_TYPE);
|
||||
}
|
||||
|
||||
protected abstract void bytecodeGenMultiBody(InstructionAdapter var1, int var2, int var3);
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
package org.bxteam.divinemc.dfc.common.ast.binary;
|
||||
|
||||
import org.bxteam.divinemc.dfc.common.ast.AstNode;
|
||||
import org.bxteam.divinemc.dfc.common.ast.EvalType;
|
||||
import org.bxteam.divinemc.dfc.common.gen.BytecodeGen;
|
||||
import org.objectweb.asm.Type;
|
||||
import org.objectweb.asm.commons.InstructionAdapter;
|
||||
|
||||
public class AddNode extends AbstractBinaryNode {
|
||||
public AddNode(AstNode left, AstNode right) {
|
||||
super(left, right);
|
||||
}
|
||||
|
||||
protected AstNode newInstance(AstNode left, AstNode right) {
|
||||
return new AddNode(left, right);
|
||||
}
|
||||
|
||||
public double evalSingle(int x, int y, int z, EvalType type) {
|
||||
return this.left.evalSingle(x, y, z, type) + this.right.evalSingle(x, y, z, type);
|
||||
}
|
||||
|
||||
public void evalMulti(double[] res, int[] x, int[] y, int[] z, EvalType type) {
|
||||
double[] res1 = new double[res.length];
|
||||
this.left.evalMulti(res, x, y, z, type);
|
||||
this.right.evalMulti(res1, x, y, z, type);
|
||||
|
||||
for(int i = 0; i < res1.length; ++i) {
|
||||
res[i] += res1[i];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void doBytecodeGenSingle(BytecodeGen.Context context, InstructionAdapter m, BytecodeGen.Context.LocalVarConsumer localVarConsumer) {
|
||||
super.doBytecodeGenSingle(context, m, localVarConsumer);
|
||||
m.add(Type.DOUBLE_TYPE);
|
||||
m.areturn(Type.DOUBLE_TYPE);
|
||||
}
|
||||
|
||||
protected void bytecodeGenMultiBody(InstructionAdapter m, int idx, int res1) {
|
||||
m.load(1, InstructionAdapter.OBJECT_TYPE);
|
||||
m.load(idx, Type.INT_TYPE);
|
||||
m.dup2();
|
||||
m.aload(Type.DOUBLE_TYPE);
|
||||
m.load(res1, InstructionAdapter.OBJECT_TYPE);
|
||||
m.load(idx, Type.INT_TYPE);
|
||||
m.aload(Type.DOUBLE_TYPE);
|
||||
m.add(Type.DOUBLE_TYPE);
|
||||
m.astore(Type.DOUBLE_TYPE);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
package org.bxteam.divinemc.dfc.common.ast.binary;
|
||||
|
||||
import org.bxteam.divinemc.dfc.common.ast.AstNode;
|
||||
import org.bxteam.divinemc.dfc.common.ast.EvalType;
|
||||
import org.bxteam.divinemc.dfc.common.gen.BytecodeGen;
|
||||
import org.objectweb.asm.Type;
|
||||
import org.objectweb.asm.commons.InstructionAdapter;
|
||||
|
||||
public class MaxNode extends AbstractBinaryNode {
|
||||
public MaxNode(AstNode left, AstNode right) {
|
||||
super(left, right);
|
||||
}
|
||||
|
||||
protected AstNode newInstance(AstNode left, AstNode right) {
|
||||
return new MaxNode(left, right);
|
||||
}
|
||||
|
||||
public double evalSingle(int x, int y, int z, EvalType type) {
|
||||
return Math.max(this.left.evalSingle(x, y, z, type), this.right.evalSingle(x, y, z, type));
|
||||
}
|
||||
|
||||
public void evalMulti(double[] res, int[] x, int[] y, int[] z, EvalType type) {
|
||||
double[] res1 = new double[res.length];
|
||||
this.left.evalMulti(res, x, y, z, type);
|
||||
this.right.evalMulti(res1, x, y, z, type);
|
||||
|
||||
for(int i = 0; i < res1.length; ++i) {
|
||||
res[i] = Math.max(res[i], res1[i]);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void doBytecodeGenSingle(BytecodeGen.Context context, InstructionAdapter m, BytecodeGen.Context.LocalVarConsumer localVarConsumer) {
|
||||
super.doBytecodeGenSingle(context, m, localVarConsumer);
|
||||
m.invokestatic(Type.getInternalName(Math.class), "max", Type.getMethodDescriptor(Type.DOUBLE_TYPE, new Type[]{Type.DOUBLE_TYPE, Type.DOUBLE_TYPE}), false);
|
||||
m.areturn(Type.DOUBLE_TYPE);
|
||||
}
|
||||
|
||||
protected void bytecodeGenMultiBody(InstructionAdapter m, int idx, int res1) {
|
||||
m.load(1, InstructionAdapter.OBJECT_TYPE);
|
||||
m.load(idx, Type.INT_TYPE);
|
||||
m.dup2();
|
||||
m.aload(Type.DOUBLE_TYPE);
|
||||
m.load(res1, InstructionAdapter.OBJECT_TYPE);
|
||||
m.load(idx, Type.INT_TYPE);
|
||||
m.aload(Type.DOUBLE_TYPE);
|
||||
m.invokestatic(Type.getInternalName(Math.class), "max", Type.getMethodDescriptor(Type.DOUBLE_TYPE, new Type[]{Type.DOUBLE_TYPE, Type.DOUBLE_TYPE}), false);
|
||||
m.astore(Type.DOUBLE_TYPE);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
package org.bxteam.divinemc.dfc.common.ast.binary;
|
||||
|
||||
import org.bxteam.divinemc.dfc.common.ast.AstNode;
|
||||
import org.bxteam.divinemc.dfc.common.ast.EvalType;
|
||||
import org.bxteam.divinemc.dfc.common.gen.BytecodeGen.Context;
|
||||
import org.objectweb.asm.Label;
|
||||
import org.objectweb.asm.Type;
|
||||
import org.objectweb.asm.commons.InstructionAdapter;
|
||||
|
||||
public class MaxShortNode extends AbstractBinaryNode {
|
||||
private final double rightMax;
|
||||
|
||||
public MaxShortNode(AstNode left, AstNode right, double rightMax) {
|
||||
super(left, right);
|
||||
this.rightMax = rightMax;
|
||||
}
|
||||
|
||||
protected AstNode newInstance(AstNode left, AstNode right) {
|
||||
return new MaxShortNode(left, right, this.rightMax);
|
||||
}
|
||||
|
||||
public double evalSingle(int x, int y, int z, EvalType type) {
|
||||
double evaled = this.left.evalSingle(x, y, z, type);
|
||||
return evaled >= this.rightMax ? evaled : Math.max(evaled, this.right.evalSingle(x, y, z, type));
|
||||
}
|
||||
|
||||
public void evalMulti(double[] res, int[] x, int[] y, int[] z, EvalType type) {
|
||||
this.left.evalMulti(res, x, y, z, type);
|
||||
|
||||
for(int i = 0; i < res.length; ++i) {
|
||||
res[i] = res[i] >= this.rightMax ? res[i] : Math.max(res[i], this.right.evalSingle(x[i], y[i], z[i], type));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void doBytecodeGenSingle(Context context, InstructionAdapter m, Context.LocalVarConsumer localVarConsumer) {
|
||||
String leftMethod = context.newSingleMethod(this.left);
|
||||
String rightMethod = context.newSingleMethod(this.right);
|
||||
Label minLabel = new Label();
|
||||
context.callDelegateSingle(m, leftMethod);
|
||||
m.dup2();
|
||||
m.dconst(this.rightMax);
|
||||
m.cmpl(Type.DOUBLE_TYPE);
|
||||
m.iflt(minLabel);
|
||||
m.areturn(Type.DOUBLE_TYPE);
|
||||
m.visitLabel(minLabel);
|
||||
context.callDelegateSingle(m, rightMethod);
|
||||
m.invokestatic(Type.getInternalName(Math.class), "max", Type.getMethodDescriptor(Type.DOUBLE_TYPE, new Type[]{Type.DOUBLE_TYPE, Type.DOUBLE_TYPE}), false);
|
||||
m.areturn(Type.DOUBLE_TYPE);
|
||||
}
|
||||
|
||||
public void doBytecodeGenMulti(Context context, InstructionAdapter m, Context.LocalVarConsumer localVarConsumer) {
|
||||
String leftMethod = context.newMultiMethod(this.left);
|
||||
String rightMethodSingle = context.newSingleMethod(this.right);
|
||||
context.callDelegateMulti(m, leftMethod);
|
||||
context.doCountedLoop(m, localVarConsumer, (idx) -> {
|
||||
Label minLabel = new Label();
|
||||
Label end = new Label();
|
||||
m.load(1, InstructionAdapter.OBJECT_TYPE);
|
||||
m.load(idx, Type.INT_TYPE);
|
||||
m.load(1, InstructionAdapter.OBJECT_TYPE);
|
||||
m.load(idx, Type.INT_TYPE);
|
||||
m.aload(Type.DOUBLE_TYPE);
|
||||
m.dup2();
|
||||
m.dconst(this.rightMax);
|
||||
m.cmpl(Type.DOUBLE_TYPE);
|
||||
m.iflt(minLabel);
|
||||
m.goTo(end);
|
||||
m.visitLabel(minLabel);
|
||||
m.load(0, InstructionAdapter.OBJECT_TYPE);
|
||||
m.load(2, InstructionAdapter.OBJECT_TYPE);
|
||||
m.load(idx, Type.INT_TYPE);
|
||||
m.aload(Type.INT_TYPE);
|
||||
m.load(3, InstructionAdapter.OBJECT_TYPE);
|
||||
m.load(idx, Type.INT_TYPE);
|
||||
m.aload(Type.INT_TYPE);
|
||||
m.load(4, InstructionAdapter.OBJECT_TYPE);
|
||||
m.load(idx, Type.INT_TYPE);
|
||||
m.aload(Type.INT_TYPE);
|
||||
m.load(5, InstructionAdapter.OBJECT_TYPE);
|
||||
m.invokevirtual(context.className, rightMethodSingle, Context.SINGLE_DESC, false);
|
||||
m.invokestatic(Type.getInternalName(Math.class), "max", Type.getMethodDescriptor(Type.DOUBLE_TYPE, new Type[]{Type.DOUBLE_TYPE, Type.DOUBLE_TYPE}), false);
|
||||
m.visitLabel(end);
|
||||
m.astore(Type.DOUBLE_TYPE);
|
||||
});
|
||||
m.areturn(Type.VOID_TYPE);
|
||||
}
|
||||
|
||||
protected void bytecodeGenMultiBody(InstructionAdapter m, int idx, int res1) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
package org.bxteam.divinemc.dfc.common.ast.binary;
|
||||
|
||||
import org.bxteam.divinemc.dfc.common.ast.AstNode;
|
||||
import org.bxteam.divinemc.dfc.common.ast.EvalType;
|
||||
import org.bxteam.divinemc.dfc.common.gen.BytecodeGen;
|
||||
import org.objectweb.asm.Type;
|
||||
import org.objectweb.asm.commons.InstructionAdapter;
|
||||
|
||||
public class MinNode extends AbstractBinaryNode {
|
||||
public MinNode(AstNode left, AstNode right) {
|
||||
super(left, right);
|
||||
}
|
||||
|
||||
protected AstNode newInstance(AstNode left, AstNode right) {
|
||||
return new MinNode(left, right);
|
||||
}
|
||||
|
||||
public double evalSingle(int x, int y, int z, EvalType type) {
|
||||
return Math.min(this.left.evalSingle(x, y, z, type), this.right.evalSingle(x, y, z, type));
|
||||
}
|
||||
|
||||
public void evalMulti(double[] res, int[] x, int[] y, int[] z, EvalType type) {
|
||||
double[] res1 = new double[res.length];
|
||||
this.left.evalMulti(res, x, y, z, type);
|
||||
this.right.evalMulti(res1, x, y, z, type);
|
||||
|
||||
for(int i = 0; i < res1.length; ++i) {
|
||||
res[i] = Math.min(res[i], res1[i]);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void doBytecodeGenSingle(BytecodeGen.Context context, InstructionAdapter m, BytecodeGen.Context.LocalVarConsumer localVarConsumer) {
|
||||
super.doBytecodeGenSingle(context, m, localVarConsumer);
|
||||
m.invokestatic(Type.getInternalName(Math.class), "min", Type.getMethodDescriptor(Type.DOUBLE_TYPE, new Type[]{Type.DOUBLE_TYPE, Type.DOUBLE_TYPE}), false);
|
||||
m.areturn(Type.DOUBLE_TYPE);
|
||||
}
|
||||
|
||||
protected void bytecodeGenMultiBody(InstructionAdapter m, int idx, int res1) {
|
||||
m.load(1, InstructionAdapter.OBJECT_TYPE);
|
||||
m.load(idx, Type.INT_TYPE);
|
||||
m.dup2();
|
||||
m.aload(Type.DOUBLE_TYPE);
|
||||
m.load(res1, InstructionAdapter.OBJECT_TYPE);
|
||||
m.load(idx, Type.INT_TYPE);
|
||||
m.aload(Type.DOUBLE_TYPE);
|
||||
m.invokestatic(Type.getInternalName(Math.class), "min", Type.getMethodDescriptor(Type.DOUBLE_TYPE, new Type[]{Type.DOUBLE_TYPE, Type.DOUBLE_TYPE}), false);
|
||||
m.astore(Type.DOUBLE_TYPE);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
package org.bxteam.divinemc.dfc.common.ast.binary;
|
||||
|
||||
import org.bxteam.divinemc.dfc.common.ast.AstNode;
|
||||
import org.bxteam.divinemc.dfc.common.ast.EvalType;
|
||||
import org.bxteam.divinemc.dfc.common.gen.BytecodeGen.Context;
|
||||
import org.objectweb.asm.Label;
|
||||
import org.objectweb.asm.Type;
|
||||
import org.objectweb.asm.commons.InstructionAdapter;
|
||||
|
||||
public class MinShortNode extends AbstractBinaryNode {
|
||||
private final double rightMin;
|
||||
|
||||
public MinShortNode(AstNode left, AstNode right, double rightMin) {
|
||||
super(left, right);
|
||||
this.rightMin = rightMin;
|
||||
}
|
||||
|
||||
protected AstNode newInstance(AstNode left, AstNode right) {
|
||||
return new MinShortNode(left, right, this.rightMin);
|
||||
}
|
||||
|
||||
public double evalSingle(int x, int y, int z, EvalType type) {
|
||||
double evaled = this.left.evalSingle(x, y, z, type);
|
||||
return evaled <= this.rightMin ? evaled : Math.min(evaled, this.right.evalSingle(x, y, z, type));
|
||||
}
|
||||
|
||||
public void evalMulti(double[] res, int[] x, int[] y, int[] z, EvalType type) {
|
||||
this.left.evalMulti(res, x, y, z, type);
|
||||
|
||||
for(int i = 0; i < res.length; ++i) {
|
||||
res[i] = res[i] <= this.rightMin ? res[i] : Math.min(res[i], this.right.evalSingle(x[i], y[i], z[i], type));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void doBytecodeGenSingle(Context context, InstructionAdapter m, Context.LocalVarConsumer localVarConsumer) {
|
||||
String leftMethod = context.newSingleMethod(this.left);
|
||||
String rightMethod = context.newSingleMethod(this.right);
|
||||
Label minLabel = new Label();
|
||||
context.callDelegateSingle(m, leftMethod);
|
||||
m.dup2();
|
||||
m.dconst(this.rightMin);
|
||||
m.cmpg(Type.DOUBLE_TYPE);
|
||||
m.ifgt(minLabel);
|
||||
m.areturn(Type.DOUBLE_TYPE);
|
||||
m.visitLabel(minLabel);
|
||||
context.callDelegateSingle(m, rightMethod);
|
||||
m.invokestatic(Type.getInternalName(Math.class), "min", Type.getMethodDescriptor(Type.DOUBLE_TYPE, new Type[]{Type.DOUBLE_TYPE, Type.DOUBLE_TYPE}), false);
|
||||
m.areturn(Type.DOUBLE_TYPE);
|
||||
}
|
||||
|
||||
public void doBytecodeGenMulti(Context context, InstructionAdapter m, Context.LocalVarConsumer localVarConsumer) {
|
||||
String leftMethod = context.newMultiMethod(this.left);
|
||||
String rightMethodSingle = context.newSingleMethod(this.right);
|
||||
context.callDelegateMulti(m, leftMethod);
|
||||
context.doCountedLoop(m, localVarConsumer, (idx) -> {
|
||||
Label minLabel = new Label();
|
||||
Label end = new Label();
|
||||
m.load(1, InstructionAdapter.OBJECT_TYPE);
|
||||
m.load(idx, Type.INT_TYPE);
|
||||
m.load(1, InstructionAdapter.OBJECT_TYPE);
|
||||
m.load(idx, Type.INT_TYPE);
|
||||
m.aload(Type.DOUBLE_TYPE);
|
||||
m.dup2();
|
||||
m.dconst(this.rightMin);
|
||||
m.cmpg(Type.DOUBLE_TYPE);
|
||||
m.ifgt(minLabel);
|
||||
m.goTo(end);
|
||||
m.visitLabel(minLabel);
|
||||
m.load(0, InstructionAdapter.OBJECT_TYPE);
|
||||
m.load(2, InstructionAdapter.OBJECT_TYPE);
|
||||
m.load(idx, Type.INT_TYPE);
|
||||
m.aload(Type.INT_TYPE);
|
||||
m.load(3, InstructionAdapter.OBJECT_TYPE);
|
||||
m.load(idx, Type.INT_TYPE);
|
||||
m.aload(Type.INT_TYPE);
|
||||
m.load(4, InstructionAdapter.OBJECT_TYPE);
|
||||
m.load(idx, Type.INT_TYPE);
|
||||
m.aload(Type.INT_TYPE);
|
||||
m.load(5, InstructionAdapter.OBJECT_TYPE);
|
||||
m.invokevirtual(context.className, rightMethodSingle, Context.SINGLE_DESC, false);
|
||||
m.invokestatic(Type.getInternalName(Math.class), "min", Type.getMethodDescriptor(Type.DOUBLE_TYPE, new Type[]{Type.DOUBLE_TYPE, Type.DOUBLE_TYPE}), false);
|
||||
m.visitLabel(end);
|
||||
m.astore(Type.DOUBLE_TYPE);
|
||||
});
|
||||
m.areturn(Type.VOID_TYPE);
|
||||
}
|
||||
|
||||
protected void bytecodeGenMultiBody(InstructionAdapter m, int idx, int res1) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
package org.bxteam.divinemc.dfc.common.ast.binary;
|
||||
|
||||
import org.bxteam.divinemc.dfc.common.ast.AstNode;
|
||||
import org.bxteam.divinemc.dfc.common.ast.EvalType;
|
||||
import org.bxteam.divinemc.dfc.common.gen.BytecodeGen.Context;
|
||||
import org.objectweb.asm.Label;
|
||||
import org.objectweb.asm.Type;
|
||||
import org.objectweb.asm.commons.InstructionAdapter;
|
||||
|
||||
public class MulNode extends AbstractBinaryNode {
|
||||
public MulNode(AstNode left, AstNode right) {
|
||||
super(left, right);
|
||||
}
|
||||
|
||||
protected AstNode newInstance(AstNode left, AstNode right) {
|
||||
return new MulNode(left, right);
|
||||
}
|
||||
|
||||
public double evalSingle(int x, int y, int z, EvalType type) {
|
||||
double evaled = this.left.evalSingle(x, y, z, type);
|
||||
return evaled == 0.0 ? 0.0 : evaled * this.right.evalSingle(x, y, z, type);
|
||||
}
|
||||
|
||||
public void evalMulti(double[] res, int[] x, int[] y, int[] z, EvalType type) {
|
||||
this.left.evalMulti(res, x, y, z, type);
|
||||
|
||||
for(int i = 0; i < res.length; ++i) {
|
||||
res[i] = res[i] == 0.0 ? 0.0 : res[i] * this.right.evalSingle(x[i], y[i], z[i], type);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void doBytecodeGenSingle(Context context, InstructionAdapter m, Context.LocalVarConsumer localVarConsumer) {
|
||||
String leftMethod = context.newSingleMethod(this.left);
|
||||
String rightMethod = context.newSingleMethod(this.right);
|
||||
Label notZero = new Label();
|
||||
context.callDelegateSingle(m, leftMethod);
|
||||
m.dup2();
|
||||
m.dconst(0.0);
|
||||
m.cmpl(Type.DOUBLE_TYPE);
|
||||
m.ifne(notZero);
|
||||
m.dconst(0.0);
|
||||
m.areturn(Type.DOUBLE_TYPE);
|
||||
m.visitLabel(notZero);
|
||||
context.callDelegateSingle(m, rightMethod);
|
||||
m.mul(Type.DOUBLE_TYPE);
|
||||
m.areturn(Type.DOUBLE_TYPE);
|
||||
}
|
||||
|
||||
public void doBytecodeGenMulti(Context context, InstructionAdapter m, Context.LocalVarConsumer localVarConsumer) {
|
||||
String leftMethod = context.newMultiMethod(this.left);
|
||||
String rightMethodSingle = context.newSingleMethod(this.right);
|
||||
context.callDelegateMulti(m, leftMethod);
|
||||
context.doCountedLoop(m, localVarConsumer, (idx) -> {
|
||||
Label minLabel = new Label();
|
||||
Label end = new Label();
|
||||
m.load(1, InstructionAdapter.OBJECT_TYPE);
|
||||
m.load(idx, Type.INT_TYPE);
|
||||
m.load(1, InstructionAdapter.OBJECT_TYPE);
|
||||
m.load(idx, Type.INT_TYPE);
|
||||
m.aload(Type.DOUBLE_TYPE);
|
||||
m.dup2();
|
||||
m.dconst(0.0);
|
||||
m.cmpl(Type.DOUBLE_TYPE);
|
||||
m.ifne(minLabel);
|
||||
m.pop2();
|
||||
m.dconst(0.0);
|
||||
m.goTo(end);
|
||||
m.visitLabel(minLabel);
|
||||
m.load(0, InstructionAdapter.OBJECT_TYPE);
|
||||
m.load(2, InstructionAdapter.OBJECT_TYPE);
|
||||
m.load(idx, Type.INT_TYPE);
|
||||
m.aload(Type.INT_TYPE);
|
||||
m.load(3, InstructionAdapter.OBJECT_TYPE);
|
||||
m.load(idx, Type.INT_TYPE);
|
||||
m.aload(Type.INT_TYPE);
|
||||
m.load(4, InstructionAdapter.OBJECT_TYPE);
|
||||
m.load(idx, Type.INT_TYPE);
|
||||
m.aload(Type.INT_TYPE);
|
||||
m.load(5, InstructionAdapter.OBJECT_TYPE);
|
||||
m.invokevirtual(context.className, rightMethodSingle, Context.SINGLE_DESC, false);
|
||||
m.mul(Type.DOUBLE_TYPE);
|
||||
m.visitLabel(end);
|
||||
m.astore(Type.DOUBLE_TYPE);
|
||||
});
|
||||
m.areturn(Type.VOID_TYPE);
|
||||
}
|
||||
|
||||
protected void bytecodeGenMultiBody(InstructionAdapter m, int idx, int res1) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package org.bxteam.divinemc.dfc.common.ast.dfvisitor;
|
||||
|
||||
import net.minecraft.world.level.levelgen.DensityFunction;
|
||||
import net.minecraft.world.level.levelgen.DensityFunctions;
|
||||
import net.minecraft.world.level.levelgen.NoiseChunk;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class StripBlending implements DensityFunction.Visitor {
|
||||
public static final StripBlending INSTANCE = new StripBlending();
|
||||
|
||||
private StripBlending() {
|
||||
}
|
||||
|
||||
public @NotNull DensityFunction apply(@NotNull DensityFunction densityFunction) {
|
||||
return switch (densityFunction) {
|
||||
case NoiseChunk.BlendAlpha _ -> DensityFunctions.constant(1.0);
|
||||
case NoiseChunk.BlendOffset _ -> DensityFunctions.constant(0.0);
|
||||
case DensityFunctions.BlendAlpha _ -> DensityFunctions.constant(1.0);
|
||||
case DensityFunctions.BlendOffset _ -> DensityFunctions.constant(0.0);
|
||||
default -> densityFunction;
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,256 @@
|
||||
package org.bxteam.divinemc.dfc.common.ast.misc;
|
||||
|
||||
import org.bxteam.divinemc.dfc.common.ast.AstNode;
|
||||
import org.bxteam.divinemc.dfc.common.ast.AstTransformer;
|
||||
import org.bxteam.divinemc.dfc.common.ast.EvalType;
|
||||
import org.bxteam.divinemc.dfc.common.ducks.IFastCacheLike;
|
||||
import org.bxteam.divinemc.dfc.common.gen.BytecodeGen.Context;
|
||||
import org.bxteam.divinemc.dfc.common.gen.IMultiMethod;
|
||||
import org.bxteam.divinemc.dfc.common.gen.ISingleMethod;
|
||||
import org.bxteam.divinemc.dfc.common.gen.SubCompiledDensityFunction;
|
||||
import java.util.Objects;
|
||||
import net.minecraft.world.level.levelgen.DensityFunction;
|
||||
import net.minecraft.world.level.levelgen.DensityFunctions;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.objectweb.asm.Handle;
|
||||
import org.objectweb.asm.Label;
|
||||
import org.objectweb.asm.Type;
|
||||
import org.objectweb.asm.commons.InstructionAdapter;
|
||||
|
||||
public class CacheLikeNode implements AstNode {
|
||||
private final IFastCacheLike cacheLike;
|
||||
private final AstNode delegate;
|
||||
|
||||
public CacheLikeNode(IFastCacheLike cacheLike, AstNode delegate) {
|
||||
this.cacheLike = cacheLike;
|
||||
this.delegate = (AstNode)Objects.requireNonNull(delegate);
|
||||
}
|
||||
|
||||
public double evalSingle(int x, int y, int z, EvalType type) {
|
||||
if (this.cacheLike == null) {
|
||||
return this.delegate.evalSingle(x, y, z, type);
|
||||
} else {
|
||||
double cached = this.cacheLike.c2me$getCached(x, y, z, type);
|
||||
if (Double.doubleToRawLongBits(cached) != 9222769054270909007L) {
|
||||
return cached;
|
||||
} else {
|
||||
double eval = this.delegate.evalSingle(x, y, z, type);
|
||||
this.cacheLike.c2me$cache(x, y, z, type, eval);
|
||||
return eval;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void evalMulti(double[] res, int[] x, int[] y, int[] z, EvalType type) {
|
||||
if (this.cacheLike == null) {
|
||||
this.delegate.evalMulti(res, x, y, z, type);
|
||||
} else {
|
||||
boolean cached = this.cacheLike.c2me$getCached(res, x, y, z, type);
|
||||
if (!cached) {
|
||||
this.delegate.evalMulti(res, x, y, z, type);
|
||||
this.cacheLike.c2me$cache(res, x, y, z, type);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public AstNode[] getChildren() {
|
||||
return new AstNode[]{this.delegate};
|
||||
}
|
||||
|
||||
public AstNode transform(AstTransformer transformer) {
|
||||
AstNode delegate = this.delegate.transform(transformer);
|
||||
return this.delegate == delegate ? transformer.transform(this) : transformer.transform(new CacheLikeNode(this.cacheLike, delegate));
|
||||
}
|
||||
|
||||
public void doBytecodeGenSingle(@NotNull Context context, @NotNull InstructionAdapter m, Context.@NotNull LocalVarConsumer localVarConsumer) {
|
||||
String delegateMethod = context.newSingleMethod(this.delegate);
|
||||
String cacheLikeField = context.newField(IFastCacheLike.class, this.cacheLike);
|
||||
this.genPostprocessingMethod(context, cacheLikeField);
|
||||
int eval = localVarConsumer.createLocalVariable("eval", Type.DOUBLE_TYPE.getDescriptor());
|
||||
Label cacheExists = new Label();
|
||||
Label cacheMiss = new Label();
|
||||
m.load(0, InstructionAdapter.OBJECT_TYPE);
|
||||
m.getfield(context.className, cacheLikeField, Type.getDescriptor(IFastCacheLike.class));
|
||||
m.ifnonnull(cacheExists);
|
||||
context.callDelegateSingle(m, delegateMethod);
|
||||
m.areturn(Type.DOUBLE_TYPE);
|
||||
m.visitLabel(cacheExists);
|
||||
m.load(0, InstructionAdapter.OBJECT_TYPE);
|
||||
m.getfield(context.className, cacheLikeField, Type.getDescriptor(IFastCacheLike.class));
|
||||
m.load(1, Type.INT_TYPE);
|
||||
m.load(2, Type.INT_TYPE);
|
||||
m.load(3, Type.INT_TYPE);
|
||||
m.load(4, InstructionAdapter.OBJECT_TYPE);
|
||||
m.invokeinterface(Type.getInternalName(IFastCacheLike.class), "c2me$getCached", Type.getMethodDescriptor(Type.DOUBLE_TYPE, new Type[]{Type.INT_TYPE, Type.INT_TYPE, Type.INT_TYPE, Type.getType(EvalType.class)}));
|
||||
m.dup2();
|
||||
m.invokestatic(Type.getInternalName(Double.class), "doubleToRawLongBits", Type.getMethodDescriptor(Type.LONG_TYPE, new Type[]{Type.DOUBLE_TYPE}), false);
|
||||
m.lconst(9222769054270909007L);
|
||||
m.lcmp();
|
||||
m.ifeq(cacheMiss);
|
||||
m.areturn(Type.DOUBLE_TYPE);
|
||||
m.visitLabel(cacheMiss);
|
||||
m.pop2();
|
||||
context.callDelegateSingle(m, delegateMethod);
|
||||
m.store(eval, Type.DOUBLE_TYPE);
|
||||
m.load(0, InstructionAdapter.OBJECT_TYPE);
|
||||
m.getfield(context.className, cacheLikeField, Type.getDescriptor(IFastCacheLike.class));
|
||||
m.load(1, Type.INT_TYPE);
|
||||
m.load(2, Type.INT_TYPE);
|
||||
m.load(3, Type.INT_TYPE);
|
||||
m.load(4, InstructionAdapter.OBJECT_TYPE);
|
||||
m.load(eval, Type.DOUBLE_TYPE);
|
||||
m.invokeinterface(Type.getInternalName(IFastCacheLike.class), "c2me$cache", Type.getMethodDescriptor(Type.VOID_TYPE, new Type[]{Type.INT_TYPE, Type.INT_TYPE, Type.INT_TYPE, Type.getType(EvalType.class), Type.DOUBLE_TYPE}));
|
||||
m.load(eval, Type.DOUBLE_TYPE);
|
||||
m.areturn(Type.DOUBLE_TYPE);
|
||||
}
|
||||
|
||||
public void doBytecodeGenMulti(@NotNull Context context, @NotNull InstructionAdapter m, Context.LocalVarConsumer localVarConsumer) {
|
||||
String delegateMethod = context.newMultiMethod(this.delegate);
|
||||
String cacheLikeField = context.newField(IFastCacheLike.class, this.cacheLike);
|
||||
this.genPostprocessingMethod(context, cacheLikeField);
|
||||
Label cacheExists = new Label();
|
||||
Label cacheMiss = new Label();
|
||||
m.load(0, InstructionAdapter.OBJECT_TYPE);
|
||||
m.getfield(context.className, cacheLikeField, Type.getDescriptor(IFastCacheLike.class));
|
||||
m.ifnonnull(cacheExists);
|
||||
context.callDelegateMulti(m, delegateMethod);
|
||||
m.areturn(Type.VOID_TYPE);
|
||||
m.visitLabel(cacheExists);
|
||||
m.load(0, InstructionAdapter.OBJECT_TYPE);
|
||||
m.getfield(context.className, cacheLikeField, Type.getDescriptor(IFastCacheLike.class));
|
||||
m.load(1, InstructionAdapter.OBJECT_TYPE);
|
||||
m.load(2, InstructionAdapter.OBJECT_TYPE);
|
||||
m.load(3, InstructionAdapter.OBJECT_TYPE);
|
||||
m.load(4, InstructionAdapter.OBJECT_TYPE);
|
||||
m.load(5, InstructionAdapter.OBJECT_TYPE);
|
||||
m.invokeinterface(Type.getInternalName(IFastCacheLike.class), "c2me$getCached", Type.getMethodDescriptor(Type.BOOLEAN_TYPE, new Type[]{Type.getType(double[].class), Type.getType(int[].class), Type.getType(int[].class), Type.getType(int[].class), Type.getType(EvalType.class)}));
|
||||
m.ifeq(cacheMiss);
|
||||
m.areturn(Type.VOID_TYPE);
|
||||
m.visitLabel(cacheMiss);
|
||||
context.callDelegateMulti(m, delegateMethod);
|
||||
m.load(0, InstructionAdapter.OBJECT_TYPE);
|
||||
m.getfield(context.className, cacheLikeField, Type.getDescriptor(IFastCacheLike.class));
|
||||
m.load(1, InstructionAdapter.OBJECT_TYPE);
|
||||
m.load(2, InstructionAdapter.OBJECT_TYPE);
|
||||
m.load(3, InstructionAdapter.OBJECT_TYPE);
|
||||
m.load(4, InstructionAdapter.OBJECT_TYPE);
|
||||
m.load(5, InstructionAdapter.OBJECT_TYPE);
|
||||
m.invokeinterface(Type.getInternalName(IFastCacheLike.class), "c2me$cache", Type.getMethodDescriptor(Type.VOID_TYPE, new Type[]{Type.getType(double[].class), Type.getType(int[].class), Type.getType(int[].class), Type.getType(int[].class), Type.getType(EvalType.class)}));
|
||||
m.areturn(Type.VOID_TYPE);
|
||||
}
|
||||
|
||||
private void genPostprocessingMethod(@NotNull Context context, String cacheLikeField) {
|
||||
String methodName = String.format("postProcessing_%s", cacheLikeField);
|
||||
String delegateSingle = context.newSingleMethod(this.delegate);
|
||||
String delegateMulti = context.newMultiMethod(this.delegate);
|
||||
context.genPostprocessingMethod(methodName, (m) -> {
|
||||
Label cacheExists = new Label();
|
||||
m.load(0, InstructionAdapter.OBJECT_TYPE);
|
||||
m.load(0, InstructionAdapter.OBJECT_TYPE);
|
||||
m.getfield(context.className, cacheLikeField, Type.getDescriptor(IFastCacheLike.class));
|
||||
m.dup();
|
||||
m.ifnonnull(cacheExists);
|
||||
m.pop();
|
||||
m.pop();
|
||||
m.areturn(Type.VOID_TYPE);
|
||||
m.visitLabel(cacheExists);
|
||||
m.anew(Type.getType(SubCompiledDensityFunction.class));
|
||||
m.dup();
|
||||
m.load(0, InstructionAdapter.OBJECT_TYPE);
|
||||
m.invokedynamic("evalSingle", Type.getMethodDescriptor(Type.getType(ISingleMethod.class), new Type[]{Type.getType(context.classDesc)}), new Handle(6, "java/lang/invoke/LambdaMetafactory", "metafactory", "(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;", false), new Object[]{Type.getMethodType(Context.SINGLE_DESC), new Handle(5, context.className, delegateSingle, Context.SINGLE_DESC, false), Type.getMethodType(Context.SINGLE_DESC)});
|
||||
m.load(0, InstructionAdapter.OBJECT_TYPE);
|
||||
m.invokedynamic("evalMulti", Type.getMethodDescriptor(Type.getType(IMultiMethod.class), new Type[]{Type.getType(context.classDesc)}), new Handle(6, "java/lang/invoke/LambdaMetafactory", "metafactory", "(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;", false), new Object[]{Type.getMethodType(Context.MULTI_DESC), new Handle(5, context.className, delegateMulti, Context.MULTI_DESC, false), Type.getMethodType(Context.MULTI_DESC)});
|
||||
m.load(0, InstructionAdapter.OBJECT_TYPE);
|
||||
m.getfield(context.className, cacheLikeField, Type.getDescriptor(IFastCacheLike.class));
|
||||
m.checkcast(Type.getType(DensityFunction.class));
|
||||
m.invokespecial(Type.getInternalName(SubCompiledDensityFunction.class), "<init>", Type.getMethodDescriptor(Type.VOID_TYPE, new Type[]{Type.getType(ISingleMethod.class), Type.getType(IMultiMethod.class), Type.getType(DensityFunction.class)}), false);
|
||||
m.checkcast(Type.getType(DensityFunction.class));
|
||||
m.invokeinterface(Type.getInternalName(IFastCacheLike.class), "c2me$withDelegate", Type.getMethodDescriptor(Type.getType(DensityFunction.class), new Type[]{Type.getType(DensityFunction.class)}));
|
||||
m.putfield(context.className, cacheLikeField, Type.getDescriptor(IFastCacheLike.class));
|
||||
m.areturn(Type.VOID_TYPE);
|
||||
});
|
||||
}
|
||||
|
||||
public IFastCacheLike getCacheLike() {
|
||||
return this.cacheLike;
|
||||
}
|
||||
|
||||
public AstNode getDelegate() {
|
||||
return this.delegate;
|
||||
}
|
||||
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
} else if (o != null && this.getClass() == o.getClass()) {
|
||||
CacheLikeNode that = (CacheLikeNode)o;
|
||||
return equals(this.cacheLike, that.cacheLike) && Objects.equals(this.delegate, that.delegate);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean equals(IFastCacheLike a, IFastCacheLike b) {
|
||||
if (a instanceof DensityFunctions.Marker wrappingA) {
|
||||
if (b instanceof DensityFunctions.Marker wrappingB) {
|
||||
return wrappingA.type() == wrappingB.type();
|
||||
}
|
||||
}
|
||||
|
||||
return a.equals(b);
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
int result = 1;
|
||||
result = 31 * result + this.getClass().hashCode();
|
||||
result = 31 * result + hashCode(this.cacheLike);
|
||||
result = 31 * result + this.delegate.hashCode();
|
||||
return result;
|
||||
}
|
||||
|
||||
private static int hashCode(IFastCacheLike o) {
|
||||
if (o instanceof DensityFunctions.Marker wrapping) {
|
||||
return wrapping.type().hashCode();
|
||||
} else {
|
||||
return o.hashCode();
|
||||
}
|
||||
}
|
||||
|
||||
public boolean relaxedEquals(AstNode o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
} else if (o != null && this.getClass() == o.getClass()) {
|
||||
CacheLikeNode that = (CacheLikeNode)o;
|
||||
return relaxedEquals(this.cacheLike, that.cacheLike) && this.delegate.relaxedEquals(that.delegate);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean relaxedEquals(IFastCacheLike a, IFastCacheLike b) {
|
||||
if (a instanceof DensityFunctions.Marker wrappingA) {
|
||||
if (b instanceof DensityFunctions.Marker wrappingB) {
|
||||
return wrappingA.type() == wrappingB.type();
|
||||
}
|
||||
}
|
||||
|
||||
return a.getClass() == b.getClass();
|
||||
}
|
||||
|
||||
public int relaxedHashCode() {
|
||||
int result = 1;
|
||||
result = 31 * result + this.getClass().hashCode();
|
||||
result = 31 * result + relaxedHashCode(this.cacheLike);
|
||||
result = 31 * result + this.delegate.relaxedHashCode();
|
||||
return result;
|
||||
}
|
||||
|
||||
private static int relaxedHashCode(IFastCacheLike o) {
|
||||
if (o instanceof DensityFunctions.Marker wrapping) {
|
||||
return wrapping.type().hashCode();
|
||||
} else {
|
||||
return o.getClass().hashCode();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
package org.bxteam.divinemc.dfc.common.ast.misc;
|
||||
|
||||
import org.bxteam.divinemc.dfc.common.ast.AstNode;
|
||||
import org.bxteam.divinemc.dfc.common.ast.AstTransformer;
|
||||
import org.bxteam.divinemc.dfc.common.ast.EvalType;
|
||||
import org.bxteam.divinemc.dfc.common.gen.BytecodeGen;
|
||||
import java.util.Arrays;
|
||||
import org.objectweb.asm.Type;
|
||||
import org.objectweb.asm.commons.InstructionAdapter;
|
||||
|
||||
public class ConstantNode implements AstNode {
|
||||
private final double value;
|
||||
|
||||
public ConstantNode(double value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public double evalSingle(int x, int y, int z, EvalType type) {
|
||||
return this.value;
|
||||
}
|
||||
|
||||
public void evalMulti(double[] res, int[] x, int[] y, int[] z, EvalType type) {
|
||||
Arrays.fill(res, this.value);
|
||||
}
|
||||
|
||||
public AstNode[] getChildren() {
|
||||
return new AstNode[0];
|
||||
}
|
||||
|
||||
public AstNode transform(AstTransformer transformer) {
|
||||
return transformer.transform(this);
|
||||
}
|
||||
|
||||
public void doBytecodeGenSingle(BytecodeGen.Context context, InstructionAdapter m, BytecodeGen.Context.LocalVarConsumer localVarConsumer) {
|
||||
m.dconst(this.value);
|
||||
m.areturn(Type.DOUBLE_TYPE);
|
||||
}
|
||||
|
||||
public void doBytecodeGenMulti(BytecodeGen.Context context, InstructionAdapter m, BytecodeGen.Context.LocalVarConsumer localVarConsumer) {
|
||||
m.load(1, InstructionAdapter.OBJECT_TYPE);
|
||||
m.dconst(this.value);
|
||||
m.invokestatic(Type.getInternalName(Arrays.class), "fill", Type.getMethodDescriptor(Type.VOID_TYPE, new Type[]{Type.getType(double[].class), Type.DOUBLE_TYPE}), false);
|
||||
m.areturn(Type.VOID_TYPE);
|
||||
}
|
||||
|
||||
public double getValue() {
|
||||
return this.value;
|
||||
}
|
||||
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
} else if (o != null && this.getClass() == o.getClass()) {
|
||||
ConstantNode that = (ConstantNode)o;
|
||||
return Double.compare(this.value, that.value) == 0;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
return Double.hashCode(this.value);
|
||||
}
|
||||
|
||||
public boolean relaxedEquals(AstNode o) {
|
||||
return this.equals(o);
|
||||
}
|
||||
|
||||
public int relaxedHashCode() {
|
||||
return this.hashCode();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,140 @@
|
||||
package org.bxteam.divinemc.dfc.common.ast.misc;
|
||||
|
||||
import org.bxteam.divinemc.dfc.common.ast.AstNode;
|
||||
import org.bxteam.divinemc.dfc.common.ast.AstTransformer;
|
||||
import org.bxteam.divinemc.dfc.common.ast.EvalType;
|
||||
import org.bxteam.divinemc.dfc.common.gen.BytecodeGen;
|
||||
import org.bxteam.divinemc.dfc.common.util.ArrayCache;
|
||||
import org.bxteam.divinemc.dfc.common.vif.EachApplierVanillaInterface;
|
||||
import org.bxteam.divinemc.dfc.common.vif.NoisePosVanillaInterface;
|
||||
import java.util.Objects;
|
||||
import net.minecraft.world.level.levelgen.DensityFunction;
|
||||
import org.objectweb.asm.Label;
|
||||
import org.objectweb.asm.Type;
|
||||
import org.objectweb.asm.commons.InstructionAdapter;
|
||||
|
||||
public class DelegateNode implements AstNode {
|
||||
private final DensityFunction densityFunction;
|
||||
|
||||
public DelegateNode(DensityFunction densityFunction) {
|
||||
this.densityFunction = Objects.requireNonNull(densityFunction);
|
||||
}
|
||||
|
||||
public double evalSingle(int x, int y, int z, EvalType type) {
|
||||
return this.densityFunction.compute(new NoisePosVanillaInterface(x, y, z, type));
|
||||
}
|
||||
|
||||
public void evalMulti(double[] res, int[] x, int[] y, int[] z, EvalType type) {
|
||||
if (res.length == 1) {
|
||||
res[0] = this.evalSingle(x[0], y[0], z[0], type);
|
||||
} else {
|
||||
this.densityFunction.fillArray(res, new EachApplierVanillaInterface(x, y, z, type));
|
||||
}
|
||||
}
|
||||
|
||||
public AstNode[] getChildren() {
|
||||
return new AstNode[0];
|
||||
}
|
||||
|
||||
public AstNode transform(AstTransformer transformer) {
|
||||
return transformer.transform(this);
|
||||
}
|
||||
|
||||
public void doBytecodeGenSingle(BytecodeGen.Context context, InstructionAdapter m, BytecodeGen.Context.LocalVarConsumer localVarConsumer) {
|
||||
String newField = context.newField(DensityFunction.class, this.densityFunction);
|
||||
m.load(0, InstructionAdapter.OBJECT_TYPE);
|
||||
m.getfield(context.className, newField, Type.getDescriptor(DensityFunction.class));
|
||||
m.anew(Type.getType(NoisePosVanillaInterface.class));
|
||||
m.dup();
|
||||
m.load(1, Type.INT_TYPE);
|
||||
m.load(2, Type.INT_TYPE);
|
||||
m.load(3, Type.INT_TYPE);
|
||||
m.load(4, InstructionAdapter.OBJECT_TYPE);
|
||||
m.invokespecial(Type.getInternalName(NoisePosVanillaInterface.class), "<init>", Type.getMethodDescriptor(Type.VOID_TYPE, Type.INT_TYPE, Type.INT_TYPE, Type.INT_TYPE, Type.getType(EvalType.class)), false);
|
||||
m.invokeinterface(Type.getInternalName(DensityFunction.class), "compute", Type.getMethodDescriptor(Type.DOUBLE_TYPE, Type.getType(DensityFunction.FunctionContext.class)));
|
||||
m.areturn(Type.DOUBLE_TYPE);
|
||||
}
|
||||
|
||||
public void doBytecodeGenMulti(BytecodeGen.Context context, InstructionAdapter m, BytecodeGen.Context.LocalVarConsumer localVarConsumer) {
|
||||
String newField = context.newField(DensityFunction.class, this.densityFunction);
|
||||
Label moreThanTwoLabel = new Label();
|
||||
m.load(1, InstructionAdapter.OBJECT_TYPE);
|
||||
m.arraylength();
|
||||
m.iconst(1);
|
||||
m.ificmpgt(moreThanTwoLabel);
|
||||
m.load(1, InstructionAdapter.OBJECT_TYPE);
|
||||
m.iconst(0);
|
||||
m.load(0, InstructionAdapter.OBJECT_TYPE);
|
||||
m.getfield(context.className, newField, Type.getDescriptor(DensityFunction.class));
|
||||
m.anew(Type.getType(NoisePosVanillaInterface.class));
|
||||
m.dup();
|
||||
m.load(2, InstructionAdapter.OBJECT_TYPE);
|
||||
m.iconst(0);
|
||||
m.aload(Type.INT_TYPE);
|
||||
m.load(3, InstructionAdapter.OBJECT_TYPE);
|
||||
m.iconst(0);
|
||||
m.aload(Type.INT_TYPE);
|
||||
m.load(4, InstructionAdapter.OBJECT_TYPE);
|
||||
m.iconst(0);
|
||||
m.aload(Type.INT_TYPE);
|
||||
m.load(5, InstructionAdapter.OBJECT_TYPE);
|
||||
m.invokespecial(Type.getInternalName(NoisePosVanillaInterface.class), "<init>", Type.getMethodDescriptor(Type.VOID_TYPE, Type.INT_TYPE, Type.INT_TYPE, Type.INT_TYPE, Type.getType(EvalType.class)), false);
|
||||
m.invokeinterface(Type.getInternalName(DensityFunction.class), "compute", Type.getMethodDescriptor(Type.DOUBLE_TYPE, Type.getType(DensityFunction.FunctionContext.class)));
|
||||
m.astore(Type.DOUBLE_TYPE);
|
||||
m.areturn(Type.VOID_TYPE);
|
||||
m.visitLabel(moreThanTwoLabel);
|
||||
m.load(0, InstructionAdapter.OBJECT_TYPE);
|
||||
m.getfield(context.className, newField, Type.getDescriptor(DensityFunction.class));
|
||||
m.load(1, InstructionAdapter.OBJECT_TYPE);
|
||||
m.anew(Type.getType(EachApplierVanillaInterface.class));
|
||||
m.dup();
|
||||
m.load(2, InstructionAdapter.OBJECT_TYPE);
|
||||
m.load(3, InstructionAdapter.OBJECT_TYPE);
|
||||
m.load(4, InstructionAdapter.OBJECT_TYPE);
|
||||
m.load(5, InstructionAdapter.OBJECT_TYPE);
|
||||
m.load(6, InstructionAdapter.OBJECT_TYPE);
|
||||
m.invokespecial(Type.getInternalName(EachApplierVanillaInterface.class), "<init>", Type.getMethodDescriptor(Type.VOID_TYPE, Type.getType(int[].class), Type.getType(int[].class), Type.getType(int[].class), Type.getType(EvalType.class), Type.getType(ArrayCache.class)), false);
|
||||
m.invokeinterface(Type.getInternalName(DensityFunction.class), "fillArray", Type.getMethodDescriptor(Type.VOID_TYPE, Type.getType(double[].class), Type.getType(DensityFunction.ContextProvider.class)));
|
||||
m.areturn(Type.VOID_TYPE);
|
||||
}
|
||||
|
||||
public DensityFunction getDelegate() {
|
||||
return this.densityFunction;
|
||||
}
|
||||
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
} else if (o != null && this.getClass() == o.getClass()) {
|
||||
DelegateNode that = (DelegateNode)o;
|
||||
return Objects.equals(this.densityFunction, that.densityFunction);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
int result = 1;
|
||||
result = 31 * result + Objects.hashCode(this.getClass());
|
||||
result = 31 * result + Objects.hashCode(this.densityFunction);
|
||||
return result;
|
||||
}
|
||||
|
||||
public boolean relaxedEquals(AstNode o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
} else if (o != null && this.getClass() == o.getClass()) {
|
||||
DelegateNode that = (DelegateNode)o;
|
||||
return this.densityFunction.getClass() == that.densityFunction.getClass();
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public int relaxedHashCode() {
|
||||
int result = 1;
|
||||
result = 31 * result + Objects.hashCode(this.getClass());
|
||||
result = 31 * result + Objects.hashCode(this.densityFunction.getClass());
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,337 @@
|
||||
package org.bxteam.divinemc.dfc.common.ast.misc;
|
||||
|
||||
import org.bxteam.divinemc.dfc.common.ast.AstNode;
|
||||
import org.bxteam.divinemc.dfc.common.ast.AstTransformer;
|
||||
import org.bxteam.divinemc.dfc.common.ast.EvalType;
|
||||
import org.bxteam.divinemc.dfc.common.gen.BytecodeGen;
|
||||
import org.bxteam.divinemc.dfc.common.gen.BytecodeGen.Context;
|
||||
import java.util.Objects;
|
||||
import org.objectweb.asm.Label;
|
||||
import org.objectweb.asm.Type;
|
||||
import org.objectweb.asm.commons.InstructionAdapter;
|
||||
|
||||
public class RangeChoiceNode implements AstNode {
|
||||
private final AstNode input;
|
||||
private final double minInclusive;
|
||||
private final double maxExclusive;
|
||||
private final AstNode whenInRange;
|
||||
private final AstNode whenOutOfRange;
|
||||
|
||||
public RangeChoiceNode(AstNode input, double minInclusive, double maxExclusive, AstNode whenInRange, AstNode whenOutOfRange) {
|
||||
this.input = (AstNode)Objects.requireNonNull(input);
|
||||
this.minInclusive = minInclusive;
|
||||
this.maxExclusive = maxExclusive;
|
||||
this.whenInRange = (AstNode)Objects.requireNonNull(whenInRange);
|
||||
this.whenOutOfRange = (AstNode)Objects.requireNonNull(whenOutOfRange);
|
||||
}
|
||||
|
||||
public double evalSingle(int x, int y, int z, EvalType type) {
|
||||
double v = this.input.evalSingle(x, y, z, type);
|
||||
return v >= this.minInclusive && v < this.maxExclusive ? this.whenInRange.evalSingle(x, y, z, type) : this.whenOutOfRange.evalSingle(x, y, z, type);
|
||||
}
|
||||
|
||||
public void evalMulti(double[] res, int[] x, int[] y, int[] z, EvalType type) {
|
||||
this.input.evalMulti(res, x, y, z, type);
|
||||
int numInRange = 0;
|
||||
|
||||
int numOutOfRange;
|
||||
for(numOutOfRange = 0; numOutOfRange < x.length; ++numOutOfRange) {
|
||||
double v = res[numOutOfRange];
|
||||
if (v >= this.minInclusive && v < this.maxExclusive) {
|
||||
++numInRange;
|
||||
}
|
||||
}
|
||||
|
||||
numOutOfRange = res.length - numInRange;
|
||||
if (numInRange == 0) {
|
||||
this.evalChildMulti(this.whenOutOfRange, res, x, y, z, type);
|
||||
} else if (numInRange == res.length) {
|
||||
this.evalChildMulti(this.whenInRange, res, x, y, z, type);
|
||||
} else {
|
||||
int idx1 = 0;
|
||||
int[] i1 = new int[numInRange];
|
||||
double[] res1 = new double[numInRange];
|
||||
int[] x1 = new int[numInRange];
|
||||
int[] y1 = new int[numInRange];
|
||||
int[] z1 = new int[numInRange];
|
||||
int idx2 = 0;
|
||||
int[] i2 = new int[numOutOfRange];
|
||||
double[] res2 = new double[numOutOfRange];
|
||||
int[] x2 = new int[numOutOfRange];
|
||||
int[] y2 = new int[numOutOfRange];
|
||||
int[] z2 = new int[numOutOfRange];
|
||||
|
||||
int i;
|
||||
for(i = 0; i < res.length; ++i) {
|
||||
double v = res[i];
|
||||
int index;
|
||||
if (v >= this.minInclusive && v < this.maxExclusive) {
|
||||
index = idx1++;
|
||||
i1[index] = i;
|
||||
x1[index] = x[i];
|
||||
y1[index] = y[i];
|
||||
z1[index] = z[i];
|
||||
} else {
|
||||
index = idx2++;
|
||||
i2[index] = i;
|
||||
x2[index] = x[i];
|
||||
y2[index] = y[i];
|
||||
z2[index] = z[i];
|
||||
}
|
||||
}
|
||||
|
||||
this.evalChildMulti(this.whenInRange, res1, x1, y1, z1, type);
|
||||
this.evalChildMulti(this.whenOutOfRange, res2, x2, y2, z2, type);
|
||||
|
||||
for(i = 0; i < numInRange; ++i) {
|
||||
res[i1[i]] = res1[i];
|
||||
}
|
||||
|
||||
for(i = 0; i < numOutOfRange; ++i) {
|
||||
res[i2[i]] = res2[i];
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static void evalMultiStatic(double[] res, int[] x, int[] y, int[] z, EvalType type, double minInclusive, double maxExclusive, BytecodeGen.EvalSingleInterface whenInRangeSingle, BytecodeGen.EvalSingleInterface whenOutOfRangeSingle, BytecodeGen.EvalMultiInterface inputMulti, BytecodeGen.EvalMultiInterface whenInRangeMulti, BytecodeGen.EvalMultiInterface whenOutOfRangeMulti) {
|
||||
inputMulti.evalMulti(res, x, y, z, type);
|
||||
int numInRange = 0;
|
||||
|
||||
int numOutOfRange;
|
||||
for(numOutOfRange = 0; numOutOfRange < x.length; ++numOutOfRange) {
|
||||
double v = res[numOutOfRange];
|
||||
if (v >= minInclusive && v < maxExclusive) {
|
||||
++numInRange;
|
||||
}
|
||||
}
|
||||
|
||||
numOutOfRange = res.length - numInRange;
|
||||
if (numInRange == 0) {
|
||||
evalChildMulti(whenOutOfRangeSingle, whenOutOfRangeMulti, res, x, y, z, type);
|
||||
} else if (numInRange == res.length) {
|
||||
evalChildMulti(whenInRangeSingle, whenInRangeMulti, res, x, y, z, type);
|
||||
} else {
|
||||
int idx1 = 0;
|
||||
int[] i1 = new int[numInRange];
|
||||
double[] res1 = new double[numInRange];
|
||||
int[] x1 = new int[numInRange];
|
||||
int[] y1 = new int[numInRange];
|
||||
int[] z1 = new int[numInRange];
|
||||
int idx2 = 0;
|
||||
int[] i2 = new int[numOutOfRange];
|
||||
double[] res2 = new double[numOutOfRange];
|
||||
int[] x2 = new int[numOutOfRange];
|
||||
int[] y2 = new int[numOutOfRange];
|
||||
int[] z2 = new int[numOutOfRange];
|
||||
|
||||
int i;
|
||||
for(i = 0; i < res.length; ++i) {
|
||||
double v = res[i];
|
||||
int index;
|
||||
if (v >= minInclusive && v < maxExclusive) {
|
||||
index = idx1++;
|
||||
i1[index] = i;
|
||||
x1[index] = x[i];
|
||||
y1[index] = y[i];
|
||||
z1[index] = z[i];
|
||||
} else {
|
||||
index = idx2++;
|
||||
i2[index] = i;
|
||||
x2[index] = x[i];
|
||||
y2[index] = y[i];
|
||||
z2[index] = z[i];
|
||||
}
|
||||
}
|
||||
|
||||
evalChildMulti(whenInRangeSingle, whenInRangeMulti, res1, x1, y1, z1, type);
|
||||
evalChildMulti(whenOutOfRangeSingle, whenOutOfRangeMulti, res2, x2, y2, z2, type);
|
||||
|
||||
for(i = 0; i < numInRange; ++i) {
|
||||
res[i1[i]] = res1[i];
|
||||
}
|
||||
|
||||
for(i = 0; i < numOutOfRange; ++i) {
|
||||
res[i2[i]] = res2[i];
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static void evalChildMulti(BytecodeGen.EvalSingleInterface single, BytecodeGen.EvalMultiInterface multi, double[] res, int[] x, int[] y, int[] z, EvalType type) {
|
||||
if (res.length == 1) {
|
||||
res[0] = single.evalSingle(x[0], y[0], z[0], type);
|
||||
} else {
|
||||
multi.evalMulti(res, x, y, z, type);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void evalChildMulti(AstNode child, double[] res, int[] x, int[] y, int[] z, EvalType type) {
|
||||
if (res.length == 1) {
|
||||
res[0] = child.evalSingle(x[0], y[0], z[0], type);
|
||||
} else {
|
||||
child.evalMulti(res, x, y, z, type);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public AstNode[] getChildren() {
|
||||
return new AstNode[]{this.input, this.whenInRange, this.whenOutOfRange};
|
||||
}
|
||||
|
||||
public AstNode transform(AstTransformer transformer) {
|
||||
AstNode input = this.input.transform(transformer);
|
||||
AstNode whenInRange = this.whenInRange.transform(transformer);
|
||||
AstNode whenOutOfRange = this.whenOutOfRange.transform(transformer);
|
||||
return this.input == input && this.whenInRange == whenInRange && this.whenOutOfRange == whenOutOfRange ? transformer.transform(this) : transformer.transform(new RangeChoiceNode(input, this.minInclusive, this.maxExclusive, whenInRange, whenOutOfRange));
|
||||
}
|
||||
|
||||
public void doBytecodeGenSingle(Context context, InstructionAdapter m, Context.LocalVarConsumer localVarConsumer) {
|
||||
String inputMethod = context.newSingleMethod(this.input);
|
||||
String whenInRangeMethod = context.newSingleMethod(this.whenInRange);
|
||||
String whenOutOfRangeMethod = context.newSingleMethod(this.whenOutOfRange);
|
||||
int inputValue = localVarConsumer.createLocalVariable("inputValue", Type.DOUBLE_TYPE.getDescriptor());
|
||||
context.callDelegateSingle(m, inputMethod);
|
||||
m.store(inputValue, Type.DOUBLE_TYPE);
|
||||
Label whenOutOfRangeLabel = new Label();
|
||||
Label end = new Label();
|
||||
m.load(inputValue, Type.DOUBLE_TYPE);
|
||||
m.dconst(this.minInclusive);
|
||||
m.cmpl(Type.DOUBLE_TYPE);
|
||||
m.iflt(whenOutOfRangeLabel);
|
||||
m.load(inputValue, Type.DOUBLE_TYPE);
|
||||
m.dconst(this.maxExclusive);
|
||||
m.cmpg(Type.DOUBLE_TYPE);
|
||||
m.ifge(whenOutOfRangeLabel);
|
||||
if (whenInRangeMethod.equals(inputMethod)) {
|
||||
m.load(inputValue, Type.DOUBLE_TYPE);
|
||||
} else {
|
||||
context.callDelegateSingle(m, whenInRangeMethod);
|
||||
}
|
||||
|
||||
m.goTo(end);
|
||||
m.visitLabel(whenOutOfRangeLabel);
|
||||
if (whenOutOfRangeMethod.equals(inputMethod)) {
|
||||
m.load(inputValue, Type.DOUBLE_TYPE);
|
||||
} else {
|
||||
context.callDelegateSingle(m, whenOutOfRangeMethod);
|
||||
}
|
||||
|
||||
m.visitLabel(end);
|
||||
m.areturn(Type.DOUBLE_TYPE);
|
||||
}
|
||||
|
||||
public void doBytecodeGenMulti(Context context, InstructionAdapter m, Context.LocalVarConsumer localVarConsumer) {
|
||||
String inputSingle = context.newSingleMethod(this.input);
|
||||
String whenInRangeSingle = context.newSingleMethod(this.whenInRange);
|
||||
String whenOutOfRangeSingle = context.newSingleMethod(this.whenOutOfRange);
|
||||
String inputMulti = context.newMultiMethod(this.input);
|
||||
context.callDelegateMulti(m, inputMulti);
|
||||
context.doCountedLoop(m, localVarConsumer, (idx) -> {
|
||||
Label whenOutOfRangeLabel = new Label();
|
||||
Label end = new Label();
|
||||
m.load(1, InstructionAdapter.OBJECT_TYPE);
|
||||
m.load(idx, Type.INT_TYPE);
|
||||
m.load(1, InstructionAdapter.OBJECT_TYPE);
|
||||
m.load(idx, Type.INT_TYPE);
|
||||
m.aload(Type.DOUBLE_TYPE);
|
||||
m.dconst(this.minInclusive);
|
||||
m.cmpl(Type.DOUBLE_TYPE);
|
||||
m.iflt(whenOutOfRangeLabel);
|
||||
m.load(1, InstructionAdapter.OBJECT_TYPE);
|
||||
m.load(idx, Type.INT_TYPE);
|
||||
m.aload(Type.DOUBLE_TYPE);
|
||||
m.dconst(this.maxExclusive);
|
||||
m.cmpg(Type.DOUBLE_TYPE);
|
||||
m.ifge(whenOutOfRangeLabel);
|
||||
if (whenInRangeSingle.equals(inputSingle)) {
|
||||
m.load(1, InstructionAdapter.OBJECT_TYPE);
|
||||
m.load(idx, Type.INT_TYPE);
|
||||
m.aload(Type.DOUBLE_TYPE);
|
||||
} else {
|
||||
m.load(0, InstructionAdapter.OBJECT_TYPE);
|
||||
m.load(2, InstructionAdapter.OBJECT_TYPE);
|
||||
m.load(idx, Type.INT_TYPE);
|
||||
m.aload(Type.INT_TYPE);
|
||||
m.load(3, InstructionAdapter.OBJECT_TYPE);
|
||||
m.load(idx, Type.INT_TYPE);
|
||||
m.aload(Type.INT_TYPE);
|
||||
m.load(4, InstructionAdapter.OBJECT_TYPE);
|
||||
m.load(idx, Type.INT_TYPE);
|
||||
m.aload(Type.INT_TYPE);
|
||||
m.load(5, InstructionAdapter.OBJECT_TYPE);
|
||||
m.invokevirtual(context.className, whenInRangeSingle, Context.SINGLE_DESC, false);
|
||||
}
|
||||
|
||||
m.goTo(end);
|
||||
m.visitLabel(whenOutOfRangeLabel);
|
||||
if (whenOutOfRangeSingle.equals(inputSingle)) {
|
||||
m.load(1, InstructionAdapter.OBJECT_TYPE);
|
||||
m.load(idx, Type.INT_TYPE);
|
||||
m.aload(Type.DOUBLE_TYPE);
|
||||
} else {
|
||||
m.load(0, InstructionAdapter.OBJECT_TYPE);
|
||||
m.load(2, InstructionAdapter.OBJECT_TYPE);
|
||||
m.load(idx, Type.INT_TYPE);
|
||||
m.aload(Type.INT_TYPE);
|
||||
m.load(3, InstructionAdapter.OBJECT_TYPE);
|
||||
m.load(idx, Type.INT_TYPE);
|
||||
m.aload(Type.INT_TYPE);
|
||||
m.load(4, InstructionAdapter.OBJECT_TYPE);
|
||||
m.load(idx, Type.INT_TYPE);
|
||||
m.aload(Type.INT_TYPE);
|
||||
m.load(5, InstructionAdapter.OBJECT_TYPE);
|
||||
m.invokevirtual(context.className, whenOutOfRangeSingle, Context.SINGLE_DESC, false);
|
||||
}
|
||||
|
||||
m.visitLabel(end);
|
||||
m.astore(Type.DOUBLE_TYPE);
|
||||
});
|
||||
m.areturn(Type.VOID_TYPE);
|
||||
}
|
||||
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
} else if (o != null && this.getClass() == o.getClass()) {
|
||||
RangeChoiceNode that = (RangeChoiceNode)o;
|
||||
return Double.compare(this.minInclusive, that.minInclusive) == 0 && Double.compare(this.maxExclusive, that.maxExclusive) == 0 && Objects.equals(this.input, that.input) && Objects.equals(this.whenInRange, that.whenInRange) && Objects.equals(this.whenOutOfRange, that.whenOutOfRange);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
int result = 1;
|
||||
result = 31 * result + this.getClass().hashCode();
|
||||
result = 31 * result + this.input.hashCode();
|
||||
result = 31 * result + Double.hashCode(this.minInclusive);
|
||||
result = 31 * result + Double.hashCode(this.maxExclusive);
|
||||
result = 31 * result + this.whenInRange.hashCode();
|
||||
result = 31 * result + this.whenOutOfRange.hashCode();
|
||||
return result;
|
||||
}
|
||||
|
||||
public boolean relaxedEquals(AstNode o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
} else if (o != null && this.getClass() == o.getClass()) {
|
||||
RangeChoiceNode that = (RangeChoiceNode)o;
|
||||
return Double.compare(this.minInclusive, that.minInclusive) == 0 && Double.compare(this.maxExclusive, that.maxExclusive) == 0 && this.input.relaxedEquals(that.input) && this.whenInRange.relaxedEquals(that.whenInRange) && this.whenOutOfRange.relaxedEquals(that.whenOutOfRange);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public int relaxedHashCode() {
|
||||
int result = 1;
|
||||
result = 31 * result + this.getClass().hashCode();
|
||||
result = 31 * result + this.input.relaxedHashCode();
|
||||
result = 31 * result + Double.hashCode(this.minInclusive);
|
||||
result = 31 * result + Double.hashCode(this.maxExclusive);
|
||||
result = 31 * result + this.whenInRange.relaxedHashCode();
|
||||
result = 31 * result + this.whenOutOfRange.relaxedHashCode();
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
package org.bxteam.divinemc.dfc.common.ast.misc;
|
||||
|
||||
import org.bxteam.divinemc.dfc.common.ast.AstNode;
|
||||
import org.bxteam.divinemc.dfc.common.ast.AstTransformer;
|
||||
import org.bxteam.divinemc.dfc.common.ast.EvalType;
|
||||
import org.bxteam.divinemc.dfc.common.gen.BytecodeGen;
|
||||
import java.util.Objects;
|
||||
import org.objectweb.asm.Type;
|
||||
import org.objectweb.asm.commons.InstructionAdapter;
|
||||
|
||||
public class RootNode implements AstNode {
|
||||
private final AstNode next;
|
||||
|
||||
public RootNode(AstNode next) {
|
||||
this.next = (AstNode)Objects.requireNonNull(next);
|
||||
}
|
||||
|
||||
public double evalSingle(int x, int y, int z, EvalType type) {
|
||||
return this.next.evalSingle(x, y, z, type);
|
||||
}
|
||||
|
||||
public void evalMulti(double[] res, int[] x, int[] y, int[] z, EvalType type) {
|
||||
this.next.evalMulti(res, x, y, z, type);
|
||||
}
|
||||
|
||||
public AstNode[] getChildren() {
|
||||
return new AstNode[]{this.next};
|
||||
}
|
||||
|
||||
public AstNode transform(AstTransformer transformer) {
|
||||
AstNode next = this.next.transform(transformer);
|
||||
return next == this.next ? transformer.transform(this) : transformer.transform(new RootNode(next));
|
||||
}
|
||||
|
||||
public void doBytecodeGenSingle(BytecodeGen.Context context, InstructionAdapter m, BytecodeGen.Context.LocalVarConsumer localVarConsumer) {
|
||||
String nextMethod = context.newSingleMethod(this.next);
|
||||
context.callDelegateSingle(m, nextMethod);
|
||||
m.areturn(Type.DOUBLE_TYPE);
|
||||
}
|
||||
|
||||
public void doBytecodeGenMulti(BytecodeGen.Context context, InstructionAdapter m, BytecodeGen.Context.LocalVarConsumer localVarConsumer) {
|
||||
String nextMethod = context.newMultiMethod(this.next);
|
||||
context.callDelegateMulti(m, nextMethod);
|
||||
m.areturn(Type.VOID_TYPE);
|
||||
}
|
||||
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
} else if (o != null && this.getClass() == o.getClass()) {
|
||||
RootNode that = (RootNode)o;
|
||||
return Objects.equals(this.next, that.next);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
int result = 1;
|
||||
result = 31 * result + this.getClass().hashCode();
|
||||
result = 31 * result + this.next.hashCode();
|
||||
return result;
|
||||
}
|
||||
|
||||
public boolean relaxedEquals(AstNode o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
} else if (o != null && this.getClass() == o.getClass()) {
|
||||
RootNode that = (RootNode)o;
|
||||
return this.next.relaxedEquals(that.next);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public int relaxedHashCode() {
|
||||
int result = 1;
|
||||
result = 31 * result + this.getClass().hashCode();
|
||||
result = 31 * result + this.next.relaxedHashCode();
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
package org.bxteam.divinemc.dfc.common.ast.misc;
|
||||
|
||||
import org.bxteam.divinemc.dfc.common.ast.AstNode;
|
||||
import org.bxteam.divinemc.dfc.common.ast.AstTransformer;
|
||||
import org.bxteam.divinemc.dfc.common.ast.EvalType;
|
||||
import org.bxteam.divinemc.dfc.common.gen.BytecodeGen;
|
||||
import net.minecraft.util.Mth;
|
||||
import org.objectweb.asm.Type;
|
||||
import org.objectweb.asm.commons.InstructionAdapter;
|
||||
|
||||
public class YClampedGradientNode implements AstNode {
|
||||
private final double fromY;
|
||||
private final double toY;
|
||||
private final double fromValue;
|
||||
private final double toValue;
|
||||
|
||||
public YClampedGradientNode(double fromY, double toY, double fromValue, double toValue) {
|
||||
this.fromY = fromY;
|
||||
this.toY = toY;
|
||||
this.fromValue = fromValue;
|
||||
this.toValue = toValue;
|
||||
}
|
||||
|
||||
public double evalSingle(int x, int y, int z, EvalType type) {
|
||||
return Mth.clampedMap((double)y, this.fromY, this.toY, this.fromValue, this.toValue);
|
||||
}
|
||||
|
||||
public void evalMulti(double[] res, int[] x, int[] y, int[] z, EvalType type) {
|
||||
for(int i = 0; i < res.length; ++i) {
|
||||
res[i] = Mth.clampedMap((double)y[i], this.fromY, this.toY, this.fromValue, this.toValue);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public AstNode[] getChildren() {
|
||||
return new AstNode[0];
|
||||
}
|
||||
|
||||
public AstNode transform(AstTransformer transformer) {
|
||||
return transformer.transform(this);
|
||||
}
|
||||
|
||||
public void doBytecodeGenSingle(BytecodeGen.Context context, InstructionAdapter m, BytecodeGen.Context.LocalVarConsumer localVarConsumer) {
|
||||
m.load(2, Type.INT_TYPE);
|
||||
m.cast(Type.INT_TYPE, Type.DOUBLE_TYPE);
|
||||
m.dconst(this.fromY);
|
||||
m.dconst(this.toY);
|
||||
m.dconst(this.fromValue);
|
||||
m.dconst(this.toValue);
|
||||
m.invokestatic(Type.getInternalName(Mth.class), "clampedMap", "(DDDDD)D", false);
|
||||
m.areturn(Type.DOUBLE_TYPE);
|
||||
}
|
||||
|
||||
public void doBytecodeGenMulti(BytecodeGen.Context context, InstructionAdapter m, BytecodeGen.Context.LocalVarConsumer localVarConsumer) {
|
||||
context.doCountedLoop(m, localVarConsumer, (idx) -> {
|
||||
m.load(1, InstructionAdapter.OBJECT_TYPE);
|
||||
m.load(idx, Type.INT_TYPE);
|
||||
m.load(3, InstructionAdapter.OBJECT_TYPE);
|
||||
m.load(idx, Type.INT_TYPE);
|
||||
m.aload(Type.INT_TYPE);
|
||||
m.cast(Type.INT_TYPE, Type.DOUBLE_TYPE);
|
||||
m.dconst(this.fromY);
|
||||
m.dconst(this.toY);
|
||||
m.dconst(this.fromValue);
|
||||
m.dconst(this.toValue);
|
||||
m.invokestatic(Type.getInternalName(Mth.class), "clampedMap", "(DDDDD)D", false);
|
||||
m.astore(Type.DOUBLE_TYPE);
|
||||
});
|
||||
m.areturn(Type.VOID_TYPE);
|
||||
}
|
||||
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
} else if (o != null && this.getClass() == o.getClass()) {
|
||||
YClampedGradientNode that = (YClampedGradientNode)o;
|
||||
return Double.compare(this.fromY, that.fromY) == 0 && Double.compare(this.toY, that.toY) == 0 && Double.compare(this.fromValue, that.fromValue) == 0 && Double.compare(this.toValue, that.toValue) == 0;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
int result = 1;
|
||||
result = 31 * result + this.getClass().hashCode();
|
||||
result = 31 * result + Double.hashCode(this.fromY);
|
||||
result = 31 * result + Double.hashCode(this.toY);
|
||||
result = 31 * result + Double.hashCode(this.fromValue);
|
||||
result = 31 * result + Double.hashCode(this.toValue);
|
||||
return result;
|
||||
}
|
||||
|
||||
public boolean relaxedEquals(AstNode o) {
|
||||
return this.equals(o);
|
||||
}
|
||||
|
||||
public int relaxedHashCode() {
|
||||
return this.hashCode();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,131 @@
|
||||
package org.bxteam.divinemc.dfc.common.ast.noise;
|
||||
|
||||
import org.bxteam.divinemc.dfc.common.ast.AstNode;
|
||||
import org.bxteam.divinemc.dfc.common.ast.AstTransformer;
|
||||
import org.bxteam.divinemc.dfc.common.ast.EvalType;
|
||||
import org.bxteam.divinemc.dfc.common.gen.BytecodeGen;
|
||||
import java.util.Objects;
|
||||
import net.minecraft.world.level.levelgen.DensityFunction;
|
||||
import org.objectweb.asm.Type;
|
||||
import org.objectweb.asm.commons.InstructionAdapter;
|
||||
|
||||
public class DFTNoiseNode implements AstNode {
|
||||
private final DensityFunction.NoiseHolder noise;
|
||||
private final double xzScale;
|
||||
private final double yScale;
|
||||
|
||||
public DFTNoiseNode(DensityFunction.NoiseHolder noise, double xzScale, double yScale) {
|
||||
this.noise = (DensityFunction.NoiseHolder)Objects.requireNonNull(noise);
|
||||
this.xzScale = xzScale;
|
||||
this.yScale = yScale;
|
||||
}
|
||||
|
||||
public double evalSingle(int x, int y, int z, EvalType type) {
|
||||
return this.noise.getValue((double)x * this.xzScale, (double)y * this.yScale, (double)z * this.xzScale);
|
||||
}
|
||||
|
||||
public void evalMulti(double[] res, int[] x, int[] y, int[] z, EvalType type) {
|
||||
for(int i = 0; i < res.length; ++i) {
|
||||
res[i] = this.noise.getValue((double)x[i] * this.xzScale, (double)y[i] * this.yScale, (double)z[i] * this.xzScale);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public AstNode[] getChildren() {
|
||||
return new AstNode[0];
|
||||
}
|
||||
|
||||
public AstNode transform(AstTransformer transformer) {
|
||||
return transformer.transform(this);
|
||||
}
|
||||
|
||||
public void doBytecodeGenSingle(BytecodeGen.Context context, InstructionAdapter m, BytecodeGen.Context.LocalVarConsumer localVarConsumer) {
|
||||
String noiseField = context.newField(DensityFunction.NoiseHolder.class, this.noise);
|
||||
m.load(0, InstructionAdapter.OBJECT_TYPE);
|
||||
m.getfield(context.className, noiseField, Type.getDescriptor(DensityFunction.NoiseHolder.class));
|
||||
m.load(1, Type.INT_TYPE);
|
||||
m.cast(Type.INT_TYPE, Type.DOUBLE_TYPE);
|
||||
m.dconst(this.xzScale);
|
||||
m.mul(Type.DOUBLE_TYPE);
|
||||
m.load(2, Type.INT_TYPE);
|
||||
m.cast(Type.INT_TYPE, Type.DOUBLE_TYPE);
|
||||
m.dconst(this.yScale);
|
||||
m.mul(Type.DOUBLE_TYPE);
|
||||
m.load(3, Type.INT_TYPE);
|
||||
m.cast(Type.INT_TYPE, Type.DOUBLE_TYPE);
|
||||
m.dconst(this.xzScale);
|
||||
m.mul(Type.DOUBLE_TYPE);
|
||||
m.invokevirtual(Type.getInternalName(DensityFunction.NoiseHolder.class), "getValue", "(DDD)D", false);
|
||||
m.areturn(Type.DOUBLE_TYPE);
|
||||
}
|
||||
|
||||
public void doBytecodeGenMulti(BytecodeGen.Context context, InstructionAdapter m, BytecodeGen.Context.LocalVarConsumer localVarConsumer) {
|
||||
String noiseField = context.newField(DensityFunction.NoiseHolder.class, this.noise);
|
||||
context.doCountedLoop(m, localVarConsumer, (idx) -> {
|
||||
m.load(1, InstructionAdapter.OBJECT_TYPE);
|
||||
m.load(idx, Type.INT_TYPE);
|
||||
m.load(0, InstructionAdapter.OBJECT_TYPE);
|
||||
m.getfield(context.className, noiseField, Type.getDescriptor(DensityFunction.NoiseHolder.class));
|
||||
m.load(2, InstructionAdapter.OBJECT_TYPE);
|
||||
m.load(idx, Type.INT_TYPE);
|
||||
m.aload(Type.INT_TYPE);
|
||||
m.cast(Type.INT_TYPE, Type.DOUBLE_TYPE);
|
||||
m.dconst(this.xzScale);
|
||||
m.mul(Type.DOUBLE_TYPE);
|
||||
m.load(3, InstructionAdapter.OBJECT_TYPE);
|
||||
m.load(idx, Type.INT_TYPE);
|
||||
m.aload(Type.INT_TYPE);
|
||||
m.cast(Type.INT_TYPE, Type.DOUBLE_TYPE);
|
||||
m.dconst(this.yScale);
|
||||
m.mul(Type.DOUBLE_TYPE);
|
||||
m.load(4, InstructionAdapter.OBJECT_TYPE);
|
||||
m.load(idx, Type.INT_TYPE);
|
||||
m.aload(Type.INT_TYPE);
|
||||
m.cast(Type.INT_TYPE, Type.DOUBLE_TYPE);
|
||||
m.dconst(this.xzScale);
|
||||
m.mul(Type.DOUBLE_TYPE);
|
||||
m.invokevirtual(Type.getInternalName(DensityFunction.NoiseHolder.class), "getValue", "(DDD)D", false);
|
||||
m.astore(Type.DOUBLE_TYPE);
|
||||
});
|
||||
m.areturn(Type.VOID_TYPE);
|
||||
}
|
||||
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
} else if (o != null && this.getClass() == o.getClass()) {
|
||||
DFTNoiseNode that = (DFTNoiseNode)o;
|
||||
return Double.compare(this.xzScale, that.xzScale) == 0 && Double.compare(this.yScale, that.yScale) == 0 && Objects.equals(this.noise, that.noise);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
int result = 1;
|
||||
result = 31 * result + this.getClass().hashCode();
|
||||
result = 31 * result + this.noise.hashCode();
|
||||
result = 31 * result + Double.hashCode(this.xzScale);
|
||||
result = 31 * result + Double.hashCode(this.yScale);
|
||||
return result;
|
||||
}
|
||||
|
||||
public boolean relaxedEquals(AstNode o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
} else if (o != null && this.getClass() == o.getClass()) {
|
||||
DFTNoiseNode that = (DFTNoiseNode)o;
|
||||
return Double.compare(this.xzScale, that.xzScale) == 0 && Double.compare(this.yScale, that.yScale) == 0;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public int relaxedHashCode() {
|
||||
int result = 1;
|
||||
result = 31 * result + this.getClass().hashCode();
|
||||
result = 31 * result + Double.hashCode(this.xzScale);
|
||||
result = 31 * result + Double.hashCode(this.yScale);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
package org.bxteam.divinemc.dfc.common.ast.noise;
|
||||
|
||||
import org.bxteam.divinemc.dfc.common.ast.AstNode;
|
||||
import org.bxteam.divinemc.dfc.common.ast.AstTransformer;
|
||||
import org.bxteam.divinemc.dfc.common.ast.EvalType;
|
||||
import org.bxteam.divinemc.dfc.common.gen.BytecodeGen;
|
||||
import java.util.Objects;
|
||||
import net.minecraft.world.level.levelgen.DensityFunction;
|
||||
import org.objectweb.asm.Type;
|
||||
import org.objectweb.asm.commons.InstructionAdapter;
|
||||
|
||||
public class DFTShiftANode implements AstNode {
|
||||
private final DensityFunction.NoiseHolder offsetNoise;
|
||||
|
||||
public DFTShiftANode(DensityFunction.NoiseHolder offsetNoise) {
|
||||
this.offsetNoise = (DensityFunction.NoiseHolder)Objects.requireNonNull(offsetNoise);
|
||||
}
|
||||
|
||||
public double evalSingle(int x, int y, int z, EvalType type) {
|
||||
return this.offsetNoise.getValue((double)x * 0.25, 0.0, (double)z * 0.25) * 4.0;
|
||||
}
|
||||
|
||||
public void evalMulti(double[] res, int[] x, int[] y, int[] z, EvalType type) {
|
||||
for(int i = 0; i < res.length; ++i) {
|
||||
res[i] = this.offsetNoise.getValue((double)x[i] * 0.25, 0.0, (double)z[i] * 0.25) * 4.0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public AstNode[] getChildren() {
|
||||
return new AstNode[0];
|
||||
}
|
||||
|
||||
public AstNode transform(AstTransformer transformer) {
|
||||
return transformer.transform(this);
|
||||
}
|
||||
|
||||
public void doBytecodeGenSingle(BytecodeGen.Context context, InstructionAdapter m, BytecodeGen.Context.LocalVarConsumer localVarConsumer) {
|
||||
String noiseField = context.newField(DensityFunction.NoiseHolder.class, this.offsetNoise);
|
||||
m.load(0, InstructionAdapter.OBJECT_TYPE);
|
||||
m.getfield(context.className, noiseField, Type.getDescriptor(DensityFunction.NoiseHolder.class));
|
||||
m.load(1, Type.INT_TYPE);
|
||||
m.cast(Type.INT_TYPE, Type.DOUBLE_TYPE);
|
||||
m.dconst(0.25);
|
||||
m.mul(Type.DOUBLE_TYPE);
|
||||
m.dconst(0.0);
|
||||
m.load(3, Type.INT_TYPE);
|
||||
m.cast(Type.INT_TYPE, Type.DOUBLE_TYPE);
|
||||
m.dconst(0.25);
|
||||
m.mul(Type.DOUBLE_TYPE);
|
||||
m.invokevirtual(Type.getInternalName(DensityFunction.NoiseHolder.class), "getValue", "(DDD)D", false);
|
||||
m.dconst(4.0);
|
||||
m.mul(Type.DOUBLE_TYPE);
|
||||
m.areturn(Type.DOUBLE_TYPE);
|
||||
}
|
||||
|
||||
public void doBytecodeGenMulti(BytecodeGen.Context context, InstructionAdapter m, BytecodeGen.Context.LocalVarConsumer localVarConsumer) {
|
||||
String noiseField = context.newField(DensityFunction.NoiseHolder.class, this.offsetNoise);
|
||||
context.doCountedLoop(m, localVarConsumer, (idx) -> {
|
||||
m.load(1, InstructionAdapter.OBJECT_TYPE);
|
||||
m.load(idx, Type.INT_TYPE);
|
||||
m.load(0, InstructionAdapter.OBJECT_TYPE);
|
||||
m.getfield(context.className, noiseField, Type.getDescriptor(DensityFunction.NoiseHolder.class));
|
||||
m.load(2, InstructionAdapter.OBJECT_TYPE);
|
||||
m.load(idx, Type.INT_TYPE);
|
||||
m.aload(Type.INT_TYPE);
|
||||
m.cast(Type.INT_TYPE, Type.DOUBLE_TYPE);
|
||||
m.dconst(0.25);
|
||||
m.mul(Type.DOUBLE_TYPE);
|
||||
m.dconst(0.0);
|
||||
m.load(4, InstructionAdapter.OBJECT_TYPE);
|
||||
m.load(idx, Type.INT_TYPE);
|
||||
m.aload(Type.INT_TYPE);
|
||||
m.cast(Type.INT_TYPE, Type.DOUBLE_TYPE);
|
||||
m.dconst(0.25);
|
||||
m.mul(Type.DOUBLE_TYPE);
|
||||
m.invokevirtual(Type.getInternalName(DensityFunction.NoiseHolder.class), "getValue", "(DDD)D", false);
|
||||
m.dconst(4.0);
|
||||
m.mul(Type.DOUBLE_TYPE);
|
||||
m.astore(Type.DOUBLE_TYPE);
|
||||
});
|
||||
m.areturn(Type.VOID_TYPE);
|
||||
}
|
||||
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
} else if (o != null && this.getClass() == o.getClass()) {
|
||||
DFTShiftANode that = (DFTShiftANode)o;
|
||||
return Objects.equals(this.offsetNoise, that.offsetNoise);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
int result = 1;
|
||||
Object o = this.getClass();
|
||||
result = 31 * result + o.hashCode();
|
||||
result = 31 * result + this.offsetNoise.hashCode();
|
||||
return result;
|
||||
}
|
||||
|
||||
public boolean relaxedEquals(AstNode o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
} else {
|
||||
return o != null && this.getClass() == o.getClass();
|
||||
}
|
||||
}
|
||||
|
||||
public int relaxedHashCode() {
|
||||
return this.getClass().hashCode();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
package org.bxteam.divinemc.dfc.common.ast.noise;
|
||||
|
||||
import org.bxteam.divinemc.dfc.common.ast.AstNode;
|
||||
import org.bxteam.divinemc.dfc.common.ast.AstTransformer;
|
||||
import org.bxteam.divinemc.dfc.common.ast.EvalType;
|
||||
import org.bxteam.divinemc.dfc.common.gen.BytecodeGen;
|
||||
import java.util.Objects;
|
||||
import net.minecraft.world.level.levelgen.DensityFunction;
|
||||
import org.objectweb.asm.Type;
|
||||
import org.objectweb.asm.commons.InstructionAdapter;
|
||||
|
||||
public class DFTShiftBNode implements AstNode {
|
||||
private final DensityFunction.NoiseHolder offsetNoise;
|
||||
|
||||
public DFTShiftBNode(DensityFunction.NoiseHolder offsetNoise) {
|
||||
this.offsetNoise = (DensityFunction.NoiseHolder)Objects.requireNonNull(offsetNoise);
|
||||
}
|
||||
|
||||
public double evalSingle(int x, int y, int z, EvalType type) {
|
||||
return this.offsetNoise.getValue((double)z * 0.25, (double)x * 0.25, 0.0) * 4.0;
|
||||
}
|
||||
|
||||
public void evalMulti(double[] res, int[] x, int[] y, int[] z, EvalType type) {
|
||||
for(int i = 0; i < res.length; ++i) {
|
||||
res[i] = this.offsetNoise.getValue((double)z[i] * 0.25, (double)x[i] * 0.25, 0.0) * 4.0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public AstNode[] getChildren() {
|
||||
return new AstNode[0];
|
||||
}
|
||||
|
||||
public AstNode transform(AstTransformer transformer) {
|
||||
return transformer.transform(this);
|
||||
}
|
||||
|
||||
public void doBytecodeGenSingle(BytecodeGen.Context context, InstructionAdapter m, BytecodeGen.Context.LocalVarConsumer localVarConsumer) {
|
||||
String noiseField = context.newField(DensityFunction.NoiseHolder.class, this.offsetNoise);
|
||||
m.load(0, InstructionAdapter.OBJECT_TYPE);
|
||||
m.getfield(context.className, noiseField, Type.getDescriptor(DensityFunction.NoiseHolder.class));
|
||||
m.load(3, Type.INT_TYPE);
|
||||
m.cast(Type.INT_TYPE, Type.DOUBLE_TYPE);
|
||||
m.dconst(0.25);
|
||||
m.mul(Type.DOUBLE_TYPE);
|
||||
m.load(1, Type.INT_TYPE);
|
||||
m.cast(Type.INT_TYPE, Type.DOUBLE_TYPE);
|
||||
m.dconst(0.25);
|
||||
m.mul(Type.DOUBLE_TYPE);
|
||||
m.dconst(0.0);
|
||||
m.invokevirtual(Type.getInternalName(DensityFunction.NoiseHolder.class), "getValue", "(DDD)D", false);
|
||||
m.dconst(4.0);
|
||||
m.mul(Type.DOUBLE_TYPE);
|
||||
m.areturn(Type.DOUBLE_TYPE);
|
||||
}
|
||||
|
||||
public void doBytecodeGenMulti(BytecodeGen.Context context, InstructionAdapter m, BytecodeGen.Context.LocalVarConsumer localVarConsumer) {
|
||||
String noiseField = context.newField(DensityFunction.NoiseHolder.class, this.offsetNoise);
|
||||
context.doCountedLoop(m, localVarConsumer, (idx) -> {
|
||||
m.load(1, InstructionAdapter.OBJECT_TYPE);
|
||||
m.load(idx, Type.INT_TYPE);
|
||||
m.load(0, InstructionAdapter.OBJECT_TYPE);
|
||||
m.getfield(context.className, noiseField, Type.getDescriptor(DensityFunction.NoiseHolder.class));
|
||||
m.load(4, InstructionAdapter.OBJECT_TYPE);
|
||||
m.load(idx, Type.INT_TYPE);
|
||||
m.aload(Type.INT_TYPE);
|
||||
m.cast(Type.INT_TYPE, Type.DOUBLE_TYPE);
|
||||
m.dconst(0.25);
|
||||
m.mul(Type.DOUBLE_TYPE);
|
||||
m.load(2, InstructionAdapter.OBJECT_TYPE);
|
||||
m.load(idx, Type.INT_TYPE);
|
||||
m.aload(Type.INT_TYPE);
|
||||
m.cast(Type.INT_TYPE, Type.DOUBLE_TYPE);
|
||||
m.dconst(0.25);
|
||||
m.mul(Type.DOUBLE_TYPE);
|
||||
m.dconst(0.0);
|
||||
m.invokevirtual(Type.getInternalName(DensityFunction.NoiseHolder.class), "getValue", "(DDD)D", false);
|
||||
m.dconst(4.0);
|
||||
m.mul(Type.DOUBLE_TYPE);
|
||||
m.astore(Type.DOUBLE_TYPE);
|
||||
});
|
||||
m.areturn(Type.VOID_TYPE);
|
||||
}
|
||||
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
} else if (o != null && this.getClass() == o.getClass()) {
|
||||
DFTShiftBNode that = (DFTShiftBNode)o;
|
||||
return Objects.equals(this.offsetNoise, that.offsetNoise);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
int result = 1;
|
||||
Object o = this.getClass();
|
||||
result = 31 * result + o.hashCode();
|
||||
result = 31 * result + this.offsetNoise.hashCode();
|
||||
return result;
|
||||
}
|
||||
|
||||
public boolean relaxedEquals(AstNode o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
} else {
|
||||
return o != null && this.getClass() == o.getClass();
|
||||
}
|
||||
}
|
||||
|
||||
public int relaxedHashCode() {
|
||||
return this.getClass().hashCode();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,123 @@
|
||||
package org.bxteam.divinemc.dfc.common.ast.noise;
|
||||
|
||||
import org.bxteam.divinemc.dfc.common.ast.AstNode;
|
||||
import org.bxteam.divinemc.dfc.common.ast.AstTransformer;
|
||||
import org.bxteam.divinemc.dfc.common.ast.EvalType;
|
||||
import org.bxteam.divinemc.dfc.common.gen.BytecodeGen;
|
||||
import java.util.Objects;
|
||||
import net.minecraft.world.level.levelgen.DensityFunction;
|
||||
import org.objectweb.asm.Type;
|
||||
import org.objectweb.asm.commons.InstructionAdapter;
|
||||
|
||||
public class DFTShiftNode implements AstNode {
|
||||
private final DensityFunction.NoiseHolder offsetNoise;
|
||||
|
||||
public DFTShiftNode(DensityFunction.NoiseHolder offsetNoise) {
|
||||
this.offsetNoise = (DensityFunction.NoiseHolder)Objects.requireNonNull(offsetNoise);
|
||||
}
|
||||
|
||||
public double evalSingle(int x, int y, int z, EvalType type) {
|
||||
return this.offsetNoise.getValue((double)x * 0.25, (double)y * 0.25, (double)z * 0.25) * 4.0;
|
||||
}
|
||||
|
||||
public void evalMulti(double[] res, int[] x, int[] y, int[] z, EvalType type) {
|
||||
for(int i = 0; i < res.length; ++i) {
|
||||
res[i] = this.offsetNoise.getValue((double)x[i] * 0.25, (double)y[i] * 0.25, (double)z[i] * 0.25) * 4.0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public AstNode[] getChildren() {
|
||||
return new AstNode[0];
|
||||
}
|
||||
|
||||
public AstNode transform(AstTransformer transformer) {
|
||||
return transformer.transform(this);
|
||||
}
|
||||
|
||||
public void doBytecodeGenSingle(BytecodeGen.Context context, InstructionAdapter m, BytecodeGen.Context.LocalVarConsumer localVarConsumer) {
|
||||
String noiseField = context.newField(DensityFunction.NoiseHolder.class, this.offsetNoise);
|
||||
m.load(0, InstructionAdapter.OBJECT_TYPE);
|
||||
m.getfield(context.className, noiseField, Type.getDescriptor(DensityFunction.NoiseHolder.class));
|
||||
m.load(1, Type.INT_TYPE);
|
||||
m.cast(Type.INT_TYPE, Type.DOUBLE_TYPE);
|
||||
m.dconst(0.25);
|
||||
m.mul(Type.DOUBLE_TYPE);
|
||||
m.load(2, Type.INT_TYPE);
|
||||
m.cast(Type.INT_TYPE, Type.DOUBLE_TYPE);
|
||||
m.dconst(0.25);
|
||||
m.mul(Type.DOUBLE_TYPE);
|
||||
m.load(3, Type.INT_TYPE);
|
||||
m.cast(Type.INT_TYPE, Type.DOUBLE_TYPE);
|
||||
m.dconst(0.25);
|
||||
m.mul(Type.DOUBLE_TYPE);
|
||||
m.invokevirtual(Type.getInternalName(DensityFunction.NoiseHolder.class), "getValue", "(DDD)D", false);
|
||||
m.dconst(4.0);
|
||||
m.mul(Type.DOUBLE_TYPE);
|
||||
m.areturn(Type.DOUBLE_TYPE);
|
||||
}
|
||||
|
||||
public void doBytecodeGenMulti(BytecodeGen.Context context, InstructionAdapter m, BytecodeGen.Context.LocalVarConsumer localVarConsumer) {
|
||||
String noiseField = context.newField(DensityFunction.NoiseHolder.class, this.offsetNoise);
|
||||
context.doCountedLoop(m, localVarConsumer, (idx) -> {
|
||||
m.load(1, InstructionAdapter.OBJECT_TYPE);
|
||||
m.load(idx, Type.INT_TYPE);
|
||||
m.load(0, InstructionAdapter.OBJECT_TYPE);
|
||||
m.getfield(context.className, noiseField, Type.getDescriptor(DensityFunction.NoiseHolder.class));
|
||||
m.load(2, InstructionAdapter.OBJECT_TYPE);
|
||||
m.load(idx, Type.INT_TYPE);
|
||||
m.aload(Type.INT_TYPE);
|
||||
m.cast(Type.INT_TYPE, Type.DOUBLE_TYPE);
|
||||
m.dconst(0.25);
|
||||
m.mul(Type.DOUBLE_TYPE);
|
||||
m.load(3, InstructionAdapter.OBJECT_TYPE);
|
||||
m.load(idx, Type.INT_TYPE);
|
||||
m.aload(Type.INT_TYPE);
|
||||
m.cast(Type.INT_TYPE, Type.DOUBLE_TYPE);
|
||||
m.dconst(0.25);
|
||||
m.mul(Type.DOUBLE_TYPE);
|
||||
m.load(4, InstructionAdapter.OBJECT_TYPE);
|
||||
m.load(idx, Type.INT_TYPE);
|
||||
m.aload(Type.INT_TYPE);
|
||||
m.cast(Type.INT_TYPE, Type.DOUBLE_TYPE);
|
||||
m.dconst(0.25);
|
||||
m.mul(Type.DOUBLE_TYPE);
|
||||
m.invokevirtual(Type.getInternalName(DensityFunction.NoiseHolder.class), "getValue", "(DDD)D", false);
|
||||
m.dconst(4.0);
|
||||
m.mul(Type.DOUBLE_TYPE);
|
||||
m.astore(Type.DOUBLE_TYPE);
|
||||
});
|
||||
m.areturn(Type.VOID_TYPE);
|
||||
}
|
||||
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
} else if (o != null && this.getClass() == o.getClass()) {
|
||||
DFTShiftNode that = (DFTShiftNode)o;
|
||||
return Objects.equals(this.offsetNoise, that.offsetNoise);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
int result = 1;
|
||||
Object o = this.getClass();
|
||||
result = 31 * result + o.hashCode();
|
||||
result = 31 * result + this.offsetNoise.hashCode();
|
||||
return result;
|
||||
}
|
||||
|
||||
public boolean relaxedEquals(AstNode o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
} else {
|
||||
return o != null && this.getClass() == o.getClass();
|
||||
}
|
||||
}
|
||||
|
||||
public int relaxedHashCode() {
|
||||
return this.getClass().hashCode();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,169 @@
|
||||
package org.bxteam.divinemc.dfc.common.ast.noise;
|
||||
|
||||
import org.bxteam.divinemc.dfc.common.IDensityFunctionsCaveScaler;
|
||||
import org.bxteam.divinemc.dfc.common.ast.AstNode;
|
||||
import org.bxteam.divinemc.dfc.common.ast.AstTransformer;
|
||||
import org.bxteam.divinemc.dfc.common.ast.EvalType;
|
||||
import org.bxteam.divinemc.dfc.common.gen.BytecodeGen;
|
||||
import java.util.Objects;
|
||||
import net.minecraft.world.level.levelgen.DensityFunction;
|
||||
import net.minecraft.world.level.levelgen.DensityFunctions;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.objectweb.asm.Type;
|
||||
import org.objectweb.asm.commons.InstructionAdapter;
|
||||
|
||||
public class DFTWeirdScaledSamplerNode implements AstNode {
|
||||
private final AstNode input;
|
||||
private final DensityFunction.NoiseHolder noise;
|
||||
private final DensityFunctions.WeirdScaledSampler.RarityValueMapper mapper;
|
||||
|
||||
public DFTWeirdScaledSamplerNode(AstNode input, DensityFunction.NoiseHolder noise, DensityFunctions.WeirdScaledSampler.RarityValueMapper mapper) {
|
||||
this.input = Objects.requireNonNull(input);
|
||||
this.noise = Objects.requireNonNull(noise);
|
||||
this.mapper = Objects.requireNonNull(mapper);
|
||||
}
|
||||
|
||||
public double evalSingle(int x, int y, int z, EvalType type) {
|
||||
double v = this.input.evalSingle(x, y, z, type);
|
||||
double d = (this.mapper.mapper).get(v);
|
||||
return d * Math.abs(this.noise.getValue((double)x / d, (double)y / d, (double)z / d));
|
||||
}
|
||||
|
||||
public void evalMulti(double[] res, int[] x, int[] y, int[] z, EvalType type) {
|
||||
this.input.evalMulti(res, x, y, z, type);
|
||||
|
||||
for(int i = 0; i < res.length; ++i) {
|
||||
double d = (this.mapper.mapper).get(res[i]);
|
||||
res[i] = d * Math.abs(this.noise.getValue((double)x[i] / d, (double)y[i] / d, (double)z[i] / d));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public AstNode[] getChildren() {
|
||||
return new AstNode[]{this.input};
|
||||
}
|
||||
|
||||
public AstNode transform(AstTransformer transformer) {
|
||||
AstNode input = this.input.transform(transformer);
|
||||
return input == this.input ? transformer.transform(this) : transformer.transform(new DFTWeirdScaledSamplerNode(input, this.noise, this.mapper));
|
||||
}
|
||||
|
||||
public void doBytecodeGenSingle(BytecodeGen.@NotNull Context context, InstructionAdapter m, BytecodeGen.Context.@NotNull LocalVarConsumer localVarConsumer) {
|
||||
String inputMethod = context.newSingleMethod(this.input);
|
||||
String noiseField = context.newField(DensityFunction.NoiseHolder.class, this.noise);
|
||||
int scale = localVarConsumer.createLocalVariable("scale", Type.DOUBLE_TYPE.getDescriptor());
|
||||
context.callDelegateSingle(m, inputMethod);
|
||||
switch (this.mapper) {
|
||||
case TYPE1 -> m.invokestatic(Type.getInternalName(IDensityFunctionsCaveScaler.class), "invokeScaleTunnels", Type.getMethodDescriptor(Type.DOUBLE_TYPE, Type.DOUBLE_TYPE), true);
|
||||
case TYPE2 -> m.invokestatic(Type.getInternalName(IDensityFunctionsCaveScaler.class), "invokeScaleCaves", Type.getMethodDescriptor(Type.DOUBLE_TYPE, Type.DOUBLE_TYPE), true);
|
||||
default -> throw new UnsupportedOperationException(String.format("Unknown mapper %s", this.mapper));
|
||||
}
|
||||
|
||||
m.store(scale, Type.DOUBLE_TYPE);
|
||||
m.load(0, InstructionAdapter.OBJECT_TYPE);
|
||||
m.getfield(context.className, noiseField, Type.getDescriptor(DensityFunction.NoiseHolder.class));
|
||||
m.load(1, Type.INT_TYPE);
|
||||
m.cast(Type.INT_TYPE, Type.DOUBLE_TYPE);
|
||||
m.load(scale, Type.DOUBLE_TYPE);
|
||||
m.div(Type.DOUBLE_TYPE);
|
||||
m.load(2, Type.INT_TYPE);
|
||||
m.cast(Type.INT_TYPE, Type.DOUBLE_TYPE);
|
||||
m.load(scale, Type.DOUBLE_TYPE);
|
||||
m.div(Type.DOUBLE_TYPE);
|
||||
m.load(3, Type.INT_TYPE);
|
||||
m.cast(Type.INT_TYPE, Type.DOUBLE_TYPE);
|
||||
m.load(scale, Type.DOUBLE_TYPE);
|
||||
m.div(Type.DOUBLE_TYPE);
|
||||
m.invokevirtual(Type.getInternalName(DensityFunction.NoiseHolder.class), "getValue", "(DDD)D", false);
|
||||
m.invokestatic(Type.getInternalName(Math.class), "abs", "(D)D", false);
|
||||
m.load(scale, Type.DOUBLE_TYPE);
|
||||
m.mul(Type.DOUBLE_TYPE);
|
||||
m.areturn(Type.DOUBLE_TYPE);
|
||||
}
|
||||
|
||||
public void doBytecodeGenMulti(BytecodeGen.Context context, InstructionAdapter m, BytecodeGen.Context.LocalVarConsumer localVarConsumer) {
|
||||
String inputMethod = context.newMultiMethod(this.input);
|
||||
String noiseField = context.newField(DensityFunction.NoiseHolder.class, this.noise);
|
||||
context.callDelegateMulti(m, inputMethod);
|
||||
context.doCountedLoop(m, localVarConsumer, (idx) -> {
|
||||
int scale = localVarConsumer.createLocalVariable("scale", Type.DOUBLE_TYPE.getDescriptor());
|
||||
m.load(1, InstructionAdapter.OBJECT_TYPE);
|
||||
m.load(idx, Type.INT_TYPE);
|
||||
m.load(1, InstructionAdapter.OBJECT_TYPE);
|
||||
m.load(idx, Type.INT_TYPE);
|
||||
m.aload(Type.DOUBLE_TYPE);
|
||||
switch (this.mapper) {
|
||||
case TYPE1 -> m.invokestatic(Type.getInternalName(IDensityFunctionsCaveScaler.class), "invokeScaleTunnels", Type.getMethodDescriptor(Type.DOUBLE_TYPE, Type.DOUBLE_TYPE), true);
|
||||
case TYPE2 -> m.invokestatic(Type.getInternalName(IDensityFunctionsCaveScaler.class), "invokeScaleCaves", Type.getMethodDescriptor(Type.DOUBLE_TYPE, Type.DOUBLE_TYPE), true);
|
||||
default -> throw new UnsupportedOperationException(String.format("Unknown mapper %s", this.mapper));
|
||||
}
|
||||
|
||||
m.store(scale, Type.DOUBLE_TYPE);
|
||||
m.load(0, InstructionAdapter.OBJECT_TYPE);
|
||||
m.getfield(context.className, noiseField, Type.getDescriptor(DensityFunction.NoiseHolder.class));
|
||||
m.load(2, InstructionAdapter.OBJECT_TYPE);
|
||||
m.load(idx, Type.INT_TYPE);
|
||||
m.aload(Type.INT_TYPE);
|
||||
m.cast(Type.INT_TYPE, Type.DOUBLE_TYPE);
|
||||
m.load(scale, Type.DOUBLE_TYPE);
|
||||
m.div(Type.DOUBLE_TYPE);
|
||||
m.load(3, InstructionAdapter.OBJECT_TYPE);
|
||||
m.load(idx, Type.INT_TYPE);
|
||||
m.aload(Type.INT_TYPE);
|
||||
m.cast(Type.INT_TYPE, Type.DOUBLE_TYPE);
|
||||
m.load(scale, Type.DOUBLE_TYPE);
|
||||
m.div(Type.DOUBLE_TYPE);
|
||||
m.load(4, InstructionAdapter.OBJECT_TYPE);
|
||||
m.load(idx, Type.INT_TYPE);
|
||||
m.aload(Type.INT_TYPE);
|
||||
m.cast(Type.INT_TYPE, Type.DOUBLE_TYPE);
|
||||
m.load(scale, Type.DOUBLE_TYPE);
|
||||
m.div(Type.DOUBLE_TYPE);
|
||||
m.invokevirtual(Type.getInternalName(DensityFunction.NoiseHolder.class), "getValue", "(DDD)D", false);
|
||||
m.invokestatic(Type.getInternalName(Math.class), "abs", "(D)D", false);
|
||||
m.load(scale, Type.DOUBLE_TYPE);
|
||||
m.mul(Type.DOUBLE_TYPE);
|
||||
m.astore(Type.DOUBLE_TYPE);
|
||||
});
|
||||
m.areturn(Type.VOID_TYPE);
|
||||
}
|
||||
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
} else if (o != null && this.getClass() == o.getClass()) {
|
||||
DFTWeirdScaledSamplerNode that = (DFTWeirdScaledSamplerNode)o;
|
||||
return Objects.equals(this.input, that.input) && Objects.equals(this.noise, that.noise) && this.mapper == that.mapper;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
int result = 1;
|
||||
result = 31 * result + this.getClass().hashCode();
|
||||
result = 31 * result + this.input.hashCode();
|
||||
result = 31 * result + this.noise.hashCode();
|
||||
result = 31 * result + this.mapper.hashCode();
|
||||
return result;
|
||||
}
|
||||
|
||||
public boolean relaxedEquals(AstNode o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
} else if (o != null && this.getClass() == o.getClass()) {
|
||||
DFTWeirdScaledSamplerNode that = (DFTWeirdScaledSamplerNode)o;
|
||||
return this.input.relaxedEquals(that.input) && this.mapper == that.mapper;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public int relaxedHashCode() {
|
||||
int result = 1;
|
||||
result = 31 * result + this.getClass().hashCode();
|
||||
result = 31 * result + this.input.relaxedHashCode();
|
||||
result = 31 * result + this.mapper.hashCode();
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,215 @@
|
||||
package org.bxteam.divinemc.dfc.common.ast.noise;
|
||||
|
||||
import org.bxteam.divinemc.dfc.common.ast.AstNode;
|
||||
import org.bxteam.divinemc.dfc.common.ast.AstTransformer;
|
||||
import org.bxteam.divinemc.dfc.common.ast.EvalType;
|
||||
import org.bxteam.divinemc.dfc.common.gen.BytecodeGen.Context;
|
||||
import org.bxteam.divinemc.dfc.common.util.ArrayCache;
|
||||
import java.util.Objects;
|
||||
import net.minecraft.world.level.levelgen.DensityFunction;
|
||||
import org.objectweb.asm.Type;
|
||||
import org.objectweb.asm.commons.InstructionAdapter;
|
||||
|
||||
public class ShiftedNoiseNode implements AstNode {
|
||||
private final AstNode shiftX;
|
||||
private final AstNode shiftY;
|
||||
private final AstNode shiftZ;
|
||||
private final double xzScale;
|
||||
private final double yScale;
|
||||
private final DensityFunction.NoiseHolder noise;
|
||||
|
||||
public ShiftedNoiseNode(AstNode shiftX, AstNode shiftY, AstNode shiftZ, double xzScale, double yScale, DensityFunction.NoiseHolder noise) {
|
||||
this.shiftX = (AstNode)Objects.requireNonNull(shiftX);
|
||||
this.shiftY = (AstNode)Objects.requireNonNull(shiftY);
|
||||
this.shiftZ = (AstNode)Objects.requireNonNull(shiftZ);
|
||||
this.xzScale = xzScale;
|
||||
this.yScale = yScale;
|
||||
this.noise = (DensityFunction.NoiseHolder)Objects.requireNonNull(noise);
|
||||
}
|
||||
|
||||
public double evalSingle(int x, int y, int z, EvalType type) {
|
||||
double d = (double)x * this.xzScale + this.shiftX.evalSingle(x, y, z, type);
|
||||
double e = (double)y * this.yScale + this.shiftY.evalSingle(x, y, z, type);
|
||||
double f = (double)z * this.xzScale + this.shiftZ.evalSingle(x, y, z, type);
|
||||
return this.noise.getValue(d, e, f);
|
||||
}
|
||||
|
||||
public void evalMulti(double[] res, int[] x, int[] y, int[] z, EvalType type) {
|
||||
double[] res1 = new double[res.length];
|
||||
double[] res2 = new double[res.length];
|
||||
this.shiftX.evalMulti(res, x, y, z, type);
|
||||
this.shiftY.evalMulti(res1, x, y, z, type);
|
||||
this.shiftZ.evalMulti(res2, x, y, z, type);
|
||||
|
||||
for(int i = 0; i < res.length; ++i) {
|
||||
res[i] = this.noise.getValue((double)x[i] * this.xzScale + res[i], (double)y[i] * this.yScale + res1[i], (double)z[i] * this.xzScale + res2[i]);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public AstNode[] getChildren() {
|
||||
return new AstNode[]{this.shiftX, this.shiftY, this.shiftZ};
|
||||
}
|
||||
|
||||
public AstNode transform(AstTransformer transformer) {
|
||||
AstNode shiftX = this.shiftX.transform(transformer);
|
||||
AstNode shiftY = this.shiftY.transform(transformer);
|
||||
AstNode shiftZ = this.shiftZ.transform(transformer);
|
||||
return shiftX == this.shiftX && shiftY == this.shiftY && shiftZ == this.shiftZ ? transformer.transform(this) : transformer.transform(new ShiftedNoiseNode(shiftX, shiftY, shiftZ, this.xzScale, this.yScale, this.noise));
|
||||
}
|
||||
|
||||
public void doBytecodeGenSingle(Context context, InstructionAdapter m, Context.LocalVarConsumer localVarConsumer) {
|
||||
String noiseField = context.newField(DensityFunction.NoiseHolder.class, this.noise);
|
||||
String shiftXMethod = context.newSingleMethod(this.shiftX);
|
||||
String shiftYMethod = context.newSingleMethod(this.shiftY);
|
||||
String shiftZMethod = context.newSingleMethod(this.shiftZ);
|
||||
m.load(0, InstructionAdapter.OBJECT_TYPE);
|
||||
m.getfield(context.className, noiseField, Type.getDescriptor(DensityFunction.NoiseHolder.class));
|
||||
m.load(1, Type.INT_TYPE);
|
||||
m.cast(Type.INT_TYPE, Type.DOUBLE_TYPE);
|
||||
m.dconst(this.xzScale);
|
||||
m.mul(Type.DOUBLE_TYPE);
|
||||
context.callDelegateSingle(m, shiftXMethod);
|
||||
m.add(Type.DOUBLE_TYPE);
|
||||
m.load(2, Type.INT_TYPE);
|
||||
m.cast(Type.INT_TYPE, Type.DOUBLE_TYPE);
|
||||
m.dconst(this.yScale);
|
||||
m.mul(Type.DOUBLE_TYPE);
|
||||
context.callDelegateSingle(m, shiftYMethod);
|
||||
m.add(Type.DOUBLE_TYPE);
|
||||
m.load(3, Type.INT_TYPE);
|
||||
m.cast(Type.INT_TYPE, Type.DOUBLE_TYPE);
|
||||
m.dconst(this.xzScale);
|
||||
m.mul(Type.DOUBLE_TYPE);
|
||||
context.callDelegateSingle(m, shiftZMethod);
|
||||
m.add(Type.DOUBLE_TYPE);
|
||||
m.invokevirtual(Type.getInternalName(DensityFunction.NoiseHolder.class), "getValue", "(DDD)D", false);
|
||||
m.areturn(Type.DOUBLE_TYPE);
|
||||
}
|
||||
|
||||
public void doBytecodeGenMulti(Context context, InstructionAdapter m, Context.LocalVarConsumer localVarConsumer) {
|
||||
String noiseField = context.newField(DensityFunction.NoiseHolder.class, this.noise);
|
||||
String shiftXMethod = context.newMultiMethod(this.shiftX);
|
||||
String shiftYMethod = context.newMultiMethod(this.shiftY);
|
||||
String shiftZMethod = context.newMultiMethod(this.shiftZ);
|
||||
int res1 = localVarConsumer.createLocalVariable("res1", Type.getDescriptor(double[].class));
|
||||
int res2 = localVarConsumer.createLocalVariable("res2", Type.getDescriptor(double[].class));
|
||||
m.load(6, InstructionAdapter.OBJECT_TYPE);
|
||||
m.load(1, InstructionAdapter.OBJECT_TYPE);
|
||||
m.arraylength();
|
||||
m.iconst(0);
|
||||
m.invokevirtual(Type.getInternalName(ArrayCache.class), "getDoubleArray", Type.getMethodDescriptor(Type.getType(double[].class), new Type[]{Type.INT_TYPE, Type.BOOLEAN_TYPE}), false);
|
||||
m.store(res1, InstructionAdapter.OBJECT_TYPE);
|
||||
m.load(6, InstructionAdapter.OBJECT_TYPE);
|
||||
m.load(1, InstructionAdapter.OBJECT_TYPE);
|
||||
m.arraylength();
|
||||
m.iconst(0);
|
||||
m.invokevirtual(Type.getInternalName(ArrayCache.class), "getDoubleArray", Type.getMethodDescriptor(Type.getType(double[].class), new Type[]{Type.INT_TYPE, Type.BOOLEAN_TYPE}), false);
|
||||
m.store(res2, InstructionAdapter.OBJECT_TYPE);
|
||||
context.callDelegateMulti(m, shiftXMethod);
|
||||
m.load(0, InstructionAdapter.OBJECT_TYPE);
|
||||
m.load(res1, InstructionAdapter.OBJECT_TYPE);
|
||||
m.load(2, InstructionAdapter.OBJECT_TYPE);
|
||||
m.load(3, InstructionAdapter.OBJECT_TYPE);
|
||||
m.load(4, InstructionAdapter.OBJECT_TYPE);
|
||||
m.load(5, InstructionAdapter.OBJECT_TYPE);
|
||||
m.load(6, InstructionAdapter.OBJECT_TYPE);
|
||||
m.invokevirtual(context.className, shiftYMethod, Context.MULTI_DESC, false);
|
||||
m.load(0, InstructionAdapter.OBJECT_TYPE);
|
||||
m.load(res2, InstructionAdapter.OBJECT_TYPE);
|
||||
m.load(2, InstructionAdapter.OBJECT_TYPE);
|
||||
m.load(3, InstructionAdapter.OBJECT_TYPE);
|
||||
m.load(4, InstructionAdapter.OBJECT_TYPE);
|
||||
m.load(5, InstructionAdapter.OBJECT_TYPE);
|
||||
m.load(6, InstructionAdapter.OBJECT_TYPE);
|
||||
m.invokevirtual(context.className, shiftZMethod, Context.MULTI_DESC, false);
|
||||
context.doCountedLoop(m, localVarConsumer, (idx) -> {
|
||||
m.load(1, InstructionAdapter.OBJECT_TYPE);
|
||||
m.load(idx, Type.INT_TYPE);
|
||||
m.load(0, InstructionAdapter.OBJECT_TYPE);
|
||||
m.getfield(context.className, noiseField, Type.getDescriptor(DensityFunction.NoiseHolder.class));
|
||||
m.load(2, InstructionAdapter.OBJECT_TYPE);
|
||||
m.load(idx, Type.INT_TYPE);
|
||||
m.aload(Type.INT_TYPE);
|
||||
m.cast(Type.INT_TYPE, Type.DOUBLE_TYPE);
|
||||
m.dconst(this.xzScale);
|
||||
m.mul(Type.DOUBLE_TYPE);
|
||||
m.load(1, InstructionAdapter.OBJECT_TYPE);
|
||||
m.load(idx, Type.INT_TYPE);
|
||||
m.aload(Type.DOUBLE_TYPE);
|
||||
m.add(Type.DOUBLE_TYPE);
|
||||
m.load(3, InstructionAdapter.OBJECT_TYPE);
|
||||
m.load(idx, Type.INT_TYPE);
|
||||
m.aload(Type.INT_TYPE);
|
||||
m.cast(Type.INT_TYPE, Type.DOUBLE_TYPE);
|
||||
m.dconst(this.yScale);
|
||||
m.mul(Type.DOUBLE_TYPE);
|
||||
m.load(res1, InstructionAdapter.OBJECT_TYPE);
|
||||
m.load(idx, Type.INT_TYPE);
|
||||
m.aload(Type.DOUBLE_TYPE);
|
||||
m.add(Type.DOUBLE_TYPE);
|
||||
m.load(4, InstructionAdapter.OBJECT_TYPE);
|
||||
m.load(idx, Type.INT_TYPE);
|
||||
m.aload(Type.INT_TYPE);
|
||||
m.cast(Type.INT_TYPE, Type.DOUBLE_TYPE);
|
||||
m.dconst(this.xzScale);
|
||||
m.mul(Type.DOUBLE_TYPE);
|
||||
m.load(res2, InstructionAdapter.OBJECT_TYPE);
|
||||
m.load(idx, Type.INT_TYPE);
|
||||
m.aload(Type.DOUBLE_TYPE);
|
||||
m.add(Type.DOUBLE_TYPE);
|
||||
m.invokevirtual(Type.getInternalName(DensityFunction.NoiseHolder.class), "getValue", "(DDD)D", false);
|
||||
m.astore(Type.DOUBLE_TYPE);
|
||||
});
|
||||
m.load(6, InstructionAdapter.OBJECT_TYPE);
|
||||
m.load(res1, InstructionAdapter.OBJECT_TYPE);
|
||||
m.invokevirtual(Type.getInternalName(ArrayCache.class), "recycle", Type.getMethodDescriptor(Type.VOID_TYPE, new Type[]{Type.getType(double[].class)}), false);
|
||||
m.load(6, InstructionAdapter.OBJECT_TYPE);
|
||||
m.load(res2, InstructionAdapter.OBJECT_TYPE);
|
||||
m.invokevirtual(Type.getInternalName(ArrayCache.class), "recycle", Type.getMethodDescriptor(Type.VOID_TYPE, new Type[]{Type.getType(double[].class)}), false);
|
||||
m.areturn(Type.VOID_TYPE);
|
||||
}
|
||||
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
} else if (o != null && this.getClass() == o.getClass()) {
|
||||
ShiftedNoiseNode that = (ShiftedNoiseNode)o;
|
||||
return Double.compare(this.xzScale, that.xzScale) == 0 && Double.compare(this.yScale, that.yScale) == 0 && Objects.equals(this.shiftX, that.shiftX) && Objects.equals(this.shiftY, that.shiftY) && Objects.equals(this.shiftZ, that.shiftZ) && Objects.equals(this.noise, that.noise);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
int result = 1;
|
||||
result = 31 * result + this.shiftX.hashCode();
|
||||
result = 31 * result + this.shiftY.hashCode();
|
||||
result = 31 * result + this.shiftZ.hashCode();
|
||||
result = 31 * result + Double.hashCode(this.xzScale);
|
||||
result = 31 * result + Double.hashCode(this.yScale);
|
||||
result = 31 * result + this.noise.hashCode();
|
||||
return result;
|
||||
}
|
||||
|
||||
public boolean relaxedEquals(AstNode o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
} else if (o != null && this.getClass() == o.getClass()) {
|
||||
ShiftedNoiseNode that = (ShiftedNoiseNode)o;
|
||||
return Double.compare(this.xzScale, that.xzScale) == 0 && Double.compare(this.yScale, that.yScale) == 0 && this.shiftX.relaxedEquals(that.shiftX) && this.shiftY.relaxedEquals(that.shiftY) && this.shiftZ.relaxedEquals(that.shiftZ);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public int relaxedHashCode() {
|
||||
int result = 1;
|
||||
result = 31 * result + this.shiftX.relaxedHashCode();
|
||||
result = 31 * result + this.shiftY.relaxedHashCode();
|
||||
result = 31 * result + this.shiftZ.relaxedHashCode();
|
||||
result = 31 * result + Double.hashCode(this.xzScale);
|
||||
result = 31 * result + Double.hashCode(this.yScale);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,453 @@
|
||||
package org.bxteam.divinemc.dfc.common.ast.spline;
|
||||
|
||||
import org.bxteam.divinemc.dfc.common.ast.AstNode;
|
||||
import org.bxteam.divinemc.dfc.common.ast.AstTransformer;
|
||||
import org.bxteam.divinemc.dfc.common.ast.EvalType;
|
||||
import org.bxteam.divinemc.dfc.common.ast.McToAst;
|
||||
import org.bxteam.divinemc.dfc.common.gen.BytecodeGen;
|
||||
import org.bxteam.divinemc.dfc.common.vif.NoisePosVanillaInterface;
|
||||
import it.unimi.dsi.fastutil.Pair;
|
||||
import it.unimi.dsi.fastutil.ints.IntObjectPair;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import net.minecraft.util.CubicSpline;
|
||||
import net.minecraft.util.Mth;
|
||||
import net.minecraft.world.level.levelgen.DensityFunction;
|
||||
import net.minecraft.world.level.levelgen.DensityFunctions;
|
||||
import org.bxteam.divinemc.util.Assertions;
|
||||
import org.objectweb.asm.Label;
|
||||
import org.objectweb.asm.Type;
|
||||
import org.objectweb.asm.commons.AnalyzerAdapter;
|
||||
import org.objectweb.asm.commons.InstructionAdapter;
|
||||
|
||||
public class SplineAstNode implements AstNode {
|
||||
public static final String SPLINE_METHOD_DESC;
|
||||
private final CubicSpline<DensityFunctions.Spline.Point, DensityFunctions.Spline.Coordinate> spline;
|
||||
|
||||
public SplineAstNode(CubicSpline<DensityFunctions.Spline.Point, DensityFunctions.Spline.Coordinate> spline) {
|
||||
this.spline = spline;
|
||||
}
|
||||
|
||||
public double evalSingle(int x, int y, int z, EvalType type) {
|
||||
return (double)this.spline.apply(new DensityFunctions.Spline.Point(new NoisePosVanillaInterface(x, y, z, type)));
|
||||
}
|
||||
|
||||
public void evalMulti(double[] res, int[] x, int[] y, int[] z, EvalType type) {
|
||||
for(int i = 0; i < res.length; ++i) {
|
||||
res[i] = this.evalSingle(x[i], y[i], z[i], type);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public AstNode[] getChildren() {
|
||||
return new AstNode[0];
|
||||
}
|
||||
|
||||
public AstNode transform(AstTransformer transformer) {
|
||||
return transformer.transform(this);
|
||||
}
|
||||
|
||||
public void doBytecodeGenSingle(BytecodeGen.Context context, InstructionAdapter m, BytecodeGen.Context.LocalVarConsumer localVarConsumer) {
|
||||
ValuesMethodDef splineMethod = doBytecodeGenSpline(context, this.spline);
|
||||
callSplineSingle(context, m, splineMethod);
|
||||
m.cast(Type.FLOAT_TYPE, Type.DOUBLE_TYPE);
|
||||
m.areturn(Type.DOUBLE_TYPE);
|
||||
}
|
||||
|
||||
private static ValuesMethodDef doBytecodeGenSpline(BytecodeGen.Context context, CubicSpline<DensityFunctions.Spline.Point, DensityFunctions.Spline.Coordinate> spline) {
|
||||
String name = context.getCachedSplineMethod(spline);
|
||||
if (name != null) {
|
||||
return new ValuesMethodDef(false, name, 0.0F);
|
||||
} else if (spline instanceof CubicSpline.Constant) {
|
||||
CubicSpline.Constant<DensityFunctions.Spline.Point, DensityFunctions.Spline.Coordinate> spline1 = (CubicSpline.Constant)spline;
|
||||
return new ValuesMethodDef(true, (String)null, spline1.value());
|
||||
} else {
|
||||
name = context.nextMethodName("Spline");
|
||||
InstructionAdapter m = new InstructionAdapter(new AnalyzerAdapter(context.className, 18, name, SPLINE_METHOD_DESC, context.classWriter.visitMethod(18, name, SPLINE_METHOD_DESC, (String)null, (String[])null)));
|
||||
List<IntObjectPair<Pair<String, String>>> extraLocals = new ArrayList();
|
||||
Label start = new Label();
|
||||
Label end = new Label();
|
||||
m.visitLabel(start);
|
||||
BytecodeGen.Context.LocalVarConsumer localVarConsumer = (localName, localDesc) -> {
|
||||
int ordinal = extraLocals.size() + 5;
|
||||
extraLocals.add(IntObjectPair.of(ordinal, Pair.of(localName, localDesc)));
|
||||
return ordinal;
|
||||
};
|
||||
if (spline instanceof CubicSpline.Multipoint) {
|
||||
CubicSpline.Multipoint<DensityFunctions.Spline.Point, DensityFunctions.Spline.Coordinate> impl = (CubicSpline.Multipoint)spline;
|
||||
ValuesMethodDef[] valuesMethods = (ValuesMethodDef[])impl.values().stream().map((spline1x) -> {
|
||||
return doBytecodeGenSpline(context, spline1x);
|
||||
}).toArray((x$0) -> {
|
||||
return new ValuesMethodDef[x$0];
|
||||
});
|
||||
String locations = context.newField(float[].class, impl.locations());
|
||||
String derivatives = context.newField(float[].class, impl.derivatives());
|
||||
int point = localVarConsumer.createLocalVariable("point", Type.FLOAT_TYPE.getDescriptor());
|
||||
int rangeForLocation = localVarConsumer.createLocalVariable("rangeForLocation", Type.INT_TYPE.getDescriptor());
|
||||
int lastConst = impl.locations().length - 1;
|
||||
String locationFunction = context.newSingleMethod(McToAst.toAst((DensityFunction)((DensityFunctions.Spline.Coordinate)impl.coordinate()).function().value()));
|
||||
context.callDelegateSingle(m, locationFunction);
|
||||
m.cast(Type.DOUBLE_TYPE, Type.FLOAT_TYPE);
|
||||
m.store(point, Type.FLOAT_TYPE);
|
||||
if (valuesMethods.length == 1) {
|
||||
m.load(point, Type.FLOAT_TYPE);
|
||||
m.load(0, InstructionAdapter.OBJECT_TYPE);
|
||||
m.getfield(context.className, locations, Type.getDescriptor(float[].class));
|
||||
callSplineSingle(context, m, valuesMethods[0]);
|
||||
m.load(0, InstructionAdapter.OBJECT_TYPE);
|
||||
m.getfield(context.className, derivatives, Type.getDescriptor(float[].class));
|
||||
m.iconst(0);
|
||||
m.invokestatic(Type.getInternalName(SplineSupport.class), "sampleOutsideRange", Type.getMethodDescriptor(Type.FLOAT_TYPE, new Type[]{Type.FLOAT_TYPE, Type.getType(float[].class), Type.FLOAT_TYPE, Type.getType(float[].class), Type.INT_TYPE}), false);
|
||||
m.areturn(Type.FLOAT_TYPE);
|
||||
} else {
|
||||
m.load(0, InstructionAdapter.OBJECT_TYPE);
|
||||
m.getfield(context.className, locations, Type.getDescriptor(float[].class));
|
||||
m.load(point, Type.FLOAT_TYPE);
|
||||
m.invokestatic(Type.getInternalName(SplineSupport.class), "findRangeForLocation", Type.getMethodDescriptor(Type.INT_TYPE, new Type[]{Type.getType(float[].class), Type.FLOAT_TYPE}), false);
|
||||
m.store(rangeForLocation, Type.INT_TYPE);
|
||||
Label label1 = new Label();
|
||||
Label label2 = new Label();
|
||||
m.load(rangeForLocation, Type.INT_TYPE);
|
||||
m.ifge(label1);
|
||||
m.load(point, Type.FLOAT_TYPE);
|
||||
m.load(0, InstructionAdapter.OBJECT_TYPE);
|
||||
m.getfield(context.className, locations, Type.getDescriptor(float[].class));
|
||||
callSplineSingle(context, m, valuesMethods[0]);
|
||||
m.load(0, InstructionAdapter.OBJECT_TYPE);
|
||||
m.getfield(context.className, derivatives, Type.getDescriptor(float[].class));
|
||||
m.iconst(0);
|
||||
m.invokestatic(Type.getInternalName(SplineSupport.class), "sampleOutsideRange", Type.getMethodDescriptor(Type.FLOAT_TYPE, new Type[]{Type.FLOAT_TYPE, Type.getType(float[].class), Type.FLOAT_TYPE, Type.getType(float[].class), Type.INT_TYPE}), false);
|
||||
m.areturn(Type.FLOAT_TYPE);
|
||||
m.visitLabel(label1);
|
||||
m.load(rangeForLocation, Type.INT_TYPE);
|
||||
m.iconst(lastConst);
|
||||
m.ificmpne(label2);
|
||||
m.load(point, Type.FLOAT_TYPE);
|
||||
m.load(0, InstructionAdapter.OBJECT_TYPE);
|
||||
m.getfield(context.className, locations, Type.getDescriptor(float[].class));
|
||||
callSplineSingle(context, m, valuesMethods[lastConst]);
|
||||
m.load(0, InstructionAdapter.OBJECT_TYPE);
|
||||
m.getfield(context.className, derivatives, Type.getDescriptor(float[].class));
|
||||
m.iconst(lastConst);
|
||||
m.invokestatic(Type.getInternalName(SplineSupport.class), "sampleOutsideRange", Type.getMethodDescriptor(Type.FLOAT_TYPE, new Type[]{Type.FLOAT_TYPE, Type.getType(float[].class), Type.FLOAT_TYPE, Type.getType(float[].class), Type.INT_TYPE}), false);
|
||||
m.areturn(Type.FLOAT_TYPE);
|
||||
m.visitLabel(label2);
|
||||
int loc0 = localVarConsumer.createLocalVariable("loc0", Type.FLOAT_TYPE.getDescriptor());
|
||||
int loc1 = localVarConsumer.createLocalVariable("loc1", Type.FLOAT_TYPE.getDescriptor());
|
||||
int locDist = localVarConsumer.createLocalVariable("locDist", Type.FLOAT_TYPE.getDescriptor());
|
||||
int k = localVarConsumer.createLocalVariable("k", Type.FLOAT_TYPE.getDescriptor());
|
||||
int n = localVarConsumer.createLocalVariable("n", Type.FLOAT_TYPE.getDescriptor());
|
||||
int o = localVarConsumer.createLocalVariable("o", Type.FLOAT_TYPE.getDescriptor());
|
||||
int onDist = localVarConsumer.createLocalVariable("onDist", Type.FLOAT_TYPE.getDescriptor());
|
||||
int p = localVarConsumer.createLocalVariable("p", Type.FLOAT_TYPE.getDescriptor());
|
||||
int q = localVarConsumer.createLocalVariable("q", Type.FLOAT_TYPE.getDescriptor());
|
||||
m.load(0, InstructionAdapter.OBJECT_TYPE);
|
||||
m.getfield(context.className, locations, Type.getDescriptor(float[].class));
|
||||
m.load(rangeForLocation, Type.INT_TYPE);
|
||||
m.aload(Type.FLOAT_TYPE);
|
||||
m.store(loc0, Type.FLOAT_TYPE);
|
||||
m.load(0, InstructionAdapter.OBJECT_TYPE);
|
||||
m.getfield(context.className, locations, Type.getDescriptor(float[].class));
|
||||
m.load(rangeForLocation, Type.INT_TYPE);
|
||||
m.iconst(1);
|
||||
m.add(Type.INT_TYPE);
|
||||
m.aload(Type.FLOAT_TYPE);
|
||||
m.store(loc1, Type.FLOAT_TYPE);
|
||||
m.load(loc1, Type.FLOAT_TYPE);
|
||||
m.load(loc0, Type.FLOAT_TYPE);
|
||||
m.sub(Type.FLOAT_TYPE);
|
||||
m.store(locDist, Type.FLOAT_TYPE);
|
||||
m.load(point, Type.FLOAT_TYPE);
|
||||
m.load(loc0, Type.FLOAT_TYPE);
|
||||
m.sub(Type.FLOAT_TYPE);
|
||||
m.load(locDist, Type.FLOAT_TYPE);
|
||||
m.div(Type.FLOAT_TYPE);
|
||||
m.store(k, Type.FLOAT_TYPE);
|
||||
Label[] jumpLabels = new Label[valuesMethods.length - 1];
|
||||
boolean[] jumpGenerated = new boolean[valuesMethods.length - 1];
|
||||
|
||||
for(int i = 0; i < valuesMethods.length - 1; ++i) {
|
||||
jumpLabels[i] = new Label();
|
||||
}
|
||||
|
||||
Label defaultLabel = new Label();
|
||||
Label label3 = new Label();
|
||||
m.load(rangeForLocation, Type.INT_TYPE);
|
||||
m.tableswitch(0, valuesMethods.length - 2, defaultLabel, jumpLabels);
|
||||
|
||||
for(int i = 0; i < valuesMethods.length - 1; ++i) {
|
||||
if (!jumpGenerated[i]) {
|
||||
m.visitLabel(jumpLabels[i]);
|
||||
jumpGenerated[i] = true;
|
||||
|
||||
for(int j = i + 1; j < valuesMethods.length - 1; ++j) {
|
||||
if (valuesMethods[i].equals(valuesMethods[j]) && valuesMethods[i + 1].equals(valuesMethods[j + 1])) {
|
||||
m.visitLabel(jumpLabels[j]);
|
||||
jumpGenerated[j] = true;
|
||||
}
|
||||
}
|
||||
|
||||
callSplineSingle(context, m, valuesMethods[i]);
|
||||
if (valuesMethods[i].equals(valuesMethods[i + 1])) {
|
||||
m.dup();
|
||||
m.store(n, Type.FLOAT_TYPE);
|
||||
m.store(o, Type.FLOAT_TYPE);
|
||||
} else {
|
||||
m.store(n, Type.FLOAT_TYPE);
|
||||
callSplineSingle(context, m, valuesMethods[i + 1]);
|
||||
m.store(o, Type.FLOAT_TYPE);
|
||||
}
|
||||
|
||||
m.goTo(label3);
|
||||
}
|
||||
}
|
||||
|
||||
m.visitLabel(defaultLabel);
|
||||
m.iconst(0);
|
||||
m.aconst("boom");
|
||||
m.invokestatic(Type.getInternalName(Assertions.class), "assertTrue", Type.getMethodDescriptor(Type.VOID_TYPE, new Type[]{Type.BOOLEAN_TYPE, Type.getType(String.class)}), false);
|
||||
m.fconst(Float.NaN);
|
||||
m.areturn(Type.FLOAT_TYPE);
|
||||
m.visitLabel(label3);
|
||||
m.load(o, Type.FLOAT_TYPE);
|
||||
m.load(n, Type.FLOAT_TYPE);
|
||||
m.sub(Type.FLOAT_TYPE);
|
||||
m.store(onDist, Type.FLOAT_TYPE);
|
||||
m.load(0, InstructionAdapter.OBJECT_TYPE);
|
||||
m.getfield(context.className, derivatives, Type.getDescriptor(float[].class));
|
||||
m.load(rangeForLocation, Type.INT_TYPE);
|
||||
m.aload(Type.FLOAT_TYPE);
|
||||
m.load(locDist, Type.FLOAT_TYPE);
|
||||
m.mul(Type.FLOAT_TYPE);
|
||||
m.load(onDist, Type.FLOAT_TYPE);
|
||||
m.sub(Type.FLOAT_TYPE);
|
||||
m.store(p, Type.FLOAT_TYPE);
|
||||
m.load(0, InstructionAdapter.OBJECT_TYPE);
|
||||
m.getfield(context.className, derivatives, Type.getDescriptor(float[].class));
|
||||
m.load(rangeForLocation, Type.INT_TYPE);
|
||||
m.iconst(1);
|
||||
m.add(Type.INT_TYPE);
|
||||
m.aload(Type.FLOAT_TYPE);
|
||||
m.neg(Type.FLOAT_TYPE);
|
||||
m.load(locDist, Type.FLOAT_TYPE);
|
||||
m.mul(Type.FLOAT_TYPE);
|
||||
m.load(onDist, Type.FLOAT_TYPE);
|
||||
m.add(Type.FLOAT_TYPE);
|
||||
m.store(q, Type.FLOAT_TYPE);
|
||||
m.load(k, Type.FLOAT_TYPE);
|
||||
m.load(n, Type.FLOAT_TYPE);
|
||||
m.load(o, Type.FLOAT_TYPE);
|
||||
m.invokestatic(Type.getInternalName(Mth.class), "lerp", "(FFF)F", false);
|
||||
m.load(k, Type.FLOAT_TYPE);
|
||||
m.fconst(1.0F);
|
||||
m.load(k, Type.FLOAT_TYPE);
|
||||
m.sub(Type.FLOAT_TYPE);
|
||||
m.mul(Type.FLOAT_TYPE);
|
||||
m.load(k, Type.FLOAT_TYPE);
|
||||
m.load(p, Type.FLOAT_TYPE);
|
||||
m.load(q, Type.FLOAT_TYPE);
|
||||
m.invokestatic(Type.getInternalName(Mth.class), "lerp", "(FFF)F", false);
|
||||
m.mul(Type.FLOAT_TYPE);
|
||||
m.add(Type.FLOAT_TYPE);
|
||||
m.areturn(Type.FLOAT_TYPE);
|
||||
}
|
||||
} else {
|
||||
if (!(spline instanceof CubicSpline.Constant)) {
|
||||
throw new UnsupportedOperationException(String.format("Unsupported spline implementation: %s", spline.getClass().getName()));
|
||||
}
|
||||
|
||||
CubicSpline.Constant<DensityFunctions.Spline.Point, DensityFunctions.Spline.Coordinate> floatFunction = (CubicSpline.Constant)spline;
|
||||
m.fconst(floatFunction.value());
|
||||
m.areturn(Type.FLOAT_TYPE);
|
||||
}
|
||||
|
||||
m.visitLabel(end);
|
||||
m.visitLocalVariable("this", context.classDesc, (String)null, start, end, 0);
|
||||
m.visitLocalVariable("x", Type.INT_TYPE.getDescriptor(), (String)null, start, end, 1);
|
||||
m.visitLocalVariable("y", Type.INT_TYPE.getDescriptor(), (String)null, start, end, 2);
|
||||
m.visitLocalVariable("z", Type.INT_TYPE.getDescriptor(), (String)null, start, end, 3);
|
||||
m.visitLocalVariable("evalType", Type.getType(EvalType.class).getDescriptor(), (String)null, start, end, 4);
|
||||
Iterator var35 = extraLocals.iterator();
|
||||
|
||||
while(var35.hasNext()) {
|
||||
IntObjectPair<Pair<String, String>> local = (IntObjectPair)var35.next();
|
||||
m.visitLocalVariable((String)((Pair)local.right()).left(), (String)((Pair)local.right()).right(), (String)null, start, end, local.leftInt());
|
||||
}
|
||||
|
||||
m.visitMaxs(0, 0);
|
||||
context.cacheSplineMethod(spline, name);
|
||||
return new ValuesMethodDef(false, name, 0.0F);
|
||||
}
|
||||
}
|
||||
|
||||
private static void callSplineSingle(BytecodeGen.Context context, InstructionAdapter m, ValuesMethodDef target) {
|
||||
if (target.isConst()) {
|
||||
m.fconst(target.constValue());
|
||||
} else {
|
||||
m.load(0, InstructionAdapter.OBJECT_TYPE);
|
||||
m.load(1, Type.INT_TYPE);
|
||||
m.load(2, Type.INT_TYPE);
|
||||
m.load(3, Type.INT_TYPE);
|
||||
m.load(4, InstructionAdapter.OBJECT_TYPE);
|
||||
m.invokevirtual(context.className, target.generatedMethod(), SPLINE_METHOD_DESC, false);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void doBytecodeGenMulti(BytecodeGen.Context context, InstructionAdapter m, BytecodeGen.Context.LocalVarConsumer localVarConsumer) {
|
||||
context.delegateToSingle(m, localVarConsumer, this);
|
||||
m.areturn(Type.VOID_TYPE);
|
||||
}
|
||||
|
||||
private static boolean deepEquals(CubicSpline<DensityFunctions.Spline.Point, DensityFunctions.Spline.Coordinate> a, CubicSpline<DensityFunctions.Spline.Point, DensityFunctions.Spline.Coordinate> b) {
|
||||
if (a instanceof CubicSpline.Constant<DensityFunctions.Spline.Point, DensityFunctions.Spline.Coordinate> a1) {
|
||||
if (b instanceof CubicSpline.Constant<DensityFunctions.Spline.Point, DensityFunctions.Spline.Coordinate> b1) {
|
||||
return a1.value() == b1.value();
|
||||
}
|
||||
}
|
||||
|
||||
if (a instanceof CubicSpline.Multipoint<DensityFunctions.Spline.Point, DensityFunctions.Spline.Coordinate> a1) {
|
||||
if (b instanceof CubicSpline.Multipoint<DensityFunctions.Spline.Point, DensityFunctions.Spline.Coordinate> b1) {
|
||||
boolean equals1 = Arrays.equals(a1.derivatives(), b1.derivatives()) && Arrays.equals(a1.locations(), b1.locations()) && a1.values().size() == b1.values().size() && McToAst.toAst((DensityFunction)((DensityFunctions.Spline.Coordinate)a1.coordinate()).function().value()).equals(McToAst.toAst((DensityFunction)((DensityFunctions.Spline.Coordinate)b1.coordinate()).function().value()));
|
||||
if (!equals1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int size = a1.values().size();
|
||||
|
||||
for(int i = 0; i < size; ++i) {
|
||||
if (!deepEquals((CubicSpline)a1.values().get(i), (CubicSpline)b1.values().get(i))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static boolean deepRelaxedEquals(CubicSpline<DensityFunctions.Spline.Point, DensityFunctions.Spline.Coordinate> a, CubicSpline<DensityFunctions.Spline.Point, DensityFunctions.Spline.Coordinate> b) {
|
||||
if (a instanceof CubicSpline.Constant<DensityFunctions.Spline.Point, DensityFunctions.Spline.Coordinate> a1) {
|
||||
if (b instanceof CubicSpline.Constant<DensityFunctions.Spline.Point, DensityFunctions.Spline.Coordinate> b1) {
|
||||
return a1.value() == b1.value();
|
||||
}
|
||||
}
|
||||
|
||||
if (a instanceof CubicSpline.Multipoint<DensityFunctions.Spline.Point, DensityFunctions.Spline.Coordinate> a1) {
|
||||
if (b instanceof CubicSpline.Multipoint<DensityFunctions.Spline.Point, DensityFunctions.Spline.Coordinate> b1) {
|
||||
boolean equals1 = a1.values().size() == b1.values().size() && McToAst.toAst((DensityFunction)((DensityFunctions.Spline.Coordinate)a1.coordinate()).function().value()).relaxedEquals(McToAst.toAst((DensityFunction)((DensityFunctions.Spline.Coordinate)b1.coordinate()).function().value()));
|
||||
if (!equals1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int size = a1.values().size();
|
||||
|
||||
for(int i = 0; i < size; ++i) {
|
||||
if (!deepRelaxedEquals((CubicSpline)a1.values().get(i), (CubicSpline)b1.values().get(i))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static int deepHashcode(CubicSpline<DensityFunctions.Spline.Point, DensityFunctions.Spline.Coordinate> a) {
|
||||
if (a instanceof CubicSpline.Constant<DensityFunctions.Spline.Point, DensityFunctions.Spline.Coordinate> a1) {
|
||||
return Float.hashCode(a1.value());
|
||||
} else if (!(a instanceof CubicSpline.Multipoint<DensityFunctions.Spline.Point, DensityFunctions.Spline.Coordinate> a1)) {
|
||||
return a.hashCode();
|
||||
} else {
|
||||
int result = 1;
|
||||
result = 31 * result + Arrays.hashCode(a1.derivatives());
|
||||
result = 31 * result + Arrays.hashCode(a1.locations());
|
||||
|
||||
CubicSpline spline;
|
||||
for(Iterator var4 = a1.values().iterator(); var4.hasNext(); result = 31 * result + deepHashcode(spline)) {
|
||||
spline = (CubicSpline)var4.next();
|
||||
}
|
||||
|
||||
result = 31 * result + McToAst.toAst((DensityFunction)((DensityFunctions.Spline.Coordinate)a1.coordinate()).function().value()).hashCode();
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
private static int deepRelaxedHashcode(CubicSpline<DensityFunctions.Spline.Point, DensityFunctions.Spline.Coordinate> a) {
|
||||
if (a instanceof CubicSpline.Constant<DensityFunctions.Spline.Point, DensityFunctions.Spline.Coordinate> a1) {
|
||||
return Float.hashCode(a1.value());
|
||||
} else if (!(a instanceof CubicSpline.Multipoint<DensityFunctions.Spline.Point, DensityFunctions.Spline.Coordinate> a1)) {
|
||||
return a.hashCode();
|
||||
} else {
|
||||
int result = 1;
|
||||
|
||||
CubicSpline spline;
|
||||
for(Iterator var4 = a1.values().iterator(); var4.hasNext(); result = 31 * result + deepRelaxedHashcode(spline)) {
|
||||
spline = (CubicSpline)var4.next();
|
||||
}
|
||||
|
||||
result = 31 * result + McToAst.toAst((DensityFunction)((DensityFunctions.Spline.Coordinate)a1.coordinate()).function().value()).relaxedHashCode();
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
} else if (o != null && this.getClass() == o.getClass()) {
|
||||
SplineAstNode that = (SplineAstNode)o;
|
||||
return deepEquals(this.spline, that.spline);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
return deepHashcode(this.spline);
|
||||
}
|
||||
|
||||
public boolean relaxedEquals(AstNode o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
} else if (o != null && this.getClass() == o.getClass()) {
|
||||
SplineAstNode that = (SplineAstNode)o;
|
||||
return deepRelaxedEquals(this.spline, that.spline);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public int relaxedHashCode() {
|
||||
return deepRelaxedHashcode(this.spline);
|
||||
}
|
||||
|
||||
static {
|
||||
SPLINE_METHOD_DESC = Type.getMethodDescriptor(Type.getType(Float.TYPE), new Type[]{Type.getType(Integer.TYPE), Type.getType(Integer.TYPE), Type.getType(Integer.TYPE), Type.getType(EvalType.class)});
|
||||
}
|
||||
|
||||
private static record ValuesMethodDef(boolean isConst, String generatedMethod, float constValue) {
|
||||
private ValuesMethodDef(boolean isConst, String generatedMethod, float constValue) {
|
||||
this.isConst = isConst;
|
||||
this.generatedMethod = generatedMethod;
|
||||
this.constValue = constValue;
|
||||
}
|
||||
|
||||
public boolean isConst() {
|
||||
return this.isConst;
|
||||
}
|
||||
|
||||
public String generatedMethod() {
|
||||
return this.generatedMethod;
|
||||
}
|
||||
|
||||
public float constValue() {
|
||||
return this.constValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package org.bxteam.divinemc.dfc.common.ast.spline;
|
||||
|
||||
public class SplineSupport {
|
||||
public SplineSupport() {
|
||||
}
|
||||
|
||||
public static int findRangeForLocation(float[] locations, float x) {
|
||||
int min = 0;
|
||||
int i = locations.length;
|
||||
|
||||
while(i > 0) {
|
||||
int j = i / 2;
|
||||
int k = min + j;
|
||||
if (x < locations[k]) {
|
||||
i = j;
|
||||
} else {
|
||||
min = k + 1;
|
||||
i -= j + 1;
|
||||
}
|
||||
}
|
||||
|
||||
return min - 1;
|
||||
}
|
||||
|
||||
public static float sampleOutsideRange(float point, float[] locations, float value, float[] derivatives, int i) {
|
||||
float f = derivatives[i];
|
||||
return f == 0.0F ? value : value + f * (point - locations[i]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
package org.bxteam.divinemc.dfc.common.ast.unary;
|
||||
|
||||
import org.bxteam.divinemc.dfc.common.ast.AstNode;
|
||||
import org.bxteam.divinemc.dfc.common.ast.EvalType;
|
||||
import org.bxteam.divinemc.dfc.common.gen.BytecodeGen;
|
||||
import org.objectweb.asm.Type;
|
||||
import org.objectweb.asm.commons.InstructionAdapter;
|
||||
|
||||
public class AbsNode extends AbstractUnaryNode {
|
||||
public AbsNode(AstNode operand) {
|
||||
super(operand);
|
||||
}
|
||||
|
||||
protected AstNode newInstance(AstNode operand) {
|
||||
return new AbsNode(operand);
|
||||
}
|
||||
|
||||
public double evalSingle(int x, int y, int z, EvalType type) {
|
||||
return Math.abs(this.operand.evalSingle(x, y, z, type));
|
||||
}
|
||||
|
||||
public void evalMulti(double[] res, int[] x, int[] y, int[] z, EvalType type) {
|
||||
this.operand.evalMulti(res, x, y, z, type);
|
||||
|
||||
for(int i = 0; i < res.length; ++i) {
|
||||
res[i] = Math.abs(res[i]);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void doBytecodeGenSingle(BytecodeGen.Context context, InstructionAdapter m, BytecodeGen.Context.LocalVarConsumer localVarConsumer) {
|
||||
super.doBytecodeGenSingle(context, m, localVarConsumer);
|
||||
m.invokestatic(Type.getInternalName(Math.class), "abs", Type.getMethodDescriptor(Type.DOUBLE_TYPE, new Type[]{Type.DOUBLE_TYPE}), false);
|
||||
m.areturn(Type.DOUBLE_TYPE);
|
||||
}
|
||||
|
||||
public void doBytecodeGenMulti(BytecodeGen.Context context, InstructionAdapter m, BytecodeGen.Context.LocalVarConsumer localVarConsumer) {
|
||||
super.doBytecodeGenMulti(context, m, localVarConsumer);
|
||||
context.doCountedLoop(m, localVarConsumer, (idx) -> {
|
||||
m.load(1, InstructionAdapter.OBJECT_TYPE);
|
||||
m.load(idx, Type.INT_TYPE);
|
||||
m.dup2();
|
||||
m.aload(Type.DOUBLE_TYPE);
|
||||
m.invokestatic(Type.getInternalName(Math.class), "abs", Type.getMethodDescriptor(Type.DOUBLE_TYPE, new Type[]{Type.DOUBLE_TYPE}), false);
|
||||
m.astore(Type.DOUBLE_TYPE);
|
||||
});
|
||||
m.areturn(Type.VOID_TYPE);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
package org.bxteam.divinemc.dfc.common.ast.unary;
|
||||
|
||||
import org.bxteam.divinemc.dfc.common.ast.AstNode;
|
||||
import org.bxteam.divinemc.dfc.common.ast.AstTransformer;
|
||||
import org.bxteam.divinemc.dfc.common.gen.BytecodeGen;
|
||||
import java.util.Objects;
|
||||
import org.objectweb.asm.commons.InstructionAdapter;
|
||||
|
||||
public abstract class AbstractUnaryNode implements AstNode {
|
||||
protected final AstNode operand;
|
||||
|
||||
public AbstractUnaryNode(AstNode operand) {
|
||||
this.operand = (AstNode)Objects.requireNonNull(operand);
|
||||
}
|
||||
|
||||
public AstNode[] getChildren() {
|
||||
return new AstNode[]{this.operand};
|
||||
}
|
||||
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
} else if (o != null && this.getClass() == o.getClass()) {
|
||||
AbstractUnaryNode that = (AbstractUnaryNode)o;
|
||||
return Objects.equals(this.operand, that.operand);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
int result = 1;
|
||||
result = 31 * result + this.getClass().hashCode();
|
||||
result = 31 * result + this.operand.hashCode();
|
||||
return result;
|
||||
}
|
||||
|
||||
public boolean relaxedEquals(AstNode o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
} else if (o != null && this.getClass() == o.getClass()) {
|
||||
AbstractUnaryNode that = (AbstractUnaryNode)o;
|
||||
return this.operand.relaxedEquals(that.operand);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public int relaxedHashCode() {
|
||||
int result = 1;
|
||||
result = 31 * result + this.getClass().hashCode();
|
||||
result = 31 * result + this.operand.relaxedHashCode();
|
||||
return result;
|
||||
}
|
||||
|
||||
protected abstract AstNode newInstance(AstNode var1);
|
||||
|
||||
public AstNode transform(AstTransformer transformer) {
|
||||
AstNode operand = this.operand.transform(transformer);
|
||||
return this.operand == operand ? transformer.transform(this) : transformer.transform(this.newInstance(operand));
|
||||
}
|
||||
|
||||
public void doBytecodeGenSingle(BytecodeGen.Context context, InstructionAdapter m, BytecodeGen.Context.LocalVarConsumer localVarConsumer) {
|
||||
String operandMethod = context.newSingleMethod(this.operand);
|
||||
context.callDelegateSingle(m, operandMethod);
|
||||
}
|
||||
|
||||
public void doBytecodeGenMulti(BytecodeGen.Context context, InstructionAdapter m, BytecodeGen.Context.LocalVarConsumer localVarConsumer) {
|
||||
String operandMethod = context.newMultiMethod(this.operand);
|
||||
context.callDelegateMulti(m, operandMethod);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
package org.bxteam.divinemc.dfc.common.ast.unary;
|
||||
|
||||
import org.bxteam.divinemc.dfc.common.ast.AstNode;
|
||||
import org.bxteam.divinemc.dfc.common.ast.EvalType;
|
||||
import org.bxteam.divinemc.dfc.common.gen.BytecodeGen;
|
||||
import org.objectweb.asm.Type;
|
||||
import org.objectweb.asm.commons.InstructionAdapter;
|
||||
|
||||
public class CubeNode extends AbstractUnaryNode {
|
||||
public CubeNode(AstNode operand) {
|
||||
super(operand);
|
||||
}
|
||||
|
||||
protected AstNode newInstance(AstNode operand) {
|
||||
return new CubeNode(operand);
|
||||
}
|
||||
|
||||
public double evalSingle(int x, int y, int z, EvalType type) {
|
||||
double v = this.operand.evalSingle(x, y, z, type);
|
||||
return v * v * v;
|
||||
}
|
||||
|
||||
public void evalMulti(double[] res, int[] x, int[] y, int[] z, EvalType type) {
|
||||
this.operand.evalMulti(res, x, y, z, type);
|
||||
|
||||
for(int i = 0; i < res.length; ++i) {
|
||||
res[i] = res[i] * res[i] * res[i];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void doBytecodeGenSingle(BytecodeGen.Context context, InstructionAdapter m, BytecodeGen.Context.LocalVarConsumer localVarConsumer) {
|
||||
super.doBytecodeGenSingle(context, m, localVarConsumer);
|
||||
m.dup2();
|
||||
m.dup2();
|
||||
m.mul(Type.DOUBLE_TYPE);
|
||||
m.mul(Type.DOUBLE_TYPE);
|
||||
m.areturn(Type.DOUBLE_TYPE);
|
||||
}
|
||||
|
||||
public void doBytecodeGenMulti(BytecodeGen.Context context, InstructionAdapter m, BytecodeGen.Context.LocalVarConsumer localVarConsumer) {
|
||||
super.doBytecodeGenMulti(context, m, localVarConsumer);
|
||||
context.doCountedLoop(m, localVarConsumer, (idx) -> {
|
||||
m.load(1, InstructionAdapter.OBJECT_TYPE);
|
||||
m.load(idx, Type.INT_TYPE);
|
||||
m.dup2();
|
||||
m.aload(Type.DOUBLE_TYPE);
|
||||
m.dup2();
|
||||
m.dup2();
|
||||
m.mul(Type.DOUBLE_TYPE);
|
||||
m.mul(Type.DOUBLE_TYPE);
|
||||
m.astore(Type.DOUBLE_TYPE);
|
||||
});
|
||||
m.areturn(Type.VOID_TYPE);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
package org.bxteam.divinemc.dfc.common.ast.unary;
|
||||
|
||||
import org.bxteam.divinemc.dfc.common.ast.AstNode;
|
||||
import org.bxteam.divinemc.dfc.common.ast.EvalType;
|
||||
import org.bxteam.divinemc.dfc.common.gen.BytecodeGen;
|
||||
import org.objectweb.asm.Label;
|
||||
import org.objectweb.asm.Type;
|
||||
import org.objectweb.asm.commons.InstructionAdapter;
|
||||
|
||||
public class NegMulNode extends AbstractUnaryNode {
|
||||
private final double negMul;
|
||||
|
||||
public NegMulNode(AstNode operand, double negMul) {
|
||||
super(operand);
|
||||
this.negMul = negMul;
|
||||
}
|
||||
|
||||
protected AstNode newInstance(AstNode operand) {
|
||||
return new NegMulNode(operand, this.negMul);
|
||||
}
|
||||
|
||||
public double evalSingle(int x, int y, int z, EvalType type) {
|
||||
double v = this.operand.evalSingle(x, y, z, type);
|
||||
return v > 0.0 ? v : v * this.negMul;
|
||||
}
|
||||
|
||||
public void evalMulti(double[] res, int[] x, int[] y, int[] z, EvalType type) {
|
||||
this.operand.evalMulti(res, x, y, z, type);
|
||||
|
||||
for(int i = 0; i < res.length; ++i) {
|
||||
double v = res[i];
|
||||
res[i] = v > 0.0 ? v : v * this.negMul;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void doBytecodeGenSingle(BytecodeGen.Context context, InstructionAdapter m, BytecodeGen.Context.LocalVarConsumer localVarConsumer) {
|
||||
super.doBytecodeGenSingle(context, m, localVarConsumer);
|
||||
int v = localVarConsumer.createLocalVariable("v", Type.DOUBLE_TYPE.getDescriptor());
|
||||
m.store(v, Type.DOUBLE_TYPE);
|
||||
Label negMulLabel = new Label();
|
||||
Label end = new Label();
|
||||
m.load(v, Type.DOUBLE_TYPE);
|
||||
m.dconst(0.0);
|
||||
m.cmpl(Type.DOUBLE_TYPE);
|
||||
m.ifle(negMulLabel);
|
||||
m.load(v, Type.DOUBLE_TYPE);
|
||||
m.goTo(end);
|
||||
m.visitLabel(negMulLabel);
|
||||
m.load(v, Type.DOUBLE_TYPE);
|
||||
m.dconst(this.negMul);
|
||||
m.mul(Type.DOUBLE_TYPE);
|
||||
m.visitLabel(end);
|
||||
m.areturn(Type.DOUBLE_TYPE);
|
||||
}
|
||||
|
||||
public void doBytecodeGenMulti(BytecodeGen.Context context, InstructionAdapter m, BytecodeGen.Context.LocalVarConsumer localVarConsumer) {
|
||||
super.doBytecodeGenMulti(context, m, localVarConsumer);
|
||||
context.doCountedLoop(m, localVarConsumer, (idx) -> {
|
||||
int v = localVarConsumer.createLocalVariable("v", Type.DOUBLE_TYPE.getDescriptor());
|
||||
m.load(1, InstructionAdapter.OBJECT_TYPE);
|
||||
m.load(idx, Type.INT_TYPE);
|
||||
m.dup2();
|
||||
m.aload(Type.DOUBLE_TYPE);
|
||||
m.store(v, Type.DOUBLE_TYPE);
|
||||
Label negMulLabel = new Label();
|
||||
Label end = new Label();
|
||||
m.load(v, Type.DOUBLE_TYPE);
|
||||
m.dconst(0.0);
|
||||
m.cmpl(Type.DOUBLE_TYPE);
|
||||
m.ifle(negMulLabel);
|
||||
m.load(v, Type.DOUBLE_TYPE);
|
||||
m.goTo(end);
|
||||
m.visitLabel(negMulLabel);
|
||||
m.load(v, Type.DOUBLE_TYPE);
|
||||
m.dconst(this.negMul);
|
||||
m.mul(Type.DOUBLE_TYPE);
|
||||
m.visitLabel(end);
|
||||
m.astore(Type.DOUBLE_TYPE);
|
||||
});
|
||||
m.areturn(Type.VOID_TYPE);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
package org.bxteam.divinemc.dfc.common.ast.unary;
|
||||
|
||||
import org.bxteam.divinemc.dfc.common.ast.AstNode;
|
||||
import org.bxteam.divinemc.dfc.common.ast.EvalType;
|
||||
import org.bxteam.divinemc.dfc.common.gen.BytecodeGen;
|
||||
import org.objectweb.asm.Type;
|
||||
import org.objectweb.asm.commons.InstructionAdapter;
|
||||
|
||||
public class SquareNode extends AbstractUnaryNode {
|
||||
public SquareNode(AstNode operand) {
|
||||
super(operand);
|
||||
}
|
||||
|
||||
protected AstNode newInstance(AstNode operand) {
|
||||
return new SquareNode(operand);
|
||||
}
|
||||
|
||||
public double evalSingle(int x, int y, int z, EvalType type) {
|
||||
double v = this.operand.evalSingle(x, y, z, type);
|
||||
return v * v;
|
||||
}
|
||||
|
||||
public void evalMulti(double[] res, int[] x, int[] y, int[] z, EvalType type) {
|
||||
this.operand.evalMulti(res, x, y, z, type);
|
||||
|
||||
for(int i = 0; i < res.length; ++i) {
|
||||
res[i] *= res[i];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void doBytecodeGenSingle(BytecodeGen.Context context, InstructionAdapter m, BytecodeGen.Context.LocalVarConsumer localVarConsumer) {
|
||||
super.doBytecodeGenSingle(context, m, localVarConsumer);
|
||||
m.dup2();
|
||||
m.mul(Type.DOUBLE_TYPE);
|
||||
m.areturn(Type.DOUBLE_TYPE);
|
||||
}
|
||||
|
||||
public void doBytecodeGenMulti(BytecodeGen.Context context, InstructionAdapter m, BytecodeGen.Context.LocalVarConsumer localVarConsumer) {
|
||||
super.doBytecodeGenMulti(context, m, localVarConsumer);
|
||||
context.doCountedLoop(m, localVarConsumer, (idx) -> {
|
||||
m.load(1, InstructionAdapter.OBJECT_TYPE);
|
||||
m.load(idx, Type.INT_TYPE);
|
||||
m.dup2();
|
||||
m.aload(Type.DOUBLE_TYPE);
|
||||
m.dup2();
|
||||
m.mul(Type.DOUBLE_TYPE);
|
||||
m.astore(Type.DOUBLE_TYPE);
|
||||
});
|
||||
m.areturn(Type.VOID_TYPE);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
package org.bxteam.divinemc.dfc.common.ast.unary;
|
||||
|
||||
import org.bxteam.divinemc.dfc.common.ast.AstNode;
|
||||
import org.bxteam.divinemc.dfc.common.ast.EvalType;
|
||||
import org.bxteam.divinemc.dfc.common.gen.BytecodeGen;
|
||||
import net.minecraft.util.Mth;
|
||||
import org.objectweb.asm.Type;
|
||||
import org.objectweb.asm.commons.InstructionAdapter;
|
||||
|
||||
public class SqueezeNode extends AbstractUnaryNode {
|
||||
public SqueezeNode(AstNode operand) {
|
||||
super(operand);
|
||||
}
|
||||
|
||||
protected AstNode newInstance(AstNode operand) {
|
||||
return new SqueezeNode(operand);
|
||||
}
|
||||
|
||||
public double evalSingle(int x, int y, int z, EvalType type) {
|
||||
double v = Mth.clamp(this.operand.evalSingle(x, y, z, type), -1.0, 1.0);
|
||||
return v / 2.0 - v * v * v / 24.0;
|
||||
}
|
||||
|
||||
public void evalMulti(double[] res, int[] x, int[] y, int[] z, EvalType type) {
|
||||
this.operand.evalMulti(res, x, y, z, type);
|
||||
|
||||
for(int i = 0; i < res.length; ++i) {
|
||||
double v = Mth.clamp(res[i], -1.0, 1.0);
|
||||
res[i] = v / 2.0 - v * v * v / 24.0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void doBytecodeGenSingle(BytecodeGen.Context context, InstructionAdapter m, BytecodeGen.Context.LocalVarConsumer localVarConsumer) {
|
||||
super.doBytecodeGenSingle(context, m, localVarConsumer);
|
||||
m.dconst(1.0);
|
||||
m.invokestatic(Type.getInternalName(Math.class), "min", Type.getMethodDescriptor(Type.DOUBLE_TYPE, new Type[]{Type.DOUBLE_TYPE, Type.DOUBLE_TYPE}), false);
|
||||
m.dconst(-1.0);
|
||||
m.invokestatic(Type.getInternalName(Math.class), "max", Type.getMethodDescriptor(Type.DOUBLE_TYPE, new Type[]{Type.DOUBLE_TYPE, Type.DOUBLE_TYPE}), false);
|
||||
int v = localVarConsumer.createLocalVariable("v", Type.DOUBLE_TYPE.getDescriptor());
|
||||
m.store(v, Type.DOUBLE_TYPE);
|
||||
m.load(v, Type.DOUBLE_TYPE);
|
||||
m.dconst(2.0);
|
||||
m.div(Type.DOUBLE_TYPE);
|
||||
m.load(v, Type.DOUBLE_TYPE);
|
||||
m.dup2();
|
||||
m.dup2();
|
||||
m.mul(Type.DOUBLE_TYPE);
|
||||
m.mul(Type.DOUBLE_TYPE);
|
||||
m.dconst(24.0);
|
||||
m.div(Type.DOUBLE_TYPE);
|
||||
m.sub(Type.DOUBLE_TYPE);
|
||||
m.areturn(Type.DOUBLE_TYPE);
|
||||
}
|
||||
|
||||
public void doBytecodeGenMulti(BytecodeGen.Context context, InstructionAdapter m, BytecodeGen.Context.LocalVarConsumer localVarConsumer) {
|
||||
super.doBytecodeGenMulti(context, m, localVarConsumer);
|
||||
context.doCountedLoop(m, localVarConsumer, (idx) -> {
|
||||
int v = localVarConsumer.createLocalVariable("v", Type.DOUBLE_TYPE.getDescriptor());
|
||||
m.load(1, InstructionAdapter.OBJECT_TYPE);
|
||||
m.load(idx, Type.INT_TYPE);
|
||||
m.dup2();
|
||||
m.aload(Type.DOUBLE_TYPE);
|
||||
m.dconst(1.0);
|
||||
m.invokestatic(Type.getInternalName(Math.class), "min", Type.getMethodDescriptor(Type.DOUBLE_TYPE, new Type[]{Type.DOUBLE_TYPE, Type.DOUBLE_TYPE}), false);
|
||||
m.dconst(-1.0);
|
||||
m.invokestatic(Type.getInternalName(Math.class), "max", Type.getMethodDescriptor(Type.DOUBLE_TYPE, new Type[]{Type.DOUBLE_TYPE, Type.DOUBLE_TYPE}), false);
|
||||
m.store(v, Type.DOUBLE_TYPE);
|
||||
m.load(v, Type.DOUBLE_TYPE);
|
||||
m.dconst(2.0);
|
||||
m.div(Type.DOUBLE_TYPE);
|
||||
m.load(v, Type.DOUBLE_TYPE);
|
||||
m.dup2();
|
||||
m.dup2();
|
||||
m.mul(Type.DOUBLE_TYPE);
|
||||
m.mul(Type.DOUBLE_TYPE);
|
||||
m.dconst(24.0);
|
||||
m.div(Type.DOUBLE_TYPE);
|
||||
m.sub(Type.DOUBLE_TYPE);
|
||||
m.astore(Type.DOUBLE_TYPE);
|
||||
});
|
||||
m.areturn(Type.VOID_TYPE);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package org.bxteam.divinemc.dfc.common.ducks;
|
||||
|
||||
import org.bxteam.divinemc.dfc.common.util.ArrayCache;
|
||||
|
||||
public interface IArrayCacheCapable {
|
||||
ArrayCache c2me$getArrayCache();
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
package org.bxteam.divinemc.dfc.common.ducks;
|
||||
|
||||
public interface IBlendingAwareVisitor {
|
||||
boolean c2me$isBlendingEnabled();
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
package org.bxteam.divinemc.dfc.common.ducks;
|
||||
|
||||
public interface ICoordinatesFilling {
|
||||
void c2me$fillCoordinates(int[] var1, int[] var2, int[] var3);
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package org.bxteam.divinemc.dfc.common.ducks;
|
||||
|
||||
public interface IEqualityOverriding {
|
||||
void c2me$overrideEquality(Object var1);
|
||||
|
||||
Object c2me$getOverriddenEquality();
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package org.bxteam.divinemc.dfc.common.ducks;
|
||||
|
||||
import org.bxteam.divinemc.dfc.common.ast.EvalType;
|
||||
import net.minecraft.world.level.levelgen.DensityFunction;
|
||||
|
||||
public interface IFastCacheLike extends DensityFunction {
|
||||
long CACHE_MISS_NAN_BITS = 9222769054270909007L;
|
||||
|
||||
double c2me$getCached(int var1, int var2, int var3, EvalType var4);
|
||||
|
||||
boolean c2me$getCached(double[] var1, int[] var2, int[] var3, int[] var4, EvalType var5);
|
||||
|
||||
void c2me$cache(int var1, int var2, int var3, EvalType var4, double var5);
|
||||
|
||||
void c2me$cache(double[] var1, int[] var2, int[] var3, int[] var4, EvalType var5);
|
||||
|
||||
DensityFunction c2me$getDelegate();
|
||||
|
||||
DensityFunction c2me$withDelegate(DensityFunction var1);
|
||||
}
|
||||
@@ -0,0 +1,499 @@
|
||||
package org.bxteam.divinemc.dfc.common.gen;
|
||||
|
||||
import com.google.common.io.Files;
|
||||
import org.bxteam.divinemc.dfc.common.ast.AstNode;
|
||||
import org.bxteam.divinemc.dfc.common.ast.EvalType;
|
||||
import org.bxteam.divinemc.dfc.common.ast.McToAst;
|
||||
import org.bxteam.divinemc.dfc.common.ast.dfvisitor.StripBlending;
|
||||
import org.bxteam.divinemc.dfc.common.ast.misc.ConstantNode;
|
||||
import org.bxteam.divinemc.dfc.common.ast.misc.RootNode;
|
||||
import org.bxteam.divinemc.dfc.common.util.ArrayCache;
|
||||
import org.bxteam.divinemc.dfc.common.vif.AstVanillaInterface;
|
||||
import it.unimi.dsi.fastutil.Hash;
|
||||
import it.unimi.dsi.fastutil.Pair;
|
||||
import it.unimi.dsi.fastutil.ints.IntObjectPair;
|
||||
import it.unimi.dsi.fastutil.objects.Object2ReferenceMap;
|
||||
import it.unimi.dsi.fastutil.objects.Object2ReferenceMaps;
|
||||
import it.unimi.dsi.fastutil.objects.Object2ReferenceOpenCustomHashMap;
|
||||
import it.unimi.dsi.fastutil.objects.Object2ReferenceOpenHashMap;
|
||||
import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet;
|
||||
import it.unimi.dsi.fastutil.objects.Reference2ObjectOpenHashMap;
|
||||
import it.unimi.dsi.fastutil.objects.Reference2ReferenceMap;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Comparator;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.ListIterator;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.IntConsumer;
|
||||
import java.util.stream.Collectors;
|
||||
import net.minecraft.util.CubicSpline;
|
||||
import net.minecraft.world.level.levelgen.DensityFunction;
|
||||
import net.minecraft.world.level.levelgen.DensityFunctions;
|
||||
import org.objectweb.asm.ClassWriter;
|
||||
import org.objectweb.asm.Label;
|
||||
import org.objectweb.asm.Type;
|
||||
import org.objectweb.asm.commons.AnalyzerAdapter;
|
||||
import org.objectweb.asm.commons.InstructionAdapter;
|
||||
|
||||
public class BytecodeGen {
|
||||
private static final File exportDir = new File("./cache/c2me-dfc");
|
||||
private static final AtomicLong ordinal = new AtomicLong();
|
||||
public static final Hash.Strategy<AstNode> RELAXED_STRATEGY;
|
||||
private static final Object2ReferenceMap<AstNode, Class<?>> compilationCache;
|
||||
|
||||
public BytecodeGen() {
|
||||
}
|
||||
|
||||
public static DensityFunction compile(DensityFunction densityFunction, Reference2ReferenceMap<DensityFunction, DensityFunction> tempCache) {
|
||||
DensityFunction cached = (DensityFunction)tempCache.get(densityFunction);
|
||||
if (cached != null) {
|
||||
return cached;
|
||||
} else if (densityFunction instanceof AstVanillaInterface) {
|
||||
AstVanillaInterface vif = (AstVanillaInterface)densityFunction;
|
||||
AstNode ast = vif.getAstNode();
|
||||
return new CompiledDensityFunction(compile0(ast), vif.getBlendingFallback());
|
||||
} else {
|
||||
AstNode ast = McToAst.toAst(densityFunction.mapAll(StripBlending.INSTANCE));
|
||||
if (ast instanceof ConstantNode) {
|
||||
ConstantNode constantNode = (ConstantNode)ast;
|
||||
return DensityFunctions.constant(constantNode.getValue());
|
||||
} else {
|
||||
CompiledDensityFunction compiled = new CompiledDensityFunction(compile0(ast), densityFunction);
|
||||
tempCache.put(densityFunction, compiled);
|
||||
return compiled;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static synchronized CompiledEntry compile0(AstNode node) {
|
||||
Class<?> cached = (Class)compilationCache.get(node);
|
||||
ClassWriter writer = new ClassWriter(3);
|
||||
String name = cached != null ? String.format("DfcCompiled_discarded") : String.format("DfcCompiled_%d", ordinal.getAndIncrement());
|
||||
writer.visit(65, 17, name, (String)null, Type.getInternalName(Object.class), new String[]{Type.getInternalName(CompiledEntry.class)});
|
||||
RootNode rootNode = new RootNode(node);
|
||||
Context genContext = new Context(writer, name);
|
||||
genContext.newSingleMethod0((adapter, localVarConsumer) -> {
|
||||
rootNode.doBytecodeGenSingle(genContext, adapter, localVarConsumer);
|
||||
}, "evalSingle", true);
|
||||
genContext.newMultiMethod0((adapter, localVarConsumer) -> {
|
||||
rootNode.doBytecodeGenMulti(genContext, adapter, localVarConsumer);
|
||||
}, "evalMulti", true);
|
||||
List<Object> args = (List)genContext.args.entrySet().stream().sorted(Comparator.comparingInt((o) -> {
|
||||
return ((Context.FieldRecord)o.getValue()).ordinal();
|
||||
})).map(Map.Entry::getKey).collect(Collectors.toCollection(ArrayList::new));
|
||||
if (cached != null) {
|
||||
try {
|
||||
return (CompiledEntry)cached.getConstructor(List.class).newInstance(args);
|
||||
} catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException | InstantiationException var11) {
|
||||
ReflectiveOperationException e = var11;
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
} else {
|
||||
genConstructor(genContext);
|
||||
genGetArgs(genContext);
|
||||
genNewInstance(genContext);
|
||||
|
||||
Object var8;
|
||||
for(ListIterator<Object> iterator = args.listIterator(); iterator.hasNext(); var8 = iterator.next()) {
|
||||
}
|
||||
|
||||
byte[] bytes = writer.toByteArray();
|
||||
dumpClass(genContext.className, bytes);
|
||||
Class<?> defined = defineClass(genContext.className, bytes);
|
||||
compilationCache.put(node, defined);
|
||||
|
||||
try {
|
||||
return (CompiledEntry)defined.getConstructor(List.class).newInstance(args);
|
||||
} catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException | InstantiationException var12) {
|
||||
ReflectiveOperationException e = var12;
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void genConstructor(Context context) {
|
||||
InstructionAdapter m = new InstructionAdapter(new AnalyzerAdapter(context.className, 1, "<init>", Type.getMethodDescriptor(Type.VOID_TYPE, new Type[]{Type.getType(List.class)}), context.classWriter.visitMethod(1, "<init>", Type.getMethodDescriptor(Type.VOID_TYPE, new Type[]{Type.getType(List.class)}), (String)null, (String[])null)));
|
||||
Label start = new Label();
|
||||
Label end = new Label();
|
||||
m.visitLabel(start);
|
||||
m.load(0, InstructionAdapter.OBJECT_TYPE);
|
||||
m.invokespecial(Type.getInternalName(Object.class), "<init>", Type.getMethodDescriptor(Type.VOID_TYPE, new Type[0]), false);
|
||||
Iterator var4 = context.args.entrySet().stream().sorted(Comparator.comparingInt((o) -> {
|
||||
return ((Context.FieldRecord)o.getValue()).ordinal();
|
||||
})).toList().iterator();
|
||||
|
||||
while(var4.hasNext()) {
|
||||
Map.Entry<Object, Context.FieldRecord> entry = (Map.Entry)var4.next();
|
||||
String name = ((Context.FieldRecord)entry.getValue()).name();
|
||||
Class<?> type = ((Context.FieldRecord)entry.getValue()).type();
|
||||
int ordinal = ((Context.FieldRecord)entry.getValue()).ordinal();
|
||||
m.load(0, InstructionAdapter.OBJECT_TYPE);
|
||||
m.load(1, InstructionAdapter.OBJECT_TYPE);
|
||||
m.iconst(ordinal);
|
||||
m.invokeinterface(Type.getInternalName(List.class), "get", Type.getMethodDescriptor(InstructionAdapter.OBJECT_TYPE, new Type[]{Type.INT_TYPE}));
|
||||
m.checkcast(Type.getType(type));
|
||||
m.putfield(context.className, name, Type.getDescriptor(type));
|
||||
}
|
||||
|
||||
var4 = context.postProcessMethods.stream().sorted().toList().iterator();
|
||||
|
||||
while(var4.hasNext()) {
|
||||
String postProcessingMethod = (String)var4.next();
|
||||
m.load(0, InstructionAdapter.OBJECT_TYPE);
|
||||
m.invokevirtual(context.className, postProcessingMethod, "()V", false);
|
||||
}
|
||||
|
||||
m.areturn(Type.VOID_TYPE);
|
||||
m.visitLabel(end);
|
||||
m.visitLocalVariable("this", context.classDesc, (String)null, start, end, 0);
|
||||
m.visitLocalVariable("list", Type.getDescriptor(List.class), (String)null, start, end, 1);
|
||||
m.visitMaxs(0, 0);
|
||||
}
|
||||
|
||||
private static void genGetArgs(Context context) {
|
||||
InstructionAdapter m = new InstructionAdapter(new AnalyzerAdapter(context.className, 17, "getArgs", Type.getMethodDescriptor(Type.getType(List.class), new Type[0]), context.classWriter.visitMethod(17, "getArgs", Type.getMethodDescriptor(Type.getType(List.class), new Type[0]), (String)null, (String[])null)));
|
||||
Label start = new Label();
|
||||
Label end = new Label();
|
||||
m.visitLabel(start);
|
||||
m.anew(Type.getType(ArrayList.class));
|
||||
m.dup();
|
||||
m.iconst(context.args.size());
|
||||
m.invokespecial(Type.getInternalName(ArrayList.class), "<init>", Type.getMethodDescriptor(Type.VOID_TYPE, new Type[]{Type.INT_TYPE}), false);
|
||||
m.store(1, InstructionAdapter.OBJECT_TYPE);
|
||||
Iterator var4 = context.args.entrySet().stream().sorted(Comparator.comparingInt((o) -> {
|
||||
return ((Context.FieldRecord)o.getValue()).ordinal();
|
||||
})).toList().iterator();
|
||||
|
||||
while(var4.hasNext()) {
|
||||
Map.Entry<Object, Context.FieldRecord> entry = (Map.Entry)var4.next();
|
||||
String name = ((Context.FieldRecord)entry.getValue()).name();
|
||||
Class<?> type = ((Context.FieldRecord)entry.getValue()).type();
|
||||
m.load(1, InstructionAdapter.OBJECT_TYPE);
|
||||
m.load(0, InstructionAdapter.OBJECT_TYPE);
|
||||
m.getfield(context.className, name, Type.getDescriptor(type));
|
||||
m.invokeinterface(Type.getInternalName(List.class), "add", Type.getMethodDescriptor(Type.BOOLEAN_TYPE, new Type[]{InstructionAdapter.OBJECT_TYPE}));
|
||||
m.pop();
|
||||
}
|
||||
|
||||
m.load(1, InstructionAdapter.OBJECT_TYPE);
|
||||
m.areturn(InstructionAdapter.OBJECT_TYPE);
|
||||
m.visitLabel(end);
|
||||
m.visitLocalVariable("this", context.classDesc, (String)null, start, end, 0);
|
||||
m.visitLocalVariable("list", Type.getDescriptor(List.class), (String)null, start, end, 1);
|
||||
m.visitMaxs(0, 0);
|
||||
}
|
||||
|
||||
private static void genNewInstance(Context context) {
|
||||
InstructionAdapter m = new InstructionAdapter(new AnalyzerAdapter(context.className, 17, "newInstance", Type.getMethodDescriptor(Type.getType(CompiledEntry.class), new Type[]{Type.getType(List.class)}), context.classWriter.visitMethod(17, "newInstance", Type.getMethodDescriptor(Type.getType(CompiledEntry.class), new Type[]{Type.getType(List.class)}), (String)null, (String[])null)));
|
||||
Label start = new Label();
|
||||
Label end = new Label();
|
||||
m.visitLabel(start);
|
||||
m.anew(Type.getType(context.classDesc));
|
||||
m.dup();
|
||||
m.load(1, InstructionAdapter.OBJECT_TYPE);
|
||||
m.invokespecial(context.className, "<init>", Type.getMethodDescriptor(Type.VOID_TYPE, new Type[]{Type.getType(List.class)}), false);
|
||||
m.areturn(InstructionAdapter.OBJECT_TYPE);
|
||||
m.visitLabel(end);
|
||||
m.visitLocalVariable("this", context.classDesc, (String)null, start, end, 0);
|
||||
m.visitLocalVariable("list", Type.getDescriptor(List.class), (String)null, start, end, 1);
|
||||
m.visitMaxs(0, 0);
|
||||
}
|
||||
|
||||
private static void dumpClass(String className, byte[] bytes) {
|
||||
File outputFile = new File(exportDir, className + ".class");
|
||||
outputFile.getParentFile().mkdirs();
|
||||
|
||||
try {
|
||||
Files.write(bytes, outputFile);
|
||||
} catch (IOException var4) {
|
||||
IOException e = var4;
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static Class<?> defineClass(final String className, final byte[] bytes) {
|
||||
ClassLoader classLoader = new ClassLoader(BytecodeGen.class.getClassLoader()) {
|
||||
public Class<?> loadClass(String name) throws ClassNotFoundException {
|
||||
return name.equals(className) ? super.defineClass(name, bytes, 0, bytes.length) : super.loadClass(name);
|
||||
}
|
||||
};
|
||||
|
||||
try {
|
||||
return classLoader.loadClass(className);
|
||||
} catch (ClassNotFoundException var4) {
|
||||
ClassNotFoundException e = var4;
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
static {
|
||||
try {
|
||||
org.bxteam.divinemc.util.Files.deleteRecursively(exportDir);
|
||||
} catch (IOException var1) {
|
||||
IOException e = var1;
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
RELAXED_STRATEGY = new Hash.Strategy<AstNode>() {
|
||||
public int hashCode(AstNode o) {
|
||||
return o.relaxedHashCode();
|
||||
}
|
||||
|
||||
public boolean equals(AstNode a, AstNode b) {
|
||||
return a.relaxedEquals(b);
|
||||
}
|
||||
};
|
||||
compilationCache = Object2ReferenceMaps.synchronize(new Object2ReferenceOpenCustomHashMap<>(RELAXED_STRATEGY));
|
||||
}
|
||||
|
||||
public static class Context {
|
||||
public static final String SINGLE_DESC;
|
||||
public static final String MULTI_DESC;
|
||||
public final ClassWriter classWriter;
|
||||
public final String className;
|
||||
public final String classDesc;
|
||||
private int methodIdx = 0;
|
||||
private final Object2ReferenceOpenHashMap<AstNode, String> singleMethods = new Object2ReferenceOpenHashMap<>();
|
||||
private final Object2ReferenceOpenHashMap<AstNode, String> multiMethods = new Object2ReferenceOpenHashMap<>();
|
||||
private final Object2ReferenceOpenHashMap<CubicSpline<DensityFunctions.Spline.Point, DensityFunctions.Spline.Coordinate>, String> splineMethods = new Object2ReferenceOpenHashMap<>();
|
||||
private final ObjectOpenHashSet<String> postProcessMethods = new ObjectOpenHashSet<>();
|
||||
private final Reference2ObjectOpenHashMap<Object, FieldRecord> args = new Reference2ObjectOpenHashMap<>();
|
||||
|
||||
public Context(ClassWriter classWriter, String className) {
|
||||
this.classWriter = (ClassWriter)Objects.requireNonNull(classWriter);
|
||||
this.className = (String)Objects.requireNonNull(className);
|
||||
this.classDesc = String.format("L%s;", this.className);
|
||||
}
|
||||
|
||||
public String nextMethodName() {
|
||||
return String.format("method_%d", this.methodIdx++);
|
||||
}
|
||||
|
||||
public String nextMethodName(String suffix) {
|
||||
return String.format("method_%d_%s", this.methodIdx++, suffix);
|
||||
}
|
||||
|
||||
public String newSingleMethod(AstNode node) {
|
||||
return this.singleMethods.computeIfAbsent(node, (AstNode node1) -> this.newSingleMethod((adapter, localVarConsumer) -> node1.doBytecodeGenSingle(this, adapter, localVarConsumer), nextMethodName(node.getClass().getSimpleName())));
|
||||
}
|
||||
|
||||
public String newSingleMethod(BiConsumer<InstructionAdapter, LocalVarConsumer> generator) {
|
||||
return this.newSingleMethod(generator, this.nextMethodName());
|
||||
}
|
||||
|
||||
public String newSingleMethod(BiConsumer<InstructionAdapter, LocalVarConsumer> generator, String name) {
|
||||
this.newSingleMethod0(generator, name, false);
|
||||
return name;
|
||||
}
|
||||
|
||||
private void newSingleMethod0(BiConsumer<InstructionAdapter, LocalVarConsumer> generator, String name, boolean isPublic) {
|
||||
InstructionAdapter adapter = new InstructionAdapter(new AnalyzerAdapter(this.className, (isPublic ? 1 : 2) | 16, name, SINGLE_DESC, this.classWriter.visitMethod((isPublic ? 1 : 2) | 16, name, SINGLE_DESC, (String)null, (String[])null)));
|
||||
List<IntObjectPair<Pair<String, String>>> extraLocals = new ArrayList<>();
|
||||
Label start = new Label();
|
||||
Label end = new Label();
|
||||
adapter.visitLabel(start);
|
||||
generator.accept(adapter, (localName, localDesc) -> {
|
||||
int ordinal = extraLocals.size() + 5;
|
||||
extraLocals.add(IntObjectPair.of(ordinal, Pair.of(localName, localDesc)));
|
||||
return ordinal;
|
||||
});
|
||||
adapter.visitLabel(end);
|
||||
adapter.visitLocalVariable("this", this.classDesc, (String)null, start, end, 0);
|
||||
adapter.visitLocalVariable("x", Type.INT_TYPE.getDescriptor(), (String)null, start, end, 1);
|
||||
adapter.visitLocalVariable("y", Type.INT_TYPE.getDescriptor(), (String)null, start, end, 2);
|
||||
adapter.visitLocalVariable("z", Type.INT_TYPE.getDescriptor(), (String)null, start, end, 3);
|
||||
adapter.visitLocalVariable("evalType", Type.getType(EvalType.class).getDescriptor(), (String)null, start, end, 4);
|
||||
Iterator var8 = extraLocals.iterator();
|
||||
|
||||
while(var8.hasNext()) {
|
||||
IntObjectPair<Pair<String, String>> local = (IntObjectPair)var8.next();
|
||||
adapter.visitLocalVariable((String)((Pair)local.right()).left(), (String)((Pair)local.right()).right(), (String)null, start, end, local.leftInt());
|
||||
}
|
||||
|
||||
adapter.visitMaxs(0, 0);
|
||||
}
|
||||
|
||||
public String newMultiMethod(AstNode node) {
|
||||
return this.multiMethods.computeIfAbsent(node, (AstNode node1) -> this.newMultiMethod((adapter, localVarConsumer) -> node1.doBytecodeGenMulti(this, adapter, localVarConsumer), nextMethodName(node.getClass().getSimpleName())));
|
||||
}
|
||||
|
||||
public String newMultiMethod(BiConsumer<InstructionAdapter, LocalVarConsumer> generator) {
|
||||
return this.newMultiMethod(generator, this.nextMethodName());
|
||||
}
|
||||
|
||||
public String newMultiMethod(BiConsumer<InstructionAdapter, LocalVarConsumer> generator, String name) {
|
||||
this.newMultiMethod0(generator, name, false);
|
||||
return name;
|
||||
}
|
||||
|
||||
private void newMultiMethod0(BiConsumer<InstructionAdapter, LocalVarConsumer> generator, String name, boolean isPublic) {
|
||||
InstructionAdapter adapter = new InstructionAdapter(new AnalyzerAdapter(this.className, (isPublic ? 1 : 2) | 16, name, MULTI_DESC, this.classWriter.visitMethod((isPublic ? 1 : 2) | 16, name, MULTI_DESC, (String)null, (String[])null)));
|
||||
List<IntObjectPair<Pair<String, String>>> extraLocals = new ArrayList();
|
||||
Label start = new Label();
|
||||
Label end = new Label();
|
||||
adapter.visitLabel(start);
|
||||
generator.accept(adapter, (localName, localDesc) -> {
|
||||
int ordinal = extraLocals.size() + 7;
|
||||
extraLocals.add(IntObjectPair.of(ordinal, Pair.of(localName, localDesc)));
|
||||
return ordinal;
|
||||
});
|
||||
adapter.visitLabel(end);
|
||||
adapter.visitLocalVariable("this", this.classDesc, (String)null, start, end, 0);
|
||||
adapter.visitLocalVariable("res", Type.getType(double[].class).getDescriptor(), (String)null, start, end, 1);
|
||||
adapter.visitLocalVariable("x", Type.getType(double[].class).getDescriptor(), (String)null, start, end, 2);
|
||||
adapter.visitLocalVariable("y", Type.getType(double[].class).getDescriptor(), (String)null, start, end, 3);
|
||||
adapter.visitLocalVariable("z", Type.getType(double[].class).getDescriptor(), (String)null, start, end, 4);
|
||||
adapter.visitLocalVariable("evalType", Type.getType(EvalType.class).getDescriptor(), (String)null, start, end, 5);
|
||||
adapter.visitLocalVariable("arrayCache", Type.getType(ArrayCache.class).getDescriptor(), (String)null, start, end, 6);
|
||||
Iterator var8 = extraLocals.iterator();
|
||||
|
||||
while(var8.hasNext()) {
|
||||
IntObjectPair<Pair<String, String>> local = (IntObjectPair)var8.next();
|
||||
adapter.visitLocalVariable((String)((Pair)local.right()).left(), (String)((Pair)local.right()).right(), (String)null, start, end, local.leftInt());
|
||||
}
|
||||
|
||||
adapter.visitMaxs(0, 0);
|
||||
}
|
||||
|
||||
public String getCachedSplineMethod(CubicSpline<DensityFunctions.Spline.Point, DensityFunctions.Spline.Coordinate> spline) {
|
||||
return (String)this.splineMethods.get(spline);
|
||||
}
|
||||
|
||||
public void cacheSplineMethod(CubicSpline<DensityFunctions.Spline.Point, DensityFunctions.Spline.Coordinate> spline, String method) {
|
||||
this.splineMethods.put(spline, method);
|
||||
}
|
||||
|
||||
public void callDelegateSingle(InstructionAdapter m, String target) {
|
||||
m.load(0, InstructionAdapter.OBJECT_TYPE);
|
||||
m.load(1, Type.INT_TYPE);
|
||||
m.load(2, Type.INT_TYPE);
|
||||
m.load(3, Type.INT_TYPE);
|
||||
m.load(4, InstructionAdapter.OBJECT_TYPE);
|
||||
m.invokevirtual(this.className, target, SINGLE_DESC, false);
|
||||
}
|
||||
|
||||
public void callDelegateMulti(InstructionAdapter m, String target) {
|
||||
m.load(0, InstructionAdapter.OBJECT_TYPE);
|
||||
m.load(1, InstructionAdapter.OBJECT_TYPE);
|
||||
m.load(2, InstructionAdapter.OBJECT_TYPE);
|
||||
m.load(3, InstructionAdapter.OBJECT_TYPE);
|
||||
m.load(4, InstructionAdapter.OBJECT_TYPE);
|
||||
m.load(5, InstructionAdapter.OBJECT_TYPE);
|
||||
m.load(6, InstructionAdapter.OBJECT_TYPE);
|
||||
m.invokevirtual(this.className, target, MULTI_DESC, false);
|
||||
}
|
||||
|
||||
public <T> String newField(Class<T> type, T data) {
|
||||
FieldRecord existing = (FieldRecord)this.args.get(data);
|
||||
if (existing != null) {
|
||||
return existing.name();
|
||||
} else {
|
||||
int size = this.args.size();
|
||||
String name = String.format("field_%d", size);
|
||||
this.classWriter.visitField(2, name, Type.getDescriptor(type), (String)null, (Object)null);
|
||||
this.args.put(data, new FieldRecord(name, size, type));
|
||||
return name;
|
||||
}
|
||||
}
|
||||
|
||||
public void doCountedLoop(InstructionAdapter m, LocalVarConsumer localVarConsumer, IntConsumer bodyGenerator) {
|
||||
int loopIdx = localVarConsumer.createLocalVariable("loopIdx", Type.INT_TYPE.getDescriptor());
|
||||
m.iconst(0);
|
||||
m.store(loopIdx, Type.INT_TYPE);
|
||||
Label start = new Label();
|
||||
Label end = new Label();
|
||||
m.visitLabel(start);
|
||||
m.load(loopIdx, Type.INT_TYPE);
|
||||
m.load(1, InstructionAdapter.OBJECT_TYPE);
|
||||
m.arraylength();
|
||||
m.ificmpge(end);
|
||||
bodyGenerator.accept(loopIdx);
|
||||
m.iinc(loopIdx, 1);
|
||||
m.goTo(start);
|
||||
m.visitLabel(end);
|
||||
}
|
||||
|
||||
public void delegateToSingle(InstructionAdapter m, LocalVarConsumer localVarConsumer, AstNode current) {
|
||||
String singleMethod = this.newSingleMethod(current);
|
||||
this.doCountedLoop(m, localVarConsumer, (idx) -> {
|
||||
m.load(1, InstructionAdapter.OBJECT_TYPE);
|
||||
m.load(idx, Type.INT_TYPE);
|
||||
m.load(0, InstructionAdapter.OBJECT_TYPE);
|
||||
m.load(2, InstructionAdapter.OBJECT_TYPE);
|
||||
m.load(idx, Type.INT_TYPE);
|
||||
m.aload(Type.INT_TYPE);
|
||||
m.load(3, InstructionAdapter.OBJECT_TYPE);
|
||||
m.load(idx, Type.INT_TYPE);
|
||||
m.aload(Type.INT_TYPE);
|
||||
m.load(4, InstructionAdapter.OBJECT_TYPE);
|
||||
m.load(idx, Type.INT_TYPE);
|
||||
m.aload(Type.INT_TYPE);
|
||||
m.load(5, InstructionAdapter.OBJECT_TYPE);
|
||||
m.invokevirtual(this.className, singleMethod, SINGLE_DESC, false);
|
||||
m.astore(Type.DOUBLE_TYPE);
|
||||
});
|
||||
}
|
||||
|
||||
public void genPostprocessingMethod(String name, Consumer<InstructionAdapter> generator) {
|
||||
if (!this.postProcessMethods.contains(name)) {
|
||||
InstructionAdapter adapter = new InstructionAdapter(new AnalyzerAdapter(this.className, 18, name, "()V", this.classWriter.visitMethod(18, name, "()V", (String)null, (String[])null)));
|
||||
Label start = new Label();
|
||||
Label end = new Label();
|
||||
adapter.visitLabel(start);
|
||||
generator.accept(adapter);
|
||||
adapter.visitLabel(end);
|
||||
adapter.visitMaxs(0, 0);
|
||||
adapter.visitLocalVariable("this", this.classDesc, (String)null, start, end, 0);
|
||||
this.postProcessMethods.add(name);
|
||||
}
|
||||
}
|
||||
|
||||
static {
|
||||
SINGLE_DESC = Type.getMethodDescriptor(Type.getType(Double.TYPE), new Type[]{Type.getType(Integer.TYPE), Type.getType(Integer.TYPE), Type.getType(Integer.TYPE), Type.getType(EvalType.class)});
|
||||
MULTI_DESC = Type.getMethodDescriptor(Type.VOID_TYPE, new Type[]{Type.getType(double[].class), Type.getType(int[].class), Type.getType(int[].class), Type.getType(int[].class), Type.getType(EvalType.class), Type.getType(ArrayCache.class)});
|
||||
}
|
||||
|
||||
public interface LocalVarConsumer {
|
||||
int createLocalVariable(String var1, String var2);
|
||||
}
|
||||
|
||||
private static record FieldRecord(String name, int ordinal, Class<?> type) {
|
||||
private FieldRecord(String name, int ordinal, Class<?> type) {
|
||||
this.name = name;
|
||||
this.ordinal = ordinal;
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public String name() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
public int ordinal() {
|
||||
return this.ordinal;
|
||||
}
|
||||
|
||||
public Class<?> type() {
|
||||
return this.type;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
public interface EvalMultiInterface {
|
||||
void evalMulti(double[] var1, int[] var2, int[] var3, int[] var4, EvalType var5);
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
public interface EvalSingleInterface {
|
||||
double evalSingle(int var1, int var2, int var3, EvalType var4);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
package org.bxteam.divinemc.dfc.common.gen;
|
||||
|
||||
import com.google.common.base.Suppliers;
|
||||
import org.bxteam.divinemc.dfc.common.ducks.IBlendingAwareVisitor;
|
||||
import org.bxteam.divinemc.dfc.common.ducks.IFastCacheLike;
|
||||
import java.util.List;
|
||||
import java.util.ListIterator;
|
||||
import java.util.Objects;
|
||||
import java.util.function.Supplier;
|
||||
import net.minecraft.world.level.levelgen.DensityFunction;
|
||||
|
||||
public class CompiledDensityFunction extends SubCompiledDensityFunction {
|
||||
private final CompiledEntry compiledEntry;
|
||||
|
||||
public CompiledDensityFunction(CompiledEntry compiledEntry, DensityFunction blendingFallback) {
|
||||
super(compiledEntry, compiledEntry, blendingFallback);
|
||||
this.compiledEntry = (CompiledEntry)Objects.requireNonNull(compiledEntry);
|
||||
}
|
||||
|
||||
private CompiledDensityFunction(CompiledEntry compiledEntry, Supplier<DensityFunction> blendingFallback) {
|
||||
super(compiledEntry, compiledEntry, blendingFallback);
|
||||
this.compiledEntry = (CompiledEntry)Objects.requireNonNull(compiledEntry);
|
||||
}
|
||||
|
||||
public DensityFunction mapAll(Visitor visitor) {
|
||||
if (visitor instanceof IBlendingAwareVisitor blendingAwareVisitor) {
|
||||
if (blendingAwareVisitor.c2me$isBlendingEnabled()) {
|
||||
DensityFunction fallback1 = this.getFallback();
|
||||
if (fallback1 == null) {
|
||||
throw new IllegalStateException("blendingFallback is no more");
|
||||
}
|
||||
|
||||
return fallback1.mapAll(visitor);
|
||||
}
|
||||
}
|
||||
|
||||
boolean modified = false;
|
||||
List<Object> args = this.compiledEntry.getArgs();
|
||||
ListIterator<Object> iterator = args.listIterator();
|
||||
|
||||
Object next;
|
||||
while(iterator.hasNext()) {
|
||||
next = iterator.next();
|
||||
if (next instanceof DensityFunction df) {
|
||||
if (!(df instanceof IFastCacheLike)) {
|
||||
DensityFunction applied = df.mapAll(visitor);
|
||||
if (df != applied) {
|
||||
iterator.set(applied);
|
||||
modified = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (next instanceof NoiseHolder noise) {
|
||||
NoiseHolder applied = visitor.visitNoise(noise);
|
||||
if (noise != applied) {
|
||||
iterator.set(applied);
|
||||
modified = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
iterator = args.listIterator();
|
||||
|
||||
while(iterator.hasNext()) {
|
||||
next = iterator.next();
|
||||
if (next instanceof IFastCacheLike cacheLike) {
|
||||
DensityFunction applied = visitor.apply(cacheLike);
|
||||
if (applied == cacheLike.c2me$getDelegate()) {
|
||||
iterator.set((Object)null);
|
||||
modified = true;
|
||||
} else {
|
||||
if (!(applied instanceof IFastCacheLike)) {
|
||||
throw new UnsupportedOperationException("Unsupported transformation on Wrapping node");
|
||||
}
|
||||
|
||||
IFastCacheLike newCacheLike = (IFastCacheLike)applied;
|
||||
iterator.set(newCacheLike);
|
||||
modified = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Supplier<DensityFunction> fallback = this.blendingFallback != null ? Suppliers.memoize(() -> {
|
||||
DensityFunction densityFunction = (DensityFunction)this.blendingFallback.get();
|
||||
return densityFunction != null ? densityFunction.mapAll(visitor) : null;
|
||||
}) : null;
|
||||
if (fallback != this.blendingFallback) {
|
||||
modified = true;
|
||||
}
|
||||
|
||||
if (modified) {
|
||||
return new CompiledDensityFunction(this.compiledEntry.newInstance(args), fallback);
|
||||
} else {
|
||||
return this;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package org.bxteam.divinemc.dfc.common.gen;
|
||||
|
||||
import org.bxteam.divinemc.dfc.common.ast.EvalType;
|
||||
import org.bxteam.divinemc.dfc.common.util.ArrayCache;
|
||||
import java.util.List;
|
||||
|
||||
public interface CompiledEntry extends ISingleMethod, IMultiMethod {
|
||||
double evalSingle(int var1, int var2, int var3, EvalType var4);
|
||||
|
||||
void evalMulti(double[] var1, int[] var2, int[] var3, int[] var4, EvalType var5, ArrayCache var6);
|
||||
|
||||
CompiledEntry newInstance(List<?> var1);
|
||||
|
||||
List<Object> getArgs();
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package org.bxteam.divinemc.dfc.common.gen;
|
||||
|
||||
import org.bxteam.divinemc.dfc.common.ducks.IBlendingAwareVisitor;
|
||||
import java.util.Objects;
|
||||
import net.minecraft.world.level.levelgen.DensityFunction;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class DelegatingBlendingAwareVisitor implements IBlendingAwareVisitor, DensityFunction.Visitor {
|
||||
private final DensityFunction.Visitor delegate;
|
||||
private final boolean blendingEnabled;
|
||||
|
||||
public DelegatingBlendingAwareVisitor(DensityFunction.Visitor delegate, boolean blendingEnabled) {
|
||||
this.delegate = Objects.requireNonNull(delegate);
|
||||
this.blendingEnabled = blendingEnabled;
|
||||
}
|
||||
|
||||
public @NotNull DensityFunction apply(@NotNull DensityFunction densityFunction) {
|
||||
return this.delegate.apply(densityFunction);
|
||||
}
|
||||
|
||||
public DensityFunction.@NotNull NoiseHolder visitNoise(DensityFunction.@NotNull NoiseHolder noiseDensityFunction) {
|
||||
return this.delegate.visitNoise(noiseDensityFunction);
|
||||
}
|
||||
|
||||
public boolean c2me$isBlendingEnabled() {
|
||||
return this.blendingEnabled;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package org.bxteam.divinemc.dfc.common.gen;
|
||||
|
||||
import org.bxteam.divinemc.dfc.common.ast.EvalType;
|
||||
import org.bxteam.divinemc.dfc.common.util.ArrayCache;
|
||||
|
||||
@FunctionalInterface
|
||||
public interface IMultiMethod {
|
||||
void evalMulti(double[] var1, int[] var2, int[] var3, int[] var4, EvalType var5, ArrayCache var6);
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
package org.bxteam.divinemc.dfc.common.gen;
|
||||
|
||||
import org.bxteam.divinemc.dfc.common.ast.EvalType;
|
||||
|
||||
@FunctionalInterface
|
||||
public interface ISingleMethod {
|
||||
double evalSingle(int var1, int var2, int var3, EvalType var4);
|
||||
}
|
||||
@@ -0,0 +1,142 @@
|
||||
package org.bxteam.divinemc.dfc.common.gen;
|
||||
|
||||
import com.google.common.base.Suppliers;
|
||||
import org.bxteam.divinemc.dfc.common.ast.EvalType;
|
||||
import org.bxteam.divinemc.dfc.common.ducks.IArrayCacheCapable;
|
||||
import org.bxteam.divinemc.dfc.common.ducks.IBlendingAwareVisitor;
|
||||
import org.bxteam.divinemc.dfc.common.ducks.ICoordinatesFilling;
|
||||
import org.bxteam.divinemc.dfc.common.util.ArrayCache;
|
||||
import org.bxteam.divinemc.dfc.common.vif.EachApplierVanillaInterface;
|
||||
import java.util.Objects;
|
||||
import java.util.function.Supplier;
|
||||
import net.minecraft.util.KeyDispatchDataCodec;
|
||||
import net.minecraft.world.level.levelgen.DensityFunction;
|
||||
import net.minecraft.world.level.levelgen.NoiseChunk;
|
||||
import net.minecraft.world.level.levelgen.blending.Blender;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public class SubCompiledDensityFunction implements DensityFunction {
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(SubCompiledDensityFunction.class);
|
||||
private final ISingleMethod singleMethod;
|
||||
private final IMultiMethod multiMethod;
|
||||
protected final Supplier<DensityFunction> blendingFallback;
|
||||
|
||||
public SubCompiledDensityFunction(ISingleMethod singleMethod, IMultiMethod multiMethod, DensityFunction blendingFallback) {
|
||||
this(singleMethod, multiMethod, unwrap(blendingFallback));
|
||||
}
|
||||
|
||||
protected SubCompiledDensityFunction(ISingleMethod singleMethod, IMultiMethod multiMethod, Supplier<DensityFunction> blendingFallback) {
|
||||
this.singleMethod = (ISingleMethod)Objects.requireNonNull(singleMethod);
|
||||
this.multiMethod = (IMultiMethod)Objects.requireNonNull(multiMethod);
|
||||
this.blendingFallback = blendingFallback;
|
||||
}
|
||||
|
||||
private static Supplier<DensityFunction> unwrap(DensityFunction densityFunction) {
|
||||
if (densityFunction instanceof SubCompiledDensityFunction scdf) {
|
||||
return scdf.blendingFallback;
|
||||
} else {
|
||||
return densityFunction != null ? Suppliers.ofInstance(densityFunction) : null;
|
||||
}
|
||||
}
|
||||
|
||||
public double compute(FunctionContext pos) {
|
||||
if (pos.getBlender() != Blender.empty()) {
|
||||
DensityFunction fallback = this.getFallback();
|
||||
if (fallback == null) {
|
||||
throw new IllegalStateException("blendingFallback is no more");
|
||||
} else {
|
||||
return fallback.compute(pos);
|
||||
}
|
||||
} else {
|
||||
return this.singleMethod.evalSingle(pos.blockX(), pos.blockY(), pos.blockZ(), EvalType.from(pos));
|
||||
}
|
||||
}
|
||||
|
||||
public void fillArray(double[] densities, ContextProvider applier) {
|
||||
if (applier instanceof NoiseChunk sampler) {
|
||||
if (sampler.getBlender() != Blender.empty()) {
|
||||
DensityFunction fallback = this.getFallback();
|
||||
if (fallback == null) {
|
||||
throw new IllegalStateException("blendingFallback is no more");
|
||||
}
|
||||
|
||||
fallback.fillArray(densities, applier);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (applier instanceof EachApplierVanillaInterface vanillaInterface) {
|
||||
this.multiMethod.evalMulti(densities, vanillaInterface.getX(), vanillaInterface.getY(), vanillaInterface.getZ(), EvalType.from(applier), vanillaInterface.c2me$getArrayCache());
|
||||
} else {
|
||||
ArrayCache var10000;
|
||||
if (applier instanceof IArrayCacheCapable cacheCapable) {
|
||||
var10000 = cacheCapable.c2me$getArrayCache();
|
||||
} else {
|
||||
var10000 = new ArrayCache();
|
||||
}
|
||||
|
||||
ArrayCache cache = var10000;
|
||||
int[] x = cache.getIntArray(densities.length, false);
|
||||
int[] y = cache.getIntArray(densities.length, false);
|
||||
int[] z = cache.getIntArray(densities.length, false);
|
||||
if (applier instanceof ICoordinatesFilling coordinatesFilling) {
|
||||
coordinatesFilling.c2me$fillCoordinates(x, y, z);
|
||||
} else {
|
||||
for(int i = 0; i < densities.length; ++i) {
|
||||
FunctionContext pos = applier.forIndex(i);
|
||||
x[i] = pos.blockX();
|
||||
y[i] = pos.blockY();
|
||||
z[i] = pos.blockZ();
|
||||
}
|
||||
}
|
||||
|
||||
this.multiMethod.evalMulti(densities, x, y, z, EvalType.from(applier), cache);
|
||||
}
|
||||
}
|
||||
|
||||
public DensityFunction mapAll(Visitor visitor) {
|
||||
if (this.getClass() != SubCompiledDensityFunction.class) {
|
||||
throw new AbstractMethodError();
|
||||
} else {
|
||||
if (visitor instanceof IBlendingAwareVisitor) {
|
||||
IBlendingAwareVisitor blendingAwareVisitor = (IBlendingAwareVisitor)visitor;
|
||||
if (blendingAwareVisitor.c2me$isBlendingEnabled()) {
|
||||
DensityFunction fallback1 = this.getFallback();
|
||||
if (fallback1 == null) {
|
||||
throw new IllegalStateException("blendingFallback is no more");
|
||||
}
|
||||
|
||||
return fallback1.mapAll(visitor);
|
||||
}
|
||||
}
|
||||
|
||||
boolean modified = false;
|
||||
Supplier<DensityFunction> fallback = this.blendingFallback != null ? Suppliers.memoize(() -> {
|
||||
DensityFunction densityFunction = (DensityFunction)this.blendingFallback.get();
|
||||
return densityFunction != null ? densityFunction.mapAll(visitor) : null;
|
||||
}) : null;
|
||||
if (fallback != this.blendingFallback) {
|
||||
modified = true;
|
||||
}
|
||||
|
||||
return modified ? new SubCompiledDensityFunction(this.singleMethod, this.multiMethod, fallback) : this;
|
||||
}
|
||||
}
|
||||
|
||||
public double minValue() {
|
||||
return Double.MIN_VALUE;
|
||||
}
|
||||
|
||||
public double maxValue() {
|
||||
return Double.MAX_VALUE;
|
||||
}
|
||||
|
||||
public KeyDispatchDataCodec<? extends DensityFunction> codec() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
protected DensityFunction getFallback() {
|
||||
return this.blendingFallback != null ? (DensityFunction)this.blendingFallback.get() : null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
package org.bxteam.divinemc.dfc.common.util;
|
||||
|
||||
import it.unimi.dsi.fastutil.ints.Int2ReferenceArrayMap;
|
||||
import it.unimi.dsi.fastutil.objects.ReferenceArrayList;
|
||||
import java.util.Arrays;
|
||||
|
||||
public class ArrayCache {
|
||||
private final Int2ReferenceArrayMap<ReferenceArrayList<double[]>> doubleArrayCache = new Int2ReferenceArrayMap<>();
|
||||
private final Int2ReferenceArrayMap<ReferenceArrayList<int[]>> intArrayCache = new Int2ReferenceArrayMap<>();
|
||||
|
||||
public ArrayCache() {
|
||||
}
|
||||
|
||||
public double[] getDoubleArray(int size, boolean zero) {
|
||||
ReferenceArrayList<double[]> list = this.doubleArrayCache.computeIfAbsent(size, (k) -> {
|
||||
return new ReferenceArrayList<>();
|
||||
});
|
||||
if (list.isEmpty()) {
|
||||
return new double[size];
|
||||
} else {
|
||||
double[] popped = list.pop();
|
||||
if (zero) {
|
||||
Arrays.fill(popped, 0.0);
|
||||
}
|
||||
|
||||
return popped;
|
||||
}
|
||||
}
|
||||
|
||||
public int[] getIntArray(int size, boolean zero) {
|
||||
ReferenceArrayList<int[]> list = this.intArrayCache.computeIfAbsent(size, (k) -> {
|
||||
return new ReferenceArrayList<>();
|
||||
});
|
||||
if (list.isEmpty()) {
|
||||
return new int[size];
|
||||
} else {
|
||||
int[] popped = list.pop();
|
||||
if (zero) {
|
||||
Arrays.fill(popped, 0);
|
||||
}
|
||||
|
||||
return popped;
|
||||
}
|
||||
}
|
||||
|
||||
public void recycle(double[] array) {
|
||||
this.doubleArrayCache.computeIfAbsent(array.length, (k) -> {
|
||||
return new ReferenceArrayList<>();
|
||||
}).add(array);
|
||||
}
|
||||
|
||||
public void recycle(int[] array) {
|
||||
this.intArrayCache.computeIfAbsent(array.length, (k) -> {
|
||||
return new ReferenceArrayList<>();
|
||||
}).add(array);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
package org.bxteam.divinemc.dfc.common.vif;
|
||||
|
||||
import org.bxteam.divinemc.dfc.common.ast.AstNode;
|
||||
import org.bxteam.divinemc.dfc.common.ast.EvalType;
|
||||
import org.bxteam.divinemc.dfc.common.ast.misc.CacheLikeNode;
|
||||
import org.bxteam.divinemc.dfc.common.ast.misc.DelegateNode;
|
||||
import org.bxteam.divinemc.dfc.common.ducks.IFastCacheLike;
|
||||
import java.util.Objects;
|
||||
import net.minecraft.util.KeyDispatchDataCodec;
|
||||
import net.minecraft.world.level.levelgen.DensityFunction;
|
||||
import net.minecraft.world.level.levelgen.NoiseChunk;
|
||||
import net.minecraft.world.level.levelgen.blending.Blender;
|
||||
|
||||
public class AstVanillaInterface implements DensityFunction {
|
||||
private final AstNode astNode;
|
||||
private final DensityFunction blendingFallback;
|
||||
|
||||
public AstVanillaInterface(AstNode astNode, DensityFunction blendingFallback) {
|
||||
this.astNode = (AstNode)Objects.requireNonNull(astNode);
|
||||
this.blendingFallback = blendingFallback;
|
||||
}
|
||||
|
||||
public double compute(FunctionContext pos) {
|
||||
if (pos.getBlender() != Blender.empty()) {
|
||||
if (this.blendingFallback == null) {
|
||||
throw new IllegalStateException("blendingFallback is no more");
|
||||
} else {
|
||||
return this.blendingFallback.compute(pos);
|
||||
}
|
||||
} else {
|
||||
return this.astNode.evalSingle(pos.blockX(), pos.blockY(), pos.blockZ(), EvalType.from(pos));
|
||||
}
|
||||
}
|
||||
|
||||
public void fillArray(double[] densities, ContextProvider applier) {
|
||||
if (applier instanceof NoiseChunk sampler) {
|
||||
if (sampler.getBlender() != Blender.empty()) {
|
||||
if (this.blendingFallback == null) {
|
||||
throw new IllegalStateException("blendingFallback is no more");
|
||||
}
|
||||
|
||||
this.blendingFallback.fillArray(densities, applier);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (applier instanceof EachApplierVanillaInterface vanillaInterface) {
|
||||
this.astNode.evalMulti(densities, vanillaInterface.getX(), vanillaInterface.getY(), vanillaInterface.getZ(), EvalType.from(applier));
|
||||
} else {
|
||||
int[] x = new int[densities.length];
|
||||
int[] y = new int[densities.length];
|
||||
int[] z = new int[densities.length];
|
||||
|
||||
for(int i = 0; i < densities.length; ++i) {
|
||||
FunctionContext pos = applier.forIndex(i);
|
||||
x[i] = pos.blockX();
|
||||
y[i] = pos.blockY();
|
||||
z[i] = pos.blockZ();
|
||||
}
|
||||
|
||||
this.astNode.evalMulti(densities, x, y, z, EvalType.from(applier));
|
||||
}
|
||||
}
|
||||
|
||||
public DensityFunction mapAll(Visitor visitor) {
|
||||
AstNode transformed = this.astNode.transform((astNode) -> {
|
||||
if (astNode instanceof DelegateNode delegateNode) {
|
||||
return new DelegateNode(delegateNode.getDelegate().mapAll(visitor));
|
||||
} else if (astNode instanceof CacheLikeNode cacheLikeNode) {
|
||||
return new CacheLikeNode((IFastCacheLike)cacheLikeNode.getCacheLike().mapAll(visitor), cacheLikeNode.getDelegate());
|
||||
} else {
|
||||
return astNode;
|
||||
}
|
||||
});
|
||||
DensityFunction blendingFallback1 = this.blendingFallback != null ? this.blendingFallback.mapAll(visitor) : null;
|
||||
return transformed == this.astNode && blendingFallback1 == this.blendingFallback ? this : new AstVanillaInterface(transformed, blendingFallback1);
|
||||
}
|
||||
|
||||
public double minValue() {
|
||||
return this.blendingFallback.minValue();
|
||||
}
|
||||
|
||||
public double maxValue() {
|
||||
return this.blendingFallback.maxValue();
|
||||
}
|
||||
|
||||
public KeyDispatchDataCodec<? extends DensityFunction> codec() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
public AstNode getAstNode() {
|
||||
return this.astNode;
|
||||
}
|
||||
|
||||
public DensityFunction getBlendingFallback() {
|
||||
return this.blendingFallback;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
package org.bxteam.divinemc.dfc.common.vif;
|
||||
|
||||
import org.bxteam.divinemc.dfc.common.ast.EvalType;
|
||||
import org.bxteam.divinemc.dfc.common.ducks.IArrayCacheCapable;
|
||||
import org.bxteam.divinemc.dfc.common.util.ArrayCache;
|
||||
import java.util.Objects;
|
||||
import net.minecraft.world.level.levelgen.DensityFunction;
|
||||
|
||||
public class EachApplierVanillaInterface implements DensityFunction.ContextProvider, IArrayCacheCapable {
|
||||
private final int[] x;
|
||||
private final int[] y;
|
||||
private final int[] z;
|
||||
private final EvalType type;
|
||||
private final ArrayCache cache;
|
||||
|
||||
public EachApplierVanillaInterface(int[] x, int[] y, int[] z, EvalType type) {
|
||||
this(x, y, z, type, new ArrayCache());
|
||||
}
|
||||
|
||||
public EachApplierVanillaInterface(int[] x, int[] y, int[] z, EvalType type, ArrayCache cache) {
|
||||
this.x = (int[])Objects.requireNonNull(x);
|
||||
this.y = (int[])Objects.requireNonNull(y);
|
||||
this.z = (int[])Objects.requireNonNull(z);
|
||||
this.type = (EvalType)Objects.requireNonNull(type);
|
||||
this.cache = (ArrayCache)Objects.requireNonNull(cache);
|
||||
}
|
||||
|
||||
public DensityFunction.FunctionContext forIndex(int index) {
|
||||
return new NoisePosVanillaInterface(this.x[index], this.y[index], this.z[index], this.type);
|
||||
}
|
||||
|
||||
public void fillAllDirectly(double[] densities, DensityFunction densityFunction) {
|
||||
for(int i = 0; i < this.x.length; ++i) {
|
||||
densities[i] = densityFunction.compute(this.forIndex(i));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public int[] getX() {
|
||||
return this.x;
|
||||
}
|
||||
|
||||
public int[] getY() {
|
||||
return this.y;
|
||||
}
|
||||
|
||||
public int[] getZ() {
|
||||
return this.z;
|
||||
}
|
||||
|
||||
public EvalType getType() {
|
||||
return this.type;
|
||||
}
|
||||
|
||||
public ArrayCache c2me$getArrayCache() {
|
||||
return this.cache;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
package org.bxteam.divinemc.dfc.common.vif;
|
||||
|
||||
import org.bxteam.divinemc.dfc.common.ast.EvalType;
|
||||
import java.util.Objects;
|
||||
import net.minecraft.world.level.levelgen.DensityFunction;
|
||||
|
||||
public class NoisePosVanillaInterface implements DensityFunction.FunctionContext {
|
||||
private final int x;
|
||||
private final int y;
|
||||
private final int z;
|
||||
private final EvalType type;
|
||||
|
||||
public NoisePosVanillaInterface(int x, int y, int z, EvalType type) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.z = z;
|
||||
this.type = (EvalType)Objects.requireNonNull(type);
|
||||
}
|
||||
|
||||
public int blockX() {
|
||||
return this.x;
|
||||
}
|
||||
|
||||
public int blockY() {
|
||||
return this.y;
|
||||
}
|
||||
|
||||
public int blockZ() {
|
||||
return this.z;
|
||||
}
|
||||
|
||||
public EvalType getType() {
|
||||
return this.type;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,284 @@
|
||||
package org.bxteam.divinemc.entity.pathfinding;
|
||||
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.level.pathfinder.Node;
|
||||
import net.minecraft.world.level.pathfinder.Path;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public class AsyncPath extends Path {
|
||||
/**
|
||||
* marks whether this async path has been processed
|
||||
*/
|
||||
private volatile PathProcessState processState = PathProcessState.WAITING;
|
||||
|
||||
/**
|
||||
* runnables waiting for this to be processed
|
||||
*/
|
||||
private final List<Runnable> postProcessing = new ArrayList<>(0);
|
||||
|
||||
/**
|
||||
* a list of positions that this path could path towards
|
||||
*/
|
||||
private final Set<BlockPos> positions;
|
||||
|
||||
/**
|
||||
* the supplier of the real processed path
|
||||
*/
|
||||
private final Supplier<Path> pathSupplier;
|
||||
|
||||
/*
|
||||
* Processed values
|
||||
*/
|
||||
|
||||
/**
|
||||
* this is a reference to the nodes list in the parent `Path` object
|
||||
*/
|
||||
private final List<Node> nodes;
|
||||
/**
|
||||
* the block we're trying to path to
|
||||
* <p>
|
||||
* while processing, we have no idea where this is so consumers of `Path` should check that the path is processed before checking the target block
|
||||
*/
|
||||
private @Nullable BlockPos target;
|
||||
/**
|
||||
* how far we are to the target
|
||||
* <p>
|
||||
* while processing, the target could be anywhere but theoretically we're always "close" to a theoretical target so default is 0
|
||||
*/
|
||||
private float distToTarget = 0;
|
||||
/**
|
||||
* whether we can reach the target
|
||||
* <p>
|
||||
* while processing, we can always theoretically reach the target so default is true
|
||||
*/
|
||||
private boolean canReach = true;
|
||||
|
||||
public AsyncPath(@NotNull List<Node> emptyNodeList, @NotNull Set<BlockPos> positions, @NotNull Supplier<Path> pathSupplier) {
|
||||
//noinspection ConstantConditions
|
||||
super(emptyNodeList, null, false);
|
||||
|
||||
this.nodes = emptyNodeList;
|
||||
this.positions = positions;
|
||||
this.pathSupplier = pathSupplier;
|
||||
|
||||
AsyncPathProcessor.queue(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isProcessed() {
|
||||
return this.processState == PathProcessState.COMPLETED;
|
||||
}
|
||||
|
||||
/**
|
||||
* returns the future representing the processing state of this path
|
||||
*/
|
||||
public synchronized void postProcessing(@NotNull Runnable runnable) {
|
||||
if (isProcessed()) {
|
||||
runnable.run();
|
||||
} else {
|
||||
this.postProcessing.add(runnable);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* an easy way to check if this processing path is the same as an attempted new path
|
||||
*
|
||||
* @param positions - the positions to compare against
|
||||
* @return true if we are processing the same positions
|
||||
*/
|
||||
public boolean hasSameProcessingPositions(final Set<BlockPos> positions) {
|
||||
if (this.positions.size() != positions.size()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return this.positions.containsAll(positions);
|
||||
}
|
||||
|
||||
/**
|
||||
* starts processing this path
|
||||
*/
|
||||
public synchronized void process() {
|
||||
if (this.processState == PathProcessState.COMPLETED ||
|
||||
this.processState == PathProcessState.PROCESSING) {
|
||||
return;
|
||||
}
|
||||
|
||||
processState = PathProcessState.PROCESSING;
|
||||
|
||||
final Path bestPath = this.pathSupplier.get();
|
||||
|
||||
this.nodes.addAll(bestPath.nodes); // we mutate this list to reuse the logic in Path
|
||||
this.target = bestPath.getTarget();
|
||||
this.distToTarget = bestPath.getDistToTarget();
|
||||
this.canReach = bestPath.canReach();
|
||||
|
||||
processState = PathProcessState.COMPLETED;
|
||||
|
||||
for (Runnable runnable : this.postProcessing) {
|
||||
runnable.run();
|
||||
} // Run tasks after processing
|
||||
}
|
||||
|
||||
/**
|
||||
* if this path is accessed while it hasn't processed, just process it in-place
|
||||
*/
|
||||
private void checkProcessed() {
|
||||
if (this.processState == PathProcessState.WAITING ||
|
||||
this.processState == PathProcessState.PROCESSING) { // Block if we are on processing
|
||||
this.process();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* overrides we need for final fields that we cannot modify after processing
|
||||
*/
|
||||
|
||||
@Override
|
||||
public @NotNull BlockPos getTarget() {
|
||||
this.checkProcessed();
|
||||
|
||||
return this.target;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getDistToTarget() {
|
||||
this.checkProcessed();
|
||||
|
||||
return this.distToTarget;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canReach() {
|
||||
this.checkProcessed();
|
||||
|
||||
return this.canReach;
|
||||
}
|
||||
|
||||
/*
|
||||
* overrides to ensure we're processed first
|
||||
*/
|
||||
|
||||
@Override
|
||||
public boolean isDone() {
|
||||
return this.processState == PathProcessState.COMPLETED && super.isDone();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void advance() {
|
||||
this.checkProcessed();
|
||||
|
||||
super.advance();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean notStarted() {
|
||||
this.checkProcessed();
|
||||
|
||||
return super.notStarted();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Node getEndNode() {
|
||||
this.checkProcessed();
|
||||
|
||||
return super.getEndNode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node getNode(int index) {
|
||||
this.checkProcessed();
|
||||
|
||||
return super.getNode(index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void truncateNodes(int length) {
|
||||
this.checkProcessed();
|
||||
|
||||
super.truncateNodes(length);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void replaceNode(int index, Node node) {
|
||||
this.checkProcessed();
|
||||
|
||||
super.replaceNode(index, node);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getNodeCount() {
|
||||
this.checkProcessed();
|
||||
|
||||
return super.getNodeCount();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getNextNodeIndex() {
|
||||
this.checkProcessed();
|
||||
|
||||
return super.getNextNodeIndex();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setNextNodeIndex(int nodeIndex) {
|
||||
this.checkProcessed();
|
||||
|
||||
super.setNextNodeIndex(nodeIndex);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Vec3 getEntityPosAtNode(Entity entity, int index) {
|
||||
this.checkProcessed();
|
||||
|
||||
return super.getEntityPosAtNode(entity, index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockPos getNodePos(int index) {
|
||||
this.checkProcessed();
|
||||
|
||||
return super.getNodePos(index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Vec3 getNextEntityPos(Entity entity) {
|
||||
this.checkProcessed();
|
||||
|
||||
return super.getNextEntityPos(entity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockPos getNextNodePos() {
|
||||
this.checkProcessed();
|
||||
|
||||
return super.getNextNodePos();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node getNextNode() {
|
||||
this.checkProcessed();
|
||||
|
||||
return super.getNextNode();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Node getPreviousNode() {
|
||||
this.checkProcessed();
|
||||
|
||||
return super.getPreviousNode();
|
||||
}
|
||||
|
||||
public PathProcessState getProcessState() {
|
||||
return processState;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
package org.bxteam.divinemc.entity.pathfinding;
|
||||
|
||||
import com.google.common.util.concurrent.ThreadFactoryBuilder;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.world.level.pathfinder.Path;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.concurrent.*;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
* used to handle the scheduling of async path processing
|
||||
*/
|
||||
public class AsyncPathProcessor {
|
||||
private static final Executor pathProcessingExecutor = new ThreadPoolExecutor(
|
||||
1,
|
||||
org.bxteam.divinemc.DivineConfig.asyncPathfindingMaxThreads,
|
||||
org.bxteam.divinemc.DivineConfig.asyncPathfindingKeepalive, TimeUnit.SECONDS,
|
||||
new LinkedBlockingQueue<>(),
|
||||
new ThreadFactoryBuilder()
|
||||
.setNameFormat("DivineMC Async Pathfinding Thread - %d")
|
||||
.setPriority(Thread.NORM_PRIORITY - 2)
|
||||
.build()
|
||||
);
|
||||
|
||||
protected static CompletableFuture<Void> queue(@NotNull AsyncPath path) {
|
||||
return CompletableFuture.runAsync(path::process, pathProcessingExecutor);
|
||||
}
|
||||
|
||||
/**
|
||||
* takes a possibly unprocessed path, and waits until it is completed
|
||||
* the consumer will be immediately invoked if the path is already processed
|
||||
* the consumer will always be called on the main thread
|
||||
*
|
||||
* @param path a path to wait on
|
||||
* @param afterProcessing a consumer to be called
|
||||
*/
|
||||
public static void awaitProcessing(@Nullable Path path, Consumer<@Nullable Path> afterProcessing) {
|
||||
if (path != null && !path.isProcessed() && path instanceof AsyncPath asyncPath) {
|
||||
asyncPath.postProcessing(() ->
|
||||
MinecraftServer.getServer().scheduleOnMain(() -> afterProcessing.accept(path))
|
||||
);
|
||||
} else {
|
||||
afterProcessing.accept(path);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
package org.bxteam.divinemc.entity.pathfinding;
|
||||
|
||||
import net.minecraft.world.level.pathfinder.NodeEvaluator;
|
||||
import org.apache.commons.lang.Validate;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Queue;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
|
||||
public class NodeEvaluatorCache {
|
||||
private static final Map<NodeEvaluatorFeatures, ConcurrentLinkedQueue<NodeEvaluator>> threadLocalNodeEvaluators = new ConcurrentHashMap<>();
|
||||
private static final Map<NodeEvaluator, NodeEvaluatorGenerator> nodeEvaluatorToGenerator = new ConcurrentHashMap<>();
|
||||
|
||||
private static @NotNull Queue<NodeEvaluator> getQueueForFeatures(@NotNull NodeEvaluatorFeatures nodeEvaluatorFeatures) {
|
||||
return threadLocalNodeEvaluators.computeIfAbsent(nodeEvaluatorFeatures, key -> new ConcurrentLinkedQueue<>());
|
||||
}
|
||||
|
||||
public static @NotNull NodeEvaluator takeNodeEvaluator(@NotNull NodeEvaluatorGenerator generator, @NotNull NodeEvaluator localNodeEvaluator) {
|
||||
final NodeEvaluatorFeatures nodeEvaluatorFeatures = NodeEvaluatorFeatures.fromNodeEvaluator(localNodeEvaluator);
|
||||
NodeEvaluator nodeEvaluator = getQueueForFeatures(nodeEvaluatorFeatures).poll();
|
||||
|
||||
if (nodeEvaluator == null) {
|
||||
nodeEvaluator = generator.generate(nodeEvaluatorFeatures);
|
||||
}
|
||||
|
||||
nodeEvaluatorToGenerator.put(nodeEvaluator, generator);
|
||||
|
||||
return nodeEvaluator;
|
||||
}
|
||||
|
||||
public static void returnNodeEvaluator(@NotNull NodeEvaluator nodeEvaluator) {
|
||||
final NodeEvaluatorGenerator generator = nodeEvaluatorToGenerator.remove(nodeEvaluator);
|
||||
Validate.notNull(generator, "NodeEvaluator already returned");
|
||||
|
||||
final NodeEvaluatorFeatures nodeEvaluatorFeatures = NodeEvaluatorFeatures.fromNodeEvaluator(nodeEvaluator);
|
||||
getQueueForFeatures(nodeEvaluatorFeatures).offer(nodeEvaluator);
|
||||
}
|
||||
|
||||
public static void removeNodeEvaluator(@NotNull NodeEvaluator nodeEvaluator) {
|
||||
nodeEvaluatorToGenerator.remove(nodeEvaluator);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package org.bxteam.divinemc.entity.pathfinding;
|
||||
|
||||
import net.minecraft.world.level.pathfinder.NodeEvaluator;
|
||||
import net.minecraft.world.level.pathfinder.SwimNodeEvaluator;
|
||||
|
||||
public record NodeEvaluatorFeatures(
|
||||
NodeEvaluatorType type,
|
||||
boolean canPassDoors,
|
||||
boolean canFloat,
|
||||
boolean canWalkOverFences,
|
||||
boolean canOpenDoors,
|
||||
boolean allowBreaching
|
||||
) {
|
||||
public static NodeEvaluatorFeatures fromNodeEvaluator(NodeEvaluator nodeEvaluator) {
|
||||
NodeEvaluatorType type = NodeEvaluatorType.fromNodeEvaluator(nodeEvaluator);
|
||||
boolean canPassDoors = nodeEvaluator.canPassDoors();
|
||||
boolean canFloat = nodeEvaluator.canFloat();
|
||||
boolean canWalkOverFences = nodeEvaluator.canWalkOverFences();
|
||||
boolean canOpenDoors = nodeEvaluator.canOpenDoors();
|
||||
boolean allowBreaching = nodeEvaluator instanceof SwimNodeEvaluator swimNodeEvaluator && swimNodeEvaluator.allowBreaching;
|
||||
return new NodeEvaluatorFeatures(type, canPassDoors, canFloat, canWalkOverFences, canOpenDoors, allowBreaching);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
package org.bxteam.divinemc.entity.pathfinding;
|
||||
|
||||
import net.minecraft.world.level.pathfinder.NodeEvaluator;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public interface NodeEvaluatorGenerator {
|
||||
@NotNull NodeEvaluator generate(NodeEvaluatorFeatures nodeEvaluatorFeatures);
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package org.bxteam.divinemc.entity.pathfinding;
|
||||
|
||||
import net.minecraft.world.level.pathfinder.*;
|
||||
|
||||
public enum NodeEvaluatorType {
|
||||
WALK,
|
||||
SWIM,
|
||||
AMPHIBIOUS,
|
||||
FLY;
|
||||
|
||||
public static NodeEvaluatorType fromNodeEvaluator(NodeEvaluator nodeEvaluator) {
|
||||
if (nodeEvaluator instanceof SwimNodeEvaluator) return SWIM;
|
||||
if (nodeEvaluator instanceof FlyNodeEvaluator) return FLY;
|
||||
if (nodeEvaluator instanceof AmphibiousNodeEvaluator) return AMPHIBIOUS;
|
||||
return WALK;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package org.bxteam.divinemc.entity.pathfinding;
|
||||
|
||||
public enum PathProcessState {
|
||||
WAITING,
|
||||
PROCESSING,
|
||||
COMPLETED
|
||||
}
|
||||
@@ -0,0 +1,141 @@
|
||||
package org.bxteam.divinemc.entity.tracking;
|
||||
|
||||
import ca.spottedleaf.moonrise.common.list.ReferenceList;
|
||||
import ca.spottedleaf.moonrise.common.misc.NearbyPlayers;
|
||||
import ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemServerLevel;
|
||||
import ca.spottedleaf.moonrise.patches.chunk_system.level.entity.server.ServerEntityLookup;
|
||||
import ca.spottedleaf.moonrise.patches.entity_tracker.EntityTrackerEntity;
|
||||
import ca.spottedleaf.moonrise.patches.entity_tracker.EntityTrackerTrackedEntity;
|
||||
import com.google.common.util.concurrent.ThreadFactoryBuilder;
|
||||
import net.minecraft.server.level.ChunkMap;
|
||||
import net.minecraft.server.level.FullChunkStatus;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
import java.util.concurrent.ThreadPoolExecutor;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class MultithreadedTracker {
|
||||
private static final Logger LOGGER = LogManager.getLogger("MultithreadedTracker");
|
||||
|
||||
public static class MultithreadedTrackerThread extends Thread {
|
||||
@Override
|
||||
public void run() {
|
||||
super.run();
|
||||
}
|
||||
}
|
||||
|
||||
private static final Executor trackerExecutor = new ThreadPoolExecutor(
|
||||
1,
|
||||
org.bxteam.divinemc.DivineConfig.asyncEntityTrackerMaxThreads,
|
||||
org.bxteam.divinemc.DivineConfig.asyncEntityTrackerKeepalive, TimeUnit.SECONDS,
|
||||
new LinkedBlockingQueue<>(),
|
||||
new ThreadFactoryBuilder()
|
||||
.setThreadFactory(
|
||||
r -> new MultithreadedTrackerThread() {
|
||||
@Override
|
||||
public void run() {
|
||||
r.run();
|
||||
}
|
||||
}
|
||||
)
|
||||
.setNameFormat("DivineMC Async Tracker Thread - %d")
|
||||
.setPriority(Thread.NORM_PRIORITY - 2)
|
||||
.build());
|
||||
|
||||
private MultithreadedTracker() { }
|
||||
|
||||
public static Executor getTrackerExecutor() {
|
||||
return trackerExecutor;
|
||||
}
|
||||
|
||||
public static void tick(ChunkSystemServerLevel level) {
|
||||
try {
|
||||
if (!org.bxteam.divinemc.DivineConfig.multithreadedCompatModeEnabled) {
|
||||
tickAsync(level);
|
||||
} else {
|
||||
tickAsyncWithCompatMode(level);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
LOGGER.error("Error occurred while executing async task.", e);
|
||||
}
|
||||
}
|
||||
|
||||
private static void tickAsync(ChunkSystemServerLevel level) {
|
||||
final NearbyPlayers nearbyPlayers = level.moonrise$getNearbyPlayers();
|
||||
final ServerEntityLookup entityLookup = (ServerEntityLookup) level.moonrise$getEntityLookup();
|
||||
|
||||
final ReferenceList<Entity> trackerEntities = entityLookup.trackerEntities;
|
||||
final Entity[] trackerEntitiesRaw = trackerEntities.getRawDataUnchecked();
|
||||
|
||||
// Move tracking to off-main
|
||||
trackerExecutor.execute(() -> {
|
||||
for (final Entity entity : trackerEntitiesRaw) {
|
||||
if (entity == null) continue;
|
||||
|
||||
final ChunkMap.TrackedEntity tracker = ((EntityTrackerEntity) entity).moonrise$getTrackedEntity();
|
||||
|
||||
if (tracker == null) continue;
|
||||
|
||||
((EntityTrackerTrackedEntity) tracker).moonrise$tick(nearbyPlayers.getChunk(entity.chunkPosition()));
|
||||
tracker.serverEntity.sendChanges();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private static void tickAsyncWithCompatMode(ChunkSystemServerLevel level) {
|
||||
final NearbyPlayers nearbyPlayers = level.moonrise$getNearbyPlayers();
|
||||
final ServerEntityLookup entityLookup = (ServerEntityLookup) level.moonrise$getEntityLookup();
|
||||
|
||||
final ReferenceList<Entity> trackerEntities = entityLookup.trackerEntities;
|
||||
final Entity[] trackerEntitiesRaw = trackerEntities.getRawDataUnchecked();
|
||||
final Runnable[] sendChangesTasks = new Runnable[trackerEntitiesRaw.length];
|
||||
int index = 0;
|
||||
|
||||
for (final Entity entity : trackerEntitiesRaw) {
|
||||
if (entity == null) continue;
|
||||
|
||||
final ChunkMap.TrackedEntity tracker = ((EntityTrackerEntity) entity).moonrise$getTrackedEntity();
|
||||
|
||||
if (tracker == null) continue;
|
||||
|
||||
((EntityTrackerTrackedEntity) tracker).moonrise$tick(nearbyPlayers.getChunk(entity.chunkPosition()));
|
||||
sendChangesTasks[index++] = () -> tracker.serverEntity.sendChanges(); // Collect send changes to task array
|
||||
}
|
||||
|
||||
// batch submit tasks
|
||||
trackerExecutor.execute(() -> {
|
||||
for (final Runnable sendChanges : sendChangesTasks) {
|
||||
if (sendChanges == null) continue;
|
||||
|
||||
sendChanges.run();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Original ChunkMap#newTrackerTick of Paper
|
||||
// Just for diff usage for future update
|
||||
@SuppressWarnings("DuplicatedCode")
|
||||
private static void tickOriginal(ServerLevel level) {
|
||||
final ca.spottedleaf.moonrise.patches.chunk_system.level.entity.server.ServerEntityLookup entityLookup = (ca.spottedleaf.moonrise.patches.chunk_system.level.entity.server.ServerEntityLookup) ((ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemServerLevel) level).moonrise$getEntityLookup();
|
||||
|
||||
final ca.spottedleaf.moonrise.common.list.ReferenceList<net.minecraft.world.entity.Entity> trackerEntities = entityLookup.trackerEntities;
|
||||
final Entity[] trackerEntitiesRaw = trackerEntities.getRawDataUnchecked();
|
||||
for (int i = 0, len = trackerEntities.size(); i < len; ++i) {
|
||||
final Entity entity = trackerEntitiesRaw[i];
|
||||
final ChunkMap.TrackedEntity tracker = ((ca.spottedleaf.moonrise.patches.entity_tracker.EntityTrackerEntity) entity).moonrise$getTrackedEntity();
|
||||
if (tracker == null) {
|
||||
continue;
|
||||
}
|
||||
((ca.spottedleaf.moonrise.patches.entity_tracker.EntityTrackerTrackedEntity) tracker).moonrise$tick(((ca.spottedleaf.moonrise.patches.chunk_system.entity.ChunkSystemEntity) entity).moonrise$getChunkData().nearbyPlayers);
|
||||
if (((ca.spottedleaf.moonrise.patches.entity_tracker.EntityTrackerTrackedEntity) tracker).moonrise$hasPlayers()
|
||||
|| ((ca.spottedleaf.moonrise.patches.chunk_system.entity.ChunkSystemEntity) entity).moonrise$getChunkStatus().isOrAfter(FullChunkStatus.ENTITY_TICKING)) {
|
||||
tracker.serverEntity.sendChanges();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
package org.bxteam.divinemc.math;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import java.lang.foreign.MemorySegment;
|
||||
import java.lang.invoke.MethodHandle;
|
||||
|
||||
public class Bindings {
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(Bindings.class);
|
||||
|
||||
private static final MethodHandle MH_c2me_natives_noise_perlin_double = bind(BindingsTemplate.c2me_natives_noise_perlin_double, "c2me_natives_noise_perlin_double");
|
||||
private static final MethodHandle MH_c2me_natives_noise_perlin_double_ptr = bind(BindingsTemplate.c2me_natives_noise_perlin_double_ptr, "c2me_natives_noise_perlin_double");
|
||||
private static final MethodHandle MH_c2me_natives_noise_perlin_double_batch = bind(BindingsTemplate.c2me_natives_noise_perlin_double_batch, "c2me_natives_noise_perlin_double_batch");
|
||||
private static final MethodHandle MH_c2me_natives_noise_perlin_double_batch_partial_ptr = bind(BindingsTemplate.c2me_natives_noise_perlin_double_batch_ptr, "c2me_natives_noise_perlin_double_batch");
|
||||
private static final MethodHandle MH_c2me_natives_noise_interpolated = bind(BindingsTemplate.c2me_natives_noise_interpolated, "c2me_natives_noise_interpolated");
|
||||
private static final MethodHandle MH_c2me_natives_noise_interpolated_ptr = bind(BindingsTemplate.c2me_natives_noise_interpolated_ptr, "c2me_natives_noise_interpolated");
|
||||
private static final MethodHandle MH_c2me_natives_end_islands_sample = bind(BindingsTemplate.c2me_natives_end_islands_sample, "c2me_natives_end_islands_sample");
|
||||
private static final MethodHandle MH_c2me_natives_end_islands_sample_ptr = bind(BindingsTemplate.c2me_natives_end_islands_sample_ptr, "c2me_natives_end_islands_sample");
|
||||
private static final MethodHandle MH_c2me_natives_biome_access_sample = bind(BindingsTemplate.c2me_natives_biome_access_sample, "c2me_natives_biome_access_sample");
|
||||
|
||||
private static @Nullable MethodHandle bind(@NotNull MethodHandle template, String prefix) {
|
||||
if (NativeLoader.currentMachineTarget == null) {
|
||||
LOGGER.warn("Call to bindings was found! Please disable native acceleration in config, as your system may be incompatible");
|
||||
return null;
|
||||
}
|
||||
return template.bindTo(NativeLoader.lookup.find(prefix + NativeLoader.currentMachineTarget.getSuffix()).get());
|
||||
}
|
||||
|
||||
public static double c2me_natives_noise_perlin_double(MemorySegment data, double x, double y, double z) {
|
||||
try {
|
||||
return (double) MH_c2me_natives_noise_perlin_double.invokeExact(data, x, y, z);
|
||||
} catch (Throwable e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public static double c2me_natives_noise_perlin_double(long data_ptr, double x, double y, double z) {
|
||||
try {
|
||||
return (double) MH_c2me_natives_noise_perlin_double_ptr.invokeExact(data_ptr, x, y, z);
|
||||
} catch (Throwable e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public static void c2me_natives_noise_perlin_double_batch(MemorySegment data, MemorySegment res, MemorySegment x, MemorySegment y, MemorySegment z, int length) {
|
||||
try {
|
||||
MH_c2me_natives_noise_perlin_double_batch.invokeExact(data, res, x, y, z, length);
|
||||
} catch (Throwable e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public static void c2me_natives_noise_perlin_double_batch(long data_ptr, MemorySegment res, MemorySegment x, MemorySegment y, MemorySegment z, int length) {
|
||||
try {
|
||||
MH_c2me_natives_noise_perlin_double_batch_partial_ptr.invokeExact(data_ptr, res, x, y, z, length);
|
||||
} catch (Throwable e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public static double c2me_natives_noise_interpolated(MemorySegment data, double x, double y, double z) {
|
||||
try {
|
||||
return (double) MH_c2me_natives_noise_interpolated.invokeExact(data, x, y, z);
|
||||
} catch (Throwable e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public static double c2me_natives_noise_interpolated(long data_ptr, double x, double y, double z) {
|
||||
try {
|
||||
return (double) MH_c2me_natives_noise_interpolated_ptr.invokeExact(data_ptr, x, y, z);
|
||||
} catch (Throwable e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public static float c2me_natives_end_islands_sample(MemorySegment data, int x, int z) {
|
||||
try {
|
||||
return (float) MH_c2me_natives_end_islands_sample.invokeExact(data, x, z);
|
||||
} catch (Throwable e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public static float c2me_natives_end_islands_sample(long data_ptr, int x, int z) {
|
||||
if ((x * x + z * z) < 0) { // workaround some compiler bugs
|
||||
return Float.NaN;
|
||||
}
|
||||
try {
|
||||
return (float) MH_c2me_natives_end_islands_sample_ptr.invokeExact(data_ptr, x, z);
|
||||
} catch (Throwable e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public static int c2me_natives_biome_access_sample(long seed, int x, int y, int z) {
|
||||
try {
|
||||
return (int) MH_c2me_natives_biome_access_sample.invokeExact(seed, x, y, z);
|
||||
} catch (Throwable e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,407 @@
|
||||
package org.bxteam.divinemc.math;
|
||||
|
||||
import net.minecraft.world.level.levelgen.synth.BlendedNoise;
|
||||
import net.minecraft.world.level.levelgen.synth.ImprovedNoise;
|
||||
import net.minecraft.world.level.levelgen.synth.PerlinNoise;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.bxteam.divinemc.util.MemoryUtil;
|
||||
|
||||
import java.lang.foreign.*;
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.VarHandle;
|
||||
import java.util.Objects;
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
public class BindingsTemplate {
|
||||
// double c2me_natives_noise_perlin_sample (const uint8_t *permutations, double originX, double originY, double originZ, double x, double y, double z, double yScale, double yMax)
|
||||
public static final MethodHandle c2me_natives_noise_perlin_sample = NativeLoader.linker.downcallHandle(
|
||||
FunctionDescriptor.of(
|
||||
ValueLayout.JAVA_DOUBLE,
|
||||
ValueLayout.ADDRESS,
|
||||
ValueLayout.JAVA_DOUBLE,
|
||||
ValueLayout.JAVA_DOUBLE,
|
||||
ValueLayout.JAVA_DOUBLE,
|
||||
ValueLayout.JAVA_DOUBLE,
|
||||
ValueLayout.JAVA_DOUBLE,
|
||||
ValueLayout.JAVA_DOUBLE,
|
||||
ValueLayout.JAVA_DOUBLE,
|
||||
ValueLayout.JAVA_DOUBLE
|
||||
),
|
||||
Linker.Option.critical(true)
|
||||
);
|
||||
|
||||
// c2me_natives_noise_perlin_double, double, (const double_octave_sampler_data_t *data, double x, double y, double z)
|
||||
public static final MethodHandle c2me_natives_noise_perlin_double = NativeLoader.linker.downcallHandle(
|
||||
FunctionDescriptor.of(
|
||||
ValueLayout.JAVA_DOUBLE,
|
||||
ValueLayout.ADDRESS,
|
||||
ValueLayout.JAVA_DOUBLE,
|
||||
ValueLayout.JAVA_DOUBLE,
|
||||
ValueLayout.JAVA_DOUBLE
|
||||
),
|
||||
Linker.Option.critical(false)
|
||||
);
|
||||
public static final MethodHandle c2me_natives_noise_perlin_double_ptr = NativeLoader.linker.downcallHandle(
|
||||
FunctionDescriptor.of(
|
||||
ValueLayout.JAVA_DOUBLE,
|
||||
ValueLayout.JAVA_LONG,
|
||||
ValueLayout.JAVA_DOUBLE,
|
||||
ValueLayout.JAVA_DOUBLE,
|
||||
ValueLayout.JAVA_DOUBLE
|
||||
),
|
||||
Linker.Option.critical(false)
|
||||
);
|
||||
|
||||
// c2me_natives_noise_perlin_double_batch, void, (const double_octave_sampler_data_t *const data,
|
||||
// double *const res, const double *const x,
|
||||
// const double *const y, const double *const z,
|
||||
// const uint32_t length)
|
||||
public static final MethodHandle c2me_natives_noise_perlin_double_batch = NativeLoader.linker.downcallHandle(
|
||||
FunctionDescriptor.ofVoid(
|
||||
ValueLayout.ADDRESS,
|
||||
ValueLayout.ADDRESS,
|
||||
ValueLayout.ADDRESS,
|
||||
ValueLayout.ADDRESS,
|
||||
ValueLayout.ADDRESS,
|
||||
ValueLayout.JAVA_INT
|
||||
),
|
||||
Linker.Option.critical(true)
|
||||
);
|
||||
public static final MethodHandle c2me_natives_noise_perlin_double_batch_ptr = NativeLoader.linker.downcallHandle(
|
||||
FunctionDescriptor.ofVoid(
|
||||
ValueLayout.JAVA_LONG,
|
||||
ValueLayout.ADDRESS,
|
||||
ValueLayout.ADDRESS,
|
||||
ValueLayout.ADDRESS,
|
||||
ValueLayout.ADDRESS,
|
||||
ValueLayout.JAVA_INT
|
||||
),
|
||||
Linker.Option.critical(true)
|
||||
);
|
||||
|
||||
|
||||
public static final StructLayout double_octave_sampler_data = MemoryLayout.structLayout(
|
||||
ValueLayout.JAVA_LONG.withName("length"),
|
||||
ValueLayout.JAVA_DOUBLE.withName("amplitude"),
|
||||
ValueLayout.ADDRESS.withName("need_shift"),
|
||||
ValueLayout.ADDRESS.withName("lacunarity_powd"),
|
||||
ValueLayout.ADDRESS.withName("persistence_powd"),
|
||||
ValueLayout.ADDRESS.withName("sampler_permutations"),
|
||||
ValueLayout.ADDRESS.withName("sampler_originX"),
|
||||
ValueLayout.ADDRESS.withName("sampler_originY"),
|
||||
ValueLayout.ADDRESS.withName("sampler_originZ"),
|
||||
ValueLayout.ADDRESS.withName("amplitudes")
|
||||
).withByteAlignment(32).withName("double_double_octave_sampler_data");
|
||||
public static final VarHandle double_octave_sampler_data$length = double_octave_sampler_data.varHandle(MemoryLayout.PathElement.groupElement("length"));
|
||||
public static final VarHandle double_octave_sampler_data$amplitude = double_octave_sampler_data.varHandle(MemoryLayout.PathElement.groupElement("amplitude"));
|
||||
public static final VarHandle double_octave_sampler_data$need_shift = double_octave_sampler_data.varHandle(MemoryLayout.PathElement.groupElement("need_shift"));
|
||||
public static final VarHandle double_octave_sampler_data$lacunarity_powd = double_octave_sampler_data.varHandle(MemoryLayout.PathElement.groupElement("lacunarity_powd"));
|
||||
public static final VarHandle double_octave_sampler_data$persistence_powd = double_octave_sampler_data.varHandle(MemoryLayout.PathElement.groupElement("persistence_powd"));
|
||||
public static final VarHandle double_octave_sampler_data$sampler_permutations = double_octave_sampler_data.varHandle(MemoryLayout.PathElement.groupElement("sampler_permutations"));
|
||||
public static final VarHandle double_octave_sampler_data$sampler_originX = double_octave_sampler_data.varHandle(MemoryLayout.PathElement.groupElement("sampler_originX"));
|
||||
public static final VarHandle double_octave_sampler_data$sampler_originY = double_octave_sampler_data.varHandle(MemoryLayout.PathElement.groupElement("sampler_originY"));
|
||||
public static final VarHandle double_octave_sampler_data$sampler_originZ = double_octave_sampler_data.varHandle(MemoryLayout.PathElement.groupElement("sampler_originZ"));
|
||||
public static final VarHandle double_octave_sampler_data$amplitudes = double_octave_sampler_data.varHandle(MemoryLayout.PathElement.groupElement("amplitudes"));
|
||||
|
||||
public static MemorySegment double_octave_sampler_data$create(Arena arena, @NotNull PerlinNoise firstSampler, PerlinNoise secondSampler, double amplitude) {
|
||||
long nonNullSamplerCount = 0;
|
||||
for (ImprovedNoise sampler : (firstSampler.noiseLevels)) {
|
||||
if (sampler != null) {
|
||||
nonNullSamplerCount++;
|
||||
}
|
||||
}
|
||||
for (ImprovedNoise sampler : (secondSampler.noiseLevels)) {
|
||||
if (sampler != null) {
|
||||
nonNullSamplerCount++;
|
||||
}
|
||||
}
|
||||
final MemorySegment data = arena.allocate(double_octave_sampler_data.byteSize(), 64);
|
||||
final MemorySegment need_shift = arena.allocate(nonNullSamplerCount, 64);
|
||||
final MemorySegment lacunarity_powd = arena.allocate(nonNullSamplerCount * 8, 64);
|
||||
final MemorySegment persistence_powd = arena.allocate(nonNullSamplerCount * 8, 64);
|
||||
final MemorySegment sampler_permutations = arena.allocate(nonNullSamplerCount * 256 * 4, 64);
|
||||
final MemorySegment sampler_originX = arena.allocate(nonNullSamplerCount * 8, 64);
|
||||
final MemorySegment sampler_originY = arena.allocate(nonNullSamplerCount * 8, 64);
|
||||
final MemorySegment sampler_originZ = arena.allocate(nonNullSamplerCount * 8, 64);
|
||||
final MemorySegment amplitudes = arena.allocate(nonNullSamplerCount * 8, 64);
|
||||
double_octave_sampler_data$length.set(data, 0L, nonNullSamplerCount);
|
||||
double_octave_sampler_data$amplitude.set(data, 0L, amplitude);
|
||||
double_octave_sampler_data$need_shift.set(data, 0L, need_shift);
|
||||
double_octave_sampler_data$lacunarity_powd.set(data, 0L, lacunarity_powd);
|
||||
double_octave_sampler_data$persistence_powd.set(data, 0L, persistence_powd);
|
||||
double_octave_sampler_data$sampler_permutations.set(data, 0L, sampler_permutations);
|
||||
double_octave_sampler_data$sampler_originX.set(data, 0L, sampler_originX);
|
||||
double_octave_sampler_data$sampler_originY.set(data, 0L, sampler_originY);
|
||||
double_octave_sampler_data$sampler_originZ.set(data, 0L, sampler_originZ);
|
||||
double_octave_sampler_data$amplitudes.set(data, 0L, amplitudes);
|
||||
long index = 0;
|
||||
{
|
||||
ImprovedNoise[] octaveSamplers = (firstSampler.noiseLevels);
|
||||
for (int i = 0, octaveSamplersLength = octaveSamplers.length; i < octaveSamplersLength; i++) {
|
||||
ImprovedNoise sampler = octaveSamplers[i];
|
||||
if (sampler != null) {
|
||||
need_shift.set(ValueLayout.JAVA_BOOLEAN, index, false);
|
||||
lacunarity_powd.set(ValueLayout.JAVA_DOUBLE, index * 8, (firstSampler.lowestFreqInputFactor) * Math.pow(2.0, i));
|
||||
persistence_powd.set(ValueLayout.JAVA_DOUBLE, index * 8, (firstSampler.lowestFreqValueFactor) * Math.pow(2.0, -i));
|
||||
MemorySegment.copy(MemorySegment.ofArray(MemoryUtil.byte2int(sampler.p)), 0, sampler_permutations, index * 256L * 4L, 256 * 4);
|
||||
sampler_originX.set(ValueLayout.JAVA_DOUBLE, index * 8, sampler.xo);
|
||||
sampler_originY.set(ValueLayout.JAVA_DOUBLE, index * 8, sampler.yo);
|
||||
sampler_originZ.set(ValueLayout.JAVA_DOUBLE, index * 8, sampler.zo);
|
||||
amplitudes.set(ValueLayout.JAVA_DOUBLE, index * 8, (firstSampler.amplitudes).getDouble(i));
|
||||
index++;
|
||||
}
|
||||
}
|
||||
}
|
||||
{
|
||||
ImprovedNoise[] octaveSamplers = (secondSampler.noiseLevels);
|
||||
for (int i = 0, octaveSamplersLength = octaveSamplers.length; i < octaveSamplersLength; i++) {
|
||||
ImprovedNoise sampler = octaveSamplers[i];
|
||||
if (sampler != null) {
|
||||
need_shift.set(ValueLayout.JAVA_BOOLEAN, index, true);
|
||||
lacunarity_powd.set(ValueLayout.JAVA_DOUBLE, index * 8, (secondSampler.lowestFreqInputFactor) * Math.pow(2.0, i));
|
||||
persistence_powd.set(ValueLayout.JAVA_DOUBLE, index * 8, (secondSampler.lowestFreqValueFactor) * Math.pow(2.0, -i));
|
||||
MemorySegment.copy(MemorySegment.ofArray(MemoryUtil.byte2int(sampler.p)), 0, sampler_permutations, index * 256L * 4L, 256 * 4);
|
||||
sampler_originX.set(ValueLayout.JAVA_DOUBLE, index * 8, sampler.xo);
|
||||
sampler_originY.set(ValueLayout.JAVA_DOUBLE, index * 8, sampler.yo);
|
||||
sampler_originZ.set(ValueLayout.JAVA_DOUBLE, index * 8, sampler.zo);
|
||||
amplitudes.set(ValueLayout.JAVA_DOUBLE, index * 8, (secondSampler.amplitudes).getDouble(i));
|
||||
index++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
VarHandle.fullFence();
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
// c2me_natives_noise_interpolated, double, (const interpolated_noise_sampler_t *const data, const double x, const double y, const double z)
|
||||
public static final MethodHandle c2me_natives_noise_interpolated = NativeLoader.linker.downcallHandle(
|
||||
FunctionDescriptor.of(
|
||||
ValueLayout.JAVA_DOUBLE,
|
||||
ValueLayout.ADDRESS,
|
||||
ValueLayout.JAVA_DOUBLE,
|
||||
ValueLayout.JAVA_DOUBLE,
|
||||
ValueLayout.JAVA_DOUBLE
|
||||
),
|
||||
Linker.Option.critical(false)
|
||||
);
|
||||
public static final MethodHandle c2me_natives_noise_interpolated_ptr = NativeLoader.linker.downcallHandle(
|
||||
FunctionDescriptor.of(
|
||||
ValueLayout.JAVA_DOUBLE,
|
||||
ValueLayout.JAVA_LONG,
|
||||
ValueLayout.JAVA_DOUBLE,
|
||||
ValueLayout.JAVA_DOUBLE,
|
||||
ValueLayout.JAVA_DOUBLE
|
||||
),
|
||||
Linker.Option.critical(false)
|
||||
);
|
||||
|
||||
// typedef const struct interpolated_noise_sub_sampler {
|
||||
// const aligned_uint8_ptr sampler_permutations;
|
||||
// const aligned_double_ptr sampler_originX;
|
||||
// const aligned_double_ptr sampler_originY;
|
||||
// const aligned_double_ptr sampler_originZ;
|
||||
// const aligned_double_ptr sampler_mulFactor;
|
||||
// const uint32_t length;
|
||||
// } interpolated_noise_sub_sampler_t;
|
||||
public static final StructLayout interpolated_noise_sub_sampler = MemoryLayout.structLayout(
|
||||
ValueLayout.ADDRESS.withName("sampler_permutations"),
|
||||
ValueLayout.ADDRESS.withName("sampler_originX"),
|
||||
ValueLayout.ADDRESS.withName("sampler_originY"),
|
||||
ValueLayout.ADDRESS.withName("sampler_originZ"),
|
||||
ValueLayout.ADDRESS.withName("sampler_mulFactor"),
|
||||
ValueLayout.JAVA_INT.withName("length"),
|
||||
MemoryLayout.paddingLayout(4)
|
||||
).withName("interpolated_noise_sub_sampler_t");
|
||||
|
||||
// typedef const struct interpolated_noise_sampler {
|
||||
// const double scaledXzScale;
|
||||
// const double scaledYScale;
|
||||
// const double xzFactor;
|
||||
// const double yFactor;
|
||||
// const double smearScaleMultiplier;
|
||||
// const double xzScale;
|
||||
// const double yScale;
|
||||
//
|
||||
// const interpolated_noise_sub_sampler_t lower;
|
||||
// const interpolated_noise_sub_sampler_t upper;
|
||||
// const interpolated_noise_sub_sampler_t normal;
|
||||
// } interpolated_noise_sampler_t;
|
||||
public static final StructLayout interpolated_noise_sampler = MemoryLayout.structLayout(
|
||||
ValueLayout.JAVA_DOUBLE.withName("scaledXzScale"),
|
||||
ValueLayout.JAVA_DOUBLE.withName("scaledYScale"),
|
||||
ValueLayout.JAVA_DOUBLE.withName("xzFactor"),
|
||||
ValueLayout.JAVA_DOUBLE.withName("yFactor"),
|
||||
ValueLayout.JAVA_DOUBLE.withName("smearScaleMultiplier"),
|
||||
ValueLayout.JAVA_DOUBLE.withName("xzScale"),
|
||||
ValueLayout.JAVA_DOUBLE.withName("yScale"),
|
||||
|
||||
interpolated_noise_sub_sampler.withName("lower"),
|
||||
interpolated_noise_sub_sampler.withName("upper"),
|
||||
interpolated_noise_sub_sampler.withName("normal")
|
||||
).withByteAlignment(32).withName("interpolated_noise_sampler_t");
|
||||
|
||||
public static final VarHandle interpolated_noise_sampler$scaledXzScale = interpolated_noise_sampler.varHandle(MemoryLayout.PathElement.groupElement("scaledXzScale"));
|
||||
public static final VarHandle interpolated_noise_sampler$scaledYScale = interpolated_noise_sampler.varHandle(MemoryLayout.PathElement.groupElement("scaledYScale"));
|
||||
public static final VarHandle interpolated_noise_sampler$xzFactor = interpolated_noise_sampler.varHandle(MemoryLayout.PathElement.groupElement("xzFactor"));
|
||||
public static final VarHandle interpolated_noise_sampler$yFactor = interpolated_noise_sampler.varHandle(MemoryLayout.PathElement.groupElement("yFactor"));
|
||||
public static final VarHandle interpolated_noise_sampler$smearScaleMultiplier = interpolated_noise_sampler.varHandle(MemoryLayout.PathElement.groupElement("smearScaleMultiplier"));
|
||||
public static final VarHandle interpolated_noise_sampler$xzScale = interpolated_noise_sampler.varHandle(MemoryLayout.PathElement.groupElement("xzScale"));
|
||||
public static final VarHandle interpolated_noise_sampler$yScale = interpolated_noise_sampler.varHandle(MemoryLayout.PathElement.groupElement("yScale"));
|
||||
public static final VarHandle interpolated_noise_sampler$lower$sampler_permutations = interpolated_noise_sampler.varHandle(MemoryLayout.PathElement.groupElement("lower"), MemoryLayout.PathElement.groupElement("sampler_permutations"));
|
||||
public static final VarHandle interpolated_noise_sampler$lower$sampler_originX = interpolated_noise_sampler.varHandle(MemoryLayout.PathElement.groupElement("lower"), MemoryLayout.PathElement.groupElement("sampler_originX"));
|
||||
public static final VarHandle interpolated_noise_sampler$lower$sampler_originY = interpolated_noise_sampler.varHandle(MemoryLayout.PathElement.groupElement("lower"), MemoryLayout.PathElement.groupElement("sampler_originY"));
|
||||
public static final VarHandle interpolated_noise_sampler$lower$sampler_originZ = interpolated_noise_sampler.varHandle(MemoryLayout.PathElement.groupElement("lower"), MemoryLayout.PathElement.groupElement("sampler_originZ"));
|
||||
public static final VarHandle interpolated_noise_sampler$lower$sampler_mulFactor = interpolated_noise_sampler.varHandle(MemoryLayout.PathElement.groupElement("lower"), MemoryLayout.PathElement.groupElement("sampler_mulFactor"));
|
||||
public static final VarHandle interpolated_noise_sampler$lower$length = interpolated_noise_sampler.varHandle(MemoryLayout.PathElement.groupElement("lower"), MemoryLayout.PathElement.groupElement("length"));
|
||||
public static final VarHandle interpolated_noise_sampler$upper$sampler_permutations = interpolated_noise_sampler.varHandle(MemoryLayout.PathElement.groupElement("upper"), MemoryLayout.PathElement.groupElement("sampler_permutations"));
|
||||
public static final VarHandle interpolated_noise_sampler$upper$sampler_originX = interpolated_noise_sampler.varHandle(MemoryLayout.PathElement.groupElement("upper"), MemoryLayout.PathElement.groupElement("sampler_originX"));
|
||||
public static final VarHandle interpolated_noise_sampler$upper$sampler_originY = interpolated_noise_sampler.varHandle(MemoryLayout.PathElement.groupElement("upper"), MemoryLayout.PathElement.groupElement("sampler_originY"));
|
||||
public static final VarHandle interpolated_noise_sampler$upper$sampler_originZ = interpolated_noise_sampler.varHandle(MemoryLayout.PathElement.groupElement("upper"), MemoryLayout.PathElement.groupElement("sampler_originZ"));
|
||||
public static final VarHandle interpolated_noise_sampler$upper$sampler_mulFactor = interpolated_noise_sampler.varHandle(MemoryLayout.PathElement.groupElement("upper"), MemoryLayout.PathElement.groupElement("sampler_mulFactor"));
|
||||
public static final VarHandle interpolated_noise_sampler$upper$length = interpolated_noise_sampler.varHandle(MemoryLayout.PathElement.groupElement("upper"), MemoryLayout.PathElement.groupElement("length"));
|
||||
public static final VarHandle interpolated_noise_sampler$normal$sampler_permutations = interpolated_noise_sampler.varHandle(MemoryLayout.PathElement.groupElement("normal"), MemoryLayout.PathElement.groupElement("sampler_permutations"));
|
||||
public static final VarHandle interpolated_noise_sampler$normal$sampler_originX = interpolated_noise_sampler.varHandle(MemoryLayout.PathElement.groupElement("normal"), MemoryLayout.PathElement.groupElement("sampler_originX"));
|
||||
public static final VarHandle interpolated_noise_sampler$normal$sampler_originY = interpolated_noise_sampler.varHandle(MemoryLayout.PathElement.groupElement("normal"), MemoryLayout.PathElement.groupElement("sampler_originY"));
|
||||
public static final VarHandle interpolated_noise_sampler$normal$sampler_originZ = interpolated_noise_sampler.varHandle(MemoryLayout.PathElement.groupElement("normal"), MemoryLayout.PathElement.groupElement("sampler_originZ"));
|
||||
public static final VarHandle interpolated_noise_sampler$normal$sampler_mulFactor = interpolated_noise_sampler.varHandle(MemoryLayout.PathElement.groupElement("normal"), MemoryLayout.PathElement.groupElement("sampler_mulFactor"));
|
||||
public static final VarHandle interpolated_noise_sampler$normal$length = interpolated_noise_sampler.varHandle(MemoryLayout.PathElement.groupElement("normal"), MemoryLayout.PathElement.groupElement("length"));
|
||||
|
||||
public static boolean interpolated_noise_sampler$isSpecializedBase3dNoiseFunction(BlendedNoise interpolated) {
|
||||
return IntStream.range(0, 16).mapToObj((interpolated).minLimitNoise::getOctaveNoise).filter(Objects::nonNull).count() == 16 &&
|
||||
IntStream.range(0, 16).mapToObj((interpolated).maxLimitNoise::getOctaveNoise).filter(Objects::nonNull).count() == 16 &&
|
||||
IntStream.range(0, 8).mapToObj((interpolated).mainNoise::getOctaveNoise).filter(Objects::nonNull).count() == 8;
|
||||
}
|
||||
|
||||
public static MemorySegment interpolated_noise_sampler$create(@NotNull Arena arena, BlendedNoise interpolated) {
|
||||
final MemorySegment data = arena.allocate(interpolated_noise_sampler.byteSize(), 64);
|
||||
interpolated_noise_sampler$scaledXzScale.set(data, 0L, (interpolated).xzMultiplier);
|
||||
interpolated_noise_sampler$scaledYScale.set(data, 0L, (interpolated).yMultiplier);
|
||||
interpolated_noise_sampler$xzFactor.set(data, 0L, (interpolated).xzFactor);
|
||||
interpolated_noise_sampler$yFactor.set(data, 0L, (interpolated).yFactor);
|
||||
interpolated_noise_sampler$smearScaleMultiplier.set(data, 0L, (interpolated).smearScaleMultiplier);
|
||||
interpolated_noise_sampler$xzScale.set(data, 0L, (interpolated).xzScale);
|
||||
interpolated_noise_sampler$yScale.set(data, 0L, (interpolated).yScale);
|
||||
|
||||
// if (true) {
|
||||
// System.out.println(String.format("Interpolated total: %d", countNonNull));
|
||||
// System.out.println(String.format("lower: %d", IntStream.range(0, 16).mapToObj(((IInterpolatedNoiseSampler) interpolated).getLowerInterpolatedNoise()::getOctave).filter(Objects::nonNull).count()));
|
||||
// System.out.println(String.format("upper: %d", IntStream.range(0, 16).mapToObj(((IInterpolatedNoiseSampler) interpolated).getUpperInterpolatedNoise()::getOctave).filter(Objects::nonNull).count()));
|
||||
// System.out.println(String.format("normal: %d", IntStream.range(0, 8).mapToObj(((IInterpolatedNoiseSampler) interpolated).getInterpolationNoise()::getOctave).filter(Objects::nonNull).count()));
|
||||
// }
|
||||
|
||||
final MemorySegment sampler_permutations = arena.allocate(40 * 256L * 4L, 64);
|
||||
final MemorySegment sampler_originX = arena.allocate(40 * 8L, 64);
|
||||
final MemorySegment sampler_originY = arena.allocate(40 * 8L, 64);
|
||||
final MemorySegment sampler_originZ = arena.allocate(40 * 8L, 64);
|
||||
final MemorySegment sampler_mulFactor = arena.allocate(40 * 8L, 64);
|
||||
|
||||
int index = 0;
|
||||
|
||||
{
|
||||
int startIndex = index;
|
||||
|
||||
for (int i = 0; i < 8; i++) {
|
||||
ImprovedNoise sampler = (interpolated.mainNoise).getOctaveNoise(i);
|
||||
if (sampler != null) {
|
||||
MemorySegment.copy(MemorySegment.ofArray(MemoryUtil.byte2int(sampler.p)), 0, sampler_permutations, index * 256L * 4L, 256 * 4);
|
||||
sampler_originX.set(ValueLayout.JAVA_DOUBLE, index * 8L, sampler.xo);
|
||||
sampler_originY.set(ValueLayout.JAVA_DOUBLE, index * 8L, sampler.yo);
|
||||
sampler_originZ.set(ValueLayout.JAVA_DOUBLE, index * 8L, sampler.zo);
|
||||
sampler_mulFactor.set(ValueLayout.JAVA_DOUBLE, index * 8L, Math.pow(2, -i));
|
||||
index ++;
|
||||
}
|
||||
}
|
||||
|
||||
BindingsTemplate.interpolated_noise_sampler$normal$sampler_permutations.set(data, 0L, sampler_permutations.asSlice(startIndex * 256L * 4L));
|
||||
BindingsTemplate.interpolated_noise_sampler$normal$sampler_originX.set(data, 0L, sampler_originX.asSlice(startIndex * 8L));
|
||||
BindingsTemplate.interpolated_noise_sampler$normal$sampler_originY.set(data, 0L, sampler_originY.asSlice(startIndex * 8L));
|
||||
BindingsTemplate.interpolated_noise_sampler$normal$sampler_originZ.set(data, 0L, sampler_originZ.asSlice(startIndex * 8L));
|
||||
BindingsTemplate.interpolated_noise_sampler$normal$sampler_mulFactor.set(data, 0L, sampler_mulFactor.asSlice(startIndex * 8L));
|
||||
BindingsTemplate.interpolated_noise_sampler$normal$length.set(data, 0L, index - startIndex);
|
||||
}
|
||||
|
||||
{
|
||||
int startIndex = index = 8;
|
||||
|
||||
for (int i = 0; i < 16; i++) {
|
||||
ImprovedNoise sampler = (interpolated.minLimitNoise).getOctaveNoise(i);
|
||||
if (sampler != null) {
|
||||
MemorySegment.copy(MemorySegment.ofArray(MemoryUtil.byte2int(sampler.p)), 0, sampler_permutations, index * 256L * 4L, 256 * 4);
|
||||
sampler_originX.set(ValueLayout.JAVA_DOUBLE, index * 8L, sampler.xo);
|
||||
sampler_originY.set(ValueLayout.JAVA_DOUBLE, index * 8L, sampler.yo);
|
||||
sampler_originZ.set(ValueLayout.JAVA_DOUBLE, index * 8L, sampler.zo);
|
||||
sampler_mulFactor.set(ValueLayout.JAVA_DOUBLE, index * 8L, Math.pow(2, -i));
|
||||
index ++;
|
||||
}
|
||||
}
|
||||
|
||||
BindingsTemplate.interpolated_noise_sampler$lower$sampler_permutations.set(data, 0L, sampler_permutations.asSlice(startIndex * 256L * 4L));
|
||||
BindingsTemplate.interpolated_noise_sampler$lower$sampler_originX.set(data, 0L, sampler_originX.asSlice(startIndex * 8L));
|
||||
BindingsTemplate.interpolated_noise_sampler$lower$sampler_originY.set(data, 0L, sampler_originY.asSlice(startIndex * 8L));
|
||||
BindingsTemplate.interpolated_noise_sampler$lower$sampler_originZ.set(data, 0L, sampler_originZ.asSlice(startIndex * 8L));
|
||||
BindingsTemplate.interpolated_noise_sampler$lower$sampler_mulFactor.set(data, 0L, sampler_mulFactor.asSlice(startIndex * 8L));
|
||||
BindingsTemplate.interpolated_noise_sampler$lower$length.set(data, 0L, index - startIndex);
|
||||
}
|
||||
|
||||
{
|
||||
int startIndex = index = 8 + 16;
|
||||
|
||||
for (int i = 0; i < 16; i++) {
|
||||
ImprovedNoise sampler = (interpolated.maxLimitNoise).getOctaveNoise(i);
|
||||
if (sampler != null) {
|
||||
MemorySegment.copy(MemorySegment.ofArray(MemoryUtil.byte2int(sampler.p)), 0, sampler_permutations, index * 256L * 4L, 256 * 4);
|
||||
sampler_originX.set(ValueLayout.JAVA_DOUBLE, index * 8L, sampler.xo);
|
||||
sampler_originY.set(ValueLayout.JAVA_DOUBLE, index * 8L, sampler.yo);
|
||||
sampler_originZ.set(ValueLayout.JAVA_DOUBLE, index * 8L, sampler.zo);
|
||||
sampler_mulFactor.set(ValueLayout.JAVA_DOUBLE, index * 8L, Math.pow(2, -i));
|
||||
index ++;
|
||||
}
|
||||
}
|
||||
|
||||
BindingsTemplate.interpolated_noise_sampler$upper$sampler_permutations.set(data, 0L, sampler_permutations.asSlice(startIndex * 256L * 4L));
|
||||
BindingsTemplate.interpolated_noise_sampler$upper$sampler_originX.set(data, 0L, sampler_originX.asSlice(startIndex * 8L));
|
||||
BindingsTemplate.interpolated_noise_sampler$upper$sampler_originY.set(data, 0L, sampler_originY.asSlice(startIndex * 8L));
|
||||
BindingsTemplate.interpolated_noise_sampler$upper$sampler_originZ.set(data, 0L, sampler_originZ.asSlice(startIndex * 8L));
|
||||
BindingsTemplate.interpolated_noise_sampler$upper$sampler_mulFactor.set(data, 0L, sampler_mulFactor.asSlice(startIndex * 8L));
|
||||
BindingsTemplate.interpolated_noise_sampler$upper$length.set(data, 0L, index - startIndex);
|
||||
}
|
||||
|
||||
VarHandle.fullFence();
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
// c2me_natives_end_islands_sample, float, (const int32_t *const simplex_permutations, const int32_t x, const int32_t z)
|
||||
public static final MethodHandle c2me_natives_end_islands_sample = NativeLoader.linker.downcallHandle(
|
||||
FunctionDescriptor.of(
|
||||
ValueLayout.JAVA_FLOAT,
|
||||
ValueLayout.ADDRESS,
|
||||
ValueLayout.JAVA_INT,
|
||||
ValueLayout.JAVA_INT
|
||||
),
|
||||
Linker.Option.critical(true)
|
||||
);
|
||||
public static final MethodHandle c2me_natives_end_islands_sample_ptr = NativeLoader.linker.downcallHandle(
|
||||
FunctionDescriptor.of(
|
||||
ValueLayout.JAVA_FLOAT,
|
||||
ValueLayout.JAVA_LONG,
|
||||
ValueLayout.JAVA_INT,
|
||||
ValueLayout.JAVA_INT
|
||||
),
|
||||
Linker.Option.critical(false)
|
||||
);
|
||||
|
||||
// c2me_natives_biome_access_sample, uint32_t, (const int64_t theSeed, const int32_t x, const int32_t y, const int32_t z)
|
||||
public static final MethodHandle c2me_natives_biome_access_sample = NativeLoader.linker.downcallHandle(
|
||||
FunctionDescriptor.of(
|
||||
ValueLayout.JAVA_INT,
|
||||
ValueLayout.JAVA_LONG,
|
||||
ValueLayout.JAVA_INT,
|
||||
ValueLayout.JAVA_INT,
|
||||
ValueLayout.JAVA_INT
|
||||
),
|
||||
Linker.Option.critical(false)
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package org.bxteam.divinemc.math;
|
||||
|
||||
import org.bxteam.divinemc.math.isa.ISA_aarch64;
|
||||
import org.bxteam.divinemc.math.isa.ISA_x86_64;
|
||||
|
||||
public interface ISATarget {
|
||||
int ordinal();
|
||||
|
||||
String getSuffix();
|
||||
|
||||
boolean isNativelySupported();
|
||||
|
||||
static Class<? extends Enum<? extends ISATarget>> getInstance() {
|
||||
return switch (NativeLoader.NORMALIZED_ARCH) {
|
||||
case "x86_64" -> ISA_x86_64.class;
|
||||
case "aarch_64" -> ISA_aarch64.class;
|
||||
default -> null;
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,179 @@
|
||||
package org.bxteam.divinemc.math;
|
||||
|
||||
import io.netty.util.internal.SystemPropertyUtil;
|
||||
import org.bxteam.divinemc.DivineConfig;
|
||||
import org.jetbrains.annotations.Contract;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.lang.foreign.*;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.StandardCopyOption;
|
||||
import java.util.Locale;
|
||||
|
||||
public class NativeLoader {
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(NativeLoader.class);
|
||||
|
||||
public static final String NORMALIZED_ARCH = normalizeArch(SystemPropertyUtil.get("os.arch", ""));
|
||||
public static final String NORMALIZED_OS = normalizeOs(SystemPropertyUtil.get("os.name", ""));
|
||||
|
||||
private static final Arena arena = Arena.ofAuto();
|
||||
public static final SymbolLookup lookup;
|
||||
public static final Linker linker = Linker.nativeLinker();
|
||||
public static final ISATarget currentMachineTarget;
|
||||
|
||||
static {
|
||||
String libName = String.format("%s-%s-%s", NORMALIZED_OS, NORMALIZED_ARCH, System.mapLibraryName("c2me-opts-natives-math"));
|
||||
lookup = load0(libName);
|
||||
if (lookup == null) {
|
||||
currentMachineTarget = null;
|
||||
DivineConfig.nativeAccelerationEnabled = false;
|
||||
LOGGER.warn("Disabling native math optimization due to unsupported platform.");
|
||||
} else {
|
||||
try {
|
||||
LOGGER.info("Attempting to call native library. If your game crashes right after this point, native acceleration may not be available for your system.");
|
||||
int level = (int) linker.downcallHandle(
|
||||
lookup.find("c2me_natives_get_system_isa").get(),
|
||||
FunctionDescriptor.of(
|
||||
ValueLayout.JAVA_INT,
|
||||
ValueLayout.JAVA_BOOLEAN
|
||||
)
|
||||
).invokeExact(DivineConfig.allowAVX512);
|
||||
ISATarget target;
|
||||
if (DivineConfig.isaTargetLevelOverride != -1) {
|
||||
target = (ISATarget) ISATarget.getInstance().getEnumConstants()[DivineConfig.isaTargetLevelOverride];
|
||||
} else {
|
||||
target = (ISATarget) ISATarget.getInstance().getEnumConstants()[level];
|
||||
while (!target.isNativelySupported()) target = (ISATarget) ISATarget.getInstance().getEnumConstants()[target.ordinal() - 1];
|
||||
}
|
||||
currentMachineTarget = target;
|
||||
LOGGER.info("Detected maximum supported ISA target: {}", currentMachineTarget);
|
||||
} catch (Throwable e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Contract(pure = true)
|
||||
public static @NotNull String getAvailabilityString() {
|
||||
if (lookup != null) {
|
||||
return String.format("Available, with ISA target %s", currentMachineTarget);
|
||||
} else {
|
||||
return "Unavailable";
|
||||
}
|
||||
}
|
||||
|
||||
private static @Nullable SymbolLookup load0(String libName) {
|
||||
// load from resources
|
||||
try (final InputStream in = NativeLoader.class.getClassLoader().getResourceAsStream(libName)) {
|
||||
if (in == null) {
|
||||
LOGGER.warn("Cannot find native library {}, possibly unsupported platform for native acceleration", libName);
|
||||
return null;
|
||||
}
|
||||
final Path tempFile;
|
||||
if (Boolean.getBoolean("vectorizedgen.preserveNative")) {
|
||||
tempFile = Path.of(".", libName);
|
||||
} else {
|
||||
tempFile = Files.createTempFile(null, libName);
|
||||
tempFile.toFile().deleteOnExit();
|
||||
}
|
||||
Files.copy(in, tempFile, StandardCopyOption.REPLACE_EXISTING);
|
||||
return SymbolLookup.libraryLookup(tempFile, arena);
|
||||
} catch (Throwable e) {
|
||||
LOGGER.warn("Failed to load native library", e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static @NotNull String normalize(@NotNull String value) {
|
||||
return value.toLowerCase(Locale.US).replaceAll("[^a-z0-9]+", "");
|
||||
}
|
||||
|
||||
private static @NotNull String normalizeArch(String value) {
|
||||
value = normalize(value);
|
||||
if (value.matches("^(x8664|amd64|ia32e|em64t|x64)$")) {
|
||||
return "x86_64";
|
||||
}
|
||||
if (value.matches("^(x8632|x86|i[3-6]86|ia32|x32)$")) {
|
||||
return "x86_32";
|
||||
}
|
||||
if (value.matches("^(ia64|itanium64)$")) {
|
||||
return "itanium_64";
|
||||
}
|
||||
if (value.matches("^(sparc|sparc32)$")) {
|
||||
return "sparc_32";
|
||||
}
|
||||
if (value.matches("^(sparcv9|sparc64)$")) {
|
||||
return "sparc_64";
|
||||
}
|
||||
if (value.matches("^(arm|arm32)$")) {
|
||||
return "arm_32";
|
||||
}
|
||||
if ("aarch64".equals(value)) {
|
||||
return "aarch_64";
|
||||
}
|
||||
if (value.matches("^(ppc|ppc32)$")) {
|
||||
return "ppc_32";
|
||||
}
|
||||
if ("ppc64".equals(value)) {
|
||||
return "ppc_64";
|
||||
}
|
||||
if ("ppc64le".equals(value)) {
|
||||
return "ppcle_64";
|
||||
}
|
||||
if ("s390".equals(value)) {
|
||||
return "s390_32";
|
||||
}
|
||||
if ("s390x".equals(value)) {
|
||||
return "s390_64";
|
||||
}
|
||||
if ("loongarch64".equals(value)) {
|
||||
return "loongarch_64";
|
||||
}
|
||||
|
||||
return "unknown";
|
||||
}
|
||||
|
||||
private static @NotNull String normalizeOs(String value) {
|
||||
value = normalize(value);
|
||||
if (value.startsWith("aix")) {
|
||||
return "aix";
|
||||
}
|
||||
if (value.startsWith("hpux")) {
|
||||
return "hpux";
|
||||
}
|
||||
if (value.startsWith("os400")) {
|
||||
// Avoid the names such as os4000
|
||||
if (value.length() <= 5 || !Character.isDigit(value.charAt(5))) {
|
||||
return "os400";
|
||||
}
|
||||
}
|
||||
if (value.startsWith("linux")) {
|
||||
return "linux";
|
||||
}
|
||||
if (value.startsWith("macosx") || value.startsWith("osx") || value.startsWith("darwin")) {
|
||||
return "osx";
|
||||
}
|
||||
if (value.startsWith("freebsd")) {
|
||||
return "freebsd";
|
||||
}
|
||||
if (value.startsWith("openbsd")) {
|
||||
return "openbsd";
|
||||
}
|
||||
if (value.startsWith("netbsd")) {
|
||||
return "netbsd";
|
||||
}
|
||||
if (value.startsWith("solaris") || value.startsWith("sunos")) {
|
||||
return "sunos";
|
||||
}
|
||||
if (value.startsWith("windows")) {
|
||||
return "windows";
|
||||
}
|
||||
|
||||
return "unknown";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package org.bxteam.divinemc.math.isa;
|
||||
|
||||
import org.bxteam.divinemc.math.ISATarget;
|
||||
|
||||
public enum ISA_aarch64 implements ISATarget {
|
||||
GENERIC("_generic", true);
|
||||
|
||||
private final String suffix;
|
||||
private final boolean nativelySupported;
|
||||
|
||||
ISA_aarch64(String suffix, boolean nativelySupported) {
|
||||
this.suffix = suffix;
|
||||
this.nativelySupported = nativelySupported;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSuffix() {
|
||||
return this.suffix;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isNativelySupported() {
|
||||
return this.nativelySupported;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package org.bxteam.divinemc.math.isa;
|
||||
|
||||
import org.bxteam.divinemc.math.ISATarget;
|
||||
|
||||
public enum ISA_x86_64 implements ISATarget {
|
||||
SSE2("_sse2", true), // 0
|
||||
SSE4_1("_sse2", false), // 1, not implemented
|
||||
SSE4_2("_sse4_2", true), // 2
|
||||
AVX("_avx", true), // 3
|
||||
AVX2("_avx2", true), // 4
|
||||
AVX2ADL("_avx2adl", true), // 5
|
||||
AVX512KNL("_avx2", false), // 6, not implemented
|
||||
AVX512SKX("_avx512skx", true), // 7
|
||||
AVX512ICL("_avx512icl", true), // 8
|
||||
AVX512SPR("_avx512spr", true); // 9
|
||||
|
||||
private final String suffix;
|
||||
private final boolean nativelySupported;
|
||||
|
||||
ISA_x86_64(String suffix, boolean nativelySupported) {
|
||||
this.suffix = suffix;
|
||||
this.nativelySupported = nativelySupported;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSuffix() {
|
||||
return this.suffix;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isNativelySupported() {
|
||||
return this.nativelySupported;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package org.bxteam.divinemc.server;
|
||||
|
||||
import ca.spottedleaf.moonrise.common.util.TickThread;
|
||||
import java.util.concurrent.ThreadFactory;
|
||||
|
||||
public class ServerLevelTickExecutorThreadFactory implements ThreadFactory {
|
||||
private final String worldName;
|
||||
|
||||
public ServerLevelTickExecutorThreadFactory(String worldName) {
|
||||
this.worldName = worldName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Thread newThread(Runnable runnable) {
|
||||
TickThread.ServerLevelTickThread tickThread = new TickThread.ServerLevelTickThread(runnable, "serverlevel-tick-worker [" + worldName + "]");
|
||||
|
||||
if (tickThread.isDaemon()) {
|
||||
tickThread.setDaemon(false);
|
||||
}
|
||||
|
||||
if (tickThread.getPriority() != 5) {
|
||||
tickThread.setPriority(5);
|
||||
}
|
||||
|
||||
return tickThread;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,116 @@
|
||||
package org.bxteam.divinemc.server.chunk;
|
||||
|
||||
import ca.spottedleaf.moonrise.common.PlatformHooks;
|
||||
import io.netty.util.internal.PlatformDependent;
|
||||
import net.objecthunter.exp4j.ExpressionBuilder;
|
||||
import net.objecthunter.exp4j.function.Function;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import oshi.util.tuples.Pair;
|
||||
import java.util.function.BiFunction;
|
||||
|
||||
public enum ChunkSystemAlgorithms {
|
||||
MOONRISE((configWorkerThreads, configIoThreads) -> {
|
||||
int defaultWorkerThreads = Runtime.getRuntime().availableProcessors() / 2;
|
||||
if (defaultWorkerThreads <= 4) {
|
||||
defaultWorkerThreads = defaultWorkerThreads <= 3 ? 1 : 2;
|
||||
} else {
|
||||
defaultWorkerThreads = defaultWorkerThreads / 2;
|
||||
}
|
||||
defaultWorkerThreads = Integer.getInteger(PlatformHooks.get().getBrand() + ".WorkerThreadCount", Integer.valueOf(defaultWorkerThreads));
|
||||
|
||||
int workerThreads = configWorkerThreads;
|
||||
|
||||
if (workerThreads <= 0) {
|
||||
workerThreads = defaultWorkerThreads;
|
||||
}
|
||||
final int ioThreads = Math.max(1, configIoThreads);
|
||||
return new Pair<>(workerThreads, ioThreads);
|
||||
}),
|
||||
C2ME_AGGRESSIVE((configWorkerThreads, configIoThreads) -> {
|
||||
String expression = """
|
||||
|
||||
max(
|
||||
1,
|
||||
min(
|
||||
if( is_windows,
|
||||
(cpus / 1.6),
|
||||
(cpus / 1.3)
|
||||
) - if(is_client, 1, 0),
|
||||
( ( mem_gb - (if(is_client, 1.0, 0.5)) ) / 0.6 )
|
||||
)
|
||||
)
|
||||
\040""";
|
||||
int eval = configWorkerThreads <= 0 ? tryEvaluateExpression(expression) : configWorkerThreads;
|
||||
return new Pair<>(eval, Math.max(1, configIoThreads));
|
||||
}),
|
||||
C2ME((configWorkerThreads, configIoThreads) -> {
|
||||
String expression = """
|
||||
|
||||
max(
|
||||
1,
|
||||
min(
|
||||
if( is_windows,
|
||||
(cpus / 1.6 - 2),
|
||||
(cpus / 1.2 - 2)
|
||||
) - if(is_client, 2, 0),
|
||||
if( is_j9vm,
|
||||
( ( mem_gb - (if(is_client, 0.6, 0.2)) ) / 0.4 ),
|
||||
( ( mem_gb - (if(is_client, 1.2, 0.6)) ) / 0.6 )
|
||||
)
|
||||
)
|
||||
)
|
||||
\040""";
|
||||
int eval = configWorkerThreads <= 0 ? tryEvaluateExpression(expression) : configWorkerThreads;
|
||||
return new Pair<>(eval, Math.max(1, configIoThreads));
|
||||
});
|
||||
|
||||
private final BiFunction<Integer, Integer, Pair<Integer, Integer>> eval;
|
||||
|
||||
ChunkSystemAlgorithms(BiFunction<Integer, Integer, Pair<Integer, Integer>> eval) {
|
||||
this.eval = eval;
|
||||
}
|
||||
|
||||
private static int tryEvaluateExpression(String expression) {
|
||||
return (int) Math.max(1,
|
||||
new ExpressionBuilder(expression)
|
||||
.variables("is_windows", "is_j9vm", "is_client", "cpus", "mem_gb")
|
||||
.function(new Function("max", 2) {
|
||||
@Override
|
||||
public double apply(double... args) {
|
||||
return Math.max(args[0], args[1]);
|
||||
}
|
||||
})
|
||||
.function(new Function("min", 2) {
|
||||
@Override
|
||||
public double apply(double... args) {
|
||||
return Math.min(args[0], args[1]);
|
||||
}
|
||||
})
|
||||
.function(new Function("if", 3) {
|
||||
@Override
|
||||
public double apply(double... args) {
|
||||
return args[0] != 0 ? args[1] : args[2];
|
||||
}
|
||||
})
|
||||
.build()
|
||||
.setVariable("is_windows", PlatformDependent.isWindows() ? 1 : 0)
|
||||
.setVariable("is_j9vm", PlatformDependent.isJ9Jvm() ? 1 : 0)
|
||||
.setVariable("is_client", 0)
|
||||
.setVariable("cpus", Runtime.getRuntime().availableProcessors())
|
||||
.setVariable("mem_gb", Runtime.getRuntime().maxMemory() / 1024.0 / 1024.0 / 1024.0)
|
||||
.evaluate()
|
||||
);
|
||||
}
|
||||
|
||||
public int evalWorkers(final int configWorkerThreads, final int configIoThreads) {
|
||||
return eval.apply(configWorkerThreads, configIoThreads).getA();
|
||||
}
|
||||
|
||||
public int evalIO(final int configWorkerThreads, final int configIoThreads) {
|
||||
return eval.apply(configWorkerThreads, configIoThreads).getB();
|
||||
}
|
||||
|
||||
public @NotNull String asDebugString() {
|
||||
return this + "(" + evalWorkers(-1, -1) + ")";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,421 @@
|
||||
package org.bxteam.divinemc.util;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.jetbrains.annotations.Contract;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* Assertion utility class that assists in validating arguments.
|
||||
*
|
||||
* <p>Useful for identifying programmer errors early and clearly at runtime.
|
||||
*
|
||||
* <p>For example, if the contract of a public method states it does not
|
||||
* allow {@code null} arguments, {@code Assert} can be used to validate that
|
||||
* contract. Doing this clearly indicates a contract violation when it
|
||||
* occurs and protects the class's invariants.
|
||||
*
|
||||
* <p>Typically used to validate method arguments rather than configuration
|
||||
* properties, to check for cases that are usually programmer errors rather
|
||||
* than configuration errors. In contrast to configuration initialization
|
||||
* code, there is usually no point in falling back to defaults in such methods.
|
||||
*
|
||||
* <p>This class is similar to JUnit's assertion library. If an argument value is
|
||||
* deemed invalid, an {@link IllegalArgumentException} is thrown (typically).
|
||||
* For example:
|
||||
*
|
||||
* <pre class="code">
|
||||
* Assert.notNull(clazz, "The class must not be null");
|
||||
* Assert.isTrue(i > 0, "The value must be greater than zero");</pre>
|
||||
*
|
||||
* <p>Mainly for internal use within the framework; for a more comprehensive suite
|
||||
* of assertion utilities consider {@code org.apache.commons.lang3.Validate} from
|
||||
* <a href="https://commons.apache.org/proper/commons-lang/">Apache Commons Lang</a>,
|
||||
* Google Guava's
|
||||
* <a href="https://github.com/google/guava/wiki/PreconditionsExplained">Preconditions</a>,
|
||||
* or similar third-party libraries.
|
||||
*
|
||||
* @author Keith Donald
|
||||
* @author Juergen Hoeller
|
||||
* @author Sam Brannen
|
||||
* @author Colin Sampaleanu
|
||||
* @author Rob Harrop
|
||||
* @author Sebastien Deleuze
|
||||
*/
|
||||
public abstract class Assert {
|
||||
/**
|
||||
* Assert a boolean expression, throwing an {@code IllegalStateException}
|
||||
* if the expression evaluates to {@code false}.
|
||||
* <p>Call {@link #isTrue} if you wish to throw an {@code IllegalArgumentException}
|
||||
* on an assertion failure.
|
||||
* <pre class="code">Assert.state(id == null, "The id property must not already be initialized");</pre>
|
||||
* @param expression a boolean expression
|
||||
* @param message the exception message to use if the assertion fails
|
||||
* @throws IllegalStateException if {@code expression} is {@code false}
|
||||
*/
|
||||
@Contract("false, _ -> fail")
|
||||
public static void state(boolean expression, String message) {
|
||||
if (!expression) {
|
||||
throw new IllegalStateException(message);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Assert a boolean expression, throwing an {@code IllegalStateException}
|
||||
* if the expression evaluates to {@code false}.
|
||||
* <p>Call {@link #isTrue} if you wish to throw an {@code IllegalArgumentException}
|
||||
* on an assertion failure.
|
||||
* <pre class="code">
|
||||
* Assert.state(entity.getId() == null,
|
||||
* () -> "ID for entity " + entity.getName() + " must not already be initialized");
|
||||
* </pre>
|
||||
* @param expression a boolean expression
|
||||
* @param messageSupplier a supplier for the exception message to use if the
|
||||
* assertion fails
|
||||
* @throws IllegalStateException if {@code expression} is {@code false}
|
||||
* @since 5.0
|
||||
*/
|
||||
@Contract("false, _ -> fail")
|
||||
public static void state(boolean expression, Supplier<String> messageSupplier) {
|
||||
if (!expression) {
|
||||
throw new IllegalStateException(nullSafeGet(messageSupplier));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Assert a boolean expression, throwing an {@code IllegalArgumentException}
|
||||
* if the expression evaluates to {@code false}.
|
||||
* <pre class="code">Assert.isTrue(i > 0, "The value must be greater than zero");</pre>
|
||||
* @param expression a boolean expression
|
||||
* @param message the exception message to use if the assertion fails
|
||||
* @throws IllegalArgumentException if {@code expression} is {@code false}
|
||||
*/
|
||||
@Contract("false, _ -> fail")
|
||||
public static void isTrue(boolean expression, String message) {
|
||||
if (!expression) {
|
||||
throw new IllegalArgumentException(message);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Assert a boolean expression, throwing an {@code IllegalArgumentException}
|
||||
* if the expression evaluates to {@code false}.
|
||||
* <pre class="code">
|
||||
* Assert.isTrue(i > 0, () -> "The value '" + i + "' must be greater than zero");
|
||||
* </pre>
|
||||
* @param expression a boolean expression
|
||||
* @param messageSupplier a supplier for the exception message to use if the
|
||||
* assertion fails
|
||||
* @throws IllegalArgumentException if {@code expression} is {@code false}
|
||||
* @since 5.0
|
||||
*/
|
||||
@Contract("false, _ -> fail")
|
||||
public static void isTrue(boolean expression, Supplier<String> messageSupplier) {
|
||||
if (!expression) {
|
||||
throw new IllegalArgumentException(nullSafeGet(messageSupplier));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Assert that an object is {@code null}.
|
||||
* <pre class="code">Assert.isNull(value, "The value must be null");</pre>
|
||||
* @param object the object to check
|
||||
* @param message the exception message to use if the assertion fails
|
||||
* @throws IllegalArgumentException if the object is not {@code null}
|
||||
*/
|
||||
@Contract("!null, _ -> fail")
|
||||
public static void isNull(@Nullable Object object, String message) {
|
||||
if (object != null) {
|
||||
throw new IllegalArgumentException(message);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Assert that an object is {@code null}.
|
||||
* <pre class="code">
|
||||
* Assert.isNull(value, () -> "The value '" + value + "' must be null");
|
||||
* </pre>
|
||||
* @param object the object to check
|
||||
* @param messageSupplier a supplier for the exception message to use if the
|
||||
* assertion fails
|
||||
* @throws IllegalArgumentException if the object is not {@code null}
|
||||
* @since 5.0
|
||||
*/
|
||||
@Contract("!null, _ -> fail")
|
||||
public static void isNull(@Nullable Object object, Supplier<String> messageSupplier) {
|
||||
if (object != null) {
|
||||
throw new IllegalArgumentException(nullSafeGet(messageSupplier));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Assert that an object is not {@code null}.
|
||||
* <pre class="code">Assert.notNull(clazz, "The class must not be null");</pre>
|
||||
* @param object the object to check
|
||||
* @param message the exception message to use if the assertion fails
|
||||
* @throws IllegalArgumentException if the object is {@code null}
|
||||
*/
|
||||
@Contract("null, _ -> fail")
|
||||
public static void notNull(@Nullable Object object, String message) {
|
||||
if (object == null) {
|
||||
throw new IllegalArgumentException(message);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Assert that an object is not {@code null}.
|
||||
* <pre class="code">
|
||||
* Assert.notNull(entity.getId(),
|
||||
* () -> "ID for entity " + entity.getName() + " must not be null");
|
||||
* </pre>
|
||||
* @param object the object to check
|
||||
* @param messageSupplier a supplier for the exception message to use if the
|
||||
* assertion fails
|
||||
* @throws IllegalArgumentException if the object is {@code null}
|
||||
* @since 5.0
|
||||
*/
|
||||
@Contract("null, _ -> fail")
|
||||
public static void notNull(@Nullable Object object, Supplier<String> messageSupplier) {
|
||||
if (object == null) {
|
||||
throw new IllegalArgumentException(nullSafeGet(messageSupplier));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Assert that an array contains no {@code null} elements.
|
||||
* <p>Note: Does not complain if the array is empty!
|
||||
* <pre class="code">Assert.noNullElements(array, "The array must contain non-null elements");</pre>
|
||||
* @param array the array to check
|
||||
* @param message the exception message to use if the assertion fails
|
||||
* @throws IllegalArgumentException if the object array contains a {@code null} element
|
||||
*/
|
||||
public static void noNullElements(Object @Nullable [] array, String message) {
|
||||
if (array != null) {
|
||||
for (Object element : array) {
|
||||
if (element == null) {
|
||||
throw new IllegalArgumentException(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Assert that an array contains no {@code null} elements.
|
||||
* <p>Note: Does not complain if the array is empty!
|
||||
* <pre class="code">
|
||||
* Assert.noNullElements(array, () -> "The " + arrayType + " array must contain non-null elements");
|
||||
* </pre>
|
||||
* @param array the array to check
|
||||
* @param messageSupplier a supplier for the exception message to use if the
|
||||
* assertion fails
|
||||
* @throws IllegalArgumentException if the object array contains a {@code null} element
|
||||
* @since 5.0
|
||||
*/
|
||||
public static void noNullElements(Object @Nullable [] array, Supplier<String> messageSupplier) {
|
||||
if (array != null) {
|
||||
for (Object element : array) {
|
||||
if (element == null) {
|
||||
throw new IllegalArgumentException(nullSafeGet(messageSupplier));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Assert that a collection contains no {@code null} elements.
|
||||
* <p>Note: Does not complain if the collection is empty!
|
||||
* <pre class="code">Assert.noNullElements(collection, "Collection must contain non-null elements");</pre>
|
||||
* @param collection the collection to check
|
||||
* @param message the exception message to use if the assertion fails
|
||||
* @throws IllegalArgumentException if the collection contains a {@code null} element
|
||||
* @since 5.2
|
||||
*/
|
||||
public static void noNullElements(@Nullable Collection<?> collection, String message) {
|
||||
if (collection != null) {
|
||||
for (Object element : collection) {
|
||||
if (element == null) {
|
||||
throw new IllegalArgumentException(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Assert that a collection contains no {@code null} elements.
|
||||
* <p>Note: Does not complain if the collection is empty!
|
||||
* <pre class="code">
|
||||
* Assert.noNullElements(collection, () -> "Collection " + collectionName + " must contain non-null elements");
|
||||
* </pre>
|
||||
* @param collection the collection to check
|
||||
* @param messageSupplier a supplier for the exception message to use if the
|
||||
* assertion fails
|
||||
* @throws IllegalArgumentException if the collection contains a {@code null} element
|
||||
* @since 5.2
|
||||
*/
|
||||
public static void noNullElements(@Nullable Collection<?> collection, Supplier<String> messageSupplier) {
|
||||
if (collection != null) {
|
||||
for (Object element : collection) {
|
||||
if (element == null) {
|
||||
throw new IllegalArgumentException(nullSafeGet(messageSupplier));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Assert that the provided object is an instance of the provided class.
|
||||
* <pre class="code">Assert.instanceOf(Foo.class, foo, "Foo expected");</pre>
|
||||
* @param type the type to check against
|
||||
* @param obj the object to check
|
||||
* @param message a message which will be prepended to provide further context.
|
||||
* If it is empty or ends in ":" or ";" or "," or ".", a full exception message
|
||||
* will be appended. If it ends in a space, the name of the offending object's
|
||||
* type will be appended. In any other case, a ":" with a space and the name
|
||||
* of the offending object's type will be appended.
|
||||
* @throws IllegalArgumentException if the object is not an instance of type
|
||||
*/
|
||||
@Contract("_, null, _ -> fail")
|
||||
public static void isInstanceOf(Class<?> type, @Nullable Object obj, String message) {
|
||||
notNull(type, "Type to check against must not be null");
|
||||
if (!type.isInstance(obj)) {
|
||||
instanceCheckFailed(type, obj, message);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Assert that the provided object is an instance of the provided class.
|
||||
* <pre class="code">
|
||||
* Assert.instanceOf(Foo.class, foo, () -> "Processing " + Foo.class.getSimpleName() + ":");
|
||||
* </pre>
|
||||
* @param type the type to check against
|
||||
* @param obj the object to check
|
||||
* @param messageSupplier a supplier for the exception message to use if the
|
||||
* assertion fails. See {@link #isInstanceOf(Class, Object, String)} for details.
|
||||
* @throws IllegalArgumentException if the object is not an instance of type
|
||||
* @since 5.0
|
||||
*/
|
||||
@Contract("_, null, _ -> fail")
|
||||
public static void isInstanceOf(Class<?> type, @Nullable Object obj, Supplier<String> messageSupplier) {
|
||||
notNull(type, "Type to check against must not be null");
|
||||
if (!type.isInstance(obj)) {
|
||||
instanceCheckFailed(type, obj, nullSafeGet(messageSupplier));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Assert that the provided object is an instance of the provided class.
|
||||
* <pre class="code">Assert.instanceOf(Foo.class, foo);</pre>
|
||||
* @param type the type to check against
|
||||
* @param obj the object to check
|
||||
* @throws IllegalArgumentException if the object is not an instance of type
|
||||
*/
|
||||
@Contract("_, null -> fail")
|
||||
public static void isInstanceOf(Class<?> type, @Nullable Object obj) {
|
||||
isInstanceOf(type, obj, "");
|
||||
}
|
||||
|
||||
/**
|
||||
* Assert that {@code superType.isAssignableFrom(subType)} is {@code true}.
|
||||
* <pre class="code">Assert.isAssignable(Number.class, myClass, "Number expected");</pre>
|
||||
* @param superType the supertype to check against
|
||||
* @param subType the subtype to check
|
||||
* @param message a message which will be prepended to provide further context.
|
||||
* If it is empty or ends in ":" or ";" or "," or ".", a full exception message
|
||||
* will be appended. If it ends in a space, the name of the offending subtype
|
||||
* will be appended. In any other case, a ":" with a space and the name of the
|
||||
* offending subtype will be appended.
|
||||
* @throws IllegalArgumentException if the classes are not assignable
|
||||
*/
|
||||
@Contract("_, null, _ -> fail")
|
||||
public static void isAssignable(Class<?> superType, @Nullable Class<?> subType, String message) {
|
||||
notNull(superType, "Supertype to check against must not be null");
|
||||
if (subType == null || !superType.isAssignableFrom(subType)) {
|
||||
assignableCheckFailed(superType, subType, message);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Assert that {@code superType.isAssignableFrom(subType)} is {@code true}.
|
||||
* <pre class="code">
|
||||
* Assert.isAssignable(Number.class, myClass, () -> "Processing " + myAttributeName + ":");
|
||||
* </pre>
|
||||
* @param superType the supertype to check against
|
||||
* @param subType the subtype to check
|
||||
* @param messageSupplier a supplier for the exception message to use if the
|
||||
* assertion fails. See {@link #isAssignable(Class, Class, String)} for details.
|
||||
* @throws IllegalArgumentException if the classes are not assignable
|
||||
* @since 5.0
|
||||
*/
|
||||
@Contract("_, null, _ -> fail")
|
||||
public static void isAssignable(Class<?> superType, @Nullable Class<?> subType, Supplier<String> messageSupplier) {
|
||||
notNull(superType, "Supertype to check against must not be null");
|
||||
if (subType == null || !superType.isAssignableFrom(subType)) {
|
||||
assignableCheckFailed(superType, subType, nullSafeGet(messageSupplier));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Assert that {@code superType.isAssignableFrom(subType)} is {@code true}.
|
||||
* <pre class="code">Assert.isAssignable(Number.class, myClass);</pre>
|
||||
* @param superType the supertype to check
|
||||
* @param subType the subtype to check
|
||||
* @throws IllegalArgumentException if the classes are not assignable
|
||||
*/
|
||||
@Contract("_, null -> fail")
|
||||
public static void isAssignable(Class<?> superType, @Nullable Class<?> subType) {
|
||||
isAssignable(superType, subType, "");
|
||||
}
|
||||
|
||||
|
||||
private static void instanceCheckFailed(Class<?> type, @Nullable Object obj, @Nullable String msg) {
|
||||
String className = (obj != null ? obj.getClass().getName() : "null");
|
||||
String result = "";
|
||||
boolean defaultMessage = true;
|
||||
if (StringUtil.hasLength(msg)) {
|
||||
if (endsWithSeparator(msg)) {
|
||||
result = msg + " ";
|
||||
}
|
||||
else {
|
||||
result = messageWithTypeName(msg, className);
|
||||
defaultMessage = false;
|
||||
}
|
||||
}
|
||||
if (defaultMessage) {
|
||||
result = result + ("Object of class [" + className + "] must be an instance of " + type);
|
||||
}
|
||||
throw new IllegalArgumentException(result);
|
||||
}
|
||||
|
||||
private static void assignableCheckFailed(Class<?> superType, @Nullable Class<?> subType, @Nullable String msg) {
|
||||
String result = "";
|
||||
boolean defaultMessage = true;
|
||||
if (StringUtil.hasLength(msg)) {
|
||||
if (endsWithSeparator(msg)) {
|
||||
result = msg + " ";
|
||||
}
|
||||
else {
|
||||
result = messageWithTypeName(msg, subType);
|
||||
defaultMessage = false;
|
||||
}
|
||||
}
|
||||
if (defaultMessage) {
|
||||
result = result + (subType + " is not assignable to " + superType);
|
||||
}
|
||||
throw new IllegalArgumentException(result);
|
||||
}
|
||||
|
||||
private static boolean endsWithSeparator(@NotNull String msg) {
|
||||
return (msg.endsWith(":") || msg.endsWith(";") || msg.endsWith(",") || msg.endsWith("."));
|
||||
}
|
||||
|
||||
@Contract(pure = true)
|
||||
private static @NotNull String messageWithTypeName(@NotNull String msg, @Nullable Object typeName) {
|
||||
return msg + (msg.endsWith(" ") ? "" : ": ") + typeName;
|
||||
}
|
||||
|
||||
private static @Nullable String nullSafeGet(@Nullable Supplier<String> messageSupplier) {
|
||||
return (messageSupplier != null ? messageSupplier.get() : null);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package org.bxteam.divinemc.util;
|
||||
|
||||
public final class Assertions {
|
||||
public static void assertTrue(boolean value, String message) {
|
||||
if (!value) {
|
||||
final AssertionError error = new AssertionError(message);
|
||||
error.printStackTrace();
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
public static void assertTrue(boolean state, String format, Object... args) {
|
||||
if (!state) {
|
||||
final AssertionError error = new AssertionError(String.format(format, args));
|
||||
error.printStackTrace();
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
public static void assertTrue(boolean value) {
|
||||
if (!value) {
|
||||
final AssertionError error = new AssertionError();
|
||||
error.printStackTrace();
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
package org.bxteam.divinemc.util;
|
||||
|
||||
import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
|
||||
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
|
||||
import net.minecraft.world.level.block.entity.TickingBlockEntity;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
|
||||
/**
|
||||
* A list for ServerLevel's blockEntityTickers
|
||||
* <p>
|
||||
* This list is behaving identically to ObjectArrayList, but it has an additional method, `removeAllByIndex`, that allows a list of integers to be passed indicating what
|
||||
* indexes should be deleted from the list
|
||||
* <p>
|
||||
* This is faster than using removeAll, since we don't need to compare the identity of each block entity, and faster than looping thru each index manually and deleting with remove,
|
||||
* since we don't need to resize the array every single remove.
|
||||
*/
|
||||
public final class BlockEntityTickersList extends ObjectArrayList<TickingBlockEntity> {
|
||||
private final IntOpenHashSet toRemove = new IntOpenHashSet();
|
||||
private int startSearchFromIndex = -1;
|
||||
|
||||
/** Creates a new array list with {@link #DEFAULT_INITIAL_CAPACITY} capacity. */
|
||||
public BlockEntityTickersList() {
|
||||
super();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new array list and fills it with a given collection.
|
||||
*
|
||||
* @param c a collection that will be used to fill the array list.
|
||||
*/
|
||||
public BlockEntityTickersList(final Collection<? extends TickingBlockEntity> c) {
|
||||
super(c);
|
||||
}
|
||||
|
||||
/**
|
||||
* Marks an entry as removed
|
||||
*
|
||||
* @param index the index of the item on the list to be marked as removed
|
||||
*/
|
||||
public void markAsRemoved(final int index) {
|
||||
// The block entities list always loop starting from 0, so we only need to check if the startSearchFromIndex is -1 and that's it
|
||||
if (this.startSearchFromIndex == -1)
|
||||
this.startSearchFromIndex = index;
|
||||
this.toRemove.add(index);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes elements that have been marked as removed.
|
||||
*/
|
||||
public void removeMarkedEntries() {
|
||||
if (this.startSearchFromIndex == -1) // No entries in the list, skip
|
||||
return;
|
||||
|
||||
removeAllByIndex(startSearchFromIndex, toRemove);
|
||||
toRemove.clear();
|
||||
this.startSearchFromIndex = -1; // Reset the start search index
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes elements by their index.
|
||||
*/
|
||||
private void removeAllByIndex(final int startSearchFromIndex, final IntOpenHashSet c) { // can't use Set<Integer> because we want to avoid autoboxing when using contains
|
||||
final int requiredMatches = c.size();
|
||||
if (requiredMatches == 0)
|
||||
return; // exit early, we don't need to do anything
|
||||
|
||||
final Object[] a = this.a;
|
||||
int j = startSearchFromIndex;
|
||||
int matches = 0;
|
||||
for (int i = startSearchFromIndex; i < size; i++) { // If the user knows the first index to be removed, we can skip a lot of unnecessary comparsions
|
||||
if (!c.contains(i)) {
|
||||
// TODO: It can be possible to optimize this loop by tracking the start/finish and then using arraycopy to "skip" the elements,
|
||||
// this would optimize cases where the index to be removed are far apart, HOWEVER it does have a big performance impact if you are doing
|
||||
// "arraycopy" for each element
|
||||
a[j++] = a[i];
|
||||
} else {
|
||||
matches++;
|
||||
}
|
||||
|
||||
if (matches == requiredMatches) { // Exit the loop if we already removed everything, we don't need to check anything else
|
||||
// We need to update the final size here, because we know that we already found everything!
|
||||
// Because we know that the size must be currentSize - requiredMatches (because we have matched everything), let's update the value
|
||||
// However, we need to copy the rest of the stuff over
|
||||
if (i != (size - 1)) { // If it isn't the last index...
|
||||
// i + 1 because we want to copy the *next* element over
|
||||
// and the size - i - 1 is because we want to get the current size, minus the current index (which is i), and then - 1 because we want to copy -1 ahead (remember, we are adding +1 to copy the *next* element)
|
||||
System.arraycopy(a, i + 1, a, j, size - i - 1);
|
||||
}
|
||||
j = size - requiredMatches;
|
||||
break;
|
||||
}
|
||||
}
|
||||
Arrays.fill(a, j, size, null);
|
||||
size = j;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
package org.bxteam.divinemc.util;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
public final class Files {
|
||||
public static void deleteRecursively(File dir) throws IOException {
|
||||
if (dir == null || !dir.isDirectory()) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
File[] files = dir.listFiles();
|
||||
if (files == null) {
|
||||
throw new IOException("Error enumerating directory during recursive delete operation: " + dir.getAbsolutePath());
|
||||
}
|
||||
|
||||
for (File child : files) {
|
||||
if (child.isDirectory()) {
|
||||
Files.deleteRecursively(child);
|
||||
} else if (child.isFile()) {
|
||||
if (!child.delete()) {
|
||||
throw new IOException("Error deleting file during recursive delete operation: " + child.getAbsolutePath());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!dir.delete()) {
|
||||
throw new IOException("Error deleting directory during recursive delete operation: " + dir.getAbsolutePath());
|
||||
}
|
||||
} catch (SecurityException ex) {
|
||||
throw new IOException("Security error during recursive delete operation", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package org.bxteam.divinemc.util;
|
||||
|
||||
public final class MemoryUtil {
|
||||
public static int[] byte2int(byte[] data) {
|
||||
if (data == null) return null;
|
||||
int[] ints = new int[data.length];
|
||||
for (int i = 0; i < data.length; i++) {
|
||||
ints[i] = data[i] & 0xff;
|
||||
}
|
||||
return ints;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
package org.bxteam.divinemc.util;
|
||||
|
||||
import org.jetbrains.annotations.Contract;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import java.util.Arrays;
|
||||
|
||||
public class ObjectUtil {
|
||||
@Contract("null, null -> true; null, _ -> false; _, null -> false")
|
||||
public static boolean nullSafeEquals(@Nullable Object o1, @Nullable Object o2) {
|
||||
if (o1 == o2) {
|
||||
return true;
|
||||
}
|
||||
if (o1 == null || o2 == null) {
|
||||
return false;
|
||||
}
|
||||
if (o1.equals(o2)) {
|
||||
return true;
|
||||
}
|
||||
if (o1.getClass().isArray() && o2.getClass().isArray()) {
|
||||
return arrayEquals(o1, o2);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compare the given arrays with {@code Arrays.equals}, performing an equality
|
||||
* check based on the array elements rather than the array reference.
|
||||
* @param o1 first array to compare
|
||||
* @param o2 second array to compare
|
||||
* @return whether the given objects are equal
|
||||
* @see #nullSafeEquals(Object, Object)
|
||||
* @see java.util.Arrays#equals
|
||||
*/
|
||||
private static boolean arrayEquals(Object o1, Object o2) {
|
||||
if (o1 instanceof Object[] objects1 && o2 instanceof Object[] objects2) {
|
||||
return Arrays.equals(objects1, objects2);
|
||||
}
|
||||
if (o1 instanceof boolean[] booleans1 && o2 instanceof boolean[] booleans2) {
|
||||
return Arrays.equals(booleans1, booleans2);
|
||||
}
|
||||
if (o1 instanceof byte[] bytes1 && o2 instanceof byte[] bytes2) {
|
||||
return Arrays.equals(bytes1, bytes2);
|
||||
}
|
||||
if (o1 instanceof char[] chars1 && o2 instanceof char[] chars2) {
|
||||
return Arrays.equals(chars1, chars2);
|
||||
}
|
||||
if (o1 instanceof double[] doubles1 && o2 instanceof double[] doubles2) {
|
||||
return Arrays.equals(doubles1, doubles2);
|
||||
}
|
||||
if (o1 instanceof float[] floats1 && o2 instanceof float[] floats2) {
|
||||
return Arrays.equals(floats1, floats2);
|
||||
}
|
||||
if (o1 instanceof int[] ints1 && o2 instanceof int[] ints2) {
|
||||
return Arrays.equals(ints1, ints2);
|
||||
}
|
||||
if (o1 instanceof long[] longs1 && o2 instanceof long[] longs2) {
|
||||
return Arrays.equals(longs1, longs2);
|
||||
}
|
||||
if (o1 instanceof short[] shorts1 && o2 instanceof short[] shorts2) {
|
||||
return Arrays.equals(shorts1, shorts2);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a hash code for the given object; typically the value of
|
||||
* {@code Object#hashCode()}}. If the object is an array,
|
||||
* this method will delegate to any of the {@code Arrays.hashCode}
|
||||
* methods. If the object is {@code null}, this method returns 0.
|
||||
* @see Object#hashCode()
|
||||
* @see Arrays
|
||||
*/
|
||||
public static int nullSafeHashCode(@Nullable Object obj) {
|
||||
if (obj == null) {
|
||||
return 0;
|
||||
}
|
||||
if (obj.getClass().isArray()) {
|
||||
if (obj instanceof Object[] objects) {
|
||||
return Arrays.hashCode(objects);
|
||||
}
|
||||
if (obj instanceof boolean[] booleans) {
|
||||
return Arrays.hashCode(booleans);
|
||||
}
|
||||
if (obj instanceof byte[] bytes) {
|
||||
return Arrays.hashCode(bytes);
|
||||
}
|
||||
if (obj instanceof char[] chars) {
|
||||
return Arrays.hashCode(chars);
|
||||
}
|
||||
if (obj instanceof double[] doubles) {
|
||||
return Arrays.hashCode(doubles);
|
||||
}
|
||||
if (obj instanceof float[] floats) {
|
||||
return Arrays.hashCode(floats);
|
||||
}
|
||||
if (obj instanceof int[] ints) {
|
||||
return Arrays.hashCode(ints);
|
||||
}
|
||||
if (obj instanceof long[] longs) {
|
||||
return Arrays.hashCode(longs);
|
||||
}
|
||||
if (obj instanceof short[] shorts) {
|
||||
return Arrays.hashCode(shorts);
|
||||
}
|
||||
}
|
||||
return obj.hashCode();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
package org.bxteam.divinemc.util;
|
||||
|
||||
import net.minecraft.util.Mth;
|
||||
import net.minecraft.util.RandomSource;
|
||||
import net.minecraft.world.level.levelgen.LegacyRandomSource;
|
||||
import net.minecraft.world.level.levelgen.PositionalRandomFactory;
|
||||
import net.minecraft.world.level.levelgen.SingleThreadedRandomSource;
|
||||
import net.minecraft.world.level.levelgen.Xoroshiro128PlusPlus;
|
||||
import net.minecraft.world.level.levelgen.XoroshiroRandomSource;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public final class RandomUtil {
|
||||
public static @NotNull RandomSource getRandom(PositionalRandomFactory deriver) {
|
||||
if (deriver instanceof XoroshiroRandomSource.XoroshiroPositionalRandomFactory) {
|
||||
return new XoroshiroRandomSource(0L, 0L);
|
||||
}
|
||||
if (deriver instanceof LegacyRandomSource.LegacyPositionalRandomFactory) {
|
||||
return new SingleThreadedRandomSource(0L);
|
||||
}
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
|
||||
private static final ThreadLocal<XoroshiroRandomSource> xoroshiro = ThreadLocal.withInitial(() -> new XoroshiroRandomSource(0L, 0L));
|
||||
private static final ThreadLocal<SingleThreadedRandomSource> simple = ThreadLocal.withInitial(() -> new SingleThreadedRandomSource(0L));
|
||||
|
||||
public static void derive(PositionalRandomFactory deriver, RandomSource random, int x, int y, int z) {
|
||||
if (deriver instanceof final XoroshiroRandomSource.XoroshiroPositionalRandomFactory deriver1) {
|
||||
final Xoroshiro128PlusPlus implementation = ((XoroshiroRandomSource) random).randomNumberGenerator;
|
||||
implementation.seedLo = (Mth.getSeed(x, y, z) ^ deriver1.seedLo());
|
||||
implementation.seedHi = (deriver1.seedHi());
|
||||
return;
|
||||
}
|
||||
if (deriver instanceof LegacyRandomSource.LegacyPositionalRandomFactory(long seed)) {
|
||||
final SingleThreadedRandomSource random1 = (SingleThreadedRandomSource) random;
|
||||
random1.setSeed(Mth.getSeed(x, y, z) ^ seed);
|
||||
return;
|
||||
}
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package org.bxteam.divinemc.util;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public final class StringUtil {
|
||||
public static boolean hasLength(@Nullable String str) {
|
||||
return (str != null && !str.isEmpty());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,217 @@
|
||||
package org.bxteam.divinemc.util.collections;
|
||||
|
||||
import it.unimi.dsi.fastutil.bytes.ByteBytePair;
|
||||
import it.unimi.dsi.fastutil.ints.IntArrayList;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.util.RandomSource;
|
||||
import net.minecraft.world.entity.ai.behavior.LongJumpToRandomPos;
|
||||
import java.util.AbstractList;
|
||||
import java.util.Arrays;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
public class LongJumpChoiceList extends AbstractList<LongJumpToRandomPos.PossibleJump> {
|
||||
/**
|
||||
* A cache of choice lists for different ranges. The elements must not be mutated, but copied instead.
|
||||
* In vanilla minecraft there should be two elements, one for frog jumps and one for goat jumps.
|
||||
*/
|
||||
private static final ConcurrentHashMap<ByteBytePair, LongJumpChoiceList> CHOICE_LISTS = new ConcurrentHashMap<>();
|
||||
/**
|
||||
* The choice list for frog jumps. Skipping the hash map access. Must not be mutated, but copied instead.
|
||||
*/
|
||||
private static final LongJumpChoiceList FROG_JUMP = new LongJumpChoiceList((byte) 4, (byte) 2);
|
||||
/**
|
||||
* The choice list for goat jumps. Skipping the hash map access. Must not be mutated, but copied instead.
|
||||
*/
|
||||
private static final LongJumpChoiceList GOAT_JUMP = new LongJumpChoiceList((byte) 5, (byte) 5);
|
||||
|
||||
|
||||
private final BlockPos origin;
|
||||
private final IntArrayList[] packedOffsetsByDistanceSq;
|
||||
private final int[] weightByDistanceSq;
|
||||
private int totalWeight;
|
||||
|
||||
/**
|
||||
* Constructs a new LongJumpChoiceList with the given horizontal and vertical range.
|
||||
* We avoid creating too many objects here, e.g. LongJumpTask.Target is not created yet.
|
||||
* @param horizontalRange the horizontal range
|
||||
* @param verticalRange the vertical range
|
||||
*/
|
||||
public LongJumpChoiceList(byte horizontalRange, byte verticalRange) {
|
||||
if (horizontalRange < 0 || verticalRange < 0) {
|
||||
throw new IllegalArgumentException("The ranges must be within 0..127!");
|
||||
}
|
||||
|
||||
this.origin = BlockPos.ZERO;
|
||||
int maxSqDistance = horizontalRange*horizontalRange * 2 + verticalRange*verticalRange;
|
||||
this.packedOffsetsByDistanceSq = new IntArrayList[maxSqDistance];
|
||||
this.weightByDistanceSq = new int[maxSqDistance];
|
||||
|
||||
for (int x = -horizontalRange; x <= horizontalRange; x++) {
|
||||
for (int y = -verticalRange; y <= verticalRange; y++) {
|
||||
for (int z = -horizontalRange; z <= horizontalRange; z++) {
|
||||
int squaredDistance = x * x + y * y + z * z;
|
||||
int index = squaredDistance - 1;
|
||||
if (index >= 0) { //exclude origin (distance 0)
|
||||
int packedOffset = this.packOffset(x, y, z);
|
||||
IntArrayList offsets = this.packedOffsetsByDistanceSq[index];
|
||||
if (offsets == null) {
|
||||
this.packedOffsetsByDistanceSq[index] = offsets = new IntArrayList();
|
||||
}
|
||||
offsets.add(packedOffset);
|
||||
this.weightByDistanceSq[index] += squaredDistance;
|
||||
this.totalWeight += squaredDistance;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public LongJumpChoiceList(BlockPos origin, IntArrayList[] packedOffsetsByDistanceSq, int[] weightByDistanceSq, int totalWeight) {
|
||||
this.origin = origin;
|
||||
this.packedOffsetsByDistanceSq = packedOffsetsByDistanceSq;
|
||||
this.weightByDistanceSq = weightByDistanceSq;
|
||||
this.totalWeight = totalWeight;
|
||||
}
|
||||
|
||||
private int packOffset(int x, int y, int z) {
|
||||
return (x + 128) | ((y + 128) << 8) | ((z + 128) << 16);
|
||||
}
|
||||
|
||||
private int unpackX(int packedOffset) {
|
||||
return (packedOffset & 0xFF) - 128;
|
||||
}
|
||||
|
||||
private int unpackY(int packedOffset) {
|
||||
return ((packedOffset >>> 8) & 0xFF) - 128;
|
||||
}
|
||||
|
||||
private int unpackZ(int packedOffset) {
|
||||
return ((packedOffset >>> 16) & 0xFF) - 128;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a LongJumpChoiceList for the given center position and ranges.
|
||||
* Quickly creates the list by copying an existing, memoized list.
|
||||
* @param centerPos the center position
|
||||
* @param horizontalRange the horizontal range
|
||||
* @param verticalRange the vertical range
|
||||
* @return a LongJumpChoiceList for the given parameters
|
||||
*/
|
||||
public static LongJumpChoiceList forCenter(BlockPos centerPos, byte horizontalRange, byte verticalRange) {
|
||||
if (horizontalRange < 0 || verticalRange < 0) {
|
||||
throw new IllegalArgumentException("The ranges must be within 0..127!");
|
||||
}
|
||||
|
||||
LongJumpChoiceList jumpDestinationsList;
|
||||
short range = (short) ((horizontalRange << 8) | verticalRange);
|
||||
if (range == ((4 << 8) | 2)) {
|
||||
//Frog jump
|
||||
jumpDestinationsList = LongJumpChoiceList.FROG_JUMP;
|
||||
} else if (range == ((5 << 8) | 5)) {
|
||||
//Goat jump
|
||||
jumpDestinationsList = LongJumpChoiceList.GOAT_JUMP;
|
||||
} else {
|
||||
jumpDestinationsList = LongJumpChoiceList.CHOICE_LISTS.computeIfAbsent(
|
||||
ByteBytePair.of(horizontalRange, verticalRange),
|
||||
key -> new LongJumpChoiceList(key.leftByte(), key.rightByte())
|
||||
);
|
||||
}
|
||||
|
||||
return jumpDestinationsList.offsetCopy(centerPos);
|
||||
}
|
||||
|
||||
private LongJumpChoiceList offsetCopy(BlockPos offset) {
|
||||
IntArrayList[] packedOffsetsByDistanceSq = new IntArrayList[this.packedOffsetsByDistanceSq.length];
|
||||
for (int i = 0; i < packedOffsetsByDistanceSq.length; i++) {
|
||||
IntArrayList packedOffsets = this.packedOffsetsByDistanceSq[i];
|
||||
if (packedOffsets != null) {
|
||||
packedOffsetsByDistanceSq[i] = packedOffsets.clone();
|
||||
}
|
||||
}
|
||||
|
||||
return new LongJumpChoiceList(
|
||||
this.origin.offset(offset),
|
||||
packedOffsetsByDistanceSq,
|
||||
Arrays.copyOf(this.weightByDistanceSq, this.weightByDistanceSq.length), this.totalWeight);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes and returns a random target from the list, weighted by squared distance.
|
||||
* @param random the random number generator
|
||||
* @return a random target
|
||||
*/
|
||||
public LongJumpToRandomPos.PossibleJump removeRandomWeightedByDistanceSq(RandomSource random) {
|
||||
int targetWeight = random.nextInt(this.totalWeight);
|
||||
for (int index = 0; targetWeight >= 0 && index < this.weightByDistanceSq.length; index++) {
|
||||
targetWeight -= this.weightByDistanceSq[index];
|
||||
if (targetWeight < 0) {
|
||||
int distanceSq = index + 1;
|
||||
IntArrayList elementsOfDistance = this.packedOffsetsByDistanceSq[index];
|
||||
int elementIndex = random.nextInt(elementsOfDistance.size());
|
||||
|
||||
//fast remove by swapping to end and removing, order does not matter
|
||||
elementsOfDistance.set(elementIndex, elementsOfDistance.set(elementsOfDistance.size() - 1, elementsOfDistance.getInt(elementIndex)));
|
||||
int packedOffset = elementsOfDistance.removeInt(elementsOfDistance.size() - 1);
|
||||
this.weightByDistanceSq[index] -= distanceSq;
|
||||
this.totalWeight -= distanceSq;
|
||||
|
||||
return new LongJumpToRandomPos.PossibleJump(this.origin.offset(this.unpackX(packedOffset), this.unpackY(packedOffset), this.unpackZ(packedOffset)), distanceSq);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LongJumpToRandomPos.PossibleJump get(int index) {
|
||||
int elementIndex = index;
|
||||
IntArrayList[] offsetsByDistanceSq = this.packedOffsetsByDistanceSq;
|
||||
for (int distanceSq = 0; distanceSq < offsetsByDistanceSq.length; distanceSq++) {
|
||||
IntArrayList packedOffsets = offsetsByDistanceSq[distanceSq];
|
||||
if (packedOffsets != null) {
|
||||
if (elementIndex < packedOffsets.size()) {
|
||||
int packedOffset = packedOffsets.getInt(elementIndex);
|
||||
return new LongJumpToRandomPos.PossibleJump(this.origin.offset(this.unpackX(packedOffset), this.unpackY(packedOffset), this.unpackZ(packedOffset)), distanceSq);
|
||||
}
|
||||
elementIndex -= packedOffsets.size();
|
||||
}
|
||||
}
|
||||
throw new IndexOutOfBoundsException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
return this.totalWeight == 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
int size = 0;
|
||||
for (IntArrayList packedOffsets : this.packedOffsetsByDistanceSq) {
|
||||
if (packedOffsets != null) {
|
||||
size += packedOffsets.size();
|
||||
}
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LongJumpToRandomPos.PossibleJump remove(int index) {
|
||||
int elementIndex = index;
|
||||
IntArrayList[] offsetsByDistanceSq = this.packedOffsetsByDistanceSq;
|
||||
for (int distanceSq = 0; distanceSq < offsetsByDistanceSq.length; distanceSq++) {
|
||||
IntArrayList packedOffsets = offsetsByDistanceSq[distanceSq];
|
||||
if (packedOffsets != null) {
|
||||
if (elementIndex < packedOffsets.size()) {
|
||||
int packedOffset = packedOffsets.getInt(elementIndex);
|
||||
packedOffsets.set(elementIndex, packedOffsets.set(packedOffsets.size() - 1, packedOffsets.getInt(elementIndex)));
|
||||
packedOffsets.removeInt(packedOffsets.size() - 1);
|
||||
this.weightByDistanceSq[distanceSq] -= distanceSq;
|
||||
this.totalWeight -= distanceSq;
|
||||
return new LongJumpToRandomPos.PossibleJump(this.origin.offset(this.unpackX(packedOffset), this.unpackY(packedOffset), this.unpackZ(packedOffset)), distanceSq);
|
||||
}
|
||||
elementIndex -= packedOffsets.size();
|
||||
}
|
||||
}
|
||||
throw new IndexOutOfBoundsException();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,150 @@
|
||||
package org.bxteam.divinemc.util.collections;
|
||||
|
||||
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
|
||||
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
|
||||
import java.util.AbstractList;
|
||||
import java.util.BitSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.Spliterator;
|
||||
import java.util.Spliterators;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public class MaskedList<E> extends AbstractList<E> {
|
||||
private final ObjectArrayList<E> allElements;
|
||||
private final BitSet visibleMask;
|
||||
private final Object2IntOpenHashMap<E> element2Index;
|
||||
private final boolean defaultVisibility;
|
||||
private int numCleared;
|
||||
|
||||
public MaskedList(ObjectArrayList<E> allElements, boolean defaultVisibility) {
|
||||
this.allElements = new ObjectArrayList<>();
|
||||
this.visibleMask = new BitSet();
|
||||
this.defaultVisibility = defaultVisibility;
|
||||
this.element2Index = new Object2IntOpenHashMap<>();
|
||||
this.element2Index.defaultReturnValue(-1);
|
||||
|
||||
this.addAll(allElements);
|
||||
}
|
||||
|
||||
public MaskedList() {
|
||||
this(new ObjectArrayList<>(), true);
|
||||
}
|
||||
|
||||
public int totalSize() {
|
||||
return this.allElements.size();
|
||||
}
|
||||
|
||||
public void addOrSet(E element, boolean visible) {
|
||||
int index = this.element2Index.getInt(element);
|
||||
if (index != -1) {
|
||||
this.visibleMask.set(index, visible);
|
||||
} else {
|
||||
this.add(element);
|
||||
this.setVisible(element, visible);
|
||||
}
|
||||
}
|
||||
|
||||
public void setVisible(E element, final boolean visible) {
|
||||
int index = this.element2Index.getInt(element);
|
||||
if (index != -1) {
|
||||
this.visibleMask.set(index, visible);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<E> iterator() {
|
||||
return new Iterator<>() {
|
||||
int nextIndex = 0;
|
||||
int cachedNext = -1;
|
||||
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
return (this.cachedNext = MaskedList.this.visibleMask.nextSetBit(this.nextIndex)) != -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public E next() {
|
||||
int index = this.cachedNext;
|
||||
this.cachedNext = -1;
|
||||
this.nextIndex = index + 1;
|
||||
return MaskedList.this.allElements.get(index);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public Spliterator<E> spliterator() {
|
||||
return new Spliterators.AbstractSpliterator<E>(Long.MAX_VALUE, Spliterator.ORDERED | Spliterator.NONNULL) {
|
||||
int nextIndex = 0;
|
||||
|
||||
@Override
|
||||
public boolean tryAdvance(Consumer<? super E> action) {
|
||||
int index = MaskedList.this.visibleMask.nextSetBit(this.nextIndex);
|
||||
if (index == -1) {
|
||||
return false;
|
||||
}
|
||||
this.nextIndex = index + 1;
|
||||
action.accept(MaskedList.this.allElements.get(index));
|
||||
return true;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean add(E e) {
|
||||
int oldIndex = this.element2Index.put(e, this.allElements.size());
|
||||
if (oldIndex != -1) {
|
||||
throw new IllegalStateException("MaskedList must not contain duplicates! Trying to add " + e + " but it is already present at index " + oldIndex + ". Current size: " + this.allElements.size());
|
||||
}
|
||||
this.visibleMask.set(this.allElements.size(), this.defaultVisibility);
|
||||
return this.allElements.add(e);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean remove(Object o) {
|
||||
int index = this.element2Index.removeInt(o);
|
||||
if (index == -1) {
|
||||
return false;
|
||||
}
|
||||
this.visibleMask.clear(index);
|
||||
this.allElements.set(index, null);
|
||||
this.numCleared++;
|
||||
|
||||
|
||||
if (this.numCleared * 2 > this.allElements.size()) {
|
||||
ObjectArrayList<E> clonedElements = this.allElements.clone();
|
||||
BitSet clonedVisibleMask = (BitSet) this.visibleMask.clone();
|
||||
this.allElements.clear();
|
||||
this.visibleMask.clear();
|
||||
this.element2Index.clear();
|
||||
for (int i = 0; i < clonedElements.size(); i++) {
|
||||
E element = clonedElements.get(i);
|
||||
int newIndex = this.allElements.size();
|
||||
this.allElements.add(element);
|
||||
this.visibleMask.set(newIndex, clonedVisibleMask.get(i));
|
||||
this.element2Index.put(element, newIndex);
|
||||
}
|
||||
this.numCleared = 0;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public E get(int index) {
|
||||
if (index < 0 || index >= this.size()) {
|
||||
throw new IndexOutOfBoundsException(index);
|
||||
}
|
||||
|
||||
int i = 0;
|
||||
while (index >= 0) {
|
||||
index--;
|
||||
i = this.visibleMask.nextSetBit(i + 1);
|
||||
}
|
||||
return this.allElements.get(i);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return this.visibleMask.cardinality();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
package org.bxteam.divinemc.util.entity;
|
||||
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.world.entity.LivingEntity;
|
||||
import net.minecraft.world.entity.ai.Brain;
|
||||
import net.minecraft.world.entity.ai.sensing.Sensor;
|
||||
import net.minecraft.world.entity.ai.sensing.SensorType;
|
||||
|
||||
public class SensorHelper {
|
||||
public static void disableSensor(LivingEntity brainedEntity, SensorType<?> sensorType) {
|
||||
if (brainedEntity.level().isClientSide()) {
|
||||
return;
|
||||
}
|
||||
Brain<?> brain = brainedEntity.getBrain();
|
||||
Sensor<?> sensor = (brain).sensors.get(sensorType);
|
||||
if (sensor != null) {
|
||||
long lastSenseTime = sensor.timeToTick;
|
||||
int senseInterval = sensor.scanRate;
|
||||
|
||||
long maxMultipleOfSenseInterval = Long.MAX_VALUE - (Long.MAX_VALUE % senseInterval);
|
||||
maxMultipleOfSenseInterval -= senseInterval;
|
||||
maxMultipleOfSenseInterval += lastSenseTime;
|
||||
|
||||
sensor.timeToTick = (maxMultipleOfSenseInterval);
|
||||
}
|
||||
}
|
||||
|
||||
public static <T extends LivingEntity, U extends Sensor<T>> void enableSensor(T brainedEntity, SensorType<U> sensorType) {
|
||||
enableSensor(brainedEntity, sensorType, false);
|
||||
}
|
||||
|
||||
public static <T extends LivingEntity, U extends Sensor<T>> void enableSensor(T brainedEntity, SensorType<U> sensorType, boolean extraTick) {
|
||||
if (brainedEntity.level().isClientSide()) {
|
||||
return;
|
||||
}
|
||||
|
||||
Brain<?> brain = brainedEntity.getBrain();
|
||||
U sensor = (U) (brain).sensors.get(sensorType);
|
||||
if (sensor != null) {
|
||||
long lastSenseTime = sensor.timeToTick;
|
||||
int senseInterval = sensor.scanRate;
|
||||
|
||||
if (lastSenseTime > senseInterval) {
|
||||
lastSenseTime = lastSenseTime % senseInterval;
|
||||
if (extraTick) {
|
||||
(sensor).timeToTick = (0L);
|
||||
sensor.tick((ServerLevel) brainedEntity.level(), brainedEntity);
|
||||
}
|
||||
}
|
||||
sensor.timeToTick = (lastSenseTime);
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,165 @@
|
||||
package org.bxteam.divinemc.util.structure;
|
||||
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Vec3i;
|
||||
import net.minecraft.world.phys.AABB;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class BoxOctree {
|
||||
private static final int subdivideThreshold = 10;
|
||||
private static final int maximumDepth = 3;
|
||||
private final AABB boundary;
|
||||
private final Vec3i size;
|
||||
private final int depth;
|
||||
private final List<AABB> innerBoxes = new ArrayList<>();
|
||||
private final List<BoxOctree> childrenOctants = new ArrayList<>();
|
||||
|
||||
public BoxOctree(AABB axisAlignedBB) {
|
||||
this(axisAlignedBB, 0);
|
||||
}
|
||||
|
||||
private BoxOctree(@NotNull AABB axisAlignedBB, int parentDepth) {
|
||||
boundary = axisAlignedBB.move(0, 0, 0); // deep copy
|
||||
size = new Vec3i(roundAwayFromZero(boundary.getXsize()), roundAwayFromZero(boundary.getYsize()), roundAwayFromZero(boundary.getZsize()));
|
||||
depth = parentDepth + 1;
|
||||
}
|
||||
|
||||
private int roundAwayFromZero(double value) {
|
||||
return (value >= 0) ? (int)Math.ceil(value) : (int)Math.floor(value);
|
||||
}
|
||||
|
||||
private void subdivide() {
|
||||
if (!childrenOctants.isEmpty()) {
|
||||
throw new UnsupportedOperationException("Tried to subdivide when there are already children octants.");
|
||||
}
|
||||
|
||||
int halfXSize = size.getX()/2;
|
||||
int halfYSize = size.getY()/2;
|
||||
int halfZSize = size.getZ()/2;
|
||||
// Lower Left Back Corner
|
||||
childrenOctants.add(new BoxOctree(new AABB(
|
||||
boundary.minX, boundary.minY, boundary.minZ,
|
||||
boundary.minX + halfXSize, boundary.minY + halfYSize, boundary.minZ + halfZSize),
|
||||
depth));
|
||||
// Lower Left Front Corner
|
||||
childrenOctants.add(new BoxOctree(new AABB(
|
||||
boundary.minX, boundary.minY, boundary.minZ + halfZSize,
|
||||
boundary.minX + halfXSize, boundary.minY + halfYSize, boundary.maxZ),
|
||||
depth));
|
||||
// Lower Right Back Corner
|
||||
childrenOctants.add(new BoxOctree(new AABB(
|
||||
boundary.minX + halfXSize, boundary.minY, boundary.minZ,
|
||||
boundary.maxX, boundary.minY + halfYSize, boundary.minZ + halfZSize),
|
||||
depth));
|
||||
// Lower Right Front Corner
|
||||
childrenOctants.add(new BoxOctree(new AABB(
|
||||
boundary.minX + halfXSize, boundary.minY, boundary.minZ + halfZSize,
|
||||
boundary.maxX, boundary.minY + halfYSize, boundary.maxZ),
|
||||
depth));
|
||||
// Upper Left Back Corner
|
||||
childrenOctants.add(new BoxOctree(new AABB(
|
||||
boundary.minX, boundary.minY + halfYSize, boundary.minZ,
|
||||
boundary.minX + halfXSize, boundary.maxY, boundary.minZ + halfZSize),
|
||||
depth));
|
||||
// Upper Left Front Corner
|
||||
childrenOctants.add(new BoxOctree(new AABB(
|
||||
boundary.minX, boundary.minY + halfYSize, boundary.minZ + halfZSize,
|
||||
boundary.minX + halfXSize, boundary.maxY, boundary.maxZ),
|
||||
depth));
|
||||
// Upper Right Back Corner
|
||||
childrenOctants.add(new BoxOctree(new AABB(
|
||||
boundary.minX + halfXSize, boundary.minY + halfYSize, boundary.minZ,
|
||||
boundary.maxX, boundary.maxY, boundary.minZ + halfZSize),
|
||||
depth));
|
||||
// Upper Right Front Corner
|
||||
childrenOctants.add(new BoxOctree(new AABB(
|
||||
boundary.minX + halfXSize, boundary.minY + halfYSize, boundary.minZ + halfZSize,
|
||||
boundary.maxX, boundary.maxY, boundary.maxZ),
|
||||
depth));
|
||||
|
||||
for (AABB parentInnerBox : innerBoxes) {
|
||||
for (BoxOctree octree : childrenOctants) {
|
||||
if (octree.boundaryIntersects(parentInnerBox)) {
|
||||
octree.addBox(parentInnerBox);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
innerBoxes.clear();
|
||||
}
|
||||
|
||||
public void addBox(AABB axisAlignedBB) {
|
||||
if (depth < maximumDepth && innerBoxes.size() > subdivideThreshold) {
|
||||
subdivide();
|
||||
} if (!childrenOctants.isEmpty()) {
|
||||
for (BoxOctree octree : childrenOctants) {
|
||||
if (octree.boundaryIntersects(axisAlignedBB)) {
|
||||
octree.addBox(axisAlignedBB);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Prevent re-adding the same box if it already exists
|
||||
for (AABB parentInnerBox : innerBoxes) {
|
||||
if (parentInnerBox.equals(axisAlignedBB)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
innerBoxes.add(axisAlignedBB);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean boundaryEntirelyContains(@NotNull AABB axisAlignedBB) {
|
||||
return boundary.contains(axisAlignedBB.minX, axisAlignedBB.minY, axisAlignedBB.minZ) &&
|
||||
boundary.contains(axisAlignedBB.maxX, axisAlignedBB.maxY, axisAlignedBB.maxZ);
|
||||
}
|
||||
|
||||
public boolean boundaryIntersects(AABB axisAlignedBB) {
|
||||
return boundary.intersects(axisAlignedBB);
|
||||
}
|
||||
|
||||
public boolean withinBoundsButNotIntersectingChildren(AABB axisAlignedBB) {
|
||||
return this.boundaryEntirelyContains(axisAlignedBB) && !this.intersectsAnyBox(axisAlignedBB);
|
||||
}
|
||||
|
||||
public boolean intersectsAnyBox(AABB axisAlignedBB) {
|
||||
if (!childrenOctants.isEmpty()) {
|
||||
for (BoxOctree octree : childrenOctants) {
|
||||
if (octree.boundaryIntersects(axisAlignedBB) && octree.intersectsAnyBox(axisAlignedBB)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
for (AABB innerBox : innerBoxes) {
|
||||
if (innerBox.intersects(axisAlignedBB)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean boundaryContains(@NotNull BlockPos position) {
|
||||
return boundary.contains(position.getX(), position.getY(), position.getZ());
|
||||
}
|
||||
|
||||
public boolean withinAnyBox(BlockPos position) {
|
||||
if (!childrenOctants.isEmpty()) {
|
||||
for (BoxOctree octree : childrenOctants) {
|
||||
if (octree.boundaryContains(position) && octree.withinAnyBox(position)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
for (AABB innerBox : innerBoxes) {
|
||||
if (innerBox.contains(position.getX(), position.getY(), position.getZ())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
package org.bxteam.divinemc.util.structure;
|
||||
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectArrayMap;
|
||||
import it.unimi.dsi.fastutil.ints.IntArrayList;
|
||||
import it.unimi.dsi.fastutil.ints.IntComparators;
|
||||
import net.minecraft.Util;
|
||||
import net.minecraft.core.FrontAndTop;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.nbt.NumericTag;
|
||||
import net.minecraft.nbt.StringTag;
|
||||
import net.minecraft.util.RandomSource;
|
||||
import net.minecraft.world.level.block.JigsawBlock;
|
||||
import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplate;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public final class GeneralUtils {
|
||||
private GeneralUtils() {}
|
||||
|
||||
// More optimized with checking if the jigsaw blocks can connect
|
||||
public static boolean canJigsawsAttach(StructureTemplate.@NotNull JigsawBlockInfo jigsaw1, StructureTemplate.@NotNull JigsawBlockInfo jigsaw2) {
|
||||
FrontAndTop prop1 = jigsaw1.info().state().getValue(JigsawBlock.ORIENTATION);
|
||||
FrontAndTop prop2 = jigsaw2.info().state().getValue(JigsawBlock.ORIENTATION);
|
||||
|
||||
return prop1.front() == prop2.front().getOpposite() &&
|
||||
(prop1.top() == prop2.top() || isRollableJoint(jigsaw1, prop1)) &&
|
||||
getStringMicroOptimised(jigsaw1.info().nbt(), "target").equals(getStringMicroOptimised(jigsaw2.info().nbt(), "name"));
|
||||
}
|
||||
|
||||
private static boolean isRollableJoint(StructureTemplate.@NotNull JigsawBlockInfo jigsaw1, FrontAndTop prop1) {
|
||||
String joint = getStringMicroOptimised(jigsaw1.info().nbt(), "joint");
|
||||
if(!joint.equals("rollable") && !joint.equals("aligned")) {
|
||||
return !prop1.front().getAxis().isHorizontal();
|
||||
}
|
||||
else {
|
||||
return joint.equals("rollable");
|
||||
}
|
||||
}
|
||||
|
||||
public static void shuffleAndPrioritize(@NotNull List<StructureTemplate.JigsawBlockInfo> list, RandomSource random) {
|
||||
Int2ObjectArrayMap<List<StructureTemplate.JigsawBlockInfo>> buckets = new Int2ObjectArrayMap<>();
|
||||
|
||||
// Add entries to the bucket
|
||||
for (StructureTemplate.JigsawBlockInfo structureBlockInfo : list) {
|
||||
int key = 0;
|
||||
if (structureBlockInfo.info().nbt() != null) {
|
||||
key = getIntMicroOptimised(structureBlockInfo.info().nbt(), "selection_priority");
|
||||
}
|
||||
|
||||
buckets.computeIfAbsent(key, k -> new ArrayList<>()).add(structureBlockInfo);
|
||||
}
|
||||
|
||||
// Shuffle the entries in the bucket
|
||||
for (List<StructureTemplate.JigsawBlockInfo> bucketList : buckets.values()) {
|
||||
Util.shuffle(bucketList, random);
|
||||
}
|
||||
|
||||
if (buckets.size() == 1) {
|
||||
list.clear();
|
||||
copyAll(buckets.int2ObjectEntrySet().fastIterator().next().getValue(), list);
|
||||
}
|
||||
else if (buckets.size() > 1) {
|
||||
// Priorities found. Concat them into a single new master list in reverse order to match vanilla behavior
|
||||
list.clear();
|
||||
|
||||
IntArrayList keys = new IntArrayList(buckets.keySet());
|
||||
keys.sort(IntComparators.OPPOSITE_COMPARATOR);
|
||||
|
||||
for (int i = 0; i < keys.size(); i++) {
|
||||
copyAll(buckets.get(keys.getInt(i)), list);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static int getIntMicroOptimised(@NotNull CompoundTag tag, String key) {
|
||||
return tag.get(key) instanceof NumericTag numericTag ? numericTag.getAsInt() : 0;
|
||||
}
|
||||
|
||||
public static @NotNull String getStringMicroOptimised(@NotNull CompoundTag tag, String key) {
|
||||
return tag.get(key) instanceof StringTag stringTag ? stringTag.getAsString() : "";
|
||||
}
|
||||
|
||||
public static <T> void copyAll(@NotNull List<T> src, List<T> dest) {
|
||||
// Do not listen to IDE. This is faster than addAll
|
||||
for (int i = 0; i < src.size(); i++) {
|
||||
dest.add(src.get(i));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,274 @@
|
||||
package org.bxteam.divinemc.util.structure;
|
||||
|
||||
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplate;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.ListIterator;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
public class PalettedStructureBlockInfoList implements List<StructureTemplate.StructureBlockInfo> {
|
||||
private static final long[] EMPTY_DATA = new long[0];
|
||||
private static final CompoundTag[] NULL_TAGS = new CompoundTag[]{null};
|
||||
private static final String UNSUPPORTED_OPERATION_ERROR_MESSAGE = "Structure Layout Optimizer: No mod should be modifying a StructureTemplate's Palette itself. Please reach out to Structure Layout Optimizer dev for this crash to investigate this mod compat issue.";
|
||||
|
||||
protected final long[] data;
|
||||
protected final BlockState[] states;
|
||||
protected final CompoundTag[] nbts;
|
||||
protected final int xBits, yBits, zBits;
|
||||
protected final int stateBits, nbtBits;
|
||||
protected final int bitsPerEntry;
|
||||
protected final int size;
|
||||
private WeakReference<List<StructureTemplate.StructureBlockInfo>> cachedStructureBlockInfoList = new WeakReference<>(null);
|
||||
|
||||
public PalettedStructureBlockInfoList(List<StructureTemplate.StructureBlockInfo> infos) {
|
||||
this(infos, null);
|
||||
}
|
||||
|
||||
public PalettedStructureBlockInfoList(List<StructureTemplate.StructureBlockInfo> infos, Predicate<StructureTemplate.StructureBlockInfo> predicate) {
|
||||
List<Entry> entries = new ArrayList<>();
|
||||
List<BlockState> states = new ArrayList<>();
|
||||
List<CompoundTag> tags = new ArrayList<>();
|
||||
|
||||
int maxX = 0;
|
||||
int maxY = 0;
|
||||
int maxZ = 0;
|
||||
|
||||
for (StructureTemplate.StructureBlockInfo info : infos) {
|
||||
if (predicate != null && !predicate.test(info)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
int state = states.indexOf(info.state());
|
||||
if (state == -1) {
|
||||
state = states.size();
|
||||
states.add(info.state());
|
||||
}
|
||||
|
||||
int tag = indexOf(tags, info.nbt());
|
||||
if (tag == -1) {
|
||||
tag = tags.size();
|
||||
tags.add(info.nbt());
|
||||
}
|
||||
|
||||
int x = info.pos().getX();
|
||||
int y = info.pos().getY();
|
||||
int z = info.pos().getZ();
|
||||
|
||||
if (x < 0 || y < 0 || z < 0) {
|
||||
throw new RuntimeException("StructureLayoutOptimizer: Invalid StructureBlockInfo position: " + info.pos());
|
||||
}
|
||||
if (x > maxX) {
|
||||
maxX = x;
|
||||
}
|
||||
if (y > maxY) {
|
||||
maxY = y;
|
||||
}
|
||||
if (z > maxZ) {
|
||||
maxZ = z;
|
||||
}
|
||||
|
||||
entries.add(new Entry(x, y, z, state, tag));
|
||||
}
|
||||
|
||||
this.xBits = bits(maxX);
|
||||
this.yBits = bits(maxY);
|
||||
this.zBits = bits(maxZ);
|
||||
this.stateBits = bits(states.size() - 1);
|
||||
this.nbtBits = bits(tags.size() - 1);
|
||||
this.bitsPerEntry = this.xBits + this.yBits + this.zBits + this.stateBits + this.nbtBits;
|
||||
|
||||
if (this.bitsPerEntry > 64) {
|
||||
throw new RuntimeException("StructureLayoutOptimizer: Too many bits per entry: " + this.bitsPerEntry);
|
||||
}
|
||||
|
||||
this.size = entries.size();
|
||||
if (this.bitsPerEntry != 0) {
|
||||
int entriesPerLong = 64 / this.bitsPerEntry;
|
||||
this.data = new long[(this.size + entriesPerLong - 1) / entriesPerLong];
|
||||
for (int i = 0; i < this.size; i++) {
|
||||
this.data[i / entriesPerLong] |= entries.get(i).compress(this.xBits, this.yBits, this.zBits, this.stateBits) << ((i % entriesPerLong) * this.bitsPerEntry);
|
||||
}
|
||||
} else {
|
||||
this.data = EMPTY_DATA;
|
||||
}
|
||||
this.states = states.toArray(new BlockState[0]);
|
||||
this.nbts = tags.size() == 1 && tags.get(0) == null ? NULL_TAGS : tags.toArray(new CompoundTag[0]);
|
||||
}
|
||||
|
||||
private static int bits(int i) {
|
||||
int bits = 0;
|
||||
while (i >= 1 << bits) {
|
||||
bits++;
|
||||
}
|
||||
return bits;
|
||||
}
|
||||
|
||||
private static <T> int indexOf(@NotNull List<T> list, T o) {
|
||||
for (int i = 0; i < list.size(); i++) {
|
||||
if (list.get(i) == o) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
private @NotNull List<StructureTemplate.StructureBlockInfo> convertBackToStructureBlockInfoListAndCache() {
|
||||
synchronized(data) {
|
||||
List<StructureTemplate.StructureBlockInfo> structureBlockInfos = cachedStructureBlockInfoList.get();
|
||||
if (structureBlockInfos != null) {
|
||||
return structureBlockInfos;
|
||||
}
|
||||
|
||||
structureBlockInfos = new ObjectArrayList<>(new PalettedStructureBlockInfoListIterator(this));
|
||||
cachedStructureBlockInfoList = new WeakReference<>(structureBlockInfos);
|
||||
return structureBlockInfos;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return this.size;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
return this.size == 0;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Iterator<StructureTemplate.StructureBlockInfo> iterator() {
|
||||
return convertBackToStructureBlockInfoListAndCache().iterator();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public ListIterator<StructureTemplate.StructureBlockInfo> listIterator() {
|
||||
return convertBackToStructureBlockInfoListAndCache().listIterator();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public ListIterator<StructureTemplate.StructureBlockInfo> listIterator(int index) {
|
||||
return convertBackToStructureBlockInfoListAndCache().listIterator(index);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean contains(Object o) {
|
||||
return convertBackToStructureBlockInfoListAndCache().contains(o);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsAll(@NotNull Collection<?> c) {
|
||||
return new HashSet<>(convertBackToStructureBlockInfoListAndCache()).containsAll(c);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Object @NotNull [] toArray() {
|
||||
return convertBackToStructureBlockInfoListAndCache().toArray();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public <T> T @NotNull [] toArray(@NotNull T @NotNull [] a) {
|
||||
return convertBackToStructureBlockInfoListAndCache().toArray(a);
|
||||
}
|
||||
|
||||
@Override
|
||||
public StructureTemplate.StructureBlockInfo get(int index) {
|
||||
return convertBackToStructureBlockInfoListAndCache().get(index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int indexOf(Object o) {
|
||||
return convertBackToStructureBlockInfoListAndCache().indexOf(o);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int lastIndexOf(Object o) {
|
||||
return convertBackToStructureBlockInfoListAndCache().lastIndexOf(o);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public List<StructureTemplate.StructureBlockInfo> subList(int fromIndex, int toIndex) {
|
||||
return convertBackToStructureBlockInfoListAndCache().subList(fromIndex, toIndex);
|
||||
}
|
||||
|
||||
@Override
|
||||
public StructureTemplate.StructureBlockInfo set(int index, StructureTemplate.StructureBlockInfo element) {
|
||||
throw new UnsupportedOperationException(UNSUPPORTED_OPERATION_ERROR_MESSAGE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void add(int index, StructureTemplate.StructureBlockInfo element) {
|
||||
throw new UnsupportedOperationException(UNSUPPORTED_OPERATION_ERROR_MESSAGE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean add(StructureTemplate.StructureBlockInfo info) {
|
||||
throw new UnsupportedOperationException(UNSUPPORTED_OPERATION_ERROR_MESSAGE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean remove(Object o) {
|
||||
throw new UnsupportedOperationException(UNSUPPORTED_OPERATION_ERROR_MESSAGE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public StructureTemplate.StructureBlockInfo remove(int index) {
|
||||
throw new UnsupportedOperationException(UNSUPPORTED_OPERATION_ERROR_MESSAGE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean addAll(@NotNull Collection<? extends StructureTemplate.StructureBlockInfo> c) {
|
||||
throw new UnsupportedOperationException(UNSUPPORTED_OPERATION_ERROR_MESSAGE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean addAll(int index, @NotNull Collection<? extends StructureTemplate.StructureBlockInfo> c) {
|
||||
throw new UnsupportedOperationException(UNSUPPORTED_OPERATION_ERROR_MESSAGE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean removeAll(@NotNull Collection<?> c) {
|
||||
throw new UnsupportedOperationException(UNSUPPORTED_OPERATION_ERROR_MESSAGE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean retainAll(@NotNull Collection<?> c) {
|
||||
throw new UnsupportedOperationException(UNSUPPORTED_OPERATION_ERROR_MESSAGE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
throw new UnsupportedOperationException(UNSUPPORTED_OPERATION_ERROR_MESSAGE);
|
||||
}
|
||||
|
||||
private static class Entry {
|
||||
private final int x, y, z;
|
||||
private final int state, nbt;
|
||||
|
||||
private Entry(int x, int y, int z, int state, int nbt) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.z = z;
|
||||
this.state = state;
|
||||
this.nbt = nbt;
|
||||
}
|
||||
|
||||
private long compress(int xBits, int yBits, int zBits, int stateBits) {
|
||||
return this.x + ((this.y + ((this.z + ((this.state + ((long) this.nbt << stateBits)) << zBits)) << yBits)) << xBits);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
package org.bxteam.divinemc.util.structure;
|
||||
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplate;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import java.util.Iterator;
|
||||
|
||||
public class PalettedStructureBlockInfoListIterator implements Iterator<StructureTemplate.StructureBlockInfo> {
|
||||
private final PalettedStructureBlockInfoList infos;
|
||||
|
||||
private final int xOffset, yOffset, zOffset, stateOffset;
|
||||
private final int xMask, yMask, zMask, stateMask, tagMask;
|
||||
|
||||
private int index = 0;
|
||||
|
||||
public PalettedStructureBlockInfoListIterator(@NotNull PalettedStructureBlockInfoList infos) {
|
||||
this.infos = infos;
|
||||
this.xOffset = infos.xBits;
|
||||
this.yOffset = this.xOffset + infos.yBits;
|
||||
this.zOffset = this.yOffset + infos.zBits;
|
||||
this.stateOffset = this.zOffset + infos.stateBits;
|
||||
this.xMask = (1 << infos.xBits) - 1;
|
||||
this.yMask = (1 << infos.yBits) - 1;
|
||||
this.zMask = (1 << infos.zBits) - 1;
|
||||
this.stateMask = (1 << infos.stateBits) - 1;
|
||||
this.tagMask = (1 << infos.nbtBits) - 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
return this.index < this.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public StructureTemplate.StructureBlockInfo next() {
|
||||
int index = this.toIndex(this.index++);
|
||||
if (index >= this.infos.size) {
|
||||
throw new IndexOutOfBoundsException();
|
||||
}
|
||||
|
||||
int bitsPerEntry = this.infos.bitsPerEntry;
|
||||
if (bitsPerEntry == 0) {
|
||||
return new StructureTemplate.StructureBlockInfo(new BlockPos(0, 0, 0), this.infos.states[0], this.infos.nbts[0]);
|
||||
}
|
||||
|
||||
int entriesPerLong = 64 / bitsPerEntry;
|
||||
long entry = this.infos.data[index / entriesPerLong] >>> ((index % entriesPerLong) * bitsPerEntry);
|
||||
|
||||
int x = (int) (entry & this.xMask);
|
||||
int y = (int) (entry >> this.xOffset & this.yMask);
|
||||
int z = (int) (entry >> this.yOffset & this.zMask);
|
||||
int state = (int) (entry >> this.zOffset & this.stateMask);
|
||||
int tag = (int) (entry >> this.stateOffset & this.tagMask);
|
||||
|
||||
return new StructureTemplate.StructureBlockInfo(new BlockPos(x, y, z), this.infos.states[state], this.infos.nbts[tag]);
|
||||
}
|
||||
|
||||
protected int toIndex(int index) {
|
||||
return index;
|
||||
}
|
||||
|
||||
protected int size() {
|
||||
return this.infos.size;
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user