Submit Score
The submitscore method posts a player's score to a leaderboard. Scores are automatically ranked based on the leaderboard's configuration, and players can optionally attach metadata to their submissions.
Overview
Submit scores when players complete levels, achieve milestones, or finish matches. The Moitribe backend automatically handles ranking, timespan categorization, and deduplication based on your leaderboard settings.
Players must be authenticated to submit scores. Check authentication status before attempting to submit.
Basic Usage
JavaScript Example
MoitribeSDK('my-game-id', 'submitscore', {
leaderboardid: 'high-scores',
score: 1500,
callback: (result) => {
if (result.success) {
console.log('Score submitted successfully!');
} else {
console.error('Failed to submit score:', result.msg);
}
}
});
TypeScript Example
import MoitribeSDK from '@veniso/moitribe-js';
interface SubmitScoreParams {
leaderboardid: string;
score: number;
scoretag?: string;
callback: (result: { success: boolean; msg?: string }) => void;
}
const params: SubmitScoreParams = {
leaderboardid: 'high-scores',
score: 1500,
callback: (result) => {
if (result.success) {
console.log('Score submitted successfully!');
} else {
console.error('Failed:', result.msg);
}
}
};
MoitribeSDK('my-game-id', 'submitscore', params);
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
leaderboardid | string | Yes | The leaderboard ID to submit to |
score | number | Yes | The score value to submit |
scoretag | string | No | Optional metadata (JSON string) |
callback | function | Yes | Called with the result |
Leaderboard ID
The leaderboardid must match an ID from your game's leaderboard metadata:
// Get valid IDs from metadata
MoitribeSDK('my-game-id', 'loadleaderboardmetadata', {
onlyData: true,
callback: (result) => {
if (result.success) {
result.leaderboards.forEach((lb) => {
console.log('Valid ID:', lb.leaderboardID);
});
}
}
});
Score Value
The score parameter must be a number:
- For high-score leaderboards: Higher values rank better
- For time-trial leaderboards: Lower values rank better
- Use integers for whole numbers:
1500,10000 - Use floats for decimals:
99.99,123.45
Convert time values to consistent units before submitting. For example, convert minutes to milliseconds or seconds for consistency.
Score Tag (Optional)
The scoretag parameter allows you to attach metadata to a score:
const metadata = {
level: 5,
difficulty: 'hard',
playTime: 120,
powerupsUsed: 3
};
MoitribeSDK('my-game-id', 'submitscore', {
leaderboardid: 'high-scores',
score: 1500,
scoretag: JSON.stringify(metadata),
callback: (result) => {
console.log('Score with metadata submitted');
}
});
Response Format
The callback receives a result object:
{
success: boolean;
msg?: string; // Error message if success is false
}
Success Response
{
success: true
}
Error Response
{
success: false,
msg: 'Player not authenticated'
}
Common Use Cases
Submit After Level Completion
Post score when player finishes a level:
function onLevelComplete(level, score) {
// Check if player is authenticated
MoitribeSDK('my-game-id', 'isAuthenticated', {}, (authResult) => {
if (authResult.success) {
// Submit score with level info
MoitribeSDK('my-game-id', 'submitscore', {
leaderboardid: 'high-scores',
score: score,
scoretag: JSON.stringify({ level: level }),
callback: (result) => {
if (result.success) {
showSuccessMessage('Score submitted to leaderboard!');
} else {
console.error('Score submission failed:', result.msg);
}
}
});
} else {
// Prompt to log in
showLoginPrompt('Log in to save your score to the leaderboard!');
}
});
}
// Usage
onLevelComplete(5, 1500);
Submit Multiple Leaderboards
Post the same score to different leaderboards:
function submitToMultipleLeaderboards(score, metadata) {
const leaderboards = ['high-scores', 'daily-challenge', 'weekly-contest'];
leaderboards.forEach((leaderboardId) => {
MoitribeSDK('my-game-id', 'submitscore', {
leaderboardid: leaderboardId,
score: score,
scoretag: JSON.stringify(metadata),
callback: (result) => {
if (result.success) {
console.log(`Submitted to ${leaderboardId}`);
}
}
});
});
}
// Submit to all leaderboards
submitToMultipleLeaderboards(1500, { level: 5, difficulty: 'hard' });
Submit Time-Based Score
Convert time to appropriate format:
function submitTimeScore(minutes, seconds, milliseconds) {
// Convert to total milliseconds
const totalMs = (minutes * 60000) + (seconds * 1000) + milliseconds;
MoitribeSDK('my-game-id', 'submitscore', {
leaderboardid: 'speed-run',
score: totalMs,
scoretag: JSON.stringify({
displayTime: `${minutes}:${seconds}.${milliseconds}`,
timestamp: Date.now()
}),
callback: (result) => {
if (result.success) {
console.log('Time submitted successfully!');
}
}
});
}
// Submit a time of 2:34.567
submitTimeScore(2, 34, 567);
Submit with Rich Metadata
Include detailed game context:
function submitScoreWithContext(score, gameData) {
const metadata = {
score: score,
level: gameData.level,
difficulty: gameData.difficulty,
playTime: gameData.playTime,
powerupsUsed: gameData.powerupsUsed,
deaths: gameData.deaths,
accuracy: gameData.accuracy,
timestamp: Date.now(),
version: '1.2.0'
};
MoitribeSDK('my-game-id', 'submitscore', {
leaderboardid: 'high-scores',
score: score,
scoretag: JSON.stringify(metadata),
callback: (result) => {
if (result.success) {
console.log('Score with context submitted');
}
}
});
}
// Usage
submitScoreWithContext(1500, {
level: 5,
difficulty: 'hard',
playTime: 180,
powerupsUsed: 3,
deaths: 2,
accuracy: 0.85
});
Retry Failed Submissions
Implement retry logic for failed submissions:
function submitScoreWithRetry(leaderboardId, score, maxRetries = 3) {
let attempts = 0;
function attemptSubmit() {
attempts++;
MoitribeSDK('my-game-id', 'submitscore', {
leaderboardid: leaderboardId,
score: score,
callback: (result) => {
if (result.success) {
console.log('Score submitted successfully!');
} else {
if (attempts < maxRetries) {
console.log(`Retry ${attempts}/${maxRetries}...`);
setTimeout(attemptSubmit, 1000 * attempts); // Exponential backoff
} else {
console.error('Failed to submit score after', maxRetries, 'attempts');
showError('Unable to submit score. Please try again later.');
}
}
}
});
}
attemptSubmit();
}
// Usage
submitScoreWithRetry('high-scores', 1500, 3);
Queue Offline Submissions
Store scores when offline, submit when back online:
const scoreQueue = [];
function submitOrQueue(leaderboardId, score, scoretag) {
if (navigator.onLine) {
submitScore(leaderboardId, score, scoretag);
} else {
// Queue for later
scoreQueue.push({ leaderboardId, score, scoretag });
localStorage.setItem('scoreQueue', JSON.stringify(scoreQueue));
showMessage('Score saved. Will submit when online.');
}
}
function submitScore(leaderboardId, score, scoretag) {
MoitribeSDK('my-game-id', 'submitscore', {
leaderboardid: leaderboardId,
score: score,
scoretag: scoretag,
callback: (result) => {
if (result.success) {
console.log('Score submitted');
}
}
});
}
// Process queue when back online
window.addEventListener('online', () => {
const queue = JSON.parse(localStorage.getItem('scoreQueue') || '[]');
queue.forEach((item) => {
submitScore(item.leaderboardId, item.score, item.scoretag);
});
localStorage.removeItem('scoreQueue');
});
TypeScript Submit Manager
Create a type-safe score submission manager:
interface ScoreMetadata {
level?: number;
difficulty?: string;
playTime?: number;
[key: string]: any;
}
class ScoreSubmitter {
private gameId: string;
constructor(gameId: string) {
this.gameId = gameId;
}
async submit(
leaderboardId: string,
score: number,
metadata?: ScoreMetadata
): Promise<boolean> {
return new Promise((resolve, reject) => {
MoitribeSDK(this.gameId, 'submitscore', {
leaderboardid: leaderboardId,
score: score,
scoretag: metadata ? JSON.stringify(metadata) : undefined,
callback: (result: { success: boolean; msg?: string }) => {
if (result.success) {
resolve(true);
} else {
reject(new Error(result.msg || 'Failed to submit score'));
}
}
});
});
}
async submitIfAuthenticated(
leaderboardId: string,
score: number,
metadata?: ScoreMetadata
): Promise<boolean> {
const isAuth = await this.checkAuthentication();
if (!isAuth) {
throw new Error('Player not authenticated');
}
return this.submit(leaderboardId, score, metadata);
}
private async checkAuthentication(): Promise<boolean> {
return new Promise((resolve) => {
MoitribeSDK(this.gameId, 'isAuthenticated', {}, (result: { success: boolean }) => {
resolve(result.success);
});
});
}
}
// Usage
const submitter = new ScoreSubmitter('my-game-id');
try {
await submitter.submitIfAuthenticated('high-scores', 1500, {
level: 5,
difficulty: 'hard'
});
console.log('Score submitted successfully!');
} catch (error) {
console.error('Submission failed:', error.message);
}
Score Validation
Client-Side Validation
Validate scores before submission:
function validateScore(score) {
if (typeof score !== 'number') {
return { valid: false, error: 'Score must be a number' };
}
if (!isFinite(score)) {
return { valid: false, error: 'Score must be finite' };
}
if (score < 0) {
return { valid: false, error: 'Score cannot be negative' };
}
if (score > Number.MAX_SAFE_INTEGER) {
return { valid: false, error: 'Score is too large' };
}
return { valid: true };
}
function submitValidatedScore(leaderboardId, score) {
const validation = validateScore(score);
if (!validation.valid) {
console.error('Invalid score:', validation.error);
showError(validation.error);
return;
}
MoitribeSDK('my-game-id', 'submitscore', {
leaderboardid: leaderboardId,
score: score,
callback: (result) => {
if (result.success) {
console.log('Valid score submitted');
}
}
});
}
Prevent Duplicate Submissions
Avoid submitting the same score multiple times:
let lastSubmittedScore = null;
let lastSubmitTime = 0;
function submitScoreOnce(leaderboardId, score) {
const now = Date.now();
const cooldown = 5000; // 5 seconds
if (score === lastSubmittedScore && (now - lastSubmitTime) < cooldown) {
console.log('Score already submitted recently');
return;
}
MoitribeSDK('my-game-id', 'submitscore', {
leaderboardid: leaderboardId,
score: score,
callback: (result) => {
if (result.success) {
lastSubmittedScore = score;
lastSubmitTime = now;
console.log('Score submitted');
}
}
});
}
Error Handling
Handle common submission errors:
MoitribeSDK('my-game-id', 'submitscore', {
leaderboardid: 'high-scores',
score: 1500,
callback: (result) => {
if (result.success) {
showSuccessMessage('Score submitted to leaderboard!');
playSuccessSound();
} else {
console.error('Submission failed:', result.msg);
// Handle specific errors
if (result.msg.includes('authenticated') || result.msg.includes('login')) {
showLoginPrompt('Please log in to submit scores');
} else if (result.msg.includes('leaderboard')) {
showError('Leaderboard not found. Please contact support.');
} else if (result.msg.includes('network') || result.msg.includes('connection')) {
showError('Network error. Please check your connection.');
queueScoreForLater('high-scores', 1500);
} else {
showError('Failed to submit score. Please try again.');
}
}
}
});
Best Practices
1. Check Authentication First
Always verify authentication before submitting:
function submitScoreSafely(leaderboardId, score) {
MoitribeSDK('my-game-id', 'isAuthenticated', {}, (authResult) => {
if (authResult.success) {
// Proceed with submission
MoitribeSDK('my-game-id', 'submitscore', {
leaderboardid: leaderboardId,
score: score,
callback: (result) => {
console.log('Submitted:', result.success);
}
});
} else {
showLoginPrompt('Log in to save your score!');
}
});
}
2. Provide User Feedback
Give clear feedback about submission status:
function submitWithFeedback(leaderboardId, score) {
// Show loading state
showLoading('Submitting score...');
MoitribeSDK('my-game-id', 'submitscore', {
leaderboardid: leaderboardId,
score: score,
callback: (result) => {
hideLoading();
if (result.success) {
showSuccess('Score submitted successfully!');
setTimeout(() => {
showLeaderboard(leaderboardId);
}, 2000);
} else {
showError('Failed to submit score');
}
}
});
}
3. Include Relevant Metadata
Add context to help with analytics and debugging:
function submitWithMetadata(score, gameState) {
const metadata = {
level: gameState.level,
difficulty: gameState.difficulty,
timestamp: Date.now(),
gameVersion: '1.2.0',
platform: navigator.platform
};
MoitribeSDK('my-game-id', 'submitscore', {
leaderboardid: 'high-scores',
score: score,
scoretag: JSON.stringify(metadata),
callback: (result) => {
console.log('Score with metadata submitted');
}
});
}
4. Handle Edge Cases
Account for special scenarios:
function submitScoreRobust(leaderboardId, score) {
// Validate score
if (!score || score < 0 || !isFinite(score)) {
console.error('Invalid score value:', score);
return;
}
// Round to reasonable precision
const roundedScore = Math.round(score * 100) / 100;
MoitribeSDK('my-game-id', 'submitscore', {
leaderboardid: leaderboardId,
score: roundedScore,
callback: (result) => {
if (result.success) {
console.log('Score submitted:', roundedScore);
}
}
});
}
Next Steps
- Get Top Scores - View leaderboard rankings
- Score Collections - Social vs Global explained
- Timespans - Daily, Weekly, All-time rankings
- Best Practices - Leaderboard tips and patterns
Related topics:
- Authentication Overview - Required for submissions
- Get Metadata - Get valid leaderboard IDs
- Leaderboards Overview - Complete guide