Last active
February 7, 2022 01:51
-
-
Save IanSSenne/6c940f2a2224c8ade8aa1a8004876371 to your computer and use it in GitHub Desktop.
a small Persistant storage i wrote for minecrafts gametest 1.18.2 (free for anyone to use with credit)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import * as MC from "mojang-minecraft"; | |
interface IStringSaver { | |
save(value: string): void; | |
load(): string; | |
hasData(): boolean; | |
} | |
class MemorySaver implements IStringSaver { | |
private _data: string; | |
constructor() { | |
this._data = ""; | |
} | |
public save(value: string): void { | |
this._data = value; | |
} | |
public load(): string { | |
return this._data; | |
} | |
public hasData(): boolean { | |
return this._data !== ""; | |
} | |
} | |
class EntityNameSaver implements IStringSaver { | |
private _entities: MC.Entity[] = []; | |
constructor(private position: MC.BlockLocation, private prefix: string = "") { | |
if (this.prefix.includes("|")) { | |
throw new Error("Prefix cannot contain |"); | |
} | |
this.syncEntities(); | |
} | |
private syncEntities() { | |
this._entities = MC.World.getDimension("overworld") | |
.getEntitiesAtBlockLocation(this.position) | |
.filter((entity) => entity.nameTag.startsWith("DB" + this.prefix)); | |
} | |
public save(value: string): void { | |
this.syncEntities(); | |
let chunkSize = 32767 - 8 - this.prefix.length; | |
let chunkCount = Math.ceil(value.length / chunkSize); | |
if (this._entities.length < chunkCount) { | |
for (let i = this._entities.length; i < chunkCount; i++) { | |
let e = MC.World.getDimension("overworld").spawnEntity( | |
"minecraft:armor_stand", | |
this.position | |
); | |
// this is an empty payload, if for some reason one or more of these are not assigned data they will not effect the output | |
e.nameTag = "DB" + this.prefix + "|99999"; | |
this._entities.push(e); | |
} | |
} else if (this._entities.length > chunkCount) { | |
for (let i = this._entities.length - 1; i > chunkCount; i--) { | |
this._entities[i].kill(); | |
} | |
this._entities.splice(chunkCount); | |
} | |
for (let i = 0; i < chunkCount; i++) { | |
let chunk = value.substr(i * chunkSize, chunkSize); | |
this._entities[i].nameTag = | |
"DB" + this.prefix + "|" + i.toString().padStart(5, "0") + chunk; | |
} | |
} | |
public load(): string { | |
this.syncEntities(); | |
let sortedEntities = this._entities.sort( | |
(a, b) => | |
parseInt( | |
a.nameTag.substring(3 + this.prefix.length, 8 + this.prefix.length) | |
) - | |
parseInt( | |
b.nameTag.substring(3 + this.prefix.length, 8 + this.prefix.length) | |
) | |
); | |
let content = sortedEntities | |
.map((ent) => ent.nameTag.substring(8 + this.prefix.length)) | |
.join(""); | |
return content; | |
} | |
public hasData(): boolean { | |
this.syncEntities(); | |
return this._entities.length > 0; | |
} | |
} | |
class DB<T extends IStringSaver> { | |
public static tickQueue: Function[] = []; | |
private value: Record<string, any> | null = null; | |
private lastSaveTime = 0; | |
private hasSaveQueued: boolean; | |
constructor(private saveHandler: T) { | |
this.value = null; | |
this.lastSaveTime = Date.now(); | |
this.hasSaveQueued = false; | |
} | |
public queueSave(): void { | |
if (!this.hasSaveQueued) { | |
const handler = (e) => { | |
// if (this.lastSaveTime < Date.now() - 1000) { | |
this.saveData(); | |
this.lastSaveTime = Date.now(); | |
this.hasSaveQueued = false; | |
DB.tickQueue.splice(DB.tickQueue.indexOf(handler), 1); | |
// } | |
// MC.Commands.run(`say hi`, MC.World.getDimension("overworld")); | |
}; | |
DB.tickQueue.push(handler); | |
this.hasSaveQueued = true; | |
} | |
} | |
private maybeLoadData() { | |
if (this.value !== null) { | |
return; | |
} | |
let data = this.saveHandler.hasData() ? this.saveHandler.load() : "{}"; | |
this.value = JSON.parse(data); | |
} | |
public saveData(): void { | |
this.saveHandler.save(JSON.stringify(this.value)); | |
} | |
public get(key: string): any { | |
this.maybeLoadData(); | |
return this.value[key]; | |
} | |
public set(key: string, value: any): void { | |
this.maybeLoadData(); | |
this.value[key] = value; | |
this.queueSave(); | |
} | |
public has(key: string): boolean { | |
this.maybeLoadData(); | |
return this.value.hasOwnProperty(key); | |
} | |
public delete(key: string): void { | |
this.maybeLoadData(); | |
delete this.value[key]; | |
this.queueSave(); | |
} | |
public clear(): void { | |
this.maybeLoadData(); | |
this.value = {}; | |
this.queueSave(); | |
} | |
public keys(): string[] { | |
this.maybeLoadData(); | |
return Object.keys(this.value); | |
} | |
public size(): number { | |
return this.keys().length; | |
} | |
public values(): any[] { | |
this.maybeLoadData(); | |
return Object.values(this.value); | |
} | |
public entries(): [string, any][] { | |
this.maybeLoadData(); | |
return Object.entries(this.value); | |
} | |
public forEach( | |
callback: (value: any, key: string, map: DB<T>) => void, | |
thisArg?: any | |
): void { | |
this.maybeLoadData(); | |
Object.keys(this.value).forEach((key) => { | |
callback(this.value[key], key, this); | |
}); | |
} | |
debug() { | |
return this.saveHandler.load().length; | |
} | |
} | |
MC.World.events.tick.subscribe(() => { | |
for (const handler of DB.tickQueue) { | |
try { | |
handler(); | |
} catch (e) { | |
console.warn(e.message, e.stack); | |
} | |
} | |
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// same as above but not minified | |
import * as MC from "mojang-minecraft"; | |
class MemorySaver { | |
constructor() { | |
this._data = ""; | |
} | |
save(value) { | |
this._data = value; | |
} | |
load() { | |
return this._data; | |
} | |
hasData() { | |
return this._data !== ""; | |
} | |
} | |
class EntityNameSaver { | |
constructor(position, prefix = "") { | |
this.position = position; | |
this.prefix = prefix; | |
this._entities = []; | |
if (this.prefix.includes("|")) { | |
throw new Error("Prefix cannot contain |"); | |
} | |
this.syncEntities(); | |
} | |
syncEntities() { | |
this._entities = MC.World.getDimension("overworld") | |
.getEntitiesAtBlockLocation(this.position) | |
.filter((entity) => entity.nameTag.startsWith("DB" + this.prefix)); | |
} | |
save(value) { | |
this.syncEntities(); | |
let chunkSize = 32767 - 8 - this.prefix.length; | |
let chunkCount = Math.ceil(value.length / chunkSize); | |
if (this._entities.length < chunkCount) { | |
for (let i = this._entities.length; i < chunkCount; i++) { | |
let e = MC.World.getDimension("overworld").spawnEntity("minecraft:armor_stand", this.position); | |
// this is an empty payload, if for some reason one or more of these are not assigned data they will not effect the output | |
e.nameTag = "DB" + this.prefix + "|99999"; | |
this._entities.push(e); | |
} | |
} | |
else if (this._entities.length > chunkCount) { | |
for (let i = this._entities.length - 1; i > chunkCount; i--) { | |
this._entities[i].kill(); | |
} | |
this._entities.splice(chunkCount); | |
} | |
for (let i = 0; i < chunkCount; i++) { | |
let chunk = value.substr(i * chunkSize, chunkSize); | |
this._entities[i].nameTag = | |
"DB" + this.prefix + "|" + i.toString().padStart(5, "0") + chunk; | |
} | |
} | |
load() { | |
this.syncEntities(); | |
let sortedEntities = this._entities.sort((a, b) => parseInt(a.nameTag.substring(3 + this.prefix.length, 8 + this.prefix.length)) - | |
parseInt(b.nameTag.substring(3 + this.prefix.length, 8 + this.prefix.length))); | |
let content = sortedEntities | |
.map((ent) => ent.nameTag.substring(8 + this.prefix.length)) | |
.join(""); | |
return content; | |
} | |
hasData() { | |
this.syncEntities(); | |
return this._entities.length > 0; | |
} | |
} | |
class DB { | |
constructor(saveHandler) { | |
this.saveHandler = saveHandler; | |
this.value = null; | |
this.lastSaveTime = 0; | |
this.value = null; | |
this.lastSaveTime = Date.now(); | |
this.hasSaveQueued = false; | |
} | |
queueSave() { | |
if (!this.hasSaveQueued) { | |
const handler = (e) => { | |
if (this.lastSaveTime < Date.now() - 1000) { | |
this.saveData(); | |
this.lastSaveTime = Date.now(); | |
this.hasSaveQueued = false; | |
DB.tickQueue.splice(DB.tickQueue.indexOf(handler), 1); | |
} | |
}; | |
DB.tickQueue.push(handler); | |
this.hasSaveQueued = true; | |
} | |
} | |
maybeLoadData() { | |
if (this.value !== null) { | |
return; | |
} | |
let data = this.saveHandler.hasData() ? this.saveHandler.load() : "{}"; | |
this.value = JSON.parse(data); | |
} | |
saveData() { | |
this.saveHandler.save(JSON.stringify(this.value)); | |
} | |
get(key) { | |
this.maybeLoadData(); | |
return this.value[key]; | |
} | |
set(key, value) { | |
this.maybeLoadData(); | |
this.value[key] = value; | |
this.queueSave(); | |
} | |
has(key) { | |
this.maybeLoadData(); | |
return this.value.hasOwnProperty(key); | |
} | |
delete(key) { | |
this.maybeLoadData(); | |
delete this.value[key]; | |
this.queueSave(); | |
} | |
clear() { | |
this.maybeLoadData(); | |
this.value = {}; | |
this.queueSave(); | |
} | |
keys() { | |
this.maybeLoadData(); | |
return Object.keys(this.value); | |
} | |
size() { | |
return this.keys().length; | |
} | |
values() { | |
this.maybeLoadData(); | |
return Object.values(this.value); | |
} | |
entries() { | |
this.maybeLoadData(); | |
return Object.entries(this.value); | |
} | |
forEach(callback, thisArg) { | |
this.maybeLoadData(); | |
Object.keys(this.value).forEach((key) => { | |
callback(this.value[key], key, this); | |
}); | |
} | |
debug() { | |
return this.saveHandler.load().length; | |
} | |
} | |
DB.tickQueue = []; | |
MC.World.events.tick.subscribe(() => { | |
for (const handler of DB.tickQueue) { | |
try { | |
handler(); | |
} | |
catch (e) { | |
console.warn(e.message, e.stack); | |
} | |
} | |
}); | |
export { DB as DataStore, MemorySaver, EntityNameSaver }; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// this is a module you can add to your gametest library that adds the PersistantStorage as DataStore | |
import*as t from"mojang-minecraft";class e{constructor(){this._data=""}save(t){this._data=t}load(){return this._data}hasData(){return""!==this._data}}class s{constructor(t,e=""){if(this.position=t,this.prefix=e,this._entities=[],this.prefix.includes("|"))throw new Error("Prefix cannot contain |");this.syncEntities()}syncEntities(){this._entities=t.World.getDimension("overworld").getEntitiesAtBlockLocation(this.position).filter((t=>t.nameTag.startsWith("DB"+this.prefix)))}save(e){this.syncEntities();let s=32759-this.prefix.length,i=Math.ceil(e.length/s);if(this._entities.length<i)for(let e=this._entities.length;e<i;e++){let e=t.World.getDimension("overworld").spawnEntity("minecraft:armor_stand",this.position);e.nameTag="DB"+this.prefix+"|99999",this._entities.push(e)}else if(this._entities.length>i){for(let t=this._entities.length-1;t>i;t--)this._entities[t].kill();this._entities.splice(i)}for(let t=0;t<i;t++){let i=e.substr(t*s,s);this._entities[t].nameTag="DB"+this.prefix+"|"+t.toString().padStart(5,"0")+i}}load(){return this.syncEntities(),this._entities.sort(((t,e)=>parseInt(t.nameTag.substring(3+this.prefix.length,8+this.prefix.length))-parseInt(e.nameTag.substring(3+this.prefix.length,8+this.prefix.length)))).map((t=>t.nameTag.substring(8+this.prefix.length))).join("")}hasData(){return this.syncEntities(),this._entities.length>0}}class i{constructor(t){this.saveHandler=t,this.value=null,this.lastSaveTime=0,this.value=null,this.lastSaveTime=Date.now(),this.hasSaveQueued=!1}queueSave(){if(!this.hasSaveQueued){const t=e=>{this.lastSaveTime<Date.now()-1e3&&(this.saveData(),this.lastSaveTime=Date.now(),this.hasSaveQueued=!1,i.tickQueue.splice(i.tickQueue.indexOf(t),1))};i.tickQueue.push(t),this.hasSaveQueued=!0}}maybeLoadData(){if(null!==this.value)return;let t=this.saveHandler.hasData()?this.saveHandler.load():"{}";this.value=JSON.parse(t)}saveData(){this.saveHandler.save(JSON.stringify(this.value))}get(t){return this.maybeLoadData(),this.value[t]}set(t,e){this.maybeLoadData(),this.value[t]=e,this.queueSave()}has(t){return this.maybeLoadData(),this.value.hasOwnProperty(t)}delete(t){this.maybeLoadData(),delete this.value[t],this.queueSave()}clear(){this.maybeLoadData(),this.value={},this.queueSave()}keys(){return this.maybeLoadData(),Object.keys(this.value)}size(){return this.keys().length}values(){return this.maybeLoadData(),Object.values(this.value)}entries(){return this.maybeLoadData(),Object.entries(this.value)}forEach(t,e){this.maybeLoadData(),Object.keys(this.value).forEach((e=>{t(this.value[e],e,this)}))}debug(){return this.saveHandler.load().length}}i.tickQueue=[],t.World.events.tick.subscribe((()=>{for(const t of i.tickQueue)try{t()}catch(t){console.warn(t.message,t.stack)}}));export{i as DataStore,e as MemorySaver,s as EntityNameSaver}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const testSaver = new EntityNameSaver( | |
new MC.BlockLocation(0, 200, 0), | |
"TestDB" | |
); | |
const testDB = new DB(testSaver); | |
let deltaTimeArray = []; | |
//tickevent | |
//@ts-ignore | |
MC.World.events.tick.subscribe(({ currentTick, deltaTime }) => { | |
for (let i = 0; i < 10000; i++) { | |
testDB.set("tick" + i, Date.now()); | |
} | |
// @ts-ignore | |
deltaTimeArray.unshift(deltaTime); | |
if (deltaTimeArray.length > 250) { | |
deltaTimeArray.pop(); | |
} | |
console.warn( | |
"TPS" + | |
(1 / | |
(deltaTimeArray.reduce( | |
(previousValue, currentValue) => previousValue + currentValue | |
) / | |
deltaTimeArray.length) + | |
"," + | |
deltaTimeArray.length) | |
); | |
}); | |
MC.World.events.beforeChat.subscribe(({ message }) => { | |
try { | |
const [cmd, target, ...payloadarr] = message.split(" "); | |
const payload = payloadarr.join(" "); | |
if (Reflect.has(testDB, cmd)) { | |
let res = testDB[cmd](target, payload); | |
MC.Commands.run( | |
`say testDB.${cmd}("${target}"): ${res}`, | |
MC.World.getDimension("overworld") | |
); | |
} | |
} catch (e) { | |
MC.Commands.run(`say ${e.message}`, MC.World.getDimension("overworld")); | |
e.stack.split("\n").forEach((element) => { | |
MC.Commands.run(`say ${element}`, MC.World.getDimension("overworld")); | |
}); | |
} | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment