# Conflicts:
#	pom.xml
This commit is contained in:
zimzaza4
2025-07-10 20:29:36 +08:00
41 changed files with 1694 additions and 913 deletions

44
.gitignore vendored
View File

@@ -1,2 +1,42 @@
# 项目排除路径 .gradle
/target/ build/
!gradle/wrapper/gradle-wrapper.jar
!**/src/main/**/build/
!**/src/test/**/build/
### IntelliJ IDEA ###
.idea/modules.xml
.idea/jarRepositories.xml
.idea/compiler.xml
.idea/libraries/
*.iws
*.iml
*.ipr
out/
!**/src/main/**/out/
!**/src/test/**/out/
### Eclipse ###
.apt_generated
.classpath
.factorypath
.project
.settings
.springBeans
.sts4-cache
bin/
!**/src/main/**/bin/
!**/src/test/**/bin/
### NetBeans ###
/nbproject/private/
/nbbuild/
/dist/
/nbdist/
/.nb-gradle/
### VS Code ###
.vscode/
### Mac OS ###
.DS_Store

6
.idea/compiler.xml generated Normal file
View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="CompilerConfiguration">
<bytecodeTargetLevel target="21" />
</component>
</project>

7
.idea/encodings.xml generated Normal file
View File

@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Encoding">
<file url="file://$PROJECT_DIR$/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/src/main/resources" charset="UTF-8" />
</component>
</project>

16
.idea/gradle.xml generated Normal file
View File

@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="GradleMigrationSettings" migrationVersion="1" />
<component name="GradleSettings">
<option name="linkedExternalProjectsSettings">
<GradleProjectSettings>
<option name="externalProjectPath" value="$PROJECT_DIR$" />
<option name="modules">
<set>
<option value="$PROJECT_DIR$" />
</set>
</option>
</GradleProjectSettings>
</option>
</component>
</project>

65
.idea/jarRepositories.xml generated Normal file
View File

@@ -0,0 +1,65 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="RemoteRepositoriesConfiguration">
<remote-repository>
<option name="id" value="codemc-snapshots" />
<option name="name" value="codemc-snapshots" />
<option name="url" value="https://repo.codemc.io/repository/maven-snapshots/" />
</remote-repository>
<remote-repository>
<option name="id" value="central" />
<option name="name" value="Central Repository" />
<option name="url" value="https://repo.maven.apache.org/maven2" />
</remote-repository>
<remote-repository>
<option name="id" value="opencollab-snapshot-repo" />
<option name="name" value="opencollab-snapshot-repo" />
<option name="url" value="https://repo.opencollab.dev/maven-snapshots/" />
</remote-repository>
<remote-repository>
<option name="id" value="sonatype" />
<option name="name" value="sonatype" />
<option name="url" value="https://oss.sonatype.org/content/groups/public/" />
</remote-repository>
<remote-repository>
<option name="id" value="opencollab-release-repo" />
<option name="name" value="opencollab-release-repo" />
<option name="url" value="https://repo.opencollab.dev/maven-releases/" />
</remote-repository>
<remote-repository>
<option name="id" value="codemc-releases" />
<option name="name" value="codemc-releases" />
<option name="url" value="https://repo.codemc.io/repository/maven-releases/" />
</remote-repository>
<remote-repository>
<option name="id" value="dmulloy2-repo" />
<option name="name" value="dmulloy2-repo" />
<option name="url" value="https://repo.dmulloy2.net/repository/public/" />
</remote-repository>
<remote-repository>
<option name="id" value="papermc-repo" />
<option name="name" value="papermc-repo" />
<option name="url" value="https://repo.papermc.io/repository/maven-public/" />
</remote-repository>
<remote-repository>
<option name="id" value="central" />
<option name="name" value="Maven Central repository" />
<option name="url" value="https://repo1.maven.org/maven2" />
</remote-repository>
<remote-repository>
<option name="id" value="nexus" />
<option name="name" value="Lumine Public" />
<option name="url" value="https://mvn.lumine.io/repository/maven-public/" />
</remote-repository>
<remote-repository>
<option name="id" value="jboss.community" />
<option name="name" value="JBoss Community repository" />
<option name="url" value="https://repository.jboss.org/nexus/content/repositories/public/" />
</remote-repository>
<remote-repository>
<option name="id" value="md_5-public" />
<option name="name" value="md_5-public" />
<option name="url" value="https://repo.md-5.net/content/groups/public/" />
</remote-repository>
</component>
</project>

5
.idea/misc.xml generated Normal file
View File

@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ExternalStorageConfigurationManager" enabled="true" />
<component name="ProjectRootManager" version="2" languageLevel="JDK_21" default="true" project-jdk-name="21" project-jdk-type="JavaSDK" />
</project>

6
.idea/vcs.xml generated Normal file
View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="" vcs="Git" />
</component>
</project>

139
.idea/workspace.xml generated Normal file
View File

@@ -0,0 +1,139 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="AutoImportSettings">
<option name="autoReloadType" value="SELECTIVE" />
</component>
<component name="ChangeListManager">
<list default="true" id="ff2e9770-ec88-4715-adeb-b9dbda130e1a" name="Changes" comment="">
<change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
</list>
<option name="SHOW_DIALOG" value="false" />
<option name="HIGHLIGHT_CONFLICTS" value="true" />
<option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
<option name="LAST_RESOLUTION" value="IGNORE" />
</component>
<component name="ExternalProjectsData">
<projectState path="$PROJECT_DIR$">
<ProjectState />
</projectState>
</component>
<component name="ExternalProjectsManager">
<system id="GRADLE">
<state>
<task path="$PROJECT_DIR$">
<activation />
</task>
<projects_view>
<tree_state>
<expand>
<path>
<item name="" type="6a2764b6:ExternalProjectsStructure$RootNode" />
<item name="GeyserModelEngine" type="f1a62948:ProjectNode" />
</path>
<path>
<item name="" type="6a2764b6:ExternalProjectsStructure$RootNode" />
<item name="GeyserModelEngine" type="f1a62948:ProjectNode" />
<item name="Tasks" type="e4a08cd1:TasksNode" />
</path>
<path>
<item name="" type="6a2764b6:ExternalProjectsStructure$RootNode" />
<item name="GeyserModelEngine" type="f1a62948:ProjectNode" />
<item name="Tasks" type="e4a08cd1:TasksNode" />
<item name="build" type="c8890929:TasksNode$1" />
</path>
</expand>
<select />
</tree_state>
</projects_view>
</state>
</system>
</component>
<component name="FileTemplateManagerImpl">
<option name="RECENT_TEMPLATES">
<list>
<option value="Interface" />
<option value="Class" />
</list>
</option>
</component>
<component name="Git.Settings">
<option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$" />
</component>
<component name="MavenRunner">
<option name="delegateBuildToMaven" value="true" />
</component>
<component name="ProjectColorInfo">{
&quot;associatedIndex&quot;: 8
}</component>
<component name="ProjectId" id="2xedme8VKz03tyMoE1OuGEibnGo" />
<component name="ProjectLevelVcsManager">
<ConfirmationsSetting value="2" id="Add" />
</component>
<component name="ProjectViewState">
<option name="hideEmptyMiddlePackages" value="true" />
<option name="showLibraryContents" value="true" />
</component>
<component name="PropertiesComponent">{
&quot;keyToString&quot;: {
&quot;Gradle.GeyserModelEngine [jar].executor&quot;: &quot;Run&quot;,
&quot;Maven.GeyserModelEngine [install...].executor&quot;: &quot;Run&quot;,
&quot;Maven.GeyserModelEngine [install].executor&quot;: &quot;Run&quot;,
&quot;ModuleVcsDetector.initialDetectionPerformed&quot;: &quot;true&quot;,
&quot;RunOnceActivity.ShowReadmeOnStart&quot;: &quot;true&quot;,
&quot;RunOnceActivity.git.unshallow&quot;: &quot;true&quot;,
&quot;git-widget-placeholder&quot;: &quot;main&quot;,
&quot;last_opened_file_path&quot;: &quot;D:/Coding/Forks/Minecraft/GeyserModelEngine&quot;,
&quot;project.structure.last.edited&quot;: &quot;Project&quot;,
&quot;project.structure.proportion&quot;: &quot;0.0&quot;,
&quot;project.structure.side.proportion&quot;: &quot;0.2&quot;,
&quot;settings.editor.selected.configurable&quot;: &quot;reference.settings.project.maven.runner&quot;
}
}</component>
<component name="RecentsManager">
<key name="CopyFile.RECENT_KEYS">
<recent name="D:\Coding\Forks\Minecraft\GeyserModelEngine" />
</key>
<key name="MoveFile.RECENT_KEYS">
<recent name="D:\Coding\Forks\Minecraft\GeyserModelEngine\libs" />
</key>
</component>
<component name="RunManager">
<configuration name="GeyserModelEngine [jar]" type="GradleRunConfiguration" factoryName="Gradle" temporary="true">
<ExternalSystemSettings>
<option name="executionName" />
<option name="externalProjectPath" value="$PROJECT_DIR$" />
<option name="externalSystemIdString" value="GRADLE" />
<option name="scriptParameters" />
<option name="taskDescriptions">
<list />
</option>
<option name="taskNames">
<list>
<option value="jar" />
</list>
</option>
<option name="vmOptions" />
</ExternalSystemSettings>
<ExternalSystemDebugServerProcess>true</ExternalSystemDebugServerProcess>
<ExternalSystemReattachDebugProcess>true</ExternalSystemReattachDebugProcess>
<DebugAllEnabled>false</DebugAllEnabled>
<RunAsTest>false</RunAsTest>
<method v="2" />
</configuration>
<recent_temporary>
<list>
<item itemvalue="Gradle.GeyserModelEngine [jar]" />
</list>
</recent_temporary>
</component>
<component name="TaskManager">
<task active="true" id="Default" summary="Default task">
<changelist id="ff2e9770-ec88-4715-adeb-b9dbda130e1a" name="Changes" comment="" />
<created>1748302633990</created>
<option name="number" value="Default" />
<option name="presentableId" value="Default" />
<updated>1748302633990</updated>
</task>
<servers />
</component>
</project>

View File

@@ -1,16 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<module version="4">
<component name="FacetManager">
<facet type="minecraft" name="Minecraft">
<configuration>
<autoDetectTypes>
<platformType>PAPER</platformType>
<platformType>ADVENTURE</platformType>
</autoDetectTypes>
</configuration>
</facet>
</component>
<component name="McpModuleSettings">
<option name="srgType" value="SRG" />
</component>
</module>

