-- TODO: test multiplayer functionality -- -- Note: The x-coordinate is reversed in sign between minetest and minecraft, -- and the API compensates for this. if minetest.request_insecure_environment then ie = minetest.request_insecure_environment() else ie = _G end local source = ie.debug.getinfo(1).source:sub(2) -- Detect windows via backslashes in paths local mypath = minetest.get_modpath(minetest.get_current_modname()) local is_windows = (nil ~= string.find(ie.package.path..ie.package.cpath..source..mypath, "%\\%?")) local path_separator if is_windows then path_separator = "\\" else path_separator = "/" end mypath = mypath .. path_separator local script_window_id = "minetest-rjm-python-script" ie.package.path = ie.package.path .. ";" .. mypath .. "?.lua" if is_windows then ie.package.cpath = ie.package.cpath .. ";" .. mypath .. "?.dll" else ie.package.cpath = ie.package.cpath .. ";" .. mypath .. "?.so" end local block = ie.require("block") local socket = ie.require("socket") local block_hits = {} local chat_record = {} local player_table = {} local socket_client_list = {} script_running = false restrict_to_sword = true max_player_id = 0 default_player_id = -1 world_immutable = false local settings = Settings(mypath .. "settings.conf") local update_settings = false python_interpreter = settings:get("python") if python_interpreter == nil then python_interpreter = "python" update_settings = true settings:set("python", python_interpreter) end local local_only = settings:get_bool("restrict_to_local_connections") if local_only == nil then local_only = false update_settings = true settings:set("restrict_to_local_connections", tostring(local_only)) end local ws = settings:get_bool("support_websockets") if ws == nil then ws = true update_settings = true settings:set("support_websockets", tostring(ws)) end if update_settings then settings:write() end local settings = Settings(mypath .. "override.conf") local x = settings:get("python") if x ~= nil then python_interpreter = x end x = settings:get_bool("restrict_to_local_connections") if x ~= nil then local_only = x end x = settings:get_bool("support_websockets") if x ~= nil then ws = x end local remote_address if local_only then remote_address = "127.0.0.1" else remote_address = "*" end local server,err = socket.bind(remote_address, 4711) assert(server, err) server:setoption('tcp-nodelay',true) server:settimeout(0) local ws_server = nil if ws then if not bit or not bit.bxor then bit = ie.require("slowbit32") end tools = ie.require("tools") base64 = ie.require("base64") ws_server = socket.bind(remote_address, 14711) ws_server:setoption('tcp-nodelay',true) ws_server:settimeout(0) end minetest.register_globalstep(function(dtime) local newclient,err if server then newclient,err = server:accept() if not err then newclient:settimeout(0) table.insert(socket_client_list, {client=newclient,handler=safe_handle_command,read_mode="*l"}) minetest.log("action", "RJM socket client connected") end end if ws_server then newclient,err = ws_server:accept() if not err then newclient:settimeout(0) table.insert(socket_client_list, {client=newclient,handler=handle_websocket_header,ws={},read_mode="*l"}) minetest.log("action", "RJM websocket client attempting handshake") end end local command_count = 1000 for i = 1, #socket_client_list do err = false local line local finished = false while not err and not finished do local source = socket_client_list[i] line,err = source.client:receive(source.read_mode) if err == "closed" then table.remove(socket_client_list,i) minetest.log("action", "RJM socket client disconnected") finished = true elseif not err then err = source:handler(line) if err then source.client:close() table.remove(socket_client_list,i) if err ~= "closed" then minetest.log("error", "Error "..err.." in command: RJM socket client disconnected") end finished = true err = "handling" else command_count = command_count - 1 if command_count < 0 then finished = true end end end end if finished then break end end flush_block_buffer() end) local old_is_protected = minetest.is_protected function minetest.is_protected(pos, name) return world_immutable or old_is_protected(pos, name) end minetest.register_on_shutdown(function() if (script_running) then minetest.log("action", "Stopping scripts") kill(script_window_id) script_running = false end minetest.log("action", "RJM socket clients disconnected") for i = 1, #socket_client_list do socket_client_list[i].client:close() end socket_client_list = {} player_table = {} max_player_id = 0 default_player_id = -1 end) minetest.register_on_joinplayer(function(player) minetest.log("action", "Hello, player "..player:get_player_name()) max_player_id = max_player_id + 1 player_table[max_player_id] = player if default_player_id < 0 then default_player_id = max_player_id end end) minetest.register_on_leaveplayer(function(player) minetest.log("action", "Goodbye, player "..player:get_player_name()) local id = get_player_id(player) if id then player_table[id] = nil end if id == default_player_id then default_player_id = max_player_id for i,p in pairs(player_table) do if p ~= nil and i < default_player_id then default_player_id = i end end end end) minetest.register_on_punchnode(function(pos, oldnode, puncher, pointed_thing) -- TODO: find a way to get right clicks -- TODO: find a way to get clicked side if (puncher:is_player()) then local item = puncher:get_wielded_item() if not restrict_to_sword or (item and item:get_name():find("%:sword")) then table.insert(block_hits, pos.x..","..pos.y..","..(-pos.z)..",7,"..get_entity_id(puncher)) end end end) minetest.register_chatcommand("top", {params="" , description="Move player on top of everything.", func = function(name, args) local player = minetest.get_player_by_name(name) local pos = player:getpos() pos.y = get_height(pos.x, pos.z)+1.5 player:setpos(pos) end}) if minetest.emerge_area then minetest.register_chatcommand("emerge", {params="[]" , description="Emerge cubical area at and above player (default size=200).", func = function(name, args) local player = minetest.get_player_by_name(name) local pos = player:getpos() local size if args ~= "" then size = tonumber(args) else size = 200 end local p1 = {x = math.floor(pos.x - size/2), y = pos.y, z = math.floor(pos.z - size/2) } local p2 = {x = math.ceil(pos.x + size/2), y = pos.y + size, z = math.ceil(pos.z + size/2) } minetest.emerge_area(p1,p2) end}) end minetest.register_chatcommand("py", {params="[