init 1.21.5

This commit is contained in:
叁玖领域 2025-04-10 09:03:09 +08:00
commit c3bff64c61
134 changed files with 7655 additions and 0 deletions

View File

@ -0,0 +1,125 @@
package com.r3944realms.leashedplayer;
import com.r3944realms.leashedplayer.client.renderer.LeashRendererUtil;
import com.r3944realms.leashedplayer.client.renderer.PlayerLeashState;
import com.r3944realms.leashedplayer.client.renderer.entities.ChestItemLayerRenderer;
import com.r3944realms.leashedplayer.client.renderer.entities.LeashRopeArrowRenderer;
import com.r3944realms.leashedplayer.client.renderer.entities.SpectralLeashRopeArrowRenderer;
import com.r3944realms.leashedplayer.client.renderer.item.ConditionalRangeItemModel;
import com.r3944realms.leashedplayer.client.renderer.item.properties.conditional.ChargeExtend;
import com.r3944realms.leashedplayer.content.ModKeyMapping;
import com.r3944realms.leashedplayer.content.entities.ModEntityRegister;
import com.r3944realms.leashedplayer.modInterface.IPlayerRenderStateExtension;
import com.r3944realms.leashedplayer.modInterface.PlayerLeashable;
import com.r3944realms.leashedplayer.network.server.Code;
import com.r3944realms.leashedplayer.network.server.DecreaseLeashRopeLength;
import com.r3944realms.leashedplayer.network.server.IncreaseLeashRopeLength;
import com.r3944realms.leashedplayer.utils.Util;
import net.minecraft.client.Minecraft;
import net.minecraft.client.player.LocalPlayer;
import net.minecraft.client.renderer.entity.player.PlayerRenderer;
import net.minecraft.client.renderer.entity.state.PlayerRenderState;
import net.minecraft.client.resources.PlayerSkin;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.phys.HitResult;
import net.neoforged.api.distmarker.Dist;
import net.neoforged.bus.api.SubscribeEvent;
import net.neoforged.fml.common.EventBusSubscriber;
import net.neoforged.neoforge.client.event.*;
import net.neoforged.neoforge.network.PacketDistributor;
import java.util.List;
public class ClientEventHandler {
@EventBusSubscriber(value = Dist.CLIENT, bus = EventBusSubscriber.Bus.GAME, modid = LeashedPlayer.MOD_ID)
public static class Game {
@SubscribeEvent
public static void onPlayerRendererEventPre(RenderPlayerEvent.Post event) {
PlayerRenderState renderState = event.getRenderState();
PlayerLeashState playerLeashState = ((IPlayerRenderStateExtension) renderState).getPlayerLeashState();
if(playerLeashState != null) {
if (playerLeashState.vanilaLeashState != null)
LeashRendererUtil.renderLeash(event.getPoseStack(), event.getMultiBufferSource(), playerLeashState.vanilaLeashState);
else
LeashRendererUtil.renderLeash(event.getPoseStack(), event.getMultiBufferSource(), renderState);
}
}
@SubscribeEvent
public static void onKetBoardInput(InputEvent.Key event) {
Minecraft minecraft = Minecraft.getInstance();
LocalPlayer player = minecraft.player;
assert player != null;
if (ModKeyMapping.KEY_ADD_LEASH_LENGTH.isDown()) {
PlayerLeashable playerLeashable = (PlayerLeashable) player;
if (playerLeashable.getLeashDataFromEntityData() == null) {
assert minecraft.level != null;
List<HitResult> refLookAtEntityHitResult = Util.getRefLookAtEntityHitResult(player, minecraft.level, 32, entity -> entity instanceof LivingEntity);
Entity theNearestEntityFromHitResultList = Util.getTheNearestEntityFromHitResultList(player, refLookAtEntityHitResult);
if(theNearestEntityFromHitResultList != null) {
if (theNearestEntityFromHitResultList instanceof Player refPlayer) {
PacketDistributor.sendToServer(new IncreaseLeashRopeLength(Code.OTHER_ST, refPlayer.getStringUUID()));
}
}
}
if (playerLeashable.getLeashDataFromEntityData() != null) {
PacketDistributor.sendToServer(new IncreaseLeashRopeLength(Code.SELF, player.getStringUUID()));
}
}
if (ModKeyMapping.KEY_SUB_LEASH_LENGTH.isDown()) {
PlayerLeashable playerLeashable = (PlayerLeashable) player;
if (playerLeashable.getLeashDataFromEntityData() == null) {
assert minecraft.level != null;
List<HitResult> refLookAtEntityHitResult = Util.getRefLookAtEntityHitResult(player, minecraft.level, 32, entity -> entity instanceof LivingEntity);
Entity theNearestEntityFromHitResultList = Util.getTheNearestEntityFromHitResultList(player, refLookAtEntityHitResult);
if(theNearestEntityFromHitResultList != null) {
if (theNearestEntityFromHitResultList instanceof Player refPlayer) {
PacketDistributor.sendToServer(new DecreaseLeashRopeLength(Code.OTHER_ST, refPlayer.getStringUUID()));
}
}
}
if (playerLeashable.getLeashDataFromEntityData() != null) {
PacketDistributor.sendToServer(new DecreaseLeashRopeLength(Code.SELF, player.getStringUUID()));
}
}
}
}
@EventBusSubscriber(value = Dist.CLIENT, bus = EventBusSubscriber.Bus.MOD, modid = LeashedPlayer.MOD_ID)
public static class Mod {
@SubscribeEvent
public static void onRegisterItemModels(RegisterItemModelsEvent event) {
event.register(ResourceLocation.fromNamespaceAndPath(LeashedPlayer.MOD_ID, "conditional_range"), ConditionalRangeItemModel.Unbaked.MAP_CODEC);
}
@SubscribeEvent
public static void registrySelectItemModelProperty(RegisterSelectItemModelPropertyEvent event) {
event.register(ResourceLocation.fromNamespaceAndPath(LeashedPlayer.MOD_ID, "charge_extend_type"), ChargeExtend.TYPE);
}
@SubscribeEvent
public static void registerKeyMapping(RegisterKeyMappingsEvent event) {
event.register(ModKeyMapping.KEY_ADD_LEASH_LENGTH);
event.register(ModKeyMapping.KEY_SUB_LEASH_LENGTH);
}
@SubscribeEvent
public static void onAddLayers (EntityRenderersEvent.AddLayers event){
PlayerRenderer renderer = event.getSkin(PlayerSkin.Model.WIDE);
if (renderer instanceof PlayerRenderer playerRenderer) {
playerRenderer.addLayer(new ChestItemLayerRenderer<>(playerRenderer, event.getContext().getEntityRenderDispatcher().getItemInHandRenderer()));
}
PlayerRenderer slimRenderer = event.getSkin(PlayerSkin.Model.SLIM);
if (slimRenderer instanceof PlayerRenderer slimPlayerRenderer) {
slimPlayerRenderer.addLayer(new ChestItemLayerRenderer<>(slimPlayerRenderer, event.getContext().getEntityRenderDispatcher().getItemInHandRenderer()));
}
}
@SubscribeEvent
public static void RegisterRenderer(EntityRenderersEvent.RegisterRenderers event) {
event.registerEntityRenderer(ModEntityRegister.LEASH_ROPE_ARROW.get(), LeashRopeArrowRenderer::new);
event.registerEntityRenderer(ModEntityRegister.SPECTRAL_LEASH_ROPE_ARROW.get(), SpectralLeashRopeArrowRenderer::new);
}
}
}

View File

@ -0,0 +1,108 @@
package com.r3944realms.leashedplayer;
import com.mojang.brigadier.CommandDispatcher;
import com.r3944realms.leashedplayer.content.commands.LeashCommand;
import com.r3944realms.leashedplayer.content.commands.MotionCommand;
import com.r3944realms.leashedplayer.content.commands.TickCommand;
import com.r3944realms.leashedplayer.content.effects.ModPotionRegister;
import com.r3944realms.leashedplayer.content.items.ModItemRegister;
import com.r3944realms.leashedplayer.content.misc.LeadBreakItemBehavior;
import com.r3944realms.leashedplayer.utils.Util;
import net.minecraft.ChatFormatting;
import net.minecraft.commands.CommandSourceStack;
import net.minecraft.core.component.DataComponents;
import net.minecraft.network.chat.Component;
import net.minecraft.sounds.SoundEvents;
import net.minecraft.tags.ItemTags;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.EquipmentSlot;
import net.minecraft.world.entity.animal.Fox;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.Items;
import net.minecraft.world.item.alchemy.PotionBrewing;
import net.minecraft.world.item.alchemy.Potions;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.DispenserBlock;
import net.neoforged.bus.api.SubscribeEvent;
import net.neoforged.fml.common.EventBusSubscriber;
import net.neoforged.fml.event.lifecycle.FMLCommonSetupEvent;
import net.neoforged.neoforge.event.AnvilUpdateEvent;
import net.neoforged.neoforge.event.RegisterCommandsEvent;
import net.neoforged.neoforge.event.brewing.RegisterBrewingRecipesEvent;
import net.neoforged.neoforge.event.tick.EntityTickEvent;
public class CommonEventHandler {
@EventBusSubscriber(modid = LeashedPlayer.MOD_ID)
public static class Game extends CommonEventHandler {
@SubscribeEvent
public static void onRegisterCommander(RegisterCommandsEvent event) {
CommandDispatcher<CommandSourceStack> dispatcher = event.getDispatcher();
LeashCommand.register(dispatcher);
MotionCommand.register(dispatcher);
TickCommand.register(dispatcher);
}
@SubscribeEvent
public static void OnRegisterPotionBrewing(RegisterBrewingRecipesEvent event) {
PotionBrewing.Builder builder = event.getBuilder();
builder.addMix(Potions.WATER, Items.SLIME_BALL, ModPotionRegister.NO_LEASH);
}
@SubscribeEvent
public static void OnLivingTickEvent(EntityTickEvent.Post event) {
Entity entity = event.getEntity();
Level level = entity.level();
if (level.isClientSide()) {
return;
}
if (entity instanceof Fox fox) {
if (fox.getMainHandItem().is(ItemTags.ANVIL)) {
fox.setItemSlot(EquipmentSlot.MAINHAND, ItemStack.EMPTY);
Util.throwItemTowardsLook(fox, ModItemRegister.NEOFORGE.get(), 0.3f, 0.1f);
fox.playSound(SoundEvents.FOX_EAT);
} else if (fox.getMainHandItem().is(ModItemRegister.NEOFORGE.get())) {
// 繞圈參數
float rotationSpeed = 10.0f; // tick 旋轉的角度
// 計算新的旋轉角度
fox.yBodyRot += rotationSpeed; // 身體旋轉
fox.yHeadRot += rotationSpeed; // 頭部旋轉
fox.yRotO += rotationSpeed; // 當前旋轉角度
fox.yHeadRotO += rotationSpeed; // 頭部的當前旋轉角度
// 確保旋轉角度不超出 360 重置為 0 以便持續旋轉
if (fox.yBodyRot >= 360) fox.yBodyRot -= 360;
if (fox.yHeadRot >= 360) fox.yHeadRot -= 360;
}
}
}
@SubscribeEvent
public static void OnAnvilUpdated(AnvilUpdateEvent event) {
String name = event.getName();
ItemStack left = event.getLeft();
if (left.is(Items.ANVIL) && name != null && name.equals("NeoForge")) {
event.setCost(1);
event.setOutput(ModItemRegister.NEOFORGE.get().getDefaultInstance());
} else if (left.is(ModItemRegister.NEOFORGE.get().asItem()) && name != null && name.equals("Forge")) {
ItemStack instance = Items.ANVIL.getDefaultInstance();
instance.set(DataComponents.CUSTOM_NAME, Component.literal("Forge").withStyle(ChatFormatting.BOLD).withStyle(ChatFormatting.AQUA));
event.setOutput(instance);
event.setCost(1);
}
}
}
@EventBusSubscriber(modid = LeashedPlayer.MOD_ID, bus = EventBusSubscriber.Bus.MOD)
public static class Mod extends CommonEventHandler {
@SubscribeEvent
public static void onCommonSetup(FMLCommonSetupEvent event) {
event.enqueueWork(() -> {
DispenserBlock.registerProjectileBehavior(ModItemRegister.LEASH_ROPE_ARROW.get());
DispenserBlock.registerProjectileBehavior(ModItemRegister.TIPPED_LEASH_ROPE_ARROW.get());
DispenserBlock.registerProjectileBehavior(ModItemRegister.SPECTRAL_LEASH_ROPE_ARROW.get());
DispenserBlock.registerBehavior(ModItemRegister.AMETHYST_SHEARS.get(), new LeadBreakItemBehavior());
});
}
}
}

View File

@ -0,0 +1,70 @@
package com.r3944realms.leashedplayer;
import com.r3944realms.leashedplayer.config.LeashPlayerCommonConfig;
import com.r3944realms.leashedplayer.content.criteriaTriggers.ModCriteriaTriggers;
import com.r3944realms.leashedplayer.content.effects.ModEffectRegister;
import com.r3944realms.leashedplayer.content.effects.ModPotionRegister;
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.content.items.repcipe.ModRecipeRegister;
import com.r3944realms.leashedplayer.content.paintings.ModPaintingsRegister;
import com.r3944realms.leashedplayer.content.sounds.ModSoundRegister;
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 Double M1;//拴繩掉落距離倍基數
private static Double M2;//繩箭拴繩掉落距離倍基數
private static Integer M3; //拴绳最小长度
private static Integer M4; //拴绳最大长度
public LeashedPlayer(IEventBus event) {
ModItemRegister.register(event);
ModRecipeRegister.register(event);
ModSoundRegister.register(event);
ModPaintingsRegister.register(event);
ModEffectRegister.register(event);
ModPotionRegister.register(event);
ModEntityRegister.register(event);
ModCreativeTab.register(event);
ModCriteriaTriggers.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 Double M1() {
if(M1 == null) {
M1 = LeashPlayerCommonConfig.TheLeashBreakLengthTimesBase.get();
}
return M1;
}
public static Double M2() {
if(M2 == null) {
M2 = LeashPlayerCommonConfig.TheMultipleThatLeashRopeArrowBreakLength.get();
}
return M2;
}
public static Integer M3() {
if(M3 == null) {
M3 = LeashPlayerCommonConfig.MinimumLeashLengthCanBeSet.get();
}
return M3;
}
public static Integer M4() {
if(M4 == null) {
M4 = LeashPlayerCommonConfig.MaximumLeashLengthCanBeSet.get();
}
return M4;
}
}

View File

@ -0,0 +1,53 @@
package com.r3944realms.leashedplayer.client.processBar;
/**
* 进度条
*/
public interface IProcessBar {
/**
*
* @return 存活计数 <=0 则会被清除
*/
int aliveCount();
/**
* 维持计数
*/
void retain();
void decreaseAliveCount();
void setRenderStatus(boolean status);
boolean shouldRender();
int getCurrentProcess();
int getMaxProcess();
void setMaxProcess(int maxProcess);
void setProcess(int process);
void resetProcess();
/**
* 完成进度条执行的任务由实现者实现
*/
void completeTask();
default void decrease() {
int updateValue = getCurrentProcess() - 1;
if(updateValue < 0) {
setRenderStatus(false);
setProcess(0);
}
else {
setRenderStatus(true);
setProcess(updateValue);
}
}
default void increase() {
int updateValue = getCurrentProcess() + 1;
if(updateValue > getMaxProcess()) {
setRenderStatus(false);
setProcess(getMaxProcess());
completeTask();//执行任务
}
else {
setRenderStatus(true);
setProcess(updateValue);
}
}
}

View File

@ -0,0 +1,92 @@
package com.r3944realms.leashedplayer.client.processBar;
import net.minecraft.client.Minecraft;
import net.minecraft.network.chat.ChatType;
import net.minecraft.network.chat.Component;
import java.util.concurrent.CompletableFuture;
public class TestProcessBar implements IProcessBar {
private int aliveCount = 0;
private boolean isRendering;
private int maxProgress;
private int progress;
public TestProcessBar() {
aliveCount++;
isRendering = false;
maxProgress = 100;
progress = 0;
}
public TestProcessBar(int maxProgress) {
aliveCount++;
isRendering = false;
this.maxProgress = maxProgress;
progress = 0;
}
public TestProcessBar(int maxProgress, int progress) {
aliveCount++;
isRendering = false;
this.maxProgress = maxProgress;
this.progress = progress;
}
@Override
public int aliveCount() {
return aliveCount;
}
@Override
public void retain() {
aliveCount++;
}
@Override
public void decreaseAliveCount() {
aliveCount = aliveCount > 0 ? aliveCount - 1 : 0;
}
@Override
public void setRenderStatus(boolean status) {
isRendering = status;
}
@Override
public boolean shouldRender() {
return isRendering;
}
@Override
public int getCurrentProcess() {
return progress;
}
@Override
public int getMaxProcess() {
return maxProgress;
}
@Override
public void setMaxProcess(int maxProcess) {
this.maxProgress = maxProcess;
}
@Override
public void setProcess(int process) {
this.progress = process < 0 ? 0 : (Math.min(process, maxProgress));
}
@Override
public void resetProcess() {
this.progress = 0;
}
@Override
public void completeTask() {
Runnable runnable = () -> {
if (Minecraft.getInstance().player != null) {
Minecraft.getInstance().getChatListener().handleDisguisedChatMessage(Component.literal("Completed"), ChatType.bind(ChatType.MSG_COMMAND_OUTGOING, Minecraft.getInstance().player));
}
};
CompletableFuture.runAsync(runnable);
}
}

View File

@ -0,0 +1,540 @@
package com.r3944realms.leashedplayer.client.renderer;
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.IPlayerRenderStateExtension;
import com.r3944realms.leashedplayer.modInterface.PlayerLeashable;
import net.minecraft.client.Camera;
import net.minecraft.client.Minecraft;
import net.minecraft.client.multiplayer.ClientLevel;
import net.minecraft.client.player.AbstractClientPlayer;
import net.minecraft.client.renderer.LightTexture;
import net.minecraft.client.renderer.MultiBufferSource;
import net.minecraft.client.renderer.RenderType;
import net.minecraft.client.renderer.entity.state.EntityRenderState;
import net.minecraft.client.renderer.entity.state.PlayerRenderState;
import net.minecraft.core.BlockPos;
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.entity.vehicle.AbstractMinecart;
import net.minecraft.world.entity.vehicle.NewMinecartBehavior;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.LightLayer;
import net.minecraft.world.phys.AABB;
import net.minecraft.world.phys.Vec3;
import net.neoforged.api.distmarker.Dist;
import net.neoforged.api.distmarker.OnlyIn;
import org.joml.Matrix4f;
import java.util.List;
import java.util.UUID;
@OnlyIn(Dist.CLIENT)
public class LeashRendererUtil {
public static void renderLeash(PoseStack poseStack, MultiBufferSource buffer, EntityRenderState.LeashState leashState) {
float f = 0.025F;
float f1 = (float)(leashState.end.x - leashState.start.x);
float f2 = (float)(leashState.end.y - leashState.start.y + 0.2);
float f3 = (float)(leashState.end.z - leashState.start.z);
float f4 = Mth.invSqrt(f1 * f1 + f3 * f3) * 0.025F / 2.0F;
float f5 = f3 * f4;
float f6 = f1 * f4;
poseStack.pushPose();
poseStack.translate(leashState.offset.add(0, -0.2, -0.2));
VertexConsumer vertexconsumer = buffer.getBuffer(RenderType.leash());
Matrix4f matrix4f = poseStack.last().pose();
for (int i = 0; i <= 24; i++) {
addVertexPair(
vertexconsumer,
matrix4f,
f1,
f2,
f3,
leashState.startBlockLight,
leashState.endBlockLight,
leashState.startSkyLight,
leashState.endSkyLight,
0.025F,
0.025F,
f5,
f6,
i,
false
);
}
for (int j = 24; j >= 0; j--) {
addVertexPair(
vertexconsumer,
matrix4f,
f1,
f2,
f3,
leashState.startBlockLight,
leashState.endBlockLight,
leashState.startSkyLight,
leashState.endSkyLight,
0.025F,
0.0F,
f5,
f6,
j,
true
);
}
poseStack.popPose();
}
/**
* <h1>1. 角度与弧度转换</h1>
* {@snippet lang=java :
* double d0 = (double)(pEntity.getPreciseBodyRotation(pPartialTick) * (float)(Math.PI / 180.0)) + (Math.PI / 2);
* }
* <ul>
* <li><code>pEntity.getPreciseBodyRotation(pPartialTick)</code> 返回实体的旋转角度通常是以度为单位/li>
* <li> <code>(Math.PI / 180.0)</code> 是将度数转换为弧度的乘数因为大多数三角函数 <code>cos</code> <code>sin</code>都需要弧度值</li>
* <li><code>+ (Math.PI / 2)</code> 用于将结果平移90度四分之一圆可能是为了校正方向或设置起始方向 </li>
* </ul>
*
* <p>
* <h1> 2. 三角函数计算位移</h1>
* {@snippet lang=java :
* double d1 = Math.cos(d0) * vec31.z + Math.sin(d0) * vec31.x;
* double d2 = Math.sin(d0) * vec31.z - Math.cos(d0) * vec31.x;
* }
* <ul>
* <li><code>d1</code> <code>d2</code> 是利用三角函数 <code>cos</code> <code>sin</code> 计算出来的位移量用于确定实体相对于其旋转的实际位置</li>
* <li><code>Math.cos(d0) * vec31.z</code> <code>Math.sin(d0) * vec31.x</code> 分别计算沿 X Z 轴的位移分量这种计算通常用于旋转一个点或向量</li>
* <li>两个公式结合起来用于旋转平面内的一个点 <code>(vec31.x, vec31.z)</code>从而得到旋转后的新坐标</li>
* </ul>
* <p>
* <h1> 3. 线性插值 (Lerp) </h1>
* {@snippet lang=java :
* 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;
* }
* <ul>
* <li><code>Mth.lerp</code> 是线性插值函数通常用于在两个值之间平滑过渡</li>
* <li><code>pEntity.xo</code>, <code>pEntity.yo</code>, <code>pEntity.zo</code> 是实体在上一个刻度tick中的位置 <code>pEntity.getX()</code>, <code>pEntity.getY()</code>, <code>pEntity.getZ()</code> 是当前刻度的位置</li>
* <li><code>pPartialTick</code> 介于 <code>0</code> <code>1</code> 之间用来平滑过渡使得动画更加流畅</li>
* </ul>
* <p>
* <h1> 4. 向量差值 </h1>
* {@snippet lang=java :
* float f = (float)(vec3.x - d3);
* float f1 = (float)(vec3.y - d4);
* float f2 = (float)(vec3.z - d5);
* }
* <ul>
* <li>计算两个点<code>vec3</code> <code>(d3, d4, d5)</code>之间的差值得到的 <code>f</code><code>f1</code><code>f2</code> 是向量差用于后续的渲染计算</li>
* </ul>
* <p>
* <h1> 5. 逆平方根与比例因子 </h1>
* {@snippet lang=java :
* float f4 = Mth.invSqrt(f * f + f2 * f2) * 0.025F / 2.0F;
* }
* <ul>
* <li><code>Mth.invSqrt</code> 计算的是逆平方根通常用于归一化向量或调整比例</li>
* <li><code>f * f + f2 * f2</code> 是计算向量 <code>(f, f2)</code> 的平方和用于得到其长度的平方</li>
* <li>乘以 <code>0.025F / 2.0F</code> 用于缩放结果使得线条在渲染时具有合适的比例</li>
* </ul>
* <p>
* <h1> 6. 循环绘制 </h1>
* {@snippet lang=java :
* 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);
* }
* }
* <ul>
* <li>循环从 <code>0</code> <code>24</code>用于创建24个顶点对形成一个链状结构或绳索的外观</li>
* <li>每个循环迭代都会更新顶点的位置颜色光照等属性使得链状结构被绘制出来</li>
* </ul>
* <p>
* <h1> 总结 </h1>
* 这些数学运算主要用于计算实体在三维空间中的位置和方向以确保在渲染链状结构如拴住的绳索链条能够跟随实体的移动和旋转并正确显示在图形编程中这些计算非常常见尤其是在处理旋转插值和光照效果时
*/
public static void renderLeash(
PoseStack poseStack,
MultiBufferSource bufferSource,
PlayerRenderState playerRenderState
) {
float f = 0.025F;
// 获得绳索持有者的位置
PlayerLeashState leashState = ((IPlayerRenderStateExtension)playerRenderState).getPlayerLeashState();
if (leashState == null)
return;
Vec3 Holder = leashState.pos;//TODO问题修复
Vec3 o = leashState.o;
// 计算实体的朝向角度弧度
float partialTick = playerRenderState.partialTick;
double entityRotationAngleRadians = (double)(leashState.getPreciseBodyRotation(partialTick) * (float) (Math.PI / 180.0)) + (Math.PI / 2);
// 计算实体的绳索偏移此处add偏移让渲染拴绳显示在玩家头部下大约在脖子处
// Logger.logger.info("eyeHeight:{}",playerRenderState.eyeHeight);
Vec3 cameraEntityLeashOffset = new Vec3(0.0, leashState.eyeHeight , playerRenderState.boundingBoxWidth * 0.4F).add(0, -0.2, -0.2);
double leashOffsetX = Math.cos(entityRotationAngleRadians) * cameraEntityLeashOffset.z + Math.sin(entityRotationAngleRadians) * cameraEntityLeashOffset.x;
double leashOffsetZ = Math.sin(entityRotationAngleRadians) * cameraEntityLeashOffset.z - Math.cos(entityRotationAngleRadians) * cameraEntityLeashOffset.x;
// 计算实体当前的实际位置
double entityPosX = Mth.lerp(partialTick, o.x, playerRenderState.x) + leashOffsetX;
double entityPosY = Mth.lerp(partialTick, o.y, playerRenderState.y) + cameraEntityLeashOffset.y;
double entityPosZ = Mth.lerp(partialTick, o.z, playerRenderState.z) + leashOffsetZ;
// 计算绳索的相对位置差
float deltaX = (float)(Holder.x - entityPosX);
float deltaY = (float)(Holder.y - entityPosY);
float deltaZ = (float)(Holder.z - entityPosZ);
// 计算比例因子用于调节绳索的粗细
float leashLengthRatio = Mth.invSqrt(deltaX * deltaX + deltaZ * deltaZ) * 0.025F / 2.0F;
// 计算比例因子用于调节绳索的粗细
float leashXZScaleX = deltaZ * leashLengthRatio;
float leashXZScaleZ = deltaX * leashLengthRatio;
poseStack.pushPose();
poseStack.translate(leashOffsetX, cameraEntityLeashOffset.y,leashOffsetZ);
VertexConsumer vertexconsumer = bufferSource.getBuffer(RenderType.leash());
Matrix4f matrix4f = poseStack.last().pose();
for (int i = 0; i <= 24; i++) {
addVertexPair(
vertexconsumer,
matrix4f,
deltaX,
deltaY,
deltaZ,
leashState.startBlockLight,
leashState.endBlockLight,
leashState.startSkyLight,
leashState.endSkyLight,
0.025F,
0.025F,
leashXZScaleX,
leashXZScaleZ,
i,
false
);
}
for (int j = 24; j >= 0; j--) {
addVertexPair(
vertexconsumer,
matrix4f,
deltaX,
deltaY,
deltaZ,
leashState.startBlockLight,
leashState.endBlockLight,
leashState.startSkyLight,
leashState.endSkyLight,
0.025F,
0.0F,
leashXZScaleX,
leashXZScaleZ,
j,
true
);
}
poseStack.popPose();
}
protected static void addVertexPair(
VertexConsumer buffer,
Matrix4f pose,
float startX,
float startY,
float startZ,
int entityBlockLight,
int holderBlockLight,
int entitySkyLight,
int holderSkyLight,
float yOffset,
float dy,
float dx,
float dz,
int index,
boolean reverse
) {
float f = (float)index / 24.0F;
int i = (int)Mth.lerp(f, (float)entityBlockLight, (float)holderBlockLight);
int j = (int)Mth.lerp(f, (float)entitySkyLight, (float)holderSkyLight);
int k = LightTexture.pack(i, j);
float f1 = index % 2 == (reverse ? 1 : 0) ? 0.7F : 1.0F;
float f2 = 0.5F * f1;
float f3 = 0.4F * f1;
float f4 = 0.3F * f1;
float f5 = startX * f;
float f6 = startY > 0.0F ? startY * f * f : startY - startY * (1.0F - f) * (1.0F - f);
float f7 = startZ * f;
buffer.addVertex(pose, f5 - dx, f6 + dy, f7 + dz).setColor(f2, f3, f4, 1.0F).setLight(k);
buffer.addVertex(pose, f5 + dx, f6 + yOffset - dy, f7 - dz).setColor(f2, f3, f4, 1.0F).setLight(k);
}
public static <E extends Entity> void renderLeashForCamera(
Camera camera,
float partialTick,
PoseStack poseStack,
MultiBufferSource bufferSource,
E leashHolder,
Vec3 holderOffset
) {
poseStack.pushPose();
// Logger.logger.info("eyeHeight{}", camera.getEntity().getEyeHeight());
// 获得绳索持有者的位置
Vec3 leashHolderPosition = leashHolder.getRopeHoldPosition(partialTick).add(holderOffset);
// 获取当前观察的实体
Entity cameraEntity = camera.getEntity();
// 计算实体的朝向角度弧度
double entityRotationAngleRadians = (double)(cameraEntity.getPreciseBodyRotation(partialTick) * (float) (Math.PI / 180.0)) + (Math.PI / 2);
// 计算实体的绳索偏移此处add偏移让渲染拴绳显示在玩家头部下大约在脖子处
Vec3 cameraEntityLeashOffset = cameraEntity.getLeashOffset(partialTick).add(0, -0.2, -0.5);
double leashOffsetX = Math.cos(entityRotationAngleRadians) * cameraEntityLeashOffset.z + Math.sin(entityRotationAngleRadians) * cameraEntityLeashOffset.x;
double leashOffsetZ = Math.sin(entityRotationAngleRadians) * cameraEntityLeashOffset.z - Math.cos(entityRotationAngleRadians) * cameraEntityLeashOffset.x;
// 计算实体当前的实际位置
double entityPosX = Mth.lerp(partialTick, cameraEntity.xo, cameraEntity.getX()) + leashOffsetX;
double entityPosY = Mth.lerp(partialTick, cameraEntity.yo, cameraEntity.getY()) + cameraEntityLeashOffset.y;
double entityPosZ = Mth.lerp(partialTick, cameraEntity.zo, cameraEntity.getZ()) + leashOffsetZ;
// 在当前变换矩阵上应用偏移
poseStack.translate(leashOffsetX, cameraEntityLeashOffset.y , leashOffsetZ);
// 计算绳索的相对位置差
float deltaX = (float)(leashHolderPosition.x - entityPosX);
float deltaY = (float)(leashHolderPosition.y - entityPosY);
float deltaZ = (float)(leashHolderPosition.z - entityPosZ);
// 获取顶点消费者用于绘制绳索
VertexConsumer vertexConsumer = bufferSource.getBuffer(RenderType.leash());
Matrix4f matrix = poseStack.last().pose();
// 计算比例因子用于调节绳索的粗细
float leashLengthRatio = Mth.invSqrt(deltaX * deltaX + deltaZ * deltaZ) * 0.025F / 2.0F;
float leashXZScaleX = deltaZ * leashLengthRatio;
float leashXZScaleZ = deltaX * leashLengthRatio;
// 获取光照信息
BlockPos cameraEntityBlockPos = BlockPos.containing(cameraEntity.getEyePosition(partialTick));
BlockPos leashHolderBlockPos = BlockPos.containing(leashHolder.getEyePosition(partialTick));
int cameraEntityBlockLightLevel = getBlockLightLevel(cameraEntity, cameraEntityBlockPos);
int leashHolderBlockLightLevel = getBlockLightLevel(leashHolder, leashHolderBlockPos); //getBlockLightLevel(leashHolder, leashHolderBlockPos);
int cameraEntitySkyLightLevel = cameraEntity.level().getBrightness(LightLayer.SKY, cameraEntityBlockPos);
int leashHolderSkyLightLevel = cameraEntity.level().getBrightness(LightLayer.SKY, leashHolderBlockPos);
// 绘制绳索的上半部分
for (int segment = 0; segment <= 24; segment++) {
addVertexPair(vertexConsumer, matrix, deltaX, deltaY, deltaZ, cameraEntityBlockLightLevel, leashHolderBlockLightLevel, cameraEntitySkyLightLevel, leashHolderSkyLightLevel, 0.025F, 0.025F, leashXZScaleX, leashXZScaleZ, segment, false);
}
// 绘制绳索的下半部分
for (int segment = 24; segment >= 0; segment--) {
addVertexPair(vertexConsumer, matrix, deltaX, deltaY, deltaZ, cameraEntityBlockLightLevel, leashHolderBlockLightLevel, cameraEntitySkyLightLevel, leashHolderSkyLightLevel, 0.025F, 0.0F, leashXZScaleX, leashXZScaleZ, segment, true);
}
poseStack.popPose();
}
public static void levelRenderLeash(ClientLevel level, Camera pCamera, PoseStack poseStack, MultiBufferSource.BufferSource multibuffersource$buffersource) {
for(Entity entity : level.entitiesForRendering()) {
//对于玩家实体拴绳渲染从第一人称视角
if (entity instanceof AbstractClientPlayer abstractClientPlayer) {
if(!(pCamera.getEntity() instanceof AbstractClientPlayer)) continue;
Minecraft mc = Minecraft.getInstance();
if (mc.options.getCameraType().isFirstPerson()) {
Leashable.LeashData leashDataFromEntityData = ((PlayerLeashable) abstractClientPlayer).getLeashDataFromEntityData();
if(leashDataFromEntityData == null) continue;
Either<UUID, BlockPos> delayedLeashInfo = leashDataFromEntityData.delayedLeashInfo;
if(delayedLeashInfo != null) {
float partialTickTime = pCamera.getPartialTickTime();
Vec3 position = pCamera.getPosition();
double dX = Mth.lerp(partialTickTime, abstractClientPlayer.xOld, abstractClientPlayer.getX()) - position.x;
double dY = Mth.lerp(partialTickTime, abstractClientPlayer.yOld, abstractClientPlayer.getY()) - position.y;
double dZ = Mth.lerp(partialTickTime, abstractClientPlayer.zOld, abstractClientPlayer.getZ()) - position.z;
Vec3 vec3 = getRenderOffset(entity, partialTickTime);
double dX_ = dX + vec3.x();
double dY_ = dY + vec3.y();
double dZ_ = dZ + vec3.z();
poseStack.pushPose();
poseStack.translate(dX_, dY_, dZ_);
if (delayedLeashInfo.right().isPresent() && delayedLeashInfo.left().isEmpty()) {
renderLeashForCamera(pCamera, partialTickTime, poseStack, multibuffersource$buffersource, LeashFenceKnotEntity.getOrCreateKnot(level, delayedLeashInfo.right().get()), Vec3.ZERO);
} else if (delayedLeashInfo.right().isEmpty() && delayedLeashInfo.left().isPresent()) {
Player playerByUUID = level.getPlayerByUUID(delayedLeashInfo.left().get());
if (playerByUUID != null) {
renderLeashForCamera(pCamera, partialTickTime, poseStack, multibuffersource$buffersource, playerByUUID, Vec3.ZERO);
} else {
double MaxLeashLength = ((ILivingEntityExtension) abstractClientPlayer).getLeashLength() * LeashedPlayer.M1() * LeashedPlayer.M2();
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) {
if(holder instanceof LeashRopeArrow) {
renderLeashForCamera(pCamera, partialTickTime, poseStack, multibuffersource$buffersource, holder, new Vec3(0.,-0.09, 0));//TODO: 待擴展Vec3吗
}
else renderLeashForCamera(pCamera, partialTickTime, poseStack, multibuffersource$buffersource, holder, Vec3.ZERO);
}
}
break;
}
}
}
}
}
}
public static Vec3 getRenderOffset(Entity entity, float partialTickTime) {
Vec3 ret = Vec3.ZERO;
if(entity.isPassenger()
&& entity.getVehicle() instanceof AbstractMinecart abstractminecart
&& abstractminecart.getBehavior() instanceof NewMinecartBehavior newminecartbehavior
&& newminecartbehavior.cartHasPosRotLerp()
) {
double d2 = Mth.lerp(partialTickTime, abstractminecart.xOld, abstractminecart.getX());
double d0 = Mth.lerp(partialTickTime, abstractminecart.yOld, abstractminecart.getY());
double d1 = Mth.lerp(partialTickTime, abstractminecart.zOld, abstractminecart.getZ());
ret = newminecartbehavior.getCartLerpPosition(partialTickTime).subtract(new Vec3(d2, d0, d1));
}
return ret;
}
public static void createPlayerLeashState(AbstractClientPlayer abstractClientPlayer, IPlayerRenderStateExtension playerRenderState, float partialTick) {
Leashable.LeashData leashDataFromEntityData = ((PlayerLeashable) abstractClientPlayer).getLeashDataFromEntityData();
Leashable.LeashData leashData = ((PlayerLeashable) abstractClientPlayer).getLeashData();
PlayerRenderState playerRs = (PlayerRenderState)playerRenderState;
if (leashDataFromEntityData != null) {
Entity leashHolder = leashDataFromEntityData.leashHolder;
Either<UUID, BlockPos> delayedLeashInfo = leashDataFromEntityData.delayedLeashInfo;
PlayerLeashState playerLeashState = new PlayerLeashState();
if (delayedLeashInfo != null) {
createPlayerLeashState_(abstractClientPlayer, playerRenderState, leashDataFromEntityData, delayedLeashInfo, playerLeashState, partialTick);
BlockPos blockpos1 = BlockPos.containing(abstractClientPlayer.getEyePosition(partialTick));
playerLeashState.startBlockLight = getBlockLightLevel(abstractClientPlayer, blockpos1);
playerLeashState.startSkyLight = abstractClientPlayer.level().getBrightness(LightLayer.SKY, blockpos1);
} else if(leashHolder != null) {
createPlayerLeashState__(abstractClientPlayer, playerRenderState, leashHolder, playerLeashState, partialTick);
} else {
playerRenderState.setPlayerLeashState(null);
}
} else if(leashData != null) {
playerRs.leashState = null;
Entity leashHolder = ((PlayerLeashable) abstractClientPlayer).getLeashHolder();
PlayerLeashState playerLeashState = playerRenderState.getPlayerLeashState();
if (leashHolder != null & playerLeashState != null) {
float f = leashHolder.getPreciseBodyRotation(partialTick) * (float) (Math.PI / 180.0);
Vec3 vec3 = abstractClientPlayer.getLeashOffset(partialTick).yRot(-f);
BlockPos blockpos1 = BlockPos.containing(abstractClientPlayer.getEyePosition(partialTick));
BlockPos blockpos = BlockPos.containing(leashHolder.getEyePosition(partialTick));
playerLeashState.vanilaLeashState = new EntityRenderState.LeashState();
EntityRenderState.LeashState entityrenderstate$leashstate = playerLeashState.vanilaLeashState;
entityrenderstate$leashstate.offset = vec3;
entityrenderstate$leashstate.start = abstractClientPlayer.getPosition(partialTick).add(vec3);
entityrenderstate$leashstate.end = leashHolder.getRopeHoldPosition(partialTick);
entityrenderstate$leashstate.startBlockLight = getBlockLightLevel(abstractClientPlayer, blockpos1);
entityrenderstate$leashstate.endBlockLight = getBlockLightLevel(leashHolder, blockpos);
entityrenderstate$leashstate.startSkyLight = abstractClientPlayer.level().getBrightness(LightLayer.SKY, blockpos1);
entityrenderstate$leashstate.endSkyLight = abstractClientPlayer.level().getBrightness(LightLayer.SKY, blockpos);
} else {
playerRenderState.setPlayerLeashState(null);
}
} else {
playerRenderState.setPlayerLeashState(null);
}
}
private static <T extends Entity> int getBlockLightLevel(T entity, BlockPos pos) {
return entity.isOnFire() ? 15 : entity.level().getBrightness(LightLayer.BLOCK, pos);
}
private static int getBlockLightLevel(Level level, BlockPos pos) {
return level.getBrightness(LightLayer.BLOCK, pos);
}
private static void createPlayerLeashState__(AbstractClientPlayer abstractClientPlayer, IPlayerRenderStateExtension playerRenderState, Entity leashHolder, PlayerLeashState playerLeashState, float partialTick) {
playerLeashState.pos = leashHolder.position().add(0.0, leashHolder.getEyeHeight() * 0.6, -0.2);
playerLeashState.o = abstractClientPlayer.getPosition(partialTick);
playerLeashState.yRotO = abstractClientPlayer.yRotO;
playerLeashState.yRot = abstractClientPlayer.getYRot();
playerLeashState.eyeHeight = abstractClientPlayer.getEyeHeight();
playerRenderState.setPlayerLeashState(playerLeashState);
}
private static void createPlayerLeashState_(AbstractClientPlayer abstractClientPlayer, IPlayerRenderStateExtension playerRenderState, Leashable.LeashData leashDataFromEntityData, Either<UUID, BlockPos> delayedLeashInfo, PlayerLeashState playerLeashState, float partialTick) {
Minecraft mc = Minecraft.getInstance();
ClientLevel level = mc.level;
if (delayedLeashInfo.right().isPresent() && delayedLeashInfo.left().isEmpty()) {
assert level != null;
playerLeashState.pos = delayedLeashInfo.right().get().getCenter();
playerLeashState.endBlockLight = getBlockLightLevel(level, delayedLeashInfo.right().get());
playerLeashState.endSkyLight = level.getBrightness(LightLayer.SKY, delayedLeashInfo.right().get());
} else if (delayedLeashInfo.right().isEmpty() && delayedLeashInfo.left().isPresent()) {
assert level != null;
Player playerByUUID = level.getPlayerByUUID(delayedLeashInfo.left().get());
if (playerByUUID != null) {
playerLeashState.pos = playerByUUID.position().add(0.0, playerByUUID.getEyeHeight() * 0.6, 0);
} else {
double breakDistanceTime = (leashDataFromEntityData.leashHolder instanceof LeashRopeArrow) ? LeashedPlayer.M1() * LeashedPlayer.M2() : LeashedPlayer.M1();
double MaxLeashLength = ((ILivingEntityExtension) abstractClientPlayer).getLeashLength() * breakDistanceTime;
List<Entity> entities = level.getEntities(
null,
new AABB(
abstractClientPlayer.getX() - MaxLeashLength * LeashedPlayer.M2(),
abstractClientPlayer.getY() - MaxLeashLength * LeashedPlayer.M2(),
abstractClientPlayer.getZ() - MaxLeashLength * LeashedPlayer.M2(),
abstractClientPlayer.getX() + MaxLeashLength * LeashedPlayer.M2(),
abstractClientPlayer.getY() + MaxLeashLength * LeashedPlayer.M2(),
abstractClientPlayer.getZ() + MaxLeashLength * LeashedPlayer.M2()
)
);
Entity holder = null;
for (Entity entity_ : entities) {
if (entity_.getUUID().equals(delayedLeashInfo.left().get())) {
holder = entity_;
break;
}
}
if (holder != null) {
playerLeashState.pos = holder.position().add(0.0, holder.getEyeHeight() * 0.6, 0);//TODO: 待擴展Vec3
playerLeashState.endBlockLight = getBlockLightLevel(level, holder.getOnPos());
playerLeashState.endSkyLight = level.getBrightness(LightLayer.SKY, holder.getOnPos());
} else {
playerLeashState.pos = abstractClientPlayer.position();
}
}
}
playerLeashState.o = abstractClientPlayer.getPosition(partialTick);
playerLeashState.yRotO = abstractClientPlayer.yRotO;
playerLeashState.yRot = abstractClientPlayer.getYRot();
playerLeashState.eyeHeight = abstractClientPlayer.getEyeHeight();
playerRenderState.setPlayerLeashState(playerLeashState);
}
}