56
build.gradle.kts Normal file
View File

@@ -0,0 +1,56 @@
plugins {
id("java")
id("io.github.goooler.shadow") version "8.1.7"
}
group = "re.imc"
version = "1.0.0"
repositories {
mavenCentral()
maven("https://repo.papermc.io/repository/maven-public/")
maven("https://central.sonatype.com/repository/maven-snapshots/")
maven("https://mvn.lumine.io/repository/maven-public/")
maven("https://repo.opencollab.dev/main/")
maven("https://repo.codemc.io/repository/maven-public/")
maven("https://repo.codemc.io/repository/maven-releases/")
}
dependencies {
compileOnly("io.papermc.paper:paper-api:1.21.7-R0.1-SNAPSHOT")
implementation("dev.jorel:commandapi-bukkit-shade-mojang-mapped:10.1.1")
compileOnly("com.ticxo.modelengine:ModelEngine:R4.0.4")
compileOnly(files("libs/geyserutils-spigot-1.0-SNAPSHOT.jar"))
compileOnly("org.geysermc.floodgate:api:2.2.4-SNAPSHOT")
implementation("com.github.retrooper:packetevents-spigot:2.9.0-SNAPSHOT")
implementation("org.reflections:reflections:0.10.2")
}
java {
toolchain.languageVersion.set(JavaLanguageVersion.of(21))
}
tasks.compileJava {
options.encoding = "UTF-8"
}
tasks.shadowJar {
relocate("dev.jorel.commandapi", "re.imc.geysermodelengine.libs.commandapi")
relocate("com.github.retrooper", "re.imc.geysermodelengine.libs.com.github.retrooper.packetevents")
relocate("io.github.retrooper", "re.imc.geysermodelengine.libs.io.github.retrooper.packetevents")
relocate("org.reflections", "re.imc.geysermodelengine.libs.reflections")
}
tasks.jar {
dependsOn(tasks.shadowJar)
}

BIN
gradle/wrapper/gradle-wrapper.jar vendored Normal file

Binary file not shown.

View File

@@ -0,0 +1,7 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.12-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

251
gradlew vendored Normal file
View File

@@ -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=$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
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, 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" \
org.gradle.wrapper.GradleWrapperMain \
"$@"
# 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" "$@"

94
gradlew.bat vendored Normal file
View File

@@ -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=%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% 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

2
settings.gradle.kts Normal file
View File

@@ -0,0 +1,2 @@
rootProject.name = "GeyserModelEngine"

View File

@@ -2,151 +2,98 @@ package re.imc.geysermodelengine;
import com.github.retrooper.packetevents.PacketEvents; import com.github.retrooper.packetevents.PacketEvents;
import com.github.retrooper.packetevents.event.PacketListenerPriority; import com.github.retrooper.packetevents.event.PacketListenerPriority;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import com.ticxo.modelengine.api.ModelEngineAPI;
import com.ticxo.modelengine.api.model.ActiveModel; import com.ticxo.modelengine.api.model.ActiveModel;
import com.ticxo.modelengine.api.model.ModeledEntity; import dev.jorel.commandapi.CommandAPI;
import com.ticxo.modelengine.api.model.bone.type.Mount; import dev.jorel.commandapi.CommandAPIBukkitConfig;
import io.github.retrooper.packetevents.factory.spigot.SpigotPacketEventsBuilder; import io.github.retrooper.packetevents.factory.spigot.SpigotPacketEventsBuilder;
import lombok.Getter;
import org.apache.commons.lang3.tuple.Pair;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.World;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Player;
import org.bukkit.plugin.java.JavaPlugin; import org.bukkit.plugin.java.JavaPlugin;
import re.imc.geysermodelengine.commands.ReloadCommand;
import re.imc.geysermodelengine.listener.ModelListener; import re.imc.geysermodelengine.listener.ModelListener;
import re.imc.geysermodelengine.listener.MountPacketListener; import re.imc.geysermodelengine.listener.MountPacketListener;
import re.imc.geysermodelengine.model.BedrockMountControl; import re.imc.geysermodelengine.managers.ConfigManager;
import re.imc.geysermodelengine.model.ModelEntity; import re.imc.geysermodelengine.managers.commands.CommandManager;
import re.imc.geysermodelengine.managers.model.EntityTaskManager;
import re.imc.geysermodelengine.managers.model.ModelManager;
import re.imc.geysermodelengine.managers.model.data.ModelEntityData;
import re.imc.geysermodelengine.runnables.BedrockMountControlRunnable;
import re.imc.geysermodelengine.runnables.UpdateTaskRunnable;
import java.util.*; import java.util.*;
import java.util.concurrent.*; import java.util.concurrent.*;
public final class GeyserModelEngine extends JavaPlugin { public class GeyserModelEngine extends JavaPlugin {
@Getter private ConfigManager configManager;
private static GeyserModelEngine instance;
@Getter private CommandManager commandManager;
private static boolean alwaysSendSkin;
@Getter private ModelManager modelManager;
private int sendDelay; private EntityTaskManager entityTaskManager;
@Getter
private int viewDistance;
@Getter
private Set<Player> joinedPlayers = new HashSet<>();
@Getter
private int joinSendDelay;
@Getter
private long entityPositionUpdatePeriod;
@Getter
private boolean debug;
@Getter
private Map<Player, Pair<ActiveModel, Mount>> drivers = new ConcurrentHashMap<>();
@Getter
private boolean initialized = false;
@Getter
private List<String> enablePartVisibilityModels = new ArrayList<>();
@Getter
private ScheduledExecutorService scheduler;
private ScheduledFuture<?> updateTask;
@Override @Override
public void onLoad() { public void onLoad() {
PacketEvents.setAPI(SpigotPacketEventsBuilder.build(this)); PacketEvents.setAPI(SpigotPacketEventsBuilder.build(this));
PacketEvents.getAPI().load(); PacketEvents.getAPI().load();
CommandAPI.onLoad(new CommandAPIBukkitConfig(this));
} }
@Override @Override
public void onEnable() { public void onEnable() {
PacketEvents.getAPI().init(); loadHooks();
PacketEvents.getAPI().getEventManager().registerListener(new MountPacketListener(), PacketListenerPriority.NORMAL); loadManagers();
/* loadRunnables();
scheduler.scheduleAtFixedRate(() -> {
try {
for (Map<ActiveModel, ModelEntity> models : ModelEntity.ENTITIES.values()) {
models.values().forEach(ModelEntity::teleportToModel);
}
} catch (Throwable t) {
t.printStackTrace();
}
}, 10, entityPositionUpdatePeriod, TimeUnit.MILLISECONDS);
*/ PacketEvents.getAPI().getEventManager().registerListener(new MountPacketListener(this), PacketListenerPriority.NORMAL);
reload(); Bukkit.getPluginManager().registerEvents(new ModelListener(this), this);
getCommand("geysermodelengine").setExecutor(new ReloadCommand(this));
Bukkit.getPluginManager().registerEvents(new ModelListener(), this);
Bukkit.getScheduler()
.runTaskLater(GeyserModelEngine.getInstance(), () -> {
for (World world : Bukkit.getWorlds()) {
for (Entity entity : world.getEntities()) {
if (!ModelEntity.ENTITIES.containsKey(entity.getEntityId())) {
ModeledEntity modeledEntity = ModelEngineAPI.getModeledEntity(entity);
if (modeledEntity != null) {
Optional<ActiveModel> model = modeledEntity.getModels().values().stream().findFirst();
model.ifPresent(m -> ModelEntity.create(modeledEntity, m));
}
}
}
}
initialized = true;
}, 100);
BedrockMountControl.startTask();
}
public void reload() {
saveDefaultConfig();
// alwaysSendSkin = getConfig().getBoolean("always-send-skin");
sendDelay = getConfig().getInt("data-send-delay", 5);
scheduler = Executors.newScheduledThreadPool(getConfig().getInt("thread-pool-size", 4));
viewDistance = getConfig().getInt("entity-view-distance", 60);
debug = getConfig().getBoolean("debug", false);
joinSendDelay = getConfig().getInt("join-send-delay", 20);
entityPositionUpdatePeriod = getConfig().getLong("entity-position-update-period", 35);
enablePartVisibilityModels.addAll(getConfig().getStringList("enable-part-visibility-models"));
instance = this;
if (updateTask != null) {
updateTask.cancel(true);
}
updateTask = scheduler.scheduleWithFixedDelay(() -> {
try {
for (Map<ActiveModel, ModelEntity> models : ModelEntity.ENTITIES.values()) {
models.values().forEach(model -> model.getTask().updateEntityProperties(model.getViewers(), false));
}
} catch (Throwable t) {
t.printStackTrace();
}
}, 10, entityPositionUpdatePeriod, TimeUnit.MILLISECONDS);
} }
@Override @Override
public void onDisable() { public void onDisable() {
PacketEvents.getAPI().terminate(); PacketEvents.getAPI().terminate();
for (Map<ActiveModel, ModelEntity> entities : ModelEntity.ENTITIES.values()) {
for (Map<ActiveModel, ModelEntityData> entities : modelManager.getEntitiesCache().values()) {
entities.forEach((model, modelEntity) -> { entities.forEach((model, modelEntity) -> {
modelEntity.getEntity().remove(); modelEntity.getEntity().remove();
}); });
} }
// Plugin shutdown logic
CommandAPI.onDisable();
} }
private void loadHooks() {
PacketEvents.getAPI().init();
CommandAPI.onEnable();
}
private void loadManagers() {
this.configManager = new ConfigManager(this);
this.commandManager = new CommandManager(this);
this.modelManager = new ModelManager(this);
this.entityTaskManager = new EntityTaskManager(this);
}
private void loadRunnables() {
Bukkit.getAsyncScheduler().runAtFixedRate(this, new UpdateTaskRunnable(this), 10, configManager.getConfig().getLong("entity-position-update-period", 35), TimeUnit.MILLISECONDS);
Bukkit.getAsyncScheduler().runAtFixedRate(this, new BedrockMountControlRunnable(this), 1, 1, TimeUnit.MILLISECONDS);
}
public ConfigManager getConfigManager() {
return configManager;
}
public CommandManager getCommandManager() {
return commandManager;
}
public ModelManager getModelManager() {
return modelManager;
}
public EntityTaskManager getEntityTaskManager() {
return entityTaskManager;
}
} }

View File

@@ -1,31 +0,0 @@
package re.imc.geysermodelengine.commands;
import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import re.imc.geysermodelengine.GeyserModelEngine;
public class ReloadCommand implements CommandExecutor {
private final GeyserModelEngine plugin;
public ReloadCommand(GeyserModelEngine plugin) {
this.plugin = plugin;
}
@Override
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
if (sender instanceof Player && !sender.hasPermission("geysermodelengine.reload")) {
sender.sendMessage("§cYou don't have permission to use this command.");
return true;
}
plugin.reloadConfig();
plugin.reload();
sender.sendMessage("§aGeyserModelEngine configuration reloaded!");
return true;
}
}

View File

@@ -0,0 +1,28 @@
package re.imc.geysermodelengine.commands.geysermodelenginecommands;
import dev.jorel.commandapi.CommandAPICommand;
import org.bukkit.Bukkit;
import re.imc.geysermodelengine.GeyserModelEngine;
import re.imc.geysermodelengine.managers.commands.subcommands.SubCommands;
import re.imc.geysermodelengine.util.ColourUtils;
public class GeyserModelEngineReloadCommand implements SubCommands {
private final GeyserModelEngine plugin;
private final ColourUtils colourUtils = new ColourUtils();
public GeyserModelEngineReloadCommand(GeyserModelEngine plugin) {
this.plugin = plugin;
}
@Override
public CommandAPICommand onCommand() {
return new CommandAPICommand("reload")
.withPermission("geysermodelengine.commands.reload")
.executes((sender, args) -> {
Bukkit.getAsyncScheduler().runNow(plugin, scheduledTask -> plugin.getConfigManager().load());
sender.sendMessage(colourUtils.miniFormat(plugin.getConfigManager().getLang().getString("commands.reload.successfully-reloaded")));
});
}
}

View File

@@ -3,126 +3,72 @@ package re.imc.geysermodelengine.listener;
import com.ticxo.modelengine.api.events.AddModelEvent; import com.ticxo.modelengine.api.events.AddModelEvent;
import com.ticxo.modelengine.api.events.ModelDismountEvent; import com.ticxo.modelengine.api.events.ModelDismountEvent;
import com.ticxo.modelengine.api.events.ModelMountEvent; import com.ticxo.modelengine.api.events.ModelMountEvent;
import com.ticxo.modelengine.api.events.RemoveModelEvent;
import com.ticxo.modelengine.api.model.ActiveModel; import com.ticxo.modelengine.api.model.ActiveModel;
import org.apache.commons.lang3.tuple.Pair; import org.apache.commons.lang3.tuple.Pair;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.World;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler; import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority; import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener; import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerJoinEvent; import org.bukkit.event.player.PlayerJoinEvent;
import org.bukkit.event.player.PlayerLoginEvent;
import org.bukkit.event.player.PlayerQuitEvent; import org.bukkit.event.player.PlayerQuitEvent;
import org.bukkit.event.world.WorldInitEvent;
import org.bukkit.event.world.WorldLoadEvent;
import re.imc.geysermodelengine.GeyserModelEngine; import re.imc.geysermodelengine.GeyserModelEngine;
import re.imc.geysermodelengine.model.ModelEntity; import re.imc.geysermodelengine.managers.model.data.ModelEntityData;
import java.util.Map; import java.util.Map;
public class ModelListener implements Listener { public class ModelListener implements Listener {
@EventHandler(priority = EventPriority.MONITOR) private final GeyserModelEngine plugin;
public void onAddModel(AddModelEvent event) {
if (event.isCancelled()) {
return;
}
if (!GeyserModelEngine.getInstance().isInitialized()) { public ModelListener(GeyserModelEngine plugin) {
return; this.plugin = plugin;
}
ModelEntity.create(event.getTarget(), event.getModel());
} }
@EventHandler(priority = EventPriority.MONITOR)
public void onAddModel(AddModelEvent event) {
if (event.isCancelled()) return;
@EventHandler plugin.getModelManager().create(event.getTarget(), event.getModel());
public void onRemoveModel(RemoveModelEvent event) {
} }
@EventHandler(priority = EventPriority.MONITOR) @EventHandler(priority = EventPriority.MONITOR)
public void onModelMount(ModelMountEvent event) { public void onModelMount(ModelMountEvent event) {
Map<ActiveModel, ModelEntity> map = ModelEntity.ENTITIES.get(event.getVehicle().getModeledEntity().getBase().getEntityId()); Map<ActiveModel, ModelEntityData> map = plugin.getModelManager().getEntitiesCache().get(event.getVehicle().getModeledEntity().getBase().getEntityId());
if (map == null) { if (!event.isDriver()) return;
}
if (!event.isDriver()) { ModelEntityData model = map.get(event.getVehicle());
return;
}
ModelEntity model = map.get(event.getVehicle());
if (model != null && event.getPassenger() instanceof Player player) { if (model != null && event.getPassenger() instanceof Player player) {
GeyserModelEngine.getInstance().getDrivers().put(player, Pair.of(event.getVehicle(), event.getSeat())); plugin.getModelManager().getDriversCache().put(player.getUniqueId(), Pair.of(event.getVehicle(), event.getSeat()));
} }
} }
@EventHandler(priority = EventPriority.MONITOR) @EventHandler(priority = EventPriority.MONITOR)
public void onModelDismount(ModelDismountEvent event) { public void onModelDismount(ModelDismountEvent event) {
if (event.getPassenger() instanceof Player player) { if (event.getPassenger() instanceof Player player) {
GeyserModelEngine.getInstance().getDrivers().remove(player); plugin.getModelManager().getDriversCache().remove(player.getUniqueId());
} }
} }
/*
@EventHandler(priority = EventPriority.MONITOR)
public void onModelEntityHurt(EntityDamageEvent event) {
if (event.isCancelled()) {
return;
}
Map<ActiveModel, ModelEntity> model = ModelEntity.ENTITIES.get(event.getEntity().getEntityId());
if (model != null) {
for (Map.Entry<ActiveModel, ModelEntity> entry : model.entrySet()) {
if (!entry.getValue().getEntity().isDead()) {
//entry.getValue().getEntity().sendHurtPacket(entry.getValue().getViewers());
}
}
}
}
/*
@EventHandler @EventHandler
public void onModelAttack(EntityDamageByEntityEvent event) { public void onWorldInit(WorldInitEvent event) {
ModelEntity model = ModelEntity.ENTITIES.get(event.getDamager().getEntityId()); World world = event.getWorld();
if (model != null) { world.getEntities().forEach(entity -> plugin.getModelManager().processEntities(entity));
EntityTask task = model.getTask();
task.playAnimation("attack", 55);
}
}|
@EventHandler(ignoreCancelled = true, priority = EventPriority.MONITOR)
public void onAnimationPlay(AnimationPlayEvent event) {
Map<ActiveModel, ModelEntity> map = ModelEntity.ENTITIES.get(event.getModel().getModeledEntity().getBase().getEntityId());
if (map == null) {
return;
}
ModelEntity model = map.get(event.getModel());
model.getTask().updateEntityProperties(model.getViewers(), false, event.getProperty().getName());
} }
@EventHandler(ignoreCancelled = true, priority = EventPriority.MONITOR)
public void onAnimationEnd(AnimationEndEvent event) {
Map<ActiveModel, ModelEntity> map = ModelEntity.ENTITIES.get(event.getModel().getModeledEntity().getBase().getEntityId());
if (map == null) {
return;
}
ModelEntity model = map.get(event.getModel());
model.getTask().updateEntityProperties(model.getViewers(), false, event.getProperty().);
}
*/
@EventHandler @EventHandler
public void onPlayerLogin(PlayerJoinEvent event) { public void onPlayerJoin(PlayerJoinEvent event) {
Bukkit.getScheduler().runTaskLater(GeyserModelEngine.getInstance(), () -> GeyserModelEngine.getInstance().getJoinedPlayers().add(event.getPlayer()), 10); Player player = event.getPlayer();
plugin.getModelManager().getPlayerJoinedCache().add(player.getUniqueId());
} }
@EventHandler @EventHandler
public void onPlayerQuit(PlayerQuitEvent event) { public void onPlayerQuit(PlayerQuitEvent event) {
Bukkit.getScheduler().runTaskLater(GeyserModelEngine.getInstance(), () -> GeyserModelEngine.getInstance().getJoinedPlayers().remove(event.getPlayer()), 10); Player player = event.getPlayer();
plugin.getModelManager().getPlayerJoinedCache().remove(player.getUniqueId());
} }
} }

View File

@@ -8,24 +8,31 @@ import com.ticxo.modelengine.api.ModelEngineAPI;
import com.ticxo.modelengine.api.model.ActiveModel; import com.ticxo.modelengine.api.model.ActiveModel;
import com.ticxo.modelengine.api.model.bone.type.Mount; import com.ticxo.modelengine.api.model.bone.type.Mount;
import org.apache.commons.lang3.tuple.Pair; import org.apache.commons.lang3.tuple.Pair;
import org.bukkit.entity.Player;
import org.geysermc.floodgate.api.FloodgateApi; import org.geysermc.floodgate.api.FloodgateApi;
import re.imc.geysermodelengine.GeyserModelEngine; import re.imc.geysermodelengine.GeyserModelEngine;
public class MountPacketListener implements PacketListener { public class MountPacketListener implements PacketListener {
private final GeyserModelEngine plugin;
public MountPacketListener(GeyserModelEngine plugin) {
this.plugin = plugin;
}
@Override @Override
public void onPacketReceive(PacketReceiveEvent event) { public void onPacketReceive(PacketReceiveEvent event) {
if (event.getPacketType() != PacketType.Play.Client.ENTITY_ACTION) { if (event.getPacketType() != PacketType.Play.Client.ENTITY_ACTION) return;
return; if (!FloodgateApi.getInstance().isFloodgatePlayer(event.getUser().getUUID())) return;
}
if (!FloodgateApi.getInstance().isFloodgatePlayer(event.getUser().getUUID())) { Player player = event.getPlayer();
return;
}
WrapperPlayClientEntityAction action = new WrapperPlayClientEntityAction(event); WrapperPlayClientEntityAction action = new WrapperPlayClientEntityAction(event);
Pair<ActiveModel, Mount> seat = GeyserModelEngine.getInstance().getDrivers().get(event.getPlayer()); Pair<ActiveModel, Mount> seat = plugin.getModelManager().getDriversCache().get(player.getUniqueId());
if (seat != null) {
if (action.getAction() == WrapperPlayClientEntityAction.Action.START_SNEAKING) { if (seat == null) return;
ModelEngineAPI.getMountPairManager().tryDismount(event.getPlayer()); if (action.getAction() != WrapperPlayClientEntityAction.Action.START_SNEAKING) return;
}
} ModelEngineAPI.getMountPairManager().tryDismount(event.getPlayer());
} }
} }

