reldens
Version:
Reldens - MMORPG Platform
319 lines (299 loc) • 10.3 kB
JavaScript
/**
*
* Reldens - AudioManager
*
* Manages audio data, categories, and player configurations on the server side.
*
*/
const { AudioConst } = require('../constants');
const { Logger, sc } = require('@reldens/utils');
/**
* @typedef {import('@colyseus/core').Client} ColyseusClient
* @typedef {import('../../rooms/server/scene').RoomScene} RoomScene
*
* @typedef {Object} AudioManagerProps
* @property {Object} roomsManager
* @property {Object} dataServer
*/
class AudioManager
{
/**
* @param {AudioManagerProps} props
*/
constructor(props)
{
/** @type {Object<string, Object>} */
this.categories = {};
/** @type {Object<string, Object>} */
this.globalAudios = {};
/** @type {Object<string, Object>} */
this.roomsAudios = {};
/** @type {Object} */
this.roomsManager = props.roomsManager;
/** @type {Object} */
this.dataServer = props.dataServer;
this.setRepositories();
}
/**
* @returns {boolean}
*/
setRepositories()
{
if(!this.dataServer){
Logger.error('DataServer undefined in AudioManager.');
return false;
}
this.audioRepository = this.dataServer.getEntity('audio');
this.audioPlayerConfigRepository = this.dataServer.getEntity('audioPlayerConfig');
this.audioCategoriesRepository = this.dataServer.getEntity('audioCategories');
}
/**
* @returns {Promise<void>}
*/
async loadAudioCategories()
{
// @TODO - BETA - Include config fields in audio categories table.
this.categories = await this.audioCategoriesRepository.loadBy('enabled', 1);
}
/**
* @returns {Promise<Object>}
*/
async loadGlobalAudios()
{
if(0 === Object.keys(this.globalAudios).length){
let loadedGlobalAudios = await this.audioRepository.loadWithRelations(
{room_id: null, enabled: 1},
['related_audio_categories', 'related_audio_markers']
);
this.globalAudios = sc.convertObjectsArrayToObjectByKeys(
this.convertAudiosConfigJsonToObjects(loadedGlobalAudios),
'audio_key'
);
}
return this.globalAudios;
}
/**
* @param {Array<Object>} loadedGlobalAudios
* @returns {Array<Object>}
*/
convertAudiosConfigJsonToObjects(loadedGlobalAudios)
{
for(let audio of loadedGlobalAudios){
if(audio.config){
let convertedData = sc.toJson(audio.config);
if(convertedData){
audio.config = convertedData;
}
}
if(audio.related_audio_markers){
for(let marker of audio.related_audio_markers){
if(marker.config){
let convertedData = sc.toJson(marker.config);
if(convertedData){
marker.config = convertedData;
}
}
}
}
}
return loadedGlobalAudios;
}
/**
* @param {number} roomId
* @returns {Promise<Object>}
*/
async loadRoomAudios(roomId)
{
if(!sc.hasOwn(this.roomsAudios, roomId)){
let loadedRoomAudios = await this.audioRepository.loadWithRelations(
{room_id: roomId, enabled: 1},
['related_rooms', 'related_audio_categories', 'related_audio_markers']
);
this.roomsAudios[roomId] = sc.convertObjectsArrayToObjectByKeys(
this.convertAudiosConfigJsonToObjects(loadedRoomAudios),
'audio_key'
);
}
return this.roomsAudios[roomId];
}
/**
* @param {number} playerId
* @returns {Promise<Object>}
*/
async loadAudioPlayerConfig(playerId)
{
// @TODO - BETA - Improve login performance, avoid query by getting config from existent player schema.
let configModels = await this.audioPlayerConfigRepository.loadBy('player_id', playerId);
if(0 === configModels.length){
return {};
}
let playerConfig = {};
for(let config of configModels) {
playerConfig[config['category_id']] = config['enabled'];
}
return playerConfig;
}
/**
* @param {ColyseusClient} client
* @param {Object} message
* @param {RoomScene} room
* @returns {Promise<boolean|Object>}
*/
async executeMessageActions(client, message, room)
{
if(message.act !== AudioConst.AUDIO_UPDATE){
return false;
}
let currentPlayer = room.playerBySessionIdFromState(client.sessionId);
let audioCategory = await this.audioCategoriesRepository.loadOneBy(
'category_key',
message[AudioConst.MESSAGE.DATA.UPDATE_TYPE]
);
if(!currentPlayer || !currentPlayer.player_id || !audioCategory){
return false;
}
let filters = {
player_id: currentPlayer.player_id,
category_id: audioCategory.id
};
let playerConfig = await this.audioPlayerConfigRepository.loadOne(filters);
let updatePatch = {enabled: (message[AudioConst.MESSAGE.DATA.UPDATE_VALUE] ? 1 : 0)};
if(playerConfig){
return await this.audioPlayerConfigRepository.update(filters, updatePatch);
}
return await this.audioPlayerConfigRepository.create(Object.assign(updatePatch, filters));
}
/**
* @param {Object} options
* @returns {boolean}
*/
hotPlugAudio(options)
{
if(options?.newAudioModel?.room_id){
return this.hotPlugGlobalAudio(options?.newAudioModel);
}
return this.hotPlugRoomAudio(options?.newAudioModel);
}
/**
* @param {Object} newAudioModel
* @returns {boolean}
*/
hotPlugRoomAudio(newAudioModel)
{
let roomId = newAudioModel.room_id;
if(!sc.hasOwn(this.roomsAudios, roomId)){
this.roomsAudios[roomId] = {};
}
this.roomsAudios[roomId][newAudioModel.id] = newAudioModel;
let roomInstance = this.findRoom(roomId, this.roomsManager.createdInstances);
if(!roomInstance){
// @NOTE: since the room could not be created yet (because none is connected), we don't need to broadcast
// the new audio, it will be loaded automatically when the room is created.
return true;
}
roomInstance.broadcast('*', {act: AudioConst.AUDIO_UPDATE, roomId, audios: [newAudioModel]});
}
/**
* @param {Object} newAudioModel
* @returns {boolean}
*/
hotPlugGlobalAudio(newAudioModel)
{
this.globalAudios[newAudioModel.id] = newAudioModel;
let createdRooms = Object.keys(this.roomsManager.createdInstances);
if(0 === createdRooms.length){
return false;
}
for(let i of createdRooms){
let roomInstance = this.roomsManager.createdInstances[i];
let broadcastData = {
act: AudioConst.AUDIO_UPDATE,
roomId: i,
audios: {}
};
broadcastData['audios'][newAudioModel.id] = newAudioModel;
roomInstance.broadcast('*', broadcastData);
}
}
/**
* @param {Object} props
* @returns {boolean}
*/
hotUnplugAudio(props)
{
let {newAudioModel, id} = props;
if(newAudioModel.room_id){
return this.hotUnplugRoomAudio(newAudioModel, id);
}
return this.hotUnplugGlobalAudio(newAudioModel, id);
}
/**
* @param {Object} newAudioModel
* @param {number} id
* @returns {boolean}
*/
hotUnplugRoomAudio(newAudioModel, id)
{
let roomAudiosList = sc.get(this.roomsAudios, newAudioModel.room_id, false);
if(false !== roomAudiosList && sc.hasOwn(roomAudiosList, id)){
delete this.roomsAudios[newAudioModel.room_id][id];
}
let roomInstance = this.findRoom(newAudioModel.room_id, this.roomsManager.createdInstances);
if(!roomInstance){
return true;
}
let data = {
act: AudioConst.AUDIO_DELETE,
roomId: newAudioModel.room_id,
audios: {}
};
data['audios'][newAudioModel.id] = newAudioModel;
roomInstance.broadcast('*', data);
}
/**
* @param {Object} newAudioModel
* @param {number} id
* @returns {boolean}
*/
hotUnplugGlobalAudio(newAudioModel, id)
{
delete this.globalAudios[id];
let createdRooms = Object.keys(this.roomsManager.createdInstances);
if(0 === createdRooms.length){
return false;
}
for(let i of createdRooms){
let roomInstance = this.roomsManager.createdInstances[i];
let broadcastData = {
act: AudioConst.AUDIO_DELETE,
roomId: i,
audios: {}
};
broadcastData['audios'][newAudioModel.id] = newAudioModel;
roomInstance.broadcast('*', broadcastData);
}
}
/**
* @param {number} roomId
* @param {Object} [instancesList]
* @returns {Object|boolean}
*/
findRoom(roomId, instancesList = {})
{
let roomInstances = Object.keys(instancesList);
if(0 === roomInstances.length){
return false;
}
for(let i of roomInstances){
let room = instancesList[i];
if(!room.roomData){
continue;
}
if(room.roomData.roomId === roomId){
return room;
}
}
return false;
}
}
module.exports.AudioManager = AudioManager;