Обход кэширования игр, сделанных на Construct 3
Добрый день!
Меня зовут Руслан.
С 2016 года делаю игры в редакторе Construct 3 (раньше он назывался Construct 2), преимущественно без использования "настоящего" кода.
Что-то в последнее время у меня загорелось начать где-то сохранять свой опыт работы с этим редактором. И пока идут праздники, решил попробовать.
В этом посте описан мой способ обновлять игры, сделанные на Construct 3.
Сегодня китайская нейросеть сподобилась выдать работающую версию скрипта для автоматического обновления игры. Хочу поделиться результатом.
Суть проблемы:
Браузеры любят всё кэшировать. Они распихивают себе по кармашкам (в кэш) все скрипты игры, картинки, звуки и прочее, из чего состоит игра.
В итоге, когда мы заливаем на сервер новую версию, то может произойти вот что.
1. Игрок, который раньше уже играл в эту игру, запускает её у себя в браузере,
2. Браузер показывает ему устаревшую версию, загруженную из кэша, а не с сервера. Вроде как это снижает нагрузку на сервер, но в итоге игрок не увидит обновление.
Как это решается теперь в наших проектах:
1. Чтобы понять, новая это версия или старая, я стараюсь всегда выводить на экран номер версии, где-нибудь в нижнем углу, бледненько, но читаемо.
При старте каждого экрана в это текст вывожу номер версии из системной переменной projectversion.
Иначе можно бесконечно чинить баги, не понимая, это старая версия загружена или просто новая сломалась.
2. Добавляю в проект группу с группами, в которой происходит обработка автоматического обновления.
Немного ниже покажу содержимое этих групп подробнее.
3. Создаю отдельный слой update, на котором располагаю одноцветный фон, вращающуюся иконку загрузки, кнопку обновления и текст с оповещением, что найдена новая версия.
Фон растягиваю на весь экран.
4. Прозрачность слоя update привязываю к прозрачности иконки на этом слое. Иконке заданы поведения Rotate и Tween, что позволяет ей бесконечно крутиться и плавно менять прозрачность.
То есть, теперь можно управлять прозрачностью слоя, задавая прозрачность иконки.
На старте каждого экрана делаю иконку и фон полностью прозрачными, чтобы они не появились раньше времени.
5. Далее, используем плагин Browser. Когда он сигнализирует, что нашёл обновление - включаем окно (точнее сказать - слой) Обновления.
Важный момент. Как Браузер понимает, что есть новая версия игры?
По номеру версии!
Поэтому стараемся не забывать обновлять этот номер в поле Version (в главных настройках проекта в самом верху.
Для удобства, я нумерую версии текущей датой и примерным временем сохранения проекта.
6. Параллельно с включением окна (слоя) Обновления у меня блокируются все активные игровые слои, чтобы игрок не ускакал случайно на другой экран.
Также включаю вращение иконки (Rotate, до включения слоя она не вращается, чтобы не тратить системные ресурсы).
И затем с помощью Tween плавно делаю иконку, а за ней и весь слой непрозрачным, чтобы игрок увидел предупреждение о новой версии, которая в данный момент загружается.
7. Когда обновление наконец загрузилось (определяем это с помощью того же плагина Browser), проверяем, если иконка Обновления ещё не включалась (может случиться и такое), то включаем её и показываем поверх кнопку "Обновить".
Если же экран обновления уже включен, то просто включаем кнопку, чтобы игрок мог сам нажать её в удобный для него момент.
Также меняем текст на новый.
8. И наконец последнее - обработка нажатия кнопки.
Тут всплыла та самая закавыка со скриптом, из-за которой пришлось напрягать китайскую нейросеть.
В браузерах Chrome, Opera, Yandex - в целом всё работает нормально и без скрипта. Когда кнопка нажата - страница просто обновляется с помощью Browser - Reload и всё ок - грузится новая версия.
Но Firefox решил выпендриться и в нём эта конструкция уходит в бесконечную перезагрузку: Грузится старая версия и снова появляется окно Обновления, пока игрок не догадается нажать Shift-F5, чтобы вручную загрузить самую новую версию.
if ('serviceWorker' in navigator) {
navigator.serviceWorker.getRegistrations().then(function(registrations) {
// 1. Удаляем все сервис-воркеры
for (let registration of registrations) {
registration.unregister();
}
// 2. Очищаем кеш (если доступно)
if ('caches' in window) {
caches.keys().then(function(cacheNames) {
cacheNames.forEach(function(cacheName) {
caches.delete(cacheName);
});
});
}
// 3. Задержка 500 мс + жесткая перезагрузка
setTimeout(() => {
window.location.reload(); // Или window.location.href = window.location.href;
}, 1000);
});
} else {
// Просто перезагружаем, если сервис-воркеров нет
window.location.reload();
}
Поэтому здесь пришлось прикрутить вышеупомянутый JS-скрипт, сгенерированный нейросетью с 20-й попытки.
Переменную browserName пришлось завести вручную, чтобы определять, в каком браузере запущена игра.
var userAgent = navigator.userAgent;
if (userAgent.indexOf("Chrome") > -1) {
runtime.globalVars.browserName = "Chrome";
} else if (userAgent.indexOf("Firefox") > -1) {
runtime.globalVars.browserName = "Firefox";
} else if (userAgent.indexOf("Safari") > -1) {
runtime.globalVars.browserName = "Safari";
} else if (userAgent.indexOf("Edge") > -1) {
runtime.globalVars.browserName = "Edge";
} else if (userAgent.indexOf("MSIE") > -1 || userAgent.indexOf("Trident") > -1) {
runtime.globalVars.browserName = "Internet Explorer";
} else {
runtime.globalVars.browserName = "Unknown";
}
// Теперь вы можете использовать переменную browserName в вашем проекте
console.log("Browser: " + runtime.globalVars.browserName);
Соответственно, для Firefox используем чудо-скрипт на JS, а в остальных браузерах, стандартный Browser - Reload.
У меня этот способ сработал. Возможно в каких-то ещё браузерах тоже нужно обрабатывать нажатие кнопки с помощью скрипта.
P.S. В следующих постах планирую выкладывать другие нюансы работы в Construct 3, которые применяю в своей работе.
P.P.S. Для более надёжного хранения завёл также телеграм-канал Игрушечный программист: https://t.me/toyprogrammer