From 43c31f57c3599e03e6b6332a81d5987e5a80b3db Mon Sep 17 00:00:00 2001 From: moruiyi Date: Fri, 20 Aug 2021 04:09:20 +0000 Subject: [PATCH 1/9] refactor(util): export to new package --- package.json | 3 +- packages/netcode/example/mock-net.ts | 2 +- packages/netcode/package.json | 5 +- packages/netcode/src/comp-interface.ts | 6 + packages/netcode/src/domain.ts | 6 +- packages/netcode/src/message-manager.ts | 3 +- packages/netcode/src/misc.ts | 62 -------- packages/netcode/test/misc.test.ts | 54 +------ packages/util/esbuild.js | 43 +++++ packages/util/jest.config.js | 198 ++++++++++++++++++++++++ packages/util/package.json | 24 +++ packages/util/src/deferred.ts | 61 ++++++++ packages/util/src/index.ts | 2 + packages/util/src/ring-buffer.ts | 102 ++++++++++++ packages/util/test/deferred.test.ts | 53 +++++++ packages/util/test/ring-buffer.test.ts | 147 ++++++++++++++++++ packages/util/test/util.ts | 7 + packages/util/tsconfig.json | 37 +++++ yarn.lock | 9 +- 19 files changed, 701 insertions(+), 123 deletions(-) create mode 100644 packages/util/esbuild.js create mode 100644 packages/util/jest.config.js create mode 100644 packages/util/package.json create mode 100644 packages/util/src/deferred.ts create mode 100644 packages/util/src/index.ts create mode 100644 packages/util/src/ring-buffer.ts create mode 100644 packages/util/test/deferred.test.ts create mode 100644 packages/util/test/ring-buffer.test.ts create mode 100644 packages/util/test/util.ts create mode 100644 packages/util/tsconfig.json diff --git a/package.json b/package.json index 18656ab..06aecb3 100644 --- a/package.json +++ b/package.json @@ -21,7 +21,8 @@ "lerna": "^4.0.0", "lint-staged": "^11.1.2", "prettier": "^2.2.1", - "rimraf": "^3.0.2" + "rimraf": "^3.0.2", + "typescript": "^4.3.5" }, "scripts": { "build": "lerna run build", diff --git a/packages/netcode/example/mock-net.ts b/packages/netcode/example/mock-net.ts index e13a3d4..a63551c 100644 --- a/packages/netcode/example/mock-net.ts +++ b/packages/netcode/example/mock-net.ts @@ -1,4 +1,4 @@ -import { Deferred } from "../src"; +import { Deferred } from "@netcodejs/util"; export interface Receiver { receive(data: any): void; diff --git a/packages/netcode/package.json b/packages/netcode/package.json index 05c9ed1..b2c6583 100644 --- a/packages/netcode/package.json +++ b/packages/netcode/package.json @@ -34,5 +34,8 @@ "path": "dist/netcodejs.esm.js", "limit": "10 KB" } - ] + ], + "dependencies": { + "@netcodejs/util": "^0.0.0" + } } diff --git a/packages/netcode/src/comp-interface.ts b/packages/netcode/src/comp-interface.ts index f7791ee..36281cb 100644 --- a/packages/netcode/src/comp-interface.ts +++ b/packages/netcode/src/comp-interface.ts @@ -11,6 +11,12 @@ export abstract class IComp { get $comps() { return this._entity!.$comps; } + get logicTime() { + return this.domain?.logicTime; + } + get renderTime() { + return this.domain?.renderTime; + } get(ctr: { new (): T }): T | null { return this._entity!.get(ctr); diff --git a/packages/netcode/src/domain.ts b/packages/netcode/src/domain.ts index 6158224..025519e 100644 --- a/packages/netcode/src/domain.ts +++ b/packages/netcode/src/domain.ts @@ -98,7 +98,7 @@ export class Domain { public readonly readonlyInternalMsgMng!: MessageManager; private _fixedSecAccumulator = 0; - public readonly time: Entity; + public readonly singleton: Entity; public readonly logicTime: LogicTimeComp; public readonly renderTime: RenderTimeComp; @@ -123,10 +123,10 @@ export class Domain { this.logicTime = new LogicTimeComp(); this.renderTime = new RenderTimeComp(); - this.time = new Entity(this.logicTime, this.renderTime); + this.singleton = new Entity(this.logicTime, this.renderTime); this.logicTime.delta = this.option.fixedTimeSec; - this.reg(this.time); + this.reg(this.singleton); } //#region public methods diff --git a/packages/netcode/src/message-manager.ts b/packages/netcode/src/message-manager.ts index d9d8d11..43b2f8d 100644 --- a/packages/netcode/src/message-manager.ts +++ b/packages/netcode/src/message-manager.ts @@ -1,4 +1,4 @@ -import { composeVersion, decomposeVersion, Deferred } from "./misc"; +import { composeVersion, decomposeVersion } from "./misc"; import { IDataBufferReader, IDataBufferWriter, @@ -8,6 +8,7 @@ import { import { DataTypeVoid, ISchema } from "./comp-schema"; import { RPC_MAX_UUID } from "./builtin"; import { IComp } from "./comp-interface"; +import { Deferred } from "@netcodejs/util"; export enum MessageType { UPDATE_COMPONENT, diff --git a/packages/netcode/src/misc.ts b/packages/netcode/src/misc.ts index fb82620..f7fabae 100644 --- a/packages/netcode/src/misc.ts +++ b/packages/netcode/src/misc.ts @@ -32,65 +32,3 @@ export function assert( } export type ProtoOf = Pick; - -export class Deferred { - public promise: Promise; - - private fate: "resolved" | "unresolved"; - private state: "pending" | "fulfilled" | "rejected"; - - private _resolve!: (value: T | PromiseLike) => void; - private _reject!: (reason?: any) => void; - private _value: any; - public get value() { - return this._value; - } - - constructor() { - this.state = "pending"; - this.fate = "unresolved"; - this.promise = new Promise((resolve, reject) => { - this._resolve = resolve; - this._reject = reject; - }); - this.promise.then( - (res) => { - this.state = "fulfilled"; - this._value = res; - }, - () => (this.state = "rejected") - ); - } - - resolve(value: T | PromiseLike) { - if (this.fate === "resolved") { - throw "Deferred cannot be resolved twice"; - } - this.fate = "resolved"; - this._resolve(value); - } - - reject(reason?: any) { - if (this.fate === "resolved") { - throw "Deferred cannot be resolved twice"; - } - this.fate = "resolved"; - this._reject(reason); - } - - isResolved() { - return this.fate === "resolved"; - } - - isPending() { - return this.state === "pending"; - } - - isFulfilled() { - return this.state === "fulfilled"; - } - - isRejected() { - return this.state === "rejected"; - } -} diff --git a/packages/netcode/test/misc.test.ts b/packages/netcode/test/misc.test.ts index a8451ec..5c96a89 100644 --- a/packages/netcode/test/misc.test.ts +++ b/packages/netcode/test/misc.test.ts @@ -1,5 +1,4 @@ -import { asSerable, assert, Deferred, isPrimitive } from "../src"; -import { wait } from "./util"; +import { asSerable, assert, isPrimitive } from "../src"; test("isPrimitive", () => { expect(isPrimitive(true)).toBe(true); @@ -17,57 +16,6 @@ test("asset", () => { expect(() => assert(true, Error)).not.toThrowError(Error); }); -describe("Deferred", () => { - test("basic", () => { - const defer = new Deferred(); - expect(defer.promise).toBeTruthy(); - expect(defer.value).toBeFalsy(); - expect(defer.isFulfilled()).toBe(false); - expect(defer.isPending()).toBe(true); - expect(defer.isResolved()).toBe(false); - expect(defer.isRejected()).toBe(false); - }); - - test("resolved", async () => { - let outResult = 0; - const inResult = 1; - const defer = new Deferred(); - defer.resolve(inResult); - expect(() => defer.resolve(inResult)).toThrow( - "Deferred cannot be resolved twice" - ); - outResult = await defer.promise; - - expect(outResult).toBe(inResult); - expect(defer.value).toBe(inResult); - expect(defer.isFulfilled()).toBe(true); - expect(defer.isPending()).toBe(false); - expect(defer.isResolved()).toBe(true); - expect(defer.isRejected()).toBe(false); - }); - - test("rejected", async () => { - const inReason = 1; - let outReason = 0; - const defer = new Deferred(); - defer.promise.then(undefined, (r) => { - outReason = r; - }); - defer.reject(inReason); - expect(() => defer.reject(inReason)).toThrow( - "Deferred cannot be resolved twice" - ); - await wait(); - - expect(outReason).toBe(inReason); - expect(defer.value).toBeFalsy; - expect(defer.isFulfilled()).toBe(false); - expect(defer.isPending()).toBe(false); - expect(defer.isResolved()).toBe(true); - expect(defer.isRejected()).toBe(true); - }); -}); - test("asSerable", () => { class S { ser() {} diff --git a/packages/util/esbuild.js b/packages/util/esbuild.js new file mode 100644 index 0000000..32c225d --- /dev/null +++ b/packages/util/esbuild.js @@ -0,0 +1,43 @@ +const builder = require("esbuild"); +const rimarf = require("rimraf"); + +const timeRecord = Date.now(); + +rimarf("dist/", (err) => { + if (err) { + return console.error(err); + } + run(); +}); +function run() { + const prom = [ + builder.buildSync({ + entryPoints: ["src/index.ts"], + bundle: true, + format: "cjs", + outfile: "dist/util.cjs.js", + define: { + "process.env.NODE_ENV": "production", + }, + minify: true, + sourcemap: true, + }), + builder.buildSync({ + entryPoints: ["src/index.ts"], + bundle: true, + format: "esm", + outfile: "dist/util.esm.js", + define: { + "process.env.NODE_ENV": "production", + }, + sourcemap: true, + minify: true, + }), + ]; + + Promise.all(prom).then(() => { + console.log( + `Build finish! The time used is ${Date.now() - timeRecord}ms` + ); + }); +} diff --git a/packages/util/jest.config.js b/packages/util/jest.config.js new file mode 100644 index 0000000..3d9ad3f --- /dev/null +++ b/packages/util/jest.config.js @@ -0,0 +1,198 @@ +/* + * For a detailed explanation regarding each configuration property and type check, visit: + * https://jestjs.io/docs/en/configuration.html + */ +module.exports = { + // All imported modules in your tests should be mocked automatically + // automock: false, + + // Stop running tests after `n` failures + // bail: 0, + + // The directory where Jest should store its cached dependency information + // cacheDirectory: "C:\\Users\\ProjectAlpha\\AppData\\Local\\Temp\\jest", + + // Automatically clear mock calls and instances between every test + clearMocks: true, + + // Indicates whether the coverage information should be collected while executing the test + // collectCoverage: false, + + // An array of glob patterns indicating a set of files for which coverage information should be collected + collectCoverageFrom: [ + "src/**/*.ts", + "!**/*.d.ts", + "!**/base-dirty-data.ts", + "!src/lib/**/*", + ], + + // The directory where Jest should output its coverage files + coverageDirectory: "coverage", + + // An array of regexp pattern strings used to skip coverage collection + // coveragePathIgnorePatterns: [ + // "\\\\node_modules\\\\" + // ], + + // Indicates which provider should be used to instrument code for coverage + // coverageProvider: "babel", + + // A list of reporter names that Jest uses when writing coverage reports + // coverageReporters: [ + // "json", + // "text", + // "lcov", + // "clover" + // ], + + // An object that configures minimum threshold enforcement for coverage results + // coverageThreshold: undefined, + + // A path to a custom dependency extractor + // dependencyExtractor: undefined, + + // Make calling deprecated APIs throw helpful error messages + // errorOnDeprecated: false, + + // Force coverage collection from ignored files using an array of glob patterns + // forceCoverageMatch: [], + + // A path to a module which exports an async function that is triggered once before all test suites + // globalSetup: undefined, + + // A path to a module which exports an async function that is triggered once after all test suites + // globalTeardown: undefined, + + // A set of global variables that need to be available in all test environments + // globals: {}, + + // The maximum amount of workers used to run your tests. Can be specified as % or a number. E.g. maxWorkers: 10% will use 10% of your CPU amount + 1 as the maximum worker number. maxWorkers: 2 will use a maximum of 2 workers. + // maxWorkers: "50%", + + // An array of directory names to be searched recursively up from the requiring module's location + // moduleDirectories: [ + // "node_modules" + // ], + + // An array of file extensions your modules use + // moduleFileExtensions: ["js", "json", "jsx", "ts", "tsx", "node"], + + // A map from regular expressions to module names or to arrays of module names that allow to stub out resources with a single module + // moduleNameMapper: {}, + + // An array of regexp pattern strings, matched against all module paths before considered 'visible' to the module loader + // modulePathIgnorePatterns: [], + + // Activates notifications for test results + // notify: false, + + // An enum that specifies notification mode. Requires { notify: true } + // notifyMode: "failure-change", + + // A preset that is used as a base for Jest's configuration + // preset: undefined, + + // Run tests from one or more projects + // projects: undefined, + + // Use this configuration option to add custom reporters to Jest + // reporters: undefined, + + // Automatically reset mock state between every test + // resetMocks: false, + + // Reset the module registry before running each individual test + // resetModules: false, + + // A path to a custom resolver + // resolver: undefined, + + // Automatically restore mock state between every test + // restoreMocks: false, + + // The root directory that Jest should scan for tests and modules within + // rootDir: "test", + + // A list of paths to directories that Jest should use to search for files in + // roots: [ + // "" + // ], + + // Allows you to use a custom runner instead of Jest's default test runner + // runner: "jest-runner", + + // The paths to modules that run some code to configure or set up the testing environment before each test + // setupFiles: [], + + // A list of paths to modules that run some code to configure or set up the testing framework before each test + // setupFilesAfterEnv: [], + + // The number of seconds after which a test is considered as slow and reported as such in the results. + // slowTestThreshold: 5, + + // A list of paths to snapshot serializer modules Jest should use for snapshot testing + // snapshotSerializers: [], + + // The test environment that will be used for testing + // testEnvironment: "node", + + // Options that will be passed to the testEnvironment + // testEnvironmentOptions: {}, + + // Adds a location field to test results + // testLocationInResults: false, + + // The glob patterns Jest uses to detect test files + // testMatch: [ + // "**/__tests__/**/*.[jt]s?(x)", + // "**/?(*.)+(spec|test).[tj]s?(x)" + // ], + + // An array of regexp pattern strings that are matched against all test paths, matched tests are skipped + // testPathIgnorePatterns: [ + // "\\\\node_modules\\\\" + // ], + + // The regexp pattern or array of patterns that Jest uses to detect test files + // testRegex: [], + + // This option allows the use of a custom results processor + // testResultsProcessor: undefined, + + // This option allows use of a custom test runner + // testRunner: "jasmine2", + + // This option sets the URL for the jsdom environment. It is reflected in properties such as location.href + // testURL: "http://localhost", + + // Setting this value to "fake" allows the use of fake timers for functions such as "setTimeout" + // timers: "real", + + // A map from regular expressions to paths to transformers + transform: { + "^.+\\.tsx?$": [ + "esbuild-jest", + { + sourcemap: true, + }, + ], + }, + + // An array of regexp pattern strings that are matched against all source file paths, matched files will skip transformation + // transformIgnorePatterns: [ + // "\\\\node_modules\\\\", + // "\\.pnp\\.[^\\\\]+$" + // ], + + // An array of regexp pattern strings that are matched against all modules before the module loader will automatically return a mock for them + // unmockedModulePathPatterns: undefined, + + // Indicates whether each individual test should be reported during the run + // verbose: undefined, + + // An array of regexp patterns that are matched against all source file paths before re-running tests in watch mode + // watchPathIgnorePatterns: [], + + // Whether to use watchman for file crawling + // watchman: true, +}; diff --git a/packages/util/package.json b/packages/util/package.json new file mode 100644 index 0000000..bd98868 --- /dev/null +++ b/packages/util/package.json @@ -0,0 +1,24 @@ +{ + "name": "@netcodejs/util", + "version": "0.0.0", + "description": "The utility for netcodejs", + "module": "dist/util.esm.js", + "main": "dist/util.cjs.js", + "types": "dist/index.d.ts", + "repository": "https://github.com/netcodejs/netcode", + "author": "mooooooi", + "license": "GPL-3.0-or-later", + "scripts": { + "build": "node esbuild.js", + "build:nojit": "cross-env ENSURE_JIT=false yarn build", + "build:jit": "cross-env ENSURE_JIT=true yarn build", + "build:example": "node ./esbuild.example.js", + "build:ci": "yarn build && tsc --emitDeclarationOnly --outdir dist", + "test": "jest", + "test:ci": "jest --ci --coverage" + }, + "files": [ + "dist", + "README.md" + ] +} diff --git a/packages/util/src/deferred.ts b/packages/util/src/deferred.ts new file mode 100644 index 0000000..484de38 --- /dev/null +++ b/packages/util/src/deferred.ts @@ -0,0 +1,61 @@ +export class Deferred { + public promise: Promise; + + private fate: "resolved" | "unresolved"; + private state: "pending" | "fulfilled" | "rejected"; + + private _resolve!: (value: T | PromiseLike) => void; + private _reject!: (reason?: any) => void; + private _value: any; + public get value() { + return this._value; + } + + constructor() { + this.state = "pending"; + this.fate = "unresolved"; + this.promise = new Promise((resolve, reject) => { + this._resolve = resolve; + this._reject = reject; + }); + this.promise.then( + (res) => { + this.state = "fulfilled"; + this._value = res; + }, + () => (this.state = "rejected") + ); + } + + resolve(value: T | PromiseLike) { + if (this.fate === "resolved") { + throw "Deferred cannot be resolved twice"; + } + this.fate = "resolved"; + this._resolve(value); + } + + reject(reason?: any) { + if (this.fate === "resolved") { + throw "Deferred cannot be resolved twice"; + } + this.fate = "resolved"; + this._reject(reason); + } + + isResolved() { + return this.fate === "resolved"; + } + + isPending() { + return this.state === "pending"; + } + + isFulfilled() { + return this.state === "fulfilled"; + } + + isRejected() { + return this.state === "rejected"; + } +} diff --git a/packages/util/src/index.ts b/packages/util/src/index.ts new file mode 100644 index 0000000..dd8ab27 --- /dev/null +++ b/packages/util/src/index.ts @@ -0,0 +1,2 @@ +export * from "./deferred"; +export * from "./ring-buffer"; diff --git a/packages/util/src/ring-buffer.ts b/packages/util/src/ring-buffer.ts new file mode 100644 index 0000000..a3596e7 --- /dev/null +++ b/packages/util/src/ring-buffer.ts @@ -0,0 +1,102 @@ +export class RingBuffer { + protected _container: (T | null)[]; + protected _start = 0; + protected _end = 0; + protected get _capacityPlusOne() { + return this.capacity + 1; + } + constructor(readonly capacity: number) { + this._container = new Array(this._capacityPlusOne); + } + + get length() { + return ( + (this._end - this._start + this._capacityPlusOne) % + this._capacityPlusOne + ); + } + + get isEmpty() { + return this._start == this._end; + } + + get isFull() { + return (this._end + 1) % this._capacityPlusOne == this._start; + } + + get head(): T | null { + return this._container[this._start] ?? null; + } + + get tail(): T | null { + if (this.isEmpty) return null; + return ( + this._container[ + (this._end - 1 + this._capacityPlusOne) % this._capacityPlusOne + ] ?? null + ); + } + + get container(): readonly (T | null)[] { + return this._container; + } + + get(idx: number) { + const rIdx = this._getRealIndex(idx); + if (rIdx == -1) { + return null; + } + return this._container[rIdx] ?? null; + } + + set(idx: number, value: T) { + const rIdx = this._getRealIndex(idx); + this._container[rIdx] = value; + } + + /** + * Append a data into the tail of buffer. + * @param {T} data + * @returns {number} The index of data stored in the ring buffer. It will return -1 while the buffer is full. + */ + push(data: T): number { + if (this.isFull) return -1; + const idx = this._end++; + this._end %= this._capacityPlusOne; + this._container[idx] = data; + return this._end - this._start + 1; + } + + pop(): T | null { + if (this.isEmpty) return null; + this._end--; + this._end += this._capacityPlusOne; + this._end %= this._capacityPlusOne; + return this._container[this._end] ?? null; + } + + unshift(data: T) { + if (this.isEmpty) return null; + this._start--; + this._start += this._capacityPlusOne; + this._start %= this._capacityPlusOne; + const idx = this._start; + this._container[idx] = data; + return this._end - this._start + 1; + } + + shift(): T | null { + if (this.isEmpty) return null; + const idx = this._start++; + this._start %= this._capacityPlusOne; + return this._container[idx] ?? null; + } + + protected _getRealIndex(idx: number) { + if (idx < 0 || idx >= this.capacity) { + return -1; + } + const rIdx = (this._start + idx) % this._capacityPlusOne; + return rIdx; + } +} diff --git a/packages/util/test/deferred.test.ts b/packages/util/test/deferred.test.ts new file mode 100644 index 0000000..4522f43 --- /dev/null +++ b/packages/util/test/deferred.test.ts @@ -0,0 +1,53 @@ +import { Deferred } from "../src/deferred"; +import { wait } from "./util"; + +describe("Deferred", () => { + test("basic", () => { + const defer = new Deferred(); + expect(defer.promise).toBeTruthy(); + expect(defer.value).toBeFalsy(); + expect(defer.isFulfilled()).toBe(false); + expect(defer.isPending()).toBe(true); + expect(defer.isResolved()).toBe(false); + expect(defer.isRejected()).toBe(false); + }); + + test("resolved", async () => { + let outResult = 0; + const inResult = 1; + const defer = new Deferred(); + defer.resolve(inResult); + expect(() => defer.resolve(inResult)).toThrow( + "Deferred cannot be resolved twice" + ); + outResult = await defer.promise; + + expect(outResult).toBe(inResult); + expect(defer.value).toBe(inResult); + expect(defer.isFulfilled()).toBe(true); + expect(defer.isPending()).toBe(false); + expect(defer.isResolved()).toBe(true); + expect(defer.isRejected()).toBe(false); + }); + + test("rejected", async () => { + const inReason = 1; + let outReason = 0; + const defer = new Deferred(); + defer.promise.then(undefined, (r) => { + outReason = r; + }); + defer.reject(inReason); + expect(() => defer.reject(inReason)).toThrow( + "Deferred cannot be resolved twice" + ); + await wait(); + + expect(outReason).toBe(inReason); + expect(defer.value).toBeFalsy; + expect(defer.isFulfilled()).toBe(false); + expect(defer.isPending()).toBe(false); + expect(defer.isResolved()).toBe(true); + expect(defer.isRejected()).toBe(true); + }); +}); diff --git a/packages/util/test/ring-buffer.test.ts b/packages/util/test/ring-buffer.test.ts new file mode 100644 index 0000000..70d93aa --- /dev/null +++ b/packages/util/test/ring-buffer.test.ts @@ -0,0 +1,147 @@ +import { RingBuffer } from "../src/ring-buffer"; + +test("initialization state", () => { + const buffer = new RingBuffer(2); + + expect(buffer.capacity).toBe(2); + expect(buffer.isEmpty).toBe(true); + expect(buffer.isFull).toBe(false); + expect(buffer.head).toBe(null); + expect(buffer.tail).toBe(null); + expect(buffer.length).toBe(0); + expect(buffer.get(0)).toBe(null); +}); + +test("normal", () => { + const buffer = new RingBuffer(2); + buffer.push(0); + + expect(buffer.capacity).toBe(2); + expect(buffer.isEmpty).toBe(false); + expect(buffer.isFull).toBe(false); + expect(buffer.head).toBe(0); + expect(buffer.tail).toBe(0); + expect(buffer.length).toBe(1); + expect(buffer.get(0)).toBe(0); +}); + +test("full", () => { + const buffer = new RingBuffer(2); + buffer.push(0); + buffer.push(1); + + expect(buffer.capacity).toBe(2); + expect(buffer.isEmpty).toBe(false); + expect(buffer.isFull).toBe(true); + expect(buffer.head).toBe(0); + expect(buffer.tail).toBe(1); + expect(buffer.length).toBe(2); + expect(buffer.get(0)).toBe(0); + expect(buffer.get(1)).toBe(1); + expect(buffer.get(2)).toBe(null); +}); + +test("overflow", () => { + const buffer = new RingBuffer(2); + buffer.push(0); + buffer.push(1); + buffer.push(2); + + expect(buffer.capacity).toBe(2); + expect(buffer.isEmpty).toBe(false); + expect(buffer.isFull).toBe(true); + expect(buffer.head).toBe(0); + expect(buffer.tail).toBe(1); + expect(buffer.length).toBe(2); + expect(buffer.get(0)).toBe(0); + expect(buffer.get(1)).toBe(1); + expect(buffer.get(2)).toBe(null); +}); + +test("pop", () => { + const buffer = new RingBuffer(2); + buffer.push(0); + buffer.push(1); + + expect(buffer.pop()).toBe(1); + buffer.push(2); + + expect(buffer.capacity).toBe(2); + expect(buffer.isEmpty).toBe(false); + expect(buffer.isFull).toBe(true); + expect(buffer.head).toBe(0); + expect(buffer.tail).toBe(2); + expect(buffer.length).toBe(2); + expect(buffer.get(0)).toBe(0); + expect(buffer.get(1)).toBe(2); + expect(buffer.get(2)).toBe(null); +}); + +test("shift-push", () => { + const buffer = new RingBuffer(2); + buffer.push(0); + buffer.push(1); + + expect(buffer.shift()).toBe(0); + buffer.push(2); + + expect(buffer.capacity).toBe(2); + expect(buffer.isEmpty).toBe(false); + expect(buffer.isFull).toBe(true); + expect(buffer.head).toBe(1); + expect(buffer.tail).toBe(2); + expect(buffer.length).toBe(2); + expect(buffer.get(0)).toBe(1); + expect(buffer.get(1)).toBe(2); + expect(buffer.get(2)).toBe(null); + + expect(buffer.shift()).toBe(1); + buffer.push(3); + + expect(buffer.capacity).toBe(2); + expect(buffer.isEmpty).toBe(false); + expect(buffer.isFull).toBe(true); + expect(buffer.head).toBe(2); + expect(buffer.tail).toBe(3); + expect(buffer.length).toBe(2); + expect(buffer.get(0)).toBe(2); + expect(buffer.get(1)).toBe(3); + expect(buffer.get(2)).toBe(null); + + expect(buffer.container).toStrictEqual([3, 1, 2]); +}); + +test("pop-unshift", () => { + const buffer = new RingBuffer(2); + buffer.push(0); + buffer.push(1); + + expect(buffer.pop()).toBe(1); + buffer.unshift(1); + + expect(buffer.capacity).toBe(2); + expect(buffer.isEmpty).toBe(false); + expect(buffer.isFull).toBe(true); + expect(buffer.head).toBe(1); + expect(buffer.tail).toBe(0); + expect(buffer.length).toBe(2); + expect(buffer.get(0)).toBe(1); + expect(buffer.get(1)).toBe(0); + expect(buffer.get(2)).toBe(null); + expect(buffer.container).toStrictEqual([0, 1, 1]); + + expect(buffer.pop()).toBe(0); + buffer.unshift(0); + + expect(buffer.capacity).toBe(2); + expect(buffer.isEmpty).toBe(false); + expect(buffer.isFull).toBe(true); + expect(buffer.head).toBe(0); + expect(buffer.tail).toBe(1); + expect(buffer.length).toBe(2); + expect(buffer.get(0)).toBe(0); + expect(buffer.get(1)).toBe(1); + expect(buffer.get(2)).toBe(null); + + expect(buffer.container).toStrictEqual([0, 0, 1]); +}); diff --git a/packages/util/test/util.ts b/packages/util/test/util.ts new file mode 100644 index 0000000..4350844 --- /dev/null +++ b/packages/util/test/util.ts @@ -0,0 +1,7 @@ +export function wait(sec = 0) { + return new Promise((resolve) => { + setTimeout(() => { + resolve(); + }, sec); + }); +} diff --git a/packages/util/tsconfig.json b/packages/util/tsconfig.json new file mode 100644 index 0000000..71f3d9f --- /dev/null +++ b/packages/util/tsconfig.json @@ -0,0 +1,37 @@ +{ + // see https://www.typescriptlang.org/tsconfig to better understand tsconfigs + "include": ["src", "types"], + "compilerOptions": { + "target": "es2015", + "module": "esnext", + "lib": ["dom", "esnext"], + "importHelpers": true, + // output .d.ts declaration files for consumers + "declaration": true, + // output .js.map sourcemap files for consumers + "sourceMap": true, + // match output dir to input dir. e.g. dist/index instead of dist/src/index + "rootDir": "./src", + // stricter type-checking for stronger correctness. Recommended by TS + "strict": true, + // linter checks for common issues + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": true, + // noUnused* overlap with @typescript-eslint/no-unused-vars, can disable if duplicative + "noUnusedLocals": true, + "noUnusedParameters": true, + // use Node's module resolution algorithm, instead of the legacy TS one + "moduleResolution": "node", + // transpile JSX to React.createElement + "jsx": "react", + // interop between ESM and CJS modules. Recommended by TS + "esModuleInterop": true, + // significant perf increase by skipping checking .d.ts files, particularly those in node_modules. Recommended by TS + "skipLibCheck": true, + // error out if import and file system have a casing mismatch. Recommended by TS + "forceConsistentCasingInFileNames": true, + // `tsdx build` ignores this option, but it is commonly used when type-checking separately with `tsc` + // "noEmit": true, + "experimentalDecorators": true + } + } \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index 07c60ff..7e336f9 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5272,7 +5272,9 @@ neo-async@^2.6.0: integrity sha1-tKr7k+OustgXTKU88WOrfXMIMF8= "netcodejs@file:packages/netcode": - version "0.0.9" + version "0.0.12" + dependencies: + "@netcodejs/util" "^0.0.0" nice-try@^1.0.4: version "1.0.5" @@ -7070,6 +7072,11 @@ typedarray@^0.0.6: resolved "https://registry.nlark.com/typedarray/download/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c= +typescript@^4.3.5: + version "4.3.5" + resolved "https://registry.nlark.com/typescript/download/typescript-4.3.5.tgz#4d1c37cc16e893973c45a06886b7113234f119f4" + integrity sha1-TRw3zBbok5c8RaBohrcRMjTxGfQ= + uglify-js@^3.1.4: version "3.14.1" resolved "https://registry.nlark.com/uglify-js/download/uglify-js-3.14.1.tgz#e2cb9fe34db9cb4cf7e35d1d26dfea28e09a7d06" From c53196e94ad63a9eee28631825eaf85192b35007 Mon Sep 17 00:00:00 2001 From: moruiyi Date: Fri, 20 Aug 2021 07:09:05 +0000 Subject: [PATCH 2/9] chore(netcodejs/util): update package scripts --- .github/workflows/main.yml | 9 ++++++--- packages/netcode/package.json | 12 +++++++----- packages/util/package.json | 13 +++++++------ 3 files changed, 20 insertions(+), 14 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index a73134d..2562eb9 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -1,4 +1,4 @@ -name: CI Any To Main +name: CI on: push: branches: @@ -58,7 +58,7 @@ jobs: install-command: yarn --frozen-lockfile --silent - name: Test - run: yarn test:ci + run: yarn test:coverage - name: Coveralls - ${{ env.package_array }} uses: coverallsapp/github-action@master @@ -94,13 +94,16 @@ jobs: install-command: yarn --frozen-lockfile --silent - name: Build - run: yarn build:ci + run: yarn build - name: Monorepo publish id: publish run: yarn lerna publish from-git --yes env: NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} + + - name: Build example + run: yarn lerna run build:example --scope=netcodejs - name: Deploy example 🚀 uses: JamesIves/github-pages-deploy-action@4.1.4 diff --git a/packages/netcode/package.json b/packages/netcode/package.json index b2c6583..fadc176 100644 --- a/packages/netcode/package.json +++ b/packages/netcode/package.json @@ -9,13 +9,15 @@ "author": "littlemoi", "license": "GPL-3.0-or-later", "scripts": { - "build": "node esbuild.js", + "build:fast": "node esbuild.js", + "build": "yarn build:fast && tsc --emitDeclarationOnly --outdir dist", + + "test": "jest", + "test:coverage": "jest --ci --coverage", + "build:nojit": "cross-env ENSURE_JIT=false yarn build", "build:jit": "cross-env ENSURE_JIT=true yarn build", - "build:example": "node ./esbuild.example.js", - "build:ci": "yarn build && yarn build:example && tsc --emitDeclarationOnly --outdir dist", - "test": "jest", - "test:ci": "jest --ci --coverage" + "build:example": "node ./esbuild.example.js" }, "files": [ "dist", diff --git a/packages/util/package.json b/packages/util/package.json index bd98868..6f4a33e 100644 --- a/packages/util/package.json +++ b/packages/util/package.json @@ -9,13 +9,14 @@ "author": "mooooooi", "license": "GPL-3.0-or-later", "scripts": { - "build": "node esbuild.js", - "build:nojit": "cross-env ENSURE_JIT=false yarn build", - "build:jit": "cross-env ENSURE_JIT=true yarn build", - "build:example": "node ./esbuild.example.js", - "build:ci": "yarn build && tsc --emitDeclarationOnly --outdir dist", + "build:fast": "node esbuild.js", + "build": "yarn build:fast && tsc --emitDeclarationOnly --outdir dist", + "test": "jest", - "test:ci": "jest --ci --coverage" + "test:coverage": "jest --ci --coverage", + + "build:nojit": "cross-env ENSURE_JIT=false yarn build", + "build:jit": "cross-env ENSURE_JIT=true yarn build" }, "files": [ "dist", From 49fc8cb38eb3d4e90f187e9be107cd7928a70ae6 Mon Sep 17 00:00:00 2001 From: moruiyi Date: Fri, 20 Aug 2021 07:12:00 +0000 Subject: [PATCH 3/9] chore(netcodejs/util): update package scripts --- .github/workflows/main.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 2562eb9..89731c7 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -32,12 +32,13 @@ jobs: with: install-command: yarn --frozen-lockfile --silent + - name: Build + run: yarn build + - name: Test run: yarn test # - name: Lint # run: yarn lint - - name: Build - run: yarn build testAndUploadCoverall: needs: multi_build_and_test From 0e5ade59bac3296571c8a8192667ddac709d4487 Mon Sep 17 00:00:00 2001 From: moruiyi Date: Fri, 20 Aug 2021 07:15:37 +0000 Subject: [PATCH 4/9] chore(netcodejs/util): update package scripts --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 06aecb3..f607862 100644 --- a/package.json +++ b/package.json @@ -26,9 +26,9 @@ }, "scripts": { "build": "lerna run build", - "build:ci": "lerna run build:ci", + "build:fast": "lerna run build:fast", "test": "lerna run test", - "test:ci": "lerna run test:ci", + "test:coverage": "lerna run test:ci", "prepare": "lerna bootstrap && husky install" }, "lint-staged": { From d09e762269ebca5bc77e5b37dcc01d907084d502 Mon Sep 17 00:00:00 2001 From: moruiyi Date: Fri, 20 Aug 2021 07:19:00 +0000 Subject: [PATCH 5/9] chore(netcodejs/util): update package scripts --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index f607862..1d70ace 100644 --- a/package.json +++ b/package.json @@ -28,7 +28,7 @@ "build": "lerna run build", "build:fast": "lerna run build:fast", "test": "lerna run test", - "test:coverage": "lerna run test:ci", + "test:coverage": "lerna run test:coverage", "prepare": "lerna bootstrap && husky install" }, "lint-staged": { From 376b910920f3b33f4dd3a4aca1cdcaa783859118 Mon Sep 17 00:00:00 2001 From: moruiyi Date: Fri, 20 Aug 2021 07:26:49 +0000 Subject: [PATCH 6/9] chore(ci): update main.yml --- .github/workflows/main.yml | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 89731c7..da26f24 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -34,7 +34,7 @@ jobs: - name: Build run: yarn build - + - name: Test run: yarn test # - name: Lint @@ -57,7 +57,10 @@ jobs: uses: bahmutov/npm-install@v1 with: install-command: yarn --frozen-lockfile --silent - + + - name: Build all package + run: yarn build + - name: Test run: yarn test:coverage From d0f21d816eebaa2838895da8edcc28070b7793b3 Mon Sep 17 00:00:00 2001 From: moruiyi Date: Fri, 20 Aug 2021 07:34:57 +0000 Subject: [PATCH 7/9] chore(ci): update converage --- .github/workflows/main.yml | 13 ++++++++++++- packages/netcode/test/blash.test.ts | 4 ++-- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index da26f24..be5664d 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -60,7 +60,7 @@ jobs: - name: Build all package run: yarn build - + - name: Test run: yarn test:coverage @@ -75,6 +75,17 @@ jobs: parallel: true flag-name: ${{ env.package_array }} + - name: Coveralls - ${{ env.package_array }} + uses: coverallsapp/github-action@master + env: + package_array: "util" + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + path-to-lcov: packages/${{ env.package_array }}/coverage/lcov.info + base-path: packages/${{ env.package_array }} + parallel: true + flag-name: ${{ env.package_array }} + buildAndPublish: needs: multi_build_and_test if: ${{ github.event_name == 'push' }} diff --git a/packages/netcode/test/blash.test.ts b/packages/netcode/test/blash.test.ts index 8da977a..b6a02bc 100644 --- a/packages/netcode/test/blash.test.ts +++ b/packages/netcode/test/blash.test.ts @@ -363,12 +363,12 @@ describe("Serable", () => { class LifecycleTestComp extends IComp { logicUpdate() { hasLogicUpdate = true; - logicTime = this.domain.logicTime.duration; + logicTime = this.logicTime.duration; } renderUpdate() { hasRenderUpdate = true; - renderTime = this.domain.renderTime.duration; + renderTime = this.renderTime.duration; } init() { From ca1dec52a8585875d764c4ad54fd90a14e7d2641 Mon Sep 17 00:00:00 2001 From: moruiyi Date: Fri, 20 Aug 2021 07:43:23 +0000 Subject: [PATCH 8/9] chore(ci): update converage --- packages/netcode/src/comp-interface.ts | 4 ++-- packages/netcode/test/blash.test.ts | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/packages/netcode/src/comp-interface.ts b/packages/netcode/src/comp-interface.ts index 36281cb..180485d 100644 --- a/packages/netcode/src/comp-interface.ts +++ b/packages/netcode/src/comp-interface.ts @@ -12,10 +12,10 @@ export abstract class IComp { return this._entity!.$comps; } get logicTime() { - return this.domain?.logicTime; + return this.domain!.logicTime; } get renderTime() { - return this.domain?.renderTime; + return this.domain!.renderTime; } get(ctr: { new (): T }): T | null { diff --git a/packages/netcode/test/blash.test.ts b/packages/netcode/test/blash.test.ts index b6a02bc..937b795 100644 --- a/packages/netcode/test/blash.test.ts +++ b/packages/netcode/test/blash.test.ts @@ -385,7 +385,8 @@ describe("Serable", () => { "default", new StringDomainOption(RpcType.SERVER) ); - const e = new Entity(new LifecycleTestComp()); + const lifecycle = new LifecycleTestComp(); + const e = new Entity(lifecycle); d.reg(e); d.update(1); d.unreg(e); From 816740f6498a21185265abce882b037c25eaf3f7 Mon Sep 17 00:00:00 2001 From: moruiyi Date: Fri, 20 Aug 2021 08:50:19 +0000 Subject: [PATCH 9/9] chore(test): upgrade coverage --- package.json | 2 ++ packages/netcode/test/blash.test.ts | 6 ++++++ packages/netcode/test/rpc.test.ts | 13 ++++++++++--- 3 files changed, 18 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index 1d70ace..71ae52e 100644 --- a/package.json +++ b/package.json @@ -29,6 +29,8 @@ "build:fast": "lerna run build:fast", "test": "lerna run test", "test:coverage": "lerna run test:coverage", + "test:main": "lerna run test --scope=netcodejs --stream", + "test:main:coverage": "lerna run test:coverage --scope=netcodejs --stream", "prepare": "lerna bootstrap && husky install" }, "lint-staged": { diff --git a/packages/netcode/test/blash.test.ts b/packages/netcode/test/blash.test.ts index 937b795..ce2b897 100644 --- a/packages/netcode/test/blash.test.ts +++ b/packages/netcode/test/blash.test.ts @@ -156,6 +156,7 @@ describe("entity-componrnt", () => { expect(ent.mget(LogicComponent)).toStrictEqual([logic, newLogic]); expect(view.get(ViewComponentNoDecoration)).toBeNull(); expect(ent.mget(ViewComponentNoDecoration)).toStrictEqual([]); + expect(ent.mget(LengthArrComp)).toStrictEqual([]); expect(view.get(ArrComp)).toBeNull(); expect(ent.has(ViewComponentNoDecoration)).toBeFalsy(); expect(view.$comps.view === view); @@ -169,6 +170,11 @@ describe("entity-componrnt", () => { expect(hasView).toBeTruthy(); // expect(getView).not.toBeTruthy(); expect(getView).toBeTruthy(); + + expect(ent.indexOf(view)).toBe(1); + expect(ent.indexOf(newLogic)).toBe(3); + expect(ent.indexOf(new ArrComp())).toBeLessThan(0); + expect(ent.indexOf(null!)).toBeLessThan(0); }); test("addComp-white-no-decoration", () => { expect(() => { diff --git a/packages/netcode/test/rpc.test.ts b/packages/netcode/test/rpc.test.ts index 0045bdd..210f0b5 100644 --- a/packages/netcode/test/rpc.test.ts +++ b/packages/netcode/test/rpc.test.ts @@ -83,9 +83,12 @@ describe("rpc-return-type", () => { class Console extends IComp { @NetVar(DataType.F32) callCount = 0; + @NetVar(DataType.DOUBLE) + num: number = 0; @Rpc(Role.AUTHORITY, DataType.BOOL) - log(@RpcVar(DataType.INT) value: number) { + log(num: number, @RpcVar(DataType.INT) value: number) { + this.num = num; if (value <= 0) return Promise.resolve(false); console.log(`[${this.domain.name}]I am number: ${value}`); return Promise.resolve(true); @@ -109,22 +112,26 @@ describe("rpc-return-type", () => { const clientLogic = client.get(serverEnt.id)!.get(Console)!; let check = false; - clientLogic.log(1).then((result) => { + clientLogic.log(123, 1).then((result) => { check = result; }); server.setData(client.asData()); await wait(); + expect(serverLogic.num).toBe(123); client.setData(server.asData()); await wait(); expect(check).toEqual(true); + expect(clientLogic.num).toBe(123); - clientLogic.log(0).then((result) => { + clientLogic.log(124, 0).then((result) => { check = result; }); server.setData(client.asData()); await wait(); + expect(serverLogic.num).toBe(124); client.setData(server.asData()); await wait(); expect(check).toEqual(false); + expect(clientLogic.num).toBe(124); }); });