Skip to content

Commit

Permalink
feat(auth): show object perms in console and inspector
Browse files Browse the repository at this point in the history
  • Loading branch information
mwfarb committed Oct 18, 2022
1 parent 18f75d4 commit f2d5a52
Show file tree
Hide file tree
Showing 10 changed files with 133 additions and 43 deletions.
27 changes: 18 additions & 9 deletions Editor/ThirdParty/PrettyHierarchy/Runtime/PrettyObject.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
// Modified from: https://github.com/NCEEGEE/PrettyHierarchy

using System;
using UnityEditor;
using UnityEngine;

Expand All @@ -8,6 +9,23 @@ namespace PrettyHierarchy
[DisallowMultipleComponent]
public class PrettyObject : MonoBehaviour
{
private bool hasPermissions;

private void updateText()
{
// for now, arena objects colored text consistently in Editor/ThirdParty/PrettyHierarchy/Editor/Utils/EditorColors.cs
if (EditorGUIUtility.isProSkin)
textColor = hasPermissions ? new Color32(0, 255, 0, 255) : new Color32(255, 165, 0, 255); // TODO dark theme=light green
else
textColor = hasPermissions ? new Color32(0, 128, 0, 255) : new Color32(255, 165, 0, 255); // TODO light theme=dark green
#if UNITY_EDITOR

EditorApplication.RepaintHierarchyWindow();
#endif
}

public bool HasPermissions { get { return hasPermissions; } set { hasPermissions = value; updateText(); } }

#if UNITY_EDITOR
//[Header("Background")]
//[SerializeField]
Expand Down Expand Up @@ -41,15 +59,6 @@ public class PrettyObject : MonoBehaviour
public TextAnchor Alignment { get { return alignment; } }
public bool TextDropShadow { get { return textDropShadow; } }

private void Awake()
{
// for now, arena objects colored text consistently in Editor/ThirdParty/PrettyHierarchy/Editor/Utils/EditorColors.cs
// if (EditorGUIUtility.isProSkin)
// textColor = new Color32(0, 255, 0, 255); //dark theme=light green
// else
// textColor = new Color32(0, 128, 0, 255); //light theme=dark green
}

private void OnValidate()
{
EditorApplication.RepaintHierarchyWindow();
Expand Down
2 changes: 1 addition & 1 deletion Runtime/ArenaCamera.cs
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ public bool PublishCreateUpdate()
// publish
msg.data = dataUnity;
string payload = JsonConvert.SerializeObject(msg);
ArenaClientScene.Instance.PublishCamera(msg.object_id, payload);
ArenaClientScene.Instance.PublishCamera(msg.object_id, payload, HasPermissions);
if (!created)
created = true;

Expand Down
41 changes: 41 additions & 0 deletions Runtime/ArenaCameraEditor.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/**
* Open source software under the terms in /LICENSE
* Copyright (c) 2021, The CONIX Research Center. All rights reserved.
*/

using UnityEditor;
using UnityEngine;

namespace ArenaUnity
{
#if UNITY_EDITOR
[CustomEditor(typeof(ArenaCamera))]
public class ArenaCameraEditor : Editor
{
public override void OnInspectorGUI()
{
ArenaCamera acobj = (ArenaCamera)target;

// edit authorization
GUILayout.BeginHorizontal("Box");
GUILayout.Label("Publish Permission:");
if (Application.isPlaying)
{
var authStyle = new GUIStyle(EditorStyles.label);
authStyle.normal.textColor = acobj.HasPermissions ? Color.green : (Color)new Color32(255, 165, 0, 255);
var authString = acobj.HasPermissions ? "A" : "Not a";
GUILayout.Label($"{authString}uthorized to publish changes", authStyle);
}
else
{
GUILayout.Label("Determined in playmode");
}
GUILayout.EndHorizontal();

GUI.enabled = !Application.isPlaying && acobj.HasPermissions;
DrawDefaultInspector();
GUI.enabled = true;
}
}
#endif
}
11 changes: 11 additions & 0 deletions Runtime/ArenaCameraEditor.cs.meta

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

41 changes: 23 additions & 18 deletions Runtime/ArenaClientScene.cs
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,8 @@ protected override void Awake()
/// </summary>
public string sceneUrl { get; private set; }

internal bool sceneObjectRights { get; private set; }

private string sceneTopic = null;
internal Dictionary<string, GameObject> arenaObjs = new Dictionary<string, GameObject>();
internal Dictionary<string, GameObject> childObjs = new Dictionary<string, GameObject>();
Expand Down Expand Up @@ -171,7 +173,6 @@ public IEnumerator ConnectArena()
sceneTopic = $"{realm}/s/{namespaceName}/{sceneName}";
sceneUrl = $"https://{brokerAddress}/{namespaceName}/{sceneName}";
}
bool sceneObjectRights = false;
dynamic perms = JsonConvert.DeserializeObject(permissions);
foreach (dynamic pubperm in perms.publ)
{
Expand All @@ -184,18 +185,19 @@ public IEnumerator ConnectArena()
if ((cam.name == Camera.main.name || camlist.Length == 1) && !foundFirstCam)
{
// publish main/selected camera
cam.HasPermissions = true; // TODO: client connection always gets at least one
cam.userid = userid;
cam.camid = camid;
foundFirstCam = true;
}
else
{
cam.HasPermissions = sceneObjectRights;
// TODO: fix: other cameras are auto-generated, and account must have all scene rights
// if (!sceneObjectRights)
// {
// LogAndExit($"Using more than one ArenaCamera requires full scene permissions. Login with an Editor or Owner account with write permissions for this scene.");
// yield break;
// }
if (!sceneObjectRights)
{
Debug.LogWarning($"Using more than one ArenaCamera requires full scene permissions. Only one camera will be published.");
}
var random = UnityEngine.Random.Range(0, 100000000);
cam.userid = $"{random:D8}_unity";
cam.camid = $"camera_{random:D8}_unity";
Expand Down Expand Up @@ -258,7 +260,7 @@ protected override void Update()
persist = true,
};
string payload = JsonConvert.SerializeObject(msg);
PublishObject(msg.object_id, payload);
PublishObject(msg.object_id, payload, sceneObjectRights);
}
}
#endif
Expand Down Expand Up @@ -801,42 +803,42 @@ private IEnumerator HttpRequestRaw(string url)
/// <summary>
/// Object changes are published using a ClientId + ObjectId topic, a user must have permissions for the entire scene graph.
/// </summary>
public void PublishObject(string object_id, string msgJson)
public void PublishObject(string object_id, string msgJson, bool hasPermissions = true)
{
dynamic msg = JsonConvert.DeserializeObject(msgJson);
msg.timestamp = GetTimestamp();
PublishSceneMessage($"{sceneTopic}/{client.ClientId}/{object_id}", JsonConvert.SerializeObject(msg));
PublishSceneMessage($"{sceneTopic}/{client.ClientId}/{object_id}", JsonConvert.SerializeObject(msg), hasPermissions);
}

/// <summary>
/// Camera presence changes are published using a ObjectId-only topic, a user might only have permissions for their camid.
/// </summary>
public void PublishCamera(string object_id, string msgJson)
public void PublishCamera(string object_id, string msgJson, bool hasPermissions = true)
{
dynamic msg = JsonConvert.DeserializeObject(msgJson);
msg.timestamp = GetTimestamp();
PublishSceneMessage($"{sceneTopic}/{object_id}", JsonConvert.SerializeObject(msg));
PublishSceneMessage($"{sceneTopic}/{object_id}", JsonConvert.SerializeObject(msg), hasPermissions);
}

/// <summary>
/// Camera events are published using a ObjectId-only topic, a user might only have permissions for their camid.
/// </summary>
public void PublishEvent(string object_id, string eventType, string msgJsonData)
public void PublishEvent(string object_id, string eventType, string msgJsonData, bool hasPermissions = true)
{
dynamic msg = new ExpandoObject();
msg.object_id = camid;
msg.action = "clientEvent";
msg.type = eventType;
msg.data = JsonConvert.DeserializeObject(msgJsonData);
msg.timestamp = GetTimestamp();
PublishSceneMessage($"{sceneTopic}/{object_id}", JsonConvert.SerializeObject(msg));
PublishSceneMessage($"{sceneTopic}/{object_id}", JsonConvert.SerializeObject(msg), hasPermissions);
}

private void PublishSceneMessage(string topic, string msg)
private void PublishSceneMessage(string topic, string msg, bool hasPermissions)
{
byte[] payload = System.Text.Encoding.UTF8.GetBytes(msg);
Publish(topic, payload);
LogMessage("Sent", JsonConvert.DeserializeObject(msg));
LogMessage("Sending", JsonConvert.DeserializeObject(msg), hasPermissions);
}

