完善了ssc数据驱动加载的逻辑
放入data/<mod_id>/ssc_animations(避免与其它动画模组路径冲突) 将示例中的硬编码注册改为从数据json中读取
This commit is contained in:
parent
667a786c77
commit
ab8373d62e
|
|
@ -0,0 +1,2 @@
|
|||
// 1.20.1 2025-11-28T18:32:27.7132485 Languages: zh_cn
|
||||
33a56369fb517ec3b528128539e1e160666d9602 assets/sccore/lang/zh_cn.json
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
// 1.20.1 2025-11-28T18:32:27.715234 Languages: en_us
|
||||
941682565bf7998e144253f88be175d198e43566 assets/sccore/lang/en_us.json
|
||||
1
out/production/resources/META-INF/accesstransformer.cfg
Normal file
1
out/production/resources/META-INF/accesstransformer.cfg
Normal file
|
|
@ -0,0 +1 @@
|
|||
public net.minecraft.client.Camera f_90552_ # position
|
||||
42
out/production/resources/META-INF/mods.toml
Normal file
42
out/production/resources/META-INF/mods.toml
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
modLoader = "javafml"
|
||||
loaderVersion = "${loader_version_range}"
|
||||
license = "${mod_license}"
|
||||
issueTrackerURL="${mod_url}"
|
||||
|
||||
[[mods]]
|
||||
modId = "${mod_id}"
|
||||
version = "${mod_version}"
|
||||
displayName = "${mod_name}"
|
||||
displayURL="${mod_url}"
|
||||
logoFile="logo.png"
|
||||
credits="${mod_credits}"
|
||||
authors = "${mod_authors}"
|
||||
description = '''${mod_description}'''
|
||||
|
||||
[[dependencies."${mod_id}"]]
|
||||
modId = "forge"
|
||||
mandatory = true
|
||||
versionRange = "${forge_version_range}"
|
||||
ordering = "NONE"
|
||||
side = "BOTH"
|
||||
|
||||
[[dependencies."${mod_id}"]]
|
||||
modId = "minecraft"
|
||||
mandatory = true
|
||||
versionRange = "${minecraft_version_range}"
|
||||
ordering = "NONE"
|
||||
side = "BOTH"
|
||||
|
||||
[[dependencies."${mod_id}"]]
|
||||
modId = "playeranimator"
|
||||
mandatory = false
|
||||
versionRange = "[1.0.1,)"
|
||||
ordering = "AFTER"
|
||||
side = "BOTH"
|
||||
|
||||
[[dependencies."${mod_id}"]]
|
||||
modId = "bendylib"
|
||||
mandatory = false
|
||||
versionRange = "[4.0.0,)"
|
||||
ordering = "AFTER"
|
||||
side = "BOTH"
|
||||
32
out/production/resources/assets/sccore/lang/en_us.json
Normal file
32
out/production/resources/assets/sccore/lang/en_us.json
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
{
|
||||
"translation.sccore.command.animation.accept_apply_success": "%s has accepted the application of %s.",
|
||||
"translation.sccore.command.animation.accept_invite_success": "Invitation accepted.",
|
||||
"translation.sccore.command.animation.accept_message_click": "Click here to accept.",
|
||||
"translation.sccore.command.animation.accept_request_success": "Request accepted.",
|
||||
"translation.sccore.command.animation.animation_cooldown": "You cannot perform this operation: Cooling down (%s second(s)).",
|
||||
"translation.sccore.command.animation.animation_expire": "You cannot perform this operation: It has expired.",
|
||||
"translation.sccore.command.animation.animation_json_path": "%s",
|
||||
"translation.sccore.command.animation.animation_operation_cancelled": "Exception: Operation cancelled.",
|
||||
"translation.sccore.command.animation.animation_operation_unsupported": "Error: Unsupported operation.",
|
||||
"translation.sccore.command.animation.animation_out_range": "You cannot perform this operation: The distance is not within %s blocks.",
|
||||
"translation.sccore.command.animation.animation_resource_not_found": "Error: Resource not found, please check if there are any errors in the resource or operation.",
|
||||
"translation.sccore.command.animation.animation_to_json": "The animation %s has been stored in the path on %s:",
|
||||
"translation.sccore.command.animation.applied_join_message": "%S§b§l Apply for §r to join your animation. ",
|
||||
"translation.sccore.command.animation.apply_join_message": "Application sent.",
|
||||
"translation.sccore.command.animation.apply_success": "%s has accepted your animation application.",
|
||||
"translation.sccore.command.animation.clear_animations": "Animation cleared.",
|
||||
"translation.sccore.command.animation.command_run_fail": "Command run fail.",
|
||||
"translation.sccore.command.animation.command_run_success": "Command run success.",
|
||||
"translation.sccore.command.animation.invite_message": "Invitation sent.",
|
||||
"translation.sccore.command.animation.invite_success": "%s has accepted your animation invitation.",
|
||||
"translation.sccore.command.animation.invited_message": "%s§c§l invites§r you to animation: %s. ",
|
||||
"translation.sccore.command.animation.list_animation_resource": "The %2$s on %1$s has : %s",
|
||||
"translation.sccore.command.animation.play_animation_fail": "Fail to play animation with: %s",
|
||||
"translation.sccore.command.animation.play_animation_success": "Successfully played animation on %s player(s).",
|
||||
"translation.sccore.command.animation.refresh_animations": "Animation refreshed.",
|
||||
"translation.sccore.command.animation.remove_animation_fail": "Fail to remove animation with: %s",
|
||||
"translation.sccore.command.animation.remove_animation_success": "Successfully removed animation on %s player(s).",
|
||||
"translation.sccore.command.animation.request_message": "Request sent.",
|
||||
"translation.sccore.command.animation.request_success": "%s has accepted your animation request.",
|
||||
"translation.sccore.command.animation.requested_message": "%s§d§l requests§r you to animation: %s. "
|
||||
}
|
||||
32
out/production/resources/assets/sccore/lang/zh_cn.json
Normal file
32
out/production/resources/assets/sccore/lang/zh_cn.json
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
{
|
||||
"translation.sccore.command.animation.accept_apply_success": "%s 接受了 %s 的申请。",
|
||||
"translation.sccore.command.animation.accept_invite_success": "已接受邀请。",
|
||||
"translation.sccore.command.animation.accept_message_click": "单击此处同意。",
|
||||
"translation.sccore.command.animation.accept_request_success": "已接受请求。",
|
||||
"translation.sccore.command.animation.animation_cooldown": "你不能执行该操作: 冷却中(%s秒)。",
|
||||
"translation.sccore.command.animation.animation_expire": "你不能执行该操作: 已过期。",
|
||||
"translation.sccore.command.animation.animation_json_path": "%s",
|
||||
"translation.sccore.command.animation.animation_operation_cancelled": "异常: 操作被取消。",
|
||||
"translation.sccore.command.animation.animation_operation_unsupported": "错误: 不支持这样做。",
|
||||
"translation.sccore.command.animation.animation_out_range": "你不能执行该操作: 距离不在%s格以内。",
|
||||
"translation.sccore.command.animation.animation_resource_not_found": "错误: 资源未找到,请检查资源或操作是否有误。",
|
||||
"translation.sccore.command.animation.animation_to_json": "动画%s已经存储到%s路径:",
|
||||
"translation.sccore.command.animation.applied_join_message": "%s§b§l 申请§r加入动画。",
|
||||
"translation.sccore.command.animation.apply_join_message": "已发送申请。",
|
||||
"translation.sccore.command.animation.apply_success": "%s 接受了你的动画申请。",
|
||||
"translation.sccore.command.animation.clear_animations": "动画已清除。",
|
||||
"translation.sccore.command.animation.command_run_fail": "命令执行失败。",
|
||||
"translation.sccore.command.animation.command_run_success": "命令执行成功。",
|
||||
"translation.sccore.command.animation.invite_message": "已发送邀请。",
|
||||
"translation.sccore.command.animation.invite_success": "%s 接受了你的动画邀请。",
|
||||
"translation.sccore.command.animation.invited_message": "%s§c§l 邀请§r你进行动画:%s。",
|
||||
"translation.sccore.command.animation.list_animation_resource": "%s侧的%s有:%s",
|
||||
"translation.sccore.command.animation.play_animation_fail": "在这些玩家上播放动画失败:%s",
|
||||
"translation.sccore.command.animation.play_animation_success": "在%s个玩家上播放动画成功。",
|
||||
"translation.sccore.command.animation.refresh_animations": "动画同步状态已刷新。",
|
||||
"translation.sccore.command.animation.remove_animation_fail": "在这些玩家上移除动画失败:%s",
|
||||
"translation.sccore.command.animation.remove_animation_success": "在%s个玩家上移除动画成功。",
|
||||
"translation.sccore.command.animation.request_message": "已发送请求。",
|
||||
"translation.sccore.command.animation.request_success": "%s 接受了你的动画请求。",
|
||||
"translation.sccore.command.animation.requested_message": "%s§d§l 请求§r你进行动画:%s。"
|
||||
}
|
||||
|
|
@ -0,0 +1,112 @@
|
|||
{
|
||||
"name": "am_lying_to_right_lying",
|
||||
"author": "LostInLinearPast",
|
||||
"description": "fix in 1.20.1 from CreatorGalaxy",
|
||||
"emote":{
|
||||
"isLoop": "false",
|
||||
"returnTick": 2,
|
||||
"beginTick":0,
|
||||
"endTick":6,
|
||||
"stopTick":2147483647,
|
||||
"degrees":false,
|
||||
"moves":[
|
||||
|
||||
{
|
||||
"tick":1,
|
||||
"easing": "EASEINOUTQUAD",
|
||||
"turn": 0,
|
||||
"torso":{
|
||||
"yaw":0.0
|
||||
}
|
||||
},
|
||||
{
|
||||
"tick":6,
|
||||
"easing": "EASEINOUTQUAD",
|
||||
"turn": 0,
|
||||
"torso":{
|
||||
"yaw":-0.08066412806510925
|
||||
}
|
||||
},
|
||||
{
|
||||
"tick":1,
|
||||
"easing": "EASEINOUTQUAD",
|
||||
"turn": 0,
|
||||
"torso":{
|
||||
"roll":-0.0
|
||||
}
|
||||
},
|
||||
{
|
||||
"tick":6,
|
||||
"easing": "EASEINOUTQUAD",
|
||||
"turn": 0,
|
||||
"torso":{
|
||||
"roll":-1.568853497505188
|
||||
}
|
||||
},
|
||||
{
|
||||
"tick":1,
|
||||
"easing": "EASEINOUTQUAD",
|
||||
"turn": 0,
|
||||
"torso":{
|
||||
"pitch":1.5707963705062866
|
||||
}
|
||||
},
|
||||
{
|
||||
"tick":6,
|
||||
"easing": "EASEINOUTQUAD",
|
||||
"turn": 0,
|
||||
"torso":{
|
||||
"pitch":1.5704461336135864
|
||||
}
|
||||
},
|
||||
{
|
||||
"tick":1,
|
||||
"easing": "EASEINOUTQUAD",
|
||||
"turn": 0,
|
||||
"torso":{
|
||||
"y":-0.623153030872345
|
||||
}
|
||||
},
|
||||
{
|
||||
"tick":6,
|
||||
"easing": "EASEINOUTQUAD",
|
||||
"turn": 0,
|
||||
"torso":{
|
||||
"y":-0.4366978108882904
|
||||
}
|
||||
},
|
||||
{
|
||||
"tick":1,
|
||||
"easing": "EASEINOUTQUAD",
|
||||
"turn": 0,
|
||||
"torso":{
|
||||
"x":0.0
|
||||
}
|
||||
},
|
||||
{
|
||||
"tick":6,
|
||||
"easing": "EASEINOUTQUAD",
|
||||
"turn": 0,
|
||||
"torso":{
|
||||
"x":0.0
|
||||
}
|
||||
},
|
||||
{
|
||||
"tick":1,
|
||||
"easing": "EASEINOUTQUAD",
|
||||
"turn": 0,
|
||||
"torso":{
|
||||
"z":-0.0
|
||||
}
|
||||
},
|
||||
{
|
||||
"tick":6,
|
||||
"easing": "EASEINOUTQUAD",
|
||||
"turn": 0,
|
||||
"torso":{
|
||||
"z":-0.0
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,64 @@
|
|||
{
|
||||
"name": "am_stand_to_lying",
|
||||
"author": "LostInLinearPast",
|
||||
"description": "fix in 1.20.1 from CreatorGalaxy",
|
||||
"emote":{
|
||||
"isLoop": "false",
|
||||
"returnTick": 2,
|
||||
"beginTick":0,
|
||||
"endTick":5,
|
||||
"stopTick":2147483647,
|
||||
"degrees":false,
|
||||
"moves":[
|
||||
|
||||
{
|
||||
"tick":5,
|
||||
"easing": "EASEINOUTQUAD",
|
||||
"turn": 0,
|
||||
"torso":{
|
||||
"yaw":0.0
|
||||
}
|
||||
},
|
||||
{
|
||||
"tick":5,
|
||||
"easing": "EASEINOUTQUAD",
|
||||
"turn": 0,
|
||||
"torso":{
|
||||
"roll":-0.0
|
||||
}
|
||||
},
|
||||
{
|
||||
"tick":5,
|
||||
"easing": "EASEINOUTQUAD",
|
||||
"turn": 0,
|
||||
"torso":{
|
||||
"pitch":1.5707963705062866
|
||||
}
|
||||
},
|
||||
{
|
||||
"tick":5,
|
||||
"easing": "EASEINOUTQUAD",
|
||||
"turn": 0,
|
||||
"torso":{
|
||||
"y":-0.623153030872345
|
||||
}
|
||||
},
|
||||
{
|
||||
"tick":5,
|
||||
"easing": "EASEINOUTQUAD",
|
||||
"turn": 0,
|
||||
"torso":{
|
||||
"x":0.0
|
||||
}
|
||||
},
|
||||
{
|
||||
"tick":5,
|
||||
"easing": "EASEINOUTQUAD",
|
||||
"turn": 0,
|
||||
"torso":{
|
||||
"z":-0.0
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
BIN
out/production/resources/logo.png
Normal file
BIN
out/production/resources/logo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 5.6 KiB |
6
out/production/resources/pack.mcmeta
Normal file
6
out/production/resources/pack.mcmeta
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"pack": {
|
||||
"description": "SnowyCrescentCore resources",
|
||||
"pack_format": 15
|
||||
}
|
||||
}
|
||||
20
out/production/resources/sccore.mixins.json
Normal file
20
out/production/resources/sccore.mixins.json
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
{
|
||||
"required": true,
|
||||
"minVersion": "0.8",
|
||||
"package": "com.linearpast.sccore.mixin",
|
||||
"compatibilityLevel": "JAVA_8",
|
||||
"refmap": "sccore.refmap.json",
|
||||
"plugin": "com.linearpast.sccore.mixin.SCCoreMixinPlugin",
|
||||
"mixins": [
|
||||
"animation.MixinEntity",
|
||||
"animation.client.MixinPlayerAnimationFactoryHolder"
|
||||
],
|
||||
"client": [
|
||||
"animation.client.MixinEntity",
|
||||
"animation.client.MixinHumanoidModel",
|
||||
"animation.client.MixinKeyframeAnimationPlayer"
|
||||
],
|
||||
"injectors": {
|
||||
"defaultRequire": 1
|
||||
}
|
||||
}
|
||||
|
|
@ -1,2 +1,2 @@
|
|||
// 1.20.1 2025-11-28T18:32:27.7132485 Languages: zh_cn
|
||||
33a56369fb517ec3b528128539e1e160666d9602 assets/sccore/lang/zh_cn.json
|
||||
// 1.20.1 2026-01-12T12:57:53.3841242 Languages: zh_cn
|
||||
5a0677ff9ef6fd59ce316b70b9e796c12aa2ca3a assets/sccore/lang/zh_cn.json
|
||||
|
|
|
|||
|
|
@ -0,0 +1,3 @@
|
|||
// 1.20.1 2026-01-12T13:25:00.4308564 Animations: sccore
|
||||
784975521a546e0773d97b6cd53e6b38e45e6241 data/sccore/scc_animations/waltz_gentleman.anim.json
|
||||
8f8fb0ecd48254b9fddacec6decffe8f9de56e23 data/sccore/scc_animations/waltz_lady.anim.json
|
||||
|
|
@ -1,2 +1,2 @@
|
|||
// 1.20.1 2025-11-28T18:32:27.715234 Languages: en_us
|
||||
941682565bf7998e144253f88be175d198e43566 assets/sccore/lang/en_us.json
|
||||
// 1.20.1 2026-01-12T12:57:53.3876407 Languages: en_us
|
||||
cb12ddd7129449cbee2282fc61d9d50dc514d62a assets/sccore/lang/en_us.json
|
||||
|
|
|
|||
|
|
@ -0,0 +1,2 @@
|
|||
// 1.20.1 2026-01-12T13:25:00.4276551 Animation Layer Data: sccore
|
||||
183c6a30f08de7f1a086b30b922164ba302e7ad2 data/sccore/scc_animations/animation.layer.json
|
||||
|
|
@ -9,7 +9,7 @@
|
|||
"translation.sccore.command.animation.animation_operation_cancelled": "Exception: Operation cancelled.",
|
||||
"translation.sccore.command.animation.animation_operation_unsupported": "Error: Unsupported operation.",
|
||||
"translation.sccore.command.animation.animation_out_range": "You cannot perform this operation: The distance is not within %s blocks.",
|
||||
"translation.sccore.command.animation.animation_resource_not_found": "Error: Resource not found, please check if there are any errors in the resource or operation.",
|
||||
"translation.sccore.command.animation.animation_resource_not_found": "Error: Resource not found, please check if there are any errors in the resource or operation : %s",
|
||||
"translation.sccore.command.animation.animation_to_json": "The animation %s has been stored in the path on %s:",
|
||||
"translation.sccore.command.animation.applied_join_message": "%S§b§l Apply for §r to join your animation. ",
|
||||
"translation.sccore.command.animation.apply_join_message": "Application sent.",
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@
|
|||
"translation.sccore.command.animation.animation_operation_cancelled": "异常: 操作被取消。",
|
||||
"translation.sccore.command.animation.animation_operation_unsupported": "错误: 不支持这样做。",
|
||||
"translation.sccore.command.animation.animation_out_range": "你不能执行该操作: 距离不在%s格以内。",
|
||||
"translation.sccore.command.animation.animation_resource_not_found": "错误: 资源未找到,请检查资源或操作是否有误。",
|
||||
"translation.sccore.command.animation.animation_resource_not_found": "错误: 资源未找到,请检查资源或操作是否有误: %s",
|
||||
"translation.sccore.command.animation.animation_to_json": "动画%s已经存储到%s路径:",
|
||||
"translation.sccore.command.animation.applied_join_message": "%s§b§l 申请§r加入动画。",
|
||||
"translation.sccore.command.animation.apply_join_message": "已发送申请。",
|
||||
|
|
|
|||
|
|
@ -0,0 +1,6 @@
|
|||
[
|
||||
{
|
||||
"key": "sccore:normal_layers",
|
||||
"priority": 42
|
||||
}
|
||||
]
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
{
|
||||
"camPosOffset": {
|
||||
"relative": true,
|
||||
"x": 0.0,
|
||||
"y": 0.0,
|
||||
"z": 1.0
|
||||
},
|
||||
"heightModifier": 1.0,
|
||||
"key": "sccore:waltz_gentleman",
|
||||
"name": "Waltz-Gentleman",
|
||||
"priority": 0,
|
||||
"withRide": {
|
||||
"componentsAnimation": [
|
||||
"sccore:waltz_lady"
|
||||
],
|
||||
"existTick": 0,
|
||||
"offset": {
|
||||
"x": 0.0,
|
||||
"y": 0.0,
|
||||
"z": 0.0
|
||||
},
|
||||
"xRot": 0.0,
|
||||
"yRot": 0.0
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
{
|
||||
"camYaw": 180.0,
|
||||
"heightModifier": 1.0,
|
||||
"key": "sccore:waltz_lady",
|
||||
"name": "Waltz-Lady",
|
||||
"priority": 0,
|
||||
"withRide": {
|
||||
"componentsAnimation": [
|
||||
"sccore:waltz_gentleman"
|
||||
],
|
||||
"existTick": 0,
|
||||
"offset": {
|
||||
"x": 0.0,
|
||||
"y": 0.0,
|
||||
"z": 0.0
|
||||
},
|
||||
"xRot": 0.0,
|
||||
"yRot": 0.0
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,172 @@
|
|||
package com.linearpast.sccore.animation.data.util;
|
||||
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.linearpast.sccore.SnowyCrescentCore;
|
||||
import net.minecraft.data.CachedOutput;
|
||||
import net.minecraft.data.DataGenerator;
|
||||
import net.minecraft.data.DataProvider;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
public abstract class SCCAnimationLayerProvider implements DataProvider {
|
||||
|
||||
private final DataGenerator generator;
|
||||
private final String modId;
|
||||
|
||||
/**
|
||||
* Constructor for animation layer data provider
|
||||
*
|
||||
* @param generator Data generator instance
|
||||
* @param modId Mod ID for namespace
|
||||
*/
|
||||
public SCCAnimationLayerProvider(DataGenerator generator, String modId) {
|
||||
this.generator = generator;
|
||||
this.modId = modId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull CompletableFuture<?> run(@NotNull CachedOutput output) {
|
||||
// Create layer data
|
||||
Map<ResourceLocation, Integer> layers = createLayerData().build();
|
||||
|
||||
// Convert to JSON array
|
||||
JsonArray jsonArray = createJsonArray(layers);
|
||||
|
||||
// Save file
|
||||
Path outputPath = getOutputPath();
|
||||
|
||||
try {
|
||||
return DataProvider.saveStable(output, jsonArray, outputPath);
|
||||
} catch (Exception e) {
|
||||
SnowyCrescentCore.log.error("Failed to save animation layer data", e);
|
||||
return CompletableFuture.failedFuture(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create layer data
|
||||
* Define all animation layers and their priorities here
|
||||
*
|
||||
* @return Map of layer ResourceLocations to priority values Builder
|
||||
*/
|
||||
protected abstract LayerBuilder createLayerData();
|
||||
|
||||
/**
|
||||
* Convert layer data to JSON array format compatible with AnimLayerJson
|
||||
*
|
||||
* @param layers Layer data map
|
||||
* @return JSON array representation
|
||||
*/
|
||||
private JsonArray createJsonArray(Map<ResourceLocation, Integer> layers) {
|
||||
return getJsonElements(layers);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get output path for layer configuration file
|
||||
* Path: data/<modId>/scc_animations/animation.layer.json
|
||||
*
|
||||
* @return Full output path
|
||||
*/
|
||||
private Path getOutputPath() {
|
||||
return generator.getPackOutput().getOutputFolder()
|
||||
.resolve("data")
|
||||
.resolve(modId)
|
||||
.resolve("scc_animations")
|
||||
.resolve("animation.layer.json");
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull String getName() {
|
||||
return "Animation Layer Data: " + modId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper class for building animation layer data
|
||||
* Provides fluent API for creating layer configurations
|
||||
*/
|
||||
public static class LayerBuilder {
|
||||
private final Map<ResourceLocation, Integer> layers = new LinkedHashMap<>();
|
||||
|
||||
/**
|
||||
* Create layer builder
|
||||
*
|
||||
*/
|
||||
public static LayerBuilder create() {
|
||||
return new LayerBuilder();
|
||||
}
|
||||
/**
|
||||
* Constructor for layer builder
|
||||
*
|
||||
*/
|
||||
private LayerBuilder() {}
|
||||
|
||||
/**
|
||||
* Add a base layer with specified priority
|
||||
*
|
||||
* @param name Layer name
|
||||
* @param priority Priority value (lower numbers = higher priority)
|
||||
* @return LayerBuilder instance for chaining
|
||||
*/
|
||||
public LayerBuilder addBaseLayer(ResourceLocation name, int priority) {
|
||||
layers.put(name, priority);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a custom layer with specified priority
|
||||
*
|
||||
* @param name Layer name
|
||||
* @param priority Priority value (lower numbers = higher priority)
|
||||
* @return LayerBuilder instance for chaining
|
||||
*/
|
||||
public LayerBuilder addCustomLayer(ResourceLocation name, int priority) {
|
||||
layers.put(name, priority);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the layer map
|
||||
*
|
||||
* @return Unmodifiable map of layers
|
||||
*/
|
||||
public Map<ResourceLocation, Integer> build() {
|
||||
return new LinkedHashMap<>(layers);
|
||||
}
|
||||
|
||||
/**
|
||||
* Build JSON array representation
|
||||
*
|
||||
* @return JSON array of layers
|
||||
*/
|
||||
public JsonArray buildJsonArray() {
|
||||
return getJsonElements(layers);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Convert layer map to JSON array
|
||||
* Layers are sorted by priority (ascending)
|
||||
*
|
||||
* @param layers Layer data map
|
||||
* @return JSON array representation
|
||||
*/
|
||||
@NotNull
|
||||
static JsonArray getJsonElements(Map<ResourceLocation, Integer> layers) {
|
||||
JsonArray jsonArray = new JsonArray();
|
||||
|
||||
layers.entrySet().stream()
|
||||
.sorted(Map.Entry.comparingByValue())
|
||||
.forEachOrdered(entry -> {
|
||||
JsonObject layerObject = new JsonObject();
|
||||
layerObject.addProperty("key", entry.getKey().toString());
|
||||
layerObject.addProperty("priority", entry.getValue());
|
||||
jsonArray.add(layerObject);
|
||||
});
|
||||
|
||||
return jsonArray;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,170 @@
|
|||
/*
|
||||
* *
|
||||
* * Copyright (c) 2026 R3944Realms. All rights reserved.
|
||||
* *
|
||||
* * This work is licensed under the Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.
|
||||
* * To view a copy of this license, visit http://creativecommons.org/licenses/by-nc-sa/4.0/
|
||||
* * or send a letter to Creative Commons, PO Box 1866, Mountain View, CA 94042, USA.
|
||||
* *
|
||||
* * This work is licensed under the Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.
|
||||
*
|
||||
*/
|
||||
|
||||
package com.linearpast.sccore.animation.data.util;
|
||||
|
||||
import com.google.gson.JsonObject;
|
||||
import com.linearpast.sccore.animation.data.GenericAnimationData;
|
||||
import com.linearpast.sccore.animation.data.Ride;
|
||||
import net.minecraft.data.CachedOutput;
|
||||
import net.minecraft.data.DataGenerator;
|
||||
import net.minecraft.data.DataProvider;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
* Abstract base class for animation data providers
|
||||
* Handles generation of animation JSON files in the scc_animations directory
|
||||
*/
|
||||
public abstract class SCCAnimationProvider implements DataProvider {
|
||||
private final DataGenerator generator;
|
||||
private final String modId;
|
||||
|
||||
/**
|
||||
* Constructor for animation provider
|
||||
*
|
||||
* @param generator Data generator instance
|
||||
* @param modId Mod ID for namespace
|
||||
*/
|
||||
protected SCCAnimationProvider(DataGenerator generator, String modId) {
|
||||
this.generator = generator;
|
||||
this.modId = modId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull CompletableFuture<?> run(@NotNull CachedOutput output) {
|
||||
List<CompletableFuture<?>> futures = new ArrayList<>();
|
||||
Path outputFolder = generator.getPackOutput().getOutputFolder();
|
||||
|
||||
// Register animations and save them as JSON files
|
||||
registerAnimations(animation -> {
|
||||
ResourceLocation key = animation.getKey();
|
||||
Path path = outputFolder
|
||||
.resolve("data")
|
||||
.resolve(key.getNamespace())
|
||||
.resolve("scc_animations")
|
||||
.resolve(key.getPath() + ".anim.json");
|
||||
|
||||
JsonObject json = convertToJson(animation);
|
||||
|
||||
futures.add(DataProvider.saveStable(output, json, path));
|
||||
});
|
||||
|
||||
return CompletableFuture.allOf(futures.toArray(new CompletableFuture[0]));
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull String getName() {
|
||||
return "Animations: " + modId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register animations to be generated
|
||||
* Implement this method to define which animations to create
|
||||
*
|
||||
* @param consumer Consumer that accepts GenericAnimationData instances
|
||||
*/
|
||||
protected abstract void registerAnimations(Consumer<GenericAnimationData> consumer);
|
||||
|
||||
/**
|
||||
* Convert GenericAnimationData to JSON object
|
||||
*
|
||||
* @param animation Animation data to convert
|
||||
* @return JSON object representation
|
||||
*/
|
||||
private JsonObject convertToJson(GenericAnimationData animation) {
|
||||
JsonObject json = getJsonObject(animation);
|
||||
|
||||
// Add camera position offset if present
|
||||
Vec3 camOffset = animation.getCamPosOffset();
|
||||
if (!camOffset.equals(Vec3.ZERO) || animation.isCamPosOffsetRelative()) {
|
||||
JsonObject camOffsetJson = new JsonObject();
|
||||
camOffsetJson.addProperty("x", camOffset.x);
|
||||
camOffsetJson.addProperty("y", camOffset.y);
|
||||
camOffsetJson.addProperty("z", camOffset.z);
|
||||
camOffsetJson.addProperty("relative", animation.isCamPosOffsetRelative());
|
||||
json.add("camPosOffset", camOffsetJson);
|
||||
}
|
||||
|
||||
// Add ride configuration if present
|
||||
Ride ride = animation.getRide();
|
||||
if (ride != null) {
|
||||
JsonObject rideJson = new JsonObject();
|
||||
|
||||
JsonObject offsetJson = new JsonObject();
|
||||
Vec3 offset = ride.getOffset();
|
||||
offsetJson.addProperty("x", offset.x);
|
||||
offsetJson.addProperty("y", offset.y);
|
||||
offsetJson.addProperty("z", offset.z);
|
||||
rideJson.add("offset", offsetJson);
|
||||
|
||||
rideJson.addProperty("xRot", ride.getXRot());
|
||||
rideJson.addProperty("yRot", ride.getYRot());
|
||||
rideJson.addProperty("existTick", ride.getExistTick());
|
||||
|
||||
if (!ride.getComponentAnimations().isEmpty()) {
|
||||
com.google.gson.JsonArray componentsArray = new com.google.gson.JsonArray();
|
||||
ride.getComponentAnimations().forEach(component ->
|
||||
componentsArray.add(component.toString())
|
||||
);
|
||||
rideJson.add("componentsAnimation", componentsArray);
|
||||
}
|
||||
|
||||
json.add("withRide", rideJson);
|
||||
}
|
||||
|
||||
return json;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create base JSON object with common animation properties
|
||||
*
|
||||
* @param animation Animation data
|
||||
* @return Base JSON object
|
||||
*/
|
||||
private static @NotNull JsonObject getJsonObject(GenericAnimationData animation) {
|
||||
JsonObject json = new JsonObject();
|
||||
ResourceLocation key = animation.getKey();
|
||||
json.addProperty("key", key.toString());
|
||||
|
||||
if (animation.getName() != null) {
|
||||
json.addProperty("name", animation.getName());
|
||||
}
|
||||
|
||||
if (animation.getLyingType() != null) {
|
||||
json.addProperty("lyingType", animation.getLyingType().getName());
|
||||
}
|
||||
|
||||
json.addProperty("heightModifier", animation.getHeightModifier());
|
||||
json.addProperty("priority", animation.getCamComputePriority());
|
||||
|
||||
if (animation.getCamPitch() != 0) {
|
||||
json.addProperty("camPitch", animation.getCamPitch());
|
||||
}
|
||||
|
||||
if (animation.getCamRoll() != 0) {
|
||||
json.addProperty("camRoll", animation.getCamRoll());
|
||||
}
|
||||
|
||||
if (animation.getCamYaw() != 0) {
|
||||
json.addProperty("camYaw", animation.getCamYaw());
|
||||
}
|
||||
return json;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
package com.linearpast.sccore.animation.event.server;
|
||||
|
||||
import com.linearpast.sccore.SnowyCrescentCore;
|
||||
import com.linearpast.sccore.animation.register.AnimationRegistry;
|
||||
import net.minecraftforge.api.distmarker.Dist;
|
||||
import net.minecraftforge.event.AddReloadListenerEvent;
|
||||
import net.minecraftforge.eventbus.api.SubscribeEvent;
|
||||
import net.minecraftforge.fml.common.Mod;
|
||||
|
||||
@Mod.EventBusSubscriber(modid = SnowyCrescentCore.MODID, value = Dist.DEDICATED_SERVER, bus = Mod.EventBusSubscriber.Bus.MOD)
|
||||
public class ResourceReloadListener {
|
||||
@SubscribeEvent
|
||||
public static void init (AddReloadListenerEvent event) {
|
||||
event.addListener(AnimationRegistry.AnimationDataManager.INSTANCE);
|
||||
event.addListener(AnimationRegistry.LayerDataManager.INSTANCE);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,11 @@
|
|||
package com.linearpast.sccore.animation.register;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParseException;
|
||||
import com.linearpast.sccore.SnowyCrescentCore;
|
||||
import com.linearpast.sccore.animation.capability.AnimationDataCapability;
|
||||
import com.linearpast.sccore.animation.capability.RawAnimationDataCapability;
|
||||
|
|
@ -33,15 +38,22 @@ import net.minecraft.client.player.AbstractClientPlayer;
|
|||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.server.packs.PackType;
|
||||
import net.minecraft.server.packs.resources.ResourceManager;
|
||||
import net.minecraft.server.packs.resources.SimpleJsonResourceReloadListener;
|
||||
import net.minecraft.util.GsonHelper;
|
||||
import net.minecraft.util.profiling.ProfilerFiller;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.level.storage.LevelResource;
|
||||
import net.minecraftforge.api.distmarker.Dist;
|
||||
import net.minecraftforge.api.distmarker.OnlyIn;
|
||||
import net.minecraftforge.common.MinecraftForge;
|
||||
import net.minecraftforge.event.AddReloadListenerEvent;
|
||||
import net.minecraftforge.event.entity.player.PlayerEvent;
|
||||
import net.minecraftforge.event.server.ServerStartedEvent;
|
||||
import net.minecraftforge.eventbus.api.SubscribeEvent;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
|
|
@ -74,74 +86,266 @@ public class AnimationRegistry {
|
|||
layers.putAll(layerMap);
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public static void onAddReloadListeners(AddReloadListenerEvent event) {
|
||||
event.addListener(AnimationDataManager.INSTANCE);
|
||||
event.addListener(LayerDataManager.INSTANCE);
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public static void onServerStarted(ServerStartedEvent event) {
|
||||
Path dataPackPath = event.getServer().getWorldPath(LevelResource.DATAPACK_DIR);
|
||||
// Load legacy animations from datapacks
|
||||
loadLegacyDataPackAnimations(event.getServer());
|
||||
|
||||
// Load animations from registration events
|
||||
loadFromRegistrationEvents();
|
||||
|
||||
SnowyCrescentCore.log.info("Animation loading completed. Total animations: {}, Total layers: {}",
|
||||
animations.size(), layers.size());
|
||||
}
|
||||
|
||||
/**
|
||||
* Load legacy datapack animations from world/datapacks/animation directory
|
||||
*/
|
||||
private static void loadLegacyDataPackAnimations(MinecraftServer server) {
|
||||
Path dataPackPath = server.getWorldPath(LevelResource.DATAPACK_DIR);
|
||||
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")
|
||||
);
|
||||
Set<Path> layerZipPaths = FileUtils.getAllFile(
|
||||
dataPackPath.resolve("animation"),
|
||||
path -> path.toString().endsWith(".layer.zip")
|
||||
);
|
||||
for (Path zipPath : animZipPaths) {
|
||||
FileUtils.safeUnzip(zipPath.toString(), animationPath.toAbsolutePath().toString());
|
||||
}
|
||||
for (Path zipPath : layerZipPaths) {
|
||||
FileUtils.safeUnzip(zipPath.toString(), animationPath.toAbsolutePath().toString());
|
||||
}
|
||||
|
||||
Set<Path> animPaths = FileUtils.getAllFile(
|
||||
dataPackPath.resolve("animation"),
|
||||
path -> path.toString().endsWith(".anim.json")
|
||||
);
|
||||
Set<Path> layerPaths = FileUtils.getAllFile(
|
||||
dataPackPath.resolve("animation"),
|
||||
path -> path.getFileName().toString().equals("animation.layer.json")
|
||||
);
|
||||
Set<GenericAnimationData> animationsSet = new HashSet<>();
|
||||
Map<ResourceLocation, Integer> layersMap = new HashMap<>();
|
||||
for (Path path : animPaths) {
|
||||
try {
|
||||
AnimJson.Reader reader = AnimJson.Reader.stream(path);
|
||||
GenericAnimationData anim = reader.parse();
|
||||
animationsSet.add(anim);
|
||||
} catch (Exception ignored) {
|
||||
SnowyCrescentCore.log.error("Failed to parse animation JSON: {}", path.toString());
|
||||
}
|
||||
}
|
||||
for (Path path : layerPaths) {
|
||||
try {
|
||||
AnimLayerJson.Reader reader = AnimLayerJson.Reader.stream(path);
|
||||
Map<ResourceLocation, Integer> parse = reader.parse();
|
||||
layersMap.putAll(parse);
|
||||
} catch (Exception ignored) {
|
||||
SnowyCrescentCore.log.error("Failed to parse layer JSON: {}", path.toString());
|
||||
} catch (IOException e) {
|
||||
SnowyCrescentCore.log.error("Failed to create legacy animation directory", e);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
animations.clear();
|
||||
try {
|
||||
// Handle zip files
|
||||
if (Files.exists(dataPackPath.resolve("animation.zip"))) {
|
||||
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 = FileUtils.getAllFile(
|
||||
dataPackPath.resolve("animation"),
|
||||
path -> path.toString().endsWith(".layer.zip")
|
||||
);
|
||||
|
||||
for (Path zipPath : animZipPaths) {
|
||||
FileUtils.safeUnzip(zipPath.toString(), animationPath.toAbsolutePath().toString());
|
||||
}
|
||||
|
||||
for (Path zipPath : layerZipPaths) {
|
||||
FileUtils.safeUnzip(zipPath.toString(), animationPath.toAbsolutePath().toString());
|
||||
}
|
||||
|
||||
// Load animation JSON files
|
||||
Set<Path> animPaths = FileUtils.getAllFile(
|
||||
dataPackPath.resolve("animation"),
|
||||
path -> path.toString().endsWith(".anim.json")
|
||||
);
|
||||
|
||||
Set<Path> layerPaths = FileUtils.getAllFile(
|
||||
dataPackPath.resolve("animation"),
|
||||
path -> path.getFileName().toString().equals("animation.layer.json")
|
||||
);
|
||||
|
||||
for (Path path : animPaths) {
|
||||
try {
|
||||
AnimJson.Reader reader = AnimJson.Reader.stream(path);
|
||||
GenericAnimationData anim = reader.parse();
|
||||
animations.put(anim.getKey(), anim);
|
||||
SnowyCrescentCore.log.info("Loaded legacy animation: {} -> {}",
|
||||
anim.getKey(), anim.getName());
|
||||
} catch (Exception e) {
|
||||
SnowyCrescentCore.log.error("Failed to parse legacy animation JSON: {}", path, e);
|
||||
}
|
||||
}
|
||||
|
||||
for (Path path : layerPaths) {
|
||||
try {
|
||||
AnimLayerJson.Reader reader = AnimLayerJson.Reader.stream(path);
|
||||
Map<ResourceLocation, Integer> parse = reader.parse();
|
||||
layers.putAll(parse);
|
||||
SnowyCrescentCore.log.info("Loaded {} legacy layer configurations from {}",
|
||||
parse.size(), path);
|
||||
} catch (Exception e) {
|
||||
SnowyCrescentCore.log.error("Failed to parse legacy layer JSON: {}", path, e);
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
SnowyCrescentCore.log.error("Error loading legacy animations", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Load animations from registration events
|
||||
*/
|
||||
private static void loadFromRegistrationEvents() {
|
||||
AnimationRegisterEvent.Animation animationRegisterEvent = new AnimationRegisterEvent.Animation();
|
||||
MinecraftForge.EVENT_BUS.post(animationRegisterEvent);
|
||||
Map<ResourceLocation, GenericAnimationData> animationMap = animationRegisterEvent.getAnimations();
|
||||
animations.putAll(animationMap);
|
||||
animations.putAll(animationsSet.stream().collect(Collectors.toMap(GenericAnimationData::getKey, animation -> animation)));
|
||||
|
||||
layers.clear();
|
||||
AnimationRegisterEvent.Layer layerRegisterEvent = new AnimationRegisterEvent.Layer();
|
||||
MinecraftForge.EVENT_BUS.post(layerRegisterEvent);
|
||||
Map<ResourceLocation, Integer> layerMap = layerRegisterEvent.getLayers();
|
||||
layers.putAll(layerMap);
|
||||
layers.putAll(layersMap);
|
||||
|
||||
SnowyCrescentCore.log.info("Loaded {} animations and {} layers from registration events",
|
||||
animationMap.size(), layerMap.size());
|
||||
}
|
||||
|
||||
/**
|
||||
* Animation data manager using SimpleJsonResourceReloadListener
|
||||
*/
|
||||
public static class AnimationDataManager extends SimpleJsonResourceReloadListener {
|
||||
public static final AnimationDataManager INSTANCE = new AnimationDataManager();
|
||||
|
||||
private AnimationDataManager() {
|
||||
super(new GsonBuilder()
|
||||
.setPrettyPrinting()
|
||||
.disableHtmlEscaping()
|
||||
.create(), "scc_animations");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void apply(@Nonnull Map<ResourceLocation, JsonElement> resources,
|
||||
@Nonnull ResourceManager resourceManager,
|
||||
@Nonnull ProfilerFiller profiler) {
|
||||
SnowyCrescentCore.log.info("Loading animations from data packs...");
|
||||
|
||||
Map<ResourceLocation, JsonElement> sorted = new LinkedHashMap<>();
|
||||
|
||||
// Sort resources by priority
|
||||
resourceManager.listPacks().forEach(packResources -> {
|
||||
Set<String> namespaces = packResources.getNamespaces(PackType.SERVER_DATA);
|
||||
namespaces.forEach(namespace ->
|
||||
packResources.listResources(PackType.SERVER_DATA, namespace, "scc_animations",
|
||||
(resourceLocation, inputStreamIoSupplier) -> {
|
||||
String path = resourceLocation.getPath();
|
||||
if (path.endsWith(".anim.json")) {
|
||||
ResourceLocation rl = new ResourceLocation(namespace,
|
||||
path.substring("scc_animations/".length(), path.length() - ".json".length()));
|
||||
|
||||
JsonElement el = resources.get(rl);
|
||||
if (el != null) {
|
||||
rl = new ResourceLocation(namespace,
|
||||
path.substring("scc_animations/".length(), path.length() - ".anim.json".length()));
|
||||
sorted.put(rl, el);
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
);
|
||||
});
|
||||
|
||||
int loadedCount = 0;
|
||||
for (Map.Entry<ResourceLocation, JsonElement> entry : sorted.entrySet()) {
|
||||
ResourceLocation animKey = entry.getKey();
|
||||
|
||||
try {
|
||||
JsonObject json = GsonHelper.convertToJsonObject(entry.getValue(), "animation");
|
||||
|
||||
// Parse animation using existing AnimJson.Reader
|
||||
AnimJson.Reader animReader = AnimJson.Reader.stream(json);
|
||||
GenericAnimationData anim = animReader.parse();
|
||||
|
||||
// Ensure the key matches
|
||||
if (!anim.getKey().equals(animKey)) {
|
||||
SnowyCrescentCore.log.warn("Animation key mismatch: file={}, expected={}, actual={}",
|
||||
entry.getKey(), animKey, anim.getKey());
|
||||
anim = anim.withName(animKey.getPath()); // Create a copy with correct key if possible
|
||||
}
|
||||
|
||||
animations.put(animKey, anim);
|
||||
loadedCount++;
|
||||
SnowyCrescentCore.log.debug("Loaded animation: {} -> {}", animKey, anim.getName());
|
||||
|
||||
} catch (IllegalArgumentException | JsonParseException e) {
|
||||
SnowyCrescentCore.log.error("Parsing error loading animation {}", animKey, e);
|
||||
}
|
||||
}
|
||||
|
||||
SnowyCrescentCore.log.info("Loaded {} animations from data packs", loadedCount);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Layer data manager using SimpleJsonResourceReloadListener
|
||||
*/
|
||||
public static class LayerDataManager extends SimpleJsonResourceReloadListener {
|
||||
public static final LayerDataManager INSTANCE = new LayerDataManager();
|
||||
|
||||
|
||||
private LayerDataManager() {
|
||||
super(new GsonBuilder()
|
||||
.setPrettyPrinting()
|
||||
.disableHtmlEscaping()
|
||||
.create(), "scc_animations");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void apply(@Nonnull Map<ResourceLocation, JsonElement> resources,
|
||||
@Nonnull ResourceManager resourceManager,
|
||||
@Nonnull ProfilerFiller profiler) {
|
||||
SnowyCrescentCore.log.info("Loading layer configurations from data packs...");
|
||||
|
||||
Map<ResourceLocation, JsonElement> sorted = new LinkedHashMap<>();
|
||||
|
||||
// Sort resources by priority
|
||||
resourceManager.listPacks().forEach(packResources -> {
|
||||
Set<String> namespaces = packResources.getNamespaces(PackType.SERVER_DATA);
|
||||
namespaces.forEach(namespace ->
|
||||
packResources.listResources(PackType.SERVER_DATA, namespace, "scc_animations",
|
||||
(resourceLocation, inputStreamIoSupplier) -> {
|
||||
String path = resourceLocation.getPath();
|
||||
if (path.endsWith("animation.layer.json")) {
|
||||
ResourceLocation rl = new ResourceLocation(namespace, "animation.layer");
|
||||
|
||||
JsonElement el = resources.get(rl);
|
||||
if (el != null) {
|
||||
rl = new ResourceLocation(namespace, "animation_layer");
|
||||
sorted.put(rl, el);
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
);
|
||||
});
|
||||
|
||||
int loadedCount = 0;
|
||||
for (Map.Entry<ResourceLocation, JsonElement> entry : sorted.entrySet()) {
|
||||
try {
|
||||
JsonElement json = entry.getValue();
|
||||
AnimLayerJson.Reader layerReader = AnimLayerJson.Reader.stream(json);
|
||||
Map<ResourceLocation, Integer> parsedLayers = layerReader.parse();
|
||||
|
||||
// Merge layer configurations
|
||||
parsedLayers.forEach((layerKey, priority) -> {
|
||||
if (layers.containsKey(layerKey)) {
|
||||
SnowyCrescentCore.log.debug("Overriding layer {} with priority {}", layerKey, priority);
|
||||
}
|
||||
layers.put(layerKey, priority);
|
||||
});
|
||||
|
||||
loadedCount += parsedLayers.size();
|
||||
SnowyCrescentCore.log.debug("Loaded {} layer configurations from {}",
|
||||
parsedLayers.size(), entry.getKey().getNamespace());
|
||||
|
||||
} catch (IllegalArgumentException | JsonParseException e) {
|
||||
SnowyCrescentCore.log.error("Parsing error loading layer configuration", e);
|
||||
}
|
||||
}
|
||||
|
||||
SnowyCrescentCore.log.info("Loaded {} layer configurations from data packs", loadedCount);
|
||||
}
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
|
|
@ -149,29 +353,64 @@ public class AnimationRegistry {
|
|||
if (event.getEntity() instanceof ServerPlayer serverPlayer) {
|
||||
MinecraftServer server = serverPlayer.getServer();
|
||||
if(server == null) return;
|
||||
Path dataPackPath = server.getWorldPath(LevelResource.DATAPACK_DIR);
|
||||
Path animationPath = dataPackPath.resolve("animation");
|
||||
if (!Files.exists(animationPath)) {
|
||||
try {Files.createDirectories(animationPath);}
|
||||
catch (IOException e) { return; }
|
||||
}
|
||||
ModChannel.sendToPlayer(new AnimationClientStatusPacket(AnimationClientStatusPacket.Status.ANIM_CACHE_CLEAR), serverPlayer);
|
||||
for (GenericAnimationData value : animations.values()) {
|
||||
JsonElement json = AnimJson.Writer.stream(value).toJson();
|
||||
String string = json.toString();
|
||||
ModChannel.sendToPlayer(new AnimationJsonPacket(string, false), serverPlayer);
|
||||
}
|
||||
ModChannel.sendToPlayer(new AnimationClientStatusPacket(AnimationClientStatusPacket.Status.ANIM_REGISTER), serverPlayer);
|
||||
ModChannel.sendToPlayer(new AnimationClientStatusPacket(AnimationClientStatusPacket.Status.LAYER_CACHE_CLEAR), serverPlayer);
|
||||
Map<String, JsonElement> jsonElementMap = AnimLayerJson.Writer.stream(animationPath).allToJson();
|
||||
jsonElementMap.forEach((key, value) ->
|
||||
ModChannel.sendToPlayer(new AnimationJsonPacket(value.toString(), true), serverPlayer)
|
||||
);
|
||||
ModChannel.sendToPlayer(new AnimationClientStatusPacket(AnimationClientStatusPacket.Status.LAYER_REGISTER), serverPlayer);
|
||||
}
|
||||
|
||||
// Send animations to client
|
||||
sendAnimationsToClient(serverPlayer);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Send all animations and layers to the client
|
||||
*/
|
||||
private static void sendAnimationsToClient(ServerPlayer player) {
|
||||
SnowyCrescentCore.log.debug("Sending animations to player: {}", player.getName().getString());
|
||||
|
||||
// Clear client cache first
|
||||
ModChannel.sendToPlayer(new AnimationClientStatusPacket(
|
||||
AnimationClientStatusPacket.Status.ANIM_CACHE_CLEAR), player);
|
||||
|
||||
// Send all animations
|
||||
for (GenericAnimationData anim : animations.values()) {
|
||||
JsonElement json = AnimJson.Writer.stream(anim).toJson();
|
||||
String jsonString = json.toString();
|
||||
ModChannel.sendToPlayer(new AnimationJsonPacket(jsonString, false), player);
|
||||
}
|
||||
|
||||
// Register animations on client
|
||||
ModChannel.sendToPlayer(new AnimationClientStatusPacket(
|
||||
AnimationClientStatusPacket.Status.ANIM_REGISTER), player);
|
||||
|
||||
// Clear layer cache
|
||||
ModChannel.sendToPlayer(new AnimationClientStatusPacket(
|
||||
AnimationClientStatusPacket.Status.LAYER_CACHE_CLEAR), player);
|
||||
|
||||
// Send layer configurations
|
||||
JsonElement layerJson = convertLayersToJson(layers);
|
||||
ModChannel.sendToPlayer(new AnimationJsonPacket(layerJson.toString(), true), player);
|
||||
|
||||
// Register layers on client
|
||||
ModChannel.sendToPlayer(new AnimationClientStatusPacket(
|
||||
AnimationClientStatusPacket.Status.LAYER_REGISTER), player);
|
||||
|
||||
SnowyCrescentCore.log.debug("Sent {} animations and {} layers to player {}",
|
||||
animations.size(), layers.size(), player.getName().getString());
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert layers map to JSON
|
||||
*/
|
||||
private static JsonElement convertLayersToJson(Map<ResourceLocation, Integer> layers) {
|
||||
JsonArray jsonArray = new JsonArray();
|
||||
|
||||
for (Map.Entry<ResourceLocation, Integer> entry : layers.entrySet()) {
|
||||
JsonObject jsonObject = new JsonObject();
|
||||
jsonObject.addProperty("key", entry.getKey().toString());
|
||||
jsonObject.addProperty("priority", entry.getValue());
|
||||
jsonArray.add(jsonObject);
|
||||
}
|
||||
|
||||
return jsonArray;
|
||||
}
|
||||
|
||||
@OnlyIn(Dist.CLIENT)
|
||||
public static class ClientCache {
|
||||
|
|
@ -188,7 +427,6 @@ public class AnimationRegistry {
|
|||
layersCache.put(location, priority);
|
||||
}
|
||||
|
||||
@SuppressWarnings({"JavaReflectionMemberAccess", "UnstableApiUsage", "unchecked"})
|
||||
public static void animationStatusUpdate(AnimationClientStatusPacket.Status status) {
|
||||
switch (status) {
|
||||
case ANIM_CACHE_CLEAR -> animationsCache.clear();
|
||||
|
|
@ -206,94 +444,155 @@ public class AnimationRegistry {
|
|||
registerLayers(layersCache);
|
||||
layersCache.forEach((key, value) -> PlayerAnimationFactory.ANIMATION_DATA_FACTORY.registerFactory(
|
||||
key, value, player -> {
|
||||
if(Minecraft.getInstance().player == null) return ClientCache.registerPlayerAnimation(player);
|
||||
Map<ResourceLocation, IAnimation> animationMap = modifierLayers.getOrDefault(player.getUUID(), new HashMap<>());
|
||||
if(animationMap.containsKey(key)) return animationMap.get(key);
|
||||
if (Minecraft.getInstance().player == null) {
|
||||
return ClientCache.registerPlayerAnimation(player);
|
||||
}
|
||||
Map<ResourceLocation, IAnimation> animationMap =
|
||||
modifierLayers.getOrDefault(player.getUUID(), new HashMap<>());
|
||||
if (animationMap.containsKey(key)) {
|
||||
return animationMap.get(key);
|
||||
}
|
||||
IAnimation iAnimation = ClientCache.registerPlayerAnimation(player);
|
||||
animationMap.put(key, iAnimation);
|
||||
modifierLayers.put(player.getUUID(), animationMap);
|
||||
return iAnimation;
|
||||
})
|
||||
);
|
||||
|
||||
// Update existing players' animation stacks
|
||||
ClientLevel level = Minecraft.getInstance().level;
|
||||
if(level == null) {
|
||||
SnowyCrescentCore.log.error("{} : Level is null", ClientCache.class.getName());
|
||||
if (level == null) {
|
||||
SnowyCrescentCore.log.error("Level is null, cannot update animation layers");
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
for (AbstractClientPlayer player : level.players()) {
|
||||
try {
|
||||
Class<?> playerClass = Player.class;
|
||||
Field animationStackField = playerClass.getDeclaredField("animationStack");
|
||||
animationStackField.setAccessible(true);
|
||||
Method createAnimationStack = playerClass.getDeclaredMethod("createAnimationStack");
|
||||
createAnimationStack.setAccessible(true);
|
||||
AnimationStack newAnimationStack = (AnimationStack) createAnimationStack.invoke(player);
|
||||
AnimationStack oldAnimationStack = (AnimationStack) animationStackField.get(player);
|
||||
Field layersField = AnimationStack.class.getDeclaredField("layers");
|
||||
layersField.setAccessible(true);
|
||||
ArrayList<Pair<Integer, IAnimation>> oldArrayList = new ArrayList<>((ArrayList<Pair<Integer, IAnimation>>) layersField.get(oldAnimationStack));
|
||||
ArrayList<Pair<Integer, IAnimation>> newArrayList = new ArrayList<>((ArrayList<Pair<Integer, IAnimation>>) layersField.get(newAnimationStack));
|
||||
ArrayList<Pair<Integer, IAnimation>> result = new ArrayList<>();
|
||||
for (Pair<Integer, IAnimation> oldAnimationPair : List.copyOf(oldArrayList)) {
|
||||
for (Pair<Integer, IAnimation> newAnimationPair : List.copyOf(newArrayList)) {
|
||||
if(Objects.equals(oldAnimationPair.getLeft(), newAnimationPair.getLeft())) {
|
||||
KeyframeAnimation oldData = Optional.ofNullable((KeyframeAnimationPlayer) ((ModifierLayer<?>) oldAnimationPair.getRight()).getAnimation())
|
||||
.map(KeyframeAnimationPlayer::getData).orElse(null);
|
||||
KeyframeAnimation newData = Optional.ofNullable((KeyframeAnimationPlayer) ((ModifierLayer<?>) newAnimationPair.getRight()).getAnimation())
|
||||
.map(KeyframeAnimationPlayer::getData).orElse(null);
|
||||
if(Objects.equals(oldData, newData)) oldArrayList.remove(oldAnimationPair);
|
||||
}
|
||||
}
|
||||
}
|
||||
result.addAll(oldArrayList);
|
||||
result.addAll(newArrayList);
|
||||
|
||||
layersField.set(newAnimationStack, result);
|
||||
animationStackField.set(player, newAnimationStack);
|
||||
Field animationApplierField = playerClass.getDeclaredField("animationApplier");
|
||||
animationApplierField.setAccessible(true);
|
||||
animationApplierField.set(player, new AnimationApplier(newAnimationStack));
|
||||
IAnimationCapability data = AnimationDataCapability.getCapability(player).orElse(null);
|
||||
if(data == null) continue;
|
||||
RawAnimationDataCapability rawData = RawAnimationDataCapability.getCapability(player).orElse(null);
|
||||
if(rawData == null) continue;
|
||||
Map<ResourceLocation, ResourceLocation> dataAnimations = new HashMap<>();
|
||||
dataAnimations.putAll(data.getAnimations());
|
||||
dataAnimations.putAll(rawData.getAnimations());
|
||||
ResourceLocation riderAnimLayer = data.getRiderAnimLayer();
|
||||
if(riderAnimLayer != null) {
|
||||
dataAnimations.put(riderAnimLayer, data.getRiderAnimation());
|
||||
}
|
||||
for (ResourceLocation location : dataAnimations.keySet()) {
|
||||
ModifierLayer<IAnimation> modifierLayer = (ModifierLayer<IAnimation>) PlayerAnimationAccess
|
||||
.getPlayerAssociatedData(player).get(location);
|
||||
if(modifierLayer == null) continue;
|
||||
KeyframeAnimation keyframeAnimation;
|
||||
ResourceLocation animationLocation = dataAnimations.get(location);
|
||||
GenericAnimationData anim = animations.get(animationLocation);
|
||||
if(anim == null) {
|
||||
RawAnimationData rawAnim = RawAnimationService.INSTANCE.getAnimation(animationLocation);
|
||||
if(rawAnim == null) return;
|
||||
keyframeAnimation = rawAnim.getAnimation();
|
||||
} else keyframeAnimation = anim.getAnimation();
|
||||
if(keyframeAnimation == null) continue;
|
||||
modifierLayer.replaceAnimationWithFade(
|
||||
AbstractFadeModifier.standardFadeIn(3, Ease.INOUTSINE),
|
||||
new KeyframeAnimationPlayer(keyframeAnimation)
|
||||
);
|
||||
}
|
||||
}catch (Exception e){
|
||||
SnowyCrescentCore.log.error("Failed to register on {} animation layer: {}", player, e.getMessage(), e);
|
||||
}
|
||||
updatePlayerAnimationStack(player);
|
||||
}
|
||||
} catch (Exception ignored) {}
|
||||
} catch (Exception e) {
|
||||
SnowyCrescentCore.log.error("Failed to update player animation layers", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update a player's animation stack with new layers
|
||||
*/
|
||||
@SuppressWarnings({"unchecked", "JavaReflectionMemberAccess"})
|
||||
private static void updatePlayerAnimationStack(AbstractClientPlayer player) {
|
||||
try {
|
||||
Class<?> playerClass = Player.class;
|
||||
Field animationStackField = playerClass.getDeclaredField("animationStack");
|
||||
animationStackField.setAccessible(true);
|
||||
|
||||
Method createAnimationStack = playerClass.getDeclaredMethod("createAnimationStack");
|
||||
createAnimationStack.setAccessible(true);
|
||||
|
||||
AnimationStack newAnimationStack = (AnimationStack) createAnimationStack.invoke(player);
|
||||
AnimationStack oldAnimationStack = (AnimationStack) animationStackField.get(player);
|
||||
|
||||
Field layersField = AnimationStack.class.getDeclaredField("layers");
|
||||
layersField.setAccessible(true);
|
||||
|
||||
ArrayList<Pair<Integer, IAnimation>> oldArrayList =
|
||||
new ArrayList<>((ArrayList<Pair<Integer, IAnimation>>) layersField.get(oldAnimationStack));
|
||||
ArrayList<Pair<Integer, IAnimation>> newArrayList =
|
||||
new ArrayList<>((ArrayList<Pair<Integer, IAnimation>>) layersField.get(newAnimationStack));
|
||||
|
||||
// Merge layers, keeping unique ones
|
||||
ArrayList<Pair<Integer, IAnimation>> result = new ArrayList<>();
|
||||
|
||||
// Add old layers that aren't replaced by new ones
|
||||
for (Pair<Integer, IAnimation> oldPair : oldArrayList) {
|
||||
boolean isReplaced = false;
|
||||
for (Pair<Integer, IAnimation> newPair : newArrayList) {
|
||||
if (Objects.equals(oldPair.getLeft(), newPair.getLeft())) {
|
||||
isReplaced = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!isReplaced) {
|
||||
result.add(oldPair);
|
||||
}
|
||||
}
|
||||
|
||||
// Add all new layers
|
||||
result.addAll(newArrayList);
|
||||
|
||||
// Set the merged layers
|
||||
layersField.set(newAnimationStack, result);
|
||||
animationStackField.set(player, newAnimationStack);
|
||||
|
||||
// Update animation applier
|
||||
Field animationApplierField = playerClass.getDeclaredField("animationApplier");
|
||||
animationApplierField.setAccessible(true);
|
||||
//noinspection UnstableApiUsage
|
||||
animationApplierField.set(player, new AnimationApplier(newAnimationStack));
|
||||
|
||||
// Restore any playing animations
|
||||
restorePlayingAnimations(player);
|
||||
|
||||
} catch (Exception e) {
|
||||
SnowyCrescentCore.log.error("Failed to update animation stack for player: {}", player, e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Restore animations that were playing before the update
|
||||
*/
|
||||
private static void restorePlayingAnimations(AbstractClientPlayer player) {
|
||||
try {
|
||||
IAnimationCapability data = AnimationDataCapability.getCapability(player).orElse(null);
|
||||
if (data == null) return;
|
||||
|
||||
RawAnimationDataCapability rawData = RawAnimationDataCapability.getCapability(player).orElse(null);
|
||||
if (rawData == null) return;
|
||||
|
||||
Map<ResourceLocation, ResourceLocation> dataAnimations = new HashMap<>();
|
||||
dataAnimations.putAll(data.getAnimations());
|
||||
dataAnimations.putAll(rawData.getAnimations());
|
||||
|
||||
ResourceLocation riderAnimLayer = data.getRiderAnimLayer();
|
||||
if (riderAnimLayer != null && data.getRiderAnimation() != null) {
|
||||
dataAnimations.put(riderAnimLayer, data.getRiderAnimation());
|
||||
}
|
||||
|
||||
for (Map.Entry<ResourceLocation, ResourceLocation> entry : dataAnimations.entrySet()) {
|
||||
ResourceLocation layerKey = entry.getKey();
|
||||
ResourceLocation animKey = entry.getValue();
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
ModifierLayer<IAnimation> modifierLayer = (ModifierLayer<IAnimation>)
|
||||
PlayerAnimationAccess.getPlayerAssociatedData(player).get(layerKey);
|
||||
if (modifierLayer == null) continue;
|
||||
|
||||
KeyframeAnimation keyframeAnimation;
|
||||
GenericAnimationData anim = animations.get(animKey);
|
||||
|
||||
if (anim == null) {
|
||||
RawAnimationData rawAnim = RawAnimationService.INSTANCE.getAnimation(animKey);
|
||||
if (rawAnim == null) continue;
|
||||
keyframeAnimation = rawAnim.getAnimation();
|
||||
} else {
|
||||
keyframeAnimation = anim.getAnimation();
|
||||
}
|
||||
|
||||
if (keyframeAnimation == null) continue;
|
||||
|
||||
modifierLayer.replaceAnimationWithFade(
|
||||
AbstractFadeModifier.standardFadeIn(3, Ease.INOUTSINE),
|
||||
new KeyframeAnimationPlayer(keyframeAnimation)
|
||||
);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
SnowyCrescentCore.log.error("Failed to restore playing animations for player: {}", player, e);
|
||||
}
|
||||
}
|
||||
|
||||
private static IAnimation registerPlayerAnimation(AbstractClientPlayer player) {
|
||||
return new ModifierLayer<>();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,8 @@
|
|||
package com.linearpast.sccore.core.datagen;
|
||||
|
||||
import com.linearpast.sccore.SnowyCrescentCore;
|
||||
import com.linearpast.sccore.core.datagen.provider.ModAnimationLayerProvider;
|
||||
import com.linearpast.sccore.core.datagen.provider.ModAnimationProvider;
|
||||
import com.linearpast.sccore.core.datagen.provider.ModLangProvider;
|
||||
import net.minecraft.core.HolderLookup;
|
||||
import net.minecraft.data.DataGenerator;
|
||||
|
|
@ -24,5 +26,7 @@ public class DataGenEvent {
|
|||
|
||||
generator.addProvider(event.includeClient(), new ModLangProvider(packOutput, ModLangProvider.Lang.EN_US));
|
||||
generator.addProvider(event.includeClient(), new ModLangProvider(packOutput, ModLangProvider.Lang.ZH_CN));
|
||||
generator.addProvider(true, new ModAnimationProvider(generator));
|
||||
generator.addProvider(true, new ModAnimationLayerProvider(generator));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,20 @@
|
|||
package com.linearpast.sccore.core.datagen.provider;
|
||||
|
||||
import com.linearpast.sccore.SnowyCrescentCore;
|
||||
|
||||
import com.linearpast.sccore.animation.data.util.SCCAnimationLayerProvider;
|
||||
import com.linearpast.sccore.example.animation.ModAnimation;
|
||||
import net.minecraft.data.DataGenerator;
|
||||
|
||||
public class ModAnimationLayerProvider extends SCCAnimationLayerProvider {
|
||||
public ModAnimationLayerProvider(DataGenerator generator) {
|
||||
super(generator, SnowyCrescentCore.MODID);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected LayerBuilder createLayerData() {
|
||||
return LayerBuilder.create()
|
||||
.addCustomLayer(ModAnimation.normalLayers, 42);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
package com.linearpast.sccore.core.datagen.provider;
|
||||
|
||||
import com.linearpast.sccore.SnowyCrescentCore;
|
||||
import com.linearpast.sccore.animation.data.GenericAnimationData;
|
||||
import com.linearpast.sccore.animation.data.Ride;
|
||||
import com.linearpast.sccore.animation.data.util.SCCAnimationProvider;
|
||||
import net.minecraft.data.DataGenerator;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import static com.linearpast.sccore.example.animation.ModAnimation.WaltzGentleman;
|
||||
import static com.linearpast.sccore.example.animation.ModAnimation.WaltzLady;
|
||||
|
||||
public class ModAnimationProvider extends SCCAnimationProvider {
|
||||
|
||||
public ModAnimationProvider(DataGenerator generator) {
|
||||
super(generator, SnowyCrescentCore.MODID);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void registerAnimations(Consumer<GenericAnimationData> consumer) {
|
||||
{
|
||||
GenericAnimationData waltzGentleman = (GenericAnimationData) GenericAnimationData
|
||||
.create(WaltzGentleman)
|
||||
.withName("Waltz-Gentleman")
|
||||
.addCamPosOffset(new Vec3(0.0, 0.0, 1.0))
|
||||
.withCamPosOffsetRelative(true)
|
||||
.withRide(Ride.create().addComponentAnimation(WaltzLady));
|
||||
GenericAnimationData waltzLady = (GenericAnimationData) GenericAnimationData
|
||||
.create(WaltzLady)
|
||||
.withName("Waltz-Lady")
|
||||
.withCamYaw(180)
|
||||
.withRide(Ride.create().addComponentAnimation(WaltzGentleman));
|
||||
consumer.accept(waltzGentleman);
|
||||
consumer.accept(waltzLady);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -38,7 +38,7 @@ public class ModAnimation {
|
|||
* @param event event
|
||||
*/
|
||||
public static void onLayerRegister(AnimationRegisterEvent.Layer event) {
|
||||
event.registerLayer(normalLayers, 42);
|
||||
// event.registerLayer(normalLayers, 42);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -54,23 +54,23 @@ public class ModAnimation {
|
|||
// GenericAnimationData amSTL = GenericAnimationData.create(AmStandToLying)
|
||||
// .withName("Stand-to-Lying")
|
||||
// .withLyingType(GenericAnimationData.LyingType.FRONT);
|
||||
GenericAnimationData waltzGentleman = (GenericAnimationData) GenericAnimationData
|
||||
.create(WaltzGentleman)
|
||||
.withName("Waltz-Gentleman")
|
||||
.addCamPosOffset(new Vec3(0.0,0.0,1.0))
|
||||
.withCamPosOffsetRelative(true)
|
||||
.withRide(Ride.create().addComponentAnimation(WaltzLady));
|
||||
GenericAnimationData waltzLady = (GenericAnimationData) GenericAnimationData
|
||||
.create(WaltzLady)
|
||||
.withName("Waltz-Lady")
|
||||
.withCamYaw(180)
|
||||
.withRide(Ride.create().addComponentAnimation(WaltzGentleman));
|
||||
// GenericAnimationData waltzGentleman = (GenericAnimationData) GenericAnimationData
|
||||
// .create(WaltzGentleman)
|
||||
// .withName("Waltz-Gentleman")
|
||||
// .addCamPosOffset(new Vec3(0.0,0.0,1.0))
|
||||
// .withCamPosOffsetRelative(true)
|
||||
// .withRide(Ride.create().addComponentAnimation(WaltzLady));
|
||||
// GenericAnimationData waltzLady = (GenericAnimationData) GenericAnimationData
|
||||
// .create(WaltzLady)
|
||||
// .withName("Waltz-Lady")
|
||||
// .withCamYaw(180)
|
||||
// .withRide(Ride.create().addComponentAnimation(WaltzGentleman));
|
||||
|
||||
//You can use it to invite an Animation
|
||||
// event.registerAnimation(AmLyingToRightLying, amLTRL);
|
||||
// event.registerAnimation(AmStandToLying, amSTL);
|
||||
event.registerAnimation(WaltzGentleman, waltzGentleman);
|
||||
event.registerAnimation(WaltzLady, waltzLady);
|
||||
// event.registerAnimation(WaltzGentleman, waltzGentleman);
|
||||
// event.registerAnimation(WaltzLady, waltzLady);
|
||||
}
|
||||
|
||||
public static void onRawAnimationRegister(AnimationRegisterEvent.RawAnimation event) {
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user