diff --git a/Basis/Packages/com.basis.framework/Avatar/BasisAvatarFactory.cs b/Basis/Packages/com.basis.framework/Avatar/BasisAvatarFactory.cs index e307dff6c8..e68aeb54ce 100644 --- a/Basis/Packages/com.basis.framework/Avatar/BasisAvatarFactory.cs +++ b/Basis/Packages/com.basis.framework/Avatar/BasisAvatarFactory.cs @@ -471,7 +471,7 @@ public static async void DeleteLastAvatar(BasisPlayer Player) public static void SetupRemoteAvatar(BasisRemotePlayer Player) { Player.RemoteAvatarDriver.RemoteCalibration(Player); - Player.BasisAvatar.OnAvatarReady?.Invoke(false); + Player.BasisAvatar.NotifyAvatarReady(false); } /// @@ -480,7 +480,7 @@ public static void SetupRemoteAvatar(BasisRemotePlayer Player) public static void SetupLocalAvatar(BasisLocalPlayer Player) { Player.LocalAvatarDriver.InitialLocalCalibration(Player); - Player.BasisAvatar.OnAvatarReady?.Invoke(true); + Player.BasisAvatar.NotifyAvatarReady(true); BasisLocalAvatarDriver.CalibrationComplete?.Invoke(); } diff --git a/Basis/Packages/com.basis.sdk/Scripts/Basis Components/BasisAvatar.cs b/Basis/Packages/com.basis.sdk/Scripts/Basis Components/BasisAvatar.cs index 3c42e59a95..d1f33de03c 100644 --- a/Basis/Packages/com.basis.sdk/Scripts/Basis Components/BasisAvatar.cs +++ b/Basis/Packages/com.basis.sdk/Scripts/Basis Components/BasisAvatar.cs @@ -63,7 +63,12 @@ public class BasisAvatar : BasisContentBase /// /// True if this avatar is owned by the local player. /// - public bool IsOwnedLocally; + public bool IsOwnedLocally { get; set; } + + /// + /// True once avatar setup has completed and readiness callbacks have fired for this instance. + /// + public bool IsReady { get; private set; } /// /// Gets or sets the linked player ID. Setting also marks true. @@ -112,7 +117,56 @@ public bool TryGetLinkedPlayer(out ushort Id) /// /// Event triggered when the avatar is ready for further initialization or data queries. /// - public OnReady OnAvatarReady; + public OnReady OnAvatarReady {get; set;} + + /// + /// Marks this avatar as ready and notifies listeners with the owner locality. + /// + /// True when this avatar belongs to the local player. + public void NotifyAvatarReady(bool isOwner) + { + IsOwnedLocally = isOwner; + IsReady = true; + OnAvatarReady?.Invoke(isOwner); + } + + public static GameObject GetGameObject(object o) + { + GameObject currentGameobject = null; + if (o is GameObject go) + { + currentGameobject = go; + } + else if (o is Component c) + { + currentGameobject = c.gameObject; + } + + if (currentGameobject == null) + { + Debug.LogError($"Object {o} is not a GameObject or Component."); + return null; + } + + while (currentGameobject != null) + { + if (currentGameobject.TryGetComponent(out _)) + { + return currentGameobject; + } + + Transform parent = currentGameobject.transform.parent; + if (parent == null) + { + break; + } + + currentGameobject = parent.gameObject; + } + + Debug.LogError($"Object {o} is not part of an avatar hierarchy."); + return null; + } /// /// Processing options used when the avatar is processed. This is always null after the avatar is processed. diff --git a/Basis/Packages/com.basis.sdk/Settings/AvatarContentPoliceSelector.asset b/Basis/Packages/com.basis.sdk/Settings/AvatarContentPoliceSelector.asset index f1c2e87616..5cec2383db 100644 --- a/Basis/Packages/com.basis.sdk/Settings/AvatarContentPoliceSelector.asset +++ b/Basis/Packages/com.basis.sdk/Settings/AvatarContentPoliceSelector.asset @@ -191,4 +191,5 @@ MonoBehaviour: - UnityEngine.AudioEchoFilter - UnityEngine.AudioDistortionFilter - Cilbox.CilboxAvatarBasis + - Basis.Shims.BasisAvatarShim - Basis.Shims.BasisNetworkShim diff --git a/Basis/Packages/com.basis.shim/Shims/BasisAvatarShim.cs b/Basis/Packages/com.basis.shim/Shims/BasisAvatarShim.cs new file mode 100644 index 0000000000..05d04c6821 --- /dev/null +++ b/Basis/Packages/com.basis.shim/Shims/BasisAvatarShim.cs @@ -0,0 +1,209 @@ +using Basis.Scripts.BasisSdk; +using Cilbox; +using UnityEngine; +namespace Basis.Shims +{ + [DisallowMultipleComponent] + public sealed class BasisAvatarShim : CilboxShim + { + private static readonly int[] DefaultFaceVisemeMovement = new int[] { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }; + private static readonly int[] DefaultBlinkViseme = new int[] { -1 }; + + public delegate void OnReady(bool IsOwner); + public delegate void AvatarReadyEvent(bool isLocalPlayer); + + public Vector2 AvatarEyePosition; + public Vector2 AvatarMouthPosition; + public Vector3 AnimatorHumanScale = Vector3.one; + + private BasisAvatar avatar; + private bool isLocalPlayer; + private OnReady onAvatarReady; + + public OnReady OnAvatarReady + { + get => onAvatarReady; + set + { + OnReady previousHandlers = onAvatarReady; + onAvatarReady = value; + + // Always keep the newly assigned handlers. If the avatar is not ready yet, + // they remain registered and AvatarReady(...) will invoke them later. + bool shouldReplayImmediately = IsReady && onAvatarReady != null; + if (!shouldReplayImmediately) + { + return; + } + + ReplayNewReadyHandlers(previousHandlers, onAvatarReady, isLocalPlayer); + } + } + + public bool IsReady => avatar != null && avatar.IsReady; + public bool IsLocalPlayer => isLocalPlayer; + public bool IsOwner => isLocalPlayer; + public BasisAvatar Avatar => avatar; + public bool HasLinkedPlayer => avatar != null && avatar.HasLinkedPlayer; + public Animator Animator + { + get => avatar != null ? avatar.Animator : null; + } + + public SkinnedMeshRenderer FaceVisemeMesh + { + get => avatar != null ? avatar.FaceVisemeMesh : null; + } + + public SkinnedMeshRenderer FaceBlinkMesh + { + get => avatar != null ? avatar.FaceBlinkMesh : null; + } + + public int[] FaceVisemeMovement + { + get => avatar != null ? avatar.FaceVisemeMovement : DefaultFaceVisemeMovement; + } + + public int[] BlinkViseme + { + get => avatar != null ? avatar.BlinkViseme : DefaultBlinkViseme; + } + + public int laughterBlendTarget + { + get => avatar != null ? avatar.laughterBlendTarget : -1; + } + + public bool IsOwnedLocally + { + get => avatar != null ? avatar.IsOwnedLocally : isLocalPlayer; + } + + public float HumanScale + { + get => avatar != null ? avatar.HumanScale : 1; + set + { + if (avatar != null) + { + avatar.HumanScale = value; + } + } + } + + public ushort LinkedPlayerID + { + get => avatar != null ? avatar.LinkedPlayerID : (ushort)0; + } + + private void Awake() + { + avatar = GetComponent(); + if (avatar == null) + { + avatar = GetComponentInParent(true); + } + + if (avatar == null) + { + BasisDebug.LogError("[BasisAvatarShim] Could not resolve a BasisAvatar for this shim."); + return; + } + + SyncFieldsFromAvatar(); + avatar.OnAvatarReady -= AvatarReady; + avatar.OnAvatarReady += AvatarReady; + + if (avatar.IsReady) + { + AvatarReady(avatar.IsOwnedLocally); + } + } + + private void OnDestroy() + { + if (avatar != null) + { + avatar.OnAvatarReady -= AvatarReady; + } + } + + private void AvatarReady(bool ownerIsLocal) + { + isLocalPlayer = ownerIsLocal; + SyncFieldsFromAvatar(); + OnAvatarReady?.Invoke(ownerIsLocal); + } + + public bool TryGetLinkedPlayer(out ushort Id) + { + if (avatar == null) + { + Id = 0; + return false; + } + + return avatar.TryGetLinkedPlayer(out Id); + } + + private void NotifyAvatarReady(bool isOwner) + { + if (avatar == null) + { + return; + } + + avatar.NotifyAvatarReady(isOwner); + SyncFieldsFromAvatar(); + } + + private static void ReplayNewReadyHandlers(OnReady previousHandlers, OnReady currentHandlers, bool ownerIsLocal) + { + System.Collections.Generic.Dictionary existingHandlers = new System.Collections.Generic.Dictionary(); + if (previousHandlers != null) + { + foreach (System.Delegate handler in previousHandlers.GetInvocationList()) + { + if (existingHandlers.TryGetValue(handler, out int count)) + { + existingHandlers[handler] = count + 1; + } + else + { + existingHandlers[handler] = 1; + } + } + } + + foreach (System.Delegate handler in currentHandlers.GetInvocationList()) + { + if (existingHandlers.TryGetValue(handler, out int count) && count > 0) + { + existingHandlers[handler] = count - 1; + continue; + } + + ((OnReady)handler).Invoke(ownerIsLocal); + } + } + // Cilbox remaps BasisAvatar.GetGameObject(...) to this shim type, so forward to the real avatar helper. + public static GameObject GetGameObject(object o) + { + return BasisAvatar.GetGameObject(o); + } + + private void SyncFieldsFromAvatar() + { + if (avatar == null) + { + return; + } + + AvatarEyePosition = avatar.AvatarEyePosition; + AvatarMouthPosition = avatar.AvatarMouthPosition; + AnimatorHumanScale = avatar.AnimatorHumanScale; + isLocalPlayer = avatar.IsOwnedLocally; + } + } +} diff --git a/Basis/Packages/com.basis.shim/Shims/BasisAvatarShim.cs.meta b/Basis/Packages/com.basis.shim/Shims/BasisAvatarShim.cs.meta new file mode 100644 index 0000000000..4ae952353d --- /dev/null +++ b/Basis/Packages/com.basis.shim/Shims/BasisAvatarShim.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 2ec2ae2858ce4a9b9ef3f5169c12e719 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Basis/Packages/com.basis.shim/Shims/BasisCilboxBuildHook.cs b/Basis/Packages/com.basis.shim/Shims/BasisCilboxBuildHook.cs index 81557c088f..7e19d59afe 100644 --- a/Basis/Packages/com.basis.shim/Shims/BasisCilboxBuildHook.cs +++ b/Basis/Packages/com.basis.shim/Shims/BasisCilboxBuildHook.cs @@ -17,6 +17,13 @@ private static void Initialize() //Debug.Log("BasisCilboxBuildHook initialized."); BasisAssetBundlePipeline.OnBeforeBuildPrefab -= HandleBeforeBuildPrefab; BasisAssetBundlePipeline.OnBeforeBuildPrefab += HandleBeforeBuildPrefab; + BasisAvatarSDKInspector.OnBeforeTestInEditor -= HandleBeforeTestInEditor; + BasisAvatarSDKInspector.OnBeforeTestInEditor += HandleBeforeTestInEditor; + } + + private static void HandleBeforeTestInEditor(GameObject prefabRoot) + { + HandleBeforeBuildPrefab(prefabRoot, null); } private static void HandleBeforeBuildPrefab(GameObject prefabRoot, BasisAssetBundleObject settings) diff --git a/Basis/Packages/com.basis.shim/Shims/BasisShims.cs b/Basis/Packages/com.basis.shim/Shims/BasisShims.cs index ab8b15b503..451e21c20c 100644 --- a/Basis/Packages/com.basis.shim/Shims/BasisShims.cs +++ b/Basis/Packages/com.basis.shim/Shims/BasisShims.cs @@ -1,4 +1,5 @@ using Basis.Shims; +using Basis.Scripts.BasisSdk; using System; using UnityEngine.Networking; using UnityEngine; diff --git a/Basis/Packages/com.basis.shim/Shims/CilboxAvatarBasis.cs b/Basis/Packages/com.basis.shim/Shims/CilboxAvatarBasis.cs index 238e94340a..31fe1d4785 100644 --- a/Basis/Packages/com.basis.shim/Shims/CilboxAvatarBasis.cs +++ b/Basis/Packages/com.basis.shim/Shims/CilboxAvatarBasis.cs @@ -24,6 +24,9 @@ public class CilboxAvatarBasis : Cilbox "Basis.Scripts.BasisSdk.Players.BasisLocalPlayer", "Basis.Scripts.Networking.NetworkedAvatar.BasisNetworkPlayer", "Basis.Shims.BasisNet*", // Restrictive, only used as a type and for events. + "Basis.Shims.BasisAvatarShim", + "Basis.Shims.BasisAvatarShim+OnReady", + "Basis.Shims.BasisAvatarShim+AvatarReadyEvent", "Basis.Shims.BasisCilboxInstantiateShim", // Restrictive, only used as a type and for Instantiate methods. "Basis.Shims.BasisDebugPropsShim", // Restrictive, only used as a type and for logging methods. @@ -42,6 +45,7 @@ public class CilboxAvatarBasis : Cilbox "System.DateTime", "System.DateTimeOffset", "System.DayOfWeek", + "System.Delegate", "System.Diagnostics.Stopwatch", "System.Double", "System.Exception", @@ -88,6 +92,7 @@ public class CilboxAvatarBasis : Cilbox "UnityEngine.Rigidbody", "UnityEngine.RenderTexture", "UnityEngine.RenderTextureFormat", + "UnityEngine.SkinnedMeshRenderer", "UnityEngine.UI.*", "UnityEngine.Vector*", "UnityEngine.UI.InputField", @@ -107,6 +112,7 @@ public class CilboxAvatarBasis : Cilbox "UnityEngine.Vector*.y", "UnityEngine.Vector*.z", "UnityEngine.Vector*.w", + "UnityEngine.Quaternion*", // System fields "System.Array.*", @@ -114,6 +120,18 @@ public class CilboxAvatarBasis : Cilbox // Basis types + "Basis.Shims.BasisAvatarShim.Animator", + "Basis.Shims.BasisAvatarShim.FaceVisemeMesh", + "Basis.Shims.BasisAvatarShim.FaceBlinkMesh", + "Basis.Shims.BasisAvatarShim.AvatarEyePosition", + "Basis.Shims.BasisAvatarShim.AvatarMouthPosition", + "Basis.Shims.BasisAvatarShim.FaceVisemeMovement", + "Basis.Shims.BasisAvatarShim.BlinkViseme", + "Basis.Shims.BasisAvatarShim.laughterBlendTarget", + "Basis.Shims.BasisAvatarShim.AnimatorHumanScale", + "Basis.Shims.BasisAvatarShim.IsOwnedLocally", + "Basis.Shims.BasisAvatarShim.HumanScale", + "Basis.Scripts.BasisSdk.BasisProcessingAvatarOptions.doNotAutoRenameBones", "Basis.Scripts.BasisSdk.Interactions.BasisPickupInteractable.OnPickupUse", "Basis.Scripts.BasisSdk.Interactions.BasisInteractableObject.OnInteractStartEvent", "Basis.Scripts.BasisSdk.Interactions.BasisInteractableObject.OnInteractEndEvent", @@ -219,7 +237,7 @@ override public bool CheckMethodAllowed( out MethodInfo mi, Type declaringType, return true; } - public override bool GetComponentTypeOverride(string sType, out Type t) + public override bool GetTypeOverride(string sType, out Type t) { switch(sType) { @@ -242,6 +260,12 @@ public override bool GetComponentTypeOverride(string sType, out Type t) case "UnityEngine.Debug": t = typeof(Basis.Shims.BasisDebugPropsShim); return true; + case "Basis.Scripts.BasisSdk.BasisAvatar": + t = typeof(Basis.Shims.BasisAvatarShim); + return true; + case "Basis.Scripts.BasisSdk.BasisAvatar+OnReady": + t = typeof(Basis.Shims.BasisAvatarShim.OnReady); + return true; default: t = null; return false; diff --git a/Basis/Packages/com.basis.shim/Shims/CilboxPropBasis.cs b/Basis/Packages/com.basis.shim/Shims/CilboxPropBasis.cs index a2cf393599..61025b6340 100644 --- a/Basis/Packages/com.basis.shim/Shims/CilboxPropBasis.cs +++ b/Basis/Packages/com.basis.shim/Shims/CilboxPropBasis.cs @@ -40,6 +40,7 @@ public class CilboxPropBasis : Cilbox "System.DateTime", "System.DateTimeOffset", "System.DayOfWeek", + "System.Delegate", "System.Diagnostics.Stopwatch", "System.Double", "System.Exception", @@ -107,6 +108,7 @@ public class CilboxPropBasis : Cilbox "UnityEngine.Vector*.y", "UnityEngine.Vector*.z", "UnityEngine.Vector*.w", + "UnityEngine.Quaternion*", // System fields "System.Array.*", @@ -219,11 +221,10 @@ override public bool CheckMethodAllowed( out MethodInfo mi, Type declaringType, return true; } - public override bool GetComponentTypeOverride(string sType, out Type t) + public override bool GetTypeOverride(string sType, out Type t) { switch(sType) { - case "UnityEngine.Video.VideoPlayer": t = typeof(Basis.Shims.VideoPlayerShim); return true; diff --git a/Basis/Packages/com.basis.shim/Shims/CilboxSceneBasis.cs b/Basis/Packages/com.basis.shim/Shims/CilboxSceneBasis.cs index bf321b937b..9be7fe6ebf 100644 --- a/Basis/Packages/com.basis.shim/Shims/CilboxSceneBasis.cs +++ b/Basis/Packages/com.basis.shim/Shims/CilboxSceneBasis.cs @@ -135,6 +135,10 @@ public class CilboxSceneBasis : Cilbox "UnityEngine.Vector*.y", "UnityEngine.Vector*.z", "UnityEngine.Vector*.w", + "UnityEngine.Quaternion.x", + "UnityEngine.Quaternion.y", + "UnityEngine.Quaternion.z", + "UnityEngine.Quaternion.w", // System fields "System.Array.*", @@ -233,7 +237,7 @@ override public bool CheckMethodAllowed( out MethodInfo mi, Type declaringType, return true; } - public override bool GetComponentTypeOverride(string sType, out Type t) + public override bool GetTypeOverride(string sType, out Type t) { switch(sType) { diff --git a/Basis/Packages/com.cnlohr.cilbox/Cilbox.cs b/Basis/Packages/com.cnlohr.cilbox/Cilbox.cs index 19953109e2..84d1c79ecf 100644 --- a/Basis/Packages/com.cnlohr.cilbox/Cilbox.cs +++ b/Basis/Packages/com.cnlohr.cilbox/Cilbox.cs @@ -2047,7 +2047,7 @@ public Cilbox() abstract public bool CheckMethodAllowed( out MethodInfo mi, Type declaringType, String name, Serializee [] parametersIn, Serializee [] genericArgumentsIn, String fullSignature ); abstract public bool CheckTypeAllowed( String sType ); abstract public bool CheckFieldAllowed( String sType, String sFieldName ); - abstract public bool GetComponentTypeOverride( String sType, out Type t ); + abstract public bool GetTypeOverride( String sType, out Type t ); public delegate void CilboxDisabledEvent( Cilbox box, string reason ); diff --git a/Basis/Packages/com.cnlohr.cilbox/CilboxAvatar.cs b/Basis/Packages/com.cnlohr.cilbox/CilboxAvatar.cs index 6b33e9e71e..a444341120 100644 --- a/Basis/Packages/com.cnlohr.cilbox/CilboxAvatar.cs +++ b/Basis/Packages/com.cnlohr.cilbox/CilboxAvatar.cs @@ -115,7 +115,7 @@ override public bool CheckMethodAllowed( out MethodInfo mi, Type declaringType, return true; } - public override bool GetComponentTypeOverride(string sType, out Type t) + public override bool GetTypeOverride(string sType, out Type t) { t = null; return false; diff --git a/Basis/Packages/com.cnlohr.cilbox/CilboxProxy.cs b/Basis/Packages/com.cnlohr.cilbox/CilboxProxy.cs index 69e4540120..0e25a67d25 100644 --- a/Basis/Packages/com.cnlohr.cilbox/CilboxProxy.cs +++ b/Basis/Packages/com.cnlohr.cilbox/CilboxProxy.cs @@ -229,7 +229,7 @@ public void RuntimeProxyLoad() continue; } Type t = o.GetType(); - if(box.GetComponentTypeOverride( t.FullName, out Type overrideType )) { + if(box.GetTypeOverride( t.FullName, out Type overrideType )) { Debug.Log( $"RuntimeProxyLoad: Override {t.FullName} with {overrideType.FullName}" ); t = overrideType; if(typeof(CilboxShim).IsAssignableFrom(t) && fieldsObjects[i] is Component gameObjectComponent) diff --git a/Basis/Packages/com.cnlohr.cilbox/CilboxScene.cs b/Basis/Packages/com.cnlohr.cilbox/CilboxScene.cs index 765bc7c0ff..31b8a7b264 100644 --- a/Basis/Packages/com.cnlohr.cilbox/CilboxScene.cs +++ b/Basis/Packages/com.cnlohr.cilbox/CilboxScene.cs @@ -126,7 +126,7 @@ override public bool CheckMethodAllowed( out MethodInfo mi, Type declaringType, return true; } - public override bool GetComponentTypeOverride(string sType, out Type t) + public override bool GetTypeOverride(string sType, out Type t) { t = null; return false; diff --git a/Basis/Packages/com.cnlohr.cilbox/CilboxUsage.cs b/Basis/Packages/com.cnlohr.cilbox/CilboxUsage.cs index 32832584b8..84c7b8a9e0 100644 --- a/Basis/Packages/com.cnlohr.cilbox/CilboxUsage.cs +++ b/Basis/Packages/com.cnlohr.cilbox/CilboxUsage.cs @@ -67,8 +67,8 @@ public MethodBase GetNativeMethodFromTypeAndName( Type declaringType, String nam if( !bDisallowed ) goto disallowed; if( mi != null ) return mi; - // Replace any delegate creations with their proxies. - if( typeof(Delegate).IsAssignableFrom(declaringType) ) + // Replace delegate construction with cilbox-backed host delegates. + if( typeof(Delegate).IsAssignableFrom(declaringType) && name == ".ctor" ) { int argct = declaringType.GenericTypeArguments.Length; Type specific = typeof(CilboxPlatform); @@ -385,7 +385,7 @@ public Type GetNativeTypeFromSerializee( Serializee s ) String typeName = ses["n"].AsString(); String assemblyName = ses["a"].AsString(); if( IsCilboxInternalType( typeName ) ) return null; - if(box.GetComponentTypeOverride( typeName, out Type overrideType )) { + if(box.GetTypeOverride( typeName, out Type overrideType )) { Debug.Log( $"GetNativeTypeFromSerializee: Override {typeName} with {overrideType.FullName}" ); typeName = overrideType.FullName; assemblyName = overrideType.Assembly.GetName().Name; @@ -441,7 +441,7 @@ public String GetNativeTypeNameFromSerializee( Serializee s ) if( ses.TryGetValue( "ut", out utSer ) ) return GetNativeTypeNameFromSerializee( utSer ); String typeName = ses["n"].AsString(); if( IsCilboxInternalType( typeName ) ) return typeName; - if(box.GetComponentTypeOverride( typeName, out Type overrideType )) { + if(box.GetTypeOverride( typeName, out Type overrideType )) { Debug.Log( $"GetNativeTypeFromSerializee: Override {typeName} with {overrideType.FullName}" ); typeName = overrideType.FullName; } @@ -618,8 +618,25 @@ public class CilboxPlatform { // This is called only when creating a new action, not when it's called. // T is the delegate, not the arguments of the delegate. - static public object ProxyForGeneratingActions( CilboxProxy proxy, CilboxMethod method ) + static public object ProxyForGeneratingActions( CilboxProxy proxy, object methodOrDelegate ) { + if( methodOrDelegate is T existingTypedDelegate ) + { + return existingTypedDelegate; + } + + if( methodOrDelegate is Delegate existingDelegate && + typeof(T).IsAssignableFrom( existingDelegate.GetType() ) ) + { + return existingDelegate; + } + + if( methodOrDelegate is not CilboxMethod method ) + { + throw new ArgumentException( + $"ProxyForGeneratingActions expected a CilboxMethod or compatible delegate, got {methodOrDelegate?.GetType().FullName ?? "null"}" ); + } + CilboxPlatform.DelegateRepackage rp = new CilboxPlatform.DelegateRepackage(); rp.meth = method; rp.o = proxy;