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