Skip to content

Commit

Permalink
创建
Browse files Browse the repository at this point in the history
  • Loading branch information
aweiu committed Sep 8, 2019
0 parents commit 30dfb00
Show file tree
Hide file tree
Showing 13 changed files with 504 additions and 0 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
node_modules/
.idea
4 changes: 4 additions & 0 deletions .prettierrc.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
semi: false # 不添加分号
singleQuote: true # js 内使用单引号
trailingComma: 'all' # 末尾总是添加逗号
arrowParens: 'always' # 参数总是用括号包裹
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# wow-state-machine

像多线程那样去轮询多个状态,不同的状态满足后去执行不同的定时任务。

该状态机基本是为写游戏自动脚本量身定做,它就是整个脚本的"调度中心"。即使是基于 Node.js 的单线程,你也能够实现"同时"检测角色血条,掉落物品,游戏状态等等各种来触发不同的操作,搭配 [dm.dll](/documents/dm.dll/) 食用更佳!

[文档地址](https://aweiu.com/documents/wow-state-machine/)
9 changes: 9 additions & 0 deletions dist/promise-interval.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
declare type PromiseFun = () => Promise<any>;
export default class PromiseInterval {
private ms;
private timer?;
constructor(ms: number);
start(promiseFun: PromiseFun, onError?: (e: Error) => any): void;
stop(): void;
}
export {};
29 changes: 29 additions & 0 deletions dist/promise-interval.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const set_promise_interval_1 = require("set-promise-interval");
class PromiseInterval {
constructor(ms) {
this.ms = ms;
}
start(promiseFun, onError) {
if (this.timer === undefined) {
this.timer = set_promise_interval_1.default(async () => {
try {
await promiseFun();
}
catch (e) {
this.stop();
if (onError)
onError(e);
else
throw e;
}
}, this.ms);
}
}
stop() {
set_promise_interval_1.clearPromiseInterval(this.timer);
this.timer = undefined;
}
}
exports.default = PromiseInterval;
32 changes: 32 additions & 0 deletions dist/state-machine.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
declare type DataOrPromiseData<T> = T | Promise<T>;
declare type Publisher<T> = () => DataOrPromiseData<T>;
declare type Subscriber = () => any;
declare type OnTick<T> = (state: T, lastState: T | undefined, isFirstTick: boolean) => any;
declare type OnError = (e: Error) => any;
declare type OnTimeout<T> = (state: T) => any;
export default class StateMachine<T extends string | number> {
private publisher;
private onTickCallback?;
private onErrorCallback?;
private onTimeoutCallback?;
private mainLoop?;
private lastState?;
private states;
private runners;
private timeoutChecker;
constructor(publisher: Publisher<T>);
private errorHandle;
private timeoutHandle;
private startTimeoutChecker;
private stopTimeoutChecker;
private startRunner;
private stopRunner;
private publish;
on(state: T, stateMachineOrSubscriber: StateMachine<any> | Subscriber, timeout?: number, tick?: number): this;
onTick(callback: OnTick<T>): this;
onError(callback: OnError): this;
onTimeout(callback: OnTimeout<T>): this;
start(tick?: number): void;
stop(): void;
}
export {};
128 changes: 128 additions & 0 deletions dist/state-machine.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const promise_interval_1 = require("./promise-interval");
class StateMachine {
constructor(publisher) {
this.states = {};
this.runners = [];
this.timeoutChecker = [];
this.publisher = publisher;
}
errorHandle(e) {
this.stop();
if (this.onErrorCallback)
this.onErrorCallback(e);
else
throw e;
}
timeoutHandle(state) {
this.stop();
if (this.onTimeoutCallback)
this.onTimeoutCallback(state);
else
throw Error(`state: ${state}超时`);
}
startTimeoutChecker(state, ms) {
if (ms) {
this.timeoutChecker.push(setTimeout(() => this.timeoutHandle(state), ms));
}
}
stopTimeoutChecker() {
for (let checker of this.timeoutChecker)
clearTimeout(checker);
this.timeoutChecker = [];
}
startRunner(stateMachineOrSubscriber, tick) {
if (stateMachineOrSubscriber instanceof StateMachine) {
try {
stateMachineOrSubscriber.onError((e) => this.errorHandle(e));
}
catch (e) {
/* tslint:disable:no-empty */
}
try {
stateMachineOrSubscriber.onTimeout((state) => this.onTimeout(state));
}
catch (e) {
/* tslint:disable:no-empty */
}
stateMachineOrSubscriber.start(tick);
this.runners.push(stateMachineOrSubscriber);
}
else {
const runner = new promise_interval_1.default(tick);
runner.start(stateMachineOrSubscriber, (e) => this.errorHandle(e));
this.runners.push(runner);
}
}
stopRunner() {
for (let runner of this.runners)
runner.stop();
this.runners = [];
}
async publish(isFirstTick) {
let state = await this.publisher();
if (this.onTickCallback)
await this.onTickCallback(state, this.lastState, isFirstTick);
if (this.lastState !== state) {
this.lastState = state;
this.stopRunner();
this.stopTimeoutChecker();
if (this.states.hasOwnProperty(state)) {
const subscribers = this
.states[state];
for (let [subscriber, timeout, tick] of subscribers) {
this.startTimeoutChecker(state, timeout);
if (tick === -Infinity) {
await subscriber();
this.stopTimeoutChecker();
}
else
this.startRunner(subscriber, tick);
}
}
}
}
on(state, stateMachineOrSubscriber, timeout = 0, tick = 200) {
if (!this.states.hasOwnProperty(state))
this.states[state] = [];
this.states[state].push([stateMachineOrSubscriber, timeout, tick]);
return this;
}
onTick(callback) {
if (this.onTickCallback)
throw Error('just allow one TickHandler');
this.onTickCallback = callback;
return this;
}
onError(callback) {
if (this.onErrorCallback)
throw Error('just allow one ErrorHandler');
this.onErrorCallback = callback;
return this;
}
onTimeout(callback) {
if (this.onTimeoutCallback)
throw Error('just allow one TimeoutHandler');
this.onTimeoutCallback = callback;
return this;
}
start(tick = 200) {
let isFirstTick = true;
this.lastState = undefined;
this.mainLoop = new promise_interval_1.default(tick);
this.mainLoop.start(() => {
const _isFirstTick = isFirstTick;
if (isFirstTick)
isFirstTick = false;
return this.publish(_isFirstTick);
}, (e) => this.errorHandle(e));
}
stop() {
if (this.mainLoop)
this.mainLoop.stop();
this.stopRunner();
this.stopTimeoutChecker();
}
}
exports.default = StateMachine;
26 changes: 26 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
{
"name": "wow-state-machine",
"version": "1.0.1",
"description": "像多线程那样去轮询多个状态,不同的状态满足后去执行不同的任务",
"keywords": [
"state-machine",
"async",
"thread",
"interval",
"timer"
],
"main": "dist/wow-state-machine.js",
"typings": "types/wow-state-machine.d.ts",
"author": "aweiu",
"license": "ISC",
"repository": {
"type": "git",
"url": "git+https://github.com/aweiu/wow-state-machine"
},
"dependencies": {
"set-promise-interval": "^1.0.2"
},
"devDependencies": {
"typescript": "^3.5.3"
}
}
31 changes: 31 additions & 0 deletions src/promise-interval.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import setPromiseInterval, { clearPromiseInterval } from 'set-promise-interval'

type PromiseFun = () => Promise<any>

export default class PromiseInterval {
private ms: number
private timer?: number

constructor(ms: number) {
this.ms = ms
}

start(promiseFun: PromiseFun, onError?: (e: Error) => any) {
if (this.timer === undefined) {
this.timer = setPromiseInterval(async () => {
try {
await promiseFun()
} catch (e) {
this.stop()
if (onError) onError(e)
else throw e
}
}, this.ms)
}
}

stop() {
clearPromiseInterval(this.timer)
this.timer = undefined
}
}
Loading

0 comments on commit 30dfb00

Please sign in to comment.