import path from "path"; import { resolveContentPath } from "./contentTransforms.js"; export function sanitizeWorldId(worldId) { const raw = String(worldId || "").trim(); if (!raw) { return "world"; } return raw.replace(/[^a-zA-Z0-9_-]/g, "_"); } export function defaultWorldDirRel(worldId) { return `worlds/${sanitizeWorldId(worldId)}`; } export function buildWorldChunkFileName(chunkX, chunkY) { return `${Math.floor(Number(chunkX) || 0)}_${Math.floor(Number(chunkY) || 0)}.json`; } export function getWorldStoragePaths(contentRoot, worldEntryOrId) { const worldId = typeof worldEntryOrId === "string" ? String(worldEntryOrId || "").trim() : String(worldEntryOrId?.id || "").trim(); const worldDirRel = typeof worldEntryOrId === "string" ? defaultWorldDirRel(worldId) : String(worldEntryOrId?.worldDir || defaultWorldDirRel(worldId)); const worldDirAbs = resolveContentPath(contentRoot, worldDirRel); const chunksDirRel = `${worldDirRel}/chunks`; return { worldId, worldDirRel, worldDirAbs, worldJsonRel: `${worldDirRel}/world.json`, worldJsonAbs: path.join(worldDirAbs, "world.json"), bookmarksRel: `${worldDirRel}/bookmarks.json`, bookmarksAbs: path.join(worldDirAbs, "bookmarks.json"), chunksDirRel, chunksDirAbs: path.join(worldDirAbs, "chunks"), }; } export function normalizeWorldIndexEntry(entry) { const id = sanitizeWorldId(entry?.id || ""); return { id, name: String(entry?.name || id || "World"), worldDir: String(entry?.worldDir || defaultWorldDirRel(id)), }; } export function normalizeWorldIndexPayload(payload) { const worlds = Array.isArray(payload?.worlds) ? payload.worlds .filter((entry) => entry && typeof entry === "object" && !Array.isArray(entry)) .map((entry) => normalizeWorldIndexEntry(entry)) : []; return { schemaVersion: typeof payload?.schemaVersion === "number" ? payload.schemaVersion : 1, worlds, }; } export function normalizeWorldBookmark(entry, index = 0) { const fallbackId = `bookmark_${index + 1}`; return { id: String(entry?.id || fallbackId).trim() || fallbackId, label: String(entry?.label || entry?.id || fallbackId).trim() || fallbackId, x: Math.floor(Number(entry?.x) || 0), y: Math.floor(Number(entry?.y) || 0), }; }