commit
60b57a0720
13 changed files with 415 additions and 0 deletions
@ -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 |
@ -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…
Reference in new issue