const Item = require("./item"); const EventEmitter = require("events"); var nextId = 0; class Room extends EventEmitter { constructor(map) { super(); this.id = nextId++; this.players = {}; // id => Player this.items = {}; // key => Item this.map = map; // server tags sent by the first client to connect, so we know some stuff about the level this.serverTagsSent = false; this.tagsByType = {}; //type => Item this.tagsByName = {}; this.tags = []; this.tagsByGroup = {}; this.nextSpawn = 0; this.nextPlayerOwner = 0; } addPlayer(p) { if (this.players[p] !== undefined) return false; this.players[p.id] = p; p.room = this; p.sendMessage("enter_room", {room_id:this.id, map:this.map, items:Object.values(this.items).map(i => i.getJSON())}); this.broadcastMessage("new_player", {player_id:p.id}); p.addToRoom(); if (this.serverTagsSent) { this.spawnPlayer(p); } return true; } spawnPlayer(p) { console.log("Spawning " + p.id); const spawns = this.tagsByType['spawn']; if (!spawns || spawns.length == 0) return; this.nextSpawn = this.nextSpawn % spawns.length; console.log("Found spawns, using " + this.nextSpawn); var spawn = spawns[this.nextSpawn]; this.nextSpawn++; this.spawnItem(p.id, 'player', {x:spawn.x, y:spawn.y, z:spawn.z}); } spawnItem(playerId, type, itemDetails) { if (this.players[playerId] !== undefined) { console.log("Spawning " + type + " for " + playerId); console.log(itemDetails); // invalid key and item_id so we know it's a new spawn let details = {...itemDetails, player_id:playerId, type:type, item_id:-1, key:'no_assigned_key'}; this.players[playerId].sendMessage('update_items', {items:[details]}); } } changeItemsOwner(items, playerId /*optional*/) { if (!Array.isArray(items)) { items = [items, playerId]; } this.broadcastMessage('take_ownership', {items:items.map(([item, pId]) => ({key:item.key, player_id:pId}))}); for (let [item, pId] of items) { console.log("Swapping " + item.key + " to " + pId); item.details['__change_ownership_player__'] = pId; } } removePlayer(p) { if (this.players[p.id] === undefined) return false; delete this.players[p.id]; var swapPlayers = Object.values(this.players); this.broadcastMessage("remove_player", {player_id:p.id}); var toDelete = []; var toChangeOwner = []; for (var item of Object.values(this.items)) { if (item.playerId == p.id) //deleteOnDisconnect { var shouldDelete = item.details.delete_on_disconnect; var shouldSwap = false; if (item.details.swap_on_disconnect) { if (swapPlayers.length == 0) { shouldDelete = true; } else { shouldSwap = true; } } if (shouldDelete) { item.details.__delete__ = true; toDelete.push(item); console.log("Deleting:"); console.log(item); item.stop(); delete this.items[item.key]; } else if (shouldSwap) //swapOnDisconnect { this.nextPlayerOwner = this.nextPlayerOwner % swapPlayers.length; toChangeOwner.push([item, swapPlayers[this.nextPlayerOwner].id]); } } } this.updateItems(toDelete); this.changeItemsOwner(toChangeOwner); //console.log(this.items); p.removeFromRoom(); if (swapPlayers.length == 0) { this.emit('lost_last_player'); } return true; } getItem(playerId, itemId) { const key = playerId + "_" + itemId; if (this.items[key] === undefined) return null; return this.items[key]; } playerMessage(player, messageStr) { const message = JSON.parse(messageStr); switch (message.type) { case 'server_tags': if (!this.serverTagsSent && message.tags !== undefined) { this.serverTagsSent = true; console.log(message.tags); for (var tag of message.tags) { const type = tag.type; const group = tag.group; const name = tag.name; this.tags.push(tag); if (tag.name) { tag.name = "" + tag.name; this.tagsByName[tag.name] = tag; } if (tag.group) { tag.group = "" + tag.group; if (this.tagsByGroup[tag.group] === undefined) this.tagsByGroup[tag.group] = []; this.tagsByGroup[tag.group].push(tag); } if (tag.type) { tag.type = "" + tag.type; if (this.tagsByType[tag.type] === undefined) this.tagsByType[tag.type] = []; this.tagsByType[tag.type].push(tag); } } for (var p of Object.values(this.players)) { this.spawnPlayer(p); } } break; case 'update_items': if (message.items !== undefined) { let toSend = []; for (var itemData of message.items) { if (itemData.item_id !== undefined) { const itemId = parseInt(itemData.item_id); let wasRenamed = false; if (itemData.__change_ownership_old_key__) { const oldKey = "" + itemData.__change_ownership_old_key__; if (this.items[oldKey] !== undefined && this.items[oldKey].details.__change_ownership_player__ == player.id) { let renameItem = this.items[oldKey]; delete this.items[oldKey]; renameItem.playerId = player.id; renameItem.itemId = itemId; renameItem.updateKey(); delete renameItem.details.__change_ownership_player__; this.items[renameItem.key] = renameItem; wasRenamed = true; console.log("Received re-ownership of " + oldKey + " to " + player.id + "_" + itemId); } } let item = this.getItem(player.id, itemId); let isNew = item == null; let isDeleted = false; if (!isNew && itemData.__delete__) { delete this.items[item.key]; isDelete = true; } if (isNew) { if (wasRenamed) { console.log("Uhoh, wasRenamed item not found!"); } item = new Item(player.id, itemId); this.items[item.key] = item; } const { item_id, player_id, key, ...cleanDetails } = itemData; item.details = cleanDetails; item.detailsUpdated(); if (isNew) { item.start(); console.log("Inserting: (" + Object.values(this.items).length + ")"); console.log(item); } if (isDelete) { item.remove(); console.log("Deleting: " + item.key); } toSend.push(item); } } //console.log("ITEMS: "); //console.log(this.items); this.updateItems(toSend, player); } break; } } updateItems(items, skipPlayer = null) { if (items.length > 0) { var toSend = items.map(i => i.getJSON()); //console.log("TOSEND: "); //console.log(toSend); this.broadcastMessage('update_items', {items:toSend}, skipPlayer); } } broadcastString(msg, skipPlayer = null) { for (var p of Object.values(this.players)) { if (p == skipPlayer) continue; p.socket.send(msg); } } broadcastMessage(type, details, skipPlayer = null) { const msg = JSON.stringify({...details, type:type}); for (var p of Object.values(this.players)) { if (p == skipPlayer) continue; p.socket.send(msg); } } remove() { for (var item of Object.values(this.items)) { item.stop(); } this.items = {}; for (var player of Object.values(this.players)) { player.removeFromRoom(); } this.players.length = 0; } } module.exports = Room;