View File

@ -0,0 +1,41 @@
package com.r3944realms.leashedplayer.client.renderer;
import net.minecraft.client.renderer.entity.state.EntityRenderState;
import net.minecraft.util.Mth;
import net.minecraft.world.phys.Vec3;
import net.neoforged.api.distmarker.Dist;
import net.neoforged.api.distmarker.OnlyIn;
import javax.annotation.Nullable;
@OnlyIn(Dist.CLIENT)
public class PlayerLeashState {
public Vec3 o, pos, offset;
public float yRotO, yRot;
public float eyeHeight;
public int startBlockLight;
public int endBlockLight;
public int startSkyLight;
public int endSkyLight;
@Nullable
public EntityRenderState.LeashState vanilaLeashState;
public float getPreciseBodyRotation(float pPartialTick) {
return Mth.lerp(pPartialTick, this.yRotO, this.yRot);
}
public PlayerLeashState() {
this.offset = Vec3.ZERO;
this.o = Vec3.ZERO;
this.pos = Vec3.ZERO;
this.yRot = 0.0f;
this.yRotO = 0.0f;
this.eyeHeight = 0.0f;
this.startBlockLight = 0;
this.endBlockLight = 0;
this.startSkyLight = 15;
this.endSkyLight = 15;
this.vanilaLeashState = null;
}
}

View File

@ -0,0 +1,13 @@
package com.r3944realms.leashedplayer.client.renderer;
import net.minecraft.world.entity.LivingEntity;
import net.neoforged.api.distmarker.Dist;
import net.neoforged.api.distmarker.OnlyIn;
@OnlyIn(Dist.CLIENT)
public class PlayerSlotItemLayerState {
public LivingEntity entity;
public PlayerSlotItemLayerState(LivingEntity entity) {
this.entity = entity;
}
}

View File

@ -0,0 +1,120 @@
package com.r3944realms.leashedplayer.client.renderer.entities;
import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.math.Axis;
import com.r3944realms.leashedplayer.modInterface.IPlayerRenderStateExtension;
import net.minecraft.client.model.EntityModel;
import net.minecraft.client.model.PlayerModel;
import net.minecraft.client.renderer.ItemInHandRenderer;
import net.minecraft.client.renderer.MultiBufferSource;
import net.minecraft.client.renderer.entity.RenderLayerParent;
import net.minecraft.client.renderer.entity.layers.RenderLayer;
import net.minecraft.client.renderer.entity.state.PlayerRenderState;
import net.minecraft.world.entity.EquipmentSlot;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.item.ItemDisplayContext;
import net.minecraft.world.item.ItemStack;
import org.jetbrains.annotations.NotNull;
public class ChestItemLayerRenderer<S extends PlayerRenderState, M extends EntityModel<S>> extends RenderLayer<S, M> {
private final ItemInHandRenderer heldItemRenderer;
public ChestItemLayerRenderer(RenderLayerParent<S, M> context, ItemInHandRenderer heldItemRenderer) {
super(context);
this.heldItemRenderer = heldItemRenderer;
}
@Override
public void render(@NotNull PoseStack poseStack, @NotNull MultiBufferSource bufferSource, int packedLight, @NotNull S renderState, float yRot, float xRot) {
ItemDisplayContext mode = ItemDisplayContext.FIXED;
IPlayerRenderStateExtension rs = (IPlayerRenderStateExtension) renderState;
LivingEntity entity = rs.getPlayerSlotItemLayerState().entity;
ItemStack chestStack = entity.getItemBySlot(EquipmentSlot.CHEST);
if (!chestStack.isEmpty()) {
if (!(entity.getEquipmentSlotForItem(chestStack).equals(EquipmentSlot.CHEST))) {
poseStack.pushPose();
((PlayerModel) this.getParentModel()).body.translateAndRotate(poseStack);
poseStack.mulPose(Axis.XP.rotationDegrees(180));
poseStack.translate(0, -0.24f, 0);
poseStack.mulPose(Axis.YP.rotationDegrees(180));
this.heldItemRenderer.renderItem(entity, chestStack, mode, poseStack, bufferSource, packedLight);
poseStack.scale(1.01f, 1.01f, 1.01f);
poseStack.translate(0, -1 / 4f, 0);
this.heldItemRenderer.renderItem(entity, chestStack, mode, poseStack, bufferSource, packedLight);
poseStack.popPose();
poseStack.pushPose();
((PlayerModel) this.getParentModel()).rightArm.translateAndRotate(poseStack);
poseStack.mulPose(Axis.XP.rotationDegrees(180));
poseStack.scale(2/3f, 2/3f, 2/3f);
poseStack.translate(-1/12f, 0, 0);
poseStack.mulPose(Axis.YP.rotationDegrees(180));
this.heldItemRenderer.renderItem(entity, chestStack, mode, poseStack, bufferSource, packedLight);
poseStack.scale(0.99f, 0.99f, 0.99f);
poseStack.translate(0, -1/2f, 0);
this.heldItemRenderer.renderItem(entity, chestStack, mode, poseStack, bufferSource, packedLight);
poseStack.popPose();
poseStack.pushPose();
((PlayerModel) this.getParentModel()).leftArm.translateAndRotate(poseStack);
poseStack.mulPose(Axis.XP.rotationDegrees(180));
poseStack.scale(2/3f, 2/3f, 2/3f);
poseStack.translate(1/12f, 0, 0);
poseStack.mulPose(Axis.YP.rotationDegrees(180));
this.heldItemRenderer.renderItem(entity, chestStack, mode, poseStack, bufferSource, packedLight);
poseStack.scale(0.99f, 0.99f, 0.99f);
poseStack.translate(0, -1/2f, 0);
this.heldItemRenderer.renderItem(entity, chestStack, mode, poseStack, bufferSource, packedLight);
poseStack.popPose();
}
}
ItemStack legsStack = entity.getItemBySlot(EquipmentSlot.LEGS);
if (!legsStack.isEmpty()) {
if (!(entity.getEquipmentSlotForItem(legsStack).equals(EquipmentSlot.LEGS))) {
poseStack.pushPose();
((PlayerModel) this.getParentModel()).rightLeg.translateAndRotate(poseStack);
poseStack.mulPose(Axis.XP.rotationDegrees(180));
poseStack.scale(2/3f, 2/3f, 2/3f);
poseStack.translate(0, -1/6f, 0);
poseStack.mulPose(Axis.YP.rotationDegrees(180));
this.heldItemRenderer.renderItem(entity, legsStack, mode, poseStack, bufferSource, packedLight);
poseStack.scale(1.01f, 1.01f, 1.01f);
poseStack.translate(0, -1/3f, 0);
this.heldItemRenderer.renderItem(entity, legsStack, mode, poseStack, bufferSource, packedLight);
poseStack.popPose();
poseStack.pushPose();
((PlayerModel) this.getParentModel()).leftLeg.translateAndRotate(poseStack);
poseStack.mulPose(Axis.XP.rotationDegrees(180));
poseStack.scale(2/3f, 2/3f, 2/3f);
poseStack.translate(0, -1/6f, 0);
poseStack.mulPose(Axis.YP.rotationDegrees(180));
this.heldItemRenderer.renderItem(entity, legsStack, mode, poseStack, bufferSource, packedLight);
poseStack.scale(1.01f, 1.01f, 1.01f);
poseStack.translate(0, -1/3f, 0);
this.heldItemRenderer.renderItem(entity, legsStack, mode, poseStack, bufferSource, packedLight);
poseStack.popPose();
}
}
ItemStack feetStack = entity.getItemBySlot(EquipmentSlot.FEET);
if (!feetStack.isEmpty()) {
if (!(entity.getEquipmentSlotForItem(feetStack).equals(EquipmentSlot.FEET))) {
poseStack.pushPose();
((PlayerModel) this.getParentModel()).rightLeg.translateAndRotate(poseStack);
poseStack.mulPose(Axis.XP.rotationDegrees(180));
poseStack.scale(0.75f, 0.75f, 0.75f);
poseStack.translate(0, -0.8f, 0);
poseStack.mulPose(Axis.YP.rotationDegrees(180));
this.heldItemRenderer.renderItem(entity, feetStack, mode, poseStack, bufferSource, packedLight);
poseStack.popPose();
poseStack.pushPose();
((PlayerModel) this.getParentModel()).leftLeg.translateAndRotate(poseStack);
poseStack.mulPose(Axis.XP.rotationDegrees(180));
poseStack.scale(0.75f, 0.75f, 0.75f);
poseStack.translate(0, -0.8f, 0);
poseStack.mulPose(Axis.YP.rotationDegrees(180));
this.heldItemRenderer.renderItem(entity, feetStack, mode, poseStack, bufferSource, packedLight);
poseStack.popPose();
}
}
}
}

View File

@ -0,0 +1,30 @@
package com.r3944realms.leashedplayer.client.renderer.entities;
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.client.renderer.entity.state.ArrowRenderState;
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, ArrowRenderState> {
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 ArrowRenderState createRenderState() {
return new ArrowRenderState();
}
@Override
protected @NotNull ResourceLocation getTextureLocation(@NotNull ArrowRenderState arrowRenderState) {
return LEASH_ROPE_ARROW;
}
}

View File

@ -0,0 +1,31 @@
package com.r3944realms.leashedplayer.client.renderer.entities;
import com.r3944realms.leashedplayer.LeashedPlayer;
import com.r3944realms.leashedplayer.content.entities.SpectralLeashRopeArrow;
import net.minecraft.client.renderer.entity.ArrowRenderer;
import net.minecraft.client.renderer.entity.EntityRendererProvider;
import net.minecraft.client.renderer.entity.state.ArrowRenderState;
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 SpectralLeashRopeArrowRenderer extends ArrowRenderer<SpectralLeashRopeArrow, ArrowRenderState> {
public static final ResourceLocation SPECTRAL_LEASH_ROPE_ARROW = ResourceLocation.fromNamespaceAndPath(LeashedPlayer.MOD_ID, "textures/entity/projectiles/spectral_leash_rope_arrow.png");
public SpectralLeashRopeArrowRenderer(EntityRendererProvider.Context pContext) {
super(pContext);
}
@Override
public @NotNull ArrowRenderState createRenderState() {
return new ArrowRenderState();
}
@Override
protected @NotNull ResourceLocation getTextureLocation(@NotNull ArrowRenderState arrowRenderState) {
return SPECTRAL_LEASH_ROPE_ARROW;
}
}

View File

@ -0,0 +1,42 @@
package com.r3944realms.leashedplayer.client.renderer.gui;
import com.google.common.collect.Maps;
import com.mojang.blaze3d.vertex.PoseStack;
import com.r3944realms.leashedplayer.LeashedPlayer;
import com.r3944realms.leashedplayer.client.processBar.IProcessBar;
import net.minecraft.client.gui.GuiGraphics;
import net.neoforged.api.distmarker.Dist;
import net.neoforged.bus.api.SubscribeEvent;
import net.neoforged.fml.common.EventBusSubscriber;
import net.neoforged.neoforge.client.event.RenderGuiEvent;
import java.util.Map;
@EventBusSubscriber(modid = LeashedPlayer.MOD_ID, value = Dist.CLIENT)
public class AdaptiveGuiRendererHandler {
private static final Map<IProcessBar, IProcessBarRenderer<? extends IProcessBar>> processBars = Maps.newConcurrentMap();
public static void addProcessBar(IProcessBar processBar, IProcessBarRenderer<? extends IProcessBar> renderer) {
processBars.put(processBar, renderer);
}
public static IProcessBarRenderer<? extends IProcessBar> getProcessBarRenderer(IProcessBar processBar) {
return processBars.get(processBar);
}
@SubscribeEvent
public static void onRendererLevel(RenderGuiEvent.Pre event) {
PoseStack matrixStack = event.getGuiGraphics().pose();
GuiGraphics guiGraphics = event.getGuiGraphics();
processBars.keySet().forEach(
processBar -> {
IProcessBarRenderer<? extends IProcessBar> renderer = processBars.get(processBar);
if(processBar.shouldRender()) {
renderer.renderProcessBar(matrixStack, guiGraphics);
} else {
if (processBar.aliveCount() <= 0) {//计数为非正时移除
processBars.remove(processBar);
}
}
}
);
}
}

View File

@ -0,0 +1,83 @@
package com.r3944realms.leashedplayer.client.renderer.gui;
import com.mojang.blaze3d.vertex.PoseStack;
import com.r3944realms.leashedplayer.client.processBar.IProcessBar;
import com.r3944realms.leashedplayer.client.processBar.TestProcessBar;
import net.minecraft.client.gui.GuiGraphics;
public interface IFadingProcessBarRenderer<T extends IProcessBar> extends IProcessBarRenderer<T> {
/**
* @return 当前透明度0.0完全透明 1.0完全不透明
*/
float getFadeAlpha();
void setFadeAlpha(float alpha);
/**
* @return 是否淡入
*/
boolean isFadingIn();
void setFadingIn(boolean fadingIn);
/**
* @return 是否淡出
*/
boolean isFadingOut();
void setFadingOut(boolean fadingOut);
/**
* @return 淡化速度
*/
float getFadeRate();
void setFadeRate(float fadeRate);
/**
* @return 完全淡入/淡出所需时间
*/
float getFadeDuration();
void setFadeDuration(float fadeDuration);
/**
* 更新淡化效果
*/
default void updateFadeEffect() {
float fadeSpeed = getFadeRate() / getFadeDuration();
if (isFadingIn()) {
fadeInTick(fadeSpeed);
} else if (isFadingOut()) {
fadeOutTick(fadeSpeed);
if(getFadeAlpha() <= 0.0F){
if (getProcessBar().getCurrentProcess() != 0 || getProcessBar().getCurrentProcess() != getProcessBar().getMaxProcess()) {
getProcessBar().retain();
}
stopRender();
}
}
}
/**
* Do Not Override, instead of using <code>renderProcessBar0()</code><br/>
* 请勿重载请重载renderProcessBar0()替代
* @deprecated
*/
@Deprecated
@Override
default void renderProcessBar(PoseStack poseStack, GuiGraphics guiGraphics) {
updateFadeEffect();
renderProcessBar0(poseStack, guiGraphics);
}
void renderProcessBar0(PoseStack poseStack, GuiGraphics guiGraphics);
default void fadeInTick(float fadeSpeed) {
if (getFadeAlpha() >= 1.0f) {
setFadingIn(false);
return;
}
setFadeAlpha(Math.min(getFadeAlpha() + fadeSpeed, 1.0f));
}
default void fadeOutTick(float fadeSpeed) {
if (getFadeAlpha() >= 1.0f) {
setFadingOut(false);
return;
}
setFadeAlpha(Math.max(getFadeAlpha() - fadeSpeed, 0.0f));
}
void setProcessBar(TestProcessBar processBar);
}

View File

@ -0,0 +1,16 @@
package com.r3944realms.leashedplayer.client.renderer.gui;
import com.mojang.blaze3d.vertex.PoseStack;
import com.r3944realms.leashedplayer.client.processBar.IProcessBar;
import net.minecraft.client.gui.GuiGraphics;
public interface IProcessBarRenderer<T extends IProcessBar> {
T getProcessBar();
void setProcessBar(T processBar);
void renderProcessBar(PoseStack poseStack, GuiGraphics guiGraphics);
default void stopRender() {
T processBar = getProcessBar();
processBar.decreaseAliveCount();
processBar.setRenderStatus(false);
}
}

View File

@ -0,0 +1,90 @@
package com.r3944realms.leashedplayer.client.renderer.gui;
import com.mojang.blaze3d.vertex.PoseStack;
import com.r3944realms.leashedplayer.LeashedPlayer;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.GuiGraphics;
import net.minecraft.world.entity.player.Player;
import net.neoforged.api.distmarker.Dist;
import net.neoforged.bus.api.SubscribeEvent;
import net.neoforged.fml.common.EventBusSubscriber;
import net.neoforged.neoforge.client.event.ClientTickEvent;
import net.neoforged.neoforge.client.event.RenderGuiEvent;
@EventBusSubscriber(modid = LeashedPlayer.MOD_ID, value = Dist.CLIENT)
public class ProcessBarRenderer {
private static boolean isRendering = false;
private static final int progressDuration = 100;
private static int currentProgress = 0;
public static void startRenderingProgressBar(Player player) {
isRendering = true;
currentProgress = 0;
}
public static void stopRenderingProgressBar() {
isRendering = false;
}
@SubscribeEvent
public static void onClientTick(ClientTickEvent.Pre event) {
if (isRendering) {
currentProgress++;
if (currentProgress >= progressDuration) {
stopRenderingProgressBar();
}
}
}
@SubscribeEvent
public static void onRendererLevel(RenderGuiEvent.Pre event) {
PoseStack matrixStack = event.getGuiGraphics().pose();
GuiGraphics guiGraphics = event.getGuiGraphics();
if(isRendering) {
renderProgressBar(matrixStack, guiGraphics);
}
}
public static float getProgress() {
return (float) currentProgress / progressDuration;
}
private static void renderProgressBar(PoseStack matrixStack, GuiGraphics guiGraphics) {
Minecraft mc = Minecraft.getInstance();
int screenWidth = mc.getWindow().getGuiScaledWidth();
int screenHeight = mc.getWindow().getGuiScaledHeight();
// 设置进度条的位置和尺寸
int barWidth = screenWidth / 4;
int barHeight = 12;
int x = (screenWidth - barWidth) / 2;
int y = screenHeight - 47;
// 绘制背景灰色
guiGraphics.fill(x, y, x + barWidth, y + barHeight, 0xFF555555);
// 计算当前进度并绘制前景渐变颜色
float progress = getProgress();
int progressWidth = (int) (progress * barWidth);
// 渐变颜色效果从红色到绿色
int color = lerpColor(progress);
guiGraphics.fill(x, y, x + progressWidth, y + barHeight, color);
// 绘制边框分别绘制四条边
int borderColor = 0xFFFFFFFF; // 白色
guiGraphics.fill(x - 1, y - 1, x + barWidth + 1, y, borderColor); // 顶边
guiGraphics.fill(x - 1, y + barHeight, x + barWidth + 1, y + barHeight + 1, borderColor); // 底边
guiGraphics.fill(x - 1, y, x, y + barHeight, borderColor); // 左边
guiGraphics.fill(x + barWidth, y, x + barWidth + 1, y + barHeight, borderColor); // 右边
}
// 渐变颜色计算
private static int lerpColor(float progress) {
int a = (int) (getAlpha(-65536) * (1 - progress) + getAlpha(-16711936) * progress);
int r = (int) (getRed(-65536) * (1 - progress) + getRed(-16711936) * progress);
int g = (int) (getGreen(-65536) * (1 - progress) + getGreen(-16711936) * progress);
int b = (int) (getBlue(-65536) * (1 - progress) + getBlue(-16711936) * progress);
return (a << 24) | (r << 16) | (g << 8) | b;
}
private static int getAlpha(int color) { return (color >> 24) & 0xFF; }
private static int getRed(int color) { return (color >> 16) & 0xFF; }
private static int getGreen(int color) { return (color >> 8) & 0xFF; }
private static int getBlue(int color) { return color & 0xFF; }
}

View File

@ -0,0 +1,161 @@
package com.r3944realms.leashedplayer.client.renderer.gui;
import com.mojang.blaze3d.vertex.PoseStack;
import com.r3944realms.leashedplayer.client.processBar.IProcessBar;
import com.r3944realms.leashedplayer.client.processBar.TestProcessBar;
import com.r3944realms.leashedplayer.utils.ColorUtil;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.GuiGraphics;
import org.jetbrains.annotations.Nullable;
public class TestProcessBarRenderer implements IFadingProcessBarRenderer<IProcessBar>{
@Nullable
private TestProcessBar processBar;
private float FadeAlpha;
private boolean fadingIn, fadingOut;
private float FadeRate;
private float FadeDuration;
public TestProcessBarRenderer(boolean fadingIn, boolean fadingOut, float FadeRate, float FadeDuration){
this.fadingIn = fadingIn;
this.fadingOut = fadingOut;
this.FadeRate = FadeRate;
this.FadeDuration = FadeDuration;
this.FadeAlpha = 0.0F;
}
public TestProcessBarRenderer(boolean fadingIn, boolean fadingOut, float FadeRate, float FadeDuration, float FadeAlpha){
this.fadingIn = fadingIn;
this.fadingOut = fadingOut;
this.FadeRate = FadeRate;
this.FadeDuration = FadeDuration;
this.FadeAlpha = FadeAlpha;
}
public TestProcessBarRenderer(TestProcessBar bar, boolean fadingIn, boolean fadingOut, float FadeRate, float FadeDuration) {
this(fadingIn, fadingOut, FadeRate, FadeDuration);
setProcessBar(bar);
}
public TestProcessBarRenderer(TestProcessBar bar, boolean fadingIn, boolean fadingOut, float FadeRate, float FadeDuration, float FadeAlpha) {
this(fadingIn, fadingOut, FadeRate, FadeDuration, FadeAlpha);
setProcessBar(bar);
}
public TestProcessBarRenderer() {
this(true, false, 0.02f, 10.0f, 0.0f);
}
public TestProcessBarRenderer(TestProcessBar bar) {
this(true, false, 0.02f, 10.0f);
setProcessBar(bar);
}
@Override
public float getFadeAlpha() {
return this.FadeAlpha;
}
@Override
public void setFadeAlpha(float alpha) {
this.FadeAlpha = alpha;
}
@Override
public boolean isFadingIn() {
return this.fadingIn;
}
@Override
public void setFadingIn(boolean fadingIn) {
this.fadingIn = fadingIn;
}
@Override
public boolean isFadingOut() {
return this.fadingOut;
}
@Override
public void setFadingOut(boolean fadingOut) {
this.fadingOut = fadingOut;
}
@Override
public float getFadeRate() {
return this.FadeRate;
}
@Override
public void setFadeRate(float fadeRate) {
this.FadeRate = fadeRate;
}
@Override
public float getFadeDuration() {
return this.FadeDuration;
}
@Override
public void setFadeDuration(float fadeDuration) {
this.FadeDuration = fadeDuration;
}
@Override
public @Nullable TestProcessBar getProcessBar() {
return this.processBar;
}
@Override
public void setProcessBar(IProcessBar processBar) {
if(processBar instanceof TestProcessBar pB){
setProcessBar(pB);
} else throw new UnsupportedOperationException("Not supported Non-TestProcessBar");
}
@Override
public void setProcessBar(TestProcessBar processBar) {
this.processBar = processBar;
}
@Override
public void renderProcessBar0(PoseStack poseStack, GuiGraphics guiGraphics) {
Minecraft mc = Minecraft.getInstance();
int screenWidth = mc.getWindow().getGuiScaledWidth();
int screenHeight = mc.getWindow().getGuiScaledHeight();
int elementWidth = 200;
int elementHeight = 12;
int x = (screenWidth - elementWidth) / 2;
int y = screenHeight - elementHeight - 47; // 控制 GUI 元素的位置
// 计算透明度
float progress = 0;
if (getProcessBar() != null) {
progress = (float) getProcessBar().getCurrentProcess() / getProcessBar().getMaxProcess();
}
int alpha = (int) (FadeAlpha * 255);
int backgroundColor = (alpha << 24) | 0xFF555555; // 背景颜色
int foregroundColor = (alpha << 24) | ColorUtil.lerpColor(progress);; // 前景颜色
// 绘制背景
guiGraphics.fill(x, y, x + elementWidth, y + elementHeight, backgroundColor);
// 绘制前景例如进度条
int progressWidth = (int) (progress * elementWidth);
guiGraphics.fill(x, y, x + progressWidth, y + elementHeight, foregroundColor);
// 绘制边框
int borderColor = 0xFFFFFFFF; // 白色边框
guiGraphics.fill(x - 1, y - 1, x + elementWidth + 1, y, borderColor); // 顶边
guiGraphics.fill(x - 1, y + elementHeight, x + elementWidth + 1, y + elementHeight + 1, borderColor); // 底边
guiGraphics.fill(x - 1, y, x, y + elementHeight, borderColor); // 左边
guiGraphics.fill(x + elementWidth, y, x + elementWidth + 1, y + elementHeight, borderColor); // 右边
}
}

View File

@ -0,0 +1,139 @@
package com.r3944realms.leashedplayer.client.renderer.item;
import com.mojang.serialization.Codec;
import com.mojang.serialization.MapCodec;
import com.mojang.serialization.codecs.RecordCodecBuilder;
import com.r3944realms.leashedplayer.client.renderer.item.properties.conditionalRange.ConditionalRangeItemModelProperties;
import com.r3944realms.leashedplayer.client.renderer.item.properties.conditionalRange.ConditionalRangeItemModelProperty;
import net.minecraft.client.multiplayer.ClientLevel;
import net.minecraft.client.renderer.item.ItemModel;
import net.minecraft.client.renderer.item.ItemModelResolver;
import net.minecraft.client.renderer.item.ItemModels;
import net.minecraft.client.renderer.item.ItemStackRenderState;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.item.ItemDisplayContext;
import net.minecraft.world.item.ItemStack;
import net.neoforged.api.distmarker.Dist;
import net.neoforged.api.distmarker.OnlyIn;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.*;
@OnlyIn(Dist.CLIENT)
public class ConditionalRangeItemModel implements ItemModel {
private final ConditionalRangeItemModelProperty property;
private final float scale;
private final float[] onTrueThresholds;
private final float[] onFalseThresholds;
private final ItemModel[] onTrueModels;
private final ItemModel[] onFalseModels;
private final ItemModel fallback;
public ConditionalRangeItemModel(ConditionalRangeItemModelProperty property, float scale, float[] onTrueThresholds, float[] onFalseThresholds, ItemModel[] onTrueModels, ItemModel[] onFalseModels, ItemModel fallback) {
this.property = property;
this.scale = scale;
this.onTrueThresholds = onTrueThresholds;
this.onFalseThresholds = onFalseThresholds;
this.onTrueModels = onTrueModels;
this.onFalseModels = onFalseModels;
this.fallback = fallback;
}
private static int lastIndexLessOrEqual(float[] thresholds, float value) {
if (thresholds.length < 16) {
for (int k = 0; k < thresholds.length; k++) {
if (thresholds[k] > value) {
return k - 1;
}
}
return thresholds.length - 1;
} else {
int i = Arrays.binarySearch(thresholds, value);
if (i < 0) {
int j = ~i;
return j - 1;
} else {
return i;
}
}
}
@Override
public void update(@NotNull ItemStackRenderState renderState, @NotNull ItemStack stack, @NotNull ItemModelResolver itemModelResolver, @NotNull ItemDisplayContext displayContext, @Nullable ClientLevel level, @Nullable LivingEntity entity, int seed) {
ConditionalRangeItemModelProperty.ConditionalRangeData conditionalRangeData = this.property.get(stack, level, entity, seed, displayContext);
float f = conditionalRangeData.floatValue() * this.scale;
boolean b = conditionalRangeData.boolValue();
ItemModel itemModel;
if(Float.isNaN(f)) {
itemModel = this.fallback; ;
} else {
int i;
if (b) {
i = lastIndexLessOrEqual(this.onTrueThresholds, f);
}
else {
i = lastIndexLessOrEqual(this.onFalseThresholds, f);
}
itemModel = i == -1 ? this.fallback : b ? onTrueModels[i] : onFalseModels[i];
}
itemModel.update(renderState, stack, itemModelResolver, displayContext, level, entity, seed);
}
public record Entry(float threshold, ItemModel.Unbaked model) {
public static final Codec<Entry> CODEC = RecordCodecBuilder.create(
instance -> instance.group(
Codec.FLOAT.fieldOf("threshold").forGetter(Entry::threshold),
ItemModels.CODEC.fieldOf("model").forGetter(Entry::model)
).apply(instance, Entry::new)
);
public static final Comparator<Entry> BY_THRESHOLD = Comparator.comparingDouble(Entry::threshold);
}
public record Unbaked(
ConditionalRangeItemModelProperty property, float scale, List<Entry> trueModels, List<Entry> falseModels, Optional<ItemModel.Unbaked> fallback
) implements ItemModel.Unbaked {
public static final MapCodec<Unbaked> MAP_CODEC = RecordCodecBuilder.mapCodec(
instance -> instance.group(
ConditionalRangeItemModelProperties.MAP_CODEC.forGetter(Unbaked::property),
Codec.FLOAT.optionalFieldOf("scale", 1.0F).forGetter(Unbaked::scale),
Entry.CODEC.listOf().fieldOf("trueModels").forGetter(Unbaked::trueModels),
Entry.CODEC.listOf().fieldOf("falseModels").forGetter(Unbaked::falseModels),
ItemModels.CODEC.optionalFieldOf("fallback").forGetter(Unbaked::fallback)
).apply(instance, Unbaked::new)
);
@Override
public @NotNull MapCodec<? extends ItemModel.Unbaked> type() {
return MAP_CODEC;
}
@Override
public @NotNull ItemModel bake(@NotNull BakingContext context) {
float[] floatArrA = new float[this.trueModels.size()];
float[] floatArrB = new float[this.falseModels.size()];
ItemModel[] itemModelArrA = new ItemModel[this.trueModels.size()];
ItemModel[] itemModelArrB = new ItemModel[this.falseModels.size()];
List<Entry> trueModelList = new ArrayList<>(this.trueModels);
List<Entry> falseModelList = new ArrayList<>(this.falseModels);
trueModelList.sort(Entry.BY_THRESHOLD);
falseModelList.sort(Entry.BY_THRESHOLD);
for (int i = 0; i < trueModelList.size(); i++) {
Entry rangeselectitemmodel$entry = trueModelList.get(i);
floatArrA[i] = rangeselectitemmodel$entry.threshold;
itemModelArrA[i] = rangeselectitemmodel$entry.model.bake(context);
}
for (int i = 0; i < falseModels.size(); i++) {
Entry rangeselectitemmodel$entry = falseModelList.get(i);
floatArrB[i] = rangeselectitemmodel$entry.threshold;
itemModelArrB[i] = rangeselectitemmodel$entry.model.bake(context);
}
ItemModel itemmodel = this.fallback.map(p_387030_ -> p_387030_.bake(context)).orElse(context.missingItemModel());
return new ConditionalRangeItemModel(this.property, this.scale, floatArrA, floatArrB, itemModelArrA, itemModelArrB, itemmodel);
}
@Override
public void resolveDependencies(@NotNull Resolver resolver) {
this.fallback.ifPresent(itemModel -> itemModel.resolveDependencies(resolver));
this.trueModels.forEach(entry -> entry.model.resolveDependencies(resolver));
this.falseModels.forEach(entry -> entry.model.resolveDependencies(resolver));
}
}
}

View File

@ -0,0 +1,46 @@
package com.r3944realms.leashedplayer.client.renderer.item.properties.conditional;
import com.mojang.serialization.Codec;
import com.mojang.serialization.MapCodec;
import com.r3944realms.leashedplayer.content.items.type.ILeashRopeArrow;
import com.r3944realms.leashedplayer.extend.CrossbowItem$ChargeTypeExtend;
import net.minecraft.client.multiplayer.ClientLevel;
import net.minecraft.client.renderer.item.properties.select.SelectItemModelProperty;
import net.minecraft.core.component.DataComponents;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.item.ItemDisplayContext;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.Items;
import net.minecraft.world.item.component.ChargedProjectiles;
import net.neoforged.api.distmarker.Dist;
import net.neoforged.api.distmarker.OnlyIn;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@OnlyIn(Dist.CLIENT)
public record ChargeExtend() implements SelectItemModelProperty<CrossbowItem$ChargeTypeExtend> {
public static Codec<CrossbowItem$ChargeTypeExtend> CODEC = CrossbowItem$ChargeTypeExtend.CODEC;
public static Type<ChargeExtend, CrossbowItem$ChargeTypeExtend> TYPE = Type.create(
MapCodec.unit(new ChargeExtend()), CODEC
);
@Override
public @NotNull CrossbowItem$ChargeTypeExtend get(@NotNull ItemStack stack, @Nullable ClientLevel level, @Nullable LivingEntity entity, int seed, @NotNull ItemDisplayContext displayContext) {
ChargedProjectiles chargedprojectiles = stack.get(DataComponents.CHARGED_PROJECTILES);
if(chargedprojectiles == null || chargedprojectiles.isEmpty()) {
return CrossbowItem$ChargeTypeExtend.NONE;
} else {
return ILeashRopeArrow.isLeashRopeArrow(stack, entity) ? CrossbowItem$ChargeTypeExtend.LEASH_ROPE_ARROW :
chargedprojectiles.contains(Items.FIREWORK_ROCKET) ? CrossbowItem$ChargeTypeExtend.ROCKET : CrossbowItem$ChargeTypeExtend.ARROW;
}
}
@Override
public @NotNull Codec<CrossbowItem$ChargeTypeExtend> valueCodec() {
return CODEC;
}
@Override
public @NotNull Type<? extends SelectItemModelProperty<CrossbowItem$ChargeTypeExtend>, CrossbowItem$ChargeTypeExtend> type() {
return TYPE;
}
}

View File

