UNPKG

@hastearcade/snowglobe

Version:

A TypeScript port of CrystalOrb, a high-level Rust game networking library

1,385 lines (1,360 loc) 76.7 kB
"use strict"; var __create = Object.create; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __getProtoOf = Object.getPrototypeOf; var __hasOwnProp = Object.prototype.hasOwnProperty; var __commonJS = (cb, mod) => function __require() { return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports; }; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( // If the importer is in node compatibility mode or this is not an ESM // file that has been converted to a CommonJS file using a Babel- // compatible transform (i.e. "__esModule" has not been set), then set // "default" to the CommonJS "module.exports" for node compatibility. isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, mod )); var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); // node_modules/csv-writer/dist/lib/csv-stringifiers/abstract.js var require_abstract = __commonJS({ "node_modules/csv-writer/dist/lib/csv-stringifiers/abstract.js"(exports) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); var DEFAULT_RECORD_DELIMITER = "\n"; var VALID_RECORD_DELIMITERS = [DEFAULT_RECORD_DELIMITER, "\r\n"]; var CsvStringifier = ( /** @class */ function() { function CsvStringifier2(fieldStringifier, recordDelimiter) { if (recordDelimiter === void 0) { recordDelimiter = DEFAULT_RECORD_DELIMITER; } this.fieldStringifier = fieldStringifier; this.recordDelimiter = recordDelimiter; _validateRecordDelimiter(recordDelimiter); } CsvStringifier2.prototype.getHeaderString = function() { var headerRecord = this.getHeaderRecord(); return headerRecord ? this.joinRecords([this.getCsvLine(headerRecord)]) : null; }; CsvStringifier2.prototype.stringifyRecords = function(records) { var _this = this; var csvLines = Array.from(records, function(record) { return _this.getCsvLine(_this.getRecordAsArray(record)); }); return this.joinRecords(csvLines); }; CsvStringifier2.prototype.getCsvLine = function(record) { var _this = this; return record.map(function(fieldValue) { return _this.fieldStringifier.stringify(fieldValue); }).join(this.fieldStringifier.fieldDelimiter); }; CsvStringifier2.prototype.joinRecords = function(records) { return records.join(this.recordDelimiter) + this.recordDelimiter; }; return CsvStringifier2; }() ); exports.CsvStringifier = CsvStringifier; function _validateRecordDelimiter(delimiter) { if (VALID_RECORD_DELIMITERS.indexOf(delimiter) === -1) { throw new Error("Invalid record delimiter `" + delimiter + "` is specified"); } } } }); // node_modules/csv-writer/dist/lib/csv-stringifiers/array.js var require_array = __commonJS({ "node_modules/csv-writer/dist/lib/csv-stringifiers/array.js"(exports) { "use strict"; var __extends = exports && exports.__extends || function() { var extendStatics = function(d, b) { extendStatics = Object.setPrototypeOf || { __proto__: [] } instanceof Array && function(d2, b2) { d2.__proto__ = b2; } || function(d2, b2) { for (var p in b2) if (b2.hasOwnProperty(p)) d2[p] = b2[p]; }; return extendStatics(d, b); }; return function(d, b) { extendStatics(d, b); function __() { this.constructor = d; } d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); }; }(); Object.defineProperty(exports, "__esModule", { value: true }); var abstract_1 = require_abstract(); var ArrayCsvStringifier = ( /** @class */ function(_super) { __extends(ArrayCsvStringifier2, _super); function ArrayCsvStringifier2(fieldStringifier, recordDelimiter, header) { var _this = _super.call(this, fieldStringifier, recordDelimiter) || this; _this.header = header; return _this; } ArrayCsvStringifier2.prototype.getHeaderRecord = function() { return this.header; }; ArrayCsvStringifier2.prototype.getRecordAsArray = function(record) { return record; }; return ArrayCsvStringifier2; }(abstract_1.CsvStringifier) ); exports.ArrayCsvStringifier = ArrayCsvStringifier; } }); // node_modules/csv-writer/dist/lib/field-stringifier.js var require_field_stringifier = __commonJS({ "node_modules/csv-writer/dist/lib/field-stringifier.js"(exports) { "use strict"; var __extends = exports && exports.__extends || function() { var extendStatics = function(d, b) { extendStatics = Object.setPrototypeOf || { __proto__: [] } instanceof Array && function(d2, b2) { d2.__proto__ = b2; } || function(d2, b2) { for (var p in b2) if (b2.hasOwnProperty(p)) d2[p] = b2[p]; }; return extendStatics(d, b); }; return function(d, b) { extendStatics(d, b); function __() { this.constructor = d; } d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); }; }(); Object.defineProperty(exports, "__esModule", { value: true }); var DEFAULT_FIELD_DELIMITER = ","; var VALID_FIELD_DELIMITERS = [DEFAULT_FIELD_DELIMITER, ";"]; var FieldStringifier = ( /** @class */ function() { function FieldStringifier2(fieldDelimiter) { this.fieldDelimiter = fieldDelimiter; } FieldStringifier2.prototype.isEmpty = function(value) { return typeof value === "undefined" || value === null || value === ""; }; FieldStringifier2.prototype.quoteField = function(field) { return '"' + field.replace(/"/g, '""') + '"'; }; return FieldStringifier2; }() ); exports.FieldStringifier = FieldStringifier; var DefaultFieldStringifier = ( /** @class */ function(_super) { __extends(DefaultFieldStringifier2, _super); function DefaultFieldStringifier2() { return _super !== null && _super.apply(this, arguments) || this; } DefaultFieldStringifier2.prototype.stringify = function(value) { if (this.isEmpty(value)) return ""; var str = String(value); return this.needsQuote(str) ? this.quoteField(str) : str; }; DefaultFieldStringifier2.prototype.needsQuote = function(str) { return str.includes(this.fieldDelimiter) || str.includes("\n") || str.includes('"'); }; return DefaultFieldStringifier2; }(FieldStringifier) ); var ForceQuoteFieldStringifier = ( /** @class */ function(_super) { __extends(ForceQuoteFieldStringifier2, _super); function ForceQuoteFieldStringifier2() { return _super !== null && _super.apply(this, arguments) || this; } ForceQuoteFieldStringifier2.prototype.stringify = function(value) { return this.isEmpty(value) ? "" : this.quoteField(String(value)); }; return ForceQuoteFieldStringifier2; }(FieldStringifier) ); function createFieldStringifier(fieldDelimiter, alwaysQuote) { if (fieldDelimiter === void 0) { fieldDelimiter = DEFAULT_FIELD_DELIMITER; } if (alwaysQuote === void 0) { alwaysQuote = false; } _validateFieldDelimiter(fieldDelimiter); return alwaysQuote ? new ForceQuoteFieldStringifier(fieldDelimiter) : new DefaultFieldStringifier(fieldDelimiter); } exports.createFieldStringifier = createFieldStringifier; function _validateFieldDelimiter(delimiter) { if (VALID_FIELD_DELIMITERS.indexOf(delimiter) === -1) { throw new Error("Invalid field delimiter `" + delimiter + "` is specified"); } } } }); // node_modules/csv-writer/dist/lib/lang/object.js var require_object = __commonJS({ "node_modules/csv-writer/dist/lib/lang/object.js"(exports) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.isObject = function(value) { return Object.prototype.toString.call(value) === "[object Object]"; }; } }); // node_modules/csv-writer/dist/lib/csv-stringifiers/object.js var require_object2 = __commonJS({ "node_modules/csv-writer/dist/lib/csv-stringifiers/object.js"(exports) { "use strict"; var __extends = exports && exports.__extends || function() { var extendStatics = function(d, b) { extendStatics = Object.setPrototypeOf || { __proto__: [] } instanceof Array && function(d2, b2) { d2.__proto__ = b2; } || function(d2, b2) { for (var p in b2) if (b2.hasOwnProperty(p)) d2[p] = b2[p]; }; return extendStatics(d, b); }; return function(d, b) { extendStatics(d, b); function __() { this.constructor = d; } d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); }; }(); Object.defineProperty(exports, "__esModule", { value: true }); var abstract_1 = require_abstract(); var object_1 = require_object(); var ObjectCsvStringifier = ( /** @class */ function(_super) { __extends(ObjectCsvStringifier2, _super); function ObjectCsvStringifier2(fieldStringifier, header, recordDelimiter, headerIdDelimiter) { var _this = _super.call(this, fieldStringifier, recordDelimiter) || this; _this.header = header; _this.headerIdDelimiter = headerIdDelimiter; return _this; } ObjectCsvStringifier2.prototype.getHeaderRecord = function() { if (!this.isObjectHeader) return null; return this.header.map(function(field) { return field.title; }); }; ObjectCsvStringifier2.prototype.getRecordAsArray = function(record) { var _this = this; return this.fieldIds.map(function(fieldId) { return _this.getNestedValue(record, fieldId); }); }; ObjectCsvStringifier2.prototype.getNestedValue = function(obj, key) { if (!this.headerIdDelimiter) return obj[key]; return key.split(this.headerIdDelimiter).reduce(function(subObj, keyPart) { return (subObj || {})[keyPart]; }, obj); }; Object.defineProperty(ObjectCsvStringifier2.prototype, "fieldIds", { get: function() { return this.isObjectHeader ? this.header.map(function(column) { return column.id; }) : this.header; }, enumerable: true, configurable: true }); Object.defineProperty(ObjectCsvStringifier2.prototype, "isObjectHeader", { get: function() { return object_1.isObject(this.header && this.header[0]); }, enumerable: true, configurable: true }); return ObjectCsvStringifier2; }(abstract_1.CsvStringifier) ); exports.ObjectCsvStringifier = ObjectCsvStringifier; } }); // node_modules/csv-writer/dist/lib/csv-stringifier-factory.js var require_csv_stringifier_factory = __commonJS({ "node_modules/csv-writer/dist/lib/csv-stringifier-factory.js"(exports) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); var array_1 = require_array(); var field_stringifier_1 = require_field_stringifier(); var object_1 = require_object2(); var CsvStringifierFactory = ( /** @class */ function() { function CsvStringifierFactory2() { } CsvStringifierFactory2.prototype.createArrayCsvStringifier = function(params) { var fieldStringifier = field_stringifier_1.createFieldStringifier(params.fieldDelimiter, params.alwaysQuote); return new array_1.ArrayCsvStringifier(fieldStringifier, params.recordDelimiter, params.header); }; CsvStringifierFactory2.prototype.createObjectCsvStringifier = function(params) { var fieldStringifier = field_stringifier_1.createFieldStringifier(params.fieldDelimiter, params.alwaysQuote); return new object_1.ObjectCsvStringifier(fieldStringifier, params.header, params.recordDelimiter, params.headerIdDelimiter); }; return CsvStringifierFactory2; }() ); exports.CsvStringifierFactory = CsvStringifierFactory; } }); // node_modules/csv-writer/dist/lib/lang/promise.js var require_promise = __commonJS({ "node_modules/csv-writer/dist/lib/lang/promise.js"(exports) { "use strict"; var __spreadArrays = exports && exports.__spreadArrays || function() { for (var s = 0, i = 0, il = arguments.length; i < il; i++) s += arguments[i].length; for (var r = Array(s), k = 0, i = 0; i < il; i++) for (var a = arguments[i], j = 0, jl = a.length; j < jl; j++, k++) r[k] = a[j]; return r; }; Object.defineProperty(exports, "__esModule", { value: true }); function promisify(fn) { return function() { var args = []; for (var _i = 0; _i < arguments.length; _i++) { args[_i] = arguments[_i]; } return new Promise(function(resolve, reject) { var nodeCallback = function(err, result) { if (err) reject(err); else resolve(result); }; fn.apply(null, __spreadArrays(args, [nodeCallback])); }); }; } exports.promisify = promisify; } }); // node_modules/csv-writer/dist/lib/file-writer.js var require_file_writer = __commonJS({ "node_modules/csv-writer/dist/lib/file-writer.js"(exports) { "use strict"; var __awaiter = exports && exports.__awaiter || function(thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function(resolve) { resolve(value); }); } return new (P || (P = Promise))(function(resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; var __generator = exports && exports.__generator || function(thisArg, body) { var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; function verb(n) { return function(v) { return step([n, v]); }; } function step(op) { if (f) throw new TypeError("Generator is already executing."); while (_) try { if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; if (y = 0, t) op = [op[0] & 2, t.value]; switch (op[0]) { case 0: case 1: t = op; break; case 4: _.label++; return { value: op[1], done: false }; case 5: _.label++; y = op[1]; op = [0]; continue; case 7: op = _.ops.pop(); _.trys.pop(); continue; default: if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } if (op[0] === 3 && (!t || op[1] > t[0] && op[1] < t[3])) { _.label = op[1]; break; } if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } if (t[2]) _.ops.pop(); _.trys.pop(); continue; } op = body.call(thisArg, _); } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; } }; Object.defineProperty(exports, "__esModule", { value: true }); var promise_1 = require_promise(); var fs_1 = require("fs"); var writeFilePromise = promise_1.promisify(fs_1.writeFile); var DEFAULT_ENCODING = "utf8"; var FileWriter = ( /** @class */ function() { function FileWriter2(path, append, encoding) { if (encoding === void 0) { encoding = DEFAULT_ENCODING; } this.path = path; this.append = append; this.encoding = encoding; } FileWriter2.prototype.write = function(string) { return __awaiter(this, void 0, void 0, function() { return __generator(this, function(_a) { switch (_a.label) { case 0: return [4, writeFilePromise(this.path, string, this.getWriteOption())]; case 1: _a.sent(); this.append = true; return [ 2 /*return*/ ]; } }); }); }; FileWriter2.prototype.getWriteOption = function() { return { encoding: this.encoding, flag: this.append ? "a" : "w" }; }; return FileWriter2; }() ); exports.FileWriter = FileWriter; } }); // node_modules/csv-writer/dist/lib/csv-writer.js var require_csv_writer = __commonJS({ "node_modules/csv-writer/dist/lib/csv-writer.js"(exports) { "use strict"; var __awaiter = exports && exports.__awaiter || function(thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function(resolve) { resolve(value); }); } return new (P || (P = Promise))(function(resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; var __generator = exports && exports.__generator || function(thisArg, body) { var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; function verb(n) { return function(v) { return step([n, v]); }; } function step(op) { if (f) throw new TypeError("Generator is already executing."); while (_) try { if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; if (y = 0, t) op = [op[0] & 2, t.value]; switch (op[0]) { case 0: case 1: t = op; break; case 4: _.label++; return { value: op[1], done: false }; case 5: _.label++; y = op[1]; op = [0]; continue; case 7: op = _.ops.pop(); _.trys.pop(); continue; default: if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } if (op[0] === 3 && (!t || op[1] > t[0] && op[1] < t[3])) { _.label = op[1]; break; } if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } if (t[2]) _.ops.pop(); _.trys.pop(); continue; } op = body.call(thisArg, _); } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; } }; Object.defineProperty(exports, "__esModule", { value: true }); var file_writer_1 = require_file_writer(); var DEFAULT_INITIAL_APPEND_FLAG = false; var CsvWriter = ( /** @class */ function() { function CsvWriter2(csvStringifier, path, encoding, append) { if (append === void 0) { append = DEFAULT_INITIAL_APPEND_FLAG; } this.csvStringifier = csvStringifier; this.append = append; this.fileWriter = new file_writer_1.FileWriter(path, this.append, encoding); } CsvWriter2.prototype.writeRecords = function(records) { return __awaiter(this, void 0, void 0, function() { var recordsString, writeString; return __generator(this, function(_a) { switch (_a.label) { case 0: recordsString = this.csvStringifier.stringifyRecords(records); writeString = this.headerString + recordsString; return [4, this.fileWriter.write(writeString)]; case 1: _a.sent(); this.append = true; return [ 2 /*return*/ ]; } }); }); }; Object.defineProperty(CsvWriter2.prototype, "headerString", { get: function() { var headerString = !this.append && this.csvStringifier.getHeaderString(); return headerString || ""; }, enumerable: true, configurable: true }); return CsvWriter2; }() ); exports.CsvWriter = CsvWriter; } }); // node_modules/csv-writer/dist/lib/csv-writer-factory.js var require_csv_writer_factory = __commonJS({ "node_modules/csv-writer/dist/lib/csv-writer-factory.js"(exports) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); var csv_writer_1 = require_csv_writer(); var CsvWriterFactory = ( /** @class */ function() { function CsvWriterFactory2(csvStringifierFactory) { this.csvStringifierFactory = csvStringifierFactory; } CsvWriterFactory2.prototype.createArrayCsvWriter = function(params) { var csvStringifier = this.csvStringifierFactory.createArrayCsvStringifier({ header: params.header, fieldDelimiter: params.fieldDelimiter, recordDelimiter: params.recordDelimiter, alwaysQuote: params.alwaysQuote }); return new csv_writer_1.CsvWriter(csvStringifier, params.path, params.encoding, params.append); }; CsvWriterFactory2.prototype.createObjectCsvWriter = function(params) { var csvStringifier = this.csvStringifierFactory.createObjectCsvStringifier({ header: params.header, fieldDelimiter: params.fieldDelimiter, recordDelimiter: params.recordDelimiter, headerIdDelimiter: params.headerIdDelimiter, alwaysQuote: params.alwaysQuote }); return new csv_writer_1.CsvWriter(csvStringifier, params.path, params.encoding, params.append); }; return CsvWriterFactory2; }() ); exports.CsvWriterFactory = CsvWriterFactory; } }); // node_modules/csv-writer/dist/index.js var require_dist = __commonJS({ "node_modules/csv-writer/dist/index.js"(exports) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); var csv_stringifier_factory_1 = require_csv_stringifier_factory(); var csv_writer_factory_1 = require_csv_writer_factory(); var csvStringifierFactory = new csv_stringifier_factory_1.CsvStringifierFactory(); var csvWriterFactory = new csv_writer_factory_1.CsvWriterFactory(csvStringifierFactory); exports.createArrayCsvStringifier = function(params) { return csvStringifierFactory.createArrayCsvStringifier(params); }; exports.createObjectCsvStringifier = function(params) { return csvStringifierFactory.createObjectCsvStringifier(params); }; exports.createArrayCsvWriter = function(params) { return csvWriterFactory.createArrayCsvWriter(params); }; exports.createObjectCsvWriter = function(params) { return csvWriterFactory.createObjectCsvWriter(params); }; } }); // lib/dist/esm/index.js var esm_exports = {}; __export(esm_exports, { CLOCK_SYNC_MESSAGE_TYPE_ID: () => CLOCK_SYNC_MESSAGE_TYPE_ID, COMMAND_MESSAGE_TYPE_ID: () => COMMAND_MESSAGE_TYPE_ID, Client: () => Client, SNAPSHOT_MESSAGE_TYPE_ID: () => SNAPSHOT_MESSAGE_TYPE_ID, Server: () => Server, StageState: () => StageState, TweeningMethod: () => TweeningMethod, getTimestamp: () => get, makeConfig: () => makeConfig, setTimestamp: () => set }); module.exports = __toCommonJS(esm_exports); // lib/dist/esm/analytics.js var csv = __toESM(require_dist(), 1); var AnalyticType; (function(AnalyticType2) { AnalyticType2[AnalyticType2["currentworld"] = 0] = "currentworld"; AnalyticType2[AnalyticType2["recvcommand"] = 1] = "recvcommand"; AnalyticType2[AnalyticType2["issuecommand"] = 2] = "issuecommand"; AnalyticType2[AnalyticType2["snapshotapplied"] = 3] = "snapshotapplied"; AnalyticType2[AnalyticType2["snapshotgenerated"] = 4] = "snapshotgenerated"; AnalyticType2[AnalyticType2["worldhistory"] = 5] = "worldhistory"; })(AnalyticType || (AnalyticType = {})); var Analytics = class { id; data; // tick number, map of analytic to data value // we need to store for every tick // a list of commands to process // any snapshots generated // any snapshots applied // world history at that tick // current world at that tick // Data structure // { tick0: {commands: string, snapshotsapplied: string, snapshotsgenerated: string, worldhistory: string, currentworld: string }} constructor(id) { this.id = id; this.data = /* @__PURE__ */ new Map(); } store(tickNumber, type, data) { if (!this.data.has(tickNumber)) { this.data.set(tickNumber, ["", "", "", "", ""]); } const dataStore = this.data.get(tickNumber); dataStore[type] = data; } async flush() { const csvWriter = csv.createArrayCsvWriter({ path: `${this.id}-${Date.now()}.csv`, header: [ "tick", "currentworld", "recvcommand", "issuecommand", "snapshotapplied", "snapshotgenerated", "worldhistory" ] }); const data = []; this.data.forEach((val, key) => { data.push([key.toString()].concat(val)); }); await csvWriter.writeRecords(data); } }; // lib/dist/esm/lib.js function makeConfig(config = {}) { return Object.assign({ serverTimeDelayLatency: 0.3, blendLatency: 0.1, timestepSeconds: 1 / 60, clockSyncNeededSampleCount: 8, clockSyncRequestPeriod: 0.2, clockSyncAssumedOutlierRate: 0.2, maxTolerableClockDeviation: 0.1, snapshotSendPeriod: 0.1, updateDeltaSecondsMax: 0.25, timestampSkipThresholdSeconds: 1, fastForwardMaxPerStep: 10, tweeningMethod: TweeningMethod.Interpolated, serverCommandHistoryFrameBufferSize: 60, lagCompensateCommands: false // this variable is used to delay commands for "other" players in the system so they see everything in the past }, config); } var TweeningMethod; (function(TweeningMethod2) { TweeningMethod2[TweeningMethod2["MostRecentlyPassed"] = 0] = "MostRecentlyPassed"; TweeningMethod2[TweeningMethod2["Nearest"] = 1] = "Nearest"; TweeningMethod2[TweeningMethod2["Interpolated"] = 2] = "Interpolated"; })(TweeningMethod || (TweeningMethod = {})); function shapeInterpolationT(method, t) { switch (method) { case TweeningMethod.MostRecentlyPassed: return Math.floor(t); case TweeningMethod.Nearest: return Math.round(t); case TweeningMethod.Interpolated: return t; } } function serverTimeDelayFrameCount(config) { return Math.round(config.serverTimeDelayLatency / (1 / 60)); } function clockSyncSamplesToDiscardPerExtreme(config) { return Math.ceil(Math.max(config.clockSyncNeededSampleCount * config.clockSyncAssumedOutlierRate / 2, 1)); } function clockSyncSamplesNeededToStore(config) { return config.clockSyncNeededSampleCount + clockSyncSamplesToDiscardPerExtreme(config) * 2; } function blendProgressPerFrame(config) { return config.timestepSeconds / config.blendLatency; } // lib/dist/esm/message.js var nextTypeId = 0; function makeTypeId() { return nextTypeId++; } var CLOCK_SYNC_MESSAGE_TYPE_ID = makeTypeId(); var COMMAND_MESSAGE_TYPE_ID = makeTypeId(); var SNAPSHOT_MESSAGE_TYPE_ID = makeTypeId(); var NetworkMessageType; (function(NetworkMessageType2) { NetworkMessageType2[NetworkMessageType2["ClockSyncMessage"] = CLOCK_SYNC_MESSAGE_TYPE_ID] = "ClockSyncMessage"; NetworkMessageType2[NetworkMessageType2["CommandMessage"] = COMMAND_MESSAGE_TYPE_ID] = "CommandMessage"; NetworkMessageType2[NetworkMessageType2["SnapshotMessage"] = SNAPSHOT_MESSAGE_TYPE_ID] = "SnapshotMessage"; })(NetworkMessageType || (NetworkMessageType = {})); // lib/dist/esm/clock_sync.js var ClockSyncer = class { _config; _serverSecondsOffset; _serverSecondsOffsetSamples = []; _secondsSinceLastRequestSent = 0; _clientId; _pingArr; _latestPing; constructor(_config) { this._config = _config; this._pingArr = []; this._latestPing = 30; } update(deltaSeconds, secondsSinceStartup, net) { var _a; this._secondsSinceLastRequestSent += deltaSeconds; if (this._secondsSinceLastRequestSent > this._config.clockSyncRequestPeriod) { this._secondsSinceLastRequestSent = 0; net.broadcastMessage(CLOCK_SYNC_MESSAGE_TYPE_ID, { clientPing: this._latestPing, clientSendSecondsSinceStartup: secondsSinceStartup, serverSecondsSinceStartup: 0, clientId: 0 }); } let latestServerSecondsOffset; for (const [, connection] of net.connections()) { let sync; while ((sync = connection.recvClockSync()) != null) { const { clientId, clientSendSecondsSinceStartup, serverSecondsSinceStartup } = sync; const receivedTime = secondsSinceStartup; const ping = Math.round(Math.max(0, (receivedTime - clientSendSecondsSinceStartup) / 2 * 1e3)); this._pingArr.push(ping); if (this._pingArr.length > 10) { const averagePingForLastTwenty = this._pingArr.map((p) => p).reduce((p, c) => p += c) / 10; connection.setPing(Math.ceil(averagePingForLastTwenty)); this._latestPing = Math.ceil(averagePingForLastTwenty); this._pingArr = []; } const correspondingClientTime = (clientSendSecondsSinceStartup + receivedTime) / 2; const offset = serverSecondsSinceStartup - correspondingClientTime; latestServerSecondsOffset = offset; const existingId = (_a = this._clientId) != null ? _a : this._clientId = clientId; console.assert(existingId === clientId, "The clock sync client ids should be the same"); } } if (latestServerSecondsOffset !== void 0) { this.addSample(latestServerSecondsOffset); } } isReady() { return this._serverSecondsOffset !== void 0 && this._clientId !== void 0; } sampleCount() { return this._serverSecondsOffsetSamples.length; } samplesNeeded() { return clockSyncSamplesNeededToStore(this._config); } clientId() { return this._clientId; } serverSecondsOffset() { return this._serverSecondsOffset; } serverSecondsSinceStartup(clientSecondsSinceStartup) { return this._serverSecondsOffset !== void 0 ? this._serverSecondsOffset + clientSecondsSinceStartup : this._serverSecondsOffset; } addSample(measuredSecondsOffset) { this._serverSecondsOffsetSamples.unshift(measuredSecondsOffset); console.assert(this._serverSecondsOffsetSamples.length <= clockSyncSamplesNeededToStore(this._config), "You offset samples are too high"); if (this._serverSecondsOffsetSamples.length >= clockSyncSamplesNeededToStore(this._config)) { const rollingMeanOffsetSeconds = this.rollingMeanOffsetSeconds(); const isInitialSync = this._serverSecondsOffset === void 0; const hasDesynced = this._serverSecondsOffset !== void 0 ? Math.abs(rollingMeanOffsetSeconds - this._serverSecondsOffset) > this._config.maxTolerableClockDeviation : false; if (isInitialSync || hasDesynced) { this._serverSecondsOffset = rollingMeanOffsetSeconds; } this._serverSecondsOffsetSamples.pop(); } } rollingMeanOffsetSeconds() { const samples = this._serverSecondsOffsetSamples.slice(); samples.sort((a, b) => a - b); const samplesWithoutOutliers = samples.slice(clockSyncSamplesToDiscardPerExtreme(this._config), samples.length - clockSyncSamplesToDiscardPerExtreme(this._config)); return samplesWithoutOutliers.reduce((sum, sample) => sum + sample, 0) / samplesWithoutOutliers.length; } }; // lib/dist/esm/math.js function remEuclid(lhs, rhs) { const r = lhs % rhs; return r < 0 ? r + Math.abs(rhs) : r; } function clamp(value, min, max) { return Math.min(Math.max(value, min), max); } // lib/dist/esm/timestamp.js var MAX = 32767; var i16 = new Int16Array(1); function make(value = 0) { i16[0] = value; return i16[0]; } function fromSeconds(seconds, timestampSeconds) { return make(makeFromSecondsFloat(seconds, timestampSeconds)); } function comparableRangeWithMidpoint(timestamp) { const maxDistanceFromMidpoint = MAX / 2; return { min: make(timestamp - maxDistanceFromMidpoint), max: make(timestamp + maxDistanceFromMidpoint) }; } function acceptableTimestampRange(baseline, timestamp) { const { min, max } = comparableRangeWithMidpoint(baseline); return cmp(timestamp, min) >= 0 && cmp(timestamp, max) < 0; } function asSeconds(timestamp, timestampSeconds) { return timestamp * timestampSeconds; } function add(timestamp, rhs) { return make(timestamp + rhs); } function sub(timestamp, rhs) { return make(timestamp - rhs); } function cmp(timestamp1, timestamp2) { const difference = sub(timestamp1, timestamp2); if (difference < 0) { return -1; } if (difference === 0) { return 0; } return 1; } function toFloat(timestamp) { return +timestamp; } function makeFromUnwrappedFloat(frames) { return remEuclid(frames + Math.pow(2, 15), Math.pow(2, 16)) - Math.pow(2, 15); } function makeFromSecondsFloat(seconds, timestampSeconds) { return makeFromUnwrappedFloat(seconds / timestampSeconds); } function ceil(timestamp) { return make(Math.ceil(timestamp)); } function floor(timestamp) { return make(Math.floor(timestamp)); } function subFloat(timestamp, rhs) { return makeFromUnwrappedFloat(timestamp - rhs); } function set(timestamped, timestamp) { ; timestamped.timestamp = timestamp; return timestamped; } function get(timestamped) { return timestamped.timestamp; } // lib/dist/esm/command.js var CommandBuffer = class { map; _timestamp; constructor(map = /* @__PURE__ */ new Map(), _timestamp = make()) { this.map = map; this._timestamp = _timestamp; } timestamp() { return this._timestamp; } filterStaleTimestamps(timestamp, before) { if (timestamp != null) { this.map.forEach((value, key) => { if (cmp(key, timestamp) === (before ? -1 : 1)) { this.map.delete(key); } }); } } updateTimestamp(timestamp) { this._timestamp = timestamp; const acceptableRange = comparableRangeWithMidpoint(this._timestamp); this.filterStaleTimestamps(acceptableRange.min, true); this.filterStaleTimestamps(acceptableRange.max, false); } drainAll() { const sortedCommands = [...this.map.entries()].sort((a, b) => cmp(a[0], b[0])); this.map.clear(); return sortedCommands.map((tc) => tc[1]).flat(); } drainUpTo(timestamp) { const sortedCommands = [...this.map.entries()].sort((a, b) => cmp(a[0], b[0])); const filteredCommands = sortedCommands.filter((tc) => cmp(tc[0], timestamp) <= 0); for (const [timestamp2] of filteredCommands) { this.map.delete(timestamp2); } return filteredCommands.map((tc) => tc[1]).flat(); } insert(timestampedCommand) { const incomingTimestamp = get(timestampedCommand); if (acceptableTimestampRange(this._timestamp, incomingTimestamp)) { const commandsExist = this.map.get(incomingTimestamp); if (commandsExist == null) { this.map.set(incomingTimestamp, [timestampedCommand]); } else { this.map.set(incomingTimestamp, [...commandsExist, timestampedCommand]); } } else { console.warn("The command's timestamp is outside the acceptable range and will be ignored"); } } commandsAt(timestamp) { return this.map.get(timestamp); } length() { return this.map.size; } [Symbol.iterator]() { return this.map.entries(); } clone() { return new CommandBuffer(new Map(Array.from(this.map.entries()).map(([timestamp, commands]) => [ timestamp, commands.map((command) => set(command.clone(), timestamp)) ])), make(this._timestamp)); } }; // lib/dist/esm/display_state.js var Tweened = class { _displayState; timestamp; constructor(_displayState, timestamp) { this._displayState = _displayState; this.timestamp = timestamp; } displayState() { return this._displayState; } floatTimestamp() { return this.timestamp; } clone() { return new Tweened(this._displayState.clone(), this.timestamp); } }; function timestampedFromInterpolation(state1, state2, t, fromInterpolation) { if (t === 0) { return set(state1.clone(), get(state1)); } else if (Math.abs(t - 1) < Number.EPSILON) { return set(state2.clone(), get(state2)); } else { console.assert(get(state1) === get(state2), "The timestamps for interpolation do not match"); return set(fromInterpolation(state1, state2, t), get(state1)); } } function tweenedFromInterpolation(state1, state2, t, fromInterpolation) { const timestampDifference = sub(get(state2), get(state1)); const timestampOffset = t * timestampDifference; const timestampInterpolated = get(state1) + timestampOffset; const results = fromInterpolation(state1, state2, t); state1.dispose(); return new Tweened(results, timestampInterpolated); } // lib/dist/esm/fixed_timestepper.js var TerminationCondition; (function(TerminationCondition2) { TerminationCondition2[TerminationCondition2["LastUndershoot"] = 0] = "LastUndershoot"; TerminationCondition2[TerminationCondition2["FirstOvershoot"] = 1] = "FirstOvershoot"; })(TerminationCondition || (TerminationCondition = {})); function decomposeFloatTimestamp(condition, floatTimestamp, timestepSeconds) { let timestamp; switch (condition) { case TerminationCondition.LastUndershoot: timestamp = floor(floatTimestamp); break; case TerminationCondition.FirstOvershoot: timestamp = ceil(floatTimestamp); break; } const overshootSeconds = asSeconds(subFloat(toFloat(timestamp), floatTimestamp), timestepSeconds); return [timestamp, overshootSeconds]; } function shouldTerminate(condition, currentOvershootSeconds, nextOvershootSeconds) { switch (condition) { case TerminationCondition.LastUndershoot: return nextOvershootSeconds > 0; case TerminationCondition.FirstOvershoot: return currentOvershootSeconds >= 0; } } var TimeKeeper = class { stepper; terminationCondition; timestepOvershootSeconds = 0; config; constructor(stepper, config, terminationCondition = TerminationCondition.LastUndershoot) { this.stepper = stepper; this.config = config; this.terminationCondition = terminationCondition; } update(deltaSeconds, serverSecondsSinceStartup) { const startTime = Date.now(); const compensateStart = Date.now(); const compensatedDeltaSeconds = this.deltaSecondsCompensateForDrift(deltaSeconds, serverSecondsSinceStartup); const compensateEnd = Date.now(); const stepStart = Date.now(); const stepCount = this.advanceStepper(compensatedDeltaSeconds); const stepEnd = Date.now(); const skipStart = Date.now(); this.timeskipIfNeeded(serverSecondsSinceStartup); const skipEnd = Date.now(); const postStart = Date.now(); if (stepCount > 0) this.stepper.postUpdate(this.timestepOvershootSeconds); const postEnd = Date.now(); if (Date.now() - startTime > 15) { console.log(`updating timekeeper took too long: ${Date.now() - startTime}`); console.log(`updating drift took too long: ${compensateEnd - compensateStart}`); console.log(`updating step took too long: ${stepEnd - stepStart}`); console.log(`updating timeskip took too long: ${skipEnd - skipStart}`); console.log(`updating postUpdate took too long: ${postEnd - postStart}`); } } currentLogicalTimestamp() { return subFloat(toFloat(this.stepper.lastCompletedTimestamp()), makeFromSecondsFloat(this.timestepOvershootSeconds, this.config.timestepSeconds)); } targetLogicalTimestamp(serverSecondsSinceStartup) { return makeFromSecondsFloat(serverSecondsSinceStartup, 1 / 60); } timestampDriftSeconds(serverSecondsSinceStartup) { const frameDrift = subFloat(this.currentLogicalTimestamp(), this.targetLogicalTimestamp(serverSecondsSinceStartup)); const secondsDrift = asSeconds(frameDrift, this.config.timestepSeconds); return secondsDrift; } deltaSecondsCompensateForDrift(deltaSeconds, serverSecondsSinceStartup) { let timestampDriftSeconds; const drift = this.timestampDriftSeconds(serverSecondsSinceStartup - deltaSeconds); if (Math.abs(drift) < this.config.timestepSeconds * 0.5) { timestampDriftSeconds = 0; } else { timestampDriftSeconds = drift; } const uncappedCompensatedDeltaSeconds = Math.max(deltaSeconds - timestampDriftSeconds, 0); const compensatedDeltaSeconds = ( // Attempted to advance more than the allowed delta seconds. This should not happen too often. uncappedCompensatedDeltaSeconds > this.config.updateDeltaSecondsMax ? this.config.updateDeltaSecondsMax : uncappedCompensatedDeltaSeconds ); return compensatedDeltaSeconds; } advanceStepper(deltaSeconds) { let stepCount = 0; this.timestepOvershootSeconds -= deltaSeconds; while (true) { const nextOvershootSeconds = this.timestepOvershootSeconds + this.config.timestepSeconds; if (shouldTerminate(this.terminationCondition, this.timestepOvershootSeconds, nextOvershootSeconds)) { break; } this.stepper.step(); stepCount++; this.timestepOvershootSeconds = nextOvershootSeconds; } return stepCount; } timeskipIfNeeded(serverSecondsSinceStartup) { const driftSeconds = this.timestampDriftSeconds(serverSecondsSinceStartup); if (Math.abs(driftSeconds) >= this.config.timestampSkipThresholdSeconds) { const [correctedTimestamp, correctedOvershootSeconds] = decomposeFloatTimestamp(this.terminationCondition, this.targetLogicalTimestamp(serverSecondsSinceStartup), this.config.timestepSeconds); this.stepper.resetLastCompletedTimestamp(correctedTimestamp); this.timestepOvershootSeconds = correctedOvershootSeconds; } } }; // lib/dist/esm/old_new.js var OldNewState; (function(OldNewState2) { OldNewState2[OldNewState2["LeftOldRightNew"] = 0] = "LeftOldRightNew"; OldNewState2[OldNewState2["LeftNewRightOld"] = 1] = "LeftNewRightOld"; })(OldNewState || (OldNewState = {})); var OldNew = class { left; right; state = OldNewState.LeftNewRightOld; constructor(left, right) { this.left = left; this.right = right; } setNew(value) { if (this.state === OldNewState.LeftNewRightOld) { this.left = value; } else { this.right = value; } } get() { if (this.state === OldNewState.LeftNewRightOld) { return { old: this.right, new: this.left }; } else { return { old: this.left, new: this.right }; } } swap() { if (this.state === OldNewState.LeftNewRightOld) { this.state = OldNewState.LeftOldRightNew; } else { this.state = OldNewState.LeftNewRightOld; } } }; // lib/dist/esm/simulation.js var InitializationType; (function(InitializationType2) { InitializationType2[InitializationType2["PreInitialized"] = 0] = "PreInitialized"; InitializationType2[InitializationType2["NeedsInitialization"] = 1] = "NeedsInitialization"; })(InitializationType || (InitializationType = {})); var Simulation = class { world; commandBuffer = new CommandBuffer(); hasInitialized; constructor(world, initializationType) { this.world = world; this.hasInitialized = initializationType === InitializationType.PreInitialized; } step(endingTimestamp = this.simulatingTimestamp()) { const startTime = Date.now(); const commands = this.commandBuffer.drainUpTo(endingTimestamp); for (const command of commands) { this.world.applyCommand(command); } this.world.step(); this.commandBuffer.updateTimestamp(add(this.lastCompletedTimestamp(), 1)); const endTime = Date.now(); if (endTime - startTime > 15) { console.log(` simulation step took ${endTime - startTime}`); } } getWorld() { return this.world; } simulatingTimestamp() { return add(this.lastCompletedTimestamp(), 1); } scheduleCommand(command) { this.commandBuffer.insert(command); } tryCompletingSimulationsUpTo(targetCompletedTimestamp, maxSteps) { for (let i = 0; i < maxSteps; i++) { if (cmp(this.lastCompletedTimestamp(), targetCompletedTimestamp) > -1) { break; } this.step(); } } applyCompletedSnapshot(completedSnapshot, rewoundCommandBuffer) { this.world.applySnapshot(set(completedSnapshot, completedSnapshot.timestamp)); this.commandBuffer = rewoundCommandBuffer; this.commandBuffer.updateTimestamp(get(completedSnapshot)); this.hasInitialized = true; } lastCompletedSnapshot() { return set(this.world.snapshot(), this.lastCompletedTimestamp()); } lastCompletedTimestamp() { return this.commandBuffer.timestamp(); } resetLastCompletedTimestamp(timestamp) { const oldTimestamp = this.lastCompletedTimestamp(); this.commandBuffer.updateTimestamp(timestamp); if (cmp(timestamp, oldTimestamp) === -1) { const commands = this.commandBuffer.drainAll(); for (const command of commands) { this.world.applyCommand(command); } } } displayState() { if (this.hasInitialized) { return set(this.world.displayState(), this.lastCompletedTimestamp()); } return void 0; } bufferedCommands() { return this.commandBuffer[Symbol.iterator](); } postUpdate() { } }; // lib/dist/esm/client.js var StageState; (function(StageState2) { StageState2[StageState2["SyncingClock"] = 0] = "SyncingClock"; StageState2[StageState2["SyncingInitialState"] = 1] =