Initial commit

main
Jerry Aldrich 4 years ago
commit 60b57a0720
  1. 29
      client/Dockerfile
  2. 17
      client/entrypoint.sh
  3. 155
      client/index.html.template
  4. 23
      client/lighttpd.conf
  5. 24
      content/Dockerfile
  6. 28
      content/lighttpd.conf
  7. 30
      docker-compose.yaml
  8. 3
      server/.gitignore
  9. 20
      server/Dockerfile
  10. 19
      server/README.md
  11. 42
      server/entrypoint.sh
  12. 1
      server/rcon.cfg.template
  13. 24
      server/server.cfg.template

@ -0,0 +1,29 @@
FROM alpine:latest as build
RUN apk add --no-cache git
WORKDIR /src
RUN git clone --depth=1 --recurse-submodules https://github.com/glennhartmann/quakejs
FROM alpine:latest
RUN apk add --no-cache lighttpd
RUN chown -R lighttpd /var/log/lighttpd
COPY lighttpd.conf /etc/lighttpd/lighttpd.conf
COPY --from=build /src/quakejs/html/ /srv/http/
RUN chown -R lighttpd /srv
COPY index.html.template /bootstrap/index.html.template
RUN chown -R lighttpd /bootstrap
COPY entrypoint.sh /entrypoint.sh
RUN chmod +x /entrypoint.sh
USER lighttpd
EXPOSE 8080
ENTRYPOINT ["/entrypoint.sh"]
CMD ["lighttpd", "-D", "-f", "/etc/lighttpd/lighttpd.conf"]

@ -0,0 +1,17 @@
#!/bin/sh
# Exit on error
set -e
sed -i "s/QUAKEJS_TAB_TITLE/${QUAKEJS_TAB_TITLE:=Quake JS}/" /bootstrap/index.html.template
sed -i "s/QUAKEJS_CONTENT_URL/${QUAKEJS_CONTENT_URL:=content.quakejs.com}/" /bootstrap/index.html.template
sed -i "s/QUAKEJS_SERVER_URL/$QUAKEJS_SERVER_URL/" /bootstrap/index.html.template
if [ -n "$QUAKEJS_USE_SSL" ]; then
sed -i "s/'http:\/\/'/\'https:\/\/'/g" /srv/http/ioquake3.js
sed -i "s/'ws:\/\/'/\'wss:\/\/'/g" /srv/http/ioquake3.js
fi
cp /bootstrap/index.html.template /srv/http/index.html
lighttpd -D -f /etc/lighttpd/lighttpd.conf

@ -0,0 +1,155 @@
<!DOCTYPE html>
<html>
<head>
<title>QUAKEJS_TAB_TITLE</title>
<link rel="stylesheet" href="game.css"></link>
<script type="text/javascript" src="ioquake3.js"></script>
<link rel="apple-touch-icon" sizes="57x57" href="/apple-icon-57x57.png">
<link rel="apple-touch-icon" sizes="60x60" href="/apple-icon-60x60.png">
<link rel="apple-touch-icon" sizes="72x72" href="/apple-icon-72x72.png">
<link rel="apple-touch-icon" sizes="76x76" href="/apple-icon-76x76.png">
<link rel="apple-touch-icon" sizes="114x114" href="/apple-icon-114x114.png">
<link rel="apple-touch-icon" sizes="120x120" href="/apple-icon-120x120.png">
<link rel="apple-touch-icon" sizes="144x144" href="/apple-icon-144x144.png">
<link rel="apple-touch-icon" sizes="152x152" href="/apple-icon-152x152.png">
<link rel="apple-touch-icon" sizes="180x180" href="/apple-icon-180x180.png">
<link rel="icon" type="image/png" sizes="192x192" href="/android-icon-192x192.png">
<link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png">
<link rel="icon" type="image/png" sizes="96x96" href="/favicon-96x96.png">
<link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png">
<link rel="manifest" href="/manifest.json">
<meta name="msapplication-TileColor" content="#ffffff">
<meta name="msapplication-TileImage" content="/ms-icon-144x144.png">
<meta name="theme-color" content="#ffffff">
<script type="text/javascript">
function resizeViewport() {
if (!ioq3.canvas) {
// ignore if the canvas hasn't yet initialized
return;
}
if ((document['webkitFullScreenElement'] || document['webkitFullscreenElement'] ||
document['mozFullScreenElement'] || document['mozFullscreenElement'] ||
document['fullScreenElement'] || document['fullscreenElement'])) { // ignore resize events due to going fullscreen
return;
}
ioq3.setCanvasSize(ioq3.viewport.offsetWidth, ioq3.viewport.offsetHeight);
}
function startQuake(mode) {
document.getElementById('viewport-frame').style.visibility = "visible";
document.getElementById('start').style.visibility = "hidden";
ioq3.viewport = document.getElementById('viewport-frame');
ioq3.elementPointerLock = true;
ioq3.exitHandler = function (err) {
if (err) {
var form = document.createElement('form');
form.setAttribute('method', 'POST');
form.setAttribute('action', '/');
var hiddenField = document.createElement('input');
hiddenField.setAttribute('type', 'hidden');
hiddenField.setAttribute('name', 'error');
hiddenField.setAttribute('value', err);
form.appendChild(hiddenField);
document.body.appendChild(form);
form.submit();
return;
}
window.location.href = '/';
}
window.addEventListener('resize', resizeViewport);
name = document.getElementById('name').value;
model = document.getElementById('model').value;
color = document.getElementById('color').value;
var args = [];
args.push('+set', 'fs_cdn', 'QUAKEJS_CONTENT_URL');
args.push('+name', name);
args.push('+model', model + "/" + color);
if(mode == 'multiplayer') {
args.push('+connect', 'QUAKEJS_SERVER_URL');
} else {
args.push('+mode_start', 'FFA');
args.push('+map', 'q3dm1');
}
http://www.quakejs.com/play?set fs_game cpma&set mode_start FFA&set g_teamAutoJoin 1&map cpm1a
ioq3.callMain(args);
}
window.onload = function () {
document.getElementById('viewport-frame').style.visibility = "hidden";
}
</script>
</head>
<style>
body {
background: black;
}
#start {
border: 3px solid green;
margin: 0;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
text-align: left;
display: grid;
grid-template-columns: max-content max-content;
grid-gap: 5px;
}
#start * {
margin: 10px;
color: red;
cursor: pointer;
outline: none;
background-color: black;
font: calc(2.4vmin) monospace;
}
#start > label {
cursor: default;
}
#start > input[type=text] {
width: 9em;
}
#start > input[type=button] {
display: inline;
padding: 10px;
}
</style>
<body>
<div id="viewport-frame"></div>
<form id="start">
<label for="name">Player Name:</label>
<input id="name" type="text" value="Anonymous">
<label for="model">Player Model:</label>
<select id="model">
<option value="sarge">Sarge</option>
<option value="grunt">Grunt</option>
<option value="major">Major</option>
<option value="visor">Visor</option>
</select>
<label for="color">Player Color:</label>
<select id="color">
<option value="default">Default</option>
<option value="red">Red</option>
<option value="blue">Blue</option>
</select>
<input type="button" value="Singleplayer" onclick="startQuake('singleplayer')">
<input type="button" value="Multiplayer" onclick="startQuake('multiplayer')">
</form>
</body>
</html>

