@series-inc/venus-sdk 3.0.4 → 3.1.0-beta.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +324 -123
- package/dist/{AdsApi-CNGRf6j0.d.mts → AdsApi-meVfUcZy.d.mts} +37 -25
- package/dist/{AdsApi-CNGRf6j0.d.ts → AdsApi-meVfUcZy.d.ts} +37 -25
- package/dist/chunk-2PDL7CQK.mjs +26 -0
- package/dist/chunk-2PDL7CQK.mjs.map +1 -0
- package/dist/{chunk-PXWCNWJ6.mjs → chunk-EMVTVSGL.mjs} +421 -109
- package/dist/chunk-EMVTVSGL.mjs.map +1 -0
- package/dist/chunk-IZLOB7DV.mjs +343 -0
- package/dist/chunk-IZLOB7DV.mjs.map +1 -0
- package/dist/{chunk-W7IPHM67.mjs → chunk-QABXMFND.mjs} +3 -26
- package/dist/chunk-QABXMFND.mjs.map +1 -0
- package/dist/core-5JLON75E.mjs +4 -0
- package/dist/{core-R3FHW62G.mjs.map → core-5JLON75E.mjs.map} +1 -1
- package/dist/index.cjs +768 -105
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.mts +140 -25
- package/dist/index.d.ts +140 -25
- package/dist/index.mjs +5 -3
- package/dist/index.mjs.map +1 -1
- package/dist/venus-api/index.cjs +452 -101
- package/dist/venus-api/index.cjs.map +1 -1
- package/dist/venus-api/index.d.mts +2 -2
- package/dist/venus-api/index.d.ts +2 -2
- package/dist/venus-api/index.mjs +46 -3
- package/dist/venus-api/index.mjs.map +1 -1
- package/dist/vite/index.cjs +534 -0
- package/dist/vite/index.cjs.map +1 -0
- package/dist/vite/index.mjs +527 -0
- package/dist/vite/index.mjs.map +1 -0
- package/dist/webview/index.cjs +346 -0
- package/dist/webview/index.cjs.map +1 -0
- package/dist/webview/index.d.mts +17 -0
- package/dist/webview/index.d.ts +17 -0
- package/dist/webview/index.mjs +4 -0
- package/dist/webview/index.mjs.map +1 -0
- package/package.json +19 -1
- package/dist/chunk-PXWCNWJ6.mjs.map +0 -1
- package/dist/chunk-W7IPHM67.mjs.map +0 -1
- package/dist/core-R3FHW62G.mjs +0 -3
package/README.md
CHANGED
|
@@ -500,6 +500,95 @@ const shareResult = await VenusAPI.post.sharePostAsync({
|
|
|
500
500
|
|
|
501
501
|
---
|
|
502
502
|
|
|
503
|
+
### Social API (BETA)
|
|
504
|
+
|
|
505
|
+
Share links and QR codes for challenges and UGC (user-generated content):
|
|
506
|
+
|
|
507
|
+
```typescript
|
|
508
|
+
// Share a high score challenge - automatically opens share dialog!
|
|
509
|
+
const { shareUrl } = await VenusAPI.social.shareLinkAsync({
|
|
510
|
+
launchParams: {
|
|
511
|
+
challengeType: 'highscore',
|
|
512
|
+
scoreTobeat: '1500',
|
|
513
|
+
challengerId: VenusAPI.profile.getCurrentProfile().id
|
|
514
|
+
},
|
|
515
|
+
metadata: {
|
|
516
|
+
title: 'Beat my score!',
|
|
517
|
+
description: 'Can you beat 1500 points in Word Search?',
|
|
518
|
+
imageUrl: 'https://cdn.example.com/share-highscore.png'
|
|
519
|
+
}
|
|
520
|
+
})
|
|
521
|
+
// This function automatically triggers the share UI:
|
|
522
|
+
// - iOS/Android: Opens native share sheet
|
|
523
|
+
// - Web: Uses navigator.share() if available, otherwise copies to clipboard
|
|
524
|
+
// The returned shareUrl is the generated link that was shared
|
|
525
|
+
|
|
526
|
+
// Generate QR code for in-person challenges (does NOT open share dialog)
|
|
527
|
+
const { shareUrl, qrCode } = await VenusAPI.social.createQRCodeAsync({
|
|
528
|
+
launchParams: {
|
|
529
|
+
challengeType: 'daily',
|
|
530
|
+
dailyPuzzleId: '2024-11-04',
|
|
531
|
+
invitedBy: VenusAPI.profile.getCurrentProfile().username
|
|
532
|
+
},
|
|
533
|
+
metadata: {
|
|
534
|
+
title: 'Daily Challenge',
|
|
535
|
+
description: 'Scan to play today\'s puzzle with me!'
|
|
536
|
+
},
|
|
537
|
+
qrOptions: {
|
|
538
|
+
size: 512, // Size in pixels (default: 256)
|
|
539
|
+
margin: 4, // Margin in modules (default: 2)
|
|
540
|
+
format: 'png' // 'png' or 'svg'
|
|
541
|
+
}
|
|
542
|
+
})
|
|
543
|
+
// This only generates the QR code - you control how to display it
|
|
544
|
+
|
|
545
|
+
// Display the QR code in your UI
|
|
546
|
+
const img = document.createElement('img')
|
|
547
|
+
img.src = qrCode // Data URL (e.g., 'data:image/png;base64,...')
|
|
548
|
+
document.body.appendChild(img)
|
|
549
|
+
|
|
550
|
+
// Handle incoming challenge when app opens from share
|
|
551
|
+
const launchParams = VenusAPI.getLaunchParams()
|
|
552
|
+
if (launchParams.challengeType === 'highscore') {
|
|
553
|
+
const targetScore = parseInt(launchParams.scoreTobeat, 10)
|
|
554
|
+
const challengerId = launchParams.challengerId
|
|
555
|
+
startHighScoreChallenge(targetScore, challengerId)
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
// Handle daily puzzle invites
|
|
559
|
+
if (launchParams.challengeType === 'daily') {
|
|
560
|
+
const puzzleId = launchParams.dailyPuzzleId
|
|
561
|
+
const inviter = launchParams.invitedBy
|
|
562
|
+
loadDailyPuzzle(puzzleId, inviter)
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
// Referral system example
|
|
566
|
+
if (launchParams.referredBy) {
|
|
567
|
+
await rewardReferrer(launchParams.referredBy)
|
|
568
|
+
showMessage(`You were invited by ${launchParams.referredBy}!`)
|
|
569
|
+
}
|
|
570
|
+
```
|
|
571
|
+
|
|
572
|
+
**Launch Parameters:**
|
|
573
|
+
|
|
574
|
+
Launch parameters are completely arbitrary - define whatever your game needs:
|
|
575
|
+
|
|
576
|
+
Examples from the code above:
|
|
577
|
+
- `challengeType`, `scoreTobeat`, `challengerId` - For beat-my-score challenges
|
|
578
|
+
- `dailyPuzzleId`, `invitedBy` - For daily puzzle invites
|
|
579
|
+
- `referredBy` - For referral tracking
|
|
580
|
+
- `levelId`, `replayId`, `customMapId` - For UGC content
|
|
581
|
+
|
|
582
|
+
All parameters are custom game data - no reserved or required fields.
|
|
583
|
+
|
|
584
|
+
**Size Limits:**
|
|
585
|
+
|
|
586
|
+
- **Max total size**: Keep `launchParams` under ~100KB (share data stored in Firestore with 1MB document limit)
|
|
587
|
+
- **Use compact formats**: IDs and short strings work best
|
|
588
|
+
- **Best practice**: Store large UGC separately and only pass an ID in `launchParams`
|
|
589
|
+
|
|
590
|
+
---
|
|
591
|
+
|
|
503
592
|
### Analytics API
|
|
504
593
|
|
|
505
594
|
```typescript
|
|
@@ -575,24 +664,26 @@ await VenusAPI.rooms.sendMessage({
|
|
|
575
664
|
|
|
576
665
|
### Time API
|
|
577
666
|
|
|
578
|
-
Server time synchronization and formatting:
|
|
667
|
+
Server time synchronization and formatting with automatic locale detection:
|
|
579
668
|
|
|
580
669
|
```typescript
|
|
581
670
|
// Get server time
|
|
582
671
|
const serverTimeData = await VenusAPI.time.requestTimeAsync()
|
|
583
672
|
// Returns: { serverTime, localTime, timezoneOffset, formattedTime, locale }
|
|
584
673
|
|
|
585
|
-
// Format time
|
|
674
|
+
// Format time (locale is automatically determined from user settings)
|
|
586
675
|
const formatted = VenusAPI.time.formatTime(Date.now(), {
|
|
587
|
-
|
|
588
|
-
|
|
676
|
+
dateStyle: 'full', // 'full', 'long', 'medium', 'short'
|
|
677
|
+
timeStyle: 'medium',
|
|
678
|
+
hour12: true
|
|
589
679
|
})
|
|
590
680
|
|
|
591
|
-
// Format number
|
|
681
|
+
// Format number (locale is automatically determined from user settings)
|
|
592
682
|
const formattedNumber = VenusAPI.time.formatNumber(1234567.89, {
|
|
593
|
-
locale: 'en-US',
|
|
594
683
|
style: 'currency',
|
|
595
|
-
currency: 'USD'
|
|
684
|
+
currency: 'USD',
|
|
685
|
+
minimumFractionDigits: 2,
|
|
686
|
+
maximumFractionDigits: 2
|
|
596
687
|
})
|
|
597
688
|
|
|
598
689
|
// Get future time
|
|
@@ -692,151 +783,221 @@ VenusAPI.logging.debug('Debug info')
|
|
|
692
783
|
|
|
693
784
|
### Leaderboard API (BETA)
|
|
694
785
|
|
|
695
|
-
Competitive leaderboards with
|
|
786
|
+
Competitive leaderboards with three security levels. Choose based on your game's requirements.
|
|
696
787
|
|
|
697
|
-
|
|
788
|
+
---
|
|
698
789
|
|
|
699
|
-
|
|
700
|
-
// Start a leaderboard session
|
|
701
|
-
const session = await VenusAPI.leaderboard.startRunAsync({
|
|
702
|
-
mode: 'classic' // Optional: 'classic', 'hard', etc. Defaults to 'default'
|
|
703
|
-
})
|
|
704
|
-
// Returns: { sessionId, startTime, expiresAt, hashNonce?, mode }
|
|
790
|
+
#### 🟢 Simple Mode (Default - Casual Games)
|
|
705
791
|
|
|
706
|
-
|
|
792
|
+
Submit scores directly without tokens:
|
|
793
|
+
|
|
794
|
+
```typescript
|
|
795
|
+
// One call - no token needed!
|
|
707
796
|
const result = await VenusAPI.leaderboard.submitScoreAsync({
|
|
708
|
-
sessionId: session.sessionId,
|
|
709
797
|
score: 1500,
|
|
710
|
-
|
|
711
|
-
mode: 'classic',
|
|
712
|
-
metadata: {
|
|
798
|
+
duration: 120,
|
|
799
|
+
mode: 'classic',
|
|
800
|
+
metadata: {
|
|
713
801
|
levelCompleted: 10,
|
|
714
802
|
powerUpsUsed: 3
|
|
715
803
|
},
|
|
716
|
-
telemetry: {
|
|
804
|
+
telemetry: {
|
|
717
805
|
clickCount: 245,
|
|
718
806
|
avgReactionTime: 0.32
|
|
719
|
-
}
|
|
720
|
-
|
|
807
|
+
}
|
|
808
|
+
})
|
|
809
|
+
|
|
810
|
+
console.log(`Your rank: ${result.rank}`)
|
|
811
|
+
```
|
|
812
|
+
|
|
813
|
+
**Configuration:** None needed (uses defaults)
|
|
814
|
+
|
|
815
|
+
**Security provided:**
|
|
816
|
+
- ✅ Score/duration bounds validation
|
|
817
|
+
- ✅ Rate limiting (60 second cooldown per player)
|
|
818
|
+
- ✅ Trust scores & shadow banning for repeat offenders
|
|
819
|
+
- ❌ No session replay protection
|
|
820
|
+
- ❌ No tamper protection
|
|
821
|
+
|
|
822
|
+
**Best for:** Simple integration
|
|
823
|
+
|
|
824
|
+
---
|
|
825
|
+
|
|
826
|
+
#### 🟡 Token Mode (Competitive Games)
|
|
827
|
+
|
|
828
|
+
Add session validation for replay protection:
|
|
829
|
+
|
|
830
|
+
```typescript
|
|
831
|
+
// Step 1: Create score token
|
|
832
|
+
const scoreToken = await VenusAPI.leaderboard.createScoreTokenAsync({
|
|
833
|
+
mode: 'ranked'
|
|
834
|
+
})
|
|
835
|
+
// Returns: { token, startTime, expiresAt, mode }
|
|
836
|
+
|
|
837
|
+
// Step 2: Play game...
|
|
838
|
+
|
|
839
|
+
// Step 3: Submit with token
|
|
840
|
+
const result = await VenusAPI.leaderboard.submitScoreAsync({
|
|
841
|
+
token: scoreToken.token,
|
|
842
|
+
score: 1500,
|
|
843
|
+
duration: 120,
|
|
844
|
+
metadata: { ... }
|
|
845
|
+
})
|
|
846
|
+
```
|
|
847
|
+
|
|
848
|
+
**Configuration:**
|
|
849
|
+
```json
|
|
850
|
+
{
|
|
851
|
+
"leaderboard": {
|
|
852
|
+
"requiresToken": true
|
|
853
|
+
}
|
|
854
|
+
}
|
|
855
|
+
```
|
|
856
|
+
|
|
857
|
+
**Additional security:**
|
|
858
|
+
- ✅ All simple mode security
|
|
859
|
+
- ✅ Session validation (tokens expire in 1 hour)
|
|
860
|
+
- ✅ Replay attack prevention (one-time use)
|
|
861
|
+
- ✅ Mode locking (token locks game mode)
|
|
862
|
+
- ❌ No tamper protection
|
|
863
|
+
|
|
864
|
+
**Best for:** Preventing replay attacks
|
|
865
|
+
|
|
866
|
+
---
|
|
867
|
+
|
|
868
|
+
#### 🔴 Score Sealing Mode (High-Stakes Games)
|
|
869
|
+
|
|
870
|
+
Add cryptographic tamper protection:
|
|
871
|
+
|
|
872
|
+
```typescript
|
|
873
|
+
// Step 1: Create score token (sealing data included automatically if enabled)
|
|
874
|
+
const scoreToken = await VenusAPI.leaderboard.createScoreTokenAsync({
|
|
875
|
+
mode: 'tournament'
|
|
721
876
|
})
|
|
722
|
-
// Returns: {
|
|
723
|
-
|
|
724
|
-
//
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
877
|
+
// Returns: { token, sealingNonce, sealingSecret, ... }
|
|
878
|
+
|
|
879
|
+
// Step 2: Play game...
|
|
880
|
+
|
|
881
|
+
// Step 3: Submit score - SDK auto-computes hash internally!
|
|
882
|
+
const result = await VenusAPI.leaderboard.submitScoreAsync({
|
|
883
|
+
token: scoreToken.token,
|
|
884
|
+
score: 1500,
|
|
885
|
+
duration: 120,
|
|
731
886
|
})
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
887
|
+
```
|
|
888
|
+
|
|
889
|
+
**Configuration:**
|
|
890
|
+
```json
|
|
891
|
+
{
|
|
892
|
+
"leaderboard": {
|
|
893
|
+
"requiresToken": true,
|
|
894
|
+
"enableScoreSealing": true,
|
|
895
|
+
"scoreSealingSecret": "your-secret-key-change-in-production"
|
|
896
|
+
}
|
|
897
|
+
}
|
|
898
|
+
```
|
|
899
|
+
|
|
900
|
+
**Note:** Hash computation is handled internally by the SDK using Web Crypto API (HMAC-SHA256). Games never need to implement cryptographic hashing manually. The hash always includes: `score`, `duration`, and `token`.
|
|
901
|
+
|
|
902
|
+
**Maximum security:**
|
|
903
|
+
- ✅ All token mode security
|
|
904
|
+
- ✅ Tamper-proof scores (HMAC-SHA256 verification)
|
|
905
|
+
- ✅ Client-side cheat detection
|
|
906
|
+
- ✅ Automatic hash computation (no crypto code needed in games)
|
|
907
|
+
|
|
908
|
+
**Best for:** Reduced hacking
|
|
909
|
+
|
|
910
|
+
---
|
|
911
|
+
|
|
912
|
+
#### Query Methods (Same for All Modes)
|
|
913
|
+
|
|
914
|
+
All query methods work identically regardless of security mode:
|
|
915
|
+
|
|
916
|
+
```typescript
|
|
917
|
+
// Get paginated scores
|
|
918
|
+
const pagedScores = await VenusAPI.leaderboard.getPagedScoresAsync({
|
|
743
919
|
mode: 'classic',
|
|
744
920
|
period: 'daily',
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
contextBehind: 2 // Players behind you
|
|
921
|
+
limit: 50,
|
|
922
|
+
cursor: nextCursor
|
|
748
923
|
})
|
|
749
|
-
|
|
750
|
-
//
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
//
|
|
760
|
-
|
|
761
|
-
// totalEntries: 150
|
|
762
|
-
// }
|
|
763
|
-
|
|
764
|
-
// Get player stats
|
|
765
|
-
const stats = await VenusAPI.leaderboard.getPlayerStatsAsync({
|
|
924
|
+
|
|
925
|
+
// Get podium + player context
|
|
926
|
+
const podiumScores = await VenusAPI.leaderboard.getPodiumScoresAsync({
|
|
927
|
+
mode: 'classic',
|
|
928
|
+
period: 'daily',
|
|
929
|
+
topCount: 3,
|
|
930
|
+
contextAhead: 4,
|
|
931
|
+
contextBehind: 2
|
|
932
|
+
})
|
|
933
|
+
|
|
934
|
+
// Get my rank
|
|
935
|
+
const rank = await VenusAPI.leaderboard.getMyRankAsync({
|
|
766
936
|
mode: 'classic',
|
|
767
937
|
period: 'daily'
|
|
768
938
|
})
|
|
769
|
-
// Returns: {
|
|
770
|
-
// rank: 42,
|
|
771
|
-
// score: 1500,
|
|
772
|
-
// totalPlayers: 150,
|
|
773
|
-
// percentile: 0.72, // 72nd percentile
|
|
774
|
-
// trustScore: 85,
|
|
775
|
-
// periodInstance: 'daily_2025-11-04'
|
|
776
|
-
// }
|
|
777
939
|
```
|
|
778
940
|
|
|
779
|
-
|
|
941
|
+
---
|
|
942
|
+
|
|
943
|
+
#### Configuration Reference
|
|
780
944
|
|
|
781
945
|
Add a `leaderboard` object to your game's `config.json` file:
|
|
782
946
|
|
|
783
947
|
```json
|
|
784
948
|
{
|
|
785
949
|
"leaderboard": {
|
|
786
|
-
|
|
787
|
-
"disabledMessage": "Leaderboards are temporarily unavailable.",
|
|
788
|
-
"requiresHash": true,
|
|
789
|
-
"hashSecret": "your-secret-key-change-in-production",
|
|
790
|
-
"scoringFields": ["score", "durationSec", "sessionId"],
|
|
791
|
-
"minDurationSec": 10,
|
|
792
|
-
"maxDurationSec": 600,
|
|
950
|
+
// Basic settings
|
|
793
951
|
"minScore": 0,
|
|
794
952
|
"maxScore": 999999999,
|
|
953
|
+
"minDurationSec": 10,
|
|
954
|
+
"maxDurationSec": 600,
|
|
955
|
+
|
|
956
|
+
// Security (progressive levels)
|
|
957
|
+
"requiresToken": false, // Simple mode (default)
|
|
958
|
+
"enableScoreSealing": false, // Requires requiresToken=true
|
|
959
|
+
"scoreSealingSecret": "secret", // Required if enableScoreSealing=true
|
|
960
|
+
|
|
961
|
+
// Game modes
|
|
795
962
|
"modes": {
|
|
796
|
-
"default": {
|
|
797
|
-
|
|
798
|
-
},
|
|
799
|
-
"hard": {
|
|
800
|
-
"displayName": "Hard Mode",
|
|
801
|
-
"minDurationSec": 5,
|
|
802
|
-
"maxDurationSec": 300
|
|
803
|
-
}
|
|
963
|
+
"default": { "displayName": "Normal" },
|
|
964
|
+
"hard": { "displayName": "Hard Mode" }
|
|
804
965
|
},
|
|
966
|
+
|
|
967
|
+
// Time periods
|
|
805
968
|
"periods": {
|
|
806
|
-
"daily": {
|
|
807
|
-
|
|
808
|
-
"type": "daily"
|
|
809
|
-
},
|
|
810
|
-
"alltime": {
|
|
811
|
-
"displayName": "All Time",
|
|
812
|
-
"type": "alltime"
|
|
813
|
-
}
|
|
969
|
+
"daily": { "displayName": "Daily", "type": "daily" },
|
|
970
|
+
"alltime": { "displayName": "All Time", "type": "alltime" }
|
|
814
971
|
},
|
|
972
|
+
|
|
973
|
+
// Anti-cheat
|
|
815
974
|
"antiCheat": {
|
|
816
|
-
"enableZScoreDetection": false,
|
|
817
|
-
"zScoreThreshold": 3,
|
|
818
975
|
"enableRateLimit": true,
|
|
819
976
|
"minTimeBetweenSubmissionsSec": 60,
|
|
820
977
|
"trustScoreDecayPerFlag": 10,
|
|
821
|
-
"shadowBanThreshold": 20
|
|
978
|
+
"shadowBanThreshold": 20,
|
|
979
|
+
"enableZScoreDetection": false,
|
|
980
|
+
"zScoreThreshold": 3
|
|
822
981
|
},
|
|
982
|
+
|
|
983
|
+
// Display
|
|
823
984
|
"displaySettings": {
|
|
824
985
|
"maxEntriesPerPage": 50
|
|
825
986
|
},
|
|
987
|
+
|
|
988
|
+
// Seed data (optional)
|
|
826
989
|
"seedEntries": {
|
|
827
990
|
"default": {
|
|
828
991
|
"daily": [
|
|
829
992
|
{
|
|
830
993
|
"score": 18500,
|
|
831
994
|
"username": "ProPlayer",
|
|
832
|
-
"
|
|
833
|
-
"durationSec": 180
|
|
995
|
+
"duration": 180
|
|
834
996
|
},
|
|
835
997
|
{
|
|
836
998
|
"score": 15200,
|
|
837
999
|
"username": "Challenger",
|
|
838
|
-
"
|
|
839
|
-
"durationSec": 210
|
|
1000
|
+
"duration": 210
|
|
840
1001
|
}
|
|
841
1002
|
]
|
|
842
1003
|
}
|
|
@@ -845,34 +1006,18 @@ Add a `leaderboard` object to your game's `config.json` file:
|
|
|
845
1006
|
}
|
|
846
1007
|
```
|
|
847
1008
|
|
|
848
|
-
**Configuration Options:**
|
|
849
1009
|
|
|
850
|
-
|
|
851
|
-
- `requiresHash`: Enable HMAC-based score verification (recommended for production)
|
|
852
|
-
- `hashSecret`: Secret key for HMAC verification (keep this secure!)
|
|
853
|
-
- `scoringFields`: Fields included in hash verification (can include nested fields like `metadata.level`)
|
|
854
|
-
- `minDurationSec` / `maxDurationSec`: Valid gameplay duration range
|
|
855
|
-
- `minScore` / `maxScore`: Valid score bounds
|
|
856
|
-
- `modes`: Game modes (can override min/max values per mode)
|
|
857
|
-
- `periods`: Time periods for leaderboards (`daily`, `weekly`, `monthly`, `alltime`)
|
|
858
|
-
- `antiCheat`: Anti-cheat settings
|
|
859
|
-
- `seedEntries`: Pre-populate leaderboards with NPC scores (nested by mode → period)
|
|
1010
|
+
|
|
860
1011
|
|
|
861
1012
|
**Features:**
|
|
1013
|
+
- **Three Security Levels**: Simple, Token, Sealed - choose based on game stakes
|
|
862
1014
|
- **Multiple Modes**: Support different game modes (classic, hard, etc.)
|
|
863
1015
|
- **Time Periods**: Daily, weekly, monthly, and all-time leaderboards
|
|
864
|
-
- **Anti-Cheat**:
|
|
865
|
-
- **Shadow Banning**: Cheaters see their own scores but are hidden from others
|
|
866
|
-
- **Trust Scores**: Per-player reputation that decays with suspicious behavior
|
|
1016
|
+
- **Anti-Cheat**: Rate limiting, trust scores, shadow banning, optional session validation & sealing
|
|
867
1017
|
- **Seed Entries**: Pre-populate leaderboards with NPC scores
|
|
868
1018
|
- **Pagination**: Cursor-based pagination for large leaderboards
|
|
869
1019
|
- **UTC-Based Periods**: All players globally compete in same daily/weekly/monthly periods
|
|
870
1020
|
|
|
871
|
-
**Security Notes:**
|
|
872
|
-
- Session tokens expire after 1 hour and can only be used once to prevent replay attacks
|
|
873
|
-
- Use `requiresHash: true` in production with a strong `hashSecret`
|
|
874
|
-
- Keep `hashSecret` out of version control (use environment variables or local config)
|
|
875
|
-
- Store `hashSecret` in `config.local.json` (gitignored) for local development
|
|
876
1021
|
|
|
877
1022
|
---
|
|
878
1023
|
|
|
@@ -984,9 +1129,65 @@ Tests are written using Jest with JSDOM environment for browser API simulation.
|
|
|
984
1129
|
|
|
985
1130
|
---
|
|
986
1131
|
|
|
987
|
-
##
|
|
1132
|
+
## Embedded Libraries (Vite)
|
|
1133
|
+
|
|
1134
|
+
For **Vite projects**, use the included plugin to automatically reduce bundle size by using embedded libraries:
|
|
1135
|
+
|
|
1136
|
+
```bash
|
|
1137
|
+
npm install @series-inc/venus-sdk
|
|
1138
|
+
```
|
|
1139
|
+
|
|
1140
|
+
```typescript
|
|
1141
|
+
// vite.config.ts
|
|
1142
|
+
import { venusLibrariesPlugin } from '@series-inc/venus-sdk/vite';
|
|
1143
|
+
|
|
1144
|
+
export default defineConfig({
|
|
1145
|
+
plugins: [venusLibrariesPlugin({ appName: 'my-game' })],
|
|
1146
|
+
build: { target: 'es2022' } // Required for top-level await
|
|
1147
|
+
});
|
|
1148
|
+
```
|
|
1149
|
+
|
|
1150
|
+
**Build commands:**
|
|
1151
|
+
- `npm run build` → Embedded libraries (default, optimal)
|
|
1152
|
+
- `VENUS_DISABLE_EMBEDDED_LIBS=true npm run build` → Bundled (standalone)
|
|
1153
|
+
|
|
1154
|
+
**Supported libraries:** Phaser, React, ReactDOM, Three.js, Matter.js, inkjs, Zustand
|
|
1155
|
+
|
|
1156
|
+
See `src/vite/README.md` for details.
|
|
1157
|
+
|
|
1158
|
+
### For Non-Vite Bundlers
|
|
1159
|
+
|
|
1160
|
+
The Vite plugin does three things you can recreate in any bundler:
|
|
1161
|
+
|
|
1162
|
+
**1. Externalize library imports** - Replace imports with virtual modules:
|
|
1163
|
+
```javascript
|
|
1164
|
+
// Input: import Phaser from 'phaser';
|
|
1165
|
+
// Output: const lib = await (async () => {
|
|
1166
|
+
// await window.__venusLibraryShim.ready();
|
|
1167
|
+
// return window.__venusLibraryExports['phaser@3.90.0'];
|
|
1168
|
+
// })();
|
|
1169
|
+
// export default lib;
|
|
1170
|
+
```
|
|
1171
|
+
|
|
1172
|
+
**2. Inject config into HTML `<head>`:**
|
|
1173
|
+
```html
|
|
1174
|
+
<script>
|
|
1175
|
+
window.__venusLibrariesConfig = {
|
|
1176
|
+
enabled: true,
|
|
1177
|
+
required: ["phaser@3.90.0"],
|
|
1178
|
+
manifest: { "phaser@3.90.0": { cdnPath: "...", globalVar: "Phaser" } },
|
|
1179
|
+
cdnBase: "https://venus-static-01293ak.web.app/libs/"
|
|
1180
|
+
};
|
|
1181
|
+
</script>
|
|
1182
|
+
```
|
|
1183
|
+
|
|
1184
|
+
**3. Inject minimal shim** (loads from CDN, resolves `ready()` promise):
|
|
1185
|
+
```javascript
|
|
1186
|
+
window.__venusLibraryShim = { ready: () => Promise };
|
|
1187
|
+
// Fetch libraries from CDN, evaluate, register in __venusLibraryExports
|
|
1188
|
+
```
|
|
988
1189
|
|
|
989
|
-
|
|
1190
|
+
See `embeddedLibrariesManifest.ts` for library definitions and `webviewLibraryShimSource.ts` for the full shim (used on mobile).
|
|
990
1191
|
|
|
991
1192
|
---
|
|
992
1193
|
|