fix: servers misc fixes (#5475)

* fix: tags in project settings to have icons and ordered correctly

* fix copy in project list layout settings

* fix tag item in header navigation

* adjust ping ranges

* add handle click tag

* fix: dont show offline in project page for draft status

* move tags above creators in app

* preload server project page on load and optimize queries

* add server project card to organization page

* fix minecraft_java_server label

* pnpm prepr

* have user option in project create modal be circle

* feat: implement better mobile project page view

* disable summary line clamp for servers

* fix: unlink instance doesnt update instance

* increase icon upload size

* small fix on button size

* improve how server ping info loads

* remove unnecessary pings for instance page

* fix order of computing dependency diff

* remove linked_project_id from world, use name+address to match for managed world instead

* pnpm prepr

* hide duplicate worlds with same domain name in worlds list

* add install content warning for server instance

* increase summary max width

* add handling for server projects for bulk editing links

* implement include user unlisted projects in published modpack select

* pnpm prepr

* filter to only user unlisted status

* add bad link warnings

* fix modpack tags appearing in server

* cargo fmt
This commit is contained in:
Truman Gao
2026-03-06 18:11:45 -08:00
committed by GitHub
parent 98175a58a6
commit 83d53dafe7
44 changed files with 993 additions and 377 deletions

View File

@@ -30,7 +30,6 @@ export type ServerWorld = BaseWorld & {
index: number
address: string
pack_status: ServerPackStatus
linked_project_id?: string
}
export type World = SingleplayerWorld | ServerWorld
@@ -141,14 +140,12 @@ export async function add_server_to_profile(
name: string,
address: string,
packStatus: ServerPackStatus,
linkedProjectId?: string,
): Promise<number> {
return await invoke('plugin:worlds|add_server_to_profile', {
path,
name,
address,
packStatus,
linkedProjectId,
})
}
@@ -158,7 +155,6 @@ export async function edit_server_in_profile(
name: string,
address: string,
packStatus: ServerPackStatus,
linkedProjectId?: string,
): Promise<void> {
return await invoke('plugin:worlds|edit_server_in_profile', {
path,
@@ -166,7 +162,6 @@ export async function edit_server_in_profile(
name,
address,
packStatus,
linkedProjectId,
})
}
@@ -204,11 +199,6 @@ export function getWorldIdentifier(world: World) {
export function sortWorlds(worlds: World[]) {
worlds.sort((a, b) => {
const aLinked = isLinkedWorld(a)
const bLinked = isLinkedWorld(b)
if (aLinked !== bLinked) {
return aLinked ? -1 : 1
}
if (!a.last_played) {
return 1
}
@@ -227,8 +217,129 @@ export function isServerWorld(world: World): world is ServerWorld {
return world.type === 'server'
}
export function isLinkedWorld(world: World): boolean {
return world.type === 'server' && !!world.linked_project_id
const DEFAULT_MINECRAFT_SERVER_PORT = 25565
function parseServerPort(port: string): number | null {
const parsed = Number.parseInt(port, 10)
return Number.isInteger(parsed) && parsed > 0 && parsed <= 65535 ? parsed : null
}
function parseServerHost(address: string): string {
const trimmedAddress = address.trim()
if (!trimmedAddress) return ''
if (trimmedAddress.startsWith('[')) {
const closingBracket = trimmedAddress.indexOf(']')
if (closingBracket > 0) {
return trimmedAddress.slice(1, closingBracket).trim().toLowerCase()
}
}
const firstColon = trimmedAddress.indexOf(':')
const lastColon = trimmedAddress.lastIndexOf(':')
if (firstColon !== -1 && firstColon === lastColon) {
return trimmedAddress.slice(0, firstColon).trim().toLowerCase()
}
return trimmedAddress.toLowerCase()
}
function isIPv4Host(host: string): boolean {
const segments = host.split('.')
if (segments.length !== 4) return false
return segments.every((segment) => {
if (!/^\d+$/.test(segment)) return false
const value = Number.parseInt(segment, 10)
return value >= 0 && value <= 255
})
}
/**
* Normalization converts addresses to a canonical form (lowercase-host:port, default port 25565)
*/
export function normalizeServerAddress(address: string): string {
const trimmedAddress = address.trim()
const host = parseServerHost(trimmedAddress)
if (!host) return ''
let port = DEFAULT_MINECRAFT_SERVER_PORT
// ipv6 address
if (trimmedAddress.startsWith('[')) {
const closingBracket = trimmedAddress.indexOf(']')
if (closingBracket > 0) {
const suffix = trimmedAddress.slice(closingBracket + 1)
if (suffix.startsWith(':')) {
const parsedPort = parseServerPort(suffix.slice(1))
if (parsedPort != null) {
port = parsedPort
}
}
}
// ipv4 address or hostname
} else {
const firstColon = trimmedAddress.indexOf(':')
const lastColon = trimmedAddress.lastIndexOf(':')
if (firstColon !== -1 && firstColon === lastColon) {
const parsedPort = parseServerPort(trimmedAddress.slice(firstColon + 1))
if (parsedPort != null) {
port = parsedPort
}
}
}
return `${host}:${port}`
}
/**
* Domain key used for deduping server entries by removing a single leading subdomain.
* Example: test.cobblemon.gg and cobblemon.gg map to cobblemon.gg
*/
export function getServerDomainKey(address: string): string {
const normalizedAddress = normalizeServerAddress(address)
if (!normalizedAddress) return ''
const separator = normalizedAddress.lastIndexOf(':')
if (separator <= 0 || separator === normalizedAddress.length - 1) return normalizedAddress
const host = normalizedAddress.slice(0, separator).replace(/\.+$/, '')
if (!host) return normalizedAddress
if (host.includes(':') || isIPv4Host(host)) return normalizedAddress
const segments = host.split('.').filter(Boolean)
if (segments.length <= 2) return host
return segments.slice(1).join('.')
}
export function resolveManagedServerWorld(
worlds: World[],
managedName: string | null | undefined,
managedAddress: string | null | undefined,
): ServerWorld | null {
if (!managedName || !managedAddress) return null
const normalizedManagedAddress = normalizeServerAddress(managedAddress)
if (!normalizedManagedAddress) return null
const servers = worlds
.filter(isServerWorld)
.slice()
.sort((a, b) => a.index - b.index)
const exactMatch = servers.find(
(server) =>
server.name === managedName &&
normalizeServerAddress(server.address) === normalizedManagedAddress,
)
if (exactMatch) return exactMatch
return (
servers.find((server) => normalizeServerAddress(server.address) === normalizedManagedAddress) ??
null
)
}
export async function getServerLatency(