diff --git a/CHANGELOG.md b/CHANGELOG.md index 77dad58..27a60bb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## 2.2.1 (2026-10-06) + +### Fixed +- Texture packs on Pterodactyl and other container hosts: an internal allocation IP (or the container's own + address) could be sent as the download address, and players got "Error 10061" (connection refused). Only a + public allocation IP is used now; otherwise the console asks for `publicAddress`, and it prints the URL + players download from. + ## 2.2.0 (2026-10-05) ### Added diff --git a/README.md b/README.md index 15366e1..7160e93 100644 --- a/README.md +++ b/README.md @@ -140,9 +140,9 @@ MCScript runs on Pterodactyl panels (and others like it) with a Node.js egg, suc 4. Edit `config/server.json` from **Files** to set the name, MOTD and `owners`, and restart from the panel. `/restart` exits and lets the panel start the server again. The panel restarts crashed servers by default; if -yours doesn't, use the panel's Restart button. Texture packs in `texpacks/` use the allocation's address -automatically; if the panel shows the allocation as `0.0.0.0`, set `publicAddress` in `config/server.json` to -the address players connect to (for example `play.example.com:25565`). +yours doesn't, use the panel's Restart button. For texture packs in `texpacks/`, set `publicAddress` in +`config/server.json` to the address shown in the panel (for example `play.example.com:25565`). Without it the +allocation's IP is used, but only when it is a public one. The console prints the URL players download from. ### Backups diff --git a/docs/CONFIGURATION.md b/docs/CONFIGURATION.md index 7fea0c3..fb37645 100644 --- a/docs/CONFIGURATION.md +++ b/docs/CONFIGURATION.md @@ -28,7 +28,7 @@ write its own copy over your changes on shutdown. | `welcomeMessage` | | Sent to players when they join. `{player}` is replaced with their name | | `rules` | three lines | Shown by `/rules` | | `defaultTexture` | `""` | Texture pack URL for levels that don't set their own | -| `publicAddress` | `""` | Your server's domain or IP (and `:port` if needed), used to serve packs from `texpacks/`. On Pterodactyl it is detected | +| `publicAddress` | `""` | Your server's domain or IP (and `:port` if needed), used to serve packs from `texpacks/`. Set it on hosts and panels. Without it, Pterodactyl's allocation IP is used if it is public, and players on the same machine or network get the address they connected to | | `autosaveMinutes` | `5` | `0` turns autosave off | | `backupMinutes` | `30` | How often changed levels are backed up. `0` turns backups off | | `backupsToKeep` | `10` | Backups kept per level | diff --git a/lib/server.js b/lib/server.js index 7cd4c7e..d083f38 100644 --- a/lib/server.js +++ b/lib/server.js @@ -221,17 +221,30 @@ class MCScriptServer { // URL players' clients use to download a pack from texpacks/ (needs publicAddress in the config) texturePackUrl (fileName, player = null) { let address = String(this.config.publicAddress || '').replace(/^https?:\/\//, '').replace(/\/+$/, '') - // Pterodactyl tells us the address of the allocation + // Pterodactyl tells us the address of the allocation, but it can be an internal one const panelIp = process.env.SERVER_IP - if (!address && panelIp && panelIp !== '0.0.0.0') address = `${panelIp}:${this.port}` - // a player on this machine or the same network can use the address they connected to - if (!address && player && player.conn.localAddress && isPrivateIp(player.conn.socketIp)) { + if (!address && panelIp && panelIp !== '0.0.0.0' && !isPrivateIp(panelIp)) address = `${panelIp}:${this.port}` + // a player on this machine or the same network can use the address they connected to. Not inside a panel's + // container: there every player comes from the container network, and that address is useless to them + const inPanel = Boolean(process.env.P_SERVER_UUID) + if (!address && !inPanel && player && player.conn.localAddress && isPrivateIp(player.conn.socketIp)) { const ip = player.conn.localAddress address = `${ip.includes(':') ? `[${ip}]` : ip}:${this.port}` } - if (!address) return null + if (!address) { + if (!this._warnedTextureAddress) { + this._warnedTextureAddress = true + this.log.warn(`Texture pack ${fileName} can't be sent: set "publicAddress" in config/server.json to the address players join with (for example play.example.com:${this.port}).`) + } + return null + } const host = /:\d+$/.test(address) ? address : `${address}:${this.port}` - return `http://${host}/texpacks/${fileName}` + const url = `http://${host}/texpacks/${fileName}` + if (this._loggedTextureUrl !== url) { + this._loggedTextureUrl = url + this.log.info(`Texture pack URL: ${url}`) + } + return url } _allocateId () { diff --git a/package-lock.json b/package-lock.json index b351a20..4e52749 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "mcscript", - "version": "2.2.0", + "version": "2.2.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "mcscript", - "version": "2.2.0", + "version": "2.2.1", "license": "MIT", "devDependencies": { "standard": "^17.1.2" diff --git a/package.json b/package.json index 1b384b9..168f50f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "mcscript", - "version": "2.2.0", + "version": "2.2.1", "description": "A ClassiCube (Minecraft Classic + CPE) server written in JavaScript, with plugins", "main": "lib/index.js", "author": "perronosaurio", diff --git a/test/server.test.js b/test/server.test.js index 3ea0a93..2179dc3 100644 --- a/test/server.test.js +++ b/test/server.test.js @@ -1036,4 +1036,15 @@ test('texture packs in texpacks/ are served on the game port', async (t) => { server.config.publicAddress = '' const player = server.findPlayer('Boss') assert.equal(server.texturePackUrl('demo.zip', player), `http://127.0.0.1:${server.port}/texpacks/demo.zip`) + + // inside a panel's container that address is the container's, and an internal SERVER_IP is no better + const saved = { uuid: process.env.P_SERVER_UUID, ip: process.env.SERVER_IP } + t.after(() => { + for (const [k, v] of [['P_SERVER_UUID', saved.uuid], ['SERVER_IP', saved.ip]]) { if (v === undefined) delete process.env[k]; else process.env[k] = v } + }) + process.env.P_SERVER_UUID = 'test' + process.env.SERVER_IP = '172.18.0.5' + assert.equal(server.texturePackUrl('demo.zip', player), null) + process.env.SERVER_IP = '203.0.113.5' + assert.equal(server.texturePackUrl('demo.zip', player), `http://203.0.113.5:${server.port}/texpacks/demo.zip`) })