134 lines
4.5 KiB
JavaScript
134 lines
4.5 KiB
JavaScript
'use strict'
|
|
|
|
/**
|
|
* Soft-reconnect UX helpers for mesh border migration (ADR-0008).
|
|
* Java Edition cannot silently rebind TCP; we minimize friction:
|
|
* - Prefer the same localhost port when retargeting the guest tunnel
|
|
* - Print a clear Direct Connect banner
|
|
* - Kick reason includes the next 127.0.0.1:port when known
|
|
*/
|
|
|
|
/**
|
|
* @param {object} opts
|
|
* @param {string} [opts.host='127.0.0.1']
|
|
* @param {number} opts.port
|
|
* @param {string} [opts.regionName]
|
|
* @param {string} [opts.regionId]
|
|
* @param {{x?:number,y?:number,z?:number}|null} [opts.mapped]
|
|
* @param {boolean} [opts.samePort]
|
|
*/
|
|
function formatReconnectBanner(opts = {}) {
|
|
const host = opts.host || '127.0.0.1'
|
|
const port = Number(opts.port)
|
|
const region = opts.regionName || opts.regionId || 'neighbor region'
|
|
const lines = [
|
|
'',
|
|
'══════════════════════════════════════════════════════',
|
|
' Flying Jib — mesh region switch',
|
|
` Destination: ${region}`,
|
|
` Java Edition → Direct Connect: ${host}:${port}`,
|
|
'══════════════════════════════════════════════════════'
|
|
]
|
|
if (opts.samePort) {
|
|
lines.push(' (Same local port as before — re-add/reconnect that entry.)')
|
|
} else {
|
|
lines.push(' (Port changed — update Direct Connect or Multiplayer entry.)')
|
|
}
|
|
if (opts.mapped && (opts.mapped.x != null || opts.mapped.z != null)) {
|
|
lines.push(
|
|
` Spawn hint: ~ ${Math.floor(opts.mapped.x || 0)}, ${Math.floor(opts.mapped.y || 64)}, ${Math.floor(opts.mapped.z || 0)}`
|
|
)
|
|
}
|
|
lines.push(
|
|
' Inventory handoff applies after you join the new server.',
|
|
' Press multiplayer → connect (or create Direct Connect).',
|
|
'══════════════════════════════════════════════════════',
|
|
''
|
|
)
|
|
return lines.join('\n')
|
|
}
|
|
|
|
/**
|
|
* Kick / disconnect reason text (Minecraft shows this in the disconnect screen).
|
|
* Keep relatively short (chat component limits vary by version).
|
|
* @param {object} opts
|
|
* @param {string} [opts.regionName]
|
|
* @param {string} [opts.regionId]
|
|
* @param {number} [opts.port]
|
|
* @param {string} [opts.host='127.0.0.1']
|
|
*/
|
|
function formatKickReason(opts = {}) {
|
|
const region = opts.regionName || opts.regionId || 'neighbor'
|
|
const host = opts.host || '127.0.0.1'
|
|
if (opts.port != null && Number.isFinite(Number(opts.port))) {
|
|
return `Mesh → ${region}. Reconnect to ${host}:${opts.port}`
|
|
}
|
|
return `Mesh border → ${region}. Check Flying Jib CLI for reconnect address.`
|
|
}
|
|
|
|
/**
|
|
* Prefer keeping the guest local port stable across tunnel retargets.
|
|
* @param {object} opts
|
|
* @param {number|null} [opts.previousLocalPort]
|
|
* @param {number|null} [opts.squidPort] host Squid port if still running locally
|
|
* @param {number} [opts.fallback=25565]
|
|
* @returns {{ localPort: number, samePort: boolean, reason: string }}
|
|
*/
|
|
function preferStableLocalPort(opts = {}) {
|
|
const fallback = opts.fallback != null ? Number(opts.fallback) : 25565
|
|
const prev = opts.previousLocalPort != null ? Number(opts.previousLocalPort) : null
|
|
const squid = opts.squidPort != null ? Number(opts.squidPort) : null
|
|
|
|
if (prev && (!squid || prev !== squid)) {
|
|
return {
|
|
localPort: prev,
|
|
samePort: true,
|
|
reason: 'reuse-previous-guest-port'
|
|
}
|
|
}
|
|
|
|
// Host machine also running Squid: guest tunnel must not steal Squid's port
|
|
if (squid) {
|
|
const alt = squid === 25565 ? 25566 : squid + 1
|
|
return {
|
|
localPort: prev && prev !== squid ? prev : alt,
|
|
samePort: !!(prev && prev !== squid),
|
|
reason: 'avoid-local-squid-port'
|
|
}
|
|
}
|
|
|
|
if (prev) {
|
|
return { localPort: prev, samePort: true, reason: 'reuse-previous' }
|
|
}
|
|
|
|
return { localPort: fallback, samePort: false, reason: 'fallback' }
|
|
}
|
|
|
|
/**
|
|
* Structured reconnect hint for CLI / future GUI.
|
|
* @param {object} opts
|
|
*/
|
|
function buildReconnectHint(opts = {}) {
|
|
const host = opts.host || '127.0.0.1'
|
|
const port = Number(opts.port)
|
|
return {
|
|
type: 'reconnect-hint',
|
|
host,
|
|
port,
|
|
address: `${host}:${port}`,
|
|
regionId: opts.regionId || null,
|
|
regionName: opts.regionName || null,
|
|
mapped: opts.mapped || null,
|
|
samePort: !!opts.samePort,
|
|
banner: formatReconnectBanner(opts),
|
|
kickReason: formatKickReason(opts)
|
|
}
|
|
}
|
|
|
|
module.exports = {
|
|
formatReconnectBanner,
|
|
formatKickReason,
|
|
preferStableLocalPort,
|
|
buildReconnectHint
|
|
}
|