Как восстанавливать энергию через определённое время?
Доброго времени суток! Интересует вопрос со стороны лучшего, более производительного варианта. Суть такова - есть игроки, у них используется "энергия" для определённых действий (не важно каких). Я хотел, что бы через какое-то определённое время "энергия" восстанавливалась, при этом нужно учитывать тип аккаунта (простой или премиум, название не важно), так же в условие необходимо учесть, пользователь онлайн или нет.
У меня на данный момент 3 варианта:
1. Cron. При этом, на мой взгляд, тут будет много запросов в БД (если будет много игроков), так как учитывается ещё и онлайн.
2. WebSocket. Данный вариант, на мой взгляд, идеален для восстановления энергии онлайн пользователей. Но как мне кажется, ужасен для расчёта энергии в остальном.
3. Cron + WebSocket. Вот этот вариант более лучший. Т.к. cron выполняет просчёт кол. восстановленной энергии для типов пользователей. А соккет будет расчитывать энергию для онлайн-пользователей с учётом типа аккаунта.
Но тут есть одно "но" - нужно будет учесть так, что бы 2 разных алгоритма не работали с одними же пользователями, иначе о онлайн пользователей будет преимущество в восстановлении энергии.
Если есть какой-то лучший вариант, то прошу Вас написать об этом. Если же один из моих вариантов подходит для данной реализации, но нужно его как-то доработать, то прошу об этом написать (желательно с пояснением). Заранее спасибо!)
devalone, под соккетом я имел ввиду получение игроков онлайн, а дальнейшая работа всё равно со стороны php бы велась. Хотя да, неудачный вариант для моей цели.
Можно очень просто. Сохраняешь в бд последнее время, когда игрок обновил энергию. Потом, при следующем запросе ты смотришь, пора? Далее, если необходимо, проверяешь, сколько раз стоило восстановить энергию. Ну и добавляешь необходимое количество пунктов.
Получается нужно писать данную проверку перед самим запросом к остальным ресурсам, т.е. это не крон!?) И как это будет сказываться на производительности? Это же новый запрос в БД...
Дмитрий Кузнецов, как-то я думал о оптимизированном варианте примерно такого же задания. А что если сохранять в БД последнее время обновление энергии, потом, получается что пользователь вышел из игры, а когда снова заходит, то идет отправка в БД, сверяется время и рассчитывается сумма для пополнения энергии. Всего лишь 1 запрос 1 раз для 1-го человека. И никакого ежеминутного крона для всех аккаунтов.
Дмитрий Кузнецов, в любом случае будет запрос к базе, когда ты будешь проверять, возможно ли действие.
Это хороший вариант, лучше чем крон.
Что-то вроде:
ударить(игрок, палка, цель)
запрос(игрок, энергия, когда_восстанавливать)
если время > когда восстанавливать:
сколько_восстановить = подсчет_сколько(время, когда восстанавливать)
восстановить(сколько_восстановить)
совершить_удар(игрок, палка, цель)
Запрос текущего количества энергии тебе в любом случае делать, а вот количество насильственных запросов к базе резко сокращается(не надо всех опрашивать каждые N секунд).
Чёрт, пока писал псевдокод-пример - почувствовал себя 1Сником :D
доступ к бд должны быть только по запросу пользователей, иначе мощности не наберётесь
Вы правы, но иногда требуются делать так, как не хорошо со стороны оптимизации. Например, как в моём случае, выполнять код в том случае, если пользователь не онлайн.
Благодаря Максим Компаниец и Вячеслав Бойко написал пробный код. Вроде работает хорошо, но нужно его в будущем переписать, так как не особо правильно (кажется) задал формулы...
Дмитрий Кузнецов, абсолютно всегда любое якобы непрерывное действие преобразуется в действие по запросу. Не бывает таких случаев, когда холостая работа приносит пользу.
Дмитрий Кузнецов, кстати, по моему варианту можно не только при входе делать запрос на рассчет, а в любое время, хоть каждые 5 минут ( но логичнее сделать при вызове определенных функций ), когда пользователь будет онлайн. Ведь в любом случае будет сравниваться время последнего начисления энергии с текущим, а от этого будет зависеть пора ли начислять энергию снова или нет.
Максим Компаниец, у меня так:
Каждый запрос авторизованного пользователя вызывает функцию, в которой проверяется последнее время проверки, и если оно больше 5 минут, то изменяем энергию по формуле (или хз что это у меня получилось) . Если же время прошло меньше 5 минут, то ни чего не делаем.
Написано
Saboteur
@saboteur_kiev Куратор тега Разработка игр
software engineer
Сохраняйте timestamp последнего обновления и обновляйте энергию при каждом обращении к информации для этого игрока (или даже при каждом обращении к информации игрока, которому нужно вернуть энергию).
Как и обещал - сделал руководство по созданию системы регенерации энергии в браузерной игре (делал для своей игры, но почему бы и не поделиться наработками с товарищами по ремеслу):
Раз уж вы используете MySQL, то наилучший вариант будет использование MySQL Event Scheduler вместе с полем "Дата последнего обновления энергии".
По теме есть хорошая статья на хабре и rldp + официальная документация.
Дмитрий Кузнецов, создание "энергии" в браузерной игре - довольно шаблонная задача. У меня есть черновой набросок на эту тему (PHP+MySQL), надо довести его до ума, добавить JS-скрипт (который даже без обновления страницы и запросов к серверу будет обновлять цифру энергии локально у пользователя)
В общем, если будет время - постараюсь на этой неделе доработать свой скрипт и оформить его гайдом + исходники.
"Оптимальный" путь зависит многих факторов.
Например, от архитектуры игр: предполагается постоянный коннект клиента с сервером, или можно жить в оффлайне?
Например, от UI и игровой логики: должен ли у пользователя быть постоянный real-time доступ к состоянию таймера восстановления, или оно просто должно тикать где-то в фоне?
Может ли он взаимодействовать с этим таймером (напр. дебаффать других игроков, что бы замедлить восстановление, или задонатить что бы сократить время)
В целом детерминированная по определенным правильнам, регулярно случающаяся активность идеально попадает под Scheduled Job \ Timer внутри игровой логики.
У вас есть момент инициализации таймера, в который вы рассчитываете с какой скоростью и в каком объеме у вас должен происходить "тик" восстановления энергии.
У вас есть момент окончания таймера - объект из буффера кидается в очередь на передачу клиента, обрабатывается клиентом, происходит так.
У вас есть критерии его окончания - пользователь ушел в оффлайн? Грохнули все таймеры для него, например. Или, наоборот, зафризили.
Самое сложное тут - правильно отслеживать состояние таймера и апдейтить его при необходимости.
Для этого надо правильно проработать условия возникновения, истечения и обработки таймеров и сложить их в прозрачное и пригодное для дебаггинга-тестирования хранилище.
предполагается постоянный коннект клиента с сервером, или можно жить в оффлайне?
Предпологается, если пользователь онлайн - то его скорость восстановление больше (ещё, если пользователь примиум - т.е. задонатил на определённый статус, то скорость суммируется). Если пользователь офлайн, то будем скорость восстановления меньше (а если задоначен акк, то скорость чуть выше).
Может ли он взаимодействовать с этим таймером
Если только донат (платная игровая валюта или премиум акк)