@ -0,0 +1,27 @@
package com.r3944realms.leashedplayer.client.renderer.item.properties.conditionalRange;
import com.mojang.serialization.MapCodec;
import com.r3944realms.leashedplayer.content.items.type.ILeashRopeArrow;
import net.minecraft.client.multiplayer.ClientLevel;
import net.minecraft.client.renderer.item.properties.numeric.UseDuration;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.item.ItemDisplayContext;
import net.minecraft.world.item.ItemStack;
import org.jetbrains.annotations.Nullable;
public record BowPull() implements ConditionalRangeItemModelProperty {
public static final MapCodec<BowPull> MAP_CODEC = MapCodec.unit(new BowPull());
@Override
public ConditionalRangeData get(ItemStack stack, @Nullable ClientLevel level, @Nullable LivingEntity entity, int seed, ItemDisplayContext displayContext) {
if (entity != null && entity.getUseItem() == stack) {
return ConditionalRangeData.create(ILeashRopeArrow.isLeashRopeArrow(stack, entity), (float)UseDuration.useDuration(stack, entity));
}
else return ConditionalRangeData.create(false, 0.0F);
}
@Override
public MapCodec<? extends ConditionalRangeItemModelProperty> type() {
return MAP_CODEC;
}
}

View File

@ -0,0 +1,19 @@
package com.r3944realms.leashedplayer.client.renderer.item.properties.conditionalRange;
import com.mojang.serialization.MapCodec;
import com.r3944realms.leashedplayer.LeashedPlayer;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.util.ExtraCodecs;
import net.neoforged.api.distmarker.Dist;
import net.neoforged.api.distmarker.OnlyIn;
@OnlyIn(Dist.CLIENT)
public class ConditionalRangeItemModelProperties {
private static final ExtraCodecs.LateBoundIdMapper<ResourceLocation, MapCodec<? extends ConditionalRangeItemModelProperty>> ID_MAPPER = new ExtraCodecs.LateBoundIdMapper<>();
public static final MapCodec<ConditionalRangeItemModelProperty> MAP_CODEC = ID_MAPPER.codec(ResourceLocation.CODEC)
.dispatchMap("property", ConditionalRangeItemModelProperty::type, mapCodec -> mapCodec);
public static void bootstrap() {
ID_MAPPER.put(ResourceLocation.fromNamespaceAndPath(LeashedPlayer.MOD_ID, "bow_pull"), BowPull.MAP_CODEC);
net.neoforged.fml.ModLoader.postEvent(new com.r3944realms.leashedplayer.content.events.RegisterConditionalRangeItemPropertyEvent(ID_MAPPER));
}
}

View File

@ -0,0 +1,23 @@
package com.r3944realms.leashedplayer.client.renderer.item.properties.conditionalRange;
import com.mojang.serialization.MapCodec;
import net.minecraft.client.multiplayer.ClientLevel;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.item.ItemDisplayContext;
import net.minecraft.world.item.ItemStack;
import net.neoforged.api.distmarker.Dist;
import net.neoforged.api.distmarker.OnlyIn;
import javax.annotation.Nullable;
@OnlyIn(Dist.CLIENT)
public interface ConditionalRangeItemModelProperty {
ConditionalRangeData get(ItemStack stack, @Nullable ClientLevel level, @Nullable LivingEntity entity, int seed, ItemDisplayContext displayContext);
MapCodec<? extends ConditionalRangeItemModelProperty> type();
record ConditionalRangeData(boolean boolValue, float floatValue) {
public static ConditionalRangeData create(boolean boolValue, float floatValue) {
return new ConditionalRangeData(boolValue, floatValue);
}
}
}

View File

@ -0,0 +1,39 @@
package com.r3944realms.leashedplayer.config;
import net.neoforged.neoforge.common.ModConfigSpec;
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, TheLeashArrowMaxLifeTime;
public static ModConfigSpec.DoubleValue TheMultipleThatLeashRopeArrowBreakLength, TheLeashBreakLengthTimesBase;
static {
BUILDER.comment("Leash Player Config");
BUILDER.comment("Leash Player Command");
BUILDER.push("Command");
EnableLeashPlayerCommandPrefix = BUILDER.comment("The prefix of this mod's commands"," [ Default: true] ").define("EnableLeashPlayerCommand", true);
LeashedPlayerModCommandPrefix = BUILDER.comment("The prefix of this mod's commands"," [ Default:'lp'] ").define("LeashedPlayerModCommandPrefix", "lp");
BUILDER.pop();
BUILDER.comment("Leash Player Arrow");
BUILDER.push("LeashRopeArrow");
TheMultipleThatLeashRopeArrowBreakLength = BUILDER.comment("How many times is the length of the arrow rope based on BreakLength TimeBase", "[ Default : 5.0f, Invalid Range:[2.0f, 10.0f] ]").defineInRange("TheMultipleArrowBreak", 5.0f, 2.0f , 10.0f);
TheLeashArrowMaxLifeTime = BUILDER.comment("If the LeashArrowEntity's life is bigger than this value ,it will be discarded", "[ Default : 2400, Invalid Range:[1200 , 10240]]").defineInRange("TheLeashArrowMaxLifeTime",2400, 1200, 10240);
BUILDER.pop();
BUILDER.comment("Leash Player Misc");
BUILDER.push("Misc");
BUILDER.comment("Leash Player Length");
BUILDER.push("LeashLength");
TheLeashBreakLengthTimesBase = BUILDER.comment("When it exceeds how many times, the leash will drop"," [ Default:3.0f, Valid Range:[3.0f, 6.0f] ]").defineInRange("BreakLengthTimeBase", 3.0f, 3.0f ,6.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);
BUILDER.pop().pop();
SPEC = BUILDER.build();
}
}

View File

@ -0,0 +1,37 @@
package com.r3944realms.leashedplayer.content;
import com.mojang.blaze3d.platform.InputConstants;
import net.minecraft.client.KeyMapping;
import net.neoforged.neoforge.client.settings.KeyConflictContext;
import net.neoforged.neoforge.client.settings.KeyModifier;
import org.lwjgl.glfw.GLFW;
public class ModKeyMapping {
static String KEY_ROOT_ = "key.leashedplayer.";
public static String CATEGORY = "key.leashedplayer.category";
public static String
ADD_LEASH_LENGTH_KEY = KEY_ROOT_ + "leash_length.add",
SUB_LEASH_LENGTH_KEY = KEY_ROOT_ + "leash_length.sub",
NOT_SUPPORT_TO_NOT_PLAYER_ENTITY = KEY_ROOT_ + "leash_length.not_support_to_not_player_entity";
public static final KeyMapping KEY_ADD_LEASH_LENGTH =
new KeyMapping(
ADD_LEASH_LENGTH_KEY,
KeyConflictContext.IN_GAME,
KeyModifier.CONTROL,
InputConstants.Type.KEYSYM,
GLFW.GLFW_KEY_UP,
CATEGORY
);
public static final KeyMapping KEY_SUB_LEASH_LENGTH =
new KeyMapping(
SUB_LEASH_LENGTH_KEY,
KeyConflictContext.IN_GAME,
KeyModifier.CONTROL,
InputConstants.Type.KEYSYM,
GLFW.GLFW_KEY_DOWN,
CATEGORY
);
}

View File

@ -0,0 +1,22 @@
package com.r3944realms.leashedplayer.content.commands;
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
import com.r3944realms.leashedplayer.config.LeashPlayerCommonConfig;
import net.minecraft.commands.CommandSourceStack;
import net.minecraft.commands.Commands;
import org.jetbrains.annotations.Nullable;
import java.util.List;
public class Command {
public static final String PREFIX = LeashPlayerCommonConfig.LeashedPlayerModCommandPrefix.get();
public static boolean SHOULD_USE_PREFIX = LeashPlayerCommonConfig.EnableLeashPlayerCommandPrefix.get();
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;
}
}

View File

@ -0,0 +1,58 @@
package com.r3944realms.leashedplayer.content.commands;
import com.mojang.brigadier.CommandDispatcher;
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
import net.minecraft.commands.CommandSourceStack;
import net.minecraft.commands.Commands;
import net.minecraft.commands.arguments.EntityArgument;
import net.minecraft.network.Connection;
import net.minecraft.network.PacketSendListener;
import net.minecraft.network.chat.Component;
import net.minecraft.network.chat.MutableComponent;
import net.minecraft.network.protocol.common.ClientboundDisconnectPacket;
import net.minecraft.server.level.ServerPlayer;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.List;
import static com.r3944realms.leashedplayer.content.commands.Command.SHOULD_USE_PREFIX;
import static com.r3944realms.leashedplayer.content.commands.Command.getLiterArgumentBuilderOfCSS;
public class DebugCommand {
public static void register(CommandDispatcher<CommandSourceStack> dispatcher) {
@Nullable List<LiteralArgumentBuilder<CommandSourceStack>> nodeList = SHOULD_USE_PREFIX ? null : new ArrayList<>();
LiteralArgumentBuilder<CommandSourceStack> literalArgumentBuilder = Commands.literal(Command.PREFIX);
LiteralArgumentBuilder<CommandSourceStack> $$DebugRoot = getLiterArgumentBuilderOfCSS("debug", !SHOULD_USE_PREFIX, nodeList);
com.mojang.brigadier.Command<CommandSourceStack> kickTargets = context -> {
for(ServerPlayer entity : EntityArgument.getPlayers(context, "targets")){
MutableComponent literal = Component.literal("You are already connected to this proxy!");
Connection connection = entity.connection.getConnection();
connection.send(new ClientboundDisconnectPacket(literal), PacketSendListener.thenRun(() -> connection.disconnect(literal)));
}
return 0;
};
com.mojang.brigadier.Command<CommandSourceStack> kickSelf = context -> {
CommandSourceStack source = context.getSource();
if (source.getEntity() instanceof ServerPlayer serverPlayer) {
MutableComponent literal = Component.literal("You are already connected to this proxy!");
Connection connection = serverPlayer.connection.getConnection();
connection.send(new ClientboundDisconnectPacket(literal), PacketSendListener.thenRun(() -> connection.disconnect(literal)));
}
return 0;
};
LiteralArgumentBuilder<CommandSourceStack> debugKick = $$DebugRoot.requires(commandSourceStack -> commandSourceStack.hasPermission(2))
.then(
Commands.literal("kick").executes(kickSelf)
.then(Commands.argument("targets", EntityArgument.entities())
.executes(kickTargets)
)
);
if(SHOULD_USE_PREFIX){
literalArgumentBuilder.then(debugKick);
dispatcher.register(literalArgumentBuilder);
} else {
nodeList.forEach(dispatcher::register);
}
}
}

View File

@ -0,0 +1,485 @@
package com.r3944realms.leashedplayer.content.commands;
import com.mojang.brigadier.Command;
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.config.LeashPlayerCommonConfig;
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 org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import static com.r3944realms.leashedplayer.content.commands.Command.getLiterArgumentBuilderOfCSS;
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";
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"
;
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 {
ServerPlayer player = source.getPlayerOrException();
float leashLength = ((ILivingEntityExtension)player).getLeashLength();
source.sendSuccess(() -> Component.translatable(LEASH_LENGTH_SHOW, player.getDisplayName(), leashLength), true);
} catch (Exception e) {
source.sendFailure(Component.translatable(LEASH_FAILED));
return -1;
}
return 0;
};
Command<CommandSourceStack> getRefPlayerLeashLength = context -> {
CommandSourceStack source = context.getSource();
try {
ServerPlayer player = EntityArgument.getPlayer(context, "targetPlayer");
float leashLength = ((ILivingEntityExtension)player).getLeashLength();
source.sendSuccess(() -> Component.translatable(LEASH_LENGTH_SHOW, player.getDisplayName(), leashLength), true);
} catch (Exception e) {
source.sendFailure(Component.translatable(LEASH_FAILED));
return -1;
}
return 0;
};
Command<CommandSourceStack> getRefPlayersLeashLength = context -> {
CommandSourceStack source = context.getSource();
try {
Collection<ServerPlayer> playerCol = EntityArgument.getPlayers(context, "targetPlayers");
playerCol.forEach(player -> {
float leashLength = ((ILivingEntityExtension) player).getLeashLength();
source.sendSuccess(() -> Component.translatable(LEASH_LENGTH_SHOW, player.getDisplayName(), leashLength), true);
});
} catch (Exception e) {
source.sendFailure(Component.translatable(LEASH_FAILED));
return -1;
}
return 0;
};
Command<CommandSourceStack> setSelfLengthLeashLength = context -> {
CommandSourceStack source = context.getSource();
try {
ServerPlayer player = source.getPlayerOrException();
float leashLength = context.getArgument("leashLength", Float.class);
((ILivingEntityExtension)player).setLeashLength(leashLength);
source.sendSuccess(() -> Component.translatable(LEASH_LENGTH_SET, player.getDisplayName(), leashLength), true);
} catch (Exception e) {
source.sendFailure(Component.translatable(LEASH_FAILED));
return -1;
}
return 0;
};
Command<CommandSourceStack> setRefPlayerLengthLeashLength = context -> {
CommandSourceStack source = context.getSource();
try {
ServerPlayer player = EntityArgument.getPlayer(context, "targetPlayer");
float leashLength = context.getArgument("leashLength", Float.class);
((ILivingEntityExtension)player).setLeashLength(leashLength);
source.sendSuccess(() -> Component.translatable(LEASH_LENGTH_SET, player.getDisplayName(), leashLength), true);
} catch (Exception e) {
source.sendFailure(Component.translatable(LEASH_FAILED));
return -1;
}
return 0;
};
Command<CommandSourceStack> setRefPlayersLengthLeashLength = context -> {
CommandSourceStack source = context.getSource();
float leashLength = context.getArgument("leashLength", Float.class);
try {
Collection<ServerPlayer> playerCol = EntityArgument.getPlayers(context, "targetPlayers");
playerCol.forEach(player -> {
try {
((ILivingEntityExtension)player).setLeashLength(leashLength);
source.sendSuccess(() -> Component.translatable(LEASH_LENGTH_SET, player.getDisplayName(), leashLength), true);
} catch (Exception e) {
throw new RuntimeException(e);
}
});
} catch (Exception e) {
source.sendFailure(Component.translatable(LEASH_FAILED));
return -1;
}
return 0;
};
//获取Data 构造一个MutableComponent显示数据
Command<CommandSourceStack> geSelfLeashData = context -> {
CommandSourceStack source = context.getSource();
try {
ServerPlayer player = source.getPlayerOrException();
Integer x = LeashLengthGetResultInt(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 = LeashLengthGetResultInt(player, source);
if (x != null) return x;
} catch (Exception e) {
source.sendFailure(Component.translatable(LEASH_FAILED));
return -1;
}
return 0;
};
Command<CommandSourceStack> getRefPlayersLeashData = context -> {
CommandSourceStack source = context.getSource();
try {
Collection<ServerPlayer> playerCol = EntityArgument.getPlayers(context, "targetPlayers");
playerCol.forEach(player -> {
try {
LeashLengthGetResultInt(player, source);
} catch (Exception e) {
throw new RuntimeException(e);
}
});
} 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 = LeashDataEntitySetResultInt(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 = LeashDataBlockPosSetResultInt(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 = LeashDataEntitySetResultInt(context, player, source);
if (x != null) return x;
} catch (Exception e) {
source.sendFailure(Component.translatable(LEASH_FAILED));
return -1;
}
return 0;
};
Command<CommandSourceStack> setRefPlayersLeashDataEntity = context -> {
CommandSourceStack source = context.getSource();
try {
Collection<ServerPlayer> playerCol = EntityArgument.getPlayers(context, "targetPlayers");
playerCol.forEach(player -> {
try {
LeashDataEntitySetResultInt(context, player, source);
} catch (CommandSyntaxException e) {
throw new RuntimeException(e);
}
});
} catch (Exception e) {
source.sendFailure(Component.translatable(LEASH_FAILED));
return -1;
}
return 0;
};
Command<CommandSourceStack> setRefPlayersLeashDataByBlockPos = context -> {
CommandSourceStack source = context.getSource();
try {
Collection<ServerPlayer> playerCol = EntityArgument.getPlayers(context, "targetPlayers");
playerCol.forEach(player -> {
LeashDataBlockPosSetResultInt(context, source, player);
});
} 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 = LeashDataBlockPosSetResultInt(context, source, player);
if (x != null) return x;
} catch (Exception e) {
source.sendFailure(Component.translatable(LEASH_FAILED));
return -1;
}
return 0;
};
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.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> clearRefPlayersLeashData = context -> {
CommandSourceStack source = context.getSource();
try {
Collection<ServerPlayer> playerCol = EntityArgument.getPlayers(context, "targetPlayers");
playerCol.forEach(player -> {
try {
LeashDataClearResultInt(source, PlayerLeashable.getLeashDataEntity(player, source.getLevel()),player);
} catch (Exception e) {
throw new RuntimeException(e);
}
});
} 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("targetPlayer", 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(setRefPlayerLengthLeashLength)
)
)
)
.then(Commands.argument("targetPlayers", EntityArgument.players()).executes(getRefPlayersLeashLength)
.then(Commands.literal("get").executes(getRefPlayersLeashLength))
.then(Commands.literal("set").requires(cs -> cs.hasPermission(2))
.then(
Commands.argument("leashLength", FloatArgumentType.floatArg(MIN_VALUE, MAX_VALUE)).executes(setRefPlayersLengthLeashLength)
)
)
)
);
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.argument("BlockPos", BlockPosArgument.blockPos())
.executes(setRefPlayerLeashDataByBlockPos)
)
)
.then(Commands.literal("clear").requires(cs -> cs.hasPermission(2)).executes(clearRefPlayerLeashData))
)
.then(Commands.argument("targetPlayers", EntityArgument.players()).executes(getRefPlayersLeashData)
.then(Commands.literal("get")
.executes(getRefPlayersLeashData)
)
.then(Commands.literal("set").requires(cs -> cs.hasPermission(2))
.then(Commands.argument("holderEntity", EntityArgument.entity())
.executes(setRefPlayersLeashDataEntity)
)
.then(Commands.argument("BlockPos", BlockPosArgument.blockPos())
.executes(setRefPlayersLeashDataByBlockPos)
)
)
.then(Commands.literal("clear").requires(cs -> cs.hasPermission(2)).executes(clearRefPlayersLeashData))
));
LiteralArgumentBuilder<CommandSourceStack> SelfData = $$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)
)
)
.then(Commands.literal("clear").requires(cs -> cs.hasPermission(2))
.executes(clearSelfLeashData)
)
);
if(shouldUsePrefix) {
literalArgumentBuilder
.then(RefPlayerLeashLength)
.then(SelfLeashLength)
.then(RefPLayerData)
.then(SelfData);
dispatcher.register(literalArgumentBuilder);
} else {
nodeList.forEach(dispatcher::register);
}
}
private static @Nullable Integer LeashLengthGetResultInt(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 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;
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 LeashDataCommonPartSetResultInt(source, player, leashDataEntity, leashedPlayer, targetPlayerDisplayName, leashDataEntityDisplayName);
}
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();
Component leashDataEntityDisplayName = leashDataEntity.getDisplayName();
if(player.equals(leashDataEntity)) {
source.sendFailure(Component.translatable(LEASH_DATA_SET_FAILED_FORBID_SAME_ENTITY));
return 1;
}
return LeashDataCommonPartSetResultInt(source, player, leashDataEntity, leashedPlayer, targetPlayerDisplayName, 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;
}
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;
}
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;
}
Leashable.dropLeash((Entity & Leashable) serverPlayer, true, false);
source.sendSuccess(() -> Component.translatable(LEASH_DATA_CLEAR, serverPlayer.getDisplayName(), leashDataEntity.getDisplayName()), true);
return null;
}
}

View File

@ -0,0 +1,129 @@
package com.r3944realms.leashedplayer.content.commands;
import com.mojang.brigadier.Command;
import com.mojang.brigadier.CommandDispatcher;
import com.mojang.brigadier.arguments.DoubleArgumentType;
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
import com.r3944realms.leashedplayer.config.LeashPlayerCommonConfig;
import com.r3944realms.leashedplayer.network.client.UpdatePlayerMovement;
import net.minecraft.commands.CommandSourceStack;
import net.minecraft.commands.Commands;
import net.minecraft.commands.arguments.EntityArgument;
import net.minecraft.network.chat.Component;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.phys.Vec3;
import net.neoforged.neoforge.network.PacketDistributor;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.List;
import static com.r3944realms.leashedplayer.content.commands.Command.getLiterArgumentBuilderOfCSS;
public class MotionCommand {
private final static String LEASHED_PLAYER_MOTION_MESSAGE_ = "leashedplayer.command.motion.message.";
public final static String MOTION_SETTER_SUCCESSFUL = LEASHED_PLAYER_MOTION_MESSAGE_ + "setter.successful",
MOTION_ADDER_SUCCESSFUL = LEASHED_PLAYER_MOTION_MESSAGE_ + "adder.successful",
MOTION_MULTIPLY_SUCCESSFUL = LEASHED_PLAYER_MOTION_MESSAGE_ + "multiply.successful";
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> $$motionRoot = getLiterArgumentBuilderOfCSS("motion", !shouldUsePrefix, nodeList);
Command<CommandSourceStack> motionVecAdder = context -> {
CommandSourceStack source = context.getSource();
for(Entity entity : EntityArgument.getEntities(context, "targets")){
Vec3 motionVec = new Vec3(
DoubleArgumentType.getDouble(context, "vecX"),
DoubleArgumentType.getDouble(context, "vecY"),
DoubleArgumentType.getDouble(context, "vecZ")
);
if(entity instanceof ServerPlayer player) {
PacketDistributor.sendToPlayer(player, new UpdatePlayerMovement(UpdatePlayerMovement.Operation.ADD, motionVec.x, motionVec.y, motionVec.z));
player.addDeltaMovement(new Vec3(motionVec.x, motionVec.y, motionVec.z));
} else {
entity.addDeltaMovement(motionVec);
}
double vecX = entity.getDeltaMovement().x, vecY = entity.getDeltaMovement().y, vecZ = entity.getDeltaMovement().z;
source.sendSuccess(() -> Component.translatable(MOTION_ADDER_SUCCESSFUL, entity.getName().copy().withStyle(), vecX, vecY, vecZ), true);
}
return 0;
};
Command<CommandSourceStack> motionVecSetter = context -> {
CommandSourceStack source = context.getSource();
for(Entity entity : EntityArgument.getEntities(context, "targets")){
Vec3 motionVec = new Vec3(
DoubleArgumentType.getDouble(context, "vecX"),
DoubleArgumentType.getDouble(context, "vecY"),
DoubleArgumentType.getDouble(context, "vecZ")
);
if(entity instanceof ServerPlayer player) {
PacketDistributor.sendToPlayer(player, new UpdatePlayerMovement(UpdatePlayerMovement.Operation.SET, motionVec.x, motionVec.y, motionVec.z));
player.setDeltaMovement(new Vec3(motionVec.x, motionVec.y, motionVec.z));
} else {
entity.setDeltaMovement(motionVec);
}
double vecX = entity.getDeltaMovement().x, vecY = entity.getDeltaMovement().y, vecZ = entity.getDeltaMovement().z;
source.sendSuccess(() -> Component.translatable(MOTION_SETTER_SUCCESSFUL, entity.getName().copy(), vecX, vecY, vecZ), true);
}
return 0;
};
Command<CommandSourceStack> motionVecMultiply = context -> {
CommandSourceStack source = context.getSource();
for(Entity entity : EntityArgument.getEntities(context, "targets")){
Vec3 motionFactorVec = new Vec3(
DoubleArgumentType.getDouble(context, "vecXFactor"),
DoubleArgumentType.getDouble(context, "vecYFactor"),
DoubleArgumentType.getDouble(context, "vecZFactor")
);
if(entity instanceof ServerPlayer player) {
PacketDistributor.sendToPlayer(player, new UpdatePlayerMovement(UpdatePlayerMovement.Operation.MULTIPLY, motionFactorVec.x, motionFactorVec.y, motionFactorVec.z));
player.setDeltaMovement(entity.getDeltaMovement().multiply(motionFactorVec));
} else {
entity.setDeltaMovement(entity.getDeltaMovement().multiply(motionFactorVec));
}
double vecX = entity.getDeltaMovement().x, vecY = entity.getDeltaMovement().y, vecZ = entity.getDeltaMovement().z;
source.sendSuccess(() -> Component.translatable(MOTION_MULTIPLY_SUCCESSFUL, entity.getName().copy(), vecX, vecY, vecZ), true);
}
return 0;
};
LiteralArgumentBuilder<CommandSourceStack> Motion = $$motionRoot.requires(cs -> cs.hasPermission(2))
.then(Commands.argument("targets", EntityArgument.entities())
.then(Commands.literal("add")
.then(Commands.argument("vecX", DoubleArgumentType.doubleArg())
.then(Commands.argument("vecY", DoubleArgumentType.doubleArg())
.then(Commands.argument("vecZ", DoubleArgumentType.doubleArg())
.executes(motionVecAdder)
)
)
)
)
.then(Commands.literal("set")
.then(Commands.argument("vecX", DoubleArgumentType.doubleArg())
.then(Commands.argument("vecY", DoubleArgumentType.doubleArg())
.then(Commands.argument("vecZ", DoubleArgumentType.doubleArg())
.executes(motionVecSetter)
)
)
)
)
.then(Commands.literal("multiply")
.then(Commands.argument("vecXFactor", DoubleArgumentType.doubleArg())
.then(Commands.argument("vecYFactor", DoubleArgumentType.doubleArg())
.then(Commands.argument("vecZFactor", DoubleArgumentType.doubleArg())
.executes(motionVecMultiply)
)
)
)
)
);
if(shouldUsePrefix){
literalArgumentBuilder.then(Motion);
dispatcher.register(literalArgumentBuilder);
} else {
nodeList.forEach(dispatcher::register);
}
}
}

View File

@ -0,0 +1,181 @@
package com.r3944realms.leashedplayer.content.commands;
import com.mojang.brigadier.CommandDispatcher;
import com.mojang.brigadier.arguments.FloatArgumentType;
import com.mojang.brigadier.arguments.IntegerArgumentType;
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
import com.r3944realms.leashedplayer.config.LeashPlayerCommonConfig;
import net.minecraft.commands.CommandSourceStack;
import net.minecraft.commands.Commands;
import net.minecraft.commands.SharedSuggestionProvider;
import net.minecraft.commands.arguments.TimeArgument;
import net.minecraft.network.chat.Component;
import net.minecraft.server.ServerTickRateManager;
import net.minecraft.util.TimeUtil;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import static com.r3944realms.leashedplayer.content.commands.Command.getLiterArgumentBuilderOfCSS;
public class TickCommand {
private static final float MAX_TICKRATE = 10000.0F;
private static final String DEFAULT_TICKRATE = String.valueOf(20);
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(Command.PREFIX);
LiteralArgumentBuilder<CommandSourceStack> $$TickRoot = getLiterArgumentBuilderOfCSS("tick", !shouldUsePrefix, nodeList);
LiteralArgumentBuilder<CommandSourceStack> Tick = $$TickRoot.requires(p_308941_ -> p_308941_.hasPermission(2))
.then(Commands.literal("query").executes(p_308950_ -> tickQuery(p_308950_.getSource())))
.then(
Commands.literal("rate")
.then(
Commands.argument("rate", FloatArgumentType.floatArg(1.0F, 10000.0F))
.suggests((p_308897_, p_308880_) -> SharedSuggestionProvider.suggest(new String[]{DEFAULT_TICKRATE}, p_308880_))
.executes(p_309119_ -> setTickingRate(p_309119_.getSource(), FloatArgumentType.getFloat(p_309119_, "rate")))
)
)
.then(
Commands.literal("step")
.executes(p_309496_ -> step(p_309496_.getSource(), 1))
.then(Commands.literal("stop").executes(p_309035_ -> stopStepping(p_309035_.getSource())))
.then(
Commands.argument("time", TimeArgument.time(1))
.suggests((p_309113_, p_309105_) -> SharedSuggestionProvider.suggest(new String[]{"1t", "1s"}, p_309105_))
.executes(p_308930_ -> step(p_308930_.getSource(), IntegerArgumentType.getInteger(p_308930_, "time")))
)
)
.then(
Commands.literal("sprint")
.then(Commands.literal("stop").executes(p_309190_ -> stopSprinting(p_309190_.getSource())))
.then(
Commands.argument("time", TimeArgument.time(1))
.suggests((p_308987_, p_309101_) -> SharedSuggestionProvider.suggest(new String[]{"60s", "1d", "3d"}, p_309101_))
.executes(p_308904_ -> sprint(p_308904_.getSource(), IntegerArgumentType.getInteger(p_308904_, "time")))
)
)
.then(Commands.literal("unfreeze").executes(p_309184_ -> setFreeze(p_309184_.getSource(), false)))
.then(Commands.literal("freeze").executes(p_309070_ -> setFreeze(p_309070_.getSource(), true)));
if(shouldUsePrefix){
literalArgumentBuilder.then(Tick);
dispatcher.register(literalArgumentBuilder);
} else {
nodeList.forEach(dispatcher::register);
}
}
private static String nanosToMilisString(long nanos) {
return String.format("%.1f", (float)nanos / (float) TimeUtil.NANOSECONDS_PER_MILLISECOND);
}
private static int setTickingRate(CommandSourceStack source, float tickRate) {
ServerTickRateManager servertickratemanager = source.getServer().tickRateManager();
servertickratemanager.setTickRate(tickRate);
String s = String.format("%.1f", tickRate);
source.sendSuccess(() -> Component.translatable("commands.tick.rate.success", s), true);
return (int)tickRate;
}
private static int tickQuery(CommandSourceStack source) {
ServerTickRateManager servertickratemanager = source.getServer().tickRateManager();
String s = nanosToMilisString(source.getServer().getAverageTickTimeNanos());
float f = servertickratemanager.tickrate();
String s1 = String.format("%.1f", f);
if (servertickratemanager.isSprinting()) {
source.sendSuccess(() -> Component.translatable("commands.tick.status.sprinting"), false);
source.sendSuccess(() -> Component.translatable("commands.tick.query.rate.sprinting", s1, s), false);
} else {
if (servertickratemanager.isFrozen()) {
source.sendSuccess(() -> Component.translatable("commands.tick.status.frozen"), false);
} else if (servertickratemanager.nanosecondsPerTick() < source.getServer().getAverageTickTimeNanos()) {
source.sendSuccess(() -> Component.translatable("commands.tick.status.lagging"), false);
} else {
source.sendSuccess(() -> Component.translatable("commands.tick.status.running"), false);
}
String s2 = nanosToMilisString(servertickratemanager.nanosecondsPerTick());
source.sendSuccess(() -> Component.translatable("commands.tick.query.rate.running", s1, s, s2), false);
}
long[] along = Arrays.copyOf(source.getServer().getTickTimesNanos(), source.getServer().getTickTimesNanos().length);
Arrays.sort(along);
String s3 = nanosToMilisString(along[along.length / 2]);
String s4 = nanosToMilisString(along[(int)((double)along.length * 0.95)]);
String s5 = nanosToMilisString(along[(int)((double)along.length * 0.99)]);
source.sendSuccess(() -> Component.translatable("commands.tick.query.percentiles", s3, s4, s5, along.length), false);
return (int)f;
}
private static int sprint(CommandSourceStack source, int sprintTime) {
boolean flag = source.getServer().tickRateManager().requestGameToSprint(sprintTime);
if (flag) {
source.sendSuccess(() -> Component.translatable("commands.tick.sprint.stop.success"), true);
}
source.sendSuccess(() -> Component.translatable("commands.tick.status.sprinting"), true);
return 1;
}
private static int setFreeze(CommandSourceStack source, boolean frozen) {
ServerTickRateManager servertickratemanager = source.getServer().tickRateManager();
if (frozen) {
if (servertickratemanager.isSprinting()) {
servertickratemanager.stopSprinting();
}
if (servertickratemanager.isSteppingForward()) {
servertickratemanager.stopStepping();
}
}
servertickratemanager.setFrozen(frozen);
if (frozen) {
source.sendSuccess(() -> Component.translatable("commands.tick.status.frozen"), true);
} else {
source.sendSuccess(() -> Component.translatable("commands.tick.status.running"), true);
}
return frozen ? 1 : 0;
}
private static int step(CommandSourceStack source, int ticks) {
ServerTickRateManager servertickratemanager = source.getServer().tickRateManager();
boolean flag = servertickratemanager.stepGameIfPaused(ticks);
if (flag) {
source.sendSuccess(() -> Component.translatable("commands.tick.step.success", ticks), true);
} else {
source.sendFailure(Component.translatable("commands.tick.step.fail"));
}
return 1;
}
private static int stopStepping(CommandSourceStack source) {
ServerTickRateManager servertickratemanager = source.getServer().tickRateManager();
boolean flag = servertickratemanager.stopStepping();
if (flag) {
source.sendSuccess(() -> Component.translatable("commands.tick.step.stop.success"), true);
return 1;
} else {
source.sendFailure(Component.translatable("commands.tick.step.stop.fail"));
return 0;
}
}
private static int stopSprinting(CommandSourceStack source) {
ServerTickRateManager servertickratemanager = source.getServer().tickRateManager();
boolean flag = servertickratemanager.stopSprinting();
if (flag) {
source.sendSuccess(() -> Component.translatable("commands.tick.sprint.stop.success"), true);
return 1;
} else {
source.sendFailure(Component.translatable("commands.tick.sprint.stop.fail"));
return 0;
}
}
}

View File

@ -0,0 +1,47 @@
package com.r3944realms.leashedplayer.content.criteriaTriggers;
import com.mojang.serialization.Codec;
import com.mojang.serialization.codecs.RecordCodecBuilder;
import com.r3944realms.leashedplayer.modInterface.PlayerLeashable;
import net.minecraft.advancements.Criterion;
import net.minecraft.advancements.critereon.ContextAwarePredicate;
import net.minecraft.advancements.critereon.EntityPredicate;
import net.minecraft.advancements.critereon.SimpleCriterionTrigger;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.entity.Entity;
import org.jetbrains.annotations.NotNull;
import java.util.Optional;
public class LeashPlayerTrigger extends SimpleCriterionTrigger<LeashPlayerTrigger.TriggerInstance> {
@Override
public @NotNull Codec<TriggerInstance> codec() {
return TriggerInstance.CODEC;
}
public void trigger(ServerPlayer pPlayer, Entity pHolderEntity) {
this.trigger(pPlayer, pl -> pl.matches(pPlayer, pHolderEntity));
}
public record TriggerInstance(Optional<ContextAwarePredicate> player, Optional<EntityPredicate> holder) implements SimpleInstance {
public static final Codec<TriggerInstance> CODEC =
RecordCodecBuilder.create(instance ->
instance.group(
EntityPredicate.ADVANCEMENT_CODEC.optionalFieldOf("player").forGetter(TriggerInstance::player),
EntityPredicate.CODEC.optionalFieldOf("holder").forGetter(TriggerInstance::holder)
)
.apply(instance, TriggerInstance::new));
// public static Criterion<TriggerInstance> LeashSelf() {
// return ModCriteriaTriggers.LEASH_PLAYER_TRIGGER.get().createCriterion(new TriggerInstance(Optional.empty(), Optional.empty()));
// }
public static Criterion<TriggerInstance> LeashPlayer(EntityPredicate.Builder holder) {
return ModCriteriaTriggers.LEASH_PLAYER_TRIGGER.get().createCriterion(new TriggerInstance(Optional.empty(), Optional.of(holder.build())));
}
public boolean matches(ServerPlayer player, Entity holder) {
PlayerLeashable ppl = (PlayerLeashable) player;
return ppl.isLeashed() && holder.equals(PlayerLeashable.getLeashDataEntity(player, player.serverLevel())) && this.holder.isPresent() && this.holder.get().matches(player, holder);
}
}
}

View File

@ -0,0 +1,17 @@
package com.r3944realms.leashedplayer.content.criteriaTriggers;
import com.r3944realms.leashedplayer.LeashedPlayer;
import net.minecraft.advancements.CriterionTrigger;
import net.minecraft.core.registries.Registries;
import net.neoforged.bus.api.IEventBus;
import net.neoforged.neoforge.registries.DeferredHolder;
import net.neoforged.neoforge.registries.DeferredRegister;
public class ModCriteriaTriggers {
public static final DeferredRegister<CriterionTrigger<?>> TRIGGERS = DeferredRegister.create(Registries.TRIGGER_TYPE, LeashedPlayer.MOD_ID);
public static final DeferredHolder<CriterionTrigger<?>, LeashPlayerTrigger> LEASH_PLAYER_TRIGGER =
TRIGGERS.register("leash_player", LeashPlayerTrigger::new);
public static void register(IEventBus eventBus) {
TRIGGERS.register(eventBus);
}
}

View File

@ -0,0 +1,33 @@
package com.r3944realms.leashedplayer.content.effects;
import com.r3944realms.leashedplayer.LeashedPlayer;
import com.r3944realms.leashedplayer.content.effects.type.NoLeashEffect;
import net.minecraft.core.registries.Registries;
import net.minecraft.world.effect.MobEffect;
import net.minecraft.world.effect.MobEffectCategory;
import net.neoforged.bus.api.IEventBus;
import net.neoforged.neoforge.registries.DeferredHolder;
import net.neoforged.neoforge.registries.DeferredRegister;
import java.util.function.Supplier;
public class ModEffectRegister {
public static DeferredRegister<MobEffect> MOB_EFFECT = DeferredRegister.create(Registries.MOB_EFFECT, LeashedPlayer.MOD_ID);
public static DeferredHolder<MobEffect, ? extends MobEffect> NO_LEASH_EFFECT = register(
"no_leash",
() -> new NoLeashEffect(MobEffectCategory.NEUTRAL, 12063764)
);
public static <T extends MobEffect>DeferredHolder<MobEffect, T> register(String name, Supplier<T> effect) {
return MOB_EFFECT.register(name, effect);
}
public static String getEffectKey(MobEffect effect) {
return effect.getDescriptionId();
}
public static String getModEffectKey(DeferredHolder<MobEffect, ? extends MobEffect> effect) {
return getEffectKey(effect.get());
}
public static void register(IEventBus eventBus) {
MOB_EFFECT.register(eventBus);
}
}

View File

@ -0,0 +1,41 @@
package com.r3944realms.leashedplayer.content.effects;
import com.r3944realms.leashedplayer.LeashedPlayer;
import net.minecraft.core.registries.Registries;
import net.minecraft.world.effect.MobEffectInstance;
import net.minecraft.world.item.alchemy.Potion;
import net.neoforged.bus.api.IEventBus;
import net.neoforged.neoforge.registries.DeferredHolder;
import net.neoforged.neoforge.registries.DeferredRegister;
import java.util.function.Supplier;
public class ModPotionRegister {
public static DeferredRegister<Potion> POTIONS = DeferredRegister.create(Registries.POTION, LeashedPlayer.MOD_ID);
public static final DeferredHolder<Potion, Potion> NO_LEASH = register("no_leash",
() -> new Potion("no_leash", new MobEffectInstance(ModEffectRegister.NO_LEASH_EFFECT, 7200, 0))
);
public static <T extends Potion>DeferredHolder<Potion, Potion> register(String Name, Supplier<T> supplier) {
return POTIONS.register(Name, supplier);
}
/**
*
* @param name the Name of Potion
* @param type (char)<br/> [ <br/> 0 & 3 ~ 255 : potion <br/>1 : lingering_potion <br/>2 : splash_potion<br/>]
* @return Language Key
*/
public static String getPotionNameKey(String name, char type) {
return "item.minecraft." +
(type == 1 ? "lingering_potion" :
(type == 2 ? "splash_potion" : "potion")
)
+ ".effect." + name;
}
public static String getTippedArrowNameKey(String Name) {
return "item.minecraft.tipped_arrow.effect." + Name;
}
public static void register(IEventBus eventBus) {
POTIONS.register(eventBus);
}
}

