From 34df8df7310d7d751c466dbbcf7fe0cbd572ec74 Mon Sep 17 00:00:00 2001 From: iBlqzed <101911086+iBlqzed@users.noreply.github.com> Date: Sun, 21 Jan 2024 02:22:31 -0600 Subject: [PATCH] Updated database (#350) --- scripts/database/index.d.ts | 67 ++++++++-------- scripts/database/index.js | 147 ++++++++++++++++------------------ scripts/database/index.ts | 155 +++++++++++++++++++----------------- 3 files changed, 185 insertions(+), 184 deletions(-) diff --git a/scripts/database/index.d.ts b/scripts/database/index.d.ts index 5a2deb91..2591b304 100644 --- a/scripts/database/index.d.ts +++ b/scripts/database/index.d.ts @@ -1,43 +1,39 @@ -/** - * Database - */ -export declare class Database { - protected readonly data: Map; - /** - * The name of the database - */ +// Script example for ScriptAPI +// Author: iBlqzed +// Project: https://github.com/JaylyDev/ScriptAPI + +export declare class Database { readonly name: string; - /** - * Create a new database! - */ - constructor(name: string); - /** - * The length of the database - */ - get length(): number; + private readonly defaultValue; + static readonly databases: Database[]; + private cache; + constructor(name: string, defaultValue?: string); /** * Set a value from a key - * @param {string} key Key to set - * @param {any} value The value + * @remarks Doesn't save instantly, call .save() or wait 1 minute to save automatically + * @param {string} property Key to set + * @param {V} value The value */ - set(key: string, value: any): void; + set(property: string, value: V): void; /** * Get a value from a key - * @param {string} key Key to get - * @returns {any} The value that was set for the key (or undefined) + * @param {string} property Key to get + * @returns {V} The value that was set for the key (or undefined) */ - get(key: string): any; + get(property: string): V; /** * Test for whether or not the database has the key - * @param {string} key Key to test for + * @param {string} property Key to test for * @returns {boolean} Whether or not the database has the key */ - has(key: string): boolean; + has(property: string): boolean; /** * Delete a key from the database - * @param {string} key Key to delete from the database + * @remarks Doesn't save instantly, call .save() or wait 1 minute to save automatically + * @param {string} property Key to delete from the database + * @returns {boolean} Whether the database had the key to begin with */ - delete(key: string): void; + delete(property: string): boolean; /** * Get an array of all keys in the database * @returns {string[]} An array of all keys in the database @@ -45,17 +41,24 @@ export declare class Database { keys(): string[]; /** * Get an array of all values in the database - * @returns {any[]} An array of all values in the database + * @returns {V[]} An array of all values in the database */ - values(): any[]; + values(): V[]; /** * Clears all values in the database + * @remarks Saves instantly */ clear(): void; /** - * Loop through all keys and values of the database - * @param {(key: string, value: any) => void} callback Code to run per loop + * Get an object with all keys and values + * @remarks All changes will save + * @returns {Record} An object of all keys and values + */ + getAll(): Record; + /** + * Save the database instantly */ - forEach(callback: (key: string, value: any) => void): void; - [Symbol.iterator](): IterableIterator<[string, any]>; + save(): void; + protected static save(): void; + protected static getAll(name: string, defaultValue: string): Record; } diff --git a/scripts/database/index.js b/scripts/database/index.js index c60f803d..f5ba4428 100644 --- a/scripts/database/index.js +++ b/scripts/database/index.js @@ -1,123 +1,114 @@ // Script example for ScriptAPI // Author: iBlqzed // Project: https://github.com/JaylyDev/ScriptAPI -import { world } from "@minecraft/server"; -const names = []; -/** - * Database - */ + +import { world, system } from "@minecraft/server"; export class Database { - /** - * Create a new database! - */ - constructor(name) { - this.data = new Map(); - this.name = JSON.stringify(name).slice(1, -1).replaceAll(/"/g, '\\"'); - if (names.includes(this.name)) - throw new Error(`You can't have 2 of the same databases`); - if (this.name.includes('"')) - throw new TypeError(`Database names can't include "!`); - if (this.name.length > 13 || this.name.length === 0) - throw new Error(`Database names can't be more than 13 characters long, and it can't be nothing!`); - names.push(this.name); - runCommand(`scoreboard objectives add "DB_${this.name}" dummy`); - world.scoreboard.getObjective(`DB_${this.name}`).getParticipants().forEach(e => this.data.set(e.displayName.split("_")[0].replaceAll(/\\"/g, '"'), JSON.parse(e.displayName.split("_").filter((v, i) => i > 0).join("_").replaceAll(/\\"/g, '"')))); - } - /** - * The length of the database - */ - get length() { - return this.data.size; + constructor(name, defaultValue = "{}") { + this.name = name; + this.defaultValue = defaultValue; + this.cache = Database.getAll(this.name, this.defaultValue); + Database.databases.push(this); } /** * Set a value from a key - * @param {string} key Key to set - * @param {any} value The value + * @remarks Doesn't save instantly, call .save() or wait 1 minute to save automatically + * @param {string} property Key to set + * @param {V} value The value */ - set(key, value) { - if (key.includes('_')) - throw new TypeError(`Database keys can't include "_"`); - if ((JSON.stringify(value).replaceAll(/"/g, '\\"').length + key.replaceAll(/"/g, '\\"').length + 1) > 32000) - throw new Error(`Database setter to long... somehow`); - if (this.data.has(key)) - runCommand(`scoreboard players reset "${key.replaceAll(/"/g, '\\"')}_${JSON.stringify(this.data.get(key)).replaceAll(/"/g, '\\"')}" "DB_${this.name}"`); - runCommand(`scoreboard players set "${key.replaceAll(/"/g, '\\"')}_${JSON.stringify(value).replaceAll(/"/g, '\\"')}" "DB_${this.name}" 0`); - this.data.set(key, value); + set(property, value) { + this.cache[property] = value; } /** * Get a value from a key - * @param {string} key Key to get - * @returns {any} The value that was set for the key (or undefined) + * @param {string} property Key to get + * @returns {V} The value that was set for the key (or undefined) */ - get(key) { - if (this.data.has(key)) - return this.data.get(key); - return undefined; + get(property) { + return this.cache[property]; } /** * Test for whether or not the database has the key - * @param {string} key Key to test for + * @param {string} property Key to test for * @returns {boolean} Whether or not the database has the key */ - has(key) { - if (!this.data.has(key)) - return false; - return true; + has(property) { + return (property in this.cache); } /** * Delete a key from the database - * @param {string} key Key to delete from the database + * @remarks Doesn't save instantly, call .save() or wait 1 minute to save automatically + * @param {string} property Key to delete from the database + * @returns {boolean} Whether the database had the key to begin with */ - delete(key) { - if (!this.data.has(key)) - return; - runCommand(`scoreboard players reset "${key.replaceAll(/"/g, '\\"')}_${JSON.stringify(this.data.get(key)).replaceAll(/"/g, '\\"')}" "DB_${this.name}"`); - this.data.delete(key); + delete(property) { + return delete this.cache[property]; } /** * Get an array of all keys in the database * @returns {string[]} An array of all keys in the database */ keys() { - return [...this.data.keys()]; + return Object.keys(this.cache); } /** * Get an array of all values in the database - * @returns {any[]} An array of all values in the database + * @returns {V[]} An array of all values in the database */ values() { - return [...this.data.values()]; + return Object.values(this.cache); } /** * Clears all values in the database + * @remarks Saves instantly */ clear() { - runCommand(`scoreboard objectives remove "DB_${this.name}"`); - runCommand(`scoreboard objectives add "DB_${this.name}" dummy`); - this.data.clear(); + this.cache = {}; + this.save(); } /** - * Loop through all keys and values of the database - * @param {(key: string, value: any) => void} callback Code to run per loop + * Get an object with all keys and values + * @remarks All changes will save + * @returns {Record} An object of all keys and values */ - forEach(callback) { - this.data.forEach((v, k) => callback(k, v)); + getAll() { + return this.cache ?? (this.cache = Database.getAll(this.name, this.defaultValue)); } - *[Symbol.iterator]() { - yield* this.data.entries(); + /** + * Save the database instantly + */ + save() { + const stringified = JSON.stringify(this.cache); + const index = Math.ceil(stringified.length / 32000); + world.setDynamicProperty(`${this.name}Index`); + for (let i = 0; i < index; i++) { + world.setDynamicProperty(`${this.name}:${i}`, stringified.slice(i * 32000, (i + 1) * 32000)); + } } -} -/** - * Run a command! - * @param {string} cmd Command to run - * @returns {{ error: boolean, data: any }} Whether or not the command errors, and command data - * @example runCommand(`give @a diamond`) - */ -function runCommand(cmd) { - try { - return { error: false, data: world.getDimension('overworld').runCommand(cmd) }; + static save() { + this.databases.forEach((database) => { + database.save(); + }); } - catch { - return { error: true, data: undefined }; + static getAll(name, defaultValue) { + let stringified = ""; + const index = world.getDynamicProperty(`${name}Index`); + if (!index) { + world.setDynamicProperty(`${name}Index`, 1); + world.setDynamicProperty(`${name}:0`, defaultValue); + stringified = defaultValue; + } + else { + for (let i = 0; i < index; i++) { + const value = world.getDynamicProperty(`${this.name}:${i}`); + stringified += value; + } + } + return JSON.parse(stringified); } } +Database.databases = new Array(); +system.runInterval(() => { + //@ts-ignore + Database.save(); +}, 1200); diff --git a/scripts/database/index.ts b/scripts/database/index.ts index 27c7d74b..9c598a0f 100644 --- a/scripts/database/index.ts +++ b/scripts/database/index.ts @@ -2,117 +2,124 @@ // Author: iBlqzed // Project: https://github.com/JaylyDev/ScriptAPI -import { world } from "@minecraft/server" +import { world, system } from "@minecraft/server"; -const names: string[] = [] +export class Database { + static readonly databases = new Array>() + private cache = Database.getAll(this.name, this.defaultValue) -/** - * Database - */ -export class Database { - protected readonly data: Map = new Map() - /** - * The name of the database - */ - readonly name: string - /** - * Create a new database! - */ - constructor(name: string) { - this.name = JSON.stringify(name).slice(1, -1).replaceAll(/"/g, '\\"') - if (names.includes(this.name)) throw new Error(`You can't have 2 of the same databases`) - if (this.name.includes('"')) throw new TypeError(`Database names can't include "!`) - if (this.name.length > 13 || this.name.length === 0) throw new Error(`Database names can't be more than 13 characters long, and it can't be nothing!`) - names.push(this.name) - runCommand(`scoreboard objectives add "DB_${this.name}" dummy`) - world.scoreboard.getObjective(`DB_${this.name}`).getParticipants().forEach(e => this.data.set(e.displayName.split("_")[0].replaceAll(/\\"/g, '"'), JSON.parse(e.displayName.split("_").filter((v, i) => i > 0).join("_").replaceAll(/\\"/g, '"')))) - } - /** - * The length of the database - */ - get length(): number { - return this.data.size + public constructor(readonly name: string, private readonly defaultValue: string = "{}") { + Database.databases.push(this) } + /** * Set a value from a key - * @param {string} key Key to set - * @param {any} value The value + * @remarks Doesn't save instantly, call .save() or wait 1 minute to save automatically + * @param {string} property Key to set + * @param {V} value The value */ - set(key: string, value: any): void { - if (key.includes('_')) throw new TypeError(`Database keys can't include "_"`) - if ((JSON.stringify(value).replaceAll(/"/g, '\\"').length + key.replaceAll(/"/g, '\\"').length + 1) > 32000) throw new Error(`Database setter to long... somehow`) - if (this.data.has(key)) runCommand(`scoreboard players reset "${key.replaceAll(/"/g, '\\"')}_${JSON.stringify(this.data.get(key)).replaceAll(/"/g, '\\"')}" "DB_${this.name}"`) - runCommand(`scoreboard players set "${key.replaceAll(/"/g, '\\"')}_${JSON.stringify(value).replaceAll(/"/g, '\\"')}" "DB_${this.name}" 0`) - this.data.set(key, value) + public set(property: string, value: V): void { + this.cache[property] = value } + /** * Get a value from a key - * @param {string} key Key to get - * @returns {any} The value that was set for the key (or undefined) + * @param {string} property Key to get + * @returns {V} The value that was set for the key (or undefined) */ - get(key: string): any { - if (this.data.has(key)) return this.data.get(key) - return undefined + public get(property: string): V { + return this.cache[property] } + /** * Test for whether or not the database has the key - * @param {string} key Key to test for + * @param {string} property Key to test for * @returns {boolean} Whether or not the database has the key */ - has(key: string): boolean { - if (!this.data.has(key)) return false - return true + public has(property: string): boolean { + return (property in this.cache) } + /** * Delete a key from the database - * @param {string} key Key to delete from the database + * @remarks Doesn't save instantly, call .save() or wait 1 minute to save automatically + * @param {string} property Key to delete from the database + * @returns {boolean} Whether the database had the key to begin with */ - delete(key: string): void { - if (!this.data.has(key)) return; - runCommand(`scoreboard players reset "${key.replaceAll(/"/g, '\\"')}_${JSON.stringify(this.data.get(key)).replaceAll(/"/g, '\\"')}" "DB_${this.name}"`) - this.data.delete(key) + public delete(property: string): boolean { + return delete this.cache[property] } + /** * Get an array of all keys in the database * @returns {string[]} An array of all keys in the database */ - keys(): string[] { - return [...this.data.keys()] + public keys(): string[] { + return Object.keys(this.cache); } /** * Get an array of all values in the database - * @returns {any[]} An array of all values in the database + * @returns {V[]} An array of all values in the database */ - values(): any[] { - return [...this.data.values()] + public values(): V[] { + return Object.values(this.cache) } + /** * Clears all values in the database + * @remarks Saves instantly + */ + public clear() { + this.cache = {} + this.save() + } + + /** + * Get an object with all keys and values + * @remarks All changes will save + * @returns {Record} An object of all keys and values */ - clear(): void { - runCommand(`scoreboard objectives remove "DB_${this.name}"`) - runCommand(`scoreboard objectives add "DB_${this.name}" dummy`) - this.data.clear() + public getAll(): Record { + return this.cache ??= Database.getAll(this.name, this.defaultValue) } + /** - * Loop through all keys and values of the database - * @param {(key: string, value: any) => void} callback Code to run per loop + * Save the database instantly */ - forEach(callback: (key: string, value: any) => void) { - this.data.forEach((v, k) => callback(k, v)) + public save(): void { + const stringified = JSON.stringify(this.cache) + const index = Math.ceil(stringified.length / 32000) + world.setDynamicProperty(`${this.name}Index`) + for (let i = 0; i < index; i++) { + world.setDynamicProperty(`${this.name}:${i}`, stringified.slice(i * 32000, (i + 1) * 32000)) + } } - *[Symbol.iterator](): IterableIterator<[string, any]> { - yield* this.data.entries() + + protected static save() { + this.databases.forEach((database) => { + database.save() + }) + } + + protected static getAll(name: string, defaultValue: string): Record { + let stringified = "" + const index = world.getDynamicProperty(`${name}Index`) as number + if (!index) { + world.setDynamicProperty(`${name}Index`, 1) + world.setDynamicProperty(`${name}:0`, defaultValue) + stringified = defaultValue + } else { + for (let i = 0; i < index; i++) { + const value = world.getDynamicProperty(`${this.name}:${i}`) + stringified += value + } + } + return JSON.parse(stringified) } } -/** - * Run a command! - * @param {string} cmd Command to run - * @returns {{ error: boolean, data: any }} Whether or not the command errors, and command data - * @example runCommand(`give @a diamond`) - */ -function runCommand(cmd: string): { error: boolean, data: any } { - try { return { error: false, data: world.getDimension('overworld').runCommand(cmd) } } catch { return { error: true, data: undefined } } -} \ No newline at end of file +system.runInterval(() => { + //@ts-ignore + Database.save() +}, 1200) \ No newline at end of file