diff --git a/index.js b/index.js index 64c712b..202fe31 100644 --- a/index.js +++ b/index.js @@ -1,10 +1,26 @@ const WebSocket = require('ws'); const Player = require('./player'); const Room = require('./room'); +const EventEmitter = require("events"); -let lobby = new Room("Lobby"); +let lobby = null; +function createNewLobby() +{ + if (lobby) + { + console.log("Removing lobby"); + lobby.remove(); + lobby = null; + } + console.log("Creating lobby"); + lobby = new Room("Lobby"); + lobby.on('lost_last_player', createNewLobby); +} + let playersBySocket = new Map(); +createNewLobby(); + const ws = new WebSocket.Server({port: 9997}); @@ -65,6 +81,8 @@ playersBySocket.delete(conn); p.room.removePlayer(p); + + p.remove(); } } ); diff --git a/index.js b/index.js index 64c712b..202fe31 100644 --- a/index.js +++ b/index.js @@ -1,10 +1,26 @@ const WebSocket = require('ws'); const Player = require('./player'); const Room = require('./room'); +const EventEmitter = require("events"); -let lobby = new Room("Lobby"); +let lobby = null; +function createNewLobby() +{ + if (lobby) + { + console.log("Removing lobby"); + lobby.remove(); + lobby = null; + } + console.log("Creating lobby"); + lobby = new Room("Lobby"); + lobby.on('lost_last_player', createNewLobby); +} + let playersBySocket = new Map(); +createNewLobby(); + const ws = new WebSocket.Server({port: 9997}); @@ -65,6 +81,8 @@ playersBySocket.delete(conn); p.room.removePlayer(p); + + p.remove(); } } ); diff --git a/item.js b/item.js index 6a4a64d..91da176 100644 --- a/item.js +++ b/item.js @@ -4,13 +4,53 @@ { this.playerId = playerId; this.itemId = itemId; - this.key = playerId + "_" + itemId; + this.updateKey(); this.details = {}; + + this.handlers = []; + } + + updateKey() + { + this.key = this.playerId + "_" + this.itemId; } getJSON() { - return {...this.details, player_id:this.playerId, item_id:this.itemId, key:this.key}; + var ret = {...this.details, player_id:this.playerId, item_id:this.itemId, key:this.key}; + delete ret.__change_ownership_player__; + return ret; + } + + detailsUpdated() + { + for (var h of this.handlers) + { + if (typeof h.itemUpdated === "function") h.itemUpdated(); + } + } + + start() + { + } + + stop() + { + for (var h of this.handlers) + { + if (typeof h.stop === "function") h.stop(); + } + this.handlers.length = 0; + } + + removeHandler(h) + { + const index = this.handlers.indexOf(h); + if (index != -1) + { + this.handlers.splice(index, 1); + if (typeof h.stop === "function") h.stop(); + } } } diff --git a/index.js b/index.js index 64c712b..202fe31 100644 --- a/index.js +++ b/index.js @@ -1,10 +1,26 @@ const WebSocket = require('ws'); const Player = require('./player'); const Room = require('./room'); +const EventEmitter = require("events"); -let lobby = new Room("Lobby"); +let lobby = null; +function createNewLobby() +{ + if (lobby) + { + console.log("Removing lobby"); + lobby.remove(); + lobby = null; + } + console.log("Creating lobby"); + lobby = new Room("Lobby"); + lobby.on('lost_last_player', createNewLobby); +} + let playersBySocket = new Map(); +createNewLobby(); + const ws = new WebSocket.Server({port: 9997}); @@ -65,6 +81,8 @@ playersBySocket.delete(conn); p.room.removePlayer(p); + + p.remove(); } } ); diff --git a/item.js b/item.js index 6a4a64d..91da176 100644 --- a/item.js +++ b/item.js @@ -4,13 +4,53 @@ { this.playerId = playerId; this.itemId = itemId; - this.key = playerId + "_" + itemId; + this.updateKey(); this.details = {}; + + this.handlers = []; + } + + updateKey() + { + this.key = this.playerId + "_" + this.itemId; } getJSON() { - return {...this.details, player_id:this.playerId, item_id:this.itemId, key:this.key}; + var ret = {...this.details, player_id:this.playerId, item_id:this.itemId, key:this.key}; + delete ret.__change_ownership_player__; + return ret; + } + + detailsUpdated() + { + for (var h of this.handlers) + { + if (typeof h.itemUpdated === "function") h.itemUpdated(); + } + } + + start() + { + } + + stop() + { + for (var h of this.handlers) + { + if (typeof h.stop === "function") h.stop(); + } + this.handlers.length = 0; + } + + removeHandler(h) + { + const index = this.handlers.indexOf(h); + if (index != -1) + { + this.handlers.splice(index, 1); + if (typeof h.stop === "function") h.stop(); + } } } diff --git a/player.js b/player.js index ebd6b9a..8be2147 100644 --- a/player.js +++ b/player.js @@ -11,6 +11,15 @@ { this.socket.send(JSON.stringify({...details, type:type})); } + addToRoom() + { + } + removeFromRoom() + { + } + remove() + { + } } module.exports = Player; diff --git a/index.js b/index.js index 64c712b..202fe31 100644 --- a/index.js +++ b/index.js @@ -1,10 +1,26 @@ const WebSocket = require('ws'); const Player = require('./player'); const Room = require('./room'); +const EventEmitter = require("events"); -let lobby = new Room("Lobby"); +let lobby = null; +function createNewLobby() +{ + if (lobby) + { + console.log("Removing lobby"); + lobby.remove(); + lobby = null; + } + console.log("Creating lobby"); + lobby = new Room("Lobby"); + lobby.on('lost_last_player', createNewLobby); +} + let playersBySocket = new Map(); +createNewLobby(); + const ws = new WebSocket.Server({port: 9997}); @@ -65,6 +81,8 @@ playersBySocket.delete(conn); p.room.removePlayer(p); + + p.remove(); } } ); diff --git a/item.js b/item.js index 6a4a64d..91da176 100644 --- a/item.js +++ b/item.js @@ -4,13 +4,53 @@ { this.playerId = playerId; this.itemId = itemId; - this.key = playerId + "_" + itemId; + this.updateKey(); this.details = {}; + + this.handlers = []; + } + + updateKey() + { + this.key = this.playerId + "_" + this.itemId; } getJSON() { - return {...this.details, player_id:this.playerId, item_id:this.itemId, key:this.key}; + var ret = {...this.details, player_id:this.playerId, item_id:this.itemId, key:this.key}; + delete ret.__change_ownership_player__; + return ret; + } + + detailsUpdated() + { + for (var h of this.handlers) + { + if (typeof h.itemUpdated === "function") h.itemUpdated(); + } + } + + start() + { + } + + stop() + { + for (var h of this.handlers) + { + if (typeof h.stop === "function") h.stop(); + } + this.handlers.length = 0; + } + + removeHandler(h) + { + const index = this.handlers.indexOf(h); + if (index != -1) + { + this.handlers.splice(index, 1); + if (typeof h.stop === "function") h.stop(); + } } } diff --git a/player.js b/player.js index ebd6b9a..8be2147 100644 --- a/player.js +++ b/player.js @@ -11,6 +11,15 @@ { this.socket.send(JSON.stringify({...details, type:type})); } + addToRoom() + { + } + removeFromRoom() + { + } + remove() + { + } } module.exports = Player; diff --git a/room.js b/room.js index 813a0a2..d02b5a5 100644 --- a/room.js +++ b/room.js @@ -1,14 +1,26 @@ const Item = require("./item"); +const EventEmitter = require("events"); var nextId = 0; -class Room +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) { @@ -21,30 +33,114 @@ 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 && item.details.dod) //deleteOnDisconnect + if (item.playerId == p.id) //deleteOnDisconnect { - item.details.__delete__ = true; - toDelete.push(item); - console.log("Deleting:"); - console.log(item); - delete this.items[item.key]; + 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; } @@ -60,6 +156,46 @@ 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) { @@ -69,16 +205,56 @@ if (itemData.item_id !== undefined) { const itemId = parseInt(itemData.item_id); - let item = this.getItem(player.id, itemId); - if (item == null) + 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; - console.log("Inserting: (" + Object.values(this.items).length + ")"); - console.log(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); } @@ -102,7 +278,7 @@ } broadcastString(msg, skipPlayer = null) { - for (var p of Object.values(this.playeres)) + for (var p of Object.values(this.players)) { if (p == skipPlayer) continue; p.socket.send(msg); @@ -117,6 +293,20 @@ 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;