Compare commits

..

1 Commits

Author SHA1 Message Date
1725ecd4c7 WIP Limit one chat every 30 seconds per IP 2017-12-03 15:54:30 -05:00
15 changed files with 24 additions and 403 deletions

2
.gitignore vendored
View File

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

View File

@ -1,10 +1,12 @@
import calendar
import logging import logging
import shlex
import subprocess import subprocess
import unicodedata from datetime import datetime, timedelta
import redis
from flask import Flask, request from flask import Flask, request
conn = redis.Redis('localhost')
app = Flask(__name__) app = Flask(__name__)
@ -14,31 +16,27 @@ 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([ subprocess.call(['/usr/bin/screen', '-S', 'mc-panic-shack', '-p', '0', '-X', 'stuff',
'/usr/bin/screen', '-S', 'mc-panic-shack', '-p', '0', '-X', 'stuff', '/say [{}]: {}\015'.format(request.form['say-username'], request.form['say-text'])])
'/say [{}]: {}\015'.format(
sanitize_input(request.form['say-username']),
sanitize_input(request.form['say-text']))
])
else: else:
subprocess.call([ subprocess.call(['/usr/bin/screen', '-S', 'mc-panic-shack', '-p', '0', '-X', 'stuff',
'/usr/bin/screen', '-S', 'mc-panic-shack', '-p', '0', '-X', 'stuff', '/say {}\015'.format(request.form['say-text'])])
'/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

@ -1,13 +0,0 @@
[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

View File

@ -1,50 +0,0 @@
[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

View File

@ -1,12 +0,0 @@
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

@ -1,36 +0,0 @@
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;
}
}

View File

@ -1,130 +0,0 @@
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,18 +130,3 @@ 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;
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 328 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 496 KiB

View File

Before

Width:  |  Height:  |  Size: 76 KiB

After

Width:  |  Height:  |  Size: 76 KiB

View File

@ -7,13 +7,9 @@
<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/deathpit_shader_sm.png" alt="Screenshot from the Minecraft server of the swamp and the large house in the distance"> <img src="img/bridge.jpg" alt="Screenshot from the Minecraft server of a bridge over a lake in a jungle biome">
<h2>Server Status</h2> <h2>Server Status</h2>
<div id="server-status-error"> <div id="server-status-error">
@ -64,7 +60,6 @@
<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>
@ -74,22 +69,16 @@
<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 Maps</h2> <h2>Server Map</h2>
<a href="map/" class="map-link"> <a href="map/" id="map-link">
<img src="img/deathpit.png" alt="Screenshot from the in-browser map of the death pit server"> <img src="img/map.jpg" alt="Screenshot from the in-browser map of the server centered on the lake and bridge">
View Death Pit map View the server map
</a> </a>
<p class="small-text"> <p class="small-text">
This map is updated every day around 3 am EST. Last updated:&nbsp; Map is updated every day around 3 am EST. Last updated:&nbsp;
<span id="map-last-updated" class="small-text"></span><br> <span id="map-last-updated" class="small-text"></span>
</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>
@ -120,59 +109,6 @@
<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>

View File

@ -1,52 +0,0 @@
#!/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)

View File

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

View File

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