UNPKG

@mixer/interactive-node

Version:

A NodeJS and Browser compatible client for mixer.com's interactive 2 Protocol

384 lines 14.3 kB
"use strict"; var __extends = (this && this.__extends) || (function () { var extendStatics = Object.setPrototypeOf || ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; return function (d, b) { extendStatics(d, b); function __() { this.constructor = d; } d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); }; })(); var __assign = (this && this.__assign) || Object.assign || function(t) { for (var s, i = 1, n = arguments.length; i < n; i++) { s = arguments[i]; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p]; } return t; }; Object.defineProperty(exports, "__esModule", { value: true }); var events_1 = require("events"); var Client_1 = require("../Client"); var ClockSync_1 = require("../ClockSync"); var errors_1 = require("../errors"); var merge_1 = require("../merge"); var MethodHandlerManager_1 = require("../methods/MethodHandlerManager"); var packets_1 = require("../wire/packets"); var Group_1 = require("./Group"); var StateFactory_1 = require("./StateFactory"); /** * State is a store of all of the components of an interactive session. * * It contains Scenes, Groups and Participants and keeps them up to date by listening to * interactive events which update and change them. You can query State to * examine and alter components of the interactive session. */ var State = /** @class */ (function (_super) { __extends(State, _super); /** * Constructs a new State instance. Based on the passed client type it will * hook into the appropriate methods for that type to keep itself up to date. */ function State(clientType) { var _this = _super.call(this) || this; _this.clientType = clientType; /** * A Map of group ids to their corresponding Group Object. */ _this.groups = new Map(); _this.methodHandler = new MethodHandlerManager_1.MethodHandlerManager(); _this.stateFactory = new StateFactory_1.StateFactory(); _this.scenes = new Map(); _this.world = {}; _this.participants = new Map(); _this.clockDelta = 0; _this.clockSyncer = new ClockSync_1.ClockSync({ sampleFunc: function () { return _this.client.getTime(); }, }); _this.methodHandler.addHandler('onReady', function (readyMethod) { _this.isReady = readyMethod.params.isReady; _this.emit('ready', _this.isReady); }); // Scene Events _this.methodHandler.addHandler('onSceneCreate', function (res) { res.params.scenes.forEach(function (scene) { return _this.onSceneCreate(scene); }); }); _this.methodHandler.addHandler('onSceneDelete', function (res) { _this.onSceneDelete(res.params.sceneID, res.params.reassignSceneID); }); _this.methodHandler.addHandler('onSceneUpdate', function (res) { res.params.scenes.forEach(function (scene) { return _this.onSceneUpdate(scene); }); }); // Group Events _this.methodHandler.addHandler('onGroupCreate', function (res) { res.params.groups.forEach(function (group) { return _this.onGroupCreate(group); }); }); _this.methodHandler.addHandler('onGroupDelete', function (res) { _this.onGroupDelete(res.params.groupID, res.params.reassignGroupID); }); _this.methodHandler.addHandler('onGroupUpdate', function (res) { res.params.groups.forEach(function (group) { return _this.onGroupUpdate(group); }); }); // Control Events _this.methodHandler.addHandler('onControlCreate', function (res) { var scene = _this.scenes.get(res.params.sceneID); if (scene) { scene.onControlsCreated(res.params.controls); } }); _this.methodHandler.addHandler('onControlDelete', function (res) { var scene = _this.scenes.get(res.params.sceneID); if (scene) { scene.onControlsDeleted(res.params.controls); } }); _this.methodHandler.addHandler('onControlUpdate', function (res) { var scene = _this.scenes.get(res.params.sceneID); if (scene) { scene.onControlsUpdated(res.params.controls); } }); _this.methodHandler.addHandler('onWorldUpdate', function (res) { // A WHOLE NEW WORLD // A NEW FANTASTIC POINT OF VIEW // Cloned to preserve original var newWorld = Object.assign({}, res.params); // Filter scenes out delete newWorld.scenes; _this.onWorldUpdate(newWorld); res.params.scenes.forEach(function (scene) { return _this.onSceneUpdate(scene); }); }); _this.clockSyncer.on('delta', function (delta) { _this.clockDelta = delta; }); if (_this.clientType === Client_1.ClientType.GameClient) { _this.addGameClientHandlers(); } else { _this.addParticipantHandlers(); } return _this; } /** * Synchronize scenes takes a collection of scenes from the server * and hydrates the Scene store with them. */ State.prototype.synchronizeScenes = function (data) { var _this = this; return data.scenes.map(function (scene) { return _this.onSceneCreate(scene); }); }; State.prototype.synchronizeGroups = function (data) { var _this = this; return data.groups.map(function (group) { return _this.onGroupCreate(group); }); }; State.prototype.addParticipantHandlers = function () { var _this = this; // A participant only gets onParticipantUpdate/Join events for themselves. this.methodHandler.addHandler('onParticipantUpdate', function (res) { _this.emit('selfUpdate', res.params.participants[0]); }); this.methodHandler.addHandler('onParticipantJoin', function (res) { _this.emit('selfUpdate', res.params.participants[0]); }); }; State.prototype.addGameClientHandlers = function () { var _this = this; this.methodHandler.addHandler('onParticipantJoin', function (res) { res.params.participants.forEach(function (participant) { _this.participants.set(participant.sessionID, participant); _this.emit('participantJoin', participant); }); }); this.methodHandler.addHandler('onParticipantLeave', function (res) { res.params.participants.forEach(function (participant) { _this.participants.delete(participant.sessionID); _this.emit('participantLeave', participant.sessionID, participant); }); }); this.methodHandler.addHandler('onParticipantUpdate', function (res) { res.params.participants.forEach(function (participant) { merge_1.merge(_this.participants.get(participant.sessionID), participant); }); }); this.methodHandler.addHandler('giveInput', function (res) { var control = _this.getControl(res.params.input.controlID); if (control) { var participant = _this.getParticipantBySessionID(res.params.participantID); control.receiveInput(res.params, participant); } }); }; State.prototype.setClient = function (client) { var _this = this; this.client = client; this.client.on('open', function () { return _this.clockSyncer.start(); }); this.client.on('close', function () { return _this.clockSyncer.stop(); }); this.stateFactory.setClient(client); }; /** * Processes a server side method using State's method handler. */ State.prototype.processMethod = function (method) { try { return this.methodHandler.handle(method); } catch (e) { if (e instanceof errors_1.InteractiveError.Base) { return packets_1.Reply.fromError(method.id, e); } throw e; } }; /** * Returns the local time matched to the sync of the Mixer server clock. */ State.prototype.synchronizeLocalTime = function (time) { if (time === void 0) { time = Date.now(); } if (time instanceof Date) { time = time.getTime(); } return new Date(time - this.clockDelta); }; /** * Returns the remote time matched to the local clock. */ State.prototype.synchronizeRemoteTime = function (time) { if (time instanceof Date) { time = time.getTime(); } return new Date(time + this.clockDelta); }; /** * Completely clears this state instance emptying all Scene, Group and Participant records */ State.prototype.reset = function () { this.scenes.forEach(function (scene) { return scene.destroy(); }); this.scenes.clear(); this.clockDelta = 0; this.isReady = false; this.participants.clear(); this.groups.clear(); }; /** * Updates an existing scene in the game session. */ State.prototype.onSceneUpdate = function (scene) { var targetScene = this.getScene(scene.sceneID); if (targetScene) { targetScene.update(scene); } }; /** * Removes a scene and reassigns the groups that were on it. */ State.prototype.onSceneDelete = function (sceneID, reassignSceneID) { var targetScene = this.getScene(sceneID); if (targetScene) { targetScene.destroy(); this.scenes.delete(sceneID); this.emit('sceneDeleted', sceneID, reassignSceneID); } }; /** * Inserts a new scene into the game session. */ State.prototype.onSceneCreate = function (data) { var scene = this.scenes.get(data.sceneID); if (scene) { this.onSceneUpdate(data); return scene; } scene = this.stateFactory.createScene(data); if (data.controls) { scene.onControlsCreated(data.controls); } this.scenes.set(data.sceneID, scene); this.emit('sceneCreated', scene); return scene; }; /** * Adds an array of Scenes to its state store. */ State.prototype.addScenes = function (scenes) { var _this = this; return scenes.map(function (scene) { return _this.onSceneCreate(scene); }); }; /** * Updates an existing scene in the game session. */ State.prototype.onGroupUpdate = function (group) { var targetGroup = this.getGroup(group.groupID); if (targetGroup) { targetGroup.update(group); } }; /** * Removes a group and reassigns the participants that were in it. */ State.prototype.onGroupDelete = function (groupID, reassignGroupID) { var targetGroup = this.getGroup(groupID); if (targetGroup) { targetGroup.destroy(); this.groups.delete(groupID); this.emit('groupDeleted', groupID, reassignGroupID); } }; /** * Inserts a new group into the game session. */ State.prototype.onGroupCreate = function (data) { var group = this.groups.get(data.groupID); if (group) { this.onGroupUpdate(data); return group; } group = new Group_1.Group(data); this.groups.set(data.groupID, group); this.emit('groupCreated', group); return group; }; /** * Merges in new world properties and emits an event to any listeners. */ State.prototype.onWorldUpdate = function (data) { this.world = __assign({}, this.world, data); this.emit('worldUpdated', this.world); }; State.prototype.getWorld = function () { return this.world; }; /** * Retrieve all groups. */ State.prototype.getGroups = function () { return this.groups; }; /** * Retrieve a group with the matching ID from the group store. */ State.prototype.getGroup = function (id) { return this.groups.get(id); }; /** * Retrieve all scenes */ State.prototype.getScenes = function () { return this.scenes; }; /** * Retrieve a scene with the matching ID from the scene store. */ State.prototype.getScene = function (id) { return this.scenes.get(id); }; /** * Searches through all stored Scenes to find a Control with the matching ID */ State.prototype.getControl = function (id) { var result; this.scenes.forEach(function (scene) { var found = scene.getControl(id); if (found) { result = found; } }); return result; }; /** * Retrieve all participants. */ State.prototype.getParticipants = function () { return this.participants; }; State.prototype.getParticipantBy = function (field, value) { var result; this.participants.forEach(function (participant) { if (participant[field] === value) { result = participant; } }); return result; }; /** * Retrieve a participant by their Mixer UserId. */ State.prototype.getParticipantByUserID = function (id) { return this.getParticipantBy('userID', id); }; /** * Retrieve a participant by their Mixer Username. */ State.prototype.getParticipantByUsername = function (name) { return this.getParticipantBy('username', name); }; /** * Retrieve a participant by their sessionID with the current Interactive session. */ State.prototype.getParticipantBySessionID = function (id) { return this.participants.get(id); }; return State; }(events_1.EventEmitter)); exports.State = State; //# sourceMappingURL=State.js.map