@ -0,0 +1,23 @@
var.logdir = "/var/log/lighttpd"
var.statedir = "/var/lib/lighttpd"
server.modules = (
"mod_access",
"mod_accesslog",
)
include "mime-types.conf"
server.document-root = "/srv/http/"
server.pid-file = "/tmp/lighttpd.pid"
server.errorlog-use-syslog = "enable"
accesslog.use-syslog = "enable"
server.indexfiles = ("index.php", "index.html", "index.htm", "default.htm")
server.port = 8080
url.access-deny = ("~", ".inc")
server.network-backend = "writev"

@ -0,0 +1,24 @@
FROM alpine:latest as build
RUN apk add --no-cache bash wget jq
WORKDIR /src
RUN wget https://steamforge.net/files/quakejs/get_assets.sh
RUN bash get_assets.sh
FROM alpine:latest
RUN apk add --no-cache lighttpd
RUN chown -R lighttpd /var/log/lighttpd
USER lighttpd
COPY lighttpd.conf /etc/lighttpd/lighttpd.conf
COPY --from=build /src/assets /srv/http/assets
EXPOSE 8080
CMD ["lighttpd", "-D", "-f", "/etc/lighttpd/lighttpd.conf"]

@ -0,0 +1,28 @@
var.logdir = "/var/log/lighttpd"
var.statedir = "/var/lib/lighttpd"
server.modules = (
"mod_access",
"mod_accesslog",
"mod_setenv",
)
setenv.add-response-header = (
"Access-Control-Allow-Origin" => "*",
)
include "mime-types.conf"
server.document-root = "/srv/http/"
server.pid-file = "/tmp/lighttpd.pid"
server.errorlog-use-syslog = "enable"
accesslog.use-syslog = "enable"
server.indexfiles = ("index.php", "index.html", "index.htm", "default.htm")
server.port = 8080
url.access-deny = ("~", ".inc")
server.network-backend = "writev"

@ -0,0 +1,30 @@
version: "3.3"
services:
content:
build: content/
ports:
- "8080:8080"
server:
build: server/
ports:
- "8888:27960"
client:
depends_on:
- content
- server
build: client/
ports:
- "9999:8080"
environment:
- "QUAKEJS_CONTENT_URL=172.69.0.1:8080"
- "QUAKEJS_SERVER_URL=172.69.0.1:8888"
# Prevent subnet conflict with OpenVPN
# Can connect to services via 172.69.0.1:PORT
networks:
default:
ipam:
driver: default
config:
- subnet: 172.69.0.0/16

3
server/.gitignore vendored

@ -0,0 +1,3 @@
quakejs/
server.cfg.override
rcon.cfg.override

