{
  "name": "serverWipe",
  "author": "Mad",
  "supportedGames": [
    "all"
  ],
  "takaroVersion": "main",
  "versions": [
    {
      "tag": "latest",
      "description": "The ServerWipe module provides administrators with a powerful tool to reset server data. It allows selective wiping of player currency, teleport locations, and waypoints with a single command.",
      "configSchema": "{\"$schema\":\"http://json-schema.org/draft-07/schema#\",\"type\":\"object\",\"required\":[],\"additionalProperties\":false,\"properties\":{\"wipeCurrency\":{\"title\":\"wipeCurrency\",\"description\":\"Whether to reset all player currency during wipe\",\"default\":false,\"type\":\"boolean\"},\"wipeTeleports\":{\"title\":\"wipeTeleports\",\"description\":\"Whether to delete all teleport locations during wipe.\",\"default\":false,\"type\":\"boolean\"},\"wipeStarterKitClaims\":{\"title\":\"wipeStarterKitClaims\",\"description\":\"wipeStarterKitClaims\",\"default\":false,\"type\":\"boolean\"},\"globalAnnouncement\":{\"title\":\"globalAnnouncement\",\"description\":\"Message to broadcast to all players after the wipe\",\"default\":\"\",\"type\":\"string\"}}}",
      "uiSchema": "{}",
      "commands": [
        {
          "name": "wipecontinue",
          "trigger": "wipecontinue",
          "helpText": "No help text available",
          "function": "// commands/wipe_continue_batch.js\nimport { takaro, data, checkPermission, TakaroUserError } from '@takaro/helpers';\n\n// --- Configuration ---\nconst PAGE_LIMIT = 100; // Items per fetch\nconst BATCH_SIZE = 100; // Items per concurrent processing batch (Adjust lower if timeouts persist)\nconst PAGES_PER_RUN = 4; // Number of pages to process per command execution (4 * 100 = 400 items)\n\n// --- State Variable Keys ---\nconst STATE_KEYS = {\n    currencyPage: 'wipe_cont_currency_page',\n    teleportsPage: 'wipe_cont_teleports_page',\n    starterKitsPage: 'wipe_cont_starterkits_page',\n    teleportsModuleId: 'wipe_cont_teleports_module_id',\n    waypointsModuleId: 'wipe_cont_waypoints_module_id'\n};\n\n// --- State Management Helpers ---\nasync function getStateVariable(key, gameServerId, moduleId) {\n    const existingVar = await takaro.variable.variableControllerSearch({\n        filters: { key: [key], gameServerId: [gameServerId], moduleId: [moduleId] },\n        limit: 1\n    });\n    if (existingVar.data.data.length > 0) {\n        try { return JSON.parse(existingVar.data.data[0].value) ?? 0; }\n        catch (e) { console.error(`State Error (Get: ${key}): ${e}`); return 0; }\n    }\n    return 0;\n}\n\nasync function setStateVariable(key, value, gameServerId, moduleId) {\n    const existingVar = await takaro.variable.variableControllerSearch({\n        filters: { key: [key], gameServerId: [gameServerId], moduleId: [moduleId] },\n        limit: 1\n    });\n    const valueString = JSON.stringify(value);\n    if (existingVar.data.data.length > 0) {\n        if (existingVar.data.data[0].value !== valueString) {\n            console.log(`State Set: ${key} = ${valueString}`);\n            await takaro.variable.variableControllerUpdate(existingVar.data.data[0].id, { value: valueString });\n        }\n    } else {\n        console.log(`State Create: ${key} = ${valueString}`);\n        await takaro.variable.variableControllerCreate({ key, value: valueString, gameServerId, moduleId });\n    }\n}\n\n// --- Main Function ---\nasync function main() {\n    const { player, module: mod, gameServerId, arguments: args, pog } = data;\n    const wipeType = args.type?.toLowerCase();\n\n    // --- Initial Checks ---\n    if (!checkPermission(pog, 'SERVER_WIPE')) throw new TakaroUserError('Permission denied.');\n    if (!wipeType || !['currency', 'teleports', 'starterkits'].includes(wipeType)) throw new TakaroUserError('Specify type: currency, teleports, or starterkits.');\n\n    await player.pm(`Continuing wipe (~${PAGES_PER_RUN * PAGE_LIMIT} items) for: ${wipeType}...`);\n    console.log(`Wipe Continue: Starting run for ${wipeType}`);\n\n    // --- Determine State Key & Function ---\n    let stateKeyPage = '';\n    let processingFunction = null;\n    let functionArgs = []; // Arguments for the processing function, page added dynamically\n\n    switch (wipeType) {\n        case 'currency':\n            stateKeyPage = STATE_KEYS.currencyPage;\n            // Define inline processing function for currency\n            processingFunction = async (page, gameServerId) => {\n                let itemsProcessed = 0, errorCount = 0, hasMore = false;\n                const pogsResponse = await takaro.playerOnGameserver.playerOnGameServerControllerSearch({\n                    filters: { gameServerId: [gameServerId] }, limit: PAGE_LIMIT, page: page\n                });\n                const pogs = pogsResponse.data.data;\n                const countOnPage = pogs.length;\n                if (!pogs || countOnPage === 0) return { itemsProcessed: 0, errorCount: 0, hasMore: false };\n                hasMore = countOnPage === PAGE_LIMIT;\n                for (let i = 0; i < countOnPage; i += BATCH_SIZE) {\n                    const batch = pogs.slice(i, i + BATCH_SIZE);\n                    // Set to 0.01 instead of 0 to satisfy API validation\n                    const results = await Promise.allSettled(batch.map(p =>\n                        takaro.playerOnGameserver.playerOnGameServerControllerSetCurrency(gameServerId, p.playerId, { currency: 0.01 })\n                    ));\n                    results.forEach(r => {\n                        if (r.status === 'fulfilled') {\n                            itemsProcessed++;\n                        } else {\n                            errorCount++;\n                            console.error(`Currency Error: ${r.reason?.message || r.reason}`);\n                        }\n                    });\n                }\n                return { itemsProcessed, errorCount, hasMore };\n            };\n            functionArgs = [gameServerId]; // Arguments excluding page\n            break;\n        case 'teleports':\n        case 'starterkits': // Combine logic as they both use processVariablePage\n            stateKeyPage = (wipeType === 'teleports') ? STATE_KEYS.teleportsPage : STATE_KEYS.starterKitsPage;\n            let identifier, typeName;\n            if (wipeType === 'teleports') {\n                identifier = await getStateVariable(STATE_KEYS.teleportsModuleId, gameServerId, mod.moduleId);\n                typeName = 'Teleport';\n                if (!identifier) throw new TakaroUserError(`Cannot continue ${wipeType}: Module ID not found in state.`);\n                // TODO: Handle Waypoints logic here if needed\n            } else { // starterkits\n                identifier = 'var_key_t_starterkit_lock';\n                typeName = 'Starter Kit';\n            }\n            // Define inline processing function for variables\n            processingFunction = async (page, gameServerId, moduleId, varKeyOrModId, typeNameArg) => {\n                let itemsProcessed = 0, errorCount = 0, hasMore = false;\n                const filters = { gameServerId: [gameServerId] };\n                if (varKeyOrModId.startsWith('var_key_')) {\n                    filters.key = [varKeyOrModId.substring(8)];\n                    filters.moduleId = [moduleId];\n                } else {\n                    filters.moduleId = [varKeyOrModId];\n                }\n                const varsResponse = await takaro.variable.variableControllerSearch({ filters, limit: PAGE_LIMIT, page });\n                const variables = varsResponse.data.data;\n                const countOnPage = variables.length;\n                if (!variables || countOnPage === 0) return { itemsProcessed: 0, errorCount: 0, hasMore: false };\n                hasMore = countOnPage === PAGE_LIMIT;\n                for (let i = 0; i < countOnPage; i += BATCH_SIZE) {\n                    const batch = variables.slice(i, i + BATCH_SIZE);\n                    const results = await Promise.allSettled(batch.map(v =>\n                        takaro.variable.variableControllerDelete(v.id)\n                    ));\n                    results.forEach(r => {\n                        if (r.status === 'fulfilled') {\n                            itemsProcessed++;\n                        } else {\n                            errorCount++;\n                            console.error(`${typeNameArg} Delete Error: ${r.reason?.message || r.reason}`);\n                        }\n                    });\n                }\n                return { itemsProcessed, errorCount, hasMore };\n            };\n            functionArgs = [gameServerId, mod.moduleId, identifier, typeName]; // Arguments excluding page\n            break;\n        default:\n            throw new TakaroUserError(`Internal error: Unhandled wipe type \"${wipeType}\"`);\n    }\n\n    // --- Process Continuation Batches ---\n    let itemsProcessedThisRun = 0;\n    let errorsThisRun = 0;\n    let continueProcessing = true;\n    let pagesProcessedThisRun = 0;\n\n    try {\n        for (let i = 0; i < PAGES_PER_RUN; i++) {\n            const currentPage = await getStateVariable(stateKeyPage, gameServerId, mod.moduleId);\n\n            if (currentPage === 0) {\n                console.log(`Wipe Continue (${wipeType}): Page is 0, indicating completion.`);\n                continueProcessing = false;\n                break;\n            }\n\n            console.log(`Wipe Continue (${wipeType}): Attempting page ${currentPage}`);\n            await player.pm(`Attempting page ${currentPage + 1} for ${wipeType}...`);\n            let pageResult = { itemsProcessed: 0, errorCount: 0, hasMore: false };\n\n            try {\n                // Call the processing function with current page prepended to args\n                pageResult = await processingFunction(currentPage, ...functionArgs);\n                pagesProcessedThisRun++;\n            } catch (pageError) {\n                console.error(`Wipe Continue (${wipeType}): Error processing page ${currentPage}: ${pageError}`);\n                await player.pm(`Error during ${wipeType} wipe on page ${currentPage + 1}. Use continue command again to retry.`);\n                continueProcessing = true;\n                break;\n            }\n\n            itemsProcessedThisRun += pageResult.itemsProcessed;\n            errorsThisRun += pageResult.errorCount;\n            continueProcessing = pageResult.hasMore;\n\n            if (!continueProcessing) {\n                console.log(`Wipe Continue (${wipeType}): Reached end at page ${currentPage}.`);\n                await setStateVariable(stateKeyPage, 0, gameServerId, mod.moduleId);\n                break;\n            } else {\n                await setStateVariable(stateKeyPage, currentPage + 1, gameServerId, mod.moduleId);\n            }\n        } // End page loop\n\n        // --- Report Results ---\n        let summaryMsg = `Continuation batch for ${wipeType} finished.\\nProcessed ${itemsProcessedThisRun} items across ${pagesProcessedThisRun} pages this run with ${errorsThisRun} errors.`;\n\n        if (continueProcessing) {\n            const nextPage = await getStateVariable(stateKeyPage, gameServerId, mod.moduleId);\n            summaryMsg += `\\nMore items remain. Next page to process: ${nextPage}. Run the command again.`;\n        } else {\n            summaryMsg += `\\nAll items for ${wipeType} complete. Page counter reset.`;\n            const currencyPg = await getStateVariable(STATE_KEYS.currencyPage, gameServerId, mod.moduleId);\n            const teleportsPg = await getStateVariable(STATE_KEYS.teleportsPage, gameServerId, mod.moduleId);\n            const starterKitsPg = await getStateVariable(STATE_KEYS.starterKitsPage, gameServerId, mod.moduleId);\n            if (currencyPg === 0 && teleportsPg === 0 && starterKitsPg === 0) {\n                summaryMsg += \"\\n\\nAll wipe tasks appear complete!\";\n                if (mod.userConfig.globalAnnouncement?.length > 0) {\n                    try {\n                        await takaro.gameserver.gameServerControllerSendMessage(gameServerId, { message: mod.userConfig.globalAnnouncement });\n                    } catch (error) {\n                        console.error(`Announcement Error: ${error}`);\n                        await player.pm(\"Error sending final announcement.\");\n                    }\n                }\n            }\n        }\n        await player.pm(summaryMsg);\n\n    } catch (error) {\n        console.error(`Wipe Continue: Error during setup or loop for ${wipeType}: ${error.stack || error.message}`);\n        throw new TakaroUserError(`Failed processing continuation batch for ${wipeType}: ${error.message}`);\n    }\n    console.log(`Wipe Continue: Finished run for type: ${wipeType}`);\n\n    return { success: true };\n}\n\nawait main();",
          "arguments": [
            {
              "name": "type",
              "type": "string",
              "helpText": "help",
              "defaultValue": "",
              "position": 0
            },
            {
              "name": "confirm",
              "type": "boolean",
              "helpText": "help",
              "defaultValue": "false",
              "position": 1
            }
          ]
        },
        {
          "name": "wipe",
          "trigger": "wipe",
          "helpText": "Wipe server data according to configuration",
          "function": "// commands/wipe_start_batch.js\nimport { takaro, data, checkPermission, TakaroUserError } from '@takaro/helpers';\n\n// --- Configuration ---\nconst PAGE_LIMIT = 100; // Items per fetch\nconst BATCH_SIZE = 100; // Items per concurrent processing batch (Adjust lower if timeouts persist)\nconst PAGES_PER_RUN = 4; // Number of pages to process per command execution (4 * 100 = 400 items)\n\n// --- State Variable Keys ---\nconst STATE_KEYS = {\n    currencyPage: 'wipe_cont_currency_page',\n    teleportsPage: 'wipe_cont_teleports_page',\n    starterKitsPage: 'wipe_cont_starterkits_page',\n    teleportsModuleId: 'wipe_cont_teleports_module_id',\n    waypointsModuleId: 'wipe_cont_waypoints_module_id'\n};\n\n// --- State Management Helpers ---\nasync function getStateVariable(key, gameServerId, moduleId) {\n    const existingVar = await takaro.variable.variableControllerSearch({\n        filters: { key: [key], gameServerId: [gameServerId], moduleId: [moduleId] },\n        limit: 1\n    });\n    if (existingVar.data.data.length > 0) {\n        try { return JSON.parse(existingVar.data.data[0].value) ?? 0; }\n        catch (e) { console.error(`State Error (Get: ${key}): ${e}`); return 0; }\n    }\n    return 0;\n}\n\nasync function setStateVariable(key, value, gameServerId, moduleId) {\n    const existingVar = await takaro.variable.variableControllerSearch({\n        filters: { key: [key], gameServerId: [gameServerId], moduleId: [moduleId] },\n        limit: 1\n    });\n    const valueString = JSON.stringify(value);\n    if (existingVar.data.data.length > 0) {\n        if (existingVar.data.data[0].value !== valueString) {\n            console.log(`State Set: ${key} = ${valueString}`);\n            await takaro.variable.variableControllerUpdate(existingVar.data.data[0].id, { value: valueString });\n        }\n    } else {\n        console.log(`State Create: ${key} = ${valueString}`);\n        await takaro.variable.variableControllerCreate({ key, value: valueString, gameServerId, moduleId });\n    }\n}\n\n// --- Currency wipe helper function ---\nasync function wipeCurrency(gameServerId, playerId, currentCurrency) {\n    try {\n        if (currentCurrency > 0) {\n            await takaro.playerOnGameserver.playerOnGameServerControllerDeductCurrency(gameServerId, playerId, { currency: currentCurrency });\n        }\n        await takaro.playerOnGameserver.playerOnGameServerControllerAddCurrency(gameServerId, playerId, { currency: 0.01 });\n        return { success: true };\n    } catch (error) {\n        console.error(`Currency wipe failed for player ${playerId}: ${error.message}`);\n        return { success: false, error };\n    }\n}\n\n// --- Main Function ---\nasync function main() {\n    const { player, module: mod, gameServerId, arguments: args, pog } = data;\n    const wipeType = args.target?.toLowerCase();\n    const wipeAll = wipeType === 'all';\n\n    // --- Initial Checks ---\n    if (!checkPermission(pog, 'SERVER_WIPE')) throw new TakaroUserError('Permission denied.');\n    if (!args.confirm || args.confirm.toString().toLowerCase() !== 'true') throw new TakaroUserError('Please confirm with `true`. Usage: /wipe {type|all} true');\n    if (!wipeType) throw new TakaroUserError('Specify target: all, currency, teleports, or starterkits.');\n\n    await player.pm(`Starting initial wipe batch (~${PAGES_PER_RUN * PAGE_LIMIT} items) for: ${wipeType}...`);\n\n    // --- Reset State & Find Module IDs ---\n    console.log('Wipe Start: Resetting state variables...');\n    await setStateVariable(STATE_KEYS.currencyPage, 0, gameServerId, mod.moduleId);\n    await setStateVariable(STATE_KEYS.teleportsPage, 0, gameServerId, mod.moduleId);\n    await setStateVariable(STATE_KEYS.starterKitsPage, 0, gameServerId, mod.moduleId);\n\n    const teleportsModule = await takaro.module.moduleControllerSearch({ filters: { name: ['teleports'] } });\n    const teleportsModuleId = teleportsModule.data.data.length > 0 ? teleportsModule.data.data[0].id : null;\n    await setStateVariable(STATE_KEYS.teleportsModuleId, teleportsModuleId, gameServerId, mod.moduleId);\n\n    const waypointsModule = await takaro.module.moduleControllerSearch({ filters: { name: ['Waypoints'] } });\n    const waypointsModuleId = waypointsModule.data.data.length > 0 ? waypointsModule.data.data[0].id : null;\n    await setStateVariable(STATE_KEYS.waypointsModuleId, waypointsModuleId, gameServerId, mod.moduleId);\n    console.log('Wipe Start: State reset complete.');\n\n    // --- Determine Types ---\n    const typesToProcess = [];\n    if (wipeAll || wipeType === 'currency') typesToProcess.push('currency');\n    if (wipeAll || wipeType === 'teleports') typesToProcess.push('teleports');\n    if (wipeAll || wipeType === 'starterkits') typesToProcess.push('starterkits');\n\n    let overallHasMore = false;\n\n    // --- Process Each Type ---\n    for (const currentType of typesToProcess) {\n        await player.pm(`Processing initial ${PAGES_PER_RUN} pages for ${currentType}...`);\n        console.log(`Wipe Start: Processing type ${currentType}`);\n\n        let currentPageForType = 0;\n        let itemsProcessedThisType = 0;\n        let errorsThisType = 0;\n        let typeHasMore = false;\n        let pagesProcessedThisTypeRun = 0;\n\n        for (let pageIndex = 0; pageIndex < PAGES_PER_RUN; pageIndex++) {\n            currentPageForType = await getStateVariable(STATE_KEYS[`${currentType}Page`], gameServerId, mod.moduleId);\n\n            if (currentPageForType === 0 && pageIndex > 0) {\n                typeHasMore = false;\n                break;\n            }\n\n            console.log(`Wipe Start (${currentType}): Attempting page ${currentPageForType}`);\n            let pageResult = { itemsProcessed: 0, errorCount: 0, hasMore: false };\n\n            try {\n                // *** Page Processing Logic ***\n                if (currentType === 'currency') {\n                    const pogsResponse = await takaro.playerOnGameserver.playerOnGameServerControllerSearch({\n                        filters: { gameServerId: [gameServerId] }, limit: PAGE_LIMIT, page: currentPageForType\n                    });\n                    const pogs = pogsResponse.data.data;\n                    const countOnPage = pogs.length;\n                    if (!pogs || countOnPage === 0) {\n                        pageResult.hasMore = false;\n                    } else {\n                        pageResult.hasMore = countOnPage === PAGE_LIMIT;\n                        for (let i = 0; i < countOnPage; i += BATCH_SIZE) {\n                            const batch = pogs.slice(i, i + BATCH_SIZE);\n                            const results = await Promise.allSettled(batch.map(p => wipeCurrency(gameServerId, p.playerId, p.currency)));\n                            results.forEach(r => {\n                                if (r.status === 'fulfilled' && r.value.success) {\n                                    pageResult.itemsProcessed++;\n                                } else {\n                                    pageResult.errorCount++;\n                                    console.error(`Currency Error: ${r.reason?.message || r.value?.error?.message || r.reason}`);\n                                }\n                            });\n                        }\n                    }\n                } else if (currentType === 'teleports') {\n                    const tpModuleId = await getStateVariable(STATE_KEYS.teleportsModuleId, gameServerId, mod.moduleId);\n                    if (!tpModuleId) { await player.pm('Skipping teleports: Module ID not found.'); break; }\n                    const varsResponse = await takaro.variable.variableControllerSearch({\n                        filters: { gameServerId: [gameServerId], moduleId: [tpModuleId] }, limit: PAGE_LIMIT, page: currentPageForType\n                    });\n                    const variables = varsResponse.data.data;\n                    const countOnPage = variables.length;\n                    if (!variables || countOnPage === 0) {\n                        pageResult.hasMore = false;\n                    } else {\n                        pageResult.hasMore = countOnPage === PAGE_LIMIT;\n                        for (let i = 0; i < countOnPage; i += BATCH_SIZE) {\n                            const batch = variables.slice(i, i + BATCH_SIZE);\n                            const results = await Promise.allSettled(batch.map(v =>\n                                takaro.variable.variableControllerDelete(v.id)\n                            ));\n                            results.forEach(r => {\n                                if (r.status === 'fulfilled') {\n                                    pageResult.itemsProcessed++;\n                                } else {\n                                    pageResult.errorCount++;\n                                    console.error(`Teleports Error: ${r.reason?.message || r.reason}`);\n                                }\n                            });\n                        }\n                    }\n                } else if (currentType === 'starterkits') {\n                    const varsResponse = await takaro.variable.variableControllerSearch({\n                        filters: { gameServerId: [gameServerId], key: ['t_starterkit_lock'], moduleId: [mod.moduleId] },\n                        limit: PAGE_LIMIT, page: currentPageForType\n                    });\n                    const variables = varsResponse.data.data;\n                    const countOnPage = variables.length;\n                    if (!variables || countOnPage === 0) {\n                        pageResult.hasMore = false;\n                    } else {\n                        pageResult.hasMore = countOnPage === PAGE_LIMIT;\n                        for (let i = 0; i < countOnPage; i += BATCH_SIZE) {\n                            const batch = variables.slice(i, i + BATCH_SIZE);\n                            const results = await Promise.allSettled(batch.map(v =>\n                                takaro.variable.variableControllerDelete(v.id)\n                            ));\n                            results.forEach(r => {\n                                if (r.status === 'fulfilled') {\n                                    pageResult.itemsProcessed++;\n                                } else {\n                                    pageResult.errorCount++;\n                                    console.error(`Starter Kits Error: ${r.reason?.message || r.reason}`);\n                                }\n                            });\n                        }\n                    }\n                }\n\n                itemsProcessedThisType += pageResult.itemsProcessed;\n                errorsThisType += pageResult.errorCount;\n                typeHasMore = pageResult.hasMore;\n                pagesProcessedThisTypeRun++;\n\n                if (!typeHasMore) {\n                    console.log(`Wipe Start (${currentType}): Reached end at page ${currentPageForType}`);\n                    await setStateVariable(STATE_KEYS[`${currentType}Page`], 0, gameServerId, mod.moduleId);\n                    break;\n                } else {\n                    await setStateVariable(STATE_KEYS[`${currentType}Page`], currentPageForType + 1, gameServerId, mod.moduleId);\n                }\n\n            } catch (error) {\n                console.error(`Wipe Start (${currentType}): Error processing page ${currentPageForType}: ${error}`);\n                await player.pm(`Error during ${currentType} wipe on page ${currentPageForType + 1}. Use continue command to retry.`);\n                await setStateVariable(STATE_KEYS[`${currentType}Page`], currentPageForType, gameServerId, mod.moduleId);\n                typeHasMore = true;\n                break;\n            }\n        } // End page loop (PAGES_PER_RUN)\n\n        await player.pm(`Initial batch for ${currentType}: Processed ${itemsProcessedThisType} items across ${pagesProcessedThisTypeRun} pages with ${errorsThisType} errors.`);\n        if (typeHasMore) {\n            await player.pm(`More ${currentType} items remain. Use /wipecontinue ${currentType}`);\n            overallHasMore = true;\n        } else {\n            await player.pm(`${currentType} wipe completed.`);\n        }\n    } // End loop over types\n\n    // --- Final Message & Announcement ---\n    if (!overallHasMore) {\n        await player.pm(\"Initial wipe batch finished, and all targeted items completed.\");\n        if (wipeAll && mod.userConfig.globalAnnouncement?.length > 0) {\n            try {\n                await takaro.gameserver.gameServerControllerSendMessage(gameServerId, { message: mod.userConfig.globalAnnouncement });\n            } catch (error) {\n                console.error(`Announcement Error: ${error}`);\n                await player.pm(\"Error sending final announcement.\");\n            }\n        }\n    } else {\n        await player.pm(\"Initial wipe batch finished. Use /wipecontinue {type} for remaining items.\");\n    }\n    console.log('Wipe Start: Finished.');\n\n    return { success: true };\n}\n\nawait main();",
          "arguments": [
            {
              "name": "confirm",
              "type": "boolean",
              "helpText": "Confirm wipe operation (must be true to execute)",
              "defaultValue": "false",
              "position": 1
            },
            {
              "name": "target",
              "type": "string",
              "helpText": "What to wipe: all, currency, teleports, waypoints, or starterkits",
              "defaultValue": "all",
              "position": 0
            }
          ]
        }
      ],
      "hooks": [],
      "cronJobs": [],
      "functions": [
        {
          "name": "wipeHelpers.js",
          "description": "",
          "function": "// functions/wipeHelpers.js\nimport { takaro } from '@takaro/helpers';\n\nexport const PAGE_LIMIT = 100; // How many items to FETCH per page\nexport const BATCH_SIZE = 100; // How many items to PROCESS concurrently within a page (Adjust if needed)\n\n// --- State Variable Keys ---\nexport const STATE_KEYS = {\n    currencyPage: 'wipe_cont_currency_page',\n    teleportsPage: 'wipe_cont_teleports_page',\n    starterKitsPage: 'wipe_cont_starterkits_page',\n    // We also need to store the module IDs found by the start command\n    teleportsModuleId: 'wipe_cont_teleports_module_id',\n    waypointsModuleId: 'wipe_cont_waypoints_module_id'\n};\n\n// --- Get/Set State ---\nexport async function getStateVariable(key, gameServerId, moduleId) {\n    const existingVar = await takaro.variable.variableControllerSearch({\n        filters: { key: [key], gameServerId: [gameServerId], moduleId: [moduleId] },\n        limit: 1\n    });\n    if (existingVar.data.data.length > 0) {\n        try {\n            const parsed = JSON.parse(existingVar.data.data[0].value);\n            return parsed ?? 0; // Default to 0 if null/undefined after parsing\n        } catch (e) {\n            console.error(`Wipe Helpers: Error parsing state variable ${key}: ${e}. Defaulting to 0.`);\n            return 0;\n        }\n    }\n    console.log(`Wipe Helpers: State variable ${key} not found, defaulting to 0.`);\n    return 0; // Default to 0 if variable doesn't exist\n}\n\nexport async function setStateVariable(key, value, gameServerId, moduleId) {\n    const existingVar = await takaro.variable.variableControllerSearch({\n        filters: { key: [key], gameServerId: [gameServerId], moduleId: [moduleId] },\n        limit: 1\n    });\n    const valueString = JSON.stringify(value);\n    if (existingVar.data.data.length > 0) {\n        if (existingVar.data.data[0].value !== valueString) {\n            console.log(`Wipe Helpers: Updating state variable ${key} to ${valueString}`);\n            await takaro.variable.variableControllerUpdate(existingVar.data.data[0].id, { value: valueString });\n        }\n    } else {\n        console.log(`Wipe Helpers: Creating state variable ${key} with value ${valueString}`);\n        await takaro.variable.variableControllerCreate({\n            key: key, value: valueString, gameServerId, moduleId\n        });\n    }\n}\n\n// --- Process One Page of Currency ---\nexport async function processCurrencyPage(page, gameServerId) {\n    let itemsProcessed = 0;\n    let errorCount = 0;\n    let hasMore = false;\n\n    console.log(`Wipe Helpers: Processing currency page ${page}`);\n\n    const pogsResponse = await takaro.playerOnGameserver.playerOnGameServerControllerSearch({\n        filters: { gameServerId: [gameServerId] },\n        limit: PAGE_LIMIT,\n        page: page\n    });\n\n    const pogs = pogsResponse.data.data;\n    const countOnPage = pogs.length;\n    console.log(`Wipe Helpers: Currency page ${page} fetched ${countOnPage} items.`);\n\n    if (!pogs || countOnPage === 0) {\n        console.log(`Wipe Helpers: Currency page ${page} - No items found.`);\n        return { itemsProcessed: 0, errorCount: 0, hasMore: false };\n    }\n\n    hasMore = countOnPage === PAGE_LIMIT;\n\n    for (let i = 0; i < countOnPage; i += BATCH_SIZE) {\n        const batch = pogs.slice(i, i + BATCH_SIZE);\n        // console.log(`Wipe Helpers: Currency page ${page}, processing batch index ${i} (size ${batch.length})`); // Optional detailed log\n        const results = await Promise.allSettled(\n            batch.map(pogEntry =>\n                takaro.playerOnGameserver.playerOnGameServerControllerSetCurrency(\n                    gameServerId, pogEntry.playerId, { currency: 0.01 }  // Changed from 0 to 0.01\n                )\n            )\n        );\n        results.forEach(result => {\n            if (result.status === 'fulfilled') {\n                itemsProcessed++;\n            } else {\n                errorCount++;\n                console.error(`Currency Error: ${result.reason?.message || result.reason}`);\n            }\n        });\n    }\n    console.log(`Wipe Helpers: Currency page ${page} finished. Processed: ${itemsProcessed}, Errors: ${errorCount}, HasMore: ${hasMore}`);\n    return { itemsProcessed, errorCount, hasMore };\n}\n\n// --- Process One Page of Variables ---\nexport async function processVariablePage(page, gameServerId, moduleId, variableKeyOrModuleId, typeName) {\n    let itemsProcessed = 0;\n    let errorCount = 0;\n    let hasMore = false;\n\n    console.log(`Wipe Helpers: Processing ${typeName} variable page ${page}`);\n\n    const filters = { gameServerId: [gameServerId] };\n    if (variableKeyOrModuleId.startsWith('var_key_')) {\n        filters.key = [variableKeyOrModuleId.substring(8)];\n        filters.moduleId = [moduleId]; // Scope to this module when searching by key\n    } else if (variableKeyOrModuleId) { // Check if module ID is provided and not null/empty\n        filters.moduleId = [variableKeyOrModuleId]; // Use specific module ID\n    } else {\n        console.error(`Wipe Helpers: Invalid variableKeyOrModuleId for ${typeName}`);\n        return { itemsProcessed: 0, errorCount: 0, hasMore: false }; // Cannot process without key/moduleID\n    }\n\n    const varsResponse = await takaro.variable.variableControllerSearch({\n        filters: filters, limit: PAGE_LIMIT, page: page\n    });\n    const variables = varsResponse.data.data;\n    const countOnPage = variables.length;\n    console.log(`Wipe Helpers: ${typeName} page ${page} fetched ${countOnPage} items.`);\n\n    if (!variables || countOnPage === 0) {\n        console.log(`Wipe Helpers: ${typeName} page ${page} - No items found.`);\n        return { itemsProcessed: 0, errorCount: 0, hasMore: false };\n    }\n\n    hasMore = countOnPage === PAGE_LIMIT;\n\n    for (let i = 0; i < countOnPage; i += BATCH_SIZE) {\n        const batch = variables.slice(i, i + BATCH_SIZE);\n        // console.log(`Wipe Helpers: ${typeName} page ${page}, processing batch index ${i} (size ${batch.length})`); // Optional detailed log\n        const results = await Promise.allSettled(\n            batch.map(variable =>\n                takaro.variable.variableControllerDelete(variable.id)\n            )\n        );\n        results.forEach(result => {\n            if (result.status === 'fulfilled') {\n                itemsProcessed++;\n            } else {\n                errorCount++;\n                console.error(`${typeName} Delete Error: ${result.reason?.message || result.reason}`);\n            }\n        });\n    }\n    console.log(`Wipe Helpers: ${typeName} page ${page} finished. Processed: ${itemsProcessed}, Errors: ${errorCount}, HasMore: ${hasMore}`);\n    return { itemsProcessed, errorCount, hasMore };\n}\n"
        }
      ],
      "permissions": [
        {
          "permission": "SERVER_WIPE",
          "friendlyName": "SERVER_WIPE",
          "description": "Permission to wipe server",
          "canHaveCount": false
        }
      ]
    }
  ]
}