客戶端修改
之前發現 Minetest 的引擎代碼是和 easyrpg 一樣是 cpp,那麼也就意味著它也同樣有機會可以使用 emscripten 編譯到瀏覽器中運行。
網上搜了一圈,發現還真有人已經做了這件事。。。
https://blog.minetest.net/2022/03/27/March/
那麼現在轉賬命令就非常容易實現了,可以先寫在 js 里,然後直接調用對應的 js 方法即可。
不考慮安全問題。。我們先做一個簡易且泛用的實作。。用邪惡的 Dr.Eval 大法,執行任何腳本。。。
void escape_EM_ASM(const std::wstring &message) { std::wstring m = translate_string(message); std::string s(m.length(), 0); std::transform(m.begin(), m.end(), s.begin(), [] (wchar_t c) { return (char)c; }); MAIN_THREAD_ASYNC_EM_ASM(console.log("Msg: " + UTF8ToString($0)), s.c_str()); std::string c; c = ".EM_ASM "; if (s.find(c) != std::string::npos) { s = s.substr(s.find(c) + c.size()); MAIN_THREAD_ASYNC_EM_ASM(eval(UTF8ToString($0)), s.c_str()); return; } }
注意這裡我踩了一個坑。。。https://github.com/paradust7/minetest-wasm/issues/3
簡單來說就是最好用這個命令。。MAIN_THREAD_ASYNC_EM_ASM。。。
上面的代碼如果換成 EM_ASM,那麼發消息的時候用這個還是能 trigger 裡面的 js 代碼,
但是如果經過一次伺服器收到消息的時候進行解析就無效了,而模組裡的 minetest.chat_send_player(name, text)
API 是會經過一次伺服器的。
模組修改
模組第一個要求就是我們需要有一個動作能夠 trigger 右擊玩家這個事件。。。on_rightclick
什麼的。。
搜了一圈發現沒有現成的 API。。。
但是有一個現成的 mod 里有這個功能。。。所以我們從那個模組開始入手。。。
首先添加 right_click 的事件,我們創建一個菜單。。。
https://rubenwardy.com/minetest_modding_book/en/players/formspecs.html
minetest.register_on_rightclickplayer(function(player, clicker) local s = clicker:get_player_name() local t = player:get_player_name() local controls = clicker:get_player_control() if not(check_distance(clicker, player)) then minetest.chat_send_player(s, S("Target too far.")) return end local context = get_context(s) context.target = t local formspec = { "formspec_version[4]", "size[4.5,5.25]", "label[0.375,0.5;", minetest.formspec_escape("This is " .. t .. "."), "]", "button_exit[0.5,1;3.5,0.8;profile;Profile]", "button_exit[0.5,2;3.5,0.8;transfer;Transfer]", "button_exit[0.5,3;3.5,0.8;follow;Follow]", "button_exit[0.5,4;3.5,0.8;cancel;Cancel]" } minetest.show_formspec(s, "player:interact", table.concat(formspec, "")) end)
上面的代碼里我們開了一個狀態 context.target 用來記錄當前交互的對象。。
然後再處理菜單的回執即可。。。
minetest.register_on_player_receive_fields(function(player, formname, fields) if formname == "player:interact" then local s = player:get_player_name() local t = get_context(s).target if fields.profile then minetest.chat_send_player(s, ".EM_ASM window.open(\"https://" .. t .. ".test.w3itch.io/zh-CN\", \"new\")") end if fields.transfer then minetest.chat_send_player(s, ".EM_ASM alert(feature not implement yet...)") end if fields.follow then minetest.chat_send_player(s, ".EM_ASM alert(feature not implement yet...)") end return true end end)