@ -0,0 +1,20 @@
FROM node:16-alpine
# Read the README...it's important (TL;DR: quakejs is .gitignore'd)
COPY quakejs /srv/quakejs
COPY server.cfg.template /srv/server.cfg.template
COPY rcon.cfg.template /srv/rcon.cfg.template
COPY entrypoint.sh /srv
RUN chmod +x /srv/entrypoint.sh
RUN chown -R node /srv
RUN mkdir /bootstrap
RUN chown -R node /bootstrap
EXPOSE 27960
USER node
ENTRYPOINT ["/srv/entrypoint.sh"]

@ -0,0 +1,19 @@
# QuakeJS
## Building quakejs and assets
```
git clone --recurse-submodules https://github.com/glennhartmann/quakejs.git
cd quakejs
npm install
node build/ioq3ded.js +set fs_game baseq3 +set dedicated 1
# Hold down/press enter until you see `Agreee? (y/n)`
# You read the EULA right? Right!?!...if so...enter `y`
# Then use ctrl+c to end the server
```
> If the date of the last commit in the above quakejs GitHub repo is after
> 2020-06-09, let Jerry know, he will be amused. Email: me@jerryaldrichiii.com
After doing the above, `cd ..` then build the Docker image as you usually would

@ -0,0 +1,42 @@
#!/bin/sh
set -e
set_conf() {
echo "Setting: $1"
sed -i "s/$1/$2/" /srv/server.cfg.template
}
if [ -f /bootstrap/server.cfg.override ]; then
echo "Config file override exists, copying to final location"
cp /bootstrap/server.cfg.override /srv/quakejs/base/baseq3/server.cfg
else
set_conf QUAKEJS_NETPORT ${QUAKEJS_NETPORT:=27960}
set_conf QUAKEJS_HOSTNAME "${QUAKEJS_HOSTNAME:=Docker QuakeJS Server}"
set_conf QUAKEJS_MOTD "${QUAKEJS_MOTD:=DOCKER + QuakeJS = <3}"
set_conf QUAKEJS_MAXCLIENTS ${QUAKEJS_MAXCLIENT:=12}
set_conf QUAKEJS_QUADFACTOR ${QUAKEJS_QUADEFACTOR:=3}
set_conf QUAKEJS_GAMETYPE ${QUAKEJS_GAMETYPE:=0}
set_conf QUAKEJS_TIMELIMIT ${QUAKEJS_TIMELIMIT:=10}
set_conf QUAKEJS_FRAGLIMIT ${QUAKEJS_FRAGLIMIT:=10}
set_conf QUAKEJS_WEAPONRESPAWN ${QUAKEJS_WEAPONRESPAWN:=3}
set_conf QUAKEJS_INACTIVITY ${QUAKEJS_INACTIVITY:=300}
set_conf QUAKEJS_FORCERESPAWN ${QUAKEJS_FORCERESPAWN:=0}
set_conf QUAKEJS_BOT_ENABLE ${QUAKEJS_BOT_ENABLE:=1}
cp /srv/server.cfg.template /srv/quakejs/base/baseq3/server.cfg
fi
if [ -f /bootstrap/rcon.cfg.override ]; then
echo "RCON config file override exists, copying to final location"
cp /bootstrap/rcon.cfg.override /srv/quakejs/base/baseq3/rcon.cfg
else
set_conf QUAKEJS_RCON_PASSWORD ${QUAKEJS_RCON_PASSWORD:=changemeorbehacked}
cp /srv/rcon.cfg.template /srv/quakejs/base/baseq3/rcon.cfg
fi
cd /srv/quakejs
node build/ioq3ded.js +set fs_game baseq3 +set dedicated 0 +exec server.cfg +exec rcon.cfg

@ -0,0 +1 @@
seta rconPassword QUAKEJS_RCON_PASSWORD

@ -0,0 +1,24 @@
seta net_port QUAKEJS_NETPORT
seta rconPassword QUAKEJS_RCON_PASSWORD
seta sv_hostname QUAKEJS_HOSTNAME
seta g_motd QUAKEJS_MOTD
seta sv_maxclients QUAKEJS_MAXCLIENTS
seta g_quadfactor QUAKEJS_QUADFACTOR
seta g_gametype QUAKEJS_GAMETYPE
seta timelimit QUAKEJS_TIMELIMIT
seta fraglimit QUAKEJS_FRAGLIMIT
seta g_weaponrespawn QUAKEJS_WEAPONRESPAWN
seta g_inactivity QUAKEJS_INACTIVITY
seta g_forcerespawn QUAKEJS_FORCERESPAWN
seta bot_enable QUAKEJS_BOT_ENABLE
set d1 "map q3dm1 ; set nextmap vstr d2"
set d2 "map q3dm7 ; set nextmap vstr d3"
set d3 "map q3dm17 ; set nextmap vstr d4"
set d4 "map pro-q3tourney2 ; set nextmap vstr d5"
set d5 "map pro-q3tourney4 ; set nextmap vstr d6"
set d6 "map pro-q3dm6 ; set nextmap vstr d7"
set d7 "map pro-q3dm13 ; set nextmap vstr d8"
set d8 "map q3tourney2 ; set nextmap vstr d1"
vstr d1
Loading…
Cancel
Save