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 Assets/Talo Game Services/Talo/Runtime/APIs/ChannelsAPI.cs
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ public class ChannelsAPI : BaseAPI
public event Action<Channel, ChannelStoragePropError[]> OnChannelStoragePropsFailedToSet;
public event Action<Channel, ChannelStorageProp[], ChannelStorageProp[]> OnChannelStoragePropsUpdated;

private ChannelStorageManager _storageManager = new ChannelStorageManager();
private readonly ChannelStorageManager _storageManager = new ();

public ChannelsAPI() : base("v1/game-channels")
{
Expand Down
283 changes: 283 additions & 0 deletions Assets/Talo Game Services/Talo/Runtime/APIs/PlayerRelationshipsAPI.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,283 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using UnityEngine;

namespace TaloGameServices
{
public enum ConfirmedFilter
{
Any,
Confirmed,
Unconfirmed
}

public enum RelationshipTypeFilter
{
Any,
Unidirectional,
Bidirectional
}

public class GetSubscribersOptions
{
public int page = 0;
public ConfirmedFilter confirmed = ConfirmedFilter.Any;
public int aliasId = -1;
public RelationshipTypeFilter relationshipType = RelationshipTypeFilter.Any;

public string ToQueryString()
{
var query = new Dictionary<string, string> { ["page"] = page.ToString() };

if (confirmed != ConfirmedFilter.Any)
{
// 'true' or 'false'
query["confirmed"] = (confirmed == ConfirmedFilter.Confirmed).ToString().ToLower();
}

if (aliasId != -1)
{
query["aliasId"] = aliasId.ToString();
}

if (relationshipType != RelationshipTypeFilter.Any)
{
var type = relationshipType == RelationshipTypeFilter.Bidirectional ? RelationshipType.Bidirectional : RelationshipType.Unidirectional;
query["relationshipType"] = PlayerAliasSubscription.RelationshipTypeToString(type);
}

return string.Join("&", query.Select((param) => $"{param.Key}={Uri.EscapeDataString(param.Value)}"));
}
}

public class GetSubscriptionsOptions
{
public int page = 0;
public ConfirmedFilter confirmed = ConfirmedFilter.Any;
public int aliasId = -1;
public RelationshipTypeFilter relationshipType = RelationshipTypeFilter.Any;

public string ToQueryString()
{
var query = new Dictionary<string, string> { ["page"] = page.ToString() };

if (confirmed != ConfirmedFilter.Any)
{
query["confirmed"] = (confirmed == ConfirmedFilter.Confirmed).ToString().ToLower();
}

if (aliasId != -1)
{
query["aliasId"] = aliasId.ToString();
}

if (relationshipType != RelationshipTypeFilter.Any)
{
var type = relationshipType == RelationshipTypeFilter.Bidirectional ? RelationshipType.Bidirectional : RelationshipType.Unidirectional;
query["relationshipType"] = PlayerAliasSubscription.RelationshipTypeToString(type);
}

return string.Join("&", query.Select((param) => $"{param.Key}={Uri.EscapeDataString(param.Value)}"));
}
}

public class PlayerRelationshipsAPI : BaseAPI
{
public event Action<PlayerAlias, string> OnMessageReceived;
public event Action<PlayerAlias> OnRelationshipRequestReceived;
public event Action<PlayerAlias> OnRelationshipRequestCancelled;
public event Action<PlayerAlias> OnRelationshipConfirmed;
public event Action<PlayerAlias> OnRelationshipEnded;

public PlayerRelationshipsAPI() : base("v1/players/relationships")
{
Talo.Socket.OnMessageReceived += HandleSocketMessage;
}

private void HandleSocketMessage(SocketResponse response)
{
var responseType = response.GetResponseType();

switch (responseType)
{
case "v1.player-relationships.broadcast":
var broadcastData = response.GetData<PlayerRelationshipsBroadcastResponse>();
if (broadcastData?.playerAlias != null)
{
OnMessageReceived?.Invoke(broadcastData.playerAlias, broadcastData.message);
}
break;

case "v1.player-relationships.subscription-created":
var createdData = response.GetData<PlayerRelationshipsSubscriptionCreatedResponse>();
if (createdData?.subscription?.subscriber != null)
{
OnRelationshipRequestReceived?.Invoke(createdData.subscription.subscriber);
}
break;

case "v1.player-relationships.subscription-confirmed":
var confirmedData = response.GetData<PlayerRelationshipsSubscriptionConfirmedResponse>();
if (confirmedData?.subscription != null)
{
var subscription = confirmedData.subscription;
var otherAlias = GetOtherAlias(subscription);
if (otherAlias != null)
{
OnRelationshipConfirmed?.Invoke(otherAlias);
}
}
break;

case "v1.player-relationships.subscription-deleted":
var deletedData = response.GetData<PlayerRelationshipsSubscriptionDeletedResponse>();
if (deletedData?.subscription != null)
{
var subscription = deletedData.subscription;
var otherAlias = GetOtherAlias(subscription);
if (otherAlias != null)
{
if (subscription.confirmed)
{
OnRelationshipEnded?.Invoke(otherAlias);
}
else
{
OnRelationshipRequestCancelled?.Invoke(otherAlias);
}
}
}
break;
}
}

private PlayerAlias GetOtherAlias(PlayerAliasSubscription subscription)
{
if (subscription.subscriber?.player?.id == Talo.CurrentPlayer?.id)
{
return subscription.subscribedTo;
}
return subscription.subscriber;
}

public void Broadcast(string message)
{
Talo.IdentityCheck();

var payload = new PlayerRelationshipsBroadcastRequest
{
message = message
};

Talo.Socket.Send(new SocketRequest<PlayerRelationshipsBroadcastRequest>("v1.player-relationships.broadcast", payload));
}

public async Task<PlayerAliasSubscription> SubscribeTo(int playerAliasId, RelationshipType relationshipType)
{
Talo.IdentityCheck();

var request = new PlayerRelationshipsSubscribeToRequest
{
aliasId = playerAliasId,
relationshipType = PlayerAliasSubscription.RelationshipTypeToString(relationshipType)
};

var json = await Call(GetUri(), "POST", JsonUtility.ToJson(request));
var response = JsonUtility.FromJson<PlayerRelationshipsSubscriptionResponse>(json);
return response.subscription;
}

public async Task RevokeSubscription(int subscriptionId)
{
Talo.IdentityCheck();

await Call(new Uri($"{baseUrl}/{subscriptionId}"), "DELETE");
}

public async Task<bool> UnsubscribeFrom(int playerAliasId)
{
Talo.IdentityCheck();

var options = new GetSubscriptionsOptions { aliasId = playerAliasId };
var page = await GetSubscriptions(options);

if (page.subscriptions.Length == 0)
{
return false;
}

await RevokeSubscription(page.subscriptions[0].id);
return true;
}

public async Task<PlayerAliasSubscription> ConfirmSubscriptionById(int subscriptionId)
{
Talo.IdentityCheck();

var json = await Call(new Uri($"{baseUrl}/{subscriptionId}/confirm"), "PUT");
var response = JsonUtility.FromJson<PlayerRelationshipsSubscriptionResponse>(json);
return response.subscription;
}

public async Task<bool> ConfirmSubscriptionFrom(int playerAliasId)
{
Talo.IdentityCheck();

var options = new GetSubscribersOptions
{
confirmed = ConfirmedFilter.Unconfirmed,
aliasId = playerAliasId
};
var page = await GetSubscribers(options);

if (page.subscriptions.Length == 0)
{
return false;
}

var subscription = page.subscriptions[0];
var confirmed = await ConfirmSubscriptionById(subscription.id);
return confirmed != null;
}

public async Task<bool> IsSubscribedTo(int playerAliasId, bool confirmed)
{
Talo.IdentityCheck();

var options = new GetSubscriptionsOptions
{
confirmed = confirmed ? ConfirmedFilter.Confirmed : ConfirmedFilter.Any,
aliasId = playerAliasId
};
var page = await GetSubscriptions(options);

return page.subscriptions.Length > 0;
}

public async Task<PlayerRelationshipsSubscriptionsListResponse> GetSubscribers(GetSubscribersOptions options = null)
{
Talo.IdentityCheck();

options ??= new GetSubscribersOptions();

var queryString = options.ToQueryString();
var uri = new Uri($"{baseUrl}/subscribers?{queryString}");
var json = await Call(uri, "GET");
return JsonUtility.FromJson<PlayerRelationshipsSubscriptionsListResponse>(json);
}

public async Task<PlayerRelationshipsSubscriptionsListResponse> GetSubscriptions(GetSubscriptionsOptions options = null)
{
Talo.IdentityCheck();

options ??= new GetSubscriptionsOptions();

var queryString = options.ToQueryString();
var uri = new Uri($"{baseUrl}/subscriptions?{queryString}");
var json = await Call(uri, "GET");
return JsonUtility.FromJson<PlayerRelationshipsSubscriptionsListResponse>(json);
}
}
}

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

1 change: 1 addition & 0 deletions Assets/Talo Game Services/Talo/Runtime/Entities/Player.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ public class Player : EntityWithProps
public string id;
public PlayerAlias[] aliases;
public GroupStub[] groups;
public PlayerPresence presence;

public override string ToString()
{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
using System;

namespace TaloGameServices
{
public enum RelationshipType
{
Unidirectional,
Bidirectional
}

[Serializable]
public class PlayerAliasSubscription
{
public int id;
public PlayerAlias subscriber;
public PlayerAlias subscribedTo;
public bool confirmed;
public string relationshipType;
public string createdAt;
public string updatedAt;

public RelationshipType GetRelationshipType()
{
return ParseRelationshipType(relationshipType);
}

public static RelationshipType ParseRelationshipType(string type)
{
return type == "bidirectional" ? RelationshipType.Bidirectional : RelationshipType.Unidirectional;
}

public static string RelationshipTypeToString(RelationshipType type)
{
return type == RelationshipType.Bidirectional ? "bidirectional" : "unidirectional";
}
}
}

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,11 @@
using System;

namespace TaloGameServices
{
[Serializable]
public class PlayerRelationshipsSubscribeToRequest
{
public int aliasId;
public string relationshipType;
}
}

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,10 @@
using System;

namespace TaloGameServices
{
[Serializable]
public class PlayerRelationshipsSubscriptionResponse
{
public PlayerAliasSubscription subscription;
}
}

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,13 @@
using System;

namespace TaloGameServices
{
[Serializable]
public class PlayerRelationshipsSubscriptionsListResponse
{
public PlayerAliasSubscription[] subscriptions;
public int count;
public int itemsPerPage;
public bool isLastPage;
}
}

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

Loading