View File

@ -0,0 +1,38 @@
package com.r3944realms.leashedplayer.content.effects.type;
import com.r3944realms.leashedplayer.content.effects.ModEffectRegister;
import com.r3944realms.leashedplayer.content.entities.LeashRopeArrow;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.effect.MobEffect;
import net.minecraft.world.effect.MobEffectCategory;
import net.minecraft.world.effect.MobEffectInstance;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.Leashable;
import net.minecraft.world.entity.LivingEntity;
import org.jetbrains.annotations.NotNull;
public class NoLeashEffect extends MobEffect {
public NoLeashEffect(MobEffectCategory pCategory, int pColor) {
super(pCategory, pColor);
}
@Override
public boolean applyEffectTick(@NotNull ServerLevel level, @NotNull LivingEntity pLivingEntity, int pAmplifier) {
MobEffectInstance effect = pLivingEntity.getEffect(ModEffectRegister.NO_LEASH_EFFECT);
if(effect != null && effect.getDuration() != 0) {
if (pLivingEntity instanceof Leashable leashable) {
if (leashable.getLeashHolder() instanceof LeashRopeArrow arrow) {
arrow.dropLeashHandler();
Leashable.dropLeash((Entity & Leashable) leashable,true, false);
}
Leashable.dropLeash((Entity & Leashable) leashable,true, true);
}
}
return true;
}
@Override
public boolean shouldApplyEffectTickThisTick(int pDuration, int pAmplifier) {
return true;
}
}

View File

@ -0,0 +1,11 @@
package com.r3944realms.leashedplayer.content.effects.type;
import net.minecraft.world.effect.MobEffect;
import net.minecraft.world.effect.MobEffectCategory;
public class StunnedEffect extends MobEffect {
public StunnedEffect() {
super(MobEffectCategory.HARMFUL, 0XFFFBC5);
}
}

View File

@ -0,0 +1,457 @@
package com.r3944realms.leashedplayer.content.entities;
import com.r3944realms.leashedplayer.config.LeashPlayerCommonConfig;
import com.r3944realms.leashedplayer.content.effects.ModEffectRegister;
import com.r3944realms.leashedplayer.content.gamerules.GameruleRegistry;
import com.r3944realms.leashedplayer.content.gamerules.Server.KeepLeashNotDropTime;
import com.r3944realms.leashedplayer.content.items.ModItemRegister;
import com.r3944realms.leashedplayer.modInterface.ILivingEntityExtension;
import com.r3944realms.leashedplayer.modInterface.PlayerLeashable;
import net.minecraft.core.component.DataComponents;
import net.minecraft.core.particles.ColorParticleOption;
import net.minecraft.core.particles.ParticleTypes;
import net.minecraft.network.chat.Component;
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.server.level.ServerPlayer;
import net.minecraft.sounds.SoundEvents;
import net.minecraft.tags.BlockTags;
import net.minecraft.world.effect.MobEffectInstance;
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.decoration.LeashFenceKnotEntity;
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.item.alchemy.PotionContents;
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 {
public static final String PUSH_SHIFT_TO_PICKUP_QUICKLY = "leashedplayer.leash_rope_arrow.try_to_pickup.push_shift_tip";
private static final int EXPOSED_POTION_DECAY_TIME = 600;
private static final int NO_EFFECT_COLOR = -1;
private static final EntityDataAccessor<Integer> ID_EFFECT_COLOR = SynchedEntityData.defineId(LeashRopeArrow.class, EntityDataSerializers.INT);
private static final byte EVENT_POTION_PUFF = 0;
private static final int maxLifeTime = LeashPlayerCommonConfig.TheLeashArrowMaxLifeTime.get();
protected LeashRopeArrow(EntityType<? extends AbstractArrow> entityType,Level pLevel) {
super(entityType, pLevel);
}
private PotionContents getPotionContents() {
ItemStack pickupItemStackOrigin = this.getPickupItemStackOrigin();
pickupItemStackOrigin.setCount(1);
return pickupItemStackOrigin.getOrDefault(DataComponents.POTION_CONTENTS, PotionContents.EMPTY);
}
public void addEffect(MobEffectInstance pEffectInstance) {
this.setPotionContents(this.getPotionContents().withEffectAdded(pEffectInstance));
}
private void setPotionContents(PotionContents pPotionContents) {
this.getPickupItemStackOrigin().set(DataComponents.POTION_CONTENTS, pPotionContents);
this.updateColor();
}
private void updateColor() {
PotionContents potioncontents = this.getPotionContents();
this.entityData.set(ID_EFFECT_COLOR, potioncontents.equals(PotionContents.EMPTY) ? -1 : potioncontents.getColor());
}
@Override
protected void defineSynchedData(SynchedEntityData.@NotNull Builder pBuilder) {
super.defineSynchedData(pBuilder);
pBuilder.define(ID_EFFECT_COLOR, -1);
}
@Override
protected void setPickupItemStack(@NotNull ItemStack pPickupItemStack) {
super.setPickupItemStack(pPickupItemStack);
this.updateColor();
}
public LeashRopeArrow(EntityType<? extends AbstractArrow> entityType, double pX, double pY, double pZ, Level pLevel, ItemStack pPickupItemStack, @Nullable ItemStack pFiredFromWeapon, @Nullable ServerPlayer serverPlayer) {
super(entityType, pX, pY, pZ, pLevel, pPickupItemStack, pFiredFromWeapon);
this.updateColor();
if(serverPlayer != null && !level().isClientSide) {
Entity leashDataEntity = PlayerLeashable.getLeashDataEntity(serverPlayer, (ServerLevel) level());
if(leashDataEntity instanceof LeashRopeArrow leashRopeArrow) {
leashRopeArrow.setOwner(null);//将先前的箭矢置空
}
((PlayerLeashable)serverPlayer).setLeashedTo(this, true);
}
}
public LeashRopeArrow(EntityType<? extends AbstractArrow> entityType, LivingEntity pOwner, Level pLevel, ItemStack pPickupItemStack, @Nullable ItemStack pFiredFromWeapon) {
super(entityType, pOwner, pLevel, pPickupItemStack, pFiredFromWeapon);
this.updateColor();
if (pOwner instanceof PlayerLeashable lPlayer && !level().isClientSide) {
Entity leashDataEntity = PlayerLeashable.getLeashDataEntity((ServerPlayer) lPlayer, (ServerLevel) level());
if(leashDataEntity instanceof LeashRopeArrow leashRopeArrow) {
leashRopeArrow.setOwner(null);
}
lPlayer.setLeashedTo(this, true);
}
}
@Override
protected @NotNull ItemStack getDefaultPickupItem() {
return new ItemStack(ModItemRegister.LEASH_ROPE_ARROW.get());
}
@Override
public void setOwner(@Nullable Entity pEntity) {
boolean isNull = pEntity == null;
this.ownerUUID = isNull ? null : pEntity.getUUID();
this.cachedOwner = isNull ? null : pEntity;
this.pickup = this.pickup == Pickup.CREATIVE_ONLY ? this.pickup : Pickup.DISALLOWED;
}
@Override
protected boolean tryPickup(@NotNull Player pPlayer) {
//时间1.40 禁止
//时间2.240
// 如果(非仅创造拾取)
// 如果 (按Shift )
// 如果(拥有者) -> 拾取到完整箭取消绑定(super给父类处理)
// 否则:时间仍为原需时间 ->不能获取完整的箭重绑定当前拥有者的Holder是否为本箭才重绑定
// 否则: 禁止
// 否则:
// 如果 (按Shift )
// 如果(拥有者) -> 且拾取到完整箭取消绑定
// 否则:时间仍为原需时间 ->不能获取完整的箭重绑定
// 否则: 禁止
//时间3
// 如果(拥有者) -> 拾取到完整箭取消绑定
// 否则:不能获取完整的箭重绑定
if(life <= 40 ) {
return false;
} else {
PlayerLeashable playerLeashable = (PlayerLeashable) pPlayer;
if(this.getOwner() != null) {//未有Owner始终可检
if(life <= 240) {
if(pPlayer.isShiftKeyDown()) {
Entity leashDataEntity = this.getOwner() instanceof PlayerLeashable ? PlayerLeashable.getLeashDataEntity((ServerPlayer) this.getOwner(), (ServerLevel) level()) : this.getOwner();
if(this.ownedBy(pPlayer)) {
this.pickup = Pickup.ALLOWED;
if(this.equals(leashDataEntity)) {
pPlayer.playSound(SoundEvents.LEASH_KNOT_BREAK, 1, 1);
Leashable.dropLeash((Entity & Leashable)playerLeashable, true, false);
}
} else {
if(life >= 120) {
Entity owner = getOwner();
if( owner != null ) {
// if(this.equals(leashDataEntity)) {
if(owner instanceof PlayerLeashable player) {
player.setLeashedTo(pPlayer, true);
pPlayer.playSound(SoundEvents.LEASH_KNOT_PLACE, 1, 1);
} else if(owner instanceof Leashable leashable) {
leashable.setLeashedTo(pPlayer, true);
pPlayer.playSound(SoundEvents.LEASH_KNOT_PLACE, 1, 1);
}
ItemEntity itemEntity = new ItemEntity(level(), getX(), getY(), getZ(), getOrginalItemStack());
level().addFreshEntity(itemEntity);
discard();
// }
}
} else return false;
}
} else {
((ServerPlayer)pPlayer).sendSystemMessage(Component.translatable(PUSH_SHIFT_TO_PICKUP_QUICKLY), true);
return false;
}
} else {
Entity leashDataEntity = this.getOwner() instanceof PlayerLeashable ? PlayerLeashable.getLeashDataEntity((ServerPlayer) this.getOwner(), (ServerLevel) level()) : this.getOwner();
if(this.ownedBy(pPlayer)) {
this.pickup = Pickup.ALLOWED;
if(this.equals(leashDataEntity)) {
pPlayer.playSound(SoundEvents.LEASH_KNOT_BREAK, 1, 1);
Leashable.dropLeash((Entity & Leashable) playerLeashable,true, false);
}
} else {
Entity owner = getOwner();
if(owner instanceof PlayerLeashable player) {
player.setLeashedTo(pPlayer, true);
pPlayer.playSound(SoundEvents.LEASH_KNOT_PLACE, 1, 1);
} else if(owner instanceof Leashable leashable) {
leashable.setLeashedTo(pPlayer, true);
pPlayer.playSound(SoundEvents.LEASH_KNOT_PLACE, 1, 1);
}
ItemEntity itemEntity = new ItemEntity(level(), getX(), getY(), getZ(), getOrginalItemStack());
level().addFreshEntity(itemEntity);
discard();
}
}
} else if (this.pickup != Pickup.CREATIVE_ONLY) this.pickup = Pickup.ALLOWED;
else return pPlayer.hasInfiniteMaterials() && this.pickup == Pickup.CREATIVE_ONLY;
}
return super.tryPickup(pPlayer);
}
protected void hitOnEntityHandler(Entity pEntity) {
if(pEntity instanceof LivingEntity pLiving) {
super.doPostHurtEffects(pLiving);
Entity entity = this.getEffectSource();
PotionContents potioncontents = this.getPotionContents();
if (potioncontents.potion().isPresent()) {
for (MobEffectInstance mobeffectinstance : potioncontents.potion().get().value().getEffects()) {
pLiving.addEffect(
new MobEffectInstance(
mobeffectinstance.getEffect(),
Math.max(mobeffectinstance.mapDuration(p_268168_ -> p_268168_ / 8), 1),
mobeffectinstance.getAmplifier(),
mobeffectinstance.isAmbient(),
mobeffectinstance.isVisible()
),
entity
);
}
}
for (MobEffectInstance effectInstance : potioncontents.customEffects()) {
pLiving.addEffect(effectInstance, entity);
}
}
}
protected ItemStack getOrginalItemStack() {
return Items.ARROW.getDefaultInstance();
}
protected ItemStack getSelfItemStack() {
return ModItemRegister.LEASH_ROPE_ARROW.get().getDefaultInstance();
}
@Override
protected void tickDespawn() {
this.life++;
if (this.life >= maxLifeTime) {
ItemEntity leash_rope_arrow = new ItemEntity(this.level(), this.position().x, this.position().y, this.position().z, getSelfItemStack());
this.level().addFreshEntity(leash_rope_arrow);
this.discard();
}
}
@Override
public void tick() {
super.tick();
if (this.level().isClientSide) {
if (this.isInGround()) {
if (this.inGroundTime % 5 == 0) {
this.makeParticle(1);
}
} else {
this.makeParticle(2);
}
} else if (this.isInGround() && this.inGroundTime != 0 && !this.getPotionContents().equals(PotionContents.EMPTY) && this.inGroundTime >= 600) {
this.level().broadcastEntityEvent(this, (byte)0);
this.setPickupItemStack(new ItemStack(getSelfItemStack().getItem()));
}
}
private void makeParticle(int pParticleAmount) {
int i = this.getColor();
if (i != -1 && pParticleAmount > 0) {
for (int j = 0; j < pParticleAmount; j++) {
this.level()
.addParticle(
ColorParticleOption.create(ParticleTypes.ENTITY_EFFECT, i),
this.getRandomX(0.5),
this.getRandomY(),
this.getRandomZ(0.5),
0.0,
0.0,
0.0
);
}
}
}
public int getColor() {
return this.entityData.get(ID_EFFECT_COLOR);
}
@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) {
Leashable.dropLeash((Entity & Leashable) pL, true, !(leashDataEntity instanceof LeashRopeArrow));
if(leashDataEntity instanceof LeashRopeArrow leashRopeArrow) {
leashRopeArrow.dropLeashHandler();
}
}
Entity leashKnotFence = PlayerLeashable.createLeashKnotFence((ServerLevel) this.level(), pResult.getBlockPos());
ILivingEntityExtension pLL = (ILivingEntityExtension) pL;
pLL.setKeepLeashTick(GameruleRegistry.getGameruleIntValue(level(), KeepLeashNotDropTime.ID));
pL.setLeashedTo(leashKnotFence, true);
leashKnotFence.playSound(SoundEvents.LEASH_KNOT_PLACE, 1, 1);
ItemEntity arrow = new ItemEntity(this.level(), this.position().x, this.position().y, this.position().z, getOrginalItemStack());
this.level().addFreshEntity(arrow);
discard();
}
} else if(getOwner() instanceof Leashable L) {
if (this.level().getBlockState(pResult.getBlockPos()).is(BlockTags.FENCES)) {
Entity leashDataEntity = this.getOwner();
if(leashDataEntity != null) {
leashDataEntity.playSound(SoundEvents.LEASH_KNOT_BREAK, 1, 1);
Leashable.dropLeash((Entity & Leashable) L,true, false);
if(leashDataEntity instanceof LeashRopeArrow leashRopeArrow) {
leashRopeArrow.setOwner(null);
}
}
Entity leashKnotFence = LeashFenceKnotEntity.getOrCreateKnot(this.level(), pResult.getBlockPos());
L.setLeashedTo(leashKnotFence, true);
leashKnotFence.playSound(SoundEvents.LEASH_KNOT_PLACE, 1, 1);
ItemEntity arrow = new ItemEntity(this.level(), this.position().x, this.position().y, this.position().z, getOrginalItemStack());
this.level().addFreshEntity(arrow);
discard();
}
}
}
super.onHitBlock(pResult);
}
@Override
protected void onHitEntity(@NotNull EntityHitResult pResult) {
if (!level().isClientSide()) {
Entity entity = pResult.getEntity();
hitOnEntityHandler(entity);
if (this.getOwner() instanceof LivingEntity livingEntity ) {
MobEffectInstance effect = livingEntity.getEffect(ModEffectRegister.NO_LEASH_EFFECT);
if (effect != null && effect.getDuration() != 0) {
this.dropLeashHandler();
}
}
if(entity instanceof LivingEntity livingEntity) {
if(livingEntity.equals(this.getOwner())) return;
if(this.getOwner() == null && livingEntity instanceof PlayerLeashable pL) { //发射器发出或命令生成
setOwner(livingEntity);
Entity leashDataEntity = PlayerLeashable.getLeashDataEntity((ServerPlayer) getOwner(), (ServerLevel) level());
if(leashDataEntity != null) {
Leashable.dropLeash((Entity & Leashable) pL,true, !(leashDataEntity instanceof LeashRopeArrow));
if(leashDataEntity instanceof LeashRopeArrow leashRopeArrow) {
leashRopeArrow.dropLeashHandler();
}
}
ILivingEntityExtension pLL = (ILivingEntityExtension) pL;
pLL.setKeepLeashTick(GameruleRegistry.getGameruleIntValue(level(), KeepLeashNotDropTime.ID));
livingEntity.playSound(SoundEvents.LEASH_KNOT_PLACE, 1, 1);
pL.setLeashedTo(this, true);
} else if (this.getOwner() instanceof PlayerLeashable pL) {
Entity leashDataEntity = PlayerLeashable.getLeashDataEntity((ServerPlayer) getOwner(), (ServerLevel) level());
if(leashDataEntity != null) {
Leashable.dropLeash((Entity & Leashable) pL, true, !(leashDataEntity instanceof LeashRopeArrow));
if (leashDataEntity instanceof LeashRopeArrow leashRopeArrow) {
leashRopeArrow.dropLeashHandler();
}
}
ItemEntity arrow = new ItemEntity(this.level(), this.position().x, this.position().y, this.position().z, getOrginalItemStack());
ILivingEntityExtension pLL = (ILivingEntityExtension) pL;
pLL.setKeepLeashTick(GameruleRegistry.getGameruleIntValue(level(), KeepLeashNotDropTime.ID));
livingEntity.playSound(SoundEvents.LEASH_KNOT_PLACE, 1, 1);
pL.setLeashedTo(pResult.getEntity(), true);
this.level().addFreshEntity(arrow);
discard();
} else {
if (entity instanceof Leashable leashable) {
if (getOwner() == null) {
Entity leashDataEntity = leashable.getLeashHolder();
if (leashDataEntity != null) {
Leashable.dropLeash((Entity & Leashable) leashable,true, !(leashDataEntity instanceof LeashRopeArrow));
if (leashDataEntity instanceof LeashRopeArrow leashRopeArrow) {
leashRopeArrow.dropLeashHandler();
}
}
livingEntity.playSound(SoundEvents.LEASH_KNOT_PLACE, 1, 1);
leashable.setLeashedTo(this, true);
this.setOwner(entity);
return;
}
}
if (entity instanceof LivingEntity living) {
if (this.getOwner() != null && this.getOwner()instanceof Leashable leashable) {
livingEntity.playSound(SoundEvents.LEASH_KNOT_PLACE, 1, 1);
leashable.setLeashedTo(living, true);
ItemEntity arrow = new ItemEntity(this.level(), this.position().x, this.position().y, this.position().z, getOrginalItemStack());
this.level().addFreshEntity(arrow);
discard();
return;
}
}
ItemEntity lead = new ItemEntity(this.level(), this.position().x, this.position().y, this.position().z, Items.LEAD.getDefaultInstance());
this.level().addFreshEntity(lead);
}
}
else if (entity instanceof LeashFenceKnotEntity leashKnotFence) {
if (getOwner() instanceof PlayerLeashable pL) {
Entity leashDataEntity = PlayerLeashable.getLeashDataEntity((ServerPlayer) getOwner(), (ServerLevel) level());
if (leashDataEntity != null) {
Leashable.dropLeash((Entity & Leashable) pL,true, true);
if (leashDataEntity instanceof LeashRopeArrow leashRopeArrow) {
leashRopeArrow.dropLeashHandler();
}
}
ItemEntity arrow = new ItemEntity(this.level(), this.position().x, this.position().y, this.position().z, getOrginalItemStack());
ILivingEntityExtension pLL = (ILivingEntityExtension) pL;
pLL.setKeepLeashTick(GameruleRegistry.getGameruleIntValue(level(), KeepLeashNotDropTime.ID));
leashKnotFence.playSound(SoundEvents.LEASH_KNOT_PLACE, 1, 1);
pL.setLeashedTo(leashKnotFence, true);
this.level().addFreshEntity(arrow);
discard();
}
} else {
ItemEntity lead = new ItemEntity(this.level(), this.position().x, this.position().y, this.position().z, Items.LEAD.getDefaultInstance());
this.level().addFreshEntity(lead);
}
}
}
/**
* Handles an entity event received from a {@link net.minecraft.network.protocol.game.ClientboundEntityEventPacket}.
*/
@Override
public void handleEntityEvent(byte pId) {
if (pId == 0) {
int i = this.getColor();
if (i != -1) {
float f = (float)(i >> 16 & 0xFF) / 255.0F;
float f1 = (float)(i >> 8 & 0xFF) / 255.0F;
float f2 = (float)(i & 0xFF) / 255.0F;
for (int j = 0; j < 20; j++) {
this.level()
.addParticle(
ColorParticleOption.create(ParticleTypes.ENTITY_EFFECT, f, f1, f2),
this.getRandomX(0.5),
this.getRandomY(),
this.getRandomZ(0.5),
0.0,
0.0,
0.0
);
}
}
} else {
super.handleEntityEvent(pId);
}
}
public void dropLeashHandler() {
this.playSound(SoundEvents.LEASH_KNOT_PLACE, 1, 1);
this.setOwner(null);
}
}

View File

