diff --git a/.gradle/8.12/checksums/checksums.lock b/.gradle/8.12/checksums/checksums.lock
new file mode 100644
index 0000000..7df35ae
Binary files /dev/null and b/.gradle/8.12/checksums/checksums.lock differ
diff --git a/.gradle/8.12/checksums/md5-checksums.bin b/.gradle/8.12/checksums/md5-checksums.bin
new file mode 100644
index 0000000..e69b641
Binary files /dev/null and b/.gradle/8.12/checksums/md5-checksums.bin differ
diff --git a/.gradle/8.12/checksums/sha1-checksums.bin b/.gradle/8.12/checksums/sha1-checksums.bin
new file mode 100644
index 0000000..74a6969
Binary files /dev/null and b/.gradle/8.12/checksums/sha1-checksums.bin differ
diff --git a/.gradle/8.12/executionHistory/executionHistory.bin b/.gradle/8.12/executionHistory/executionHistory.bin
new file mode 100644
index 0000000..46bb097
Binary files /dev/null and b/.gradle/8.12/executionHistory/executionHistory.bin differ
diff --git a/.gradle/8.12/executionHistory/executionHistory.lock b/.gradle/8.12/executionHistory/executionHistory.lock
new file mode 100644
index 0000000..63b4f05
Binary files /dev/null and b/.gradle/8.12/executionHistory/executionHistory.lock differ
diff --git a/.gradle/8.12/fileChanges/last-build.bin b/.gradle/8.12/fileChanges/last-build.bin
new file mode 100644
index 0000000..f76dd23
Binary files /dev/null and b/.gradle/8.12/fileChanges/last-build.bin differ
diff --git a/.gradle/8.12/fileHashes/fileHashes.bin b/.gradle/8.12/fileHashes/fileHashes.bin
new file mode 100644
index 0000000..39baf8f
Binary files /dev/null and b/.gradle/8.12/fileHashes/fileHashes.bin differ
diff --git a/.gradle/8.12/fileHashes/fileHashes.lock b/.gradle/8.12/fileHashes/fileHashes.lock
new file mode 100644
index 0000000..e36d0e0
Binary files /dev/null and b/.gradle/8.12/fileHashes/fileHashes.lock differ
diff --git a/.gradle/8.12/fileHashes/resourceHashesCache.bin b/.gradle/8.12/fileHashes/resourceHashesCache.bin
new file mode 100644
index 0000000..d62eefa
Binary files /dev/null and b/.gradle/8.12/fileHashes/resourceHashesCache.bin differ
diff --git a/.gradle/8.12/gc.properties b/.gradle/8.12/gc.properties
new file mode 100644
index 0000000..e69de29
diff --git a/.gradle/buildOutputCleanup/buildOutputCleanup.lock b/.gradle/buildOutputCleanup/buildOutputCleanup.lock
new file mode 100644
index 0000000..90c493c
Binary files /dev/null and b/.gradle/buildOutputCleanup/buildOutputCleanup.lock differ
diff --git a/.gradle/buildOutputCleanup/cache.properties b/.gradle/buildOutputCleanup/cache.properties
new file mode 100644
index 0000000..507882f
--- /dev/null
+++ b/.gradle/buildOutputCleanup/cache.properties
@@ -0,0 +1,2 @@
+#Tue May 27 16:38:57 BST 2025
+gradle.version=8.12
diff --git a/.gradle/buildOutputCleanup/outputFiles.bin b/.gradle/buildOutputCleanup/outputFiles.bin
new file mode 100644
index 0000000..a70a0e2
Binary files /dev/null and b/.gradle/buildOutputCleanup/outputFiles.bin differ
diff --git a/.gradle/file-system.probe b/.gradle/file-system.probe
new file mode 100644
index 0000000..b573649
Binary files /dev/null and b/.gradle/file-system.probe differ
diff --git a/.gradle/vcs-1/gc.properties b/.gradle/vcs-1/gc.properties
new file mode 100644
index 0000000..e69de29
diff --git a/.idea/compiler.xml b/.idea/compiler.xml
new file mode 100644
index 0000000..d0700b4
--- /dev/null
+++ b/.idea/compiler.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/encodings.xml b/.idea/encodings.xml
new file mode 100644
index 0000000..aa00ffa
--- /dev/null
+++ b/.idea/encodings.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/jarRepositories.xml b/.idea/jarRepositories.xml
new file mode 100644
index 0000000..4a2aa3a
--- /dev/null
+++ b/.idea/jarRepositories.xml
@@ -0,0 +1,65 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
new file mode 100644
index 0000000..9dc782b
--- /dev/null
+++ b/.idea/misc.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
new file mode 100644
index 0000000..35eb1dd
--- /dev/null
+++ b/.idea/vcs.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/workspace.xml b/.idea/workspace.xml
new file mode 100644
index 0000000..389e93e
--- /dev/null
+++ b/.idea/workspace.xml
@@ -0,0 +1,49 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 1748302633990
+
+
+ 1748302633990
+
+
+
+
\ No newline at end of file
diff --git a/GeyserModelEngine.iml b/GeyserModelEngine.iml
deleted file mode 100644
index d913b81..0000000
--- a/GeyserModelEngine.iml
+++ /dev/null
@@ -1,16 +0,0 @@
-
-
-
-
-
-
- PAPER
- ADVENTURE
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/build.gradle.kts b/build.gradle.kts
new file mode 100644
index 0000000..77eeaf6
--- /dev/null
+++ b/build.gradle.kts
@@ -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)
+}
\ No newline at end of file
diff --git a/build/classes/java/main/re/imc/geysermodelengine/GeyserModelEngine.class b/build/classes/java/main/re/imc/geysermodelengine/GeyserModelEngine.class
new file mode 100644
index 0000000..96f94f3
Binary files /dev/null and b/build/classes/java/main/re/imc/geysermodelengine/GeyserModelEngine.class differ
diff --git a/build/classes/java/main/re/imc/geysermodelengine/commands/geysermodelenginecommands/GeyserModelEngineReloadCommand.class b/build/classes/java/main/re/imc/geysermodelengine/commands/geysermodelenginecommands/GeyserModelEngineReloadCommand.class
new file mode 100644
index 0000000..d0dfc7e
Binary files /dev/null and b/build/classes/java/main/re/imc/geysermodelengine/commands/geysermodelenginecommands/GeyserModelEngineReloadCommand.class differ
diff --git a/build/classes/java/main/re/imc/geysermodelengine/listener/ModelListener.class b/build/classes/java/main/re/imc/geysermodelengine/listener/ModelListener.class
new file mode 100644
index 0000000..feaad3b
Binary files /dev/null and b/build/classes/java/main/re/imc/geysermodelengine/listener/ModelListener.class differ
diff --git a/build/classes/java/main/re/imc/geysermodelengine/listener/MountPacketListener.class b/build/classes/java/main/re/imc/geysermodelengine/listener/MountPacketListener.class
new file mode 100644
index 0000000..779c016
Binary files /dev/null and b/build/classes/java/main/re/imc/geysermodelengine/listener/MountPacketListener.class differ
diff --git a/build/classes/java/main/re/imc/geysermodelengine/managers/ConfigManager.class b/build/classes/java/main/re/imc/geysermodelengine/managers/ConfigManager.class
new file mode 100644
index 0000000..dc9ab0a
Binary files /dev/null and b/build/classes/java/main/re/imc/geysermodelengine/managers/ConfigManager.class differ
diff --git a/build/classes/java/main/re/imc/geysermodelengine/managers/bedrock/BedrockMountControlManager.class b/build/classes/java/main/re/imc/geysermodelengine/managers/bedrock/BedrockMountControlManager.class
new file mode 100644
index 0000000..74586e4
Binary files /dev/null and b/build/classes/java/main/re/imc/geysermodelengine/managers/bedrock/BedrockMountControlManager.class differ
diff --git a/build/classes/java/main/re/imc/geysermodelengine/managers/commands/CommandManager.class b/build/classes/java/main/re/imc/geysermodelengine/managers/commands/CommandManager.class
new file mode 100644
index 0000000..ca8f636
Binary files /dev/null and b/build/classes/java/main/re/imc/geysermodelengine/managers/commands/CommandManager.class differ
diff --git a/build/classes/java/main/re/imc/geysermodelengine/managers/commands/CommandManagers.class b/build/classes/java/main/re/imc/geysermodelengine/managers/commands/CommandManagers.class
new file mode 100644
index 0000000..2d0cc12
Binary files /dev/null and b/build/classes/java/main/re/imc/geysermodelengine/managers/commands/CommandManagers.class differ
diff --git a/build/classes/java/main/re/imc/geysermodelengine/managers/commands/managers/geysermodelengine/GeyserModelEngineCommandManager.class b/build/classes/java/main/re/imc/geysermodelengine/managers/commands/managers/geysermodelengine/GeyserModelEngineCommandManager.class
new file mode 100644
index 0000000..41bb32e
Binary files /dev/null and b/build/classes/java/main/re/imc/geysermodelengine/managers/commands/managers/geysermodelengine/GeyserModelEngineCommandManager.class differ
diff --git a/build/classes/java/main/re/imc/geysermodelengine/managers/commands/subcommands/SubCommands.class b/build/classes/java/main/re/imc/geysermodelengine/managers/commands/subcommands/SubCommands.class
new file mode 100644
index 0000000..01cd174
Binary files /dev/null and b/build/classes/java/main/re/imc/geysermodelengine/managers/commands/subcommands/SubCommands.class differ
diff --git a/build/classes/java/main/re/imc/geysermodelengine/managers/model/EntityTaskManager.class b/build/classes/java/main/re/imc/geysermodelengine/managers/model/EntityTaskManager.class
new file mode 100644
index 0000000..2a5e04c
Binary files /dev/null and b/build/classes/java/main/re/imc/geysermodelengine/managers/model/EntityTaskManager.class differ
diff --git a/build/classes/java/main/re/imc/geysermodelengine/managers/model/ModelManager.class b/build/classes/java/main/re/imc/geysermodelengine/managers/model/ModelManager.class
new file mode 100644
index 0000000..0fc48c2
Binary files /dev/null and b/build/classes/java/main/re/imc/geysermodelengine/managers/model/ModelManager.class differ
diff --git a/build/classes/java/main/re/imc/geysermodelengine/managers/model/data/ModelEntityData.class b/build/classes/java/main/re/imc/geysermodelengine/managers/model/data/ModelEntityData.class
new file mode 100644
index 0000000..ee46170
Binary files /dev/null and b/build/classes/java/main/re/imc/geysermodelengine/managers/model/data/ModelEntityData.class differ
diff --git a/build/classes/java/main/re/imc/geysermodelengine/managers/player/PlayerManager.class b/build/classes/java/main/re/imc/geysermodelengine/managers/player/PlayerManager.class
new file mode 100644
index 0000000..d9fc723
Binary files /dev/null and b/build/classes/java/main/re/imc/geysermodelengine/managers/player/PlayerManager.class differ
diff --git a/build/classes/java/main/re/imc/geysermodelengine/managers/server/ServerData.class b/build/classes/java/main/re/imc/geysermodelengine/managers/server/ServerData.class
new file mode 100644
index 0000000..9c2f448
Binary files /dev/null and b/build/classes/java/main/re/imc/geysermodelengine/managers/server/ServerData.class differ
diff --git a/build/classes/java/main/re/imc/geysermodelengine/managers/server/ServerManager.class b/build/classes/java/main/re/imc/geysermodelengine/managers/server/ServerManager.class
new file mode 100644
index 0000000..977adf5
Binary files /dev/null and b/build/classes/java/main/re/imc/geysermodelengine/managers/server/ServerManager.class differ
diff --git a/build/classes/java/main/re/imc/geysermodelengine/packet/entity/PacketEntity.class b/build/classes/java/main/re/imc/geysermodelengine/packet/entity/PacketEntity.class
new file mode 100644
index 0000000..ca5d881
Binary files /dev/null and b/build/classes/java/main/re/imc/geysermodelengine/packet/entity/PacketEntity.class differ
diff --git a/build/classes/java/main/re/imc/geysermodelengine/runnables/BedrockMountControlRunnable.class b/build/classes/java/main/re/imc/geysermodelengine/runnables/BedrockMountControlRunnable.class
new file mode 100644
index 0000000..b3ca4c0
Binary files /dev/null and b/build/classes/java/main/re/imc/geysermodelengine/runnables/BedrockMountControlRunnable.class differ
diff --git a/build/classes/java/main/re/imc/geysermodelengine/runnables/EntityTaskRunnable.class b/build/classes/java/main/re/imc/geysermodelengine/runnables/EntityTaskRunnable.class
new file mode 100644
index 0000000..1d33c4d
Binary files /dev/null and b/build/classes/java/main/re/imc/geysermodelengine/runnables/EntityTaskRunnable.class differ
diff --git a/build/classes/java/main/re/imc/geysermodelengine/runnables/UpdateTaskRunnable.class b/build/classes/java/main/re/imc/geysermodelengine/runnables/UpdateTaskRunnable.class
new file mode 100644
index 0000000..692d904
Binary files /dev/null and b/build/classes/java/main/re/imc/geysermodelengine/runnables/UpdateTaskRunnable.class differ
diff --git a/build/classes/java/main/re/imc/geysermodelengine/util/BooleanPacker.class b/build/classes/java/main/re/imc/geysermodelengine/util/BooleanPacker.class
new file mode 100644
index 0000000..777b201
Binary files /dev/null and b/build/classes/java/main/re/imc/geysermodelengine/util/BooleanPacker.class differ
diff --git a/build/classes/java/main/re/imc/geysermodelengine/util/ColourUtils.class b/build/classes/java/main/re/imc/geysermodelengine/util/ColourUtils.class
new file mode 100644
index 0000000..e542e09
Binary files /dev/null and b/build/classes/java/main/re/imc/geysermodelengine/util/ColourUtils.class differ
diff --git a/build/libs/GeyserModelEngine-1.0.0-all.jar b/build/libs/GeyserModelEngine-1.0.0-all.jar
new file mode 100644
index 0000000..da31d7d
Binary files /dev/null and b/build/libs/GeyserModelEngine-1.0.0-all.jar differ
diff --git a/build/libs/GeyserModelEngine-1.0.0.jar b/build/libs/GeyserModelEngine-1.0.0.jar
new file mode 100644
index 0000000..ff24642
Binary files /dev/null and b/build/libs/GeyserModelEngine-1.0.0.jar differ
diff --git a/build/reports/problems/problems-report.html b/build/reports/problems/problems-report.html
new file mode 100644
index 0000000..d6f75ed
--- /dev/null
+++ b/build/reports/problems/problems-report.html
@@ -0,0 +1,663 @@
+
+
+
+
+
+
+
+
+
+
+
+
+ Gradle Configuration Cache
+
+
+
+
+
+
+ Loading...
+
+
+
+
+
+
+
diff --git a/build/resources/main/Lang/messages.yml b/build/resources/main/Lang/messages.yml
new file mode 100644
index 0000000..fdc999a
--- /dev/null
+++ b/build/resources/main/Lang/messages.yml
@@ -0,0 +1,4 @@
+
+commands:
+ reload:
+ successfully-reloaded: "<#55FF55>GeyserModelEngine configuration reloaded!"
\ No newline at end of file
diff --git a/build/resources/main/config.yml b/build/resources/main/config.yml
new file mode 100644
index 0000000..ba9fd1e
--- /dev/null
+++ b/build/resources/main/config.yml
@@ -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
\ No newline at end of file
diff --git a/build/resources/main/paper-plugin.yml b/build/resources/main/paper-plugin.yml
new file mode 100644
index 0000000..5a771d5
--- /dev/null
+++ b/build/resources/main/paper-plugin.yml
@@ -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
\ No newline at end of file
diff --git a/build/tmp/compileJava/compileTransaction/stash-dir/BedrockMountControlRunnable.class.uniqueId12 b/build/tmp/compileJava/compileTransaction/stash-dir/BedrockMountControlRunnable.class.uniqueId12
new file mode 100644
index 0000000..b3ca4c0
Binary files /dev/null and b/build/tmp/compileJava/compileTransaction/stash-dir/BedrockMountControlRunnable.class.uniqueId12 differ
diff --git a/build/tmp/compileJava/compileTransaction/stash-dir/CommandManager.class.uniqueId11 b/build/tmp/compileJava/compileTransaction/stash-dir/CommandManager.class.uniqueId11
new file mode 100644
index 0000000..ca8f636
Binary files /dev/null and b/build/tmp/compileJava/compileTransaction/stash-dir/CommandManager.class.uniqueId11 differ
diff --git a/build/tmp/compileJava/compileTransaction/stash-dir/ConfigManager.class.uniqueId9 b/build/tmp/compileJava/compileTransaction/stash-dir/ConfigManager.class.uniqueId9
new file mode 100644
index 0000000..dc9ab0a
Binary files /dev/null and b/build/tmp/compileJava/compileTransaction/stash-dir/ConfigManager.class.uniqueId9 differ
diff --git a/build/tmp/compileJava/compileTransaction/stash-dir/EntityTaskManager.class.uniqueId5 b/build/tmp/compileJava/compileTransaction/stash-dir/EntityTaskManager.class.uniqueId5
new file mode 100644
index 0000000..cf01e52
Binary files /dev/null and b/build/tmp/compileJava/compileTransaction/stash-dir/EntityTaskManager.class.uniqueId5 differ
diff --git a/build/tmp/compileJava/compileTransaction/stash-dir/EntityTaskRunnable.class.uniqueId10 b/build/tmp/compileJava/compileTransaction/stash-dir/EntityTaskRunnable.class.uniqueId10
new file mode 100644
index 0000000..1d33c4d
Binary files /dev/null and b/build/tmp/compileJava/compileTransaction/stash-dir/EntityTaskRunnable.class.uniqueId10 differ
diff --git a/build/tmp/compileJava/compileTransaction/stash-dir/GeyserModelEngine.class.uniqueId0 b/build/tmp/compileJava/compileTransaction/stash-dir/GeyserModelEngine.class.uniqueId0
new file mode 100644
index 0000000..96f94f3
Binary files /dev/null and b/build/tmp/compileJava/compileTransaction/stash-dir/GeyserModelEngine.class.uniqueId0 differ
diff --git a/build/tmp/compileJava/compileTransaction/stash-dir/GeyserModelEngineCommandManager.class.uniqueId3 b/build/tmp/compileJava/compileTransaction/stash-dir/GeyserModelEngineCommandManager.class.uniqueId3
new file mode 100644
index 0000000..41bb32e
Binary files /dev/null and b/build/tmp/compileJava/compileTransaction/stash-dir/GeyserModelEngineCommandManager.class.uniqueId3 differ
diff --git a/build/tmp/compileJava/compileTransaction/stash-dir/GeyserModelEngineReloadCommand.class.uniqueId1 b/build/tmp/compileJava/compileTransaction/stash-dir/GeyserModelEngineReloadCommand.class.uniqueId1
new file mode 100644
index 0000000..d0dfc7e
Binary files /dev/null and b/build/tmp/compileJava/compileTransaction/stash-dir/GeyserModelEngineReloadCommand.class.uniqueId1 differ
diff --git a/build/tmp/compileJava/compileTransaction/stash-dir/ModelEntityData.class.uniqueId7 b/build/tmp/compileJava/compileTransaction/stash-dir/ModelEntityData.class.uniqueId7
new file mode 100644
index 0000000..ee46170
Binary files /dev/null and b/build/tmp/compileJava/compileTransaction/stash-dir/ModelEntityData.class.uniqueId7 differ
diff --git a/build/tmp/compileJava/compileTransaction/stash-dir/ModelListener.class.uniqueId2 b/build/tmp/compileJava/compileTransaction/stash-dir/ModelListener.class.uniqueId2
new file mode 100644
index 0000000..feaad3b
Binary files /dev/null and b/build/tmp/compileJava/compileTransaction/stash-dir/ModelListener.class.uniqueId2 differ
diff --git a/build/tmp/compileJava/compileTransaction/stash-dir/ModelManager.class.uniqueId8 b/build/tmp/compileJava/compileTransaction/stash-dir/ModelManager.class.uniqueId8
new file mode 100644
index 0000000..0fc48c2
Binary files /dev/null and b/build/tmp/compileJava/compileTransaction/stash-dir/ModelManager.class.uniqueId8 differ
diff --git a/build/tmp/compileJava/compileTransaction/stash-dir/MountPacketListener.class.uniqueId6 b/build/tmp/compileJava/compileTransaction/stash-dir/MountPacketListener.class.uniqueId6
new file mode 100644
index 0000000..779c016
Binary files /dev/null and b/build/tmp/compileJava/compileTransaction/stash-dir/MountPacketListener.class.uniqueId6 differ
diff --git a/build/tmp/compileJava/compileTransaction/stash-dir/UpdateTaskRunnable.class.uniqueId4 b/build/tmp/compileJava/compileTransaction/stash-dir/UpdateTaskRunnable.class.uniqueId4
new file mode 100644
index 0000000..692d904
Binary files /dev/null and b/build/tmp/compileJava/compileTransaction/stash-dir/UpdateTaskRunnable.class.uniqueId4 differ
diff --git a/build/tmp/compileJava/previous-compilation-data.bin b/build/tmp/compileJava/previous-compilation-data.bin
new file mode 100644
index 0000000..f26b533
Binary files /dev/null and b/build/tmp/compileJava/previous-compilation-data.bin differ
diff --git a/build/tmp/jar/MANIFEST.MF b/build/tmp/jar/MANIFEST.MF
new file mode 100644
index 0000000..59499bc
--- /dev/null
+++ b/build/tmp/jar/MANIFEST.MF
@@ -0,0 +1,2 @@
+Manifest-Version: 1.0
+
diff --git a/build/tmp/shadowJar/MANIFEST.MF b/build/tmp/shadowJar/MANIFEST.MF
new file mode 100644
index 0000000..59499bc
--- /dev/null
+++ b/build/tmp/shadowJar/MANIFEST.MF
@@ -0,0 +1,2 @@
+Manifest-Version: 1.0
+
diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 0000000..a4b76b9
Binary files /dev/null and b/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..cea7a79
--- /dev/null
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -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
diff --git a/gradlew b/gradlew
new file mode 100644
index 0000000..f3b75f3
--- /dev/null
+++ b/gradlew
@@ -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" "$@"
diff --git a/gradlew.bat b/gradlew.bat
new file mode 100644
index 0000000..9d21a21
--- /dev/null
+++ b/gradlew.bat
@@ -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
diff --git a/pom.xml b/pom.xml
deleted file mode 100644
index 48b716b..0000000
--- a/pom.xml
+++ /dev/null
@@ -1,152 +0,0 @@
-
-
- 4.0.0
-
- re.imc
- GeyserModelEngine
- 1.0-SNAPSHOT
- jar
-
- GeyserModelEngine
-
-
- 1.8
- UTF-8
-
-
-
-
-
- org.apache.maven.plugins
- maven-compiler-plugin
- 3.8.1
-
- 16
- 16
-
-
-
- org.apache.maven.plugins
- maven-shade-plugin
- 3.4.1
-
-
-
- com.github.retrooper.packetevents
- re.imc.geysermodelengine.libs.com.github.retrooper.packetevents
-
-
- io.github.retrooper.packetevents
- re.imc.geysermodelengine.libs.io.github.retrooper.packetevents
-
-
-
-
-
- package
-
- shade
-
-
- false
-
-
-
-
-
-
-
- src/main/resources
- true
-
-
-
-
-
-
- papermc-repo
- https://repo.papermc.io/repository/maven-public/
-
-
- sonatype
- https://oss.sonatype.org/content/groups/public/
-
-
- nexus
- Lumine Public
- https://mvn.lumine.io/repository/maven-public/
-
-
- md_5-public
- https://repo.md-5.net/content/groups/public/
-
-
- opencollab-release-repo
- https://repo.opencollab.dev/maven-releases/
-
- true
-
-
- true
-
-
-
- opencollab-snapshot-repo
- https://repo.opencollab.dev/maven-snapshots/
-
- false
-
-
- true
-
-
-
- dmulloy2-repo
- https://repo.dmulloy2.net/repository/public/
-
-
- codemc-releases
- https://repo.codemc.io/repository/maven-releases/
-
-
- codemc-snapshots
- https://repo.codemc.io/repository/maven-snapshots/
-
-
-
-
-
- io.papermc.paper
- paper-api
- 1.21.5-R0.1-SNAPSHOT
- provided
-
-
- com.ticxo.modelengine
- ModelEngine
- R4.0.4
- provided
-
-
- com.github.geyserextensionists
- geyserutils-spigot
- 1.0.0
- system
- ${project.basedir}/libs/geyserutils-spigot-1.0-SNAPSHOT.jar
-
-
- org.geysermc.floodgate
- api
- 2.2.2-SNAPSHOT
- provided
-
-
- com.github.retrooper
- packetevents-spigot
- 2.8.0
- compile
-
-
-
diff --git a/settings.gradle.kts b/settings.gradle.kts
new file mode 100644
index 0000000..8e9efd7
--- /dev/null
+++ b/settings.gradle.kts
@@ -0,0 +1,2 @@
+rootProject.name = "GeyserModelEngine"
+
diff --git a/src/main/java/re/imc/geysermodelengine/GeyserModelEngine.java b/src/main/java/re/imc/geysermodelengine/GeyserModelEngine.java
index 1f0735a..25ea6f5 100644
--- a/src/main/java/re/imc/geysermodelengine/GeyserModelEngine.java
+++ b/src/main/java/re/imc/geysermodelengine/GeyserModelEngine.java
@@ -2,149 +2,143 @@ package re.imc.geysermodelengine;
import com.github.retrooper.packetevents.PacketEvents;
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.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 lombok.Getter;
-import org.apache.commons.lang3.tuple.Pair;
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 re.imc.geysermodelengine.commands.ReloadCommand;
import re.imc.geysermodelengine.listener.ModelListener;
import re.imc.geysermodelengine.listener.MountPacketListener;
-import re.imc.geysermodelengine.model.BedrockMountControl;
-import re.imc.geysermodelengine.model.ModelEntity;
+import re.imc.geysermodelengine.managers.ConfigManager;
+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.concurrent.*;
-public final class GeyserModelEngine extends JavaPlugin {
+public class GeyserModelEngine extends JavaPlugin {
- @Getter
- private static GeyserModelEngine instance;
+ private ConfigManager configManager;
+ private ServerManager serverManager;
- @Getter
- private static boolean alwaysSendSkin;
+ private CommandManager commandManager;
- @Getter
- private int sendDelay;
+ private ModelManager modelManager;
+ private EntityTaskManager entityTaskManager;
+ private BedrockMountControlManager bedrockMountControlManager;
- @Getter
- private int viewDistance;
-
- @Getter
- private Set joinedPlayers = new HashSet<>();
-
- @Getter
- private int joinSendDelay;
-
- @Getter
- private long entityPositionUpdatePeriod;
-
- @Getter
- private boolean debug;
-
- @Getter
- private Map> drivers = new ConcurrentHashMap<>();
-
- @Getter
- private boolean initialized = false;
-
- @Getter
- private List enablePartVisibilityModels = new ArrayList<>();
-
- @Getter
- private ScheduledExecutorService scheduler;
- private ScheduledFuture> updateTask;
+ private PlayerManager playerManager;
@Override
public void onLoad() {
PacketEvents.setAPI(SpigotPacketEventsBuilder.build(this));
PacketEvents.getAPI().load();
+
+ CommandAPI.onLoad(new CommandAPIBukkitConfig(this));
}
@Override
public void onEnable() {
- PacketEvents.getAPI().init();
- PacketEvents.getAPI().getEventManager().registerListener(new MountPacketListener(), PacketListenerPriority.NORMAL);
- /*
- scheduler.scheduleAtFixedRate(() -> {
- try {
- for (Map models : ModelEntity.ENTITIES.values()) {
- models.values().forEach(ModelEntity::teleportToModel);
- }
- } catch (Throwable t) {
- t.printStackTrace();
- }
- }, 10, entityPositionUpdatePeriod, TimeUnit.MILLISECONDS);
+ loadHooks();
+ loadManagers();
+ loadRunnables();
- */
+ PacketEvents.getAPI().getEventManager().registerListener(new MountPacketListener(this), PacketListenerPriority.NORMAL);
- reload();
- getCommand("geysermodelengine").setExecutor(new ReloadCommand(this));
- Bukkit.getPluginManager().registerEvents(new ModelListener(), this);
- Bukkit.getScheduler()
- .runTaskLater(GeyserModelEngine.getInstance(), () -> {
+ Bukkit.getPluginManager().registerEvents(new ModelListener(this), this);
+
+ Bukkit.getScheduler().runTaskLater(this, () -> {
for (World world : Bukkit.getWorlds()) {
+
for (Entity entity : world.getEntities()) {
- if (!ModelEntity.ENTITIES.containsKey(entity.getEntityId())) {
+
+ if (!modelManager.getEntitiesCache().containsKey(entity.getEntityId())) {
+
ModeledEntity modeledEntity = ModelEngineAPI.getModeledEntity(entity);
+
if (modeledEntity != null) {
Optional model = modeledEntity.getModels().values().stream().findFirst();
- model.ifPresent(m -> ModelEntity.create(modeledEntity, m));
+ model.ifPresent(m -> modelManager.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 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
public void onDisable() {
PacketEvents.getAPI().terminate();
- for (Map entities : ModelEntity.ENTITIES.values()) {
+
+ for (Map entities : modelManager.getEntitiesCache().values()) {
entities.forEach((model, modelEntity) -> {
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;
+ }
}
diff --git a/src/main/java/re/imc/geysermodelengine/commands/ReloadCommand.java b/src/main/java/re/imc/geysermodelengine/commands/ReloadCommand.java
deleted file mode 100644
index 418b640..0000000
--- a/src/main/java/re/imc/geysermodelengine/commands/ReloadCommand.java
+++ /dev/null
@@ -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;
- }
-}
\ No newline at end of file
diff --git a/src/main/java/re/imc/geysermodelengine/commands/geysermodelenginecommands/GeyserModelEngineReloadCommand.java b/src/main/java/re/imc/geysermodelengine/commands/geysermodelenginecommands/GeyserModelEngineReloadCommand.java
new file mode 100644
index 0000000..628fca1
--- /dev/null
+++ b/src/main/java/re/imc/geysermodelengine/commands/geysermodelenginecommands/GeyserModelEngineReloadCommand.java
@@ -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")));
+ });
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/re/imc/geysermodelengine/listener/ModelListener.java b/src/main/java/re/imc/geysermodelengine/listener/ModelListener.java
index d06f180..519e4ff 100644
--- a/src/main/java/re/imc/geysermodelengine/listener/ModelListener.java
+++ b/src/main/java/re/imc/geysermodelengine/listener/ModelListener.java
@@ -3,7 +3,6 @@ package re.imc.geysermodelengine.listener;
import com.ticxo.modelengine.api.events.AddModelEvent;
import com.ticxo.modelengine.api.events.ModelDismountEvent;
import com.ticxo.modelengine.api.events.ModelMountEvent;
-import com.ticxo.modelengine.api.events.RemoveModelEvent;
import com.ticxo.modelengine.api.model.ActiveModel;
import org.apache.commons.lang3.tuple.Pair;
import org.bukkit.Bukkit;
@@ -12,117 +11,54 @@ import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerJoinEvent;
-import org.bukkit.event.player.PlayerLoginEvent;
import org.bukkit.event.player.PlayerQuitEvent;
import re.imc.geysermodelengine.GeyserModelEngine;
-import re.imc.geysermodelengine.model.ModelEntity;
+import re.imc.geysermodelengine.managers.model.data.ModelEntityData;
import java.util.Map;
public class ModelListener implements Listener {
- @EventHandler(priority = EventPriority.MONITOR)
- public void onAddModel(AddModelEvent event) {
- if (event.isCancelled()) {
- return;
- }
+ private final GeyserModelEngine plugin;
- if (!GeyserModelEngine.getInstance().isInitialized()) {
- return;
- }
- ModelEntity.create(event.getTarget(), event.getModel());
+ public ModelListener(GeyserModelEngine plugin) {
+ this.plugin = plugin;
}
+ @EventHandler(priority = EventPriority.MONITOR)
+ public void onAddModel(AddModelEvent event) {
+ if (event.isCancelled()) return;
- @EventHandler
- public void onRemoveModel(RemoveModelEvent event) {
+ plugin.getModelManager().create(event.getTarget(), event.getModel());
}
@EventHandler(priority = EventPriority.MONITOR)
public void onModelMount(ModelMountEvent event) {
- Map map = ModelEntity.ENTITIES.get(event.getVehicle().getModeledEntity().getBase().getEntityId());
- if (map == null) {
- }
- if (!event.isDriver()) {
- return;
- }
- ModelEntity model = map.get(event.getVehicle());
+ Map map = plugin.getModelManager().getEntitiesCache().get(event.getVehicle().getModeledEntity().getBase().getEntityId());
+ if (!event.isDriver()) return;
+
+ ModelEntityData model = map.get(event.getVehicle());
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)
public void onModelDismount(ModelDismountEvent event) {
if (event.getPassenger() instanceof Player player) {
- GeyserModelEngine.getInstance().getDrivers().remove(player);
+ plugin.getBedrockMountControlManager().getDriversCache().remove(player);
}
}
-
-
-
-
-
- /*
- @EventHandler(priority = EventPriority.MONITOR)
- public void onModelEntityHurt(EntityDamageEvent event) {
- if (event.isCancelled()) {
- return;
- }
-
- Map model = ModelEntity.ENTITIES.get(event.getEntity().getEntityId());
- if (model != null) {
- for (Map.Entry 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 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 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().);
- }
- */
+ //TODO Find out why we need this bc uh what?
@EventHandler
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
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);
}
}
diff --git a/src/main/java/re/imc/geysermodelengine/listener/MountPacketListener.java b/src/main/java/re/imc/geysermodelengine/listener/MountPacketListener.java
index 6f83e35..43dafe0 100644
--- a/src/main/java/re/imc/geysermodelengine/listener/MountPacketListener.java
+++ b/src/main/java/re/imc/geysermodelengine/listener/MountPacketListener.java
@@ -14,6 +14,12 @@ import re.imc.geysermodelengine.GeyserModelEngine;
public class MountPacketListener implements PacketListener {
+ private final GeyserModelEngine plugin;
+
+ public MountPacketListener(GeyserModelEngine plugin) {
+ this.plugin = plugin;
+ }
+
@Override
public void onPacketReceive(PacketReceiveEvent event) {
if (event.getPacketType() != PacketType.Play.Client.ENTITY_ACTION) return;
@@ -22,7 +28,7 @@ public class MountPacketListener implements PacketListener {
Player player = event.getPlayer();
WrapperPlayClientEntityAction action = new WrapperPlayClientEntityAction(event);
- Pair seat = GeyserModelEngine.getInstance().getDrivers().get(player);
+ Pair seat = plugin.getBedrockMountControlManager().getDriversCache().get(player);
if (seat == null) return;
if (action.getAction() != WrapperPlayClientEntityAction.Action.START_SNEAKING) return;
diff --git a/src/main/java/re/imc/geysermodelengine/managers/ConfigManager.java b/src/main/java/re/imc/geysermodelengine/managers/ConfigManager.java
new file mode 100644
index 0000000..0d1d8c0
--- /dev/null
+++ b/src/main/java/re/imc/geysermodelengine/managers/ConfigManager.java
@@ -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;
+ }
+}
diff --git a/src/main/java/re/imc/geysermodelengine/managers/bedrock/BedrockMountControlManager.java b/src/main/java/re/imc/geysermodelengine/managers/bedrock/BedrockMountControlManager.java
new file mode 100644
index 0000000..74e1917
--- /dev/null
+++ b/src/main/java/re/imc/geysermodelengine/managers/bedrock/BedrockMountControlManager.java
@@ -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> driversCache = new ConcurrentHashMap<>();
+
+ public ConcurrentHashMap> getDriversCache() {
+ return driversCache;
+ }
+}
diff --git a/src/main/java/re/imc/geysermodelengine/managers/commands/CommandManager.java b/src/main/java/re/imc/geysermodelengine/managers/commands/CommandManager.java
new file mode 100644
index 0000000..fe46a7a
--- /dev/null
+++ b/src/main/java/re/imc/geysermodelengine/managers/commands/CommandManager.java
@@ -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 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 getCommandManagersCache() {
+ return commandManagersCache;
+ }
+}
diff --git a/src/main/java/re/imc/geysermodelengine/managers/commands/CommandManagers.java b/src/main/java/re/imc/geysermodelengine/managers/commands/CommandManagers.java
new file mode 100644
index 0000000..d757bbc
--- /dev/null
+++ b/src/main/java/re/imc/geysermodelengine/managers/commands/CommandManagers.java
@@ -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 getCommands();
+}
diff --git a/src/main/java/re/imc/geysermodelengine/managers/commands/managers/geysermodelengine/GeyserModelEngineCommandManager.java b/src/main/java/re/imc/geysermodelengine/managers/commands/managers/geysermodelengine/GeyserModelEngineCommandManager.java
new file mode 100644
index 0000000..3d13b29
--- /dev/null
+++ b/src/main/java/re/imc/geysermodelengine/managers/commands/managers/geysermodelengine/GeyserModelEngineCommandManager.java
@@ -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 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 getCommands() {
+ return commands;
+ }
+}
diff --git a/src/main/java/re/imc/geysermodelengine/managers/commands/subcommands/SubCommands.java b/src/main/java/re/imc/geysermodelengine/managers/commands/subcommands/SubCommands.java
new file mode 100644
index 0000000..ebdc2a0
--- /dev/null
+++ b/src/main/java/re/imc/geysermodelengine/managers/commands/subcommands/SubCommands.java
@@ -0,0 +1,7 @@
+package re.imc.geysermodelengine.managers.commands.subcommands;
+
+import dev.jorel.commandapi.CommandAPICommand;
+
+public interface SubCommands {
+ CommandAPICommand onCommand();
+}
diff --git a/src/main/java/re/imc/geysermodelengine/managers/model/EntityTaskManager.java b/src/main/java/re/imc/geysermodelengine/managers/model/EntityTaskManager.java
new file mode 100644
index 0000000..0826b3d
--- /dev/null
+++ b/src/main/java/re/imc/geysermodelengine/managers/model/EntityTaskManager.java
@@ -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 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 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 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;
+ }
+}
diff --git a/src/main/java/re/imc/geysermodelengine/managers/model/ModelManager.java b/src/main/java/re/imc/geysermodelengine/managers/model/ModelManager.java
new file mode 100644
index 0000000..f057c09
--- /dev/null
+++ b/src/main/java/re/imc/geysermodelengine/managers/model/ModelManager.java
@@ -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> entitiesCache = new ConcurrentHashMap<>();
+ private final Map 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 map = entitiesCache.computeIfAbsent(id, k -> new HashMap<>());
+
+ for (Map.Entry entry : map.entrySet()) {
+ if (entry.getKey() != model && entry.getKey().getBlueprint().getName().equals(model.getBlueprint().getName())) {
+ return;
+ }
+ }
+
+ map.put(model, modelEntity);
+ }
+
+ public ConcurrentHashMap> getEntitiesCache() {
+ return entitiesCache;
+ }
+
+ public Map getModelEntitiesCache() {
+ return modelEntitiesCache;
+ }
+}
diff --git a/src/main/java/re/imc/geysermodelengine/managers/model/data/ModelEntityData.java b/src/main/java/re/imc/geysermodelengine/managers/model/data/ModelEntityData.java
new file mode 100644
index 0000000..0dccec4
--- /dev/null
+++ b/src/main/java/re/imc/geysermodelengine/managers/model/data/ModelEntityData.java
@@ -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 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 getViewers() {
+ return viewers;
+ }
+
+ public ModeledEntity getModeledEntity() {
+ return modeledEntity;
+ }
+
+ public ActiveModel getActiveModel() {
+ return activeModel;
+ }
+
+ public EntityTaskRunnable getEntityTask() {
+ return entityTask;
+ }
+}
diff --git a/src/main/java/re/imc/geysermodelengine/managers/player/PlayerManager.java b/src/main/java/re/imc/geysermodelengine/managers/player/PlayerManager.java
new file mode 100644
index 0000000..c7a4d7a
--- /dev/null
+++ b/src/main/java/re/imc/geysermodelengine/managers/player/PlayerManager.java
@@ -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 playerJoinedCache = new HashSet<>();
+
+ public HashSet getPlayerJoinedCache() {
+ return playerJoinedCache;
+ }
+}
diff --git a/src/main/java/re/imc/geysermodelengine/managers/server/ServerData.java b/src/main/java/re/imc/geysermodelengine/managers/server/ServerData.java
new file mode 100644
index 0000000..5b6bbfc
--- /dev/null
+++ b/src/main/java/re/imc/geysermodelengine/managers/server/ServerData.java
@@ -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 activeRunnablesCache = new ConcurrentHashMap<>();
+
+ public ConcurrentHashMap getActiveRunnablesCache() {
+ return activeRunnablesCache;
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/re/imc/geysermodelengine/managers/server/ServerManager.java b/src/main/java/re/imc/geysermodelengine/managers/server/ServerManager.java
new file mode 100644
index 0000000..a86d1b6
--- /dev/null
+++ b/src/main/java/re/imc/geysermodelengine/managers/server/ServerManager.java
@@ -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;
+ }
+}
diff --git a/src/main/java/re/imc/geysermodelengine/model/BedrockMountControl.java b/src/main/java/re/imc/geysermodelengine/model/BedrockMountControl.java
deleted file mode 100644
index 83a11c2..0000000
--- a/src/main/java/re/imc/geysermodelengine/model/BedrockMountControl.java
+++ /dev/null
@@ -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 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);
-
-
-
- }
-}
diff --git a/src/main/java/re/imc/geysermodelengine/model/EntityTask.java b/src/main/java/re/imc/geysermodelengine/model/EntityTask.java
deleted file mode 100644
index d7539fa..0000000
--- a/src/main/java/re/imc/geysermodelengine/model/EntityTask.java
+++ /dev/null
@@ -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 lastIntSet = new ConcurrentHashMap<>();
- Cache 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 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 = 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 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 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 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 players, boolean firstSend, String... forceAnims) {
- int entity = model.getEntity().getEntityId();
- Set forceAnimSet = Set.of(forceAnims);
-
- Map boneUpdates = new HashMap<>();
- Map animUpdates = new HashMap<>();
- Set 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 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 lastPlayed = new HashSet<>(lastPlayedAnim.asMap().keySet());
-
- for (Map.Entry 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 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 list = new ArrayList<>(boneUpdates.keySet());
- Collections.sort(list);
-
- for (Player player : players) {
- EntityUtils.sendIntProperties(player, entity, intUpdates);
- }
- }
-
- private void processBone(BlueprintBone bone, Map 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);
- }
-}
diff --git a/src/main/java/re/imc/geysermodelengine/model/ModelEntity.java b/src/main/java/re/imc/geysermodelengine/model/ModelEntity.java
deleted file mode 100644
index 0bb73fe..0000000
--- a/src/main/java/re/imc/geysermodelengine/model/ModelEntity.java
+++ /dev/null
@@ -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> ENTITIES = new ConcurrentHashMap<>();
-
- public static Map MODEL_ENTITIES = new ConcurrentHashMap<>();
-
- private PacketEntity entity;
-
- private final Set 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 map = ENTITIES.computeIfAbsent(id, k -> new HashMap<>());
- for (Map.Entry 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());
- }
-
-
-}
diff --git a/src/main/java/re/imc/geysermodelengine/packet/entity/PacketEntity.java b/src/main/java/re/imc/geysermodelengine/packet/entity/PacketEntity.java
index bfb14c6..f3cf64e 100644
--- a/src/main/java/re/imc/geysermodelengine/packet/entity/PacketEntity.java
+++ b/src/main/java/re/imc/geysermodelengine/packet/entity/PacketEntity.java
@@ -27,14 +27,6 @@ import java.util.concurrent.ThreadLocalRandom;
@Setter
public class PacketEntity {
- public PacketEntity(EntityType type, Set 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 UUID uuid;
private EntityType type;
@@ -44,6 +36,15 @@ public class PacketEntity {
private float headPitch;
private boolean removed = false;
+
+ public PacketEntity(EntityType type, Set 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() {
return location;
}
@@ -51,10 +52,9 @@ public class PacketEntity {
public boolean teleport(@NotNull Location location) {
boolean sent = this.location.getWorld() != location.getWorld() || this.location.distanceSquared(location) > 0.000001;
this.location = location.clone();
- if (sent) {
- sendLocationPacket(viewers);
- // sendHeadRotation(viewers); // TODO
- }
+
+ if (sent) sendLocationPacket(viewers);
+
return true;
}
@@ -73,8 +73,6 @@ public class PacketEntity {
}
public void sendSpawnPacket(Collection 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);
players.forEach(player -> PacketEvents.getAPI().getPlayerManager().sendPacket(player, spawnEntity));
}
@@ -83,13 +81,14 @@ public class PacketEntity {
PacketWrapper> packet;
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)) {
packet = new WrapperPlayServerEntityPositionSync(id, data, false);
} else {
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 players) {
diff --git a/src/main/java/re/imc/geysermodelengine/runnables/BedrockMountControlRunnable.java b/src/main/java/re/imc/geysermodelengine/runnables/BedrockMountControlRunnable.java
new file mode 100644
index 0000000..ad1f9a7
--- /dev/null
+++ b/src/main/java/re/imc/geysermodelengine/runnables/BedrockMountControlRunnable.java
@@ -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 {
+
+ 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 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);
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/src/main/java/re/imc/geysermodelengine/runnables/EntityTaskRunnable.java b/src/main/java/re/imc/geysermodelengine/runnables/EntityTaskRunnable.java
new file mode 100644
index 0000000..bb31aa2
--- /dev/null
+++ b/src/main/java/re/imc/geysermodelengine/runnables/EntityTaskRunnable.java
@@ -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 {
+
+ 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 lastIntSet = new ConcurrentHashMap<>();
+ private final Cache 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 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 players, boolean firstSend, String... forceAnims) {
+ int entity = model.getEntity().getEntityId();
+ Set forceAnimSet = Set.of(forceAnims);
+
+ Map boneUpdates = new HashMap<>();
+ Map animUpdates = new HashMap<>();
+ Set anims = new HashSet<>();
+
+ model.getActiveModel().getBlueprint().getBones().forEach((s, bone) -> processBone(model, bone, boneUpdates));
+
+ AnimationHandler handler = model.getActiveModel().getAnimationHandler();
+ Set 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 lastPlayed = new HashSet<>(lastPlayedAnim.asMap().keySet());
+
+ for (Map.Entry 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 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 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 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 getLastIntSet() {
+ return lastIntSet;
+ }
+
+ public Cache getLastPlayedAnim() {
+ return lastPlayedAnim;
+ }
+}
diff --git a/src/main/java/re/imc/geysermodelengine/runnables/UpdateTaskRunnable.java b/src/main/java/re/imc/geysermodelengine/runnables/UpdateTaskRunnable.java
new file mode 100644
index 0000000..015682b
--- /dev/null
+++ b/src/main/java/re/imc/geysermodelengine/runnables/UpdateTaskRunnable.java
@@ -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 {
+
+ private final GeyserModelEngine plugin;
+
+ public UpdateTaskRunnable(GeyserModelEngine plugin) {
+ this.plugin = plugin;
+ }
+
+ @Override
+ public void accept(ScheduledTask scheduledTask) {
+ try {
+ for (Map models : plugin.getModelManager().getEntitiesCache().values()) {
+ models.values().forEach(model -> model.getEntityTask().updateEntityProperties(model, model.getViewers(), false));
+ }
+ } catch (Throwable err) {
+ throw new RuntimeException(err);
+ }
+ }
+}
diff --git a/src/main/java/re/imc/geysermodelengine/util/BooleanPacker.java b/src/main/java/re/imc/geysermodelengine/util/BooleanPacker.java
index 7b75956..5692a9d 100644
--- a/src/main/java/re/imc/geysermodelengine/util/BooleanPacker.java
+++ b/src/main/java/re/imc/geysermodelengine/util/BooleanPacker.java
@@ -6,25 +6,30 @@ import java.util.List;
import java.util.Map;
public class BooleanPacker {
- public static final int MAX_BOOLEANS = 24;
- public static int booleansToInt(List booleans) {
+ private final int MAX_BOOLEANS = 24;
+
+ public int booleansToInt(List booleans) {
int result = 0;
int i = 1;
+
for (boolean b : booleans) {
if (b) {
result += i;
}
i *= 2;
}
+
return result;
}
- public static int mapBooleansToInt(Map booleanMap) {
+ public int mapBooleansToInt(Map booleanMap) {
int result = 0;
int i = 1;
+
List keys = new ArrayList<>(booleanMap.keySet());
Collections.sort(keys);
+
for (String key : keys) {
if (booleanMap.get(key)) {
result += i;
@@ -34,11 +39,12 @@ public class BooleanPacker {
return result;
}
- public static List booleansToInts(List booleans) {
+ public List booleansToInts(List booleans) {
List results = new ArrayList<>();
int result = 0;
int i = 1;
int i1 = 1;
+
for (boolean b : booleans) {
if (b) {
result += i;
@@ -56,15 +62,15 @@ public class BooleanPacker {
return results;
}
- public static List mapBooleansToInts(Map booleanMap) {
+ public List mapBooleansToInts(Map booleanMap) {
List keys = new ArrayList<>(booleanMap.keySet());
List booleans = new ArrayList<>();
+
Collections.sort(keys);
+
for (String key : keys) {
booleans.add(booleanMap.get(key));
}
return booleansToInts(booleans);
}
-
-
}
diff --git a/src/main/java/re/imc/geysermodelengine/util/ColourUtils.java b/src/main/java/re/imc/geysermodelengine/util/ColourUtils.java
new file mode 100644
index 0000000..5f53f7a
--- /dev/null
+++ b/src/main/java/re/imc/geysermodelengine/util/ColourUtils.java
@@ -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;
+ }
+}
diff --git a/src/main/resources/Lang/messages.yml b/src/main/resources/Lang/messages.yml
new file mode 100644
index 0000000..fdc999a
--- /dev/null
+++ b/src/main/resources/Lang/messages.yml
@@ -0,0 +1,4 @@
+
+commands:
+ reload:
+ successfully-reloaded: "<#55FF55>GeyserModelEngine configuration reloaded!"
\ No newline at end of file
diff --git a/src/main/resources/paper-plugin.yml b/src/main/resources/paper-plugin.yml
new file mode 100644
index 0000000..5a771d5
--- /dev/null
+++ b/src/main/resources/paper-plugin.yml
@@ -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
\ No newline at end of file
diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml
deleted file mode 100644
index 8f31d29..0000000
--- a/src/main/resources/plugin.yml
+++ /dev/null
@@ -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
\ No newline at end of file