First public release
parent
93080246f6
commit
998c758df5
|
@ -0,0 +1,42 @@
|
||||||
|
---
|
||||||
|
kind: pipeline
|
||||||
|
type: docker
|
||||||
|
name: default
|
||||||
|
steps:
|
||||||
|
- name: "Build"
|
||||||
|
image: "eclipse-temurin:17-jdk"
|
||||||
|
commands:
|
||||||
|
- "./gradlew assemble"
|
||||||
|
- name: "Test"
|
||||||
|
image: "eclipse-temurin:17-jdk"
|
||||||
|
commands:
|
||||||
|
- "./gradlew test"
|
||||||
|
|
||||||
|
- name: "Create Release"
|
||||||
|
image: plugins/gitea-release
|
||||||
|
settings:
|
||||||
|
api_key:
|
||||||
|
from_secret: "gitea_api_key"
|
||||||
|
base_url:
|
||||||
|
from_secret: "gitea_base_url"
|
||||||
|
files: build/libs/DLib-*.jar
|
||||||
|
when:
|
||||||
|
event:
|
||||||
|
- tag
|
||||||
|
- name: "Publish to Maven"
|
||||||
|
image: "eclipse-temurin:17-jdk"
|
||||||
|
commands:
|
||||||
|
- "./gradlew publishReleasePublicationToMavenRepository"
|
||||||
|
environment:
|
||||||
|
MAVEN_REPO_URL:
|
||||||
|
from_secret: maven_repo_url
|
||||||
|
MAVEN_REPO_TOKEN:
|
||||||
|
from_secret: gitea_api_key
|
||||||
|
when:
|
||||||
|
event:
|
||||||
|
- tag
|
||||||
|
trigger:
|
||||||
|
event:
|
||||||
|
- push
|
||||||
|
- pull_request
|
||||||
|
- tag
|
|
@ -1,3 +1,7 @@
|
||||||
|
build/
|
||||||
|
.idea/
|
||||||
|
.gradle/
|
||||||
|
|
||||||
# ---> Kotlin
|
# ---> Kotlin
|
||||||
# Compiled class file
|
# Compiled class file
|
||||||
*.class
|
*.class
|
||||||
|
@ -23,3 +27,4 @@
|
||||||
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
|
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
|
||||||
hs_err_pid*
|
hs_err_pid*
|
||||||
|
|
||||||
|
!gradle/wrapper/gradle-wrapper.jar
|
||||||
|
|
|
@ -0,0 +1,90 @@
|
||||||
|
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
|
||||||
|
|
||||||
|
plugins {
|
||||||
|
kotlin("jvm") version "1.7.0"
|
||||||
|
kotlin("plugin.serialization") version "1.7.0"
|
||||||
|
id("maven-publish")
|
||||||
|
id("com.github.johnrengelman.shadow") version "7.1.2"
|
||||||
|
id("io.papermc.paperweight.userdev") version "1.3.5"
|
||||||
|
}
|
||||||
|
|
||||||
|
group = "nl.kallestruik"
|
||||||
|
version = "1.5.0"
|
||||||
|
|
||||||
|
repositories {
|
||||||
|
mavenCentral()
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
paperDevBundle("1.19-R0.1-SNAPSHOT")
|
||||||
|
|
||||||
|
implementation(kotlin("reflect"))
|
||||||
|
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.3.1")
|
||||||
|
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.0")
|
||||||
|
implementation("org.jetbrains.kotlin:kotlin-scripting-common")
|
||||||
|
implementation("org.jetbrains.kotlin:kotlin-scripting-jvm")
|
||||||
|
implementation("org.jetbrains.kotlin:kotlin-scripting-jvm-host")
|
||||||
|
|
||||||
|
testImplementation(kotlin("test-junit5"))
|
||||||
|
testImplementation("org.junit.jupiter:junit-jupiter-api:5.6.0")
|
||||||
|
testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.6.0")
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks {
|
||||||
|
build {
|
||||||
|
dependsOn(shadowJar)
|
||||||
|
}
|
||||||
|
assemble {
|
||||||
|
dependsOn(reobfJar)
|
||||||
|
}
|
||||||
|
|
||||||
|
test {
|
||||||
|
useJUnitPlatform()
|
||||||
|
}
|
||||||
|
|
||||||
|
withType<KotlinCompile> {
|
||||||
|
kotlinOptions.jvmTarget = "17"
|
||||||
|
}
|
||||||
|
|
||||||
|
processResources {
|
||||||
|
expand("version" to project.version)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val sourcesJar by tasks.creating(Jar::class) {
|
||||||
|
archiveClassifier.set("sources")
|
||||||
|
from(sourceSets["main"].allSource)
|
||||||
|
}
|
||||||
|
|
||||||
|
publishing {
|
||||||
|
publications {
|
||||||
|
create<MavenPublication>("release") {
|
||||||
|
from(components["kotlin"])
|
||||||
|
artifact(sourcesJar)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
repositories {
|
||||||
|
maven {
|
||||||
|
url = uri(System.getenv("MAVEN_REPO_URL") ?: "")
|
||||||
|
|
||||||
|
credentials(HttpHeaderCredentials::class) {
|
||||||
|
name = "Authorization"
|
||||||
|
value = "token ${System.getenv("MAVEN_REPO_TOKEN")}"
|
||||||
|
}
|
||||||
|
|
||||||
|
authentication {
|
||||||
|
create<HttpHeaderAuthentication>("Authentication Header")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
kotlin {
|
||||||
|
sourceSets {
|
||||||
|
all {
|
||||||
|
languageSettings.optIn("kotlin.RequiresOptIn")
|
||||||
|
languageSettings.optIn("kotlinx.serialization.ExperimentalSerializationApi")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1 @@
|
||||||
|
kotlin.code.style=official
|
Binary file not shown.
|
@ -0,0 +1,5 @@
|
||||||
|
distributionBase=GRADLE_USER_HOME
|
||||||
|
distributionPath=wrapper/dists
|
||||||
|
distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-all.zip
|
||||||
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
|
zipStorePath=wrapper/dists
|
|
@ -0,0 +1,185 @@
|
||||||
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
|
#
|
||||||
|
# Copyright 2015 the original author or 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.
|
||||||
|
#
|
||||||
|
|
||||||
|
##############################################################################
|
||||||
|
##
|
||||||
|
## Gradle start up script for UN*X
|
||||||
|
##
|
||||||
|
##############################################################################
|
||||||
|
|
||||||
|
# Attempt to set APP_HOME
|
||||||
|
# Resolve links: $0 may be a link
|
||||||
|
PRG="$0"
|
||||||
|
# Need this for relative symlinks.
|
||||||
|
while [ -h "$PRG" ] ; do
|
||||||
|
ls=`ls -ld "$PRG"`
|
||||||
|
link=`expr "$ls" : '.*-> \(.*\)$'`
|
||||||
|
if expr "$link" : '/.*' > /dev/null; then
|
||||||
|
PRG="$link"
|
||||||
|
else
|
||||||
|
PRG=`dirname "$PRG"`"/$link"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
SAVED="`pwd`"
|
||||||
|
cd "`dirname \"$PRG\"`/" >/dev/null
|
||||||
|
APP_HOME="`pwd -P`"
|
||||||
|
cd "$SAVED" >/dev/null
|
||||||
|
|
||||||
|
APP_NAME="Gradle"
|
||||||
|
APP_BASE_NAME=`basename "$0"`
|
||||||
|
|
||||||
|
# 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"'
|
||||||
|
|
||||||
|
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||||
|
MAX_FD="maximum"
|
||||||
|
|
||||||
|
warn () {
|
||||||
|
echo "$*"
|
||||||
|
}
|
||||||
|
|
||||||
|
die () {
|
||||||
|
echo
|
||||||
|
echo "$*"
|
||||||
|
echo
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# 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=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
||||||
|
|
||||||
|
|
||||||
|
# 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"
|
||||||
|
which java >/dev/null 2>&1 || 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
|
||||||
|
|
||||||
|
# Increase the maximum file descriptors if we can.
|
||||||
|
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
|
||||||
|
MAX_FD_LIMIT=`ulimit -H -n`
|
||||||
|
if [ $? -eq 0 ] ; then
|
||||||
|
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
|
||||||
|
MAX_FD="$MAX_FD_LIMIT"
|
||||||
|
fi
|
||||||
|
ulimit -n $MAX_FD
|
||||||
|
if [ $? -ne 0 ] ; then
|
||||||
|
warn "Could not set maximum file descriptor limit: $MAX_FD"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# For Darwin, add options to specify how the application appears in the dock
|
||||||
|
if $darwin; then
|
||||||
|
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
|
||||||
|
fi
|
||||||
|
|
||||||
|
# For Cygwin or MSYS, switch paths to Windows format before running java
|
||||||
|
if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
|
||||||
|
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
|
||||||
|
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
|
||||||
|
|
||||||
|
JAVACMD=`cygpath --unix "$JAVACMD"`
|
||||||
|
|
||||||
|
# We build the pattern for arguments to be converted via cygpath
|
||||||
|
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
|
||||||
|
SEP=""
|
||||||
|
for dir in $ROOTDIRSRAW ; do
|
||||||
|
ROOTDIRS="$ROOTDIRS$SEP$dir"
|
||||||
|
SEP="|"
|
||||||
|
done
|
||||||
|
OURCYGPATTERN="(^($ROOTDIRS))"
|
||||||
|
# Add a user-defined pattern to the cygpath arguments
|
||||||
|
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
|
||||||
|
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
|
||||||
|
fi
|
||||||
|
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
||||||
|
i=0
|
||||||
|
for arg in "$@" ; do
|
||||||
|
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
|
||||||
|
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
|
||||||
|
|
||||||
|
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
|
||||||
|
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
|
||||||
|
else
|
||||||
|
eval `echo args$i`="\"$arg\""
|
||||||
|
fi
|
||||||
|
i=`expr $i + 1`
|
||||||
|
done
|
||||||
|
case $i in
|
||||||
|
0) set -- ;;
|
||||||
|
1) set -- "$args0" ;;
|
||||||
|
2) set -- "$args0" "$args1" ;;
|
||||||
|
3) set -- "$args0" "$args1" "$args2" ;;
|
||||||
|
4) set -- "$args0" "$args1" "$args2" "$args3" ;;
|
||||||
|
5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
|
||||||
|
6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
|
||||||
|
7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
|
||||||
|
8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
|
||||||
|
9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
|
||||||
|
esac
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Escape application args
|
||||||
|
save () {
|
||||||
|
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
|
||||||
|
echo " "
|
||||||
|
}
|
||||||
|
APP_ARGS=`save "$@"`
|
||||||
|
|
||||||
|
# Collect all arguments for the java command, following the shell quoting and substitution rules
|
||||||
|
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
|
||||||
|
|
||||||
|
exec "$JAVACMD" "$@"
|
|
@ -0,0 +1,89 @@
|
||||||
|
@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
|
||||||
|
|
||||||
|
@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=.
|
||||||
|
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%" == "0" goto execute
|
||||||
|
|
||||||
|
echo.
|
||||||
|
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||||
|
echo.
|
||||||
|
echo Please set the JAVA_HOME variable in your environment to match the
|
||||||
|
echo location of your Java installation.
|
||||||
|
|
||||||
|
goto fail
|
||||||
|
|
||||||
|
:findJavaFromJavaHome
|
||||||
|
set JAVA_HOME=%JAVA_HOME:"=%
|
||||||
|
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||||
|
|
||||||
|
if exist "%JAVA_EXE%" goto execute
|
||||||
|
|
||||||
|
echo.
|
||||||
|
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
||||||
|
echo.
|
||||||
|
echo Please set the JAVA_HOME variable in your environment to match the
|
||||||
|
echo location of your Java installation.
|
||||||
|
|
||||||
|
goto fail
|
||||||
|
|
||||||
|
:execute
|
||||||
|
@rem Setup the command line
|
||||||
|
|
||||||
|
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
||||||
|
|
||||||
|
|
||||||
|
@rem Execute Gradle
|
||||||
|
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
|
||||||
|
|
||||||
|
:end
|
||||||
|
@rem End local scope for the variables with windows NT shell
|
||||||
|
if "%ERRORLEVEL%"=="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!
|
||||||
|
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
|
||||||
|
exit /b 1
|
||||||
|
|
||||||
|
:mainEnd
|
||||||
|
if "%OS%"=="Windows_NT" endlocal
|
||||||
|
|
||||||
|
:omega
|
|
@ -0,0 +1,9 @@
|
||||||
|
pluginManagement {
|
||||||
|
repositories {
|
||||||
|
gradlePluginPortal()
|
||||||
|
maven("https://papermc.io/repo/repository/maven-public/")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
rootProject.name = "DLib"
|
||||||
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
package nl.kallestruik.dlib
|
||||||
|
|
||||||
|
import org.bukkit.plugin.java.JavaPlugin
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class is only here to make sure that paper loads the plugin.
|
||||||
|
*/
|
||||||
|
class DLib: JavaPlugin()
|
|
@ -0,0 +1,45 @@
|
||||||
|
package nl.kallestruik.dlib
|
||||||
|
|
||||||
|
import org.bukkit.util.Vector
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
class DUtil(
|
||||||
|
private val random: Random
|
||||||
|
) {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a random vector with integer offsets within the min and max.
|
||||||
|
* @param min The minimum offset.
|
||||||
|
* @param max The maximum offset.
|
||||||
|
* @param height Whether the height should also have a random offset.
|
||||||
|
* @return A [Vector] containing the offsets.
|
||||||
|
*/
|
||||||
|
fun getRandomLocationOffset(min: Int, max: Int, height: Boolean): Vector {
|
||||||
|
// Create the vectors with all positive numbers.
|
||||||
|
val vec = if (height) {
|
||||||
|
Vector(
|
||||||
|
min + random.nextInt(max - min),
|
||||||
|
min + random.nextInt(max - min),
|
||||||
|
min + random.nextInt(max - min)
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
Vector(
|
||||||
|
min + random.nextInt(max - min),
|
||||||
|
0,
|
||||||
|
min + random.nextInt(max - min)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Randomly make some components negative instead of positive.
|
||||||
|
vec.multiply(
|
||||||
|
Vector(
|
||||||
|
if (random.nextBoolean()) -1 else 1,
|
||||||
|
if (random.nextBoolean()) -1 else 1,
|
||||||
|
if (random.nextBoolean()) -1 else 1
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
// Return the completed vector.
|
||||||
|
return vec
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,26 @@
|
||||||
|
package nl.kallestruik.dlib
|
||||||
|
|
||||||
|
class MathHelper {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clamp an integer between a min and a max value.
|
||||||
|
* @param value The value to clamp.
|
||||||
|
* @param min The minimal value.
|
||||||
|
* @param max The maximum value.
|
||||||
|
*/
|
||||||
|
fun clamp(value: Int, min: Int, max: Int): Int {
|
||||||
|
return if (value < min) min else value.coerceAtMost(max)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Force a value to between 0 15 (inclusive) wrapping around to 15 at negative values.
|
||||||
|
* @param value The value.
|
||||||
|
* @return A positive int between 0 and 15 (inclusive).
|
||||||
|
*/
|
||||||
|
fun chunkAbs(value: Int): Int {
|
||||||
|
return if (value >= 0)
|
||||||
|
value % 16
|
||||||
|
else
|
||||||
|
16 + (value % 16)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,37 @@
|
||||||
|
package nl.kallestruik.dlib
|
||||||
|
|
||||||
|
import java.lang.reflect.Field
|
||||||
|
|
||||||
|
class ReflectionUtil {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a {@link Field} from the class provided.
|
||||||
|
* @param clazz The class to get the field from.
|
||||||
|
* @param fieldName The name of the field to get.
|
||||||
|
*/
|
||||||
|
@Throws(NoSuchFieldException::class)
|
||||||
|
fun getField(clazz: Class<*>, fieldName: String): Field {
|
||||||
|
return try {
|
||||||
|
clazz.getDeclaredField(fieldName)
|
||||||
|
} catch (e: NoSuchFieldException) {
|
||||||
|
val superClass = clazz.superclass
|
||||||
|
superClass?.let { getField(it, fieldName) } ?: throw e
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the value from a field on an object.
|
||||||
|
* @param instance The instance of the object to take the value from.
|
||||||
|
* @param fieldName The name of the field to take the value from.
|
||||||
|
*/
|
||||||
|
fun getValueFromField(instance: Any, fieldName: String): Any? {
|
||||||
|
try {
|
||||||
|
val field = getField(instance.javaClass, fieldName)
|
||||||
|
field.isAccessible = true //required if field is not normally accessible
|
||||||
|
return field.get(instance)
|
||||||
|
} catch (e: NoSuchFieldException) {
|
||||||
|
e.printStackTrace()
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,223 @@
|
||||||
|
package nl.kallestruik.dlib.config
|
||||||
|
|
||||||
|
import kotlinx.serialization.descriptors.*
|
||||||
|
import kotlinx.serialization.json.JsonPrimitive
|
||||||
|
import nl.kallestruik.dlib.config.annotations.*
|
||||||
|
import nl.kallestruik.dlib.config.data.ConfigSchema
|
||||||
|
import nl.kallestruik.dlib.config.data.ConfigValueConstraints
|
||||||
|
import nl.kallestruik.dlib.config.data.ConfigValueType
|
||||||
|
import nl.kallestruik.dlib.exceptions.ConfigSchemaGenerationException
|
||||||
|
import kotlin.math.min
|
||||||
|
import kotlin.math.max
|
||||||
|
|
||||||
|
fun SerialDescriptor.toConfigSchema(annotations: List<Annotation> = listOf()): ConfigSchema {
|
||||||
|
val allAnnotations = annotations + this.annotations
|
||||||
|
return when (this.kind) {
|
||||||
|
SerialKind.ENUM -> createEnumConfigSchema(this, allAnnotations)
|
||||||
|
PrimitiveKind.BOOLEAN -> createBooleanConfigSchema(allAnnotations)
|
||||||
|
PrimitiveKind.BYTE -> createIntegerConfigSchema(allAnnotations, Byte.MAX_VALUE.toLong(), Byte.MIN_VALUE.toLong())
|
||||||
|
PrimitiveKind.CHAR -> createStringConfigSchema(allAnnotations, maxLength=1)
|
||||||
|
PrimitiveKind.SHORT -> createIntegerConfigSchema(allAnnotations, Short.MAX_VALUE.toLong(), Short.MIN_VALUE.toLong())
|
||||||
|
PrimitiveKind.INT -> createIntegerConfigSchema(allAnnotations, Int.MAX_VALUE.toLong(), Int.MIN_VALUE.toLong())
|
||||||
|
PrimitiveKind.LONG -> createIntegerConfigSchema(allAnnotations, Long.MAX_VALUE, Long.MIN_VALUE)
|
||||||
|
PrimitiveKind.FLOAT -> createFloatConfigSchema(allAnnotations, Float.MAX_VALUE.toDouble(), -Float.MAX_VALUE.toDouble())
|
||||||
|
PrimitiveKind.DOUBLE -> createFloatConfigSchema(allAnnotations, Double.MAX_VALUE, -Double.MAX_VALUE)
|
||||||
|
PrimitiveKind.STRING -> createStringConfigSchema(allAnnotations)
|
||||||
|
StructureKind.LIST -> createListConfigSchema(this, allAnnotations)
|
||||||
|
StructureKind.MAP -> createMapConfigSchema(this, allAnnotations)
|
||||||
|
StructureKind.OBJECT, StructureKind.CLASS -> createObjectConfigSchema(this, allAnnotations)
|
||||||
|
else -> throw ConfigSchemaGenerationException("Creating a schema for type ${this.kind} is not currently supported.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun createObjectConfigSchema(descriptor: SerialDescriptor, annotations: List<Annotation>): ConfigSchema {
|
||||||
|
val description = annotations
|
||||||
|
.filterIsInstance<Description>()
|
||||||
|
.flatMap { it.lines.toList() }
|
||||||
|
|
||||||
|
val children = descriptor.elementDescriptors
|
||||||
|
.mapIndexed { i, child ->
|
||||||
|
descriptor.getElementName(i) to child.toConfigSchema(descriptor.getElementAnnotations(i))
|
||||||
|
}.toMap()
|
||||||
|
|
||||||
|
return ConfigSchema(
|
||||||
|
type = ConfigValueType.OBJECT,
|
||||||
|
children = children,
|
||||||
|
description = description,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun createListConfigSchema(descriptor: SerialDescriptor, annotations: List<Annotation>): ConfigSchema {
|
||||||
|
val description = annotations
|
||||||
|
.filterIsInstance<Description>()
|
||||||
|
.flatMap { it.lines.toList() }
|
||||||
|
|
||||||
|
return ConfigSchema(
|
||||||
|
type = ConfigValueType.LIST,
|
||||||
|
description = description,
|
||||||
|
items = descriptor.elementDescriptors.first().toConfigSchema(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun createMapConfigSchema(descriptor: SerialDescriptor, annotations: List<Annotation>): ConfigSchema {
|
||||||
|
val description = annotations
|
||||||
|
.filterIsInstance<Description>()
|
||||||
|
.flatMap { it.lines.toList() }
|
||||||
|
|
||||||
|
val allowedKeys = annotations
|
||||||
|
.filterIsInstance<AllowedKeys>()
|
||||||
|
.flatMap { annotation -> annotation.keys
|
||||||
|
.map { listOf(it) }
|
||||||
|
}
|
||||||
|
|
||||||
|
val allowedKeysExclusive = annotations
|
||||||
|
.filterIsInstance<AllowedKeysExclusive>()
|
||||||
|
.map { it.exclusiveKeys.toList() }
|
||||||
|
|
||||||
|
if (descriptor.elementDescriptors.first().kind != PrimitiveKind.STRING) {
|
||||||
|
throw ConfigSchemaGenerationException("Map keys have to be strings.")
|
||||||
|
}
|
||||||
|
|
||||||
|
return ConfigSchema(
|
||||||
|
type = ConfigValueType.MAP,
|
||||||
|
description = description,
|
||||||
|
items = descriptor.elementDescriptors.last().toConfigSchema(),
|
||||||
|
constraints = ConfigValueConstraints(
|
||||||
|
allowedKeys = allowedKeys + allowedKeysExclusive,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun createStringConfigSchema(annotations: List<Annotation>, maxLength: Int? = null): ConfigSchema {
|
||||||
|
val description = annotations
|
||||||
|
.filterIsInstance<Description>()
|
||||||
|
.flatMap { it.lines.toList() }
|
||||||
|
|
||||||
|
val allowedValues = annotations
|
||||||
|
.filterIsInstance<StringEnum>()
|
||||||
|
.flatMap { it.values.toList() }
|
||||||
|
|
||||||
|
val default = annotations
|
||||||
|
.filterIsInstance<DefaultString>()
|
||||||
|
.firstOrNull()
|
||||||
|
?.let { JsonPrimitive(it.default) }
|
||||||
|
|
||||||
|
return ConfigSchema(
|
||||||
|
type = ConfigValueType.STRING,
|
||||||
|
description = description,
|
||||||
|
default = default,
|
||||||
|
constraints = ConfigValueConstraints(
|
||||||
|
enum = allowedValues.ifEmpty { null },
|
||||||
|
maxLength = maxLength,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun createEnumConfigSchema(descriptor: SerialDescriptor, annotations: List<Annotation>): ConfigSchema {
|
||||||
|
val description = annotations
|
||||||
|
.filterIsInstance<Description>()
|
||||||
|
.flatMap { it.lines.toList() }
|
||||||
|
|
||||||
|
val allowedValues = descriptor.elementNames.toList()
|
||||||
|
|
||||||
|
val default = annotations
|
||||||
|
.filterIsInstance<DefaultString>()
|
||||||
|
.firstOrNull()
|
||||||
|
?.let { JsonPrimitive(it.default) }
|
||||||
|
|
||||||
|
return ConfigSchema(
|
||||||
|
type = ConfigValueType.STRING,
|
||||||
|
description = description,
|
||||||
|
default = default,
|
||||||
|
constraints = ConfigValueConstraints(
|
||||||
|
enum = allowedValues.ifEmpty { null },
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun createBooleanConfigSchema(annotations: List<Annotation>): ConfigSchema {
|
||||||
|
val description = annotations
|
||||||
|
.filterIsInstance<Description>()
|
||||||
|
.flatMap { it.lines.toList() }
|
||||||
|
|
||||||
|
val default = annotations
|
||||||
|
.filterIsInstance<DefaultBoolean>()
|
||||||
|
.firstOrNull()
|
||||||
|
?.let { JsonPrimitive(it.default) }
|
||||||
|
|
||||||
|
return ConfigSchema(
|
||||||
|
type = ConfigValueType.BOOLEAN,
|
||||||
|
description = description,
|
||||||
|
default = default,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun createIntegerConfigSchema(annotations: List<Annotation>, maxValue: Long, minValue: Long): ConfigSchema {
|
||||||
|
val description = annotations
|
||||||
|
.filterIsInstance<Description>()
|
||||||
|
.flatMap { it.lines.toList() }
|
||||||
|
|
||||||
|
val max = annotations
|
||||||
|
.filterIsInstance<MaxInt>()
|
||||||
|
.map { it.max }
|
||||||
|
.reduceOrNull { acc, next ->
|
||||||
|
return@reduceOrNull max(acc, next)
|
||||||
|
} ?: maxValue
|
||||||
|
|
||||||
|
val min = annotations
|
||||||
|
.filterIsInstance<MinInt>()
|
||||||
|
.map { it.min }
|
||||||
|
.reduceOrNull { acc, next ->
|
||||||
|
min(acc, next)
|
||||||
|
} ?: minValue
|
||||||
|
|
||||||
|
val default = annotations
|
||||||
|
.filterIsInstance<DefaultInt>()
|
||||||
|
.firstOrNull()
|
||||||
|
?.let { JsonPrimitive(it.default) }
|
||||||
|
|
||||||
|
return ConfigSchema(
|
||||||
|
type = ConfigValueType.INTEGER,
|
||||||
|
description = description,
|
||||||
|
default = default,
|
||||||
|
constraints = ConfigValueConstraints(
|
||||||
|
maxValueInt = max,
|
||||||
|
minValueInt = min,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun createFloatConfigSchema(annotations: List<Annotation>, maxValue: Double, minValue: Double): ConfigSchema {
|
||||||
|
val description = annotations
|
||||||
|
.filterIsInstance<Description>()
|
||||||
|
.flatMap { it.lines.toList() }
|
||||||
|
|
||||||
|
val max = annotations
|
||||||
|
.filterIsInstance<MaxFloat>()
|
||||||
|
.map { it.max }
|
||||||
|
.reduceOrNull { acc, next ->
|
||||||
|
return@reduceOrNull max(acc, next)
|
||||||
|
} ?: maxValue
|
||||||
|
|
||||||
|
val min = annotations
|
||||||
|
.filterIsInstance<MinFloat>()
|
||||||
|
.map { it.min }
|
||||||
|
.reduceOrNull { acc, next ->
|
||||||
|
min(acc, next)
|
||||||
|
} ?: minValue
|
||||||
|
|
||||||
|
val default = annotations
|
||||||
|
.filterIsInstance<DefaultFloat>()
|
||||||
|
.firstOrNull()
|
||||||
|
?.let { JsonPrimitive(it.default) }
|
||||||
|
|
||||||
|
return ConfigSchema(
|
||||||
|
type = ConfigValueType.FLOAT,
|
||||||
|
description = description,
|
||||||
|
default = default,
|
||||||
|
constraints = ConfigValueConstraints(
|
||||||
|
maxValueFloat = max,
|
||||||
|
minValueFloat = min,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
package nl.kallestruik.dlib.config.annotations
|
||||||
|
|
||||||
|
import kotlinx.serialization.SerialInfo
|
||||||
|
|
||||||
|
@SerialInfo
|
||||||
|
@Repeatable
|
||||||
|
@Target(AnnotationTarget.FIELD, AnnotationTarget.PROPERTY)
|
||||||
|
@Retention(AnnotationRetention.BINARY)
|
||||||
|
annotation class AllowedKeys(
|
||||||
|
vararg val keys: String,
|
||||||
|
)
|
|
@ -0,0 +1,11 @@
|
||||||
|
package nl.kallestruik.dlib.config.annotations
|
||||||
|
|
||||||
|
import kotlinx.serialization.SerialInfo
|
||||||
|
|
||||||
|
@SerialInfo
|
||||||
|
@Repeatable
|
||||||
|
@Target(AnnotationTarget.FIELD, AnnotationTarget.PROPERTY)
|
||||||
|
@Retention(AnnotationRetention.BINARY)
|
||||||
|
annotation class AllowedKeysExclusive(
|
||||||
|
vararg val exclusiveKeys: String,
|
||||||
|
)
|
|
@ -0,0 +1,10 @@
|
||||||
|
package nl.kallestruik.dlib.config.annotations
|
||||||
|
|
||||||
|
import kotlinx.serialization.SerialInfo
|
||||||
|
|
||||||
|
@SerialInfo
|
||||||
|
@Target(AnnotationTarget.FIELD, AnnotationTarget.PROPERTY)
|
||||||
|
@Retention(AnnotationRetention.BINARY)
|
||||||
|
annotation class DefaultBoolean(
|
||||||
|
val default: Boolean,
|
||||||
|
)
|
|
@ -0,0 +1,10 @@
|
||||||
|
package nl.kallestruik.dlib.config.annotations
|
||||||
|
|
||||||
|
import kotlinx.serialization.SerialInfo
|
||||||
|
|
||||||
|
@SerialInfo
|
||||||
|
@Target(AnnotationTarget.FIELD, AnnotationTarget.PROPERTY)
|
||||||
|
@Retention(AnnotationRetention.BINARY)
|
||||||
|
annotation class DefaultFloat(
|
||||||
|
val default: Double,
|
||||||
|
)
|
|
@ -0,0 +1,10 @@
|
||||||
|
package nl.kallestruik.dlib.config.annotations
|
||||||
|
|
||||||
|
import kotlinx.serialization.SerialInfo
|
||||||
|
|
||||||
|
@SerialInfo
|
||||||
|
@Target(AnnotationTarget.FIELD, AnnotationTarget.PROPERTY)
|
||||||
|
@Retention(AnnotationRetention.BINARY)
|
||||||
|
annotation class DefaultInt(
|
||||||
|
val default: Long,
|
||||||
|
)
|
|
@ -0,0 +1,10 @@
|
||||||
|
package nl.kallestruik.dlib.config.annotations
|
||||||
|
|
||||||
|
import kotlinx.serialization.SerialInfo
|
||||||
|
|
||||||
|
@SerialInfo
|
||||||
|
@Target(AnnotationTarget.FIELD, AnnotationTarget.PROPERTY)
|
||||||
|
@Retention(AnnotationRetention.BINARY)
|
||||||
|
annotation class DefaultString(
|
||||||
|
val default: String,
|
||||||
|
)
|
|
@ -0,0 +1,11 @@
|
||||||
|
package nl.kallestruik.dlib.config.annotations
|
||||||
|
|
||||||
|
import kotlinx.serialization.SerialInfo
|
||||||
|
|
||||||
|
@SerialInfo
|
||||||
|
@Repeatable
|
||||||
|
@Target(AnnotationTarget.FIELD, AnnotationTarget.PROPERTY)
|
||||||
|
@Retention(AnnotationRetention.BINARY)
|
||||||
|
annotation class Description(
|
||||||
|
vararg val lines: String,
|
||||||
|
)
|
|
@ -0,0 +1,11 @@
|
||||||
|
package nl.kallestruik.dlib.config.annotations
|
||||||
|
|
||||||
|
import kotlinx.serialization.SerialInfo
|
||||||
|
import nl.kallestruik.dlib.config.data.ConfigDisplayType
|
||||||
|
|
||||||
|
@SerialInfo
|
||||||
|
@Target(AnnotationTarget.FIELD, AnnotationTarget.PROPERTY, AnnotationTarget.CLASS)
|
||||||
|
@Retention(AnnotationRetention.BINARY)
|
||||||
|
annotation class DisplayType(
|
||||||
|
val type: ConfigDisplayType
|
||||||
|
)
|
|
@ -0,0 +1,10 @@
|
||||||
|
package nl.kallestruik.dlib.config.annotations
|
||||||
|
|
||||||
|
import kotlinx.serialization.SerialInfo
|
||||||
|
|
||||||
|
@SerialInfo
|
||||||
|
@Target(AnnotationTarget.FIELD, AnnotationTarget.PROPERTY, AnnotationTarget.CLASS)
|
||||||
|
@Retention(AnnotationRetention.BINARY)
|
||||||
|
annotation class MaterialField(
|
||||||
|
val field: String,
|
||||||
|
)
|
|
@ -0,0 +1,10 @@
|
||||||
|
package nl.kallestruik.dlib.config.annotations
|
||||||
|
|
||||||
|
import kotlinx.serialization.SerialInfo
|
||||||
|
|
||||||
|
@SerialInfo
|
||||||
|
@Target(AnnotationTarget.FIELD, AnnotationTarget.PROPERTY)
|
||||||
|
@Retention(AnnotationRetention.BINARY)
|
||||||
|
annotation class MaxFloat(
|
||||||
|
val max: Double,
|
||||||
|
)
|
|
@ -0,0 +1,10 @@
|
||||||
|
package nl.kallestruik.dlib.config.annotations
|
||||||
|
|
||||||
|
import kotlinx.serialization.SerialInfo
|
||||||
|
|
||||||
|
@SerialInfo
|
||||||
|
@Target(AnnotationTarget.FIELD, AnnotationTarget.PROPERTY)
|
||||||
|
@Retention(AnnotationRetention.BINARY)
|
||||||
|
annotation class MaxInt(
|
||||||
|
val max: Long,
|
||||||
|
)
|
|
@ -0,0 +1,10 @@
|
||||||
|
package nl.kallestruik.dlib.config.annotations
|
||||||
|
|
||||||
|
import kotlinx.serialization.SerialInfo
|
||||||
|
|
||||||
|
@SerialInfo
|
||||||
|
@Target(AnnotationTarget.FIELD, AnnotationTarget.PROPERTY)
|
||||||
|
@Retention(AnnotationRetention.BINARY)
|
||||||
|
annotation class MinFloat(
|
||||||
|
val min: Double,
|
||||||
|
)
|
|
@ -0,0 +1,10 @@
|
||||||
|
package nl.kallestruik.dlib.config.annotations
|
||||||
|
|
||||||
|
import kotlinx.serialization.SerialInfo
|
||||||
|
|
||||||
|
@SerialInfo
|
||||||
|
@Target(AnnotationTarget.FIELD, AnnotationTarget.PROPERTY)
|
||||||
|
@Retention(AnnotationRetention.BINARY)
|
||||||
|
annotation class MinInt(
|
||||||
|
val min: Long,
|
||||||
|
)
|
|
@ -0,0 +1,11 @@
|
||||||
|
package nl.kallestruik.dlib.config.annotations
|
||||||
|
|
||||||
|
import kotlinx.serialization.SerialInfo
|
||||||
|
import org.bukkit.Material
|
||||||
|
|
||||||
|
@SerialInfo
|
||||||
|
@Target(AnnotationTarget.FIELD, AnnotationTarget.PROPERTY)
|
||||||
|
@Retention(AnnotationRetention.BINARY)
|
||||||
|
annotation class StaticMaterial(
|
||||||
|
val material: Material,
|
||||||
|
)
|
|
@ -0,0 +1,11 @@
|
||||||
|
package nl.kallestruik.dlib.config.annotations
|
||||||
|
|
||||||
|
import kotlinx.serialization.SerialInfo
|
||||||
|
|
||||||
|
@SerialInfo
|
||||||
|
@Repeatable
|
||||||
|
@Target(AnnotationTarget.FIELD, AnnotationTarget.PROPERTY)
|
||||||
|
@Retention(AnnotationRetention.BINARY)
|
||||||
|
annotation class StringEnum(
|
||||||
|
vararg val values: String,
|
||||||
|
)
|
|
@ -0,0 +1,20 @@
|
||||||
|
package nl.kallestruik.dlib.config.data
|
||||||
|
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class ConfigDisplaySettings(
|
||||||
|
val type: ConfigDisplayType = ConfigDisplayType.DEFAULT,
|
||||||
|
val materialField: String? = null,
|
||||||
|
val staticMaterial: String? = null,
|
||||||
|
val amountField: String? = null,
|
||||||
|
val staticAmount: Int? = null,
|
||||||
|
val inventorySize: Int? = null,
|
||||||
|
)
|
||||||
|
|
||||||
|
enum class ConfigDisplayType {
|
||||||
|
DEFAULT,
|
||||||
|
CHECKLIST,
|
||||||
|
ITEM,
|
||||||
|
INVENTORY,
|
||||||
|
}
|
|
@ -0,0 +1,15 @@
|
||||||
|
package nl.kallestruik.dlib.config.data
|
||||||
|
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
import kotlinx.serialization.json.JsonPrimitive
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class ConfigSchema(
|
||||||
|
val type: ConfigValueType,
|
||||||
|
val description: List<String>? = null,
|
||||||
|
val children: Map<String, ConfigSchema>? = null,
|
||||||
|
val items: ConfigSchema? = null,
|
||||||
|
val default: JsonPrimitive? = null,
|
||||||
|
val constraints: ConfigValueConstraints = ConfigValueConstraints(),
|
||||||
|
val display: ConfigDisplaySettings? = null,
|
||||||
|
)
|
|
@ -0,0 +1,14 @@
|
||||||
|
package nl.kallestruik.dlib.config.data
|
||||||
|
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class ConfigValueConstraints(
|
||||||
|
val enum: List<String>? = null,
|
||||||
|
val allowedKeys: List<List<String>>? = null,
|
||||||
|
val maxLength: Int? = null,
|
||||||
|
val maxValueInt: Long? = null,
|
||||||
|
val minValueInt: Long? = null,
|
||||||
|
val maxValueFloat: Double? = null,
|
||||||
|
val minValueFloat: Double? = null,
|
||||||
|
)
|
|
@ -0,0 +1,11 @@
|
||||||
|
package nl.kallestruik.dlib.config.data
|
||||||
|
|
||||||
|
enum class ConfigValueType {
|
||||||
|
STRING,
|
||||||
|
MAP,
|
||||||
|
LIST,
|
||||||
|
OBJECT,
|
||||||
|
BOOLEAN,
|
||||||
|
INTEGER,
|
||||||
|
FLOAT
|
||||||
|
}
|
|
@ -0,0 +1,3 @@
|
||||||
|
package nl.kallestruik.dlib.exceptions
|
||||||
|
|
||||||
|
class ConfigSchemaGenerationException(message: String = ""): RuntimeException(message)
|
|
@ -0,0 +1,222 @@
|
||||||
|
package nl.kallestruik.dlib.gui
|
||||||
|
|
||||||
|
import kotlinx.coroutines.runBlocking
|
||||||
|
import net.kyori.adventure.text.Component
|
||||||
|
import org.bukkit.Bukkit
|
||||||
|
import org.bukkit.Material
|
||||||
|
import org.bukkit.entity.Player
|
||||||
|
import org.bukkit.event.EventHandler
|
||||||
|
import org.bukkit.event.HandlerList
|
||||||
|
import org.bukkit.event.Listener
|
||||||
|
import org.bukkit.event.inventory.ClickType
|
||||||
|
import org.bukkit.event.inventory.InventoryAction
|
||||||
|
import org.bukkit.event.inventory.InventoryClickEvent
|
||||||
|
import org.bukkit.event.inventory.InventoryCloseEvent
|
||||||
|
import org.bukkit.inventory.InventoryView
|
||||||
|
import org.bukkit.inventory.ItemStack
|
||||||
|
import org.bukkit.plugin.Plugin
|
||||||
|
|
||||||
|
fun chestGUI(player: Player, plugin: Plugin, configure: suspend ChestGUI.() -> Unit) {
|
||||||
|
val chestGUI = ChestGUI()
|
||||||
|
runBlocking {
|
||||||
|
chestGUI.configure()
|
||||||
|
}
|
||||||
|
|
||||||
|
chestGUI.display(player, plugin)
|
||||||
|
}
|
||||||
|
|
||||||
|
class ChestGUI: Listener {
|
||||||
|
private data class Configuration(
|
||||||
|
var border: (suspend ChestGUIEntry.() -> Unit)? = null,
|
||||||
|
var content: MutableList<suspend ChestGUIEntry.() -> Unit> = mutableListOf(),
|
||||||
|
var itemStacks: MutableMap<Int, ItemStack> = mutableMapOf(),
|
||||||
|
var onClose: suspend (gui: ChestGUI, player: Player, event: InventoryCloseEvent) -> Unit = { _, _, _ ->}
|
||||||
|
)
|
||||||
|
|
||||||
|
private val configuration = Configuration()
|
||||||
|
var title: Component = Component.empty()
|
||||||
|
var rows: Int = 0
|
||||||
|
var cancelInteraction: Boolean = true
|
||||||
|
|
||||||
|
private lateinit var view: InventoryView
|
||||||
|
private lateinit var entries: Array<ChestGUIEntry?>
|
||||||
|
|
||||||
|
|
||||||
|
fun onClose(onClose: suspend (gui: ChestGUI, player: Player, event: InventoryCloseEvent) -> Unit) {
|
||||||
|
configuration.onClose = onClose
|
||||||
|
}
|
||||||
|
|
||||||
|
fun entry(entry: suspend ChestGUIEntry.() -> Unit) {
|
||||||
|
configuration.content.add(entry)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun border(border: suspend ChestGUIEntry.() -> Unit) {
|
||||||
|
configuration.border = border
|
||||||
|
}
|
||||||
|
|
||||||
|
fun itemStack(slot: Int, itemStack: ItemStack) {
|
||||||
|
configuration.itemStacks[slot] = itemStack
|
||||||
|
}
|
||||||
|
|
||||||
|
fun display(player: Player, plugin: Plugin) {
|
||||||
|
val inventory = Bukkit.createInventory(null, rows * 9, title)
|
||||||
|
view = player.openInventory(inventory) ?: return
|
||||||
|
|
||||||
|
entries = arrayOfNulls(rows * 9)
|
||||||
|
|
||||||
|
if (configuration.border != null) {
|
||||||
|
val border = ChestGUIEntry()
|
||||||
|
runBlocking {
|
||||||
|
configuration.border!!.invoke(border)
|
||||||
|
}
|
||||||
|
|
||||||
|
val borderStack = border.toItemStack()
|
||||||
|
|
||||||
|
// Top/Bottom border
|
||||||
|
for (x in 0..8) {
|
||||||
|
view.setItem(x, borderStack)
|
||||||
|
view.setItem((rows - 1) * 9 + x, borderStack)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Middle side border
|
||||||
|
for (y in 1..rows - 2) {
|
||||||
|
view.setItem(9 * y, borderStack)
|
||||||
|
view.setItem(9 * y + 8, borderStack)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
configuration.content.forEach {
|
||||||
|
val entry = ChestGUIEntry()
|
||||||
|
runBlocking {
|
||||||
|
it.invoke(entry)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (entry.getSlot() < 0)
|
||||||
|
return@forEach
|
||||||
|
|
||||||
|
entries[entry.getSlot()] = entry
|
||||||
|
}
|
||||||
|
|
||||||
|
entries.forEach { entry ->
|
||||||
|
if (entry == null)
|
||||||
|
return@forEach
|
||||||
|
|
||||||
|
if (entry.x != -1 || entry.y != -1)
|
||||||
|
return@forEach
|
||||||
|
|
||||||
|
inventory.setItem(entry.getSlot(), entry.toItemStack())
|
||||||
|
}
|
||||||
|
|
||||||
|
configuration.itemStacks.forEach { (slot, itemStack) ->
|
||||||
|
inventory.setItem(slot, itemStack)
|
||||||
|
}
|
||||||
|
|
||||||
|
plugin.server.pluginManager.registerEvents(this, plugin)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun cleanup() {
|
||||||
|
HandlerList.unregisterAll(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Bukkit event handlers
|
||||||
|
*/
|
||||||
|
@EventHandler
|
||||||
|
fun onInventoryClose(event: InventoryCloseEvent) {
|
||||||
|
if (event.view != this.view)
|
||||||
|
return
|
||||||
|
|
||||||
|
cleanup()
|
||||||
|
|
||||||
|
if (event.reason == InventoryCloseEvent.Reason.PLUGIN) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (event.player !is Player)
|
||||||
|
return
|
||||||
|
|
||||||
|
runBlocking {
|
||||||
|
configuration.onClose(this@ChestGUI, event.player as Player, event)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@EventHandler
|
||||||
|
fun onClick(event: InventoryClickEvent) {
|
||||||
|
if (event.view != view) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (event.action != InventoryAction.PICKUP_ALL) {
|
||||||
|
if (cancelInteraction)
|
||||||
|
event.isCancelled = true
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
val slot = view.convertSlot(event.rawSlot)
|
||||||
|
|
||||||
|
if (cancelInteraction)
|
||||||
|
event.isCancelled = true
|
||||||
|
|
||||||
|
val entry = entries[slot] ?: return
|
||||||
|
|
||||||
|
if (event.whoClicked !is Player)
|
||||||
|
return
|
||||||
|
|
||||||
|
runBlocking {
|
||||||
|
when (event.click) {
|
||||||
|
ClickType.LEFT -> entry.onLeftClick(event)
|
||||||
|
ClickType.MIDDLE -> entry.onMiddleClick(event)
|
||||||
|
ClickType.RIGHT -> entry.onRightClick(event)
|
||||||
|
else -> entry.onOtherClick(event)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
data class ChestGUIEntry(
|
||||||
|
var name: Component = Component.empty(),
|
||||||
|
var material: Material = Material.AIR,
|
||||||
|
var amount: Int = 1,
|
||||||
|
var lore: MutableList<Component> = mutableListOf(),
|
||||||
|
var x: Int = -1,
|
||||||
|
var y: Int = -1,
|
||||||
|
var onLeftClick: suspend (event: InventoryClickEvent) -> Unit = {},
|
||||||
|
var onMiddleClick: suspend (event: InventoryClickEvent) -> Unit = {},
|
||||||
|
var onRightClick: suspend (event: InventoryClickEvent) -> Unit = {},
|
||||||
|
var onOtherClick: suspend (event: InventoryClickEvent) -> Unit = {},
|
||||||
|
) {
|
||||||
|
|
||||||
|
fun onLeftClick(onLeftClick: suspend (event: InventoryClickEvent) -> Unit) {
|
||||||
|
this.onLeftClick = onLeftClick
|
||||||
|
}
|
||||||
|
|
||||||
|
fun onMiddleClick(onMiddleClick: suspend (event: InventoryClickEvent) -> Unit) {
|
||||||
|
this.onMiddleClick = onMiddleClick
|
||||||
|
}
|
||||||
|
|
||||||
|
fun onRightClick(onRightClick: suspend (event: InventoryClickEvent) -> Unit) {
|
||||||
|
this.onRightClick = onRightClick
|
||||||
|
}
|
||||||
|
|
||||||
|
fun onOtherClick(onOtherClick: suspend (event: InventoryClickEvent) -> Unit) {
|
||||||
|
this.onOtherClick = onOtherClick
|
||||||
|
}
|
||||||
|
|
||||||
|
fun lore(line: Component) {
|
||||||
|
lore.add(line)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getSlot(): Int {
|
||||||
|
return x + y * 9
|
||||||
|
}
|
||||||
|
|
||||||
|
fun toItemStack(): ItemStack {
|
||||||
|
val itemStack = ItemStack(material, amount)
|
||||||
|
val itemMeta = itemStack.itemMeta
|
||||||
|
itemMeta.displayName(name)
|
||||||
|
itemMeta.lore(lore)
|
||||||
|
|
||||||
|
itemStack.itemMeta = itemMeta
|
||||||
|
return itemStack
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,159 @@
|
||||||
|
package nl.kallestruik.dlib.gui
|
||||||
|
|
||||||
|
import kotlinx.coroutines.runBlocking
|
||||||
|
import net.kyori.adventure.text.Component
|
||||||
|
import net.kyori.adventure.text.format.NamedTextColor
|
||||||
|
import org.bukkit.Bukkit
|
||||||
|
import org.bukkit.Material
|
||||||
|
import org.bukkit.entity.Player
|
||||||
|
import org.bukkit.event.EventHandler
|
||||||
|
import org.bukkit.event.HandlerList
|
||||||
|
import org.bukkit.event.Listener
|
||||||
|
import org.bukkit.event.inventory.InventoryAction
|
||||||
|
import org.bukkit.event.inventory.InventoryClickEvent
|
||||||
|
import org.bukkit.event.inventory.InventoryCloseEvent
|
||||||
|
import org.bukkit.inventory.InventoryView
|
||||||
|
import org.bukkit.inventory.ItemStack
|
||||||
|
import org.bukkit.plugin.Plugin
|
||||||
|
|
||||||
|
fun confirmationDialog(player: Player, plugin: Plugin, configure: suspend ConfirmationDialog.() -> Unit) {
|
||||||
|
val confirmationDialog = ConfirmationDialog()
|
||||||
|
runBlocking {
|
||||||
|
confirmationDialog.configure()
|
||||||
|
}
|
||||||
|
|
||||||
|
confirmationDialog.display(player, plugin)
|
||||||
|
}
|
||||||
|
|
||||||
|
class ConfirmationDialog: Listener {
|
||||||
|
private data class Configuration(
|
||||||
|
var onConfirm: suspend () -> Unit = {},
|
||||||
|
var onCancel: suspend () -> Unit = {}
|
||||||
|
)
|
||||||
|
|
||||||
|
var title: Component = Component.empty()
|
||||||
|
var description: Component = Component.empty()
|
||||||
|
|
||||||
|
private val configuration = Configuration()
|
||||||
|
private lateinit var plugin: Plugin
|
||||||
|
private lateinit var view: InventoryView
|
||||||
|
|
||||||
|
fun onConfirm(onConfirm: suspend () -> Unit) {
|
||||||
|
configuration.onConfirm = onConfirm
|
||||||
|
}
|
||||||
|
|
||||||
|
fun onCancel(onCancel: suspend () -> Unit) {
|
||||||
|
configuration.onCancel = onCancel
|
||||||
|
}
|
||||||
|
|
||||||
|
fun display(player: Player, plugin: Plugin) {
|
||||||
|
player.closeInventory()
|
||||||
|
this.plugin = plugin
|
||||||
|
|
||||||
|
val inventory = Bukkit.createInventory(null, 3 * 9, title)
|
||||||
|
view = player.openInventory(inventory) ?: return
|
||||||
|
|
||||||
|
val borderStack = ItemStack(Material.ORANGE_STAINED_GLASS_PANE, 1)
|
||||||
|
val borderMeta = borderStack.itemMeta
|
||||||
|
borderMeta.displayName(Component.empty())
|
||||||
|
borderStack.itemMeta = borderMeta
|
||||||
|
|
||||||
|
// Top border
|
||||||
|
for (x in 8 downTo 0) {
|
||||||
|
view.setItem(x, borderStack)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Middle sides
|
||||||
|
view.setItem(9, borderStack)
|
||||||
|
view.setItem(17, borderStack)
|
||||||
|
|
||||||
|
// Bottom border
|
||||||
|
for (x in 26 downTo 18) {
|
||||||
|
view.setItem(x, borderStack)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Description item
|
||||||
|
val descriptionItem = ItemStack(Material.PAPER, 1)
|
||||||
|
val descriptionMeta = descriptionItem.itemMeta
|
||||||
|
descriptionMeta.displayName(description)
|
||||||
|
descriptionItem.itemMeta = descriptionMeta
|
||||||
|
|
||||||
|
// Confirm item
|
||||||
|
val confirmItem = ItemStack(Material.SUNFLOWER, 1)
|
||||||
|
val confirmMeta = confirmItem.itemMeta
|
||||||
|
confirmMeta.displayName(Component.text("Confirm").color(NamedTextColor.GREEN))
|
||||||
|
confirmItem.itemMeta = confirmMeta
|
||||||
|
|
||||||
|
// Cancel item
|
||||||
|
val cancelItem = ItemStack(Material.BARRIER, 1)
|
||||||
|
val cancelMeta = cancelItem.itemMeta
|
||||||
|
cancelMeta.displayName(Component.text("Cancel").color(NamedTextColor.RED))
|
||||||
|
cancelItem.itemMeta = cancelMeta
|
||||||
|
|
||||||
|
// 0 1 2 3 4 5 6 7 8
|
||||||
|
// 9 10 11 12 13 14 15 16 17
|
||||||
|
// 18 19 20 21 22 23 24 25 26
|
||||||
|
|
||||||
|
view.setItem(4, descriptionItem)
|
||||||
|
view.setItem(11, confirmItem)
|
||||||
|
view.setItem(15, cancelItem)
|
||||||
|
|
||||||
|
plugin.server.pluginManager.registerEvents(this, plugin)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun cleanup() {
|
||||||
|
view.close()
|
||||||
|
HandlerList.unregisterAll(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Bukkit event handlers
|
||||||
|
*/
|
||||||
|
@EventHandler
|
||||||
|
fun onInventoryClose(event: InventoryCloseEvent) {
|
||||||
|
if (event.view != this.view)
|
||||||
|
return
|
||||||
|
|
||||||
|
if (event.reason == InventoryCloseEvent.Reason.PLUGIN) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
runBlocking {
|
||||||
|
configuration.onCancel.invoke()
|
||||||
|
cleanup()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@EventHandler
|
||||||
|
fun onOperationFinish(event: InventoryClickEvent) {
|
||||||
|
if (event.view != view) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (event.action != InventoryAction.PICKUP_ALL) {
|
||||||
|
event.isCancelled = true
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
val slot = view.convertSlot(event.rawSlot)
|
||||||
|
|
||||||
|
if (slot != 11 && slot != 15) {
|
||||||
|
event.isCancelled = true
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
event.isCancelled = true
|
||||||
|
|
||||||
|
if (slot == 11) {
|
||||||
|
runBlocking {
|
||||||
|
configuration.onConfirm()
|
||||||
|
cleanup()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
runBlocking {
|
||||||
|
configuration.onCancel()
|
||||||
|
cleanup()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,138 @@
|
||||||
|
package nl.kallestruik.dlib.gui
|
||||||
|
|
||||||
|
import kotlinx.coroutines.runBlocking
|
||||||
|
import net.kyori.adventure.text.Component
|
||||||
|
import org.bukkit.Material
|
||||||
|
import org.bukkit.entity.Player
|
||||||
|
import org.bukkit.event.EventHandler
|
||||||
|
import org.bukkit.event.HandlerList
|
||||||
|
import org.bukkit.event.Listener
|
||||||
|
import org.bukkit.event.inventory.*
|
||||||
|
import org.bukkit.inventory.InventoryView
|
||||||
|
import org.bukkit.inventory.ItemStack
|
||||||
|
import org.bukkit.plugin.Plugin
|
||||||
|
|
||||||
|
fun textInputDialog(player: Player, plugin: Plugin, configure: suspend TextInputDialog.() -> Unit) {
|
||||||
|
val textInputDialog = TextInputDialog()
|
||||||
|
runBlocking {
|
||||||
|
textInputDialog.configure()
|
||||||
|
}
|
||||||
|
|
||||||
|
textInputDialog.display(player, plugin)
|
||||||
|
}
|
||||||
|
|
||||||
|
class TextInputDialog: Listener {
|
||||||
|
private data class Configuration(
|
||||||
|
var item: suspend TextInputDialogItem.() -> Unit = {},
|
||||||
|
var onFinish: suspend (value: String) -> Unit = {},
|
||||||
|
var onCancel: suspend () -> Unit = {}
|
||||||
|
)
|
||||||
|
|
||||||
|
private val configuration = Configuration()
|
||||||
|
var prompt: Component = Component.empty()
|
||||||
|
|
||||||
|
private lateinit var view: InventoryView
|
||||||
|
private lateinit var plugin: Plugin
|
||||||
|
|
||||||
|
|
||||||
|
fun item(entry: suspend TextInputDialogItem.() -> Unit) {
|
||||||
|
configuration.item = entry
|
||||||
|
}
|
||||||
|
|
||||||
|
fun onFinish(onFinish: suspend (value: String) -> Unit) {
|
||||||
|
configuration.onFinish = onFinish
|
||||||
|
}
|
||||||
|
|
||||||
|
fun onCancel(onCancel: suspend () -> Unit) {
|
||||||
|
configuration.onCancel = onCancel
|
||||||
|
}
|
||||||
|
|
||||||
|
fun display(player: Player, plugin: Plugin) {
|
||||||
|
player.closeInventory()
|
||||||
|
this.plugin = plugin
|
||||||
|
view = player.openAnvil(null, true) ?: return
|
||||||
|
|
||||||
|
val entry = TextInputDialogItem()
|
||||||
|
runBlocking {
|
||||||
|
configuration.item.invoke(entry)
|
||||||
|
}
|
||||||
|
|
||||||
|
val itemStack = ItemStack(entry.material, entry.amount)
|
||||||
|
val itemMeta = itemStack.itemMeta
|
||||||
|
itemMeta.displayName(prompt)
|
||||||
|
itemStack.itemMeta = itemMeta
|
||||||
|
|
||||||
|
view.setItem(0, itemStack)
|
||||||
|
|
||||||
|
plugin.server.pluginManager.registerEvents(this, plugin)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun cleanup() {
|
||||||
|
view.close()
|
||||||
|
HandlerList.unregisterAll(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Bukkit event handlers
|
||||||
|
*/
|
||||||
|
@EventHandler
|
||||||
|
fun onInventoryClose(event: InventoryCloseEvent) {
|
||||||
|
if (event.view != this.view)
|
||||||
|
return
|
||||||
|
|
||||||
|
if (event.reason == InventoryCloseEvent.Reason.PLUGIN) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
runBlocking {
|
||||||
|
configuration.onCancel.invoke()
|
||||||
|
cleanup()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@EventHandler
|
||||||
|
fun onAnvilPrepare(event: PrepareAnvilEvent) {
|
||||||
|
if (event.view != view)
|
||||||
|
return
|
||||||
|
|
||||||
|
// Always keep the cost at 1. 0 Causes the client to not be able to click it.
|
||||||
|
event.inventory.repairCost = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
@EventHandler
|
||||||
|
fun onOperationFinish(event: InventoryClickEvent) {
|
||||||
|
if (event.view != view) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (event.action != InventoryAction.PICKUP_ALL) {
|
||||||
|
event.isCancelled = true
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (view.convertSlot(event.rawSlot) != 2) {
|
||||||
|
event.isCancelled = true
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
val item = event.currentItem
|
||||||
|
val name = item?.itemMeta?.displayName ?: ""
|
||||||
|
|
||||||
|
if (name == "") {
|
||||||
|
event.isCancelled = true
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
event.isCancelled = true
|
||||||
|
|
||||||
|
runBlocking {
|
||||||
|
configuration.onFinish.invoke(name)
|
||||||
|
cleanup()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
data class TextInputDialogItem(
|
||||||
|
var material: Material = Material.AIR,
|
||||||
|
var amount: Int = 1,
|
||||||
|
)
|
|
@ -0,0 +1,21 @@
|
||||||
|
package nl.kallestruik.dlib.serializers
|
||||||
|
|
||||||
|
import kotlinx.serialization.KSerializer
|
||||||
|
import kotlinx.serialization.descriptors.PrimitiveKind
|
||||||
|
import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor
|
||||||
|
import kotlinx.serialization.descriptors.SerialDescriptor
|
||||||
|
import kotlinx.serialization.encoding.Decoder
|
||||||
|
import kotlinx.serialization.encoding.Encoder
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
object UUIDSerializer: KSerializer<UUID> {
|
||||||
|
override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("uuid", PrimitiveKind.STRING)
|
||||||
|
|
||||||
|
override fun deserialize(decoder: Decoder): UUID {
|
||||||
|
return UUID.fromString(decoder.decodeString())
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun serialize(encoder: Encoder, value: UUID) {
|
||||||
|
encoder.encodeString(value.toString())
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,4 @@
|
||||||
|
name: "DLib"
|
||||||
|
main: nl.kallestruik.dlib.DLib
|
||||||
|
version: ${version}
|
||||||
|
api-version: 1.16
|
|
@ -0,0 +1,161 @@
|
||||||
|
package nl.kallestruik.dlib.config
|
||||||
|
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
import kotlinx.serialization.json.JsonPrimitive
|
||||||
|
import nl.kallestruik.dlib.config.annotations.*
|
||||||
|
import kotlin.test.Test
|
||||||
|
import kotlin.test.assertEquals
|
||||||
|
|
||||||
|
|
||||||
|
class ConfigSchemaBuilderTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `config scheme contains all descriptions`() {
|
||||||
|
val schema = Company.serializer().descriptor.toConfigSchema()
|
||||||
|
|
||||||
|
assertEquals(
|
||||||
|
listOf("The name of the company."),
|
||||||
|
schema.children!!["name"]!!.description
|
||||||
|
)
|
||||||
|
assertEquals(
|
||||||
|
listOf("The person that owns the company."),
|
||||||
|
schema.children!!["owner"]!!.description
|
||||||
|
)
|
||||||
|
assertEquals(
|
||||||
|
listOf("All the employees of the company.", "This list should also contain the owner for legacy reasons ;)."),
|
||||||
|
schema.children!!["employees"]!!.description
|
||||||
|
)
|
||||||
|
|
||||||
|
val personSchema = schema.children!!["owner"]!!
|
||||||
|
|
||||||
|
assertEquals(
|
||||||
|
listOf("The list of qualifications that this person has."),
|
||||||
|
personSchema.children!!["qualifications"]!!.description
|
||||||
|
)
|
||||||
|
assertEquals(
|
||||||
|
listOf("The age of the person."),
|
||||||
|
personSchema.children!!["age"]!!.description
|
||||||
|
)
|
||||||
|
assertEquals(
|
||||||
|
listOf("Whether the person is currently employed at a company."),
|
||||||
|
personSchema.children!!["isEmployed"]!!.description
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `config schema has correct defaults`() {
|
||||||
|
val schema = Company.serializer().descriptor.toConfigSchema()
|
||||||
|
val personSchema = schema.children!!["owner"]!!
|
||||||
|
|
||||||
|
assertEquals(JsonPrimitive("Bob"), personSchema.children!!["firstName"]!!.default)
|
||||||
|
assertEquals(JsonPrimitive(18.0), personSchema.children!!["age"]!!.default)
|
||||||
|
assertEquals(JsonPrimitive(true), personSchema.children!!["isEmployed"]!!.default)
|
||||||
|
|
||||||
|
val qualificationSchema = personSchema.children!!["qualifications"]!!.items!!
|
||||||
|
|
||||||
|
assertEquals(JsonPrimitive("EXPERIENCE"), qualificationSchema.children!!["type"]!!.default)
|
||||||
|
assertEquals(JsonPrimitive(0), qualificationSchema.children!!["yearObtained"]!!.default)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `config schema allowed keys are correct`() {
|
||||||
|
val schema = Company.serializer().descriptor.toConfigSchema()
|
||||||
|
|
||||||
|
assertEquals(
|
||||||
|
listOf(listOf("Bob"), listOf("Joe"), listOf("Kon"), listOf("Jane", "Kevin")),
|
||||||
|
schema.children!!["employees"]!!.constraints.allowedKeys
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `config schema enum values are correct`() {
|
||||||
|
val schema = Company.serializer().descriptor.toConfigSchema()
|
||||||
|
val personSchema = schema.children!!["owner"]!!
|
||||||
|
|
||||||
|
assertEquals(
|
||||||
|
listOf("Bob", "Joe", "Kon", "Jane", "Kevin"),
|
||||||
|
personSchema.children!!["firstName"]!!.constraints.enum
|
||||||
|
)
|
||||||
|
|
||||||
|
val qualificationSchema = personSchema.children!!["qualifications"]!!.items!!
|
||||||
|
|
||||||
|
assertEquals(
|
||||||
|
listOf("DIPLOMA", "EXPERIENCE", "CERTIFICATE"),
|
||||||
|
qualificationSchema.children!!["type"]!!.constraints.enum
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `config schema has correct min and max values`() {
|
||||||
|
val schema = Company.serializer().descriptor.toConfigSchema()
|
||||||
|
val personSchema = schema.children!!["owner"]!!
|
||||||
|
|
||||||
|
assertEquals(
|
||||||
|
0.0,
|
||||||
|
personSchema.children!!["age"]!!.constraints.minValueFloat
|
||||||
|
)
|
||||||
|
assertEquals(
|
||||||
|
75.0,
|
||||||
|
personSchema.children!!["age"]!!.constraints.maxValueFloat
|
||||||
|
)
|
||||||
|
|
||||||
|
val qualificationSchema = personSchema.children!!["qualifications"]!!.items!!
|
||||||
|
|
||||||
|
assertEquals(
|
||||||
|
0,
|
||||||
|
qualificationSchema.children!!["yearObtained"]!!.constraints.minValueInt
|
||||||
|
)
|
||||||
|
assertEquals(
|
||||||
|
9999,
|
||||||
|
qualificationSchema.children!!["yearObtained"]!!.constraints.maxValueInt
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class Company(
|
||||||
|
@Description("The name of the company.")
|
||||||
|
val name: String,
|
||||||
|
@Description("The person that owns the company.")
|
||||||
|
val owner: Person,
|
||||||
|
@Description("All the employees of the company.", "This list should also contain the owner for legacy reasons ;).")
|
||||||
|
@AllowedKeys("Bob", "Joe", "Kon") // Don't question it. I need to test stuff.
|
||||||
|
@AllowedKeysExclusive("Jane", "Kevin")
|
||||||
|
val employees: Map<String, Person>,
|
||||||
|
)
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class Person(
|
||||||
|
@DefaultString("Bob")
|
||||||
|
@StringEnum("Bob", "Joe", "Kon", "Jane", "Kevin")
|
||||||
|
val firstName: String,
|
||||||
|
val lastName: String,
|
||||||
|
@Description("The list of qualifications that this person has.")
|
||||||
|
val qualifications: List<Qualification>,
|
||||||
|
@DefaultFloat(18.0)
|
||||||
|
@MinFloat(0.0)
|
||||||
|
@MaxFloat(75.0)
|
||||||
|
@Description("The age of the person.")
|
||||||
|
val age: Float,
|
||||||
|
@DefaultBoolean(true)
|
||||||
|
@Description("Whether the person is currently employed at a company.")
|
||||||
|
val isEmployed: Boolean,
|
||||||
|
)
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class Qualification(
|
||||||
|
val name: String,
|
||||||
|
@DefaultString("EXPERIENCE")
|
||||||
|
val type: QualificationType,
|
||||||
|
@DefaultInt(0)
|
||||||
|
@MinInt(0)
|
||||||
|
@MaxInt(9999)
|
||||||
|
val yearObtained: Int,
|
||||||
|
)
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
enum class QualificationType {
|
||||||
|
DIPLOMA,
|
||||||
|
EXPERIENCE,
|
||||||
|
CERTIFICATE,
|
||||||
|
}
|
Loading…
Reference in New Issue