2024-09-04

版本0.0.2.2
增加:
1. 增加了新的指令/lp leash data系列
2. 增加了新的游戏规则LP.CreateLeashFenceKnotEntityIfAbsent
3.加入了本模組的logo
修改与调整:
1.调整了原语言部分可能存在误解的翻译
2.调整让非玩家实体牵引玩家实体时第一和第二三人称栓绳能被渲染
3.簡化了指令名
4.將get指令權限放開,set指令需2級及以上的權限執行
具體新内容請見 README.md 介紹
This commit is contained in:
叁玖领域 2024-09-04 15:50:10 +08:00
parent beec615046
commit 3da99bc6f3
19 changed files with 446 additions and 56 deletions

View File

@ -1,3 +1,4 @@
# 版本 0.0.2.2 【注意:本解釋簡繁混寫,因爲趕時間,所以並不怎麽規範,請諒解】
## 简介
现在开始你可以用拴绳栓住玩家,也可以栓住自己了,不如尝试栓住彼此来通关我的世界吧(
@ -11,10 +12,16 @@
## 指令
* `/lp leash length [<玩家>] setLength <长度> ` - 设置该玩家的拴绳长度 [ 如果<玩家>为空则代表执行对象是自己 ,<长度> 为在 5 ~ 1024之间的浮点数 ]
* `/lp leash length [<玩家>] set <长度> ` - 设置该玩家的拴绳长度 [ 如果<玩家>为空则代表执行对象是自己 ,<长度> 为在 5 ~ 1024之间的浮点数 ]
* `/lp leash length [<玩家>] [getLength]` - 获取该玩家的拴绳长度 [ 如果<玩家>为空则代表执行对象是自己 , [getLength] 可不写]
* `/lp leash length [<玩家>] [get]` - 顯示该玩家的拴绳长度 [ 如果<玩家>为空则代表执行对象是自己 , [get] 可不写]
* `/lp leash data [<玩家>] set <實體>/<方塊坐標>` - 設置該玩家的的拴繩數據實體設置為<實體>/位於<方塊坐標>處的柵欄上的拴繩結實體
* `/lp leash data [<玩家>] [get]` - 顯示该玩家的拴绳數據 [ 如果<玩家>为空则代表执行对象是自己 , [get] 可不写]
## 游戏规则
* `LP.TeleportWithLeashedPlayers` - 来决定被栓玩家是否在传送时能随拴绳持有者一起传送 [默认值: True]
* `LP.TeleportWithLeashedPlayers` - 此規則啓用后, 被栓玩家將會随玩家拴绳持有者一起传送 [默认值: True]
* `LP.CreateLeashFenceKnotEntityIfAbsent` - 此規則啓用后, 在設置 leashData 時,如果對應方塊坐標上的柵欄沒有拴繩結則會自動創建這個實體並綁定 [默认值: True]

View File

@ -32,7 +32,7 @@ mod_name=Leashed Player
# The license of the mod. Review your options at https://choosealicense.com/. All Rights Reserved is the default.
mod_license=All Rights Reserved
# The mod version. See https://semver.org/
mod_version=0.0.1
mod_version=0.0.2.2
# The group ID for the mod. It is only important when publishing as an artifact to a Maven repository.
# This should match the base package used for the mod sources.
# See https://maven.apache.org/guides/mini/guide-naming-conventions.html

View File

@ -1,2 +1,2 @@
// 1.21 2024-09-03T17:46:39.9837977 Languages: en_us for mod: leashedplayer
d770cde6d74b269e8a0447edeb0a65668dda07d3 assets/leashedplayer/lang/en_us.json
// 1.21 2024-09-04T14:54:14.4635752 Languages: en_us for mod: leashedplayer
3e231f3b588ab61d2990c49bf1f1deca5593dbbf assets/leashedplayer/lang/en_us.json

View File

@ -1,2 +1,2 @@
// 1.21 2024-09-03T17:46:39.9827933 Languages: zh_cn for mod: leashedplayer
eb4dc9fdfece3e001d0f32abcad5de6b926b0a4d assets/leashedplayer/lang/zh_cn.json
// 1.21 2024-09-04T14:54:14.4585471 Languages: zh_cn for mod: leashedplayer
6db8ccbbd7d3bf13e819c2ae1762f37e2c332043 assets/leashedplayer/lang/zh_cn.json

View File

@ -1,2 +1,2 @@
// 1.21 2024-09-03T17:46:39.9817953 Languages: zh_tw for mod: leashedplayer
0f4c1499f9241f8d8c33033dbcad397c4f3df5fa assets/leashedplayer/lang/zh_tw.json
// 1.21 2024-09-04T14:54:14.4535174 Languages: zh_tw for mod: leashedplayer
86fa3a80c53a50ad5af47129384e506cddda79aa assets/leashedplayer/lang/zh_tw.json

View File

@ -1,7 +1,16 @@
{
"gamerule.RWN.TeleportWithLeashedPlayers": "Teleport with leashed player",
"gamerule.RWN.TeleportWithLeashedPlayers.description": "You will teleport with your leashed players ",
"leashedplayer.command.leash.message.leash.length.fail": "Failed (Internal Error)",
"leashedplayer.command.leash.message.leash.length.set": "The Leash length of %s is set to %f blocks",
"leashedplayer.command.leash.message.leash.length.show": "The Leash Length of %s is %f blocks"
"gamerule.LP.CreateLeashFenceKnotEntityIfAbsent": "Create Leash Fence Knot Entity if absent",
"gamerule.LP.CreateLeashFenceKnotEntityIfAbsent.description": "Create LeashKnot Entity if it's absent on fence",
"gamerule.LP.TeleportWithLeashedPlayers": "Teleport leashed player with player holder",
"gamerule.LP.TeleportWithLeashedPlayers.description": "Holder will teleport with their leashed players ",
"leashedplayer.command.leash.message.leash.data.null": "%1$s has no LeashDataEntity",
"leashedplayer.command.leash.message.leash.data.show": "%1$s's LeashDataEntity is %2$s",
"leashedplayer.command.leash.message.leash.leash_data.set": "%1$s LeashDataEntity now is set as %2$s",
"leashedplayer.command.leash.message.leash.leash_data.set.failed.diff_level": "%1$s and %2$s are not at a same level",
"leashedplayer.command.leash.message.leash.leash_data.set.failed.forbid_same_entity": "Prohibit setting the same entity",
"leashedplayer.command.leash.message.leash.leash_data.set.failed.no_knot_exist_in_that_pos": "No knot found at (X:%f,Y:%f,Z:%f)",
"leashedplayer.command.leash.message.leash.leash_data.set.failed.too_far": "The distance between %1$s and %2$s is larger than the 1.2 times of LeashLength, LeashLength is %3$s blocks",
"leashedplayer.command.leash.message.leash.length.failed": "Failed (Internal Error, maybe your command is incorrect)",
"leashedplayer.command.leash.message.leash.length.set": "The Leash length of %1$s is set to %2$s blocks",
"leashedplayer.command.leash.message.leash.length.show": "The Leash Length of %1$s is %2$s blocks"
}

View File

@ -1,7 +1,16 @@
{
"gamerule.RWN.TeleportWithLeashedPlayers": "传送被栓玩家",
"gamerule.RWN.TeleportWithLeashedPlayers.description": "传送时将被栓玩家与自己一起传送",
"leashedplayer.command.leash.message.leash.length.fail": "失败(内部错误)",
"leashedplayer.command.leash.message.leash.length.set": "%s的拴绳长度被设置为%f格",
"leashedplayer.command.leash.message.leash.length.show": "%s的拴绳长度为%f格"
"gamerule.LP.CreateLeashFenceKnotEntityIfAbsent": "如果缺失则创建栓绳结",
"gamerule.LP.CreateLeashFenceKnotEntityIfAbsent.description": "如果在栅栏处缺失栓绳结,则创建它",
"gamerule.LP.TeleportWithLeashedPlayers": "被栓玩家随玩家持有者传送",
"gamerule.LP.TeleportWithLeashedPlayers.description": "传送时将被栓玩家与持有者一起传送",
"leashedplayer.command.leash.message.leash.data.null": "%1$s沒有拴绳数据实体",
"leashedplayer.command.leash.message.leash.data.show": "%1$s的拴绳数据实体为%2$s",
"leashedplayer.command.leash.message.leash.leash_data.set": "%1$s拴绳数据实体被设置为%2$s",
"leashedplayer.command.leash.message.leash.leash_data.set.failed.diff_level": "%1$s和%2$s不在同一维度上",
"leashedplayer.command.leash.message.leash.leash_data.set.failed.forbid_same_entity": "禁止设置同一实体",
"leashedplayer.command.leash.message.leash.leash_data.set.failed.no_knot_exist_in_that_pos": "未找到栓绳结在(X:%f, Y:%f, Z:%f)处",
"leashedplayer.command.leash.message.leash.leash_data.set.failed.too_far": "%1$s到%2$s的距离超过了1.2倍 栓绳长度,原长:%3$s 格",
"leashedplayer.command.leash.message.leash.length.failed": "失败(内部错误,可能是你输的指令有误)",
"leashedplayer.command.leash.message.leash.length.set": "%1$s的拴绳长度被设置为%2$s格",
"leashedplayer.command.leash.message.leash.length.show": "%1$s的拴绳长度为%2$s格"
}

View File

@ -1,7 +1,16 @@
{
"gamerule.RWN.TeleportWithLeashedPlayers": "傳送被栓玩家",
"gamerule.RWN.TeleportWithLeashedPlayers.description": "傳送時將被栓玩家與隨自己一起傳送",
"leashedplayer.command.leash.message.leash.length.fail": "失敗(内部錯誤)",
"leashedplayer.command.leash.message.leash.length.set": "%s的栓繩長度被設置為%f格",
"leashedplayer.command.leash.message.leash.length.show": "%s的栓繩長度為%f格"
"gamerule.LP.CreateLeashFenceKnotEntityIfAbsent": "如果缺失則創建栓繩結",
"gamerule.LP.CreateLeashFenceKnotEntityIfAbsent.description": "如果在柵欄処缺失拴繩結,則創建它",
"gamerule.LP.TeleportWithLeashedPlayers": "被栓玩家随玩家持有者傳送",
"gamerule.LP.TeleportWithLeashedPlayers.description": "將被栓玩家將隨持有者一起傳送",
"leashedplayer.command.leash.message.leash.data.null": "%1$s沒有栓繩數據實體",
"leashedplayer.command.leash.message.leash.data.show": "%1$s栓繩數據實體為%2$s",
"leashedplayer.command.leash.message.leash.leash_data.set": "%1$s栓繩數據實體設置為%2$s",
"leashedplayer.command.leash.message.leash.leash_data.set.failed.diff_level": "%1$s和%2$s不在同一緯度上",
"leashedplayer.command.leash.message.leash.leash_data.set.failed.forbid_same_entity": "禁止設置同一實體",
"leashedplayer.command.leash.message.leash.leash_data.set.failed.no_knot_exist_in_that_pos": "未找到栓繩結在(X:%f, Y:%f, Z:%f)処",
"leashedplayer.command.leash.message.leash.leash_data.set.failed.too_far": "%1$s到%2$s的距離超過了1.2倍栓繩長度,原長:%3$s 格",
"leashedplayer.command.leash.message.leash.length.failed": "失敗(内部錯誤,,可能你輸入的指令有誤)",
"leashedplayer.command.leash.message.leash.length.set": "%1$s的栓繩長度被設置為%2$s格",
"leashedplayer.command.leash.message.leash.length.show": "%1$s的栓繩長度為%2$s格"
}

View File

@ -1,7 +1,7 @@
package com.r3944realms.leashedplayer;
import net.neoforged.fml.common.Mod;
//TODO: 13:40
@Mod(LeashedPlayer.MOD_ID)
public class LeashedPlayer {
public static final String MOD_ID = "leashedplayer";

View File

@ -5,19 +5,40 @@ import com.mojang.brigadier.CommandDispatcher;
import com.mojang.brigadier.arguments.FloatArgumentType;
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
import com.mojang.brigadier.context.CommandContext;
import com.mojang.brigadier.exceptions.CommandSyntaxException;
import com.r3944realms.leashedplayer.content.gamerules.GameruleRegistry;
import com.r3944realms.leashedplayer.content.gamerules.Server.CreateLeashFenceKnotEntityIfAbsent;
import com.r3944realms.leashedplayer.modInterface.ILivingEntityExtension;
import com.r3944realms.leashedplayer.modInterface.PlayerLeashable;
import net.minecraft.commands.CommandSourceStack;
import net.minecraft.commands.Commands;
import net.minecraft.commands.arguments.EntityArgument;
import net.minecraft.commands.arguments.coordinates.BlockPosArgument;
import net.minecraft.core.BlockPos;
import net.minecraft.network.chat.Component;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.tags.BlockTags;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.Leashable;
import net.minecraft.world.entity.decoration.LeashFenceKnotEntity;
import org.jetbrains.annotations.Nullable;
public class LeashCommand {
private final static String LEASHEDPLAYER_LEASH_MESSAGE_ = "leashedplayer.command.leash.message.";
public final static String LEASH_LENGTH_SHOW = LEASHEDPLAYER_LEASH_MESSAGE_ + "leash.length.show",
LEASH_LENGTH_FAIL = LEASHEDPLAYER_LEASH_MESSAGE_ + "leash.length.fail",
LEASH_LENGTH_SET = LEASHEDPLAYER_LEASH_MESSAGE_ + "leash.length.set";
public final static String LEASH_FAILED = LEASHEDPLAYER_LEASH_MESSAGE_ + "leash.length.failed",
LEASH_LENGTH_SHOW = LEASHEDPLAYER_LEASH_MESSAGE_ + "leash.length.show",
LEASH_LENGTH_SET = LEASHEDPLAYER_LEASH_MESSAGE_ + "leash.length.set",
NO_LEASH_DATA = LEASHEDPLAYER_LEASH_MESSAGE_ + "leash.data.null",
LEASH_DATA_SHOW = LEASHEDPLAYER_LEASH_MESSAGE_ + "leash.data.show",
LEASH_DATA_SET = LEASHEDPLAYER_LEASH_MESSAGE_ + "leash.leash_data.set",
LEASH_DATA_SET_FAILED_DIFF_LEVEL = LEASH_DATA_SET + ".failed.diff_level",
LEASH_DATA_SET_FAILED_TOO_FAR = LEASH_DATA_SET + ".failed.too_far",
LEASH_DATA_SET_FAILED_NO_KNOT_EXISTED_IN_THAT_POS = LEASH_DATA_SET + ".failed.no_knot_exist_in_that_pos",
LEASH_DATA_SET_FAILED_FORBID_SAME_ENTITY = LEASH_DATA_SET + ".failed.forbid_same_entity";
public static void register(CommandDispatcher<CommandSourceStack> dispatcher) {
LiteralArgumentBuilder<CommandSourceStack> literalArgumentBuilder = Commands.literal(com.r3944realms.leashedplayer.content.commands.Command.PREFIX);
Command<CommandSourceStack> getSelfLeashLength = context -> {
@ -25,9 +46,9 @@ public class LeashCommand {
try {
ServerPlayer player = source.getPlayerOrException();
float leashLength = ((ILivingEntityExtension)player).getLeashLength();
source.sendSuccess(() -> Component.translatable(LEASH_LENGTH_SHOW, player.getName(), leashLength), true);
source.sendSuccess(() -> Component.translatable(LEASH_LENGTH_SHOW, player.getDisplayName(), leashLength), true);
} catch (Exception e) {
source.sendFailure(Component.translatable(LEASH_LENGTH_FAIL));
source.sendFailure(Component.translatable(LEASH_FAILED));
return -1;
}
return 0;
@ -37,9 +58,9 @@ public class LeashCommand {
try {
ServerPlayer player = EntityArgument.getPlayer(context, "player");
float leashLength = ((ILivingEntityExtension)player).getLeashLength();
source.sendSuccess(() -> Component.translatable(LEASH_LENGTH_SHOW, player.getName(), leashLength), true);
source.sendSuccess(() -> Component.translatable(LEASH_LENGTH_SHOW, player.getDisplayName(), leashLength), true);
} catch (Exception e) {
source.sendFailure(Component.translatable(LEASH_LENGTH_FAIL));
source.sendFailure(Component.translatable(LEASH_FAILED));
return -1;
}
return 0;
@ -50,9 +71,9 @@ public class LeashCommand {
ServerPlayer player = source.getPlayerOrException();
float leashLength = context.getArgument("leashLength", Float.class);
((ILivingEntityExtension)player).setLeashLength(leashLength);
source.sendSuccess(() -> Component.translatable(LEASH_LENGTH_SET, player.getName(), leashLength), true);
source.sendSuccess(() -> Component.translatable(LEASH_LENGTH_SET, player.getDisplayName(), leashLength), true);
} catch (Exception e) {
source.sendFailure(Component.translatable(LEASH_LENGTH_FAIL));
source.sendFailure(Component.translatable(LEASH_FAILED));
return -1;
}
return 0;
@ -60,22 +81,102 @@ public class LeashCommand {
Command<CommandSourceStack> setLengthLeashLength = context -> {
CommandSourceStack source = context.getSource();
try {
// Player player = context.getArgument("player", Player.class);
ServerPlayer player = EntityArgument.getPlayer(context, "player");
float leashLength = context.getArgument("leashLength", Float.class);
((ILivingEntityExtension)player).setLeashLength(leashLength);
source.sendSuccess(() -> Component.translatable(LEASH_LENGTH_SET, player.getName(), leashLength), true);
source.sendSuccess(() -> Component.translatable(LEASH_LENGTH_SET, player.getDisplayName(), leashLength), true);
} catch (Exception e) {
source.sendFailure(Component.translatable(LEASH_LENGTH_FAIL));
source.sendFailure(Component.translatable(LEASH_FAILED));
return -1;
}
return 0;
};
LiteralArgumentBuilder<CommandSourceStack> $$leashRoot = Commands.literal("leash").requires(cs -> cs.hasPermission(2));
//获取Data 构造一个MutableComponent显示数据
Command<CommandSourceStack> geSelfLeashData = context -> {
CommandSourceStack source = context.getSource();
try {
ServerPlayer player = source.getPlayerOrException();
Integer x = LeashLengthResultInt(player, source);
if (x != null) return x;
} catch (Exception e) {
source.sendFailure(Component.translatable(LEASH_FAILED));
return -1;
}
return 0;
};
Command<CommandSourceStack> getRefPlayerLeashData = context -> {
CommandSourceStack source = context.getSource();
try {
ServerPlayer player = EntityArgument.getPlayer(context, "targetPlayer");
Integer x = LeashLengthResultInt(player, source);
if (x != null) return x;
} catch (Exception e) {
source.sendFailure(Component.translatable(LEASH_FAILED));
return -1;
}
return 0;
};
//设置前要判断其实体距离同一维度且距离不得大于其绳长的1.2倍待定也许可以设置在配置文件里
Command<CommandSourceStack> setSelfLeashDataEntity = context -> {
CommandSourceStack source = context.getSource();
try {
ServerPlayer player = source.getPlayerOrException();
Integer x = LeashDataEntityResultInt(context, player, source);
if (x != null) return x;
} catch (Exception e) {
source.sendFailure(Component.translatable(LEASH_FAILED));
return -1;
}
return 0;
};
Command<CommandSourceStack> setSelfLeashDataByBlockPos = context -> {
CommandSourceStack source = context.getSource();
try {
ServerPlayer player = source.getPlayerOrException();
Integer x = LeashDataBlockPosResultInt(context, source, player);
if (x != null) return x;
} catch (Exception e) {
source.sendFailure(Component.translatable(LEASH_FAILED));
return -1;
}
return 0;
};
Command<CommandSourceStack> setRefPlayerLeashDataEntity = context -> {
CommandSourceStack source = context.getSource();
try {
ServerPlayer player = EntityArgument.getPlayer(context, "targetPlayer");
Integer x = LeashDataEntityResultInt(context, player, source);
if (x != null) return x;
} catch (Exception e) {
source.sendFailure(Component.translatable(LEASH_FAILED));
return -1;
}
return 0;
};
Command<CommandSourceStack> setRefPlayerLeashDataByBlockPos = context -> {
CommandSourceStack source = context.getSource();
try {
ServerPlayer player = EntityArgument.getPlayer(context, "targetPlayer");
Integer x = LeashDataBlockPosResultInt(context, source, player);
if (x != null) return x;
} catch (Exception e) {
source.sendFailure(Component.translatable(LEASH_FAILED));
return -1;
}
return 0;
};
LiteralArgumentBuilder<CommandSourceStack> $$leashRoot = Commands.literal("leash");
literalArgumentBuilder.then(
$$leashRoot.then(Commands.literal("length").executes(getSelfLeashLength)
.then(Commands.literal("getLength").executes(getSelfLeashLength))
.then(Commands.literal("setLength")
.then(Commands.literal("get").executes(getSelfLeashLength))
.then(Commands.literal("set").requires(cs -> cs.hasPermission(2))
.then(Commands.argument("leashLength", FloatArgumentType.floatArg(5, 1024)).executes(setSelfLengthLeashLength)
)
)
@ -85,8 +186,8 @@ public class LeashCommand {
$$leashRoot.then(
Commands.literal("length")
.then(Commands.argument("player", EntityArgument.player()).executes(getRefPlayerLeashLength)
.then(Commands.literal("getLength").executes(getRefPlayerLeashLength))
.then(Commands.literal("setLength")
.then(Commands.literal("get").executes(getRefPlayerLeashLength))
.then(Commands.literal("set").requires(cs -> cs.hasPermission(2))
.then(
Commands.argument("leashLength", FloatArgumentType.floatArg(5, 1024)).executes(setLengthLeashLength)
)
@ -95,7 +196,106 @@ public class LeashCommand {
)
);
literalArgumentBuilder.then(
$$leashRoot.then(
Commands.literal("data")
.then(Commands.argument("targetPlayer", EntityArgument.player()).executes(getRefPlayerLeashData)
.then(Commands.literal("get")
.executes(getRefPlayerLeashData)
)
.then(Commands.literal("set").requires(cs -> cs.hasPermission(2))
.then(Commands.argument("holderEntity", EntityArgument.entity())
.executes(setRefPlayerLeashDataEntity)
)
.then(Commands.argument("BlockPos", BlockPosArgument.blockPos())
.executes(setRefPlayerLeashDataByBlockPos)
)
)
)
)
);
literalArgumentBuilder.then(
$$leashRoot.then(
Commands.literal("data")
.then(Commands.literal("get")
.executes(geSelfLeashData)
)
.then(Commands.literal("set").requires(cs -> cs.hasPermission(2))
.then(Commands.argument("holderEntity", EntityArgument.entity())
.executes(setSelfLeashDataEntity)
)
.then(Commands.argument("BlockPos", BlockPosArgument.blockPos())
.executes(setSelfLeashDataByBlockPos)
)
)
)
);
dispatcher.register(literalArgumentBuilder);
}
private static @Nullable Integer LeashLengthResultInt(ServerPlayer player, CommandSourceStack source) throws Exception {
Leashable.LeashData leashDataFromEntityData = ((PlayerLeashable) player).getLeashDataFromEntityData();
if(leashDataFromEntityData == null) {
source.sendSuccess(() -> Component.translatable(NO_LEASH_DATA, player.getDisplayName()), true);
return 1;
} else {
Entity leashDataEntity = PlayerLeashable.getLeashDataEntityOrThrown(leashDataFromEntityData, source.getLevel());
source.sendSuccess(() -> Component.translatable(LEASH_DATA_SHOW, player.getDisplayName(), leashDataEntity.getDisplayName()), true);
}
return null;
}
private static @Nullable Integer LeashDataBlockPosResultInt(CommandContext<CommandSourceStack> context, CommandSourceStack source, ServerPlayer player) {
BlockPos blockPos = BlockPosArgument.getBlockPos(context, "BlockPos");
Entity leashDataEntity = PlayerLeashable.getLeashFenceKnotEntity(source.getLevel(), blockPos);
PlayerLeashable leashedPlayer = (PlayerLeashable) player;
Component targetPlayerDisplayName = player.getDisplayName();
if(leashDataEntity == null) {
ServerLevel level = context.getSource().getLevel();
if(GameruleRegistry.getGameruleBoolValue(level,CreateLeashFenceKnotEntityIfAbsent.ID)) {
if(level.getBlockState(blockPos).is(BlockTags.FENCES)) {
Entity leashKnotFence = PlayerLeashable.createLeashKnotFence(level, blockPos);
leashedPlayer.setLeashedTo(leashKnotFence, true);
source.sendSuccess(() -> Component.translatable(LEASH_DATA_SET, targetPlayerDisplayName, leashKnotFence.getDisplayName()), true);
return null;
}
}
source.sendFailure(Component.translatable(LEASH_DATA_SET_FAILED_NO_KNOT_EXISTED_IN_THAT_POS, blockPos.getX(), blockPos.getY(), blockPos.getZ()));
return 1;
}
Component leashDataEntityDisplayName = leashDataEntity.getDisplayName();
return LeashDataCommonPartResultInt(source, player, leashDataEntity, leashedPlayer, targetPlayerDisplayName, leashDataEntityDisplayName);
}
private static @Nullable Integer LeashDataEntityResultInt(CommandContext<CommandSourceStack> context, ServerPlayer player, CommandSourceStack source) throws CommandSyntaxException {
Entity leashDataEntity = EntityArgument.getEntity(context, "holderEntity");
PlayerLeashable leashedPlayer = (PlayerLeashable) player;
Component targetPlayerDisplayName = player.getDisplayName();
Component leashDataEntityDisplayName = leashDataEntity.getDisplayName();
if(player.equals(leashDataEntity)) {
source.sendFailure(Component.translatable(LEASH_DATA_SET_FAILED_FORBID_SAME_ENTITY));
return 1;
}
return LeashDataCommonPartResultInt(source, player, leashDataEntity, leashedPlayer, targetPlayerDisplayName, leashDataEntityDisplayName);
}
private static @Nullable Integer LeashDataCommonPartResultInt(CommandSourceStack source, ServerPlayer player, Entity leashDataEntity, PlayerLeashable leashedPlayer, Component targetPlayerDisplayName, Component leashDataEntityDisplayName) {
if(player.level() != leashDataEntity.level()) {
source.sendFailure(Component.translatable(LEASH_DATA_SET_FAILED_DIFF_LEVEL, targetPlayerDisplayName, leashDataEntityDisplayName));
return 2;
}
ILivingEntityExtension targetPlayerExtension = (ILivingEntityExtension) player;
if (player.distanceTo(leashDataEntity) > targetPlayerExtension.getLeashLength() * 1.2f) {
source.sendFailure(Component.translatable(LEASH_DATA_SET_FAILED_TOO_FAR, targetPlayerDisplayName, leashDataEntityDisplayName, targetPlayerExtension.getLeashLength()));
return 3;
}
leashedPlayer.setLeashedTo(leashDataEntity, true);
source.sendSuccess(() -> Component.translatable(LEASH_DATA_SET, targetPlayerDisplayName, leashDataEntityDisplayName), true);
return null;
}
}

View File

@ -0,0 +1,25 @@
package com.r3944realms.leashedplayer.content.gamerules.Server;
import com.r3944realms.leashedplayer.LeashedPlayer;
import com.r3944realms.leashedplayer.content.gamerules.Gamerules;
import com.r3944realms.leashedplayer.utils.Util;
import net.minecraft.world.level.GameRules;
import net.neoforged.bus.api.SubscribeEvent;
import net.neoforged.fml.common.EventBusSubscriber;
import net.neoforged.fml.event.lifecycle.FMLCommonSetupEvent;
import static com.r3944realms.leashedplayer.content.gamerules.Gamerules.GAMERULE_REGISTRY;
@EventBusSubscriber(modid = LeashedPlayer.MOD_ID, bus = EventBusSubscriber.Bus.MOD)
public class CreateLeashFenceKnotEntityIfAbsent {
public static final boolean DEFAULT_VALUE = true;
public static final String ID = Util.getGameruleName(CreateLeashFenceKnotEntityIfAbsent.class);
public static final String DESCRIPTION_KEY = Gamerules.getDescriptionKey(CreateLeashFenceKnotEntityIfAbsent.class);
public static final String NAME_KEY = Gamerules.getNameKey(CreateLeashFenceKnotEntityIfAbsent.class);
public static final GameRules.Category CATEGORY = GameRules.Category.PLAYER;
@SubscribeEvent
public static void onCommonSetup(final FMLCommonSetupEvent event) {
GAMERULE_REGISTRY.registerGamerule(ID, CATEGORY, DEFAULT_VALUE);
}
}

View File

@ -1,6 +1,7 @@
package com.r3944realms.leashedplayer.datagen.LanguageAndOtherData;
import com.r3944realms.leashedplayer.content.commands.LeashCommand;
import com.r3944realms.leashedplayer.content.gamerules.Server.CreateLeashFenceKnotEntityIfAbsent;
import com.r3944realms.leashedplayer.content.gamerules.Server.TeleportWithLeashedPlayers;
import com.r3944realms.leashedplayer.utils.Enum.LanguageEnum;
import com.r3944realms.leashedplayer.utils.Enum.ModPartEnum;
@ -13,13 +14,23 @@ import java.util.function.Supplier;
public enum ModLangKeyValue {
//COMMAND_MESSAGE
MESSAGE_LEASH_LENGTH_FAIL(LeashCommand.LEASH_LENGTH_FAIL, ModPartEnum.COMMAND, "Failed (Internal Error)", "失败(内部错误)", "失敗(内部錯誤)", false),
MESSAGE_LEASH_LENGTH_SHOW(LeashCommand.LEASH_LENGTH_SHOW, ModPartEnum.COMMAND, "The Leash Length of %s is %f blocks", "%s的拴绳长度为%f格", "%s的栓繩長度為%f格" , false),
MESSAGE_LEASH_LENGTH_SET(LeashCommand.LEASH_LENGTH_SET, ModPartEnum.COMMAND, "The Leash length of %s is set to %f blocks", "%s的拴绳长度被设置为%f格", "%s的栓繩長度被設置為%f格" , false),
MESSAGE_LEASH_LENGTH_FAILED(LeashCommand.LEASH_FAILED, ModPartEnum.COMMAND, "Failed (Internal Error, maybe your command is incorrect)", "失败(内部错误,可能是你输的指令有误)", "失敗(内部錯誤,,可能你輸入的指令有誤)", false),
MESSAGE_LEASH_LENGTH_SHOW(LeashCommand.LEASH_LENGTH_SHOW, ModPartEnum.COMMAND, "The Leash Length of %1$s is %2$s blocks", "%1$s的拴绳长度为%2$s格", "%1$s的栓繩長度為%2$s格" , false),
MESSAGE_LEASH_LENGTH_SET(LeashCommand.LEASH_LENGTH_SET, ModPartEnum.COMMAND, "The Leash length of %1$s is set to %2$s blocks", "%1$s的拴绳长度被设置为%2$s格", "%1$s的栓繩長度被設置為%2$s格" , false),
MESSAGE_LEASH_GET_LEASH_DATA(LeashCommand.LEASH_DATA_SHOW, ModPartEnum.COMMAND, "%1$s's LeashDataEntity is %2$s", "%1$s的拴绳数据实体为%2$s", "%1$s栓繩數據實體為%2$s",false),
MESSAGE_LEASH_NO_LEASH_DATA(LeashCommand.NO_LEASH_DATA, ModPartEnum.COMMAND, "%1$s has no LeashDataEntity", "%1$s沒有拴绳数据实体", "%1$s沒有栓繩數據實體", false),
MESSAGE_LEASH_SET_LEASH_DATA(LeashCommand.LEASH_DATA_SET, ModPartEnum.COMMAND, "%1$s LeashDataEntity now is set as %2$s", "%1$s拴绳数据实体被设置为%2$s", "%1$s栓繩數據實體設置為%2$s", false),
MESSAGE_LEASH_SET_FAILED_DIFF_LEVEL(LeashCommand.LEASH_DATA_SET_FAILED_DIFF_LEVEL, ModPartEnum.COMMAND,"%1$s and %2$s are not at a same level", "%1$s和%2$s不在同一维度上", "%1$s和%2$s不在同一緯度上", false),
MESSAGE_LEASH_SET_FAILED_TOO_FAR(LeashCommand.LEASH_DATA_SET_FAILED_TOO_FAR, ModPartEnum.COMMAND,"The distance between %1$s and %2$s is larger than the 1.2 times of LeashLength, LeashLength is %3$s blocks", "%1$s到%2$s的距离超过了1.2倍 栓绳长度,原长:%3$s 格", "%1$s到%2$s的距離超過了1.2倍栓繩長度,原長:%3$s 格", false),
MESSAGE_LEASH_SET_FAILED_NO_KNOT_EXIST_IN_THAT_POS(LeashCommand.LEASH_DATA_SET_FAILED_NO_KNOT_EXISTED_IN_THAT_POS, ModPartEnum.COMMAND, "No knot found at (X:%f,Y:%f,Z:%f)", "未找到栓绳结在(X:%f, Y:%f, Z:%f)处", "未找到栓繩結在(X:%f, Y:%f, Z:%f)処", false),
MESSAGE_LEASH_SET_FAILED_FORBID_SAME_ENTITY(LeashCommand.LEASH_DATA_SET_FAILED_FORBID_SAME_ENTITY, ModPartEnum.COMMAND, "Prohibit setting the same entity","禁止设置同一实体", "禁止設置同一實體", false),
//GAME_RULE_NAME
TELEPORT_WITH_LEASHED_PLAYERS(TeleportWithLeashedPlayers.NAME_KEY, ModPartEnum.NAME, "Teleport with leashed player", "传送被栓玩家", "傳送被栓玩家" ,false),
TELEPORT_WITH_LEASHED_PLAYERS(TeleportWithLeashedPlayers.NAME_KEY, ModPartEnum.NAME, "Teleport leashed player with player holder", "被栓玩家随玩家持有者传送", "被栓玩家随玩家持有者傳送" ,false),
CREATE_LEASH_FENCE_KNOT_ENTITY_IF_ABSENT(CreateLeashFenceKnotEntityIfAbsent.NAME_KEY, ModPartEnum.NAME, "Create Leash Fence Knot Entity if absent", "如果缺失则创建栓绳结", "如果缺失則創建栓繩結", false),
//GAME_RULE_DESCRIPTION
TELEPORT_WITH_LEASHED_DESCRIPTION(TeleportWithLeashedPlayers.DESCRIPTION_KEY, ModPartEnum.DESCRIPTION, "You will teleport with your leashed players ", "传送时将被栓玩家与自己一起传送", "傳送時將被栓玩家與隨自己一起傳送" ,false),
TELEPORT_WITH_LEASHED_DESCRIPTION(TeleportWithLeashedPlayers.DESCRIPTION_KEY, ModPartEnum.DESCRIPTION, "Holder will teleport with their leashed players ", "传送时将被栓玩家与持有者一起传送", "將被栓玩家將隨持有者一起傳送" ,false),
CREATE_LEASH_FENCE_KNOT_ENTITY_IF_ABSENT_DESCRIPTION(CreateLeashFenceKnotEntityIfAbsent.DESCRIPTION_KEY, ModPartEnum.DESCRIPTION, "Create LeashKnot Entity if it's absent on fence", "如果在栅栏处缺失栓绳结,则创建它", "如果在柵欄処缺失拴繩結,則創建它",false),
;
private final Supplier<?> supplier;
private String key;

View File

@ -213,6 +213,7 @@ public abstract class MixinPlayer extends LivingEntity implements PlayerLeashabl
private void saveLeashData(@org.jetbrains.annotations.Nullable LeashData pLeashData) {
CompoundTag compoundTag = new CompoundTag();
this.writeLeashData(compoundTag, pLeashData);
this.entityData.set(Pl$LEASH_DATA, compoundTag);
}
@SuppressWarnings("AddedMixinMembersNamePattern")

View File

@ -2,6 +2,7 @@ package com.r3944realms.leashedplayer.mixin.client;
import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.datafixers.util.Either;
import com.r3944realms.leashedplayer.modInterface.ILivingEntityExtension;
import com.r3944realms.leashedplayer.modInterface.IPlayerRendererExtension;
import com.r3944realms.leashedplayer.modInterface.PlayerLeashable;
import net.minecraft.client.Camera;
@ -12,11 +13,13 @@ import net.minecraft.client.player.AbstractClientPlayer;
import net.minecraft.client.renderer.*;
import net.minecraft.client.renderer.entity.player.PlayerRenderer;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Holder;
import net.minecraft.util.Mth;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.Leashable;
import net.minecraft.world.entity.decoration.LeashFenceKnotEntity;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.phys.AABB;
import net.minecraft.world.phys.Vec3;
import org.joml.Matrix4f;
import org.spongepowered.asm.mixin.Final;
@ -27,6 +30,7 @@ import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import javax.annotation.Nullable;
import java.util.List;
import java.util.UUID;
@Mixin(LevelRenderer.class)
@ -83,9 +87,33 @@ public abstract class MixinLevelRenderer {
playerRendererExtension.renderLeashForCamera(pCamera, partialTickTime, poseStack, multibuffersource$buffersource, LeashFenceKnotEntity.getOrCreateKnot(level, delayedLeashInfo.right().get()));
} else if (delayedLeashInfo.right().isEmpty() && delayedLeashInfo.left().isPresent()) {
assert level != null;
Player playerByUUID = level.getPlayerByUUID(delayedLeashInfo.left().get());
if (playerByUUID != null) {
playerRendererExtension.renderLeashForCamera(pCamera, partialTickTime, poseStack, multibuffersource$buffersource, playerByUUID);
} else {
float MaxLeashLength = ((ILivingEntityExtension) abstractClientPlayer).getLeashLength() * 2f;
List<Entity> entities = level.getEntities(
null,
new AABB(
abstractClientPlayer.getX() - MaxLeashLength,
abstractClientPlayer.getY() - MaxLeashLength,
abstractClientPlayer.getZ() - MaxLeashLength,
abstractClientPlayer.getX() + MaxLeashLength,
abstractClientPlayer.getY() + MaxLeashLength,
abstractClientPlayer.getZ() + MaxLeashLength
)
);
Entity holder = null;
for (Entity entity_ : entities) {
if(entity_.getUUID().equals(delayedLeashInfo.left().get())) {
holder = entity_;
break;
}
}
if (holder != null) {
playerRendererExtension.renderLeashForCamera(pCamera, partialTickTime, poseStack, multibuffersource$buffersource, holder);
}
}
}
}

View File

@ -3,6 +3,7 @@ package com.r3944realms.leashedplayer.mixin.client;
import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.blaze3d.vertex.VertexConsumer;
import com.mojang.datafixers.util.Either;
import com.r3944realms.leashedplayer.modInterface.ILivingEntityExtension;
import com.r3944realms.leashedplayer.modInterface.IPlayerRendererExtension;
import com.r3944realms.leashedplayer.modInterface.PlayerLeashable;
import net.minecraft.client.Camera;
@ -22,6 +23,7 @@ import net.minecraft.world.entity.Leashable;
import net.minecraft.world.entity.decoration.LeashFenceKnotEntity;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.level.LightLayer;
import net.minecraft.world.phys.AABB;
import net.minecraft.world.phys.Vec3;
import org.joml.Matrix4f;
import org.spongepowered.asm.mixin.Mixin;
@ -30,6 +32,7 @@ import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import java.util.List;
import java.util.UUID;
@Mixin(PlayerRenderer.class)
@ -57,6 +60,29 @@ public abstract class MixinPlayerRenderer extends LivingEntityRenderer<AbstractC
Player playerByUUID = level.getPlayerByUUID(delayedLeashInfo.left().get());
if (playerByUUID != null) {
renderLeash(pEntity, pPartialTicks, pPoseStack, pBuffer, playerByUUID);
} else {
float MaxLeashLength = ((ILivingEntityExtension) pEntity).getLeashLength() * 2f;
List<Entity> entities = level.getEntities(
null,
new AABB(
pEntity.getX() - MaxLeashLength,
pEntity.getY() - MaxLeashLength,
pEntity.getZ() - MaxLeashLength,
pEntity.getX() + MaxLeashLength,
pEntity.getY() + MaxLeashLength,
pEntity.getZ() + MaxLeashLength
)
);
Entity holder = null;
for (Entity entity_ : entities) {
if(entity_.getUUID().equals(delayedLeashInfo.left().get())) {
holder = entity_;
break;
}
}
if (holder != null) {
renderLeash(pEntity, pPartialTicks, pPoseStack, pBuffer, holder);
}
}
}
}

View File

@ -24,12 +24,12 @@ public class MixinServerGamePacketListenerImpl {
@Shadow
public ServerPlayer player;
@Unique
private List<Entity> Whimsy$LeashPlayers = new ArrayList<>();
private List<Entity> Pl$LeashPlayers = new ArrayList<>();
@Inject(method = {"teleport(DDDFFLjava/util/Set;)V"}, at = {@At("HEAD")})
private void teleportHead(double pX, double pY, double pZ, float pYaw, float pPitch, Set<RelativeMovement> pRelativeSet, CallbackInfo ci) {
try {
//獲取Holder
this.Whimsy$LeashPlayers = ((PlayerLeashable)this.player).getLeashHolder() != null ? Collections.emptyList() : Objects.requireNonNull(this.player.getServer()).getPlayerList().getPlayers().stream().filter(serverPlayer -> (serverPlayer instanceof PlayerLeashable) && ((PlayerLeashable)serverPlayer).getLeashHolder() == this.player && player != serverPlayer).collect(Collectors.toList());
this.Pl$LeashPlayers = ((PlayerLeashable)this.player).getLeashHolder() != null ? Collections.emptyList() : Objects.requireNonNull(this.player.getServer()).getPlayerList().getPlayers().stream().filter(serverPlayer -> (serverPlayer instanceof PlayerLeashable) && ((PlayerLeashable)serverPlayer).getLeashHolder() == this.player && player != serverPlayer).collect(Collectors.toList());
} catch (Exception e) {
logger.error("Internal Error:",e);
}
@ -37,9 +37,9 @@ public class MixinServerGamePacketListenerImpl {
@Inject(method = {"teleport(DDDFFLjava/util/Set;)V"}, at = {@At("TAIL")})
private void teleportTail(double pX, double pY, double pZ, float pYaw, float pPitch, Set<RelativeMovement> pRelativeSet, CallbackInfo ci) {
if(GameruleRegistry.getGameruleBoolValue(this.player.serverLevel(), TeleportWithLeashedPlayers.ID)) {
for (Entity whimsy$LeashPlayer : this.Whimsy$LeashPlayers) {
if(whimsy$LeashPlayer instanceof ServerPlayer) {
if(whimsy$LeashPlayer instanceof PlayerLeashable playerLeashable) {
for (Entity Pl$LeashPlayer : this.Pl$LeashPlayers) {
if(Pl$LeashPlayer instanceof ServerPlayer) {
if(Pl$LeashPlayer instanceof PlayerLeashable playerLeashable) {
playerLeashable.dropLeash(false,false);
if(((ServerPlayer) playerLeashable).serverLevel() == this.player.serverLevel()) {
((ServerPlayer) playerLeashable).connection.teleport(pX, pY, pZ, pYaw, pPitch, pRelativeSet);

View File

@ -1,13 +1,18 @@
package com.r3944realms.leashedplayer.modInterface;
import net.minecraft.core.BlockPos;
import net.minecraft.network.protocol.game.ClientboundSetEntityLinkPacket;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.Leashable;
import net.minecraft.world.entity.decoration.LeashFenceKnotEntity;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.phys.AABB;
import org.jetbrains.annotations.NotNull;
import javax.annotation.Nullable;
import java.util.Optional;
import java.util.UUID;
public interface PlayerLeashable extends Leashable {
@ -51,4 +56,64 @@ public interface PlayerLeashable extends Leashable {
//这边覆写去掉了乘坐相关的逻辑即乘坐状态下也可以正常被栓住不影响其乘坐状态
}
static Entity getLeashDataEntityOrThrown(@NotNull Leashable.LeashData leashDataFromEntityData, @NotNull ServerLevel level) throws Exception {
if(leashDataFromEntityData.delayedLeashInfo != null) {
Optional<UUID> UUID = leashDataFromEntityData.delayedLeashInfo.left();
Optional<BlockPos> BlockPos = leashDataFromEntityData.delayedLeashInfo.right();
if (UUID.isPresent()) {
return level.getEntity(UUID.get());
} else if(BlockPos.isPresent()) {
return LeashFenceKnotEntity.getOrCreateKnot(level, BlockPos.get());
} else {
throw new Exception("invalid delayedLeashInfo");
}
}
else if(leashDataFromEntityData.leashHolder != null) {
return leashDataFromEntityData.leashHolder;
}
else if(leashDataFromEntityData.delayedLeashHolderId != 0) {
Entity entity = level.getEntity(leashDataFromEntityData.delayedLeashHolderId);
if(entity == null) {
throw new Exception("Not found Entity. maybe the pId is invalid");
}
return entity;
}
else {
throw new Exception("invalid leash data");
}
}
static boolean isLeashFenceKnotEntityExisted(ServerLevel pLevel, BlockPos pPos) {
int i = pPos.getX();
int j = pPos.getY();
int k = pPos.getZ();
for (LeashFenceKnotEntity leashfenceknotentity : pLevel.getEntitiesOfClass(
LeashFenceKnotEntity.class, new AABB((double)i - 1.0, (double)j - 1.0, (double)k - 1.0, (double)i + 1.0, (double)j + 1.0, (double)k + 1.0)
)) {
if (leashfenceknotentity.getPos().equals(pPos)) {
return true;
}
}
return false;
}
@Nullable
static Entity getLeashFenceKnotEntity(ServerLevel pLevel, BlockPos pPos) {
int i = pPos.getX();
int j = pPos.getY();
int k = pPos.getZ();
for (LeashFenceKnotEntity leashfenceknotentity : pLevel.getEntitiesOfClass(
LeashFenceKnotEntity.class, new AABB((double)i - 1.0, (double)j - 1.0, (double)k - 1.0, (double)i + 1.0, (double)j + 1.0, (double)k + 1.0)
)) {
if (leashfenceknotentity.getPos().equals(pPos)) {
return leashfenceknotentity;
}
}
return null;
}
static Entity createLeashKnotFence(ServerLevel pLevel, BlockPos pPos) {
LeashFenceKnotEntity leashfenceknotentity = new LeashFenceKnotEntity(pLevel, pPos);
pLevel.addFreshEntity(leashfenceknotentity);
return leashfenceknotentity;
}
}

View File

@ -14,7 +14,7 @@ loaderVersion="${loader_version_range}" #mandatory
license="${mod_license}"
# A URL to refer people to when problems occur with this mod
#issueTrackerURL="https://change.me.to.your.issue.tracker.example.invalid/" #optional
issueTrackerURL="https://github.com/3944Realms/R39_Whimsy_NeoForgeModProject_Sub/issues" #optional
# A list of mods - how many allowed here is determined by the individual mod loader
[[mods]] #mandatory
@ -32,10 +32,10 @@ displayName="${mod_name}" #mandatory
#updateJSONURL="https://change.me.example.invalid/updates.json" #optional
# A URL for the "homepage" for this mod, displayed in the mod UI
#displayURL="https://change.me.to.your.mods.homepage.example.invalid/" #optional
displayURL="https://github.com/3944Realms/R39_Whimsy_NeoForgeModProject_Sub" #optional
# A file name (in the root of the mod JAR) containing a logo for display
#logoFile="examplemod.png" #optional
logoFile= "leashedplayerlogo.png" #optional
# A text field displayed in the mod UI
#credits="" #optional

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB