Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
235 changes: 145 additions & 90 deletions scripts/quick-db/index.js
Original file line number Diff line number Diff line change
@@ -1,102 +1,157 @@
// Script example for ScriptAPI
// Author: Nperma <https://github.com/nperma>
// Project: https://github.com/JaylyDev/ScriptAPI
import { world, World } from "@minecraft/server";

const DATABASE_PREFIX = "\u0235\u0235";
import { world, World } from '@minecraft/server';

const DATABASE_PREFIX = '\u0235\u0235';

const {
getDynamicProperty: GET,
setDynamicProperty: SET,
getDynamicPropertyIds: IDS
getDynamicProperty: GET,
setDynamicProperty: SET,
getDynamicPropertyIds: IDS
} = World.prototype;

//adapt code to JalyDev/scriptAPI
class QuickDB {
#identifier;
constructor(id) {
this.#identifier = `${DATABASE_PREFIX}${id}${DATABASE_PREFIX}`;
}

get size() {
return IDS.call(world).filter((id) => id.startsWith(this.#identifier))
.length;
}

has(key) {
return !!(
GET.call(world, `${this.#identifier}${key}`) &&
GET.call(world, `${this.#identifier}${key}`) !== undefined
);
}

get(key) {
return this.has(key)
? JSON.parse(GET.call(world, `${this.#identifier}${key}`))
: undefined;
}

set(key, value) {
if (typeof key !== "string") return false;
SET.call(world, `${this.#identifier}${key}`, JSON.stringify(value));
return true;
}

delete(key) {
if (!this.has(key)) return false;
SET.call(world, `${this.#identifier}${key}`, undefined);
return true;
}

keys() {
return Array.from(this.#UIDX("keys"));
}

values() {
return Array.from(this.#UIDX("values"));
}

entries() {
return Array.from(this.#UIDX("entries"));
}

#UIDX(type) {
const ids = this.getIds();
let u_idx = 0;
const len = ids.length;

return function* () {
while (u_idx < len) {
const id = ids[u_idx];
const key = id.split(this.#identifier)[1];
const value = this.get(key);
switch (type) {
case "key":
yield key;
break;
case "value":
yield this.has(key) ? JSON.parse(value) : undefined;
break;
case "entries":
yield [key, JSON.parse(value)];
break;
}
u_idx++;
}
}.bind(this)();
}

getIds() {
return world
.getDynamicPropertyIds()
.filter((id) => id.startsWith(this.#identifier));
}

clear() {
for (const id of this.getIds()) {
this.delete(id.replace(this.#identifier,""));
}
}
#identifier;
__cache = {};

/**
* @param {string} id - Unique database identifier.
*/
constructor(id) {
if (typeof id !== 'string' || !id.trim()) {
throw new Error('Invalid database ID');
}
this.#identifier = `${DATABASE_PREFIX}${id}${DATABASE_PREFIX}`;

for (const keyFull of this.getIds()) {
const key = keyFull.replace(this.#identifier, '');
const rawValue = GET.call(world, keyFull);
this.__cache[key] = this.#parseValue(rawValue);
}
}

/**
* Parses stored string values into their appropriate types.
* @param {any} value
* @returns {any}
*/
#parseValue(value) {
if (typeof value === 'string') {
if (value.startsWith('obj')) return JSON.parse(value.slice(3));
if (value === 'null') return null;
if (value === 'true' || value === 'false') return value === 'true';
const num = Number(value);
if (!isNaN(num)) return num;
}
return value;
}

/**
* Converts values into a storable format.
* @param {any} value
* @returns {string}
*/
#stringifyValue(value) {
if (typeof value === 'object' && value !== null) return 'obj' + JSON.stringify(value);
if (typeof value === 'boolean' || value === null) return String(value);
return String(value);
}

/** @returns {number} */
get size() {
return this.keys().length;
}

/** @returns {string[]} */
keys() {
return Object.keys(this.__cache);
}

/** @returns {any[]} */
values() {
return Object.values(this.__cache);
}

/** @returns {[string, any][]} */
entries() {
return Object.entries(this.__cache);
}

/**
* Stores a key-value pair.
* @param {string} key
* @param {any} value
* @returns {boolean}
*/
set(key, value) {
if (typeof key !== 'string' || !key.trim()) throw new Error('Key must be a non-empty string');
const finalValue = this.#stringifyValue(value);
SET.call(world, this.#identifier + key, finalValue);
this.__cache[key] = value;
return true;
}

/**
* Deletes a key.
* @param {string} key
* @returns {boolean}
*/
delete(key) {
if (!this.has(key)) return false;
SET.call(world, this.#identifier + key, undefined);
delete this.__cache[key];
return true;
}

/**
* Retrieves a value.
* @param {string} key
* @returns {any}
*/
get(key) {
if (typeof key !== 'string' || !key.trim()) throw new Error('Key must be a non-empty string');
return this.__cache[key];
}

/**
* Checks if a key exists.
* @param {string} key
* @returns {boolean}
*/
has(key) {
return key in this.__cache;
}

/** @returns {string[]} */
static get ids() {
return [...new Set(
IDS.call(world)
.filter((id) => id.startsWith(DATABASE_PREFIX))
.map((k) => k.slice(DATABASE_PREFIX.length).split(DATABASE_PREFIX)[0])
)];
}

/** @returns {string[]} */
getIds() {
return IDS.call(world).filter((id) => id.startsWith(this.#identifier));
}

/** Clears the database. */
clear() {
for (const key of this.keys()) {
this.delete(key);
}
this.__cache = {};
}

/** Clears all databases globally. */
static clearAll() {
for (const real_id of IDS.call(world).filter((id) => id.startsWith(DATABASE_PREFIX))) {
SET.call(world, real_id, undefined);
}
}
}

export default QuickDB;
Expand Down