-
Server: …
Map: …
Modus: …
Du: …
+
Willkommen auf deinem Server 👋
-
-
- - Workshop prüfen
- - Server-Content
- - Initialisierung
-
+
+
-
Fortschritt
+
-
Workshop0%
-
-
Warte auf Daten …
+
Workshop-Downloadslädt …
+
+
Warte auf Workshop …
+
-
Server-Content0%
-
-
Warte auf Daten …
+
Server-Contentlädt …
+
+
Warte auf Server-Dateien …
+
-
Initialisierung…
-
+
Initialisierungwartet …
+
Warte auf Client-Info …
Datei: –
-
Tipps
@@ -116,7 +111,7 @@ Dieses Overlay benötigt JavaScript, um Server/Map/Modus und Fortschritt zu zeig
- Tab zeigt die Rollen-Übersicht.
@@ -127,103 +122,88 @@ Dieses Overlay benötigt JavaScript, um Server/Map/Modus und Fortschritt zu zeig
(() => {
const $ = id => document.getElementById(id);
- // Elemente
- const ph = { work:$('ph-work'), srv:$('ph-srv'), init:$('ph-init') };
- const work = { fill:$('w-fill'), pct:$('w-pct'), sub:$('w-sub'), total:0, need:0, last:0, done:false };
- const srv = { fill:$('s-fill'), pct:$('s-pct'), sub:$('s-sub'), total:0, need:0, last:0, done:false };
- const init = { pct:$('i-pct'), sub:$('i-sub') };
- let phase = 'work';
+ // Spielername aus Query (bevorzugt), sonst SteamID
+ const qp = new URLSearchParams(location.search);
+ const qpName = decodeURIComponent((qp.get('name')||qp.get('nick')||qp.get('player')||qp.get('n')||'').replace(/\+/g,' ')).trim();
- function setPhase(next){
- phase = next;
- // Chips visuell updaten
- [ph.work, ph.srv, ph.init].forEach(el => el.classList.remove('on'));
- if (next === 'work') ph.work.classList.add('on');
- if (next === 'srv') ph.srv.classList.add('on');
- if (next === 'init') ph.init.classList.add('on');
+ // Grunddaten aus URL für sofortige Anzeige
+ $('map').textContent = qp.get('map') || 'Unbekannt';
+ $('me').textContent = qpName || qp.get('sid') || '—';
+
+ // Step/Phase-Helpers
+ const steps = {
+ work: { stepEl: $('st-work'), bar: $('w-bar'), right: $('w-right'), sub: $('w-sub') },
+ srv: { stepEl: $('st-srv'), bar: $('s-bar'), right: $('s-right'), sub: $('s-sub') },
+ init: { stepEl: $('st-init'), bar: $('i-bar'), right: $('i-right'), sub: $('i-sub') }
+ };
+ let active = 'work';
+
+ function setActive(key){
+ ['work','srv','init'].forEach(k=>{
+ const c = steps[k].stepEl.classList;
+ c.remove('active');
+ });
+ steps[key].stepEl.classList.add('active');
+ active = key;
}
- function setPct(obj, v){
- v = Math.max(obj.last || 0, Math.min(100, Math.round(v)));
- obj.last = v;
- obj.fill.style.width = v + '%';
- obj.pct.textContent = v + '%';
+ function markDone(key, text){
+ steps[key].stepEl.classList.add('done');
+ steps[key].bar.classList.remove('indet');
+ steps[key].bar.classList.add('done');
+ steps[key].right.textContent = text || 'fertig';
}
- function updateBars(){
- // Workshop
- if (work.total > 0){
- const done = Math.max(0, work.total - (work.need||0));
- setPct(work, (done / work.total) * 100);
- work.sub.textContent = `${done} / ${work.total} Dateien`;
- if (!work.done && done >= work.total){ work.done = true; setPhase('srv'); }
- }
- // Server
- if (srv.total > 0){
- const done = Math.max(0, srv.total - (srv.need||0));
- setPct(srv, (done / srv.total) * 100);
- srv.sub.textContent = `${done} / ${srv.total} Dateien`;
- if (!srv.done && done >= srv.total){ srv.done = true; setPhase('init'); }
- }
- }
-
- // ===== von GMod aufgerufene Funktionen =====
- window.GameDetails = function(serverName, serverURL, mapName, maxPlayers, steamID, gamemode){
- $('sv').textContent = serverName || 'Unbekannt';
+ // ===== GMod Callbacks =====
+ window.GameDetails = function(serverName, serverUrl, mapName, maxPlayers, steamID, gamemode){
+ $('sv').textContent = serverName || 'Unbekannt';
$('sv2').textContent = serverName || 'deinem Server';
- $('map').textContent = mapName || 'Unbekannt';
- $('gm').textContent = gamemode || 'TTT2';
- $('me').textContent = steamID || '—';
+ $('gm').textContent = gamemode || 'TTT2';
+ if (mapName) $('map').textContent = mapName;
+
+ // Name bevorzugen, sonst SteamID
+ $('me').textContent = qpName || steamID || '—';
};
- // GMod ruft diese zweimal in „Wellen“ auf: erst Workshop, dann Server-DLs.
- window.SetFilesTotal = function(total){
- total = Number(total) || 0;
- // Heuristik: Wenn im Workshop die Gesamtzahl plötzlich kleiner wird, dann startet Server-Content
- if (phase === 'work' && work.total && total && total < work.total && (work.last >= 95 || work.done)){
- work.done = true; setPhase('srv');
- }
- if (phase === 'work'){ work.total = total; }
- else if (phase === 'srv'){ srv.total = total; }
- updateBars();
- };
-
- window.SetFilesNeeded = function(need){
- need = Number(need) || 0;
- if (phase === 'work'){ work.need = need; }
- else if (phase === 'srv'){ srv.need = need; }
- updateBars();
- };
+ // Wir ignorieren die Prozent-Callbacks absichtlich (falsch/inkonsistent).
+ window.SetFilesTotal = function(){ /* noop (indeterminierte Balken) */ };
+ window.SetFilesNeeded = function(){ /* noop (indeterminierte Balken) */ };
window.DownloadingFile = function(path){
$('file').textContent = (path||'').split('/').slice(-3).join('/');
+ // Kleiner UX-Touch: zeige Datei unter der aktuell aktiven Phase
+ if (active === 'work') steps.work.sub.textContent = 'Lade: ' + $('file').textContent;
+ else if (active === 'srv') steps.srv.sub.textContent = 'Lade: ' + $('file').textContent;
};
window.SetStatusChanged = function(status){
- const s = (status || '').toLowerCase();
+ const s = (status||'').toLowerCase();
- // Phasenwechsel anhand Status
- if (s.includes('workshop')) setPhase('work');
- if (s.includes('download') || s.includes('materials') || s.includes('models') || s.includes('.bsp')) setPhase('srv');
- if (s.includes('sending client info') || s.includes('precaching') || s.includes('client info') || s.includes('spawn'))
- setPhase('init');
+ // rudimentäres Phasen-Mapping nur für Optik
+ if (s.includes('workshop')) { setActive('work'); steps.work.sub.textContent = status; }
+ else if (s.includes('download') || s.includes('materials') || s.includes('models') || s.includes('.bsp') || s.includes('file')) {
+ steps.work.stepEl.classList.add('done'); setActive('srv'); steps.srv.sub.textContent = status;
+ }
+ else if (s.includes('sending client info') || s.includes('precaching') || s.includes('client info') || s.includes('parsing')) {
+ steps.work.stepEl.classList.add('done'); steps.srv.stepEl.classList.add('done');
+ setActive('init'); steps.init.sub.textContent = status;
+ // wenn wir hier sind, frieren wir die Balken hübsch ein
+ markDone('work'); markDone('srv'); markDone('init','bereit');
+ }
- // Untertitel je nach Phase
- if (phase === 'work') work.sub.textContent = status || '';
- if (phase === 'srv') srv.sub.textContent = status || '';
- if (phase === 'init'){ init.sub.textContent = status || 'Initialisiere …'; init.pct.textContent = '…'; }
+ // falls ein „complete“ kommt, markiere die aktuelle Phase als done
+ if (s.includes('complete')) {
+ if (active === 'work') { markDone('work'); setActive('srv'); }
+ else if (active === 'srv') { markDone('srv'); setActive('init'); }
+ }
};
- // Kleiner Fallback fürs Testen im normalen Browser (kein Muss)
- if (!('GameDetails' in window)) {
- // Dummy-Werte
- window.GameDetails('Gaming Mäuse - TTT', '', 'ttt_lego', 16, 'STEAM_0:1:123456', 'terrortown');
- // Fake-Progress
- let a=0,b=0; const fake = setInterval(()=>{
- if (a < 100){ work.total=100; work.need=100-a; a+=7; updateBars(); }
- else if (b < 100){ setPhase('srv'); srv.total=100; srv.need=100-b; b+=12; updateBars(); }
- else { setPhase('init'); clearInterval(fake); }
- }, 500);
+ // Fallback, falls GMod gar nichts ruft (nur für Browser-Preview)
+ if (document.visibilityState !== 'hidden') {
+ // steppt automatisch durch, rein für Demo/Preview
+ setTimeout(()=>{ setActive('srv'); }, 2000);
+ setTimeout(()=>{ setActive('init'); }, 4000);
+ setTimeout(()=>{ markDone('work'); markDone('srv'); markDone('init','bereit'); }, 6000);
}
})();