commit a20c2eb4211420c53ab2b0840a9d2e254a455853
Author: zimzaza4 <3625282098@qq.com>
Date: Sat Apr 20 21:55:57 2024 +0800
Initial commit
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..5ff6309
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,38 @@
+target/
+!.mvn/wrapper/maven-wrapper.jar
+!**/src/main/**/target/
+!**/src/test/**/target/
+
+### IntelliJ IDEA ###
+.idea/modules.xml
+.idea/jarRepositories.xml
+.idea/compiler.xml
+.idea/libraries/
+*.iws
+*.iml
+*.ipr
+
+### Eclipse ###
+.apt_generated
+.classpath
+.factorypath
+.project
+.settings
+.springBeans
+.sts4-cache
+
+### NetBeans ###
+/nbproject/private/
+/nbbuild/
+/dist/
+/nbdist/
+/.nb-gradle/
+build/
+!**/src/main/**/build/
+!**/src/test/**/build/
+
+### VS Code ###
+.vscode/
+
+### Mac OS ###
+.DS_Store
\ No newline at end of file
diff --git a/libs/geyserutils-geyser-1.0-SNAPSHOT.jar b/libs/geyserutils-geyser-1.0-SNAPSHOT.jar
new file mode 100644
index 0000000..e5aeb05
Binary files /dev/null and b/libs/geyserutils-geyser-1.0-SNAPSHOT.jar differ
diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000..dcd0771
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,66 @@
+
+
+ 4.0.0
+
+ re.imc
+ GeyserModelEnginePackGenerator
+ 1.0-SNAPSHOT
+
+
+ 17
+ 17
+ UTF-8
+
+
+
+
+ opencollab-release-repo
+ https://repo.opencollab.dev/maven-releases/
+
+ true
+
+
+ true
+
+
+
+ opencollab-snapshot-repo
+ https://repo.opencollab.dev/maven-snapshots/
+
+ false
+
+
+ true
+
+
+
+
+
+
+ com.google.code.gson
+ gson
+ 2.8.8
+
+
+ org.projectlombok
+ lombok
+ 1.18.28
+ compile
+
+
+ org.geysermc.geyser
+ api
+ 2.2.3-SNAPSHOT
+ provided
+
+
+ me.zimzaza4
+ geyserutils-geyser
+ 1.0.0
+ system
+ ${project.basedir}/libs/geyserutils-geyser-1.0-SNAPSHOT.jar
+
+
+
\ No newline at end of file
diff --git a/src/main/java/re/imc/geysermodelenginepackgenerator/ExtensionMain.java b/src/main/java/re/imc/geysermodelenginepackgenerator/ExtensionMain.java
new file mode 100644
index 0000000..0f27091
--- /dev/null
+++ b/src/main/java/re/imc/geysermodelenginepackgenerator/ExtensionMain.java
@@ -0,0 +1,52 @@
+package re.imc.geysermodelenginepackgenerator;
+
+import me.zimzaza4.geyserutils.geyser.GeyserUtils;
+import org.geysermc.event.subscribe.Subscribe;
+import org.geysermc.geyser.api.event.lifecycle.GeyserLoadResourcePacksEvent;
+import org.geysermc.geyser.api.event.lifecycle.GeyserPreInitializeEvent;
+import org.geysermc.geyser.api.extension.Extension;
+import re.imc.geysermodelenginepackgenerator.util.ZipUtil;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.zip.ZipOutputStream;
+
+public class ExtensionMain implements Extension {
+
+ private File source;
+
+ @Subscribe
+ public void onLoad(GeyserPreInitializeEvent event) {
+ source = dataFolder().resolve("input").toFile();
+ source.mkdirs();
+
+ File[] files = source.listFiles();
+ if (files != null) {
+ for (File file : files) {
+ String id = "modelengine:" + file.getName();
+ GeyserUtils.addCustomEntity(id);
+ }
+ }
+ }
+ @Subscribe
+ public void onPackLoad(GeyserLoadResourcePacksEvent event) {
+
+ File generatedPack = dataFolder().resolve("generated_pack").toFile();
+
+ GeneratorMain.startGenerate(source, generatedPack);
+
+
+ Path generatedPackZip = dataFolder().resolve("generated_pack.zip");
+
+ try (ZipOutputStream zipOutputStream = new ZipOutputStream(Files.newOutputStream(generatedPackZip))) {
+ // 压缩文件夹
+ ZipUtil.compressFolder(generatedPack, generatedPack.getName(), zipOutputStream);
+
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ event.resourcePacks().add(generatedPackZip);
+ }
+}
diff --git a/src/main/java/re/imc/geysermodelenginepackgenerator/GeneratorMain.java b/src/main/java/re/imc/geysermodelenginepackgenerator/GeneratorMain.java
new file mode 100644
index 0000000..ed558c3
--- /dev/null
+++ b/src/main/java/re/imc/geysermodelenginepackgenerator/GeneratorMain.java
@@ -0,0 +1,169 @@
+package re.imc.geysermodelenginepackgenerator;
+
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+import com.google.gson.JsonParser;
+import re.imc.geysermodelenginepackgenerator.generator.*;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.StandardCopyOption;
+import java.util.HashMap;
+import java.util.Map;
+
+public class GeneratorMain {
+ public static final Map entityMap = new HashMap<>();
+ public static final Map animationMap = new HashMap<>();
+ public static final Map geometryMap = new HashMap<>();
+ public static final Map textureMap = new HashMap<>();
+ public static final Gson GSON = new GsonBuilder()
+ .setPrettyPrinting()
+ .create();
+
+
+ public static void main(String[] args) {
+ File source = new File(args.length > 0 ? args[0] : "input");
+
+ File output = new File("output");
+
+ startGenerate(source, output);
+ }
+
+ public static void startGenerate(File source, File output) {
+
+
+ for (File file1 : source.listFiles()) {
+ if (file1.isDirectory()) {
+ if (file1.listFiles() == null) {
+ continue;
+ }
+ String modelId = file1.getName();
+
+ entityMap.put(modelId, new Entity(modelId));
+ for (File e : file1.listFiles()) {
+ if (e.getName().endsWith(".png")) {
+ textureMap.put(modelId, new Texture(modelId, e.toPath()));
+ }
+
+ if (e.getName().endsWith(".json")) {
+ try {
+ String json = Files.readString(e.toPath());
+ if (isAnimationFile(json)) {
+ Animation animation = new Animation();
+ animation.load(json);
+ animation.setModelId(modelId);
+ animationMap.put(modelId, animation);
+ }
+
+ if (isGeometryFile(json)) {
+ System.out.println("G");
+ Geometry geometry = new Geometry();
+ geometry.load(json);
+ geometry.setModelId(modelId);
+ geometryMap.put(modelId, geometry);
+ }
+ } catch (IOException ex) {
+ ex.printStackTrace();
+ }
+ }
+ }
+ }
+ }
+
+ 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");
+
+
+ boolean generateManifest = false;
+ if (!entityFolder.exists()) {
+ generateManifest = true;
+ }
+ File[] files = entityFolder.listFiles();
+ if (files == null || files.length < entityMap.size()) {
+ generateManifest = true;
+ }
+
+ if (generateManifest) {
+ output.mkdirs();
+ Path path = new File(output, "manifest.json").toPath();
+ try {
+ Files.writeString(path,
+ PackManifest.generate(), StandardCharsets.UTF_8);
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+
+ animationsFolder.mkdirs();
+ entityFolder.mkdirs();
+ modelsFolder.mkdirs();
+ texturesFolder.mkdirs();
+
+ for (Map.Entry stringAnimationEntry : animationMap.entrySet()) {
+ stringAnimationEntry.getValue().modify();
+ Geometry geo = geometryMap.get(stringAnimationEntry.getKey());
+ if (geo != null) {
+ stringAnimationEntry.getValue().addHeadBind(geo);
+ }
+ Path path = animationsFolder.toPath().resolve(stringAnimationEntry.getKey() + ".animation.json");
+ try {
+ Files.writeString(path, GSON.toJson(stringAnimationEntry.getValue().getJson()), StandardCharsets.UTF_8);
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+
+ for (Map.Entry stringGeometryEntry : geometryMap.entrySet()) {
+ stringGeometryEntry.getValue().modify();
+ Path path = modelsFolder.toPath().resolve(stringGeometryEntry.getKey() + ".geo.json");
+ try {
+ Files.writeString(path, GSON.toJson(stringGeometryEntry.getValue().getJson()), StandardCharsets.UTF_8);
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+
+ for (Map.Entry stringTextureEntry : textureMap.entrySet()) {
+ Path path = texturesFolder.toPath().resolve(stringTextureEntry.getKey() + ".png");
+ try {
+ Files.copy(stringTextureEntry.getValue().getPath(), path, StandardCopyOption.REPLACE_EXISTING);
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+
+ for (Map.Entry stringEntityEntry : entityMap.entrySet()) {
+ stringEntityEntry.getValue().modify();
+ Path path = entityFolder.toPath().resolve(stringEntityEntry.getKey() + ".entity.json");
+
+ try {
+ Files.writeString(path, stringEntityEntry.getValue().getJson(), StandardCharsets.UTF_8);
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+
+ }
+
+ private static boolean isGeometryFile(String json) {
+ try {
+ return new JsonParser().parse(json).getAsJsonObject().has("minecraft:geometry");
+ } catch (Throwable e) {
+ return false;
+ }
+ }
+
+ private static boolean isAnimationFile(String json) {
+ try {
+ return new JsonParser().parse(json).getAsJsonObject().has("animations");
+ } catch (Throwable e) {
+ return false;
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/src/main/java/re/imc/geysermodelenginepackgenerator/generator/Animation.java b/src/main/java/re/imc/geysermodelenginepackgenerator/generator/Animation.java
new file mode 100644
index 0000000..8e630db
--- /dev/null
+++ b/src/main/java/re/imc/geysermodelenginepackgenerator/generator/Animation.java
@@ -0,0 +1,81 @@
+package re.imc.geysermodelenginepackgenerator.generator;
+
+import com.google.gson.JsonArray;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+import com.google.gson.JsonParser;
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+import re.imc.geysermodelenginepackgenerator.GeneratorMain;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileReader;
+import java.util.Iterator;
+import java.util.Map;
+
+@Getter
+@Setter
+@AllArgsConstructor
+@NoArgsConstructor
+public class Animation {
+
+ public static final String HEAD_TEMPLATE = """
+ {
+ "relative_to" : {
+ "rotation" : "entity"
+ },
+ "rotation" : [ "query.target_x_rotation - this", "query.target_y_rotation - this", 0.0 ]
+ }
+ """;
+
+ String modelId;
+ JsonObject json;
+
+ public void load(String json) {
+ this.json = new JsonParser().parse(json).getAsJsonObject();
+ }
+
+ public void modify() {
+ JsonObject newAnimations = new JsonObject();
+ for (Map.Entry element : json.get("animations").getAsJsonObject().entrySet()) {
+ newAnimations.add("animation." + modelId + "." + element.getKey(), 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, new JsonParser().parse(HEAD_TEMPLATE));
+ }
+ i++;
+ }
+ }
+ if (i == 0) {
+ return;
+ }
+ GeneratorMain.entityMap
+ .get(modelId).setHasHeadAnimation(true);
+ object.add("bones", bones);
+ json.get("animations").getAsJsonObject().add("animation." + modelId + ".look_at_target", object);
+ }
+}
diff --git a/src/main/java/re/imc/geysermodelenginepackgenerator/generator/Entity.java b/src/main/java/re/imc/geysermodelenginepackgenerator/generator/Entity.java
new file mode 100644
index 0000000..855c093
--- /dev/null
+++ b/src/main/java/re/imc/geysermodelenginepackgenerator/generator/Entity.java
@@ -0,0 +1,64 @@
+package re.imc.geysermodelenginepackgenerator.generator;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+
+@Getter
+@Setter
+@AllArgsConstructor
+@NoArgsConstructor
+public class Entity {
+ public static final String TEMPLATE = """
+ {
+ "format_version": "1.10.0",
+ "minecraft:client_entity": {
+ "description": {
+ "identifier": "modelengine:%entity_id%",
+ "materials": {
+ "default": "entity_alphatest"
+ },
+ "textures": {
+ "default": "%texture%"
+ },
+ "geometry": {
+ "default": "%geometry%"
+ },
+ "animations": {
+ "default": "animation.%geometry%.idle",
+ "look_at_target": "%look_at_target%"
+ },
+ "scripts": {
+ "animate": [
+ "default",
+ "look_at_target"
+ ]
+ },
+ "render_controllers": [
+ "controller.render.default"
+ ]
+ }
+ }
+ }
+ """;
+
+
+ String modelId;
+ String json;
+ boolean hasHeadAnimation = false;
+ public Entity(String modelId) {
+ this.modelId = modelId;
+ }
+
+ public void modify() {
+ json = TEMPLATE.replace("%entity_id%", modelId)
+ .replace("%geometry%", "geometry.modelengine_" + modelId)
+ .replace("%texture%", "textures/entity/" + modelId)
+ .replace("%look_at_target%", hasHeadAnimation ? "animation." + modelId + ".look_at_target" : "animation.common.look_at_target")
+ ;
+ }
+
+
+
+}
diff --git a/src/main/java/re/imc/geysermodelenginepackgenerator/generator/Geometry.java b/src/main/java/re/imc/geysermodelenginepackgenerator/generator/Geometry.java
new file mode 100644
index 0000000..6427367
--- /dev/null
+++ b/src/main/java/re/imc/geysermodelenginepackgenerator/generator/Geometry.java
@@ -0,0 +1,52 @@
+package re.imc.geysermodelenginepackgenerator.generator;
+
+import com.google.gson.JsonArray;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+import com.google.gson.JsonParser;
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+
+import java.util.Iterator;
+
+@Getter
+@Setter
+@AllArgsConstructor
+@NoArgsConstructor
+public class Geometry {
+
+ String modelId;
+ JsonObject json;
+ public void load(String json) {
+ this.json = new JsonParser().parse(json).getAsJsonObject();
+ }
+ public void setId(String id) {
+ getInternal().get("description").getAsJsonObject().addProperty("identifier", id);
+ }
+
+ 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();
+ if (name.equals("hitbox") ||
+ name.startsWith("p_") ||
+ name.startsWith("b_") ||
+ name.startsWith("ob_")) {
+ iterator.remove();
+ }
+ }
+ }
+ setId("geometry.modelengine_" + modelId);
+ }
+}
diff --git a/src/main/java/re/imc/geysermodelenginepackgenerator/generator/PackManifest.java b/src/main/java/re/imc/geysermodelenginepackgenerator/generator/PackManifest.java
new file mode 100644
index 0000000..c44fadf
--- /dev/null
+++ b/src/main/java/re/imc/geysermodelenginepackgenerator/generator/PackManifest.java
@@ -0,0 +1,30 @@
+package re.imc.geysermodelenginepackgenerator.generator;
+
+import java.util.UUID;
+
+public class PackManifest {
+ public static final String TEMPLATE = """
+ {
+ "format_version": 1,
+ "header": {
+ "name": "ModelEngine",
+ "description": "ModelEngine For Geyser",
+ "uuid": "%uuid-1%",
+ "version": [0, 0, 1]
+ },
+ "modules": [
+ {
+ "type": "resources",
+ "description": "ModelEngine",
+ "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/src/main/java/re/imc/geysermodelenginepackgenerator/generator/Texture.java b/src/main/java/re/imc/geysermodelenginepackgenerator/generator/Texture.java
new file mode 100644
index 0000000..1ac0828
--- /dev/null
+++ b/src/main/java/re/imc/geysermodelenginepackgenerator/generator/Texture.java
@@ -0,0 +1,16 @@
+package re.imc.geysermodelenginepackgenerator.generator;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import lombok.Setter;
+
+import java.nio.file.Path;
+
+@Getter
+@Setter
+@AllArgsConstructor
+public class Texture {
+
+ String modelId;
+ Path path;
+}
diff --git a/src/main/java/re/imc/geysermodelenginepackgenerator/util/ZipUtil.java b/src/main/java/re/imc/geysermodelenginepackgenerator/util/ZipUtil.java
new file mode 100644
index 0000000..d51d5c1
--- /dev/null
+++ b/src/main/java/re/imc/geysermodelenginepackgenerator/util/ZipUtil.java
@@ -0,0 +1,37 @@
+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()) {
+ compressFolder(file, folderName + "/" + file.getName(), zipOutputStream);
+ } else {
+ 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/src/main/resources/extension.yml b/src/main/resources/extension.yml
new file mode 100644
index 0000000..a3de91a
--- /dev/null
+++ b/src/main/resources/extension.yml
@@ -0,0 +1,6 @@
+name: GeyserModelEnginePackGenerator
+id: geysermodelenginepackgenerator
+main: re.imc.geysermodelenginepackgenerator.ExtensionMain
+api: 1.0.0
+version: 1.0.0
+authors: [zimzaza4]
\ No newline at end of file