83 Commits
1.0.0 ... main

Author SHA1 Message Date
xSquishyLiam
b7f78c500b removing my debugger 2025-12-15 04:19:16 +00:00
xSquishyLiam
9d64d02f3d just sorting my build to match the main build 2025-12-14 03:10:14 +00:00
xSquishyLiam
a109d21b76 updated PacketEvents andd addeddd zims stuff 2025-12-10 05:10:33 +00:00
xSquishyLiam
be7b067889 updated BetterModel Support 2025-11-25 03:25:17 +00:00
xSquishyLiam
76b857e8a4 whoops forgot to remove the pack part for one of them smh 2025-11-20 14:09:39 +00:00
xSquishyLiam
1b759c78d6 Update extension.yml 2025-11-20 14:09:09 +00:00
xSquishyLiam
223e4885b2 just a rename to GeyserModelEnginePackGenerator 2025-11-20 14:09:02 +00:00
xSquishyLiam
0b06963c1a never touch things when tired ;-; 2025-11-16 20:11:48 +00:00
xSquishyLiam
d0c42c4e06 merged GeyserModelEnginePackGenerator to here 2025-11-15 19:26:01 +00:00
xSquishyLiam
742351ec66 removed a debugger i had 2025-11-09 22:44:10 +00:00
xSquishyLiam
f82dd2b7f8 removed an old import 2025-11-06 01:29:44 +00:00
xSquishyLiam
f93f49980c Merge branch 'bettermodel-support-dev' 2025-11-06 01:26:04 +00:00
xSquishyLiam
0c90fe4809 updated PE to 2.10.1 2025-11-06 00:46:49 +00:00
xSquishyLiam
6892ffb519 removed so unused config options 2025-11-06 00:41:04 +00:00
xSquishyLiam
af1984bef8 Update config.yml 2025-11-05 18:58:25 +00:00
xSquishyLiam
9be6efdc84 reflecting on a cleaner change i didd inside bettermodel support dev 2025-11-05 18:56:21 +00:00
xSquishyLiam
84f50af049 seeing something 2025-11-05 18:52:21 +00:00
xSquishyLiam
f93559c822 just testing 2025-11-04 13:57:39 +00:00
xSquishyLiam
468757762f updated BetterModel API? 2025-11-02 16:46:32 +00:00
xSquishyLiam
e7114faf84 updated CommandAPI 2025-10-30 20:04:46 +00:00
xSquishyLiam
63bf26d7a9 updated BetterModel API 2025-10-30 19:51:00 +00:00
xSquishyLiam
bdc75512d8 pushed my side for PE to lastest 2025-10-22 11:11:42 +01:00
xSquishyLiam
c5a34f1690 testing out renovatebot 2025-10-16 18:33:53 +01:00
xSquishyLiam
74432c2800 updated GeyserUtils 2025-10-16 01:21:28 +01:00
xSquishyLiam
7f8b648685 updated PE to 2.10.0 SNAPSHOT 2025-10-10 15:25:00 +01:00
xSquishyLiam
f5f424ace2 updated to 1.21.9/1.21.10 2025-10-04 16:46:35 +01:00
xSquishyLiam
a4fba7715d Update config.yml 2025-09-26 04:06:21 +01:00
xSquishyLiam
0176d86233 cleaner config.yml (guess i never pushed this) 2025-09-26 04:05:48 +01:00
xSquishyLiam
79f99ffebe added Damage Tint and comments to interfaces 2025-09-10 22:35:44 +01:00
xSquishyLiam
ae3b67a733 pushing beta to github 2025-09-09 23:41:26 +01:00
xSquishyLiam
8e36bf2829 work in progress 2025-09-07 00:26:16 +01:00
xSquishyLiam
804d2aac04 working on it 2025-09-06 23:24:53 +01:00
xSquishyLiam
b6425ffa51 addedd namespace support 2025-09-01 14:56:01 +01:00
zimzaza4
1c47fe83d2 Merge pull request #53 from xSquishyLiam/main
Updated PE to 2.9.5 & added bstats & fixed build.yml
2025-08-31 04:07:22 +08:00
xSquishyLiam
7bbe66928c added an option to disable bStats 2025-08-30 20:56:17 +01:00
xSquishyLiam
a26ea6e148 updated actions/checkout to v4 2025-08-30 12:59:55 +01:00
xSquishyLiam
d1ef89088c learning workflows 2025-08-30 12:55:22 +01:00
xSquishyLiam
c98e807827 bumped shadow 2025-08-30 12:54:08 +01:00
xSquishyLiam
35131c2d58 i wonder 2025-08-30 12:46:37 +01:00
xSquishyLiam
6651ae99f7 addedd a comment for an idea 2025-08-30 00:49:27 +01:00
xSquishyLiam
ca9a54ae71 added `` to true for floodgate 2025-08-30 00:04:19 +01:00
xSquishyLiam
865f5f567c changed readme a bit, removed the old method 2025-08-30 00:03:33 +01:00
xSquishyLiam
b5d9d9b9ca added info about key.pem 2025-08-29 18:03:14 +01:00
xSquishyLiam
31dac482bd made the info more cleaner 2025-08-29 18:01:59 +01:00
xSquishyLiam
b2bfa2977e addedd floodgate send data to set to true info 2025-08-29 17:52:22 +01:00
xSquishyLiam
84012a23b7 bumped PE to 2.9.5 2025-08-26 01:22:14 +01:00
xSquishyLiam
3943381b2c added bstats 2025-08-21 00:12:29 +01:00
TheLividaProject
42d7ea506f Update build.gradle.kts 2025-07-23 21:36:53 +01:00
zimzaza4
e177c8af74 Merge pull request #49 from TheLividaProject/main
Bumped PacketEvents to 2.9.4
2025-07-23 04:33:42 +08:00
TheLividaProject
2c5dba8097 bumped PacketEvents to 2.9.4 2025-07-22 20:45:31 +01:00
TheLividaProject
2a9fdec8b6 Update build.gradle.kts 2025-07-22 20:17:35 +01:00
TheLividaProject
94df76b9a6 Now renames the jar to the project name and it's version 2025-07-22 18:07:31 +01:00
TheLividaProject
03f27514ef added a comment ModelListener issue in regards of pig spawning (such a thing issue) - mainly for those who want to know why it has a delay on the player join, i didn't know this when forked it and took it off but added it back now and should hopefully fix the issue 2025-07-22 17:23:06 +01:00
TheLividaProject
4679fa6f2b changed a comment about the pig issue 2025-07-22 12:23:07 +01:00
TheLividaProject
db99b2fe00 we love big spawning issue, should be fixed till better solution 2025-07-22 11:03:23 +01:00
TheLividaProject
ffd72b0a56 Merge branch 'GeyserExtensionists:main' into main 2025-07-21 22:40:21 +01:00
TheLividaProject
e83caacd2f nvm 90% sure it works i'm just dumb 2025-07-21 22:39:58 +01:00
TheLividaProject
170e67e059 just testing something 2025-07-21 22:32:45 +01:00
zimzaza4
d150d7e324 Merge pull request #48 from TheLividaProject/main
Updates to 1.21.8
2025-07-22 05:11:03 +08:00
TheLividaProject
db0367537e disables default jar hopefully 2025-07-21 22:05:39 +01:00
TheLividaProject
36ea0e5fa0 Merge branch 'GeyserExtensionists:main' into main 2025-07-21 21:59:54 +01:00
TheLividaProject
13c0b45d2a 1.21.8 support 2025-07-21 21:57:06 +01:00
TheLividaProject
8f67ade51c Added some fixes and improved schedulers - still waiting for 1.21.8 support 2025-07-20 16:51:45 +01:00
TheLividaProject
d0fd719753 updated PE to 2.9.1 and disabled the default jar and only use shaded jar 2025-07-11 10:50:05 +01:00
zimzaza4
f4c5167ef5 Rename .github/workflows/maven.yml to build.yml 2025-07-10 22:04:08 +08:00
zimzaza4
dc4e39506d gradle build 2025-07-10 20:35:06 +08:00
zimzaza4
5fdf93db1e Merge branch 'main' of https://github.com/TheLividaProject/mc-GeyserModelEngine-plugin
# Conflicts:
#	pom.xml
2025-07-10 20:29:36 +08:00
TheLividaProject
16e0dfcdfd Delete .gradle directory - bad bean 2025-07-07 21:07:27 +01:00
TheLividaProject
b1969646e6 Delete build directory - guess i have to yeet it manually ;-; 2025-07-07 21:05:34 +01:00
TheLividaProject
501eaaa37a Sorted .gitignore and added the suggesion from onebeastchris 2025-07-07 20:43:55 +01:00
TheLividaProject
f67ee6d9f8 Updated a cache to use UUID instead of Player 2025-07-04 17:17:25 +01:00
TheLividaProject
3adb965697 updated to 1.21.7 2025-07-04 17:10:24 +01:00
TheLividaProject
63c20d3f74 updated to PE 2.9.0 SNAPSHOT 2025-07-02 13:41:42 +01:00
TheLividaProject
c83fc4dd0a updated most dependencies 2025-07-02 12:50:47 +01:00
TheLividaProject
c0ed740b47 updated dependencies 2025-06-24 16:58:43 +01:00
zimzaza4
4d75f0615c Fix maven.yml 2025-06-21 15:50:56 +08:00
zimzaza4
97e5bdefcd Update PacketEvents 2025-06-21 15:47:42 +08:00
TheLividaProject
ed61baebf2 updated gradle.build.kts 2025-05-28 12:16:43 +01:00
TheLividaProject
e22170d219 Removed the weird schedulers on playerJoin and playerQuit 2025-05-28 11:46:48 +01:00
TheLividaProject
6e4947d827 Updated playerJoinedCache to use UUID instead of player object 2025-05-28 11:37:32 +01:00
TheLividaProject
0a1151be75 merged BedrockMountControlManager to ModelManager 2025-05-28 11:12:21 +01:00
TheLividaProject
16fa85f52e Removed the weird check for worlds 2025-05-28 02:49:55 +01:00
TheLividaProject
ff1133a4d5 added auto file create (may of forgotten to add it)
TODO
-Need to move the other schedule to a better system
2025-05-28 01:14:30 +01:00
145 changed files with 4014 additions and 1676 deletions

