2014年4月24日 星期四

在Archlinux 上編譯qtiplot

最近因為寫論文的關係,要作不少數據模擬、量測的圖,一般實驗室都是用Origin來作圖,不過我不知道為啥,在虛擬機裡面用Origin都會GG,一堆功能一點就當;而且Origin又不開源,吃土去吧。
這類的東西有沒有開源的替代方案呢?當然有。
Qtiplot SciDavis
兩套的介面其實非常像,不過qtiplot的功能比較完整。

--

用一用之後,就會對qtiplot愈來愈不爽()
反正有一些些功能還是怪怪的,例如改了range之後整張圖要重畫。
不過這是開源軟體,不像Origin不爽就只能不爽,想了一下決定立馬加入這個專案,反正這個軟體可能還要用一陣子。

結果載了最新的0.9.8.9的原始碼下來,第一大難關:不能編譯,費了不少勁才解掉。
首先要先裝必要的套件:qt4, vtk, boost lib, qt-assistant-compat,老實說多到我都有點記不住了。

然後有不少修改要做,主要是參考:
這個AUR包括了編譯成功需要的修改,果然Archlinux開發者都好猛OAO
照著改完就能成功編譯惹。

--

當然編成功了還是要mur一下,完全無法理解幹嘛把project搞到這麼難編譯,我真的覺得一個project容易編譯與否會直接關乎有多少開發者想要參與,編譯成功才能進行修改的測試,這是開始改project的第一步。
當然說回來,這個project也有一到兩年沒動過,大概也脫離密集開發期了。
現在至少累積了400features在排隊,編譯遇到的問題在原網站(BerilOS)幾乎都沒答案,而是分散到Debian, Archlinux, LaunchPad的幾個大的bug report去了,這大概是一個project在末期自然的狀況吧。

Reference:
1. qtiplot:
2. SciDavis:
3. qtiplot develop webpage:

2014年4月14日 星期一

使用git squash 合併commit

小弟之前一直有個習慣,每次寫程式都要寫到結果正確了,才把該commit的commit;這樣造成的結果是,常常累積了數百行的差異才commit,要是中途不小心手滑了一下,辛苦就全化作流水了。

阿蹦大神曰:不用結果正確,編譯可過就commit
這樣…不是會跑出一堆亂七八槽的commit嗎?

這就要用到git squash功能了
比如說現在我隨便commit一些版本,log顯示為:
commit 7549a19b591f1c802addf9b2344be2f607beff42
Date: Mon Apr 14 16:05:38 2014 +0000

    more line num

commit a15d1dde304646d542dc9cb596afbcd900a609c7
Date: Mon Apr 14 16:05:22 2014 +0000

    line num

commit 265e09db81b1c6aff81a6bedcd3a0e2f22e55acc
Date: Mon Apr 14 16:04:49 2014 +0000

    initial commit

如果要合併line num, more line num兩個版本:
$ git rebase -i 265e09db81b1c6aff81a6bedcd3a0e2f22e55acc

然後編輯將
pick a15d1dd line num
pick 7549a19 more line num
要squash起來的commit編輯為squash, 或fixup,前者會保留squash的commit message,後者只用最新的commit message,先改成:
pick a15d1dd line num
squash 7549a19 more line num

再來它會要求你修改commit message,這就隨便你改,預設是把兩個訊息寫在一起。
commit 0a947a06fd258e07615fb236696b8f31f04f4043
Date: Mon Apr 14 16:05:22 2014 +0000
    line num
    more line num

commit 265e09db81b1c6aff81a6bedcd3a0e2f22e55acc
Date: Mon Apr 14 16:04:49 2014 +0000

    initial commit

以後就寫個段落就commit一下,等到功能都寫完了,再全部squash起來即可。
參考資料: man git rebase
致謝: 本文感謝傳說中的阿蹦大神指導

2014年3月17日 星期一

使用python struct實作Dex file parser

最近因為學校作業的關係,開始碰一些android的相關內容;有一個作業要我們寫一個程式去改android dex file的opCode,不過我實力不足,最後用smali/baksmali+shell來實作,一整個就不是熟練的程式人該作的事O_O。
不過,為了補一下實力不足,還是用python 寫了一個Dex file parser:

因為Dex file已經包成binary的形式,要去parse其中的內容,最方便的套件就是python struct了,在這裡以Dex為例,來介紹python struct的使用:

要使用python struct,首先要寫出format string,struct 會依照format string對資料進行處理。
基本的格式是“nX”,其中n為重複數字,X為格式化代碼,例如I代表unsigned int_32。 前面還有endian的符號,這部分請自行查閱參考資料。
有了format string就可以呼叫struct的function: pack, unpack,它們會照format string,把資料寫到binary或從binary讀成一個tuple。

 例如:我們可以看到dex 35的header為:
typedefstructDexHeader {
u1 magic[8]; /*includes version number */
u4 checksum; /*adler32 checksum */
u1 signature[kSHA1DigestLen]; /*SHA-1 hash */
u4 fileSize; /*length of entire file */
u4 headerSize; /*offset to start of next section */
u4 endianTag;
u4 linkSize;
u4 linkOff;
u4 mapOff;
u4 stringIdsSize;
u4 stringIdsOff;
u4 typeIdsSize;
u4 typeIdsOff;
u4 protoIdsSize;
u4 protoIdsOff;
u4 fieldIdsSize;
u4 fieldIdsOff;
u4 methodIdsSize;
u4 methodIdsOff;
u4 classDefsSize;
u4 classDefsOff;
u4 dataSize;
u4 dataOff;
}DexHeader;

