54 Commits

Author SHA1 Message Date
xSquishyLiam
0c90fe4809 updated PE to 2.10.1 2025-11-06 00:46:49 +00:00
xSquishyLiam
6892ffb519 removed so unused config options 2025-11-06 00:41:04 +00:00
xSquishyLiam
f93559c822 just testing 2025-11-04 13:57:39 +00:00
xSquishyLiam
468757762f updated BetterModel API? 2025-11-02 16:46:32 +00:00
xSquishyLiam
e7114faf84 updated CommandAPI 2025-10-30 20:04:46 +00:00
xSquishyLiam
63bf26d7a9 updated BetterModel API 2025-10-30 19:51:00 +00:00
xSquishyLiam
a4fba7715d Update config.yml 2025-09-26 04:06:21 +01:00
xSquishyLiam
0176d86233 cleaner config.yml (guess i never pushed this) 2025-09-26 04:05:48 +01:00
xSquishyLiam
79f99ffebe added Damage Tint and comments to interfaces 2025-09-10 22:35:44 +01:00
xSquishyLiam
ae3b67a733 pushing beta to github 2025-09-09 23:41:26 +01:00
xSquishyLiam
8e36bf2829 work in progress 2025-09-07 00:26:16 +01:00
xSquishyLiam
804d2aac04 working on it 2025-09-06 23:24:53 +01:00
xSquishyLiam
b6425ffa51 addedd namespace support 2025-09-01 14:56:01 +01:00
zimzaza4
1c47fe83d2 Merge pull request #53 from xSquishyLiam/main
Updated PE to 2.9.5 & added bstats & fixed build.yml
2025-08-31 04:07:22 +08:00
xSquishyLiam
7bbe66928c added an option to disable bStats 2025-08-30 20:56:17 +01:00
xSquishyLiam
a26ea6e148 updated actions/checkout to v4 2025-08-30 12:59:55 +01:00
xSquishyLiam
d1ef89088c learning workflows 2025-08-30 12:55:22 +01:00
xSquishyLiam
c98e807827 bumped shadow 2025-08-30 12:54:08 +01:00
xSquishyLiam
35131c2d58 i wonder 2025-08-30 12:46:37 +01:00
xSquishyLiam
6651ae99f7 addedd a comment for an idea 2025-08-30 00:49:27 +01:00
xSquishyLiam
ca9a54ae71 added `` to true for floodgate 2025-08-30 00:04:19 +01:00
xSquishyLiam
865f5f567c changed readme a bit, removed the old method 2025-08-30 00:03:33 +01:00
xSquishyLiam
b5d9d9b9ca added info about key.pem 2025-08-29 18:03:14 +01:00
xSquishyLiam
31dac482bd made the info more cleaner 2025-08-29 18:01:59 +01:00
xSquishyLiam
b2bfa2977e addedd floodgate send data to set to true info 2025-08-29 17:52:22 +01:00
xSquishyLiam
84012a23b7 bumped PE to 2.9.5 2025-08-26 01:22:14 +01:00
xSquishyLiam
3943381b2c added bstats 2025-08-21 00:12:29 +01:00
TheLividaProject
42d7ea506f Update build.gradle.kts 2025-07-23 21:36:53 +01:00
zimzaza4
e177c8af74 Merge pull request #49 from TheLividaProject/main
Bumped PacketEvents to 2.9.4
2025-07-23 04:33:42 +08:00
TheLividaProject
2c5dba8097 bumped PacketEvents to 2.9.4 2025-07-22 20:45:31 +01:00
TheLividaProject
2a9fdec8b6 Update build.gradle.kts 2025-07-22 20:17:35 +01:00
TheLividaProject
94df76b9a6 Now renames the jar to the project name and it's version 2025-07-22 18:07:31 +01:00
TheLividaProject
03f27514ef added a comment ModelListener issue in regards of pig spawning (such a thing issue) - mainly for those who want to know why it has a delay on the player join, i didn't know this when forked it and took it off but added it back now and should hopefully fix the issue 2025-07-22 17:23:06 +01:00
TheLividaProject
4679fa6f2b changed a comment about the pig issue 2025-07-22 12:23:07 +01:00
TheLividaProject
db99b2fe00 we love big spawning issue, should be fixed till better solution 2025-07-22 11:03:23 +01:00
TheLividaProject
ffd72b0a56 Merge branch 'GeyserExtensionists:main' into main 2025-07-21 22:40:21 +01:00
TheLividaProject
e83caacd2f nvm 90% sure it works i'm just dumb 2025-07-21 22:39:58 +01:00
TheLividaProject
170e67e059 just testing something 2025-07-21 22:32:45 +01:00
zimzaza4
d150d7e324 Merge pull request #48 from TheLividaProject/main
Updates to 1.21.8
2025-07-22 05:11:03 +08:00
TheLividaProject
db0367537e disables default jar hopefully 2025-07-21 22:05:39 +01:00
TheLividaProject
36ea0e5fa0 Merge branch 'GeyserExtensionists:main' into main 2025-07-21 21:59:54 +01:00
TheLividaProject
13c0b45d2a 1.21.8 support 2025-07-21 21:57:06 +01:00
TheLividaProject
8f67ade51c Added some fixes and improved schedulers - still waiting for 1.21.8 support 2025-07-20 16:51:45 +01:00
TheLividaProject
d0fd719753 updated PE to 2.9.1 and disabled the default jar and only use shaded jar 2025-07-11 10:50:05 +01:00
zimzaza4
f4c5167ef5 Rename .github/workflows/maven.yml to build.yml 2025-07-10 22:04:08 +08:00
zimzaza4
dc4e39506d gradle build 2025-07-10 20:35:06 +08:00
zimzaza4
5fdf93db1e Merge branch 'main' of https://github.com/TheLividaProject/mc-GeyserModelEngine-plugin
# Conflicts:
#	pom.xml
2025-07-10 20:29:36 +08:00
TheLividaProject
16e0dfcdfd Delete .gradle directory - bad bean 2025-07-07 21:07:27 +01:00
TheLividaProject
b1969646e6 Delete build directory - guess i have to yeet it manually ;-; 2025-07-07 21:05:34 +01:00
TheLividaProject
501eaaa37a Sorted .gitignore and added the suggesion from onebeastchris 2025-07-07 20:43:55 +01:00
TheLividaProject
f67ee6d9f8 Updated a cache to use UUID instead of Player 2025-07-04 17:17:25 +01:00
TheLividaProject
3adb965697 updated to 1.21.7 2025-07-04 17:10:24 +01:00
zimzaza4
4d75f0615c Fix maven.yml 2025-06-21 15:50:56 +08:00
zimzaza4
97e5bdefcd Update PacketEvents 2025-06-21 15:47:42 +08:00
96 changed files with 1600 additions and 1237 deletions

View File

@@ -4,14 +4,15 @@ on:
push:
branches:
- main
- bettermodel-support-dev
jobs:
build:
name: Build
runs-on: ubuntu-20.04
runs-on: ubuntu-22.04
steps:
- name: Checkout
uses: actions/checkout@v2
uses: actions/checkout@v4
# Setup JDK
- name: Setup Java JDK
@@ -19,25 +20,19 @@ jobs:
with:
java-version: 17
distribution: 'zulu'
- name: Restore Maven cache
uses: skjolber/maven-cache-github-action@v1
with:
step: restore
# Build
- name: Build with Maven
run: mvn package
- name: Save Maven cache
uses: skjolber/maven-cache-github-action@v1
with:
step: save
- name: Make gradlew executable
run: chmod +x gradlew
- name: Build with Gradle
run: ./gradlew build
- name: Auto release
uses: "marvinpinto/action-automatic-releases@latest"
with:
repo_token: "${{ secrets.GITHUB_TOKEN }}"
repo_token: "${{secrets.GITHUB_TOKEN}}"
automatic_release_tag: latest
prerelease: false
files: |
target/GeyserModelEngine*.jar
build/libs/GeyserModelEngine*.jar

44
.gitignore vendored
View File

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

View File

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

Binary file not shown.

120
.idea/workspace.xml generated
View File

