diff --git a/.github/workflows/run-tests-and-package.yml b/.github/workflows/run-tests-and-package.yml index 9eb0f9b7..7de57eea 100644 --- a/.github/workflows/run-tests-and-package.yml +++ b/.github/workflows/run-tests-and-package.yml @@ -24,6 +24,8 @@ jobs: runs-on: [ubuntu-latest] needs: [] timeout-minutes: 10 + # Skip push events on ci/* branches (let pull_request handle them to avoid duplicates) + if: github.event_name != 'push' || !startsWith(github.ref, 'refs/heads/ci/') env: LL_USE_STAGE: false strategy: @@ -95,6 +97,21 @@ jobs: restore-keys: | ${{ matrix.targetPlatform }}-Library-unity-sdk-smoketest-project Library- + - name: Setup Unity environment + run: | + # Install required system packages + sudo apt-get update + sudo apt-get install -y xvfb libx11-6 libxcursor1 libxrandr2 libasound2-dev + # Create Unity cache directory with proper permissions + sudo mkdir -p /root/.cache/unity3d + sudo chmod -R 777 /root/.cache/unity3d + # Start virtual framebuffer for headless Unity + export DISPLAY=:99 + echo "DISPLAY=:99" >> $GITHUB_ENV + Xvfb :99 -screen 0 1024x768x24 > /dev/null 2>&1 & + sleep 3 + # Verify Xvfb is running + ps aux | grep Xvfb - name: Run Smoke Tests ${{ matrix.unityVersion }}-${{ matrix.testMode }} id: editor-smoke-tests-gameci uses: game-ci/unity-test-runner@v4.3.1 @@ -114,7 +131,6 @@ jobs: name: Test SDK in StandaloneLinux64 build runs-on: [ubuntu-latest] needs: [editor-smoke-test] - if: false timeout-minutes: 10 env: LL_USE_STAGE: false @@ -149,6 +165,21 @@ jobs: tee /etc/apt/sources.list.d/docker.list > /dev/null apt-get update apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin + - name: Setup Unity environment for all builds + run: | + # Install required system packages + sudo apt-get update + sudo apt-get install -y xvfb libx11-6 libxcursor1 libxrandr2 libasound2-dev + # Create Unity cache directory with proper permissions + sudo mkdir -p /root/.cache/unity3d + sudo chmod -R 777 /root/.cache/unity3d + # Start virtual framebuffer for headless Unity + export DISPLAY=:99 + echo "DISPLAY=:99" >> $GITHUB_ENV + Xvfb :99 -screen 0 1024x768x24 > /dev/null 2>&1 & + sleep 3 + # Verify Xvfb is running + ps aux | grep Xvfb - name: Select stage keys if: ${{ env.LL_USE_STAGE == 'true' }} run: | @@ -227,7 +258,7 @@ jobs: runs-on: [ubuntu-latest] needs: [editor-smoke-test] timeout-minutes: 20 - if: false && (startsWith(github.ref, 'refs/pull') && endsWith(github.base_ref, 'main')) || startsWith(github.ref, 'refs/tags/v') || (startsWith(github.ref, 'refs/heads') && endsWith(github.ref, 'main')) + if: (startsWith(github.ref, 'refs/pull') && endsWith(github.base_ref, 'main')) || startsWith(github.ref, 'refs/tags/v') || (startsWith(github.ref, 'refs/heads') && endsWith(github.ref, 'main')) || (startsWith(github.ref, 'refs/heads/ci')) env: LL_USE_STAGE: false strategy: @@ -261,6 +292,21 @@ jobs: tee /etc/apt/sources.list.d/docker.list > /dev/null apt-get update apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin + - name: Setup Unity environment for WebGL build + run: | + # Install required system packages + sudo apt-get update + sudo apt-get install -y xvfb libx11-6 libxcursor1 libxrandr2 libasound2-dev + # Create Unity cache directory with proper permissions + sudo mkdir -p /root/.cache/unity3d + sudo chmod -R 777 /root/.cache/unity3d + # Start virtual framebuffer for headless Unity + export DISPLAY=:99 + echo "DISPLAY=:99" >> $GITHUB_ENV + Xvfb :99 -screen 0 1024x768x24 > /dev/null 2>&1 & + sleep 3 + # Verify Xvfb is running + ps aux | grep Xvfb - name: Select stage keys if: ${{ env.LL_USE_STAGE == 'true' }} run: | @@ -505,6 +551,22 @@ jobs: path: tests~/Library key: Library-${{ matrix.unityVersion }}-${{ ENV.JSON_LIBRARY }} restore-keys: Library- + - name: Setup Unity environment for integration tests + if: ${{ vars.ENABLE_INTEGRATION_TESTS == 'true' }} + run: | + # Install required system packages + sudo apt-get update + sudo apt-get install -y xvfb libx11-6 libxcursor1 libxrandr2 libasound2-dev + # Create Unity cache directory with proper permissions + sudo mkdir -p /root/.cache/unity3d + sudo chmod -R 777 /root/.cache/unity3d + # Start virtual framebuffer for headless Unity + export DISPLAY=:99 + echo "DISPLAY=:99" >> $GITHUB_ENV + Xvfb :99 -screen 0 1024x768x24 > /dev/null 2>&1 & + sleep 3 + # Verify Xvfb is running + ps aux | grep Xvfb - name: Run tests in ${{ matrix.unityVersion }} towards ${{ ENV.TARGET_ENVIRONMENT }} environment with json library ${{ ENV.JSON_LIBRARY }} uses: game-ci/unity-test-runner@v4.3.1 if: ${{ vars.ENABLE_INTEGRATION_TESTS == 'true' }} @@ -598,6 +660,21 @@ jobs: restore-keys: | ${{ matrix.targetPlatform }}-Library-unity-sdk-sample-tester Library- + - name: Setup Unity environment for sample tests + run: | + # Install required system packages + sudo apt-get update + sudo apt-get install -y xvfb libx11-6 libxcursor1 libxrandr2 libasound2-dev + # Create Unity cache directory with proper permissions + sudo mkdir -p /root/.cache/unity3d + sudo chmod -R 777 /root/.cache/unity3d + # Start virtual framebuffer for headless Unity + export DISPLAY=:99 + echo "DISPLAY=:99" >> $GITHUB_ENV + Xvfb :99 -screen 0 1024x768x24 > /dev/null 2>&1 & + sleep 3 + # Verify Xvfb is running + ps aux | grep Xvfb - name: Compile and run all sample scenes ${{ matrix.unityVersion }} id: test-samples uses: game-ci/unity-test-runner@v4.3.1 @@ -615,7 +692,7 @@ jobs: validate-sdk: name: Validate SDK runs-on: [ubuntu-latest] - if: ${{ true == false }} + if: (startsWith(github.ref, 'refs/pull') && endsWith(github.base_ref, 'main')) || startsWith(github.ref, 'refs/tags/v') || (startsWith(github.ref, 'refs/heads') && endsWith(github.ref, 'main')) || (startsWith(github.ref, 'refs/heads/ci')) needs: [editor-smoke-test] timeout-minutes: 8 env: @@ -736,6 +813,21 @@ jobs: restore-keys: | ${{ matrix.targetPlatform }}-Library-unity-sdk-packager Library- + - name: Setup Unity environment for packaging + run: | + # Install required system packages + sudo apt-get update + sudo apt-get install -y xvfb libx11-6 libxcursor1 libxrandr2 libasound2-dev + # Create Unity cache directory with proper permissions + sudo mkdir -p /root/.cache/unity3d + sudo chmod -R 777 /root/.cache/unity3d + # Start virtual framebuffer for headless Unity + export DISPLAY=:99 + echo "DISPLAY=:99" >> $GITHUB_ENV + Xvfb :99 -screen 0 1024x768x24 > /dev/null 2>&1 & + sleep 3 + # Verify Xvfb is running + ps aux | grep Xvfb - name: Package SDK for ${{ matrix.unityVersion }} id: package-sdk-gameci uses: game-ci/unity-test-runner@v4.3.1 diff --git a/Runtime/Client/EndPointClass.cs b/Runtime/Client/EndPointClass.cs index 0aab85fc..f266daf0 100644 --- a/Runtime/Client/EndPointClass.cs +++ b/Runtime/Client/EndPointClass.cs @@ -27,22 +27,50 @@ public EndPointClass(string endPoint, LootLockerHTTPMethod httpMethod, LootLocke public string WithPathParameter(object arg0) { - return string.Format(endPoint, WebUtility.UrlEncode(arg0.ToString())); + try { + return string.Format(endPoint, WebUtility.UrlEncode(arg0?.ToString() ?? "null")); + } + catch (FormatException e) + { + LootLockerLogger.Log($"Error formatting endpoint \"{endPoint}\" with path parameter \"{WebUtility.UrlEncode(arg0?.ToString() ?? "null")}\": {e}", LootLockerLogger.LogLevel.Error); + return endPoint; + } } public string WithPathParameters(object arg0, object arg1) { - return string.Format(endPoint, WebUtility.UrlEncode(arg0.ToString()), WebUtility.UrlEncode(arg1.ToString())); + try { + return string.Format(endPoint, WebUtility.UrlEncode(arg0?.ToString() ?? "null"), WebUtility.UrlEncode(arg1?.ToString() ?? "null")); + } + catch (FormatException e) + { + LootLockerLogger.Log($"Error formatting endpoint \"{endPoint}\" with path parameters \"{WebUtility.UrlEncode(arg0?.ToString() ?? "null")}\", \"{WebUtility.UrlEncode(arg1?.ToString() ?? "null")}\": {e}", LootLockerLogger.LogLevel.Error); + return endPoint; + } } public string WithPathParameters(object arg0, object arg1, object arg2) { - return string.Format(endPoint, WebUtility.UrlEncode(arg0.ToString()), WebUtility.UrlEncode(arg1.ToString()), WebUtility.UrlEncode(arg2.ToString())); + try { + return string.Format(endPoint, WebUtility.UrlEncode(arg0?.ToString() ?? "null"), WebUtility.UrlEncode(arg1?.ToString() ?? "null"), WebUtility.UrlEncode(arg2?.ToString() ?? "null")); + } + catch (FormatException e) + { + LootLockerLogger.Log($"Error formatting endpoint \"{endPoint}\" with path parameters \"{WebUtility.UrlEncode(arg0?.ToString() ?? "null")}\", \"{WebUtility.UrlEncode(arg1?.ToString() ?? "null")}\", \"{WebUtility.UrlEncode(arg2?.ToString() ?? "null")}\": {e}", LootLockerLogger.LogLevel.Error); + return endPoint; + } } public string WithPathParameters(object arg0, object arg1, object arg2, object arg3) { - return string.Format(endPoint, WebUtility.UrlEncode(arg0.ToString()), WebUtility.UrlEncode(arg1.ToString()), WebUtility.UrlEncode(arg2.ToString()), WebUtility.UrlEncode(arg3.ToString())); + try { + return string.Format(endPoint, WebUtility.UrlEncode(arg0?.ToString() ?? "null"), WebUtility.UrlEncode(arg1?.ToString() ?? "null"), WebUtility.UrlEncode(arg2?.ToString() ?? "null"), WebUtility.UrlEncode(arg3?.ToString() ?? "null")); + } + catch (FormatException e) + { + LootLockerLogger.Log($"Error formatting endpoint \"{endPoint}\" with path parameters \"{WebUtility.UrlEncode(arg0?.ToString() ?? "null")}\", \"{WebUtility.UrlEncode(arg1?.ToString() ?? "null")}\", \"{WebUtility.UrlEncode(arg2?.ToString() ?? "null")}\", \"{WebUtility.UrlEncode(arg3?.ToString() ?? "null")}\": {e}", LootLockerLogger.LogLevel.Error); + return endPoint; + } } } } diff --git a/Runtime/Client/LootLockerEndPoints.cs b/Runtime/Client/LootLockerEndPoints.cs index 1dcea118..08b23bd1 100644 --- a/Runtime/Client/LootLockerEndPoints.cs +++ b/Runtime/Client/LootLockerEndPoints.cs @@ -274,7 +274,7 @@ public class LootLockerEndPoints [Header("Catalogs")] public static EndPointClass listCatalogs = new EndPointClass("catalogs", LootLockerHTTPMethod.GET); public static EndPointClass deprecatedListCatalogItemsByKey = new EndPointClass("catalog/key/{0}/prices", LootLockerHTTPMethod.GET); - public static EndPointClass listCatalogItemsByKey = new EndPointClass("catalogs/inspired-ibex/v1/catalog/key/{key}/list", LootLockerHTTPMethod.GET); + public static EndPointClass listCatalogItemsByKey = new EndPointClass("catalogs/inspired-ibex/v1/catalog/key/{0}/list", LootLockerHTTPMethod.GET); // Misc [Header("Misc")] diff --git a/Runtime/Client/LootLockerHTTPClient.cs b/Runtime/Client/LootLockerHTTPClient.cs index d00519ab..5157db3f 100644 --- a/Runtime/Client/LootLockerHTTPClient.cs +++ b/Runtime/Client/LootLockerHTTPClient.cs @@ -566,8 +566,11 @@ private void CallListenersAndMarkDone(LootLockerHTTPExecutionQueueItem execution executionItem.Done = true; response.requestContext = new LootLockerRequestContext(executionItem.RequestData.ForPlayerWithUlid, executionItem.RequestData.RequestStartTime); executionItem.Response = response; + if (!CompletedRequestIDs.Contains(executionItem.RequestData.RequestId)) + { + CompletedRequestIDs.Add(executionItem.RequestData.RequestId); + } executionItem.RequestData.CallListenersWithResult(response); - CompletedRequestIDs.Add(executionItem.RequestData.RequestId); } private IEnumerator RefreshSession(string refreshForPlayerUlid, string forExecutionItemId, Action onSessionRefreshedCallback) @@ -647,8 +650,6 @@ private IEnumerator RefreshSession(string refreshForPlayerUlid, string forExecut }, refreshForPlayerUlid); } break; - case LL_AuthPlatforms.PlayStationNetwork: - case LL_AuthPlatforms.XboxOne: case LL_AuthPlatforms.AmazonLuna: { LootLockerSDKManager.StartAmazonLunaSession(playerData.Identifier, (response) => @@ -658,6 +659,8 @@ private IEnumerator RefreshSession(string refreshForPlayerUlid, string forExecut }, playerData.SessionOptionals); } break; + case LL_AuthPlatforms.PlayStationNetwork: + case LL_AuthPlatforms.XboxOne: case LL_AuthPlatforms.NintendoSwitch: case LL_AuthPlatforms.Steam: { diff --git a/Runtime/Client/LootLockerHttpRequestData.cs b/Runtime/Client/LootLockerHttpRequestData.cs index 51d4976b..4bc2cb99 100644 --- a/Runtime/Client/LootLockerHttpRequestData.cs +++ b/Runtime/Client/LootLockerHttpRequestData.cs @@ -87,7 +87,14 @@ public void CallListenersWithResult(LootLockerResponse response) { foreach(var listener in Listeners) { - listener?.Invoke(response); + try + { + listener?.Invoke(response); + } + catch (Exception e) + { + LootLockerLogger.Log($"Exception thrown in HTTP request listener for request id {RequestId}. Exception was: {e}.", LootLockerLogger.LogLevel.Error); + } } HaveListenersBeenInvoked = true; } diff --git a/Runtime/Client/LootLockerStateData.cs b/Runtime/Client/LootLockerStateData.cs index 6548b789..e8116815 100644 --- a/Runtime/Client/LootLockerStateData.cs +++ b/Runtime/Client/LootLockerStateData.cs @@ -195,6 +195,9 @@ public static LootLockerPlayerData GetStateForPlayerOrDefaultStateOrEmpty(string } string playerULIDToGetDataFor = string.IsNullOrEmpty(playerULID) ? ActiveMetaData.DefaultPlayer : playerULID; + // Make this player the default for requests if there is no default yet or if the current default is not currently active + bool shouldBeMadeDefault = ActivePlayerData.Count == 0 && !playerULIDToGetDataFor.Equals(ActiveMetaData.DefaultPlayer, StringComparison.OrdinalIgnoreCase); + if (ActivePlayerData.TryGetValue(playerULIDToGetDataFor, out var data)) { return data; @@ -204,6 +207,10 @@ public static LootLockerPlayerData GetStateForPlayerOrDefaultStateOrEmpty(string { if (ActivePlayerData.TryGetValue(playerULIDToGetDataFor, out var data2)) { + if (shouldBeMadeDefault) + { + SetDefaultPlayerULID(data2.ULID); + } return data2; } } @@ -264,7 +271,7 @@ public static bool SetPlayerData(LootLockerPlayerData updatedPlayerData) { ActiveMetaData.WhiteLabelEmailToPlayerUlidMap[updatedPlayerData.WhiteLabelEmail] = updatedPlayerData.ULID; } - if (string.IsNullOrEmpty(ActiveMetaData.DefaultPlayer)) + if (string.IsNullOrEmpty(ActiveMetaData.DefaultPlayer) || !ActivePlayerData.ContainsKey(ActiveMetaData.DefaultPlayer)) { SetDefaultPlayerULID(updatedPlayerData.ULID); } diff --git a/Runtime/Game/LootLockerSDKManager.cs b/Runtime/Game/LootLockerSDKManager.cs index 634e5e95..3ac2a649 100644 --- a/Runtime/Game/LootLockerSDKManager.cs +++ b/Runtime/Game/LootLockerSDKManager.cs @@ -3242,6 +3242,40 @@ public static void LookupPlayerNamesByXboxIds(string[] xboxIds, Action + /// Get player names and important ids of a set of players from their last active platform by Epic Games ID's + /// + /// A list of multiple player Epic Games ID's + /// onComplete Action for handling the response of type PlayerNameLookupResponse + /// Optional : Execute the request for the specified player. If not supplied, the default player will be used. + public static void LookupPlayerNamesByEpicGamesIds(string[] epicGamesIds, Action onComplete, string forPlayerWithUlid = null) + { + if (!CheckInitialized(false, forPlayerWithUlid)) + { + onComplete?.Invoke(LootLockerResponseFactory.SDKNotInitializedError(forPlayerWithUlid)); + return; + } + + LootLockerAPIManager.LookupPlayerNames(forPlayerWithUlid, "epic_games_id", epicGamesIds, onComplete); + } + + /// + /// Get player names and important ids of a set of players from their last active platform by Google Play Games ID's + /// + /// A list of multiple player Google Play Games ID's + /// onComplete Action for handling the response of type PlayerNameLookupResponse + /// Optional : Execute the request for the specified player. If not supplied, the default player will be used. + public static void LookupPlayerNamesByGooglePlayGamesIds(string[] googlePlayGamesIds, Action onComplete, string forPlayerWithUlid = null) + { + if (!CheckInitialized(false, forPlayerWithUlid)) + { + onComplete?.Invoke(LootLockerResponseFactory.SDKNotInitializedError(forPlayerWithUlid)); + return; + } + + LootLockerAPIManager.LookupPlayerNames(forPlayerWithUlid, "google_play_games_id", googlePlayGamesIds, onComplete); + } + /// /// Mark the logged in player for deletion. After 30 days the player will be deleted from the system. /// @@ -4025,9 +4059,10 @@ public static void GetHeroInventory(int heroID, Action /// List the loadout of the specified hero that the current player owns /// + /// HeroID Id of the hero /// onComplete Action for handling the response of type LootLockerHeroLoadoutResponse /// Optional : Execute the request for the specified player. If not supplied, the default player will be used. - public static void GetHeroLoadout(Action onComplete, string forPlayerWithUlid = null) + public static void GetHeroLoadout(int HeroID, Action onComplete, string forPlayerWithUlid = null) { if (!CheckInitialized(false, forPlayerWithUlid)) { @@ -4035,7 +4070,7 @@ public static void GetHeroLoadout(Action onComple return; } - LootLockerAPIManager.GetHeroLoadout(forPlayerWithUlid, onComplete); + LootLockerAPIManager.GetHeroLoadout(forPlayerWithUlid, HeroID, onComplete); } /// @@ -4076,7 +4111,7 @@ public static void AddAssetToHeroLoadout(int heroID, int assetInstanceID, Action data.hero_id = heroID; - LootLockerAPIManager.AddAssetToHeroLoadout(forPlayerWithUlid, data, onComplete); + LootLockerAPIManager.AddAssetToHeroLoadout(forPlayerWithUlid, heroID, data, onComplete); } /// @@ -4102,7 +4137,7 @@ public static void AddAssetVariationToHeroLoadout(int heroID, int assetID, int a data.asset_id = assetID; data.asset_variation_id = assetInstanceID; - LootLockerAPIManager.AddAssetVariationToHeroLoadout(forPlayerWithUlid, data, onComplete); + LootLockerAPIManager.AddAssetVariationToHeroLoadout(forPlayerWithUlid, heroID, data, onComplete); } /// @@ -4113,7 +4148,7 @@ public static void AddAssetVariationToHeroLoadout(int heroID, int assetID, int a /// Id of the hero /// onComplete Action for handling the response of type LootLockerHeroLoadoutResponse /// Optional : Execute the request for the specified player. If not supplied, the default player will be used. - public static void RemoveAssetFromHeroLoadout(string assetID, string heroID, Action onComplete, string forPlayerWithUlid = null) + public static void RemoveAssetFromHeroLoadout(int assetID, int heroID, Action onComplete, string forPlayerWithUlid = null) { if (!CheckInitialized(false, forPlayerWithUlid)) { @@ -4121,12 +4156,7 @@ public static void RemoveAssetFromHeroLoadout(string assetID, string heroID, Act return; } - LootLockerGetRequest lootLockerGetRequest = new LootLockerGetRequest(); - - lootLockerGetRequest.getRequests.Add(assetID); - lootLockerGetRequest.getRequests.Add(heroID); - - LootLockerAPIManager.RemoveAssetFromHeroLoadout(forPlayerWithUlid, lootLockerGetRequest, onComplete); + LootLockerAPIManager.RemoveAssetFromHeroLoadout(forPlayerWithUlid, heroID, assetID, onComplete); } #endregion diff --git a/Runtime/Game/Requests/HeroRequest.cs b/Runtime/Game/Requests/HeroRequest.cs index 7199fbb4..cdd9c3cf 100644 --- a/Runtime/Game/Requests/HeroRequest.cs +++ b/Runtime/Game/Requests/HeroRequest.cs @@ -116,10 +116,7 @@ public static void ListPlayerHeroes(string forPlayerWithUlid, Action onComplete) { EndPointClass endPoint = LootLockerEndPoints.listOtherPlayersHeroesBySteamID64; - - string getVariable = endPoint.endPoint; - - LootLockerServerRequest.CallAPI(forPlayerWithUlid, getVariable, endPoint.httpMethod, SteamID64.ToString(), (serverResponse) => { LootLockerResponse.Deserialize(onComplete, serverResponse); }); + LootLockerServerRequest.CallAPI(forPlayerWithUlid, endPoint.WithPathParameter(SteamID64.ToString()), endPoint.httpMethod, SteamID64.ToString(), (serverResponse) => { LootLockerResponse.Deserialize(onComplete, serverResponse); }); } public static void CreateHero(LootLockerCreateHeroRequest data, @@ -154,19 +151,14 @@ public static void CreateHeroWithVariation(string forPlayerWithUlid, LootLockerC public static void GetHero(string forPlayerWithUlid, int HeroID, Action onComplete) { EndPointClass endPoint = LootLockerEndPoints.getHero; - - string getVariable = endPoint.endPoint; - - LootLockerServerRequest.CallAPI(forPlayerWithUlid, getVariable, endPoint.httpMethod, HeroID.ToString(), (serverResponse) => { LootLockerResponse.Deserialize(onComplete, serverResponse); }); + LootLockerServerRequest.CallAPI(forPlayerWithUlid, endPoint.WithPathParameter(HeroID.ToString()), endPoint.httpMethod, HeroID.ToString(), (serverResponse) => { LootLockerResponse.Deserialize(onComplete, serverResponse); }); } public static void GetOtherPlayersDefaultHeroBySteamID64(string forPlayerWithUlid, int steamID64, Action onComplete) { EndPointClass endPoint = LootLockerEndPoints.getOtherPlayersDefaultHeroBySteamID64; - string getVariable = endPoint.endPoint; - - LootLockerServerRequest.CallAPI(forPlayerWithUlid, getVariable, endPoint.httpMethod, steamID64.ToString(), (serverResponse) => { LootLockerResponse.Deserialize(onComplete, serverResponse); }); + LootLockerServerRequest.CallAPI(forPlayerWithUlid, endPoint.WithPathParameter(steamID64.ToString()), endPoint.httpMethod, steamID64.ToString(), (serverResponse) => { LootLockerResponse.Deserialize(onComplete, serverResponse); }); } public static void UpdateHero(string forPlayerWithUlid, LootLockerGetRequest lootLockerGetRequest, LootLockerUpdateHeroRequest data, Action onComplete) @@ -189,39 +181,31 @@ public static void DeleteHero(string forPlayerWithUlid, int HeroID, Action { LootLockerResponse.Deserialize(onComplete, serverResponse); }); + LootLockerServerRequest.CallAPI(forPlayerWithUlid, endPoint.WithPathParameter(HeroID.ToString()), endPoint.httpMethod, HeroID.ToString(), (serverResponse) => { LootLockerResponse.Deserialize(onComplete, serverResponse); }); } public static void GetHeroInventory(string forPlayerWithUlid, int HeroID, Action onComplete) { EndPointClass endPoint = LootLockerEndPoints.getHeroInventory; - string getVariable = endPoint.endPoint; - - LootLockerServerRequest.CallAPI(forPlayerWithUlid, getVariable, endPoint.httpMethod, HeroID.ToString(), (serverResponse) => { LootLockerResponse.Deserialize(onComplete, serverResponse); }); + LootLockerServerRequest.CallAPI(forPlayerWithUlid, endPoint.WithPathParameter(HeroID.ToString()), endPoint.httpMethod, HeroID.ToString(), (serverResponse) => { LootLockerResponse.Deserialize(onComplete, serverResponse); }); } - public static void GetHeroLoadout(string forPlayerWithUlid, Action onComplete) + public static void GetHeroLoadout(string forPlayerWithUlid, int HeroID, Action onComplete) { EndPointClass endPoint = LootLockerEndPoints.getHeroLoadout; - string getVariable = endPoint.endPoint; - - LootLockerServerRequest.CallAPI(forPlayerWithUlid, getVariable, endPoint.httpMethod, null, (serverResponse) => { LootLockerResponse.Deserialize(onComplete, serverResponse); }); + LootLockerServerRequest.CallAPI(forPlayerWithUlid, endPoint.WithPathParameter(HeroID.ToString()), endPoint.httpMethod, null, (serverResponse) => { LootLockerResponse.Deserialize(onComplete, serverResponse); }); } public static void GetOtherPlayersHeroLoadout(string forPlayerWithUlid, int HeroID, Action onComplete) { EndPointClass endPoint = LootLockerEndPoints.getOtherPlayersHeroLoadout; - string getVariable = endPoint.endPoint; - - LootLockerServerRequest.CallAPI(forPlayerWithUlid, getVariable, endPoint.httpMethod, HeroID.ToString(), (serverResponse) => { LootLockerResponse.Deserialize(onComplete, serverResponse); }); + LootLockerServerRequest.CallAPI(forPlayerWithUlid, endPoint.WithPathParameter(HeroID.ToString()), endPoint.httpMethod, HeroID.ToString(), (serverResponse) => { LootLockerResponse.Deserialize(onComplete, serverResponse); }); } - public static void AddAssetToHeroLoadout(string forPlayerWithUlid, LootLockerAddAssetToHeroLoadoutRequest data, Action onComplete) + public static void AddAssetToHeroLoadout(string forPlayerWithUlid, int HeroID, LootLockerAddAssetToHeroLoadoutRequest data, Action onComplete) { EndPointClass endPoint = LootLockerEndPoints.addAssetToHeroLoadout; @@ -232,12 +216,10 @@ public static void AddAssetToHeroLoadout(string forPlayerWithUlid, LootLockerAdd } string json = LootLockerJson.SerializeObject(data); - string getVariable = endPoint.endPoint; - - LootLockerServerRequest.CallAPI(forPlayerWithUlid, getVariable, endPoint.httpMethod, json, (serverResponse) => { LootLockerResponse.Deserialize(onComplete, serverResponse); }); + LootLockerServerRequest.CallAPI(forPlayerWithUlid, endPoint.WithPathParameter(HeroID.ToString()), endPoint.httpMethod, json, (serverResponse) => { LootLockerResponse.Deserialize(onComplete, serverResponse); }); } - public static void AddAssetVariationToHeroLoadout(string forPlayerWithUlid, LootLockerAddAssetVariationToHeroLoadoutRequest data, Action onComplete) + public static void AddAssetVariationToHeroLoadout(string forPlayerWithUlid, int HeroID, LootLockerAddAssetVariationToHeroLoadoutRequest data, Action onComplete) { EndPointClass endPoint = LootLockerEndPoints.addAssetVariationToHeroLoadout; @@ -248,18 +230,14 @@ public static void AddAssetVariationToHeroLoadout(string forPlayerWithUlid, Loot } string json = LootLockerJson.SerializeObject(data); - string getVariable = endPoint.endPoint; - - LootLockerServerRequest.CallAPI(forPlayerWithUlid, getVariable, endPoint.httpMethod, json, (serverResponse) => { LootLockerResponse.Deserialize(onComplete, serverResponse); }); + LootLockerServerRequest.CallAPI(forPlayerWithUlid, endPoint.WithPathParameter(HeroID.ToString()), endPoint.httpMethod, json, (serverResponse) => { LootLockerResponse.Deserialize(onComplete, serverResponse); }); } - public static void RemoveAssetFromHeroLoadout(string forPlayerWithUlid, LootLockerGetRequest lootLockerGetRequest, Action onComplete) + public static void RemoveAssetFromHeroLoadout(string forPlayerWithUlid, int HeroID, int assetID, Action onComplete) { EndPointClass endPoint = LootLockerEndPoints.removeAssetFromHeroLoadout; - string getVariable = endPoint.WithPathParameters(lootLockerGetRequest.getRequests[0], lootLockerGetRequest.getRequests[1]); - - LootLockerServerRequest.CallAPI(forPlayerWithUlid, getVariable, endPoint.httpMethod, null, (serverResponse) => { LootLockerResponse.Deserialize(onComplete, serverResponse); }); ; + LootLockerServerRequest.CallAPI(forPlayerWithUlid, endPoint.WithPathParameters(assetID.ToString(), HeroID.ToString()), endPoint.httpMethod, null, (serverResponse) => { LootLockerResponse.Deserialize(onComplete, serverResponse); }); } diff --git a/Runtime/Game/Requests/PlayerRequest.cs b/Runtime/Game/Requests/PlayerRequest.cs index ea6ce362..d23f54d4 100644 --- a/Runtime/Game/Requests/PlayerRequest.cs +++ b/Runtime/Game/Requests/PlayerRequest.cs @@ -270,7 +270,7 @@ public static void LookupPlayerNames(string forPlayerWithUlid, string idType, st queryParams.Add(idType, identifier); } - LootLockerServerRequest.CallAPI(forPlayerWithUlid, endPoint.endPoint += queryParams.Build(), endPoint.httpMethod, null, onComplete: (serverResponse) => { LootLockerResponse.Deserialize(onComplete, serverResponse); }); + LootLockerServerRequest.CallAPI(forPlayerWithUlid, endPoint.endPoint + queryParams.Build(), endPoint.httpMethod, null, onComplete: (serverResponse) => { LootLockerResponse.Deserialize(onComplete, serverResponse); }); } public static void LookupPlayer1stPartyPlatformIDs(string forPlayerWithUlid, LookupPlayer1stPartyPlatformIDsRequest lookupPlayer1stPartyPlatformIDsRequest, Action onComplete) @@ -289,7 +289,7 @@ public static void LookupPlayer1stPartyPlatformIDs(string forPlayerWithUlid, Loo queryParams.Add("player_public_uid", playerPublicUID); } - LootLockerServerRequest.CallAPI(forPlayerWithUlid, endPoint.endPoint += queryParams.Build(), endPoint.httpMethod, null, onComplete: (serverResponse) => { LootLockerResponse.Deserialize(onComplete, serverResponse); }); + LootLockerServerRequest.CallAPI(forPlayerWithUlid, endPoint.endPoint + queryParams.Build(), endPoint.httpMethod, null, onComplete: (serverResponse) => { LootLockerResponse.Deserialize(onComplete, serverResponse); }); } } } diff --git a/Runtime/Game/Requests/ReportRequets.cs b/Runtime/Game/Requests/ReportRequets.cs index e14a9bfa..54ea3e8a 100644 --- a/Runtime/Game/Requests/ReportRequets.cs +++ b/Runtime/Game/Requests/ReportRequets.cs @@ -115,7 +115,7 @@ public static void GetRemovedUGCForPlayer(string forPlayerWithUlid, GetRemovedUG queryParams.Add("since", input.Since); } - LootLockerServerRequest.CallAPI(forPlayerWithUlid, endPoint.endPoint += queryParams.Build(), endPoint.httpMethod, null, (serverResponse) => { LootLockerResponse.Deserialize(onComplete, serverResponse); }); + LootLockerServerRequest.CallAPI(forPlayerWithUlid, endPoint.endPoint + queryParams.Build(), endPoint.httpMethod, null, (serverResponse) => { LootLockerResponse.Deserialize(onComplete, serverResponse); }); } public static void CreatePlayerReport(string forPlayerWithUlid, ReportsCreatePlayerRequest data, Action onComplete) diff --git a/Runtime/Game/Utilities/LootLockerHttpUtilities.cs b/Runtime/Game/Utilities/LootLockerHttpUtilities.cs index 5881d8a6..1f2a9c97 100644 --- a/Runtime/Game/Utilities/LootLockerHttpUtilities.cs +++ b/Runtime/Game/Utilities/LootLockerHttpUtilities.cs @@ -59,7 +59,7 @@ public string Build() foreach (KeyValuePair pair in _queryParams) { - if (string.IsNullOrEmpty(pair.Value)) + if (string.IsNullOrEmpty(pair.Key) || string.IsNullOrEmpty(pair.Value)) continue; if (query.Length > 1) diff --git a/Tests/LootLockerTests/PlayMode/MultiUserTests.cs b/Tests/LootLockerTests/PlayMode/MultiUserTests.cs index b8a9b424..fe251728 100644 --- a/Tests/LootLockerTests/PlayMode/MultiUserTests.cs +++ b/Tests/LootLockerTests/PlayMode/MultiUserTests.cs @@ -806,7 +806,7 @@ public IEnumerator MultiUser_SetPlayerDataWhenPlayerCachesExistButNoPlayersAreAc // When bool loginCompleted = false; - LootLockerSDKManager.StartGuestSession(response => + LootLockerSDKManager.StartGuestSession("completely-novel-identifier", response => { ulids.Add(response.player_ulid); loginCompleted = true; @@ -826,6 +826,55 @@ public IEnumerator MultiUser_SetPlayerDataWhenPlayerCachesExistButNoPlayersAreAc yield return null; } + + [UnityTest, Category("LootLocker"), Category("LootLockerCI"), Category("LootLockerCIFast")] + public IEnumerator MultiUser_GetPlayerDataWhenPlayerCachesExistButNoPlayersAreActive_GetsPlayerAndSetsDefault() + { + // Setup Succeeded + Assert.IsFalse(SetupFailed, "Setup did not succeed"); + + // Given + List ulids = new List(); + int guestUsersToCreate = 3; + for (int i = 0; i < guestUsersToCreate; i++) + { + bool guestLoginCompleted = false; + LootLockerSDKManager.StartGuestSession(response => + { + ulids.Add(response.player_ulid); + guestLoginCompleted = true; + }); + yield return new WaitUntil(() => guestLoginCompleted); + } + + foreach (var ulid in ulids) + { + LootLockerStateData.SetPlayerULIDToInactive(ulid); + } + + // When + bool pingCompleted = false; + string pingUlid = null; + LootLockerSDKManager.Ping(response => + { + pingUlid = response.requestContext.player_ulid; + pingCompleted = true; + }, ulids[ulids.Count - 1]); + yield return new WaitUntil(() => pingCompleted); + + // Then + int postPingActivePlayerCount = LootLockerStateData.GetActivePlayerULIDs().Count; + var postPingDefaultPlayerPlayerData = LootLockerStateData.GetStateForPlayerOrDefaultStateOrEmpty(null); + var postPingDefaultPlayerUlid = LootLockerStateData.GetDefaultPlayerULID(); + + Assert.AreEqual(1, postPingActivePlayerCount); + Assert.IsNotNull(postPingDefaultPlayerUlid); + Assert.IsNotNull(postPingDefaultPlayerPlayerData); + Assert.AreEqual(ulids[ulids.Count - 1], postPingDefaultPlayerUlid); + Assert.AreEqual(ulids[ulids.Count - 1], postPingDefaultPlayerPlayerData.ULID); + + yield return null; + } [UnityTest, Category("LootLocker"), Category("LootLockerCI"), Category("LootLockerCIFast")] public IEnumerator MultiUser_GetPlayerUlidFromWLEmailWhenPlayerIsCached_ReturnsCorrectULID()