Initial commit

This commit is contained in:
zimzaza4
2024-04-20 21:55:57 +08:00
commit a20c2eb421
12 changed files with 611 additions and 0 deletions

38
.gitignore vendored Normal file
View File

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

Binary file not shown.

66
pom.xml Normal file
View File

@@ -0,0 +1,66 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>re.imc</groupId>
<artifactId>GeyserModelEnginePackGenerator</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<repositories>
<repository>
<id>opencollab-release-repo</id>
<url>https://repo.opencollab.dev/maven-releases/</url>
<releases>
<enabled>true</enabled>
</releases>
<snapshots>
<enabled>true</enabled>
</snapshots>
</repository>
<repository>
<id>opencollab-snapshot-repo</id>
<url>https://repo.opencollab.dev/maven-snapshots/</url>
<releases>
<enabled>false</enabled>
</releases>
<snapshots>
<enabled>true</enabled>
</snapshots>
</repository>
</repositories>
<dependencies>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.8.8</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.28</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.geysermc.geyser</groupId>
<artifactId>api</artifactId>
<version>2.2.3-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>me.zimzaza4</groupId>
<artifactId>geyserutils-geyser</artifactId>
<version>1.0.0</version>
<scope>system</scope>
<systemPath>${project.basedir}/libs/geyserutils-geyser-1.0-SNAPSHOT.jar</systemPath>
</dependency>
</dependencies>
</project>

View File

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

View File

@@ -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<String, Entity> entityMap = new HashMap<>();
public static final Map<String, Animation> animationMap = new HashMap<>();
public static final Map<String, Geometry> geometryMap = new HashMap<>();
public static final Map<String, Texture> 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<String, Animation> 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<String, Geometry> 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<String, Texture> 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<String, Entity> 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;
}
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,6 @@
name: GeyserModelEnginePackGenerator
id: geysermodelenginepackgenerator
main: re.imc.geysermodelenginepackgenerator.ExtensionMain
api: 1.0.0
version: 1.0.0
authors: [zimzaza4]