Communicating with Minetest Server Using APIs

I have been playing Minetest for a while now, mostly in creative mode. Recently I wanted to control it programmatically. So I started looking for ways to do it. I found mineysocket mod, which gives remote access to Minetest server over a network/socket API. There are wrappers available for various languages, including Python.

mineysocket (fork) - A network API mod for minetest. The goal of this mod is to open the 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 the network/internet.

mineysocket ( fork of it)

To use mineysocket, you must install it as a mod and enable it. So instead of modifying my local server setup. I built a simple (crude, it needs to be optimized) docker image that I can run. It's not secure enough to run on a public network. So I am currently running it on my local network to experiment. Please send me changes to optimize it and secure it.

Setup Docker

Here is the crude Dockerfile, that you can use to build the image and start the container. Currently, I am not installing the mineysocket module in it, but installing all the dependencies required to run it. You can create the folders locally to map to the container, so it becomes easy to change and experiment.

FROM ubuntu:20.04 as build
ENV TZ="Asia/Kolkata"
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
RUN apt-get update && apt-get install tzdata -y
RUN apt-get install build-essential cmake wget libirrlicht-dev libbz2-dev libpng-dev libjpeg-dev libxxf86vm-dev libgl1-mesa-dev libsqlite3-dev libogg-dev libvorbis-dev libopenal-dev libcurl4-gnutls-dev libfreetype6-dev zlib1g-dev libgmp-dev libjsoncpp-dev g++ make libc6-dev cmake libpng-dev libjpeg-dev libxi-dev libgl1-mesa-dev libsqlite3-dev libogg-dev libvorbis-dev libopenal-dev libcurl4-gnutls-dev libfreetype6-dev zlib1g-dev libgmp-dev libjsoncpp-dev libzstd-dev libluajit-5.1-dev lua-socket lua-cjson -y

RUN wget https://github.com/minetest/minetest/archive/refs/tags/5.6.1.tar.gz && tar xf 5.6.1.tar.gz && mv minetest-5.6.1 ./minetest && rm 5.6.1.tar.gz

RUN wget https://github.com/minetest/irrlicht/archive/master.tar.gz && tar xf master.tar.gz && mv irrlicht-master ./minetest/lib/irrlicht

WORKDIR /minetest

RUN cmake . -DRUN_IN_PLACE=TRUE -DBUILD_SERVER=TRUE -DBUILD_CLIENT=FALSE -DIRRLICHT_INCLUDE_DIR=./lib/irrlicht/include 
RUN make -j $(nproc)

VOLUME /minetest/bin/debug.txt
VOLUME /minetest/minetest.conf
VOLUME /minetest/mods
VOLUME /minetest/games
VOLUME /minetest/worlds

EXPOSE 30000/udp
EXPOSE 29999

ENTRYPOINT ["/minetest/bin/minetestserver"]
CMD [""]

Save the above as a file with name Dockerfile in a folder and build using docker or podman

podman build -t thejeshgn/minetest:latest .

Setup the docker volumes by creating folders inside your main folder called mintest_api_volume

mkdir games
mkdir worlds
mkdir mods
touch debug.txt
touch minetest.conf

Add default game

You need a default game to play. So let's setup a default game inside the games folder

cd mintest_api_volume/games
wget wget https://github.com/minetest/minetest_game/archive/master.tar.gz 
tar xf master.tar.gz 
mv minetest_game-master minetest_game 
rm master.tar.gz

Add mineysocket

We will need to add mineysocket to mods folder. We will do by cloning the folder directly under it

cd mintest_api_volume/mods 
git clone https://github.com/esoadamo/mineysocket

Start Server and Create user

Launch the minetest docker server using the above built image and settings

podman run --name=minetest_api_container \
 -p 30000:30000/udp \
 -p 29999:29999 \
 -e TZ=Asia/Kolkata \
 -e CLI_ARGS="--gameid minetest --port 30000" \
 -v /home/thej/mintest_api_volume/debug.txt:/minetest/debug.txt \
 -v /home/thej/mintest_api_volume/minetest.conf:/minetest/minetest.conf \
 -v /home/thej/mintest_api_volume/games:/minetest/games \
 -v /home/thej/mintest_api_volume/mods:/minetest/mods \
 -v /home/thej/mintest_api_volume/worlds:/minetest/worlds \
  localhost/thejeshgn/minetest

Connect to it using the standard minetest client.

Server: localhost
Port: 30000

First, click on the register and register with a username and password. And then connect to it, to see if it works.

Register and Login with your local server

Change configuration

Update the mintest_api_volume/minetest.conf with the following details

name = thej
creative_mode = true
secure.trusted_mods = mineysocket
mineysocket.host_ip = 0.0.0.0

Update the mintest_api_volume/worlds/world/world.mt and enable the mineysocket by adding

load_mod_mineysocket = true

and restart the docker server

podman restart minetest_api_container

mineysocket starts a server at port 29999 by default. You can change it if required. Hence if you look at our Dockerfile, port 29999 is exposed.

Connect using raw Sockets

import socket
import time
import os
import json

chunk_size = 4096
data_buffer: bytes = b""

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(("localhost", int(29999)))
s.send(b"ping\n")
reply = s.recv(4096).decode()
print(reply)
auth_message = {"playername": "thej", "password": "thej"}
raw_data: bytes = str.encode(json.dumps(auth_message) + "\n")
if len(raw_data) < chunk_size:
    print("sending")
    x = s.sendall(raw_data)
    print(x)

print("Recv")
data_buffer = s.recv(4096)
print(data_buffer)

print("Sending")
data = {"lua": "return 12 + 2", "id":"myrandomstring12"}
raw_data: bytes = str.encode(json.dumps(data) + "\n")
x = s.sendall(raw_data)
print(x)
print("Recv")
data_buffer = s.recv(4096)
print(data_buffer)

Connecting to Minetest using raw sockets

First part just pings the Minetest server and the server returns pong as an output. In the second message I am trying to auth to the server. And it will result the response with auth_ok if evrything went well

b'{"result":["auth_ok","10.0.2.100:36494"],"id":"auth"}\n'

In the third call, I am sending some lua code to run on the server. It will run and respond back with the answer. In fact this is what we will use to execute various commands on the server.

b'{"result":["auth_ok","10.0.2.100:36494"],"id":"auth"}\n'

If you don't want to write raw JSON commands, then you can use a Python wrapper package called Miney. It makes working with mineysocket easy. We will do that in our next blog post.

Debug

if you face any issues you can look for logs inside your debug.txt


You can read this blog using RSS Feed. But if you are the person who loves getting emails, then you can join my readers by signing up.

Join 2,241 other subscribers

2 Responses

  1. February 14, 2023

    […] Previous Post Communicating with Minetest Server Using APIs https://thejeshgn.com/2023/02/14/communicating-with-minetest-server-using-apis/ via @thej Posted on February 14, 2023 by @thej in […]

  2. February 17, 2023

    […] with Minetest continues; I wrote a blog post about Communicating with Minetest Server Using APIs as a result. I want a simple Python API that I can share with my niece and nephew where they […]