version 0.1.3

This commit is contained in:
LostInLinearPast 2025-12-06 16:52:28 +08:00
parent 45288f6de8
commit d5adec5aac
8 changed files with 318 additions and 64 deletions

View File

@ -1 +1,43 @@
A lib by LostInLinearPast.
# SnowyCrescentCore
### Project Introduction
#### 1.Capability Tool
- You can register a capability with very little code. And It will auto sync to client.
- **Supports for players and other entities**
#### 2.Player Animator Api
- **The animation has compose now!!! You can dance with other player.**
- You can **register layers** on the server via JSON or Event.
- You can **register animation** on the server via JSON or Event.
- You can **register raw animation** on the client via JSON or Event.
- You can invite other player to participate in certain animations together.
- You can apply to join other player who is playing animation with ride.
- You can request a player to playing an animation.
### How to implementation?
**In repositories:**
```java
repositories {
maven {
name = "Mafuyu404 Maven"
url = "https://maven.sighs.cc/repository/maven-public/"
}
}
```
**In dependencies:**
```java
dependencies {
implementation("com.linearpast:sccore:1.20.1-0.1.3")
}
```

View File

@ -12,7 +12,7 @@ mapping_version=2023.09.03-1.20.1
mod_id=sccore
mod_name=SnowyCrescentCore
mod_license=GNU AGPL 3.0
mod_version=1.20.1-0.1.2.1
mod_version=1.20.1-0.1.3
mod_group_id=com.linearpast
mod_authors=LostInLinearPast
mod_description=A lib about capability and player animator.

View File