private static string GetTimestamp()
Expand Down Expand Up @@ -918,7 +920,7 @@ private IEnumerator ProcessArenaMessage(dynamic msg, object menuCommand = null)
}
}

private void LogMessage(string dir, dynamic msg)
private void LogMessage(string dir, dynamic msg, bool hasPermissions = true)
{
// determine logging level
if (!Convert.ToBoolean(msg.persist) && !logMqttNonPersist) return;
Expand All @@ -928,7 +930,10 @@ private void LogMessage(string dir, dynamic msg)
if (!logMqttObjects) return;
}
if (msg.action == "clientEvent" && !logMqttEvents) return;
Debug.Log($"{dir}: {JsonConvert.SerializeObject(msg)}");
if (hasPermissions)
Debug.Log($"{dir}: {JsonConvert.SerializeObject(msg)}");
else
Debug.LogWarning($"Permissions FAILED {dir}: {JsonConvert.SerializeObject(msg)}");
}

protected override void OnApplicationQuit()
Expand All @@ -942,7 +947,7 @@ protected override void OnApplicationQuit()
action = "delete",
};
string delCamMsg = JsonConvert.SerializeObject(msg);
PublishCamera(camid, delCamMsg);
PublishCamera(camid, delCamMsg, sceneObjectRights);
}
base.OnApplicationQuit();
}
Expand Down
7 changes: 4 additions & 3 deletions Runtime/ArenaMenu.cs
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,8 @@ private static void PublishPrimitive(MenuCommand menuCommand, string object_type
Vector3 cameraPoint = cam.transform.position + cam.transform.forward * distance;

dynamic msg = new ExpandoObject();
if (ArenaClientScene.Instance.arenaObjs.ContainsKey(object_id))
var client = ArenaClientScene.Instance;
if (client.arenaObjs.ContainsKey(object_id))
object_id = $"{object_id}-{UnityEngine.Random.Range(0, 1000000)}";
msg.object_id = object_id;
msg.action = "create";
Expand All @@ -158,8 +159,8 @@ private static void PublishPrimitive(MenuCommand menuCommand, string object_type
data.material = material;
msg.data = data;
string payload = JsonConvert.SerializeObject(msg);
ArenaClientScene.Instance.PublishObject(msg.object_id, payload); // remote
ArenaClientScene.Instance.ProcessMessage(payload, menuCommand); // local
client.PublishObject(msg.object_id, payload, client.sceneObjectRights); // remote
client.ProcessMessage(payload, menuCommand); // local
}
#endif
}
Expand Down
6 changes: 3 additions & 3 deletions Runtime/ArenaMqttClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -137,17 +137,17 @@ protected override void DecodeMessage(string topic, byte[] message)

public void Publish(string topic, byte[] payload)
{
client.Publish(topic, payload);
if (client != null) client.Publish(topic, payload);
}

public void Subscribe(string[] topics)
{
client.Subscribe(topics, new byte[] { MqttMsgBase.QOS_LEVEL_AT_MOST_ONCE });
if (client != null) client.Subscribe(topics, new byte[] { MqttMsgBase.QOS_LEVEL_AT_MOST_ONCE });
}

public void Unsubscribe(string[] topics)
{
client.Unsubscribe(topics);
if (client != null) client.Unsubscribe(topics);
}

protected void OnDestroy()
Expand Down
11 changes: 8 additions & 3 deletions Runtime/ArenaObject.cs
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@ public void OnEnable()

