Skip to main content

Join Tournament

The groupTournamentJoin method registers a player to participate in a specific tournament. Players must join a tournament before they can submit scores or appear in rankings. This method ensures players are officially registered and can track their participation.

Overview

Use this method to:

  • Register players for tournament participation
  • Ensure players are eligible to submit scores
  • Track tournament participant counts
  • Handle tournament capacity limits
  • Validate player requirements before joining

Method Signature

MoitribeSDK(gameId, 'groupTournamentJoin', {
tournamentid: string,
callback: (result) => {
// Handle response
}
});

Parameters

ParameterTypeRequiredDescription
tournamentidstringYesUnique identifier of the tournament to join
callbackfunctionYesFunction to handle the response

Response Format

{
success: boolean;
message?: string; // Success message or additional info
participantId?: string; // Player's participant ID for this tournament
joinedAt?: number; // Timestamp when player joined
msg?: string; // Error message if success is false
}

JavaScript Example

// Join a tournament
MoitribeSDK('my-game-id', 'groupTournamentJoin', {
tournamentid: 'weekly-challenge-001',
callback: (result) => {
if (result.success) {
console.log('Successfully joined tournament!');
console.log('Participant ID:', result.participantId);
console.log('Joined at:', new Date(result.joinedAt));

// Enable score submission
enableScoreSubmission('weekly-challenge-001');

// Show tournament UI
showTournamentInterface();

} else {
console.error('Failed to join tournament:', result.msg);

// Handle specific errors
if (result.msg.includes('already joined')) {
showMessage('You are already participating in this tournament');
} else if (result.msg.includes('started')) {
showMessage('Cannot join tournament after it has started');
} else if (result.msg.includes('ended')) {
showMessage('This tournament has already ended');
} else if (result.msg.includes('full')) {
showMessage('Tournament is full');
} else {
showMessage('Failed to join tournament. Please try again.');
}
}
}
});

TypeScript Example

import MoitribeSDK from '@veniso/moitribe-js';

interface JoinTournamentResponse {
success: boolean;
message?: string;
participantId?: string;
joinedAt?: number;
msg?: string;
}

// Join tournament with proper typing
MoitribeSDK('my-game-id', 'groupTournamentJoin', {
tournamentid: 'weekly-challenge-001',
callback: (result: JoinTournamentResponse) => {
if (result.success) {
handleSuccessfulJoin(result);
} else {
handleJoinError(result.msg);
}
}
});

function handleSuccessfulJoin(result: JoinTournamentResponse) {
// Store participant info
const participantInfo = {
tournamentId: 'weekly-challenge-001',
participantId: result.participantId,
joinedAt: result.joinedAt,
canSubmitScores: true
};

// Save to local storage for persistence
localStorage.setItem('tournament-participant', JSON.stringify(participantInfo));

// Update UI
updateTournamentUI(participantInfo);

// Show success message
showNotification('Successfully joined tournament!', 'success');

// Start tracking tournament progress
startTournamentTracking('weekly-challenge-001');
}

function handleJoinError(errorMsg?: string) {
let userMessage = 'Failed to join tournament';

if (errorMsg) {
if (errorMsg.includes('authenticated')) {
userMessage = 'Please log in to join tournaments';
showLoginPrompt();
} else if (errorMsg.includes('requirements')) {
userMessage = 'You do not meet the requirements for this tournament';
} else if (errorMsg.includes('already joined')) {
userMessage = 'You are already participating in this tournament';
// Enable score submission anyway
enableScoreSubmission('weekly-challenge-001');
} else if (errorMsg.includes('full')) {
userMessage = 'This tournament has reached maximum participants';
} else if (errorMsg.includes('started')) {
userMessage = 'Registration has closed for this tournament';
} else if (errorMsg.includes('ended')) {
userMessage = 'This tournament has already ended';
} else {
userMessage = errorMsg;
}
}

showNotification(userMessage, 'error');
}

function updateTournamentUI(participantInfo: any) {
// Update tournament status display
const statusElement = document.getElementById('tournament-status');
statusElement.innerHTML = `
<div class="participant-info">
<span class="status-badge">PARTICIPATING</span>
<span class="participant-id">ID: ${participantInfo.participantId}</span>
<span class="joined-time">Joined: ${new Date(participantInfo.joinedAt).toLocaleString()}</span>
</div>
`;

// Show score submission interface
const scoreSection = document.getElementById('score-section');
scoreSection.style.display = 'block';

// Enable tournament features
enableTournamentFeatures();
}

