Generalize game ID normalization for all _game suffixes
Previously the system only handled the specific case of minetest_game -> minetest. Based on Luanti developer feedback that "_game is removed from IDs as part of normalisation for all game IDs not just MTG", this change generalizes the pattern. ## Changes Made ### Enhanced Game ID Normalization - `mapToActualGameId()`: Now automatically removes "_game" suffix from any game ID - `mapGameIdForWorldCreation()`: Generalizes suffix removal for world.mt files - `mapInternalGameIdToDirectory()`: Enhanced to dynamically check filesystem for directories ### Backwards Compatibility - All existing minetest_game -> minetest mappings continue to work - Now also handles any other games with _game suffix (e.g., survival_game -> survival) - Original EJS templates already compatible via world.gameTitle || world.gameid pattern ### Technical Implementation - Replaces hardcoded mapping tables with general suffix detection - Maintains proper fallback behavior for games without _game suffix - Filesystem-aware directory resolution for reverse mapping 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -182,33 +182,72 @@ class LuantiPaths {
|
|||||||
|
|
||||||
mapToActualGameId(directoryName) {
|
mapToActualGameId(directoryName) {
|
||||||
// Map directory names to the actual game IDs that Luanti recognizes
|
// Map directory names to the actual game IDs that Luanti recognizes
|
||||||
// For most cases, the directory name IS the game ID
|
// Luanti normalizes game IDs by removing "_game" suffix from all games
|
||||||
const gameIdMap = {
|
// See https://github.com/luanti-org/luanti/blob/c9d4c33174c87ede1f49c5fe5e8e49a784798eb6/src/content/subgames.cpp#L21
|
||||||
// Luanti internal alias mapping - see https://github.com/luanti-org/luanti/blob/c9d4c33174c87ede1f49c5fe5e8e49a784798eb6/src/content/subgames.cpp#L21
|
|
||||||
'minetest_game': 'minetest',
|
|
||||||
};
|
|
||||||
|
|
||||||
return gameIdMap[directoryName] || directoryName;
|
// Remove "_game" suffix if present
|
||||||
|
if (directoryName.endsWith('_game')) {
|
||||||
|
return directoryName.slice(0, -5); // Remove last 5 characters ("_game")
|
||||||
|
}
|
||||||
|
|
||||||
|
return directoryName;
|
||||||
}
|
}
|
||||||
|
|
||||||
mapGameIdForWorldCreation(gameId) {
|
mapGameIdForWorldCreation(gameId) {
|
||||||
// When creating worlds, map game IDs that Luanti expects to use different IDs internally
|
// When creating worlds, map game IDs that Luanti expects to use different IDs internally
|
||||||
// This is the reverse of directory-based detection - we're setting what goes in world.mt
|
// This is the reverse of directory-based detection - we're setting what goes in world.mt
|
||||||
const worldGameIdMap = {
|
// Luanti normalizes all game IDs by removing "_game" suffix
|
||||||
'minetest_game': 'minetest', // Luanti expects 'minetest' in world.mt even for minetest_game
|
|
||||||
};
|
|
||||||
|
|
||||||
return worldGameIdMap[gameId] || gameId;
|
// Remove "_game" suffix if present
|
||||||
|
if (gameId.endsWith('_game')) {
|
||||||
|
return gameId.slice(0, -5); // Remove last 5 characters ("_game")
|
||||||
|
}
|
||||||
|
|
||||||
|
return gameId;
|
||||||
}
|
}
|
||||||
|
|
||||||
mapInternalGameIdToDirectory(internalGameId) {
|
async mapInternalGameIdToDirectory(internalGameId) {
|
||||||
// Reverse mapping: convert internal game ID back to directory name for display/reference
|
// Reverse mapping: convert internal game ID back to directory name for display/reference
|
||||||
// This helps when we read a world.mt with "minetest" but want to show "Minetest Game"
|
// This helps when we read a world.mt with normalized game ID but want to show directory name
|
||||||
const reverseGameIdMap = {
|
// Since Luanti normalizes by removing "_game" suffix, we need to check if a directory
|
||||||
'minetest': 'minetest_game', // world.mt has 'minetest' but directory is 'minetest_game'
|
// with "_game" suffix exists for this normalized ID
|
||||||
};
|
|
||||||
|
|
||||||
return reverseGameIdMap[internalGameId] || internalGameId;
|
const fs = require('fs').promises;
|
||||||
|
const path = require('path');
|
||||||
|
|
||||||
|
// Check both system and user games directories
|
||||||
|
const gameDirs = [this.getGamesPath()];
|
||||||
|
if (this.getSystemGamesPath() && this.getSystemGamesPath() !== this.getGamesPath()) {
|
||||||
|
gameDirs.push(this.getSystemGamesPath());
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const gameDir of gameDirs) {
|
||||||
|
try {
|
||||||
|
// First check if the internalGameId as-is exists as a directory
|
||||||
|
const directPath = path.join(gameDir, internalGameId);
|
||||||
|
const stat = await fs.stat(directPath);
|
||||||
|
if (stat.isDirectory()) {
|
||||||
|
return internalGameId;
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
// Directory doesn't exist, try with "_game" suffix
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Check if there's a directory with "_game" suffix
|
||||||
|
const gameIdWithSuffix = internalGameId + '_game';
|
||||||
|
const suffixPath = path.join(gameDir, gameIdWithSuffix);
|
||||||
|
const stat = await fs.stat(suffixPath);
|
||||||
|
if (stat.isDirectory()) {
|
||||||
|
return gameIdWithSuffix;
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
// Directory doesn't exist either
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fallback: return the original ID
|
||||||
|
return internalGameId;
|
||||||
}
|
}
|
||||||
|
|
||||||
async getInstalledGames() {
|
async getInstalledGames() {
|
||||||
|
Reference in New Issue
Block a user