1

In the past week I install a Terraria 1.3.5.3 server into an Ubuntu v18.04 OS, for playing online with friends. This server should be powered on 24/7, without any GUI, only been accessed by SSH on internal LAN.

My friends ask me if there is a way for them to control the server, e.g. send a message, via internal in-game chat, so I thought use a special character ($) in front of the desired command ('$say something' or '$save', for instance) and a python program, that read the terminal output via pipe, interpreter the command and send it back with a bash command.

I follow these instructions to install the server:

https://www.linode.com/docs/game-servers/host-a-terraria-server-on-your-linode

And config my router to forward a dedicated port to the terraria server.

All is working fine, but I really struggle to make python send a command via "terrariad" bash script, described in the link above.

Here is a code used to send a command, in python:

import subprocess

subprocess.Popen("terrariad save", shell=True)

This works fine, but if I try to input a string with space:

import subprocess

subprocess.Popen("terrariad \"say something\"", shell=True)

it stop the command in the space char, output this on the terminal:

: say

Instead of the desired:

: say something
<Server>something

What could I do to solve this problem? I tried so much things but I get the same result.

P.S. If I send the command manually in the ssh putty terminal, it works!

Edit 1:

I abandoned the python solution, by now I'll try it with bash instead, seem to be more logic to do this way.

Edit 2:

I found the "terrariad" script expect just one argument, but the Popen is splitting my argument into two no matter the method I use, as my input string has one space char in the middle. Like this:

Expected:

terrariad "say\ something"

$1 = "say something"

But I get this of python Popen:

subprocess.Popen("terrariad \"say something\"", shell=True)

$1 = "say
$2 = something"

No matter i try to list it:

subprocess.Popen(["terrariad", "say something"])

$1 = "say
$2 = something"

Or use \ quote before the space char, It always split variables if it reach a space char.

Edit 3:

Looking in the bash script I could understand what is going on when I send a command... Basically it use the command "stuff", from the screen program, to send characters to the terraria screen session:

screen -S terraria -X stuff $send

$send is a printf command:

send="`printf \"$*\r\"`"

And it seems to me that if I run the bash file from Python, it has a different result than running from the command line. How this is possible? Is this a bug or bad implementation of the function?

Thanks!

  • Does this answer your question? [How to do multiple arguments with Python Popen?](https://stackoverflow.com/questions/11284147/how-to-do-multiple-arguments-with-python-popen) – SiHa Apr 21 '20 at 12:52

1 Answers1

0

I finally come with a solution to this, using pipes instead of the Popen solution.

It seems to me that Popen isn't the best solution to run bash scripts, as described in How to do multiple arguments with Python Popen?, the link that SiHa send in the comments (Thanks!):

"However, using Python as a wrapper for many system commands is not really a good idea. At the very least, you should be breaking up your commands into separate Popens, so that non-zero exits can be handled adequately. In reality, this script seems like it'd be much better suited as a shell script.".

So I came with the solution, using a fifo file:

First, create a fifo to be use as a pipe, in the desired directory (for instance, /samba/terraria/config):

mkfifo cmdOutput

*/samba/terraria - this is the directory I create in order to easily edit the scripts, save and load maps to the server using another computer, that are shared with samba (https://linuxize.com/post/how-to-install-and-configure-samba-on-ubuntu-18-04/)

Then I create a python script to read from the screen output and then write to a pipe file (I know, probably there is other ways to this):

import shlex, os

outputFile = os.open("/samba/terraria/config/cmdOutput", os.O_WRONLY )


print("python script has started!")
while 1:
    line = input()
    print(line)
    cmdPosition = line.find("&")
    if( cmdPosition != -1 ):
        cmd = slice(cmdPosition+1,len(line))
        cmdText = line[cmd]
        os.write(outputFile, bytes( cmdText + "\r\r", 'utf-8'))
        os.write(outputFile, bytes("say Command executed!!!\r\r", 'utf-8'))

Then I edit the terraria.service file to call this script, piped from terrariaServer, and redirect the errors to another file:

ExecStart=/usr/bin/screen -dmS terraria /bin/bash -c "/opt/terraria/TerrariaServer.bin.x86_64 -config /samba/terraria/config/serverconfig.txt < /samba/terraria/config/cmdOutput 2>/samba/terraria/config/errorLog.txt | python3 /samba/terraria/scripts/allowCommands.py"

*/samba/terraria/scripts/allowCommands.py - where my script is.

**/samba/terraria/config/errorLog.txt - save Log of errors in a file.

Now I can send commands, like 'noon' or 'dawn' so I can change the in-game time, save world and backup it with samba server before boss fights, do another stuff if I have some time XD, and have the terminal showing what is going on with the server.