2024-09-02
初版完成
This commit is contained in:
parent
0c3dd146cf
commit
6b912c6498
|
|
@ -163,7 +163,9 @@ publishing {
|
|||
tasks.withType(JavaCompile).configureEach {
|
||||
options.encoding = 'UTF-8' // Use the UTF-8 charset for Java compilation
|
||||
}
|
||||
|
||||
minecraft {
|
||||
accessTransformers.file("src/main/resources/META-INF/accesstransformer.cfg")
|
||||
}
|
||||
// IDEA no longer automatically downloads sources/javadoc jars for dependencies, so we need to explicitly enable the behavior.
|
||||
idea {
|
||||
module {
|
||||
|
|
|
|||
|
|
@ -32,12 +32,12 @@ mod_name=Leashed Player
|
|||
# The license of the mod. Review your options at https://choosealicense.com/. All Rights Reserved is the default.
|
||||
mod_license=All Rights Reserved
|
||||
# The mod version. See https://semver.org/
|
||||
mod_version=1.0.0
|
||||
mod_version=0.0.1
|
||||
# The group ID for the mod. It is only important when publishing as an artifact to a Maven repository.
|
||||
# This should match the base package used for the mod sources.
|
||||
# See https://maven.apache.org/guides/mini/guide-naming-conventions.html
|
||||
mod_group_id=com.r3944realms.leashedplayer
|
||||
# The authors of the mod. This is a simple text string that is used for display purposes in the mod list.
|
||||
mod_authors=YourNameHere, OtherNameHere
|
||||
mod_authors=r3944Realms
|
||||
# The description of the mod. This is a simple multiline text string that is used for display purposes in the mod list.
|
||||
mod_description=Example mod description.\nNewline characters can be used and will be replaced properly.
|
||||
mod_description= Player but can be leash
|
||||
|
|
|
|||
|
|
@ -0,0 +1,2 @@
|
|||
// 1.21 2024-09-03T17:46:39.9837977 Languages: en_us for mod: leashedplayer
|
||||
d770cde6d74b269e8a0447edeb0a65668dda07d3 assets/leashedplayer/lang/en_us.json
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
// 1.21 2024-09-03T17:46:39.9787945 Languages: lzh for mod: leashedplayer
|
||||
bf21a9e8fbc5a3846fb05b4fa0859e0917b2202f assets/leashedplayer/lang/lzh.json
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
// 1.21 2024-09-03T17:46:39.9827933 Languages: zh_cn for mod: leashedplayer
|
||||
eb4dc9fdfece3e001d0f32abcad5de6b926b0a4d assets/leashedplayer/lang/zh_cn.json
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
// 1.21 2024-09-03T17:46:39.9817953 Languages: zh_tw for mod: leashedplayer
|
||||
0f4c1499f9241f8d8c33033dbcad397c4f3df5fa assets/leashedplayer/lang/zh_tw.json
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"gamerule.RWN.TeleportWithLeashedPlayers": "Teleport with leashed player",
|
||||
"gamerule.RWN.TeleportWithLeashedPlayers.description": "You will teleport with your leashed players ",
|
||||
"leashedplayer.command.leash.message.leash.length.fail": "Failed (Internal Error)",
|
||||
"leashedplayer.command.leash.message.leash.length.set": "The Leash length of %s is set to %f blocks",
|
||||
"leashedplayer.command.leash.message.leash.length.show": "The Leash Length of %s is %f blocks"
|
||||
}
|
||||
|
|
@ -0,0 +1 @@
|
|||
{}
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"gamerule.RWN.TeleportWithLeashedPlayers": "传送被栓玩家",
|
||||
"gamerule.RWN.TeleportWithLeashedPlayers.description": "传送时将被栓玩家与自己一起传送",
|
||||
"leashedplayer.command.leash.message.leash.length.fail": "失败(内部错误)",
|
||||
"leashedplayer.command.leash.message.leash.length.set": "%s的拴绳长度被设置为%f格",
|
||||
"leashedplayer.command.leash.message.leash.length.show": "%s的拴绳长度为%f格"
|
||||
}
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"gamerule.RWN.TeleportWithLeashedPlayers": "傳送被栓玩家",
|
||||
"gamerule.RWN.TeleportWithLeashedPlayers.description": "傳送時將被栓玩家與隨自己一起傳送",
|
||||
"leashedplayer.command.leash.message.leash.length.fail": "失敗(内部錯誤)",
|
||||
"leashedplayer.command.leash.message.leash.length.set": "%s的栓繩長度被設置為%f格",
|
||||
"leashedplayer.command.leash.message.leash.length.show": "%s的栓繩長度為%f格"
|
||||
}
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
package com.r3944realms.leashedplayer;
|
||||
|
||||
import com.mojang.brigadier.CommandDispatcher;
|
||||
import com.r3944realms.leashedplayer.content.commands.LeashCommand;
|
||||
import net.minecraft.commands.CommandSourceStack;
|
||||
import net.neoforged.bus.api.SubscribeEvent;
|
||||
import net.neoforged.fml.common.EventBusSubscriber;
|
||||
import net.neoforged.neoforge.event.RegisterCommandsEvent;
|
||||
|
||||
@EventBusSubscriber(modid = LeashedPlayer.MOD_ID)
|
||||
public class CommonEventHandler {
|
||||
@SubscribeEvent
|
||||
public static void onRegisterCommander(RegisterCommandsEvent event) {
|
||||
CommandDispatcher<CommandSourceStack> dispatcher = event.getDispatcher();
|
||||
LeashCommand.register(dispatcher);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,63 +0,0 @@
|
|||
package com.r3944realms.leashedplayer;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import net.minecraft.core.registries.BuiltInRegistries;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.world.item.Item;
|
||||
import net.neoforged.bus.api.SubscribeEvent;
|
||||
import net.neoforged.fml.common.EventBusSubscriber;
|
||||
import net.neoforged.fml.event.config.ModConfigEvent;
|
||||
import net.neoforged.neoforge.common.ModConfigSpec;
|
||||
|
||||
// An example config class. This is not required, but it's a good idea to have one to keep your config organized.
|
||||
// Demonstrates how to use Neo's config APIs
|
||||
@EventBusSubscriber(modid = ExampleMod.MODID, bus = EventBusSubscriber.Bus.MOD)
|
||||
public class Config
|
||||
{
|
||||
private static final ModConfigSpec.Builder BUILDER = new ModConfigSpec.Builder();
|
||||
|
||||
private static final ModConfigSpec.BooleanValue LOG_DIRT_BLOCK = BUILDER
|
||||
.comment("Whether to log the dirt block on common setup")
|
||||
.define("logDirtBlock", true);
|
||||
|
||||
private static final ModConfigSpec.IntValue MAGIC_NUMBER = BUILDER
|
||||
.comment("A magic number")
|
||||
.defineInRange("magicNumber", 42, 0, Integer.MAX_VALUE);
|
||||
|
||||
public static final ModConfigSpec.ConfigValue<String> MAGIC_NUMBER_INTRODUCTION = BUILDER
|
||||
.comment("What you want the introduction message to be for the magic number")
|
||||
.define("magicNumberIntroduction", "The magic number is... ");
|
||||
|
||||
// a list of strings that are treated as resource locations for items
|
||||
private static final ModConfigSpec.ConfigValue<List<? extends String>> ITEM_STRINGS = BUILDER
|
||||
.comment("A list of items to log on common setup.")
|
||||
.defineListAllowEmpty("items", List.of("minecraft:iron_ingot"), Config::validateItemName);
|
||||
|
||||
static final ModConfigSpec SPEC = BUILDER.build();
|
||||
|
||||
public static boolean logDirtBlock;
|
||||
public static int magicNumber;
|
||||
public static String magicNumberIntroduction;
|
||||
public static Set<Item> items;
|
||||
|
||||
private static boolean validateItemName(final Object obj)
|
||||
{
|
||||
return obj instanceof String itemName && BuiltInRegistries.ITEM.containsKey(ResourceLocation.parse(itemName));
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
static void onLoad(final ModConfigEvent event)
|
||||
{
|
||||
logDirtBlock = LOG_DIRT_BLOCK.get();
|
||||
magicNumber = MAGIC_NUMBER.get();
|
||||
magicNumberIntroduction = MAGIC_NUMBER_INTRODUCTION.get();
|
||||
|
||||
// convert the list of strings into a set of items
|
||||
items = ITEM_STRINGS.get().stream()
|
||||
.map(itemName -> BuiltInRegistries.ITEM.get(ResourceLocation.parse(itemName)))
|
||||
.collect(Collectors.toSet());
|
||||
}
|
||||
}
|
||||
|
|
@ -1,136 +0,0 @@
|
|||
package com.r3944realms.leashedplayer;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import com.mojang.logging.LogUtils;
|
||||
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.core.registries.BuiltInRegistries;
|
||||
import net.minecraft.core.registries.Registries;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.world.food.FoodProperties;
|
||||
import net.minecraft.world.item.BlockItem;
|
||||
import net.minecraft.world.item.CreativeModeTab;
|
||||
import net.minecraft.world.item.CreativeModeTabs;
|
||||
import net.minecraft.world.item.Item;
|
||||
import net.minecraft.world.level.block.Block;
|
||||
import net.minecraft.world.level.block.Blocks;
|
||||
import net.minecraft.world.level.block.state.BlockBehaviour;
|
||||
import net.minecraft.world.level.material.MapColor;
|
||||
import net.neoforged.api.distmarker.Dist;
|
||||
import net.neoforged.bus.api.IEventBus;
|
||||
import net.neoforged.bus.api.SubscribeEvent;
|
||||
import net.neoforged.fml.ModContainer;
|
||||
import net.neoforged.fml.common.EventBusSubscriber;
|
||||
import net.neoforged.fml.common.Mod;
|
||||
import net.neoforged.fml.config.ModConfig;
|
||||
import net.neoforged.fml.event.lifecycle.FMLClientSetupEvent;
|
||||
import net.neoforged.fml.event.lifecycle.FMLCommonSetupEvent;
|
||||
import net.neoforged.neoforge.common.NeoForge;
|
||||
import net.neoforged.neoforge.event.BuildCreativeModeTabContentsEvent;
|
||||
import net.neoforged.neoforge.event.server.ServerStartingEvent;
|
||||
import net.neoforged.neoforge.registries.DeferredBlock;
|
||||
import net.neoforged.neoforge.registries.DeferredHolder;
|
||||
import net.neoforged.neoforge.registries.DeferredItem;
|
||||
import net.neoforged.neoforge.registries.DeferredRegister;
|
||||
|
||||
// The value here should match an entry in the META-INF/neoforge.mods.toml file
|
||||
@Mod(ExampleMod.MODID)
|
||||
public class ExampleMod
|
||||
{
|
||||
// Define mod id in a common place for everything to reference
|
||||
public static final String MODID = "examplemod";
|
||||
// Directly reference a slf4j logger
|
||||
private static final Logger LOGGER = LogUtils.getLogger();
|
||||
// Create a Deferred Register to hold Blocks which will all be registered under the "examplemod" namespace
|
||||
public static final DeferredRegister.Blocks BLOCKS = DeferredRegister.createBlocks(MODID);
|
||||
// Create a Deferred Register to hold Items which will all be registered under the "examplemod" namespace
|
||||
public static final DeferredRegister.Items ITEMS = DeferredRegister.createItems(MODID);
|
||||
// Create a Deferred Register to hold CreativeModeTabs which will all be registered under the "examplemod" namespace
|
||||
public static final DeferredRegister<CreativeModeTab> CREATIVE_MODE_TABS = DeferredRegister.create(Registries.CREATIVE_MODE_TAB, MODID);
|
||||
|
||||
// Creates a new Block with the id "examplemod:example_block", combining the namespace and path
|
||||
public static final DeferredBlock<Block> EXAMPLE_BLOCK = BLOCKS.registerSimpleBlock("example_block", BlockBehaviour.Properties.of().mapColor(MapColor.STONE));
|
||||
// Creates a new BlockItem with the id "examplemod:example_block", combining the namespace and path
|
||||
public static final DeferredItem<BlockItem> EXAMPLE_BLOCK_ITEM = ITEMS.registerSimpleBlockItem("example_block", EXAMPLE_BLOCK);
|
||||
|
||||
// Creates a new food item with the id "examplemod:example_id", nutrition 1 and saturation 2
|
||||
public static final DeferredItem<Item> EXAMPLE_ITEM = ITEMS.registerSimpleItem("example_item", new Item.Properties().food(new FoodProperties.Builder()
|
||||
.alwaysEdible().nutrition(1).saturationModifier(2f).build()));
|
||||
|
||||
// Creates a creative tab with the id "examplemod:example_tab" for the example item, that is placed after the combat tab
|
||||
public static final DeferredHolder<CreativeModeTab, CreativeModeTab> EXAMPLE_TAB = CREATIVE_MODE_TABS.register("example_tab", () -> CreativeModeTab.builder()
|
||||
.title(Component.translatable("itemGroup.examplemod")) //The language key for the title of your CreativeModeTab
|
||||
.withTabsBefore(CreativeModeTabs.COMBAT)
|
||||
.icon(() -> EXAMPLE_ITEM.get().getDefaultInstance())
|
||||
.displayItems((parameters, output) -> {
|
||||
output.accept(EXAMPLE_ITEM.get()); // Add the example item to the tab. For your own tabs, this method is preferred over the event
|
||||
}).build());
|
||||
|
||||
// The constructor for the mod class is the first code that is run when your mod is loaded.
|
||||
// FML will recognize some parameter types like IEventBus or ModContainer and pass them in automatically.
|
||||
public ExampleMod(IEventBus modEventBus, ModContainer modContainer)
|
||||
{
|
||||
// Register the commonSetup method for modloading
|
||||
modEventBus.addListener(this::commonSetup);
|
||||
|
||||
// Register the Deferred Register to the mod event bus so blocks get registered
|
||||
BLOCKS.register(modEventBus);
|
||||
// Register the Deferred Register to the mod event bus so items get registered
|
||||
ITEMS.register(modEventBus);
|
||||
// Register the Deferred Register to the mod event bus so tabs get registered
|
||||
CREATIVE_MODE_TABS.register(modEventBus);
|
||||
|
||||
// Register ourselves for server and other game events we are interested in.
|
||||
// Note that this is necessary if and only if we want *this* class (ExampleMod) to respond directly to events.
|
||||
// Do not add this line if there are no @SubscribeEvent-annotated functions in this class, like onServerStarting() below.
|
||||
NeoForge.EVENT_BUS.register(this);
|
||||
|
||||
// Register the item to a creative tab
|
||||
modEventBus.addListener(this::addCreative);
|
||||
|
||||
// Register our mod's ModConfigSpec so that FML can create and load the config file for us
|
||||
modContainer.registerConfig(ModConfig.Type.COMMON, Config.SPEC);
|
||||
}
|
||||
|
||||
private void commonSetup(final FMLCommonSetupEvent event)
|
||||
{
|
||||
// Some common setup code
|
||||
LOGGER.info("HELLO FROM COMMON SETUP");
|
||||
|
||||
if (Config.logDirtBlock)
|
||||
LOGGER.info("DIRT BLOCK >> {}", BuiltInRegistries.BLOCK.getKey(Blocks.DIRT));
|
||||
|
||||
LOGGER.info(Config.magicNumberIntroduction + Config.magicNumber);
|
||||
|
||||
Config.items.forEach((item) -> LOGGER.info("ITEM >> {}", item.toString()));
|
||||
}
|
||||
|
||||
// Add the example block item to the building blocks tab
|
||||
private void addCreative(BuildCreativeModeTabContentsEvent event)
|
||||
{
|
||||
if (event.getTabKey() == CreativeModeTabs.BUILDING_BLOCKS)
|
||||
event.accept(EXAMPLE_BLOCK_ITEM);
|
||||
}
|
||||
|
||||
// You can use SubscribeEvent and let the Event Bus discover methods to call
|
||||
@SubscribeEvent
|
||||
public void onServerStarting(ServerStartingEvent event)
|
||||
{
|
||||
// Do something when the server starts
|
||||
LOGGER.info("HELLO from server starting");
|
||||
}
|
||||
|
||||
// You can use EventBusSubscriber to automatically register all static methods in the class annotated with @SubscribeEvent
|
||||
@EventBusSubscriber(modid = MODID, bus = EventBusSubscriber.Bus.MOD, value = Dist.CLIENT)
|
||||
public static class ClientModEvents
|
||||
{
|
||||
@SubscribeEvent
|
||||
public static void onClientSetup(FMLClientSetupEvent event)
|
||||
{
|
||||
// Some client setup code
|
||||
LOGGER.info("HELLO FROM CLIENT SETUP");
|
||||
LOGGER.info("MINECRAFT NAME >> {}", Minecraft.getInstance().getUser().getName());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
package com.r3944realms.leashedplayer;
|
||||
|
||||
import net.neoforged.fml.common.Mod;
|
||||
//TODO: 13:40
|
||||
@Mod(LeashedPlayer.MOD_ID)
|
||||
public class LeashedPlayer {
|
||||
public static final String MOD_ID = "leashedplayer";
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
package com.r3944realms.leashedplayer.content.commands;
|
||||
|
||||
public class Command {
|
||||
public static final String PREFIX = "lp";
|
||||
}
|
||||
|
|
@ -0,0 +1,101 @@
|
|||
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.r3944realms.leashedplayer.modInterface.ILivingEntityExtension;
|
||||
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;
|
||||
|
||||
public class LeashCommand {
|
||||
|
||||
private final static String LEASHEDPLAYER_LEASH_MESSAGE_ = "leashedplayer.command.leash.message.";
|
||||
public final static String LEASH_LENGTH_SHOW = LEASHEDPLAYER_LEASH_MESSAGE_ + "leash.length.show",
|
||||
LEASH_LENGTH_FAIL = LEASHEDPLAYER_LEASH_MESSAGE_ + "leash.length.fail",
|
||||
LEASH_LENGTH_SET = LEASHEDPLAYER_LEASH_MESSAGE_ + "leash.length.set";
|
||||
public static void register(CommandDispatcher<CommandSourceStack> dispatcher) {
|
||||
LiteralArgumentBuilder<CommandSourceStack> literalArgumentBuilder = Commands.literal(com.r3944realms.leashedplayer.content.commands.Command.PREFIX);
|
||||
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.getName(), leashLength), true);
|
||||
} catch (Exception e) {
|
||||
source.sendFailure(Component.translatable(LEASH_LENGTH_FAIL));
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
};
|
||||
Command<CommandSourceStack> getRefPlayerLeashLength = context -> {
|
||||
CommandSourceStack source = context.getSource();
|
||||
try {
|
||||
ServerPlayer player = EntityArgument.getPlayer(context, "player");
|
||||
float leashLength = ((ILivingEntityExtension)player).getLeashLength();
|
||||
source.sendSuccess(() -> Component.translatable(LEASH_LENGTH_SHOW, player.getName(), leashLength), true);
|
||||
} catch (Exception e) {
|
||||
source.sendFailure(Component.translatable(LEASH_LENGTH_FAIL));
|
||||
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.getName(), leashLength), true);
|
||||
} catch (Exception e) {
|
||||
source.sendFailure(Component.translatable(LEASH_LENGTH_FAIL));
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
};
|
||||
Command<CommandSourceStack> setLengthLeashLength = context -> {
|
||||
CommandSourceStack source = context.getSource();
|
||||
try {
|
||||
// Player player = context.getArgument("player", Player.class);
|
||||
ServerPlayer player = EntityArgument.getPlayer(context, "player");
|
||||
float leashLength = context.getArgument("leashLength", Float.class);
|
||||
((ILivingEntityExtension)player).setLeashLength(leashLength);
|
||||
source.sendSuccess(() -> Component.translatable(LEASH_LENGTH_SET, player.getName(), leashLength), true);
|
||||
} catch (Exception e) {
|
||||
source.sendFailure(Component.translatable(LEASH_LENGTH_FAIL));
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
};
|
||||
LiteralArgumentBuilder<CommandSourceStack> $$leashRoot = Commands.literal("leash").requires(cs -> cs.hasPermission(2));
|
||||
literalArgumentBuilder.then(
|
||||
$$leashRoot.then(Commands.literal("length").executes(getSelfLeashLength)
|
||||
.then(Commands.literal("getLength").executes(getSelfLeashLength))
|
||||
.then(Commands.literal("setLength")
|
||||
.then(Commands.argument("leashLength", FloatArgumentType.floatArg(5, 1024)).executes(setSelfLengthLeashLength)
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
literalArgumentBuilder.then(
|
||||
$$leashRoot.then(
|
||||
Commands.literal("length")
|
||||
.then(Commands.argument("player", EntityArgument.player()).executes(getRefPlayerLeashLength)
|
||||
.then(Commands.literal("getLength").executes(getRefPlayerLeashLength))
|
||||
.then(Commands.literal("setLength")
|
||||
.then(
|
||||
Commands.argument("leashLength", FloatArgumentType.floatArg(5, 1024)).executes(setLengthLeashLength)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
);
|
||||
dispatcher.register(literalArgumentBuilder);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,70 @@
|
|||
package com.r3944realms.leashedplayer.content.gamerules;
|
||||
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.world.level.GameRules;
|
||||
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, 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 level.getGameRules().getBoolean((GameRules.Key<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 level.getGameRules().getInt((GameRules.Key<GameRules.IntegerValue>)gamerules.get(gameruleName));
|
||||
}
|
||||
|
||||
public void registerGamerule(String gameruleName, GameRules.Category category, boolean pDefault) {
|
||||
registerGamerule(gameruleName, category, pDefault, (s,i)->{});//最后一个仅占位无用
|
||||
}
|
||||
public void registerGamerule(String gameruleName, GameRules.Category category, boolean pDefault, BiConsumer<MinecraftServer, GameRules.BooleanValue> pChangeListener) {
|
||||
gamerules.put(gameruleName, GameRules.register(gameruleName, category, GameRules.BooleanValue.create(pDefault, pChangeListener)));
|
||||
gameruleDataTypes.put(gameruleName, RuleDataType.BOOLEAN);
|
||||
}
|
||||
public void registerGamerule(String gameruleName, GameRules.Category category, int pDefault) {
|
||||
registerGamerule(gameruleName, category, pDefault, (BiConsumer<MinecraftServer, GameRules.IntegerValue>) (s, i)->{});//最后一个仅占位无用
|
||||
}
|
||||
public void registerGamerule(String gameruleName, GameRules.Category category, int pDefault, BiConsumer<MinecraftServer, GameRules.IntegerValue> pChangeListener) {
|
||||
gamerules.put(gameruleName, GameRules.register(gameruleName, category, GameRules.IntegerValue.create(pDefault, pChangeListener)));
|
||||
gameruleDataTypes.put(gameruleName, RuleDataType.INTEGER);
|
||||
}
|
||||
public void registerGamerule(String gameruleName, GameRules.Category category, int pDefault, int pMin, int pMax, BiConsumer<MinecraftServer, GameRules.IntegerValue> pChangeListener) {
|
||||
gamerules.put(gameruleName, GameRules.register(gameruleName, category, GameRules.IntegerValue.create(pDefault, pMin, pMax, pChangeListener)));
|
||||
gameruleDataTypes.put(gameruleName, RuleDataType.INTEGER);
|
||||
}
|
||||
public void registerGamerule(String gameruleName, GameRules.Category category,float value) {
|
||||
registerGamerule(gameruleName, category, value, (s,i)->{});
|
||||
}
|
||||
public void registerGamerule(String gameruleName, GameRules.Category category, float pDefault, BiConsumer<MinecraftServer, Gamerules.FloatValue> pChangeListener) {
|
||||
gamerules.put(gameruleName, GameRules.register(gameruleName, category, Gamerules.FloatValue.create(pDefault, pChangeListener)));
|
||||
gameruleDataTypes.put(gameruleName, RuleDataType.INTEGER);
|
||||
}
|
||||
public void registerGamerule(String gameruleName, GameRules.Category category, float pDefault, float pMin, float pMax,BiConsumer<MinecraftServer, Gamerules.FloatValue> pChangeListener) {
|
||||
gamerules.put(gameruleName, GameRules.register(gameruleName, category, Gamerules.FloatValue.create(pDefault, pMin, pMax,pChangeListener)));
|
||||
gameruleDataTypes.put(gameruleName, RuleDataType.INTEGER);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,102 @@
|
|||
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.level.GameRules;
|
||||
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 = "RWN.";
|
||||
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 GameRules.Value<FloatValue> {
|
||||
private float value;
|
||||
public static GameRules.Type<FloatValue> create(
|
||||
float pDefaultValue, BiConsumer<MinecraftServer, FloatValue> pChangeListener
|
||||
) {
|
||||
return new GameRules.Type<>
|
||||
(FloatArgumentType::floatArg,
|
||||
pType -> new FloatValue(pType, pDefaultValue),
|
||||
pChangeListener,
|
||||
GameRules.GameRuleTypeVisitor::visit
|
||||
);
|
||||
}
|
||||
public static GameRules.Type<FloatValue> create(
|
||||
float pDefaultValue, float pMin, float pMax , BiConsumer<MinecraftServer, FloatValue> pChangeListener
|
||||
) {
|
||||
return new GameRules.Type<>(
|
||||
() -> FloatArgumentType.floatArg(pMin, pMax),
|
||||
pType -> new FloatValue(pType, pDefaultValue),
|
||||
pChangeListener,
|
||||
GameRules.GameRuleTypeVisitor::visit
|
||||
);
|
||||
}
|
||||
public FloatValue(GameRules.Type<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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
package com.r3944realms.leashedplayer.content.gamerules.Server;
|
||||
|
||||
import com.r3944realms.leashedplayer.LeashedPlayer;
|
||||
import com.r3944realms.leashedplayer.content.gamerules.Gamerules;
|
||||
import com.r3944realms.leashedplayer.utils.Util;
|
||||
import net.minecraft.world.level.GameRules;
|
||||
import net.neoforged.bus.api.SubscribeEvent;
|
||||
import net.neoforged.fml.common.EventBusSubscriber;
|
||||
import net.neoforged.fml.event.lifecycle.FMLCommonSetupEvent;
|
||||
|
||||
import static com.r3944realms.leashedplayer.content.gamerules.Gamerules.GAMERULE_REGISTRY;
|
||||
|
||||
//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 GameRules.Category CATEGORY = GameRules.Category.PLAYER;
|
||||
|
||||
@SubscribeEvent
|
||||
public static void onCommonSetup(final FMLCommonSetupEvent event) {
|
||||
GAMERULE_REGISTRY.registerGamerule(ID, CATEGORY, DEFAULT_VALUE);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,117 @@
|
|||
package com.r3944realms.leashedplayer.datagen.LanguageAndOtherData;
|
||||
|
||||
import com.r3944realms.leashedplayer.content.commands.LeashCommand;
|
||||
import com.r3944realms.leashedplayer.content.gamerules.Server.TeleportWithLeashedPlayers;
|
||||
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;
|
||||
|
||||
public enum ModLangKeyValue {
|
||||
//COMMAND_MESSAGE
|
||||
MESSAGE_LEASH_LENGTH_FAIL(LeashCommand.LEASH_LENGTH_FAIL, ModPartEnum.COMMAND, "Failed (Internal Error)", "失败(内部错误)", "失敗(内部錯誤)", false),
|
||||
MESSAGE_LEASH_LENGTH_SHOW(LeashCommand.LEASH_LENGTH_SHOW, ModPartEnum.COMMAND, "The Leash Length of %s is %f blocks", "%s的拴绳长度为%f格", "%s的栓繩長度為%f格" , false),
|
||||
MESSAGE_LEASH_LENGTH_SET(LeashCommand.LEASH_LENGTH_SET, ModPartEnum.COMMAND, "The Leash length of %s is set to %f blocks", "%s的拴绳长度被设置为%f格", "%s的栓繩長度被設置為%f格" , false),
|
||||
//GAME_RULE_NAME
|
||||
TELEPORT_WITH_LEASHED_PLAYERS(TeleportWithLeashedPlayers.NAME_KEY, ModPartEnum.NAME, "Teleport with leashed player", "传送被栓玩家", "傳送被栓玩家" ,false),
|
||||
//GAME_RULE_DESCRIPTION
|
||||
TELEPORT_WITH_LEASHED_DESCRIPTION(TeleportWithLeashedPlayers.DESCRIPTION_KEY, ModPartEnum.DESCRIPTION, "You will teleport with your leashed players ", "传送时将被栓玩家与自己一起传送", "傳送時將被栓玩家與隨自己一起傳送" ,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;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
package com.r3944realms.leashedplayer.datagen;
|
||||
|
||||
import com.r3944realms.leashedplayer.LeashedPlayer;
|
||||
import com.r3944realms.leashedplayer.datagen.provider.ModLanguageProvider;
|
||||
import com.r3944realms.leashedplayer.utils.Enum.LanguageEnum;
|
||||
import net.minecraft.core.HolderLookup;
|
||||
import net.minecraft.data.DataProvider;
|
||||
import net.neoforged.bus.api.SubscribeEvent;
|
||||
import net.neoforged.fml.common.EventBusSubscriber;
|
||||
import net.neoforged.neoforge.common.data.ExistingFileHelper;
|
||||
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 event) {
|
||||
CompletableFuture<HolderLookup.Provider> HolderFolder = event.getLookupProvider();
|
||||
|
||||
ExistingFileHelper existingFileHelper = event.getExistingFileHelper();
|
||||
/*Language Provider ENGLISH CHINESE(SIM/TRA)*/
|
||||
addLanguage(event, LanguageEnum.English, "en_us");
|
||||
addLanguage(event, LanguageEnum.SimpleChinese, "zh_cn");
|
||||
addLanguage(event, LanguageEnum.TraditionalChinese, "zh_tw");
|
||||
addLanguage(event, LanguageEnum.LiteraryChinese, "lzh");
|
||||
}
|
||||
private static void addLanguage(GatherDataEvent event, LanguageEnum language, String lan_regex){
|
||||
event.getGenerator().addProvider(
|
||||
event.includeClient(),
|
||||
(DataProvider.Factory<ModLanguageProvider>) pOutput -> new ModLanguageProvider(pOutput, LeashedPlayer.MOD_ID, language)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -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)));
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
package com.r3944realms.leashedplayer.mixin.both;
|
||||
|
||||
import com.r3944realms.leashedplayer.modInterface.PlayerLeashable;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.entity.Leashable;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||
|
||||
@Mixin(Entity.class)
|
||||
public class MixinEntity {
|
||||
/**
|
||||
* 这里重定向,当实体类实现了{@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/world/entity/Entity;)V")
|
||||
)
|
||||
<E extends Entity & Leashable> void checkAndCancelIfTure(E entity) {
|
||||
if(!(entity instanceof PlayerLeashable)) {
|
||||
Leashable.tickLeash(entity);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,53 @@
|
|||
package com.r3944realms.leashedplayer.mixin.both;
|
||||
|
||||
import com.r3944realms.leashedplayer.modInterface.ILivingEntityExtension;
|
||||
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.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.Unique;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
@Mixin(LivingEntity.class)
|
||||
public abstract class MixinLivingEntity extends Entity implements ILivingEntityExtension {
|
||||
public MixinLivingEntity(EntityType<?> pEntityType, Level pLevel) {
|
||||
super(pEntityType, pLevel);
|
||||
}
|
||||
@Unique
|
||||
@SuppressWarnings("WrongEntityDataParameterClass")
|
||||
private static final EntityDataAccessor<Float> DATA_ENTITY_LEASH_LENGTH = SynchedEntityData.defineId(LivingEntity.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);
|
||||
}
|
||||
|
||||
@Inject(method = {"defineSynchedData"}, at = {@At("TAIL")})
|
||||
//定义Client/Server实体同步数据
|
||||
private void defineSyncData(SynchedEntityData.Builder pBuilder, CallbackInfo ci) {
|
||||
pBuilder.define(DATA_ENTITY_LEASH_LENGTH, 5.0F);
|
||||
}
|
||||
@Inject(method = {"readAdditionalSaveData"}, at = {@At("RETURN")})
|
||||
private void readSaveData(CompoundTag compoundTag, CallbackInfo callbackInfo) {
|
||||
if(compoundTag.contains("LeashLength")) {
|
||||
this.setLeashLength(compoundTag.getFloat("LeashLength"));
|
||||
}
|
||||
}
|
||||
@Inject(method = {"addAdditionalSaveData"}, at = {@At("RETURN")})
|
||||
private void addSaveData(CompoundTag compoundTag, CallbackInfo callbackInfo) {
|
||||
compoundTag.putFloat("LeashLength", getLeashLength());
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,253 @@
|
|||
package com.r3944realms.leashedplayer.mixin.both;
|
||||
|
||||
import com.r3944realms.leashedplayer.modInterface.ILivingEntityExtension;
|
||||
import com.r3944realms.leashedplayer.modInterface.PlayerLeashable;
|
||||
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.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.function.Consumer;
|
||||
|
||||
@Mixin(Player.class)
|
||||
public abstract class MixinPlayer extends LivingEntity implements PlayerLeashable {
|
||||
|
||||
@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);
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
@Unique
|
||||
private static void Pl$UpdateLeash(Entity holderEntity, Entity restrainedEntity) {
|
||||
if(holderEntity == null || holderEntity.level() != restrainedEntity.level())
|
||||
return;
|
||||
float leashLength = 6.0f;
|
||||
if(restrainedEntity instanceof ILivingEntityExtension iEntity) {
|
||||
//获取长度
|
||||
float leashLengthFormValue = iEntity.getLeashLength();
|
||||
leashLength = leashLengthFormValue > 6 ? leashLengthFormValue : 6;
|
||||
}
|
||||
//两者距离
|
||||
float distance = holderEntity.distanceTo(restrainedEntity);
|
||||
//大于长度情况
|
||||
if(distance > leashLength) {
|
||||
//作用对象(实体所坐载体还是实体【根据isPassenger来判断】
|
||||
Entity applyMovementEntity = restrainedEntity.isPassenger() ? restrainedEntity.getVehicle() : restrainedEntity;
|
||||
if(applyMovementEntity != null){
|
||||
double dX = (holderEntity.getX() - applyMovementEntity.getX()) / (double) distance;
|
||||
double dY = (holderEntity.getY() - applyMovementEntity.getY()) / (double) distance;
|
||||
double dZ = (holderEntity.getZ() - applyMovementEntity.getZ()) / (double) distance;
|
||||
//给予作用实体其向holderEntity的一个速度动量
|
||||
applyMovementEntity.setDeltaMovement(
|
||||
applyMovementEntity.getDeltaMovement().add(
|
||||
Math.copySign(dX * dX * 0.4d, dX),
|
||||
Math.copySign(dY * dY * 0.4d, dY),
|
||||
Math.copySign(dZ * dZ * 0.4d, dZ)
|
||||
)
|
||||
);
|
||||
//刹车,避免偏激移动
|
||||
Whimsy$Brake(applyMovementEntity, 1, 1, 1);
|
||||
}
|
||||
}
|
||||
|
||||
//降低坠落伤害
|
||||
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 = deltaMovement.x > pMaxX ? 0 : deltaMovement.x;
|
||||
double dY = deltaMovement.y > pMaxY ? 0 : deltaMovement.y;
|
||||
double dZ = 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 = deltaMovement.x > 1 ? 0 : deltaMovement.x;
|
||||
double dY = deltaMovement.y > 1 ? 0 : deltaMovement.y;
|
||||
double dZ = 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直接退出
|
||||
//info -> Holder整理
|
||||
Pl$RestoreLeashFormSave();
|
||||
//默认值设为6.0f距离
|
||||
float leashLength = 6.0f;
|
||||
Entity entity = this.Pl$LeashData.leashHolder;
|
||||
//保存数据
|
||||
saveLeashData(Pl$LeashData);
|
||||
if(this instanceof ILivingEntityExtension iEntityExtension) {
|
||||
//获取设定值
|
||||
float leashLengthSelf = iEntityExtension.getLeashLength();
|
||||
leashLength = leashLengthSelf > 6 ? leashLengthSelf : 6;
|
||||
}
|
||||
if (entity != null) {
|
||||
if(!isAlive() || !entity.isAlive() || distanceTo(entity) > Math.max(leashLength * 2.0f, 10.0f)){
|
||||
//玩家死亡 或 持有者不存在 或 距离大于设定值的2倍(长度2倍若低于10格,则选10格) ,
|
||||
// 则取消拴绳关系,并掉落拴绳
|
||||
dropLeash(true, true);
|
||||
} else if(distanceTo(entity) > leashLength * 1.3f) {
|
||||
//大于1.3倍绳长则会让其跳跃(在<1.25格阻拦情况下,跳跃阻拦
|
||||
jumpFromGround();
|
||||
}
|
||||
}
|
||||
}
|
||||
@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
|
||||
public LeashData getLeashDataFromEntityData() {
|
||||
CompoundTag compoundTag = this.entityData.get(Pl$LEASH_DATA);
|
||||
return readLeashData(compoundTag);
|
||||
}
|
||||
@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);
|
||||
}
|
||||
@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);
|
||||
}
|
||||
@Inject(
|
||||
method = {"readAdditionalSaveData"}, at = {@At("RETURN")}
|
||||
)//数据读取
|
||||
private void readSaveData(CompoundTag pCompound, CallbackInfo ci) {
|
||||
if(pCompound.contains("Pl$LeashData")) {
|
||||
CompoundTag pl$LeashData = pCompound.getCompound("Pl$LeashData");
|
||||
this.entityData.set(Pl$LEASH_DATA, pl$LeashData);
|
||||
Pl$LeashData = readLeashData(pl$LeashData);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
package com.r3944realms.leashedplayer.mixin.client;
|
||||
|
||||
import net.minecraft.client.player.AbstractClientPlayer;
|
||||
import net.minecraft.client.renderer.entity.EntityRenderer;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||
|
||||
@Mixin(EntityRenderer.class)
|
||||
public abstract class MixinEntityRenderer {
|
||||
|
||||
@Redirect(
|
||||
method = {"renderLeash"},
|
||||
at = @At(
|
||||
value = "INVOKE",
|
||||
target = "Lnet/minecraft/world/entity/Entity;getLeashOffset(F)Lnet/minecraft/world/phys/Vec3;"
|
||||
)
|
||||
)
|
||||
private @NotNull Vec3 ret(Entity instance, float pPartialTick) {
|
||||
if(instance instanceof AbstractClientPlayer) {
|
||||
//为了使拴绳在在第三视角下位于玩家脖子处
|
||||
return instance.getLeashOffset(pPartialTick).add(0, -0.2, -0.2);
|
||||
}
|
||||
return instance.getLeashOffset(pPartialTick);//非实现这个接口则不变
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,97 @@
|
|||
package com.r3944realms.leashedplayer.mixin.client;
|
||||
|
||||
import com.mojang.blaze3d.vertex.PoseStack;
|
||||
import com.mojang.datafixers.util.Either;
|
||||
import com.r3944realms.leashedplayer.modInterface.IPlayerRendererExtension;
|
||||
import com.r3944realms.leashedplayer.modInterface.PlayerLeashable;
|
||||
import net.minecraft.client.Camera;
|
||||
import net.minecraft.client.DeltaTracker;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.multiplayer.ClientLevel;
|
||||
import net.minecraft.client.player.AbstractClientPlayer;
|
||||
import net.minecraft.client.renderer.*;
|
||||
import net.minecraft.client.renderer.entity.player.PlayerRenderer;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.util.Mth;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.entity.Leashable;
|
||||
import net.minecraft.world.entity.decoration.LeashFenceKnotEntity;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
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;
|
||||
import java.util.UUID;
|
||||
|
||||
@Mixin(LevelRenderer.class)
|
||||
public abstract class MixinLevelRenderer {
|
||||
@Shadow
|
||||
@Nullable
|
||||
private ClientLevel level;
|
||||
|
||||
@Shadow protected abstract void renderEntity(Entity pEntity, double pCamX, double pCamY, double pCamZ, float pPartialTick, PoseStack pPoseStack, MultiBufferSource pBufferSource);
|
||||
|
||||
@Shadow @Final
|
||||
private Minecraft minecraft;
|
||||
|
||||
@Shadow @Final private RenderBuffers renderBuffers;
|
||||
|
||||
@Inject(
|
||||
method = {"renderLevel"},
|
||||
at = @At(
|
||||
value = "INVOKE",
|
||||
target = "Lnet/minecraft/client/renderer/RenderBuffers;bufferSource()Lnet/minecraft/client/renderer/MultiBufferSource$BufferSource;",
|
||||
shift = At.Shift.AFTER
|
||||
)
|
||||
)
|
||||
private void renderLevel(DeltaTracker pDeltaTracker, boolean pRenderBlockOutline, Camera pCamera, GameRenderer pGameRenderer, LightTexture pLightTexture, Matrix4f pFrustumMatrix, Matrix4f pProjectionMatrix, CallbackInfo ci) {
|
||||
assert this.level != null;
|
||||
PoseStack poseStack = new PoseStack();
|
||||
MultiBufferSource.BufferSource multibuffersource$buffersource = this.renderBuffers.bufferSource();
|
||||
for(Entity entity : this.level.entitiesForRendering()) {
|
||||
//对于玩家实体拴绳渲染(从第一人称视角)
|
||||
if (entity instanceof AbstractClientPlayer abstractClientPlayer) {
|
||||
if(!(pCamera.getEntity() instanceof AbstractClientPlayer)) return;
|
||||
Minecraft mc = Minecraft.getInstance();
|
||||
PlayerRenderer playerRenderer = (PlayerRenderer) mc.getEntityRenderDispatcher().getRenderer(abstractClientPlayer);
|
||||
IPlayerRendererExtension playerRendererExtension = (IPlayerRendererExtension) playerRenderer;
|
||||
if (mc.options.getCameraType().isFirstPerson()) {
|
||||
Leashable.LeashData leashDataFromEntityData = ((PlayerLeashable) abstractClientPlayer).getLeashDataFromEntityData();
|
||||
if(leashDataFromEntityData == null) return;
|
||||
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 = playerRenderer.getRenderOffset(abstractClientPlayer, partialTickTime);
|
||||
double dX_ = dX + vec3.x();
|
||||
double dY_ = dY + vec3.y();
|
||||
double dZ_ = dZ + vec3.z();
|
||||
poseStack.pushPose();
|
||||
poseStack.translate(dX_, dY_, dZ_);
|
||||
ClientLevel level = mc.level;
|
||||
if (delayedLeashInfo.right().isPresent() && delayedLeashInfo.left().isEmpty()) {
|
||||
assert level != null;
|
||||
playerRendererExtension.renderLeashForCamera(pCamera, partialTickTime, poseStack, multibuffersource$buffersource, LeashFenceKnotEntity.getOrCreateKnot(level, delayedLeashInfo.right().get()));
|
||||
} else if (delayedLeashInfo.right().isEmpty() && delayedLeashInfo.left().isPresent()) {
|
||||
assert level != null;
|
||||
Player playerByUUID = level.getPlayerByUUID(delayedLeashInfo.left().get());
|
||||
if (playerByUUID != null) {
|
||||
playerRendererExtension.renderLeashForCamera(pCamera, partialTickTime, poseStack, multibuffersource$buffersource, playerByUUID);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,202 @@
|
|||
package com.r3944realms.leashedplayer.mixin.client;
|
||||
|
||||
import com.mojang.blaze3d.vertex.PoseStack;
|
||||
import com.mojang.blaze3d.vertex.VertexConsumer;
|
||||
import com.mojang.datafixers.util.Either;
|
||||
import com.r3944realms.leashedplayer.modInterface.IPlayerRendererExtension;
|
||||
import com.r3944realms.leashedplayer.modInterface.PlayerLeashable;
|
||||
import net.minecraft.client.Camera;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.model.PlayerModel;
|
||||
import net.minecraft.client.multiplayer.ClientLevel;
|
||||
import net.minecraft.client.player.AbstractClientPlayer;
|
||||
import net.minecraft.client.renderer.MultiBufferSource;
|
||||
import net.minecraft.client.renderer.RenderType;
|
||||
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.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.level.LightLayer;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
import org.joml.Matrix4f;
|
||||
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 java.util.UUID;
|
||||
|
||||
@Mixin(PlayerRenderer.class)
|
||||
public abstract class MixinPlayerRenderer extends LivingEntityRenderer<AbstractClientPlayer, PlayerModel<AbstractClientPlayer>> implements IPlayerRendererExtension {
|
||||
public MixinPlayerRenderer(EntityRendererProvider.Context pContext, PlayerModel<AbstractClientPlayer> pModel, float pShadowRadius) {
|
||||
super(pContext, pModel, pShadowRadius);
|
||||
}
|
||||
|
||||
@Inject(
|
||||
at = @At("HEAD"),
|
||||
method = "render(Lnet/minecraft/client/player/AbstractClientPlayer;FFLcom/mojang/blaze3d/vertex/PoseStack;Lnet/minecraft/client/renderer/MultiBufferSource;I)V"
|
||||
)
|
||||
private void renderMixin(AbstractClientPlayer pEntity, float pEntityYaw, float pPartialTicks, PoseStack pPoseStack, MultiBufferSource pBuffer, int pPackedLight, CallbackInfo ci) {
|
||||
Leashable.LeashData leashDataFromEntityData = ((PlayerLeashable) pEntity).getLeashDataFromEntityData();
|
||||
if(leashDataFromEntityData == null) return;
|
||||
Either<UUID, BlockPos> delayedLeashInfo = leashDataFromEntityData.delayedLeashInfo;
|
||||
if(delayedLeashInfo != null) {
|
||||
Minecraft mc = Minecraft.getInstance();
|
||||
ClientLevel level = mc.level;
|
||||
if (delayedLeashInfo.right().isPresent() && delayedLeashInfo.left().isEmpty()) {
|
||||
assert level != null;
|
||||
renderLeash(pEntity, pPartialTicks, pPoseStack, pBuffer, LeashFenceKnotEntity.getOrCreateKnot(level, delayedLeashInfo.right().get()));
|
||||
} else if (delayedLeashInfo.right().isEmpty() && delayedLeashInfo.left().isPresent()) {
|
||||
assert level != null;
|
||||
Player playerByUUID = level.getPlayerByUUID(delayedLeashInfo.left().get());
|
||||
if (playerByUUID != null) {
|
||||
renderLeash(pEntity, pPartialTicks, pPoseStack, pBuffer, playerByUUID);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* <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>
|
||||
* 这些数学运算主要用于计算实体在三维空间中的位置和方向,以确保在渲染链状结构(如拴住的绳索)时,链条能够跟随实体的移动和旋转并正确显示。在图形编程中,这些计算非常常见,尤其是在处理旋转、插值和光照效果时。
|
||||
*/
|
||||
@SuppressWarnings("AddedMixinMembersNamePattern")
|
||||
@Unique
|
||||
public <E extends net.minecraft.world.entity.Entity> void renderLeashForCamera(
|
||||
Camera camera,
|
||||
float partialTick,
|
||||
com.mojang.blaze3d.vertex.PoseStack poseStack,
|
||||
net.minecraft.client.renderer.MultiBufferSource bufferSource,
|
||||
E leashHolder
|
||||
) {
|
||||
|
||||
poseStack.pushPose();
|
||||
|
||||
// 获得绳索持有者的位置
|
||||
Vec3 leashHolderPosition = leashHolder.getRopeHoldPosition(partialTick);
|
||||
|
||||
// 获取当前观察的实体
|
||||
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 = this.getBlockLightLevel((AbstractClientPlayer) cameraEntity, cameraEntityBlockPos);
|
||||
int leashHolderBlockLightLevel = 0; //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();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,50 @@
|
|||
package com.r3944realms.leashedplayer.mixin.item;
|
||||
|
||||
import com.r3944realms.leashedplayer.modInterface.PlayerLeashable;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.world.InteractionResult;
|
||||
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);
|
||||
//自己
|
||||
PlayerLeashable self = (PlayerLeashable) pPlayer;
|
||||
//获取拴绳结实体
|
||||
LeashFenceKnotEntity leashfenceknotentity = LeashFenceKnotEntity.getOrCreateKnot(pLevel, pPos);
|
||||
//播放绳结被放置的声音
|
||||
leashfenceknotentity.playPlacementSound();
|
||||
//将自己与拴绳结绑定LeashData
|
||||
self.setLeashedTo(leashfenceknotentity, true);
|
||||
pLevel.gameEvent(GameEvent.BLOCK_ATTACH, pPos, GameEvent.Context.of(pPlayer));
|
||||
cir.setReturnValue(InteractionResult.SUCCESS);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,56 @@
|
|||
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.RelativeMovement;
|
||||
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> Whimsy$LeashPlayers = new ArrayList<>();
|
||||
@Inject(method = {"teleport(DDDFFLjava/util/Set;)V"}, at = {@At("HEAD")})
|
||||
private void teleportHead(double pX, double pY, double pZ, float pYaw, float pPitch, Set<RelativeMovement> pRelativeSet, CallbackInfo ci) {
|
||||
try {
|
||||
//獲取Holder
|
||||
this.Whimsy$LeashPlayers = ((PlayerLeashable)this.player).getLeashHolder() != null ? Collections.emptyList() : Objects.requireNonNull(this.player.getServer()).getPlayerList().getPlayers().stream().filter(serverPlayer -> (serverPlayer instanceof PlayerLeashable) && ((PlayerLeashable)serverPlayer).getLeashHolder() == this.player && player != serverPlayer).collect(Collectors.toList());
|
||||
} catch (Exception e) {
|
||||
logger.error("Internal Error:",e);
|
||||
}
|
||||
}
|
||||
@Inject(method = {"teleport(DDDFFLjava/util/Set;)V"}, at = {@At("TAIL")})
|
||||
private void teleportTail(double pX, double pY, double pZ, float pYaw, float pPitch, Set<RelativeMovement> pRelativeSet, CallbackInfo ci) {
|
||||
if(GameruleRegistry.getGameruleBoolValue(this.player.serverLevel(), TeleportWithLeashedPlayers.ID)) {
|
||||
for (Entity whimsy$LeashPlayer : this.Whimsy$LeashPlayers) {
|
||||
if(whimsy$LeashPlayer instanceof ServerPlayer) {
|
||||
if(whimsy$LeashPlayer instanceof PlayerLeashable playerLeashable) {
|
||||
playerLeashable.dropLeash(false,false);
|
||||
if(((ServerPlayer) playerLeashable).serverLevel() == this.player.serverLevel()) {
|
||||
((ServerPlayer) playerLeashable).connection.teleport(pX, pY, pZ, pYaw, pPitch, pRelativeSet);
|
||||
} else {
|
||||
((ServerPlayer) playerLeashable).teleportTo(this.player.serverLevel(), pX, pY, pZ, pYaw, pPitch);
|
||||
((ServerPlayer) playerLeashable).stopRiding();
|
||||
}
|
||||
playerLeashable.setLeashedTo(this.player, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
package com.r3944realms.leashedplayer.modInterface;
|
||||
|
||||
public interface ILivingEntityExtension {
|
||||
/**
|
||||
* 获取拴绳的长度
|
||||
* @return length 拴绳的长度(Float)
|
||||
*/
|
||||
float getLeashLength();
|
||||
|
||||
/**
|
||||
* 设置拴绳的长度
|
||||
* @param length 拴绳的长度(Float)
|
||||
*/
|
||||
void setLeashLength(float length);
|
||||
}
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
package com.r3944realms.leashedplayer.modInterface;
|
||||
|
||||
import net.minecraft.client.Camera;
|
||||
|
||||
public interface IPlayerRendererExtension {
|
||||
<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
|
||||
);
|
||||
}
|
||||
|
|
@ -0,0 +1,54 @@
|
|||
package com.r3944realms.leashedplayer.modInterface;
|
||||
|
||||
import net.minecraft.network.protocol.game.ClientboundSetEntityLinkPacket;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.entity.Leashable;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public interface PlayerLeashable extends Leashable {
|
||||
|
||||
/**
|
||||
* 获取拴绳的持有者实体 可能不存在
|
||||
*/
|
||||
@Nullable
|
||||
Entity getLeashHolder();
|
||||
|
||||
/**
|
||||
* 获取拴绳的持有者数据 (从EntityData中获取
|
||||
*/
|
||||
Leashable.LeashData getLeashDataFromEntityData();
|
||||
|
||||
/**
|
||||
* 是否立即可被拴住
|
||||
*/
|
||||
boolean canBeLeashedInstantly(Player player);
|
||||
|
||||
/**
|
||||
* 设置栓绳数据
|
||||
* @param pLeashHolder 拴绳持有者
|
||||
* @param pBroadcastPacket 是否广播包
|
||||
*/
|
||||
default void setLeashedTo(@NotNull Entity pLeashHolder, boolean pBroadcastPacket) {
|
||||
setLeashedTo((Entity & Leashable)this, pLeashHolder, pBroadcastPacket);
|
||||
}
|
||||
|
||||
static <E extends Entity & Leashable> void setLeashedTo(E pEntity, Entity pLeashHolder, boolean pBroadcastPacket) {
|
||||
Leashable.LeashData leashable$leashdata = pEntity.getLeashData();
|
||||
if (leashable$leashdata == null) {
|
||||
leashable$leashdata = new Leashable.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));
|
||||
}
|
||||
//这边覆写去掉了乘坐相关的逻辑,即乘坐状态下也可以正常被栓住,不影响其乘坐状态
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
package com.r3944realms.leashedplayer.utils.Enum;
|
||||
|
||||
public enum LanguageEnum {
|
||||
English("en_us"),
|
||||
SimpleChinese("zh_cn"),
|
||||
TraditionalChinese("zh_tw"),
|
||||
LiteraryChinese("lzh"),
|
||||
;
|
||||
public final String local;
|
||||
LanguageEnum(String local) {
|
||||
this.local = local;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
package com.r3944realms.leashedplayer.utils.Enum;
|
||||
|
||||
public enum ModPartEnum {
|
||||
DEFAULT,
|
||||
ITEM,
|
||||
BLOCK,
|
||||
ENCHANTMENT,
|
||||
ADVANCEMENT,
|
||||
CREATIVE_TAB,
|
||||
CONFIG,
|
||||
ENTITY,
|
||||
GUI,
|
||||
AUTHOR,
|
||||
TITLE,
|
||||
NAME,
|
||||
DESCRIPTION,
|
||||
INFO,
|
||||
MESSAGE,
|
||||
COMMAND,
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
package com.r3944realms.leashedplayer.utils;
|
||||
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public class Logger {
|
||||
public static final org.slf4j.Logger logger = LoggerFactory.getLogger(Logger.class);
|
||||
}
|
||||
13
src/main/java/com/r3944realms/leashedplayer/utils/Util.java
Normal file
13
src/main/java/com/r3944realms/leashedplayer/utils/Util.java
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
package com.r3944realms.leashedplayer.utils;
|
||||
|
||||
import com.r3944realms.leashedplayer.content.gamerules.Gamerules;
|
||||
|
||||
public class Util {
|
||||
public static String getGameruleName(Class<?> clazz) {
|
||||
return Gamerules.GAMERULE_PREFIX + clazz.getSimpleName();
|
||||
}
|
||||
public static String getGameruleName(String gamerulesName) {
|
||||
return Gamerules.GAMERULE_PREFIX + gamerulesName;
|
||||
}
|
||||
|
||||
}
|
||||
14
src/main/resources/META-INF/accesstransformer.cfg
Normal file
14
src/main/resources/META-INF/accesstransformer.cfg
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
#package-private -> public
|
||||
public net.minecraft.world.entity.Leashable$LeashData <init>(Lnet/minecraft/world/entity/Entity;)V # LeashData
|
||||
#priavte ->protected
|
||||
protected net.minecraft.client.renderer.entity.EntityRenderer renderLeash(Lnet/minecraft/world/entity/Entity;FLcom/mojang/blaze3d/vertex/PoseStack;Lnet/minecraft/client/renderer/MultiBufferSource;Lnet/minecraft/world/entity/Entity;)V # renderLeash
|
||||
#priavte ->protected
|
||||
protected net.minecraft.client.renderer.entity.EntityRenderer addVertexPair(Lcom/mojang/blaze3d/vertex/VertexConsumer;Lorg/joml/Matrix4f;FFFIIIIFFFFIZ)V # addVertexPair
|
||||
#这个方法原包为package-priavet 有时需要限制范围故修改
|
||||
public net.minecraft.world.level.GameRules$IntegerValue create(IIILjava/util/function/BiConsumer;)Lnet/minecraft/world/level/GameRules$Type; # create
|
||||
#因为'net.minecraft.world.level.GameRules.Type' 中<init>不为 public。无法从外部软件包访问
|
||||
public net.minecraft.world.level.GameRules$Type <init>(Ljava/util/function/Supplier;Ljava/util/function/Function;Ljava/util/function/BiConsumer;Lnet/minecraft/world/level/GameRules$VisitorCaller;)V # Type
|
||||
#因为'net.minecraft.world.level.GameRules.VisitorCaller' 在 'net.minecraft.world.level.GameRules' 中不为 public。无法从外部软件包访问
|
||||
public net.minecraft.world.level.GameRules$VisitorCaller #Interface
|
||||
#private -> public
|
||||
public net.minecraft.world.entity.Leashable$LeashData delayedLeashHolderId # delayedLeashHolderId
|
||||
|
|
@ -47,13 +47,13 @@ authors="${mod_authors}" #optional
|
|||
description='''${mod_description}'''
|
||||
|
||||
# The [[mixins]] block allows you to declare your mixin config to FML so that it gets loaded.
|
||||
#[[mixins]]
|
||||
#config="${mod_id}.mixins.json"
|
||||
[[mixins]]
|
||||
config="${mod_id}.mixins.json"
|
||||
|
||||
# The [[accessTransformers]] block allows you to declare where your AT file is.
|
||||
# If this block is omitted, a fallback attempt will be made to load an AT from META-INF/accesstransformer.cfg
|
||||
#[[accessTransformers]]
|
||||
#file="META-INF/accesstransformer.cfg"
|
||||
[[accessTransformers]]
|
||||
file="META-INF/accesstransformer.cfg"
|
||||
|
||||
# The coremods config file path is not configurable and is always loaded from META-INF/coremods.json
|
||||
|
||||
|
|
|
|||
19
src/main/resources/leashedplayer.mixins.json
Normal file
19
src/main/resources/leashedplayer.mixins.json
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
{
|
||||
"package": "com.r3944realms.leashedplayer.mixin",
|
||||
"mixins": [
|
||||
"both.MixinEntity",
|
||||
"both.MixinLivingEntity",
|
||||
"both.MixinPlayer",
|
||||
"item.MixinLeadItem",
|
||||
"server.MixinServerGamePacketListenerImpl"
|
||||
],
|
||||
"client": [
|
||||
"client.MixinEntityRenderer",
|
||||
"client.MixinLevelRenderer",
|
||||
"client.MixinPlayerRenderer"
|
||||
],
|
||||
"refmap": "whimsicality.refmap.json",
|
||||
"required": true,
|
||||
"minVersion": "0.8",
|
||||
"compatibilityLevel": "JAVA_17"
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user