對這個我們可以寫出v35 format string為:"8sI20s20I",就這麼簡單。 接著我們可以呼叫unpack來取得header的內容。
infile = self.open(“yay.dex”, “rb”)
header = struct.unpack(self.v35fmt, infile.read(struct.calcsize(self.v35fmt)))
比較麻煩的一點是,unpack的資料長度必須和format string會處理到的長度一樣,這裡struct提供了calcsize來處理這個問題,它會回傳format string代表的長度。
print(header) (b'dex\n035\x00', 3615126987, b'A\x89\xd9Y\xd8mm\xe4\xfe\x9d8\x0c\xc25\xbc\xcc\x9b\x86\xbd)', 912, 112, 305419896, 0, 0, 752, 16, 112, 8, 176, 4, 208, 1, 256, 5, 264, 1, 304, 576, 336)

可以看見資料已經寫入tuple中了,之後再進行轉出即可。

2014年2月23日 星期日

Minecraft plugin fastbuild 2

這篇接續上一篇 最近花了一點時間,把fastbuild plugin本來預定的功能寫完了。

至於為什麼會相隔這麼久(16天),因為作者平常都在打東方有很多事情要忙,加上Java實在不是作者熟悉的語言,以致進度緩慢。
這次主要寫的是break的部分,原始碼比place 的listener多了一倍,因為break還涉及手中工具的耐久度設定、是否要掉下東西等,功能更複雜。
廢話不多說,來看看怎麼設計。

Event Handler

首先我們要聲請監聽票的event是BlockBreakEvent,這個event會在block被破壞的時候呼叫。 這個event會包含資訊有:玩家,被破壞的方塊。
為了要做到fastbuild的功能,我另外監看了PlayerInteractEvent,可以從這個事件中取得,玩家是碰到方塊的哪一個面。
因為plugin只能做到event based,因此這個plugin還是有一點限制,我們不能讓使用者邊敲方塊,後面一整排的方塊都開始出現裂痕,只能處理BlockBreakEvent(方塊已經爆了),再把後面的方塊設成空氣。這樣會產生一個問題:如果使用者用鏟子爆了泥土,可是後面是石頭,這樣不就可以用鏟子當超強挖礦工具?
所以這裡我們限制會一起挖的,只能是同樣類型的block。
for (int i = 0; i < n-1; i++) {
  nextBlock = block.getRelative(face);
  //currently only deal with same type block
  if (nextBlock.getType() == originType) {
    Collection drops = getDrops(tool, nextBlock);
    nextBlock.setType(Material.AIR);
    if (!isCreative) {
      // drops
      createDrops(nextBlock, drops);
      // durability
      if(!reduceDurability(tool,player)) {
        break;
      }
    }
  } else {
    break;
  }

Durability:

這部分參考[2][3],透過ItemStack的setDurability()跟getDurability()去設值,要注意的是durability值愈高表示工具愈爛,並且可以用ItemStack.getType().getMaxDurability(),來確定工具壞了,以免工具只能挖一格,卻把20格都挖掉了。

Enchantment:

我們處理的enchantment有unbreaking跟silk touch,分別影響durability跟drop items。 minecraft裡物品的item max durability值是恆定的,unbreaking只是在增加durability時加上一個機率,有一定的機不扣durability,在這裡我們複製這個設定。 可以透過ItemStack.getEnchantmentLevel(Enchantment ench); 來取得enchantment的值,非0表示有enchantment。

drops:

這裡我們呼叫 block.getDrops(ItemStack) 來產生drop items內容,用這個的好處是,它會自動判斷工具的等級高低,像用木鎬挖鐵礦,這個事件就會回傳空的內容。不過它不會處理工具有Silk touch的狀況,因些有Silk touch的時候要自己把原本的方塊傳回去。
最後再利用World.dropItemNaturally(Location, ItemStack)產生drop items即可。

Demo:

這個是用gtk-recordMyDesktop錄的,聲音好像比畫面還要慢一點,我也不知道問題在哪lol。

原始碼:

本程式公開所有原始碼,遇到bugs歡迎修改後丟pull request https://github.com/lc85301/FastBuild

參考資料:

1. Bukkit API Overview,要寫plugin不看這個不行www
http://jd.bukkit.org/
2. Minecraft Wiki enchantment:
http://minecraft.gamepedia.com/Enchanting
3. Minecraft Wiki tools
http://minecraft.gamepedia.com/Tools

2014年2月8日 星期六

Minecraft plugin fastbuild

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

本plugin的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方塊怎麼寫了()
https://github.com/lc85301/FastBuild

參考資料:

1. minecraft mod fastbuild
http://www.youtube.com/watch?v=yT5zaBC9O_U
2. bukkit Plugin Tutorial
http://wiki.bukkit.org/Portal:Developers
3. YAML definition
http://wiki.bukkit.org/Plugin_YAML
4. Bukkit API Overview,喔這妖受好用的
http://jd.bukkit.org/