Skip to content

Commit b952ca7

Browse files
authored
Merge pull request #84 from MerlinVR/sync-beta
Sync beta -> Master
2 parents 2cb4a66 + 53b8fbc commit b952ca7

31 files changed

+356
-198
lines changed

Assets/UdonSharp/Editor/Editors/UdonSharpComponentExtensions.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,12 @@ public static UdonSharpBehaviour AddUdonSharpComponent(this GameObject gameObjec
145145
UdonSharpProgramAsset programAsset = UdonSharpProgramAsset.GetProgramAssetForClass(type);
146146

147147
udonBehaviour.programSource = programAsset;
148+
#pragma warning disable CS0618 // Type or member is obsolete
149+
udonBehaviour.SynchronizePosition = false;
148150
udonBehaviour.AllowCollisionOwnershipTransfer = false;
151+
#pragma warning restore CS0618 // Type or member is obsolete
152+
153+
udonBehaviour.Reliable = programAsset.behaviourSyncMode == BehaviourSyncMode.Manual;
149154

150155
SerializedObject componentAsset = new SerializedObject(udonBehaviour);
151156
SerializedProperty serializedProgramAssetProperty = componentAsset.FindProperty("serializedProgramAsset");

Assets/UdonSharp/Editor/Editors/UdonSharpGUI.cs

Lines changed: 122 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@
1717
namespace UdonSharpEditor
1818
{
1919
#region Beta SDK sync mode menu editor
20-
#if UDON_BETA_SDK
2120
internal class SyncModeMenu : EditorWindow
2221
{
2322
static SyncModeMenu menu;
@@ -228,7 +227,6 @@ void ShowDropDown(Rect controlRect, Vector2 size)
228227
showAsDropDownMethod.Invoke(this, new object[] { controlRect, size, popupLocationArray });
229228
}
230229
}
231-
#endif
232230
#endregion
233231

234232
public static class UdonSharpGUI
@@ -1342,40 +1340,96 @@ public static bool DrawProgramSource(UnityEngine.Object target, bool drawScript
13421340

13431341
static readonly GUIContent ownershipTransferOnCollisionContent = new GUIContent("Allow Ownership Transfer on Collision",
13441342
"Transfer ownership on collision, requires a Collision component on the same game object");
1345-
1346-
#if UDON_BETA_SDK
1343+
13471344
static MethodInfo dropdownButtonMethod;
1348-
#endif
13491345

13501346
internal static void DrawSyncSettings(UdonBehaviour behaviour)
13511347
{
1352-
#if UDON_BETA_SDK
13531348
UdonSharpProgramAsset programAsset = (UdonSharpProgramAsset)behaviour.programSource;
13541349

1355-
bool allowsSyncConfig = programAsset.behaviourSyncMode == BehaviourSyncMode.Any;
1350+
EditorGUI.BeginDisabledGroup(Application.isPlaying);
13561351

1357-
EditorGUI.BeginDisabledGroup(EditorApplication.isPlaying || !allowsSyncConfig);
1352+
UdonBehaviour[] behavioursOnObject = behaviour.GetComponents<UdonBehaviour>();
13581353

1359-
Rect syncMethodRect = EditorGUILayout.GetControlRect();
1360-
int id = GUIUtility.GetControlID("DropdownButton".GetHashCode(), FocusType.Keyboard, syncMethodRect);
1361-
Rect dropdownRect = EditorGUI.PrefixLabel(syncMethodRect, id, new GUIContent("Synchronization Method"));
1354+
// Sanity checking for mixed sync modes
1355+
if (behavioursOnObject.Length > 1)
1356+
{
1357+
bool hasContinuousSync = false;
1358+
bool hasReliableSync = false;
1359+
1360+
foreach (UdonBehaviour otherBehaviour in behavioursOnObject)
1361+
{
1362+
if (otherBehaviour.Reliable)
1363+
hasReliableSync = true;
1364+
else
1365+
hasContinuousSync = true;
1366+
}
13621367

1363-
if (dropdownButtonMethod == null)
1364-
dropdownButtonMethod = typeof(EditorGUI).GetMethod("DropdownButton", BindingFlags.NonPublic | BindingFlags.Static, null, new Type[] { typeof(int), typeof(Rect), typeof(GUIContent), typeof(GUIStyle) }, null);
1368+
if (hasContinuousSync && hasReliableSync)
1369+
{
1370+
if (programAsset.behaviourSyncMode == BehaviourSyncMode.NoVariableSync)
1371+
EditorGUILayout.HelpBox("NoVariableSync mode uses Continuous sync mode internally. You are mixing sync methods between UdonBehaviours on the same game object, this will cause all behaviours to use the sync method of the last component on the game object.", MessageType.Error);
1372+
else
1373+
EditorGUILayout.HelpBox("You are mixing sync methods between UdonBehaviours on the same game object, this will cause all behaviours to use the sync method of the last component on the game object.", MessageType.Error);
1374+
}
1375+
}
13651376

1366-
if ((bool)dropdownButtonMethod.Invoke(null, new object[] { id, dropdownRect, new GUIContent(behaviour.Reliable ? "Manual" : "Continuous"), EditorStyles.miniPullDown }))
1377+
// Dropdown for the sync settings
1378+
if (programAsset.behaviourSyncMode != BehaviourSyncMode.NoVariableSync)
13671379
{
1368-
SyncModeMenu.Show(syncMethodRect, new UdonBehaviour[] { behaviour });
1380+
bool allowsSyncConfig = programAsset.behaviourSyncMode == BehaviourSyncMode.Any;
1381+
1382+
EditorGUI.BeginDisabledGroup(EditorApplication.isPlaying || !allowsSyncConfig);
1383+
1384+
Rect syncMethodRect = EditorGUILayout.GetControlRect();
1385+
int id = GUIUtility.GetControlID("DropdownButton".GetHashCode(), FocusType.Keyboard, syncMethodRect);
1386+
GUIContent dropdownContent = allowsSyncConfig ? new GUIContent("Synchronization Method") : new GUIContent("Synchronization Method", "This sync mode is currently set by the UdonBehaviourSyncMode attribute on the script");
13691387

1370-
GUIUtility.ExitGUI();
1388+
Rect dropdownRect = EditorGUI.PrefixLabel(syncMethodRect, id, dropdownContent);
1389+
1390+
if (dropdownButtonMethod == null)
1391+
dropdownButtonMethod = typeof(EditorGUI).GetMethod("DropdownButton", BindingFlags.NonPublic | BindingFlags.Static, null, new Type[] { typeof(int), typeof(Rect), typeof(GUIContent), typeof(GUIStyle) }, null);
1392+
1393+
if ((bool)dropdownButtonMethod.Invoke(null, new object[] { id, dropdownRect, new GUIContent(behaviour.Reliable ? "Manual" : "Continuous"), EditorStyles.miniPullDown }))
1394+
{
1395+
SyncModeMenu.Show(syncMethodRect, new UdonBehaviour[] { behaviour });
1396+
1397+
GUIUtility.ExitGUI();
1398+
}
1399+
1400+
EditorGUI.EndDisabledGroup();
1401+
1402+
bool newReliableState = behaviour.Reliable;
1403+
1404+
// Handle auto setting of sync mode if the component has just been created
1405+
if (programAsset.behaviourSyncMode == BehaviourSyncMode.Continuous && behaviour.Reliable)
1406+
newReliableState = false;
1407+
else if (programAsset.behaviourSyncMode == BehaviourSyncMode.Manual && !behaviour.Reliable)
1408+
newReliableState = true;
1409+
1410+
if (newReliableState != behaviour.Reliable)
1411+
{
1412+
Undo.RecordObject(behaviour, "Update sync mode");
1413+
behaviour.Reliable = newReliableState;
1414+
}
13711415
}
13721416

1373-
EditorGUI.EndDisabledGroup();
1374-
#else
1375-
EditorGUI.BeginChangeCheck();
1417+
// Validate that we don't have a VRC Object Sync on continuous synced objects since it is not valid
1418+
if (behaviour.Reliable)
1419+
{
1420+
var objSync = behaviour.GetComponent<VRC.SDK3.Components.VRCObjectSync>();
13761421

1377-
EditorGUI.BeginDisabledGroup(Application.isPlaying);
1378-
bool newSyncPos = EditorGUILayout.Toggle("Synchronize Position", behaviour.SynchronizePosition);
1422+
#pragma warning disable CS0618 // Type or member is obsolete
1423+
if (behaviour.SynchronizePosition)
1424+
#pragma warning restore CS0618 // Type or member is obsolete
1425+
EditorGUILayout.HelpBox("Manual sync cannot be used on GameObjects with Position Sync", MessageType.Error);
1426+
else if (objSync)
1427+
EditorGUILayout.HelpBox("Manual sync cannot be used on GameObjects with VRC Object Sync", MessageType.Error);
1428+
}
1429+
1430+
// Position sync upgrade warnings & collision transfer handling
1431+
#pragma warning disable CS0618 // Type or member is obsolete
1432+
EditorGUI.BeginChangeCheck();
13791433
bool newCollisionTransfer = behaviour.AllowCollisionOwnershipTransfer;
13801434
if (behaviour.GetComponent<Collider>() != null)
13811435
{
@@ -1384,24 +1438,64 @@ internal static void DrawSyncSettings(UdonBehaviour behaviour)
13841438
if (newCollisionTransfer)
13851439
EditorGUILayout.HelpBox("Collision transfer is currently bugged and can cause network spam that lags your world, use at your own risk.", MessageType.Warning);
13861440
}
1387-
else if(newCollisionTransfer)
1441+
else if (newCollisionTransfer)
13881442
{
13891443
newCollisionTransfer = false;
13901444

13911445
GUI.changed = true;
13921446
}
1393-
EditorGUI.EndDisabledGroup();
13941447

13951448
if (EditorGUI.EndChangeCheck())
13961449
{
1397-
Undo.RecordObject(behaviour, "Change sync setting");
1398-
behaviour.SynchronizePosition = newSyncPos;
1450+
Undo.RecordObject(behaviour, "Changed ownership transfer");
13991451
behaviour.AllowCollisionOwnershipTransfer = newCollisionTransfer;
1452+
}
1453+
1454+
// For now we'll do a warning, later on we may add a validation pass that just converts everything automatically
1455+
if (behaviour.SynchronizePosition)
1456+
{
1457+
var objectSync = behaviour.GetComponent<VRC.SDK3.Components.VRCObjectSync>();
1458+
1459+
if (objectSync)
1460+
{
1461+
if (behaviour.AllowCollisionOwnershipTransfer && !objectSync.AllowCollisionOwnershipTransfer)
1462+
{
1463+
Undo.RecordObject(behaviour, "Object sync owner transfer");
1464+
objectSync.AllowCollisionOwnershipTransfer = true;
1465+
}
1466+
1467+
Undo.RecordObject(behaviour, "Change sync position");
1468+
behaviour.SynchronizePosition = false;
1469+
}
1470+
else
1471+
{
1472+
EditorGUILayout.HelpBox("This behaviour has sync position enabled on it, sync position is deprecated and you should now use the VRC Object Sync script.", MessageType.Warning);
1473+
if (GUILayout.Button("Switch to VRC Object Sync"))
1474+
{
1475+
var newObjSync = Undo.AddComponent<VRC.SDK3.Components.VRCObjectSync>(behaviour.gameObject);
1476+
while (UnityEditorInternal.ComponentUtility.MoveComponentUp(newObjSync)) { }
1477+
1478+
UdonBehaviour[] behaviours = behaviour.GetComponents<UdonBehaviour>();
1479+
1480+
bool usesCollisionTransfer = false;
1481+
1482+
foreach (UdonBehaviour otherBehaviour in behaviours)
1483+
{
1484+
usesCollisionTransfer |= otherBehaviour.AllowCollisionOwnershipTransfer;
1485+
1486+
Undo.RecordObject(behaviour, "Convert to VRC Object Sync");
1487+
behaviour.SynchronizePosition = false;
1488+
behaviour.AllowCollisionOwnershipTransfer = false;
1489+
}
14001490

1401-
if (PrefabUtility.IsPartOfPrefabInstance(behaviour))
1402-
PrefabUtility.RecordPrefabInstancePropertyModifications(behaviour);
1491+
Undo.RecordObject(newObjSync, "Object sync collision transfer");
1492+
newObjSync.AllowCollisionOwnershipTransfer = newCollisionTransfer;
1493+
}
1494+
}
14031495
}
1404-
#endif
1496+
#pragma warning restore CS0618 // Type or member is obsolete
1497+
1498+
EditorGUI.EndDisabledGroup();
14051499
}
14061500

14071501
/// <summary>

Assets/UdonSharp/Editor/Editors/UdonSharpUndo.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,9 @@ public static UdonSharpBehaviour AddComponent(GameObject gameObject, System.Type
4949
UdonSharpProgramAsset programAsset = UdonSharpProgramAsset.GetProgramAssetForClass(type);
5050

5151
udonBehaviour.programSource = programAsset;
52+
#pragma warning disable CS0618 // Type or member is obsolete
5253
udonBehaviour.AllowCollisionOwnershipTransfer = false;
54+
#pragma warning restore CS0618 // Type or member is obsolete
5355

5456
SerializedObject componentAsset = new SerializedObject(udonBehaviour);
5557
SerializedProperty serializedProgramAssetProperty = componentAsset.FindProperty("serializedProgramAsset");

Assets/UdonSharp/Editor/UdonSharpASTVisitor.cs

Lines changed: 23 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
using Microsoft.CodeAnalysis;
1+

2+
using Microsoft.CodeAnalysis;
23
using Microsoft.CodeAnalysis.CSharp;
34
using Microsoft.CodeAnalysis.CSharp.Syntax;
45
using System.Collections.Generic;
@@ -18,9 +19,7 @@ public class ASTVisitorContext
1819
public int behaviourExecutionOrder = 0;
1920
public List<ClassDefinition> externClassDefinitions;
2021
public Dictionary<string, FieldDefinition> localFieldDefinitions;
21-
#if UDON_BETA_SDK
2222
public BehaviourSyncMode behaviourSyncMode = BehaviourSyncMode.Any;
23-
#endif
2423

2524
public Stack<ExpressionCaptureScope> expressionCaptureStack = new Stack<ExpressionCaptureScope>();
2625

@@ -34,9 +33,8 @@ public class ASTVisitorContext
3433
public int maxMethodFrameSize = 0; // The maximum size for a "stack frame" for a method. This is used to initialize the correct default size of the artificial stack so that we know we only need to double the size of it at most.
3534
public SymbolDefinition artificalStackSymbol = null;
3635
public SymbolDefinition stackAddressSymbol = null;
37-
#if UDON_BETA_SDK
3836
public bool requiresVRCReturn = false;
39-
#endif
37+
4038
public Stack<JumpLabel> continueLabelStack = new Stack<JumpLabel>();
4139
public Stack<JumpLabel> breakLabelStack = new Stack<JumpLabel>();
4240

@@ -281,8 +279,7 @@ public override void VisitClassDeclaration(ClassDeclarationSyntax node)
281279
throw new System.ArgumentException("Execution order attribute must have an integer argument");
282280
}
283281
}
284-
285-
#if UDON_BETA_SDK
282+
286283
if (captureType != null && captureType == typeof(UdonBehaviourSyncModeAttribute))
287284
{
288285
if (attribute.ArgumentList != null &&
@@ -300,7 +297,6 @@ public override void VisitClassDeclaration(ClassDeclarationSyntax node)
300297
}
301298
}
302299
}
303-
#endif
304300
}
305301
}
306302
}
@@ -1102,9 +1098,7 @@ public override void VisitMethodDeclaration(MethodDeclarationSyntax node)
11021098
JumpLabel returnLabel = visitorContext.labelTable.GetNewJumpLabel("return");
11031099
visitorContext.returnLabel = returnLabel;
11041100
visitorContext.returnSymbol = definition.returnSymbol;
1105-
#if UDON_BETA_SDK
11061101
visitorContext.requiresVRCReturn = functionName == "_onOwnershipRequest" ? true : false;
1107-
#endif
11081102

