### Complete Viverse SDK Initialization Flow Source: https://github.com/vivedeveloperrelations/viverseunitysdk/blob/master/Unity_Viverse_SDK_Developer_Guide.md Outlines the full asynchronous initialization sequence for the Viverse SDK, including core SDK setup, mandatory SSO initialization, and subsequent initialization of other services like Leaderboard, Achievement, and Avatar, with error handling. ```csharp private async Task InitializeViverse() { try { // 1. Get environment configuration _hostConfig = GetEnvironmentConfig(); // 2. Initialize core SDK _core = new ViverseCore(); ViverseResult initResult = await _core.Initialize(_hostConfig, destroyCancellationToken); if (!initResult.IsSuccess) { initResult.LogError("SDK Core Initialize"); throw new Exception($"SDK initialization failed: {initResult.ErrorMessage}"); } // 3. Initialize SSO (Authentication) - REQUIRED FIRST bool ssoSuccess = await InitializeSSO(); if (!ssoSuccess) return; // 4. Initialize other services only after successful authentication await TestLeaderboardFunctionality(); await TestAchievementFunctionality(); await InitializeAvatarService(); } catch (Exception e) { Debug.LogError($"Error during Viverse initialization: {e.Message}"); } } ``` -------------------------------- ### Handle Authentication Errors in Viverse SDK Source: https://github.com/vivedeveloperrelations/viverseunitysdk/blob/master/Unity_Viverse_SDK_Developer_Guide.md Provides an example of handling authentication-related errors, specifically SSO initialization failures. It demonstrates checking for specific return codes like network timeout or invalid parameters to provide targeted user feedback. ```csharp // SSO initialization failure if (!ssoResult.IsSuccess) { if (ssoResult.RawResult.ReturnCode == (int)ViverseSDKReturnCode.ErrorNetworkTimeout) { Debug.LogWarning("Network timeout - check connectivity"); } else if (ssoResult.RawResult.ReturnCode == (int)ViverseSDKReturnCode.ErrorInvalidParameter) { Debug.LogError("Invalid App ID or configuration"); } } ``` -------------------------------- ### Define Viverse SDK Application and Client IDs Source: https://github.com/vivedeveloperrelations/viverseunitysdk/blob/master/Unity_Viverse_SDK_Developer_Guide.md Illustrates the setup for `AppID` and `ClientID` variables. In production, these values are expected to be identical, but may differ during development or testing phases. ```csharp // Note: In production, ClientID and AppID will be the same value // For development/testing, they may be different private string AppID = "your-app-id-here"; private string ClientID = AppID; // Will be unified in production ``` -------------------------------- ### Retrieve Leaderboard Scores with Various Configurations in C# Source: https://github.com/vivedeveloperrelations/viverseunitysdk/blob/master/Unity_Viverse_SDK_Developer_Guide.md This C# example demonstrates retrieving leaderboard scores using different `LeaderboardConfig` settings. It showcases default, global, local, and 'around user' configurations, allowing developers to fetch specific ranges, regions, and timeframes of scores. The results are logged, and error handling is implemented for each retrieval attempt. ```csharp private async Task TestGetScores(string leaderboardName) { string appId = AppID; // Use the same App ID throughout // Multiple configuration examples LeaderboardConfig[] configs = new[] { // Default configuration LeaderboardConfig.CreateDefault(leaderboardName), // Global leaderboard, all time, top 100 new LeaderboardConfig { Name = leaderboardName, RangeStart = 0, RangeEnd = 100, Region = LeaderboardRegion.Global, TimeRange = LeaderboardTimeRange.Alltime, AroundUser = false }, // Local leaderboard, weekly, specific range new LeaderboardConfig { Name = leaderboardName, RangeStart = 10, RangeEnd = 50, Region = LeaderboardRegion.Local, TimeRange = LeaderboardTimeRange.Weekly, AroundUser = false }, // Around user rankings (must set AroundUser first) new LeaderboardConfig { AroundUser = true, Name = leaderboardName, RangeStart = -5, // 5 positions before user RangeEnd = 10, // 10 positions after user Region = LeaderboardRegion.Global, TimeRange = LeaderboardTimeRange.Monthly, } }; foreach (LeaderboardConfig config in configs) { ViverseResult result = await _core.LeaderboardService.GetLeaderboardScores(appId, config); if (result.IsSuccess) { Debug.Log($"Total entries: {result.Data.total_count}"); if (result.Data.ranking != null) { foreach (LeaderboardEntry entry in result.Data.ranking) { Debug.Log($"Rank {entry.rank}: {entry.name} - {entry.value}"); } } } else { result.LogError("Get Leaderboard Scores"); } } return true; } ``` -------------------------------- ### Initialize Viverse Core SDK and Services Source: https://github.com/vivedeveloperrelations/viverseunitysdk/blob/master/Unity_Viverse_SDK_Developer_Guide.md Demonstrates the hierarchical initialization pattern for the Viverse SDK. First, the core SDK is initialized with host configuration, then individual services like SSO, Avatar, and Leaderboard are initialized asynchronously. ```csharp // Step 1: Initialize Core SDK ViverseCore _core = new ViverseCore(); HostConfig _hostConfig = GetEnvironmentConfig(); ViverseResult initResult = await _core.Initialize(_hostConfig, cancellationToken); // Step 2: Initialize individual services as needed await _core.SSOService.Initialize(clientId); await _core.AvatarService.Initialize(); await _core.LeaderboardService.Initialize(); ``` -------------------------------- ### Initialize Viverse Leaderboard Service in C# Source: https://github.com/vivedeveloperrelations/viverseunitysdk/blob/master/Unity_Viverse_SDK_Developer_Guide.md This C# snippet demonstrates how to initialize the Viverse Leaderboard Service. It requires prior authentication and checks the `ViverseResult` to ensure successful initialization, logging any errors if the operation fails. ```csharp // Initialize leaderboard service (requires authentication) ViverseResult initResult = await _core.LeaderboardService.Initialize(); if (!initResult.IsSuccess) { initResult.LogError("Initialize Leaderboard Service"); return false; } ``` -------------------------------- ### Initialize Viverse SSO Service and Handle Login Source: https://github.com/vivedeveloperrelations/viverseunitysdk/blob/master/Unity_Viverse_SDK_Developer_Guide.md Demonstrates the initialization of the Viverse SSO service using the `AppID`. It checks for an existing access token and triggers a new login flow if authentication is required, returning true on success. ```csharp private async Task InitializeSSO() { try { // Initialize SSO service with app ID (used as client ID) bool ssoInitSuccess = await _core.SSOService.Initialize(AppID); // Check for existing authentication if (await AlreadyHadAccessToken()) { return true; } // Trigger new login if needed LoginResult loginResult = await DoLogin(); return loginResult != null; } catch (Exception e) { Debug.LogError($"SSO initialization failed: {e.Message}"); return false; } } ``` -------------------------------- ### Install UniVRM and UniGLTF Packages via Git URL in Unity Source: https://github.com/vivedeveloperrelations/viverseunitysdk/blob/master/Samples~/ViverseSampleScenes/Scripts/AvatarRendering/README_AvatarRendering.md Instructions to add UniVRM and UniGLTF packages to a Unity project using the Unity Package Manager (UPM). This involves specifying Git URLs with specific paths and versions to ensure the correct dependencies for VRM avatar support are installed and compatible with the VIVERSE SDK. ```Unity Package Manager https://github.com/vrm-c/UniVRM.git?path=/Assets/UniGLTF#v0.128.2 https://github.com/vrm-c/UniVRM.git?path=/Assets/VRM#v0.128.2 https://github.com/vrm-c/UniVRM.git?path=/Assets/VRM10#v0.128.2 ``` -------------------------------- ### Specify UniVRM and VRM Package Dependencies for Unity Source: https://github.com/vivedeveloperrelations/viverseunitysdk/blob/master/README.md These Git URLs specify the required UniVRM and VRM packages for proper avatar rendering within the Viverse Unity SDK. They can be manually added to the Unity project's 'Packages/manifest.json' if automatic installation via the WebGL Settings window fails or custom package management is desired. ```Git https://github.com/vrm-c/UniVRM.git?path=/Assets/UniGLTF#v0.128.2 https://github.com/vrm-c/UniVRM.git?path=/Assets/VRM#v0.128.2 https://github.com/vrm-c/UniVRM.git?path=/Assets/VRM10#v0.128.2 ``` -------------------------------- ### Configure Viverse SDK Services Based on Environment Source: https://github.com/vivedeveloperrelations/viverseunitysdk/blob/master/Unity_Viverse_SDK_Developer_Guide.md Provides a method for dynamically determining the appropriate HostConfig based on the application's current URL. This allows for environment-specific configurations, with a fallback to production settings if detection fails. ```csharp // Use environment-appropriate configuration private HostConfig GetEnvironmentConfig() { // Automatically detect environment from URL HostConfigUtil.HostType hostType = new HostConfigUtil().GetHostTypeFromPageURLIfPossible(Application.absoluteURL); // Fall back to production if detection fails return HostConfigLookup.HostTypeToDefaultHostConfig.TryGetValue(hostType, out var config) ? config : HostConfigLookup.HostTypeToDefaultHostConfig[HostConfigUtil.HostType.PROD]; } ``` -------------------------------- ### Follow Required Initialization Order for Viverse SDK Services Source: https://github.com/vivedeveloperrelations/viverseunitysdk/blob/master/Unity_Viverse_SDK_Developer_Guide.md Outlines the critical sequence for initializing Viverse SDK components: Core SDK, then SSO service and authentication, followed by individual services like Avatar and Leaderboard. Adhering to this order ensures proper SDK functionality. ```csharp // REQUIRED ORDER: // 1. Core SDK initialization // 2. SSO service initialization and authentication // 3. Individual service initialization (Avatar, Leaderboard) // 4. Service operations await _core.Initialize(_hostConfig, cancellationToken); await InitializeSSO(); // Must complete successfully first await _core.AvatarService.Initialize(); await _core.LeaderboardService.Initialize(); ``` -------------------------------- ### Manage Service Initialization Errors in Viverse SDK Source: https://github.com/vivedeveloperrelations/viverseunitysdk/blob/master/Unity_Viverse_SDK_Developer_Guide.md Shows how to handle errors during the initialization of individual SDK services, such as the Avatar service. It includes logging the error and checking if the initialization failure is recoverable, preventing further operations if necessary. ```csharp // Avatar service initialization if (!avatarInit.IsSuccess) { avatarInit.LogError("Initialize Avatar Service"); if (avatarInit.IsRecoverableError()) { Debug.LogWarning("Avatar service initialization failed but may be recoverable"); } return; // Don't proceed with avatar operations } ``` -------------------------------- ### Retrieve Viverse SDK Environment Configuration Source: https://github.com/vivedeveloperrelations/viverseunitysdk/blob/master/Unity_Viverse_SDK_Developer_Guide.md Provides a utility method to determine the appropriate `HostConfig` based on the application's URL, falling back to production configuration if no specific host type is found. ```csharp private HostConfig GetEnvironmentConfig() { HostConfigUtil.HostType hostType = new HostConfigUtil().GetHostTypeFromPageURLIfPossible(Application.absoluteURL); return HostConfigLookup.HostTypeToDefaultHostConfig.TryGetValue(hostType, out var config) ? config : HostConfigLookup.HostTypeToDefaultHostConfig[HostConfigUtil.HostType.PROD]; } ``` -------------------------------- ### Manage Application Configuration with a Static Class in C# Source: https://github.com/vivedeveloperrelations/viverseunitysdk/blob/master/Unity_Viverse_SDK_Developer_Guide.md Shows how to centralize application configuration values using a static C# class. It defines constants for application ID, client ID, leaderboard names, and achievement IDs, promoting easy management and consistency across the application. ```csharp // Centralize configuration values public class ViverseConfig { // Primary App ID (used for all services) public const string APP_ID = "your-app-id-here"; // Note: In production, Client ID = App ID // For testing, these may be different values public const string CLIENT_ID = APP_ID; // Leaderboard names public const string LEADERBOARD_SCORES = "main_scores"; public const string LEADERBOARD_WEEKLY = "weekly_challenge"; // Achievement IDs public const string ACHIEVEMENT_FIRST_PLAY = "first_play"; public const string ACHIEVEMENT_HIGH_SCORE = "high_score"; } ``` -------------------------------- ### Manage Authentication State for Viverse SDK Operations Source: https://github.com/vivedeveloperrelations/viverseunitysdk/blob/master/Unity_Viverse_SDK_Developer_Guide.md Emphasizes the importance of checking authentication state before performing operations that require it. It also shows how to store authentication-related information, such as AuthKey and UserInfo, for subsequent service access. ```csharp // Always check authentication before proceeding if (_authKey == null) { Debug.LogWarning("No auth key - cannot proceed with authenticated operations"); return; } // Store authentication state for service access private AuthKey _authKey; private UserInfo _userInfo; ``` -------------------------------- ### Validate Operation Results and Data in C# Source: https://github.com/vivedeveloperrelations/viverseunitysdk/blob/master/Unity_Viverse_SDK_Developer_Guide.md Illustrates how to safely access data after an operation by checking for success and null data. It covers scenarios where an operation succeeds with data, succeeds without data, or fails, guiding on appropriate error handling. ```csharp // Validate data before processing if (result.IsSuccess && result.Data != null) { // Safe to access result.Data ProcessData(result.Data); } else if (result.IsSuccess && result.Data == null) { Debug.Log("Operation succeeded but returned no data"); } else { // Handle error case result.LogError("Operation Name"); } ``` -------------------------------- ### Initialize VIVE Unity SDK Avatar Service Source: https://github.com/vivedeveloperrelations/viverseunitysdk/blob/master/Unity_Viverse_SDK_Developer_Guide.md This C# asynchronous method initializes the VIVE Avatar Service. It calls `_core.AvatarService.Initialize()` and includes error handling to log any failures during the initialization process. Upon successful initialization, it indicates that subsequent avatar-related methods can be safely invoked. ```csharp private async Task InitializeAvatarService() { ViverseResult avatarInit = await _core.AvatarService.Initialize(); if (!avatarInit.IsSuccess) { avatarInit.LogError("Initialize Avatar Service"); return; } Debug.Log("✅ Avatar service initialized successfully"); // Now safe to call avatar methods await TestGetProfile(); await TestGetActiveAvatar(); // ... other avatar operations } ``` -------------------------------- ### Unlock Multiple Achievements in Batch in C# Source: https://github.com/vivedeveloperrelations/viverseunitysdk/blob/master/Unity_Viverse_SDK_Developer_Guide.md This asynchronous C# method provides an example of how to unlock multiple achievements simultaneously using a batch operation. It iterates through an array of achievement IDs, constructs a list of `Achievement` objects, and submits them in a single request to the Viverse Leaderboard Service, reporting the overall success and failure counts. ```csharp private async Task UnlockAchievementsBatch(string[] achievementIds) { string appId = AppID; // Use the same App ID throughout // Create achievement objects for all IDs var achievements = new List(); foreach (var id in achievementIds) { achievements.Add(new Achievement { api_name = id, unlock = true }); } // Submit batch request ViverseResult result = await _core.LeaderboardService.UploadUserAchievement(appId, achievements); if (!result.IsSuccess) { Debug.LogError($"Failed to upload achievements batch: {result.ErrorMessage}"); return false; } int successCount = result.Data?.success?.total ?? 0; int failureCount = result.Data?.failure?.total ?? 0; Debug.Log($"Batch achievement result: {successCount} succeeded, {failureCount} failed"); return successCount > 0; } ``` -------------------------------- ### Utilize Safe Logging Extensions for Detailed Error Reporting Source: https://github.com/vivedeveloperrelations/viverseunitysdk/blob/master/Unity_Viverse_SDK_Developer_Guide.md Illustrates the use of safe logging extensions provided by the SDK for detailed error reporting. This includes methods for comprehensive and detailed logging, as well as safe access to result properties to prevent null reference exceptions. ```csharp // Comprehensive error logging result.LogError("Operation Name"); // Detailed logging for debugging result.LogDetailed("Operation Name"); // Safe access to result properties string safeMessage = result.SafeMessage; T safePayload = result.SafePayload; ``` -------------------------------- ### Define Unity WebGL Build Output Directory Structure Source: https://github.com/vivedeveloperrelations/viverseunitysdk/blob/master/README.md This snippet illustrates the recommended directory structure for a Unity WebGL project, where the build output is placed in a 'Build' folder parallel to the 'Assets' directory. This structure is required for early releases of the Viverse Unity SDK and is currently not configurable. ```Shell PROJECT_ROOT/ ├── Assets/ ├── Build/ <- Build output here (name is currently not configurable for early releases) ├── Library/ └── ... ``` -------------------------------- ### Handle Recoverable and Non-Recoverable Errors in C# Source: https://github.com/vivedeveloperrelations/viverseunitysdk/blob/master/Unity_Viverse_SDK_Developer_Guide.md Demonstrates how to check for operation success, log errors, and differentiate between recoverable and non-recoverable errors. It suggests implementing retry logic or user notifications for recoverable issues and handling permanent failures for non-recoverable ones. ```csharp // Check for recoverable errors and provide user guidance if (!result.IsSuccess) { result.LogError("Operation Name"); if (result.IsRecoverableError()) { Debug.LogWarning("Operation failed but may be recoverable"); // Implement retry logic or user notification } else { Debug.LogError("Operation failed with non-recoverable error"); // Handle permanent failure } } ``` -------------------------------- ### Implement Try-Catch for Viverse SDK Operation Exception Handling Source: https://github.com/vivedeveloperrelations/viverseunitysdk/blob/master/Unity_Viverse_SDK_Developer_Guide.md Recommends using try-catch blocks for robust exception handling around SDK operations. This ensures that both ViverseResult-based errors and general programming exceptions are caught and logged appropriately. ```csharp // Use try-catch for exception handling try { ViverseResult result = await operation(); if (!result.IsSuccess) { result.LogError("Operation Name"); return; } // Process successful result } catch (Exception e) { Debug.LogError($"Exception during operation: {e.Message}"); Debug.LogException(e); } ``` -------------------------------- ### Adding a New Service Manager in Viverse Unity SDK Source: https://github.com/vivedeveloperrelations/viverseunitysdk/blob/master/Samples~/ViverseSampleScenes/Scripts/UI/README_ModularArchitecture.md This C# example demonstrates how to create a new service manager by inheriting from `ViverseManagerBase` and how to integrate it into the `InitializeManagers` method of a controller. It highlights the steps required to extend the modular architecture with new functionalities. ```csharp // 1. Create the manager public class ViverseNewServiceManager : ViverseManagerBase { public ViverseNewServiceManager(IViverseServiceContext context, VisualElement root) : base(context, root) { } // Implement abstract methods } // 2. Add to controller private ViverseNewServiceManager _newServiceManager; private void InitializeManagers() { // ... existing managers _newServiceManager = new ViverseNewServiceManager(_serviceContext, root); _newServiceManager.Initialize(); } ``` -------------------------------- ### Implement Async/Await for Viverse SDK Operations Source: https://github.com/vivedeveloperrelations/viverseunitysdk/blob/master/Unity_Viverse_SDK_Developer_Guide.md Explains that all Viverse SDK operations are asynchronous and must be awaited. It also highlights the proper use of cancellation tokens for managing long-running or interruptible operations. ```csharp // All SDK operations are async and must be awaited ViverseResult result = await _core.SomeService.SomeOperation(); // Use proper cancellation token handling ViverseResult initResult = await _core.Initialize(_hostConfig, destroyCancellationToken); ``` -------------------------------- ### Handle ViverseResult for SDK Operation Outcomes Source: https://github.com/vivedeveloperrelations/viverseunitysdk/blob/master/Unity_Viverse_SDK_Developer_Guide.md Demonstrates the ViverseResult pattern for handling SDK operation results. It shows how to check for success, access data, and perform comprehensive error handling, including logging and identifying recoverable errors. ```csharp ViverseResult result = await someOperation(); // Check success/failure if (result.IsSuccess) { T data = result.Data; // Access successful result } else { // Error handling with multiple approaches result.LogError("Operation Name"); // Comprehensive logging // Check if error is recoverable if (result.IsRecoverableError()) { Debug.LogWarning("This error may be recoverable - retry may succeed"); } // Access detailed error information Debug.LogError($"Error: {result.ErrorMessage}"); Debug.LogError($"Return Code: {result.RawResult.ReturnCode}"); Debug.LogError($"Raw Message: {result.RawResult.Message}"); } ``` -------------------------------- ### Upload Score to Viverse Leaderboard in C# Source: https://github.com/vivedeveloperrelations/viverseunitysdk/blob/master/Unity_Viverse_SDK_Developer_Guide.md This C# function illustrates how to upload a score to a specified leaderboard using the Viverse SDK. It takes an `appId` and `leaderboardName`, uploads a test score, and then processes the `LeaderboardResult` to log the total entries and individual rankings if available. Error handling is included. ```csharp private async Task TestPostScore(string leaderboardName) { string appId = AppID; // Use the same App ID throughout string testScore = "1000"; ViverseResult scoreResult = await _core.LeaderboardService.UploadScore(appId, leaderboardName, testScore); if (!scoreResult.IsSuccess) { scoreResult.LogError("Upload Score"); return false; } LeaderboardResult result = scoreResult.Data; Debug.Log($"Uploaded score. Total entries: {result.total_count}"); if (result.ranking != null) { foreach (LeaderboardEntry entry in result.ranking) { Debug.Log($"Rank {entry.rank}: {entry.name} - {entry.value}"); } } return true; } ``` -------------------------------- ### Apply Basic Styles to HTML Body and Iframe Source: https://github.com/vivedeveloperrelations/viverseunitysdk/blob/master/Editor/Templates/iframe.html This CSS snippet defines basic styling for the HTML `body` and `iframe` elements. It sets a default font, removes margin and padding from the body, and configures the iframe to take full width and a fixed height without a border. This is commonly used for embedding content cleanly within a web page. ```CSS body { font-family: Arial, sans-serif; margin: 0; padding: 0; } iframe { width: 100%; height: 600px; border: none; } ``` -------------------------------- ### Check for Existing Viverse SSO Access Token Source: https://github.com/vivedeveloperrelations/viverseunitysdk/blob/master/Unity_Viverse_SDK_Developer_Guide.md Illustrates how to check for an already existing access token using `_core.SSOService.CheckAuth()`. If a valid token is found, it's stored and returns true, indicating the user is already logged in. ```csharp private async Task AlreadyHadAccessToken() { ViverseResult tokenResult = await _core.SSOService.CheckAuth(); if (!tokenResult.IsSuccess) { tokenResult.LogError("Get Access Token"); return false; } if (tokenResult.Data?.access_token != null) { Debug.Log("Got access token immediately, already logged in"); _authKey = new AuthKey(tokenResult.Data.access_token); return true; } return false; } ``` -------------------------------- ### Address API Call Errors with Specific Return Codes Source: https://github.com/vivedeveloperrelations/viverseunitysdk/blob/master/Unity_Viverse_SDK_Developer_Guide.md Demonstrates handling errors that occur during API calls, such as uploading a leaderboard score. It uses a switch statement on ViverseSDKReturnCode to provide specific error handling for common issues like network timeouts, invalid parameters, or unauthorized access. ```csharp // Leaderboard score upload if (!scoreResult.IsSuccess) { scoreResult.LogError("Upload Score"); // Specific error handling switch (scoreResult.RawResult.ReturnCode) { case (int)ViverseSDKReturnCode.ErrorNetworkTimeout: Debug.LogWarning("Score upload timed out - may retry "); break; case (int)ViverseSDKReturnCode.ErrorInvalidParameter: Debug.LogError("Invalid score format or App ID"); break; case (int)ViverseSDKReturnCode.ErrorUnauthorized: Debug.LogError("Authentication required or expired"); break; } } ``` -------------------------------- ### Unit Testing Individual Managers in Viverse Unity SDK Source: https://github.com/vivedeveloperrelations/viverseunitysdk/blob/master/Samples~/ViverseSampleScenes/Scripts/UI/README_ModularArchitecture.md This C# unit test example demonstrates how to test a single manager in isolation within the Viverse Unity SDK. It utilizes mocking frameworks to create mock dependencies for `IViverseServiceContext` and `VisualElement`, allowing for focused testing of the manager's behavior. ```csharp [Test] public void TestConfigurationManager() { // Mock dependencies var mockContext = new Mock(); var mockRoot = new Mock(); // Test manager in isolation var configManager = new ViverseConfigurationManager(mockContext.Object, mockRoot.Object); configManager.Initialize(); // Verify behavior Assert.IsTrue(configManager.IsInitialized); } ``` -------------------------------- ### VIVE Unity SDK Authentication Requirements Source: https://github.com/vivedeveloperrelations/viverseunitysdk/blob/master/Unity_Viverse_SDK_Developer_Guide.md Details the essential requirements for authenticating with VIVE services. It specifies the need for an App ID (which serves as the Client ID for SSO initialization), an Access Token for all authenticated service calls, proper storage of the `AuthKey` object, and checking existing login state before initiating new authentication flows. It also clarifies the unification of App ID and Client ID in production environments. ```APIDOC Authentication Requirements: - App ID: Required for SSO initialization (serves as Client ID) - Access Token: Required for all authenticated service calls - Auth Key Storage: Must store `AuthKey` object for service authentication - Login State: Check existing auth before triggering new login Note: In production environments, the App ID and Client ID are unified into a single identifier. During development and testing, these may be separate values, but the App ID should be used consistently throughout your application. ``` -------------------------------- ### Tracking and Verifying Achievement States (C#) Source: https://github.com/vivedeveloperrelations/viverseunitysdk/blob/master/Unity_Viverse_SDK_Developer_Guide.md This C# code provides helper methods for interacting with the Viverse Unity SDK's achievement system. It includes `GetAchievementStates` to retrieve a user's current achievement status and `TestSequentialAchievementUnlocking` to demonstrate how to programmatically unlock and verify achievements in a sequence, handling already unlocked states and verification failures. ```csharp // Helper method to track achievement states for verification private async Task> GetAchievementStates() { var achievementStates = new Dictionary(); string appId = AppID; // Use the same App ID throughout ViverseResult result = await _core.LeaderboardService.GetUserAchievement(appId); if (result.IsSuccess && result.Data.achievements != null) { foreach (var achievement in result.Data.achievements) { achievementStates[achievement.api_name] = achievement.is_achieved; } } return achievementStates; } // Sequential testing with verification private async Task TestSequentialAchievementUnlocking() { string[] achievementIds = { "achievement1", "achievement2", "achievement3" }; Dictionary initialStates = await GetAchievementStates(); foreach (var achievementId in achievementIds) { bool wasUnlocked = await UnlockAchievement(achievementId); Dictionary currentStates = await GetAchievementStates(); bool initialState = initialStates.ContainsKey(achievementId) && initialStates[achievementId]; bool currentState = currentStates.ContainsKey(achievementId) && currentStates[achievementId]; if (wasUnlocked && !initialState && currentState) { Debug.Log($"✓ Successfully unlocked: {achievementId}"); } else if (initialState) { Debug.Log($"ℹ Already unlocked: {achievementId}"); } else { Debug.LogWarning($"⚠ Failed to verify: {achievementId}"); } initialStates = currentStates; } } ``` -------------------------------- ### Retrieve User Achievements in C# Source: https://github.com/vivedeveloperrelations/viverseunitysdk/blob/master/Unity_Viverse_SDK_Developer_Guide.md This asynchronous C# method demonstrates how to fetch a user's achievements from the Viverse Leaderboard Service. It takes an application ID, calls the `GetUserAchievement` method, handles potential errors, and logs the retrieved achievement details, including display name, API name, description, and unlock status. ```csharp private async Task GetUserAchievements() { string appId = AppID; // Use the same App ID throughout ViverseResult result = await _core.LeaderboardService.GetUserAchievement(appId); if (!result.IsSuccess) { Debug.LogError($"Failed to get user achievements: {result.ErrorMessage}"); return false; } Debug.Log($"Got {result.Data.total} achievements"); if (result.Data.achievements != null && result.Data.achievements.Length > 0) { foreach (var achievement in result.Data.achievements) { Debug.Log($"Achievement: {achievement.display_name} ({achievement.api_name})"); Debug.Log($" Description: {achievement.description}"); Debug.Log($" Unlocked: {achievement.is_achieved} (times: {achievement.achieved_times})"); } } return true; } ``` -------------------------------- ### Implement VIVE Unity SDK Login Flow Source: https://github.com/vivedeveloperrelations/viverseunitysdk/blob/master/Unity_Viverse_SDK_Developer_Guide.md This C# asynchronous method handles user login using the Viverse SSO service. It attempts to log in with `LoginWithWorlds`, checks for success, logs errors, and extracts the authentication token to store as an `AuthKey` for subsequent service calls. It includes robust error handling for recoverable errors and scenarios where no authentication token is received. ```csharp private async Task DoLogin() { Debug.Log("Initiating enhanced login with LoginWithWorlds"); ViverseResult loginResult = await _core.SSOService.LoginWithWorlds(); if (!loginResult.IsSuccess) { loginResult.LogError("Login With Worlds failed"); if (loginResult.IsRecoverableError()) { Debug.LogWarning("Login failed with recoverable error - user may try again"); } return null; } string authToken = loginResult?.Data?.access_token; if (string.IsNullOrEmpty(authToken)) { Debug.LogError("Login failed: No auth token received"); return null; } // Store authentication key for service access _authKey = new AuthKey(loginResult.Data.access_token); return loginResult.Data; } ``` -------------------------------- ### Unlock a Single Achievement in C# Source: https://github.com/vivedeveloperrelations/viverseunitysdk/blob/master/Unity_Viverse_SDK_Developer_Guide.md This asynchronous C# method shows how to unlock a specific achievement for a user using the Viverse Unity SDK. It constructs an `Achievement` object with the given ID, submits it to the `UploadUserAchievement` service, and processes the success or failure of the operation, logging detailed results for both successful and failed unlocks. ```csharp private async Task UnlockAchievement(string achievementId) { string appId = AppID; // Use the same App ID throughout // Create achievement object to unlock var achievements = new List { new Achievement { api_name = achievementId, unlock = true } }; // Submit unlock request ViverseResult result = await _core.LeaderboardService.UploadUserAchievement(appId, achievements); if (!result.IsSuccess) { Debug.LogError($"Failed to upload achievement: {result.ErrorMessage}"); return false; } // Process results int successCount = result.Data?.success?.total ?? 0; int failureCount = result.Data?.failure?.total ?? 0; Debug.Log($"Achievement upload - Success: {successCount}, Failures: {failureCount}"); // Log successful achievements if (successCount > 0 && result.Data.success.achievements != null) { foreach (var achievement in result.Data.success.achievements) { Debug.Log($"Successfully unlocked: {achievement.api_name} at {achievement.time_stamp}"); } } // Log failed achievements if (failureCount > 0 && result.Data.failure.achievements != null) { foreach (var achievement in result.Data.failure.achievements) { Debug.LogWarning($"Failed to unlock: {achievement.api_name}, Code: {achievement.code}, Message: {achievement.message}"); } } return successCount > 0; } ``` -------------------------------- ### Define Leaderboard Data Structures in C# Source: https://github.com/vivedeveloperrelations/viverseunitysdk/blob/master/Unity_Viverse_SDK_Developer_Guide.md This snippet defines the C# classes and enums used to configure and represent leaderboard data within the Viverse Unity SDK. It includes structures for leaderboard configuration (name, range, region, time), results (total count, entries), and individual entries (rank, name, value), along with enums for region and time range. ```csharp public class LeaderboardConfig { public string Name; // Leaderboard identifier public int RangeStart; // Starting position (0-based) public int RangeEnd; // Ending position public LeaderboardRegion Region; // Global, Local, etc. public LeaderboardTimeRange TimeRange; // Alltime, Daily, Weekly, Monthly public bool AroundUser; // Center results around current user } public class LeaderboardResult { public int total_count; // Total number of entries public LeaderboardEntry[] ranking; // Array of leaderboard entries } public class LeaderboardEntry { public int rank; // Player's rank position public string name; // Player display name public string value; // Player's score value } public enum LeaderboardRegion { Global, Local } public enum LeaderboardTimeRange { Alltime, Daily, Weekly, Monthly } ``` -------------------------------- ### Viverse Unity SDK Achievement Data Structures (C#) Source: https://github.com/vivedeveloperrelations/viverseunitysdk/blob/master/Unity_Viverse_SDK_Developer_Guide.md This C# code defines the essential data structures used within the Viverse Unity SDK for managing achievements. It includes classes like `Achievement`, `UserAchievementResult`, `UserAchievementInfo`, `AchievementUploadResult`, `AchievementResultSection`, and `AchievementProcessedInfo`, detailing the properties for achievement identification, status, and processing results. ```csharp public class Achievement { public string api_name; // Achievement identifier public bool unlock; // True to unlock, false to reset (if supported) } public class UserAchievementResult { public int total; // Total number of achievements public UserAchievementInfo[] achievements; // Array of user's achievements } public class UserAchievementInfo { public string api_name; // Achievement identifier public string display_name; // Human-readable name public string description; // Achievement description public bool is_achieved; // Whether user has unlocked this public int achieved_times; // Number of times achieved (for repeatable achievements) } public class AchievementUploadResult { public AchievementResultSection success; // Successfully processed achievements public AchievementResultSection failure; // Failed achievement operations } public class AchievementResultSection { public int total; // Count of achievements in this section public AchievementProcessedInfo[] achievements; // Details for each achievement } public class AchievementProcessedInfo { public string api_name; // Achievement identifier public long time_stamp; // Processing timestamp (for successes) public int code; // Error code (for failures) public string message; // Error message (for failures) } ``` -------------------------------- ### Define Avatar and User Profile Data Structures in C# Source: https://github.com/vivedeveloperrelations/viverseunitysdk/blob/master/Unity_Viverse_SDK_Developer_Guide.md This C# code defines the `Avatar` and `UserProfile` classes, which represent the data structures for user avatars and profiles within the Viverse Unity SDK. The `Avatar` class includes properties like ID, VRM URL, and timestamps, while `UserProfile` links to an active avatar and user display name. ```csharp public class Avatar { public string id; // Avatar identifier public bool isPrivate; // Private (user-owned) vs public avatar public string vrmUrl; // URL to VRM model file public string headIconUrl; // URL to avatar head icon public string snapshot; // URL to avatar preview image public long createTime; // Creation timestamp (milliseconds) public long updateTime; // Last update timestamp (milliseconds) } public class UserProfile { public string name; // User display name public Avatar activeAvatar; // Currently selected avatar (null if none) } ``` -------------------------------- ### Retrieve User Profile with VIVE Unity SDK Avatar Service Source: https://github.com/vivedeveloperrelations/viverseunitysdk/blob/master/Unity_Viverse_SDK_Developer_Guide.md This C# asynchronous method demonstrates how to fetch the current user's profile information using `_core.AvatarService.GetProfile()`. It parses and logs various details from the retrieved `UserProfile` object, such as the user's name and active avatar information, including its ID, privacy status, and VRM URL. The method also includes error logging for failed profile retrieval operations. ```csharp // Get user profile information private async Task TestGetProfile() { ViverseResult profile = await _core.AvatarService.GetProfile(); if (profile.IsSuccess && profile.Data != null) { Debug.Log($"User Name: {profile.Data.name}"); Debug.Log($"Active Avatar: {(profile.Data.activeAvatar != null ? "Present" : "null")}"); if (profile.Data.activeAvatar != null) { Debug.Log($"Active Avatar ID: {profile.Data.activeAvatar.id}"); Debug.Log($"Is Private: {profile.Data.activeAvatar.isPrivate}"); Debug.Log($"VRM URL: {profile.Data.activeAvatar.vrmUrl}"); } } else { profile.LogError("GetProfile"); } } ``` -------------------------------- ### Manage User and Public Avatar Lists with VIVE Unity SDK Source: https://github.com/vivedeveloperrelations/viverseunitysdk/blob/master/Unity_Viverse_SDK_Developer_Guide.md This C# code demonstrates various methods for managing avatar lists within the VIVE Unity SDK's Avatar Service. It shows how to retrieve the current user's avatar collection using `GetAvatarList`, fetch a list of public avatars using `GetPublicAvatarList`, and retrieve a specific public avatar by its ID using `GetPublicAvatarByID`. Each operation includes success checks and logging of the results, such as the count of avatars found or details of a specific avatar. ```csharp // Get user's avatar collection ViverseResult avatarListResult = await _core.AvatarService.GetAvatarList(); if (avatarListResult.IsSuccess && avatarListResult.Data?.avatars != null) { List avatarList = new List(avatarListResult.Data.avatars); Debug.Log($"Found {avatarList.Count} avatars"); foreach (var avatar in avatarList) { Debug.Log($"Avatar ID: {avatar.id}, Private: {avatar.isPrivate}"); } } // Get public avatars (no authentication required) ViverseResult publicAvatarsResult = await _core.AvatarService.GetPublicAvatarList(); if (publicAvatarsResult.IsSuccess && publicAvatarsResult.Data?.avatars != null) { Debug.Log($"Found {publicAvatarsResult.Data.avatars.Length} public avatars"); } // Get specific public avatar by ID ViverseResult avatarByIdResult = await _core.AvatarService.GetPublicAvatarByID("avatar-id"); if (avatarByIdResult.IsSuccess && avatarByIdResult.Data != null) { Debug.Log($"Retrieved avatar: {avatarByIdResult.Data.id}"); } ``` -------------------------------- ### Retrieve Active Avatar with VIVE Unity SDK Avatar Service Source: https://github.com/vivedeveloperrelations/viverseunitysdk/blob/master/Unity_Viverse_SDK_Developer_Guide.md This C# asynchronous method retrieves the user's currently active avatar using `_core.AvatarService.GetActiveAvatar()`. It processes the `Avatar` object, logging various details such as the avatar's ID, privacy status, VRM URL, head icon URL, snapshot URL, and creation/update timestamps. The method also handles cases where no active avatar is set and logs errors for failed API calls. ```csharp // Get user's currently active avatar private async Task TestGetActiveAvatar() { ViverseResult activeAvatarResult = await _core.AvatarService.GetActiveAvatar(); if (activeAvatarResult.IsSuccess) { if (activeAvatarResult.Data != null) { var avatar = activeAvatarResult.Data; Debug.Log($"Avatar ID: {avatar.id}"); Debug.Log($"Is Private: {avatar.isPrivate}"); Debug.Log($"VRM URL: {avatar.vrmUrl}"); Debug.Log($"Head Icon URL: {avatar.headIconUrl}"); Debug.Log($"Snapshot URL: {avatar.snapshot}"); Debug.Log($"Create Time: {avatar.createTime}"); Debug.Log($"Update Time: {avatar.updateTime}"); } else { Debug.Log("No active avatar set"); } } else { activeAvatarResult.LogError("GetActiveAvatar"); } } ``` -------------------------------- ### Initialize Viverse SDK and Service Clients in JavaScript Source: https://github.com/vivedeveloperrelations/viverseunitysdk/blob/master/Samples~/ViverseHTMLPage/ViverseAPIIntegrationPage.html Provides functions to initialize the main Viverse client, as well as specific clients for Avatar and Game Dashboard services. These functions check for the presence of the SDK and the configured client ID, then instantiate the respective clients using provided configuration or authentication tokens. ```javascript let avatarClient = null; let gameDashboardClient = null; function initializeViverseClient() { if (!VIVERSE_CONFIG.clientId) { alert('Please configure your Client ID first'); return false; } if (globalThis.viverse) { globalThis.viverseClient = new globalThis.viverse.client(VIVERSE_CONFIG); console.log('Viverse client initialized'); return true; } else { console.error('Viverse SDK is not initialized'); return false; } } async function initializeAvatarClient(token) { if (globalThis.viverse) { avatarClient = new globalThis.viverse.avatar({ baseURL: 'https://sdk-api.viverse.com/', token: token }); console.log('Avatar client initialized'); return true; } return false; } async function initializeGameDashboardClient(token) { if (globalThis.viverse) { gameDashboardClient = new globalThis.viverse.gameDashboard({ baseURL: 'https://www.viveport.com/', communityBaseURL: 'https://www.viverse.com/', token: token }); console.log('Game Dashboard client initialized'); return true; } return false; } ``` -------------------------------- ### Initiate Viverse User Login Flow in JavaScript Source: https://github.com/vivedeveloperrelations/viverseunitysdk/blob/master/Samples~/ViverseHTMLPage/ViverseAPIIntegrationPage.html This asynchronous function orchestrates the user login process with Viverse. It first ensures the SDK is loaded and the main Viverse client is initialized, then constructs a redirect URL and initiates the `loginWithWorlds` flow, handling potential errors during the process. ```javascript async function startLogin() { try { if (!VIVERSE_CONFIG.clientId) { alert('Please configure your Client ID first'); return; } if (!globalThis.viverse) { await loadViverseSDK(); } if (!initializeViverseClient()) { throw new Error('Failed to initialize Viverse client'); } const protocol = window.location.protocol; const hostname = window.location.hostname; const port = window.location.port ? `:${window.location.port}` : ''; const pathname = window.location.pathname; const redirectUrl = `${protocol}//${hostname}${port}${pathname}`; // v1.2.9 uses loginWithWorlds instead of loginWithRedirect await globalThis.viverseClient.loginWithWorlds(); } catch (error) { document.getElementById('loginResult').textContent = `Error during login process: ${error.message}`; } } ``` -------------------------------- ### Handle Viverse SDK Authentication Redirect and Initialization Source: https://github.com/vivedeveloperrelations/viverseunitysdk/blob/master/Samples~/ViverseHTMLPage/ViverseAPIIntegrationPage.html This code block manages the Viverse SDK authentication flow, specifically handling the redirect callback after a user logs in. It checks for authentication parameters in the URL, initializes the Viverse client if not already done, verifies authentication status, retrieves access tokens, and then initializes other related clients (Avatar, Game Dashboard) using the obtained token. It also includes an event listener to trigger this logic upon page load if a redirect code is present. ```javascript async function handleRedirectCallback() { if (window.location.search.includes('code=') && window.location.search.includes('state=')) { if (!globalThis.viverseClient && globalThis.viverse) { initializeViverseClient(); } if (globalThis.viverseClient) { try { // v1.2.9 - check authentication status instead of handling callback const result = await globalThis.viverseClient.checkAuth(); document.getElementById('loginResult').textContent = JSON.stringify(result, null, 2); const tokenResponse = await globalThis.viverseClient.getToken({ detailedResponse: true }); document.getElementById('tokenResult').textContent = JSON.stringify(tokenResponse, null, 2); if (tokenResponse.access_token) { await initializeAvatarClient(tokenResponse.access_token); await initializeGameDashboardClient(tokenResponse.access_token); } } catch (error) { console.error('Failed to handle redirect callback:', error); document.getElementById('loginResult').textContent = `Error handling callback: ${error.message}`; } } } } window.addEventListener('load', async () => { try { if (window.location.search.includes('code=')) { await loadViverseSDK(); handleRedirectCallback(); } } catch (error) { document.getElementById('loginResult').textContent = `Error during initialization: ${error.message}`; } }); ```