mirror of
https://github.com/GeyserExtensionists/GeyserModelEngine.git
synced 2025-12-19 15:09:18 +00:00
Major changes alongside PacketEvents API updated for 1.21.11
This commit is contained in:
4
.github/workflows/build.yml
vendored
4
.github/workflows/build.yml
vendored
@@ -4,6 +4,7 @@ on:
|
|||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
- main
|
- main
|
||||||
|
- bettermodel-support-dev
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
@@ -34,4 +35,5 @@ jobs:
|
|||||||
automatic_release_tag: latest
|
automatic_release_tag: latest
|
||||||
prerelease: false
|
prerelease: false
|
||||||
files: |
|
files: |
|
||||||
build/libs/GeyserModelEngine*.jar
|
paper/build/libs/GeyserModelEngine*.jar
|
||||||
|
geyser/build/libs/GeyserModelEngine*.jar
|
||||||
|
|||||||
3
.idea/.gitignore
generated
vendored
Normal file
3
.idea/.gitignore
generated
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
# Default ignored files
|
||||||
|
/shelf/
|
||||||
|
/workspace.xml
|
||||||
4
.idea/encodings.xml
generated
4
.idea/encodings.xml
generated
@@ -1,7 +1,7 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<project version="4">
|
<project version="4">
|
||||||
<component name="Encoding">
|
<component name="Encoding">
|
||||||
<file url="file://$PROJECT_DIR$/src/main/java" charset="UTF-8" />
|
<file url="file://$PROJECT_DIR$/paper/src/main/java" charset="UTF-8" />
|
||||||
<file url="file://$PROJECT_DIR$/src/main/resources" charset="UTF-8" />
|
<file url="file://$PROJECT_DIR$/paper/src/main/resources" charset="UTF-8" />
|
||||||
</component>
|
</component>
|
||||||
</project>
|
</project>
|
||||||
2
.idea/gradle.xml
generated
2
.idea/gradle.xml
generated
@@ -8,6 +8,8 @@
|
|||||||
<option name="modules">
|
<option name="modules">
|
||||||
<set>
|
<set>
|
||||||
<option value="$PROJECT_DIR$" />
|
<option value="$PROJECT_DIR$" />
|
||||||
|
<option value="$PROJECT_DIR$/geyser" />
|
||||||
|
<option value="$PROJECT_DIR$/paper" />
|
||||||
</set>
|
</set>
|
||||||
</option>
|
</option>
|
||||||
</GradleProjectSettings>
|
</GradleProjectSettings>
|
||||||
|
|||||||
123
.idea/workspace.xml
generated
123
.idea/workspace.xml
generated
@@ -4,14 +4,15 @@
|
|||||||
<option name="autoReloadType" value="SELECTIVE" />
|
<option name="autoReloadType" value="SELECTIVE" />
|
||||||
</component>
|
</component>
|
||||||
<component name="ChangeListManager">
|
<component name="ChangeListManager">
|
||||||
<list default="true" id="ff2e9770-ec88-4715-adeb-b9dbda130e1a" name="Changes" comment="">
|
<list default="true" id="ff2e9770-ec88-4715-adeb-b9dbda130e1a" name="Changes" comment="" />
|
||||||
<change beforePath="$PROJECT_DIR$/build.gradle.kts" beforeDir="false" afterPath="$PROJECT_DIR$/build.gradle.kts" afterDir="false" />
|
|
||||||
</list>
|
|
||||||
<option name="SHOW_DIALOG" value="false" />
|
<option name="SHOW_DIALOG" value="false" />
|
||||||
<option name="HIGHLIGHT_CONFLICTS" value="true" />
|
<option name="HIGHLIGHT_CONFLICTS" value="true" />
|
||||||
<option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
|
<option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
|
||||||
<option name="LAST_RESOLUTION" value="IGNORE" />
|
<option name="LAST_RESOLUTION" value="IGNORE" />
|
||||||
</component>
|
</component>
|
||||||
|
<component name="DarkyenusTimeTracker">
|
||||||
|
<option name="totalTimeSeconds" value="24424" />
|
||||||
|
</component>
|
||||||
<component name="ExternalProjectsData">
|
<component name="ExternalProjectsData">
|
||||||
<projectState path="$PROJECT_DIR$">
|
<projectState path="$PROJECT_DIR$">
|
||||||
<ProjectState />
|
<ProjectState />
|
||||||
@@ -59,7 +60,7 @@
|
|||||||
<component name="Git.Settings">
|
<component name="Git.Settings">
|
||||||
<option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$" />
|
<option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$" />
|
||||||
</component>
|
</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="GradleScriptDefinitionsStorage" workingDir="$PROJECT_DIR$" gradleHome="C:\Users\xsqui\.gradle\wrapper\dists\gradle-9.2.0-bin\11i5gvueggl8a5cioxuftxrik\gradle-9.2.0" javaHome="C:\Program Files\Java\jdk-21" gradleVersion="9.2.0" />
|
||||||
<component name="MavenRunner">
|
<component name="MavenRunner">
|
||||||
<option name="delegateBuildToMaven" value="true" />
|
<option name="delegateBuildToMaven" value="true" />
|
||||||
</component>
|
</component>
|
||||||
@@ -77,35 +78,88 @@
|
|||||||
<option name="hideEmptyMiddlePackages" value="true" />
|
<option name="hideEmptyMiddlePackages" value="true" />
|
||||||
<option name="showLibraryContents" value="true" />
|
<option name="showLibraryContents" value="true" />
|
||||||
</component>
|
</component>
|
||||||
<component name="PropertiesComponent"><![CDATA[{
|
<component name="PropertiesComponent">{
|
||||||
"keyToString": {
|
"keyToString": {
|
||||||
"Gradle.Download Sources.executor": "Run",
|
"Gradle.Build GeyserModelEngine.executor": "Run",
|
||||||
"Gradle.GeyserModelEngine [build].executor": "Run",
|
"Gradle.Download Sources.executor": "Run",
|
||||||
"Gradle.GeyserModelEngine [jar].executor": "Run",
|
"Gradle.GeyserModelEngine [buildDependents].executor": "Run",
|
||||||
"Maven.GeyserModelEngine [install...].executor": "Run",
|
"Gradle.GeyserModelEngine [buildNeeded].executor": "Run",
|
||||||
"Maven.GeyserModelEngine [install].executor": "Run",
|
"Gradle.GeyserModelEngine [build].executor": "Run",
|
||||||
"ModuleVcsDetector.initialDetectionPerformed": "true",
|
"Gradle.GeyserModelEngine [clean].executor": "Run",
|
||||||
"RunOnceActivity.ShowReadmeOnStart": "true",
|
"Gradle.GeyserModelEngine [jar].executor": "Run",
|
||||||
"RunOnceActivity.git.unshallow": "true",
|
"Maven.GeyserModelEngine [install...].executor": "Run",
|
||||||
"git-widget-placeholder": "main",
|
"Maven.GeyserModelEngine [install].executor": "Run",
|
||||||
"kotlin-language-version-configured": "true",
|
"ModuleVcsDetector.initialDetectionPerformed": "true",
|
||||||
"last_opened_file_path": "C:/Users/Livid/OneDrive/Desktop/Coding/GeyserModelEngine",
|
"RunOnceActivity.ShowReadmeOnStart": "true",
|
||||||
"project.structure.last.edited": "Project",
|
"RunOnceActivity.TerminalTabsStorage.copyFrom.TerminalArrangementManager.252": "true",
|
||||||
"project.structure.proportion": "0.0",
|
"RunOnceActivity.git.unshallow": "true",
|
||||||
"project.structure.side.proportion": "0.2",
|
"git-widget-placeholder": "main",
|
||||||
"settings.editor.selected.configurable": "reference.settings.project.maven.runner"
|
"ignore.virus.scanning.warn.message": "true",
|
||||||
|
"kotlin-language-version-configured": "true",
|
||||||
|
"last_opened_file_path": "D:/Coding/Forks/Minecraft/GeyserModelEngine/geyser",
|
||||||
|
"project.structure.last.edited": "Project",
|
||||||
|
"project.structure.proportion": "0.0",
|
||||||
|
"project.structure.side.proportion": "0.2",
|
||||||
|
"settings.editor.selected.configurable": "reference.settingsdialog.project.gradle"
|
||||||
}
|
}
|
||||||
}]]></component>
|
}</component>
|
||||||
<component name="RecentsManager">
|
<component name="RecentsManager">
|
||||||
<key name="CopyFile.RECENT_KEYS">
|
<key name="CopyFile.RECENT_KEYS">
|
||||||
|
<recent name="D:\Coding\Forks\Minecraft\GeyserModelEngine\geyser" />
|
||||||
|
<recent name="D:\Coding\Forks\Minecraft\GeyserModelEngine\paper" />
|
||||||
<recent name="D:\Coding\Forks\Minecraft\GeyserModelEngine" />
|
<recent name="D:\Coding\Forks\Minecraft\GeyserModelEngine" />
|
||||||
</key>
|
</key>
|
||||||
<key name="MoveFile.RECENT_KEYS">
|
<key name="MoveFile.RECENT_KEYS">
|
||||||
|
<recent name="D:\Coding\Forks\Minecraft\GeyserModelEngine\paper" />
|
||||||
<recent name="D:\Coding\Forks\Minecraft\GeyserModelEngine\.github\workflows" />
|
<recent name="D:\Coding\Forks\Minecraft\GeyserModelEngine\.github\workflows" />
|
||||||
<recent name="D:\Coding\Forks\Minecraft\GeyserModelEngine\libs" />
|
<recent name="D:\Coding\Forks\Minecraft\GeyserModelEngine\libs" />
|
||||||
</key>
|
</key>
|
||||||
</component>
|
</component>
|
||||||
<component name="RunManager" selected="Gradle.GeyserModelEngine [build]">
|
<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">
|
<configuration name="GeyserModelEngine [build]" type="GradleRunConfiguration" factoryName="Gradle" temporary="true">
|
||||||
<ExternalSystemSettings>
|
<ExternalSystemSettings>
|
||||||
<option name="executionName" />
|
<option name="executionName" />
|
||||||
@@ -128,6 +182,28 @@
|
|||||||
<RunAsTest>false</RunAsTest>
|
<RunAsTest>false</RunAsTest>
|
||||||
<method v="2" />
|
<method v="2" />
|
||||||
</configuration>
|
</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">
|
<configuration name="GeyserModelEngine [jar]" type="GradleRunConfiguration" factoryName="Gradle" temporary="true">
|
||||||
<ExternalSystemSettings>
|
<ExternalSystemSettings>
|
||||||
<option name="executionName" />
|
<option name="executionName" />
|
||||||
@@ -154,6 +230,9 @@
|
|||||||
<list>
|
<list>
|
||||||
<item itemvalue="Gradle.GeyserModelEngine [build]" />
|
<item itemvalue="Gradle.GeyserModelEngine [build]" />
|
||||||
<item itemvalue="Gradle.GeyserModelEngine [jar]" />
|
<item itemvalue="Gradle.GeyserModelEngine [jar]" />
|
||||||
|
<item itemvalue="Gradle.GeyserModelEngine [clean]" />
|
||||||
|
<item itemvalue="Gradle.GeyserModelEngine [buildNeeded]" />
|
||||||
|
<item itemvalue="Gradle.GeyserModelEngine [buildDependents]" />
|
||||||
</list>
|
</list>
|
||||||
</recent_temporary>
|
</recent_temporary>
|
||||||
</component>
|
</component>
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
plugins {
|
plugins {
|
||||||
id("java")
|
id("java")
|
||||||
id("io.github.goooler.shadow") version "8.1.8"
|
id("com.gradleup.shadow") version "9.2.2"
|
||||||
}
|
}
|
||||||
|
|
||||||
group = "re.imc"
|
group = "re.imc"
|
||||||
@@ -8,30 +8,10 @@ version = "1.0.0"
|
|||||||
|
|
||||||
repositories {
|
repositories {
|
||||||
mavenCentral()
|
mavenCentral()
|
||||||
maven("https://repo.papermc.io/repository/maven-public/")
|
|
||||||
maven("https://central.sonatype.com/repository/maven-snapshots/")
|
|
||||||
|
|
||||||
maven("https://mvn.lumine.io/repository/maven-public/")
|
|
||||||
|
|
||||||
maven("https://repo.opencollab.dev/main/")
|
|
||||||
|
|
||||||
maven("https://repo.codemc.io/repository/maven-public/")
|
|
||||||
maven("https://repo.codemc.io/repository/maven-releases/")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
compileOnly("io.papermc.paper:paper-api:1.21.10-R0.1-SNAPSHOT")
|
|
||||||
implementation("dev.jorel:commandapi-paper-shade:11.0.0")
|
|
||||||
|
|
||||||
compileOnly("com.ticxo.modelengine:ModelEngine:R4.0.9")
|
|
||||||
|
|
||||||
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.10.0")
|
|
||||||
implementation("org.bstats:bstats-bukkit:3.0.2")
|
|
||||||
|
|
||||||
implementation("org.reflections:reflections:0.10.2")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
java {
|
java {
|
||||||
@@ -41,20 +21,3 @@ java {
|
|||||||
tasks.compileJava {
|
tasks.compileJava {
|
||||||
options.encoding = "UTF-8"
|
options.encoding = "UTF-8"
|
||||||
}
|
}
|
||||||
|
|
||||||
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.build {
|
|
||||||
dependsOn("shadowJar")
|
|
||||||
}
|
|
||||||
35
geyser/build.gradle.kts
Normal file
35
geyser/build.gradle.kts
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
plugins {
|
||||||
|
id("java")
|
||||||
|
id("com.gradleup.shadow") version "9.2.2"
|
||||||
|
}
|
||||||
|
|
||||||
|
group = "me.zimzaza4"
|
||||||
|
version = "1.0-SNAPSHOT"
|
||||||
|
|
||||||
|
repositories {
|
||||||
|
mavenCentral()
|
||||||
|
|
||||||
|
maven("https://repo.opencollab.dev/main/")
|
||||||
|
|
||||||
|
maven("https://maven.tomalbrc.de")
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
compileOnly("org.geysermc.geyser:api:2.9.0-SNAPSHOT")
|
||||||
|
|
||||||
|
compileOnly(files("libs/geyserutils-geyser-1.0-SNAPSHOT.jar"))
|
||||||
|
|
||||||
|
implementation("org.spongepowered:configurate-yaml:4.2.0-GeyserMC-SNAPSHOT")
|
||||||
|
implementation("com.google.code.gson:gson:2.13.1")
|
||||||
|
implementation("de.tomalbrc:blockbench-import-library:1.7.0+1.21.9")
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks.shadowJar {
|
||||||
|
archiveFileName.set("${rootProject.name}Extension-${version}.jar")
|
||||||
|
|
||||||
|
relocate("org.spongepowered.configurate", "me.zimzaza4.geysermodelenginepackgenerator.libs.configurate")
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks.build {
|
||||||
|
dependsOn("shadowJar")
|
||||||
|
}
|
||||||
BIN
geyser/libs/geyserutils-geyser-1.0-SNAPSHOT.jar
Normal file
BIN
geyser/libs/geyserutils-geyser-1.0-SNAPSHOT.jar
Normal file
Binary file not shown.
@@ -0,0 +1,71 @@
|
|||||||
|
package re.imc.geysermodelengineextension;
|
||||||
|
|
||||||
|
import org.geysermc.event.subscribe.Subscribe;
|
||||||
|
import org.geysermc.geyser.api.command.Command;
|
||||||
|
import org.geysermc.geyser.api.command.CommandSource;
|
||||||
|
import org.geysermc.geyser.api.event.lifecycle.GeyserDefineCommandsEvent;
|
||||||
|
import org.geysermc.geyser.api.event.lifecycle.GeyserDefineResourcePacksEvent;
|
||||||
|
import org.geysermc.geyser.api.event.lifecycle.GeyserPreInitializeEvent;
|
||||||
|
import org.geysermc.geyser.api.extension.Extension;
|
||||||
|
import org.geysermc.geyser.api.pack.PackCodec;
|
||||||
|
import org.geysermc.geyser.api.pack.ResourcePack;
|
||||||
|
import re.imc.geysermodelengineextension.managers.ConfigManager;
|
||||||
|
import re.imc.geysermodelengineextension.managers.resourcepack.ResourcePackManager;
|
||||||
|
|
||||||
|
public class GeyserModelEngineExtension implements Extension {
|
||||||
|
|
||||||
|
private static GeyserModelEngineExtension extension;
|
||||||
|
|
||||||
|
private ConfigManager configManager;
|
||||||
|
|
||||||
|
private ResourcePackManager resourcePackManager;
|
||||||
|
|
||||||
|
@Subscribe
|
||||||
|
public void onLoad(GeyserPreInitializeEvent event) {
|
||||||
|
extension = this;
|
||||||
|
|
||||||
|
loadManagers();
|
||||||
|
|
||||||
|
resourcePackManager.loadPack();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Subscribe
|
||||||
|
public void onDefineCommand(GeyserDefineCommandsEvent event) {
|
||||||
|
event.register(Command.builder(this)
|
||||||
|
.name("reload")
|
||||||
|
.source(CommandSource.class)
|
||||||
|
.playerOnly(false)
|
||||||
|
.description("GeyserModelPackGenerator Reload Command")
|
||||||
|
.permission("geysermodelenginepackgenerator.commands.reload")
|
||||||
|
.executor((source, command, args) -> {
|
||||||
|
resourcePackManager.loadPack();
|
||||||
|
source.sendMessage(configManager.getLang().getString("commands.geysermodelenginepackgenerator.reload.successfully-reloaded"));
|
||||||
|
})
|
||||||
|
.build());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Subscribe
|
||||||
|
public void onPackLoad(GeyserDefineResourcePacksEvent event) {
|
||||||
|
if (!configManager.getConfig().getBoolean("options.resource-pack.auto-load")) return;
|
||||||
|
|
||||||
|
ResourcePack resourcePack = ResourcePack.create(PackCodec.path(resourcePackManager.getGeneratedPackZipPath()));
|
||||||
|
event.register(resourcePack);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void loadManagers() {
|
||||||
|
this.configManager = new ConfigManager();
|
||||||
|
this.resourcePackManager = new ResourcePackManager(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static GeyserModelEngineExtension getExtension() {
|
||||||
|
return extension;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ConfigManager getConfigManager() {
|
||||||
|
return configManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ResourcePackManager getResourcePackManager() {
|
||||||
|
return resourcePackManager;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
package re.imc.geysermodelengineextension.managers;
|
||||||
|
|
||||||
|
import re.imc.geysermodelengineextension.util.FileConfiguration;
|
||||||
|
|
||||||
|
public class ConfigManager {
|
||||||
|
|
||||||
|
private FileConfiguration config, lang;
|
||||||
|
|
||||||
|
public ConfigManager() {
|
||||||
|
load();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void load() {
|
||||||
|
this.config = new FileConfiguration("config.yml");
|
||||||
|
this.lang = new FileConfiguration("Lang/messages.yml");
|
||||||
|
}
|
||||||
|
|
||||||
|
public FileConfiguration getConfig() {
|
||||||
|
return config;
|
||||||
|
}
|
||||||
|
|
||||||
|
public FileConfiguration getLang() {
|
||||||
|
return lang;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,446 @@
|
|||||||
|
package re.imc.geysermodelengineextension.managers.resourcepack;
|
||||||
|
|
||||||
|
import com.google.gson.Gson;
|
||||||
|
import com.google.gson.GsonBuilder;
|
||||||
|
import com.google.gson.JsonParser;
|
||||||
|
import re.imc.geysermodelengineextension.GeyserModelEngineExtension;
|
||||||
|
import re.imc.geysermodelengineextension.managers.resourcepack.generator.*;
|
||||||
|
import re.imc.geysermodelengineextension.managers.resourcepack.generator.data.TextureData;
|
||||||
|
import re.imc.geysermodelengineextension.util.ZipUtil;
|
||||||
|
|
||||||
|
import java.io.*;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.zip.ZipEntry;
|
||||||
|
import java.util.zip.ZipFile;
|
||||||
|
import java.util.zip.ZipOutputStream;
|
||||||
|
|
||||||
|
public class ResourcePackManager {
|
||||||
|
|
||||||
|
private final GeyserModelEngineExtension extension;
|
||||||
|
|
||||||
|
private final File inputFolder;
|
||||||
|
private final File generatedPack;
|
||||||
|
|
||||||
|
private Path generatedPackZipPath;
|
||||||
|
|
||||||
|
private final HashMap<String, Entity> entityCache = new HashMap<>();
|
||||||
|
private final HashMap<String, Animation> animationCache = new HashMap<>();
|
||||||
|
private final HashMap<String, Geometry> geometryCache = new HashMap<>();
|
||||||
|
private final HashMap<String, Map<String, TextureData>> textureCache = new HashMap<>();
|
||||||
|
|
||||||
|
private final Gson GSON = new GsonBuilder().setPrettyPrinting().create();
|
||||||
|
|
||||||
|
public ResourcePackManager(GeyserModelEngineExtension extension) {
|
||||||
|
this.extension = extension;
|
||||||
|
|
||||||
|
this.inputFolder = extension.dataFolder().resolve("input").toFile();
|
||||||
|
this.inputFolder.mkdirs();
|
||||||
|
|
||||||
|
this.generatedPack = extension.dataFolder().resolve("generated_pack").toFile();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void loadPack() {
|
||||||
|
generateResourcePack(inputFolder, generatedPack);
|
||||||
|
|
||||||
|
generatedPackZipPath = extension.dataFolder().resolve("generated_pack.zip");
|
||||||
|
|
||||||
|
try (ZipOutputStream zipOutputStream = new ZipOutputStream(Files.newOutputStream(generatedPackZipPath))) {
|
||||||
|
ZipUtil.compressFolder(generatedPack, null, zipOutputStream);
|
||||||
|
} catch (IOException err) {
|
||||||
|
throw new RuntimeException(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (Entity entity : entityCache.values()) {
|
||||||
|
entity.register(extension.getConfigManager().getConfig().getString("models.namespace"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void generateResourcePack(File inputFolder, File output) {
|
||||||
|
generateFromFolder("", inputFolder, true);
|
||||||
|
|
||||||
|
File animationsFolder = new File(output, "animations");
|
||||||
|
File entityFolder = new File(output, "entity");
|
||||||
|
File modelsFolder = new File(output, "models/entity");
|
||||||
|
File texturesFolder = new File(output, "textures/entity");
|
||||||
|
File animationControllersFolder = new File(output, "animation_controllers");
|
||||||
|
File renderControllersFolder = new File(output, "render_controllers");
|
||||||
|
File materialsFolder = new File(output, "materials");
|
||||||
|
|
||||||
|
File manifestFile = new File(output, "manifest.json");
|
||||||
|
|
||||||
|
output.mkdirs();
|
||||||
|
if (!manifestFile.exists()) {
|
||||||
|
try {
|
||||||
|
Files.writeString(manifestFile.toPath(), PackManifest.generate(), StandardCharsets.UTF_8);
|
||||||
|
} catch (IOException err) {
|
||||||
|
throw new RuntimeException(err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
animationsFolder.mkdirs();
|
||||||
|
entityFolder.mkdirs();
|
||||||
|
modelsFolder.mkdirs();
|
||||||
|
texturesFolder.mkdirs();
|
||||||
|
animationControllersFolder.mkdirs();
|
||||||
|
renderControllersFolder.mkdirs();
|
||||||
|
materialsFolder.mkdirs();
|
||||||
|
|
||||||
|
File materialFile = new File(materialsFolder, "entity.material");
|
||||||
|
|
||||||
|
if (!materialFile.exists()) {
|
||||||
|
try {
|
||||||
|
Files.writeString(materialFile.toPath(), Material.TEMPLATE, StandardCharsets.UTF_8);
|
||||||
|
} catch (IOException err) {
|
||||||
|
throw new RuntimeException(err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (Map.Entry<String, Animation> entry : animationCache.entrySet()) {
|
||||||
|
Entity entity = entityCache.get(entry.getKey());
|
||||||
|
Geometry geo = geometryCache.get(entry.getKey());
|
||||||
|
|
||||||
|
if (geo != null) entry.getValue().addHeadBind(geo);
|
||||||
|
|
||||||
|
Path path = animationsFolder.toPath().resolve(entry.getValue().getPath() + entry.getKey() + ".animation.json");
|
||||||
|
Path pathController = animationControllersFolder.toPath().resolve(entry.getValue().getPath() + entry.getKey() + ".animation_controllers.json");
|
||||||
|
|
||||||
|
pathController.toFile().getParentFile().mkdirs();
|
||||||
|
path.toFile().getParentFile().mkdirs();
|
||||||
|
|
||||||
|
if (path.toFile().exists()) continue;
|
||||||
|
|
||||||
|
AnimationController controller = new AnimationController();
|
||||||
|
controller.load(extension, entry.getValue(), entity);
|
||||||
|
|
||||||
|
try {
|
||||||
|
Files.writeString(path, GSON.toJson(entry.getValue().getJson()), StandardCharsets.UTF_8);
|
||||||
|
Files.writeString(pathController, controller.getJson().toString(), StandardCharsets.UTF_8);
|
||||||
|
} catch (IOException err) {
|
||||||
|
throw new RuntimeException(err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (Map.Entry<String, Geometry> entry : geometryCache.entrySet()) {
|
||||||
|
entry.getValue().modify();
|
||||||
|
Path path = modelsFolder.toPath().resolve(entry.getValue().getPath() + entry.getKey() + ".geo.json");
|
||||||
|
path.toFile().getParentFile().mkdirs();
|
||||||
|
String id = entry.getValue().getGeometryId();
|
||||||
|
|
||||||
|
Entity entity = entityCache.get(entry.getKey());
|
||||||
|
if (entity != null) {
|
||||||
|
ModelConfig modelConfig = entity.getModelConfig();
|
||||||
|
if (!modelConfig.getPerTextureUvSize().isEmpty()) {
|
||||||
|
for (Map.Entry<String, TextureData> textureEntry : entity.getTextureMap().entrySet()) {
|
||||||
|
String name = textureEntry.getKey();
|
||||||
|
|
||||||
|
Integer[] size = modelConfig.getPerTextureUvSize().getOrDefault(name, new Integer[]{16, 16});
|
||||||
|
String suffix = size[0] + "_" + size[1];
|
||||||
|
entry.getValue().setTextureWidth(size[0]);
|
||||||
|
entry.getValue().setTextureHeight(size[1]);
|
||||||
|
path = modelsFolder.toPath().resolve(entry.getValue().getPath() + entry.getKey() + "_" + suffix + ".geo.json");
|
||||||
|
|
||||||
|
entry.getValue().setId(id + "_" + suffix);
|
||||||
|
|
||||||
|
if (path.toFile().exists()) continue;
|
||||||
|
|
||||||
|
try {
|
||||||
|
Files.writeString(path, GSON.toJson(entry.getValue().getJson()), StandardCharsets.UTF_8);
|
||||||
|
} catch (IOException err) {
|
||||||
|
throw new RuntimeException(err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (path.toFile().exists()) continue;
|
||||||
|
|
||||||
|
try {
|
||||||
|
Files.writeString(path, GSON.toJson(entry.getValue().getJson()), StandardCharsets.UTF_8);
|
||||||
|
} catch (IOException err) {
|
||||||
|
throw new RuntimeException(err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (Map.Entry<String, Map<String, TextureData>> textures : textureCache.entrySet()) {
|
||||||
|
for (Map.Entry<String, TextureData> entry : textures.getValue().entrySet()) {
|
||||||
|
Path path = texturesFolder.toPath().resolve(entry.getValue().getPath() + textures.getKey() + "/" + entry.getKey() + ".png");
|
||||||
|
path.toFile().getParentFile().mkdirs();
|
||||||
|
|
||||||
|
if (path.toFile().exists()) continue;
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (entry.getValue().getImage() != null) Files.write(path, entry.getValue().getImage());
|
||||||
|
} catch (IOException err) {
|
||||||
|
throw new RuntimeException(err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (Map.Entry<String, Entity> entry : entityCache.entrySet()) {
|
||||||
|
Entity entity = entry.getValue();
|
||||||
|
entity.modify(extension.getConfigManager().getConfig().getString("models.namespace"));
|
||||||
|
|
||||||
|
Path entityPath = entityFolder.toPath().resolve(entity.getPath() + entry.getKey() + ".entity.json");
|
||||||
|
entityPath.toFile().getParentFile().mkdirs();
|
||||||
|
|
||||||
|
if (entityPath.toFile().exists()) continue;
|
||||||
|
|
||||||
|
try {
|
||||||
|
Files.writeString(entityPath, entity.getJson().toString(), StandardCharsets.UTF_8);
|
||||||
|
} catch (IOException err) {
|
||||||
|
throw new RuntimeException(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
// render controller part
|
||||||
|
|
||||||
|
String id = entity.getModelId();
|
||||||
|
if (!geometryCache.containsKey(id)) continue;
|
||||||
|
RenderController controller = new RenderController(id, geometryCache.get(id).getBones(), entity);
|
||||||
|
entity.setRenderController(controller);
|
||||||
|
Path renderPath = new File(renderControllersFolder, "controller.render." + id + ".json").toPath();
|
||||||
|
if (renderPath.toFile().exists()) continue;
|
||||||
|
|
||||||
|
try {
|
||||||
|
Files.writeString(renderPath, controller.generate(extension.getConfigManager().getConfig().getString("models.namespace")), StandardCharsets.UTF_8);
|
||||||
|
} catch (IOException err) {
|
||||||
|
throw new RuntimeException(err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void generateFromFolder(String currentPath, File folder, boolean root) {
|
||||||
|
if (folder.listFiles() == null) return;
|
||||||
|
|
||||||
|
String modelId = root ? "" : folder.getName().toLowerCase();
|
||||||
|
|
||||||
|
Entity entity = new Entity(modelId);
|
||||||
|
ModelConfig modelConfig = new ModelConfig();
|
||||||
|
boolean shouldOverrideConfig = false;
|
||||||
|
File textureConfigFile = new File(folder, "config.json");
|
||||||
|
|
||||||
|
if (textureConfigFile.exists()) {
|
||||||
|
try {
|
||||||
|
modelConfig = GSON.fromJson(Files.readString(textureConfigFile.toPath()), ModelConfig.class);
|
||||||
|
} catch (IOException err) {
|
||||||
|
throw new RuntimeException(err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean canAdd = false;
|
||||||
|
for (File file : folder.listFiles()) {
|
||||||
|
if (file.isDirectory()) generateFromFolder(currentPath + (root ? "" : folder.getName() + "/"), file, false);
|
||||||
|
|
||||||
|
if (file.getName().endsWith(".zip")) {
|
||||||
|
try {
|
||||||
|
generateFromZip(currentPath, file.getName().replace(".zip", "").toLowerCase(Locale.ROOT), new ZipFile(file));
|
||||||
|
} catch (IOException err) {
|
||||||
|
throw new RuntimeException(err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (entityCache.containsKey(modelId)) continue;
|
||||||
|
|
||||||
|
if (file.getName().endsWith(".png")) {
|
||||||
|
String textureName = file.getName().replace(".png", "");
|
||||||
|
Set<String> bindingBones = new HashSet<>();
|
||||||
|
bindingBones.add("*");
|
||||||
|
if (modelConfig.getBingingBones().containsKey(textureName)) bindingBones = modelConfig.getBingingBones().get(textureName);
|
||||||
|
|
||||||
|
Map<String, TextureData> map = textureCache.computeIfAbsent(modelId, s -> new HashMap<>());
|
||||||
|
try {
|
||||||
|
map.put(textureName, new TextureData(modelId, currentPath, bindingBones, Files.readAllBytes(file.toPath())));
|
||||||
|
} catch (IOException err) {
|
||||||
|
throw new RuntimeException(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
entity.setTextureMap(map);
|
||||||
|
if (modelConfig.getBingingBones().isEmpty()) {
|
||||||
|
modelConfig.getBingingBones().put(textureName, Set.of("*"));
|
||||||
|
shouldOverrideConfig = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (file.getName().endsWith(".json")) {
|
||||||
|
try {
|
||||||
|
String json = Files.readString(file.toPath());
|
||||||
|
if (isAnimationFile(json)) {
|
||||||
|
Animation animation = new Animation();
|
||||||
|
animation.setPath(currentPath);
|
||||||
|
animation.setModelId(modelId);
|
||||||
|
|
||||||
|
animation.load(json);
|
||||||
|
animationCache.put(modelId, animation);
|
||||||
|
entity.setAnimation(animation);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isGeometryFile(json)) {
|
||||||
|
Geometry geometry = new Geometry();
|
||||||
|
geometry.load(json);
|
||||||
|
geometry.setPath(currentPath);
|
||||||
|
geometry.setModelId(modelId);
|
||||||
|
geometryCache.put(modelId, geometry);
|
||||||
|
entity.setGeometry(geometry);
|
||||||
|
canAdd = true;
|
||||||
|
}
|
||||||
|
} catch (IOException err) {
|
||||||
|
throw new RuntimeException(err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (canAdd) {
|
||||||
|
// old config
|
||||||
|
File oldConfig = new File(folder, "config.properties");
|
||||||
|
Properties old = new Properties();
|
||||||
|
try {
|
||||||
|
if (oldConfig.exists()) {
|
||||||
|
old.load(new FileReader(oldConfig));
|
||||||
|
modelConfig.setMaterial(old.getProperty("material", "entity_alphatest_change_color"));
|
||||||
|
modelConfig.setEnableBlendTransition(Boolean.parseBoolean(old.getProperty("blend-transition", "true")));
|
||||||
|
modelConfig.setEnableHeadRotation(Boolean.parseBoolean(old.getProperty("head-rotation", "true")));
|
||||||
|
shouldOverrideConfig = true;
|
||||||
|
oldConfig.delete();
|
||||||
|
}
|
||||||
|
} catch (IOException err) {
|
||||||
|
throw new RuntimeException(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (shouldOverrideConfig) {
|
||||||
|
try {
|
||||||
|
Files.writeString(textureConfigFile.toPath(), GSON.toJson(modelConfig));
|
||||||
|
} catch (IOException err) {
|
||||||
|
throw new RuntimeException(err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
entity.setModelConfig(modelConfig);
|
||||||
|
entity.setPath(currentPath);
|
||||||
|
entityCache.put(modelId, entity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void generateFromZip(String currentPath, String modelId, ZipFile zip) {
|
||||||
|
Entity entity = new Entity(modelId);
|
||||||
|
if (entityCache.containsKey(modelId)) return;
|
||||||
|
|
||||||
|
ModelConfig modelConfig = new ModelConfig();
|
||||||
|
ZipEntry textureConfigFile = null;
|
||||||
|
|
||||||
|
for (Iterator<? extends ZipEntry> it = zip.entries().asIterator(); it.hasNext(); ) {
|
||||||
|
ZipEntry entry = it.next();
|
||||||
|
if (entry.getName().endsWith("config.json")) {
|
||||||
|
textureConfigFile = entry;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (textureConfigFile != null) {
|
||||||
|
try {
|
||||||
|
modelConfig = GSON.fromJson(new InputStreamReader(zip.getInputStream(textureConfigFile)), ModelConfig.class);
|
||||||
|
} catch (IOException err) {
|
||||||
|
throw new RuntimeException(err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean canAdd = false;
|
||||||
|
for (Iterator<? extends ZipEntry> it = zip.entries().asIterator(); it.hasNext(); ) {
|
||||||
|
ZipEntry e = it.next();
|
||||||
|
if (e.getName().endsWith(".png")) {
|
||||||
|
String[] path = e.getName().split("/");
|
||||||
|
String textureName = path[path.length - 1].replace(".png", "");
|
||||||
|
Set<String> bindingBones = new HashSet<>();
|
||||||
|
bindingBones.add("*");
|
||||||
|
|
||||||
|
if (modelConfig.getBingingBones().containsKey(textureName)) {
|
||||||
|
bindingBones = modelConfig.getBingingBones().get(textureName);
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, TextureData> map = textureCache.computeIfAbsent(modelId, s -> new HashMap<>());
|
||||||
|
try {
|
||||||
|
map.put(textureName, new TextureData(modelId, currentPath, bindingBones, zip.getInputStream(e).readAllBytes()));
|
||||||
|
} catch (IOException err) {
|
||||||
|
throw new RuntimeException(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
entity.setTextureMap(map);
|
||||||
|
if (modelConfig.getBingingBones().isEmpty()) modelConfig.getBingingBones().put(textureName, Set.of("*"));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (e.getName().endsWith(".json")) {
|
||||||
|
try {
|
||||||
|
InputStream stream = zip.getInputStream(e);
|
||||||
|
String json = new String(stream.readAllBytes());
|
||||||
|
if (isAnimationFile(json)) {
|
||||||
|
Animation animation = new Animation();
|
||||||
|
animation.setPath(currentPath);
|
||||||
|
animation.setModelId(modelId);
|
||||||
|
|
||||||
|
animation.load(json);
|
||||||
|
animationCache.put(modelId, animation);
|
||||||
|
entity.setAnimation(animation);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isGeometryFile(json)) {
|
||||||
|
Geometry geometry = new Geometry();
|
||||||
|
geometry.load(json);
|
||||||
|
geometry.setPath(currentPath);
|
||||||
|
geometry.setModelId(modelId);
|
||||||
|
geometryCache.put(modelId, geometry);
|
||||||
|
entity.setGeometry(geometry);
|
||||||
|
canAdd = true;
|
||||||
|
}
|
||||||
|
} catch (IOException err) {
|
||||||
|
throw new RuntimeException(err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (canAdd) {
|
||||||
|
entity.setModelConfig(modelConfig);
|
||||||
|
entity.setPath(currentPath);
|
||||||
|
entityCache.put(modelId, entity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isGeometryFile(String json) {
|
||||||
|
try {
|
||||||
|
return JsonParser.parseString(json).getAsJsonObject().has("minecraft:geometry");
|
||||||
|
} catch (Throwable ignored) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isAnimationFile(String json) {
|
||||||
|
try {
|
||||||
|
return JsonParser.parseString(json).getAsJsonObject().has("animations");
|
||||||
|
} catch (Throwable ignored) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public File getInputFolder() {
|
||||||
|
return inputFolder;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Path getGeneratedPackZipPath() {
|
||||||
|
return generatedPackZipPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
public HashMap<String, Entity> getEntityCache() {
|
||||||
|
return entityCache;
|
||||||
|
}
|
||||||
|
|
||||||
|
public HashMap<String, Animation> getAnimationCache() {
|
||||||
|
return animationCache;
|
||||||
|
}
|
||||||
|
|
||||||
|
public HashMap<String, Geometry> getGeometryCache() {
|
||||||
|
return geometryCache;
|
||||||
|
}
|
||||||
|
|
||||||
|
public HashMap<String, Map<String, TextureData>> getTextureCache() {
|
||||||
|
return textureCache;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,151 @@
|
|||||||
|
package re.imc.geysermodelengineextension.managers.resourcepack.generator;
|
||||||
|
|
||||||
|
import com.google.gson.JsonArray;
|
||||||
|
import com.google.gson.JsonElement;
|
||||||
|
import com.google.gson.JsonObject;
|
||||||
|
import com.google.gson.JsonParser;
|
||||||
|
import re.imc.geysermodelengineextension.GeyserModelEngineExtension;
|
||||||
|
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
public class Animation {
|
||||||
|
|
||||||
|
private String modelId;
|
||||||
|
private JsonObject json;
|
||||||
|
private Set<String> animationIds = new HashSet<>();
|
||||||
|
|
||||||
|
private String path;
|
||||||
|
|
||||||
|
public static final String HEAD_TEMPLATE = """
|
||||||
|
{
|
||||||
|
"relative_to" : {
|
||||||
|
"rotation" : "entity"
|
||||||
|
},
|
||||||
|
"rotation" : [ "query.target_x_rotation - this", "query.target_y_rotation - this", 0.0 ]
|
||||||
|
}
|
||||||
|
""";
|
||||||
|
|
||||||
|
public void load(String string) {
|
||||||
|
this.json = JsonParser.parseString(string).getAsJsonObject();
|
||||||
|
JsonObject newAnimations = new JsonObject();
|
||||||
|
|
||||||
|
for (Map.Entry<String, JsonElement> element : json.get("animations").getAsJsonObject().entrySet()) {
|
||||||
|
animationIds.add(element.getKey());
|
||||||
|
JsonObject animation = element.getValue().getAsJsonObject();
|
||||||
|
|
||||||
|
if (animation.has("override_previous_animation")) {
|
||||||
|
if (animation.get("override_previous_animation").getAsBoolean()) {
|
||||||
|
if (!animation.has("loop")) {
|
||||||
|
animation.addProperty("loop", "hold_on_last_frame");
|
||||||
|
// play once but override must use this to avoid strange anim
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
animation.remove("override_previous_animation");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (animation.has("loop")) {
|
||||||
|
if (animation.get("loop").getAsJsonPrimitive().isString()) {
|
||||||
|
if (animation.get("loop").getAsString().equals("hold_on_last_frame")) {
|
||||||
|
if (!animation.has("bones")) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
for (Map.Entry<String, JsonElement> bone : animation.get("bones").getAsJsonObject().entrySet()) {
|
||||||
|
|
||||||
|
for (Map.Entry<String, JsonElement> anim : bone.getValue().getAsJsonObject().entrySet()) {
|
||||||
|
float max = -1;
|
||||||
|
JsonObject end = null;
|
||||||
|
if (!anim.getValue().isJsonObject()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
for (Map.Entry<String, JsonElement> timeline : anim.getValue().getAsJsonObject().entrySet()) {
|
||||||
|
float time = Float.parseFloat(timeline.getKey());
|
||||||
|
if (time > max) {
|
||||||
|
max = time;
|
||||||
|
if (timeline.getValue().isJsonObject()) {
|
||||||
|
end = timeline.getValue().getAsJsonObject();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Throwable ignored) {}
|
||||||
|
if (end != null && end.has("lerp_mode") && end.get("lerp_mode").getAsString().equals("catmullrom")) {
|
||||||
|
end.addProperty("lerp_mode", "linear");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
newAnimations.add("animation." + modelId + "." + element.getKey().replace(" ", "_"), element.getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
json.add("animations", newAnimations);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addHeadBind(Geometry geometry) {
|
||||||
|
JsonObject object = new JsonObject();
|
||||||
|
object.addProperty("loop", true);
|
||||||
|
JsonObject bones = new JsonObject();
|
||||||
|
JsonArray array = geometry.getInternal().get("bones").getAsJsonArray();
|
||||||
|
|
||||||
|
int i = 0;
|
||||||
|
|
||||||
|
for (JsonElement element : array) {
|
||||||
|
if (element.isJsonObject()) {
|
||||||
|
String name = element.getAsJsonObject().get("name").getAsString();
|
||||||
|
|
||||||
|
String parent = "";
|
||||||
|
if (element.getAsJsonObject().has("parent")) parent = element.getAsJsonObject().get("parent").getAsString();
|
||||||
|
if (parent.startsWith("h_") || parent.startsWith("hi_")) continue;
|
||||||
|
|
||||||
|
if (name.startsWith("h_") || name.startsWith("hi_")) {
|
||||||
|
bones.add(name, JsonParser.parseString(HEAD_TEMPLATE));
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i == 0) return;
|
||||||
|
|
||||||
|
GeyserModelEngineExtension.getExtension().getResourcePackManager().getEntityCache().get(modelId).setHasHeadAnimation(true);
|
||||||
|
|
||||||
|
object.add("bones", bones);
|
||||||
|
json.get("animations").getAsJsonObject().add("animation." + modelId + ".look_at_target", object);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setModelId(String modelId) {
|
||||||
|
this.modelId = modelId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setJson(JsonObject json) {
|
||||||
|
this.json = json;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAnimationIds(Set<String> animationIds) {
|
||||||
|
this.animationIds = animationIds;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getModelId() {
|
||||||
|
return modelId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPath(String path) {
|
||||||
|
this.path = path;
|
||||||
|
}
|
||||||
|
|
||||||
|
public JsonObject getJson() {
|
||||||
|
return json;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set<String> getAnimationIds() {
|
||||||
|
return animationIds;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getPath() {
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,84 @@
|
|||||||
|
package re.imc.geysermodelengineextension.managers.resourcepack.generator;
|
||||||
|
|
||||||
|
import com.google.gson.JsonElement;
|
||||||
|
import com.google.gson.JsonObject;
|
||||||
|
import com.google.gson.JsonParser;
|
||||||
|
import re.imc.geysermodelengineextension.GeyserModelEngineExtension;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public class AnimationController {
|
||||||
|
|
||||||
|
private JsonObject json;
|
||||||
|
private Entity entity;
|
||||||
|
|
||||||
|
public static final String CONTROLLER_TEMPLATE =
|
||||||
|
"""
|
||||||
|
{
|
||||||
|
"initial_state": "stop",
|
||||||
|
"states": {
|
||||||
|
"play": {
|
||||||
|
"animations": [
|
||||||
|
"%anim%"
|
||||||
|
],
|
||||||
|
"blend_transition": 0.1,
|
||||||
|
"transitions": [{ "stop": "%query% == 0"}]
|
||||||
|
},
|
||||||
|
"stop": {
|
||||||
|
"blend_transition": 0.1,
|
||||||
|
"transitions": [{ "play": "%query% != 0"}]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
""";
|
||||||
|
|
||||||
|
public void load(GeyserModelEngineExtension extension, Animation animation, Entity entity) {
|
||||||
|
JsonObject root = new JsonObject();
|
||||||
|
json = root;
|
||||||
|
root.addProperty("format_version", "1.10.0");
|
||||||
|
|
||||||
|
JsonObject animationControllers = new JsonObject();
|
||||||
|
root.add("animation_controllers", animationControllers);
|
||||||
|
|
||||||
|
List<String> sorted = new ArrayList<>(animation.getAnimationIds());
|
||||||
|
int i = 0;
|
||||||
|
|
||||||
|
Collections.sort(sorted);
|
||||||
|
for (String id : sorted) {
|
||||||
|
id = id.replace(" ", "_");
|
||||||
|
int n = (int) Math.pow(2, (i % 24));
|
||||||
|
|
||||||
|
JsonObject controller = JsonParser.parseString(CONTROLLER_TEMPLATE.replace("%anim%", id).replace("%query%", "math.mod(math.floor(query.property('" + extension.getConfigManager().getConfig().getString("models.namespace") + ":anim" + i / 24 + "') / " + n + "), 2)")).getAsJsonObject();
|
||||||
|
animationControllers.add("controller.animation." + animation.getModelId() + "." + id, controller);
|
||||||
|
i++;
|
||||||
|
if (entity != null) {
|
||||||
|
boolean blend = entity.getModelConfig().isEnableBlendTransition();
|
||||||
|
|
||||||
|
if (!blend) {
|
||||||
|
for (Map.Entry<String, JsonElement> states : controller.get("states").getAsJsonObject().entrySet()) {
|
||||||
|
states.getValue().getAsJsonObject().remove("blend_transition");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setJson(JsonObject json) {
|
||||||
|
this.json = json;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setEntity(Entity entity) {
|
||||||
|
this.entity = entity;
|
||||||
|
}
|
||||||
|
|
||||||
|
public JsonObject getJson() {
|
||||||
|
return json;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Entity getEntity() {
|
||||||
|
return entity;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,207 @@
|
|||||||
|
package re.imc.geysermodelengineextension.managers.resourcepack.generator;
|
||||||
|
|
||||||
|
import com.google.gson.JsonArray;
|
||||||
|
import com.google.gson.JsonObject;
|
||||||
|
import com.google.gson.JsonParser;
|
||||||
|
import me.zimzaza4.geyserutils.geyser.GeyserUtils;
|
||||||
|
import re.imc.geysermodelengineextension.managers.resourcepack.generator.data.TextureData;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
public class Entity {
|
||||||
|
|
||||||
|
public static final Set<String> REGISTERED_ENTITIES = new HashSet<>();
|
||||||
|
|
||||||
|
private String modelId;
|
||||||
|
private JsonObject json;
|
||||||
|
private boolean hasHeadAnimation = false;
|
||||||
|
private Animation animation;
|
||||||
|
private Geometry geometry;
|
||||||
|
private RenderController renderController;
|
||||||
|
private String path;
|
||||||
|
private Map<String, TextureData> textureMap = new HashMap<>();
|
||||||
|
private ModelConfig modelConfig;
|
||||||
|
|
||||||
|
public static final String TEMPLATE = """
|
||||||
|
{
|
||||||
|
"format_version": "1.10.0",
|
||||||
|
"minecraft:client_entity": {
|
||||||
|
"description": {
|
||||||
|
"identifier": "%namespace%:%entity_id%",
|
||||||
|
"materials": {
|
||||||
|
"default": "%material%",
|
||||||
|
"anim": "entity_alphatest_anim_change_color_one_sided"
|
||||||
|
},
|
||||||
|
"textures": {
|
||||||
|
},
|
||||||
|
"geometry": {
|
||||||
|
|
||||||
|
},
|
||||||
|
"animations": {
|
||||||
|
"look_at_target": "%look_at_target%"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"animate": [
|
||||||
|
"look_at_target"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"render_controllers": [
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
""";
|
||||||
|
|
||||||
|
public Entity(String modelId) {
|
||||||
|
this.modelId = modelId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void modify(String namespace) {
|
||||||
|
this.json = JsonParser.parseString(TEMPLATE.replace("%namespace%", namespace)
|
||||||
|
.replace("%entity_id%", modelId)
|
||||||
|
.replace("%geometry%", "geometry.meg_" + modelId)
|
||||||
|
.replace("%texture%", "textures/entity/" + path + modelId)
|
||||||
|
.replace("%look_at_target%", modelConfig.isEnableHeadRotation() ? "animation." + modelId + ".look_at_target" : "animation.none")
|
||||||
|
.replace("%material%", modelConfig.getMaterial())).getAsJsonObject();
|
||||||
|
|
||||||
|
JsonObject description = json.get("minecraft:client_entity").getAsJsonObject().get("description").getAsJsonObject();
|
||||||
|
JsonObject jsonAnimations = description.get("animations").getAsJsonObject();
|
||||||
|
JsonObject jsonTextures = description.get("textures").getAsJsonObject();
|
||||||
|
JsonObject jsonGeometry = description.get("geometry").getAsJsonObject();
|
||||||
|
JsonObject jsonMaterials = description.get("materials").getAsJsonObject();
|
||||||
|
|
||||||
|
JsonArray jsonRenderControllers = description.get("render_controllers").getAsJsonArray();
|
||||||
|
|
||||||
|
Map<String, String> materials = modelConfig.getTextureMaterials();
|
||||||
|
materials.forEach(jsonMaterials::addProperty);
|
||||||
|
|
||||||
|
if (modelConfig.getPerTextureUvSize().isEmpty()) {
|
||||||
|
jsonGeometry.addProperty("default", "geometry.meg_" + modelId);
|
||||||
|
jsonTextures.addProperty("default", "textures/entity/" + path + modelId + "/" + textureMap.keySet().stream().findFirst().orElse("def"));
|
||||||
|
}
|
||||||
|
|
||||||
|
for (String name : textureMap.keySet()) {
|
||||||
|
if (name.endsWith("_e")) continue;
|
||||||
|
|
||||||
|
if (modelConfig.getPerTextureUvSize().containsKey(name)) {
|
||||||
|
Integer[] size = modelConfig.getPerTextureUvSize().getOrDefault(name, new Integer[]{16, 16});
|
||||||
|
String suffix = size[0] + "_" + size[1];
|
||||||
|
|
||||||
|
jsonGeometry.addProperty("t_" + suffix, "geometry.meg_" + modelId + "_" + suffix);
|
||||||
|
jsonTextures.addProperty(name, "textures/entity/" + path + modelId + "/" + name);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
jsonRenderControllers.add("controller.render." + modelId + "_" + name);
|
||||||
|
}
|
||||||
|
|
||||||
|
JsonArray animate = description.get("scripts").getAsJsonObject().get("animate").getAsJsonArray();
|
||||||
|
|
||||||
|
if (animation != null) {
|
||||||
|
for (String animation : animation.getAnimationIds()) {
|
||||||
|
animation = animation.replace(" ", "_");
|
||||||
|
String controller = "controller.animation." + modelId + "." + animation;
|
||||||
|
animate.add(animation + "_control");
|
||||||
|
jsonAnimations.addProperty(animation, "animation." + modelId + "." + animation);
|
||||||
|
jsonAnimations.addProperty(animation + "_control", controller);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void register(String namespace) {
|
||||||
|
String id = namespace + ":" + modelId;
|
||||||
|
|
||||||
|
boolean registered = REGISTERED_ENTITIES.contains(id);
|
||||||
|
if (registered) return;
|
||||||
|
|
||||||
|
REGISTERED_ENTITIES.add(id);
|
||||||
|
GeyserUtils.addCustomEntity(id);
|
||||||
|
if (geometry == null) return;
|
||||||
|
|
||||||
|
if (!modelConfig.isDisablePartVisibility()) {
|
||||||
|
for (int i = 0; i < Math.ceil(geometry.getBones().size() / 24f); i++) {
|
||||||
|
GeyserUtils.addProperty(id, namespace + ":" + "bone" + i, Integer.class);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (animation != null) {
|
||||||
|
for (int i = 0; i < Math.ceil(animation.getAnimationIds().size() / 24f); i++) {
|
||||||
|
GeyserUtils.addProperty(id, namespace + ":" + "anim" + i, Integer.class);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
GeyserUtils.registerProperties(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setModelId(String modelId) {
|
||||||
|
this.modelId = modelId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setJson(JsonObject json) {
|
||||||
|
this.json = json;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setHasHeadAnimation(boolean hasHeadAnimation) {
|
||||||
|
this.hasHeadAnimation = hasHeadAnimation;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAnimation(Animation animation) {
|
||||||
|
this.animation = animation;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setGeometry(Geometry geometry) {
|
||||||
|
this.geometry = geometry;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setRenderController(RenderController renderController) {
|
||||||
|
this.renderController = renderController;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPath(String path) {
|
||||||
|
this.path = path;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTextureMap(Map<String, TextureData> textureMap) {
|
||||||
|
this.textureMap = textureMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setModelConfig(ModelConfig modelConfig) {
|
||||||
|
this.modelConfig = modelConfig;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getModelId() {
|
||||||
|
return modelId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public JsonObject getJson() {
|
||||||
|
return json;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isHasHeadAnimation() {
|
||||||
|
return hasHeadAnimation;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Animation getAnimation() {
|
||||||
|
return animation;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Geometry getGeometry() {
|
||||||
|
return geometry;
|
||||||
|
}
|
||||||
|
|
||||||
|
public RenderController getRenderController() {
|
||||||
|
return renderController;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getPath() {
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map<String, TextureData> getTextureMap() {
|
||||||
|
return textureMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ModelConfig getModelConfig() {
|
||||||
|
return modelConfig;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,113 @@
|
|||||||
|
package re.imc.geysermodelengineextension.managers.resourcepack.generator;
|
||||||
|
|
||||||
|
import com.google.gson.*;
|
||||||
|
import re.imc.geysermodelengineextension.managers.resourcepack.generator.data.BoneData;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
public class Geometry {
|
||||||
|
|
||||||
|
private String modelId;
|
||||||
|
private String geometryId;
|
||||||
|
private JsonObject json;
|
||||||
|
private final Map<String, BoneData> bones = new HashMap<>();
|
||||||
|
|
||||||
|
private String path;
|
||||||
|
|
||||||
|
public void load(String json) {
|
||||||
|
this.json = JsonParser.parseString(json).getAsJsonObject();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setId(String id) {
|
||||||
|
geometryId = id;
|
||||||
|
getInternal().get("description").getAsJsonObject().addProperty("identifier", id);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTextureWidth(int w) {
|
||||||
|
getInternal().get("description").getAsJsonObject().addProperty("texture_width", w);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTextureHeight(int h) {
|
||||||
|
getInternal().get("description").getAsJsonObject().addProperty("texture_height", h);
|
||||||
|
}
|
||||||
|
|
||||||
|
public JsonObject getInternal() {
|
||||||
|
return json.get("minecraft:geometry").getAsJsonArray().get(0).getAsJsonObject();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void modify() {
|
||||||
|
JsonArray array = getInternal().get("bones").getAsJsonArray();
|
||||||
|
Iterator<JsonElement> iterator = array.iterator();
|
||||||
|
while (iterator.hasNext()) {
|
||||||
|
JsonElement element = iterator.next();
|
||||||
|
if (element.isJsonObject()) {
|
||||||
|
String name = element.getAsJsonObject().get("name").getAsString().toLowerCase(Locale.ROOT);
|
||||||
|
|
||||||
|
String parent = element.getAsJsonObject().has("parent") ? element.getAsJsonObject().get("parent").getAsString().toLowerCase() : null;
|
||||||
|
element.getAsJsonObject().remove("name");
|
||||||
|
element.getAsJsonObject().addProperty("name", name);
|
||||||
|
|
||||||
|
if (name.equals("hitbox") || name.equals("shadow") || name.equals("mount") || name.startsWith("b_") || name.startsWith("ob_")) {
|
||||||
|
iterator.remove();
|
||||||
|
} else {
|
||||||
|
bones.put(name, new BoneData(name, parent, new HashSet<>(), new HashSet<>()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (BoneData bone : bones.values()) {
|
||||||
|
if (bone.getParent() != null) {
|
||||||
|
BoneData parent = bones.get(bone.getParent());
|
||||||
|
if (parent != null) {
|
||||||
|
parent.getChildren().add(bone);
|
||||||
|
addAllChildren(parent, bone);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
setId("geometry.meg_" + modelId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addAllChildren(BoneData p, BoneData c) {
|
||||||
|
p.getAllChildren().add(c);
|
||||||
|
BoneData parent = bones.get(p.getParent());
|
||||||
|
if (parent != null) {
|
||||||
|
addAllChildren(parent, c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setModelId(String modelId) {
|
||||||
|
this.modelId = modelId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setGeometryId(String geometryId) {
|
||||||
|
this.geometryId = geometryId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setJson(JsonObject json) {
|
||||||
|
this.json = json;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPath(String path) {
|
||||||
|
this.path = path;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getModelId() {
|
||||||
|
return modelId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getGeometryId() {
|
||||||
|
return geometryId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public JsonObject getJson() {
|
||||||
|
return json;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getPath() {
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map<String, BoneData> getBones() {
|
||||||
|
return bones;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
package re.imc.geysermodelengineextension.managers.resourcepack.generator;
|
||||||
|
|
||||||
|
public class Material {
|
||||||
|
|
||||||
|
public static final String TEMPLATE = """
|
||||||
|
{
|
||||||
|
"materials":{
|
||||||
|
"version":"1.0.0",
|
||||||
|
"entity_alphatest_anim_change_color:entity_alphatest_change_color":{
|
||||||
|
"+defines":[
|
||||||
|
"USE_UV_ANIM"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"entity_change_color_one_sided:entity": {
|
||||||
|
"+defines": [
|
||||||
|
"USE_OVERLAY",
|
||||||
|
"USE_COLOR_MASK"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"entity_alphatest_change_color_one_sided:entity_change_color_one_sided": {
|
||||||
|
"+defines": [ "ALPHA_TEST" ],
|
||||||
|
"+samplerStates": [
|
||||||
|
{
|
||||||
|
"samplerIndex": 1,
|
||||||
|
"textureWrap": "Repeat"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"msaaSupport": "Both"
|
||||||
|
},
|
||||||
|
"entity_alphatest_anim_change_color_one_sided:entity_alphatest_change_color_one_sided":{
|
||||||
|
"+defines":[
|
||||||
|
"USE_UV_ANIM"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
""";
|
||||||
|
}
|
||||||
@@ -0,0 +1,104 @@
|
|||||||
|
package re.imc.geysermodelengineextension.managers.resourcepack.generator;
|
||||||
|
|
||||||
|
import com.google.gson.annotations.SerializedName;
|
||||||
|
import lombok.*;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
@AllArgsConstructor
|
||||||
|
@NoArgsConstructor
|
||||||
|
@ToString
|
||||||
|
public class ModelConfig {
|
||||||
|
|
||||||
|
@SerializedName("head_rotation")
|
||||||
|
boolean enableHeadRotation = true;
|
||||||
|
@SerializedName("material")
|
||||||
|
String material = "entity_alphatest_change_color_one_sided";
|
||||||
|
@SerializedName("blend_transition")
|
||||||
|
boolean enableBlendTransition = true;
|
||||||
|
@SerializedName("binding_bones")
|
||||||
|
Map<String, Set<String>> bingingBones = new HashMap<>();
|
||||||
|
@SerializedName("anim_textures")
|
||||||
|
Map<String, AnimTextureOptions> animTextures = new HashMap<>();
|
||||||
|
@SerializedName("texture_materials")
|
||||||
|
Map<String, String> textureMaterials = new HashMap<>();
|
||||||
|
@SerializedName("per_texture_uv_size")
|
||||||
|
Map<String, Integer[]> perTextureUvSize;
|
||||||
|
@SerializedName("disable_part_visibility")
|
||||||
|
boolean disablePartVisibility = true;
|
||||||
|
|
||||||
|
public void setEnableHeadRotation(boolean enableHeadRotation) {
|
||||||
|
this.enableHeadRotation = enableHeadRotation;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMaterial(String material) {
|
||||||
|
this.material = material;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setEnableBlendTransition(boolean enableBlendTransition) {
|
||||||
|
this.enableBlendTransition = enableBlendTransition;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setBingingBones(Map<String, Set<String>> bingingBones) {
|
||||||
|
this.bingingBones = bingingBones;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAnimTextures(Map<String, AnimTextureOptions> animTextures) {
|
||||||
|
this.animTextures = animTextures;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTextureMaterials(Map<String, String> textureMaterials) {
|
||||||
|
this.textureMaterials = textureMaterials;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPerTextureUvSize(Map<String, Integer[]> perTextureUvSize) {
|
||||||
|
this.perTextureUvSize = perTextureUvSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDisablePartVisibility(boolean disablePartVisibility) {
|
||||||
|
this.disablePartVisibility = disablePartVisibility;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map<String, String> getTextureMaterials() {
|
||||||
|
return textureMaterials != null ? textureMaterials : Map.of();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map<String, Integer[]> getPerTextureUvSize() {
|
||||||
|
return perTextureUvSize != null ? perTextureUvSize : Map.of();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isEnableHeadRotation() {
|
||||||
|
return enableHeadRotation;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getMaterial() {
|
||||||
|
return material;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isEnableBlendTransition() {
|
||||||
|
return enableBlendTransition;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map<String, Set<String>> getBingingBones() {
|
||||||
|
return bingingBones;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map<String, AnimTextureOptions> getAnimTextures() {
|
||||||
|
return animTextures;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isDisablePartVisibility() {
|
||||||
|
return disablePartVisibility;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
public static class AnimTextureOptions {
|
||||||
|
float fps;
|
||||||
|
int frames;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
package re.imc.geysermodelengineextension.managers.resourcepack.generator;
|
||||||
|
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
public class PackManifest {
|
||||||
|
|
||||||
|
public static final String TEMPLATE = """
|
||||||
|
{
|
||||||
|
"format_version": 2,
|
||||||
|
"header": {
|
||||||
|
"name": "GeyserModelEngine",
|
||||||
|
"description": "GeyserModelEngine For Geyser",
|
||||||
|
"uuid": "%uuid-1%",
|
||||||
|
"version": [0, 0, 1],
|
||||||
|
"min_engine_version": [1, 21, 100]
|
||||||
|
},
|
||||||
|
"modules": [
|
||||||
|
{
|
||||||
|
"type": "resources",
|
||||||
|
"description": "GeyserModelEngine",
|
||||||
|
"uuid": "%uuid-2%",
|
||||||
|
"version": [0, 0, 1]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
""";
|
||||||
|
|
||||||
|
public static String generate() {
|
||||||
|
return TEMPLATE.replace("%uuid-1%", UUID.randomUUID().toString())
|
||||||
|
.replace("%uuid-2%", UUID.randomUUID().toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,178 @@
|
|||||||
|
package re.imc.geysermodelengineextension.managers.resourcepack.generator;
|
||||||
|
|
||||||
|
import com.google.gson.JsonArray;
|
||||||
|
import com.google.gson.JsonObject;
|
||||||
|
import re.imc.geysermodelengineextension.managers.resourcepack.generator.data.BoneData;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
public class RenderController {
|
||||||
|
|
||||||
|
public static final Set<String> NEED_REMOVE_WHEN_SORT = Set.of("pbody_", "plarm_", "prarm_", "plleg_", "prleg_", "phead_", "p_");
|
||||||
|
|
||||||
|
private final String modelId;
|
||||||
|
private final Map<String, BoneData> bones;
|
||||||
|
private final Entity entity;
|
||||||
|
|
||||||
|
public RenderController(String modelId, Map<String, BoneData> bones, Entity entity) {
|
||||||
|
this.modelId = modelId;
|
||||||
|
this.bones = bones;
|
||||||
|
this.entity = entity;
|
||||||
|
}
|
||||||
|
|
||||||
|
// look, I'm fine with your other code and stuff, but I ain't using templates for JSON lmao
|
||||||
|
public String generate(String namespace) {
|
||||||
|
List<String> se = new ArrayList<>(bones.keySet());
|
||||||
|
Collections.sort(se);
|
||||||
|
JsonObject root = new JsonObject();
|
||||||
|
root.addProperty("format_version", "1.8.0");
|
||||||
|
|
||||||
|
JsonObject renderControllers = new JsonObject();
|
||||||
|
root.add("render_controllers", renderControllers);
|
||||||
|
|
||||||
|
Set<BoneData> processedBones = new HashSet<>();
|
||||||
|
boolean singleTexture = entity.getTextureMap().size() == 1 && entity.getModelConfig().getPerTextureUvSize().isEmpty();
|
||||||
|
for (String key : entity.getTextureMap().keySet()) {
|
||||||
|
if (key.endsWith("_e")) continue;
|
||||||
|
|
||||||
|
// Texture texture = entity.textureMap.get(key);
|
||||||
|
Set<String> uvBonesId = entity.getModelConfig().getBingingBones().get(key);
|
||||||
|
|
||||||
|
if (uvBonesId == null) {
|
||||||
|
if (!singleTexture) {
|
||||||
|
continue;
|
||||||
|
} else {
|
||||||
|
uvBonesId = new HashSet<>();
|
||||||
|
uvBonesId.add("*");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ModelConfig.AnimTextureOptions anim = entity.getModelConfig().getAnimTextures().get(key);
|
||||||
|
|
||||||
|
JsonObject controller = new JsonObject();
|
||||||
|
|
||||||
|
renderControllers.add("controller.render." + modelId + "_" + key, controller);
|
||||||
|
|
||||||
|
if (!entity.getModelConfig().getPerTextureUvSize().isEmpty()) {
|
||||||
|
Integer[] size = entity.getModelConfig().getPerTextureUvSize().getOrDefault(key, new Integer[]{16, 16});
|
||||||
|
String suffix = "t_" + size[0] + "_" + size[1];
|
||||||
|
controller.addProperty("geometry", "Geometry." + suffix);
|
||||||
|
} else {
|
||||||
|
controller.addProperty("geometry", "Geometry.default");
|
||||||
|
}
|
||||||
|
|
||||||
|
JsonArray materials = new JsonArray();
|
||||||
|
String material = entity.getModelConfig().getTextureMaterials().get(key);
|
||||||
|
|
||||||
|
JsonObject materialItem = new JsonObject();
|
||||||
|
if (material != null) {
|
||||||
|
materialItem.addProperty("*", "Material." + material);
|
||||||
|
} else if (anim != null) {
|
||||||
|
materialItem.addProperty("*", "Material.anim");
|
||||||
|
JsonObject uvAnim = new JsonObject();
|
||||||
|
controller.add("uv_anim", uvAnim);
|
||||||
|
JsonArray offset = new JsonArray();
|
||||||
|
offset.add(0.0);
|
||||||
|
offset.add("math.mod(math.floor(q.life_time * " + anim.fps + ")," + anim.frames + ") / " + anim.frames);
|
||||||
|
uvAnim.add("offset", offset);
|
||||||
|
JsonArray scale = new JsonArray();
|
||||||
|
scale.add(1.0);
|
||||||
|
scale.add("1 / " + anim.frames);
|
||||||
|
uvAnim.add("scale", scale);
|
||||||
|
} else {
|
||||||
|
materialItem.addProperty("*", "Material.default");
|
||||||
|
}
|
||||||
|
materials.add(materialItem);
|
||||||
|
controller.add("materials", materials);
|
||||||
|
|
||||||
|
JsonArray textures = new JsonArray();
|
||||||
|
if (singleTexture) {
|
||||||
|
textures.add("Texture.default");
|
||||||
|
} else {
|
||||||
|
textures.add("Texture." + key);
|
||||||
|
}
|
||||||
|
|
||||||
|
controller.add("textures", textures);
|
||||||
|
|
||||||
|
// if (enable) {
|
||||||
|
JsonArray partVisibility = new JsonArray();
|
||||||
|
JsonObject visibilityDefault = new JsonObject();
|
||||||
|
visibilityDefault.addProperty("*", false);
|
||||||
|
partVisibility.add(visibilityDefault);
|
||||||
|
int i = 0;
|
||||||
|
List<String> sorted = new ArrayList<>(bones.keySet());
|
||||||
|
Map<String, String> originalId = new HashMap<>();
|
||||||
|
ListIterator<String> iterator = sorted.listIterator();
|
||||||
|
while (iterator.hasNext()) {
|
||||||
|
String s = iterator.next();
|
||||||
|
String o = s;
|
||||||
|
for (String r : NEED_REMOVE_WHEN_SORT) {
|
||||||
|
s = s.replace(r, "");
|
||||||
|
}
|
||||||
|
iterator.set(s);
|
||||||
|
originalId.put(s, o);
|
||||||
|
}
|
||||||
|
Collections.sort(sorted);
|
||||||
|
|
||||||
|
Set<String> uvAllBones = new HashSet<>();
|
||||||
|
for (String uvBone : uvBonesId) {
|
||||||
|
if (uvBone.equals("*")) {
|
||||||
|
uvAllBones.addAll(bones.keySet());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!bones.containsKey(uvBone.toLowerCase())) continue;
|
||||||
|
|
||||||
|
uvAllBones.add(uvBone.toLowerCase());
|
||||||
|
}
|
||||||
|
|
||||||
|
for (String boneName : sorted) {
|
||||||
|
boneName = originalId.get(boneName);
|
||||||
|
JsonObject visibilityItem = new JsonObject();
|
||||||
|
BoneData bone = bones.get(boneName);
|
||||||
|
boolean uvParent = false;
|
||||||
|
|
||||||
|
for (BoneData child : bone.getAllChildren()) {
|
||||||
|
if (child.getName().startsWith("uv_")) {
|
||||||
|
if (uvAllBones.contains(child.getName())) {
|
||||||
|
uvParent = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
for (Map.Entry<String, Set<String>> entry : entity.getModelConfig().getBingingBones().entrySet()) {
|
||||||
|
if (entry.getKey().equals(key)) continue;
|
||||||
|
|
||||||
|
if (entry.getValue().stream().anyMatch(boneName::equalsIgnoreCase)) {
|
||||||
|
uvParent = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!processedBones.contains(bone) && (uvParent || uvAllBones.contains(boneName) || uvBonesId.contains("*"))) {
|
||||||
|
int index = i;
|
||||||
|
if (boneName.startsWith("uv_")) {
|
||||||
|
index = sorted.indexOf(bone.getParent());
|
||||||
|
}
|
||||||
|
|
||||||
|
int n = (int) Math.pow(2, (index % 24));
|
||||||
|
if (entity.getModelConfig().isDisablePartVisibility()) {
|
||||||
|
visibilityItem.addProperty(boneName, true);
|
||||||
|
} else {
|
||||||
|
visibilityItem.addProperty(boneName, "math.mod(math.floor(query.property('" + namespace + ":bone" + index / 24 + "') / " + n + "), 2) == 1");
|
||||||
|
}
|
||||||
|
partVisibility.add(visibilityItem);
|
||||||
|
if (!uvBonesId.contains("*")) {
|
||||||
|
processedBones.add(bone);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!boneName.startsWith("uv_")) {
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
controller.add("part_visibility", partVisibility);
|
||||||
|
//}
|
||||||
|
}
|
||||||
|
|
||||||
|
return root.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
package re.imc.geysermodelengineextension.managers.resourcepack.generator.data;
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
public class BoneData {
|
||||||
|
|
||||||
|
private final String name;
|
||||||
|
private final String parent;
|
||||||
|
private final Set<BoneData> children;
|
||||||
|
private final Set<BoneData> allChildren;
|
||||||
|
|
||||||
|
public BoneData(String name, String parent, Set<BoneData> children, Set<BoneData> allChildren) {
|
||||||
|
this.name = name;
|
||||||
|
this.parent = parent;
|
||||||
|
this.children = children;
|
||||||
|
this.allChildren = allChildren;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getParent() {
|
||||||
|
return parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set<BoneData> getChildren() {
|
||||||
|
return children;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set<BoneData> getAllChildren() {
|
||||||
|
return allChildren;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
package re.imc.geysermodelengineextension.managers.resourcepack.generator.data;
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
public class TextureData {
|
||||||
|
|
||||||
|
private final String modelId;
|
||||||
|
private final String path;
|
||||||
|
private final Set<String> bindingBones;
|
||||||
|
private final byte[] image;
|
||||||
|
|
||||||
|
public TextureData(String modelId, String path, Set<String> bindingBones, byte[] image) {
|
||||||
|
this.modelId = modelId;
|
||||||
|
this.path = path;
|
||||||
|
this.bindingBones = bindingBones;
|
||||||
|
this.image = image;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getModelId() {
|
||||||
|
return modelId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getPath() {
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set<String> getBindingBones() {
|
||||||
|
return bindingBones;
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] getImage() {
|
||||||
|
return image;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,68 @@
|
|||||||
|
package re.imc.geysermodelengineextension.util;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
public class BooleanPacker {
|
||||||
|
|
||||||
|
public static final int MAX_BOOLEANS = 24;
|
||||||
|
|
||||||
|
public static int booleansToInt(List<Boolean> booleans) {
|
||||||
|
int result = 0;
|
||||||
|
int i = 1;
|
||||||
|
|
||||||
|
for (boolean b : booleans) {
|
||||||
|
if (b) result += i;
|
||||||
|
i *= 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int mapBooleansToInt(Map<String, Boolean> booleanMap) {
|
||||||
|
int result = 0;
|
||||||
|
int i = 1;
|
||||||
|
List<String> keys = new ArrayList<>(booleanMap.keySet());
|
||||||
|
Collections.sort(keys);
|
||||||
|
|
||||||
|
for (String key : keys) {
|
||||||
|
if (booleanMap.get(key)) result += i;
|
||||||
|
i *= 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<Integer> booleansToInts(List<Boolean> booleans) {
|
||||||
|
List<Integer> results = new ArrayList<>();
|
||||||
|
int result = 0;
|
||||||
|
int i = 1;
|
||||||
|
int i1 = 1;
|
||||||
|
for (boolean b : booleans) {
|
||||||
|
if (b) {
|
||||||
|
result += i;
|
||||||
|
}
|
||||||
|
if (i1 % MAX_BOOLEANS == 0 || i1 == booleans.size()) {
|
||||||
|
results.add(result);
|
||||||
|
result = 0;
|
||||||
|
i = 1;
|
||||||
|
} else {
|
||||||
|
i *= 2;
|
||||||
|
}
|
||||||
|
i1++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<Integer> mapBooleansToInts(Map<String, Boolean> booleanMap) {
|
||||||
|
List<String> keys = new ArrayList<>(booleanMap.keySet());
|
||||||
|
List<Boolean> booleans = new ArrayList<>();
|
||||||
|
Collections.sort(keys);
|
||||||
|
|
||||||
|
for (String key : keys) {
|
||||||
|
booleans.add(booleanMap.get(key));
|
||||||
|
}
|
||||||
|
|
||||||
|
return booleansToInts(booleans);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,146 @@
|
|||||||
|
package re.imc.geysermodelengineextension.util;
|
||||||
|
|
||||||
|
import org.spongepowered.configurate.CommentedConfigurationNode;
|
||||||
|
import org.spongepowered.configurate.serialize.SerializationException;
|
||||||
|
import org.spongepowered.configurate.yaml.YamlConfigurationLoader;
|
||||||
|
import re.imc.geysermodelengineextension.GeyserModelEngineExtension;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class FileConfiguration {
|
||||||
|
|
||||||
|
private final GeyserModelEngineExtension extension = GeyserModelEngineExtension.getExtension();
|
||||||
|
|
||||||
|
private final Path dataDirectory = extension.dataFolder();
|
||||||
|
|
||||||
|
protected final String configFile;
|
||||||
|
private final CommentedConfigurationNode configurationNode;
|
||||||
|
|
||||||
|
public FileConfiguration(String configFile) {
|
||||||
|
this.configFile = configFile;
|
||||||
|
this.configurationNode = load(configFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
public FileConfiguration(CommentedConfigurationNode configurationNode, String configFile) {
|
||||||
|
this.configFile = configFile;
|
||||||
|
this.configurationNode = configurationNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
private CommentedConfigurationNode load(String fileName) {
|
||||||
|
try {
|
||||||
|
if (!Files.exists(this.dataDirectory)) Files.createDirectories(this.dataDirectory);
|
||||||
|
|
||||||
|
Path config = this.dataDirectory.resolve(fileName);
|
||||||
|
|
||||||
|
FileUtils.createFiles(extension, fileName);
|
||||||
|
|
||||||
|
YamlConfigurationLoader loader = YamlConfigurationLoader.builder().path(config).build();
|
||||||
|
|
||||||
|
return loader.load();
|
||||||
|
} catch (IOException err) {
|
||||||
|
throw new RuntimeException(err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public FileConfiguration getConfigurationSection(String path) {
|
||||||
|
CommentedConfigurationNode sectionNode = getConfigurationSectionNode(path);
|
||||||
|
if (sectionNode == null || sectionNode.virtual()) return null;
|
||||||
|
return new FileConfiguration(sectionNode, this.configFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getString(String path) {
|
||||||
|
CommentedConfigurationNode node = getInternal(path);
|
||||||
|
if (node == null) return null;
|
||||||
|
return node.getString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<String> getStringList(String path) {
|
||||||
|
CommentedConfigurationNode node = getInternal(path);
|
||||||
|
if (node == null || node.virtual()) return List.of();
|
||||||
|
|
||||||
|
try {
|
||||||
|
return node.getList(String.class, List.of());
|
||||||
|
} catch (SerializationException err) {
|
||||||
|
throw new RuntimeException(err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getInt(String path) {
|
||||||
|
CommentedConfigurationNode node = getInternal(path);
|
||||||
|
if (node == null) return 0;
|
||||||
|
return node.getInt();
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Integer> getIntegerList(String path) {
|
||||||
|
CommentedConfigurationNode node = getInternal(path);
|
||||||
|
if (node == null || node.virtual()) return List.of();
|
||||||
|
|
||||||
|
try {
|
||||||
|
return node.getList(Integer.class, List.of());
|
||||||
|
} catch (SerializationException err) {
|
||||||
|
throw new RuntimeException(err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public double getDouble(String path) {
|
||||||
|
CommentedConfigurationNode node = getInternal(path);
|
||||||
|
if (node == null) return 0;
|
||||||
|
return node.getDouble();
|
||||||
|
}
|
||||||
|
|
||||||
|
public double getLong(String path) {
|
||||||
|
CommentedConfigurationNode node = getInternal(path);
|
||||||
|
if (node == null) return 0;
|
||||||
|
return node.getLong();
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Long> getLongList(String path) {
|
||||||
|
CommentedConfigurationNode node = getInternal(path);
|
||||||
|
if (node == null || node.virtual()) return List.of();
|
||||||
|
|
||||||
|
try {
|
||||||
|
return node.getList(Long.class, List.of());
|
||||||
|
} catch (SerializationException err) {
|
||||||
|
throw new RuntimeException(err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean getBoolean(String path) {
|
||||||
|
CommentedConfigurationNode node = getInternal(path);
|
||||||
|
if (node == null) return false;
|
||||||
|
return node.getBoolean();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isBoolean(String path) {
|
||||||
|
CommentedConfigurationNode node = getInternal(path);
|
||||||
|
return node != null && node.raw() instanceof Boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
public File toFile() {
|
||||||
|
return this.dataDirectory.resolve(configFile).toFile();
|
||||||
|
}
|
||||||
|
|
||||||
|
private CommentedConfigurationNode getInternal(String path) {
|
||||||
|
CommentedConfigurationNode node = toSplitRoot(path, this.configurationNode);
|
||||||
|
if (node.virtual()) return null;
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
private CommentedConfigurationNode toSplitRoot(String path, CommentedConfigurationNode node) {
|
||||||
|
if (path == null) return node;
|
||||||
|
path = path.startsWith(".") ? path.substring(1) : path;
|
||||||
|
return node.node(path.contains(".") ? path.split("\\.") : new Object[]{path});
|
||||||
|
}
|
||||||
|
|
||||||
|
private CommentedConfigurationNode getConfigurationSectionNode(String path) {
|
||||||
|
return getInternal(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
public CommentedConfigurationNode getRootNode() {
|
||||||
|
return configurationNode;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
package re.imc.geysermodelengineextension.util;
|
||||||
|
|
||||||
|
import re.imc.geysermodelengineextension.GeyserModelEngineExtension;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class FileUtils {
|
||||||
|
|
||||||
|
public static List<File> getAllFiles(File folder, String fileType) {
|
||||||
|
List<File> files = new ArrayList<>();
|
||||||
|
if (folder == null || !folder.exists()) return files;
|
||||||
|
|
||||||
|
for (File file : folder.listFiles()) {
|
||||||
|
if (file.isDirectory()) {
|
||||||
|
files.addAll(getAllFiles(file, fileType));
|
||||||
|
} else if (file.getName().endsWith(fileType)) {
|
||||||
|
files.add(file);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return files;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void createFiles(GeyserModelEngineExtension extension, String fileName) {
|
||||||
|
Path config = extension.dataFolder().resolve(fileName);
|
||||||
|
if (Files.exists(config)) return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
Path parentDirectory = config.getParent();
|
||||||
|
if (parentDirectory != null && !Files.exists(parentDirectory)) Files.createDirectories(parentDirectory);
|
||||||
|
|
||||||
|
try (InputStream resourceAsStream = extension.getClass().getClassLoader().getResourceAsStream("Extension/" + fileName)) {
|
||||||
|
if (resourceAsStream == null) {
|
||||||
|
extension.logger().warning(fileName + " is invalid!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Files.copy(resourceAsStream, config);
|
||||||
|
} catch (IOException err) {
|
||||||
|
throw new RuntimeException(err);
|
||||||
|
}
|
||||||
|
} catch (IOException err) {
|
||||||
|
throw new RuntimeException(err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
package re.imc.geysermodelengineextension.util;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.zip.ZipEntry;
|
||||||
|
import java.util.zip.ZipOutputStream;
|
||||||
|
|
||||||
|
public class ZipUtil {
|
||||||
|
|
||||||
|
public static void compressFolder(File folder, String folderName, ZipOutputStream zipOutputStream) throws IOException {
|
||||||
|
File[] files = folder.listFiles();
|
||||||
|
|
||||||
|
if (files != null) {
|
||||||
|
for (File file : files) {
|
||||||
|
if (file.isDirectory()) {
|
||||||
|
if (folderName == null) {
|
||||||
|
compressFolder(file, file.getName(), zipOutputStream);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
compressFolder(file, folderName + "/" + file.getName(), zipOutputStream);
|
||||||
|
} else {
|
||||||
|
if (folderName == null) {
|
||||||
|
addToZipFile(file.getName(), file, zipOutputStream);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
addToZipFile(folderName + "/" + file.getName(), file, zipOutputStream);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void addToZipFile(String fileName, File file, ZipOutputStream zipOutputStream) throws IOException {
|
||||||
|
ZipEntry entry = new ZipEntry(fileName);
|
||||||
|
zipOutputStream.putNextEntry(entry);
|
||||||
|
|
||||||
|
try (FileInputStream fileInputStream = new FileInputStream(file)) {
|
||||||
|
byte[] buffer = new byte[1024];
|
||||||
|
int bytesRead;
|
||||||
|
while ((bytesRead = fileInputStream.read(buffer)) != -1) {
|
||||||
|
zipOutputStream.write(buffer, 0, bytesRead);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
zipOutputStream.closeEntry();
|
||||||
|
}
|
||||||
|
}
|
||||||
4
geyser/src/main/resources/Extension/Lang/messages.yml
Normal file
4
geyser/src/main/resources/Extension/Lang/messages.yml
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
commands:
|
||||||
|
geysermodelenginepackgenerator:
|
||||||
|
reload:
|
||||||
|
successfully-reloaded: "GeyserModelEnginePackGenerator reloaded!"
|
||||||
6
geyser/src/main/resources/Extension/config.yml
Normal file
6
geyser/src/main/resources/Extension/config.yml
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
models:
|
||||||
|
namespace: "modelengine"
|
||||||
|
|
||||||
|
options:
|
||||||
|
resource-pack:
|
||||||
|
auto-load: true
|
||||||
8
geyser/src/main/resources/extension.yml
Normal file
8
geyser/src/main/resources/extension.yml
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
name: GeyserModelEngineExtension
|
||||||
|
id: geysermodelengineextension
|
||||||
|
main: re.imc.geysermodelengineextension.GeyserModelEngineExtension
|
||||||
|
api: 2.7.0
|
||||||
|
version: 1.0.0
|
||||||
|
authors:
|
||||||
|
- zimzaza4
|
||||||
|
- xSquishyLiam
|
||||||
2
gradle/wrapper/gradle-wrapper.properties
vendored
2
gradle/wrapper/gradle-wrapper.properties
vendored
@@ -1,6 +1,6 @@
|
|||||||
distributionBase=GRADLE_USER_HOME
|
distributionBase=GRADLE_USER_HOME
|
||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.12-bin.zip
|
distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.0-bin.zip
|
||||||
networkTimeout=10000
|
networkTimeout=10000
|
||||||
validateDistributionUrl=true
|
validateDistributionUrl=true
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
|
|||||||
61
paper/build.gradle.kts
Normal file
61
paper/build.gradle.kts
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
plugins {
|
||||||
|
id("java")
|
||||||
|
id("com.gradleup.shadow") version "9.2.2"
|
||||||
|
}
|
||||||
|
|
||||||
|
group = "re.imc"
|
||||||
|
version = "1.0.0"
|
||||||
|
|
||||||
|
repositories {
|
||||||
|
mavenCentral()
|
||||||
|
maven("https://repo.papermc.io/repository/maven-public/")
|
||||||
|
maven("https://central.sonatype.com/repository/maven-snapshots/")
|
||||||
|
|
||||||
|
maven("https://mvn.lumine.io/repository/maven-public/")
|
||||||
|
|
||||||
|
maven("https://repo.opencollab.dev/main/")
|
||||||
|
|
||||||
|
maven("https://repo.codemc.io/repository/maven-public/")
|
||||||
|
maven("https://repo.codemc.io/repository/maven-releases/")
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
compileOnly("io.papermc.paper:paper-api:1.21.10-R0.1-SNAPSHOT")
|
||||||
|
// implementation("dev.jorel:commandapi-paper-shade:11.0.0")
|
||||||
|
|
||||||
|
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.11.0")
|
||||||
|
implementation("org.bstats:bstats-bukkit:3.0.2")
|
||||||
|
|
||||||
|
implementation("org.reflections:reflections:0.10.2")
|
||||||
|
}
|
||||||
|
|
||||||
|
java {
|
||||||
|
toolchain.languageVersion.set(JavaLanguageVersion.of(21))
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks.compileJava {
|
||||||
|
options.encoding = "UTF-8"
|
||||||
|
}
|
||||||
|
|
||||||
|
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.build {
|
||||||
|
dependsOn("shadowJar")
|
||||||
|
}
|
||||||
Binary file not shown.
@@ -2,9 +2,6 @@ package re.imc.geysermodelengine;
|
|||||||
|
|
||||||
import com.github.retrooper.packetevents.PacketEvents;
|
import com.github.retrooper.packetevents.PacketEvents;
|
||||||
import com.github.retrooper.packetevents.event.PacketListenerPriority;
|
import com.github.retrooper.packetevents.event.PacketListenerPriority;
|
||||||
import dev.jorel.commandapi.CommandAPI;
|
|
||||||
import dev.jorel.commandapi.CommandAPIBukkitConfig;
|
|
||||||
import dev.jorel.commandapi.CommandAPIPaperConfig;
|
|
||||||
import io.github.retrooper.packetevents.factory.spigot.SpigotPacketEventsBuilder;
|
import io.github.retrooper.packetevents.factory.spigot.SpigotPacketEventsBuilder;
|
||||||
import org.bstats.bukkit.Metrics;
|
import org.bstats.bukkit.Metrics;
|
||||||
import org.bukkit.Bukkit;
|
import org.bukkit.Bukkit;
|
||||||
@@ -36,7 +33,7 @@ public class GeyserModelEngine extends JavaPlugin {
|
|||||||
PacketEvents.setAPI(SpigotPacketEventsBuilder.build(this));
|
PacketEvents.setAPI(SpigotPacketEventsBuilder.build(this));
|
||||||
PacketEvents.getAPI().load();
|
PacketEvents.getAPI().load();
|
||||||
|
|
||||||
CommandAPI.onLoad(new CommandAPIPaperConfig(this));
|
// CommandAPI.onLoad(new CommandAPIPaperConfig(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -54,20 +51,19 @@ public class GeyserModelEngine extends JavaPlugin {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onDisable() {
|
public void onDisable() {
|
||||||
PacketEvents.getAPI().terminate();
|
|
||||||
|
|
||||||
this.modelManager.removeEntities();
|
this.modelManager.removeEntities();
|
||||||
|
|
||||||
CommandAPI.onDisable();
|
PacketEvents.getAPI().terminate();
|
||||||
|
// CommandAPI.onDisable();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void loadHooks() {
|
private void loadHooks() {
|
||||||
PacketEvents.getAPI().init();
|
PacketEvents.getAPI().init();
|
||||||
CommandAPI.onEnable();
|
// CommandAPI.onEnable();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void loadBStats() {
|
private void loadBStats() {
|
||||||
if (configManager.getConfig().getBoolean("bstats", true)) new Metrics(this, 26981);
|
if (configManager.getConfig().getBoolean("metrics.bstats", true)) new Metrics(this, 26981);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void loadManagers() {
|
private void loadManagers() {
|
||||||
@@ -80,10 +76,10 @@ public class GeyserModelEngine extends JavaPlugin {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void loadRunnables() {
|
private void loadRunnables() {
|
||||||
this.schedulerPool = Executors.newScheduledThreadPool(configManager.getConfig().getInt("thread-pool-size", 4));
|
this.schedulerPool = Executors.newScheduledThreadPool(configManager.getConfig().getInt("models.thread-pool-size", 4));
|
||||||
|
|
||||||
Bukkit.getAsyncScheduler().runAtFixedRate(this, new UpdateTaskRunnable(this), 10, configManager.getConfig().getLong("entity-position-update-period", 35), TimeUnit.MILLISECONDS);
|
schedulerPool.scheduleAtFixedRate(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);
|
schedulerPool.scheduleAtFixedRate(new BedrockMountControlRunnable(this), 1, 1, TimeUnit.MILLISECONDS);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ConfigManager getConfigManager() {
|
public ConfigManager getConfigManager() {
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
package re.imc.geysermodelengine.commands.geysermodelenginecommands;
|
||||||
|
|
||||||
|
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")));
|
||||||
|
// });
|
||||||
|
// }
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
package re.imc.geysermodelengine.listener;
|
||||||
|
|
||||||
|
import org.bukkit.Bukkit;
|
||||||
|
import org.bukkit.World;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
import org.bukkit.event.EventHandler;
|
||||||
|
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.geysermc.floodgate.api.FloodgateApi;
|
||||||
|
import re.imc.geysermodelengine.GeyserModelEngine;
|
||||||
|
import re.imc.geysermodelengine.util.BedrockUtils;
|
||||||
|
|
||||||
|
public class ModelListener implements Listener {
|
||||||
|
|
||||||
|
private final GeyserModelEngine plugin;
|
||||||
|
|
||||||
|
public ModelListener(GeyserModelEngine plugin) {
|
||||||
|
this.plugin = plugin;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
/ xSquishyLiam:
|
||||||
|
/ May change this into a better system?
|
||||||
|
*/
|
||||||
|
@EventHandler
|
||||||
|
public void onWorldInit(WorldInitEvent event) {
|
||||||
|
World world = event.getWorld();
|
||||||
|
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();
|
||||||
|
if (!BedrockUtils.isBedrockPlayer(player)) return;
|
||||||
|
Bukkit.getGlobalRegionScheduler().runDelayed(plugin, scheduledTask -> plugin.getModelManager().getPlayerJoinedCache().add(player.getUniqueId()), 10);
|
||||||
|
}
|
||||||
|
|
||||||
|
@EventHandler
|
||||||
|
public void onPlayerQuit(PlayerQuitEvent event) {
|
||||||
|
Player player = event.getPlayer();
|
||||||
|
if (!BedrockUtils.isBedrockPlayer(player)) return;
|
||||||
|
plugin.getModelManager().getPlayerJoinedCache().remove(player.getUniqueId());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -11,6 +11,7 @@ import org.apache.commons.lang3.tuple.Pair;
|
|||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
import org.geysermc.floodgate.api.FloodgateApi;
|
import org.geysermc.floodgate.api.FloodgateApi;
|
||||||
import re.imc.geysermodelengine.GeyserModelEngine;
|
import re.imc.geysermodelengine.GeyserModelEngine;
|
||||||
|
import re.imc.geysermodelengine.util.BedrockUtils;
|
||||||
|
|
||||||
public class MountPacketListener implements PacketListener {
|
public class MountPacketListener implements PacketListener {
|
||||||
|
|
||||||
@@ -23,9 +24,9 @@ public class MountPacketListener implements PacketListener {
|
|||||||
@Override
|
@Override
|
||||||
public void onPacketReceive(PacketReceiveEvent event) {
|
public void onPacketReceive(PacketReceiveEvent event) {
|
||||||
if (event.getPacketType() != PacketType.Play.Client.ENTITY_ACTION) return;
|
if (event.getPacketType() != PacketType.Play.Client.ENTITY_ACTION) return;
|
||||||
if (!FloodgateApi.getInstance().isFloodgatePlayer(event.getUser().getUUID())) return;
|
|
||||||
|
|
||||||
Player player = event.getPlayer();
|
Player player = event.getPlayer();
|
||||||
|
if (!BedrockUtils.isBedrockPlayer(player)) return;
|
||||||
|
|
||||||
WrapperPlayClientEntityAction action = new WrapperPlayClientEntityAction(event);
|
WrapperPlayClientEntityAction action = new WrapperPlayClientEntityAction(event);
|
||||||
Pair<ActiveModel, Mount> seat = plugin.getModelManager().getDriversCache().get(player.getUniqueId());
|
Pair<ActiveModel, Mount> seat = plugin.getModelManager().getDriversCache().get(player.getUniqueId());
|
||||||
@@ -21,8 +21,8 @@ public class CommandManager {
|
|||||||
for (Class<?> clazz : new Reflections(path).getSubTypesOf(CommandManagers.class)) {
|
for (Class<?> clazz : new Reflections(path).getSubTypesOf(CommandManagers.class)) {
|
||||||
try {
|
try {
|
||||||
CommandManagers commandManager = (CommandManagers) clazz.getDeclaredConstructor(GeyserModelEngine.class).newInstance(plugin);
|
CommandManagers commandManager = (CommandManagers) clazz.getDeclaredConstructor(GeyserModelEngine.class).newInstance(plugin);
|
||||||
plugin.getLogger().warning("Loading Command Manager - " + commandManager.name());
|
plugin.getLogger().info("Loading Command Manager - " + commandManager.getName());
|
||||||
commandManagersCache.put(commandManager.name(), commandManager);
|
commandManagersCache.put(commandManager.getName(), commandManager);
|
||||||
} catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException err) {
|
} catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException err) {
|
||||||
plugin.getLogger().severe("Failed to load Command Manager " + clazz.getName());
|
plugin.getLogger().severe("Failed to load Command Manager " + clazz.getName());
|
||||||
throw new RuntimeException(err);
|
throw new RuntimeException(err);
|
||||||
@@ -6,7 +6,13 @@ import java.util.ArrayList;
|
|||||||
|
|
||||||
public interface CommandManagers {
|
public interface CommandManagers {
|
||||||
|
|
||||||
String name();
|
/**
|
||||||
|
* Gets the name of the command manager
|
||||||
|
*/
|
||||||
|
String getName();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the command manager subcommands
|
||||||
|
*/
|
||||||
ArrayList<SubCommands> getCommands();
|
ArrayList<SubCommands> getCommands();
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,5 @@
|
|||||||
package re.imc.geysermodelengine.managers.commands.managers.geysermodelengine;
|
package re.imc.geysermodelengine.managers.commands.managers.geysermodelengine;
|
||||||
|
|
||||||
import dev.jorel.commandapi.CommandAPICommand;
|
|
||||||
import re.imc.geysermodelengine.GeyserModelEngine;
|
import re.imc.geysermodelengine.GeyserModelEngine;
|
||||||
import re.imc.geysermodelengine.commands.geysermodelenginecommands.GeyserModelEngineReloadCommand;
|
import re.imc.geysermodelengine.commands.geysermodelenginecommands.GeyserModelEngineReloadCommand;
|
||||||
import re.imc.geysermodelengine.managers.commands.CommandManagers;
|
import re.imc.geysermodelengine.managers.commands.CommandManagers;
|
||||||
@@ -19,15 +18,15 @@ public class GeyserModelEngineCommandManager implements CommandManagers {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void registerCommand() {
|
private void registerCommand() {
|
||||||
CommandAPICommand geyserModelEngineCommand = new CommandAPICommand(name());
|
// CommandAPICommand geyserModelEngineCommand = new CommandAPICommand(getName());
|
||||||
|
//
|
||||||
commands.forEach(subCommands -> geyserModelEngineCommand.withSubcommand(subCommands.onCommand()));
|
// commands.forEach(subCommands -> geyserModelEngineCommand.withSubcommand(subCommands.onCommand()));
|
||||||
|
//
|
||||||
geyserModelEngineCommand.register();
|
// geyserModelEngineCommand.register();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String name() {
|
public String getName() {
|
||||||
return "geysermodelengine";
|
return "geysermodelengine";
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1,7 +1,9 @@
|
|||||||
package re.imc.geysermodelengine.managers.commands.subcommands;
|
package re.imc.geysermodelengine.managers.commands.subcommands;
|
||||||
|
|
||||||
import dev.jorel.commandapi.CommandAPICommand;
|
|
||||||
|
|
||||||
public interface SubCommands {
|
public interface SubCommands {
|
||||||
CommandAPICommand onCommand();
|
|
||||||
|
/**
|
||||||
|
* Subcommand setup
|
||||||
|
*/
|
||||||
|
// CommandAPICommand onCommand();
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,102 @@
|
|||||||
|
package re.imc.geysermodelengine.managers.model;
|
||||||
|
|
||||||
|
import com.ticxo.modelengine.api.animation.BlueprintAnimation;
|
||||||
|
import com.ticxo.modelengine.api.model.ActiveModel;
|
||||||
|
import me.zimzaza4.geyserutils.spigot.api.EntityUtils;
|
||||||
|
import org.bukkit.Bukkit;
|
||||||
|
import org.bukkit.Location;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
import re.imc.geysermodelengine.GeyserModelEngine;
|
||||||
|
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.util.BedrockUtils;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
public class EntityTaskManager {
|
||||||
|
|
||||||
|
private final GeyserModelEngine plugin;
|
||||||
|
|
||||||
|
private PropertyHandler propertyHandler;
|
||||||
|
|
||||||
|
public EntityTaskManager(GeyserModelEngine plugin) {
|
||||||
|
this.plugin = plugin;
|
||||||
|
|
||||||
|
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 void checkViewers(EntityData model, Set<Player> viewers) {
|
||||||
|
for (Player onlinePlayer : Bukkit.getOnlinePlayers()) {
|
||||||
|
if (!BedrockUtils.isBedrockPlayer(onlinePlayer)) return;
|
||||||
|
|
||||||
|
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(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("models.join-send-delay") / 50);
|
||||||
|
} else {
|
||||||
|
task.sendEntityData(model, onlinePlayer, 5);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean canSee(Player player, PacketEntity entity) {
|
||||||
|
if (!player.isOnline()) return false;
|
||||||
|
if (!plugin.getModelManager().getPlayerJoinedCache().contains(player.getUniqueId())) return false;
|
||||||
|
|
||||||
|
Location playerLocation = player.getLocation().clone();
|
||||||
|
Location entityLocation = entity.getLocation().clone();
|
||||||
|
playerLocation.setY(0);
|
||||||
|
entityLocation.setY(0);
|
||||||
|
|
||||||
|
if (playerLocation.getWorld() != entityLocation.getWorld()) return false;
|
||||||
|
if (playerLocation.distanceSquared(entityLocation) > player.getSendViewDistance() * player.getSendViewDistance() * 48) return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void sendHitBoxToAll(EntityData model) {
|
||||||
|
for (Player viewer : model.getViewers()) {
|
||||||
|
EntityUtils.sendCustomHitBox(viewer, model.getEntity().getEntityId(), 0.01f, 0.01f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//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 PropertyHandler getPropertyHandler() {
|
||||||
|
return propertyHandler;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,74 @@
|
|||||||
|
package re.imc.geysermodelengine.managers.model;
|
||||||
|
|
||||||
|
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.Bukkit;
|
||||||
|
import re.imc.geysermodelengine.GeyserModelEngine;
|
||||||
|
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;
|
||||||
|
|
||||||
|
public class ModelManager {
|
||||||
|
|
||||||
|
private final GeyserModelEngine plugin;
|
||||||
|
|
||||||
|
private ModelHandler modelHandler;
|
||||||
|
|
||||||
|
private final HashSet<UUID> playerJoinedCache = new HashSet<>();
|
||||||
|
|
||||||
|
private final ConcurrentHashMap<Integer, Model> modelEntitiesCache = new ConcurrentHashMap<>();
|
||||||
|
private final ConcurrentHashMap<Integer, Map<Model, EntityData>> entitiesCache = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
|
// MEG ONLY
|
||||||
|
private final ConcurrentHashMap<UUID, Pair<ActiveModel, Mount>> driversCache = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
|
public ModelManager(GeyserModelEngine plugin) {
|
||||||
|
this.plugin = plugin;
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
modelHandler.loadListeners();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void removeEntities() {
|
||||||
|
for (Map<Model, EntityData> entities : entitiesCache.values()) {
|
||||||
|
entities.forEach((model, modelEntity) -> modelEntity.getEntity().remove());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public ModelHandler getModelHandler() {
|
||||||
|
return modelHandler;
|
||||||
|
}
|
||||||
|
|
||||||
|
public HashSet<UUID> getPlayerJoinedCache() {
|
||||||
|
return playerJoinedCache;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ConcurrentHashMap<Integer, Map<Model, EntityData>> getEntitiesCache() {
|
||||||
|
return entitiesCache;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ConcurrentHashMap<Integer, Model> getModelEntitiesCache() {
|
||||||
|
return modelEntitiesCache;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ConcurrentHashMap<UUID, Pair<ActiveModel, Mount>> getDriversCache() {
|
||||||
|
return driversCache;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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.entity.BaseEntity;
|
||||||
|
import kr.toxicity.model.api.tracker.EntityTracker;
|
||||||
|
import kr.toxicity.model.api.tracker.Tracker;
|
||||||
|
import org.bukkit.Location;
|
||||||
|
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 BaseEntity entitySource;
|
||||||
|
private final Tracker tracker;
|
||||||
|
private final EntityTracker entityTracker;
|
||||||
|
|
||||||
|
private BetterModelTaskHandler entityTask;
|
||||||
|
|
||||||
|
private boolean hurt;
|
||||||
|
|
||||||
|
public BetterModelEntityData(GeyserModelEngine plugin, BaseEntity 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.location());
|
||||||
|
|
||||||
|
runEntityTask();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void teleportToModel() {
|
||||||
|
Location location = entitySource.location();
|
||||||
|
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 BaseEntity getEntitySource() {
|
||||||
|
return entitySource;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Tracker getTracker() {
|
||||||
|
return tracker;
|
||||||
|
}
|
||||||
|
|
||||||
|
public EntityTracker getEntityTracker() {
|
||||||
|
return entityTracker;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isHurt() {
|
||||||
|
return hurt;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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();
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
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.github.retrooper.packetevents.protocol.entity.type.EntityTypes;
|
||||||
import com.google.common.collect.Sets;
|
import com.google.common.collect.Sets;
|
||||||
@@ -7,57 +7,58 @@ import com.ticxo.modelengine.api.model.ModeledEntity;
|
|||||||
import org.bukkit.Location;
|
import org.bukkit.Location;
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
import re.imc.geysermodelengine.GeyserModelEngine;
|
import re.imc.geysermodelengine.GeyserModelEngine;
|
||||||
|
import re.imc.geysermodelengine.managers.model.taskshandler.ModelEngineTaskHandler;
|
||||||
import re.imc.geysermodelengine.packet.entity.PacketEntity;
|
import re.imc.geysermodelengine.packet.entity.PacketEntity;
|
||||||
import re.imc.geysermodelengine.runnables.EntityTaskRunnable;
|
|
||||||
|
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
public class ModelEntityData {
|
public class ModelEngineEntityData implements EntityData {
|
||||||
|
|
||||||
private final GeyserModelEngine plugin;
|
private final GeyserModelEngine plugin;
|
||||||
|
|
||||||
private PacketEntity entity;
|
private final PacketEntity entity;
|
||||||
|
|
||||||
private final Set<Player> viewers = Sets.newConcurrentHashSet();
|
private final Set<Player> viewers = Sets.newConcurrentHashSet();
|
||||||
|
|
||||||
private final ModeledEntity modeledEntity;
|
private final ModeledEntity modeledEntity;
|
||||||
|
|
||||||
private final ActiveModel activeModel;
|
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.plugin = plugin;
|
||||||
|
|
||||||
this.modeledEntity = modeledEntity;
|
this.modeledEntity = modeledEntity;
|
||||||
this.activeModel = model;
|
this.activeModel = activeModel;
|
||||||
this.entity = spawnEntity();
|
this.entity = new PacketEntity(EntityTypes.PIG, viewers, modeledEntity.getBase().getLocation());
|
||||||
|
|
||||||
runEntityTask();
|
runEntityTask();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void teleportToModel() {
|
public void teleportToModel() {
|
||||||
Location location = modeledEntity.getBase().getLocation();
|
Location location = modeledEntity.getBase().getLocation();
|
||||||
entity.teleport(location);
|
entity.teleport(location);
|
||||||
}
|
}
|
||||||
|
|
||||||
public PacketEntity spawnEntity() {
|
|
||||||
entity = new PacketEntity(EntityTypes.PIG, viewers, modeledEntity.getBase().getLocation());
|
|
||||||
return entity;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void runEntityTask() {
|
public void runEntityTask() {
|
||||||
entityTask = new EntityTaskRunnable(plugin, this);
|
entityTask = new ModelEngineTaskHandler(plugin, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public PacketEntity getEntity() {
|
public PacketEntity getEntity() {
|
||||||
return entity;
|
return entity;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public Set<Player> getViewers() {
|
public Set<Player> getViewers() {
|
||||||
return viewers;
|
return viewers;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ModelEngineTaskHandler getEntityTask() {
|
||||||
|
return entityTask;
|
||||||
|
}
|
||||||
|
|
||||||
public ModeledEntity getModeledEntity() {
|
public ModeledEntity getModeledEntity() {
|
||||||
return modeledEntity;
|
return modeledEntity;
|
||||||
}
|
}
|
||||||
@@ -65,8 +66,4 @@ public class ModelEntityData {
|
|||||||
public ActiveModel getActiveModel() {
|
public ActiveModel getActiveModel() {
|
||||||
return activeModel;
|
return activeModel;
|
||||||
}
|
}
|
||||||
|
|
||||||
public EntityTaskRunnable getEntityTask() {
|
|
||||||
return entityTask;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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();
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,66 @@
|
|||||||
|
package re.imc.geysermodelengine.managers.model.modelhandler;
|
||||||
|
|
||||||
|
import kr.toxicity.model.api.entity.BaseEntity;
|
||||||
|
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) {
|
||||||
|
BaseEntity entitySource = (BaseEntity) objects[0];
|
||||||
|
Tracker tracker = (Tracker) objects[1];
|
||||||
|
EntityTracker entityTracker = (EntityTracker) objects[2];
|
||||||
|
|
||||||
|
int entityID = entitySource.id();
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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();
|
||||||
|
}
|
||||||
@@ -0,0 +1,167 @@
|
|||||||
|
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);
|
||||||
|
|
||||||
|
players.forEach(player -> 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,179 @@
|
|||||||
|
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.*;
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
players.forEach(player -> EntityUtils.sendCustomScale(player, modelEngineEntityData.getEntity().getEntityId(), average));
|
||||||
|
} catch (Exception err) {
|
||||||
|
throw new RuntimeException(err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void sendColor(EntityData entityData, Collection<Player> players, Color lastColor, boolean firstSend) {
|
||||||
|
if (players.isEmpty()) return;
|
||||||
|
|
||||||
|
ModelEngineEntityData data = (ModelEngineEntityData) entityData;
|
||||||
|
Color color = calculateCurrentColor(data);
|
||||||
|
|
||||||
|
if (!firstSend && color.equals(lastColor)) return;
|
||||||
|
|
||||||
|
players.forEach(player -> EntityUtils.sendCustomColor(player, data.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 LinkedHashMap<>();
|
||||||
|
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());
|
||||||
|
|
||||||
|
players.forEach(player -> EntityUtils.sendIntProperties(player, entity, intUpdates));
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
bone.getChildren().values().forEach(child -> processBone(model, child, map));
|
||||||
|
|
||||||
|
ModelBone activeBone = model.getActiveModel().getBones().get(bone.getName());
|
||||||
|
|
||||||
|
boolean visible = false;
|
||||||
|
if (activeBone != null) visible = activeBone.isVisible();
|
||||||
|
|
||||||
|
map.put(name, visible);
|
||||||
|
}
|
||||||
|
|
||||||
|
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 Color calculateCurrentColor(ModelEngineEntityData modelEngineEntityData) {
|
||||||
|
if (modelEngineEntityData.getActiveModel().isMarkedHurt()) return new Color(modelEngineEntityData.getActiveModel().getDamageTint().asARGB());
|
||||||
|
return new Color(modelEngineEntityData.getActiveModel().getDefaultTint().asARGB());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
|
}
|
||||||
@@ -0,0 +1,171 @@
|
|||||||
|
package re.imc.geysermodelengine.managers.model.taskshandler;
|
||||||
|
|
||||||
|
import com.google.common.cache.Cache;
|
||||||
|
import com.google.common.cache.CacheBuilder;
|
||||||
|
import kr.toxicity.model.api.entity.BaseEntity;
|
||||||
|
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();
|
||||||
|
BaseEntity entitySource = entityData.getEntitySource();
|
||||||
|
Tracker tracker = entityData.getTracker();
|
||||||
|
|
||||||
|
entityData.teleportToModel();
|
||||||
|
|
||||||
|
if (entitySource.dead() || tracker.forRemoval()) {
|
||||||
|
removed = true;
|
||||||
|
entity.remove();
|
||||||
|
|
||||||
|
plugin.getModelManager().getEntitiesCache().remove(entitySource.id());
|
||||||
|
plugin.getModelManager().getModelEntitiesCache().remove(entitySource.id());
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,168 @@
|
|||||||
|
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() {
|
||||||
|
if (removed || entityData == null) return;
|
||||||
|
|
||||||
|
PacketEntity entity = entityData.getEntity();
|
||||||
|
if (entity == null || entity.isDead()) return;
|
||||||
|
|
||||||
|
plugin.getEntityTaskManager().checkViewers(entityData, entityData.getViewers());
|
||||||
|
|
||||||
|
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) {
|
||||||
|
viewers.removeIf(viewer -> !plugin.getEntityTaskManager().canSee(viewer, entityData.getEntity()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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();
|
||||||
|
}
|
||||||
@@ -3,9 +3,7 @@ package re.imc.geysermodelengine.packet.entity;
|
|||||||
import com.github.retrooper.packetevents.PacketEvents;
|
import com.github.retrooper.packetevents.PacketEvents;
|
||||||
import com.github.retrooper.packetevents.manager.server.ServerVersion;
|
import com.github.retrooper.packetevents.manager.server.ServerVersion;
|
||||||
import com.github.retrooper.packetevents.protocol.entity.EntityPositionData;
|
import com.github.retrooper.packetevents.protocol.entity.EntityPositionData;
|
||||||
import com.github.retrooper.packetevents.protocol.entity.data.EntityMetadataProvider;
|
|
||||||
import com.github.retrooper.packetevents.protocol.entity.type.EntityType;
|
import com.github.retrooper.packetevents.protocol.entity.type.EntityType;
|
||||||
import com.github.retrooper.packetevents.protocol.entity.type.EntityTypes;
|
|
||||||
import com.github.retrooper.packetevents.protocol.teleport.RelativeFlag;
|
import com.github.retrooper.packetevents.protocol.teleport.RelativeFlag;
|
||||||
import com.github.retrooper.packetevents.util.Vector3d;
|
import com.github.retrooper.packetevents.util.Vector3d;
|
||||||
import com.github.retrooper.packetevents.wrapper.PacketWrapper;
|
import com.github.retrooper.packetevents.wrapper.PacketWrapper;
|
||||||
@@ -16,7 +14,6 @@ import lombok.Setter;
|
|||||||
import org.bukkit.Location;
|
import org.bukkit.Location;
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import re.imc.geysermodelengine.GeyserModelEngine;
|
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
@@ -50,7 +47,7 @@ public class PacketEntity {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public boolean teleport(@NotNull Location location) {
|
public boolean teleport(@NotNull Location location) {
|
||||||
boolean sent = this.location.getWorld() != location.getWorld() || this.location.distanceSquared(location) > 0.000001;
|
boolean sent = this.location.getWorld() != location.getWorld() || this.location.distanceSquared(location) > 0.000001 || this.location.getYaw() != location.getYaw() || this.location.getPitch() != location.getPitch();
|
||||||
this.location = location.clone();
|
this.location = location.clone();
|
||||||
|
|
||||||
if (sent) sendLocationPacket(viewers);
|
if (sent) sendLocationPacket(viewers);
|
||||||
@@ -58,7 +55,6 @@ public class PacketEntity {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public void remove() {
|
public void remove() {
|
||||||
removed = true;
|
removed = true;
|
||||||
sendEntityDestroyPacket(viewers);
|
sendEntityDestroyPacket(viewers);
|
||||||
@@ -78,7 +74,6 @@ public class PacketEntity {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void sendLocationPacket(Collection<Player> players) {
|
public void sendLocationPacket(Collection<Player> players) {
|
||||||
|
|
||||||
PacketWrapper<?> packet;
|
PacketWrapper<?> packet;
|
||||||
EntityPositionData data = new EntityPositionData(SpigotConversionUtil.fromBukkitLocation(location).getPosition(), Vector3d.zero(), location.getYaw(), location.getPitch());
|
EntityPositionData data = new EntityPositionData(SpigotConversionUtil.fromBukkitLocation(location).getPosition(), Vector3d.zero(), location.getYaw(), location.getPitch());
|
||||||
|
|
||||||
@@ -5,16 +5,14 @@ import com.ticxo.modelengine.api.entity.BukkitEntity;
|
|||||||
import com.ticxo.modelengine.api.model.ActiveModel;
|
import com.ticxo.modelengine.api.model.ActiveModel;
|
||||||
import com.ticxo.modelengine.api.model.bone.type.Mount;
|
import com.ticxo.modelengine.api.model.bone.type.Mount;
|
||||||
import com.ticxo.modelengine.api.mount.controller.MountController;
|
import com.ticxo.modelengine.api.mount.controller.MountController;
|
||||||
import io.papermc.paper.threadedregions.scheduler.ScheduledTask;
|
|
||||||
import org.apache.commons.lang3.tuple.Pair;
|
import org.apache.commons.lang3.tuple.Pair;
|
||||||
import org.bukkit.Bukkit;
|
import org.bukkit.Bukkit;
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
import org.geysermc.floodgate.api.FloodgateApi;
|
|
||||||
import re.imc.geysermodelengine.GeyserModelEngine;
|
import re.imc.geysermodelengine.GeyserModelEngine;
|
||||||
|
|
||||||
import java.util.function.Consumer;
|
import java.util.UUID;
|
||||||
|
|
||||||
public class BedrockMountControlRunnable implements Consumer<ScheduledTask> {
|
public class BedrockMountControlRunnable implements Runnable {
|
||||||
|
|
||||||
private final GeyserModelEngine plugin;
|
private final GeyserModelEngine plugin;
|
||||||
|
|
||||||
@@ -23,13 +21,12 @@ public class BedrockMountControlRunnable implements Consumer<ScheduledTask> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void accept(ScheduledTask scheduledTask) {
|
public void run() {
|
||||||
for (Player player : Bukkit.getOnlinePlayers()) {
|
for (UUID playerUUID : plugin.getModelManager().getPlayerJoinedCache()) {
|
||||||
if (!FloodgateApi.getInstance().isFloodgatePlayer(player.getUniqueId())) continue;
|
Player player = Bukkit.getPlayer(playerUUID);
|
||||||
|
|
||||||
float pitch = player.getLocation().getPitch();
|
float pitch = player.getLocation().getPitch();
|
||||||
Pair<ActiveModel, Mount> seat = plugin.getModelManager().getDriversCache().get(player.getUniqueId());
|
Pair<ActiveModel, Mount> seat = plugin.getModelManager().getDriversCache().get(player.getUniqueId());
|
||||||
|
|
||||||
if (seat == null) continue;
|
if (seat == null) continue;
|
||||||
|
|
||||||
if (pitch < -30) {
|
if (pitch < -30) {
|
||||||
@@ -45,13 +42,10 @@ public class BedrockMountControlRunnable implements Consumer<ScheduledTask> {
|
|||||||
|
|
||||||
if (pitch > 80) {
|
if (pitch > 80) {
|
||||||
if (seat.getKey().getModeledEntity().getBase() instanceof BukkitEntity bukkitEntity) {
|
if (seat.getKey().getModeledEntity().getBase() instanceof BukkitEntity bukkitEntity) {
|
||||||
if (bukkitEntity.getOriginal().isOnGround()) {
|
if (bukkitEntity.getOriginal().isOnGround()) continue;
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
MountController controller = ModelEngineAPI.getMountPairManager().getController(player.getUniqueId());
|
MountController controller = ModelEngineAPI.getMountPairManager().getController(player.getUniqueId());
|
||||||
|
|
||||||
if (controller != null) {
|
if (controller != null) {
|
||||||
MountController.MountInput input = controller.getInput();
|
MountController.MountInput input = controller.getInput();
|
||||||
if (input != null) {
|
if (input != null) {
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
package re.imc.geysermodelengine.runnables;
|
||||||
|
|
||||||
|
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;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
|
public class UpdateTaskRunnable implements Runnable {
|
||||||
|
|
||||||
|
private final GeyserModelEngine plugin;
|
||||||
|
|
||||||
|
public UpdateTaskRunnable(GeyserModelEngine plugin) {
|
||||||
|
this.plugin = plugin;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
ConcurrentHashMap<Integer, Map<Model, EntityData>> entitiesCache = plugin.getModelManager().getEntitiesCache();
|
||||||
|
if (entitiesCache.isEmpty()) return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
for (Map<Model, EntityData> models : entitiesCache.values()) {
|
||||||
|
models.values().forEach(entityData -> {
|
||||||
|
if (entityData.getViewers().isEmpty()) return;
|
||||||
|
plugin.getEntityTaskManager().getPropertyHandler().updateEntityProperties(entityData, entityData.getViewers(), false);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch (Throwable err) {
|
||||||
|
throw new RuntimeException(err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
package re.imc.geysermodelengine.util;
|
||||||
|
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
import org.geysermc.floodgate.api.FloodgateApi;
|
||||||
|
|
||||||
|
public class BedrockUtils {
|
||||||
|
|
||||||
|
private static FloodgateApi FLOODGATE_API;
|
||||||
|
|
||||||
|
public static boolean isBedrockPlayer(Player player) {
|
||||||
|
if (FLOODGATE_API != null) return FLOODGATE_API.isFloodgatePlayer(player.getUniqueId());
|
||||||
|
return player.getClientBrandName().contains("Geyser");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static FloodgateApi getFloodgateApi() {
|
||||||
|
return FLOODGATE_API;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -7,9 +7,9 @@ import java.util.Map;
|
|||||||
|
|
||||||
public class BooleanPacker {
|
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 result = 0;
|
||||||
int i = 1;
|
int i = 1;
|
||||||
|
|
||||||
@@ -23,7 +23,7 @@ public class BooleanPacker {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int mapBooleansToInt(Map<String, Boolean> booleanMap) {
|
public static int mapBooleansToInt(Map<String, Boolean> booleanMap) {
|
||||||
int result = 0;
|
int result = 0;
|
||||||
int i = 1;
|
int i = 1;
|
||||||
|
|
||||||
@@ -39,7 +39,7 @@ public class BooleanPacker {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<Integer> booleansToInts(List<Boolean> booleans) {
|
public static List<Integer> booleansToInts(List<Boolean> booleans) {
|
||||||
List<Integer> results = new ArrayList<>();
|
List<Integer> results = new ArrayList<>();
|
||||||
int result = 0;
|
int result = 0;
|
||||||
int i = 1;
|
int i = 1;
|
||||||
@@ -62,7 +62,7 @@ public class BooleanPacker {
|
|||||||
return results;
|
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<String> keys = new ArrayList<>(booleanMap.keySet());
|
||||||
List<Boolean> booleans = new ArrayList<>();
|
List<Boolean> booleans = new ArrayList<>();
|
||||||
|
|
||||||
13
paper/src/main/resources/config.yml
Normal file
13
paper/src/main/resources/config.yml
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
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
|
||||||
@@ -6,6 +6,7 @@ api-version: '1.21'
|
|||||||
authors:
|
authors:
|
||||||
- zimzaza4
|
- zimzaza4
|
||||||
- willem.dev
|
- willem.dev
|
||||||
|
- xSquishyLiam
|
||||||
|
|
||||||
load: STARTUP
|
load: STARTUP
|
||||||
|
|
||||||
@@ -15,7 +16,9 @@ dependencies:
|
|||||||
required: true
|
required: true
|
||||||
packetevents:
|
packetevents:
|
||||||
required: true
|
required: true
|
||||||
ModelEngine:
|
|
||||||
required: true
|
|
||||||
floodgate:
|
floodgate:
|
||||||
required: true
|
required: false
|
||||||
|
ModelEngine:
|
||||||
|
required: false
|
||||||
|
BetterModel:
|
||||||
|
required: false
|
||||||
@@ -1,2 +1,5 @@
|
|||||||
rootProject.name = "GeyserModelEngine"
|
rootProject.name = "GeyserModelEngine"
|
||||||
|
|
||||||
|
include("paper")
|
||||||
|
include("geyser")
|
||||||
|
|
||||||
|
|||||||
@@ -1,28 +0,0 @@
|
|||||||
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")));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,77 +0,0 @@
|
|||||||
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.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.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 {
|
|
||||||
|
|
||||||
private final GeyserModelEngine plugin;
|
|
||||||
|
|
||||||
public ModelListener(GeyserModelEngine plugin) {
|
|
||||||
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.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());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@EventHandler
|
|
||||||
public void onWorldInit(WorldInitEvent event) {
|
|
||||||
World world = event.getWorld();
|
|
||||||
world.getEntities().forEach(entity -> plugin.getModelManager().processEntities(entity));
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
/ xSquishyLiam:
|
|
||||||
/ Delay is required due to when a player joins the server the packet for mob spawning is instant so the client resyncs itself
|
|
||||||
*/
|
|
||||||
@EventHandler
|
|
||||||
public void onPlayerJoin(PlayerJoinEvent event) {
|
|
||||||
Player player = event.getPlayer();
|
|
||||||
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());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,152 +0,0 @@
|
|||||||
package re.imc.geysermodelengine.managers.model;
|
|
||||||
|
|
||||||
import com.ticxo.modelengine.api.animation.BlueprintAnimation;
|
|
||||||
import com.ticxo.modelengine.api.generator.blueprint.BlueprintBone;
|
|
||||||
import com.ticxo.modelengine.api.model.ActiveModel;
|
|
||||||
import com.ticxo.modelengine.api.model.render.DisplayRenderer;
|
|
||||||
import me.zimzaza4.geyserutils.spigot.api.EntityUtils;
|
|
||||||
import org.bukkit.Bukkit;
|
|
||||||
import org.bukkit.Location;
|
|
||||||
import org.bukkit.entity.Player;
|
|
||||||
import org.geysermc.floodgate.api.FloodgateApi;
|
|
||||||
import org.joml.Vector3fc;
|
|
||||||
import re.imc.geysermodelengine.GeyserModelEngine;
|
|
||||||
import re.imc.geysermodelengine.managers.model.data.ModelEntityData;
|
|
||||||
import re.imc.geysermodelengine.packet.entity.PacketEntity;
|
|
||||||
import re.imc.geysermodelengine.runnables.EntityTaskRunnable;
|
|
||||||
|
|
||||||
import java.awt.*;
|
|
||||||
import java.lang.reflect.Method;
|
|
||||||
import java.util.*;
|
|
||||||
|
|
||||||
public class EntityTaskManager {
|
|
||||||
|
|
||||||
private final GeyserModelEngine plugin;
|
|
||||||
|
|
||||||
private final Method scaleMethod;
|
|
||||||
|
|
||||||
public EntityTaskManager(GeyserModelEngine plugin) {
|
|
||||||
this.plugin = plugin;
|
|
||||||
|
|
||||||
try {
|
|
||||||
this.scaleMethod = ActiveModel.class.getMethod("getScale");
|
|
||||||
} catch (NoSuchMethodException err) {
|
|
||||||
throw new RuntimeException(err);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public String unstripName(BlueprintBone bone) {
|
|
||||||
String name = bone.getName();
|
|
||||||
if (bone.getBehaviors().get("head") != null) {
|
|
||||||
if (!bone.getBehaviors().get("head").isEmpty()) return "hi_" + name;
|
|
||||||
return "h_" + name;
|
|
||||||
}
|
|
||||||
|
|
||||||
return name;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void sendScale(ModelEntityData model, Collection<Player> players, float lastScale, boolean firstSend) {
|
|
||||||
try {
|
|
||||||
if (players.isEmpty()) return;
|
|
||||||
|
|
||||||
Vector3fc scale = (Vector3fc) scaleMethod.invoke(model.getActiveModel());
|
|
||||||
|
|
||||||
float average = (scale.x() + scale.y() + scale.z()) / 3;
|
|
||||||
|
|
||||||
if (!firstSend) {
|
|
||||||
if (average == lastScale) return;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (Player player : players) {
|
|
||||||
EntityUtils.sendCustomScale(player, model.getEntity().getEntityId(), average);
|
|
||||||
}
|
|
||||||
} catch (Throwable ignored) {}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void sendColor(ModelEntityData model, Collection<Player> players, Color lastColor, boolean firstSend) {
|
|
||||||
if (players.isEmpty()) return;
|
|
||||||
|
|
||||||
Color color = new Color(model.getActiveModel().getDefaultTint().asARGB());
|
|
||||||
if (model.getActiveModel().isMarkedHurt()) color = new Color(model.getActiveModel().getDamageTint().asARGB());
|
|
||||||
|
|
||||||
if (firstSend) {
|
|
||||||
if (color.equals(lastColor)) return;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (Player player : players) {
|
|
||||||
EntityUtils.sendCustomColor(player, model.getEntity().getEntityId(), color);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void checkViewers(ModelEntityData model, Set<Player> viewers) {
|
|
||||||
for (Player onlinePlayer : Bukkit.getOnlinePlayers()) {
|
|
||||||
if (!FloodgateApi.getInstance().isFloodgatePlayer(onlinePlayer.getUniqueId())) 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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void sendSpawnPacket(ModelEntityData model, Player onlinePlayer) {
|
|
||||||
EntityTaskRunnable task = model.getEntityTask();
|
|
||||||
boolean firstJoined = !plugin.getModelManager().getPlayerJoinedCache().contains(onlinePlayer.getUniqueId());
|
|
||||||
|
|
||||||
if (firstJoined) {
|
|
||||||
task.sendEntityData(model, onlinePlayer, plugin.getConfigManager().getConfig().getInt("join-send-delay") / 50);
|
|
||||||
} else {
|
|
||||||
task.sendEntityData(model, onlinePlayer, 5);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean canSee(Player player, PacketEntity entity) {
|
|
||||||
if (!player.isOnline()) return false;
|
|
||||||
if (!plugin.getModelManager().getPlayerJoinedCache().contains(player.getUniqueId())) return false;
|
|
||||||
|
|
||||||
Location playerLocation = player.getLocation().clone();
|
|
||||||
Location entityLocation = entity.getLocation().clone();
|
|
||||||
playerLocation.setY(0);
|
|
||||||
entityLocation.setY(0);
|
|
||||||
|
|
||||||
if (playerLocation.getWorld() != entityLocation.getWorld()) return false;
|
|
||||||
if (playerLocation.distanceSquared(entityLocation) > player.getSendViewDistance() * player.getSendViewDistance() * 48) return false;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void sendHitBoxToAll(ModelEntityData model) {
|
|
||||||
for (Player viewer : model.getViewers()) {
|
|
||||||
EntityUtils.sendCustomHitBox(viewer, model.getEntity().getEntityId(), 0.01f, 0.01f);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void sendHitBox(ModelEntityData model, Player viewer) {
|
|
||||||
float w = 0;
|
|
||||||
|
|
||||||
if (model.getActiveModel().isShadowVisible()) {
|
|
||||||
if (model.getActiveModel().getModelRenderer() instanceof DisplayRenderer displayRenderer) {
|
|
||||||
// w = displayRenderer.getHitbox().getShadowRadius().get();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
EntityUtils.sendCustomHitBox(viewer, model.getEntity().getEntityId(), 0.02f, w);
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean hasAnimation(ModelEntityData model, String animation) {
|
|
||||||
ActiveModel activeModel = model.getActiveModel();
|
|
||||||
BlueprintAnimation animationProperty = activeModel.getBlueprint().getAnimations().get(animation);
|
|
||||||
return !(animationProperty == null);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Method getScaleMethod() {
|
|
||||||
return scaleMethod;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,76 +0,0 @@
|
|||||||
package re.imc.geysermodelengine.managers.model;
|
|
||||||
|
|
||||||
import com.ticxo.modelengine.api.ModelEngineAPI;
|
|
||||||
import com.ticxo.modelengine.api.model.ActiveModel;
|
|
||||||
import com.ticxo.modelengine.api.model.ModeledEntity;
|
|
||||||
import com.ticxo.modelengine.api.model.bone.type.Mount;
|
|
||||||
import org.apache.commons.lang3.tuple.Pair;
|
|
||||||
import org.bukkit.entity.Entity;
|
|
||||||
import re.imc.geysermodelengine.GeyserModelEngine;
|
|
||||||
import re.imc.geysermodelengine.managers.model.data.ModelEntityData;
|
|
||||||
|
|
||||||
import java.util.*;
|
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
|
||||||
|
|
||||||
public class ModelManager {
|
|
||||||
|
|
||||||
private final GeyserModelEngine plugin;
|
|
||||||
|
|
||||||
private final HashSet<UUID> playerJoinedCache = new HashSet<>();
|
|
||||||
|
|
||||||
private final ConcurrentHashMap<Integer, Map<ActiveModel, ModelEntityData>> entitiesCache = new ConcurrentHashMap<>();
|
|
||||||
private final ConcurrentHashMap<Integer, ModelEntityData> modelEntitiesCache = new ConcurrentHashMap<>();
|
|
||||||
|
|
||||||
private final ConcurrentHashMap<UUID, Pair<ActiveModel, Mount>> driversCache = new ConcurrentHashMap<>();
|
|
||||||
|
|
||||||
public ModelManager(GeyserModelEngine plugin) {
|
|
||||||
this.plugin = plugin;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void create(ModeledEntity entity, ActiveModel model) {
|
|
||||||
ModelEntityData modelEntity = new ModelEntityData(plugin, entity, model);
|
|
||||||
int id = entity.getBase().getEntityId();
|
|
||||||
|
|
||||||
Map<ActiveModel, ModelEntityData> map = entitiesCache.computeIfAbsent(id, k -> new HashMap<>());
|
|
||||||
|
|
||||||
for (Map.Entry<ActiveModel, ModelEntityData> entry : map.entrySet()) {
|
|
||||||
if (entry.getKey() != model && entry.getKey().getBlueprint().getName().equals(model.getBlueprint().getName())) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
map.put(model, modelEntity);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void processEntities(Entity entity) {
|
|
||||||
if (entitiesCache.containsKey(entity.getEntityId())) return;
|
|
||||||
|
|
||||||
ModeledEntity modeledEntity = ModelEngineAPI.getModeledEntity(entity);
|
|
||||||
if (modeledEntity == null) return;
|
|
||||||
|
|
||||||
Optional<ActiveModel> model = modeledEntity.getModels().values().stream().findFirst();
|
|
||||||
model.ifPresent(m -> create(modeledEntity, m));
|
|
||||||
}
|
|
||||||
|
|
||||||
public void removeEntities() {
|
|
||||||
for (Map<ActiveModel, ModelEntityData> entities : entitiesCache.values()) {
|
|
||||||
entities.forEach((model, modelEntity) -> modelEntity.getEntity().remove());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public HashSet<UUID> getPlayerJoinedCache() {
|
|
||||||
return playerJoinedCache;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ConcurrentHashMap<Integer, Map<ActiveModel, ModelEntityData>> getEntitiesCache() {
|
|
||||||
return entitiesCache;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ConcurrentHashMap<Integer, ModelEntityData> getModelEntitiesCache() {
|
|
||||||
return modelEntitiesCache;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ConcurrentHashMap<UUID, Pair<ActiveModel, Mount>> getDriversCache() {
|
|
||||||
return driversCache;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,268 +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 me.zimzaza4.geyserutils.spigot.api.EntityUtils;
|
|
||||||
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.ScheduledFuture;
|
|
||||||
import java.util.concurrent.TimeUnit;
|
|
||||||
|
|
||||||
public class EntityTaskRunnable {
|
|
||||||
|
|
||||||
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();
|
|
||||||
|
|
||||||
private final ScheduledFuture scheduledFuture;
|
|
||||||
|
|
||||||
public EntityTaskRunnable(GeyserModelEngine plugin, ModelEntityData model) {
|
|
||||||
this.plugin = plugin;
|
|
||||||
|
|
||||||
this.model = model;
|
|
||||||
|
|
||||||
plugin.getEntityTaskManager().sendHitBoxToAll(model);
|
|
||||||
|
|
||||||
scheduledFuture = plugin.getSchedulerPool().scheduleAtFixedRate(this::runAsync, 0, 20, TimeUnit.MILLISECONDS);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void runAsync() {
|
|
||||||
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());
|
|
||||||
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 cancel() {
|
|
||||||
scheduledFuture.cancel(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void sendEntityData(ModelEntityData model, Player player, int delay) {
|
|
||||||
//TODO with ModelEngine, you can define the namespace inside the config, make an option to change it here as well? if i'm right about this
|
|
||||||
EntityUtils.setCustomEntity(player, model.getEntity().getEntityId(), "modelengine:" + model.getActiveModel().getBlueprint().getName().toLowerCase());
|
|
||||||
|
|
||||||
plugin.getSchedulerPool().schedule(() -> {
|
|
||||||
model.getEntity().sendSpawnPacket(Collections.singletonList(player));
|
|
||||||
|
|
||||||
plugin.getSchedulerPool().schedule(() -> {
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ScheduledFuture getScheduledFuture() {
|
|
||||||
return scheduledFuture;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
package re.imc.geysermodelengine.runnables;
|
|
||||||
|
|
||||||
import com.ticxo.modelengine.api.model.ActiveModel;
|
|
||||||
import io.papermc.paper.threadedregions.scheduler.ScheduledTask;
|
|
||||||
import re.imc.geysermodelengine.GeyserModelEngine;
|
|
||||||
import re.imc.geysermodelengine.managers.model.data.ModelEntityData;
|
|
||||||
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.function.Consumer;
|
|
||||||
|
|
||||||
public class UpdateTaskRunnable implements Consumer<ScheduledTask> {
|
|
||||||
|
|
||||||
private final GeyserModelEngine plugin;
|
|
||||||
|
|
||||||
public UpdateTaskRunnable(GeyserModelEngine plugin) {
|
|
||||||
this.plugin = plugin;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void accept(ScheduledTask scheduledTask) {
|
|
||||||
try {
|
|
||||||
for (Map<ActiveModel, ModelEntityData> models : plugin.getModelManager().getEntitiesCache().values()) {
|
|
||||||
models.values().forEach(model -> model.getEntityTask().updateEntityProperties(model, model.getViewers(), false));
|
|
||||||
}
|
|
||||||
} catch (Throwable err) {
|
|
||||||
throw new RuntimeException(err);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
bstats: true
|
|
||||||
|
|
||||||
data-send-delay: 5
|
|
||||||
entity-view-distance: 50
|
|
||||||
join-send-delay: 20
|
|
||||||
entity-position-update-period: 35
|
|
||||||
thread-pool-size: 4
|
|
||||||
model-entity-type: BAT # must be a living entity
|
|
||||||
enable-part-visibility-models:
|
|
||||||
- example
|
|
||||||
debug: false
|
|
||||||
Reference in New Issue
Block a user