function enableTournamentFeatures() {
// Enable score submission button
const submitBtn = document.getElementById('submit-score-btn');
submitBtn.disabled = false;
submitBtn.textContent = 'Submit Score';

// Show current rankings
loadTournamentRankings();

// Start score tracking
initializeScoreTracking();
}

Common Use Cases

Tournament Join Flow

Create a complete tournament joining experience:

function joinTournamentFlow(tournamentId) {
// Step 1: Check authentication
checkAuthentication()
.then(isAuthenticated => {
if (!isAuthenticated) {
throw new Error('Authentication required');
}

// Step 2: Get tournament details
return getTournamentDetails(tournamentId);
})
.then(tournament => {
// Step 3: Validate tournament can be joined
if (!canJoinTournament(tournament)) {
throw new Error(tournament.joinError);
}

// Step 4: Show confirmation dialog
return showJoinConfirmation(tournament);
})
.then(confirmed => {
if (confirmed) {
// Step 5: Join tournament
return performTournamentJoin(tournamentId);
} else {
throw new Error('User cancelled');
}
})
.then(result => {
// Step 6: Handle successful join
handleSuccessfulJoin(result, tournamentId);
})
.catch(error => {
handleJoinError(error.message);
});
}

function canJoinTournament(tournament) {
const now = Date.now();

if (tournament.status !== 'upcoming' && tournament.status !== 'active') {
tournament.joinError = 'Tournament is not accepting participants';
return false;
}

if (tournament.status === 'active' && tournament.joinDeadline) {
if (now > tournament.joinDeadline) {
tournament.joinError = 'Registration deadline has passed';
return false;
}
}

if (tournament.maxParticipants &&
tournament.currentParticipants >= tournament.maxParticipants) {
tournament.joinError = 'Tournament is full';
return false;
}

if (tournament.requirements) {
// Check requirements (would need additional implementation)
if (!meetsRequirements(tournament.requirements)) {
tournament.joinError = 'You do not meet the requirements';
return false;
}
}

return true;
}

function showJoinConfirmation(tournament) {
return new Promise((resolve) => {
const modal = document.createElement('div');
modal.className = 'join-confirmation-modal';
modal.innerHTML = `
<div class="modal-content">
<h2>Join Tournament: ${tournament.name}</h2>
<div class="tournament-summary">
<p><strong>Description:</strong> ${tournament.description}</p>
<p><strong>Type:</strong> ${tournament.type}</p>
<p><strong>Scoring:</strong> ${tournament.scoreType}</p>
<p><strong>Participants:</strong> ${tournament.currentParticipants}/${tournament.maxParticipants || '∞'}</p>
${tournament.prizes ? '<p><strong>Prizes:</strong> Available for top performers</p>' : ''}
</div>
<div class="confirmation-actions">
<button class="btn-cancel" onclick="closeJoinModal(false)">Cancel</button>
<button class="btn-confirm" onclick="closeJoinModal(true)">Join Tournament</button>
</div>
</div>
`;

document.body.appendChild(modal);

window.closeJoinModal = (confirmed) => {
document.body.removeChild(modal);
resolve(confirmed);
};
});
}

function performTournamentJoin(tournamentId) {
return new Promise((resolve, reject) => {
MoitribeSDK('my-game-id', 'groupTournamentJoin', {
tournamentid: tournamentId,
callback: (result) => {
if (result.success) {
resolve(result);
} else {
reject(new Error(result.msg || 'Failed to join tournament'));
}
}
});
});
}

Quick Join Button

Simple tournament join functionality:

