Created
August 5, 2024 12:41
-
-
Save recursivecodes/3ae9fd87753d28afacc5ba58741fae75 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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