客户端修改
之前发现 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)