@@ -4,15 +4,15 @@
<option name="autoReloadType" value="SELECTIVE" />
</component>
<component name="ChangeListManager">
<list default="true" id="ff2e9770-ec88-4715-adeb-b9dbda130e1a" name="Changes" comment="">
<change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/main/java/re/imc/geysermodelengine/commands/geysermodelenginecommands/GeyserModelEngineReloadCommand.java" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/java/re/imc/geysermodelengine/commands/geysermodelenginecommands/GeyserModelEngineReloadCommand.java" afterDir="false" />
</list>
<list default="true" id="ff2e9770-ec88-4715-adeb-b9dbda130e1a" name="Changes" comment="" />
<option name="SHOW_DIALOG" value="false" />
<option name="HIGHLIGHT_CONFLICTS" value="true" />
<option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
<option name="LAST_RESOLUTION" value="IGNORE" />
</component>
<component name="DarkyenusTimeTracker">
<option name="totalTimeSeconds" value="1293" />
</component>
<component name="ExternalProjectsData">
<projectState path="$PROJECT_DIR$">
<ProjectState />
@@ -60,9 +60,13 @@
<component name="Git.Settings">
<option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$" />
</component>
<component name="GradleScriptDefinitionsStorage" workingDir="$PROJECT_DIR$" gradleHome="C:\Users\Livid\.gradle\wrapper\dists\gradle-8.12-bin\cetblhg4pflnnks72fxwobvgv\gradle-8.12" javaHome="C:\Users\Livid\.jdks\openjdk-21.0.1" gradleVersion="8.12" />
<component name="MavenRunner">
<option name="delegateBuildToMaven" value="true" />
</component>
<component name="ProblemsViewState">
<option name="selectedTabId" value="DEPENDENCY_CHECKER_PROBLEMS_TAB" />
</component>
<component name="ProjectColorInfo">{
&quot;associatedIndex&quot;: 8
}</component>
@@ -76,13 +80,21 @@
</component>
<component name="PropertiesComponent">{
&quot;keyToString&quot;: {
&quot;Gradle.Build GeyserModelEngine.executor&quot;: &quot;Run&quot;,
&quot;Gradle.Download Sources.executor&quot;: &quot;Run&quot;,
&quot;Gradle.GeyserModelEngine [buildDependents].executor&quot;: &quot;Run&quot;,
&quot;Gradle.GeyserModelEngine [buildNeeded].executor&quot;: &quot;Run&quot;,
&quot;Gradle.GeyserModelEngine [build].executor&quot;: &quot;Run&quot;,
&quot;Gradle.GeyserModelEngine [clean].executor&quot;: &quot;Run&quot;,
&quot;Gradle.GeyserModelEngine [jar].executor&quot;: &quot;Run&quot;,
&quot;Maven.GeyserModelEngine [install...].executor&quot;: &quot;Run&quot;,
&quot;Maven.GeyserModelEngine [install].executor&quot;: &quot;Run&quot;,
&quot;ModuleVcsDetector.initialDetectionPerformed&quot;: &quot;true&quot;,
&quot;RunOnceActivity.ShowReadmeOnStart&quot;: &quot;true&quot;,
&quot;RunOnceActivity.TerminalTabsStorage.copyFrom.TerminalArrangementManager.252&quot;: &quot;true&quot;,
&quot;RunOnceActivity.git.unshallow&quot;: &quot;true&quot;,
&quot;git-widget-placeholder&quot;: &quot;main&quot;,
&quot;git-widget-placeholder&quot;: &quot;bettermodel-support-dev&quot;,
&quot;kotlin-language-version-configured&quot;: &quot;true&quot;,
&quot;last_opened_file_path&quot;: &quot;D:/Coding/Forks/Minecraft/GeyserModelEngine&quot;,
&quot;project.structure.last.edited&quot;: &quot;Project&quot;,
&quot;project.structure.proportion&quot;: &quot;0.0&quot;,
@@ -94,8 +106,100 @@
<key name="CopyFile.RECENT_KEYS">
<recent name="D:\Coding\Forks\Minecraft\GeyserModelEngine" />
</key>
<key name="MoveFile.RECENT_KEYS">
<recent name="D:\Coding\Forks\Minecraft\GeyserModelEngine\.github\workflows" />
<recent name="D:\Coding\Forks\Minecraft\GeyserModelEngine\libs" />
</key>
</component>
<component name="RunManager">
<component name="RunManager" selected="Gradle.GeyserModelEngine [build]">
<configuration name="GeyserModelEngine [buildDependents]" type="GradleRunConfiguration" factoryName="Gradle" temporary="true">
<ExternalSystemSettings>
<option name="executionName" />
<option name="externalProjectPath" value="$PROJECT_DIR$" />
<option name="externalSystemIdString" value="GRADLE" />
<option name="scriptParameters" />
<option name="taskDescriptions">
<list />
</option>
<option name="taskNames">
<list>
<option value="buildDependents" />
</list>
</option>
<option name="vmOptions" />
</ExternalSystemSettings>
<ExternalSystemDebugServerProcess>true</ExternalSystemDebugServerProcess>
<ExternalSystemReattachDebugProcess>true</ExternalSystemReattachDebugProcess>
<DebugAllEnabled>false</DebugAllEnabled>
<RunAsTest>false</RunAsTest>
<method v="2" />
</configuration>
<configuration name="GeyserModelEngine [buildNeeded]" type="GradleRunConfiguration" factoryName="Gradle" temporary="true">
<ExternalSystemSettings>
<option name="executionName" />
<option name="externalProjectPath" value="$PROJECT_DIR$" />
<option name="externalSystemIdString" value="GRADLE" />
<option name="scriptParameters" />
<option name="taskDescriptions">
<list />
</option>
<option name="taskNames">
<list>
<option value="buildNeeded" />
</list>
</option>
<option name="vmOptions" />
</ExternalSystemSettings>
<ExternalSystemDebugServerProcess>true</ExternalSystemDebugServerProcess>
<ExternalSystemReattachDebugProcess>true</ExternalSystemReattachDebugProcess>
<DebugAllEnabled>false</DebugAllEnabled>
<RunAsTest>false</RunAsTest>
<method v="2" />
</configuration>
<configuration name="GeyserModelEngine [build]" type="GradleRunConfiguration" factoryName="Gradle" temporary="true">
<ExternalSystemSettings>
<option name="executionName" />
<option name="externalProjectPath" value="$PROJECT_DIR$" />
<option name="externalSystemIdString" value="GRADLE" />
<option name="scriptParameters" />
<option name="taskDescriptions">
<list />
</option>
<option name="taskNames">
<list>
<option value="build" />
</list>
</option>
<option name="vmOptions" />
</ExternalSystemSettings>
<ExternalSystemDebugServerProcess>true</ExternalSystemDebugServerProcess>
<ExternalSystemReattachDebugProcess>true</ExternalSystemReattachDebugProcess>
<DebugAllEnabled>false</DebugAllEnabled>
<RunAsTest>false</RunAsTest>
<method v="2" />
</configuration>
<configuration name="GeyserModelEngine [clean]" type="GradleRunConfiguration" factoryName="Gradle" temporary="true">
<ExternalSystemSettings>
<option name="executionName" />
<option name="externalProjectPath" value="$PROJECT_DIR$" />
<option name="externalSystemIdString" value="GRADLE" />
<option name="scriptParameters" />
<option name="taskDescriptions">
<list />
</option>
<option name="taskNames">
<list>
<option value="clean" />
</list>
</option>
<option name="vmOptions" />
</ExternalSystemSettings>
<ExternalSystemDebugServerProcess>true</ExternalSystemDebugServerProcess>
<ExternalSystemReattachDebugProcess>true</ExternalSystemReattachDebugProcess>
<DebugAllEnabled>false</DebugAllEnabled>
<RunAsTest>false</RunAsTest>
<method v="2" />
</configuration>
<configuration name="GeyserModelEngine [jar]" type="GradleRunConfiguration" factoryName="Gradle" temporary="true">
<ExternalSystemSettings>
<option name="executionName" />
@@ -120,7 +224,11 @@
</configuration>
<recent_temporary>
<list>
<item itemvalue="Gradle.GeyserModelEngine [build]" />
<item itemvalue="Gradle.GeyserModelEngine [clean]" />
<item itemvalue="Gradle.GeyserModelEngine [jar]" />
<item itemvalue="Gradle.GeyserModelEngine [buildDependents]" />
<item itemvalue="Gradle.GeyserModelEngine [buildNeeded]" />
</list>
</recent_temporary>
</component>

View File

@@ -20,27 +20,22 @@ Download the following plugins according to what server software you use.
- Put `GeyserModelEngine` in the plugins folder (only Spigot or forks of Spigot supported)
- Put either `geyserutils-spigot` in your plugins folder aswell (`geyserutils-velocity`/`geyserutils-bungeecord` in your Velocity/Bungeecord plugins folder if you use it)
- Put `GeyserModelEnginePackGenerator` and `geyserutils-geyser` into `plugins/[Geyser-Folder]/extensions`
- Inside `floodgate` set `send-floodgate-data` to `true` in your Velocity/Bungeecord folder and copy over the key.pem into your backend `floodgate` folders
Start the server to generate the relevant configuration files, and then shut down the server to convert any models.
# Convert Models
This is old method to convert model:
`GeyserModelEnginePackGenerator` is capable of generating models all by itself. After generating it will also apply this pack automatically.
- First go to `plugins/[Geyser-Folder]/extensions/geysermodelenginepackgenerator/input/`
- Create a folder in this directory with the ID of the model. (this is the same name as your model within ModelEngine 4.)
- Firstly, install [packer plugin](https://github.com/GeyserExtensionists/GeyserModelEngineBlockbenchPacker) for your blockbench.
- Then, open your bbmodel, go `File -> Export -> Export GeyserModelEngine Model`, you will get a zip, just unzip it to `input` folder.
<img src="docsimg/example.jpg" width="500">
> Each model should have a separate model folder
> Subfolders are supported if you want to categorize them
- Now use BlockBench and convert your model to a Bedrock Entity, this will allow you to export the Bedrock Geometry and Animations.
- Put the geometry, animations and texture file in this folder you've made.
<img src="docsimg/example1.jpg" width="500">
- Restart the server or reload geyser to start generating the resource pack.
- Go to `plugins/[Geyser-Folder]/extensions/geysermodelenginepackgenerator`, and you should see your pack generated!
@@ -49,12 +44,6 @@ This is old method to convert model:
- Final step, reload Geyser or restart the server to load the resource pack.
- Congratulations, you've completed this tutorial!
# Model Packer
This is new way to convert model
- Firstly, install [packer plugin](https://github.com/GeyserExtensionists/GeyserModelEngineBlockbenchPacker) for your blockbench.
- Then, open your bbmodel, go `File -> Export -> Export GeyserModelEngine Model`, you will get a zip, just unzip it to `input` folder.
# Tips
* Pay attention! The pack only regenerates when the number of models changes, you can technically speaking remove the generated_pack folder to force a reload aswell.

View File

@@ -1,6 +1,6 @@
plugins {
id("java")
id("io.github.goooler.shadow") version "8.1.7"
id("io.github.goooler.shadow") version "8.1.8"
}
group = "re.imc"
@@ -13,24 +13,24 @@ repositories {
maven("https://mvn.lumine.io/repository/maven-public/")
maven("https://repo.opencollab.dev/maven-releases/")
maven("https://repo.opencollab.dev/maven-snapshots/")
maven("https://repo.opencollab.dev/main/")
maven("https://repo.codemc.io/repository/maven-public/")
maven("https://repo.codemc.io/repository/maven-releases/")
}
dependencies {
compileOnly("io.papermc.paper:paper-api:1.21.7-R0.1-SNAPSHOT")
compileOnly("io.papermc.paper:paper-api:1.21.10-R0.1-SNAPSHOT")
implementation("dev.jorel:commandapi-paper-shade:11.0.0")
implementation("dev.jorel:commandapi-bukkit-shade-mojang-mapped:10.1.1")
compileOnly("com.ticxo.modelengine:ModelEngine:R4.0.4")
compileOnly("com.ticxo.modelengine:ModelEngine:R4.0.9")
compileOnly("io.github.toxicity188:bettermodel:1.14.0")
compileOnly(files("libs/geyserutils-spigot-1.0-SNAPSHOT.jar"))
compileOnly("org.geysermc.floodgate:api:2.2.4-SNAPSHOT")
implementation("com.github.retrooper:packetevents-spigot:2.9.0-SNAPSHOT")
implementation("com.github.retrooper:packetevents-spigot:2.10.1")
implementation("org.bstats:bstats-bukkit:3.0.2")
implementation("org.reflections:reflections:0.10.2")
}
@@ -44,14 +44,18 @@ tasks.compileJava {
}
tasks.shadowJar {
archiveFileName.set("${rootProject.name}-${version}.jar")
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.bstats", "re.imc.geysermodelengine.libs.bstats")
relocate("org.reflections", "re.imc.geysermodelengine.libs.reflections")
}
tasks.jar {
dependsOn(tasks.shadowJar)
tasks.build {
dependsOn("shadowJar")
}

File diff suppressed because one or more lines are too long

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -2,10 +2,11 @@ package re.imc.geysermodelengine;
import com.github.retrooper.packetevents.PacketEvents;
import com.github.retrooper.packetevents.event.PacketListenerPriority;
import com.ticxo.modelengine.api.model.ActiveModel;
import dev.jorel.commandapi.CommandAPI;
import dev.jorel.commandapi.CommandAPIBukkitConfig;
import dev.jorel.commandapi.CommandAPIPaperConfig;
import io.github.retrooper.packetevents.factory.spigot.SpigotPacketEventsBuilder;
import org.bstats.bukkit.Metrics;
import org.bukkit.Bukkit;
import org.bukkit.plugin.java.JavaPlugin;
import re.imc.geysermodelengine.listener.ModelListener;
@@ -14,11 +15,9 @@ import re.imc.geysermodelengine.managers.ConfigManager;
import re.imc.geysermodelengine.managers.commands.CommandManager;
import re.imc.geysermodelengine.managers.model.EntityTaskManager;
import re.imc.geysermodelengine.managers.model.ModelManager;
import re.imc.geysermodelengine.managers.model.data.ModelEntityData;
import re.imc.geysermodelengine.runnables.BedrockMountControlRunnable;
import re.imc.geysermodelengine.runnables.UpdateTaskRunnable;
import java.util.*;
import java.util.concurrent.*;
public class GeyserModelEngine extends JavaPlugin {
@@ -30,12 +29,14 @@ public class GeyserModelEngine extends JavaPlugin {
private ModelManager modelManager;
private EntityTaskManager entityTaskManager;
private ScheduledExecutorService schedulerPool;
@Override
public void onLoad() {
PacketEvents.setAPI(SpigotPacketEventsBuilder.build(this));
PacketEvents.getAPI().load();
CommandAPI.onLoad(new CommandAPIBukkitConfig(this));
CommandAPI.onLoad(new CommandAPIPaperConfig(this));
}
@Override
@@ -44,6 +45,8 @@ public class GeyserModelEngine extends JavaPlugin {
loadManagers();
loadRunnables();
loadBStats();
PacketEvents.getAPI().getEventManager().registerListener(new MountPacketListener(this), PacketListenerPriority.NORMAL);
Bukkit.getPluginManager().registerEvents(new ModelListener(this), this);
@@ -51,14 +54,9 @@ public class GeyserModelEngine extends JavaPlugin {
@Override
public void onDisable() {
this.modelManager.removeEntities();
PacketEvents.getAPI().terminate();
for (Map<ActiveModel, ModelEntityData> entities : modelManager.getEntitiesCache().values()) {
entities.forEach((model, modelEntity) -> {
modelEntity.getEntity().remove();
});
}
CommandAPI.onDisable();
}
@@ -67,6 +65,10 @@ public class GeyserModelEngine extends JavaPlugin {
CommandAPI.onEnable();
}
private void loadBStats() {
if (configManager.getConfig().getBoolean("metrics.bstats", true)) new Metrics(this, 26981);
}
private void loadManagers() {
this.configManager = new ConfigManager(this);
@@ -77,7 +79,9 @@ public class GeyserModelEngine extends JavaPlugin {
}
private void loadRunnables() {
Bukkit.getAsyncScheduler().runAtFixedRate(this, new UpdateTaskRunnable(this), 10, configManager.getConfig().getLong("entity-position-update-period", 35), TimeUnit.MILLISECONDS);
this.schedulerPool = Executors.newScheduledThreadPool(configManager.getConfig().getInt("models.thread-pool-size", 4));
Bukkit.getAsyncScheduler().runAtFixedRate(this, new UpdateTaskRunnable(this), 10, configManager.getConfig().getLong("models.entity-position-update-period", 35), TimeUnit.MILLISECONDS);
Bukkit.getAsyncScheduler().runAtFixedRate(this, new BedrockMountControlRunnable(this), 1, 1, TimeUnit.MILLISECONDS);
}
@@ -96,4 +100,8 @@ public class GeyserModelEngine extends JavaPlugin {
public EntityTaskManager getEntityTaskManager() {
return entityTaskManager;
}
public ScheduledExecutorService getSchedulerPool() {
return schedulerPool;
}
}

View File

@@ -0,0 +1,38 @@
package re.imc.geysermodelengine.listener;
import kr.toxicity.model.api.event.CreateEntityTrackerEvent;
import org.bukkit.entity.Entity;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.entity.EntityDamageByEntityEvent;
import re.imc.geysermodelengine.GeyserModelEngine;
import re.imc.geysermodelengine.managers.model.entity.BetterModelEntityData;
import re.imc.geysermodelengine.managers.model.model.Model;
public class BetterModelListener implements Listener {
private final GeyserModelEngine plugin;
public BetterModelListener(GeyserModelEngine plugin) {
this.plugin = plugin;
}
@EventHandler(priority = EventPriority.MONITOR)
public void onModelSpawn(CreateEntityTrackerEvent event) {
plugin.getModelManager().getModelHandler().createModel(event.sourceEntity(), event.getTracker(), event.tracker());
}
@EventHandler
public void onModelDamage(EntityDamageByEntityEvent event) {
Entity entity = event.getEntity();
Model model = plugin.getModelManager().getModelEntitiesCache().get(entity.getEntityId());
if (model == null) return;
BetterModelEntityData entityData = (BetterModelEntityData) model.getEntityData();
entityData.setHurt(true);
}
}

View File

@@ -0,0 +1,58 @@
package re.imc.geysermodelengine.listener;
import com.ticxo.modelengine.api.events.*;
import com.ticxo.modelengine.api.model.ActiveModel;
import org.apache.commons.lang3.tuple.Pair;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import re.imc.geysermodelengine.GeyserModelEngine;
import re.imc.geysermodelengine.managers.model.entity.EntityData;
import re.imc.geysermodelengine.managers.model.model.Model;
import java.util.Map;
public class ModelEngineListener implements Listener {
private final GeyserModelEngine plugin;
public ModelEngineListener(GeyserModelEngine plugin) {
this.plugin = plugin;
}
@EventHandler(priority = EventPriority.MONITOR)
public void onAddModel(AddModelEvent event) {
if (event.isCancelled()) return;
plugin.getModelManager().getModelHandler().createModel(event.getTarget(), event.getModel());
}
// Needs Testing
@EventHandler(priority = EventPriority.MONITOR)
public void onModelMount(ModelMountEvent event) {
if (!event.isDriver()) return;
ActiveModel activeModel = event.getVehicle();
if (activeModel == null) return;
int entityID = activeModel.getModeledEntity().getBase().getEntityId();
Map<Model, EntityData> entityDataCache = plugin.getModelManager().getEntitiesCache().get(entityID);
if (entityDataCache == null) return;
Model model = plugin.getModelManager().getModelEntitiesCache().get(entityID);
EntityData entityData = entityDataCache.get(model);
if (entityData != null && event.getPassenger() instanceof Player player) {
plugin.getModelManager().getDriversCache().put(player.getUniqueId(), Pair.of(event.getVehicle(), event.getSeat()));
}
}
@EventHandler(priority = EventPriority.MONITOR)
public void onModelDismount(ModelDismountEvent event) {
if (event.getPassenger() instanceof Player player) {
plugin.getModelManager().getDriversCache().remove(player.getUniqueId());
}
}
}

View File

@@ -1,24 +1,15 @@
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.model.ActiveModel;
import org.apache.commons.lang3.tuple.Pair;
import org.bukkit.Bukkit;
import org.bukkit.World;
import org.bukkit.entity.Player;
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.PlayerQuitEvent;
import org.bukkit.event.world.WorldInitEvent;
import org.bukkit.event.world.WorldLoadEvent;
import org.geysermc.floodgate.api.FloodgateApi;
import re.imc.geysermodelengine.GeyserModelEngine;
import re.imc.geysermodelengine.managers.model.data.ModelEntityData;
import java.util.Map;
public class ModelListener implements Listener {
@@ -28,47 +19,31 @@ public class ModelListener implements Listener {
this.plugin = plugin;
}
@EventHandler(priority = EventPriority.MONITOR)
public void onAddModel(AddModelEvent event) {
if (event.isCancelled()) return;
plugin.getModelManager().create(event.getTarget(), event.getModel());
}
@EventHandler(priority = EventPriority.MONITOR)
public void onModelMount(ModelMountEvent event) {
Map<ActiveModel, ModelEntityData> 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) {
plugin.getModelManager().getDriversCache().put(player, Pair.of(event.getVehicle(), event.getSeat()));
}
}
@EventHandler(priority = EventPriority.MONITOR)
public void onModelDismount(ModelDismountEvent event) {
if (event.getPassenger() instanceof Player player) {
plugin.getModelManager().getDriversCache().remove(player);
}
}
/*
/ xSquishyLiam:
/ May change this into a better system?
*/
@EventHandler
public void onWorldInit(WorldInitEvent event) {
World world = event.getWorld();
world.getEntities().forEach(entity -> plugin.getModelManager().processEntities(entity));
world.getEntities().forEach(entity -> plugin.getModelManager().getModelHandler().processEntities(entity));
}
/*
/ xSquishyLiam:
/ A runDelay makes sure the client doesn't see pigs on login due to the client resyncing themselves back to normal
*/
@EventHandler
public void onPlayerJoin(PlayerJoinEvent event) {
Player player = event.getPlayer();
plugin.getModelManager().getPlayerJoinedCache().add(player.getUniqueId());
if (!FloodgateApi.getInstance().isFloodgatePlayer(player.getUniqueId())) return;
Bukkit.getGlobalRegionScheduler().runDelayed(plugin, scheduledTask -> plugin.getModelManager().getPlayerJoinedCache().add(player.getUniqueId()), 10);
}
@EventHandler
public void onPlayerQuit(PlayerQuitEvent event) {
Player player = event.getPlayer();
if (!FloodgateApi.getInstance().isFloodgatePlayer(player.getUniqueId())) return;
plugin.getModelManager().getPlayerJoinedCache().remove(player.getUniqueId());
}
}

View File

@@ -28,11 +28,11 @@ public class MountPacketListener implements PacketListener {
Player player = event.getPlayer();
WrapperPlayClientEntityAction action = new WrapperPlayClientEntityAction(event);
Pair<ActiveModel, Mount> seat = plugin.getModelManager().getDriversCache().get(player);
Pair<ActiveModel, Mount> seat = plugin.getModelManager().getDriversCache().get(player.getUniqueId());
if (seat == null) return;
if (action.getAction() != WrapperPlayClientEntityAction.Action.START_SNEAKING) return;
ModelEngineAPI.getMountPairManager().tryDismount(event.getPlayer());
ModelEngineAPI.getMountPairManager().tryDismount(player);
}
}

View File

@@ -21,8 +21,8 @@ public class CommandManager {
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);
plugin.getLogger().info("Loading Command Manager - " + commandManager.getName());
commandManagersCache.put(commandManager.getName(), commandManager);
} catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException err) {
plugin.getLogger().severe("Failed to load Command Manager " + clazz.getName());
throw new RuntimeException(err);

View File

@@ -6,7 +6,13 @@ import java.util.ArrayList;
public interface CommandManagers {
String name();
/**
* Gets the name of the command manager
*/
String getName();
/**
* Gets the command manager subcommands
*/
ArrayList<SubCommands> getCommands();
}

View File

@@ -19,7 +19,7 @@ public class GeyserModelEngineCommandManager implements CommandManagers {
}
private void registerCommand() {
CommandAPICommand geyserModelEngineCommand = new CommandAPICommand(name());
CommandAPICommand geyserModelEngineCommand = new CommandAPICommand(getName());
commands.forEach(subCommands -> geyserModelEngineCommand.withSubcommand(subCommands.onCommand()));
@@ -27,7 +27,7 @@ public class GeyserModelEngineCommandManager implements CommandManagers {
}
@Override
public String name() {
public String getName() {
return "geysermodelengine";
}

View File

@@ -3,5 +3,9 @@ package re.imc.geysermodelengine.managers.commands.subcommands;
import dev.jorel.commandapi.CommandAPICommand;
public interface SubCommands {
/**
* Subcommand setup
*/
CommandAPICommand onCommand();
}

View File

@@ -1,111 +1,68 @@
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.managers.model.entity.EntityData;
import re.imc.geysermodelengine.managers.model.propertyhandler.BetterModelPropertyHandler;
import re.imc.geysermodelengine.managers.model.propertyhandler.ModelEnginePropertyHandler;
import re.imc.geysermodelengine.managers.model.propertyhandler.PropertyHandler;
import re.imc.geysermodelengine.managers.model.entity.ModelEngineEntityData;
import re.imc.geysermodelengine.managers.model.taskshandler.TaskHandler;
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;
private PropertyHandler propertyHandler;
public EntityTaskManager(GeyserModelEngine plugin) {
this.plugin = plugin;
try {
this.scaleMethod = ActiveModel.class.getMethod("getScale");
} catch (NoSuchMethodException err) {
throw new RuntimeException(err);
if (Bukkit.getPluginManager().getPlugin("ModelEngine") != null) {
this.propertyHandler = new ModelEnginePropertyHandler(plugin);
plugin.getLogger().info("Using ModelEngine property handler!");
} else if (Bukkit.getPluginManager().getPlugin("BetterModel") != null) {
this.propertyHandler = new BetterModelPropertyHandler(plugin);
plugin.getLogger().info("Using BetterModel property handler!");
} else {
plugin.getLogger().severe("No supported model engine found!");
plugin.getServer().getPluginManager().disablePlugin(plugin);
}
}
public String unstripName(BlueprintBone bone) {
String name = bone.getName();
if (bone.getBehaviors().get("head") != null) {
if (!bone.getBehaviors().get("head").isEmpty()) return "hi_" + name;
return "h_" + name;
}
return name;
}
public void sendScale(ModelEntityData model, Collection<Player> players, float lastScale, boolean firstSend) {
try {
if (players.isEmpty()) return;
Vector3fc scale = (Vector3fc) scaleMethod.invoke(model.getActiveModel());
float average = (scale.x() + scale.y() + scale.z()) / 3;
if (!firstSend) {
if (average == lastScale) return;
}
for (Player player : players) {
EntityUtils.sendCustomScale(player, model.getEntity().getEntityId(), average);
}
} catch (Throwable t) {
// ignore
}
}
public void sendColor(ModelEntityData model, Collection<Player> players, Color lastColor, boolean firstSend) {
if (players.isEmpty()) return;
Color color = new Color(model.getActiveModel().getDefaultTint().asARGB());
if (model.getActiveModel().isMarkedHurt()) color = new Color(model.getActiveModel().getDamageTint().asARGB());
if (firstSend) {
if (color.equals(lastColor)) return;
}
for (Player player : players) {
EntityUtils.sendCustomColor(player, model.getEntity().getEntityId(), color);
}
}
public void checkViewers(ModelEntityData model, Set<Player> viewers) {
public void checkViewers(EntityData model, Set<Player> viewers) {
for (Player onlinePlayer : Bukkit.getOnlinePlayers()) {
if (FloodgateApi.getInstance().isFloodgatePlayer(onlinePlayer.getUniqueId())) {
if (!FloodgateApi.getInstance().isFloodgatePlayer(onlinePlayer.getUniqueId())) continue;
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);
}
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();
private void sendSpawnPacket(EntityData model, Player onlinePlayer) {
TaskHandler task = model.getEntityTask();
boolean firstJoined = !plugin.getModelManager().getPlayerJoinedCache().contains(onlinePlayer.getUniqueId());
if (firstJoined) {
task.sendEntityData(model, onlinePlayer, plugin.getConfigManager().getConfig().getInt("join-send-delay") / 50);
task.sendEntityData(model, onlinePlayer, plugin.getConfigManager().getConfig().getInt("models.join-send-delay") / 50);
} else {
task.sendEntityData(model, onlinePlayer, 5);
}
@@ -126,31 +83,20 @@ public class EntityTaskManager {
return true;
}
public void sendHitBoxToAll(ModelEntityData model) {
public void sendHitBoxToAll(EntityData 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) {
//TODO move this
public boolean hasAnimation(ModelEngineEntityData model, String animation) {
ActiveModel activeModel = model.getActiveModel();
BlueprintAnimation animationProperty = activeModel.getBlueprint().getAnimations().get(animation);
return !(animationProperty == null);
}
public Method getScaleMethod() {
return scaleMethod;
public PropertyHandler getPropertyHandler() {
return propertyHandler;
}
}

View File

@@ -1,14 +1,15 @@
package re.imc.geysermodelengine.managers.model;
import com.ticxo.modelengine.api.ModelEngineAPI;
import com.ticxo.modelengine.api.model.ActiveModel;
import com.ticxo.modelengine.api.model.ModeledEntity;
import com.ticxo.modelengine.api.model.bone.type.Mount;
import org.apache.commons.lang3.tuple.Pair;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Player;
import org.bukkit.Bukkit;
import re.imc.geysermodelengine.GeyserModelEngine;
import re.imc.geysermodelengine.managers.model.data.ModelEntityData;
import re.imc.geysermodelengine.managers.model.entity.EntityData;
import re.imc.geysermodelengine.managers.model.model.Model;
import re.imc.geysermodelengine.managers.model.modelhandler.BetterModelHandler;
import re.imc.geysermodelengine.managers.model.modelhandler.ModelEngineHandler;
import re.imc.geysermodelengine.managers.model.modelhandler.ModelHandler;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
@@ -17,55 +18,57 @@ public class ModelManager {
private final GeyserModelEngine plugin;
private ModelHandler modelHandler;
private final HashSet<UUID> playerJoinedCache = new HashSet<>();
private final ConcurrentHashMap<Integer, Map<ActiveModel, ModelEntityData>> entitiesCache = new ConcurrentHashMap<>();
private final ConcurrentHashMap<Integer, ModelEntityData> modelEntitiesCache = new ConcurrentHashMap<>();
private final ConcurrentHashMap<Integer, Model> modelEntitiesCache = new ConcurrentHashMap<>();
private final ConcurrentHashMap<Integer, Map<Model, EntityData>> entitiesCache = new ConcurrentHashMap<>();
private final ConcurrentHashMap<Player, Pair<ActiveModel, Mount>> driversCache = new ConcurrentHashMap<>();
// MEG ONLY
private final ConcurrentHashMap<UUID, Pair<ActiveModel, Mount>> driversCache = new ConcurrentHashMap<>();
public ModelManager(GeyserModelEngine plugin) {
this.plugin = plugin;
}
public void create(ModeledEntity entity, ActiveModel model) {
ModelEntityData modelEntity = new ModelEntityData(plugin, entity, model);
int id = entity.getBase().getEntityId();
Map<ActiveModel, ModelEntityData> map = entitiesCache.computeIfAbsent(id, k -> new HashMap<>());
for (Map.Entry<ActiveModel, ModelEntityData> entry : map.entrySet()) {
if (entry.getKey() != model && entry.getKey().getBlueprint().getName().equals(model.getBlueprint().getName())) {
return;
}
if (Bukkit.getPluginManager().getPlugin("ModelEngine") != null) {
this.modelHandler = new ModelEngineHandler(plugin);
plugin.getLogger().info("Using ModelEngine handler!");
} else if (Bukkit.getPluginManager().getPlugin("BetterModel") != null) {
this.modelHandler = new BetterModelHandler(plugin);
plugin.getLogger().info("Using BetterModel handler!");
} else {
plugin.getLogger().severe("No supported model engine found!");
plugin.getServer().getPluginManager().disablePlugin(plugin);
return;
}
map.put(model, modelEntity);
modelHandler.loadListeners();
}
public void processEntities(Entity entity) {
if (entitiesCache.containsKey(entity.getEntityId())) return;
public void removeEntities() {
for (Map<Model, EntityData> entities : entitiesCache.values()) {
entities.forEach((model, modelEntity) -> modelEntity.getEntity().remove());
}
}
ModeledEntity modeledEntity = ModelEngineAPI.getModeledEntity(entity);
if (modeledEntity == null) return;
Optional<ActiveModel> model = modeledEntity.getModels().values().stream().findFirst();
model.ifPresent(m -> create(modeledEntity, m));
public ModelHandler getModelHandler() {
return modelHandler;
}
public HashSet<UUID> getPlayerJoinedCache() {
return playerJoinedCache;
}
public ConcurrentHashMap<Integer, Map<ActiveModel, ModelEntityData>> getEntitiesCache() {
public ConcurrentHashMap<Integer, Map<Model, EntityData>> getEntitiesCache() {
return entitiesCache;
}
public ConcurrentHashMap<Integer, ModelEntityData> getModelEntitiesCache() {
public ConcurrentHashMap<Integer, Model> getModelEntitiesCache() {
return modelEntitiesCache;
}
public ConcurrentHashMap<Player, Pair<ActiveModel, Mount>> getDriversCache() {
public ConcurrentHashMap<UUID, Pair<ActiveModel, Mount>> getDriversCache() {
return driversCache;
}
}

View File

@@ -0,0 +1,86 @@
package re.imc.geysermodelengine.managers.model.entity;
import com.github.retrooper.packetevents.protocol.entity.type.EntityTypes;
import com.google.common.collect.Sets;
import kr.toxicity.model.api.tracker.EntityTracker;
import kr.toxicity.model.api.tracker.Tracker;
import org.bukkit.Location;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Player;
import re.imc.geysermodelengine.GeyserModelEngine;
import re.imc.geysermodelengine.managers.model.taskshandler.BetterModelTaskHandler;
import re.imc.geysermodelengine.packet.entity.PacketEntity;
import java.util.Set;
public class BetterModelEntityData implements EntityData {
private final GeyserModelEngine plugin;
private final PacketEntity entity;
private final Set<Player> viewers = Sets.newConcurrentHashSet();
private final Entity entitySource;
private final Tracker tracker;
private final EntityTracker entityTracker;
private BetterModelTaskHandler entityTask;
private boolean hurt;
public BetterModelEntityData(GeyserModelEngine plugin, Entity entitySource, Tracker tracker, EntityTracker entityTracker) {
this.plugin = plugin;
this.entitySource = entitySource;
this.tracker = tracker;
this.entityTracker = entityTracker;
this.entity = new PacketEntity(EntityTypes.PIG, viewers, entitySource.getLocation());
runEntityTask();
}
@Override
public void teleportToModel() {
Location location = entitySource.getLocation();
entity.teleport(location);
}
public void runEntityTask() {
entityTask = new BetterModelTaskHandler(plugin, this);
}
@Override
public PacketEntity getEntity() {
return entity;
}
@Override
public Set<Player> getViewers() {
return viewers;
}
@Override
public BetterModelTaskHandler getEntityTask() {
return entityTask;
}
public void setHurt(boolean hurt) {
this.hurt = hurt;
}
public Entity getEntitySource() {
return entitySource;
}
public Tracker getTracker() {
return tracker;
}
public EntityTracker getEntityTracker() {
return entityTracker;
}
public boolean isHurt() {
return hurt;
}
}

View File

@@ -0,0 +1,30 @@
package re.imc.geysermodelengine.managers.model.entity;
import org.bukkit.entity.Player;
import re.imc.geysermodelengine.managers.model.taskshandler.TaskHandler;
import re.imc.geysermodelengine.packet.entity.PacketEntity;
import java.util.Set;
public interface EntityData {
/**
* Teleports the packet entity to the model
*/
void teleportToModel();
/**
* Gets the packet Entity
*/
PacketEntity getEntity();
/**
* Gets the entity view of players
*/
Set<Player> getViewers();
/**
* Get the entity task handler
*/
TaskHandler getEntityTask();
}

View File

@@ -1,66 +1,64 @@
package re.imc.geysermodelengine.managers.model.data;
package re.imc.geysermodelengine.managers.model.entity;
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.managers.model.taskshandler.ModelEngineTaskHandler;
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 {
public class ModelEngineEntityData implements EntityData {
private final GeyserModelEngine plugin;
private PacketEntity entity;
private final PacketEntity entity;
private final Set<Player> viewers = Sets.newConcurrentHashSet();
private final ModeledEntity modeledEntity;
private final ActiveModel activeModel;
private EntityTaskRunnable entityTask;
private ModelEngineTaskHandler entityTask;
public ModelEntityData(GeyserModelEngine plugin, ModeledEntity modeledEntity, ActiveModel model) {
public ModelEngineEntityData(GeyserModelEngine plugin, ModeledEntity modeledEntity, ActiveModel activeModel) {
this.plugin = plugin;
this.modeledEntity = modeledEntity;
this.activeModel = model;
this.entity = spawnEntity();
this.activeModel = activeModel;
this.entity = new PacketEntity(EntityTypes.PIG, viewers, modeledEntity.getBase().getLocation());
runEntityTask();
}
@Override
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);
entityTask = new ModelEngineTaskHandler(plugin, this);
}
@Override
public PacketEntity getEntity() {
return entity;
}
@Override
public Set<Player> getViewers() {
return viewers;
}
@Override
public ModelEngineTaskHandler getEntityTask() {
return entityTask;
}
public ModeledEntity getModeledEntity() {
return modeledEntity;
}
@@ -68,8 +66,4 @@ public class ModelEntityData {
public ActiveModel getActiveModel() {
return activeModel;
}
public EntityTaskRunnable getEntityTask() {
return entityTask;
}
}

View File

@@ -0,0 +1,41 @@
package re.imc.geysermodelengine.managers.model.model;
import kr.toxicity.model.api.tracker.Tracker;
import re.imc.geysermodelengine.managers.model.entity.EntityData;
import re.imc.geysermodelengine.managers.model.modelhandler.ModelHandler;
import re.imc.geysermodelengine.managers.model.propertyhandler.PropertyHandler;
public class BetterModelModel implements Model {
private final Tracker tracker;
private final ModelHandler modelHandler;
private final EntityData entityData;
private final PropertyHandler propertyHandler;
public BetterModelModel(Tracker tracker, ModelHandler modelHandler, EntityData entityData, PropertyHandler propertyHandler) {
this.tracker = tracker;
this.modelHandler = modelHandler;
this.entityData = entityData;
this.propertyHandler = propertyHandler;
}
@Override
public String getName() {
return tracker.name();
}
@Override
public ModelHandler getModelHandler() {
return modelHandler;
}
@Override
public EntityData getEntityData() {
return entityData;
}
@Override
public PropertyHandler getPropertyHandler() {
return propertyHandler;
}
}

View File

@@ -0,0 +1,28 @@
package re.imc.geysermodelengine.managers.model.model;
import re.imc.geysermodelengine.managers.model.entity.EntityData;
import re.imc.geysermodelengine.managers.model.modelhandler.ModelHandler;
import re.imc.geysermodelengine.managers.model.propertyhandler.PropertyHandler;
public interface Model {
/**
* Gets the model's name
*/
String getName();
/**
* Gets the model's entity data
*/
EntityData getEntityData();
/**
* Gets the model's model handler
*/
ModelHandler getModelHandler();
/**
* Gets the model's property handler
*/
PropertyHandler getPropertyHandler();
}

View File

@@ -0,0 +1,45 @@
package re.imc.geysermodelengine.managers.model.model;
import com.ticxo.modelengine.api.model.ActiveModel;
import re.imc.geysermodelengine.managers.model.entity.EntityData;
import re.imc.geysermodelengine.managers.model.modelhandler.ModelHandler;
import re.imc.geysermodelengine.managers.model.propertyhandler.PropertyHandler;
public class ModelEngineModel implements Model {
private final ActiveModel activeModel;
private final ModelHandler modelHandler;
private final EntityData entityData;
private final PropertyHandler propertyHandler;
public ModelEngineModel(ActiveModel activeModel, ModelHandler modelHandler, EntityData entityData, PropertyHandler propertyHandler) {
this.activeModel = activeModel;
this.modelHandler = modelHandler;
this.entityData = entityData;
this.propertyHandler = propertyHandler;
}
@Override
public String getName() {
return activeModel.getBlueprint().getName();
}
@Override
public ModelHandler getModelHandler() {
return modelHandler;
}
@Override
public EntityData getEntityData() {
return entityData;
}
@Override
public PropertyHandler getPropertyHandler() {
return propertyHandler;
}
public ActiveModel getActiveModel() {
return activeModel;
}
}

View File

@@ -0,0 +1,65 @@
package re.imc.geysermodelengine.managers.model.modelhandler;
import kr.toxicity.model.api.tracker.EntityTracker;
import kr.toxicity.model.api.tracker.Tracker;
import org.bukkit.Bukkit;
import org.bukkit.entity.Entity;
import re.imc.geysermodelengine.GeyserModelEngine;
import re.imc.geysermodelengine.listener.BetterModelListener;
import re.imc.geysermodelengine.managers.model.propertyhandler.PropertyHandler;
import re.imc.geysermodelengine.managers.model.entity.BetterModelEntityData;
import re.imc.geysermodelengine.managers.model.entity.EntityData;
import re.imc.geysermodelengine.managers.model.model.BetterModelModel;
import re.imc.geysermodelengine.managers.model.model.Model;
import java.util.HashMap;
import java.util.Map;
public class BetterModelHandler implements ModelHandler {
private final GeyserModelEngine plugin;
public BetterModelHandler(GeyserModelEngine plugin) {
this.plugin = plugin;
}
//TODO fix dupe issue - dupe happens when server restart
@Override
public void createModel(Object... objects) {
Entity entitySource = (Entity) objects[0];
Tracker tracker = (Tracker) objects[1];
EntityTracker entityTracker = (EntityTracker) objects[2];
int entityID = entitySource.getEntityId();
PropertyHandler propertyHandler = plugin.getEntityTaskManager().getPropertyHandler();
EntityData entityData = new BetterModelEntityData(plugin, entitySource, tracker, entityTracker);
Model model = new BetterModelModel(tracker, this, entityData, propertyHandler);
Map<Model, EntityData> entityDataCache = plugin.getModelManager().getEntitiesCache().computeIfAbsent(entityID, k -> new HashMap<>());
for (Map.Entry<Model, EntityData> entry : entityDataCache.entrySet()) {
if (entry.getKey() != model && entry.getKey().getName().equals(tracker.name())) {
return;
}
}
plugin.getModelManager().getModelEntitiesCache().put(entityID, model);
entityDataCache.put(model, entityData);
}
@Override
public void processEntities(Entity entity) {
// if (plugin.getModelManager().getEntitiesCache().containsKey(entity.getEntityId())) return;
//
// @NotNull Optional<EntityTrackerRegistry> modeledEntity = BetterModel.registry(entity);
//
// modeledEntity.ifPresent(m -> createModel(modeledEntity.get().entity(), m.));
}
@Override
public void loadListeners() {
Bukkit.getPluginManager().registerEvents(new BetterModelListener(plugin), plugin);
}
}

View File

@@ -0,0 +1,69 @@
package re.imc.geysermodelengine.managers.model.modelhandler;
import com.ticxo.modelengine.api.ModelEngineAPI;
import com.ticxo.modelengine.api.model.ActiveModel;
import com.ticxo.modelengine.api.model.ModeledEntity;
import org.bukkit.Bukkit;
import org.bukkit.entity.Entity;
import re.imc.geysermodelengine.GeyserModelEngine;
import re.imc.geysermodelengine.listener.ModelEngineListener;
import re.imc.geysermodelengine.managers.model.entity.EntityData;
import re.imc.geysermodelengine.managers.model.entity.ModelEngineEntityData;
import re.imc.geysermodelengine.managers.model.model.Model;
import re.imc.geysermodelengine.managers.model.model.ModelEngineModel;
import re.imc.geysermodelengine.managers.model.propertyhandler.PropertyHandler;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
public class ModelEngineHandler implements ModelHandler {
//TODO move driver hashmap here
private final GeyserModelEngine plugin;
public ModelEngineHandler(GeyserModelEngine plugin) {
this.plugin = plugin;
}
@Override
public void createModel(Object... objects) {
ModeledEntity megEntity = (ModeledEntity) objects[0];
ActiveModel megActiveModel = (ActiveModel) objects[1];
int entityID = megEntity.getBase().getEntityId();
PropertyHandler propertyHandler = plugin.getEntityTaskManager().getPropertyHandler();
EntityData entityData = new ModelEngineEntityData(plugin, megEntity, megActiveModel);
Model model = new ModelEngineModel(megActiveModel, this, entityData, propertyHandler);
Map<Model, EntityData> entityDataCache = plugin.getModelManager().getEntitiesCache().computeIfAbsent(entityID, k -> new HashMap<>());
for (Map.Entry<Model, EntityData> entry : entityDataCache.entrySet()) {
if (entry.getKey() != model && entry.getKey().getName().equals(megActiveModel.getBlueprint().getName())) {
return;
}
}
plugin.getModelManager().getModelEntitiesCache().put(entityID, model);
entityDataCache.put(model, entityData);
}
@Override
public void processEntities(Entity entity) {
if (plugin.getModelManager().getEntitiesCache().containsKey(entity.getEntityId())) return;
ModeledEntity modeledEntity = ModelEngineAPI.getModeledEntity(entity);
if (modeledEntity == null) return;
Optional<ActiveModel> model = modeledEntity.getModels().values().stream().findFirst();
model.ifPresent(m -> createModel(modeledEntity, m));
}
@Override
public void loadListeners() {
Bukkit.getPluginManager().registerEvents(new ModelEngineListener(plugin), plugin);
}
}

View File

@@ -0,0 +1,23 @@
package re.imc.geysermodelengine.managers.model.modelhandler;
import org.bukkit.entity.Entity;
public interface ModelHandler {
/**
* Creates the model from the required Model Engine
* @param objects Processes the required objects
*/
void createModel(Object... objects);
/**
* Processes entities into createModel()
* @param entity Registers bukkit entities
*/
void processEntities(Entity entity);
/**
* Loads the required listeners
*/
void loadListeners();
}

View File

@@ -0,0 +1,169 @@
package re.imc.geysermodelengine.managers.model.propertyhandler;
import kr.toxicity.model.api.animation.AnimationIterator;
import kr.toxicity.model.api.bone.RenderedBone;
import kr.toxicity.model.api.data.blueprint.BlueprintAnimation;
import kr.toxicity.model.api.data.renderer.RenderPipeline;
import kr.toxicity.model.api.nms.ModelDisplay;
import me.zimzaza4.geyserutils.spigot.api.EntityUtils;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import re.imc.geysermodelengine.GeyserModelEngine;
import re.imc.geysermodelengine.managers.model.entity.BetterModelEntityData;
import re.imc.geysermodelengine.managers.model.entity.EntityData;
import re.imc.geysermodelengine.util.BooleanPacker;
import java.awt.*;
import java.util.*;
import java.util.List;
public class BetterModelPropertyHandler implements PropertyHandler {
private final GeyserModelEngine plugin;
public BetterModelPropertyHandler(GeyserModelEngine plugin) {
this.plugin = plugin;
}
// Figure out on how to get the scale from BetterModel
@Override
public void sendScale(EntityData entityData, Collection<Player> players, float lastScale, boolean firstSend) {
BetterModelEntityData betterModelEntityData = (BetterModelEntityData) entityData;
}
@Override
public void sendColor(EntityData entityData, Collection<Player> players, Color lastColor, boolean firstSend) {
if (players.isEmpty()) return;
BetterModelEntityData betterModelEntityData = (BetterModelEntityData) entityData;
Color color = new Color(0xFFFFFF);
if (betterModelEntityData.isHurt()) color = new Color(betterModelEntityData.getEntityTracker().damageTintValue());
if (firstSend) {
if (color.equals(lastColor)) return;
}
for (Player player : players) {
EntityUtils.sendCustomColor(player, betterModelEntityData.getEntity().getEntityId(), color);
}
betterModelEntityData.setHurt(false);
}
@Override
public void sendHitBox(EntityData entityData, Player player) {
BetterModelEntityData betterModelEntityData = (BetterModelEntityData) entityData;
float w = 0;
EntityUtils.sendCustomHitBox(player, betterModelEntityData.getEntity().getEntityId(), 0.02f, w);
}
@Override
public void updateEntityProperties(EntityData entityData, Collection<Player> players, boolean firstSend, String... forceAnims) {
BetterModelEntityData model = (BetterModelEntityData) entityData;
int entity = model.getEntity().getEntityId();
Set<String> forceAnimSet = Set.of(forceAnims);
Map<String, Boolean> boneUpdates = new HashMap<>();
Map<String, Boolean> animUpdates = new HashMap<>();
Set<String> anims = new HashSet<>();
model.getTracker().bones().forEach(bone -> processBone(model, bone, boneUpdates));
RenderPipeline handler = model.getTracker().getPipeline();
for (RenderedBone renderedBone : handler.bones()) {
if (model.getTracker().bone(renderedBone.name()).runningAnimation() != null) {
BlueprintAnimation anim = model.getTracker().renderer().animations().get(renderedBone.runningAnimation().name());
anims.add(renderedBone.runningAnimation().name());
if (anim.override() && anim.loop() == AnimationIterator.Type.PLAY_ONCE) {
break;
}
}
}
for (String id : handler.getParent().animations().keySet()) {
if (anims.contains(id)) {
animUpdates.put(id, true);
} else {
animUpdates.put(id, false);
}
}
Set<String> lastPlayed = new HashSet<>(model.getEntityTask().getLastPlayedAnim().asMap().keySet());
for (Map.Entry<String, Boolean> anim : animUpdates.entrySet()) {
if (anim.getValue()) {
model.getEntityTask().getLastPlayedAnim().put(anim.getKey(), true);
}
}
for (String anim : lastPlayed) animUpdates.put(anim, true);
if (boneUpdates.isEmpty() && animUpdates.isEmpty()) return;
Map<String, Integer> intUpdates = new HashMap<>();
int i = 0;
for (Integer integer : BooleanPacker.mapBooleansToInts(boneUpdates)) {
intUpdates.put(plugin.getConfigManager().getConfig().getString("models.namespace") + ":bone" + i, integer);
i++;
}
i = 0;
for (Integer integer : BooleanPacker.mapBooleansToInts(animUpdates)) {
intUpdates.put(plugin.getConfigManager().getConfig().getString("models.namespace") + ":anim" + i, integer);
i++;
}
if (!firstSend) {
if (intUpdates.equals(model.getEntityTask().getLastIntSet())) {
return;
} else {
model.getEntityTask().getLastIntSet().clear();
model.getEntityTask().getLastIntSet().putAll(intUpdates);
}
}
if (plugin.getConfigManager().getConfig().getBoolean("options.debug")) plugin.getLogger().info(animUpdates.toString());
List<String> list = new ArrayList<>(boneUpdates.keySet());
Collections.sort(list);
for (Player player : players) {
EntityUtils.sendIntProperties(player, entity, intUpdates);
}
}
public String unstripName(RenderedBone bone) {
@NotNull String name = bone.name().rawName();
if (name.equals("head")) {
if (!bone.getChildren().isEmpty()) return "hi_" + name;
return "h_" + name;
}
return name;
}
private void processBone(BetterModelEntityData entityData, RenderedBone bone, Map<String, Boolean> map) {
String name = unstripName(bone).toLowerCase();
if (name.equals("hitbox") || name.equals("shadow") || name.equals("mount") || name.startsWith("p_") || name.startsWith("b_") || name.startsWith("ob_")) return;
for (RenderedBone renderedBone : bone.getChildren().values()) {
processBone(entityData, renderedBone, map);
}
RenderedBone activeBone = entityData.getTracker().bone(bone.name());
ModelDisplay modelDisplay = activeBone.getDisplay();
if (modelDisplay == null) return;
boolean visible = activeBone.getDisplay().invisible();
map.put(name, visible);
}
}

View File

@@ -0,0 +1,186 @@
package re.imc.geysermodelengine.managers.model.propertyhandler;
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.bone.ModelBone;
import com.ticxo.modelengine.api.model.render.DisplayRenderer;
import me.zimzaza4.geyserutils.spigot.api.EntityUtils;
import org.bukkit.entity.Player;
import org.joml.Vector3fc;
import re.imc.geysermodelengine.GeyserModelEngine;
import re.imc.geysermodelengine.managers.model.entity.EntityData;
import re.imc.geysermodelengine.managers.model.entity.ModelEngineEntityData;
import re.imc.geysermodelengine.util.BooleanPacker;
import java.awt.*;
import java.util.*;
import java.util.List;
public class ModelEnginePropertyHandler implements PropertyHandler {
private final GeyserModelEngine plugin;
public ModelEnginePropertyHandler(GeyserModelEngine plugin) {
this.plugin = plugin;
}
@Override
public void sendScale(EntityData modelData, Collection<Player> players, float lastScale, boolean firstSend) {
try {
if (players.isEmpty()) return;
ModelEngineEntityData modelEngineEntityData = (ModelEngineEntityData) modelData;
Vector3fc scale = modelEngineEntityData.getActiveModel().getScale();
float average = (scale.x() + scale.y() + scale.z()) / 3;
if (!firstSend) {
if (average == lastScale) return;
}
for (Player player : players) {
EntityUtils.sendCustomScale(player, modelEngineEntityData.getEntity().getEntityId(), average);
}
} catch (Throwable ignored) {}
}
@Override
public void sendColor(EntityData entityData, Collection<Player> players, Color lastColor, boolean firstSend) {
if (players.isEmpty()) return;
ModelEngineEntityData modelEngineEntityData = (ModelEngineEntityData) entityData;
Color color = new Color(modelEngineEntityData.getActiveModel().getDefaultTint().asARGB());
if (modelEngineEntityData.getActiveModel().isMarkedHurt()) color = new Color(modelEngineEntityData.getActiveModel().getDamageTint().asARGB());
if (firstSend) {
if (color.equals(lastColor)) return;
}
for (Player player : players) {
EntityUtils.sendCustomColor(player, modelEngineEntityData.getEntity().getEntityId(), color);
}
}
@Override
public void sendHitBox(EntityData entityData, Player player) {
ModelEngineEntityData modelEngineEntityData = (ModelEngineEntityData) entityData;
float w = 0;
if (modelEngineEntityData.getActiveModel().isShadowVisible()) {
if (modelEngineEntityData.getActiveModel().getModelRenderer() instanceof DisplayRenderer displayRenderer) {
// w = displayRenderer.getHitbox().getShadowRadius().get();
}
}
EntityUtils.sendCustomHitBox(player, modelEngineEntityData.getEntity().getEntityId(), 0.02f, w);
}
@Override
public void updateEntityProperties(EntityData entityData, Collection<Player> players, boolean firstSend, String... forceAnims) {
ModelEngineEntityData model = (ModelEngineEntityData) entityData;
int entity = model.getEntity().getEntityId();
Set<String> forceAnimSet = Set.of(forceAnims);
Map<String, Boolean> boneUpdates = new HashMap<>();
Map<String, Boolean> animUpdates = new HashMap<>();
Set<String> anims = new HashSet<>();
model.getActiveModel().getBlueprint().getBones().forEach((s, bone) -> processBone(model, bone, boneUpdates));
AnimationHandler handler = model.getActiveModel().getAnimationHandler();
Set<String> priority = model.getActiveModel().getBlueprint().getAnimationDescendingPriority();
for (String animId : priority) {
if (handler.isPlayingAnimation(animId)) {
BlueprintAnimation anim = model.getActiveModel().getBlueprint().getAnimations().get(animId);
anims.add(animId);
if (anim.isOverride() && anim.getLoopMode() == BlueprintAnimation.LoopMode.ONCE) {
break;
}
}
}
for (String id : priority) {
if (anims.contains(id)) {
animUpdates.put(id, true);
} else {
animUpdates.put(id, false);
}
}
Set<String> lastPlayed = new HashSet<>(model.getEntityTask().getLastPlayedAnim().asMap().keySet());
for (Map.Entry<String, Boolean> anim : animUpdates.entrySet()) {
if (anim.getValue()) {
model.getEntityTask().getLastPlayedAnim().put(anim.getKey(), true);
}
}
for (String anim : lastPlayed) animUpdates.put(anim, true);
if (boneUpdates.isEmpty() && animUpdates.isEmpty()) return;
Map<String, Integer> intUpdates = new HashMap<>();
int i = 0;
for (Integer integer : BooleanPacker.mapBooleansToInts(boneUpdates)) {
intUpdates.put(plugin.getConfigManager().getConfig().getString("models.namespace") + ":bone" + i, integer);
i++;
}
i = 0;
for (Integer integer : BooleanPacker.mapBooleansToInts(animUpdates)) {
intUpdates.put(plugin.getConfigManager().getConfig().getString("models.namespace") + ":anim" + i, integer);
i++;
}
if (!firstSend) {
if (intUpdates.equals(model.getEntityTask().getLastIntSet())) {
return;
} else {
model.getEntityTask().getLastIntSet().clear();
model.getEntityTask().getLastIntSet().putAll(intUpdates);
}
}
if (plugin.getConfigManager().getConfig().getBoolean("options.debug")) plugin.getLogger().info(animUpdates.toString());
List<String> list = new ArrayList<>(boneUpdates.keySet());
Collections.sort(list);
for (Player player : players) {
EntityUtils.sendIntProperties(player, entity, intUpdates);
}
}
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;
}
private void processBone(ModelEngineEntityData model, BlueprintBone bone, Map<String, Boolean> map) {
String name = unstripName(bone).toLowerCase();
if (name.equals("hitbox") || name.equals("shadow") || name.equals("mount") || name.startsWith("p_") || name.startsWith("b_") || name.startsWith("ob_")) return;
for (BlueprintBone blueprintBone : bone.getChildren().values()) {
processBone(model, blueprintBone, map);
}
ModelBone activeBone = model.getActiveModel().getBones().get(bone.getName());
boolean visible = false;
if (activeBone != null) visible = activeBone.isVisible();
map.put(name, visible);
}
}

View File

@@ -0,0 +1,44 @@
package re.imc.geysermodelengine.managers.model.propertyhandler;
import org.bukkit.entity.Player;
import re.imc.geysermodelengine.managers.model.entity.EntityData;
import java.awt.*;
import java.util.Collection;
public interface PropertyHandler {
/**
* Sends scale of the entity to the player
* @param entityData The data of the entity
* @param players Collection of players from the entity view
* @param lastScale Sends the last scale to the player
* @param firstSend Checks if it's the first time to send scale to the player
*/
void sendScale(EntityData entityData, Collection<Player> players, float lastScale, boolean firstSend);
/**
* Sends a colour tint to the player
* @param entityData The data of the entity
* @param players Collection of players from the entity view
* @param lastColor Sends the last colour to the player
* @param firstSend Checks if it's the first time to send colour to the player
*/
void sendColor(EntityData entityData, Collection<Player> players, Color lastColor, boolean firstSend);
/**
* Sends a hitbox to the player
* @param entityData The data of the entity
* @param player Sends the player the entity hitbox
*/
void sendHitBox(EntityData entityData, Player player);
/**
* Updates the entity to all viewable players
* @param entityData The data of the entity
* @param players Collection of players from the entity view
* @param firstSend Checks if it's the first time to send the entity to the player
* @param forceAnims Forces the entity to do an animation
*/
void updateEntityProperties(EntityData entityData, Collection<Player> players, boolean firstSend, String... forceAnims);
}

View File

@@ -0,0 +1,170 @@
package re.imc.geysermodelengine.managers.model.taskshandler;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import kr.toxicity.model.api.tracker.Tracker;
import me.zimzaza4.geyserutils.spigot.api.EntityUtils;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Player;
import re.imc.geysermodelengine.GeyserModelEngine;
import re.imc.geysermodelengine.managers.model.entity.BetterModelEntityData;
import re.imc.geysermodelengine.managers.model.entity.EntityData;
import re.imc.geysermodelengine.packet.entity.PacketEntity;
import java.awt.*;
import java.util.Collections;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
public class BetterModelTaskHandler implements TaskHandler {
private final GeyserModelEngine plugin;
private final BetterModelEntityData entityData;
private int tick = 0;
private int syncTick = 0;
private float lastScale = -1.0f;
private Color lastColor = null;
private boolean removed = false;
private final ConcurrentHashMap<String, Integer> lastIntSet = new ConcurrentHashMap<>();
private final Cache<String, Boolean> lastPlayedAnim = CacheBuilder.newBuilder().expireAfterWrite(30, TimeUnit.MILLISECONDS).build();
private ScheduledFuture scheduledFuture;
public BetterModelTaskHandler(GeyserModelEngine plugin, BetterModelEntityData entityData) {
this.plugin = plugin;
this.entityData = entityData;
plugin.getEntityTaskManager().sendHitBoxToAll(entityData);
scheduledFuture = plugin.getSchedulerPool().scheduleAtFixedRate(this::runAsync, 0, 20, TimeUnit.MILLISECONDS);
}
@Override
public void runAsync() {
plugin.getEntityTaskManager().checkViewers(entityData, entityData.getViewers());
PacketEntity entity = entityData.getEntity();
if (entity.isDead()) return;
Set<Player> viewers = entityData.getViewers();
Entity entitySource = entityData.getEntitySource();
Tracker tracker = entityData.getTracker();
entityData.teleportToModel();
if (entitySource.isDead() || tracker.forRemoval()) {
removed = true;
entity.remove();
plugin.getModelManager().getEntitiesCache().remove(entitySource.getEntityId());
plugin.getModelManager().getModelEntitiesCache().remove(entitySource.getEntityId());
cancel();
return;
}
if (tick % 5 == 0) {
if (tick % 40 == 0) {
for (Player viewer : Set.copyOf(viewers)) {
if (!plugin.getEntityTaskManager().canSee(viewer, entityData.getEntity())) {
viewers.remove(viewer);
}
}
}
}
tick++;
if (tick > 400) {
tick = 0;
plugin.getEntityTaskManager().sendHitBoxToAll(entityData);
}
if (viewers.isEmpty()) return;
plugin.getEntityTaskManager().getPropertyHandler().sendScale(entityData, viewers, lastScale, false);
plugin.getEntityTaskManager().getPropertyHandler().sendColor(entityData, viewers, lastColor, false);
}
@Override
public void sendEntityData(EntityData entityData, Player player, int delay) {
BetterModelEntityData betterModelEntityData = (BetterModelEntityData) entityData;
EntityUtils.setCustomEntity(player, betterModelEntityData.getEntity().getEntityId(), plugin.getConfigManager().getConfig().getString("models.namespace") + ":" + betterModelEntityData.getTracker().name().toLowerCase());
plugin.getSchedulerPool().schedule(() -> {
entityData.getEntity().sendSpawnPacket(Collections.singletonList(player));
plugin.getSchedulerPool().schedule(() -> {
plugin.getEntityTaskManager().getPropertyHandler().sendHitBox(entityData, player);
plugin.getEntityTaskManager().getPropertyHandler().sendScale(entityData, Collections.singleton(player), lastScale, true);
plugin.getEntityTaskManager().getPropertyHandler().sendColor(entityData, Collections.singleton(player), lastColor, true);
plugin.getEntityTaskManager().getPropertyHandler().updateEntityProperties(entityData, Collections.singleton(player), true);
}, 500, TimeUnit.MILLISECONDS);
}, delay * 50L, TimeUnit.MILLISECONDS);
}
@Override
public void cancel() {
scheduledFuture.cancel(true);
}
public void setTick(int tick) {
this.tick = tick;
}
public void setSyncTick(int syncTick) {
this.syncTick = syncTick;
}
public void setRemoved(boolean removed) {
this.removed = removed;
}
public void setLastScale(float lastScale) {
this.lastScale = lastScale;
}
public int getTick() {
return tick;
}
public int getSyncTick() {
return syncTick;
}
public void setLastColor(Color lastColor) {
this.lastColor = lastColor;
}
public float getLastScale() {
return lastScale;
}
public Color getLastColor() {
return lastColor;
}
public boolean isRemoved() {
return removed;
}
public ConcurrentHashMap<String, Integer> getLastIntSet() {
return lastIntSet;
}
public Cache<String, Boolean> getLastPlayedAnim() {
return lastPlayedAnim;
}
public ScheduledFuture getScheduledFuture() {
return scheduledFuture;
}
}

View File

@@ -0,0 +1,170 @@
package re.imc.geysermodelengine.managers.model.taskshandler;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import com.ticxo.modelengine.api.model.ActiveModel;
import com.ticxo.modelengine.api.model.ModeledEntity;
import me.zimzaza4.geyserutils.spigot.api.EntityUtils;
import org.bukkit.entity.Player;
import re.imc.geysermodelengine.GeyserModelEngine;
import re.imc.geysermodelengine.managers.model.entity.EntityData;
import re.imc.geysermodelengine.managers.model.entity.ModelEngineEntityData;
import re.imc.geysermodelengine.packet.entity.PacketEntity;
import java.awt.*;
import java.util.Collections;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
public class ModelEngineTaskHandler implements TaskHandler {
private final GeyserModelEngine plugin;
private final ModelEngineEntityData entityData;
private int tick = 0;
private int syncTick = 0;
private float lastScale = -1.0f;
private Color lastColor = null;
private boolean removed = false;
private final ConcurrentHashMap<String, Integer> lastIntSet = new ConcurrentHashMap<>();
private final Cache<String, Boolean> lastPlayedAnim = CacheBuilder.newBuilder().expireAfterWrite(30, TimeUnit.MILLISECONDS).build();
private ScheduledFuture scheduledFuture;
public ModelEngineTaskHandler(GeyserModelEngine plugin, ModelEngineEntityData entityData) {
this.plugin = plugin;
this.entityData = entityData;
plugin.getEntityTaskManager().sendHitBoxToAll(entityData);
scheduledFuture = plugin.getSchedulerPool().scheduleAtFixedRate(this::runAsync, 0, 20, TimeUnit.MILLISECONDS);
}
@Override
public void runAsync() {
plugin.getEntityTaskManager().checkViewers(entityData, entityData.getViewers());
PacketEntity entity = entityData.getEntity();
if (entity.isDead()) return;
entityData.teleportToModel();
Set<Player> viewers = entityData.getViewers();
ActiveModel activeModel = entityData.getActiveModel();
ModeledEntity modeledEntity = entityData.getModeledEntity();
if (activeModel.isDestroyed() || activeModel.isRemoved()) {
removed = true;
entity.remove();
plugin.getModelManager().getEntitiesCache().remove(modeledEntity.getBase().getEntityId());
plugin.getModelManager().getModelEntitiesCache().remove(modeledEntity.getBase().getEntityId());
cancel();
return;
}
if (tick % 5 == 0) {
if (tick % 40 == 0) {
for (Player viewer : Set.copyOf(viewers)) {
if (!plugin.getEntityTaskManager().canSee(viewer, entityData.getEntity())) {
viewers.remove(viewer);
}
}
}
}
tick ++;
if (tick > 400) {
tick = 0;
plugin.getEntityTaskManager().sendHitBoxToAll(entityData);
}
if (viewers.isEmpty()) return;
plugin.getEntityTaskManager().getPropertyHandler().sendScale(entityData, viewers, lastScale, false);
plugin.getEntityTaskManager().getPropertyHandler().sendColor(entityData, viewers, lastColor, false);
}
@Override
public void sendEntityData(EntityData entityData, Player player, int delay) {
ModelEngineEntityData modelEngineEntityData = (ModelEngineEntityData) entityData;
EntityUtils.setCustomEntity(player, modelEngineEntityData.getEntity().getEntityId(), plugin.getConfigManager().getConfig().getString("models.namespace") + ":" + modelEngineEntityData.getActiveModel().getBlueprint().getName().toLowerCase());
plugin.getSchedulerPool().schedule(() -> {
entityData.getEntity().sendSpawnPacket(Collections.singletonList(player));
plugin.getSchedulerPool().schedule(() -> {
plugin.getEntityTaskManager().getPropertyHandler().sendHitBox(entityData, player);
plugin.getEntityTaskManager().getPropertyHandler().sendScale(entityData, Collections.singleton(player), lastScale, true);
plugin.getEntityTaskManager().getPropertyHandler().sendColor(entityData, Collections.singleton(player), lastColor, true);
plugin.getEntityTaskManager().getPropertyHandler().updateEntityProperties(entityData, Collections.singleton(player), true);
}, 500, TimeUnit.MILLISECONDS);
}, delay * 50L, TimeUnit.MILLISECONDS);
}
@Override
public void cancel() {
scheduledFuture.cancel(true);
}
public void setTick(int tick) {
this.tick = tick;
}
public void setSyncTick(int syncTick) {
this.syncTick = syncTick;
}
public void setRemoved(boolean removed) {
this.removed = removed;
}
public void setLastScale(float lastScale) {
this.lastScale = lastScale;
}
public int getTick() {
return tick;
}
public int getSyncTick() {
return syncTick;
}
public void setLastColor(Color lastColor) {
this.lastColor = lastColor;
}
public float getLastScale() {
return lastScale;
}
public Color getLastColor() {
return lastColor;
}
public boolean isRemoved() {
return removed;
}
public ConcurrentHashMap<String, Integer> getLastIntSet() {
return lastIntSet;
}
public Cache<String, Boolean> getLastPlayedAnim() {
return lastPlayedAnim;
}
public ScheduledFuture getScheduledFuture() {
return scheduledFuture;
}
}

View File

@@ -0,0 +1,25 @@
package re.imc.geysermodelengine.managers.model.taskshandler;
import org.bukkit.entity.Player;
import re.imc.geysermodelengine.managers.model.entity.EntityData;
public interface TaskHandler {
/**
* Runs the entity scheduler
*/
void runAsync();
/**
* Spawns the entity to the player
* @param entityData The data of the entity
* @param player Sends the entity to the player
* @param delay Delays sending the entity to the player
*/
void sendEntityData(EntityData entityData, Player player, int delay);
/**
* Cancels the entity scheduler
*/
void cancel();
}

View File

@@ -58,7 +58,6 @@ public class PacketEntity {
return true;
}
public void remove() {
removed = true;
sendEntityDestroyPacket(viewers);
@@ -78,7 +77,6 @@ public class PacketEntity {
}
public void sendLocationPacket(Collection<Player> players) {
PacketWrapper<?> packet;
EntityPositionData data = new EntityPositionData(SpigotConversionUtil.fromBukkitLocation(location).getPosition(), Vector3d.zero(), location.getYaw(), location.getPitch());

View File

@@ -9,9 +9,9 @@ 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.UUID;
import java.util.function.Consumer;
public class BedrockMountControlRunnable implements Consumer<ScheduledTask> {
@@ -24,11 +24,11 @@ public class BedrockMountControlRunnable implements Consumer<ScheduledTask> {
@Override
public void accept(ScheduledTask scheduledTask) {
for (Player player : Bukkit.getOnlinePlayers()) {
if (!FloodgateApi.getInstance().isFloodgatePlayer(player.getUniqueId())) continue;
for (UUID playerUUID : plugin.getModelManager().getPlayerJoinedCache()) {
Player player = Bukkit.getPlayer(playerUUID);
float pitch = player.getLocation().getPitch();
Pair<ActiveModel, Mount> seat = plugin.getModelManager().getDriversCache().get(player);
Pair<ActiveModel, Mount> seat = plugin.getModelManager().getDriversCache().get(player.getUniqueId());
if (seat == null) continue;

View File

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

View File

@@ -1,9 +1,9 @@
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 re.imc.geysermodelengine.managers.model.entity.EntityData;
import re.imc.geysermodelengine.managers.model.model.Model;
import java.util.Map;
import java.util.function.Consumer;
@@ -19,8 +19,8 @@ public class UpdateTaskRunnable implements Consumer<ScheduledTask> {
@Override
public void accept(ScheduledTask scheduledTask) {
try {
for (Map<ActiveModel, ModelEntityData> models : plugin.getModelManager().getEntitiesCache().values()) {
models.values().forEach(model -> model.getEntityTask().updateEntityProperties(model, model.getViewers(), false));
for (Map<Model, EntityData> models : plugin.getModelManager().getEntitiesCache().values()) {
models.values().forEach(entityData -> plugin.getEntityTaskManager().getPropertyHandler().updateEntityProperties(entityData, entityData.getViewers(), false));
}
} catch (Throwable err) {
throw new RuntimeException(err);

View File

@@ -7,9 +7,9 @@ import java.util.Map;
public class BooleanPacker {
private final int MAX_BOOLEANS = 24;
private static final int MAX_BOOLEANS = 24;
public int booleansToInt(List<Boolean> booleans) {
public static int booleansToInt(List<Boolean> booleans) {
int result = 0;
int i = 1;
@@ -23,7 +23,7 @@ public class BooleanPacker {
return result;
}
public int mapBooleansToInt(Map<String, Boolean> booleanMap) {
public static int mapBooleansToInt(Map<String, Boolean> booleanMap) {
int result = 0;
int i = 1;
@@ -39,7 +39,7 @@ public class BooleanPacker {
return result;
}
public List<Integer> booleansToInts(List<Boolean> booleans) {
public static List<Integer> booleansToInts(List<Boolean> booleans) {
List<Integer> results = new ArrayList<>();
int result = 0;
int i = 1;
@@ -62,7 +62,7 @@ public class BooleanPacker {
return results;
}
public List<Integer> mapBooleansToInts(Map<String, Boolean> booleanMap) {
public static List<Integer> mapBooleansToInts(Map<String, Boolean> booleanMap) {
List<String> keys = new ArrayList<>(booleanMap.keySet());
List<Boolean> booleans = new ArrayList<>();

View File

@@ -1,8 +1,13 @@
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
metrics:
bstats: true
models:
namespace: "modelengine"
data-send-delay: 5
entity-view-distance: 50
join-send-delay: 20
entity-position-update-period: 35
thread-pool-size: 4
options:
debug: false

View File

@@ -6,7 +6,7 @@ api-version: '1.21'
authors:
- zimzaza4
- willem.dev
- TheLividaProject
- xSquishyLiam
load: STARTUP
@@ -16,7 +16,9 @@ dependencies:
required: true
packetevents:
required: true
ModelEngine:
required: true
floodgate:
required: true
required: true
ModelEngine:
required: false
BetterModel:
required: false