改为基于SCC的附加
This commit is contained in:
parent
e8d4227abb
commit
a81cbefe9b
10
build.gradle
10
build.gradle
|
|
@ -115,6 +115,10 @@ repositories {
|
|||
name = "Illusive Soulworks maven"
|
||||
url = "https://maven.theillusivec4.top/"
|
||||
}
|
||||
maven {
|
||||
name = "LTD Maven"
|
||||
url = "https://nexus.bot.leisuretimedock.top/repository/maven-public/"
|
||||
}
|
||||
}
|
||||
|
||||
jarJar.enable()
|
||||
|
|
@ -130,10 +134,7 @@ dependencies {
|
|||
|
||||
implementation fg.deobf("dev.kosmx.player-anim:player-animation-lib-forge:1.0.2-rc1+1.20")
|
||||
implementation fg.deobf("io.github.kosmx.bendy-lib:bendy-lib-forge:4.0.0")
|
||||
|
||||
// implementation fg.deobf("curse.maven:cloth-config-348521:5729105")
|
||||
// implementation fg.deobf("curse.maven:better-combat-by-daedelus-639842:5625757")
|
||||
// implementation fg.deobf("curse.maven:freecam-by-zergatul-618947:5402097")
|
||||
implementation("io.zershyan:sccore:1.20.1-1.0.0")
|
||||
}
|
||||
|
||||
tasks.named('processResources', ProcessResources).configure {
|
||||
|
|
@ -151,6 +152,7 @@ tasks.named('processResources', ProcessResources).configure {
|
|||
mod_description: mod_description,
|
||||
mod_credits: mod_credits,
|
||||
mod_url: mod_url,
|
||||
sccore_verson: sccore_verson
|
||||
]
|
||||
|
||||
inputs.properties replaceProperties
|
||||
|
|
|
|||
|
|
@ -12,9 +12,10 @@ mapping_version=2023.09.03-1.20.1
|
|||
mod_id=animcore
|
||||
mod_name=AnimationCore
|
||||
mod_license=GPL 3.0
|
||||
mod_version=1.20.1-26H4
|
||||
mod_version=1.20.1-26H7
|
||||
mod_group_id=top.leisuiretimedock.animationcore
|
||||
mod_authors=LostInLinearPast, R3944Realms
|
||||
mod_description=A lib about player animator(modified by R3944Realms).
|
||||
sccore_verson=1.0.0
|
||||
mod_authors=R3944Realms
|
||||
mod_description=
|
||||
mod_credits=
|
||||
mod_url=https://github.com/LeisureTimeDock/AnimCore
|
||||
mod_url=https://github.com/LeisureTimeDock/AnimationCore
|
||||
|
|
|
|||
|
|
@ -1,2 +0,0 @@
|
|||
// 1.20.1 2026-01-13T10:48:06.5809965 Languages: zh_cn
|
||||
28620aef6acf68c887244c604393f326e8d7a2ef assets/animcore/lang/zh_cn.json
|
||||
|
|
@ -1,2 +0,0 @@
|
|||
// 1.20.1 2026-01-13T10:48:06.5779981 Animation Layer Data: animcore
|
||||
643d9b82415b5fbac109dcfb4476752f16363326 data/animcore/ac_animations/animation.layer.json
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
// 1.20.1 2026-01-13T10:48:06.5819961 Animations: animcore
|
||||
730671616cc7fa1e8763c64f8cdcf5ce981583d2 data/animcore/ac_animations/waltz_gentleman.anim.json
|
||||
ea25ceb6798cd833d1b613517e5016df989b4d40 data/animcore/ac_animations/waltz_lady.anim.json
|
||||
|
|
@ -1,2 +0,0 @@
|
|||
// 1.20.1 2026-01-13T10:48:06.5829971 Languages: en_us
|
||||
12951874f3a152154b6a388d367095c1bb30d2b3 assets/animcore/lang/en_us.json
|
||||
|
|
@ -1,32 +0,0 @@
|
|||
{
|
||||
"translation.animcore.command.animation.accept_apply_success": "%s has accepted the application of %s.",
|
||||
"translation.animcore.command.animation.accept_invite_success": "Invitation accepted.",
|
||||
"translation.animcore.command.animation.accept_message_click": "Click here to accept.",
|
||||
"translation.animcore.command.animation.accept_request_success": "Request accepted.",
|
||||
"translation.animcore.command.animation.animation_cooldown": "You cannot perform this operation: Cooling down (%s second(s)).",
|
||||
"translation.animcore.command.animation.animation_expire": "You cannot perform this operation: It has expired.",
|
||||
"translation.animcore.command.animation.animation_json_path": "%s",
|
||||
"translation.animcore.command.animation.animation_operation_cancelled": "Exception: Operation cancelled.",
|
||||
"translation.animcore.command.animation.animation_operation_unsupported": "Error: Unsupported operation.",
|
||||
"translation.animcore.command.animation.animation_out_range": "You cannot perform this operation: The distance is not within %s blocks.",
|
||||
"translation.animcore.command.animation.animation_resource_not_found": "Error: Resource not found, please check if there are any errors in the resource or operation : %s",
|
||||
"translation.animcore.command.animation.animation_to_json": "The animation %s has been stored in the path on %s:",
|
||||
"translation.animcore.command.animation.applied_join_message": "%S§b§l Apply for §r to join your animation. ",
|
||||
"translation.animcore.command.animation.apply_join_message": "Application sent.",
|
||||
"translation.animcore.command.animation.apply_success": "%s has accepted your animation application.",
|
||||
"translation.animcore.command.animation.clear_animations": "Animation cleared.",
|
||||
"translation.animcore.command.animation.command_run_fail": "Command run fail.",
|
||||
"translation.animcore.command.animation.command_run_success": "Command run success.",
|
||||
"translation.animcore.command.animation.invite_message": "Invitation sent.",
|
||||
"translation.animcore.command.animation.invite_success": "%s has accepted your animation invitation.",
|
||||
"translation.animcore.command.animation.invited_message": "%s§c§l invites§r you to animation: %s. ",
|
||||
"translation.animcore.command.animation.list_animation_resource": "The %2$s on %1$s has : %s",
|
||||
"translation.animcore.command.animation.play_animation_fail": "Fail to play animation with: %s",
|
||||
"translation.animcore.command.animation.play_animation_success": "Successfully played animation on %s player(s).",
|
||||
"translation.animcore.command.animation.refresh_animations": "Animation refreshed.",
|
||||
"translation.animcore.command.animation.remove_animation_fail": "Fail to remove animation with: %s",
|
||||
"translation.animcore.command.animation.remove_animation_success": "Successfully removed animation on %s player(s).",
|
||||
"translation.animcore.command.animation.request_message": "Request sent.",
|
||||
"translation.animcore.command.animation.request_success": "%s has accepted your animation request.",
|
||||
"translation.animcore.command.animation.requested_message": "%s§d§l requests§r you to animation: %s. "
|
||||
}
|
||||
|
|
@ -1,32 +0,0 @@
|
|||
{
|
||||
"translation.animcore.command.animation.accept_apply_success": "%s 接受了 %s 的申请。",
|
||||
"translation.animcore.command.animation.accept_invite_success": "已接受邀请。",
|
||||
"translation.animcore.command.animation.accept_message_click": "单击此处同意。",
|
||||
"translation.animcore.command.animation.accept_request_success": "已接受请求。",
|
||||
"translation.animcore.command.animation.animation_cooldown": "你不能执行该操作: 冷却中(%s秒)。",
|
||||
"translation.animcore.command.animation.animation_expire": "你不能执行该操作: 已过期。",
|
||||
"translation.animcore.command.animation.animation_json_path": "%s",
|
||||
"translation.animcore.command.animation.animation_operation_cancelled": "异常: 操作被取消。",
|
||||
"translation.animcore.command.animation.animation_operation_unsupported": "错误: 不支持这样做。",
|
||||
"translation.animcore.command.animation.animation_out_range": "你不能执行该操作: 距离不在%s格以内。",
|
||||
"translation.animcore.command.animation.animation_resource_not_found": "错误: 资源未找到,请检查资源或操作是否有误: %s",
|
||||
"translation.animcore.command.animation.animation_to_json": "动画%s已经存储到%s路径:",
|
||||
"translation.animcore.command.animation.applied_join_message": "%s§b§l 申请§r加入动画。",
|
||||
"translation.animcore.command.animation.apply_join_message": "已发送申请。",
|
||||
"translation.animcore.command.animation.apply_success": "%s 接受了你的动画申请。",
|
||||
"translation.animcore.command.animation.clear_animations": "动画已清除。",
|
||||
"translation.animcore.command.animation.command_run_fail": "命令执行失败。",
|
||||
"translation.animcore.command.animation.command_run_success": "命令执行成功。",
|
||||
"translation.animcore.command.animation.invite_message": "已发送邀请。",
|
||||
"translation.animcore.command.animation.invite_success": "%s 接受了你的动画邀请。",
|
||||
"translation.animcore.command.animation.invited_message": "%s§c§l 邀请§r你进行动画:%s。",
|
||||
"translation.animcore.command.animation.list_animation_resource": "%s侧的%s有:%s",
|
||||
"translation.animcore.command.animation.play_animation_fail": "在这些玩家上播放动画失败:%s",
|
||||
"translation.animcore.command.animation.play_animation_success": "在%s个玩家上播放动画成功。",
|
||||
"translation.animcore.command.animation.refresh_animations": "动画同步状态已刷新。",
|
||||
"translation.animcore.command.animation.remove_animation_fail": "在这些玩家上移除动画失败:%s",
|
||||
"translation.animcore.command.animation.remove_animation_success": "在%s个玩家上移除动画成功。",
|
||||
"translation.animcore.command.animation.request_message": "已发送请求。",
|
||||
"translation.animcore.command.animation.request_success": "%s 接受了你的动画请求。",
|
||||
"translation.animcore.command.animation.requested_message": "%s§d§l 请求§r你进行动画:%s。"
|
||||
}
|
||||
|
|
@ -1,6 +0,0 @@
|
|||
[
|
||||
{
|
||||
"key": "animcore:normal_layers",
|
||||
"priority": 42
|
||||
}
|
||||
]
|
||||
|
|
@ -1,25 +0,0 @@
|
|||
{
|
||||
"camPosOffset": {
|
||||
"relative": true,
|
||||
"x": 0.0,
|
||||
"y": 0.0,
|
||||
"z": 1.0
|
||||
},
|
||||
"heightModifier": 1.0,
|
||||
"key": "animcore:waltz_gentleman",
|
||||
"name": "Waltz-Gentleman",
|
||||
"priority": 0,
|
||||
"withRide": {
|
||||
"componentsAnimation": [
|
||||
"animcore:waltz_lady"
|
||||
],
|
||||
"existTick": 0,
|
||||
"offset": {
|
||||
"x": 0.0,
|
||||
"y": 0.0,
|
||||
"z": 0.0
|
||||
},
|
||||
"xRot": 0.0,
|
||||
"yRot": 0.0
|
||||
}
|
||||
}
|
||||
|
|
@ -1,20 +0,0 @@
|
|||
{
|
||||
"camYaw": 180.0,
|
||||
"heightModifier": 1.0,
|
||||
"key": "animcore:waltz_lady",
|
||||
"name": "Waltz-Lady",
|
||||
"priority": 0,
|
||||
"withRide": {
|
||||
"componentsAnimation": [
|
||||
"animcore:waltz_gentleman"
|
||||
],
|
||||
"existTick": 0,
|
||||
"offset": {
|
||||
"x": 0.0,
|
||||
"y": 0.0,
|
||||
"z": 0.0
|
||||
},
|
||||
"xRot": 0.0,
|
||||
"yRot": 0.0
|
||||
}
|
||||
}
|
||||
|
|
@ -16,46 +16,29 @@
|
|||
package top.leisuretimedock.animationcore;
|
||||
|
||||
|
||||
import top.leisuretimedock.animationcore.animation.service.IAnimationService;
|
||||
import top.leisuretimedock.animationcore.capability.CapabilityUtils;
|
||||
import top.leisuretimedock.animationcore.core.ModChannel;
|
||||
import top.leisuretimedock.animationcore.core.ModCommands;
|
||||
import top.leisuretimedock.animationcore.core.configs.ModConfigs;
|
||||
import top.leisuretimedock.animationcore.example.animation.ModAnimation;
|
||||
import top.leisuretimedock.animationcore.example.capability.ModCapability;
|
||||
import net.minecraftforge.common.MinecraftForge;
|
||||
import net.minecraftforge.eventbus.api.IEventBus;
|
||||
import net.minecraftforge.fml.ModLoadingContext;
|
||||
import net.minecraftforge.event.AddReloadListenerEvent;
|
||||
import net.minecraftforge.eventbus.api.SubscribeEvent;
|
||||
import net.minecraftforge.fml.common.Mod;
|
||||
import net.minecraftforge.fml.config.ModConfig;
|
||||
import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext;
|
||||
import net.minecraftforge.fml.loading.FMLEnvironment;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import top.leisuretimedock.animationcore.animation.AnimationDataManager;
|
||||
import top.leisuretimedock.animationcore.animation.LayerDataManager;
|
||||
|
||||
|
||||
@Mod(AnimationCore.MOD_ID)
|
||||
public class AnimationCore {
|
||||
public static final Logger log = LoggerFactory.getLogger(AnimationCore.class);
|
||||
public static final Logger LOGGER = LoggerFactory.getLogger(AnimationCore.class);
|
||||
public static final String MOD_ID = "animcore";
|
||||
public static final String ENABLE_EXAMPLES_PROPERTY_KEY = "animcore.enable_examples";
|
||||
|
||||
public AnimationCore() {
|
||||
ModLoadingContext modLoadingContext = ModLoadingContext.get();
|
||||
modLoadingContext.registerConfig(ModConfig.Type.SERVER, ModConfigs.Server.SPEC);
|
||||
|
||||
IEventBus modBus = FMLJavaModLoadingContext.get().getModEventBus();
|
||||
IEventBus forgeBus = MinecraftForge.EVENT_BUS;
|
||||
|
||||
CapabilityUtils.registerHandler(forgeBus);
|
||||
ModChannel.register();
|
||||
IAnimationService.register(forgeBus, modBus);
|
||||
ModCommands.registerCommands(forgeBus, modBus);
|
||||
|
||||
if(!FMLEnvironment.production || Boolean.getBoolean(ENABLE_EXAMPLES_PROPERTY_KEY)) {
|
||||
ModCapability.register();
|
||||
ModCapability.addListenerToEvent(forgeBus);
|
||||
ModAnimation.register(forgeBus, modBus);
|
||||
}
|
||||
LOGGER.info("Loading Animation Core");
|
||||
}
|
||||
@Mod.EventBusSubscriber(modid = MOD_ID, bus = Mod.EventBusSubscriber.Bus.FORGE)
|
||||
public static class CommonGame {
|
||||
@SubscribeEvent
|
||||
public static void onAddReloadListeners(AddReloadListenerEvent event) {
|
||||
event.addListener(AnimationDataManager.INSTANCE);
|
||||
event.addListener(LayerDataManager.INSTANCE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,46 +0,0 @@
|
|||
/*
|
||||
* Anim Core mod
|
||||
* Copyright (C) 2026 LeisureTimeDock
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package top.leisuretimedock.animationcore.animation;
|
||||
|
||||
import top.leisuretimedock.animationcore.animation.helper.AnimationDataHelper;
|
||||
import top.leisuretimedock.animationcore.animation.helper.AnimationHelper;
|
||||
import top.leisuretimedock.animationcore.animation.helper.AnimationJsonHelper;
|
||||
import top.leisuretimedock.animationcore.animation.helper.AnimationServiceGetterHelper;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
|
||||
public class AnimationApi {
|
||||
public static AnimationJsonHelper getJsonHelper(MinecraftServer server) {
|
||||
return AnimationJsonHelper.getHelper(server);
|
||||
}
|
||||
|
||||
public static AnimationDataHelper getDataHelper() {
|
||||
return AnimationDataHelper.getHelper();
|
||||
}
|
||||
|
||||
public static AnimationServiceGetterHelper getServiceGetterHelper(ResourceLocation location) {
|
||||
return new AnimationServiceGetterHelper(location);
|
||||
}
|
||||
|
||||
public static AnimationServiceGetterHelper getServiceGetterHelper() {
|
||||
return new AnimationServiceGetterHelper();
|
||||
}
|
||||
|
||||
public static AnimationHelper getHelper(Player player) {
|
||||
return AnimationHelper.getHelper(player);
|
||||
}
|
||||
}
|
||||
|
|
@ -13,8 +13,12 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package top.leisuretimedock.animationcore.animation.data;
|
||||
package top.leisuretimedock.animationcore.animation;
|
||||
|
||||
import io.zershyan.sccore.animation.data.AnimationData;
|
||||
import io.zershyan.sccore.animation.data.GenericAnimationData;
|
||||
import io.zershyan.sccore.animation.data.RawAnimationData;
|
||||
import io.zershyan.sccore.animation.data.Ride;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
|
@ -260,24 +264,23 @@ public class AnimationDataBuilder {
|
|||
AnimationData animationData;
|
||||
|
||||
if (isGeneric) {
|
||||
GenericAnimationData genericData = new GenericAnimationData(key);
|
||||
genericData.heightModifier = heightModifier;
|
||||
genericData.name = genericName;
|
||||
animationData = genericData;
|
||||
animationData = GenericAnimationData.create(key)
|
||||
.withHeightModifier(heightModifier)
|
||||
.withName(genericName);
|
||||
} else {
|
||||
animationData = new RawAnimationData(key);
|
||||
animationData = RawAnimationData.create(key);
|
||||
}
|
||||
|
||||
// Apply common properties
|
||||
animationData.camYaw = camYaw;
|
||||
animationData.camPitch = camPitch;
|
||||
animationData.camRoll = camRoll;
|
||||
animationData.camComputePriority = camComputePriority;
|
||||
animationData.setCamPosOffset(camPosOffset);
|
||||
animationData.camPosOffsetRelative = camPosOffsetRelative;
|
||||
animationData.setLyingType(lyingType);
|
||||
animationData.ride = ride;
|
||||
|
||||
animationData
|
||||
.withCamYaw(camYaw)
|
||||
.withCamPitch(camPitch)
|
||||
.withCamRoll(camRoll)
|
||||
.withCamComputePriority(camComputePriority)
|
||||
.setCamPosOffset(camPosOffset)
|
||||
.withCamPosOffsetRelative(camPosOffsetRelative)
|
||||
.withRide(ride)
|
||||
.setLyingType(lyingType);
|
||||
// Apply post-build actions
|
||||
for (Consumer<AnimationData> action : postBuildActions) {
|
||||
action.accept(animationData);
|
||||
|
|
@ -0,0 +1,113 @@
|
|||
/*
|
||||
* Anim Core mod
|
||||
* Copyright (C) 2026 LeisureTimeDock
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package top.leisuretimedock.animationcore.animation;
|
||||
|
||||
import com.google.gson.GsonBuilder;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParseException;
|
||||
import io.zershyan.sccore.animation.data.AnimationData;
|
||||
import io.zershyan.sccore.animation.data.GenericAnimationData;
|
||||
import io.zershyan.sccore.animation.data.util.AnimJson;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
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 org.jetbrains.annotations.NotNull;
|
||||
import top.leisuretimedock.animationcore.AnimationCore;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
public class AnimationDataManager extends SimpleJsonResourceReloadListener {
|
||||
public static final AnimationDataManager INSTANCE = new AnimationDataManager();
|
||||
public static final Map<ResourceLocation, GenericAnimationData> ANIMATIONS = new LinkedHashMap<>();
|
||||
private AnimationDataManager() {
|
||||
super(new GsonBuilder()
|
||||
.setPrettyPrinting()
|
||||
.disableHtmlEscaping()
|
||||
.create(), "ac_animations");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void apply(@NotNull Map<ResourceLocation, JsonElement> resources,
|
||||
@NotNull ResourceManager resourceManager,
|
||||
@NotNull ProfilerFiller profiler) {
|
||||
ANIMATIONS.clear();
|
||||
AnimationCore.LOGGER.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, "ac_animations",
|
||||
(resourceLocation, inputStreamIoSupplier) -> {
|
||||
String path = resourceLocation.getPath();
|
||||
if (path.endsWith(".anim.json")) {
|
||||
ResourceLocation rl = new ResourceLocation(namespace,
|
||||
path.substring("ac_animations/".length(), path.length() - ".json".length()));
|
||||
|
||||
JsonElement el = resources.get(rl);
|
||||
if (el != null) {
|
||||
rl = new ResourceLocation(namespace,
|
||||
path.substring("ac_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)) {
|
||||
AnimationCore.LOGGER.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
|
||||
}
|
||||
|
||||
registerAnimation(animKey, anim);
|
||||
loadedCount++;
|
||||
AnimationCore.LOGGER.debug("Loaded animation: {} -> {}", animKey, anim.getName());
|
||||
|
||||
} catch (IllegalArgumentException | JsonParseException e) {
|
||||
AnimationCore.LOGGER.error("Parsing error loading animation {}", animKey, e);
|
||||
}
|
||||
}
|
||||
|
||||
AnimationCore.LOGGER.info("Loaded {} animations from data packs", loadedCount);
|
||||
}
|
||||
|
||||
public static void registerAnimation(ResourceLocation key, GenericAnimationData animation) {
|
||||
ANIMATIONS.put(key, animation);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,114 @@
|
|||
/*
|
||||
* Anim Core mod
|
||||
* Copyright (C) 2026 LeisureTimeDock
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package top.leisuretimedock.animationcore.animation;
|
||||
|
||||
import com.google.gson.GsonBuilder;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonParseException;
|
||||
import io.zershyan.sccore.animation.data.util.AnimLayerJson;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.server.packs.PackType;
|
||||
import net.minecraft.server.packs.resources.ResourceManager;
|
||||
import net.minecraft.server.packs.resources.SimpleJsonResourceReloadListener;
|
||||
import net.minecraft.util.profiling.ProfilerFiller;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import top.leisuretimedock.animationcore.AnimationCore;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
public class LayerDataManager extends SimpleJsonResourceReloadListener {
|
||||
public static final LayerDataManager INSTANCE = new LayerDataManager();
|
||||
public static final Map<ResourceLocation, Integer> LAYERS = new LinkedHashMap<>();
|
||||
|
||||
private LayerDataManager() {
|
||||
super(new GsonBuilder()
|
||||
.setPrettyPrinting()
|
||||
.disableHtmlEscaping()
|
||||
.create(), "ac_animations");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void apply(@NotNull Map<ResourceLocation, JsonElement> resources,
|
||||
@NotNull ResourceManager resourceManager,
|
||||
@NotNull ProfilerFiller profiler) {
|
||||
LAYERS.clear();
|
||||
AnimationCore.LOGGER.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, "ac_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 {
|
||||
Map<ResourceLocation, Integer> parsedLayers = getResourceLocationIntegerMap(entry);
|
||||
|
||||
loadedCount += parsedLayers.size();
|
||||
AnimationCore.LOGGER.debug("Loaded {} layer configurations from {}",
|
||||
parsedLayers.size(), entry.getKey().getNamespace());
|
||||
|
||||
} catch (IllegalArgumentException | JsonParseException e) {
|
||||
AnimationCore.LOGGER.error("Parsing error loading layer configuration", e);
|
||||
}
|
||||
}
|
||||
|
||||
AnimationCore.LOGGER.info("Loaded {} layer configurations from data packs", loadedCount);
|
||||
}
|
||||
|
||||
private static @NotNull Map<ResourceLocation, Integer> getResourceLocationIntegerMap(Map.Entry<ResourceLocation, JsonElement> entry) {
|
||||
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 (containsKey(layerKey)) {
|
||||
AnimationCore.LOGGER.debug("Overriding layer {} with priority {}", layerKey, priority);
|
||||
}
|
||||
registerLayer(layerKey, priority);
|
||||
});
|
||||
return parsedLayers;
|
||||
}
|
||||
|
||||
public static void registerLayer(ResourceLocation key, int priority) {
|
||||
LAYERS.put(key, priority);
|
||||
}
|
||||
|
||||
public static boolean containsKey(ResourceLocation key) {
|
||||
return LAYERS.containsKey(key);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,206 +0,0 @@
|
|||
/*
|
||||
* Anim Core mod
|
||||
* Copyright (C) 2026 LeisureTimeDock
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package top.leisuretimedock.animationcore.animation.capability;
|
||||
|
||||
import top.leisuretimedock.animationcore.AnimationCore;
|
||||
import top.leisuretimedock.animationcore.animation.capability.inter.IAnimationCapability;
|
||||
import top.leisuretimedock.animationcore.animation.network.toclient.AnimationCapabilityPacket;
|
||||
import top.leisuretimedock.animationcore.animation.register.AnimationRegistry;
|
||||
import top.leisuretimedock.animationcore.animation.service.AnimationService;
|
||||
import top.leisuretimedock.animationcore.capability.CapabilityUtils;
|
||||
import top.leisuretimedock.animationcore.capability.data.ICapabilitySync;
|
||||
import top.leisuretimedock.animationcore.capability.data.player.SimplePlayerCapabilitySync;
|
||||
import top.leisuretimedock.animationcore.capability.network.SimpleCapabilityPacket;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
|
||||
public class AnimationDataCapability extends SimplePlayerCapabilitySync implements IAnimationCapability {
|
||||
public static final ResourceLocation key = new ResourceLocation(AnimationCore.MOD_ID, "animation_data");
|
||||
|
||||
public static final String AnimMap = "AnimMap";
|
||||
public static final String RideAnimLayer = "RideAnimLayer";
|
||||
public static final String RideAnimation = "RideAnimation";
|
||||
|
||||
private final Map<ResourceLocation, ResourceLocation> animMap = new HashMap<>();
|
||||
private ResourceLocation rideAnimLayer;
|
||||
private ResourceLocation rideAnimation;
|
||||
|
||||
@Override
|
||||
public void mergeAnimations(Map<ResourceLocation, ResourceLocation> animations) {
|
||||
animations.forEach((key, value) -> {
|
||||
if (AnimationRegistry.getLayers().containsKey(key)) {
|
||||
if (AnimationService.INSTANCE.isAnimationPresent(value)) {
|
||||
if(Objects.equals(rideAnimLayer, key)) {
|
||||
removeRiderAnimation();
|
||||
}
|
||||
this.animMap.put(key, value);
|
||||
setDirty(true);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean mergeAnimation(ResourceLocation layer, ResourceLocation animation) {
|
||||
if (AnimationRegistry.getLayers().containsKey(layer)) {
|
||||
if (AnimationService.INSTANCE.isAnimationPresent(animation)) {
|
||||
if(Objects.equals(rideAnimLayer, layer)) {
|
||||
removeRiderAnimation();
|
||||
}
|
||||
this.animMap.put(layer, animation);
|
||||
setDirty(true);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean removeAnimation(ResourceLocation layer) {
|
||||
ResourceLocation remove = this.animMap.remove(layer);
|
||||
if(remove != null) {
|
||||
setDirty(true);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public ResourceLocation getAnimation(ResourceLocation layer) {
|
||||
return animMap.getOrDefault(layer, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<ResourceLocation, ResourceLocation> getAnimations() {
|
||||
return Map.copyOf(animMap);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearAnimations() {
|
||||
this.animMap.clear();
|
||||
setDirty(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAnimationPresent(ResourceLocation layer) {
|
||||
return animMap.containsKey(layer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResourceLocation getRiderAnimLayer() {
|
||||
return rideAnimLayer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResourceLocation getRiderAnimation() {
|
||||
return rideAnimation;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setRiderAnimation(@NotNull ResourceLocation layer, @NotNull ResourceLocation animation) {
|
||||
if(AnimationService.INSTANCE.isAnimationLayerPresent(layer)) {
|
||||
this.rideAnimLayer = layer;
|
||||
this.rideAnimation = animation;
|
||||
if(animMap.get(layer) != null) {
|
||||
animMap.remove(layer);
|
||||
}
|
||||
setDirty(true);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeRiderAnimation() {
|
||||
this.rideAnimLayer = null;
|
||||
this.rideAnimation = null;
|
||||
setDirty(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void copyFrom(ICapabilitySync<?> oldData) {
|
||||
IAnimationCapability data = (IAnimationCapability) oldData;
|
||||
this.animMap.clear();
|
||||
this.animMap.putAll(data.getAnimations());
|
||||
this.rideAnimLayer = data.getRiderAnimLayer();
|
||||
this.rideAnimation = data.getRiderAnimation();
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompoundTag toTag(CompoundTag tag) {
|
||||
if(!animMap.isEmpty()) {
|
||||
CompoundTag animMapTag = new CompoundTag();
|
||||
animMap.forEach((string, animation) ->
|
||||
animMapTag.putString(string.toString(), animation.toString())
|
||||
);
|
||||
tag.put(AnimMap, animMapTag);
|
||||
}
|
||||
if(rideAnimLayer != null) tag.putString(RideAnimLayer, rideAnimLayer.toString());
|
||||
if(rideAnimation != null) tag.putString(RideAnimation, rideAnimation.toString());
|
||||
return tag;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fromTag(CompoundTag tag) {
|
||||
this.animMap.clear();
|
||||
this.rideAnimLayer = null;
|
||||
this.rideAnimation = null;
|
||||
if(tag.contains(AnimMap)) {
|
||||
CompoundTag animMapTag = tag.getCompound(AnimMap);
|
||||
animMapTag.getAllKeys().forEach(key -> this.animMap.put(
|
||||
new ResourceLocation(key),
|
||||
new ResourceLocation(animMapTag.getString(key))
|
||||
));
|
||||
}
|
||||
if(tag.contains(RideAnimLayer)) this.rideAnimLayer = new ResourceLocation(tag.getString(RideAnimLayer));
|
||||
if(tag.contains(RideAnimation)) this.rideAnimation = new ResourceLocation(tag.getString(RideAnimation));
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResourceLocation getKey() {
|
||||
return key;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SimpleCapabilityPacket<Player> getDefaultPacket() {
|
||||
return new AnimationCapabilityPacket(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void attachInit(Player player) {
|
||||
Map<ResourceLocation, ResourceLocation> map = new HashMap<>(this.animMap);
|
||||
map.forEach((key, value) -> {
|
||||
if(!AnimationService.INSTANCE.isAnimationLayerPresent(key)) this.animMap.remove(key);
|
||||
if(!AnimationService.INSTANCE.isAnimationPresent(value)) this.animMap.remove(key);
|
||||
});
|
||||
if(rideAnimLayer != null && !AnimationService.INSTANCE.isAnimationLayerPresent(rideAnimLayer)) {
|
||||
removeRiderAnimation();
|
||||
}
|
||||
}
|
||||
|
||||
public static Optional<IAnimationCapability> getCapability(Player player){
|
||||
return Optional.ofNullable(CapabilityUtils.getPlayerCapability(
|
||||
player, AnimationDataCapability.key, IAnimationCapability.class
|
||||
));
|
||||
}
|
||||
}
|
||||
|
|
@ -1,128 +0,0 @@
|
|||
/*
|
||||
* Anim Core mod
|
||||
* Copyright (C) 2026 LeisureTimeDock
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package top.leisuretimedock.animationcore.animation.capability;
|
||||
|
||||
import top.leisuretimedock.animationcore.AnimationCore;
|
||||
import top.leisuretimedock.animationcore.animation.network.toclient.RawAnimationCapabilityPacket;
|
||||
import top.leisuretimedock.animationcore.animation.register.AnimationRegistry;
|
||||
import top.leisuretimedock.animationcore.capability.CapabilityUtils;
|
||||
import top.leisuretimedock.animationcore.capability.data.ICapabilitySync;
|
||||
import top.leisuretimedock.animationcore.capability.data.player.SimplePlayerCapabilitySync;
|
||||
import top.leisuretimedock.animationcore.capability.network.SimpleCapabilityPacket;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
public class RawAnimationDataCapability extends SimplePlayerCapabilitySync {
|
||||
public static final ResourceLocation key = new ResourceLocation(AnimationCore.MOD_ID, "raw_animation_data");
|
||||
|
||||
public static final String AnimMap = "AnimMap";
|
||||
|
||||
private final Map<ResourceLocation, ResourceLocation> animMap = new HashMap<>();
|
||||
|
||||
public boolean mergeAnimation(ResourceLocation layer, ResourceLocation animation) {
|
||||
if (AnimationRegistry.getLayers().containsKey(layer)) {
|
||||
this.animMap.put(layer, animation);
|
||||
setDirty(true);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean removeAnimation(ResourceLocation layer) {
|
||||
ResourceLocation remove = this.animMap.remove(layer);
|
||||
if(remove != null) {
|
||||
setDirty(true);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public ResourceLocation getAnimation(ResourceLocation layer) {
|
||||
return animMap.getOrDefault(layer, null);
|
||||
}
|
||||
|
||||
public Map<ResourceLocation, ResourceLocation> getAnimations() {
|
||||
return Map.copyOf(animMap);
|
||||
}
|
||||
|
||||
public void clearAnimations() {
|
||||
this.animMap.clear();
|
||||
setDirty(true);
|
||||
}
|
||||
|
||||
public boolean isAnimationPresent(ResourceLocation layer) {
|
||||
return animMap.containsKey(layer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void copyFrom(ICapabilitySync<?> oldData) {
|
||||
RawAnimationDataCapability data = (RawAnimationDataCapability) oldData;
|
||||
this.animMap.clear();
|
||||
this.animMap.putAll(data.getAnimations());
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompoundTag toTag(CompoundTag tag) {
|
||||
if(!animMap.isEmpty()) {
|
||||
CompoundTag animMapTag = new CompoundTag();
|
||||
animMap.forEach((string, animation) ->
|
||||
animMapTag.putString(string.toString(), animation.toString())
|
||||
);
|
||||
tag.put(AnimMap, animMapTag);
|
||||
}
|
||||
return tag;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fromTag(CompoundTag tag) {
|
||||
this.animMap.clear();
|
||||
if(tag.contains(AnimMap)) {
|
||||
CompoundTag animMapTag = tag.getCompound(AnimMap);
|
||||
animMapTag.getAllKeys().forEach(key -> this.animMap.put(
|
||||
new ResourceLocation(key),
|
||||
new ResourceLocation(animMapTag.getString(key))
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResourceLocation getKey() {
|
||||
return key;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SimpleCapabilityPacket<Player> getDefaultPacket() {
|
||||
return new RawAnimationCapabilityPacket(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void attachInit(Player entity) {
|
||||
clearAnimations();
|
||||
}
|
||||
|
||||
public static Optional<RawAnimationDataCapability> getCapability(Player player){
|
||||
return Optional.ofNullable(CapabilityUtils.getPlayerCapability(
|
||||
player, RawAnimationDataCapability.key, RawAnimationDataCapability.class
|
||||
));
|
||||
}
|
||||
}
|
||||
|
|
@ -1,38 +0,0 @@
|
|||
/*
|
||||
* Anim Core mod
|
||||
* Copyright (C) 2026 LeisureTimeDock
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package top.leisuretimedock.animationcore.animation.capability.inter;
|
||||
|
||||
import top.leisuretimedock.animationcore.capability.data.ICapabilitySync;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
public interface IAnimationCapability extends ICapabilitySync<Player> {
|
||||
void mergeAnimations(Map<ResourceLocation, ResourceLocation> animations);
|
||||
boolean mergeAnimation(ResourceLocation layer, ResourceLocation animation);
|
||||
boolean removeAnimation(ResourceLocation layer);
|
||||
@Nullable ResourceLocation getAnimation(ResourceLocation layer);
|
||||
Map<ResourceLocation, ResourceLocation> getAnimations();
|
||||
void clearAnimations();
|
||||
boolean isAnimationPresent(ResourceLocation layer);
|
||||
|
||||
ResourceLocation getRiderAnimLayer();
|
||||
ResourceLocation getRiderAnimation();
|
||||
void setRiderAnimation(ResourceLocation layer, ResourceLocation animation);
|
||||
void removeRiderAnimation();
|
||||
}
|
||||
|
|
@ -1,140 +0,0 @@
|
|||
/*
|
||||
* Anim Core mod
|
||||
* Copyright (C) 2026 LeisureTimeDock
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package top.leisuretimedock.animationcore.animation.command;
|
||||
|
||||
import top.leisuretimedock.animationcore.AnimationCore;
|
||||
import top.leisuretimedock.animationcore.animation.AnimationApi;
|
||||
import top.leisuretimedock.animationcore.animation.command.exception.ApiBackException;
|
||||
import top.leisuretimedock.animationcore.animation.utils.ApiBack;
|
||||
import top.leisuretimedock.animationcore.core.configs.ModConfigs;
|
||||
import top.leisuretimedock.animationcore.core.datagen.ModLang;
|
||||
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
|
||||
import com.mojang.brigadier.context.CommandContext;
|
||||
import net.minecraft.ChatFormatting;
|
||||
import net.minecraft.commands.CommandSourceStack;
|
||||
import net.minecraft.commands.arguments.EntityArgument;
|
||||
import net.minecraft.network.chat.ClickEvent;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.network.chat.MutableComponent;
|
||||
import net.minecraft.network.chat.Style;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
|
||||
import static net.minecraft.commands.Commands.argument;
|
||||
import static net.minecraft.commands.Commands.literal;
|
||||
|
||||
public class ApplyCommand {
|
||||
public static void register(LiteralArgumentBuilder<CommandSourceStack> animCommand) {
|
||||
animCommand
|
||||
.then(literal("apply")
|
||||
.then(argument("target", EntityArgument.player())
|
||||
.executes(ApplyCommand::apply)
|
||||
)
|
||||
.then(literal("accept")
|
||||
.then(argument("player", EntityArgument.player())
|
||||
.executes(ApplyCommand::acceptApply)
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
private static int apply(CommandContext<CommandSourceStack> context) {
|
||||
CommandSourceStack source = context.getSource();
|
||||
try {
|
||||
ServerPlayer target = EntityArgument.getPlayer(context, "target");
|
||||
ServerPlayer player = source.getPlayerOrException();
|
||||
|
||||
Entity vehicle = target.getVehicle();
|
||||
if(vehicle == null) throw new ApiBackException(ApiBack.UNSUPPORTED);
|
||||
|
||||
ApiBack back = AnimationApi.getHelper(player).applyAnimation(target);
|
||||
if(back == ApiBack.COOLDOWN) {
|
||||
int cooldown = ModConfigs.Server.applyCooldown.get();
|
||||
throw ApiBackException.withCooldown(cooldown);
|
||||
}
|
||||
if(back != ApiBack.SUCCESS) throw new ApiBackException(back);
|
||||
|
||||
//click event
|
||||
Style pStyle = Style.EMPTY.withBold(true).withColor(ChatFormatting.GREEN).withClickEvent(
|
||||
new ClickEvent(ClickEvent.Action.RUN_COMMAND, "/animcore anim apply accept " + player.getName().getString())
|
||||
).withUnderlined(true);
|
||||
|
||||
//send message to all participants
|
||||
for (Entity passenger : vehicle.getPassengers()) {
|
||||
passenger.sendSystemMessage(Component.translatable(
|
||||
ModLang.TranslatableMessage.APPLIED_JOIN_MESSAGE.getKey(),
|
||||
player.getName().copy()
|
||||
).append(Component.translatable(
|
||||
ModLang.TranslatableMessage.ACCEPT_MESSAGE_CLICK.getKey()
|
||||
).setStyle(pStyle)));
|
||||
}
|
||||
source.sendSuccess(() -> Component.translatable(
|
||||
ModLang.TranslatableMessage.APPLY_JOIN_MESSAGE.getKey()
|
||||
).withStyle(ChatFormatting.GREEN), true);
|
||||
return 1;
|
||||
} catch (ApiBackException e) {
|
||||
source.sendFailure(e.getCommandFailBack().withStyle(ChatFormatting.RED));
|
||||
} catch (Exception e) {
|
||||
source.sendFailure(Component.translatable(
|
||||
ModLang.TranslatableMessage.COMMAND_RUN_FAIL.getKey()
|
||||
).withStyle(ChatFormatting.RED));
|
||||
AnimationCore.log.error(e.getMessage());
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
private static int acceptApply(CommandContext<CommandSourceStack> context) {
|
||||
CommandSourceStack source = context.getSource();
|
||||
try {
|
||||
ServerPlayer player = source.getPlayerOrException();
|
||||
ServerPlayer applier = EntityArgument.getPlayer(context, "applier");
|
||||
|
||||
Entity vehicle = player.getVehicle();
|
||||
if(vehicle == null) throw new ApiBackException(ApiBack.UNSUPPORTED);
|
||||
|
||||
ApiBack back = AnimationApi.getHelper(player).acceptApply(applier);
|
||||
if(back == ApiBack.OUT_RANGE) throw ApiBackException.withOutRange(ModConfigs.Server.applyValidDistance.get());
|
||||
if(back != ApiBack.SUCCESS) throw new ApiBackException(back);
|
||||
|
||||
//define message
|
||||
MutableComponent successMessage = Component.translatable(
|
||||
ModLang.TranslatableMessage.ACCEPT_APPLY_SUCCESS.getKey(),
|
||||
player.getName().copy(), applier.getName().copy()
|
||||
).withStyle(ChatFormatting.GREEN);
|
||||
|
||||
//send message
|
||||
source.sendSuccess(() -> successMessage, true);
|
||||
for (Entity passenger : vehicle.getPassengers()) {
|
||||
if(!passenger.getUUID().equals(player.getUUID()) && !passenger.getUUID().equals(applier.getUUID())) {
|
||||
passenger.sendSystemMessage(successMessage);
|
||||
}
|
||||
}
|
||||
applier.sendSystemMessage(Component.translatable(
|
||||
ModLang.TranslatableMessage.APPLY_SUCCESS.getKey(),
|
||||
player.getName().copy()
|
||||
).withStyle(ChatFormatting.GREEN));
|
||||
} catch (ApiBackException e) {
|
||||
source.sendFailure(e.getCommandFailBack().withStyle(ChatFormatting.RED));
|
||||
} catch (Exception e) {
|
||||
source.sendFailure(Component.translatable(
|
||||
ModLang.TranslatableMessage.COMMAND_RUN_FAIL.getKey()
|
||||
).withStyle(ChatFormatting.RED));
|
||||
AnimationCore.log.error(e.getMessage());
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,142 +0,0 @@
|
|||
/*
|
||||
* Anim Core mod
|
||||
* Copyright (C) 2026 LeisureTimeDock
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package top.leisuretimedock.animationcore.animation.command;
|
||||
|
||||
import top.leisuretimedock.animationcore.AnimationCore;
|
||||
import top.leisuretimedock.animationcore.animation.AnimationApi;
|
||||
import top.leisuretimedock.animationcore.animation.command.argument.AnimationArgument;
|
||||
import top.leisuretimedock.animationcore.animation.command.argument.AnimationLayerArgument;
|
||||
import top.leisuretimedock.animationcore.animation.command.exception.ApiBackException;
|
||||
import top.leisuretimedock.animationcore.animation.utils.ApiBack;
|
||||
import top.leisuretimedock.animationcore.core.configs.ModConfigs;
|
||||
import top.leisuretimedock.animationcore.core.datagen.ModLang;
|
||||
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
|
||||
import com.mojang.brigadier.context.CommandContext;
|
||||
import net.minecraft.ChatFormatting;
|
||||
import net.minecraft.commands.CommandSourceStack;
|
||||
import net.minecraft.commands.arguments.EntityArgument;
|
||||
import net.minecraft.network.chat.ClickEvent;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.network.chat.Style;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
import static net.minecraft.commands.Commands.argument;
|
||||
import static net.minecraft.commands.Commands.literal;
|
||||
|
||||
public class InviteCommand {
|
||||
public static void register(LiteralArgumentBuilder<CommandSourceStack> animCommand) {
|
||||
animCommand.then(literal("invite")
|
||||
.then(argument("players", EntityArgument.players())
|
||||
.then(argument("layer", AnimationLayerArgument.layer())
|
||||
.then(argument("anim", AnimationArgument.animation())
|
||||
.executes(InviteCommand::invite)
|
||||
)
|
||||
)
|
||||
)
|
||||
.then(literal("accept")
|
||||
.then(argument("player", EntityArgument.player())
|
||||
.executes(InviteCommand::acceptInvite)
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
private static int invite(CommandContext<CommandSourceStack> context) {
|
||||
CommandSourceStack source = context.getSource();
|
||||
try {
|
||||
ServerPlayer player = source.getPlayerOrException();
|
||||
Collection<ServerPlayer> players = EntityArgument.getPlayers(context, "players");
|
||||
|
||||
String layerString = AnimationLayerArgument.getLayer(context, "layer");
|
||||
String animString = AnimationArgument.getAnimation(context, "anim");
|
||||
ResourceLocation layer = new ResourceLocation(layerString);
|
||||
ResourceLocation anim = new ResourceLocation(animString);
|
||||
|
||||
//test info present
|
||||
List<UUID> targets = players.stream().map(Entity::getUUID).toList();
|
||||
ApiBack back = AnimationApi.getHelper(player).inviteAnimation(layer, anim, targets);
|
||||
if(back == ApiBack.COOLDOWN) {
|
||||
int cooldown = ModConfigs.Server.inviteCooldown.get();
|
||||
throw ApiBackException.withCooldown(cooldown);
|
||||
}
|
||||
if(back != ApiBack.SUCCESS) throw new ApiBackException(back);
|
||||
|
||||
//click event
|
||||
Style pStyle = Style.EMPTY.withBold(true).withColor(ChatFormatting.GREEN).withClickEvent(
|
||||
new ClickEvent(ClickEvent.Action.RUN_COMMAND, "/animcore anim invite accept " + player.getName().getString())
|
||||
).withUnderlined(true);
|
||||
//send message
|
||||
for (ServerPlayer target : players) {
|
||||
target.sendSystemMessage(Component.translatable(
|
||||
ModLang.TranslatableMessage.INVITED_MESSAGE.getKey(),
|
||||
player.getName().copy(),
|
||||
anim.toString()
|
||||
).append(Component.translatable(
|
||||
ModLang.TranslatableMessage.ACCEPT_MESSAGE_CLICK.getKey()
|
||||
).setStyle(pStyle)));
|
||||
}
|
||||
source.sendSuccess(() -> Component.translatable(
|
||||
ModLang.TranslatableMessage.INVITE_MESSAGE.getKey()
|
||||
).withStyle(ChatFormatting.GREEN), true);
|
||||
return 1;
|
||||
} catch (ApiBackException e) {
|
||||
source.sendFailure(e.getCommandFailBack().withStyle(ChatFormatting.RED));
|
||||
} catch (Exception e) {
|
||||
source.sendFailure(Component.translatable(
|
||||
ModLang.TranslatableMessage.COMMAND_RUN_FAIL.getKey()
|
||||
).withStyle(ChatFormatting.RED));
|
||||
AnimationCore.log.error(e.getMessage());
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
private static int acceptInvite(CommandContext<CommandSourceStack> context) {
|
||||
CommandSourceStack source = context.getSource();
|
||||
try {
|
||||
ServerPlayer player = source.getPlayerOrException();
|
||||
ServerPlayer inviter = EntityArgument.getPlayer(context, "player");
|
||||
|
||||
//play animation
|
||||
ApiBack back = AnimationApi.getHelper(player).acceptInvite(inviter);
|
||||
if(back == ApiBack.OUT_RANGE) throw ApiBackException.withOutRange(ModConfigs.Server.inviteValidDistance.get());
|
||||
if(back != ApiBack.SUCCESS) throw new ApiBackException(back);
|
||||
|
||||
//send message
|
||||
source.sendSuccess(() -> Component.translatable(
|
||||
ModLang.TranslatableMessage.ACCEPT_INVITE_SUCCESS.getKey()
|
||||
).withStyle(ChatFormatting.GREEN), true);
|
||||
inviter.sendSystemMessage(Component.translatable(
|
||||
ModLang.TranslatableMessage.INVITE_SUCCESS.getKey(),
|
||||
player.getName().copy()
|
||||
).withStyle(ChatFormatting.GREEN));
|
||||
return 1;
|
||||
} catch (ApiBackException e) {
|
||||
source.sendFailure(e.getCommandFailBack().withStyle(ChatFormatting.RED));
|
||||
} catch (Exception e) {
|
||||
source.sendFailure(Component.translatable(
|
||||
ModLang.TranslatableMessage.COMMAND_RUN_FAIL.getKey()
|
||||
).withStyle(ChatFormatting.RED));
|
||||
AnimationCore.log.error(e.getMessage());
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,131 +0,0 @@
|
|||
/*
|
||||
* Anim Core mod
|
||||
* Copyright (C) 2026 LeisureTimeDock
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package top.leisuretimedock.animationcore.animation.command;
|
||||
|
||||
import top.leisuretimedock.animationcore.AnimationCore;
|
||||
import top.leisuretimedock.animationcore.animation.helper.AnimationJsonHelper;
|
||||
import top.leisuretimedock.animationcore.core.datagen.ModLang;
|
||||
import com.mojang.brigadier.arguments.BoolArgumentType;
|
||||
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
|
||||
import com.mojang.brigadier.context.CommandContext;
|
||||
import net.minecraft.ChatFormatting;
|
||||
import net.minecraft.commands.CommandSourceStack;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.network.chat.MutableComponent;
|
||||
|
||||
import java.nio.file.Path;
|
||||
|
||||
import static net.minecraft.commands.Commands.argument;
|
||||
import static net.minecraft.commands.Commands.literal;
|
||||
|
||||
public class JsonCommand {
|
||||
public static void register(LiteralArgumentBuilder<CommandSourceStack> animCommand) {
|
||||
animCommand.then(literal("json").requires(cs -> cs.hasPermission(2))
|
||||
.then(literal("clearFile").executes(JsonCommand::clearJson))
|
||||
.then(literal("generate")
|
||||
.then(literal("anim")
|
||||
.then(literal("example").executes(JsonCommand::generateExample))
|
||||
.executes(context -> generateJson(context, false, false))
|
||||
.then(argument("reset", BoolArgumentType.bool())
|
||||
.executes(context ->
|
||||
generateJson(context, false, BoolArgumentType.getBool(context, "reset"))
|
||||
)
|
||||
)
|
||||
)
|
||||
.then(literal("layer")
|
||||
.executes(context -> generateJson(context, true, false))
|
||||
.then(argument("reset", BoolArgumentType.bool())
|
||||
.executes(context ->
|
||||
generateJson(context, true, BoolArgumentType.getBool(context, "reset"))
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
private static int generateJson(CommandContext<CommandSourceStack> context, boolean isLayer, boolean isReset) {
|
||||
CommandSourceStack source = context.getSource();
|
||||
try {
|
||||
//generate
|
||||
AnimationJsonHelper helper = AnimationJsonHelper.getHelper(source.getServer());
|
||||
Path path = helper.generateJson(isLayer, isReset);
|
||||
|
||||
if(path == null) throw new Exception();
|
||||
MutableComponent component;
|
||||
String key = ModLang.TranslatableMessage.ANIMATION_TO_JSON.getKey();
|
||||
if(isLayer) component = Component.translatable(key, "layer", "Server");
|
||||
else component = Component.translatable(key, "anim", "Server");
|
||||
|
||||
component.withStyle(ChatFormatting.GREEN).append(Component.translatable(
|
||||
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()
|
||||
).withStyle(ChatFormatting.RED));
|
||||
AnimationCore.log.error(e.getMessage());
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
private static int clearJson(CommandContext<CommandSourceStack> context) {
|
||||
CommandSourceStack source = context.getSource();
|
||||
try {
|
||||
//clear path
|
||||
AnimationJsonHelper.getHelper(source.getServer()).clearPath();
|
||||
} catch (Exception e) {
|
||||
source.sendFailure(Component.translatable(
|
||||
ModLang.TranslatableMessage.COMMAND_RUN_FAIL.getKey()
|
||||
).withStyle(ChatFormatting.RED));
|
||||
AnimationCore.log.error(e.getMessage());
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
private static int generateExample(CommandContext<CommandSourceStack> context) {
|
||||
CommandSourceStack source = context.getSource();
|
||||
try {
|
||||
//generate
|
||||
AnimationJsonHelper helper = AnimationJsonHelper.getHelper(source.getServer());
|
||||
Path path = helper.generateExample();
|
||||
if(path == null) throw new Exception();
|
||||
|
||||
//send message
|
||||
MutableComponent component = Component.translatable(
|
||||
ModLang.TranslatableMessage.ANIMATION_TO_JSON.getKey(),
|
||||
"anim example", "Server"
|
||||
).withStyle(ChatFormatting.GREEN);
|
||||
component.append(Component.translatable(
|
||||
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()
|
||||
).withStyle(ChatFormatting.RED));
|
||||
AnimationCore.log.error(e.getMessage());
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,88 +0,0 @@
|
|||
/*
|
||||
* Anim Core mod
|
||||
* Copyright (C) 2026 LeisureTimeDock
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package top.leisuretimedock.animationcore.animation.command;
|
||||
|
||||
import top.leisuretimedock.animationcore.animation.register.AnimationRegistry;
|
||||
import top.leisuretimedock.animationcore.core.datagen.ModLang;
|
||||
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
|
||||
import com.mojang.brigadier.context.CommandContext;
|
||||
import net.minecraft.ChatFormatting;
|
||||
import net.minecraft.commands.CommandSourceStack;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.network.chat.MutableComponent;
|
||||
import net.minecraft.network.chat.Style;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
|
||||
import static net.minecraft.commands.Commands.literal;
|
||||
|
||||
public class ListServerCommand {
|
||||
public static void register(LiteralArgumentBuilder<CommandSourceStack> animCommand) {
|
||||
animCommand.then(literal("list")
|
||||
.then(literal("layers").executes(ListServerCommand::listLayers))
|
||||
.then(literal("serverAnimations")
|
||||
.executes(ListServerCommand::listAnimations)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
private static int listAnimations(CommandContext<CommandSourceStack> context) {
|
||||
try {
|
||||
CommandSourceStack source = context.getSource();
|
||||
List<ResourceLocation> list = AnimationRegistry.getAnimations().keySet().stream().toList();
|
||||
source.sendSuccess(() -> Component.translatable(
|
||||
ModLang.TranslatableMessage.LIST_ANIMATION_RESOURCE.getKey(),
|
||||
"Server", "Animations", getString(list)
|
||||
), false);
|
||||
return 1;
|
||||
} catch (Exception ignored) {}
|
||||
return 0;
|
||||
}
|
||||
|
||||
private static int listLayers(CommandContext<CommandSourceStack> context) {
|
||||
try {
|
||||
CommandSourceStack source = context.getSource();
|
||||
List<ResourceLocation> list = AnimationRegistry.getLayers().keySet().stream().toList();
|
||||
source.sendSuccess(() -> Component.translatable(
|
||||
ModLang.TranslatableMessage.LIST_ANIMATION_RESOURCE.getKey(),
|
||||
"Server", "Layers", getString(list)
|
||||
), false);
|
||||
return 1;
|
||||
} catch (Exception ignored) {}
|
||||
return 0;
|
||||
}
|
||||
|
||||
public static Component getString(List<ResourceLocation> list) {
|
||||
MutableComponent component = Component.empty();
|
||||
Random random = new Random(System.currentTimeMillis());
|
||||
for (int i = 0; i < list.size(); i++) {
|
||||
component.append(Component.literal(list.get(i).toString()).withStyle(randomColor(random)));
|
||||
if(i < list.size()-1){
|
||||
component.append(", ").withStyle(ChatFormatting.WHITE);
|
||||
}
|
||||
}
|
||||
component.append(".").withStyle(Style.EMPTY.withColor(ChatFormatting.RED));
|
||||
return component;
|
||||
}
|
||||
|
||||
private static ChatFormatting randomColor(Random random){
|
||||
int i = random.nextInt(14) + 1;
|
||||
ChatFormatting byId = ChatFormatting.getById(i);
|
||||
return byId == null ? ChatFormatting.WHITE : byId;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,263 +0,0 @@
|
|||
/*
|
||||
* Anim Core mod
|
||||
* Copyright (C) 2026 LeisureTimeDock
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package top.leisuretimedock.animationcore.animation.command;
|
||||
|
||||
import top.leisuretimedock.animationcore.AnimationCore;
|
||||
import top.leisuretimedock.animationcore.animation.AnimationApi;
|
||||
import top.leisuretimedock.animationcore.animation.command.argument.AnimationArgument;
|
||||
import top.leisuretimedock.animationcore.animation.command.argument.AnimationLayerArgument;
|
||||
import top.leisuretimedock.animationcore.animation.command.exception.ApiBackException;
|
||||
import top.leisuretimedock.animationcore.animation.data.AnimationData;
|
||||
import top.leisuretimedock.animationcore.animation.helper.AnimationHelper;
|
||||
import top.leisuretimedock.animationcore.animation.helper.AnimationServiceGetterHelper;
|
||||
import top.leisuretimedock.animationcore.animation.network.toclient.AnimationClearPacket;
|
||||
import top.leisuretimedock.animationcore.animation.service.IAnimationService;
|
||||
import top.leisuretimedock.animationcore.animation.service.RawAnimationService;
|
||||
import top.leisuretimedock.animationcore.animation.utils.ApiBack;
|
||||
import top.leisuretimedock.animationcore.core.ModChannel;
|
||||
import top.leisuretimedock.animationcore.core.datagen.ModLang;
|
||||
import com.mojang.brigadier.arguments.BoolArgumentType;
|
||||
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
|
||||
import com.mojang.brigadier.context.CommandContext;
|
||||
import net.minecraft.ChatFormatting;
|
||||
import net.minecraft.commands.CommandSourceStack;
|
||||
import net.minecraft.commands.arguments.EntityArgument;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.network.chat.MutableComponent;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import static net.minecraft.commands.Commands.argument;
|
||||
import static net.minecraft.commands.Commands.literal;
|
||||
|
||||
public class PlayCommand {
|
||||
public static void register(LiteralArgumentBuilder<CommandSourceStack> animCommand){
|
||||
animCommand
|
||||
.then(literal("play")
|
||||
.then(argument("players", EntityArgument.players())
|
||||
.requires(cs -> cs.hasPermission(2))
|
||||
.then(argument("layer", AnimationLayerArgument.layer())
|
||||
.then(argument("animation", AnimationArgument.animation())
|
||||
.executes(context -> playAnimation(context, false))
|
||||
.then(argument("withRide", BoolArgumentType.bool())
|
||||
.executes(context -> playAnimation(
|
||||
context, BoolArgumentType.getBool(context, "withRide")
|
||||
))
|
||||
.then(argument("forced", BoolArgumentType.bool())
|
||||
.executes(context -> playAnimation(
|
||||
context, BoolArgumentType.getBool(context, "withRide"))
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
.then(literal("self")
|
||||
.then(argument("layer", AnimationLayerArgument.layer())
|
||||
.then(argument("animation", AnimationArgument.animation())
|
||||
.executes(context -> playAnimation(context, false))
|
||||
.then(argument("withRide", BoolArgumentType.bool())
|
||||
.executes(context -> playAnimation(
|
||||
context, BoolArgumentType.getBool(context, "withRide")
|
||||
))
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
.then(literal("clear")
|
||||
.executes(PlayCommand::clearAnimation)
|
||||
.then(argument("players", EntityArgument.players())
|
||||
.requires(cs -> cs.hasPermission(2))
|
||||
.executes(PlayCommand::clearAnimation)
|
||||
.then(argument("layer", AnimationLayerArgument.layer())
|
||||
.executes(PlayCommand::removeAnimation)
|
||||
)
|
||||
)
|
||||
.then(argument("layer", AnimationLayerArgument.layer())
|
||||
.executes(PlayCommand::removeAnimation)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
private static int playAnimation(CommandContext<CommandSourceStack> context, boolean withRide) {
|
||||
CommandSourceStack source = context.getSource();
|
||||
try {
|
||||
Collection<ServerPlayer> targets = new ArrayList<>();
|
||||
ServerPlayer player = source.getPlayerOrException();
|
||||
try { targets.addAll(EntityArgument.getPlayers(context, "players"));}
|
||||
catch (Exception ignored) {}
|
||||
String layerString = AnimationLayerArgument.getLayer(context, "layer");
|
||||
String animString = AnimationArgument.getAnimation(context, "animation");
|
||||
ResourceLocation layer = new ResourceLocation(layerString);
|
||||
ResourceLocation anim = new ResourceLocation(animString);
|
||||
|
||||
//play with players
|
||||
IAnimationService<?, ?> helper = AnimationServiceGetterHelper.create(anim).getService();
|
||||
if (helper == null) throw new ApiBackException(ApiBack.RESOURCE_NOT_FOUND);
|
||||
AnimationData animationData = helper.getAnimation(anim);
|
||||
if(animationData == null) throw new ApiBackException(ApiBack.RESOURCE_NOT_FOUND);
|
||||
if(!targets.isEmpty()) {
|
||||
Set<ServerPlayer> playerSet = Set.copyOf(targets);
|
||||
playerSet.forEach(p -> {
|
||||
if(withRide) {
|
||||
boolean forced = false;
|
||||
try { forced = BoolArgumentType.getBool(context, "forced");}
|
||||
catch (Exception ignored) {}
|
||||
ApiBack back = helper.playAnimationWithRide(p, layer, animationData, forced);
|
||||
if(back == ApiBack.SUCCESS) targets.remove(p);
|
||||
} else {
|
||||
ApiBack back = helper.playAnimation(p, layer, animationData);
|
||||
if (back == ApiBack.SUCCESS) targets.remove(p);
|
||||
}
|
||||
});
|
||||
|
||||
int successNum = playerSet.size() - targets.size();
|
||||
if(successNum > 0) {
|
||||
source.sendSuccess(() -> Component.translatable(
|
||||
ModLang.TranslatableMessage.PLAY_ANIMATION_SUCCESS.getKey(),
|
||||
successNum
|
||||
).withStyle(ChatFormatting.GREEN), true);
|
||||
}
|
||||
List<ServerPlayer> list = targets.stream().toList();
|
||||
if(!list.isEmpty()) {
|
||||
MutableComponent failPlayers = Component.literal("");
|
||||
for (int i = 0; i < list.size(); i++) {
|
||||
failPlayers.append(list.get(i).getName().copy());
|
||||
if (i < list.size() - 1) {
|
||||
failPlayers.append(", ");
|
||||
}
|
||||
}
|
||||
failPlayers.append(".");
|
||||
source.sendFailure(Component.translatable(
|
||||
ModLang.TranslatableMessage.PLAY_ANIMATION_FAIL.getKey(),
|
||||
failPlayers
|
||||
).withStyle(ChatFormatting.RED));
|
||||
}
|
||||
} else {
|
||||
//play with self
|
||||
ApiBack back;
|
||||
RawAnimationService instance = RawAnimationService.INSTANCE;
|
||||
if(withRide) back = instance.playAnimationWithRide(player, layer, animationData, false);
|
||||
else back = instance.playAnimation(player, layer, animationData);
|
||||
|
||||
if(back != ApiBack.SUCCESS) throw new ApiBackException(back);
|
||||
|
||||
source.sendSuccess(() -> Component.translatable(
|
||||
ModLang.TranslatableMessage.COMMAND_RUN_SUCCESS.getKey()
|
||||
).withStyle(ChatFormatting.GREEN), true);
|
||||
}
|
||||
return 1;
|
||||
} catch (Exception e) {
|
||||
source.sendFailure(Component.translatable(
|
||||
ModLang.TranslatableMessage.COMMAND_RUN_FAIL.getKey()
|
||||
).withStyle(ChatFormatting.RED));
|
||||
AnimationCore.log.error(e.getMessage());
|
||||
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
private static int removeAnimation(CommandContext<CommandSourceStack> context) {
|
||||
CommandSourceStack source = context.getSource();
|
||||
try {
|
||||
Collection<ServerPlayer> targets = new ArrayList<>();
|
||||
ServerPlayer player = source.getPlayerOrException();
|
||||
try { targets.addAll(EntityArgument.getPlayers(context, "players"));}
|
||||
catch (Exception ignored) {}
|
||||
String layer = AnimationLayerArgument.getLayer(context, "layer");
|
||||
ResourceLocation layerLocation = new ResourceLocation(layer);
|
||||
|
||||
//remove with players
|
||||
if(!targets.isEmpty()) {
|
||||
Set<ServerPlayer> playerSet = Set.copyOf(targets);
|
||||
playerSet.forEach(p -> {
|
||||
ApiBack back = AnimationApi.getHelper(p).removeAnimation(layerLocation);
|
||||
if (back == ApiBack.SUCCESS) targets.remove(p);
|
||||
});
|
||||
int successNum = playerSet.size() - targets.size();
|
||||
if(successNum > 0) {
|
||||
source.sendSuccess(() -> Component.translatable(
|
||||
ModLang.TranslatableMessage.REMOVE_ANIMATION_SUCCESS.getKey(),
|
||||
successNum
|
||||
).withStyle(ChatFormatting.GREEN), true);
|
||||
}
|
||||
List<ServerPlayer> list = targets.stream().toList();
|
||||
if(!list.isEmpty()) {
|
||||
MutableComponent failPlayers = Component.literal("");
|
||||
for (int i = 0; i < list.size(); i++) {
|
||||
failPlayers.append(list.get(i).getName().copy());
|
||||
if (i < list.size() - 1) {
|
||||
failPlayers.append(", ");
|
||||
}
|
||||
}
|
||||
failPlayers.append(".");
|
||||
source.sendFailure(Component.translatable(
|
||||
ModLang.TranslatableMessage.REMOVE_ANIMATION_FAIL.getKey(),
|
||||
failPlayers
|
||||
).withStyle(ChatFormatting.RED));
|
||||
}
|
||||
} else {
|
||||
ApiBack back = AnimationApi.getHelper(player).removeAnimation(layerLocation);
|
||||
if (back != ApiBack.SUCCESS) throw new ApiBackException(back);
|
||||
|
||||
source.sendSuccess(() -> Component.translatable(
|
||||
ModLang.TranslatableMessage.COMMAND_RUN_SUCCESS.getKey()
|
||||
).withStyle(ChatFormatting.GREEN), true);
|
||||
}
|
||||
return 1;
|
||||
} catch (ApiBackException e){
|
||||
source.sendFailure(e.getCommandFailBack().withStyle(ChatFormatting.RED));
|
||||
} catch (Exception e) {
|
||||
source.sendFailure(Component.translatable(
|
||||
ModLang.TranslatableMessage.COMMAND_RUN_FAIL.getKey()
|
||||
).withStyle(ChatFormatting.RED));
|
||||
AnimationCore.log.error(e.getMessage());
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
private static int clearAnimation(CommandContext<CommandSourceStack> context) {
|
||||
CommandSourceStack source = context.getSource();
|
||||
try {
|
||||
Collection<ServerPlayer> players;
|
||||
try {players = EntityArgument.getPlayers(context, "players");}
|
||||
catch (Exception ignored) { players = Set.of(source.getPlayerOrException()); }
|
||||
Set.copyOf(players).forEach(player -> {
|
||||
AnimationHelper helper = AnimationApi.getHelper(player);
|
||||
helper.clearAnimation();
|
||||
helper.detachAnimation();
|
||||
ModChannel.sendToPlayer(new AnimationClearPacket((ResourceLocation) null), player);
|
||||
});
|
||||
source.sendSuccess(() -> Component.translatable(
|
||||
ModLang.TranslatableMessage.CLEAR_ANIMATIONS.getKey()
|
||||
).withStyle(ChatFormatting.GREEN), true);
|
||||
return 1;
|
||||
} catch (Exception e) {
|
||||
source.sendFailure(Component.translatable(
|
||||
ModLang.TranslatableMessage.COMMAND_RUN_FAIL.getKey()
|
||||
).withStyle(ChatFormatting.RED));
|
||||
AnimationCore.log.error(e.getMessage());
|
||||
}
|
||||
return 0;
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -1,141 +0,0 @@
|
|||
/*
|
||||
* Anim Core mod
|
||||
* Copyright (C) 2026 LeisureTimeDock
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package top.leisuretimedock.animationcore.animation.command;
|
||||
|
||||
import top.leisuretimedock.animationcore.AnimationCore;
|
||||
import top.leisuretimedock.animationcore.animation.AnimationApi;
|
||||
import top.leisuretimedock.animationcore.animation.command.argument.AnimationArgument;
|
||||
import top.leisuretimedock.animationcore.animation.command.argument.AnimationLayerArgument;
|
||||
import top.leisuretimedock.animationcore.animation.command.exception.ApiBackException;
|
||||
import top.leisuretimedock.animationcore.animation.utils.ApiBack;
|
||||
import top.leisuretimedock.animationcore.core.configs.ModConfigs;
|
||||
import top.leisuretimedock.animationcore.core.datagen.ModLang;
|
||||
import com.mojang.brigadier.arguments.BoolArgumentType;
|
||||
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
|
||||
import com.mojang.brigadier.context.CommandContext;
|
||||
import net.minecraft.ChatFormatting;
|
||||
import net.minecraft.commands.CommandSourceStack;
|
||||
import net.minecraft.commands.arguments.EntityArgument;
|
||||
import net.minecraft.network.chat.ClickEvent;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.network.chat.Style;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
|
||||
import static net.minecraft.commands.Commands.argument;
|
||||
import static net.minecraft.commands.Commands.literal;
|
||||
|
||||
/**
|
||||
* Request target player play animation.
|
||||
*/
|
||||
public class RequestCommand {
|
||||
public static void register(LiteralArgumentBuilder<CommandSourceStack> animCommand) {
|
||||
animCommand.then(literal("request")
|
||||
.then(argument("player", EntityArgument.player()).then(
|
||||
argument("layer", AnimationLayerArgument.layer())
|
||||
.then(argument("animation", AnimationArgument.animation())
|
||||
.requires(cs -> cs.hasPermission(2))
|
||||
.executes(context -> request(context, false))
|
||||
.then(argument("withRide", BoolArgumentType.bool())
|
||||
.executes(context -> request(
|
||||
context, BoolArgumentType.getBool(context, "withRide")
|
||||
))
|
||||
)
|
||||
)
|
||||
))
|
||||
.then(literal("acceptRequest")
|
||||
.then(argument("player", EntityArgument.player())
|
||||
.executes(RequestCommand::acceptRequest)
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
private static int request(CommandContext<CommandSourceStack> context, boolean withRide) {
|
||||
CommandSourceStack source = context.getSource();
|
||||
try {
|
||||
ServerPlayer player = source.getPlayerOrException();
|
||||
ServerPlayer target = EntityArgument.getPlayer(context, "player");
|
||||
String layerString = AnimationLayerArgument.getLayer(context, "layer");
|
||||
String animString = AnimationArgument.getAnimation(context, "animation");
|
||||
ResourceLocation layer = new ResourceLocation(layerString);
|
||||
ResourceLocation anim = new ResourceLocation(animString);
|
||||
|
||||
ApiBack back = AnimationApi.getHelper(player).requestAnimation(target, layer, anim, withRide);
|
||||
if(back == ApiBack.COOLDOWN) {
|
||||
int cooldown = ModConfigs.Server.requestCooldown.get();
|
||||
throw ApiBackException.withCooldown(cooldown);
|
||||
}
|
||||
if(back != ApiBack.SUCCESS) throw new ApiBackException(back);
|
||||
|
||||
//click event
|
||||
Style pStyle = Style.EMPTY.withBold(true).withColor(ChatFormatting.GREEN).withClickEvent(
|
||||
new ClickEvent(ClickEvent.Action.RUN_COMMAND, "/animcore anim request acceptRequest " + player.getName().getString())
|
||||
).withUnderlined(true);
|
||||
|
||||
//send message
|
||||
target.sendSystemMessage(Component.translatable(
|
||||
ModLang.TranslatableMessage.REQUESTED_MESSAGE.getKey(),
|
||||
player.getName().copy(),
|
||||
anim.toString()
|
||||
).append(Component.translatable(
|
||||
ModLang.TranslatableMessage.ACCEPT_MESSAGE_CLICK.getKey()
|
||||
).setStyle(pStyle)));
|
||||
source.sendSuccess(() -> Component.translatable(
|
||||
ModLang.TranslatableMessage.REQUEST_MESSAGE.getKey()
|
||||
).withStyle(ChatFormatting.GREEN), true);
|
||||
return 1;
|
||||
} catch (ApiBackException e) {
|
||||
source.sendFailure(e.getCommandFailBack().withStyle(ChatFormatting.RED));
|
||||
} catch (Exception e) {
|
||||
source.sendFailure(Component.translatable(
|
||||
ModLang.TranslatableMessage.COMMAND_RUN_FAIL.getKey()
|
||||
).withStyle(ChatFormatting.RED));
|
||||
AnimationCore.log.error(e.getMessage());
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
private static int acceptRequest(CommandContext<CommandSourceStack> context) {
|
||||
CommandSourceStack source = context.getSource();
|
||||
try {
|
||||
ServerPlayer player = source.getPlayerOrException();
|
||||
ServerPlayer requestor = EntityArgument.getPlayer(context, "requestor");
|
||||
|
||||
//play
|
||||
ApiBack back = AnimationApi.getHelper(player).acceptRequest(requestor);
|
||||
if(back != ApiBack.SUCCESS) throw new ApiBackException(back);
|
||||
|
||||
//send message
|
||||
source.sendSuccess(() -> Component.translatable(
|
||||
ModLang.TranslatableMessage.ACCEPT_REQUEST_SUCCESS.getKey()
|
||||
).withStyle(ChatFormatting.GREEN), true);
|
||||
requestor.sendSystemMessage(Component.translatable(
|
||||
ModLang.TranslatableMessage.REQUEST_SUCCESS.getKey(),
|
||||
player.getName().copy()
|
||||
).withStyle(ChatFormatting.GREEN));
|
||||
return 1;
|
||||
} catch (ApiBackException e) {
|
||||
source.sendFailure(e.getCommandFailBack().withStyle(ChatFormatting.RED));
|
||||
} catch (Exception e) {
|
||||
source.sendFailure(Component.translatable(
|
||||
ModLang.TranslatableMessage.COMMAND_RUN_FAIL.getKey()
|
||||
).withStyle(ChatFormatting.RED));
|
||||
AnimationCore.log.error(e.getMessage());
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,120 +0,0 @@
|
|||
/*
|
||||
* Anim Core mod
|
||||
* Copyright (C) 2026 LeisureTimeDock
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package top.leisuretimedock.animationcore.animation.command.argument;
|
||||
|
||||
import top.leisuretimedock.animationcore.animation.data.GenericAnimationData;
|
||||
import top.leisuretimedock.animationcore.animation.register.AnimationRegistry;
|
||||
import top.leisuretimedock.animationcore.animation.register.RawAnimationRegistry;
|
||||
import com.mojang.brigadier.StringReader;
|
||||
import com.mojang.brigadier.arguments.ArgumentType;
|
||||
import com.mojang.brigadier.context.CommandContext;
|
||||
import com.mojang.brigadier.suggestion.Suggestions;
|
||||
import com.mojang.brigadier.suggestion.SuggestionsBuilder;
|
||||
import net.minecraft.commands.CommandSourceStack;
|
||||
import net.minecraft.commands.SharedSuggestionProvider;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraftforge.api.distmarker.Dist;
|
||||
import net.minecraftforge.api.distmarker.OnlyIn;
|
||||
import net.minecraftforge.fml.DistExecutor;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public class AnimationArgument implements ArgumentType<String> {
|
||||
private static final Supplier<Collection<String>> EXAMPLES = AnimationArgument::getAnimationNames;
|
||||
|
||||
private static Supplier<Set<String>> animationNames;
|
||||
public AnimationArgument() {
|
||||
resetAnimationNames();
|
||||
}
|
||||
|
||||
public static void resetAnimationNames() {
|
||||
Set<String> set = new HashSet<>(getAnimationNames());
|
||||
Set<String> strings = DistExecutor.unsafeCallWhenOn(Dist.CLIENT, () ->
|
||||
AnimationArgument::getAnimationNamesClient
|
||||
);
|
||||
if (strings != null && !strings.isEmpty()) set.addAll(strings);
|
||||
animationNames = () -> set;
|
||||
}
|
||||
|
||||
private static Set<String> getAnimationNames(){
|
||||
Set<String> set = new HashSet<>();
|
||||
AnimationRegistry.getAnimations().forEach((key, value) -> {
|
||||
String name = value.getName();
|
||||
if(name != null && !set.contains(name)) {
|
||||
set.add(name);
|
||||
} else set.add(key.toString());
|
||||
});
|
||||
return set;
|
||||
}
|
||||
|
||||
@OnlyIn(Dist.CLIENT)
|
||||
private static Set<String> getAnimationNamesClient() {
|
||||
Set<String> set = new HashSet<>();
|
||||
RawAnimationRegistry.getAnimations().keySet().forEach(location ->
|
||||
set.add(location.toString())
|
||||
);
|
||||
return set;
|
||||
}
|
||||
|
||||
public static AnimationArgument animation() {
|
||||
return new AnimationArgument();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static ResourceLocation getAnimationByName(String name) {
|
||||
for (GenericAnimationData animation : AnimationRegistry.getAnimations().values()) {
|
||||
if (Objects.equals(animation.getName(), name)) {
|
||||
return animation.getKey();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static String getAnimation(CommandContext<CommandSourceStack> context, String name) {
|
||||
String argument = context.getArgument(name, String.class);
|
||||
if(argument.contains(":")) return argument;
|
||||
ResourceLocation animationByName = getAnimationByName(argument);
|
||||
if(animationByName == null) return argument;
|
||||
else return animationByName.toString();
|
||||
}
|
||||
|
||||
public <S> CompletableFuture<Suggestions> listSuggestions(CommandContext<S> context, SuggestionsBuilder builder) {
|
||||
return SharedSuggestionProvider.suggest(animationNames.get(), builder);
|
||||
}
|
||||
|
||||
public Collection<String> getExamples() {
|
||||
return EXAMPLES.get();
|
||||
}
|
||||
|
||||
public String parse(StringReader reader) {
|
||||
int start = reader.getCursor();
|
||||
while (reader.canRead() && canRead(reader.peek())) {
|
||||
reader.skip();
|
||||
}
|
||||
return reader.getString().substring(start, reader.getCursor());
|
||||
}
|
||||
|
||||
private static boolean canRead(char peek) {
|
||||
boolean origin = StringReader.isAllowedInUnquotedString(peek);
|
||||
return origin || peek == ':';
|
||||
}
|
||||
}
|
||||
|
|
@ -1,84 +0,0 @@
|
|||
/*
|
||||
* Anim Core mod
|
||||
* Copyright (C) 2026 LeisureTimeDock
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package top.leisuretimedock.animationcore.animation.command.argument;
|
||||
|
||||
import top.leisuretimedock.animationcore.animation.register.AnimationRegistry;
|
||||
import com.mojang.brigadier.StringReader;
|
||||
import com.mojang.brigadier.arguments.ArgumentType;
|
||||
import com.mojang.brigadier.context.CommandContext;
|
||||
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
||||
import com.mojang.brigadier.exceptions.DynamicCommandExceptionType;
|
||||
import com.mojang.brigadier.suggestion.Suggestions;
|
||||
import com.mojang.brigadier.suggestion.SuggestionsBuilder;
|
||||
import net.minecraft.commands.CommandSourceStack;
|
||||
import net.minecraft.commands.SharedSuggestionProvider;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class AnimationLayerArgument implements ArgumentType<String> {
|
||||
private static final Supplier<Collection<String>> EXAMPLES = () -> AnimationRegistry.getLayers().keySet().stream()
|
||||
.map(ResourceLocation::toString).collect(Collectors.toSet());
|
||||
private static final DynamicCommandExceptionType UNKNOWN_TYPE = new DynamicCommandExceptionType(
|
||||
layer -> Component.literal("Unknow layer : " + layer.toString())
|
||||
);
|
||||
|
||||
private final Supplier<Set<String>> animationLayers;
|
||||
public AnimationLayerArgument() {
|
||||
this.animationLayers = () -> AnimationRegistry.getLayers().keySet().stream()
|
||||
.map(ResourceLocation::toString).collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
public static AnimationLayerArgument layer() {
|
||||
return new AnimationLayerArgument();
|
||||
}
|
||||
|
||||
public static String getLayer(CommandContext<CommandSourceStack> context, String name) {
|
||||
return context.getArgument(name, String.class);
|
||||
}
|
||||
|
||||
public <S> CompletableFuture<Suggestions> listSuggestions(CommandContext<S> context, SuggestionsBuilder builder) {
|
||||
return SharedSuggestionProvider.suggest(animationLayers.get(), builder);
|
||||
}
|
||||
|
||||
public Collection<String> getExamples() {
|
||||
return EXAMPLES.get();
|
||||
}
|
||||
|
||||
public String parse(StringReader reader) throws CommandSyntaxException {
|
||||
int start = reader.getCursor();
|
||||
while (reader.canRead() && canRead(reader.peek())) {
|
||||
reader.skip();
|
||||
}
|
||||
String s = reader.getString().substring(start, reader.getCursor());
|
||||
if (!animationLayers.get().contains(s)) {
|
||||
throw UNKNOWN_TYPE.create(s);
|
||||
} else {
|
||||
return s;
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean canRead(char peek) {
|
||||
boolean origin = StringReader.isAllowedInUnquotedString(peek);
|
||||
return origin || peek == ':';
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,53 +0,0 @@
|
|||
/*
|
||||
* Anim Core mod
|
||||
* Copyright (C) 2026 LeisureTimeDock
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package top.leisuretimedock.animationcore.animation.command.client;
|
||||
|
||||
import top.leisuretimedock.animationcore.animation.command.ListServerCommand;
|
||||
import top.leisuretimedock.animationcore.animation.register.RawAnimationRegistry;
|
||||
import top.leisuretimedock.animationcore.core.datagen.ModLang;
|
||||
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
|
||||
import com.mojang.brigadier.context.CommandContext;
|
||||
import net.minecraft.commands.CommandSourceStack;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraftforge.api.distmarker.Dist;
|
||||
import net.minecraftforge.api.distmarker.OnlyIn;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static net.minecraft.commands.Commands.literal;
|
||||
|
||||
@OnlyIn(Dist.CLIENT)
|
||||
public class ListClientCommand {
|
||||
public static void register(LiteralArgumentBuilder<CommandSourceStack> animCommand) {
|
||||
animCommand.then(literal("list").then(literal("clientAnimations")
|
||||
.executes(ListClientCommand::listAnimations))
|
||||
);
|
||||
}
|
||||
|
||||
private static int listAnimations(CommandContext<CommandSourceStack> context) {
|
||||
try {
|
||||
CommandSourceStack source = context.getSource();
|
||||
List<ResourceLocation> list = RawAnimationRegistry.getAnimations().keySet().stream().toList();
|
||||
source.sendSuccess(() -> Component.translatable(
|
||||
ModLang.TranslatableMessage.LIST_ANIMATION_RESOURCE.getKey(),
|
||||
"Client", "Animations", ListServerCommand.getString(list)
|
||||
), false);
|
||||
return 1;
|
||||
} catch (Exception ignored) {}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,73 +0,0 @@
|
|||
/*
|
||||
* Anim Core mod
|
||||
* Copyright (C) 2026 LeisureTimeDock
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package top.leisuretimedock.animationcore.animation.command.client;
|
||||
|
||||
import top.leisuretimedock.animationcore.AnimationCore;
|
||||
import top.leisuretimedock.animationcore.animation.AnimationApi;
|
||||
import top.leisuretimedock.animationcore.animation.register.AnimationRegistry;
|
||||
import top.leisuretimedock.animationcore.core.datagen.ModLang;
|
||||
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
|
||||
import com.mojang.brigadier.context.CommandContext;
|
||||
import dev.kosmx.playerAnim.api.layered.IAnimation;
|
||||
import dev.kosmx.playerAnim.minecraftApi.PlayerAnimationAccess;
|
||||
import net.minecraft.ChatFormatting;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.player.LocalPlayer;
|
||||
import net.minecraft.commands.CommandSourceStack;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraftforge.api.distmarker.Dist;
|
||||
import net.minecraftforge.api.distmarker.OnlyIn;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
import static net.minecraft.commands.Commands.literal;
|
||||
|
||||
@OnlyIn(Dist.CLIENT)
|
||||
public class RefreshCommand {
|
||||
public static void register(LiteralArgumentBuilder<CommandSourceStack> animCommand){
|
||||
animCommand.then(literal("refresh").executes(RefreshCommand::refresh));
|
||||
}
|
||||
|
||||
private static int refresh(CommandContext<CommandSourceStack> ctx){
|
||||
CommandSourceStack source = ctx.getSource();
|
||||
try {
|
||||
Minecraft instance = Minecraft.getInstance();
|
||||
LocalPlayer player = instance.player;
|
||||
if(player == null) throw new RuntimeException();
|
||||
AnimationApi.getHelper(player).refreshAnimation();
|
||||
|
||||
for (ResourceLocation layer : Set.copyOf(AnimationRegistry.getLayers().keySet())) {
|
||||
PlayerAnimationAccess.PlayerAssociatedAnimationData playerAssociatedData = PlayerAnimationAccess.getPlayerAssociatedData(player);
|
||||
IAnimation iAnimation = playerAssociatedData.get(layer);
|
||||
if(iAnimation == null) continue;
|
||||
ResourceLocation playing = AnimationApi.getHelper(player).getAnimationPlaying(layer);
|
||||
if(playing == null) playerAssociatedData.set(layer, null);
|
||||
}
|
||||
|
||||
source.sendSuccess(() -> Component.translatable(
|
||||
ModLang.TranslatableMessage.REFRESH_ANIMATIONS.getKey()
|
||||
).withStyle(ChatFormatting.GREEN), true);
|
||||
} catch (Exception e) {
|
||||
source.sendFailure(Component.translatable(
|
||||
ModLang.TranslatableMessage.COMMAND_RUN_FAIL.getKey()
|
||||
).withStyle(ChatFormatting.RED));
|
||||
AnimationCore.log.error(e.getMessage());
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,63 +0,0 @@
|
|||
/*
|
||||
* Anim Core mod
|
||||
* Copyright (C) 2026 LeisureTimeDock
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package top.leisuretimedock.animationcore.animation.command.exception;
|
||||
|
||||
import top.leisuretimedock.animationcore.animation.utils.ApiBack;
|
||||
import top.leisuretimedock.animationcore.core.datagen.ModLang;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.network.chat.MutableComponent;
|
||||
|
||||
public class ApiBackException extends Exception {
|
||||
private final ApiBack apiBack;
|
||||
private Object[] args;
|
||||
public ApiBackException(ApiBack apiBack) {
|
||||
this.apiBack = apiBack;
|
||||
}
|
||||
|
||||
public ApiBackException(ApiBack apiBack, Object... args) {
|
||||
this.apiBack = apiBack;
|
||||
this.args = args;
|
||||
}
|
||||
|
||||
public static ApiBackException withCooldown(Integer cooldown) {
|
||||
return new ApiBackException(ApiBack.COOLDOWN, cooldown);
|
||||
}
|
||||
|
||||
public static ApiBackException withOutRange(Integer distance) {
|
||||
return new ApiBackException(ApiBack.OUT_RANGE, distance);
|
||||
}
|
||||
|
||||
public MutableComponent getCommandFailBack() {
|
||||
if(args != null && args.length > 0) {
|
||||
return Component.translatable(getLang(), args);
|
||||
} else {
|
||||
return Component.translatable(getLang());
|
||||
}
|
||||
}
|
||||
|
||||
public String getLang() {
|
||||
return switch (apiBack) {
|
||||
case FAIL -> ModLang.TranslatableMessage.COMMAND_RUN_FAIL.getKey();
|
||||
case UNSUPPORTED -> ModLang.TranslatableMessage.ANIMATION_OPERATION_UNSUPPORTED.getKey();
|
||||
case COOLDOWN -> ModLang.TranslatableMessage.ANIMATION_COOLDOWN.getKey();
|
||||
case OUT_RANGE -> ModLang.TranslatableMessage.ANIMATION_OUT_RANGE.getKey();
|
||||
case OPERATION_EXPIRE -> ModLang.TranslatableMessage.ANIMATION_EXPIRE.getKey();
|
||||
case RESOURCE_NOT_FOUND -> ModLang.TranslatableMessage.ANIMATION_RESOURCE_NOT_FOUND.getKey();
|
||||
case SUCCESS -> ModLang.TranslatableMessage.COMMAND_RUN_SUCCESS.getKey();
|
||||
case BE_CANCELLED -> ModLang.TranslatableMessage.ANIMATION_OPERATION_CANCELLED.getKey();
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
@ -1,49 +0,0 @@
|
|||
/*
|
||||
* Anim Core mod
|
||||
* Copyright (C) 2026 LeisureTimeDock
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package top.leisuretimedock.animationcore.animation.command.exception;
|
||||
|
||||
import top.leisuretimedock.animationcore.core.datagen.ModLang;
|
||||
import net.minecraft.ChatFormatting;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.network.chat.Style;
|
||||
|
||||
public class CommandComponentException extends Exception {
|
||||
private final Component component;
|
||||
private final boolean isCommandFail;
|
||||
public CommandComponentException(ModLang.TranslatableMessage message, Object ... args) {
|
||||
super("Expected command exception.");
|
||||
this.component = Component.translatable(
|
||||
message.getKey(),
|
||||
args
|
||||
).withStyle(ChatFormatting.RED);
|
||||
this.isCommandFail = true;
|
||||
}
|
||||
public CommandComponentException(ModLang.TranslatableMessage message, Style style, Object ... args) {
|
||||
super("Expected command exception.");
|
||||
this.component = Component.translatable(
|
||||
message.getKey(),
|
||||
args
|
||||
).withStyle(style);
|
||||
this.isCommandFail = false;
|
||||
}
|
||||
|
||||
public Component getCommandFailBack() {
|
||||
return isCommandFail ? Component.translatable(
|
||||
ModLang.TranslatableMessage.COMMAND_RUN_FAIL.getKey(),
|
||||
component
|
||||
).withStyle(ChatFormatting.RED) : component;
|
||||
}
|
||||
}
|
||||
|
|
@ -13,7 +13,7 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package top.leisuretimedock.animationcore.animation.data.util;
|
||||
package top.leisuretimedock.animationcore.animation.data;
|
||||
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonObject;
|
||||
|
|
@ -58,7 +58,7 @@ public abstract class ACAnimationLayerProvider implements DataProvider {
|
|||
try {
|
||||
return DataProvider.saveStable(output, jsonArray, outputPath);
|
||||
} catch (Exception e) {
|
||||
AnimationCore.log.error("Failed to save animation layer data", e);
|
||||
AnimationCore.LOGGER.error("Failed to save animation layer data", e);
|
||||
return CompletableFuture.failedFuture(e);
|
||||
}
|
||||
}
|
||||
|
|
@ -13,11 +13,11 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package top.leisuretimedock.animationcore.animation.data.util;
|
||||
package top.leisuretimedock.animationcore.animation.data;
|
||||
|
||||
import com.google.gson.JsonObject;
|
||||
import top.leisuretimedock.animationcore.animation.data.GenericAnimationData;
|
||||
import top.leisuretimedock.animationcore.animation.data.Ride;
|
||||
import io.zershyan.sccore.animation.data.GenericAnimationData;
|
||||
import io.zershyan.sccore.animation.data.Ride;
|
||||
import net.minecraft.data.CachedOutput;
|
||||
import net.minecraft.data.DataGenerator;
|
||||
import net.minecraft.data.DataProvider;
|
||||
|
|
@ -1,258 +0,0 @@
|
|||
/*
|
||||
* Anim Core mod
|
||||
* Copyright (C) 2026 LeisureTimeDock
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package top.leisuretimedock.animationcore.animation.data;
|
||||
|
||||
import dev.kosmx.playerAnim.core.data.KeyframeAnimation;
|
||||
import dev.kosmx.playerAnim.minecraftApi.PlayerAnimationRegistry;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.nbt.ListTag;
|
||||
import net.minecraft.nbt.StringTag;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
import net.minecraftforge.api.distmarker.Dist;
|
||||
import net.minecraftforge.api.distmarker.OnlyIn;
|
||||
import net.minecraftforge.common.util.INBTSerializable;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class AnimationData implements INBTSerializable<CompoundTag> {
|
||||
protected ResourceLocation key;
|
||||
float camYaw;
|
||||
float camPitch;
|
||||
float camRoll;
|
||||
int camComputePriority;
|
||||
private Vec3 camPosOffset = new Vec3(0.0F, 0.0F, 0.0F);
|
||||
boolean camPosOffsetRelative;
|
||||
private @Nullable GenericAnimationData.LyingType lyingType;
|
||||
protected @Nullable Ride ride;
|
||||
|
||||
public enum LyingType {
|
||||
RIGHT("RIGHT", 0),
|
||||
LEFT("LEFT", 1),
|
||||
FRONT("FRONT", 2),
|
||||
BACK("BACK", 3);
|
||||
private final String name;
|
||||
private final int id;
|
||||
LyingType(String name, int id) {
|
||||
this.name = name;
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public int getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static LyingType getLyingType(int id) {
|
||||
for (LyingType type : LyingType.values()) {
|
||||
if (type.id == id) {
|
||||
return type;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public ResourceLocation getKey() {
|
||||
return key;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@OnlyIn(Dist.CLIENT)
|
||||
public KeyframeAnimation getAnimation() {
|
||||
return PlayerAnimationRegistry.getAnimation(key);
|
||||
}
|
||||
|
||||
public @Nullable Ride getRide() {
|
||||
return ride;
|
||||
}
|
||||
|
||||
public void setLyingType(@Nullable LyingType lyingType) {
|
||||
this.lyingType = lyingType;
|
||||
}
|
||||
|
||||
public float getCamRoll() {
|
||||
return camRoll;
|
||||
}
|
||||
|
||||
public float getCamPitch() {
|
||||
return camPitch;
|
||||
}
|
||||
|
||||
public float getCamYaw() {
|
||||
return camYaw;
|
||||
}
|
||||
|
||||
public int getCamComputePriority() {
|
||||
return camComputePriority;
|
||||
}
|
||||
|
||||
public boolean isCamPosOffsetRelative() {
|
||||
return camPosOffsetRelative;
|
||||
}
|
||||
|
||||
public Vec3 getCamPosOffset() {
|
||||
return camPosOffset;
|
||||
}
|
||||
|
||||
public @Nullable GenericAnimationData.LyingType getLyingType() {
|
||||
return lyingType;
|
||||
}
|
||||
|
||||
public AnimationData withRide(Ride ride) {
|
||||
this.ride = ride;
|
||||
return this;
|
||||
}
|
||||
|
||||
public AnimationData withCamYaw(float camYaw) {
|
||||
this.camYaw = camYaw;
|
||||
return this;
|
||||
}
|
||||
|
||||
public AnimationData withCamPitch(float camPitch) {
|
||||
this.camPitch = camPitch;
|
||||
return this;
|
||||
}
|
||||
|
||||
public AnimationData withCamRoll(float camRoll) {
|
||||
this.camRoll = camRoll;
|
||||
return this;
|
||||
}
|
||||
|
||||
public AnimationData withCamComputePriority(int camPosPriority) {
|
||||
this.camComputePriority = camPosPriority;
|
||||
return this;
|
||||
}
|
||||
|
||||
public AnimationData addCamPosOffset(Vec3 camPosOffset) {
|
||||
this.camPosOffset = this.camPosOffset.add(camPosOffset);
|
||||
return this;
|
||||
}
|
||||
|
||||
public AnimationData setCamPosOffset(Vec3 camPosOffset) {
|
||||
this.camPosOffset = camPosOffset;
|
||||
return this;
|
||||
}
|
||||
|
||||
public AnimationData withCamPosOffsetRelative(boolean camPosOffsetRelative) {
|
||||
this.camPosOffsetRelative = camPosOffsetRelative;
|
||||
return this;
|
||||
}
|
||||
|
||||
public AnimationData withLyingType(@Nullable AnimationData.LyingType lyingType) {
|
||||
this.lyingType = lyingType;
|
||||
if(lyingType == null) return this;
|
||||
this.camPosOffset = new Vec3(0, -1.3f, 0);
|
||||
this.camPitch = -90.0f;
|
||||
switch (lyingType) {
|
||||
case RIGHT -> {
|
||||
this.camRoll = 90.0f;
|
||||
this.camYaw = 90.0f;
|
||||
}
|
||||
case LEFT -> {
|
||||
this.camRoll = -90.0f;
|
||||
this.camYaw = -90.0f;
|
||||
}
|
||||
case BACK -> this.camPitch = 90.0f;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompoundTag serializeNBT() {
|
||||
CompoundTag tag = new CompoundTag();
|
||||
tag.putString("key", key.toString());
|
||||
tag.putInt("priority", camComputePriority);
|
||||
if(lyingType != null) tag.putInt("lyingType", lyingType.getId());
|
||||
tag.putFloat("camYaw", camYaw);
|
||||
tag.putFloat("camPitch", camPitch);
|
||||
tag.putFloat("camRoll", camRoll);
|
||||
CompoundTag camOffset = new CompoundTag();
|
||||
camOffset.putDouble("x", camPosOffset.x);
|
||||
camOffset.putDouble("y", camPosOffset.y);
|
||||
camOffset.putDouble("z", camPosOffset.z);
|
||||
camOffset.putBoolean("relative", camPosOffsetRelative);
|
||||
tag.put("camOffset", camOffset);
|
||||
if (ride != null) {
|
||||
CompoundTag rideTag = new CompoundTag();
|
||||
Vec3 offset = ride.getOffset();
|
||||
rideTag.putDouble("x", offset.x);
|
||||
rideTag.putDouble("y", offset.y);
|
||||
rideTag.putDouble("z", offset.z);
|
||||
rideTag.putInt("existTick", ride.getExistTick());
|
||||
rideTag.putFloat("xRot", ride.getXRot());
|
||||
rideTag.putFloat("yRot", ride.getYRot());
|
||||
List<ResourceLocation> componentAnimations = ride.getComponentAnimations();
|
||||
ListTag listTag = new ListTag();
|
||||
for (ResourceLocation animation : componentAnimations) {
|
||||
listTag.add(StringTag.valueOf(animation.toString()));
|
||||
}
|
||||
rideTag.put("subAnimations", listTag);
|
||||
tag.put("ride", rideTag);
|
||||
}
|
||||
return tag;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deserializeNBT(CompoundTag nbt) {
|
||||
String string = nbt.getString("key");
|
||||
this.key = new ResourceLocation(string);
|
||||
this.camComputePriority = nbt.getInt("priority");
|
||||
if(nbt.contains("lyingType")) this.lyingType = LyingType.getLyingType(nbt.getInt("lyingType"));
|
||||
else this.lyingType = null;
|
||||
this.camYaw = nbt.getFloat("camYaw");
|
||||
this.camPitch = nbt.getFloat("camPitch");
|
||||
this.camRoll = nbt.getFloat("camRoll");
|
||||
CompoundTag camOffset = nbt.getCompound("camOffset");
|
||||
this.camPosOffset = new Vec3(
|
||||
camOffset.getDouble("x"),
|
||||
camOffset.getDouble("y"),
|
||||
camOffset.getDouble("z")
|
||||
);
|
||||
this.camPosOffsetRelative = camOffset.getBoolean("relative");
|
||||
try {
|
||||
if(nbt.contains("ride")) {
|
||||
CompoundTag rideTag = nbt.getCompound("ride");
|
||||
Vec3 offset = new Vec3(
|
||||
rideTag.getDouble("x"),
|
||||
rideTag.getDouble("y"),
|
||||
rideTag.getDouble("z")
|
||||
);
|
||||
int existTick = rideTag.getInt("existTick");
|
||||
float xRot = rideTag.getFloat("xRot");
|
||||
float yRot = rideTag.getFloat("yRot");
|
||||
List<ResourceLocation> componentAnimations = new ArrayList<>();
|
||||
rideTag.getList("subAnimations", 8).forEach(tag ->
|
||||
componentAnimations.add(new ResourceLocation(tag.getAsString()))
|
||||
);
|
||||
this.ride = Ride.create()
|
||||
.withOffset(offset)
|
||||
.withExistTick(existTick)
|
||||
.withXRot(xRot)
|
||||
.withYRot(yRot)
|
||||
.setComponentAnimations(componentAnimations);
|
||||
return;
|
||||
}
|
||||
} catch (Exception ignored) {}
|
||||
this.ride = null;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,72 +0,0 @@
|
|||
/*
|
||||
* Anim Core mod
|
||||
* Copyright (C) 2026 LeisureTimeDock
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package top.leisuretimedock.animationcore.animation.data;
|
||||
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class GenericAnimationData extends AnimationData {
|
||||
@Nullable
|
||||
String name;
|
||||
float heightModifier = 1.0f;
|
||||
|
||||
GenericAnimationData(ResourceLocation key) {
|
||||
this.key = key;
|
||||
}
|
||||
public GenericAnimationData(){}
|
||||
|
||||
public static GenericAnimationData create(ResourceLocation name) {
|
||||
return new GenericAnimationData(name);
|
||||
}
|
||||
|
||||
public GenericAnimationData withHeightModifier(float heightModifier) {
|
||||
this.heightModifier = heightModifier;
|
||||
return this;
|
||||
}
|
||||
|
||||
public GenericAnimationData withRide(Ride ride) {
|
||||
this.ride = ride;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public GenericAnimationData withLyingType(@Nullable LyingType lyingType) {
|
||||
this.heightModifier = 0.3f;
|
||||
return (GenericAnimationData) super.withLyingType(lyingType);
|
||||
}
|
||||
|
||||
public GenericAnimationData withName(String name) {
|
||||
String regex = "^[a-zA-Z0-9_-]+$";
|
||||
Pattern pattern = Pattern.compile(regex);
|
||||
if (!pattern.matcher(name).matches()) {
|
||||
throw new IllegalArgumentException(
|
||||
"Invalid animation name: " + name + ", must match " + regex
|
||||
);
|
||||
}
|
||||
this.name = name;
|
||||
return this;
|
||||
}
|
||||
|
||||
public float getHeightModifier() {
|
||||
return heightModifier;
|
||||
}
|
||||
|
||||
public @Nullable String getName() {
|
||||
return name;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,34 +0,0 @@
|
|||
/*
|
||||
* Anim Core mod
|
||||
* Copyright (C) 2026 LeisureTimeDock
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package top.leisuretimedock.animationcore.animation.data;
|
||||
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
|
||||
public class RawAnimationData extends AnimationData{
|
||||
RawAnimationData(ResourceLocation key) {
|
||||
this.key = key;
|
||||
}
|
||||
public RawAnimationData(){}
|
||||
|
||||
public static RawAnimationData create(ResourceLocation name) {
|
||||
return new RawAnimationData(name);
|
||||
}
|
||||
|
||||
public RawAnimationData withRide(Ride ride) {
|
||||
this.ride = ride;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,102 +0,0 @@
|
|||
/*
|
||||
* Anim Core mod
|
||||
* Copyright (C) 2026 LeisureTimeDock
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package top.leisuretimedock.animationcore.animation.data;
|
||||
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class Ride {
|
||||
private final List<ResourceLocation> componentAnimations = new ArrayList<>();
|
||||
private Vec3 offset = new Vec3(0.0D, 0.0D, 0.0D);
|
||||
private int existTick;
|
||||
private float xRot;
|
||||
private float yRot;
|
||||
|
||||
|
||||
public static Ride create() {
|
||||
return new Ride();
|
||||
}
|
||||
|
||||
public Ride withOffset(Vec3 offset) {
|
||||
this.offset = offset;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Ride withExistTick(int existTick) {
|
||||
this.existTick = existTick;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Ride withXRot(float xRot) {
|
||||
this.xRot = xRot;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Ride withYRot(float yRot) {
|
||||
this.yRot = yRot;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Ride setComponentAnimations(List<ResourceLocation> animations) {
|
||||
this.componentAnimations.clear();
|
||||
this.componentAnimations.addAll(animations);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Ride addComponentAnimation(ResourceLocation animation) {
|
||||
this.componentAnimations.add(animation);
|
||||
return this;
|
||||
}
|
||||
|
||||
public float getXRot() {
|
||||
return xRot;
|
||||
}
|
||||
|
||||
public void setXRot(float xRot) {
|
||||
this.xRot = xRot;
|
||||
}
|
||||
|
||||
public float getYRot() {
|
||||
return yRot;
|
||||
}
|
||||
|
||||
public void setYRot(float yRot) {
|
||||
this.yRot = yRot;
|
||||
}
|
||||
|
||||
public Vec3 getOffset() {
|
||||
return offset;
|
||||
}
|
||||
|
||||
public void setOffset(Vec3 offset) {
|
||||
this.offset = offset;
|
||||
}
|
||||
|
||||
public int getExistTick() {
|
||||
return existTick;
|
||||
}
|
||||
|
||||
public void setExistTick(int existTick) {
|
||||
this.existTick = existTick;
|
||||
}
|
||||
|
||||
public List<ResourceLocation> getComponentAnimations() {
|
||||
return componentAnimations;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,234 +0,0 @@
|
|||
/*
|
||||
* Anim Core mod
|
||||
* Copyright (C) 2026 LeisureTimeDock
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package top.leisuretimedock.animationcore.animation.data.util;
|
||||
|
||||
import com.google.gson.*;
|
||||
import top.leisuretimedock.animationcore.AnimationCore;
|
||||
import top.leisuretimedock.animationcore.animation.data.AnimationData;
|
||||
import top.leisuretimedock.animationcore.animation.data.GenericAnimationData;
|
||||
import top.leisuretimedock.animationcore.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 AnimJson {
|
||||
private static final String Key = "key";
|
||||
private static final String Name = "name";
|
||||
private static final String LyingType = "lyingType";
|
||||
private static final String HeightModifier = "heightModifier";
|
||||
private static final String CamPitch = "camPitch";
|
||||
private static final String CamRoll = "camRoll";
|
||||
private static final String CamYaw = "camYaw";
|
||||
private static final String CamPosOffset = "camPosOffset";
|
||||
private static final String Relative = "relative";
|
||||
private static final String Priority = "priority";
|
||||
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 GenericAnimationData parse() {
|
||||
return fromJson();
|
||||
}
|
||||
|
||||
public GenericAnimationData fromJson() {
|
||||
try {
|
||||
JsonObject json = originElement.getAsJsonObject();
|
||||
GenericAnimationData animation = GenericAnimationData.create(new ResourceLocation(json.get(Key).getAsString()));
|
||||
if(json.has(Name)) animation.withName(json.get(Name).getAsString());
|
||||
if(json.has(LyingType)) animation.withLyingType(AnimationData.LyingType.valueOf(json.get(LyingType).getAsString()));
|
||||
if(json.has(HeightModifier)) animation.withHeightModifier(json.get(HeightModifier).getAsFloat());
|
||||
if(json.has(Priority)) animation.withCamComputePriority(json.get(Priority).getAsInt());
|
||||
if(json.has(CamPitch)) animation.withCamPitch(json.get(CamPitch).getAsFloat());
|
||||
if(json.has(CamRoll)) animation.withCamRoll(json.get(CamRoll).getAsFloat());
|
||||
if(json.has(CamYaw)) animation.withCamYaw(json.get(CamYaw).getAsFloat());
|
||||
if(json.has(CamPosOffset)) {
|
||||
JsonObject camOffset = json.get(CamPosOffset).getAsJsonObject();
|
||||
Vec3 vec3 = Vec3.ZERO;
|
||||
if(camOffset.has("x")) vec3 = vec3.add(camOffset.get("x").getAsDouble(), 0, 0);
|
||||
if(camOffset.has("y")) vec3 = vec3.add(0, camOffset.get("y").getAsDouble(), 0);
|
||||
if(camOffset.has("z")) vec3 = vec3.add(0, 0, camOffset.get("z").getAsDouble());
|
||||
if(!vec3.equals(Vec3.ZERO)) animation.setCamPosOffset(vec3);
|
||||
if(camOffset.has(Relative)) animation.withCamPosOffsetRelative(camOffset.get(Relative).getAsBoolean());
|
||||
}
|
||||
if(json.has(WithRide)){
|
||||
Ride ride = Ride.create();
|
||||
JsonObject withRide = json.get(WithRide).getAsJsonObject();
|
||||
if(withRide.has(ExistTick)) ride.setExistTick(withRide.get(ExistTick).getAsInt());
|
||||
if(withRide.has(XRot)) ride.setXRot(withRide.get(XRot).getAsFloat());
|
||||
if(withRide.has(YRot)) ride.setYRot(withRide.get(YRot).getAsFloat());
|
||||
if(withRide.has(Offset)) {
|
||||
JsonObject offsetJson = withRide.get(Offset).getAsJsonObject();
|
||||
Vec3 offset = new Vec3(
|
||||
offsetJson.get("x").getAsDouble(),
|
||||
offsetJson.get("y").getAsDouble(),
|
||||
offsetJson.get("z").getAsDouble()
|
||||
);
|
||||
ride.withOffset(offset);
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
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 GenericAnimationData animation;
|
||||
Writer(@Nullable Path file, GenericAnimationData animation) {
|
||||
this.animation = animation;
|
||||
this.file = file;
|
||||
}
|
||||
|
||||
public static Writer stream(Path path, GenericAnimationData animation) {
|
||||
return new Writer(path, animation);
|
||||
}
|
||||
|
||||
public static Writer stream(GenericAnimationData animation) {
|
||||
return new Writer(null, animation);
|
||||
}
|
||||
|
||||
public static Path syntaxExample(Path directory) throws Exception {
|
||||
ResourceLocation exampleLocation = new ResourceLocation(AnimationCore.MOD_ID, Writer.example);
|
||||
GenericAnimationData example = (GenericAnimationData) GenericAnimationData
|
||||
.create(exampleLocation)
|
||||
.withName(Writer.example)
|
||||
.withLyingType(GenericAnimationData.LyingType.RIGHT)
|
||||
.withHeightModifier(0.3f)
|
||||
.setCamPosOffset(new Vec3(0.0f, -1.3f, 0.0f))
|
||||
.withCamComputePriority(0)
|
||||
.withCamPosOffsetRelative(false)
|
||||
.withCamPitch(-90.0f)
|
||||
.withCamRoll(90.0f)
|
||||
.withCamYaw(90.0f)
|
||||
.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(animation.getName() != null) {
|
||||
resultPath = modIdPath.resolve(animation.getName() + ".anim.json");
|
||||
if(resultPath.toFile().exists()) {
|
||||
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());
|
||||
if (animation.getName() != null) json.addProperty(Name, animation.getName());
|
||||
json.addProperty(Priority, animation.getCamComputePriority());
|
||||
if (animation.getLyingType() != null) json.addProperty(LyingType, animation.getLyingType().getName());
|
||||
json.addProperty(HeightModifier, animation.getHeightModifier());
|
||||
JsonObject camOffset = new JsonObject();
|
||||
camOffset.addProperty("x", animation.getCamPosOffset().x);
|
||||
camOffset.addProperty("y", animation.getCamPosOffset().y);
|
||||
camOffset.addProperty("z", animation.getCamPosOffset().z);
|
||||
camOffset.addProperty(Relative, animation.isCamPosOffsetRelative());
|
||||
json.add(CamPosOffset, camOffset);
|
||||
json.addProperty(CamPitch, animation.getCamPitch());
|
||||
json.addProperty(CamRoll, animation.getCamRoll());
|
||||
json.addProperty(CamYaw, animation.getCamYaw());
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -1,144 +0,0 @@
|
|||
/*
|
||||
* Anim Core mod
|
||||
* Copyright (C) 2026 LeisureTimeDock
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package top.leisuretimedock.animationcore.animation.data.util;
|
||||
|
||||
import com.google.gson.*;
|
||||
import top.leisuretimedock.animationcore.animation.register.AnimationRegistry;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
|
||||
import java.io.*;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class AnimLayerJson {
|
||||
|
||||
private static final String Key = "key";
|
||||
private static final String Priority = "priority";
|
||||
|
||||
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 Map<ResourceLocation, Integer> parse() {
|
||||
return fromJson();
|
||||
}
|
||||
|
||||
private Map<ResourceLocation, Integer> fromJson() {
|
||||
try {
|
||||
JsonArray jsonArray = originElement.getAsJsonArray();
|
||||
Map<ResourceLocation, Integer> map = new HashMap<>();
|
||||
for (JsonElement element : jsonArray) {
|
||||
JsonObject jsonObject = element.getAsJsonObject();
|
||||
ResourceLocation location = new ResourceLocation(jsonObject.get(Key).getAsString());
|
||||
int priority = jsonObject.get(Priority).getAsInt();
|
||||
map.put(location, priority);
|
||||
}
|
||||
return map;
|
||||
} catch (Exception e) {
|
||||
throw new JsonParseException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class Writer {
|
||||
private final Path file;
|
||||
private final Map<ResourceLocation, Integer> layers = AnimationRegistry.getLayers();
|
||||
private final Map<String, Set<ResourceLocation>> layerNames = new HashMap<>();
|
||||
Writer(Path file) {
|
||||
this.file = file;
|
||||
for (ResourceLocation location : layers.keySet()) {
|
||||
String namespace = location.getNamespace();
|
||||
Set<ResourceLocation> locationSet = layerNames.getOrDefault(namespace, new HashSet<>());
|
||||
locationSet.add(location);
|
||||
layerNames.put(namespace, locationSet);
|
||||
}
|
||||
}
|
||||
|
||||
public static Writer stream(Path path) {
|
||||
return new Writer(path);
|
||||
}
|
||||
|
||||
public static Path syntaxImmediately(Path directory) throws IOException {
|
||||
Writer writer = stream(directory);
|
||||
return writer.syntax();
|
||||
}
|
||||
|
||||
public Map<String, JsonElement> allToJson() {
|
||||
Map<String, JsonElement> map = new HashMap<>();
|
||||
for (String namespace : layerNames.keySet()) {
|
||||
Set<ResourceLocation> locationSet = layerNames.get(namespace);
|
||||
JsonElement json = toJson(locationSet);
|
||||
map.put(namespace, json);
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
public Path syntax(String ... namespaces) throws IOException {
|
||||
Set<String> namespaceSet;
|
||||
if(namespaces.length == 0) {
|
||||
namespaceSet = layerNames.keySet();
|
||||
} else {
|
||||
namespaceSet = Arrays.stream(namespaces).collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
for (String name : namespaceSet) {
|
||||
Set<ResourceLocation> locationSet = layerNames.get(name);
|
||||
Path modIdPath = file.resolve(name);
|
||||
Path resultPath = modIdPath.resolve("animation.layer.json");
|
||||
if(!Files.exists(modIdPath)) Files.createDirectories(modIdPath);
|
||||
Gson gson = new GsonBuilder().setPrettyPrinting().create();
|
||||
try (FileWriter writer = new FileWriter(resultPath.toFile())) {
|
||||
gson.toJson(toJson(locationSet), writer);
|
||||
}
|
||||
}
|
||||
return file;
|
||||
}
|
||||
|
||||
private JsonElement toJson(Set<ResourceLocation> locationSet) {
|
||||
JsonArray jsonArray = new JsonArray();
|
||||
for (ResourceLocation location : locationSet) {
|
||||
if(layers.containsKey(location)) {
|
||||
JsonObject jsonObject = new JsonObject();
|
||||
jsonObject.addProperty(Key, location.toString());
|
||||
jsonObject.addProperty(Priority, layers.get(location));
|
||||
jsonArray.add(jsonObject);
|
||||
}
|
||||
}
|
||||
return jsonArray;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,121 +0,0 @@
|
|||
/*
|
||||
* Anim Core mod
|
||||
* Copyright (C) 2026 LeisureTimeDock
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package top.leisuretimedock.animationcore.animation.data.util;
|
||||
|
||||
import com.google.gson.*;
|
||||
import top.leisuretimedock.animationcore.animation.data.AnimationData;
|
||||
import top.leisuretimedock.animationcore.animation.data.RawAnimationData;
|
||||
import top.leisuretimedock.animationcore.animation.data.Ride;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileReader;
|
||||
import java.nio.file.Path;
|
||||
|
||||
public class RawAnimJson {
|
||||
private static final String Key = "key";
|
||||
private static final String LyingType = "lyingType";
|
||||
private static final String CamPitch = "camPitch";
|
||||
private static final String CamRoll = "camRoll";
|
||||
private static final String CamYaw = "camYaw";
|
||||
private static final String CamPosOffset = "camPosOffset";
|
||||
private static final String Relative = "relative";
|
||||
private static final String Priority = "priority";
|
||||
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 RawAnimJson.Reader stream(Path path) throws Exception {
|
||||
return new RawAnimJson.Reader(path);
|
||||
}
|
||||
|
||||
public static RawAnimJson.Reader stream(JsonElement jsonElement) {
|
||||
return new RawAnimJson.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(LyingType)) animation.withLyingType(AnimationData.LyingType.valueOf(json.get(LyingType).getAsString()));
|
||||
if(json.has(Priority)) animation.withCamComputePriority(json.get(Priority).getAsInt());
|
||||
if(json.has(CamPitch)) animation.withCamPitch(json.get(CamPitch).getAsFloat());
|
||||
if(json.has(CamRoll)) animation.withCamRoll(json.get(CamRoll).getAsFloat());
|
||||
if(json.has(CamYaw)) animation.withCamYaw(json.get(CamYaw).getAsFloat());
|
||||
if(json.has(CamPosOffset)) {
|
||||
JsonObject camOffset = json.get(CamPosOffset).getAsJsonObject();
|
||||
Vec3 vec3 = Vec3.ZERO;
|
||||
if(camOffset.has("x")) vec3 = vec3.add(camOffset.get("x").getAsDouble(), 0, 0);
|
||||
if(camOffset.has("y")) vec3 = vec3.add(0, camOffset.get("y").getAsDouble(), 0);
|
||||
if(camOffset.has("z")) vec3 = vec3.add(0, 0, camOffset.get("z").getAsDouble());
|
||||
if(!vec3.equals(Vec3.ZERO)) animation.setCamPosOffset(vec3);
|
||||
if(camOffset.has(Relative)) animation.withCamPosOffsetRelative(camOffset.get(Relative).getAsBoolean());
|
||||
}
|
||||
if(json.has(WithRide)){
|
||||
Ride ride = Ride.create();
|
||||
JsonObject withRide = json.get(WithRide).getAsJsonObject();
|
||||
if(withRide.has(ExistTick)) ride.setExistTick(withRide.get(ExistTick).getAsInt());
|
||||
if(withRide.has(XRot)) ride.setXRot(withRide.get(XRot).getAsFloat());
|
||||
if(withRide.has(YRot)) ride.setYRot(withRide.get(YRot).getAsFloat());
|
||||
if(withRide.has(Offset)) {
|
||||
JsonObject offsetJson = withRide.get(Offset).getAsJsonObject();
|
||||
Vec3 offset = new Vec3(
|
||||
offsetJson.get("x").getAsDouble(),
|
||||
offsetJson.get("y").getAsDouble(),
|
||||
offsetJson.get("z").getAsDouble()
|
||||
);
|
||||
ride.withOffset(offset);
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
animation.withRide(ride);
|
||||
}
|
||||
return animation;
|
||||
} catch (Exception e) {
|
||||
throw new JsonParseException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,223 +0,0 @@
|
|||
/*
|
||||
* Anim Core mod
|
||||
* Copyright (C) 2026 LeisureTimeDock
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package top.leisuretimedock.animationcore.animation.entity;
|
||||
|
||||
import top.leisuretimedock.animationcore.animation.capability.AnimationDataCapability;
|
||||
import top.leisuretimedock.animationcore.animation.capability.inter.IAnimationCapability;
|
||||
import top.leisuretimedock.animationcore.animation.data.AnimationData;
|
||||
import top.leisuretimedock.animationcore.animation.data.Ride;
|
||||
import top.leisuretimedock.animationcore.animation.register.AnimationEntities;
|
||||
import top.leisuretimedock.animationcore.animation.service.AnimationService;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.network.protocol.Packet;
|
||||
import net.minecraft.network.protocol.game.ClientGamePacketListener;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.entity.LivingEntity;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
import net.minecraftforge.network.NetworkHooks;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
public class AnimationRideEntity extends Entity {
|
||||
public AnimationRideEntity(Level pLevel) {
|
||||
super(AnimationEntities.RIDE.get(), pLevel);
|
||||
this.noPhysics = true;
|
||||
}
|
||||
|
||||
private final Set<ServerPlayer> players = new HashSet<>();
|
||||
private final Map<ResourceLocation, UUID> animationPair = new HashMap<>();
|
||||
private AnimationData animation;
|
||||
private ServerPlayer player;
|
||||
private ResourceLocation layer;
|
||||
public AnimationRideEntity(ServerPlayer pPlayer, ResourceLocation layer, AnimationData animation) {
|
||||
this(pPlayer.level());
|
||||
this.player = pPlayer;
|
||||
this.layer = layer;
|
||||
this.animation = animation;
|
||||
Ride ride = animation.getRide();
|
||||
if(ride != null) {
|
||||
List<ResourceLocation> componentAnimations = ride.getComponentAnimations();
|
||||
for (ResourceLocation componentAnimation : componentAnimations) {
|
||||
animationPair.put(componentAnimation, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public ResourceLocation getLayer() {
|
||||
return layer;
|
||||
}
|
||||
|
||||
public Set<ServerPlayer> getPlayers() {
|
||||
return players;
|
||||
}
|
||||
|
||||
public ServerPlayer getPlayer() {
|
||||
return player;
|
||||
}
|
||||
|
||||
public AnimationData getAnimation() {
|
||||
return animation;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void defineSynchedData() {}
|
||||
|
||||
@Override
|
||||
protected void readAdditionalSaveData(@NotNull CompoundTag pCompound) {}
|
||||
|
||||
@Override
|
||||
protected void addAdditionalSaveData(@NotNull CompoundTag pCompound) {}
|
||||
|
||||
@Override
|
||||
public void tick() {
|
||||
super.tick();
|
||||
if(!this.level().isClientSide) {
|
||||
Ride ride = animation == null ? null : animation.getRide();
|
||||
if(!this.getPassengers().contains(player) || (ride != null && ride.getExistTick() > 0 && this.tickCount >= ride.getExistTick())) {
|
||||
this.remove(RemovalReason.DISCARDED);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getPassengersRidingOffset() {
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean canRide(@NotNull Entity entity) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldRiderSit() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull Packet<ClientGamePacketListener> getAddEntityPacket(){
|
||||
return NetworkHooks.getEntitySpawningPacket(this);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static AnimationRideEntity create(ServerPlayer pPlayer, ResourceLocation layer, AnimationData anim, boolean force, Vec3 pos) {
|
||||
if(anim == null) return null;
|
||||
if(anim.getRide() == null) return null;
|
||||
IAnimationCapability data = AnimationDataCapability.getCapability(pPlayer).orElse(null);
|
||||
if(data == null) return null;
|
||||
data.setRiderAnimation(layer, anim.getKey());
|
||||
AnimationRideEntity seat = new AnimationRideEntity(pPlayer, layer, anim);
|
||||
float xRot = anim.getRide().getXRot();
|
||||
float yRot = anim.getRide().getYRot();
|
||||
if(xRot == 0 && yRot == 0) seat.setRot(pPlayer.getXRot(), pPlayer.getYRot());
|
||||
else seat.setRot(yRot, xRot);
|
||||
pos.add(anim.getRide().getOffset());
|
||||
seat.setPos(pos.x, pos.y + 0.35f, pos.z);
|
||||
pPlayer.level().addFreshEntity(seat);
|
||||
pPlayer.startRiding(seat, force);
|
||||
return seat;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static AnimationRideEntity create(ServerPlayer pPlayer, ResourceLocation layer, AnimationData anim, boolean force) {
|
||||
return create(pPlayer, layer, anim, force, pPlayer.position());
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static AnimationRideEntity create(ServerPlayer pPlayer, ResourceLocation layer, AnimationData anim, boolean force, BlockPos pos) {
|
||||
return create(pPlayer, layer, anim, force, pos.getCenter());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void positionRider(@NotNull Entity pPassenger, @NotNull MoveFunction pCallback) {
|
||||
super.positionRider(pPassenger, pCallback);
|
||||
pPassenger.setYBodyRot(this.getYRot());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPassengerTurned(@NotNull Entity pEntityToUpdate) {
|
||||
pEntityToUpdate.setYBodyRot(this.getYRot());
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull Vec3 getDismountLocationForPassenger(@NotNull LivingEntity entity) {
|
||||
Ride ride = animation.getRide();
|
||||
if(ride != null) {
|
||||
Vec3 position = entity.position();
|
||||
return position.subtract(ride.getOffset());
|
||||
}
|
||||
return entity.position();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void addPassenger(@NotNull Entity entity) {
|
||||
int passengerNum = getPassengers().size();
|
||||
super.addPassenger(entity);
|
||||
if(passengerNum == 0) return;
|
||||
if(entity instanceof ServerPlayer serverPlayer) {
|
||||
Ride ride = animation.getRide();
|
||||
if(ride == null) return;
|
||||
List<ResourceLocation> componentAnimations = ride.getComponentAnimations();
|
||||
if(componentAnimations.isEmpty()) return;
|
||||
if(passengerNum > componentAnimations.size()) return;
|
||||
ResourceLocation animLocation = null;
|
||||
for (ResourceLocation location : animationPair.keySet()) {
|
||||
if(animationPair.get(location) == null)
|
||||
animLocation = location;
|
||||
}
|
||||
if(animLocation == null) return;
|
||||
animationPair.put(animLocation, serverPlayer.getUUID());
|
||||
IAnimationCapability data = AnimationDataCapability.getCapability(serverPlayer).orElse(null);
|
||||
if(data == null) return;
|
||||
data.setRiderAnimation(layer, animLocation);
|
||||
AnimationService.INSTANCE.syncAnimation(serverPlayer, player);
|
||||
players.add(serverPlayer);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void removePassenger(@NotNull Entity entity) {
|
||||
super.removePassenger(entity);
|
||||
if(entity instanceof ServerPlayer serverPlayer) {
|
||||
AnimationService.INSTANCE.removeAnimation(serverPlayer, layer);
|
||||
players.remove(serverPlayer);
|
||||
new HashMap<>(animationPair).forEach((key, value) -> {
|
||||
if(Objects.equals(value, serverPlayer.getUUID())) {
|
||||
animationPair.put(key, null);
|
||||
}
|
||||
});
|
||||
AnimationDataCapability.getCapability(serverPlayer).ifPresent(
|
||||
IAnimationCapability::removeRiderAnimation
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canAddPassenger(@NotNull Entity pPassenger) {
|
||||
boolean isServerPlayer = pPassenger instanceof ServerPlayer;
|
||||
int size = players.size();
|
||||
Ride ride = animation.getRide();
|
||||
if(ride == null) return false;
|
||||
int maxSize = ride.getComponentAnimations().size();
|
||||
return isServerPlayer && size < maxSize;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,48 +0,0 @@
|
|||
/*
|
||||
* Anim Core mod
|
||||
* Copyright (C) 2026 LeisureTimeDock
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package top.leisuretimedock.animationcore.animation.entity.renderer;
|
||||
|
||||
import top.leisuretimedock.animationcore.animation.entity.AnimationRideEntity;
|
||||
import com.mojang.blaze3d.vertex.PoseStack;
|
||||
import net.minecraft.client.renderer.MultiBufferSource;
|
||||
import net.minecraft.client.renderer.culling.Frustum;
|
||||
import net.minecraft.client.renderer.entity.EntityRenderer;
|
||||
import net.minecraft.client.renderer.entity.EntityRendererProvider;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class AnimationRideRenderer extends EntityRenderer<AnimationRideEntity> {
|
||||
public AnimationRideRenderer(EntityRendererProvider.Context pContext) {
|
||||
super(pContext);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull ResourceLocation getTextureLocation(@NotNull AnimationRideEntity pEntity) {
|
||||
return new ResourceLocation("");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldRender(@NotNull AnimationRideEntity pLivingEntity, @NotNull Frustum pCamera, double pCamX, double pCamY, double pCamZ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render(@NotNull AnimationRideEntity pEntity, float pEntityYaw, float pPartialTick, @NotNull PoseStack pPoseStack, @NotNull MultiBufferSource pBuffer, int pPackedLight) {}
|
||||
|
||||
@Override
|
||||
protected void renderNameTag(@NotNull AnimationRideEntity pEntity, @NotNull Component pDisplayName, @NotNull PoseStack pPoseStack, @NotNull MultiBufferSource pBuffer, int pPackedLight) {}
|
||||
}
|
||||
|
|
@ -1,40 +0,0 @@
|
|||
/*
|
||||
* Anim Core mod
|
||||
* Copyright (C) 2026 LeisureTimeDock
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package top.leisuretimedock.animationcore.animation.event;
|
||||
|
||||
import top.leisuretimedock.animationcore.animation.capability.AnimationDataCapability;
|
||||
import top.leisuretimedock.animationcore.animation.entity.AnimationRideEntity;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraftforge.event.TickEvent;
|
||||
import net.minecraftforge.eventbus.api.SubscribeEvent;
|
||||
|
||||
public class PlayerTickEvent {
|
||||
@SubscribeEvent
|
||||
public static void onPlayerTickEvent(TickEvent.PlayerTickEvent event) {
|
||||
if (event.side.isServer() && event.phase == TickEvent.Phase.END) {
|
||||
if(event.player.tickCount % 20 == 0) {
|
||||
Player player = event.player;
|
||||
if(!(player.getVehicle() instanceof AnimationRideEntity)){
|
||||
AnimationDataCapability.getCapability(player).ifPresent(capability -> {
|
||||
if(capability.getRiderAnimLayer() != null) {
|
||||
capability.removeRiderAnimation();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,142 +0,0 @@
|
|||
/*
|
||||
* Anim Core mod
|
||||
* Copyright (C) 2026 LeisureTimeDock
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package top.leisuretimedock.animationcore.animation.event.client;
|
||||
|
||||
import top.leisuretimedock.animationcore.animation.capability.AnimationDataCapability;
|
||||
import top.leisuretimedock.animationcore.animation.capability.inter.IAnimationCapability;
|
||||
import top.leisuretimedock.animationcore.animation.data.AnimationData;
|
||||
import top.leisuretimedock.animationcore.animation.data.RawAnimationData;
|
||||
import top.leisuretimedock.animationcore.animation.utils.AnimationUtils;
|
||||
import dev.kosmx.playerAnim.core.util.MathHelper;
|
||||
import net.minecraft.client.Camera;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.player.LocalPlayer;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
import net.minecraftforge.api.distmarker.Dist;
|
||||
import net.minecraftforge.api.distmarker.OnlyIn;
|
||||
import net.minecraftforge.client.event.ViewportEvent;
|
||||
import net.minecraftforge.eventbus.api.SubscribeEvent;
|
||||
|
||||
@OnlyIn(Dist.CLIENT)
|
||||
public class CameraModify {
|
||||
private static float targetYaw = 0.0F;
|
||||
private static float targetPitch = 0.0F;
|
||||
private static float targetRoll = 0.0F;
|
||||
private static float currentYaw = 0.0F;
|
||||
private static float currentPitch = 0.0F;
|
||||
private static float currentRoll = 0.0F;
|
||||
|
||||
@SubscribeEvent
|
||||
public static void changeCameraView(ViewportEvent.ComputeCameraAngles event){
|
||||
Camera camera = event.getCamera();
|
||||
Entity entity = camera.getEntity();
|
||||
Minecraft minecraft = Minecraft.getInstance();
|
||||
if (entity == minecraft.player && minecraft.options.getCameraType().isFirstPerson()) {
|
||||
LocalPlayer player = minecraft.player;
|
||||
if (player != null) {
|
||||
IAnimationCapability data = AnimationDataCapability.getCapability(player).orElse(null);
|
||||
if(data == null) return;
|
||||
AnimationData animation = AnimationUtils.getPredicateAnimationData(animationData -> {
|
||||
float camYaw = animationData.getCamYaw();
|
||||
float camPitch = animationData.getCamPitch();
|
||||
float camRoll = animationData.getCamRoll();
|
||||
return !(camYaw == camPitch && camPitch == camRoll && camYaw == 0);
|
||||
});
|
||||
|
||||
if(animation != null) {
|
||||
targetPitch = animation.getCamPitch();
|
||||
targetYaw = animation.getCamYaw();
|
||||
targetRoll = animation.getCamRoll();
|
||||
} else {
|
||||
targetYaw = 0.0F;
|
||||
targetPitch = 0.0F;
|
||||
targetRoll = 0.0F;
|
||||
}
|
||||
}
|
||||
float var3 = Minecraft.getInstance().getDeltaFrameTime();
|
||||
float var4 = var3 / 5.0F;
|
||||
if (var4 == 0.0F) {
|
||||
var4 = 0.0022857143F;
|
||||
}
|
||||
|
||||
currentPitch = MathHelper.lerp(var4, currentPitch, targetPitch);
|
||||
currentYaw = MathHelper.lerp(var4, currentYaw, targetYaw);
|
||||
currentRoll = MathHelper.lerp(var4, currentRoll, targetRoll);
|
||||
event.setPitch(event.getPitch() + currentPitch);
|
||||
event.setYaw(event.getYaw() + currentYaw);
|
||||
event.setRoll(event.getRoll() + currentRoll);
|
||||
}
|
||||
}
|
||||
|
||||
private static Vec3 targetOffset = Vec3.ZERO;
|
||||
private static Vec3 currentOffset = Vec3.ZERO;
|
||||
|
||||
@SubscribeEvent
|
||||
public static void changeCameraPos(ViewportEvent.ComputeCameraAngles event) {
|
||||
Camera camera = event.getCamera();
|
||||
Entity entity = camera.getEntity();
|
||||
Minecraft minecraft = Minecraft.getInstance();
|
||||
if(entity == minecraft.player && minecraft.options.getCameraType().isFirstPerson()) {
|
||||
LocalPlayer player = minecraft.player;
|
||||
|
||||
AnimationData animation = AnimationUtils.getPredicateAnimationData(animationData ->
|
||||
!animationData.getCamPosOffset().multiply(1,0,1).equals(Vec3.ZERO)
|
||||
);
|
||||
float var3 = Minecraft.getInstance().getDeltaFrameTime();
|
||||
float var4 = var3 / 5.0F;
|
||||
if (var4 == 0.0F) {
|
||||
var4 = 0.0022857143F;
|
||||
}
|
||||
|
||||
targetOffset = Vec3.ZERO;
|
||||
if(animation != null) {
|
||||
Vec3 camPosOffset = animation.getCamPosOffset().multiply(1,0,1);
|
||||
AnimationData data = AnimationUtils.getEyeModifierAnimationData(player);
|
||||
if(data instanceof RawAnimationData) {
|
||||
camPosOffset = camPosOffset.add(0, data.getCamPosOffset().y, 0);
|
||||
}
|
||||
if(animation.isCamPosOffsetRelative()) {
|
||||
float yRot = player.yBodyRotO + (player.yBodyRot - player.yBodyRotO) * minecraft.getPartialTick();
|
||||
float bodyAngel = -(yRot + 90) * ((float)Math.PI / 180F);
|
||||
double cos = Math.cos(bodyAngel);
|
||||
double sin = Math.sin(bodyAngel);
|
||||
double x = camPosOffset.x;
|
||||
double z = camPosOffset.z;
|
||||
targetOffset = new Vec3(
|
||||
sin * x + cos * z,
|
||||
camPosOffset.y,
|
||||
cos * x - sin * z
|
||||
);
|
||||
} else {
|
||||
if(camPosOffset.distanceToSqr(Vec3.ZERO) <= 10.0 * 10.0 * 10.0) {
|
||||
targetOffset = camPosOffset;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
currentOffset = new Vec3(
|
||||
MathHelper.lerp(var4, currentOffset.x, targetOffset.x),
|
||||
MathHelper.lerp(var4, currentOffset.y, targetOffset.y),
|
||||
MathHelper.lerp(var4, currentOffset.z, targetOffset.z)
|
||||
);
|
||||
if(!currentOffset.equals(Vec3.ZERO)) {
|
||||
camera.position = player.getEyePosition(minecraft.getPartialTick())
|
||||
.add(currentOffset);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,55 +0,0 @@
|
|||
/*
|
||||
* Anim Core mod
|
||||
* Copyright (C) 2026 LeisureTimeDock
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package top.leisuretimedock.animationcore.animation.event.client;
|
||||
|
||||
import top.leisuretimedock.animationcore.animation.AnimationApi;
|
||||
import net.minecraft.client.player.AbstractClientPlayer;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraftforge.api.distmarker.Dist;
|
||||
import net.minecraftforge.api.distmarker.OnlyIn;
|
||||
import net.minecraftforge.event.TickEvent;
|
||||
import net.minecraftforge.eventbus.api.SubscribeEvent;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
@OnlyIn(Dist.CLIENT)
|
||||
public class ClientPlayerEvent {
|
||||
@SubscribeEvent
|
||||
public static void onPlayerTick(TickEvent.PlayerTickEvent event) {
|
||||
if (event.side.isClient() && event.phase == TickEvent.Phase.START) {
|
||||
Player player = event.player;
|
||||
if(player.tickCount % 10 != 0) return;
|
||||
if (!(player instanceof AbstractClientPlayer clientPlayer)) return;
|
||||
AnimationApi.getHelper(clientPlayer).refreshAnimation();
|
||||
}
|
||||
}
|
||||
|
||||
public static Map<Runnable, Map.Entry<Integer, Integer>> runs = new HashMap<>();
|
||||
@SubscribeEvent
|
||||
public static void delayRuns(TickEvent.PlayerTickEvent event) {
|
||||
if (event.side.isClient() && event.phase == TickEvent.Phase.END) {
|
||||
Map.copyOf(runs).forEach((runnable, countMap) -> {
|
||||
if(countMap.getValue() >= countMap.getKey()) {
|
||||
runnable.run();
|
||||
runs.remove(runnable);
|
||||
} else {
|
||||
countMap.setValue(countMap.getValue() + 1);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,28 +0,0 @@
|
|||
/*
|
||||
* Anim Core mod
|
||||
* Copyright (C) 2026 LeisureTimeDock
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package top.leisuretimedock.animationcore.animation.event.client;
|
||||
|
||||
import top.leisuretimedock.animationcore.animation.entity.renderer.AnimationRideRenderer;
|
||||
import top.leisuretimedock.animationcore.animation.register.AnimationEntities;
|
||||
import net.minecraftforge.client.event.EntityRenderersEvent;
|
||||
import net.minecraftforge.eventbus.api.SubscribeEvent;
|
||||
|
||||
public class EntityRendererRegisterEvent {
|
||||
@SubscribeEvent
|
||||
public static void registerEntityRenderer(EntityRenderersEvent.RegisterRenderers event) {
|
||||
event.registerEntityRenderer(AnimationEntities.RIDE.get(), AnimationRideRenderer::new);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,132 +0,0 @@
|
|||
/*
|
||||
* Anim Core mod
|
||||
* Copyright (C) 2026 LeisureTimeDock
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package top.leisuretimedock.animationcore.animation.event.create;
|
||||
|
||||
import top.leisuretimedock.animationcore.animation.data.AnimationData;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraftforge.eventbus.api.Cancelable;
|
||||
import net.minecraftforge.eventbus.api.Event;
|
||||
import net.minecraftforge.fml.LogicalSide;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
@Cancelable
|
||||
@Event.HasResult
|
||||
public class AnimationEvent extends Event {
|
||||
public final LogicalSide side;
|
||||
|
||||
AnimationEvent(LogicalSide side) {
|
||||
this.side = side;
|
||||
}
|
||||
|
||||
public enum Type {
|
||||
APPLY, INVITE, REQUEST
|
||||
}
|
||||
|
||||
public static class Send extends AnimationEvent {
|
||||
private int validTick;
|
||||
private int cooldownTick;
|
||||
public final Type type;
|
||||
public Send(LogicalSide side, int validTick, int cooldownTick, Type type) {
|
||||
super(side);
|
||||
this.validTick = validTick;
|
||||
this.cooldownTick = cooldownTick;
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public int getValidTick() {
|
||||
return validTick;
|
||||
}
|
||||
public int getCooldownTick() {
|
||||
return cooldownTick;
|
||||
}
|
||||
|
||||
public void setValidTick(int validTick) {
|
||||
this.validTick = validTick;
|
||||
}
|
||||
public void setCooldownTick(int cooldownTick) {
|
||||
this.cooldownTick = cooldownTick;
|
||||
}
|
||||
}
|
||||
|
||||
public static class Accept extends AnimationEvent {
|
||||
private int validDistance;
|
||||
public final Type type;
|
||||
public Accept(Type type) {
|
||||
super(LogicalSide.SERVER);
|
||||
this.type = type;
|
||||
}
|
||||
public Accept(Type type, int validDistance) {
|
||||
this(type);
|
||||
this.validDistance = validDistance;
|
||||
}
|
||||
|
||||
public int getValidDistance() {
|
||||
return validDistance;
|
||||
}
|
||||
public void setValidDistance(int validDistance) {
|
||||
this.validDistance = validDistance;
|
||||
}
|
||||
}
|
||||
|
||||
public static class Play extends AnimationEvent {
|
||||
private final @Nullable Player player;
|
||||
private final ResourceLocation layer;
|
||||
private final AnimationData animation;
|
||||
public Play(LogicalSide side, @Nullable Player player, ResourceLocation layer, AnimationData animation) {
|
||||
super(side);
|
||||
this.player = player;
|
||||
this.layer = layer;
|
||||
this.animation = animation;
|
||||
}
|
||||
|
||||
public @Nullable Player getPlayer() {
|
||||
return player;
|
||||
}
|
||||
public ResourceLocation getLayer() {
|
||||
return layer;
|
||||
}
|
||||
public AnimationData getAnimation() {
|
||||
return animation;
|
||||
}
|
||||
}
|
||||
|
||||
public static class Join extends AnimationEvent {
|
||||
private final Player player;
|
||||
private final Player target;
|
||||
private boolean force;
|
||||
public Join(Player player, Player target, boolean force) {
|
||||
super(LogicalSide.SERVER);
|
||||
this.player = player;
|
||||
this.target = target;
|
||||
this.force = force;
|
||||
}
|
||||
|
||||
public Player getPlayer() {
|
||||
return player;
|
||||
}
|
||||
public Player getTarget() {
|
||||
return target;
|
||||
}
|
||||
public boolean isForce() {
|
||||
return force;
|
||||
}
|
||||
|
||||
public void setForce(boolean force) {
|
||||
this.force = force;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,65 +0,0 @@
|
|||
/*
|
||||
* Anim Core mod
|
||||
* Copyright (C) 2026 LeisureTimeDock
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package top.leisuretimedock.animationcore.animation.event.create;
|
||||
|
||||
import top.leisuretimedock.animationcore.animation.data.GenericAnimationData;
|
||||
import top.leisuretimedock.animationcore.animation.data.RawAnimationData;
|
||||
import top.leisuretimedock.animationcore.animation.register.RawAnimationRegistry;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraftforge.eventbus.api.Event;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class AnimationRegisterEvent extends Event {
|
||||
public static class Layer extends AnimationRegisterEvent {
|
||||
private final Map<ResourceLocation, Integer> layers = new HashMap<>();
|
||||
|
||||
public Map<ResourceLocation, Integer> getLayers() {
|
||||
return layers;
|
||||
}
|
||||
|
||||
public void registerLayer(ResourceLocation key, Integer value) {
|
||||
layers.put(key, value);
|
||||
}
|
||||
}
|
||||
|
||||
public static class Animation extends AnimationRegisterEvent {
|
||||
private final Map<ResourceLocation, GenericAnimationData> animations = new HashMap<>();
|
||||
|
||||
public Map<ResourceLocation, GenericAnimationData> getAnimations() {
|
||||
return new HashMap<>(animations);
|
||||
}
|
||||
|
||||
public void registerAnimation(ResourceLocation location, GenericAnimationData animation) {
|
||||
animations.put(location, animation);
|
||||
}
|
||||
}
|
||||
|
||||
public static class RawAnimation extends AnimationRegisterEvent {
|
||||
private final Map<ResourceLocation, RawAnimationData> animations = new HashMap<>();
|
||||
|
||||
public Map<ResourceLocation, RawAnimationData> getAnimations() {
|
||||
return new HashMap<>(animations);
|
||||
}
|
||||
|
||||
public void registerAnimation(ResourceLocation location, RawAnimationData animation) {
|
||||
if (RawAnimationRegistry.validateLocation(location)) {
|
||||
animations.put(location, animation);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,81 +0,0 @@
|
|||
/*
|
||||
* Anim Core mod
|
||||
* Copyright (C) 2026 LeisureTimeDock
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package top.leisuretimedock.animationcore.animation.helper;
|
||||
|
||||
import top.leisuretimedock.animationcore.animation.AnimationApi;
|
||||
import top.leisuretimedock.animationcore.animation.data.AnimationData;
|
||||
import top.leisuretimedock.animationcore.animation.register.AnimationRegistry;
|
||||
import top.leisuretimedock.animationcore.animation.register.RawAnimationRegistry;
|
||||
import top.leisuretimedock.animationcore.animation.service.IAnimationService;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraftforge.api.distmarker.Dist;
|
||||
import net.minecraftforge.api.distmarker.OnlyIn;
|
||||
import net.minecraftforge.fml.DistExecutor;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
public class AnimationDataHelper {
|
||||
AnimationDataHelper() {}
|
||||
public static AnimationDataHelper getHelper() {
|
||||
return new AnimationDataHelper();
|
||||
}
|
||||
|
||||
public Set<ResourceLocation> getAllAnimations() {
|
||||
Set<ResourceLocation> set = new HashSet<>(getAnimationNames());
|
||||
Set<ResourceLocation> strings = DistExecutor.unsafeCallWhenOn(Dist.CLIENT,
|
||||
() -> this::getAnimationsClient);
|
||||
if (strings != null && !strings.isEmpty()) set.addAll(strings);
|
||||
return set;
|
||||
}
|
||||
|
||||
@OnlyIn(Dist.CLIENT)
|
||||
private Set<ResourceLocation> getAnimationsClient() {
|
||||
return new HashSet<>(RawAnimationRegistry.getAnimations().keySet());
|
||||
}
|
||||
|
||||
private Set<ResourceLocation> getAnimationNames(){
|
||||
return new HashSet<>(AnimationRegistry.getAnimations().keySet());
|
||||
}
|
||||
|
||||
public Set<ResourceLocation> getLayers() {
|
||||
return new HashSet<>(AnimationRegistry.getLayers().keySet());
|
||||
}
|
||||
|
||||
public boolean isLayerPresent(ResourceLocation location) {
|
||||
return getLayers().contains(location);
|
||||
}
|
||||
|
||||
public boolean isAnimationPresent(ResourceLocation location) {
|
||||
return getAllAnimations().contains(location);
|
||||
}
|
||||
|
||||
public @Nullable AnimationData getAnimationData(ResourceLocation location) {
|
||||
IAnimationService<?, ?> helper = AnimationApi.getServiceGetterHelper(location).getService();
|
||||
if(helper == null) return null;
|
||||
return helper.getAnimation(location);
|
||||
}
|
||||
|
||||
public AnimationData getDataByCompoundTag(CompoundTag tag) {
|
||||
AnimationData animationData = new AnimationData();
|
||||
animationData.deserializeNBT(tag);
|
||||
IAnimationService<?, ?> service = AnimationApi.getServiceGetterHelper(animationData.getKey()).getService();
|
||||
if(service == null) return animationData;
|
||||
return service.getAnimation(tag);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,281 +0,0 @@
|
|||
/*
|
||||
* Anim Core mod
|
||||
* Copyright (C) 2026 LeisureTimeDock
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package top.leisuretimedock.animationcore.animation.helper;
|
||||
|
||||
import top.leisuretimedock.animationcore.animation.AnimationApi;
|
||||
import top.leisuretimedock.animationcore.animation.data.AnimationData;
|
||||
import top.leisuretimedock.animationcore.animation.service.IAnimationService;
|
||||
import top.leisuretimedock.animationcore.animation.utils.ApiBack;
|
||||
import top.leisuretimedock.animationcore.core.IModLazyRun;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.multiplayer.ClientLevel;
|
||||
import net.minecraft.client.player.AbstractClientPlayer;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
import net.minecraftforge.api.distmarker.Dist;
|
||||
import net.minecraftforge.api.distmarker.OnlyIn;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.UUID;
|
||||
|
||||
public class AnimationHelper {
|
||||
private final Player player;
|
||||
private final AnimationLazyHelper lazyRun;
|
||||
AnimationHelper(Player player) {
|
||||
this.player = player;
|
||||
this.lazyRun = new AnimationLazyHelper(player);
|
||||
}
|
||||
|
||||
public static AnimationHelper getHelper(Player player) {
|
||||
return new AnimationHelper(player);
|
||||
}
|
||||
|
||||
public ApiBack playAnimation(ResourceLocation layer, ResourceLocation location) {
|
||||
IAnimationService<?, ?> service = AnimationApi.getServiceGetterHelper(location).getService();
|
||||
if(service == null) return ApiBack.FAIL;
|
||||
AnimationData data = service.getAnimation(location);
|
||||
if(data == null) return ApiBack.FAIL;
|
||||
return lazyRun.testLoadedAndCall(
|
||||
() -> service.playAnimation(lazyRun.getServerPlayer(), layer, data),
|
||||
() -> service.playAnimation(lazyRun.getClientPlayer(), layer, location)
|
||||
);
|
||||
}
|
||||
|
||||
public ApiBack playAnimationWithRide(ResourceLocation layer, AnimationData animation, boolean isForce) {
|
||||
IAnimationService<?, ?> service = AnimationApi.getServiceGetterHelper(animation.getKey()).getService();
|
||||
if(service == null) return ApiBack.FAIL;
|
||||
return lazyRun.testLoadedAndCall(
|
||||
() -> service.playAnimationWithRide(lazyRun.getServerPlayer(), layer, animation, isForce),
|
||||
() -> service.playAnimationWithRide(lazyRun.getClientPlayer(), layer, animation.getKey(), isForce)
|
||||
);
|
||||
}
|
||||
public ApiBack playAnimationWithRide(ResourceLocation layer, AnimationData animation, boolean isForce, Vec3 pos) {
|
||||
IAnimationService<?, ?> service = AnimationApi.getServiceGetterHelper(animation.getKey()).getService();
|
||||
if(service == null) return ApiBack.FAIL;
|
||||
return lazyRun.testLoadedAndCall(
|
||||
() -> service.playAnimationWithRide(lazyRun.getServerPlayer(), layer, animation, isForce, pos),
|
||||
() -> ApiBack.UNSUPPORTED
|
||||
);
|
||||
}
|
||||
public ApiBack playAnimationWithRide(ResourceLocation layer, AnimationData animation, boolean isForce, BlockPos pos) {
|
||||
return playAnimationWithRide(layer, animation, isForce, pos.getCenter());
|
||||
}
|
||||
|
||||
public void clearAnimation() {
|
||||
for (IAnimationService<?, ?> service : AnimationApi.getServiceGetterHelper().getAllServices()) {
|
||||
lazyRun.testLoadedAndRun(
|
||||
() -> service.clearAnimations(lazyRun.getServerPlayer()),
|
||||
() -> service.clearAnimations(lazyRun.getClientPlayer())
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public ApiBack detachAnimation() {
|
||||
ApiBack result = ApiBack.FAIL;
|
||||
for (IAnimationService<?, ?> service : AnimationApi.getServiceGetterHelper().getAllServices()) {
|
||||
ApiBack back = lazyRun.testLoadedAndCall(() -> service.detachAnimation(lazyRun.getServerPlayer()));
|
||||
if(back == ApiBack.SUCCESS) return back;
|
||||
else result = back;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public ApiBack joinAnimation(Player target, boolean force) {
|
||||
ApiBack result = ApiBack.FAIL;
|
||||
for (IAnimationService<?, ?> service : AnimationApi.getServiceGetterHelper().getAllServices()) {
|
||||
ApiBack apiBack = lazyRun.testLoadedAndCall(() -> {
|
||||
if(!(target instanceof ServerPlayer targetPlayer)) return ApiBack.UNSUPPORTED;
|
||||
return service.joinAnimation(lazyRun.getServerPlayer(), targetPlayer, force);
|
||||
});
|
||||
if(apiBack == ApiBack.SUCCESS) return apiBack;
|
||||
else result = apiBack;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public ApiBack syncToAnimation(Player target) {
|
||||
ApiBack result = ApiBack.FAIL;
|
||||
for (IAnimationService<?, ?> service : AnimationApi.getServiceGetterHelper().getAllServices()) {
|
||||
ApiBack apiBack = lazyRun.testLoadedAndCall(() -> {
|
||||
if(!(target instanceof ServerPlayer targetPlayer)) return ApiBack.UNSUPPORTED;
|
||||
service.syncAnimation(lazyRun.getServerPlayer(), targetPlayer);
|
||||
return ApiBack.SUCCESS;
|
||||
}, () -> {
|
||||
if(!(target instanceof AbstractClientPlayer targetPlayer)) return ApiBack.UNSUPPORTED;
|
||||
service.syncAnimation(lazyRun.getClientPlayer(), targetPlayer);
|
||||
return ApiBack.SUCCESS;
|
||||
});
|
||||
if(apiBack == ApiBack.SUCCESS) return apiBack;
|
||||
else result = apiBack;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@OnlyIn(Dist.CLIENT)
|
||||
public void refreshAnimation() {
|
||||
for (IAnimationService<?, ?> service : AnimationApi.getServiceGetterHelper().getAllServices()) {
|
||||
service.refreshAnimation(lazyRun.getClientPlayer());
|
||||
}
|
||||
}
|
||||
|
||||
@OnlyIn(Dist.CLIENT)
|
||||
public void refreshAnimationUnsafe() {
|
||||
for (IAnimationService<?, ?> service : AnimationApi.getServiceGetterHelper().getAllServices()) {
|
||||
service.refreshAnimationUnsafe(lazyRun.getClientPlayer());
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public ResourceLocation getAnimationPlaying(@Nullable ResourceLocation layer) {
|
||||
for (IAnimationService<?, ?> service : AnimationApi.getServiceGetterHelper().getAllServices()) {
|
||||
ResourceLocation playing = service.getAnimationPlaying(player, layer);
|
||||
if(playing != null) return playing;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public ApiBack removeAnimation(ResourceLocation layer) {
|
||||
ApiBack result = ApiBack.FAIL;
|
||||
for (IAnimationService<?, ?> service : AnimationApi.getServiceGetterHelper().getAllServices()) {
|
||||
ApiBack apiBack = lazyRun.testLoadedAndCall(
|
||||
() -> service.removeAnimation(lazyRun.getServerPlayer(), layer),
|
||||
() -> service.removeAnimation(lazyRun.getClientPlayer(), layer)
|
||||
);
|
||||
if(apiBack == ApiBack.SUCCESS) return apiBack;
|
||||
else result = apiBack;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public ApiBack inviteAnimation(ResourceLocation layer, ResourceLocation location, Collection<UUID> targets) {
|
||||
IAnimationService<?, ?> service = AnimationApi.getServiceGetterHelper(location).getService();
|
||||
if(service == null) return ApiBack.FAIL;
|
||||
return lazyRun.testLoadedAndCall(
|
||||
() -> {
|
||||
AnimationData data = service.getAnimation(location);
|
||||
if(data == null) return ApiBack.FAIL;
|
||||
return service.invite(lazyRun.getServerPlayer(), layer, data, targets);
|
||||
},
|
||||
() -> {
|
||||
ClientLevel level = Minecraft.getInstance().level;
|
||||
if(level == null) return ApiBack.FAIL;
|
||||
List<AbstractClientPlayer> list = targets.stream().map(level::getPlayerByUUID)
|
||||
.filter(Objects::nonNull).map(AbstractClientPlayer.class::cast).toList();
|
||||
if(list.isEmpty()) return ApiBack.FAIL;
|
||||
return service.invite(layer, location, list.toArray(new AbstractClientPlayer[]{}));
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
public ApiBack applyAnimation(Player target) {
|
||||
ApiBack result = ApiBack.FAIL;
|
||||
for (IAnimationService<?, ?> service : AnimationApi.getServiceGetterHelper().getAllServices()) {
|
||||
ApiBack apiBack = lazyRun.testLoadedAndCall(() -> {
|
||||
if(!(target instanceof ServerPlayer targetPlayer)) return ApiBack.UNSUPPORTED;
|
||||
return service.apply(lazyRun.getServerPlayer(), targetPlayer);
|
||||
}, () -> {
|
||||
if(!(target instanceof AbstractClientPlayer targetPlayer)) return ApiBack.UNSUPPORTED;
|
||||
return service.apply(targetPlayer);
|
||||
});
|
||||
if(apiBack == ApiBack.SUCCESS) return apiBack;
|
||||
else result = apiBack;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public ApiBack requestAnimation(Player target, ResourceLocation layer, ResourceLocation location, boolean isRide) {
|
||||
IAnimationService<?, ?> service = AnimationApi.getServiceGetterHelper(location).getService();
|
||||
if(service == null) return ApiBack.FAIL;
|
||||
return lazyRun.testLoadedAndCall(
|
||||
() -> {
|
||||
AnimationData data = service.getAnimation(location);
|
||||
if(data == null) return ApiBack.FAIL;
|
||||
if(!(target instanceof ServerPlayer targetPlayer)) return ApiBack.UNSUPPORTED;
|
||||
return service.request(lazyRun.getServerPlayer(), targetPlayer, layer, data, isRide);
|
||||
},
|
||||
() -> {
|
||||
if(!(target instanceof AbstractClientPlayer targetPlayer)) return ApiBack.UNSUPPORTED;
|
||||
return service.request(targetPlayer, layer, location);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
public ApiBack acceptInvite(Player inviter) {
|
||||
ApiBack result = ApiBack.FAIL;
|
||||
for (IAnimationService<?, ?> service : AnimationApi.getServiceGetterHelper().getAllServices()) {
|
||||
ApiBack apiBack = lazyRun.testLoadedAndCall(() -> {
|
||||
if(!(inviter instanceof ServerPlayer inviterPlayer)) return ApiBack.UNSUPPORTED;
|
||||
return service.acceptInvite(lazyRun.getServerPlayer(), inviterPlayer);
|
||||
});
|
||||
if(apiBack == ApiBack.SUCCESS) return apiBack;
|
||||
else result = apiBack;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public ApiBack acceptApply(Player applier) {
|
||||
ApiBack result = ApiBack.FAIL;
|
||||
for (IAnimationService<?, ?> service : AnimationApi.getServiceGetterHelper().getAllServices()) {
|
||||
ApiBack apiBack = lazyRun.testLoadedAndCall(() -> {
|
||||
if(!(applier instanceof ServerPlayer applierPlayer)) return ApiBack.UNSUPPORTED;
|
||||
return service.acceptApply(lazyRun.getServerPlayer(), applierPlayer);
|
||||
});
|
||||
if(apiBack == ApiBack.SUCCESS) return apiBack;
|
||||
else result = apiBack;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public ApiBack acceptRequest(Player requestor) {
|
||||
ApiBack result = ApiBack.FAIL;
|
||||
for (IAnimationService<?, ?> service : AnimationApi.getServiceGetterHelper().getAllServices()) {
|
||||
ApiBack apiBack = lazyRun.testLoadedAndCall(() -> {
|
||||
if(!(requestor instanceof ServerPlayer requestorPlayer)) return ApiBack.UNSUPPORTED;
|
||||
return service.acceptRequest(lazyRun.getServerPlayer(), requestorPlayer);
|
||||
});
|
||||
if(apiBack == ApiBack.SUCCESS) return apiBack;
|
||||
else result = apiBack;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static class AnimationLazyHelper implements IModLazyRun {
|
||||
private final Player player;
|
||||
AnimationLazyHelper(Player player) {
|
||||
this.player = player;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean testCondition() {
|
||||
return player instanceof ServerPlayer;
|
||||
}
|
||||
|
||||
public ServerPlayer getServerPlayer() {
|
||||
return (ServerPlayer) player;
|
||||
}
|
||||
|
||||
@OnlyIn(Dist.CLIENT)
|
||||
public AbstractClientPlayer getClientPlayer() {
|
||||
return (AbstractClientPlayer) player;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,108 +0,0 @@
|
|||
/*
|
||||
* Anim Core mod
|
||||
* Copyright (C) 2026 LeisureTimeDock
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package top.leisuretimedock.animationcore.animation.helper;
|
||||
|
||||
import top.leisuretimedock.animationcore.animation.data.GenericAnimationData;
|
||||
import top.leisuretimedock.animationcore.animation.data.util.AnimJson;
|
||||
import top.leisuretimedock.animationcore.animation.data.util.AnimLayerJson;
|
||||
import top.leisuretimedock.animationcore.animation.register.AnimationRegistry;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.world.level.storage.LevelResource;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Comparator;
|
||||
|
||||
public class AnimationJsonHelper {
|
||||
private final MinecraftServer server;
|
||||
AnimationJsonHelper(MinecraftServer server) {
|
||||
this.server = server;
|
||||
}
|
||||
|
||||
public static AnimationJsonHelper getHelper(MinecraftServer server) {
|
||||
return new AnimationJsonHelper(server);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get animation path
|
||||
* @return path
|
||||
*/
|
||||
private Path getAnimationPath() {
|
||||
Path dataPackPath = server.getWorldPath(LevelResource.DATAPACK_DIR);
|
||||
return dataPackPath.resolve("animation");
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete directories
|
||||
* @throws IOException Exception
|
||||
*/
|
||||
public void clearPath() throws IOException {
|
||||
Path animationPath = getAnimationPath();
|
||||
if (!Files.exists(animationPath)) return;
|
||||
try (var pathStream = Files.walk(animationPath)) {
|
||||
pathStream.sorted(Comparator.reverseOrder()).forEach(path -> {
|
||||
try {
|
||||
Files.delete(path);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
});
|
||||
} catch (RuntimeException ignored) {}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate all json from server animation
|
||||
* @param isLayer If layer
|
||||
* @param isReset If reset
|
||||
* @return Generate path
|
||||
*/
|
||||
@Nullable
|
||||
public Path generateJson(boolean isLayer, boolean isReset) {
|
||||
try {
|
||||
Path animationPath = getAnimationPath();
|
||||
if (!Files.exists(animationPath)) {
|
||||
try {Files.createDirectories(animationPath);}
|
||||
catch (IOException e) { throw new RuntimeException(e); }
|
||||
}
|
||||
if(isReset) clearPath();
|
||||
|
||||
if(isLayer) {
|
||||
return AnimLayerJson.Writer.syntaxImmediately(animationPath);
|
||||
} else {
|
||||
for (GenericAnimationData value : AnimationRegistry.getAnimations().values()) {
|
||||
AnimJson.Writer.stream(animationPath, value).syntax();
|
||||
}
|
||||
}
|
||||
return animationPath;
|
||||
} catch (Exception ignored){}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate example json
|
||||
* @return Example json path
|
||||
*/
|
||||
@Nullable
|
||||
public Path generateExample() {
|
||||
try {
|
||||
Path animationPath = getAnimationPath();
|
||||
return AnimJson.Writer.syntaxExample(animationPath);
|
||||
} catch (Exception ignored) {}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,73 +0,0 @@
|
|||
/*
|
||||
* Anim Core mod
|
||||
* Copyright (C) 2026 LeisureTimeDock
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package top.leisuretimedock.animationcore.animation.helper;
|
||||
|
||||
import top.leisuretimedock.animationcore.animation.data.AnimationData;
|
||||
import top.leisuretimedock.animationcore.animation.service.IAnimationService;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
|
||||
public class AnimationServiceGetterHelper implements IAnimationServiceGetter {
|
||||
private final @Nullable ResourceLocation location;
|
||||
private final @Nullable CompoundTag tag;
|
||||
|
||||
public AnimationServiceGetterHelper(@NotNull ResourceLocation location) {
|
||||
this.location = location;
|
||||
this.tag = null;
|
||||
}
|
||||
|
||||
public AnimationServiceGetterHelper(@NotNull CompoundTag tag) {
|
||||
this.tag = tag;
|
||||
this.location = null;
|
||||
}
|
||||
|
||||
public AnimationServiceGetterHelper(){
|
||||
this.tag = null;
|
||||
this.location = null;
|
||||
}
|
||||
|
||||
public static IAnimationServiceGetter create(ResourceLocation location) {
|
||||
return new AnimationServiceGetterHelper(location);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean filter(IAnimationService<?, ?> helper) {
|
||||
if(location != null && helper.isAnimationPresent(location)) return true;
|
||||
return Optional.ofNullable(tag).map(helper::getAnimation).map(AnimationData::getKey)
|
||||
.map(helper::isAnimationPresent).isPresent();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable IAnimationService<?, ?> getService() {
|
||||
return IAnimationServiceGetter.super.getService();
|
||||
}
|
||||
|
||||
public Set<IAnimationService<?, ?>> getAllServices() {
|
||||
return IAnimationServiceGetter.HELPERS;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public <T extends IAnimationService<?, ?>> T getService(Class<T> tClass) {
|
||||
IAnimationService<?, ?> service = getService();
|
||||
if(tClass.isInstance(service)) return tClass.cast(service);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,49 +0,0 @@
|
|||
/*
|
||||
* Anim Core mod
|
||||
* Copyright (C) 2026 LeisureTimeDock
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package top.leisuretimedock.animationcore.animation.helper;
|
||||
|
||||
import top.leisuretimedock.animationcore.animation.service.AnimationService;
|
||||
import top.leisuretimedock.animationcore.animation.service.IAnimationService;
|
||||
import top.leisuretimedock.animationcore.animation.service.RawAnimationService;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Set;
|
||||
|
||||
public interface IAnimationServiceGetter {
|
||||
/**
|
||||
* Get helper
|
||||
*/
|
||||
Set<IAnimationService<?, ?>> HELPERS = new LinkedHashSet<>(){{
|
||||
add(AnimationService.INSTANCE);
|
||||
add(RawAnimationService.INSTANCE);
|
||||
}};
|
||||
|
||||
/**
|
||||
* @see IAnimationServiceGetter#filter
|
||||
*/
|
||||
@Nullable
|
||||
default IAnimationService<?, ?> getService() {
|
||||
for (IAnimationService<?, ?> helper : HELPERS) {
|
||||
if (filter(helper)) {
|
||||
return helper;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
boolean filter(IAnimationService<?, ?> helper);
|
||||
}
|
||||
|
|
@ -1,24 +0,0 @@
|
|||
/*
|
||||
* Anim Core mod
|
||||
* Copyright (C) 2026 LeisureTimeDock
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package top.leisuretimedock.animationcore.animation.mixin;
|
||||
|
||||
import net.minecraftforge.api.distmarker.Dist;
|
||||
import net.minecraftforge.api.distmarker.OnlyIn;
|
||||
|
||||
@OnlyIn(Dist.CLIENT)
|
||||
public interface IMixinKeyframeAnimationPlayer {
|
||||
void animcore$setCurrentTick(int tick);
|
||||
}
|
||||
|
|
@ -1,27 +0,0 @@
|
|||
/*
|
||||
* Anim Core mod
|
||||
* Copyright (C) 2026 LeisureTimeDock
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package top.leisuretimedock.animationcore.animation.mixin;
|
||||
|
||||
import dev.kosmx.playerAnim.api.layered.IAnimation;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public interface IMixinPlayerAnimationFactoryHolder {
|
||||
record DataHolder(@Nullable ResourceLocation id, int priority, @NotNull IAnimation animation) {}
|
||||
|
||||
void animcore$clearAnimations();
|
||||
}
|
||||
|
|
@ -1,62 +0,0 @@
|
|||
/*
|
||||
* Anim Core mod
|
||||
* Copyright (C) 2026 LeisureTimeDock
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package top.leisuretimedock.animationcore.animation.network;
|
||||
|
||||
import top.leisuretimedock.animationcore.animation.helper.IAnimationServiceGetter;
|
||||
import top.leisuretimedock.animationcore.animation.service.AnimationService;
|
||||
import top.leisuretimedock.animationcore.animation.service.IAnimationService;
|
||||
import top.leisuretimedock.animationcore.animation.service.RawAnimationService;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public abstract class ServiceGetterPacket implements IAnimationServiceGetter {
|
||||
/**
|
||||
* Override it to filter helper in network packet
|
||||
* @param helper helper
|
||||
* @return Is right helper
|
||||
*/
|
||||
public boolean filter(IAnimationService<?, ?> helper) {
|
||||
if(helper instanceof AnimationService service) {
|
||||
ResourceLocation animation = getAnimation();
|
||||
if(animation != null) return service.isAnimationPresent(animation);
|
||||
} else if (helper instanceof RawAnimationService service) {
|
||||
CompoundTag tag = getAnimationTag();
|
||||
if(tag != null) return service.getAnimation(tag) != null;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Selectable to override it
|
||||
* @return Animation loacation
|
||||
* @see ServiceGetterPacket#filter
|
||||
*/
|
||||
@Nullable
|
||||
protected ResourceLocation getAnimation() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Selectable to override it
|
||||
* @return Animation data
|
||||
* @see ServiceGetterPacket#filter
|
||||
*/
|
||||
@Nullable
|
||||
protected CompoundTag getAnimationTag() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,90 +0,0 @@
|
|||
/*
|
||||
* Anim Core mod
|
||||
* Copyright (C) 2026 LeisureTimeDock
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package top.leisuretimedock.animationcore.animation.network.toclient;
|
||||
|
||||
import top.leisuretimedock.animationcore.animation.capability.AnimationDataCapability;
|
||||
import top.leisuretimedock.animationcore.animation.capability.inter.IAnimationCapability;
|
||||
import top.leisuretimedock.animationcore.animation.utils.AnimationUtils;
|
||||
import top.leisuretimedock.animationcore.capability.data.ICapabilitySync;
|
||||
import top.leisuretimedock.animationcore.capability.data.player.SimplePlayerCapabilitySync;
|
||||
import top.leisuretimedock.animationcore.capability.network.SimpleCapabilityPacket;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.multiplayer.ClientLevel;
|
||||
import net.minecraft.client.player.AbstractClientPlayer;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.network.FriendlyByteBuf;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraftforge.network.NetworkEvent;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
|
||||
public class AnimationCapabilityPacket extends SimpleCapabilityPacket<Player> {
|
||||
public AnimationCapabilityPacket(ICapabilitySync<Player> packet) {
|
||||
super(packet);
|
||||
}
|
||||
|
||||
public AnimationCapabilityPacket(FriendlyByteBuf buf) {
|
||||
super(buf);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handler(NetworkEvent.Context context) {
|
||||
context.setPacketHandled(true);
|
||||
Minecraft instance = Minecraft.getInstance();
|
||||
ClientLevel level = instance.level;
|
||||
if (level == null) return;
|
||||
CompoundTag nbt = getData();
|
||||
Player player = level.getPlayerByUUID(nbt.getUUID(SimplePlayerCapabilitySync.OwnerUUID));
|
||||
if(player == null) return;
|
||||
try {
|
||||
IAnimationCapability data = AnimationDataCapability.getCapability(player).orElse(null);
|
||||
testPlayAnimations((AbstractClientPlayer) player, nbt, data);
|
||||
syncData(nbt, data);
|
||||
}catch (Exception ignored) {}
|
||||
}
|
||||
|
||||
private void testPlayAnimations(AbstractClientPlayer player, CompoundTag tag, IAnimationCapability data) {
|
||||
if(data == null) return;
|
||||
ResourceLocation oldRiderAnimLayer = data.getRiderAnimLayer();
|
||||
String riderAnimLayerString = tag.getString(AnimationDataCapability.RideAnimLayer);
|
||||
ResourceLocation newRiderAnimLayer = riderAnimLayerString.isEmpty() ? null : new ResourceLocation(riderAnimLayerString);
|
||||
if(!Objects.equals(oldRiderAnimLayer, newRiderAnimLayer)) {
|
||||
String riderAnimationString = tag.getString(AnimationDataCapability.RideAnimation);
|
||||
ResourceLocation newRiderAnimation = riderAnimationString.isEmpty() ? null : new ResourceLocation(riderAnimationString);
|
||||
if(oldRiderAnimLayer != null) AnimationUtils.playAnimation(player, oldRiderAnimLayer, null);
|
||||
if(newRiderAnimLayer != null) AnimationUtils.playAnimation(player, newRiderAnimLayer, newRiderAnimation);
|
||||
}
|
||||
|
||||
Set<ResourceLocation> oldLayerSet = new HashSet<>(data.getAnimations().keySet());
|
||||
CompoundTag animMap = tag.getCompound(AnimationDataCapability.AnimMap);
|
||||
for (String newLayerString : animMap.getAllKeys()) {
|
||||
ResourceLocation newLayerLocation = new ResourceLocation(newLayerString);
|
||||
String newAnimString = animMap.getString(newLayerString);
|
||||
ResourceLocation newAnimLocation = newAnimString.isEmpty() ? null : new ResourceLocation(newAnimString);
|
||||
ResourceLocation oldAnimLocation = data.getAnimation(newLayerLocation);
|
||||
if (!Objects.equals(newAnimLocation, oldAnimLocation)) {
|
||||
AnimationUtils.playAnimation(player, newLayerLocation, newAnimLocation);
|
||||
}
|
||||
oldLayerSet.remove(newLayerLocation);
|
||||
}
|
||||
for (ResourceLocation oldLayerLocation : oldLayerSet) {
|
||||
AnimationUtils.playAnimation(player, oldLayerLocation, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,60 +0,0 @@
|
|||
/*
|
||||
* Anim Core mod
|
||||
* Copyright (C) 2026 LeisureTimeDock
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package top.leisuretimedock.animationcore.animation.network.toclient;
|
||||
|
||||
import top.leisuretimedock.animationcore.animation.register.AnimationRegistry;
|
||||
import top.leisuretimedock.animationcore.animation.utils.AnimationUtils;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.player.LocalPlayer;
|
||||
import net.minecraft.network.FriendlyByteBuf;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraftforge.api.distmarker.Dist;
|
||||
import net.minecraftforge.api.distmarker.OnlyIn;
|
||||
import net.minecraftforge.fml.DistExecutor;
|
||||
import net.minecraftforge.network.NetworkEvent;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public record AnimationClearPacket(@Nullable ResourceLocation layer) {
|
||||
public AnimationClearPacket(FriendlyByteBuf buf) {
|
||||
this(buf.readNullable(FriendlyByteBuf::readResourceLocation));
|
||||
}
|
||||
|
||||
public void encode(FriendlyByteBuf buf) {
|
||||
buf.writeNullable(layer, FriendlyByteBuf::writeResourceLocation);
|
||||
}
|
||||
|
||||
public void handle(Supplier<NetworkEvent.Context> supplier) {
|
||||
NetworkEvent.Context context = supplier.get();
|
||||
context.enqueueWork(() -> {
|
||||
context.setPacketHandled(true);
|
||||
DistExecutor.unsafeRunWhenOn(Dist.CLIENT, () -> this::handle);
|
||||
});
|
||||
}
|
||||
|
||||
@OnlyIn(Dist.CLIENT)
|
||||
public void handle(){
|
||||
LocalPlayer player = Minecraft.getInstance().player;
|
||||
if(player == null) return;
|
||||
List<ResourceLocation> layers = new ArrayList<>();
|
||||
if(layer != null) layers.add(layer);
|
||||
else layers.addAll(AnimationRegistry.getLayers().keySet());
|
||||
layers.forEach(layer -> AnimationUtils.playAnimation(player, layer, null));
|
||||
}
|
||||
}
|
||||
|
|
@ -1,62 +0,0 @@
|
|||
/*
|
||||
* Anim Core mod
|
||||
* Copyright (C) 2026 LeisureTimeDock
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package top.leisuretimedock.animationcore.animation.network.toclient;
|
||||
|
||||
import top.leisuretimedock.animationcore.animation.register.AnimationRegistry;
|
||||
import net.minecraft.network.FriendlyByteBuf;
|
||||
import net.minecraftforge.network.NetworkEvent;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public record AnimationClientStatusPacket(int status) {
|
||||
public enum Status {
|
||||
ANIM_CACHE_CLEAR(0),
|
||||
LAYER_CACHE_CLEAR(1),
|
||||
ANIM_REGISTER(2),
|
||||
LAYER_REGISTER(3),;
|
||||
private final int value;
|
||||
Status(final int value) {
|
||||
this.value = value;
|
||||
}
|
||||
@Nullable
|
||||
public static Status getStatus(final int value) {
|
||||
for (Status status : values()) {
|
||||
if (status.value == value) {
|
||||
return status;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
public AnimationClientStatusPacket(Status status) {
|
||||
this(status.value);
|
||||
}
|
||||
public AnimationClientStatusPacket(FriendlyByteBuf buf) {
|
||||
this(buf.readInt());
|
||||
}
|
||||
public void encode(FriendlyByteBuf buf) {
|
||||
buf.writeInt(status);
|
||||
}
|
||||
public void handle(Supplier<NetworkEvent.Context> supplier) {
|
||||
NetworkEvent.Context context = supplier.get();
|
||||
context.enqueueWork(() -> {
|
||||
Status state = Status.getStatus(status);
|
||||
if(state == null) return;
|
||||
AnimationRegistry.ClientCache.animationStatusUpdate(state);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -1,54 +0,0 @@
|
|||
/*
|
||||
* Anim Core mod
|
||||
* Copyright (C) 2026 LeisureTimeDock
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package top.leisuretimedock.animationcore.animation.network.toclient;
|
||||
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonParser;
|
||||
import top.leisuretimedock.animationcore.animation.data.GenericAnimationData;
|
||||
import top.leisuretimedock.animationcore.animation.data.util.AnimJson;
|
||||
import top.leisuretimedock.animationcore.animation.data.util.AnimLayerJson;
|
||||
import top.leisuretimedock.animationcore.animation.register.AnimationRegistry;
|
||||
import net.minecraft.network.FriendlyByteBuf;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraftforge.network.NetworkEvent;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public record AnimationJsonPacket(String json, boolean isLayer) {
|
||||
public AnimationJsonPacket(FriendlyByteBuf buf) {
|
||||
this(buf.readUtf(), buf.readBoolean());
|
||||
}
|
||||
public void encode(FriendlyByteBuf buf) {
|
||||
buf.writeUtf(json);
|
||||
buf.writeBoolean(isLayer);
|
||||
}
|
||||
|
||||
public void handle(Supplier<NetworkEvent.Context> supplier) {
|
||||
NetworkEvent.Context context = supplier.get();
|
||||
context.enqueueWork(() -> {
|
||||
context.setPacketHandled(true);
|
||||
JsonElement element = JsonParser.parseString(json);
|
||||
if(isLayer) {
|
||||
Map<ResourceLocation, Integer> parse = AnimLayerJson.Reader.stream(element).parse();
|
||||
parse.forEach(AnimationRegistry.ClientCache::cacheAddAnimationLayer);
|
||||
} else {
|
||||
GenericAnimationData animation = AnimJson.Reader.stream(element).parse();
|
||||
AnimationRegistry.ClientCache.cacheAddAnimation(animation.getKey(), animation);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -1,79 +0,0 @@
|
|||
/*
|
||||
* Anim Core mod
|
||||
* Copyright (C) 2026 LeisureTimeDock
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package top.leisuretimedock.animationcore.animation.network.toclient;
|
||||
|
||||
import top.leisuretimedock.animationcore.animation.capability.RawAnimationDataCapability;
|
||||
import top.leisuretimedock.animationcore.animation.utils.AnimationUtils;
|
||||
import top.leisuretimedock.animationcore.capability.data.ICapabilitySync;
|
||||
import top.leisuretimedock.animationcore.capability.data.player.SimplePlayerCapabilitySync;
|
||||
import top.leisuretimedock.animationcore.capability.network.SimpleCapabilityPacket;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.multiplayer.ClientLevel;
|
||||
import net.minecraft.client.player.AbstractClientPlayer;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.network.FriendlyByteBuf;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraftforge.network.NetworkEvent;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
|
||||
public class RawAnimationCapabilityPacket extends SimpleCapabilityPacket<Player> {
|
||||
public RawAnimationCapabilityPacket(ICapabilitySync<Player> packet) {
|
||||
super(packet);
|
||||
}
|
||||
|
||||
public RawAnimationCapabilityPacket(FriendlyByteBuf buf) {
|
||||
super(buf);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handler(NetworkEvent.Context context) {
|
||||
context.setPacketHandled(true);
|
||||
Minecraft instance = Minecraft.getInstance();
|
||||
ClientLevel level = instance.level;
|
||||
if (level == null) return;
|
||||
CompoundTag nbt = getData();
|
||||
Player player = level.getPlayerByUUID(nbt.getUUID(SimplePlayerCapabilitySync.OwnerUUID));
|
||||
if(player == null) return;
|
||||
try {
|
||||
RawAnimationDataCapability data = RawAnimationDataCapability.getCapability(player).orElse(null);
|
||||
testPlayAnimations((AbstractClientPlayer) player, nbt, data);
|
||||
syncData(nbt, data);
|
||||
}catch (Exception ignored) {}
|
||||
}
|
||||
|
||||
private void testPlayAnimations(AbstractClientPlayer player, CompoundTag tag, RawAnimationDataCapability data) {
|
||||
if(data == null) return;
|
||||
Set<ResourceLocation> oldLayerSet = new HashSet<>(data.getAnimations().keySet());
|
||||
CompoundTag animMap = tag.getCompound(RawAnimationDataCapability.AnimMap);
|
||||
for (String newLayerString : animMap.getAllKeys()) {
|
||||
ResourceLocation newLayerLocation = new ResourceLocation(newLayerString);
|
||||
String newAnimString = animMap.getString(newLayerString);
|
||||
ResourceLocation newAnimLocation = newAnimString.isEmpty() ? null : new ResourceLocation(newAnimString);
|
||||
ResourceLocation oldAnimLocation = data.getAnimation(newLayerLocation);
|
||||
if (!Objects.equals(newAnimLocation, oldAnimLocation)) {
|
||||
AnimationUtils.playAnimation(player, newLayerLocation, newAnimLocation);
|
||||
}
|
||||
oldLayerSet.remove(newLayerLocation);
|
||||
}
|
||||
for (ResourceLocation oldLayerLocation : oldLayerSet) {
|
||||
AnimationUtils.playAnimation(player, oldLayerLocation, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,63 +0,0 @@
|
|||
/*
|
||||
* Anim Core mod
|
||||
* Copyright (C) 2026 LeisureTimeDock
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package top.leisuretimedock.animationcore.animation.network.toclient;
|
||||
|
||||
import top.leisuretimedock.animationcore.animation.event.client.ClientPlayerEvent;
|
||||
import top.leisuretimedock.animationcore.animation.utils.AnimationUtils;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.multiplayer.ClientLevel;
|
||||
import net.minecraft.client.player.AbstractClientPlayer;
|
||||
import net.minecraft.network.FriendlyByteBuf;
|
||||
import net.minecraftforge.network.NetworkEvent;
|
||||
|
||||
import java.util.AbstractMap;
|
||||
import java.util.UUID;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public class SyncAnimationPacket {
|
||||
private final UUID playerUUID;
|
||||
private final UUID targetUUID;
|
||||
public SyncAnimationPacket(UUID playerUUID, UUID targetUUID) {
|
||||
this.playerUUID = playerUUID;
|
||||
this.targetUUID = targetUUID;
|
||||
}
|
||||
|
||||
public SyncAnimationPacket(FriendlyByteBuf buf) {
|
||||
this.playerUUID = buf.readUUID();
|
||||
this.targetUUID = buf.readUUID();
|
||||
}
|
||||
|
||||
public void encode(FriendlyByteBuf buf) {
|
||||
buf.writeUUID(this.playerUUID);
|
||||
buf.writeUUID(this.targetUUID);
|
||||
}
|
||||
|
||||
public void handle(Supplier<NetworkEvent.Context> supplier) {
|
||||
NetworkEvent.Context context = supplier.get();
|
||||
context.enqueueWork(() -> {
|
||||
context.setPacketHandled(true);
|
||||
Minecraft instance = Minecraft.getInstance();
|
||||
ClientLevel level = instance.level;
|
||||
if(level == null) return;
|
||||
AbstractClientPlayer player = (AbstractClientPlayer) level.getPlayerByUUID(playerUUID);
|
||||
AbstractClientPlayer target = (AbstractClientPlayer) level.getPlayerByUUID(targetUUID);
|
||||
ClientPlayerEvent.runs.put(
|
||||
() -> AnimationUtils.syncAnimation(player, target),
|
||||
new AbstractMap.SimpleEntry<>(5, 0)
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -1,63 +0,0 @@
|
|||
/*
|
||||
* Anim Core mod
|
||||
* Copyright (C) 2026 LeisureTimeDock
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package top.leisuretimedock.animationcore.animation.network.toserver;
|
||||
|
||||
import top.leisuretimedock.animationcore.animation.network.ServiceGetterPacket;
|
||||
import top.leisuretimedock.animationcore.animation.service.IAnimationService;
|
||||
import net.minecraft.network.FriendlyByteBuf;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraftforge.network.NetworkEvent;
|
||||
|
||||
import java.util.UUID;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public class ApplyAnimationPacket extends ServiceGetterPacket {
|
||||
private final UUID targetUUID;
|
||||
|
||||
public ApplyAnimationPacket(UUID targetUUID) {
|
||||
this.targetUUID = targetUUID;
|
||||
}
|
||||
|
||||
public ApplyAnimationPacket(FriendlyByteBuf buf) {
|
||||
this.targetUUID = buf.readUUID();
|
||||
}
|
||||
|
||||
public void encode(FriendlyByteBuf buf) {
|
||||
buf.writeUUID(targetUUID);
|
||||
}
|
||||
|
||||
public void handle(Supplier<NetworkEvent.Context> supplier) {
|
||||
NetworkEvent.Context context = supplier.get();
|
||||
context.enqueueWork(() -> {
|
||||
context.setPacketHandled(true);
|
||||
ServerPlayer target;
|
||||
ServerPlayer sender = context.getSender();
|
||||
if(sender == null || sender.getServer() == null) return;
|
||||
if(this.targetUUID == null) target = sender;
|
||||
else target = sender.getServer().getPlayerList().getPlayer(this.targetUUID);
|
||||
if(target == null) return;
|
||||
IAnimationService<?, ?> service = getService();
|
||||
if(service == null) return;
|
||||
if(target == sender) return;
|
||||
service.apply(sender, target);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean filter(IAnimationService<?, ?> helper) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,83 +0,0 @@
|
|||
/*
|
||||
* Anim Core mod
|
||||
* Copyright (C) 2026 LeisureTimeDock
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package top.leisuretimedock.animationcore.animation.network.toserver;
|
||||
|
||||
import top.leisuretimedock.animationcore.animation.data.AnimationData;
|
||||
import top.leisuretimedock.animationcore.animation.network.ServiceGetterPacket;
|
||||
import top.leisuretimedock.animationcore.animation.service.IAnimationService;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.network.FriendlyByteBuf;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraftforge.network.NetworkEvent;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.UUID;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public class InviteAnimationPacket extends ServiceGetterPacket {
|
||||
private final @Nullable CompoundTag animationTag;
|
||||
private final ResourceLocation layer;
|
||||
private final ResourceLocation animation;
|
||||
private final Collection<UUID> targets;
|
||||
|
||||
public InviteAnimationPacket(AnimationData data, ResourceLocation layer, Collection<UUID> targets) {
|
||||
this.animationTag = data.serializeNBT();
|
||||
this.animation = data.getKey();
|
||||
this.layer = layer;
|
||||
this.targets = targets;
|
||||
}
|
||||
|
||||
public InviteAnimationPacket(FriendlyByteBuf buf) {
|
||||
this.animationTag = buf.readNullable(FriendlyByteBuf::readAnySizeNbt);
|
||||
this.layer = buf.readResourceLocation();
|
||||
this.animation = buf.readResourceLocation();
|
||||
this.targets = buf.readList(FriendlyByteBuf::readUUID);
|
||||
}
|
||||
|
||||
public void encode(FriendlyByteBuf buf) {
|
||||
buf.writeNullable(animationTag, FriendlyByteBuf::writeNbt);
|
||||
buf.writeResourceLocation(layer);
|
||||
buf.writeResourceLocation(animation);
|
||||
buf.writeCollection(targets, FriendlyByteBuf::writeUUID);
|
||||
}
|
||||
|
||||
public void handle(Supplier<NetworkEvent.Context> supplier) {
|
||||
NetworkEvent.Context context = supplier.get();
|
||||
context.enqueueWork(() -> {
|
||||
context.setPacketHandled(true);
|
||||
ServerPlayer sender = context.getSender();
|
||||
if(sender == null) return;
|
||||
IAnimationService<?, ?> service = getService();
|
||||
if(service == null) return;
|
||||
AnimationData data = service.getAnimation(animationTag);
|
||||
if(data == null) return;
|
||||
if(!targets.isEmpty()) service.invite(sender, layer, data, targets);
|
||||
else service.playAnimationWithRide(sender, layer, data, false);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable CompoundTag getAnimationTag() {
|
||||
return animationTag;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable ResourceLocation getAnimation() {
|
||||
return animation;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,86 +0,0 @@
|
|||
/*
|
||||
* Anim Core mod
|
||||
* Copyright (C) 2026 LeisureTimeDock
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package top.leisuretimedock.animationcore.animation.network.toserver;
|
||||
|
||||
import top.leisuretimedock.animationcore.animation.data.AnimationData;
|
||||
import top.leisuretimedock.animationcore.animation.network.ServiceGetterPacket;
|
||||
import top.leisuretimedock.animationcore.animation.service.IAnimationService;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.network.FriendlyByteBuf;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraftforge.network.NetworkEvent;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.UUID;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public class PlayAnimationPacket extends ServiceGetterPacket {
|
||||
private final @Nullable CompoundTag animationTag;
|
||||
private final ResourceLocation layer;
|
||||
private final ResourceLocation animation;
|
||||
private final @Nullable UUID uuid;
|
||||
|
||||
public PlayAnimationPacket(AnimationData data, ResourceLocation layer, ResourceLocation animation, @Nullable UUID uuid) {
|
||||
this.animationTag = data.serializeNBT();
|
||||
this.layer = layer;
|
||||
this.animation = animation;
|
||||
this.uuid = uuid;
|
||||
}
|
||||
|
||||
public PlayAnimationPacket(FriendlyByteBuf buf){
|
||||
this.animationTag = buf.readNullable(FriendlyByteBuf::readAnySizeNbt);
|
||||
this.layer = buf.readResourceLocation();
|
||||
this.animation = buf.readResourceLocation();
|
||||
this.uuid = buf.readNullable(FriendlyByteBuf::readUUID);
|
||||
}
|
||||
|
||||
public void encode(FriendlyByteBuf buf){
|
||||
buf.writeNullable(animationTag, FriendlyByteBuf::writeNbt);
|
||||
buf.writeResourceLocation(layer);
|
||||
buf.writeResourceLocation(animation);
|
||||
buf.writeNullable(uuid, FriendlyByteBuf::writeUUID);
|
||||
}
|
||||
|
||||
public void handle(Supplier<NetworkEvent.Context> supplier){
|
||||
NetworkEvent.Context context = supplier.get();
|
||||
context.enqueueWork(() -> {
|
||||
context.setPacketHandled(true);
|
||||
ServerPlayer target;
|
||||
ServerPlayer sender = context.getSender();
|
||||
if(sender == null || sender.getServer() == null) return;
|
||||
if(this.uuid == null) target = sender;
|
||||
else target = sender.getServer().getPlayerList().getPlayer(this.uuid);
|
||||
if(target == null) return;
|
||||
IAnimationService<?, ?> service = getService();
|
||||
if(service == null) return;
|
||||
AnimationData data = service.getAnimation(animationTag);
|
||||
if(data == null) return;
|
||||
if(target == sender) service.playAnimation(target, layer, data);
|
||||
else service.request(sender, target, layer, data, false);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable CompoundTag getAnimationTag() {
|
||||
return animationTag;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable ResourceLocation getAnimation() {
|
||||
return animation;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,90 +0,0 @@
|
|||
/*
|
||||
* Anim Core mod
|
||||
* Copyright (C) 2026 LeisureTimeDock
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package top.leisuretimedock.animationcore.animation.network.toserver;
|
||||
|
||||
import top.leisuretimedock.animationcore.animation.data.AnimationData;
|
||||
import top.leisuretimedock.animationcore.animation.network.ServiceGetterPacket;
|
||||
import top.leisuretimedock.animationcore.animation.service.IAnimationService;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.network.FriendlyByteBuf;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraftforge.network.NetworkEvent;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.UUID;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public class PlayAnimationRidePacket extends ServiceGetterPacket {
|
||||
private final @Nullable CompoundTag animationTag;
|
||||
private final ResourceLocation layer;
|
||||
private final ResourceLocation animation;
|
||||
private final @Nullable UUID uuid;
|
||||
private final boolean force;
|
||||
|
||||
public PlayAnimationRidePacket(@Nullable AnimationData data, ResourceLocation layer, ResourceLocation animation, @Nullable UUID uuid, boolean force) {
|
||||
this.animationTag = data != null ? data.serializeNBT() : null;
|
||||
this.layer = layer;
|
||||
this.animation = animation;
|
||||
this.uuid = uuid;
|
||||
this.force = force;
|
||||
}
|
||||
|
||||
public PlayAnimationRidePacket(FriendlyByteBuf buf) {
|
||||
this.animationTag = buf.readNullable(FriendlyByteBuf::readAnySizeNbt);
|
||||
this.layer = buf.readResourceLocation();
|
||||
this.animation = buf.readResourceLocation();
|
||||
this.uuid = buf.readNullable(FriendlyByteBuf::readUUID);
|
||||
this.force = buf.readBoolean();
|
||||
}
|
||||
|
||||
public void encode(FriendlyByteBuf buf) {
|
||||
buf.writeNullable(animationTag, FriendlyByteBuf::writeNbt);
|
||||
buf.writeResourceLocation(layer);
|
||||
buf.writeResourceLocation(animation);
|
||||
buf.writeNullable(uuid, FriendlyByteBuf::writeUUID);
|
||||
buf.writeBoolean(force);
|
||||
}
|
||||
|
||||
public void handle(Supplier<NetworkEvent.Context> supplier) {
|
||||
NetworkEvent.Context context = supplier.get();
|
||||
context.enqueueWork(() -> {
|
||||
context.setPacketHandled(true);
|
||||
ServerPlayer target;
|
||||
ServerPlayer sender = context.getSender();
|
||||
if(sender == null || sender.getServer() == null) return;
|
||||
if(this.uuid == null) target = sender;
|
||||
else target = sender.getServer().getPlayerList().getPlayer(this.uuid);
|
||||
if(target == null) return;
|
||||
IAnimationService<?, ?> service = getService();
|
||||
if(service == null) return;
|
||||
AnimationData data = service.getAnimation(animationTag);
|
||||
if(data == null) return;
|
||||
if(target == sender) service.playAnimationWithRide(target, layer, data, force);
|
||||
else service.request(sender, target, layer, data, true);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable CompoundTag getAnimationTag() {
|
||||
return animationTag;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable ResourceLocation getAnimation() {
|
||||
return animation;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,86 +0,0 @@
|
|||
/*
|
||||
* Anim Core mod
|
||||
* Copyright (C) 2026 LeisureTimeDock
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package top.leisuretimedock.animationcore.animation.network.toserver;
|
||||
|
||||
import top.leisuretimedock.animationcore.animation.data.AnimationData;
|
||||
import top.leisuretimedock.animationcore.animation.network.ServiceGetterPacket;
|
||||
import top.leisuretimedock.animationcore.animation.service.IAnimationService;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.network.FriendlyByteBuf;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraftforge.network.NetworkEvent;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.UUID;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public class RequestAnimationPacket extends ServiceGetterPacket {
|
||||
private final @Nullable CompoundTag animationTag;
|
||||
private final ResourceLocation layer;
|
||||
private final ResourceLocation animation;
|
||||
private final UUID targetUUID;
|
||||
|
||||
public RequestAnimationPacket(AnimationData data, ResourceLocation layer, UUID targetUUID) {
|
||||
this.animationTag = data.serializeNBT();
|
||||
this.animation = data.getKey();
|
||||
this.layer = layer;
|
||||
this.targetUUID = targetUUID;
|
||||
}
|
||||
|
||||
public RequestAnimationPacket(FriendlyByteBuf buf) {
|
||||
this.animationTag = buf.readNullable(FriendlyByteBuf::readAnySizeNbt);
|
||||
this.layer = buf.readResourceLocation();
|
||||
this.animation = buf.readResourceLocation();
|
||||
this.targetUUID = buf.readUUID();
|
||||
}
|
||||
|
||||
public void encode(FriendlyByteBuf buf) {
|
||||
buf.writeNullable(animationTag, FriendlyByteBuf::writeNbt);
|
||||
buf.writeResourceLocation(layer);
|
||||
buf.writeResourceLocation(animation);
|
||||
buf.writeUUID(targetUUID);
|
||||
}
|
||||
|
||||
public void handle(Supplier<NetworkEvent.Context> supplier) {
|
||||
NetworkEvent.Context context = supplier.get();
|
||||
context.enqueueWork(() -> {
|
||||
context.setPacketHandled(true);
|
||||
ServerPlayer target;
|
||||
ServerPlayer sender = context.getSender();
|
||||
if(sender == null || sender.getServer() == null) return;
|
||||
if(this.targetUUID == null) target = sender;
|
||||
else target = sender.getServer().getPlayerList().getPlayer(this.targetUUID);
|
||||
if(target == null) return;
|
||||
IAnimationService<?, ?> service = getService();
|
||||
if(service == null) return;
|
||||
AnimationData data = service.getAnimation(animationTag);
|
||||
if(data == null) return;
|
||||
if(target == sender) return;
|
||||
service.request(sender, target, layer, data, false);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable CompoundTag getAnimationTag() {
|
||||
return animationTag;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable ResourceLocation getAnimation() {
|
||||
return animation;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,45 +0,0 @@
|
|||
/*
|
||||
* Anim Core mod
|
||||
* Copyright (C) 2026 LeisureTimeDock
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package top.leisuretimedock.animationcore.animation.network.toserver;
|
||||
|
||||
import top.leisuretimedock.animationcore.animation.AnimationApi;
|
||||
import net.minecraft.network.FriendlyByteBuf;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraftforge.network.NetworkEvent;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public record StopAnimationPacket(ResourceLocation layer) {
|
||||
public StopAnimationPacket(FriendlyByteBuf buf) {
|
||||
this(buf.readResourceLocation());
|
||||
}
|
||||
|
||||
public void encode(FriendlyByteBuf buf) {
|
||||
buf.writeResourceLocation(layer);
|
||||
}
|
||||
|
||||
public void handle(Supplier<NetworkEvent.Context> supplier) {
|
||||
NetworkEvent.Context context = supplier.get();
|
||||
context.enqueueWork(() -> {
|
||||
context.setPacketHandled(true);
|
||||
ServerPlayer sender = context.getSender();
|
||||
if(sender == null) return;
|
||||
AnimationApi.getHelper(sender).removeAnimation(layer);
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,62 +0,0 @@
|
|||
/*
|
||||
* Anim Core mod
|
||||
* Copyright (C) 2026 LeisureTimeDock
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package top.leisuretimedock.animationcore.animation.register;
|
||||
|
||||
import top.leisuretimedock.animationcore.animation.capability.AnimationDataCapability;
|
||||
import top.leisuretimedock.animationcore.animation.capability.RawAnimationDataCapability;
|
||||
import top.leisuretimedock.animationcore.animation.capability.inter.IAnimationCapability;
|
||||
import top.leisuretimedock.animationcore.animation.network.toclient.AnimationCapabilityPacket;
|
||||
import top.leisuretimedock.animationcore.animation.network.toclient.RawAnimationCapabilityPacket;
|
||||
import top.leisuretimedock.animationcore.capability.CapabilityUtils;
|
||||
import top.leisuretimedock.animationcore.capability.data.player.PlayerCapabilityRegistry;
|
||||
import top.leisuretimedock.animationcore.capability.network.CapabilityChannel;
|
||||
import top.leisuretimedock.animationcore.core.ModChannel;
|
||||
import net.minecraftforge.common.capabilities.CapabilityManager;
|
||||
import net.minecraftforge.common.capabilities.CapabilityToken;
|
||||
|
||||
public class AnimationCapabilities {
|
||||
public static void registerAnimationCapability() {
|
||||
CapabilityChannel channel = CapabilityUtils.createChannel();
|
||||
CapabilityUtils.registerPlayerCapabilityWithNetwork(
|
||||
AnimationDataCapability.key,
|
||||
new PlayerCapabilityRegistry.CapabilityRecord<>(
|
||||
AnimationDataCapability.class,
|
||||
CapabilityManager.get(new CapabilityToken<>() {}),
|
||||
IAnimationCapability.class
|
||||
),
|
||||
channel,
|
||||
ModChannel.getAndAddCid(),
|
||||
AnimationCapabilityPacket.class,
|
||||
AnimationCapabilityPacket::new,
|
||||
AnimationCapabilityPacket::encode,
|
||||
AnimationCapabilityPacket::handle
|
||||
);
|
||||
CapabilityUtils.registerPlayerCapabilityWithNetwork(
|
||||
RawAnimationDataCapability.key,
|
||||
new PlayerCapabilityRegistry.CapabilityRecord<>(
|
||||
RawAnimationDataCapability.class,
|
||||
CapabilityManager.get(new CapabilityToken<>() {}),
|
||||
RawAnimationDataCapability.class
|
||||
),
|
||||
channel,
|
||||
ModChannel.getAndAddCid(),
|
||||
RawAnimationCapabilityPacket.class,
|
||||
RawAnimationCapabilityPacket::new,
|
||||
RawAnimationCapabilityPacket::encode,
|
||||
RawAnimationCapabilityPacket::handle
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,85 +0,0 @@
|
|||
/*
|
||||
* Anim Core mod
|
||||
* Copyright (C) 2026 LeisureTimeDock
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package top.leisuretimedock.animationcore.animation.register;
|
||||
|
||||
import top.leisuretimedock.animationcore.animation.network.toclient.AnimationClearPacket;
|
||||
import top.leisuretimedock.animationcore.animation.network.toclient.AnimationClientStatusPacket;
|
||||
import top.leisuretimedock.animationcore.animation.network.toclient.AnimationJsonPacket;
|
||||
import top.leisuretimedock.animationcore.animation.network.toclient.SyncAnimationPacket;
|
||||
import top.leisuretimedock.animationcore.animation.network.toserver.*;
|
||||
import top.leisuretimedock.animationcore.core.ModChannel;
|
||||
import net.minecraftforge.network.NetworkDirection;
|
||||
|
||||
public class AnimationChannels {
|
||||
public static void registerChannel() {
|
||||
ModChannel.INSTANCE.messageBuilder(SyncAnimationPacket.class, cid(), NetworkDirection.PLAY_TO_CLIENT)
|
||||
.decoder(SyncAnimationPacket::new)
|
||||
.encoder(SyncAnimationPacket::encode)
|
||||
.consumerMainThread(SyncAnimationPacket::handle)
|
||||
.add();
|
||||
ModChannel.INSTANCE.messageBuilder(AnimationJsonPacket.class, cid(), NetworkDirection.PLAY_TO_CLIENT)
|
||||
.decoder(AnimationJsonPacket::new)
|
||||
.encoder(AnimationJsonPacket::encode)
|
||||
.consumerMainThread(AnimationJsonPacket::handle)
|
||||
.add();
|
||||
ModChannel.INSTANCE.messageBuilder(AnimationClientStatusPacket.class, cid(), NetworkDirection.PLAY_TO_CLIENT)
|
||||
.decoder(AnimationClientStatusPacket::new)
|
||||
.encoder(AnimationClientStatusPacket::encode)
|
||||
.consumerMainThread(AnimationClientStatusPacket::handle)
|
||||
.add();
|
||||
ModChannel.INSTANCE.messageBuilder(AnimationClearPacket.class, cid(), NetworkDirection.PLAY_TO_CLIENT)
|
||||
.decoder(AnimationClearPacket::new)
|
||||
.encoder(AnimationClearPacket::encode)
|
||||
.consumerMainThread(AnimationClearPacket::handle)
|
||||
.add();
|
||||
|
||||
//To server
|
||||
ModChannel.INSTANCE.messageBuilder(PlayAnimationPacket.class, cid(), NetworkDirection.PLAY_TO_SERVER)
|
||||
.decoder(PlayAnimationPacket::new)
|
||||
.encoder(PlayAnimationPacket::encode)
|
||||
.consumerMainThread(PlayAnimationPacket::handle)
|
||||
.add();
|
||||
ModChannel.INSTANCE.messageBuilder(PlayAnimationRidePacket.class, cid(), NetworkDirection.PLAY_TO_SERVER)
|
||||
.decoder(PlayAnimationRidePacket::new)
|
||||
.encoder(PlayAnimationRidePacket::encode)
|
||||
.consumerMainThread(PlayAnimationRidePacket::handle)
|
||||
.add();
|
||||
ModChannel.INSTANCE.messageBuilder(ApplyAnimationPacket.class, cid(), NetworkDirection.PLAY_TO_SERVER)
|
||||
.decoder(ApplyAnimationPacket::new)
|
||||
.encoder(ApplyAnimationPacket::encode)
|
||||
.consumerMainThread(ApplyAnimationPacket::handle)
|
||||
.add();
|
||||
ModChannel.INSTANCE.messageBuilder(InviteAnimationPacket.class, cid(), NetworkDirection.PLAY_TO_SERVER)
|
||||
.decoder(InviteAnimationPacket::new)
|
||||
.encoder(InviteAnimationPacket::encode)
|
||||
.consumerMainThread(InviteAnimationPacket::handle)
|
||||
.add();
|
||||
ModChannel.INSTANCE.messageBuilder(RequestAnimationPacket.class, cid(), NetworkDirection.PLAY_TO_SERVER)
|
||||
.decoder(RequestAnimationPacket::new)
|
||||
.encoder(RequestAnimationPacket::encode)
|
||||
.consumerMainThread(RequestAnimationPacket::handle)
|
||||
.add();
|
||||
ModChannel.INSTANCE.messageBuilder(StopAnimationPacket.class, cid(), NetworkDirection.PLAY_TO_SERVER)
|
||||
.decoder(StopAnimationPacket::new)
|
||||
.encoder(StopAnimationPacket::encode)
|
||||
.consumerMainThread(StopAnimationPacket::handle)
|
||||
.add();
|
||||
}
|
||||
|
||||
private static int cid() {
|
||||
return ModChannel.getAndAddCid();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,70 +0,0 @@
|
|||
/*
|
||||
* Anim Core mod
|
||||
* Copyright (C) 2026 LeisureTimeDock
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package top.leisuretimedock.animationcore.animation.register;
|
||||
|
||||
import top.leisuretimedock.animationcore.animation.command.*;
|
||||
import top.leisuretimedock.animationcore.animation.command.argument.AnimationArgument;
|
||||
import top.leisuretimedock.animationcore.animation.command.argument.AnimationLayerArgument;
|
||||
import top.leisuretimedock.animationcore.animation.command.client.ListClientCommand;
|
||||
import top.leisuretimedock.animationcore.animation.command.client.RefreshCommand;
|
||||
import top.leisuretimedock.animationcore.animation.service.IAnimationService;
|
||||
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
|
||||
import net.minecraft.commands.CommandSourceStack;
|
||||
import net.minecraft.commands.synchronization.ArgumentTypeInfo;
|
||||
import net.minecraft.commands.synchronization.ArgumentTypeInfos;
|
||||
import net.minecraft.commands.synchronization.SingletonArgumentInfo;
|
||||
import net.minecraftforge.registries.DeferredRegister;
|
||||
|
||||
import static net.minecraft.commands.Commands.literal;
|
||||
|
||||
public class AnimationCommands {
|
||||
public static void commonCommandRegister(LiteralArgumentBuilder<CommandSourceStack> builder) {
|
||||
if(IAnimationService.ANIMATION_RUNNER.testCondition()){
|
||||
LiteralArgumentBuilder<CommandSourceStack> anim = literal("anim");
|
||||
ApplyCommand.register(anim);
|
||||
InviteCommand.register(anim);
|
||||
JsonCommand.register(anim);
|
||||
ListServerCommand.register(anim);
|
||||
PlayCommand.register(anim);
|
||||
RequestCommand.register(anim);
|
||||
builder.then(anim);
|
||||
}
|
||||
}
|
||||
|
||||
public static void clientCommandRegister(LiteralArgumentBuilder<CommandSourceStack> builder) {
|
||||
if(IAnimationService.ANIMATION_RUNNER.testCondition()) {
|
||||
LiteralArgumentBuilder<CommandSourceStack> anim = literal("anim");
|
||||
ListClientCommand.register(anim);
|
||||
RefreshCommand.register(anim);
|
||||
builder.then(anim);
|
||||
}
|
||||
}
|
||||
|
||||
public static void registerArguments(DeferredRegister<ArgumentTypeInfo<?, ?>> register) {
|
||||
register.register("animations",
|
||||
() -> ArgumentTypeInfos.registerByClass(
|
||||
AnimationArgument.class,
|
||||
SingletonArgumentInfo.contextFree(AnimationArgument::animation)
|
||||
)
|
||||
);
|
||||
register.register("layers",
|
||||
() -> ArgumentTypeInfos.registerByClass(
|
||||
AnimationLayerArgument.class,
|
||||
SingletonArgumentInfo.contextFree(AnimationLayerArgument::layer)
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,48 +0,0 @@
|
|||
/*
|
||||
* Anim Core mod
|
||||
* Copyright (C) 2026 LeisureTimeDock
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package top.leisuretimedock.animationcore.animation.register;
|
||||
|
||||
import top.leisuretimedock.animationcore.AnimationCore;
|
||||
import top.leisuretimedock.animationcore.animation.entity.AnimationRideEntity;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.entity.EntityType;
|
||||
import net.minecraft.world.entity.MobCategory;
|
||||
import net.minecraftforge.eventbus.api.IEventBus;
|
||||
import net.minecraftforge.registries.DeferredRegister;
|
||||
import net.minecraftforge.registries.ForgeRegistries;
|
||||
import net.minecraftforge.registries.RegistryObject;
|
||||
|
||||
|
||||
|
||||
public class AnimationEntities {
|
||||
public static final DeferredRegister<EntityType<?>> REGISTER = DeferredRegister.create(ForgeRegistries.ENTITY_TYPES, AnimationCore.MOD_ID);
|
||||
|
||||
public static final RegistryObject<EntityType<AnimationRideEntity>> RIDE = register(
|
||||
"animation_ride_entity", EntityType.Builder.<AnimationRideEntity>of((type, world) -> new AnimationRideEntity(world), MobCategory.MISC)
|
||||
.sized(0.0F, 0.0F)
|
||||
.setCustomClientFactory((spawnEntity, world) ->
|
||||
new AnimationRideEntity(world)
|
||||
)
|
||||
);
|
||||
|
||||
private static <T extends Entity> RegistryObject<EntityType<T>> register(String name, EntityType.Builder<T> builder) {
|
||||
return REGISTER.register(name, () -> builder.build(name));
|
||||
}
|
||||
|
||||
public static void register(IEventBus modBus){
|
||||
REGISTER.register(modBus);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,611 +0,0 @@
|
|||
/*
|
||||
* Anim Core mod
|
||||
* Copyright (C) 2026 LeisureTimeDock
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package top.leisuretimedock.animationcore.animation.register;
|
||||
|
||||
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 top.leisuretimedock.animationcore.AnimationCore;
|
||||
import top.leisuretimedock.animationcore.animation.capability.AnimationDataCapability;
|
||||
import top.leisuretimedock.animationcore.animation.capability.RawAnimationDataCapability;
|
||||
import top.leisuretimedock.animationcore.animation.capability.inter.IAnimationCapability;
|
||||
import top.leisuretimedock.animationcore.animation.data.GenericAnimationData;
|
||||
import top.leisuretimedock.animationcore.animation.data.RawAnimationData;
|
||||
import top.leisuretimedock.animationcore.animation.data.util.AnimJson;
|
||||
import top.leisuretimedock.animationcore.animation.data.util.AnimLayerJson;
|
||||
import top.leisuretimedock.animationcore.animation.event.create.AnimationRegisterEvent;
|
||||
import top.leisuretimedock.animationcore.animation.mixin.IMixinPlayerAnimationFactoryHolder;
|
||||
import top.leisuretimedock.animationcore.animation.network.toclient.AnimationClientStatusPacket;
|
||||
import top.leisuretimedock.animationcore.animation.network.toclient.AnimationJsonPacket;
|
||||
import top.leisuretimedock.animationcore.animation.service.RawAnimationService;
|
||||
import top.leisuretimedock.animationcore.animation.utils.FileUtils;
|
||||
import top.leisuretimedock.animationcore.core.ModChannel;
|
||||
import dev.kosmx.playerAnim.api.layered.AnimationStack;
|
||||
import dev.kosmx.playerAnim.api.layered.IAnimation;
|
||||
import dev.kosmx.playerAnim.api.layered.KeyframeAnimationPlayer;
|
||||
import dev.kosmx.playerAnim.api.layered.ModifierLayer;
|
||||
import dev.kosmx.playerAnim.api.layered.modifier.AbstractFadeModifier;
|
||||
import dev.kosmx.playerAnim.core.data.KeyframeAnimation;
|
||||
import dev.kosmx.playerAnim.core.util.Ease;
|
||||
import dev.kosmx.playerAnim.core.util.Pair;
|
||||
import dev.kosmx.playerAnim.impl.animation.AnimationApplier;
|
||||
import dev.kosmx.playerAnim.minecraftApi.PlayerAnimationAccess;
|
||||
import dev.kosmx.playerAnim.minecraftApi.PlayerAnimationFactory;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.multiplayer.ClientLevel;
|
||||
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;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.*;
|
||||
|
||||
public class AnimationRegistry {
|
||||
private static final Map<ResourceLocation, GenericAnimationData> animations = new HashMap<>();
|
||||
private static final Map<ResourceLocation, Integer> layers = new HashMap<>();
|
||||
|
||||
public static Map<ResourceLocation, GenericAnimationData> getAnimations() {
|
||||
return Map.copyOf(animations);
|
||||
}
|
||||
|
||||
public static Map<ResourceLocation, Integer> getLayers() {
|
||||
return Map.copyOf(layers);
|
||||
}
|
||||
|
||||
@OnlyIn(Dist.CLIENT)
|
||||
public static void registerAnimations(Map<ResourceLocation, GenericAnimationData> animationMap) {
|
||||
animations.clear();
|
||||
animations.putAll(animationMap);
|
||||
}
|
||||
|
||||
@OnlyIn(Dist.CLIENT)
|
||||
public static void registerLayers(Map<ResourceLocation, Integer> layerMap) {
|
||||
layers.clear();
|
||||
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) {
|
||||
// Load legacy animations from datapacks
|
||||
loadLegacyDataPackAnimations(event.getServer());
|
||||
|
||||
// Load animations from registration events
|
||||
loadFromRegistrationEvents();
|
||||
|
||||
AnimationCore.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) {
|
||||
AnimationCore.log.error("Failed to create legacy animation directory", e);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
AnimationCore.log.info("Loaded legacy animation: {} -> {}",
|
||||
anim.getKey(), anim.getName());
|
||||
} catch (Exception e) {
|
||||
AnimationCore.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);
|
||||
AnimationCore.log.info("Loaded {} legacy layer configurations from {}",
|
||||
parse.size(), path);
|
||||
} catch (Exception e) {
|
||||
AnimationCore.log.error("Failed to parse legacy layer JSON: {}", path, e);
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
AnimationCore.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);
|
||||
|
||||
AnimationRegisterEvent.Layer layerRegisterEvent = new AnimationRegisterEvent.Layer();
|
||||
MinecraftForge.EVENT_BUS.post(layerRegisterEvent);
|
||||
Map<ResourceLocation, Integer> layerMap = layerRegisterEvent.getLayers();
|
||||
layers.putAll(layerMap);
|
||||
|
||||
AnimationCore.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(), "ac_animations");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void apply(@Nonnull Map<ResourceLocation, JsonElement> resources,
|
||||
@Nonnull ResourceManager resourceManager,
|
||||
@Nonnull ProfilerFiller profiler) {
|
||||
AnimationCore.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, "ac_animations",
|
||||
(resourceLocation, inputStreamIoSupplier) -> {
|
||||
String path = resourceLocation.getPath();
|
||||
if (path.endsWith(".anim.json")) {
|
||||
ResourceLocation rl = new ResourceLocation(namespace,
|
||||
path.substring("ac_animations/".length(), path.length() - ".json".length()));
|
||||
|
||||
JsonElement el = resources.get(rl);
|
||||
if (el != null) {
|
||||
rl = new ResourceLocation(namespace,
|
||||
path.substring("ac_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)) {
|
||||
AnimationCore.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++;
|
||||
AnimationCore.log.debug("Loaded animation: {} -> {}", animKey, anim.getName());
|
||||
|
||||
} catch (IllegalArgumentException | JsonParseException e) {
|
||||
AnimationCore.log.error("Parsing error loading animation {}", animKey, e);
|
||||
}
|
||||
}
|
||||
|
||||
AnimationCore.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(), "ac_animations");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void apply(@Nonnull Map<ResourceLocation, JsonElement> resources,
|
||||
@Nonnull ResourceManager resourceManager,
|
||||
@Nonnull ProfilerFiller profiler) {
|
||||
AnimationCore.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, "ac_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)) {
|
||||
AnimationCore.log.debug("Overriding layer {} with priority {}", layerKey, priority);
|
||||
}
|
||||
layers.put(layerKey, priority);
|
||||
});
|
||||
|
||||
loadedCount += parsedLayers.size();
|
||||
AnimationCore.log.debug("Loaded {} layer configurations from {}",
|
||||
parsedLayers.size(), entry.getKey().getNamespace());
|
||||
|
||||
} catch (IllegalArgumentException | JsonParseException e) {
|
||||
AnimationCore.log.error("Parsing error loading layer configuration", e);
|
||||
}
|
||||
}
|
||||
|
||||
AnimationCore.log.info("Loaded {} layer configurations from data packs", loadedCount);
|
||||
}
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public static void onPlayerLoggedIn(PlayerEvent.PlayerLoggedInEvent event) {
|
||||
if (event.getEntity() instanceof ServerPlayer serverPlayer) {
|
||||
MinecraftServer server = serverPlayer.getServer();
|
||||
if(server == null) return;
|
||||
|
||||
// Send animations to client
|
||||
sendAnimationsToClient(serverPlayer);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Send all animations and layers to the client
|
||||
*/
|
||||
private static void sendAnimationsToClient(ServerPlayer player) {
|
||||
AnimationCore.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);
|
||||
|
||||
AnimationCore.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 {
|
||||
public static boolean isAnimationRegistered = false;
|
||||
private static final Map<ResourceLocation, GenericAnimationData> animationsCache = new HashMap<>();
|
||||
private static final Map<ResourceLocation, Integer> layersCache = new HashMap<>();
|
||||
private static final Map<UUID, Map<ResourceLocation, IAnimation>> modifierLayers = new HashMap<>();
|
||||
|
||||
public static void cacheAddAnimation(ResourceLocation location, GenericAnimationData animation) {
|
||||
animationsCache.put(location, animation);
|
||||
}
|
||||
|
||||
public static void cacheAddAnimationLayer(ResourceLocation location, Integer priority) {
|
||||
layersCache.put(location, priority);
|
||||
}
|
||||
|
||||
public static void animationStatusUpdate(AnimationClientStatusPacket.Status status) {
|
||||
switch (status) {
|
||||
case ANIM_CACHE_CLEAR -> animationsCache.clear();
|
||||
case LAYER_CACHE_CLEAR -> {
|
||||
((IMixinPlayerAnimationFactoryHolder)(PlayerAnimationFactory.ANIMATION_DATA_FACTORY))
|
||||
.animcore$clearAnimations();
|
||||
layersCache.clear();
|
||||
}
|
||||
case ANIM_REGISTER -> {
|
||||
isAnimationRegistered = true;
|
||||
registerAnimations(animationsCache);
|
||||
RawAnimationRegistry.triggerRegistry();
|
||||
}
|
||||
case LAYER_REGISTER -> {
|
||||
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);
|
||||
}
|
||||
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) {
|
||||
AnimationCore.log.error("Level is null, cannot update animation layers");
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
for (AbstractClientPlayer player : level.players()) {
|
||||
updatePlayerAnimationStack(player);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
AnimationCore.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) {
|
||||
AnimationCore.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) {
|
||||
AnimationCore.log.error("Failed to restore playing animations for player: {}", player, e);
|
||||
}
|
||||
}
|
||||
|
||||
private static IAnimation registerPlayerAnimation(AbstractClientPlayer player) {
|
||||
return new ModifierLayer<>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,117 +0,0 @@
|
|||
/*
|
||||
* Anim Core mod
|
||||
* Copyright (C) 2026 LeisureTimeDock
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package top.leisuretimedock.animationcore.animation.register;
|
||||
|
||||
import top.leisuretimedock.animationcore.AnimationCore;
|
||||
import top.leisuretimedock.animationcore.animation.command.argument.AnimationArgument;
|
||||
import top.leisuretimedock.animationcore.animation.data.RawAnimationData;
|
||||
import top.leisuretimedock.animationcore.animation.data.util.RawAnimJson;
|
||||
import top.leisuretimedock.animationcore.animation.event.create.AnimationRegisterEvent;
|
||||
import top.leisuretimedock.animationcore.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 {
|
||||
private static final Map<ResourceLocation, RawAnimationData> animations = new HashMap<>();
|
||||
|
||||
public static Map<ResourceLocation, RawAnimationData> getAnimations() {
|
||||
return Map.copyOf(animations);
|
||||
}
|
||||
|
||||
private static void registerAnimations(Map<ResourceLocation, RawAnimationData> animationMap) {
|
||||
animations.clear();
|
||||
Map.copyOf(animationMap).keySet().forEach(location -> {
|
||||
if(!validateLocation(location)) animations.remove(location);
|
||||
});
|
||||
animations.putAll(animationMap);
|
||||
AnimationArgument.resetAnimationNames();
|
||||
}
|
||||
|
||||
public static boolean register(ResourceLocation location, RawAnimationData rawAnimation) {
|
||||
if (validateLocation(location)) {
|
||||
animations.put(location, rawAnimation);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static void triggerRegistry() {
|
||||
resetAnimations();
|
||||
AnimationRegisterEvent.RawAnimation event = new AnimationRegisterEvent.RawAnimation();
|
||||
MinecraftForge.EVENT_BUS.post(event);
|
||||
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) {
|
||||
AnimationCore.log.error("Failed to parse raw animation JSON: {}", path.toString());
|
||||
}
|
||||
}
|
||||
registerAnimations(animationDataMap);
|
||||
}
|
||||
|
||||
private static void resetAnimations() {
|
||||
animations.clear();
|
||||
}
|
||||
|
||||
public static boolean validateLocation(ResourceLocation location) {
|
||||
try {
|
||||
if(!AnimationRegistry.ClientCache.isAnimationRegistered)
|
||||
throw new RuntimeException("Server animation is not registered!");
|
||||
if(AnimationRegistry.getAnimations().containsKey(location))
|
||||
throw new RuntimeException("Duplicated animation on server: " + location);
|
||||
return true;
|
||||
} catch (RuntimeException e) {
|
||||
AnimationCore.log.error(e.getMessage(), e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,157 +0,0 @@
|
|||
/*
|
||||
* Anim Core mod
|
||||
* Copyright (C) 2026 LeisureTimeDock
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package top.leisuretimedock.animationcore.animation.service;
|
||||
|
||||
import top.leisuretimedock.animationcore.animation.capability.AnimationDataCapability;
|
||||
import top.leisuretimedock.animationcore.animation.capability.inter.IAnimationCapability;
|
||||
import top.leisuretimedock.animationcore.animation.data.AnimationData;
|
||||
import top.leisuretimedock.animationcore.animation.data.GenericAnimationData;
|
||||
import top.leisuretimedock.animationcore.animation.register.AnimationRegistry;
|
||||
import top.leisuretimedock.animationcore.animation.utils.AnimationUtils;
|
||||
import top.leisuretimedock.animationcore.animation.utils.ApiBack;
|
||||
import net.minecraft.client.player.AbstractClientPlayer;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraftforge.api.distmarker.Dist;
|
||||
import net.minecraftforge.api.distmarker.OnlyIn;
|
||||
import net.minecraftforge.common.util.FakePlayer;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Animation Util. May be you can call it Api.
|
||||
*/
|
||||
public class AnimationService implements IAnimationService<GenericAnimationData, IAnimationCapability> {
|
||||
public static final AnimationService INSTANCE = new AnimationService();
|
||||
|
||||
/**
|
||||
* Get the HeightModifier when there are animations which playing on player. <br>
|
||||
* And It will return the first which be found.
|
||||
* @param player Target player
|
||||
* @return The first HeightModifier it find.
|
||||
*/
|
||||
public float getHeightModifier(Player player) {
|
||||
Float result = ANIMATION_RUNNER.testLoadedAndCall(() -> {
|
||||
IAnimationCapability data = AnimationDataCapability.getCapability(player).orElse(null);
|
||||
if (data == null) return 1.0f;
|
||||
float heightModifier = 1.0f;
|
||||
for (ResourceLocation value : data.getAnimations().values()) {
|
||||
GenericAnimationData animation = getAnimation(value);
|
||||
if (animation == null) continue;
|
||||
float animationHeightModifier = animation.getHeightModifier();
|
||||
heightModifier = Math.min(heightModifier, animationHeightModifier);
|
||||
}
|
||||
return heightModifier;
|
||||
});
|
||||
return result == null ? 1.0f : result;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public @Nullable GenericAnimationData getAnimation(ResourceLocation location) {
|
||||
return AnimationRegistry.getAnimations().getOrDefault(location, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable GenericAnimationData getAnimation(CompoundTag tag) {
|
||||
return new GenericAnimationData(){{deserializeNBT(tag);}};
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable IAnimationCapability getCapability(Player player) {
|
||||
return AnimationDataCapability.getCapability(player).orElse(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearAnimations(ServerPlayer serverPlayer) {
|
||||
ANIMATION_RUNNER.testLoadedAndRun(() -> {
|
||||
Optional.ofNullable(getCapability(serverPlayer)).ifPresent(IAnimationCapability::clearAnimations);
|
||||
detachAnimation(serverPlayer);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAnimationPresent(ResourceLocation location) {
|
||||
return ANIMATION_RUNNER.testLoadedAndCall(() -> AnimationRegistry.getAnimations().containsKey(location));
|
||||
}
|
||||
|
||||
@Override
|
||||
@OnlyIn(Dist.CLIENT)
|
||||
public void refreshAnimation(AbstractClientPlayer clientPlayer) {
|
||||
ANIMATION_RUNNER.testLoadedAndRun(() -> {
|
||||
IAnimationCapability data = getCapability(clientPlayer);
|
||||
if(data == null) return;
|
||||
Set<ResourceLocation> oldLayers = new HashSet<>(data.getAnimations().keySet());
|
||||
for (ResourceLocation layer : Set.copyOf(oldLayers)) {
|
||||
if (AnimationUtils.isClientAnimationStop(clientPlayer, layer)) {
|
||||
removeAnimation(clientPlayer, layer);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable ResourceLocation getAnimationPlaying(Player player, @Nullable ResourceLocation layer) {
|
||||
return ANIMATION_RUNNER.testLoadedAndCall(() -> {
|
||||
IAnimationCapability data = getCapability(player);
|
||||
if(data == null) return null;
|
||||
if(layer == null){
|
||||
for (ResourceLocation value : data.getAnimations().values()) {
|
||||
if(value != null) return value;
|
||||
}
|
||||
} else if (isAnimationLayerPresent(layer)) {
|
||||
if(data.isAnimationPresent(layer)){
|
||||
return data.getAnimation(layer);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public ApiBack removeAnimation(@NotNull ServerPlayer serverPlayer, ResourceLocation layer) {
|
||||
boolean result = ANIMATION_RUNNER.testLoadedAndCall(() -> Optional.ofNullable(getCapability(serverPlayer))
|
||||
.map(data -> data.removeAnimation(layer)).orElse(false));
|
||||
return result ? ApiBack.SUCCESS : ApiBack.FAIL;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ApiBack playAnimationServer(@NotNull ServerPlayer player, ResourceLocation layer, AnimationData animation) {
|
||||
return ANIMATION_RUNNER.testLoadedAndCall(() -> {
|
||||
ResourceLocation key = animation.getKey();
|
||||
if(!isAnimationLayerPresent(layer) || !isAnimationPresent(key))
|
||||
return ApiBack.RESOURCE_NOT_FOUND;
|
||||
if(player instanceof FakePlayer)
|
||||
return ApiBack.UNSUPPORTED;
|
||||
Boolean flag = Optional.ofNullable(getCapability(player)).map(data ->
|
||||
data.mergeAnimation(layer, key)).orElse(false);
|
||||
return flag ? ApiBack.SUCCESS : ApiBack.FAIL;
|
||||
});
|
||||
}
|
||||
|
||||
public ApiBack playAnimation(@NotNull ServerPlayer player, ResourceLocation layer, ResourceLocation animation) {
|
||||
return playAnimation(player, layer, getAnimation(animation));
|
||||
}
|
||||
public ApiBack playAnimationWithRide(@NotNull ServerPlayer player, ResourceLocation layer, ResourceLocation animation, boolean force) {
|
||||
return playAnimationWithRide(player, layer, getAnimation(animation), force);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,705 +0,0 @@
|
|||
/*
|
||||
* Anim Core mod
|
||||
* Copyright (C) 2026 LeisureTimeDock
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package top.leisuretimedock.animationcore.animation.service;
|
||||
|
||||
import top.leisuretimedock.animationcore.animation.data.AnimationData;
|
||||
import top.leisuretimedock.animationcore.animation.entity.AnimationRideEntity;
|
||||
import top.leisuretimedock.animationcore.animation.event.PlayerTickEvent;
|
||||
import top.leisuretimedock.animationcore.animation.event.client.CameraModify;
|
||||
import top.leisuretimedock.animationcore.animation.event.client.ClientPlayerEvent;
|
||||
import top.leisuretimedock.animationcore.animation.event.client.EntityRendererRegisterEvent;
|
||||
import top.leisuretimedock.animationcore.animation.event.create.AnimationEvent;
|
||||
import top.leisuretimedock.animationcore.animation.network.toclient.SyncAnimationPacket;
|
||||
import top.leisuretimedock.animationcore.animation.network.toserver.*;
|
||||
import top.leisuretimedock.animationcore.animation.register.AnimationCapabilities;
|
||||
import top.leisuretimedock.animationcore.animation.register.AnimationChannels;
|
||||
import top.leisuretimedock.animationcore.animation.register.AnimationEntities;
|
||||
import top.leisuretimedock.animationcore.animation.register.AnimationRegistry;
|
||||
import top.leisuretimedock.animationcore.animation.utils.AnimationUtils;
|
||||
import top.leisuretimedock.animationcore.animation.utils.ApiBack;
|
||||
import top.leisuretimedock.animationcore.capability.data.ICapabilitySync;
|
||||
import top.leisuretimedock.animationcore.core.ModChannel;
|
||||
import top.leisuretimedock.animationcore.core.ModCompatRun;
|
||||
import top.leisuretimedock.animationcore.core.configs.ModConfigs;
|
||||
import dev.kosmx.playerAnim.core.data.KeyframeAnimation;
|
||||
import dev.kosmx.playerAnim.minecraftApi.PlayerAnimationRegistry;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.player.AbstractClientPlayer;
|
||||
import net.minecraft.client.player.LocalPlayer;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
import net.minecraftforge.api.distmarker.Dist;
|
||||
import net.minecraftforge.api.distmarker.OnlyIn;
|
||||
import net.minecraftforge.common.MinecraftForge;
|
||||
import net.minecraftforge.common.util.FakePlayer;
|
||||
import net.minecraftforge.eventbus.api.Event;
|
||||
import net.minecraftforge.eventbus.api.IEventBus;
|
||||
import net.minecraftforge.fml.LogicalSide;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* Animation helper interface
|
||||
* @param <D> Animation data
|
||||
* @param <C> Capability
|
||||
*/
|
||||
@SuppressWarnings({"UnusedReturnValue", "unused"})
|
||||
public interface IAnimationService<D extends AnimationData, C extends ICapabilitySync<?>>{
|
||||
String AnimModId = "playeranimator";
|
||||
/**
|
||||
* Lazy runner
|
||||
*/
|
||||
ModCompatRun ANIMATION_RUNNER = new ModCompatRun(AnimModId) {
|
||||
@Override
|
||||
public void addCommonListener(IEventBus forgeBus, IEventBus modBus) {
|
||||
AnimationEntities.register(modBus);
|
||||
forgeBus.register(AnimationRegistry.class);
|
||||
forgeBus.register(PlayerTickEvent.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addClientListener(IEventBus forgeBus, IEventBus modBus) {
|
||||
modBus.register(EntityRendererRegisterEvent.class);
|
||||
forgeBus.register(CameraModify.class);
|
||||
forgeBus.register(ClientPlayerEvent.class);
|
||||
}
|
||||
};
|
||||
|
||||
//Cache info record, not persistent
|
||||
record ApplyAnimationRecord(UUID target, int expireTick) {}
|
||||
record InviteAnimationRecord(ResourceLocation layer, AnimationData animation, int expireTick, List<UUID> targets) {}
|
||||
record RequestAnimationRecord(ResourceLocation layer, AnimationData animation, int expireTick, UUID target, boolean isRide) {}
|
||||
|
||||
//Apply & invite & request history record
|
||||
Map<UUID, ApplyAnimationRecord> applyMap = new ConcurrentHashMap<>();
|
||||
Map<UUID, InviteAnimationRecord> inviteMap = new ConcurrentHashMap<>();
|
||||
Map<UUID, RequestAnimationRecord> requestMap = new ConcurrentHashMap<>();
|
||||
|
||||
//Last apply & invite & request tick map
|
||||
Map<UUID, Integer> lastApplyTickMap = new ConcurrentHashMap<>();
|
||||
Map<UUID, Integer> lastInviteTickMap = new ConcurrentHashMap<>();
|
||||
Map<UUID, Integer> lastRequestTickMap = new ConcurrentHashMap<>();
|
||||
|
||||
/**
|
||||
* Equal to {@link AnimationRegistry#getLayers()}
|
||||
* @return Resource location set
|
||||
*/
|
||||
default Set<ResourceLocation> getLayers() {
|
||||
return Set.copyOf(AnimationRegistry.getLayers().keySet());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get animation by location
|
||||
* @param location location
|
||||
* @return Animation data
|
||||
*/
|
||||
@Nullable
|
||||
D getAnimation(ResourceLocation location);
|
||||
|
||||
/**
|
||||
* Get animation by Tag (deserializeNBT)
|
||||
* @param tag tag
|
||||
* @return Animation data
|
||||
*/
|
||||
@Nullable
|
||||
D getAnimation(CompoundTag tag);
|
||||
|
||||
/**
|
||||
* Get capability
|
||||
* @param player player
|
||||
* @return Capability
|
||||
*/
|
||||
@Nullable
|
||||
C getCapability(Player player);
|
||||
|
||||
/**
|
||||
* To register handler
|
||||
* @param forgeBus Forge event bus
|
||||
* @param modBus Mod event bus
|
||||
*/
|
||||
static void register(IEventBus forgeBus, IEventBus modBus){
|
||||
ANIMATION_RUNNER.testLoadedAndRun(() -> {
|
||||
AnimationCapabilities.registerAnimationCapability();
|
||||
AnimationChannels.registerChannel();
|
||||
});
|
||||
ANIMATION_RUNNER.testLoadedAndAddListener(forgeBus, modBus);
|
||||
}
|
||||
|
||||
default void clearAnimations(AbstractClientPlayer player){
|
||||
for (ResourceLocation layer : getLayers()) {
|
||||
AnimationUtils.removeAnimation(player, layer);
|
||||
}
|
||||
}
|
||||
|
||||
//clear animations
|
||||
void clearAnimations(ServerPlayer serverPlayer);
|
||||
//according to location, judge if animation is present
|
||||
boolean isAnimationPresent(ResourceLocation location);
|
||||
//stop riding
|
||||
default ApiBack detachAnimation(ServerPlayer player){
|
||||
return ANIMATION_RUNNER.testLoadedAndCall(() -> {
|
||||
if(player.getVehicle() instanceof AnimationRideEntity) {
|
||||
player.stopRiding();
|
||||
return ApiBack.SUCCESS;
|
||||
}
|
||||
return ApiBack.UNSUPPORTED;
|
||||
});
|
||||
};
|
||||
//start ride
|
||||
default ApiBack joinAnimationServer(ServerPlayer player, ServerPlayer target, boolean force){
|
||||
return ANIMATION_RUNNER.testLoadedAndCall(() -> {
|
||||
Entity vehicle = target.getVehicle();
|
||||
if(vehicle instanceof AnimationRideEntity) {
|
||||
boolean result = player.startRiding(vehicle, force);
|
||||
return result ? ApiBack.SUCCESS : ApiBack.FAIL;
|
||||
}
|
||||
return ApiBack.UNSUPPORTED;
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Trigger event and let implementation class handle
|
||||
* @param player player
|
||||
* @param target target
|
||||
* @param force is force
|
||||
* @return Api back
|
||||
*/
|
||||
default ApiBack joinAnimation(ServerPlayer player, ServerPlayer target, boolean force){
|
||||
return ANIMATION_RUNNER.testLoadedAndCall(() -> {
|
||||
AnimationEvent.Join playEvent = new AnimationEvent.Join(player, target, force);
|
||||
boolean post = MinecraftForge.EVENT_BUS.post(playEvent);
|
||||
Event.Result eventResult = playEvent.getResult();
|
||||
if(post || eventResult == Event.Result.DENY) return ApiBack.BE_CANCELLED;
|
||||
return joinAnimationServer(player, target, playEvent.isForce());
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Sync animation tick to client
|
||||
* @param player Player
|
||||
* @param target Target player
|
||||
*/
|
||||
default void syncAnimation(ServerPlayer player, ServerPlayer target) {
|
||||
ANIMATION_RUNNER.testLoadedAndRun(() -> {
|
||||
ModChannel.sendToPlayer(new SyncAnimationPacket(player.getUUID(), target.getUUID()), player);
|
||||
ModChannel.sendToPlayer(new SyncAnimationPacket(player.getUUID(), target.getUUID()), target);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Sync animation tick on client
|
||||
* @param player Player
|
||||
* @param target Target player
|
||||
*/
|
||||
@OnlyIn(Dist.CLIENT)
|
||||
default void syncAnimation(AbstractClientPlayer player, AbstractClientPlayer target) {
|
||||
ANIMATION_RUNNER.testLoadedAndRun(() -> AnimationUtils.syncAnimation(player, target));
|
||||
}
|
||||
|
||||
/**
|
||||
* Refresh animation throw capability
|
||||
* @param clientPlayer player
|
||||
*/
|
||||
@OnlyIn(Dist.CLIENT)
|
||||
void refreshAnimation(AbstractClientPlayer clientPlayer);
|
||||
|
||||
/**
|
||||
* Refresh animation on client, it will not sync to capability
|
||||
* @param clientPlayer player
|
||||
*/
|
||||
@OnlyIn(Dist.CLIENT)
|
||||
default void refreshAnimationUnsafe(AbstractClientPlayer clientPlayer) {
|
||||
ANIMATION_RUNNER.testLoadedAndRun(() -> {
|
||||
Set<ResourceLocation> oldLayers = new HashSet<>(AnimationRegistry.getLayers().keySet());
|
||||
for (ResourceLocation layer : Set.copyOf(oldLayers)) {
|
||||
if (AnimationUtils.isClientAnimationStop(clientPlayer, layer)) {
|
||||
removeAnimation(clientPlayer, layer);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Test if layer exist and has been invite.
|
||||
* @param layer Target layer
|
||||
* @return If layer exist and has been invited
|
||||
*/
|
||||
default boolean isAnimationLayerPresent(ResourceLocation layer) {
|
||||
return ANIMATION_RUNNER.testLoadedAndCall(() -> getLayers().contains(layer));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get animation which is playing now on player. <br>
|
||||
* If layer is null, it will return the first playing animation which can be found.
|
||||
* @param player Target player
|
||||
* @param layer Target layer
|
||||
* @return Playing animation resource location
|
||||
*/
|
||||
@Nullable
|
||||
ResourceLocation getAnimationPlaying(Player player, @Nullable ResourceLocation layer);
|
||||
|
||||
/**
|
||||
* Remove animation.
|
||||
* @param player Target player
|
||||
* @param layer Target layer
|
||||
* @return If success
|
||||
*/
|
||||
@OnlyIn(Dist.CLIENT)
|
||||
default ApiBack removeAnimation(@Nullable AbstractClientPlayer player, ResourceLocation layer) {
|
||||
return ANIMATION_RUNNER.testLoadedAndCall(() -> {
|
||||
AnimationUtils.removeAnimation(player, layer);
|
||||
LocalPlayer self = Minecraft.getInstance().player;
|
||||
if(self != null && player != null && Objects.equals(self.getUUID(), player.getUUID())) {
|
||||
ModChannel.INSTANCE.sendToServer(new StopAnimationPacket(layer));
|
||||
}
|
||||
return ApiBack.SUCCESS;
|
||||
});
|
||||
}
|
||||
ApiBack removeAnimation(@NotNull ServerPlayer serverPlayer, ResourceLocation layer);
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* Play animation with ride. Player will ride an entity, then play animation.
|
||||
* When player unride, animation will be remove.
|
||||
* If player is riding and the "force" is false, it will return false
|
||||
* </pre>
|
||||
* @param player Target player
|
||||
* @param layer Target layer
|
||||
* @param animation Animation
|
||||
* @param force If force to ride and play animation
|
||||
* @return If success
|
||||
*/
|
||||
@OnlyIn(Dist.CLIENT)
|
||||
default ApiBack playAnimationWithRide(@Nullable AbstractClientPlayer player, ResourceLocation layer, ResourceLocation animation, boolean force) {
|
||||
return ANIMATION_RUNNER.testLoadedAndCall(() -> {
|
||||
if(isAnimationLayerPresent(layer) && isAnimationPresent(animation))
|
||||
return ApiBack.RESOURCE_NOT_FOUND;
|
||||
UUID uuid = player == null ? null : player.getUUID();
|
||||
AnimationData anim = getAnimation(animation);
|
||||
if(anim == null || anim.getRide() == null) return ApiBack.RESOURCE_NOT_FOUND;
|
||||
ModChannel.sendToServer(new PlayAnimationRidePacket(anim, layer, animation, uuid, force));
|
||||
return ApiBack.SUCCESS;
|
||||
});
|
||||
}
|
||||
default ApiBack playAnimationWithRide(@NotNull ServerPlayer player, ResourceLocation layer, AnimationData animation, boolean force, Vec3 pos) {
|
||||
return ANIMATION_RUNNER.testLoadedAndCall(() -> {
|
||||
ResourceLocation key = animation.getKey();
|
||||
if(!isAnimationLayerPresent(layer) || !isAnimationPresent(key))
|
||||
return ApiBack.RESOURCE_NOT_FOUND;
|
||||
if(animation.getRide() == null)
|
||||
return ApiBack.RESOURCE_NOT_FOUND;
|
||||
if(player instanceof FakePlayer)
|
||||
return ApiBack.UNSUPPORTED;
|
||||
boolean flag = player.getVehicle() != null;
|
||||
if(flag && force) player.stopRiding();
|
||||
else if(flag) return ApiBack.UNSUPPORTED;
|
||||
boolean result = AnimationRideEntity.create(player, layer, animation, force, pos) != null;
|
||||
return result ? ApiBack.SUCCESS : ApiBack.FAIL;
|
||||
});
|
||||
}
|
||||
default ApiBack playAnimationWithRide(@NotNull ServerPlayer player, ResourceLocation layer, AnimationData animation, boolean force) {
|
||||
return playAnimationWithRide(player, layer, animation, force, player.position());
|
||||
}
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* Play animation.
|
||||
* If run in Dist.CLIENT, player can be null, it will play animation only client.
|
||||
* If animation be null, it will remove animation on layer.
|
||||
* </pre>
|
||||
* @param player Target player
|
||||
* @param layer Target layer
|
||||
* @param animation Animation
|
||||
* @return If success
|
||||
*/
|
||||
@OnlyIn(Dist.CLIENT)
|
||||
default ApiBack playAnimation(@Nullable AbstractClientPlayer player, ResourceLocation layer, ResourceLocation animation) {
|
||||
return ANIMATION_RUNNER.testLoadedAndCall(() -> {
|
||||
if(isAnimationLayerPresent(layer) && isAnimationPresent(animation))
|
||||
return ApiBack.RESOURCE_NOT_FOUND;
|
||||
UUID uuid = player == null ? null : player.getUUID();
|
||||
AnimationData anim = getAnimation(animation);
|
||||
if(anim == null) return ApiBack.RESOURCE_NOT_FOUND;
|
||||
|
||||
AnimationEvent.Play playEvent = new AnimationEvent.Play(
|
||||
LogicalSide.CLIENT,
|
||||
player,
|
||||
layer,
|
||||
anim
|
||||
);
|
||||
boolean post = MinecraftForge.EVENT_BUS.post(playEvent);
|
||||
Event.Result eventResult = playEvent.getResult();
|
||||
if(post || eventResult == Event.Result.DENY) return ApiBack.BE_CANCELLED;
|
||||
ModChannel.sendToServer(new PlayAnimationPacket(anim, layer, animation, uuid));
|
||||
return ApiBack.SUCCESS;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Trigger event and let implementation class handle
|
||||
* @param player player
|
||||
* @param layer target layer
|
||||
* @param animation animation
|
||||
* @return Api back
|
||||
*/
|
||||
default ApiBack playAnimation(@NotNull ServerPlayer player, ResourceLocation layer, AnimationData animation){
|
||||
return ANIMATION_RUNNER.testLoadedAndCall(() -> {
|
||||
AnimationEvent.Play playEvent = new AnimationEvent.Play(LogicalSide.SERVER, player, layer, animation);
|
||||
boolean post = MinecraftForge.EVENT_BUS.post(playEvent);
|
||||
Event.Result eventResult = playEvent.getResult();
|
||||
if(post || eventResult == Event.Result.DENY) return ApiBack.BE_CANCELLED;
|
||||
return playAnimationServer(player, layer, animation);
|
||||
});
|
||||
}
|
||||
ApiBack playAnimationServer(@NotNull ServerPlayer player, ResourceLocation layer, AnimationData animation);
|
||||
|
||||
/**
|
||||
* Request animation
|
||||
*/
|
||||
default ApiBack request(ServerPlayer player, ServerPlayer target, ResourceLocation layer, AnimationData animation, boolean isRide) {
|
||||
return ANIMATION_RUNNER.testLoadedAndCall(() -> {
|
||||
if(!isAnimationLayerPresent(layer) || !isAnimationPresent(animation.getKey()))
|
||||
return ApiBack.RESOURCE_NOT_FOUND;
|
||||
int tickCount = player.server.getTickCount();
|
||||
UUID playerUUID = player.getUUID();
|
||||
UUID targetUUID = target.getUUID();
|
||||
|
||||
int origin = ModConfigs.Server.requestValidTime.get() * 20;
|
||||
int cooldown = ModConfigs.Server.requestCooldown.get() * 20;
|
||||
AnimationEvent.Send sendEvent = new AnimationEvent.Send(LogicalSide.SERVER, origin, cooldown, AnimationEvent.Type.APPLY);
|
||||
boolean post = MinecraftForge.EVENT_BUS.post(sendEvent);
|
||||
if(post) return ApiBack.BE_CANCELLED;
|
||||
Event.Result eventResult = sendEvent.getResult();
|
||||
switch (eventResult) {
|
||||
case DENY : return ApiBack.BE_CANCELLED;
|
||||
case DEFAULT : {
|
||||
//Test if is not in cooldown
|
||||
int lastTick = lastRequestTickMap.getOrDefault(playerUUID, 0);
|
||||
if(Math.max(tickCount - sendEvent.getCooldownTick(), 0) >= lastTick) {
|
||||
lastRequestTickMap.put(playerUUID, tickCount);
|
||||
} else return ApiBack.COOLDOWN;
|
||||
}
|
||||
case ALLOW : {
|
||||
//Add to cache, done
|
||||
int expireTick = sendEvent.getValidTick() + tickCount;
|
||||
requestMap.put(playerUUID, new RequestAnimationRecord(layer, animation, expireTick, targetUUID, isRide));
|
||||
return ApiBack.SUCCESS;
|
||||
}
|
||||
default: return ApiBack.UNSUPPORTED;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Client request
|
||||
* @param target target
|
||||
* @param layer layer
|
||||
* @param animation animation
|
||||
* @return Api back
|
||||
*/
|
||||
default ApiBack request(AbstractClientPlayer target, ResourceLocation layer, ResourceLocation animation) {
|
||||
return ANIMATION_RUNNER.testLoadedAndCall(() -> {
|
||||
if(!isAnimationLayerPresent(layer) || !isAnimationPresent(animation))
|
||||
return ApiBack.RESOURCE_NOT_FOUND;
|
||||
KeyframeAnimation keyframeAnimation = PlayerAnimationRegistry.getAnimation(animation);
|
||||
if(keyframeAnimation == null) return ApiBack.RESOURCE_NOT_FOUND;
|
||||
D data = getAnimation(animation);
|
||||
if(data == null) return ApiBack.RESOURCE_NOT_FOUND;
|
||||
AnimationEvent.Send sendEvent = new AnimationEvent.Send(
|
||||
LogicalSide.CLIENT,
|
||||
0,
|
||||
0,
|
||||
AnimationEvent.Type.REQUEST
|
||||
);
|
||||
boolean post = MinecraftForge.EVENT_BUS.post(sendEvent);
|
||||
Event.Result eventResult = sendEvent.getResult();
|
||||
if(post || eventResult == Event.Result.DENY) return ApiBack.BE_CANCELLED;
|
||||
ModChannel.sendToServer(new RequestAnimationPacket(data, layer, target.getUUID()));
|
||||
return ApiBack.SUCCESS;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Accept Request
|
||||
*/
|
||||
default ApiBack acceptRequest(ServerPlayer player, ServerPlayer requestor) {
|
||||
return ANIMATION_RUNNER.testLoadedAndCall(() -> {
|
||||
UUID requestorUUID = requestor.getUUID();
|
||||
RequestAnimationRecord record = requestMap.getOrDefault(requestorUUID, null);
|
||||
if (record == null) return ApiBack.UNSUPPORTED;
|
||||
UUID uuid = player.getUUID();
|
||||
if (!record.target().equals(uuid)) return ApiBack.UNSUPPORTED;
|
||||
|
||||
AnimationEvent.Accept acceptEvent = new AnimationEvent.Accept(AnimationEvent.Type.REQUEST, 0);
|
||||
boolean post = MinecraftForge.EVENT_BUS.post(acceptEvent);
|
||||
if(post) return ApiBack.BE_CANCELLED;
|
||||
Event.Result eventResult = acceptEvent.getResult();
|
||||
switch (eventResult) {
|
||||
case DENY : return ApiBack.BE_CANCELLED;
|
||||
case DEFAULT : {
|
||||
//Test if is in valid time
|
||||
int tickCount = requestor.server.getTickCount();
|
||||
if (tickCount >= record.expireTick()) {
|
||||
requestMap.remove(requestorUUID);
|
||||
return ApiBack.OPERATION_EXPIRE;
|
||||
}
|
||||
}
|
||||
case ALLOW : {
|
||||
//done
|
||||
ApiBack back;
|
||||
if(record.isRide()) back = playAnimationWithRide(player, record.layer(), record.animation(), false);
|
||||
else back = playAnimation(player, record.layer(), record.animation());
|
||||
if(back == ApiBack.SUCCESS) requestMap.remove(requestorUUID);
|
||||
return back;
|
||||
}
|
||||
default: return ApiBack.UNSUPPORTED;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Send apply to join animation on server side
|
||||
*/
|
||||
default ApiBack apply(ServerPlayer player, ServerPlayer target) {
|
||||
return ANIMATION_RUNNER.testLoadedAndCall(() -> {
|
||||
int tickCount = player.server.getTickCount();
|
||||
|
||||
int origin = ModConfigs.Server.applyValidTime.get() * 20;
|
||||
int cooldown = ModConfigs.Server.applyCooldown.get() * 20;
|
||||
AnimationEvent.Send sendEvent = new AnimationEvent.Send(
|
||||
LogicalSide.SERVER,
|
||||
origin,
|
||||
cooldown,
|
||||
AnimationEvent.Type.APPLY
|
||||
);
|
||||
boolean post = MinecraftForge.EVENT_BUS.post(sendEvent);
|
||||
if(post) return ApiBack.BE_CANCELLED;
|
||||
Event.Result eventResult = sendEvent.getResult();
|
||||
switch (eventResult) {
|
||||
case DENY : return ApiBack.BE_CANCELLED;
|
||||
case DEFAULT : {
|
||||
//Test if is not in cooldown
|
||||
int lastTick = lastApplyTickMap.getOrDefault(player.getUUID(), 0);
|
||||
if(Math.max(tickCount - sendEvent.getCooldownTick(), 0) >= lastTick) {
|
||||
lastApplyTickMap.put(player.getUUID(), tickCount);
|
||||
} else return ApiBack.COOLDOWN;
|
||||
}
|
||||
case ALLOW : {
|
||||
//Add to cache, done
|
||||
int expireTick = sendEvent.getValidTick() + tickCount;
|
||||
applyMap.put(player.getUUID(), new ApplyAnimationRecord(target.getUUID(), expireTick));
|
||||
return ApiBack.SUCCESS;
|
||||
}
|
||||
default: return ApiBack.UNSUPPORTED;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Send apply to join animation on client side. <br>
|
||||
* It will send network packet and work on server side.
|
||||
*/
|
||||
@OnlyIn(Dist.CLIENT)
|
||||
default ApiBack apply(AbstractClientPlayer target) {
|
||||
return ANIMATION_RUNNER.testLoadedAndCall(() -> {
|
||||
AnimationEvent.Send sendEvent = new AnimationEvent.Send(
|
||||
LogicalSide.CLIENT,
|
||||
0,
|
||||
0,
|
||||
AnimationEvent.Type.APPLY
|
||||
);
|
||||
boolean post = MinecraftForge.EVENT_BUS.post(sendEvent);
|
||||
Event.Result eventResult = sendEvent.getResult();
|
||||
if(post || eventResult == Event.Result.DENY) return ApiBack.BE_CANCELLED;
|
||||
ModChannel.sendToServer(new ApplyAnimationPacket(target.getUUID()));
|
||||
return ApiBack.SUCCESS;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Player accept join apply.
|
||||
* @param player Acceptor
|
||||
* @param applier Applier
|
||||
* @return If accept and riding success
|
||||
*/
|
||||
default ApiBack acceptApply(ServerPlayer player, ServerPlayer applier) {
|
||||
return ANIMATION_RUNNER.testLoadedAndCall(() -> {
|
||||
ApplyAnimationRecord record = applyMap.getOrDefault(applier.getUUID(), null);
|
||||
if (record == null) return ApiBack.UNSUPPORTED;
|
||||
UUID uuid = player.getUUID();
|
||||
if (!record.target().equals(uuid)) return ApiBack.UNSUPPORTED;
|
||||
|
||||
int maxDistance = ModConfigs.Server.applyValidDistance.get();
|
||||
AnimationEvent.Accept acceptEvent = new AnimationEvent.Accept(AnimationEvent.Type.APPLY, maxDistance);
|
||||
boolean post = MinecraftForge.EVENT_BUS.post(acceptEvent);
|
||||
if(post) return ApiBack.BE_CANCELLED;
|
||||
Event.Result eventResult = acceptEvent.getResult();
|
||||
switch (eventResult) {
|
||||
case DENY : return ApiBack.BE_CANCELLED;
|
||||
case DEFAULT : {
|
||||
//Test if is in valid distance
|
||||
int validDistance = acceptEvent.getValidDistance();
|
||||
if(player.distanceToSqr(applier) > validDistance * validDistance) {
|
||||
return ApiBack.OUT_RANGE;
|
||||
}
|
||||
int tickCount = applier.server.getTickCount();
|
||||
if (tickCount > record.expireTick()) {
|
||||
applyMap.remove(applier.getUUID());
|
||||
return ApiBack.OPERATION_EXPIRE;
|
||||
}
|
||||
}
|
||||
case ALLOW : {
|
||||
//done
|
||||
ApiBack back = joinAnimation(applier, player, false);
|
||||
if(back == ApiBack.SUCCESS) applyMap.remove(applier.getUUID());
|
||||
return back;
|
||||
}
|
||||
default: return ApiBack.UNSUPPORTED;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Send invite on server side.
|
||||
* @param player Sender
|
||||
* @param animation Raw animation info
|
||||
* @param layer Target layer
|
||||
* @param targets Be invited players
|
||||
*/
|
||||
default ApiBack invite(ServerPlayer player, ResourceLocation layer, AnimationData animation, Collection<UUID> targets) {
|
||||
return ANIMATION_RUNNER.testLoadedAndCall(() -> {
|
||||
if(!isAnimationLayerPresent(layer) || !isAnimationPresent(animation.getKey()))
|
||||
return ApiBack.RESOURCE_NOT_FOUND;
|
||||
int tickCount = player.server.getTickCount();
|
||||
|
||||
int origin = ModConfigs.Server.inviteValidTime.get() * 20;
|
||||
int cooldown = ModConfigs.Server.inviteCooldown.get() * 20;
|
||||
AnimationEvent.Send sendEvent = new AnimationEvent.Send(
|
||||
LogicalSide.SERVER,
|
||||
origin,
|
||||
cooldown,
|
||||
AnimationEvent.Type.INVITE
|
||||
);
|
||||
boolean post = MinecraftForge.EVENT_BUS.post(sendEvent);
|
||||
if(post) return ApiBack.BE_CANCELLED;
|
||||
Event.Result eventResult = sendEvent.getResult();
|
||||
switch (eventResult) {
|
||||
case DENY : return ApiBack.BE_CANCELLED;
|
||||
case DEFAULT : {
|
||||
//Test if is not in cooldown
|
||||
int lastTick = lastInviteTickMap.getOrDefault(player.getUUID(), 0);
|
||||
if(Math.max(tickCount - sendEvent.getCooldownTick(), 0) >= lastTick) {
|
||||
lastInviteTickMap.put(player.getUUID(), tickCount);
|
||||
} else return ApiBack.COOLDOWN;
|
||||
}
|
||||
case ALLOW : {
|
||||
//Add to cache, done
|
||||
int expireTick = sendEvent.getValidTick() + tickCount;
|
||||
inviteMap.put(player.getUUID(), new InviteAnimationRecord(layer, animation, expireTick, new ArrayList<>(targets)));
|
||||
return ApiBack.SUCCESS;
|
||||
}
|
||||
default: return ApiBack.UNSUPPORTED;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Send invite on client side. <br>
|
||||
* It will send network packet and work on server side.
|
||||
* @param animation Raw animation info
|
||||
* @param layer Target layer
|
||||
* @param targets Be invited players
|
||||
* @return If send to server successfully.
|
||||
*/
|
||||
@OnlyIn(Dist.CLIENT)
|
||||
default ApiBack invite(ResourceLocation layer, ResourceLocation animation, AbstractClientPlayer ... targets){
|
||||
return ANIMATION_RUNNER.testLoadedAndCall(() -> {
|
||||
if(!isAnimationLayerPresent(layer) || !isAnimationPresent(animation))
|
||||
return ApiBack.RESOURCE_NOT_FOUND;
|
||||
KeyframeAnimation keyframeAnimation = PlayerAnimationRegistry.getAnimation(animation);
|
||||
if(keyframeAnimation == null) return ApiBack.RESOURCE_NOT_FOUND;
|
||||
Set<UUID> list = Arrays.stream(targets).map(AbstractClientPlayer::getUUID).collect(Collectors.toSet());
|
||||
D data = getAnimation(animation);
|
||||
if(data == null) return ApiBack.RESOURCE_NOT_FOUND;
|
||||
AnimationEvent.Send sendEvent = new AnimationEvent.Send(
|
||||
LogicalSide.CLIENT,
|
||||
0,
|
||||
0,
|
||||
AnimationEvent.Type.INVITE
|
||||
);
|
||||
boolean post = MinecraftForge.EVENT_BUS.post(sendEvent);
|
||||
Event.Result eventResult = sendEvent.getResult();
|
||||
if(post || eventResult == Event.Result.DENY) return ApiBack.BE_CANCELLED;
|
||||
ModChannel.sendToServer(new InviteAnimationPacket(data, layer, list));
|
||||
return ApiBack.SUCCESS;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Player accept invite
|
||||
* @param player Acceptor
|
||||
* @param inviter Inviter
|
||||
* @return If accept and riding success.
|
||||
*/
|
||||
default ApiBack acceptInvite(ServerPlayer player, ServerPlayer inviter) {
|
||||
return ANIMATION_RUNNER.testLoadedAndCall(() -> {
|
||||
InviteAnimationRecord record = inviteMap.getOrDefault(inviter.getUUID(), null);
|
||||
if(record == null) return ApiBack.UNSUPPORTED;
|
||||
UUID uuid = player.getUUID();
|
||||
if (!record.targets().contains(uuid)) return ApiBack.UNSUPPORTED;
|
||||
|
||||
int maxDistance = ModConfigs.Server.inviteValidDistance.get();
|
||||
AnimationEvent.Accept acceptEvent = new AnimationEvent.Accept(AnimationEvent.Type.INVITE, maxDistance);
|
||||
boolean post = MinecraftForge.EVENT_BUS.post(acceptEvent);
|
||||
if(post) return ApiBack.BE_CANCELLED;
|
||||
Event.Result eventResult = acceptEvent.getResult();
|
||||
switch (eventResult) {
|
||||
case DENY : return ApiBack.BE_CANCELLED;
|
||||
case DEFAULT : {
|
||||
//Test if is in valid distance
|
||||
int validDistance = acceptEvent.getValidDistance();
|
||||
if(player.distanceToSqr(inviter) > validDistance * validDistance) {
|
||||
return ApiBack.OUT_RANGE;
|
||||
}
|
||||
int tickCount = inviter.server.getTickCount();
|
||||
if(tickCount >= record.expireTick()) {
|
||||
inviteMap.remove(inviter.getUUID());
|
||||
return ApiBack.OPERATION_EXPIRE;
|
||||
}
|
||||
}
|
||||
case ALLOW : {
|
||||
//done
|
||||
ApiBack apiBack = playAnimationWithRide(inviter, record.layer(), record.animation(), false);
|
||||
if(apiBack == ApiBack.SUCCESS) {
|
||||
if(record.targets().isEmpty()) inviteMap.remove(inviter.getUUID());
|
||||
ApiBack back = joinAnimation(player, inviter, false);
|
||||
if(back == ApiBack.SUCCESS) record.targets().remove(uuid);
|
||||
return back;
|
||||
} else return apiBack;
|
||||
}
|
||||
default: return ApiBack.UNSUPPORTED;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -1,139 +0,0 @@
|
|||
/*
|
||||
* Anim Core mod
|
||||
* Copyright (C) 2026 LeisureTimeDock
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package top.leisuretimedock.animationcore.animation.service;
|
||||
|
||||
import top.leisuretimedock.animationcore.animation.capability.RawAnimationDataCapability;
|
||||
import top.leisuretimedock.animationcore.animation.data.AnimationData;
|
||||
import top.leisuretimedock.animationcore.animation.data.RawAnimationData;
|
||||
import top.leisuretimedock.animationcore.animation.event.create.AnimationRegisterEvent;
|
||||
import top.leisuretimedock.animationcore.animation.register.RawAnimationRegistry;
|
||||
import top.leisuretimedock.animationcore.animation.utils.AnimationUtils;
|
||||
import top.leisuretimedock.animationcore.animation.utils.ApiBack;
|
||||
import net.minecraft.client.player.AbstractClientPlayer;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraftforge.api.distmarker.Dist;
|
||||
import net.minecraftforge.api.distmarker.OnlyIn;
|
||||
import net.minecraftforge.common.util.FakePlayer;
|
||||
import net.minecraftforge.fml.loading.FMLEnvironment;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
|
||||
public class RawAnimationService implements IAnimationService<RawAnimationData, RawAnimationDataCapability> {
|
||||
public static final RawAnimationService INSTANCE = new RawAnimationService();
|
||||
|
||||
/**
|
||||
* Trigger raw animation registry. <br>
|
||||
* It will clear all have been registered raw animation, then trigger register event. <br>
|
||||
* If you need dynamic register, see {@link RawAnimationRegistry#register}, but it will reset when registry call register event. <br>
|
||||
* If you need static register, you can add listener to {@link AnimationRegisterEvent.RawAnimation}
|
||||
*/
|
||||
@OnlyIn(Dist.CLIENT)
|
||||
public void triggerRegistry() {
|
||||
ANIMATION_RUNNER.testLoadedAndRun(RawAnimationRegistry::triggerRegistry);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable RawAnimationData getAnimation(ResourceLocation location) {
|
||||
if(FMLEnvironment.dist == Dist.CLIENT) {
|
||||
return RawAnimationRegistry.getAnimations().getOrDefault(location, null);
|
||||
} else {
|
||||
return RawAnimationData.create(location);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable RawAnimationData getAnimation(CompoundTag tag) {
|
||||
return new RawAnimationData(){{deserializeNBT(tag);}};
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable RawAnimationDataCapability getCapability(Player player) {
|
||||
return RawAnimationDataCapability.getCapability(player).orElse(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearAnimations(ServerPlayer serverPlayer) {
|
||||
ANIMATION_RUNNER.testLoadedAndRun(() -> {
|
||||
Optional.ofNullable(getCapability(serverPlayer)).ifPresent(RawAnimationDataCapability::clearAnimations);
|
||||
detachAnimation(serverPlayer);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAnimationPresent(ResourceLocation location) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
@OnlyIn(Dist.CLIENT)
|
||||
public void refreshAnimation(AbstractClientPlayer clientPlayer) {
|
||||
ANIMATION_RUNNER.testLoadedAndRun(() -> {
|
||||
RawAnimationDataCapability data = getCapability(clientPlayer);
|
||||
if(data == null) return;
|
||||
Set<ResourceLocation> oldLayers = new HashSet<>(data.getAnimations().keySet());
|
||||
for (ResourceLocation layer : Set.copyOf(oldLayers)) {
|
||||
if (AnimationUtils.isClientAnimationStop(clientPlayer, layer)) {
|
||||
removeAnimation(clientPlayer, layer);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable ResourceLocation getAnimationPlaying(Player player, @Nullable ResourceLocation layer) {
|
||||
return ANIMATION_RUNNER.testLoadedAndCall(() -> {
|
||||
RawAnimationDataCapability data = getCapability(player);
|
||||
if(data == null) return null;
|
||||
if(layer == null){
|
||||
for (ResourceLocation value : data.getAnimations().values()) {
|
||||
if(value != null) return value;
|
||||
}
|
||||
} else if (isAnimationLayerPresent(layer)) {
|
||||
if(data.isAnimationPresent(layer)){
|
||||
return data.getAnimation(layer);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public ApiBack removeAnimation(@NotNull ServerPlayer serverPlayer, ResourceLocation layer) {
|
||||
boolean result = ANIMATION_RUNNER.testLoadedAndCall(() -> Optional.ofNullable(getCapability(serverPlayer))
|
||||
.map(data -> data.removeAnimation(layer)).orElse(false));
|
||||
return result ? ApiBack.SUCCESS : ApiBack.FAIL;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ApiBack playAnimationServer(@NotNull ServerPlayer player, ResourceLocation layer, AnimationData animation) {
|
||||
return ANIMATION_RUNNER.testLoadedAndCall(() -> {
|
||||
ResourceLocation key = animation.getKey();
|
||||
if(!isAnimationLayerPresent(layer) || !isAnimationPresent(key))
|
||||
return ApiBack.RESOURCE_NOT_FOUND;
|
||||
if(player instanceof FakePlayer) return ApiBack.UNSUPPORTED;
|
||||
Boolean flag = Optional.ofNullable(getCapability(player)).map(data ->
|
||||
data.mergeAnimation(layer, key)).orElse(false);
|
||||
return flag ? ApiBack.SUCCESS : ApiBack.FAIL;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -1,296 +0,0 @@
|
|||
/*
|
||||
* Anim Core mod
|
||||
* Copyright (C) 2026 LeisureTimeDock
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package top.leisuretimedock.animationcore.animation.utils;
|
||||
|
||||
import top.leisuretimedock.animationcore.AnimationCore;
|
||||
import top.leisuretimedock.animationcore.animation.AnimationApi;
|
||||
import top.leisuretimedock.animationcore.animation.capability.AnimationDataCapability;
|
||||
import top.leisuretimedock.animationcore.animation.capability.RawAnimationDataCapability;
|
||||
import top.leisuretimedock.animationcore.animation.capability.inter.IAnimationCapability;
|
||||
import top.leisuretimedock.animationcore.animation.data.AnimationData;
|
||||
import top.leisuretimedock.animationcore.animation.data.GenericAnimationData;
|
||||
import top.leisuretimedock.animationcore.animation.mixin.IMixinKeyframeAnimationPlayer;
|
||||
import top.leisuretimedock.animationcore.animation.register.AnimationRegistry;
|
||||
import top.leisuretimedock.animationcore.animation.service.AnimationService;
|
||||
import top.leisuretimedock.animationcore.animation.service.IAnimationService;
|
||||
import top.leisuretimedock.animationcore.animation.service.RawAnimationService;
|
||||
import top.leisuretimedock.animationcore.core.datagen.ModLang;
|
||||
import dev.kosmx.playerAnim.api.layered.IAnimation;
|
||||
import dev.kosmx.playerAnim.api.layered.KeyframeAnimationPlayer;
|
||||
import dev.kosmx.playerAnim.api.layered.ModifierLayer;
|
||||
import dev.kosmx.playerAnim.api.layered.modifier.AbstractFadeModifier;
|
||||
import dev.kosmx.playerAnim.core.data.KeyframeAnimation;
|
||||
import dev.kosmx.playerAnim.core.util.Ease;
|
||||
import dev.kosmx.playerAnim.minecraftApi.PlayerAnimationAccess;
|
||||
import net.minecraft.ChatFormatting;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.player.AbstractClientPlayer;
|
||||
import net.minecraft.client.player.LocalPlayer;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraftforge.api.distmarker.Dist;
|
||||
import net.minecraftforge.api.distmarker.OnlyIn;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
public class AnimationUtils {
|
||||
/**
|
||||
* Test if layer exist animation which is not end. <br>
|
||||
* Only in dist client
|
||||
* @param player Target player
|
||||
* @param layer Target layer
|
||||
* @return True when animation is loop, or currentTick not larger than endTick
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
@OnlyIn(Dist.CLIENT)
|
||||
public static boolean isClientAnimationNotEnd(AbstractClientPlayer player, @Nullable ResourceLocation layer) {
|
||||
return IAnimationService.ANIMATION_RUNNER.testLoadedAndCall(() -> {
|
||||
try {
|
||||
Set<ResourceLocation> resourceLocations = new HashSet<>();
|
||||
if(layer == null) resourceLocations.addAll(AnimationRegistry.getLayers().keySet());
|
||||
else resourceLocations.add(layer);
|
||||
for (ResourceLocation location : resourceLocations) {
|
||||
ModifierLayer<IAnimation> animationModifierLayer = (ModifierLayer<IAnimation>) PlayerAnimationAccess
|
||||
.getPlayerAssociatedData(player).get(location);
|
||||
if(animationModifierLayer == null) continue;
|
||||
KeyframeAnimationPlayer animation = (KeyframeAnimationPlayer) animationModifierLayer.getAnimation();
|
||||
if(animation == null) return false;
|
||||
int currentTick = animation.getCurrentTick();
|
||||
boolean isLoop = animation.getData().isInfinite;
|
||||
int endTick = animation.getData().endTick;
|
||||
return isLoop || currentTick <= endTick;
|
||||
}
|
||||
} catch (Exception ignored) {}
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Test if layer exist animation which is not stop. <br>
|
||||
* Only in dist client
|
||||
* @param player Target player
|
||||
* @param layer Target layer
|
||||
* @return True when the currentTick not larger than stopTick
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
@OnlyIn(Dist.CLIENT)
|
||||
public static boolean isClientAnimationStop(AbstractClientPlayer player, @Nullable ResourceLocation layer) {
|
||||
return IAnimationService.ANIMATION_RUNNER.testLoadedAndCall(() -> {
|
||||
try {
|
||||
Set<ResourceLocation> resourceLocations = new HashSet<>();
|
||||
if(layer == null) resourceLocations.addAll(AnimationRegistry.getLayers().keySet());
|
||||
else resourceLocations.add(layer);
|
||||
boolean isNoneAnimation = true;
|
||||
for (ResourceLocation location : resourceLocations) {
|
||||
ModifierLayer<IAnimation> animationModifierLayer = (ModifierLayer<IAnimation>) PlayerAnimationAccess
|
||||
.getPlayerAssociatedData(player).get(location);
|
||||
if(animationModifierLayer == null) continue;
|
||||
KeyframeAnimationPlayer animation = (KeyframeAnimationPlayer) animationModifierLayer.getAnimation();
|
||||
if(animation == null) continue;
|
||||
int currentTick = animation.getCurrentTick();
|
||||
int stopTick = animation.getStopTick();
|
||||
if(currentTick < stopTick) isNoneAnimation = false;
|
||||
}
|
||||
return isNoneAnimation;
|
||||
} catch (Exception ignored) {}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Client sync animation
|
||||
* @param clientPlayer player
|
||||
* @param target target
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
@OnlyIn(Dist.CLIENT)
|
||||
public static void syncAnimation(AbstractClientPlayer clientPlayer, AbstractClientPlayer target) {
|
||||
IAnimationCapability clientPlayerData = AnimationDataCapability.getCapability(clientPlayer).orElse(null);
|
||||
IAnimationCapability targetData = AnimationDataCapability.getCapability(target).orElse(null);
|
||||
if(clientPlayerData == null) return;
|
||||
if(targetData == null) return;
|
||||
ResourceLocation clientPlayerLayer = clientPlayerData.getRiderAnimLayer();
|
||||
ResourceLocation targetLayer = targetData.getRiderAnimLayer();
|
||||
try {
|
||||
if(clientPlayerLayer == null || targetLayer == null) return;
|
||||
ModifierLayer<IAnimation> modifierLayer = (ModifierLayer<IAnimation>) PlayerAnimationAccess
|
||||
.getPlayerAssociatedData(clientPlayer).get(clientPlayerLayer);
|
||||
ModifierLayer<IAnimation> targetModifierLayer = (ModifierLayer<IAnimation>) PlayerAnimationAccess
|
||||
.getPlayerAssociatedData(target).get(targetLayer);
|
||||
if(modifierLayer == null || targetModifierLayer == null) return;
|
||||
IMixinKeyframeAnimationPlayer animation = (IMixinKeyframeAnimationPlayer) modifierLayer.getAnimation();
|
||||
KeyframeAnimationPlayer targetAnimation = (KeyframeAnimationPlayer) targetModifierLayer.getAnimation();
|
||||
if(animation == null || targetAnimation == null) return;
|
||||
int currentTick = targetAnimation.getCurrentTick();
|
||||
animation.animcore$setCurrentTick(currentTick);
|
||||
} catch (Exception ignored) {}
|
||||
}
|
||||
|
||||
/**
|
||||
* client remove animation
|
||||
* @param clientPlayer player
|
||||
* @param layer layer
|
||||
*/
|
||||
@OnlyIn(Dist.CLIENT)
|
||||
public static void removeAnimation(@Nullable AbstractClientPlayer clientPlayer, ResourceLocation layer) {
|
||||
playAnimation(clientPlayer, layer, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Client play animation
|
||||
* @param clientPlayer player
|
||||
* @param layer layer
|
||||
* @param animation animation
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
@OnlyIn(Dist.CLIENT)
|
||||
public static void playAnimation(@Nullable AbstractClientPlayer clientPlayer, ResourceLocation layer, @Nullable ResourceLocation animation) {
|
||||
try {
|
||||
LocalPlayer localPlayer = Minecraft.getInstance().player;
|
||||
if(clientPlayer == null) clientPlayer = localPlayer;
|
||||
if(clientPlayer == null) return;
|
||||
ModifierLayer<IAnimation> modifierLayer = (ModifierLayer<IAnimation>) PlayerAnimationAccess
|
||||
.getPlayerAssociatedData(clientPlayer).get(layer);
|
||||
if(animation == null) {
|
||||
if(modifierLayer != null) {
|
||||
modifierLayer.replaceAnimationWithFade(
|
||||
AbstractFadeModifier.standardFadeIn(3, Ease.INOUTSINE),
|
||||
null
|
||||
);
|
||||
}
|
||||
return;
|
||||
}
|
||||
if(modifierLayer == null) return;
|
||||
AnimationData anim = AnimationService.INSTANCE.getAnimation(animation);
|
||||
if(anim == null) {
|
||||
if((anim = RawAnimationService.INSTANCE.getAnimation(animation)) == null)
|
||||
return;
|
||||
}
|
||||
KeyframeAnimation keyframeAnimation = anim.getAnimation();
|
||||
if(keyframeAnimation == null) {
|
||||
if(localPlayer == null) return;
|
||||
localPlayer.sendSystemMessage(Component.translatable(
|
||||
ModLang.TranslatableMessage.ANIMATION_RESOURCE_NOT_FOUND.getKey(),
|
||||
animation.toString()
|
||||
).withStyle(ChatFormatting.RED));
|
||||
modifierLayer.replaceAnimationWithFade(
|
||||
AbstractFadeModifier.standardFadeIn(3, Ease.INOUTSINE),
|
||||
null
|
||||
);
|
||||
IAnimationService<?, ?> service = AnimationApi.getServiceGetterHelper(anim.getKey()).getService();
|
||||
if(service != null) service.removeAnimation(clientPlayer, layer);
|
||||
return;
|
||||
}
|
||||
modifierLayer.replaceAnimationWithFade(
|
||||
AbstractFadeModifier.standardFadeIn(3, Ease.INOUTSINE),
|
||||
new KeyframeAnimationPlayer(keyframeAnimation)
|
||||
);
|
||||
}catch (Exception e) {
|
||||
AnimationCore.log.error("Failed to play animation : {}", animation, e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the LyingType when there are animations which playing on player. <br>
|
||||
* And It will return the first which be found.
|
||||
* @param player Target player
|
||||
* @return The first LyingType it find.
|
||||
*/
|
||||
@Nullable
|
||||
public static GenericAnimationData.LyingType getSideView(Player player) {
|
||||
return IAnimationService.ANIMATION_RUNNER.testLoadedAndCall(() -> {
|
||||
IAnimationCapability data = AnimationDataCapability.getCapability(player).orElse(null);
|
||||
RawAnimationDataCapability rawData = RawAnimationDataCapability.getCapability(player).orElse(null);
|
||||
if(data == null) return null;
|
||||
if(rawData == null) return null;
|
||||
|
||||
Map.Entry<Integer, AnimationData.LyingType> animations = null;
|
||||
ArrayList<ResourceLocation> resourceLocations = new ArrayList<>();
|
||||
resourceLocations.addAll(data.getAnimations().values());
|
||||
resourceLocations.addAll(rawData.getAnimations().values());
|
||||
for (ResourceLocation value : resourceLocations) {
|
||||
AnimationData animation = AnimationApi.getDataHelper().getAnimationData(value);
|
||||
if(animation == null) return null;
|
||||
AnimationData.LyingType type = animation.getLyingType();
|
||||
if(type == null) continue;
|
||||
switch (type) {
|
||||
case FRONT,BACK -> {}
|
||||
case LEFT,RIGHT -> {
|
||||
if(animations == null || animations.getKey() < animation.getCamComputePriority()) {
|
||||
animations = new AbstractMap.SimpleEntry<>(animation.getCamComputePriority(), type);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return animations == null ? null : animations.getValue();
|
||||
});
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@OnlyIn(Dist.CLIENT)
|
||||
public static AnimationData getPredicateAnimationData(Predicate<AnimationData> predicate) {
|
||||
return IAnimationService.ANIMATION_RUNNER.testLoadedAndCall(() -> {
|
||||
LocalPlayer player = Minecraft.getInstance().player;
|
||||
if(player == null) return null;
|
||||
IAnimationCapability data = AnimationDataCapability.getCapability(player).orElse(null);
|
||||
RawAnimationDataCapability rawData = RawAnimationDataCapability.getCapability(player).orElse(null);
|
||||
if(data == null) return null;
|
||||
if(rawData == null) return null;
|
||||
|
||||
Map.Entry<Integer, AnimationData> animations = null;
|
||||
ArrayList<ResourceLocation> resourceLocations = new ArrayList<>();
|
||||
resourceLocations.addAll(data.getAnimations().values());
|
||||
resourceLocations.addAll(rawData.getAnimations().values());
|
||||
if(data.getRiderAnimation() != null) resourceLocations.add(data.getRiderAnimation());
|
||||
for (ResourceLocation value : resourceLocations) {
|
||||
AnimationData animation = AnimationApi.getDataHelper().getAnimationData(value);
|
||||
if(animation == null) continue;
|
||||
if(!predicate.test(animation)) continue;
|
||||
if(animations == null || animations.getKey() < animation.getCamComputePriority()) {
|
||||
animations = new AbstractMap.SimpleEntry<>(animation.getCamComputePriority(), animation);
|
||||
}
|
||||
}
|
||||
return animations == null ? null : animations.getValue();
|
||||
});
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static AnimationData getEyeModifierAnimationData(Player player) {
|
||||
IAnimationCapability data = AnimationDataCapability.getCapability(player).orElse(null);
|
||||
RawAnimationDataCapability rawData = RawAnimationDataCapability.getCapability(player).orElse(null);
|
||||
if(data == null) return null;
|
||||
if(rawData == null) return null;
|
||||
Map.Entry<AnimationData, Integer> entry = null;
|
||||
List<ResourceLocation> values = new ArrayList<>();
|
||||
if(data.getRiderAnimation() != null) values.add(data.getRiderAnimation());
|
||||
values.addAll(data.getAnimations().values());
|
||||
values.addAll(rawData.getAnimations().values());
|
||||
for (ResourceLocation value : values) {
|
||||
AnimationData animation = AnimationApi.getDataHelper().getAnimationData(value);
|
||||
if(animation == null) continue;
|
||||
float animationCamY = (float) animation.getCamPosOffset().y;
|
||||
int priority = animation.getCamComputePriority();
|
||||
if((entry == null && animationCamY != 0)
|
||||
|| (entry != null && priority > entry.getValue())) {
|
||||
entry = new HashMap.SimpleEntry<>(animation, priority);
|
||||
}
|
||||
}
|
||||
return entry == null ? null : entry.getKey();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,49 +0,0 @@
|
|||
/*
|
||||
* Anim Core mod
|
||||
* Copyright (C) 2026 LeisureTimeDock
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package top.leisuretimedock.animationcore.animation.utils;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public enum ApiBack {
|
||||
FAIL(0),
|
||||
SUCCESS(1),
|
||||
COOLDOWN(2),
|
||||
RESOURCE_NOT_FOUND(3),
|
||||
OUT_RANGE(4),
|
||||
OPERATION_EXPIRE(5),
|
||||
UNSUPPORTED(6),
|
||||
BE_CANCELLED(7),
|
||||
;
|
||||
public final int value;
|
||||
ApiBack(int value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public boolean isValueOf(int value) {
|
||||
return this.value == value;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static ApiBack valueOf(int value) {
|
||||
ApiBack[] values = ApiBack.values();
|
||||
for (ApiBack v : values) {
|
||||
if (v.isValueOf(value)) {
|
||||
return v;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,71 +0,0 @@
|
|||
/*
|
||||
* Anim Core mod
|
||||
* Copyright (C) 2026 LeisureTimeDock
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package top.leisuretimedock.animationcore.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) {}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,212 +0,0 @@
|
|||
/*
|
||||
* Anim Core mod
|
||||
* Copyright (C) 2026 LeisureTimeDock
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package top.leisuretimedock.animationcore.capability;
|
||||
|
||||
import top.leisuretimedock.animationcore.capability.data.ICapabilitySync;
|
||||
import top.leisuretimedock.animationcore.capability.data.entity.EntityCapabilityHandler;
|
||||
import top.leisuretimedock.animationcore.capability.data.entity.EntityCapabilityRegistry;
|
||||
import top.leisuretimedock.animationcore.capability.data.player.PlayerCapabilityHandler;
|
||||
import top.leisuretimedock.animationcore.capability.data.player.PlayerCapabilityRegistry;
|
||||
import top.leisuretimedock.animationcore.capability.network.CapabilityChannel;
|
||||
import top.leisuretimedock.animationcore.capability.network.ICapabilityPacket;
|
||||
import top.leisuretimedock.animationcore.core.ModChannel;
|
||||
import net.minecraft.network.FriendlyByteBuf;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraftforge.eventbus.api.IEventBus;
|
||||
import net.minecraftforge.network.NetworkEvent;
|
||||
import net.minecraftforge.network.simple.SimpleChannel;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public class CapabilityUtils {
|
||||
|
||||
/**
|
||||
* Simultaneously invite player capability and corresponding network packets
|
||||
* @param key The unique name of capability
|
||||
* @param capabilityRecord Registration data for capability
|
||||
* @param channelRegister You should create an instance in advance to pass in, see: {@link CapabilityUtils#createChannel}
|
||||
* @param cid Network Channel Index
|
||||
* @param clazz Class of network packets
|
||||
* @param decoder Decoding of network packets
|
||||
* @param encoder Encoding of network packets
|
||||
* @param handler Handle of network packets
|
||||
* @param <T> extend {@code ICapabilityPacket<?>}
|
||||
*/
|
||||
public static <T extends ICapabilityPacket<?>> void registerPlayerCapabilityWithNetwork(
|
||||
ResourceLocation key, PlayerCapabilityRegistry.CapabilityRecord<? extends ICapabilitySync<Player>> capabilityRecord,
|
||||
CapabilityChannel channelRegister,
|
||||
int cid,
|
||||
Class<T> clazz,
|
||||
Function<FriendlyByteBuf, T> decoder,
|
||||
BiConsumer<T, FriendlyByteBuf> encoder,
|
||||
BiConsumer<T, Supplier<NetworkEvent.Context>> handler
|
||||
) {
|
||||
PlayerCapabilityRegistry.registerCapability(key, capabilityRecord);
|
||||
channelRegister.register(clazz, cid, decoder, encoder, handler);
|
||||
}
|
||||
|
||||
/**
|
||||
* Simultaneously invite entity capability and corresponding network packets
|
||||
* @param key The unique name of capability
|
||||
* @param capabilityRecord Registration data for capability
|
||||
* @param channelRegister You should create an instance in advance to pass in, see: {@link CapabilityUtils#createChannel}
|
||||
* @param cid Network Channel Index
|
||||
* @param clazz Class of network packets
|
||||
* @param decoder Decoding of network packets
|
||||
* @param encoder Encoding of network packets
|
||||
* @param handler Handle of network packets
|
||||
* @param <T> {@code ICapabilityPacket<?>}
|
||||
*/
|
||||
public static <T extends ICapabilityPacket<?>> void registerEntityCapabilityWithNetwork(
|
||||
ResourceLocation key, EntityCapabilityRegistry.CapabilityRecord<? extends ICapabilitySync<? extends Entity>> capabilityRecord,
|
||||
CapabilityChannel channelRegister,
|
||||
int cid,
|
||||
Class<T> clazz,
|
||||
Function<FriendlyByteBuf, T> decoder,
|
||||
BiConsumer<T, FriendlyByteBuf> encoder,
|
||||
BiConsumer<T, Supplier<NetworkEvent.Context>> handler
|
||||
) {
|
||||
EntityCapabilityRegistry.registerCapability(key, capabilityRecord);
|
||||
channelRegister.register(clazz, cid, decoder, encoder, handler);
|
||||
}
|
||||
|
||||
/**
|
||||
* See {@link PlayerCapabilityRegistry#registerCapability(ResourceLocation, PlayerCapabilityRegistry.CapabilityRecord)}
|
||||
* @param key The unique name of capability.
|
||||
* @param capabilityRecord Record is used to store various data of the capabilities that should be registered, refer to: {@link PlayerCapabilityRegistry.CapabilityRecord}
|
||||
* @param <T> extends {@code ICapabilitySync<Player>}
|
||||
*/
|
||||
public static <T extends ICapabilitySync<Player>> void registerPlayerCapability(ResourceLocation key, PlayerCapabilityRegistry.CapabilityRecord<T> capabilityRecord){
|
||||
PlayerCapabilityRegistry.registerCapability(key, capabilityRecord);
|
||||
}
|
||||
|
||||
/**
|
||||
* See {@link EntityCapabilityRegistry#registerCapability(ResourceLocation, EntityCapabilityRegistry.CapabilityRecord)}
|
||||
* @param key The unique name of capability.
|
||||
* @param capabilityRecord Record is used to store various data of the capabilities that should be registered, refer to: {@link EntityCapabilityRegistry.CapabilityRecord}
|
||||
* @param <T> extends {@code ICapabilitySync<Entity>}
|
||||
*/
|
||||
public static <T extends ICapabilitySync<Entity>> void registerEntityCapability(ResourceLocation key, EntityCapabilityRegistry.CapabilityRecord<T> capabilityRecord){
|
||||
EntityCapabilityRegistry.registerCapability(key, capabilityRecord);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a new PlayerCapabilityChannel instance through this method<br>
|
||||
* Generally, only network packets registered on different channels will be used
|
||||
* @param channel Your own mod channel
|
||||
* @return newInstances
|
||||
*/
|
||||
public static CapabilityChannel createChannel(SimpleChannel channel) {
|
||||
return new CapabilityChannel(channel);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the PlayerCapabilityChannel instance of the Channel in SCCore through this method
|
||||
* @return newInstances
|
||||
*/
|
||||
public static CapabilityChannel createChannel() {
|
||||
return new CapabilityChannel(ModChannel.INSTANCE);
|
||||
}
|
||||
|
||||
/**
|
||||
* By using this method to listen for capability events, all functions will be enabled<br>
|
||||
* Repeated calls will not cause anything
|
||||
* @param forgeBus Forge event bus
|
||||
*/
|
||||
public static void registerHandler(IEventBus forgeBus){
|
||||
PlayerCapabilityHandler.register(forgeBus);
|
||||
EntityCapabilityHandler.register(forgeBus);
|
||||
}
|
||||
|
||||
/**
|
||||
* Please obtain capability through this method
|
||||
* @param entity Target entity, type: {@code <E extends Entity>}
|
||||
* @param key The unique name of capability
|
||||
* @param clazz The capability type that should be returned. If it is null, will return: {@code ICapabilitySync<E>}
|
||||
* @param <E> extend {@code Entity}
|
||||
* @param <T> extend {@code ICapabilitySync<E>}
|
||||
* @return Return the corresponding capability
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
@Nullable
|
||||
public static <E extends Entity, T extends ICapabilitySync<E>> T getEntityCapability(E entity, ResourceLocation key, @Nullable Class<T> clazz) {
|
||||
try {
|
||||
ICapabilitySync<?> capabilitySync = entity.getCapability(
|
||||
EntityCapabilityRegistry.getCapabilityMap().get(key).capability()
|
||||
).resolve().orElse(null);
|
||||
if(clazz == null) return (T) capabilitySync;
|
||||
if(clazz.isInstance(capabilitySync))
|
||||
return clazz.cast(capabilitySync);
|
||||
else return null;
|
||||
}catch(Exception e){
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Please obtain capability through this method
|
||||
* @param entity Target entity, type: {@code <E extends Player>}
|
||||
* @param key The unique name of capability
|
||||
* @param clazz The capability type that should be returned. If it is null, will return: {@code ICapabilitySync<E>}
|
||||
* @param <E> extend {@code Player}
|
||||
* @param <T> extend {@code ICapabilitySync<E>}
|
||||
* @return Return the corresponding capability
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
@Nullable
|
||||
public static <E extends Player, T extends ICapabilitySync<E>> T getPlayerCapability(E entity, ResourceLocation key, @Nullable Class<T> clazz) {
|
||||
try {
|
||||
ICapabilitySync<?> capabilitySync = entity.getCapability(
|
||||
PlayerCapabilityRegistry.getCapabilityMap().get(key).capability()
|
||||
).resolve().orElse(null);
|
||||
if(clazz == null) return (T) capabilitySync;
|
||||
if(clazz.isInstance(capabilitySync))
|
||||
return clazz.cast(capabilitySync);
|
||||
else return null;
|
||||
}catch(Exception e){
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a capability of an unconverted type
|
||||
* @param entity Target
|
||||
* @param key The unique name of capability
|
||||
* @return An unconverted capability
|
||||
*/
|
||||
@Nullable
|
||||
public static ICapabilitySync<?> getCapability(Entity entity, ResourceLocation key) {
|
||||
if(entity == null) return null;
|
||||
try {
|
||||
if(entity instanceof Player) {
|
||||
return entity.getCapability(
|
||||
PlayerCapabilityRegistry.getCapabilityMap().get(key).capability()
|
||||
).resolve().orElse(null);
|
||||
}
|
||||
return entity.getCapability(
|
||||
EntityCapabilityRegistry.getCapabilityMap().get(key).capability()
|
||||
).resolve().orElse(null);
|
||||
}catch(Exception e){
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -1,84 +0,0 @@
|
|||
/*
|
||||
* Anim Core mod
|
||||
* Copyright (C) 2026 LeisureTimeDock
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package top.leisuretimedock.animationcore.capability.data;
|
||||
|
||||
import top.leisuretimedock.animationcore.capability.network.SimpleCapabilityPacket;
|
||||
import top.leisuretimedock.animationcore.core.ModChannel;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraftforge.common.util.INBTSerializable;
|
||||
|
||||
public interface ICapabilitySync<T extends Entity> extends INBTSerializable<CompoundTag> {
|
||||
/**
|
||||
* You should call it to set it to true in the setter of each property to trigger automatic synchronization
|
||||
* @param dirty dirty
|
||||
*/
|
||||
void setDirty(boolean dirty);
|
||||
|
||||
boolean isDirty();
|
||||
|
||||
ResourceLocation getKey();
|
||||
|
||||
/**
|
||||
* When this method is overridden, the super method should be called at the end
|
||||
* @param oldData old data
|
||||
* @param listenDone Whether to execute the completion method at the end: {@link ICapabilitySync#onCopyDone()}
|
||||
*/
|
||||
default void copyFrom(ICapabilitySync<?> oldData, boolean listenDone) {
|
||||
this.setDirty(oldData.isDirty());
|
||||
if(listenDone) onCopyDone();
|
||||
}
|
||||
|
||||
/**
|
||||
* After the copy is completed, if certain values need to be redefined, you should override this method <br>
|
||||
* Commonly used for resetting data when players cross dimensions or die
|
||||
*/
|
||||
default void onCopyDone(){}
|
||||
|
||||
/**
|
||||
* In general, it is recommended to rewrite it, otherwise it will be sent as a Channel instance of SCCore<br>
|
||||
* The server sends client synchronized data to all players
|
||||
*/
|
||||
default void sendToClient(){
|
||||
ModChannel.sendAllPlayer(getDefaultPacket());
|
||||
}
|
||||
|
||||
/**
|
||||
* In general, it is recommended to rewrite it, otherwise it will be sent as a Channel instance of SCCore<br>
|
||||
* The server sends client synchronized data to a single player
|
||||
* @param player Target player
|
||||
*/
|
||||
default void sendToClient(ServerPlayer player){
|
||||
ModChannel.sendToPlayer(getDefaultPacket(), player);
|
||||
}
|
||||
|
||||
/**
|
||||
* Rewrite this method to set a network packet class for your Capability <br>
|
||||
* When calling the sendToClient method, network packets will be obtained from here and sent directly <br>
|
||||
* In general, you should extend SimpleCapacityPackage and then override the method to return your subclass
|
||||
* @return SimpleCapacityPacket, a network packet class
|
||||
*/
|
||||
SimpleCapabilityPacket<T> getDefaultPacket();
|
||||
|
||||
/**
|
||||
* When players log in or entity join the world, the capability initialization will be called <br>
|
||||
* Must be implemented, but can be an empty method
|
||||
* @param entity Target
|
||||
*/
|
||||
void attachInit(T entity);
|
||||
}
|
||||
|
|
@ -1,81 +0,0 @@
|
|||
/*
|
||||
* Anim Core mod
|
||||
* Copyright (C) 2026 LeisureTimeDock
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package top.leisuretimedock.animationcore.capability.data.entity;
|
||||
|
||||
import top.leisuretimedock.animationcore.AnimationCore;
|
||||
import top.leisuretimedock.animationcore.capability.data.ICapabilitySync;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraftforge.common.capabilities.RegisterCapabilitiesEvent;
|
||||
import net.minecraftforge.event.AttachCapabilitiesEvent;
|
||||
import net.minecraftforge.eventbus.api.EventPriority;
|
||||
import net.minecraftforge.eventbus.api.IEventBus;
|
||||
import net.minecraftforge.eventbus.api.SubscribeEvent;
|
||||
import net.minecraftforge.fml.common.Mod;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@Mod.EventBusSubscriber(modid = AnimationCore.MOD_ID, bus = Mod.EventBusSubscriber.Bus.FORGE)
|
||||
public class EntityCapabilityHandler {
|
||||
private static final Logger log = LoggerFactory.getLogger(EntityCapabilityHandler.class);
|
||||
private static boolean isRegistered = false;
|
||||
|
||||
/**
|
||||
* It should be called in the Forge mainline to listen to the capability registration <br>
|
||||
* Suggest calling it in the Mod constructor method <br>
|
||||
* Normally AnimCore will call it, so you should not call it
|
||||
* @param forgeBus forge event bus
|
||||
*/
|
||||
public static void register(IEventBus forgeBus) {
|
||||
if (isRegistered) return;
|
||||
//remainder
|
||||
forgeBus.addListener(EventPriority.HIGHEST, EntityCapabilityRemainder::capabilitySync);
|
||||
forgeBus.addListener(EventPriority.HIGHEST, EntityCapabilityRemainder::onEntityBeTracked);
|
||||
forgeBus.addListener(EventPriority.HIGHEST, EntityCapabilityRemainder::onEntityJoin);
|
||||
isRegistered = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register capability
|
||||
* @param event event
|
||||
*/
|
||||
@SubscribeEvent(priority = EventPriority.HIGHEST)
|
||||
public static void registerCapability(RegisterCapabilitiesEvent event) {
|
||||
EntityCapabilityRegistry.getCapabilityMap().values().forEach(record ->
|
||||
event.register(record.interfaceClass())
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Attach capability to entity
|
||||
* @param event event
|
||||
*/
|
||||
@SubscribeEvent(priority = EventPriority.HIGHEST)
|
||||
public static void attachCapability(AttachCapabilitiesEvent<?> event) {
|
||||
if(event.getObject() instanceof Entity entity) {
|
||||
EntityCapabilityRegistry.getCapabilityMap().forEach((key, record) ->
|
||||
record.targets().forEach(target -> {
|
||||
if(target.isInstance(entity)) {
|
||||
try {
|
||||
ICapabilitySync<?> capabilitySync = (ICapabilitySync<?>) record.aClass().getDeclaredConstructor().newInstance();
|
||||
event.addCapability(key, new EntityCapabilityProvider<>(key, capabilitySync));
|
||||
} catch (Exception e) {
|
||||
log.error("Failed to instantiate capability sync class {}. Your capability invite is wrong.", record.aClass(), e);
|
||||
}
|
||||
}
|
||||
}));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,65 +0,0 @@
|
|||
/*
|
||||
* Anim Core mod
|
||||
* Copyright (C) 2026 LeisureTimeDock
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package top.leisuretimedock.animationcore.capability.data.entity;
|
||||
|
||||
import top.leisuretimedock.animationcore.capability.data.ICapabilitySync;
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraftforge.common.capabilities.AutoRegisterCapability;
|
||||
import net.minecraftforge.common.capabilities.Capability;
|
||||
import net.minecraftforge.common.capabilities.ICapabilitySerializable;
|
||||
import net.minecraftforge.common.util.LazyOptional;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* The final serialization, deserialization, and retrieval methods of capability
|
||||
* @param <C> extends {@link ICapabilitySync}
|
||||
*/
|
||||
@AutoRegisterCapability
|
||||
public class EntityCapabilityProvider<C extends ICapabilitySync<? extends Entity>> implements ICapabilitySerializable<CompoundTag> {
|
||||
private final C instance;
|
||||
private final ResourceLocation resourceLocation;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
* @param resourceLocation key
|
||||
* @param instance instance
|
||||
*/
|
||||
public EntityCapabilityProvider(ResourceLocation resourceLocation, C instance) {
|
||||
this.resourceLocation = resourceLocation;
|
||||
this.instance = instance;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public @NotNull <R> LazyOptional<R> getCapability(@NotNull Capability<R> cap, @Nullable Direction side) {
|
||||
Capability<C> iCapabilitySyncCapability = (Capability<C>) EntityCapabilityRegistry.getCapabilityRecord(resourceLocation).capability();
|
||||
return iCapabilitySyncCapability.orEmpty(cap, LazyOptional.of(() -> instance));
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompoundTag serializeNBT() {
|
||||
return instance.serializeNBT();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deserializeNBT(CompoundTag nbt) {
|
||||
instance.deserializeNBT(nbt);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,68 +0,0 @@
|
|||
/*
|
||||
* Anim Core mod
|
||||
* Copyright (C) 2026 LeisureTimeDock
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package top.leisuretimedock.animationcore.capability.data.entity;
|
||||
|
||||
import top.leisuretimedock.animationcore.capability.data.ICapabilitySync;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraftforge.common.capabilities.Capability;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
public class EntityCapabilityRegistry {
|
||||
public static final EntityCapabilityRegistry CAPABILITIES = new EntityCapabilityRegistry();
|
||||
private final Map<ResourceLocation, CapabilityRecord<?>> capabilityRecordMap = new HashMap<>();
|
||||
|
||||
/**
|
||||
* Registering entity capabilities through this method only applies to {@link net.minecraftforge.common.capabilities.RegisterCapabilitiesEvent}<br>
|
||||
* @param key The unique name of capability.
|
||||
* @param capabilityRecord Record is used to store various data of the capabilities that should be registered, refer to: {@link EntityCapabilityRegistry.CapabilityRecord}
|
||||
*/
|
||||
public static void registerCapability(ResourceLocation key, CapabilityRecord<?> capabilityRecord) {
|
||||
CAPABILITIES.capabilityRecordMap.put(key, capabilityRecord);
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtain corresponding capability data through this method
|
||||
* @param key Obtain based on key
|
||||
* @return capability
|
||||
*/
|
||||
public static CapabilityRecord<?> getCapabilityRecord(ResourceLocation key){
|
||||
return CAPABILITIES.capabilityRecordMap.get(key);
|
||||
}
|
||||
|
||||
public static Map<ResourceLocation, CapabilityRecord<?>> getCapabilityMap(){
|
||||
return CAPABILITIES.capabilityRecordMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* Record the registration data of capability
|
||||
* @param aClass The instance that will ultimately be attached to the entity. Should be an instance of ICapabilitySync
|
||||
* @param capability In general, it is not necessary to initialize it, default: <span>{@code CapabilityManager.get(new CapabilityToken<>(){})}</span>
|
||||
* @param interfaceClass The interface class corresponding to the instance, such as: ICapabilitySync.class.
|
||||
* @param targets Targets types attached to capability
|
||||
*/
|
||||
public record CapabilityRecord<T extends ICapabilitySync<? extends Entity>>(
|
||||
Class<?> aClass,
|
||||
Capability<T> capability,
|
||||
Class<T> interfaceClass,
|
||||
Set<Class<? extends Entity>> targets
|
||||
) {
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -1,85 +0,0 @@
|
|||
/*
|
||||
* Anim Core mod
|
||||
* Copyright (C) 2026 LeisureTimeDock
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package top.leisuretimedock.animationcore.capability.data.entity;
|
||||
|
||||
import top.leisuretimedock.animationcore.capability.CapabilityUtils;
|
||||
import top.leisuretimedock.animationcore.capability.data.ICapabilitySync;
|
||||
import top.leisuretimedock.animationcore.capability.data.player.PlayerCapabilityRegistry;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.entity.LivingEntity;
|
||||
import net.minecraftforge.event.entity.EntityJoinLevelEvent;
|
||||
import net.minecraftforge.event.entity.living.LivingEvent;
|
||||
import net.minecraftforge.event.entity.player.PlayerEvent;
|
||||
|
||||
public class EntityCapabilityRemainder {
|
||||
/**
|
||||
* Player start tracking an entity event<br>
|
||||
* When other entities are loaded, the client requires the capabilities of the other party, and this event can be actively sent<br>
|
||||
* Will call{@link ICapabilitySync#sendToClient(ServerPlayer)}
|
||||
* @param event event
|
||||
*/
|
||||
public static void onEntityBeTracked(PlayerEvent.StartTracking event) {
|
||||
if (event.getEntity() instanceof ServerPlayer attacker) {
|
||||
PlayerCapabilityRegistry.getCapabilityMap().forEach((key, value) -> {
|
||||
ICapabilitySync<?> data = CapabilityUtils.getCapability(event.getTarget(), key);
|
||||
if(data == null) return;
|
||||
data.sendToClient(attacker);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Entity Tick Event<br>
|
||||
* If the capability is dirty, it will call {@link ICapabilitySync#sendToClient()} <br>
|
||||
* For performance reasons, synchronization is only triggered once per second
|
||||
* @param event event
|
||||
*/
|
||||
public static void capabilitySync(LivingEvent.LivingTickEvent event) {
|
||||
LivingEntity entity = event.getEntity();
|
||||
if(!entity.level().isClientSide){
|
||||
if (entity.tickCount % 20 == 0) {
|
||||
PlayerCapabilityRegistry.getCapabilityMap().forEach((key, value) -> {
|
||||
ICapabilitySync<?> data = CapabilityUtils.getCapability(entity, key);
|
||||
if(data == null) return;
|
||||
if(data.isDirty()) {
|
||||
data.setDirty(false);
|
||||
data.sendToClient();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Event of entity joining level, initialization
|
||||
* @param event event
|
||||
*/
|
||||
public static void onEntityJoin(EntityJoinLevelEvent event) {
|
||||
Entity entity = event.getEntity();
|
||||
if(entity.level().isClientSide) return;
|
||||
EntityCapabilityRegistry.getCapabilityMap().forEach((key, value) -> {
|
||||
ICapabilitySync<Entity> data = CapabilityUtils.getEntityCapability(entity, key, null);
|
||||
if(data == null) return;
|
||||
if(data instanceof SimpleEntityCapabilitySync<?> capabilitySync){
|
||||
capabilitySync.setId(entity.getId());
|
||||
}
|
||||
data.attachInit(entity);
|
||||
data.setDirty(false);
|
||||
data.sendToClient();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -1,139 +0,0 @@
|
|||
/*
|
||||
* Anim Core mod
|
||||
* Copyright (C) 2026 LeisureTimeDock
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package top.leisuretimedock.animationcore.capability.data.entity;
|
||||
|
||||
import top.leisuretimedock.animationcore.capability.data.ICapabilitySync;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
|
||||
/**
|
||||
* It is recommended to manually add it during implementation: <br>
|
||||
* {@code key} ---- As the unique identifier of capability. <br>
|
||||
* {@code getCapability(Entity entity)} ---- Simplified method for obtaining capability<br>
|
||||
* 例:
|
||||
* <pre>
|
||||
* {@code
|
||||
* public static final ResourceLocation key =
|
||||
* new ResourceLocation(MyMod.MOD_ID, "sheep_data");
|
||||
* public static Optional<SheepDataCapability> getCapability(Sheep sheep){
|
||||
* return Optional.ofNullable(CapabilityUtils.getEntityCapability(
|
||||
* player, SheepDataCapability.key, SheepDataCapability.class
|
||||
* ));
|
||||
* }
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
*/
|
||||
public abstract class SimpleEntityCapabilitySync<T extends Entity> implements ICapabilitySync<T> {
|
||||
/**
|
||||
* Id
|
||||
*/
|
||||
public static final String Id = "Id";
|
||||
|
||||
private boolean dirty;
|
||||
private Integer id;
|
||||
|
||||
@Override
|
||||
public boolean isDirty() {
|
||||
return dirty;
|
||||
}
|
||||
|
||||
/**
|
||||
* You should call it to set it to true in the setter of each property to trigger automatic synchronization
|
||||
* @param dirty dirty
|
||||
*/
|
||||
@Override
|
||||
public void setDirty(boolean dirty) {
|
||||
this.dirty = dirty;
|
||||
}
|
||||
|
||||
/**
|
||||
* get id
|
||||
* @return Id
|
||||
*/
|
||||
public Integer getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
/**
|
||||
* set id
|
||||
* @param id id
|
||||
*/
|
||||
public void setId(Integer id) {
|
||||
this.id = id;
|
||||
setDirty(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy data from parameter instance to current instance <br>
|
||||
*You shouldn't rewrite it, you should implement: {@link SimpleEntityCapabilitySync#copyFrom(ICapabilitySync)}
|
||||
* @param oldData old data
|
||||
* @param listenDone Whether to execute the completion method at the end: {@link ICapabilitySync#onCopyDone()}
|
||||
*/
|
||||
@Override
|
||||
public void copyFrom(ICapabilitySync<?> oldData, boolean listenDone) {
|
||||
SimpleEntityCapabilitySync<?> data = (SimpleEntityCapabilitySync<?>) oldData;
|
||||
this.setId(data.getId());
|
||||
copyFrom(data);
|
||||
ICapabilitySync.super.copyFrom(oldData, listenDone);
|
||||
}
|
||||
|
||||
/**
|
||||
* The method that will be executed when triggering data replication
|
||||
* @param oldData Copy from this data to the current instance
|
||||
*/
|
||||
public abstract void copyFrom(ICapabilitySync<?> oldData);
|
||||
|
||||
/**
|
||||
* Serialize to tag <br>
|
||||
* You shouldn't rewrite it, you should implement: {@link SimpleEntityCapabilitySync#toTag(CompoundTag)}
|
||||
* @return tag
|
||||
*/
|
||||
@Override
|
||||
public CompoundTag serializeNBT() {
|
||||
CompoundTag tag = new CompoundTag();
|
||||
if(id != null) tag.putInt(Id, id);
|
||||
tag = toTag(tag);
|
||||
return tag;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deserialize to instance object <br>
|
||||
* You don't need to rewrite it, you should implement: {@link SimpleEntityCapabilitySync#fromTag(CompoundTag)}
|
||||
* @param nbt nbt
|
||||
*/
|
||||
@Override
|
||||
public void deserializeNBT(CompoundTag nbt) {
|
||||
this.id = null;
|
||||
if(nbt.contains(Id)) this.id = nbt.getInt(Id);
|
||||
fromTag(nbt);
|
||||
}
|
||||
|
||||
/**
|
||||
* In the serializeNBT method of SimpleElementCapability Sync, it will be called <br>
|
||||
* Actually equivalent to serializeNBT()
|
||||
* @param tag data tag
|
||||
* @return tag
|
||||
*/
|
||||
public abstract CompoundTag toTag(CompoundTag tag);
|
||||
|
||||
/**
|
||||
* In the deserializeNBT method of SimpleElementCapability Sync, it will be called <br>
|
||||
* Actually equivalent to deserializeNBT()
|
||||
* @param tag data tag
|
||||
*/
|
||||
public abstract void fromTag(CompoundTag tag);
|
||||
}
|
||||
|
|
@ -1,80 +0,0 @@
|
|||
/*
|
||||
* Anim Core mod
|
||||
* Copyright (C) 2026 LeisureTimeDock
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package top.leisuretimedock.animationcore.capability.data.player;
|
||||
|
||||
import top.leisuretimedock.animationcore.AnimationCore;
|
||||
import top.leisuretimedock.animationcore.capability.data.ICapabilitySync;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraftforge.common.capabilities.RegisterCapabilitiesEvent;
|
||||
import net.minecraftforge.event.AttachCapabilitiesEvent;
|
||||
import net.minecraftforge.eventbus.api.EventPriority;
|
||||
import net.minecraftforge.eventbus.api.IEventBus;
|
||||
import net.minecraftforge.eventbus.api.SubscribeEvent;
|
||||
import net.minecraftforge.fml.common.Mod;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@Mod.EventBusSubscriber(modid = AnimationCore.MOD_ID, bus = Mod.EventBusSubscriber.Bus.FORGE)
|
||||
public class PlayerCapabilityHandler {
|
||||
private static final Logger log = LoggerFactory.getLogger(PlayerCapabilityHandler.class);
|
||||
private static boolean isRegistered = false;
|
||||
|
||||
/**
|
||||
* It should be called in the Forge mainline to listen to the capability registration <br>
|
||||
* Suggest calling it in the Mod constructor method <br>
|
||||
* Normally SCCore will call it, so you should not call it
|
||||
* @param forgeBus forge event bus
|
||||
*/
|
||||
public static void register(IEventBus forgeBus) {
|
||||
if (isRegistered) return;
|
||||
//remainder
|
||||
forgeBus.addListener(EventPriority.HIGHEST, PlayerCapabilityRemainder::capabilitySync);
|
||||
forgeBus.addListener(EventPriority.HIGHEST, PlayerCapabilityRemainder::onPlayerClone);
|
||||
forgeBus.addListener(EventPriority.HIGHEST, PlayerCapabilityRemainder::onPlayerRespawn);
|
||||
forgeBus.addListener(EventPriority.HIGHEST, PlayerCapabilityRemainder::onEntityBeTracked);
|
||||
forgeBus.addListener(EventPriority.HIGHEST, PlayerCapabilityRemainder::onPlayerLogin);
|
||||
isRegistered = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register capability
|
||||
* @param event event
|
||||
*/
|
||||
@SubscribeEvent(priority = EventPriority.HIGHEST)
|
||||
public static void registerCapability(RegisterCapabilitiesEvent event) {
|
||||
PlayerCapabilityRegistry.getCapabilityMap().values().forEach(record ->
|
||||
event.register(record.interfaceClass())
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Attach capability to entity
|
||||
* @param event event
|
||||
*/
|
||||
@SubscribeEvent(priority = EventPriority.HIGHEST)
|
||||
public static void attachCapability(AttachCapabilitiesEvent<?> event) {
|
||||
if(event.getObject() instanceof Player) {
|
||||
PlayerCapabilityRegistry.getCapabilityMap().forEach((key, record) -> {
|
||||
try {
|
||||
ICapabilitySync<?> capabilitySync = (ICapabilitySync<?>) record.aClass().getDeclaredConstructor().newInstance();
|
||||
event.addCapability(key, new PlayerCapabilityProvider<>(key, capabilitySync));
|
||||
} catch (Exception e) {
|
||||
log.error("Failed to instantiate capability sync class {}. Your capability invite is wrong.", record.aClass(), e);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,64 +0,0 @@
|
|||
/*
|
||||
* Anim Core mod
|
||||
* Copyright (C) 2026 LeisureTimeDock
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package top.leisuretimedock.animationcore.capability.data.player;
|
||||
|
||||
import top.leisuretimedock.animationcore.capability.data.ICapabilitySync;
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraftforge.common.capabilities.AutoRegisterCapability;
|
||||
import net.minecraftforge.common.capabilities.Capability;
|
||||
import net.minecraftforge.common.capabilities.ICapabilitySerializable;
|
||||
import net.minecraftforge.common.util.LazyOptional;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* The final serialization, deserialization, and retrieval methods of capability
|
||||
* @param <C> extends {@link ICapabilitySync}
|
||||
*/
|
||||
@AutoRegisterCapability
|
||||
public class PlayerCapabilityProvider<C extends ICapabilitySync<?>> implements ICapabilitySerializable<CompoundTag> {
|
||||
private final C instance;
|
||||
private final ResourceLocation resourceLocation;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
* @param resourceLocation key
|
||||
* @param instance instance
|
||||
*/
|
||||
public PlayerCapabilityProvider(ResourceLocation resourceLocation, C instance) {
|
||||
this.resourceLocation = resourceLocation;
|
||||
this.instance = instance;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public @NotNull <R> LazyOptional<R> getCapability(@NotNull Capability<R> cap, @Nullable Direction side) {
|
||||
Capability<C> iCapabilitySyncCapability = (Capability<C>) PlayerCapabilityRegistry.getCapabilityRecord(resourceLocation).capability();
|
||||
return iCapabilitySyncCapability.orEmpty(cap, LazyOptional.of(() -> instance));
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompoundTag serializeNBT() {
|
||||
return instance.serializeNBT();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deserializeNBT(CompoundTag nbt) {
|
||||
instance.deserializeNBT(nbt);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,59 +0,0 @@
|
|||
/*
|
||||
* Anim Core mod
|
||||
* Copyright (C) 2026 LeisureTimeDock
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package top.leisuretimedock.animationcore.capability.data.player;
|
||||
|
||||
import top.leisuretimedock.animationcore.capability.data.ICapabilitySync;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraftforge.common.capabilities.Capability;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class PlayerCapabilityRegistry {
|
||||
public static final PlayerCapabilityRegistry CAPABILITIES = new PlayerCapabilityRegistry();
|
||||
private final Map<ResourceLocation, CapabilityRecord<?>> capabilityRecordMap = new HashMap<>();
|
||||
|
||||
/**
|
||||
* Registering player capabilities through this method only applies to {@link net.minecraftforge.common.capabilities.RegisterCapabilitiesEvent}<br>
|
||||
* @param key The unique name of capability.
|
||||
* @param capabilityRecord Record is used to store various data of the capabilities that should be registered, refer to: {@link PlayerCapabilityRegistry.CapabilityRecord}
|
||||
*/
|
||||
public static void registerCapability(ResourceLocation key, CapabilityRecord<?> capabilityRecord) {
|
||||
CAPABILITIES.capabilityRecordMap.put(key, capabilityRecord);
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtain corresponding capability data through this method
|
||||
* @param key Obtain based on key
|
||||
* @return capability
|
||||
*/
|
||||
public static CapabilityRecord<?> getCapabilityRecord(ResourceLocation key){
|
||||
return CAPABILITIES.capabilityRecordMap.get(key);
|
||||
}
|
||||
|
||||
public static Map<ResourceLocation, CapabilityRecord<?>> getCapabilityMap(){
|
||||
return CAPABILITIES.capabilityRecordMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* Record the registration data of capability
|
||||
* @param aClass The instance that will ultimately be attached to the player. Should be an instance of ICapabilitySync
|
||||
* @param capability In general, it is not necessary to initialize it, default: <span>{@code CapabilityManager.get(new CapabilityToken<>(){})}</span>
|
||||
* @param interfaceClass The interface class corresponding to the instance, such as: ICapabilitySync.class.
|
||||
*/
|
||||
public record CapabilityRecord<T extends ICapabilitySync<? extends Player>>(Class<?> aClass, Capability<T> capability, Class<T> interfaceClass) { }
|
||||
}
|
||||
|
|
@ -1,114 +0,0 @@
|
|||
/*
|
||||
* Anim Core mod
|
||||
* Copyright (C) 2026 LeisureTimeDock
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package top.leisuretimedock.animationcore.capability.data.player;
|
||||
|
||||
import top.leisuretimedock.animationcore.capability.CapabilityUtils;
|
||||
import top.leisuretimedock.animationcore.capability.data.ICapabilitySync;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraftforge.event.TickEvent;
|
||||
import net.minecraftforge.event.entity.player.PlayerEvent;
|
||||
|
||||
public class PlayerCapabilityRemainder {
|
||||
/**
|
||||
* Players should transfer data to a new body when crossing dimensions or dying
|
||||
* @param event event
|
||||
*/
|
||||
public static void onPlayerClone(PlayerEvent.Clone event) {
|
||||
Player entity = event.getEntity();
|
||||
if(entity instanceof ServerPlayer newPlayer) {
|
||||
Player original = event.getOriginal();
|
||||
original.reviveCaps();
|
||||
PlayerCapabilityRegistry.getCapabilityMap().forEach((key, value) -> {
|
||||
ICapabilitySync<?> originData = CapabilityUtils.getCapability(original, key);
|
||||
ICapabilitySync<?> newData = CapabilityUtils.getCapability(newPlayer, key);
|
||||
if(originData != null && newData != null) {
|
||||
newData.copyFrom(originData, true);
|
||||
newData.sendToClient();
|
||||
}
|
||||
});
|
||||
original.invalidateCaps();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Players should update their capabilities when they are reborn
|
||||
* @param event event
|
||||
*/
|
||||
public static void onPlayerRespawn(PlayerEvent.PlayerRespawnEvent event) {
|
||||
if(event.getEntity() instanceof ServerPlayer newPlayer){
|
||||
PlayerCapabilityRegistry.getCapabilityMap().forEach((key, value) -> {
|
||||
ICapabilitySync<?> data = CapabilityUtils.getCapability(newPlayer, key);
|
||||
if(data == null) return;
|
||||
data.sendToClient(newPlayer);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Player start tracking an player event<br>
|
||||
* When other entities are loaded, the client requires the capabilities of the other party, and this event can be actively sent<br>
|
||||
* Will call{@link ICapabilitySync#sendToClient(ServerPlayer)}
|
||||
* @param event event
|
||||
*/
|
||||
public static void onEntityBeTracked(PlayerEvent.StartTracking event) {
|
||||
if (event.getTarget() instanceof Player target && event.getEntity() instanceof ServerPlayer attacker) {
|
||||
PlayerCapabilityRegistry.getCapabilityMap().forEach((key, value) -> {
|
||||
ICapabilitySync<?> data = CapabilityUtils.getCapability(target, key);
|
||||
if(data == null) return;
|
||||
data.sendToClient(attacker);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Player Tick Event<br>
|
||||
* If the capability is dirty, it will call {@link ICapabilitySync#sendToClient()} <br>
|
||||
* @param event event
|
||||
*/
|
||||
public static void capabilitySync(TickEvent.PlayerTickEvent event) {
|
||||
if(!event.player.level().isClientSide){
|
||||
PlayerCapabilityRegistry.getCapabilityMap().forEach((key, value) -> {
|
||||
ICapabilitySync<?> data = CapabilityUtils.getCapability(event.player, key);
|
||||
if(data == null) return;
|
||||
if(data.isDirty()) {
|
||||
data.setDirty(false);
|
||||
data.sendToClient();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Player login event <br>
|
||||
* Reinitialize the login player's capability <br>
|
||||
* @param event event
|
||||
*/
|
||||
public static void onPlayerLogin(PlayerEvent.PlayerLoggedInEvent event) {
|
||||
Player player = event.getEntity();
|
||||
if(!(player instanceof ServerPlayer serverPlayer)) return;
|
||||
PlayerCapabilityRegistry.getCapabilityMap().forEach((key, value) -> {
|
||||
ICapabilitySync<Player> data = CapabilityUtils.getPlayerCapability(player, key, null);
|
||||
if(data == null) return;
|
||||
if(data instanceof SimplePlayerCapabilitySync capabilitySync) {
|
||||
capabilitySync.setOwnerUUID(serverPlayer.getUUID());
|
||||
}
|
||||
data.attachInit(serverPlayer);
|
||||
data.setDirty(false);
|
||||
data.sendToClient();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -1,127 +0,0 @@
|
|||
/*
|
||||
* Anim Core mod
|
||||
* Copyright (C) 2026 LeisureTimeDock
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package top.leisuretimedock.animationcore.capability.data.player;
|
||||
|
||||
import top.leisuretimedock.animationcore.capability.data.ICapabilitySync;
|
||||
import top.leisuretimedock.animationcore.capability.data.entity.SimpleEntityCapabilitySync;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* It is recommended to manually add it during implementation: <br>
|
||||
* {@code key} ---- As the unique identifier of capability. <br>
|
||||
* {@code getCapability(Player player)} ---- Simplified method for obtaining capability<br>
|
||||
* 例:
|
||||
* <pre>
|
||||
* {@code
|
||||
* public static final ResourceLocation key =
|
||||
* new ResourceLocation(MyMod.MOD_ID, "my_data");
|
||||
* public static Optional<MyDataCapability> getCapability(Player player){
|
||||
* return Optional.ofNullable(CapabilityUtils.getPlayerCapability(
|
||||
* player, MyDataCapability.key, MyDataCapability.class
|
||||
* ));
|
||||
* }
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
*/
|
||||
public abstract class SimplePlayerCapabilitySync implements ICapabilitySync<Player> {
|
||||
public static final String OwnerUUID = "OwnerUUID";
|
||||
|
||||
private boolean dirty;
|
||||
private UUID ownerUUID;
|
||||
|
||||
@Override
|
||||
public boolean isDirty() {
|
||||
return dirty;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDirty(boolean dirty) {
|
||||
this.dirty = dirty;
|
||||
}
|
||||
|
||||
public UUID getOwnerUUID() {
|
||||
return ownerUUID;
|
||||
}
|
||||
|
||||
public void setOwnerUUID(UUID ownerUUID) {
|
||||
this.ownerUUID = ownerUUID;
|
||||
setDirty(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy data from parameter instance to current instance <br>
|
||||
*You shouldn't rewrite it, you should implement: {@link SimpleEntityCapabilitySync#copyFrom(ICapabilitySync)}
|
||||
* @param oldData old data
|
||||
* @param listenDone Whether to execute the completion method at the end: {@link ICapabilitySync#onCopyDone()}
|
||||
*/
|
||||
@Override
|
||||
public void copyFrom(ICapabilitySync<?> oldData, boolean listenDone) {
|
||||
SimplePlayerCapabilitySync data = (SimplePlayerCapabilitySync) oldData;
|
||||
this.setOwnerUUID(data.getOwnerUUID());
|
||||
copyFrom(data);
|
||||
ICapabilitySync.super.copyFrom(oldData, listenDone);
|
||||
}
|
||||
|
||||
/**
|
||||
* The method that will be executed when triggering data replication
|
||||
* @param oldData Copy from this data to the current instance
|
||||
*/
|
||||
public abstract void copyFrom(ICapabilitySync<?> oldData);
|
||||
|
||||
/**
|
||||
* Serialize to tag <br>
|
||||
* You shouldn't rewrite it, you should implement: {@link SimplePlayerCapabilitySync#toTag(CompoundTag)}
|
||||
* @return tag
|
||||
*/
|
||||
@Override
|
||||
public CompoundTag serializeNBT() {
|
||||
CompoundTag tag = new CompoundTag();
|
||||
if(ownerUUID != null) tag.putUUID(OwnerUUID, ownerUUID);
|
||||
tag = toTag(tag);
|
||||
return tag;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deserialize to instance object <br>
|
||||
* You don't need to rewrite it, you should implement: {@link SimplePlayerCapabilitySync#fromTag(CompoundTag)}
|
||||
* @param nbt nbt
|
||||
*/
|
||||
@Override
|
||||
public void deserializeNBT(CompoundTag nbt) {
|
||||
this.ownerUUID = null;
|
||||
if(nbt.contains(OwnerUUID)) this.ownerUUID = nbt.getUUID(OwnerUUID);
|
||||
fromTag(nbt);
|
||||
}
|
||||
|
||||
/**
|
||||
* In the serializeNBT method of SimpleElementCapability Sync, it will be called <br>
|
||||
* Actually equivalent to serializeNBT()
|
||||
* @param tag data tag
|
||||
* @return tag
|
||||
*/
|
||||
public abstract CompoundTag toTag(CompoundTag tag);
|
||||
|
||||
/**
|
||||
* In the deserializeNBT method of SimpleElementCapability Sync, it will be called <br>
|
||||
* Actually equivalent to deserializeNBT()
|
||||
* @param tag data tag
|
||||
*/
|
||||
public abstract void fromTag(CompoundTag tag);
|
||||
}
|
||||
|
|
@ -1,68 +0,0 @@
|
|||
/*
|
||||
* Anim Core mod
|
||||
* Copyright (C) 2026 LeisureTimeDock
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package top.leisuretimedock.animationcore.capability.network;
|
||||
|
||||
import net.minecraft.network.FriendlyByteBuf;
|
||||
import net.minecraftforge.network.NetworkDirection;
|
||||
import net.minecraftforge.network.NetworkEvent;
|
||||
import net.minecraftforge.network.simple.SimpleChannel;
|
||||
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
/**
|
||||
* Call createChannel in the Mod main class construction method logic. There are two ways:<br>
|
||||
* <pre>
|
||||
* 1. {@link top.leisuretimedock.animationcore.capability.CapabilityUtils#createChannel(SimpleChannel)}
|
||||
* If you do this, you must override all the sendToPlayer methods in the Capability class, and call your Channel in the override
|
||||
* </pre>
|
||||
* <pre>
|
||||
* 2. {@link top.leisuretimedock.animationcore.capability.CapabilityUtils#createChannel()}
|
||||
* If this is done, the network package will be registered with SCCore's Channel
|
||||
* </pre>
|
||||
* The added network packet must implement the ICapabilityPacket interface
|
||||
*/
|
||||
public class CapabilityChannel {
|
||||
private final SimpleChannel channel;
|
||||
|
||||
public CapabilityChannel(SimpleChannel channel) {
|
||||
this.channel = channel;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a network packet through this method and invite
|
||||
* @param clazz Network packet class
|
||||
* @param cid index
|
||||
* @param decoder decoder
|
||||
* @param encoder encoder
|
||||
* @param handler handler
|
||||
* @param <T> extend {@code ICapabilityPacket<?>}
|
||||
*/
|
||||
public <T extends ICapabilityPacket<?>> void register(
|
||||
Class<T> clazz,
|
||||
int cid,
|
||||
Function<FriendlyByteBuf, T> decoder,
|
||||
BiConsumer<T, FriendlyByteBuf> encoder,
|
||||
BiConsumer<T, Supplier<NetworkEvent.Context>> handler
|
||||
) {
|
||||
channel.messageBuilder(clazz, cid, NetworkDirection.PLAY_TO_CLIENT)
|
||||
.decoder(decoder)
|
||||
.encoder(encoder)
|
||||
.consumerMainThread(handler)
|
||||
.add();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,64 +0,0 @@
|
|||
/*
|
||||
* Anim Core mod
|
||||
* Copyright (C) 2026 LeisureTimeDock
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package top.leisuretimedock.animationcore.capability.network;
|
||||
|
||||
import top.leisuretimedock.animationcore.capability.data.ICapabilitySync;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.network.FriendlyByteBuf;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraftforge.network.NetworkEvent;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public interface ICapabilityPacket<T extends Entity> {
|
||||
/**
|
||||
* Decoding network packets
|
||||
* @param buf FriendlyByteBuf
|
||||
*/
|
||||
void encode(FriendlyByteBuf buf);
|
||||
|
||||
/**
|
||||
* Network packet processing events generally do not need to be rewritten, and the default behavior is sufficient for use
|
||||
* @param supplier supplier
|
||||
*/
|
||||
default void handle(Supplier<NetworkEvent.Context> supplier){
|
||||
NetworkEvent.Context context = supplier.get();
|
||||
context.enqueueWork(() -> handler(context));
|
||||
}
|
||||
|
||||
/**
|
||||
* Network packet processing events should be rewritten here
|
||||
* @param context NetworkEvent.Context
|
||||
*/
|
||||
void handler(NetworkEvent.Context context);
|
||||
|
||||
|
||||
/**
|
||||
* Get tag
|
||||
* @return tag
|
||||
*/
|
||||
CompoundTag getData();
|
||||
|
||||
/**
|
||||
* Convert tags to capability data in network packets and deserialize them directly by default
|
||||
* @param dataTag tag
|
||||
* @param data The data that should be written into the data
|
||||
*/
|
||||
default void syncData(CompoundTag dataTag, ICapabilitySync<?> data){
|
||||
if(data == null) return;
|
||||
data.deserializeNBT(dataTag);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,101 +0,0 @@
|
|||
/*
|
||||
* Anim Core mod
|
||||
* Copyright (C) 2026 LeisureTimeDock
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package top.leisuretimedock.animationcore.capability.network;
|
||||
|
||||
import top.leisuretimedock.animationcore.AnimationCore;
|
||||
import top.leisuretimedock.animationcore.capability.CapabilityUtils;
|
||||
import top.leisuretimedock.animationcore.capability.data.ICapabilitySync;
|
||||
import top.leisuretimedock.animationcore.capability.data.entity.SimpleEntityCapabilitySync;
|
||||
import top.leisuretimedock.animationcore.capability.data.player.SimplePlayerCapabilitySync;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.multiplayer.ClientLevel;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.network.FriendlyByteBuf;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraftforge.network.NetworkEvent;
|
||||
|
||||
public class SimpleCapabilityPacket<T extends Entity> implements ICapabilityPacket<T> {
|
||||
private final ResourceLocation key;
|
||||
private final CompoundTag data;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
* @param data data tag
|
||||
*/
|
||||
public SimpleCapabilityPacket(ResourceLocation key, CompoundTag data) {
|
||||
this.key = key;
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
public SimpleCapabilityPacket(ICapabilitySync<T> packet) {
|
||||
this.key = packet.getKey();
|
||||
this.data = packet.serializeNBT();
|
||||
}
|
||||
|
||||
/**
|
||||
* decoder
|
||||
* @param buf buf
|
||||
*/
|
||||
public SimpleCapabilityPacket(FriendlyByteBuf buf) {
|
||||
this.key = buf.readResourceLocation();
|
||||
this.data = buf.readNbt();
|
||||
}
|
||||
|
||||
/**
|
||||
* encoder
|
||||
* @param buf buf
|
||||
*/
|
||||
@Override
|
||||
public void encode(FriendlyByteBuf buf) {
|
||||
buf.writeResourceLocation(key);
|
||||
buf.writeNbt(data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Default network packet handle, generally sufficient for use
|
||||
* @param context NetworkEvent.Context
|
||||
*/
|
||||
@Override
|
||||
public void handler(NetworkEvent.Context context) {
|
||||
context.setPacketHandled(true);
|
||||
Minecraft instance = Minecraft.getInstance();
|
||||
ClientLevel level = instance.level;
|
||||
if (level == null) return;
|
||||
CompoundTag nbt = getData();
|
||||
Entity entity = null;
|
||||
if(nbt.contains(SimpleEntityCapabilitySync.Id)){
|
||||
entity = level.getEntity(nbt.getInt(SimpleEntityCapabilitySync.Id));
|
||||
}
|
||||
if(nbt.contains(SimplePlayerCapabilitySync.OwnerUUID)){
|
||||
entity = level.getPlayerByUUID(nbt.getUUID(SimplePlayerCapabilitySync.OwnerUUID));
|
||||
}
|
||||
if(entity == null) return;
|
||||
try {
|
||||
ICapabilitySync<?> data = CapabilityUtils.getCapability(entity, key);
|
||||
if(data == null) {
|
||||
AnimationCore.log.error("key {} not found when sync capability on entity {}", key, entity);
|
||||
return;
|
||||
}
|
||||
syncData(nbt, data);
|
||||
}catch (Exception ignored) {}
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompoundTag getData() {
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,50 +0,0 @@
|
|||
/*
|
||||
* Anim Core mod
|
||||
* Copyright (C) 2026 LeisureTimeDock
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package top.leisuretimedock.animationcore.core;
|
||||
|
||||
import java.util.concurrent.Callable;
|
||||
|
||||
@SuppressWarnings("UnusedReturnValue")
|
||||
public interface IModLazyRun {
|
||||
default boolean testLoadedAndRun(Runnable runnable){
|
||||
if(testCondition()) runnable.run();
|
||||
else return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
default void testLoadedAndRun(Runnable runnable, Runnable elseRun){
|
||||
if(testCondition()) runnable.run();
|
||||
else elseRun.run();
|
||||
}
|
||||
|
||||
default <T> T testLoadedAndCall(Callable<T> callable) {
|
||||
try {
|
||||
if(testCondition()) return callable.call();
|
||||
} catch (Exception ignored) {}
|
||||
return null;
|
||||
}
|
||||
|
||||
default <T> T testLoadedAndCall(Callable<T> callable, Callable<T> elseCall) {
|
||||
try {
|
||||
if(testCondition()) return callable.call();
|
||||
else return elseCall.call();
|
||||
}catch(Exception ignored) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
boolean testCondition();
|
||||
}
|
||||
|
|
@ -1,58 +0,0 @@
|
|||
/*
|
||||
* Anim Core mod
|
||||
* Copyright (C) 2026 LeisureTimeDock
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package top.leisuretimedock.animationcore.core;
|
||||
|
||||
import top.leisuretimedock.animationcore.AnimationCore;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraftforge.fml.ModList;
|
||||
import net.minecraftforge.network.NetworkRegistry;
|
||||
import net.minecraftforge.network.PacketDistributor;
|
||||
import net.minecraftforge.network.simple.SimpleChannel;
|
||||
|
||||
public class ModChannel {
|
||||
private static int cid = 0;
|
||||
private static final String PROTOCOL_VERSION = ModList.get()
|
||||
.getModContainerById(AnimationCore.MOD_ID)
|
||||
.map(c -> c.getModInfo().getVersion().toString())
|
||||
.orElse("unknown");
|
||||
public static final SimpleChannel INSTANCE = NetworkRegistry.newSimpleChannel(
|
||||
new ResourceLocation(AnimationCore.MOD_ID, AnimationCore.MOD_ID),
|
||||
() -> PROTOCOL_VERSION,
|
||||
PROTOCOL_VERSION::equals,
|
||||
PROTOCOL_VERSION::equals
|
||||
);
|
||||
|
||||
public static void register() {
|
||||
|
||||
}
|
||||
|
||||
public static int getAndAddCid() {
|
||||
return cid++;
|
||||
}
|
||||
|
||||
public static <MSG> void sendAllPlayer(MSG message){
|
||||
INSTANCE.send(PacketDistributor.ALL.noArg(), message);
|
||||
}
|
||||
|
||||
public static <MSG> void sendToPlayer(MSG message, ServerPlayer player){
|
||||
INSTANCE.send(PacketDistributor.PLAYER.with(() -> player), message);
|
||||
}
|
||||
|
||||
public static <MSG> void sendToServer(MSG message){
|
||||
INSTANCE.send(PacketDistributor.SERVER.noArg(), message);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,78 +0,0 @@
|
|||
/*
|
||||
* Anim Core mod
|
||||
* Copyright (C) 2026 LeisureTimeDock
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package top.leisuretimedock.animationcore.core;
|
||||
|
||||
import top.leisuretimedock.animationcore.AnimationCore;
|
||||
import top.leisuretimedock.animationcore.animation.register.AnimationCommands;
|
||||
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
|
||||
import net.minecraft.commands.CommandBuildContext;
|
||||
import net.minecraft.commands.CommandSourceStack;
|
||||
import net.minecraft.commands.synchronization.ArgumentTypeInfo;
|
||||
import net.minecraftforge.client.event.RegisterClientCommandsEvent;
|
||||
import net.minecraftforge.event.RegisterCommandsEvent;
|
||||
import net.minecraftforge.eventbus.api.IEventBus;
|
||||
import net.minecraftforge.registries.DeferredRegister;
|
||||
import net.minecraftforge.registries.ForgeRegistries;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import static net.minecraft.commands.Commands.literal;
|
||||
|
||||
public class ModCommands {
|
||||
static final Set<String> animationCommand = new HashSet<>(Set.of(AnimationCore.MOD_ID, "animationcore", "ac"));
|
||||
public static void addCommandAlias(String alias) {
|
||||
animationCommand.add(alias);
|
||||
}
|
||||
public static Set<String> getAnimationCommand() {
|
||||
return animationCommand;
|
||||
}
|
||||
|
||||
public static void registerCommands(IEventBus forgeBus, IEventBus modBus) {
|
||||
forgeBus.addListener(ModCommands::commonCommandRegister);
|
||||
forgeBus.addListener(ModCommands::clientCommandRegister);
|
||||
Arguments.register(modBus);
|
||||
}
|
||||
|
||||
|
||||
public static void commonCommandRegister(RegisterCommandsEvent event) {
|
||||
animationCommand.forEach(string -> {
|
||||
LiteralArgumentBuilder<CommandSourceStack> builder = literal(string);
|
||||
AnimationCommands.commonCommandRegister(builder);
|
||||
|
||||
event.getDispatcher().register(builder);
|
||||
});
|
||||
}
|
||||
|
||||
public static void clientCommandRegister(RegisterClientCommandsEvent event) {
|
||||
animationCommand.forEach(string -> {
|
||||
LiteralArgumentBuilder<CommandSourceStack> builder = literal(string);
|
||||
AnimationCommands.clientCommandRegister(builder);
|
||||
|
||||
event.getDispatcher().register(builder);
|
||||
});
|
||||
}
|
||||
|
||||
public static class Arguments {
|
||||
public static final DeferredRegister<ArgumentTypeInfo<?, ?>> REGISTRY = DeferredRegister.create(
|
||||
ForgeRegistries.Keys.COMMAND_ARGUMENT_TYPES, AnimationCore.MOD_ID
|
||||
);
|
||||
public static void register(IEventBus eventBus) {
|
||||
AnimationCommands.registerArguments(REGISTRY);
|
||||
REGISTRY.register(eventBus);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,44 +0,0 @@
|
|||
/*
|
||||
* Anim Core mod
|
||||
* Copyright (C) 2026 LeisureTimeDock
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package top.leisuretimedock.animationcore.core;
|
||||
|
||||
import net.minecraftforge.api.distmarker.Dist;
|
||||
import net.minecraftforge.eventbus.api.IEventBus;
|
||||
import net.minecraftforge.fml.ModList;
|
||||
import net.minecraftforge.fml.loading.FMLLoader;
|
||||
|
||||
public abstract class ModCompatRun implements IModLazyRun{
|
||||
private final String modId;
|
||||
public ModCompatRun(String modId) {
|
||||
this.modId = modId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean testCondition() {
|
||||
return ModList.get().isLoaded(modId);
|
||||
}
|
||||
|
||||
public void addCommonListener(IEventBus forgeBus, IEventBus modBus){}
|
||||
public void addClientListener(IEventBus forgeBus, IEventBus modBus){}
|
||||
public void testLoadedAndAddListener(IEventBus forgeBus, IEventBus modBus) {
|
||||
if(testCondition()){
|
||||
addCommonListener(forgeBus, modBus);
|
||||
if(FMLLoader.getDist() == Dist.CLIENT){
|
||||
addClientListener(forgeBus, modBus);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,87 +0,0 @@
|
|||
/*
|
||||
* Anim Core mod
|
||||
* Copyright (C) 2026 LeisureTimeDock
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package top.leisuretimedock.animationcore.core.configs;
|
||||
|
||||
import net.minecraftforge.common.ForgeConfigSpec;
|
||||
|
||||
public class ModConfigs {
|
||||
public enum ConfigName {
|
||||
inviteValidTime("inviteValidTime"),
|
||||
inviteValidDistance("inviteValidDistance"),
|
||||
inviteCooldown("inviteCooldown"),
|
||||
|
||||
applyValidTime("applyValidTime"),
|
||||
applyValidDistance("applyValidDistance"),
|
||||
applyCooldown("applyCooldown"),
|
||||
|
||||
requestValidTime("requestValidTime"),
|
||||
requestCooldown("requestCooldown"),
|
||||
;
|
||||
|
||||
private final String name;
|
||||
ConfigName(String name){
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
}
|
||||
public static class Server {
|
||||
public static final ForgeConfigSpec.Builder BUILDER = new ForgeConfigSpec.Builder();
|
||||
public static final ForgeConfigSpec SPEC;
|
||||
|
||||
//invite
|
||||
public static final ForgeConfigSpec.ConfigValue<Integer> inviteValidTime;
|
||||
public static final ForgeConfigSpec.ConfigValue<Integer> inviteValidDistance;
|
||||
public static final ForgeConfigSpec.ConfigValue<Integer> inviteCooldown;
|
||||
//apply
|
||||
public static final ForgeConfigSpec.ConfigValue<Integer> applyValidTime;
|
||||
public static final ForgeConfigSpec.ConfigValue<Integer> applyValidDistance;
|
||||
public static final ForgeConfigSpec.ConfigValue<Integer> applyCooldown;
|
||||
//request
|
||||
public static final ForgeConfigSpec.ConfigValue<Integer> requestValidTime;
|
||||
public static final ForgeConfigSpec.ConfigValue<Integer> requestCooldown;
|
||||
|
||||
static {
|
||||
BUILDER.push("Animation");
|
||||
//invite
|
||||
inviteValidTime = BUILDER.comment("Animation invite valid time. Ignore when zero. (seconds)")
|
||||
.defineInRange(ConfigName.inviteValidTime.name, 120, 0, Integer.MAX_VALUE);
|
||||
inviteValidDistance = BUILDER.comment("Animation invite max distance. Ignore when zero. (blocks)")
|
||||
.defineInRange(ConfigName.inviteValidDistance.name, 6, 0, Integer.MAX_VALUE);
|
||||
inviteCooldown = BUILDER.comment("Animation invite cooldown. (seconds)")
|
||||
.defineInRange(ConfigName.inviteCooldown.name, 60, 0, Integer.MAX_VALUE);
|
||||
|
||||
//apply
|
||||
applyValidTime = BUILDER.comment("Animation apply valid time. Ignore when zero. (seconds)")
|
||||
.defineInRange(ConfigName.applyValidTime.name, 120, 0, Integer.MAX_VALUE);
|
||||
applyValidDistance = BUILDER.comment("Animation apply max distance. Ignore when zero. (blocks)")
|
||||
.defineInRange(ConfigName.applyValidDistance.name, 6, 0, Integer.MAX_VALUE);
|
||||
applyCooldown = BUILDER.comment("Animation apply cooldown. (seconds)")
|
||||
.defineInRange(ConfigName.applyCooldown.name, 60, 0, Integer.MAX_VALUE);
|
||||
|
||||
//request
|
||||
requestValidTime = BUILDER.comment("Animation request valid time. Ignore when zero (seconds)")
|
||||
.defineInRange(ConfigName.requestValidTime.name, 120, 0, Integer.MAX_VALUE);
|
||||
requestCooldown = BUILDER.comment("Animation request cooldown. (seconds)")
|
||||
.defineInRange(ConfigName.requestCooldown.name, 60, 0, Integer.MAX_VALUE);
|
||||
|
||||
BUILDER.pop();
|
||||
SPEC = BUILDER.build();
|
||||
}
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user