mirror of
https://gitlab.com/distant-horizons-team/distant-horizons-server-plugin.git
synced 2026-11-05 17:02:33 -05:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0004539b41 | ||
|
|
9ef0f09dd2 | ||
|
|
79cb5aa302 | ||
|
|
5e9760c3d1 | ||
|
|
637636b5e8 | ||
|
|
0ffe620f92 | ||
|
|
2bcf9c7ca9 | ||
|
|
222221f9dc | ||
|
|
d086458546 | ||
|
|
12b1580535 | ||
|
|
81326b415d | ||
|
|
328684cf1d | ||
|
|
d89cfbfec5 | ||
|
|
413e2a6e70 | ||
|
|
ee385ea5af | ||
|
|
37db471c4f | ||
|
|
3e9165b7a3 | ||
|
|
da893ec1f0 | ||
|
|
320c04aeb7 | ||
|
|
fb8f98c52f | ||
|
|
99cee46ae5 | ||
|
|
a05ee01b8b | ||
|
|
b6878aaaaf | ||
|
|
0fbc612eef | ||
|
|
da2dba88b1 | ||
|
|
35822cb8a7 | ||
|
|
9625035edd | ||
|
|
ecda68771c | ||
|
|
f7822bc204 | ||
|
|
ca4a3a6bcc | ||
|
|
a189487623 | ||
|
|
721d4c2027 | ||
|
|
577a45be5c | ||
|
|
8bc102a26f | ||
|
|
8aa9fa9c31 | ||
|
|
3644051706 | ||
|
|
2786f4b8c7 | ||
|
|
930483ee0c | ||
|
|
64b24aba31 | ||
|
|
22447f5bd5 | ||
|
|
697ca18299 | ||
|
|
bef45fe732 | ||
|
|
f60736f21e | ||
|
|
a36102c16e | ||
|
|
ed98a7e0d4 | ||
|
|
1b4138f537 | ||
|
|
a387c7730d | ||
|
|
dbb9d8a2cf | ||
|
|
3c76e8f229 | ||
|
|
967cd2e20d | ||
|
|
83c4a3f6a6 | ||
|
|
c6b89e8794 | ||
|
|
7725a8c0eb | ||
|
|
79cc6e3218 | ||
|
|
15542e3a36 | ||
|
|
275b495675 | ||
|
|
dcf08ba286 | ||
|
|
6532418b5b | ||
|
|
16a68d802f | ||
|
|
f2a40d405d | ||
|
|
c95f54cc0b | ||
|
|
cdeb8f67c9 | ||
|
|
33f374fc41 | ||
|
|
32f78afb39 | ||
|
|
53051b71e3 | ||
|
|
651fd09dde | ||
|
|
a5709d96a4 | ||
|
|
6e11678a80 | ||
|
|
30ea7d5438 | ||
|
|
6c2b77fe25 | ||
|
|
24e2727bf9 | ||
|
|
54ddb357e3 | ||
|
|
7d341959ae | ||
|
|
ca53f19c87 | ||
|
|
4f2a782254 | ||
|
|
e5d4ab61a0 | ||
|
|
375146229e | ||
|
|
95839b85ce | ||
|
|
0243f9bccb | ||
|
|
edfd1df1b9 | ||
|
|
f068a2fa2b | ||
|
|
ffcfded127 | ||
|
|
4cb0d86217 | ||
|
|
5ae9ae65f8 | ||
|
|
c1e0fd8de8 | ||
|
|
6e54fed39e | ||
|
|
a2239bba85 | ||
|
|
f962a0253e | ||
|
|
ac0f4503de | ||
|
|
27680c67ad | ||
|
|
0dacd96ab4 |
No files matched your search
@@ -1,5 +0,0 @@
|
||||
# Should match the UID and GID of the project files.
|
||||
# With Docker this is typically your own UID and GID.
|
||||
# With Podman this is probably root's UID and GID..
|
||||
UID=1000
|
||||
GID=1000
|
||||
+3
-8
@@ -27,14 +27,9 @@ replay_pid*
|
||||
/.idea
|
||||
/*.iml
|
||||
|
||||
# Maven output
|
||||
/dependency-reduced-pom.xml
|
||||
/target
|
||||
/jars
|
||||
/.m2
|
||||
|
||||
# Env vars
|
||||
.env
|
||||
# Gradle output
|
||||
/.gradle
|
||||
/build
|
||||
|
||||
# Test server
|
||||
/server
|
||||
|
||||
+6
-26
@@ -1,49 +1,29 @@
|
||||
include:
|
||||
- template: Security/SAST.gitlab-ci.yml
|
||||
|
||||
variables:
|
||||
MAVEN_OPTS: >-
|
||||
-Dhttps.protocols=TLSv1.2
|
||||
-Dmaven.repo.local=.m2/repository
|
||||
-Dorg.slf4j.simpleLogger.log.org.apache.maven.cli.transfer.Slf4jMavenTransferListener=WARN
|
||||
-Dorg.slf4j.simpleLogger.showDateTime=true
|
||||
-Djava.awt.headless=true
|
||||
|
||||
MAVEN_CLI_OPTS: >-
|
||||
--batch-mode
|
||||
--errors
|
||||
--fail-at-end
|
||||
--show-version
|
||||
-DinstallAtEnd=true
|
||||
-DdeployAtEnd=true
|
||||
|
||||
stages:
|
||||
- build
|
||||
- test
|
||||
|
||||
default:
|
||||
image: maven:3.9-eclipse-temurin-21-alpine
|
||||
image: eclipse-temurin:21
|
||||
|
||||
cache:
|
||||
key: maven-repo-${CI_COMMIT_REF_SLUG}
|
||||
key: gradle-${CI_COMMIT_REF_SLUG}
|
||||
unprotect: true
|
||||
|
||||
paths:
|
||||
- .m2/repository
|
||||
- .gradle
|
||||
|
||||
build:
|
||||
stage: build
|
||||
|
||||
parallel:
|
||||
matrix:
|
||||
- MC_VER: [ "1.16.5", "1.17.1", "1.18.2", "1.19.2", "1.19.4", "1.20.1", "1.20.2", "1.20.4", "1.20.6", "1.21.1", "1.21.3", "1.21.4", "1.21.5", "1.21.7" ]
|
||||
|
||||
script:
|
||||
- mvn package --projects pom-${MC_VER}.xml ${MAVEN_CLI_OPTS}
|
||||
- mv jars/*.jar ./
|
||||
- ./gradlew build
|
||||
- mv build/libs/*/*.jar ./
|
||||
|
||||
artifacts:
|
||||
name: DHS-${MC_VER}-${CI_COMMIT_REF_SLUG}-${CI_JOB_STARTED_AT}
|
||||
name: DistantHorizonsSupport-${CI_COMMIT_REF_SLUG}-${CI_JOB_STARTED_AT}
|
||||
|
||||
paths:
|
||||
- "./*.jar"
|
||||
-20
@@ -1,20 +0,0 @@
|
||||
FROM maven:3.9.9-eclipse-temurin-22
|
||||
|
||||
# Default to first user on Debian based distros.
|
||||
# Pass in your own UID and GID as environment variables to override.
|
||||
ARG UID=1000
|
||||
ARG GID=1000
|
||||
|
||||
# Create a user with the same UID and GID as the host user if they don't already exist.
|
||||
RUN getent group $GID || groupadd -g $GID app
|
||||
RUN getent passwd $UID || useradd -m -u $UID -g $GID -s /bin/bash -d /app app
|
||||
|
||||
# If an alternate user was created, let it sudo.
|
||||
RUN ( getent passwd app > /dev/null && echo 'app ALL=(ALL:ALL) NOPASSWD: ALL' | tee -a /etc/sudoers > /dev/null ) || true
|
||||
|
||||
# Drop privileges.
|
||||
USER $UID:$GID
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
ENTRYPOINT [ "mvn", "-Dmaven.repo.local=/app/.m2" ]
|
||||
@@ -10,13 +10,9 @@ Please see [our wiki](https://gitlab.com/distant-horizons-team/distant-horizons-
|
||||
|
||||
## Building
|
||||
|
||||
### Dockerized
|
||||
This project uses Gradle.
|
||||
|
||||
If you have Docker; run `docker compose up` to build the project inside a container. See `.env.example` if you need to adjust the user/group of the build process.
|
||||
|
||||
### Using Maven directly
|
||||
|
||||
Just run `mvn` in the project directory to compile and package a new JAR.
|
||||
`gradlew clean build`
|
||||
|
||||
## Contribution
|
||||
|
||||
|
||||
+107
@@ -0,0 +1,107 @@
|
||||
plugins {
|
||||
id 'java'
|
||||
id 'com.gradleup.shadow' version '8.3.8'
|
||||
}
|
||||
|
||||
group = 'no.jckf.dhs'
|
||||
version = '0.14.0'
|
||||
description = 'Distant Horizons Support'
|
||||
|
||||
java {
|
||||
toolchain.languageVersion.set(JavaLanguageVersion.of(21))
|
||||
}
|
||||
|
||||
def paperVersions = [
|
||||
'1.16.5',
|
||||
]
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
maven { url = 'https://repo.papermc.io/repository/maven-public/' }
|
||||
maven { url = 'https://jitpack.io/' }
|
||||
}
|
||||
|
||||
configurations {
|
||||
shade
|
||||
implementation {
|
||||
extendsFrom shade
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compileOnly 'org.jetbrains:annotations:25.0.0'
|
||||
|
||||
implementation 'com.google.guava:guava:33.3.1-jre'
|
||||
|
||||
shade 'org.bstats:bstats-bukkit:3.1.0'
|
||||
shade 'org.json:json:20240303'
|
||||
shade 'org.tukaani:xz:1.10'
|
||||
shade 'com.github.technicallycoded:FoliaLib:v0.4.3'
|
||||
|
||||
testImplementation 'junit:junit:4.11'
|
||||
}
|
||||
|
||||
paperVersions.each { mcVersion ->
|
||||
def taskSuffix = mcVersion.replace('.', '_')
|
||||
def taskName = "buildJar_${taskSuffix}"
|
||||
def outputDir = file("build/libs/$mcVersion")
|
||||
|
||||
def apiVersion = mcVersion.tokenize('.')[0..1].join('.')
|
||||
|
||||
def sourceSetName = "main"
|
||||
|
||||
// Custom processResources task per version
|
||||
def processTask = tasks.register("processResources_${taskSuffix}", Copy) {
|
||||
from(sourceSets[sourceSetName].resources.srcDirs)
|
||||
into("$buildDir/resources-processed/$mcVersion")
|
||||
|
||||
include '**/*.yml'
|
||||
|
||||
expand(
|
||||
version: project.version,
|
||||
'mainClass': 'no.jckf.dhsupport.bukkit.DhSupportBukkitPlugin',
|
||||
'mcApiVersion': apiVersion,
|
||||
)
|
||||
}
|
||||
|
||||
configurations {
|
||||
"compileOnly${mcVersion}" {
|
||||
canBeResolved = true
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compileOnly "com.destroystokyo.paper:paper-api:${mcVersion}-R0.1-SNAPSHOT"
|
||||
//compileOnly "io.papermc.paper:paper-api:${mcVersion}-R0.1-SNAPSHOT"
|
||||
}
|
||||
|
||||
task(taskName, type: com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) {
|
||||
group = 'build'
|
||||
|
||||
archiveBaseName.set("DistantHorizonsSupport")
|
||||
archiveVersion.set(version)
|
||||
destinationDirectory.set(outputDir)
|
||||
|
||||
// Use processed resources as input
|
||||
// Exclude raw YAML to avoid unprocessed plugin.yml in final JAR
|
||||
from(sourceSets.main.output) {
|
||||
exclude '**/*.yml'
|
||||
}
|
||||
|
||||
from("$buildDir/resources-processed/$mcVersion")
|
||||
|
||||
configurations = [project.configurations.shade]
|
||||
|
||||
relocate 'org.bstats', 'no.jckf.dhsupport.bstats'
|
||||
relocate 'org.json', 'no.jckf.dhsupport.json'
|
||||
relocate 'org.tukaani.xz', 'no.jckf.dhsupport.xz'
|
||||
relocate 'com.tcoded.folialib', 'no.jckf.dhsupport.folialib'
|
||||
|
||||
exclude 'org/jetbrains/**'
|
||||
exclude 'org/intellij/**'
|
||||
|
||||
dependsOn processTask
|
||||
}
|
||||
|
||||
build.dependsOn(taskName)
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
services:
|
||||
maven:
|
||||
build:
|
||||
context: .
|
||||
|
||||
args:
|
||||
UID: ${UID}
|
||||
GID: ${GID}
|
||||
|
||||
volumes:
|
||||
- ./:/app
|
||||
@@ -0,0 +1,2 @@
|
||||
org.gradle.parallel=true
|
||||
org.gradle.configureondemand=true
|
||||
Vendored
BIN
Binary file not shown.
+7
@@ -0,0 +1,7 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.3-bin.zip
|
||||
networkTimeout=10000
|
||||
validateDistributionUrl=true
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
@@ -0,0 +1,251 @@
|
||||
#!/bin/sh
|
||||
|
||||
#
|
||||
# Copyright © 2015-2021 the original authors.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# https://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
|
||||
##############################################################################
|
||||
#
|
||||
# Gradle start up script for POSIX generated by Gradle.
|
||||
#
|
||||
# Important for running:
|
||||
#
|
||||
# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
|
||||
# noncompliant, but you have some other compliant shell such as ksh or
|
||||
# bash, then to run this script, type that shell name before the whole
|
||||
# command line, like:
|
||||
#
|
||||
# ksh Gradle
|
||||
#
|
||||
# Busybox and similar reduced shells will NOT work, because this script
|
||||
# requires all of these POSIX shell features:
|
||||
# * functions;
|
||||
# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
|
||||
# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
|
||||
# * compound commands having a testable exit status, especially «case»;
|
||||
# * various built-in commands including «command», «set», and «ulimit».
|
||||
#
|
||||
# Important for patching:
|
||||
#
|
||||
# (2) This script targets any POSIX shell, so it avoids extensions provided
|
||||
# by Bash, Ksh, etc; in particular arrays are avoided.
|
||||
#
|
||||
# The "traditional" practice of packing multiple parameters into a
|
||||
# space-separated string is a well documented source of bugs and security
|
||||
# problems, so this is (mostly) avoided, by progressively accumulating
|
||||
# options in "$@", and eventually passing that to Java.
|
||||
#
|
||||
# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
|
||||
# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
|
||||
# see the in-line comments for details.
|
||||
#
|
||||
# There are tweaks for specific operating systems such as AIX, CygWin,
|
||||
# Darwin, MinGW, and NonStop.
|
||||
#
|
||||
# (3) This script is generated from the Groovy template
|
||||
# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
|
||||
# within the Gradle project.
|
||||
#
|
||||
# You can find Gradle at https://github.com/gradle/gradle/.
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
# Attempt to set APP_HOME
|
||||
|
||||
# Resolve links: $0 may be a link
|
||||
app_path=$0
|
||||
|
||||
# Need this for daisy-chained symlinks.
|
||||
while
|
||||
APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
|
||||
[ -h "$app_path" ]
|
||||
do
|
||||
ls=$( ls -ld "$app_path" )
|
||||
link=${ls#*' -> '}
|
||||
case $link in #(
|
||||
/*) app_path=$link ;; #(
|
||||
*) app_path=$APP_HOME$link ;;
|
||||
esac
|
||||
done
|
||||
|
||||
# This is normally unused
|
||||
# shellcheck disable=SC2034
|
||||
APP_BASE_NAME=${0##*/}
|
||||
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
|
||||
APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit
|
||||
|
||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||
MAX_FD=maximum
|
||||
|
||||
warn () {
|
||||
echo "$*"
|
||||
} >&2
|
||||
|
||||
die () {
|
||||
echo
|
||||
echo "$*"
|
||||
echo
|
||||
exit 1
|
||||
} >&2
|
||||
|
||||
# OS specific support (must be 'true' or 'false').
|
||||
cygwin=false
|
||||
msys=false
|
||||
darwin=false
|
||||
nonstop=false
|
||||
case "$( uname )" in #(
|
||||
CYGWIN* ) cygwin=true ;; #(
|
||||
Darwin* ) darwin=true ;; #(
|
||||
MSYS* | MINGW* ) msys=true ;; #(
|
||||
NONSTOP* ) nonstop=true ;;
|
||||
esac
|
||||
|
||||
CLASSPATH="\\\"\\\""
|
||||
|
||||
|
||||
# Determine the Java command to use to start the JVM.
|
||||
if [ -n "$JAVA_HOME" ] ; then
|
||||
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
||||
# IBM's JDK on AIX uses strange locations for the executables
|
||||
JAVACMD=$JAVA_HOME/jre/sh/java
|
||||
else
|
||||
JAVACMD=$JAVA_HOME/bin/java
|
||||
fi
|
||||
if [ ! -x "$JAVACMD" ] ; then
|
||||
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
else
|
||||
JAVACMD=java
|
||||
if ! command -v java >/dev/null 2>&1
|
||||
then
|
||||
die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
fi
|
||||
|
||||
# Increase the maximum file descriptors if we can.
|
||||
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
|
||||
case $MAX_FD in #(
|
||||
max*)
|
||||
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
|
||||
# shellcheck disable=SC2039,SC3045
|
||||
MAX_FD=$( ulimit -H -n ) ||
|
||||
warn "Could not query maximum file descriptor limit"
|
||||
esac
|
||||
case $MAX_FD in #(
|
||||
'' | soft) :;; #(
|
||||
*)
|
||||
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
|
||||
# shellcheck disable=SC2039,SC3045
|
||||
ulimit -n "$MAX_FD" ||
|
||||
warn "Could not set maximum file descriptor limit to $MAX_FD"
|
||||
esac
|
||||
fi
|
||||
|
||||
# Collect all arguments for the java command, stacking in reverse order:
|
||||
# * args from the command line
|
||||
# * the main class name
|
||||
# * -classpath
|
||||
# * -D...appname settings
|
||||
# * --module-path (only if needed)
|
||||
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
|
||||
|
||||
# For Cygwin or MSYS, switch paths to Windows format before running java
|
||||
if "$cygwin" || "$msys" ; then
|
||||
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
|
||||
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
|
||||
|
||||
JAVACMD=$( cygpath --unix "$JAVACMD" )
|
||||
|
||||
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
||||
for arg do
|
||||
if
|
||||
case $arg in #(
|
||||
-*) false ;; # don't mess with options #(
|
||||
/?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
|
||||
[ -e "$t" ] ;; #(
|
||||
*) false ;;
|
||||
esac
|
||||
then
|
||||
arg=$( cygpath --path --ignore --mixed "$arg" )
|
||||
fi
|
||||
# Roll the args list around exactly as many times as the number of
|
||||
# args, so each arg winds up back in the position where it started, but
|
||||
# possibly modified.
|
||||
#
|
||||
# NB: a `for` loop captures its iteration list before it begins, so
|
||||
# changing the positional parameters here affects neither the number of
|
||||
# iterations, nor the values presented in `arg`.
|
||||
shift # remove old arg
|
||||
set -- "$@" "$arg" # push replacement arg
|
||||
done
|
||||
fi
|
||||
|
||||
|
||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||
|
||||
# Collect all arguments for the java command:
|
||||
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
|
||||
# and any embedded shellness will be escaped.
|
||||
# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
|
||||
# treated as '${Hostname}' itself on the command line.
|
||||
|
||||
set -- \
|
||||
"-Dorg.gradle.appname=$APP_BASE_NAME" \
|
||||
-classpath "$CLASSPATH" \
|
||||
-jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \
|
||||
"$@"
|
||||
|
||||
# Stop when "xargs" is not available.
|
||||
if ! command -v xargs >/dev/null 2>&1
|
||||
then
|
||||
die "xargs is not available"
|
||||
fi
|
||||
|
||||
# Use "xargs" to parse quoted args.
|
||||
#
|
||||
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
|
||||
#
|
||||
# In Bash we could simply go:
|
||||
#
|
||||
# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
|
||||
# set -- "${ARGS[@]}" "$@"
|
||||
#
|
||||
# but POSIX shell has neither arrays nor command substitution, so instead we
|
||||
# post-process each arg (as a line of input to sed) to backslash-escape any
|
||||
# character that might be a shell metacharacter, then use eval to reverse
|
||||
# that process (while maintaining the separation between arguments), and wrap
|
||||
# the whole thing up as a single "set" statement.
|
||||
#
|
||||
# This will of course break if any of these variables contains a newline or
|
||||
# an unmatched quote.
|
||||
#
|
||||
|
||||
eval "set -- $(
|
||||
printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
|
||||
xargs -n1 |
|
||||
sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
|
||||
tr '\n' ' '
|
||||
)" '"$@"'
|
||||
|
||||
exec "$JAVACMD" "$@"
|
||||
Vendored
+94
@@ -0,0 +1,94 @@
|
||||
@rem
|
||||
@rem Copyright 2015 the original author or authors.
|
||||
@rem
|
||||
@rem Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@rem you may not use this file except in compliance with the License.
|
||||
@rem You may obtain a copy of the License at
|
||||
@rem
|
||||
@rem https://www.apache.org/licenses/LICENSE-2.0
|
||||
@rem
|
||||
@rem Unless required by applicable law or agreed to in writing, software
|
||||
@rem distributed under the License is distributed on an "AS IS" BASIS,
|
||||
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
@rem See the License for the specific language governing permissions and
|
||||
@rem limitations under the License.
|
||||
@rem
|
||||
@rem SPDX-License-Identifier: Apache-2.0
|
||||
@rem
|
||||
|
||||
@if "%DEBUG%"=="" @echo off
|
||||
@rem ##########################################################################
|
||||
@rem
|
||||
@rem Gradle startup script for Windows
|
||||
@rem
|
||||
@rem ##########################################################################
|
||||
|
||||
@rem Set local scope for the variables with windows NT shell
|
||||
if "%OS%"=="Windows_NT" setlocal
|
||||
|
||||
set DIRNAME=%~dp0
|
||||
if "%DIRNAME%"=="" set DIRNAME=.
|
||||
@rem This is normally unused
|
||||
set APP_BASE_NAME=%~n0
|
||||
set APP_HOME=%DIRNAME%
|
||||
|
||||
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
|
||||
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
|
||||
|
||||
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
|
||||
|
||||
@rem Find java.exe
|
||||
if defined JAVA_HOME goto findJavaFromJavaHome
|
||||
|
||||
set JAVA_EXE=java.exe
|
||||
%JAVA_EXE% -version >NUL 2>&1
|
||||
if %ERRORLEVEL% equ 0 goto execute
|
||||
|
||||
echo. 1>&2
|
||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
|
||||
echo. 1>&2
|
||||
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
|
||||
echo location of your Java installation. 1>&2
|
||||
|
||||
goto fail
|
||||
|
||||
:findJavaFromJavaHome
|
||||
set JAVA_HOME=%JAVA_HOME:"=%
|
||||
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||
|
||||
if exist "%JAVA_EXE%" goto execute
|
||||
|
||||
echo. 1>&2
|
||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
|
||||
echo. 1>&2
|
||||
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
|
||||
echo location of your Java installation. 1>&2
|
||||
|
||||
goto fail
|
||||
|
||||
:execute
|
||||
@rem Setup the command line
|
||||
|
||||
set CLASSPATH=
|
||||
|
||||
|
||||
@rem Execute Gradle
|
||||
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %*
|
||||
|
||||
:end
|
||||
@rem End local scope for the variables with windows NT shell
|
||||
if %ERRORLEVEL% equ 0 goto mainEnd
|
||||
|
||||
:fail
|
||||
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
||||
rem the _cmd.exe /c_ return code!
|
||||
set EXIT_CODE=%ERRORLEVEL%
|
||||
if %EXIT_CODE% equ 0 set EXIT_CODE=1
|
||||
if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
|
||||
exit /b %EXIT_CODE%
|
||||
|
||||
:mainEnd
|
||||
if "%OS%"=="Windows_NT" endlocal
|
||||
|
||||
:omega
|
||||
@@ -1,27 +0,0 @@
|
||||
<project>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>DHSupport-${mc.api-version}${mc.api-version-patch}</artifactId>
|
||||
|
||||
<parent>
|
||||
<groupId>no.jckf</groupId>
|
||||
<artifactId>DHSupport</artifactId>
|
||||
<version>${revision}</version>
|
||||
<relativePath>./pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
<properties>
|
||||
<skip-copy>false</skip-copy>
|
||||
<mc.api-version>1.16</mc.api-version>
|
||||
<mc.api-version-patch>.5</mc.api-version-patch>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.destroystokyo.paper</groupId>
|
||||
<artifactId>paper-api</artifactId>
|
||||
<version>${mc.api-version}${mc.api-version-patch}-R0.1-SNAPSHOT</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
@@ -1,27 +0,0 @@
|
||||
<project>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>DHSupport-${mc.api-version}${mc.api-version-patch}</artifactId>
|
||||
|
||||
<parent>
|
||||
<groupId>no.jckf</groupId>
|
||||
<artifactId>DHSupport</artifactId>
|
||||
<version>${revision}</version>
|
||||
<relativePath>./pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
<properties>
|
||||
<skip-copy>false</skip-copy>
|
||||
<mc.api-version>1.17</mc.api-version>
|
||||
<mc.api-version-patch>.1</mc.api-version-patch>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>io.papermc.paper</groupId>
|
||||
<artifactId>paper-api</artifactId>
|
||||
<version>${mc.api-version}${mc.api-version-patch}-R0.1-SNAPSHOT</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
@@ -1,27 +0,0 @@
|
||||
<project>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>DHSupport-${mc.api-version}${mc.api-version-patch}</artifactId>
|
||||
|
||||
<parent>
|
||||
<groupId>no.jckf</groupId>
|
||||
<artifactId>DHSupport</artifactId>
|
||||
<version>${revision}</version>
|
||||
<relativePath>./pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
<properties>
|
||||
<skip-copy>false</skip-copy>
|
||||
<mc.api-version>1.18</mc.api-version>
|
||||
<mc.api-version-patch>.2</mc.api-version-patch>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>io.papermc.paper</groupId>
|
||||
<artifactId>paper-api</artifactId>
|
||||
<version>${mc.api-version}${mc.api-version-patch}-R0.1-SNAPSHOT</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
@@ -1,27 +0,0 @@
|
||||
<project>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>DHSupport-${mc.api-version}${mc.api-version-patch}</artifactId>
|
||||
|
||||
<parent>
|
||||
<groupId>no.jckf</groupId>
|
||||
<artifactId>DHSupport</artifactId>
|
||||
<version>${revision}</version>
|
||||
<relativePath>./pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
<properties>
|
||||
<skip-copy>false</skip-copy>
|
||||
<mc.api-version>1.19</mc.api-version>
|
||||
<mc.api-version-patch>.2</mc.api-version-patch>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>io.papermc.paper</groupId>
|
||||
<artifactId>paper-api</artifactId>
|
||||
<version>${mc.api-version}${mc.api-version-patch}-R0.1-SNAPSHOT</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
@@ -1,27 +0,0 @@
|
||||
<project>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>DHSupport-${mc.api-version}${mc.api-version-patch}</artifactId>
|
||||
|
||||
<parent>
|
||||
<groupId>no.jckf</groupId>
|
||||
<artifactId>DHSupport</artifactId>
|
||||
<version>${revision}</version>
|
||||
<relativePath>./pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
<properties>
|
||||
<skip-copy>false</skip-copy>
|
||||
<mc.api-version>1.19</mc.api-version>
|
||||
<mc.api-version-patch>.4</mc.api-version-patch>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>io.papermc.paper</groupId>
|
||||
<artifactId>paper-api</artifactId>
|
||||
<version>${mc.api-version}${mc.api-version-patch}-R0.1-SNAPSHOT</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
@@ -1,27 +0,0 @@
|
||||
<project>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>DHSupport-${mc.api-version}${mc.api-version-patch}</artifactId>
|
||||
|
||||
<parent>
|
||||
<groupId>no.jckf</groupId>
|
||||
<artifactId>DHSupport</artifactId>
|
||||
<version>${revision}</version>
|
||||
<relativePath>./pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
<properties>
|
||||
<skip-copy>false</skip-copy>
|
||||
<mc.api-version>1.20</mc.api-version>
|
||||
<mc.api-version-patch>.1</mc.api-version-patch>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>io.papermc.paper</groupId>
|
||||
<artifactId>paper-api</artifactId>
|
||||
<version>${mc.api-version}${mc.api-version-patch}-R0.1-SNAPSHOT</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
@@ -1,27 +0,0 @@
|
||||
<project>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>DHSupport-${mc.api-version}${mc.api-version-patch}</artifactId>
|
||||
|
||||
<parent>
|
||||
<groupId>no.jckf</groupId>
|
||||
<artifactId>DHSupport</artifactId>
|
||||
<version>${revision}</version>
|
||||
<relativePath>./pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
<properties>
|
||||
<skip-copy>false</skip-copy>
|
||||
<mc.api-version>1.20</mc.api-version>
|
||||
<mc.api-version-patch>.2</mc.api-version-patch>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>io.papermc.paper</groupId>
|
||||
<artifactId>paper-api</artifactId>
|
||||
<version>${mc.api-version}${mc.api-version-patch}-R0.1-SNAPSHOT</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
@@ -1,27 +0,0 @@
|
||||
<project>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>DHSupport-${mc.api-version}${mc.api-version-patch}</artifactId>
|
||||
|
||||
<parent>
|
||||
<groupId>no.jckf</groupId>
|
||||
<artifactId>DHSupport</artifactId>
|
||||
<version>${revision}</version>
|
||||
<relativePath>./pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
<properties>
|
||||
<skip-copy>false</skip-copy>
|
||||
<mc.api-version>1.20</mc.api-version>
|
||||
<mc.api-version-patch>.4</mc.api-version-patch>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>io.papermc.paper</groupId>
|
||||
<artifactId>paper-api</artifactId>
|
||||
<version>${mc.api-version}${mc.api-version-patch}-R0.1-SNAPSHOT</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
@@ -1,26 +0,0 @@
|
||||
<project>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>DHSupport-${mc.api-version}</artifactId>
|
||||
|
||||
<parent>
|
||||
<groupId>no.jckf</groupId>
|
||||
<artifactId>DHSupport</artifactId>
|
||||
<version>${revision}</version>
|
||||
<relativePath>./pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
<properties>
|
||||
<skip-copy>false</skip-copy>
|
||||
<mc.api-version>1.20.6</mc.api-version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>io.papermc.paper</groupId>
|
||||
<artifactId>paper-api</artifactId>
|
||||
<version>${mc.api-version}-R0.1-SNAPSHOT</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
@@ -1,26 +0,0 @@
|
||||
<project>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>DHSupport-${mc.api-version}</artifactId>
|
||||
|
||||
<parent>
|
||||
<groupId>no.jckf</groupId>
|
||||
<artifactId>DHSupport</artifactId>
|
||||
<version>${revision}</version>
|
||||
<relativePath>./pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
<properties>
|
||||
<skip-copy>false</skip-copy>
|
||||
<mc.api-version>1.21.1</mc.api-version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>io.papermc.paper</groupId>
|
||||
<artifactId>paper-api</artifactId>
|
||||
<version>${mc.api-version}-R0.1-SNAPSHOT</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
@@ -1,26 +0,0 @@
|
||||
<project>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>DHSupport-${mc.api-version}</artifactId>
|
||||
|
||||
<parent>
|
||||
<groupId>no.jckf</groupId>
|
||||
<artifactId>DHSupport</artifactId>
|
||||
<version>${revision}</version>
|
||||
<relativePath>./pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
<properties>
|
||||
<skip-copy>false</skip-copy>
|
||||
<mc.api-version>1.21.3</mc.api-version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>io.papermc.paper</groupId>
|
||||
<artifactId>paper-api</artifactId>
|
||||
<version>${mc.api-version}-R0.1-SNAPSHOT</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
@@ -1,26 +0,0 @@
|
||||
<project>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>DHSupport-${mc.api-version}</artifactId>
|
||||
|
||||
<parent>
|
||||
<groupId>no.jckf</groupId>
|
||||
<artifactId>DHSupport</artifactId>
|
||||
<version>${revision}</version>
|
||||
<relativePath>./pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
<properties>
|
||||
<skip-copy>false</skip-copy>
|
||||
<mc.api-version>1.21.4</mc.api-version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>io.papermc.paper</groupId>
|
||||
<artifactId>paper-api</artifactId>
|
||||
<version>${mc.api-version}-R0.1-SNAPSHOT</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
@@ -1,26 +0,0 @@
|
||||
<project>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>DHSupport-${mc.api-version}</artifactId>
|
||||
|
||||
<parent>
|
||||
<groupId>no.jckf</groupId>
|
||||
<artifactId>DHSupport</artifactId>
|
||||
<version>${revision}</version>
|
||||
<relativePath>./pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
<properties>
|
||||
<skip-copy>false</skip-copy>
|
||||
<mc.api-version>1.21.5</mc.api-version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>io.papermc.paper</groupId>
|
||||
<artifactId>paper-api</artifactId>
|
||||
<version>${mc.api-version}-R0.1-SNAPSHOT</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
@@ -1,26 +0,0 @@
|
||||
<project>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>DHSupport-${mc.api-version}</artifactId>
|
||||
|
||||
<parent>
|
||||
<groupId>no.jckf</groupId>
|
||||
<artifactId>DHSupport</artifactId>
|
||||
<version>${revision}</version>
|
||||
<relativePath>./pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
<properties>
|
||||
<skip-copy>false</skip-copy>
|
||||
<mc.api-version>1.21.7</mc.api-version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>io.papermc.paper</groupId>
|
||||
<artifactId>paper-api</artifactId>
|
||||
<version>${mc.api-version}-R0.1-SNAPSHOT</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
@@ -1,233 +0,0 @@
|
||||
<project>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<groupId>no.jckf</groupId>
|
||||
<artifactId>DHSupport</artifactId>
|
||||
<version>${revision}</version>
|
||||
<packaging>pom</packaging>
|
||||
|
||||
<properties>
|
||||
<revision>0.10.0</revision>
|
||||
<jdk>21</jdk>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<main.class>no.jckf.dhsupport.bukkit.DhSupportBukkitPlugin</main.class>
|
||||
<mc.api-version></mc.api-version>
|
||||
<mc.api-version-patch></mc.api-version-patch>
|
||||
<skip-copy>true</skip-copy>
|
||||
</properties>
|
||||
|
||||
<developers>
|
||||
<developer>
|
||||
<id>jckf</id>
|
||||
<name>Jim C K Flaten</name>
|
||||
<email>jckf@jckf.no</email>
|
||||
</developer>
|
||||
</developers>
|
||||
|
||||
<repositories>
|
||||
<repository>
|
||||
<id>central</id>
|
||||
<url>https://repo1.maven.org/maven2/</url>
|
||||
</repository>
|
||||
|
||||
<repository>
|
||||
<id>papermc</id>
|
||||
<url>https://repo.papermc.io/repository/maven-public</url>
|
||||
</repository>
|
||||
|
||||
<repository>
|
||||
<id>jitpack</id>
|
||||
<url>https://jitpack.io</url>
|
||||
</repository>
|
||||
</repositories>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<version>4.11</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.bstats</groupId>
|
||||
<artifactId>bstats-bukkit</artifactId>
|
||||
<version>3.1.0</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.google.guava</groupId>
|
||||
<artifactId>guava</artifactId>
|
||||
<version>33.3.1-jre</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.json</groupId>
|
||||
<artifactId>json</artifactId>
|
||||
<version>20240303</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.jetbrains</groupId>
|
||||
<artifactId>annotations</artifactId>
|
||||
<version>25.0.0</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.tukaani</groupId>
|
||||
<artifactId>xz</artifactId>
|
||||
<version>1.10</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.github.technicallycoded</groupId>
|
||||
<artifactId>FoliaLib</artifactId>
|
||||
<version>v0.4.3</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<defaultGoal>clean test package</defaultGoal>
|
||||
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>3.13.0</version>
|
||||
|
||||
<configuration>
|
||||
<source>${jdk}</source>
|
||||
<target>${jdk}</target>
|
||||
<encoding>${project.build.sourceEncoding}</encoding>
|
||||
</configuration>
|
||||
</plugin>
|
||||
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-shade-plugin</artifactId>
|
||||
<version>3.6.0</version>
|
||||
|
||||
<configuration>
|
||||
<shadedArtifactAttached>false</shadedArtifactAttached>
|
||||
<shadedClassifierName>all</shadedClassifierName>
|
||||
|
||||
<relocations>
|
||||
<relocation>
|
||||
<pattern>org.bstats</pattern>
|
||||
<shadedPattern>no.jckf.dhsupport.bstats</shadedPattern>
|
||||
</relocation>
|
||||
|
||||
<relocation>
|
||||
<pattern>com.tcoded.folialib</pattern>
|
||||
<shadedPattern>no.jckf.dhsupport.folialib</shadedPattern>
|
||||
</relocation>
|
||||
</relocations>
|
||||
</configuration>
|
||||
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>package</phase>
|
||||
|
||||
<goals>
|
||||
<goal>shade</goal>
|
||||
</goals>
|
||||
|
||||
<configuration>
|
||||
<filters>
|
||||
<filter>
|
||||
<artifact>*:*</artifact>
|
||||
<excludes>
|
||||
<exclude>META-INF/*</exclude>
|
||||
</excludes>
|
||||
</filter>
|
||||
</filters>
|
||||
<artifactSet>
|
||||
<includes>
|
||||
<include>org.json</include>
|
||||
<include>org.tukaani</include>
|
||||
<include>org.bstats</include>
|
||||
<include>com.github.technicallycoded</include>
|
||||
</includes>
|
||||
</artifactSet>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-dependency-plugin</artifactId>
|
||||
<version>3.8.0</version>
|
||||
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>package</phase>
|
||||
|
||||
<goals>
|
||||
<goal>copy</goal>
|
||||
</goals>
|
||||
|
||||
<configuration>
|
||||
<outputDirectory>jars</outputDirectory>
|
||||
|
||||
<skip>${skip-copy}</skip>
|
||||
|
||||
<artifactItems>
|
||||
<artifactItem>
|
||||
<groupId>${project.groupId}</groupId>
|
||||
<artifactId>${project.artifactId}</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<type>${project.packaging}</type>
|
||||
<destFileName>DistantHorizonsSupport-Bukkit-Paper-${project.version}-${mc.api-version}${mc.api-version-patch}.jar</destFileName>
|
||||
</artifactItem>
|
||||
</artifactItems>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
|
||||
<resources>
|
||||
<resource>
|
||||
<directory>${basedir}/src/main/resources</directory>
|
||||
<filtering>true</filtering>
|
||||
<targetPath>.</targetPath>
|
||||
|
||||
<includes>
|
||||
<include>plugin.yml</include>
|
||||
</includes>
|
||||
</resource>
|
||||
|
||||
<resource>
|
||||
<directory>${basedir}/src/main/resources</directory>
|
||||
<targetPath>.</targetPath>
|
||||
|
||||
<includes>
|
||||
<include>config.yml</include>
|
||||
</includes>
|
||||
</resource>
|
||||
</resources>
|
||||
</build>
|
||||
|
||||
<modules>
|
||||
<module>pom-1.16.5.xml</module>
|
||||
<module>pom-1.17.1.xml</module>
|
||||
<module>pom-1.18.2.xml</module>
|
||||
<module>pom-1.19.2.xml</module>
|
||||
<module>pom-1.19.4.xml</module>
|
||||
<module>pom-1.20.1.xml</module>
|
||||
<module>pom-1.20.2.xml</module>
|
||||
<module>pom-1.20.4.xml</module>
|
||||
<module>pom-1.20.6.xml</module>
|
||||
<module>pom-1.21.1.xml</module>
|
||||
<module>pom-1.21.3.xml</module>
|
||||
<module>pom-1.21.4.xml</module>
|
||||
<module>pom-1.21.5.xml</module>
|
||||
<module>pom-1.21.7.xml</module>
|
||||
</modules>
|
||||
</project>
|
||||
@@ -0,0 +1 @@
|
||||
rootProject.name = 'DistantHorizonsSupport'
|
||||
@@ -36,6 +36,8 @@ public class BukkitScheduler implements Scheduler
|
||||
|
||||
protected ExecutorService executor;
|
||||
|
||||
protected ScheduledExecutorService scheduledExecutor;
|
||||
|
||||
public BukkitScheduler(DhSupportBukkitPlugin plugin)
|
||||
{
|
||||
this.plugin = plugin;
|
||||
@@ -47,18 +49,29 @@ public class BukkitScheduler implements Scheduler
|
||||
this.executor = new ThreadPoolExecutor(
|
||||
threadCount, threadCount,
|
||||
60, TimeUnit.SECONDS,
|
||||
new LinkedBlockingQueue<>()
|
||||
new LinkedBlockingQueue<>(),
|
||||
new ThreadFactory() {
|
||||
private final java.util.concurrent.atomic.AtomicInteger threadNumber = new java.util.concurrent.atomic.AtomicInteger(1);
|
||||
|
||||
@Override
|
||||
public Thread newThread(Runnable r)
|
||||
{
|
||||
return new Thread(r, "DHSupport-Worker-" + threadNumber.getAndIncrement());
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
((ThreadPoolExecutor) this.executor).allowCoreThreadTimeOut(true);
|
||||
|
||||
this.scheduledExecutor = Executors.newSingleThreadScheduledExecutor(r -> new Thread(r, "DHSupport-Scheduler"));
|
||||
|
||||
this.plugin.getDhSupport().info("Using " + Utils.ucFirst(this.foliaLib.getImplType().name().toLowerCase().replace('_', ' ')) + " scheduler.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canReadWorldAsync()
|
||||
{
|
||||
return !this.foliaLib.isFolia();
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -66,13 +79,49 @@ public class BukkitScheduler implements Scheduler
|
||||
{
|
||||
CompletableFuture<U> future = new CompletableFuture<>();
|
||||
|
||||
this.foliaLib.getScheduler().runNextTick((task) -> {
|
||||
if (this.foliaLib.getScheduler().isGlobalTickThread()) {
|
||||
future.complete(supplier.get());
|
||||
} else {
|
||||
this.foliaLib.getScheduler().runNextTick((task) -> {
|
||||
try {
|
||||
future.complete(supplier.get());
|
||||
} catch (Exception exception) {
|
||||
future.completeExceptionally(exception);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return future;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <U> CompletableFuture<U> runOnMainThreadDelayed(Supplier<U> supplier, long delayTicks)
|
||||
{
|
||||
CompletableFuture<U> future = new CompletableFuture<>();
|
||||
|
||||
this.foliaLib.getScheduler().runLater((task) -> {
|
||||
try {
|
||||
future.complete(supplier.get());
|
||||
} catch (Exception exception) {
|
||||
future.completeExceptionally(exception);
|
||||
}
|
||||
});
|
||||
}, delayTicks);
|
||||
|
||||
return future;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <U> CompletableFuture<U> runOnMainThreadDelayed(Supplier<U> supplier, long delayAmount, TimeUnit delayUnit)
|
||||
{
|
||||
CompletableFuture<U> future = new CompletableFuture<>();
|
||||
|
||||
this.foliaLib.getScheduler().runLater((task) -> {
|
||||
try {
|
||||
future.complete(supplier.get());
|
||||
} catch (Exception exception) {
|
||||
future.completeExceptionally(exception);
|
||||
}
|
||||
}, delayAmount, delayUnit);
|
||||
|
||||
return future;
|
||||
}
|
||||
@@ -89,17 +138,67 @@ public class BukkitScheduler implements Scheduler
|
||||
z
|
||||
);
|
||||
|
||||
this.foliaLib.getScheduler().runAtLocation(
|
||||
location,
|
||||
(task) -> {
|
||||
try {
|
||||
future.complete(supplier.get());
|
||||
} catch (Exception exception) {
|
||||
future.completeExceptionally(exception);
|
||||
}
|
||||
}
|
||||
if (this.foliaLib.getScheduler().isOwnedByCurrentRegion(location)) {
|
||||
future.complete(supplier.get());
|
||||
} else {
|
||||
this.foliaLib.getScheduler().runAtLocation(
|
||||
location,
|
||||
(task) -> {
|
||||
try {
|
||||
future.complete(supplier.get());
|
||||
} catch (Exception exception) {
|
||||
future.completeExceptionally(exception);
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
return future;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <U> CompletableFuture<U> runOnRegionThreadDelayed(UUID worldId, int x, int z, Supplier<U> supplier, long delayTicks)
|
||||
{
|
||||
CompletableFuture<U> future = new CompletableFuture<>();
|
||||
|
||||
Location location = new Location(
|
||||
this.plugin.getServer().getWorld(worldId),
|
||||
x,
|
||||
0,
|
||||
z
|
||||
);
|
||||
|
||||
this.foliaLib.getScheduler().runAtLocationLater(location, (task) -> {
|
||||
try {
|
||||
future.complete(supplier.get());
|
||||
} catch (Exception exception) {
|
||||
future.completeExceptionally(exception);
|
||||
}
|
||||
}, delayTicks);
|
||||
|
||||
return future;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <U> CompletableFuture<U> runOnRegionThreadDelayed(UUID worldId, int x, int z, Supplier<U> supplier, long delayAmount, TimeUnit delayUnit)
|
||||
{
|
||||
CompletableFuture<U> future = new CompletableFuture<>();
|
||||
|
||||
Location location = new Location(
|
||||
this.plugin.getServer().getWorld(worldId),
|
||||
x,
|
||||
0,
|
||||
z
|
||||
);
|
||||
|
||||
this.foliaLib.getScheduler().runAtLocationLater(location, (task) -> {
|
||||
try {
|
||||
future.complete(supplier.get());
|
||||
} catch (Exception exception) {
|
||||
future.completeExceptionally(exception);
|
||||
}
|
||||
}, delayAmount, delayUnit);
|
||||
|
||||
return future;
|
||||
}
|
||||
|
||||
@@ -119,10 +218,37 @@ public class BukkitScheduler implements Scheduler
|
||||
return future;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <U> CompletableFuture<U> runOnSeparateThreadDelayed(Supplier<U> supplier, long delayTicks)
|
||||
{
|
||||
return this.runOnSeparateThreadDelayed(supplier, delayTicks * 50L, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <U> CompletableFuture<U> runOnSeparateThreadDelayed(Supplier<U> supplier, long delayAmount, TimeUnit delayUnit)
|
||||
{
|
||||
CompletableFuture<U> future = new CompletableFuture<>();
|
||||
|
||||
this.scheduledExecutor.schedule(() -> {
|
||||
this.runOnSeparateThread(() -> {
|
||||
try {
|
||||
future.complete(supplier.get());
|
||||
} catch (Exception exception) {
|
||||
future.completeExceptionally(exception);
|
||||
}
|
||||
|
||||
return null;
|
||||
});
|
||||
}, delayAmount, delayUnit);
|
||||
|
||||
return future;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cancelTasks()
|
||||
{
|
||||
this.executor.shutdown();
|
||||
this.scheduledExecutor.shutdownNow();
|
||||
this.executor.shutdownNow();
|
||||
this.foliaLib.getScheduler().cancelAllTasks();
|
||||
}
|
||||
|
||||
|
||||
@@ -19,21 +19,20 @@
|
||||
package no.jckf.dhsupport.bukkit;
|
||||
|
||||
import no.jckf.dhsupport.core.Coordinates;
|
||||
import no.jckf.dhsupport.core.Utils;
|
||||
import no.jckf.dhsupport.core.configuration.Configuration;
|
||||
import no.jckf.dhsupport.core.configuration.DhsConfig;
|
||||
import no.jckf.dhsupport.core.configuration.WorldConfiguration;
|
||||
import no.jckf.dhsupport.core.scheduling.Scheduler;
|
||||
import no.jckf.dhsupport.core.world.MaterialRecord;
|
||||
import no.jckf.dhsupport.core.world.WorldInterface;
|
||||
import org.bukkit.*;
|
||||
import org.bukkit.block.Beacon;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.io.File;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.UUID;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
@@ -53,7 +52,20 @@ public class BukkitWorldInterface implements WorldInterface
|
||||
|
||||
protected Logger logger;
|
||||
|
||||
protected Map<String, ChunkSnapshot> chunks = new HashMap<>();
|
||||
protected boolean dummyMode = false;
|
||||
|
||||
protected Map<Integer, String> dummyColumn;
|
||||
|
||||
protected boolean trustHeightMap = true;
|
||||
|
||||
private Integer sectionX;
|
||||
private Integer sectionZ;
|
||||
|
||||
private final ChunkSnapshot[] chunks = new ChunkSnapshot[16];
|
||||
|
||||
protected EnumMap<Material, MaterialRecord> materialCache = new EnumMap<>(Material.class);
|
||||
|
||||
protected Map<String, Map<String, String>> propertyCache = new HashMap<>();
|
||||
|
||||
protected UnsafeValues unsafeValues;
|
||||
|
||||
@@ -66,12 +78,17 @@ public class BukkitWorldInterface implements WorldInterface
|
||||
@Nullable
|
||||
protected Method getChunkAtAsync;
|
||||
|
||||
protected int lightOffset = 0;
|
||||
|
||||
public BukkitWorldInterface(DhSupportBukkitPlugin plugin, World world, Configuration config)
|
||||
{
|
||||
this.plugin = plugin;
|
||||
this.world = world;
|
||||
this.config = config;
|
||||
this.worldConfig = new WorldConfiguration(this, config);
|
||||
|
||||
this.lightOffset = this.getConfig().getInt(DhsConfig.LIGHT_OFFSET_HACK, this.plugin.getDhSupport().getGameVersion().equals("1.20.1") ? this.world.getMinHeight() : 0);
|
||||
this.trustHeightMap = this.getConfig().getBool(DhsConfig.TRUST_HEIGHT_MAP, this.trustHeightMap);
|
||||
}
|
||||
|
||||
public void setLogger(Logger logger)
|
||||
@@ -137,19 +154,47 @@ public class BukkitWorldInterface implements WorldInterface
|
||||
int chunkX = Coordinates.blockToChunk(x);
|
||||
int chunkZ = Coordinates.blockToChunk(z);
|
||||
|
||||
String key = chunkX + "x" + chunkZ;
|
||||
int sectionX = Coordinates.chunkToSection(chunkX);
|
||||
int sectionZ = Coordinates.chunkToSection(chunkZ);
|
||||
|
||||
if (this.chunks.containsKey(key)) {
|
||||
return this.chunks.get(key);
|
||||
if (this.sectionX == null) {
|
||||
this.sectionX = sectionX;
|
||||
this.sectionZ = sectionZ;
|
||||
} else if (this.sectionX != sectionX || this.sectionZ != sectionZ) {
|
||||
throw new IllegalStateException("We appear to be leaking world interface instances across LOD sections");
|
||||
}
|
||||
|
||||
ChunkSnapshot chunk = this.world.getChunkAt(chunkX, chunkZ).getChunkSnapshot(true, true, false);
|
||||
int baseChunkX = Coordinates.sectionToChunk(sectionX);
|
||||
int baseChunkZ = Coordinates.sectionToChunk(sectionZ);
|
||||
|
||||
this.chunks.put(key, chunk);
|
||||
int index = (chunkX - baseChunkX) + ((chunkZ - baseChunkZ) << 2);
|
||||
|
||||
ChunkSnapshot chunk = this.chunks[index];
|
||||
|
||||
if (chunk != null) {
|
||||
return chunk;
|
||||
}
|
||||
|
||||
Scheduler scheduler = this.plugin.getDhSupport().getScheduler();
|
||||
|
||||
if (scheduler.canReadWorldAsync()) {
|
||||
chunk = this.world.getChunkAt(chunkX, chunkZ).getChunkSnapshot(true, true, false);
|
||||
} else {
|
||||
chunk = scheduler.runOnRegionThread(world.getUID(), x, z, () ->
|
||||
this.world.getChunkAt(chunkX, chunkZ).getChunkSnapshot(true, true, false)
|
||||
).join();
|
||||
}
|
||||
|
||||
this.chunks[index] = chunk;
|
||||
|
||||
return chunk;
|
||||
}
|
||||
|
||||
public boolean isInsideSection(int x, int z)
|
||||
{
|
||||
return Coordinates.blockToSection(x) == this.sectionX && Coordinates.blockToSection(z) == this.sectionZ;
|
||||
}
|
||||
|
||||
@Override
|
||||
public UUID getId()
|
||||
{
|
||||
@@ -189,6 +234,60 @@ public class BukkitWorldInterface implements WorldInterface
|
||||
return this.worldConfig.getBool(DhsConfig.USE_VANILLA_WORLD_BORDER, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDummyMode(boolean enable)
|
||||
{
|
||||
this.dummyMode = enable;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDummyMode()
|
||||
{
|
||||
return this.dummyMode;
|
||||
}
|
||||
|
||||
protected Map<Integer, String> buildDummyColumn()
|
||||
{
|
||||
int minY = this.getMinY();
|
||||
|
||||
Map<Integer, String> column = new HashMap<>();
|
||||
|
||||
for (String spec : this.getConfig().getStringList(DhsConfig.DUMMY_CHUNK)) {
|
||||
String[] components = spec.toLowerCase().split(",", 2);
|
||||
|
||||
String namespacedKey = Utils.namespaceKey(components[1]);
|
||||
|
||||
if (namespacedKey.equals("minecraft:air") || namespacedKey.equals("minecraft:void_air")) {
|
||||
continue;
|
||||
}
|
||||
|
||||
String[] range = components[0].split("-", 2);
|
||||
|
||||
int rangeStart = Integer.parseInt(range[0]);
|
||||
int rangeEnd = range.length == 1 ? rangeStart : Integer.parseInt(range[1]);
|
||||
|
||||
for (int i = rangeStart; i <= rangeEnd; i++) {
|
||||
column.put(minY + i, namespacedKey);
|
||||
}
|
||||
}
|
||||
|
||||
return column;
|
||||
}
|
||||
|
||||
protected Map<Integer, String> getDummyColumn()
|
||||
{
|
||||
if (this.dummyColumn == null) {
|
||||
this.dummyColumn = this.buildDummyColumn();
|
||||
}
|
||||
|
||||
return this.dummyColumn;
|
||||
}
|
||||
|
||||
protected String getDummyMaterialAt(int y)
|
||||
{
|
||||
return this.getDummyColumn().getOrDefault(y, "minecraft:air");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer getWorldBorderX()
|
||||
{
|
||||
@@ -230,6 +329,10 @@ public class BukkitWorldInterface implements WorldInterface
|
||||
@Override
|
||||
public boolean chunkExists(int x, int z)
|
||||
{
|
||||
if (this.isDummyMode()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
int chunkX = Coordinates.blockToChunk(x);
|
||||
int chunkZ = Coordinates.blockToChunk(z);
|
||||
|
||||
@@ -260,6 +363,10 @@ public class BukkitWorldInterface implements WorldInterface
|
||||
@Override
|
||||
public boolean isChunkLoaded(int x, int z)
|
||||
{
|
||||
if (this.isDummyMode()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
int chunkX = Coordinates.blockToChunk(x);
|
||||
int chunkZ = Coordinates.blockToChunk(z);
|
||||
|
||||
@@ -269,6 +376,10 @@ public class BukkitWorldInterface implements WorldInterface
|
||||
@Override
|
||||
public boolean loadChunk(int x, int z)
|
||||
{
|
||||
if (this.isDummyMode()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
int chunkX = Coordinates.blockToChunk(x);
|
||||
int chunkZ = Coordinates.blockToChunk(z);
|
||||
|
||||
@@ -278,6 +389,10 @@ public class BukkitWorldInterface implements WorldInterface
|
||||
@Override
|
||||
public boolean loadOrGenerateChunk(int x, int z)
|
||||
{
|
||||
if (this.isDummyMode()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
int chunkX = Coordinates.blockToChunk(x);
|
||||
int chunkZ = Coordinates.blockToChunk(z);
|
||||
|
||||
@@ -287,6 +402,10 @@ public class BukkitWorldInterface implements WorldInterface
|
||||
@Override
|
||||
public CompletableFuture<Boolean> loadChunkAsync(int x, int z)
|
||||
{
|
||||
if (this.isDummyMode()) {
|
||||
return CompletableFuture.completedFuture(true);
|
||||
}
|
||||
|
||||
if (this.getChunkAtAsync == null) {
|
||||
return this.plugin.getDhSupport().getScheduler().runOnMainThread(() -> this.loadChunk(x, z));
|
||||
}
|
||||
@@ -306,6 +425,10 @@ public class BukkitWorldInterface implements WorldInterface
|
||||
@Override
|
||||
public CompletableFuture<Boolean> loadOrGenerateChunkAsync(int x, int z)
|
||||
{
|
||||
if (this.isDummyMode()) {
|
||||
return CompletableFuture.completedFuture(true);
|
||||
}
|
||||
|
||||
if (this.getChunkAtAsync == null) {
|
||||
return this.plugin.getDhSupport().getScheduler().runOnMainThread(() -> this.loadOrGenerateChunk(x, z));
|
||||
}
|
||||
@@ -325,6 +448,10 @@ public class BukkitWorldInterface implements WorldInterface
|
||||
@Override
|
||||
public boolean unloadChunk(int x, int z)
|
||||
{
|
||||
if (this.isDummyMode()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
int chunkX = Coordinates.blockToChunk(x);
|
||||
int chunkZ = Coordinates.blockToChunk(z);
|
||||
|
||||
@@ -334,6 +461,10 @@ public class BukkitWorldInterface implements WorldInterface
|
||||
@Override
|
||||
public boolean unloadChunkAsync(int x, int z)
|
||||
{
|
||||
if (this.isDummyMode()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
int chunkX = Coordinates.blockToChunk(x);
|
||||
int chunkZ = Coordinates.blockToChunk(z);
|
||||
|
||||
@@ -343,6 +474,10 @@ public class BukkitWorldInterface implements WorldInterface
|
||||
@Override
|
||||
public boolean discardChunk(int x, int z)
|
||||
{
|
||||
if (this.isDummyMode()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
int chunkX = Coordinates.blockToChunk(x);
|
||||
int chunkZ = Coordinates.blockToChunk(z);
|
||||
|
||||
@@ -367,21 +502,51 @@ public class BukkitWorldInterface implements WorldInterface
|
||||
return Math.min(Math.max(this.getMinY(), this.world.getSeaLevel()), this.getMaxY());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSpawnX()
|
||||
{
|
||||
return this.world.getSpawnLocation().getBlockX();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSpawnY()
|
||||
{
|
||||
return this.world.getSpawnLocation().getBlockY();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSpawnZ()
|
||||
{
|
||||
return this.world.getSpawnLocation().getBlockZ();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getHighestYAt(int x, int z)
|
||||
{
|
||||
if (!this.trustHeightMap) {
|
||||
return this.getMaxY() - 1;
|
||||
}
|
||||
|
||||
if (this.isDummyMode()) {
|
||||
return this.getDummyColumn().isEmpty() ? this.getMinY() : Collections.max(this.getDummyColumn().keySet());
|
||||
}
|
||||
|
||||
return this.getChunk(x, z).getHighestBlockYAt(Coordinates.blockToChunkRelative(x), Coordinates.blockToChunkRelative(z));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getBiomeAt(int x, int z)
|
||||
public String getBiomeAt(int x, int y, int z)
|
||||
{
|
||||
NamespacedKey key = this.getChunk(x, z).getBiome(Coordinates.blockToChunkRelative(x), this.getSeaLevel(), Coordinates.blockToChunkRelative(z)).getKey();
|
||||
if (this.isDummyMode()) {
|
||||
return "minecraft:plains";
|
||||
}
|
||||
|
||||
NamespacedKey key = this.getChunk(x, z).getBiome(Coordinates.blockToChunkRelative(x), y, Coordinates.blockToChunkRelative(z)).getKey();
|
||||
|
||||
// If the server just reports "custom" and we have access to getBiomeKey, try to get the correct biome name.
|
||||
if (key.toString().equals("minecraft:custom") && this.getBiomeKey != null) {
|
||||
try {
|
||||
key = (NamespacedKey) this.getBiomeKey.invoke(this.unsafeValues, this.world, x, this.getSeaLevel(), z);
|
||||
key = (NamespacedKey) this.getBiomeKey.invoke(this.unsafeValues, this.world, x, y, z);
|
||||
} catch (IllegalAccessException | InvocationTargetException exception) {
|
||||
throw new RuntimeException(exception);
|
||||
}
|
||||
@@ -390,52 +555,129 @@ public class BukkitWorldInterface implements WorldInterface
|
||||
return key.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getBiomeAt(int x, int z)
|
||||
{
|
||||
return this.getBiomeAt(x, this.getSeaLevel(), z);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMaterialAt(int x, int y, int z)
|
||||
{
|
||||
return this.getChunk(x, z).getBlockType(Coordinates.blockToChunkRelative(x), y, Coordinates.blockToChunkRelative(z)).getKey().toString();
|
||||
if (this.isDummyMode()) {
|
||||
return Utils.namespaceKey(this.getDummyMaterialAt(y));
|
||||
}
|
||||
|
||||
return this.getMaterialRecordAt(x, y, z).toString();
|
||||
}
|
||||
|
||||
public MaterialRecord getMaterialRecordAt(int x, int y, int z)
|
||||
{
|
||||
return this.materialCache.computeIfAbsent(
|
||||
this.getChunk(x, z).getBlockType(Coordinates.blockToChunkRelative(x), y, Coordinates.blockToChunkRelative(z)),
|
||||
(mat) -> {
|
||||
String key = Utils.unNamespaceKey(mat.getKey().toString());
|
||||
String mapped = this.config.getString(DhsConfig.MATERIAL_MAP + '.' + key, key);
|
||||
|
||||
return new MaterialRecord(
|
||||
Utils.namespaceKey(mapped == null ? key : mapped),
|
||||
!key.equals(mapped)
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getBlockStateAsStringAt(int x, int y, int z)
|
||||
{
|
||||
return this.getChunk(x, z).getBlockData(Coordinates.blockToChunkRelative(x), y, Coordinates.blockToChunkRelative(z)).getAsString();
|
||||
MaterialRecord materialRecord = this.getMaterialRecordAt(x, y, z);
|
||||
|
||||
if (this.isDummyMode() || materialRecord.isMapped()) {
|
||||
return materialRecord.toString();
|
||||
}
|
||||
|
||||
Map<String, String> properties = this.getBlockPropertiesAt(x, y, z);
|
||||
|
||||
if (properties.isEmpty()) {
|
||||
return materialRecord.toString();
|
||||
}
|
||||
|
||||
StringBuilder builder = new StringBuilder();
|
||||
|
||||
builder.append(materialRecord.toString())
|
||||
.append('[');
|
||||
|
||||
for (Map.Entry<String, String> property : properties.entrySet()) {
|
||||
builder.append(property.getKey())
|
||||
.append('=')
|
||||
.append(property.getValue())
|
||||
.append(',');
|
||||
}
|
||||
|
||||
builder.setLength(builder.length() - 1);
|
||||
builder.append(']');
|
||||
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, String> getBlockPropertiesAt(int x, int y, int z)
|
||||
{
|
||||
Map<String, String> properties = new HashMap<>();
|
||||
if (this.isDummyMode()) {
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
|
||||
String dataString = this.getBlockStateAsStringAt(x, y, z);
|
||||
String dataString = this.getChunk(x, z).getBlockData(Coordinates.blockToChunkRelative(x), y, Coordinates.blockToChunkRelative(z)).getAsString();
|
||||
|
||||
int kvStart = dataString.indexOf("[");
|
||||
Map<String, String> cached = this.propertyCache.get(dataString);
|
||||
|
||||
if (cached != null) {
|
||||
return cached;
|
||||
}
|
||||
|
||||
int kvStart = dataString.indexOf('[');
|
||||
|
||||
if (kvStart == -1) {
|
||||
return properties;
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
|
||||
String[] kvStrings = dataString.substring(kvStart + 1, dataString.length() - 1).split(",");
|
||||
|
||||
Arrays.sort(kvStrings);
|
||||
|
||||
Map<String, String> properties = new LinkedHashMap<>(kvStrings.length);
|
||||
|
||||
for (String kvString : kvStrings) {
|
||||
String[] kv = kvString.split("=", 2);
|
||||
|
||||
properties.put(kv[0], kv[1]);
|
||||
}
|
||||
|
||||
properties = Collections.unmodifiableMap(properties);
|
||||
|
||||
this.propertyCache.put(dataString, properties);
|
||||
|
||||
return properties;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte getBlockLightAt(int x, int y, int z)
|
||||
{
|
||||
return (byte) this.getChunk(x, z).getBlockEmittedLight(Coordinates.blockToChunkRelative(x), y, Coordinates.blockToChunkRelative(z));
|
||||
if (this.isDummyMode()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return (byte) this.getChunk(x, z).getBlockEmittedLight(Coordinates.blockToChunkRelative(x), Math.clamp(y + this.lightOffset, this.getMinY(), this.getMaxY() - 1), Coordinates.blockToChunkRelative(z));
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte getSkyLightAt(int x, int y, int z)
|
||||
{
|
||||
return (byte) this.getChunk(x, z).getBlockSkyLight(Coordinates.blockToChunkRelative(x), y, Coordinates.blockToChunkRelative(z));
|
||||
if (this.isDummyMode()) {
|
||||
return (byte) (this.getDummyMaterialAt(y + 1).equals("minecraft:air") ? 15 : 0);
|
||||
}
|
||||
|
||||
return (byte) this.getChunk(x, z).getBlockSkyLight(Coordinates.blockToChunkRelative(x), Math.clamp(y + this.lightOffset, this.getMinY(), this.getMaxY() - 1), Coordinates.blockToChunkRelative(z));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -447,14 +689,126 @@ public class BukkitWorldInterface implements WorldInterface
|
||||
@Override
|
||||
public boolean isBeacon(int x, int y, int z)
|
||||
{
|
||||
if (!this.getMaterialAt(x, y, z).equals("minecraft:beacon")) {
|
||||
if (this.isDummyMode()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return this.plugin.getDhSupport().getScheduler()
|
||||
.runOnRegionThread(this.world.getUID(), x, z, () ->
|
||||
((Beacon) this.world.getBlockAt(x, y, z).getState()).getTier() > 0
|
||||
)
|
||||
.join();
|
||||
if (!this.getChunk(x, z).getBlockType(Coordinates.blockToChunkRelative(x), y, Coordinates.blockToChunkRelative(z)).equals(Material.BEACON)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return this.hasTierOneBeaconBase(x, y, z) && this.hasClearBeaconBeamPath(x, y, z);
|
||||
}
|
||||
|
||||
public boolean hasTierOneBeaconBase(int x, int y, int z)
|
||||
{
|
||||
int baseY = y - 1;
|
||||
|
||||
if (baseY < this.getMinY()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int checkX = x - 1; checkX <= x + 1; checkX++) {
|
||||
for (int checkZ = z - 1; checkZ <= z + 1; checkZ++) {
|
||||
if (this.isInsideSection(checkX, checkZ) && !this.isBeaconBaseBlock(this.getMaterialAt(checkX, baseY, checkZ))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean isBeaconBaseBlock(String material)
|
||||
{
|
||||
switch (material) {
|
||||
case "minecraft:iron_block":
|
||||
case "minecraft:gold_block":
|
||||
case "minecraft:diamond_block":
|
||||
case "minecraft:emerald_block":
|
||||
case "minecraft:netherite_block":
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean hasClearBeaconBeamPath(int x, int y, int z)
|
||||
{
|
||||
for (int checkY = y + 1; checkY < this.getMaxY(); checkY++) {
|
||||
String material = this.getMaterialAt(x, checkY, z);
|
||||
|
||||
switch (material) {
|
||||
case "minecraft:air":
|
||||
case "minecraft:cave_air":
|
||||
case "minecraft:void_air":
|
||||
case "minecraft:glass":
|
||||
continue;
|
||||
default:
|
||||
return material.endsWith("_stained_glass");
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getBeaconColor(int x, int y, int z)
|
||||
{
|
||||
int skipped = 0;
|
||||
int n = 0;
|
||||
int red = 0;
|
||||
int green = 0;
|
||||
int blue = 0;
|
||||
|
||||
int maxY = this.getMaxY();
|
||||
|
||||
while (y + 1 < maxY) {
|
||||
y++;
|
||||
|
||||
String material = Utils.unNamespaceKey(this.getMaterialAt(x, y, z)).toUpperCase();
|
||||
|
||||
if (!material.endsWith("_STAINED_GLASS") && !material.endsWith("_STAINED_GLASS_PANE")) {
|
||||
skipped++;
|
||||
|
||||
if (skipped >= 5) {
|
||||
break;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
n++;
|
||||
|
||||
Color color = null;
|
||||
|
||||
for (DyeColor testColor : DyeColor.values()) {
|
||||
if (material.startsWith(testColor.name())) {
|
||||
color = testColor.getColor();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (color == null) {
|
||||
this.getLogger().warning("Encountered unknown color: " + material);
|
||||
continue;
|
||||
}
|
||||
|
||||
red += color.getRed();
|
||||
green += color.getGreen();
|
||||
blue += color.getBlue();
|
||||
|
||||
if (n > 1) {
|
||||
red /= 2;
|
||||
green /= 2;
|
||||
blue /= 2;
|
||||
}
|
||||
}
|
||||
|
||||
if (n == 0) {
|
||||
return Color.WHITE.asRGB();
|
||||
}
|
||||
|
||||
return (new java.awt.Color(red, green, blue)).getRGB();
|
||||
}
|
||||
}
|
||||
@@ -25,14 +25,20 @@ import no.jckf.dhsupport.bukkit.handler.PlayerHandler;
|
||||
import no.jckf.dhsupport.bukkit.handler.PluginMessageProxy;
|
||||
import no.jckf.dhsupport.bukkit.handler.WorldHandler;
|
||||
import no.jckf.dhsupport.core.DhSupport;
|
||||
import no.jckf.dhsupport.core.configuration.Configuration;
|
||||
import no.jckf.dhsupport.core.configuration.DhsConfig;
|
||||
import org.bstats.bukkit.Metrics;
|
||||
import org.bstats.charts.MultiLineChart;
|
||||
import org.bstats.charts.SingleLineChart;
|
||||
import org.bukkit.Server;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.configuration.file.YamlConfiguration;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.io.InputStreamReader;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class DhSupportBukkitPlugin extends JavaPlugin
|
||||
{
|
||||
@@ -50,25 +56,37 @@ public class DhSupportBukkitPlugin extends JavaPlugin
|
||||
public void onEnable()
|
||||
{
|
||||
String pluginVersion = "N/A";
|
||||
String mcVersion = "N/A";
|
||||
|
||||
try {
|
||||
YamlConfiguration meta = new YamlConfiguration();
|
||||
meta.load(new InputStreamReader(this.getResource("plugin.yml")));
|
||||
|
||||
pluginVersion = meta.getString("version");
|
||||
mcVersion = meta.getString("mc-version");
|
||||
} catch (Exception exception) {
|
||||
|
||||
}
|
||||
|
||||
this.getLogger().info("I am DHS " + pluginVersion + " for MC " + mcVersion + ".");
|
||||
String gameVersion = this.getGameVersion();
|
||||
|
||||
this.dhSupport = new DhSupport(pluginVersion, mcVersion);
|
||||
this.getLogger().info("I am DHS " + pluginVersion + " running on " + this.getServer().getName() + " for Minecraft " + gameVersion + ".");
|
||||
|
||||
this.dhSupport = new DhSupport(pluginVersion, gameVersion);
|
||||
this.dhSupport.setLogger(this.getLogger());
|
||||
this.dhSupport.setDataDirectory(this.getDataFolder().getAbsolutePath());
|
||||
|
||||
this.metrics = new Metrics(this, 21843);
|
||||
this.metrics.addCustomChart(new SingleLineChart("dh_users", () -> this.dhSupport.getPlayerConfigurations().size()));
|
||||
this.metrics.addCustomChart(new MultiLineChart("dh_render_distance", () -> {
|
||||
Map<String, Integer> renderDistances = new HashMap<>();
|
||||
|
||||
for (Configuration playerConfig : this.dhSupport.getPlayerConfigurations().values()) {
|
||||
String rd = String.valueOf(playerConfig.getInt(DhsConfig.RENDER_DISTANCE));
|
||||
|
||||
renderDistances.put(rd, renderDistances.getOrDefault(rd, 0) + 1);
|
||||
}
|
||||
|
||||
return renderDistances;
|
||||
}));
|
||||
|
||||
this.loadDhsConfig();
|
||||
|
||||
@@ -99,6 +117,10 @@ public class DhSupportBukkitPlugin extends JavaPlugin
|
||||
@Override
|
||||
public void onDisable()
|
||||
{
|
||||
if (this.metrics != null) {
|
||||
this.metrics.shutdown();
|
||||
}
|
||||
|
||||
if (this.scheduler != null) {
|
||||
this.scheduler.cancelTasks();
|
||||
}
|
||||
@@ -142,6 +164,21 @@ public class DhSupportBukkitPlugin extends JavaPlugin
|
||||
return null;
|
||||
}
|
||||
|
||||
protected String getGameVersion()
|
||||
{
|
||||
try {
|
||||
Class<? extends Server> serverClass = this.getServer().getClass();
|
||||
|
||||
serverClass.getMethod("getMinecraftVersion");
|
||||
|
||||
return this.getServer().getMinecraftVersion();
|
||||
} catch (NoSuchMethodException exception) {
|
||||
// If we're here, that probably means we're not running Paper or any Paper fork.
|
||||
}
|
||||
|
||||
return this.getServer().getBukkitVersion().split("-", 2)[0];
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public DhSupport getDhSupport()
|
||||
{
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
package no.jckf.dhsupport.bukkit.commands;
|
||||
|
||||
import no.jckf.dhsupport.bukkit.DhSupportBukkitPlugin;
|
||||
import no.jckf.dhsupport.core.configuration.DhsConfig;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandExecutor;
|
||||
@@ -37,6 +38,10 @@ public class DhCommand implements CommandExecutor
|
||||
@Override
|
||||
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args)
|
||||
{
|
||||
if (!this.plugin.getDhSupport().getConfig().getBool(DhsConfig.ENABLE_DH_COMMAND_HELP_TEXT, true)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
sender.sendMessage(ChatColor.RED + "You have issued a " + ChatColor.YELLOW + "/dh" + ChatColor.RED + " command which is meant for the Distant Horizons " + ChatColor.YELLOW + "mod" + ChatColor.RED + ", but you are using the " + ChatColor.YELLOW + "plugin" + ChatColor.RED + ". Please make sure you are reading the correct documentation.");
|
||||
|
||||
return true;
|
||||
|
||||
@@ -75,6 +75,12 @@ public class DhsCommand implements CommandExecutor
|
||||
|
||||
case "trim":
|
||||
return this.trim(sender, Arrays.copyOfRange(args, 1, args.length));
|
||||
|
||||
case "bench":
|
||||
return this.bench(sender, Arrays.copyOfRange(args, 1, args.length));
|
||||
|
||||
case "config":
|
||||
return this.config(sender, Arrays.copyOfRange(args, 1, args.length));
|
||||
}
|
||||
|
||||
sender.sendMessage(ChatColor.RED + "Unknown sub-command.");
|
||||
@@ -251,7 +257,7 @@ public class DhsCommand implements CommandExecutor
|
||||
|
||||
boolean force = args.length >= 5 && args[4].equals("force");
|
||||
|
||||
sender.sendMessage(ChatColor.YELLOW + "Generating LODs for view distance of " + ChatColor.GREEN + Coordinates.blockToChunk(radius) + ChatColor.YELLOW + " chunks in world " + ChatColor.GREEN + world.getName() + ChatColor.YELLOW + " starting at center " + ChatColor.GREEN + centerX + " x " + centerZ + ChatColor.YELLOW + "...");
|
||||
sender.sendMessage(ChatColor.YELLOW + "Generating LODs for view distance of " + ChatColor.GREEN + Coordinates.blockToChunk(radius) + ChatColor.YELLOW + " chunks in world " + ChatColor.GREEN + world.getName() + ChatColor.YELLOW + " starting at center " + ChatColor.GREEN + centerX + " " + centerZ + ChatColor.YELLOW + "...");
|
||||
|
||||
if (force) {
|
||||
sender.sendMessage(ChatColor.YELLOW + "All existing LODs in this area will be re-generated.");
|
||||
@@ -432,7 +438,7 @@ public class DhsCommand implements CommandExecutor
|
||||
return true;
|
||||
}
|
||||
|
||||
sender.sendMessage(ChatColor.YELLOW + "Trimming LODs outside view distance of " + ChatColor.GREEN + radius + ChatColor.YELLOW + " blocks in world " + ChatColor.GREEN + world.getName() + ChatColor.YELLOW + " centered at " + ChatColor.GREEN + centerX + " x " + centerZ + ChatColor.YELLOW + "...");
|
||||
sender.sendMessage(ChatColor.YELLOW + "Trimming LODs outside view distance of " + ChatColor.GREEN + radius + ChatColor.YELLOW + " blocks in world " + ChatColor.GREEN + world.getName() + ChatColor.YELLOW + " centered at " + ChatColor.GREEN + centerX + " " + centerZ + ChatColor.YELLOW + "...");
|
||||
|
||||
this.plugin.getDhSupport().trim(world, centerX, centerZ, radius)
|
||||
.thenAccept((trimmedCount) -> {
|
||||
@@ -441,4 +447,95 @@ public class DhsCommand implements CommandExecutor
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
protected boolean bench(CommandSender sender, String[] args)
|
||||
{
|
||||
int schedulerThreads = this.plugin.getDhSupport().getConfig().getInt(DhsConfig.SCHEDULER_THREADS);
|
||||
|
||||
WorldInterface world;
|
||||
int iterations = 1000;
|
||||
int concurrency = schedulerThreads;
|
||||
|
||||
if (args.length >= 1) {
|
||||
World bukkitWorld = this.plugin.getWorld(args[0]);
|
||||
|
||||
if (bukkitWorld == null) {
|
||||
sender.sendMessage(ChatColor.RED + "Unknown world.");
|
||||
return true;
|
||||
}
|
||||
|
||||
world = this.plugin.getDhSupport().getWorldInterface(bukkitWorld.getUID());
|
||||
} else if (sender instanceof Player) {
|
||||
world = this.plugin.getDhSupport().getWorldInterface(((Player) sender).getWorld().getUID());
|
||||
} else {
|
||||
world = null;
|
||||
}
|
||||
|
||||
if (world == null) {
|
||||
sender.sendMessage(ChatColor.RED + "No world specified.");
|
||||
return true;
|
||||
}
|
||||
|
||||
if (args.length >= 2) {
|
||||
iterations = Integer.parseInt(args[1]);
|
||||
}
|
||||
|
||||
if (args.length >= 3) {
|
||||
concurrency = Integer.parseInt(args[2]);
|
||||
}
|
||||
|
||||
if (concurrency > schedulerThreads) {
|
||||
sender.sendMessage(ChatColor.RED + "Concurrency of " + ChatColor.YELLOW + concurrency + ChatColor.RED + " is more than the " + ChatColor.YELLOW + schedulerThreads + ChatColor.RED + " scheduler threads this server is running.");
|
||||
return true;
|
||||
}
|
||||
|
||||
sender.sendMessage(ChatColor.BLUE + "Starting benchmark with " + ChatColor.GREEN + iterations + ChatColor.BLUE + " iterations times " + ChatColor.GREEN + concurrency + ChatColor.BLUE + " threads in world " + ChatColor.GREEN + world.getName() + ChatColor.BLUE + "...");
|
||||
|
||||
this.plugin.getDhSupport().benchmark(world.getId(), iterations, concurrency)
|
||||
.thenAccept((lodsPerSecond) -> {
|
||||
sender.sendMessage(ChatColor.GREEN + "Benchmark completed! Result: " + ChatColor.BLUE + String.format("%.2f", lodsPerSecond * 16) + ChatColor.GREEN + " CPS.");
|
||||
});
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean config(CommandSender sender, String[] args)
|
||||
{
|
||||
if (args.length < 1) {
|
||||
sender.sendMessage(ChatColor.RED + "Missing action.");
|
||||
return true;
|
||||
}
|
||||
|
||||
if (args.length < 2) {
|
||||
sender.sendMessage(ChatColor.RED + "Missing key.");
|
||||
return true;
|
||||
}
|
||||
|
||||
switch (args[0]) {
|
||||
case "set":
|
||||
if (args.length < 3) {
|
||||
sender.sendMessage(ChatColor.RED + "Missing value.");
|
||||
return true;
|
||||
}
|
||||
|
||||
this.plugin.getDhSupport().getConfig().set(args[1], args[2]);
|
||||
|
||||
sender.sendMessage(ChatColor.GREEN + "Set " + ChatColor.BLUE + args[1] + ChatColor.GREEN + " to " + ChatColor.BLUE + args[2] + ChatColor.GREEN + ".");
|
||||
return true;
|
||||
|
||||
case "get":
|
||||
sender.sendMessage(ChatColor.GREEN + "Value of " + ChatColor.BLUE + args[1] + ChatColor.GREEN + " is " + ChatColor.BLUE + this.plugin.getDhSupport().getConfig().get(args[1]) + ChatColor.GREEN + ".");
|
||||
return true;
|
||||
|
||||
case "unset":
|
||||
this.plugin.getDhSupport().getConfig().unset(args[1]);
|
||||
|
||||
sender.sendMessage(ChatColor.GREEN + "Unset " + ChatColor.BLUE + args[1] + ChatColor.GREEN + ".");
|
||||
return true;
|
||||
}
|
||||
|
||||
sender.sendMessage(ChatColor.RED + "Unknown action.");
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -19,10 +19,18 @@
|
||||
package no.jckf.dhsupport.bukkit.handler;
|
||||
|
||||
import no.jckf.dhsupport.bukkit.DhSupportBukkitPlugin;
|
||||
import no.jckf.dhsupport.core.DhSupport;
|
||||
import no.jckf.dhsupport.core.configuration.DhsConfig;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.player.PlayerJoinEvent;
|
||||
import org.bukkit.event.player.PlayerQuitEvent;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class PlayerHandler implements Listener
|
||||
{
|
||||
protected DhSupportBukkitPlugin plugin;
|
||||
@@ -32,6 +40,26 @@ public class PlayerHandler implements Listener
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onPlayerConnect(PlayerJoinEvent event)
|
||||
{
|
||||
Player player = event.getPlayer();
|
||||
|
||||
DhSupport dhSupport = this.plugin.getDhSupport();
|
||||
|
||||
dhSupport.getScheduler().runOnMainThreadDelayed(() -> {
|
||||
if (dhSupport.getPlayerConfiguration(player.getUniqueId()) != null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
for (String message : dhSupport.getConfig().getStringList(DhsConfig.AD_MESSAGE, Collections.emptyList())) {
|
||||
player.sendMessage(message);
|
||||
}
|
||||
|
||||
return null;
|
||||
}, 10, TimeUnit.SECONDS); // Arbitrary delay, waiting for DH config message (or the lack of one).
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onPlayerDisconnect(PlayerQuitEvent event)
|
||||
{
|
||||
|
||||
@@ -21,6 +21,7 @@ package no.jckf.dhsupport.bukkit.handler;
|
||||
import no.jckf.dhsupport.bukkit.BukkitPluginMessageSender;
|
||||
import no.jckf.dhsupport.bukkit.DhSupportBukkitPlugin;
|
||||
import no.jckf.dhsupport.core.handler.PluginMessageHandler;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.plugin.messaging.PluginMessageListener;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@@ -55,6 +56,10 @@ public class PluginMessageProxy extends Handler implements PluginMessageListener
|
||||
@Override
|
||||
public void onPluginMessageReceived(@NotNull String channel, @NotNull Player player, byte[] message)
|
||||
{
|
||||
this.pluginMessageHandler.onPluginMessageReceived(channel, player.getUniqueId(), message);
|
||||
try {
|
||||
this.pluginMessageHandler.onPluginMessageReceived(channel, player.getUniqueId(), message);
|
||||
} catch (RuntimeException exception) {
|
||||
player.sendMessage(ChatColor.RED + "Distant Horizons: " + exception.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -20,25 +20,40 @@ package no.jckf.dhsupport.bukkit.handler;
|
||||
|
||||
import no.jckf.dhsupport.bukkit.BukkitWorldInterface;
|
||||
import no.jckf.dhsupport.bukkit.DhSupportBukkitPlugin;
|
||||
import no.jckf.dhsupport.core.configuration.DhsConfig;
|
||||
import org.bukkit.Chunk;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.block.Block;
|
||||
import org.bukkit.block.BlockState;
|
||||
import org.bukkit.event.Event;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.EventPriority;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.block.*;
|
||||
import org.bukkit.event.inventory.FurnaceBurnEvent;
|
||||
import org.bukkit.event.world.StructureGrowEvent;
|
||||
import org.bukkit.event.world.WorldLoadEvent;
|
||||
import org.bukkit.event.world.WorldUnloadEvent;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class WorldHandler implements Listener
|
||||
{
|
||||
protected static final String[] EVENT_METHOD_NAMES = { "blockList", "getBlocks", "getBlock", "getLocation", "getChunk" };
|
||||
|
||||
protected DhSupportBukkitPlugin plugin;
|
||||
|
||||
protected Map<String, Method> eventMethods = new HashMap<>();
|
||||
|
||||
public WorldHandler(DhSupportBukkitPlugin plugin)
|
||||
{
|
||||
this.plugin = plugin;
|
||||
|
||||
this.plugin.getServer().getWorlds().forEach(this::addWorldInterface);
|
||||
|
||||
this.registerUpdateListeners();
|
||||
}
|
||||
|
||||
protected void addWorldInterface(World world)
|
||||
@@ -57,9 +72,94 @@ public class WorldHandler implements Listener
|
||||
this.plugin.getDhSupport().setWorldInterface(world.getUID(), null);
|
||||
}
|
||||
|
||||
protected void touchLod(Location location)
|
||||
protected void touchLod(Location location, String reason)
|
||||
{
|
||||
this.plugin.getDhSupport().touchLod(location.getWorld().getUID(), location.getBlockX(), location.getBlockZ());
|
||||
this.plugin.getDhSupport().touchLod(location.getWorld().getUID(), location.getBlockX(), location.getBlockZ(), reason);
|
||||
}
|
||||
|
||||
protected void registerUpdateListeners()
|
||||
{
|
||||
Listener dummyListener = new Listener() {};
|
||||
|
||||
for (String eventClassName : this.plugin.getDhSupport().getConfig().getStringList(DhsConfig.UPDATE_EVENTS)) {
|
||||
try {
|
||||
Class<? extends Event> eventClass = Class.forName(eventClassName).asSubclass(Event.class);
|
||||
|
||||
this.plugin.getServer().getPluginManager().registerEvent(
|
||||
eventClass,
|
||||
dummyListener,
|
||||
EventPriority.MONITOR,
|
||||
(listener, event) -> this.handleUpdateEvent(event),
|
||||
this.plugin
|
||||
);
|
||||
|
||||
this.plugin.getDhSupport().info("Listening for " + eventClassName + ".");
|
||||
} catch (ClassNotFoundException exception) {
|
||||
this.plugin.getDhSupport().warning("Could not find event class: " + eventClassName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void handleUpdateEvent(Event event)
|
||||
{
|
||||
Class<? extends Event> eventClass = event.getClass();
|
||||
String eventClassName = eventClass.getName();
|
||||
Method method = null;
|
||||
|
||||
if (this.eventMethods.containsKey(eventClassName)) {
|
||||
method = this.eventMethods.get(eventClassName);
|
||||
|
||||
// We've seen this event before, but don't know how to handle it.
|
||||
if (method == null) {
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
for (String methodName : EVENT_METHOD_NAMES) {
|
||||
try {
|
||||
method = eventClass.getMethod(methodName);
|
||||
break;
|
||||
} catch (NoSuchMethodException exception) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// Store the result, even if it is null.
|
||||
this.eventMethods.put(eventClassName, method);
|
||||
|
||||
// This is the first time we see this even, and we don't know how to handle it.
|
||||
if (method == null) {
|
||||
this.plugin.getDhSupport().warning("Unsure how to handle event: " + eventClass.getName());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
List<?> list;
|
||||
|
||||
try {
|
||||
Object result = method.invoke(event);
|
||||
|
||||
if (result instanceof Location location) {
|
||||
list = List.of(location.getBlock());
|
||||
} else if (result instanceof Chunk chunk) {
|
||||
list = List.of(chunk.getBlock(0, 0, 0));
|
||||
} else if (result instanceof List) {
|
||||
list = (List<?>) result;
|
||||
} else {
|
||||
list = List.of(result);
|
||||
}
|
||||
} catch (IllegalAccessException | InvocationTargetException exception) {
|
||||
throw new RuntimeException(exception);
|
||||
}
|
||||
|
||||
for (Object entry : list) {
|
||||
if (entry instanceof Block block) {
|
||||
this.touchLod(block.getLocation(), eventClassName);
|
||||
} else if (entry instanceof BlockState state) {
|
||||
this.touchLod(state.getLocation(), eventClassName);
|
||||
} else {
|
||||
this.plugin.getDhSupport().warning("Unknown block " + entry + " from event: " + eventClassName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
@@ -73,117 +173,4 @@ public class WorldHandler implements Listener
|
||||
{
|
||||
this.removeWorldInterface(worldUnload.getWorld());
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onBlockPlace(BlockPlaceEvent blockPlace)
|
||||
{
|
||||
this.touchLod(blockPlace.getBlock().getLocation());
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onBlockBreak(BlockBreakEvent blockBreak)
|
||||
{
|
||||
this.touchLod(blockBreak.getBlock().getLocation());
|
||||
}
|
||||
|
||||
// FIXME messes with 1.16.5 compilation
|
||||
// @EventHandler
|
||||
// public void onTNTPrime(TNTPrimeEvent blockBreak)
|
||||
// {
|
||||
// this.touchLod(blockBreak.getBlock().getLocation());
|
||||
// }
|
||||
|
||||
@EventHandler
|
||||
public void onBlockExplode(BlockExplodeEvent blockExplode)
|
||||
{
|
||||
for (var block : blockExplode.blockList()) {
|
||||
this.touchLod(block.getLocation());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@EventHandler
|
||||
public void onBlockIgnite(BlockIgniteEvent blockIgnite)
|
||||
{
|
||||
this.touchLod(blockIgnite.getBlock().getLocation());
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onBlockBurn(BlockBurnEvent blockBurn)
|
||||
{
|
||||
this.touchLod(blockBurn.getBlock().getLocation());
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onLeavesDecay(LeavesDecayEvent leavesDecay)
|
||||
{
|
||||
this.touchLod(leavesDecay.getBlock().getLocation());
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onBlockFade(BlockFadeEvent blockFade)
|
||||
{
|
||||
this.touchLod(blockFade.getBlock().getLocation());
|
||||
}
|
||||
|
||||
|
||||
@EventHandler
|
||||
public void onBlockGrow(BlockGrowEvent blockGrow)
|
||||
{
|
||||
this.touchLod(blockGrow.getBlock().getLocation());
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onMoistureChange(MoistureChangeEvent moistureChange)
|
||||
{
|
||||
this.touchLod(moistureChange.getBlock().getLocation());
|
||||
}
|
||||
|
||||
|
||||
@EventHandler
|
||||
public void onFurnaceBurn(FurnaceBurnEvent furnaceBurn)
|
||||
{
|
||||
this.touchLod(furnaceBurn.getBlock().getLocation());
|
||||
}
|
||||
|
||||
|
||||
@EventHandler
|
||||
public void onBlockRedstone(BlockRedstoneEvent blockRedstone)
|
||||
{
|
||||
this.touchLod(blockRedstone.getBlock().getLocation());
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onBlockPistonExtend(BlockPistonExtendEvent blockPistonExtend)
|
||||
{
|
||||
for (var block : blockPistonExtend.getBlocks()) {
|
||||
this.touchLod(block.getLocation());
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onBlockPistonRetract(BlockPistonRetractEvent blockPistonRetract)
|
||||
{
|
||||
for (var block : blockPistonRetract.getBlocks()) {
|
||||
this.touchLod(block.getLocation());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@EventHandler
|
||||
public void onStructureGrow(StructureGrowEvent structureGrow)
|
||||
{
|
||||
for (var blockState : structureGrow.getBlocks()) {
|
||||
this.touchLod(blockState.getLocation());
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onSpongeAbsorb(SpongeAbsorbEvent spongeAbsorb)
|
||||
{
|
||||
for (var blockState : spongeAbsorb.getBlocks()) {
|
||||
this.touchLod(blockState.getLocation());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -18,6 +18,8 @@
|
||||
|
||||
package no.jckf.dhsupport.core;
|
||||
|
||||
import java.util.function.BiConsumer;
|
||||
|
||||
// Block -> chunk -> section -> region
|
||||
public class Coordinates
|
||||
{
|
||||
@@ -101,4 +103,39 @@ public class Coordinates
|
||||
{
|
||||
return value & 0x0F;
|
||||
}
|
||||
|
||||
// PACK/UNPACK
|
||||
|
||||
public static long pack(int x, int z)
|
||||
{
|
||||
return ((long) x & 0xffffffffL)
|
||||
| (((long) z & 0xffffffffL) << 32);
|
||||
}
|
||||
|
||||
public static int unpackX(long packed)
|
||||
{
|
||||
return (int) packed;
|
||||
}
|
||||
|
||||
public static int unpackZ(long packed)
|
||||
{
|
||||
return (int) (packed >> 32);
|
||||
}
|
||||
|
||||
// ???
|
||||
|
||||
public static void foreachChunkInSection(int sectionX, int sectionZ, BiConsumer<Integer, Integer> consumer)
|
||||
{
|
||||
int worldX = sectionToBlock(sectionX);
|
||||
int worldZ = sectionToBlock(sectionZ);
|
||||
|
||||
for (int xMultiplier = 0; xMultiplier < 4; xMultiplier++) {
|
||||
for (int zMultiplier = 0; zMultiplier < 4; zMultiplier++) {
|
||||
consumer.accept(
|
||||
worldX + 16 * xMultiplier,
|
||||
worldZ + 16 * zMultiplier
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -23,7 +23,9 @@ import no.jckf.dhsupport.core.configuration.Configurable;
|
||||
import no.jckf.dhsupport.core.configuration.Configuration;
|
||||
import no.jckf.dhsupport.core.configuration.DhsConfig;
|
||||
import no.jckf.dhsupport.core.database.Database;
|
||||
import no.jckf.dhsupport.core.database.migrations.AddLodVersionColumn;
|
||||
import no.jckf.dhsupport.core.database.migrations.CreateLodsTable;
|
||||
import no.jckf.dhsupport.core.database.migrations.DeleteLodsWithoutVersion;
|
||||
import no.jckf.dhsupport.core.database.models.LodModel;
|
||||
import no.jckf.dhsupport.core.database.repositories.AsyncLodRepository;
|
||||
import no.jckf.dhsupport.core.dataobject.Lod;
|
||||
@@ -43,10 +45,7 @@ import org.bukkit.entity.Player;
|
||||
import javax.annotation.Nullable;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
@@ -57,7 +56,7 @@ public class DhSupport implements Configurable
|
||||
{
|
||||
protected String pluginVersion;
|
||||
|
||||
protected String mcVersion;
|
||||
protected String gameVersion;
|
||||
|
||||
protected String dataDirectory;
|
||||
|
||||
@@ -92,10 +91,10 @@ public class DhSupport implements Configurable
|
||||
|
||||
protected Map<UUID, PreGenerator> preGenerators = new HashMap<>();
|
||||
|
||||
public DhSupport(String pluginVersion, String mcVersion)
|
||||
public DhSupport(String pluginVersion, String gameVersion)
|
||||
{
|
||||
this.pluginVersion = pluginVersion;
|
||||
this.mcVersion = mcVersion;
|
||||
this.gameVersion = gameVersion;
|
||||
|
||||
this.configuration = new Configuration();
|
||||
|
||||
@@ -118,6 +117,8 @@ public class DhSupport implements Configurable
|
||||
);
|
||||
|
||||
this.database.addMigration(CreateLodsTable.class);
|
||||
this.database.addMigration(AddLodVersionColumn.class);
|
||||
this.database.addMigration(DeleteLodsWithoutVersion.class);
|
||||
|
||||
this.database.migrate();
|
||||
} catch (Exception exception) {
|
||||
@@ -246,6 +247,16 @@ public class DhSupport implements Configurable
|
||||
return this.lodRepository;
|
||||
}
|
||||
|
||||
public String getPluginVersion()
|
||||
{
|
||||
return this.pluginVersion;
|
||||
}
|
||||
|
||||
public String getGameVersion()
|
||||
{
|
||||
return this.gameVersion;
|
||||
}
|
||||
|
||||
public Configuration getConfig()
|
||||
{
|
||||
return this.configuration;
|
||||
@@ -274,7 +285,12 @@ public class DhSupport implements Configurable
|
||||
|
||||
public void debug(String message)
|
||||
{
|
||||
//this.getLogger().info("[DEBUG] " + message);
|
||||
// TODO: Use log levels properly?
|
||||
if (!this.getConfig().getBool(DhsConfig.DEBUG, false)) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.getLogger().info("[DEBUG] " + message);
|
||||
}
|
||||
|
||||
public void checkUpdates()
|
||||
@@ -337,25 +353,12 @@ public class DhSupport implements Configurable
|
||||
return this.queuedBuilders.get(key);
|
||||
}
|
||||
|
||||
Scheduler scheduler = this.getScheduler();
|
||||
CompletableFuture<Lod> queued = this.getScheduler().runOnSeparateThread(() -> {
|
||||
this.joinPauseState();
|
||||
|
||||
CompletableFuture<Lod> queued;
|
||||
|
||||
// TODO: Fetch chunk data on region thread, then move to builder thread.
|
||||
if (scheduler.canReadWorldAsync()) {
|
||||
queued = this.getScheduler().runOnSeparateThread(
|
||||
builder::generate
|
||||
);
|
||||
} else {
|
||||
queued = this.getScheduler().runOnRegionThread(
|
||||
worldId,
|
||||
Coordinates.sectionToBlock(position.getX()),
|
||||
Coordinates.sectionToBlock(position.getZ()),
|
||||
builder::generate
|
||||
);
|
||||
}
|
||||
|
||||
queued = queued.thenApply((lod) -> {
|
||||
return builder.generate();
|
||||
})
|
||||
.thenApply((lod) -> {
|
||||
this.queuedBuilders.remove(key);
|
||||
|
||||
return lod;
|
||||
@@ -384,14 +387,12 @@ public class DhSupport implements Configurable
|
||||
}
|
||||
|
||||
// Otherwise generate a new one.
|
||||
return this.generateLod(worldId, position);
|
||||
return this.generateLod(worldId, position, true);
|
||||
});
|
||||
}
|
||||
|
||||
protected CompletableFuture<LodModel> generateLod(UUID worldId, SectionPosition position)
|
||||
protected CompletableFuture<LodModel> generateLod(UUID worldId, SectionPosition position, boolean manageChunks)
|
||||
{
|
||||
this.joinPauseState();
|
||||
|
||||
int worldX = Coordinates.sectionToBlock(position.getX());
|
||||
int worldZ = Coordinates.sectionToBlock(position.getZ());
|
||||
|
||||
@@ -399,22 +400,19 @@ public class DhSupport implements Configurable
|
||||
|
||||
boolean generateNewChunks = world.getConfig().getBool(DhsConfig.GENERATE_NEW_CHUNKS, true);
|
||||
|
||||
Map<String, CompletableFuture<Boolean>> loads = new HashMap<>();
|
||||
|
||||
// Load all the chunks we need for this request.
|
||||
for (int xMultiplier = 0; xMultiplier < 4; xMultiplier++) {
|
||||
for (int zMultiplier = 0; zMultiplier < 4; zMultiplier++) {
|
||||
int chunkX = worldX + 16 * xMultiplier;
|
||||
int chunkZ = worldZ + 16 * zMultiplier;
|
||||
Map<Long, CompletableFuture<Boolean>> loads = new HashMap<>();
|
||||
|
||||
if (manageChunks) {
|
||||
// Load all the chunks we need for this request.
|
||||
Coordinates.foreachChunkInSection(position.getX(), position.getZ(), (chunkX, chunkZ) -> {
|
||||
if (!world.isChunkLoaded(chunkX, chunkZ)) {
|
||||
if (generateNewChunks) {
|
||||
loads.put(worldX + "x" + worldZ, world.loadOrGenerateChunkAsync(chunkX, chunkZ));
|
||||
loads.put(Coordinates.pack(chunkX, chunkZ), world.loadOrGenerateChunkAsync(chunkX, chunkZ));
|
||||
} else {
|
||||
loads.put(worldX + "x" + worldZ, world.loadChunkAsync(chunkX, chunkZ));
|
||||
loads.put(Coordinates.pack(chunkX, chunkZ), world.loadChunkAsync(chunkX, chunkZ));
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Wait for chunk loads, then...
|
||||
@@ -433,46 +431,36 @@ public class DhSupport implements Configurable
|
||||
.anyMatch(Predicate.isEqual(false));
|
||||
|
||||
if (loadRejected) {
|
||||
// Discard the chunks we loaded.
|
||||
this.getScheduler().runOnRegionThread(worldId, worldX, worldZ, () -> {
|
||||
for (String key : loads.keySet()) {
|
||||
String[] xz = key.split("x", 2);
|
||||
|
||||
world.discardChunk(
|
||||
Integer.parseInt(xz[0]),
|
||||
Integer.parseInt(xz[1])
|
||||
);
|
||||
}
|
||||
|
||||
return null;
|
||||
// Use dummy chunks instead of real world data.
|
||||
world.setDummyMode(true);
|
||||
} else {
|
||||
// Prime chunk data in world interface.
|
||||
Coordinates.foreachChunkInSection(position.getX(), position.getZ(), (chunkX, chunkZ) -> {
|
||||
world.getMaterialAt(chunkX, world.getSeaLevel(), chunkZ);
|
||||
});
|
||||
|
||||
return CompletableFuture.completedFuture(null);
|
||||
}
|
||||
|
||||
// Discard the chunks we loaded.
|
||||
this.getScheduler().runOnRegionThread(worldId, worldX, worldZ, () -> {
|
||||
for (long packed : loads.keySet()) {
|
||||
world.discardChunk(
|
||||
Coordinates.unpackX(packed),
|
||||
Coordinates.unpackZ(packed)
|
||||
);
|
||||
}
|
||||
|
||||
return null;
|
||||
});
|
||||
|
||||
// No LOD was found. Start building a new one.
|
||||
CompletableFuture<Lod> lodFuture = this.queueBuilder(worldId, position, this.getBuilder(world, position));
|
||||
|
||||
// Combine the LOD and beacons and save the result in the database.
|
||||
return lodFuture.thenApply((lod) -> {
|
||||
// Discard the chunks we loaded.
|
||||
this.getScheduler().runOnRegionThread(worldId, worldX, worldZ, () -> {
|
||||
for (String key : loads.keySet()) {
|
||||
String[] xz = key.split("x", 2);
|
||||
|
||||
world.discardChunk(
|
||||
Integer.parseInt(xz[0]),
|
||||
Integer.parseInt(xz[1])
|
||||
);
|
||||
}
|
||||
|
||||
return null;
|
||||
});
|
||||
|
||||
Encoder lodEncoder = new Encoder();
|
||||
Encoder lodEncoder = new Encoder(8192);
|
||||
lod.encode(lodEncoder);
|
||||
|
||||
Encoder beaconEncoder = new Encoder();
|
||||
Encoder beaconEncoder = new Encoder(16 + lod.getBeacons().size() * 16);
|
||||
beaconEncoder.writeCollection(lod.getBeacons());
|
||||
|
||||
this.generationTracker.ping();
|
||||
@@ -482,14 +470,15 @@ public class DhSupport implements Configurable
|
||||
position.getX(),
|
||||
position.getZ(),
|
||||
lodEncoder.toByteArray(),
|
||||
beaconEncoder.toByteArray()
|
||||
beaconEncoder.toByteArray(),
|
||||
LodModel.VERSION
|
||||
);
|
||||
})
|
||||
.thenCompose((f) -> f); // Unwrap the nested future.
|
||||
});
|
||||
}
|
||||
|
||||
public void touchLod(UUID worldId, int x, int z)
|
||||
public void touchLod(UUID worldId, int x, int z, @Nullable String reason)
|
||||
{
|
||||
int sectionX = Coordinates.blockToSection(x);
|
||||
int sectionZ = Coordinates.blockToSection(z);
|
||||
@@ -505,9 +494,16 @@ public class DhSupport implements Configurable
|
||||
return;
|
||||
}
|
||||
|
||||
this.debug("Touched LOD at " + this.getWorldInterface(worldId).getName() + " " + x + " " + z + (reason == null ? "." : ": " + reason));
|
||||
|
||||
this.touchedLods.put(key, lodModel);
|
||||
}
|
||||
|
||||
public void touchLod(UUID worldId, int x, int z)
|
||||
{
|
||||
this.touchLod(worldId, x, z, null);
|
||||
}
|
||||
|
||||
public void updateTouchedLods()
|
||||
{
|
||||
if (this.isPaused()) {
|
||||
@@ -520,7 +516,7 @@ public class DhSupport implements Configurable
|
||||
|
||||
WorldInterface world = this.getWorldInterface(lodModelToDelete.getWorldId());
|
||||
|
||||
this.debug("Changes detected in " + world.getName() + " " + lodModelToDelete.getX() + "x" + lodModelToDelete.getZ() + ".");
|
||||
this.debug("Changes detected in " + world.getName() + " " + lodModelToDelete.getX() + " " + lodModelToDelete.getZ() + ".");
|
||||
|
||||
this.getLodRepository().lodExistsAsync(lodModelToDelete.getWorldId(), lodModelToDelete.getX(), lodModelToDelete.getZ()).thenAccept((exists) -> {
|
||||
if (!exists) {
|
||||
@@ -530,7 +526,7 @@ public class DhSupport implements Configurable
|
||||
this.getLodRepository().deleteLodAsync(lodModelToDelete.getWorldId(), lodModelToDelete.getX(), lodModelToDelete.getZ())
|
||||
.thenAccept((deleted) -> {
|
||||
if (!deleted) {
|
||||
this.warning("Could not delete LOD " + world.getName() + " " + lodModelToDelete.getX() + "x" + lodModelToDelete.getZ() + ".");
|
||||
this.warning("Could not delete LOD " + world.getName() + " " + lodModelToDelete.getX() + " " + lodModelToDelete.getZ() + ".");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -547,7 +543,7 @@ public class DhSupport implements Configurable
|
||||
boolean updatesEnabled = worldConfig.getBool(DhsConfig.REAL_TIME_UPDATES_ENABLED);
|
||||
|
||||
if (!updatesEnabled) {
|
||||
this.debug("New LOD " + world.getName() + " " + lodModelToDelete.getX() + "x" + lodModelToDelete.getZ() + " generated, but real-time updates are disabled.");
|
||||
this.debug("New LOD " + world.getName() + " " + lodModelToDelete.getX() + " " + lodModelToDelete.getZ() + " generated, but real-time updates are disabled.");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -575,7 +571,7 @@ public class DhSupport implements Configurable
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!playerConfig.getBool(DhsConfig.DISTANT_GENERATION_ENABLED) || !playerConfig.getBool(DhsConfig.REAL_TIME_UPDATES_ENABLED)) {
|
||||
if (!playerConfig.getBool(DhsConfig.REAL_TIME_UPDATES_ENABLED)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -610,11 +606,11 @@ public class DhSupport implements Configurable
|
||||
FullDataChunkMessage chunkResponse = new FullDataChunkMessage();
|
||||
chunkResponse.setBufferId(myBufferId);
|
||||
chunkResponse.setIsFirst(chunkNo == 0);
|
||||
chunkResponse.setData(Arrays.copyOfRange(
|
||||
chunkResponse.setData(
|
||||
data,
|
||||
LodHandler.CHUNK_SIZE * chunkNo,
|
||||
Math.min(LodHandler.CHUNK_SIZE * chunkNo + LodHandler.CHUNK_SIZE, data.length)
|
||||
));
|
||||
Math.min(LodHandler.CHUNK_SIZE, data.length - LodHandler.CHUNK_SIZE * chunkNo)
|
||||
);
|
||||
|
||||
this.pluginMessageHandler.sendPluginMessage(player.getUniqueId(), chunkResponse);
|
||||
}
|
||||
@@ -622,7 +618,7 @@ public class DhSupport implements Configurable
|
||||
this.pluginMessageHandler.sendPluginMessage(player.getUniqueId(), partialUpdateMessage);
|
||||
}
|
||||
|
||||
this.debug("Updated LOD " + world.getName() + " " + lodModelToDelete.getX() + "x" + lodModelToDelete.getZ() + " sent to " + playersInRangeCount + " players. Found " + playersOutOfRangeCount + " players out of range, and " + playersWithoutDhCount + " players without DH.");
|
||||
this.debug("Updated LOD " + world.getName() + " " + lodModelToDelete.getX() + " " + lodModelToDelete.getZ() + " sent to " + playersInRangeCount + " players. Found " + playersOutOfRangeCount + " players out of range, and " + playersWithoutDhCount + " players without DH.");
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -675,4 +671,47 @@ public class DhSupport implements Configurable
|
||||
Coordinates.blockToSection(centerZ + radius)
|
||||
);
|
||||
}
|
||||
|
||||
public CompletableFuture<Double> benchmark(UUID worldId, int iterations, int concurrency)
|
||||
{
|
||||
WorldInterface world = this.getWorldInterface(worldId);
|
||||
|
||||
List<SectionPosition> positions = new ArrayList<>(concurrency);
|
||||
|
||||
for (int i = 0; i < concurrency; i++) {
|
||||
SectionPosition position = new SectionPosition();
|
||||
position.setX(Coordinates.blockToSection(world.getSpawnX()) + i);
|
||||
position.setZ(Coordinates.blockToSection(world.getSpawnZ()));
|
||||
|
||||
positions.add(position);
|
||||
}
|
||||
|
||||
return CompletableFuture.allOf(
|
||||
positions.stream()
|
||||
.map((position) -> this.generateLod(world.getId(), position, false))
|
||||
.toArray(CompletableFuture[]::new)
|
||||
)
|
||||
.thenCompose((foo) -> {
|
||||
List<CompletableFuture<?>> chains = new ArrayList<>(positions.size());
|
||||
|
||||
long ts = System.currentTimeMillis();
|
||||
|
||||
for (SectionPosition position : positions) {
|
||||
CompletableFuture<?> chain = CompletableFuture.completedFuture(null);
|
||||
|
||||
for (int i = 0; i < iterations; i++) {
|
||||
chain = chain.thenCompose((bar) -> this.generateLod(world.getId(), position, false));
|
||||
}
|
||||
|
||||
chains.add(chain);
|
||||
}
|
||||
|
||||
return CompletableFuture.allOf(chains.toArray(CompletableFuture[]::new))
|
||||
.thenApply((bar) -> {
|
||||
double elapsed = (System.currentTimeMillis() - ts) / 1000d;
|
||||
|
||||
return iterations * concurrency / elapsed;
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -76,7 +76,7 @@ public class PreGenerator implements Runnable
|
||||
int[][] directions = {{ 0, 1 }, { 1, 0 }, { 0, -1 }, { -1, 0 }};
|
||||
int dirIndex = 0;
|
||||
|
||||
List<CompletableFuture<LodModel>> requests = new ArrayList<>();
|
||||
List<CompletableFuture<LodModel>> requests = new ArrayList<>(rateLimit);
|
||||
|
||||
this.startTime = System.currentTimeMillis();
|
||||
|
||||
@@ -105,7 +105,7 @@ public class PreGenerator implements Runnable
|
||||
position.setZ(currentZ);
|
||||
position.setDetailLevel(6);
|
||||
|
||||
CompletableFuture<LodModel> request = this.dhSupport.generateLod(this.world.getId(), position);
|
||||
CompletableFuture<LodModel> request = this.dhSupport.generateLod(this.world.getId(), position, true);
|
||||
|
||||
request.handle((lodModel, exception) -> this.inFlight--);
|
||||
|
||||
|
||||
@@ -21,11 +21,16 @@ package no.jckf.dhsupport.core;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.time.Duration;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
public class Utils
|
||||
{
|
||||
private static final byte[] HEX_ARRAY = "0123456789ABCDEF".getBytes(StandardCharsets.US_ASCII);
|
||||
|
||||
private static final Map<String, String> NAMESPACE_CACHE = new ConcurrentHashMap<>();
|
||||
private static final Map<String, String> UNNAMESPACE_CACHE = new ConcurrentHashMap<>();
|
||||
|
||||
public static String bytesToHex(byte[] bytes)
|
||||
{
|
||||
byte[] hexChars = new byte[bytes.length * 2];
|
||||
@@ -114,4 +119,30 @@ public class Utils
|
||||
|
||||
return builder.substring(0, builder.length() - 2);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated For awareness: This method caches results by key only.
|
||||
*/
|
||||
public static String namespaceKey(String key, String defaultNamespace)
|
||||
{
|
||||
return NAMESPACE_CACHE.computeIfAbsent(key, (k) -> k.contains(":") ? k : defaultNamespace + ":" + k);
|
||||
}
|
||||
|
||||
public static String namespaceKey(String key)
|
||||
{
|
||||
return namespaceKey(key, "minecraft");
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated For awareness: This method caches results by key only.
|
||||
*/
|
||||
public static String unNamespaceKey(String key, String namespace)
|
||||
{
|
||||
return UNNAMESPACE_CACHE.computeIfAbsent(key, (k) -> k.startsWith(namespace + ":") ? k.substring(namespace.length() + 1) : k);
|
||||
}
|
||||
|
||||
public static String unNamespaceKey(String key)
|
||||
{
|
||||
return unNamespaceKey(key, "minecraft");
|
||||
}
|
||||
}
|
||||
@@ -110,7 +110,7 @@ public class Decoder
|
||||
{
|
||||
int size = this.readInt();
|
||||
|
||||
List<T> items = new ArrayList<>();
|
||||
List<T> items = new ArrayList<>(Math.min(size, 128));
|
||||
|
||||
for (int i = 0; i < size; i++) {
|
||||
T item = this.readObject(className);
|
||||
|
||||
@@ -33,11 +33,21 @@ public class Encoder
|
||||
this.output = ByteStreams.newDataOutput();
|
||||
}
|
||||
|
||||
public Encoder(int size)
|
||||
{
|
||||
this.output = ByteStreams.newDataOutput(size);
|
||||
}
|
||||
|
||||
public void write(byte[] data)
|
||||
{
|
||||
this.output.write(data);
|
||||
}
|
||||
|
||||
public void write(byte[] data, int offset, int length)
|
||||
{
|
||||
this.output.write(data, offset, length);
|
||||
}
|
||||
|
||||
public void writeByte(byte value)
|
||||
{
|
||||
this.output.writeByte(value);
|
||||
|
||||
@@ -21,6 +21,9 @@ package no.jckf.dhsupport.core.configuration;
|
||||
import org.checkerframework.checker.nullness.qual.PolyNull;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
@@ -33,8 +36,25 @@ public class Configuration
|
||||
this.variables.clear();
|
||||
}
|
||||
|
||||
public boolean has(String key)
|
||||
{
|
||||
return this.variables.containsKey(key);
|
||||
}
|
||||
|
||||
public void set(String key, @Nullable Object value)
|
||||
{
|
||||
if (value instanceof String stringValue) {
|
||||
if ("false".equalsIgnoreCase(stringValue)) {
|
||||
value = false;
|
||||
} else if ("true".equalsIgnoreCase(stringValue)) {
|
||||
value = true;
|
||||
} else {
|
||||
try {
|
||||
value = Integer.parseInt(stringValue);
|
||||
} catch (NumberFormatException exception) {}
|
||||
}
|
||||
}
|
||||
|
||||
this.variables.put(key, value);
|
||||
}
|
||||
|
||||
@@ -85,6 +105,16 @@ public class Configuration
|
||||
return this.getString(key, null);
|
||||
}
|
||||
|
||||
public @PolyNull List<String> getStringList(String key, @PolyNull List<String> defaultValue)
|
||||
{
|
||||
return (List<String>) this.get(key, defaultValue);
|
||||
}
|
||||
|
||||
public List<String> getStringList(String key)
|
||||
{
|
||||
return this.getStringList(key, Collections.emptyList());
|
||||
}
|
||||
|
||||
public int increment(String key)
|
||||
{
|
||||
return (int) this.variables.compute(key, (k, v) -> v == null ? 1 : (int) v + 1);
|
||||
|
||||
@@ -48,6 +48,8 @@ public abstract class DhsConfig
|
||||
|
||||
public static String SCHEDULER_THREADS = "scheduler_threads";
|
||||
|
||||
public static String SERVER_KEY = "server_key";
|
||||
|
||||
public static String LEVEL_KEY_PREFIX = "level_key_prefix";
|
||||
|
||||
public static String BORDER_CENTER_X = "border_center_x";
|
||||
@@ -68,6 +70,8 @@ public abstract class DhsConfig
|
||||
|
||||
public static String BUILDER_TYPE = "builder_type";
|
||||
|
||||
public static String TRUST_HEIGHT_MAP = "trust_height_map";
|
||||
|
||||
public static String BUILDER_RESOLUTION = "builder_resolution";
|
||||
|
||||
public static String SCAN_TO_SEA_LEVEL= "scan_to_sea_level";
|
||||
@@ -77,4 +81,18 @@ public abstract class DhsConfig
|
||||
public static String INCLUDE_NON_COLLIDING_TOP_LAYER = "include_non-colliding_top_layer";
|
||||
|
||||
public static String PERFORM_UNDERGLOW_HACK = "perform_underglow_hack";
|
||||
|
||||
public static String SAMPLE_BIOMES_3D = "sample_biomes_3d";
|
||||
|
||||
public static String UPDATE_EVENTS = "update_events";
|
||||
|
||||
public static String ENABLE_DH_COMMAND_HELP_TEXT = "enable_dh_command_help_text";
|
||||
|
||||
public static String DUMMY_CHUNK = "dummy_chunk";
|
||||
|
||||
public static String MATERIAL_MAP = "material_map";
|
||||
|
||||
public static String AD_MESSAGE = "ad_message";
|
||||
|
||||
public static String LIGHT_OFFSET_HACK = "light_offset_hack";
|
||||
}
|
||||
@@ -21,7 +21,8 @@ package no.jckf.dhsupport.core.database;
|
||||
import no.jckf.dhsupport.core.database.migrations.Migration;
|
||||
|
||||
import java.sql.*;
|
||||
import java.util.HashMap;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
@@ -34,7 +35,7 @@ public class Database
|
||||
|
||||
protected Map<String, PreparedStatement> preparedStatements = new ConcurrentHashMap<>();
|
||||
|
||||
protected Map<String, Class<? extends Migration>> migrations = new HashMap<>();
|
||||
protected List<Class<? extends Migration>> migrations = new ArrayList<>();
|
||||
|
||||
protected CompletableFuture<?> optimizing;
|
||||
|
||||
@@ -76,7 +77,7 @@ public class Database
|
||||
return this.preparedStatements.get(sql);
|
||||
}
|
||||
|
||||
protected void clearQueryCache()
|
||||
public void clearQueryCache()
|
||||
{
|
||||
for (String key : this.preparedStatements.keySet()) {
|
||||
try {
|
||||
@@ -103,7 +104,7 @@ public class Database
|
||||
|
||||
public void addMigration(Class<? extends Migration> migration)
|
||||
{
|
||||
this.migrations.put(migration.getSimpleName(), migration);
|
||||
this.migrations.add(migration);
|
||||
}
|
||||
|
||||
public boolean hasMigrationRan(String name) throws SQLException
|
||||
@@ -135,12 +136,14 @@ public class Database
|
||||
{
|
||||
this.createMigrationsTable();
|
||||
|
||||
for (String name : this.migrations.keySet()) {
|
||||
for (Class<? extends Migration> migrationClass : this.migrations) {
|
||||
String name = migrationClass.getSimpleName();
|
||||
|
||||
if (this.hasMigrationRan(name)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Migration migration = this.migrations.get(name).getConstructor(this.getClass()).newInstance(this);
|
||||
Migration migration = migrationClass.getConstructor(this.getClass()).newInstance(this);
|
||||
|
||||
migration.up();
|
||||
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* DH Support, server-side support for Distant Horizons.
|
||||
* Copyright (C) 2024 Jim C K Flaten
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package no.jckf.dhsupport.core.database.migrations;
|
||||
|
||||
import no.jckf.dhsupport.core.database.Database;
|
||||
|
||||
import java.sql.Statement;
|
||||
|
||||
public class AddLodVersionColumn extends Migration
|
||||
{
|
||||
public AddLodVersionColumn(Database database)
|
||||
{
|
||||
super(database);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void up() throws Exception
|
||||
{
|
||||
try (Statement statement = this.database.getConnection().createStatement()) {
|
||||
statement.execute("""
|
||||
ALTER TABLE lods ADD version INT NULL;
|
||||
""");
|
||||
}
|
||||
}
|
||||
}
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* DH Support, server-side support for Distant Horizons.
|
||||
* Copyright (C) 2024 Jim C K Flaten
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package no.jckf.dhsupport.core.database.migrations;
|
||||
|
||||
import no.jckf.dhsupport.core.database.Database;
|
||||
|
||||
import java.sql.Statement;
|
||||
|
||||
public class DeleteLodsWithoutVersion extends Migration
|
||||
{
|
||||
public DeleteLodsWithoutVersion(Database database)
|
||||
{
|
||||
super(database);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void up() throws Exception
|
||||
{
|
||||
try (Statement statement = this.database.getConnection().createStatement()) {
|
||||
statement.execute("""
|
||||
DELETE FROM lods WHERE version IS NULL;
|
||||
""");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -22,6 +22,8 @@ import java.util.UUID;
|
||||
|
||||
public class LodModel extends Model
|
||||
{
|
||||
public static int VERSION = 2;
|
||||
|
||||
public static LodModel create()
|
||||
{
|
||||
return new LodModel();
|
||||
@@ -39,6 +41,8 @@ public class LodModel extends Model
|
||||
|
||||
protected int timestamp;
|
||||
|
||||
protected int version;
|
||||
|
||||
public LodModel setWorldId(UUID worldId)
|
||||
{
|
||||
this.worldId = worldId;
|
||||
@@ -60,7 +64,7 @@ public class LodModel extends Model
|
||||
|
||||
public int getX()
|
||||
{
|
||||
return x;
|
||||
return this.x;
|
||||
}
|
||||
|
||||
public LodModel setZ(int z)
|
||||
@@ -72,7 +76,7 @@ public class LodModel extends Model
|
||||
|
||||
public int getZ()
|
||||
{
|
||||
return z;
|
||||
return this.z;
|
||||
}
|
||||
|
||||
public LodModel setData(byte[] data)
|
||||
@@ -84,7 +88,7 @@ public class LodModel extends Model
|
||||
|
||||
public byte[] getData()
|
||||
{
|
||||
return data;
|
||||
return this.data;
|
||||
}
|
||||
|
||||
public LodModel setBeacons(byte[] beacons)
|
||||
@@ -96,7 +100,7 @@ public class LodModel extends Model
|
||||
|
||||
public byte[] getBeacons()
|
||||
{
|
||||
return beacons;
|
||||
return this.beacons;
|
||||
}
|
||||
|
||||
public LodModel setTimestamp(int timestamp)
|
||||
@@ -111,6 +115,18 @@ public class LodModel extends Model
|
||||
return this.timestamp;
|
||||
}
|
||||
|
||||
public LodModel setVersion(int version)
|
||||
{
|
||||
this.version = version;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public int getVersion()
|
||||
{
|
||||
return this.version;
|
||||
}
|
||||
|
||||
public String toString()
|
||||
{
|
||||
return this.getWorldId() + "@" + this.getX() + "x" + this.getZ();
|
||||
|
||||
@@ -51,9 +51,9 @@ public class AsyncLodRepository extends LodRepository
|
||||
return future;
|
||||
}
|
||||
|
||||
public CompletableFuture<LodModel> saveLodAsync(UUID worldId, int sectionX, int sectionZ, byte[] data, byte[] beacons)
|
||||
public CompletableFuture<LodModel> saveLodAsync(UUID worldId, int sectionX, int sectionZ, byte[] data, byte[] beacons, int version)
|
||||
{
|
||||
return this.queueTask(() -> this.saveLod(worldId, sectionX, sectionZ, data, beacons));
|
||||
return this.queueTask(() -> this.saveLod(worldId, sectionX, sectionZ, data, beacons, version));
|
||||
}
|
||||
|
||||
public CompletableFuture<LodModel> loadLodAsync(UUID worldId, int sectionX, int sectionZ)
|
||||
|
||||
@@ -29,7 +29,7 @@ import java.util.logging.Logger;
|
||||
|
||||
public class LodRepository
|
||||
{
|
||||
protected static final String SQL_SAVE_LOD = "REPLACE INTO lods (worldId, x, z, data, beacons, timestamp) VALUES (?, ?, ?, ?, ?, ?)";
|
||||
protected static final String SQL_SAVE_LOD = "REPLACE INTO lods (worldId, x, z, data, beacons, timestamp, version) VALUES (?, ?, ?, ?, ?, ?, ?)";
|
||||
|
||||
protected static final String SQL_LOAD_LOD = "SELECT data, beacons, timestamp FROM lods WHERE worldId = ? AND x = ? AND z = ? LIMIT 1";
|
||||
|
||||
@@ -58,7 +58,7 @@ public class LodRepository
|
||||
return this.logger;
|
||||
}
|
||||
|
||||
public LodModel saveLod(UUID worldId, int sectionX, int sectionZ, byte[] data, byte[] beacons)
|
||||
public LodModel saveLod(UUID worldId, int sectionX, int sectionZ, byte[] data, byte[] beacons, int version)
|
||||
{
|
||||
int timestamp = (int) (System.currentTimeMillis() / 1000);
|
||||
|
||||
@@ -71,6 +71,7 @@ public class LodRepository
|
||||
statement.setBytes(4, data);
|
||||
statement.setBytes(5, beacons);
|
||||
statement.setInt(6, timestamp);
|
||||
statement.setInt(7, version);
|
||||
|
||||
statement.executeUpdate();
|
||||
|
||||
@@ -80,10 +81,13 @@ public class LodRepository
|
||||
.setZ(sectionZ)
|
||||
.setData(data)
|
||||
.setBeacons(beacons)
|
||||
.setTimestamp(timestamp);
|
||||
.setTimestamp(timestamp)
|
||||
.setVersion(version);
|
||||
} catch (SQLException exception) {
|
||||
this.getLogger().warning("Could not save LOD: " + exception);
|
||||
|
||||
this.database.clearQueryCache();
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -98,6 +102,8 @@ public class LodRepository
|
||||
statement.setInt(3, sectionZ);
|
||||
|
||||
try (ResultSet result = statement.executeQuery()) {
|
||||
result.next();
|
||||
|
||||
byte[] data = result.getBytes("data");
|
||||
|
||||
if (data == null) {
|
||||
@@ -115,6 +121,8 @@ public class LodRepository
|
||||
} catch (SQLException exception) {
|
||||
this.getLogger().warning("Could not load LOD: " + exception);
|
||||
|
||||
this.database.clearQueryCache();
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -129,11 +137,15 @@ public class LodRepository
|
||||
statement.setInt(3, sectionZ);
|
||||
|
||||
try (ResultSet result = statement.executeQuery()) {
|
||||
result.next();
|
||||
|
||||
return result.getInt(1) == 1;
|
||||
}
|
||||
} catch (SQLException exception) {
|
||||
this.getLogger().warning("Could not check LOD existence: " + exception);
|
||||
|
||||
this.database.clearQueryCache();
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -153,6 +165,8 @@ public class LodRepository
|
||||
} catch (SQLException exception) {
|
||||
this.getLogger().warning("Could not delete LOD: " + exception);
|
||||
|
||||
this.database.clearQueryCache();
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -76,16 +76,9 @@ public class DataPoint extends DataObject
|
||||
this.data |= ((long) startY & START_Y_MASK) << START_Y_OFFSET;
|
||||
}
|
||||
|
||||
|
||||
public int getStartY()
|
||||
{
|
||||
int raw = (int) ((this.data >> START_Y_OFFSET) & START_Y_MASK);
|
||||
|
||||
if ((raw & 0x0800) != 0) {
|
||||
raw |= (int) ~START_Y_MASK;
|
||||
}
|
||||
|
||||
return raw;
|
||||
return (int) ((this.data >> START_Y_OFFSET) & START_Y_MASK);
|
||||
}
|
||||
|
||||
public void setSkyLight(byte skyLight)
|
||||
|
||||
@@ -42,6 +42,10 @@ public class Lod extends DataObject
|
||||
|
||||
public static int width = 64;
|
||||
|
||||
protected static volatile byte[] compressedGenerationStep;
|
||||
|
||||
protected static volatile byte[] compressedWorldCompressionType;
|
||||
|
||||
protected WorldInterface worldInterface;
|
||||
|
||||
protected SectionPosition position;
|
||||
@@ -63,7 +67,7 @@ public class Lod extends DataObject
|
||||
|
||||
protected void encodeData(Encoder encoder)
|
||||
{
|
||||
Encoder toCompress = new Encoder();
|
||||
Encoder toCompress = new Encoder(8192);
|
||||
|
||||
for (List<DataPoint> column : this.columns) {
|
||||
toCompress.writeShort(column.size());
|
||||
@@ -79,12 +83,25 @@ public class Lod extends DataObject
|
||||
encoder.write(compressed);
|
||||
}
|
||||
|
||||
protected void encodeAdjacentData(Encoder encoder)
|
||||
{
|
||||
encoder.writeInt(0);
|
||||
encoder.writeInt(0);
|
||||
encoder.writeInt(0);
|
||||
encoder.writeInt(0);
|
||||
}
|
||||
|
||||
protected void encodeColumnGenerationStep(Encoder encoder)
|
||||
{
|
||||
byte[] bytesToCompress = new byte[Lod.width * Lod.width];
|
||||
Arrays.fill(bytesToCompress, (byte) GenerationStep.LIGHT.value);
|
||||
byte[] compressed = Lod.compressedGenerationStep;
|
||||
|
||||
byte[] compressed = this.compress(bytesToCompress);
|
||||
if (compressed == null) {
|
||||
byte[] bytesToCompress = new byte[Lod.width * Lod.width];
|
||||
Arrays.fill(bytesToCompress, (byte) GenerationStep.LIGHT.value);
|
||||
|
||||
compressed = this.compress(bytesToCompress);
|
||||
Lod.compressedGenerationStep = compressed;
|
||||
}
|
||||
|
||||
encoder.writeInt(compressed.length);
|
||||
encoder.write(compressed);
|
||||
@@ -92,9 +109,14 @@ public class Lod extends DataObject
|
||||
|
||||
protected void encodeWorldCompressionType(Encoder encoder)
|
||||
{
|
||||
byte[] bytesToCompress = new byte[Lod.width * Lod.width]; // All zeroes means WorldCompressionType.STRICT
|
||||
byte[] compressed = Lod.compressedWorldCompressionType;
|
||||
|
||||
byte[] compressed = this.compress(bytesToCompress);
|
||||
if (compressed == null) {
|
||||
byte[] bytesToCompress = new byte[Lod.width * Lod.width]; // All zeroes means WorldCompressionType.STRICT
|
||||
|
||||
compressed = this.compress(bytesToCompress);
|
||||
Lod.compressedWorldCompressionType = compressed;
|
||||
}
|
||||
|
||||
encoder.writeInt(compressed.length);
|
||||
encoder.write(compressed);
|
||||
@@ -102,7 +124,7 @@ public class Lod extends DataObject
|
||||
|
||||
protected void encodeMappings(Encoder encoder)
|
||||
{
|
||||
Encoder toCompress = new Encoder();
|
||||
Encoder toCompress = new Encoder(1024);
|
||||
|
||||
toCompress.writeInt(this.idMappings.size());
|
||||
|
||||
@@ -126,6 +148,7 @@ public class Lod extends DataObject
|
||||
encoder.writeInt(0); // Checksum
|
||||
|
||||
this.encodeData(encoder);
|
||||
this.encodeAdjacentData(encoder);
|
||||
this.encodeColumnGenerationStep(encoder);
|
||||
this.encodeWorldCompressionType(encoder);
|
||||
this.encodeMappings(encoder);
|
||||
@@ -146,7 +169,11 @@ public class Lod extends DataObject
|
||||
ByteArrayOutputStream compressedStream = new ByteArrayOutputStream();
|
||||
|
||||
try {
|
||||
OutputStream compressorStream = new XZOutputStream(compressedStream, new LZMA2Options(3), XZ.CHECK_CRC64);
|
||||
LZMA2Options options = new LZMA2Options(3);
|
||||
|
||||
options.setDictSize(Math.max(LZMA2Options.DICT_SIZE_MIN, Math.min(uncompressedData.length, 1 << 20)));
|
||||
|
||||
OutputStream compressorStream = new XZOutputStream(compressedStream, options, XZ.CHECK_CRC64);
|
||||
|
||||
compressorStream.write(uncompressedData);
|
||||
compressorStream.flush();
|
||||
|
||||
@@ -30,7 +30,6 @@ import no.jckf.dhsupport.core.message.plugin.FullDataSourceResponseMessage;
|
||||
import no.jckf.dhsupport.core.world.WorldInterface;
|
||||
import org.bukkit.Bukkit;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.UUID;
|
||||
|
||||
public class LodHandler
|
||||
@@ -50,7 +49,7 @@ public class LodHandler
|
||||
public void register()
|
||||
{
|
||||
this.pluginMessageHandler.getEventBus().registerHandler(FullDataSourceRequestMessage.class, (requestMessage) -> {
|
||||
//this.dhSupport.info("LOD request for " + requestMessage.getPosition().getX() + " x " + requestMessage.getPosition().getZ());
|
||||
//this.dhSupport.info("LOD request for " + requestMessage.getPosition().getX() + " " + requestMessage.getPosition().getZ());
|
||||
|
||||
if (requestMessage.getPosition().getDetailLevel() != 6) {
|
||||
ExceptionMessage exceptionMessage = new ExceptionMessage();
|
||||
@@ -91,6 +90,15 @@ public class LodHandler
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.dhSupport.isPaused() && !this.dhSupport.getLodRepository().lodExists(worldUuid, position.getX(), position.getZ())) {
|
||||
ExceptionMessage exceptionMessage = new ExceptionMessage();
|
||||
exceptionMessage.isResponseTo(requestMessage);
|
||||
exceptionMessage.setTypeId(ExceptionMessage.TYPE_REQUEST_REJECTED);
|
||||
exceptionMessage.setMessage("Server has paused Distant Horizons");
|
||||
this.pluginMessageHandler.sendPluginMessage(requestMessage.getSender(), exceptionMessage);
|
||||
return;
|
||||
}
|
||||
|
||||
int worldX = Coordinates.sectionToBlock(position.getX());
|
||||
int worldZ = Coordinates.sectionToBlock(position.getZ());
|
||||
|
||||
@@ -165,16 +173,16 @@ public class LodHandler
|
||||
FullDataChunkMessage chunkResponse = new FullDataChunkMessage();
|
||||
chunkResponse.setBufferId(myBufferId);
|
||||
chunkResponse.setIsFirst(chunkNo == 0);
|
||||
chunkResponse.setData(Arrays.copyOfRange(
|
||||
chunkResponse.setData(
|
||||
data,
|
||||
CHUNK_SIZE * chunkNo,
|
||||
Math.min(CHUNK_SIZE * chunkNo + CHUNK_SIZE, data.length)
|
||||
));
|
||||
Math.min(CHUNK_SIZE, data.length - CHUNK_SIZE * chunkNo)
|
||||
);
|
||||
|
||||
this.pluginMessageHandler.sendPluginMessage(requestMessage.getSender(), chunkResponse);
|
||||
}
|
||||
|
||||
//this.dhSupport.info("LOD in " + chunkCount + " parts sent for " + requestMessage.getPosition().getX() + " x " + requestMessage.getPosition().getZ());
|
||||
//this.dhSupport.info("LOD in " + chunkCount + " parts sent for " + requestMessage.getPosition().getX() + " " + requestMessage.getPosition().getZ());
|
||||
}
|
||||
|
||||
this.pluginMessageHandler.sendPluginMessage(requestMessage.getSender(), responseMessage);
|
||||
|
||||
@@ -22,11 +22,17 @@ import no.jckf.dhsupport.core.DhSupport;
|
||||
import no.jckf.dhsupport.core.configuration.Configuration;
|
||||
import no.jckf.dhsupport.core.configuration.DhsConfig;
|
||||
import no.jckf.dhsupport.core.message.plugin.LevelInitMessage;
|
||||
import no.jckf.dhsupport.core.message.plugin.LevelInitRequestMessage;
|
||||
import no.jckf.dhsupport.core.message.plugin.RemotePlayerConfigMessage;
|
||||
import no.jckf.dhsupport.core.world.WorldInterface;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.NamespacedKey;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
public class PlayerConfigHandler
|
||||
{
|
||||
public final static String USAGE_PERMISSION = "distant_horizons.worlds.%s";
|
||||
@@ -43,16 +49,28 @@ public class PlayerConfigHandler
|
||||
|
||||
public void register()
|
||||
{
|
||||
this.pluginMessageHandler.getEventBus().registerHandler(RemotePlayerConfigMessage.class, (configMessage) -> {
|
||||
this.pluginMessageHandler.getEventBus().registerHandler(LevelInitRequestMessage.class, (initRequestMessage) -> {
|
||||
// TODO: Some sort of Player wrapper or interface object. Bukkit classes should not be imported here.
|
||||
Player player = Bukkit.getPlayer(configMessage.getSender());
|
||||
Player player = Bukkit.getPlayer(initRequestMessage.getSender());
|
||||
|
||||
WorldInterface world = this.dhSupport.getWorldInterface(player.getWorld().getUID());
|
||||
NamespacedKey worldKey = NamespacedKey.fromString(initRequestMessage.getWorldKey());
|
||||
|
||||
String permission = USAGE_PERMISSION.formatted(world.getName());
|
||||
boolean dhUseIsAllowed = !player.isPermissionSet(permission) || player.hasPermission(permission);
|
||||
// TODO: This too 👎
|
||||
UUID worldUuid = null;
|
||||
|
||||
double coordinateScale = world.getCoordinateScale();
|
||||
for (World world : Bukkit.getServer().getWorlds()) {
|
||||
if (world.getKey().equals(worldKey)) {
|
||||
worldUuid = world.getUID();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (worldUuid == null) {
|
||||
this.dhSupport.warning("Player " + player.getName() + " requested init for unknown world " + worldKey + ".");
|
||||
return;
|
||||
}
|
||||
|
||||
WorldInterface world = this.dhSupport.getWorldInterface(worldUuid);
|
||||
|
||||
Configuration dhsConfig = world.getConfig();
|
||||
|
||||
@@ -63,12 +81,24 @@ public class PlayerConfigHandler
|
||||
levelKey = levelKeyPrefix + levelKey;
|
||||
}
|
||||
|
||||
this.dhSupport.info("Received DH config for " + player.getName() + " in " + levelKey);
|
||||
LevelInitMessage levelInitResponse = new LevelInitMessage();
|
||||
levelInitResponse.setNamespacedKey(worldKey.toString());
|
||||
levelInitResponse.setServerKey(dhsConfig.getString(DhsConfig.SERVER_KEY, ""));
|
||||
levelInitResponse.setLevelKey(levelKey);
|
||||
levelInitResponse.setTime(System.currentTimeMillis());
|
||||
this.pluginMessageHandler.sendPluginMessage(initRequestMessage.getSender(), levelInitResponse);
|
||||
});
|
||||
|
||||
LevelInitMessage levelKeyResponse = new LevelInitMessage();
|
||||
levelKeyResponse.setKey(levelKey);
|
||||
levelKeyResponse.setTime(System.currentTimeMillis());
|
||||
this.pluginMessageHandler.sendPluginMessage(configMessage.getSender(), levelKeyResponse);
|
||||
this.pluginMessageHandler.getEventBus().registerHandler(RemotePlayerConfigMessage.class, (configMessage) -> {
|
||||
// TODO: Some sort of Player wrapper or interface object. Bukkit classes should not be imported here.
|
||||
Player player = Bukkit.getPlayer(configMessage.getSender());
|
||||
|
||||
WorldInterface world = this.dhSupport.getWorldInterface(player.getWorld().getUID());
|
||||
|
||||
String permission = USAGE_PERMISSION.formatted(world.getName());
|
||||
boolean dhUseIsAllowed = !player.isPermissionSet(permission) || player.hasPermission(permission);
|
||||
|
||||
Configuration dhsConfig = world.getConfig();
|
||||
|
||||
Configuration clientConfig = configMessage.toConfiguration();
|
||||
|
||||
@@ -80,9 +110,19 @@ public class PlayerConfigHandler
|
||||
Object clientValue = clientConfig.get(key);
|
||||
Object keepValue = null;
|
||||
|
||||
if (key.equals(DhsConfig.DISTANT_GENERATION_ENABLED) && !dhUseIsAllowed) {
|
||||
this.dhSupport.info("Player " + player.getName() + " has been denied the use of Distant Horizons through permissions.");
|
||||
if (key.equals(DhsConfig.DISTANT_GENERATION_ENABLED)) {
|
||||
if (dhUseIsAllowed) {
|
||||
if (!(boolean) clientValue) {
|
||||
player.sendMessage(ChatColor.GOLD + "This server supports Distant Horizons, but you have disabled Distant Generation in your DH settings.");
|
||||
}
|
||||
} else {
|
||||
this.dhSupport.info("Player " + player.getName() + " has been denied the use of Distant Horizons through permissions.");
|
||||
|
||||
keepValue = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (key.equals(DhsConfig.REAL_TIME_UPDATES_ENABLED) && !dhUseIsAllowed) {
|
||||
keepValue = false;
|
||||
}
|
||||
|
||||
@@ -109,7 +149,7 @@ public class PlayerConfigHandler
|
||||
}
|
||||
}
|
||||
|
||||
clientConfig.set(key, keepValue);
|
||||
clientConfig.set(key, keepValue == null ? -1 : keepValue);
|
||||
}
|
||||
|
||||
this.dhSupport.setPlayerConfiguration(configMessage.getSender(), clientConfig);
|
||||
|
||||
@@ -36,9 +36,9 @@ public class PluginMessageHandler
|
||||
|
||||
private MessageTypeRegistry messageTypeRegistry;
|
||||
|
||||
public final String pluginChannel = "distant_horizons:message";
|
||||
public final String pluginChannel = "distant_horizons:msg";
|
||||
|
||||
public final short protocolVersion = 11;
|
||||
public final short protocolVersion = 15;
|
||||
|
||||
private EventBus<PluginMessage> eventBus;
|
||||
|
||||
@@ -53,13 +53,14 @@ public class PluginMessageHandler
|
||||
this.messageTypeRegistry.registerMessageType(0, null);
|
||||
this.messageTypeRegistry.registerMessageType(1, CloseReasonMessage.class);
|
||||
this.messageTypeRegistry.registerMessageType(2, LevelInitMessage.class);
|
||||
this.messageTypeRegistry.registerMessageType(3, RemotePlayerConfigMessage.class);
|
||||
this.messageTypeRegistry.registerMessageType(4, CancelMessage.class);
|
||||
this.messageTypeRegistry.registerMessageType(5, ExceptionMessage.class);
|
||||
this.messageTypeRegistry.registerMessageType(6, FullDataSourceRequestMessage.class);
|
||||
this.messageTypeRegistry.registerMessageType(7, FullDataSourceResponseMessage.class);
|
||||
this.messageTypeRegistry.registerMessageType(8, FullDataPartialUpdateMessage.class);
|
||||
this.messageTypeRegistry.registerMessageType(9, FullDataChunkMessage.class);
|
||||
this.messageTypeRegistry.registerMessageType(3, LevelInitRequestMessage.class);
|
||||
this.messageTypeRegistry.registerMessageType(4, RemotePlayerConfigMessage.class);
|
||||
this.messageTypeRegistry.registerMessageType(5, CancelMessage.class);
|
||||
this.messageTypeRegistry.registerMessageType(6, ExceptionMessage.class);
|
||||
this.messageTypeRegistry.registerMessageType(7, FullDataSourceRequestMessage.class);
|
||||
this.messageTypeRegistry.registerMessageType(8, FullDataSourceResponseMessage.class);
|
||||
this.messageTypeRegistry.registerMessageType(9, FullDataPartialUpdateMessage.class);
|
||||
this.messageTypeRegistry.registerMessageType(10, FullDataChunkMessage.class);
|
||||
}
|
||||
|
||||
public void onEnable()
|
||||
@@ -94,6 +95,10 @@ public class PluginMessageHandler
|
||||
return;
|
||||
}
|
||||
|
||||
if (message instanceof CloseReasonMessage) {
|
||||
throw new RuntimeException(((CloseReasonMessage) message).getReason());
|
||||
}
|
||||
|
||||
message.setSender(senderUuid);
|
||||
|
||||
this.eventBus.dispatch(message);
|
||||
@@ -112,7 +117,7 @@ public class PluginMessageHandler
|
||||
CloseReasonMessage closeReasonMessage = new CloseReasonMessage();
|
||||
closeReasonMessage.setReason((protocolVersion > this.protocolVersion ? "Server" : "Client") + " is outdated.");
|
||||
this.sendPluginMessage(senderUuid, closeReasonMessage);
|
||||
return null;
|
||||
return closeReasonMessage;
|
||||
}
|
||||
|
||||
// Read the message type ID.
|
||||
@@ -149,19 +154,7 @@ public class PluginMessageHandler
|
||||
return;
|
||||
}
|
||||
|
||||
byte[] data;
|
||||
|
||||
try {
|
||||
Encoder encoder = new Encoder();
|
||||
message.encode(encoder);
|
||||
data = encoder.toByteArray();
|
||||
} catch (Exception exception) {
|
||||
this.dhSupport.warning("Failed to encode " + message.getClass().getSimpleName());
|
||||
exception.printStackTrace();
|
||||
return;
|
||||
}
|
||||
|
||||
Encoder encoder = new Encoder();
|
||||
Encoder encoder = new Encoder(message.bufferSize());
|
||||
|
||||
encoder.writeShort(this.protocolVersion);
|
||||
encoder.writeShort(messageTypeId);
|
||||
@@ -170,7 +163,13 @@ public class PluginMessageHandler
|
||||
encoder.writeInt(((TrackablePluginMessage) message).getTracker());
|
||||
}
|
||||
|
||||
encoder.write(data);
|
||||
try {
|
||||
message.encode(encoder);
|
||||
} catch (Exception exception) {
|
||||
this.dhSupport.warning("Failed to encode " + message.getClass().getSimpleName());
|
||||
exception.printStackTrace();
|
||||
return;
|
||||
}
|
||||
|
||||
byte[] fullMessage = encoder.toByteArray();
|
||||
|
||||
|
||||
@@ -24,7 +24,6 @@ import no.jckf.dhsupport.core.dataobject.*;
|
||||
import no.jckf.dhsupport.core.world.WorldInterface;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.awt.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
@@ -55,13 +54,14 @@ public class FastOverworldBuilder extends LodBuilder
|
||||
boolean scanToSeaLevel = this.worldInterface.getConfig().getBool(DhsConfig.SCAN_TO_SEA_LEVEL, false);
|
||||
boolean underfill = this.worldInterface.getConfig().getBool(DhsConfig.FAST_UNDERFILL, true);
|
||||
boolean includeNonCollidingTopLayer = this.worldInterface.getConfig().getBool(DhsConfig.INCLUDE_NON_COLLIDING_TOP_LAYER, true);
|
||||
boolean sampleBiomes3d = this.worldInterface.getConfig().getBool(DhsConfig.SAMPLE_BIOMES_3D, false);
|
||||
|
||||
List<IdMapping> idMappings = new ArrayList<>();
|
||||
Map<String, Integer> mapMap = new HashMap<>();
|
||||
List<IdMapping> idMappings = new ArrayList<>(64);
|
||||
Map<String, Map<String, Integer>> mapMap = new HashMap<>();
|
||||
|
||||
List<List<DataPoint>> columns = new ArrayList<>();
|
||||
List<List<DataPoint>> columns = new ArrayList<>(Lod.width * Lod.width);
|
||||
|
||||
List<Beacon> beacons = new ArrayList<>();
|
||||
List<Beacon> beacons = new ArrayList<>(8);
|
||||
|
||||
for (int relativeX = 0; relativeX < Lod.width; relativeX++) {
|
||||
for (int relativeZ = 0; relativeZ < Lod.width; relativeZ++) {
|
||||
@@ -94,9 +94,13 @@ public class FastOverworldBuilder extends LodBuilder
|
||||
// Distance from bottom to top-most block.
|
||||
int relativeTopLayer = topLayer - minY;
|
||||
|
||||
String biome = this.worldInterface.getBiomeAt(worldX, worldZ);
|
||||
String biome = "";
|
||||
|
||||
List<DataPoint> column = new ArrayList<>();
|
||||
if (!sampleBiomes3d) {
|
||||
biome = this.worldInterface.getBiomeAt(worldX, worldZ);
|
||||
}
|
||||
|
||||
List<DataPoint> column = new ArrayList<>(8);
|
||||
|
||||
@Nullable
|
||||
DataPoint previous = null;
|
||||
@@ -125,7 +129,16 @@ public class FastOverworldBuilder extends LodBuilder
|
||||
String material = this.worldInterface.getMaterialAt(worldX, highWorldY, worldZ);
|
||||
|
||||
if (this.worldInterface.isBeacon(worldX, highWorldY, worldZ)) {
|
||||
beacons.add(new Beacon(worldX, highWorldY, worldZ, Color.WHITE.getRGB())); // TODO: Color
|
||||
beacons.add(new Beacon(
|
||||
worldX,
|
||||
highWorldY,
|
||||
worldZ,
|
||||
worldInterface.getBeaconColor(
|
||||
worldX,
|
||||
highWorldY,
|
||||
worldZ
|
||||
)
|
||||
));
|
||||
}
|
||||
|
||||
if (solidGround == null && (!scanToSeaLevel || highWorldY <= seaLevel)) {
|
||||
@@ -141,15 +154,22 @@ public class FastOverworldBuilder extends LodBuilder
|
||||
}
|
||||
}
|
||||
|
||||
String compositeKey = biome + "|" + material;
|
||||
if (sampleBiomes3d) {
|
||||
biome = this.worldInterface.getBiomeAt(worldX, highWorldY, worldZ);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
Integer id = mapMap.get(compositeKey);
|
||||
Map<String, Integer> biomeMap = mapMap.get(biome);
|
||||
if (biomeMap == null) {
|
||||
biomeMap = new HashMap<>();
|
||||
mapMap.put(biome, biomeMap);
|
||||
}
|
||||
|
||||
Integer id = biomeMap.get(material);
|
||||
|
||||
if (id == null) {
|
||||
idMappings.add(new IdMapping(biome, material, null));
|
||||
id = idMappings.size() - 1;
|
||||
mapMap.put(compositeKey, id);
|
||||
biomeMap.put(material, id);
|
||||
}
|
||||
|
||||
DataPoint point;
|
||||
@@ -167,10 +187,8 @@ public class FastOverworldBuilder extends LodBuilder
|
||||
point.setHeight(thisStep);
|
||||
point.setMappingId(id);
|
||||
|
||||
if (highWorldY + 1 < maxY) {
|
||||
point.setSkyLight(this.worldInterface.getSkyLightAt(worldX, highWorldY + 1, worldZ));
|
||||
point.setBlockLight(this.worldInterface.getBlockLightAt(worldX, highWorldY + 1, worldZ));
|
||||
}
|
||||
point.setSkyLight(this.worldInterface.getSkyLightAt(worldX, highWorldY + 1, worldZ));
|
||||
point.setBlockLight(this.worldInterface.getBlockLightAt(worldX, highWorldY + 1, worldZ));
|
||||
|
||||
if (material.equals("minecraft:air") || material.equals("minecraft:void_air")) {
|
||||
// Start by filling the top of the column with air, then jump down to the top layer.
|
||||
|
||||
@@ -24,7 +24,6 @@ import no.jckf.dhsupport.core.dataobject.*;
|
||||
import no.jckf.dhsupport.core.world.WorldInterface;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.awt.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
@@ -51,13 +50,14 @@ public class FullBuilder extends LodBuilder
|
||||
|
||||
boolean includeNonCollidingTopLayer = this.worldInterface.getConfig().getBool(DhsConfig.INCLUDE_NON_COLLIDING_TOP_LAYER, true);
|
||||
boolean performUnderglowHack = this.worldInterface.getConfig().getBool(DhsConfig.PERFORM_UNDERGLOW_HACK, false);
|
||||
boolean sampleBiomes3d = this.worldInterface.getConfig().getBool(DhsConfig.SAMPLE_BIOMES_3D, false);
|
||||
|
||||
List<IdMapping> idMappings = new ArrayList<>();
|
||||
Map<String, Integer> mapMap = new HashMap<>();
|
||||
List<IdMapping> idMappings = new ArrayList<>(64);
|
||||
Map<String, Map<String, Map<String, Integer>>> mapMap = new HashMap<>();
|
||||
|
||||
List<List<DataPoint>> columns = new ArrayList<>();
|
||||
List<List<DataPoint>> columns = new ArrayList<>(Lod.width * Lod.width);
|
||||
|
||||
List<Beacon> beacons = new ArrayList<>();
|
||||
List<Beacon> beacons = new ArrayList<>(8);
|
||||
|
||||
for (int relativeX = 0; relativeX < Lod.width; relativeX++) {
|
||||
for (int relativeZ = 0; relativeZ < Lod.width; relativeZ++) {
|
||||
@@ -90,9 +90,13 @@ public class FullBuilder extends LodBuilder
|
||||
// Distance from bottom to top-most block.
|
||||
int relativeTopLayer = topLayer - minY;
|
||||
|
||||
String biome = this.worldInterface.getBiomeAt(worldX, worldZ);
|
||||
String biome = "";
|
||||
|
||||
List<DataPoint> column = new ArrayList<>();
|
||||
if (!sampleBiomes3d) {
|
||||
biome = this.worldInterface.getBiomeAt(worldX, worldZ);
|
||||
}
|
||||
|
||||
List<DataPoint> column = new ArrayList<>(32);
|
||||
|
||||
@Nullable
|
||||
DataPoint previous = null;
|
||||
@@ -118,18 +122,42 @@ public class FullBuilder extends LodBuilder
|
||||
String material = this.worldInterface.getMaterialAt(worldX, highWorldY, worldZ);
|
||||
|
||||
if (this.worldInterface.isBeacon(worldX, highWorldY, worldZ)) {
|
||||
beacons.add(new Beacon(worldX, highWorldY, worldZ, Color.WHITE.getRGB())); // TODO: Color
|
||||
beacons.add(new Beacon(
|
||||
worldX,
|
||||
highWorldY,
|
||||
worldZ,
|
||||
worldInterface.getBeaconColor(
|
||||
worldX,
|
||||
highWorldY,
|
||||
worldZ
|
||||
)
|
||||
));
|
||||
}
|
||||
|
||||
String compositeKey = biome + "|" + material + "|" + this.worldInterface.getBlockStateAsStringAt(worldX, highWorldY, worldZ);
|
||||
if (sampleBiomes3d) {
|
||||
biome = this.worldInterface.getBiomeAt(worldX, highWorldY, worldZ);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
Integer id = mapMap.get(compositeKey);
|
||||
Map<String, Map<String, Integer>> biomeMap = mapMap.get(biome);
|
||||
if (biomeMap == null) {
|
||||
biomeMap = new HashMap<>();
|
||||
mapMap.put(biome, biomeMap);
|
||||
}
|
||||
|
||||
Map<String, Integer> materialMap = biomeMap.get(material);
|
||||
if (materialMap == null) {
|
||||
materialMap = new HashMap<>();
|
||||
biomeMap.put(material, materialMap);
|
||||
}
|
||||
|
||||
String blockState = this.worldInterface.getBlockStateAsStringAt(worldX, highWorldY, worldZ);
|
||||
|
||||
Integer id = materialMap.get(blockState);
|
||||
|
||||
if (id == null) {
|
||||
idMappings.add(new IdMapping(biome, material, this.worldInterface.getBlockPropertiesAt(worldX, highWorldY, worldZ)));
|
||||
id = idMappings.size() - 1;
|
||||
mapMap.put(compositeKey, id);
|
||||
materialMap.put(blockState, id);
|
||||
}
|
||||
|
||||
DataPoint point;
|
||||
@@ -147,10 +175,8 @@ public class FullBuilder extends LodBuilder
|
||||
point.setHeight(thisStep);
|
||||
point.setMappingId(id);
|
||||
|
||||
if (highWorldY + 1 < maxY) {
|
||||
point.setSkyLight(this.worldInterface.getSkyLightAt(worldX, highWorldY + 1, worldZ));
|
||||
point.setBlockLight(this.worldInterface.getBlockLightAt(worldX, highWorldY + 1, worldZ));
|
||||
}
|
||||
point.setSkyLight(this.worldInterface.getSkyLightAt(worldX, highWorldY + 1, worldZ));
|
||||
point.setBlockLight(this.worldInterface.getBlockLightAt(worldX, highWorldY + 1, worldZ));
|
||||
|
||||
if (material.equals("minecraft:air") || material.equals("minecraft:void_air")) {
|
||||
// Start by filling the top of the column with air, then jump down to the top layer.
|
||||
|
||||
@@ -25,6 +25,11 @@ import no.jckf.dhsupport.core.bytestream.Encoder;
|
||||
|
||||
public abstract class Message implements Encodable, Decodable
|
||||
{
|
||||
public int bufferSize()
|
||||
{
|
||||
return 64;
|
||||
}
|
||||
|
||||
public void encode(Encoder encoder)
|
||||
{
|
||||
throw new UnsupportedOperationException();
|
||||
|
||||
@@ -26,6 +26,10 @@ public class FullDataChunkMessage extends PluginMessage
|
||||
|
||||
protected byte[] data;
|
||||
|
||||
protected int offset;
|
||||
|
||||
protected int length;
|
||||
|
||||
protected boolean isFirst = true;
|
||||
|
||||
public void setBufferId(int bufferId)
|
||||
@@ -38,9 +42,11 @@ public class FullDataChunkMessage extends PluginMessage
|
||||
return this.bufferId;
|
||||
}
|
||||
|
||||
public void setData(byte[] data)
|
||||
public void setData(byte[] data, int offset, int length)
|
||||
{
|
||||
this.data = data;
|
||||
this.offset = offset;
|
||||
this.length = length;
|
||||
}
|
||||
|
||||
public byte[] getData()
|
||||
@@ -58,12 +64,18 @@ public class FullDataChunkMessage extends PluginMessage
|
||||
return this.isFirst;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int bufferSize()
|
||||
{
|
||||
return 32 + this.length;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void encode(Encoder encoder)
|
||||
{
|
||||
encoder.writeInt(this.bufferId);
|
||||
encoder.writeInt(this.data.length);
|
||||
encoder.write(this.data);
|
||||
encoder.writeInt(this.length);
|
||||
encoder.write(this.data, this.offset, this.length);
|
||||
encoder.writeBoolean(this.isFirst);
|
||||
}
|
||||
}
|
||||
@@ -58,6 +58,12 @@ public class FullDataPartialUpdateMessage extends PluginMessage
|
||||
return this.beacons;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int bufferSize()
|
||||
{
|
||||
return 32 + this.beacons.length;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void encode(Encoder encoder)
|
||||
{
|
||||
|
||||
@@ -48,6 +48,12 @@ public class FullDataSourceResponseMessage extends TrackablePluginMessage
|
||||
return this.beacons;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int bufferSize()
|
||||
{
|
||||
return this.bufferId == null ? super.bufferSize() : 16 + this.beacons.length;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void encode(Encoder encoder)
|
||||
{
|
||||
|
||||
@@ -21,20 +21,44 @@ package no.jckf.dhsupport.core.message.plugin;
|
||||
import no.jckf.dhsupport.core.bytestream.Decoder;
|
||||
import no.jckf.dhsupport.core.bytestream.Encoder;
|
||||
|
||||
public class LevelInitMessage extends PluginMessage
|
||||
public class LevelInitMessage extends PluginMessage // TODO: Should probably eventually be a TrackablePluginMessage
|
||||
{
|
||||
protected String key;
|
||||
protected String namespacedKey;
|
||||
|
||||
protected String serverKey;
|
||||
|
||||
protected String levelKey;
|
||||
|
||||
protected long time;
|
||||
|
||||
public void setKey(String key)
|
||||
public void setNamespacedKey(String namespacedKey)
|
||||
{
|
||||
this.key = key;
|
||||
this.namespacedKey = namespacedKey;
|
||||
}
|
||||
|
||||
public String getKey()
|
||||
public String getNamespacedKey()
|
||||
{
|
||||
return this.key;
|
||||
return namespacedKey;
|
||||
}
|
||||
|
||||
public void setServerKey(String serverKey)
|
||||
{
|
||||
this.serverKey = serverKey;
|
||||
}
|
||||
|
||||
public String getServerKey()
|
||||
{
|
||||
return this.serverKey;
|
||||
}
|
||||
|
||||
public void setLevelKey(String levelKey)
|
||||
{
|
||||
this.levelKey = levelKey;
|
||||
}
|
||||
|
||||
public String getLevelKey()
|
||||
{
|
||||
return this.levelKey;
|
||||
}
|
||||
|
||||
public void setTime(long time)
|
||||
@@ -50,14 +74,18 @@ public class LevelInitMessage extends PluginMessage
|
||||
@Override
|
||||
public void encode(Encoder encoder)
|
||||
{
|
||||
encoder.writeShortString(this.key);
|
||||
encoder.writeShortString(this.namespacedKey);
|
||||
encoder.writeShortString(this.serverKey);
|
||||
encoder.writeShortString(this.levelKey);
|
||||
encoder.writeLong(this.time);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void decode(Decoder decoder)
|
||||
{
|
||||
this.key = decoder.readShortString();
|
||||
this.namespacedKey = decoder.readShortString();
|
||||
this.serverKey = decoder.readShortString();
|
||||
this.levelKey = decoder.readShortString();
|
||||
this.time = decoder.readLong();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* DH Support, server-side support for Distant Horizons.
|
||||
* Copyright (C) 2024 Jim C K Flaten
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package no.jckf.dhsupport.core.message.plugin;
|
||||
|
||||
import no.jckf.dhsupport.core.bytestream.Decoder;
|
||||
|
||||
public class LevelInitRequestMessage extends PluginMessage
|
||||
{
|
||||
protected String worldKey;
|
||||
|
||||
public String getWorldKey()
|
||||
{
|
||||
return worldKey;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void decode(Decoder decoder)
|
||||
{
|
||||
this.worldKey = decoder.readShortString();
|
||||
}
|
||||
}
|
||||
@@ -21,6 +21,7 @@ package no.jckf.dhsupport.core.scheduling;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public interface Scheduler
|
||||
@@ -29,10 +30,22 @@ public interface Scheduler
|
||||
|
||||
<U> CompletableFuture<U> runOnMainThread(Supplier<U> supplier);
|
||||
|
||||
<U> CompletableFuture<U> runOnMainThreadDelayed(Supplier<U> supplier, long delayTicks);
|
||||
|
||||
<U> CompletableFuture<U> runOnMainThreadDelayed(Supplier<U> supplier, long delayAmount, TimeUnit delayUnit);
|
||||
|
||||
<U> CompletableFuture<U> runOnRegionThread(UUID worldId, int x, int z, Supplier<U> supplier);
|
||||
|
||||
<U> CompletableFuture<U> runOnRegionThreadDelayed(UUID worldId, int x, int z, Supplier<U> supplier, long delayTicks);
|
||||
|
||||
<U> CompletableFuture<U> runOnRegionThreadDelayed(UUID worldId, int x, int z, Supplier<U> supplier, long delayAmount, TimeUnit delayUnit);
|
||||
|
||||
<U> CompletableFuture<U> runOnSeparateThread(Supplier<U> supplier);
|
||||
|
||||
<U> CompletableFuture<U> runOnSeparateThreadDelayed(Supplier<U> supplier, long delayTicks);
|
||||
|
||||
<U> CompletableFuture<U> runOnSeparateThreadDelayed(Supplier<U> supplier, long delayAmount, TimeUnit delayUnit);
|
||||
|
||||
void cancelTasks();
|
||||
|
||||
Executor getExecutor();
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* DH Support, server-side support for Distant Horizons.
|
||||
* Copyright (C) 2024 Jim C K Flaten
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package no.jckf.dhsupport.core.world;
|
||||
|
||||
public class MaterialRecord
|
||||
{
|
||||
protected final String key;
|
||||
|
||||
protected final boolean isMapped;
|
||||
|
||||
public MaterialRecord(String key, boolean mapped)
|
||||
{
|
||||
this.key = key;
|
||||
this.isMapped = mapped;
|
||||
}
|
||||
|
||||
public boolean isMapped()
|
||||
{
|
||||
return this.isMapped;
|
||||
}
|
||||
|
||||
public String toString()
|
||||
{
|
||||
return this.key;
|
||||
}
|
||||
}
|
||||
@@ -43,6 +43,10 @@ public interface WorldInterface extends Configurable
|
||||
|
||||
@Nullable Integer getWorldBorderRadius();
|
||||
|
||||
void setDummyMode(boolean enable);
|
||||
|
||||
boolean isDummyMode();
|
||||
|
||||
boolean chunkExists(int x, int z);
|
||||
|
||||
boolean isChunkLoaded(int x, int z);
|
||||
@@ -67,14 +71,30 @@ public interface WorldInterface extends Configurable
|
||||
|
||||
int getSeaLevel();
|
||||
|
||||
int getSpawnX();
|
||||
|
||||
int getSpawnY();
|
||||
|
||||
int getSpawnZ();
|
||||
|
||||
int getHighestYAt(int x, int z);
|
||||
|
||||
String getBiomeAt(int x, int z);
|
||||
|
||||
String getBiomeAt(int x, int y, int z);
|
||||
|
||||
String getMaterialAt(int x, int y, int z);
|
||||
|
||||
/**
|
||||
* Get the string representation of the block state at the given coordinates.
|
||||
* Block state properties are ordered in ascending order.
|
||||
*/
|
||||
String getBlockStateAsStringAt(int x, int y, int z);
|
||||
|
||||
/**
|
||||
* Get block properties as key/value pairs.
|
||||
* Ordered in ascending order.
|
||||
*/
|
||||
Map<String, String> getBlockPropertiesAt(int x, int y, int z);
|
||||
|
||||
byte getBlockLightAt(int x, int y, int z);
|
||||
@@ -82,4 +102,6 @@ public interface WorldInterface extends Configurable
|
||||
byte getSkyLightAt(int x, int y, int z);
|
||||
|
||||
boolean isBeacon(int x, int y, int z);
|
||||
|
||||
int getBeaconColor(int x, int y, int z);
|
||||
}
|
||||
@@ -3,7 +3,7 @@
|
||||
# Default: "{datadir}/data.sqlite"
|
||||
database_path: "{datadir}/data.sqlite"
|
||||
|
||||
# Maximum supported render distance. Clients will be capped to this value.
|
||||
# Maximum supported render distance, given as a radius in number of chunks. Clients will be capped to this value.
|
||||
# Required.
|
||||
# Default: 1024
|
||||
render_distance: 1024
|
||||
@@ -24,7 +24,7 @@ full_data_request_concurrency_limit: 20
|
||||
# Default: true
|
||||
real_time_updates_enabled: true
|
||||
|
||||
# How close to an update players have to be in order to receive it.
|
||||
# How close to an update players have to be in order to receive it. Given as a radius in number of chunks.
|
||||
# Required.
|
||||
# Default: 128
|
||||
real_time_update_radius: 128
|
||||
@@ -34,7 +34,7 @@ real_time_update_radius: 128
|
||||
# Default: true
|
||||
login_data_sync_enabled: true
|
||||
|
||||
# How far away can a player request updates for during login sync?
|
||||
# How far away can a player request updates for during login sync? Given as a radius in number of chunks.
|
||||
# Required.
|
||||
# Default: 128
|
||||
login_data_sync_radius: 128
|
||||
@@ -60,10 +60,18 @@ max_data_transfer_speed: 0
|
||||
# Default: true
|
||||
#generate_new_chunks_warning: false
|
||||
|
||||
# Specs for a fake LOD to send when chunk generation is disabled and LOD requests otherwise would have been rejected.
|
||||
# Optional.
|
||||
# Default: Disabled
|
||||
#dummy_chunk:
|
||||
# - "0-4,bedrock"
|
||||
# - "5-79,stone"
|
||||
# - "80-126,water"
|
||||
|
||||
# How frequently (in seconds) should LODs be updated if world changes are detected?
|
||||
# Required.
|
||||
# Default: 5
|
||||
lod_refresh_interval: 5
|
||||
# Default: 15
|
||||
lod_refresh_interval: 15
|
||||
|
||||
# Select which LOD builder to use.
|
||||
# Valid options are: FullBuilder, FastOverworldBuilder, and None.
|
||||
@@ -71,6 +79,13 @@ lod_refresh_interval: 5
|
||||
# Default: FullBuilder
|
||||
builder_type: FullBuilder
|
||||
|
||||
# Trust world height map.
|
||||
# Disable if you're using a world that was generated with an external tool and your LODs come out broken.
|
||||
# WARNING: This will make LOD building MUCH slower!
|
||||
# Optional.
|
||||
# Default: true
|
||||
#trust_height_map: false
|
||||
|
||||
# Number of blocks to advance between each sample on the Y axis when generating LODs.
|
||||
# A setting of 1 will sample every block, while 2 will sample every other block, and so on.
|
||||
# CURRENTLY BROKEN. THIS VALUE IS IGNORED.
|
||||
@@ -84,6 +99,12 @@ builder_resolution: 2
|
||||
# Default: true
|
||||
#include_non-colliding_top_layer: false
|
||||
|
||||
# If your server is accessible via multiple different addresses, consider setting a server key to let clients know
|
||||
# that even though the address is different; it's the same server.
|
||||
# This value must be GLOBALLY UNIQUE! No other server on planet Earth should have the same server key as you.
|
||||
# Optional.
|
||||
#server_key:
|
||||
|
||||
# When running DHS on a proxied server, set this to a value that is unique to this specific server.
|
||||
# Optional.
|
||||
#level_key_prefix:
|
||||
@@ -100,6 +121,29 @@ builder_resolution: 2
|
||||
# Default: auto
|
||||
#vanilla_world_border_expansion: 12
|
||||
|
||||
# List of blocks to replace with other blocks during LOD building.
|
||||
# Useful to hide ugly blocks or valuable resources.
|
||||
# Don't use the "minecraft:" prefix for vanilla blocks.
|
||||
# DO use the namespace prefix for custom blocks.
|
||||
# Optional.
|
||||
# Default: Hide bamboo, seagrass, kelp, and iron + diamond ores.
|
||||
material_map:
|
||||
bamboo: air
|
||||
seagrass: water
|
||||
tall_seagrass: water
|
||||
kelp: water
|
||||
kelp_plant: water
|
||||
iron_ore: stone
|
||||
diamond_ore: stone
|
||||
|
||||
# Vertical distance to offset light data.
|
||||
# This is a hack to work around SPIGOT-7498. If you're somehow on a 1.20.1 server _with_ this patch, set the value to 0.
|
||||
# Optional.
|
||||
# Default:
|
||||
# - On 1.20.1: Minimum build height (-64 for vanilla worlds)
|
||||
# - Any other version: 0
|
||||
#light_offset_hack: 0
|
||||
|
||||
# World specific settings.
|
||||
# You can override any of the above settings on a per-world basis.
|
||||
worlds:
|
||||
@@ -107,6 +151,12 @@ worlds:
|
||||
# Use builder that is optimized for overworld.
|
||||
builder_type: FastOverworldBuilder
|
||||
|
||||
# Should biome be checked for every data point, or just once per column?
|
||||
# Enable if your world has 3d/vertical biomes.
|
||||
# Optional.
|
||||
# Default: false
|
||||
#sample_biomes_3d: true
|
||||
|
||||
# When using the FastOverworldBuilder, should LODs always contain data down to at least sea level?
|
||||
# Enable if your world has floating islands or mountains/structures with large overhangs above sea level.
|
||||
# Optional.
|
||||
@@ -119,7 +169,7 @@ worlds:
|
||||
# Default: true
|
||||
#fast_underfill: false
|
||||
|
||||
# Limit LOD generation to within this area.
|
||||
# Limit LOD generation to within this area. Alle three values are in blocks.
|
||||
# Optional.
|
||||
#border_center_x: 0
|
||||
#border_center_z: 0
|
||||
@@ -135,16 +185,54 @@ worlds:
|
||||
#perform_underglow_hack: true
|
||||
#builder_resolution: 1
|
||||
|
||||
# List of events that should trigger LOD updates.
|
||||
# Required.
|
||||
# Default: A bunch (:
|
||||
update_events:
|
||||
- org.bukkit.event.block.BlockPlaceEvent
|
||||
- org.bukkit.event.block.BlockBreakEvent
|
||||
# - org.bukkit.event.block.TNTPrimeEvent
|
||||
- org.bukkit.event.block.BlockExplodeEvent
|
||||
# - org.bukkit.event.block.BlockIgniteEvent
|
||||
# - org.bukkit.event.block.BlockBurnEvent
|
||||
# - org.bukkit.event.block.LeavesDecayEvent
|
||||
# - org.bukkit.event.block.BlockFadeEvent
|
||||
# - org.bukkit.event.block.BlockGrowEvent
|
||||
# - org.bukkit.event.block.MoistureChangeEvent
|
||||
# - org.bukkit.event.block.BlockRedstoneEvent
|
||||
- org.bukkit.event.block.BlockPistonExtendEvent
|
||||
- org.bukkit.event.block.BlockPistonRetractEvent
|
||||
# - org.bukkit.event.block.SpongeAbsorbEvent
|
||||
# - org.bukkit.event.inventory.FurnaceBurnEvent
|
||||
- org.bukkit.event.world.StructureGrowEvent
|
||||
- org.bukkit.event.world.ChunkPopulateEvent
|
||||
|
||||
# Number of worker threads used to generate LODs.
|
||||
# Does not affect Folia servers.
|
||||
# Required.
|
||||
# Default: 4
|
||||
scheduler_threads: 4
|
||||
|
||||
# Show a message to players who join if they don't have Distant Horizons installed?
|
||||
# Optional.
|
||||
# Default: Disabled
|
||||
#ad_message:
|
||||
# - "§aThis server supports §bDistant Horizons§a!"
|
||||
# - "§aVisit §nhttp://dh-mod.com/§r§a to learn more."
|
||||
|
||||
# If a user uses /dh should we give them a help text?
|
||||
# Optional.
|
||||
# Default: true
|
||||
#enable_dh_command_help_text: false
|
||||
|
||||
# Should we notify server admins when a new version of the plugin is available?
|
||||
# Optional.
|
||||
# Default: true
|
||||
#check_for_updates: false
|
||||
|
||||
# Enable debug logging?
|
||||
# Optional.
|
||||
# Default: false
|
||||
#debug: true
|
||||
|
||||
# Don't touch :)
|
||||
config_version: 8
|
||||
config_version: 10
|
||||
@@ -1,8 +1,7 @@
|
||||
name: DHSupport
|
||||
version: ${project.version}
|
||||
main: ${main.class}
|
||||
api-version: ${mc.api-version}
|
||||
mc-version: ${mc.api-version}${mc.api-version-patch}
|
||||
version: ${version}
|
||||
main: ${mainClass}
|
||||
api-version: ${mcApiVersion}
|
||||
prefix: DHS
|
||||
folia-supported: true
|
||||
author: Jckf
|
||||
|
||||
@@ -54,7 +54,7 @@ public class DataPointTest extends TestCase
|
||||
this.testHeight();
|
||||
}
|
||||
|
||||
public void testPositiveStartY()
|
||||
public void testStartY()
|
||||
{
|
||||
int randomNumber = (int) Math.floor(Math.random() * 100);
|
||||
|
||||
@@ -63,9 +63,9 @@ public class DataPointTest extends TestCase
|
||||
assertEquals(randomNumber, this.dataPoint.getStartY());
|
||||
}
|
||||
|
||||
public void testNegativeStartY()
|
||||
public void testHighStartY()
|
||||
{
|
||||
int randomNumber = (int) Math.floor(0 - Math.random() * 100);
|
||||
int randomNumber = 4000; // Chosen by fair dice roll. Guaranteed to be random.
|
||||
|
||||
this.dataPoint.setStartY(randomNumber);
|
||||
|
||||
@@ -74,7 +74,7 @@ public class DataPointTest extends TestCase
|
||||
|
||||
public void testStartYAgain()
|
||||
{
|
||||
this.testPositiveStartY();
|
||||
this.testStartY();
|
||||
}
|
||||
|
||||
public void testSkyLight()
|
||||
|
||||
Reference in new issue
Block a user