restructure realtime server for clarity (#780)

* restructure realtime server for clarity

* better indenting on signal.js

* don't need it because socketioconnection is being imported now
This commit is contained in:
Connor Turland 2016-10-18 20:29:21 -04:00 committed by GitHub
parent d8cd536a95
commit 139837e997
13 changed files with 387 additions and 4256 deletions

BIN
.env.swp

Binary file not shown.

File diff suppressed because one or more lines are too long

View file

@ -1,23 +0,0 @@
function SocketIoConnection(config) {
this.connection = io.connect(config.url, config.socketio);
}
SocketIoConnection.prototype.on = function (ev, fn) {
this.connection.on(ev, fn);
};
SocketIoConnection.prototype.emit = function () {
this.connection.emit.apply(this.connection, arguments);
};
SocketIoConnection.prototype.removeAllListeners = function () {
this.connection.removeAllListeners();
};
SocketIoConnection.prototype.getSessionid = function () {
return this.connection.socket.sessionid;
};
SocketIoConnection.prototype.disconnect = function () {
return this.connection.disconnect();
};

View file

@ -1,4 +1,4 @@
/* global Metamaps, $, SocketIoConnection */
/* global Metamaps, $ */
/*
* Metamaps.Realtime.js
@ -15,6 +15,7 @@
import _ from 'lodash'
import SimpleWebRTC from 'simplewebrtc'
import SocketIoConnection from 'simplewebrtc/socketioconnection'
import Active from '../Active'
import GlobalUI from '../GlobalUI'
@ -99,6 +100,7 @@ import {
leaveCall,
sendMapperInfo,
sendCoords,
createMessage,
dragTopic,
createTopic,
updateTopic,
@ -510,6 +512,7 @@ const sendables = [
['leaveCall',leaveCall],
['sendMapperInfo',sendMapperInfo],
['sendCoords',sendCoords],
['createMessage',createMessage],
['dragTopic',dragTopic],
['createTopic',createTopic],
['updateTopic',updateTopic],

View file

@ -40,7 +40,7 @@
"react-dom": "15.3.2",
"react-dropzone": "3.6.0",
"simplewebrtc": "2.2.0",
"socket.io": "0.9.12",
"socket.io": "1.3.7",
"underscore": "1.4.4",
"webpack": "1.13.2"
},

80
realtime/global.js Normal file
View file

@ -0,0 +1,80 @@
import {
// server sendable, client receivable
TOPIC_UPDATED,
TOPIC_DELETED,
SYNAPSE_UPDATED,
SYNAPSE_DELETED,
LIVE_MAPS_RECEIVED,
MAP_WENT_LIVE,
MAP_CEASED_LIVE,
MAP_UPDATED,
// server receivable, client sendable
REQUEST_LIVE_MAPS,
JOIN_MAP,
LEAVE_MAP,
UPDATE_TOPIC,
DELETE_TOPIC,
UPDATE_SYNAPSE,
DELETE_SYNAPSE,
UPDATE_MAP
} from '../frontend/src/Metamaps/Realtime/events'
const adjustAndBroadcast = (io, socket, state, event, data) => {
if (event === JOIN_MAP) {
if (!state.liveMaps[data.mapid]) {
state.liveMaps[data.mapid] = data.map // { name: '', desc: '', numTopics: '' }
state.liveMaps[data.mapid].mapper_count = 1
io.sockets.emit(MAP_WENT_LIVE, state.liveMaps[data.mapid])
}
else {
state.liveMaps[data.mapid].mapper_count++
}
}
else if (event === LEAVE_MAP) {
const mapid = socket.mapid
if (state.liveMaps[mapid] && state.liveMaps[mapid].mapper_count == 1) {
delete state.liveMaps[mapid]
io.sockets.emit(MAP_CEASED_LIVE, { id: mapid })
}
else if (state.liveMaps[mapid]) {
state.liveMaps[mapid].mapper_count--
}
}
}
module.exports = function (io, state) {
io.on('connection', function (socket) {
socket.on(REQUEST_LIVE_MAPS, function (activeUser) {
//constrain response to maps visible to user
var maps = Object.keys(state.liveMaps).map(function(key) { return state.liveMaps[key] })
socket.emit(LIVE_MAPS_RECEIVED, maps)
})
socket.on(JOIN_MAP, data => adjustAndBroadcast(io, socket, state, JOIN_MAP, data))
socket.on(LEAVE_MAP, () => adjustAndBroadcast(io, socket, state, LEAVE_MAP))
socket.on('disconnect', () => adjustAndBroadcast(io, socket, state, LEAVE_MAP))
socket.on(UPDATE_TOPIC, function (data) {
socket.broadcast.emit(TOPIC_UPDATED, data)
})
socket.on(DELETE_TOPIC, function (data) {
socket.broadcast.emit(TOPIC_DELETED, data)
})
socket.on(UPDATE_SYNAPSE, function (data) {
socket.broadcast.emit(SYNAPSE_UPDATED, data)
})
socket.on(DELETE_SYNAPSE, function (data) {
socket.broadcast.emit(SYNAPSE_DELETED, data)
})
socket.on(UPDATE_MAP, function (data) {
socket.broadcast.emit(MAP_UPDATED, data)
})
})
}

62
realtime/junto.js Normal file
View file

@ -0,0 +1,62 @@
import {
INVITED_TO_CALL,
INVITED_TO_JOIN,
CALL_ACCEPTED,
CALL_DENIED,
INVITE_DENIED,
CALL_IN_PROGRESS,
CALL_STARTED,
MAPPER_JOINED_CALL,
MAPPER_LEFT_CALL,
CHECK_FOR_CALL,
ACCEPT_CALL,
DENY_CALL,
DENY_INVITE,
INVITE_TO_JOIN,
INVITE_A_CALL,
JOIN_CALL,
LEAVE_CALL
} from '../frontend/src/Metamaps/Realtime/events'
const { mapRoom, userMapRoom } = require('./rooms')
module.exports = function (io, state) {
io.on('connection', function (socket) {
socket.on(CHECK_FOR_CALL, function (data) {
var callInProgress = Object.keys(io.nsps['/'].adapter.rooms[data.room] || {}).length
if (callInProgress) socket.emit(CALL_IN_PROGRESS)
})
socket.on(INVITE_A_CALL, function (data) {
socket.broadcast.in(userMapRoom(data.invited, data.mapid)).emit(INVITED_TO_CALL, data.inviter)
})
socket.on(INVITE_TO_JOIN, function (data) {
socket.broadcast.in(userMapRoom(data.invited, data.mapid)).emit(INVITED_TO_JOIN, data.inviter)
})
socket.on(ACCEPT_CALL, function (data) {
socket.broadcast.in(userMapRoom(data.inviter, data.mapid)).emit(CALL_ACCEPTED, data.invited)
socket.broadcast.in(mapRoom(data.mapid)).emit(CALL_STARTED)
})
socket.on(DENY_CALL, function (data) {
socket.broadcast.in(userMapRoom(data.inviter, data.mapid)).emit(CALL_DENIED, data.invited)
})
socket.on(DENY_INVITE, function (data) {
socket.broadcast.in(userMapRoom(data.inviter, data.mapid)).emit(INVITE_DENIED, data.invited)
})
socket.on(JOIN_CALL, function (data) {
socket.broadcast.in(mapRoom(data.mapid)).emit(MAPPER_JOINED_CALL, data.id)
})
socket.on(LEAVE_CALL, function (data) {
socket.broadcast.in(mapRoom(data.mapid)).emit(MAPPER_LEFT_CALL, data.id)
})
})
}

115
realtime/map.js Normal file
View file

@ -0,0 +1,115 @@
import {
MAPPER_LIST_UPDATED,
NEW_MAPPER,
LOST_MAPPER,
MESSAGE_CREATED,
TOPIC_DRAGGED,
TOPIC_CREATED,
TOPIC_REMOVED,
SYNAPSE_CREATED,
SYNAPSE_REMOVED,
PEER_COORDS_UPDATED,
JOIN_MAP,
LEAVE_MAP,
SEND_MAPPER_INFO,
SEND_COORDS,
CREATE_MESSAGE,
DRAG_TOPIC,
CREATE_TOPIC,
REMOVE_TOPIC,
CREATE_SYNAPSE,
REMOVE_SYNAPSE
} from '../frontend/src/Metamaps/Realtime/events'
const { mapRoom, userMapRoom } = require('./rooms')
module.exports = function (io, state) {
io.on('connection', function (socket) {
// this will ping everyone on a map that there's a person just joined the map
socket.on(JOIN_MAP, function (data) {
socket.mapid = data.mapid
socket.userid = data.userid
socket.username = data.username
socket.userimage = data.userimage
var newUser = {
userid: data.userid,
username: data.username,
userimage: data.userimage
}
socket.join(mapRoom(data.mapid))
socket.join(userMapRoom(data.userid, data.mapid))
socket.broadcast.in(mapRoom(data.mapid)).emit(NEW_MAPPER, newUser)
})
const leaveMap = () => {
var data = {
username: socket.username,
userid: socket.userid
}
socket.leave(mapRoom(socket.mapid))
socket.leave(userMapRoom(socket.userid, socket.mapid))
socket.broadcast.in(mapRoom(socket.mapid)).emit(LOST_MAPPER, data)
socket.mapid = null
}
socket.on(LEAVE_MAP, leaveMap)
socket.on('disconnect', leaveMap)
// this will ping a new person with awareness of who's already on the map
socket.on(SEND_MAPPER_INFO, function (data) {
var existingUser = {
userid: data.userid,
username: data.username,
userinconversation: data.userinconversation,
userimage: data.userimage
}
socket.broadcast.in(userMapRoom(data.userToNotify, data.mapid)).emit(MAPPER_LIST_UPDATED, existingUser)
})
socket.on(SEND_COORDS, function (data) {
var peer = {
userid: data.userid,
usercoords: data.usercoords
}
socket.broadcast.in(mapRoom(data.mapid)).emit(PEER_COORDS_UPDATED, peer)
})
socket.on(CREATE_MESSAGE, function (data) {
var mapId = data.mapid
delete data.mapid
socket.broadcast.in(mapRoom(mapId)).emit(MESSAGE_CREATED, data)
})
socket.on(DRAG_TOPIC, function (data) {
var mapId = data.mapid
delete data.mapid
socket.broadcast.in(mapRoom(mapId)).emit(TOPIC_DRAGGED, data)
})
socket.on(CREATE_TOPIC, function (data) {
var mapId = data.mapid
delete data.mapid
socket.broadcast.in(mapRoom(mapId)).emit(TOPIC_CREATED, data)
})
socket.on(REMOVE_TOPIC, function (data) {
var mapId = data.mapid
delete data.mapid
socket.broadcast.in(mapRoom(mapId)).emit(TOPIC_REMOVED, data)
})
socket.on(CREATE_SYNAPSE, function (data) {
var mapId = data.mapid
delete data.mapid
socket.broadcast.in(mapRoom(mapId)).emit(SYNAPSE_CREATED, data)
})
socket.on(REMOVE_SYNAPSE, function (data) {
var mapId = data.mapid
delete data.mapid
socket.broadcast.in(mapRoom(mapId)).emit(SYNAPSE_REMOVED, data)
})
})
}

View file

@ -1,267 +1,18 @@
var
io = require('socket.io').listen(5001),
signalServer = require('./signal'),
io = require('socket.io')(),
signalling = require('./signal'),
junto = require('./junto'),
map = require('./map'),
global = require('./global'),
stunservers = [{"url": "stun:stun.l.google.com:19302"}]
import {
// server sendable, client receivable
INVITED_TO_CALL,
INVITED_TO_JOIN,
CALL_ACCEPTED,
CALL_DENIED,
INVITE_DENIED,
CALL_IN_PROGRESS,
CALL_STARTED,
MAPPER_JOINED_CALL,
MAPPER_LEFT_CALL,
MAPPER_LIST_UPDATED,
NEW_MAPPER,
LOST_MAPPER,
MESSAGE_CREATED,
TOPIC_DRAGGED,
TOPIC_CREATED,
TOPIC_UPDATED,
TOPIC_REMOVED,
TOPIC_DELETED,
SYNAPSE_CREATED,
SYNAPSE_UPDATED,
SYNAPSE_REMOVED,
SYNAPSE_DELETED,
PEER_COORDS_UPDATED,
LIVE_MAPS_RECEIVED,
MAP_WENT_LIVE,
MAP_CEASED_LIVE,
MAP_UPDATED,
// server receivable, client sendable
REQUEST_LIVE_MAPS,
JOIN_MAP,
LEAVE_MAP,
CHECK_FOR_CALL,
ACCEPT_CALL,
DENY_CALL,
DENY_INVITE,
INVITE_TO_JOIN,
INVITE_A_CALL,
JOIN_CALL,
LEAVE_CALL,
REQUEST_MAPPER_INFO,
SEND_MAPPER_INFO,
SEND_COORDS,
CREATE_MESSAGE,
DRAG_TOPIC,
CREATE_TOPIC,
UPDATE_TOPIC,
REMOVE_TOPIC,
DELETE_TOPIC,
CREATE_SYNAPSE,
UPDATE_SYNAPSE,
REMOVE_SYNAPSE,
DELETE_SYNAPSE,
UPDATE_MAP
} from '../frontend/src/Metamaps/Realtime/events'
io.set('log', false)
function start() {
var livemaps = {}
signalServer(io, stunservers)
io.on('connection', function (socket) {
socket.on(REQUEST_LIVE_MAPS, function (activeUser) {
//constrain response to maps visible to user
var maps = Object.keys(livemaps).map(function(key) { return livemaps[key] })
socket.emit(LIVE_MAPS_RECEIVED, maps)
})
// this will ping a new person with awareness of who's already on the map
socket.on(SEND_MAPPER_INFO, function (data) {
var existingUser = {
userid: data.userid,
username: data.username,
userrealtime: data.userrealtime,
userinconversation: data.userinconversation,
userimage: data.userimage
}
//socket.broadcast.emit(data.userToNotify + '-' + data.mapid + '-UpdateMapperList', existingUser)
socket.broadcast.emit(MAPPER_LIST_UPDATED, existingUser)
})
// as a new mapper check whether there's a call in progress to join
socket.on(CHECK_FOR_CALL, function (data) {
var socketsInRoom = io.sockets.clients(data.room)
//if (socketsInRoom.length) socket.emit('maps-' + data.mapid + '-callInProgress')
if (socketsInRoom.length) socket.emit(CALL_IN_PROGRESS)
})
// send the invitation to start a call
socket.on(INVITE_A_CALL, function (data) {
//socket.broadcast.emit(data.invited + '-' + data.mapid + '-invitedToCall', data.inviter)
socket.broadcast.emit(INVITED_TO_CALL, data.inviter)
})
// send an invitation to join a call in progress
socket.on(INVITE_TO_JOIN, function (data) {
//socket.broadcast.emit(data.invited + '-' + data.mapid + '-invitedToJoin', data.inviter)
socket.broadcast.emit(INVITED_TO_JOIN, data.inviter)
})
// send response back to the inviter
socket.on(ACCEPT_CALL, function (data) {
//socket.broadcast.emit(data.inviter + '-' + data.mapid + '-callAccepted', data.invited)
//socket.broadcast.emit('maps-' + data.mapid + '-callStarting')
socket.broadcast.emit(CALL_ACCEPTED, data.invited)
socket.broadcast.emit(CALL_STARTED)
})
socket.on(DENY_CALL, function (data) {
//socket.broadcast.emit(data.inviter + '-' + data.mapid + '-callDenied', data.invited)
socket.broadcast.emit(CALL_DENIED, data.invited)
})
socket.on(DENY_INVITE, function (data) {
//socket.broadcast.emit(data.inviter + '-' + data.mapid + '-inviteDenied', data.invited)
socket.broadcast.emit(INVITE_DENIED, data.invited)
})
socket.on(JOIN_CALL, function (data) {
//socket.broadcast.emit('maps-' + data.mapid + '-mapperJoinedCall', data.id)
socket.broadcast.emit(MAPPER_JOINED_CALL, data.id)
})
socket.on(LEAVE_CALL, function (data) {
//socket.broadcast.emit('maps-' + data.mapid + '-mapperLeftCall', data.id)
socket.broadcast.emit(MAPPER_LEFT_CALL, data.id)
})
// this will ping everyone on a map that there's a person just joined the map
socket.on(JOIN_MAP, function (data) {
if (!livemaps[data.mapid]) {
livemaps[data.mapid] = data.map // { name: '', desc: '', numTopics: '' }
livemaps[data.mapid].mapper_count = 1
io.sockets.emit(MAP_WENT_LIVE, livemaps[data.mapid])
}
else {
livemaps[data.mapid].mapper_count++
}
socket.set('mapid', data.mapid)
socket.set('userid', data.userid)
socket.set('username', data.username)
var newUser = {
userid: data.userid,
username: data.username,
userimage: data.userimage
}
//socket.broadcast.emit('maps-' + data.mapid + '-newmapper', newUser)
socket.broadcast.emit(NEW_MAPPER, newUser)
})
var end = function () {
var socketUserName, socketUserID
socket.get('userid', function (err, id) {
socketUserID = id
})
socket.get('username', function (err, name) {
socketUserName = name
})
var data = {
username: socketUserName,
userid: socketUserID
}
socket.get('mapid', function (err, mapid) {
if (livemaps[mapid] && livemaps[mapid].mapper_count == 1) {
delete livemaps[mapid]
io.sockets.emit(MAP_CEASED_LIVE, { id: mapid })
}
else if (livemaps[mapid]) {
livemaps[mapid].mapper_count--
}
// scope by map
socket.broadcast.emit(LOST_MAPPER, data)
})
}
// this will ping everyone on a map that there's a person just left the map
socket.on('disconnect', end)
socket.on(LEAVE_MAP, end)
socket.on(SEND_COORDS, function (data) {
var peer = {
userid: data.userid,
usercoords: data.usercoords
}
//socket.broadcast.emit('maps-' + data.mapid + '-updatePeerCoords', peer)
socket.broadcast.emit(PEER_COORDS_UPDATED, peer)
})
socket.on(DRAG_TOPIC, function (data) {
var mapId = data.mapid
delete data.mapid
//socket.broadcast.emit('maps-' + mapId + '-topicDrag', data)
socket.broadcast.emit(TOPIC_DRAGGED, data)
})
socket.on(CREATE_MESSAGE, function (data) {
var mapId = data.mapid
delete data.mapid
//socket.broadcast.emit('maps-' + mapId + '-newMessage', data)
socket.broadcast.emit(MESSAGE_CREATED, data)
})
socket.on(CREATE_TOPIC, function (data) {
var mapId = data.mapid
delete data.mapid
//socket.broadcast.emit('maps-' + mapId + '-newTopic', data)
socket.broadcast.emit(TOPIC_CREATED, data)
})
socket.on(UPDATE_TOPIC, function (data) {
socket.broadcast.emit(TOPIC_UPDATED, data)
})
socket.on(REMOVE_TOPIC, function (data) {
var mapId = data.mapid
delete data.mapid
//socket.broadcast.emit('maps-' + mapId + '-removeTopic', data)
socket.broadcast.emit(TOPIC_REMOVED, data)
})
socket.on(DELETE_TOPIC, function (data) {
socket.broadcast.emit(TOPIC_DELETED, data)
})
socket.on(CREATE_SYNAPSE, function (data) {
var mapId = data.mapid
delete data.mapid
//socket.broadcast.emit('maps-' + mapId + '-newSynapse', data)
socket.broadcast.emit(SYNAPSE_CREATED, data)
})
socket.on(UPDATE_SYNAPSE, function (data) {
socket.broadcast.emit(SYNAPSE_UPDATED, data)
})
socket.on(REMOVE_SYNAPSE, function (data) {
var mapId = data.mapid
delete data.mapid
//socket.broadcast.emit('maps-' + mapId + '-removeSynapse', data)
socket.broadcast.emit(SYNAPSE_REMOVED, data)
})
socket.on(DELETE_SYNAPSE, function (data) {
//socket.broadcast.emit('deleteSynapseFromServer', data)
socket.broadcast.emit(SYNAPSE_DELETED, data)
})
socket.on(UPDATE_MAP, function (data) {
socket.broadcast.emit(MAP_UPDATED, data)
})
})
var state = {
connectedPeople: {},
liveMaps: {}
}
signalling(io, stunservers, state)
junto(io, state)
map(io, state)
global(io, state)
io.listen(5001)
start()

6
realtime/rooms.js Normal file
View file

@ -0,0 +1,6 @@
module.exports = {
mapRoom: mapId => `maps/${mapId}`,
userMapRoom: (mapperId, mapId) => `mappers/${mapperId}/maps/${mapId}`,
userRoom: mapperId => `mappers/${mapperId}`
}

View file

@ -1,111 +1,119 @@
var uuid = require('node-uuid');
var uuid = require('node-uuid')
module.exports = function(io, stunservers) {
// based off of https://github.com/andyet/signalmaster
// since it was updated to socket.io 1.3.7
var
activePeople = 0;
function safeCb(cb) {
if (typeof cb === 'function') {
return cb;
} else {
return function () {};
}
}
function describeRoom(name) {
var clients = io.sockets.clients(name);
var result = {
clients: {}
};
clients.forEach(function (client) {
result.clients[client.id] = client.resources;
});
return result;
module.exports = function(io, stunservers, state) {
io.on('connection', function (socket) {
socket.resources = {
screen: false,
video: true,
audio: false
}
function safeCb(cb) {
if (typeof cb === 'function') {
return cb;
} else {
return function () {};
// pass a message to another id
socket.on('message', function (details) {
if (!details) return
var otherClient = io.to(details.to)
if (!otherClient) return
details.from = socket.id
otherClient.emit('message', details)
})
socket.on('shareScreen', function () {
socket.resources.screen = true
})
socket.on('unshareScreen', function (type) {
socket.resources.screen = false
removeFeed('screen')
})
socket.on('join', join)
function removeFeed(type) {
if (socket.room) {
io.sockets.in(socket.room).emit('remove', {
id: socket.id,
type: type
})
if (!type) {
socket.leave(socket.room)
socket.room = undefined
}
}
}
io.sockets.on('connection', function (client) {
activePeople += 1;
function join(name, cb) {
// sanity check
if (typeof name !== 'string') return
// leave any existing rooms
removeFeed()
safeCb(cb)(null, describeRoom(name))
socket.join(name)
socket.room = name
}
client.resources = {
screen: false,
video: true,
audio: false
};
// we don't want to pass "leave" directly because the
// event type string of "socket end" gets passed too.
socket.on('disconnect', function () {
removeFeed()
})
socket.on('leave', function () {
removeFeed()
})
// pass a message to another id
client.on('message', function (details) {
if (!details) return;
socket.on('create', function (name, cb) {
if (arguments.length == 2) {
cb = (typeof cb == 'function') ? cb : function () {}
name = name || uuid()
} else {
cb = name
name = uuid()
}
// check if exists
var room = io.nsps['/'].adapter.rooms[name]
if (room && room.length) {
safeCb(cb)('taken')
} else {
join(name)
safeCb(cb)(null, name)
}
})
var otherClient = io.sockets.sockets[details.to];
if (!otherClient) return;
// support for logging full webrtc traces to stdout
// useful for large-scale error monitoring
socket.on('trace', function (data) {
console.log('trace', JSON.stringify(
[data.type, data.session, data.prefix, data.peer, data.time, data.value]
))
})
})
details.from = client.id;
otherClient.emit('message', details);
});
function describeRoom(name) {
var adapter = io.nsps['/'].adapter
var sockets = adapter.rooms[name] || {}
var result = {
clients: {}
}
Object.keys(sockets).forEach(function (id) {
result.clients[id] = adapter.nsp.connected[id].resources
})
return result
}
client.on('shareScreen', function () {
client.resources.screen = true;
});
client.on('unshareScreen', function (type) {
client.resources.screen = false;
removeFeed('screen');
});
client.on('join', join);
function removeFeed(type) {
if (client.room) {
io.sockets.in(client.room).emit('remove', {
id: client.id,
type: type
});
if (!type) {
client.leave(client.room);
client.room = undefined;
}
}
}
function join(name, cb) {
// sanity check
if (typeof name !== 'string') return;
// leave any existing rooms
removeFeed();
safeCb(cb)(null, describeRoom(name));
client.join(name);
client.room = name;
}
// we don't want to pass "leave" directly because the
// event type string of "socket end" gets passed too.
client.on('disconnect', function () {
removeFeed();
activePeople -= 1;
});
client.on('leave', function () {
removeFeed();
});
client.on('create', function (name, cb) {
if (arguments.length == 2) {
cb = (typeof cb == 'function') ? cb : function () {};
name = name || uuid();
} else {
cb = name;
name = uuid();
}
// check if exists
if (io.sockets.clients(name).length) {
safeCb(cb)('taken');
} else {
join(name);
safeCb(cb)(null, name);
}
});
// tell client about stun and turn servers and generate nonces
client.emit('stunservers', stunservers || []);
});
};
function socketsInRoom(name) {
return io.sockets.sockets(name).length
}
}