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

BedwarsRel的项目地址:https://github.com/BedwarsRel/BedwarsRel
由于项目较大,我们一点点翻会比较浪费时间,所以用github强大的代码搜索功能。
那么,搜什么呢?我们先想想有关回档的插件,例如Coreprotect,他的原理是记录所有的存档变化,然后回档的时候进行一次反操作。BedwarsRel会不会也是如此呢?
我们假设是这个原理,那么方块破坏事件一定少不了。对!我们先找找它对方块破坏事件的处理!!!
搜索框输入"BlockBreakEvent",然后自信的按下回车。
复制代码这里说明他有很高优先级,回头想想,对于记录方块变化来说,这是不利的,因为他会被最先执行。这样的话如果后面事件被取消,那么记录与实际就矛盾了。再根据类名判断"TrapListener",这应该是涉及游戏中的玩法的。所以,第一个不是我们要找的!
在L102-124的地方,我们发现这样一段代码
复制代码
这应该就是我们要找的东西了。为什么这样说呢?
复制代码我们看这段代码,从字面意思上看是"在游戏区域添加已破坏的的方块",这就很接近开头提到的记录方块变化了。我们再看看Game类.
我们并不能找到getRegion()方法,因为它使用了"lombok"
我们直接找"region"这个变量就好了。
复制代码
嗯....我们再看看Region类
开头的一堆HashMap和List,让我们感觉到我们的猜测很可能是对的。
复制代码
但是我们不能妄下结论,我们应该看看这个类的方法。
reset()方法引起了我们的注意,这会不会就是我们开头提到的回档??
复制代码
实锤!起床战争插件就是通过记录世界变化来实现回档的!!!我们也可以根据此实现类似的回档功能啦!!
起床战争小游戏大家应该都玩过(或听说过),他最神奇的地方就是,开始一个新游戏后,上一次游戏中玩家放置的方块,以及其他打斗痕迹就会消失不见。很多小游戏都能够回档地图(当然,是那些可以操作方块的)。对于要开发小游戏插件的开发者来说,实现地图回档也许是一个重要部分。
值得庆幸的是,BedwarsRel是一个开源的插件,我们可以分析源代码来了解它的地图回档机制。

BedwarsRel的项目地址:https://github.com/BedwarsRel/BedwarsRel
由于项目较大,我们一点点翻会比较浪费时间,所以用github强大的代码搜索功能。
那么,搜什么呢?我们先想想有关回档的插件,例如Coreprotect,他的原理是记录所有的存档变化,然后回档的时候进行一次反操作。BedwarsRel会不会也是如此呢?
我们假设是这个原理,那么方块破坏事件一定少不了。对!我们先找找它对方块破坏事件的处理!!!
搜索框输入"BlockBreakEvent",然后自信的按下回车。
我们看到,这个插件里有两个关于此事件的处理器,那么哪个是呢?
- @EventHandler(priority = EventPriority.HIGH)
那么接下来,我们就要到下面那个类里面去找我们想要的了。
在L102-124的地方,我们发现这样一段代码
- Material targetMaterial = g.getTargetMaterial();
- if (e.getBlock().getType() == targetMaterial) {
- e.setCancelled(true);
- g.handleDestroyTargetMaterial(p, e.getBlock());
- return;
- }
- Block breakedBlock = e.getBlock();
- if (!g.getRegion().isPlacedBlock(breakedBlock)) {
- if (breakedBlock == null) {
- e.setCancelled(true);
- return;
- }
- if (BedwarsRel.getInstance().isBreakableType(breakedBlock.getType())) {
- g.getRegion().addBreakedBlock(breakedBlock);
- e.setCancelled(false);
- return;
- }
这应该就是我们要找的东西了。为什么这样说呢?
- g.getRegion().addBreakedBlock(breakedBlock);
我们并不能找到getRegion()方法,因为它使用了"lombok"
我们直接找"region"这个变量就好了。
- private Region region = null;
嗯....我们再看看Region类
开头的一堆HashMap和List,让我们感觉到我们的猜测很可能是对的。
- public final static int CHUNK_SIZE = 16;
- private HashMap<Block, Byte> breakedBlockData = null;
- private HashMap<Block, BlockFace> breakedBlockFace = null;
- private HashMap<Block, Boolean> breakedBlockPower = null;
- private HashMap<Block, Integer> breakedBlockTypes = null;
- private List<Block> breakedBlocks = null;
- private List<Inventory> inventories = null;
- private Location maxCorner = null;
- private Location minCorner = null;
- private String name = null;
- private List<Block> placedBlocks = null;
- private List<Block> placedUnbreakableBlocks = null;
- private List<Entity> removingEntities = null;
- private World world = null;
但是我们不能妄下结论,我们应该看看这个类的方法。
reset()方法引起了我们的注意,这会不会就是我们开头提到的回档??
- 加载世界区块。
- 清空容器物品。
- 清理放置的方块(如果那个方块的位置不是空气的话就设置为空气)
- 恢复被破坏的的方块,并还原其数据(如朝向,是否被充能等)
- 恢复队伍的床。
- 清除掉落物
- 清除游戏玩法中生成的实体(比如TNT羊)
- 清除的敌对生物(以及箭)
- 其他生物设置离远后不会被刷掉。
实锤!起床战争插件就是通过记录世界变化来实现回档的!!!我们也可以根据此实现类似的回档功能啦!!
emmmmmm
其实这种方法如果有其他插件通过setType改变了方块而且没与起床做兼容的话那个方块就永久消失了
我写过游戏插件,复原的方法是服主或其他人员要改变世界只能在Copy世界进行改变
Game世界改变后下次开服还是Copy世界的内容
说白了就是插件在开启服务器的时候
Copy世界的内容被复制到了Game世界
其实这种方法如果有其他插件通过setType改变了方块而且没与起床做兼容的话那个方块就永久消失了
我写过游戏插件,复原的方法是服主或其他人员要改变世界只能在Copy世界进行改变
Game世界改变后下次开服还是Copy世界的内容
说白了就是插件在开启服务器的时候
Copy世界的内容被复制到了Game世界
810587921 发表于 2019-6-7 06:33
emmmmmm
其实这种方法如果有其他插件通过setType改变了方块而且没与起床做兼容的话那个方块就永久消失了
我 ...
这是个不错的方法,几乎不会出现还原问题。
在运行过程中可以Copy吗?
666666666666666
大佬分析的不错,感谢
我之前写的小游戏插件,是通过卸载世界后,然后copy地图文件,在载入2333
大佬,问一下,地图不重置怎么办
copy如果是多局游戏在一个世界就不能使用了(一个世界一个游戏资源消耗还是比较大的),所以状态记录也是有好处的
已明白谢谢作者