Compare commits

..

15 Commits

Author SHA1 Message Date
c90cb7d000 Update images 2020-09-07 16:41:58 -04:00
ac51f5d3ae Add new overviewer map 2020-09-06 02:53:11 -04:00
7a08ba4ac2 Resume update for September 2020 2020-09-06 02:01:39 -04:00
d449146207 Upgrade server, added old-worlds directory 2019-07-15 10:31:15 -04:00
86e9ab7c88 Update notice 2018-08-22 13:43:06 -04:00
25df631ce8 Add maintenance notice about generating new map
Have to take the server down while map is generating. The minecraft overviewer
1.13 branch is abysmally slow generating a new map.
2018-08-21 20:20:04 -04:00
1fd090e379 More notices about map not working in 1.13 2018-08-17 13:39:19 -04:00
56580c2afe Add upgrade notice to webpage 2018-08-16 12:22:28 -04:00
6845a6f232 Add notices and info about username whitelist 2018-04-15 15:08:04 -04:00
55c01499dd Just use one gunicorn worker to save memory 2018-03-27 15:58:50 -04:00
21caa3f7a1 Sanitize chat input
Don't let someone send a chat message with "^C" and kill the server.
2018-03-27 15:58:33 -04:00
095de45d54 Changing config symlinks to hard links w/ content 2017-12-08 10:30:18 -05:00
e276004266 Add config files to config/ subdir 2017-12-07 15:24:24 -05:00
2d28923d3b Add overviewerConfig.py to repo 2017-12-07 15:18:47 -05:00
75ef33e181 Add script that notifies of joins over IFTTT 2017-12-04 13:34:01 -05:00
15 changed files with 403 additions and 24 deletions

2
.gitignore vendored
View File

@ -1,2 +1,4 @@
server.log server.log
map map
map-old
secrets.py

View File

@ -1,12 +1,10 @@
import calendar
import logging import logging
import shlex
import subprocess import subprocess
from datetime import datetime, timedelta import unicodedata
import redis
from flask import Flask, request from flask import Flask, request
conn = redis.Redis('localhost')
app = Flask(__name__) app = Flask(__name__)
@ -16,27 +14,31 @@ def setup_logging():
app.logger.setLevel(logging.INFO) app.logger.setLevel(logging.INFO)
def sanitize_input(input):
input = "".join(ch for ch in input if unicodedata.category(ch)[0] != "C")
return shlex.quote(input.replace('^', ''))
@app.route('/chat/', methods=['POST']) @app.route('/chat/', methods=['POST'])
def send_chat(): def send_chat():
if request.method == 'POST': if request.method == 'POST':
ip = request.headers.get('X-Forwarded-For', request.remote_addr)
recent_ips = conn.hgetall("minecraft_chat_recent_ips")
now = datetime.utcnow()
if ip in recent_ips:
if (now - datetime.fromutctimestamp(recent_ips[ip])) > timedelta.seconds(30):
recent_ips[ip] = calendar.timegm(now.utctimetuple())
if request.form.get('email', None): if request.form.get('email', None):
return 'Text was entered into honeypot!', 200 return 'Text was entered into honeypot!', 200
if not request.form.get('say-text', None): if not request.form.get('say-text', None):
return 'No message to send!', 422 return 'No message to send!', 422
if request.form.get('say-username', None): if request.form.get('say-username', None):
subprocess.call(['/usr/bin/screen', '-S', 'mc-panic-shack', '-p', '0', '-X', 'stuff', subprocess.call([
'/say [{}]: {}\015'.format(request.form['say-username'], request.form['say-text'])]) '/usr/bin/screen', '-S', 'mc-panic-shack', '-p', '0', '-X', 'stuff',
'/say [{}]: {}\015'.format(
sanitize_input(request.form['say-username']),
sanitize_input(request.form['say-text']))
])
else: else:
subprocess.call(['/usr/bin/screen', '-S', 'mc-panic-shack', '-p', '0', '-X', 'stuff', subprocess.call([
'/say {}\015'.format(request.form['say-text'])]) '/usr/bin/screen', '-S', 'mc-panic-shack', '-p', '0', '-X', 'stuff',
'/say {}\015'.format(sanitize_input(request.form['say-text']))
])
return 'Sending chat: ' + request.form.get('say-username', '') + ': ' + request.form['say-text'] return 'Sending chat: ' + request.form.get('say-username', '') + ': ' + request.form['say-text']
if __name__ == "__main__": if __name__ == "__main__":
app.run(host='0.0.0.0', port="8888") app.run(host='0.0.0.0', port="8888")

