Back

小组项目开发记录:ShulkerBoxDrop 从初版到重构版

从零开始的探索

技术基础薄弱,但热情满满

第一版实现了最基础的功能监盒子的破坏时间
同时实现盒子的复制逻辑 代码结构也比较简单 有些逻辑写死为后续更改全部推翻原有逻辑 应为经验不足 导致后面出现了许多bug
例如: 游戏内有多种染色盒子但是插件仅仅支持一种 同时因为没有考虑盒子内物品的数据 导致复制后盒子内的所有物品信息全部丢失 代码全部卸载了一个java类里面 后面优化各种函数错误 同时盒子数量的复制条件写死在了源码里面 导致每次想快速测试都需要从新更改源码
例如下述代码 就是逻辑写死 导致物品内部数据丢失 为后续埋雷

private void dropShulkerBoxes(BlockBreakEvent event) {
        UUID playerUUID = event.getPlayer().getUniqueId();
        int count = (Integer)this.shulkerBoxCount.getOrDefault(playerUUID, 0);
        ++count;
        if (count == 10) {
            event.setDropItems(false);
            event.getBlock().getWorld().dropItemNaturally(event.getBlock().getLocation(), new ItemStack(Material.SHULKER_BOX, 2));
            count = 0;
        }

        this.shulkerBoxCount.put(playerUUID, count);
    }

小组成员分工

  • 边世恒 : 负责主逻辑和时间监听以及处理后续bug
  • 薛锡冰 : 负责测试插件反馈bug
  • 宋欣雨 : 负责文档与README的修改工作
  • 左广圆 : 负责项目结构整理

最终版项目:重构、优化与专业化

随着我们经验的增长,我们决定对此项目的屎山代码来一次彻底的重构

代码结构全面升级

优化包结构
逻辑处理做模块化设计 主类调用父类
加入并启用配置文件 如 : config.yml & plugin.yml
更改掉落逻辑 实现支持游戏内所有盒子以及保存盒子内部数据
同时兼容使用低版本jdk构建支持更多版本服务端
最终版不仅功能更完善,代码也更专业,真正达到了可发布、可维护的水平
同时让我们的代码质量不断提高 可读性不断优化 代码行数从几十行上升到上百行 <br

从第一版到最终版:我们学到了什么

团队协作的重要性

从最初的各写各的,到后来的分工明确、互相代码审查,我们的协作能力提升非常明显

代码质量不是一蹴而就的

第一版能跑,但有许多bug
最终版不仅能跑,还能维护、能扩展

具链的使用

  • GitHub
  • Issues
  • Pull Request
  • 版本管理
  • 文档编写 这些都是我们在项目中真正学会的技能

代码展示

第一版

package hy.shulkerboxdrop;

import java.util.HashMap;
import java.util.Objects;
import java.util.UUID;
import org.bukkit.Bukkit;
import org.bukkit.block.BlockState;
import org.bukkit.block.ShulkerBox;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;
import org.bukkit.command.PluginCommand;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.block.BlockBreakEvent;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.BlockStateMeta;
import org.bukkit.inventory.meta.ItemMeta;
import org.bukkit.plugin.java.JavaPlugin;

public final class ShulkerBoxDrop extends JavaPlugin implements Listener {
    private static final String PLUGIN_NAME = "[ShulkerBoxDrop]";
    private PluginState pluginState;
    private static final int SHULKER_BOX_BREAK_LIMIT = 10;
    private final HashMap<UUID, Integer> shulkerBoxCount;

    public ShulkerBoxDrop() {
        this.pluginState = ShulkerBoxDrop.PluginState.ENABLED;
        this.shulkerBoxCount = new HashMap();
    }
//开关
    public void onEnable() {
        this.getServer().getPluginManager().registerEvents(this, this);
        ((PluginCommand)Objects.requireNonNull(this.getCommand("shulkerboxdrop"))).setExecutor(this);
        Bukkit.getConsoleSender().sendMessage("[ShulkerBoxDrop]§aEnable");
   
    }

    public void onDisable() {
        Bukkit.getConsoleSender().sendMessage("[ShulkerBoxDrop]§cDisable");
   
    }
// 挖掘逻辑
    private void dropShulkerBoxes(BlockBreakEvent event) {
        UUID playerUUID = event.getPlayer().getUniqueId();
        int count = (Integer)this.shulkerBoxCount.getOrDefault(playerUUID, 0);
        ++count;
        if (count == 10) {
            event.setDropItems(false);
            BlockState blockState = event.getBlock().getState();
            if (blockState instanceof ShulkerBox) {
                ShulkerBox shulkerBox = (ShulkerBox)blockState;
                Inventory inventory = shulkerBox.getInventory();
                ItemStack itemStack = new ItemStack(event.getBlock().getType(), 2);
                ItemMeta itemMeta = itemStack.getItemMeta();
                if (itemMeta instanceof BlockStateMeta) {
                    BlockStateMeta blockStateMeta = (BlockStateMeta)itemMeta;
                    BlockState blockStateCopy = blockStateMeta.getBlockState();
                    if (blockStateCopy instanceof ShulkerBox) {
                        ShulkerBox shulkerBoxCopy = (ShulkerBox)blockStateCopy;
                        shulkerBoxCopy.getInventory().setContents(inventory.getContents());
                        blockStateMeta.setBlockState(shulkerBoxCopy);
                        itemStack.setItemMeta(blockStateMeta);
                    }
                }

                event.getBlock().getWorld().dropItemNaturally(event.getBlock().getLocation(), itemStack);
            }

            count = 0;
        }

        this.shulkerBoxCount.put(playerUUID, count);
    }

    @EventHandler
    public void onBlockBreak(BlockBreakEvent event) {
        if (this.pluginState == ShulkerBoxDrop.PluginState.ENABLED && event.getBlock().getType().isBlock()) {
            this.dropShulkerBoxes(event);
        }

    }
//指令
    public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
        if (command.getName().equalsIgnoreCase("shulkerboxdrop")) {
            if (sender instanceof Player) {
                this.pluginState = this.pluginState == ShulkerBoxDrop.PluginState.ENABLED ? ShulkerBoxDrop.PluginState.DISABLED : ShulkerBoxDrop.PluginState.ENABLED;
                sender.sendMessage("[ShulkerBoxDrop] is now " + this.pluginState + ".");
            } else {
                sender.sendMessage("This command can only be used by players.");
            }

            return true;
        } else {
            return false;
        }
    }

    private static enum PluginState {
        ENABLED,
        DISABLED;
    }
}
comments powered by Disqus
Built with Hugo
Theme Stack designed by Jimmy
© Licensed Under CC BY-NC-SA 4.0