Created
October 14, 2024 23:24
-
-
Save FairlySadPanda/86aacac58fc705670716acd348b63f35 to your computer and use it in GitHub Desktop.
Event Bus pattern in UdonSharp for VRChat :)
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 UdonSharp; | |
using UnityEngine; | |
using VRC.SDK3.Data; | |
using VRC.SDKBase; | |
using VRC.Udon; | |
using Event = VRCBilliardsCE.Packages.com.vrcbilliards.vrcbce.Runtime.ScriptsV2.Core.Domain.Event; | |
namespace VRCBilliardsCE.Packages.com.vrcbilliards.vrcbce.Runtime.ScriptsV2.Core.Infrastructure | |
{ | |
// An event bus. This manager handles discrete events being issued to it from VRCBilliards, and dispatches these | |
// events to delegates. | |
public class EventBus : LocalBehaviour | |
{ | |
// Dictionary<string, Dictionary<UdonBehaviour, Dictionary<string, bool>>> | |
// A dictionary of registered events to registered UdonBehaviours to registered methods. | |
private DataDictionary registeredDelegates = new DataDictionary(); | |
// List<string> | |
private DataList eventBuffer = new DataList(); | |
public void LateUpdate() | |
{ | |
Tick(); | |
} | |
public bool Register(Event ev, UdonSharpBehaviour behaviour, string delegateMethod) | |
{ | |
Debug.Log($"registering {ev} with delegate {behaviour} and method {delegateMethod}"); | |
var eventKey = new DataToken(ev.ToString()); | |
var behaviourKey = new DataToken(behaviour); | |
var delegateKey = new DataToken(delegateMethod); | |
if (!registeredDelegates.ContainsKey(eventKey)) | |
{ | |
Debug.Log($"event {eventKey} has no dictionary: assigning"); | |
registeredDelegates.Add(eventKey, new DataDictionary()); | |
} | |
DataDictionary eventDict = registeredDelegates[eventKey].DataDictionary; | |
if (!eventDict.ContainsKey(behaviourKey)) | |
{ | |
Debug.Log($"event {eventKey} behaviour {behaviourKey} has no dictionary: assigning"); | |
eventDict.Add(behaviourKey, new DataDictionary()); | |
} | |
if (eventDict[behaviourKey].DataDictionary.ContainsKey(delegateKey)) | |
{ | |
Debug.Log($"event {eventKey} behaviour {behaviourKey} already registered function {delegateKey}: discarding registration"); | |
return false; | |
} | |
Debug.Log($"event {eventKey} behaviour {behaviourKey} registered function {delegateKey}"); | |
eventDict[behaviourKey].DataDictionary[delegateKey] = true; | |
return true; | |
} | |
public void Raise(Event ev) | |
{ | |
Debug.Log($"raising event ID {ev}"); | |
eventBuffer.Add(new DataToken(ev.ToString())); | |
} | |
public void RaiseImmediate(Event ev) | |
{ | |
HandleEvent(ev.ToString()); | |
} | |
private void Tick() | |
{ | |
RemoveDeadDelegates(); | |
// Drain the buffer. | |
foreach (var ev in eventBuffer.ToArray()) | |
{ | |
Debug.Log($"Handling event {ev}"); | |
HandleEvent(ev.String); | |
} | |
eventBuffer.Clear(); | |
} | |
private void RemoveDeadDelegates() | |
{ | |
// TODO: Assert that this roundabout way of having to access VRC data structures is intentional. | |
foreach (var value in registeredDelegates.GetValues().ToArray()) | |
{ | |
foreach (var key in value.DataDictionary.GetKeys().ToArray()) | |
{ | |
if (!Utilities.IsValid(key.Reference)) | |
{ | |
value.DataDictionary.Remove(key); | |
} | |
} | |
} | |
} | |
private void HandleEvent(string ev) | |
{ | |
var eventToken = registeredDelegates[ev]; | |
if (eventToken.Error != DataError.None) | |
{ | |
Debug.Log($"discarding {ev}: {eventToken.Error}"); | |
return; | |
} | |
var eventDictionary = eventToken.DataDictionary; | |
// Each key is an Udon(Sharp)Behaviour. | |
foreach (var key in eventDictionary.GetKeys().ToArray()) | |
{ | |
Debug.Log($"transmitting to delegates on {key.Reference}"); | |
// Each key is a function to call. | |
foreach (var funcName in eventDictionary[key].DataDictionary.GetKeys().ToArray()) | |
{ | |
Debug.Log($"transmitting to delegate {funcName.String} on {key.Reference}"); | |
((UdonBehaviour)key.Reference).SendCustomEvent(funcName.String); | |
} | |
} | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment