feat: API封装
fix: 部分内容
This commit is contained in:
parent
1a56faad9f
commit
e82f84f2a4
30
build.gradle
30
build.gradle
|
|
@ -135,7 +135,10 @@ legacyForge {
|
||||||
}
|
}
|
||||||
|
|
||||||
// 源码资源目录
|
// 源码资源目录
|
||||||
sourceSets.main.resources { srcDir 'src/generated/resources' }
|
sourceSets.main.resources {
|
||||||
|
srcDir 'src/generated/resources'
|
||||||
|
srcDir 'src/main/resources' // 记得加这一行,把 coremods 包含进去
|
||||||
|
}
|
||||||
|
|
||||||
// ========== 依赖 ==========
|
// ========== 依赖 ==========
|
||||||
configurations {
|
configurations {
|
||||||
|
|
@ -150,8 +153,10 @@ dependencies {
|
||||||
modRuntimeOnly("mezz.jei:jei-${minecraft_version}-forge:${jei_version}")
|
modRuntimeOnly("mezz.jei:jei-${minecraft_version}-forge:${jei_version}")
|
||||||
modRuntimeOnly("curse.maven:spark-361579:4738952")
|
modRuntimeOnly("curse.maven:spark-361579:4738952")
|
||||||
compileOnly ('me.lucko:spark-api:0.1-SNAPSHOT')
|
compileOnly ('me.lucko:spark-api:0.1-SNAPSHOT')
|
||||||
|
implementation 'org.ow2.asm:asm:9.6'
|
||||||
|
implementation 'org.ow2.asm:asm-tree:9.6'
|
||||||
|
implementation 'org.ow2.asm:asm-commons:9.6'
|
||||||
|
implementation 'org.ow2.asm:asm-util:9.6'
|
||||||
}
|
}
|
||||||
|
|
||||||
// ========== 编译配置 ==========
|
// ========== 编译配置 ==========
|
||||||
|
|
@ -221,6 +226,14 @@ publishing {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// 新建一个可解析配置专门给 ProGuard
|
||||||
|
configurations {
|
||||||
|
proguardLibs {
|
||||||
|
canBeResolved = true
|
||||||
|
canBeConsumed = false
|
||||||
|
extendsFrom modCompileOnly
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// ========== ProGuard 配置 ==========
|
// ========== ProGuard 配置 ==========
|
||||||
tasks.register('proguard', ProGuardTask) {
|
tasks.register('proguard', ProGuardTask) {
|
||||||
|
|
@ -231,8 +244,8 @@ tasks.register('proguard', ProGuardTask) {
|
||||||
// JDK jmods 库
|
// JDK jmods 库
|
||||||
libraryjars "${System.getProperty('java.home')}/jmods"
|
libraryjars "${System.getProperty('java.home')}/jmods"
|
||||||
|
|
||||||
// 项目依赖作为库输入
|
// 使用可解析配置
|
||||||
configurations.compileClasspath.files.each { file ->
|
configurations.proguardLibs.resolve().each { file ->
|
||||||
libraryjars file.absolutePath
|
libraryjars file.absolutePath
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -298,7 +311,14 @@ tasks.register("runWithRenderDoc", Exec) {
|
||||||
println "Environment MOD_CLASSES: ${environment['MOD_CLASSES']}"
|
println "Environment MOD_CLASSES: ${environment['MOD_CLASSES']}"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
tasks.register("copyCoreMods", Copy) {
|
||||||
|
from("src/main/resources/coremods") // 源目录
|
||||||
|
into("$buildDir/classes/java/main/coremods") // 目标目录
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks.named("classes") {
|
||||||
|
dependsOn("copyCoreMods")
|
||||||
|
}
|
||||||
|
|
||||||
// IDEA 支持
|
// IDEA 支持
|
||||||
idea {
|
idea {
|
||||||
|
|
|
||||||
4
proguard.pro
vendored
4
proguard.pro
vendored
|
|
@ -36,6 +36,10 @@
|
||||||
-keepclassmembers class cpw.mods.** { *; }
|
-keepclassmembers class cpw.mods.** { *; }
|
||||||
-dontwarn cpw.mods.**
|
-dontwarn cpw.mods.**
|
||||||
|
|
||||||
|
-keep class mezz.jei.**
|
||||||
|
-keepclassmembers class mezz.jei.**{ *; }
|
||||||
|
-dontwarn mezz.jei.**
|
||||||
|
|
||||||
#---------------------------------------
|
#---------------------------------------
|
||||||
# 保留资源文件 (mods.toml / assets / data / pack.mcmeta)
|
# 保留资源文件 (mods.toml / assets / data / pack.mcmeta)
|
||||||
#---------------------------------------
|
#---------------------------------------
|
||||||
|
|
|
||||||
|
|
@ -50,8 +50,11 @@ import net.minecraftforge.event.level.LevelEvent;
|
||||||
import net.minecraftforge.event.server.ServerStartingEvent;
|
import net.minecraftforge.event.server.ServerStartingEvent;
|
||||||
import net.minecraftforge.event.server.ServerStoppingEvent;
|
import net.minecraftforge.event.server.ServerStoppingEvent;
|
||||||
import net.minecraftforge.eventbus.api.SubscribeEvent;
|
import net.minecraftforge.eventbus.api.SubscribeEvent;
|
||||||
|
import net.minecraftforge.fml.event.config.ModConfigEvent;
|
||||||
import net.minecraftforge.fml.event.lifecycle.FMLCommonSetupEvent;
|
import net.minecraftforge.fml.event.lifecycle.FMLCommonSetupEvent;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import top.r3944realms.superleadrope.config.LeashCommonConfig;
|
||||||
|
import top.r3944realms.superleadrope.config.LeashConfigManager;
|
||||||
import top.r3944realms.superleadrope.content.capability.CapabilityHandler;
|
import top.r3944realms.superleadrope.content.capability.CapabilityHandler;
|
||||||
import top.r3944realms.superleadrope.content.capability.CapabilityRemainder;
|
import top.r3944realms.superleadrope.content.capability.CapabilityRemainder;
|
||||||
import top.r3944realms.superleadrope.content.capability.impi.LeashDataImpl;
|
import top.r3944realms.superleadrope.content.capability.impi.LeashDataImpl;
|
||||||
|
|
@ -71,6 +74,8 @@ import top.r3944realms.superleadrope.core.register.SLPItems;
|
||||||
import top.r3944realms.superleadrope.core.util.PotatoMode;
|
import top.r3944realms.superleadrope.core.util.PotatoMode;
|
||||||
import top.r3944realms.superleadrope.core.util.PotatoModeHelper;
|
import top.r3944realms.superleadrope.core.util.PotatoModeHelper;
|
||||||
import top.r3944realms.superleadrope.datagen.data.SLPLangKeyValue;
|
import top.r3944realms.superleadrope.datagen.data.SLPLangKeyValue;
|
||||||
|
import top.r3944realms.superleadrope.util.capability.LeashDataAPI;
|
||||||
|
import top.r3944realms.superleadrope.util.capability.LeashStateAPI;
|
||||||
import top.r3944realms.superleadrope.util.model.RidingRelationship;
|
import top.r3944realms.superleadrope.util.model.RidingRelationship;
|
||||||
import top.r3944realms.superleadrope.util.riding.RidingApplier;
|
import top.r3944realms.superleadrope.util.riding.RidingApplier;
|
||||||
import top.r3944realms.superleadrope.util.riding.RidingDismounts;
|
import top.r3944realms.superleadrope.util.riding.RidingDismounts;
|
||||||
|
|
@ -84,6 +89,7 @@ import java.util.UUID;
|
||||||
import java.util.concurrent.atomic.AtomicReference;
|
import java.util.concurrent.atomic.AtomicReference;
|
||||||
|
|
||||||
public class CommonEventHandler {
|
public class CommonEventHandler {
|
||||||
|
public volatile static LeashConfigManager leashConfigManager;
|
||||||
@net.minecraftforge.fml.common.Mod.EventBusSubscriber(modid = SuperLeadRope.MOD_ID, bus = net.minecraftforge.fml.common.Mod.EventBusSubscriber.Bus.FORGE)
|
@net.minecraftforge.fml.common.Mod.EventBusSubscriber(modid = SuperLeadRope.MOD_ID, bus = net.minecraftforge.fml.common.Mod.EventBusSubscriber.Bus.FORGE)
|
||||||
public static class Game {
|
public static class Game {
|
||||||
@SubscribeEvent
|
@SubscribeEvent
|
||||||
|
|
@ -91,8 +97,8 @@ public class CommonEventHandler {
|
||||||
Entity entity = event.getEntity();
|
Entity entity = event.getEntity();
|
||||||
if (entity.level().isClientSide) return;
|
if (entity.level().isClientSide) return;
|
||||||
if (entity instanceof LivingEntity || entity instanceof Boat || entity instanceof Minecart) {
|
if (entity instanceof LivingEntity || entity instanceof Boat || entity instanceof Minecart) {
|
||||||
entity.getCapability(CapabilityHandler.LEASH_DATA_CAP).ifPresent(LeashSyncManager.Data::track);
|
LeashDataAPI.getLeashData(entity).ifPresent(LeashSyncManager.Data::track);
|
||||||
entity.getCapability(CapabilityHandler.LEASH_STATE_CAP).ifPresent(LeashSyncManager.State::track);
|
LeashStateAPI.getLeashState(entity).ifPresent(LeashSyncManager.State::track);
|
||||||
if (entity instanceof ServerPlayer serverPlayer) {
|
if (entity instanceof ServerPlayer serverPlayer) {
|
||||||
LeashSyncManager.Data.forEach(i -> {
|
LeashSyncManager.Data.forEach(i -> {
|
||||||
if (i.isLeashedBy(serverPlayer) && i.isInDelayedLeash(serverPlayer.getUUID())) {
|
if (i.isLeashedBy(serverPlayer) && i.isInDelayedLeash(serverPlayer.getUUID())) {
|
||||||
|
|
@ -115,8 +121,8 @@ public class CommonEventHandler {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
entity.getCapability(CapabilityHandler.LEASH_DATA_CAP).ifPresent(LeashSyncManager.Data::untrack);
|
LeashDataAPI.getLeashData(entity).ifPresent(LeashSyncManager.Data::untrack);
|
||||||
entity.getCapability(CapabilityHandler.LEASH_STATE_CAP).ifPresent(LeashSyncManager.State::untrack);
|
LeashStateAPI.getLeashState(entity).ifPresent(LeashSyncManager.State::untrack);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@SubscribeEvent
|
@SubscribeEvent
|
||||||
|
|
@ -139,9 +145,6 @@ public class CommonEventHandler {
|
||||||
@SubscribeEvent
|
@SubscribeEvent
|
||||||
public static void onPlayerRightHitOnBlock(PlayerInteractEvent.RightClickBlock event) {
|
public static void onPlayerRightHitOnBlock(PlayerInteractEvent.RightClickBlock event) {
|
||||||
Level level = event.getLevel();
|
Level level = event.getLevel();
|
||||||
if (level.isClientSide) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
BlockPos blockPos = event.getHitVec().getBlockPos();
|
BlockPos blockPos = event.getHitVec().getBlockPos();
|
||||||
BlockState blockState = level.getBlockState(blockPos);
|
BlockState blockState = level.getBlockState(blockPos);
|
||||||
Player player = event.getEntity();
|
Player player = event.getEntity();
|
||||||
|
|
@ -256,7 +259,7 @@ public class CommonEventHandler {
|
||||||
List<Entity> entities = LeashDataImpl.leashableInArea(telEntity);
|
List<Entity> entities = LeashDataImpl.leashableInArea(telEntity);
|
||||||
//规则关闭则禁止
|
//规则关闭则禁止
|
||||||
if(!SLPGameruleRegistry.getGameruleBoolValue(event.getEntity().level(), TeleportWithLeashedEntities.ID)) {
|
if(!SLPGameruleRegistry.getGameruleBoolValue(event.getEntity().level(), TeleportWithLeashedEntities.ID)) {
|
||||||
entities.forEach(i -> i.getCapability(CapabilityHandler.LEASH_DATA_CAP).ifPresent(j -> j.removeLeash(i)));
|
entities.forEach(entity -> LeashDataAPI.LeashOperations.detach(entity, telEntity));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
for (Entity beLeashedEntity : entities) {
|
for (Entity beLeashedEntity : entities) {
|
||||||
|
|
@ -268,9 +271,9 @@ public class CommonEventHandler {
|
||||||
Vec3 originalDeltaMovement = beLeashedEntity.getDeltaMovement();
|
Vec3 originalDeltaMovement = beLeashedEntity.getDeltaMovement();
|
||||||
|
|
||||||
AtomicReference<ILeashData.LeashInfo> originalLeashInfo = new AtomicReference<>();
|
AtomicReference<ILeashData.LeashInfo> originalLeashInfo = new AtomicReference<>();
|
||||||
beLeashedEntity.getCapability(CapabilityHandler.LEASH_DATA_CAP).ifPresent(cap -> {
|
LeashDataAPI.getLeashData(beLeashedEntity).ifPresent(data -> {
|
||||||
originalLeashInfo.set(cap.getLeashInfo(telEntity).orElse(null));
|
originalLeashInfo.set(data.getLeashInfo(telEntity).orElse(null));
|
||||||
cap.removeLeash(telEntity);
|
data.removeLeash(telEntity);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -302,14 +305,11 @@ public class CommonEventHandler {
|
||||||
entity.setPose(originalPose);
|
entity.setPose(originalPose);
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- 恢复拴绳 ---
|
// --- 将holder替换 ---
|
||||||
ILeashData.LeashInfo leashInfo = Optional.ofNullable(originalLeashInfo.get())
|
ILeashData.LeashInfo leashInfo = Optional.ofNullable(originalLeashInfo.get())
|
||||||
.map(info -> info.transferHolder(telEntity))
|
|
||||||
.orElse(ILeashData.LeashInfo.EMPTY);
|
.orElse(ILeashData.LeashInfo.EMPTY);
|
||||||
|
|
||||||
beLeashedEntity.getCapability(CapabilityHandler.LEASH_DATA_CAP).ifPresent(
|
LeashDataAPI.LeashOperations.attachWithInfo(beLeashedEntity, telEntity, leashInfo);
|
||||||
cap -> cap.addLeash(telEntity, leashInfo)
|
|
||||||
);
|
|
||||||
|
|
||||||
// --- 重新应用骑乘关系,仅保留白名单根载具 ---
|
// --- 重新应用骑乘关系,仅保留白名单根载具 ---
|
||||||
RidingRelationship filteredRelationship = RidingSaver.filterByWhitelistRoot(originalRidingRelationship);
|
RidingRelationship filteredRelationship = RidingSaver.filterByWhitelistRoot(originalRidingRelationship);
|
||||||
|
|
@ -362,7 +362,8 @@ public class CommonEventHandler {
|
||||||
@net.minecraftforge.fml.common.Mod.EventBusSubscriber(modid = SuperLeadRope.MOD_ID, bus= net.minecraftforge.fml.common.Mod.EventBusSubscriber.Bus.MOD)
|
@net.minecraftforge.fml.common.Mod.EventBusSubscriber(modid = SuperLeadRope.MOD_ID, bus= net.minecraftforge.fml.common.Mod.EventBusSubscriber.Bus.MOD)
|
||||||
public static class Mod {
|
public static class Mod {
|
||||||
@SubscribeEvent
|
@SubscribeEvent
|
||||||
public static void onCommonInit (FMLCommonSetupEvent event) {
|
public static void onFMLCommonInit(FMLCommonSetupEvent event) {
|
||||||
|
event.enqueueWork(Mod::checkAndSet);
|
||||||
event.enqueueWork(SLPGameruleRegistry::register);//规则注册
|
event.enqueueWork(SLPGameruleRegistry::register);//规则注册
|
||||||
}
|
}
|
||||||
@SubscribeEvent
|
@SubscribeEvent
|
||||||
|
|
@ -375,6 +376,43 @@ public class CommonEventHandler {
|
||||||
event.accept(SLPItems.SUPER_LEAD_ROPE);
|
event.accept(SLPItems.SUPER_LEAD_ROPE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@SubscribeEvent
|
||||||
|
public void onConfigReloading(ModConfigEvent.Reloading event) {
|
||||||
|
if (event.getConfig().getSpec() == LeashCommonConfig.SPEC) {
|
||||||
|
SuperLeadRope.logger.debug("Config reloading detected...");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private static void checkAndSet() {
|
||||||
|
if (leashConfigManager == null) {
|
||||||
|
synchronized (LeashConfigManager.class) {
|
||||||
|
if (leashConfigManager == null) {
|
||||||
|
leashConfigManager = new LeashConfigManager();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@SubscribeEvent
|
||||||
|
public void onConfigLoaded(ModConfigEvent.Loading event) {
|
||||||
|
if (event.getConfig().getSpec() == LeashCommonConfig.SPEC) {
|
||||||
|
checkAndSet();
|
||||||
|
LeashConfigManager.loading(leashConfigManager);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@SubscribeEvent
|
||||||
|
public void onConfigReloaded(ModConfigEvent.Reloading event) {
|
||||||
|
if (event.getConfig().getSpec() == LeashCommonConfig.SPEC) {
|
||||||
|
checkAndSet();
|
||||||
|
LeashConfigManager.reloading(leashConfigManager);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@SubscribeEvent
|
||||||
|
public void onConfigUnloaded(ModConfigEvent.Unloading event) {
|
||||||
|
if (event.getConfig().getSpec() == LeashCommonConfig.SPEC) {
|
||||||
|
LeashConfigManager.unloading(leashConfigManager);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -33,6 +33,7 @@ import top.r3944realms.superleadrope.util.file.ConfigUtil;
|
||||||
@Mod(value = SuperLeadRope.MOD_ID)
|
@Mod(value = SuperLeadRope.MOD_ID)
|
||||||
public class SuperLeadRope {
|
public class SuperLeadRope {
|
||||||
public static final Logger logger = LoggerFactory.getLogger(SuperLeadRope.class);
|
public static final Logger logger = LoggerFactory.getLogger(SuperLeadRope.class);
|
||||||
|
|
||||||
public static final String MOD_ID = "superleadrope";
|
public static final String MOD_ID = "superleadrope";
|
||||||
public SuperLeadRope() {
|
public SuperLeadRope() {
|
||||||
IEventBus eventBus = FMLJavaModLoadingContext.get().getModEventBus();
|
IEventBus eventBus = FMLJavaModLoadingContext.get().getModEventBus();
|
||||||
|
|
@ -41,6 +42,7 @@ public class SuperLeadRope {
|
||||||
SLPSoundEvents.register(eventBus);
|
SLPSoundEvents.register(eventBus);
|
||||||
NetworkHandler.register();
|
NetworkHandler.register();
|
||||||
initialize();
|
initialize();
|
||||||
|
|
||||||
}
|
}
|
||||||
public static void initialize() {
|
public static void initialize() {
|
||||||
logger.info("Initializing SuperLeadRope");
|
logger.info("Initializing SuperLeadRope");
|
||||||
|
|
|
||||||
|
|
@ -69,7 +69,7 @@ public class ClientEventHandler {
|
||||||
(itemStack, clientLevel, livingEntity, i) -> {
|
(itemStack, clientLevel, livingEntity, i) -> {
|
||||||
if (!itemStack.isDamageableItem()) return 0.0F;
|
if (!itemStack.isDamageableItem()) return 0.0F;
|
||||||
|
|
||||||
return itemStack.getDamageValue() > 1024 - 50 ? 1.0F : 0.0F; // 损坏 → 返回 1.0(触发 override)
|
return itemStack.getDamageValue() > 1024 - 33 ? 1.0F : 0.0F; // 损坏 → 返回 1.0(触发 override)
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
PotatoMode mode = getCurrentMode();
|
PotatoMode mode = getCurrentMode();
|
||||||
|
|
|
||||||
|
|
@ -24,9 +24,9 @@ import net.minecraft.world.entity.Entity;
|
||||||
import net.minecraft.world.level.Level;
|
import net.minecraft.world.level.Level;
|
||||||
import top.r3944realms.superleadrope.SuperLeadRope;
|
import top.r3944realms.superleadrope.SuperLeadRope;
|
||||||
import top.r3944realms.superleadrope.client.renderer.resolver.SuperLeashStateResolver;
|
import top.r3944realms.superleadrope.client.renderer.resolver.SuperLeashStateResolver;
|
||||||
import top.r3944realms.superleadrope.content.capability.CapabilityHandler;
|
|
||||||
import top.r3944realms.superleadrope.content.capability.inter.ILeashData;
|
import top.r3944realms.superleadrope.content.capability.inter.ILeashData;
|
||||||
import top.r3944realms.superleadrope.content.entity.SuperLeashKnotEntity;
|
import top.r3944realms.superleadrope.content.entity.SuperLeashKnotEntity;
|
||||||
|
import top.r3944realms.superleadrope.util.capability.LeashDataAPI;
|
||||||
|
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
@ -46,10 +46,9 @@ public class LeashRenderHandler {
|
||||||
|
|
||||||
// 遍历摄像机附近所有实体
|
// 遍历摄像机附近所有实体
|
||||||
for (Entity entity : level.getEntitiesOfClass(Entity.class,
|
for (Entity entity : level.getEntitiesOfClass(Entity.class,
|
||||||
cameraEntity.getBoundingBox().inflate(50))) {
|
cameraEntity.getBoundingBox().inflate(100))) {
|
||||||
|
|
||||||
entity.getCapability(CapabilityHandler.LEASH_DATA_CAP).ifPresent(leashData -> {
|
LeashDataAPI.getLeashData(entity).ifPresent(leashData -> {
|
||||||
if(leashData instanceof ILeashData) {}
|
|
||||||
for (ILeashData.LeashInfo leashInfo : leashData.getAllLeashes()) {
|
for (ILeashData.LeashInfo leashInfo : leashData.getAllLeashes()) {
|
||||||
renderLeashFromInfo(entity, leashInfo, poseStack, bufferSource, partialTick);
|
renderLeashFromInfo(entity, leashInfo, poseStack, bufferSource, partialTick);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -52,7 +52,7 @@ public class SuperLeashRenderer {
|
||||||
int skyLightEnd = getSkyLight(BlockPos.containing(endWorld));
|
int skyLightEnd = getSkyLight(BlockPos.containing(endWorld));
|
||||||
|
|
||||||
// 差向量 + 偏移
|
// 差向量 + 偏移
|
||||||
Offsets offsets = computeOffsets(state, startWorld, endWorld);
|
Offsets offsets = computeOffsets(startWorld, endWorld);
|
||||||
|
|
||||||
// pass1: 0 → N
|
// pass1: 0 → N
|
||||||
for (int i = 0; i <= LEASH_STEPS; i++) {
|
for (int i = 0; i <= LEASH_STEPS; i++) {
|
||||||
|
|
@ -76,7 +76,7 @@ public class SuperLeashRenderer {
|
||||||
|
|
||||||
|
|
||||||
/** 计算差向量和偏移 */
|
/** 计算差向量和偏移 */
|
||||||
private static Offsets computeOffsets(SuperLeashRenderState state, Vec3 start, Vec3 end) {
|
private static Offsets computeOffsets(Vec3 start, Vec3 end) {
|
||||||
float dx = (float) (end.x - start.x);
|
float dx = (float) (end.x - start.x);
|
||||||
float dy = (float) (end.y - start.y);
|
float dy = (float) (end.y - start.y);
|
||||||
float dz = (float) (end.z - start.z);
|
float dz = (float) (end.z - start.z);
|
||||||
|
|
|
||||||
|
|
@ -20,11 +20,10 @@ import net.minecraft.world.level.Level;
|
||||||
import net.minecraft.world.phys.Vec3;
|
import net.minecraft.world.phys.Vec3;
|
||||||
import top.r3944realms.superleadrope.SuperLeadRope;
|
import top.r3944realms.superleadrope.SuperLeadRope;
|
||||||
import top.r3944realms.superleadrope.client.renderer.state.SuperLeashRenderState;
|
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.impi.LeashDataImpl;
|
||||||
import top.r3944realms.superleadrope.content.capability.inter.ILeashData;
|
import top.r3944realms.superleadrope.content.capability.inter.ILeashData;
|
||||||
import top.r3944realms.superleadrope.content.entity.SuperLeashKnotEntity;
|
import top.r3944realms.superleadrope.content.entity.SuperLeashKnotEntity;
|
||||||
import top.r3944realms.superleadrope.util.capability.LeashUtil;
|
import top.r3944realms.superleadrope.util.capability.LeashStateAPI;
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.concurrent.atomic.AtomicReference;
|
import java.util.concurrent.atomic.AtomicReference;
|
||||||
|
|
@ -56,7 +55,7 @@ public class SuperLeashStateResolver {
|
||||||
}
|
}
|
||||||
AtomicReference<Vec3> holderOffset = new AtomicReference<>();
|
AtomicReference<Vec3> holderOffset = new AtomicReference<>();
|
||||||
AtomicReference<Vec3> entityOffset = new AtomicReference<>();
|
AtomicReference<Vec3> entityOffset = new AtomicReference<>();
|
||||||
LeashUtil.getLeashState(leashedEntity).ifPresent(state ->
|
LeashStateAPI.getLeashState(leashedEntity).ifPresent(state ->
|
||||||
state
|
state
|
||||||
.getLeashState(holder)
|
.getLeashState(holder)
|
||||||
.ifPresent(ls -> {
|
.ifPresent(ls -> {
|
||||||
|
|
@ -110,7 +109,6 @@ public class SuperLeashStateResolver {
|
||||||
return Optional.of(new SuperLeashRenderState(
|
return Optional.of(new SuperLeashRenderState(
|
||||||
currentHolderPos,
|
currentHolderPos,
|
||||||
currentEntityPos,
|
currentEntityPos,
|
||||||
leashInfo.attachOffset(),
|
|
||||||
lastHolderPos,
|
lastHolderPos,
|
||||||
lastEntityPos,
|
lastEntityPos,
|
||||||
tension,
|
tension,
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,6 @@ import net.minecraft.world.phys.Vec3;
|
||||||
public record SuperLeashRenderState(
|
public record SuperLeashRenderState(
|
||||||
Vec3 startPos, // 当前帧起点位置
|
Vec3 startPos, // 当前帧起点位置
|
||||||
Vec3 endPos, // 当前帧终点位置
|
Vec3 endPos, // 当前帧终点位置
|
||||||
Vec3 attachOffset, // 拴绳附着点偏移
|
|
||||||
Vec3 lastStartPos, // 上一帧起点位置(用于摆动计算)
|
Vec3 lastStartPos, // 上一帧起点位置(用于摆动计算)
|
||||||
Vec3 lastEndPos, // 上一帧终点位置(用于摆动计算)
|
Vec3 lastEndPos, // 上一帧终点位置(用于摆动计算)
|
||||||
float tension, // 张力强度(0.0-1.0)
|
float tension, // 张力强度(0.0-1.0)
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,8 @@ package top.r3944realms.superleadrope.config;
|
||||||
import net.minecraftforge.common.ForgeConfigSpec;
|
import net.minecraftforge.common.ForgeConfigSpec;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
public class LeashCommonConfig {
|
public class LeashCommonConfig {
|
||||||
public static ForgeConfigSpec.Builder BUILDER = new ForgeConfigSpec.Builder();
|
public static ForgeConfigSpec.Builder BUILDER = new ForgeConfigSpec.Builder();
|
||||||
|
|
@ -38,13 +40,18 @@ public class LeashCommonConfig {
|
||||||
public final ForgeConfigSpec.DoubleValue springDampening;
|
public final ForgeConfigSpec.DoubleValue springDampening;
|
||||||
public final ForgeConfigSpec.ConfigValue<List<? extends Double>> axisSpecificElasticity;
|
public final ForgeConfigSpec.ConfigValue<List<? extends Double>> axisSpecificElasticity;
|
||||||
public final ForgeConfigSpec.IntValue maxLeashesPerEntity;
|
public final ForgeConfigSpec.IntValue maxLeashesPerEntity;
|
||||||
|
public final ForgeConfigSpec.ConfigValue<List<? extends String>> defaultApplyEntityLocationOffset;
|
||||||
|
// 正则表达式模式
|
||||||
|
static final Pattern OFFSET_PATTERN = Pattern.compile(
|
||||||
|
"(?i)(?:vec3|vec3d|vector3|offset)\\s*\\(\\s*([-+]?[0-9]*\\.?[0-9]+)\\s*,\\s*([-+]?[0-9]*\\.?[0-9]+)\\s*,\\s*([-+]?[0-9]*\\.?[0-9]+)\\s*\\)\\s*:\\s*\\[\\s*([^]]+?)\\s*]\\s*" );
|
||||||
|
|
||||||
public Common(ForgeConfigSpec.Builder builder) {
|
public Common(ForgeConfigSpec.Builder builder) {
|
||||||
BUILDER.push("Command");
|
BUILDER.push("Command");
|
||||||
EnableSLPModCommandPrefix = builder
|
EnableSLPModCommandPrefix = builder
|
||||||
.comment("The prefix of this mod's commands")
|
.comment("The prefix of this mod's commands")
|
||||||
.define("SLPModCommandPrefix", true);
|
.define("SLPModCommandPrefix", true);
|
||||||
SLPModCommandPrefix = builder
|
SLPModCommandPrefix = builder
|
||||||
.comment("The prefix of this mod's commands"," [ Default:'slp'] ")
|
.comment("The prefix of this mod's commands", " [ Default:'slp'] ")
|
||||||
.define("EnableSLPModCommandPrefix", "slp");
|
.define("EnableSLPModCommandPrefix", "slp");
|
||||||
BUILDER.pop();
|
BUILDER.pop();
|
||||||
builder.push("Entity");
|
builder.push("Entity");
|
||||||
|
|
@ -59,7 +66,7 @@ public class LeashCommonConfig {
|
||||||
.defineListAllowEmpty(
|
.defineListAllowEmpty(
|
||||||
List.of("allowedTeleportEntities"),
|
List.of("allowedTeleportEntities"),
|
||||||
List.of("#minecraft", "modernlife:bicycle", "modernlife:motorboat"),
|
List.of("#minecraft", "modernlife:bicycle", "modernlife:motorboat"),
|
||||||
o -> o instanceof String s && isValidFormat(s)
|
o -> o instanceof String s && isValidEntityRefFormat(s)
|
||||||
);
|
);
|
||||||
builder.pop();
|
builder.pop();
|
||||||
builder.push("LeashSettings");
|
builder.push("LeashSettings");
|
||||||
|
|
@ -87,20 +94,70 @@ public class LeashCommonConfig {
|
||||||
.defineInRange("maxLeashesPerEntity", 6, 1, 24);
|
.defineInRange("maxLeashesPerEntity", 6, 1, 24);
|
||||||
|
|
||||||
builder.pop();
|
builder.pop();
|
||||||
|
builder.push("LeashStateSettings");
|
||||||
|
defaultApplyEntityLocationOffset = builder
|
||||||
|
.comment(
|
||||||
|
"Default leash attachment point offsets for entities.",
|
||||||
|
"Format: vec3(x,y,z) : [entity_list]",
|
||||||
|
"Optional Name : vector3, vec3d, offset",
|
||||||
|
"Entity list can contain:",
|
||||||
|
" - modid:entity_id : specific entity (e.g. minecraft:bee)",
|
||||||
|
" - #modid:tag_name : entity type tag (e.g. #minecraft:boats)",
|
||||||
|
" - #modid : mod-wide (e.g. #minecraft)",
|
||||||
|
"Multiple entities can be separated by commas",
|
||||||
|
"Example: vec3(0,0.2,0) : [minecraft:bee, minecraft:horse]",
|
||||||
|
"Priority: specific entity > tag > mod"
|
||||||
|
)
|
||||||
|
.defineListAllowEmpty(
|
||||||
|
List.of("defaultApplyEntityLocationOffset"),
|
||||||
|
List.of(
|
||||||
|
"vec3(0,0.2,0) : [minecraft:bee]",
|
||||||
|
"vec3(0,1.0,0) : [minecraft:horse, minecraft:donkey]",
|
||||||
|
"vec3(0,0.5,0) : [#minecraft:boats]",
|
||||||
|
"vec3(0,0.4,0) : [#minecraft:minecarts]",
|
||||||
|
"vec3(0,0.3,0) : [#minecraft]",
|
||||||
|
"vec3(0,0.6,0) : [#modernlife]"
|
||||||
|
),
|
||||||
|
o -> o instanceof String s && isValidOffsetRefFormat(s)
|
||||||
|
);
|
||||||
|
BUILDER.pop();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean isValidFormat(String s) {
|
private static boolean isValidEntityRefFormat(String s) {
|
||||||
if (s.startsWith("#")) {
|
if (s.startsWith("#")) {
|
||||||
String body = s.substring(1);
|
String body = s.substring(1);
|
||||||
// 支持 #modid (整个模组)
|
// 支持 #modid (整个模组)或 #modid:tag_name (标签)
|
||||||
if (body.matches("[a-z0-9_]+")) {
|
return body.matches("[a-z0-9_]+(:[a-z0-9_/]+)?");
|
||||||
return true;
|
|
||||||
}
|
|
||||||
// 支持 #modid:tag_name (标签)
|
|
||||||
return body.matches("[a-z0-9_]+:[a-z0-9_/]+");
|
|
||||||
}
|
}
|
||||||
// 普通实体 ID
|
// 普通实体 ID: modid:entity_id
|
||||||
return s.matches("[a-z0-9_]+:[a-z0-9_/]+");
|
return s.matches("[a-z0-9_]+:[a-z0-9_/]+");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static boolean isValidOffsetRefFormat(String s) {
|
||||||
|
// 匹配格式: vec3(x,y,z) : [entity_list]
|
||||||
|
Matcher matcher = Common.OFFSET_PATTERN.matcher(s);
|
||||||
|
if (!matcher.matches()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查坐标值是否有效
|
||||||
|
try {
|
||||||
|
Double.parseDouble(matcher.group(1));
|
||||||
|
Double.parseDouble(matcher.group(2));
|
||||||
|
Double.parseDouble(matcher.group(3));
|
||||||
|
|
||||||
|
// 检查实体列表格式
|
||||||
|
String entityList = matcher.group(4);
|
||||||
|
String[] entities = entityList.split(",");
|
||||||
|
for (String entity : entities) {
|
||||||
|
if (!isValidEntityRefFormat(entity.trim())) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,361 @@
|
||||||
|
/*
|
||||||
|
* 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 <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package top.r3944realms.superleadrope.config;
|
||||||
|
|
||||||
|
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 net.minecraft.world.phys.Vec3;
|
||||||
|
import top.r3944realms.superleadrope.SuperLeadRope;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
|
||||||
|
import static top.r3944realms.superleadrope.config.LeashCommonConfig.Common.OFFSET_PATTERN;
|
||||||
|
|
||||||
|
public class LeashConfigManager {
|
||||||
|
private final Map<String, double[]> entityOffsetMap = new ConcurrentHashMap<>();
|
||||||
|
private final Map<String, double[]> tagOffsetMap = new ConcurrentHashMap<>();
|
||||||
|
private final Map<String, double[]> modOffsetMap = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
|
// 缓存常用配置值以提高性能
|
||||||
|
private volatile List<String> teleportWhitelistCache;
|
||||||
|
private volatile String commandPrefixCache;
|
||||||
|
private volatile boolean enableCommandPrefixCache;
|
||||||
|
private volatile double maxLeashLengthCache;
|
||||||
|
private volatile double elasticDistanceCache;
|
||||||
|
private volatile double extremeSnapFactorCache;
|
||||||
|
private volatile double springDampeningCache;
|
||||||
|
private volatile List<Double> axisSpecificElasticityCache;
|
||||||
|
private volatile int maxLeashesPerEntityCache;
|
||||||
|
|
||||||
|
public LeashConfigManager() {
|
||||||
|
this.reloadAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 解析偏移配置(线程安全)
|
||||||
|
*/
|
||||||
|
public void parseOffsetConfig() {
|
||||||
|
Map<String, double[]> newEntityOffsetMap = new HashMap<>();
|
||||||
|
Map<String, double[]> newTagOffsetMap = new HashMap<>();
|
||||||
|
Map<String, double[]> newModOffsetMap = new HashMap<>();
|
||||||
|
|
||||||
|
List<? extends String> offsets = LeashCommonConfig.COMMON.defaultApplyEntityLocationOffset.get();
|
||||||
|
for (String offsetConfig : offsets) {
|
||||||
|
Matcher matcher = OFFSET_PATTERN.matcher(offsetConfig);
|
||||||
|
if (!matcher.matches()) continue;
|
||||||
|
|
||||||
|
try {
|
||||||
|
double x = Double.parseDouble(matcher.group(1).trim());
|
||||||
|
double y = Double.parseDouble(matcher.group(2).trim());
|
||||||
|
double z = Double.parseDouble(matcher.group(3).trim());
|
||||||
|
double[] offset = new double[]{x, y, z};
|
||||||
|
|
||||||
|
String entityList = matcher.group(4);
|
||||||
|
String[] entities = entityList.split(",");
|
||||||
|
|
||||||
|
for (String entity : entities) {
|
||||||
|
String trimmed = entity.trim();
|
||||||
|
if (trimmed.startsWith("#")) {
|
||||||
|
String body = trimmed.substring(1).trim();
|
||||||
|
if (body.contains(":")) {
|
||||||
|
newTagOffsetMap.put(body, offset);
|
||||||
|
} else {
|
||||||
|
newModOffsetMap.put(body, offset);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
newEntityOffsetMap.put(trimmed, offset);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
System.err.println("Invalid offset config: " + offsetConfig);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 原子性更新映射
|
||||||
|
entityOffsetMap.clear();
|
||||||
|
entityOffsetMap.putAll(newEntityOffsetMap);
|
||||||
|
|
||||||
|
tagOffsetMap.clear();
|
||||||
|
tagOffsetMap.putAll(newTagOffsetMap);
|
||||||
|
|
||||||
|
modOffsetMap.clear();
|
||||||
|
modOffsetMap.putAll(newModOffsetMap);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 重新加载所有配置值到缓存
|
||||||
|
*/
|
||||||
|
public void reloadAll() {
|
||||||
|
// 加载偏移配置
|
||||||
|
parseOffsetConfig();
|
||||||
|
|
||||||
|
// 加载其他配置到缓存
|
||||||
|
teleportWhitelistCache = new ArrayList<>(LeashCommonConfig.COMMON.teleportWhitelist.get());
|
||||||
|
commandPrefixCache = LeashCommonConfig.COMMON.SLPModCommandPrefix.get();
|
||||||
|
enableCommandPrefixCache = LeashCommonConfig.COMMON.EnableSLPModCommandPrefix.get();
|
||||||
|
maxLeashLengthCache = LeashCommonConfig.COMMON.maxLeashLength.get();
|
||||||
|
elasticDistanceCache = LeashCommonConfig.COMMON.elasticDistance.get();
|
||||||
|
extremeSnapFactorCache = LeashCommonConfig.COMMON.extremeSnapFactor.get();
|
||||||
|
springDampeningCache = LeashCommonConfig.COMMON.springDampening.get();
|
||||||
|
axisSpecificElasticityCache = new ArrayList<>(LeashCommonConfig.COMMON.axisSpecificElasticity.get());
|
||||||
|
maxLeashesPerEntityCache = LeashCommonConfig.COMMON.maxLeashesPerEntity.get();
|
||||||
|
|
||||||
|
SuperLeadRope.logger.debug("All configs reloaded: {}", getStats());
|
||||||
|
}
|
||||||
|
|
||||||
|
// ========== 偏移配置相关方法 ==========
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取实体类型的偏移量
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("deprecation")
|
||||||
|
public Vec3 getEntityOffset(EntityType<?> entityType) {
|
||||||
|
String entityId = entityType.builtInRegistryHolder().key().location().toString();
|
||||||
|
String modId = entityId.split(":")[0]; // 从实体ID提取modId
|
||||||
|
|
||||||
|
// 获取实体的标签
|
||||||
|
List<String> tagStrings = new ArrayList<>();
|
||||||
|
for (var tag : entityType.builtInRegistryHolder().tags().toList()) {
|
||||||
|
tagStrings.add(tag.location().toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
double[] offset = getEntityOffset(entityId, modId, tagStrings);
|
||||||
|
return offset != null ? new Vec3(offset[0], offset[1], offset[2]) : Vec3.ZERO;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取实体对象的偏移量(便捷方法)
|
||||||
|
*/
|
||||||
|
public Vec3 getEntityOffset(Entity entity) {
|
||||||
|
return getEntityOffset(entity.getType());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取实体偏移量(原始数据)
|
||||||
|
*/
|
||||||
|
public double[] getEntityOffset(String entityId, String modId, List<String> tags) {
|
||||||
|
// 1. 首先检查特定实体
|
||||||
|
if (entityOffsetMap.containsKey(entityId)) {
|
||||||
|
return entityOffsetMap.get(entityId);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. 检查标签
|
||||||
|
for (String tag : tags) {
|
||||||
|
if (tagOffsetMap.containsKey(tag)) {
|
||||||
|
return tagOffsetMap.get(tag);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. 检查模组
|
||||||
|
if (modOffsetMap.containsKey(modId)) {
|
||||||
|
return modOffsetMap.get(modId);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ========== 传送白名单相关方法 ==========
|
||||||
|
|
||||||
|
public List<String> getTeleportWhitelist() {
|
||||||
|
return Collections.unmodifiableList(teleportWhitelistCache);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("deprecation")
|
||||||
|
public boolean isEntityTeleportAllowed(EntityType<?> entityType) {
|
||||||
|
String entityId = entityType.builtInRegistryHolder().key().location().toString();
|
||||||
|
String modid = entityId.split(":")[0];
|
||||||
|
|
||||||
|
for (String entry : teleportWhitelistCache) {
|
||||||
|
if (entry.startsWith("#")) {
|
||||||
|
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<EntityType<?>> tag = TagKey.create(Registries.ENTITY_TYPE, tagId);
|
||||||
|
if (entityType.builtInRegistryHolder().is(tag)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Case 3: modid:entity_name → allow a specific entity
|
||||||
|
if (entry.equals(entityId)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isEntityTeleportAllowed(String entityId) {
|
||||||
|
// 对于字符串ID,我们无法检查标签,只能检查模组和特定实体
|
||||||
|
String modid = entityId.contains(":") ? entityId.split(":")[0] : "minecraft";
|
||||||
|
|
||||||
|
for (String entry : teleportWhitelistCache) {
|
||||||
|
if (entry.startsWith("#")) {
|
||||||
|
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 → 字符串ID无法检查标签,跳过
|
||||||
|
// 如果需要支持标签检查,需要传入EntityType而不是String
|
||||||
|
} else {
|
||||||
|
// Case 3: modid:entity_name → allow a specific entity
|
||||||
|
if (entry.equals(entityId)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 辅助方法:检查实体ID是否匹配模式(用于旧的匹配逻辑)
|
||||||
|
private boolean matchesTeleportPattern(String pattern, String entityId) {
|
||||||
|
if (pattern.startsWith("#")) {
|
||||||
|
String body = pattern.substring(1);
|
||||||
|
if (body.contains(":")) {
|
||||||
|
// 标签格式: #modid:tag_name - 字符串ID无法准确匹配标签
|
||||||
|
// 返回模组匹配作为近似
|
||||||
|
String patternModId = body.split(":")[0];
|
||||||
|
String entityModId = entityId.split(":")[0];
|
||||||
|
return entityModId.equals(patternModId);
|
||||||
|
} else {
|
||||||
|
// 模组格式: #modid
|
||||||
|
String entityModId = entityId.split(":")[0];
|
||||||
|
return entityModId.equals(body);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 实体格式: modid:entity_id
|
||||||
|
return entityId.equals(pattern);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 添加一个重载方法,方便使用Entity对象
|
||||||
|
public boolean isEntityTeleportAllowed(Entity entity) {
|
||||||
|
return isEntityTeleportAllowed(entity.getType());
|
||||||
|
}
|
||||||
|
|
||||||
|
// ========== 命令配置相关方法 ==========
|
||||||
|
|
||||||
|
public String getCommandPrefix() {
|
||||||
|
return commandPrefixCache;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isCommandPrefixEnabled() {
|
||||||
|
return enableCommandPrefixCache;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getFullCommand(String subCommand) {
|
||||||
|
return isCommandPrefixEnabled() ?
|
||||||
|
getCommandPrefix() + " " + subCommand :
|
||||||
|
subCommand;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ========== 拴绳物理配置相关方法 ==========
|
||||||
|
|
||||||
|
public double getMaxLeashLength() {
|
||||||
|
return maxLeashLengthCache;
|
||||||
|
}
|
||||||
|
|
||||||
|
public double getElasticDistance() {
|
||||||
|
return elasticDistanceCache;
|
||||||
|
}
|
||||||
|
|
||||||
|
public double getExtremeSnapFactor() {
|
||||||
|
return extremeSnapFactorCache;
|
||||||
|
}
|
||||||
|
|
||||||
|
public double getBreakDistance() {
|
||||||
|
return getMaxLeashLength() * getExtremeSnapFactor();
|
||||||
|
}
|
||||||
|
|
||||||
|
public double getSpringDampening() {
|
||||||
|
return springDampeningCache;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Double> getAxisSpecificElasticity() {
|
||||||
|
return Collections.unmodifiableList(axisSpecificElasticityCache);
|
||||||
|
}
|
||||||
|
|
||||||
|
public double getXElasticity() {
|
||||||
|
return !axisSpecificElasticityCache.isEmpty() ? axisSpecificElasticityCache.get(0) : 0.8;
|
||||||
|
}
|
||||||
|
|
||||||
|
public double getYElasticity() {
|
||||||
|
return axisSpecificElasticityCache.size() > 1 ? axisSpecificElasticityCache.get(1) : 0.2;
|
||||||
|
}
|
||||||
|
|
||||||
|
public double getZElasticity() {
|
||||||
|
return axisSpecificElasticityCache.size() > 2 ? axisSpecificElasticityCache.get(2) : 0.8;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ========== 实体限制配置相关方法 ==========
|
||||||
|
|
||||||
|
public int getMaxLeashesPerEntity() {
|
||||||
|
return maxLeashesPerEntityCache;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean canEntityAcceptMoreLeashes(Entity entity, int currentLeashCount) {
|
||||||
|
return currentLeashCount < getMaxLeashesPerEntity();
|
||||||
|
}
|
||||||
|
|
||||||
|
// ========== 管理方法 ==========
|
||||||
|
|
||||||
|
public void reload() {
|
||||||
|
reloadAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void clear() {
|
||||||
|
entityOffsetMap.clear();
|
||||||
|
tagOffsetMap.clear();
|
||||||
|
modOffsetMap.clear();
|
||||||
|
teleportWhitelistCache = Collections.emptyList();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getStats() {
|
||||||
|
return String.format("Entities: %d, Tags: %d, Mods: %d, TeleportWhitelist: %d",
|
||||||
|
entityOffsetMap.size(), tagOffsetMap.size(), modOffsetMap.size(),
|
||||||
|
teleportWhitelistCache.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void loading(LeashConfigManager manager) {
|
||||||
|
manager.reloadAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void reloading(LeashConfigManager manager) {
|
||||||
|
manager.reloadAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void unloading(LeashConfigManager manager) {
|
||||||
|
manager.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -18,7 +18,7 @@ package top.r3944realms.superleadrope.content.capability;
|
||||||
import net.minecraft.server.level.ServerPlayer;
|
import net.minecraft.server.level.ServerPlayer;
|
||||||
import net.minecraft.world.entity.player.Player;
|
import net.minecraft.world.entity.player.Player;
|
||||||
import net.minecraftforge.event.entity.player.PlayerEvent;
|
import net.minecraftforge.event.entity.player.PlayerEvent;
|
||||||
import top.r3944realms.superleadrope.util.capability.LeashUtil;
|
import top.r3944realms.superleadrope.util.capability.LeashStateAPI;
|
||||||
|
|
||||||
public class CapabilityRemainder {
|
public class CapabilityRemainder {
|
||||||
public static void onPlayerClone(PlayerEvent.Clone event) {
|
public static void onPlayerClone(PlayerEvent.Clone event) {
|
||||||
|
|
@ -26,9 +26,9 @@ public class CapabilityRemainder {
|
||||||
if(newEntity instanceof ServerPlayer newPlayer) {
|
if(newEntity instanceof ServerPlayer newPlayer) {
|
||||||
Player original = event.getOriginal();
|
Player original = event.getOriginal();
|
||||||
original.reviveCaps();
|
original.reviveCaps();
|
||||||
LeashUtil.getLeashState(original)
|
LeashStateAPI.getLeashState(original)
|
||||||
.ifPresent(oldCap ->
|
.ifPresent(oldCap ->
|
||||||
LeashUtil.getLeashState(newPlayer)
|
LeashStateAPI.getLeashState(newPlayer)
|
||||||
.ifPresent(newData ->
|
.ifPresent(newData ->
|
||||||
newData.copy(oldCap, newEntity)
|
newData.copy(oldCap, newEntity)
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -21,8 +21,8 @@ import net.minecraft.nbt.ListTag;
|
||||||
import net.minecraft.nbt.NbtUtils;
|
import net.minecraft.nbt.NbtUtils;
|
||||||
import net.minecraft.server.level.ServerLevel;
|
import net.minecraft.server.level.ServerLevel;
|
||||||
import net.minecraft.server.level.ServerPlayer;
|
import net.minecraft.server.level.ServerPlayer;
|
||||||
|
import net.minecraft.sounds.SoundSource;
|
||||||
import net.minecraft.world.entity.Entity;
|
import net.minecraft.world.entity.Entity;
|
||||||
import net.minecraft.world.entity.EntityType;
|
|
||||||
import net.minecraft.world.entity.LivingEntity;
|
import net.minecraft.world.entity.LivingEntity;
|
||||||
import net.minecraft.world.entity.Mob;
|
import net.minecraft.world.entity.Mob;
|
||||||
import net.minecraft.world.entity.animal.horse.Llama;
|
import net.minecraft.world.entity.animal.horse.Llama;
|
||||||
|
|
@ -30,26 +30,26 @@ import net.minecraft.world.entity.player.Player;
|
||||||
import net.minecraft.world.entity.vehicle.Boat;
|
import net.minecraft.world.entity.vehicle.Boat;
|
||||||
import net.minecraft.world.entity.vehicle.Minecart;
|
import net.minecraft.world.entity.vehicle.Minecart;
|
||||||
import net.minecraft.world.level.Level;
|
import net.minecraft.world.level.Level;
|
||||||
|
import net.minecraft.world.level.pathfinder.Path;
|
||||||
import net.minecraft.world.phys.AABB;
|
import net.minecraft.world.phys.AABB;
|
||||||
import net.minecraft.world.phys.Vec3;
|
import net.minecraft.world.phys.Vec3;
|
||||||
import net.minecraftforge.network.PacketDistributor;
|
import net.minecraftforge.network.PacketDistributor;
|
||||||
import org.jetbrains.annotations.Contract;
|
import org.jetbrains.annotations.Contract;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import top.r3944realms.superleadrope.CommonEventHandler;
|
||||||
import top.r3944realms.superleadrope.SuperLeadRope;
|
import top.r3944realms.superleadrope.SuperLeadRope;
|
||||||
import top.r3944realms.superleadrope.config.LeashCommonConfig;
|
|
||||||
import top.r3944realms.superleadrope.content.capability.inter.ILeashData;
|
import top.r3944realms.superleadrope.content.capability.inter.ILeashData;
|
||||||
import top.r3944realms.superleadrope.content.entity.SuperLeashKnotEntity;
|
import top.r3944realms.superleadrope.content.entity.SuperLeashKnotEntity;
|
||||||
|
import top.r3944realms.superleadrope.core.register.SLPSoundEvents;
|
||||||
import top.r3944realms.superleadrope.network.NetworkHandler;
|
import top.r3944realms.superleadrope.network.NetworkHandler;
|
||||||
import top.r3944realms.superleadrope.network.toClient.LeashDataSyncPacket;
|
import top.r3944realms.superleadrope.network.toClient.LeashDataSyncPacket;
|
||||||
import top.r3944realms.superleadrope.util.capability.LeashUtil;
|
import top.r3944realms.superleadrope.util.capability.LeashDataAPI;
|
||||||
import top.r3944realms.superleadrope.util.nbt.NBTReader;
|
import top.r3944realms.superleadrope.util.capability.LeashStateAPI;
|
||||||
import top.r3944realms.superleadrope.util.nbt.NBTWriter;
|
|
||||||
import top.r3944realms.superleadrope.util.riding.RindingLeash;
|
import top.r3944realms.superleadrope.util.riding.RindingLeash;
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
import java.util.concurrent.CopyOnWriteArraySet;
|
import java.util.concurrent.CopyOnWriteArraySet;
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
import java.util.function.Predicate;
|
import java.util.function.Predicate;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
@ -85,34 +85,6 @@ import java.util.stream.Stream;
|
||||||
* </table>
|
* </table>
|
||||||
*/
|
*/
|
||||||
public class LeashDataImpl implements ILeashData {
|
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<? extends Double> 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 final Entity entity;
|
||||||
private boolean needsSync = false;
|
private boolean needsSync = false;
|
||||||
private long lastSyncTime;
|
private long lastSyncTime;
|
||||||
|
|
@ -172,18 +144,18 @@ public class LeashDataImpl implements ILeashData {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean addLeash(Entity holder) {
|
public boolean addLeash(Entity holder) {
|
||||||
return addLeash(holder, Config.maxLeashDistance());
|
return addLeash(holder, CommonEventHandler.leashConfigManager.getMaxLeashLength());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean addLeash(Entity holder, String reserved) {
|
public boolean addLeash(Entity holder, String reserved) {
|
||||||
return addLeash(holder, Config.maxLeashDistance(), reserved);
|
return addLeash(holder, CommonEventHandler.leashConfigManager.getMaxLeashLength(), reserved);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 添加拴绳(支持自定义最大长度)
|
// 添加拴绳(支持自定义最大长度)
|
||||||
@Override
|
@Override
|
||||||
public boolean addLeash(Entity holder, double maxDistance) {
|
public boolean addLeash(Entity holder, double maxDistance) {
|
||||||
return addLeash(holder, maxDistance, Config.leashElasticDist(), 0, "");
|
return addLeash(holder, maxDistance, CommonEventHandler.leashConfigManager.getElasticDistance(), 0, "");
|
||||||
}
|
}
|
||||||
|
|
||||||
// 添加拴绳(支持自定义最大长度和弹性距离)
|
// 添加拴绳(支持自定义最大长度和弹性距离)
|
||||||
|
|
@ -195,7 +167,7 @@ public class LeashDataImpl implements ILeashData {
|
||||||
// 添加拴绳(支持自定义最大长度 + reserved 字段)
|
// 添加拴绳(支持自定义最大长度 + reserved 字段)
|
||||||
@Override
|
@Override
|
||||||
public boolean addLeash(Entity holder, double maxDistance, String reserved) {
|
public boolean addLeash(Entity holder, double maxDistance, String reserved) {
|
||||||
return addLeash(holder, maxDistance, Config.leashElasticDist(), 0, reserved);
|
return addLeash(holder, maxDistance, CommonEventHandler.leashConfigManager.getElasticDistance(), 0, reserved);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 添加拴绳(最终实现:支持最大长度、弹性距离、保持 Tick、reserved)
|
// 添加拴绳(最终实现:支持最大长度、弹性距离、保持 Tick、reserved)
|
||||||
|
|
@ -219,7 +191,6 @@ public class LeashDataImpl implements ILeashData {
|
||||||
LeashInfo info = LeashInfo.create(
|
LeashInfo info = LeashInfo.create(
|
||||||
holder,
|
holder,
|
||||||
reserved,
|
reserved,
|
||||||
calculateAttachOffset(entity),
|
|
||||||
maxDistance,
|
maxDistance,
|
||||||
elasticDistance,
|
elasticDistance,
|
||||||
maxKeepLeashTicks,
|
maxKeepLeashTicks,
|
||||||
|
|
@ -231,7 +202,7 @@ public class LeashDataImpl implements ILeashData {
|
||||||
} else {
|
} else {
|
||||||
leashHolders.put(holder.getUUID(), info);
|
leashHolders.put(holder.getUUID(), info);
|
||||||
}
|
}
|
||||||
|
LeashStateAPI.Operations.attach(entity, holder);
|
||||||
markForSync();
|
markForSync();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
@ -301,7 +272,6 @@ public class LeashDataImpl implements ILeashData {
|
||||||
old.holderUUIDOpt().get(),
|
old.holderUUIDOpt().get(),
|
||||||
old.holderIdOpt().get(),
|
old.holderIdOpt().get(),
|
||||||
old.reserved(),
|
old.reserved(),
|
||||||
old.attachOffset(),
|
|
||||||
newMaxDistance,
|
newMaxDistance,
|
||||||
old.elasticDistance(),
|
old.elasticDistance(),
|
||||||
old.keepLeashTicks(),
|
old.keepLeashTicks(),
|
||||||
|
|
@ -316,7 +286,6 @@ public class LeashDataImpl implements ILeashData {
|
||||||
old.holderUUIDOpt().get(),
|
old.holderUUIDOpt().get(),
|
||||||
old.holderIdOpt().get(),
|
old.holderIdOpt().get(),
|
||||||
old.reserved(),
|
old.reserved(),
|
||||||
old.attachOffset(),
|
|
||||||
newMaxDistance,
|
newMaxDistance,
|
||||||
old.elasticDistance(),
|
old.elasticDistance(),
|
||||||
Math.min(old.keepLeashTicks(), newMaxKeepLeashTicks),
|
Math.min(old.keepLeashTicks(), newMaxKeepLeashTicks),
|
||||||
|
|
@ -331,7 +300,6 @@ public class LeashDataImpl implements ILeashData {
|
||||||
old.holderUUIDOpt().get(),
|
old.holderUUIDOpt().get(),
|
||||||
old.holderIdOpt().get(),
|
old.holderIdOpt().get(),
|
||||||
reserved,
|
reserved,
|
||||||
old.attachOffset(),
|
|
||||||
distance,
|
distance,
|
||||||
old.elasticDistance(),
|
old.elasticDistance(),
|
||||||
Math.min(old.keepLeashTicks(), maxKeepTicks),
|
Math.min(old.keepLeashTicks(), maxKeepTicks),
|
||||||
|
|
@ -346,7 +314,6 @@ public class LeashDataImpl implements ILeashData {
|
||||||
old.blockPosOpt().get(),
|
old.blockPosOpt().get(),
|
||||||
old.holderIdOpt().get(),
|
old.holderIdOpt().get(),
|
||||||
old.reserved(),
|
old.reserved(),
|
||||||
old.attachOffset(),
|
|
||||||
newMaxDistance,
|
newMaxDistance,
|
||||||
old.elasticDistance(),
|
old.elasticDistance(),
|
||||||
old.keepLeashTicks(),
|
old.keepLeashTicks(),
|
||||||
|
|
@ -361,7 +328,6 @@ public class LeashDataImpl implements ILeashData {
|
||||||
old.blockPosOpt().get(),
|
old.blockPosOpt().get(),
|
||||||
old.holderIdOpt().get(),
|
old.holderIdOpt().get(),
|
||||||
old.reserved(),
|
old.reserved(),
|
||||||
old.attachOffset(),
|
|
||||||
newMaxDistance,
|
newMaxDistance,
|
||||||
old.elasticDistance(),
|
old.elasticDistance(),
|
||||||
Math.min(old.keepLeashTicks(), newMaxKeepLeashTicks),
|
Math.min(old.keepLeashTicks(), newMaxKeepLeashTicks),
|
||||||
|
|
@ -376,7 +342,6 @@ public class LeashDataImpl implements ILeashData {
|
||||||
old.blockPosOpt().get(),
|
old.blockPosOpt().get(),
|
||||||
old.holderIdOpt().get(),
|
old.holderIdOpt().get(),
|
||||||
reserved,
|
reserved,
|
||||||
old.attachOffset(),
|
|
||||||
distance,
|
distance,
|
||||||
old.elasticDistance(),
|
old.elasticDistance(),
|
||||||
Math.min(old.keepLeashTicks(), maxKeepTicks),
|
Math.min(old.keepLeashTicks(), maxKeepTicks),
|
||||||
|
|
@ -399,7 +364,6 @@ public class LeashDataImpl implements ILeashData {
|
||||||
old.holderUUIDOpt().get(),
|
old.holderUUIDOpt().get(),
|
||||||
old.holderIdOpt().get(),
|
old.holderIdOpt().get(),
|
||||||
old.reserved(),
|
old.reserved(),
|
||||||
old.attachOffset(),
|
|
||||||
old.maxDistance(),
|
old.maxDistance(),
|
||||||
newElasticDistance,
|
newElasticDistance,
|
||||||
old.keepLeashTicks(),
|
old.keepLeashTicks(),
|
||||||
|
|
@ -414,7 +378,6 @@ public class LeashDataImpl implements ILeashData {
|
||||||
old.blockPosOpt().get(),
|
old.blockPosOpt().get(),
|
||||||
old.holderIdOpt().get(),
|
old.holderIdOpt().get(),
|
||||||
old.reserved(),
|
old.reserved(),
|
||||||
old.attachOffset(),
|
|
||||||
old.maxDistance(),
|
old.maxDistance(),
|
||||||
newElasticDistance,
|
newElasticDistance,
|
||||||
old.keepLeashTicks(),
|
old.keepLeashTicks(),
|
||||||
|
|
@ -444,7 +407,6 @@ public class LeashDataImpl implements ILeashData {
|
||||||
old.holderUUIDOpt().get(),
|
old.holderUUIDOpt().get(),
|
||||||
old.holderIdOpt().get(),
|
old.holderIdOpt().get(),
|
||||||
old.reserved(),
|
old.reserved(),
|
||||||
old.attachOffset(),
|
|
||||||
old.maxDistance(),
|
old.maxDistance(),
|
||||||
newElasticDistance,
|
newElasticDistance,
|
||||||
Math.min(old.keepLeashTicks(), newMaxKeepLeashTicks),
|
Math.min(old.keepLeashTicks(), newMaxKeepLeashTicks),
|
||||||
|
|
@ -459,7 +421,6 @@ public class LeashDataImpl implements ILeashData {
|
||||||
old.holderUUIDOpt().get(),
|
old.holderUUIDOpt().get(),
|
||||||
old.holderIdOpt().get(),
|
old.holderIdOpt().get(),
|
||||||
reserved,
|
reserved,
|
||||||
old.attachOffset(),
|
|
||||||
old.maxDistance(),
|
old.maxDistance(),
|
||||||
distance,
|
distance,
|
||||||
Math.min(old.keepLeashTicks(), maxKeepTicks),
|
Math.min(old.keepLeashTicks(), maxKeepTicks),
|
||||||
|
|
@ -474,7 +435,6 @@ public class LeashDataImpl implements ILeashData {
|
||||||
old.blockPosOpt().get(),
|
old.blockPosOpt().get(),
|
||||||
old.holderIdOpt().get(),
|
old.holderIdOpt().get(),
|
||||||
old.reserved(),
|
old.reserved(),
|
||||||
old.attachOffset(),
|
|
||||||
old.maxDistance(),
|
old.maxDistance(),
|
||||||
newElasticDistance,
|
newElasticDistance,
|
||||||
old.keepLeashTicks(),
|
old.keepLeashTicks(),
|
||||||
|
|
@ -489,7 +449,6 @@ public class LeashDataImpl implements ILeashData {
|
||||||
old.blockPosOpt().get(),
|
old.blockPosOpt().get(),
|
||||||
old.holderIdOpt().get(),
|
old.holderIdOpt().get(),
|
||||||
reserved,
|
reserved,
|
||||||
old.attachOffset(),
|
|
||||||
old.maxDistance(),
|
old.maxDistance(),
|
||||||
newElasticDistance,
|
newElasticDistance,
|
||||||
Math.min(old.keepLeashTicks(), newMaxKeepLeashTicks),
|
Math.min(old.keepLeashTicks(), newMaxKeepLeashTicks),
|
||||||
|
|
@ -502,13 +461,17 @@ public class LeashDataImpl implements ILeashData {
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void applyLeashForces() {
|
public void applyLeashForces() {
|
||||||
Vec3 combinedForce = Vec3.ZERO; // 初始化合力向量
|
Vec3 combinedForce = Vec3.ZERO;
|
||||||
|
Vec3 combinedDirection = Vec3.ZERO;
|
||||||
|
int validLeashes = 0;
|
||||||
|
|
||||||
// 计算所有拴绳的合力
|
// 计算所有拴绳的合力和平均方向
|
||||||
for (Map.Entry<UUID, LeashInfo> entry : leashHolders.entrySet()) {
|
for (Map.Entry<UUID, LeashInfo> entry : leashHolders.entrySet()) {
|
||||||
Vec3 force = calculateLeashForceForUUID(entry);
|
Vec3 force = calculateLeashForceForUUID(entry);
|
||||||
if (force != null) {
|
if (force != null) {
|
||||||
combinedForce = combinedForce.add(force);
|
combinedForce = combinedForce.add(force);
|
||||||
|
combinedDirection = combinedDirection.add(force.normalize());
|
||||||
|
validLeashes++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -516,27 +479,152 @@ public class LeashDataImpl implements ILeashData {
|
||||||
Vec3 force = calculateLeashForceForBlockPos(entry);
|
Vec3 force = calculateLeashForceForBlockPos(entry);
|
||||||
if (force != null) {
|
if (force != null) {
|
||||||
combinedForce = combinedForce.add(force);
|
combinedForce = combinedForce.add(force);
|
||||||
|
combinedDirection = combinedDirection.add(force.normalize());
|
||||||
|
validLeashes++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean hasForce = !combinedForce.equals(Vec3.ZERO);
|
boolean hasForce = !combinedForce.equals(Vec3.ZERO);
|
||||||
Entity finalApplyEntity = RindingLeash.getFinalEntityForLeashIfForce(entity, hasForce);
|
Entity finalApplyEntity = RindingLeash.getFinalEntityForLeashIfForce(entity, hasForce);
|
||||||
if (hasForce) {
|
|
||||||
|
|
||||||
|
if (hasForce) {
|
||||||
|
// 处理玩家和其他实体
|
||||||
if (finalApplyEntity instanceof ServerPlayer player) {
|
if (finalApplyEntity instanceof ServerPlayer player) {
|
||||||
RindingLeash.applyForceToPlayer(player, combinedForce);
|
RindingLeash.applyForceToPlayer(player, combinedForce);
|
||||||
return;
|
|
||||||
} else {
|
} else {
|
||||||
finalApplyEntity.setDeltaMovement(finalApplyEntity.getDeltaMovement().add(combinedForce));
|
finalApplyEntity.setDeltaMovement(finalApplyEntity.getDeltaMovement().add(combinedForce));
|
||||||
finalApplyEntity.hurtMarked = true;
|
finalApplyEntity.hurtMarked = true;
|
||||||
|
|
||||||
|
// 对生物使用合力方向进行移动(只有在能够移动时才执行)
|
||||||
|
if (finalApplyEntity instanceof Mob mob && validLeashes > 0 && canMobMove(mob)) {
|
||||||
|
moveMobTowardsCombinedDirection(mob, combinedDirection, validLeashes, combinedForce.length());
|
||||||
|
} else if (finalApplyEntity instanceof Mob mob) {
|
||||||
|
// 无法移动时停止导航
|
||||||
|
mob.getNavigation().stop();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
RindingLeash.protectAnimalMovement(finalApplyEntity, true);
|
RindingLeash.protectAnimalMovement(finalApplyEntity, true);
|
||||||
} else {
|
} else {
|
||||||
RindingLeash.protectAnimalMovement(finalApplyEntity, false);
|
RindingLeash.protectAnimalMovement(finalApplyEntity, false);
|
||||||
|
|
||||||
|
// 没有力时也停止导航
|
||||||
|
if (finalApplyEntity instanceof Mob mob) {
|
||||||
|
mob.getNavigation().stop();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查生物是否能够移动
|
||||||
|
*/
|
||||||
|
private boolean canMobMove(Mob mob) {
|
||||||
|
// 检查各种无法移动的情况
|
||||||
|
return !mob.isNoGravity() && // 有重力才能移动
|
||||||
|
!mob.isSleeping() && // 没有在睡觉
|
||||||
|
!mob.isDeadOrDying() && // 没有死亡或濒死
|
||||||
|
!mob.isFreezing() && // 没有被冻结
|
||||||
|
mob.canUpdate() && // 可以更新
|
||||||
|
mob.isEffectiveAi() && // AI有效
|
||||||
|
mob.getDeltaMovement().lengthSqr() < 100.0; // 移动速度不是特别快(防止异常情况)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 让生物朝着合力方向移动
|
||||||
|
*/
|
||||||
|
private void moveMobTowardsCombinedDirection(Mob mob, Vec3 combinedDirection, int leashCount, double forceMagnitude) {
|
||||||
|
if (combinedDirection.equals(Vec3.ZERO)) return;
|
||||||
|
|
||||||
|
// 再次检查是否能够移动
|
||||||
|
if (!canMobMove(mob)) {
|
||||||
|
mob.getNavigation().stop();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 计算平均方向
|
||||||
|
Vec3 averageDirection = combinedDirection.scale(1.0 / leashCount).normalize();
|
||||||
|
|
||||||
|
// 根据力的大小调整移动速度
|
||||||
|
double speed = calculateMobSpeed(mob, forceMagnitude);
|
||||||
|
|
||||||
|
// 计算目标位置(在合力方向上稍微超前一点)
|
||||||
|
Vec3 targetPos = mob.position().add(averageDirection.scale(3.0)); // 3格距离
|
||||||
|
|
||||||
|
// 检查目标位置是否可达
|
||||||
|
if (isPositionReachable(mob, targetPos)) {
|
||||||
|
// 设置移动目标
|
||||||
|
mob.getNavigation().moveTo(targetPos.x, targetPos.y, targetPos.z, speed);
|
||||||
|
|
||||||
|
// 设置生物朝向合力方向
|
||||||
|
mob.getLookControl().setLookAt(targetPos);
|
||||||
|
} else {
|
||||||
|
// 位置不可达时停止导航
|
||||||
|
mob.getNavigation().stop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查位置是否可达
|
||||||
|
*/
|
||||||
|
private boolean isPositionReachable(Mob mob, Vec3 targetPos) {
|
||||||
|
// 简单的距离检查
|
||||||
|
double distance = mob.position().distanceTo(targetPos);
|
||||||
|
if (distance > 20.0) { // 距离太远
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查是否有导航路径
|
||||||
|
Path path = mob.getNavigation().createPath(targetPos.x, targetPos.y, targetPos.z, 0);
|
||||||
|
return path != null && !path.isDone();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 增强的移动能力检查
|
||||||
|
*/
|
||||||
|
private boolean canMobMoveEnhanced(Mob mob) {
|
||||||
|
// 基础移动检查
|
||||||
|
if (!canMobMove(mob)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查导航系统是否可用
|
||||||
|
if (mob.getNavigation().isDone() || mob.getNavigation().isStuck()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查生物是否被拴绳过度拉扯(防止无限尝试移动)
|
||||||
|
Vec3 motion = mob.getDeltaMovement();
|
||||||
|
if (motion.lengthSqr() > 4.0 && mob.tickCount % 20 == 0) {
|
||||||
|
// 如果移动速度过快,偶尔跳过移动尝试
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查生物是否在尝试移动但实际没有移动(卡住检测)
|
||||||
|
if (mob.getNavigation().getPath() != null &&
|
||||||
|
!mob.getNavigation().getPath().isDone()) {
|
||||||
|
|
||||||
|
// 获取上一tick的位置进行比较
|
||||||
|
double distanceMoved = mob.position().distanceTo(new Vec3(mob.xOld, mob.yOld, mob.zOld));
|
||||||
|
|
||||||
|
// 生物卡住了(有路径但几乎没有移动)
|
||||||
|
return !(distanceMoved < 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* 根据生物类型和力的大小计算移动速度
|
||||||
|
*/
|
||||||
|
private double calculateMobSpeed(Mob mob, double forceMagnitude) {
|
||||||
|
double baseSpeed = mob instanceof Llama ? 2.0 : 1.0;
|
||||||
|
|
||||||
|
// 力越大,移动速度越快(但有上限)
|
||||||
|
double forceFactor = Math.min(forceMagnitude * 0.5, 2.0); // 限制最大加速2倍
|
||||||
|
|
||||||
|
return baseSpeed * (1.0 + forceFactor);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 为UUID拴绳计算力
|
* 为UUID拴绳计算力
|
||||||
|
|
@ -569,9 +657,9 @@ public class LeashDataImpl implements ILeashData {
|
||||||
private Vec3 calculateLeashForce(Entity holder, Map.Entry<?, LeashInfo> entry) {
|
private Vec3 calculateLeashForce(Entity holder, Map.Entry<?, LeashInfo> entry) {
|
||||||
Vec3 holderPos = holder.position().add(0, holder.getBbHeight() * 0.7, 0);
|
Vec3 holderPos = holder.position().add(0, holder.getBbHeight() * 0.7, 0);
|
||||||
LeashInfo info = entry.getValue();
|
LeashInfo info = entry.getValue();
|
||||||
Vec3 entityPos = entity.position().add(info.attachOffset());
|
Vec3 entityPos = entity.position();
|
||||||
double distance = holderPos.distanceTo(entityPos);
|
double distance = holderPos.distanceTo(entityPos);
|
||||||
double extremeSnapDist = info.maxDistance() * Config.leashExtremeSnapDistFactor();
|
double extremeSnapDist = info.maxDistance() * CommonEventHandler.leashConfigManager.getExtremeSnapFactor();
|
||||||
|
|
||||||
// 1. 检查是否超出断裂距离
|
// 1. 检查是否超出断裂距离
|
||||||
if (distance > extremeSnapDist) {
|
if (distance > extremeSnapDist) {
|
||||||
|
|
@ -583,6 +671,8 @@ public class LeashDataImpl implements ILeashData {
|
||||||
}
|
}
|
||||||
// 断裂
|
// 断裂
|
||||||
removeLeash(holder);
|
removeLeash(holder);
|
||||||
|
//TODO: 是不是应该考虑让断裂统一发出声音,还是就这样由断裂发出
|
||||||
|
entity.level().playSound(null, holder.getOnPos(), SLPSoundEvents.LEAD_BREAK.get(), SoundSource.PLAYERS);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -590,15 +680,6 @@ public class LeashDataImpl implements ILeashData {
|
||||||
Vec3 pullForce = Vec3.ZERO;
|
Vec3 pullForce = Vec3.ZERO;
|
||||||
if (distance > info.elasticDistance()) {
|
if (distance > info.elasticDistance()) {
|
||||||
pullForce = calculatePullForce(holderPos, entityPos, distance, info);
|
pullForce = calculatePullForce(holderPos, entityPos, distance, info);
|
||||||
|
|
||||||
// 生物添加跟随逻辑(保持不变)
|
|
||||||
if(entity instanceof Mob mob) {
|
|
||||||
Vec3 vec3 = (new Vec3(holder.getX() - entity.getX(), holder.getY() - entity.getY(), holder.getZ() - entity.getZ()))
|
|
||||||
.normalize()
|
|
||||||
.scale(Math.max(distance - 2.0F, 0.0F));
|
|
||||||
double speed = mob instanceof Llama ? 2.0 : 1.0;
|
|
||||||
mob.getNavigation().moveTo(entity.getX() + vec3.x, entity.getY() + vec3.y, entity.getZ() + vec3.z, speed);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3. 重置缓冲Tick
|
// 3. 重置缓冲Tick
|
||||||
|
|
@ -621,13 +702,13 @@ public class LeashDataImpl implements ILeashData {
|
||||||
}
|
}
|
||||||
|
|
||||||
Vec3 pullForce = pullDirection.scale(
|
Vec3 pullForce = pullDirection.scale(
|
||||||
(distance - info.elasticDistance()) * pullStrength * Config.springDampening()
|
(distance - info.elasticDistance()) * pullStrength * CommonEventHandler.leashConfigManager.getSpringDampening()
|
||||||
);
|
);
|
||||||
|
|
||||||
return new Vec3(
|
return new Vec3(
|
||||||
pullForce.x * Config.axisSpecificElasticity().x,
|
pullForce.x * CommonEventHandler.leashConfigManager.getXElasticity(),
|
||||||
pullForce.y * Config.axisSpecificElasticity().y,
|
pullForce.y * CommonEventHandler.leashConfigManager.getXElasticity(),
|
||||||
pullForce.z * Config.axisSpecificElasticity().z
|
pullForce.z * CommonEventHandler.leashConfigManager.getZElasticity()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -638,13 +719,13 @@ public class LeashDataImpl implements ILeashData {
|
||||||
double pullStrength = 1.0 + excessRatio * 2.0;
|
double pullStrength = 1.0 + excessRatio * 2.0;
|
||||||
|
|
||||||
Vec3 pullForce = pullDirection.scale(
|
Vec3 pullForce = pullDirection.scale(
|
||||||
(distance - info.elasticDistance()) * pullStrength * Config.springDampening()
|
(distance - info.elasticDistance()) * pullStrength * CommonEventHandler.leashConfigManager.getSpringDampening()
|
||||||
);
|
);
|
||||||
|
|
||||||
return new Vec3(
|
return new Vec3(
|
||||||
pullForce.x * Config.axisSpecificElasticity().x,
|
pullForce.x * CommonEventHandler.leashConfigManager.getXElasticity(),
|
||||||
pullForce.y * Config.axisSpecificElasticity().y,
|
pullForce.y * CommonEventHandler.leashConfigManager.getYElasticity(),
|
||||||
pullForce.z * Config.axisSpecificElasticity().z
|
pullForce.z * CommonEventHandler.leashConfigManager.getZElasticity()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -658,16 +739,20 @@ public class LeashDataImpl implements ILeashData {
|
||||||
@Override
|
@Override
|
||||||
public boolean removeLeash(UUID holderUUID) {
|
public boolean removeLeash(UUID holderUUID) {
|
||||||
boolean removed = leashHolders.remove(holderUUID) != null;
|
boolean removed = leashHolders.remove(holderUUID) != null;
|
||||||
if (removed)
|
if (removed) {
|
||||||
|
LeashStateAPI.Operations.detach(entity, holderUUID);
|
||||||
markForSync();
|
markForSync();
|
||||||
|
}
|
||||||
return removed;
|
return removed;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean removeLeash(BlockPos knotPos) {
|
public boolean removeLeash(BlockPos knotPos) {
|
||||||
boolean removed = leashKnots.remove(knotPos) != null;
|
boolean removed = leashKnots.remove(knotPos) != null;
|
||||||
if (removed)
|
if (removed) {
|
||||||
|
LeashStateAPI.Operations.detach(entity, knotPos);
|
||||||
markForSync();
|
markForSync();
|
||||||
|
}
|
||||||
return removed;
|
return removed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -675,18 +760,21 @@ public class LeashDataImpl implements ILeashData {
|
||||||
public void removeAllLeashes() {
|
public void removeAllLeashes() {
|
||||||
leashHolders.clear();
|
leashHolders.clear();
|
||||||
leashKnots.clear();
|
leashKnots.clear();
|
||||||
|
LeashStateAPI.Offset.removeAll(entity);
|
||||||
markForSync();
|
markForSync();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void removeAllHolderLeashes() {
|
public void removeAllHolderLeashes() {
|
||||||
leashHolders.clear();
|
leashHolders.clear();
|
||||||
|
LeashStateAPI.Offset.removeAllUUIDs(entity);
|
||||||
markForSync();
|
markForSync();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void removeAllKnotLeashes() {
|
public void removeAllKnotLeashes() {
|
||||||
leashKnots.clear();
|
leashKnots.clear();
|
||||||
|
LeashStateAPI.Offset.removeAllBlockPoses(entity);
|
||||||
markForSync();
|
markForSync();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -716,6 +804,7 @@ public class LeashDataImpl implements ILeashData {
|
||||||
LeashInfo leashInfo = info.transferHolder(newHolder);
|
LeashInfo leashInfo = info.transferHolder(newHolder);
|
||||||
leashHolders.put(newHolder.getUUID(), leashInfo);
|
leashHolders.put(newHolder.getUUID(), leashInfo);
|
||||||
}
|
}
|
||||||
|
LeashStateAPI.Operations.transfer(entity, oldHolderUUID, newHolder);
|
||||||
markForSync();
|
markForSync();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
@ -730,6 +819,7 @@ public class LeashDataImpl implements ILeashData {
|
||||||
LeashInfo leashInfo = info.transferHolder(newHolder, reserved);
|
LeashInfo leashInfo = info.transferHolder(newHolder, reserved);
|
||||||
leashHolders.put(newHolder.getUUID(), leashInfo);
|
leashHolders.put(newHolder.getUUID(), leashInfo);
|
||||||
}
|
}
|
||||||
|
LeashStateAPI.Operations.transfer(entity, oldHolderUUID, newHolder);
|
||||||
markForSync();
|
markForSync();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
@ -745,6 +835,7 @@ public class LeashDataImpl implements ILeashData {
|
||||||
LeashInfo leashInfo = info.transferHolder(newHolder);
|
LeashInfo leashInfo = info.transferHolder(newHolder);
|
||||||
leashHolders.put(newHolder.getUUID(), leashInfo);
|
leashHolders.put(newHolder.getUUID(), leashInfo);
|
||||||
}
|
}
|
||||||
|
LeashStateAPI.Operations.transfer(entity, knotPos, newHolder);
|
||||||
markForSync();
|
markForSync();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
@ -760,6 +851,7 @@ public class LeashDataImpl implements ILeashData {
|
||||||
LeashInfo leashInfo = info.transferHolder(newHolder, reserved);
|
LeashInfo leashInfo = info.transferHolder(newHolder, reserved);
|
||||||
leashHolders.put(newHolder.getUUID(), leashInfo);
|
leashHolders.put(newHolder.getUUID(), leashInfo);
|
||||||
}
|
}
|
||||||
|
LeashStateAPI.Operations.transfer(entity, knotPos, newHolder);
|
||||||
markForSync();
|
markForSync();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
@ -885,7 +977,6 @@ public class LeashDataImpl implements ILeashData {
|
||||||
}
|
}
|
||||||
infoTag.putInt("HolderID", info.holderIdOpt().get());
|
infoTag.putInt("HolderID", info.holderIdOpt().get());
|
||||||
infoTag.putString("LeashItem", info.reserved());
|
infoTag.putString("LeashItem", info.reserved());
|
||||||
infoTag.put("Offset", NBTWriter.writeVec3(info.attachOffset()));
|
|
||||||
infoTag.putDouble("MaxDistance", info.maxDistance());
|
infoTag.putDouble("MaxDistance", info.maxDistance());
|
||||||
infoTag.putDouble("ElasticDistance", info.elasticDistance());
|
infoTag.putDouble("ElasticDistance", info.elasticDistance());
|
||||||
infoTag.putInt("KeepLeashTicks", info.keepLeashTicks());
|
infoTag.putInt("KeepLeashTicks", info.keepLeashTicks());
|
||||||
|
|
@ -933,7 +1024,6 @@ public class LeashDataImpl implements ILeashData {
|
||||||
infoTag.getUUID("HolderUUID"),
|
infoTag.getUUID("HolderUUID"),
|
||||||
infoTag.getInt("HolderID"),
|
infoTag.getInt("HolderID"),
|
||||||
infoTag.getString("LeashItem"),
|
infoTag.getString("LeashItem"),
|
||||||
NBTReader.readVec3(infoTag.getCompound("Offset")),
|
|
||||||
infoTag.getDouble("MaxDistance"),
|
infoTag.getDouble("MaxDistance"),
|
||||||
infoTag.contains("ElasticDistance") ? infoTag.getDouble("ElasticDistance") : 6.0,
|
infoTag.contains("ElasticDistance") ? infoTag.getDouble("ElasticDistance") : 6.0,
|
||||||
infoTag.getInt("KeepLeashTicks"),
|
infoTag.getInt("KeepLeashTicks"),
|
||||||
|
|
@ -949,7 +1039,6 @@ public class LeashDataImpl implements ILeashData {
|
||||||
NbtUtils.readBlockPos(infoTag.getCompound("KnotBlockPos")),
|
NbtUtils.readBlockPos(infoTag.getCompound("KnotBlockPos")),
|
||||||
infoTag.getInt("HolderID"),
|
infoTag.getInt("HolderID"),
|
||||||
infoTag.getString("LeashItem"),
|
infoTag.getString("LeashItem"),
|
||||||
NBTReader.readVec3(infoTag.getCompound("Offset")),
|
|
||||||
infoTag.getDouble("MaxDistance"),
|
infoTag.getDouble("MaxDistance"),
|
||||||
infoTag.contains("ElasticDistance") ? infoTag.getDouble("ElasticDistance") : 6.0,
|
infoTag.contains("ElasticDistance") ? infoTag.getDouble("ElasticDistance") : 6.0,
|
||||||
infoTag.getInt("KeepLeashTicks"),
|
infoTag.getInt("KeepLeashTicks"),
|
||||||
|
|
@ -961,7 +1050,7 @@ public class LeashDataImpl implements ILeashData {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean canBeLeashed() {
|
public boolean canBeLeashed() {
|
||||||
return (leashHolders.size() + leashKnots.size()) <= Config.maxLeashesPerEntity();
|
return (leashHolders.size() + leashKnots.size()) <= CommonEventHandler.leashConfigManager.getMaxLeashesPerEntity();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -1007,48 +1096,28 @@ public class LeashDataImpl implements ILeashData {
|
||||||
return leashableInArea(holder, i -> isLeashHolder(i, holder), 1024D);
|
return leashableInArea(holder, i -> isLeashHolder(i, holder), 1024D);
|
||||||
}
|
}
|
||||||
public boolean canBeAttachedTo(Entity pEntity) {
|
public boolean canBeAttachedTo(Entity pEntity) {
|
||||||
if(pEntity == entity) {
|
if (pEntity == entity) {
|
||||||
return false;
|
return false;
|
||||||
} else {
|
} else {
|
||||||
Optional<LeashInfo> leashInfo = getLeashInfo(pEntity);
|
Optional<LeashInfo> leashInfo = getLeashInfo(pEntity);
|
||||||
return leashInfo.isEmpty() && (entity.distanceTo(pEntity) <= Config.leashElasticDist() * Config.leashExtremeSnapDistFactor()) && canBeLeashed();//距离最大,则不可以被固定或转移
|
return leashInfo.isEmpty() && (entity.distanceTo(pEntity) <= CommonEventHandler.leashConfigManager.getElasticDistance() * CommonEventHandler.leashConfigManager.getExtremeSnapFactor()) && canBeLeashed();//距离最大,则不可以被固定或转移
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
public static boolean isLeashHolder(@NotNull Entity pEntity, UUID pHolderUUID) {
|
|
||||||
AtomicBoolean isTarget = new AtomicBoolean(false);
|
|
||||||
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);
|
|
||||||
LeashUtil.getLeashData(pEntity)
|
|
||||||
.ifPresent(i ->
|
|
||||||
isTarget.set(i.isLeashedBy(pKnotPos))
|
|
||||||
);
|
|
||||||
return isTarget.get();
|
|
||||||
}
|
|
||||||
public static boolean isLeashHolder(@NotNull Entity pEntity, Entity pTestHolder) {
|
public static boolean isLeashHolder(@NotNull Entity pEntity, Entity pTestHolder) {
|
||||||
return pTestHolder instanceof SuperLeashKnotEntity superLeashKnotEntity ?
|
return pTestHolder instanceof SuperLeashKnotEntity superLeashKnotEntity ?
|
||||||
isLeashHolder(pEntity, superLeashKnotEntity.getPos()) :
|
isLeashHolder(pEntity, superLeashKnotEntity.getPos()) :
|
||||||
isLeashHolder(pEntity, pTestHolder.getUUID());
|
isLeashHolder(pEntity, pTestHolder.getUUID());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static boolean isLeashHolder(@NotNull Entity pEntity, UUID pHolderUUID) {
|
||||||
// 计算拴绳附着点
|
return LeashDataAPI.getLeashData(pEntity)
|
||||||
@Contract("_ -> new")
|
.map(leashData -> leashData.isLeashedBy(pHolderUUID))
|
||||||
private @NotNull Vec3 calculateAttachOffset(@NotNull Entity entity) {
|
.orElse(false);
|
||||||
EntityType<?> type = entity.getType();
|
|
||||||
if (type == EntityType.HORSE || type == EntityType.DONKEY) {
|
|
||||||
return new Vec3(0, 1.4, 0.3);
|
|
||||||
} else if (type == EntityType.IRON_GOLEM) {
|
|
||||||
return new Vec3(0, 1.8, 0);
|
|
||||||
}
|
|
||||||
//TODO: 未来自定义配置
|
|
||||||
return new Vec3(0, entity.getBbHeight() * 0.8, 0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static boolean isLeashHolder(@NotNull Entity pEntity, BlockPos pKnotPos) {
|
||||||
|
return LeashDataAPI.getLeashData(pEntity)
|
||||||
|
.map(leashData -> leashData.isLeashedBy(pKnotPos))
|
||||||
|
.orElse(false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -127,7 +127,7 @@ public class LeashStateImpl implements ILeashState {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void resetLeashHolderLocationOffset(Entity holder) {
|
public void resetLeashHolderLocationOffset(Entity holder) {
|
||||||
if (entity instanceof SuperLeashKnotEntity leashKnot) {
|
if (holder instanceof SuperLeashKnotEntity leashKnot) {
|
||||||
resetLeashHolderLocationOffset(leashKnot.getPos());
|
resetLeashHolderLocationOffset(leashKnot.getPos());
|
||||||
} else resetLeashHolderLocationOffset(holder.getUUID());
|
} else resetLeashHolderLocationOffset(holder.getUUID());
|
||||||
}
|
}
|
||||||
|
|
@ -146,45 +146,97 @@ public class LeashStateImpl implements ILeashState {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setLeashHolderLocationOffset(Entity holder, Vec3 offset) {
|
public void setLeashHolderLocationOffset(Entity holder, Vec3 offset) {
|
||||||
if (entity instanceof SuperLeashKnotEntity leashKnot) {
|
if (holder instanceof SuperLeashKnotEntity leashKnot) {
|
||||||
setLeashHolderLocationOffset(leashKnot.getPos(), offset);
|
setLeashHolderLocationOffset(leashKnot.getPos(), offset);
|
||||||
} else setLeashHolderLocationOffset(holder.getUUID(), offset);
|
} else setLeashHolderLocationOffset(holder.getUUID(), offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setLeashHolderLocationOffset(UUID holderUUID, Vec3 offset) {
|
public void setLeashHolderLocationOffset(UUID holderUUID, Vec3 offset) {
|
||||||
leashHolders.computeIfPresent(holderUUID, (uuid, state) -> state.setHolderLocationOffset(offset));
|
LeashState currentState = leashHolders.get(holderUUID);
|
||||||
|
if (currentState == null) {
|
||||||
|
// 创建新的状态,使用默认的应用实体偏移量
|
||||||
|
leashHolders.put(holderUUID, new LeashState(
|
||||||
|
offset,
|
||||||
|
getDefaultLeashApplyEntityLocationOffset(),
|
||||||
|
Vec3.ZERO // 或者合适的默认值
|
||||||
|
));
|
||||||
|
} else {
|
||||||
|
// 更新现有状态
|
||||||
|
leashHolders.put(holderUUID,
|
||||||
|
currentState.setHolderLocationOffset(offset)
|
||||||
|
);
|
||||||
|
}
|
||||||
markForSync();
|
markForSync();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setLeashHolderLocationOffset(BlockPos knotPos, Vec3 leashHolderLocationOffset) {
|
public void setLeashHolderLocationOffset(BlockPos knotPos, Vec3 offset) {
|
||||||
leashKnots.computeIfPresent(knotPos, (blockPos, state) -> state.setHolderLocationOffset(leashHolderLocationOffset));
|
LeashState currentState = leashKnots.get(knotPos);
|
||||||
|
if (currentState == null) {
|
||||||
|
// 创建新的状态
|
||||||
|
leashKnots.put(knotPos, new LeashState(
|
||||||
|
offset,
|
||||||
|
getDefaultLeashApplyEntityLocationOffset(),
|
||||||
|
Vec3.ZERO
|
||||||
|
));
|
||||||
|
} else {
|
||||||
|
// 更新现有状态
|
||||||
|
leashKnots.put(knotPos,
|
||||||
|
currentState.setHolderLocationOffset(offset)
|
||||||
|
);
|
||||||
|
}
|
||||||
markForSync();
|
markForSync();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void addLeashHolderLocationOffset(Entity holder, Vec3 offset) {
|
public void addLeashHolderLocationOffset(Entity holder, Vec3 offset) {
|
||||||
if (entity instanceof SuperLeashKnotEntity leashKnot) {
|
if (holder instanceof SuperLeashKnotEntity leashKnot) {
|
||||||
addLeashHolderLocationOffset(leashKnot.getPos(), offset);
|
addLeashHolderLocationOffset(leashKnot.getPos(), offset);
|
||||||
} else addLeashHolderLocationOffset(holder.getUUID(), offset);
|
} else addLeashHolderLocationOffset(holder.getUUID(), offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void addLeashHolderLocationOffset(UUID holderUUID, Vec3 offset) {
|
public void addLeashHolderLocationOffset(UUID holderUUID, Vec3 offset) {
|
||||||
leashHolders.computeIfPresent(holderUUID, (uuid, state) -> state.setHolderLocationOffset(state.holderLocationOffset().add(offset)));
|
LeashState currentState = leashHolders.get(holderUUID);
|
||||||
|
if (currentState == null) {
|
||||||
|
// 创建新的状态,使用默认的应用实体偏移量
|
||||||
|
leashHolders.put(holderUUID, new LeashState(
|
||||||
|
offset,
|
||||||
|
getDefaultLeashApplyEntityLocationOffset(),
|
||||||
|
Vec3.ZERO // 或者合适的默认值
|
||||||
|
));
|
||||||
|
} else {
|
||||||
|
// 更新现有状态
|
||||||
|
leashHolders.put(holderUUID,
|
||||||
|
currentState.setHolderLocationOffset(currentState.holderLocationOffset().add(offset))
|
||||||
|
);
|
||||||
|
}
|
||||||
markForSync();
|
markForSync();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void addLeashHolderLocationOffset(BlockPos knotPos, Vec3 offset) {
|
public void addLeashHolderLocationOffset(BlockPos knotPos, Vec3 offset) {
|
||||||
leashKnots.computeIfPresent(knotPos, (blockPos, state) -> state.setHolderLocationOffset(state.holderLocationOffset().add(offset)));
|
LeashState currentState = leashKnots.get(knotPos);
|
||||||
|
if (currentState == null) {
|
||||||
|
// 创建新的状态
|
||||||
|
leashKnots.put(knotPos, new LeashState(
|
||||||
|
offset,
|
||||||
|
getDefaultLeashApplyEntityLocationOffset(),
|
||||||
|
Vec3.ZERO
|
||||||
|
));
|
||||||
|
} else {
|
||||||
|
// 更新现有状态
|
||||||
|
leashKnots.put(knotPos,
|
||||||
|
currentState.setHolderLocationOffset(currentState.holderLocationOffset().add(offset))
|
||||||
|
);
|
||||||
|
}
|
||||||
markForSync();
|
markForSync();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void removeLeashHolderLocationOffset(Entity holder) {
|
public void removeLeashHolderLocationOffset(Entity holder) {
|
||||||
if (entity instanceof SuperLeashKnotEntity leashKnot) {
|
if (holder instanceof SuperLeashKnotEntity leashKnot) {
|
||||||
removeLeashHolderLocationOffset(leashKnot.getPos());
|
removeLeashHolderLocationOffset(leashKnot.getPos());
|
||||||
} else removeLeashHolderLocationOffset(holder.getUUID());
|
} else removeLeashHolderLocationOffset(holder.getUUID());
|
||||||
}
|
}
|
||||||
|
|
@ -201,6 +253,25 @@ public class LeashStateImpl implements ILeashState {
|
||||||
markForSync();
|
markForSync();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void removeAllLeashHolderLocationOffset() {
|
||||||
|
leashKnots.clear();
|
||||||
|
leashHolders.clear();
|
||||||
|
markForSync();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void removeAllLeashHolderUUIDLocationOffset() {
|
||||||
|
leashHolders.clear();
|
||||||
|
markForSync();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void removeAllLeashHolderBlockPosLocationOffset() {
|
||||||
|
leashKnots.clear();
|
||||||
|
markForSync();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void resetAllLeashHolderLocationsOffset() {
|
public void resetAllLeashHolderLocationsOffset() {
|
||||||
leashKnots.replaceAll((pos, leashState) -> leashState.resetHolderLocationOffset());
|
leashKnots.replaceAll((pos, leashState) -> leashState.resetHolderLocationOffset());
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,6 @@ import net.minecraft.core.BlockPos;
|
||||||
import net.minecraft.nbt.CompoundTag;
|
import net.minecraft.nbt.CompoundTag;
|
||||||
import net.minecraft.world.entity.Entity;
|
import net.minecraft.world.entity.Entity;
|
||||||
import net.minecraft.world.entity.player.Player;
|
import net.minecraft.world.entity.player.Player;
|
||||||
import net.minecraft.world.phys.Vec3;
|
|
||||||
import net.minecraftforge.common.util.INBTSerializable;
|
import net.minecraftforge.common.util.INBTSerializable;
|
||||||
import top.r3944realms.superleadrope.content.entity.SuperLeashKnotEntity;
|
import top.r3944realms.superleadrope.content.entity.SuperLeashKnotEntity;
|
||||||
|
|
||||||
|
|
@ -142,7 +141,6 @@ public interface ILeashData extends INBTSerializable<CompoundTag> {
|
||||||
Optional<UUID> holderUUIDOpt,
|
Optional<UUID> holderUUIDOpt,
|
||||||
Optional<Integer> holderIdOpt, // Only for client side use
|
Optional<Integer> holderIdOpt, // Only for client side use
|
||||||
String reserved, // 保留字段
|
String reserved, // 保留字段
|
||||||
Vec3 attachOffset,
|
|
||||||
double maxDistance,
|
double maxDistance,
|
||||||
double elasticDistance,
|
double elasticDistance,
|
||||||
int keepLeashTicks, // 剩余 Tick 数
|
int keepLeashTicks, // 剩余 Tick 数
|
||||||
|
|
@ -150,14 +148,13 @@ public interface ILeashData extends INBTSerializable<CompoundTag> {
|
||||||
) {
|
) {
|
||||||
public static final LeashInfo EMPTY = new LeashInfo(
|
public static final LeashInfo EMPTY = new LeashInfo(
|
||||||
Optional.empty(), Optional.empty(), Optional.empty(),
|
Optional.empty(), Optional.empty(), Optional.empty(),
|
||||||
"", Vec3.ZERO, 12.0D, 6.0D, 0, 0
|
"", 12.0D, 6.0D, 0, 0
|
||||||
);
|
);
|
||||||
|
|
||||||
/* ---------- Factory ---------- */
|
/* ---------- Factory ---------- */
|
||||||
public static LeashInfo create(
|
public static LeashInfo create(
|
||||||
Entity entity,
|
Entity entity,
|
||||||
String reserved,
|
String reserved,
|
||||||
Vec3 offset,
|
|
||||||
double maxDistance,
|
double maxDistance,
|
||||||
double elasticDistance,
|
double elasticDistance,
|
||||||
int keepTicks,
|
int keepTicks,
|
||||||
|
|
@ -165,32 +162,31 @@ public interface ILeashData extends INBTSerializable<CompoundTag> {
|
||||||
) {
|
) {
|
||||||
return entity instanceof SuperLeashKnotEntity knot
|
return entity instanceof SuperLeashKnotEntity knot
|
||||||
? new LeashInfo(knot.getPos(), entity.getId(), reserved,
|
? new LeashInfo(knot.getPos(), entity.getId(), reserved,
|
||||||
offset, maxDistance, elasticDistance, keepTicks, maxKeepTicks)
|
maxDistance, elasticDistance, keepTicks, maxKeepTicks)
|
||||||
: new LeashInfo(entity.getUUID(), entity.getId(), reserved,
|
: new LeashInfo(entity.getUUID(), entity.getId(), reserved, maxDistance, elasticDistance, keepTicks, maxKeepTicks);
|
||||||
offset, maxDistance, elasticDistance, keepTicks, maxKeepTicks);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public LeashInfo(UUID holderUUID, int holderId, String reserved, Vec3 offset,
|
public LeashInfo(UUID holderUUID, int holderId, String reserved,
|
||||||
double maxDistance, double elasticDistance, int keepTicks, int maxKeepTicks) {
|
double maxDistance, double elasticDistance, int keepTicks, int maxKeepTicks) {
|
||||||
this(Optional.empty(), Optional.of(holderUUID), Optional.of(holderId),
|
this(Optional.empty(), Optional.of(holderUUID), Optional.of(holderId),
|
||||||
reserved, offset, maxDistance, elasticDistance, keepTicks, maxKeepTicks);
|
reserved, maxDistance, elasticDistance, keepTicks, maxKeepTicks);
|
||||||
}
|
}
|
||||||
|
|
||||||
public LeashInfo(BlockPos knotPos, int holderId, String reserved, Vec3 offset,
|
public LeashInfo(BlockPos knotPos, int holderId, String reserved,
|
||||||
double maxDistance, double elasticDistance, int keepTicks, int maxKeepTicks) {
|
double maxDistance, double elasticDistance, int keepTicks, int maxKeepTicks) {
|
||||||
this(Optional.of(knotPos), Optional.empty(), Optional.of(holderId),
|
this(Optional.of(knotPos), Optional.empty(), Optional.of(holderId),
|
||||||
reserved, offset, maxDistance, elasticDistance, keepTicks, maxKeepTicks);
|
reserved, maxDistance, elasticDistance, keepTicks, maxKeepTicks);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ---------- State updates ---------- */
|
/* ---------- State updates ---------- */
|
||||||
public LeashInfo decrementKeepTicks() {
|
public LeashInfo decrementKeepTicks() {
|
||||||
return new LeashInfo(blockPosOpt, holderUUIDOpt, holderIdOpt, reserved, attachOffset,
|
return new LeashInfo(blockPosOpt, holderUUIDOpt, holderIdOpt, reserved,
|
||||||
maxDistance, elasticDistance,
|
maxDistance, elasticDistance,
|
||||||
Math.max(0, keepLeashTicks - 1), maxKeepLeashTicks);
|
Math.max(0, keepLeashTicks - 1), maxKeepLeashTicks);
|
||||||
}
|
}
|
||||||
|
|
||||||
public LeashInfo resetKeepTicks() {
|
public LeashInfo resetKeepTicks() {
|
||||||
return new LeashInfo(blockPosOpt, holderUUIDOpt, holderIdOpt, reserved, attachOffset,
|
return new LeashInfo(blockPosOpt, holderUUIDOpt, holderIdOpt, reserved,
|
||||||
maxDistance, elasticDistance,
|
maxDistance, elasticDistance,
|
||||||
maxKeepLeashTicks, maxKeepLeashTicks);
|
maxKeepLeashTicks, maxKeepLeashTicks);
|
||||||
}
|
}
|
||||||
|
|
@ -205,7 +201,7 @@ public interface ILeashData extends INBTSerializable<CompoundTag> {
|
||||||
isKnot ? Optional.of(((SuperLeashKnotEntity) entity).getPos()) : Optional.empty(),
|
isKnot ? Optional.of(((SuperLeashKnotEntity) entity).getPos()) : Optional.empty(),
|
||||||
!isKnot ? Optional.of(entity.getUUID()) : Optional.empty(),
|
!isKnot ? Optional.of(entity.getUUID()) : Optional.empty(),
|
||||||
Optional.of(entity.getId()),
|
Optional.of(entity.getId()),
|
||||||
newReserved, attachOffset, maxDistance, elasticDistance,
|
newReserved, maxDistance, elasticDistance,
|
||||||
keepLeashTicks, maxKeepLeashTicks
|
keepLeashTicks, maxKeepLeashTicks
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -67,6 +67,9 @@ public interface ILeashState extends INBTSerializable<CompoundTag> {
|
||||||
void removeLeashHolderLocationOffset(Entity holder);
|
void removeLeashHolderLocationOffset(Entity holder);
|
||||||
void removeLeashHolderLocationOffset(UUID holderUUID);
|
void removeLeashHolderLocationOffset(UUID holderUUID);
|
||||||
void removeLeashHolderLocationOffset(BlockPos knotPos);
|
void removeLeashHolderLocationOffset(BlockPos knotPos);
|
||||||
|
void removeAllLeashHolderLocationOffset();
|
||||||
|
void removeAllLeashHolderUUIDLocationOffset();
|
||||||
|
void removeAllLeashHolderBlockPosLocationOffset();
|
||||||
|
|
||||||
/* ----------------------
|
/* ----------------------
|
||||||
* Apply-entity offset
|
* Apply-entity offset
|
||||||
|
|
|
||||||
|
|
@ -19,13 +19,13 @@ import com.mojang.brigadier.builder.LiteralArgumentBuilder;
|
||||||
import net.minecraft.commands.CommandSourceStack;
|
import net.minecraft.commands.CommandSourceStack;
|
||||||
import net.minecraft.commands.Commands;
|
import net.minecraft.commands.Commands;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
import top.r3944realms.superleadrope.config.LeashCommonConfig;
|
import top.r3944realms.superleadrope.CommonEventHandler;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public class Command {
|
public class Command {
|
||||||
public static final String PREFIX = LeashCommonConfig.COMMON.SLPModCommandPrefix.get();
|
public static final String PREFIX = CommonEventHandler.leashConfigManager.getCommandPrefix();
|
||||||
public static boolean SHOULD_USE_PREFIX = LeashCommonConfig.COMMON.EnableSLPModCommandPrefix.get();
|
public static boolean SHOULD_USE_PREFIX = CommonEventHandler.leashConfigManager.isCommandPrefixEnabled();
|
||||||
static LiteralArgumentBuilder<CommandSourceStack> getLiterArgumentBuilderOfCSS(String name, boolean shouldAddToList, @Nullable List<LiteralArgumentBuilder<CommandSourceStack>> list) {
|
static LiteralArgumentBuilder<CommandSourceStack> getLiterArgumentBuilderOfCSS(String name, boolean shouldAddToList, @Nullable List<LiteralArgumentBuilder<CommandSourceStack>> list) {
|
||||||
LiteralArgumentBuilder<CommandSourceStack> literal = Commands.literal(name);
|
LiteralArgumentBuilder<CommandSourceStack> literal = Commands.literal(name);
|
||||||
if (shouldAddToList) {
|
if (shouldAddToList) {
|
||||||
|
|
|
||||||
|
|
@ -16,13 +16,525 @@
|
||||||
package top.r3944realms.superleadrope.content.command;
|
package top.r3944realms.superleadrope.content.command;
|
||||||
|
|
||||||
import com.mojang.brigadier.CommandDispatcher;
|
import com.mojang.brigadier.CommandDispatcher;
|
||||||
|
import com.mojang.brigadier.arguments.*;
|
||||||
|
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
|
||||||
|
import com.mojang.brigadier.builder.RequiredArgumentBuilder;
|
||||||
|
import com.mojang.brigadier.context.CommandContext;
|
||||||
|
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
||||||
import net.minecraft.commands.CommandSourceStack;
|
import net.minecraft.commands.CommandSourceStack;
|
||||||
|
import net.minecraft.commands.Commands;
|
||||||
|
import net.minecraft.commands.arguments.*;
|
||||||
|
import net.minecraft.commands.arguments.coordinates.BlockPosArgument;
|
||||||
|
import net.minecraft.commands.arguments.selector.EntitySelector;
|
||||||
|
import net.minecraft.core.BlockPos;
|
||||||
|
import net.minecraft.network.chat.Component;
|
||||||
|
import net.minecraft.server.level.ServerLevel;
|
||||||
|
import net.minecraft.world.entity.Entity;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
import top.r3944realms.superleadrope.CommonEventHandler;
|
||||||
|
import top.r3944realms.superleadrope.SuperLeadRope;
|
||||||
|
import top.r3944realms.superleadrope.content.capability.inter.ILeashData;
|
||||||
|
import top.r3944realms.superleadrope.content.entity.SuperLeashKnotEntity;
|
||||||
|
import top.r3944realms.superleadrope.content.gamerule.server.CreateSuperLeashKnotEntityIfAbsent;
|
||||||
|
import top.r3944realms.superleadrope.core.register.SLPGameruleRegistry;
|
||||||
|
import top.r3944realms.superleadrope.util.capability.LeashDataAPI;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
import static top.r3944realms.superleadrope.content.command.Command.*;
|
||||||
|
import static top.r3944realms.superleadrope.content.command.Command.SHOULD_USE_PREFIX;
|
||||||
public class LeashDataCommand {
|
public class LeashDataCommand {
|
||||||
// 获取Data
|
public static final String SLP_LEASH_MESSAGE_ = SuperLeadRope.MOD_ID + ".command.leash.message.";
|
||||||
// 设置Data
|
public static final String LEASH_DATA_GET_ = SLP_LEASH_MESSAGE_ + ".get.",
|
||||||
// <add/transfer/remove> Holder<BlockPos/Entity<需判断实体类型>>
|
TITLE = LEASH_DATA_GET_ + "title",
|
||||||
// 设置对应目标的 最大长度 最长断裂距离 保持不断裂时间刻
|
TOTAL = LEASH_DATA_GET_ + "total",
|
||||||
|
BLOCK = LEASH_DATA_GET_ + "block",
|
||||||
|
UUID = LEASH_DATA_GET_ + "uuid",
|
||||||
|
MAX = LEASH_DATA_GET_ + "max",
|
||||||
|
ELASTIC = LEASH_DATA_GET_ + "elastic",
|
||||||
|
KEEP = LEASH_DATA_GET_ + "keep",
|
||||||
|
RESERVED = LEASH_DATA_GET_ + "reserved"
|
||||||
|
;
|
||||||
public static void register(CommandDispatcher<CommandSourceStack> dispatcher) {
|
public static void register(CommandDispatcher<CommandSourceStack> dispatcher) {
|
||||||
|
@Nullable List<LiteralArgumentBuilder<CommandSourceStack>> nodeList = SHOULD_USE_PREFIX ? null : new ArrayList<>();
|
||||||
|
LiteralArgumentBuilder<CommandSourceStack> literalArgumentBuilder = Commands.literal(PREFIX);
|
||||||
|
LiteralArgumentBuilder<CommandSourceStack> $$leashDataRoot = getLiterArgumentBuilderOfCSS("leashdata", !SHOULD_USE_PREFIX, nodeList);
|
||||||
|
RequiredArgumentBuilder<CommandSourceStack, EntitySelector> $$$add$holder = Commands.argument("holder", EntityArgument.entity())
|
||||||
|
.executes(LeashDataCommand::addLeash)
|
||||||
|
.then(Commands.argument("maxDistance", DoubleArgumentType.doubleArg(1.0, 256.0))
|
||||||
|
.executes(context -> addLeash(context,
|
||||||
|
DoubleArgumentType.getDouble(context, "maxDistance")))
|
||||||
|
.then(Commands.argument("elasticDistance", DoubleArgumentType.doubleArg(1.0, 128.0))
|
||||||
|
.executes(context -> addLeash(context,
|
||||||
|
DoubleArgumentType.getDouble(context, "maxDistance"),
|
||||||
|
DoubleArgumentType.getDouble(context, "elasticDistance")))
|
||||||
|
.then(Commands.argument("keepTicks", IntegerArgumentType.integer(0))
|
||||||
|
.executes(context -> addLeash(context,
|
||||||
|
DoubleArgumentType.getDouble(context, "maxDistance"),
|
||||||
|
DoubleArgumentType.getDouble(context, "elasticDistance"),
|
||||||
|
IntegerArgumentType.getInteger(context, "keepTicks")))
|
||||||
|
.then(Commands.argument("reserved", StringArgumentType.string())
|
||||||
|
.executes(context -> addLeash(context,
|
||||||
|
DoubleArgumentType.getDouble(context, "maxDistance"),
|
||||||
|
DoubleArgumentType.getDouble(context, "elasticDistance"),
|
||||||
|
IntegerArgumentType.getInteger(context, "keepTicks"),
|
||||||
|
StringArgumentType.getString(context, "reserved")))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
LiteralArgumentBuilder<CommandSourceStack> $$$add$pos = Commands.literal("block")
|
||||||
|
.then(Commands.argument("pos", BlockPosArgument.blockPos())
|
||||||
|
.executes(LeashDataCommand::addBlockLeash)
|
||||||
|
.then(Commands.argument("maxDistance", DoubleArgumentType.doubleArg(1.0, 256.0))
|
||||||
|
.executes(context -> addBlockLeash(context,
|
||||||
|
DoubleArgumentType.getDouble(context, "maxDistance")))
|
||||||
|
.then(Commands.argument("elasticDistance", DoubleArgumentType.doubleArg(1.0, 128.0))
|
||||||
|
.executes(context -> addBlockLeash(context,
|
||||||
|
DoubleArgumentType.getDouble(context, "maxDistance"),
|
||||||
|
DoubleArgumentType.getDouble(context, "elasticDistance"), 0, ""))
|
||||||
|
.then(Commands.argument("keepTicks", IntegerArgumentType.integer(0))
|
||||||
|
.executes(context -> addBlockLeash(context,
|
||||||
|
DoubleArgumentType.getDouble(context, "maxDistance"),
|
||||||
|
DoubleArgumentType.getDouble(context, "elasticDistance"),
|
||||||
|
IntegerArgumentType.getInteger(context, "keepTicks")))
|
||||||
|
.then(Commands.argument("reserved", StringArgumentType.string())
|
||||||
|
.executes(context -> addBlockLeash(context,
|
||||||
|
DoubleArgumentType.getDouble(context, "maxDistance"),
|
||||||
|
DoubleArgumentType.getDouble(context, "elasticDistance"),
|
||||||
|
IntegerArgumentType.getInteger(context, "keepTicks"),
|
||||||
|
StringArgumentType.getString(context, "reserved")))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
LiteralArgumentBuilder<CommandSourceStack> $$$add = Commands.literal("add")
|
||||||
|
.then(Commands.argument("target", EntityArgument.entities())
|
||||||
|
// 实体拴绳
|
||||||
|
.then($$$add$holder)
|
||||||
|
|
||||||
|
// 方块拴绳
|
||||||
|
.then($$$add$pos)
|
||||||
|
);
|
||||||
|
LiteralArgumentBuilder<CommandSourceStack> $$$remove = Commands.literal("remove")
|
||||||
|
.then(Commands.argument("target", EntityArgument.entities())
|
||||||
|
// 移除特定实体拴绳
|
||||||
|
.then(Commands.argument("holder", EntityArgument.entity())
|
||||||
|
.executes(LeashDataCommand::removeLeash)
|
||||||
|
)
|
||||||
|
|
||||||
|
// 移除方块拴绳
|
||||||
|
.then(Commands.literal("block")
|
||||||
|
.then(Commands.argument("pos", BlockPosArgument.blockPos())
|
||||||
|
.executes(LeashDataCommand::removeBlockLeash)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
// 批量移除
|
||||||
|
.then(Commands.literal("all")
|
||||||
|
.executes(LeashDataCommand::removeAllLeashes)
|
||||||
|
)
|
||||||
|
.then(Commands.literal("holders")
|
||||||
|
.executes(LeashDataCommand::removeAllHolderLeashes)
|
||||||
|
)
|
||||||
|
.then(Commands.literal("blocks")
|
||||||
|
.executes(LeashDataCommand::removeAllBlockLeashes)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
LiteralArgumentBuilder<CommandSourceStack> $$$transfer = Commands.literal("transfer")
|
||||||
|
.then(Commands.argument("target", EntityArgument.entities())
|
||||||
|
// 实体到实体转移
|
||||||
|
.then(Commands.argument("from", EntityArgument.entity())
|
||||||
|
.then(Commands.argument("to", EntityArgument.entity())
|
||||||
|
.executes(LeashDataCommand::transferLeash)
|
||||||
|
.then(Commands.argument("reserved", StringArgumentType.string())
|
||||||
|
.executes(context -> transferLeash(context,
|
||||||
|
StringArgumentType.getString(context, "reserved")))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
// 方块到实体转移
|
||||||
|
.then(Commands.literal("fromBlock")
|
||||||
|
.then(Commands.argument("fromPos", BlockPosArgument.blockPos())
|
||||||
|
.then(Commands.argument("to", EntityArgument.entity())
|
||||||
|
.executes(LeashDataCommand::transferFromBlock)
|
||||||
|
.then(Commands.argument("reserved", StringArgumentType.string())
|
||||||
|
.executes(context -> transferFromBlock(context,
|
||||||
|
StringArgumentType.getString(context, "reserved")))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
RequiredArgumentBuilder<CommandSourceStack, EntitySelector> $$$set$holder = Commands.argument("holder", EntityArgument.entity())
|
||||||
|
// 设置最大距离
|
||||||
|
.then(Commands.literal("maxDistance")
|
||||||
|
.then(Commands.argument("distance", DoubleArgumentType.doubleArg(1.0, 256.0))
|
||||||
|
.executes(LeashDataCommand::setMaxDistance)
|
||||||
|
.then(Commands.argument("keepTicks", IntegerArgumentType.integer(0))
|
||||||
|
.executes(context -> setMaxDistance(context,
|
||||||
|
IntegerArgumentType.getInteger(context, "keepTicks")))
|
||||||
|
.then(Commands.argument("reserved", StringArgumentType.string())
|
||||||
|
.executes(context -> setMaxDistance(context,
|
||||||
|
IntegerArgumentType.getInteger(context, "keepTicks"),
|
||||||
|
StringArgumentType.getString(context, "reserved")))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
// 设置弹性距离
|
||||||
|
.then(Commands.literal("elasticDistance")
|
||||||
|
.then(Commands.argument("distance", DoubleArgumentType.doubleArg(1.0, 128.0))
|
||||||
|
.executes(context -> setElasticDistance(context, 0, ""))
|
||||||
|
.then(Commands.argument("keepTicks", IntegerArgumentType.integer(0))
|
||||||
|
.executes(context -> setElasticDistance(context,
|
||||||
|
IntegerArgumentType.getInteger(context, "keepTicks"), ""))
|
||||||
|
.then(Commands.argument("reserved", StringArgumentType.string())
|
||||||
|
.executes(context -> setElasticDistance(context,
|
||||||
|
IntegerArgumentType.getInteger(context, "keepTicks"),
|
||||||
|
StringArgumentType.getString(context, "reserved")))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
LiteralArgumentBuilder<CommandSourceStack> $$$set$pos = Commands.literal("block")
|
||||||
|
.then(Commands.argument("pos", BlockPosArgument.blockPos())
|
||||||
|
// 设置最大距离
|
||||||
|
.then(Commands.literal("maxDistance")
|
||||||
|
.then(Commands.argument("distance", DoubleArgumentType.doubleArg(1.0, 256.0))
|
||||||
|
.executes(LeashDataCommand::setBlockMaxDistance)
|
||||||
|
.then(Commands.argument("keepTicks", IntegerArgumentType.integer(0))
|
||||||
|
.executes(context -> setBlockMaxDistance(context,
|
||||||
|
IntegerArgumentType.getInteger(context, "keepTicks")))
|
||||||
|
.then(Commands.argument("reserved", StringArgumentType.string())
|
||||||
|
.executes(context -> setBlockMaxDistance(context,
|
||||||
|
IntegerArgumentType.getInteger(context, "keepTicks"),
|
||||||
|
StringArgumentType.getString(context, "reserved")))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
// 设置弹性距离
|
||||||
|
.then(Commands.literal("elasticDistance")
|
||||||
|
.then(Commands.argument("distance", DoubleArgumentType.doubleArg(1.0, 128.0))
|
||||||
|
.executes(LeashDataCommand::setBlockElasticDistance)
|
||||||
|
.then(Commands.argument("keepTicks", IntegerArgumentType.integer(0))
|
||||||
|
.executes(context -> setBlockElasticDistance(context,
|
||||||
|
IntegerArgumentType.getInteger(context, "keepTicks")))
|
||||||
|
.then(Commands.argument("reserved", StringArgumentType.string())
|
||||||
|
.executes(context -> setBlockElasticDistance(context,
|
||||||
|
IntegerArgumentType.getInteger(context, "keepTicks"),
|
||||||
|
StringArgumentType.getString(context, "reserved")))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
LiteralArgumentBuilder<CommandSourceStack> $$$set = Commands.literal("set")
|
||||||
|
.then(Commands.argument("target", EntityArgument.entities())
|
||||||
|
// 实体拴绳设置
|
||||||
|
.then($$$set$holder)
|
||||||
|
|
||||||
|
// 方块拴绳设置
|
||||||
|
.then($$$set$pos)
|
||||||
|
);
|
||||||
|
LiteralArgumentBuilder<CommandSourceStack> $$$applayForces = Commands.literal("applyForces")
|
||||||
|
.then(Commands.argument("target", EntityArgument.entities())
|
||||||
|
.executes(LeashDataCommand::applyForces)
|
||||||
|
);
|
||||||
|
LiteralArgumentBuilder<CommandSourceStack> $$$get = Commands.literal("get")
|
||||||
|
.then(Commands.argument("target", EntityArgument.entities())
|
||||||
|
.executes(LeashDataCommand::getLeashData)
|
||||||
|
);
|
||||||
|
$$leashDataRoot
|
||||||
|
.requires(source -> source.hasPermission(2)) // 需要OP权限
|
||||||
|
|
||||||
|
// ==================== GET 命令 ====================
|
||||||
|
.then($$$get)
|
||||||
|
|
||||||
|
// ==================== ADD 命令 ====================
|
||||||
|
.then($$$add)
|
||||||
|
|
||||||
|
// ==================== REMOVE 命令 ====================
|
||||||
|
.then($$$remove)
|
||||||
|
|
||||||
|
// ==================== TRANSFER 命令 ====================
|
||||||
|
.then($$$transfer)
|
||||||
|
|
||||||
|
// ==================== SET 命令 ====================
|
||||||
|
.then($$$set)
|
||||||
|
|
||||||
|
// ==================== APPLY FORCES 命令 ====================
|
||||||
|
.then($$$applayForces);
|
||||||
|
if(SHOULD_USE_PREFIX){
|
||||||
|
literalArgumentBuilder.then($$leashDataRoot);
|
||||||
|
dispatcher.register(literalArgumentBuilder);
|
||||||
|
} else {
|
||||||
|
if (nodeList != null) {
|
||||||
|
nodeList.forEach(dispatcher::register);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public static final String SET_MAX_DISTANCE = SLP_LEASH_MESSAGE_ + "set.max_distance";
|
||||||
|
private static int setMaxDistance(CommandContext<CommandSourceStack> context) throws CommandSyntaxException {
|
||||||
|
return setMaxDistance(context, CommonEventHandler.leashConfigManager.getMaxLeashLength(), "");
|
||||||
|
}
|
||||||
|
private static int setMaxDistance(CommandContext<CommandSourceStack> context, double maxDistance) throws CommandSyntaxException {
|
||||||
|
return setMaxDistance(context, maxDistance, "");
|
||||||
|
}
|
||||||
|
private static int setMaxDistance(CommandContext<CommandSourceStack> context, double maxDistance, String reserved) throws CommandSyntaxException {
|
||||||
|
Collection<? extends Entity> targets = EntityArgument.getEntities(context, "target");
|
||||||
|
Entity holder = EntityArgument.getEntity(context, "holder");
|
||||||
|
for (Entity target : targets) {
|
||||||
|
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
public static final String REMOVE_ALL_BLOCK_LEASHES = SLP_LEASH_MESSAGE_ + "remove.all_block_leashes";
|
||||||
|
private static int removeAllBlockLeashes(CommandContext<CommandSourceStack> context) throws CommandSyntaxException {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
public static final String REMOVE_ALL_HOLDER_LEASHES = SLP_LEASH_MESSAGE_ + "remove.all_holder_leashes";
|
||||||
|
private static int removeAllHolderLeashes(CommandContext<CommandSourceStack> context) throws CommandSyntaxException {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
public static final String TRANSFER_FROM_BLOCK = SLP_LEASH_MESSAGE_ + "transfer.from_block";
|
||||||
|
private static int transferFromBlock(CommandContext<CommandSourceStack> context) throws CommandSyntaxException {
|
||||||
|
return transferFromBlock(context, "");
|
||||||
|
}
|
||||||
|
private static int transferFromBlock(CommandContext<CommandSourceStack> context, String reserved) throws CommandSyntaxException {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
public static final String SET_ELASTIC_DISTANCE = SLP_LEASH_MESSAGE_ + "set.elastic_distance";
|
||||||
|
private static int setElasticDistance(CommandContext<CommandSourceStack> context) throws CommandSyntaxException {
|
||||||
|
return setElasticDistance(context, 0 ,"");
|
||||||
|
}
|
||||||
|
private static int setElasticDistance(CommandContext<CommandSourceStack> context, int keepTicks) throws CommandSyntaxException {
|
||||||
|
return setElasticDistance(context, keepTicks ,"");
|
||||||
|
}
|
||||||
|
private static int setElasticDistance(CommandContext<CommandSourceStack> context, int keepTicks, String reserved) throws CommandSyntaxException {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
public static final String SET_BLOCK_MAX_DISTANCE = SLP_LEASH_MESSAGE_ + "set.block_max_distance";
|
||||||
|
private static int setBlockMaxDistance(CommandContext<CommandSourceStack> context) throws CommandSyntaxException {
|
||||||
|
return setBlockMaxDistance(context, 0 ,"");
|
||||||
|
}
|
||||||
|
private static int setBlockMaxDistance(CommandContext<CommandSourceStack> context, int keepTicks) throws CommandSyntaxException {
|
||||||
|
return setBlockMaxDistance(context, keepTicks ,"");
|
||||||
|
}
|
||||||
|
private static int setBlockMaxDistance(CommandContext<CommandSourceStack> context, int keepTicks, String reserved) throws CommandSyntaxException {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
public static final String SET_BLOCK_ELASTIC_DISTANCE = SLP_LEASH_MESSAGE_ + "set.block_elastic_distance";
|
||||||
|
private static int setBlockElasticDistance(CommandContext<CommandSourceStack> context) throws CommandSyntaxException {
|
||||||
|
return setBlockElasticDistance(context, 0 ,"");
|
||||||
|
}
|
||||||
|
private static int setBlockElasticDistance(CommandContext<CommandSourceStack> context, int keepTicks) throws CommandSyntaxException {
|
||||||
|
return setBlockElasticDistance(context, keepTicks ,"");
|
||||||
|
}
|
||||||
|
private static int setBlockElasticDistance(CommandContext<CommandSourceStack> context, int keepTicks, String reserved) throws CommandSyntaxException {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ==================== 命令执行方法 ====================
|
||||||
|
|
||||||
|
private static int getLeashData(CommandContext<CommandSourceStack> context) throws CommandSyntaxException {
|
||||||
|
Collection<? extends Entity> targets = EntityArgument.getEntities(context, "target");
|
||||||
|
CommandSourceStack source = context.getSource();
|
||||||
|
|
||||||
|
for (Entity target : targets) {
|
||||||
|
Collection<ILeashData.LeashInfo> leashes = LeashDataAPI.QueryOperations.getAllLeashes(target);
|
||||||
|
|
||||||
|
source.sendSuccess(() -> Component.literal("=== Leash Data for " + target.getName().getString() + " ==="), false);
|
||||||
|
source.sendSuccess(() -> Component.literal("Total leashes: " + leashes.size()), false);
|
||||||
|
// TODO:翻译支持 HoverTip实现部分信息简化显示
|
||||||
|
for (ILeashData.LeashInfo leash : leashes) {
|
||||||
|
StringBuilder info = new StringBuilder();
|
||||||
|
leash.blockPosOpt().ifPresent(pos -> info.append("Block: ").append(pos.toShortString()).append(" "));
|
||||||
|
leash.holderUUIDOpt().ifPresent(uuid -> info.append("UUID: ").append(uuid).append(" "));
|
||||||
|
info.append("Max: ").append(leash.maxDistance()).append(" ");
|
||||||
|
info.append("Elastic: ").append(leash.elasticDistance()).append(" ");
|
||||||
|
info.append("Keep: ").append(leash.keepLeashTicks()).append("/").append(leash.maxKeepLeashTicks());
|
||||||
|
if (!leash.reserved().isEmpty()) {
|
||||||
|
info.append(" Reserved: ").append(leash.reserved());
|
||||||
|
}
|
||||||
|
|
||||||
|
source.sendSuccess(() -> Component.literal(info.toString()), false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return targets.size();
|
||||||
|
}
|
||||||
|
private static int addLeash(CommandContext<CommandSourceStack> context) throws CommandSyntaxException {
|
||||||
|
return addLeash(context, CommonEventHandler.leashConfigManager.getMaxLeashLength(), CommonEventHandler.leashConfigManager.getElasticDistance(), 0, "");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int addLeash(CommandContext<CommandSourceStack> context,
|
||||||
|
double maxDistance) throws CommandSyntaxException {
|
||||||
|
return addLeash(context, maxDistance, CommonEventHandler.leashConfigManager.getElasticDistance(), 0, "");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int addLeash(CommandContext<CommandSourceStack> context,
|
||||||
|
double maxDistance, double elasticDistance) throws CommandSyntaxException {
|
||||||
|
return addLeash(context, maxDistance, elasticDistance, 0, "");
|
||||||
|
}
|
||||||
|
private static int addLeash(CommandContext<CommandSourceStack> context,
|
||||||
|
double maxDistance, double elasticDistance, int keepTicks) throws CommandSyntaxException {
|
||||||
|
return addLeash(context, maxDistance, elasticDistance, keepTicks, "");
|
||||||
|
}
|
||||||
|
private static int addLeash(CommandContext<CommandSourceStack> context,
|
||||||
|
double maxDistance, double elasticDistance, int keepTicks, String reserved)
|
||||||
|
throws CommandSyntaxException {
|
||||||
|
Collection<? extends Entity> targets = EntityArgument.getEntities(context, "target");
|
||||||
|
Entity holder = EntityArgument.getEntity(context, "holder");
|
||||||
|
CommandSourceStack source = context.getSource();
|
||||||
|
List<Entity> successful = new ArrayList<>(), failed = new ArrayList<>();
|
||||||
|
for (Entity target : targets) {
|
||||||
|
if(LeashDataAPI.LeashOperations.attach(target, holder, maxDistance, elasticDistance, keepTicks, reserved)) {
|
||||||
|
successful.add(target);
|
||||||
|
} else failed.add(target);
|
||||||
|
}
|
||||||
|
// todo: source.sendSuccess(() -> Component.translatable(/*成功{},失败{}*/), true);
|
||||||
|
return successful.size();
|
||||||
|
}
|
||||||
|
private static int addBlockLeash(CommandContext<CommandSourceStack> context) throws CommandSyntaxException {
|
||||||
|
return addBlockLeash(context, CommonEventHandler.leashConfigManager.getMaxLeashLength(), CommonEventHandler.leashConfigManager.getElasticDistance(), 0, "");
|
||||||
|
}
|
||||||
|
private static int addBlockLeash(CommandContext<CommandSourceStack> context,
|
||||||
|
double maxDistance) throws CommandSyntaxException {
|
||||||
|
return addBlockLeash(context, maxDistance, CommonEventHandler.leashConfigManager.getElasticDistance(), 0, "");
|
||||||
|
}
|
||||||
|
private static int addBlockLeash(CommandContext<CommandSourceStack> context,
|
||||||
|
double maxDistance, double elasticDistance, int keepTicks) throws CommandSyntaxException {
|
||||||
|
return addBlockLeash(context, maxDistance, elasticDistance, keepTicks, "");
|
||||||
|
}
|
||||||
|
private static int addBlockLeash(CommandContext<CommandSourceStack> context,
|
||||||
|
double maxDistance, double elasticDistance, int keepTicks, String reserved)
|
||||||
|
throws CommandSyntaxException {
|
||||||
|
Collection<? extends Entity> targets = EntityArgument.getEntities(context, "target");
|
||||||
|
BlockPos pos = BlockPosArgument.getBlockPos(context, "pos");
|
||||||
|
CommandSourceStack source = context.getSource();
|
||||||
|
ServerLevel level = source.getLevel();
|
||||||
|
SuperLeashKnotEntity knotEntity = SuperLeashKnotEntity.get(level, pos)
|
||||||
|
.or(() -> {
|
||||||
|
if (SLPGameruleRegistry.getGameruleBoolValue(level, CreateSuperLeashKnotEntityIfAbsent.NAME_KEY))
|
||||||
|
return Optional.of(SuperLeashKnotEntity.createKnot(level, pos, true));
|
||||||
|
else return Optional.empty();
|
||||||
|
}).orElse(null);
|
||||||
|
if (knotEntity == null) {
|
||||||
|
// todo: source.sendFailure(Component.translatable(/*失败,目标上无拴绳结*/));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
List<Entity> successful = new ArrayList<>(), failed = new ArrayList<>();
|
||||||
|
for (Entity target : targets) {
|
||||||
|
if(LeashDataAPI.LeashOperations.attach(target, knotEntity, maxDistance, elasticDistance, keepTicks, reserved)) {
|
||||||
|
successful.add(target);
|
||||||
|
} else failed.add(target);
|
||||||
|
}
|
||||||
|
// todo: source.sendSuccess(() -> Component.translatable(/*成功{},失败{}*/), true);
|
||||||
|
return successful.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int removeLeash(CommandContext<CommandSourceStack> context) throws CommandSyntaxException {
|
||||||
|
Collection<? extends Entity> targets = EntityArgument.getEntities(context, "target");
|
||||||
|
Entity holder = EntityArgument.getEntity(context, "holder");
|
||||||
|
CommandSourceStack source = context.getSource();
|
||||||
|
int successCount = 0;
|
||||||
|
|
||||||
|
for (Entity target : targets) {
|
||||||
|
boolean success = LeashDataAPI.LeashOperations.detach(target, holder);
|
||||||
|
|
||||||
|
if (success) {
|
||||||
|
successCount++;
|
||||||
|
source.sendSuccess(() -> Component.literal("Removed leash from " + target.getName().getString() +
|
||||||
|
" held by " + holder.getName().getString()), false);
|
||||||
|
} else {
|
||||||
|
source.sendFailure(Component.literal("No leash found for " + holder.getName().getString() +
|
||||||
|
" on " + target.getName().getString()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return successCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int removeBlockLeash(CommandContext<CommandSourceStack> context) throws CommandSyntaxException {
|
||||||
|
Collection<? extends Entity> targets = EntityArgument.getEntities(context, "target");
|
||||||
|
BlockPos pos = BlockPosArgument.getBlockPos(context, "pos");
|
||||||
|
CommandSourceStack source = context.getSource();
|
||||||
|
int successCount = 0;
|
||||||
|
|
||||||
|
for (Entity target : targets) {
|
||||||
|
boolean success = LeashDataAPI.LeashOperations.detach(target, pos);
|
||||||
|
|
||||||
|
if (success) {
|
||||||
|
successCount++;
|
||||||
|
source.sendSuccess(() -> Component.literal("Removed block leash from " + target.getName().getString() +
|
||||||
|
" at " + pos.toShortString()), false);
|
||||||
|
} else {
|
||||||
|
source.sendFailure(Component.literal("No block leash found at " + pos.toShortString() +
|
||||||
|
" on " + target.getName().getString()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return successCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int removeAllLeashes(CommandContext<CommandSourceStack> context) throws CommandSyntaxException {
|
||||||
|
Collection<? extends Entity> targets = EntityArgument.getEntities(context, "target");
|
||||||
|
CommandSourceStack source = context.getSource();
|
||||||
|
|
||||||
|
for (Entity target : targets) {
|
||||||
|
LeashDataAPI.LeashOperations.detachAll(target);
|
||||||
|
source.sendSuccess(() -> Component.literal("Removed all leashes from " + target.getName().getString()), false);
|
||||||
|
}
|
||||||
|
|
||||||
|
return targets.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int transferLeash(CommandContext<CommandSourceStack> context) throws CommandSyntaxException {
|
||||||
|
return transferLeash(context, "");
|
||||||
|
}
|
||||||
|
private static int transferLeash(CommandContext<CommandSourceStack> context, String reserved)
|
||||||
|
throws CommandSyntaxException {
|
||||||
|
Collection<? extends Entity> targets = EntityArgument.getEntities(context, "target");
|
||||||
|
Entity from = EntityArgument.getEntity(context, "from");
|
||||||
|
Entity to = EntityArgument.getEntity(context, "to");
|
||||||
|
CommandSourceStack source = context.getSource();
|
||||||
|
int successCount = 0;
|
||||||
|
|
||||||
|
for (Entity target : targets) {
|
||||||
|
boolean success = reserved.isEmpty() ?
|
||||||
|
LeashDataAPI.TransferOperations.transfer(target, from, to) :
|
||||||
|
LeashDataAPI.TransferOperations.transfer(target, from, to, reserved);
|
||||||
|
|
||||||
|
if (success) {
|
||||||
|
successCount++;
|
||||||
|
source.sendSuccess(() -> Component.literal("Transferred leash from " + from.getName().getString() +
|
||||||
|
" to " + to.getName().getString() + " for " + target.getName().getString()), false);
|
||||||
|
} else {
|
||||||
|
source.sendFailure(Component.literal("Failed to transfer leash for " + target.getName().getString()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return successCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int applyForces(CommandContext<CommandSourceStack> context) throws CommandSyntaxException {
|
||||||
|
Collection<? extends Entity> targets = EntityArgument.getEntities(context, "target");
|
||||||
|
CommandSourceStack source = context.getSource();
|
||||||
|
|
||||||
|
for (Entity target : targets) {
|
||||||
|
LeashDataAPI.PhysicsOperations.applyForces(target);
|
||||||
|
source.sendSuccess(() -> Component.literal("Applied leash forces to " + target.getName().getString()), false);
|
||||||
|
}
|
||||||
|
|
||||||
|
return targets.size();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -34,13 +34,13 @@ import net.minecraft.world.level.block.state.BlockState;
|
||||||
import net.minecraft.world.level.gameevent.GameEvent;
|
import net.minecraft.world.level.gameevent.GameEvent;
|
||||||
import net.minecraft.world.phys.AABB;
|
import net.minecraft.world.phys.AABB;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import top.r3944realms.superleadrope.content.capability.CapabilityHandler;
|
|
||||||
import top.r3944realms.superleadrope.content.capability.impi.LeashDataImpl;
|
import top.r3944realms.superleadrope.content.capability.impi.LeashDataImpl;
|
||||||
import top.r3944realms.superleadrope.core.register.SLPEntityTypes;
|
import top.r3944realms.superleadrope.core.register.SLPEntityTypes;
|
||||||
import top.r3944realms.superleadrope.util.capability.LeashUtil;
|
import top.r3944realms.superleadrope.util.capability.LeashDataAPI;
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
|
|
||||||
public class SuperLeashKnotEntity extends LeashFenceKnotEntity {
|
public class SuperLeashKnotEntity extends LeashFenceKnotEntity {
|
||||||
|
|
@ -83,7 +83,7 @@ public class SuperLeashKnotEntity extends LeashFenceKnotEntity {
|
||||||
this.playSound(SoundEvents.LEASH_KNOT_BREAK);
|
this.playSound(SoundEvents.LEASH_KNOT_BREAK);
|
||||||
List<Entity> entities = LeashDataImpl.leashableInArea(this.level(), pos.getCenter(), entity -> LeashDataImpl.isLeashHolder(entity, this));
|
List<Entity> entities = LeashDataImpl.leashableInArea(this.level(), pos.getCenter(), entity -> LeashDataImpl.isLeashHolder(entity, this));
|
||||||
entities.forEach(entity ->
|
entities.forEach(entity ->
|
||||||
LeashUtil.getLeashData(entity)
|
LeashDataAPI.getLeashData(entity)
|
||||||
.map(iLeashDataCapability -> iLeashDataCapability.removeLeash(this))
|
.map(iLeashDataCapability -> iLeashDataCapability.removeLeash(this))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
@ -120,6 +120,27 @@ public class SuperLeashKnotEntity extends LeashFenceKnotEntity {
|
||||||
pLevel.addFreshEntity(superLeashKnotEntity1);
|
pLevel.addFreshEntity(superLeashKnotEntity1);
|
||||||
return superLeashKnotEntity1;
|
return superLeashKnotEntity1;
|
||||||
}
|
}
|
||||||
|
public static @NotNull Optional<SuperLeashKnotEntity> get(@NotNull Level level, @NotNull BlockPos pos) {
|
||||||
|
AABB searchArea = new AABB(pos).inflate(1.0D);
|
||||||
|
|
||||||
|
return level.getEntitiesOfClass(SuperLeashKnotEntity.class, searchArea)
|
||||||
|
.stream()
|
||||||
|
.filter(knot -> knot.getPos().equals(pos))
|
||||||
|
.findFirst();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建拴绳结,请不用直接调用这个,除非你知道自己在干上面
|
||||||
|
* @return 拴绳结
|
||||||
|
*/
|
||||||
|
public static @NotNull SuperLeashKnotEntity createKnot(@NotNull Level pLevel, @NotNull BlockPos pPos, boolean isEmpty) {
|
||||||
|
if(isEmpty) {
|
||||||
|
SuperLeashKnotEntity superLeashKnotEntity1 = new SuperLeashKnotEntity(pLevel, pPos);
|
||||||
|
pLevel.addFreshEntity(superLeashKnotEntity1);
|
||||||
|
return superLeashKnotEntity1;
|
||||||
|
}
|
||||||
|
throw new IllegalArgumentException("Cannot create Knot Entity of type " + SuperLeashKnotEntity.class.getSimpleName());
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void recalculateBoundingBox() {
|
protected void recalculateBoundingBox() {
|
||||||
|
|
@ -169,7 +190,7 @@ public class SuperLeashKnotEntity extends LeashFenceKnotEntity {
|
||||||
List<Entity> entities = LeashDataImpl.leashableInArea(player);
|
List<Entity> entities = LeashDataImpl.leashableInArea(player);
|
||||||
for(Entity entity : entities) {
|
for(Entity entity : entities) {
|
||||||
if (LeashDataImpl.isLeashHolder(entity, player.getUUID()))
|
if (LeashDataImpl.isLeashHolder(entity, player.getUUID()))
|
||||||
LeashUtil.getLeashData(entity)
|
LeashDataAPI.getLeashData(entity)
|
||||||
.ifPresent(i -> {
|
.ifPresent(i -> {
|
||||||
i.transferLeash(player.getUUID(), this);
|
i.transferLeash(player.getUUID(), this);
|
||||||
isTransferLeash.set(true);
|
isTransferLeash.set(true);
|
||||||
|
|
@ -182,7 +203,7 @@ public class SuperLeashKnotEntity extends LeashFenceKnotEntity {
|
||||||
this.discard();
|
this.discard();
|
||||||
List<Entity> entities1 = LeashDataImpl.leashableInArea(this);
|
List<Entity> entities1 = LeashDataImpl.leashableInArea(this);
|
||||||
entities1.forEach(entity ->
|
entities1.forEach(entity ->
|
||||||
LeashUtil.getLeashData(entity)
|
LeashDataAPI.getLeashData(entity)
|
||||||
.ifPresent(iLeashDataCapability -> {
|
.ifPresent(iLeashDataCapability -> {
|
||||||
iLeashDataCapability.removeLeash(this);
|
iLeashDataCapability.removeLeash(this);
|
||||||
isRemoveLeashKnot.set(true);
|
isRemoveLeashKnot.set(true);
|
||||||
|
|
|
||||||
|
|
@ -24,12 +24,12 @@ public class SLPGamerules {
|
||||||
public static final SLPGameruleRegistry GAMERULE_REGISTRY = SLPGameruleRegistry.INSTANCE;
|
public static final SLPGameruleRegistry GAMERULE_REGISTRY = SLPGameruleRegistry.INSTANCE;
|
||||||
public static final HashMap<String, Boolean> gamerulesBooleanValuesClient = new HashMap<>();
|
public static final HashMap<String, Boolean> gamerulesBooleanValuesClient = new HashMap<>();
|
||||||
public static final HashMap<String, Integer> gameruleIntegerValuesClient = new HashMap<>();
|
public static final HashMap<String, Integer> gameruleIntegerValuesClient = new HashMap<>();
|
||||||
public static final String RULE_KEY_PERFix_ = "gamerule." + GAMERULE_PREFIX.toLowerCase();
|
public static final String RULE_KEY_PERFiX_ = "gamerule." + GAMERULE_PREFIX.toLowerCase();
|
||||||
public static String getDescriptionKey(Class<?> gameRuleClass) {
|
public static String getDescriptionKey(Class<?> gameRuleClass) {
|
||||||
return RULE_KEY_PERFix_ + gameRuleClass.getSimpleName() + ".description";
|
return RULE_KEY_PERFiX_ + gameRuleClass.getSimpleName() + ".description";
|
||||||
}
|
}
|
||||||
public static String getDescriptionKey(String gameRuleName) {
|
public static String getDescriptionKey(String gameRuleName) {
|
||||||
return RULE_KEY_PERFix_ + gameRuleName + ".description";
|
return RULE_KEY_PERFiX_ + gameRuleName + ".description";
|
||||||
}
|
}
|
||||||
public static String getGameruleName(Class<?> clazz) {
|
public static String getGameruleName(Class<?> clazz) {
|
||||||
return SLPGamerules.GAMERULE_PREFIX + clazz.getSimpleName();
|
return SLPGamerules.GAMERULE_PREFIX + clazz.getSimpleName();
|
||||||
|
|
@ -39,7 +39,7 @@ public class SLPGamerules {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String getNameKey(Class<?> gameRuleClass) {
|
public static String getNameKey(Class<?> gameRuleClass) {
|
||||||
return RULE_KEY_PERFix_ + gameRuleClass.getSimpleName();
|
return RULE_KEY_PERFiX_ + gameRuleClass.getSimpleName();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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 <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package top.r3944realms.superleadrope.content.gamerule.server;
|
||||||
|
|
||||||
|
import net.minecraft.world.level.GameRules;
|
||||||
|
import top.r3944realms.superleadrope.content.gamerule.SLPGamerules;
|
||||||
|
|
||||||
|
import static top.r3944realms.superleadrope.content.gamerule.SLPGamerules.GAMERULE_REGISTRY;
|
||||||
|
|
||||||
|
public class CreateSuperLeashKnotEntityIfAbsent {
|
||||||
|
public static final boolean DEFAULT_VALUE = true;
|
||||||
|
public static final String ID = SLPGamerules.getGameruleName(CreateSuperLeashKnotEntityIfAbsent.class);
|
||||||
|
public static final String DESCRIPTION_KEY = SLPGamerules.getDescriptionKey(CreateSuperLeashKnotEntityIfAbsent.class);
|
||||||
|
public static final String NAME_KEY = SLPGamerules.getNameKey(CreateSuperLeashKnotEntityIfAbsent.class);
|
||||||
|
public static final GameRules.Category CATEGORY = GameRules.Category.PLAYER;
|
||||||
|
|
||||||
|
public static void register() {
|
||||||
|
GAMERULE_REGISTRY.registerGamerule(ID, CATEGORY, DEFAULT_VALUE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -29,15 +29,13 @@ import net.minecraft.world.level.Level;
|
||||||
import net.minecraft.world.level.block.state.BlockState;
|
import net.minecraft.world.level.block.state.BlockState;
|
||||||
import net.minecraft.world.level.gameevent.GameEvent;
|
import net.minecraft.world.level.gameevent.GameEvent;
|
||||||
import net.minecraftforge.common.extensions.IForgeItem;
|
import net.minecraftforge.common.extensions.IForgeItem;
|
||||||
import net.minecraftforge.common.util.LazyOptional;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import top.r3944realms.superleadrope.content.SLPToolTier;
|
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.impi.LeashDataImpl;
|
||||||
import top.r3944realms.superleadrope.content.capability.inter.ILeashData;
|
import top.r3944realms.superleadrope.content.capability.inter.ILeashData;
|
||||||
import top.r3944realms.superleadrope.content.entity.SuperLeashKnotEntity;
|
import top.r3944realms.superleadrope.content.entity.SuperLeashKnotEntity;
|
||||||
import top.r3944realms.superleadrope.core.register.SLPSoundEvents;
|
import top.r3944realms.superleadrope.core.register.SLPSoundEvents;
|
||||||
import top.r3944realms.superleadrope.util.capability.LeashUtil;
|
import top.r3944realms.superleadrope.util.capability.LeashDataAPI;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
@ -83,7 +81,7 @@ public class SuperLeadRopeItem extends TieredItem implements IForgeItem {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean canUse(ItemStack itemStack) {
|
public static boolean canUse(ItemStack itemStack) {
|
||||||
return itemStack.getDamageValue() < 974;
|
return itemStack.getDamageValue() < 1200;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -106,11 +104,10 @@ public class SuperLeadRopeItem extends TieredItem implements IForgeItem {
|
||||||
* @param newHolder 新实体
|
* @param newHolder 新实体
|
||||||
* @param player 明确持有玩家
|
* @param player 明确持有玩家
|
||||||
* @param level 维度世界
|
* @param level 维度世界
|
||||||
* @param leashStack 拴绳物品实例
|
|
||||||
* @return 是否成功
|
* @return 是否成功
|
||||||
*/
|
*/
|
||||||
public static boolean bindToEntity(Entity newHolder, Player player, Level level, ItemStack leashStack) {
|
public static boolean bindToEntity(Entity newHolder, Player player, Level level) {
|
||||||
return bindToEntity(newHolder, player, level, player.getOnPos(), leashStack);
|
return bindToEntity(newHolder, player, level, player.getOnPos());
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* 右键蹲下绑定到另一实体上
|
* 右键蹲下绑定到另一实体上
|
||||||
|
|
@ -118,10 +115,9 @@ public class SuperLeadRopeItem extends TieredItem implements IForgeItem {
|
||||||
* @param player 明确持有玩家
|
* @param player 明确持有玩家
|
||||||
* @param level 维度世界
|
* @param level 维度世界
|
||||||
* @param pos 坐标(一般是明确持有玩家的位置)
|
* @param pos 坐标(一般是明确持有玩家的位置)
|
||||||
* @param leashStack 拴绳物品实例
|
|
||||||
* @return 是否成功
|
* @return 是否成功
|
||||||
*/
|
*/
|
||||||
public static boolean bindToEntity(Entity newHolder, Player player, Level level, BlockPos pos, ItemStack leashStack) {
|
public static boolean bindToEntity(Entity newHolder, Player player, Level level, BlockPos pos) {
|
||||||
boolean isSuccess = false;
|
boolean isSuccess = false;
|
||||||
|
|
||||||
// 查找当前玩家持有的可拴生物
|
// 查找当前玩家持有的可拴生物
|
||||||
|
|
@ -131,7 +127,7 @@ public class SuperLeadRopeItem extends TieredItem implements IForgeItem {
|
||||||
);
|
);
|
||||||
|
|
||||||
for (Entity e : list) {
|
for (Entity e : list) {
|
||||||
Optional<ILeashData> leashDataOpt = LeashUtil.getLeashData(e);
|
Optional<ILeashData> leashDataOpt = LeashDataAPI.getLeashData(e);
|
||||||
|
|
||||||
if (leashDataOpt.map(i -> i.canBeAttachedTo(newHolder)).orElse(false)) {
|
if (leashDataOpt.map(i -> i.canBeAttachedTo(newHolder)).orElse(false)) {
|
||||||
leashDataOpt.ifPresent(i -> i.transferLeash(player.getUUID(), newHolder));
|
leashDataOpt.ifPresent(i -> i.transferLeash(player.getUUID(), newHolder));
|
||||||
|
|
@ -182,16 +178,16 @@ public class SuperLeadRopeItem extends TieredItem implements IForgeItem {
|
||||||
if (leashStack.isEmpty() || !canUse(leashStack)) {
|
if (leashStack.isEmpty() || !canUse(leashStack)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
knot = SuperLeashKnotEntity.getOrCreateKnot(level, pos);
|
knot = SuperLeashKnotEntity.getOrCreateKnot(level, pos);
|
||||||
knot.playPlacementSound();
|
knot.playPlacementSound();
|
||||||
|
|
||||||
SuperLeashKnotEntity finalKnot = knot;
|
SuperLeashKnotEntity finalKnot = knot;
|
||||||
LeashUtil.getLeashData(player).ifPresent(i -> {
|
LeashDataAPI.getLeashData(player).ifPresent(i -> {
|
||||||
if (i.canBeAttachedTo(finalKnot)) {
|
if (i.canBeAttachedTo(finalKnot)) {
|
||||||
i.addLeash(finalKnot);
|
if (!level.isClientSide) i.addLeash(finalKnot);
|
||||||
isSuccess.set(true);
|
isSuccess.set(true);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
// 情况二:把已有生物拴到 knot
|
// 情况二:把已有生物拴到 knot
|
||||||
else if (!list.isEmpty()) {
|
else if (!list.isEmpty()) {
|
||||||
|
|
@ -202,9 +198,9 @@ public class SuperLeadRopeItem extends TieredItem implements IForgeItem {
|
||||||
}
|
}
|
||||||
SuperLeashKnotEntity finalKnot = knot;
|
SuperLeashKnotEntity finalKnot = knot;
|
||||||
|
|
||||||
LeashUtil.getLeashData(e).ifPresent(i -> {
|
LeashDataAPI.getLeashData(e).ifPresent(i -> {
|
||||||
if (i.canBeAttachedTo(finalKnot)) {
|
if (i.canBeAttachedTo(finalKnot)) {
|
||||||
i.transferLeash(uuid, finalKnot);
|
if (!level.isClientSide) i.transferLeash(uuid, finalKnot);
|
||||||
isSuccess.set(true);
|
isSuccess.set(true);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,44 @@
|
||||||
|
/*
|
||||||
|
* 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 <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package top.r3944realms.superleadrope.core.hook;
|
||||||
|
|
||||||
|
import net.minecraft.client.renderer.culling.Frustum;
|
||||||
|
import net.minecraft.world.entity.Entity;
|
||||||
|
import net.minecraft.world.entity.Mob;
|
||||||
|
import top.r3944realms.superleadrope.SuperLeadRope;
|
||||||
|
import top.r3944realms.superleadrope.util.capability.LeashDataAPI;
|
||||||
|
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
|
|
||||||
|
public class LeashRenderHook {
|
||||||
|
public static boolean shouldRenderExtra(Mob mob, Frustum camera) {
|
||||||
|
SuperLeadRope.logger.debug("[SuperLeash] Checking entity: {} at position: {}, {}, {}", mob.getName().getString(), mob.getX(), mob.getY(), mob.getZ());
|
||||||
|
AtomicBoolean flag = new AtomicBoolean(false);
|
||||||
|
LeashDataAPI.getLeashData(mob).ifPresent(i -> {
|
||||||
|
i.getAllLeashes().forEach(j -> {
|
||||||
|
Optional<Integer> i1 = j.holderIdOpt();
|
||||||
|
if (i1.isPresent()) {
|
||||||
|
Entity entity = mob.level().getEntity(i1.get());
|
||||||
|
if (entity != null) {
|
||||||
|
flag.set(camera.isVisible(entity.getBoundingBoxForCulling()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
return flag.get();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -16,6 +16,8 @@
|
||||||
package top.r3944realms.superleadrope.core.leash;
|
package top.r3944realms.superleadrope.core.leash;
|
||||||
|
|
||||||
|
|
||||||
|
import net.minecraft.sounds.SoundEvent;
|
||||||
|
import net.minecraft.sounds.SoundEvents;
|
||||||
import net.minecraft.sounds.SoundSource;
|
import net.minecraft.sounds.SoundSource;
|
||||||
import net.minecraft.world.InteractionHand;
|
import net.minecraft.world.InteractionHand;
|
||||||
import net.minecraft.world.InteractionResult;
|
import net.minecraft.world.InteractionResult;
|
||||||
|
|
@ -24,15 +26,16 @@ import net.minecraft.world.entity.player.Player;
|
||||||
import net.minecraft.world.item.ItemStack;
|
import net.minecraft.world.item.ItemStack;
|
||||||
import net.minecraft.world.level.Level;
|
import net.minecraft.world.level.Level;
|
||||||
import net.minecraft.world.level.gameevent.GameEvent;
|
import net.minecraft.world.level.gameevent.GameEvent;
|
||||||
import net.minecraftforge.common.util.LazyOptional;
|
|
||||||
import net.minecraftforge.event.entity.player.AttackEntityEvent;
|
import net.minecraftforge.event.entity.player.AttackEntityEvent;
|
||||||
import net.minecraftforge.event.entity.player.PlayerInteractEvent;
|
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.impi.LeashDataImpl;
|
||||||
import top.r3944realms.superleadrope.content.capability.inter.ILeashData;
|
import top.r3944realms.superleadrope.content.capability.inter.ILeashData;
|
||||||
import top.r3944realms.superleadrope.content.item.SuperLeadRopeItem;
|
import top.r3944realms.superleadrope.content.item.SuperLeadRopeItem;
|
||||||
import top.r3944realms.superleadrope.core.register.SLPItems;
|
import top.r3944realms.superleadrope.core.register.SLPItems;
|
||||||
import top.r3944realms.superleadrope.core.register.SLPSoundEvents;
|
import top.r3944realms.superleadrope.core.register.SLPSoundEvents;
|
||||||
|
import top.r3944realms.superleadrope.util.capability.LeashDataAPI;
|
||||||
|
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
public class LeashInteractHandler {
|
public class LeashInteractHandler {
|
||||||
//只有玩家可以互动触发(其它的暂不支持(考虑到0 Mixin)
|
//只有玩家可以互动触发(其它的暂不支持(考虑到0 Mixin)
|
||||||
|
|
@ -48,6 +51,7 @@ public class LeashInteractHandler {
|
||||||
event.setCanceled(true);
|
event.setCanceled(true);
|
||||||
event.setCancellationResult(InteractionResult.SUCCESS);
|
event.setCancellationResult(InteractionResult.SUCCESS);
|
||||||
}
|
}
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (hand == InteractionHand.OFF_HAND) {
|
if (hand == InteractionHand.OFF_HAND) {
|
||||||
|
|
@ -57,8 +61,8 @@ public class LeashInteractHandler {
|
||||||
if (!LeashDataImpl.isLeashable(target)) {
|
if (!LeashDataImpl.isLeashable(target)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
LazyOptional<ILeashData> LeashCap = target.getCapability(CapabilityHandler.LEASH_DATA_CAP);
|
Optional<ILeashData> LeashCap = LeashDataAPI.getLeashData(target);
|
||||||
if (!LeashCap.isPresent()) {
|
if (LeashCap.isEmpty()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -72,7 +76,7 @@ public class LeashInteractHandler {
|
||||||
|
|
||||||
) {
|
) {
|
||||||
|
|
||||||
boolean isSuccess = SuperLeadRopeItem.bindToEntity(target, player, player.level(), player.getOnPos(), ItemStack.EMPTY);
|
boolean isSuccess = SuperLeadRopeItem.bindToEntity(target, player, player.level(), player.getOnPos());
|
||||||
if (isSuccess) {
|
if (isSuccess) {
|
||||||
event.setCanceled(true);
|
event.setCanceled(true);
|
||||||
event.setCancellationResult(InteractionResult.SUCCESS);
|
event.setCancellationResult(InteractionResult.SUCCESS);
|
||||||
|
|
@ -103,7 +107,7 @@ public class LeashInteractHandler {
|
||||||
boolean success = iLeashDataCapability.addLeash(player);
|
boolean success = iLeashDataCapability.addLeash(player);
|
||||||
if (success) {
|
if (success) {
|
||||||
if(!player.isCreative())
|
if(!player.isCreative())
|
||||||
itemStack.hurtAndBreak(24, player, e->{});
|
itemStack.hurtAndBreak(24, player, e-> e.level().playSound(null, player.getOnPos(), SoundEvents.ITEM_BREAK, SoundSource.PLAYERS));
|
||||||
level.playSound(null, target.getOnPos(), SLPSoundEvents.LEAD_TIED.get(), SoundSource.PLAYERS);
|
level.playSound(null, target.getOnPos(), SLPSoundEvents.LEAD_TIED.get(), SoundSource.PLAYERS);
|
||||||
event.setCanceled(true);
|
event.setCanceled(true);
|
||||||
event.setCancellationResult(InteractionResult.SUCCESS);
|
event.setCancellationResult(InteractionResult.SUCCESS);
|
||||||
|
|
@ -123,7 +127,7 @@ public class LeashInteractHandler {
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (flag) {
|
if (flag) {
|
||||||
target.getCapability(CapabilityHandler.LEASH_DATA_CAP).ifPresent(leashDataCapability -> {
|
LeashDataAPI.getLeashData(target).ifPresent(leashDataCapability -> {
|
||||||
if (leashDataCapability.hasLeash()){
|
if (leashDataCapability.hasLeash()){
|
||||||
int size = leashDataCapability.getAllLeashes().size();
|
int size = leashDataCapability.getAllLeashes().size();
|
||||||
if (player.isSecondaryUseActive())
|
if (player.isSecondaryUseActive())
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,9 @@ package top.r3944realms.superleadrope.datagen.data;
|
||||||
import net.minecraft.world.item.Item;
|
import net.minecraft.world.item.Item;
|
||||||
import net.minecraft.world.level.block.Block;
|
import net.minecraft.world.level.block.Block;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import top.r3944realms.superleadrope.content.command.LeashDataCommand;
|
||||||
import top.r3944realms.superleadrope.content.command.MotionCommand;
|
import top.r3944realms.superleadrope.content.command.MotionCommand;
|
||||||
|
import top.r3944realms.superleadrope.content.gamerule.server.CreateSuperLeashKnotEntityIfAbsent;
|
||||||
import top.r3944realms.superleadrope.content.gamerule.server.TeleportWithLeashedEntities;
|
import top.r3944realms.superleadrope.content.gamerule.server.TeleportWithLeashedEntities;
|
||||||
import top.r3944realms.superleadrope.content.item.EternalPotatoItem;
|
import top.r3944realms.superleadrope.content.item.EternalPotatoItem;
|
||||||
import top.r3944realms.superleadrope.core.register.SLPEntityTypes;
|
import top.r3944realms.superleadrope.core.register.SLPEntityTypes;
|
||||||
|
|
@ -42,21 +44,24 @@ public enum SLPLangKeyValue {
|
||||||
"Eternal Potato", "永恒土豆", "永恆馬鈴薯", "不滅薯", true
|
"Eternal Potato", "永恒土豆", "永恆馬鈴薯", "不滅薯", true
|
||||||
),
|
),
|
||||||
|
|
||||||
EP_TOOLTIP_TITLE(EternalPotatoItem.getDescKey("title"), ModPartEnum.DESCRIPTION,
|
EP_TOOLTIP_TITLE(
|
||||||
|
EternalPotatoItem.getDescKey("title"), ModPartEnum.DESCRIPTION,
|
||||||
"§6Mythical Item §7- §6Eternal Potato",
|
"§6Mythical Item §7- §6Eternal Potato",
|
||||||
"§6神话物品 §7- §6永恒土豆",
|
"§6神话物品 §7- §6永恒土豆",
|
||||||
"§6神話物品 §7- §6永恒土豆",
|
"§6神話物品 §7- §6永恒土豆",
|
||||||
"§6永恒土豆 §7- §6传奇之物"
|
"§6永恒土豆 §7- §6传奇之物"
|
||||||
),
|
),
|
||||||
|
|
||||||
EP_DESC_TOOLTIP(EternalPotatoItem.getDescKey("desc"), ModPartEnum.DESCRIPTION,
|
EP_DESC_TOOLTIP(
|
||||||
|
EternalPotatoItem.getDescKey("desc"), ModPartEnum.DESCRIPTION,
|
||||||
"§7Symbol of server-wide contract, cannot be discarded",
|
"§7Symbol of server-wide contract, cannot be discarded",
|
||||||
"§7象征全服契约,不可丢弃",
|
"§7象征全服契约,不可丢弃",
|
||||||
"§7象徵全服契約,不可丟棄",
|
"§7象徵全服契約,不可丟棄",
|
||||||
"§7象征全服契约,绝不可弃"
|
"§7象征全服契约,绝不可弃"
|
||||||
),
|
),
|
||||||
|
|
||||||
EP_BIND_OWNER(EternalPotatoItem.getDescKey("bind_owner"), ModPartEnum.DESCRIPTION,
|
EP_BIND_OWNER(
|
||||||
|
EternalPotatoItem.getDescKey("bind_owner"), ModPartEnum.DESCRIPTION,
|
||||||
"§bBound Owner: §f%s",
|
"§bBound Owner: §f%s",
|
||||||
"§b绑定主人: §f%s",
|
"§b绑定主人: §f%s",
|
||||||
"§b綁定主人: §f%s",
|
"§b綁定主人: §f%s",
|
||||||
|
|
@ -70,42 +75,48 @@ public enum SLPLangKeyValue {
|
||||||
"§c尚未绑定主人"
|
"§c尚未绑定主人"
|
||||||
),
|
),
|
||||||
|
|
||||||
EP_OBLIGATION_TOOLTIP(EternalPotatoItem.getDescKey("obligation"), ModPartEnum.DESCRIPTION,
|
EP_OBLIGATION_TOOLTIP(
|
||||||
|
EternalPotatoItem.getDescKey("obligation"), ModPartEnum.DESCRIPTION,
|
||||||
"§7Daily obligations remaining: §a%d §c(+%d§c overdue)",
|
"§7Daily obligations remaining: §a%d §c(+%d§c overdue)",
|
||||||
"§7今日剩余义务: §a%d §c(+%d §c逾期未完成)",
|
"§7今日剩余义务: §a%d §c(+%d §c逾期未完成)",
|
||||||
"§7今日剩餘義務: §a%d §c(+%d §c逾期未完成)",
|
"§7今日剩餘義務: §a%d §c(+%d §c逾期未完成)",
|
||||||
"§7今日责务尚余: §a%d §c(+%d §c逾期未尽)"
|
"§7今日责务尚余: §a%d §c(+%d §c逾期未尽)"
|
||||||
),
|
),
|
||||||
|
|
||||||
EP_PUNISH_TOOLTIP(EternalPotatoItem.getDescKey("punish"), ModPartEnum.DESCRIPTION,
|
EP_PUNISH_TOOLTIP(
|
||||||
|
EternalPotatoItem.getDescKey("punish"), ModPartEnum.DESCRIPTION,
|
||||||
"§cOverdue punishments: §4%d §7(will be applied), grace exceeded: §4%d",
|
"§cOverdue punishments: §4%d §7(will be applied), grace exceeded: §4%d",
|
||||||
"§c逾期未完成责务: §4%d §7(将会受罚),超出宽限数: §4%d",
|
"§c逾期未完成责务: §4%d §7(将会受罚),超出宽限数: §4%d",
|
||||||
"§c逾期未完成责務: §4%d §7(將會受罰),超出寬限數: §4%d",
|
"§c逾期未完成责務: §4%d §7(將會受罰),超出寬限數: §4%d",
|
||||||
"§c逾期责务尚未完成: §4%d §7(將受懲罰),超出寬限數: §4%d"
|
"§c逾期责务尚未完成: §4%d §7(將受懲罰),超出寬限數: §4%d"
|
||||||
),
|
),
|
||||||
|
|
||||||
EP_OBLIGATION_INFO(EternalPotatoItem.getMsgKey("obligation_info"), ModPartEnum.MESSAGE,
|
EP_OBLIGATION_INFO(
|
||||||
|
EternalPotatoItem.getMsgKey("obligation_info"), ModPartEnum.MESSAGE,
|
||||||
"§e[Eternal Potato] §fThis is the server-wide shared person, remaining obligations today: §a%d§f.",
|
"§e[Eternal Potato] §fThis is the server-wide shared person, remaining obligations today: §a%d§f.",
|
||||||
"§e[永恒土豆] §f这是全服共有之人,今日义务剩余:§a%d§f次。",
|
"§e[永恒土豆] §f这是全服共有之人,今日义务剩余:§a%d§f次。",
|
||||||
"§e[永恒土豆] §f這是全服共有之人,今日義務剩餘:§a%d§f次。",
|
"§e[永恒土豆] §f這是全服共有之人,今日義務剩餘:§a%d§f次。",
|
||||||
"§e[永恒土豆] §f此为全服共享之人,今日责务尚余:§a%d§f次。"
|
"§e[永恒土豆] §f此为全服共享之人,今日责务尚余:§a%d§f次。"
|
||||||
),
|
),
|
||||||
|
|
||||||
EP_POTATO_HEAL(EternalPotatoItem.getMsgKey("potato_heal"), ModPartEnum.MESSAGE,
|
EP_POTATO_HEAL(
|
||||||
|
EternalPotatoItem.getMsgKey("potato_heal"), ModPartEnum.MESSAGE,
|
||||||
"§aThe power of the Eternal Potato comforts you, it won't disappear.",
|
"§aThe power of the Eternal Potato comforts you, it won't disappear.",
|
||||||
"§a永恒土豆的力量抚慰了你,但它不会消失。",
|
"§a永恒土豆的力量抚慰了你,但它不会消失。",
|
||||||
"§a永恆土豆的力量撫慰了你,但它不會消失。",
|
"§a永恆土豆的力量撫慰了你,但它不會消失。",
|
||||||
"§a永恒土豆之力慰心,永不消逝。"
|
"§a永恒土豆之力慰心,永不消逝。"
|
||||||
),
|
),
|
||||||
|
|
||||||
EP_CANNOT_DROP(EternalPotatoItem.getMsgKey("cannot_drop"), ModPartEnum.MESSAGE,
|
EP_CANNOT_DROP(
|
||||||
|
EternalPotatoItem.getMsgKey("cannot_drop"), ModPartEnum.MESSAGE,
|
||||||
"§cThe Eternal Potato cannot be dropped! +%d punishments.",
|
"§cThe Eternal Potato cannot be dropped! +%d punishments.",
|
||||||
"§c永恒土豆是不可丢弃的,惩罚数加%d!",
|
"§c永恒土豆是不可丢弃的,惩罚数加%d!",
|
||||||
"§c永恆土豆不可丟棄,懲罰數加%d!",
|
"§c永恆土豆不可丟棄,懲罰數加%d!",
|
||||||
"§c永恒土豆不可丟棄,懲罰數增加%d!"
|
"§c永恒土豆不可丟棄,懲罰數增加%d!"
|
||||||
),
|
),
|
||||||
|
|
||||||
EP_BIND_MSG(EternalPotatoItem.getMsgKey("bind_msg"), ModPartEnum.MESSAGE,
|
EP_BIND_MSG(
|
||||||
|
EternalPotatoItem.getMsgKey("bind_msg"), ModPartEnum.MESSAGE,
|
||||||
"§6Bound to you as the server-wide shared person.",
|
"§6Bound to you as the server-wide shared person.",
|
||||||
"§6已与你绑定,成为全服共有之人。",
|
"§6已与你绑定,成为全服共有之人。",
|
||||||
"§6已與你綁定,成為全服共有之人。",
|
"§6已與你綁定,成為全服共有之人。",
|
||||||
|
|
@ -114,21 +125,24 @@ public enum SLPLangKeyValue {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
EP_OBLIGATION_DONE(EternalPotatoItem.getMsgKey("obligation_done"), ModPartEnum.MESSAGE,
|
EP_OBLIGATION_DONE(
|
||||||
|
EternalPotatoItem.getMsgKey("obligation_done"), ModPartEnum.MESSAGE,
|
||||||
"§eObligation completed, remaining: §a%d§e",
|
"§eObligation completed, remaining: §a%d§e",
|
||||||
"§e义务完成一次,剩余 §a%d §e次。",
|
"§e义务完成一次,剩余 §a%d §e次。",
|
||||||
"§e義務完成一次,剩餘 §a%d §e次。",
|
"§e義務完成一次,剩餘 §a%d §e次。",
|
||||||
"§e责务完成,尚余 §a%d §e次。"
|
"§e责务完成,尚余 §a%d §e次。"
|
||||||
),
|
),
|
||||||
|
|
||||||
EP_OBLIGATION_FULL(EternalPotatoItem.getMsgKey("obligation_full"), ModPartEnum.MESSAGE,
|
EP_OBLIGATION_FULL(
|
||||||
|
EternalPotatoItem.getMsgKey("obligation_full"), ModPartEnum.MESSAGE,
|
||||||
"§aAll obligations completed today!",
|
"§aAll obligations completed today!",
|
||||||
"§a今日义务已全部完成!",
|
"§a今日义务已全部完成!",
|
||||||
"§a今日義務已全部完成!",
|
"§a今日義務已全部完成!",
|
||||||
"§a今日责务尽矣!"
|
"§a今日责务尽矣!"
|
||||||
),
|
),
|
||||||
|
|
||||||
EP_PUNISH_MSG(EternalPotatoItem.getMsgKey("punish_msg"), ModPartEnum.MESSAGE,
|
EP_PUNISH_MSG(
|
||||||
|
EternalPotatoItem.getMsgKey("punish_msg"), ModPartEnum.MESSAGE,
|
||||||
"§cYesterday obligations incomplete, punished!",
|
"§cYesterday obligations incomplete, punished!",
|
||||||
"§c未完成昨日义务,受到惩罚!",
|
"§c未完成昨日义务,受到惩罚!",
|
||||||
"§c未完成昨日義務,受到懲罰!",
|
"§c未完成昨日義務,受到懲罰!",
|
||||||
|
|
@ -142,14 +156,16 @@ public enum SLPLangKeyValue {
|
||||||
"受罚倒数:§a%d §f瞬"
|
"受罚倒数:§a%d §f瞬"
|
||||||
),
|
),
|
||||||
|
|
||||||
EP_PICKUP_NOT_OWNER(EternalPotatoItem.getMsgKey("pickup_not_owner"), ModPartEnum.MESSAGE,
|
EP_PICKUP_NOT_OWNER(
|
||||||
|
EternalPotatoItem.getMsgKey("pickup_not_owner"), ModPartEnum.MESSAGE,
|
||||||
"§cYou are not the rightful owner and cannot pick this up!",
|
"§cYou are not the rightful owner and cannot pick this up!",
|
||||||
"§c非绑定主人无法拾取此物品!",
|
"§c非绑定主人无法拾取此物品!",
|
||||||
"§c非綁定主人無法拾取此物品!",
|
"§c非綁定主人無法拾取此物品!",
|
||||||
"§c非汝所主,勿取!"
|
"§c非汝所主,勿取!"
|
||||||
),
|
),
|
||||||
|
|
||||||
EP_PUNISH_NOT_OWNER(EternalPotatoItem.getMsgKey("punish_not_owner"), ModPartEnum.MESSAGE,
|
EP_PUNISH_NOT_OWNER(
|
||||||
|
EternalPotatoItem.getMsgKey("punish_not_owner"), ModPartEnum.MESSAGE,
|
||||||
"§cYou are not the rightful owner, punished by lightning!",
|
"§cYou are not the rightful owner, punished by lightning!",
|
||||||
"§c非绑定主人使用,受到闪电惩罚!",
|
"§c非绑定主人使用,受到闪电惩罚!",
|
||||||
"§c非綁定主人使用,受到閃電懲罰!",
|
"§c非綁定主人使用,受到閃電懲罰!",
|
||||||
|
|
@ -188,13 +204,29 @@ public enum SLPLangKeyValue {
|
||||||
SLPEntityTypes.getEntityNameKey("super_lead_knot"), ModPartEnum.ENTITY,
|
SLPEntityTypes.getEntityNameKey("super_lead_knot"), ModPartEnum.ENTITY,
|
||||||
"Super Lead Knot", "超级拴绳结", "超級拴繩結", "神駒羈縻索結"
|
"Super Lead Knot", "超级拴绳结", "超級拴繩結", "神駒羈縻索結"
|
||||||
),
|
),
|
||||||
TELEPORT_WITH_LEASHED_ENTITIES_NAME(TeleportWithLeashedEntities.NAME_KEY, ModPartEnum.GAME_RULE,
|
TELEPORT_WITH_LEASHED_ENTITIES_NAME(
|
||||||
|
TeleportWithLeashedEntities.NAME_KEY, ModPartEnum.GAME_RULE,
|
||||||
"Teleport leashed player with player holder",
|
"Teleport leashed player with player holder",
|
||||||
"被拴实体随玩家持有者传送",
|
"被拴实体随玩家持有者传送",
|
||||||
"被拴实体随玩家持有者傳送",
|
"被拴实体随玩家持有者傳送",
|
||||||
"繫畜隨持者傳送"
|
"繫畜隨持者傳送"
|
||||||
),
|
),
|
||||||
TELEPORT_WITH_LEASHED_DESCRIPTION(TeleportWithLeashedEntities.DESCRIPTION_KEY, ModPartEnum.DESCRIPTION,
|
CREATE_SUPER_LEASH_KNOT_ENTITY_IF_ABSENT_NAME(
|
||||||
|
CreateSuperLeashKnotEntityIfAbsent.NAME_KEY, ModPartEnum.NAME,
|
||||||
|
"Create Leash Fence Knot Entity if absent",
|
||||||
|
"如果缺失则创建超级拴绳结",
|
||||||
|
"如果缺失則創建超級拴繩結",
|
||||||
|
"若阙则创超级繫绳结"
|
||||||
|
),
|
||||||
|
CREATE_SUPER_LEASH_KNOT_ENTITY_IF_ABSENT_DESCRIPTION(
|
||||||
|
CreateSuperLeashKnotEntityIfAbsent.DESCRIPTION_KEY, ModPartEnum.DESCRIPTION,
|
||||||
|
"Create LeashKnot Entity if it's absent on fence or other supported positions",
|
||||||
|
"如果在栅栏等支持处缺失超级拴绳结,则创建它",
|
||||||
|
"如果在柵欄等支持處缺失超級拴繩結,則創建它",
|
||||||
|
"若栅等支处阙超级繫绳结,则创之"
|
||||||
|
),
|
||||||
|
TELEPORT_WITH_LEASHED_DESCRIPTION(
|
||||||
|
TeleportWithLeashedEntities.DESCRIPTION_KEY, ModPartEnum.DESCRIPTION,
|
||||||
"Holder will teleport with their leashed players ",
|
"Holder will teleport with their leashed players ",
|
||||||
"传送时将被拴实体与持有者一起传送",
|
"传送时将被拴实体与持有者一起传送",
|
||||||
"將被拴实体將隨持有者一起傳送",
|
"將被拴实体將隨持有者一起傳送",
|
||||||
|
|
@ -221,6 +253,90 @@ public enum SLPLangKeyValue {
|
||||||
"§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"
|
"§b倍乘既成.§a%s§7:§f[§e速勢§7:(§a%.2f§7,§a%.2f§7,§a%.2f§7)§f]§r"
|
||||||
),
|
),
|
||||||
|
MESSAGE_LEASHDATA_GET_TITLE(
|
||||||
|
LeashDataCommand.TITLE, ModPartEnum.COMMAND,
|
||||||
|
"=== Leash Data for %s ===",
|
||||||
|
"=== %s 的拴绳数据 ===",
|
||||||
|
"=== %s 的拴繩數據 ===",
|
||||||
|
"=== %s 之繫繩數據 ==="
|
||||||
|
),
|
||||||
|
MESSAGE_LEASHDATA_GET_TOTAL(
|
||||||
|
LeashDataCommand.TOTAL, ModPartEnum.COMMAND,
|
||||||
|
"Total leashes: %d",
|
||||||
|
"总拴绳数: %d",
|
||||||
|
"總拴繩數: %d",
|
||||||
|
"繫繩總數: %d"
|
||||||
|
),
|
||||||
|
MESSAGE_LEASHDATA_GET_BLOCK(
|
||||||
|
LeashDataCommand.BLOCK, ModPartEnum.COMMAND,
|
||||||
|
"§7Block: §e%s",
|
||||||
|
"§7方块: §e%s",
|
||||||
|
"§7方塊: §e%s",
|
||||||
|
"§7磚石: §e%s"
|
||||||
|
),
|
||||||
|
MESSAGE_LEASHDATA_GET_UUID(
|
||||||
|
LeashDataCommand.UUID, ModPartEnum.COMMAND,
|
||||||
|
"§7UUID: §b%s",
|
||||||
|
"§7UUID: §b%s",
|
||||||
|
"§7UUID: §b%s",
|
||||||
|
"§7UUID: §b%s"
|
||||||
|
),
|
||||||
|
MESSAGE_LEASHDATA_GET_MAX(
|
||||||
|
LeashDataCommand.MAX, ModPartEnum.COMMAND,
|
||||||
|
"§7Max: §a%.1f",
|
||||||
|
"§7最大距离: §a%.1f",
|
||||||
|
"§7最大距離: §a%.1f",
|
||||||
|
"§7極距: §a%.1f"
|
||||||
|
),
|
||||||
|
MESSAGE_LEASHDATA_GET_ELASTIC(
|
||||||
|
LeashDataCommand.ELASTIC, ModPartEnum.COMMAND,
|
||||||
|
"§7Elastic: §6%.1f",
|
||||||
|
"§7弹性距离: §6%.1f",
|
||||||
|
"§7彈性距離: §6%.1f",
|
||||||
|
"§7彈距: §6%.1f"
|
||||||
|
),
|
||||||
|
MESSAGE_LEASHDATA_GET_KEEP(
|
||||||
|
LeashDataCommand.KEEP, ModPartEnum.COMMAND,
|
||||||
|
"§7Keep: §c%d§7/§c%d",
|
||||||
|
"§7保持: §c%d§7/§c%d",
|
||||||
|
"§7保持: §c%d§7/§c%d",
|
||||||
|
"§7持時: §c%d§7/§c%d"
|
||||||
|
),
|
||||||
|
MESSAGE_LEASHDATA_GET_RESERVED(
|
||||||
|
LeashDataCommand.RESERVED, ModPartEnum.COMMAND,
|
||||||
|
"§7Reserved: §d%s",
|
||||||
|
"§7保留字段: §d%s",
|
||||||
|
"§7保留字段: §d%s",
|
||||||
|
"§7備註: §d%s"
|
||||||
|
),
|
||||||
|
MESSAGE_LEASHDATA_ADD_SUCCESS(
|
||||||
|
"command.leashdata.add.success", ModPartEnum.COMMAND,
|
||||||
|
"§bAdded leash successfully. §a%s §7→ §e%s",
|
||||||
|
"§b添加拴绳成功. §a%s §7→ §e%s",
|
||||||
|
"§b添加拴繩成功. §a%s §7→ §e%s",
|
||||||
|
"§b繫繩既添. §a%s §7→ §e%s"
|
||||||
|
),
|
||||||
|
MESSAGE_LEASHDATA_REMOVE_SUCCESS(
|
||||||
|
"command.leashdata.remove.success", ModPartEnum.COMMAND,
|
||||||
|
"§bRemoved leash successfully. §a%s §7- §e%s",
|
||||||
|
"§b移除拴绳成功. §a%s §7- §e%s",
|
||||||
|
"§b移除拴繩成功. §a%s §7- §e%s",
|
||||||
|
"§b繫繩既除. §a%s §7- §e%s"
|
||||||
|
),
|
||||||
|
MESSAGE_LEASHDATA_TRANSFER_SUCCESS(
|
||||||
|
"command.leashdata.transfer.success", ModPartEnum.COMMAND,
|
||||||
|
"§bTransferred leash successfully. §a%s §7→ §e%s §7→ §6%s",
|
||||||
|
"§b转移拴绳成功. §a%s §7→ §e%s §7→ §6%s",
|
||||||
|
"§b轉移拴繩成功. §a%s §7→ §e%s §7→ §6%s",
|
||||||
|
"§b繫繩既移. §a%s §7→ §e%s §7→ §6%s"
|
||||||
|
),
|
||||||
|
MESSAGE_LEASHDATA_SET_SUCCESS(
|
||||||
|
"command.leashdata.set.success", ModPartEnum.COMMAND,
|
||||||
|
"§bSet leash property successfully. §a%s §7: §e%s §7= §6%.1f",
|
||||||
|
"§b设置拴绳属性成功. §a%s §7: §e%s §7= §6%.1f",
|
||||||
|
"§b設置拴繩屬性成功. §a%s §7: §e%s §7= §6%.1f",
|
||||||
|
"§b繫繩性既定. §a%s §7: §e%s §7= §6%.1f"
|
||||||
|
);
|
||||||
|
|
||||||
;
|
;
|
||||||
private final Supplier<?> supplier;
|
private final Supplier<?> supplier;
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,7 @@ import net.minecraft.nbt.CompoundTag;
|
||||||
import net.minecraft.network.FriendlyByteBuf;
|
import net.minecraft.network.FriendlyByteBuf;
|
||||||
import net.minecraft.world.entity.Entity;
|
import net.minecraft.world.entity.Entity;
|
||||||
import net.minecraftforge.network.NetworkEvent;
|
import net.minecraftforge.network.NetworkEvent;
|
||||||
import top.r3944realms.superleadrope.content.capability.CapabilityHandler;
|
import top.r3944realms.superleadrope.util.capability.LeashDataAPI;
|
||||||
|
|
||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
|
|
@ -42,7 +42,7 @@ public record LeashDataSyncPacket(int entityId, CompoundTag leashData) {
|
||||||
if (level != null) {
|
if (level != null) {
|
||||||
Entity entity = level.getEntity(msg.entityId);
|
Entity entity = level.getEntity(msg.entityId);
|
||||||
if (entity != null) {
|
if (entity != null) {
|
||||||
entity.getCapability(CapabilityHandler.LEASH_DATA_CAP).ifPresent(cap -> {
|
LeashDataAPI.getLeashData(entity).ifPresent(cap -> {
|
||||||
// 只在数据确实变化时更新
|
// 只在数据确实变化时更新
|
||||||
CompoundTag current = cap.serializeNBT();
|
CompoundTag current = cap.serializeNBT();
|
||||||
if (!current.equals(msg.leashData)) {
|
if (!current.equals(msg.leashData)) {
|
||||||
|
|
|
||||||
|
|
@ -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 <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package top.r3944realms.superleadrope.util.capability;
|
||||||
|
|
||||||
|
import net.minecraft.core.BlockPos;
|
||||||
|
import net.minecraft.world.entity.Entity;
|
||||||
|
import net.minecraft.world.entity.player.Player;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import top.r3944realms.superleadrope.content.capability.CapabilityHandler;
|
||||||
|
import top.r3944realms.superleadrope.content.capability.inter.ILeashData;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 拴绳数据API - 提供统一的API接口操作拴绳数据能力
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("unused")
|
||||||
|
public final class LeashDataAPI {
|
||||||
|
// ==================== 基础能力获取 ====================
|
||||||
|
public static Optional<ILeashData> getLeashData(@NotNull Entity entity) {
|
||||||
|
Objects.requireNonNull(entity, "Entity cannot be null");
|
||||||
|
return entity.getCapability(CapabilityHandler.LEASH_DATA_CAP).resolve();
|
||||||
|
}
|
||||||
|
// ==================== 拴绳数据管理 API ====================
|
||||||
|
|
||||||
|
public static final class LeashOperations {
|
||||||
|
private LeashOperations() {}
|
||||||
|
|
||||||
|
// ---------------------- 添加拴绳 ----------------------
|
||||||
|
public static boolean attach(Entity entity, Entity holder) {
|
||||||
|
return getLeashData(entity).map(data -> data.addLeash(holder)).orElse(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean attach(Entity entity, Entity holder, String reserved) {
|
||||||
|
return getLeashData(entity).map(data -> data.addLeash(holder, reserved)).orElse(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean attach(Entity entity, Entity holder, double maxDistance) {
|
||||||
|
return getLeashData(entity).map(data -> data.addLeash(holder, maxDistance)).orElse(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean attach(Entity entity, Entity holder, double maxDistance, double elasticDistance, int maxKeepTicks) {
|
||||||
|
return getLeashData(entity).map(data -> data.addLeash(holder, maxDistance, elasticDistance, maxKeepTicks)).orElse(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean attach(Entity entity, Entity holder, double maxDistance, String reserved) {
|
||||||
|
return getLeashData(entity).map(data -> data.addLeash(holder, maxDistance, reserved)).orElse(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean attach(Entity entity, Entity holder, double maxDistance, double elasticDistance, int maxKeepTicks, String reserved) {
|
||||||
|
return getLeashData(entity).map(data -> data.addLeash(holder, maxDistance, elasticDistance, maxKeepTicks, reserved)).orElse(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void attachWithInfo(Entity entity, Entity holder, ILeashData.LeashInfo info) {
|
||||||
|
getLeashData(entity).ifPresent(data -> data.addLeash(holder, info));
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------- 延迟拴绳 ----------------------
|
||||||
|
public static void attachDelayed(Entity entity, Player holderPlayer) {
|
||||||
|
getLeashData(entity).ifPresent(data -> data.addDelayedLeash(holderPlayer));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void removeDelayed(Entity entity, UUID onceHolderPlayerUUID) {
|
||||||
|
getLeashData(entity).ifPresent(data -> data.removeDelayedLeash(onceHolderPlayerUUID));
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------- 移除拴绳 ----------------------
|
||||||
|
public static boolean detach(Entity entity, Entity holder) {
|
||||||
|
return getLeashData(entity).map(data -> data.removeLeash(holder)).orElse(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean detach(Entity entity, UUID holderUUID) {
|
||||||
|
return getLeashData(entity).map(data -> data.removeLeash(holderUUID)).orElse(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean detach(Entity entity, BlockPos knotPos) {
|
||||||
|
return getLeashData(entity).map(data -> data.removeLeash(knotPos)).orElse(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void detachAll(Entity entity) {
|
||||||
|
getLeashData(entity).ifPresent(ILeashData::removeAllLeashes);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void detachAllHolders(Entity entity) {
|
||||||
|
getLeashData(entity).ifPresent(ILeashData::removeAllHolderLeashes);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void detachAllKnots(Entity entity) {
|
||||||
|
getLeashData(entity).ifPresent(ILeashData::removeAllKnotLeashes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ==================== 拴绳属性修改 API ====================
|
||||||
|
|
||||||
|
public static final class PropertyOperations {
|
||||||
|
private PropertyOperations() {}
|
||||||
|
|
||||||
|
// ---------------------- 设置最大距离 ----------------------
|
||||||
|
public static boolean setMaxDistance(Entity entity, Entity holder, double distance) {
|
||||||
|
return getLeashData(entity).map(data -> data.setMaxDistance(holder, distance)).orElse(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean setMaxDistance(Entity entity, Entity holder, double distance, int maxKeepTicks) {
|
||||||
|
return getLeashData(entity).map(data -> data.setMaxDistance(holder, distance, maxKeepTicks)).orElse(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean setMaxDistance(Entity entity, Entity holder, double distance, int maxKeepTicks, String reserved) {
|
||||||
|
return getLeashData(entity).map(data -> data.setMaxDistance(holder, distance, maxKeepTicks, reserved)).orElse(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean setMaxDistance(Entity entity, UUID holderUUID, double distance) {
|
||||||
|
return getLeashData(entity).map(data -> data.setMaxDistance(holderUUID, distance)).orElse(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean setMaxDistance(Entity entity, UUID holderUUID, double distance, int maxKeepTicks) {
|
||||||
|
return getLeashData(entity).map(data -> data.setMaxDistance(holderUUID, distance, maxKeepTicks)).orElse(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean setMaxDistance(Entity entity, UUID holderUUID, double distance, int maxKeepTicks, String reserved) {
|
||||||
|
return getLeashData(entity).map(data -> data.setMaxDistance(holderUUID, distance, maxKeepTicks, reserved)).orElse(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean setMaxDistance(Entity entity, BlockPos knotPos, double distance) {
|
||||||
|
return getLeashData(entity).map(data -> data.setMaxDistance(knotPos, distance)).orElse(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean setMaxDistance(Entity entity, BlockPos knotPos, double distance, int maxKeepTicks) {
|
||||||
|
return getLeashData(entity).map(data -> data.setMaxDistance(knotPos, distance, maxKeepTicks)).orElse(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean setMaxDistance(Entity entity, BlockPos knotPos, double distance, int maxKeepTicks, String reserved) {
|
||||||
|
return getLeashData(entity).map(data -> data.setMaxDistance(knotPos, distance, maxKeepTicks, reserved)).orElse(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------- 设置弹性距离 ----------------------
|
||||||
|
public static boolean setElasticDistance(Entity entity, Entity holder, double distance) {
|
||||||
|
return getLeashData(entity).map(data -> data.setElasticDistance(holder, distance)).orElse(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean setElasticDistance(Entity entity, Entity holder, double distance, int maxKeepTicks) {
|
||||||
|
return getLeashData(entity).map(data -> data.setElasticDistance(holder, distance, maxKeepTicks)).orElse(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean setElasticDistance(Entity entity, Entity holder, double distance, int maxKeepTicks, String reserved) {
|
||||||
|
return getLeashData(entity).map(data -> data.setElasticDistance(holder, distance, maxKeepTicks, reserved)).orElse(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean setElasticDistance(Entity entity, UUID holderUUID, double distance) {
|
||||||
|
return getLeashData(entity).map(data -> data.setElasticDistance(holderUUID, distance)).orElse(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean setElasticDistance(Entity entity, UUID holderUUID, double distance, int maxKeepTicks) {
|
||||||
|
return getLeashData(entity).map(data -> data.setElasticDistance(holderUUID, distance, maxKeepTicks)).orElse(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean setElasticDistance(Entity entity, UUID holderUUID, double distance, int maxKeepTicks, String reserved) {
|
||||||
|
return getLeashData(entity).map(data -> data.setElasticDistance(holderUUID, distance, maxKeepTicks, reserved)).orElse(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean setElasticDistance(Entity entity, BlockPos knotPos, double distance) {
|
||||||
|
return getLeashData(entity).map(data -> data.setElasticDistance(knotPos, distance)).orElse(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean setElasticDistance(Entity entity, BlockPos knotPos, double distance, int maxKeepTicks) {
|
||||||
|
return getLeashData(entity).map(data -> data.setElasticDistance(knotPos, distance, maxKeepTicks)).orElse(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean setElasticDistance(Entity entity, BlockPos knotPos, double distance, int maxKeepTicks, String reserved) {
|
||||||
|
return getLeashData(entity).map(data -> data.setElasticDistance(knotPos, distance, maxKeepTicks, reserved)).orElse(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ==================== 物理应用 API ====================
|
||||||
|
|
||||||
|
public static final class PhysicsOperations {
|
||||||
|
private PhysicsOperations() {}
|
||||||
|
|
||||||
|
public static void applyForces(Entity entity) {
|
||||||
|
getLeashData(entity).ifPresent(ILeashData::applyLeashForces);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ==================== 拴绳转移 API ====================
|
||||||
|
|
||||||
|
public static final class TransferOperations {
|
||||||
|
private TransferOperations() {}
|
||||||
|
|
||||||
|
public static boolean transfer(Entity entity, Entity holder, Entity newHolder) {
|
||||||
|
return getLeashData(entity).map(data -> data.transferLeash(holder, newHolder)).orElse(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean transfer(Entity entity, Entity holder, Entity newHolder, String reserved) {
|
||||||
|
return getLeashData(entity).map(data -> data.transferLeash(holder, newHolder, reserved)).orElse(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean transfer(Entity entity, UUID holderUUID, Entity newHolder) {
|
||||||
|
return getLeashData(entity).map(data -> data.transferLeash(holderUUID, newHolder)).orElse(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean transfer(Entity entity, UUID holderUUID, Entity newHolder, String reserved) {
|
||||||
|
return getLeashData(entity).map(data -> data.transferLeash(holderUUID, newHolder, reserved)).orElse(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean transfer(Entity entity, BlockPos knotPos, Entity newHolder) {
|
||||||
|
return getLeashData(entity).map(data -> data.transferLeash(knotPos, newHolder)).orElse(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean transfer(Entity entity, BlockPos knotPos, Entity newHolder, String reserved) {
|
||||||
|
return getLeashData(entity).map(data -> data.transferLeash(knotPos, newHolder, reserved)).orElse(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ==================== 查询操作 API ====================
|
||||||
|
|
||||||
|
public static final class QueryOperations {
|
||||||
|
private QueryOperations() {}
|
||||||
|
|
||||||
|
public static boolean hasLeash(Entity entity) {
|
||||||
|
return getLeashData(entity).map(ILeashData::hasLeash).orElse(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean hasKnotLeash(Entity entity) {
|
||||||
|
return getLeashData(entity).map(ILeashData::hasKnotLeash).orElse(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean hasHolderLeash(Entity entity) {
|
||||||
|
return getLeashData(entity).map(ILeashData::hasHolderLeash).orElse(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Collection<ILeashData.LeashInfo> getAllLeashes(Entity entity) {
|
||||||
|
return getLeashData(entity).map(ILeashData::getAllLeashes).orElse(Collections.emptyList());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isLeashedBy(Entity entity, Entity holder) {
|
||||||
|
return getLeashData(entity).map(data -> data.isLeashedBy(holder)).orElse(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isLeashedBy(Entity entity, UUID holderUUID) {
|
||||||
|
return getLeashData(entity).map(data -> data.isLeashedBy(holderUUID)).orElse(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isLeashedBy(Entity entity, BlockPos knotPos) {
|
||||||
|
return getLeashData(entity).map(data -> data.isLeashedBy(knotPos)).orElse(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isInDelayedLeash(Entity entity, UUID holderUUID) {
|
||||||
|
return getLeashData(entity).map(data -> data.isInDelayedLeash(holderUUID)).orElse(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Optional<ILeashData.LeashInfo> getLeashInfo(Entity entity, Entity holder) {
|
||||||
|
return getLeashData(entity).flatMap(data -> data.getLeashInfo(holder));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Optional<ILeashData.LeashInfo> getLeashInfo(Entity entity, UUID holderUUID) {
|
||||||
|
return getLeashData(entity).flatMap(data -> data.getLeashInfo(holderUUID));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Optional<ILeashData.LeashInfo> getLeashInfo(Entity entity, BlockPos knotPos) {
|
||||||
|
return getLeashData(entity).flatMap(data -> data.getLeashInfo(knotPos));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean canBeLeashed(Entity entity) {
|
||||||
|
return getLeashData(entity).map(ILeashData::canBeLeashed).orElse(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean canBeAttachedTo(Entity entity, Entity target) {
|
||||||
|
return getLeashData(entity).map(data -> data.canBeAttachedTo(target)).orElse(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ==================== 占用和同步 API ====================
|
||||||
|
|
||||||
|
public static final class ManagementOperations {
|
||||||
|
private ManagementOperations() {}
|
||||||
|
|
||||||
|
public static Optional<UUID> occupyLeash(Entity entity) {
|
||||||
|
return getLeashData(entity).flatMap(ILeashData::occupyLeash);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void markForSync(Entity entity) {
|
||||||
|
getLeashData(entity).ifPresent(ILeashData::markForSync);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void immediateSync(Entity entity) {
|
||||||
|
getLeashData(entity).ifPresent(ILeashData::immediateSync);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void checkSync(Entity entity) {
|
||||||
|
getLeashData(entity).ifPresent(ILeashData::checkSync);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ==================== 工具方法 ====================
|
||||||
|
|
||||||
|
public static final class Utils {
|
||||||
|
private Utils() {}
|
||||||
|
|
||||||
|
public static boolean hasLeashData(Entity entity) {
|
||||||
|
return getLeashData(entity).isPresent();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,273 @@
|
||||||
|
/*
|
||||||
|
* 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 <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package top.r3944realms.superleadrope.util.capability;
|
||||||
|
|
||||||
|
import net.minecraft.core.BlockPos;
|
||||||
|
import net.minecraft.world.entity.Entity;
|
||||||
|
import net.minecraft.world.phys.Vec3;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import top.r3944realms.superleadrope.CommonEventHandler;
|
||||||
|
import top.r3944realms.superleadrope.content.capability.CapabilityHandler;
|
||||||
|
import top.r3944realms.superleadrope.content.capability.inter.ILeashState;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
@SuppressWarnings("unused")
|
||||||
|
public final class LeashStateAPI {
|
||||||
|
|
||||||
|
private LeashStateAPI() {
|
||||||
|
} // 防止实例化
|
||||||
|
|
||||||
|
public static Optional<ILeashState> getLeashState(@NotNull Entity entity) {
|
||||||
|
Objects.requireNonNull(entity, "Entity cannot be null");
|
||||||
|
return entity.getCapability(CapabilityHandler.LEASH_STATE_CAP).resolve();
|
||||||
|
}
|
||||||
|
|
||||||
|
// ==================== 查询操作 ====================
|
||||||
|
|
||||||
|
public static final class Query {
|
||||||
|
private Query() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Map<UUID, ILeashState.LeashState> getAllUUIDStates(Entity entity) {
|
||||||
|
return getLeashState(entity)
|
||||||
|
.map(ILeashState::getHolderLeashStates)
|
||||||
|
.orElse(Map.of());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Map<BlockPos, ILeashState.LeashState> getAllBlockPosStates(Entity entity) {
|
||||||
|
return getLeashState(entity)
|
||||||
|
.map(ILeashState::getKnotLeashStates)
|
||||||
|
.orElse(Map.of());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Optional<ILeashState.LeashState> getState(Entity entity, Entity holder) {
|
||||||
|
return getLeashState(entity).flatMap(state -> state.getLeashState(holder));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Optional<ILeashState.LeashState> getState(Entity entity, UUID holderUUID) {
|
||||||
|
return getLeashState(entity).flatMap(state -> state.getLeashState(holderUUID));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Optional<ILeashState.LeashState> getState(Entity entity, BlockPos knotPos) {
|
||||||
|
return getLeashState(entity).flatMap(state -> state.getLeashState(knotPos));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean hasState(Entity entity) {
|
||||||
|
return getLeashState(entity).isPresent();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean hasStateFor(Entity entity, Entity holder) {
|
||||||
|
return getState(entity, holder).isPresent();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean hasStateFor(Entity entity, UUID holderUUID) {
|
||||||
|
return getState(entity, holderUUID).isPresent();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean hasStateFor(Entity entity, BlockPos knotPos) {
|
||||||
|
return getState(entity, knotPos).isPresent();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ==================== 偏移量操作 ====================
|
||||||
|
|
||||||
|
public static final class Offset {
|
||||||
|
private Offset() {
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------- 重置操作 ----------------------
|
||||||
|
public static void resetAll(Entity entity) {
|
||||||
|
getLeashState(entity).ifPresent(ILeashState::resetAllLeashHolderLocationsOffset);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void resetFor(Entity entity, Entity holder) {
|
||||||
|
getLeashState(entity).ifPresent(state -> state.resetLeashHolderLocationOffset(holder));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void resetFor(Entity entity, UUID holderUUID) {
|
||||||
|
getLeashState(entity).ifPresent(state -> state.resetLeashHolderLocationOffset(holderUUID));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void resetFor(Entity entity, BlockPos knotPos) {
|
||||||
|
getLeashState(entity).ifPresent(state -> state.resetLeashHolderLocationOffset(knotPos));
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------- 设置操作 ----------------------
|
||||||
|
public static void setFor(Entity entity, Entity holder, Vec3 offset) {
|
||||||
|
getLeashState(entity).ifPresent(state -> state.setLeashHolderLocationOffset(holder, offset));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void setFor(Entity entity, UUID holderUUID, Vec3 offset) {
|
||||||
|
getLeashState(entity).ifPresent(state -> state.setLeashHolderLocationOffset(holderUUID, offset));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void setFor(Entity entity, BlockPos knotPos, Vec3 offset) {
|
||||||
|
getLeashState(entity).ifPresent(state -> state.setLeashHolderLocationOffset(knotPos, offset));
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------- 添加操作 ----------------------
|
||||||
|
public static void addTo(Entity entity, Entity holder, Vec3 offset) {
|
||||||
|
getLeashState(entity).ifPresent(state -> state.addLeashHolderLocationOffset(holder, offset));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void addTo(Entity entity, UUID holderUUID, Vec3 offset) {
|
||||||
|
getLeashState(entity).ifPresent(state -> state.addLeashHolderLocationOffset(holderUUID, offset));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void addTo(Entity entity, BlockPos knotPos, Vec3 offset) {
|
||||||
|
getLeashState(entity).ifPresent(state -> state.addLeashHolderLocationOffset(knotPos, offset));
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------- 移除操作 ----------------------
|
||||||
|
public static void removeFor(Entity entity, Entity holder) {
|
||||||
|
getLeashState(entity).ifPresent(state -> state.removeLeashHolderLocationOffset(holder));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void removeFor(Entity entity, UUID holderUUID) {
|
||||||
|
getLeashState(entity).ifPresent(state -> state.removeLeashHolderLocationOffset(holderUUID));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void removeFor(Entity entity, BlockPos knotPos) {
|
||||||
|
getLeashState(entity).ifPresent(state -> state.removeLeashHolderLocationOffset(knotPos));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void removeAll(Entity entity) {
|
||||||
|
getLeashState(entity).ifPresent(ILeashState::removeAllLeashHolderLocationOffset);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void removeAllUUIDs(Entity entity) {
|
||||||
|
getLeashState(entity).ifPresent(ILeashState::removeAllLeashHolderUUIDLocationOffset);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void removeAllBlockPoses(Entity entity) {
|
||||||
|
getLeashState(entity).ifPresent(ILeashState::removeAllLeashHolderBlockPosLocationOffset);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ==================== 应用实体偏移量操作 ====================
|
||||||
|
|
||||||
|
public static final class ApplyEntity {
|
||||||
|
private ApplyEntity() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Optional<Vec3> getOffset(Entity entity) {
|
||||||
|
return getLeashState(entity).flatMap(ILeashState::getLeashApplyEntityLocationOffset);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Vec3 getDefaultOffset(Entity entity) {
|
||||||
|
return getLeashState(entity)
|
||||||
|
.map(ILeashState::getDefaultLeashApplyEntityLocationOffset)
|
||||||
|
.orElse(Vec3.ZERO);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void resetAll(Entity entity) {
|
||||||
|
getLeashState(entity).ifPresent(ILeashState::resetAllLeashApplyEntityLocationsOffset);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void remove(Entity entity) {
|
||||||
|
getLeashState(entity).ifPresent(ILeashState::removeLeashApplyEntityLocationOffset);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void set(Entity entity, Vec3 offset) {
|
||||||
|
getLeashState(entity).ifPresent(state -> state.setLeashApplyEntityLocationOffset(offset));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void add(Entity entity, Vec3 offset) {
|
||||||
|
getLeashState(entity).ifPresent(state -> state.addLeashApplyEntityLocationOffset(offset));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ==================== 同步操作 ====================
|
||||||
|
|
||||||
|
public static final class Sync {
|
||||||
|
private Sync() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void mark(Entity entity) {
|
||||||
|
getLeashState(entity).ifPresent(ILeashState::markForSync);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void immediate(Entity entity) {
|
||||||
|
getLeashState(entity).ifPresent(ILeashState::immediateSync);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void check(Entity entity) {
|
||||||
|
getLeashState(entity).ifPresent(ILeashState::checkSync);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ==================== 高级操作 ====================
|
||||||
|
|
||||||
|
public static final class Operations {
|
||||||
|
private Operations() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void attach(Entity leashed, Entity holder) {
|
||||||
|
getLeashState(leashed).ifPresent(state ->
|
||||||
|
state.addLeashHolderLocationOffset(holder,
|
||||||
|
CommonEventHandler.leashConfigManager.getEntityOffset(holder))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void detach(Entity leashed, Entity holder) {
|
||||||
|
Offset.removeFor(leashed, holder);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void detach(Entity leashed, UUID holderUUID) {
|
||||||
|
Offset.removeFor(leashed, holderUUID);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void detach(Entity leashed, BlockPos knotPos) {
|
||||||
|
Offset.removeFor(leashed, knotPos);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void transfer(Entity leashed, Entity oldHolder, Entity newHolder) {
|
||||||
|
getLeashState(leashed).ifPresent(state -> {
|
||||||
|
state.removeLeashHolderLocationOffset(oldHolder);
|
||||||
|
state.addLeashHolderLocationOffset(newHolder,
|
||||||
|
CommonEventHandler.leashConfigManager.getEntityOffset(newHolder));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void transfer(Entity leashed, UUID oldHolderUUID, Entity newHolder) {
|
||||||
|
getLeashState(leashed).ifPresent(state -> {
|
||||||
|
state.removeLeashHolderLocationOffset(oldHolderUUID);
|
||||||
|
state.addLeashHolderLocationOffset(newHolder,
|
||||||
|
CommonEventHandler.leashConfigManager.getEntityOffset(newHolder));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void transfer(Entity leashed, BlockPos oldKnotPos, Entity newHolder) {
|
||||||
|
getLeashState(leashed).ifPresent(state -> {
|
||||||
|
state.removeLeashHolderLocationOffset(oldKnotPos);
|
||||||
|
state.addLeashHolderLocationOffset(newHolder,
|
||||||
|
CommonEventHandler.leashConfigManager.getEntityOffset(newHolder));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void copy(Entity source, Entity target) {
|
||||||
|
getLeashState(source).ifPresent(sourceState ->
|
||||||
|
getLeashState(target).ifPresent(targetState ->
|
||||||
|
targetState.copy(sourceState, target)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,36 +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 <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
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<ILeashData> getLeashData(@NotNull Entity entity) {
|
|
||||||
Objects.requireNonNull(entity, "Entity cannot be null");
|
|
||||||
return entity.getCapability(CapabilityHandler.LEASH_DATA_CAP).resolve();
|
|
||||||
}
|
|
||||||
public static Optional<ILeashState> getLeashState(@NotNull Entity entity) {
|
|
||||||
Objects.requireNonNull(entity, "Entity cannot be null");
|
|
||||||
return entity.getCapability(CapabilityHandler.LEASH_STATE_CAP).resolve();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -16,6 +16,7 @@
|
||||||
package top.r3944realms.superleadrope.util.riding;
|
package top.r3944realms.superleadrope.util.riding;
|
||||||
|
|
||||||
import net.minecraft.world.entity.Entity;
|
import net.minecraft.world.entity.Entity;
|
||||||
|
import top.r3944realms.superleadrope.CommonEventHandler;
|
||||||
import top.r3944realms.superleadrope.SuperLeadRope;
|
import top.r3944realms.superleadrope.SuperLeadRope;
|
||||||
import top.r3944realms.superleadrope.core.exception.RidingCycleException;
|
import top.r3944realms.superleadrope.core.exception.RidingCycleException;
|
||||||
import top.r3944realms.superleadrope.util.model.RidingRelationship;
|
import top.r3944realms.superleadrope.util.model.RidingRelationship;
|
||||||
|
|
@ -55,7 +56,7 @@ public class RidingApplier {
|
||||||
if (entity == null) continue;
|
if (entity == null) continue;
|
||||||
|
|
||||||
// ---------- 白名单保护 ----------
|
// ---------- 白名单保护 ----------
|
||||||
if (!RidingValidator.isInWhitelist(entity.getType())) {
|
if (!CommonEventHandler.leashConfigManager.isEntityTeleportAllowed(entity)) {
|
||||||
// 不在白名单,跳过本节点,但保留其乘客挂回上层
|
// 不在白名单,跳过本节点,但保留其乘客挂回上层
|
||||||
if (vehicle != null) {
|
if (vehicle != null) {
|
||||||
// 将当前节点的乘客挂回上层载具
|
// 将当前节点的乘客挂回上层载具
|
||||||
|
|
@ -75,7 +76,7 @@ public class RidingApplier {
|
||||||
}
|
}
|
||||||
|
|
||||||
// 如果有指定的载具,尝试上车
|
// 如果有指定的载具,尝试上车
|
||||||
if (vehicle != null && RidingValidator.isInWhitelist(vehicle.getType())) {
|
if (vehicle != null && CommonEventHandler.leashConfigManager.isEntityTeleportAllowed(vehicle)) {
|
||||||
if (RidingValidator.wouldCreateCycle(entity, vehicle)) {
|
if (RidingValidator.wouldCreateCycle(entity, vehicle)) {
|
||||||
throw new RidingCycleException(entityId, vehicleId);
|
throw new RidingCycleException(entityId, vehicleId);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@ package top.r3944realms.superleadrope.util.riding;
|
||||||
|
|
||||||
import net.minecraft.world.entity.Entity;
|
import net.minecraft.world.entity.Entity;
|
||||||
import net.minecraft.world.entity.EntityType;
|
import net.minecraft.world.entity.EntityType;
|
||||||
|
import top.r3944realms.superleadrope.CommonEventHandler;
|
||||||
import top.r3944realms.superleadrope.core.exception.RidingCycleException;
|
import top.r3944realms.superleadrope.core.exception.RidingCycleException;
|
||||||
import top.r3944realms.superleadrope.core.util.ImmutablePair;
|
import top.r3944realms.superleadrope.core.util.ImmutablePair;
|
||||||
import top.r3944realms.superleadrope.util.model.RidingRelationship;
|
import top.r3944realms.superleadrope.util.model.RidingRelationship;
|
||||||
|
|
@ -72,7 +73,7 @@ public class RidingSaver {
|
||||||
processedEntities.add(passengerId);
|
processedEntities.add(passengerId);
|
||||||
|
|
||||||
// ✅ 校验白名单
|
// ✅ 校验白名单
|
||||||
if (!RidingValidator.isInWhitelist(passenger.getType())) {
|
if (!CommonEventHandler.leashConfigManager.isEntityTeleportAllowed(passenger.getType())) {
|
||||||
// ❌ 不在白名单,直接截断
|
// ❌ 不在白名单,直接截断
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
@ -105,7 +106,7 @@ public class RidingSaver {
|
||||||
if (relationship == null) return null;
|
if (relationship == null) return null;
|
||||||
|
|
||||||
// 如果当前根节点在白名单,则直接处理子节点
|
// 如果当前根节点在白名单,则直接处理子节点
|
||||||
if (RidingValidator.isInWhitelist(Objects.requireNonNull(getEntityType(relationship.getEntityId())))) {
|
if (CommonEventHandler.leashConfigManager.isEntityTeleportAllowed(Objects.requireNonNull(getEntityType(relationship.getEntityId())))) {
|
||||||
RidingRelationship filtered = new RidingRelationship();
|
RidingRelationship filtered = new RidingRelationship();
|
||||||
filtered.setEntityId(relationship.getEntityId());
|
filtered.setEntityId(relationship.getEntityId());
|
||||||
filtered.setVehicleId(relationship.getVehicleId());
|
filtered.setVehicleId(relationship.getVehicleId());
|
||||||
|
|
@ -114,7 +115,7 @@ public class RidingSaver {
|
||||||
} else {
|
} else {
|
||||||
// 根节点不在白名单,尝试找到合法的子节点作为新的根
|
// 根节点不在白名单,尝试找到合法的子节点作为新的根
|
||||||
for (RidingRelationship child : relationship.getPassengers()) {
|
for (RidingRelationship child : relationship.getPassengers()) {
|
||||||
if (RidingValidator.isInWhitelist(Objects.requireNonNull(getEntityType(child.getEntityId())))) {
|
if (CommonEventHandler.leashConfigManager.isEntityTeleportAllowed(Objects.requireNonNull(getEntityType(child.getEntityId())))) {
|
||||||
// 设置父节点为当前节点的父(倒二叉逻辑)
|
// 设置父节点为当前节点的父(倒二叉逻辑)
|
||||||
RidingRelationship newRoot = new RidingRelationship();
|
RidingRelationship newRoot = new RidingRelationship();
|
||||||
newRoot.setEntityId(child.getEntityId());
|
newRoot.setEntityId(child.getEntityId());
|
||||||
|
|
|
||||||
|
|
@ -15,52 +15,12 @@
|
||||||
|
|
||||||
package top.r3944realms.superleadrope.util.riding;
|
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.Entity;
|
||||||
import net.minecraft.world.entity.EntityType;
|
|
||||||
import top.r3944realms.superleadrope.config.LeashCommonConfig;
|
|
||||||
|
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.Queue;
|
import java.util.Queue;
|
||||||
|
|
||||||
public class RidingValidator {
|
public class RidingValidator {
|
||||||
/**
|
|
||||||
* 是否在配置白名单里
|
|
||||||
*/
|
|
||||||
@SuppressWarnings("deprecation")
|
|
||||||
public static boolean isInWhitelist(EntityType<?> type) {
|
|
||||||
String key = type.builtInRegistryHolder().key().location().toString();
|
|
||||||
String modid = key.split(":")[0];
|
|
||||||
|
|
||||||
for (String entry : LeashCommonConfig.COMMON.teleportWhitelist.get()) {
|
|
||||||
if (entry.startsWith("#")) {
|
|
||||||
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<EntityType<?>> 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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
/**
|
/**
|
||||||
* 检查骑乘是否会产生循环引用
|
* 检查骑乘是否会产生循环引用
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,7 @@ import net.minecraft.world.entity.ai.goal.Goal;
|
||||||
import net.minecraft.world.entity.animal.Animal;
|
import net.minecraft.world.entity.animal.Animal;
|
||||||
import net.minecraft.world.phys.Vec3;
|
import net.minecraft.world.phys.Vec3;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
import top.r3944realms.superleadrope.CommonEventHandler;
|
||||||
import top.r3944realms.superleadrope.network.NetworkHandler;
|
import top.r3944realms.superleadrope.network.NetworkHandler;
|
||||||
import top.r3944realms.superleadrope.network.toClient.UpdatePlayerMovementPacket;
|
import top.r3944realms.superleadrope.network.toClient.UpdatePlayerMovementPacket;
|
||||||
|
|
||||||
|
|
@ -37,7 +38,7 @@ public class RindingLeash {
|
||||||
|
|
||||||
Entity current = root;
|
Entity current = root;
|
||||||
while (current != null) {
|
while (current != null) {
|
||||||
if (RidingValidator.isInWhitelist(current.getType())) {
|
if (CommonEventHandler.leashConfigManager.isEntityTeleportAllowed(current.getType())) {
|
||||||
return current; // 找到白名单载具
|
return current; // 找到白名单载具
|
||||||
}
|
}
|
||||||
current = current.getVehicle();
|
current = current.getVehicle();
|
||||||
|
|
@ -60,7 +61,7 @@ public class RindingLeash {
|
||||||
|
|
||||||
Entity current = root;
|
Entity current = root;
|
||||||
while (current != null) {
|
while (current != null) {
|
||||||
if (RidingValidator.isInWhitelist(current.getType())) {
|
if (CommonEventHandler.leashConfigManager.isEntityTeleportAllowed(current.getType())) {
|
||||||
return current; // 找到白名单载具
|
return current; // 找到白名单载具
|
||||||
}
|
}
|
||||||
current = current.getVehicle();
|
current = current.getVehicle();
|
||||||
|
|
|
||||||
|
|
@ -1 +1,3 @@
|
||||||
{}
|
{
|
||||||
|
"morsb_patch":"coremods/morsb_patch.js"
|
||||||
|
}
|
||||||
62
src/main/resources/coremods/morsb_patch.js
Normal file
62
src/main/resources/coremods/morsb_patch.js
Normal file
|
|
@ -0,0 +1,62 @@
|
||||||
|
/*
|
||||||
|
* 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 <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
var Opcodes = Java.type("org.objectweb.asm.Opcodes");
|
||||||
|
var ASMAPI = Java.type("net.minecraftforge.coremod.api.ASMAPI");
|
||||||
|
var VarInsnNode = Java.type("org.objectweb.asm.tree.VarInsnNode");
|
||||||
|
var MethodInsnNode = Java.type("org.objectweb.asm.tree.MethodInsnNode");
|
||||||
|
var InsnNode = Java.type("org.objectweb.asm.tree.InsnNode");
|
||||||
|
|
||||||
|
function initializeCoreMod() {
|
||||||
|
return {
|
||||||
|
"leash_render_patch": {
|
||||||
|
"target": {
|
||||||
|
"type": "METHOD",
|
||||||
|
"class": "net.minecraft.client.renderer.entity.MobRenderer",
|
||||||
|
"methodName": "m_5523_",
|
||||||
|
"methodDesc": "(Lnet/minecraft/world/entity/Mob;Lnet/minecraft/client/renderer/culling/Frustum;DDD)Z"
|
||||||
|
},
|
||||||
|
"transformer": function(method) {
|
||||||
|
var insns = method.instructions;
|
||||||
|
|
||||||
|
for (var i = 0; i < insns.size(); i++) {
|
||||||
|
var insn = insns.get(i);
|
||||||
|
if (insn.getOpcode && insn.getOpcode() === Opcodes.ICONST_0) {
|
||||||
|
var next = insns.get(i + 1);
|
||||||
|
if (next && next.getOpcode() === Opcodes.IRETURN) {
|
||||||
|
// 插入调试日志和方法调用
|
||||||
|
insns.insertBefore(insn, ASMAPI.listOf(
|
||||||
|
new VarInsnNode(Opcodes.ALOAD, 1), // Mob
|
||||||
|
new VarInsnNode(Opcodes.ALOAD, 2), // Frustum
|
||||||
|
ASMAPI.buildMethodCall(
|
||||||
|
'your/package/LeashRenderHook',
|
||||||
|
null,
|
||||||
|
ASMAPI.MethodType.STATIC,
|
||||||
|
'shouldRenderExtraWithLog',
|
||||||
|
'(Lnet/minecraft/world/entity/Mob;Lnet/minecraft/client/renderer/culling/Frustum;)Z',
|
||||||
|
ASMAPI.MethodCallMode.STATIC
|
||||||
|
)
|
||||||
|
));
|
||||||
|
// 移除原来的 ICONST_0
|
||||||
|
insns.remove(insn);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return method;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
@ -13,12 +13,10 @@
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package top.r3944realms.superleadrope.util.coremods;
|
package top.r3944realms.superleadropetest;
|
||||||
|
|
||||||
import net.minecraftforge.api.distmarker.Dist;
|
import top.r3944realms.superleadrope.util.capability.LeashDataAPI;
|
||||||
import net.minecraftforge.api.distmarker.OnlyIn;
|
|
||||||
|
|
||||||
@OnlyIn(Dist.CLIENT)
|
public class Placeholder {
|
||||||
public class InvokerMethod {
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -0,0 +1,44 @@
|
||||||
|
/*
|
||||||
|
* 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 <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package top.r3944realms.superleadropetest.asm;
|
||||||
|
|
||||||
|
import org.objectweb.asm.*;
|
||||||
|
import org.objectweb.asm.tree.*;
|
||||||
|
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.nio.file.Paths;
|
||||||
|
import java.util.jar.JarEntry;
|
||||||
|
import java.util.jar.JarFile;
|
||||||
|
|
||||||
|
public class ASMTest {
|
||||||
|
public static void main(String[] args) throws Exception {
|
||||||
|
Path jarPath = Paths.get("G:\\WhimsyMod\\SuperLeadRope\\build\\moddev\\artifacts\\forge-1.20.1-47.3.4-merged.jar");
|
||||||
|
try (JarFile jar = new JarFile(jarPath.toFile())) {
|
||||||
|
JarEntry entry = jar.getJarEntry("net/minecraft/client/renderer/entity/MobRenderer.class");
|
||||||
|
try (InputStream in = jar.getInputStream(entry)) {
|
||||||
|
byte[] classBytes = in.readAllBytes();
|
||||||
|
|
||||||
|
ClassReader reader = new ClassReader(classBytes);
|
||||||
|
ClassNode classNode = new ClassNode();
|
||||||
|
reader.accept(classNode, 0);
|
||||||
|
|
||||||
|
System.out.println("Methods in MobRenderer:");
|
||||||
|
classNode.methods.forEach(m -> System.out.println(" - " + m.name + m.desc));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,100 @@
|
||||||
|
/*
|
||||||
|
* 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 <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package top.r3944realms.superleadropetest.asm;
|
||||||
|
|
||||||
|
import org.objectweb.asm.*;
|
||||||
|
import org.objectweb.asm.tree.*;
|
||||||
|
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
|
||||||
|
public class CoreModTransformerMemoryTest {
|
||||||
|
|
||||||
|
// 1️⃣ 静态类替代 Minecraft 类
|
||||||
|
public static class TestMob {}
|
||||||
|
public static class TestFrustum {}
|
||||||
|
|
||||||
|
// Hook 模拟
|
||||||
|
public static class LeashRenderHook {
|
||||||
|
public static boolean shouldRenderExtra(TestMob mob, TestFrustum frustum) {
|
||||||
|
System.out.println("[Hook] shouldRenderExtra called with Mob=" + mob + ", Frustum=" + frustum);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void main(String[] args) throws Exception {
|
||||||
|
// 2️⃣ 构造假的 shouldRender 方法
|
||||||
|
MethodNode methodNode = new MethodNode(Opcodes.ASM9, Opcodes.ACC_PUBLIC, "shouldRender",
|
||||||
|
"(Ltop/r3944realms/superleadrope/core/test/CoreModTransformerMemoryTest$TestMob;" +
|
||||||
|
"Ltop/r3944realms/superleadrope/core/test/CoreModTransformerMemoryTest$TestFrustum;)Z",
|
||||||
|
null, null);
|
||||||
|
|
||||||
|
InsnList insns = methodNode.instructions;
|
||||||
|
insns.clear(); // 清空原有指令
|
||||||
|
// 插入 Hook 调用
|
||||||
|
InsnList patch = new InsnList();
|
||||||
|
patch.add(new VarInsnNode(Opcodes.ALOAD, 1));
|
||||||
|
patch.add(new VarInsnNode(Opcodes.ALOAD, 2));
|
||||||
|
patch.add(new MethodInsnNode(
|
||||||
|
Opcodes.INVOKESTATIC,
|
||||||
|
"top/r3944realms/superleadrope/core/test/CoreModTransformerMemoryTest$LeashRenderHook",
|
||||||
|
"shouldRenderExtra",
|
||||||
|
"(Ltop/r3944realms/superleadrope/core/test/CoreModTransformerMemoryTest$TestMob;" +
|
||||||
|
"Ltop/r3944realms/superleadrope/core/test/CoreModTransformerMemoryTest$TestFrustum;)Z",
|
||||||
|
false
|
||||||
|
));
|
||||||
|
patch.add(new InsnNode(Opcodes.IRETURN));
|
||||||
|
insns.add(patch);
|
||||||
|
|
||||||
|
// 4️⃣ 创建假的 ClassNode
|
||||||
|
ClassNode classNode = new ClassNode();
|
||||||
|
classNode.version = Opcodes.V1_8;
|
||||||
|
classNode.access = Opcodes.ACC_PUBLIC;
|
||||||
|
classNode.name = "FakeMobRenderer";
|
||||||
|
classNode.superName = "java/lang/Object";
|
||||||
|
classNode.methods.add(methodNode);
|
||||||
|
|
||||||
|
// 添加默认构造器 <init>
|
||||||
|
MethodNode constructor = new MethodNode(Opcodes.ASM9, Opcodes.ACC_PUBLIC, "<init>", "()V", null, null);
|
||||||
|
InsnList initInsns = constructor.instructions;
|
||||||
|
initInsns.add(new VarInsnNode(Opcodes.ALOAD, 0)); // this
|
||||||
|
initInsns.add(new MethodInsnNode(Opcodes.INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false));
|
||||||
|
initInsns.add(new InsnNode(Opcodes.RETURN));
|
||||||
|
constructor.maxStack = 1;
|
||||||
|
constructor.maxLocals = 1;
|
||||||
|
classNode.methods.add(constructor);
|
||||||
|
|
||||||
|
// 5️⃣ 写入字节码
|
||||||
|
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS);
|
||||||
|
classNode.accept(cw);
|
||||||
|
byte[] bytes = cw.toByteArray();
|
||||||
|
|
||||||
|
// 6️⃣ 自定义 ClassLoader,直接返回 Class 对象
|
||||||
|
ClassLoader loader = new ClassLoader(CoreModTransformerMemoryTest.class.getClassLoader()) {
|
||||||
|
public Class<?> defineClassFromBytes(byte[] b) {
|
||||||
|
return defineClass("FakeMobRenderer", b, 0, b.length);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
Class<?> clazz = ((Class<?>) loader.getClass().getMethod("defineClassFromBytes", byte[].class).invoke(loader, bytes));
|
||||||
|
|
||||||
|
// 7️⃣ 实例化
|
||||||
|
Object rendererInstance = clazz.getDeclaredConstructor().newInstance();
|
||||||
|
Method shouldRender = clazz.getMethod("shouldRender", TestMob.class, TestFrustum.class);
|
||||||
|
|
||||||
|
boolean result = (boolean) shouldRender.invoke(rendererInstance, new TestMob(), new TestFrustum());
|
||||||
|
System.out.println("[Test] Result of shouldRender (after patch): " + result);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,132 @@
|
||||||
|
/*
|
||||||
|
* 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 <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package top.r3944realms.superleadropetest.config;
|
||||||
|
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
public class OffsetReadTest {
|
||||||
|
private static final Pattern OFFSET_PATTERN = Pattern.compile(
|
||||||
|
"(?i)(?:vec3|vec3d|vector3|offset)\\s*\\(\\s*([-+]?[0-9]*\\.?[0-9]+)\\s*,\\s*([-+]?[0-9]*\\.?[0-9]+)\\s*,\\s*([-+]?[0-9]*\\.?[0-9]+)\\s*\\)\\s*:\\s*\\[\\s*([^]]+?)\\s*]\\s*"
|
||||||
|
);
|
||||||
|
private static boolean isValidEntityRefFormat(String s) {
|
||||||
|
if (s.startsWith("#")) {
|
||||||
|
String body = s.substring(1);
|
||||||
|
// 支持 #modid (整个模组)或 #modid:tag_name (标签)
|
||||||
|
return body.matches("[a-z0-9_]+(:[a-z0-9_/]+)?");
|
||||||
|
}
|
||||||
|
// 普通实体 ID: modid:entity_id
|
||||||
|
return s.matches("[a-z0-9_]+:[a-z0-9_/]+");
|
||||||
|
}
|
||||||
|
private static boolean isValidOffsetRefFormat(String s) {
|
||||||
|
// 匹配格式: function_name(x,y,z) : [entity_list]
|
||||||
|
Matcher matcher = OFFSET_PATTERN.matcher(s);
|
||||||
|
if (!matcher.matches()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查坐标值是否有效
|
||||||
|
try {
|
||||||
|
// 组索引 现在坐标在组1、2、3,实体列表在组4
|
||||||
|
Double.parseDouble(matcher.group(1));
|
||||||
|
Double.parseDouble(matcher.group(2));
|
||||||
|
Double.parseDouble(matcher.group(3));
|
||||||
|
|
||||||
|
// 检查实体列表格式
|
||||||
|
String entityList = matcher.group(4);
|
||||||
|
String[] entities = entityList.split(",");
|
||||||
|
for (String entity : entities) {
|
||||||
|
if (!isValidEntityRefFormat(entity.trim())) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private static void testCase(String testString, boolean expected) {
|
||||||
|
boolean result = isValidOffsetRefFormat(testString);
|
||||||
|
String status = result == expected ? "✓ PASS" : "✗ FAIL";
|
||||||
|
System.out.printf("%s: %s -> %s (expected: %s)%n",
|
||||||
|
status, testString, result, expected);
|
||||||
|
|
||||||
|
if (result) {
|
||||||
|
Matcher matcher = OFFSET_PATTERN.matcher(testString);
|
||||||
|
if (matcher.matches()) {
|
||||||
|
System.out.printf(" 解析结果: X=%s, Y=%s, Z=%s, Entities=%s%n%n",
|
||||||
|
matcher.group(1), matcher.group(2), matcher.group(3), matcher.group(4));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
System.out.println();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
System.out.println("=== 有效测试用例 ===");
|
||||||
|
|
||||||
|
// 基本格式测试
|
||||||
|
testCase("vec3(0,0.2,0) : [minecraft:bee]", true);
|
||||||
|
testCase("Vec3(0, 0.2, 0) : [minecraft:bee]", true);
|
||||||
|
testCase("VEC3(0,0.2,0) : [minecraft:bee]", true);
|
||||||
|
|
||||||
|
// 不同函数名测试
|
||||||
|
testCase("vec3d(0,0.2,0) : [minecraft:bee]", true);
|
||||||
|
testCase("Vector3(0,0.2,0) : [minecraft:bee]", true);
|
||||||
|
testCase("offset(0,0.2,0) : [minecraft:bee]", true);
|
||||||
|
|
||||||
|
// 空格兼容测试
|
||||||
|
testCase("vec3( 0 , 0.2 , 0 ) : [ minecraft:bee ]", true);
|
||||||
|
testCase("vec3(0,0.2,0) : [minecraft:bee] ", true);
|
||||||
|
testCase("vec3(0, 0.2, 0) : [ minecraft:bee ] ", true);
|
||||||
|
|
||||||
|
// 多实体测试
|
||||||
|
testCase("vec3(0,1.0,0) : [minecraft:horse, minecraft:donkey]", true);
|
||||||
|
testCase("vec3(0,0.5,0) : [#minecraft:boats, #minecraft:minecarts]", true);
|
||||||
|
|
||||||
|
// 标签和模组测试
|
||||||
|
testCase("vec3(0,0.5,0) : [#minecraft:boats]", true);
|
||||||
|
testCase("vec3(0,0.3,0) : [#minecraft]", true);
|
||||||
|
|
||||||
|
// 负数和小数测试
|
||||||
|
testCase("vec3(-1, 1.5, 2.8) : [minecraft:horse]", true);
|
||||||
|
testCase("vec3(0.0, -0.5, 1.23) : [minecraft:bee]", true);
|
||||||
|
|
||||||
|
System.out.println("=== 无效测试用例 ===");
|
||||||
|
|
||||||
|
// 错误函数名
|
||||||
|
testCase("vector(0,0.2,0) : [minecraft:bee]", false);
|
||||||
|
testCase("pos(0,0.2,0) : [minecraft:bee]", false);
|
||||||
|
|
||||||
|
// 格式错误
|
||||||
|
testCase("vec3(0,0.2,0) : minecraft:bee]", false); // 缺少左括号
|
||||||
|
testCase("vec3(0,0.2,0) : [minecraft:bee", false); // 缺少右括号
|
||||||
|
testCase("vec3(0,0.2) : [minecraft:bee]", false); // 缺少Z坐标
|
||||||
|
|
||||||
|
// 无效坐标
|
||||||
|
testCase("vec3(a,b,c) : [minecraft:bee]", false);
|
||||||
|
testCase("vec3(0,0.2,invalid) : [minecraft:bee]", false);
|
||||||
|
|
||||||
|
// 无效实体引用
|
||||||
|
testCase("vec3(0,0.2,0) : [invalid_entity]", false);
|
||||||
|
testCase("vec3(0,0.2,0) : [minecraft:bee, invalid]", false);
|
||||||
|
|
||||||
|
// 缺少冒号
|
||||||
|
testCase("vec3(0,0.2,0) [minecraft:bee]", false);
|
||||||
|
|
||||||
|
System.out.println("=== 测试完成 ===");
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user