Best Practices for Realtime UI updates in Unity #126
-
I'm using the library in Unity and it's working pretty well! My question might be more Unity oriented, but since i'm specifically having this problem only with this library, I figured I would ask here. Basically I'm subscribing to some Realtime channels in order to update my game's UI when the DB changes. The problem i'm facing is that Unity can't update the main thread from a different thread which seems to be the case whenever I get updates from the socket. The way I'm currently solving it is like this: public class SupabaseManager : MonoBehaviour
{
public UnityAction<string, string> onMatchRequest;
private string SupabaseURL = "";
private string SupabasePublicKey = "";
private Client _supabase;
private RealtimeBroadcast<UserBroadcast> _userBroadcast;
private static SupabaseManager instance;
private SynchronizationContext _mainThreadContext;
private readonly Dictionary<string, RealtimeChannel> channels = new Dictionary<string, RealtimeChannel>();
private void Awake()
{
instance = this;
SupabaseURL = NetworkData.GetSupabaseUrl();
SupabasePublicKey = NetworkData.GetSupabasePublicKey();
}
async void Start()
{
_mainThreadContext = SynchronizationContext.Current;
string url = SupabaseURL;
string key = SupabasePublicKey;
if (_supabase == null)
{
var options = new SupabaseOptions
{
Headers = new Dictionary<string, string>
{
["Authorization"] = "Bearer " + ApiClient.Get().AccessToken
}
};
_supabase = new Client(url, key, options);
await _supabase.InitializeAsync();
}
_supabase.Realtime.AddDebugHandler((sender, message, exception) => Debug.Log(message));
_supabase.Realtime.SetAuth(ApiClient.Get().AccessToken);
var realtimeClient = await _supabase.Realtime.ConnectAsync();
}
private void OnDestroy()
{
Debug.Log("SupabaseManager OnDestroy");
if (_supabase != null)
{
foreach (var channel in channels.Values)
{
_supabase.Realtime.Remove(channel);
}
channels.Clear();
_supabase.Realtime.RemoveStateChangedHandler(SocketEventHandler);
_supabase.Realtime.Disconnect();
_supabase = null;
}
}
public delegate void TournamentChangedHandler(Tournament tournament);
public async void SubscribeToTournamentChanges(int tournament_id, TournamentChangedHandler handler)
{
var channelName = "tournaments:" + tournament_id;
channels.TryGetValue(channelName, out RealtimeChannel channel);
if (channel == null)
{
channel = _supabase.Realtime.Channel(channelName);
channels.Add(channelName, channel);
channel.Register(new PostgresChangesOptions("public", "aw-tournaments", ListenType.Updates, "id=eq." + tournament_id ));
channel.AddPostgresChangeHandler(ListenType.Updates, (_, change) =>
{
if (change.Payload?.Data?.Table == "aw-tournaments")
{
//Tournament data changed
var model = change.Model<Tournament>();
if (model != null)
{
_mainThreadContext.Post(_ => handler.Invoke(model), null);
}
}
});
}
await channel.Subscribe();
}
public void UnsubscribeToTournamentChanges(int tournament_id)
{
var channelName = "tournaments:" + tournament_id;
channels.TryGetValue(channelName, out RealtimeChannel channel);
if (channel != null)
{
channel.Unsubscribe();
}
}
public static SupabaseManager Get()
{
return instance;
}
} I basically have a It seems to be working, but not sure if this is the best way to do this? Would love to hear other options or validation that this is correct, since I don't have a lot of experience with multi threading. Thanks! |
Beta Was this translation helpful? Give feedback.
Replies: 1 comment 2 replies
-
Yeah, it's a PITA. Here's the basic pattern for what I wind up doing. Let's say the thing I'm interested in is messages from the server. So I'd have a var incoming = new List in my MonoBehavior. Then I add messages to this List via the channel updates. Then I have an Update() method that looks for messages and removes them, which is fine as Update always runs on the UI thread. I use UniTask to make sure the async/await is managed properly WRT to setting up the context. I like your version as well, it's probably more technically accurate but I think it's basically doing the same thing WRT setting up the sync context as UniTask & Update. |
Beta Was this translation helpful? Give feedback.
Yeah, it's a PITA.
Here's the basic pattern for what I wind up doing. Let's say the thing I'm interested in is messages from the server. So I'd have a var incoming = new List in my MonoBehavior. Then I add messages to this List via the channel updates. Then I have an Update() method that looks for messages and removes them, which is fine as Update always runs on the UI thread. I use UniTask to make sure the async/await is managed properly WRT to setting up the context.
I like your version as well, it's probably more technically accurate but I think it's basically doing the same thing WRT setting up the sync context as UniTask & Update.