diff options
Diffstat (limited to 'server/frob/index.ts')
-rw-r--r-- | server/frob/index.ts | 183 |
1 files changed, 183 insertions, 0 deletions
diff --git a/server/frob/index.ts b/server/frob/index.ts new file mode 100644 index 0000000..332989a --- /dev/null +++ b/server/frob/index.ts @@ -0,0 +1,183 @@ +// this script removes specified items from inventories and storage +import { CharParser, CharWriter } from "./char.ts"; +import { StorageParser, StorageWriter } from "./storage.ts"; +import { ItemDB } from "./itemdb.ts"; + +const args: string[] = Deno.args.slice(1); +const to_remove: Set<number> = new Set(); +const is_alpha: boolean = isNaN(args.join() as unknown as number); +const char_parser = new CharParser(); +const storage_parser = new StorageParser(); +const char_writer = new CharWriter(); +const storage_writer = new StorageWriter(); +const item_db = new ItemDB(); + +const stats = { + inventory: { + removed: 0, + pruned: 0, + stub: 0, + chars: 0, + }, + storage: { + removed: 0, + pruned: 0, + stub: 0, + wiped: 0, + synced: 0, + accounts: 0, + }, +}; + + +(async () => { + const items_by_id = new Map(); // holds full item info + const items_by_name = new Map(); // holds references to the former + + for await (let item of item_db.readDB()) { + items_by_id.set(+item.id, item); + items_by_name.set(item.name, +item.id); + } + + const itemToNumber = (name: string|number): number => { + if (isNaN(name as number)) { + const item_id = items_by_name.get(name); + + if (item_id) { + return +item_id; + } else { + console.error(`cannot find item '${name}' in the item db`); + throw new Error("item not found"); + } + } else { + return +name; + } + } + + const itemToString = (id: string|number): string => { + if (isNaN(id as number)) { + return `${id}`; + } else { + const item = items_by_id.get(+id); + + if (item) { + return item.name; + } else { + console.error(`cannot find item ID ${id} in the item db`); + throw new Error("item not found"); + } + } + } + + if (args.length < 1) { + throw new RangeError("no items given!"); + } + + for (let arg of args.join(",").split(",")) { + if (arg.includes("-") || arg.includes("..")) { + const range = arg.split("-").join("..").split(".."); + let from = itemToNumber(range[0]), to = itemToNumber(range[1]); + + if (from > to) { + [from, to] = [to, from]; + } + + for (let i = from; i <= to; ++i) { + to_remove.add(i); + } + } else { + to_remove.add(itemToNumber(arg)); + } + } + + console.info("\nThe following items will be removed:"); + for (let item of to_remove) { + console.info(`[${item}]: ${itemToString(item)}`); + } + + // inventory: + for await (let char of char_parser.readDB()) { + let items_filtered = []; // this is not a Set because some items don't stack + let mod = false; + + for (let item of char.items) { + if (!items_by_id.has(+item.nameid)) { + console.log(`removing ${+item.amount || 1}x non-existant item ID ${item.nameid} from inventory of character ${char.name} [${char.account_id}:${char.char_id}]`); + stats.inventory.pruned += +item.amount; + mod = true; + } else if (+item.amount < 1) { + console.log(`removing stub of item ${itemToString(item.nameid)} [${item.nameid}] from inventory of character ${char.name} [${char.account_id}:${char.char_id}]`); + stats.inventory.stub++; + mod = true; + } else if (to_remove.has(+item.nameid)) { + console.log(`removing ${item.amount}x ${itemToString(item.nameid)} [${item.nameid}] from inventory of character ${char.name} [${char.account_id}:${char.char_id}]`); + stats.inventory.removed += +item.amount; + mod = true; + } else { + items_filtered.push(item); + } + } + + if (mod) + stats.inventory.chars++; + + char.items = items_filtered; + await char_writer.write(char); + } + + await char_writer.finalize(); + + // storage: + for await (let storage of storage_parser.readDB()) { + let items_filtered = []; // this is not a Set because some items don't stack + let mod = false; + + for (let item of storage.items) { + if (!items_by_id.has(+item.nameid)) { + console.log(`removing ${+item.amount || 1}x non-existant item ID ${item.nameid} from storage of account ${storage.account_id}`); + stats.storage.pruned += +item.amount; + storage.storage_amount--; + mod = true; + } else if (+item.amount < 1) { + console.log(`removing stub of item ${itemToString(item.nameid)} [${item.nameid}] from storage of account ${storage.account_id}`); + stats.storage.stub++; + storage.storage_amount--; + mod = true; + } else if (to_remove.has(+item.nameid)) { + console.log(`removing ${item.amount}x ${itemToString(item.nameid)} [${item.nameid}] from storage of account ${storage.account_id}`); + stats.storage.removed += +item.amount; + storage.storage_amount--; + mod = true; + } else { + items_filtered.push(item); + } + } + + if (mod) + stats.storage.accounts++; + + storage.items = items_filtered; + + if (+storage.storage_amount !== storage.items.length) { + const old_sync = +storage.storage_amount; + storage.storage_amount = storage.items.length; + console.log(`fixing sync of storage for account ${storage.account_id}: ${old_sync} => ${storage.storage_amount}`); + stats.storage.synced++; + } + + if (storage.storage_amount >= 1) { + await storage_writer.write(storage); + } else { + console.log(`storage of account ${storage.account_id} is now empty: removing it from the storage db`); + stats.storage.wiped++; + } + } + + await storage_writer.finalize(); + + console.info("\n=== all done ==="); + console.info(`removed ${stats.inventory.removed} existant, ${stats.inventory.pruned} non-existant and ${stats.inventory.stub} stub items from the inventory of ${stats.inventory.chars} characters`); + console.info(`removed ${stats.storage.removed} existant, ${stats.storage.pruned} non-existant and ${stats.storage.stub} stub items from the storage of ${stats.storage.accounts} accounts`); + console.info(`removed ${stats.storage.wiped} empty storage entries from the storage db`); + console.info(`fixed storage sync of ${stats.storage.synced} accounts`); +})() |