From 1a56faad9f114b7f8b9b0ff09b781e12599d826e Mon Sep 17 00:00:00 2001 From: 3944Realms Date: Sun, 14 Sep 2025 00:50:35 +0800 Subject: [PATCH] =?UTF-8?q?feature:=201.=E6=B7=BB=E5=8A=A0=E4=BA=86?= =?UTF-8?q?=E6=8B=B4=E7=BB=B3=E7=9B=B8=E5=85=B3=E5=B1=9E=E6=80=A7=E7=9A=84?= =?UTF-8?q?=E9=85=8D=E7=BD=AE=202.=E5=88=9B=E5=BB=BAILeashState=E5=AE=9E?= =?UTF-8?q?=E7=8E=B0=E8=87=AA=E5=AE=9A=E4=B9=89=E6=8B=B4=E7=BB=B3=E6=B8=B2?= =?UTF-8?q?=E6=9F=93=E4=BD=8D=E7=BD=AE=EF=BC=88=E5=BE=85=E5=85=B3=E8=81=94?= =?UTF-8?q?=EF=BC=89=20refactor:=201.=E8=B0=83=E6=95=B4=E5=92=8C=E4=BC=98?= =?UTF-8?q?=E5=8C=96=E4=B8=A4Cap=E7=9A=84Nbt=E5=BA=8F=E5=88=97=E5=8C=96?= =?UTF-8?q?=E5=AE=9E=E7=8E=B0=20todo:1.=E6=B8=B2=E6=9F=93=E5=99=A8?= =?UTF-8?q?=E7=9B=B8=E5=85=B3=E4=BB=A3=E7=A0=81=E5=B9=B6=E6=9C=AA=E6=B8=85?= =?UTF-8?q?=E9=99=A4=EF=BC=88=E6=9A=82=E6=97=B6=EF=BC=89=202.=E6=B0=B8?= =?UTF-8?q?=E6=81=92=E5=9C=9F=E8=B1=86=E5=BE=85=E5=88=86=E7=A6=BB=E9=80=BB?= =?UTF-8?q?=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../1de3d2ee724999f84a11b20b51c37030049be277 | 4 +- .../2a65ee2815744be1ef1ffdae1c9a37f2a9cbe2ac | 4 +- .../82018c5420b46ddbb7071e62df09fdecd98133e6 | 4 +- .../c622617f6fabf890a00b9275cd5f643584a8a2c8 | 4 +- .../assets/superleadrope/lang/en_us.json | 7 +- .../assets/superleadrope/lang/lzh.json | 7 +- .../assets/superleadrope/lang/zh_cn.json | 7 +- .../assets/superleadrope/lang/zh_tw.json | 7 +- .../superleadrope/CommonEventHandler.java | 44 +- .../superleadrope/SuperLeadRope.java | 12 + .../client/renderer/LeashRenderHandler.java | 12 +- .../resolver/SuperLeashStateResolver.java | 41 +- .../config/LeashCommonConfig.java | 63 +- .../content/capability/CapabilityHandler.java | 11 +- .../capability/CapabilityRemainder.java | 17 +- .../capability/impi/LeashDataImpl.java | 557 +++++++++++------- .../capability/impi/LeashStateImpl.java | 315 ++++++++++ .../content/capability/inter/ILeashData.java | 213 +++++++ .../inter/ILeashDataCapability.java | 184 ------ .../content/capability/inter/ILeashState.java | 111 ++++ .../provider/LeashDataProvider.java | 6 +- .../provider/LeashStateProvider.java | 54 ++ .../content/command/Command.java | 37 ++ .../content/command/LeashDataCommand.java | 28 + .../content/command/LeashStateCommand.java | 29 + .../content/command/MotionCommand.java | 173 ++++++ .../content/entity/SuperLeashKnotEntity.java | 33 +- .../content/gamerule/SLPGamerules.java | 3 +- ....java => TeleportWithLeashedEntities.java} | 8 +- .../content/item/SuperLeadRopeItem.java | 92 +-- .../core/leash/LeashInteractHandler.java | 28 +- .../core/leash/LeashSyncManager.java | 34 +- .../core/register/SLPGameruleRegistry.java | 4 +- .../datagen/data/SLPLangKeyValue.java | 36 +- .../superleadrope/network/NetworkHandler.java | 17 +- .../toClient/LeashStateSyncPacket.java | 56 ++ .../util/capability/LeashUtil.java | 36 ++ .../superleadrope/util/nbt/NBTReader.java | 33 ++ .../superleadrope/util/nbt/NBTWriter.java | 31 + .../util/riding/RidingValidator.java | 28 +- 40 files changed, 1834 insertions(+), 556 deletions(-) create mode 100644 src/main/java/top/r3944realms/superleadrope/content/capability/impi/LeashStateImpl.java create mode 100644 src/main/java/top/r3944realms/superleadrope/content/capability/inter/ILeashData.java delete mode 100644 src/main/java/top/r3944realms/superleadrope/content/capability/inter/ILeashDataCapability.java create mode 100644 src/main/java/top/r3944realms/superleadrope/content/capability/inter/ILeashState.java create mode 100644 src/main/java/top/r3944realms/superleadrope/content/capability/provider/LeashStateProvider.java create mode 100644 src/main/java/top/r3944realms/superleadrope/content/command/Command.java create mode 100644 src/main/java/top/r3944realms/superleadrope/content/command/LeashDataCommand.java create mode 100644 src/main/java/top/r3944realms/superleadrope/content/command/LeashStateCommand.java create mode 100644 src/main/java/top/r3944realms/superleadrope/content/command/MotionCommand.java rename src/main/java/top/r3944realms/superleadrope/content/gamerule/server/{TeleportWithLeashedPlayers.java => TeleportWithLeashedEntities.java} (89%) create mode 100644 src/main/java/top/r3944realms/superleadrope/network/toClient/LeashStateSyncPacket.java create mode 100644 src/main/java/top/r3944realms/superleadrope/util/capability/LeashUtil.java create mode 100644 src/main/java/top/r3944realms/superleadrope/util/nbt/NBTReader.java create mode 100644 src/main/java/top/r3944realms/superleadrope/util/nbt/NBTWriter.java diff --git a/src/generated/resources/.cache/1de3d2ee724999f84a11b20b51c37030049be277 b/src/generated/resources/.cache/1de3d2ee724999f84a11b20b51c37030049be277 index e5d1426..19e0445 100644 --- a/src/generated/resources/.cache/1de3d2ee724999f84a11b20b51c37030049be277 +++ b/src/generated/resources/.cache/1de3d2ee724999f84a11b20b51c37030049be277 @@ -1,2 +1,2 @@ -// 1.20.1 2025-09-08T23:59:30.214676 Languages: zh_tw -01bf653cea7c3be88445a248753b4cc75e5ac45e assets/superleadrope/lang/zh_tw.json +// 1.20.1 2025-09-11T20:29:48.0546389 Languages: zh_tw +852af450b5a31ef7436f17cb9e0d92d282e6831a assets/superleadrope/lang/zh_tw.json diff --git a/src/generated/resources/.cache/2a65ee2815744be1ef1ffdae1c9a37f2a9cbe2ac b/src/generated/resources/.cache/2a65ee2815744be1ef1ffdae1c9a37f2a9cbe2ac index 10254a5..ab1a373 100644 --- a/src/generated/resources/.cache/2a65ee2815744be1ef1ffdae1c9a37f2a9cbe2ac +++ b/src/generated/resources/.cache/2a65ee2815744be1ef1ffdae1c9a37f2a9cbe2ac @@ -1,2 +1,2 @@ -// 1.20.1 2025-09-08T23:59:30.214676 Languages: zh_cn -6c4c5affb9dae3253eae9155ee4fb58deb5c4b92 assets/superleadrope/lang/zh_cn.json +// 1.20.1 2025-09-11T20:29:48.04512 Languages: zh_cn +78b1f4779544658700e3bf6e8f5f53ef15db7531 assets/superleadrope/lang/zh_cn.json diff --git a/src/generated/resources/.cache/82018c5420b46ddbb7071e62df09fdecd98133e6 b/src/generated/resources/.cache/82018c5420b46ddbb7071e62df09fdecd98133e6 index 85ca492..a9055fd 100644 --- a/src/generated/resources/.cache/82018c5420b46ddbb7071e62df09fdecd98133e6 +++ b/src/generated/resources/.cache/82018c5420b46ddbb7071e62df09fdecd98133e6 @@ -1,2 +1,2 @@ -// 1.20.1 2025-09-08T23:59:30.214676 Languages: lzh -e7d897e78d2a100c249948780b7f1c07ad94e9b9 assets/superleadrope/lang/lzh.json +// 1.20.1 2025-09-11T20:29:48.0526255 Languages: lzh +f20f2b37e9d7bfc332b5c579573e6de9f4504996 assets/superleadrope/lang/lzh.json diff --git a/src/generated/resources/.cache/c622617f6fabf890a00b9275cd5f643584a8a2c8 b/src/generated/resources/.cache/c622617f6fabf890a00b9275cd5f643584a8a2c8 index 16bf93b..bcdfc8f 100644 --- a/src/generated/resources/.cache/c622617f6fabf890a00b9275cd5f643584a8a2c8 +++ b/src/generated/resources/.cache/c622617f6fabf890a00b9275cd5f643584a8a2c8 @@ -1,2 +1,2 @@ -// 1.20.1 2025-09-08T23:59:30.214676 Languages: en_us -9055e381f25bb65f46b6c4489800326812fd3659 assets/superleadrope/lang/en_us.json +// 1.20.1 2025-09-11T20:29:48.0501199 Languages: en_us +d0fa9a0b01c9a2057af32d116fc8fb4e63192fa8 assets/superleadrope/lang/en_us.json diff --git a/src/generated/resources/assets/superleadrope/lang/en_us.json b/src/generated/resources/assets/superleadrope/lang/en_us.json index 04bd09f..a8013bc 100644 --- a/src/generated/resources/assets/superleadrope/lang/en_us.json +++ b/src/generated/resources/assets/superleadrope/lang/en_us.json @@ -2,6 +2,8 @@ "death.attack.eternal_potato_not_complete": "§c%1$s was not the rightful owner, struck by lightning!", "death.attack.eternal_potato_not_owner": "§c%1$s was not the rightful owner, struck by lightning!", "entity.superleadrope.super_lead_knot": "Super Lead Knot", + "gamerule.slp.TeleportWithLeashedEntities": "Teleport leashed player with player holder", + "gamerule.slp.TeleportWithLeashedEntities.description": "Holder will teleport with their leashed players ", "item.eternal_potato.msg.bind_msg": "§6Bound to you as the server-wide shared person.", "item.eternal_potato.msg.cannot_drop": "§cThe Eternal Potato cannot be dropped! +%d punishments.", "item.eternal_potato.msg.obligation_countdown": "Punish Countdown: §a%d §fseconds remaining", @@ -22,5 +24,8 @@ "item.superleadrope.super_lead_rope": "Super Lead Rope", "sound.superleadrope.subtitle.lead_break": "Lead Break", "sound.superleadrope.subtitle.lead_tied": "Lead Tied", - "sound.superleadrope.subtitle.lead_untied": "Lead Untie" + "sound.superleadrope.subtitle.lead_untied": "Lead Untie", + "superleadrope.command.motion.message.adder.successful": "§bAdd Successfully.§a%s§7:§f[§eVec§7:§a(§f%.2f§7,§f%.2f§7,§f%.2f§7)§f]§r", + "superleadrope.command.motion.message.multiply.successful": "§bMultiply Successfully.§a%s§7:§f[§eVec§7:§a(§f%.2f§7,§f%.2f§7,§f%.2f§7)§f]§r", + "superleadrope.command.motion.message.setter.successful": "§bSet Successfully.§a%s§7:§f[§eVec§7:§a(§f%.2f§7,§f%.2f§7,§f%.2f§7)§f]§r" } \ No newline at end of file diff --git a/src/generated/resources/assets/superleadrope/lang/lzh.json b/src/generated/resources/assets/superleadrope/lang/lzh.json index c02cf9b..0434dfb 100644 --- a/src/generated/resources/assets/superleadrope/lang/lzh.json +++ b/src/generated/resources/assets/superleadrope/lang/lzh.json @@ -2,6 +2,8 @@ "death.attack.eternal_potato_not_complete": "§c%1$s 非汝所主,雷霆降身!", "death.attack.eternal_potato_not_owner": "§c%1$s 非汝所主,雷霆降身!", "entity.superleadrope.super_lead_knot": "神駒羈縻索結", + "gamerule.slp.TeleportWithLeashedEntities": "繫畜隨持者傳送", + "gamerule.slp.TeleportWithLeashedEntities.description": "傳送時繫畜隨持者同傳", "item.eternal_potato.msg.bind_msg": "§6已与汝绑定,为全服共享之人。", "item.eternal_potato.msg.cannot_drop": "§c永恒土豆不可丟棄,懲罰數增加%d!", "item.eternal_potato.msg.obligation_countdown": "受罚倒数:§a%d §f瞬", @@ -22,5 +24,8 @@ "item.superleadrope.super_lead_rope": "神駒羈縻索", "sound.superleadrope.subtitle.lead_break": "索絕", "sound.superleadrope.subtitle.lead_tied": "繫索", - "sound.superleadrope.subtitle.lead_untied": "解索" + "sound.superleadrope.subtitle.lead_untied": "解索", + "superleadrope.command.motion.message.adder.successful": "§b增益既成.§a%s§7:§f[§e速勢§7:(§a%.2f§7,§a%.2f§7,§a%.2f§7)§f]§r", + "superleadrope.command.motion.message.multiply.successful": "§b倍乘既成.§a%s§7:§f[§e速勢§7:(§a%.2f§7,§a%.2f§7,§a%.2f§7)§f]§r", + "superleadrope.command.motion.message.setter.successful": "§b定值既成.§a%s§7:§f[§e速勢§7:(§a%.2f§7,§a%.2f§7,§a%.2f§7)§f]§r" } \ No newline at end of file diff --git a/src/generated/resources/assets/superleadrope/lang/zh_cn.json b/src/generated/resources/assets/superleadrope/lang/zh_cn.json index 3ccd6d5..17c2f34 100644 --- a/src/generated/resources/assets/superleadrope/lang/zh_cn.json +++ b/src/generated/resources/assets/superleadrope/lang/zh_cn.json @@ -2,6 +2,8 @@ "death.attack.eternal_potato_not_complete": "§c%1$s 因使用非自己绑定物品,受到闪电惩罚!", "death.attack.eternal_potato_not_owner": "§c%1$s 因使用非自己绑定物品,受到闪电惩罚!", "entity.superleadrope.super_lead_knot": "超级拴绳结", + "gamerule.slp.TeleportWithLeashedEntities": "被拴实体随玩家持有者传送", + "gamerule.slp.TeleportWithLeashedEntities.description": "传送时将被拴实体与持有者一起传送", "item.eternal_potato.msg.bind_msg": "§6已与你绑定,成为全服共有之人。", "item.eternal_potato.msg.cannot_drop": "§c永恒土豆是不可丢弃的,惩罚数加%d!", "item.eternal_potato.msg.obligation_countdown": "惩罚倒计时: §a%d §f秒", @@ -22,5 +24,8 @@ "item.superleadrope.super_lead_rope": "超级拴绳", "sound.superleadrope.subtitle.lead_break": "拴绳断裂", "sound.superleadrope.subtitle.lead_tied": "拴绳系上", - "sound.superleadrope.subtitle.lead_untied": "拴绳解开" + "sound.superleadrope.subtitle.lead_untied": "拴绳解开", + "superleadrope.command.motion.message.adder.successful": "§b添加成功.§a%s§7:§f[§e加速§7:(§a%.2f§7,§a%.2f§7,§a%.2f§7)§f]§r", + "superleadrope.command.motion.message.multiply.successful": "§b倍乘成功.§a%s§7:§f[§e加速§7:(§a%.2f§7,§a%.2f§7,§a%.2f§7)§f]§r", + "superleadrope.command.motion.message.setter.successful": "§b设置成功.§a%s§7:§f[§e加速§7:(§a%.2f§7,§a%.2f§7,§a%.2f§7)§f]§r" } \ No newline at end of file diff --git a/src/generated/resources/assets/superleadrope/lang/zh_tw.json b/src/generated/resources/assets/superleadrope/lang/zh_tw.json index d1fc2cc..e53ee53 100644 --- a/src/generated/resources/assets/superleadrope/lang/zh_tw.json +++ b/src/generated/resources/assets/superleadrope/lang/zh_tw.json @@ -2,6 +2,8 @@ "death.attack.eternal_potato_not_complete": "§c%1$s 因使用非自己綁定物品,受到閃電懲罰!", "death.attack.eternal_potato_not_owner": "§c%1$s 因使用非自己綁定物品,受到閃電懲罰!", "entity.superleadrope.super_lead_knot": "超級拴繩結", + "gamerule.slp.TeleportWithLeashedEntities": "被拴实体随玩家持有者傳送", + "gamerule.slp.TeleportWithLeashedEntities.description": "將被拴实体將隨持有者一起傳送", "item.eternal_potato.msg.bind_msg": "§6已與你綁定,成為全服共有之人。", "item.eternal_potato.msg.cannot_drop": "§c永恆土豆不可丟棄,懲罰數加%d!", "item.eternal_potato.msg.obligation_countdown": "懲罰倒計時: §a%d §f秒", @@ -22,5 +24,8 @@ "item.superleadrope.super_lead_rope": "超級拴繩", "sound.superleadrope.subtitle.lead_break": "拴繩斷裂", "sound.superleadrope.subtitle.lead_tied": "拴繩係上", - "sound.superleadrope.subtitle.lead_untied": "拴繩解開" + "sound.superleadrope.subtitle.lead_untied": "拴繩解開", + "superleadrope.command.motion.message.adder.successful": "§b添加成功.§a%s§7:§f[§e加速§7:(§a%.2f§7,§a%.2f§7,§a%.2f§7)§f]§r", + "superleadrope.command.motion.message.multiply.successful": "§b倍乘成功.§a%s§7:§f[§e加速§7:(§a%.2f§7,§a%.2f§7,§a%.2f§7)§f]§r", + "superleadrope.command.motion.message.setter.successful": "§b設置成功.§a%s§7:§f[§e加速§7:(§a%.2f§7,§a%.2f§7,§a%.2f§7)§f]§r" } \ No newline at end of file diff --git a/src/main/java/top/r3944realms/superleadrope/CommonEventHandler.java b/src/main/java/top/r3944realms/superleadrope/CommonEventHandler.java index a4c9959..da71602 100644 --- a/src/main/java/top/r3944realms/superleadrope/CommonEventHandler.java +++ b/src/main/java/top/r3944realms/superleadrope/CommonEventHandler.java @@ -15,6 +15,8 @@ package top.r3944realms.superleadrope; +import com.mojang.brigadier.CommandDispatcher; +import net.minecraft.commands.CommandSourceStack; import net.minecraft.core.BlockPos; import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerPlayer; @@ -35,6 +37,7 @@ import net.minecraft.world.phys.Vec3; import net.minecraftforge.common.capabilities.RegisterCapabilitiesEvent; import net.minecraftforge.event.AttachCapabilitiesEvent; import net.minecraftforge.event.BuildCreativeModeTabContentsEvent; +import net.minecraftforge.event.RegisterCommandsEvent; import net.minecraftforge.event.TickEvent; import net.minecraftforge.event.entity.EntityJoinLevelEvent; import net.minecraftforge.event.entity.EntityLeaveLevelEvent; @@ -53,9 +56,11 @@ import top.r3944realms.superleadrope.content.capability.CapabilityHandler; import top.r3944realms.superleadrope.content.capability.CapabilityRemainder; import top.r3944realms.superleadrope.content.capability.impi.LeashDataImpl; import top.r3944realms.superleadrope.content.capability.inter.IEternalPotato; -import top.r3944realms.superleadrope.content.capability.inter.ILeashDataCapability; +import top.r3944realms.superleadrope.content.capability.inter.ILeashData; +import top.r3944realms.superleadrope.content.capability.inter.ILeashState; +import top.r3944realms.superleadrope.content.command.MotionCommand; import top.r3944realms.superleadrope.content.entity.SuperLeashKnotEntity; -import top.r3944realms.superleadrope.content.gamerule.server.TeleportWithLeashedPlayers; +import top.r3944realms.superleadrope.content.gamerule.server.TeleportWithLeashedEntities; import top.r3944realms.superleadrope.content.item.EternalPotatoItem; import top.r3944realms.superleadrope.content.item.SuperLeadRopeItem; import top.r3944realms.superleadrope.core.leash.LeashInteractHandler; @@ -86,9 +91,10 @@ public class CommonEventHandler { Entity entity = event.getEntity(); if (entity.level().isClientSide) return; if (entity instanceof LivingEntity || entity instanceof Boat || entity instanceof Minecart) { - entity.getCapability(CapabilityHandler.LEASH_DATA_CAP).ifPresent(LeashSyncManager::track); + entity.getCapability(CapabilityHandler.LEASH_DATA_CAP).ifPresent(LeashSyncManager.Data::track); + entity.getCapability(CapabilityHandler.LEASH_STATE_CAP).ifPresent(LeashSyncManager.State::track); if (entity instanceof ServerPlayer serverPlayer) { - LeashSyncManager.forEach(i -> { + LeashSyncManager.Data.forEach(i -> { if (i.isLeashedBy(serverPlayer) && i.isInDelayedLeash(serverPlayer.getUUID())) { i.removeDelayedLeash(serverPlayer.getUUID());//重新加入去除延迟 } @@ -103,13 +109,14 @@ public class CommonEventHandler { if (entity.level().isClientSide) return; if (entity instanceof LivingEntity || entity instanceof Boat || entity instanceof Minecart) { if (entity instanceof ServerPlayer serverPlayer) { - LeashSyncManager.forEach(i -> { + LeashSyncManager.Data.forEach(i -> { if(i.isLeashedBy(serverPlayer)) { i.addDelayedLeash(serverPlayer); //添加延迟 } }); } - entity.getCapability(CapabilityHandler.LEASH_DATA_CAP).ifPresent(LeashSyncManager::untrack); + entity.getCapability(CapabilityHandler.LEASH_DATA_CAP).ifPresent(LeashSyncManager.Data::untrack); + entity.getCapability(CapabilityHandler.LEASH_STATE_CAP).ifPresent(LeashSyncManager.State::untrack); } } @SubscribeEvent @@ -142,7 +149,7 @@ public class CommonEventHandler { if (SuperLeashKnotEntity.isSupportBlock(blockState)) { boolean shouldConsume = SuperLeadRopeItem.bindToBlock(player, level, blockPos, event.getItemStack(), itemInHand.is(SLPItems.SUPER_LEAD_ROPE.get())); if (shouldConsume) { - event.setCancellationResult(InteractionResult.CONSUME); + event.setCancellationResult(InteractionResult.SUCCESS); event.setCanceled(true); } } @@ -248,7 +255,7 @@ public class CommonEventHandler { // 获取范围内可被拴住实体 List entities = LeashDataImpl.leashableInArea(telEntity); //规则关闭则禁止 - if(!SLPGameruleRegistry.getGameruleBoolValue(event.getEntity().level(), TeleportWithLeashedPlayers.ID)) { + if(!SLPGameruleRegistry.getGameruleBoolValue(event.getEntity().level(), TeleportWithLeashedEntities.ID)) { entities.forEach(i -> i.getCapability(CapabilityHandler.LEASH_DATA_CAP).ifPresent(j -> j.removeLeash(i))); return; } @@ -260,7 +267,7 @@ public class CommonEventHandler { float originalPitch = beLeashedEntity.getXRot(); Vec3 originalDeltaMovement = beLeashedEntity.getDeltaMovement(); - AtomicReference originalLeashInfo = new AtomicReference<>(); + AtomicReference originalLeashInfo = new AtomicReference<>(); beLeashedEntity.getCapability(CapabilityHandler.LEASH_DATA_CAP).ifPresent(cap -> { originalLeashInfo.set(cap.getLeashInfo(telEntity).orElse(null)); cap.removeLeash(telEntity); @@ -296,9 +303,9 @@ public class CommonEventHandler { } // --- 恢复拴绳 --- - ILeashDataCapability.LeashInfo leashInfo = Optional.ofNullable(originalLeashInfo.get()) + ILeashData.LeashInfo leashInfo = Optional.ofNullable(originalLeashInfo.get()) .map(info -> info.transferHolder(telEntity)) - .orElse(ILeashDataCapability.LeashInfo.EMPTY); + .orElse(ILeashData.LeashInfo.EMPTY); beLeashedEntity.getCapability(CapabilityHandler.LEASH_DATA_CAP).ifPresent( cap -> cap.addLeash(telEntity, leashInfo) @@ -321,14 +328,16 @@ public class CommonEventHandler { // 每10 tick标记为脏(needsSync) if (tickCounter % 10 == 0) { - LeashSyncManager.forEach(ILeashDataCapability::markForSync); + LeashSyncManager.Data.forEach(ILeashData::markForSync); + LeashSyncManager.State.forEach(ILeashState::markForSync); } // 定期同步检查 - LeashSyncManager.forEach(ILeashDataCapability::checkSync); + LeashSyncManager.Data.forEach(ILeashData::checkSync); + LeashSyncManager.State.forEach(ILeashState::checkSync); // 应用物理拉力/效果 - LeashSyncManager.forEach(ILeashDataCapability::applyLeashForces); + LeashSyncManager.Data.forEach(ILeashData::applyLeashForces); } } @SubscribeEvent @@ -344,6 +353,11 @@ public class CommonEventHandler { public static void attachCapability(AttachCapabilitiesEvent event) { CapabilityHandler.attachCapability(event); } + @SubscribeEvent + public static void onRegisterCommand (RegisterCommandsEvent event) { + CommandDispatcher dispatcher = event.getDispatcher(); + MotionCommand.register(dispatcher); + } } @net.minecraftforge.fml.common.Mod.EventBusSubscriber(modid = SuperLeadRope.MOD_ID, bus= net.minecraftforge.fml.common.Mod.EventBusSubscriber.Bus.MOD) public static class Mod { @@ -363,4 +377,4 @@ public class CommonEventHandler { } } -} +} \ No newline at end of file diff --git a/src/main/java/top/r3944realms/superleadrope/SuperLeadRope.java b/src/main/java/top/r3944realms/superleadrope/SuperLeadRope.java index 5b9f8cc..72eddf6 100644 --- a/src/main/java/top/r3944realms/superleadrope/SuperLeadRope.java +++ b/src/main/java/top/r3944realms/superleadrope/SuperLeadRope.java @@ -16,6 +16,7 @@ package top.r3944realms.superleadrope; import net.minecraftforge.eventbus.api.IEventBus; +import net.minecraftforge.fml.ModList; import net.minecraftforge.fml.ModLoadingContext; import net.minecraftforge.fml.common.Mod; import net.minecraftforge.fml.config.ModConfig; @@ -48,4 +49,15 @@ public class SuperLeadRope { ModLoadingContext modLoadingContext = ModLoadingContext.get(); ConfigUtil.registerConfig(modLoadingContext, ModConfig.Type.COMMON, LeashCommonConfig.SPEC, c, "leash"); } + + public static class ModInfo { + public static final String VERSION; + static { + // 从 ModList 获取当前 ModContainer 的元数据 + VERSION = ModList.get() + .getModContainerById(MOD_ID) + .map(c -> c.getModInfo().getVersion().toString()) + .orElse("UNKNOWN"); + } + } } diff --git a/src/main/java/top/r3944realms/superleadrope/client/renderer/LeashRenderHandler.java b/src/main/java/top/r3944realms/superleadrope/client/renderer/LeashRenderHandler.java index eefaf27..22a692a 100644 --- a/src/main/java/top/r3944realms/superleadrope/client/renderer/LeashRenderHandler.java +++ b/src/main/java/top/r3944realms/superleadrope/client/renderer/LeashRenderHandler.java @@ -25,7 +25,7 @@ import net.minecraft.world.level.Level; import top.r3944realms.superleadrope.SuperLeadRope; import top.r3944realms.superleadrope.client.renderer.resolver.SuperLeashStateResolver; import top.r3944realms.superleadrope.content.capability.CapabilityHandler; -import top.r3944realms.superleadrope.content.capability.inter.ILeashDataCapability; +import top.r3944realms.superleadrope.content.capability.inter.ILeashData; import top.r3944realms.superleadrope.content.entity.SuperLeashKnotEntity; import java.util.Optional; @@ -49,15 +49,15 @@ public class LeashRenderHandler { cameraEntity.getBoundingBox().inflate(50))) { entity.getCapability(CapabilityHandler.LEASH_DATA_CAP).ifPresent(leashData -> { - if(leashData instanceof ILeashDataCapability) {} - for (ILeashDataCapability.LeashInfo leashInfo : leashData.getAllLeashes()) { + if(leashData instanceof ILeashData) {} + for (ILeashData.LeashInfo leashInfo : leashData.getAllLeashes()) { renderLeashFromInfo(entity, leashInfo, poseStack, bufferSource, partialTick); } }); } } - private static void renderLeashFromInfo(Entity entity, ILeashDataCapability.LeashInfo leashInfo, + private static void renderLeashFromInfo(Entity entity, ILeashData.LeashInfo leashInfo, PoseStack poseStack, MultiBufferSource bufferSource, float partialTick) { try { @@ -67,7 +67,7 @@ public class LeashRenderHandler { Entity holder = holderOpt.get(); // 构建渲染状态 - SuperLeashStateResolver.resolve(entity, holder, leashInfo, partialTick).ifPresent( + SuperLeashStateResolver.resolve(holder, entity, leashInfo, partialTick).ifPresent( leashRenderState -> SuperLeashRenderer.renderLeash(leashRenderState, poseStack, bufferSource) ); @@ -77,7 +77,7 @@ public class LeashRenderHandler { } } - private static Optional getHolderFromLeashInfo(Level level, ILeashDataCapability.LeashInfo leashInfo) { + private static Optional getHolderFromLeashInfo(Level level, ILeashData.LeashInfo leashInfo) { if (leashInfo.blockPosOpt().isPresent()) { BlockPos pos = leashInfo.blockPosOpt().get(); return Optional.of(SuperLeashKnotEntity.getOrCreateKnot(level, pos)); diff --git a/src/main/java/top/r3944realms/superleadrope/client/renderer/resolver/SuperLeashStateResolver.java b/src/main/java/top/r3944realms/superleadrope/client/renderer/resolver/SuperLeashStateResolver.java index 9304c87..dd35424 100644 --- a/src/main/java/top/r3944realms/superleadrope/client/renderer/resolver/SuperLeashStateResolver.java +++ b/src/main/java/top/r3944realms/superleadrope/client/renderer/resolver/SuperLeashStateResolver.java @@ -20,11 +20,15 @@ import net.minecraft.world.level.Level; import net.minecraft.world.phys.Vec3; import top.r3944realms.superleadrope.SuperLeadRope; import top.r3944realms.superleadrope.client.renderer.state.SuperLeashRenderState; +import top.r3944realms.superleadrope.content.capability.CapabilityHandler; import top.r3944realms.superleadrope.content.capability.impi.LeashDataImpl; -import top.r3944realms.superleadrope.content.capability.inter.ILeashDataCapability; +import top.r3944realms.superleadrope.content.capability.inter.ILeashData; import top.r3944realms.superleadrope.content.entity.SuperLeashKnotEntity; +import top.r3944realms.superleadrope.util.capability.LeashUtil; import java.util.*; +import java.util.concurrent.atomic.AtomicReference; + //TODO: 未来实现更高级的渲染 public class SuperLeashStateResolver { private static final float MAX_TENSION = 1.5f; @@ -44,18 +48,37 @@ public class SuperLeashStateResolver { public static Optional resolve( Entity holder, Entity leashedEntity, - ILeashDataCapability.LeashInfo leashInfo, + ILeashData.LeashInfo leashInfo, float partialTicks) { if (holder == null || leashedEntity == null) { return Optional.empty(); } + AtomicReference holderOffset = new AtomicReference<>(); + AtomicReference entityOffset = new AtomicReference<>(); + LeashUtil.getLeashState(leashedEntity).ifPresent(state -> + state + .getLeashState(holder) + .ifPresent(ls -> { + holderOffset.set( + Optional.ofNullable(ls.holderLocationOffset()) + .orElse(ls.defaultHolderLocationOffset()) + ); + entityOffset.set( + Optional.ofNullable(ls.applyEntityLocationOffset()) + .orElse( + state + .getLeashApplyEntityLocationOffset() + .orElse(state.getDefaultLeashApplyEntityLocationOffset()) + ) + ); + } + )); // 获取当前帧位置(带插值) - Vec3 currentHolderPos = getInterpolatedPosition(holder, partialTicks) - .add(0, holder.getBbHeight() * 0.7, 0); - Vec3 currentEntityPos = getInterpolatedPosition(leashedEntity, partialTicks) - .add(leashInfo.attachOffset()); + Vec3 currentHolderPos = getInterpolatedPosition(holder, partialTicks).add(holderOffset.get()); + Vec3 currentEntityPos = getInterpolatedPosition(leashedEntity, partialTicks).add(entityOffset.get()); + // 获取上一帧数据 FrameCache cache = frameCacheMap.get(leashedEntity.getUUID()); @@ -77,7 +100,7 @@ public class SuperLeashStateResolver { currentHolderPos, currentEntityPos, lastHolderPos, lastEntityPos, lastAngle, lastSpeed, - tension, partialTicks); + tension); // 更新缓存 frameCacheMap.put(leashedEntity.getUUID(), @@ -108,7 +131,7 @@ public class SuperLeashStateResolver { Vec3 currentStart, Vec3 currentEnd, Vec3 lastStart, Vec3 lastEnd, float lastAngle, float lastSpeed, - float tension, float partialTicks) { + float tension) { // 计算当前方向向量 Vec3 currentDir = currentEnd.subtract(currentStart).normalize(); @@ -182,7 +205,7 @@ public class SuperLeashStateResolver { List states = new ArrayList<>(); Level level = leashedEntity.level(); - for (ILeashDataCapability.LeashInfo leashInfo : leashData.getAllLeashes()) { + for (ILeashData.LeashInfo leashInfo : leashData.getAllLeashes()) { Entity holder = null; if (leashInfo.blockPosOpt().isEmpty() && leashInfo.holderIdOpt().isPresent()){ holder = level.getEntity(leashInfo.holderIdOpt().get()); diff --git a/src/main/java/top/r3944realms/superleadrope/config/LeashCommonConfig.java b/src/main/java/top/r3944realms/superleadrope/config/LeashCommonConfig.java index 88d7bae..3340535 100644 --- a/src/main/java/top/r3944realms/superleadrope/config/LeashCommonConfig.java +++ b/src/main/java/top/r3944realms/superleadrope/config/LeashCommonConfig.java @@ -30,27 +30,76 @@ public class LeashCommonConfig { } public static class Common { public final ForgeConfigSpec.ConfigValue> teleportWhitelist; - + public final ForgeConfigSpec.ConfigValue SLPModCommandPrefix; + public final ForgeConfigSpec.BooleanValue EnableSLPModCommandPrefix; + public final ForgeConfigSpec.DoubleValue maxLeashLength; + public final ForgeConfigSpec.DoubleValue elasticDistance; + public final ForgeConfigSpec.DoubleValue extremeSnapFactor; + public final ForgeConfigSpec.DoubleValue springDampening; + public final ForgeConfigSpec.ConfigValue> axisSpecificElasticity; + public final ForgeConfigSpec.IntValue maxLeashesPerEntity; public Common(ForgeConfigSpec.Builder builder) { - builder.push("leash"); - + BUILDER.push("Command"); + EnableSLPModCommandPrefix = builder + .comment("The prefix of this mod's commands") + .define("SLPModCommandPrefix", true); + SLPModCommandPrefix = builder + .comment("The prefix of this mod's commands"," [ Default:'slp'] ") + .define("EnableSLPModCommandPrefix", "slp"); + BUILDER.pop(); + builder.push("Entity"); teleportWhitelist = builder - .comment("Entity teleport whitelist.", - "Use `#modid` to allow teleporting to all entities from a mod.", - "Use `modid:entity_name` to allow teleporting to a specific entity.") + .comment( + "Entity teleport whitelist.", + "Accepted formats:", + " - #modid : allow teleporting to all entities from a specific mod", + " - modid:entity_name : allow teleporting to a specific entity", + " - #modid:tag_name : allow teleporting to all entities under a given entity type tag" + ) .defineListAllowEmpty( List.of("allowedTeleportEntities"), List.of("#minecraft", "modernlife:bicycle", "modernlife:motorboat"), o -> o instanceof String s && isValidFormat(s) ); + builder.pop(); + builder.push("LeashSettings"); + maxLeashLength = builder + .comment("Maximum leash distance (in blocks) for any entity") + .defineInRange("maxLeashLength", 12.0, 6.0, 256.0); + elasticDistance = builder + .comment("Default elastic distance for the Super Lead rope") + .defineInRange("elasticDistance", 6.0, 6.0, 128.0); + + extremeSnapFactor = builder + .comment("Leash break factor = maxDistance * factor") + .defineInRange("extremeSnapFactor", 2.0, 1.0, 4.0); + + springDampening = builder + .comment("Spring dampening coefficient") + .defineInRange("springDampening", 0.7, 0.0, 1.0); + + axisSpecificElasticity = builder + .comment("Axis-specific elasticity coefficients for X,Y,Z axes") + .defineList("axisSpecificElasticity", List.of(0.8, 0.2, 0.8), o -> o instanceof Double); + + maxLeashesPerEntity = builder + .comment("Maximum number of leashes per entity") + .defineInRange("maxLeashesPerEntity", 6, 1, 24); builder.pop(); } private static boolean isValidFormat(String s) { if (s.startsWith("#")) { - return s.length() > 1 && s.substring(1).matches("[a-z0-9_]+"); + String body = s.substring(1); + // 支持 #modid (整个模组) + if (body.matches("[a-z0-9_]+")) { + return true; + } + // 支持 #modid:tag_name (标签) + return body.matches("[a-z0-9_]+:[a-z0-9_/]+"); } + // 普通实体 ID return s.matches("[a-z0-9_]+:[a-z0-9_/]+"); } } diff --git a/src/main/java/top/r3944realms/superleadrope/content/capability/CapabilityHandler.java b/src/main/java/top/r3944realms/superleadrope/content/capability/CapabilityHandler.java index fe55d73..fd844f4 100644 --- a/src/main/java/top/r3944realms/superleadrope/content/capability/CapabilityHandler.java +++ b/src/main/java/top/r3944realms/superleadrope/content/capability/CapabilityHandler.java @@ -24,17 +24,21 @@ import net.minecraftforge.common.capabilities.RegisterCapabilitiesEvent; import net.minecraftforge.event.AttachCapabilitiesEvent; import top.r3944realms.superleadrope.content.capability.impi.LeashDataImpl; import top.r3944realms.superleadrope.content.capability.inter.IEternalPotato; -import top.r3944realms.superleadrope.content.capability.inter.ILeashDataCapability; +import top.r3944realms.superleadrope.content.capability.inter.ILeashData; +import top.r3944realms.superleadrope.content.capability.inter.ILeashState; import top.r3944realms.superleadrope.content.capability.provider.EternalPotatoProvider; import top.r3944realms.superleadrope.content.capability.provider.LeashDataProvider; +import top.r3944realms.superleadrope.content.capability.provider.LeashStateProvider; import top.r3944realms.superleadrope.content.item.EternalPotatoItem; public class CapabilityHandler { - public static final Capability LEASH_DATA_CAP = CapabilityManager.get(new CapabilityToken<>(){}); + public static final Capability LEASH_DATA_CAP = CapabilityManager.get(new CapabilityToken<>(){}); + public static Capability LEASH_STATE_CAP = CapabilityManager.get(new CapabilityToken<>() {}); public static Capability ETERNAL_POTATO_CAP = CapabilityManager.get(new CapabilityToken<>() {}); public static void registerCapability(RegisterCapabilitiesEvent event) { - event.register(ILeashDataCapability.class); + event.register(ILeashData.class); event.register(IEternalPotato.class); + event.register(ILeashState.class); } public static void attachCapability(AttachCapabilitiesEvent event) { @@ -43,6 +47,7 @@ public class CapabilityHandler { (LeashDataImpl.isLeashable(entity))//只对活体 船 矿车添加CAP ) { event.addCapability(LeashDataProvider.LEASH_DATA_REL, new LeashDataProvider(entity)); + event.addCapability(LeashStateProvider.LEASH_STATE_REL, new LeashStateProvider(entity)); } else if (object instanceof ItemStack stack && stack.getItem() instanceof EternalPotatoItem) { event.addCapability(EternalPotatoProvider.ETERNAL_POTATO_DATA_REL, new EternalPotatoProvider(stack)); } diff --git a/src/main/java/top/r3944realms/superleadrope/content/capability/CapabilityRemainder.java b/src/main/java/top/r3944realms/superleadrope/content/capability/CapabilityRemainder.java index 99d966b..7e71ac5 100644 --- a/src/main/java/top/r3944realms/superleadrope/content/capability/CapabilityRemainder.java +++ b/src/main/java/top/r3944realms/superleadrope/content/capability/CapabilityRemainder.java @@ -15,12 +15,25 @@ package top.r3944realms.superleadrope.content.capability; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.entity.player.Player; import net.minecraftforge.event.entity.player.PlayerEvent; +import top.r3944realms.superleadrope.util.capability.LeashUtil; public class CapabilityRemainder { public static void onPlayerClone(PlayerEvent.Clone event) { - if (event.isWasDeath()) { - // + Player newEntity = event.getEntity(); + if(newEntity instanceof ServerPlayer newPlayer) { + Player original = event.getOriginal(); + original.reviveCaps(); + LeashUtil.getLeashState(original) + .ifPresent(oldCap -> + LeashUtil.getLeashState(newPlayer) + .ifPresent(newData -> + newData.copy(oldCap, newEntity) + ) + ); + original.invalidateCaps(); } } } diff --git a/src/main/java/top/r3944realms/superleadrope/content/capability/impi/LeashDataImpl.java b/src/main/java/top/r3944realms/superleadrope/content/capability/impi/LeashDataImpl.java index 80a93fb..33f6a30 100644 --- a/src/main/java/top/r3944realms/superleadrope/content/capability/impi/LeashDataImpl.java +++ b/src/main/java/top/r3944realms/superleadrope/content/capability/impi/LeashDataImpl.java @@ -18,6 +18,7 @@ package top.r3944realms.superleadrope.content.capability.impi; import net.minecraft.core.BlockPos; import net.minecraft.nbt.CompoundTag; import net.minecraft.nbt.ListTag; +import net.minecraft.nbt.NbtUtils; import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.entity.Entity; @@ -28,7 +29,6 @@ import net.minecraft.world.entity.animal.horse.Llama; import net.minecraft.world.entity.player.Player; import net.minecraft.world.entity.vehicle.Boat; import net.minecraft.world.entity.vehicle.Minecart; -import net.minecraft.world.item.ItemStack; import net.minecraft.world.level.Level; import net.minecraft.world.phys.AABB; import net.minecraft.world.phys.Vec3; @@ -36,17 +36,21 @@ import net.minecraftforge.network.PacketDistributor; import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NotNull; import top.r3944realms.superleadrope.SuperLeadRope; -import top.r3944realms.superleadrope.content.capability.CapabilityHandler; -import top.r3944realms.superleadrope.content.capability.inter.ILeashDataCapability; +import top.r3944realms.superleadrope.config.LeashCommonConfig; +import top.r3944realms.superleadrope.content.capability.inter.ILeashData; import top.r3944realms.superleadrope.content.entity.SuperLeashKnotEntity; import top.r3944realms.superleadrope.network.NetworkHandler; import top.r3944realms.superleadrope.network.toClient.LeashDataSyncPacket; +import top.r3944realms.superleadrope.util.capability.LeashUtil; +import top.r3944realms.superleadrope.util.nbt.NBTReader; +import top.r3944realms.superleadrope.util.nbt.NBTWriter; import top.r3944realms.superleadrope.util.riding.RindingLeash; import java.util.*; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.CopyOnWriteArraySet; import java.util.concurrent.atomic.AtomicBoolean; +import java.util.function.Function; import java.util.function.Predicate; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -80,12 +84,35 @@ import java.util.stream.Stream; * * */ -public class LeashDataImpl implements ILeashDataCapability { - private static final double LEASH_ELASTIC_DIST = 6.0; // 弹性距离 - private static final double LEASH_EXTREME_SNAP_DIST_FACTOR = 2.0; // 断裂距离 = 最大距离 * 2 //TODO:未来可配置 - private static final float SPRING_DAMPENING = 0.7f; // 阻尼系数 - private static final Vec3 AXIS_SPECIFIC_ELASTICITY = new Vec3(0.8, 0.2, 0.8); // 轴向弹性系数(Y轴较弱) - private static final int MAX_LEASHES_PER_ENTITY = 6;//一个实体最多链接多少个拴绳 //TODO:未来可配置 +public class LeashDataImpl implements ILeashData { + private static final class Config { + private Config() {} // 私有构造防止实例化 + + static double maxLeashDistance() { + return LeashCommonConfig.COMMON.maxLeashLength.get(); + } + + static double leashElasticDist() { + return LeashCommonConfig.COMMON.elasticDistance.get(); + } + + static double leashExtremeSnapDistFactor() { + return LeashCommonConfig.COMMON.extremeSnapFactor.get(); + } + + static double springDampening() { + return LeashCommonConfig.COMMON.springDampening.get(); + } + + static Vec3 axisSpecificElasticity() { + List list = LeashCommonConfig.COMMON.axisSpecificElasticity.get(); + return new Vec3(list.get(0), list.get(1), list.get(2)); + } + + static int maxLeashesPerEntity() { + return LeashCommonConfig.COMMON.maxLeashesPerEntity.get(); + } + } private final Entity entity; private boolean needsSync = false; private long lastSyncTime; @@ -93,11 +120,11 @@ public class LeashDataImpl implements ILeashDataCapability { private final Map leashHolders = new ConcurrentHashMap<>(); // 引入解决 绳结不保存导致第二进入持有者不存在的问题 private final Map leashKnots = new ConcurrentHashMap<>(); -// private CompoundTag lastSyncedData = new CompoundTag(); public LeashDataImpl(Entity entity) { this.entity = entity; } + @Override public void markForSync() { if (!entity.level().isClientSide) { @@ -106,13 +133,17 @@ public class LeashDataImpl implements ILeashDataCapability { } } - /** 立即同步,无视时间间隔 */ + /** + * 立即同步,无视时间间隔 + */ @Override public void immediateSync() { syncNow(); } - /** 定期调用,每 tick 或每几秒检测 */ + /** + * 定期调用,每 tick 或每几秒检测 + */ @Override public void checkSync() { if (!needsSync || entity.level().isClientSide) return; @@ -124,7 +155,9 @@ public class LeashDataImpl implements ILeashDataCapability { } } - /** 内部统一同步方法,避免重复逻辑 */ + /** + * 内部统一同步方法,避免重复逻辑 + */ private void syncNow() { CompoundTag currentData = serializeNBT(); @@ -137,19 +170,44 @@ public class LeashDataImpl implements ILeashDataCapability { needsSync = false; } + @Override + public boolean addLeash(Entity holder) { + return addLeash(holder, Config.maxLeashDistance()); + } + + @Override + public boolean addLeash(Entity holder, String reserved) { + return addLeash(holder, Config.maxLeashDistance(), reserved); + } + + // 添加拴绳(支持自定义最大长度) + @Override + public boolean addLeash(Entity holder, double maxDistance) { + return addLeash(holder, maxDistance, Config.leashElasticDist(), 0, ""); + } + // 添加拴绳(支持自定义最大长度和弹性距离) @Override - public boolean addLeash(Entity holder, ItemStack leashStack, double maxDistance) { - boolean result = addLeash(holder, leashStack, maxDistance, LEASH_ELASTIC_DIST, 0); - if (result) markForSync(); - return result; + public boolean addLeash(Entity holder, double maxDistance, double elasticDistance, int maxKeepLeashTicks) { + return addLeash(holder, maxDistance, elasticDistance, maxKeepLeashTicks, ""); } + + // 添加拴绳(支持自定义最大长度 + reserved 字段) @Override - public boolean addLeash(Entity holder, ItemStack leashStack, double maxDistance, double elasticDistance, int maxKeepLeashTicks) { + public boolean addLeash(Entity holder, double maxDistance, String reserved) { + return addLeash(holder, maxDistance, Config.leashElasticDist(), 0, reserved); + } + + // 添加拴绳(最终实现:支持最大长度、弹性距离、保持 Tick、reserved) + @Override + public boolean addLeash(Entity holder, double maxDistance, + double elasticDistance, int maxKeepLeashTicks, String reserved) { boolean isSuperKnot = holder instanceof SuperLeashKnotEntity; - if ((!isSuperKnot && leashHolders.containsKey(holder.getUUID()) || (isSuperKnot && leashKnots.containsKey(((SuperLeashKnotEntity) holder).getPos())))) { + if ((!isSuperKnot && leashHolders.containsKey(holder.getUUID())) + || (isSuperKnot && leashKnots.containsKey(((SuperLeashKnotEntity) holder).getPos()))) { return false; } + if (!canBeLeashed()) { Optional uuidOptional = occupyLeash(); if (uuidOptional.isEmpty()) { @@ -157,36 +215,62 @@ public class LeashDataImpl implements ILeashDataCapability { } removeLeash(uuidOptional.get()); } - LeashInfo info = LeashInfo.CreateLeashInfo( + + LeashInfo info = LeashInfo.create( holder, - leashStack.getItem().getDescription().toString(), + reserved, calculateAttachOffset(entity), maxDistance, elasticDistance, maxKeepLeashTicks, maxKeepLeashTicks ); - if (holder instanceof SuperLeashKnotEntity s) { - leashKnots.put(s.getPos(), info); + + if (isSuperKnot) { + leashKnots.put(((SuperLeashKnotEntity) holder).getPos(), info); + } else { + leashHolders.put(holder.getUUID(), info); } - else leashHolders.put(holder.getUUID(), info); + markForSync(); return true; } + // 使用已有的 LeashInfo 添加拴绳(直接走最终实现) @Override - public boolean addLeash(Entity holder, LeashInfo leashInfo) { - return addLeash(holder, ItemStack.EMPTY, leashInfo.maxDistance(), leashInfo.elasticDistance(), leashInfo.maxKeepLeashTicks()); + public void addLeash(Entity holder, LeashInfo leashInfo) { + addLeash(holder, + leashInfo.maxDistance(), + leashInfo.elasticDistance(), + leashInfo.maxKeepLeashTicks(), + leashInfo.reserved() + ); } @Override - public boolean addDelayedLeash(Player holderPlayer) { - return delayedHolders.add(holderPlayer.getUUID()); + public void addDelayedLeash(Player holderPlayer) { + delayedHolders.add(holderPlayer.getUUID()); } @Override - public boolean removeDelayedLeash(UUID onceHolderUUID) { - return delayedHolders.remove(onceHolderUUID); + public void removeDelayedLeash(UUID onceHolderUUID) { + delayedHolders.remove(onceHolderUUID); + } + + private boolean updateLeashInfo( + Map map, + K key, + Function updater + ) { + LeashInfo old = map.get(key); + if (old == null || old.holderIdOpt().isEmpty()) return false; + + LeashInfo updated = updater.apply(old); + if (updated == null) return false; + + map.put(key, updated); + markForSync(); + return true; } @Override @@ -203,76 +287,101 @@ public class LeashDataImpl implements ILeashDataCapability { setMaxDistance(holder.getUUID(), newMaxDistance, newMaxKeepLeashTicks); } - // 动态修改最大拴绳长度 + @Override + public boolean setMaxDistance(Entity holder, double distance, int maxKeepTicks, String reserved) { + return holder instanceof SuperLeashKnotEntity superLeashKnotEntity ? + setMaxDistance(superLeashKnotEntity.getPos(), distance, maxKeepTicks, reserved) : + setMaxDistance(holder.getUUID(), distance, maxKeepTicks, reserved); + } + + @SuppressWarnings("OptionalGetWithoutIsPresent") @Override public boolean setMaxDistance(UUID holderUUID, double newMaxDistance) { - LeashInfo info = leashHolders.get(holderUUID); - if (info == null || info.holderUUIDOpt().isEmpty() || info.holderIdOpt().isEmpty()) return false; - leashHolders.put(holderUUID, new LeashInfo( - info.holderUUIDOpt().get(), - info.holderIdOpt().get(), - info.reserved(), - info.attachOffset(), + return updateLeashInfo(leashHolders, holderUUID, old -> new LeashInfo( + old.holderUUIDOpt().get(), + old.holderIdOpt().get(), + old.reserved(), + old.attachOffset(), newMaxDistance, - info.elasticDistance(), // 保持原有弹性距离 - info.keepLeashTicks(), - info.maxKeepLeashTicks() + old.elasticDistance(), + old.keepLeashTicks(), + old.maxKeepLeashTicks() )); - markForSync(); - return true; } + + @SuppressWarnings("OptionalGetWithoutIsPresent") @Override public boolean setMaxDistance(UUID holderUUID, double newMaxDistance, int newMaxKeepLeashTicks) { - LeashInfo info = leashHolders.get(holderUUID); - if (info == null || info.holderUUIDOpt().isEmpty() || info.holderIdOpt().isEmpty()) return false; - leashHolders.put(holderUUID, new LeashInfo( - info.holderUUIDOpt().get(), - info.holderIdOpt().get(), - info.reserved(), - info.attachOffset(), + return updateLeashInfo(leashHolders, holderUUID, old -> new LeashInfo( + old.holderUUIDOpt().get(), + old.holderIdOpt().get(), + old.reserved(), + old.attachOffset(), newMaxDistance, - info.elasticDistance(), // 保持原有弹性距离 - newMaxKeepLeashTicks, - info.maxKeepLeashTicks() - )); - markForSync(); - return true; - } - - @Override - public boolean setMaxDistance(BlockPos knotPos, double newMaxDistance) { - LeashInfo info = leashKnots.get(knotPos); - if (info == null || info.blockPosOpt().isEmpty() || info.holderIdOpt().isEmpty()) return false; - leashKnots.put(knotPos, new LeashInfo( - info.blockPosOpt().get(), - info.holderIdOpt().get(), - info.reserved(), - info.attachOffset(), - newMaxDistance, - info.elasticDistance(), // 保持原有弹性距离 - info.keepLeashTicks(), - info.maxKeepLeashTicks() - )); - markForSync(); - return true; - } - - @Override - public boolean setMaxDistance(BlockPos knotPos, double newMaxDistance, int newMaxKeepLeashTicks) { - LeashInfo info = leashKnots.get(knotPos); - if (info == null || info.blockPosOpt().isEmpty() || info.holderIdOpt().isEmpty()) return false; - leashKnots.put(knotPos, new LeashInfo( - info.blockPosOpt().get(), - info.holderIdOpt().get(), - info.reserved(), - info.attachOffset(), - newMaxDistance, - info.elasticDistance(), // 保持原有弹性距离 - info.keepLeashTicks(), + old.elasticDistance(), + Math.min(old.keepLeashTicks(), newMaxKeepLeashTicks), newMaxKeepLeashTicks )); - markForSync(); - return true; + } + + @SuppressWarnings("OptionalGetWithoutIsPresent") + @Override + public boolean setMaxDistance(UUID holderUUID, double distance, int maxKeepTicks, String reserved) { + return updateLeashInfo(leashHolders, holderUUID, old -> new LeashInfo( + old.holderUUIDOpt().get(), + old.holderIdOpt().get(), + reserved, + old.attachOffset(), + distance, + old.elasticDistance(), + Math.min(old.keepLeashTicks(), maxKeepTicks), + maxKeepTicks + )); + } + + @SuppressWarnings("OptionalGetWithoutIsPresent") + @Override + public boolean setMaxDistance(BlockPos knotPos, double newMaxDistance) { + return updateLeashInfo(leashKnots, knotPos, old -> new LeashInfo( + old.blockPosOpt().get(), + old.holderIdOpt().get(), + old.reserved(), + old.attachOffset(), + newMaxDistance, + old.elasticDistance(), + old.keepLeashTicks(), + old.maxKeepLeashTicks() + )); + } + + @SuppressWarnings("OptionalGetWithoutIsPresent") + @Override + public boolean setMaxDistance(BlockPos knotPos, double newMaxDistance, int newMaxKeepLeashTicks) { + return updateLeashInfo(leashKnots, knotPos, old -> new LeashInfo( + old.blockPosOpt().get(), + old.holderIdOpt().get(), + old.reserved(), + old.attachOffset(), + newMaxDistance, + old.elasticDistance(), + Math.min(old.keepLeashTicks(), newMaxKeepLeashTicks), + newMaxKeepLeashTicks + )); + } + + @SuppressWarnings("OptionalGetWithoutIsPresent") + @Override + public boolean setMaxDistance(BlockPos knotPos, double distance, int maxKeepTicks, String reserved) { + return updateLeashInfo(leashKnots, knotPos, old -> new LeashInfo( + old.blockPosOpt().get(), + old.holderIdOpt().get(), + reserved, + old.attachOffset(), + distance, + old.elasticDistance(), + Math.min(old.keepLeashTicks(), maxKeepTicks), + maxKeepTicks + )); } @Override @@ -283,42 +392,34 @@ public class LeashDataImpl implements ILeashDataCapability { } // 动态修改弹性距离 + @SuppressWarnings("OptionalGetWithoutIsPresent") @Override public boolean setElasticDistance(UUID holderUUID, double newElasticDistance) { - LeashInfo info = leashHolders.get(holderUUID); - if (info == null || info.holderUUIDOpt().isEmpty() || info.holderIdOpt().isEmpty()) return false; - - leashHolders.put(holderUUID, new LeashInfo( - info.holderUUIDOpt().get(), - info.holderIdOpt().get(), - info.reserved(), - info.attachOffset(), - info.maxDistance(), + return updateLeashInfo(leashHolders, holderUUID, old -> new LeashInfo( + old.holderUUIDOpt().get(), + old.holderIdOpt().get(), + old.reserved(), + old.attachOffset(), + old.maxDistance(), newElasticDistance, - info.keepLeashTicks(), - info.maxKeepLeashTicks() + old.keepLeashTicks(), + old.maxKeepLeashTicks() )); - markForSync(); - return true; } + @SuppressWarnings("OptionalGetWithoutIsPresent") @Override public boolean setElasticDistance(BlockPos knotPos, double newElasticDistance) { - LeashInfo info = leashKnots.get(knotPos); - if (info == null || info.blockPosOpt().isEmpty() || info.holderIdOpt().isEmpty()) return false; - - leashKnots.put(knotPos, new LeashInfo( - info.blockPosOpt().get(), - info.holderIdOpt().get(), - info.reserved(), - info.attachOffset(), - info.maxDistance(), + return updateLeashInfo(leashKnots, knotPos, old -> new LeashInfo( + old.blockPosOpt().get(), + old.holderIdOpt().get(), + old.reserved(), + old.attachOffset(), + old.maxDistance(), newElasticDistance, - info.keepLeashTicks(), - info.maxKeepLeashTicks() + old.keepLeashTicks(), + old.maxKeepLeashTicks() )); - markForSync(); - return true; } @Override @@ -328,41 +429,72 @@ public class LeashDataImpl implements ILeashDataCapability { setElasticDistance(holder.getUUID(), newElasticDistance, newMaxKeepLeashTicks); } - // 动态修改弹性距离 @Override - public boolean setElasticDistance(UUID holderUUID, double newElasticDistance, int newMaxKeepLeashTicks) { - LeashInfo info = leashHolders.get(holderUUID); - if (info == null || info.holderUUIDOpt().isEmpty() || info.holderIdOpt().isEmpty()) return false; - - leashHolders.put(holderUUID, new LeashInfo( - info.holderUUIDOpt().get(), - info.holderIdOpt().get(), - info.reserved(), - info.attachOffset(), - info.maxDistance(), - newElasticDistance, - Math.min(info.keepLeashTicks(), newMaxKeepLeashTicks), // 限制剩余Tick不超过新最大值 - newMaxKeepLeashTicks - )); - return true; + public boolean setElasticDistance(Entity holder, double distance, int maxKeepTicks, String reserved) { + return holder instanceof SuperLeashKnotEntity superLeashKnotEntity ? + setElasticDistance(superLeashKnotEntity.getPos(), distance, maxKeepTicks, reserved) : + setElasticDistance(holder.getUUID(), distance, maxKeepTicks, reserved); } + // 动态修改弹性距离 + @SuppressWarnings("OptionalGetWithoutIsPresent") @Override - public boolean setElasticDistance(BlockPos knotPos, double newElasticDistance, int newMaxKeepLeashTicks) { - LeashInfo info = leashKnots.get(knotPos); - if (info == null || info.blockPosOpt().isEmpty() || info.holderIdOpt().isEmpty()) return false; - - leashKnots.put(knotPos, new LeashInfo( - info.blockPosOpt().get(), - info.holderIdOpt().get(), - info.reserved(), - info.attachOffset(), - info.maxDistance(), + public boolean setElasticDistance(UUID holderUUID, double newElasticDistance, int newMaxKeepLeashTicks) { + return updateLeashInfo(leashHolders, holderUUID, old -> new LeashInfo( + old.holderUUIDOpt().get(), + old.holderIdOpt().get(), + old.reserved(), + old.attachOffset(), + old.maxDistance(), newElasticDistance, - Math.min(info.keepLeashTicks(), newMaxKeepLeashTicks), // 限制剩余Tick不超过新最大值 + Math.min(old.keepLeashTicks(), newMaxKeepLeashTicks), + newMaxKeepLeashTicks + )); + } + + @SuppressWarnings("OptionalGetWithoutIsPresent") + @Override + public boolean setElasticDistance(UUID holderUUID, double distance, int maxKeepTicks, String reserved) { + return updateLeashInfo(leashHolders, holderUUID, old -> new LeashInfo( + old.holderUUIDOpt().get(), + old.holderIdOpt().get(), + reserved, + old.attachOffset(), + old.maxDistance(), + distance, + Math.min(old.keepLeashTicks(), maxKeepTicks), + maxKeepTicks + )); + } + + @SuppressWarnings("OptionalGetWithoutIsPresent") + @Override + public boolean setElasticDistance(BlockPos knotPos, double newElasticDistance, int newMaxKeepLeashTicks) { + return updateLeashInfo(leashKnots, knotPos, old -> new LeashInfo( + old.blockPosOpt().get(), + old.holderIdOpt().get(), + old.reserved(), + old.attachOffset(), + old.maxDistance(), + newElasticDistance, + old.keepLeashTicks(), + old.maxKeepLeashTicks() + )); + } + + @SuppressWarnings("OptionalGetWithoutIsPresent") + @Override + public boolean setElasticDistance(BlockPos knotPos, double newElasticDistance, int newMaxKeepLeashTicks, String reserved) { + return updateLeashInfo(leashKnots, knotPos, old -> new LeashInfo( + old.blockPosOpt().get(), + old.holderIdOpt().get(), + reserved, + old.attachOffset(), + old.maxDistance(), + newElasticDistance, + Math.min(old.keepLeashTicks(), newMaxKeepLeashTicks), newMaxKeepLeashTicks )); - return true; } /** @@ -439,14 +571,14 @@ public class LeashDataImpl implements ILeashDataCapability { LeashInfo info = entry.getValue(); Vec3 entityPos = entity.position().add(info.attachOffset()); double distance = holderPos.distanceTo(entityPos); - double extremeSnapDist = info.maxDistance() * LEASH_EXTREME_SNAP_DIST_FACTOR; + double extremeSnapDist = info.maxDistance() * Config.leashExtremeSnapDistFactor(); // 1. 检查是否超出断裂距离 if (distance > extremeSnapDist) { if (info.keepLeashTicks() > 0) { // 计算临界拉力 Vec3 pullForce = calculateCriticalPullForce(holderPos, entityPos, distance, info); - entry.setValue(info.decrementKeepLeashTicks()); + entry.setValue(info.decrementKeepTicks()); return pullForce; } // 断裂 @@ -471,7 +603,7 @@ public class LeashDataImpl implements ILeashDataCapability { // 3. 重置缓冲Tick if (distance <= info.maxDistance() && info.keepLeashTicks() < info.maxKeepLeashTicks()) { - entry.setValue(info.resetKeepLeashTicks()); + entry.setValue(info.resetKeepTicks()); } return pullForce; @@ -489,13 +621,13 @@ public class LeashDataImpl implements ILeashDataCapability { } Vec3 pullForce = pullDirection.scale( - (distance - info.elasticDistance()) * pullStrength * SPRING_DAMPENING + (distance - info.elasticDistance()) * pullStrength * Config.springDampening() ); return new Vec3( - pullForce.x * AXIS_SPECIFIC_ELASTICITY.x, - pullForce.y * AXIS_SPECIFIC_ELASTICITY.y, - pullForce.z * AXIS_SPECIFIC_ELASTICITY.z + pullForce.x * Config.axisSpecificElasticity().x, + pullForce.y * Config.axisSpecificElasticity().y, + pullForce.z * Config.axisSpecificElasticity().z ); } @@ -506,13 +638,13 @@ public class LeashDataImpl implements ILeashDataCapability { double pullStrength = 1.0 + excessRatio * 2.0; Vec3 pullForce = pullDirection.scale( - (distance - info.elasticDistance()) * pullStrength * SPRING_DAMPENING + (distance - info.elasticDistance()) * pullStrength * Config.springDampening() ); return new Vec3( - pullForce.x * AXIS_SPECIFIC_ELASTICITY.x, - pullForce.y * AXIS_SPECIFIC_ELASTICITY.y, - pullForce.z * AXIS_SPECIFIC_ELASTICITY.z + pullForce.x * Config.axisSpecificElasticity().x, + pullForce.y * Config.axisSpecificElasticity().y, + pullForce.z * Config.axisSpecificElasticity().z ); } @@ -566,10 +698,10 @@ public class LeashDataImpl implements ILeashDataCapability { } @Override - public boolean transferLeash(Entity holder, Entity newHolder, ItemStack stack) { + public boolean transferLeash(Entity holder, Entity newHolder, String reserved) { return holder instanceof SuperLeashKnotEntity superLeashKnotEntity ? - transferLeash(superLeashKnotEntity.getPos(), newHolder, stack) : - transferLeash(holder.getUUID(), newHolder, stack); + transferLeash(superLeashKnotEntity.getPos(), newHolder, reserved) : + transferLeash(holder.getUUID(), newHolder, reserved); } // 将拴绳持有者转移到新实体(非拴绳结 -> 任意) @@ -588,14 +720,14 @@ public class LeashDataImpl implements ILeashDataCapability { return true; } @Override - public boolean transferLeash(UUID oldHolderUUID, Entity newHolder, ItemStack stack) { + public boolean transferLeash(UUID oldHolderUUID, Entity newHolder, String reserved) { LeashInfo info = leashHolders.remove(oldHolderUUID); if (info == null || newHolder == null) return false; if(newHolder instanceof SuperLeashKnotEntity superLeashKnotEntity) { - LeashInfo leashInfo = info.transferHolder(superLeashKnotEntity, stack.getDescriptionId()); + LeashInfo leashInfo = info.transferHolder(superLeashKnotEntity, reserved); leashKnots.put(superLeashKnotEntity.getPos(), leashInfo); } else { - LeashInfo leashInfo = info.transferHolder(newHolder, stack.getDescriptionId()); + LeashInfo leashInfo = info.transferHolder(newHolder, reserved); leashHolders.put(newHolder.getUUID(), leashInfo); } markForSync(); @@ -618,14 +750,14 @@ public class LeashDataImpl implements ILeashDataCapability { } @Override - public boolean transferLeash(BlockPos knotPos, Entity newHolder, ItemStack stack) { + public boolean transferLeash(BlockPos knotPos, Entity newHolder, String reserved) { LeashInfo info = leashKnots.remove(knotPos); if (info == null || newHolder == null) return false; if(newHolder instanceof SuperLeashKnotEntity superLeashKnotEntity) { - LeashInfo leashInfo = info.transferHolder(superLeashKnotEntity, stack.getDescriptionId()); + LeashInfo leashInfo = info.transferHolder(superLeashKnotEntity, reserved); leashKnots.put(superLeashKnotEntity.getPos(), leashInfo); } else { - LeashInfo leashInfo = info.transferHolder(newHolder, stack.getDescriptionId()); + LeashInfo leashInfo = info.transferHolder(newHolder, reserved); leashHolders.put(newHolder.getUUID(), leashInfo); } markForSync(); @@ -743,9 +875,7 @@ public class LeashDataImpl implements ILeashDataCapability { throw new IllegalArgumentException("LeashInfo.blockPos is empty"); } BlockPos blockPos = info.blockPosOpt().get(); - infoTag.putInt("HolderX", blockPos.getX()); - infoTag.putInt("HolderY", blockPos.getY()); - infoTag.putInt("HolderZ", blockPos.getZ()); + infoTag.put("KnotBlockPos", NbtUtils.writeBlockPos(blockPos)); return getCommonCompoundTag(info, infoTag); } @@ -755,9 +885,7 @@ public class LeashDataImpl implements ILeashDataCapability { } infoTag.putInt("HolderID", info.holderIdOpt().get()); infoTag.putString("LeashItem", info.reserved()); - infoTag.putDouble("OffsetX", info.attachOffset().x); - infoTag.putDouble("OffsetY", info.attachOffset().y); - infoTag.putDouble("OffsetZ", info.attachOffset().z); + infoTag.put("Offset", NBTWriter.writeVec3(info.attachOffset())); infoTag.putDouble("MaxDistance", info.maxDistance()); infoTag.putDouble("ElasticDistance", info.elasticDistance()); infoTag.putInt("KeepLeashTicks", info.keepLeashTicks()); @@ -793,10 +921,47 @@ public class LeashDataImpl implements ILeashDataCapability { } } } + private static @NotNull UUID getDelayedUUIDFormListTag(@NotNull CompoundTag infoTag) { + if (infoTag.contains("DelayHolderUUID")) + return infoTag.getUUID("DelayHolderUUID"); + throw new IllegalArgumentException("LeashInfo.intId is empty"); + } + @Contract("_ -> new") + private static @NotNull LeashInfo getUUIDLeashDataFormListTag(@NotNull CompoundTag infoTag) { + if (infoTag.contains("HolderUUID")){ + return new LeashInfo( + infoTag.getUUID("HolderUUID"), + infoTag.getInt("HolderID"), + infoTag.getString("LeashItem"), + NBTReader.readVec3(infoTag.getCompound("Offset")), + infoTag.getDouble("MaxDistance"), + infoTag.contains("ElasticDistance") ? infoTag.getDouble("ElasticDistance") : 6.0, + infoTag.getInt("KeepLeashTicks"), + infoTag.contains("MaxKeepLeashTicks") ? infoTag.getInt("MaxKeepLeashTicks") : 20 + ); + } else + throw new IllegalArgumentException("Unknown LeashInfo"); + } + @Contract("_ -> new") + private static @NotNull LeashInfo getBlockPosLeashDataFormListTag(@NotNull CompoundTag infoTag) { + if (infoTag.contains("KnotBlockPos")) { + return new LeashInfo( + NbtUtils.readBlockPos(infoTag.getCompound("KnotBlockPos")), + infoTag.getInt("HolderID"), + infoTag.getString("LeashItem"), + NBTReader.readVec3(infoTag.getCompound("Offset")), + infoTag.getDouble("MaxDistance"), + infoTag.contains("ElasticDistance") ? infoTag.getDouble("ElasticDistance") : 6.0, + infoTag.getInt("KeepLeashTicks"), + infoTag.contains("MaxKeepLeashTicks") ? infoTag.getInt("MaxKeepLeashTicks") : 20 + ); + } else + throw new IllegalArgumentException("Unknown LeashInfo"); + } @Override public boolean canBeLeashed() { - return (leashHolders.size() + leashKnots.size()) <= MAX_LEASHES_PER_ENTITY; + return (leashHolders.size() + leashKnots.size()) <= Config.maxLeashesPerEntity(); } @Override @@ -823,43 +988,7 @@ public class LeashDataImpl implements ILeashDataCapability { return Optional.empty(); // 理论上不会到这里 } - private static @NotNull UUID getDelayedUUIDFormListTag(@NotNull CompoundTag infoTag) { - if (infoTag.contains("DelayHolderUUID")) - return infoTag.getUUID("DelayHolderUUID"); - throw new IllegalArgumentException("LeashInfo.intId is empty"); - } - @Contract("_ -> new") - private static @NotNull LeashInfo getUUIDLeashDataFormListTag(@NotNull CompoundTag infoTag) { - if (infoTag.contains("HolderUUID")){ - return new LeashInfo( - infoTag.getUUID("HolderUUID"), - infoTag.getInt("HolderID"), - infoTag.getString("LeashItem"), - new Vec3(infoTag.getDouble("OffsetX"), infoTag.getDouble("OffsetY"), infoTag.getDouble("OffsetZ")), - infoTag.getDouble("MaxDistance"), - infoTag.contains("ElasticDistance") ? infoTag.getDouble("ElasticDistance") : 6.0, - infoTag.getInt("KeepLeashTicks"), - infoTag.contains("MaxKeepLeashTicks") ? infoTag.getInt("MaxKeepLeashTicks") : 20 - ); - } else - throw new IllegalArgumentException("Unknown LeashInfo"); - } - @Contract("_ -> new") - private static @NotNull LeashInfo getBlockPosLeashDataFormListTag(@NotNull CompoundTag infoTag) { - if (infoTag.contains("HolderX")) { - return new LeashInfo( - new BlockPos(infoTag.getInt("HolderX"), infoTag.getInt("HolderY"), infoTag.getInt("HolderZ")), - infoTag.getInt("HolderID"), - infoTag.getString("LeashItem"), - new Vec3(infoTag.getDouble("OffsetX"), infoTag.getDouble("OffsetY"), infoTag.getDouble("OffsetZ")), - infoTag.getDouble("MaxDistance"), - infoTag.contains("ElasticDistance") ? infoTag.getDouble("ElasticDistance") : 6.0, - infoTag.getInt("KeepLeashTicks"), - infoTag.contains("MaxKeepLeashTicks") ? infoTag.getInt("MaxKeepLeashTicks") : 20 - ); - } else - throw new IllegalArgumentException("Unknown LeashInfo"); - } + public static @NotNull List leashableInArea(Level pLevel, Vec3 pPos, Predicate filter) { return leashableInArea(pLevel, pPos, filter, 1024D); @@ -882,17 +1011,23 @@ public class LeashDataImpl implements ILeashDataCapability { return false; } else { Optional leashInfo = getLeashInfo(pEntity); - return leashInfo.isEmpty() && (entity.distanceTo(pEntity) <= LEASH_ELASTIC_DIST * LEASH_EXTREME_SNAP_DIST_FACTOR) && canBeLeashed();//距离最大,则不可以被固定或转移 + return leashInfo.isEmpty() && (entity.distanceTo(pEntity) <= Config.leashElasticDist() * Config.leashExtremeSnapDistFactor()) && canBeLeashed();//距离最大,则不可以被固定或转移 } } public static boolean isLeashHolder(@NotNull Entity pEntity, UUID pHolderUUID) { AtomicBoolean isTarget = new AtomicBoolean(false); - pEntity.getCapability(CapabilityHandler.LEASH_DATA_CAP).ifPresent(i -> isTarget.set(i.isLeashedBy(pHolderUUID))); + LeashUtil.getLeashData(pEntity) + .ifPresent(i -> + isTarget.set(i.isLeashedBy(pHolderUUID)) + ); return isTarget.get(); } public static boolean isLeashHolder(@NotNull Entity pEntity, BlockPos pKnotPos) { AtomicBoolean isTarget = new AtomicBoolean(false); - pEntity.getCapability(CapabilityHandler.LEASH_DATA_CAP).ifPresent(i -> isTarget.set(i.isLeashedBy(pKnotPos))); + LeashUtil.getLeashData(pEntity) + .ifPresent(i -> + isTarget.set(i.isLeashedBy(pKnotPos)) + ); return isTarget.get(); } public static boolean isLeashHolder(@NotNull Entity pEntity, Entity pTestHolder) { diff --git a/src/main/java/top/r3944realms/superleadrope/content/capability/impi/LeashStateImpl.java b/src/main/java/top/r3944realms/superleadrope/content/capability/impi/LeashStateImpl.java new file mode 100644 index 0000000..f61fb3e --- /dev/null +++ b/src/main/java/top/r3944realms/superleadrope/content/capability/impi/LeashStateImpl.java @@ -0,0 +1,315 @@ +/* + * Super Lead rope mod + * Copyright (C) 2025 R3944Realms + * 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 . + */ + +package top.r3944realms.superleadrope.content.capability.impi; + +import com.google.common.collect.Maps; +import net.minecraft.core.BlockPos; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.ListTag; +import net.minecraft.nbt.NbtUtils; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.phys.Vec3; +import net.minecraftforge.network.PacketDistributor; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import top.r3944realms.superleadrope.content.capability.inter.ILeashState; +import top.r3944realms.superleadrope.content.entity.SuperLeashKnotEntity; +import top.r3944realms.superleadrope.network.NetworkHandler; +import top.r3944realms.superleadrope.network.toClient.LeashStateSyncPacket; +import top.r3944realms.superleadrope.util.nbt.NBTReader; +import top.r3944realms.superleadrope.util.nbt.NBTWriter; + +import java.util.Map; +import java.util.Optional; +import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; +//TODO: 将拴绳状态与数据联系在一起 +public class LeashStateImpl implements ILeashState { + private Entity entity; + private boolean needsSync = false; + private long lastSyncTime; + private final Map leashHolders = new ConcurrentHashMap<>(); + private final Map leashKnots = new ConcurrentHashMap<>(); + private volatile Vec3 staticApplyEntityLocationOffset; + private volatile Vec3 defaultApplyEntityLocationOffset = new Vec3(0 , 0.45, 0); + public LeashStateImpl(Entity entity) { + this.entity = entity; + } + + @Override + public void markForSync() { + if (!entity.level().isClientSide) { + needsSync = true; + immediateSync(); // 立即同步一次 + } + } + + @Override + public void immediateSync() { + syncNow(); + } + + @Override + public void checkSync() { + if (!needsSync || entity.level().isClientSide) return; + + long now = System.currentTimeMillis(); + // 每隔 2 秒同步一次 + if (now - lastSyncTime > 2000) { + syncNow(); + } + } + + private void syncNow() { + CompoundTag currentData = serializeNBT(); + + NetworkHandler.sendToPlayer( + new LeashStateSyncPacket(entity.getId(), currentData), + entity, + PacketDistributor.TRACKING_ENTITY_AND_SELF + ); + lastSyncTime = System.currentTimeMillis(); + needsSync = false; + } + + @Override + public Map getHolderLeashStates() { + ConcurrentMap retMap = Maps.newConcurrentMap(); + retMap.putAll(leashHolders); + return retMap; + } + + @Override + public Map getKnotLeashStates() { + ConcurrentMap retMap = Maps.newConcurrentMap(); + retMap.putAll(leashKnots); + return retMap; + } + + @Override + public Optional getLeashState(Entity holder) { + return holder instanceof SuperLeashKnotEntity leashKnot ? getLeashState(leashKnot.getPos()) + : getLeashState(holder.getUUID()); + } + + @Override + public Optional getLeashState(UUID uuid) { + return leashHolders.containsKey(uuid) ? Optional.of(leashHolders.get(uuid)) : Optional.empty(); + } + + @Override + public Optional getLeashState(BlockPos pos) { + return leashKnots.containsKey(pos) ? Optional.of(leashKnots.get(pos)) : Optional.empty(); + } + + @Override + public Vec3 getDefaultLeashApplyEntityLocationOffset() { + return defaultApplyEntityLocationOffset; + } + @Override + public Optional getLeashApplyEntityLocationOffset() { + return Optional.ofNullable(staticApplyEntityLocationOffset); + } + + @Override + public void resetLeashHolderLocationOffset(Entity holder) { + if (entity instanceof SuperLeashKnotEntity leashKnot) { + resetLeashHolderLocationOffset(leashKnot.getPos()); + } else resetLeashHolderLocationOffset(holder.getUUID()); + } + + @Override + public void resetLeashHolderLocationOffset(UUID holderUUID) { + leashHolders.computeIfPresent(holderUUID, (uuid, state) -> state.resetHolderLocationOffset()); + markForSync(); + } + + @Override + public void resetLeashHolderLocationOffset(BlockPos knotPos) { + leashKnots.computeIfPresent(knotPos, (blockPos, state) -> state.resetHolderLocationOffset()); + markForSync(); + } + + @Override + public void setLeashHolderLocationOffset(Entity holder, Vec3 offset) { + if (entity instanceof SuperLeashKnotEntity leashKnot) { + setLeashHolderLocationOffset(leashKnot.getPos(), offset); + } else setLeashHolderLocationOffset(holder.getUUID(), offset); + } + + @Override + public void setLeashHolderLocationOffset(UUID holderUUID, Vec3 offset) { + leashHolders.computeIfPresent(holderUUID, (uuid, state) -> state.setHolderLocationOffset(offset)); + markForSync(); + } + + @Override + public void setLeashHolderLocationOffset(BlockPos knotPos, Vec3 leashHolderLocationOffset) { + leashKnots.computeIfPresent(knotPos, (blockPos, state) -> state.setHolderLocationOffset(leashHolderLocationOffset)); + markForSync(); + } + + @Override + public void addLeashHolderLocationOffset(Entity holder, Vec3 offset) { + if (entity instanceof SuperLeashKnotEntity leashKnot) { + addLeashHolderLocationOffset(leashKnot.getPos(), offset); + } else addLeashHolderLocationOffset(holder.getUUID(), offset); + } + + @Override + public void addLeashHolderLocationOffset(UUID holderUUID, Vec3 offset) { + leashHolders.computeIfPresent(holderUUID, (uuid, state) -> state.setHolderLocationOffset(state.holderLocationOffset().add(offset))); + markForSync(); + } + + @Override + public void addLeashHolderLocationOffset(BlockPos knotPos, Vec3 offset) { + leashKnots.computeIfPresent(knotPos, (blockPos, state) -> state.setHolderLocationOffset(state.holderLocationOffset().add(offset))); + markForSync(); + } + + @Override + public void removeLeashHolderLocationOffset(Entity holder) { + if (entity instanceof SuperLeashKnotEntity leashKnot) { + removeLeashHolderLocationOffset(leashKnot.getPos()); + } else removeLeashHolderLocationOffset(holder.getUUID()); + } + + @Override + public void removeLeashHolderLocationOffset(UUID holderUUID) { + leashHolders.remove(holderUUID); + markForSync(); + } + + @Override + public void removeLeashHolderLocationOffset(BlockPos knotPos) { + leashKnots.remove(knotPos); + markForSync(); + } + + @Override + public void resetAllLeashHolderLocationsOffset() { + leashKnots.replaceAll((pos, leashState) -> leashState.resetHolderLocationOffset()); + leashHolders.replaceAll((uuid, leashState) -> leashState.resetHolderLocationOffset()); + markForSync(); + } + + @Override + public void resetAllLeashApplyEntityLocationsOffset() { + leashKnots.replaceAll((pos, leashState) -> leashState.setApplyEntityLocationOffset(defaultApplyEntityLocationOffset)); + leashHolders.replaceAll((uuid, leashState) -> leashState.setApplyEntityLocationOffset(defaultApplyEntityLocationOffset)); + markForSync(); + } + + @Override + public void removeLeashApplyEntityLocationOffset() { + staticApplyEntityLocationOffset = null; + markForSync(); + } + + @Override + public void setLeashApplyEntityLocationOffset(Vec3 leashHolderLocationOffset) { + staticApplyEntityLocationOffset = leashHolderLocationOffset; + markForSync(); + } + + @Override + public void addLeashApplyEntityLocationOffset(Vec3 offset) { + Optional.ofNullable(this.staticApplyEntityLocationOffset) + .ifPresentOrElse(vec3 -> vec3.add(offset), () -> this.staticApplyEntityLocationOffset = offset); + markForSync(); + } + + @Override + public void copy(ILeashState other, Entity newEntity) { + this.entity = newEntity; + this.defaultApplyEntityLocationOffset = other.getDefaultLeashApplyEntityLocationOffset(); + this.staticApplyEntityLocationOffset = other.getLeashApplyEntityLocationOffset().orElse(null); + } + + @Override + public CompoundTag serializeNBT() { + CompoundTag tag = new CompoundTag(); + ListTag holdersList = new ListTag(); + leashHolders.forEach((uuid, state ) -> { + CompoundTag infoTag = generateCompoundTagFromUUIDLeashInfo(uuid, state); + holdersList.add(infoTag); + }); + leashKnots.forEach((blockPos, state ) -> { + CompoundTag infoTag = generateCompoundTagFromBlockPosLeashState(blockPos, state); + holdersList.add(infoTag); + }); + tag.put("LeashHolders", holdersList); + if (staticApplyEntityLocationOffset != null) { + tag.put("StaticApplyEntityLocationOffset", NBTWriter.writeVec3(staticApplyEntityLocationOffset)); + } + tag.put("DefaultApplyEntityLocationOffset", NBTWriter.writeVec3(defaultApplyEntityLocationOffset)); + return tag; + } + private static @NotNull CompoundTag generateCompoundTagFromUUIDLeashInfo(@NotNull UUID uuid, @NotNull ILeashState.LeashState info) { + CompoundTag infoTag = new CompoundTag(); + infoTag.putUUID("HolderUUID", uuid); + return getCommonCompoundTag(info ,infoTag); + } + private static @NotNull CompoundTag generateCompoundTagFromBlockPosLeashState(@NotNull BlockPos blockpos, @NotNull ILeashState.LeashState info) { + CompoundTag infoTag = new CompoundTag(); + infoTag.put("KnotBlockPos", NbtUtils.writeBlockPos(blockpos)); + return getCommonCompoundTag(info, infoTag); + } + + private static @NotNull CompoundTag getCommonCompoundTag(@NotNull ILeashState.LeashState info, CompoundTag infoTag) { + infoTag.put("ApplyEntityLocationOffset", NBTWriter.writeVec3(info.applyEntityLocationOffset())); + infoTag.put("HolderEntityLocationOffset", NBTWriter.writeVec3(info.holderLocationOffset())); + infoTag.put("DefaultHolderLocationOffset", NBTWriter.writeVec3(info.defaultHolderLocationOffset())); + return infoTag; + } + + @Override + public void deserializeNBT(@NotNull CompoundTag nbt) { + leashHolders.clear(); + leashKnots.clear(); + if (nbt.contains("LeashHolders", ListTag.TAG_LIST)) { + ListTag holdersList = nbt.getList("LeashHolders", ListTag.TAG_COMPOUND); + if(nbt.contains("StaticApplyEntityLocationOffset")) { + staticApplyEntityLocationOffset = NBTReader.readVec3(nbt.getCompound("StaticApplyEntityLocationOffset")); + } + if (nbt.contains("DefaultApplyEntityLocationOffset")) { + defaultApplyEntityLocationOffset = NBTReader.readVec3(nbt.getCompound("DefaultApplyEntityLocationOffset")); + } else throw new IllegalArgumentException("Nbt Lost DefaultApplyEntityLocationOffset Value"); + for (int i = 0; i < holdersList.size(); i++) { + CompoundTag infoTag = holdersList.getCompound(i); + if (infoTag.contains("HolderUUID")) { + ILeashState.LeashState uuidLeashDataFormListTag = getUUIDLeashStateForm(infoTag); + leashHolders.put(infoTag.getUUID("HolderUUID"), uuidLeashDataFormListTag); + } else { + ILeashState.LeashState blockPosLeashDataFormListTag = getUUIDLeashStateForm(infoTag); + leashKnots.put(NbtUtils.readBlockPos(infoTag.getCompound("KnotBlockPos")), blockPosLeashDataFormListTag); + } + } + + } + } + @Contract("_ -> new") + private static @NotNull ILeashState.LeashState getUUIDLeashStateForm(@NotNull CompoundTag infoTag) { + return new ILeashState.LeashState( + NBTReader.readVec3(infoTag.getCompound("HolderEntityLocationOffset")), + NBTReader.readVec3(infoTag.getCompound("ApplyEntityLocationOffset")), + NBTReader.readVec3(infoTag.getCompound("DefaultHolderLocationOffset")) + ); + } + +} diff --git a/src/main/java/top/r3944realms/superleadrope/content/capability/inter/ILeashData.java b/src/main/java/top/r3944realms/superleadrope/content/capability/inter/ILeashData.java new file mode 100644 index 0000000..da7a05e --- /dev/null +++ b/src/main/java/top/r3944realms/superleadrope/content/capability/inter/ILeashData.java @@ -0,0 +1,213 @@ +/* + * Super Lead rope mod + * Copyright (C) 2025 R3944Realms + * 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 . + */ + +package top.r3944realms.superleadrope.content.capability.inter; + +import net.minecraft.core.BlockPos; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.phys.Vec3; +import net.minecraftforge.common.util.INBTSerializable; +import top.r3944realms.superleadrope.content.entity.SuperLeashKnotEntity; + +import java.util.Collection; +import java.util.Optional; +import java.util.UUID; + +/** + * Capability interface for managing leash data of entities and knots. + */ +public interface ILeashData extends INBTSerializable { + + /* ---------------------- + * Add / remove leashes + * ---------------------- */ + boolean addLeash(Entity holder); + boolean addLeash(Entity holder, String reserved); + boolean addLeash(Entity holder, double maxDistance); + boolean addLeash(Entity holder, double maxDistance, double elasticDistance, int maxKeepTicks); + boolean addLeash(Entity holder, double maxDistance, String reserved); + boolean addLeash(Entity holder, double maxDistance, double elasticDistance, int maxKeepTicks, String reserved); + + void addLeash(Entity holder, LeashInfo info); + + void addDelayedLeash(Player holderPlayer); + void removeDelayedLeash(UUID onceHolderPlayerUUID); + + boolean removeLeash(Entity holder); + boolean removeLeash(UUID holderUUID); + boolean removeLeash(BlockPos knotPos); + + void removeAllLeashes(); + void removeAllHolderLeashes(); + void removeAllKnotLeashes(); + + /* ---------------------- + * Modify leash properties + * ---------------------- */ + boolean setMaxDistance(Entity holder, double distance); + boolean setMaxDistance(Entity holder, double distance, int maxKeepTicks); + boolean setMaxDistance(Entity holder, double distance, int maxKeepTicks, String reserved); + + boolean setMaxDistance(UUID holderUUID, double distance); + boolean setMaxDistance(UUID holderUUID, double distance, int maxKeepTicks); + boolean setMaxDistance(UUID holderUUID, double distance, int maxKeepTicks, String reserved); + + boolean setMaxDistance(BlockPos knotPos, double distance); + boolean setMaxDistance(BlockPos knotPos, double distance, int maxKeepTicks); + boolean setMaxDistance(BlockPos knotPos, double distance, int maxKeepTicks, String reserved); + + boolean setElasticDistance(Entity holder, double distance); + boolean setElasticDistance(Entity holder, double distance, int maxKeepTicks); + boolean setElasticDistance(Entity holder, double distance, int maxKeepTicks, String reserved); + + boolean setElasticDistance(UUID holderUUID, double distance); + boolean setElasticDistance(UUID holderUUID, double distance, int maxKeepTicks); + boolean setElasticDistance(UUID holderUUID, double distance, int maxKeepTicks, String reserved); + + boolean setElasticDistance(BlockPos knotPos, double distance); + boolean setElasticDistance(BlockPos knotPos, double distance, int maxKeepTicks); + boolean setElasticDistance(BlockPos knotPos, double distance, int maxKeepTicks, String reserved); + + /* ---------------------- + * Apply physics + * ---------------------- */ + void applyLeashForces(); + + /* ---------------------- + * Transfer leash holders + * ---------------------- */ + boolean transferLeash(Entity holder, Entity newHolder); + boolean transferLeash(Entity holder, Entity newHolder, String reserved); + + boolean transferLeash(UUID holderUUID, Entity newHolder); + boolean transferLeash(UUID holderUUID, Entity newHolder, String reserved); + + boolean transferLeash(BlockPos knotPos, Entity newHolder); + boolean transferLeash(BlockPos knotPos, Entity newHolder, String reserved); + + /* ---------------------- + * Query state + * ---------------------- */ + boolean hasLeash(); + boolean hasKnotLeash(); + boolean hasHolderLeash(); + + Collection getAllLeashes(); + + boolean isLeashedBy(Entity holder); + boolean isLeashedBy(UUID holderUUID); + boolean isLeashedBy(BlockPos knotPos); + + boolean isInDelayedLeash(UUID holderUUID); + + Optional getLeashInfo(Entity holder); + Optional getLeashInfo(UUID holderUUID); + Optional getLeashInfo(BlockPos knotPos); + + boolean canBeLeashed(); + boolean canBeAttachedTo(Entity entity); + + /* ---------------------- + * Occupy / sync + * ---------------------- */ + /** + * 抢占位(已离线玩家)。 + * 用于解决玩家下线后所持有对象会移除持有者的问题(实际上是占用个弱集合) + */ + Optional occupyLeash(); + + void markForSync(); + void immediateSync(); + void checkSync(); + + /* ---------------------- + * Data record + * ---------------------- */ + record LeashInfo( + Optional blockPosOpt, + Optional holderUUIDOpt, + Optional holderIdOpt, // Only for client side use + String reserved, // 保留字段 + Vec3 attachOffset, + double maxDistance, + double elasticDistance, + int keepLeashTicks, // 剩余 Tick 数 + int maxKeepLeashTicks // 最大保持 Tick 数 + ) { + public static final LeashInfo EMPTY = new LeashInfo( + Optional.empty(), Optional.empty(), Optional.empty(), + "", Vec3.ZERO, 12.0D, 6.0D, 0, 0 + ); + + /* ---------- Factory ---------- */ + public static LeashInfo create( + Entity entity, + String reserved, + Vec3 offset, + double maxDistance, + double elasticDistance, + int keepTicks, + int maxKeepTicks + ) { + return entity instanceof SuperLeashKnotEntity knot + ? new LeashInfo(knot.getPos(), entity.getId(), reserved, + offset, maxDistance, elasticDistance, keepTicks, maxKeepTicks) + : new LeashInfo(entity.getUUID(), entity.getId(), reserved, + offset, maxDistance, elasticDistance, keepTicks, maxKeepTicks); + } + + public LeashInfo(UUID holderUUID, int holderId, String reserved, Vec3 offset, + double maxDistance, double elasticDistance, int keepTicks, int maxKeepTicks) { + this(Optional.empty(), Optional.of(holderUUID), Optional.of(holderId), + reserved, offset, maxDistance, elasticDistance, keepTicks, maxKeepTicks); + } + + public LeashInfo(BlockPos knotPos, int holderId, String reserved, Vec3 offset, + double maxDistance, double elasticDistance, int keepTicks, int maxKeepTicks) { + this(Optional.of(knotPos), Optional.empty(), Optional.of(holderId), + reserved, offset, maxDistance, elasticDistance, keepTicks, maxKeepTicks); + } + + /* ---------- State updates ---------- */ + public LeashInfo decrementKeepTicks() { + return new LeashInfo(blockPosOpt, holderUUIDOpt, holderIdOpt, reserved, attachOffset, + maxDistance, elasticDistance, + Math.max(0, keepLeashTicks - 1), maxKeepLeashTicks); + } + + public LeashInfo resetKeepTicks() { + return new LeashInfo(blockPosOpt, holderUUIDOpt, holderIdOpt, reserved, attachOffset, + maxDistance, elasticDistance, + maxKeepLeashTicks, maxKeepLeashTicks); + } + + public LeashInfo transferHolder(Entity entity) { + return transferHolder(entity, reserved); + } + + public LeashInfo transferHolder(Entity entity, String newReserved) { + boolean isKnot = entity instanceof SuperLeashKnotEntity; + return new LeashInfo( + isKnot ? Optional.of(((SuperLeashKnotEntity) entity).getPos()) : Optional.empty(), + !isKnot ? Optional.of(entity.getUUID()) : Optional.empty(), + Optional.of(entity.getId()), + newReserved, attachOffset, maxDistance, elasticDistance, + keepLeashTicks, maxKeepLeashTicks + ); + } + } +} \ No newline at end of file diff --git a/src/main/java/top/r3944realms/superleadrope/content/capability/inter/ILeashDataCapability.java b/src/main/java/top/r3944realms/superleadrope/content/capability/inter/ILeashDataCapability.java deleted file mode 100644 index e27f8fe..0000000 --- a/src/main/java/top/r3944realms/superleadrope/content/capability/inter/ILeashDataCapability.java +++ /dev/null @@ -1,184 +0,0 @@ -/* - * Super Lead rope mod - * Copyright (C) 2025 R3944Realms - * 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 . - */ - -package top.r3944realms.superleadrope.content.capability.inter; - -import net.minecraft.core.BlockPos; -import net.minecraft.nbt.CompoundTag; -import net.minecraft.world.entity.Entity; -import net.minecraft.world.entity.player.Player; -import net.minecraft.world.item.ItemStack; -import net.minecraft.world.phys.Vec3; -import net.minecraftforge.common.util.INBTSerializable; -import top.r3944realms.superleadrope.content.entity.SuperLeashKnotEntity; - -import java.util.Collection; -import java.util.Optional; -import java.util.UUID; - -public interface ILeashDataCapability extends INBTSerializable { - // 原LeashData的方法接口 - boolean addLeash(Entity holder, ItemStack leashStack, double maxDistance); - boolean addLeash(Entity holder, ItemStack leashStack, double maxDistance, double elasticDistance, int maxKeepLeashTicks); - boolean addLeash(Entity holder, LeashInfo leashInfo); - boolean addDelayedLeash(Player holderPlayer); - boolean removeDelayedLeash(UUID onceHolderPlayerUUID); - boolean setMaxDistance(Entity holder, double newMaxDistance); - boolean setMaxDistance(Entity holder,double newMaxDistance, int newMaxKeepLeashTicks); - boolean setMaxDistance(UUID holderUUID, double newMaxDistance); - boolean setMaxDistance(UUID holderUUID, double newMaxDistance, int newMaxKeepLeashTicks); - boolean setMaxDistance(BlockPos knotPos, double newMaxDistance); - boolean setMaxDistance(BlockPos knotPos, double newMaxDistance, int newMaxKeepLeashTicks); - boolean setElasticDistance(Entity holder, double newElasticDistance); - boolean setElasticDistance(UUID holderUUID, double newElasticDistance); - boolean setElasticDistance(BlockPos knotPos, double newElasticDistance); - // 动态修改弹性距离 - boolean setElasticDistance(Entity holder, double newElasticDistance, int newMaxKeepLeashTicks); - boolean setElasticDistance(UUID holderUUID, double newElasticDistance, int newMaxKeepLeashTicks); - boolean setElasticDistance(BlockPos knotPos, double newElasticDistance, int newMaxKeepLeashTicks); - - void applyLeashForces(); - boolean removeLeash(Entity holder); - boolean removeLeash(UUID holderUUID); - boolean removeLeash(BlockPos knotPos); - void removeAllLeashes(); - void removeAllHolderLeashes(); - void removeAllKnotLeashes(); - - boolean transferLeash(Entity holder, Entity newHolder); - boolean transferLeash(Entity holder, Entity newHolder, ItemStack stack); - boolean transferLeash(UUID holderUUID, Entity newHolder); - boolean transferLeash(UUID holderUUID, Entity newHolder, ItemStack stack); - boolean transferLeash(BlockPos knotPos, Entity newHolder); - boolean transferLeash(BlockPos knotPos, Entity newHolder, ItemStack stack); - - // 查询方法 - boolean hasLeash(); - boolean hasKnotLeash(); - boolean hasHolderLeash(); - Collection getAllLeashes(); - boolean isLeashedBy(Entity holder); - boolean isLeashedBy(UUID holderUUID); - boolean isLeashedBy(BlockPos knotPos); - boolean isInDelayedLeash(UUID holderUUID); - Optional getLeashInfo(Entity holder); - Optional getLeashInfo(UUID holderUUID); - Optional getLeashInfo(BlockPos knotPos); - - boolean canBeLeashed(); - - /** - * 抢占位(已离线玩家) - * 用于解决玩家下线后所持有我对象,会移除持有者的问题(实际上是占用个弱集合) - */ - Optional occupyLeash(); - boolean canBeAttachedTo(Entity pEntity); - void markForSync(); - void immediateSync(); - void checkSync(); - - record LeashInfo( - Optional blockPosOpt, - Optional holderUUIDOpt, - Optional holderIdOpt,//Only for client side use - String reserved, //保留字段 - Vec3 attachOffset, - double maxDistance, - double elasticDistance, - int keepLeashTicks, // 新增:保持拴绳的剩余Tick数 - int maxKeepLeashTicks // 新增:最大保持Tick数(可配置) - ) { - public static final LeashInfo EMPTY = new LeashInfo( - Optional.empty(), Optional.empty(), Optional.empty(), - "", Vec3.ZERO, 12.0D, 6.0D, 0, 0 - ); - public static LeashInfo CreateLeashInfo( - Entity entity, - String reserved, - Vec3 attachOffset, - double maxDistance, - double elasticDistance, - int keepLeashTicks, - int maxKeepLeashTicks - ) { - return entity instanceof SuperLeashKnotEntity superLeashKnot ? - new LeashInfo(superLeashKnot.getPos(), entity.getId(), reserved, attachOffset, maxDistance, elasticDistance, keepLeashTicks, maxKeepLeashTicks) : - new LeashInfo(entity.getUUID(), entity.getId(), reserved, attachOffset, maxDistance, elasticDistance, keepLeashTicks, maxKeepLeashTicks); - } - public LeashInfo( - UUID holderUUID, - int holderId, - String reserved, - Vec3 attachOffset, - double maxDistance, - double elasticDistance, - int keepLeashTicks, - int maxKeepLeashTicks - ) { - this(Optional.empty() ,Optional.of(holderUUID), Optional.of(holderId), reserved, attachOffset, maxDistance, elasticDistance, keepLeashTicks, maxKeepLeashTicks); - } - public LeashInfo( - BlockPos knotPos, - int holderId, - String reserved, - Vec3 attachOffset, - double maxDistance, - double elasticDistance, - int keepLeashTicks, - int maxKeepLeashTicks - ) { - this(Optional.of(knotPos), Optional.empty(), Optional.of(holderId), reserved, attachOffset, maxDistance, elasticDistance, keepLeashTicks, maxKeepLeashTicks); - } - - // 返回一个减少剩余Tick的新实例 - public LeashInfo decrementKeepLeashTicks() { - return new LeashInfo( - blockPosOpt, holderUUIDOpt, holderIdOpt, reserved, attachOffset, - maxDistance, elasticDistance, - Math.max(0, keepLeashTicks - 1), - maxKeepLeashTicks - ); - } - - // 重置Tick为最大值 - public LeashInfo resetKeepLeashTicks() { - return new LeashInfo( - blockPosOpt, holderUUIDOpt, holderIdOpt, reserved, attachOffset, - maxDistance, elasticDistance, - maxKeepLeashTicks, - maxKeepLeashTicks - ); - } - public LeashInfo transferHolder (Entity entity) { - boolean isSuperKnot = entity instanceof SuperLeashKnotEntity; - return new LeashInfo( - isSuperKnot ? Optional.of(((SuperLeashKnotEntity) entity).getPos()) : Optional.empty(), - !isSuperKnot ? Optional.of(entity.getUUID()) : Optional.empty(), - Optional.of(entity.getId()), - reserved, attachOffset, maxDistance,elasticDistance, keepLeashTicks, maxKeepLeashTicks - ); - } - public LeashInfo transferHolder (Entity entity, String reserved) { - boolean isSuperKnot = entity instanceof SuperLeashKnotEntity; - return new LeashInfo( - isSuperKnot ? Optional.of(((SuperLeashKnotEntity) entity).getPos()) : Optional.empty(), - !isSuperKnot ? Optional.of(entity.getUUID()) : Optional.empty(), - Optional.of(entity.getId()), - reserved, attachOffset, maxDistance,elasticDistance, keepLeashTicks, maxKeepLeashTicks - ); - } - - } -} diff --git a/src/main/java/top/r3944realms/superleadrope/content/capability/inter/ILeashState.java b/src/main/java/top/r3944realms/superleadrope/content/capability/inter/ILeashState.java new file mode 100644 index 0000000..7a0aaf5 --- /dev/null +++ b/src/main/java/top/r3944realms/superleadrope/content/capability/inter/ILeashState.java @@ -0,0 +1,111 @@ +/* + * Super Lead rope mod + * Copyright (C) 2025 R3944Realms + * 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 . + */ + +package top.r3944realms.superleadrope.content.capability.inter; + +import net.minecraft.core.BlockPos; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.phys.Vec3; +import net.minecraftforge.common.util.INBTSerializable; + +import java.util.Map; +import java.util.Optional; +import java.util.UUID; +/** + * Capability interface for managing leash states of entities and knots. + */ +public interface ILeashState extends INBTSerializable { + + /* ---------------------- + * Query leash states + * ---------------------- */ + Map getHolderLeashStates(); + Map getKnotLeashStates(); + + Optional getLeashState(Entity entity); + Optional getLeashState(UUID uuid); + Optional getLeashState(BlockPos pos); + + /* ---------------------- + * Reset holder offsets + * ---------------------- */ + void resetAllLeashHolderLocationsOffset(); + void resetLeashHolderLocationOffset(Entity holder); + void resetLeashHolderLocationOffset(UUID holderUUID); + void resetLeashHolderLocationOffset(BlockPos knotPos); + + /* ---------------------- + * Set holder offsets + * ---------------------- */ + void setLeashHolderLocationOffset(Entity holder, Vec3 offset); + void setLeashHolderLocationOffset(UUID holderUUID, Vec3 offset); + void setLeashHolderLocationOffset(BlockPos knotPos, Vec3 offset); + + /* ---------------------- + * Add holder offsets + * ---------------------- */ + void addLeashHolderLocationOffset(Entity holder, Vec3 offset); + void addLeashHolderLocationOffset(UUID holderUUID, Vec3 offset); + void addLeashHolderLocationOffset(BlockPos knotPos, Vec3 offset); + + /* ---------------------- + * Remove holder offsets + * ---------------------- */ + void removeLeashHolderLocationOffset(Entity holder); + void removeLeashHolderLocationOffset(UUID holderUUID); + void removeLeashHolderLocationOffset(BlockPos knotPos); + + /* ---------------------- + * Apply-entity offset + * ---------------------- */ + Optional getLeashApplyEntityLocationOffset(); + Vec3 getDefaultLeashApplyEntityLocationOffset(); + + void resetAllLeashApplyEntityLocationsOffset(); + void removeLeashApplyEntityLocationOffset(); + void setLeashApplyEntityLocationOffset(Vec3 offset); + void addLeashApplyEntityLocationOffset(Vec3 offset); + + /* ---------------------- + * Utility & sync + * ---------------------- */ + void copy(ILeashState other, Entity newEntity); + + void markForSync(); + void immediateSync(); + void checkSync(); + + /* ---------------------- + * Data record + * ---------------------- */ + record LeashState( + Vec3 holderLocationOffset, + Vec3 applyEntityLocationOffset, + Vec3 defaultHolderLocationOffset + ) { + public LeashState resetHolderLocationOffset() { + return new LeashState(defaultHolderLocationOffset, applyEntityLocationOffset, defaultHolderLocationOffset); + } + + public LeashState setHolderLocationOffset(Vec3 holderLocationOffset) { + return new LeashState(holderLocationOffset, applyEntityLocationOffset, defaultHolderLocationOffset); + } + + public LeashState setApplyEntityLocationOffset(Vec3 applyEntityLocationOffset) { + return new LeashState(holderLocationOffset, applyEntityLocationOffset, defaultHolderLocationOffset); + } + } +} diff --git a/src/main/java/top/r3944realms/superleadrope/content/capability/provider/LeashDataProvider.java b/src/main/java/top/r3944realms/superleadrope/content/capability/provider/LeashDataProvider.java index dab9933..ecedaaa 100644 --- a/src/main/java/top/r3944realms/superleadrope/content/capability/provider/LeashDataProvider.java +++ b/src/main/java/top/r3944realms/superleadrope/content/capability/provider/LeashDataProvider.java @@ -27,12 +27,12 @@ import org.jetbrains.annotations.Nullable; import top.r3944realms.superleadrope.SuperLeadRope; import top.r3944realms.superleadrope.content.capability.CapabilityHandler; import top.r3944realms.superleadrope.content.capability.impi.LeashDataImpl; -import top.r3944realms.superleadrope.content.capability.inter.ILeashDataCapability; +import top.r3944realms.superleadrope.content.capability.inter.ILeashData; public class LeashDataProvider implements ICapabilitySerializable { public static final ResourceLocation LEASH_DATA_REL = new ResourceLocation(SuperLeadRope.MOD_ID, "leash_data"); - private final ILeashDataCapability instance; - private final LazyOptional optional; + private final ILeashData instance; + private final LazyOptional optional; public LeashDataProvider(Entity entity) { this.instance = new LeashDataImpl(entity); this.optional = LazyOptional.of(() -> instance); diff --git a/src/main/java/top/r3944realms/superleadrope/content/capability/provider/LeashStateProvider.java b/src/main/java/top/r3944realms/superleadrope/content/capability/provider/LeashStateProvider.java new file mode 100644 index 0000000..26e2919 --- /dev/null +++ b/src/main/java/top/r3944realms/superleadrope/content/capability/provider/LeashStateProvider.java @@ -0,0 +1,54 @@ +/* + * Super Lead rope mod + * Copyright (C) 2025 R3944Realms + * 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 . + */ + +package top.r3944realms.superleadrope.content.capability.provider; + +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.Capability; +import net.minecraftforge.common.capabilities.ICapabilitySerializable; +import net.minecraftforge.common.util.LazyOptional; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import top.r3944realms.superleadrope.SuperLeadRope; +import top.r3944realms.superleadrope.content.capability.CapabilityHandler; +import top.r3944realms.superleadrope.content.capability.impi.LeashStateImpl; +import top.r3944realms.superleadrope.content.capability.inter.ILeashState; + +public class LeashStateProvider implements ICapabilitySerializable { + public static final ResourceLocation LEASH_STATE_REL = new ResourceLocation(SuperLeadRope.MOD_ID, "leash_state"); + private final ILeashState instance; + private final LazyOptional optional; + public LeashStateProvider(Entity entity) { + this.instance = new LeashStateImpl(entity); + this.optional = LazyOptional.of(() -> instance); + } + @Override + public @NotNull LazyOptional getCapability(@NotNull Capability cap, @Nullable Direction side) { + return CapabilityHandler.LEASH_STATE_CAP.orEmpty(cap, optional); + } + + @Override + public CompoundTag serializeNBT() { + return instance.serializeNBT(); + } + + @Override + public void deserializeNBT(CompoundTag nbt) { + instance.deserializeNBT(nbt); + } +} diff --git a/src/main/java/top/r3944realms/superleadrope/content/command/Command.java b/src/main/java/top/r3944realms/superleadrope/content/command/Command.java new file mode 100644 index 0000000..105af0f --- /dev/null +++ b/src/main/java/top/r3944realms/superleadrope/content/command/Command.java @@ -0,0 +1,37 @@ +/* + * Super Lead rope mod + * Copyright (C) 2025 R3944Realms + * 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 . + */ + +package top.r3944realms.superleadrope.content.command; + +import com.mojang.brigadier.builder.LiteralArgumentBuilder; +import net.minecraft.commands.CommandSourceStack; +import net.minecraft.commands.Commands; +import org.jetbrains.annotations.Nullable; +import top.r3944realms.superleadrope.config.LeashCommonConfig; + +import java.util.List; + +public class Command { + public static final String PREFIX = LeashCommonConfig.COMMON.SLPModCommandPrefix.get(); + public static boolean SHOULD_USE_PREFIX = LeashCommonConfig.COMMON.EnableSLPModCommandPrefix.get(); + static LiteralArgumentBuilder getLiterArgumentBuilderOfCSS(String name, boolean shouldAddToList, @Nullable List> list) { + LiteralArgumentBuilder literal = Commands.literal(name); + if (shouldAddToList) { + assert list != null; + list.add(literal); + } + return literal; + } +} \ No newline at end of file diff --git a/src/main/java/top/r3944realms/superleadrope/content/command/LeashDataCommand.java b/src/main/java/top/r3944realms/superleadrope/content/command/LeashDataCommand.java new file mode 100644 index 0000000..a9366bb --- /dev/null +++ b/src/main/java/top/r3944realms/superleadrope/content/command/LeashDataCommand.java @@ -0,0 +1,28 @@ +/* + * Super Lead rope mod + * Copyright (C) 2025 R3944Realms + * 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 . + */ + +package top.r3944realms.superleadrope.content.command; + +import com.mojang.brigadier.CommandDispatcher; +import net.minecraft.commands.CommandSourceStack; + +public class LeashDataCommand { + // 获取Data + // 设置Data + // Holder> + // 设置对应目标的 最大长度 最长断裂距离 保持不断裂时间刻 + public static void register(CommandDispatcher dispatcher) { + } +} diff --git a/src/main/java/top/r3944realms/superleadrope/content/command/LeashStateCommand.java b/src/main/java/top/r3944realms/superleadrope/content/command/LeashStateCommand.java new file mode 100644 index 0000000..fd88029 --- /dev/null +++ b/src/main/java/top/r3944realms/superleadrope/content/command/LeashStateCommand.java @@ -0,0 +1,29 @@ +/* + * Super Lead rope mod + * Copyright (C) 2025 R3944Realms + * 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 . + */ + +package top.r3944realms.superleadrope.content.command; + +import com.mojang.brigadier.CommandDispatcher; +import net.minecraft.commands.CommandSourceStack; + +public class LeashStateCommand { + // 获取State + // 设置State + // Holder> + // 设置对应目标的 拴绳偏移 + public static void register(CommandDispatcher dispatcher) { + + } +} diff --git a/src/main/java/top/r3944realms/superleadrope/content/command/MotionCommand.java b/src/main/java/top/r3944realms/superleadrope/content/command/MotionCommand.java new file mode 100644 index 0000000..ff18574 --- /dev/null +++ b/src/main/java/top/r3944realms/superleadrope/content/command/MotionCommand.java @@ -0,0 +1,173 @@ +/* + * Super Lead rope mod + * Copyright (C) 2025 R3944Realms + * 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 . + */ + +package top.r3944realms.superleadrope.content.command; + +import com.mojang.brigadier.Command; +import com.mojang.brigadier.CommandDispatcher; +import com.mojang.brigadier.arguments.DoubleArgumentType; +import com.mojang.brigadier.builder.LiteralArgumentBuilder; +import net.minecraft.commands.CommandSourceStack; +import net.minecraft.commands.Commands; +import net.minecraft.commands.arguments.EntityArgument; +import net.minecraft.network.chat.Component; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.phys.Vec3; +import org.jetbrains.annotations.Nullable; +import top.r3944realms.superleadrope.SuperLeadRope; +import top.r3944realms.superleadrope.network.NetworkHandler; +import top.r3944realms.superleadrope.network.toClient.UpdatePlayerMovementPacket; + +import java.util.ArrayList; +import java.util.List; + +import static top.r3944realms.superleadrope.content.command.Command.*; + + +public class MotionCommand { + private final static String SLP_MOTION_MESSAGE_ = SuperLeadRope.MOD_ID + ".command.motion.message."; + public final static String MOTION_SETTER_SUCCESSFUL = SLP_MOTION_MESSAGE_ + "setter.successful", + MOTION_ADDER_SUCCESSFUL = SLP_MOTION_MESSAGE_ + "adder.successful", + MOTION_MULTIPLY_SUCCESSFUL = SLP_MOTION_MESSAGE_ + "multiply.successful"; + public static void register(CommandDispatcher dispatcher) { + @Nullable List> nodeList = SHOULD_USE_PREFIX ? null : new ArrayList<>(); + LiteralArgumentBuilder literalArgumentBuilder = Commands.literal(PREFIX); + LiteralArgumentBuilder $$motionRoot = getLiterArgumentBuilderOfCSS("motion", !SHOULD_USE_PREFIX, nodeList); + com.mojang.brigadier.Command motionVecAdder = context -> { + CommandSourceStack source = context.getSource(); + for(Entity entity : EntityArgument.getEntities(context, "targets")){ + Vec3 motionVec = new Vec3( + DoubleArgumentType.getDouble(context, "vecX"), + DoubleArgumentType.getDouble(context, "vecY"), + DoubleArgumentType.getDouble(context, "vecZ") + ); + boolean flag = entity instanceof ServerPlayer; + if(entity instanceof ServerPlayer player) { + NetworkHandler.sendToPlayer(new UpdatePlayerMovementPacket(UpdatePlayerMovementPacket.Operation.ADD, motionVec.x, motionVec.y, motionVec.z), player); + } else { + entity.addDeltaMovement(motionVec); + } + Vec3 deltaMovement = entity.getDeltaMovement(); + double vecX = deltaMovement.x, vecY = deltaMovement.y, vecZ = deltaMovement.z; + source.sendSuccess(() -> + Component.translatable( + MOTION_ADDER_SUCCESSFUL, + entity.getDisplayName(), + flag ? vecX + motionVec.x : vecX, + flag ? vecY + motionVec.y : vecY, + flag ? vecZ + motionVec.z : vecZ + ), true + ); + } + return 0; + }; + Command motionVecSetter = context -> { + CommandSourceStack source = context.getSource(); + for(Entity entity : EntityArgument.getEntities(context, "targets")){ + Vec3 motionVec = new Vec3( + DoubleArgumentType.getDouble(context, "vecX"), + DoubleArgumentType.getDouble(context, "vecY"), + DoubleArgumentType.getDouble(context, "vecZ") + ); + boolean flag = entity instanceof ServerPlayer; + if(entity instanceof ServerPlayer player) { + NetworkHandler.sendToPlayer(new UpdatePlayerMovementPacket(UpdatePlayerMovementPacket.Operation.SET, motionVec.x, motionVec.y, motionVec.z), player); + } else { + entity.setDeltaMovement(motionVec); + } + double vecX = entity.getDeltaMovement().x, vecY = entity.getDeltaMovement().y, vecZ = entity.getDeltaMovement().z; + source.sendSuccess(() -> + Component.translatable( + MOTION_SETTER_SUCCESSFUL, + entity.getDisplayName(), + flag ? motionVec.x : vecX, + flag ? motionVec.y : vecY, + flag ? motionVec.z : vecZ + ), true + ); + } + return 0; + }; + Command motionVecMultiply = context -> { + CommandSourceStack source = context.getSource(); + for(Entity entity : EntityArgument.getEntities(context, "targets")){ + Vec3 motionFactorVec = new Vec3( + DoubleArgumentType.getDouble(context, "vecXFactor"), + DoubleArgumentType.getDouble(context, "vecYFactor"), + DoubleArgumentType.getDouble(context, "vecZFactor") + ); + boolean flag = entity instanceof ServerPlayer; + Vec3 deltaMovement = entity.getDeltaMovement(); + if(entity instanceof ServerPlayer player) { + NetworkHandler.sendToPlayer(new UpdatePlayerMovementPacket(UpdatePlayerMovementPacket.Operation.MULTIPLY, motionFactorVec.x, motionFactorVec.y, motionFactorVec.z), player); + } else { + entity.setDeltaMovement(deltaMovement.multiply(motionFactorVec)); + } + double vecX = deltaMovement.x, vecY = deltaMovement.y, vecZ = deltaMovement.z; + source.sendSuccess(() -> + Component.translatable( + MOTION_MULTIPLY_SUCCESSFUL, + entity.getDisplayName(), + flag ? vecX * motionFactorVec.x : vecX, + flag ? vecY * motionFactorVec.y : vecY, + flag ? vecZ * motionFactorVec.z : vecZ + ), true + ); + } + return 0; + }; + + LiteralArgumentBuilder Motion = $$motionRoot.requires(cs -> cs.hasPermission(2)) + .then(Commands.argument("targets", EntityArgument.entities()) + .then(Commands.literal("add") + .then(Commands.argument("vecX", DoubleArgumentType.doubleArg()) + .then(Commands.argument("vecY", DoubleArgumentType.doubleArg()) + .then(Commands.argument("vecZ", DoubleArgumentType.doubleArg()) + .executes(motionVecAdder) + ) + ) + ) + ) + .then(Commands.literal("set") + .then(Commands.argument("vecX", DoubleArgumentType.doubleArg()) + .then(Commands.argument("vecY", DoubleArgumentType.doubleArg()) + .then(Commands.argument("vecZ", DoubleArgumentType.doubleArg()) + .executes(motionVecSetter) + ) + ) + ) + ) + .then(Commands.literal("multiply") + .then(Commands.argument("vecXFactor", DoubleArgumentType.doubleArg()) + .then(Commands.argument("vecYFactor", DoubleArgumentType.doubleArg()) + .then(Commands.argument("vecZFactor", DoubleArgumentType.doubleArg()) + .executes(motionVecMultiply) + ) + ) + ) + ) + ); + if(SHOULD_USE_PREFIX){ + literalArgumentBuilder.then(Motion); + dispatcher.register(literalArgumentBuilder); + } else { + if (nodeList != null) { + nodeList.forEach(dispatcher::register); + } + } + } +} + diff --git a/src/main/java/top/r3944realms/superleadrope/content/entity/SuperLeashKnotEntity.java b/src/main/java/top/r3944realms/superleadrope/content/entity/SuperLeashKnotEntity.java index ad7903c..f7d9fa4 100644 --- a/src/main/java/top/r3944realms/superleadrope/content/entity/SuperLeashKnotEntity.java +++ b/src/main/java/top/r3944realms/superleadrope/content/entity/SuperLeashKnotEntity.java @@ -37,6 +37,7 @@ import org.jetbrains.annotations.NotNull; import top.r3944realms.superleadrope.content.capability.CapabilityHandler; import top.r3944realms.superleadrope.content.capability.impi.LeashDataImpl; import top.r3944realms.superleadrope.core.register.SLPEntityTypes; +import top.r3944realms.superleadrope.util.capability.LeashUtil; import java.util.Arrays; import java.util.List; @@ -81,9 +82,9 @@ public class SuperLeashKnotEntity extends LeashFenceKnotEntity { this.markHurt(); this.playSound(SoundEvents.LEASH_KNOT_BREAK); List entities = LeashDataImpl.leashableInArea(this.level(), pos.getCenter(), entity -> LeashDataImpl.isLeashHolder(entity, this)); - entities.forEach(entity -> entity - .getCapability(CapabilityHandler.LEASH_DATA_CAP) - .map(iLeashDataCapability -> iLeashDataCapability.removeLeash(this)) + entities.forEach(entity -> + LeashUtil.getLeashData(entity) + .map(iLeashDataCapability -> iLeashDataCapability.removeLeash(this)) ); } @@ -168,11 +169,11 @@ public class SuperLeashKnotEntity extends LeashFenceKnotEntity { List entities = LeashDataImpl.leashableInArea(player); for(Entity entity : entities) { if (LeashDataImpl.isLeashHolder(entity, player.getUUID())) - entity.getCapability(CapabilityHandler.LEASH_DATA_CAP).ifPresent(i -> { - i.transferLeash(player.getUUID(), this); - isTransferLeash.set(true); - }); - + LeashUtil.getLeashData(entity) + .ifPresent(i -> { + i.transferLeash(player.getUUID(), this); + isTransferLeash.set(true); + }); } AtomicBoolean isRemoveLeashKnot = new AtomicBoolean(false); if (!isTransferLeash.get()) { @@ -180,14 +181,14 @@ public class SuperLeashKnotEntity extends LeashFenceKnotEntity { this.playSound(SoundEvents.LEASH_KNOT_BREAK); this.discard(); List entities1 = LeashDataImpl.leashableInArea(this); - entities1.forEach( - entity -> entity - .getCapability(CapabilityHandler.LEASH_DATA_CAP) - .ifPresent(iLeashDataCapability -> { - iLeashDataCapability.removeLeash(this); - isRemoveLeashKnot.set(true); - } - )); + entities1.forEach(entity -> + LeashUtil.getLeashData(entity) + .ifPresent(iLeashDataCapability -> { + iLeashDataCapability.removeLeash(this); + isRemoveLeashKnot.set(true); + } + ) + ); } } else { this.playSound(SoundEvents.LEASH_KNOT_PLACE); diff --git a/src/main/java/top/r3944realms/superleadrope/content/gamerule/SLPGamerules.java b/src/main/java/top/r3944realms/superleadrope/content/gamerule/SLPGamerules.java index e15bda1..e1941ae 100644 --- a/src/main/java/top/r3944realms/superleadrope/content/gamerule/SLPGamerules.java +++ b/src/main/java/top/r3944realms/superleadrope/content/gamerule/SLPGamerules.java @@ -24,8 +24,7 @@ public class SLPGamerules { public static final SLPGameruleRegistry GAMERULE_REGISTRY = SLPGameruleRegistry.INSTANCE; public static final HashMap gamerulesBooleanValuesClient = new HashMap<>(); public static final HashMap gameruleIntegerValuesClient = new HashMap<>(); - public static final HashMap gameruleFloatValuesClient = new HashMap<>(); - public static final String RULE_KEY_PERFix_ = "gamerule." + GAMERULE_PREFIX; + public static final String RULE_KEY_PERFix_ = "gamerule." + GAMERULE_PREFIX.toLowerCase(); public static String getDescriptionKey(Class gameRuleClass) { return RULE_KEY_PERFix_ + gameRuleClass.getSimpleName() + ".description"; } diff --git a/src/main/java/top/r3944realms/superleadrope/content/gamerule/server/TeleportWithLeashedPlayers.java b/src/main/java/top/r3944realms/superleadrope/content/gamerule/server/TeleportWithLeashedEntities.java similarity index 89% rename from src/main/java/top/r3944realms/superleadrope/content/gamerule/server/TeleportWithLeashedPlayers.java rename to src/main/java/top/r3944realms/superleadrope/content/gamerule/server/TeleportWithLeashedEntities.java index ca62fc1..4fa4f39 100644 --- a/src/main/java/top/r3944realms/superleadrope/content/gamerule/server/TeleportWithLeashedPlayers.java +++ b/src/main/java/top/r3944realms/superleadrope/content/gamerule/server/TeleportWithLeashedEntities.java @@ -20,11 +20,11 @@ import top.r3944realms.superleadrope.content.gamerule.SLPGamerules; import static top.r3944realms.superleadrope.content.gamerule.SLPGamerules.GAMERULE_REGISTRY; -public class TeleportWithLeashedPlayers { +public class TeleportWithLeashedEntities { public static final boolean DEFAULT_VALUE = true; - public static final String ID = SLPGamerules.getGameruleName(TeleportWithLeashedPlayers.class); - public static final String DESCRIPTION_KEY = SLPGamerules.getDescriptionKey(TeleportWithLeashedPlayers.class); - public static final String NAME_KEY = SLPGamerules.getNameKey(TeleportWithLeashedPlayers.class); + public static final String ID = SLPGamerules.getGameruleName(TeleportWithLeashedEntities.class); + public static final String DESCRIPTION_KEY = SLPGamerules.getDescriptionKey(TeleportWithLeashedEntities.class); + public static final String NAME_KEY = SLPGamerules.getNameKey(TeleportWithLeashedEntities.class); public static final GameRules.Category CATEGORY = GameRules.Category.PLAYER; public static void register() { diff --git a/src/main/java/top/r3944realms/superleadrope/content/item/SuperLeadRopeItem.java b/src/main/java/top/r3944realms/superleadrope/content/item/SuperLeadRopeItem.java index bf6c238..02d6911 100644 --- a/src/main/java/top/r3944realms/superleadrope/content/item/SuperLeadRopeItem.java +++ b/src/main/java/top/r3944realms/superleadrope/content/item/SuperLeadRopeItem.java @@ -16,6 +16,7 @@ package top.r3944realms.superleadrope.content.item; import net.minecraft.core.BlockPos; +import net.minecraft.sounds.SoundSource; import net.minecraft.world.InteractionHand; import net.minecraft.world.InteractionResult; import net.minecraft.world.InteractionResultHolder; @@ -33,11 +34,13 @@ import org.jetbrains.annotations.NotNull; import top.r3944realms.superleadrope.content.SLPToolTier; import top.r3944realms.superleadrope.content.capability.CapabilityHandler; import top.r3944realms.superleadrope.content.capability.impi.LeashDataImpl; -import top.r3944realms.superleadrope.content.capability.inter.ILeashDataCapability; +import top.r3944realms.superleadrope.content.capability.inter.ILeashData; import top.r3944realms.superleadrope.content.entity.SuperLeashKnotEntity; import top.r3944realms.superleadrope.core.register.SLPSoundEvents; +import top.r3944realms.superleadrope.util.capability.LeashUtil; import java.util.List; +import java.util.Optional; import java.util.UUID; import java.util.concurrent.atomic.AtomicBoolean; @@ -76,7 +79,7 @@ public class SuperLeadRopeItem extends TieredItem implements IForgeItem { return super.use(pLevel, pPlayer, pUsedHand); } - return InteractionResultHolder.pass(lead); + return InteractionResultHolder.success(lead); } public static boolean canUse(ItemStack itemStack) { @@ -89,14 +92,14 @@ public class SuperLeadRopeItem extends TieredItem implements IForgeItem { BlockPos pos = context.getClickedPos(); BlockState state = level.getBlockState(pos); ItemStack itemStack = context.getItemInHand(); - if (canUse(itemStack)) return InteractionResult.PASS; + if (canUse(itemStack)) return InteractionResult.SUCCESS; if(SuperLeashKnotEntity.isSupportBlock(state)) { Player player = context.getPlayer(); if(!level.isClientSide && player != null) { - return bindToBlock(player, level, pos, itemStack, false) ? InteractionResult.CONSUME : InteractionResult.PASS; + return bindToBlock(player, level, pos, itemStack, false) ? InteractionResult.CONSUME : InteractionResult.SUCCESS; } } - return InteractionResult.PASS; + return InteractionResult.SUCCESS; } /** * 右键蹲下绑定到另一实体上 @@ -119,25 +122,28 @@ public class SuperLeadRopeItem extends TieredItem implements IForgeItem { * @return 是否成功 */ public static boolean bindToEntity(Entity newHolder, Player player, Level level, BlockPos pos, ItemStack leashStack) { - AtomicBoolean isSuccess = new AtomicBoolean(false); - List list = LeashDataImpl.leashableInArea(level, pos.getCenter(), entity -> LeashDataImpl.isLeashHolder(entity, player.getUUID())); - for(Entity e : list) { - AtomicBoolean canBeAttachedTo = new AtomicBoolean(false); - LazyOptional iLeashDataCapability = e.getCapability(CapabilityHandler.LEASH_DATA_CAP); - iLeashDataCapability.ifPresent(i -> canBeAttachedTo.set(i.canBeAttachedTo(newHolder))); - if(canBeAttachedTo.get()) {//canBeAttachedTo - iLeashDataCapability.ifPresent(i -> { - i.transferLeash(player.getUUID(), newHolder, leashStack); - isSuccess.set(true); - }); + boolean isSuccess = false; + + // 查找当前玩家持有的可拴生物 + List list = LeashDataImpl.leashableInArea( + level, pos.getCenter(), + entity -> LeashDataImpl.isLeashHolder(entity, player.getUUID()) + ); + + for (Entity e : list) { + Optional leashDataOpt = LeashUtil.getLeashData(e); + + if (leashDataOpt.map(i -> i.canBeAttachedTo(newHolder)).orElse(false)) { + leashDataOpt.ifPresent(i -> i.transferLeash(player.getUUID(), newHolder)); + isSuccess = true; } } - if(!isSuccess.get()) { + + if (!isSuccess) { return false; - } - else { + } else { level.gameEvent(GameEvent.ENTITY_INTERACT, pos, GameEvent.Context.of(player)); - newHolder.playSound(SLPSoundEvents.LEAD_TIED.get()); + level.playSound(null, newHolder.getOnPos(), SLPSoundEvents.LEAD_UNTIED.get(), SoundSource.PLAYERS); return true; } } @@ -155,48 +161,56 @@ public class SuperLeadRopeItem extends TieredItem implements IForgeItem { } /** * 右键蹲下绑定到支持方块上 - * @param player 明确持有玩家 - * @param level 维度世界 - * @param pos 坐标(一般是明确持有玩家的位置) - * @param leashStack 拴绳物品实例 + * + * @param player 明确持有玩家 + * @param level 维度世界 + * @param pos 坐标(一般是明确持有玩家的位置) + * @param leashStack 拴绳物品实例 * @param shouldBindSelf 是否应该触发拴自己逻辑检查 * @return 是否成功 */ public static boolean bindToBlock(Player player, Level level, BlockPos pos, ItemStack leashStack, boolean shouldBindSelf) { - //实现个加强绳结实体 SuperLeashKnotEntity knot = null; AtomicBoolean isSuccess = new AtomicBoolean(false); UUID uuid = player.getUUID(); - List list = LeashDataImpl.leashableInArea(level, pos.getCenter(), entity -> LeashDataImpl.isLeashHolder(entity, uuid)); - if (shouldBindSelf && list.isEmpty()) {//拴自己 to new knot - if (leashStack.isEmpty() || !canUse(leashStack)) return false; - knot = SuperLeashKnotEntity.getOrCreateKnot(level, pos);; + + List list = LeashDataImpl.leashableInArea(level, pos.getCenter(), + entity -> LeashDataImpl.isLeashHolder(entity, uuid)); + + // 情况一:拴自己到新 knot + if (shouldBindSelf && list.isEmpty()) { + if (leashStack.isEmpty() || !canUse(leashStack)) { + return false; + } + knot = SuperLeashKnotEntity.getOrCreateKnot(level, pos); knot.playPlacementSound(); - SuperLeashKnotEntity finalKnot1 = knot; - player.getCapability(CapabilityHandler.LEASH_DATA_CAP).ifPresent(i -> { - i.addLeash(finalKnot1, leashStack, 8D); + SuperLeashKnotEntity finalKnot = knot; + LeashUtil.getLeashData(player).ifPresent(i -> { + if (i.canBeAttachedTo(finalKnot)) { + i.addLeash(finalKnot); isSuccess.set(true); + } }); - } + // 情况二:把已有生物拴到 knot else if (!list.isEmpty()) { - for(Entity e : list) { - if(knot == null) { + for (Entity e : list) { + if (knot == null) { knot = SuperLeashKnotEntity.getOrCreateKnot(level, pos); knot.playPlacementSound(); } SuperLeashKnotEntity finalKnot = knot; - LazyOptional iLeashDataCapability = e.getCapability(CapabilityHandler.LEASH_DATA_CAP); - iLeashDataCapability.ifPresent(i -> { - boolean flag = i.canBeAttachedTo(finalKnot); - if (flag) { + + LeashUtil.getLeashData(e).ifPresent(i -> { + if (i.canBeAttachedTo(finalKnot)) { i.transferLeash(uuid, finalKnot); isSuccess.set(true); } }); } } + if (isSuccess.get()) { level.gameEvent(GameEvent.BLOCK_ATTACH, pos, GameEvent.Context.of(player)); return true; diff --git a/src/main/java/top/r3944realms/superleadrope/core/leash/LeashInteractHandler.java b/src/main/java/top/r3944realms/superleadrope/core/leash/LeashInteractHandler.java index d94475c..0d2d455 100644 --- a/src/main/java/top/r3944realms/superleadrope/core/leash/LeashInteractHandler.java +++ b/src/main/java/top/r3944realms/superleadrope/core/leash/LeashInteractHandler.java @@ -16,6 +16,7 @@ package top.r3944realms.superleadrope.core.leash; +import net.minecraft.sounds.SoundSource; import net.minecraft.world.InteractionHand; import net.minecraft.world.InteractionResult; import net.minecraft.world.entity.Entity; @@ -28,7 +29,7 @@ import net.minecraftforge.event.entity.player.AttackEntityEvent; import net.minecraftforge.event.entity.player.PlayerInteractEvent; import top.r3944realms.superleadrope.content.capability.CapabilityHandler; import top.r3944realms.superleadrope.content.capability.impi.LeashDataImpl; -import top.r3944realms.superleadrope.content.capability.inter.ILeashDataCapability; +import top.r3944realms.superleadrope.content.capability.inter.ILeashData; import top.r3944realms.superleadrope.content.item.SuperLeadRopeItem; import top.r3944realms.superleadrope.core.register.SLPItems; import top.r3944realms.superleadrope.core.register.SLPSoundEvents; @@ -45,7 +46,7 @@ public class LeashInteractHandler { player.getItemInHand(InteractionHand.OFF_HAND).is(SLPItems.SUPER_LEAD_ROPE.get())) ) { event.setCanceled(true); - event.setCancellationResult(InteractionResult.CONSUME); + event.setCancellationResult(InteractionResult.SUCCESS); } return; } @@ -56,7 +57,7 @@ public class LeashInteractHandler { if (!LeashDataImpl.isLeashable(target)) { return; } - LazyOptional LeashCap = target.getCapability(CapabilityHandler.LEASH_DATA_CAP); + LazyOptional LeashCap = target.getCapability(CapabilityHandler.LEASH_DATA_CAP); if (!LeashCap.isPresent()) { return; } @@ -67,14 +68,14 @@ public class LeashInteractHandler { if ( mainHandItem.isEmpty() && offHandItem.isEmpty() && target.isAlive() && player.isSecondaryUseActive() && - LeashCap.map(ILeashDataCapability::canBeLeashed).orElse(false) + LeashCap.map(ILeashData::canBeLeashed).orElse(false) ) { boolean isSuccess = SuperLeadRopeItem.bindToEntity(target, player, player.level(), player.getOnPos(), ItemStack.EMPTY); if (isSuccess) { event.setCanceled(true); - event.setCancellationResult(InteractionResult.CONSUME); + event.setCancellationResult(InteractionResult.SUCCESS); } } else { if (LeashDataImpl.isLeashHolder(target, player)) { @@ -82,9 +83,9 @@ public class LeashInteractHandler { iLeashDataCapability -> iLeashDataCapability.removeLeash(player.getUUID()) ); target.gameEvent(GameEvent.ENTITY_INTERACT, player); - target.playSound(SLPSoundEvents.LEAD_UNTIED.get()); + level.playSound(null, target.getOnPos(), SLPSoundEvents.LEAD_UNTIED.get(), SoundSource.PLAYERS); event.setCanceled(true); - event.setCancellationResult(InteractionResult.CONSUME); + event.setCancellationResult(InteractionResult.SUCCESS); return; } ItemStack itemStack; @@ -99,13 +100,13 @@ public class LeashInteractHandler { if (itemStack.getItem() == SLPItems.SUPER_LEAD_ROPE.get()) { LeashCap.ifPresent(iLeashDataCapability -> { if (iLeashDataCapability.canBeAttachedTo(player)) { - boolean success = iLeashDataCapability.addLeash(player, itemStack, 12); + boolean success = iLeashDataCapability.addLeash(player); if (success) { if(!player.isCreative()) itemStack.hurtAndBreak(24, player, e->{}); - target.playSound(SLPSoundEvents.LEAD_TIED.get()); - event.setCanceled(true); - event.setCancellationResult(InteractionResult.CONSUME); + level.playSound(null, target.getOnPos(), SLPSoundEvents.LEAD_TIED.get(), SoundSource.PLAYERS); + event.setCanceled(true); + event.setCancellationResult(InteractionResult.SUCCESS); } } }); @@ -124,11 +125,12 @@ public class LeashInteractHandler { if (flag) { target.getCapability(CapabilityHandler.LEASH_DATA_CAP).ifPresent(leashDataCapability -> { if (leashDataCapability.hasLeash()){ + int size = leashDataCapability.getAllLeashes().size(); if (player.isSecondaryUseActive()) leashDataCapability.removeAllLeashes(); else - leashDataCapability.removeAllHolderLeashes(); - target.playSound(SLPSoundEvents.LEAD_UNTIED.get()); + leashDataCapability.removeAllKnotLeashes(); + if(size > leashDataCapability.getAllLeashes().size()) level.playSound(null, target.getOnPos(), SLPSoundEvents.LEAD_UNTIED.get(), SoundSource.PLAYERS); } event.setCanceled(true); }); diff --git a/src/main/java/top/r3944realms/superleadrope/core/leash/LeashSyncManager.java b/src/main/java/top/r3944realms/superleadrope/core/leash/LeashSyncManager.java index 2b4e1ad..c34eace 100644 --- a/src/main/java/top/r3944realms/superleadrope/core/leash/LeashSyncManager.java +++ b/src/main/java/top/r3944realms/superleadrope/core/leash/LeashSyncManager.java @@ -15,7 +15,8 @@ package top.r3944realms.superleadrope.core.leash; -import top.r3944realms.superleadrope.content.capability.inter.ILeashDataCapability; +import top.r3944realms.superleadrope.content.capability.inter.ILeashData; +import top.r3944realms.superleadrope.content.capability.inter.ILeashState; import java.util.Collections; import java.util.Set; @@ -24,16 +25,29 @@ import java.util.function.Consumer; // 全局LeashData同步管理器 public class LeashSyncManager { - static final Set INSTANCES = Collections.newSetFromMap(new WeakHashMap<>()); - - public static void track(ILeashDataCapability instance) { - INSTANCES.add(instance); + static final Set LEASH_DATA = Collections.newSetFromMap(new WeakHashMap<>()); + static final Set LEASH_STATES = Collections.newSetFromMap(new WeakHashMap<>()); + public static class Data { + public static void track(ILeashData instance) { + LEASH_DATA.add(instance); + } + public static void untrack(ILeashData instance) { + LEASH_DATA.remove(instance); + } + public static void forEach(Consumer consumer) { + LEASH_DATA.forEach(consumer); + } } - public static void untrack(ILeashDataCapability instance) { - INSTANCES.remove(instance); - } - public static void forEach(Consumer consumer) { - INSTANCES.forEach(consumer); + public static class State { + public static void track(ILeashState instance) { + LEASH_STATES.add(instance); + } + public static void untrack(ILeashState instance) { + LEASH_STATES.remove(instance); + } + public static void forEach(Consumer consumer) { + LEASH_STATES.forEach(consumer); + } } } \ No newline at end of file diff --git a/src/main/java/top/r3944realms/superleadrope/core/register/SLPGameruleRegistry.java b/src/main/java/top/r3944realms/superleadrope/core/register/SLPGameruleRegistry.java index 2e4d966..3d70ae2 100644 --- a/src/main/java/top/r3944realms/superleadrope/core/register/SLPGameruleRegistry.java +++ b/src/main/java/top/r3944realms/superleadrope/core/register/SLPGameruleRegistry.java @@ -19,7 +19,7 @@ import net.minecraft.server.MinecraftServer; import net.minecraft.world.level.GameRules; import net.minecraft.world.level.Level; import top.r3944realms.superleadrope.content.gamerule.SLPGamerules; -import top.r3944realms.superleadrope.content.gamerule.server.TeleportWithLeashedPlayers; +import top.r3944realms.superleadrope.content.gamerule.server.TeleportWithLeashedEntities; import java.util.HashMap; import java.util.Map; @@ -70,7 +70,7 @@ public enum SLPGameruleRegistry { gameruleDataTypes.put(gameruleName, RuleDataType.INTEGER); } public static void register() { - TeleportWithLeashedPlayers.register(); + TeleportWithLeashedEntities.register(); } } \ No newline at end of file diff --git a/src/main/java/top/r3944realms/superleadrope/datagen/data/SLPLangKeyValue.java b/src/main/java/top/r3944realms/superleadrope/datagen/data/SLPLangKeyValue.java index 330f1ea..32ab891 100644 --- a/src/main/java/top/r3944realms/superleadrope/datagen/data/SLPLangKeyValue.java +++ b/src/main/java/top/r3944realms/superleadrope/datagen/data/SLPLangKeyValue.java @@ -18,7 +18,8 @@ package top.r3944realms.superleadrope.datagen.data; import net.minecraft.world.item.Item; import net.minecraft.world.level.block.Block; import org.jetbrains.annotations.NotNull; -import top.r3944realms.superleadrope.content.gamerule.server.TeleportWithLeashedPlayers; +import top.r3944realms.superleadrope.content.command.MotionCommand; +import top.r3944realms.superleadrope.content.gamerule.server.TeleportWithLeashedEntities; import top.r3944realms.superleadrope.content.item.EternalPotatoItem; import top.r3944realms.superleadrope.core.register.SLPEntityTypes; import top.r3944realms.superleadrope.core.register.SLPItems; @@ -187,15 +188,38 @@ public enum SLPLangKeyValue { SLPEntityTypes.getEntityNameKey("super_lead_knot"), ModPartEnum.ENTITY, "Super Lead Knot", "超级拴绳结", "超級拴繩結", "神駒羈縻索結" ), - TELEPORT_WITH_LEASHED_PLAYERS_NAME(TeleportWithLeashedPlayers.NAME_KEY, ModPartEnum.GAME_RULE, + TELEPORT_WITH_LEASHED_ENTITIES_NAME(TeleportWithLeashedEntities.NAME_KEY, ModPartEnum.GAME_RULE, "Teleport leashed player with player holder", "被拴实体随玩家持有者传送", - "被拴实体随玩家持有者傳送" + "被拴实体随玩家持有者傳送", + "繫畜隨持者傳送" ), - TELEPORT_WITH_LEASHED_DESCRIPTION(TeleportWithLeashedPlayers.DESCRIPTION_KEY, ModPartEnum.DESCRIPTION, + TELEPORT_WITH_LEASHED_DESCRIPTION(TeleportWithLeashedEntities.DESCRIPTION_KEY, ModPartEnum.DESCRIPTION, "Holder will teleport with their leashed players ", - "传送时将被拴玩家与持有者一起传送", - "將被拴玩家將隨持有者一起傳送" + "传送时将被拴实体与持有者一起传送", + "將被拴实体將隨持有者一起傳送", + "傳送時繫畜隨持者同傳" + ), + MESSAGE_MOTION_ADDER_SUCCESSFUL( + MotionCommand.MOTION_ADDER_SUCCESSFUL, ModPartEnum.COMMAND, + "§bAdd Successfully.§a%s§7:§f[§eVec§7:§a(§f%.2f§7,§f%.2f§7,§f%.2f§7)§f]§r", + "§b添加成功.§a%s§7:§f[§e加速§7:(§a%.2f§7,§a%.2f§7,§a%.2f§7)§f]§r", + "§b添加成功.§a%s§7:§f[§e加速§7:(§a%.2f§7,§a%.2f§7,§a%.2f§7)§f]§r", + "§b增益既成.§a%s§7:§f[§e速勢§7:(§a%.2f§7,§a%.2f§7,§a%.2f§7)§f]§r" + ), + MESSAGE_MOTION_SETTER_SUCCESSFUL( + MotionCommand.MOTION_SETTER_SUCCESSFUL, ModPartEnum.COMMAND, + "§bSet Successfully.§a%s§7:§f[§eVec§7:§a(§f%.2f§7,§f%.2f§7,§f%.2f§7)§f]§r", + "§b设置成功.§a%s§7:§f[§e加速§7:(§a%.2f§7,§a%.2f§7,§a%.2f§7)§f]§r", + "§b設置成功.§a%s§7:§f[§e加速§7:(§a%.2f§7,§a%.2f§7,§a%.2f§7)§f]§r", + "§b定值既成.§a%s§7:§f[§e速勢§7:(§a%.2f§7,§a%.2f§7,§a%.2f§7)§f]§r" + ), + MESSAGE_MOTION_MULTIPLY_SUCCESSFUL( + MotionCommand.MOTION_MULTIPLY_SUCCESSFUL, ModPartEnum.COMMAND, + "§bMultiply Successfully.§a%s§7:§f[§eVec§7:§a(§f%.2f§7,§f%.2f§7,§f%.2f§7)§f]§r", + "§b倍乘成功.§a%s§7:§f[§e加速§7:(§a%.2f§7,§a%.2f§7,§a%.2f§7)§f]§r", + "§b倍乘成功.§a%s§7:§f[§e加速§7:(§a%.2f§7,§a%.2f§7,§a%.2f§7)§f]§r", + "§b倍乘既成.§a%s§7:§f[§e速勢§7:(§a%.2f§7,§a%.2f§7,§a%.2f§7)§f]§r" ), ; diff --git a/src/main/java/top/r3944realms/superleadrope/network/NetworkHandler.java b/src/main/java/top/r3944realms/superleadrope/network/NetworkHandler.java index b4f81ec..8f1ba9d 100644 --- a/src/main/java/top/r3944realms/superleadrope/network/NetworkHandler.java +++ b/src/main/java/top/r3944realms/superleadrope/network/NetworkHandler.java @@ -22,20 +22,16 @@ import net.minecraftforge.network.NetworkRegistry; import net.minecraftforge.network.PacketDistributor; import net.minecraftforge.network.simple.SimpleChannel; import top.r3944realms.superleadrope.SuperLeadRope; -import top.r3944realms.superleadrope.network.toClient.EternalPotatoSyncCapPacket; -import top.r3944realms.superleadrope.network.toClient.LeashDataSyncPacket; -import top.r3944realms.superleadrope.network.toClient.PacketEternalPotatoRemovePacket; -import top.r3944realms.superleadrope.network.toClient.UpdatePlayerMovementPacket; +import top.r3944realms.superleadrope.network.toClient.*; public class NetworkHandler { - private static final String PROTOCOL_VERSION = "1"; private static int cid = 0; public static final SimpleChannel INSTANCE = NetworkRegistry.newSimpleChannel( new ResourceLocation(SuperLeadRope.MOD_ID, "main"), - () -> PROTOCOL_VERSION, - PROTOCOL_VERSION::equals, - PROTOCOL_VERSION::equals + () -> SuperLeadRope.ModInfo.VERSION, + SuperLeadRope.ModInfo.VERSION::equals, + SuperLeadRope.ModInfo.VERSION::equals ); public static void register() { INSTANCE.messageBuilder(LeashDataSyncPacket.class, cid++, NetworkDirection.PLAY_TO_CLIENT) @@ -58,6 +54,11 @@ public class NetworkHandler { .encoder(PacketEternalPotatoRemovePacket::encode) .consumerNetworkThread(PacketEternalPotatoRemovePacket::handle) .add(); + INSTANCE.messageBuilder(LeashStateSyncPacket.class, cid++, NetworkDirection.PLAY_TO_CLIENT) + .decoder(LeashStateSyncPacket::decode) + .encoder(LeashStateSyncPacket::encode) + .consumerNetworkThread(LeashStateSyncPacket::handle) + .add(); } public static void sendToPlayer(MSG message, ServerPlayer player){ INSTANCE.send(PacketDistributor.PLAYER.with(() -> player), message); diff --git a/src/main/java/top/r3944realms/superleadrope/network/toClient/LeashStateSyncPacket.java b/src/main/java/top/r3944realms/superleadrope/network/toClient/LeashStateSyncPacket.java new file mode 100644 index 0000000..dd16ebf --- /dev/null +++ b/src/main/java/top/r3944realms/superleadrope/network/toClient/LeashStateSyncPacket.java @@ -0,0 +1,56 @@ +/* + * Super Lead rope mod + * Copyright (C) 2025 R3944Realms + * 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 . + */ + +package top.r3944realms.superleadrope.network.toClient; + +import net.minecraft.client.Minecraft; +import net.minecraft.client.multiplayer.ClientLevel; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.world.entity.Entity; +import net.minecraftforge.network.NetworkEvent; +import top.r3944realms.superleadrope.content.capability.CapabilityHandler; + +import java.util.function.Supplier; + +public record LeashStateSyncPacket(int entityId, CompoundTag leashState) { + public static void encode(LeashStateSyncPacket msg, FriendlyByteBuf buffer) { + buffer.writeInt(msg.entityId); + buffer.writeNbt(msg.leashState); + } + + public static LeashStateSyncPacket decode(FriendlyByteBuf buffer) { + return new LeashStateSyncPacket(buffer.readInt(), buffer.readNbt()); + } + + public static void handle(LeashStateSyncPacket msg, Supplier ctx) { + ctx.get().enqueueWork(() -> { + ClientLevel level = Minecraft.getInstance().level; + if (level != null) { + Entity entity = level.getEntity(msg.entityId); + if (entity != null) { + entity.getCapability(CapabilityHandler.LEASH_STATE_CAP).ifPresent(cap -> { + // 只在数据确实变化时更新 + CompoundTag current = cap.serializeNBT(); + if (!current.equals(msg.leashState)) { + cap.deserializeNBT(msg.leashState);//更新 + } + }); + } + } + }); + ctx.get().setPacketHandled(true); + } +} diff --git a/src/main/java/top/r3944realms/superleadrope/util/capability/LeashUtil.java b/src/main/java/top/r3944realms/superleadrope/util/capability/LeashUtil.java new file mode 100644 index 0000000..1190330 --- /dev/null +++ b/src/main/java/top/r3944realms/superleadrope/util/capability/LeashUtil.java @@ -0,0 +1,36 @@ +/* + * Super Lead rope mod + * Copyright (C) 2025 R3944Realms + * 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 . + */ + +package top.r3944realms.superleadrope.util.capability; + +import net.minecraft.world.entity.Entity; +import org.jetbrains.annotations.NotNull; +import top.r3944realms.superleadrope.content.capability.CapabilityHandler; +import top.r3944realms.superleadrope.content.capability.inter.ILeashData; +import top.r3944realms.superleadrope.content.capability.inter.ILeashState; + +import java.util.Objects; +import java.util.Optional; + +public class LeashUtil { + public static Optional getLeashData(@NotNull Entity entity) { + Objects.requireNonNull(entity, "Entity cannot be null"); + return entity.getCapability(CapabilityHandler.LEASH_DATA_CAP).resolve(); + } + public static Optional getLeashState(@NotNull Entity entity) { + Objects.requireNonNull(entity, "Entity cannot be null"); + return entity.getCapability(CapabilityHandler.LEASH_STATE_CAP).resolve(); + } +} diff --git a/src/main/java/top/r3944realms/superleadrope/util/nbt/NBTReader.java b/src/main/java/top/r3944realms/superleadrope/util/nbt/NBTReader.java new file mode 100644 index 0000000..98989e4 --- /dev/null +++ b/src/main/java/top/r3944realms/superleadrope/util/nbt/NBTReader.java @@ -0,0 +1,33 @@ +/* + * Super Lead rope mod + * Copyright (C) 2025 R3944Realms + * 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 . + */ + +package top.r3944realms.superleadrope.util.nbt; + +import net.minecraft.nbt.CompoundTag; +import net.minecraft.world.phys.Vec3; + +public class NBTReader { + public static Vec3 readVec3(CompoundTag nbt) { + if (nbt.contains("X") && nbt.contains("Y") && nbt.contains("Z")) { + return new Vec3( + nbt.getDouble("X"), + nbt.getDouble("Y"), + nbt.getDouble("Z") + ); + } else { + throw new IllegalArgumentException("NBT is missing X, Y, or Z value for Vec3"); + } + } +} diff --git a/src/main/java/top/r3944realms/superleadrope/util/nbt/NBTWriter.java b/src/main/java/top/r3944realms/superleadrope/util/nbt/NBTWriter.java new file mode 100644 index 0000000..83ae6f2 --- /dev/null +++ b/src/main/java/top/r3944realms/superleadrope/util/nbt/NBTWriter.java @@ -0,0 +1,31 @@ +/* + * Super Lead rope mod + * Copyright (C) 2025 R3944Realms + * 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 . + */ + +package top.r3944realms.superleadrope.util.nbt; + +import net.minecraft.nbt.CompoundTag; +import net.minecraft.world.phys.Vec3; + +public class NBTWriter { + public static CompoundTag writeVec3(Vec3 vec) { + CompoundTag nbt = new CompoundTag(); + if (vec == null) throw new IllegalArgumentException("Vec3 cannot be null"); + + nbt.putDouble("X", vec.x); + nbt.putDouble("Y", vec.y); + nbt.putDouble("Z", vec.z); + return nbt; + } +} diff --git a/src/main/java/top/r3944realms/superleadrope/util/riding/RidingValidator.java b/src/main/java/top/r3944realms/superleadrope/util/riding/RidingValidator.java index a914696..0445487 100644 --- a/src/main/java/top/r3944realms/superleadrope/util/riding/RidingValidator.java +++ b/src/main/java/top/r3944realms/superleadrope/util/riding/RidingValidator.java @@ -15,6 +15,9 @@ package top.r3944realms.superleadrope.util.riding; +import net.minecraft.core.registries.Registries; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.tags.TagKey; import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.EntityType; import top.r3944realms.superleadrope.config.LeashCommonConfig; @@ -26,17 +29,34 @@ public class RidingValidator { /** * 是否在配置白名单里 */ + @SuppressWarnings("deprecation") public static boolean isInWhitelist(EntityType type) { - //noinspection deprecation String key = type.builtInRegistryHolder().key().location().toString(); String modid = key.split(":")[0]; + for (String entry : LeashCommonConfig.COMMON.teleportWhitelist.get()) { if (entry.startsWith("#")) { - if (modid.equals(entry.substring(1))) { + String body = entry.substring(1); + + // Case 1: #modid → allow all entities from this mod + if (!body.contains(":")) { + if (modid.equals(body)) { + return true; + } + } + // Case 2: #modid:tag_name → allow all entities under this tag + else { + ResourceLocation tagId = new ResourceLocation(body); + TagKey> tag = TagKey.create(Registries.ENTITY_TYPE, tagId); + if (type.builtInRegistryHolder().is(tag)) { + return true; + } + } + } else { + // Case 3: modid:entity_name → allow a specific entity + if (entry.equals(key)) { return true; } - } else if (entry.equals(key)) { - return true; } } return false;