Живой общий мир на обычном хостинге без постоянных соединений
h han@hanproject.ru
->назад к журналу
$cat ~/log/zhivoy-mir-na-obychnom-hostinge-bez-websocket

Живой общий мир на обычном хостинге без постоянных соединений

Считается, что многопользовательскую игру на дешёвом хостинге не сделать: нет ни WebSocket, ни своего процесса. Оказалось, можно.

Обычный хостинг за пару сотен рублей не даёт почти ничего из того, на чём принято строить многопользовательские игры. Нет постоянных соединений: WebSocket не поднять. Нет возможности держать свой процесс: демон, который считал бы мир, некому запустить и некому перезапустить. Планировщик задач есть, но только из панели управления и с шагом в минуту — для мира, который должен шевелиться каждые полсекунды, это бесполезно.

Отсюда обычный вывод: на таком хостинге живого общего мира не бывает. Моя «Тропа» — кооперативная RPG на восьмерых — год прожила именно там.

Мир двигает тот, кто первым заметил

Ключевая мысль простая: мир не обязан идти сам по себе. Достаточно, чтобы он оказывался в правильном состоянии в тот момент, когда на него смотрят.

Каждый игрок и так шлёт запрос примерно раз в секунду — узнать, что вокруг. Вот этот запрос и двигает мир. Он смотрит, когда мир считался в последний раз, и если с тех пор прошло время — досчитывает: двигает чудищ, снимает урон, возрождает павших. Потом отдаёт результат.

Если игроков нет, мир стоит. Как только кто-то зашёл, первый же его запрос обнаруживает, что прошло, скажем, три часа, и досчитывает их одним шагом — не три часа по полсекунды, а сразу, потому что для стоящего мира промежуточные состояния никому не нужны.

Отдельной службы нет вовсе. Планировщик не нужен. Ломаться нечему.

Что мешает: двое считают одно и то же

Проблема появляется, когда запросов много. Пять игроков одновременно замечают, что пора считать такт, и считают его пять раз.

Лечится это тем, что такт берётся под замок: кто первым захватил, тот и считает, остальные просто читают готовое. В SQLite это транзакция, которая сразу объявляет намерение писать.

Здесь меня ждала ловушка, на которую я потратил вечер. Записи иногда падали с «база занята» — мгновенно, не дожидаясь положенного ожидания. Причина оказалась не в нагрузке: незакрытый курсор чтения. Если прочитать данные и не закрыть выборку до конца, соединение остаётся в снимке прошлого состояния, и следующая же попытка записи отваливается сразу, минуя все тайм-ауты. Помогло правило: любое чтение возвращает результат и немедленно закрывает выборку.

Рывки: сервер отвечает раз в секунду, кадров шестьдесят

Второе, что убивает ощущение живого мира, — дёрганое движение. Сервер присылает положение чудищ раз в секунду. Между ответами кадров проходит шестьдесят. Если просто ставить фигуры туда, куда пришло, они прыгают.

Первое решение — сглаживать: тянуть фигуру к последней известной точке. Выходит вязко и всё равно неровно, потому что каждая новая точка меняет направление рывком.

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

Разница в числах: отношение пиковой скорости к средней было двенадцатикратным — то есть фигура то стояла, то дёргалась, — стало 1,2.

Сколько это стоит

  • такт мира считается примерно за миллисекунду;
  • ответ на опрос весит около килобайта в сжатом виде;
  • кадр в браузере стоит 0,15–0,36 мс — запас к шестидесяти кадрам примерно в полсотни раз;
  • ни одной картинки: герои, чудища, земля и звук рождаются кодом, поэтому страница открывается мгновенно и не греет телефон.

Когда так делать не надо

Честно: этот подход не универсален. Он годится там, где расхождение в доли секунды никого не убивает — исследование мира, бои по очереди, совместные вылазки. Для соревновательного шутера, где важно, кто нажал раньше на сорок миллисекунд, нужен нормальный сервер с постоянным соединением, и никакие ухищрения этого не заменят.

Но если задача — чтобы друзья зашли по ссылке и вместе побегали, обычного хостинга достаточно. Проверено: «Тропа» так и жила, пока не переросла — и переехала на свой сервер уже из-за числа игроков, а не из-за подхода.

темы записи хостинг игры производительность