麻花awa
本帖最后由 hemp 于 2019-6-6 12:43 编辑

起床战争小游戏大家应该都玩过(或听说过),他最神奇的地方就是,开始一个新游戏后,上一次游戏中玩家放置的方块,以及其他打斗痕迹就会消失不见。很多小游戏都能够回档地图(当然,是那些可以操作方块的)。对于要开发小游戏插件的开发者来说,实现地图回档也许是一个重要部分。
值得庆幸的是,BedwarsRel是一个开源的插件,我们可以分析源代码来了解它的地图回档机制



BedwarsRel的项目地址:https://github.com/BedwarsRel/BedwarsRel

由于项目较大,我们一点点翻会比较浪费时间,所以用github强大的代码搜索功能。
那么,搜什么呢?我们先想想有关回档的插件,例如Coreprotect,他的原理是记录所有的存档变化,然后回档的时候进行一次反操作。BedwarsRel会不会也是如此呢?
我们假设是这个原理,那么方块破坏事件一定少不了。对!我们先找找它对方块破坏事件的处理!!!
搜索框输入"BlockBreakEvent",然后自信的按下回车。


我们看到,这个插件里有两个关于此事件的处理器,那么哪个是呢?

  1. @EventHandler(priority = EventPriority.HIGH)
复制代码
这里说明他有很高优先级,回头想想,对于记录方块变化来说,这是不利的,因为他会被最先执行。这样的话如果后面事件被取消,那么记录与实际就矛盾了。再根据类名判断"TrapListener",这应该是涉及游戏中的玩法的。所以,第一个不是我们要找的!

那么接下来,我们就要到下面那个类里面去找我们想要的了。


L102-124的地方,我们发现这样一段代码


  1.     Material targetMaterial = g.getTargetMaterial();
  2.     if (e.getBlock().getType() == targetMaterial) {
  3.       e.setCancelled(true);

  4.       g.handleDestroyTargetMaterial(p, e.getBlock());
  5.       return;
  6.     }

  7.     Block breakedBlock = e.getBlock();

  8.     if (!g.getRegion().isPlacedBlock(breakedBlock)) {
  9.       if (breakedBlock == null) {
  10.         e.setCancelled(true);
  11.         return;
  12.       }

  13.       if (BedwarsRel.getInstance().isBreakableType(breakedBlock.getType())) {
  14.         g.getRegion().addBreakedBlock(breakedBlock);
  15.         e.setCancelled(false);
  16.         return;
  17.       }
复制代码


这应该就是我们要找的东西了。为什么这样说呢?

  1. g.getRegion().addBreakedBlock(breakedBlock);
复制代码
我们看这段代码,从字面意思上看是"在游戏区域添加已破坏的的方块",这就很接近开头提到的记录方块变化了。我们再看看Game类.
我们并不能找到getRegion()方法,因为它使用了"lombok"
我们直接找"region"这个变量就好了。

  1. private Region region = null;
复制代码


嗯....我们再看看Region

开头的一堆HashMap和List,让我们感觉到我们的猜测很可能是对的。
  1. public final static int CHUNK_SIZE = 16;
  2.   private HashMap<Block, Byte> breakedBlockData = null;
  3.   private HashMap<Block, BlockFace> breakedBlockFace = null;
  4.   private HashMap<Block, Boolean> breakedBlockPower = null;
  5.   private HashMap<Block, Integer> breakedBlockTypes = null;
  6.   private List<Block> breakedBlocks = null;
  7.   private List<Inventory> inventories = null;
  8.   private Location maxCorner = null;
  9.   private Location minCorner = null;
  10.   private String name = null;
  11.   private List<Block> placedBlocks = null;
  12.   private List<Block> placedUnbreakableBlocks = null;
  13.   private List<Entity> removingEntities = null;
  14.   private World world = null;
复制代码


但是我们不能妄下结论,我们应该看看这个类的方法。
reset()方法引起了我们的注意,这会不会就是我们开头提到的回档??


  1. 加载世界区块。
  2. 清空容器物品。
  3. 清理放置的方块(如果那个方块的位置不是空气的话就设置为空气)
  4. 恢复被破坏的的方块,并还原其数据(如朝向,是否被充能等)
  5. 恢复队伍的床。
  6. 清除掉落物
  7. 清除游戏玩法中生成的实体(比如TNT羊)
  8. 清除的敌对生物(以及箭)
  9. 其他生物设置离远后不会被刷掉。
复制代码


实锤!起床战争插件就是通过记录世界变化来实现回档的!!!我们也可以根据此实现类似的回档功能啦!!


a8105
emmmmmm
其实这种方法如果有其他插件通过setType改变了方块而且没与起床做兼容的话那个方块就永久消失了
我写过游戏插件,复原的方法是服主或其他人员要改变世界只能在Copy世界进行改变
Game世界改变后下次开服还是Copy世界的内容
说白了就是插件在开启服务器的时候
Copy世界的内容被复制到了Game世界

麻花awa
810587921 发表于 2019-6-7 06:33
emmmmmm
其实这种方法如果有其他插件通过setType改变了方块而且没与起床做兼容的话那个方块就永久消失了
我 ...

这是个不错的方法,几乎不会出现还原问题。
在运行过程中可以Copy吗?

a8105
hemp 发表于 2019-6-7 12:48
这是个不错的方法,几乎不会出现还原问题。
在运行过程中可以Copy吗?

Copy=实时加载或卸载世界
参考mv多世界插件

1915254255
666666666666666

481878616
大佬分析的不错,感谢

🍓🔥灵域
我之前写的小游戏插件,是通过卸载世界后,然后copy地图文件,在载入2333

知白倾黑
大佬,问一下,地图不重置怎么办

星辰sk
copy如果是多局游戏在一个世界就不能使用了(一个世界一个游戏资源消耗还是比较大的),所以状态记录也是有好处的

1478655928
已明白谢谢作者

第一页 上一页 下一页 最后一页