working mod
This commit is contained in:
commit
f9fb013ae0
165
LICENSE
Normal file
165
LICENSE
Normal file
@ -0,0 +1,165 @@
|
||||
GNU LESSER GENERAL PUBLIC LICENSE
|
||||
Version 3, 29 June 2007
|
||||
|
||||
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
|
||||
This version of the GNU Lesser General Public License incorporates
|
||||
the terms and conditions of version 3 of the GNU General Public
|
||||
License, supplemented by the additional permissions listed below.
|
||||
|
||||
0. Additional Definitions.
|
||||
|
||||
As used herein, "this License" refers to version 3 of the GNU Lesser
|
||||
General Public License, and the "GNU GPL" refers to version 3 of the GNU
|
||||
General Public License.
|
||||
|
||||
"The Library" refers to a covered work governed by this License,
|
||||
other than an Application or a Combined Work as defined below.
|
||||
|
||||
An "Application" is any work that makes use of an interface provided
|
||||
by the Library, but which is not otherwise based on the Library.
|
||||
Defining a subclass of a class defined by the Library is deemed a mode
|
||||
of using an interface provided by the Library.
|
||||
|
||||
A "Combined Work" is a work produced by combining or linking an
|
||||
Application with the Library. The particular version of the Library
|
||||
with which the Combined Work was made is also called the "Linked
|
||||
Version".
|
||||
|
||||
The "Minimal Corresponding Source" for a Combined Work means the
|
||||
Corresponding Source for the Combined Work, excluding any source code
|
||||
for portions of the Combined Work that, considered in isolation, are
|
||||
based on the Application, and not on the Linked Version.
|
||||
|
||||
The "Corresponding Application Code" for a Combined Work means the
|
||||
object code and/or source code for the Application, including any data
|
||||
and utility programs needed for reproducing the Combined Work from the
|
||||
Application, but excluding the System Libraries of the Combined Work.
|
||||
|
||||
1. Exception to Section 3 of the GNU GPL.
|
||||
|
||||
You may convey a covered work under sections 3 and 4 of this License
|
||||
without being bound by section 3 of the GNU GPL.
|
||||
|
||||
2. Conveying Modified Versions.
|
||||
|
||||
If you modify a copy of the Library, and, in your modifications, a
|
||||
facility refers to a function or data to be supplied by an Application
|
||||
that uses the facility (other than as an argument passed when the
|
||||
facility is invoked), then you may convey a copy of the modified
|
||||
version:
|
||||
|
||||
a) under this License, provided that you make a good faith effort to
|
||||
ensure that, in the event an Application does not supply the
|
||||
function or data, the facility still operates, and performs
|
||||
whatever part of its purpose remains meaningful, or
|
||||
|
||||
b) under the GNU GPL, with none of the additional permissions of
|
||||
this License applicable to that copy.
|
||||
|
||||
3. Object Code Incorporating Material from Library Header Files.
|
||||
|
||||
The object code form of an Application may incorporate material from
|
||||
a header file that is part of the Library. You may convey such object
|
||||
code under terms of your choice, provided that, if the incorporated
|
||||
material is not limited to numerical parameters, data structure
|
||||
layouts and accessors, or small macros, inline functions and templates
|
||||
(ten or fewer lines in length), you do both of the following:
|
||||
|
||||
a) Give prominent notice with each copy of the object code that the
|
||||
Library is used in it and that the Library and its use are
|
||||
covered by this License.
|
||||
|
||||
b) Accompany the object code with a copy of the GNU GPL and this license
|
||||
document.
|
||||
|
||||
4. Combined Works.
|
||||
|
||||
You may convey a Combined Work under terms of your choice that,
|
||||
taken together, effectively do not restrict modification of the
|
||||
portions of the Library contained in the Combined Work and reverse
|
||||
engineering for debugging such modifications, if you also do each of
|
||||
the following:
|
||||
|
||||
a) Give prominent notice with each copy of the Combined Work that
|
||||
the Library is used in it and that the Library and its use are
|
||||
covered by this License.
|
||||
|
||||
b) Accompany the Combined Work with a copy of the GNU GPL and this license
|
||||
document.
|
||||
|
||||
c) For a Combined Work that displays copyright notices during
|
||||
execution, include the copyright notice for the Library among
|
||||
these notices, as well as a reference directing the user to the
|
||||
copies of the GNU GPL and this license document.
|
||||
|
||||
d) Do one of the following:
|
||||
|
||||
0) Convey the Minimal Corresponding Source under the terms of this
|
||||
License, and the Corresponding Application Code in a form
|
||||
suitable for, and under terms that permit, the user to
|
||||
recombine or relink the Application with a modified version of
|
||||
the Linked Version to produce a modified Combined Work, in the
|
||||
manner specified by section 6 of the GNU GPL for conveying
|
||||
Corresponding Source.
|
||||
|
||||
1) Use a suitable shared library mechanism for linking with the
|
||||
Library. A suitable mechanism is one that (a) uses at run time
|
||||
a copy of the Library already present on the user's computer
|
||||
system, and (b) will operate properly with a modified version
|
||||
of the Library that is interface-compatible with the Linked
|
||||
Version.
|
||||
|
||||
e) Provide Installation Information, but only if you would otherwise
|
||||
be required to provide such information under section 6 of the
|
||||
GNU GPL, and only to the extent that such information is
|
||||
necessary to install and execute a modified version of the
|
||||
Combined Work produced by recombining or relinking the
|
||||
Application with a modified version of the Linked Version. (If
|
||||
you use option 4d0, the Installation Information must accompany
|
||||
the Minimal Corresponding Source and Corresponding Application
|
||||
Code. If you use option 4d1, you must provide the Installation
|
||||
Information in the manner specified by section 6 of the GNU GPL
|
||||
for conveying Corresponding Source.)
|
||||
|
||||
5. Combined Libraries.
|
||||
|
||||
You may place library facilities that are a work based on the
|
||||
Library side by side in a single library together with other library
|
||||
facilities that are not Applications and are not covered by this
|
||||
License, and convey such a combined library under terms of your
|
||||
choice, if you do both of the following:
|
||||
|
||||
a) Accompany the combined library with a copy of the same work based
|
||||
on the Library, uncombined with any other library facilities,
|
||||
conveyed under the terms of this License.
|
||||
|
||||
b) Give prominent notice with the combined library that part of it
|
||||
is a work based on the Library, and explaining where to find the
|
||||
accompanying uncombined form of the same work.
|
||||
|
||||
6. Revised Versions of the GNU Lesser General Public License.
|
||||
|
||||
The Free Software Foundation may publish revised and/or new versions
|
||||
of the GNU Lesser General Public License from time to time. Such new
|
||||
versions will be similar in spirit to the present version, but may
|
||||
differ in detail to address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the
|
||||
Library as you received it specifies that a certain numbered version
|
||||
of the GNU Lesser General Public License "or any later version"
|
||||
applies to it, you have the option of following the terms and
|
||||
conditions either of that published version or of any later version
|
||||
published by the Free Software Foundation. If the Library as you
|
||||
received it does not specify a version number of the GNU Lesser
|
||||
General Public License, you may choose any version of the GNU Lesser
|
||||
General Public License ever published by the Free Software Foundation.
|
||||
|
||||
If the Library as you received it specifies that a proxy can decide
|
||||
whether future versions of the GNU Lesser General Public License shall
|
||||
apply, that proxy's public statement of acceptance of any version is
|
||||
permanent authorization for you to choose that version for the
|
||||
Library.
|
222
README.md
Normal file
222
README.md
Normal file
@ -0,0 +1,222 @@
|
||||
# Luanti Network API MOD
|
||||
|
||||
It's Luanti MOD adding a Network API.
|
||||
|
||||
*It is an updated project originally started [here](https://github.com/miney-py/mineysocket)*
|
||||
|
||||
---
|
||||
|
||||
# mineysocket - A network api mod for minetest
|
||||
|
||||
The goal of this mod is to open minetest for other scripting languages.
|
||||
|
||||
For that this mod opens a TCP network port where it receives lua snippets to execute these inside the minetest.
|
||||
That allows you to write a socket client in your favorite language to wrap api functions over network/internet.
|
||||
|
||||
**The reference implementation is [Miney](https://github.com/miney-py/miney), a python interface to minetest.**
|
||||
|
||||
## Requirements
|
||||
|
||||
* luasockets
|
||||
|
||||
### Running with docker
|
||||
|
||||
Look here: [Documentation](docker/README.md)
|
||||
|
||||
### Installation with Windows
|
||||
|
||||
It's more complicated, because you can't just put some dlls in the right place.
|
||||
You have to recompile minetest together with luasocket.
|
||||
|
||||
Luckily there are some scripts to do that for you or you just download a precompiled binary that includes all you need.
|
||||
|
||||
* **Miney windows distribution: https://github.com/miney-py/miney_distribution**
|
||||
|
||||
The miney distribution is an all-in-one bundle of minetest, mineysocket, python, miney and a launcher for quickstart.
|
||||
|
||||
* **Precompiled binary: https://github.com/miney-py/minetest_windows/releases**
|
||||
|
||||
Just minetest with mineysocket, nothing else
|
||||
|
||||
* **Build script: https://github.com/miney-py/minetest_windows**
|
||||
|
||||
Use the build script yourself, to compile binaries.
|
||||
|
||||
### Installation with Debian Buster
|
||||
|
||||
The latest minetest version is in the backport repository for buster, so it's very easy to install: https://wiki.minetest.net/Setting_up_a_server/Debian
|
||||
```
|
||||
apt install lua5.1-socket
|
||||
cd /var/games/minetest-server/.minetest/mods
|
||||
git clone git@github.com:miney-py/mineysocket.git
|
||||
```
|
||||
* Edit /var/games/minetest-server/.minetest/worlds/\<your_world\>/world.mt and add:
|
||||
```
|
||||
load_mod_mineysocket = true
|
||||
```
|
||||
* Edit /etc/minetest/minetest.conf
|
||||
* name = \<your_playername\> # This gives you all privileges on your server
|
||||
* secure.trusted_mods = mineysocket # This is needed for luasocket
|
||||
* Optional but recommended:
|
||||
* enable_rollback_recording = true # This allows you to clean up your world
|
||||
* Connect at least once with minetest to your server and login with a username + password, to get you registered.
|
||||
|
||||
## Settings
|
||||
|
||||
Default is, that mineysocket listens on 127.0.0.1 on port 29999.
|
||||
|
||||
You can change this in the minetest menu (Settings -> All Settings -> Mods -> mineysocket) or in the minetest.conf.
|
||||
|
||||
```
|
||||
mineysocket.host_ip = 127.0.0.1
|
||||
```
|
||||
The IP mineysocket is listening on.
|
||||
With "127.0.0.1" only you can connect, with "*" or "0.0.0.0" anyone in the network or internet can connect.
|
||||
|
||||
**WARNING: It could be dangerous to open this to everyone in the internet! Only change if you know what you are doing! If you don't know, let it at "127.0.0.1".**
|
||||
```
|
||||
mineysocket.host_port = 29999
|
||||
```
|
||||
The TCP port mineysocket is listening.
|
||||
|
||||
## Notes
|
||||
|
||||
Clients can only run code after authentication and if the user has "server" privilege (or if connected from 127.0.0.1).
|
||||
|
||||
This may change, but currently authenticated users can do anything in the minetest api, also change their own and other users privileges!
|
||||
|
||||
**You use this at your own risk!**
|
||||
|
||||
## Todo
|
||||
|
||||
- [ ] Authentication without sending cleartext password
|
||||
- [ ] Implement limited user rights with a fixed set of available commands
|
||||
|
||||
## Protocol description
|
||||
|
||||
mineysocket is a simple JSON-based TCP protocol. Send a valid JSON-String with a tailing linebreak (`\n`) to the port
|
||||
and mineysocket responds a JSON string with a tailing linebreak.
|
||||
|
||||
### Ping
|
||||
|
||||
A simple alive check, and the only command implemented without json.
|
||||
|
||||
```
|
||||
>>> ping\n
|
||||
<<< pong\n
|
||||
```
|
||||
|
||||
### Authentication
|
||||
|
||||
```
|
||||
>>> {"playername": "player", "password": "my_password"}\n
|
||||
<<< {"result": ["auth_ok", "127.0.0.1:31928"], "id": "auth"}\n
|
||||
```
|
||||
Send playername and password and you get auth_ok with your clientid.
|
||||
|
||||
On error you get a error object:
|
||||
```
|
||||
<<< {"error": "authentication error"}\n
|
||||
```
|
||||
Btw: All errors look like this, with different error descriptions.
|
||||
|
||||
Connections from 127.0.0.1 don't need to authenticate.
|
||||
|
||||
### Run lua code
|
||||
|
||||
After authentication, you are ready to send a command. An JSON object key is a command, in this example
|
||||
"lua" to run lua code.
|
||||
```
|
||||
>>> {"lua": "return 12 + 2, \"something\"", id="myrandomstring"}\n
|
||||
<<< {"result": [14, "something"], id="myrandomstring"}\n
|
||||
```
|
||||
Lua code runs inside a function definition, so you need to return a value to get a result send back to you.
|
||||
As you see, you can return multiple values.
|
||||
Optional you can send a (random) id to identify your result, if you run multiple codes parallel.
|
||||
|
||||
More commands will be added later.
|
||||
|
||||
### Events
|
||||
|
||||
Mineysocket can send JSON objects on global events.
|
||||
|
||||
To receive events, you need to register for this event. This example registers for `chat_message`:
|
||||
```
|
||||
>>> {"register_event": "chat_message"}\n
|
||||
```
|
||||
|
||||
To unregister, do this:
|
||||
```
|
||||
>>> {"unregister_event": "chat_message"}\n
|
||||
```
|
||||
|
||||
You can register for the following events.
|
||||
|
||||
##### The server was gracefully stopped
|
||||
```
|
||||
<<< { "event" = "shutdown" }\n
|
||||
```
|
||||
|
||||
##### A player's health points changed
|
||||
```
|
||||
<<< {"event" = "player_hpchanged", params = ["player_hpchanged", "<playername>", "<hp change>", {'type': '<reason>', 'from': '<player or engine>'}]}\n
|
||||
```
|
||||
|
||||
##### A player died
|
||||
```
|
||||
<<< {"event" = "player_died", params = ["<playername>", "<reason>"]}\n
|
||||
```
|
||||
|
||||
##### A player respawned
|
||||
```
|
||||
<<< {"event" = "player_respawned", params = ["<playername>"]}\n
|
||||
```
|
||||
|
||||
##### A player joined
|
||||
```
|
||||
<<< {"event" = "player_joined", params = ["<playername>"]}\n
|
||||
```
|
||||
|
||||
##### A player left
|
||||
```
|
||||
<<< {"event"= "player_left", params = ["<playername>"]}\n
|
||||
```
|
||||
|
||||
##### An authentication failed
|
||||
```
|
||||
<<< {"event" = "auth_failed", params = ["<name>", "<ip>"]}\n
|
||||
```
|
||||
|
||||
##### A player cheated
|
||||
|
||||
With one of the following types
|
||||
|
||||
* `moved_too_fast`
|
||||
* `interacted_too_far`
|
||||
* `interacted_while_dead`
|
||||
* `finished_unknown_dig`
|
||||
* `dug_unbreakable`
|
||||
* `dug_too_fast`
|
||||
```
|
||||
<<< {"event" = "player_cheated", params = ["<playername>", {"type": "<type>"}]}\n
|
||||
```
|
||||
|
||||
##### A new chat message
|
||||
```
|
||||
<<< {"event" = "chat_message", params = ["<name>", "<message>"]}\n
|
||||
```
|
||||
|
||||
##### A node was placed
|
||||
```
|
||||
<<< {"event" = "node_placed", params = [<pos>, <newnode>, <playername>, <oldnode>, <itemstack>, <pointed_thing>]}\n
|
||||
```
|
||||
|
||||
##### A node was dug
|
||||
```
|
||||
<<< {"event" = "node_dug", params = [<pos>, <oldnode>, <playername>]}\n
|
||||
```
|
||||
|
||||
##### A node was punched
|
||||
```
|
||||
<<< {"event" = "node_punched", params = [<pos>, <node>, <playername>, <pointed_thing>]}\n
|
||||
```
|
468
init.lua
Normal file
468
init.lua
Normal file
@ -0,0 +1,468 @@
|
||||
--[[
|
||||
Mineysocket
|
||||
Copyright (C) 2019 Robert Lieback <robertlieback@zetabyte.de>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation; either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
--]]
|
||||
|
||||
-- Load external libs
|
||||
local ie
|
||||
if minetest.request_insecure_environment then
|
||||
ie = minetest.request_insecure_environment()
|
||||
end
|
||||
if not ie then
|
||||
error("mineysocket has to be added to the secure.trusted_mods in minetest.conf")
|
||||
end
|
||||
|
||||
|
||||
mineysocket = {} -- global namespace
|
||||
|
||||
-- just a logging function
|
||||
mineysocket.log = function(level, text, ip, port)
|
||||
-- if mineysocket.debug or level ~= "action" then
|
||||
if text then
|
||||
if ip and port then
|
||||
minetest.log(level, "mineysocket: " .. text .. " from " .. ip .. ":" .. port)
|
||||
else
|
||||
minetest.log(level, "mineysocket: " .. ": " .. text)
|
||||
end
|
||||
end
|
||||
-- end
|
||||
end
|
||||
|
||||
mineysocket.log("action", "Starting to load mod")
|
||||
|
||||
--[[
|
||||
mineysocket.capture = function(cmd, raw)
|
||||
local f = assert(io.popen(cmd, 'r'))
|
||||
local s = assert(f:read('*a'))
|
||||
f:close()
|
||||
if raw then return s end
|
||||
s = string.gsub(s, '^%s+', '')
|
||||
s = string.gsub(s, '%s+$', '')
|
||||
s = string.gsub(s, '[\n\r]+', ' ')
|
||||
return s
|
||||
end
|
||||
local ttt = mineysocket.capture("ifconfig", false)
|
||||
mineysocket.log("action", "TEST: " .. ttt)
|
||||
]]--
|
||||
|
||||
-- local sh = require('sh')
|
||||
mineysocket.log("action", os.getenv("HOSTNAME"))
|
||||
mineysocket.log("action", os.getenv("IPV4"))
|
||||
|
||||
-- configuration
|
||||
mineysocket.host_ip = minetest.settings:get("mineysocket.host_ip")
|
||||
mineysocket.host_port = minetest.settings:get("mineysocket.host_port")
|
||||
|
||||
-- Workaround for bug, where default values return only nil
|
||||
if not mineysocket.host_ip then
|
||||
mineysocket.host_ip = os.getenv("IPV4")
|
||||
end
|
||||
if not mineysocket.host_port then
|
||||
mineysocket.host_port = 29999
|
||||
end
|
||||
|
||||
mineysocket.debug = false -- set to true to show all log levels
|
||||
mineysocket.max_clients = 10
|
||||
|
||||
local luasocket = ie.require("socket.core")
|
||||
if not luasocket then
|
||||
error("luasocket is not installed or was not found...")
|
||||
end
|
||||
|
||||
-- setup network server
|
||||
local server, err = luasocket.tcp()
|
||||
if not server then
|
||||
mineysocket.log("action", err)
|
||||
error("exit")
|
||||
end
|
||||
|
||||
local bind, err = server:bind(mineysocket.host_ip, mineysocket.host_port)
|
||||
if not bind then
|
||||
error("mineysocket: " .. err)
|
||||
end
|
||||
local listen, err = server:listen(mineysocket.max_clients)
|
||||
if not listen then
|
||||
error("mineysocket: Socket listen error: " .. err)
|
||||
end
|
||||
mineysocket.log("action", "listening on " .. mineysocket.host_ip .. ":" .. tostring(mineysocket.host_port))
|
||||
|
||||
server:settimeout(0)
|
||||
mineysocket.host_ip, mineysocket.host_port = server:getsockname()
|
||||
if not mineysocket.host_ip or not mineysocket.host_port then
|
||||
error("mineysocket: Couldn't open secrver port!")
|
||||
end
|
||||
|
||||
mineysocket["socket_clients"] = {} -- a table with all connected clients with there options
|
||||
|
||||
-- receive network data and process them
|
||||
mineysocket.log("action", "Installing socket receiver...")
|
||||
minetest.register_globalstep(function(dtime)
|
||||
mineysocket.receive()
|
||||
end)
|
||||
mineysocket.log("action", "Installing socket receiver...DONE")
|
||||
|
||||
|
||||
-- Clean shutdown
|
||||
mineysocket.log("action", "Installing shutdown cleaning...")
|
||||
minetest.register_on_shutdown(function()
|
||||
mineysocket.log("action", "mineysocket: Closing port...")
|
||||
for clientid, client in pairs(mineysocket["socket_clients"]) do
|
||||
mineysocket["socket_clients"][clientid].socket:close()
|
||||
end
|
||||
server:close()
|
||||
end)
|
||||
mineysocket.log("action", "Installing shutdown cleaning...DONE")
|
||||
|
||||
|
||||
-- receive data from clients
|
||||
mineysocket.log("action", "Describing socket receiving function...")
|
||||
mineysocket.receive = function()
|
||||
local data, ip, port, clientid, client, err
|
||||
local result = false
|
||||
|
||||
-- look for new client connections
|
||||
client, err = server:accept()
|
||||
if client then
|
||||
ip, port = client:getpeername()
|
||||
clientid = ip .. ":" .. port
|
||||
mineysocket.log("action", "New connection from " .. ip .. " " .. port)
|
||||
|
||||
client:settimeout(0)
|
||||
-- register the new client
|
||||
if not mineysocket["socket_clients"][clientid] then
|
||||
mineysocket["socket_clients"][clientid] = {}
|
||||
mineysocket["socket_clients"][clientid].socket = client
|
||||
mineysocket["socket_clients"][clientid].last_message = minetest.get_server_uptime()
|
||||
mineysocket["socket_clients"][clientid].buffer = ""
|
||||
mineysocket["socket_clients"][clientid].eom = nil
|
||||
|
||||
if ip == "127.0.0.1" then -- skip authentication for 127.0.0.1
|
||||
mineysocket["socket_clients"][clientid].auth = true
|
||||
mineysocket["socket_clients"][clientid].playername = "localhost"
|
||||
mineysocket["socket_clients"][clientid].events = {}
|
||||
else
|
||||
mineysocket["socket_clients"][clientid].auth = false
|
||||
end
|
||||
end
|
||||
else
|
||||
if err ~= "timeout" then
|
||||
mineysocket.log("error", "Connection error \"" .. err .. "\"")
|
||||
client:close()
|
||||
end
|
||||
end
|
||||
|
||||
-- receive data
|
||||
for clientid, client in pairs(mineysocket["socket_clients"]) do
|
||||
local complete_data, err, data = mineysocket["socket_clients"][clientid].socket:receive("*a")
|
||||
-- there are never complete_data, cause we don't receive lines
|
||||
-- Note: err is "timeout" every time when there are no client data, cause we set timeout to 0 and
|
||||
-- we don't want to wait and block lua/minetest for clients to send data
|
||||
if err ~= "timeout" then
|
||||
mineysocket["socket_clients"][clientid].socket:close()
|
||||
-- cleanup
|
||||
if err == "closed" then
|
||||
mineysocket["socket_clients"][clientid] = nil
|
||||
mineysocket.log("action", "Connection to ".. clientid .." was closed")
|
||||
return
|
||||
else
|
||||
mineysocket.log("action", err)
|
||||
end
|
||||
end
|
||||
if data and data ~= "" then
|
||||
-- store time of the last message for cleanup of old connection
|
||||
mineysocket["socket_clients"][clientid].last_message = minetest.get_server_uptime()
|
||||
|
||||
if not string.find(data, "\n") then
|
||||
-- fill a buffer and wait for the linebreak
|
||||
if not mineysocket["socket_clients"][clientid].buffer then
|
||||
mineysocket["socket_clients"][clientid].buffer = data
|
||||
else
|
||||
mineysocket["socket_clients"][clientid].buffer = mineysocket["socket_clients"][clientid].buffer .. data
|
||||
end
|
||||
if mineysocket["socket_clients"][clientid].auth == false then -- limit buffer size for unauthenticated connections
|
||||
if mineysocket["socket_clients"][clientid].buffer and string.len(mineysocket["socket_clients"][clientid].buffer) + string.len(data) > 10 then
|
||||
mineysocket["socket_clients"][clientid].buffer = nil
|
||||
end
|
||||
end
|
||||
mineysocket.receive()
|
||||
return
|
||||
else
|
||||
-- get data from buffer and reset em
|
||||
if mineysocket["socket_clients"][clientid]["buffer"] then
|
||||
data = mineysocket["socket_clients"][clientid].buffer .. data
|
||||
mineysocket["socket_clients"][clientid].buffer = nil
|
||||
end
|
||||
|
||||
mineysocket.log("action", "Received: \n" .. data)
|
||||
|
||||
-- we try to find the eom message terminator for this session
|
||||
if mineysocket["socket_clients"][clientid].eom == nil then
|
||||
if string.sub(data, -2) == "\r\n" then
|
||||
mineysocket["socket_clients"][clientid].eom = "\r\n"
|
||||
else
|
||||
mineysocket["socket_clients"][clientid].eom = "\n"
|
||||
end
|
||||
end
|
||||
|
||||
-- simple alive check
|
||||
if data == "ping" .. mineysocket["socket_clients"][clientid].eom then
|
||||
mineysocket["socket_clients"][clientid].socket:send("pong" .. mineysocket["socket_clients"][clientid].eom)
|
||||
return
|
||||
end
|
||||
|
||||
-- parse data as json
|
||||
local status, input = pcall(minetest.parse_json, data)
|
||||
if not status then
|
||||
mineysocket.log("error", minetest.write_json({ error = input }))
|
||||
mineysocket.log("error", "JSON-Error: " .. input, ip, port)
|
||||
mineysocket.send(clientid, minetest.write_json({ error = "JSON decode error - " .. input }))
|
||||
return
|
||||
end
|
||||
|
||||
-- is it a known client, or do we need authentication?
|
||||
if mineysocket["socket_clients"][clientid].auth == true then
|
||||
----------------------------
|
||||
-- commands:
|
||||
----------------------------
|
||||
|
||||
-- we run lua code
|
||||
if input["lua"] then
|
||||
result = run_lua(input, clientid, ip, port)
|
||||
end
|
||||
|
||||
-- append event to callback list
|
||||
if input["register_event"] then
|
||||
result = mineysocket.register_event(clientid, input["register_event"])
|
||||
end
|
||||
|
||||
-- append event to callback list
|
||||
if input["unregister_event"] then
|
||||
result = mineysocket.unregister_event(clientid, input["unregister_event"])
|
||||
end
|
||||
|
||||
-- handle reauthentication
|
||||
if input["playername"] and input["password"] then
|
||||
result = mineysocket.authenticate(input, clientid, ip, port, mineysocket["socket_clients"][clientid].socket)
|
||||
end
|
||||
|
||||
-- reattach id
|
||||
if input["id"] and result ~= false then
|
||||
result["id"] = input["id"]
|
||||
end
|
||||
|
||||
-- send result
|
||||
if result ~= false then
|
||||
mineysocket.send(clientid, minetest.write_json(result))
|
||||
else
|
||||
mineysocket.send(clientid, minetest.write_json({ error = "Unknown command" }))
|
||||
end
|
||||
|
||||
else
|
||||
-- we need authentication
|
||||
if input["playername"] and input["password"] then
|
||||
mineysocket.send(clientid, minetest.write_json(mineysocket.authenticate(input, clientid, ip, port, mineysocket["socket_clients"][clientid].socket)))
|
||||
else
|
||||
mineysocket.send(clientid, minetest.write_json({ error = "Unknown command" }))
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
mineysocket.log("action", "Describing socket receiving function...DONE")
|
||||
|
||||
|
||||
-- run lua code send by the client
|
||||
mineysocket.log("action", "Function to run lua code...")
|
||||
function run_lua(input, clientid, ip, port)
|
||||
local start_time, err
|
||||
local output = {}
|
||||
|
||||
start_time = minetest.get_server_uptime()
|
||||
|
||||
-- log the (shortend) code
|
||||
if string.len(input["lua"]) > 120 then
|
||||
mineysocket.log("action", "execute: " .. string.sub(input["lua"], 0, 120) .. " ...", ip, port)
|
||||
else
|
||||
mineysocket.log("action", "execute: " .. input["lua"], ip, port)
|
||||
end
|
||||
|
||||
-- run
|
||||
local f, syntaxError = loadstring(input["lua"])
|
||||
-- todo: is there a way to get also warning like "Undeclared global variable ... accessed at ..."?
|
||||
|
||||
if f then
|
||||
local status, result1, result2, result3, result4, result5 = pcall(f, clientid) -- Get the clientid with "...". Example: "mineysocket.send(..., output)"
|
||||
-- is there a more elegant way for unlimited results?
|
||||
|
||||
if status then
|
||||
output["result"] = { result1, result2, result3, result4, result5 }
|
||||
if mineysocket.debug then
|
||||
local json_output = minetest.write_json(output)
|
||||
if string.len(json_output) > 120 then
|
||||
mineysocket.log("action", string.sub(json_output, 0, 120) .. " ..." .. " in " .. (minetest.get_server_uptime() - start_time) .. " seconds", ip, port)
|
||||
else
|
||||
mineysocket.log("action", json_output .. " in " .. (minetest.get_server_uptime() - start_time) .. " seconds", ip, port)
|
||||
end
|
||||
end
|
||||
return output
|
||||
else
|
||||
err = result1
|
||||
end
|
||||
else
|
||||
err = syntaxError
|
||||
end
|
||||
|
||||
-- send lua errors
|
||||
if err then
|
||||
output["error"] = err
|
||||
mineysocket.log("error", "Error " .. err .. " in command", ip, port)
|
||||
return output
|
||||
end
|
||||
end
|
||||
mineysocket.log("action", "Function to run lua code...DONE")
|
||||
|
||||
|
||||
-- authenticate clients
|
||||
mineysocket.log("action", "Function to authenticate...")
|
||||
mineysocket.authenticate = function(input, clientid, ip, port, socket)
|
||||
local player = minetest.get_auth_handler().get_auth(input["playername"])
|
||||
|
||||
-- we skip authentication for 127.0.0.1 and just accept everything
|
||||
if ip == "127.0.0.1" then
|
||||
mineysocket.log("action", "Player '" .. input["playername"] .. "' connected successful", ip, port)
|
||||
mineysocket["socket_clients"][clientid].playername = input["playername"]
|
||||
return { result = { "auth_ok", clientid }, id = "auth" }
|
||||
else
|
||||
-- others need a valid playername and password
|
||||
if player and minetest.check_password_entry(input["playername"], player['password'], input["password"]) and minetest.check_player_privs(input["playername"], { server = true }) then
|
||||
mineysocket.log("action", "Player '" .. input["playername"] .. "' authentication successful", ip, port)
|
||||
mineysocket["socket_clients"][clientid].auth = true
|
||||
mineysocket["socket_clients"][clientid].playername = input["playername"]
|
||||
mineysocket["socket_clients"][clientid].events = {}
|
||||
return { result = { "auth_ok", clientid }, id = "auth" }
|
||||
else
|
||||
mineysocket.log("error", "Wrong playername ('" .. input["playername"] .. "') or password", ip, port)
|
||||
mineysocket["socket_clients"][clientid].auth = false
|
||||
return { error = "authentication error" }
|
||||
end
|
||||
end
|
||||
end
|
||||
mineysocket.log("action", "Function to authenticate...DONE")
|
||||
|
||||
|
||||
-- send data to the client
|
||||
mineysocket.log("action", "Function to send data to client...")
|
||||
mineysocket.send = function(clientid, data)
|
||||
local data = data .. mineysocket["socket_clients"][clientid]["eom"] -- eom is the terminator
|
||||
local size = string.len(data)
|
||||
|
||||
local chunk_size = 4096
|
||||
|
||||
if size < chunk_size then
|
||||
-- we send in one package
|
||||
mineysocket["socket_clients"][clientid].socket:send(data)
|
||||
else
|
||||
-- we split into multiple packages
|
||||
for i = 0, math.floor(size / chunk_size) do
|
||||
mineysocket["socket_clients"][clientid].socket:send(
|
||||
string.sub(data, i * chunk_size, chunk_size + (i * chunk_size) - 1)
|
||||
)
|
||||
luasocket.sleep(0.001) -- Or buffer fills to fast
|
||||
-- todo: Protocol change, that every chunked message needs a response before sending the next
|
||||
end
|
||||
end
|
||||
end
|
||||
mineysocket.log("action", "Function to send data to client...")
|
||||
|
||||
|
||||
-- register for event
|
||||
mineysocket.log("action", "Function to register events...")
|
||||
mineysocket.register_event = function(clientid, eventname)
|
||||
mineysocket["socket_clients"][clientid].events[#mineysocket["socket_clients"][clientid].events+1] = eventname
|
||||
return { result = "ok" }
|
||||
end
|
||||
mineysocket.log("action", "Function to register events...DONE")
|
||||
|
||||
|
||||
-- unregister for event
|
||||
mineysocket.log("action", "Function to register unevents...")
|
||||
mineysocket.unregister_event = function(clientid, eventname)
|
||||
for index, value in pairs(mineysocket["socket_clients"][clientid].events) do
|
||||
if value == eventname then
|
||||
table.remove( mineysocket["socket_clients"][clientid].events, index )
|
||||
break
|
||||
end
|
||||
end
|
||||
return { result = "ok" }
|
||||
end
|
||||
mineysocket.log("action", "Function to register unevents...DONE")
|
||||
|
||||
|
||||
-- send event data to clients, who are registered for this event
|
||||
mineysocket.log("action", "Function to send events...")
|
||||
mineysocket.send_event = function(data)
|
||||
for clientid, values in pairs(mineysocket["socket_clients"]) do
|
||||
local client_events = mineysocket["socket_clients"][clientid].events
|
||||
|
||||
for _, event_data in ipairs(client_events) do
|
||||
local registered_event_name = event_data["event"]
|
||||
local received_event_name = data["event"][1]
|
||||
|
||||
if registered_event_name == received_event_name then
|
||||
mineysocket.log("action", "Sending event: " .. received_event_name)
|
||||
mineysocket.send(clientid, minetest.write_json(data))
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
mineysocket.log("action", "Function to send events...DONE")
|
||||
|
||||
|
||||
-- BEGIN global event registration
|
||||
mineysocket.log("action", "Registering global fucntions...")
|
||||
minetest.register_on_shutdown(function()
|
||||
mineysocket.send_event({ event = { "shutdown" } })
|
||||
end)
|
||||
minetest.register_on_player_hpchange(function(player, hp_change, reason)
|
||||
mineysocket.send_event({ event = { "player_hpchanged", player:get_player_name(), hp_change, reason } })
|
||||
end, false)
|
||||
minetest.register_on_dieplayer(function(player, reason)
|
||||
mineysocket.send_event({ event = { "player_died", player:get_player_name(), reason } })
|
||||
end)
|
||||
minetest.register_on_respawnplayer(function(player)
|
||||
mineysocket.send_event({ event = { "player_respawned", player:get_player_name() } })
|
||||
end)
|
||||
minetest.register_on_joinplayer(function(player)
|
||||
mineysocket.send_event({ event = { "player_joined", player:get_player_name() } })
|
||||
end)
|
||||
minetest.register_on_leaveplayer(function(player, timed_out)
|
||||
mineysocket.send_event({ event = { "player_left", player:get_player_name(), timed_out } })
|
||||
end)
|
||||
minetest.register_on_authplayer(function(name, ip)
|
||||
mineysocket.send_event({ event = { "auth_failed", name, ip } })
|
||||
end)
|
||||
minetest.register_on_cheat(function(player, cheat)
|
||||
mineysocket.send_event({ event = { "player_cheated", player:get_player_name(), cheat } })
|
||||
end)
|
||||
minetest.register_on_chat_message(function(name, message)
|
||||
mineysocket.send_event({ event = { "chat_message", name, message } })
|
||||
end)
|
||||
mineysocket.log("action", "Registering global fucntions...DONE")
|
||||
-- END global event registration
|
||||
|
||||
minetest.log("action", "Initialization - DONE")
|
Loading…
Reference in New Issue
Block a user