View File

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

44
.gitignore vendored
View File

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

View File

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

Binary file not shown.

3
.idea/.gitignore generated vendored Normal file
View File

@@ -0,0 +1,3 @@
# Default ignored files
/shelf/
/workspace.xml

14
.idea/compiler.xml generated
View File

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

4
.idea/encodings.xml generated
View File

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

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

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

7
.idea/misc.xml generated
View File

@@ -1,12 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ExternalStorageConfigurationManager" enabled="true" />
<component name="MavenProjectsManager">
<option name="originalFiles">
<list>
<option value="$PROJECT_DIR$/pom.xml" />
</list>
</option>
</component>
<component name="ProjectRootManager" version="2" languageLevel="JDK_21" default="true" project-jdk-name="21" project-jdk-type="JavaSDK" />
</project>

228
.idea/workspace.xml generated
View File

@@ -10,32 +10,232 @@
<option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
<option name="LAST_RESOLUTION" value="IGNORE" />
</component>
<component name="DarkyenusTimeTracker">
<option name="totalTimeSeconds" value="32226" />
</component>
<component name="ExternalProjectsData">
<projectState path="$PROJECT_DIR$">
<ProjectState />
</projectState>
</component>
<component name="ExternalProjectsManager">
<system id="GRADLE">
<state>
<task path="$PROJECT_DIR$">
<activation />
</task>
<projects_view>
<tree_state>
<expand>
<path>
<item name="" type="6a2764b6:ExternalProjectsStructure$RootNode" />
<item name="GeyserModelEngine" type="f1a62948:ProjectNode" />
</path>
<path>
<item name="" type="6a2764b6:ExternalProjectsStructure$RootNode" />
<item name="GeyserModelEngine" type="f1a62948:ProjectNode" />
<item name="Tasks" type="e4a08cd1:TasksNode" />
</path>
<path>
<item name="" type="6a2764b6:ExternalProjectsStructure$RootNode" />
<item name="GeyserModelEngine" type="f1a62948:ProjectNode" />
<item name="Tasks" type="e4a08cd1:TasksNode" />
<item name="build" type="c8890929:TasksNode$1" />
</path>
</expand>
<select />
</tree_state>
</projects_view>
</state>
</system>
</component>
<component name="FileTemplateManagerImpl">
<option name="RECENT_TEMPLATES">
<list>
<option value="Interface" />
<option value="Class" />
</list>
</option>
</component>
<component name="Git.Settings">
<option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$" />
</component>
<component name="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">
<option name="delegateBuildToMaven" value="true" />
</component>
<component name="ProjectColorInfo"><![CDATA[{
"associatedIndex": 8
}]]></component>
<component name="ProblemsViewState">
<option name="selectedTabId" value="DEPENDENCY_CHECKER_PROBLEMS_TAB" />
</component>
<component name="ProjectColorInfo">{
&quot;associatedIndex&quot;: 8
}</component>
<component name="ProjectId" id="2xedme8VKz03tyMoE1OuGEibnGo" />
<component name="ProjectLevelVcsManager">
<ConfirmationsSetting value="2" id="Add" />
</component>
<component name="ProjectViewState">
<option name="hideEmptyMiddlePackages" value="true" />
<option name="showLibraryContents" value="true" />
</component>
<component name="PropertiesComponent"><![CDATA[{
"keyToString": {
"Maven.GeyserModelEngine [install...].executor": "Run",
"Maven.GeyserModelEngine [install].executor": "Run",
"ModuleVcsDetector.initialDetectionPerformed": "true",
"RunOnceActivity.ShowReadmeOnStart": "true",
"RunOnceActivity.git.unshallow": "true",
"git-widget-placeholder": "main",
"last_opened_file_path": "D:/Coding/Forks/Minecraft/GeyserModelEngine",
"settings.editor.selected.configurable": "reference.settings.project.maven.runner"
<component name="PropertiesComponent">{
&quot;keyToString&quot;: {
&quot;Gradle.Build GeyserModelEngine.executor&quot;: &quot;Run&quot;,
&quot;Gradle.Download Sources.executor&quot;: &quot;Run&quot;,
&quot;Gradle.GeyserModelEngine [buildDependents].executor&quot;: &quot;Run&quot;,
&quot;Gradle.GeyserModelEngine [buildNeeded].executor&quot;: &quot;Run&quot;,
&quot;Gradle.GeyserModelEngine [build].executor&quot;: &quot;Run&quot;,
&quot;Gradle.GeyserModelEngine [clean].executor&quot;: &quot;Run&quot;,
&quot;Gradle.GeyserModelEngine [jar].executor&quot;: &quot;Run&quot;,
&quot;Maven.GeyserModelEngine [install...].executor&quot;: &quot;Run&quot;,
&quot;Maven.GeyserModelEngine [install].executor&quot;: &quot;Run&quot;,
&quot;ModuleVcsDetector.initialDetectionPerformed&quot;: &quot;true&quot;,
&quot;RunOnceActivity.ShowReadmeOnStart&quot;: &quot;true&quot;,
&quot;RunOnceActivity.TerminalTabsStorage.copyFrom.TerminalArrangementManager.252&quot;: &quot;true&quot;,
&quot;RunOnceActivity.git.unshallow&quot;: &quot;true&quot;,
&quot;git-widget-placeholder&quot;: &quot;main&quot;,
&quot;ignore.virus.scanning.warn.message&quot;: &quot;true&quot;,
&quot;kotlin-language-version-configured&quot;: &quot;true&quot;,
&quot;last_opened_file_path&quot;: &quot;D:/Coding/Minecraft/GeyserExtensionists/GeyserModelEngine&quot;,
&quot;project.structure.last.edited&quot;: &quot;Project&quot;,
&quot;project.structure.proportion&quot;: &quot;0.0&quot;,
&quot;project.structure.side.proportion&quot;: &quot;0.2&quot;,
&quot;settings.editor.selected.configurable&quot;: &quot;reference.settingsdialog.project.gradle&quot;
}
}]]></component>
}</component>
<component name="RecentsManager">
<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" />
</key>
<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\libs" />
</key>
</component>
<component name="RunManager" selected="Gradle.GeyserModelEngine [build]">
<configuration name="GeyserModelEngine [buildDependents]" type="GradleRunConfiguration" factoryName="Gradle" temporary="true">
<ExternalSystemSettings>
<option name="executionName" />
<option name="externalProjectPath" value="$PROJECT_DIR$" />
<option name="externalSystemIdString" value="GRADLE" />
<option name="scriptParameters" />
<option name="taskDescriptions">
<list />
</option>
<option name="taskNames">
<list>
<option value="buildDependents" />
</list>
</option>
<option name="vmOptions" />
</ExternalSystemSettings>
<ExternalSystemDebugServerProcess>true</ExternalSystemDebugServerProcess>
<ExternalSystemReattachDebugProcess>true</ExternalSystemReattachDebugProcess>
<DebugAllEnabled>false</DebugAllEnabled>
<RunAsTest>false</RunAsTest>
<method v="2" />
</configuration>
<configuration name="GeyserModelEngine [buildNeeded]" type="GradleRunConfiguration" factoryName="Gradle" temporary="true">
<ExternalSystemSettings>
<option name="executionName" />
<option name="externalProjectPath" value="$PROJECT_DIR$" />
<option name="externalSystemIdString" value="GRADLE" />
<option name="scriptParameters" />
<option name="taskDescriptions">
<list />
</option>
<option name="taskNames">
<list>
<option value="buildNeeded" />
</list>
</option>
<option name="vmOptions" />
</ExternalSystemSettings>
<ExternalSystemDebugServerProcess>true</ExternalSystemDebugServerProcess>
<ExternalSystemReattachDebugProcess>true</ExternalSystemReattachDebugProcess>
<DebugAllEnabled>false</DebugAllEnabled>
<RunAsTest>false</RunAsTest>
<method v="2" />
</configuration>
<configuration name="GeyserModelEngine [build]" type="GradleRunConfiguration" factoryName="Gradle" temporary="true">
<ExternalSystemSettings>
<option name="executionName" />
<option name="externalProjectPath" value="$PROJECT_DIR$" />
<option name="externalSystemIdString" value="GRADLE" />
<option name="scriptParameters" />
<option name="taskDescriptions">
<list />
</option>
<option name="taskNames">
<list>
<option value="build" />
</list>
</option>
<option name="vmOptions" />
</ExternalSystemSettings>
<ExternalSystemDebugServerProcess>true</ExternalSystemDebugServerProcess>
<ExternalSystemReattachDebugProcess>true</ExternalSystemReattachDebugProcess>
<DebugAllEnabled>false</DebugAllEnabled>
<RunAsTest>false</RunAsTest>
<method v="2" />
</configuration>
<configuration name="GeyserModelEngine [clean]" type="GradleRunConfiguration" factoryName="Gradle" temporary="true">
<ExternalSystemSettings>
<option name="executionName" />
<option name="externalProjectPath" value="$PROJECT_DIR$" />
<option name="externalSystemIdString" value="GRADLE" />
<option name="scriptParameters" />
<option name="taskDescriptions">
<list />
</option>
<option name="taskNames">
<list>
<option value="clean" />
</list>
</option>
<option name="vmOptions" />
</ExternalSystemSettings>
<ExternalSystemDebugServerProcess>true</ExternalSystemDebugServerProcess>
<ExternalSystemReattachDebugProcess>true</ExternalSystemReattachDebugProcess>
<DebugAllEnabled>false</DebugAllEnabled>
<RunAsTest>false</RunAsTest>
<method v="2" />
</configuration>
<configuration name="GeyserModelEngine [jar]" type="GradleRunConfiguration" factoryName="Gradle" temporary="true">
<ExternalSystemSettings>
<option name="executionName" />
<option name="externalProjectPath" value="$PROJECT_DIR$" />
<option name="externalSystemIdString" value="GRADLE" />
<option name="scriptParameters" />
<option name="taskDescriptions">
<list />
</option>
<option name="taskNames">
<list>
<option value="jar" />
</list>
</option>
<option name="vmOptions" />
</ExternalSystemSettings>
<ExternalSystemDebugServerProcess>true</ExternalSystemDebugServerProcess>
<ExternalSystemReattachDebugProcess>true</ExternalSystemReattachDebugProcess>
<DebugAllEnabled>false</DebugAllEnabled>
<RunAsTest>false</RunAsTest>
<method v="2" />
</configuration>
<recent_temporary>
<list>
<item itemvalue="Gradle.GeyserModelEngine [build]" />
<item itemvalue="Gradle.GeyserModelEngine [jar]" />
<item itemvalue="Gradle.GeyserModelEngine [clean]" />
<item itemvalue="Gradle.GeyserModelEngine [buildNeeded]" />
<item itemvalue="Gradle.GeyserModelEngine [buildDependents]" />
</list>
</recent_temporary>
</component>
<component name="TaskManager">
<task active="true" id="Default" summary="Default task">
<changelist id="ff2e9770-ec88-4715-adeb-b9dbda130e1a" name="Changes" comment="" />

View File

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

View File

@@ -1,6 +1,6 @@
plugins {
id("java")
id("io.github.goooler.shadow") version "8.1.7"
id("com.gradleup.shadow") version "9.2.2"
}
group = "re.imc"
@@ -8,35 +8,10 @@ version = "1.0.0"
repositories {
mavenCentral()
maven("https://repo.papermc.io/repository/maven-public/")
maven("https://s01.oss.sonatype.org/content/repositories/snapshots/")
maven("https://mvn.lumine.io/repository/maven-public/")
maven("https://repo.opencollab.dev/maven-releases/") {
isAllowInsecureProtocol = true
}
maven("https://repo.opencollab.dev/maven-snapshots/") {
isAllowInsecureProtocol = true
}
maven("https://repo.codemc.io/repository/maven-public/")
maven("https://repo.codemc.io/repository/maven-releases/")
}
dependencies {
compileOnly("io.papermc.paper:paper-api:1.21.5-R0.1-SNAPSHOT")
implementation("dev.jorel:commandapi-bukkit-shade-mojang-mapped:10.0.1")
compileOnly("com.ticxo.modelengine:ModelEngine:R4.0.4")
compileOnly(files("libs/geyserutils-spigot-1.0-SNAPSHOT.jar"))
compileOnly("org.geysermc.floodgate:api:2.2.2-SNAPSHOT")
implementation("com.github.retrooper:packetevents-spigot:2.8.0")
implementation("org.reflections:reflections:0.10.2")
}
java {
@@ -45,17 +20,4 @@ java {
tasks.compileJava {
options.encoding = "UTF-8"
}
tasks.shadowJar {
relocate("dev.jorel.commandapi", "re.imc.geysermodelengine.libs.commandapi")
relocate("com.github.retrooper", "re.imc.geysermodelengine.libs.com.github.retrooper.packetevents")
relocate("io.github.retrooper", "re.imc.geysermodelengine.libs.io.github.retrooper.packetevents")
relocate("org.reflections", "re.imc.geysermodelengine.libs.reflections")
}
tasks.jar {
dependsOn(tasks.shadowJar)
}

File diff suppressed because one or more lines are too long

View File

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

View File

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

View File

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

35
geyser/build.gradle.kts Normal file
View 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")
}

Binary file not shown.

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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"
]
}
}
}
""";
}

View File

@@ -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;
}
}

View File

@@ -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());
}
}

View File

@@ -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();
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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);
}
}

View File

@@ -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;
}
}

View File

@@ -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);
}
}
}

View File

@@ -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();
}
}

View File

@@ -0,0 +1,4 @@
commands:
geysermodelenginepackgenerator:
reload:
successfully-reloaded: "GeyserModelEnginePackGenerator reloaded!"

View File

@@ -0,0 +1,6 @@
models:
namespace: "modelengine"
options:
resource-pack:
auto-load: true

View 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

View File

@@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME
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
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME

70
paper/build.gradle.kts Normal file
View File

@@ -0,0 +1,70 @@
plugins {
id("java")
id("com.gradleup.shadow") version "9.2.2"
}
group = "re.imc"
version = "1.0.1"
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")
}
tasks.processResources {
val props = mapOf("version" to version)
inputs.properties(props)
filteringCharset = "UTF-8"
filesMatching("paper-plugin.yml") {
expand(props)
}
}

View File

@@ -0,0 +1,108 @@
package re.imc.geysermodelengine;
import com.github.retrooper.packetevents.PacketEvents;
import com.github.retrooper.packetevents.event.PacketListenerPriority;
import io.github.retrooper.packetevents.factory.spigot.SpigotPacketEventsBuilder;
import org.bstats.bukkit.Metrics;
import org.bukkit.Bukkit;
import org.bukkit.plugin.java.JavaPlugin;
import re.imc.geysermodelengine.hooks.FloodgateAPIHook;
import re.imc.geysermodelengine.listener.ModelListener;
import re.imc.geysermodelengine.listener.MountPacketListener;
import re.imc.geysermodelengine.managers.ConfigManager;
import re.imc.geysermodelengine.managers.commands.CommandManager;
import re.imc.geysermodelengine.managers.model.EntityTaskManager;
import re.imc.geysermodelengine.managers.model.ModelManager;
import re.imc.geysermodelengine.runnables.BedrockMountControlRunnable;
import re.imc.geysermodelengine.runnables.UpdateTaskRunnable;
import java.util.concurrent.*;
public class GeyserModelEngine extends JavaPlugin {
private ConfigManager configManager;
private CommandManager commandManager;
private ModelManager modelManager;
private EntityTaskManager entityTaskManager;
private ScheduledExecutorService schedulerPool;
@Override
public void onLoad() {
PacketEvents.setAPI(SpigotPacketEventsBuilder.build(this));
PacketEvents.getAPI().load();
// CommandAPI.onLoad(new CommandAPIPaperConfig(this));
preLoadManagers();
}
@Override
public void onEnable() {
loadHooks();
loadManagers();
loadRunnables();
loadBStats();
PacketEvents.getAPI().getEventManager().registerListener(new MountPacketListener(this), PacketListenerPriority.NORMAL);
Bukkit.getPluginManager().registerEvents(new ModelListener(this), this);
}
@Override
public void onDisable() {
this.modelManager.removeEntities();
PacketEvents.getAPI().terminate();
// CommandAPI.onDisable();
}
private void loadHooks() {
PacketEvents.getAPI().init();
FloodgateAPIHook.loadHook(this);
// CommandAPI.onEnable();
}
private void loadBStats() {
if (this.configManager.getConfig().getBoolean("metrics.bstats", true)) new Metrics(this, 26981);
}
private void preLoadManagers() {
this.configManager = new ConfigManager(this);
}
private void loadManagers() {
this.commandManager = new CommandManager(this);
this.modelManager = new ModelManager(this);
this.entityTaskManager = new EntityTaskManager(this);
}
private void loadRunnables() {
this.schedulerPool = Executors.newScheduledThreadPool(configManager.getConfig().getInt("models.thread-pool-size", 4));
this.schedulerPool.scheduleAtFixedRate(new UpdateTaskRunnable(this), 10, configManager.getConfig().getLong("models.entity-position-update-period", 35), TimeUnit.MILLISECONDS);
this.schedulerPool.scheduleAtFixedRate(new BedrockMountControlRunnable(this), 1, 1, TimeUnit.MILLISECONDS);
}
public ConfigManager getConfigManager() {
return configManager;
}
public CommandManager getCommandManager() {
return commandManager;
}
public ModelManager getModelManager() {
return modelManager;
}
public EntityTaskManager getEntityTaskManager() {
return entityTaskManager;
}
public ScheduledExecutorService getSchedulerPool() {
return schedulerPool;
}
}

View File

@@ -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")));
// });
// }
}

View File

@@ -0,0 +1,24 @@
package re.imc.geysermodelengine.hooks;
import org.bukkit.Bukkit;
import org.geysermc.floodgate.api.FloodgateApi;
import re.imc.geysermodelengine.GeyserModelEngine;
public class FloodgateAPIHook {
private static FloodgateApi floodgateAPI;
public static void loadHook(GeyserModelEngine plugin) {
if (Bukkit.getPluginManager().getPlugin("floodgate") == null || !plugin.getConfigManager().getConfig().getBoolean("options.hooks.floodgate", true)) {
plugin.getLogger().info("Floodgate hook disabled!");
return;
}
floodgateAPI = FloodgateApi.getInstance();
plugin.getLogger().info("Floodgate hook enabled!");
}
public static FloodgateApi getAPI() {
return floodgateAPI;
}
}

View File

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

View File

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

View File

@@ -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());
}
}

View File

@@ -11,6 +11,7 @@ import org.apache.commons.lang3.tuple.Pair;
import org.bukkit.entity.Player;
import org.geysermc.floodgate.api.FloodgateApi;
import re.imc.geysermodelengine.GeyserModelEngine;
import re.imc.geysermodelengine.util.BedrockUtils;
public class MountPacketListener implements PacketListener {
@@ -23,16 +24,16 @@ public class MountPacketListener implements PacketListener {
@Override
public void onPacketReceive(PacketReceiveEvent event) {
if (event.getPacketType() != PacketType.Play.Client.ENTITY_ACTION) return;
if (!FloodgateApi.getInstance().isFloodgatePlayer(event.getUser().getUUID())) return;
Player player = event.getPlayer();
if (!BedrockUtils.isBedrockPlayer(player)) return;
WrapperPlayClientEntityAction action = new WrapperPlayClientEntityAction(event);
Pair<ActiveModel, Mount> seat = plugin.getBedrockMountControlManager().getDriversCache().get(player);
Pair<ActiveModel, Mount> seat = plugin.getModelManager().getDriversCache().get(player.getUniqueId());
if (seat == null) return;
if (action.getAction() != WrapperPlayClientEntityAction.Action.START_SNEAKING) return;
ModelEngineAPI.getMountPairManager().tryDismount(event.getPlayer());
ModelEngineAPI.getMountPairManager().tryDismount(player);
}
}

View File

@@ -18,6 +18,12 @@ public class ConfigManager {
}
public void load() {
if (!plugin.getDataFolder().exists()) {
plugin.getDataFolder().mkdir();
plugin.saveResource("config.yml", false);
plugin.saveResource("Lang/messages.yml", false);
}
this.config = YamlConfiguration.loadConfiguration(new File(plugin.getDataFolder(), "config.yml"));
this.lang = YamlConfiguration.loadConfiguration(new File(plugin.getDataFolder(), "Lang/messages.yml"));
}

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