diff --git a/ASAservers.html b/ASAservers.html index 419e2bd..574cf05 100644 --- a/ASAservers.html +++ b/ASAservers.html @@ -1,189 +1,610 @@ - - - ObliStudios · ASA Servers — Live Status - - - + + + ObliStudios · ASA Servers — Live Status + + + + + + - + html, body { + height: 100% + } + + body { + margin: 0; + font: 16px/1.6 Inter,system-ui,-apple-system,Segoe UI,Roboto,Helvetica,Arial,sans-serif; + color: var(--text); + background: url('img/BestWCoast.png') no-repeat center center fixed; + background-size: cover; + } + /* Soft vignette */ + body::before { + content: ""; + position: fixed; + inset: 0; + background: linear-gradient( rgba(0,0,0,0.72) 0%, rgba(0,0,0,0.55) 30%, rgba(0,0,0,0.38) 60%, rgba(0,0,0,0.62) 100% ); + z-index: -1; + } + + a { + color: inherit; + text-decoration: none + } + + .container { + max-width: 1100px; + margin: 0 auto; + padding: 0 20px + } + + /* Header (harmonized with other pages) */ + header { + position: sticky; + top: 0; + z-index: 50; + backdrop-filter: saturate(180%) blur(8px); + background: rgba(10,11,16,.6); + border-bottom: 1px solid var(--line); + } + + .nav { + height: 68px; + display: flex; + align-items: center; + justify-content: space-between + } + + .brand { + display: flex; + gap: .65rem; + align-items: center + } + + .brand svg { + width: 30px; + height: 30px; + filter: drop-shadow(0 0 10px rgba(16,227,154,.4)) + } + + .wordmark { + font-weight: 800; + letter-spacing: .2px + } + + .wordmark em { + color: var(--accent); + font-style: normal + } + + .links { + display: flex; + gap: 18px; + color: var(--muted); + font-weight: 600 + } + + .links a:hover { + color: var(--text) + } + + /* Hero */ + h1, h2 { + font-family: Cinzel, Inter, serif + } + + .hero { + padding: 64px 0 24px; + } + + h1 { + margin: .35rem 0 .4rem; + line-height: 1.15; + font-size: clamp(2rem, 1rem + 3vw, 3rem) + } + + .lead { + color: var(--muted); + max-width: 70ch + } + + .notice { + font-size: .9rem; + color: #cfd7e0 + } + + /* Controls bar */ + .controls { + display: flex; + gap: 10px; + align-items: center; + flex-wrap: wrap; + padding: 12px; + margin: 8px 0 18px; + background: rgba(17,20,33,.75); + border: 1px solid rgba(255,255,255,.08); + border-radius: 12px; + backdrop-filter: blur(6px); + } + + .controls input, .controls select, .controls button { + background: #0a0c12; + color: var(--text); + border: 1px solid rgba(255,255,255,.08); + border-radius: 10px; + padding: .55rem .7rem; + font-weight: 600; + } + + .controls button.primary { + background: linear-gradient(135deg,var(--accent),var(--accent2)); + color: #00140d; + border: none; + box-shadow: 0 8px 22px rgba(16,227,154,.25); + } + + .controls .meta { + color: var(--muted); + font-size: .95rem; + margin-left: auto; + display: flex; + gap: 12px; + align-items: center + } + + /* Grid & cards */ + .grid { + display: grid; + gap: 18px + } + + @media (min-width:760px) { + .grid { + grid-template-columns: 1fr 1fr + } + } + + .server-card { + background: rgba(17, 20, 33, 0.85); + border: 1px solid rgba(255,255,255,0.12); + border-radius: var(--radius); + backdrop-filter: blur(6px); + padding: 14px 16px; + box-shadow: var(--shadow); + } + + .server-head { + display: flex; + align-items: center; + justify-content: space-between; + gap: 12px; + } + + .pill { + display: inline-flex; + align-items: center; + gap: .4rem; + padding: .25rem .6rem; + border-radius: 999px; + font-weight: 800; + font-size: .8rem + } + + .up { + background: #0dc07f22; + border: 1px solid #0dc07f66; + color: #b6f0dc + } + + .down { + background: #ff3b3b22; + border: 1px solid #ff3b3b66; + color: #ffc9c9 + } + + .muted { + color: var(--muted) + } + + .ephemeral { + font-size: .9rem; + color: var(--muted) + } + + .row { + display: flex; + gap: 12px; + align-items: center; + flex-wrap: wrap + } + + .copy { + cursor: pointer; + border: 1px solid var(--line); + border-radius: 10px; + padding: .35rem .55rem; + font-weight: 700 + } + + .kvs { + display: grid; + grid-template-columns: repeat(3,1fr); + gap: 8px 14px; + margin-top: .5rem + } + + .kv strong { + display: block; + font-size: .9rem; + color: var(--muted) + } + + .kv span { + font-weight: 800 + } + + /* Player capacity bar */ + .bar { + height: 8px; + border-radius: 999px; + background: #0a0c12; + border: 1px solid rgba(255,255,255,.08); + overflow: hidden + } + + .bar > i { + display: block; + height: 100%; + background: linear-gradient(90deg,var(--accent),var(--accent2)); + width: 0% + } + + /* Ping chip */ + .ping { + display: inline-flex; + align-items: center; + gap: .35rem; + font-weight: 800 + } + + .dot { + width: 10px; + height: 10px; + border-radius: 50% + } + + /* Skeletons */ + .skeleton { + position: relative; + overflow: hidden; + border-radius: var(--radius); + background: rgba(255,255,255,.06); + height: 110px; + border: 1px solid rgba(255,255,255,.08) + } + + .skeleton::after { + content: ""; + position: absolute; + inset: 0; + background: linear-gradient(90deg, transparent, rgba(255,255,255,.08), transparent); + transform: translateX(-100%); + animation: shimmer 1.4s infinite; + } + + @keyframes shimmer { + 100% { + transform: translateX(100%) + } + } + + /* Footer */ + .footer { + padding: 40px 0 64px; + color: var(--muted); + border-top: 1px solid var(--line) + } + + [hidden] { + display: none !important + } + -
- -
+
+ +
-
+
+
+

ARK: Survival Ascended — Live Server Status

+

Real‑time online status, map, players, and ping for every ObliStudios ASA server.

+
This is unofficial and not affiliated with Studio Wildcard.
+
+
+ +
-

ARK: Survival Ascended — Live Server Status

-

Real‑time online status, map, players, and ping for every ObliStudios ASA server.

-
- this is unofficial and not affiliated with Studio Wildcard -
+
+ + + + +
+ + Next update: — +
+
-
-
-

Cluster Status

-
+
+

Cluster Status

+
+ +
+
+
-
- the servers are running on a best-effort basis, 24/7. occasional downtime may occur for maintenance, updates, or unexpected issues. please refer to our discord for planned maintenance windows and updates. -
-
+
+ The servers are running on a best‑effort basis, 24/7. Occasional downtime may occur for maintenance, updates, or unexpected issues. + Visit our

Discord

for planned maintenance windows and updates. +
- + +
+
- + // === TEMPLATES === + function cardTemplate(s, data, ts) { + const online = !!(data && data.online); + const pill = online + ? 'Online' + : 'Offline'; + + const map = data?.map || '—'; + const players = Number.isFinite(data?.players) ? data.players : 0; + const maxPlayers = Number.isFinite(data?.maxPlayers) ? data.maxPlayers : null; + const ping = Number.isFinite(data?.ping) ? data.ping : null; + const endpoint = `${s.host}:${s.port}`; + + const playerBar = Number.isFinite(maxPlayers) ? ` + + ${pct(players, maxPlayers)}% capacity + ` : ''; + + const details = online ? ` +
+
Map${map}
+
Players${players}${maxPlayers ? `/${maxPlayers}` : ''}
+
Ping${pingDot(ping)}${ping ?? '—'} ms
+
+ ${playerBar} + ` : `
${(data && data.error) ? data.error : 'No response from query port'}
`; + + return ` +
+
+

${s.name}

+ ${pill} +
+ +
+ Query: ${endpoint} + +
+ + ${details} + +
+ Updated ${timeAgo(ts)} +
+
+ `; + } + + function render() { + const q = document.getElementById('q').value.trim().toLowerCase(); + const filter = document.getElementById('filter').value; + const sort = document.getElementById('sort').value; + + let rows = state.slice(); + + // Filter + rows = rows.filter(({ s, data }) => { + const hay = `${s.name} ${data?.map || ''}`.toLowerCase(); + const matchesQ = !q || hay.includes(q); + const online = !!data?.online; + const matchesF = filter === 'all' || (filter === 'online' ? online : !online); + return matchesQ && matchesF; + }); + + // Sort + const by = { + status: (a, b) => Number(b.data?.online || 0) - Number(a.data?.online || 0) || a.s.name.localeCompare(b.s.name), + name: (a, b) => a.s.name.localeCompare(b.s.name), + players: (a, b) => (b.data?.players || 0) - (a.data?.players || 0), + ping: (a, b) => (a.data?.ping ?? 1e9) - (b.data?.ping ?? 1e9), + }[sort] || ((a, b) => 0); + rows.sort(by); + + // Render + $list.innerHTML = rows.map(r => cardTemplate(r.s, r.data, r.fetchedAt)).join(''); + + // Summary / meta + const total = state.length; + const onlineCount = state.filter(r => r.data?.online).length; + $summary.textContent = `Online ${onlineCount} / ${total} · Last update ${timeAgo(lastRefresh)}`; + } + + // === DATA FETCH === + function fetchWithTimeout(url, ms = 7000) { + const ctl = new AbortController(); + const id = setTimeout(() => ctl.abort(), ms); + return fetch(url, { cache: 'no-store', signal: ctl.signal }).finally(() => clearTimeout(id)); + } + + async function refresh() { + lastRefresh = Date.now(); + nextTick = 30; + + const results = await Promise.all(SERVERS.map(async s => { + const url = `${API}?ip=${encodeURIComponent(s.host)}&port=${encodeURIComponent(s.port)}`; + try { + const r = await fetchWithTimeout(url, 7000); + const data = await r.json(); + return { s, data, fetchedAt: Date.now() }; + } catch (e) { + return { s, data: { online: false, error: String(e) }, fetchedAt: Date.now() }; + } + })); + + state = results; + // Persist last successful for offline first‑paint + try { localStorage.setItem('asa:last', JSON.stringify({ t: Date.now(), results })); } catch { } + render(); + } + + // Load cached (if present) for instant first paint + (function bootFromCache() { + try { + const cached = JSON.parse(localStorage.getItem('asa:last') || 'null'); + if (cached && Array.isArray(cached.results)) { + state = cached.results; + lastRefresh = cached.t || Date.now(); + render(); + } + } catch { } + })(); + + // Polling and countdown + setInterval(() => { + if (nextTick > 0) nextTick--; + $next.textContent = `Next update: ${nextTick}s`; + if (nextTick === 0) refresh(); + }, 1000); + + // Wire controls + document.getElementById('q').addEventListener('input', render); + document.getElementById('filter').addEventListener('change', render); + document.getElementById('sort').addEventListener('change', render); + document.getElementById('refreshBtn').addEventListener('click', refresh); + + // First live refresh + refresh(); +