Skip to content

Commit 82021b1

Browse files
committed
Fix 94: BootData pre-fetch, URLEncoder, Web Audio API, integer parsing
Critical fixes for the DragonSoul web port: 1. index.html: Pre-fetch /login + /api/boot before main() starts. Bypasses the CLTLS URLEncoder crash entirely. Player name, level, gold, diamonds and stamina now load from the Netlify /api/boot endpoint on first render frame. 2. classes.js (jn_URLEncoder_encode0): Robust Fix 94 replacing Fix 86. Handles HashMap/object values passed to URLEncoder (not just JS primitives), preventing the [CLTLS-EX] crash during login. 3. classes.js (checkForDelayedBootData): Injects User object properties (name, teamLevel, gold, diamonds, stamina) from window._dsBootData BEFORE calling refreshUserInfo(), so the MainMenuScreen header actually shows correct player stats. 4. classes.js (WebAudio): Replace no-op _fakeAudioDevice stubs with a proper Web Audio API engine. WebAudioSoundImpl/WebAudioMusicImpl fetch OGGs from assets/sound/ and decode via AudioContext. 5. classes.js (jl_Integer_parseInt): Strip locale thousands-separator commas (e.g. "1,110" -> "1110") before parsing, fixing the "Problem reading cell in runeSellValues.tab" errors. https://claude.ai/code/session_01EHGKWXxt4q8QPP4xk5LvhQ
1 parent c6a959b commit 82021b1

2 files changed

Lines changed: 167 additions & 21 deletions

File tree

proto/output/web/classes.js

