diff --git a/README.md b/README.md index 509498f..ae39c01 100644 --- a/README.md +++ b/README.md @@ -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") +} +``` + + diff --git a/gradle.properties b/gradle.properties index a255263..fb0ce7b 100644 --- a/gradle.properties +++ b/gradle.properties @@ -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. diff --git a/src/main/java/com/linearpast/sccore/animation/command/JsonCommand.java b/src/main/java/com/linearpast/sccore/animation/command/JsonCommand.java index ed786a8..57efea7 100644 --- a/src/main/java/com/linearpast/sccore/animation/command/JsonCommand.java +++ b/src/main/java/com/linearpast/sccore/animation/command/JsonCommand.java @@ -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() diff --git a/src/main/java/com/linearpast/sccore/animation/data/util/RawAnimJson.java b/src/main/java/com/linearpast/sccore/animation/data/util/RawAnimJson.java new file mode 100644 index 0000000..2c5d973 --- /dev/null +++ b/src/main/java/com/linearpast/sccore/animation/data/util/RawAnimJson.java @@ -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; + } + + } +} diff --git a/src/main/java/com/linearpast/sccore/animation/register/AnimationRegistry.java b/src/main/java/com/linearpast/sccore/animation/register/AnimationRegistry.java index eb3b6b4..f07b3af 100644 --- a/src/main/java/com/linearpast/sccore/animation/register/AnimationRegistry.java +++ b/src/main/java/com/linearpast/sccore/animation/register/AnimationRegistry.java @@ -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 animations = new HashMap<>(); @@ -92,27 +85,27 @@ public class AnimationRegistry { } catch (IOException e) { return; } } - safeUnzip(dataPackPath.resolve("animation.zip").toString(), animationPath.toAbsolutePath().toString()); - Set animZipPaths = getAllFile( + FileUtils.safeUnzip(dataPackPath.resolve("animation.zip").toString(), animationPath.toAbsolutePath().toString()); + Set animZipPaths = FileUtils.getAllFile( dataPackPath.resolve("animation"), path -> path.toString().endsWith(".anim.zip") ); - Set layerZipPaths = getAllFile( + Set 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 animPaths = getAllFile( + Set animPaths = FileUtils.getAllFile( dataPackPath.resolve("animation"), path -> path.toString().endsWith(".anim.json") ); - Set layerPaths = getAllFile( + Set layerPaths = FileUtils.getAllFile( dataPackPath.resolve("animation"), path -> path.getFileName().toString().equals("animation.layer.json") ); @@ -180,44 +173,6 @@ public class AnimationRegistry { } - private static Set getAllFile(Path directory, Predicate filter) { - try (Stream 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 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 { diff --git a/src/main/java/com/linearpast/sccore/animation/register/RawAnimationRegistry.java b/src/main/java/com/linearpast/sccore/animation/register/RawAnimationRegistry.java index 6204d73..4ebbe0f 100644 --- a/src/main/java/com/linearpast/sccore/animation/register/RawAnimationRegistry.java +++ b/src/main/java/com/linearpast/sccore/animation/register/RawAnimationRegistry.java @@ -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 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 animZipPaths = FileUtils.getAllFile( + dataPackPath.resolve("animation"), + path -> path.toString().endsWith(".anim.zip") + ); + for (Path zipPath : animZipPaths) { + FileUtils.safeUnzip(zipPath.toString(), animationPath.toAbsolutePath().toString()); + } + Set 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() { diff --git a/src/main/java/com/linearpast/sccore/animation/utils/FileUtils.java b/src/main/java/com/linearpast/sccore/animation/utils/FileUtils.java new file mode 100644 index 0000000..8cc60d9 --- /dev/null +++ b/src/main/java/com/linearpast/sccore/animation/utils/FileUtils.java @@ -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 getAllFile(Path directory, Predicate filter) { + try (Stream 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 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) {} + } +} diff --git a/src/main/java/com/linearpast/sccore/example/animation/ModAnimation.java b/src/main/java/com/linearpast/sccore/example/animation/ModAnimation.java index 7df722f..0ab85e5 100644 --- a/src/main/java/com/linearpast/sccore/example/animation/ModAnimation.java +++ b/src/main/java/com/linearpast/sccore/example/animation/ModAnimation.java @@ -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); }