View File

@ -0,0 +1,13 @@
[Unit]
Description=Gunicorn instance to serve minecraft-chat wsgi service
After=network.target
[Service]
User=thallada
Group=www-data
WorkingDirectory=/var/www/panic-shack/chat
Environment="PATH=/home/thallada/.virtualenvs/minecraft-chat/bin"
ExecStart=/home/thallada/.virtualenvs/minecraft-chat/bin/gunicorn --workers 1 --bind unix:minecraft-chat.sock -m 007 server:app --log-file /srv/minecraft-panic-shack/gunicorn.log
[Install]
WantedBy=multi-user.target

50
config/minecraft@.service Normal file
View File

@ -0,0 +1,50 @@
[Unit]
Description=Minecraft Server %i
After=network.target
[Service]
WorkingDirectory=/srv/minecraft-%i
User=thallada
Group=thallada
ProtectSystem=full
ProtectHome=true
ProtectKernelTunables=true
ProtectKernelModules=true
ProtectControlGroups=true
ExecStart=/bin/sh -c '/usr/bin/screen -DmLS mc-%i /usr/bin/java -server -Xms1G -Xmx2G -XX:+UseG1GC -XX:+CMSIncrementalPacing -XX:+CMSClassUnloadingEnabled -XX:ParallelGCThreads=2 -XX:MinHeapFreeRatio=5 -XX:MaxHeapFreeRatio=10 -jar $(ls -v | grep -i "FTBServer.*jar\|minecraft_server.*jar" | head -n 1) nogui'
ExecReload=/usr/bin/screen -p 0 -S mc-%i -X eval 'stuff "reload"\\015'
ExecStop=/usr/bin/screen -p 0 -S mc-%i -X eval 'stuff "say SERVER SHUTTING DOWN IN 15 SECONDS..."\015'
ExecStop=/bin/sleep 5
ExecStop=/usr/bin/screen -p 0 -S mc-%i -X eval 'stuff "say SERVER SHUTTING DOWN IN 10 SECONDS..."\015'
ExecStop=/bin/sleep 5
ExecStop=/usr/bin/screen -p 0 -S mc-%i -X eval 'stuff "say SERVER SHUTTING DOWN IN 5 SECONDS..."\015'
ExecStop=/bin/sleep 5
ExecStop=/usr/bin/screen -p 0 -S mc-%i -X eval 'stuff "say SERVER SHUTTING DOWN. Saving map..."\\015'
ExecStop=/usr/bin/screen -p 0 -S mc-%i -X eval 'stuff "save-all"\\015'
ExecStop=/usr/bin/screen -p 0 -S mc-%i -X eval 'stuff "stop"\\015'
ExecStop=/bin/sleep 2
Restart=on-failure
RestartSec=60s
[Install]
WantedBy=multi-user.target
#########
# HowTo
#########
#
# Create directory in /opt/minecraft-XX where XX is a name like 'survival'
# Add minecraft_server.jar into dir with other conf files for minecraft server
#
# Enable/Start systemd service
# systemctl enable minecraft@survival
# systemctl start minecraft@survival
#
# To run multiple servers simply create a new dir structure and enable/start it
# systemctl enable minecraft@creative
# systemctl start minecraft@creative

12
config/nginx-panic-shack Normal file
View File

@ -0,0 +1,12 @@
server {
listen 80;
listen [::]:80;
root /var/www/panic-shack;
server_name panic-shack.hallada.net;
location / {
return 301 https://panic-shack.hallada.net$request_uri;
}
}

View File

