You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

183 lines
8.1 KiB

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>How to use tmux to send and receive things from your Minecraft server | tait.tech</title>
<link rel="stylesheet" href="/assets/css/style.css">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="author" content="Tait Hoyem">
<meta name="keywords" content="">
<meta name="description" content="">
</head>
<body>
<div id="wrapper">
<header>
<h1>tait.tech</h1>
<nav>
<a href="/" class="nav-link" >Home</a>
<a href="/blog/" class="nav-link" >Blog</a>
<a href="/ideas/" class="nav-link" >Ideas</a>
<a href="/links/" class="nav-link" >Links</a>
<a href="https://github.com/TTWNO/" class="nav-link" target="_blank" rel="noopener noreferrer" >Github</a>
</nav>
</header>
<main>
<article>
<header>
<h1 class="post-title">How to use tmux to send and receive things from your Minecraft server</h1>
<time datetime="20-06-25" class="post-date">Thursday, June 25 2020</time>
</header>
<hr>
<p>So recently I had problem.
I run a Minecraft server on a big Linux computer I have running in my room.
Now, as a system administrator it is very helpful to be able to run some simple commands without needing to login with my key, password, TFA, etc.
It is, frankly, a lot of work.
Especially when I really just want to be playing games but I just need to check something quickly.</p>
<p>So for simple things like finding out of the network, CPU, memory or disk usage is my bottleneck, I wrote this really nifty script to connect the world of Minecraft and the Linux shell.</p>
<p>My completed solution for what I needed can be found at <a href="https://github.com/TTWNO/termcraft/">https://github.com/TTWNO/termcraft</a>.</p>
<p>If you want some of the implementation details, stick around.</p>
<h2 id="solution">Solution</h2>
<p>So to solve this interesting problem, I decided to use <code class="language-plaintext highlighter-rouge">tmux</code>.
<code class="language-plaintext highlighter-rouge">tmux</code> is a <strong>t</strong>terminal <strong>mu</strong>ltiple<strong>x</strong>er.
This allows you to run a terminal session, then detach fromc it while it still runs in the background.</p>
<p>This is very valuable when running command line applications that need to have an active console connection, like a Minecraft server.</p>
<p>So first I looked at the <code class="language-plaintext highlighter-rouge">tmux</code> command <code class="language-plaintext highlighter-rouge">send-keys</code>.</p>
<h4 id="send-keys"><code class="language-plaintext highlighter-rouge">send-keys</code></h4>
<p><code class="language-plaintext highlighter-rouge">send-keys</code> allows you to send text, and key presses to a <code class="language-plaintext highlighter-rouge">tmux</code> session.
Now assuming this <code class="language-plaintext highlighter-rouge">tmux</code> session is attached to a Minecraft server,
there is no reason you could not run a command like this:</p>
<pre class="terminal">
$ tmux send-keys "tell @a This is a Test" Enter
</pre>
<p>This will send the text “tell @a This is a Test” to the Minecraft server.
Then, it will hit the newline character, this will execute the command.</p>
<p>So now we can send information to the server and have it tell the users something.</p>
<p>But how do we get information about who is typing what in the Minecraft chat?</p>
<h3 id="tmuxs-capture-pane-is-painful"><code class="language-plaintext highlighter-rouge">tmux</code>s <code class="language-plaintext highlighter-rouge">capture-pane</code> is painful</h3>
<p>So in the manual page for <code class="language-plaintext highlighter-rouge">tmux</code> I can see a section recorded below for options I can give to the <code class="language-plaintext highlighter-rouge">capture-pane</code> subcommand.</p>
<pre class="terminal">
-S and -E specify the starting and ending line numbers,
zero is the first line of the visible pane and negative
numbers are lines in the history. - to -S is the start
of the history and to -E the end of the visible pane. The
default is to capture only the visible contents of the pane.
</pre>
<p>What it seems to be saying is I can start at line <code class="language-plaintext highlighter-rouge">-S n</code> and end at line <code class="language-plaintext highlighter-rouge">-E n</code>.
Negative numbers start from the bottom, so <em>in theory</em> I can do the following: <code class="language-plaintext highlighter-rouge">tmux capture-pane -S -1</code> should capture only the last line, because Im starting from the last line. Right?</p>
<p>No. It just doesnt work. Negative numbers do <em>not</em> work with the <code class="language-plaintext highlighter-rouge">tmux capture-pane</code> subcommand.</p>
<p>So I did some simple UNIX piping, like so, to get just the last thing in the chat.</p>
<pre class="terminal">
$ tmux capture-pane -p -t steve | tail -n1
[SERVER] [ExtraDebuggingInfoHere]: &lt;TaterTheTot&gt; MY_MESSAGE
</pre>
<p>TaterTheTot is my Minecraft username :)</p>
<p><code class="language-plaintext highlighter-rouge">-p</code> prints the result to the terminal/stdout.</p>
<p><code class="language-plaintext highlighter-rouge">steve</code> is the name of the tmux session Im trying to pull form.</p>
<p>So thats done! Beauty!</p>
<p>Now that we have that, how can we extract the username and the message from the latest line?</p>
<h3 id="grep"><code class="language-plaintext highlighter-rouge">grep</code></h3>
<p><code class="language-plaintext highlighter-rouge">grep</code> is a command to find patterns of text.
<code class="language-plaintext highlighter-rouge">grep</code> has an option to only show a matching pattern of text.
This option is <code class="language-plaintext highlighter-rouge">-o</code>.</p>
<p>Lets see how we can use this in conjunction with our latest line of server output to get our results.</p>
<pre class="terminal">
$ echo "[DEBUG] [SERVER] blah blah: &lt;TaterTheTot&gt; MY_MESAGE" | grep -o "&lt;.&ast;&gt;"
&lt;TaterTheTot&gt;
</pre>
<p>Now, thats my name with the &lt; and &gt; attached. Not bad!
We can use the <code class="language-plaintext highlighter-rouge">sed</code> command to clean it up a bit.</p>
<p>The syntax is like so: <code class="language-plaintext highlighter-rouge">select/somepattern/replacewith/global</code></p>
<p>So the following command is: <code class="language-plaintext highlighter-rouge">s/[&lt;&gt;]//g</code></p>
<p>Select any characters that are either &lt; or &gt;.
Replace with nothing.
Do so globally (as in, dont stop after you replace only one character).</p>
<p>Take two!</p>
<pre class="terminal">
$ echo "[DEBUG] [SERVER] blah blah: &lt;TaterTheTot&gt; MY_MESAGE" | grep -o "&lt;.&ast;&gt;" | sed 's/[&lt;&gt;]//g'
TaterTheTot
</pre>
<p>Beautiful!</p>
<p>Now what about that pesky message?</p>
<h3 id="more-grep-more-sed">more <code class="language-plaintext highlighter-rouge">grep</code>; more <code class="language-plaintext highlighter-rouge">sed</code></h3>
<p>Simple: capture everything after the &gt;. Leaving the users message entirely in tact.</p>
<pre class="terminal">
$ echo "[DEBUG] [SERVER] blah blah: &lt;TaterTheTot&gt; MY_MESAGE" | grep -o "&gt;.&ast;$" | sed 's/&gt; //'
MY_MESSAGE
</pre>
<p>So now we have a way to get the username of someone typing in the Minecraft server chat.
We have a way to find out what they said.
And, we have a way to respond.</p>
<p>You can imagine how these might go together for your own use case.</p>
<h3 id="conclusion">Conclusion</h3>
<p>This shows some pretty fun stuff you can do with a few simple Linux commands and a Minecraft server.</p>
<p>I hope you learned something and found my explanations not horrific haha!</p>
<p>Remember to checkout the git repository to see what I did with it: <a href="https://github.com/TTWNO/termcraft">https://github.com/TTWNO/termcraft</a>.</p>
<p>Happy hacking!</p>
</article>
</main>
<hr>
<footer>
This page will be mirrored on <a href="https://beta.tait.tech/2020/06/25/tmux-minecraft/">beta.tait.tech</a>.
</footer>
</div>
</body>
</html>