Spaces:
Running
Running
File size: 5,771 Bytes
b51e03a 3597f5a fcce7e3 3597f5a fcce7e3 3597f5a fcce7e3 3597f5a fcce7e3 3597f5a fcce7e3 3597f5a fcce7e3 3597f5a fcce7e3 3597f5a fcce7e3 3597f5a | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 | ;
;
/* ============================================================================
OFFLINE
----------------------------------------------------------------------------
MASSFRONT is a single-player game that grew some online services. That order
matters: the services are additions, and none of them may become a condition
of playing. A player on a plane, on a dead signal, or who simply does not
want the game talking to anything, gets the whole game.
Everything needed to play is already in the installer β terrain generation is
procedural, the unit and item art is bundled, and the soundtrack ships inside
the build. What remains are four optional network callers:
updater checks for patches src/updater.js
asset packs downloads extra media src/assetpack.js
accounts register / sign in / sync src/authportal.js
ad boards would fetch remote creatives src/adboards.js
This file is the single gate all four ask before reaching the network, so the
answer is decided in one place rather than four. `netAllowed()` is false when
the player has chosen offline, and ALSO when the browser reports itself
offline β a request that cannot succeed should not be attempted, because the
failure path costs a visible error and a timeout the player has to sit
through.
The design rule for every caller is: no network is a NORMAL state, not an
error. Nothing here retries in a loop, nothing blocks a frame, and nothing
shows a failure for a request it should never have made.
============================================================================ */
const NET = { forced:false, online:true };
/* Offline is a per-device choice, not per-profile: it describes where the phone
is, not who is playing. */
const NET_KEY = 'massfront_offline';
function netLoad(){
try{ NET.forced = localStorage.getItem(NET_KEY) === '1'; }catch(e){}
NET.online = (typeof navigator === 'undefined') || navigator.onLine !== false;
}
function netAllowed(){
if(NET.forced) return false;
return NET.online !== false;
}
function netSetOffline(v){
NET.forced = !!v;
try{ localStorage.setItem(NET_KEY, NET.forced ? '1' : '0'); }catch(e){}
document.body.classList.toggle('offline', NET.forced);
if(typeof renderSettings === 'function') renderSettings();
if(typeof packRenderBar === 'function') packRenderBar();
if(typeof renderUpdatePanel === 'function') renderUpdatePanel();
if(typeof renderAccount === 'function') renderAccount();
/* Leaving offline is the moment a queued cloud backup should flush.
Entering it must not start a request β netAllowed() already refuses. */
if(!NET.forced && typeof cloudPush === 'function' && typeof CLOUD !== 'undefined' && CLOUD.dirty)
try{ cloudPush(); }catch(e){}
if(typeof toast === 'function')
toast(NET.forced ? 'β Offline mode β the game never contacts a server'
: 'β Online features enabled');
}
/* A fetch wrapper the online modules use instead of calling fetch directly.
Two jobs: refuse when offline, and time out. A hanging request is worse than
a failed one β it leaves a spinner up forever on a captive-portal wifi that
accepts the connection and then never answers. */
async function netFetch(url, opts){
if(!netAllowed()) throw new Error('offline');
const ms = (opts && opts.timeout) || 12000;
const ctl = (typeof AbortController !== 'undefined') ? new AbortController() : null;
const t = setTimeout(() => { try{ ctl && ctl.abort(); }catch(e){} }, ms);
try{
return await fetch(url, Object.assign({}, opts, ctl ? {signal: ctl.signal} : {}));
} finally { clearTimeout(t); }
}
function initOffline(){
netLoad();
/* Trust the events over the initial flag: a phone that regains signal should
not need a restart to sync, and one that loses it should stop trying. */
if(typeof window !== 'undefined'){
window.addEventListener('online', () => { NET.online = true;
if(typeof packRenderBar === 'function') packRenderBar();
if(typeof renderAccount === 'function') renderAccount();
if(typeof cloudPush === 'function' && typeof CLOUD !== 'undefined' && CLOUD.dirty)
try{ cloudPush(); }catch(e){}
});
window.addEventListener('offline', () => { NET.online = false;
if(typeof packRenderBar === 'function') packRenderBar();
if(typeof renderAccount === 'function') renderAccount();
});
}
document.body.classList.toggle('offline', NET.forced);
/* Add the toggle to Settings by wrapping renderSettings rather than editing
meta.js β same pattern the tutorial uses, so the two do not fight. */
if(typeof renderSettings === 'function'){
const base = renderSettings;
renderSettings = function(){
base.apply(this, arguments);
const list = document.getElementById('setList');
if(!list || list.querySelector('[data-set="offline"]')) return;
const row = document.createElement('div');
row.className = 'sItem setRow'; row.dataset.set = 'offline';
row.innerHTML = '<div class="sTx"><b>β Offline Mode</b><div class="sDs">'
+ (NET.forced
? 'The game never contacts a server. Everything still plays.'
: 'When on, the game never contacts a server. Updates and cloud saves stay on this device.')
+ '</div></div><div class="sBuy togB' + (NET.forced ? ' onT' : '') + '">'
+ (NET.forced ? 'ON' : 'OFF') + '</div>';
const flip = () => {
netSetOffline(!NET.forced);
if(typeof sfx === 'function') sfx('ui');
};
if(typeof mfBindTap === 'function') mfBindTap(row, flip);
else row.addEventListener('pointerdown', flip);
list.appendChild(row);
};
}
}
|