function quickJoinTournament(tournamentId) {
// Show loading state
const button = document.getElementById(`join-btn-${tournamentId}`);
const originalText = button.textContent;
button.disabled = true;
button.textContent = 'Joining...';

MoitribeSDK('my-game-id', 'groupTournamentJoin', {
tournamentid: tournamentId,
callback: (result) => {
if (result.success) {
// Update button to show joined status
button.textContent = 'Joined ✓';
button.className = 'btn-joined';
button.disabled = true;

// Show success notification
showNotification('Successfully joined tournament!', 'success');

// Enable score submission
enableScoreSubmission(tournamentId);

// Update tournament list
updateTournamentList();

} else {
// Restore button and show error
button.disabled = false;
button.textContent = originalText;

showNotification(result.msg || 'Failed to join tournament', 'error');
}
}
});
}

Batch Tournament Joining

Join multiple tournaments at once:

function joinMultipleTournaments(tournamentIds) {
const results = [];
let completed = 0;

function updateProgress() {
completed++;
const progress = (completed / tournamentIds.length) * 100;

// Update progress bar
const progressBar = document.getElementById('join-progress');
progressBar.style.width = `${progress}%`;
progressBar.textContent = `${Math.round(progress)}%`;

if (completed === tournamentIds.length) {
// All tournaments processed
showBatchJoinResults(results);
}
}

tournamentIds.forEach(tournamentId => {
MoitribeSDK('my-game-id', 'groupTournamentJoin', {
tournamentid: tournamentId,
callback: (result) => {
results.push({
tournamentId: tournamentId,
success: result.success,
message: result.success ? result.message : result.msg
});

updateProgress();
}
});
});
}

function showBatchJoinResults(results) {
const successful = results.filter(r => r.success);
const failed = results.filter(r => !r.success);

let message = `Joined ${successful.length} of ${results.length} tournaments`;

if (failed.length > 0) {
message += '\n\nFailed to join:';
failed.forEach(failure => {
message += `\n• ${failure.tournamentId}: ${failure.message}`;
});
}

showNotification(message, successful.length === results.length ? 'success' : 'warning');

// Update UI for successful joins
successful.forEach(success => {
updateTournamentButton(success.tournamentId, 'joined');
});
}

Tournament Join Validation

Validate tournament join conditions:

function validateTournamentJoin(tournamentId) {
return new Promise((resolve, reject) => {
// Check authentication first
MoitribeSDK('my-game-id', 'isAuthenticated', {}, (authResult) => {
if (!authResult.success) {
reject(new Error('Authentication required to join tournaments'));
return;
}

// Get tournament details
MoitribeSDK('my-game-id', 'groupTournamentData', {
tournamentid: tournamentId,
callback: (result) => {
if (!result.success) {
reject(new Error('Tournament not found'));
return;
}

const tournament = result.tournament;
const validation = validateJoinConditions(tournament);

if (validation.valid) {
resolve(tournament);
} else {
reject(new Error(validation.reason));
}
}
});
});
});
}

function validateJoinConditions(tournament) {
const now = Date.now();

// Check tournament status
if (tournament.status === 'ended' || tournament.status === 'results') {
return { valid: false, reason: 'Tournament has ended' };
}

// Check if registration is still open
if (tournament.joinDeadline && now > tournament.joinDeadline) {
return { valid: false, reason: 'Registration deadline has passed' };
}

// Check capacity
if (tournament.maxParticipants &&
tournament.currentParticipants >= tournament.maxParticipants) {
return { valid: false, reason: 'Tournament is full' };
}

// Check if already joined (would need to check local storage or API)
if (isAlreadyJoined(tournament.id)) {
return { valid: false, reason: 'Already joined this tournament' };
}

return { valid: true };
}

function isAlreadyJoined(tournamentId) {
const participant = localStorage.getItem('tournament-participant');
if (participant) {
const data = JSON.parse(participant);
return data.tournamentId === tournamentId;
}
return false;
}

Error Handling

Handle common tournament joining errors:

function handleJoinError(error) {
console.error('Tournament join error:', error);

let userMessage = 'Failed to join tournament';
let action = null;

if (error.includes('authenticated')) {
userMessage = 'Please log in to join tournaments';
action = () => showLoginModal();
} else if (error.includes('requirements')) {
userMessage = 'You do not meet the requirements for this tournament';
action = () => showRequirementsModal();
} else if (error.includes('already joined')) {
userMessage = 'You are already participating in this tournament';
// Enable score submission since they're already joined
enableScoreSubmission(tournamentId);
} else if (error.includes('full')) {
userMessage = 'This tournament has reached maximum participants';
action = () => showSimilarTournaments();
} else if (error.includes('started') || error.includes('deadline')) {
userMessage = 'Registration has closed for this tournament';
action = () => showUpcomingTournaments();
} else if (error.includes('ended')) {
userMessage = 'This tournament has already ended';
action = () => showTournamentResults();
} else if (error.includes('network')) {
userMessage = 'Network error. Please check your connection and try again.';
action = () => retryJoinTournament();
}

showNotification(userMessage, 'error', action);
}