@ -0,0 +1,40 @@
package com.r3944realms.leashedplayer.content.entities;
import com.r3944realms.leashedplayer.LeashedPlayer;
import net.minecraft.core.registries.Registries;
import net.minecraft.resources.ResourceKey;
import net.minecraft.resources.ResourceLocation;
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(ResourceKey.create(Registries.ENTITY_TYPE, ResourceLocation.fromNamespaceAndPath(LeashedPlayer.MOD_ID, "leash_rope_arrow")))
);
public static final DeferredHolder<EntityType<?>, EntityType<SpectralLeashRopeArrow>> SPECTRAL_LEASH_ROPE_ARROW = ENTITY_TYPE.register(
"spectral_leash_rope_arrow",
() -> EntityType.Builder.<SpectralLeashRopeArrow>of(SpectralLeashRopeArrow::new, MobCategory.MISC)
.sized(0.5F, 0.5F)
.eyeHeight(0.13F)
.clientTrackingRange(4)
.updateInterval(20)
.build(ResourceKey.create(Registries.ENTITY_TYPE, ResourceLocation.fromNamespaceAndPath(LeashedPlayer.MOD_ID, "spectral_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,81 @@
package com.r3944realms.leashedplayer.content.entities;
import com.r3944realms.leashedplayer.content.items.ModItemRegister;
import net.minecraft.core.particles.ParticleTypes;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.effect.MobEffectInstance;
import net.minecraft.world.effect.MobEffects;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.EntityType;
import net.minecraft.world.entity.LivingEntity;
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 org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public class SpectralLeashRopeArrow extends LeashRopeArrow {
private int duration = 200;
public SpectralLeashRopeArrow(EntityType<? extends AbstractArrow> entityType, double pX, double pY, double pZ, Level pLevel, ItemStack pPickupItemStack, @Nullable ItemStack pFiredFromWeapon, @Nullable ServerPlayer serverPlayer) {
super(entityType, pX, pY, pZ, pLevel, pPickupItemStack, pFiredFromWeapon, serverPlayer);
}
protected SpectralLeashRopeArrow(EntityType<? extends AbstractArrow> entityType, Level pLevel) {
super(entityType, pLevel);
}
public SpectralLeashRopeArrow(EntityType<? extends AbstractArrow> entityType, LivingEntity pOwner, Level pLevel, ItemStack pPickupItemStack, @Nullable ItemStack pFiredFromWeapon) {
super(entityType, pOwner, pLevel, pPickupItemStack, pFiredFromWeapon);
}
@Override
public void tick() {
super.tick();
if (this.level().isClientSide && !this.isInGround()) {
this.level().addParticle(ParticleTypes.INSTANT_EFFECT, this.getX(), this.getY(), this.getZ(), 0.0, 0.0, 0.0);
}
}
@Override
protected void doPostHurtEffects(@NotNull LivingEntity pLiving) {
super.doPostHurtEffects(pLiving);
MobEffectInstance mobeffectinstance = new MobEffectInstance(MobEffects.GLOWING, this.duration, 0);
pLiving.addEffect(mobeffectinstance, this.getEffectSource());
}
/**
* (abstract) Protected helper method to read subclass entity data from NBT.
*/
@Override
public void readAdditionalSaveData(@NotNull CompoundTag pCompound) {
super.readAdditionalSaveData(pCompound);
if (pCompound.contains("Duration")) {
this.duration = pCompound.getInt("Duration").orElse(200);
}
}
@Override
public void addAdditionalSaveData(@NotNull CompoundTag pCompound) {
super.addAdditionalSaveData(pCompound);
pCompound.putInt("Duration", this.duration);
}
@Override
protected ItemStack getOrginalItemStack() {
return Items.SPECTRAL_ARROW.getDefaultInstance();
}
@Override
protected ItemStack getSelfItemStack() {
return ModItemRegister.SPECTRAL_LEASH_ROPE_ARROW.get().getDefaultInstance();
}
@Override
protected void hitOnEntityHandler(Entity pEntity) {
if(pEntity instanceof LivingEntity livingEntity) {
livingEntity.addEffect(new MobEffectInstance(MobEffects.GLOWING, this.duration, 0));
} else {
super.hitOnEntityHandler(pEntity);
}
}
}

View File

@ -0,0 +1,29 @@
package com.r3944realms.leashedplayer.content.events;
import com.mojang.serialization.MapCodec;
import com.r3944realms.leashedplayer.client.renderer.item.properties.conditionalRange.ConditionalRangeItemModelProperty;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.util.ExtraCodecs;
import net.neoforged.bus.api.Event;
import net.neoforged.fml.event.IModBusEvent;
import org.jetbrains.annotations.ApiStatus;
/**
* Event fired when special model renderers are registered.
* <p>
* This event is fired during the model registration process for conditional range select item model properties.
* It is used to register property codecs which can be used to create custom conditional range select item model properties.
* <p>
* This event is fired on the mod event bus.
*/
public class RegisterConditionalRangeItemPropertyEvent extends Event implements IModBusEvent {
private final ExtraCodecs.LateBoundIdMapper<ResourceLocation, MapCodec<? extends ConditionalRangeItemModelProperty>> idMapper;
@ApiStatus.Internal
public RegisterConditionalRangeItemPropertyEvent(ExtraCodecs.LateBoundIdMapper<ResourceLocation, MapCodec<? extends ConditionalRangeItemModelProperty>> idMapper) {
this.idMapper = idMapper;
}
public void register(ResourceLocation location, MapCodec<? extends ConditionalRangeItemModelProperty> source) {
this.idMapper.put(location, source);
}
}

View File

@ -0,0 +1,107 @@
package com.r3944realms.leashedplayer.content.gamerules;
import com.mojang.brigadier.arguments.FloatArgumentType;
import com.mojang.brigadier.context.CommandContext;
import net.minecraft.commands.CommandSourceStack;
import net.minecraft.server.MinecraftServer;
import net.minecraft.world.flag.FeatureFlagSet;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.HashMap;
import java.util.function.BiConsumer;
public class GameRules {
public static final String GAMERULE_PREFIX = "LP.";
public static final GameruleRegistry GAMERULE_REGISTRY = GameruleRegistry.INSTANCE;
public static final HashMap<String, Boolean> gamerulesBooleanValuesClient = new HashMap<>();
public static final HashMap<String, Integer> gameruleIntegerValuesClient = new HashMap<>();
public static final HashMap<String, Float> gameruleFloatValuesClient = new HashMap<>();
public static final String RULE_KEY_PERFix_ = "gamerule." + GAMERULE_PREFIX;
public static String getDescriptionKey(Class<?> gameRuleClass) {
return RULE_KEY_PERFix_ + gameRuleClass.getSimpleName() + ".description";
}
public static String getDescriptionKey(String gameRuleName) {
return RULE_KEY_PERFix_ + gameRuleName + ".description";
}
public static String getNameKey(Class<?> gameRuleClass) {
return RULE_KEY_PERFix_ + gameRuleClass.getSimpleName();
}
//此次定义了一个浮点数类型的游戏规则
public static class FloatValue extends net.minecraft.world.level.GameRules.Value<FloatValue> {
private float value;
public static net.minecraft.world.level.GameRules.Type<FloatValue> create(
float pDefaultValue, BiConsumer<MinecraftServer, FloatValue> pChangeListener
) {
return new net.minecraft.world.level.GameRules.Type<>
(FloatArgumentType::floatArg,
pType -> new FloatValue(pType, pDefaultValue),
pChangeListener,
net.minecraft.world.level.GameRules.GameRuleTypeVisitor::visit,
FloatValue.class,
FeatureFlagSet.of()
);
}
public static net.minecraft.world.level.GameRules.Type<FloatValue> create(
float pDefaultValue, float pMin, float pMax , BiConsumer<MinecraftServer, FloatValue> pChangeListener
) {
return new net.minecraft.world.level.GameRules.Type<>(
() -> FloatArgumentType.floatArg(pMin, pMax),
pType -> new GameRules.FloatValue(pType, pDefaultValue),//
pChangeListener,
net.minecraft.world.level.GameRules.GameRuleTypeVisitor::visit,
FloatValue.class,
FeatureFlagSet.of()
);
}
public FloatValue(net.minecraft.world.level.GameRules.Type<GameRules.FloatValue> pType, float value) {
super(pType);
this.value = value;
}
@Override
protected void updateFromArgument(@NotNull CommandContext<CommandSourceStack> pContext, @NotNull String pParamName) {
this.value = FloatArgumentType.getFloat(pContext, pParamName);
}
public float get() {
return this.value;
}
public void set(float pValue, @Nullable MinecraftServer pServer) {
this.value = pValue;
this.onChanged(pServer);
}
@Override
protected void deserialize(@NotNull String pValue) {
this.value = Float.parseFloat(pValue);
}
@Override
public @NotNull String serialize() {
return Float.toString(this.value);
}
@Override
public int getCommandResult() {
return 1;
}
@Override
protected @NotNull FloatValue getSelf() {
return this;
}
@Override
protected @NotNull FloatValue copy() {
return new FloatValue(this.type, this.value);
}
@Override
public void setFrom(FloatValue pValue, @Nullable MinecraftServer pServer) {
this.value = pValue.value;
this.onChanged(pServer);
}
}
}

View File

@ -0,0 +1,71 @@
package com.r3944realms.leashedplayer.content.gamerules;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.flag.FeatureFlagSet;
import net.minecraft.world.level.Level;
import java.util.HashMap;
import java.util.Map;
import java.util.function.BiConsumer;
public enum GameruleRegistry {
INSTANCE;
public static final Map<String, net.minecraft.world.level.GameRules.Key<?>> gamerules = new HashMap<>();;
public static final Map<String, RuleDataType> gameruleDataTypes = new HashMap<>();
public enum RuleDataType {
BOOLEAN,
INTEGER,
}
@SuppressWarnings("unchecked")
public static boolean getGameruleBoolValue(Level level, String gameruleName) {
if (level.isClientSide && GameRules.gamerulesBooleanValuesClient.containsKey(gameruleName)) {
return GameRules.gamerulesBooleanValuesClient.get(gameruleName);
}
if (gameruleDataTypes.get(gameruleName) != RuleDataType.BOOLEAN) {
return false;
}
return ((ServerLevel)level).getGameRules().getBoolean((net.minecraft.world.level.GameRules.Key<net.minecraft.world.level.GameRules.BooleanValue>) gamerules.get(gameruleName));
}
@SuppressWarnings("unchecked")
public static Integer getGameruleIntValue(Level level, String gameruleName) {
if (level.isClientSide && GameRules.gameruleIntegerValuesClient.containsKey(gameruleName)) {
return GameRules.gameruleIntegerValuesClient.get(gameruleName);
}
if (gameruleDataTypes.get(gameruleName) != RuleDataType.INTEGER) {
return 0;
}
return ((ServerLevel)level).getGameRules().getInt((net.minecraft.world.level.GameRules.Key<net.minecraft.world.level.GameRules.IntegerValue>)gamerules.get(gameruleName));
}
public void registerGamerule(String gameruleName, net.minecraft.world.level.GameRules.Category category, boolean pDefault) {
registerGamerule(gameruleName, category, pDefault, (s,i)->{});//最后一个仅占位无用
}
public void registerGamerule(String gameruleName, net.minecraft.world.level.GameRules.Category category, boolean pDefault, BiConsumer<MinecraftServer, net.minecraft.world.level.GameRules.BooleanValue> pChangeListener) {
gamerules.put(gameruleName, net.minecraft.world.level.GameRules.register(gameruleName, category, net.minecraft.world.level.GameRules.BooleanValue.create(pDefault, pChangeListener)));
gameruleDataTypes.put(gameruleName, RuleDataType.BOOLEAN);
}
public void registerGamerule(String gameruleName, net.minecraft.world.level.GameRules.Category category, int pDefault) {
registerGamerule(gameruleName, category, pDefault, (BiConsumer<MinecraftServer, net.minecraft.world.level.GameRules.IntegerValue>) (s, i)->{});//最后一个仅占位无用
}
public void registerGamerule(String gameruleName, net.minecraft.world.level.GameRules.Category category, int pDefault, BiConsumer<MinecraftServer, net.minecraft.world.level.GameRules.IntegerValue> pChangeListener) {
gamerules.put(gameruleName, net.minecraft.world.level.GameRules.register(gameruleName, category, net.minecraft.world.level.GameRules.IntegerValue.create(pDefault, pChangeListener)));
gameruleDataTypes.put(gameruleName, RuleDataType.INTEGER);
}
public void registerGamerule(String gameruleName, net.minecraft.world.level.GameRules.Category category, int pDefault, int pMin, int pMax, BiConsumer<MinecraftServer, net.minecraft.world.level.GameRules.IntegerValue> pChangeListener) {
gamerules.put(gameruleName, net.minecraft.world.level.GameRules.register(gameruleName, category, net.minecraft.world.level.GameRules.IntegerValue.create(pDefault, pMin, pMax, FeatureFlagSet.of(), pChangeListener)));
gameruleDataTypes.put(gameruleName, RuleDataType.INTEGER);
}
public void registerGamerule(String gameruleName, net.minecraft.world.level.GameRules.Category category, float value) {
registerGamerule(gameruleName, category, value, (s,i)->{});
}
public void registerGamerule(String gameruleName, net.minecraft.world.level.GameRules.Category category, float pDefault, BiConsumer<MinecraftServer, GameRules.FloatValue> pChangeListener) {
gamerules.put(gameruleName, net.minecraft.world.level.GameRules.register(gameruleName, category, GameRules.FloatValue.create(pDefault, pChangeListener)));
gameruleDataTypes.put(gameruleName, RuleDataType.INTEGER);
}
public void registerGamerule(String gameruleName, net.minecraft.world.level.GameRules.Category category, float pDefault, float pMin, float pMax, BiConsumer<MinecraftServer, GameRules.FloatValue> pChangeListener) {
gamerules.put(gameruleName, net.minecraft.world.level.GameRules.register(gameruleName, category, GameRules.FloatValue.create(pDefault, pMin, pMax,pChangeListener)));
gameruleDataTypes.put(gameruleName, RuleDataType.INTEGER);
}
}

View File

@ -0,0 +1,24 @@
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.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 net.minecraft.world.level.GameRules.Category CATEGORY = net.minecraft.world.level.GameRules.Category.PLAYER;
@SubscribeEvent
public static void onCommonSetup(final FMLCommonSetupEvent event) {
GAMERULE_REGISTRY.registerGamerule(ID, CATEGORY, DEFAULT_VALUE);
}
}

View File

@ -0,0 +1,27 @@
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.server.MinecraftServer;
import net.neoforged.bus.api.SubscribeEvent;
import net.neoforged.fml.common.EventBusSubscriber;
import net.neoforged.fml.event.lifecycle.FMLCommonSetupEvent;
import java.util.function.BiConsumer;
import static com.r3944realms.leashedplayer.content.gamerules.GameRules.GAMERULE_REGISTRY;
@EventBusSubscriber(modid = LeashedPlayer.MOD_ID, bus = EventBusSubscriber.Bus.MOD)
public class KeepLeashNotDropTime {
public static final int DEFAULT_VALUE = 240;
public static final String ID = Util.getGameruleName(KeepLeashNotDropTime.class);
public static final String DESCRIPTION_KEY = GameRules.getDescriptionKey(KeepLeashNotDropTime.class);
public static final String NAME_KEY = GameRules.getNameKey(KeepLeashNotDropTime.class);
public static final net.minecraft.world.level.GameRules.Category CATEGORY = net.minecraft.world.level.GameRules.Category.MISC;
@SubscribeEvent
public static void onCommonSetup(final FMLCommonSetupEvent event) {
GAMERULE_REGISTRY.registerGamerule(ID, CATEGORY, DEFAULT_VALUE, 80, 1200, (BiConsumer<MinecraftServer, net.minecraft.world.level.GameRules.IntegerValue>) (i, j)->{});
}
}

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.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;
//It had so Long Name at first.(
@EventBusSubscriber(modid = LeashedPlayer.MOD_ID, bus = EventBusSubscriber.Bus.MOD)
public class TeleportWithLeashedPlayers {
public static final boolean DEFAULT_VALUE = true;
public static final String ID = Util.getGameruleName(TeleportWithLeashedPlayers.class);
public static final String DESCRIPTION_KEY = GameRules.getDescriptionKey(TeleportWithLeashedPlayers.class);
public static final String NAME_KEY = GameRules.getNameKey(TeleportWithLeashedPlayers.class);
public static final net.minecraft.world.level.GameRules.Category CATEGORY = net.minecraft.world.level.GameRules.Category.PLAYER;
@SubscribeEvent
public static void onCommonSetup(final FMLCommonSetupEvent event) {
GAMERULE_REGISTRY.registerGamerule(ID, CATEGORY, DEFAULT_VALUE);
}
}

View File

@ -0,0 +1,85 @@
package com.r3944realms.leashedplayer.content.items;
import com.r3944realms.leashedplayer.LeashedPlayer;
import net.minecraft.core.HolderLookup;
import net.minecraft.core.registries.Registries;
import net.minecraft.network.chat.Component;
import net.minecraft.world.flag.FeatureFlagSet;
import net.minecraft.world.item.CreativeModeTab;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.Items;
import net.minecraft.world.item.alchemy.Potion;
import net.minecraft.world.item.alchemy.PotionContents;
import net.neoforged.bus.api.IEventBus;
import net.neoforged.neoforge.common.CommonHooks;
import net.neoforged.neoforge.registries.DeferredRegister;
import org.jetbrains.annotations.NotNull;
import java.util.Objects;
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()
.title(Component.translatable(getCreativeMod(LEASHED_PLAYER_ITEM)))
.icon(() -> ModItemRegister.LEASH_ROPE_ARROW.get().getDefaultInstance())
.displayItems(((pParameters, pOutput) -> {
pOutput.accept(Items.LEAD);
pOutput.accept(Items.BOW);
pOutput.accept(Items.CROSSBOW);
pOutput.accept(ModItemRegister.LEASH_ROPE_ARROW.get());
pOutput.accept(ModItemRegister.SPECTRAL_LEASH_ROPE_ARROW.get());
pOutput.accept(ModItemRegister.AMETHYST_SHEARS.get());
pOutput.accept(ModItemRegister.NEOFORGE.get());
pParameters.holders()
.lookup(Registries.POTION)
.ifPresent(potionRegistryLookup1 ->
potionRegistryLookup1.listElements()
.filter(potionReference -> Objects.requireNonNull(potionReference.getKey()).location().getNamespace().equals(LeashedPlayer.MOD_ID))
.map(potionReference -> PotionContents.createItemStack(Items.POTION, potionReference))
.forEach(pOutput::accept));
pParameters.holders()
.lookup(Registries.POTION)
.ifPresent(potionRegistryLookup1 ->
potionRegistryLookup1.listElements()
.filter(potionReference -> Objects.requireNonNull(potionReference.getKey()).location().getNamespace().equals(LeashedPlayer.MOD_ID))
.map(potionReference -> PotionContents.createItemStack(Items.SPLASH_POTION, potionReference))
.forEach(pOutput::accept));
pParameters.holders()
.lookup(Registries.POTION)
.ifPresent(potionRegistryLookup1 ->
potionRegistryLookup1.listElements()
.filter(potionReference -> Objects.requireNonNull(potionReference.getKey()).location().getNamespace().equals(LeashedPlayer.MOD_ID))
.map(potionReference -> PotionContents.createItemStack(Items.LINGERING_POTION, potionReference))
.forEach(pOutput::accept));
pParameters.holders()
.lookup(Registries.POTION)
.ifPresent(
pPotions -> generatePotionEffectTypes(
pOutput,
pPotions,
ModItemRegister.TIPPED_LEASH_ROPE_ARROW.get(),
CreativeModeTab.TabVisibility.PARENT_AND_SEARCH_TABS,
pParameters.enabledFeatures()
)
);
})).build());
private static void generatePotionEffectTypes(
CreativeModeTab.Output pOutput, HolderLookup<Potion> pPotions, Item pItem, CreativeModeTab.TabVisibility pTabVisibility, FeatureFlagSet pRequiredFeatures
) {
pPotions.listElements()
.filter(potionReference -> potionReference.value().isEnabled(pRequiredFeatures))
.map(potionReference -> PotionContents.createItemStack(pItem, potionReference))
.forEach(itemStack -> pOutput.accept(itemStack, pTabVisibility));
}
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,65 @@
package com.r3944realms.leashedplayer.content.items;
import com.r3944realms.leashedplayer.LeashedPlayer;
import com.r3944realms.leashedplayer.content.items.type.*;
import com.r3944realms.leashedplayer.datagen.provider.attributes.ModJukeboxSongs;
import net.minecraft.core.component.DataComponents;
import net.minecraft.core.registries.BuiltInRegistries;
import net.minecraft.resources.ResourceKey;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.JukeboxSong;
import net.minecraft.world.item.Rarity;
import net.minecraft.world.item.ShearsItem;
import net.minecraft.world.item.alchemy.PotionContents;
import net.neoforged.bus.api.IEventBus;
import net.neoforged.neoforge.registries.DeferredHolder;
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<DeferredHolder<Item, Item>> ITEM_SUPPLIER = new ArrayList<>();
public static final DeferredHolder<Item, Item> LEASH_ROPE_ARROW = ModItemRegister.register("leash_rope_arrow",
() -> new LeashRopeArrowItem(new Item.Properties().stacksTo(16)
.setId(ModItemResourceKeys.LEASH_ROPE_ARROW.getResourceKey()))
);
public static final DeferredHolder<Item, Item> SPECTRAL_LEASH_ROPE_ARROW = ModItemRegister.register("spectral_leash_rope_arrow",
() -> new SpectralLeashRopeArrowItem(new Item.Properties().stacksTo(16)
.setId(ModItemResourceKeys.SPECTRAL_LEASH_ROPE_ARROW.getResourceKey())));
public static final DeferredHolder<Item, Item> TIPPED_LEASH_ROPE_ARROW = ModItemRegister.register("tipped_leash_rope_arrow",
() -> new TippedLeashRopeArrowItem(new Item.Properties().stacksTo(16).component(DataComponents.POTION_CONTENTS, PotionContents.EMPTY)
.setId(ModItemResourceKeys.TIPPED_LEASH_ROPE_ARROW.getResourceKey())));
public static final DeferredHolder<Item, Item> AMETHYST_SHEARS = ModItemRegister.register("amethyst_shears",
() -> new LeadBreakerItem(ModToolMaterials.AMETHYST, new Item.Properties().durability(100).component(DataComponents.TOOL, LeadBreakerItem.createToolProperties())
.stacksTo(1)
.setId(ModItemResourceKeys.AMETHYST_SHEARS.getResourceKey())));
public static final DeferredHolder<Item, Item> FABRIC = ModItemRegister.register("fabric",
() -> new TestItem(new Item.Properties().stacksTo(1)
.setId(ModItemResourceKeys.FABRIC.getResourceKey())));
public static final DeferredHolder<Item, Item> NEOFORGE = ModItemRegister.register("neoforge",
() -> new Item(DistProperties(ModJukeboxSongs.FOX_MUSIC)
.setId(ModItemResourceKeys.NEOFORGE.getResourceKey())));
public static Item.Properties DistProperties(ResourceKey<JukeboxSong> song) {
return new Item.Properties().stacksTo(1).rarity(Rarity.RARE).jukeboxPlayable(song);
}
public static DeferredHolder<Item, Item> register(String name, Supplier<Item> supplier) {
return register(name, supplier, true);
}
public static DeferredHolder<Item, Item> register(String name, Supplier<Item> supplier, boolean shouldJoinSupplierLists) {
DeferredHolder<Item, 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

@ -0,0 +1,29 @@
package com.r3944realms.leashedplayer.content.items;
import com.r3944realms.leashedplayer.LeashedPlayer;
import net.minecraft.core.registries.Registries;
import net.minecraft.resources.ResourceKey;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.item.Item;
import net.neoforged.neoforge.registries.DeferredHolder;
public enum ModItemResourceKeys {
LEASH_ROPE_ARROW(ModItemRegister.LEASH_ROPE_ARROW),
SPECTRAL_LEASH_ROPE_ARROW(ModItemRegister.SPECTRAL_LEASH_ROPE_ARROW),
TIPPED_LEASH_ROPE_ARROW (ModItemRegister.TIPPED_LEASH_ROPE_ARROW),
AMETHYST_SHEARS(ModItemRegister.AMETHYST_SHEARS),
FABRIC(ModItemRegister.FABRIC),
NEOFORGE(ModItemRegister.NEOFORGE)
;
private final ResourceKey<Item> resourceKey;
ModItemResourceKeys(String name) {
resourceKey = ResourceKey.create(Registries.ITEM, ResourceLocation.fromNamespaceAndPath(LeashedPlayer.MOD_ID, name));
}
ModItemResourceKeys(DeferredHolder<Item, Item> item) {
resourceKey = ResourceKey.create(Registries.ITEM, item.getId());
}
public ResourceKey<Item> getResourceKey() {
return resourceKey;
}
}

View File

@ -0,0 +1,13 @@
package com.r3944realms.leashedplayer.content.items;
import com.r3944realms.leashedplayer.datagen.LanguageAndOtherData.ModItemTags;
import net.minecraft.tags.BlockTags;
import net.minecraft.world.item.ToolMaterial;
public class ModToolMaterials {
public static final ToolMaterial AMETHYST =
new ToolMaterial(BlockTags.INCORRECT_FOR_DIAMOND_TOOL,
100, 8.0F, 2.0F, 15,
ModItemTags.AMETHYST_TOOL_MATERIALS
);
}

View File

@ -0,0 +1,27 @@
package com.r3944realms.leashedplayer.content.items.repcipe;
import com.r3944realms.leashedplayer.LeashedPlayer;
import net.minecraft.core.registries.BuiltInRegistries;
import net.minecraft.world.item.crafting.CustomRecipe;
import net.minecraft.world.item.crafting.RecipeSerializer;
import net.neoforged.bus.api.IEventBus;
import net.neoforged.neoforge.registries.DeferredHolder;
import net.neoforged.neoforge.registries.DeferredRegister;
public class ModRecipeRegister {
public static final DeferredRegister<RecipeSerializer<?>> RECIPE_SERIALIZER =
DeferredRegister.create(BuiltInRegistries.RECIPE_SERIALIZER, LeashedPlayer.MOD_ID);
public static final DeferredHolder<RecipeSerializer<?>, RecipeSerializer<TippedLeashRopeArrowRecipe.TippedLeashRopeArrowARecipe>> TIPPED_LEASH_ROPE_ARROW_A_RECIPE =
RECIPE_SERIALIZER.register("tipped_leash_rope_arrow_a_recipe", () ->
new CustomRecipe.Serializer<>(TippedLeashRopeArrowRecipe.TippedLeashRopeArrowARecipe::new)
);
public static final DeferredHolder<RecipeSerializer<?>, RecipeSerializer<TippedLeashRopeArrowRecipe.TippedLeashRopeArrowBRecipe>> TIPPED_LEASH_ROPE_ARROW_B_RECIPE =
RECIPE_SERIALIZER.register("tipped_leash_rope_arrow_b_recipe", () ->
new CustomRecipe.Serializer<>(TippedLeashRopeArrowRecipe.TippedLeashRopeArrowBRecipe::new)
);
public static void register(IEventBus eventBus) {
RECIPE_SERIALIZER.register(eventBus);
}
}

View File

@ -0,0 +1,106 @@
package com.r3944realms.leashedplayer.content.items.repcipe;
import com.r3944realms.leashedplayer.content.items.ModItemRegister;
import net.minecraft.core.HolderLookup;
import net.minecraft.core.component.DataComponents;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.Items;
import net.minecraft.world.item.crafting.CraftingBookCategory;
import net.minecraft.world.item.crafting.CraftingInput;
import net.minecraft.world.item.crafting.CustomRecipe;
import net.minecraft.world.item.crafting.RecipeSerializer;
import net.minecraft.world.level.Level;
import org.jetbrains.annotations.NotNull;
public abstract class TippedLeashRopeArrowRecipe {
public static class TippedLeashRopeArrowARecipe extends CustomRecipe {
public TippedLeashRopeArrowARecipe(CraftingBookCategory pCategory) {
super(pCategory);
}
public boolean matches(CraftingInput pInput, @NotNull Level pLevel) {
if (pInput.width() == 3 && pInput.height() == 3) {
for (int i = 0; i < pInput.height(); i++) {
for (int j = 0; j < pInput.width(); j++) {
ItemStack itemstack = pInput.getItem(j, i);
if (itemstack.isEmpty()) {
return false;
}
if (j == 1 && i == 1) {
if (!itemstack.is(Items.LINGERING_POTION)) {
return false;
}
} else if (!itemstack.is(ModItemRegister.LEASH_ROPE_ARROW.get())) {
return false;
}
}
}
return true;
} else {
return false;
}
}
public @NotNull ItemStack assemble(CraftingInput pInput, HolderLookup.@NotNull Provider pRegistries) {
ItemStack itemstack = pInput.getItem(1, 1);
if (!itemstack.is(Items.LINGERING_POTION)) {
return ItemStack.EMPTY;
} else {
ItemStack itemstack1 = new ItemStack(ModItemRegister.TIPPED_LEASH_ROPE_ARROW.get(), 8);
itemstack1.set(DataComponents.POTION_CONTENTS, itemstack.get(DataComponents.POTION_CONTENTS));
return itemstack1;
}
}
@Override
public @NotNull RecipeSerializer<? extends CustomRecipe> getSerializer() {
return ModRecipeRegister.TIPPED_LEASH_ROPE_ARROW_A_RECIPE.get();
}
}
public static class TippedLeashRopeArrowBRecipe extends CustomRecipe {
public TippedLeashRopeArrowBRecipe(CraftingBookCategory pCategory) {
super(pCategory);
}
@Override
public boolean matches(CraftingInput pInput, @NotNull Level pLevel) {
int tipped_arrow_count = 0, lead_count = 0;
for (int i = 0; i < pInput.width(); i++) {
for (int j = 0; j < pInput.height(); j++) {
if (pInput.getItem(i, j).is(Items.TIPPED_ARROW))
tipped_arrow_count++;
else if(pInput.getItem(i, j).is(Items.LEAD))
lead_count++;
}
}
return tipped_arrow_count == 1 && lead_count == 1;
}
@Override
public @NotNull ItemStack assemble(@NotNull CraftingInput pInput, HolderLookup.@NotNull Provider pRegistries) {
ItemStack tipped_arrow = null;
NODE:for (int i = 0; i < pInput.width(); i++) {
for (int j = 0; j < pInput.height(); j++) {
if (pInput.getItem(i, j).is(Items.TIPPED_ARROW)) {
tipped_arrow = pInput.getItem(i, j);
break NODE;
}
}
}
if(tipped_arrow != null) {
ItemStack itemstack1 = new ItemStack(ModItemRegister.TIPPED_LEASH_ROPE_ARROW.get(), 1);
itemstack1.set(DataComponents.POTION_CONTENTS, tipped_arrow.get(DataComponents.POTION_CONTENTS));
return itemstack1;
}
return ItemStack.EMPTY;
}
@Override
public @NotNull RecipeSerializer<? extends CustomRecipe> getSerializer() {
return ModRecipeRegister.TIPPED_LEASH_ROPE_ARROW_B_RECIPE.get();
}
}
}

View File

@ -0,0 +1,47 @@
package com.r3944realms.leashedplayer.content.items.type;
import com.r3944realms.leashedplayer.content.items.ModItemRegister;
import net.minecraft.core.component.DataComponents;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.item.BowItem;
import net.minecraft.world.item.CrossbowItem;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.component.ChargedProjectiles;
public interface ILeashRopeArrow {
static boolean isLeashRopeArrow(ItemStack bowStack, LivingEntity entity) {
Item item = bowStack.getItem();
if(item instanceof BowItem) {
if (entity == null) return false;
ItemStack projectile = entity.getProjectile(bowStack);
return projectile.getItem() instanceof ILeashRopeArrow;
} else if (item instanceof CrossbowItem) {
ChargedProjectiles chargedProjectiles = bowStack.get(DataComponents.CHARGED_PROJECTILES);
return chargedProjectiles != null &&
(chargedProjectiles.contains(ModItemRegister.LEASH_ROPE_ARROW.get()) ||
chargedProjectiles.contains(ModItemRegister.SPECTRAL_LEASH_ROPE_ARROW.get()) ||
chargedProjectiles.contains(ModItemRegister.TIPPED_LEASH_ROPE_ARROW.get()));
}
// if (entity instanceof Player player) {
// if (bowStack.getItem() instanceof CrossbowItem) {}
// // 获取将要发射的弹药
// ItemStack projectileStack = player.getProjectile(bowStack);
//
// // 判断该弹药是否为拴绳箭
// return projectileStack.getItem() instanceof ILeashRopeArrow;
// } else if (entity == null) {
// if (bowStack.getItem() instanceof CrossbowItem ) {
// ChargedProjectiles chargedProjectiles = bowStack.get(DataComponents.CHARGED_PROJECTILES);
// if (chargedProjectiles == null || chargedProjectiles.isEmpty()) {
// return false;
// }
// return chargedProjectiles.contains(ModItemRegister.LEASH_ROPE_ARROW.get()) ||
// chargedProjectiles.contains(ModItemRegister.SPECTRAL_LEASH_ROPE_ARROW.get()) ||
// chargedProjectiles.contains(ModItemRegister.TIPPED_LEASH_ROPE_ARROW.get());
// }
// }
return false;
}
}

View File

@ -0,0 +1,128 @@
package com.r3944realms.leashedplayer.content.items.type;
import com.r3944realms.leashedplayer.content.entities.LeashRopeArrow;
import com.r3944realms.leashedplayer.datagen.LanguageAndOtherData.ModItemTags;
import com.r3944realms.leashedplayer.modInterface.PlayerLeashable;
import net.minecraft.core.HolderGetter;
import net.minecraft.core.HolderSet;
import net.minecraft.core.component.DataComponents;
import net.minecraft.core.registries.BuiltInRegistries;
import net.minecraft.network.chat.Component;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.sounds.SoundEvents;
import net.minecraft.tags.BlockTags;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.InteractionResult;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.Leashable;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.ShearsItem;
import net.minecraft.world.item.ToolMaterial;
import net.minecraft.world.item.TooltipFlag;
import net.minecraft.world.item.component.Tool;
import net.minecraft.world.item.component.TooltipDisplay;
import net.minecraft.world.item.enchantment.Enchantable;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.Blocks;
import org.jetbrains.annotations.NotNull;
import java.util.List;
import java.util.function.Consumer;
public class LeadBreakerItem extends ShearsItem {
private final ToolMaterial toolMaterial;
public static final String HOVER_KEY = "leashedplayer.lead_breaker.item.desc",
MESSAGE_USE_SUF = "leashedplayer.lead_breaker.item.use_suf",
MESSAGE_USE_FAI = "leashedplayer.lead_breaker.item.use_fai";
public LeadBreakerItem(ToolMaterial toolMaterial, Properties properties) {
super(properties.durability(toolMaterial.durability())
.component(DataComponents.ENCHANTABLE, new Enchantable(toolMaterial.enchantmentValue()))
.repairable(ModItemTags.AMETHYST_TOOL_MATERIALS)
.enchantable(toolMaterial.enchantmentValue()));
this.toolMaterial = toolMaterial;
}
public static Tool createToolProperties() {
HolderGetter<Block> holdergetter = BuiltInRegistries.acquireBootstrapRegistrationLookup(BuiltInRegistries.BLOCK);
return new Tool(
List.of(
Tool.Rule.minesAndDrops(HolderSet.direct(BuiltInRegistries.BLOCK.wrapAsHolder(Blocks.COBWEB)), 18.0F),
Tool.Rule.overrideSpeed(holdergetter.getOrThrow(BlockTags.LEAVES), 25.0F),
Tool.Rule.overrideSpeed(holdergetter.getOrThrow(BlockTags.WOOL), 8.0F),
Tool.Rule.overrideSpeed(HolderSet.direct(BuiltInRegistries.BLOCK.wrapAsHolder(Blocks.VINE), BuiltInRegistries.BLOCK.wrapAsHolder(Blocks.GLOW_LICHEN)), 4.0F)
),
1.0F,
1,
true
);
}
@Override
public @NotNull InteractionResult interactLivingEntity(@NotNull ItemStack stack, @NotNull Player player, @NotNull LivingEntity entity, @NotNull InteractionHand hand) {
if(!entity.level().isClientSide) {
if (entity instanceof PlayerLeashable playerLeashable) {
if (playerLeashable.isLeashed()){
entity.playSound(SoundEvents.SHEEP_SHEAR, 1.0F, 1.0F);
Entity leashHolder = playerLeashable.getLeashHolder();
Leashable.dropLeash((Entity & Leashable) playerLeashable,true, !(leashHolder instanceof LeashRopeArrow));
if (leashHolder instanceof LeashRopeArrow leashRopeArrow) {
leashRopeArrow.setOwner(null);
}
if (!player.isCreative()) stack.hurtAndBreak(1, player, LivingEntity.getSlotForHand(hand));
if (leashHolder != null) {
((ServerPlayer)player).sendSystemMessage(Component.translatable(MESSAGE_USE_SUF, entity.getDisplayName(), leashHolder.getDisplayName()), true);
}
return InteractionResult.SUCCESS;
} else return InteractionResult.PASS;
} else if (entity instanceof Leashable leashable) {
if (leashable.isLeashed()){
entity.playSound(SoundEvents.SHEEP_SHEAR, 1.0F, 1.0F);
Entity leashHolder = leashable.getLeashHolder();
Leashable.dropLeash((Entity & Leashable) leashable, true, !(leashHolder instanceof LeashRopeArrow));
if (leashable.getLeashHolder() instanceof LeashRopeArrow leashRopeArrow) {
leashRopeArrow.setOwner(null);
}
if (!player.isCreative()) stack.hurtAndBreak(1, player, LivingEntity.getSlotForHand(hand));
if (leashHolder != null) {
((ServerPlayer)player).sendSystemMessage(Component.translatable(MESSAGE_USE_SUF, entity.getDisplayName(), leashHolder.getDisplayName()), true);
}
return InteractionResult.SUCCESS;
} else return InteractionResult.PASS;
}
}
return InteractionResult.PASS;
}
@Override
public @NotNull InteractionResult use(@NotNull Level pLevel, @NotNull Player pPlayer, @NotNull InteractionHand pUsedHand) {
if (!pLevel.isClientSide && pUsedHand == InteractionHand.MAIN_HAND) {
PlayerLeashable playerLeashable = (PlayerLeashable) pPlayer;
if (playerLeashable.isLeashed()) {
Entity leashHolder = playerLeashable.getLeashHolder();
if(!(leashHolder instanceof LeashRopeArrow)) {
Leashable.dropLeash((Entity & Leashable) playerLeashable,true, true);
pPlayer.getItemInHand(pUsedHand).hurtAndBreak(10, pPlayer, LivingEntity.getSlotForHand(pUsedHand));
if (leashHolder != null) {
((ServerPlayer)pPlayer).sendSystemMessage(Component.translatable(MESSAGE_USE_SUF, pPlayer.getDisplayName(), leashHolder.getDisplayName()), true);
}
return InteractionResult.SUCCESS;
}
((ServerPlayer)pPlayer).sendSystemMessage(Component.translatable(MESSAGE_USE_FAI, pPlayer.getDisplayName(), leashHolder.getDisplayName()), true);
}
}
if(pLevel.isClientSide) {
PlayerLeashable playerLeashable = (PlayerLeashable) pPlayer;
if (playerLeashable.getLeashDataFromEntityData() != null) {
pPlayer.playSound(SoundEvents.SHEEP_SHEAR, 1.0F, 1.0F);
}
}
return super.use(pLevel, pPlayer, pUsedHand);
}
@Override
public void appendHoverText(@NotNull ItemStack pItemStack, @NotNull TooltipContext pTooltipComponents, @NotNull TooltipDisplay pTooltipDisplay, Consumer<Component> pConsumer, @NotNull TooltipFlag pTooltipFlag) {
pConsumer.accept(Component.translatable(HOVER_KEY));
}
}

View File

@ -0,0 +1,57 @@
package com.r3944realms.leashedplayer.content.items.type;
import com.r3944realms.leashedplayer.content.entities.LeashRopeArrow;
import com.r3944realms.leashedplayer.content.entities.ModEntityRegister;
import net.minecraft.ChatFormatting;
import net.minecraft.core.Direction;
import net.minecraft.core.Position;
import net.minecraft.network.chat.Component;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.entity.projectile.AbstractArrow;
import net.minecraft.world.entity.projectile.Projectile;
import net.minecraft.world.item.ArrowItem;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.TooltipFlag;
import net.minecraft.world.item.component.TooltipDisplay;
import net.minecraft.world.level.Level;
import org.jetbrains.annotations.NotNull;
import javax.annotation.Nullable;
import java.util.List;
import java.util.function.Consumer;
public class LeashRopeArrowItem extends ArrowItem implements ILeashRopeArrow{
public static final String descKey = "item.leash_rope_arrow.description",
DESC_1 = "item.leash_rope_arrow.desc.1",
DESC_2 = "item.leash_rope_arrow.desc.2",
DESC_3 = "item.leash_rope_arrow.desc.3",
DESC_4 = "item.leash_rope_arrow.desc.4",
DESC_5 = "item.leash_rope_arrow.desc.5";
public static final String
DESC_V_1 = "item.variant.leash_rope_arrow.desc.1",
DESC_V_2 = "item.variant.leash_rope_arrow.desc.2";
public LeashRopeArrowItem(Properties pProperties) {
super(pProperties) ;
}
public @NotNull AbstractArrow createArrow(@NotNull Level pLevel, @NotNull ItemStack pAmmo, @NotNull LivingEntity pShooter, @Nullable ItemStack pWeapon) {
return new LeashRopeArrow(ModEntityRegister.LEASH_ROPE_ARROW.get(),pShooter, pLevel, pAmmo.copyWithCount(1), pWeapon);
}
@Override
public @NotNull Projectile asProjectile(@NotNull Level pLevel, @NotNull Position pPos, @NotNull ItemStack pStack, @NotNull Direction pDirection) {
LeashRopeArrow arrow = new LeashRopeArrow(ModEntityRegister.LEASH_ROPE_ARROW.get(), pPos.x(), pPos.y(), pPos.z(), pLevel, pStack,null, null);
arrow.pickup = AbstractArrow.Pickup.DISALLOWED;
return arrow;
}
@Override
public void appendHoverText(@NotNull ItemStack pItemStack, @NotNull TooltipContext pTooltipComponents, @NotNull TooltipDisplay pTooltipDisplay, Consumer<Component> pConsumer, @NotNull TooltipFlag pTooltipFlag) {
pConsumer.accept(Component.translatable(LeashRopeArrowItem.DESC_1));
pConsumer.accept(Component.translatable(LeashRopeArrowItem.DESC_2));
pConsumer.accept(Component.translatable(LeashRopeArrowItem.DESC_3));
pConsumer.accept(Component.translatable(LeashRopeArrowItem.DESC_4));
pConsumer.accept(Component.translatable(LeashRopeArrowItem.DESC_5));
}
}

View File

@ -0,0 +1,47 @@
package com.r3944realms.leashedplayer.content.items.type;
import com.r3944realms.leashedplayer.content.entities.LeashRopeArrow;
import com.r3944realms.leashedplayer.content.entities.ModEntityRegister;
import com.r3944realms.leashedplayer.content.entities.SpectralLeashRopeArrow;
import net.minecraft.ChatFormatting;
import net.minecraft.core.Direction;
import net.minecraft.core.Position;
import net.minecraft.network.chat.Component;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.entity.projectile.AbstractArrow;
import net.minecraft.world.entity.projectile.Projectile;
import net.minecraft.world.item.ArrowItem;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.TooltipFlag;
import net.minecraft.world.item.component.TooltipDisplay;
import net.minecraft.world.level.Level;
import org.jetbrains.annotations.NotNull;
import javax.annotation.Nullable;
import java.util.List;
import java.util.function.Consumer;
public class SpectralLeashRopeArrowItem extends ArrowItem implements ILeashRopeArrow{
public static final String descKey = "item.spectral_leash_rope_arrow.description",
DESC = "item.spectral_leash_rope_arrow.desc";
public SpectralLeashRopeArrowItem(Properties pProperties) {
super(pProperties);
}
public @NotNull AbstractArrow createArrow(@NotNull Level pLevel, @NotNull ItemStack pAmmo, @NotNull LivingEntity pShooter, @Nullable ItemStack pWeapon) {
return new SpectralLeashRopeArrow(ModEntityRegister.SPECTRAL_LEASH_ROPE_ARROW.get(), pShooter, pLevel, pAmmo.copyWithCount(1), pWeapon);
}
@Override
public @NotNull Projectile asProjectile(@NotNull Level pLevel, @NotNull Position pPos, @NotNull ItemStack pStack, @NotNull Direction pDirection) {
LeashRopeArrow arrow = new SpectralLeashRopeArrow(ModEntityRegister.SPECTRAL_LEASH_ROPE_ARROW.get(), pPos.x(), pPos.y(), pPos.z(), pLevel, this.getDefaultInstance(),null, null);
arrow.pickup = AbstractArrow.Pickup.DISALLOWED;
return arrow;
}
@Override
public void appendHoverText(@NotNull ItemStack pItemStack, @NotNull TooltipContext pTooltipComponents, @NotNull TooltipDisplay pTooltipDisplay, Consumer<Component> pConsumer, @NotNull TooltipFlag pTooltipFlag) {
pConsumer.accept(Component.translatable(LeashRopeArrowItem.DESC_V_1));
pConsumer.accept(Component.translatable(LeashRopeArrowItem.DESC_V_2));
pConsumer.accept(Component.translatable(DESC));
}
}

View File

@ -0,0 +1,47 @@
package com.r3944realms.leashedplayer.content.items.type;
import com.r3944realms.leashedplayer.client.processBar.IProcessBar;
import com.r3944realms.leashedplayer.client.processBar.TestProcessBar;
import com.r3944realms.leashedplayer.client.renderer.gui.AdaptiveGuiRendererHandler;
import com.r3944realms.leashedplayer.client.renderer.gui.IFadingProcessBarRenderer;
import com.r3944realms.leashedplayer.client.renderer.gui.IProcessBarRenderer;
import com.r3944realms.leashedplayer.client.renderer.gui.TestProcessBarRenderer;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.InteractionResult;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.Item;
import net.minecraft.world.level.Level;
import org.jetbrains.annotations.NotNull;
public class TestItem extends Item {
public TestProcessBar testProcessBar = null;
public TestItem(Properties pProperties) {
super(pProperties);
}
@Override
public @NotNull InteractionResult use(@NotNull Level pLevel, @NotNull Player pPlayer, @NotNull InteractionHand pUsedHand) {
if(!pLevel.isClientSide()){
//some lo
}
else {
if (testProcessBar == null) {
testProcessBar = new TestProcessBar();
testProcessBar.setRenderStatus(true);
TestProcessBarRenderer testProcessBarRenderer = new TestProcessBarRenderer(testProcessBar);
AdaptiveGuiRendererHandler.addProcessBar(testProcessBar, testProcessBarRenderer);
} else {
if(testProcessBar.getCurrentProcess() >= testProcessBar.getMaxProcess()) {
testProcessBar.increase();
IProcessBarRenderer<? extends IProcessBar> processBarRenderer = AdaptiveGuiRendererHandler.getProcessBarRenderer(testProcessBar);
((IFadingProcessBarRenderer<?>)processBarRenderer).setFadingOut(true);
testProcessBar = null;
} else {
testProcessBar.increase();
}
}
}
return super.use(pLevel, pPlayer, pUsedHand);
}
}

View File

@ -0,0 +1,49 @@
package com.r3944realms.leashedplayer.content.items.type;
import net.minecraft.core.Holder;
import net.minecraft.core.component.DataComponents;
import net.minecraft.network.chat.Component;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.TooltipFlag;
import net.minecraft.world.item.alchemy.Potion;
import net.minecraft.world.item.alchemy.PotionContents;
import net.minecraft.world.item.alchemy.Potions;
import net.minecraft.world.item.component.TooltipDisplay;
import org.jetbrains.annotations.NotNull;
import java.util.List;
import java.util.Optional;
import java.util.function.Consumer;
public class TippedLeashRopeArrowItem extends LeashRopeArrowItem {
public static final String TIPPED_LEASH_ROPE_ARROW_NAME = "item.tipped_leash_rope_arrow.name";
public static final String DESC = "item.tipped_leash_rope_arrow.desc";
public TippedLeashRopeArrowItem(Properties pProperties) {
super(pProperties);
}
@Override
public @NotNull ItemStack getDefaultInstance() {
ItemStack itemstack = super.getDefaultInstance();
itemstack.set(DataComponents.POTION_CONTENTS, new PotionContents(Potions.POISON));
return itemstack;
}
@Override
public void appendHoverText(@NotNull ItemStack pItemStack, @NotNull TooltipContext pTooltipComponents, @NotNull TooltipDisplay pTooltipDisplay, Consumer<Component> pConsumer, @NotNull TooltipFlag pTooltipFlag) {
super.appendHoverText(pItemStack, pTooltipComponents, pTooltipDisplay, pConsumer, pTooltipFlag);
}
@Override
public @NotNull Component getName(@NotNull ItemStack pStack) {
Optional<Holder<Potion>> pPotion = pStack.getOrDefault(DataComponents.POTION_CONTENTS, PotionContents.EMPTY).potion();
String s = null,s1;
if (pPotion.isPresent()) {
s = pPotion.get().value().name();
}
boolean flag = s == null;
s1 = pPotion.flatMap(Holder::unwrapKey).map(p_331494_ -> p_331494_.location().getPath()).orElse("empty");
String potionTranslateKey = "item.minecraft.potion.effect." + (flag ? s1: s);
return Component.translatable(TIPPED_LEASH_ROPE_ARROW_NAME, Component.translatable(potionTranslateKey));
}
}

View File

@ -0,0 +1,78 @@
package com.r3944realms.leashedplayer.content.misc;
import com.r3944realms.leashedplayer.content.entities.LeashRopeArrow;
import com.r3944realms.leashedplayer.modInterface.PlayerLeashable;
import net.minecraft.core.BlockPos;
import net.minecraft.core.dispenser.BlockSource;
import net.minecraft.core.dispenser.OptionalDispenseItemBehavior;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.sounds.SoundEvents;
import net.minecraft.sounds.SoundSource;
import net.minecraft.tags.BlockTags;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.EntitySelector;
import net.minecraft.world.entity.Leashable;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.block.BeehiveBlock;
import net.minecraft.world.level.block.DispenserBlock;
import net.minecraft.world.level.block.entity.BeehiveBlockEntity;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.gameevent.GameEvent;
import net.minecraft.world.phys.AABB;
import org.jetbrains.annotations.NotNull;
public class LeadBreakItemBehavior extends OptionalDispenseItemBehavior {
@Override
protected @NotNull ItemStack execute(@NotNull BlockSource blockSource, @NotNull ItemStack item) {
ServerLevel serverLevel = blockSource.level();
if(!serverLevel.isClientSide()) {
BlockPos blockPos = blockSource.pos().relative(blockSource.state().getValue(DispenserBlock.FACING));
this.setSuccess(tryShearBeehive(serverLevel, blockPos) || tryShearLivingEntity(serverLevel, blockPos, item));
if (this.isSuccess()) {
item.hurtAndBreak(1, serverLevel, null, p_348118_ -> {
});
}
}
return item;
}
private static boolean tryShearBeehive(ServerLevel level, BlockPos pos) {
BlockState blockstate = level.getBlockState(pos);
if (blockstate.is(BlockTags.BEEHIVES, p_202454_ -> p_202454_.hasProperty(BeehiveBlock.HONEY_LEVEL) && p_202454_.getBlock() instanceof BeehiveBlock)) {
int i = blockstate.getValue(BeehiveBlock.HONEY_LEVEL);
if (i >= 5) {
level.playSound(null, pos, SoundEvents.BEEHIVE_SHEAR, SoundSource.BLOCKS, 1.0F, 1.0F);
BeehiveBlock.dropHoneycomb(level, pos);
((BeehiveBlock)blockstate.getBlock())
.releaseBeesAndResetHoneyLevel(level, blockstate, pos, null, BeehiveBlockEntity.BeeReleaseStatus.BEE_RELEASED);
level.gameEvent(null, GameEvent.SHEAR, pos);
return true;
}
}
return false;
}
private static boolean tryShearLivingEntity(ServerLevel level, BlockPos pos, ItemStack item) {
for (LivingEntity livingentity : level.getEntitiesOfClass(LivingEntity.class, new AABB(pos), EntitySelector.NO_SPECTATORS)) {
if (livingentity instanceof PlayerLeashable playerLeashable) {
if (playerLeashable.isLeashed()){
Leashable.dropLeash((Entity & Leashable)playerLeashable, true, !(playerLeashable.getLeashHolder() instanceof LeashRopeArrow));
if (playerLeashable.getLeashHolder() instanceof LeashRopeArrow leashRopeArrow) {
leashRopeArrow.setOwner(null);
}
return true;
} else return false;
} else if (livingentity instanceof Leashable leashable) {
if (leashable.isLeashed()) {
Leashable.dropLeash((Entity & Leashable) leashable,true, !(leashable.getLeashHolder() instanceof LeashRopeArrow));
if (leashable.getLeashHolder() instanceof LeashRopeArrow leashRopeArrow) {
leashRopeArrow.setOwner(null);
}
return true;
} else return false;
}
}
return false;
}
}

View File

@ -0,0 +1,28 @@
package com.r3944realms.leashedplayer.content.paintings;
import com.r3944realms.leashedplayer.LeashedPlayer;
import com.r3944realms.leashedplayer.datagen.provider.attributes.ModPaintingVariants;
import net.minecraft.core.registries.Registries;
import net.minecraft.network.chat.Component;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.entity.decoration.PaintingVariant;
import net.neoforged.bus.api.IEventBus;
import net.neoforged.neoforge.registries.DeferredRegister;
import org.jetbrains.annotations.NotNull;
import java.util.Optional;
import java.util.function.Supplier;
public class ModPaintingsRegister {
public static final DeferredRegister<PaintingVariant> PAINTING_VARIANT =
DeferredRegister.create(Registries.PAINTING_VARIANT, LeashedPlayer.MOD_ID);
public static final Supplier<PaintingVariant> GROUP_PHOTO = PAINTING_VARIANT.register("group_photo", () -> new PaintingVariant(1920, 1080, getAssetId("group_photo"), Optional.of(Component.translatable(ModPaintingVariants.getPaintingVariantTitleKey(ModPaintingVariants.GROUP_PHOTO))), Optional.of(Component.translatable(ModPaintingVariants.getPaintingVariantAuthorKey(ModPaintingVariants.GROUP_PHOTO)))));
private static @NotNull ResourceLocation getAssetId(String paint_name) {
return ResourceLocation.fromNamespaceAndPath(LeashedPlayer.MOD_ID, "textures/painting/"+paint_name+".png");
}
public static void register(IEventBus eventBus) {
PAINTING_VARIANT.register(eventBus);
}
}

View File

@ -0,0 +1,35 @@
package com.r3944realms.leashedplayer.content.sounds;
import com.r3944realms.leashedplayer.LeashedPlayer;
import net.minecraft.core.registries.BuiltInRegistries;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.sounds.SoundEvent;
import net.neoforged.bus.api.IEventBus;
import net.neoforged.neoforge.registries.DeferredHolder;
import net.neoforged.neoforge.registries.DeferredRegister;
import java.util.function.Supplier;
public class ModSoundRegister {
public static DeferredRegister<SoundEvent> SOUNDS = DeferredRegister.create(BuiltInRegistries.SOUND_EVENT, LeashedPlayer.MOD_ID);
public static ResourceLocation RL_FOX_MUSIC = ResourceLocation.fromNamespaceAndPath(LeashedPlayer.MOD_ID, "music/what_does_the_fox_say");
public static final DeferredHolder<SoundEvent, SoundEvent> FOX_MUSIC =
ModSoundRegister.register("what_does_the_fox_say", () -> SoundEvent.createFixedRangeEvent(
RL_FOX_MUSIC,
128
));
public static DeferredHolder<SoundEvent, SoundEvent> register(String name, Supplier<SoundEvent> supplier){
return SOUNDS.register(name, supplier);
}
public static void register(IEventBus modBus){
SOUNDS.register(modBus);
}
public static String getJukeboxSongTranslateKey(String name) {
return "jukebox_song." + LeashedPlayer.MOD_ID + "." + name;
}
public static String getSubTitleTranslateKey(String name) {
return "sound." + LeashedPlayer.MOD_ID + ".subtitle." + name;
}
}

View File

@ -0,0 +1,43 @@
package com.r3944realms.leashedplayer.datagen.LanguageAndOtherData;
import com.r3944realms.leashedplayer.LeashedPlayer;
import javax.annotation.Nullable;
public enum ModAdvancementKey {
LEASH_START("leash_start", null),
LEASHED_FRIEND("leashed_friend", LEASH_START),
LEASHED_SELF("leashed_self", LEASH_START),
LEASH_ARROW("leash_arrow", LEASH_START),
ADVANCEMENT_LEASH_ARROW("advancement_leash_arrow", LEASH_ARROW),
FOLLOW_LEASH_ARROW("follow_arrow", LEASH_ARROW),
DOG_RUNNING_PLAYER("dog_running_player", LEASH_ARROW),
NO_LEASH("no_leash", LEASH_START),
LEASH_TERMINATOR("leash_terminator", LEASH_START),
TIPPED_LEASH_ARROW("tipped_leash_arrow", LEASH_ARROW),
NEO_FOX("neo_fox", LEASH_START),
;
private final String Name;
@Nullable
private final ModAdvancementKey Parent;
ModAdvancementKey(String name, @Nullable ModAdvancementKey parent) {
this.Name = name;
this.Parent = parent;
}
public @Nullable ModAdvancementKey getParent() {
return Parent;
}
public String getNameKey() {
return "advancement." + LeashedPlayer.MOD_ID + "." + Name;
}
public String getDescKey() {
return this.getNameKey() + ".desc";
}
public String getNameWithNameSpace() {
return LeashedPlayer.MOD_ID + ":" + this.Name;
}
}

View File

@ -0,0 +1,21 @@
package com.r3944realms.leashedplayer.datagen.LanguageAndOtherData;
import net.minecraft.core.registries.Registries;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.tags.TagKey;
import net.minecraft.world.item.Item;
public final class ModItemTags {
public static final TagKey<Item> AMETHYST_TOOL_MATERIALS = bind("amethyst_tool_materials");
private ModItemTags() {
}
private static TagKey<Item> bind(String name) {
return TagKey.create(Registries.ITEM, ResourceLocation.withDefaultNamespace(name));
}
public static TagKey<Item> create(final ResourceLocation name) {
return TagKey.create(Registries.ITEM, name);
}
}

View File

@ -0,0 +1,235 @@
package com.r3944realms.leashedplayer.datagen.LanguageAndOtherData;
import com.r3944realms.leashedplayer.content.ModKeyMapping;
import com.r3944realms.leashedplayer.content.commands.LeashCommand;
import com.r3944realms.leashedplayer.content.commands.MotionCommand;
import com.r3944realms.leashedplayer.content.effects.ModEffectRegister;
import com.r3944realms.leashedplayer.content.effects.ModPotionRegister;
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.KeepLeashNotDropTime;
import com.r3944realms.leashedplayer.content.gamerules.Server.TeleportWithLeashedPlayers;
import com.r3944realms.leashedplayer.content.items.ModCreativeTab;
import com.r3944realms.leashedplayer.content.items.ModItemRegister;
import com.r3944realms.leashedplayer.content.items.type.LeadBreakerItem;
import com.r3944realms.leashedplayer.content.items.type.LeashRopeArrowItem;
import com.r3944realms.leashedplayer.content.items.type.SpectralLeashRopeArrowItem;
import com.r3944realms.leashedplayer.content.items.type.TippedLeashRopeArrowItem;
import com.r3944realms.leashedplayer.content.sounds.ModSoundRegister;
import com.r3944realms.leashedplayer.datagen.provider.attributes.ModPaintingVariants;
import com.r3944realms.leashedplayer.network.server.Code;
import com.r3944realms.leashedplayer.network.server.DecreaseLeashRopeLength;
import com.r3944realms.leashedplayer.network.server.IncreaseLeashRopeLength;
import com.r3944realms.leashedplayer.utils.Enum.LanguageEnum;
import com.r3944realms.leashedplayer.utils.Enum.ModPartEnum;
import net.minecraft.world.item.Item;
import net.minecraft.world.level.block.Block;
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 {
KEY_CATEGORY(ModKeyMapping.CATEGORY, ModPartEnum.NAME, "Leashed Player", "可拴玩家", "可拴玩家", false),
KEY_ADD_LEASH_OTHER_LEASH_LENGTH(ModKeyMapping.ADD_LEASH_LENGTH_KEY, ModPartEnum.NAME, "Increase the Length of Leash Rope", "增加拴绳长度", "增加拴繩長度", false),
KEY_SUB_LEASH_OTHER_LEASH_LENGTH(ModKeyMapping.SUB_LEASH_LENGTH_KEY, ModPartEnum.NAME, "Decrease the Length of Leash Rope", "减小拴绳长度", "減小拴繩長度", false),
//ITEM
ITEM_LEASH_ROPE_ARROW(ModItemRegister.LEASH_ROPE_ARROW, ModPartEnum.ITEM, "Leash Rope Arrow", "拴绳箭", "拴繩箭", true),
ITEM_SPECTRAL_LEASH_ROPE_ARROW(ModItemRegister.SPECTRAL_LEASH_ROPE_ARROW, ModPartEnum.ITEM, "Spectral Leash Rope Arrow", "拴绳光灵箭", "拴繩光靈箭", true),
TEST_FABRIC_ITEM(ModItemRegister.FABRIC, ModPartEnum.ITEM, "Fabric", "Fabric", "Fabric", true),
AMETHYST_SHEARS(ModItemRegister.AMETHYST_SHEARS, ModPartEnum.ITEM, "Amethyst Shears", "紫水晶剪刀", "紫水晶剪刀", true),
NEO_FORGE(ModItemRegister.NEOFORGE, ModPartEnum.ITEM, "NeoForge", "NeoForge", "NeoForge", false),
TIPPED_LEASH_ROPE_ARROW(TippedLeashRopeArrowItem.TIPPED_LEASH_ROPE_ARROW_NAME, ModPartEnum.ITEM, "Tipped Leash Rope Arrow Soaked By %1$s", "用%1$s浸泡过的拴绳箭", "蘸有%1$s的拴繩箭", false),
//ITEM_DESC
DESC_ITEM_LEASH_R_ARROW_ONE(LeashRopeArrowItem.DESC_1, ModPartEnum.DESCRIPTION, "§7This arrow will carry the owner along with its flight:", "§7该箭将会携带拥有者随其飞行", "§7該箭將會攜帶擁有者隨其飛行", false),
DESC_ITEM_LEASH_R_ARROW_TWO(LeashRopeArrowItem.DESC_2, ModPartEnum.DESCRIPTION, "§c1.§r If it hits a fence or an entity, it will leash the owner to it and drop as a normal arrow.", "§c1.§r 若击中栅栏或生物时,将持有者拴在其上并已普通箭形式掉落;", "§c1.§r 若擊中柵欄或生物時,將持有者拴在其上並以普通箭的形式掉落;", false),
DESC_ITEM_LEASH_R_ARROW_THREE(LeashRopeArrowItem.DESC_3, ModPartEnum.DESCRIPTION,"§c2.§r Crouching near the arrow allows for faster retrieval. If the arrow's owner is not the player, the owner will be leashed to the player who picks it up.", "§c2.§r 靠近该箭下蹲可以更快拾取该箭,如果该箭持有者不是自己,则持有者将被拾取者拴住;", "§c2.§r 靠近該箭下蹲可以更快拾取該箭,如果該箭的持有者不是自己,則持有者將被拾取者拴住;", false),
DESC_ITEM_LEASH_R_ARROW_FOUR(LeashRopeArrowItem.DESC_4, ModPartEnum.DESCRIPTION, "§c3.§r When fired from its launcher, the first entity hit will become the arrow's owner and will fly along with it.", "§c3.§r 当前其发射器里发射,第一个射中的生物将成为此箭的持有者并随箭飞行;", "§c3.§r 當箭從發射器發射時,第一個射中的生物將成為此箭的持有者並隨箭飛行;", false),
DESC_ITEM_LEASH_R_ARROW_FIVE(LeashRopeArrowItem.DESC_5, ModPartEnum.DESCRIPTION, "§c4.§r Under the §c§l\"no_leash\"§r effect, the behavior of the arrow when fired will follow the launchers behavior.", "§c4.§r 在§c§l禁拴§7(§c§lno_leash§7)§r效果下射出的箭行为同发射器。","§c4.§r 在§c§l禁拴§7(§c§lno_leash§7)§r效果下射出的箭行為將與發射器的行為相同。", false),
DESC_ITEM_LEASH_ROPE_ARROW(LeashRopeArrowItem.descKey, ModPartEnum.DESCRIPTION, "Arrows with ropes attached?","带有拴绳的箭矢?", "帶有拴繩的箭矢?", false),
DESC_ITEM_V_LEASH_R_ARROW_ONE(LeashRopeArrowItem.DESC_V_1, ModPartEnum.DESCRIPTION, "§7A variant of Leash Rope Arrow", "§7拴绳箭的一个变种", "§7拴繩箭矢的一個變種", false),
DESC_ITEM_V_LEASH_R_ARROW_TWO(LeashRopeArrowItem.DESC_V_2, ModPartEnum.DESCRIPTION, "§c1.§r The function is the same as its original one。", "§c1.§r 功能同其本体;", "§c1.§r 功能與本體一致;", false),
DESC_ITEM_S_LEASH_R_ARROW_THREE(SpectralLeashRopeArrowItem.DESC, ModPartEnum.DESCRIPTION, "§c2.§r Strike the entity to give it a §e§lGlowing§r effect.", "§c2.§r 击中实体给与其§e§l发光§7(§e§lGlowing§7)§r效果", "擊中實體給予其§e§l發光§7(§e§lGlowing§7)§r效果", false),
DESC_ITEM_T_LEASH_R_ARROW_THREE(TippedLeashRopeArrowItem.DESC, ModPartEnum.DESCRIPTION, "§c2.§rStrike the entity to give it a Potion effect.", "§c2.§r 击中实体给与其药水效果", "擊中實體給予其药水效果", false),
DESC_ITEM_LEAD_BREAKER(LeadBreakerItem.HOVER_KEY, ModPartEnum.DESCRIPTION, "§7can break the link of leash", "§7可以破坏拴绳链接", "§7可以破壞拴繩鏈接", false),
//PAINTING
GROUP_PHOTO_TITLE(ModPaintingVariants.getPaintingVariantTitleKey(ModPaintingVariants.GROUP_PHOTO),ModPartEnum.TITLE, "§dGroup Photo §7[§6memorable§7]§r", "§d集体照 §7[§6纪念§7]§r", "§d集體照 §7[§6紀念§7]§r", false),
GROUP_PHOTO_AUTHOR(ModPaintingVariants.getPaintingVariantAuthorKey(ModPaintingVariants.GROUP_PHOTO),ModPartEnum.AUTHOR, "§9Leisure §4Time §eDock§r","§9闲趣§4时§e坞§r","§9閑趣§4時§e塢§r",false),
//ENTITY
LEASH_ROPE_ARROW(ModEntityRegister.getEntityNameKey("leash_rope_arrow"), ModPartEnum.ENTITY, "Leash Rope Arrow", "拴绳箭", "拴繩箭", false),
SPECTRAL_LEASH_ROPE_ARROW(ModEntityRegister.getEntityNameKey("spectral_leash_rope_arrow"), ModPartEnum.ENTITY, "Spectral Leash Rope Arrow", "拴绳光灵箭", "拴繩光靈箭", false),
kID(ModEntityRegister.getEntityNameKey("kid_player"), ModPartEnum.ENTITY, "Kid", "小孩", "小孩", "", false),
NESTLE_ROPE_ARROW(ModEntityRegister.getEntityNameKey("nestle_rope_arrow"), ModPartEnum.ENTITY, "Nestle Rope Arrow", "贴贴拴绳箭", "貼貼拴繩箭", false),
//DIST
ST_WHAT_DOES_THE_FOX_SAY(ModSoundRegister.getSubTitleTranslateKey("what_does_the_fox_say"), ModPartEnum.TITLE, "Great Chu will rise again! Chen She will be king!", "大楚兴~ 陈胜王~~", "大楚興~ 陳勝王~~", false),
JB_WHAT_DOES_THE_FOX_SAY(ModSoundRegister.getJukeboxSongTranslateKey("what_does_the_fox_say"), ModPartEnum.DESCRIPTION, "What does the fox say?", "狐狸是怎么叫的?", "狐狸是怎麽叫的?", 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_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_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),
MESSAGE_MOTION_ADDER_SUCCESSFUL(MotionCommand.MOTION_ADDER_SUCCESSFUL, ModPartEnum.COMMAND, "§bAdd Successfully.§a%s§7:§f[§eVec§7: §a(§f%f§7,§f%f§7,§f%f§a)§f]§r","§b添加成功.§a%s§7:§f[§e加速度§7:§a(§f%f§7,§f%f§7,§f%f§a)§f]§r","§b添加成功.§a%s§7:§f[§e加速度§7:§a(§f%f§7,§f%f§7,§f%f§a)§f]§r", false),
MESSAGE_MOTION_SETTER_SUCCESSFUL(MotionCommand.MOTION_SETTER_SUCCESSFUL, ModPartEnum.COMMAND, "§bSet Successfully.§a%s§7:§f[§eVec§7: §a(§f%f§7,§f%f§7,§f%f§a)§f]§r","§b设置成功.§a%s§7:§f[§e加速度§7:§a(§f%f§7,§f%f§7,§f%f§a)§f]§r","§b設置成功.§a%s§7:§f[§e加速度§7:§a(§f%f§7,§f%f§7,§f%f§a)§f]§r",false),
MESSAGE_MOTION_MULTIPLY_SUCCESSFUL(MotionCommand.MOTION_MULTIPLY_SUCCESSFUL, ModPartEnum.COMMAND, "§bMultiply Successfully.§a%s§7:§f[§eVec§7: §a(§f%f§7,§f%f§7,§f%f§a)§f]§r","§b倍乘成功.§a%s§7:§f[§e加速度§7:§a(§f%f§7,§f%f§7,§f%f§a)§f]§r","§b倍乘成功.§a%s§7:§f[§e加速度§7:§a(§f%f§7,§f%f§7,§f%f§a)§f]§r",false),
MESSAGE_LEASH_BREAKER_USE_SUF(LeadBreakerItem.MESSAGE_USE_SUF, ModPartEnum.MESSAGE, "§aSuccessfully break §f%1$s§a 's Leashed Link to §f%2$s ", "§a成功剪断§f%2$s§a对§f%1$s§a拴绳链接", "§a成功剪斷§f%2$s§a對§f%1$s§a拴繩鏈接", false),
MESSAGE_LEASH_BREAKER_USE_FAI(LeadBreakerItem.MESSAGE_USE_FAI, ModPartEnum.MESSAGE, "§cFailed to break §f%1$s§c 's Leashed Link to §f%2$s", "§c无法剪断§f%2$s§c对§f%1$s§c拴绳链接", "§c未能剪斷§f%2$s§c對§f%1$s§c拴繩鏈接", false),
MESSAGE_LEASH_NOT_SUPPORT_TO_NOT_PLAYER_ENTITY(ModKeyMapping.NOT_SUPPORT_TO_NOT_PLAYER_ENTITY, ModPartEnum.MESSAGE, "Only work on Players", "只在玩家身上有效", "僅對玩家有效", false),
MESSAGE_LEASH_ADD_LEASH_LENGTH(IncreaseLeashRopeLength.INCREASE_LEASH_ROPE_LENGTH, ModPartEnum.MESSAGE, "§aIncrease the §f%s §aLength of Leash Rope§7(§bLength§7:§e%d§7)", "§a增加§f%s的拴绳长度§a§7(§b长度§7:§e%d§7)", "§a增加§f%s§a的拴繩長度§7(§長度§7:§e%d§7)", false),
MESSAGE_LEASH_SUB_LEASH_LENGTH(DecreaseLeashRopeLength.DECREASE_LEASH_ROPE_LENGTH, ModPartEnum.MESSAGE, "§Decrease the §f%s §cLength of Leash Rope§7(§bLength§7:§e%d§7)", "§c减少§f%s的拴绳长度§c§7(§b长度§7:§e%d§7)", "§c減少§f%s§c的拴繩長度§7(§長度§7:§e%d§7)", false),
MESSAGE_LEASH_ADD_SELF_LEASH_LENGTH(IncreaseLeashRopeLength.INCREASE_SELF_LEASH_ROPE_LENGTH, ModPartEnum.MESSAGE, "§aIncrease the Length of Leash Rope§7(§bLength§7:§e%d§7)", "§a增加拴绳长度§a§7(§b长度§7:§e%d§7)", "§a增加拴繩長度§7(§長度§7:§e%d§7)", false),
MESSAGE_LEASH_SUB_SELF_LEASH_LENGTH(DecreaseLeashRopeLength.DECREASE_SELF_LEASH_ROPE_LENGTH, ModPartEnum.MESSAGE, "§cDecrease the Length of Leash Rope§7(§bLength§7:§e%d§7)", "§c减少拴绳长度§c§7(§b长度§7:§e%d§7)", "§c減少拴繩長度§7(§長度§7:§e%d§7)", false),
MESSAGE_LEASH_FAILED_SET_LENGTH(Code.LEASH_LENGTH_FAILED_SET, ModPartEnum.MESSAGE, "§cFailed", "§c失败", "§c失敗", false),
//GAME_RULE_NAME
TELEPORT_WITH_LEASHED_PLAYERS_NAME(TeleportWithLeashedPlayers.NAME_KEY, ModPartEnum.NAME, "Teleport leashed player with player holder", "被拴玩家随玩家持有者传送", "被拴玩家随玩家持有者傳送" ,false),
CREATE_LEASH_FENCE_KNOT_ENTITY_IF_ABSENT_NAME(CreateLeashFenceKnotEntityIfAbsent.NAME_KEY, ModPartEnum.NAME, "Create Leash Fence Knot Entity if absent", "如果缺失则创建拴绳结", "如果缺失則創建拴繩結", false),
KEEP_LEASH_NOT_DROP_TIME_NAME(KeepLeashNotDropTime.NAME_KEY, ModPartEnum.NAME, "Keep leash alive Time", "保持拴绳不掉落的时间", "保持其不掉落的時間", false),
TRY_TO_PICKUP_LEASHED_ROPE_ARROW(LeashRopeArrow.PUSH_SHIFT_TO_PICKUP_QUICKLY, ModPartEnum.MESSAGE, "§aPush §f§lShift§a to pick up quickly", "§a按下§f§lShift键§a以加快拾取", "§a按下§f§lShift鍵§a以加速拾取", 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),
KEEP_LEASH_NOT_DROP_TIME_DESCRIPTION(KeepLeashNotDropTime.DESCRIPTION_KEY, ModPartEnum.DESCRIPTION,"The time of Keep new leash which has far distance alive (Tick)", "当距离过远时,保持新建拴绳不掉落的时间 (刻)", "儅距離過遠時,保持其不掉落的時間(刻)", false),
//ADV_NAME
LEASH_START(ModAdvancementKey.LEASH_START.getNameKey(), ModPartEnum.NAME, "The Power of Traction", "牵引之力", "牽引之力", false),
LEASH_LR_ARROW(ModAdvancementKey.LEASH_ARROW.getNameKey(), ModPartEnum.NAME, "Arrow with a Tether?" , "拴绳之箭?", "拴繩之箭?", false),
LEASH_SLP_ARROW(ModAdvancementKey.ADVANCEMENT_LEASH_ARROW.getNameKey(), ModPartEnum.NAME, "More advanced flash arrow with a Tether?", "更闪亮的拴绳箭?", "更閃亮的拴繩箭?", false),
LEASH_SELF(ModAdvancementKey.LEASHED_SELF.getNameKey(), ModPartEnum.NAME, "Stable Connection", "稳固联结" ,"穩固聯結", false),
LEASH_PLAYER(ModAdvancementKey.LEASHED_FRIEND.getNameKey(),ModPartEnum.NAME, "Be bound by Rope", "拴绳链接", "拴繩鏈接" , false),
FOLLOW_ARROW(ModAdvancementKey.FOLLOW_LEASH_ARROW.getNameKey(), ModPartEnum.NAME, "Launch!!!", "启航!!!" , "啓航!!!",false),
FOLLOW_WOLF(ModAdvancementKey.DOG_RUNNING_PLAYER.getNameKey(), ModPartEnum.NAME, "It's Walking human time.", "遛“人”时间", "遛“人”時間",false),
TERMINATOR(ModAdvancementKey.LEASH_TERMINATOR.getNameKey(), ModPartEnum.NAME, "The Lead Terminator", "拴绳终结者","拴繩終結者", false),
NO_LEASH(ModAdvancementKey.NO_LEASH.getNameKey(), ModPartEnum.NAME, "Don't tie me up", "勿拴我", "請恁勿拴唔", false),
TIPPED_LEASH_ARROW(ModAdvancementKey.TIPPED_LEASH_ARROW.getNameKey(), ModPartEnum.NAME, "God said there should be more arrows", "神说要有更多箭矢", "神說要有更多箭矢", false),
NEO_FOX(ModAdvancementKey.NEO_FOX.getNameKey(), ModPartEnum.NAME, "NEOFORGE!", "NEOFORGE!", "NEOFORGE!", false),
//ADV_DESC
LEASH_START_DESC(ModAdvancementKey.LEASH_START.getDescKey(), ModPartEnum.DESCRIPTION, "Journey to becoming a Leash Expert", "拴绳大师之路", "拴繩大師之路", false),
LEASH_LR_ARROW_DESC(ModAdvancementKey.LEASH_ARROW.getDescKey(), ModPartEnum.DESCRIPTION, "Maybe you can using it to shoot some mob?", "也许可以用它来发射生物?", "也許可以用它發射生物?", false),
LEASH_SLP_ARROW_DESC(ModAdvancementKey.ADVANCEMENT_LEASH_ARROW.getDescKey(), ModPartEnum.DESCRIPTION, "Well, apart from glowing, there doesn't seem to be any other difference", "嗯,除了发光,似乎没有什么其它不同了" ,"嗯,除了發光,其它好像沒什麽不同", false),
LEASH_SELF_DESC(ModAdvancementKey.LEASHED_SELF.getDescKey(), ModPartEnum.DESCRIPTION, "“Restrain oneself with a rope", "用拴绳拴住自己" ,"用栓繩拴住自己", false),
LEASH_PLAYER_DESC(ModAdvancementKey.LEASHED_FRIEND.getDescKey(),ModPartEnum.DESCRIPTION, "Be Bond by player with lead", "被玩家用拴绳链接", "被玩家用拴繩鏈接", false),
FOLLOW_ARROW_DESC(ModAdvancementKey.FOLLOW_LEASH_ARROW.getDescKey(), ModPartEnum.DESCRIPTION, "Mc, what are you talking about in physics?", "抱歉,我的世界不存在物理学" , "抱歉,麦块不講物理學",false),
FOLLOW_WOLF_DESC(ModAdvancementKey.DOG_RUNNING_PLAYER.getDescKey(), ModPartEnum.DESCRIPTION, "In the park where dogs are not allowed to be walked, the dog decided to walk the human instead", "公园不能遛狗,于是狗站起来遛人", "公園裏不許遛狗,於是狗站起來遛人",false),
NO_LEASH_DESC(ModAdvancementKey.NO_LEASH.getDescKey(), ModPartEnum.DESCRIPTION, "You cannot be leashed by ANY", "你不会被任何东西拴住", "恁不會被任何拴住", false),
TERMINATOR_DESC(ModAdvancementKey.LEASH_TERMINATOR.getDescKey(), ModPartEnum.DESCRIPTION, "I am Lead Terminator!", "我來终结拴绳者!", "吾將終結拴繩!", false),
TIPPED_LEASH_ARROW_DESC(ModAdvancementKey.TIPPED_LEASH_ARROW.getDescKey(), ModPartEnum.DESCRIPTION, "A dazzling array of Leash Rope arrows", "真是琳琅满目啊", "真是琳琅滿目啊", false),
NEO_FOX_DESC(ModAdvancementKey.NEO_FOX.getDescKey(), ModPartEnum.DESCRIPTION, "It seems can be equipped.", "似乎可以戴头上", "似乎可以戴著", false),
//MOB_EFFECT
NO_LEASH_EFFECT(ModEffectRegister.getEffectKey(ModEffectRegister.NO_LEASH_EFFECT.get()), ModPartEnum.NAME, "No Leash", "禁拴", "禁拴", false),
//POTION
NO_LEASH_POTION(ModPotionRegister.getPotionNameKey("no_leash", (char) 0), ModPartEnum.NAME, "No Leash Potion", "禁拴药水", "禁拴藥水", false),
NO_LEASH_POTION_SPLASH(ModPotionRegister.getPotionNameKey("no_leash", (char) 2), ModPartEnum.NAME, "Splash No Leash Potion", "喷溅型禁拴药水", "噴濺型禁拴藥水", false),
NO_LEASH_POTION_LINGERING(ModPotionRegister.getPotionNameKey("no_leash", (char) 1), ModPartEnum.NAME, "Splash No Leash Potion", "滞留型禁拴药水", "滯留型禁拴藥水", false),
//ARROW
NO_LEASH_ARROW(ModPotionRegister.getTippedArrowNameKey("no_leash"), ModPartEnum.ITEM, "Arrow of No Leash", "禁拴之箭", "禁拴之箭", false),
;
private final Supplier<?> supplier;
private String key;
private final String US_EN;
private final String SIM_CN;
private final String TRA_CN;
private final String LZH;
private final Boolean Default;
private final ModPartEnum MPE;
ModLangKeyValue(Supplier<?> Supplier, ModPartEnum MPE, String US_EN, String SIM_CN, String TRA_CN, String LZH, Boolean isDefault) {
this.supplier = Supplier;
this.MPE = MPE;
this.US_EN = US_EN;
this.SIM_CN = SIM_CN;
this.TRA_CN = TRA_CN;
this.LZH = LZH;
this.Default = isDefault;
}
ModLangKeyValue(@NotNull String ResourceKey, ModPartEnum MPE, String US_EN, String SIM_CN, String TRA_CN, String LZH, Boolean isDefault) {
this.supplier = null;
this.key = ResourceKey;
this.MPE = MPE;
this.US_EN = US_EN;
this.SIM_CN = SIM_CN;
this.TRA_CN = TRA_CN;
this.LZH = LZH;
this.Default = isDefault;
}
ModLangKeyValue(Supplier<?> Supplier, ModPartEnum MPE, String US_EN, String SIM_CN, String TRA_CN, Boolean isDefault) {
this(Supplier, MPE, US_EN, SIM_CN, TRA_CN, null, isDefault);
}
ModLangKeyValue(@NotNull String ResourceKey, ModPartEnum MPE, String US_EN, String SIM_CN, String TRA_CN, Boolean isDefault) {
this(ResourceKey, MPE, US_EN, SIM_CN, TRA_CN, null, isDefault);
}
public static String getLan(LanguageEnum lan, ModLangKeyValue key) {
if (lan == null || lan == LanguageEnum.English) return getEnglish(key);
else {
switch (lan) {
case SimpleChinese -> {
return getSimpleChinese(key);
}
case TraditionalChinese -> {
return getTraditionalChinese(key);
}
case LiteraryChinese -> {
return getLiteraryChinese(key);
}
default -> {
return getEnglish(key);
}
}
}
}
private static String getEnglish(ModLangKeyValue key) {
return key.US_EN;
}
private static String getSimpleChinese(ModLangKeyValue key) {
return key.SIM_CN;
}
private static String getTraditionalChinese(ModLangKeyValue key) {
return key.TRA_CN;
}
@Nullable
public static String getLiteraryChinese(ModLangKeyValue key) {
return key.LZH;
}
public String getKey() {
if(key == null){
switch (MPE) {//Don't need to use "break;"[Java feature];
case CREATIVE_TAB, MESSAGE, INFO, DEFAULT, COMMAND, CONFIG -> throw new UnsupportedOperationException("The Key value is NULL! Please use the correct constructor and write the parameters correctly");
case ITEM -> key = (getItem()).getDescriptionId();
case BLOCK -> key =(getBlock()).getDescriptionId();
}
//需要完善
}
return key;
}
@SuppressWarnings("null")
public Item getItem() {
assert supplier != null;
return (Item)supplier.get();
}
@SuppressWarnings("null")
public Block getBlock() {
assert supplier != null;
return (Block)supplier.get();
}
public boolean isDefaultItem(){
return MPE == ModPartEnum.ITEM && Default;
}
public boolean isDefaultBlock() {
return MPE == ModPartEnum.BLOCK && Default;
}
}

View File

@ -0,0 +1,94 @@
package com.r3944realms.leashedplayer.datagen;
import com.r3944realms.leashedplayer.LeashedPlayer;
import com.r3944realms.leashedplayer.datagen.provider.*;
import com.r3944realms.leashedplayer.utils.Enum.LanguageEnum;
import net.minecraft.core.HolderLookup;
import net.minecraft.data.DataProvider;
import net.minecraft.data.PackOutput;
import net.neoforged.bus.api.SubscribeEvent;
import net.neoforged.fml.common.EventBusSubscriber;
import net.neoforged.neoforge.data.event.GatherDataEvent;
import java.util.concurrent.CompletableFuture;
@EventBusSubscriber(modid = LeashedPlayer.MOD_ID, bus = EventBusSubscriber.Bus.MOD)
public class ModDataGeneratorHandler {
@SubscribeEvent
public static void genData(GatherDataEvent.Client event) {
CompletableFuture<HolderLookup.Provider> holderFolder = event.getLookupProvider();
/*Language Provider ENGLISH CHINESE(SIM/TRA)*/
addLanguage(event, LanguageEnum.English);
addLanguage(event, LanguageEnum.SimpleChinese);
addLanguage(event, LanguageEnum.TraditionalChinese);
addLanguage(event, LanguageEnum.LiteraryChinese);
ModDataPackBuiltInEntriesProvider(event, holderFolder);
RecipeGenerator(event, holderFolder);
ModTagsProvider(event, holderFolder);
ModPaintVariantTagsProvider(event, holderFolder);
ModSoundProvider(event);
ModModelDataGenerate(event);
ModAdvancementProvider(event, holderFolder);
}
private static void addLanguage(GatherDataEvent event, LanguageEnum language){
event.getGenerator().addProvider(
true,
(DataProvider.Factory<ModLanguageProvider>) pOutput -> new ModLanguageProvider(pOutput, LeashedPlayer.MOD_ID, language)
);
}
private static void RecipeGenerator(GatherDataEvent event, CompletableFuture<HolderLookup.Provider> future) {
PackOutput packOutput = event.getGenerator().getPackOutput();
event.getGenerator().addProvider(
true,
new ModRecipeProvider.Runner(packOutput, future)
);
}
private static void ModTagsProvider(GatherDataEvent event, CompletableFuture<HolderLookup.Provider> completableFuture) {
ModBlockTagProvider modBlockTagProvider = event.getGenerator().addProvider(
true,
(DataProvider.Factory<ModBlockTagProvider>) pOutput -> {
return new ModBlockTagProvider(pOutput, completableFuture, LeashedPlayer.MOD_ID);
}
);
event.getGenerator().addProvider(
true,
(DataProvider.Factory<ModItemTagProvider>) pOutput ->
new ModItemTagProvider(pOutput, completableFuture, modBlockTagProvider.contentsGetter())
);
}
private static void ModSoundProvider(GatherDataEvent event) {
event.getGenerator().addProvider(
true,
(DataProvider.Factory<ModSoundDefinitionsProvider>) pOutput ->
new ModSoundDefinitionsProvider(pOutput,LeashedPlayer.MOD_ID)
);
}
private static void ModDataPackBuiltInEntriesProvider(GatherDataEvent event, CompletableFuture<HolderLookup.Provider> future) {
event.getGenerator().addProvider(
true,
(DataProvider.Factory<ModDataPackBuiltInEntriesProvider>) pOutput -> new ModDataPackBuiltInEntriesProvider(pOutput, future)
);
}
private static void ModAdvancementProvider(GatherDataEvent event, CompletableFuture<HolderLookup.Provider> pLookUpProvider) {
event.getGenerator().addProvider(
true,
(DataProvider.Factory<ModAdvancementProvider>) pOutput -> new ModAdvancementProvider(pOutput, pLookUpProvider));
}
private static void ModPaintVariantTagsProvider(GatherDataEvent event, CompletableFuture<HolderLookup.Provider> completableFuture) {
event.getGenerator().addProvider(
true,
(DataProvider.Factory<ModPaintingVariantTagsProvider>) pOutput -> {
return new ModPaintingVariantTagsProvider(pOutput, completableFuture, LeashedPlayer.MOD_ID);
}
);
}
private static void ModModelDataGenerate(GatherDataEvent event) {
event.getGenerator().addProvider(
true,
(DataProvider.Factory<ModModelDataProvider>) pOutput -> new ModModelDataProvider(pOutput, LeashedPlayer.MOD_ID)
);
}
}

View File

@ -0,0 +1,175 @@
package com.r3944realms.leashedplayer.datagen.generator;
import com.r3944realms.leashedplayer.LeashedPlayer;
import com.r3944realms.leashedplayer.content.criteriaTriggers.LeashPlayerTrigger;
import com.r3944realms.leashedplayer.content.effects.ModEffectRegister;
import com.r3944realms.leashedplayer.content.entities.ModEntityRegister;
import com.r3944realms.leashedplayer.content.items.ModItemRegister;
import com.r3944realms.leashedplayer.datagen.LanguageAndOtherData.ModAdvancementKey;
import net.minecraft.advancements.Advancement;
import net.minecraft.advancements.AdvancementHolder;
import net.minecraft.advancements.AdvancementType;
import net.minecraft.advancements.critereon.*;
import net.minecraft.core.HolderGetter;
import net.minecraft.core.HolderLookup;
import net.minecraft.core.registries.Registries;
import net.minecraft.data.advancements.AdvancementSubProvider;
import net.minecraft.network.chat.Component;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.entity.EntityType;
import net.minecraft.world.item.Items;
import java.util.function.Consumer;
public class ModAdvancementGenerator implements AdvancementSubProvider {
private final ResourceLocation ADV_BG = ResourceLocation.fromNamespaceAndPath(LeashedPlayer.MOD_ID, "textures/gui/advancements/backgrounds/leashed_player");
@Override
public void generate(HolderLookup.Provider registries, Consumer<AdvancementHolder> saver) {
HolderGetter<EntityType<?>> holdergetter = registries.lookupOrThrow(Registries.ENTITY_TYPE);
AdvancementHolder hasLeashRopeItem = Advancement.Builder.advancement().display(
Items.LEAD,
Component.translatable(ModAdvancementKey.LEASH_START.getNameKey()),
Component.translatable(ModAdvancementKey.LEASH_START.getDescKey()),
ADV_BG,
AdvancementType.TASK,
true,
false,
true
).addCriterion("has_leash_rope_item", InventoryChangeTrigger.TriggerInstance.hasItems(Items.LEAD))
.save(saver, ModAdvancementKey.LEASH_START.getNameWithNameSpace());
AdvancementHolder hasLeashRopeArrow = Advancement.Builder.advancement().display(
ModItemRegister.LEASH_ROPE_ARROW.get(),
Component.translatable(ModAdvancementKey.LEASH_ARROW.getNameKey()),
Component.translatable(ModAdvancementKey.LEASH_ARROW.getDescKey()),
null,
AdvancementType.TASK,
true,
false,
true
).addCriterion("has_leash_rope_item", InventoryChangeTrigger.TriggerInstance.hasItems(ModItemRegister.LEASH_ROPE_ARROW.get()))
.parent(hasLeashRopeItem)
.save(saver, ModAdvancementKey.LEASH_ARROW.getNameWithNameSpace());
AdvancementHolder hasFlashLeashRopeArrow = Advancement.Builder.advancement().display(
ModItemRegister.SPECTRAL_LEASH_ROPE_ARROW.get(),
Component.translatable(ModAdvancementKey.ADVANCEMENT_LEASH_ARROW.getNameKey()),
Component.translatable(ModAdvancementKey.ADVANCEMENT_LEASH_ARROW.getDescKey()),
null,
AdvancementType.TASK,
true,
false,
true
).addCriterion("has_flash_leash_rope_item", InventoryChangeTrigger.TriggerInstance.hasItems(ModItemRegister.SPECTRAL_LEASH_ROPE_ARROW.get()))
.parent(hasLeashRopeArrow)
.save(saver, ModAdvancementKey.ADVANCEMENT_LEASH_ARROW.getNameWithNameSpace());
AdvancementHolder leashedMySelf = Advancement.Builder.advancement().display(
Items.PLAYER_HEAD,
Component.translatable(ModAdvancementKey.LEASHED_SELF.getNameKey()),
Component.translatable(ModAdvancementKey.LEASHED_SELF.getDescKey()),
null,
AdvancementType.TASK,
true,
true,
true
).addCriterion("leash_self", LeashPlayerTrigger.TriggerInstance.LeashPlayer(
EntityPredicate.Builder.entity().entityType(EntityTypePredicate.of(holdergetter, EntityType.LEASH_KNOT))
))
.parent(hasLeashRopeItem).save(saver, ModAdvancementKey.LEASHED_SELF.getNameWithNameSpace());
AdvancementHolder followLeashRopeArrow = Advancement.Builder.advancement().display(
ModItemRegister.LEASH_ROPE_ARROW.get(),
Component.translatable(ModAdvancementKey.FOLLOW_LEASH_ARROW.getNameKey()),
Component.translatable(ModAdvancementKey.FOLLOW_LEASH_ARROW.getDescKey()),
null,
AdvancementType.TASK,
true,
false,
true
).addCriterion("leash_arrow", LeashPlayerTrigger.TriggerInstance.LeashPlayer(
EntityPredicate.Builder.entity().entityType(EntityTypePredicate.of(holdergetter, ModEntityRegister.LEASH_ROPE_ARROW.get()))
))
.parent(hasLeashRopeArrow)
.save(saver, ModAdvancementKey.FOLLOW_LEASH_ARROW.getNameWithNameSpace());
AdvancementHolder dogRunPlayer = Advancement.Builder.advancement().display(
Items.BONE,
Component.translatable(ModAdvancementKey.DOG_RUNNING_PLAYER.getNameKey()),
Component.translatable(ModAdvancementKey.DOG_RUNNING_PLAYER.getDescKey()),
null,
AdvancementType.CHALLENGE,
true,
true,
true
).addCriterion("leash_by_wo_do", LeashPlayerTrigger.TriggerInstance.LeashPlayer(
EntityPredicate.Builder.entity().entityType(EntityTypePredicate.of(holdergetter, EntityType.WOLF))
))
.parent(hasLeashRopeArrow).save(saver, ModAdvancementKey.DOG_RUNNING_PLAYER.getNameWithNameSpace());
AdvancementHolder leashedOther = Advancement.Builder.advancement().display(
Items.LEAD,
Component.translatable(ModAdvancementKey.LEASHED_FRIEND.getNameKey()),
Component.translatable(ModAdvancementKey.LEASHED_FRIEND.getDescKey()),
null,
AdvancementType.TASK,
true,
true,
true
).addCriterion("leash_other_player",
LeashPlayerTrigger.TriggerInstance.LeashPlayer(
EntityPredicate.Builder.entity().entityType(EntityTypePredicate.of(holdergetter, EntityType.PLAYER))
)
)
.parent(hasLeashRopeItem).save(saver, ModAdvancementKey.LEASHED_FRIEND.getNameWithNameSpace());
AdvancementHolder lead_rope_terminator = Advancement.Builder.advancement().display(
ModItemRegister.AMETHYST_SHEARS.get(),
Component.translatable(ModAdvancementKey.LEASH_TERMINATOR.getNameKey()),
Component.translatable(ModAdvancementKey.LEASH_TERMINATOR.getDescKey()),
null,
AdvancementType.TASK,
true,
true,
true
).addCriterion("has_amethyst_shears", InventoryChangeTrigger.TriggerInstance.hasItems(ModItemRegister.AMETHYST_SHEARS.get())
).parent(hasLeashRopeItem).save(saver, ModAdvancementKey.LEASH_TERMINATOR.getNameWithNameSpace());
AdvancementHolder no_leash = Advancement.Builder.advancement().display(
Items.BARRIER,
Component.translatable(ModAdvancementKey.NO_LEASH.getNameKey()),
Component.translatable(ModAdvancementKey.NO_LEASH.getDescKey()),
null,
AdvancementType.GOAL,
true,
true,
true
).addCriterion("no_leash",
EffectsChangedTrigger.TriggerInstance.hasEffects(
MobEffectsPredicate.Builder.effects()
.and(ModEffectRegister.NO_LEASH_EFFECT)
)
).parent(hasLeashRopeItem).save(saver, ModAdvancementKey.NO_LEASH.getNameWithNameSpace());
AdvancementHolder tipped_leash_arrow = Advancement.Builder.advancement().display(
ModItemRegister.TIPPED_LEASH_ROPE_ARROW.get(),
Component.translatable(ModAdvancementKey.TIPPED_LEASH_ARROW.getNameKey()),
Component.translatable(ModAdvancementKey.TIPPED_LEASH_ARROW.getDescKey()),
null,
AdvancementType.GOAL,
true,
true,
true
).addCriterion("has_tipped_leash_arrow", InventoryChangeTrigger.TriggerInstance.hasItems(ModItemRegister.TIPPED_LEASH_ROPE_ARROW.get())
).parent(hasLeashRopeArrow).save(saver, ModAdvancementKey.TIPPED_LEASH_ARROW.getNameWithNameSpace());
AdvancementHolder neo_fox = Advancement.Builder.advancement().display(
ModItemRegister.NEOFORGE.get(),
Component.translatable(ModAdvancementKey.NEO_FOX.getNameKey()),
Component.translatable(ModAdvancementKey.NEO_FOX.getDescKey()),
null,
AdvancementType.GOAL,
true,
false,
true
).addCriterion("has_neo_fox", InventoryChangeTrigger.TriggerInstance.hasItems(ModItemRegister.NEOFORGE.get())
).parent(hasLeashRopeItem).save(saver, ModAdvancementKey.NEO_FOX.getNameWithNameSpace());
}
}

View File

@ -0,0 +1,21 @@
package com.r3944realms.leashedplayer.datagen.generator;
import net.minecraft.client.data.models.BlockModelGenerators;
import net.minecraft.client.data.models.ItemModelOutput;
import net.minecraft.client.data.models.blockstates.BlockModelDefinitionGenerator;
import net.minecraft.client.data.models.model.ModelInstance;
import net.minecraft.resources.ResourceLocation;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
public class ModBlockModelGenerator extends BlockModelGenerators {
public ModBlockModelGenerator(Consumer<BlockModelDefinitionGenerator> blockStateOutput, ItemModelOutput itemModelOutput, BiConsumer<ResourceLocation, ModelInstance> modelOutput) {
super(blockStateOutput, itemModelOutput, modelOutput);
}
@Override
public void run() {
}
}

View File

@ -0,0 +1,129 @@
package com.r3944realms.leashedplayer.datagen.generator;
import com.r3944realms.leashedplayer.LeashedPlayer;
import com.r3944realms.leashedplayer.client.renderer.item.ConditionalRangeItemModel;
import com.r3944realms.leashedplayer.client.renderer.item.properties.conditional.ChargeExtend;
import com.r3944realms.leashedplayer.client.renderer.item.properties.conditionalRange.BowPull;
import com.r3944realms.leashedplayer.content.items.ModItemRegister;
import com.r3944realms.leashedplayer.datagen.LanguageAndOtherData.ModLangKeyValue;
import com.r3944realms.leashedplayer.extend.CrossbowItem$ChargeTypeExtend;
import net.minecraft.client.data.models.ItemModelGenerators;
import net.minecraft.client.data.models.ItemModelOutput;
import net.minecraft.client.data.models.model.ItemModelUtils;
import net.minecraft.client.data.models.model.ModelInstance;
import net.minecraft.client.data.models.model.ModelLocationUtils;
import net.minecraft.client.data.models.model.ModelTemplates;
import net.minecraft.client.renderer.item.ItemModel;
import net.minecraft.client.renderer.item.properties.numeric.CrossbowPull;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.Items;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.List;
import java.util.function.BiConsumer;
public class ModItemModelGenerator extends ItemModelGenerators {
private static List<Item> objectList;
public ModItemModelGenerator(ItemModelOutput itemModelOutput, BiConsumer<ResourceLocation, ModelInstance> modelOutput) {
super(itemModelOutput, modelOutput);
objectList = new ArrayList<>();
init();
}
@Override
public void run() {
DefaultModItemModelRegister();
generateTippedArrow(ModItemRegister.TIPPED_LEASH_ROPE_ARROW.get());
generateCrossbow(Items.CROSSBOW);
generateBow(Items.BOW);
}
private void init() {
for(ModLangKeyValue obj : ModLangKeyValue.values()) {
if(!obj.isDefaultItem()) continue;
objectList.add(obj.getItem());
}
}
/**
* @implNote <br/>&nbsp;先有纹理才会成功构建
*/
private void DefaultModItemModelRegister() {
objectList.forEach(i -> generateFlatItem(i, ModelTemplates.FLAT_ITEM));
}
@Override
public void generateBow(@NotNull Item bowItem) {
ItemModel.Unbaked itemmodel$unbaked = ItemModelUtils.plainModel(ModelLocationUtils.getModelLocation(bowItem));
ItemModel.Unbaked itemmodel$unbaked1 = ItemModelUtils.plainModel(this.createFlatItemModel(bowItem, "_pulling_0", ModelTemplates.BOW));
ItemModel.Unbaked itemmodel$unbaked2 = ItemModelUtils.plainModel(this.createFlatItemModel(bowItem, "_pulling_1", ModelTemplates.BOW));
ItemModel.Unbaked itemmodel$unbaked3 = ItemModelUtils.plainModel(this.createFlatItemModel(bowItem, "_pulling_2", ModelTemplates.BOW));
ItemModel.Unbaked itemmodel$unbaked4 = ItemModelUtils.plainModel(
com.r3944realms.leashedplayer.utils.dataModel.ItemModelUtils.createFlatItemModelByPath(LeashedPlayer.MOD_ID, "item/bow_lra_pulling_0", ModelTemplates.BOW, this.modelOutput)
);
ItemModel.Unbaked itemmodel$unbaked5 = ItemModelUtils.plainModel(
com.r3944realms.leashedplayer.utils.dataModel.ItemModelUtils.createFlatItemModelByPath(LeashedPlayer.MOD_ID, "item/bow_lra_pulling_1", ModelTemplates.BOW, this.modelOutput)
);
ItemModel.Unbaked itemmodel$unbaked6 = ItemModelUtils.plainModel(
com.r3944realms.leashedplayer.utils.dataModel.ItemModelUtils.createFlatItemModelByPath(LeashedPlayer.MOD_ID, "item/bow_lra_pulling_2", ModelTemplates.BOW, this.modelOutput)
);
this.itemModelOutput
.accept(
bowItem,
ItemModelUtils.conditional(
ItemModelUtils.isUsingItem(),
com.r3944realms.leashedplayer.utils.dataModel.ItemModelUtils.conditionalRange(
new BowPull(),
0.05F,
itemmodel$unbaked1,
new ConditionalRangeItemModel.Entry[] {
com.r3944realms.leashedplayer.utils.dataModel.ItemModelUtils.override(itemmodel$unbaked4, 0.0F),
com.r3944realms.leashedplayer.utils.dataModel.ItemModelUtils.override(itemmodel$unbaked5, 0.65F),
com.r3944realms.leashedplayer.utils.dataModel.ItemModelUtils.override(itemmodel$unbaked6, 0.9F)
},
new ConditionalRangeItemModel.Entry[] {
com.r3944realms.leashedplayer.utils.dataModel.ItemModelUtils.override(itemmodel$unbaked1, 0.0F),
com.r3944realms.leashedplayer.utils.dataModel.ItemModelUtils.override(itemmodel$unbaked2, 0.65F),
com.r3944realms.leashedplayer.utils.dataModel.ItemModelUtils.override(itemmodel$unbaked3, 0.9F)
}
),
itemmodel$unbaked
)
);
}
@Override
public void generateCrossbow(@NotNull Item crossbowItem) {
ItemModel.Unbaked itemmodel$unbaked = ItemModelUtils.plainModel(ModelLocationUtils.getModelLocation(crossbowItem));
ItemModel.Unbaked itemmodel$unbaked1 = ItemModelUtils.plainModel(this.createFlatItemModel(crossbowItem, "_pulling_0", ModelTemplates.CROSSBOW));
ItemModel.Unbaked itemmodel$unbaked2 = ItemModelUtils.plainModel(this.createFlatItemModel(crossbowItem, "_pulling_1", ModelTemplates.CROSSBOW));
ItemModel.Unbaked itemmodel$unbaked3 = ItemModelUtils.plainModel(this.createFlatItemModel(crossbowItem, "_pulling_2", ModelTemplates.CROSSBOW));
ItemModel.Unbaked itemmodel$unbaked4 = ItemModelUtils.plainModel(this.createFlatItemModel(crossbowItem, "_arrow", ModelTemplates.CROSSBOW));
ItemModel.Unbaked itemmodel$unbaked5 = ItemModelUtils.plainModel(this.createFlatItemModel(crossbowItem, "_firework", ModelTemplates.CROSSBOW));
ItemModel.Unbaked itemmodel$unbaked6 = ItemModelUtils.plainModel(
com.r3944realms.leashedplayer.utils.dataModel.ItemModelUtils.createFlatItemModelByPath(LeashedPlayer.MOD_ID, "item/crossbow_leash_rope_arrow", ModelTemplates.CROSSBOW, this.modelOutput)
);
this.itemModelOutput
.accept(
crossbowItem,
ItemModelUtils.conditional(
ItemModelUtils.isUsingItem(),
ItemModelUtils.rangeSelect(
new CrossbowPull(),
itemmodel$unbaked1,
ItemModelUtils.override(itemmodel$unbaked2, 0.58F),
ItemModelUtils.override(itemmodel$unbaked3, 1.0F)
),
ItemModelUtils.select(
new ChargeExtend(),
itemmodel$unbaked,
ItemModelUtils.when(CrossbowItem$ChargeTypeExtend.ARROW, itemmodel$unbaked4),
ItemModelUtils.when(CrossbowItem$ChargeTypeExtend.ROCKET, itemmodel$unbaked5),
ItemModelUtils.when(CrossbowItem$ChargeTypeExtend.LEASH_ROPE_ARROW, itemmodel$unbaked6)
)
)
);
}
}

View File

@ -0,0 +1,22 @@
package com.r3944realms.leashedplayer.datagen.provider;
import com.r3944realms.leashedplayer.datagen.generator.ModAdvancementGenerator;
import net.minecraft.core.HolderLookup;
import net.minecraft.data.PackOutput;
import net.minecraft.data.advancements.AdvancementProvider;
import java.util.List;
import java.util.concurrent.CompletableFuture;
public class ModAdvancementProvider extends AdvancementProvider {
/**
* Constructs an advancement provider using the generators to write the
* advancements to a file.
*
* @param output the target directory of the data generator
* @param registries a future of a lookup for registries and their objects
*/
public ModAdvancementProvider(PackOutput output, CompletableFuture<HolderLookup.Provider> registries) {
super(output, registries, List.of(new ModAdvancementGenerator()));
}
}

View File

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

View File

@ -0,0 +1,23 @@
package com.r3944realms.leashedplayer.datagen.provider;
import com.r3944realms.leashedplayer.LeashedPlayer;
import com.r3944realms.leashedplayer.datagen.provider.attributes.ModJukeboxSongs;
import com.r3944realms.leashedplayer.datagen.provider.attributes.ModPaintingVariants;
import net.minecraft.core.HolderLookup;
import net.minecraft.core.RegistrySetBuilder;
import net.minecraft.core.registries.Registries;
import net.minecraft.data.PackOutput;
import net.neoforged.neoforge.common.data.DatapackBuiltinEntriesProvider;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
public class ModDataPackBuiltInEntriesProvider extends DatapackBuiltinEntriesProvider {
public static final RegistrySetBuilder BUILDER = new RegistrySetBuilder()
.add(Registries.PAINTING_VARIANT, ModPaintingVariants::bootstrap)
.add(Registries.JUKEBOX_SONG, ModJukeboxSongs::bootstrap);
public ModDataPackBuiltInEntriesProvider(PackOutput output, CompletableFuture<HolderLookup.Provider> registries) {
super(output, registries, BUILDER, Set.of(LeashedPlayer.MOD_ID));
CompletableFuture<HolderLookup.Provider> registryProvider = getRegistryProvider();
}
}

View File

@ -0,0 +1,43 @@
package com.r3944realms.leashedplayer.datagen.provider;
import com.r3944realms.leashedplayer.LeashedPlayer;
import com.r3944realms.leashedplayer.content.items.ModItemRegister;
import com.r3944realms.leashedplayer.datagen.LanguageAndOtherData.ModItemTags;
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.item.Items;
import net.minecraft.world.level.block.Block;
import net.neoforged.neoforge.common.Tags;
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) {
super(pOutput, pLookupProvider, pBlockTags, LeashedPlayer.MOD_ID);
}
@Override
protected void addTags(HolderLookup.@NotNull Provider pProvider) {
this.tag(ItemTags.ARROWS)
.add(ModItemRegister.LEASH_ROPE_ARROW.get())
.add(ModItemRegister.SPECTRAL_LEASH_ROPE_ARROW.get())
.add(ModItemRegister.TIPPED_LEASH_ROPE_ARROW.get());
this.tag(ModItemTags.AMETHYST_TOOL_MATERIALS)
.add(Items.AMETHYST_SHARD);
this.tag(ItemTags.HEAD_ARMOR)
.add(ModItemRegister.NEOFORGE.get())
.add(Items.LEAD);
this.tag(ItemTags.MINING_ENCHANTABLE)
.add(ModItemRegister.AMETHYST_SHEARS.get());
this.tag(ItemTags.VANISHING_ENCHANTABLE)
.add(ModItemRegister.AMETHYST_SHEARS.get());
this.tag(ItemTags.DURABILITY_ENCHANTABLE)
.add(ModItemRegister.AMETHYST_SHEARS.get());
this.tag(Tags.Items.TOOLS_SHEAR)
.add(ModItemRegister.AMETHYST_SHEARS.get());
}
}

View File

@ -0,0 +1,40 @@
package com.r3944realms.leashedplayer.datagen.provider;
import com.r3944realms.leashedplayer.datagen.LanguageAndOtherData.ModLangKeyValue;
import com.r3944realms.leashedplayer.utils.Enum.LanguageEnum;
import net.minecraft.data.PackOutput;
import net.neoforged.neoforge.common.data.LanguageProvider;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import static com.r3944realms.leashedplayer.datagen.LanguageAndOtherData.ModLangKeyValue.getLan;
public class ModLanguageProvider extends LanguageProvider {
private final LanguageEnum Language;
private final Map<String, String> LanKeyMap;
private static final List<String> objects = new ArrayList<>();
public ModLanguageProvider(PackOutput output, String modId, LanguageEnum Lan) {
super(output, modId, Lan.local);
this.Language = Lan;
LanKeyMap = new HashMap<>();
init();
}
private void init() {
for (ModLangKeyValue key : ModLangKeyValue.values()) {
addLang(key.getKey(), getLan(Language, key));
}
}
private void addLang(String Key, String value) {
if(!objects.contains(Key)) objects.add(Key);
LanKeyMap.put(Key, value);
}
@Override
protected void addTranslations() {
objects.forEach(key -> add(key,LanKeyMap.get(key)));
}
}

View File

@ -0,0 +1,189 @@
package com.r3944realms.leashedplayer.datagen.provider;
import com.google.common.collect.Maps;
import com.google.gson.JsonElement;
import com.r3944realms.leashedplayer.datagen.generator.ModBlockModelGenerator;
import com.r3944realms.leashedplayer.datagen.generator.ModItemModelGenerator;
import net.minecraft.client.data.models.ItemModelOutput;
import net.minecraft.client.data.models.ModelProvider;
import net.minecraft.client.data.models.blockstates.BlockModelDefinitionGenerator;
import net.minecraft.client.data.models.model.ItemModelUtils;
import net.minecraft.client.data.models.model.ModelInstance;
import net.minecraft.client.data.models.model.ModelLocationUtils;
import net.minecraft.client.renderer.block.model.BlockModelDefinition;
import net.minecraft.client.renderer.item.ClientItem;
import net.minecraft.client.renderer.item.ItemModel;
import net.minecraft.core.Holder;
import net.minecraft.core.registries.BuiltInRegistries;
import net.minecraft.data.CachedOutput;
import net.minecraft.data.DataProvider;
import net.minecraft.data.PackOutput;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.item.BlockItem;
import net.minecraft.world.item.Item;
import net.minecraft.world.level.block.Block;
import net.neoforged.api.distmarker.Dist;
import net.neoforged.api.distmarker.OnlyIn;
import org.jetbrains.annotations.NotNull;
import java.nio.file.Path;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.CompletableFuture;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Stream;
/**
* 借鉴于 @author baguchi
* */
public class ModModelDataProvider extends ModelProvider {
private final PackOutput.PathProvider blockStatePathProvider;
private final PackOutput.PathProvider itemInfoPathProvider;
private final PackOutput.PathProvider modelPathProvider;
public ModModelDataProvider(PackOutput packOutput, String modId) {
super(packOutput, modId);
this.blockStatePathProvider = packOutput.createPathProvider(PackOutput.Target.RESOURCE_PACK, "blockstates");
this.itemInfoPathProvider = packOutput.createPathProvider(PackOutput.Target.RESOURCE_PACK, "items");
this.modelPathProvider = packOutput.createPathProvider(PackOutput.Target.RESOURCE_PACK, "models");
}
@Override
public @NotNull CompletableFuture<?> run(@NotNull CachedOutput cachedOutput) {
ItemInfoCollector itemModelOutput = new ItemInfoCollector(this::getKnownItems);
BlockStateGeneratorCollector blockModelOutput = new BlockStateGeneratorCollector(this::getKnownBlocks);
SimpleModelCollector modelOutput = new SimpleModelCollector();
this.registerModels(new ModBlockModelGenerator(blockModelOutput, itemModelOutput, modelOutput), new ModItemModelGenerator(itemModelOutput, modelOutput));
blockModelOutput.validate();
itemModelOutput.finalizeAndValidate();
return CompletableFuture.allOf(blockModelOutput.save(cachedOutput, this.blockStatePathProvider), modelOutput.save(cachedOutput, this.modelPathProvider), itemModelOutput.save(cachedOutput, this.itemInfoPathProvider));
}
@OnlyIn(Dist.CLIENT)
static class ItemInfoCollector implements ItemModelOutput {
private final Map<Item, ClientItem> itemInfos;
private final Map<Item, Item> copies;
private final Supplier<Stream<? extends Holder<Item>>> knownItems;
public ItemInfoCollector(Supplier<Stream<? extends Holder<Item>>> knownItems) {
this.itemInfos = new HashMap<>();
this.copies = new HashMap<>();
this.knownItems = knownItems;
}
public void accept(@NotNull Item item, ItemModel.@NotNull Unbaked model) {
this.register(item, new ClientItem(model, ClientItem.Properties.DEFAULT));
}
public void register(@NotNull Item item, @NotNull ClientItem clientItem) {
ClientItem clientitem = this.itemInfos.put(item, clientItem);
if (clientitem != null) {
throw new IllegalStateException("Duplicate item model definition for " + item);
}
}
public void copy(@NotNull Item item, @NotNull Item copyItem) {
this.copies.put(copyItem, item);
}
public void finalizeAndValidate() {
(this.knownItems.get()).map(Holder::value).forEach((item) -> {
if (!this.copies.containsKey(item) && item instanceof BlockItem blockitem) {
if (!this.itemInfos.containsKey(blockitem)) {
ResourceLocation resourcelocation = ModelLocationUtils.getModelLocation(blockitem.getBlock());
this.accept(blockitem, ItemModelUtils.plainModel(resourcelocation));
}
}
});
this.copies.forEach((item, item1) -> {
ClientItem clientitem = this.itemInfos.get(item1);
if (clientitem == null) {
String item1Name = String.valueOf(item1);
throw new IllegalStateException("Missing donor: " + item1Name + " -> " + item);
} else {
this.register(item, clientitem);
}
});
List<ResourceLocation> list = (this.knownItems.get()).filter((holder) -> !this.itemInfos.containsKey(holder.value())).map((holder) -> holder.unwrapKey().orElseThrow().location()).toList();
if (!list.isEmpty()) {
LOGGER.warn("Missing item model definitions for: {}", list);
}
}
public CompletableFuture<?> save(CachedOutput cachedOutput, PackOutput.PathProvider pathProvider) {
return DataProvider.saveAll(cachedOutput, ClientItem.CODEC, (item) -> pathProvider.json(item.builtInRegistryHolder().key().location()), this.itemInfos);
}
}
@OnlyIn(Dist.CLIENT)
static class SimpleModelCollector implements BiConsumer<ResourceLocation, ModelInstance> {
private final Map<ResourceLocation, ModelInstance> models = new HashMap<>();
SimpleModelCollector() {
}
public void accept(ResourceLocation resourceLocation, ModelInstance modelInstance) {
Supplier<JsonElement> supplier = this.models.put(resourceLocation, modelInstance);
if (supplier != null) {
throw new IllegalStateException("Duplicate model definition for " + resourceLocation);
}
}
public CompletableFuture<?> save(CachedOutput cachedOutput, PackOutput.PathProvider pathProvider) {
Objects.requireNonNull(pathProvider);
return saveAll(cachedOutput, pathProvider::json, this.models);
}
static <T> CompletableFuture<?> saveAll(CachedOutput cachedOutput, Function<T, Path> tPathFunction, Map<T, ? extends Supplier<JsonElement>> tMap) {
return DataProvider.saveAll(cachedOutput, Supplier::get, tPathFunction, tMap);
}
}
@OnlyIn(Dist.CLIENT)
static class BlockStateGeneratorCollector implements Consumer<BlockModelDefinitionGenerator> {
private final Map<Block, BlockModelDefinitionGenerator> generators = new HashMap<>();
private final Supplier<Stream<? extends Holder<Block>>> knownBlocks;
public BlockStateGeneratorCollector(Supplier<Stream<? extends Holder<Block>>> knownBlocks) {
this.knownBlocks = knownBlocks;
}
@Deprecated // Neo: Provided for vanilla/multi-loader compatibility. Use constructor with Supplier parameter.
public BlockStateGeneratorCollector() {
this(BuiltInRegistries.BLOCK::listElements);
}
public void accept(BlockModelDefinitionGenerator p_405192_) {
Block block = p_405192_.block();
BlockModelDefinitionGenerator blockmodeldefinitiongenerator = this.generators.put(block, p_405192_);
if (blockmodeldefinitiongenerator != null) {
throw new IllegalStateException("Duplicate blockstate definition for " + block);
}
}
public void validate() {
Stream<? extends Holder<Block>> stream = knownBlocks.get();
List<ResourceLocation> list = stream.filter(p_386843_ -> !this.generators.containsKey(p_386843_.value()))
.map(p_386823_ -> p_386823_.unwrapKey().orElseThrow().location())
.toList();
if (!list.isEmpty()) {
throw new IllegalStateException("Missing blockstate definitions for: " + list);
}
}
public CompletableFuture<?> save(CachedOutput output, PackOutput.PathProvider pathProvider) {
Map<Block, BlockModelDefinition> map = Maps.transformValues(this.generators, BlockModelDefinitionGenerator::create);
Function<Block, Path> function = p_387598_ -> pathProvider.json(p_387598_.builtInRegistryHolder().key().location());
return DataProvider.saveAll(output, BlockModelDefinition.CODEC, function, map);
}
}
}

View File

@ -0,0 +1,23 @@
package com.r3944realms.leashedplayer.datagen.provider;
import com.r3944realms.leashedplayer.datagen.provider.attributes.ModPaintingVariants;
import net.minecraft.core.HolderLookup;
import net.minecraft.data.PackOutput;
import net.minecraft.data.tags.PaintingVariantTagsProvider;
import net.minecraft.tags.PaintingVariantTags;
import org.jetbrains.annotations.NotNull;
import java.util.concurrent.CompletableFuture;
public class ModPaintingVariantTagsProvider extends PaintingVariantTagsProvider {
public ModPaintingVariantTagsProvider(PackOutput pOutput, CompletableFuture<HolderLookup.Provider> pProvider, String modId) {
super(pOutput, pProvider, modId);
}
@Override
protected void addTags(HolderLookup.@NotNull Provider pProvider) {
this.tag(PaintingVariantTags.PLACEABLE)
.add(ModPaintingVariants.GROUP_PHOTO);
}
}

View File

@ -0,0 +1,72 @@
package com.r3944realms.leashedplayer.datagen.provider;
import com.r3944realms.leashedplayer.content.items.ModItemRegister;
import com.r3944realms.leashedplayer.content.items.repcipe.TippedLeashRopeArrowRecipe;
import net.minecraft.core.HolderLookup;
import net.minecraft.data.PackOutput;
import net.minecraft.data.recipes.RecipeCategory;
import net.minecraft.data.recipes.RecipeOutput;
import net.minecraft.data.recipes.RecipeProvider;
import net.minecraft.data.recipes.SpecialRecipeBuilder;
import net.minecraft.world.item.Items;
import org.jetbrains.annotations.NotNull;
import java.util.concurrent.CompletableFuture;
public class ModRecipeProvider extends RecipeProvider {
public ModRecipeProvider(HolderLookup.Provider registries, RecipeOutput output) {
super(registries, output);
}
@Override
protected void buildRecipes() {
this.shapeless(RecipeCategory.MISC, ModItemRegister.LEASH_ROPE_ARROW.get(), 1)
.requires(Items.LEAD)
.requires(Items.ARROW)
.unlockedBy("has_lead",has(Items.LEAD))
.save(this.output);
this.shapeless(RecipeCategory.MISC, ModItemRegister.SPECTRAL_LEASH_ROPE_ARROW.get(),1)
.requires(Items.LEAD)
.requires(Items.SPECTRAL_ARROW)
.unlockedBy("has_lead",has(Items.LEAD))
.unlockedBy("has_spectral_arrow",has(Items.SPECTRAL_ARROW))
.save(this.output);
this.shaped(RecipeCategory.MISC, ModItemRegister.SPECTRAL_LEASH_ROPE_ARROW.get(),1)
.pattern(" $ ")
.pattern("$#$")
.pattern(" $ ")
.define('#', ModItemRegister.LEASH_ROPE_ARROW.get())
.define('$', Items.GLOWSTONE_DUST)
.unlockedBy("has_lead",has(Items.LEAD))
.unlockedBy("has_glowstone_dust",has(Items.GLOWSTONE_DUST))
.save(this.output,"leash_rope_arrow_shape");
this.shaped(RecipeCategory.MISC, ModItemRegister.AMETHYST_SHEARS.get(),1)
.pattern("#%")
.pattern("%#")
.define('#', Items.AMETHYST_SHARD)
.define('%', Items.STICK)
.unlockedBy("has_amethyst_shard",has(Items.AMETHYST_SHARD))
.save(this.output);
SpecialRecipeBuilder.special(TippedLeashRopeArrowRecipe.TippedLeashRopeArrowARecipe::new).save(this.output, "tipped_leash_rope_arrow_a");
SpecialRecipeBuilder.special(TippedLeashRopeArrowRecipe.TippedLeashRopeArrowBRecipe::new).save(this.output, "tipped_leash_rope_arrow_b");
}
public static class Runner extends RecipeProvider.Runner {
public Runner(PackOutput pPackOutput, CompletableFuture<HolderLookup.Provider> providerCompletableFuture) {
super(pPackOutput, providerCompletableFuture);
}
@Override
@NotNull
public RecipeProvider createRecipeProvider(HolderLookup.@NotNull Provider provider, @NotNull RecipeOutput output) {
return new ModRecipeProvider(provider, output);
}
@Override
public @NotNull String getName() {
return "LeashedPlayer Recipes";
}
}
}

View File

@ -0,0 +1,35 @@
package com.r3944realms.leashedplayer.datagen.provider;
import com.r3944realms.leashedplayer.content.sounds.ModSoundRegister;
import net.minecraft.data.PackOutput;
import net.neoforged.neoforge.common.data.SoundDefinition;
import net.neoforged.neoforge.common.data.SoundDefinitionsProvider;
public class ModSoundDefinitionsProvider extends SoundDefinitionsProvider {
/**
* Creates a new instance of this data provider.
*
* @param output The {@linkplain PackOutput} instance provided by the data generator.
* @param modId The mod ID of the current mod.
*/
public ModSoundDefinitionsProvider(PackOutput output, String modId) {
super(output, modId);
}
public SoundDefinition getSoundDefinition(String subTitle, SoundDefinition.Sound... sounds) {
return SoundDefinition.definition().subtitle(subTitle).with(sounds);
}
@Override
public void registerSounds() {
add(
ModSoundRegister.FOX_MUSIC,
getSoundDefinition(
ModSoundRegister.getSubTitleTranslateKey("what_does_the_fox_say"),
sound(ModSoundRegister.RL_FOX_MUSIC, SoundDefinition.SoundType.SOUND)
)
);
}
}

View File

@ -0,0 +1,39 @@
package com.r3944realms.leashedplayer.datagen.provider.attributes;
import com.r3944realms.leashedplayer.LeashedPlayer;
import com.r3944realms.leashedplayer.content.sounds.ModSoundRegister;
import net.minecraft.Util;
import net.minecraft.core.Holder;
import net.minecraft.core.registries.Registries;
import net.minecraft.data.worldgen.BootstrapContext;
import net.minecraft.network.chat.Component;
import net.minecraft.resources.ResourceKey;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.sounds.SoundEvent;
import net.minecraft.world.item.JukeboxSong;
public class ModJukeboxSongs {
public static ResourceKey<JukeboxSong> FOX_MUSIC = create("what_does_the_fox_say");
public static void bootstrap(BootstrapContext<JukeboxSong> pContext) {
JukeboxSongBootstrap(pContext);
}
public static void JukeboxSongBootstrap(BootstrapContext<JukeboxSong> pContext) {
register(pContext, FOX_MUSIC, ModSoundRegister.FOX_MUSIC, 121, 15);
}
private static void register(
BootstrapContext<JukeboxSong> pContext, ResourceKey<JukeboxSong> pKey, Holder<SoundEvent> pSoundEvent, int pLengthInSeconds, int pComparatorOutput
) {
pContext.register(
pKey,
new JukeboxSong(pSoundEvent, Component.translatable(Util.makeDescriptionId("jukebox_song", pKey.location())), (float)pLengthInSeconds, pComparatorOutput)
);
}
private static ResourceKey<JukeboxSong> create(String pName) {
return ResourceKey.create(Registries.JUKEBOX_SONG, ResourceLocation.fromNamespaceAndPath(LeashedPlayer.MOD_ID, pName));
}
}

View File

@ -0,0 +1,45 @@
package com.r3944realms.leashedplayer.datagen.provider.attributes;
import com.r3944realms.leashedplayer.LeashedPlayer;
import net.minecraft.core.registries.Registries;
import net.minecraft.data.worldgen.BootstrapContext;
import net.minecraft.network.chat.Component;
import net.minecraft.resources.ResourceKey;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.entity.decoration.PaintingVariant;
import java.util.Optional;
public class ModPaintingVariants {
public static final ResourceKey<PaintingVariant> GROUP_PHOTO = create("group_photo");
public static void bootstrap(BootstrapContext<PaintingVariant> pContext) {
PaintingVariantBootstrap(pContext);
}
public static void PaintingVariantBootstrap(BootstrapContext<PaintingVariant> pContext) {
ModPaintingVariants.register(pContext, ModPaintingVariants.GROUP_PHOTO, 4, 3, Component.translatable(ModPaintingVariants.getPaintingVariantTitleKey(ModPaintingVariants.GROUP_PHOTO)), Component.translatable(ModPaintingVariants.getPaintingVariantAuthorKey(ModPaintingVariants.GROUP_PHOTO)));
}
private static void register(BootstrapContext<PaintingVariant> pContext, ResourceKey<PaintingVariant> pKey, int pWidth, int pHeight, Component title, Component author) {
pContext.register(pKey, new PaintingVariant(pWidth, pHeight, pKey.location(), Optional.of(title), Optional.of(author)));
}
private static ResourceKey<PaintingVariant> create(String pName) {
return ResourceKey.create(Registries.PAINTING_VARIANT, ResourceLocation.fromNamespaceAndPath(LeashedPlayer.MOD_ID, pName));
}
public static String getPaintingVariantTitleKey(ResourceKey<PaintingVariant> pKey) {
return "painting." + pKey.location().getNamespace() + "." + pKey.location().getPath() + ".title";
}
public static String getPaintingVariantAuthorKey(ResourceKey<PaintingVariant> pKey) {
return "painting." + pKey.location().getNamespace() + "." + pKey.location().getPath() + ".author";
}
public static String getPaintingVariantTitleKey(String pName) {
return "painting." + LeashedPlayer.MOD_ID + "." + pName + ".title";
}
public static String getPaintingVariantAuthorKey(String pName) {
return "painting." + LeashedPlayer.MOD_ID + "." + pName + ".author";
}
}

View File

@ -0,0 +1,25 @@
package com.r3944realms.leashedplayer.extend;
import com.mojang.serialization.Codec;
import net.minecraft.util.StringRepresentable;
import org.jetbrains.annotations.NotNull;
public enum CrossbowItem$ChargeTypeExtend implements StringRepresentable {
NONE("none"),
ARROW("arrow"),
ROCKET("rocket"),
LEASH_ROPE_ARROW("leash_rope_arrow"),
;
public static final Codec<CrossbowItem$ChargeTypeExtend> CODEC = StringRepresentable.fromEnum(CrossbowItem$ChargeTypeExtend::values);
private final String name;
CrossbowItem$ChargeTypeExtend(String name) {
this.name = name;
}
@Override
public @NotNull String getSerializedName() {
return this.name;
}
}

View File

@ -0,0 +1,53 @@
package com.r3944realms.leashedplayer.mixin;
import org.objectweb.asm.tree.ClassNode;
import org.spongepowered.asm.mixin.extensibility.IMixinConfigPlugin;
import org.spongepowered.asm.mixin.extensibility.IMixinInfo;
import java.util.List;
import java.util.Set;
public class MixinPlugin implements IMixinConfigPlugin {
@Override
public void onLoad(String mixinPackage) {
}
@Override
public String getRefMapperConfig() {
return null;
}
@Override
public boolean shouldApplyMixin(String targetClassName, String mixinClassName) {
// 检查当前是否在数据生成阶段
// 根据需要添加逻辑来控制特定Mixin类的应用
boolean isRegister = mixinClassName.split("com\\.r3944realms\\.leashedplayer\\.mixin\\.")[1].split("\\.")[0].equals("registry");
if (isRegister) {
return isDataGeneration();
}
return true; // 其他Mixin类默认应用
}
private boolean isDataGeneration() {
// 使用你的逻辑来检查是否在数据生成阶段
String environment = System.getProperty("gradle.task");
return "runData".equals(environment);
}
@Override
public void acceptTargets(Set<String> myTargets, Set<String> otherTargets) {
}
@Override
public List<String> getMixins() {
return null;
}
@Override
public void preApply(String targetClassName, ClassNode targetClass, String mixinClassName, IMixinInfo mixinInfo) {
}
@Override
public void postApply(String targetClassName, ClassNode targetClass, String mixinClassName, IMixinInfo mixinInfo) {
}
}

View File

@ -0,0 +1,37 @@
package com.r3944realms.leashedplayer.mixin.both;
import com.r3944realms.leashedplayer.modInterface.PlayerLeashable;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.Leashable;
import net.minecraft.world.level.Level;
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 abstract class MixinEntity {
@Shadow public abstract void igniteForSeconds(float pSeconds);
@Shadow public abstract Level level();
/**
* 这里重定向当实体类实现了{@link PlayerLeashable}接口时<br/>
* 阻止原版的{@link Leashable} 的tickLeash方法调用将其<br/>
* 我们需自己实现相关的逻辑
* @param entity 实体
* @param <E> 实体类型
*/
@Redirect(
method = "baseTick",
at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/Leashable;tickLeash(Lnet/minecraft/server/level/ServerLevel;Lnet/minecraft/world/entity/Entity;)V")
)
<E extends Entity & Leashable> void checkAndCancelIfTure(ServerLevel f, E entity) {
if(!(entity instanceof PlayerLeashable) && level() instanceof ServerLevel serverLevel_) {
Leashable.tickLeash(serverLevel_, entity);
}
}
}

View File

@ -0,0 +1,48 @@
package com.r3944realms.leashedplayer.mixin.both;
import net.minecraft.util.Mth;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.EntityType;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.level.Level;
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.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
//OpenJDK21 JIT 激进的优化将TickHeadTurnAssign优化成死循环了
/**
* 保证 while 循环的条件永远不成立的
* @author ustc-zzzz
*/
@Mixin(LivingEntity.class)
public abstract class MixinLivingEntity extends Entity {
@Shadow
public float yBodyRot;
@Shadow
public float yBodyRotO;
@Shadow
public float yHeadRot;
@Shadow
public float yHeadRotO;
public MixinLivingEntity(EntityType<?> type, Level level) {
super(type, level);
}
@Inject(
method = "tick()V",
at = @At(value = "INVOKE_ASSIGN", target = "Lnet/minecraft/world/entity/LivingEntity;tickHeadTurn(F)V")
)
public void onTickHeadTurnAssign(CallbackInfo ignored) {
var yRot = this.getYRot();
this.yRotO = yRot - Mth.wrapDegrees(yRot - this.yRotO);
this.yBodyRotO = this.yBodyRot - Mth.wrapDegrees(this.yBodyRot - this.yBodyRotO);
var xRot = this.getXRot();
this.xRotO = xRot - Mth.wrapDegrees(xRot - this.xRotO);
this.yHeadRotO = this.yHeadRot - Mth.wrapDegrees(this.yHeadRot - this.yHeadRotO);
}
}

View File

@ -0,0 +1,326 @@
package com.r3944realms.leashedplayer.mixin.both;
import com.r3944realms.leashedplayer.LeashedPlayer;
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 com.r3944realms.leashedplayer.utils.Logger;
import net.minecraft.nbt.CompoundTag;
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.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;
import net.minecraft.world.phys.Vec3;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At;
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, ILivingEntityExtension {
@Unique
protected int Pl$LeashKeepTick;//保存状态当超过断裂绳长时若LeashKeepTick大于0则不断裂
@Unique
@Nullable
private LeashData Pl$LeashData;//Data
@SuppressWarnings("WrongEntityDataParameterClass")
@Unique//客户端与服务器端的实体同步数据
private static final EntityDataAccessor<CompoundTag> Pl$LEASH_DATA = SynchedEntityData.defineId(Player.class, EntityDataSerializers.COMPOUND_TAG);
@Unique
@SuppressWarnings("WrongEntityDataParameterClass")
private static final EntityDataAccessor<Float> DATA_ENTITY_LEASH_LENGTH = SynchedEntityData.defineId(Player.class, EntityDataSerializers.FLOAT);
@SuppressWarnings("AddedMixinMembersNamePattern")
@Override
public float getLeashLength() {
return this.entityData.get(DATA_ENTITY_LEASH_LENGTH);
}
@SuppressWarnings("AddedMixinMembersNamePattern")
@Override
public void setLeashLength(float length) {
this.entityData.set(DATA_ENTITY_LEASH_LENGTH, length);
}
@SuppressWarnings("AddedMixinMembersNamePattern")
@Override
public void setKeepLeashTick(int keepTick) {
this.Pl$LeashKeepTick = Math.max(keepTick, 0);
}
@SuppressWarnings("AddedMixinMembersNamePattern")
@Override
public int getKeepLeashTick() {
return this.Pl$LeashKeepTick;
}
protected MixinPlayer(EntityType<? extends LivingEntity> pEntityType, Level pLevel) {
super(pEntityType, pLevel);
}
@Inject(method = {"tick"}, at = {@At("HEAD")})
private void tickForLeash(CallbackInfo ci) {
if(!this.level().isClientSide) {
Pl$tickLeash();//服务器端每tick任务
}
PlayerLeashable playerLeashable = this;
Entity leashHolder = playerLeashable.getLeashHolder();
if(leashHolder != null ) {
//存在则更新
Pl$UpdateLeash(leashHolder, (Entity) playerLeashable);
try {
Objects.requireNonNull(this.getAttribute(Attributes.STEP_HEIGHT)).setBaseValue(1.25f);
} catch (Exception e) {
Logger.logger.error(e.getMessage());
}
} else {
try {
Objects.requireNonNull(this.getAttribute(Attributes.STEP_HEIGHT)).setBaseValue(0.6f);
} catch (Exception e) {
Logger.logger.error(e.getMessage());
}
}
}
@Unique
private static void Pl$UpdateLeash(Entity holderEntity, Entity restrainedEntity) {
if (holderEntity == null || holderEntity.level() != restrainedEntity.level()) {
return;
}
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 刹车的实体
* @param pMaxX X方向的最大动量
* @param pMaxY Y方向的最大动量
* @param pMaxZ Z方向的最大动量
*/
@Unique
private static void Whimsy$Brake(Entity pEntity, double pMaxX, double pMaxY, double pMaxZ) {
Vec3 deltaMovement = pEntity.getDeltaMovement();
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;
}
/**
* 刹车
* @param pEntity 刹车的实体
* @param pOpt 自定义规则
*/
@Unique
private static void Whimsy$Brake(Entity pEntity, @Nullable Consumer<Entity> pOpt) {
Consumer<Entity> consumer = pOpt;
if(pOpt == null) {
consumer = entity -> {
Vec3 deltaMovement = entity.getDeltaMovement();
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;
};
}
consumer.accept(pEntity);
}
@Unique
protected void Pl$tickLeash() {
if(this.Pl$LeashData == null) return;//没有Data直接退出
ILivingEntityExtension self = this;
int keepLeashTick = self.getKeepLeashTick();
//info -> Holder整理
Pl$RestoreLeashFormSave();
//默认值设为
float leashLength = LeashCommand.MIN_VALUE;
Entity entity = this.Pl$LeashData.leashHolder;
//保存数据
saveLeashData(Pl$LeashData);
ILivingEntityExtension iEntityExtension = this;//获取设定值
float leashLengthSelf = iEntityExtension.getLeashLength();
leashLength = leashLengthSelf > LeashCommand.MIN_VALUE ? leashLengthSelf : LeashCommand.MIN_VALUE;
if (entity != null) {
double breakDistanceTime = (entity instanceof LeashRopeArrow) ? LeashedPlayer.M1() * LeashedPlayer.M2() : LeashedPlayer.M1();
if(!isAlive() || !entity.isAlive() ||( distanceTo(entity) > Math.max(leashLength * breakDistanceTime, LeashCommand.MIN_VALUE * breakDistanceTime) && keepLeashTick == 0)){
//玩家死亡 持有者不存在 距离大于设定值的 breakDistanceTime 倍且keepTick <=0长度的 breakDistanceTime 倍若低于 LeashCommand.MIN_VALUE 则选 LeashCommand.MIN_VALUE
// 则取消拴绳关系并掉落拴绳
boolean shouldDrop = !(entity instanceof LeashRopeArrow);
Leashable.dropLeash((Entity & Leashable) this, true, shouldDrop);
} else if(distanceTo(entity) > leashLength * 0.65f * breakDistanceTime && entity.onGround()) {
//大于eashLength * 0.65f * breakDistanceTime 倍绳长且在地面则会让其跳跃<1.25格阻拦情况下跳跃阻拦//TODO:待擴展
Entity applyMovementEntity = this.isPassenger() ? this.getVehicle() : this;
if(applyMovementEntity instanceof LivingEntity applyMovementLivingEntity) {
applyMovementLivingEntity.jumpFromGround();
}
}
}
if(keepLeashTick > 0) {//keepTick--
self.setKeepLeashTick(keepLeashTick - 1);
}
}
@Override
public Entity getLeashHolder() {
if (Pl$LeashData == null) return null;
if (Pl$LeashData.leashHolder == null && Pl$LeashData.delayedLeashHolderId != 0 ) {
Pl$LeashData.leashHolder = this.level().getEntity(Pl$LeashData.delayedLeashHolderId);
}
return Pl$LeashData.leashHolder;
}
/**
* 数据整理 -> 如果Pl$LeashData非null最终Pl$LeashData的leashHolder将不为null
*/
@Unique
private void Pl$RestoreLeashFormSave() {
assert this.Pl$LeashData != null;
if(!(this.level() instanceof ServerLevel)) {
//非服务器端退出
return;
}
if(this.Pl$LeashData.delayedLeashInfo == null) {
//delayedLeashInfo无数据
if(Pl$LeashData.leashHolder != null) {//且LeashHolder不为null则直接用它
setLeashedTo(Pl$LeashData.leashHolder, true);
return;
}
return;
}
if(this.Pl$LeashData.delayedLeashInfo.left().isPresent()) {
//如果有实体的UUID一般是LivingEntity则在服务器其通过UUID来查找实体
Entity entity = ((ServerLevel) this.level()).getEntity(this.Pl$LeashData.delayedLeashInfo.left().get());
if(entity != null) {
setLeashedTo(entity, true);
}
} else if(this.Pl$LeashData.delayedLeashInfo.right().isPresent()) {
//如果有实体的坐标一般就是拴绳结在服务器端获取拴绳结实体通过给定坐标和维度获取
setLeashedTo(LeashFenceKnotEntity.getOrCreateKnot(this.level(), this.Pl$LeashData.delayedLeashInfo.right().get()), true);
}
}
@org.jetbrains.annotations.Nullable
@Override
public LeashData getLeashData() {
return Pl$LeashData;
}
@SuppressWarnings("AddedMixinMembersNamePattern")
@Override
@Nullable
public LeashData getLeashDataFromEntityData() {
CompoundTag compoundTag = this.entityData.get(Pl$LEASH_DATA);
return compoundTag.read("Pl$LeashData", Leashable.LeashData.CODEC).orElse(null);
}
@Override
public void setLeashData(@org.jetbrains.annotations.Nullable Leashable.LeashData pLeashData) {
this.Pl$LeashData = pLeashData;
saveLeashData(pLeashData);
}
@SuppressWarnings("AddedMixinMembersNamePattern")
@Unique
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")
@Override
public boolean canBeLeashedInstantly(Player player) {
return !isLeashed();
}
@Inject(
method = {"defineSynchedData"}, at = {@At("TAIL")}
)
//定义Client/Server player 同步数据
private void defineSyncData (SynchedEntityData.Builder pBuilder, CallbackInfo ci) {
CompoundTag leashCompoundTag = new CompoundTag();
this.writeLeashData(leashCompoundTag, null);
pBuilder.define(Pl$LEASH_DATA, leashCompoundTag);
pBuilder.define(DATA_ENTITY_LEASH_LENGTH, 5.0F);
}
@Inject(
method = {"addAdditionalSaveData"}, at = {@At("RETURN")}
)//数据保存
private void addSaveData(CompoundTag pCompound, CallbackInfo ci) {
CompoundTag pLeashTag = new CompoundTag();
writeLeashData(pLeashTag, Pl$LeashData);
pCompound.put("Pl$LeashData", pLeashTag);
this.entityData.set(Pl$LEASH_DATA, pLeashTag);
pCompound.putFloat("LeashLength", getLeashLength());
}
@Inject(
method = {"readAdditionalSaveData"}, at = {@At("RETURN")}
)//数据读取
private void readSaveData(CompoundTag pCompound, CallbackInfo ci) {
Leashable.LeashData leashable$leashdata = pCompound.read("Pl$LeashData", Leashable.LeashData.CODEC).orElse(null);
if (this.getLeashData() != null && leashable$leashdata == null) {
this.removeLeash();
}
this.setLeashData(leashable$leashdata);
}
}

View File

@ -0,0 +1,20 @@
package com.r3944realms.leashedplayer.mixin.client;
import com.r3944realms.leashedplayer.client.renderer.item.properties.conditionalRange.ConditionalRangeItemModelProperties;
import net.minecraft.client.ClientBootstrap;
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.callback.CallbackInfo;
@Mixin(ClientBootstrap.class)
public class MixinClientBootstrap {
@Inject(method = "bootstrap", at = @At(
value = "INVOKE",
target = "Lnet/minecraft/client/renderer/item/properties/numeric/RangeSelectItemModelProperties;bootstrap()V",
shift = At.Shift.AFTER)
)
private static void bootstrap(CallbackInfo ci) {
ConditionalRangeItemModelProperties.bootstrap();
}
}

View File

@ -0,0 +1,45 @@
package com.r3944realms.leashedplayer.mixin.client;
import com.mojang.blaze3d.resource.GraphicsResourceAllocator;
import com.mojang.blaze3d.vertex.PoseStack;
import com.r3944realms.leashedplayer.client.renderer.LeashRendererUtil;
import net.minecraft.client.Camera;
import net.minecraft.client.DeltaTracker;
import net.minecraft.client.multiplayer.ClientLevel;
import net.minecraft.client.renderer.GameRenderer;
import net.minecraft.client.renderer.LevelRenderer;
import net.minecraft.client.renderer.MultiBufferSource;
import net.minecraft.client.renderer.RenderBuffers;
import org.joml.Matrix4f;
import org.spongepowered.asm.mixin.Final;
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.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import javax.annotation.Nullable;
@Mixin(LevelRenderer.class)
public abstract class MixinLevelRenderer {
@Shadow
@Nullable
private ClientLevel level;
@Shadow @Final private RenderBuffers renderBuffers;
@Inject(
method = {"renderLevel"},
at = @At(value = "HEAD")
)
private void renderLevel(GraphicsResourceAllocator graphicsResourceAllocator, DeltaTracker deltaTracker, boolean renderBlockOutline, Camera camera, GameRenderer gameRenderer, Matrix4f frustumMatrix, Matrix4f projectionMatrix, CallbackInfo ci) {
assert this.level != null;
PoseStack poseStack = new PoseStack();
MultiBufferSource.BufferSource multibuffersource$buffersource = this.renderBuffers.bufferSource();
LeashRendererUtil.levelRenderLeash(level, camera, poseStack, multibuffersource$buffersource);
}
}

View File

@ -0,0 +1,39 @@
package com.r3944realms.leashedplayer.mixin.client;
import com.r3944realms.leashedplayer.client.renderer.PlayerLeashState;
import com.r3944realms.leashedplayer.client.renderer.PlayerSlotItemLayerState;
import com.r3944realms.leashedplayer.modInterface.IPlayerRenderStateExtension;
import net.minecraft.client.renderer.entity.state.PlayerRenderState;
import org.jetbrains.annotations.Nullable;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Unique;
@SuppressWarnings({"AddedMixinMembersNamePattern"})
@Mixin(PlayerRenderState.class)
public class MixinPlayerRenderState implements IPlayerRenderStateExtension {
@Unique
@Nullable
private PlayerLeashState playerLeashState;
@Unique
private PlayerSlotItemLayerState playerSlotItemLayerState;
@Override
public @Nullable PlayerLeashState getPlayerLeashState() {
return playerLeashState;
}
@Override
public void setPlayerLeashState(@Nullable PlayerLeashState playerRenderState) {
this.playerLeashState = playerRenderState;
}
@Override
public PlayerSlotItemLayerState getPlayerSlotItemLayerState() {
return playerSlotItemLayerState;
}
@Override
public void setPlayerSlotItemLayerState(PlayerSlotItemLayerState playerRenderState) {
this.playerSlotItemLayerState = playerRenderState;
}
}

View File

@ -0,0 +1,30 @@
package com.r3944realms.leashedplayer.mixin.client;
import com.r3944realms.leashedplayer.client.renderer.LeashRendererUtil;
import com.r3944realms.leashedplayer.client.renderer.PlayerSlotItemLayerState;
import com.r3944realms.leashedplayer.modInterface.IPlayerRenderStateExtension;
import com.r3944realms.leashedplayer.modInterface.IPlayerRendererExtension;
import net.minecraft.client.model.PlayerModel;
import net.minecraft.client.player.AbstractClientPlayer;
import net.minecraft.client.renderer.entity.EntityRendererProvider;
import net.minecraft.client.renderer.entity.LivingEntityRenderer;
import net.minecraft.client.renderer.entity.player.PlayerRenderer;
import net.minecraft.client.renderer.entity.state.PlayerRenderState;
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.callback.CallbackInfo;
@Mixin(PlayerRenderer.class)
public abstract class MixinPlayerRenderer extends LivingEntityRenderer<AbstractClientPlayer, PlayerRenderState, PlayerModel> implements IPlayerRendererExtension {
public MixinPlayerRenderer(EntityRendererProvider.Context pContext, PlayerModel pModel, float pShadowRadius) {
super(pContext, pModel, pShadowRadius);
}
@Inject(method = {"extractRenderState(Lnet/minecraft/client/player/AbstractClientPlayer;Lnet/minecraft/client/renderer/entity/state/PlayerRenderState;F)V"}, at = @At("TAIL"))
public void extractRenderState(AbstractClientPlayer abstractClientPlayer, PlayerRenderState playerRenderState, float partTicks, CallbackInfo ci) {
LeashRendererUtil.createPlayerLeashState(abstractClientPlayer, (IPlayerRenderStateExtension) playerRenderState, partTicks);
((IPlayerRenderStateExtension)playerRenderState).setPlayerSlotItemLayerState(new PlayerSlotItemLayerState(abstractClientPlayer));
}
}

View File

@ -0,0 +1,16 @@
package com.r3944realms.leashedplayer.mixin.item;
import net.minecraft.world.inventory.ArmorSlot;
import net.minecraft.world.item.ItemStack;
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.callback.CallbackInfoReturnable;
@Mixin(ArmorSlot.class)
public class MixinArmorSlotMixin {
@Inject(method = "mayPlace", at = @At("RETURN"), cancellable = true)
public void allowItemEquipping(ItemStack stack, CallbackInfoReturnable<Boolean> cir) {
cir.setReturnValue(true);
}
}

View File

@ -0,0 +1,57 @@
package com.r3944realms.leashedplayer.mixin.item;
import com.r3944realms.leashedplayer.modInterface.PlayerLeashable;
import net.minecraft.core.BlockPos;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.InteractionResult;
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.item.ItemStack;
import net.minecraft.world.item.LeadItem;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.gameevent.GameEvent;
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.callback.CallbackInfoReturnable;
import java.util.List;
@Mixin(LeadItem.class)
public class MixinLeadItem {
/**
* 拴住自己的逻辑
*/
@Inject(
method = {"bindPlayerMobs"},
at = @At("HEAD"),
cancellable = true)
private static void selfLeash(Player pPlayer, Level pLevel, BlockPos pPos, CallbackInfoReturnable<InteractionResult> cir) {
List<Leashable> list = LeadItem.leashableInArea(pLevel, pPos, p_353025_ -> p_353025_.getLeashHolder() == pPlayer);
if (list.isEmpty()) {
ItemStack mainHandItem = pPlayer.getMainHandItem();
if (!(mainHandItem.getItem() instanceof LeadItem )) {
return;
}
//非创造模式减少防止刷物品
if(!pPlayer.isCreative()) mainHandItem.shrink(1);
//自己
Entity leashDataEntity = PlayerLeashable.getLeashDataEntity((ServerPlayer) pPlayer, (ServerLevel) pLevel);
PlayerLeashable playerLeashable = (PlayerLeashable) pPlayer;
if(leashDataEntity != null) {
Leashable.dropLeash((Entity & Leashable) playerLeashable, true, true);
}
//获取拴绳结实体
LeashFenceKnotEntity leashfenceknotentity = LeashFenceKnotEntity.getOrCreateKnot(pLevel, pPos);
//播放绳结被放置的声音
leashfenceknotentity.playPlacementSound();
//将自己与拴绳结绑定LeashData
playerLeashable.setLeashedTo(leashfenceknotentity, true);
pLevel.gameEvent(GameEvent.BLOCK_ATTACH, pPos, GameEvent.Context.of(pPlayer));
cir.setReturnValue(InteractionResult.SUCCESS);
}
}
}

View File

@ -0,0 +1,18 @@
package com.r3944realms.leashedplayer.mixin.registry;
import com.r3944realms.leashedplayer.datagen.provider.attributes.ModJukeboxSongs;
import net.minecraft.data.worldgen.BootstrapContext;
import net.minecraft.world.item.JukeboxSong;
import net.minecraft.world.item.JukeboxSongs;
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.callback.CallbackInfo;
@Mixin(JukeboxSongs.class)
public interface MixinJukeboxSongs {
@Inject(method = {"bootstrap"}, at = @At("TAIL"))
private static void bootstrap(BootstrapContext<JukeboxSong> pContext, CallbackInfo ci) {
ModJukeboxSongs.JukeboxSongBootstrap(pContext);
}
}

View File

@ -0,0 +1,19 @@
package com.r3944realms.leashedplayer.mixin.registry;
import com.r3944realms.leashedplayer.datagen.provider.attributes.ModPaintingVariants;
import net.minecraft.data.worldgen.BootstrapContext;
import net.minecraft.world.entity.decoration.PaintingVariant;
import net.minecraft.world.entity.decoration.PaintingVariants;
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.callback.CallbackInfo;
@Mixin(PaintingVariants.class)
public class MixinPaintingVariants {
@Inject(method = {"bootstrap"}, at = @At("TAIL"))
private static void bootstrap(BootstrapContext<PaintingVariant> pContext , CallbackInfo ci) {
ModPaintingVariants.PaintingVariantBootstrap(pContext);
}
}

View File

@ -0,0 +1,60 @@
package com.r3944realms.leashedplayer.mixin.server;
import com.r3944realms.leashedplayer.content.gamerules.GameruleRegistry;
import com.r3944realms.leashedplayer.content.gamerules.Server.TeleportWithLeashedPlayers;
import com.r3944realms.leashedplayer.modInterface.PlayerLeashable;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.server.network.ServerGamePacketListenerImpl;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.Leashable;
import net.minecraft.world.entity.PositionMoveRotation;
import net.minecraft.world.entity.Relative;
import net.minecraft.world.phys.Vec3;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.Unique;
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.*;
import java.util.stream.Collectors;
import static com.r3944realms.leashedplayer.utils.Logger.logger;
@Mixin(ServerGamePacketListenerImpl.class)
public class MixinServerGamePacketListenerImpl {
@Shadow
public ServerPlayer player;
@Unique
private List<Entity> Pl$LeashPlayers = new ArrayList<>();
@Inject(method = {"teleport(Lnet/minecraft/world/entity/PositionMoveRotation;Ljava/util/Set;)V"}, at = {@At("HEAD")})
private void teleportHead(PositionMoveRotation positionMoveRotation, Set<Relative> pRelativeSet, CallbackInfo ci) {
try {
//獲取Holder
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);
}
}
@Inject(method = {"teleport(Lnet/minecraft/world/entity/PositionMoveRotation;Ljava/util/Set;)V"}, at = {@At("TAIL")})
private void teleportTail(PositionMoveRotation moveRotation, Set<Relative> pRelativeSet, CallbackInfo ci) {
if(GameruleRegistry.getGameruleBoolValue(this.player.serverLevel(), TeleportWithLeashedPlayers.ID)) {
for (Entity Pl$LeashPlayer : this.Pl$LeashPlayers) {
if(Pl$LeashPlayer instanceof ServerPlayer) {
if(Pl$LeashPlayer instanceof PlayerLeashable playerLeashable) {
Leashable.dropLeash((Entity & Leashable) playerLeashable, false,false);
if(((ServerPlayer) playerLeashable).serverLevel() == this.player.serverLevel()) {
((ServerPlayer) playerLeashable).connection.teleport(moveRotation, pRelativeSet);
} else {
Vec3 vec3 = moveRotation.deltaMovement();
((ServerPlayer) playerLeashable).teleportTo(this.player.serverLevel(), vec3.x, vec3.y, vec3.z, pRelativeSet, moveRotation.yRot(), moveRotation.yRot(),false);
((ServerPlayer) playerLeashable).stopRiding();
}
playerLeashable.setLeashedTo(this.player, true);
}
}
}
}
}
}

View File

@ -0,0 +1,28 @@
package com.r3944realms.leashedplayer.modInterface;
public interface ILivingEntityExtension {
/**
* 获取拴绳的长度
* @return length 拴绳的长度Float
*/
float getLeashLength();
/**
* 设置拴绳的长度
* @param length 拴绳的长度Float
*/
void setLeashLength(float length);
/**
* 设置超出断裂长度后等待时间如果时间到仍超出则会执行断裂操作
* @apiNote 该为服务器方法只能在服务器端调用切勿在客户端线程调用
* @param keepTick 等待tickint
*/
void setKeepLeashTick(int keepTick);
/**
* 获取超出断裂长度后等待时间如果时间到仍超出则会执行断裂操作
* @apiNote 该为服务器方法只能在服务器端调用切勿在客户端线程调用
* @return keepTick 等待tickint
*/
int getKeepLeashTick();
}

View File

@ -0,0 +1,11 @@
package com.r3944realms.leashedplayer.modInterface;
import com.r3944realms.leashedplayer.client.renderer.PlayerLeashState;
import com.r3944realms.leashedplayer.client.renderer.PlayerSlotItemLayerState;
public interface IPlayerRenderStateExtension {
PlayerLeashState getPlayerLeashState();
void setPlayerLeashState(PlayerLeashState playerRenderState);
PlayerSlotItemLayerState getPlayerSlotItemLayerState();
void setPlayerSlotItemLayerState(PlayerSlotItemLayerState playerRenderState);
}

View File

@ -0,0 +1,24 @@
package com.r3944realms.leashedplayer.modInterface;
import net.minecraft.client.Camera;
import net.minecraft.world.phys.Vec3;
public interface IPlayerRendererExtension {
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

@ -0,0 +1,225 @@
package com.r3944realms.leashedplayer.modInterface;
import com.r3944realms.leashedplayer.content.commands.LeashCommand;
import com.r3944realms.leashedplayer.content.criteriaTriggers.ModCriteriaTriggers;
import com.r3944realms.leashedplayer.utils.Util;
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;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.phys.AABB;
import net.minecraft.world.phys.Vec3;
import org.jetbrains.annotations.NotNull;
import javax.annotation.Nullable;
import java.util.Optional;
import java.util.UUID;
public interface PlayerLeashable extends Leashable {
/**
* 获取拴绳的持有者实体 可能不存在
*/
@Nullable
Entity getLeashHolder();
/**
* 获取拴绳的持有者数据 从EntityData中获取
*/
LeashData getLeashDataFromEntityData();
/**
* 是否立即可被拴住
*/
boolean canBeLeashedInstantly(Player player);
/**
* 设置栓绳数据
* @param pLeashHolder 拴绳持有者
* @param pBroadcastPacket 是否广播包
*/
default void setLeashedTo(@NotNull Entity pLeashHolder, boolean pBroadcastPacket) {
setLeashedTo((Entity & Leashable)this, pLeashHolder, pBroadcastPacket);
if(this instanceof ServerPlayer) {
ModCriteriaTriggers.LEASH_PLAYER_TRIGGER.get().trigger((ServerPlayer) this, pLeashHolder);
}
}
static <E extends Entity & Leashable> void setLeashedTo(E pEntity, Entity pLeashHolder, boolean pBroadcastPacket) {
LeashData leashable$leashdata = pEntity.getLeashData();
if (leashable$leashdata == null) {
leashable$leashdata = new LeashData(pLeashHolder);
pEntity.setLeashData(leashable$leashdata);
} else {
leashable$leashdata.setLeashHolder(pLeashHolder);
}
if (pBroadcastPacket && pEntity.level() instanceof ServerLevel serverlevel) {
serverlevel.getChunkSource().broadcast(pEntity, new ClientboundSetEntityLinkPacket(pEntity, pLeashHolder));
}
//这边覆写去掉了乘坐相关的逻辑即乘坐状态下也可以正常被栓住不影响其乘坐状态
}
static void legacyElasticRangeLeashBehaviour(Entity holderEntity, Entity restrainedEntity) {
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) {
MethodA(holderEntity, applyMovementEntity, distance, leashLength);
// 控制速度不要过快避免偏激移动
Util.MovementBrake(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;
});
}
}
private static void MethodA(Entity holderEntity, Entity applyMovementEntity, float distance, float 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
)
);
}
private static void MethodB(Entity holderEntity, Entity applyMovementEntity, float distance, float leashLength) {
// 计算朝向持有者的拉力方向
double dX = (holderEntity.getX() - applyMovementEntity.getX()) / (double) distance;
double dZ = (holderEntity.getZ() - applyMovementEntity.getZ()) / (double) distance;
// 稳定点的目标高度
double targetY = holderEntity.getY() + 1.0; // 根据需要调整目标高度
double currentY = applyMovementEntity.getY();
double heightDifference = targetY - currentY;
// 控制垂直方向的拉力
double dY = heightDifference > 0.5 ? 0.1 : (heightDifference < -0.5 ? -0.1 : 0);
// 拉力大小距离越远拉力越强但施加一个最大限制
double pullStrength = Math.min((distance - leashLength) * 0.1, 1.0); // 限制最大拉力
applyMovementEntity.setDeltaMovement(
applyMovementEntity.getDeltaMovement().add(
dX * pullStrength,
dY,
dZ * pullStrength
)
);
}
@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();
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

