diff --git a/src/lib/components/popups/EditMap.svelte b/src/lib/components/popups/EditMap.svelte
index 2d2833c..bd65ba6 100644
--- a/src/lib/components/popups/EditMap.svelte
+++ b/src/lib/components/popups/EditMap.svelte
@@ -5,6 +5,7 @@
import { TAServerPort, TAServerUrl, authTokenStore, client } from "$lib/stores";
import { v4 as uuidv4 } from "uuid";
import { onMount } from "svelte";
+ import type { BeatSaverMap } from "$lib/services/beatsaver";
export let tournamentGuid: string;
export let poolGuid: string;
@@ -53,33 +54,6 @@
// Playing-specific settings
let disableCustomNotesOnStream = false;
-
- type BeatSaverMap = {
- id: string;
- metadata: {
- songName: string;
- songAuthorName: string;
- levelAuthorName: string;
- songSubName: string;
- duration: number;
- bpm: number;
- };
- versions: Array<{
- hash: string;
- state: string;
- createdAt: string;
- downloadURL: string;
- diffs: Array<{
- characteristic: string;
- difficulty: string;
- maxScore: number;
- nps: number;
- }>;
- coverURL: string;
- previewURL: string;
- }>;
- uploaded: string;
- };
// Beat Saber Modifiers with incompatibility groups
interface Modifier {
@@ -433,7 +407,7 @@
selectedCharacteristic = availableCharacteristics[0] || "";
selectedDifficulty = availableDifficulties[selectedCharacteristic]?.[0] || "";
- mapBeatmapId = `custom_level_${latestVersion.hash}`;
+ mapBeatmapId = `custom_level_${latestVersion.hash.toLocaleUpperCase()}`;
return data;
} catch (err) {
@@ -510,20 +484,25 @@
disableScoresaberSubmission: mode === 'playing' ? disableScoresaberSubmission : false,
showScoreboard: mode === 'playing' ? showScoreboard : false,
useSync: false,
- target: 0
+ target: 0,
+ playerSettings: {
+ options: 0,
+ playerHeight: 0,
+ sfxVolume: 0,
+ saberTrailIntensity: 0,
+ noteJumpStartBeatOffset: 0,
+ noteJumpFixedDuration: 0,
+ noteJumpDurationTypeSettings: 0,
+ arcVisibilityType: 0,
+ }
}
};
if (map) {
- // I will need to fix this, for now this is a TA server issue
dispatch('mapUpdated', { map: mapData });
} else {
- await client.addTournamentPoolMaps(tournamentGuid, poolGuid, [mapData]);
dispatch('mapAdded', { map: mapData });
}
-
- // if (response.type !== Response_ResponseType.Success) {
- // throw new Error("Failed to save map");
- // }
+
isLoading = false;
return dispatch('close');
} catch (err) {
@@ -747,19 +726,7 @@
-
-
-
-
+
diff --git a/src/routes/tournaments/+page.svelte b/src/routes/tournaments/+page.svelte
index e19c1e3..00c4b81 100644
--- a/src/routes/tournaments/+page.svelte
+++ b/src/routes/tournaments/+page.svelte
@@ -3,7 +3,7 @@
import { onMount, onDestroy } from 'svelte';
import Notification from '$lib/components/notifications/Popup.svelte';
import { discordAuthUrl } from '$lib/config.json';
- import { discordDataStore, discordTokenStore, authTokenStore, TAServerPort, TAServerUrl, client } from '$lib/stores';
+ import { discordDataStore, discordTokenStore, authTokenStore, TAServerPort, TAServerUrl, client, TAServerPlayerPort } from '$lib/stores';
import { TAClient, Response_ResponseType, Tournament } from 'moons-ta-client';
import { bufferToImageUrl } from '$lib/services/taImages';
import { v4 as uuidv4 } from "uuid";
@@ -125,13 +125,6 @@
}
});
- onDestroy(() => {
- // if (client.isConnected == true) {
- // // Properly disconnect and clean up any listeners
- // client.disconnect();
- // }
- });
-
async function fetchTournamentAuthorisedUsers(tournamentGuid: string) {
// Get the authorised users for the tournament
let authorisedUsers = [];
@@ -213,14 +206,7 @@
try {
creating = true;
- // Connect to the TA server
- const connectResult = await client.connect('server.tournamentassistant.net', '8676');
-
- if (connectResult.details.oneofKind === "connect" && connectResult.type === Response_ResponseType.Fail) {
- authError = connectResult.details.connect.message;
- creating = false;
- return;
- }
+ if(!client.isConnected) return;
// Create the tournament object
let tournament: Tournament = {
@@ -241,6 +227,12 @@
pools: [],
allowUnauthorizedView: true
},
+ server: {
+ name: `${$TAServerUrl}:${$TAServerPort}`,
+ address: $TAServerUrl,
+ port: $TAServerPlayerPort,
+ websocketPort: $TAServerPort
+ }
};
// Convert image to Uint8Array if an image was provided
diff --git a/src/routes/tournaments/[tournamentguid]/+page.svelte b/src/routes/tournaments/[tournamentguid]/+page.svelte
index 31a702d..cf55526 100644
--- a/src/routes/tournaments/[tournamentguid]/+page.svelte
+++ b/src/routes/tournaments/[tournamentguid]/+page.svelte
@@ -3,7 +3,7 @@
import { onMount, onDestroy } from "svelte";
import { bkAPIUrl } from '$lib/config.json';
//@ts-ignore
- import { Match, Tournament, TAClient, Response_ResponseType } from 'moons-ta-client';
+ import { Match, Tournament, TAClient, Response_ResponseType, User, User_PlayStates } from 'moons-ta-client';
import { writable } from "svelte/store";
import SideMenu from "$lib/components/menus/SideMenuTournaments.svelte";
import { goto } from "$app/navigation";
@@ -15,25 +15,10 @@
let isLoading = false;
let error: string | null = null;
let matches: Match[] = [];
- let availablePlayers: any[] = [];
- let selectedPlayers: any[] = [];
- let isCreatingMatch: boolean = false;
+ let availablePlayers: User[] = [];
// Store for selected player guids
const selectedPlayerGuids = writable
([]);
-
- // Player status enum
- enum PlayerStatus {
- Downloading = "downloading",
- SelectingSong = "selecting",
- Idle = "idle"
- }
-
- enum PlayerPlayState {
- In_Menu = 0,
- Waiting_For_Coordinator = 1,
- In_Game = 2
- }
// Remove 'Bearer ' from the token
if($authTokenStore) {
@@ -49,13 +34,13 @@
}
// Return status color based on player status
- function getStatusColor(status: PlayerStatus): string {
+ function getStatusColor(status: User_PlayStates): string {
switch (status) {
- case PlayerStatus.Downloading:
+ case User_PlayStates.InGame:
return "var(--danger-color)";
- case PlayerStatus.SelectingSong:
+ case User_PlayStates.InMenu:
return "#FCD34D"; // Yellow
- case PlayerStatus.Idle:
+ case User_PlayStates.WaitingForCoordinator:
return "#10B981"; // Green
default:
return "var(--text-secondary)";
@@ -110,6 +95,7 @@
tournament = client.stateManager.getTournament(tournamentGuid);
if(!tournament?.users.some(user => user.guid == client.stateManager.getSelfGuid())) {
const joinResult = await client.joinTournament(tournamentGuid);
+ console.log('jointournament', joinResult);
if (joinResult.details.oneofKind !== 'join' || joinResult.type === Response_ResponseType.Fail) {
throw new Error('Could not join tournament');
}
@@ -159,19 +145,46 @@
associatedUsers: $selectedPlayerGuids
});
- console.log(response)
+ console.log("Match created! Response: ", response);
const newMatch = (response as any).details.createMatch.match;
goto(`/tournaments/${tournamentGuid}/matches/${newMatch.guid}`);
}
+ async function handleUserConnected(params: [User, Tournament]) {
+ if(params[1].guid == tournament?.guid) {
+ availablePlayers.push(params[0]);
+ }
+ }
+
+ async function handleUserDisconnected(params: [User, Tournament]) {
+ if(params[1].guid == tournament?.guid) {
+ if(availablePlayers.some(x => x.guid == params[0].guid)) {
+ availablePlayers = availablePlayers.filter(x => x.guid !== params[0].guid);
+ } else {
+ let newMatchObject = matches.find(x => x.associatedUsers.includes(params[0].guid))!;
+ newMatchObject.associatedUsers = newMatchObject.associatedUsers.filter(x => x !== params[0].guid);
+ matches = [
+ ...matches.filter(x => x.guid !== newMatchObject.guid),
+ newMatchObject
+ ]
+ }
+ }
+ }
+
+ function getDiscordNameBecauseShit(user: User) {
+ return user.discordInfo?.username || "";
+ }
+
onMount(async() => {
if ($authTokenStore) {
await fetchTournamentData();
client.stateManager.on('matchCreated', handleMatchCreated);
client.stateManager.on('matchDeleted', handleMatchDeleted);
client.stateManager.on('matchUpdated', handleMatchUpdated);
+ client.stateManager.on('userConnected', handleUserConnected);
+ client.stateManager.on('userDisconnected', handleUserDisconnected);
} else {
window.location.href = "/discordAuth"
}
@@ -181,6 +194,8 @@
client.stateManager.removeListener('matchUpdated', handleMatchUpdated);
client.stateManager.removeListener('matchDeleted', handleMatchDeleted);
client.stateManager.removeListener('matchCreated', handleMatchDeleted);
+ client.stateManager.removeListener('userConnected', handleUserConnected);
+ client.stateManager.removeListener('userDisconnected', handleUserDisconnected);
client.removeListener('createdMatch', handleMatchUpdated)
// client.disconnect();
});
@@ -287,8 +302,8 @@
alt="Player"
class="player-avatar">
-
{player.name} ({player.discordInfo.username})
-
{PlayerPlayState[player.playState]}
+
{player.name} ({getDiscordNameBecauseShit(player)})
+
{User_PlayStates[player.playState]}
{/each}
diff --git a/src/routes/tournaments/[tournamentguid]/mappools/[poolGuid]/+page.svelte b/src/routes/tournaments/[tournamentguid]/mappools/[poolGuid]/+page.svelte
index 9c53b60..bbdd2ae 100644
--- a/src/routes/tournaments/[tournamentguid]/mappools/[poolGuid]/+page.svelte
+++ b/src/routes/tournaments/[tournamentguid]/mappools/[poolGuid]/+page.svelte
@@ -149,16 +149,22 @@
}
async function handleMapUpdated(event: CustomEvent) {
- const res = await client.updateTournamentPoolMap(tournamentGuid, poolGuid, event.detail.map);
- console.log(res)
+ await client.updateTournamentPoolMap(tournamentGuid, poolGuid, event.detail.map);
fetchMapPoolData();
showSuccessNotification = true;
+ setTimeout(() => {
+ showSuccessNotification = false;
+ }, 2500);
successMessage = "Map successfully updated!";
}
- function handleMapAdded(event: CustomEvent) {
+ async function handleMapAdded(event: CustomEvent) {
+ await client.addTournamentPoolMaps(tournamentGuid, poolGuid, [event.detail.map]);
fetchMapPoolData();
showSuccessNotification = true;
+ setTimeout(() => {
+ showSuccessNotification = false;
+ }, 2500);
successMessage = "Map successfully added!";
}
@@ -260,25 +266,25 @@
}
const response: BeatSaverMap = await data.json();
-
+
return response;
}
function getActiveModifiersCompact(gameplayModifiers: number): string[] {
const activeModifiers: string[] = [];
- // Get all numeric enum values and sort them
+ // Get all numeric enum values (powers of 2) and sort them
const values = Object.values(GameplayModifiers_GameOptions)
.filter((value): value is number =>
typeof value === 'number' &&
- value !== GameplayModifiers_GameOptions.None
+ value !== GameplayModifiers_GameOptions.None &&
+ value > 0
)
.sort((a, b) => a - b);
-
- console.log(values)
for (const value of values) {
- if (gameplayModifiers & value) {
+ // Use bitwise AND to check if this specific flag is set
+ if ((gameplayModifiers & value) === value) {
const name = modifierNameMap[value];
if (name && name.trim() !== "") {
activeModifiers.push(name);
@@ -290,11 +296,11 @@
}
function getModifierArray(map: any): string[] {
- if (map.taData.gameplayParameters?.gameplayModifiers.options == 0) {
+ if (map.taData.gameplayParameters?.gameplayModifiers?.options == 0) {
return [];
}
- return getActiveModifiersCompact(map.taData.gameplayParameters.gameplayModifiers);
+ return getActiveModifiersCompact(map.taData.gameplayParameters?.gameplayModifiers?.options || 0);
}
onMount(async() => {
@@ -414,6 +420,18 @@
{:else}
No modifiers active
{/each}
+ {#if map.taData.gameplayParameters?.disableFail}
+
Disable Fail
+ {/if}
+ {#if map.taData.gameplayParameters?.disableCustomNotesOnStream}
+
Disable Custom Notes On Stream
+ {/if}
+ {#if map.taData.gameplayParameters?.disablePause}
+
Disable Pause
+ {/if}
+ {#if map.taData.gameplayParameters?.disableScoresaberSubmission}
+
Disable ScoreSaber Submission
+ {/if}
diff --git a/src/routes/tournaments/[tournamentguid]/matches/[matchGuid]/+page.svelte b/src/routes/tournaments/[tournamentguid]/matches/[matchGuid]/+page.svelte
index e7bcf7b..3b54938 100644
--- a/src/routes/tournaments/[tournamentguid]/matches/[matchGuid]/+page.svelte
+++ b/src/routes/tournaments/[tournamentguid]/matches/[matchGuid]/+page.svelte
@@ -258,7 +258,6 @@
}
}
- // API functions - implement these based on your TA client
async function kickPlayerFromMatch(playerGuid: string) {
const response = await client.removeUserFromMatch(tournamentGuid, matchGuid, playerGuid);
console.log('Kicking player:', playerGuid);
@@ -280,6 +279,13 @@
console.log('Sending players back to menu');
}
+ async function handleUserDisconnected(params: [User, Tournament]) {
+ if(matchPlayers.some(user => user.guid == params[0].guid)) {
+ console.log("User disconnected from match. User: ", params[0])
+ matchPlayers = matchPlayers.filter(x => x.guid !== params[0].guid);
+ }
+ }
+
onMount(async() => {
if ($authTokenStore) {
// Fetch the match data that we need to display
@@ -287,6 +293,7 @@
// Using stateManger listen to global changes to our match
client.stateManager.on('matchDeleted', handleMatchDeleted);
client.stateManager.on('matchUpdated', handleMatchUpdated);
+ client.stateManager.on('userDisconnected', handleUserDisconnected)
} else {
window.location.href = "/discordAuth"
}
@@ -296,6 +303,7 @@
// Remove the listeners that were added on mount
client.stateManager.removeListener('matchUpdated', handleMatchUpdated);
client.stateManager.removeListener('matchDeleted', handleMatchDeleted);
+ client.stateManager.removeListener('userDisconnected', handleUserDisconnected);
});
diff --git a/src/routes/tournaments/[tournamentguid]/qualifiers/+page.svelte b/src/routes/tournaments/[tournamentguid]/qualifiers/+page.svelte
new file mode 100644
index 0000000..008eac4
--- /dev/null
+++ b/src/routes/tournaments/[tournamentguid]/qualifiers/+page.svelte
@@ -0,0 +1,1100 @@
+
+
+