first commit

This commit is contained in:
Blaize committed 2023-08-08 20:20:29 +00:00
commit 78ace6d86b
9 files changed
+471

No files matched your search

BIN
View File
Binary file not shown.
+100
View File
@@ -0,0 +1,100 @@
# DOSBOX IN A CONTAINER WITH VNC CLIENT
So much fun!
1. Create a folder.
1. Place a copy of your game in the folder. I am using the shareware version of Commander Keen here.
1. In that folder, create a file called `dockerfile`, paste in the following code.
````
FROM ubuntu:22.04
ENV USER=root
ENV PASSWORD=password1
ENV DEBIAN_FRONTEND=noninteractive
ENV DEBCONF_NONINTERACTIVE_SEEN=true
COPY keen /dos/keen
RUN apt-get update && \
echo "tzdata tzdata/Areas select America" > ~/tx.txt && \
echo "tzdata tzdata/Zones/America select New York" >> ~/tx.txt && \
debconf-set-selections ~/tx.txt && \
apt-get install -y tightvncserver ratpoison dosbox novnc websockify && \
mkdir ~/.vnc/ && \
mkdir ~/.dosbox && \
echo $PASSWORD | vncpasswd -f > ~/.vnc/passwd && \
chmod 0600 ~/.vnc/passwd && \
echo "set border 0" > ~/.ratpoisonrc && \
echo "exec dosbox -conf ~/.dosbox/dosbox.conf -fullscreen -c 'MOUNT C: /dos' -c 'C:' -c 'cd keen' -c 'keen1'">> ~/.ratpoisonrc && \
export DOSCONF=$(dosbox -printconf) && \
cp $DOSCONF ~/.dosbox/dosbox.conf && \
sed -i 's/usescancodes=true/usescancodes=false/' ~/.dosbox/dosbox.conf && \
openssl req -x509 -nodes -newkey rsa:2048 -keyout ~/novnc.pem -out ~/novnc.pem -days 3650 -subj "/C=US/ST=NY/L=NY/O=NY/OU=NY/CN=NY emailAddress=email@example.com"
EXPOSE 80
CMD vncserver && websockify -D --web=/usr/share/novnc/ --cert=~/novnc.pem 80 localhost:5901 && tail -f /dev/null
````
1. Replace the COPY keen /dos/keen with your game (ie. COPY wolf3d /dos/wolf3d). 1. You can also change the default password, or override it with a -e parameter when you run the image.
1. Now, with Docker, build the image. I’m assuming you already have Docker installed and are familiar with it to some extent. CD to the directory in a console and run the command…
````
docker build -t mydosbox .
````
1. Run the image.
````
docker run -p 6080:80 mydosbox
````
1. Open a browser and point it to http://localhost:6080/vnc.html
1. You should see a prompt for the password. Type it in, and you should be able to connect to your container with DosBox running. The game is started automatically.
1. Once your image is built, you can push it to your image repository with docker push, but you’ll need to tag it appropriately.
# USE WITH KUBERNETES
Kubernetes is another part of the equation when it comes to container apps. Containers on Kubernetes are deployed into pods, which are then usually a part of a part of a deployment, which will have one or more pods associated with it. Deployments can also be used for creating scalable sets of pods for high availability too on a Kubernetes cluster. If you’re not familiar with Kubernetes, check out this webinar below where I go in depth on the matter.
Deployments and services can be defined declaratively with a YAML file. Below is a Kuberenetes YAML file that defines a deployment and a service for my retro gaming container.
The deployment is simple – it points to a single container image called blaize/keen and then tells Kubernetes what ports to expose for the container. The service defines how the deployment will be exposed on a network. In this case, it’s using a TCP load balancer, where it is exposing port 80 and mapping that to the port exposed by the deployment. The service uses selectors on the label app to match the service with the deployment.
````
apiVersion: v1
kind: Service
metadata:
name: keen-service
labels:
app: keen-deployment
spec:
ports:
- port: 80
targetPort: 6080
selector:
app: keen-deployment
type: LoadBalancer
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: keen-deployment
spec:
selector:
matchLabels:
app: keen-deployment
replicas: 1
template:
metadata:
labels:
app: keen-deployment
spec:
containers:
- name: master
image: blaize/keen
ports:
- containerPort: 80
````
To connect use this, first create a file called keen.yaml file, configure your instance kubectl to work with your instance of Kubernetes, then run deploy the sample.
````
kubectl create -f keen.yaml
````
When this is deployed to Kubernetes, Kubernetes will configure the external network to open on port 80 to listen to incoming requests. When used on Azure Kubernetes Services, AKS will create and map a public IP address (htttp://[your ip address]/vnc.html) for the service. Once connected, you can point your browser to the IP address of your cluster and have fun playing your retro games!
+1
View File
@@ -0,0 +1 @@
default-server=unix:/tmp/pulseaudio.socket
+92
View File
@@ -0,0 +1,92 @@
# This file is part of PulseAudio.
#
# PulseAudio is free software; you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# PulseAudio is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
## Configuration file for the PulseAudio daemon. See pulse-daemon.conf(5) for
## more information. Default values are commented out. Use either ; or # for
## commenting.
; daemonize = no
; fail = yes
; allow-module-loading = yes
; allow-exit = yes
; use-pid-file = yes
; system-instance = no
; local-server-type = user
; enable-shm = yes
; enable-memfd = yes
; shm-size-bytes = 0 # setting this 0 will use the system-default, usually 64 MiB
; lock-memory = no
; cpu-limit = no
; high-priority = yes
; nice-level = -11
; realtime-scheduling = yes
; realtime-priority = 5
; exit-idle-time = 20
; scache-idle-time = 20
; dl-search-path = (depends on architecture)
; load-default-script-file = yes
; default-script-file = /etc/pulse/default.pa
; log-target = auto
; log-level = notice
; log-meta = no
; log-time = no
; log-backtrace = 0
; resample-method = speex-float-1
; avoid-resampling = false
; enable-remixing = yes
; remixing-use-all-sink-channels = yes
; remixing-produce-lfe = no
; remixing-consume-lfe = no
; lfe-crossover-freq = 0
; flat-volumes = no
; rescue-streams = yes
; rlimit-fsize = -1
; rlimit-data = -1
; rlimit-stack = -1
; rlimit-core = -1
; rlimit-as = -1
; rlimit-rss = -1
; rlimit-nproc = -1
; rlimit-nofile = 256
; rlimit-memlock = -1
; rlimit-locks = -1
; rlimit-sigpending = -1
; rlimit-msgqueue = -1
; rlimit-nice = 31
; rlimit-rtprio = 9
; rlimit-rttime = 200000
; default-sample-format = s16le
; default-sample-rate = 44100
; alternate-sample-rate = 48000
; default-sample-channels = 2
; default-channel-map = front-left,front-right
default-fragments = 4
default-fragment-size-msec = 5
; enable-deferred-volume = yes
deferred-volume-safety-margin-usec = 1
; deferred-volume-extra-delay-usec = 0
+3
View File
@@ -0,0 +1,3 @@
#!/usr/bin/pulseaudio -nF
load-module module-native-protocol-unix socket=/tmp/pulseaudio.socket auth-anonymous=1
load-module module-always-sink
+76
View File
@@ -0,0 +1,76 @@
FROM ubuntu:22.04
#User Settings for VNC
ENV USER=root
ENV PASSWORD=password1
#Variables for installation
ENV DEBIAN_FRONTEND=noninteractive
ENV DEBCONF_NONINTERACTIVE_SEEN=true
ENV XKB_DEFAULT_RULES=base
#Install dependencies
RUN apt-get update && \
echo "tzdata tzdata/Areas select America" > ~/tx.txt && \
echo "tzdata tzdata/Zones/America select New York" >> ~/tx.txt && \
debconf-set-selections ~/tx.txt && \
apt-get install -y unzip gnupg apt-transport-https wget software-properties-common ratpoison novnc websockify libxv1 libglu1-mesa xauth x11-utils xorg tightvncserver libegl1-mesa xauth x11-xkb-utils software-properties-common bzip2 gstreamer1.0-plugins-good gstreamer1.0-pulseaudio gstreamer1.0-tools libglu1-mesa libgtk2.0-0 libncursesw5 libopenal1 libsdl-image1.2 libsdl-ttf2.0-0 libsdl1.2debian libsndfile1 nginx pulseaudio supervisor ucspi-tcp wget build-essential ccache dosbox
#Copy the files for audio and NGINX
COPY default.pa client.conf /etc/pulse/
COPY nginx.conf /etc/nginx/
COPY webaudio.js /usr/share/novnc/core/
COPY daemon.conf /etc/pulse/daemon.conf
#Inject code for audio in the NoVNC client
RUN sed -i "/import RFB/a \
import WebAudio from '/core/webaudio.js'" \
/usr/share/novnc/app/ui.js \
&& sed -i "/UI.rfb.resizeSession/a \
var loc = window.location, new_uri; \
if (loc.protocol === 'https:') { \
new_uri = 'wss:'; \
} else { \
new_uri = 'ws:'; \
} \
new_uri += '//' + loc.host; \
new_uri += '/audio'; \
var wa = new WebAudio(new_uri); \
document.addEventListener('keydown', e => { wa.start(); });" \
/usr/share/novnc/app/ui.js
#Install VirtualGL and TurboVNC
RUN wget https://gigenet.dl.sourceforge.net/project/virtualgl/3.1/virtualgl_3.1_amd64.deb && \
wget https://zenlayer.dl.sourceforge.net/project/turbovnc/3.0.3/turbovnc_3.0.3_amd64.deb && \
dpkg -i virtualgl_*.deb && \
dpkg -i turbovnc_*.deb
# Configure NoVNC
RUN mkdir ~/.vnc/ && \
mkdir ~/.dosbox && \
echo $PASSWORD | vncpasswd -f > ~/.vnc/passwd && \
chmod 0600 ~/.vnc/passwd && \
echo "set border 0" > ~/.ratpoisonrc && \
echo "exec cd /squashfs-root && EMU86BOX_MOUSE=evdev ./AppRun">> ~/.ratpoisonrc && \
export DOSCONF=$(dosbox -printconf) && \
cp $DOSCONF ~/.dosbox/dosbox.conf && \
sed -i 's/usescancodes=true/usescancodes=false/' ~/.dosbox/dosbox.conf && \
openssl req -x509 -nodes -newkey rsa:2048 -keyout ~/novnc.pem -out ~/novnc.pem -days 3650 -subj "/C=US/ST=NY/L=NY/O=NY/OU=NY/CN=NY emailAddress=email@example.com"
EXPOSE 80
COPY 86Box.AppImage .
RUN apt-get install -y lxterminal libfuse2 git && \
chmod +x 86Box.AppImage && \
./86Box.AppImage --appimage-extract && \
cd squashfs-root && \
git clone https://github.com/86Box/roms.git
#Copy in supervisor configuration for startup
COPY supervisord.conf /etc/supervisor/supervisord.conf
ENTRYPOINT [ "supervisord", "-c", "/etc/supervisor/supervisord.conf" ]
+40
View File
@@ -0,0 +1,40 @@
user root;
worker_processes 1;
events {
worker_connections 4096;
}
http {
include mime.types;
default_type application/octet-stream;
# Sending fille Optimization
sendfile on;
tcp_nopush on;
tcp_nodelay on;
# Keepalive Connection
keepalive_timeout 65;
server {
listen 80;
location /audio {
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "Upgrade";
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_pass http://127.0.0.1:8081;
}
location / {
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "Upgrade";
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_pass http://127.0.0.1:8080;
}
}
}
+38
View File
@@ -0,0 +1,38 @@
[supervisord]
nodaemon=true
pidfile=/root/supervisord.pid
logfile=/root/supervisord.log
[program:vncserver]
command=/opt/TurboVNC/bin/vncserver
stdout_logfile=/root/x11vnc.log
redirect_stderr=true
[program:websockify_vnc]
command=websockify --web /usr/share/novnc 8080 127.0.0.1:5901
stdout_logfile=/root/websockify-vnc.log
redirect_stderr=true
[program:pulseaudio]
command=/usr/bin/pulseaudio --disallow-module-loading -vvvv --disallow-exit --exit-idle-time=-1
stdout_logfile=/root/pulseaudio.log
redirect_stderr=true
[program:audiostream]
command=tcpserver 127.0.0.1 5902 gst-launch-1.0 -q pulsesrc server=/tmp/pulseaudio.socket ! audio/x-raw, channels=2, rate=24000 ! opusenc ! webmmux ! fdsink fd=1
stdout_logfile=/root/audiostream.log
redirect_stderr=true
[program:websockify_audio]
command=websockify 8081 127.0.0.1:5902
stdout_logfile=/root/websockify-audio.log
redirect_stderr=true
[program:nginx]
command=/usr/sbin/nginx -g "daemon off;"
autostart=true
autorestart=true
startretries=5
numprocs=1
startsecs=0
stdout_logfile=/root/nginx.log
+121
View File
@@ -0,0 +1,121 @@
export default class WebAudio {
constructor(url) {
this.url = url
this.connected = false;
//constants for audio behavoir
this.maximumAudioLag =0.25; //amount of seconds we can potentially be behind the server audio stream
this.syncLagInterval = 500; //check every x milliseconds if we are behind the server audio stream
this.updateBufferEvery = 20; //add recieved data to the player buffer every x milliseconds
this.reduceBufferInterval = 20; //trim the output audio stream buffer every x milliseconds so we don't overflow
this.maximumSecondsOfBuffering = .2; //maximum amount of data to store in the play buffer
this.connectionCheckInterval = 100; //check the connection every x milliseconds
//register all our background timers. these need to be created only once - and will run independent of the object's streams/properties
setInterval(() => this.updateQueue(), this.updateBufferEvery);
setInterval(() => this.syncInterval(), this.syncLagInterval);
setInterval(() => this.reduceBuffer(), this.reduceBufferInterval);
setInterval(() => this.tryLastPacket(), this.connectionCheckInterval);
}
//registers all the event handlers for when this stream is closed - or when data arrives.
registerHandlers() {
this.mediaSource.addEventListener('sourceended', e => this.socketDisconnected(e))
this.mediaSource.addEventListener('sourceclose', e => this.socketDisconnected(e))
this.mediaSource.addEventListener('error', e => this.socketDisconnected(e))
this.buffer.addEventListener('error', e => this.socketDisconnected(e))
this.buffer.addEventListener('abort', e => this.socketDisconnected(e))
}
//starts the web audio stream. only call this method on button click.
start() {
if (!!this.connected) return;
if (!!this.audio) this.audio.remove();
this.queue = null;
this.mediaSource = new MediaSource()
this.mediaSource.addEventListener('sourceopen', e => this.onSourceOpen())
//first we need a media source - and an audio object that contains it.
this.audio = document.createElement('audio');
this.audio.src = window.URL.createObjectURL(this.mediaSource);
//start our stream - we can only do this on user input
this.audio.play();
}
wsConnect() {
if (!!this.socket) this.socket.close();
this.socket = new WebSocket(this.url, ['binary', 'base64'])
this.socket.binaryType = 'arraybuffer'
this.socket.addEventListener('message', e => this.websocketDataArrived(e), false);
}
//this is called when the media source contains data
onSourceOpen(e) {
this.buffer = this.mediaSource.addSourceBuffer('audio/webm; codecs="opus"')
this.registerHandlers();
this.wsConnect();
}
//whenever data arrives in our websocket this is called.
websocketDataArrived(e) {
this.lastPacket = Date.now();
this.connected = true;
this.queue = this.queue == null ? e.data : this.concat(this.queue, e.data);
}
//whenever a disconnect happens this is called.
socketDisconnected(e) {
console.log(e);
this.connected = false;
}
tryLastPacket() {
if (this.lastPacket == null) return;
if ((Date.now() - this.lastPacket) > 1000) {
this.socketDisconnected('timeout');
}
}
//this updates the buffer with the data from our queue
updateQueue() {
if (!(!!this.queue && !!this.buffer && !this.buffer.updating)) {
return;
}
this.buffer.appendBuffer(this.queue);
this.queue = null;
}
//reduces the stream buffer to the minimal size that we need for streaming
reduceBuffer() {
if (!(this.buffer && !this.buffer.updating && !!this.audio && !!this.audio.currentTime && this.audio.currentTime > 1)) {
return;
}
this.buffer.remove(0, this.audio.currentTime - 1);
}
//synchronizes the current time of the stream with the server
syncInterval() {
if (!(this.audio && this.audio.currentTime && this.audio.currentTime > 1 && this.buffer && this.buffer.buffered && this.buffer.buffered.length > 1)) {
return;
}
var currentTime = this.audio.currentTime;
var targetTime = this.buffer.buffered.end(this.buffer.buffered.length - 1);
if (targetTime > (currentTime + this.maximumAudioLag)) this.audio.fastSeek(targetTime);
}
//joins two data arrays - helper function
concat(buffer1, buffer2) {
var tmp = new Uint8Array(buffer1.byteLength + buffer2.byteLength);
tmp.set(new Uint8Array(buffer1), 0);
tmp.set(new Uint8Array(buffer2), buffer1.byteLength);
return tmp.buffer;
};
}