@ -0,0 +1,35 @@
package com.r3944realms.leashedplayer.network;
import com.r3944realms.leashedplayer.LeashedPlayer;
import com.r3944realms.leashedplayer.network.client.UpdatePlayerMovement;
import com.r3944realms.leashedplayer.network.server.DecreaseLeashRopeLength;
import com.r3944realms.leashedplayer.network.server.IncreaseLeashRopeLength;
import net.neoforged.bus.api.SubscribeEvent;
import net.neoforged.fml.common.EventBusSubscriber;
import net.neoforged.neoforge.network.event.RegisterPayloadHandlersEvent;
import net.neoforged.neoforge.network.registration.PayloadRegistrar;
@EventBusSubscriber(modid = LeashedPlayer.MOD_ID, bus = EventBusSubscriber.Bus.MOD)
public class LeashedPlayerNetwork {
@SubscribeEvent
public static void registerPackets(RegisterPayloadHandlersEvent event) {
PayloadRegistrar registrar = event.registrar(LeashedPlayer.MOD_ID);
registrar.playToClient(
UpdatePlayerMovement.TYPE,
UpdatePlayerMovement.STREAM_CODEC,
UpdatePlayerMovement::handle
);
registrar.playToServer(
IncreaseLeashRopeLength.TYPE,
IncreaseLeashRopeLength.STREAM_CODEC,
IncreaseLeashRopeLength::handle
);
registrar.playToServer(
DecreaseLeashRopeLength.TYPE,
DecreaseLeashRopeLength.STREAM_CODEC,
DecreaseLeashRopeLength::handle
);
}
}

