Skip to content

kontur-web-courses/place

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

47 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Place

В задании будем делать свою версию проекта Place размером 256x256 Клиентский код лежит в папке /client, серверный — в файле server.mjs

Поставь зависимости и запусти сервер. Для этого перейди в директорию задачи и выполни команду npm install. После установки зависимостей, выполни команду npm run dev. После запуска, перейди по адресу localhost:5000

  1. Сейчас цвета палитры захардкожены в файле /client/picker.mjs, cделай так, чтобы цвета запрашивались с сервера в функции drawPalette

  2. На сервере мы будем использовать библиотеку ws для подключения по протоколу WebSocket. Она уже подключена, WebSocket-сервер уже правильно интегрирован с express, запущен и ждёт подключений по тому же порту, что и express.

Сделай так, чтобы WebSocket-сервер начал обрабатывать подключения, т.е. события connection, как в примере

  1. Сделай так, чтобы после подключения, новому клиенту присылался текущий массив состояния поля. Так как от сервера к клиенту будут передаваться разные сообщения, то удобно, чтобы каждое сообщение имело следующую структуру:
{
  type: "/* action */", // тип сообщения
  payload: {
     /* ... */ // контент сообщения
  }
}

Через веб-сокеты можно отправлять и, соответственно, принимать данные в разных форматах.

Нам же подойдут обычные строки. Чтобы передать объект сообщения в виде строки, его надо предварительно сериализовать, например, с помощью JSON.stringify().

Как принимать и отправлять строковые сообщения уже было показано в примере

  1. Сделай так, чтобы на клиенте при получении сообщения из WebSocket с начальным состоянием поля, заполнялось поле. Для этого измени обработчик события на ws в файле /client/index.mjs. В него сейчас приходят события MessageEvent. Для десериализации данных в событии используй JSON.parse(). Для отрисовки начального состояния используй drawer.putArray()

  2. Сделай так, чтобы при клике на поле, на сервер по WebSocket передавалось сообщение с координатами и цветом. Посылай сообщения с помощью метода send(). Для удобства, используй тот же формат, что и в сообщениях, посылаемых с сервера

  3. Сделай так, чтобы WebSocket-сервер при получении сообщения с координатами и цветом валидировал его на правильность координат и цвета, а затем рассылал всем активным клиентам. Как сделать broadcast посмотри в примере

  4. Сделай так, чтобы новые пиксели не только рассылались остальным клиентам, но и добавлялись в поле, хранимое на стороне сервера. Это нужно, чтобы новые клиенты получали текущее изображение, а не начальное. Как сделаешь — проверь: открой приложение с одной вкладки браузера, нарисуй что-нибудь, затем открой со второй вкладки и убедись, что изображения совпадают

  5. Удали из обработчика клика на поле отрисовку пикселя. Для этого сделай так, чтобы при broadcast-е текущему клиенту тоже отправлялось сообщение. А затем сделай так, чтобы отрисовка пикселя происходила только при получении клиентом сообщения из WebSocket. После этого сервер будет полностью контролировать целостность поля для рисования

  6. Опубликуй своё приложение на Heroku. Для этого потребуются heroku-cli, и git, если чего-то нет, пройди вот эти шаги.

  • Авторизируйся в Heroku: heroku login
  • Создай новое приложение в Heroku: heroku create
  • Версия nodejs для нашего приложения уже указана в поле engines в package.json
  • Закомить все изменения: git add ., git commit -m "add my app"
  • Задеплой в Heroku: git push heroku master

После деплоя:

  • При деплое Heroku для Node.js-приложений по умолчанию запускает npm-script с именем start
  • Чтобы запускать приложение локально можно использовать heroku local web
  • Чтобы деплоить новую версию приложения можно повторять последние два шага
  1. Сделай так, что WebSocket сервер принимал только те подключения, которые передали в параметрах правильный apiKey. Для этого измени обработчик события upgrade. Подробнее о нём можно прочитать здесь.

В обработчике уже используется объект URL, благодаря которому можно получить значение apiKey из query string. Как это сделать посмотри тут. Для закрытия соединения, которое произошло с невалидным apiKey можно использовать socket.destroy(). Подробнее можно прочитать здесь

  1. * Сделай так, чтобы после успешного upgrade происходила связь ws клиента и его apiKey. Сделать это можно внутри в handleUpgrade(). Для этого используй WeakMap, где ключами будут объекты ws, а значениями — связанные с ними apiKey. WeakMap не будет препятствовать сборщику мусора очищать ws-соединения, когда они будут закрыты

  2. * Сделай так, чтобы у каждого apiKey был свой таймаут. При подключении нового WebSocket клиента, посылай ему сообщение с временем, когда он в следующий раз может нарисовать на поле. Текущее время можно получить с помощью new Date(). Для отправки сериализуй дату с помощью .toISOString().

На клиенте при получении сообщения, обновляй таймер с помощью timeout.next =. Десериализуй полученную дату с помощью new Date(isoDateString). На сервере, при получении сообщения с координатами проверяй таймаут apiKey ws клиента и не принимай сообщения, которые произошли до истечения этого таймаута.

В ответ на принятые сообщения обновляй таймаут на 10s-60s и отсылай его клиенту. В ответ на непринятые сообщения просто посылай таймаут.

Подсказка: чтобы получить дату через n секунд после date можно использовать такое выражение: new Date(date.valueOf() + n * 1000)