function retryJoinTournament() {
const tournamentId = getCurrentTournamentId();
if (tournamentId) {
setTimeout(() => {
joinTournamentFlow(tournamentId);
}, 2000);
}
}

Best Practices

Join State Management

Track tournament join state across sessions:

class TournamentJoinManager {
constructor() {
this.joinedTournaments = this.loadJoinedTournaments();
}

loadJoinedTournaments() {
const stored = localStorage.getItem('joined-tournaments');
return stored ? JSON.parse(stored) : {};
}

saveJoinedTournaments() {
localStorage.setItem('joined-tournaments', JSON.stringify(this.joinedTournaments));
}

async joinTournament(tournamentId) {
try {
const result = await this.performJoin(tournamentId);

if (result.success) {
this.joinedTournaments[tournamentId] = {
participantId: result.participantId,
joinedAt: result.joinedAt,
canSubmitScores: true
};
this.saveJoinedTournaments();
}

return result;
} catch (error) {
throw error;
}
}

isJoined(tournamentId) {
return !!this.joinedTournaments[tournamentId];
}

getParticipantInfo(tournamentId) {
return this.joinedTournaments[tournamentId];
}

performJoin(tournamentId) {
return new Promise((resolve, reject) => {
MoitribeSDK('my-game-id', 'groupTournamentJoin', {
tournamentid: tournamentId,
callback: (result) => {
if (result.success) {
resolve(result);
} else {
reject(new Error(result.msg));
}
}
});
});
}
}

const tournamentJoinManager = new TournamentJoinManager();

Join Confirmation Best Practices

Always show clear confirmation before joining:

function showJoinConfirmationWithDetails(tournament) {
const modal = createModal();

const content = `
<div class="tournament-join-confirmation">
<h2>Join ${tournament.name}</h2>

<div class="tournament-details">
<div class="detail-row">
<span class="label">Type:</span>
<span class="value">${tournament.type}</span>
</div>
<div class="detail-row">
<span class="label">Scoring:</span>
<span class="value">${formatScoreType(tournament.scoreType)}</span>
</div>
<div class="detail-row">
<span class="label">Duration:</span>
<span class="value">${calculateDuration(tournament.startTime, tournament.endTime)}</span>
</div>
<div class="detail-row">
<span class="label">Participants:</span>
<span class="value">${tournament.currentParticipants}/${tournament.maxParticipants || '∞'}</span>
</div>
</div>

${tournament.prizes ? `
<div class="prize-preview">
<h3>Prizes Available</h3>
<div class="prize-list">
${tournament.prizes.slice(0, 3).map(prize => `
<div class="prize-item">
<span class="rank">Rank ${prize.rank}</span>
<span class="prize">${prize.description}</span>
</div>
`).join('')}
${tournament.prizes.length > 3 ? `<div class="more-prizes">+${tournament.prizes.length - 3} more prizes</div>` : ''}
</div>
</div>
` : ''}

<div class="confirmation-checkbox">
<label>
<input type="checkbox" id="agree-rules">
I agree to the tournament rules and conditions
</label>
</div>

<div class="confirmation-actions">
<button class="btn-secondary" onclick="closeModal()">Cancel</button>
<button class="btn-primary" id="confirm-join" disabled>Join Tournament</button>
</div>
</div>
`;

modal.setContent(content);
modal.show();

// Enable confirm button when checkbox is checked
const checkbox = document.getElementById('agree-rules');
const confirmBtn = document.getElementById('confirm-join');

checkbox.addEventListener('change', () => {
confirmBtn.disabled = !checkbox.checked;
});

confirmBtn.addEventListener('click', () => {
modal.close();
performTournamentJoin(tournament.id);
});
}

Next Steps

After joining a tournament:

Related topics: