Настройка переменных комнаты Socket.io

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

Это мой код:

io.on('connection', (socket, next) => {
    socket.on('create game', async () => {
        console.info('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.info('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. Как я могу это исправить?

Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Безумие обратных вызовов в javascript [JS]
Безумие обратных вызовов в javascript [JS]
Здравствуйте! Юный падаван 🚀. Присоединяйся ко мне, чтобы разобраться в одной из самых запутанных концепций, когда вы начинаете изучать мир...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
2
0
1 267
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

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

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

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

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

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

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

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

Milan Ferus-Comelo 11.12.2020 09:43

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

jfriend00 11.12.2020 18:14
Ответ принят как подходящий

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

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

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

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

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

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

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

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

io.sockets.adapter.socketRooms(socketId);

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

Простейшим решением этой проблемы было бы создание внешней переменной за пределами области соединения.

const rooms = {};

io.on('connection', (socket, next) => {
    socket.on('create game', async () => {
        console.info('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.info('Game created! ID: ', socket.roomId);
    });
}

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

Milan Ferus-Comelo 11.12.2020 09:44

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

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

к:

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

Другие вопросы по теме