11091103
visitorContext.uasmBuilder.AddJumpLabel(definition.methodUdonEntryPoint);
11101104

@@ -1116,7 +1110,7 @@ public override void VisitMethodDeclaration(MethodDeclarationSyntax node)
11161110
System.Tuple<System.Type, string>[] customEventArgs = visitorContext.resolverContext.GetMethodCustomArgs(functionName);
11171111
if (customEventArgs != null)
11181112
{
1119-
if (definition.parameters.Length == 0 && (functionName == "_onStationEntered" || functionName == "_onStationExited"))
1113+
if (definition.parameters.Length == 0 && (functionName == "_onStationEntered" || functionName == "_onStationExited" || functionName == "_onOwnershipTransferred"))
11201114
{
11211115
// It's the old version of the station entered events
11221116
}
@@ -1174,17 +1168,21 @@ public override void VisitMethodDeclaration(MethodDeclarationSyntax node)
11741168
returnSetterScope.SetToLocalSymbol(visitorContext.returnSymbol);
11751169
returnSetterScope.ExecuteSet(returnValue);
11761170
}
1177-
1178-
#if UDON_BETA_SDK
1171+
11791172
if (visitorContext.requiresVRCReturn)
11801173
{
1174+
SymbolTable globalSymbolTable = visitorContext.topTable.GetGlobalSymbolTable();
1175+
1176+
SymbolDefinition autoAssignedEventSymbol = globalSymbolTable.FindUserDefinedSymbol("__returnValue");
1177+
if (autoAssignedEventSymbol == null)
1178+
autoAssignedEventSymbol = globalSymbolTable.CreateNamedSymbol("__returnValue", typeof(System.Object), SymbolDeclTypeFlags.Private | SymbolDeclTypeFlags.BuiltinVar);
1179+
11811180
using (ExpressionCaptureScope returnValueSetMethod = new ExpressionCaptureScope(visitorContext, null))
11821181
{
1183-
returnValueSetMethod.ResolveAccessToken(nameof(VRC.Udon.UdonBehaviour.WriteReturnValue));
1184-
returnValueSetMethod.Invoke(new SymbolDefinition[] { returnValue });
1182+
returnValueSetMethod.SetToLocalSymbol(autoAssignedEventSymbol);
1183+
returnValueSetMethod.ExecuteSet(returnValue);
11851184
}
11861185
}
1187-
#endif
11881186
}
11891187
}
11901188
}
@@ -1696,18 +1694,21 @@ public override void VisitReturnStatement(ReturnStatementSyntax node)
16961694
returnOutSetter.SetToLocalSymbol(visitorContext.returnSymbol);
16971695
returnOutSetter.ExecuteSet(returnSymbol);
16981696
}
1699-
1700-
#if UDON_BETA_SDK
1701-
// Special methods like OnOwnershipRequest
1697+
17021698
if (visitorContext.requiresVRCReturn)
17031699
{
1700+
SymbolTable globalSymbolTable = visitorContext.topTable.GetGlobalSymbolTable();
1701+
1702+
SymbolDefinition autoAssignedEventSymbol = globalSymbolTable.FindUserDefinedSymbol("__returnValue");
1703+
if (autoAssignedEventSymbol == null)
1704+
autoAssignedEventSymbol = globalSymbolTable.CreateNamedSymbol("__returnValue", typeof(System.Object), SymbolDeclTypeFlags.Private | SymbolDeclTypeFlags.BuiltinVar);
1705+
17041706
using (ExpressionCaptureScope returnValueSetMethod = new ExpressionCaptureScope(visitorContext, null))
17051707
{
1706-
returnValueSetMethod.ResolveAccessToken(nameof(VRC.Udon.UdonBehaviour.WriteReturnValue));
1707-
returnValueSetMethod.Invoke(new SymbolDefinition[] { returnSymbol });
1708+
returnValueSetMethod.SetToLocalSymbol(autoAssignedEventSymbol);
1709+
returnValueSetMethod.ExecuteSet(returnSymbol);
17081710
}
17091711
}
1710-
#endif
17111712
}
17121713
}
17131714

Assets/UdonSharp/Editor/UdonSharpCompilationModule.cs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
using Microsoft.CodeAnalysis;
1+

2+
using Microsoft.CodeAnalysis;
23
using Microsoft.CodeAnalysis.CSharp;
34
using Microsoft.CodeAnalysis.CSharp.Syntax;
45
using Microsoft.CodeAnalysis.Text;
@@ -157,9 +158,7 @@ public CompileTaskResult Compile(List<ClassDefinition> classDefinitions, Microso
157158
programAsset.behaviourIDHeapVarName = visitor.GetIDHeapVarName();
158159

159160
programAsset.fieldDefinitions = fieldVisitor.visitorContext.localFieldDefinitions;
160-
#if UDON_BETA_SDK
161161
programAsset.behaviourSyncMode = visitor.visitorContext.behaviourSyncMode;
162-
#endif
163162

164163
if (debugInfo != null)
165164
debugInfo.FinalizeDebugInfo();

0 commit comments

Comments
 (0)