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