diff --git a/Assets/Talo Game Services/Talo/Runtime/APIs/ChannelsAPI.cs b/Assets/Talo Game Services/Talo/Runtime/APIs/ChannelsAPI.cs index ec6c9bf7..8aa81692 100644 --- a/Assets/Talo Game Services/Talo/Runtime/APIs/ChannelsAPI.cs +++ b/Assets/Talo Game Services/Talo/Runtime/APIs/ChannelsAPI.cs @@ -96,7 +96,6 @@ public class ChannelsAPI : BaseAPI public event Action OnOwnershipTransferred; public event Action OnChannelDeleted; public event Action OnChannelUpdated; - public event Action OnChannelPropsRejected; public event Action OnChannelStoragePropsFailedToSet; public event Action OnChannelStoragePropsUpdated; @@ -170,7 +169,7 @@ public async Task GetSubscribedChannels(GetSubscribedChannelsOptions return res.channels; } - private async Task SendCreateChannelRequest(CreateChannelOptions options) + private async Task SendCreateChannelRequest(CreateChannelOptions options) { Talo.IdentityCheck(); @@ -191,19 +190,19 @@ private async Task SendCreateChannelRequest(CreateChannelOptions option var json = await Call(uri, "POST", content); var res = JsonUtility.FromJson(json); - return res.channel; + return new ChannelUpsertResult(true, res.channel); } catch (RequestException ex) { if (ex.IsBadRequest()) { - RejectedProp.TryEmit(ex.responseBody, OnChannelPropsRejected); + return new ChannelUpsertResult(false, null, RejectedProp.FromJson(ex.responseBody)); } throw; } } - public async Task Create(CreateChannelOptions options) + public async Task Create(CreateChannelOptions options) { options ??= new CreateChannelOptions(); return await SendCreateChannelRequest(options); @@ -228,7 +227,7 @@ public async Task Leave(int channelId) await Call(uri, "POST"); } - public async Task Update(int channelId, UpdateChannelOptions options = null) + public async Task Update(int channelId, UpdateChannelOptions options = null) { Talo.IdentityCheck(); @@ -252,13 +251,13 @@ public async Task Update(int channelId, UpdateChannelOptions options = var json = await Call(uri, "PUT", content); var res = JsonUtility.FromJson(json); - return res.channel; + return new ChannelUpsertResult(true, res.channel); } catch (RequestException ex) { if (ex.IsBadRequest()) { - RejectedProp.TryEmit(ex.responseBody, OnChannelPropsRejected); + return new ChannelUpsertResult(false, null, RejectedProp.FromJson(ex.responseBody)); } throw; } @@ -400,5 +399,19 @@ public async Task ListStorageProps(int channelId, string[] return Array.Empty(); } + + public class ChannelUpsertResult + { + public bool Success { get; } + public Channel Channel { get; } + public RejectedProp[] RejectedProps { get; } + + public ChannelUpsertResult(bool success, Channel channel, RejectedProp[] rejectedProps = null) + { + Success = success; + Channel = channel; + RejectedProps = rejectedProps ?? Array.Empty(); + } + } } } diff --git a/Assets/Talo Game Services/Talo/Runtime/APIs/FeedbackAPI.cs b/Assets/Talo Game Services/Talo/Runtime/APIs/FeedbackAPI.cs index e1e2b4df..17da423d 100644 --- a/Assets/Talo Game Services/Talo/Runtime/APIs/FeedbackAPI.cs +++ b/Assets/Talo Game Services/Talo/Runtime/APIs/FeedbackAPI.cs @@ -7,8 +7,6 @@ namespace TaloGameServices { public class FeedbackAPI : BaseAPI { - public event Action OnPropsRejected; - public FeedbackAPI() : base("v1/game-feedback") { } public async Task GetCategories() @@ -20,7 +18,7 @@ public async Task GetCategories() return res.feedbackCategories; } - public async Task Send(string categoryInternalName, string comment, params (string, string)[] props) + public async Task Send(string categoryInternalName, string comment, params (string, string)[] props) { Talo.IdentityCheck(); @@ -31,15 +29,28 @@ public async Task Send(string categoryInternalName, string comment, params (stri try { await Call(uri, "POST", content); + return new FeedbackSendResult(true); } catch (RequestException ex) { if (ex.IsBadRequest()) { - RejectedProp.TryEmit(ex.responseBody, OnPropsRejected); + return new FeedbackSendResult(false, RejectedProp.FromJson(ex.responseBody)); } throw; } } + + public class FeedbackSendResult + { + public bool Success { get; } + public RejectedProp[] RejectedProps { get; } + + public FeedbackSendResult(bool success, RejectedProp[] rejectedProps = null) + { + Success = success; + RejectedProps = rejectedProps ?? Array.Empty(); + } + } } } diff --git a/Assets/Talo Game Services/Talo/Runtime/APIs/LeaderboardsAPI.cs b/Assets/Talo Game Services/Talo/Runtime/APIs/LeaderboardsAPI.cs index 39703076..9f3b54e8 100644 --- a/Assets/Talo Game Services/Talo/Runtime/APIs/LeaderboardsAPI.cs +++ b/Assets/Talo Game Services/Talo/Runtime/APIs/LeaderboardsAPI.cs @@ -45,8 +45,6 @@ public class LeaderboardsAPI : BaseAPI { private readonly LeaderboardEntriesManager _entriesManager = new(); - public event Action OnPropsRejected; - public LeaderboardsAPI() : base("v1/leaderboards") { } public List GetCachedEntries(string internalName, GetCachedEntriesOptions options = null) @@ -77,7 +75,7 @@ public async Task GetEntries(string internalName, Ge return res; } - public async Task<(LeaderboardEntry, bool)> AddEntry(string internalName, float score, params (string, string)[] propTuples) + public async Task AddEntry(string internalName, float score, params (string, string)[] propTuples) { Talo.IdentityCheck(); @@ -93,16 +91,32 @@ public async Task GetEntries(string internalName, Ge var res = JsonUtility.FromJson(json); _entriesManager.UpsertEntry(internalName, res.entry, true); - return (res.entry, res.updated); + return new AddEntryResult(true, res.entry, res.updated); } catch (RequestException ex) { if (ex.IsBadRequest()) { - RejectedProp.TryEmit(ex.responseBody, OnPropsRejected); + return new AddEntryResult(false, null, false, RejectedProp.FromJson(ex.responseBody)); } throw; } } + + public class AddEntryResult + { + public bool Success { get; } + public LeaderboardEntry Entry { get; } + public bool Updated { get; } + public RejectedProp[] RejectedProps { get; } + + public AddEntryResult(bool success, LeaderboardEntry entry, bool updated, RejectedProp[] rejectedProps = null) + { + Success = success; + Entry = entry; + Updated = updated; + RejectedProps = rejectedProps ?? Array.Empty(); + } + } } -} \ No newline at end of file +} diff --git a/Assets/Talo Game Services/Talo/Runtime/APIs/PlayersAPI.cs b/Assets/Talo Game Services/Talo/Runtime/APIs/PlayersAPI.cs index f2d739a7..fbc812cd 100644 --- a/Assets/Talo Game Services/Talo/Runtime/APIs/PlayersAPI.cs +++ b/Assets/Talo Game Services/Talo/Runtime/APIs/PlayersAPI.cs @@ -20,7 +20,6 @@ public enum DebouncedOperation public event Action OnIdentificationStarted; public event Action OnIdentificationFailed; public event Action OnIdentityCleared; - public event Action OnPropsRejected; public event Action OnPlayerUpdated; public PlayersAPI() : base("v1/players") @@ -175,11 +174,6 @@ private async Task RunUpdate() Talo.CurrentPlayer = res.player; Talo.CurrentAlias.WriteOfflineAlias(); - if (res.rejectedProps != null && res.rejectedProps.Length > 0) - { - OnPropsRejected?.Invoke(res.rejectedProps); - } - return res.rejectedProps ?? Array.Empty(); } diff --git a/Assets/Talo Game Services/Talo/Runtime/Entities/RejectedProp.cs b/Assets/Talo Game Services/Talo/Runtime/Entities/RejectedProp.cs index 5f338fc1..9ae7724c 100644 --- a/Assets/Talo Game Services/Talo/Runtime/Entities/RejectedProp.cs +++ b/Assets/Talo Game Services/Talo/Runtime/Entities/RejectedProp.cs @@ -35,14 +35,6 @@ public static RejectedProp[] FromJson(string json) return wrapper?.rejectedProps ?? Array.Empty(); } - public static void TryEmit(string json, Action onRejected) - { - var rejectedProps = FromJson(json); - if (rejectedProps.Length > 0) - { - onRejected?.Invoke(rejectedProps); - } - } } [Serializable] diff --git a/Assets/Talo Game Services/Talo/Samples/ChannelStorageDemo/Scripts/ChannelStorageDemoUIController.cs b/Assets/Talo Game Services/Talo/Samples/ChannelStorageDemo/Scripts/ChannelStorageDemoUIController.cs index 97a998ae..deb54faa 100644 --- a/Assets/Talo Game Services/Talo/Samples/ChannelStorageDemo/Scripts/ChannelStorageDemoUIController.cs +++ b/Assets/Talo Game Services/Talo/Samples/ChannelStorageDemo/Scripts/ChannelStorageDemoUIController.cs @@ -65,7 +65,17 @@ private async Task SetupDemoChannel() ("channel-storage-demo", "true") } }; - demoChannel = await Talo.Channels.Create(createOptions); + var createResult = await Talo.Channels.Create(createOptions); + if (createResult.Success) + { + demoChannel = createResult.Channel; + } + } + + if (demoChannel == null) + { + Debug.LogError("Failed to create or find a channel for the Channel Storage Demo"); + return; } await Talo.Channels.Join(demoChannel.id); diff --git a/Assets/Talo Game Services/Talo/Samples/ChatDemo/Scripts/ChatUIController.cs b/Assets/Talo Game Services/Talo/Samples/ChatDemo/Scripts/ChatUIController.cs index cf8d04b0..b8d364f0 100644 --- a/Assets/Talo Game Services/Talo/Samples/ChatDemo/Scripts/ChatUIController.cs +++ b/Assets/Talo Game Services/Talo/Samples/ChatDemo/Scripts/ChatUIController.cs @@ -132,7 +132,9 @@ private async void OnCreateChannelClick() return; } - var channel = await Talo.Channels.Create(new CreateChannelOptions() { name = channelName, autoCleanup = true }); + var result = await Talo.Channels.Create(new CreateChannelOptions() { name = channelName, autoCleanup = true }); + + var channel = result.Channel; AddChannelToList(channel); channelNameField.value = ""; activeChannelId = channel.id; diff --git a/Assets/Talo Game Services/Talo/Samples/LeaderboardsDemo/Scripts/LeaderboardUIController.cs b/Assets/Talo Game Services/Talo/Samples/LeaderboardsDemo/Scripts/LeaderboardUIController.cs index 1038aacb..52b7e27a 100644 --- a/Assets/Talo Game Services/Talo/Samples/LeaderboardsDemo/Scripts/LeaderboardUIController.cs +++ b/Assets/Talo Game Services/Talo/Samples/LeaderboardsDemo/Scripts/LeaderboardUIController.cs @@ -42,14 +42,14 @@ private async void OnPostClick() var team = UnityEngine.Random.Range(0, 2) == 0 ? "Blue" : "Red"; await Talo.Players.Identify("username", username); - (LeaderboardEntry entry, bool updated) = await Talo.Leaderboards.AddEntry( + var result = await Talo.Leaderboards.AddEntry( leaderboardName, score, ("team", team) ); infoLabel.text = $"You scored {score} for the {team} team."; - if (updated) infoLabel.text += " Your highscore was updated!"; + if (result.Updated) infoLabel.text += " Your highscore was updated!"; entriesList.Rebuild(); } diff --git a/Assets/Talo Game Services/Talo/Samples/Playground/Scripts/Feedback/SendFeedback.cs b/Assets/Talo Game Services/Talo/Samples/Playground/Scripts/Feedback/SendFeedback.cs index aad398cf..9bc57564 100644 --- a/Assets/Talo Game Services/Talo/Samples/Playground/Scripts/Feedback/SendFeedback.cs +++ b/Assets/Talo Game Services/Talo/Samples/Playground/Scripts/Feedback/SendFeedback.cs @@ -17,8 +17,15 @@ public async void OnButtonClick() try { - await Talo.Feedback.Send(categoryInternalName, feedbackComment); - ResponseMessage.SetText($"Feedback sent for {categoryInternalName}: {feedbackComment}"); + var result = await Talo.Feedback.Send(categoryInternalName, feedbackComment); + if (result.Success) + { + ResponseMessage.SetText($"Feedback sent for {categoryInternalName}: {feedbackComment}"); + } + else + { + ResponseMessage.SetText("Failed to send feedback"); + } } catch (Exception ex) { diff --git a/Assets/Talo Game Services/Talo/Samples/Playground/Scripts/Leaderboards/PostLeaderboardEntry.cs b/Assets/Talo Game Services/Talo/Samples/Playground/Scripts/Leaderboards/PostLeaderboardEntry.cs index 7bf24906..2309f113 100644 --- a/Assets/Talo Game Services/Talo/Samples/Playground/Scripts/Leaderboards/PostLeaderboardEntry.cs +++ b/Assets/Talo Game Services/Talo/Samples/Playground/Scripts/Leaderboards/PostLeaderboardEntry.cs @@ -24,9 +24,15 @@ private async Task PostEntry() try { int score = UnityEngine.Random.Range(0, 10000); - (LeaderboardEntry entry, bool updated) = await Talo.Leaderboards.AddEntry(leaderboardInternalName, score); + var result = await Talo.Leaderboards.AddEntry(leaderboardInternalName, score); - ResponseMessage.SetText($"Entry with score {score} added, position is {entry.position}, it was {(updated ? "" : "not")} updated"); + if (result.Entry == null) + { + ResponseMessage.SetText("Failed to add entry"); + return; + } + + ResponseMessage.SetText($"Entry with score {score} added, position is {result.Entry.position}, it was {(result.Updated ? "" : "not")} updated"); } catch (Exception ex) { diff --git a/Assets/Talo Game Services/Talo/Tests/PlayersAPI/PlayerUpdatedEventTest.cs b/Assets/Talo Game Services/Talo/Tests/PlayersAPI/PlayerUpdatedEventTest.cs index 1139f12b..a03af117 100644 --- a/Assets/Talo Game Services/Talo/Tests/PlayersAPI/PlayerUpdatedEventTest.cs +++ b/Assets/Talo Game Services/Talo/Tests/PlayersAPI/PlayerUpdatedEventTest.cs @@ -99,14 +99,9 @@ public IEnumerator LeadingAndTrailing_FireOnPlayerUpdatedTwice() } [UnityTest] - public IEnumerator PropRejection_BothOnPropsRejectedAndOnPlayerUpdatedFire() + public IEnumerator PropRejection_OnPlayerUpdatedFiresWithRejectedProps() { - int rejectedCount = 0; Talo.Players.OnPlayerUpdated += _mock.OnUpdated; - Talo.Players.OnPropsRejected += _ => - { - rejectedCount++; - }; var uri = new Uri($"{Talo.Settings.apiUrl}/v1/players/uuid"); RequestMock.ReplyOnce(uri, "PATCH", JsonUtility.ToJson(new PlayersUpdateResponse @@ -115,9 +110,10 @@ public IEnumerator PropRejection_BothOnPropsRejectedAndOnPlayerUpdatedFire() rejectedProps = new[] { new RejectedProp { key = "k1", error = "PROP_VALUE_TOO_LONG", message = "too long" } } })); - Talo.CurrentPlayer.SetProp("k1", "v1-updated"); + var result = Talo.CurrentPlayer.SetProp("k1", "v1-updated").GetAwaiter().GetResult(); - Assert.AreEqual(1, rejectedCount); + Assert.AreEqual(1, result.RejectedProps.Length); + Assert.AreEqual("k1", result.RejectedProps[0].key); Assert.AreEqual(1, _mock.updatedCount); Assert.IsTrue(_mock.lastSuccess);