Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,4 @@ jobs:
UNITY_EMAIL: ${{ secrets.UNITY_EMAIL }}
UNITY_PASSWORD: ${{ secrets.UNITY_PASSWORD }}
with:
unityVersion: 6000.0.28f1
unityVersion: 6000.0.59f2
4 changes: 3 additions & 1 deletion Assets/Talo Game Services/Talo/Runtime/APIs/BaseAPI.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ namespace TaloGameServices
public class BaseAPI
{
// automatically updated with a pre-commit hook
private const string ClientVersion = "0.47.0";
private const string ClientVersion = "0.48.0";

protected string baseUrl;

Expand Down Expand Up @@ -101,6 +101,8 @@ protected async Task<string> Call(
Debug.Log($"--> {method} {uri} [{www.responseCode}] {www.downloadHandler.text}");
}

await Talo.Continuity.HandlePostResponseHealthCheck(uri.ToString(), www.result);

if (www.result == UnityWebRequest.Result.Success)
{
return www.downloadHandler.text;
Expand Down
43 changes: 40 additions & 3 deletions Assets/Talo Game Services/Talo/Runtime/APIs/HealthCheckAPI.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,23 +4,60 @@

namespace TaloGameServices
{
public enum HealthCheckStatus
{
OK,
FAILED,
UNKNOWN
}

public class HealthCheckAPI : BaseAPI
{
private HealthCheckStatus lastHealthCheckStatus = HealthCheckStatus.UNKNOWN;

public HealthCheckAPI() : base("v1/health-check") { }

public HealthCheckStatus GetLastStatus()
{
return lastHealthCheckStatus;
}

public async Task<bool> Ping()
{
var uri = new Uri(baseUrl);

bool success;
try
{
await Call(uri, "GET");
return true;
} catch
success = true;
}
catch
{
Debug.LogWarning("Health check failed");
return false;
success = false;
}

bool failedLastHealthCheck = lastHealthCheckStatus == HealthCheckStatus.FAILED;

if (success)
{
lastHealthCheckStatus = HealthCheckStatus.OK;
if (failedLastHealthCheck)
{
Talo.InvokeConnectionRestored();
}
}
else
{
lastHealthCheckStatus = HealthCheckStatus.FAILED;
if (!failedLastHealthCheck)
{
Talo.InvokeConnectionLost();
}
}

return success;
}
}
}
38 changes: 37 additions & 1 deletion Assets/Talo Game Services/Talo/Runtime/APIs/PlayersAPI.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,24 @@ public class PlayersAPI : BaseAPI

private readonly string offlineDataPath = Application.persistentDataPath + "/ta.bin";

public PlayersAPI() : base("v1/players") { }
public PlayersAPI() : base("v1/players")
{
Talo.OnConnectionRestored += OnConnectionRestored;
}

private async void OnConnectionRestored()
{
await Talo.Socket.ResetConnection();

if (Talo.HasIdentity())
{
var socketToken = await CreateSocketToken();
if (!string.IsNullOrEmpty(socketToken))
{
Talo.Socket.SetSocketToken(socketToken);
}
}
}

public void InvokeIdentifiedEvent()
{
Expand Down Expand Up @@ -202,5 +219,24 @@ public async Task<PlayersSearchResponse> Search(string query)
var res = JsonUtility.FromJson<PlayersSearchResponse>(json);
return res;
}

public async Task<string> CreateSocketToken()
{
Talo.IdentityCheck();

var uri = new Uri($"{baseUrl}/socket-token");

try
{
var json = await Call(uri, "POST");
var res = JsonUtility.FromJson<PlayersSocketTokenResponse>(json);
return res.socketToken;
}
catch (Exception ex)
{
Debug.LogWarning($"Failed to create socket token: {ex.Message}");
return "";
}
}
}
}
66 changes: 50 additions & 16 deletions Assets/Talo Game Services/Talo/Runtime/APIs/SavesAPI.cs
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,41 @@ public async Task<GameSave> ReplaceSaveWithOfflineSave(GameSave offlineSave)
return res.save;
}

private async Task<GameSave[]> SyncOfflineSaves(GameSave[] offlineSaves)
{
var offlineOnlySaves = offlineSaves.Where((save) => save.id < 0).ToArray();
if (offlineOnlySaves.Length == 0) return Array.Empty<GameSave>();

Talo.IdentityCheck();

var tasks = offlineOnlySaves.Select(async (offlineSave) =>
{
try
{
var uri = new Uri(baseUrl);
var json = await Call(uri, "POST", JsonUtility.ToJson(new SavesPostRequest
{
name = offlineSave.name,
content = offlineSave.content
}));

var res = JsonUtility.FromJson<SavesPostResponse>(json);
return new { res.save, offlineId = offlineSave.id };
}
catch
{
return null;
}
});

var results = await Task.WhenAll(tasks);
var successfulResults = results.Where((res) => res != null);
var offlineIdsToDelete = successfulResults.Select((res) => res.offlineId).ToArray();
savesManager.DeleteOfflineSaves(offlineIdsToDelete);

return successfulResults.Select((res) => res.save).ToArray();
}

