2024-09-05

版本0.0.3.0-Beta.0.1
增加:
1. 增加了新的指令 data clear
2. 添加了配置文件,方便修改本模组的部分行为
3. 增加新的内容,如拴绳箭(但是有点BUG
修改与调整:
1.调整了原语言部分错别字
2.更改了之前的逻辑【栓绳运动逻辑】
具體新内容請見 README.md 介紹
This commit is contained in:
叁玖领域 2024-09-05 23:15:06 +08:00
parent 3da99bc6f3
commit 987ec866c3
47 changed files with 1024 additions and 179 deletions

View File

@ -1,15 +1,23 @@
# 版本 0.0.2.2 【注意:本解釋簡繁混寫,因爲趕時間,所以並不怎麽規範,請諒解】
# 版本 0.0.3.0-Beta.0.1 提前介绍 【注意:本解釋簡繁混寫,因爲趕時間,所以並不怎麽規範,請諒解】
## 简介
现在开始你可以用拴绳栓住玩家,也可以栓住自己了,不如尝试栓住彼此来通关我的世界吧(
现在开始你可以用拴绳拴住玩家,也可以拴住自己了,不如尝试拴住彼此来通关我的世界吧(
你可以:
1. 拴绳可以拴住玩家
2. 在无任何住实体的情况下用拴绳对准栅栏可以拴住自己
3. 可以用指令来获取和调整玩家实体的拴绳的长度
2. 在无任何住实体的情况下用拴绳对准栅栏可以拴住自己
3. 可以用指令来获取和调整玩家实体的拴绳的长度和设置拴绳数据实体
4. 目前有游戏规则来决定被栓玩家是否在传送时能随拴绳持有者一起传送
## 配置文件
1. 可改变指令前命名空间和关闭命名空间
2. 修改命令拴绳的可设置的长度范围
3. 设置拴绳在达到多少倍的拴绳长度后掉落
## 新物品 和 实体
### 拴绳箭
获得飞一样的感觉(操作不当可能会摔死
#### 射中实体时会将射击者拴绳绑定在改实体上该实体父类必须是有LivingEntity类型同时拴绳箭会以普通的箭矢掉落
#### 射中栅栏时,会自动将玩家拴在上面 ,同时拴绳箭会以普通的箭矢掉落
## 指令
* `/lp leash length [<玩家>] set <长度> ` - 设置该玩家的拴绳长度 [ 如果<玩家>为空则代表执行对象是自己 ,<长度> 为在 5 ~ 1024之间的浮点数 ]

Binary file not shown.

After

Width:  |  Height:  |  Size: 268 B

View File

@ -1,5 +1,5 @@
# Sets default memory used for gradle commands. Can be overridden by user or command line properties.
org.gradle.jvmargs=-Xmx3G
org.gradle.jvmargs=-Xmx8G
org.gradle.daemon=false
org.gradle.debug=false
@ -10,13 +10,13 @@ neogradle.subsystems.parchment.mappingsVersion=2024.07.28
# Environment Properties
# You can find the latest versions here: https://projects.neoforged.net/neoforged/neoforge
# The Minecraft version must agree with the Neo version to get a valid artifact
minecraft_version=1.21
minecraft_version=1.21.1
# The Minecraft version range can use any release version of Minecraft as bounds.
# Snapshots, pre-releases, and release candidates are not guaranteed to sort properly
# as they do not follow standard versioning conventions.
minecraft_version_range=[1.21,1.21.1)
minecraft_version_range=[1.21,1.22)
# The Neo version must agree with the Minecraft version to get a valid artifact
neo_version=21.0.167
neo_version=21.1.35
# The Neo version range can use any version of Neo as bounds
neo_version_range=[21.0.0-beta,)
# The loader version range can only use the major version of FML as bounds
@ -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.2.2
mod_version=0.0.3.0-Beta.0.1
# 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,6 +1,6 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://mirrors.cloud.tencent.com/gradle/gradle-8.8-bin.zip
distributionUrl=https\://mirrors.cloud.tencent.com/gradle/gradle-8.9-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME

View File

@ -0,0 +1,2 @@
// 1.21.1 2024-09-05T13:27:19.7536653 Item Models: leashedplayer
114d3cc5832ef047403114504483c6f3ea07e77c assets/leashedplayer/models/item/leash_rope_arrow.json

View File

@ -0,0 +1,2 @@
// 1.21.1 2024-09-05T15:23:52.4523518 Tags for minecraft:item mod id leashedplayer
a8cd7ac5b7e37b7dea044004a42a2f137d4027d6 data/minecraft/tags/item/arrows.json

View File

@ -1,2 +1,2 @@
// 1.21 2024-09-04T14:54:14.4635752 Languages: en_us for mod: leashedplayer
3e231f3b588ab61d2990c49bf1f1deca5593dbbf assets/leashedplayer/lang/en_us.json
// 1.21.1 2024-09-05T21:38:59.0514122 Languages: en_us for mod: leashedplayer
06d246d0f55a53cc97a63426253e9328cf4a189d assets/leashedplayer/lang/en_us.json

View File

@ -0,0 +1 @@
// 1.21.1 2024-09-05T15:23:52.4543401 Tags for minecraft:block mod id leashedplayer

View File

@ -1,2 +1,2 @@
// 1.21 2024-09-03T17:46:39.9787945 Languages: lzh for mod: leashedplayer
// 1.21.1 2024-09-05T10:26:39.4122865 Languages: lzh for mod: leashedplayer
bf21a9e8fbc5a3846fb05b4fa0859e0917b2202f assets/leashedplayer/lang/lzh.json

View File

@ -0,0 +1,3 @@
// 1.21.1 2024-09-05T13:20:35.9705404 Recipes
1b45d1ad8dc73f1787c97777ad13d9771c9e0ad1 data/leashedplayer/advancement/recipes/misc/leash_rope_arrow.json
974d74538b3e172946f2e169036b453b6eb6bc0a data/leashedplayer/recipe/leash_rope_arrow.json

View File

@ -1,2 +1,2 @@
// 1.21 2024-09-04T14:54:14.4585471 Languages: zh_cn for mod: leashedplayer
6db8ccbbd7d3bf13e819c2ae1762f37e2c332043 assets/leashedplayer/lang/zh_cn.json
// 1.21.1 2024-09-05T21:38:59.0494103 Languages: zh_cn for mod: leashedplayer
283bed8b7a38bb5927f126c4002a772cd64fc274 assets/leashedplayer/lang/zh_cn.json

View File

@ -1,2 +1,2 @@
// 1.21 2024-09-04T14:54:14.4535174 Languages: zh_tw for mod: leashedplayer
86fa3a80c53a50ad5af47129384e506cddda79aa assets/leashedplayer/lang/zh_tw.json
// 1.21.1 2024-09-05T21:38:59.0474148 Languages: zh_tw for mod: leashedplayer
056a2c46a8ee17f5326e70d397cd3c8b2950df2d assets/leashedplayer/lang/zh_tw.json

View File

@ -1,15 +1,21 @@
{
"creativetab.leashedplayer.leashedplayer_tab": "Leashed Player",
"entity.leashedplayer.leash_rope_arrow": "Leash Rope Arrow",
"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 ",
"item.leash_rope_arrow.description": "Arrows with ropes attached?",
"item.leashedplayer.leash_rope_arrow": "Leash Rope Arrow",
"leashedplayer.command.leash.message.leash.data.clear": "%1$s's LeashData(LeashHolderEntity: %2$s) now is clear",
"leashedplayer.command.leash.message.leash.data.clear.leash.clear.failed.no_data": "%1$s has no LeashData can be clear",
"leashedplayer.command.leash.message.leash.data.null": "%1$s has no LeashDataEntity",
"leashedplayer.command.leash.message.leash.data.set": "%1$s LeashDataEntity now is set as %2$s",
"leashedplayer.command.leash.message.leash.data.set.failed.diff_level": "%1$s and %2$s are not at a same level",
"leashedplayer.command.leash.message.leash.data.set.failed.forbid_same_entity": "Prohibit setting the same entity",
"leashedplayer.command.leash.message.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.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.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,15 +1,21 @@
{
"gamerule.LP.CreateLeashFenceKnotEntityIfAbsent": "如果缺失则创建栓绳结",
"gamerule.LP.CreateLeashFenceKnotEntityIfAbsent.description": "如果在栅栏处缺失栓绳结,则创建它",
"gamerule.LP.TeleportWithLeashedPlayers": "被栓玩家随玩家持有者传送",
"gamerule.LP.TeleportWithLeashedPlayers.description": "传送时将被栓玩家与持有者一起传送",
"creativetab.leashedplayer.leashedplayer_tab": "可拴玩家",
"entity.leashedplayer.leash_rope_arrow": "拴绳箭",
"gamerule.LP.CreateLeashFenceKnotEntityIfAbsent": "如果缺失则创建拴绳结",
"gamerule.LP.CreateLeashFenceKnotEntityIfAbsent.description": "如果在栅栏处缺失拴绳结,则创建它",
"gamerule.LP.TeleportWithLeashedPlayers": "被拴玩家随玩家持有者传送",
"gamerule.LP.TeleportWithLeashedPlayers.description": "传送时将被拴玩家与持有者一起传送",
"item.leash_rope_arrow.description": "带有拴绳的箭矢?",
"item.leashedplayer.leash_rope_arrow": "拴绳箭",
"leashedplayer.command.leash.message.leash.data.clear": "%1$s的拴绳数据拴绳持有者实体%2$s现在已清除",
"leashedplayer.command.leash.message.leash.data.clear.leash.clear.failed.no_data": "%1$s沒有拴绳数据可清除",
"leashedplayer.command.leash.message.leash.data.null": "%1$s沒有拴绳数据实体",
"leashedplayer.command.leash.message.leash.data.set": "%1$s拴绳数据实体被设置为%2$s",
"leashedplayer.command.leash.message.leash.data.set.failed.diff_level": "%1$s和%2$s不在同一维度上",
"leashedplayer.command.leash.message.leash.data.set.failed.forbid_same_entity": "禁止设置同一实体",
"leashedplayer.command.leash.message.leash.data.set.failed.no_knot_exist_in_that_pos": "未找到拴绳结在(X:%f, Y:%f, Z:%f)处",
"leashedplayer.command.leash.message.leash.data.set.failed.too_far": "%1$s到%2$s的距离超过了1.2倍 拴绳长度,原长:%3$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,16 +1,22 @@
{
"gamerule.LP.CreateLeashFenceKnotEntityIfAbsent": "如果缺失則創建栓繩結",
"creativetab.leashedplayer.leashedplayer_tab": "可拴玩家",
"entity.leashedplayer.leash_rope_arrow": "拴繩箭",
"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 格",
"gamerule.LP.TeleportWithLeashedPlayers": "被拴玩家随玩家持有者傳送",
"gamerule.LP.TeleportWithLeashedPlayers.description": "將被拴玩家將隨持有者一起傳送",
"item.leash_rope_arrow.description": "帶有拴繩的箭矢?",
"item.leashedplayer.leash_rope_arrow": "拴繩箭",
"leashedplayer.command.leash.message.leash.data.clear": "%1$s的拴繩數據拴繩持有者實體%2$s現在已清除",
"leashedplayer.command.leash.message.leash.data.clear.leash.clear.failed.no_data": "%1$s沒有拴繩數據實體可被清除",
"leashedplayer.command.leash.message.leash.data.null": "%1$s沒有拴繩數據實體",
"leashedplayer.command.leash.message.leash.data.set": "%1$s拴繩數據實體設置為%2$s",
"leashedplayer.command.leash.message.leash.data.set.failed.diff_level": "%1$s和%2$s不在同一緯度上",
"leashedplayer.command.leash.message.leash.data.set.failed.forbid_same_entity": "禁止設置同一實體",
"leashedplayer.command.leash.message.leash.data.set.failed.no_knot_exist_in_that_pos": "未找到拴繩結在(X:%f, Y:%f, Z:%f)処",
"leashedplayer.command.leash.message.leash.data.set.failed.too_far": "%1$s到%2$s的距離超過了1.2倍拴繩長度,原長:%3$s 格",
"leashedplayer.command.leash.message.leash.data.show": "%1$s拴繩數據實體為%2$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格"
"leashedplayer.command.leash.message.leash.length.set": "%1$s的繩長度被設置為%2$s格",
"leashedplayer.command.leash.message.leash.length.show": "%1$s的繩長度為%2$s格"
}

View File

@ -0,0 +1,6 @@
{
"parent": "minecraft:item/generated",
"textures": {
"layer0": "leashedplayer:item/leash_rope_arrow"
}
}

View File

@ -0,0 +1,32 @@
{
"parent": "minecraft:recipes/root",
"criteria": {
"has_lead": {
"conditions": {
"items": [
{
"items": "minecraft:lead"
}
]
},
"trigger": "minecraft:inventory_changed"
},
"has_the_recipe": {
"conditions": {
"recipe": "leashedplayer:leash_rope_arrow"
},
"trigger": "minecraft:recipe_unlocked"
}
},
"requirements": [
[
"has_the_recipe",
"has_lead"
]
],
"rewards": {
"recipes": [
"leashedplayer:leash_rope_arrow"
]
}
}

View File

@ -0,0 +1,16 @@
{
"type": "minecraft:crafting_shapeless",
"category": "misc",
"ingredients": [
{
"item": "minecraft:lead"
},
{
"item": "minecraft:arrow"
}
],
"result": {
"count": 1,
"id": "leashedplayer:leash_rope_arrow"
}
}

View File

@ -0,0 +1,5 @@
{
"values": [
"leashedplayer:leash_rope_arrow"
]
}

View File

@ -0,0 +1,16 @@
package com.r3944realms.leashedplayer;
import com.r3944realms.leashedplayer.client.renders.LeashRopeArrowRenderer;
import com.r3944realms.leashedplayer.content.entities.ModEntityRegister;
import net.neoforged.api.distmarker.Dist;
import net.neoforged.bus.api.SubscribeEvent;
import net.neoforged.fml.common.EventBusSubscriber;
import net.neoforged.neoforge.client.event.EntityRenderersEvent;
@EventBusSubscriber(value = Dist.CLIENT, bus = EventBusSubscriber.Bus.MOD, modid = LeashedPlayer.MOD_ID)
public class ClientEventHandler {
@SubscribeEvent
public static void RegisterRenderer(EntityRenderersEvent.RegisterRenderers event) {
event.registerEntityRenderer(ModEntityRegister.LEASH_ROPE_ARROW.get(), LeashRopeArrowRenderer::new);
}
}

View File

@ -1,9 +1,46 @@
package com.r3944realms.leashedplayer;
import com.r3944realms.leashedplayer.config.LeashPlayerCommonConfig;
import com.r3944realms.leashedplayer.content.entities.ModEntityRegister;
import com.r3944realms.leashedplayer.content.items.ModCreativeTab;
import com.r3944realms.leashedplayer.content.items.ModItemRegister;
import com.r3944realms.leashedplayer.utils.Util;
import net.neoforged.bus.api.IEventBus;
import net.neoforged.fml.ModLoadingContext;
import net.neoforged.fml.common.Mod;
import net.neoforged.fml.config.ModConfig;
import static com.r3944realms.leashedplayer.utils.Logger.logger;
@Mod(LeashedPlayer.MOD_ID)
public class LeashedPlayer {
public static final String MOD_ID = "leashedplayer";
private static Float M1;//拴繩掉落距離倍基數
private static Float M2;//繩箭拴繩掉落距離倍基數
public LeashedPlayer(IEventBus event) {
ModItemRegister.register(event);
ModEntityRegister.register(event);
ModCreativeTab.register(event);
initiation();
}
private void initiation() {
logger.info("Initializing LeashedPlayer Mod");
String leashedPlayerCommonConfig = "LeashedPlayerCommonConfig";
Util.configFileCreate(new String[]{leashedPlayerCommonConfig});
ModLoadingContext.get().getActiveContainer().registerConfig(ModConfig.Type.COMMON, LeashPlayerCommonConfig.SPEC, MOD_ID + "/" + leashedPlayerCommonConfig + "/LeashPlayer.toml");
}
public static Float M1() {
if(M1 == null) {
M1 = LeashPlayerCommonConfig.TheLeashBreakLengthTimesBase.get();
}
return M1;
}
public static Float M2() {
if(M2 == null) {
M2 = LeashPlayerCommonConfig.TheMultipleThatLeashRopeArrowBreakLength.get();
}
return M2;
}
}

View File

@ -0,0 +1,23 @@
package com.r3944realms.leashedplayer.client.renders;
import com.r3944realms.leashedplayer.LeashedPlayer;
import com.r3944realms.leashedplayer.content.entities.LeashRopeArrow;
import net.minecraft.client.renderer.entity.ArrowRenderer;
import net.minecraft.client.renderer.entity.EntityRendererProvider;
import net.minecraft.resources.ResourceLocation;
import net.neoforged.api.distmarker.Dist;
import net.neoforged.api.distmarker.OnlyIn;
import org.jetbrains.annotations.NotNull;
@OnlyIn(Dist.CLIENT)
public class LeashRopeArrowRenderer extends ArrowRenderer<LeashRopeArrow> {
public static final ResourceLocation LEASH_ROPE_ARROW = ResourceLocation.fromNamespaceAndPath(LeashedPlayer.MOD_ID, "textures/entity/projectiles/leash_rope_arrow.png");
public LeashRopeArrowRenderer(EntityRendererProvider.Context pContext) {
super(pContext);
}
@Override
public @NotNull ResourceLocation getTextureLocation(@NotNull LeashRopeArrow pEntity) {
return LEASH_ROPE_ARROW;
}
}

View File

@ -0,0 +1,41 @@
package com.r3944realms.leashedplayer.config;
import net.neoforged.neoforge.common.ModConfigSpec;
import java.util.function.Predicate;
public class LeashPlayerCommonConfig {
public static ModConfigSpec.Builder BUILDER = new ModConfigSpec.Builder();
public static final ModConfigSpec SPEC;
public static final ModConfigSpec.ConfigValue<String> LeashedPlayerModCommandPrefix;
public static final ModConfigSpec.BooleanValue EnableLeashPlayerCommandPrefix;
public static ModConfigSpec.IntValue MinimumLeashLengthCanBeSet, MaximumLeashLengthCanBeSet;
public static ModConfigSpec.ConfigValue<Float> TheMultipleThatLeashRopeArrowBreakLength, TheLeashBreakLengthTimesBase;
static {
BUILDER.comment("Leash Player Config");
BUILDER.push("Command");
EnableLeashPlayerCommandPrefix = BUILDER.comment("The prefix of this mod's commands"," [ Default: true] ").define("Enable Leash Player Command", true);
LeashedPlayerModCommandPrefix = BUILDER.comment("The prefix of this mod's commands"," [ Default:'lp'] ").define("LeashedPlayerModCommandPrefix", "lp");
BUILDER.pop();
BUILDER.push("Leash Rope Arrow");
TheMultipleThatLeashRopeArrowBreakLength = BUILDER.comment("How many times is the length of the arrow rope based on BreakLength TimeBase", "[ Default : 5.0f, Invalid Range:[1.0f, 10.0f] ]").define("TheMultipleArrowBreak", 5.0f, o -> (o instanceof Float f) && f >= 2.0f && f <= 10.0f);
BUILDER.pop();
BUILDER.push("Misc");
BUILDER.push("LeashLength");
TheLeashBreakLengthTimesBase = BUILDER.comment("When it exceeds how many times, the leash will drop","[ Default:2.0f, Invalid Range:[2.0f, 5.0f] ]").define("BreakLengthTimeBase", 2.0f, o -> (o instanceof Float f) && f >= 2.0f && f <= 5.0f);
MinimumLeashLengthCanBeSet = BUILDER.comment("The minimum integer's length of Leash", "[ Default:5, Invalid Range:[2,10] ]").defineInRange("MinLeashLength", 5, 2, 10);
MaximumLeashLengthCanBeSet = BUILDER.comment("The maximum integer's length of Leash", "[ Default:1024, Invalid Range:[32, 1024] ]").defineInRange("MaxLeashLength", 1024, 32, 1024);
SPEC = BUILDER.build();
}
}

View File

@ -1,5 +1,7 @@
package com.r3944realms.leashedplayer.content.commands;
import com.r3944realms.leashedplayer.config.LeashPlayerCommonConfig;
public class Command {
public static final String PREFIX = "lp";
public static final String PREFIX = LeashPlayerCommonConfig.LeashedPlayerModCommandPrefix.get();
}

View File

@ -7,6 +7,7 @@ import com.mojang.brigadier.builder.LiteralArgumentBuilder;
import com.mojang.brigadier.context.CommandContext;
import com.mojang.brigadier.exceptions.CommandSyntaxException;
import com.r3944realms.leashedplayer.config.LeashPlayerCommonConfig;
import com.r3944realms.leashedplayer.content.gamerules.GameruleRegistry;
import com.r3944realms.leashedplayer.content.gamerules.Server.CreateLeashFenceKnotEntityIfAbsent;
import com.r3944realms.leashedplayer.modInterface.ILivingEntityExtension;
@ -22,25 +23,47 @@ 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;
import java.util.ArrayList;
import java.util.List;
public class LeashCommand {
public static final Integer MIN_VALUE = LeashPlayerCommonConfig.MinimumLeashLengthCanBeSet.get();
public static final Integer MAX_VALUE = LeashPlayerCommonConfig.MaximumLeashLengthCanBeSet.get();
private final static String LEASHEDPLAYER_LEASH_MESSAGE_ = "leashedplayer.command.leash.message.";
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 final static String LEASH_FAILED = LEASHEDPLAYER_LEASH_MESSAGE_ + "leash.length.failed";
public final static String LEASH_LENGTH_SHOW = LEASHEDPLAYER_LEASH_MESSAGE_ + "leash.length.show",
LEASH_LENGTH_SET = LEASHEDPLAYER_LEASH_MESSAGE_ + "leash.length.set",
LEASH_DATA = LEASHEDPLAYER_LEASH_MESSAGE_ + "leash.data",
LEASH_DATA_CLEAR = LEASH_DATA + ".clear"
;
public final static String NO_LEASH_DATA = LEASH_DATA + ".null",
LEASH_DATA_SHOW = LEASH_DATA + ".show",
LEASH_DATA_SET = 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",
LEASH_DATA_CLEAR_FAILED_NO_DATA = LEASH_DATA_CLEAR + ".leash.clear.failed.no_data"
;
private static LiteralArgumentBuilder<CommandSourceStack> getLiterArgumentBuilderOfCSS(String name, boolean shouldAddToList, @Nullable List<LiteralArgumentBuilder<CommandSourceStack>> list) {
LiteralArgumentBuilder<CommandSourceStack> literal = Commands.literal(name);
if (shouldAddToList) {
assert list != null;
list.add(literal);
}
return literal;
}
public static void register(CommandDispatcher<CommandSourceStack> dispatcher) {
boolean shouldUsePrefix = LeashPlayerCommonConfig.EnableLeashPlayerCommandPrefix.get();
@Nullable List<LiteralArgumentBuilder<CommandSourceStack>> nodeList = shouldUsePrefix ? null : new ArrayList<>();
LiteralArgumentBuilder<CommandSourceStack> literalArgumentBuilder = Commands.literal(com.r3944realms.leashedplayer.content.commands.Command.PREFIX);
LiteralArgumentBuilder<CommandSourceStack> $$leashRoot = getLiterArgumentBuilderOfCSS("leash", !shouldUsePrefix, nodeList);
Command<CommandSourceStack> getSelfLeashLength = context -> {
CommandSourceStack source = context.getSource();
try {
@ -96,7 +119,7 @@ public class LeashCommand {
CommandSourceStack source = context.getSource();
try {
ServerPlayer player = source.getPlayerOrException();
Integer x = LeashLengthResultInt(player, source);
Integer x = LeashLengthGetResultInt(player, source);
if (x != null) return x;
} catch (Exception e) {
source.sendFailure(Component.translatable(LEASH_FAILED));
@ -109,7 +132,7 @@ public class LeashCommand {
CommandSourceStack source = context.getSource();
try {
ServerPlayer player = EntityArgument.getPlayer(context, "targetPlayer");
Integer x = LeashLengthResultInt(player, source);
Integer x = LeashLengthGetResultInt(player, source);
if (x != null) return x;
} catch (Exception e) {
source.sendFailure(Component.translatable(LEASH_FAILED));
@ -122,7 +145,7 @@ public class LeashCommand {
CommandSourceStack source = context.getSource();
try {
ServerPlayer player = source.getPlayerOrException();
Integer x = LeashDataEntityResultInt(context, player, source);
Integer x = LeashDataEntitySetResultInt(context, player, source);
if (x != null) return x;
} catch (Exception e) {
@ -135,7 +158,7 @@ public class LeashCommand {
CommandSourceStack source = context.getSource();
try {
ServerPlayer player = source.getPlayerOrException();
Integer x = LeashDataBlockPosResultInt(context, source, player);
Integer x = LeashDataBlockPosSetResultInt(context, source, player);
if (x != null) return x;
@ -150,7 +173,7 @@ public class LeashCommand {
CommandSourceStack source = context.getSource();
try {
ServerPlayer player = EntityArgument.getPlayer(context, "targetPlayer");
Integer x = LeashDataEntityResultInt(context, player, source);
Integer x = LeashDataEntitySetResultInt(context, player, source);
if (x != null) return x;
} catch (Exception e) {
@ -163,7 +186,7 @@ public class LeashCommand {
CommandSourceStack source = context.getSource();
try {
ServerPlayer player = EntityArgument.getPlayer(context, "targetPlayer");
Integer x = LeashDataBlockPosResultInt(context, source, player);
Integer x = LeashDataBlockPosSetResultInt(context, source, player);
if (x != null) return x;
} catch (Exception e) {
@ -172,52 +195,73 @@ public class LeashCommand {
}
return 0;
};
LiteralArgumentBuilder<CommandSourceStack> $$leashRoot = Commands.literal("leash");
literalArgumentBuilder.then(
$$leashRoot.then(Commands.literal("length").executes(getSelfLeashLength)
.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)
Command<CommandSourceStack> clearSelfLeashData = context -> {
CommandSourceStack source = context.getSource();
try {
ServerPlayer player = source.getPlayerOrException();
Integer x = LeashDataClearResultInt(source, PlayerLeashable.getLeashDataEntity(player, source.getLevel()), player);
if (x != null) return x;
} catch (Exception e) {
source.sendFailure(Component.translatable(LEASH_FAILED));
return -1;
}
return 0;
};
Command<CommandSourceStack> clearRefPlayerLeashData = context -> {
CommandSourceStack source = context.getSource();
try {
ServerPlayer player = EntityArgument.getPlayer(context, "targetPlayer");
Integer x = LeashDataClearResultInt(source, PlayerLeashable.getLeashDataEntityOrThrown(player, source.getLevel()),player);
if (x != null) return x;
} catch (Exception e) {
source.sendFailure(Component.translatable(LEASH_FAILED));
return -1;
}
return 0;
};
LiteralArgumentBuilder<CommandSourceStack> SelfLeashLength = $$leashRoot.then(Commands.literal("length").executes(getSelfLeashLength)
.then(Commands.literal("get").executes(getSelfLeashLength))
.then(Commands.literal("set").requires(cs -> cs.hasPermission(2))
.then(Commands.argument("leashLength", FloatArgumentType.floatArg(MIN_VALUE, MAX_VALUE)).executes(setSelfLengthLeashLength))
)
);
LiteralArgumentBuilder<CommandSourceStack> RefPlayerLeashLength = $$leashRoot.then(
Commands.literal("length")
.then(Commands.argument("player", EntityArgument.player()).executes(getRefPlayerLeashLength)
.then(Commands.literal("get").executes(getRefPlayerLeashLength))
.then(Commands.literal("set").requires(cs -> cs.hasPermission(2))
.then(
Commands.argument("leashLength", FloatArgumentType.floatArg(MIN_VALUE, MAX_VALUE)).executes(setLengthLeashLength)
)
)
)
)
);
literalArgumentBuilder.then(
$$leashRoot.then(
Commands.literal("length")
.then(Commands.argument("player", EntityArgument.player()).executes(getRefPlayerLeashLength)
.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)
)
)
)
)
);
literalArgumentBuilder.then(
$$leashRoot.then(
Commands.literal("data")
.then(Commands.argument("targetPlayer", EntityArgument.player()).executes(getRefPlayerLeashData)
.then(Commands.literal("get")
.executes(getRefPlayerLeashData)
LiteralArgumentBuilder<CommandSourceStack> RefPLayerData = $$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.literal("set").requires(cs -> cs.hasPermission(2))
.then(Commands.argument("holderEntity", EntityArgument.entity())
.executes(setRefPlayerLeashDataEntity)
)
.then(Commands.argument("BlockPos", BlockPosArgument.blockPos())
.executes(setRefPlayerLeashDataByBlockPos)
)
.then(Commands.argument("BlockPos", BlockPosArgument.blockPos())
.executes(setRefPlayerLeashDataByBlockPos)
)
)
)
.then(Commands.literal("clear").requires(cs -> cs.hasPermission(2)).executes(clearSelfLeashData))
)
);
literalArgumentBuilder.then(
$$leashRoot.then(
Commands.literal("data")
LiteralArgumentBuilder<CommandSourceStack> SelfData = $$leashRoot.then(
Commands.literal("data")
.then(Commands.literal("get")
.executes(geSelfLeashData)
)
@ -229,12 +273,25 @@ public class LeashCommand {
.executes(setSelfLeashDataByBlockPos)
)
)
)
.then(Commands.literal("clear").requires(cs -> cs.hasPermission(2))
.executes(clearSelfLeashData)
)
);
dispatcher.register(literalArgumentBuilder);
if(shouldUsePrefix) {
literalArgumentBuilder
.then(RefPlayerLeashLength)
.then(SelfLeashLength)
.then(RefPLayerData)
.then(SelfData);
dispatcher.register(literalArgumentBuilder);
} else {
nodeList.forEach(dispatcher::register);
}
}
private static @Nullable Integer LeashLengthResultInt(ServerPlayer player, CommandSourceStack source) throws Exception {
private static @Nullable Integer LeashLengthGetResultInt(ServerPlayer player, CommandSourceStack source) throws Exception {
Leashable.LeashData leashDataFromEntityData = ((PlayerLeashable) player).getLeashDataFromEntityData();
if(leashDataFromEntityData == null) {
@ -247,7 +304,7 @@ public class LeashCommand {
return null;
}
private static @Nullable Integer LeashDataBlockPosResultInt(CommandContext<CommandSourceStack> context, CommandSourceStack source, ServerPlayer player) {
private static @Nullable Integer LeashDataBlockPosSetResultInt(CommandContext<CommandSourceStack> context, CommandSourceStack source, ServerPlayer player) {
BlockPos blockPos = BlockPosArgument.getBlockPos(context, "BlockPos");
Entity leashDataEntity = PlayerLeashable.getLeashFenceKnotEntity(source.getLevel(), blockPos);
PlayerLeashable leashedPlayer = (PlayerLeashable) player;
@ -267,10 +324,10 @@ public class LeashCommand {
}
Component leashDataEntityDisplayName = leashDataEntity.getDisplayName();
return LeashDataCommonPartResultInt(source, player, leashDataEntity, leashedPlayer, targetPlayerDisplayName, leashDataEntityDisplayName);
return LeashDataCommonPartSetResultInt(source, player, leashDataEntity, leashedPlayer, targetPlayerDisplayName, leashDataEntityDisplayName);
}
private static @Nullable Integer LeashDataEntityResultInt(CommandContext<CommandSourceStack> context, ServerPlayer player, CommandSourceStack source) throws CommandSyntaxException {
private static @Nullable Integer LeashDataEntitySetResultInt(CommandContext<CommandSourceStack> context, ServerPlayer player, CommandSourceStack source) throws CommandSyntaxException {
Entity leashDataEntity = EntityArgument.getEntity(context, "holderEntity");
PlayerLeashable leashedPlayer = (PlayerLeashable) player;
Component targetPlayerDisplayName = player.getDisplayName();
@ -279,10 +336,10 @@ public class LeashCommand {
source.sendFailure(Component.translatable(LEASH_DATA_SET_FAILED_FORBID_SAME_ENTITY));
return 1;
}
return LeashDataCommonPartResultInt(source, player, leashDataEntity, leashedPlayer, targetPlayerDisplayName, leashDataEntityDisplayName);
return LeashDataCommonPartSetResultInt(source, player, leashDataEntity, leashedPlayer, targetPlayerDisplayName, leashDataEntityDisplayName);
}
private static @Nullable Integer LeashDataCommonPartResultInt(CommandSourceStack source, ServerPlayer player, Entity leashDataEntity, PlayerLeashable leashedPlayer, Component targetPlayerDisplayName, Component leashDataEntityDisplayName) {
private static @Nullable Integer LeashDataCommonPartSetResultInt(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;
@ -296,6 +353,15 @@ public class LeashCommand {
source.sendSuccess(() -> Component.translatable(LEASH_DATA_SET, targetPlayerDisplayName, leashDataEntityDisplayName), true);
return null;
}
private static @Nullable Integer LeashDataClearResultInt(CommandSourceStack source, Entity leashDataEntity, ServerPlayer serverPlayer) {
if(leashDataEntity == null) {
source.sendFailure(Component.translatable(LEASH_DATA_CLEAR_FAILED_NO_DATA, serverPlayer.getDisplayName()));
return 1;
}
((PlayerLeashable)serverPlayer).dropLeash(true, false);
source.sendSuccess(() -> Component.translatable(LEASH_DATA_CLEAR, serverPlayer.getDisplayName(), leashDataEntity.getDisplayName()), true);
return null;
}
}

View File

@ -0,0 +1,115 @@
package com.r3944realms.leashedplayer.content.entities;
import com.r3944realms.leashedplayer.content.items.ModItemRegister;
import com.r3944realms.leashedplayer.modInterface.PlayerLeashable;
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.EntityType;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.entity.item.ItemEntity;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.entity.projectile.AbstractArrow;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.Items;
import net.minecraft.world.level.Level;
import net.minecraft.world.phys.BlockHitResult;
import net.minecraft.world.phys.EntityHitResult;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public class LeashRopeArrow extends AbstractArrow {
protected LeashRopeArrow(EntityType<? extends AbstractArrow> entityType,Level pLevel) {
super(entityType, pLevel);
}
protected LeashRopeArrow(double pX, double pY, double pZ, Level pLevel, ItemStack pPickupItemStack, @Nullable ItemStack pFiredFromWeapon, @Nullable ServerPlayer serverPlayer) {
super(ModEntityRegister.LEASH_ROPE_ARROW.get(), pX, pY, pZ, pLevel, pPickupItemStack, pFiredFromWeapon);
if(serverPlayer != null && !level().isClientSide) {
((PlayerLeashable)serverPlayer).setLeashedTo(this, true);
}
}
public LeashRopeArrow(LivingEntity pOwner, Level pLevel, ItemStack pPickupItemStack, @Nullable ItemStack pFiredFromWeapon) {
super(ModEntityRegister.LEASH_ROPE_ARROW.get(), pOwner, pLevel, pPickupItemStack, pFiredFromWeapon);
if(pOwner instanceof PlayerLeashable lPlayer && !level().isClientSide) {
lPlayer.setLeashedTo(this, true);
}
}
@Override
protected @NotNull ItemStack getDefaultPickupItem() {
return ModItemRegister.LEASH_ROPE_ARROW.get().getDefaultInstance();
}
@Override
public void setOwner(@Nullable Entity pEntity) {
super.setOwner(pEntity);
this.pickup = this.pickup == Pickup.CREATIVE_ONLY ? this.pickup : Pickup.DISALLOWED;
}
@Override
protected boolean tryPickup(@NotNull Player pPlayer) {
if(life <= 240) {
return false;
} else {
if(life >= 480 || this.ownedBy(pPlayer) && (this.pickup != Pickup.CREATIVE_ONLY) ) {
this.pickup = Pickup.ALLOWED;
if(this.ownedBy(pPlayer)) {
((PlayerLeashable)pPlayer).dropLeash(true, false);
} else if (this.getOwner() instanceof PlayerLeashable pL){
pL.setLeashedTo(this, false);
}
}
return super.tryPickup(pPlayer);
}
}
@Override
protected void tickDespawn() {
this.life++;
if (this.life >= 2400) {//TODO 加到配置中去修改
ItemEntity leash_rope_arrow = new ItemEntity(this.level(), this.position().x, this.position().y, this.position().z, ModItemRegister.LEASH_ROPE_ARROW.get().getDefaultInstance());
this.level().addFreshEntity(leash_rope_arrow);
this.discard();
}
}
@Override
protected void onHitBlock(@NotNull BlockHitResult pResult) {
if(!level().isClientSide) {
if (getOwner() instanceof PlayerLeashable pL) {
if (this.level().getBlockState(pResult.getBlockPos()).is(BlockTags.FENCES)) {
Entity leashDataEntity = PlayerLeashable.getLeashDataEntity((ServerPlayer) getOwner(), (ServerLevel) level());
if(leashDataEntity != null) pL.dropLeash(true, true);
Entity leashKnotFence = PlayerLeashable.createLeashKnotFence((ServerLevel) this.level(), pResult.getBlockPos());
pL.setLeashedTo(leashKnotFence, true);
ItemEntity arrow = new ItemEntity(this.level(), this.position().x, this.position().y, this.position().z, Items.ARROW.getDefaultInstance());
this.level().addFreshEntity(arrow);
discard();
}
}
}
super.onHitBlock(pResult);
}
@Override
protected void onHitEntity(@NotNull EntityHitResult pResult) {
if(!level().isClientSide()){
if(pResult.getEntity() instanceof LivingEntity){
if (getOwner() instanceof PlayerLeashable pL) {
Entity leashDataEntity = PlayerLeashable.getLeashDataEntity((ServerPlayer) getOwner(), (ServerLevel) level());
if(leashDataEntity != null) pL.dropLeash(true, true);
pL.setLeashedTo(pResult.getEntity(), true);
ItemEntity arrow = new ItemEntity(this.level(), this.position().x, this.position().y, this.position().z, Items.ARROW.getDefaultInstance());
this.level().addFreshEntity(arrow);
discard();
}
}
}
super.onHitEntity(pResult);
}
}

View File

@ -0,0 +1,28 @@
package com.r3944realms.leashedplayer.content.entities;
import com.r3944realms.leashedplayer.LeashedPlayer;
import net.minecraft.core.registries.Registries;
import net.minecraft.world.entity.EntityType;
import net.minecraft.world.entity.MobCategory;
import net.neoforged.bus.api.IEventBus;
import net.neoforged.neoforge.registries.DeferredHolder;
import net.neoforged.neoforge.registries.DeferredRegister;
public class ModEntityRegister {
public static final DeferredRegister<EntityType<?>> ENTITY_TYPE = DeferredRegister.create(Registries.ENTITY_TYPE, LeashedPlayer.MOD_ID);
public static final DeferredHolder<EntityType<?> ,EntityType<LeashRopeArrow>> LEASH_ROPE_ARROW = ENTITY_TYPE.register(
"leash_rope_arrow",
() -> EntityType.Builder.<LeashRopeArrow>of(LeashRopeArrow::new, MobCategory.MISC)
.sized(0.5F, 0.5F)
.eyeHeight(0.13F)
.clientTrackingRange(4)
.updateInterval(20)
.build("leash_rope_arrow")
);
public static String getEntityNameKey(String entityName) {
return "entity." + LeashedPlayer.MOD_ID + "." + entityName;
}
public static void register(IEventBus eventBus) {
ENTITY_TYPE.register(eventBus);
}
}

View File

@ -0,0 +1,29 @@
package com.r3944realms.leashedplayer.content.items;
import net.minecraft.ChatFormatting;
import net.minecraft.network.chat.Component;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.entity.projectile.AbstractArrow;
import net.minecraft.world.item.ArrowItem;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.Level;
import org.jetbrains.annotations.NotNull;
import javax.annotation.Nullable;
public class LeashRopeArrowItem extends ArrowItem {
public static final String descKey = "item.leash_rope_arrow.description";
public LeashRopeArrowItem(Item.Properties pProperties) {
super(pProperties);
}
public @NotNull AbstractArrow createArrow(@NotNull Level pLevel, @NotNull ItemStack pAmmo, @NotNull LivingEntity pShooter, @Nullable ItemStack pWeapon) {
return new com.r3944realms.leashedplayer.content.entities.LeashRopeArrow(pShooter, pLevel, pAmmo.copyWithCount(1), pWeapon);
}
@Override
public @NotNull Component getDescription() {
return Component.translatable(descKey).withStyle(ChatFormatting.GRAY);
}
}

View File

@ -0,0 +1,37 @@
package com.r3944realms.leashedplayer.content.items;
import com.r3944realms.leashedplayer.LeashedPlayer;
import net.minecraft.core.registries.Registries;
import net.minecraft.network.chat.Component;
import net.minecraft.world.item.CreativeModeTab;
import net.minecraft.world.item.CreativeModeTabs;
import net.minecraft.world.item.Items;
import net.neoforged.bus.api.IEventBus;
import net.neoforged.neoforge.registries.DeferredRegister;
import org.jetbrains.annotations.NotNull;
import java.util.function.Supplier;
public class ModCreativeTab {
public static final DeferredRegister<CreativeModeTab> CREATIVE_MODE_TABS =
DeferredRegister.create(Registries.CREATIVE_MODE_TAB, LeashedPlayer.MOD_ID);
public static final String LEASHED_PLAYER_TAB_STRING = "creativetab." + LeashedPlayer.MOD_ID;
public static final String LEASHED_PLAYER_ITEM = "leashedplayer_tab";
public static final Supplier<CreativeModeTab> TEST_TAB = CREATIVE_MODE_TABS.register(LEASHED_PLAYER_ITEM,() -> CreativeModeTab.builder()
.withTabsBefore(CreativeModeTabs.COMBAT)
.title(Component.translatable(getCreativeMod(LEASHED_PLAYER_ITEM)))
.icon(() -> ModItemRegister.LEASH_ROPE_ARROW.get().getDefaultInstance())
.displayItems(((pParameters, pOutput) -> {
pOutput.accept(Items.LEAD);
pOutput.accept(ModItemRegister.LEASH_ROPE_ARROW.get());
})).build());
public static String getCreativeMod(@NotNull String tabs) {
return LEASHED_PLAYER_TAB_STRING + "." + tabs;
}
public static void register(IEventBus eventBus) {
CREATIVE_MODE_TABS.register(eventBus);
}
}

View File

@ -0,0 +1,33 @@
package com.r3944realms.leashedplayer.content.items;
import com.r3944realms.leashedplayer.LeashedPlayer;
import net.minecraft.core.registries.BuiltInRegistries;
import net.minecraft.world.item.Item;
import net.neoforged.bus.api.IEventBus;
import net.neoforged.neoforge.registries.DeferredRegister;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Supplier;
public class ModItemRegister {
public static final DeferredRegister<Item> ITEMS =
DeferredRegister.create(BuiltInRegistries.ITEM, LeashedPlayer.MOD_ID);
public static final List<Supplier<Item>> ITEM_SUPPLIER = new ArrayList<>();
public static final Supplier<Item> LEASH_ROPE_ARROW = ModItemRegister.register("leash_rope_arrow",
() -> new LeashRopeArrowItem(new Item.Properties().stacksTo(16))
);
public static Supplier<Item> register(String name, Supplier<Item> supplier) {
return register(name, supplier, true);
}
public static Supplier<Item> register(String name, Supplier<Item> supplier, boolean shouldJoinSupplierLists) {
Supplier<Item> supplierItem = ITEMS.register(name, supplier);
if(shouldJoinSupplierLists) ITEM_SUPPLIER.add(supplierItem);
return supplierItem;
}
public static void register(IEventBus eventBus) {
ITEMS.register(eventBus);
}
}

View File

@ -1,8 +1,13 @@
package com.r3944realms.leashedplayer.datagen.LanguageAndOtherData;
import com.r3944realms.leashedplayer.content.commands.LeashCommand;
import com.r3944realms.leashedplayer.content.entities.LeashRopeArrow;
import com.r3944realms.leashedplayer.content.entities.ModEntityRegister;
import com.r3944realms.leashedplayer.content.gamerules.Server.CreateLeashFenceKnotEntityIfAbsent;
import com.r3944realms.leashedplayer.content.gamerules.Server.TeleportWithLeashedPlayers;
import com.r3944realms.leashedplayer.content.items.LeashRopeArrowItem;
import com.r3944realms.leashedplayer.content.items.ModCreativeTab;
import com.r3944realms.leashedplayer.content.items.ModItemRegister;
import com.r3944realms.leashedplayer.utils.Enum.LanguageEnum;
import com.r3944realms.leashedplayer.utils.Enum.ModPartEnum;
import net.minecraft.world.item.Item;
@ -12,24 +17,36 @@ import org.jetbrains.annotations.NotNull;
import javax.annotation.Nullable;
import java.util.function.Supplier;
import static com.r3944realms.leashedplayer.content.items.ModCreativeTab.LEASHED_PLAYER_ITEM;
public enum ModLangKeyValue {
//ITEM
ITEM_LEASH_ROPE_ARROW(ModItemRegister.LEASH_ROPE_ARROW, ModPartEnum.ITEM, "Leash Rope Arrow", "拴绳箭", "拴繩箭", true),
//ITEM_DESC
DESC_ITEM_LEASH_ROPE_ARROW(LeashRopeArrowItem.descKey, ModPartEnum.DESCRIPTION, "Arrows with ropes attached?","带有拴绳的箭矢?", "帶有拴繩的箭矢?", false),
//ENTITY
LEASH_ROPE_ARROW(ModEntityRegister.getEntityNameKey("leash_rope_arrow"), ModPartEnum.ENTITY, "Leash Rope Arrow", "拴绳箭", "拴繩箭", false),
//CREATIVE_TAB
CREATIVE_TAB_NAME(ModCreativeTab.getCreativeMod(LEASHED_PLAYER_ITEM), ModPartEnum.CREATIVE_TAB, "Leashed Player","可拴玩家", "可拴玩家", false),
//COMMAND_MESSAGE
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_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_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_CLEAR(LeashCommand.LEASH_DATA_CLEAR, ModPartEnum.COMMAND, "%1$s's LeashData(LeashHolderEntity: %2$s) now is clear", "%1$s的拴绳数据拴绳持有者实体%2$s现在已清除", "%1$s的拴繩數據拴繩持有者實體%2$s現在已清除", false),
MESSAGE_LEASH_SET_FAILED_FORBID_SAME_ENTITY(LeashCommand.LEASH_DATA_SET_FAILED_FORBID_SAME_ENTITY, ModPartEnum.COMMAND, "Prohibit setting the same entity","禁止设置同一实体", "禁止設置同一實體", false),
MESSAGE_LEASH_CLEAR_FAILED(LeashCommand.LEASH_DATA_CLEAR_FAILED_NO_DATA, ModPartEnum.COMMAND, "%1$s has no LeashData can be clear", "%1$s沒有拴绳数据可清除", "%1$s沒有拴繩數據實體可被清除", false),
//GAME_RULE_NAME
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),
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, "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),
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;

View File

@ -1,7 +1,7 @@
package com.r3944realms.leashedplayer.datagen;
import com.r3944realms.leashedplayer.LeashedPlayer;
import com.r3944realms.leashedplayer.datagen.provider.ModLanguageProvider;
import com.r3944realms.leashedplayer.datagen.provider.*;
import com.r3944realms.leashedplayer.utils.Enum.LanguageEnum;
import net.minecraft.core.HolderLookup;
import net.minecraft.data.DataProvider;
@ -24,6 +24,9 @@ public class ModDataGeneratorHandler {
addLanguage(event, LanguageEnum.SimpleChinese, "zh_cn");
addLanguage(event, LanguageEnum.TraditionalChinese, "zh_tw");
addLanguage(event, LanguageEnum.LiteraryChinese, "lzh");
ItemModelGenerator(event, existingFileHelper);
RecipeGenerator(event, HolderFolder);
ModTagsProvider(event, event.getLookupProvider(), existingFileHelper);
}
private static void addLanguage(GatherDataEvent event, LanguageEnum language, String lan_regex){
event.getGenerator().addProvider(
@ -31,4 +34,28 @@ public class ModDataGeneratorHandler {
(DataProvider.Factory<ModLanguageProvider>) pOutput -> new ModLanguageProvider(pOutput, LeashedPlayer.MOD_ID, language)
);
}
private static void ItemModelGenerator(GatherDataEvent event, ExistingFileHelper helper) {
event.getGenerator().addProvider(
event.includeClient(),
(DataProvider.Factory<ModItemModelProvider>) pOutput -> new ModItemModelProvider(pOutput, LeashedPlayer.MOD_ID, helper)
);
}
private static void RecipeGenerator(GatherDataEvent event, CompletableFuture<HolderLookup.Provider> future) {
event.getGenerator().addProvider(
event.includeServer(),
(DataProvider.Factory<ModRecipeProvider>) pOutput -> new ModRecipeProvider(pOutput, future)
);
}
private static void ModTagsProvider(GatherDataEvent event, CompletableFuture<HolderLookup.Provider> completableFuture, ExistingFileHelper helper) {
ModBlockTagProvider modBlockTagProvider = event.getGenerator().addProvider(
event.includeServer(),
(DataProvider.Factory<ModBlockTagProvider>) pOutput ->
new ModBlockTagProvider(pOutput, completableFuture, LeashedPlayer.MOD_ID, helper)
);
event.getGenerator().addProvider(
event.includeServer(),
(DataProvider.Factory<ModItemTagProvider>) pOutput ->
new ModItemTagProvider(pOutput, completableFuture, modBlockTagProvider.contentsGetter(), helper)
);
}
}

View File

@ -0,0 +1,21 @@
package com.r3944realms.leashedplayer.datagen.provider;
import net.minecraft.core.HolderLookup;
import net.minecraft.data.PackOutput;
import net.neoforged.neoforge.common.data.BlockTagsProvider;
import net.neoforged.neoforge.common.data.ExistingFileHelper;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.concurrent.CompletableFuture;
public class ModBlockTagProvider extends BlockTagsProvider {
public ModBlockTagProvider(PackOutput output, CompletableFuture<HolderLookup.Provider> lookupProvider, String modId, @Nullable ExistingFileHelper existingFileHelper) {
super(output, lookupProvider, modId, existingFileHelper);
}
@Override
protected void addTags(HolderLookup.@NotNull Provider pProvider) {
}
}

View File

@ -0,0 +1,43 @@
package com.r3944realms.leashedplayer.datagen.provider;
import com.r3944realms.leashedplayer.datagen.LanguageAndOtherData.ModLangKeyValue;
import net.minecraft.data.PackOutput;
import net.minecraft.world.item.Item;
import net.neoforged.neoforge.client.model.generators.ItemModelProvider;
import net.neoforged.neoforge.common.data.ExistingFileHelper;
import java.util.ArrayList;
import java.util.List;
public class ModItemModelProvider extends ItemModelProvider {
private static List<Item> objectList;
public ModItemModelProvider(PackOutput output, String modId, ExistingFileHelper existingFileHelper) {
super(output, modId, existingFileHelper);
objectList = new ArrayList<>();
init();
}
private void init() {
for(ModLangKeyValue obj : ModLangKeyValue.values()) {
if(!obj.isDefaultItem()) continue;
objectList.add(obj.getItem());
}
}
/**
* @implNote 先有纹理才会成功构建
*/
private void DefaultModItemModelRegister() {
objectList.forEach(this::basicItem);
}
private void AdvancedModItemModelRegister() {
/*手动生成更快,其实*/
}
@Override
protected void registerModels() {
// 注册默认材质物品模型
DefaultModItemModelRegister();
// 注册进阶材质物品模型非模板大量需要可以用BlockBench生成的就行了
AdvancedModItemModelRegister();
}
}

View File

@ -0,0 +1,26 @@
package com.r3944realms.leashedplayer.datagen.provider;
import com.r3944realms.leashedplayer.LeashedPlayer;
import com.r3944realms.leashedplayer.content.items.ModItemRegister;
import net.minecraft.core.HolderLookup;
import net.minecraft.data.PackOutput;
import net.minecraft.data.tags.ItemTagsProvider;
import net.minecraft.tags.ItemTags;
import net.minecraft.world.level.block.Block;
import net.neoforged.neoforge.common.data.ExistingFileHelper;
import org.jetbrains.annotations.NotNull;
import java.util.concurrent.CompletableFuture;
public class ModItemTagProvider extends ItemTagsProvider {
public ModItemTagProvider(PackOutput pOutput, CompletableFuture<HolderLookup.Provider> pLookupProvider, CompletableFuture<TagLookup<Block>> pBlockTags, ExistingFileHelper pExistingFileHelper) {
super(pOutput, pLookupProvider, pBlockTags, LeashedPlayer.MOD_ID, pExistingFileHelper);
}
@Override
protected void addTags(HolderLookup.@NotNull Provider pProvider) {
this.tag(ItemTags.ARROWS)
.add(ModItemRegister.LEASH_ROPE_ARROW.get());
}
}

View File

@ -0,0 +1,29 @@
package com.r3944realms.leashedplayer.datagen.provider;
import com.r3944realms.leashedplayer.content.items.ModItemRegister;
import net.minecraft.core.HolderLookup;
import net.minecraft.data.PackOutput;
import net.minecraft.data.recipes.*;
import net.minecraft.world.item.Items;
import org.jetbrains.annotations.NotNull;
import java.util.concurrent.CompletableFuture;
public class ModRecipeProvider extends RecipeProvider {
public ModRecipeProvider(PackOutput pOutput, CompletableFuture<HolderLookup.Provider> future) {
super(pOutput, future);
}
@Override
protected void buildRecipes(@NotNull RecipeOutput pRecipeOutput) {
ShapelessRecipeBuilder.shapeless(RecipeCategory.MISC, ModItemRegister.LEASH_ROPE_ARROW.get(),1)
.requires(Items.LEAD)
.requires(Items.ARROW)
.unlockedBy("has_lead",has(Items.LEAD))
.save(pRecipeOutput);
}
}

View File

@ -4,11 +4,14 @@ import com.r3944realms.leashedplayer.modInterface.PlayerLeashable;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.Leashable;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Redirect;
@Mixin(Entity.class)
public class MixinEntity {
public abstract class MixinEntity {
@Shadow public abstract void igniteForSeconds(float pSeconds);
/**
* 这里重定向当实体类实现了{@link PlayerLeashable}接口时<br/>
* 阻止原版的{@link Leashable} 的tickLeash方法调用将其<br/>
@ -25,4 +28,5 @@ public class MixinEntity {
Leashable.tickLeash(entity);
}
}
}

View File

@ -1,5 +1,9 @@
package com.r3944realms.leashedplayer.mixin.both;
import com.r3944realms.leashedplayer.LeashedPlayer;
import com.r3944realms.leashedplayer.config.LeashPlayerCommonConfig;
import com.r3944realms.leashedplayer.content.commands.LeashCommand;
import com.r3944realms.leashedplayer.content.entities.LeashRopeArrow;
import com.r3944realms.leashedplayer.modInterface.ILivingEntityExtension;
import com.r3944realms.leashedplayer.modInterface.PlayerLeashable;
import net.minecraft.nbt.CompoundTag;
@ -7,10 +11,12 @@ import net.minecraft.network.syncher.EntityDataAccessor;
import net.minecraft.network.syncher.EntityDataSerializers;
import net.minecraft.network.syncher.SynchedEntityData;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.util.Mth;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.EntityType;
import net.minecraft.world.entity.Leashable;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.entity.ai.attributes.Attributes;
import net.minecraft.world.entity.decoration.LeashFenceKnotEntity;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.level.Level;
@ -22,11 +28,11 @@ import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import javax.annotation.Nullable;
import java.util.Objects;
import java.util.function.Consumer;
@Mixin(Player.class)
public abstract class MixinPlayer extends LivingEntity implements PlayerLeashable {
@Unique
@Nullable
private LeashData Pl$LeashData;//Data
@ -53,41 +59,55 @@ public abstract class MixinPlayer extends LivingEntity implements PlayerLeashabl
}
@Unique
private static void Pl$UpdateLeash(Entity holderEntity, Entity restrainedEntity) {
if(holderEntity == null || holderEntity.level() != restrainedEntity.level())
if (holderEntity == null || holderEntity.level() != restrainedEntity.level()) {
return;
float leashLength = 6.0f;
if(restrainedEntity instanceof ILivingEntityExtension iEntity) {
//获取长度
float leashLengthFormValue = iEntity.getLeashLength();
leashLength = leashLengthFormValue > 6 ? leashLengthFormValue : 6;
}
//两者距离
float distance = holderEntity.distanceTo(restrainedEntity);
//大于长度情况
if(distance > leashLength) {
//作用对象实体所坐载体还是实体根据isPassenger来判断
Entity applyMovementEntity = restrainedEntity.isPassenger() ? restrainedEntity.getVehicle() : restrainedEntity;
if(applyMovementEntity != null){
double dX = (holderEntity.getX() - applyMovementEntity.getX()) / (double) distance;
double dY = (holderEntity.getY() - applyMovementEntity.getY()) / (double) distance;
double dZ = (holderEntity.getZ() - applyMovementEntity.getZ()) / (double) distance;
//给予作用实体其向holderEntity的一个速度动量
applyMovementEntity.setDeltaMovement(
applyMovementEntity.getDeltaMovement().add(
Math.copySign(dX * dX * 0.4d, dX),
Math.copySign(dY * dY * 0.4d, dY),
Math.copySign(dZ * dZ * 0.4d, dZ)
)
);
//刹车避免偏激移动
Whimsy$Brake(applyMovementEntity, 1, 1, 1);
}
}
//降低坠落伤害
float leashLength = LeashCommand.MIN_VALUE;
if (restrainedEntity instanceof ILivingEntityExtension iEntity) {
// 获取绳长
float leashLengthFormValue = iEntity.getLeashLength();
leashLength = leashLengthFormValue > LeashCommand.MIN_VALUE ? leashLengthFormValue : LeashCommand.MIN_VALUE;
}
// 两者距离
float distance = holderEntity.distanceTo(restrainedEntity);
Entity applyMovementEntity = restrainedEntity.isPassenger() ? restrainedEntity.getVehicle() : restrainedEntity;
// 仅当距离大于绳长时施加拉力
if (applyMovementEntity != null && distance > leashLength) {
// 计算朝向持有者的拉力方向
double dX = (holderEntity.getX() - applyMovementEntity.getX()) / (double) distance;
double dY = (holderEntity.getY() - applyMovementEntity.getY()) / (double) distance;
double dZ = (holderEntity.getZ() - applyMovementEntity.getZ()) / (double) distance;
// 拉力大小距离越远拉力越强但施加一个最大限制
double pullStrength = Math.min((distance - leashLength) * 0.1, 1.0); // 限制最大拉力
applyMovementEntity.setDeltaMovement(
applyMovementEntity.getDeltaMovement().add(
dX * pullStrength,
dY * pullStrength,
dZ * pullStrength
)
);
// 控制速度不要过快避免偏激移动
Whimsy$Brake(applyMovementEntity, entity -> {
Vec3 deltaMovement = entity.getDeltaMovement();
entity.setDeltaMovement(
Math.min(Math.abs(deltaMovement.x), 1.0) * Math.signum(deltaMovement.x),
Math.min(Math.abs(deltaMovement.y), 1.0) * Math.signum(deltaMovement.y),
Math.min(Math.abs(deltaMovement.z), 1.0) * Math.signum(deltaMovement.z)
);
entity.hurtMarked = true;
});
}
// 降低坠落伤害
restrainedEntity.checkSlowFallDistance();
}
/**
* 刹车
* @param pEntity 刹车的实体
@ -98,9 +118,9 @@ public abstract class MixinPlayer extends LivingEntity implements PlayerLeashabl
@Unique
private static void Whimsy$Brake(Entity pEntity, double pMaxX, double pMaxY, double pMaxZ) {
Vec3 deltaMovement = pEntity.getDeltaMovement();
double dX = deltaMovement.x > pMaxX ? 0 : deltaMovement.x;
double dY = deltaMovement.y > pMaxY ? 0 : deltaMovement.y;
double dZ = deltaMovement.z > pMaxZ ? 0 : deltaMovement.z;
double dX = Math.abs(deltaMovement.x) > pMaxX ? 0 : deltaMovement.x;
double dY = Math.abs(deltaMovement.y) > pMaxY ? 0 : deltaMovement.y;
double dZ = Math.abs(deltaMovement.z) > pMaxZ ? 0 : deltaMovement.z;
pEntity.setDeltaMovement(dX, dY,dZ);
pEntity.hurtMarked = true;
}
@ -115,9 +135,9 @@ public abstract class MixinPlayer extends LivingEntity implements PlayerLeashabl
if(pOpt == null) {
consumer = entity -> {
Vec3 deltaMovement = entity.getDeltaMovement();
double dX = deltaMovement.x > 1 ? 0 : deltaMovement.x;
double dY = deltaMovement.y > 1 ? 0 : deltaMovement.y;
double dZ = deltaMovement.z > 1 ? 0 : deltaMovement.z;
double dX = Math.abs(deltaMovement.x) > 1 ? 0 : deltaMovement.x;
double dY = Math.abs(deltaMovement.y) > 1 ? 0 : deltaMovement.y;
double dZ = Math.abs(deltaMovement.z) > 1 ? 0 : deltaMovement.z;
entity.setDeltaMovement(dX, dY,dZ);
entity.hurtMarked = true;
};
@ -130,27 +150,34 @@ public abstract class MixinPlayer extends LivingEntity implements PlayerLeashabl
if(this.Pl$LeashData == null) return;//没有Data直接退出
//info -> Holder整理
Pl$RestoreLeashFormSave();
//默认值设为6.0f距离
float leashLength = 6.0f;
//默认值设为
float leashLength = LeashCommand.MIN_VALUE;
Entity entity = this.Pl$LeashData.leashHolder;
//保存数据
saveLeashData(Pl$LeashData);
if(this instanceof ILivingEntityExtension iEntityExtension) {
//获取设定值
float leashLengthSelf = iEntityExtension.getLeashLength();
leashLength = leashLengthSelf > 6 ? leashLengthSelf : 6;
leashLength = leashLengthSelf > LeashCommand.MIN_VALUE ? leashLengthSelf : LeashCommand.MIN_VALUE;
}
if (entity != null) {
if(!isAlive() || !entity.isAlive() || distanceTo(entity) > Math.max(leashLength * 2.0f, 10.0f)){
float breakDistanceTime = (entity instanceof LeashRopeArrow) ? LeashedPlayer.M1() * LeashedPlayer.M2() : LeashedPlayer.M1();
if(!isAlive() || !entity.isAlive() || distanceTo(entity) > Math.max(leashLength * breakDistanceTime, LeashCommand.MIN_VALUE * breakDistanceTime)){
//玩家死亡 持有者不存在 距离大于设定值的2倍长度2倍若低于10格则选10格
// 则取消拴绳关系并掉落拴绳
dropLeash(true, true);
} else if(distanceTo(entity) > leashLength * 1.3f) {
//大于1.3倍绳长则会让其跳跃<1.25格阻拦情况下跳跃阻拦
jumpFromGround();
boolean shouldDrop = !(entity instanceof LeashRopeArrow);
dropLeash(true, shouldDrop);
} else if(distanceTo(entity) > leashLength * 0.65f * breakDistanceTime && entity.onGround()) {
//大于1.3倍绳长则会让其跳跃<1.25格阻拦情况下跳跃阻拦//TODO:待擴展
Entity applyMovementEntity = this.isPassenger() ? this.getVehicle() : this;
if(applyMovementEntity instanceof LivingEntity applyMovementLivingEntity) {
applyMovementLivingEntity.jumpFromGround();
}
}
}
}
@Override
public Entity getLeashHolder() {
if (Pl$LeashData == null) return null;
@ -176,7 +203,8 @@ public abstract class MixinPlayer extends LivingEntity implements PlayerLeashabl
if(Pl$LeashData.leashHolder != null) {//且LeashHolder不为null则直接用它
setLeashedTo(Pl$LeashData.leashHolder, true);
return;
} return;
}
return;
}
if(this.Pl$LeashData.delayedLeashInfo.left().isPresent()) {

View File

@ -1,13 +1,17 @@
package com.r3944realms.leashedplayer.mixin.client;
import com.mojang.blaze3d.vertex.PoseStack;
import net.minecraft.client.player.AbstractClientPlayer;
import net.minecraft.client.renderer.MultiBufferSource;
import net.minecraft.client.renderer.entity.EntityRenderer;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.phys.Vec3;
import org.jetbrains.annotations.NotNull;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.Redirect;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(EntityRenderer.class)
public abstract class MixinEntityRenderer {
@ -22,8 +26,9 @@ public abstract class MixinEntityRenderer {
private @NotNull Vec3 ret(Entity instance, float pPartialTick) {
if(instance instanceof AbstractClientPlayer) {
//为了使拴绳在在第三视角下位于玩家脖子处
return instance.getLeashOffset(pPartialTick).add(0, -0.2, -0.2);
return instance.getLeashOffset(pPartialTick).add(0, -0.2, -0.2);//TODO:待擴展Vec3
}
return instance.getLeashOffset(pPartialTick);//非实现这个接口则不变
}
}

View File

@ -2,6 +2,8 @@ package com.r3944realms.leashedplayer.mixin.client;
import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.datafixers.util.Either;
import com.r3944realms.leashedplayer.LeashedPlayer;
import com.r3944realms.leashedplayer.content.entities.LeashRopeArrow;
import com.r3944realms.leashedplayer.modInterface.ILivingEntityExtension;
import com.r3944realms.leashedplayer.modInterface.IPlayerRendererExtension;
import com.r3944realms.leashedplayer.modInterface.PlayerLeashable;
@ -61,13 +63,13 @@ public abstract class MixinLevelRenderer {
for(Entity entity : this.level.entitiesForRendering()) {
//对于玩家实体拴绳渲染从第一人称视角
if (entity instanceof AbstractClientPlayer abstractClientPlayer) {
if(!(pCamera.getEntity() instanceof AbstractClientPlayer)) return;
if(!(pCamera.getEntity() instanceof AbstractClientPlayer)) continue;
Minecraft mc = Minecraft.getInstance();
PlayerRenderer playerRenderer = (PlayerRenderer) mc.getEntityRenderDispatcher().getRenderer(abstractClientPlayer);
IPlayerRendererExtension playerRendererExtension = (IPlayerRendererExtension) playerRenderer;
if (mc.options.getCameraType().isFirstPerson()) {
Leashable.LeashData leashDataFromEntityData = ((PlayerLeashable) abstractClientPlayer).getLeashDataFromEntityData();
if(leashDataFromEntityData == null) return;
if(leashDataFromEntityData == null) continue;
Either<UUID, BlockPos> delayedLeashInfo = leashDataFromEntityData.delayedLeashInfo;
if(delayedLeashInfo != null) {
float partialTickTime = pCamera.getPartialTickTime();
@ -92,7 +94,7 @@ public abstract class MixinLevelRenderer {
if (playerByUUID != null) {
playerRendererExtension.renderLeashForCamera(pCamera, partialTickTime, poseStack, multibuffersource$buffersource, playerByUUID);
} else {
float MaxLeashLength = ((ILivingEntityExtension) abstractClientPlayer).getLeashLength() * 2f;
float MaxLeashLength = ((ILivingEntityExtension) abstractClientPlayer).getLeashLength() * LeashedPlayer.M1() * LeashedPlayer.M2();
List<Entity> entities = level.getEntities(
null,
new AABB(
@ -112,9 +114,13 @@ public abstract class MixinLevelRenderer {
}
}
if (holder != null) {
playerRendererExtension.renderLeashForCamera(pCamera, partialTickTime, poseStack, multibuffersource$buffersource, holder);
if(holder instanceof LeashRopeArrow) {
playerRendererExtension.renderLeashForCamera(pCamera, partialTickTime, poseStack, multibuffersource$buffersource, holder, new Vec3(0.,-0.09, 0));//TODO: 待擴展Vec3
}
else playerRendererExtension.renderLeashForCamera(pCamera, partialTickTime, poseStack, multibuffersource$buffersource, holder);
}
}
break;
}
}
}

View File

@ -3,6 +3,8 @@ 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.LeashedPlayer;
import com.r3944realms.leashedplayer.content.entities.LeashRopeArrow;
import com.r3944realms.leashedplayer.modInterface.ILivingEntityExtension;
import com.r3944realms.leashedplayer.modInterface.IPlayerRendererExtension;
import com.r3944realms.leashedplayer.modInterface.PlayerLeashable;
@ -61,9 +63,10 @@ public abstract class MixinPlayerRenderer extends LivingEntityRenderer<AbstractC
if (playerByUUID != null) {
renderLeash(pEntity, pPartialTicks, pPoseStack, pBuffer, playerByUUID);
} else {
float MaxLeashLength = ((ILivingEntityExtension) pEntity).getLeashLength() * 2f;
float breakDistanceTime = (leashDataFromEntityData.leashHolder instanceof LeashRopeArrow) ? LeashedPlayer.M1() * LeashedPlayer.M2() : LeashedPlayer.M1();
float MaxLeashLength = ((ILivingEntityExtension) pEntity).getLeashLength() * breakDistanceTime;
List<Entity> entities = level.getEntities(
null,
null,
new AABB(
pEntity.getX() - MaxLeashLength,
pEntity.getY() - MaxLeashLength,
@ -81,7 +84,10 @@ public abstract class MixinPlayerRenderer extends LivingEntityRenderer<AbstractC
}
}
if (holder != null) {
renderLeash(pEntity, pPartialTicks, pPoseStack, pBuffer, holder);
if(holder instanceof LeashRopeArrow) {
renderLeash(pEntity, pPartialTicks, pPoseStack, pBuffer, holder, new Vec3(0,-0.09, 0));//TODO: 待擴展Vec3
}
else renderLeash(pEntity, pPartialTicks, pPoseStack, pBuffer, holder);
}
}
}
@ -156,6 +162,7 @@ public abstract class MixinPlayerRenderer extends LivingEntityRenderer<AbstractC
* <h1> 总结 </h1>
* 这些数学运算主要用于计算实体在三维空间中的位置和方向以确保在渲染链状结构如拴住的绳索链条能够跟随实体的移动和旋转并正确显示在图形编程中这些计算非常常见尤其是在处理旋转插值和光照效果时
*/
@SuppressWarnings("AddedMixinMembersNamePattern")
@Unique
public <E extends net.minecraft.world.entity.Entity> void renderLeashForCamera(
@ -163,13 +170,14 @@ public abstract class MixinPlayerRenderer extends LivingEntityRenderer<AbstractC
float partialTick,
com.mojang.blaze3d.vertex.PoseStack poseStack,
net.minecraft.client.renderer.MultiBufferSource bufferSource,
E leashHolder
E leashHolder,
Vec3 holderOffset
) {
poseStack.pushPose();
// 获得绳索持有者的位置
Vec3 leashHolderPosition = leashHolder.getRopeHoldPosition(partialTick);
Vec3 leashHolderPosition = leashHolder.getRopeHoldPosition(partialTick).add(holderOffset);
// 获取当前观察的实体
Entity cameraEntity = camera.getEntity();
@ -224,5 +232,42 @@ public abstract class MixinPlayerRenderer extends LivingEntityRenderer<AbstractC
poseStack.popPose();
}
protected <E extends Entity> void renderLeash(AbstractClientPlayer pEntity, float pPartialTick, PoseStack pPoseStack, MultiBufferSource pBufferSource, E pLeashHolder, Vec3 holderOffset) {
pPoseStack.pushPose();
Vec3 vec3 = pLeashHolder.getRopeHoldPosition(pPartialTick).add(holderOffset);
double d0 = (double)(pEntity.getPreciseBodyRotation(pPartialTick) * (float) (Math.PI / 180.0)) + (Math.PI / 2);
Vec3 vec31 = pEntity.getLeashOffset(pPartialTick);
double d1 = Math.cos(d0) * vec31.z + Math.sin(d0) * vec31.x;
double d2 = Math.sin(d0) * vec31.z - Math.cos(d0) * vec31.x;
double d3 = Mth.lerp(pPartialTick, pEntity.xo, pEntity.getX()) + d1;
double d4 = Mth.lerp(pPartialTick, pEntity.yo, pEntity.getY()) + vec31.y;
double d5 = Mth.lerp(pPartialTick, pEntity.zo, pEntity.getZ()) + d2;
pPoseStack.translate(d1, vec31.y, d2);
float f = (float)(vec3.x - d3);
float f1 = (float)(vec3.y - d4);
float f2 = (float)(vec3.z - d5);
float f3 = 0.025F;
VertexConsumer vertexconsumer = pBufferSource.getBuffer(RenderType.leash());
Matrix4f matrix4f = pPoseStack.last().pose();
float f4 = Mth.invSqrt(f * f + f2 * f2) * 0.025F / 2.0F;
float f5 = f2 * f4;
float f6 = f * f4;
BlockPos blockpos = BlockPos.containing(pEntity.getEyePosition(pPartialTick));
BlockPos blockpos1 = BlockPos.containing(pLeashHolder.getEyePosition(pPartialTick));
int i = this.getBlockLightLevel(pEntity, blockpos);
int j = 0;
int k = pEntity.level().getBrightness(LightLayer.SKY, blockpos);
int l = pEntity.level().getBrightness(LightLayer.SKY, blockpos1);
for (int i1 = 0; i1 <= 24; i1++) {
addVertexPair(vertexconsumer, matrix4f, f, f1, f2, i, j, k, l, 0.025F, 0.025F, f5, f6, i1, false);
}
for (int j1 = 24; j1 >= 0; j1--) {
addVertexPair(vertexconsumer, matrix4f, f, f1, f2, i, j, k, l, 0.025F, 0.0F, f5, f6, j1, true);
}
pPoseStack.popPose();
}
}

View File

@ -1,13 +1,24 @@
package com.r3944realms.leashedplayer.modInterface;
import net.minecraft.client.Camera;
import net.minecraft.world.phys.Vec3;
public interface IPlayerRendererExtension {
<E extends net. minecraft. world. entity. Entity> void renderLeashForCamera(
default <E extends net. minecraft. world. entity. Entity> void renderLeashForCamera(
Camera pCamera,
float pPartialTick,
com.mojang.blaze3d.vertex.PoseStack pPoseStack,
net.minecraft.client.renderer.MultiBufferSource pBufferSource,
E pLeashHolder
) {
renderLeashForCamera(pCamera, pPartialTick, pPoseStack, pBufferSource, pLeashHolder, Vec3.ZERO);
}
<E extends net. minecraft. world. entity. Entity> void renderLeashForCamera(
Camera pCamera,
float pPartialTick,
com.mojang.blaze3d.vertex.PoseStack pPoseStack,
net.minecraft.client.renderer.MultiBufferSource pBufferSource,
E pLeashHolder,
Vec3 holderOffset
);
}

View File

@ -3,6 +3,7 @@ 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.server.level.ServerPlayer;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.Leashable;
import net.minecraft.world.entity.decoration.LeashFenceKnotEntity;
@ -56,6 +57,39 @@ public interface PlayerLeashable extends Leashable {
//这边覆写去掉了乘坐相关的逻辑即乘坐状态下也可以正常被栓住不影响其乘坐状态
}
@Nullable
static Entity getLeashDataEntity(@NotNull ServerPlayer serverPlayer , @NotNull ServerLevel serverLevel) {
LeashData leashDataFromEntityData = ((PlayerLeashable) serverPlayer).getLeashDataFromEntityData();
if (leashDataFromEntityData != null) {
return getLeashDataEntity(leashDataFromEntityData, serverLevel);
}
else return null;
}
static Entity getLeashDataEntityOrThrown(@NotNull ServerPlayer serverPlayer ,@NotNull ServerLevel serverLevel) throws Exception {
Entity leashedEntity = getLeashDataEntity(serverPlayer, serverLevel);
if(leashedEntity == null) throw new Exception("invalid");
else return leashedEntity;
}
@Nullable
static Entity getLeashDataEntity(@NotNull Leashable.LeashData leashDataFromEntityData, @NotNull ServerLevel level) {
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 return BlockPos.map(pos -> LeashFenceKnotEntity.getOrCreateKnot(level, pos)).orElse(null);
}
else if(leashDataFromEntityData.leashHolder != null) {
return leashDataFromEntityData.leashHolder;
}
else if(leashDataFromEntityData.delayedLeashHolderId != 0) {
return level.getEntity(leashDataFromEntityData.delayedLeashHolderId);
}
else return null;
}
static Entity getLeashDataEntityOrThrown(@NotNull Leashable.LeashData leashDataFromEntityData, @NotNull ServerLevel level) throws Exception {
if(leashDataFromEntityData.delayedLeashInfo != null) {
Optional<UUID> UUID = leashDataFromEntityData.delayedLeashInfo.left();

View File

@ -1,6 +1,12 @@
package com.r3944realms.leashedplayer.utils;
import com.r3944realms.leashedplayer.LeashedPlayer;
import com.r3944realms.leashedplayer.content.gamerules.Gamerules;
import net.neoforged.fml.loading.FMLPaths;
import java.io.File;
import static com.r3944realms.leashedplayer.utils.Logger.logger;
public class Util {
public static String getGameruleName(Class<?> clazz) {
@ -10,4 +16,25 @@ public class Util {
return Gamerules.GAMERULE_PREFIX + gamerulesName;
}
public static void configFileCreate(String[] children) {//初始化配置文件目录
File configFile = new File(FMLPaths.CONFIGDIR.get().toFile(), LeashedPlayer.MOD_ID);
if (!configFile.exists()) {
boolean mkdirSuccess = configFile.mkdirs();
if (!mkdirSuccess) {
logger.error("failed to create config directory for whimsicality");
throw new RuntimeException("failed to create config directory for " + LeashedPlayer.MOD_ID);
} else {
for (String child : children) {
File file = new File(configFile, child);
if (!file.exists()) {
boolean mkdirChildrenSuccess = file.mkdirs();
if (!mkdirChildrenSuccess) {
logger.error("failed to create " + child + " directory for +" + LeashedPlayer.MOD_ID);
throw new RuntimeException("failed to create " + child + " directory for" +LeashedPlayer.MOD_ID);
}
}
}
}
}
}
}

View File

@ -11,4 +11,6 @@ public net.minecraft.world.level.GameRules$Type <init>(Ljava/util/function/Suppl
#因为'net.minecraft.world.level.GameRules.VisitorCaller' 在 'net.minecraft.world.level.GameRules' 中不为 public。无法从外部软件包访问
public net.minecraft.world.level.GameRules$VisitorCaller #Interface
#private -> public
public net.minecraft.world.entity.Leashable$LeashData delayedLeashHolderId # delayedLeashHolderId
public net.minecraft.world.entity.Leashable$LeashData delayedLeashHolderId # delayedLeashHolderId
#private -> protect
protected net.minecraft.world.entity.projectile.AbstractArrow life # life

Binary file not shown.

After

Width:  |  Height:  |  Size: 260 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 268 B