Major Overhaul (needs testing)

This commit is contained in:
TheLividaProject
2025-05-27 22:32:49 +01:00
parent 1ac7beec4c
commit f2cc0b9663
102 changed files with 2307 additions and 957 deletions

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

Binary file not shown.

View File

@@ -0,0 +1,2 @@
#Tue May 27 16:38:57 BST 2025
gradle.version=8.12

Binary file not shown.

BIN
.gradle/file-system.probe Normal file

Binary file not shown.

View File

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

@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="CompilerConfiguration">
<annotationProcessing>
<profile name="Maven default annotation processors profile" enabled="true">
<sourceOutputDir name="target/generated-sources/annotations" />
<sourceTestOutputDir name="target/generated-test-sources/test-annotations" />
<outputRelativeToContentRoot value="true" />
<module name="GeyserModelEngine" />
</profile>
</annotationProcessing>
</component>
<component name="JavacSettings">
<option name="ADDITIONAL_OPTIONS_OVERRIDE">
<module name="GeyserModelEngine" options="" />
</option>
</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>

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>

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

@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ExternalStorageConfigurationManager" enabled="true" />
<component name="MavenProjectsManager">
<option name="originalFiles">
<list>
<option value="$PROJECT_DIR$/pom.xml" />
</list>
</option>
</component>
<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>

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

@@ -0,0 +1,49 @@
<?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="" />
<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="Git.Settings">
<option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$" />
</component>
<component name="MavenRunner">
<option name="delegateBuildToMaven" value="true" />
</component>
<component name="ProjectColorInfo"><![CDATA[{
"associatedIndex": 8
}]]></component>
<component name="ProjectId" id="2xedme8VKz03tyMoE1OuGEibnGo" />
<component name="ProjectViewState">
<option name="hideEmptyMiddlePackages" value="true" />
<option name="showLibraryContents" value="true" />
</component>
<component name="PropertiesComponent"><![CDATA[{
"keyToString": {
"Maven.GeyserModelEngine [install...].executor": "Run",
"Maven.GeyserModelEngine [install].executor": "Run",
"ModuleVcsDetector.initialDetectionPerformed": "true",
"RunOnceActivity.ShowReadmeOnStart": "true",
"RunOnceActivity.git.unshallow": "true",
"git-widget-placeholder": "main",
"last_opened_file_path": "D:/Coding/Forks/Minecraft/GeyserModelEngine",
"settings.editor.selected.configurable": "reference.settings.project.maven.runner"
}
}]]></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>

60
build.gradle.kts Normal file
View File

@@ -0,0 +1,60 @@
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://s01.oss.sonatype.org/content/repositories/snapshots/")
maven("https://mvn.lumine.io/repository/maven-public/")
maven("https://repo.opencollab.dev/maven-releases/") {
isAllowInsecureProtocol = true
}
maven("https://repo.opencollab.dev/maven-snapshots/") {
isAllowInsecureProtocol = true
}
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.5-R0.1-SNAPSHOT")
implementation("dev.jorel:commandapi-bukkit-shade-mojang-mapped:10.0.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.2-SNAPSHOT")
implementation("com.github.retrooper:packetevents-spigot:2.8.0")
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)
}

Binary file not shown.

Binary file not shown.

File diff suppressed because one or more lines are too long

View File

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

View File

@@ -0,0 +1,8 @@
data-send-delay: 5
entity-view-distance: 50
join-send-delay: 20
entity-position-update-period: 35
model-entity-type: BAT # must be a living entity
enable-part-visibility-models:
- example
debug: false

View File

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

Binary file not shown.

View File

@@ -0,0 +1,2 @@
Manifest-Version: 1.0

View File

@@ -0,0 +1,2 @@
Manifest-Version: 1.0

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

152
pom.xml
View File