@ -103,6 +103,7 @@ public class JsonCommand {
ModLang.TranslatableMessage.ANIMATION_JSON_PATH.getKey(),
path.toString()
));
source.sendSuccess(() -> component, true);
} catch (Exception e) {
source.sendFailure(Component.translatable(
ModLang.TranslatableMessage.COMMAND_RUN_FAIL.getKey()

View File

@ -0,0 +1,162 @@
package com.linearpast.sccore.animation.data.util;
import com.google.gson.*;
import com.linearpast.sccore.SnowyCrescentCore;
import com.linearpast.sccore.animation.data.RawAnimationData;
import com.linearpast.sccore.animation.data.Ride;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.phys.Vec3;
import org.jetbrains.annotations.Nullable;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.nio.file.Files;
import java.nio.file.Path;
public class RawAnimJson {
private static final String Key = "key";
private static final String WithRide = "withRide";
private static final String Offset = "offset";
private static final String XRot = "xRot";
private static final String YRot = "yRot";
private static final String ExistTick = "existTick";
private static final String ComponentsAnimation = "componentsAnimation";
public static class Reader {
private final JsonElement originElement;
Reader(Path jsonFile) throws Exception {
File file = jsonFile.toFile();
if (!file.exists()) {
throw new FileNotFoundException("File does not exist: " + file.getAbsolutePath());
}
this.originElement = JsonParser.parseReader(new FileReader(file));
}
Reader(JsonElement originElement) {
this.originElement = originElement;
}
public static Reader stream(Path path) throws Exception {
return new Reader(path);
}
public static Reader stream(JsonElement jsonElement) {
return new Reader(jsonElement);
}
public RawAnimationData parse() {
return fromJson();
}
public RawAnimationData fromJson() {
try {
JsonObject json = originElement.getAsJsonObject();
RawAnimationData animation = RawAnimationData.create(new ResourceLocation(json.get(Key).getAsString()));
if(json.has(WithRide)){
Ride ride = Ride.create();
JsonObject withRide = json.get(WithRide).getAsJsonObject();
JsonObject offsetJson = withRide.get(Offset).getAsJsonObject();
if(withRide.has(ComponentsAnimation)){
JsonArray elements = withRide.get(ComponentsAnimation).getAsJsonArray();
for (JsonElement element : elements) {
String componentKeyString = element.getAsString();
ResourceLocation componentKey = new ResourceLocation(componentKeyString);
ride.addComponentAnimation(componentKey);
}
}
Vec3 offset = new Vec3(
offsetJson.get("x").getAsDouble(),
offsetJson.get("y").getAsDouble(),
offsetJson.get("z").getAsDouble()
);
ride.withOffset(offset).withExistTick(withRide.get(ExistTick).getAsInt())
.withXRot(withRide.get(XRot).getAsFloat())
.withYRot(withRide.get(YRot).getAsFloat());
animation.withRide(ride);
}
return animation;
} catch (Exception e) {
throw new JsonParseException(e);
}
}
}
public static class Writer {
private static final String example = "example";
private final @Nullable Path file;
private final RawAnimationData animation;
Writer(@Nullable Path file, RawAnimationData animation) {
this.animation = animation;
this.file = file;
}
public static Writer stream(Path path, RawAnimationData animation) {
return new Writer(path, animation);
}
public static Writer stream(RawAnimationData animation) {
return new Writer(null, animation);
}
public static Path syntaxExample(Path directory) throws Exception {
ResourceLocation exampleLocation = new ResourceLocation(SnowyCrescentCore.MODID, Writer.example);
RawAnimationData example = RawAnimationData.create(exampleLocation)
.withRide(Ride.create()
.withOffset(new Vec3(0.0f, 1.0f, 0.0f))
.withExistTick(200)
.withXRot(180)
.withYRot(0)
.addComponentAnimation(exampleLocation)
);
Writer writer = stream(directory, example);
return writer.syntax();
}
public Path syntax() throws Exception {
if(file == null) throw new NullPointerException("file is null");
Path modIdPath = file.resolve(animation.getKey().getNamespace());
Path resultPath = modIdPath.resolve(animation.getKey().getPath() + ".anim.json");
if(resultPath.toFile().exists()) return resultPath;
if(!Files.exists(modIdPath)) Files.createDirectories(modIdPath);
Gson gson = new GsonBuilder().setPrettyPrinting().create();
try (FileWriter writer = new FileWriter(resultPath.toFile())) {
gson.toJson(toJson(), writer);
return resultPath;
}
}
public JsonElement toJson() {
JsonObject json = new JsonObject();
ResourceLocation key = animation.getKey();
json.addProperty(Key, key.toString());
Ride ride = animation.getRide();
if(ride != null) {
JsonObject jsonRide = new JsonObject();
JsonObject jsonOffset = new JsonObject();
Vec3 offset = ride.getOffset();
jsonOffset.addProperty("x", offset.x);
jsonOffset.addProperty("y", offset.y);
jsonOffset.addProperty("z", offset.z);
jsonRide.add(Offset, jsonOffset);
jsonRide.addProperty(XRot, ride.getXRot());
jsonRide.addProperty(YRot, ride.getYRot());
jsonRide.addProperty(ExistTick, ride.getExistTick());
if(!ride.getComponentAnimations().isEmpty()) {
JsonArray jsonComponents = new JsonArray();
ride.getComponentAnimations().forEach(component ->
jsonComponents.add(component.toString())
);
jsonRide.add(ComponentsAnimation, jsonComponents);
}
json.add(WithRide, jsonRide);
}
return json;
}
}
}

View File

@ -14,6 +14,7 @@ import com.linearpast.sccore.animation.mixin.IMixinPlayerAnimationFactoryHolder;
import com.linearpast.sccore.animation.network.toclient.AnimationClientStatusPacket;
import com.linearpast.sccore.animation.network.toclient.AnimationJsonPacket;
import com.linearpast.sccore.animation.service.RawAnimationService;
import com.linearpast.sccore.animation.utils.FileUtils;
import com.linearpast.sccore.core.ModChannel;
import com.linearpast.sccore.utils.ModuleAccess;
import dev.kosmx.playerAnim.api.layered.AnimationStack;
@ -43,20 +44,12 @@ import net.minecraftforge.event.server.ServerStartedEvent;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.util.*;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
public class AnimationRegistry {
private static final Map<ResourceLocation, GenericAnimationData> animations = new HashMap<>();
@ -92,27 +85,27 @@ public class AnimationRegistry {
} catch (IOException e) { return; }
}
safeUnzip(dataPackPath.resolve("animation.zip").toString(), animationPath.toAbsolutePath().toString());
Set<Path> animZipPaths = getAllFile(
FileUtils.safeUnzip(dataPackPath.resolve("animation.zip").toString(), animationPath.toAbsolutePath().toString());
Set<Path> animZipPaths = FileUtils.getAllFile(
dataPackPath.resolve("animation"),
path -> path.toString().endsWith(".anim.zip")
);
Set<Path> layerZipPaths = getAllFile(
Set<Path> layerZipPaths = FileUtils.getAllFile(
dataPackPath.resolve("animation"),
path -> path.toString().endsWith(".layer.zip")
);
for (Path zipPath : animZipPaths) {
safeUnzip(zipPath.toString(), animationPath.toAbsolutePath().toString());
FileUtils.safeUnzip(zipPath.toString(), animationPath.toAbsolutePath().toString());
}
for (Path zipPath : layerZipPaths) {
safeUnzip(zipPath.toString(), animationPath.toAbsolutePath().toString());
FileUtils.safeUnzip(zipPath.toString(), animationPath.toAbsolutePath().toString());
}
Set<Path> animPaths = getAllFile(
Set<Path> animPaths = FileUtils.getAllFile(
dataPackPath.resolve("animation"),
path -> path.toString().endsWith(".anim.json")
);
Set<Path> layerPaths = getAllFile(
Set<Path> layerPaths = FileUtils.getAllFile(
dataPackPath.resolve("animation"),
path -> path.getFileName().toString().equals("animation.layer.json")
);
@ -180,44 +173,6 @@ public class AnimationRegistry {
}
private static Set<Path> getAllFile(Path directory, Predicate<Path> filter) {
try (Stream<Path> walk = Files.walk(directory)) {
return walk.filter(Files::isRegularFile)
.filter(filter)
.collect(Collectors.toSet());
} catch (Exception ignored) {
return Collections.emptySet();
}
}
private static void safeUnzip(String zipFile, String destDir) {
Path destPath = Paths.get(destDir).toAbsolutePath();
try (ZipFile zip = new ZipFile(zipFile)) {
Files.createDirectories(destPath);
Enumeration<? extends ZipEntry> entries = zip.entries();
while (entries.hasMoreElements()) {
ZipEntry entry = entries.nextElement();
Path entryPath = destPath.resolve(entry.getName()).normalize();
if (entry.isDirectory()) {
Files.createDirectories(entryPath);
} else {
Files.createDirectories(entryPath.getParent());
try (InputStream in = zip.getInputStream(entry);
OutputStream out = Files.newOutputStream(entryPath, StandardOpenOption.CREATE)) {
byte[] buffer = new byte[8192];
int bytesRead;
while ((bytesRead = in.read(buffer)) != -1) {
out.write(buffer, 0, bytesRead);
}
}
}
}
} catch (Exception ignored) {}
}
@OnlyIn(Dist.CLIENT)
public static class ClientCache {

View File

@ -3,14 +3,21 @@ package com.linearpast.sccore.animation.register;
import com.linearpast.sccore.SnowyCrescentCore;
import com.linearpast.sccore.animation.command.argument.AnimationArgument;
import com.linearpast.sccore.animation.data.RawAnimationData;
import com.linearpast.sccore.animation.data.util.RawAnimJson;
import com.linearpast.sccore.animation.event.create.AnimationRegisterEvent;
import com.linearpast.sccore.animation.utils.FileUtils;
import net.minecraft.client.Minecraft;
import net.minecraft.resources.ResourceLocation;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
import net.minecraftforge.common.MinecraftForge;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
@OnlyIn(Dist.CLIENT)
public class RawAnimationRegistry {
@ -41,7 +48,38 @@ public class RawAnimationRegistry {
resetAnimations();
AnimationRegisterEvent.RawAnimation event = new AnimationRegisterEvent.RawAnimation();
MinecraftForge.EVENT_BUS.post(event);
registerAnimations(event.getAnimations());
Map<ResourceLocation, RawAnimationData> animationDataMap = new HashMap<>(event.getAnimations());
Minecraft instance = Minecraft.getInstance();
Path dataPackPath = instance.getResourcePackDirectory();
Path animationPath = dataPackPath.resolve("animation");
if (!Files.exists(animationPath)) {
try {
Files.createDirectories(animationPath);
} catch (IOException e) { return; }
}
FileUtils.safeUnzip(dataPackPath.resolve("animation.zip").toString(), animationPath.toAbsolutePath().toString());
Set<Path> animZipPaths = FileUtils.getAllFile(
dataPackPath.resolve("animation"),
path -> path.toString().endsWith(".anim.zip")
);
for (Path zipPath : animZipPaths) {
FileUtils.safeUnzip(zipPath.toString(), animationPath.toAbsolutePath().toString());
}
Set<Path> animPaths = FileUtils.getAllFile(
dataPackPath.resolve("animation"),
path -> path.toString().endsWith(".anim.json")
);
for (Path path : animPaths) {
try {
RawAnimJson.Reader reader = RawAnimJson.Reader.stream(path);
RawAnimationData anim = reader.parse();
animationDataMap.put(anim.getKey(), anim);
} catch (Exception ignored) {
SnowyCrescentCore.log.error("Failed to parse raw animation JSON: {}", path.toString());
}
}
registerAnimations(animationDataMap);
}
private static void resetAnimations() {

View File

@ -0,0 +1,56 @@
package com.linearpast.sccore.animation.utils;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.util.Collections;
import java.util.Enumeration;
import java.util.Set;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
public class FileUtils {
public static Set<Path> getAllFile(Path directory, Predicate<Path> filter) {
try (Stream<Path> walk = Files.walk(directory)) {
return walk.filter(Files::isRegularFile)
.filter(filter)
.collect(Collectors.toSet());
} catch (Exception ignored) {
return Collections.emptySet();
}
}
public static void safeUnzip(String zipFile, String destDir) {
Path destPath = Paths.get(destDir).toAbsolutePath();
try (ZipFile zip = new ZipFile(zipFile)) {
Files.createDirectories(destPath);
Enumeration<? extends ZipEntry> entries = zip.entries();
while (entries.hasMoreElements()) {
ZipEntry entry = entries.nextElement();
Path entryPath = destPath.resolve(entry.getName()).normalize();
if (entry.isDirectory()) {
Files.createDirectories(entryPath);
} else {
Files.createDirectories(entryPath.getParent());
try (InputStream in = zip.getInputStream(entry);
OutputStream out = Files.newOutputStream(entryPath, StandardOpenOption.CREATE)) {
byte[] buffer = new byte[8192];
int bytesRead;
while ((bytesRead = in.read(buffer)) != -1) {
out.write(buffer, 0, bytesRead);
}
}
}
}
} catch (Exception ignored) {}
}
}

View File

@ -47,12 +47,12 @@ public class ModAnimation {
*/
public static void onAnimationRegister(AnimationRegisterEvent.Animation event) {
//You must define corresponding Animation to invite
GenericAnimationData amLTRL = GenericAnimationData.create(AmLyingToRightLying)
.withLyingType(GenericAnimationData.LyingType.RIGHT)
.withName("Lying-to-Right-Lying");
GenericAnimationData amSTL = GenericAnimationData.create(AmStandToLying)
.withName("Stand-to-Lying")
.withLyingType(GenericAnimationData.LyingType.FRONT);
// GenericAnimationData amLTRL = GenericAnimationData.create(AmLyingToRightLying)
// .withLyingType(GenericAnimationData.LyingType.RIGHT)
// .withName("Lying-to-Right-Lying");
// GenericAnimationData amSTL = GenericAnimationData.create(AmStandToLying)
// .withName("Stand-to-Lying")
// .withLyingType(GenericAnimationData.LyingType.FRONT);
GenericAnimationData waltzGentleman = GenericAnimationData.create(WaltzGentleman)
.withName("Waltz-Gentleman")
.withRide(Ride.create().addComponentAnimation(WaltzLady));
@ -62,8 +62,8 @@ public class ModAnimation {
.withRide(Ride.create().addComponentAnimation(WaltzGentleman));
//You can use it to invite an Animation
event.registerAnimation(AmLyingToRightLying, amLTRL);
event.registerAnimation(AmStandToLying, amSTL);
// event.registerAnimation(AmLyingToRightLying, amLTRL);
// event.registerAnimation(AmStandToLying, amSTL);
event.registerAnimation(WaltzGentleman, waltzGentleman);
event.registerAnimation(WaltzLady, waltzLady);
}