View File

@ -0,0 +1,31 @@
package com.r3944realms.leashedplayer.network.client;
import com.r3944realms.leashedplayer.LeashedPlayer;
import com.r3944realms.leashedplayer.content.gamerules.GameRules;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.network.codec.ByteBufCodecs;
import net.minecraft.network.codec.StreamCodec;
import net.minecraft.network.protocol.common.custom.CustomPacketPayload;
import net.minecraft.resources.ResourceLocation;
import net.neoforged.neoforge.network.handling.IPayloadContext;
import org.jetbrains.annotations.NotNull;
public record BooleanGameRuleValueChangeData(String gamerule_id, boolean value) implements IGameRuleValueChangeData {
public static Type<BooleanGameRuleValueChangeData> TYPE = new Type<>(ResourceLocation.fromNamespaceAndPath(LeashedPlayer.MOD_ID,"bool_gamerule_value_change"));
public static final StreamCodec<FriendlyByteBuf, BooleanGameRuleValueChangeData> STREAM_CODEC =
StreamCodec.composite(
ByteBufCodecs.STRING_UTF8, BooleanGameRuleValueChangeData::gamerule_id,
ByteBufCodecs.BOOL, BooleanGameRuleValueChangeData::value,
BooleanGameRuleValueChangeData::new
);
public void handle(IPayloadContext context) {
context.enqueueWork(() -> {
GameRules.gamerulesBooleanValuesClient.put(gamerule_id, value);
});
}
@Override
public @NotNull Type<? extends CustomPacketPayload> type() {
return TYPE;
}
}

