@geheimgang188/fmod-service-api
Version:
FMOD service API
317 lines • 24.3 kB
JavaScript
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
var __awaiter = (this && this.__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 __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.FmodZeromqApi = exports.ConnectionState = void 0;
const zmq = __importStar(require("zeromq"));
const tiny_typed_emitter_1 = require("tiny-typed-emitter");
const small_state_machine_1 = require("small-state-machine");
const semaphore_promise_1 = __importDefault(require("semaphore-promise"));
var ConnectionState;
(function (ConnectionState) {
ConnectionState["Disconnected"] = "Disconnected";
ConnectionState["Connecting"] = "Connecting";
ConnectionState["Connected"] = "Connected";
ConnectionState["Disconnecting"] = "Disconnecting";
})(ConnectionState || (exports.ConnectionState = ConnectionState = {}));
var Events;
(function (Events) {
Events["connect"] = "connect";
Events["connected"] = "connected";
Events["disconnect"] = "disconnect";
Events["disconnected"] = "disconnected";
})(Events || (Events = {}));
class FmodZeromqApi extends tiny_typed_emitter_1.TypedEmitter {
constructor(address, args) {
var _a, _b;
super();
this._verboseLogging = false;
this._logger = args === null || args === void 0 ? void 0 : args.logger;
this._verboseLogging = (args === null || args === void 0 ? void 0 : args.logger) !== undefined;
this._zmqAddress = address;
this._heartbeatInterval = (_a = args === null || args === void 0 ? void 0 : args.heartbeatIntervalMillis) !== null && _a !== void 0 ? _a : 4000;
this._socketStatusInterval = (_b = args === null || args === void 0 ? void 0 : args.socketStatusIntervalMillis) !== null && _b !== void 0 ? _b : 4000;
this._socketSempahore = new semaphore_promise_1.default(1);
this._sm = new small_state_machine_1.SmallStateMachine(ConnectionState.Disconnected);
this._sm.configure(ConnectionState.Disconnected)
.onEntry(() => this.onDisconnected())
.permit(Events.connect, ConnectionState.Connecting) // Manually calling connect()
.permit(Events.connected, ConnectionState.Connected) // Socket became available again
.ignore(Events.disconnected);
this._sm.configure(ConnectionState.Connecting)
.onEntry(() => this.onConnecting())
.permit(Events.connected, ConnectionState.Connected)
.permit(Events.disconnected, ConnectionState.Disconnected)
.ignore(Events.connect);
this._sm.configure(ConnectionState.Connected)
.onEntry(() => this.onConnected())
.permit(Events.disconnected, ConnectionState.Disconnected)
.ignore(Events.connect)
.ignore(Events.connected);
this._sm.configure(ConnectionState.Disconnecting)
.onEntry(() => this.onDisconnecting())
.permit(Events.disconnected, ConnectionState.Disconnected)
.ignore(Events.disconnect);
this._sm.onStateChange(newState => { var _a; return (_a = this._logger) === null || _a === void 0 ? void 0 : _a.debug(`Now in state ${newState}`); });
}
get connectionState() {
return this._sm.currentState;
}
get verboseLogging() {
return this._verboseLogging;
}
set verboseLogging(verbose) {
this._verboseLogging = verbose;
}
connect() {
this._sm.fire(Events.connect);
}
disconnect() {
this._sm.fire(Events.disconnected);
}
/**
* Start an event; it can be stopped again
* @param event
*/
start(event) {
return __awaiter(this, void 0, void 0, function* () {
const command = `start-event:${event}`;
yield this.sendCommand(command);
});
}
/**
* Stop a running event
* @param event
*/
stop(event) {
return __awaiter(this, void 0, void 0, function* () {
const command = `stop-event:${event}`;
yield this.sendCommand(command);
});
}
stopStartedEvents() {
return __awaiter(this, void 0, void 0, function* () {
const command = 'stop-started-events';
yield this.sendCommand(command);
});
}
/**
* Play an event (fire-and-forget)
* @param event
*/
play(event) {
return __awaiter(this, void 0, void 0, function* () {
const command = `play-event:${event}`;
yield this.sendCommand(command);
});
}
loadBank(bankName) {
return __awaiter(this, void 0, void 0, function* () {
const command = `load-bank:${bankName}`;
yield this.sendCommand(command);
});
}
unloadBank(bankName) {
return __awaiter(this, void 0, void 0, function* () {
const command = `unload-bank:${bankName}`;
yield this.sendCommand(command);
});
}
setParameter(eventId, name, value) {
return __awaiter(this, void 0, void 0, function* () {
const command = `set-parameter:${eventId};${name};${value}`;
yield this.sendCommand(command);
});
}
playVoice(eventId, key) {
return __awaiter(this, void 0, void 0, function* () {
const command = `play-voice:${eventId};${key}`;
yield this.sendCommand(command);
});
}
isPlaying(eventId) {
throw new Error('Method not implemented.');
}
listLoadedBankPaths() {
return __awaiter(this, void 0, void 0, function* () {
const command = 'list-bank-paths';
const list = yield this.sendCommand(command);
return list
.split(';')
.map(el => el.replace(/^bank:\//, ''))
.filter(el => el.length > 0);
});
}
doConnect() {
var _a, _b;
if (this._socket !== undefined)
throw new Error('Socket already exists!');
this._socket = new zmq.Request();
/*
// Connection timeouts may be helpful against calls piling up
this._socket.connectTimeout = 2000;
this._socket.sendTimeout = 200;
this._socket.receiveTimeout = 2000;
*/
(_a = this._logger) === null || _a === void 0 ? void 0 : _a.debug(`ZMQ socket connecting to ${this._zmqAddress}`);
this._socket.connect(this._zmqAddress);
this._verboseLogging && ((_b = this._logger) === null || _b === void 0 ? void 0 : _b.debug(`Setting up heartbeat and status polling`));
// Regularly send message to the API to check if it is still online
if (this._heartbeatPoll === undefined) {
this._heartbeatPoll = setInterval(() => this.checkHeartbeat(), this._heartbeatInterval);
}
// Check if socket is writable; changes to false when it goes offline
if (this._socketStatusPoll === undefined) {
let lastWritableStatus = false;
// TODO When the socket is not available, the calls pile up and are sent all at once when the socket becomes available.
// Is there a better way? Not sending calls at all does not update socket.writable status …
const checkConnection = () => __awaiter(this, void 0, void 0, function* () {
if (this._socket === undefined)
return;
const release = yield this._socketSempahore.acquire();
try {
// Socket can be …
// closed → no connection
// writable → all fine. This is how it should be after sending and receiving a message.
// readable → only when we did not read the response, but the API should always read after writing
const writableStatus = this._socket.writable;
if (writableStatus !== lastWritableStatus) {
lastWritableStatus = writableStatus;
this._sm.fire(writableStatus ? Events.connected : Events.disconnected);
}
}
finally {
release();
}
});
this._socketStatusPoll = setInterval(checkConnection, this._socketStatusInterval);
setImmediate(checkConnection);
}
}
doDisconnect() {
var _a;
(_a = this._logger) === null || _a === void 0 ? void 0 : _a.debug('Disconnecting …');
if (this._socket !== undefined) {
this._socket.disconnect(this._zmqAddress);
this._socket = undefined;
}
if (this._heartbeatPoll !== undefined) {
clearInterval(this._heartbeatPoll);
this._heartbeatPoll = undefined;
}
if (this._socketStatusPoll !== undefined) {
clearInterval(this._socketStatusPoll);
this._socketStatusPoll = undefined;
}
this._sm.fire(Events.disconnected);
}
sendCommand(command) {
return __awaiter(this, void 0, void 0, function* () {
var _a, _b, _c;
if (this._socket === undefined)
throw new Error(`Socket not initialised; did you call init()?`);
let msg = '';
const release = yield this._socketSempahore.acquire();
this._verboseLogging && ((_a = this._logger) === null || _a === void 0 ? void 0 : _a.trace(`Sending: ${command}`));
try {
/*
// Setting the sending timeout may be helpful. Needs further examination.
this._logger?.info( `Send timeout is ${this._socket.sendTimeout}` );
this._socket.sendTimeout = 200;
this._logger?.info( `Send timeout set to ${this._socket.sendTimeout}` );
*/
// After sending a message to the socket, it is not writable anymore (and hopefully not closed)
const sendPromise = this._socket.send(command);
const [response] = yield this._socket.receive();
this._verboseLogging && ((_b = this._logger) === null || _b === void 0 ? void 0 : _b.trace(`Received: ${response}`));
msg = response.toString('utf-8');
if (msg.startsWith('Error:')) {
throw new Error(msg);
}
}
finally {
release();
}
this._verboseLogging && ((_c = this._logger) === null || _c === void 0 ? void 0 : _c.trace(`Done sending ${command}`));
return msg;
});
}
checkHeartbeat() {
return __awaiter(this, void 0, void 0, function* () {
var _a;
try {
const id = yield this.sendCommand('get:id');
if (this._lastId !== id) {
if (this._lastId !== undefined) {
process.nextTick(() => this.emit('reconnect'));
}
this._lastId = id;
}
this._sm.fire(Events.connected);
}
catch (err) {
if (this._sm.currentState !== ConnectionState.Disconnected) {
this._sm.fire(Events.disconnected);
(_a = this._logger) === null || _a === void 0 ? void 0 : _a.warn('FMOD has gone:', err);
}
}
});
}
onConnecting() {
this.doConnect();
}
onConnected() {
process.nextTick(() => this.emit('connect'));
}
onDisconnecting() {
this.doDisconnect();
}
onDisconnected() {
process.nextTick(() => this.emit('disconnect'));
}
}
exports.FmodZeromqApi = FmodZeromqApi;
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZm1vZC16ZXJvbXEtYXBpLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vc3JjL2FwaS9mbW9kLXplcm9tcS1hcGkudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O0FBQ0EsNENBQThCO0FBQzlCLDJEQUFrRDtBQUNsRCw2REFBd0Q7QUFDeEQsMEVBQTBDO0FBTTFDLElBQVksZUFLWDtBQUxELFdBQVksZUFBZTtJQUN2QixnREFBNkIsQ0FBQTtJQUM3Qiw0Q0FBeUIsQ0FBQTtJQUN6QiwwQ0FBdUIsQ0FBQTtJQUN2QixrREFBK0IsQ0FBQTtBQUNuQyxDQUFDLEVBTFcsZUFBZSwrQkFBZixlQUFlLFFBSzFCO0FBRUQsSUFBSyxNQUtKO0FBTEQsV0FBSyxNQUFNO0lBQ1AsNkJBQW1CLENBQUE7SUFDbkIsaUNBQXVCLENBQUE7SUFDdkIsbUNBQXlCLENBQUE7SUFDekIsdUNBQTZCLENBQUE7QUFDakMsQ0FBQyxFQUxJLE1BQU0sS0FBTixNQUFNLFFBS1Y7QUFRRCxNQUFhLGFBQWMsU0FBUSxpQ0FBOEI7SUFrQjdELFlBQWEsT0FBZSxFQUFFLElBQXdCOztRQUNsRCxLQUFLLEVBQUUsQ0FBQztRQUhKLG9CQUFlLEdBQUcsS0FBSyxDQUFDO1FBSzVCLElBQUksQ0FBQyxPQUFPLEdBQUcsSUFBSSxhQUFKLElBQUksdUJBQUosSUFBSSxDQUFFLE1BQU0sQ0FBQztRQUM1QixJQUFJLENBQUMsZUFBZSxHQUFHLENBQUEsSUFBSSxhQUFKLElBQUksdUJBQUosSUFBSSxDQUFFLE1BQU0sTUFBSyxTQUFTLENBQUM7UUFFbEQsSUFBSSxDQUFDLFdBQVcsR0FBRyxPQUFPLENBQUM7UUFDM0IsSUFBSSxDQUFDLGtCQUFrQixHQUFHLE1BQUEsSUFBSSxhQUFKLElBQUksdUJBQUosSUFBSSxDQUFFLHVCQUF1QixtQ0FBSSxJQUFJLENBQUM7UUFDaEUsSUFBSSxDQUFDLHFC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