Skip to content

Instantly share code, notes, and snippets.

@recursivecodes
Created August 5, 2024 12:41
Show Gist options
  • Save recursivecodes/3ae9fd87753d28afacc5ba58741fae75 to your computer and use it in GitHub Desktop.
Save recursivecodes/3ae9fd87753d28afacc5ba58741fae75 to your computer and use it in GitHub Desktop.
using UnityEngine;
using Momento.Sdk;
using Momento.Sdk.Auth;
using Momento.Sdk.Config;
using Momento.Sdk.Exceptions;
using Momento.Sdk.Responses;
using System.Threading.Tasks;
using System;
using System.Threading;
using System.Collections;
using UnityEngine.Networking;
public struct TokenVendingMachineResponse
{
public string authToken;
public double expiresAt;
}
public struct GameCommand
{
public const string ENVIRONMENT = "environment";
public const string CAMERA = "camera";
public string command;
public string commandType;
}
public class ViewerInteractionManager : MonoBehaviour
{
private const string TopicName = "ivs-rtx-unity-topic";
private const string cacheName = "demo-cache";
private string clientName = "ivs-rtx-unity-client";
private CancellationTokenSource cts = null;
private ITopicClient topicClient = null;
private StringMomentoTokenProvider authProvider = null;
private TopicSubscribeResponse.Subscription subscription = null;
[SerializeField] private GameObject jump;
[SerializeField] private GameObject wall;
[SerializeField] private GameObject stone;
[SerializeField] private GameObject kart;
[SerializeField] private GameObject defaultTrackEntrance;
[SerializeField] private GameObject altTrackEntrance;
[SerializeField] private GameObject defaultTrackExit;
[SerializeField] private GameObject altTrackExit;
// The Token Vending Machine URL should be suffixed with "?name="
// such as like https://9jkmukxn68.execute-api.us-west-2.amazonaws.com/prod?name=
// See the instructions at the following URL for more information:
// https://github.com/momentohq/client-sdk-javascript/tree/main/examples/nodejs/token-vending-machine
public string tokenVendingMachineURL = "https://[redacted].execute-api.us-east-1.amazonaws.com/prod?name=";
private bool error = false;
private bool loading = false;
// Start is called before the first frame update
void Start()
{
if (tokenVendingMachineURL == "")
{
Debug.LogError("Token Vending Machine URL is not specified!");
}
StartCoroutine(GetTokenFromVendingMachine(clientName));
}
// Update is called once per frame
void Update() { }
public async void Main(ICredentialProvider authProvider)
{
try
{
loading = true;
// Set up the client
using ICacheClient client = new CacheClient(Configurations.Laptop.V1(), authProvider, TimeSpan.FromSeconds(60));
topicClient = new TopicClient(TopicConfigurations.Laptop.latest(), authProvider);
cts = new CancellationTokenSource();
// Subscribe and begin receiving messages
var subscribeResponse = await topicClient.SubscribeAsync(cacheName, TopicName);
StartCoroutine(SubscriptionCoroutine(subscribeResponse));
}
catch (Exception e)
{
Debug.LogError("Could not set up clients " + e.ToString());
}
}
IEnumerator SubscriptionCoroutine(TopicSubscribeResponse subscribeResponse)
{
switch (subscribeResponse)
{
case TopicSubscribeResponse.Subscription:
subscription = (TopicSubscribeResponse.Subscription)subscribeResponse;
Debug.Log("Successfully subscribed to topic " + TopicName);
loading = false;
try
{
var cancellableSubscription = subscription.WithCancellation(cts.Token);
var enumerator = cancellableSubscription.GetAsyncEnumerator();
while (!cts.IsCancellationRequested)
{
var message = enumerator.Current;
switch (message)
{
case TopicMessage.Binary:
Debug.Log("Received unexpected binary message from topic.");
break;
case TopicMessage.Text text:
Debug.Log(String.Format("Received string message from topic: {0}", text.Value));
GameCommand gameCommand = JsonUtility.FromJson<GameCommand>(text.Value);
HandleCommand(gameCommand);
break;
case TopicMessage.Error error:
Debug.LogError(String.Format("Received error message from topic: {0}", error.Message));
cts.Cancel();
break;
}
yield return null;
// wait for the next message
var awaitable = enumerator.MoveNextAsync().GetAwaiter();
while (!awaitable.IsCompleted)
{
if (cts.IsCancellationRequested)
{
break;
}
yield return null;
}
}
}
finally
{
Debug.Log("Subscription to the Topic has been cancelled");
}
break;
case TopicSubscribeResponse.Error error:
Debug.LogError(String.Format("Error subscribing to a topic: {0}", error.Message));
cts.Cancel();
break;
}
Dispose();
}
public void PublishMessage(string message)
{
Task.Run(async () =>
{
Debug.Log("About to publish message: " + message);
if (cts != null && !cts.IsCancellationRequested)
{
var publishResponse = await topicClient.PublishAsync(cacheName, TopicName, message);
switch (publishResponse)
{
case TopicPublishResponse.Success:
Debug.Log("Successfully published message " + message);
break;
case TopicPublishResponse.Error error:
Debug.LogError(String.Format("Error publishing a message to the topic: {0}", error.Message));
cts.Cancel();
break;
}
}
else
{
Debug.LogError("Could not publish message since cancellation already occurred");
}
});
}
private IEnumerator GetTokenFromVendingMachine(string name)
{
loading = true;
string uri = tokenVendingMachineURL + UnityWebRequest.EscapeURL(name);
using (UnityWebRequest webRequest = UnityWebRequest.Get(uri))
{
webRequest.SetRequestHeader("Cache-Control", "no-cache");
Debug.Log("Sending request to token vending machine...");
yield return webRequest.SendWebRequest();
if (webRequest.result == UnityWebRequest.Result.Success)
{
Debug.Log("Got result:" + webRequest.downloadHandler.text);
TokenVendingMachineResponse response = JsonUtility.FromJson<TokenVendingMachineResponse>(webRequest.downloadHandler.text);
Debug.Log("authToken: " + response.authToken);
DateTimeOffset dateTimeOffset = DateTimeOffset.FromUnixTimeSeconds((long)response.expiresAt);
Debug.Log("expiresAt: " + response.expiresAt + " (" + dateTimeOffset.ToLocalTime() + ")");
try
{
authProvider = new StringMomentoTokenProvider(response.authToken);
}
catch (InvalidArgumentException e)
{
Debug.LogError("Invalid auth token provided! " + e);
}
Main(authProvider);
}
else
{
Debug.LogError("Error trying to get token from vending machine: " + webRequest.error);
this.error = true;
}
}
}
private async Task EnsureCacheExistsAsync(ICacheClient client, string cacheName)
{
Debug.Log(String.Format("Creating cache {0} if it doesn't already exist.", cacheName));
var createCacheResponse = await client.CreateCacheAsync(cacheName);
Debug.Log(createCacheResponse);
switch (createCacheResponse)
{
case CreateCacheResponse.Success:
Debug.Log(String.Format("Created cache {0}.", cacheName));
break;
case CreateCacheResponse.CacheAlreadyExists:
Debug.Log(String.Format("Cache {0} already exists.", cacheName));
break;
case CreateCacheResponse.Error:
Debug.LogError(String.Format("Error creating cache: {0}", cacheName));
break;
}
}
void HandleCommand(GameCommand incomingCommand)
{
switch (incomingCommand.commandType)
{
case GameCommand.ENVIRONMENT:
if (incomingCommand.command.ToLower() == "jump")
{
Debug.Log(kart, kart);
Vector3 kartPos = kart.transform.position;
Vector3 kartDirection = kart.transform.forward;
Quaternion kartRotation = kart.transform.rotation;
float spawnDistance = 10;
Vector3 spawnPos = kartPos + kartDirection * spawnDistance;
Instantiate(jump, spawnPos, kartRotation);
}
if (incomingCommand.command.ToLower() == "stone")
{
Rigidbody rb = stone.GetComponent<Rigidbody>();
rb.velocity = new Vector3(0, 100);
Vector3 kartPos = kart.transform.position;
Vector3 kartDirection = kart.transform.forward;
Quaternion kartRotation = kart.transform.rotation;
float spawnDistance = 10;
kartPos[1] = kartPos[1] + 1;
Vector3 spawnPos = kartPos + kartDirection * spawnDistance;
Instantiate(stone, spawnPos, kartRotation);
}
if (incomingCommand.command.ToLower() == "wall")
{
Vector3 kartPos = kart.transform.position;
kartPos[0] = kartPos[0];// + 2.5F;
kartPos[1] = 1;
Vector3 kartDirection = kart.transform.forward;
Quaternion kartRotation = kart.transform.rotation;
float spawnDistance = 10;
Vector3 spawnPos = kartPos + kartDirection * spawnDistance;
Instantiate(wall, spawnPos, kartRotation);
}
if (incomingCommand.command.ToLower() == "alt-track")
{
defaultTrackEntrance.SetActive(false);
defaultTrackExit.SetActive(false);
altTrackEntrance.SetActive(true);
altTrackExit.SetActive(true);
}
if (incomingCommand.command.ToLower() == "default-track")
{
defaultTrackEntrance.SetActive(true);
defaultTrackExit.SetActive(true);
altTrackEntrance.SetActive(false);
altTrackExit.SetActive(false);
}
break;
case GameCommand.CAMERA:
break;
default:
break;
}
}
private void Dispose()
{
Debug.Log("Disposing subscription to topic " + TopicName);
subscription?.Dispose();
Debug.Log("Disposing cache and topic clients...");
topicClient?.Dispose();
topicClient?.Dispose();
}
private void OnDestroy()
{
Debug.Log("Cancelling tasks...");
cts?.Cancel();
Dispose();
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment