最近想要寫一個快速整地用跟建設用的minecraft bukkit plugin,畢竟在進行大建築物的建設時,常常要剷平一整個山丘,或者要鋪一整片地板,很費工夫而且很慢,按著shift後退加滑鼠右鍵也很累。
用worldedit的確可以達成同樣的目標,可是那又太容易了。這個fastbuild的目標,就是一個威力中等的plugin,不像worldedit這麼有破壞力,保留建築材料自己取得的挑戰,又比用鐵鏟跟專注狂點右鍵更快一些,可以把精力用在建築物的設計上。
總結來說,worldedit是要讓creative mode更creative mode;fastbuild則是要讓survival mode 更creative mode一點。

雖然已經有人寫過了類似的東西,在參考資料1 ,但它是mod,也沒有繼續更新,大體上我的目標和它是一樣的。

Spec

  1. 使用某個鍵或指令,目前設定是setn int,讀入使用者打入的數字。
  2. 之後破壞方塊和放置方塊時,可以一次破壞/放置該數字的方塊。
  3. 破壞/放置方塊時的方向,視破壞/放置方塊哪個面而定,破壞上面,就是向下破壞n格;放置上面,就是向上放置n格。
  4. 破壞方塊時,該數字目前先定在64,輸入超過數字會自動設在64。
  5. 破壞方塊時,若n格內有空氣方塊,則破壞會穿過該格空氣。
  6. 破壞方塊時,工具也會進行n次耐久減損判定,所以工具也要帶夠。
  7. 破壞方塊時,若用鏟子挖土,但n格內可能有岩石,這要視bukkit怎麼設計。
  8. 放置方塊時,該數字同樣先定在64,或者是放置時該stack所持有的方塊數量。
  9. 放置方塊時,若遇到其他方塊擋路,則只放到擋路方塊為止。
  10. 若以向上跳後向下放置方塊,一次放3個以上的方塊,可能會有窒息的風險或無法放置,這裡不處理。
  11. 不支援復原,蓋錯了就要自己打掉,拆錯了自己建回去,科科科。

實際設計

開發minecraft,想到java;提到java,想到Sun;說到Sun,想到eclipse(誤)。 主要參考minecraft bukkit server的文件(2) ,步驟很詳細沒遇上太多問題。
package name: io.github.yodalee.FastBuild

照tutorial 的步驟,創建FastBuild class,用FastBuildSetnCmd接受setn command,用hashedMap記錄每一個玩家目前設定的值。
Event Handler是主要要寫的地方,這裡就要參閱bukkit API的文件,我們這裡要處理的,大部分是在org.bukkit.event.block之下,像放置block的org.bukkit.event.block.BlockPlaceEvent,我們就是要處理這個事件。

@EventHandler
public void onPlace(BlockPlaceEvent event){
}

這樣就可以處理這個事件,看看要幹什麼。
例如BlockPlaceEvent這個class裡面有一個setCancelled的function,用來設定這個event要不要被bukkit處理,如果我們這樣寫

@EventHandler
public void onPlace(BlockPlaceEvent event){
 event.setCancelled(true);
}

那這個server就再也不能蓋東西了ww。 或者可以玩一點更勁爆的

@EventHandler
public void onPlace(BlockPlaceEvent event) {
  Player player = event.getPlayer();
  Block block = event.getBlockPlaced();
  if (block.getType() == Material.DIAMOND_BLOCK) {
    player.getWorld().strikeLightning(player.getLocation());
  }
}

算是某種禁奢條款,只要放置鑽石方塊想炫富就會被雷劈XD。

回到正題,我們要做的,其實就是從這個place Event裡,取出玩家放置的位子、方位,然後用for loop 重複呼叫placeBlockEvent即可。 目前bukkit好像無法呼叫「在某地放置方塊」的function,因此先用setType來實作,直接取代方塊。

Player player = event.getPlayer();
Block block = event.getBlockPlaced();
Block againstBlock = event.getBlockAgainst();
BlockFace face = againstBlock.getFace(block);
Block nextBlock = null;
ItemStack stackInHand = event.getItemInHand();
int stackAmount = stackInHand.getAmount();
int n,i;

//get n
if (plugin.playerN.containsKey(player)) {
  n = plugin.playerN.get(player);
} else {
  n = 1;
}
n = Math.min(n, stackAmount);

stackInHand.setAmount(stackInHand.getAmount() -1);
//build
if (face != null) {
  for ( i = 0 ; i < n-1 ; i++) {
    nextBlock = block.getRelative(face);
    if (checkReplaceable(nextBlock)) {
      nextBlock.setType(stackInHand.getType());
      block = nextBlock;
    } else {
      break;
    }
  }
}

// reduce itemStack in hand
if (player.getGameMode() != GameMode.CREATIVE) {
  stackInHand.setAmount(stackInHand.getAmount() - n);
  player.setItemInHand(stackInHand);
}

Build大體上的實作就是這樣,其實不算太難。 Break的部分,因為牽扯到手中工具耐久的問題,也許下次再來寫=w=。

Demo:

原始碼:

本程式公開所有原始碼,歡迎修改後丟pull request,這樣我也不用想break方塊怎麼寫了(誤)
FastBuild

參考資料

  1. minecraft mod fastbuild
  2. bukkit Plugin Tutorial
  3. YAML definition
  4. Bukkit API Overview ,喔這妖受好用的