1943 words
10 minutes
13 给方块上了保护
13 给方块上了保护
@EventBusSubscriber
public class BeforeBlockBreakHandler { // 如果方块上了保护了,就取消这次的破坏事件
@SubscribeEvent
public static void OnBlockBreak (BlockEvent.BreakEvent event){
BlockPos pos = event.getPos();
int level = BlockEnchantmentStorage.getLevel(Enchantments.PROTECTION, pos);
if (level>0 && !event.getPlayer().isCreative()){
event.setCanceled(true);
}
}
}
这里有一个问题就是,对于上了附魔的方块,放置在世界上之后我们并不知道这个方块是否是一个附魔了的方块,所以我们需要写一个方法,用于保存对应的拥有附魔的方块放置到了什么位置,记录这个位置和对应这个位置方块拥有的附魔.
/**
* 此类提供存储和检索方块附魔信息的方法。
*/
public class BlockEnchantmentStorage {
/**
* 将附魔信息添加到指定的方块位置。
*
* @param blockPos 要添加附魔信息的方块位置
* @param enchantments 附魔信息列表
*/
public static void addBlockEnchantment(BlockPos blockPos, ListTag enchantments) {
MinecraftServer server = ServerManager.getServerInstance(); // 获取服务器实例
// 创建 StateSaverAndLoader 实例
BlockStateSaverAndLoader state = BlockStateSaverAndLoader.getServerState(server);
// 将方块附魔信息添加到列表中
state.blockEnchantments.add(new BlockStateSaverAndLoader.BlockEnchantInfo(blockPos, enchantments));
}
/**
* 移除指定方块位置的附魔信息。
*
* @param blockPos 要移除附魔信息的方块位置
*/
public static void removeBlockEnchantment(BlockPos blockPos) {
MinecraftServer server = ServerManager.getServerInstance(); // 获取服务器实例
// 获取 BlockStateSaverAndLoader 实例
BlockStateSaverAndLoader state = BlockStateSaverAndLoader.getServerState(server);
// 调用 StateSaverAndLoader 类中的方法来移除指定位置的方块附魔信息
state.removeBlockEnchantment(blockPos);
}
/**
* 获取指定方块位置的附魔信息。
*
* @param blockPos 要获取附魔信息的方块位置
* @return 返回方块位置的附魔信息列表,如果没有找到则返回空列表
*/
public static ListTag getEnchantmentsAtPosition(BlockPos blockPos) {
MinecraftServer server = ServerManager.getServerInstance(); // 获取服务器实例
// 获取 BlockStateSaverAndLoader 实例
BlockStateSaverAndLoader state = BlockStateSaverAndLoader.getServerState(server);
// 遍历附魔信息列表,找到指定位置的方块附魔信息
for (BlockStateSaverAndLoader.BlockEnchantInfo blockEnchantment : state.blockEnchantments) {
if (blockEnchantment.blockPos.equals(blockPos)) {
// 返回指定位置的方块附魔名称
return blockEnchantment.enchantments;
}
}
// 如果没有找到指定位置的方块附魔信息,则返回空列表
return new ListTag();
}
/**
* 获取指定方块位置的特定附魔的等级。
*
* @param enchantment 要查询的附魔类型
* @param blockPos 要查询附魔的方块位置
* @return 返回附魔等级,如果没有找到则返回0
*/
public static int getLevel(ResourceKey<Enchantment> enchantment, BlockPos blockPos) {
MinecraftServer server = ServerManager.getServerInstance(); // 获取服务器实例
// 获取方块的附魔信息
ListTag enchantments = getEnchantmentsAtPosition(blockPos);
// 遍历附魔信息
for (int i = 0; i < enchantments.size(); i++) {
// 获取单个附魔信息
CompoundTag enchantmentInfo = enchantments.getCompound(i);
// 提取附魔名称和等级
String enchantmentName = enchantmentInfo.getString("id");
int level = enchantmentInfo.getInt("lvl");
// 检查附魔名称是否匹配
if (enchantmentName.equals(String.valueOf(enchantment))) {
// 返回附魔等级
return level;
}
}
// 如果没有找到匹配的附魔信息,默认返回0
return 0;
}
}
在上述的代码中我们使用到了ServerManager
这个类,这个类是我们自己写的,主要用于世界初始化的时候获得对应的MinecraftServer
对象.
@EventBusSubscriber
public class ServerManager
{
public static MinecraftServer serverInstance;
public static void setServerInstance(MinecraftServer server){
serverInstance = server;
}
public static MinecraftServer getServerInstance(){
return serverInstance;
}
@SubscribeEvent
public static void onServerStarted(ServerStartedEvent event){
serverInstance = event.getServer();
}
}
这里的存储和读取都是依赖于BlockStateSaverAndLoader
,这个类是保存了对应的list的类,并且负责序列化和反序列化当前的list.即保存和读取,使用了SavedData
这个技术,将对应的nbt数据存储到了对应的维度下.这个存储是维度级别的,即这个数据是在维度共享的.你可以通过这个实现一个跨纬度传送的一些内容.(可以见boson的levelsavedata章节,1.20.4版本的代码可以看我教程)
/**
* 此类用于存储和加载方块的附魔信息。
*/
public class BlockStateSaverAndLoader extends SavedData {
/**
* 存储所有方块的附魔信息的列表。
*/
public static List<BlockEnchantInfo> blockEnchantments = new CopyOnWriteArrayList<>();
/**
* 用于存储单个方块的附魔信息。
*/
public static class BlockEnchantInfo {
public BlockPos blockPos; // 附魔方块的位置
public ListTag enchantments; // 附魔的NBT数据
/**
* 构造函数,初始化方块位置和附魔数据。
* @param blockPos 附魔方块的位置
* @param enchantments 附魔的NBT数据
*/
public BlockEnchantInfo(BlockPos blockPos, ListTag enchantments) {
this.blockPos = blockPos;
this.enchantments = enchantments;
}
/**
* 重写equals方法,用于比较两个BlockEnchantInfo对象是否相等。
* @param o 要比较的对象
* @return 如果对象相同或包含相同的blockPos和enchantments则返回true,否则返回false
*/
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
BlockEnchantInfo that = (BlockEnchantInfo) o;
return Objects.equals(blockPos, that.blockPos) &&
Objects.equals(enchantments, that.enchantments);
}
/**
* 重写hashCode方法,用于计算对象的哈希码。
* @return 基于blockPos和enchantments的哈希码
*/
@Override
public int hashCode() {
return Objects.hash(blockPos, enchantments);
}
}
/**
* 覆盖save方法,用于将方块附魔信息保存到NBT标签中。
* @param tag 要保存的NBT标签
* @param registries 注册表提供者
* @return 包含方块附魔信息的NBT标签
*/
@Override
public CompoundTag save(CompoundTag tag, HolderLookup.Provider registries) {
ListTag blockEnchantmentsList = new ListTag(); // 创建NBT列表
for (BlockEnchantInfo blockEnchantment : blockEnchantments) {
CompoundTag blockEnchantmentNbt = new CompoundTag(); // 创建NBT标签
blockEnchantmentNbt.putIntArray("BlockPos", new int[]{blockEnchantment.blockPos.getX(), blockEnchantment.blockPos.getY(), blockEnchantment.blockPos.getZ()}); // 将方块位置存储为整数数组
blockEnchantmentNbt.put("Enchantments", blockEnchantment.enchantments); // 将附魔信息存储到NBT标签
blockEnchantmentsList.add(blockEnchantmentNbt); // 将NBT标签添加到列表
}
tag.put("BlockEnchantments", blockEnchantmentsList); // 将列表存储到NBT标签
return tag; // 返回包含方块附魔信息的NBT标签
}
/**
* 从NBT标签创建BlockStateSaverAndLoader对象。
* @param nbt 包含方块附魔信息的NBT标签
* @param lookup 注册表提供者
* @return 创建的BlockStateSaverAndLoader对象
*/
public static BlockStateSaverAndLoader createFromNbt(CompoundTag nbt, HolderLookup.Provider lookup) {
BlockStateSaverAndLoader state = new BlockStateSaverAndLoader(); // 创建对象
// 读取方块附魔信息
ListTag blockEnchantmentsList = nbt.getList("BlockEnchantments", 10); // 获取NBT列表
for (int i = 0; i < blockEnchantmentsList.size(); i++) {
CompoundTag blockEnchantmentNbt = blockEnchantmentsList.getCompound(i); // 获取NBT标签
int[] posArray = blockEnchantmentNbt.getIntArray("BlockPos"); // 获取方块位置
BlockPos blockPos = new BlockPos(posArray[0], posArray[1], posArray[2]);
ListTag enchantments = blockEnchantmentNbt.getList("Enchantments", 10); // 获取附魔信息
// 将读取的方块附魔信息添加到列表中
state.blockEnchantments.add(new BlockEnchantInfo(blockPos, enchantments));
}
return state; // 返回创建的对象
}
/**
* 定义创建BlockStateSaverAndLoader对象的工厂。
*/
private static Factory<BlockStateSaverAndLoader> type = new Factory<>(
BlockStateSaverAndLoader::new, // 若不存在 'BlockStateSaverAndLoader' 则创建
BlockStateSaverAndLoader::createFromNbt, // 若存在 'BlockStateSaverAndLoader' NBT, 则调用 'createFromNbt' 传入参数
null // 此处理论上应为 'DataFixTypes' 的枚举,但我们直接传递为空(null)也可以
);
/**
* 从Minecraft服务器获取BlockStateSaverAndLoader状态。
* @param server Minecraft服务器实例
* @return BlockStateSaverAndLoader状态对象
*/
public static BlockStateSaverAndLoader getServerState(MinecraftServer server) {
if(server!=null) {
DimensionDataStorage persistentStateManager = server.getLevel(Level.OVERWORLD).getDataStorage(); // 获取持久状态管理器
// 当第一次调用了方法 'getOrCreate' 后,它会创建新的 'BlockStateSaverAndLoader' 并将其存储于 'PersistentStateManager' 中。
// 'getOrCreate' 的后续调用将本地的 'BlockStateSaverAndLoader' NBT 传递给 'BlockStateSaverAndLoader::createFromNbt'。
BlockStateSaverAndLoader state = persistentStateManager.computeIfAbsent(type, NeoMafishMod.MODID + "_block_enchantments"); // 获取或创建状态对象
// 若状态未标记为脏(dirty),当 Minecraft 关闭时, 'writeNbt' 不会被调用,相应地,没有数据会被保存。
// 从技术上讲,只有在事实上发生数据变更时才应当将状态标记为脏(dirty)。
// 但大多数开发者和模组作者会对他们的数据未能保存而感到困惑,所以不妨直接使用 'markDirty' 。
// 另外,这只将对应的布尔值设定为 TRUE,代价是文件写入磁盘时模组的状态不会有任何改变。(这种情况非常少见)
state.setDirty(); // 标记状态为脏,确保数据被保存
return state; // 返回状态对象
}
return null; // 如果服务器为空,则返回null
}
/**
* 移除指定位置的方块附魔信息。
* @param targetBlockPos 要移除附魔的方块位置
*/
public void removeBlockEnchantment(BlockPos targetBlockPos) {
// 使用removeIf方法移除列表中符合条件的元素
blockEnchantments.removeIf(blockEnchantment -> blockEnchantment.blockPos.equals(targetBlockPos));
}
}
至于如何将方块添加到list的我只需要,使用放置方块的事件或者mixin方块放置的方法即可.之后会将讲解代码.