View File

@@ -0,0 +1,38 @@
package re.imc.geysermodelengine.managers;
import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.configuration.file.YamlConfiguration;
import re.imc.geysermodelengine.GeyserModelEngine;
import java.io.File;
public class ConfigManager {
private final GeyserModelEngine plugin;
private FileConfiguration config, lang;
public ConfigManager(GeyserModelEngine plugin) {
this.plugin = plugin;
load();
}
public void load() {
if (!plugin.getDataFolder().exists()) {
plugin.getDataFolder().mkdir();
plugin.saveResource("config.yml", false);
plugin.saveResource("Lang/messages.yml", false);
}
this.config = YamlConfiguration.loadConfiguration(new File(plugin.getDataFolder(), "config.yml"));
this.lang = YamlConfiguration.loadConfiguration(new File(plugin.getDataFolder(), "Lang/messages.yml"));
}
public FileConfiguration getConfig() {
return config;
}
public FileConfiguration getLang() {
return lang;
}
}

View File

@@ -0,0 +1,36 @@
package re.imc.geysermodelengine.managers.commands;
import org.reflections.Reflections;
import re.imc.geysermodelengine.GeyserModelEngine;
import java.lang.reflect.InvocationTargetException;
import java.util.HashMap;
public class CommandManager {
private final GeyserModelEngine plugin;
private final HashMap<String, CommandManagers> commandManagersCache = new HashMap<>();
public CommandManager(GeyserModelEngine plugin) {
this.plugin = plugin;
load("re.imc.geysermodelengine.managers.commands.managers");
}
private void load(String path) {
for (Class<?> clazz : new Reflections(path).getSubTypesOf(CommandManagers.class)) {
try {
CommandManagers commandManager = (CommandManagers) clazz.getDeclaredConstructor(GeyserModelEngine.class).newInstance(plugin);
plugin.getLogger().warning("Loading Command Manager - " + commandManager.name());
commandManagersCache.put(commandManager.name(), commandManager);
} catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException err) {
plugin.getLogger().severe("Failed to load Command Manager " + clazz.getName());
throw new RuntimeException(err);
}
}
}
public HashMap<String, CommandManagers> getCommandManagersCache() {
return commandManagersCache;
}
}

View File

@@ -0,0 +1,12 @@
package re.imc.geysermodelengine.managers.commands;
import re.imc.geysermodelengine.managers.commands.subcommands.SubCommands;
import java.util.ArrayList;
public interface CommandManagers {
String name();
ArrayList<SubCommands> getCommands();
}

View File

@@ -0,0 +1,38 @@
package re.imc.geysermodelengine.managers.commands.managers.geysermodelengine;
import dev.jorel.commandapi.CommandAPICommand;
import re.imc.geysermodelengine.GeyserModelEngine;
import re.imc.geysermodelengine.commands.geysermodelenginecommands.GeyserModelEngineReloadCommand;
import re.imc.geysermodelengine.managers.commands.CommandManagers;
import re.imc.geysermodelengine.managers.commands.subcommands.SubCommands;
import java.util.ArrayList;
public class GeyserModelEngineCommandManager implements CommandManagers {
private final ArrayList<SubCommands> commands = new ArrayList<>();
public GeyserModelEngineCommandManager(GeyserModelEngine plugin) {
commands.add(new GeyserModelEngineReloadCommand(plugin));
registerCommand();
}
private void registerCommand() {
CommandAPICommand geyserModelEngineCommand = new CommandAPICommand(name());
commands.forEach(subCommands -> geyserModelEngineCommand.withSubcommand(subCommands.onCommand()));
geyserModelEngineCommand.register();
}
@Override
public String name() {
return "geysermodelengine";
}
@Override
public ArrayList<SubCommands> getCommands() {
return commands;
}
}

View File

@@ -0,0 +1,7 @@
package re.imc.geysermodelengine.managers.commands.subcommands;
import dev.jorel.commandapi.CommandAPICommand;
public interface SubCommands {
CommandAPICommand onCommand();
}

View File

@@ -0,0 +1,156 @@
package re.imc.geysermodelengine.managers.model;
import com.ticxo.modelengine.api.animation.BlueprintAnimation;
import com.ticxo.modelengine.api.generator.blueprint.BlueprintBone;
import com.ticxo.modelengine.api.model.ActiveModel;
import com.ticxo.modelengine.api.model.render.DisplayRenderer;
import me.zimzaza4.geyserutils.spigot.api.EntityUtils;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.entity.Player;
import org.geysermc.floodgate.api.FloodgateApi;
import org.joml.Vector3fc;
import re.imc.geysermodelengine.GeyserModelEngine;
import re.imc.geysermodelengine.managers.model.data.ModelEntityData;
import re.imc.geysermodelengine.packet.entity.PacketEntity;
import re.imc.geysermodelengine.runnables.EntityTaskRunnable;
import java.awt.*;
import java.lang.reflect.Method;
import java.util.*;
public class EntityTaskManager {
private final GeyserModelEngine plugin;
private final Method scaleMethod;
public EntityTaskManager(GeyserModelEngine plugin) {
this.plugin = plugin;
try {
this.scaleMethod = ActiveModel.class.getMethod("getScale");
} catch (NoSuchMethodException err) {
throw new RuntimeException(err);
}
}
public String unstripName(BlueprintBone bone) {
String name = bone.getName();
if (bone.getBehaviors().get("head") != null) {
if (!bone.getBehaviors().get("head").isEmpty()) return "hi_" + name;
return "h_" + name;
}
return name;
}
public void sendScale(ModelEntityData model, Collection<Player> players, float lastScale, boolean firstSend) {
try {
if (players.isEmpty()) return;
Vector3fc scale = (Vector3fc) scaleMethod.invoke(model.getActiveModel());
float average = (scale.x() + scale.y() + scale.z()) / 3;
if (!firstSend) {
if (average == lastScale) return;
}
for (Player player : players) {
EntityUtils.sendCustomScale(player, model.getEntity().getEntityId(), average);
}
} catch (Throwable t) {
// ignore
}
}
public void sendColor(ModelEntityData model, Collection<Player> players, Color lastColor, boolean firstSend) {
if (players.isEmpty()) return;
Color color = new Color(model.getActiveModel().getDefaultTint().asARGB());
if (model.getActiveModel().isMarkedHurt()) color = new Color(model.getActiveModel().getDamageTint().asARGB());
if (firstSend) {
if (color.equals(lastColor)) return;
}
for (Player player : players) {
EntityUtils.sendCustomColor(player, model.getEntity().getEntityId(), color);
}
}
public void checkViewers(ModelEntityData model, Set<Player> viewers) {
for (Player onlinePlayer : Bukkit.getOnlinePlayers()) {
if (FloodgateApi.getInstance().isFloodgatePlayer(onlinePlayer.getUniqueId())) {
if (canSee(onlinePlayer, model.getEntity())) {
if (!viewers.contains(onlinePlayer)) {
sendSpawnPacket(model, onlinePlayer);
viewers.add(onlinePlayer);
}
} else {
if (viewers.contains(onlinePlayer)) {
model.getEntity().sendEntityDestroyPacket(Collections.singletonList(onlinePlayer));
viewers.remove(onlinePlayer);
}
}
}
}
}
private void sendSpawnPacket(ModelEntityData model, Player onlinePlayer) {
EntityTaskRunnable task = model.getEntityTask();
boolean firstJoined = !plugin.getModelManager().getPlayerJoinedCache().contains(onlinePlayer.getUniqueId());
if (firstJoined) {
task.sendEntityData(model, onlinePlayer, plugin.getConfigManager().getConfig().getInt("join-send-delay") / 50);
} else {
task.sendEntityData(model, onlinePlayer, 5);
}
}
public boolean canSee(Player player, PacketEntity entity) {
if (!player.isOnline()) return false;
if (!plugin.getModelManager().getPlayerJoinedCache().contains(player.getUniqueId())) return false;
Location playerLocation = player.getLocation().clone();
Location entityLocation = entity.getLocation().clone();
playerLocation.setY(0);
entityLocation.setY(0);
if (playerLocation.getWorld() != entityLocation.getWorld()) return false;
if (playerLocation.distanceSquared(entityLocation) > player.getSendViewDistance() * player.getSendViewDistance() * 48) return false;
return true;
}
public void sendHitBoxToAll(ModelEntityData model) {
for (Player viewer : model.getViewers()) {
EntityUtils.sendCustomHitBox(viewer, model.getEntity().getEntityId(), 0.01f, 0.01f);
}
}
public void sendHitBox(ModelEntityData model, Player viewer) {
float w = 0;
if (model.getActiveModel().isShadowVisible()) {
if (model.getActiveModel().getModelRenderer() instanceof DisplayRenderer displayRenderer) {
// w = displayRenderer.getHitbox().getShadowRadius().get();
}
}
EntityUtils.sendCustomHitBox(viewer, model.getEntity().getEntityId(), 0.02f, w);
}
public boolean hasAnimation(ModelEntityData model, String animation) {
ActiveModel activeModel = model.getActiveModel();
BlueprintAnimation animationProperty = activeModel.getBlueprint().getAnimations().get(animation);
return !(animationProperty == null);
}
public Method getScaleMethod() {
return scaleMethod;
}
}

View File

