OrbitMC commited on
Commit
1303c06
·
verified ·
1 Parent(s): 8f4d999

Update Dockerfile

Browse files
Files changed (1) hide show
  1. Dockerfile +123 -214
Dockerfile CHANGED
@@ -1,223 +1,132 @@
1
- FROM node:20-alpine
2
-
3
- WORKDIR /app
4
-
5
- # Install WebSocket dependency
6
- RUN npm install ws
7
-
8
- # Write the entire server and client into a single file inside the container
9
- COPY <<'EOF' server.js
10
- const http = require('http');
11
- const crypto = require('crypto');
12
- const WebSocket = require('ws');
13
-
14
- const PORT = 7860;
15
- const players = {};
16
-
17
- // --- HTTP SERVER (Cookie Handling & HTML Delivery) ---
18
- const server = http.createServer((req, res) => {
19
- const cookieHeader = req.headers.cookie || '';
20
- let playerId = cookieHeader.split('; ').find(row => row.startsWith('playerId='))?.split('=')[1];
21
- let cookieSetHeader = [];
22
-
23
- if (!playerId) {
24
- playerId = crypto.randomUUID();
25
- cookieSetHeader.push(`playerId=${playerId}; Max-Age=31536000; Path=/; SameSite=Strict`);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
26
  }
27
 
28
- if (!players[playerId]) {
29
- players[playerId] = {
30
- id: playerId,
31
- x: Math.random() * 300 + 50,
32
- y: Math.random() * 300 + 50,
33
- color: `#${Math.floor(crypto.randomInt(16777215)).toString(16).padStart(6, '0')}`
34
- };
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
35
  }
36
 
37
- res.writeHead(200, { 'Content-Type': 'text/html', 'Set-Cookie': cookieSetHeader });
38
- res.end(`
39
- <!DOCTYPE html>
40
- <html>
41
- <head>
42
- <meta charset="UTF-8">
43
- <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
44
- <title>HF Multiplayer Game</title>
45
- <style>
46
- * { box-sizing: border-box; margin: 0; padding: 0; user-select: none; }
47
- body, html { width: 100%; height: 100%; overflow: hidden; background: #111; font-family: sans-serif; }
48
- canvas { display: block; width: 100vw; height: 100vh; background: #1a1a1a; }
49
- #ui { position: absolute; top: 10px; left: 10px; color: #fff; pointer-events: none; font-size: 14px; background: rgba(0,0,0,0.6); padding: 8px 12px; border-radius: 4px; font-variant-numeric: tabular-nums; }
50
- </style>
51
- </head>
52
- <body>
53
- <div id="ui">Status: Connecting... | Ping: --ms</div>
54
- <canvas id="gameCanvas"></canvas>
55
- <script>
56
- const canvas = document.getElementById('gameCanvas');
57
- const ctx = canvas.getContext('2d');
58
- const ui = document.getElementById('ui');
59
- let myId = "${playerId}";
60
- let players = {};
61
- let ws;
62
- let lastPingTime = 0;
63
-
64
- function resize() { canvas.width = window.innerWidth; canvas.height = window.innerHeight; }
65
- window.addEventListener('resize', resize); resize();
66
-
67
- const wsProtocol = window.location.protocol === 'https:' ? 'wss://' : 'ws://';
68
- ws = new WebSocket(wsProtocol + window.location.host);
69
-
70
- ws.onopen = () => {
71
- ui.innerText = "Connected! | Ping: ...";
72
- // High-frequency ping keep-alive loop to keep connection hot
73
- setInterval(() => {
74
- if (ws.readyState === WebSocket.OPEN) {
75
- lastPingTime = performance.now();
76
- ws.send('P');
77
- }
78
- }, 1500);
79
- };
80
-
81
- ws.onclose = () => ui.innerText = "Disconnected! Reconnecting...";
82
-
83
- ws.onmessage = (event) => {
84
- const data = event.data;
85
-
86
- // Fast Pong Parsing
87
- if (data[0] === 'O') {
88
- const latency = Math.round(performance.now() - lastPingTime);
89
- ui.innerText = "Connected! | Ping: " + latency + "ms";
90
- return;
91
- }
92
-
93
- const msg = JSON.parse(data);
94
- if (msg.type === 'INIT') {
95
- players = msg.players;
96
- for (let id in players) {
97
- players[id].targetX = players[id].x;
98
- players[id].targetY = players[id].y;
99
- }
100
- } else if (msg.type === 'S') { // Tick sync state
101
- for (let id in msg.data) {
102
- if (id === myId) continue;
103
- if (!players[id]) {
104
- players[id] = msg.data[id];
105
- }
106
- players[id].targetX = msg.data[id].x;
107
- players[id].targetY = msg.data[id].y;
108
- if (msg.data[id].color) players[id].color = msg.data[id].color;
109
- }
110
- } else if (msg.type === 'LEAVE') {
111
- if (players[msg.id]) delete players[msg.id];
112
- }
113
- };
114
-
115
- let lastSendTime = 0;
116
- function sendPosition(x, y) {
117
- const now = performance.now();
118
- // Client-side input throttling (25ms limits client spam)
119
- if (now - lastSendTime > 25 && ws.readyState === WebSocket.OPEN) {
120
- ws.send("M," + Math.round(x) + "," + Math.round(y));
121
- lastSendTime = now;
122
- }
123
- }
124
-
125
- canvas.addEventListener('touchmove', (e) => {
126
- e.preventDefault();
127
- if (!players[myId]) return;
128
- const touch = e.touches[0];
129
- players[myId].x = touch.clientX - 20;
130
- players[myId].y = touch.clientY - 20;
131
- sendPosition(players[myId].x, players[myId].y);
132
- }, { passive: false });
133
-
134
- canvas.addEventListener('mousemove', (e) => {
135
- if (!players[myId]) return;
136
- players[myId].x = e.clientX - 20;
137
- players[myId].y = e.clientY - 20;
138
- sendPosition(players[myId].x, players[myId].y);
139
- });
140
-
141
- function draw() {
142
- ctx.clearRect(0, 0, canvas.width, canvas.height);
143
- for (let id in players) {
144
- const p = players[id];
145
- if (id !== myId) {
146
- // Linear interpolation smooths out proxy packet jitter
147
- p.x += (p.targetX - p.x) * 0.15;
148
- p.y += (p.targetY - p.y) * 0.15;
149
- }
150
- ctx.fillStyle = p.color || '#ffffff';
151
- ctx.fillRect(p.x, p.y, 40, 40);
152
- ctx.fillStyle = '#fff';
153
- ctx.font = '12px sans-serif';
154
- ctx.fillText(id === myId ? "You" : "Player", p.x, p.y - 6);
155
- }
156
- requestAnimationFrame(draw);
157
- }
158
- draw();
159
- </script>
160
- </body>
161
- </html>
162
- `);
163
- });
164
-
165
- // --- WEBSOCKET SERVER ---
166
- const wss = new WebSocket.Server({ noServer: true });
167
- server.on('upgrade', (req, socket, head) => {
168
- wss.handleUpgrade(req, socket, head, (ws) => { wss.emit('connection', ws, req); });
169
- });
170
-
171
- wss.on('connection', (ws, req) => {
172
- const cookieHeader = req.headers.cookie || '';
173
- const playerId = cookieHeader.split('; ').find(row => row.startsWith('playerId='))?.split('=')[1];
174
- if (!playerId || !players[playerId]) { ws.close(); return; }
175
-
176
- ws.playerId = playerId;
177
- ws.send(JSON.stringify({ type: 'INIT', players }));
178
-
179
- ws.on('message', (message) => {
180
- const raw = message.toString();
181
-
182
- // Fast plain-text string evaluations to protect server CPU cycles
183
- if (raw === 'P') {
184
- ws.send('O'); // Minimal response payload
185
- return;
186
- }
187
-
188
- if (raw.startsWith('M,')) {
189
- const parts = raw.split(',');
190
- const p = players[ws.playerId];
191
- if (p) {
192
- p.x = parseFloat(parts[1]) || p.x;
193
- p.y = parseFloat(parts[2]) || p.y;
194
- }
195
- }
196
- });
197
-
198
- ws.on('close', () => {
199
- delete players[ws.playerId];
200
- broadcast(JSON.stringify({ type: 'LEAVE', id: ws.playerId }));
201
- });
202
- });
203
-
204
- // Discrete 45ms tick rate broadcast loop (Stops HF network congestion entirely)
205
- setInterval(() => {
206
- if (Object.keys(players).length === 0) return;
207
- const tickPayload = JSON.stringify({ type: 'S', data: players });
208
- broadcast(tickPayload);
209
- }, 45);
210
-
211
- function broadcast(packet) {
212
- wss.clients.forEach(client => {
213
- if (client.readyState === WebSocket.OPEN) {
214
- client.send(packet);
215
- }
216
- });
217
  }
 