Lines changed: 146 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1214789,6 +1214789,9 @@ jl_Integer_parseInt = $s => {
12147891214789
}
12147901214790
main: while (true) { switch ($ptr) {
12147911214791
case 0:
1214792+
/* Fix 94: strip locale thousands-separator commas (e.g. "1,110" → "1110") */
1214793+
if ($s && typeof $s.$nativeString === 'string' && $s.$nativeString.indexOf(',') >= 0)
1214794+
$s = $rt_str($s.$nativeString.replace(/,/g, ''));
12147921214795
jl_Integer_$callClinit();
12147931214796
var$2 = 10;
12147941214797
$ptr = 1;
@@ -1608419,7 +1608422,7 @@ jn_URLEncoder_encode0 = ($s, $enc) => {
16084191608422
main: while (true) { switch ($ptr) {
16084201608423
case 0:
16084211608424
/* Fix 86: convert non-TeaVM values to TeaVM string for URL encoding */
1608422-
if ($s === undefined || $s === null || typeof $s === 'string' || typeof $s === 'number') $s = $rt_str($s == null ? '' : String($s));
1608425+
if (!$s || typeof $s.$length !== 'function') $s = $rt_str($s === null || $s === undefined ? '' : (typeof $s === 'object' && $s.$nativeString ? $s.$nativeString : String($s))); /* Fix 94: robust URLEncoder — handles HashMap/objects */
16084231608426
ju_Objects_requireNonNull($s);
16084241608427
ju_Objects_requireNonNull($enc);
16084251608428
$buf = new jl_StringBuffer;
@@ -1608513,7 +1608516,7 @@ jn_URLEncoder_encode = ($s, $enc) => {
16085131608516
main: while (true) { switch ($ptr) {
16085141608517
case 0:
16085151608518
/* Fix 86b: convert non-TeaVM values to TeaVM string */
1608516-
if ($s === undefined || $s === null || typeof $s === 'string' || typeof $s === 'number') $s = $rt_str($s == null ? '' : String($s));
1608519+
if (!$s || typeof $s.$length !== 'function') $s = $rt_str($s === null || $s === undefined ? '' : (typeof $s === 'object' && $s.$nativeString ? $s.$nativeString : String($s))); /* Fix 94: robust URLEncoder — handles HashMap/objects */
16085171608520
ju_Objects_requireNonNull($s);
16085181608521
ju_Objects_requireNonNull($enc);
16085191608522
try {
@@ -1695412,6 +1695415,21 @@ cpr_RPGMain.prototype.$checkForDelayedBootData = function() {
16954121695415
console.info('[Fix88] Heroes injected:', bootData.heroes.length);
16954131695416
}
16954141695417
} catch(heroErr) { console.warn('[Fix88] Hero injection error:', heroErr); }
1695418+
/* Fix 94: populate User object (name, level, resources) from bootData BEFORE refreshUserInfo */
1695419+
try {
1695420+
const _bd = window._dsBootData;
1695421+
const _user = cpr_RPG_app && cpr_RPG_app.$user2;
1695422+
if (_user && _bd) {
1695423+
if (_bd.user_id) _user.$userID0 = Long_fromNumber(_bd.user_id);
1695424+
if (_bd.name) _user.$userName = $rt_str(_bd.name);
1695425+
if ((_bd.team_level||0) > 0) _user.$teamLevel1 = _bd.team_level|0;
1695426+
cprnm_ResourceType_$callClinit();
1695427+
if (_bd.gold !== undefined) cprgo_User_setResource0(_user, cprnm_ResourceType_GOLD, _bd.gold|0, 0);
1695428+
if (_bd.diamonds !== undefined) cprgo_User_setResource0(_user, cprnm_ResourceType_DIAMONDS, _bd.diamonds|0, 0);
1695429+
if (_bd.stamina !== undefined) cprgo_User_setResource0(_user, cprnm_ResourceType_STAMINA, _bd.stamina|0, 0);
1695430+
console.info('[Fix94] User populated: name=' + (_bd.name||'?') + ' lvl=' + (_bd.team_level||0) + ' gold=' + (_bd.gold||0) + ' diamonds=' + (_bd.diamonds||0));
1695431+
}
1695432+
} catch(_uErr) { console.warn('[Fix94] User population error:', _uErr.message||_uErr); }
16954151695433
juca_AtomicReference_set(this.$delayedBootData, bd);
16954161695434
console.info('[Fix88] BootData injected into delayedBootData');
16954171695435
} catch(e) { console.error('[Fix88] BootData injection failed:', e); }
@@ -1702121,27 +1702139,135 @@ function _setupWebGL2Bridge(canvas) {
17021211702139
WebGraphics.prototype.$getFramesPerSecond = function() { return 60; };
17021221702140
WebGraphics.prototype.$getRawDeltaTime = function() { return this.$getDeltaTime(); };
17021231702141
}
1702124-
if (typeof WebAudio !== 'undefined') {
1702125-
// Stub WebAudio methods to prevent crash on sound playback
1702126-
WebAudio.prototype.$newSound = function(file) { return window._fakeAudioDevice; };
1702127-
WebAudio.prototype.$newMusic = function(file) { return window._fakeAudioDevice; };
1702128-
}
1702142+
// Fix 94: Web Audio API engine (replaces no-op stubs)
1702143+
(function() {
1702144+
let _audioCtx = null;
1702145+
function getCtx() {
1702146+
if (!_audioCtx) {
1702147+
try { _audioCtx = new (window.AudioContext || window.webkitAudioContext)(); } catch(e) { return null; }
1702148+
}
1702149+
if (_audioCtx.state === 'suspended') _audioCtx.resume().catch(()=>{});
1702150+
return _audioCtx;
1702151+
}
1702152+
const _bufCache = {};
1702153+
function loadBuffer(path, cb) {
1702154+
const fname = path.split('/').pop();
1702155+
const key = 'sound/' + fname;
1702156+
if (_bufCache[key]) { cb(_bufCache[key]); return; }
1702157+
const ctx = getCtx();
1702158+
if (!ctx) { cb(null); return; }
1702159+
fetch('assets/sound/' + fname)
1702160+
.then(r => r.ok ? r.arrayBuffer() : Promise.reject(r.status))
1702161+
.then(buf => ctx.decodeAudioData(buf))
1702162+
.then(decoded => { _bufCache[key] = decoded; cb(decoded); })
1702163+
.catch(() => cb(null));
1702164+
}
1702165+
function WebAudioSoundImpl(path) {
1702166+
this._path = path; this._buf = null; this._vol = 1.0;
1702167+
this._sources = {}; this._nextId = 1;
1702168+
loadBuffer(path, buf => { this._buf = buf; });
1702169+
}
1702170+
WebAudioSoundImpl.prototype.$play = function() { return this.$play0(this._vol, 1.0, 0.0); };
1702171+
WebAudioSoundImpl.prototype.$play0 = function(vol, pitch, pan) {
1702172+
const ctx = getCtx(); if (!ctx || !this._buf) return -1;
1702173+
const src = ctx.createBufferSource();
1702174+
src.buffer = this._buf;
1702175+
src.playbackRate.value = pitch || 1.0;
1702176+
const gain = ctx.createGain(); gain.gain.value = vol !== undefined ? vol : 1.0;
1702177+
src.connect(gain); gain.connect(ctx.destination);
1702178+
const id = this._nextId++;
1702179+
this._sources[id] = src;
1702180+
src.onended = () => { delete this._sources[id]; };
1702181+
src.start(0); return id;
1702182+
};
1702183+
WebAudioSoundImpl.prototype.$stop = function() {
1702184+
Object.values(this._sources).forEach(s => { try{s.stop();}catch(e){} }); this._sources = {};
1702185+
};
1702186+
WebAudioSoundImpl.prototype.$stop0 = function(id) {
1702187+
if (this._sources[id]) { try{this._sources[id].stop();}catch(e){} delete this._sources[id]; }
1702188+
};
1702189+
WebAudioSoundImpl.prototype.$pause = function() {};
1702190+
WebAudioSoundImpl.prototype.$pause0 = function(id) {};
1702191+
WebAudioSoundImpl.prototype.$resume = function() {};
1702192+
WebAudioSoundImpl.prototype.$resume0 = function(id) {};
1702193+
WebAudioSoundImpl.prototype.$dispose = function() { this.$stop(); };
1702194+
WebAudioSoundImpl.prototype.$setLooping = function(id, loop) { if(this._sources[id]) this._sources[id].loop=!!loop; };
1702195+
WebAudioSoundImpl.prototype.$setVolume = function(id, vol) {};
1702196+
WebAudioSoundImpl.prototype.$setPitch = function(id, pitch) {};
1702197+
WebAudioSoundImpl.prototype.$setPan = function(id, pan, vol) {};
1702198+
WebAudioSoundImpl.prototype.$isPlaying = function(id) { return this._sources[id] ? 1 : 0; };
1702199+
WebAudioSoundImpl.prototype.$isLooping = function(id) { return 0; };
1702200+
WebAudioSoundImpl.prototype.$getVolume = function() { return this._vol; };
1702201+
WebAudioSoundImpl.prototype.$getPosition = function() { return 0.0; };
1702202+
WebAudioSoundImpl.prototype.$setPosition = function(p) {};
1702203+
function WebAudioMusicImpl(path) {
1702204+
this._path = path; this._buf = null; this._src = null;
1702205+
this._gain = null; this._vol = 1.0; this._loop = false;
1702206+
this._playing = false; this._offset = 0; this._startTime = 0;
1702207+
loadBuffer(path, buf => { this._buf = buf; if (this._playing) this._doPlay(); });
1702208+
}
1702209+
WebAudioMusicImpl.prototype._doPlay = function() {
1702210+
const ctx = getCtx(); if (!ctx || !this._buf) { this._playing = true; return; }
1702211+
if (this._src) { try{this._src.stop();}catch(e){} }
1702212+
this._src = ctx.createBufferSource();
1702213+
this._src.buffer = this._buf; this._src.loop = this._loop;
1702214+
this._gain = ctx.createGain(); this._gain.gain.value = this._vol;
1702215+
this._src.connect(this._gain); this._gain.connect(ctx.destination);
1702216+
this._startTime = ctx.currentTime;
1702217+
this._src.start(0, this._offset || 0);
1702218+
this._src.onended = () => { if(!this._loop) { this._playing = false; this._offset = 0; } };
1702219+
};
1702220+
WebAudioMusicImpl.prototype.$play = function() { if(this._playing) return; this._playing=true; if(this._buf) this._doPlay(); };
1702221+
WebAudioMusicImpl.prototype.$stop = function() {
1702222+
if(this._src){try{this._src.stop();}catch(e){} this._src=null;}
1702223+
this._playing=false; this._offset=0;
1702224+
};
1702225+
WebAudioMusicImpl.prototype.$pause = function() {
1702226+
if(!this._playing||!this._src) return;
1702227+
const ctx=getCtx(); if(ctx) this._offset=ctx.currentTime-this._startTime;
1702228+
try{this._src.stop();}catch(e){} this._src=null; this._playing=false;
1702229+
};
1702230+
WebAudioMusicImpl.prototype.$resume = function() { this.$play(); };
1702231+
WebAudioMusicImpl.prototype.$dispose = function() { this.$stop(); this._buf=null; };
1702232+
WebAudioMusicImpl.prototype.$isPlaying = function() { return this._playing ? 1 : 0; };
1702233+
WebAudioMusicImpl.prototype.$isLooping = function() { return this._loop ? 1 : 0; };
1702234+
WebAudioMusicImpl.prototype.$setLooping = function(v) { this._loop=!!v; if(this._src) this._src.loop=this._loop; };
1702235+
WebAudioMusicImpl.prototype.$setVolume = function(v) { this._vol=v; if(this._gain) this._gain.gain.value=v; };
1702236+
WebAudioMusicImpl.prototype.$getVolume = function() { return this._vol; };
1702237+
WebAudioMusicImpl.prototype.$setPosition = function(p) {
1702238+
const was=this._playing; if(was) this.$stop(); this._offset=p;
1702239+
if(was){this._playing=true; if(this._buf) this._doPlay();}
1702240+
};
1702241+
WebAudioMusicImpl.prototype.$getPosition = function() {
1702242+
const ctx=getCtx();
1702243+
if(!ctx||!this._playing||!this._src) return this._offset;
1702244+
return this._offset+(ctx.currentTime-this._startTime);
1702245+
};
1702246+
if (typeof WebAudio !== 'undefined') {
1702247+
WebAudio.prototype.$newSound = function(file) {
1702248+
const path = (file && file.$path0) ? $rt_ustr(file.$path0) : null;
1702249+
if (!path) return window._fakeAudioDevice;
1702250+
return new WebAudioSoundImpl(path);
1702251+
};
1702252+
WebAudio.prototype.$newMusic = function(file) {
1702253+
const path = (file && file.$path0) ? $rt_ustr(file.$path0) : null;
1702254+
if (!path) return window._fakeAudioDevice;
1702255+
return new WebAudioMusicImpl(path);
1702256+
};
1702257+
console.info('[WebAudio] Fix94: Web Audio API engine ready');
1702258+
}
1702259+
})();
17021291702260
window._fakeAudioDevice = {
1702130-
$play: function() {},
1702131-
$stop: function() {},
1702132-
$pause: function() {},
1702133-
$resume: function() {},
1702261+
$play: function() { return -1; }, $play0: function(v,p,pan) { return -1; },
1702262+
$stop: function() {}, $stop0: function(id) {},
1702263+
$pause: function() {}, $pause0: function(id) {},
1702264+
$resume: function() {}, $resume0: function(id) {},
17021341702265
$dispose: function() {},
1702135-
$setLooping: function(v) {},
1702136-
$setVolume: function(v) {},
1702137-
$setPitch: function(v) {},
1702138-
$setPan: function(p,v) {},
1702139-
$isPlaying: function() { return 0; },
1702140-
$isLooping: function() { return 0; },
1702141-
$getVolume: function() { return 1.0; },
1702142-
$getPosition: function() { return 0.0; },
1702266+
$setLooping: function(id,v) {}, $setVolume: function(id,v) {},
1702267+
$setPitch: function(id,v) {}, $setPan: function(id,p,v) {},
1702268+
$isPlaying: function(id) { return 0; }, $isLooping: function(id) { return 0; },
1702269+
$getVolume: function() { return 1.0; }, $getPosition: function() { return 0.0; },
17021431702270
$setPosition: function(p) {},
1702144-
$play0: function(v, p, pan) { return -1; }, // Sound.play(float,float,float)
17021451702271
};
17021461702272
// Phase 3.13: WebGL30 VAO bridge (separate class from WebGL20)
17021471702273
if (typeof WebGL30 !== 'undefined') {

proto/output/web/index.html

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -538,9 +538,29 @@ <h1>&#x1F409; DragonSoul Web</h1>
538538
addLog('WARN preloadTextures: ' + e.message, 'log-warn');
539539
}
540540

541+
// 4. Pre-fetch /login + /api/boot (Fix 94: bypass CLTLS URLEncoder crash)
542+
setOverlay('Connexion au serveur...');
543+
try {
544+
await fetch('/login', { method: 'POST', credentials: 'include' });
545+
const bootResp = await fetch('/api/boot', { credentials: 'include' });
546+
if (bootResp.ok) {
547+
window._dsBootData = await bootResp.json();
548+
const bd = window._dsBootData;
549+
console.info('[BOOT-PRE] Fix94: name=' + (bd.name||'?') +
550+
' lvl=' + (bd.team_level||0) +
551+
' gold=' + (bd.gold||0) +
552+
' heroes=' + ((bd.heroes||[]).length));
553+
} else {
554+
console.warn('[BOOT-PRE] /api/boot HTTP ' + bootResp.status + ' — defaults');
555+
}
556+
} catch(e) {
557+
console.warn('[BOOT-PRE] pre-fetch error:', e.message);
558+
}
559+
window._dsBootReady = true;
560+
541561
setOverlay('Lancement game.create()...');
542562

543-
// 4. Run main() — calls game.create() + first renderFrame()
563+
// 5. Run main() — calls game.create() + first renderFrame()
544564
setStatus('st-create', 'running...', 'pending');
545565
setStatus('st-render', 'waiting...', 'pending');
546566
try {

0 commit comments

Comments
 (0)