@@ -1,152 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>re.imc</groupId>
<artifactId>GeyserModelEngine</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<name>GeyserModelEngine</name>
<properties>
<java.version>1.8</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>16</source>
<target>16</target>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.4.1</version>
<configuration>
<relocations>
<relocation>
<pattern>com.github.retrooper.packetevents</pattern>
<shadedPattern>re.imc.geysermodelengine.libs.com.github.retrooper.packetevents</shadedPattern>
</relocation>
<relocation>
<pattern>io.github.retrooper.packetevents</pattern>
<shadedPattern>re.imc.geysermodelengine.libs.io.github.retrooper.packetevents</shadedPattern>
</relocation>
</relocations>
</configuration>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<createDependencyReducedPom>false</createDependencyReducedPom>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
</resource>
</resources>
</build>
<repositories>
<repository>
<id>papermc-repo</id>
<url>https://repo.papermc.io/repository/maven-public/</url>
</repository>
<repository>
<id>sonatype</id>
<url>https://oss.sonatype.org/content/groups/public/</url>
</repository>
<repository>
<id>nexus</id>
<name>Lumine Public</name>
<url>https://mvn.lumine.io/repository/maven-public/</url>
</repository>
<repository>
<id>md_5-public</id>
<url>https://repo.md-5.net/content/groups/public/</url>
</repository>
<repository>
<id>opencollab-release-repo</id>
<url>https://repo.opencollab.dev/maven-releases/</url>
<releases>
<enabled>true</enabled>
</releases>
<snapshots>
<enabled>true</enabled>
</snapshots>
</repository>
<repository>
<id>opencollab-snapshot-repo</id>
<url>https://repo.opencollab.dev/maven-snapshots/</url>
<releases>
<enabled>false</enabled>
</releases>
<snapshots>
<enabled>true</enabled>
</snapshots>
</repository>
<repository>
<id>dmulloy2-repo</id>
<url>https://repo.dmulloy2.net/repository/public/</url>
</repository>
<repository>
<id>codemc-releases</id>
<url>https://repo.codemc.io/repository/maven-releases/</url>
</repository>
<repository>
<id>codemc-snapshots</id>
<url>https://repo.codemc.io/repository/maven-snapshots/</url>
</repository>
</repositories>
<dependencies>
<dependency>
<groupId>io.papermc.paper</groupId>
<artifactId>paper-api</artifactId>
<version>1.21.5-R0.1-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.ticxo.modelengine</groupId>
<artifactId>ModelEngine</artifactId>
<version>R4.0.4</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.github.geyserextensionists</groupId>
<artifactId>geyserutils-spigot</artifactId>
<version>1.0.0</version>
<scope>system</scope>
<systemPath>${project.basedir}/libs/geyserutils-spigot-1.0-SNAPSHOT.jar</systemPath>
</dependency>
<dependency>
<groupId>org.geysermc.floodgate</groupId>
<artifactId>api</artifactId>
<version>2.2.2-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.github.retrooper</groupId>
<artifactId>packetevents-spigot</artifactId>
<version>2.8.0</version>
<scope>compile</scope>
</dependency>
</dependencies>
</project>

2
settings.gradle.kts Normal file
View File

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

View File