View File

@ -0,0 +1,10 @@
package com.r3944realms.leashedplayer.network.client;
import net.minecraft.network.protocol.common.custom.CustomPacketPayload;
import org.jetbrains.annotations.NotNull;
public interface IGameRuleValueChangeData extends CustomPacketPayload {
@Override
@NotNull
Type<? extends CustomPacketPayload> type();
}

View File

@ -0,0 +1,55 @@
package com.r3944realms.leashedplayer.network.client;
import com.r3944realms.leashedplayer.LeashedPlayer;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.network.codec.ByteBufCodecs;
import net.minecraft.network.codec.StreamCodec;
import net.minecraft.network.protocol.common.custom.CustomPacketPayload;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.phys.Vec3;
import net.neoforged.neoforge.network.codec.NeoForgeStreamCodecs;
import net.neoforged.neoforge.network.handling.IPayloadContext;
import org.jetbrains.annotations.NotNull;
/**
* @author qyl27
* @param operation
* @param x
* @param y
* @param z
*/
public record UpdatePlayerMovement(Operation operation, double x, double y, double z) implements CustomPacketPayload {
public static final Type<UpdatePlayerMovement> TYPE = new Type<>(ResourceLocation.fromNamespaceAndPath(LeashedPlayer.MOD_ID,"update_player_movement"));
@Override
public @NotNull Type<? extends CustomPacketPayload> type() {
return TYPE;
}
public static final StreamCodec<FriendlyByteBuf, UpdatePlayerMovement> STREAM_CODEC =
StreamCodec.composite(
NeoForgeStreamCodecs.enumCodec(Operation.class), UpdatePlayerMovement::operation,
ByteBufCodecs.DOUBLE, UpdatePlayerMovement::x,
ByteBufCodecs.DOUBLE, UpdatePlayerMovement::y,
ByteBufCodecs.DOUBLE, UpdatePlayerMovement::z,
UpdatePlayerMovement::new
);
public void handle(IPayloadContext context) {
context.enqueueWork(() -> {
Player player = context.player();
switch (operation) {
case ADD -> player.addDeltaMovement(new Vec3(x, y, z));
case SET -> player.setDeltaMovement(new Vec3(x, y, z));
case MULTIPLY -> player.addDeltaMovement(player.getDeltaMovement().multiply(x, y, z));
}
}
);
}
public enum Operation {
SET,
ADD,
MULTIPLY
}
}

View File

@ -0,0 +1,8 @@
package com.r3944realms.leashedplayer.network.server;
public enum Code {
OTHER_ST,
OTHER_WE,
SELF;
public static final String LEASH_LENGTH_FAILED_SET = "leashedplayer.leash_rope.length.failed";
}

View File

@ -0,0 +1,61 @@
package com.r3944realms.leashedplayer.network.server;
import com.r3944realms.leashedplayer.LeashedPlayer;
import com.r3944realms.leashedplayer.modInterface.ILivingEntityExtension;
import com.r3944realms.leashedplayer.modInterface.PlayerLeashable;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.network.chat.Component;
import net.minecraft.network.codec.ByteBufCodecs;
import net.minecraft.network.codec.StreamCodec;
import net.minecraft.network.protocol.common.custom.CustomPacketPayload;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.player.Player;
import net.neoforged.neoforge.network.codec.NeoForgeStreamCodecs;
import net.neoforged.neoforge.network.handling.IPayloadContext;
import org.jetbrains.annotations.NotNull;
import java.util.UUID;
public record DecreaseLeashRopeLength(Code code, String playerUUID) implements CustomPacketPayload {
public static final Type<DecreaseLeashRopeLength> TYPE = new Type<>(ResourceLocation.fromNamespaceAndPath(LeashedPlayer.MOD_ID,"sub_leash_rope_length"));
public static final StreamCodec<FriendlyByteBuf, DecreaseLeashRopeLength> STREAM_CODEC =
StreamCodec.composite(
NeoForgeStreamCodecs.enumCodec(Code.class), DecreaseLeashRopeLength::code,
ByteBufCodecs.STRING_UTF8, DecreaseLeashRopeLength::playerUUID,
DecreaseLeashRopeLength::new
);
public static final String DECREASE_LEASH_ROPE_LENGTH = "leashedplayer.leash_rope.length.decrease",
DECREASE_SELF_LEASH_ROPE_LENGTH = DECREASE_LEASH_ROPE_LENGTH + ".self";
public void handle(IPayloadContext context) {
context.enqueueWork(() -> {
Player player = context.player();
ServerLevel level = (ServerLevel) player.level();
Player playerByUUID = level.getPlayerByUUID(UUID.fromString(playerUUID));
if (playerByUUID != null) {
ILivingEntityExtension entityExtension = (ILivingEntityExtension) playerByUUID;
Entity leashDataEntity = PlayerLeashable.getLeashDataEntity((ServerPlayer) playerByUUID, level);
if ((leashDataEntity != null && code == Code.OTHER_ST && leashDataEntity == player ) || (leashDataEntity != null && code == Code.OTHER_ST)) {
float newValue = Math.max(Math.min(entityExtension.getLeashLength() - 1, LeashedPlayer.M4()), LeashedPlayer.M3());
entityExtension.setLeashLength(newValue);
((ServerPlayer) playerByUUID).sendSystemMessage(Component.translatable(DECREASE_SELF_LEASH_ROPE_LENGTH, newValue), true);
((ServerPlayer) player).sendSystemMessage(Component.translatable(DECREASE_LEASH_ROPE_LENGTH, playerByUUID.getDisplayName(), newValue), true);
} else if (code == Code.SELF) {
float newValue = Math.max(Math.min(entityExtension.getLeashLength() - 1, LeashedPlayer.M4()), LeashedPlayer.M3());
entityExtension.setLeashLength(newValue);
((ServerPlayer) playerByUUID).sendSystemMessage(Component.translatable(DECREASE_SELF_LEASH_ROPE_LENGTH, newValue), true);
} else {
((ServerPlayer) playerByUUID).sendSystemMessage(Component.translatable(Code.LEASH_LENGTH_FAILED_SET), true);
((ServerPlayer) player).sendSystemMessage(Component.translatable(Code.LEASH_LENGTH_FAILED_SET), true);
}
}
});
}
@Override
public @NotNull Type<? extends CustomPacketPayload> type() {
return TYPE;
}
}

Some files were not shown because too many files have changed in this diff Show More