218
 
219
- server.listen(PORT);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
220
  EOF
221
 
 
 
 
 
222
  EXPOSE 7860
223
- CMD ["node", "server.js"]
 
 
 
1
+ # syntax=docker/dockerfile:1.4
2
+ FROM ubuntu:22.04
3
+
4
+ ENV DEBIAN_FRONTEND=noninteractive
5
+
6
+ # Install all dependencies (Nginx, MariaDB, Redis, PHP 8.1, Supervisor)
7
+ RUN apt-get update && apt-get install -y \
8
+ curl wget unzip tar supervisor cron \
9
+ mariadb-server redis-server nginx \
10
+ php-cli php-fpm php-mysql php-mbstring \
11
+ php-bcmath php-xml php-curl php-zip php-gd \
12
+ && apt-get clean \
13
+ && rm -rf /var/lib/apt/lists/*
14
+
15
+ # Install Composer
16
+ RUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer
17
+
18
+ WORKDIR /var/www/pterodactyl
19
+
20
+ # Download and extract Pterodactyl Panel
21
+ RUN curl -Lo panel.tar.gz https://github.com/pterodactyl/panel/releases/latest/download/panel.tar.gz \
22
+ && tar -xzvf panel.tar.gz \
23
+ && chmod -R 755 storage/* bootstrap/cache/ \
24
+ && cp .env.example .env
25
+
26
+ # Start MariaDB temporarily to initialize the database and install the panel
27
+ RUN service mariadb start \
28
+ && sleep 5 \
29
+ && mysql -e "CREATE DATABASE panel;" \
30
+ && mysql -e "CREATE USER 'pterodactyl'@'127.0.0.1' IDENTIFIED BY 'password';" \
31
+ && mysql -e "GRANT ALL PRIVILEGES ON panel.* TO 'pterodactyl'@'127.0.0.1' WITH GRANT OPTION;" \
32
+ && mysql -e "FLUSH PRIVILEGES;" \
33
+ && composer install --no-dev --optimize-autoloader \
34
+ && php artisan key:generate --force \
35
+ && sed -i 's/DB_PASSWORD=/DB_PASSWORD=password/g' .env \
36
+ && sed -i 's/APP_URL=http:\/\/localhost/APP_URL=http:\/\/localhost:7860/g' .env \
37
+ && sed -i 's/CACHE_DRIVER=file/CACHE_DRIVER=redis/g' .env \
38
+ && sed -i 's/SESSION_DRIVER=file/SESSION_DRIVER=redis/g' .env \
39
+ && sed -i 's/QUEUE_CONNECTION=sync/QUEUE_CONNECTION=redis/g' .env \
40
+ && php artisan migrate --seed --force \
41
+ && php artisan p:user:make --email="admin@example.com" --username="admin" --name-first="Admin" --name-last="User" --password="admin" --admin=1 \
42
+ && chown -R www-data:www-data /var/www/pterodactyl/*
43
+
44
+ # Configure Nginx to listen on Hugging Face's default port (7860)
45
+ # Using "EOF" prevents Docker from interpreting Nginx variables like $uri
46
+ RUN rm /etc/nginx/sites-enabled/default
47
+ COPY <<"EOF" /etc/nginx/sites-enabled/pterodactyl.conf
48
+ server {
49
+ listen 7860;
50
+ server_name _;
51
+ root /var/www/pterodactyl/public;
52
+ index index.html index.htm index.php;
53
+ charset utf-8;
54
+
55
+ location / {
56
+ try_files $uri $uri/ /index.php?$query_string;
57
  }
58
 
59
+ location = /favicon.ico { access_log off; log_not_found off; }
60
+ location = /robots.txt { access_log off; log_not_found off; }
61
+
62
+ access_log off;
63
+ error_log /var/log/nginx/pterodactyl.app-error.log error;
64
+
65
+ client_max_body_size 100m;
66
+ client_body_timeout 120s;
67
+
68
+ sendfile off;
69
+
70
+ location ~ \.php$ {
71
+ fastcgi_split_path_info ^(.+\.php)(/.+)$;
72
+ fastcgi_pass unix:/run/php/php8.1-fpm.sock;
73
+ fastcgi_index index.php;
74
+ include fastcgi_params;
75
+ fastcgi_param PHP_VALUE "upload_max_filesize = 100M \n post_max_size=100M";
76
+ fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
77
+ fastcgi_param HTTP_PROXY "";
78
+ fastcgi_intercept_errors off;
79
+ fastcgi_buffer_size 16k;
80
+ fastcgi_buffers 4 16k;
81
+ fastcgi_connect_timeout 300;
82
+ fastcgi_send_timeout 300;
83
+ fastcgi_read_timeout 300;
84
  }
85
 
86
+ location ~ /\.ht {
87
+ deny all;
88
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
89
  }
90
+ EOF
91
 
92
+ # Configure Supervisord to run all required services in a single container
93
+ COPY <<"EOF" /etc/supervisor/conf.d/supervisord.conf
94
+ [supervisord]
95
+ nodaemon=true
96
+ user=root
97
+
98
+ [program:mysql]
99
+ command=/usr/bin/mysqld_safe
100
+ autostart=true
101
+ autorestart=true
102
+
103
+ [program:redis]
104
+ command=/usr/bin/redis-server
105
+ autostart=true
106
+ autorestart=true
107
+
108
+ [program:php-fpm]
109
+ command=/usr/sbin/php-fpm8.1 -F
110
+ autostart=true
111
+ autorestart=true
112
+
113
+ [program:nginx]
114
+ command=/usr/sbin/nginx -g "daemon off;"
115
+ autostart=true
116
+ autorestart=true
117
+
118
+ [program:pteroq]
119
+ command=php /var/www/pterodactyl/artisan queue:work --queue=high,standard,low --sleep=3 --tries=3
120
+ autostart=true
121
+ autorestart=true
122
+ user=www-data
123
  EOF
124
 
125
+ # Fix PHP-FPM socket directory permissions
126
+ RUN mkdir -p /run/php && chown -R www-data:www-data /run/php
127
+
128
+ # Expose the single port required by Hugging Face Spaces
129
  EXPOSE 7860
130
+
131
+ # Start Supervisor which handles Nginx, MariaDB, PHP-FPM, Redis, and Pterodactyl Queue
132
+ CMD ["/usr/bin/supervisord", "-c", "/etc/supervisor/conf.d/supervisord.conf"]