@@ -0,0 +1,70 @@
package re.imc.geysermodelengine.managers.model;
import com.ticxo.modelengine.api.ModelEngineAPI;
import com.ticxo.modelengine.api.model.ActiveModel;
import com.ticxo.modelengine.api.model.ModeledEntity;
import com.ticxo.modelengine.api.model.bone.type.Mount;
import org.apache.commons.lang3.tuple.Pair;
import org.bukkit.entity.Entity;
import re.imc.geysermodelengine.GeyserModelEngine;
import re.imc.geysermodelengine.managers.model.data.ModelEntityData;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
public class ModelManager {
private final GeyserModelEngine plugin;
private final HashSet<UUID> playerJoinedCache = new HashSet<>();
private final ConcurrentHashMap<Integer, Map<ActiveModel, ModelEntityData>> entitiesCache = new ConcurrentHashMap<>();
private final ConcurrentHashMap<Integer, ModelEntityData> modelEntitiesCache = new ConcurrentHashMap<>();
private final ConcurrentHashMap<UUID, Pair<ActiveModel, Mount>> driversCache = new ConcurrentHashMap<>();
public ModelManager(GeyserModelEngine plugin) {
this.plugin = plugin;
}
public void create(ModeledEntity entity, ActiveModel model) {
ModelEntityData modelEntity = new ModelEntityData(plugin, entity, model);
int id = entity.getBase().getEntityId();
Map<ActiveModel, ModelEntityData> map = entitiesCache.computeIfAbsent(id, k -> new HashMap<>());
for (Map.Entry<ActiveModel, ModelEntityData> entry : map.entrySet()) {
if (entry.getKey() != model && entry.getKey().getBlueprint().getName().equals(model.getBlueprint().getName())) {
return;
}
}
map.put(model, modelEntity);
}
public void processEntities(Entity entity) {
if (entitiesCache.containsKey(entity.getEntityId())) return;
ModeledEntity modeledEntity = ModelEngineAPI.getModeledEntity(entity);
if (modeledEntity == null) return;
Optional<ActiveModel> model = modeledEntity.getModels().values().stream().findFirst();
model.ifPresent(m -> create(modeledEntity, m));
}
public HashSet<UUID> getPlayerJoinedCache() {
return playerJoinedCache;
}
public ConcurrentHashMap<Integer, Map<ActiveModel, ModelEntityData>> getEntitiesCache() {
return entitiesCache;
}
public ConcurrentHashMap<Integer, ModelEntityData> getModelEntitiesCache() {
return modelEntitiesCache;
}
public ConcurrentHashMap<UUID, Pair<ActiveModel, Mount>> getDriversCache() {
return driversCache;
}
}

View File

@@ -0,0 +1,75 @@
package re.imc.geysermodelengine.managers.model.data;
import com.github.retrooper.packetevents.protocol.entity.type.EntityTypes;
import com.google.common.collect.Sets;
import com.ticxo.modelengine.api.model.ActiveModel;
import com.ticxo.modelengine.api.model.ModeledEntity;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.entity.Player;
import re.imc.geysermodelengine.GeyserModelEngine;
import re.imc.geysermodelengine.packet.entity.PacketEntity;
import re.imc.geysermodelengine.runnables.EntityTaskRunnable;
import java.util.Set;
import java.util.concurrent.TimeUnit;
public class ModelEntityData {
private final GeyserModelEngine plugin;
private PacketEntity entity;
private final Set<Player> viewers = Sets.newConcurrentHashSet();
private final ModeledEntity modeledEntity;
private final ActiveModel activeModel;
private EntityTaskRunnable entityTask;
public ModelEntityData(GeyserModelEngine plugin, ModeledEntity modeledEntity, ActiveModel model) {
this.plugin = plugin;
this.modeledEntity = modeledEntity;
this.activeModel = model;
this.entity = spawnEntity();
runEntityTask();
}
public void teleportToModel() {
Location location = modeledEntity.getBase().getLocation();
entity.teleport(location);
}
public PacketEntity spawnEntity() {
entity = new PacketEntity(EntityTypes.PIG, viewers, modeledEntity.getBase().getLocation());
return entity;
}
public void runEntityTask() {
entityTask = new EntityTaskRunnable(plugin, this);
Bukkit.getAsyncScheduler().runAtFixedRate(plugin, entityTask, 0, 20, TimeUnit.MILLISECONDS);
}
public PacketEntity getEntity() {
return entity;
}
public Set<Player> getViewers() {
return viewers;
}
public ModeledEntity getModeledEntity() {
return modeledEntity;
}
public ActiveModel getActiveModel() {
return activeModel;
}
public EntityTaskRunnable getEntityTask() {
return entityTask;
}
}

View File

@@ -1,68 +0,0 @@
package re.imc.geysermodelengine.model;
import com.ticxo.modelengine.api.ModelEngineAPI;
import com.ticxo.modelengine.api.entity.BukkitEntity;
import com.ticxo.modelengine.api.model.ActiveModel;
import com.ticxo.modelengine.api.model.bone.type.Mount;
import com.ticxo.modelengine.api.mount.controller.MountController;
import org.apache.commons.lang3.tuple.Pair;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.scheduler.BukkitRunnable;
import org.geysermc.floodgate.api.FloodgateApi;
import re.imc.geysermodelengine.GeyserModelEngine;
public class BedrockMountControl {
public static void startTask() {
new BukkitRunnable() {
@Override
public void run() {
for (Player player : Bukkit.getOnlinePlayers()) {
if (!FloodgateApi.getInstance().isFloodgatePlayer(player.getUniqueId())) {
continue;
}
float pitch = player.getLocation().getPitch();
Pair<ActiveModel, Mount> seat = GeyserModelEngine.getInstance().getDrivers().get(player);
if (seat != null) {
if (pitch < -30) {
MountController controller = ModelEngineAPI.getMountPairManager()
.getController(player.getUniqueId());
if (controller != null) {
MountController.MountInput input = controller.getInput();
if (input != null) {
input.setJump(true);
controller.setInput(input);
}
}
}
if (pitch > 80) {
if (seat.getKey().getModeledEntity().getBase() instanceof BukkitEntity bukkitEntity) {
if (bukkitEntity.getOriginal().isOnGround()) {
return;
}
}
MountController controller = ModelEngineAPI.getMountPairManager()
.getController(player.getUniqueId());
if (controller != null) {
MountController.MountInput input = controller.getInput();
if (input != null) {
input.setSneak(true);
controller.setInput(input);
}
}
}
}
}
}
}.runTaskTimerAsynchronously(GeyserModelEngine.getInstance(), 1, 1);
}
}

View File