@ -0,0 +1,36 @@
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name panic-shack.hallada.net;
root /var/www/panic-shack;
ssl_certificate /etc/letsencrypt/live/panic-shack.hallada.net/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/panic-shack.hallada.net/privkey.pem;
ssl_trusted_certificate /etc/letsencrypt/live/panic-shack.hallada.net/fullchain.pem;
location /notify {
deny all;
return 404;
}
location /config {
deny all;
return 404;
}
location / {
try_files $uri $uri/ $uri.php?$args =404;
}
location /old-worlds {
autoindex on;
try_files $uri $uri/ $uri.php?$args =404;
}
location /chat {
include uwsgi_params;
proxy_set_header X-Forwarded-Host $host:$server_port; proxy_set_header X-Forwarded-Server $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_pass http://unix:/var/www/panic-shack/chat/minecraft-chat.sock;
}
}

130
config/overviewerConfig.py Normal file
View File

@ -0,0 +1,130 @@
worlds["Death Pit"] = "/srv/minecraft-panic-shack/world"
renders["normalrender"] = {
"world": "Death Pit",
"title": "North",
"rendermode": smooth_lighting,
"dimension": "overworld",
"northdirection": "upper-left",
}
renders["normalrendersouth"] = {
"world": "Death Pit",
"title": "South",
"rendermode": smooth_lighting,
"dimension": "overworld",
"northdirection": "lower-right",
}
renders["normalrenderwest"] = {
"world": "Death Pit",
"title": "West",
"rendermode": smooth_lighting,
"dimension": "overworld",
"northdirection": "upper-right",
}
renders["normalrendereast"] = {
"world": "Death Pit",
"title": "East",
"rendermode": smooth_lighting,
"dimension": "overworld",
"northdirection": "lower-left",
}
renders["nightrender"] = {
"world": "Death Pit",
"title": "Night North",
"rendermode": smooth_night,
"dimension": "overworld",
"northdirection": "upper-left",
}
renders["nightrendersouth"] = {
"world": "Death Pit",
"title": "Night South",
"rendermode": smooth_night,
"dimension": "overworld",
"northdirection": "lower-right",
}
renders["nightrenderwest"] = {
"world": "Death Pit",
"title": "Night West",
"rendermode": smooth_night,
"dimension": "overworld",
"northdirection": "upper-right",
}
renders["nightrendereast"] = {
"world": "Death Pit",
"title": "Night East",
"rendermode": smooth_night,
"dimension": "overworld",
"northdirection": "lower-left",
}
renders["caverender"] = {
"world": "Death Pit",
"title": "Cave North",
"rendermode": cave,
"dimension": "overworld",
"northdirection": "upper-left",
}
renders["caverendersouth"] = {
"world": "Death Pit",
"title": "Cave South",
"rendermode": cave,
"dimension": "overworld",
"northdirection": "lower-right",
}
renders["caverenderwest"] = {
"world": "Death Pit",
"title": "Cave West",
"rendermode": cave,
"dimension": "overworld",
"northdirection": "upper-right",
}
renders["caverendereast"] = {
"world": "Death Pit",
"title": "Cave East",
"rendermode": cave,
"dimension": "overworld",
"northdirection": "lower-left",
}
renders["netherrender"] = {
"world": "Death Pit",
"title": "Nether North",
"rendermode": nether,
"dimension": "nether",
"northdirection": "upper-left",
}
renders["netherrendersouth"] = {
"world": "Death Pit",
"title": "Nether South",
"rendermode": nether,
"dimension": "nether",
"northdirection": "lower-right",
}
renders["netherrenderwest"] = {
"world": "Death Pit",
"title": "Nether West",
"rendermode": nether,
"dimension": "nether",
"northdirection": "upper-right",
}
renders["netherrendereast"] = {
"world": "Death Pit",
"title": "Nether East",
"rendermode": nether,
"dimension": "nether",
"northdirection": "lower-left",
}
outputdir = "/var/www/panic-shack/map/"

View File