@@ -2,149 +2,143 @@ 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.ModelEngineAPI;
import com.ticxo.modelengine.api.model.ActiveModel; import com.ticxo.modelengine.api.model.ActiveModel;
import com.ticxo.modelengine.api.model.ModeledEntity; import com.ticxo.modelengine.api.model.ModeledEntity;
import com.ticxo.modelengine.api.model.bone.type.Mount; import dev.jorel.commandapi.CommandAPI;
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.World;
import org.bukkit.entity.Entity; 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.bedrock.BedrockMountControlManager;
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.player.PlayerManager;
import re.imc.geysermodelengine.managers.server.ServerManager;
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; private ServerManager serverManager;
@Getter private CommandManager commandManager;
private static boolean alwaysSendSkin;
@Getter private ModelManager modelManager;
private int sendDelay; private EntityTaskManager entityTaskManager;
private BedrockMountControlManager bedrockMountControlManager;
@Getter private PlayerManager playerManager;
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(this, () -> {
Bukkit.getScheduler()
.runTaskLater(GeyserModelEngine.getInstance(), () -> {
for (World world : Bukkit.getWorlds()) { for (World world : Bukkit.getWorlds()) {
for (Entity entity : world.getEntities()) { for (Entity entity : world.getEntities()) {
if (!ModelEntity.ENTITIES.containsKey(entity.getEntityId())) {
if (!modelManager.getEntitiesCache().containsKey(entity.getEntityId())) {
ModeledEntity modeledEntity = ModelEngineAPI.getModeledEntity(entity); ModeledEntity modeledEntity = ModelEngineAPI.getModeledEntity(entity);
if (modeledEntity != null) { if (modeledEntity != null) {
Optional<ActiveModel> model = modeledEntity.getModels().values().stream().findFirst(); Optional<ActiveModel> model = modeledEntity.getModels().values().stream().findFirst();
model.ifPresent(m -> ModelEntity.create(modeledEntity, m)); model.ifPresent(m -> modelManager.create(modeledEntity, m));
} }
} }
} }
} }
initialized = true;
}, 100); }, 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 err) {
throw new RuntimeException(err);
}
}, 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.serverManager = new ServerManager();
this.commandManager = new CommandManager(this);
this.modelManager = new ModelManager(this);
this.entityTaskManager = new EntityTaskManager(this);
this.bedrockMountControlManager = new BedrockMountControlManager();
this.playerManager = new PlayerManager();
}
private void loadRunnables() {
Bukkit.getAsyncScheduler().runAtFixedRate(this, new UpdateTaskRunnable(this), 10, configManager.getConfig().getLong("entity-position-update-period"), TimeUnit.MILLISECONDS);
Bukkit.getAsyncScheduler().runAtFixedRate(this, new BedrockMountControlRunnable(this), 1, 1, TimeUnit.MILLISECONDS);
}
public ConfigManager getConfigManager() {
return configManager;
}
public ServerManager getServerManager() {
return serverManager;
}
public CommandManager getCommandManager() {
return commandManager;
}
public ModelManager getModelManager() {
return modelManager;
}
public EntityTaskManager getEntityTaskManager() {
return entityTaskManager;
}
public BedrockMountControlManager getBedrockMountControlManager() {
return bedrockMountControlManager;
}
public PlayerManager getPlayerManager() {
return playerManager;
}
} }

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,31 @@
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,7 +3,6 @@ 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;
@@ -12,117 +11,54 @@ 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 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.getBedrockMountControlManager().getDriversCache().put(player, 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.getBedrockMountControlManager().getDriversCache().remove(player);
} }
} }
//TODO Find out why we need this bc uh what?
/*
@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
public void onModelAttack(EntityDamageByEntityEvent event) {
ModelEntity model = ModelEntity.ENTITIES.get(event.getDamager().getEntityId());
if (model != null) {
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 onPlayerLogin(PlayerJoinEvent event) {
Bukkit.getScheduler().runTaskLater(GeyserModelEngine.getInstance(), () -> GeyserModelEngine.getInstance().getJoinedPlayers().add(event.getPlayer()), 10); Bukkit.getGlobalRegionScheduler().runDelayed(plugin, scheduledTask -> plugin.getPlayerManager().getPlayerJoinedCache().add(event.getPlayer()), 10);
} }
@EventHandler @EventHandler
public void onPlayerQuit(PlayerQuitEvent event) { public void onPlayerQuit(PlayerQuitEvent event) {
Bukkit.getScheduler().runTaskLater(GeyserModelEngine.getInstance(), () -> GeyserModelEngine.getInstance().getJoinedPlayers().remove(event.getPlayer()), 10); Bukkit.getGlobalRegionScheduler().runDelayed(plugin, scheduledTask -> plugin.getPlayerManager().getPlayerJoinedCache().remove(event.getPlayer()), 10);
} }
} }

View File

@@ -14,6 +14,12 @@ 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) return; if (event.getPacketType() != PacketType.Play.Client.ENTITY_ACTION) return;
@@ -22,7 +28,7 @@ public class MountPacketListener implements PacketListener {
Player player = event.getPlayer(); Player player = event.getPlayer();
WrapperPlayClientEntityAction action = new WrapperPlayClientEntityAction(event); WrapperPlayClientEntityAction action = new WrapperPlayClientEntityAction(event);
Pair<ActiveModel, Mount> seat = GeyserModelEngine.getInstance().getDrivers().get(player); Pair<ActiveModel, Mount> seat = plugin.getBedrockMountControlManager().getDriversCache().get(player);
if (seat == null) return; if (seat == null) return;
if (action.getAction() != WrapperPlayClientEntityAction.Action.START_SNEAKING) return; if (action.getAction() != WrapperPlayClientEntityAction.Action.START_SNEAKING) return;

View File

@@ -0,0 +1,32 @@
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() {
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,17 @@
package re.imc.geysermodelengine.managers.bedrock;
import com.ticxo.modelengine.api.model.ActiveModel;
import com.ticxo.modelengine.api.model.bone.type.Mount;
import org.apache.commons.lang3.tuple.Pair;
import org.bukkit.entity.Player;
import java.util.concurrent.ConcurrentHashMap;
public class BedrockMountControlManager {
private final ConcurrentHashMap<Player, Pair<ActiveModel, Mount>> driversCache = new ConcurrentHashMap<>();
public ConcurrentHashMap<Player, Pair<ActiveModel, Mount>> getDriversCache() {
return driversCache;
}
}

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.getPlayerManager().getPlayerJoinedCache().contains(onlinePlayer);
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.getPlayerManager().getPlayerJoinedCache().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;
}
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,46 @@
package re.imc.geysermodelengine.managers.model;
import com.ticxo.modelengine.api.model.ActiveModel;
import com.ticxo.modelengine.api.model.ModeledEntity;
import re.imc.geysermodelengine.GeyserModelEngine;
import re.imc.geysermodelengine.managers.model.data.ModelEntityData;
import re.imc.geysermodelengine.runnables.EntityTaskRunnable;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
public class ModelManager {
private final GeyserModelEngine plugin;
private final ConcurrentHashMap<Integer, Map<ActiveModel, ModelEntityData>> entitiesCache = new ConcurrentHashMap<>();
private final Map<Integer, ModelEntityData> modelEntitiesCache = 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 ConcurrentHashMap<Integer, Map<ActiveModel, ModelEntityData>> getEntitiesCache() {
return entitiesCache;
}
public Map<Integer, ModelEntityData> getModelEntitiesCache() {
return modelEntitiesCache;
}
}

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

@@ -0,0 +1,14 @@
package re.imc.geysermodelengine.managers.player;
import org.bukkit.entity.Player;
import java.util.HashSet;
public class PlayerManager {
private final HashSet<Player> playerJoinedCache = new HashSet<>();
public HashSet<Player> getPlayerJoinedCache() {
return playerJoinedCache;
}
}

View File

@@ -0,0 +1,14 @@
package re.imc.geysermodelengine.managers.server;
import io.papermc.paper.threadedregions.scheduler.ScheduledTask;
import java.util.concurrent.ConcurrentHashMap;
public class ServerData {
private final ConcurrentHashMap<String, ScheduledTask> activeRunnablesCache = new ConcurrentHashMap<>();
public ConcurrentHashMap<String, ScheduledTask> getActiveRunnablesCache() {
return activeRunnablesCache;
}
}

View File

@@ -0,0 +1,14 @@
package re.imc.geysermodelengine.managers.server;
public class ServerManager {
private final ServerData serverData;
public ServerManager() {
this.serverData = new ServerData();
}
public ServerData getServerData() {
return serverData;
}
}

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,14 +27,6 @@ import java.util.concurrent.ThreadLocalRandom;
@Setter @Setter
public class PacketEntity { public class PacketEntity {
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;
@@ -44,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;
} }
@@ -51,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;
} }
@@ -73,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));
} }
@@ -83,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) {

View File

@@ -0,0 +1,66 @@
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.getBedrockMountControlManager().getDriversCache().get(player);
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!"

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