@@ -1,406 +0,0 @@
package re.imc.geysermodelengine.model;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import com.ticxo.modelengine.api.animation.BlueprintAnimation;
import com.ticxo.modelengine.api.animation.handler.AnimationHandler;
import com.ticxo.modelengine.api.generator.blueprint.BlueprintBone;
import com.ticxo.modelengine.api.model.ActiveModel;
import com.ticxo.modelengine.api.model.ModeledEntity;
import com.ticxo.modelengine.api.model.bone.ModelBone;
import com.ticxo.modelengine.api.model.render.DisplayRenderer;
import lombok.Getter;
import lombok.Setter;
import me.zimzaza4.geyserutils.spigot.api.EntityUtils;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.entity.Player;
import org.geysermc.floodgate.api.FloodgateApi;
import org.joml.Vector3f;
import org.joml.Vector3fc;
import re.imc.geysermodelengine.GeyserModelEngine;
import re.imc.geysermodelengine.packet.entity.PacketEntity;
import re.imc.geysermodelengine.util.BooleanPacker;
import java.awt.*;
import java.lang.reflect.Method;
import java.util.*;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import static re.imc.geysermodelengine.model.ModelEntity.ENTITIES;
import static re.imc.geysermodelengine.model.ModelEntity.MODEL_ENTITIES;
@Getter
@Setter
public class EntityTask {
public static final Method GET_SCALE;
static {
try {
GET_SCALE = ActiveModel.class.getMethod("getScale");
} catch (NoSuchMethodException e) {
throw new RuntimeException(e);
}
}
ModelEntity model;
int tick = 0;
int syncTick = 0;
boolean removed = false;
float lastScale = -1.0f;
Color lastColor = null;
Map<String, Integer> lastIntSet = new ConcurrentHashMap<>();
Cache<String, Boolean> lastPlayedAnim = CacheBuilder.newBuilder()
.expireAfterWrite(30, TimeUnit.MILLISECONDS).build();
private ScheduledFuture scheduledFuture;
public EntityTask(ModelEntity model) {
this.model = model;
}
public void runAsync() {
PacketEntity entity = model.getEntity();
if (entity.isDead()) {
return;
}
PacketEntity packetEntity = model.getEntity();
// packetEntity.setHeadYaw((float) Math.toDegrees(model.getModeledEntity().getYHeadRot()));
// packetEntity.setHeadPitch((float) Math.toDegrees(model.getModeledEntity().getXHeadRot()));
model.teleportToModel();
Set<Player> viewers = model.getViewers();
ActiveModel activeModel = model.getActiveModel();
ModeledEntity modeledEntity = model.getModeledEntity();
if (activeModel.isDestroyed() || activeModel.isRemoved()) {
removed = true;
entity.remove();
ENTITIES.remove(modeledEntity.getBase().getEntityId());
MODEL_ENTITIES.remove(entity.getEntityId());
cancel();
return;
}
if (tick % 5 == 0) {
if (tick % 40 == 0) {
for (Player viewer : Set.copyOf(viewers)) {
if (!canSee(viewer, model.getEntity())) {
viewers.remove(viewer);
}
}
}
}
tick ++;
if (tick > 400) {
tick = 0;
sendHitBoxToAll();
}
// Optional<Player> player = viewers.stream().findAny();
// if (player.isEmpty()) return
if (viewers.isEmpty()) {
return;
}
// updateEntityProperties(viewers, false);
// do not actually use this, atleast bundle these up ;(
sendScale(viewers, false);
sendColor(viewers, false);
}
public void checkViewers(Set<Player> viewers) {
for (Player onlinePlayer : Bukkit.getOnlinePlayers()) {
if (FloodgateApi.getInstance().isFloodgatePlayer(onlinePlayer.getUniqueId())) {
if (canSee(onlinePlayer, model.getEntity())) {
if (!viewers.contains(onlinePlayer)) {
sendSpawnPacket(onlinePlayer);
viewers.add(onlinePlayer);
}
} else {
if (viewers.contains(onlinePlayer)) {
model.getEntity().sendEntityDestroyPacket(Collections.singletonList(onlinePlayer));
viewers.remove(onlinePlayer);
}
}
}
}
}
private void sendSpawnPacket(Player onlinePlayer) {
EntityTask task = model.getTask();
boolean firstJoined = !GeyserModelEngine.getInstance().getJoinedPlayers().contains(onlinePlayer);
if (firstJoined) {
task.sendEntityData(onlinePlayer, GeyserModelEngine.getInstance().getJoinSendDelay() / 50);
} else {
task.sendEntityData(onlinePlayer, 5);
}
}
public void sendEntityData(Player player, int delay) {
EntityUtils.setCustomEntity(player, model.getEntity().getEntityId(), "modelengine:" + model.getActiveModel().getBlueprint().getName().toLowerCase());
GeyserModelEngine.getInstance().getScheduler().schedule(() -> {
model.getEntity().sendSpawnPacket(Collections.singletonList(player));
GeyserModelEngine.getInstance().getScheduler().schedule(() -> {
sendHitBox(player);
sendScale(Collections.singleton(player), true);
sendColor(Collections.singleton(player), true);
updateEntityProperties(Collections.singleton(player), true);
}, 500, TimeUnit.MILLISECONDS);
}, delay * 50L, TimeUnit.MILLISECONDS);
}
public void sendScale(Collection<Player> players, boolean firstSend) {
try {
if (players.isEmpty()) {
return;
}
Vector3fc scale = (Vector3fc) GET_SCALE.invoke(model.getActiveModel());
float average = (scale.x() + scale.y() + scale.z()) / 3;
if (!firstSend) {
if (average == lastScale) return;
}
for (Player player : players) {
EntityUtils.sendCustomScale(player, model.getEntity().getEntityId(), average);
}
lastScale = average;
} catch (Throwable t) {
// ignore
}
}
public void sendColor(Collection<Player> players, boolean firstSend) {
if (players.isEmpty()) return;
Color color = new Color(model.getActiveModel().getDefaultTint().asARGB());
if (model.getActiveModel().isMarkedHurt()) {
color = new Color(model.getActiveModel().getDamageTint().asARGB());
}
if (firstSend) {
if (color.equals(lastColor)) return;
}
for (Player player : players) {
EntityUtils.sendCustomColor(player, model.getEntity().getEntityId(), color);
}
lastColor = color;
}
public void updateEntityProperties(Collection<Player> players, boolean firstSend, String... forceAnims) {
int entity = model.getEntity().getEntityId();
Set<String> forceAnimSet = Set.of(forceAnims);
Map<String, Boolean> boneUpdates = new HashMap<>();
Map<String, Boolean> animUpdates = new HashMap<>();
Set<String> anims = new HashSet<>();
// if (GeyserModelEngine.getInstance().getEnablePartVisibilityModels().contains(model.getActiveModel().getBlueprint().getName())) {
model.getActiveModel().getBlueprint().getBones().forEach((s, bone) -> {
processBone(bone, boneUpdates);
});
// }
AnimationHandler handler = model.getActiveModel().getAnimationHandler();
Set<String> priority = model.getActiveModel().getBlueprint().getAnimationDescendingPriority();
for (String animId : priority) {
if (handler.isPlayingAnimation(animId)) {
BlueprintAnimation anim = model.getActiveModel().getBlueprint().getAnimations().get(animId);
anims.add(animId);
if (anim.isOverride() && anim.getLoopMode() == BlueprintAnimation.LoopMode.ONCE) {
break;
}
}
}
for (String id : priority) {
if (anims.contains(id)) {
animUpdates.put(id, true);
} else {
animUpdates.put(id, false);
}
}
Set<String> lastPlayed = new HashSet<>(lastPlayedAnim.asMap().keySet());
for (Map.Entry<String, Boolean> anim : animUpdates.entrySet()) {
if (anim.getValue()) {
lastPlayedAnim.put(anim.getKey(), true);
}
}
for (String anim : lastPlayed) {
animUpdates.put(anim, true);
}
if (boneUpdates.isEmpty() && animUpdates.isEmpty()) return;
Map<String, Integer> intUpdates = new HashMap<>();
int i = 0;
for (Integer integer : BooleanPacker.mapBooleansToInts(boneUpdates)) {
intUpdates.put("modelengine:bone" + i, integer);
i++;
}
i = 0;
for (Integer integer : BooleanPacker.mapBooleansToInts(animUpdates)) {
intUpdates.put("modelengine:anim" + i, integer);
i++;
}
if (!firstSend) {
if (intUpdates.equals(lastIntSet)) {
return;
} else {
lastIntSet.clear();
lastIntSet.putAll(intUpdates);
}
}
// System.out.println("AN: " + animUpdates.size() + ", BO:" + boneUpdates.size());
if (GeyserModelEngine.getInstance().isDebug()) {
GeyserModelEngine.getInstance().getLogger().info(animUpdates.toString());
}
List<String> list = new ArrayList<>(boneUpdates.keySet());
Collections.sort(list);
for (Player player : players) {
EntityUtils.sendIntProperties(player, entity, intUpdates);
}
}
private void processBone(BlueprintBone bone, Map<String, Boolean> map) {
String name = unstripName(bone).toLowerCase();
if (name.equals("hitbox") ||
name.equals("shadow") ||
name.equals("mount") ||
name.startsWith("p_") ||
name.startsWith("b_") ||
name.startsWith("ob_")) {
return;
}
for (BlueprintBone blueprintBone : bone.getChildren().values()) {
processBone(blueprintBone, map);
}
ModelBone activeBone = model.getActiveModel().getBones().get(bone.getName());
boolean visible = false;
if (activeBone != null) {
visible = activeBone.isVisible();
}
map.put(name, visible);
}
private String unstripName(BlueprintBone bone) {
String name = bone.getName();
if (bone.getBehaviors().get("head") != null) {
if (!bone.getBehaviors().get("head").isEmpty()) return "hi_" + name;
return "h_" + name;
}
return name;
}
public void sendHitBoxToAll() {
for (Player viewer : model.getViewers()) {
EntityUtils.sendCustomHitBox(viewer, model.getEntity().getEntityId(), 0.01f, 0.01f);
}
}
public void sendHitBox(Player viewer) {
float w = 0;
if (model.getActiveModel().isShadowVisible()) {
if (model.getActiveModel().getModelRenderer() instanceof DisplayRenderer displayRenderer) {
// w = displayRenderer.getHitbox().getShadowRadius().get();
}
}
EntityUtils.sendCustomHitBox(viewer, model.getEntity().getEntityId(), 0.02f, w);
}
public boolean hasAnimation(String animation) {
ActiveModel activeModel = model.getActiveModel();
BlueprintAnimation animationProperty = activeModel.getBlueprint().getAnimations().get(animation);
return !(animationProperty == null);
}
private boolean canSee(Player player, PacketEntity entity) {
if (!player.isOnline()) {
return false;
}
if (!GeyserModelEngine.getInstance().getJoinedPlayers().contains(player)) {
return false;
}
Location playerLocation = player.getLocation().clone();
Location entityLocation = entity.getLocation().clone();
playerLocation.setY(0);
entityLocation.setY(0);
if (playerLocation.getWorld() != entityLocation.getWorld()) {
return false;
}
if (playerLocation.distanceSquared(entityLocation) > player.getSendViewDistance() * player.getSendViewDistance() * 48) {
return false;
}
return true;
/*
if (entity.getLocation().getChunk() == player.getChunk()) {
return true;
}
if (entity.getLocation().getWorld() != player.getWorld()) {
return false;
}
if (player.getLocation().distanceSquared(entity.getLocation()) > player.getSimulationDistance() * player.getSimulationDistance() * 256) {
return false;
}
if (player.getLocation().distance(entity.getLocation()) > model.getActiveModel().getModeledEntity().getBase().getRenderRadius()) {
return false;
}
return true;
*/
}
public void cancel() {
// syncTask.cancel();
scheduledFuture.cancel(true);
}
public void run(GeyserModelEngine instance) {
sendHitBoxToAll();
Runnable asyncTask = () -> {
try {
checkViewers(model.getViewers());
runAsync();
} catch (Throwable t) {
}
};
scheduledFuture = GeyserModelEngine.getInstance().getScheduler().scheduleAtFixedRate(asyncTask, 0, 20, TimeUnit.MILLISECONDS);
//asyncTask.runTaskTimerAsynchronously(instance, 0, 0);
}
}

View File

@@ -1,70 +0,0 @@
package re.imc.geysermodelengine.model;
import com.github.retrooper.packetevents.protocol.entity.type.EntityTypes;
import com.google.common.collect.Sets;
import com.ticxo.modelengine.api.model.ActiveModel;
import com.ticxo.modelengine.api.model.ModeledEntity;
import lombok.Getter;
import org.bukkit.Location;
import org.bukkit.entity.Player;
import re.imc.geysermodelengine.GeyserModelEngine;
import re.imc.geysermodelengine.packet.entity.PacketEntity;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
@Getter
public class ModelEntity {
public static Map<Integer, Map<ActiveModel, ModelEntity>> ENTITIES = new ConcurrentHashMap<>();
public static Map<Integer, ModelEntity> MODEL_ENTITIES = new ConcurrentHashMap<>();
private PacketEntity entity;
private final Set<Player> viewers = Sets.newConcurrentHashSet();
private final ModeledEntity modeledEntity;
private final ActiveModel activeModel;
private EntityTask task;
private ModelEntity(ModeledEntity modeledEntity, ActiveModel model) {
this.modeledEntity = modeledEntity;
this.activeModel = model;
this.entity = spawnEntity();
runEntityTask();
}
public void teleportToModel() {
Location location = modeledEntity.getBase().getLocation();
entity.teleport(location);
}
public static ModelEntity create(ModeledEntity entity, ActiveModel model) {
ModelEntity modelEntity = new ModelEntity(entity, model);
int id = entity.getBase().getEntityId();
Map<ActiveModel, ModelEntity> map = ENTITIES.computeIfAbsent(id, k -> new HashMap<>());
for (Map.Entry<ActiveModel, ModelEntity> entry : map.entrySet()) {
if (entry.getKey() != model && entry.getKey().getBlueprint().getName().equals(model.getBlueprint().getName())) {
return null;
}
}
map.put(model, modelEntity);
return modelEntity;
}
public PacketEntity spawnEntity() {
entity = new PacketEntity(EntityTypes.PIG, viewers, modeledEntity.getBase().getLocation());
return entity;
}
public void runEntityTask() {
task = new EntityTask(this);
task.run(GeyserModelEngine.getInstance());
}
}

View File

