diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 581256c..b27e9d1 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -35,4 +35,5 @@ jobs:
automatic_release_tag: latest
prerelease: false
files: |
- build/libs/GeyserModelEngine*.jar
+ paper/build/libs/GeyserModelEngine*.jar
+ geyser/build/libs/GeyserModelEngine*.jar
diff --git a/.idea/.gitignore b/.idea/.gitignore
new file mode 100644
index 0000000..26d3352
--- /dev/null
+++ b/.idea/.gitignore
@@ -0,0 +1,3 @@
+# Default ignored files
+/shelf/
+/workspace.xml
diff --git a/.idea/encodings.xml b/.idea/encodings.xml
index aa00ffa..da33708 100644
--- a/.idea/encodings.xml
+++ b/.idea/encodings.xml
@@ -1,7 +1,7 @@
-
-
+
+
\ No newline at end of file
diff --git a/.idea/gradle.xml b/.idea/gradle.xml
index ce1c62c..79647b7 100644
--- a/.idea/gradle.xml
+++ b/.idea/gradle.xml
@@ -8,6 +8,8 @@
diff --git a/.idea/workspace.xml b/.idea/workspace.xml
index 2e64d3a..dd911a4 100644
--- a/.idea/workspace.xml
+++ b/.idea/workspace.xml
@@ -4,24 +4,14 @@
-
-
-
-
-
-
-
-
-
-
-
+
-
+
@@ -70,7 +60,7 @@
-
+
@@ -88,35 +78,39 @@
- {
- "keyToString": {
- "Gradle.Build GeyserModelEngine.executor": "Run",
- "Gradle.Download Sources.executor": "Run",
- "Gradle.GeyserModelEngine [buildDependents].executor": "Run",
- "Gradle.GeyserModelEngine [buildNeeded].executor": "Run",
- "Gradle.GeyserModelEngine [build].executor": "Run",
- "Gradle.GeyserModelEngine [clean].executor": "Run",
- "Gradle.GeyserModelEngine [jar].executor": "Run",
- "Maven.GeyserModelEngine [install...].executor": "Run",
- "Maven.GeyserModelEngine [install].executor": "Run",
- "ModuleVcsDetector.initialDetectionPerformed": "true",
- "RunOnceActivity.ShowReadmeOnStart": "true",
- "RunOnceActivity.TerminalTabsStorage.copyFrom.TerminalArrangementManager.252": "true",
- "RunOnceActivity.git.unshallow": "true",
- "git-widget-placeholder": "main",
- "kotlin-language-version-configured": "true",
- "last_opened_file_path": "D:/Coding/Forks/Minecraft/GeyserModelEngine",
- "project.structure.last.edited": "Project",
- "project.structure.proportion": "0.0",
- "project.structure.side.proportion": "0.2",
- "settings.editor.selected.configurable": "reference.settingsdialog.project.gradle"
+
+}]]>
+
+
+
@@ -235,8 +229,8 @@
-
+
diff --git a/build.gradle.kts b/build.gradle.kts
index c598abf..19d3d7a 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -8,31 +8,10 @@ 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.10.1")
- implementation("org.bstats:bstats-bukkit:3.0.2")
-
- implementation("org.reflections:reflections:0.10.2")
}
java {
@@ -41,21 +20,4 @@ java {
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")
}
\ No newline at end of file
diff --git a/geyser/build.gradle.kts b/geyser/build.gradle.kts
new file mode 100644
index 0000000..17073d5
--- /dev/null
+++ b/geyser/build.gradle.kts
@@ -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")
+}
diff --git a/geyser/libs/geyserutils-geyser-1.0-SNAPSHOT.jar b/geyser/libs/geyserutils-geyser-1.0-SNAPSHOT.jar
new file mode 100644
index 0000000..e4579a4
Binary files /dev/null and b/geyser/libs/geyserutils-geyser-1.0-SNAPSHOT.jar differ
diff --git a/geyser/src/main/java/re/imc/geysermodelenginepackgenerator/GeyserModelEnginePackGenerator.java b/geyser/src/main/java/re/imc/geysermodelenginepackgenerator/GeyserModelEnginePackGenerator.java
new file mode 100644
index 0000000..a8c1a5a
--- /dev/null
+++ b/geyser/src/main/java/re/imc/geysermodelenginepackgenerator/GeyserModelEnginePackGenerator.java
@@ -0,0 +1,71 @@
+package re.imc.geysermodelenginepackgenerator;
+
+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.geysermodelenginepackgenerator.managers.ConfigManager;
+import re.imc.geysermodelenginepackgenerator.managers.resourcepack.ResourcePackManager;
+
+public class GeyserModelEnginePackGenerator implements Extension {
+
+ private static GeyserModelEnginePackGenerator 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 GeyserModelEnginePackGenerator getExtension() {
+ return extension;
+ }
+
+ public ConfigManager getConfigManager() {
+ return configManager;
+ }
+
+ public ResourcePackManager getResourcePackManager() {
+ return resourcePackManager;
+ }
+}
diff --git a/geyser/src/main/java/re/imc/geysermodelenginepackgenerator/managers/ConfigManager.java b/geyser/src/main/java/re/imc/geysermodelenginepackgenerator/managers/ConfigManager.java
new file mode 100644
index 0000000..f494f48
--- /dev/null
+++ b/geyser/src/main/java/re/imc/geysermodelenginepackgenerator/managers/ConfigManager.java
@@ -0,0 +1,25 @@
+package re.imc.geysermodelenginepackgenerator.managers;
+
+import re.imc.geysermodelenginepackgenerator.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;
+ }
+}
diff --git a/geyser/src/main/java/re/imc/geysermodelenginepackgenerator/managers/resourcepack/ResourcePackManager.java b/geyser/src/main/java/re/imc/geysermodelenginepackgenerator/managers/resourcepack/ResourcePackManager.java
new file mode 100644
index 0000000..44ef4b7
--- /dev/null
+++ b/geyser/src/main/java/re/imc/geysermodelenginepackgenerator/managers/resourcepack/ResourcePackManager.java
@@ -0,0 +1,446 @@
+package re.imc.geysermodelenginepackgenerator.managers.resourcepack;
+
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+import com.google.gson.JsonParser;
+import re.imc.geysermodelenginepackgenerator.GeyserModelEnginePackGenerator;
+import re.imc.geysermodelenginepackgenerator.managers.resourcepack.generator.*;
+import re.imc.geysermodelenginepackgenerator.managers.resourcepack.generator.data.TextureData;
+import re.imc.geysermodelenginepackgenerator.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 GeyserModelEnginePackGenerator extension;
+
+ private final File inputFolder;
+ private final File generatedPack;
+
+ private Path generatedPackZipPath;
+
+ private final HashMap entityCache = new HashMap<>();
+ private final HashMap animationCache = new HashMap<>();
+ private final HashMap geometryCache = new HashMap<>();
+ private final HashMap> textureCache = new HashMap<>();
+
+ private final Gson GSON = new GsonBuilder().setPrettyPrinting().create();
+
+ public ResourcePackManager(GeyserModelEnginePackGenerator 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 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 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 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> textures : textureCache.entrySet()) {
+ for (Map.Entry 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 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 bindingBones = new HashSet<>();
+ bindingBones.add("*");
+ if (modelConfig.getBingingBones().containsKey(textureName)) bindingBones = modelConfig.getBingingBones().get(textureName);
+
+ Map 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 bindingBones = new HashSet<>();
+ bindingBones.add("*");
+
+ if (modelConfig.getBingingBones().containsKey(textureName)) {
+ bindingBones = modelConfig.getBingingBones().get(textureName);
+ }
+
+ Map 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 getEntityCache() {
+ return entityCache;
+ }
+
+ public HashMap getAnimationCache() {
+ return animationCache;
+ }
+
+ public HashMap getGeometryCache() {
+ return geometryCache;
+ }
+
+ public HashMap> getTextureCache() {
+ return textureCache;
+ }
+}
diff --git a/geyser/src/main/java/re/imc/geysermodelenginepackgenerator/managers/resourcepack/generator/Animation.java b/geyser/src/main/java/re/imc/geysermodelenginepackgenerator/managers/resourcepack/generator/Animation.java
new file mode 100644
index 0000000..dac86ae
--- /dev/null
+++ b/geyser/src/main/java/re/imc/geysermodelenginepackgenerator/managers/resourcepack/generator/Animation.java
@@ -0,0 +1,151 @@
+package re.imc.geysermodelenginepackgenerator.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.geysermodelenginepackgenerator.GeyserModelEnginePackGenerator;
+
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+public class Animation {
+
+ private String modelId;
+ private JsonObject json;
+ private Set 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 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 bone : animation.get("bones").getAsJsonObject().entrySet()) {
+
+ for (Map.Entry anim : bone.getValue().getAsJsonObject().entrySet()) {
+ float max = -1;
+ JsonObject end = null;
+ if (!anim.getValue().isJsonObject()) {
+ continue;
+ }
+ try {
+ for (Map.Entry 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;
+
+ GeyserModelEnginePackGenerator.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 animationIds) {
+ this.animationIds = animationIds;
+ }
+
+ public String getModelId() {
+ return modelId;
+ }
+
+ public void setPath(String path) {
+ this.path = path;
+ }
+
+ public JsonObject getJson() {
+ return json;
+ }
+
+ public Set getAnimationIds() {
+ return animationIds;
+ }
+
+ public String getPath() {
+ return path;
+ }
+}
diff --git a/geyser/src/main/java/re/imc/geysermodelenginepackgenerator/managers/resourcepack/generator/AnimationController.java b/geyser/src/main/java/re/imc/geysermodelenginepackgenerator/managers/resourcepack/generator/AnimationController.java
new file mode 100644
index 0000000..605cca5
--- /dev/null
+++ b/geyser/src/main/java/re/imc/geysermodelenginepackgenerator/managers/resourcepack/generator/AnimationController.java
@@ -0,0 +1,84 @@
+package re.imc.geysermodelenginepackgenerator.managers.resourcepack.generator;
+
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+import com.google.gson.JsonParser;
+import re.imc.geysermodelenginepackgenerator.GeyserModelEnginePackGenerator;
+
+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(GeyserModelEnginePackGenerator 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 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 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;
+ }
+}
diff --git a/geyser/src/main/java/re/imc/geysermodelenginepackgenerator/managers/resourcepack/generator/Entity.java b/geyser/src/main/java/re/imc/geysermodelenginepackgenerator/managers/resourcepack/generator/Entity.java
new file mode 100644
index 0000000..cb0d772
--- /dev/null
+++ b/geyser/src/main/java/re/imc/geysermodelenginepackgenerator/managers/resourcepack/generator/Entity.java
@@ -0,0 +1,207 @@
+package re.imc.geysermodelenginepackgenerator.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.geysermodelenginepackgenerator.managers.resourcepack.generator.data.TextureData;
+
+import java.util.*;
+
+public class Entity {
+
+ public static final Set 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 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 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 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 getTextureMap() {
+ return textureMap;
+ }
+
+ public ModelConfig getModelConfig() {
+ return modelConfig;
+ }
+}
diff --git a/geyser/src/main/java/re/imc/geysermodelenginepackgenerator/managers/resourcepack/generator/Geometry.java b/geyser/src/main/java/re/imc/geysermodelenginepackgenerator/managers/resourcepack/generator/Geometry.java
new file mode 100644
index 0000000..9f39330
--- /dev/null
+++ b/geyser/src/main/java/re/imc/geysermodelenginepackgenerator/managers/resourcepack/generator/Geometry.java
@@ -0,0 +1,113 @@
+package re.imc.geysermodelenginepackgenerator.managers.resourcepack.generator;
+
+import com.google.gson.*;
+import re.imc.geysermodelenginepackgenerator.managers.resourcepack.generator.data.BoneData;
+
+import java.util.*;
+
+public class Geometry {
+
+ private String modelId;
+ private String geometryId;
+ private JsonObject json;
+ private final Map 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 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 getBones() {
+ return bones;
+ }
+}
diff --git a/geyser/src/main/java/re/imc/geysermodelenginepackgenerator/managers/resourcepack/generator/Material.java b/geyser/src/main/java/re/imc/geysermodelenginepackgenerator/managers/resourcepack/generator/Material.java
new file mode 100644
index 0000000..fa537da
--- /dev/null
+++ b/geyser/src/main/java/re/imc/geysermodelenginepackgenerator/managers/resourcepack/generator/Material.java
@@ -0,0 +1,38 @@
+package re.imc.geysermodelenginepackgenerator.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"
+ ]
+ }
+ }
+ }
+ """;
+}
diff --git a/geyser/src/main/java/re/imc/geysermodelenginepackgenerator/managers/resourcepack/generator/ModelConfig.java b/geyser/src/main/java/re/imc/geysermodelenginepackgenerator/managers/resourcepack/generator/ModelConfig.java
new file mode 100644
index 0000000..f95ecb0
--- /dev/null
+++ b/geyser/src/main/java/re/imc/geysermodelenginepackgenerator/managers/resourcepack/generator/ModelConfig.java
@@ -0,0 +1,104 @@
+package re.imc.geysermodelenginepackgenerator.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> bingingBones = new HashMap<>();
+ @SerializedName("anim_textures")
+ Map animTextures = new HashMap<>();
+ @SerializedName("texture_materials")
+ Map textureMaterials = new HashMap<>();
+ @SerializedName("per_texture_uv_size")
+ Map 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> bingingBones) {
+ this.bingingBones = bingingBones;
+ }
+
+ public void setAnimTextures(Map animTextures) {
+ this.animTextures = animTextures;
+ }
+
+ public void setTextureMaterials(Map textureMaterials) {
+ this.textureMaterials = textureMaterials;
+ }
+
+ public void setPerTextureUvSize(Map perTextureUvSize) {
+ this.perTextureUvSize = perTextureUvSize;
+ }
+
+ public void setDisablePartVisibility(boolean disablePartVisibility) {
+ this.disablePartVisibility = disablePartVisibility;
+ }
+
+ public Map getTextureMaterials() {
+ return textureMaterials != null ? textureMaterials : Map.of();
+ }
+
+ public Map getPerTextureUvSize() {
+ return perTextureUvSize != null ? perTextureUvSize : Map.of();
+ }
+
+ public boolean isEnableHeadRotation() {
+ return enableHeadRotation;
+ }
+
+ public String getMaterial() {
+ return material;
+ }
+
+ public boolean isEnableBlendTransition() {
+ return enableBlendTransition;
+ }
+
+ public Map> getBingingBones() {
+ return bingingBones;
+ }
+
+ public Map getAnimTextures() {
+ return animTextures;
+ }
+
+ public boolean isDisablePartVisibility() {
+ return disablePartVisibility;
+ }
+
+ @NoArgsConstructor
+ @AllArgsConstructor
+ @Getter
+ @Setter
+ public static class AnimTextureOptions {
+ float fps;
+ int frames;
+ }
+}
diff --git a/geyser/src/main/java/re/imc/geysermodelenginepackgenerator/managers/resourcepack/generator/PackManifest.java b/geyser/src/main/java/re/imc/geysermodelenginepackgenerator/managers/resourcepack/generator/PackManifest.java
new file mode 100644
index 0000000..d8bd473
--- /dev/null
+++ b/geyser/src/main/java/re/imc/geysermodelenginepackgenerator/managers/resourcepack/generator/PackManifest.java
@@ -0,0 +1,32 @@
+package re.imc.geysermodelenginepackgenerator.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());
+ }
+}
diff --git a/geyser/src/main/java/re/imc/geysermodelenginepackgenerator/managers/resourcepack/generator/RenderController.java b/geyser/src/main/java/re/imc/geysermodelenginepackgenerator/managers/resourcepack/generator/RenderController.java
new file mode 100644
index 0000000..b763b73
--- /dev/null
+++ b/geyser/src/main/java/re/imc/geysermodelenginepackgenerator/managers/resourcepack/generator/RenderController.java
@@ -0,0 +1,178 @@
+package re.imc.geysermodelenginepackgenerator.managers.resourcepack.generator;
+
+import com.google.gson.JsonArray;
+import com.google.gson.JsonObject;
+import re.imc.geysermodelenginepackgenerator.managers.resourcepack.generator.data.BoneData;
+
+import java.util.*;
+
+public class RenderController {
+
+ public static final Set NEED_REMOVE_WHEN_SORT = Set.of("pbody_", "plarm_", "prarm_", "plleg_", "prleg_", "phead_", "p_");
+
+ private final String modelId;
+ private final Map bones;
+ private final Entity entity;
+
+ public RenderController(String modelId, Map 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 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 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 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 sorted = new ArrayList<>(bones.keySet());
+ Map originalId = new HashMap<>();
+ ListIterator 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 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> 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();
+ }
+
+}
diff --git a/geyser/src/main/java/re/imc/geysermodelenginepackgenerator/managers/resourcepack/generator/data/BoneData.java b/geyser/src/main/java/re/imc/geysermodelenginepackgenerator/managers/resourcepack/generator/data/BoneData.java
new file mode 100644
index 0000000..360f175
--- /dev/null
+++ b/geyser/src/main/java/re/imc/geysermodelenginepackgenerator/managers/resourcepack/generator/data/BoneData.java
@@ -0,0 +1,34 @@
+package re.imc.geysermodelenginepackgenerator.managers.resourcepack.generator.data;
+
+import java.util.Set;
+
+public class BoneData {
+
+ private final String name;
+ private final String parent;
+ private final Set children;
+ private final Set allChildren;
+
+ public BoneData(String name, String parent, Set children, Set 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 getChildren() {
+ return children;
+ }
+
+ public Set getAllChildren() {
+ return allChildren;
+ }
+}
diff --git a/geyser/src/main/java/re/imc/geysermodelenginepackgenerator/managers/resourcepack/generator/data/TextureData.java b/geyser/src/main/java/re/imc/geysermodelenginepackgenerator/managers/resourcepack/generator/data/TextureData.java
new file mode 100644
index 0000000..0d6c824
--- /dev/null
+++ b/geyser/src/main/java/re/imc/geysermodelenginepackgenerator/managers/resourcepack/generator/data/TextureData.java
@@ -0,0 +1,34 @@
+package re.imc.geysermodelenginepackgenerator.managers.resourcepack.generator.data;
+
+import java.util.Set;
+
+public class TextureData {
+
+ private final String modelId;
+ private final String path;
+ private final Set bindingBones;
+ private final byte[] image;
+
+ public TextureData(String modelId, String path, Set 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 getBindingBones() {
+ return bindingBones;
+ }
+
+ public byte[] getImage() {
+ return image;
+ }
+}
diff --git a/geyser/src/main/java/re/imc/geysermodelenginepackgenerator/util/BooleanPacker.java b/geyser/src/main/java/re/imc/geysermodelenginepackgenerator/util/BooleanPacker.java
new file mode 100644
index 0000000..19f4519
--- /dev/null
+++ b/geyser/src/main/java/re/imc/geysermodelenginepackgenerator/util/BooleanPacker.java
@@ -0,0 +1,69 @@
+package re.imc.geysermodelenginepackgenerator.util;
+
+import java.util.*;
+import java.util.concurrent.ThreadLocalRandom;
+
+public class BooleanPacker {
+
+ public static final int MAX_BOOLEANS = 24;
+
+ public static int booleansToInt(List booleans) {
+ int result = 0;
+ int i = 1;
+
+ for (boolean b : booleans) {
+ if (b) result += i;
+ i *= 2;
+ }
+
+ return result;
+ }
+
+ public static int mapBooleansToInt(Map booleanMap) {
+ int result = 0;
+ int i = 1;
+ List keys = new ArrayList<>(booleanMap.keySet());
+ Collections.sort(keys);
+
+ for (String key : keys) {
+ if (booleanMap.get(key)) result += i;
+ i *= 2;
+ }
+
+ return result;
+ }
+
+ public static List booleansToInts(List booleans) {
+ List results = new ArrayList<>();
+ int result = 0;
+ int i = 1;
+ int i1 = 1;
+ for (boolean b : booleans) {
+ if (b) {
+ result += i;
+ }
+ if (i1 % MAX_BOOLEANS == 0 || i1 == booleans.size()) {
+ results.add(result);
+ result = 0;
+ i = 1;
+ } else {
+ i *= 2;
+ }
+ i1++;
+ }
+
+ return results;
+ }
+
+ public static List mapBooleansToInts(Map booleanMap) {
+ List keys = new ArrayList<>(booleanMap.keySet());
+ List booleans = new ArrayList<>();
+ Collections.sort(keys);
+
+ for (String key : keys) {
+ booleans.add(booleanMap.get(key));
+ }
+
+ return booleansToInts(booleans);
+ }
+}
diff --git a/geyser/src/main/java/re/imc/geysermodelenginepackgenerator/util/FileConfiguration.java b/geyser/src/main/java/re/imc/geysermodelenginepackgenerator/util/FileConfiguration.java
new file mode 100644
index 0000000..8198d83
--- /dev/null
+++ b/geyser/src/main/java/re/imc/geysermodelenginepackgenerator/util/FileConfiguration.java
@@ -0,0 +1,147 @@
+package re.imc.geysermodelenginepackgenerator.util;
+
+import org.spongepowered.configurate.CommentedConfigurationNode;
+import org.spongepowered.configurate.serialize.SerializationException;
+import org.spongepowered.configurate.yaml.YamlConfigurationLoader;
+import re.imc.geysermodelenginepackgenerator.GeyserModelEnginePackGenerator;
+
+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.List;
+
+public class FileConfiguration {
+
+ private final GeyserModelEnginePackGenerator extension = GeyserModelEnginePackGenerator.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 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 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 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;
+ }
+}
diff --git a/geyser/src/main/java/re/imc/geysermodelenginepackgenerator/util/FileUtils.java b/geyser/src/main/java/re/imc/geysermodelenginepackgenerator/util/FileUtils.java
new file mode 100644
index 0000000..025e557
--- /dev/null
+++ b/geyser/src/main/java/re/imc/geysermodelenginepackgenerator/util/FileUtils.java
@@ -0,0 +1,52 @@
+package re.imc.geysermodelenginepackgenerator.util;
+
+import re.imc.geysermodelenginepackgenerator.GeyserModelEnginePackGenerator;
+
+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 getAllFiles(File folder, String fileType) {
+ List 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(GeyserModelEnginePackGenerator 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);
+ }
+ }
+}
diff --git a/geyser/src/main/java/re/imc/geysermodelenginepackgenerator/util/ZipUtil.java b/geyser/src/main/java/re/imc/geysermodelenginepackgenerator/util/ZipUtil.java
new file mode 100644
index 0000000..c6dc043
--- /dev/null
+++ b/geyser/src/main/java/re/imc/geysermodelenginepackgenerator/util/ZipUtil.java
@@ -0,0 +1,49 @@
+package re.imc.geysermodelenginepackgenerator.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();
+ }
+}
diff --git a/geyser/src/main/resources/Extension/Lang/messages.yml b/geyser/src/main/resources/Extension/Lang/messages.yml
new file mode 100644
index 0000000..e5ff741
--- /dev/null
+++ b/geyser/src/main/resources/Extension/Lang/messages.yml
@@ -0,0 +1,4 @@
+commands:
+ geysermodelenginepackgenerator:
+ reload:
+ successfully-reloaded: "GeyserModelEnginePackGenerator reloaded!"
\ No newline at end of file
diff --git a/geyser/src/main/resources/Extension/config.yml b/geyser/src/main/resources/Extension/config.yml
new file mode 100644
index 0000000..f176c8b
--- /dev/null
+++ b/geyser/src/main/resources/Extension/config.yml
@@ -0,0 +1,6 @@
+models:
+ namespace: "modelengine"
+
+options:
+ resource-pack:
+ auto-load: true
\ No newline at end of file
diff --git a/geyser/src/main/resources/extension.yml b/geyser/src/main/resources/extension.yml
new file mode 100644
index 0000000..5d0c404
--- /dev/null
+++ b/geyser/src/main/resources/extension.yml
@@ -0,0 +1,8 @@
+name: GeyserModelEnginePackGenerator
+id: geysermodelenginepackgenerator
+main: re.imc.geysermodelenginepackgenerator.geyser.GeyserModelEnginePackGenerator
+api: 2.7.0
+version: 1.0.0
+authors:
+ - zimzaza4
+ - xSquishyLiam
\ No newline at end of file
diff --git a/paper/build.gradle.kts b/paper/build.gradle.kts
new file mode 100644
index 0000000..c598abf
--- /dev/null
+++ b/paper/build.gradle.kts
@@ -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.10.1")
+ 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")
+}
\ No newline at end of file
diff --git a/libs/geyserutils-spigot-1.0-SNAPSHOT.jar b/paper/libs/geyserutils-spigot-1.0-SNAPSHOT.jar
similarity index 100%
rename from libs/geyserutils-spigot-1.0-SNAPSHOT.jar
rename to paper/libs/geyserutils-spigot-1.0-SNAPSHOT.jar
diff --git a/src/main/java/re/imc/geysermodelengine/GeyserModelEngine.java b/paper/src/main/java/re/imc/geysermodelengine/GeyserModelEngine.java
similarity index 100%
rename from src/main/java/re/imc/geysermodelengine/GeyserModelEngine.java
rename to paper/src/main/java/re/imc/geysermodelengine/GeyserModelEngine.java
diff --git a/src/main/java/re/imc/geysermodelengine/commands/geysermodelenginecommands/GeyserModelEngineReloadCommand.java b/paper/src/main/java/re/imc/geysermodelengine/commands/geysermodelenginecommands/GeyserModelEngineReloadCommand.java
similarity index 100%
rename from src/main/java/re/imc/geysermodelengine/commands/geysermodelenginecommands/GeyserModelEngineReloadCommand.java
rename to paper/src/main/java/re/imc/geysermodelengine/commands/geysermodelenginecommands/GeyserModelEngineReloadCommand.java
diff --git a/src/main/java/re/imc/geysermodelengine/listener/BetterModelListener.java b/paper/src/main/java/re/imc/geysermodelengine/listener/BetterModelListener.java
similarity index 100%
rename from src/main/java/re/imc/geysermodelengine/listener/BetterModelListener.java
rename to paper/src/main/java/re/imc/geysermodelengine/listener/BetterModelListener.java
diff --git a/src/main/java/re/imc/geysermodelengine/listener/ModelEngineListener.java b/paper/src/main/java/re/imc/geysermodelengine/listener/ModelEngineListener.java
similarity index 100%
rename from src/main/java/re/imc/geysermodelengine/listener/ModelEngineListener.java
rename to paper/src/main/java/re/imc/geysermodelengine/listener/ModelEngineListener.java
diff --git a/src/main/java/re/imc/geysermodelengine/listener/ModelListener.java b/paper/src/main/java/re/imc/geysermodelengine/listener/ModelListener.java
similarity index 100%
rename from src/main/java/re/imc/geysermodelengine/listener/ModelListener.java
rename to paper/src/main/java/re/imc/geysermodelengine/listener/ModelListener.java
diff --git a/src/main/java/re/imc/geysermodelengine/listener/MountPacketListener.java b/paper/src/main/java/re/imc/geysermodelengine/listener/MountPacketListener.java
similarity index 100%
rename from src/main/java/re/imc/geysermodelengine/listener/MountPacketListener.java
rename to paper/src/main/java/re/imc/geysermodelengine/listener/MountPacketListener.java
diff --git a/src/main/java/re/imc/geysermodelengine/managers/ConfigManager.java b/paper/src/main/java/re/imc/geysermodelengine/managers/ConfigManager.java
similarity index 100%
rename from src/main/java/re/imc/geysermodelengine/managers/ConfigManager.java
rename to paper/src/main/java/re/imc/geysermodelengine/managers/ConfigManager.java
diff --git a/src/main/java/re/imc/geysermodelengine/managers/commands/CommandManager.java b/paper/src/main/java/re/imc/geysermodelengine/managers/commands/CommandManager.java
similarity index 100%
rename from src/main/java/re/imc/geysermodelengine/managers/commands/CommandManager.java
rename to paper/src/main/java/re/imc/geysermodelengine/managers/commands/CommandManager.java
diff --git a/src/main/java/re/imc/geysermodelengine/managers/commands/CommandManagers.java b/paper/src/main/java/re/imc/geysermodelengine/managers/commands/CommandManagers.java
similarity index 100%
rename from src/main/java/re/imc/geysermodelengine/managers/commands/CommandManagers.java
rename to paper/src/main/java/re/imc/geysermodelengine/managers/commands/CommandManagers.java
diff --git a/src/main/java/re/imc/geysermodelengine/managers/commands/managers/geysermodelengine/GeyserModelEngineCommandManager.java b/paper/src/main/java/re/imc/geysermodelengine/managers/commands/managers/geysermodelengine/GeyserModelEngineCommandManager.java
similarity index 100%
rename from src/main/java/re/imc/geysermodelengine/managers/commands/managers/geysermodelengine/GeyserModelEngineCommandManager.java
rename to paper/src/main/java/re/imc/geysermodelengine/managers/commands/managers/geysermodelengine/GeyserModelEngineCommandManager.java
diff --git a/src/main/java/re/imc/geysermodelengine/managers/commands/subcommands/SubCommands.java b/paper/src/main/java/re/imc/geysermodelengine/managers/commands/subcommands/SubCommands.java
similarity index 100%
rename from src/main/java/re/imc/geysermodelengine/managers/commands/subcommands/SubCommands.java
rename to paper/src/main/java/re/imc/geysermodelengine/managers/commands/subcommands/SubCommands.java
diff --git a/src/main/java/re/imc/geysermodelengine/managers/model/EntityTaskManager.java b/paper/src/main/java/re/imc/geysermodelengine/managers/model/EntityTaskManager.java
similarity index 100%
rename from src/main/java/re/imc/geysermodelengine/managers/model/EntityTaskManager.java
rename to paper/src/main/java/re/imc/geysermodelengine/managers/model/EntityTaskManager.java
diff --git a/src/main/java/re/imc/geysermodelengine/managers/model/ModelManager.java b/paper/src/main/java/re/imc/geysermodelengine/managers/model/ModelManager.java
similarity index 100%
rename from src/main/java/re/imc/geysermodelengine/managers/model/ModelManager.java
rename to paper/src/main/java/re/imc/geysermodelengine/managers/model/ModelManager.java
diff --git a/src/main/java/re/imc/geysermodelengine/managers/model/entity/BetterModelEntityData.java b/paper/src/main/java/re/imc/geysermodelengine/managers/model/entity/BetterModelEntityData.java
similarity index 100%
rename from src/main/java/re/imc/geysermodelengine/managers/model/entity/BetterModelEntityData.java
rename to paper/src/main/java/re/imc/geysermodelengine/managers/model/entity/BetterModelEntityData.java
diff --git a/src/main/java/re/imc/geysermodelengine/managers/model/entity/EntityData.java b/paper/src/main/java/re/imc/geysermodelengine/managers/model/entity/EntityData.java
similarity index 100%
rename from src/main/java/re/imc/geysermodelengine/managers/model/entity/EntityData.java
rename to paper/src/main/java/re/imc/geysermodelengine/managers/model/entity/EntityData.java
diff --git a/src/main/java/re/imc/geysermodelengine/managers/model/entity/ModelEngineEntityData.java b/paper/src/main/java/re/imc/geysermodelengine/managers/model/entity/ModelEngineEntityData.java
similarity index 100%
rename from src/main/java/re/imc/geysermodelengine/managers/model/entity/ModelEngineEntityData.java
rename to paper/src/main/java/re/imc/geysermodelengine/managers/model/entity/ModelEngineEntityData.java
diff --git a/src/main/java/re/imc/geysermodelengine/managers/model/model/BetterModelModel.java b/paper/src/main/java/re/imc/geysermodelengine/managers/model/model/BetterModelModel.java
similarity index 100%
rename from src/main/java/re/imc/geysermodelengine/managers/model/model/BetterModelModel.java
rename to paper/src/main/java/re/imc/geysermodelengine/managers/model/model/BetterModelModel.java
diff --git a/src/main/java/re/imc/geysermodelengine/managers/model/model/Model.java b/paper/src/main/java/re/imc/geysermodelengine/managers/model/model/Model.java
similarity index 100%
rename from src/main/java/re/imc/geysermodelengine/managers/model/model/Model.java
rename to paper/src/main/java/re/imc/geysermodelengine/managers/model/model/Model.java
diff --git a/src/main/java/re/imc/geysermodelengine/managers/model/model/ModelEngineModel.java b/paper/src/main/java/re/imc/geysermodelengine/managers/model/model/ModelEngineModel.java
similarity index 100%
rename from src/main/java/re/imc/geysermodelengine/managers/model/model/ModelEngineModel.java
rename to paper/src/main/java/re/imc/geysermodelengine/managers/model/model/ModelEngineModel.java
diff --git a/src/main/java/re/imc/geysermodelengine/managers/model/modelhandler/BetterModelHandler.java b/paper/src/main/java/re/imc/geysermodelengine/managers/model/modelhandler/BetterModelHandler.java
similarity index 100%
rename from src/main/java/re/imc/geysermodelengine/managers/model/modelhandler/BetterModelHandler.java
rename to paper/src/main/java/re/imc/geysermodelengine/managers/model/modelhandler/BetterModelHandler.java
diff --git a/src/main/java/re/imc/geysermodelengine/managers/model/modelhandler/ModelEngineHandler.java b/paper/src/main/java/re/imc/geysermodelengine/managers/model/modelhandler/ModelEngineHandler.java
similarity index 100%
rename from src/main/java/re/imc/geysermodelengine/managers/model/modelhandler/ModelEngineHandler.java
rename to paper/src/main/java/re/imc/geysermodelengine/managers/model/modelhandler/ModelEngineHandler.java
diff --git a/src/main/java/re/imc/geysermodelengine/managers/model/modelhandler/ModelHandler.java b/paper/src/main/java/re/imc/geysermodelengine/managers/model/modelhandler/ModelHandler.java
similarity index 100%
rename from src/main/java/re/imc/geysermodelengine/managers/model/modelhandler/ModelHandler.java
rename to paper/src/main/java/re/imc/geysermodelengine/managers/model/modelhandler/ModelHandler.java
diff --git a/src/main/java/re/imc/geysermodelengine/managers/model/propertyhandler/BetterModelPropertyHandler.java b/paper/src/main/java/re/imc/geysermodelengine/managers/model/propertyhandler/BetterModelPropertyHandler.java
similarity index 100%
rename from src/main/java/re/imc/geysermodelengine/managers/model/propertyhandler/BetterModelPropertyHandler.java
rename to paper/src/main/java/re/imc/geysermodelengine/managers/model/propertyhandler/BetterModelPropertyHandler.java
diff --git a/src/main/java/re/imc/geysermodelengine/managers/model/propertyhandler/ModelEnginePropertyHandler.java b/paper/src/main/java/re/imc/geysermodelengine/managers/model/propertyhandler/ModelEnginePropertyHandler.java
similarity index 99%
rename from src/main/java/re/imc/geysermodelengine/managers/model/propertyhandler/ModelEnginePropertyHandler.java
rename to paper/src/main/java/re/imc/geysermodelengine/managers/model/propertyhandler/ModelEnginePropertyHandler.java
index 688775f..3047253 100644
--- a/src/main/java/re/imc/geysermodelengine/managers/model/propertyhandler/ModelEnginePropertyHandler.java
+++ b/paper/src/main/java/re/imc/geysermodelengine/managers/model/propertyhandler/ModelEnginePropertyHandler.java
@@ -15,7 +15,6 @@ import re.imc.geysermodelengine.util.BooleanPacker;
import java.awt.*;
import java.util.*;
-import java.util.List;
public class ModelEnginePropertyHandler implements PropertyHandler {
diff --git a/src/main/java/re/imc/geysermodelengine/managers/model/propertyhandler/PropertyHandler.java b/paper/src/main/java/re/imc/geysermodelengine/managers/model/propertyhandler/PropertyHandler.java
similarity index 100%
rename from src/main/java/re/imc/geysermodelengine/managers/model/propertyhandler/PropertyHandler.java
rename to paper/src/main/java/re/imc/geysermodelengine/managers/model/propertyhandler/PropertyHandler.java
diff --git a/src/main/java/re/imc/geysermodelengine/managers/model/taskshandler/BetterModelTaskHandler.java b/paper/src/main/java/re/imc/geysermodelengine/managers/model/taskshandler/BetterModelTaskHandler.java
similarity index 100%
rename from src/main/java/re/imc/geysermodelengine/managers/model/taskshandler/BetterModelTaskHandler.java
rename to paper/src/main/java/re/imc/geysermodelengine/managers/model/taskshandler/BetterModelTaskHandler.java
diff --git a/src/main/java/re/imc/geysermodelengine/managers/model/taskshandler/ModelEngineTaskHandler.java b/paper/src/main/java/re/imc/geysermodelengine/managers/model/taskshandler/ModelEngineTaskHandler.java
similarity index 100%
rename from src/main/java/re/imc/geysermodelengine/managers/model/taskshandler/ModelEngineTaskHandler.java
rename to paper/src/main/java/re/imc/geysermodelengine/managers/model/taskshandler/ModelEngineTaskHandler.java
diff --git a/src/main/java/re/imc/geysermodelengine/managers/model/taskshandler/TaskHandler.java b/paper/src/main/java/re/imc/geysermodelengine/managers/model/taskshandler/TaskHandler.java
similarity index 100%
rename from src/main/java/re/imc/geysermodelengine/managers/model/taskshandler/TaskHandler.java
rename to paper/src/main/java/re/imc/geysermodelengine/managers/model/taskshandler/TaskHandler.java
diff --git a/src/main/java/re/imc/geysermodelengine/packet/entity/PacketEntity.java b/paper/src/main/java/re/imc/geysermodelengine/packet/entity/PacketEntity.java
similarity index 94%
rename from src/main/java/re/imc/geysermodelengine/packet/entity/PacketEntity.java
rename to paper/src/main/java/re/imc/geysermodelengine/packet/entity/PacketEntity.java
index db4592e..d9a43ab 100644
--- a/src/main/java/re/imc/geysermodelengine/packet/entity/PacketEntity.java
+++ b/paper/src/main/java/re/imc/geysermodelengine/packet/entity/PacketEntity.java
@@ -3,9 +3,7 @@ package re.imc.geysermodelengine.packet.entity;
import com.github.retrooper.packetevents.PacketEvents;
import com.github.retrooper.packetevents.manager.server.ServerVersion;
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.EntityTypes;
import com.github.retrooper.packetevents.protocol.teleport.RelativeFlag;
import com.github.retrooper.packetevents.util.Vector3d;
import com.github.retrooper.packetevents.wrapper.PacketWrapper;
@@ -16,7 +14,6 @@ import lombok.Setter;
import org.bukkit.Location;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
-import re.imc.geysermodelengine.GeyserModelEngine;
import java.util.Collection;
import java.util.Set;
diff --git a/src/main/java/re/imc/geysermodelengine/runnables/BedrockMountControlRunnable.java b/paper/src/main/java/re/imc/geysermodelengine/runnables/BedrockMountControlRunnable.java
similarity index 95%
rename from src/main/java/re/imc/geysermodelengine/runnables/BedrockMountControlRunnable.java
rename to paper/src/main/java/re/imc/geysermodelengine/runnables/BedrockMountControlRunnable.java
index 745d098..3f55d56 100644
--- a/src/main/java/re/imc/geysermodelengine/runnables/BedrockMountControlRunnable.java
+++ b/paper/src/main/java/re/imc/geysermodelengine/runnables/BedrockMountControlRunnable.java
@@ -5,14 +5,12 @@ import com.ticxo.modelengine.api.entity.BukkitEntity;
import com.ticxo.modelengine.api.model.ActiveModel;
import com.ticxo.modelengine.api.model.bone.type.Mount;
import com.ticxo.modelengine.api.mount.controller.MountController;
-import io.papermc.paper.threadedregions.scheduler.ScheduledTask;
import org.apache.commons.lang3.tuple.Pair;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import re.imc.geysermodelengine.GeyserModelEngine;
import java.util.UUID;
-import java.util.function.Consumer;
public class BedrockMountControlRunnable implements Runnable {
diff --git a/src/main/java/re/imc/geysermodelengine/runnables/UpdateTaskRunnable.java b/paper/src/main/java/re/imc/geysermodelengine/runnables/UpdateTaskRunnable.java
similarity index 92%
rename from src/main/java/re/imc/geysermodelengine/runnables/UpdateTaskRunnable.java
rename to paper/src/main/java/re/imc/geysermodelengine/runnables/UpdateTaskRunnable.java
index 595a3e7..a61e1dc 100644
--- a/src/main/java/re/imc/geysermodelengine/runnables/UpdateTaskRunnable.java
+++ b/paper/src/main/java/re/imc/geysermodelengine/runnables/UpdateTaskRunnable.java
@@ -1,13 +1,11 @@
package re.imc.geysermodelengine.runnables;
-import io.papermc.paper.threadedregions.scheduler.ScheduledTask;
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;
-import java.util.function.Consumer;
public class UpdateTaskRunnable implements Runnable {
diff --git a/src/main/java/re/imc/geysermodelengine/util/BooleanPacker.java b/paper/src/main/java/re/imc/geysermodelengine/util/BooleanPacker.java
similarity index 100%
rename from src/main/java/re/imc/geysermodelengine/util/BooleanPacker.java
rename to paper/src/main/java/re/imc/geysermodelengine/util/BooleanPacker.java
diff --git a/src/main/java/re/imc/geysermodelengine/util/ColourUtils.java b/paper/src/main/java/re/imc/geysermodelengine/util/ColourUtils.java
similarity index 100%
rename from src/main/java/re/imc/geysermodelengine/util/ColourUtils.java
rename to paper/src/main/java/re/imc/geysermodelengine/util/ColourUtils.java
diff --git a/src/main/resources/Lang/messages.yml b/paper/src/main/resources/Lang/messages.yml
similarity index 100%
rename from src/main/resources/Lang/messages.yml
rename to paper/src/main/resources/Lang/messages.yml
diff --git a/src/main/resources/config.yml b/paper/src/main/resources/config.yml
similarity index 100%
rename from src/main/resources/config.yml
rename to paper/src/main/resources/config.yml
diff --git a/src/main/resources/paper-plugin.yml b/paper/src/main/resources/paper-plugin.yml
similarity index 86%
rename from src/main/resources/paper-plugin.yml
rename to paper/src/main/resources/paper-plugin.yml
index d98343d..fa18cbc 100644
--- a/src/main/resources/paper-plugin.yml
+++ b/paper/src/main/resources/paper-plugin.yml
@@ -1,4 +1,4 @@
-main: re.imc.geysermodelengine.GeyserModelEngine
+main: re.imc.geysermodelengine.paper.GeyserModelEngine
name: GeyserModelEngine
version: '1.0.0'
api-version: '1.21'
diff --git a/settings.gradle.kts b/settings.gradle.kts
index 8e9efd7..dd9810f 100644
--- a/settings.gradle.kts
+++ b/settings.gradle.kts
@@ -1,2 +1,5 @@
rootProject.name = "GeyserModelEngine"
+include("paper")
+include("geyser")
+