Настройка сокета.переменные комнаты ввода-вывода

#javascript #node.js #express #websocket #socket.io

#javascript #node.js #выражать #websocket #socket.io

Вопрос:

Я хотел бы сохранить некоторую информацию в переменных комнаты сокета, но получаю следующую ошибку: UnhandledPromiseRejectionWarning: TypeError: Cannot set property 'host' of undefined

Это мой код:

 io.on('connection', (socket, next) => {
    socket.on('create game', async () => {
        console.log('Creating game...');
        socket.username = await generateUsername();

        socket.roomId = generateId();

        socket.join(socket.roomId);

        io.sockets.adapter.rooms[socket.roomId].host = socket.username;
        io.sockets.adapter.rooms[socket.roomId].isOpen = true;
        io.sockets.adapter.rooms[socket.roomId].players = [socket.username];

        console.log('Game created! ID: ', socket.roomId);
    });
}
 

Если я попытаюсь войти socket.roomId в систему, это вернет что — то вроде rBAhx0 . И когда я регистрируюсь io.sockets.adapter.rooms , я получаю следующее:

 Map {
  'PX_o3Di9sp_xsD6oAAAB' => Set { 'PX_o3Di9sp_xsD6oAAAB' },
  'rBAhx0' => Set { 'PX_o3Di9sp_xsD6oAAAB' }
}
 

Однако, когда я пытаюсь войти io.sockets.adapter.rooms[socket.roomId] в систему, он возвращается undefined . Как я могу это исправить?

Ответ №1:

Сокет.io внесла некоторые кардинальные изменения в новейшую версию, и доступ к комнатам стал невозможен, как раньше. Это изменило множество объектов и массивов на карты и наборы, вы можете увидеть это в журналах, которые вы опубликовали.

Set объекты представляют собой наборы значений. Вы можете перебирать элементы набора в порядке вставки. Значение в наборе может встречаться только один раз; оно уникально в коллекции набора.

Map Объект содержит пары ключ-значение и запоминает исходный порядок вставки ключей. Любое значение (как объекты, так и примитивные значения) может использоваться либо как ключ, либо как значение.

Доступ к свойствам карты работает иначе, чем доступ к свойствам обычного объекта. Пример:

 const myMap = new Map();
myMap.set("foo", "bar");
console.log(myMap["foo"]) // undefined
console.log(myMap.get("foo")) // bar 

То же самое относится и к наборам, однако в вашем случае запрос этого набора, в частности, вероятно, является неправильным подходом, поскольку этот набор содержит только набор идентификаторов, а не фактические объекты room. Даже если бы вы хотели получить значение из набора, вы не смогли бы получить доступ к его свойствам (host, isOpen и players), поскольку это всего лишь строка.

Боюсь, что версия 3.0 сделала прямой доступ к списку всех комнат невозможным. Однако теперь у адаптера есть свойство socketRooms , которое можно использовать вместо него.

Чтобы облегчить доступ к комнатам сокета, вы должны получить к ним доступ следующим образом:

 io.sockets.adapter.socketRooms(socketId);
 

Однако это все равно просто вернет список строк.

Самым простым решением этой проблемы было бы создать внешнюю переменную за пределами области подключения.

 const rooms = {};

io.on('connection', (socket, next) => {
    socket.on('create game', async () => {
        console.log('Creating game...');
        socket.username = await generateUsername();

        socket.roomId = generateId();

        socket.join(socket.roomId);

        if (!rooms[socket.roomId]) rooms[socket.roomId] = {};
        rooms[socket.roomId].host = socket.username;
        rooms[socket.roomId].isOpen = true;
        rooms[socket.roomId].players = [socket.username];

        console.log('Game created! ID: ', socket.roomId);
    });
}
 

Комментарии:

1. Спасибо за быстрый ответ и разъяснения! Что бы вы порекомендовали мне сделать для хранения переменных? Вместо того, чтобы использовать базу данных, я хотел бы хранить информацию в самой комнате (например, открыта ли игра, кто является хозяином (имя пользователя) и имена пользователей игроков). Есть ли какой-либо другой способ сохранить эти данные, чтобы к ним могли обращаться другие сокеты в той же комнате?

Ответ №2:

Как вы можете видеть в своем собственном журнале, io.sockets.adapter.rooms является Map объектом (в более поздних версиях socket.io ), а не простой объект. Это означает, что доступ к комнате осуществляется с .get() помощью, а не с помощью простого индексирования, такого как то, что вы используете:

 io.sockets.adapter.rooms[socket.roomId].host = socket.username;
 

Это просто не сработает, потому что на карте нет простого socket.roomId свойства.

Если бы вы хотели получить объект room, вам пришлось бы сделать это:

 let roomObj = io.sockets.adapter.rooms.get(socket.roomId);
roomObj.host = socket.username;
 

На мой взгляд, я бы не стал напрямую связываться с объектами комнаты. Мы уже знаем этот сокет.io ранее менял их дизайн, делая любой код, который пытался их использовать, сломанным и нуждающимся в редизайне. Вместо этого я бы, вероятно, просто создал свою собственную структуру данных, в которой я хранил свои собственные данные, и тогда я не буду зависеть от прихотей сокета.ввод-вывод изменяет их дизайн. Прямой доступ к объектам комнаты не является обязательным дизайном или обязательным API для socket.io . Они могут изменить, как это работает, когда захотят, и, действительно, недавно изменили его.

Комментарии:

1. Спасибо за быстрый ответ и разъяснения! Что бы вы порекомендовали мне сделать для хранения переменных? Вместо использования базы данных я хотел бы хранить информацию в самой комнате (например, если игра открыта, кто является хозяином (имя пользователя) и имена пользователей игрока). Есть ли какой-либо другой способ сохранить эти данные, чтобы к ним могли обращаться другие сокеты в той же комнате?

2. @MilanFerus-Comelo — Вы можете просто создать свой собственный объект с roomId ключом, который отслеживает ваши собственные данные комнаты, а затем любой сокет может получить доступ к этим данным. Итак, вместо использования io.sockets.adapter.rooms используйте свой собственный объект в качестве родительского.

Ответ №3:

После аварийного обновления из сокета.ввод-вывод изменения Array на Map . Простым решением было бы изменить:

     io.sockets.adapter.rooms[socket.roomId].host = socket.username;
 

Для:

     io.sockets.adapter.get(socket.roomId).host = socket.username;