M8 CMS SDK Documentation
Table of Contents
- Overview
- Installation
- Quick Start
- Configuration
- Core Features
- API Reference
- Usage Examples
- Best Practices
- Troubleshooting
Overview
The M8 CMS SDK is a Unity package that provides seamless integration with the GameFlow CMS backend. It enables your Unity game to fetch game content (levels, collections, media) from Firebase in real-time, with intelligent caching and automatic content management.
Key Features
- Automatic Content Management: Download and cache content automatically based on player progress
- Firebase Integration: Built-in Firebase Firestore and Storage support
- Version Management: Support for multiple content versions (Live, Latest, Specific)
- Intelligent Caching: In-memory and file system caching with automatic cleanup
- Built-in Content Support: Optional built-in content for offline play
- Event-Driven Architecture: Subscribe to events for content updates
- Cross-Platform: Supports Android, iOS, and Web platforms
Installation
Prerequisites
- Unity 2021.3 or higher
- Firebase Unity SDK (already included in the package)
Setup Steps
-
Import the SDK Package
- Import the M8 CMS SDK into your Unity project
- The SDK includes all necessary Firebase dependencies
-
Add to Scene
- Drag the
M8CMS_SDK_PrefabfromAssets/M8CMS_SDK_Prefab.prefabinto your scene - Or manually add the
M8CMSManagercomponent to any GameObject
- Drag the
-
Configure App Key
- Select the M8CMSManager GameObject
- Set your
appKeyin the Inspector (this is your unique game identifier)
-
Configure Firebase (Optional)
- The SDK comes with default Firebase configuration
- To use custom Firebase project, modify the configuration in the Inspector
Quick Start
Basic Setup
- Access the Manager
C#using M8CMS; public class MyGameManager : MonoBehaviour { private M8CMSManager cmsManager; void Start() { // Get the singleton instance cmsManager = M8CMSManager.Instance; // Subscribe to events M8CMSManager.OnInitializationComplete += OnInitializationComplete; M8CMSManager.OnGameDataLoaded += OnGameDataLoaded; } void OnDestroy() { // Unsubscribe from events M8CMSManager.OnInitializationComplete -= OnInitializationComplete; M8CMSManager.OnGameDataLoaded -= OnGameDataLoaded; } void OnInitializationComplete(bool success) { if (success) { Debug.Log("SDK initialized successfully!"); } } void OnGameDataLoaded(GameData gameData) { Debug.Log($"Game data loaded! Levels: {gameData.levels.Count}"); Debug.Log($"Collections: {gameData.collections.Count}"); // Now you can access your game content ProcessGameData(gameData); } void ProcessGameData(GameData data) { foreach (var level in data.levels) { Debug.Log($"Level {level.index}: {level.levelType} - {level.difficulty}"); } } }
- Load Level Content
C#// Load level image async void LoadLevelImage(int levelIndex) { var sprite = await cmsManager.GetLevelSpriteAsync(levelIndex); if (sprite != null) { // Use the sprite myImageUI.sprite = sprite; } } // Load level video async void LoadLevelVideo(int levelIndex) { var videoData = await cmsManager.GetLevelVideoAsync(levelIndex); if (videoData != null) { // Use the video data (Unity VideoPlayer compatible) myVideoPlayer.url = Application.persistentDataPath + "/temp_video.mp4"; File.WriteAllBytes(myVideoPlayer.url, videoData); } }
Configuration
M8CMSManager Inspector Settings
Basic Configuration
- App Key: Your unique game identifier (required)
- Auto Initialize: Automatically initialize SDK on Start
- Enable Debug Logs: Enable detailed logging
Version Management
- Version Selection Mode:
Live: Use the live version (default)Specific: Use a specific version by name
- Refresh Version List on Start: Load available versions on startup
Download Options
- Download Images/Videos on Start: Number of items to download immediately (0 = disabled)
- Download New Levels Before X Level: Number of levels to pre-download ahead of player progress
- Download X New Levels Bundle: Batch size for downloads
- Enable Auto Download: Automatically download content based on player progress
- Max Concurrent Downloads: Limit simultaneous downloads (1-5 recommended)
- Max Cache Size: Maximum cache size in MB (default: 40MB)
Built-in Content (Optional)
- Enable Built-in Content: Use offline fallback content
- Built-in Level Count: Number of built-in levels
- Built-in Collection Count: Number of built-in collections
- Built-in Content Path: Resource path for offline content
Core Features
1. Automatic Content Management
The SDK automatically manages content downloads based on player progress:
C#// Consume content when player reaches a level cmsManager.ConsumeLevelContent(levelIndex); // The SDK will automatically download: // - Current level content // - N levels ahead (configured in settings)
2. Version Management
Switch between different content versions:
C#// Get all available versions var versions = await cmsManager.GetAllVersionsAsync(); // Switch to a specific version cmsManager.SetVersionSelectionMode(VersionSelectionMode.Specific); cmsManager.SetSelectedVersionName("v1.2.0"); // Or use the latest version var (versionId, version) = await cmsManager.GetLatestVersionAsync();
3. Caching System
The SDK includes three-level caching:
- In-Memory Cache: Fastest, for active content
- File System Cache: Persistent storage
- Preload Cache: For predictive downloads
C#// Check if content is cached bool isReady = cmsManager.IsLevelContentReady(levelIndex); // Get cache statistics var imageStats = cmsManager.GetImageCacheStats(); var videoStats = cmsManager.GetVideoCacheStats(); var preloadStats = cmsManager.GetPreloadCacheStats(); // Clear caches cmsManager.ClearImageCache(); cmsManager.ClearVideoCache(); cmsManager.ClearPreloadCache();
4. Event System
Subscribe to SDK events for reactive updates:
C#// Global events M8CMSManager.OnInitializationComplete += HandleInit; M8CMSManager.OnGameDataLoaded += HandleDataLoaded; M8CMSManager.OnError += HandleError; // Content ready events cmsManager.OnLevelContentReady += OnLevelContentReady; cmsManager.OnCollectionContentReady += OnCollectionContentReady;
API Reference
Initialization
M8CMSManager Instance
C#// Singleton instance M8CMSManager cmsManager = M8CMSManager.Instance;
bool IsInitialized()
C#// Check if SDK is initialized if (cmsManager.IsInitialized()) { // Ready to use }
Getting Data
GameData GetCurrentGameData()
C#// Get complete game data GameData data = cmsManager.GetCurrentGameData();
Level GetLevelByIndex(int index)
C#// Get specific level Level level = cmsManager.GetLevelByIndex(5);
CollectionItem GetCollectionByIndex(int index)
C#// Get specific collection CollectionItem collection = cmsManager.GetCollectionByIndex(3);
List<Level> GetAllLevels()
C#// Get all levels List<Level> allLevels = cmsManager.GetAllLevels();
List<CollectionItem> GetAllCollections()
C#// Get all collections List<CollectionItem> allCollections = cmsManager.GetAllCollections();
Loading Content (Async)
Task<Texture2D> GetLevelImageAsync(int levelIndex)
C#var texture = await cmsManager.GetLevelImageAsync(levelIndex);
Task<Sprite> GetLevelSpriteAsync(int levelIndex)
C#var sprite = await cmsManager.GetLevelSpriteAsync(levelIndex);
Task<byte[]> GetLevelVideoAsync(int levelIndex)
C#var videoBytes = await cmsManager.GetLevelVideoAsync(levelIndex);
Task<Texture2D> GetCollectionImageAsync(int collectionIndex)
C#var texture = await cmsManager.GetCollectionImageAsync(collectionIndex);
Task<Sprite> GetCollectionSpriteAsync(int collectionIndex)
C#var sprite = await cmsManager.GetCollectionSpriteAsync(collectionIndex);
Task<byte[]> GetCollectionVideoAsync(int collectionIndex)
C#var videoBytes = await cmsManager.GetCollectionVideoAsync(collectionIndex);
Content Status
bool IsLevelContentReady(int levelIndex)
C#// Check if level content is cached and ready bool ready = cmsManager.IsLevelContentReady(levelIndex);
bool IsCollectionContentReady(int collectionIndex)
C#// Check if collection content is cached and ready bool ready = cmsManager.IsCollectionContentReady(collectionIndex);
Content Consumption
void ConsumeLevelContent(int levelIndex)
C#// Notify SDK that player reached this level cmsManager.ConsumeLevelContent(levelIndex);
void ConsumeCollectionContent(int collectionIndex)
C#// Notify SDK that player reached this collection cmsManager.ConsumeCollectionContent(collectionIndex);
Cache Management
void ClearImageCache()
C#// Clear all cached images cmsManager.ClearImageCache();
void ClearVideoCache()
C#// Clear all cached videos cmsManager.ClearVideoCache();
void ClearPreloadCache()
C#// Clear preload cache cmsManager.ClearPreloadCache();
(int fileCount, long totalSizeMB) GetImageCacheStats()
C#var stats = cmsManager.GetImageCacheStats(); Debug.Log($"Cached images: {stats.fileCount}, Size: {stats.totalSizeMB}MB");
(int fileCount, long totalSizeMB) GetVideoCacheStats()
C#var stats = cmsManager.GetVideoCacheStats(); Debug.Log($"Cached videos: {stats.fileCount}, Size: {stats.totalSizeMB}MB");
PreloadCacheStats GetPreloadCacheStats()
C#var stats = cmsManager.GetPreloadCacheStats(); Debug.Log($"Preloaded levels: {stats.CachedLevelCount}");
Version Management
Task<List<(string versionId, Version version)>> GetAllVersionsAsync()
C#var versions = await cmsManager.GetAllVersionsAsync();
Task<(string versionId, Version version)> GetLatestVersionAsync()
C#var (versionId, version) = await cmsManager.GetLatestVersionAsync();
Task<Version> GetLiveVersionAsync()
C#var liveVersion = await cmsManager.GetLiveVersionAsync();
void SetVersionSelectionMode(VersionSelectionMode mode)
C#// Set to Live or Specific version cmsManager.SetVersionSelectionMode(VersionSelectionMode.Live);
void SetSelectedVersionName(string versionName)
C#// Set specific version by name cmsManager.SetSelectedVersionName("v1.2.0");
VersionSelectionMode GetVersionSelectionMode()
C#var mode = cmsManager.GetVersionSelectionMode();
string GetSelectedVersionName()
C#var versionName = cmsManager.GetSelectedVersionName();
string GetCurrentVersionId()
C#var versionId = cmsManager.GetCurrentVersionId();
List<(string versionId, Version version)> GetAvailableVersions()
C#// Get cached versions var versions = cmsManager.GetAvailableVersions();
Task<bool> LoadGameDataForVersionAsync(string versionId)
C#// Load specific version bool success = await cmsManager.LoadGameDataForVersionAsync(versionId);
Helper Methods
M8CMSContentHelper.IsContentAvailableForLevel(int levelIndex, M8CMSManager cmsManager)
C#using M8CMS.Helpers; // Check if content is available WITHOUT triggering downloads bool available = M8CMSContentHelper.IsContentAvailableForLevel(levelIndex, cmsManager);
M8CMSContentHelper.IsContentAvailableForCollection(int collectionIndex, M8CMSManager cmsManager)
C#// Check if collection content is available bool available = M8CMSContentHelper.IsContentAvailableForCollection(collectionIndex, cmsManager);
Usage Examples
Example 1: Loading a Level UI
C#using System.Collections; using UnityEngine; using UnityEngine.UI; using M8CMS; public class LevelButton : MonoBehaviour { [SerializeField] private Image levelImage; [SerializeField] private Text levelText; [SerializeField] private Button button; private M8CMSManager cmsManager; private Level levelData; private int levelIndex; public void Setup(Level level, int index) { cmsManager = M8CMSManager.Instance; levelData = level; levelIndex = index; // Update UI text levelText.text = $"Level {index + 1}"; // Load image asynchronously StartCoroutine(LoadLevelImage()); // Setup button button.onClick.AddListener(OnButtonClicked); } IEnumerator LoadLevelImage() { var task = cmsManager.GetLevelSpriteAsync(levelIndex); yield return new WaitUntil(() => task.IsCompleted); if (task.Result != null) { levelImage.sprite = task.Result; } } void OnButtonClicked() { // Notify SDK that player is consuming this level cmsManager.ConsumeLevelContent(levelIndex); // Load the level scene or content SceneManager.LoadScene("GameLevel"); } }
Example 2: Gallery with Collections
C#using System.Collections.Generic; using M8CMS; using M8CMS.Models; public class CollectionGallery : MonoBehaviour { [SerializeField] private Transform container; [SerializeField] private GameObject itemPrefab; private M8CMSManager cmsManager; private List<CollectionItem> collections; void Start() { cmsManager = M8CMSManager.Instance; // Wait for initialization M8CMSManager.OnGameDataLoaded += SetupGallery; if (cmsManager.IsGameDataLoaded()) { SetupGallery(cmsManager.GetCurrentGameData()); } } void SetupGallery(GameData gameData) { collections = gameData.collections; foreach (var collection in collections) { CreateCollectionItem(collection); } } void CreateCollectionItem(CollectionItem collection) { var item = Instantiate(itemPrefab, container); var button = item.GetComponent<Button>(); // Load collection preview StartCoroutine(LoadCollectionPreview(item, collection)); button.onClick.AddListener(() => OnCollectionClicked(collection.index)); } IEnumerator LoadCollectionPreview(GameObject item, CollectionItem collection) { var image = item.GetComponent<Image>(); if (collection.index < builtInCollectionCount) { // Use built-in content var sprite = cmsManager.GetCollectionImage(collection.index); image.sprite = sprite; } else { // Download from Firebase var task = cmsManager.GetCollectionSpriteAsync(collection.index); yield return new WaitUntil(() => task.IsCompleted); if (task.Result != null) { image.sprite = task.Result; } } } async void OnCollectionClicked(int collectionIndex) { // Consume collection content cmsManager.ConsumeCollectionContent(collectionIndex); // Load collection preview var videoData = await cmsManager.GetCollectionVideoAsync(collectionIndex); if (videoData != null) { ShowCollectionVideo(videoData); } } }
Example 3: Progress Tracking
C#using M8CMS; public class ProgressTracker : MonoBehaviour { private M8CMSManager cmsManager; void Start() { cmsManager = M8CMSManager.Instance; // Subscribe to content ready events cmsManager.OnLevelContentReady += OnLevelContentReady; cmsManager.OnCollectionContentReady += OnCollectionContentReady; } void OnLevelContentReady(int levelIndex, bool isReady) { if (isReady) { Debug.Log($"Level {levelIndex} content is ready!"); UpdateLevelUI(levelIndex, true); } else { Debug.Log($"Level {levelIndex} is still downloading..."); } } void OnCollectionContentReady(int collectionIndex, bool isReady) { if (isReady) { Debug.Log($"Collection {collectionIndex} content is ready!"); UpdateCollectionUI(collectionIndex, true); } } void UpdateLevelUI(int levelIndex, bool isReady) { // Update UI indicators var indicator = GetLevelIndicator(levelIndex); indicator.color = isReady ? Color.green : Color.yellow; } void UpdateCollectionUI(int collectionIndex, bool isReady) { // Update UI indicators var indicator = GetCollectionIndicator(collectionIndex); indicator.color = isReady ? Color.green : Color.yellow; } }
Example 4: Version Switching
C#using M8CMS; using UnityEngine.UI; public class VersionSelector : MonoBehaviour { [SerializeField] private Dropdown versionDropdown; private M8CMSManager cmsManager; private List<(string versionId, Version version)> versions; async void Start() { cmsManager = M8CMSManager.Instance; // Load available versions versions = await cmsManager.GetAllVersionsAsync(); // Populate dropdown versionDropdown.ClearOptions(); foreach (var (versionId, version) in versions) { versionDropdown.options.Add(new Dropdown.OptionData(version.versionName)); } versionDropdown.onValueChanged.AddListener(OnVersionChanged); } void OnVersionChanged(int index) { if (index >= 0 && index < versions.Count) { var (versionId, version) = versions[index]; LoadVersion(versionId); } } async void LoadVersion(string versionId) { Debug.Log($"Loading version: {versionId}"); bool success = await cmsManager.LoadGameDataForVersionAsync(versionId); if (success) { Debug.Log("Version loaded successfully!"); ReloadContent(); } else { Debug.LogError("Failed to load version"); } } }
Best Practices
1. Always Subscribe to Events
C#void OnEnable() { M8CMSManager.OnInitializationComplete += OnInit; M8CMSManager.OnGameDataLoaded += OnDataLoaded; } void OnDisable() { M8CMSManager.OnInitializationComplete -= OnInit; M8CMSManager.OnGameDataLoaded -= OnDataLoaded; }
2. Use Coroutines for Async Operations
C#public void LoadLevelAsync(int levelIndex) { StartCoroutine(LoadLevelImageCoroutine(levelIndex)); } IEnumerator LoadLevelImageCoroutine(int levelIndex) { var task = cmsManager.GetLevelSpriteAsync(levelIndex); yield return new WaitUntil(() => task.IsCompleted); if (task.Result != null) { levelImage.sprite = task.Result; } }
3. Consume Content Appropriately
C#// Notify SDK when player reaches a level public void StartLevel(int levelIndex) { cmsManager.ConsumeLevelContent(levelIndex); LoadLevel(levelIndex); }
4. Check Content Availability
C#using M8CMS.Helpers; void UpdateLevelButtons() { foreach (var level in levels) { bool isAvailable = M8CMSContentHelper.IsContentAvailableForLevel(level.index, cmsManager); if (isAvailable) { levelButton.interactable = true; } else { levelButton.interactable = false; levelButton.image.color = Color.gray; } } }
5. Optimize Cache Management
C#void OnApplicationPause(bool pause) { if (pause) { // Optionally clear in-memory cache to save RAM // File system cache remains intact } }
6. Handle Network Errors
C#void Start() { cmsManager.OnContentLoadError += OnError; } void OnError(int itemIndex, string errorMessage) { Debug.LogError($"Failed to load content for item {itemIndex}: {errorMessage}"); ShowErrorPopup(errorMessage); }
Troubleshooting
Issue: SDK Not Initializing
Solution:
- Check internet connection
- Verify
appKeyis correct - Enable debug logs to see initialization process
- Check Firebase configuration
Issue: Content Not Loading
Solution:
- Check if content exists in Firebase
- Verify image/video URLs are valid
- Check cache size limits
- Ensure proper permissions in Firebase
Issue: Cache Growing Too Large
Solution:
- Adjust
Max Cache Sizesetting - Implement periodic cache cleanup
- Use
ClearImageCache()andClearVideoCache()strategically
Issue: Downloads Not Starting
Solution:
- Enable auto-download in settings
- Check
Download New Levels Before X Levelsetting - Ensure content is consumed via
ConsumeLevelContent()
Issue: Built-in Content Not Working
Solution:
- Verify built-in content files exist in
Resources/BuiltInContent - Check file naming matches level/collection indices
- Enable built-in content in settings
Data Models
Level
C#public class Level { public int index; // Unique level index public string levelType; // "normal", "boss", or "tutorial" public string difficulty; // "normal" or "hard" public string gridSize; // Grid dimensions public int rewardCoin; // Coin reward public string imageUrl; // Image URL from Firebase public string thumbnailUrl; // Thumbnail URL public string videoUrl; // Video URL from Firebase public bool isCustomLevel; // Whether level is custom public string extra; // Additional data }
CollectionItem
C#public class CollectionItem { public int index; // Unique collection index public string name; // Collection name public string grid; // Grid data public string imageUrl; // Image URL from Firebase public string thumbnailUrl; // Thumbnail URL public string videoUrl; // Video URL from Firebase public int startLevel; // First level where item appears public int endLevel; // Last level where item appears }
Version
C#public class Version { public string versionName; // Version identifier public bool isLive; // Whether version is live public DateTime createdAt; // Creation timestamp }
GameData
C#public class GameData { public Game game; // Game info public Version version; // Current version public List<Level> levels; // All levels public List<CollectionItem> collections; // All collections }
Events Reference
Static Events (M8CMSManager)
OnInitializationComplete(bool success)- SDK initialization completeOnGameDataLoaded(GameData gameData)- Game data loaded from FirebaseOnError(string errorMessage)- Error occurred
Instance Events (cmsManager)
OnLevelContentReady(int levelIndex, bool isReady)- Level content readyOnCollectionContentReady(int collectionIndex, bool isReady)- Collection content readyOnContentLoadError(int itemIndex, string errorMessage)- Content load errorOnNetworkStateChanged(bool isAvailable)- Network availability changed
Support
For additional help:
- Check the example scenes in
Assets/Scripts/M8CMS/Examples/ - Review the inline code documentation
- Enable debug logs for detailed information
- Contact the M8 CMS support team
Version: 1.0
Last Updated: December 2024