Я хотел бы сохранить некоторую информацию в переменных комнаты сокетов, но получаю следующую ошибку: 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. Как я могу это исправить?



![Безумие обратных вызовов в javascript [JS]](https://i.imgur.com/WsjO6zJb.png)


Как вы можете видеть в своем собственном журнале, 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. Они могут изменить его работу, когда захотят, и недавно действительно изменили его.
@MilanFerus-Comelo — вы можете просто создать свой собственный объект с помощью ключа roomId, который отслеживает данные вашей собственной комнаты, и тогда любой сокет может получить доступ к этим данным. Итак, вместо использования io.sockets.adapter.rooms используйте свой собственный объект в качестве родителя.
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);
});
}
Спасибо за быстрый ответ и разъяснение! Что бы вы порекомендовали мне сделать для хранения переменных? Вместо использования базы данных я хотел бы хранить информацию в самой комнате (например, открыта ли игра, кто является хостом (имя пользователя) и именами пользователей игроков). Есть ли другой способ сохранить эти данные, чтобы к ним можно было получить доступ из других сокетов в той же комнате?
После критического обновления от Socket.io с заменой Array на Map.
Простое решение будет меняться:
io.sockets.adapter.rooms[socket.roomId].host = socket.username;
к:
io.sockets.adapter.get(socket.roomId).host = socket.username;
Спасибо за быстрый ответ и разъяснение! Что бы вы порекомендовали мне сделать для хранения переменных? Вместо использования базы данных я хотел бы хранить информацию в самой комнате (например, открыта ли игра, кто является хостом (имя пользователя) и именами пользователей игроков). Есть ли другой способ сохранить эти данные, чтобы к ним можно было получить доступ из других сокетов в той же комнате?