@@ -27,15 +27,6 @@ import java.util.concurrent.ThreadLocalRandom;
@Setter @Setter
public class PacketEntity { public class PacketEntity {
// public static final MinecraftVersion V1_20_5 = new MinecraftVersion("1.20.5");
public PacketEntity(EntityType type, Set<Player> viewers, Location location) {
this.id = ThreadLocalRandom.current().nextInt(300000000, 400000000);
this.uuid = UUID.randomUUID();
this.type = type;
this.viewers = viewers;
this.location = location;
}
private int id; private int id;
private UUID uuid; private UUID uuid;
private EntityType type; private EntityType type;
@@ -45,6 +36,15 @@ public class PacketEntity {
private float headPitch; private float headPitch;
private boolean removed = false; private boolean removed = false;
public PacketEntity(EntityType type, Set<Player> viewers, Location location) {
this.id = ThreadLocalRandom.current().nextInt(300000000, 400000000);
this.uuid = UUID.randomUUID();
this.type = type;
this.viewers = viewers;
this.location = location;
}
public @NotNull Location getLocation() { public @NotNull Location getLocation() {
return location; return location;
} }
@@ -52,10 +52,9 @@ public class PacketEntity {
public boolean teleport(@NotNull Location location) { public boolean teleport(@NotNull Location location) {
boolean sent = this.location.getWorld() != location.getWorld() || this.location.distanceSquared(location) > 0.000001; boolean sent = this.location.getWorld() != location.getWorld() || this.location.distanceSquared(location) > 0.000001;
this.location = location.clone(); this.location = location.clone();
if (sent) {
sendLocationPacket(viewers); if (sent) sendLocationPacket(viewers);
// sendHeadRotation(viewers); // TODO
}
return true; return true;
} }
@@ -74,8 +73,6 @@ public class PacketEntity {
} }
public void sendSpawnPacket(Collection<Player> players) { public void sendSpawnPacket(Collection<Player> players) {
// EntitySpawnPacket packet = new EntitySpawnPacket(id, uuid, type, location);
// EntityMetadataPacket metadataPacket = new EntityMetadataPacket(id);
WrapperPlayServerSpawnEntity spawnEntity = new WrapperPlayServerSpawnEntity(id, uuid, type, SpigotConversionUtil.fromBukkitLocation(location), location.getYaw(), 0, null); WrapperPlayServerSpawnEntity spawnEntity = new WrapperPlayServerSpawnEntity(id, uuid, type, SpigotConversionUtil.fromBukkitLocation(location), location.getYaw(), 0, null);
players.forEach(player -> PacketEvents.getAPI().getPlayerManager().sendPacket(player, spawnEntity)); players.forEach(player -> PacketEvents.getAPI().getPlayerManager().sendPacket(player, spawnEntity));
} }
@@ -84,13 +81,14 @@ public class PacketEntity {
PacketWrapper<?> packet; PacketWrapper<?> packet;
EntityPositionData data = new EntityPositionData(SpigotConversionUtil.fromBukkitLocation(location).getPosition(), Vector3d.zero(), location.getYaw(), location.getPitch()); EntityPositionData data = new EntityPositionData(SpigotConversionUtil.fromBukkitLocation(location).getPosition(), Vector3d.zero(), location.getYaw(), location.getPitch());
if (PacketEvents.getAPI().getServerManager().getVersion().isNewerThanOrEquals(ServerVersion.V_1_21_2)) { if (PacketEvents.getAPI().getServerManager().getVersion().isNewerThanOrEquals(ServerVersion.V_1_21_2)) {
packet = new WrapperPlayServerEntityPositionSync(id, data, false); packet = new WrapperPlayServerEntityPositionSync(id, data, false);
} else { } else {
packet = new WrapperPlayServerEntityTeleport(id, data, RelativeFlag.NONE,false); packet = new WrapperPlayServerEntityTeleport(id, data, RelativeFlag.NONE,false);
} }
players.forEach(player -> PacketEvents.getAPI().getPlayerManager().sendPacket(player, packet));
players.forEach(player -> PacketEvents.getAPI().getPlayerManager().sendPacket(player, packet));
} }
public void sendHeadRotation(Collection<Player> players) { public void sendHeadRotation(Collection<Player> players) {
@@ -106,84 +104,5 @@ public class PacketEntity {
public int getEntityId() { public int getEntityId() {
return id; return id;
} }
/*
public boolean teleport(@NotNull Location location) {
this.location = location.clone();
sendLocationPacket(viewers);
return true;
}
public void remove() {
removed = true;
sendEntityDestroyPacket(viewers);
}
public boolean isDead() {
return removed;
}
public boolean isValid() {
return !removed;
}
public void sendSpawnPacket(Collection<Player> players) {
EntitySpawnPacket packet = new EntitySpawnPacket(id, uuid, type, location);
// EntityMetadataPacket metadataPacket = new EntityMetadataPacket(id);
players.forEach(player -> {
ProtocolLibrary.getProtocolManager().sendServerPacket(player, packet.encode());
});
sendAllEquipmentPacket(players);
// players.forEach(player -> ProtocolLibrary.getProtocolManager().sendServerPacket(player, metadataPacket.encode()));
}
public void sendAllEquipmentPacket(Collection<Player> players) {
for (Map.Entry<EnumWrappers.ItemSlot, ItemStack> e : equipment.entrySet()) {
EntityEquipmentPacket packet = new EntityEquipmentPacket(id, e.getKey(), e.getValue());
players.forEach(player -> {
ProtocolLibrary.getProtocolManager().sendServerPacket(player, packet.encode());
});
}
}
public void sendLocationPacket(Collection<Player> players) {
WrapperPacket packet = MinecraftVersion.v1_21_2.atOrAbove() ? new EntityPositionSyncPacket(id, location) : new EntityTeleportPacket(id, location);
players.forEach(player -> ProtocolLibrary.getProtocolManager().sendServerPacket(player, packet.encode()));
}
public void sendHurtPacket(Collection<Player> players) {
// 1.21 error
if (MinecraftVersion.getCurrentVersion().compareTo(V1_20_5) < 0) {
}
}
EntityHurtPacket packet = new EntityHurtPacket(id);
players.forEach(player -> ProtocolLibrary.getProtocolManager().sendServerPacket(player, packet.encode()));
public void sendEntityDestroyPacket(Collection<Player> players) {
EntityDestroyPacket packet = new EntityDestroyPacket(id);
players.forEach(player -> ProtocolLibrary.getProtocolManager().sendServerPacket(player, packet.encode()));
}
public int getEntityId() {
return id;
}
public void setSlot(EnumWrappers.ItemSlot slot, ItemStack itemStack) {
if (itemStack == null) {
itemStack = new ItemStack(Material.AIR);
}
equipment.put(slot, itemStack);
EntityEquipmentPacket packet = new EntityEquipmentPacket(id, slot, itemStack);
viewers.forEach(player -> {
ProtocolLibrary.getProtocolManager().sendServerPacket(player, packet.encode());
});
}
*/
} }

View File

@@ -0,0 +1,65 @@
package re.imc.geysermodelengine.runnables;
import com.ticxo.modelengine.api.ModelEngineAPI;
import com.ticxo.modelengine.api.entity.BukkitEntity;
import com.ticxo.modelengine.api.model.ActiveModel;
import com.ticxo.modelengine.api.model.bone.type.Mount;
import com.ticxo.modelengine.api.mount.controller.MountController;
import io.papermc.paper.threadedregions.scheduler.ScheduledTask;
import org.apache.commons.lang3.tuple.Pair;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.geysermc.floodgate.api.FloodgateApi;
import re.imc.geysermodelengine.GeyserModelEngine;
import java.util.function.Consumer;
public class BedrockMountControlRunnable implements Consumer<ScheduledTask> {
private final GeyserModelEngine plugin;
public BedrockMountControlRunnable(GeyserModelEngine plugin) {
this.plugin = plugin;
}
@Override
public void accept(ScheduledTask scheduledTask) {
for (Player player : Bukkit.getOnlinePlayers()) {
if (!FloodgateApi.getInstance().isFloodgatePlayer(player.getUniqueId())) continue;
float pitch = player.getLocation().getPitch();
Pair<ActiveModel, Mount> seat = plugin.getModelManager().getDriversCache().get(player.getUniqueId());
if (seat == null) continue;
if (pitch < -30) {
MountController controller = ModelEngineAPI.getMountPairManager().getController(player.getUniqueId());
if (controller != null) {
MountController.MountInput input = controller.getInput();
if (input != null) {
input.setJump(true);
controller.setInput(input);
}
}
}
if (pitch > 80) {
if (seat.getKey().getModeledEntity().getBase() instanceof BukkitEntity bukkitEntity) {
if (bukkitEntity.getOriginal().isOnGround()) {
return;
}
}
MountController controller = ModelEngineAPI.getMountPairManager().getController(player.getUniqueId());
if (controller != null) {
MountController.MountInput input = controller.getInput();
if (input != null) {
input.setSneak(true);
controller.setInput(input);
}
}
}
}
}
}

View File

@@ -0,0 +1,258 @@
package re.imc.geysermodelengine.runnables;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import com.ticxo.modelengine.api.animation.BlueprintAnimation;
import com.ticxo.modelengine.api.animation.handler.AnimationHandler;
import com.ticxo.modelengine.api.generator.blueprint.BlueprintBone;
import com.ticxo.modelengine.api.model.ActiveModel;
import com.ticxo.modelengine.api.model.ModeledEntity;
import com.ticxo.modelengine.api.model.bone.ModelBone;
import io.papermc.paper.threadedregions.scheduler.ScheduledTask;
import me.zimzaza4.geyserutils.spigot.api.EntityUtils;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import re.imc.geysermodelengine.GeyserModelEngine;
import re.imc.geysermodelengine.managers.model.data.ModelEntityData;
import re.imc.geysermodelengine.packet.entity.PacketEntity;
import re.imc.geysermodelengine.util.BooleanPacker;
import java.awt.*;
import java.util.*;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;
public class EntityTaskRunnable implements Consumer<ScheduledTask> {
private final GeyserModelEngine plugin;
private final ModelEntityData model;
private int tick = 0;
private int syncTick = 0;
private float lastScale = -1.0f;
private Color lastColor = null;
private boolean removed = false;
private final ConcurrentHashMap<String, Integer> lastIntSet = new ConcurrentHashMap<>();
private final Cache<String, Boolean> lastPlayedAnim = CacheBuilder.newBuilder().expireAfterWrite(30, TimeUnit.MILLISECONDS).build();
private final BooleanPacker booleanPacker = new BooleanPacker();
public EntityTaskRunnable(GeyserModelEngine plugin, ModelEntityData model) {
this.plugin = plugin;
this.model = model;
plugin.getEntityTaskManager().sendHitBoxToAll(model);
}
@Override
public void accept(ScheduledTask scheduledTask) {
plugin.getEntityTaskManager().checkViewers(model, model.getViewers());
PacketEntity entity = model.getEntity();
if (entity.isDead()) return;
model.teleportToModel();
Set<Player> viewers = model.getViewers();
ActiveModel activeModel = model.getActiveModel();
ModeledEntity modeledEntity = model.getModeledEntity();
if (activeModel.isDestroyed() || activeModel.isRemoved()) {
removed = true;
entity.remove();
plugin.getModelManager().getEntitiesCache().remove(modeledEntity.getBase().getEntityId());
plugin.getModelManager().getModelEntitiesCache().remove(entity.getEntityId());
scheduledTask.cancel();
return;
}
if (tick % 5 == 0) {
if (tick % 40 == 0) {
for (Player viewer : Set.copyOf(viewers)) {
if (!plugin.getEntityTaskManager().canSee(viewer, model.getEntity())) {
viewers.remove(viewer);
}
}
}
}
tick ++;
if (tick > 400) {
tick = 0;
plugin.getEntityTaskManager().sendHitBoxToAll(model);
}
if (viewers.isEmpty()) return;
plugin.getEntityTaskManager().sendScale(model, viewers, lastScale, false);
plugin.getEntityTaskManager().sendColor(model, viewers, lastColor, false);
}
public void sendEntityData(ModelEntityData model, Player player, int delay) {
EntityUtils.setCustomEntity(player, model.getEntity().getEntityId(), "modelengine:" + model.getActiveModel().getBlueprint().getName().toLowerCase());
Bukkit.getAsyncScheduler().runDelayed(plugin, scheduledTask -> {
model.getEntity().sendSpawnPacket(Collections.singletonList(player));
Bukkit.getAsyncScheduler().runDelayed(plugin, scheduledTask1 -> {
plugin.getEntityTaskManager().sendHitBox(model, player);
plugin.getEntityTaskManager().sendScale(model, Collections.singleton(player), lastScale, true);
plugin.getEntityTaskManager().sendColor(model, Collections.singleton(player), lastColor, true);
updateEntityProperties(model, Collections.singleton(player), true);
}, 500, TimeUnit.MILLISECONDS);
}, delay * 50L, TimeUnit.MILLISECONDS);
}
public void updateEntityProperties(ModelEntityData model, Collection<Player> players, boolean firstSend, String... forceAnims) {
int entity = model.getEntity().getEntityId();
Set<String> forceAnimSet = Set.of(forceAnims);
Map<String, Boolean> boneUpdates = new HashMap<>();
Map<String, Boolean> animUpdates = new HashMap<>();
Set<String> anims = new HashSet<>();
model.getActiveModel().getBlueprint().getBones().forEach((s, bone) -> processBone(model, bone, boneUpdates));
AnimationHandler handler = model.getActiveModel().getAnimationHandler();
Set<String> priority = model.getActiveModel().getBlueprint().getAnimationDescendingPriority();
for (String animId : priority) {
if (handler.isPlayingAnimation(animId)) {
BlueprintAnimation anim = model.getActiveModel().getBlueprint().getAnimations().get(animId);
anims.add(animId);
if (anim.isOverride() && anim.getLoopMode() == BlueprintAnimation.LoopMode.ONCE) {
break;
}
}
}
for (String id : priority) {
if (anims.contains(id)) {
animUpdates.put(id, true);
} else {
animUpdates.put(id, false);
}
}
Set<String> lastPlayed = new HashSet<>(lastPlayedAnim.asMap().keySet());
for (Map.Entry<String, Boolean> anim : animUpdates.entrySet()) {
if (anim.getValue()) {
lastPlayedAnim.put(anim.getKey(), true);
}
}
for (String anim : lastPlayed) animUpdates.put(anim, true);
if (boneUpdates.isEmpty() && animUpdates.isEmpty()) return;
Map<String, Integer> intUpdates = new HashMap<>();
int i = 0;
for (Integer integer : booleanPacker.mapBooleansToInts(boneUpdates)) {
intUpdates.put("modelengine:bone" + i, integer);
i++;
}
i = 0;
for (Integer integer : booleanPacker.mapBooleansToInts(animUpdates)) {
intUpdates.put("modelengine:anim" + i, integer);
i++;
}
if (!firstSend) {
if (intUpdates.equals(lastIntSet)) {
return;
} else {
lastIntSet.clear();
lastIntSet.putAll(intUpdates);
}
}
if (plugin.getConfigManager().getConfig().getBoolean("debug")) plugin.getLogger().info(animUpdates.toString());
List<String> list = new ArrayList<>(boneUpdates.keySet());
Collections.sort(list);
for (Player player : players) {
EntityUtils.sendIntProperties(player, entity, intUpdates);
}
}
private void processBone(ModelEntityData model, BlueprintBone bone, Map<String, Boolean> map) {
String name = plugin.getEntityTaskManager().unstripName(bone).toLowerCase();
if (name.equals("hitbox") ||
name.equals("shadow") ||
name.equals("mount") ||
name.startsWith("p_") ||
name.startsWith("b_") ||
name.startsWith("ob_")) {
return;
}
for (BlueprintBone blueprintBone : bone.getChildren().values()) processBone(model, blueprintBone, map);
ModelBone activeBone = model.getActiveModel().getBones().get(bone.getName());
boolean visible = false;
if (activeBone != null) visible = activeBone.isVisible();
map.put(name, visible);
}
public void setTick(int tick) {
this.tick = tick;
}
public void setSyncTick(int syncTick) {
this.syncTick = syncTick;
}
public void setRemoved(boolean removed) {
this.removed = removed;
}
public void setLastScale(float lastScale) {
this.lastScale = lastScale;
}
public int getTick() {
return tick;
}
public int getSyncTick() {
return syncTick;
}
public void setLastColor(Color lastColor) {
this.lastColor = lastColor;
}
public float getLastScale() {
return lastScale;
}
public Color getLastColor() {
return lastColor;
}
public boolean isRemoved() {
return removed;
}
public ConcurrentHashMap<String, Integer> getLastIntSet() {
return lastIntSet;
}
public Cache<String, Boolean> getLastPlayedAnim() {
return lastPlayedAnim;
}
}

View File

@@ -0,0 +1,29 @@
package re.imc.geysermodelengine.runnables;
import com.ticxo.modelengine.api.model.ActiveModel;
import io.papermc.paper.threadedregions.scheduler.ScheduledTask;
import re.imc.geysermodelengine.GeyserModelEngine;
import re.imc.geysermodelengine.managers.model.data.ModelEntityData;
import java.util.Map;
import java.util.function.Consumer;
public class UpdateTaskRunnable implements Consumer<ScheduledTask> {
private final GeyserModelEngine plugin;
public UpdateTaskRunnable(GeyserModelEngine plugin) {
this.plugin = plugin;
}
@Override
public void accept(ScheduledTask scheduledTask) {
try {
for (Map<ActiveModel, ModelEntityData> models : plugin.getModelManager().getEntitiesCache().values()) {
models.values().forEach(model -> model.getEntityTask().updateEntityProperties(model, model.getViewers(), false));
}
} catch (Throwable err) {
throw new RuntimeException(err);
}
}
}

View File

@@ -6,25 +6,30 @@ import java.util.List;
import java.util.Map; import java.util.Map;
public class BooleanPacker { public class BooleanPacker {
public static final int MAX_BOOLEANS = 24;
public static int booleansToInt(List<Boolean> booleans) { private final int MAX_BOOLEANS = 24;
public int booleansToInt(List<Boolean> booleans) {
int result = 0; int result = 0;
int i = 1; int i = 1;
for (boolean b : booleans) { for (boolean b : booleans) {
if (b) { if (b) {
result += i; result += i;
} }
i *= 2; i *= 2;
} }
return result; return result;
} }
public static int mapBooleansToInt(Map<String, Boolean> booleanMap) { public int mapBooleansToInt(Map<String, Boolean> booleanMap) {
int result = 0; int result = 0;
int i = 1; int i = 1;
List<String> keys = new ArrayList<>(booleanMap.keySet()); List<String> keys = new ArrayList<>(booleanMap.keySet());
Collections.sort(keys); Collections.sort(keys);
for (String key : keys) { for (String key : keys) {
if (booleanMap.get(key)) { if (booleanMap.get(key)) {
result += i; result += i;
@@ -34,11 +39,12 @@ public class BooleanPacker {
return result; return result;
} }
public static List<Integer> booleansToInts(List<Boolean> booleans) { public List<Integer> booleansToInts(List<Boolean> booleans) {
List<Integer> results = new ArrayList<>(); List<Integer> results = new ArrayList<>();
int result = 0; int result = 0;
int i = 1; int i = 1;
int i1 = 1; int i1 = 1;
for (boolean b : booleans) { for (boolean b : booleans) {
if (b) { if (b) {
result += i; result += i;
@@ -56,15 +62,15 @@ public class BooleanPacker {
return results; return results;
} }
public static List<Integer> mapBooleansToInts(Map<String, Boolean> booleanMap) { public List<Integer> mapBooleansToInts(Map<String, Boolean> booleanMap) {
List<String> keys = new ArrayList<>(booleanMap.keySet()); List<String> keys = new ArrayList<>(booleanMap.keySet());
List<Boolean> booleans = new ArrayList<>(); List<Boolean> booleans = new ArrayList<>();
Collections.sort(keys); Collections.sort(keys);
for (String key : keys) { for (String key : keys) {
booleans.add(booleanMap.get(key)); booleans.add(booleanMap.get(key));
} }
return booleansToInts(booleans); return booleansToInts(booleans);
} }
} }

View File

@@ -0,0 +1,29 @@
package re.imc.geysermodelengine.util;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.TextDecoration;
import net.kyori.adventure.text.minimessage.MiniMessage;
import net.kyori.adventure.text.minimessage.tag.resolver.TagResolver;
import net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer;
import org.jetbrains.annotations.NotNull;
public class ColourUtils {
private final MiniMessage miniMessage = MiniMessage.miniMessage();
public @NotNull Component miniFormat(String message) {
return miniMessage.deserialize(message).decoration(TextDecoration.ITALIC, false);
}
public @NotNull Component miniFormat(String message, TagResolver tagResolver) {
return miniMessage.deserialize(message, tagResolver).decoration(TextDecoration.ITALIC, false);
}
public String stripColour(Component component) {
return PlainTextComponentSerializer.plainText().serialize(component);
}
public MiniMessage getMiniMessage() {
return miniMessage;
}
}

View File

@@ -0,0 +1,4 @@
commands:
reload:
successfully-reloaded: "<#55FF55>GeyserModelEngine configuration reloaded!"

View File

@@ -0,0 +1,22 @@
main: re.imc.geysermodelengine.GeyserModelEngine
name: GeyserModelEngine
version: '1.0.0'
api-version: '1.21'
authors:
- zimzaza4
- willem.dev
- TheLividaProject
load: STARTUP
dependencies:
server:
GeyserUtils:
required: true
packetevents:
required: true
ModelEngine:
required: true
floodgate:
required: true

View File

@@ -1,14 +0,0 @@
name: GeyserModelEngine
version: '${project.version}'
main: re.imc.geysermodelengine.GeyserModelEngine
authors:
- zimzaza4
- willem.dev
api-version: '1.19'
depend:
- ModelEngine
- floodgate
commands:
geysermodelengine:
usage: /geysermodelengine reload
permission: geysermodelengine.reload