UNPKG

superpowers-game-ftext-plugin

Version:

Generic text asset for the Superpowers Game system of Superpowers, the extensible HTML5 2D+3D game engine.

132 lines (103 loc) 4.01 kB
import * as OT from "operational-transform"; import * as fs from "fs"; import * as path from "path"; import fTextSettingsResource from "./fTextSettingsResource"; export default class fTextAsset extends SupCore.Data.Base.Asset { static schema: SupCore.Data.Schema = { text: { type: "string" }, draft: { type: "string" }, revisionId: { type: "integer" }, }; pub: { text: string; draft: string; revisionId: number; } document: OT.Document; hasDraft: boolean; // called from the editor onAssetReceived() as well as on server startup constructor(id: string, pub: any, serverData?: any) { super(id, pub, fTextAsset.schema, serverData); } // called on asset creation // options contain the asset's name init(options: any, callback: Function) { let defaultContent = ""; this.pub = { text: defaultContent, draft: defaultContent, revisionId: 0, } super.init(options, callback); } setup() { this.document = new OT.Document(this.pub.draft, this.pub.revisionId); this.hasDraft = this.pub.text !== this.pub.draft; } restore() { if (this.hasDraft) this.emit("setBadge", "draft", "info"); } destroy(callback: Function) { callback(); } // called on server startup load(assetPath: string) { // NOTE: We must not set this.pub with temporary values here, otherwise // the asset will be considered loaded by Dictionary.acquire // and the acquire callback will be called immediately fs.readFile(path.join(assetPath, "ftext.txt"), { encoding: "utf8" }, (err, text) => { fs.readFile(path.join(assetPath, "draft.txt"), { encoding: "utf8" }, (err, draft) => { this.pub = { revisionId: 0, text, draft: (draft != null) ? draft : text }; this.setup(); this.emit("load"); }); }); } // called when it is time to write the asset on disk, not when the user save the asset from the editor save(assetPath: string, callback: (err: Error) => any) { fs.writeFile(path.join(assetPath, "ftext.txt"), this.pub.text, { encoding: "utf8" }, (err) => { if (err != null) { callback(err); return; } if (this.hasDraft) { fs.writeFile(path.join(assetPath, "draft.txt"), this.pub.draft, { encoding: "utf8" }, callback); } else { // delete the draft.txt file if there is no draft to save and the file exists fs.unlink(path.join(assetPath, "draft.txt"), (err) => { if (err != null && err.code !== "ENOENT") { callback(err); return; } callback(null); }); } }); } server_editText(client: any, operationData: OperationData, revisionIndex: number, callback: (err: string, operationData?: any, revisionIndex?: number) => any) { if (operationData.userId !== client.id) { callback("Invalid client id"); return; } let operation = new OT.TextOperation(); if (! operation.deserialize(operationData)) { callback("Invalid operation data"); return; } try { operation = this.document.apply(operation, revisionIndex); } catch (err) { callback("Operation can't be applied"); return; } this.pub.draft = this.document.text; this.pub.revisionId++; callback(null, operation.serialize(), this.document.getRevisionId() - 1); if (!this.hasDraft) { this.hasDraft = true; this.emit("setBadge", "draft", "info"); } this.emit("change"); } client_editText(operationData: OperationData, revisionIndex: number) { let operation = new OT.TextOperation(); operation.deserialize(operationData); this.document.apply(operation, revisionIndex); this.pub.draft = this.document.text; this.pub.revisionId++; } server_saveText(client: any, callback: (err: string) => any) { this.pub.text = this.pub.draft; callback(null); if (this.hasDraft) { this.hasDraft = false; this.emit("clearBadge", "draft"); } this.emit("change"); } client_saveText() { this.pub.text = this.pub.draft; } }