@ -68,7 +68,7 @@ ol li code {
margin-bottom: 2px; margin-bottom: 2px;
} }
#map-link { .map-link {
display: block; display: block;
text-align: center; text-align: center;
} }
@ -130,3 +130,18 @@ ol li code {
margin-top: 5px; margin-top: 5px;
margin-bottom: 0; margin-bottom: 0;
} }
.maintenance-notice {
padding: 20px;
background: darkred;
color: white;
}
.maintenance-notice h3 {
margin: 0;
margin-right: 20px;
}
.maintenance-notice h3,span {
display: inline-block;
}

BIN
img/deathpit.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 328 KiB

BIN
img/deathpit_shader_sm.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 496 KiB

View File

Before

Width:  |  Height:  |  Size: 76 KiB

After

Width:  |  Height:  |  Size: 76 KiB

View File

@ -7,9 +7,13 @@
<meta charset="UTF-8"> <meta charset="UTF-8">
</head> </head>
<body> <body>
<!-- <div class="maintenance-notice"> -->
<!-- <h3>Server is down</h3> -->
<!-- <span>ETA: 15 hours for generating a new Minecraft 1.13 map.</span> -->
<!-- </div> -->
<div class="width-limit"> <div class="width-limit">
<h1>Panic Shack - Minecraft Server</h1> <h1>Panic Shack - Minecraft Server</h1>
<img src="img/bridge.jpg" alt="Screenshot from the Minecraft server of a bridge over a lake in a jungle biome"> <img src="img/deathpit_shader_sm.png" alt="Screenshot from the Minecraft server of the swamp and the large house in the distance">
<h2>Server Status</h2> <h2>Server Status</h2>
<div id="server-status-error"> <div id="server-status-error">
@ -60,6 +64,7 @@
<h2>How to Connect</h2> <h2>How to Connect</h2>
<ol> <ol>
<li>Email me at tyler@hallada.net with your Minecraft username to request to join the server</li>
<li>Launch Minecraft (version <span id="info-server-name">1.12.2</span>)</li> <li>Launch Minecraft (version <span id="info-server-name">1.12.2</span>)</li>
<li>Go to "Multiplayer"</li> <li>Go to "Multiplayer"</li>
<li>Click "Add Server"</li> <li>Click "Add Server"</li>
@ -69,16 +74,22 @@
<li>You should now see the server in the list and be able to join it!</li> <li>You should now see the server in the list and be able to join it!</li>
</ol> </ol>
<h2>Server Map</h2> <h2>Server Maps</h2>
<a href="map/" id="map-link"> <a href="map/" class="map-link">
<img src="img/map.jpg" alt="Screenshot from the in-browser map of the server centered on the lake and bridge"> <img src="img/deathpit.png" alt="Screenshot from the in-browser map of the death pit server">
View the server map View Death Pit map
</a> </a>
<p class="small-text"> <p class="small-text">
Map is updated every day around 3 am EST. Last updated:&nbsp; This map is updated every day around 3 am EST. Last updated:&nbsp;
<span id="map-last-updated" class="small-text"></span> <span id="map-last-updated" class="small-text"></span><br>
</p>
<a href="map-old/" class="map-link">
<img src="img/map-old.jpg" alt="Screenshot from the in-browser map of the panic-shack server centered on the lake and bridge">
View Panic Shack map
</a>
<p class="small-text">
This map was last updated: 04/04/2019.
</p> </p>
<h2>Server Log</h2> <h2>Server Log</h2>
</div> </div>
<pre id="server-log"></pre> <pre id="server-log"></pre>
@ -109,6 +120,59 @@
<span id="say-error" class="inline"></span> <span id="say-error" class="inline"></span>
<p id="say-notice" class="small-text">It may take a minute or two before the message appears in the above server log.</p> <p id="say-notice" class="small-text">It may take a minute or two before the message appears in the above server log.</p>
</form> </form>
<hr>
<h2>Download Old Worlds</h2>
<p>
<a href="/old-worlds">Visit here to download all of the previous worlds on this server.</a> You can load
them in Minecraft and play them in single player by following
<a href="https://minecraft.gamepedia.com/Tutorials/Map_downloads">these instructions</a>.
</p>
<h2>Notices</h2>
<p>
<ul>
<li>
<strong>September 5, 2020</strong>
Server is back up and running on Minecraft 1.16.2 with a new map.
</li>
<li>
<strong>May 18, 2020</strong>
Hibernating this server for a while to save resources for a 7DaysToDie server instead.
</li>
<li>
<strong>April 19, 2020</strong>
I switched the server over to a Spigot 1.15.2 server with the Vivecraft extension installed.
</li>
<li>
<strong>July 14, 2019:</strong>
Upgraded the server to 1.14.3 and regenerated the map. It's now using the seed "hardlead".
</li>
<li>
<strong>August 22, 2018:</strong>
Upgraded the server yet again to 1.13.1.
</li>
<li>
<strong>August 22, 2018:</strong>
The map should now be updating again nightly at 3 am EST.
</li>
<li>
<strong>August 16, 2018:</strong>
I upgraded the server to Minecraft version 1.13. The map is no longer working on this version and
won't update. I'll upgrade the map once it supports 1.13.
</li>
<li>
<strong>April 15, 2018:</strong>
The server has a user white-list. If you are not added to it then you cannot join the server.
Email me at tyler@hallada.net to request to join the server.
</li>
<li>
<strong>April 13, 2018:</strong>
I doubled the memory on the server so the server shouldn't keep running out of memory and crashing.
</li>
</ul>
</p>
</div> </div>
<script src="https://mcapi.us/scripts/minecraft.js"></script> <script src="https://mcapi.us/scripts/minecraft.js"></script>

52
notify/notify_joins.py Executable file
View File

@ -0,0 +1,52 @@
#!/usr/bin/env python
# Read the Minecraft server log and diff it against the server log it saw last. If there any new joins in the diff, send
# a notification.
import codecs
import os
import re
from datetime import datetime
import requests
from secrets import IFTTT_WEBHOOK_KEY_TYLER, IFTTT_WEBHOOK_KEY_KAELAN
LOG_FILENAME = '/srv/minecraft-panic-shack/logs/latest.log'
OLD_LOG_FILENAME = '/srv/minecraft-panic-shack/logs/last-read.log'
USERNAME_BLACKLIST = ['anarchyeight', 'kinedactyl']
IFTTT_EVENT_NAME = 'user_joined_panic_shack'
def read_log(filename):
with codecs.open(filename, encoding='utf-8') as log:
return log.readlines()
def save_log(filename, lines):
with codecs.open(filename, 'w', encoding='utf-8') as log:
log.writelines(lines)
if __name__ == '__main__':
if (datetime.fromtimestamp(os.path.getmtime(LOG_FILENAME)) >
datetime.fromtimestamp(os.path.getmtime(OLD_LOG_FILENAME))):
new_log = read_log(LOG_FILENAME)
old_log = read_log(OLD_LOG_FILENAME)
if new_log[0] != old_log[0]:
# A log rotate occured
old_log = []
if len(new_log) > len(old_log):
for new_line in new_log[len(old_log):]:
match = re.match('[\[][0-9:]+[\]]\s[\[]Server thread/INFO]: (\S+) joined the game', new_line)
if match:
username = match.group(1)
if username not in USERNAME_BLACKLIST:
# IFTTT does not support sharing Applets anymore :(
r = requests.post(
'https://maker.ifttt.com/trigger/{}/with/key/{}'.format(IFTTT_EVENT_NAME,
IFTTT_WEBHOOK_KEY_TYLER),
data={'value1': username})
r = requests.post(
'https://maker.ifttt.com/trigger/{}/with/key/{}'.format(IFTTT_EVENT_NAME,
IFTTT_WEBHOOK_KEY_KAELAN),
data={'value1': username})
save_log(OLD_LOG_FILENAME, new_log)

1
old-worlds Symbolic link
View File

@ -0,0 +1 @@
/srv/minecraft-panic-shack/old-worlds

2
tox.ini Normal file
View File

@ -0,0 +1,2 @@
[flake8]
max_line_length = 120