function login_then_go_to(url) { const domain_name = document.location.hostname.substring(document.location.hostname.indexOf('.') + 1); document.cookie = `login_to=${encodeURIComponent(url)}; path=/; domain=${domain_name}; secure; SameSite=Strict; Max-Age=30000`; document.location = '/osu_login'; } function ask_for_manual_lobby_creation(lobby_settings) { document.querySelector('main .loading-placeholder').replaceWith( document.querySelector('#manual-lobby-creation-template').content.cloneNode(true), ); document.querySelector('main .create-lobby-with-ref-btn').addEventListener('click', (evt) => { evt.preventDefault(); evt.stopPropagation(); // Input shown if bot can't create any more lobbies and needs a user-made lobby const match_input = document.querySelector('main input[name="tournament-url"]'); if (match_input.value) { lobby_settings.match_id = parseInt(match_input.value.split('/').reverse()[0], 10); } try_creating_lobby(lobby_settings); }); } async function try_creating_lobby(lobby_settings) { document.querySelector('main').innerHTML = `

Creating lobby...

`; try { const res = await fetch('/api/create-lobby/', { body: JSON.stringify(lobby_settings), credentials: 'include', headers: { 'Accept': 'application/json', 'Content-Type': 'application/json', }, method: 'POST', }); const json_res = await res.json(); if (json_res.error) { if (json_res.details == 'Cannot create any more matches.') { ask_for_manual_lobby_creation(lobby_settings); return; } throw new Error(json_res.details || json_res.error); } document.querySelector('main .loading-placeholder').replaceWith( document.querySelector('#lobby-created-template').content.cloneNode(true), ); document.querySelector('.lobby').replaceWith(render_lobby(json_res.lobby)); document.querySelector('.info').appendChild(document.querySelector('#command-list-template').content.cloneNode(true)); } catch (err) { document.querySelector('main').innerHTML = ` An error occurred while creating the lobby:
`; document.querySelector('.error-msg').innerText = err.message; } } function render_lobby(lobby, user_has_lobby_open) { const lobby_div = document.createElement('div'); let map_pool_info = 'All maps'; if (lobby.map_pool == 'collection') { const pool = document.createElement('a'); pool.href = 'https://osucollector.com/collections/' + lobby.collection_id; pool.target = '_blank'; pool.innerText = lobby.collection_name; map_pool_info = pool.outerHTML; } let map_selection_info = 'Random'; if (lobby.map_selection_algo == 'pp') { map_selection_info = 'Average player pp'; } else if (lobby.map_selection_algo == 'elo') { map_selection_info = 'Average player elo'; } if (lobby.filters.length > 0) { const filter_span = document.createElement('span'); filter_span.classList = 'cursor-help underline underline-offset-2 decoration-1 decoration-dotted'; filter_span.innerText = lobby.filters.length + ' filters'; filter_span.title = ''; for (const filter of lobby.filters) { filter_span.title += `${filter.name} between ${filter.min} and ${filter.max}\n`; } map_selection_info += ' (' + filter_span.outerHTML + ')'; } let mods_info = 'None'; if (lobby.mod_list.length > 0) { mods_info = lobby.mod_list.join(', '); } if (lobby.freemod) { mods_info += ' (freemod)'; } let status = ''; if (lobby.end_time) { function reltime(diff) { const rtf = new Intl.RelativeTimeFormat('en', {numeric: 'auto'}); if (diff > -60) { return rtf.format(Math.round(diff), 'second'); } else if (diff > -3600) { return rtf.format(Math.round(diff / 60), 'minute'); } else if (diff > -86400) { return rtf.format(Math.round(diff / 3600), 'hour'); } else { return rtf.format(Math.round(diff / 86400), 'day'); } } const seconds_ago = reltime((lobby.end_time - Date.now()) / 1000); const closing_time = new Intl.DateTimeFormat(undefined, { year: 'numeric', month: 'numeric', day: 'numeric', hour: 'numeric', minute: 'numeric', second: 'numeric', }).format(new Date(lobby.end_time)); status = `Closed ${seconds_ago}`; } else { status = `${lobby.nb_players}/16 players`; } lobby_div.style = `border: solid ${lobby.color} 2px`; lobby_div.innerHTML += `
Map pool: ${map_pool_info}
Map selection: ${map_selection_info}
Mods: ${mods_info}
${status} ยท Created by Lobby creator ${lobby.creator_name}
`; if (lobby.end_time) { lobby_div.className = 'flex-1 m-2 rounded-md'; lobby_div.innerHTML += `
`; // TODO: make "Copy settings" buttons instead of too basic "reopen" lobby_div.querySelector('.go-to-create-lobby').style = `background-color: ${lobby.color}; margin: auto; display: block`; lobby_div.querySelector('.go-to-create-lobby').addEventListener('click', (evt) => { evt.preventDefault(); evt.stopPropagation(); if (window.logged_user_id) { document.location = `/reopen-lobby/${lobby.match_id}`; } else { return login_then_go_to(`${location.origin}/reopen-lobby/${lobby.match_id}`); } }); } else { // Font Awesome Free 6.1.1 by @fontawesome - https://fontawesome.com // License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) // Copyright 2022 Fonticons, Inc. const fa_open = ''; const fa_invite = ''; lobby_div.className = 'flex m-2 rounded-md'; lobby_div.innerHTML += ` `; lobby_div.querySelector('.get-invite-btn').addEventListener('click', (evt) => { evt.preventDefault(); evt.stopPropagation(); if (!window.logged_user_id) { return login_then_go_to(`${location.origin}/lobbies/`); } async function request_invite() { try { const res = await fetch(`/get-invite/${lobby.bancho_id}`, { credentials: 'include', headers: { 'Accept': 'application/json', 'Content-Type': 'application/json', }, method: 'POST', }); const json_res = await res.json(); if (json_res.error) throw new Error(json_res.error); alert('An invite to the lobby has been sent. Check your in-game messages.'); } catch (err) { alert(err.message); } } request_invite(); }); } lobby_div.querySelector('.lobby-title').innerText = lobby.name; // Mania: display keycounts if (window.selected_ruleset == 3) { lobby_div.querySelector('.mania-keycounts').classList.remove('hidden'); } return lobby_div; } async function render_lobbies() { const template = document.querySelector('#lobbies-template').content.cloneNode(true); const res = await fetch(`/api/lobbies/`, { method: 'GET', headers: { 'Accept': 'application/json', 'Content-Type': 'application/json', }, }); const json = await res.json(); let user_has_lobby_open = false; const open_lobbies = []; const closed_lobbies = []; for (const lobby of json) { if (lobby.end_time) { closed_lobbies.push(lobby); } else { open_lobbies.push(lobby); if (lobby.creator_id == window.logged_user_id) { // User already created a lobby: hide the "Create lobby" button template.querySelector('.lobby-creation-banner').hidden = true; user_has_lobby_open = true; } } } if (open_lobbies.length == 0) { template.querySelector('.lobby-creation-banner span').innerText = 'No lobbies are open right now.'; } else { template.querySelector('.lobby-creation-banner span').innerText = 'Not satisfied?'; } template.querySelector('.go-to-create-lobby').addEventListener('click', (evt) => { evt.preventDefault(); evt.stopPropagation(); if (window.logged_user_id) { document.location = '/create-lobby/'; } else { return login_then_go_to(`${location.origin}/create-lobby/`); } }); // Display open lobbies let list = template.querySelector('.open-lobby-list'); for (const lobby of open_lobbies) { list.appendChild(render_lobby(lobby)); } // Display closed lobbies list = template.querySelector('.closed-lobby-list'); for (const lobby of closed_lobbies) { list.appendChild(render_lobby(lobby, user_has_lobby_open)); } document.querySelector('main').replaceChildren(template); setTimeout(render_lobbies, 10000); } async function render_create_lobby() { const rulesets = ['osu', 'taiko', 'catch', 'mania']; const template = document.querySelector('#lobby-creation-template').content.cloneNode(true); template.querySelector('h1').innerText = `New ${rulesets[window.selected_ruleset]} lobby`; document.querySelector('main .loading-placeholder').replaceWith(template); const filter_list = document.querySelector('.filter-list'); const render_filter = (options) => `
`; filter_list.innerHTML += render_filter({name: 'pp', label: 'performance points (PP)', min: 0, max: 2000, default_min: 150, default_max: 200, step: 25}); filter_list.innerHTML += render_filter({name: 'sr', label: 'star rating (SR)', min: 0, max: 20, default_min: 5, default_max: 6, step: 0.5}); filter_list.innerHTML += render_filter({name: 'ar', label: 'approach rate (AR)', min: 0, max: 11, default_min: 9, default_max: 9.5, step: 0.1}); filter_list.innerHTML += render_filter({name: 'cs', label: 'circle size (CS)', min: 0, max: 10, default_min: 5, default_max: 6, step: 1}); filter_list.innerHTML += render_filter({name: 'od', label: 'overall difficulty (OD)', min: 0, max: 10, default_min: 6, default_max: 8, step: 1}); filter_list.innerHTML += render_filter({name: 'bpm', label: 'beats per minute (BPM)', min: 0, max: 1000, default_min: 160, default_max: 220, step: 10}); filter_list.innerHTML += render_filter({name: 'length', label: 'length (in seconds)', min: 0, max: 3600, default_min: 30, default_max: 500, step: 30}); document.querySelector('#length_filter').checked = true; document.querySelector('input[name="title"]').addEventListener('input', (evt) => { let title_preview = evt.target.value; const replacements = [ ['$min_stars', '0'], ['$avg_stars', '5.5'], ['$max_stars', '11'], ['$stars', '0-11'], ['$min_elo', '1200'], ['$avg_elo', '1500'], ['$max_elo', '1800'], ['$elo', '1500'], ['$min_pp', '100'], ['$avg_pp', '150'], ['$max_pp', '200'], ['$pp', '150'], ]; for (let i = 0; i < replacements.length; i++) { title_preview = title_preview.replaceAll(replacements[i][0], replacements[i][1]); } title_preview = title_preview.substring(0, 50); document.querySelector('.preview').innerText = title_preview; }); document.querySelectorAll('.filter').forEach((filter) => { filter.querySelector('input[type="checkbox"]').addEventListener('change', function() { const fieldset = filter.querySelector('fieldset'); fieldset.disabled = !fieldset.disabled; }); }); // Mania-specific mods if (window.selected_ruleset == 3) { document.querySelector('.mr').classList.remove('hidden'); document.querySelector('.co').classList.remove('hidden'); document.querySelector('.fi').classList.remove('hidden'); let mods = ''; for (let i = 1; i <= 9; i++) { mods += `
${i}K
${i}K
`; } document.querySelector('.mania-keycount-settings').innerHTML = `

Key count

`; document.querySelectorAll('.mania-keycount-settings .mod-btn').forEach((btn) => btn.addEventListener('click', function() { this.classList.toggle('mod-btn-selected'); })); } // Click to toggle collapse document.querySelectorAll('.collapser').forEach((collapser) => { collapser.parentElement.addEventListener('click', () => { collapser.classList.toggle('rotated'); collapser.parentElement.nextElementSibling.classList.toggle('hidden'); }); }); // Circle size does not apply for taiko/manio if (window.selected_ruleset == 1 || window.selected_ruleset == 3) { document.querySelector('#cs_filter').parentElement.parentElement.classList.add('hidden'); } document.querySelectorAll('.mod-settings .mod-btn').forEach((btn) => btn.addEventListener('click', function() { const NM = document.querySelector('.nm'); const DT = document.querySelector('.dt'); const NC = document.querySelector('.nc'); const HT = document.querySelector('.ht'); const EZ = document.querySelector('.ez'); const HR = document.querySelector('.hr'); const mod = this.querySelector('div').innerText; this.classList.toggle('mod-btn-selected'); let selected = this.classList.contains('mod-btn-selected'); if (mod != 'NM' && mod != 'DT' && mod != 'NC' && mod != 'HT') { NM.classList.remove('mod-btn-selected'); } if (mod == 'NM' && selected) { document.querySelectorAll('.mod-settings .mod-btn').forEach((btn) => { if (btn.classList.contains('dt') || btn.classList.contains('nc') || btn.classList.contains('ht')) return; btn.classList.remove('mod-btn-selected'); }); this.classList.add('mod-btn-selected'); } if (mod == 'DT' && !selected) { DT.classList.toggle('hidden'); NC.classList.toggle('hidden'); NC.classList.add('mod-btn-selected'); selected = true; } if (mod == 'NC') { DT.classList.toggle('hidden'); NC.classList.toggle('hidden'); } if (selected && (mod == 'DT' || mod == 'NC')) { HT.classList.remove('mod-btn-selected'); } if (selected && mod == 'HT') { DT.classList.remove('mod-btn-selected'); DT.classList.remove('hidden'); NC.classList.remove('mod-btn-selected'); NC.classList.add('hidden'); } if (selected && mod == 'EZ') { HR.classList.remove('mod-btn-selected'); } if (selected && mod == 'HR') { EZ.classList.remove('mod-btn-selected'); } })); if (window.last_match_id) { const recreate_lobby_div = document.createElement('div'); recreate_lobby_div.className = 'border-2 border-orange-600 rounded-lg p-3 text-center mx-2 mt-4'; recreate_lobby_div.innerHTML = `

You have already created a lobby, but it has been closed due to inactivity.

If you wish to keep the same settings, you can just reopen it:

`; document.querySelector('main .lobby-settings').append(recreate_lobby_div); document.querySelector('.reopen-lobby').addEventListener('click', (evt) => { evt.preventDefault(); evt.stopPropagation(); if (window.logged_user_id) { document.location = `/reopen-lobby/${window.last_match_id}`; } else { return login_then_go_to(`${location.origin}/reopen-lobby/${window.last_match_id}`); } }); } document.querySelector('main .create-lobby-btn').addEventListener('click', (evt) => { evt.preventDefault(); evt.stopPropagation(); const lobby_settings = { title: document.querySelector('input[name="title"]').value, map_selection_algo: document.querySelector('main input[name="map-selection-type"]:checked').value, map_pool: document.querySelector('main input[name="map-pool"]:checked').value, collection_id: null, mod_list: [], filters: [], key_count: [], }; if (lobby_settings.map_pool == 'collection') { const collection_input = document.querySelector('main input[name="collection-url"]'); lobby_settings.collection_id = parseInt(collection_input.value.split('/').reverse()[0], 10); } const selected_mods = document.querySelectorAll('.mod-settings .mod-btn-selected'); for (const mod of selected_mods) { lobby_settings.mod_list.push(mod.innerText.trim()); } const filters = document.querySelectorAll('.filter'); for (const filter of filters) { const checkbox = filter.querySelector('input[type="checkbox"]'); if (!checkbox.checked) continue; const name = checkbox.id.substring(0, checkbox.id.indexOf('_filter')); const min = document.querySelector(`input[name="min_${name}"]`).value; const max = document.querySelector(`input[name="max_${name}"]`).value; lobby_settings.filters.push({name, min, max}); } if (window.selected_ruleset == 3) { const selected_keys = document.querySelectorAll('.mania-keycount-settings .mod-btn-selected'); for (const key of selected_keys) { lobby_settings.key_count.push(parseInt(key.innerText, 10)); } } try_creating_lobby(lobby_settings); }); const radios = document.querySelectorAll('.radio-area'); for (const area of radios) { area.addEventListener('click', function() { this.querySelector('input[type="radio"]').click(); }); } } let m; if (m = location.pathname.match(/\/create-lobby\//)) { render_create_lobby(); } else if (m = location.pathname.match(/\/reopen-lobby\/(\d+)/)) { try_creating_lobby({old_match_id: parseInt(m[1], 10)}); } else if (m = location.pathname.match(/\/lobbies\//)) { render_lobbies(); }