-
Notifications
You must be signed in to change notification settings - Fork 7
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit 30dfb00
Showing
13 changed files
with
504 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
node_modules/ | ||
.idea |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
semi: false # 不添加分号 | ||
singleQuote: true # js 内使用单引号 | ||
trailingComma: 'all' # 末尾总是添加逗号 | ||
arrowParens: 'always' # 参数总是用括号包裹 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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/) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 {}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 {}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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" | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
} | ||
} |
Oops, something went wrong.