Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -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
Expand Down
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
2 changes: 1 addition & 1 deletion docs/CONFIGURATION.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 |
Expand Down
25 changes: 19 additions & 6 deletions lib/server.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 () {
Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -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",
Expand Down
11 changes: 11 additions & 0 deletions test/server.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -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`)
})
Loading