void Start()
{
// TODO: consider how inactive objects react to find here, might need to use arenaObjs array

// runtime created arena objects still need to be checked for name uniqueness
bool found = false;
foreach (var aobj in FindObjectsOfType<ArenaObject>())
Expand Down Expand Up @@ -84,6 +86,9 @@ void Update()
if (!ArenaClientScene.Instance || ArenaClientScene.Instance.transformPublishInterval == 0 ||
Time.frameCount % ArenaClientScene.Instance.transformPublishInterval != 0)
return;

HasPermissions = ArenaClientScene.Instance.sceneObjectRights;

if (transform.hasChanged || meshChanged)
{
//TODO: prevent child objects of parent.transform.hasChanged = true from publishing unnecessarily
Expand Down Expand Up @@ -113,7 +118,7 @@ private void HandleRename()
persist = persist,
};
string payload = JsonConvert.SerializeObject(msg);
ArenaClientScene.Instance.PublishObject(msg.object_id, payload);
ArenaClientScene.Instance.PublishObject(msg.object_id, payload, HasPermissions);
// add new object with new name, it pubs
created = false;
transform.hasChanged = true;
Expand Down Expand Up @@ -193,7 +198,7 @@ public bool PublishCreateUpdate(bool transformOnly = false)
msg.data = transformOnly ? dataUnity : updatedData;
jsonData = JsonConvert.SerializeObject(updatedData, Formatting.Indented);
string payload = JsonConvert.SerializeObject(msg);
ArenaClientScene.Instance.PublishObject(msg.object_id, payload);
ArenaClientScene.Instance.PublishObject(msg.object_id, payload, HasPermissions);
if (!created)
created = true;

Expand All @@ -209,7 +214,7 @@ internal void PublishJson()
msg.persist = persist;
msg.data = JsonConvert.DeserializeObject(jsonData);
string payload = JsonConvert.SerializeObject(msg);
ArenaClientScene.Instance.PublishObject(msg.object_id, payload); // remote
ArenaClientScene.Instance.PublishObject(msg.object_id, payload, HasPermissions); // remote
ArenaClientScene.Instance.ProcessMessage(payload); // local
}

Expand Down
7 changes: 4 additions & 3 deletions Runtime/ArenaObjectAddUrlWindow.cs
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,8 @@ protected void OnGUI()
Vector3 cameraPoint = cam.transform.position + cam.transform.forward * distance;

dynamic msg = new ExpandoObject();
if (ArenaClientScene.Instance.arenaObjs.ContainsKey(object_id))
var client = ArenaClientScene.Instance;
if (client.arenaObjs.ContainsKey(object_id))
object_id = $"{object_id}-{UnityEngine.Random.Range(0, 1000000)}";
msg.object_id = object_id;
msg.action = "create";
Expand All @@ -59,8 +60,8 @@ protected void OnGUI()
data.position = ArenaUnity.ToArenaPosition(cameraPoint);
msg.data = data;
string payload = JsonConvert.SerializeObject(msg);
ArenaClientScene.Instance.PublishObject(msg.object_id, payload); // remote
ArenaClientScene.Instance.ProcessMessage(payload, menuCommand); // local
client.PublishObject(msg.object_id, payload, client.sceneObjectRights); // remote
client.ProcessMessage(payload, menuCommand); // local
Close();
}

Expand Down
23 changes: 20 additions & 3 deletions Runtime/ArenaObjectEditor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
* Copyright (c) 2021, The CONIX Research Center. All rights reserved.
*/

using System;
using UnityEditor;
using UnityEngine;

Expand All @@ -18,17 +17,35 @@ public override void OnInspectorGUI()
ArenaObject aobj = (ArenaObject)target;

// add button to publish unity changes
GUI.enabled = aobj.messageType == "object";
GUI.enabled = aobj.HasPermissions && aobj.messageType == "object";
if (GUILayout.Button("Publish Unity Data"))
{
aobj.PublishCreateUpdate();
}
GUI.enabled = true;

// edit authorization
GUILayout.BeginHorizontal("Box");
GUILayout.Label("Publish Permission:");
if (Application.isPlaying)
{
var authStyle = new GUIStyle(EditorStyles.label);
authStyle.normal.textColor = aobj.HasPermissions ? Color.green : (Color)new Color32(255, 165, 0, 255);
var authString = aobj.HasPermissions ? "A" : "Not a";
GUILayout.Label($"{authString}uthorized to publish changes", authStyle);
}
else
{
GUILayout.Label("Determined in playmode");
}
GUILayout.EndHorizontal();

GUI.enabled = !Application.isPlaying && aobj.HasPermissions;
DrawDefaultInspector();
GUI.enabled = true;

// add button to publish manual json data changes if valid
GUI.enabled = aobj.isJsonValidated;
GUI.enabled = aobj.HasPermissions && aobj.isJsonValidated;
if (GUILayout.Button("Publish Json Data"))
{
aobj.PublishJson();
Expand Down

0 comments on commit f2d5a52

Please sign in to comment.