public async Task<GameSave[]> GetSaves()
{
var saves = new List<GameSave>();
Expand All @@ -88,27 +123,26 @@ public async Task<GameSave[]> GetSaves()
var onlineSaves = res.saves;

if (offlineSaves != null)
{
var tasks = onlineSaves.Select(async (onlineSave) =>
{
var tasks = onlineSaves.Select(async (onlineSave) =>
{
var offlineSave = offlineSaves
.FirstOrDefault((offlineSave) => offlineSave.id == onlineSave.id);
var offlineSave = offlineSaves
.FirstOrDefault((offlineSave) => offlineSave.id == onlineSave.id);

if (offlineSave != null)
{
return await savesManager.SyncSave(onlineSave, offlineSave);
}
return onlineSave;
})
.ToList();
if (offlineSave != null)
{
return await savesManager.SyncSave(onlineSave, offlineSave);
}
return onlineSave;
});

onlineSaves = await Task.WhenAll(tasks);
onlineSaves = await Task.WhenAll(tasks);

var syncedSaves = await savesManager.SyncOfflineSaves(offlineSaves);
saves.AddRange(syncedSaves);
}
var syncedSaves = await SyncOfflineSaves(offlineSaves);
saves.AddRange(syncedSaves);
}

saves.AddRange(onlineSaves);
saves.AddRange(onlineSaves);
}

savesManager.HandleSavesLoaded(saves);
Expand Down
11 changes: 11 additions & 0 deletions Assets/Talo Game Services/Talo/Runtime/APIs/StatsAPI.cs
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,17 @@ public async Task<PlayerStat> FindPlayerStat(string internalName)
return res.playerStat;
}

public async Task<PlayerStat[]> ListPlayerStats()
{
Talo.IdentityCheck();

var uri = new Uri($"{baseUrl}/player-stats");

var json = await Call(uri, "GET");
var res = JsonUtility.FromJson<PlayerStatsListResponse>(json);
return res.playerStats;
}

public async Task<StatsPutResponse> Track(string internalName, float change = 1f)
{
Talo.IdentityCheck();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
namespace TaloGameServices
{
[System.Serializable]
public class PlayerStatsListResponse
{
public PlayerStat[] playerStats;
}
}

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
namespace TaloGameServices
{
[System.Serializable]
public class PlayersSocketTokenResponse
{
public string socketToken;
}
}

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

13 changes: 13 additions & 0 deletions Assets/Talo Game Services/Talo/Runtime/Talo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,23 @@ namespace TaloGameServices
{
public class Talo
{
public static event Action OnConnectionLost;
public static event Action OnConnectionRestored;

private static bool _testMode;

internal static bool TestMode => _testMode;

internal static void InvokeConnectionLost()
{
OnConnectionLost?.Invoke();
}

internal static void InvokeConnectionRestored()
{
OnConnectionRestored?.Invoke();
}

internal static EventsAPI _events;
internal static PlayersAPI _players;
internal static LeaderboardsAPI _leaderboards;
Expand Down
4 changes: 2 additions & 2 deletions Assets/Talo Game Services/Talo/Runtime/TaloManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ private async void DoFlush()
}
}

private void Update()
private async void Update()
{
if (Application.platform == RuntimePlatform.WebGLPlayer)
{
Expand All @@ -65,7 +65,7 @@ private void Update()
tmrContinuity += Time.deltaTime;
if (tmrContinuity >= 10f)
{
Talo.Continuity.ProcessRequests();
await Talo.Continuity.ProcessRequests();
tmrContinuity = 0;
}
}
Expand Down
5 changes: 0 additions & 5 deletions Assets/Talo Game Services/Talo/Runtime/TaloSocket.cs
Original file line number Diff line number Diff line change
Expand Up @@ -130,11 +130,6 @@ public void SetSocketToken(string token)

public async Task ResetConnection()
{
if (!identified)
{
return;
}

CloseConnection();
socketAuthenticated = false;
identified = false;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@
using System.Collections.Generic;
using System.Linq;
using System.IO;
using System.Threading.Tasks;
using UnityEngine;
using UnityEngine.Networking;

namespace TaloGameServices
{
Expand Down Expand Up @@ -79,7 +81,7 @@ public void PushRequest(Uri uri, string method, string content, List<HttpHeader>
WriteRequests();
}

public async void ProcessRequests()
public async Task ProcessRequests()
{
if (!HasRequests() || !await Talo.HealthCheck.Ping()) return;

Expand Down Expand Up @@ -122,5 +124,33 @@ public void ClearRequests()
_requests.Clear();
WriteRequests();
}

public async Task HandlePostResponseHealthCheck(string url, UnityWebRequest.Result result)
{
if (url.Contains("/health-check"))
{
return;
}

var isConnectionError = result == UnityWebRequest.Result.ConnectionError;
var isDataProcessingError = result == UnityWebRequest.Result.DataProcessingError;

if (result == UnityWebRequest.Result.Success)
{
// if offline mode is enabled, check if it shouldn't be
if (Talo.HealthCheck.GetLastStatus() == HealthCheckStatus.FAILED)
{
await Talo.HealthCheck.Ping();
}
}
else if (isConnectionError || isDataProcessingError)
{
// if offline mode isn't enabled, check if it should be
if (Talo.HealthCheck.GetLastStatus() != HealthCheckStatus.FAILED)
{
await Talo.HealthCheck.Ping();
}
}
}
}
}
Loading