{"TotalCount":13,"Files":[{"Ident":"facepunch.libevents","Path":"Code/SortingHelper.cs","FileName":"SortingHelper.cs","PackageType":"library","CodeKind":"Game","AssetVersionId":65480,"Code":"using System.Collections.Generic;\r\nusing System.Linq;\r\n\r\nnamespace Sandbox.Events;\r\n\r\n/// \u003Csummary\u003E\r\n/// Generate an ordering based on a set of first-most and last-most items, and\r\n/// individual constraints between pairs of items. All first-most items will be\r\n/// ordered before all last-most items, and any other items will be put in the\r\n/// middle unless forced to be elsewhere by a constraint.\r\n/// \u003C/summary\u003E\r\ninternal class SortingHelper\r\n{\r\n\tpublic record struct SortConstraint( int EarlierIndex, int LaterIndex )\r\n\t{\r\n\t\tpublic SortConstraint Complement =\u003E new ( LaterIndex, EarlierIndex );\r\n\t}\r\n\r\n\tprivate readonly int _itemCount;\r\n\r\n\tprivate readonly HashSet\u003CSortConstraint\u003E _initialConstraints = new HashSet\u003CSortConstraint\u003E();\r\n\r\n\tprivate readonly HashSet\u003Cint\u003E _first = new HashSet\u003Cint\u003E();\r\n\tprivate readonly HashSet\u003Cint\u003E _last = new HashSet\u003Cint\u003E();\r\n\r\n\tpublic SortingHelper( int itemCount )\r\n\t{\r\n\t\t_itemCount = itemCount;\r\n\t}\r\n\r\n\tpublic void AddConstraint( int earlierIndex, int laterIndex )\r\n\t{\r\n\t\t_initialConstraints.Add( new SortConstraint( earlierIndex, laterIndex ) );\r\n\t}\r\n\r\n\tpublic void AddFirst( int earlierIndex )\r\n\t{\r\n\t\t_first.Add( earlierIndex );\r\n\t}\r\n\r\n\tpublic void AddLast( int laterIndex )\r\n\t{\r\n\t\t_last.Add( laterIndex );\r\n\t}\r\n\r\n\tpublic bool Sort( List\u003Cint\u003E result, out SortConstraint invalidConstraint )\r\n\t{\r\n\t\tvar middle = new HashSet\u003Cint\u003E();\r\n\r\n\t\tfor ( var index = 0; index \u003C _itemCount; \u002B\u002Bindex )\r\n\t\t{\r\n\t\t\tif ( !_first.Contains( index ) \u0026\u0026 !_last.Contains( index ) )\r\n\t\t\t\tmiddle.Add( index );\r\n\t\t}\r\n\r\n\t\tvar allConstraints = new HashSet\u003CSortConstraint\u003E();\r\n\t\tvar newConstraints = new Queue\u003CSortConstraint\u003E();\r\n\t\tvar beforeDict = new Dictionary\u003Cint, HashSet\u003Cint\u003E\u003E();\r\n\t\tvar afterDict = new Dictionary\u003Cint, HashSet\u003Cint\u003E\u003E();\r\n\r\n\t\tbool AddWorkingConstraint( int earlierIndex, int laterIndex, out SortConstraint constraint )\r\n\t\t{\r\n\t\t\tconstraint = new SortConstraint( earlierIndex, laterIndex );\r\n\r\n\t\t\tif ( allConstraints.Contains( constraint.Complement ) )\r\n\t\t\t\treturn false;\r\n\r\n\t\t\tif ( !allConstraints.Add( constraint ) )\r\n\t\t\t\treturn true;\r\n\r\n\t\t\tnewConstraints.Enqueue( constraint );\r\n\r\n\t\t\tif ( !beforeDict.TryGetValue( earlierIndex, out var before ) )\r\n\t\t\t\tbeforeDict.Add( earlierIndex, before = new HashSet\u003Cint\u003E() );\r\n\r\n\t\t\tif ( !afterDict.TryGetValue( laterIndex, out var after ) )\r\n\t\t\t\tafterDict.Add( laterIndex, after = new HashSet\u003Cint\u003E() );\r\n\r\n\t\t\tbefore.Add( laterIndex );\r\n\t\t\tafter.Add( earlierIndex );\r\n\r\n\t\t\treturn true;\r\n\t\t}\r\n\r\n\t\t// Add initial constraints\r\n\r\n\t\tforeach ( var initialConstraint in _initialConstraints )\r\n\t\t{\r\n\t\t\tif ( !AddWorkingConstraint( initialConstraint.EarlierIndex, initialConstraint.LaterIndex, out invalidConstraint ) )\r\n\t\t\t\treturn false;\r\n\t\t}\r\n\r\n\t\t// Everything in _first should be before everything in _last\r\n\r\n\t\tforeach ( var earlierIndex in _first )\r\n\t\t{\r\n\t\t\tforeach ( var laterIndex in _last )\r\n\t\t\t{\r\n\t\t\t\tif ( !AddWorkingConstraint( earlierIndex, laterIndex, out invalidConstraint ) )\r\n\t\t\t\t\treturn false;\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\t// Keep propagating constraints until nothing changes\r\n\r\n\t\twhile ( newConstraints.TryDequeue( out var nextConstraint ) )\r\n\t\t{\r\n\t\t\t// if a \u003C b, and b \u003C c, then a \u003C c etc\r\n\r\n\t\t\tif ( beforeDict.TryGetValue( nextConstraint.LaterIndex, out var before ) )\r\n\t\t\t{\r\n\t\t\t\tforeach ( var laterIndex in before )\r\n\t\t\t\t{\r\n\t\t\t\t\tif ( !AddWorkingConstraint( nextConstraint.EarlierIndex, laterIndex, out invalidConstraint ) )\r\n\t\t\t\t\t\treturn false;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\r\n\t\t\tif ( afterDict.TryGetValue( nextConstraint.EarlierIndex, out var after ) )\r\n\t\t\t{\r\n\t\t\t\tforeach ( var earlierIndex in after )\r\n\t\t\t\t{\r\n\t\t\t\t\tif ( !AddWorkingConstraint( earlierIndex, nextConstraint.LaterIndex, out invalidConstraint ) )\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\treturn false;\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\t// Now if we have any items that aren\u0027t using GroupOrder.First, and haven\u0027t\r\n\t\t// determined that they are ordered before another item with GroupOrder.First,\r\n\t\t// we can safely order them after all GroupOrder.First items. And vice versa.\r\n\r\n\t\tforeach ( var middleIndex in middle )\r\n\t\t{\r\n\t\t\tvar isBeforeAnyFirst = beforeDict.TryGetValue( middleIndex, out var before )\r\n\t\t\t\t\u0026\u0026 before.Any( x =\u003E _first.Contains( x ) );\r\n\r\n\t\t\tvar isAfterAnyLast = afterDict.TryGetValue( middleIndex, out var after )\r\n\t\t\t\t\u0026\u0026 after.Any( x =\u003E _last.Contains( x ) );\r\n\r\n\t\t\tif ( !isBeforeAnyFirst )\r\n\t\t\t{\r\n\t\t\t\tforeach ( var earlierIndex in _first )\r\n\t\t\t\t\tAddWorkingConstraint( earlierIndex, middleIndex, out invalidConstraint );\r\n\t\t\t}\r\n\r\n\t\t\tif ( !isAfterAnyLast )\r\n\t\t\t{\r\n\t\t\t\tforeach ( var laterIndex in _last )\r\n\t\t\t\t\tAddWorkingConstraint( middleIndex, laterIndex, out invalidConstraint );\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\t// Now lets add items to the final ordering if all items that should be sorted\r\n\t\t// before them are already added to that ordering. We\u0027ll implement this by choosing\r\n\t\t// items that have an empty list / don\u0027t appear in afterDict, and update that\r\n\t\t// dictionary as we go.\r\n\r\n\t\tvar earliestRemaining = new Queue\u003Cint\u003E();\r\n\r\n\t\t// First, seed the queue with everything that\u0027s already not ordered after anything\r\n\r\n\t\tfor ( var index = 0; index \u003C _itemCount; \u002B\u002Bindex )\r\n\t\t{\r\n\t\t\tif ( !afterDict.ContainsKey( index ) )\r\n\t\t\t{\r\n\t\t\t\tearliestRemaining.Enqueue( index );\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tresult.Clear();\r\n\r\n\t\twhile ( earliestRemaining.TryDequeue( out var nextIndex ) )\r\n\t\t{\r\n\t\t\tresult.Add( nextIndex );\r\n\r\n\t\t\tforeach ( var laterIndex in beforeDict.TryGetValue( nextIndex, out var laterIndices )\r\n\t\t\t\t? laterIndices : Enumerable.Empty\u003Cint\u003E() )\r\n\t\t\t{\r\n\t\t\t\tvar beforeLater = afterDict[laterIndex];\r\n\t\t\t\tbeforeLater.Remove( nextIndex );\r\n\r\n\t\t\t\tif ( beforeLater.Count == 0 )\r\n\t\t\t\t\tearliestRemaining.Enqueue( laterIndex );\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tinvalidConstraint = default;\r\n\t\treturn result.Count == _itemCount;\r\n\t}\r\n}\r\n"},{"Ident":"facepunch.libevents","Path":"SortingHelper.cs","FileName":"SortingHelper.cs","PackageType":"library","CodeKind":"Game","AssetVersionId":65480,"Code":"using System.Collections.Generic;\r\nusing System.Linq;\r\n\r\nnamespace Sandbox.Events;\r\n\r\n/// \u003Csummary\u003E\r\n/// Generate an ordering based on a set of first-most and last-most items, and\r\n/// individual constraints between pairs of items. All first-most items will be\r\n/// ordered before all last-most items, and any other items will be put in the\r\n/// middle unless forced to be elsewhere by a constraint.\r\n/// \u003C/summary\u003E\r\ninternal class SortingHelper\r\n{\r\n\tpublic record struct SortConstraint( int EarlierIndex, int LaterIndex )\r\n\t{\r\n\t\tpublic SortConstraint Complement =\u003E new ( LaterIndex, EarlierIndex );\r\n\t}\r\n\r\n\tprivate readonly int _itemCount;\r\n\r\n\tprivate readonly HashSet\u003CSortConstraint\u003E _initialConstraints = new HashSet\u003CSortConstraint\u003E();\r\n\r\n\tprivate readonly HashSet\u003Cint\u003E _first = new HashSet\u003Cint\u003E();\r\n\tprivate readonly HashSet\u003Cint\u003E _last = new HashSet\u003Cint\u003E();\r\n\r\n\tpublic SortingHelper( int itemCount )\r\n\t{\r\n\t\t_itemCount = itemCount;\r\n\t}\r\n\r\n\tpublic void AddConstraint( int earlierIndex, int laterIndex )\r\n\t{\r\n\t\t_initialConstraints.Add( new SortConstraint( earlierIndex, laterIndex ) );\r\n\t}\r\n\r\n\tpublic void AddFirst( int earlierIndex )\r\n\t{\r\n\t\t_first.Add( earlierIndex );\r\n\t}\r\n\r\n\tpublic void AddLast( int laterIndex )\r\n\t{\r\n\t\t_last.Add( laterIndex );\r\n\t}\r\n\r\n\tpublic bool Sort( List\u003Cint\u003E result, out SortConstraint invalidConstraint )\r\n\t{\r\n\t\tvar middle = new HashSet\u003Cint\u003E();\r\n\r\n\t\tfor ( var index = 0; index \u003C _itemCount; \u002B\u002Bindex )\r\n\t\t{\r\n\t\t\tif ( !_first.Contains( index ) \u0026\u0026 !_last.Contains( index ) )\r\n\t\t\t\tmiddle.Add( index );\r\n\t\t}\r\n\r\n\t\tvar allConstraints = new HashSet\u003CSortConstraint\u003E();\r\n\t\tvar newConstraints = new Queue\u003CSortConstraint\u003E();\r\n\t\tvar beforeDict = new Dictionary\u003Cint, HashSet\u003Cint\u003E\u003E();\r\n\t\tvar afterDict = new Dictionary\u003Cint, HashSet\u003Cint\u003E\u003E();\r\n\r\n\t\tbool AddWorkingConstraint( int earlierIndex, int laterIndex, out SortConstraint constraint )\r\n\t\t{\r\n\t\t\tconstraint = new SortConstraint( earlierIndex, laterIndex );\r\n\r\n\t\t\tif ( allConstraints.Contains( constraint.Complement ) )\r\n\t\t\t\treturn false;\r\n\r\n\t\t\tif ( !allConstraints.Add( constraint ) )\r\n\t\t\t\treturn true;\r\n\r\n\t\t\tnewConstraints.Enqueue( constraint );\r\n\r\n\t\t\tif ( !beforeDict.TryGetValue( earlierIndex, out var before ) )\r\n\t\t\t\tbeforeDict.Add( earlierIndex, before = new HashSet\u003Cint\u003E() );\r\n\r\n\t\t\tif ( !afterDict.TryGetValue( laterIndex, out var after ) )\r\n\t\t\t\tafterDict.Add( laterIndex, after = new HashSet\u003Cint\u003E() );\r\n\r\n\t\t\tbefore.Add( laterIndex );\r\n\t\t\tafter.Add( earlierIndex );\r\n\r\n\t\t\treturn true;\r\n\t\t}\r\n\r\n\t\t// Add initial constraints\r\n\r\n\t\tforeach ( var initialConstraint in _initialConstraints )\r\n\t\t{\r\n\t\t\tif ( !AddWorkingConstraint( initialConstraint.EarlierIndex, initialConstraint.LaterIndex, out invalidConstraint ) )\r\n\t\t\t\treturn false;\r\n\t\t}\r\n\r\n\t\t// Everything in _first should be before everything in _last\r\n\r\n\t\tforeach ( var earlierIndex in _first )\r\n\t\t{\r\n\t\t\tforeach ( var laterIndex in _last )\r\n\t\t\t{\r\n\t\t\t\tif ( !AddWorkingConstraint( earlierIndex, laterIndex, out invalidConstraint ) )\r\n\t\t\t\t\treturn false;\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\t// Keep propagating constraints until nothing changes\r\n\r\n\t\twhile ( newConstraints.TryDequeue( out var nextConstraint ) )\r\n\t\t{\r\n\t\t\t// if a \u003C b, and b \u003C c, then a \u003C c etc\r\n\r\n\t\t\tif ( beforeDict.TryGetValue( nextConstraint.LaterIndex, out var before ) )\r\n\t\t\t{\r\n\t\t\t\tforeach ( var laterIndex in before )\r\n\t\t\t\t{\r\n\t\t\t\t\tif ( !AddWorkingConstraint( nextConstraint.EarlierIndex, laterIndex, out invalidConstraint ) )\r\n\t\t\t\t\t\treturn false;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\r\n\t\t\tif ( afterDict.TryGetValue( nextConstraint.EarlierIndex, out var after ) )\r\n\t\t\t{\r\n\t\t\t\tforeach ( var earlierIndex in after )\r\n\t\t\t\t{\r\n\t\t\t\t\tif ( !AddWorkingConstraint( earlierIndex, nextConstraint.LaterIndex, out invalidConstraint ) )\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\treturn false;\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\t// Now if we have any items that aren\u0027t using GroupOrder.First, and haven\u0027t\r\n\t\t// determined that they are ordered before another item with GroupOrder.First,\r\n\t\t// we can safely order them after all GroupOrder.First items. And vice versa.\r\n\r\n\t\tforeach ( var middleIndex in middle )\r\n\t\t{\r\n\t\t\tvar isBeforeAnyFirst = beforeDict.TryGetValue( middleIndex, out var before )\r\n\t\t\t\t\u0026\u0026 before.Any( x =\u003E _first.Contains( x ) );\r\n\r\n\t\t\tvar isAfterAnyLast = afterDict.TryGetValue( middleIndex, out var after )\r\n\t\t\t\t\u0026\u0026 after.Any( x =\u003E _last.Contains( x ) );\r\n\r\n\t\t\tif ( !isBeforeAnyFirst )\r\n\t\t\t{\r\n\t\t\t\tforeach ( var earlierIndex in _first )\r\n\t\t\t\t\tAddWorkingConstraint( earlierIndex, middleIndex, out invalidConstraint );\r\n\t\t\t}\r\n\r\n\t\t\tif ( !isAfterAnyLast )\r\n\t\t\t{\r\n\t\t\t\tforeach ( var laterIndex in _last )\r\n\t\t\t\t\tAddWorkingConstraint( middleIndex, laterIndex, out invalidConstraint );\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\t// Now lets add items to the final ordering if all items that should be sorted\r\n\t\t// before them are already added to that ordering. We\u0027ll implement this by choosing\r\n\t\t// items that have an empty list / don\u0027t appear in afterDict, and update that\r\n\t\t// dictionary as we go.\r\n\r\n\t\tvar earliestRemaining = new Queue\u003Cint\u003E();\r\n\r\n\t\t// First, seed the queue with everything that\u0027s already not ordered after anything\r\n\r\n\t\tfor ( var index = 0; index \u003C _itemCount; \u002B\u002Bindex )\r\n\t\t{\r\n\t\t\tif ( !afterDict.ContainsKey( index ) )\r\n\t\t\t{\r\n\t\t\t\tearliestRemaining.Enqueue( index );\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tresult.Clear();\r\n\r\n\t\twhile ( earliestRemaining.TryDequeue( out var nextIndex ) )\r\n\t\t{\r\n\t\t\tresult.Add( nextIndex );\r\n\r\n\t\t\tforeach ( var laterIndex in beforeDict.TryGetValue( nextIndex, out var laterIndices )\r\n\t\t\t\t? laterIndices : Enumerable.Empty\u003Cint\u003E() )\r\n\t\t\t{\r\n\t\t\t\tvar beforeLater = afterDict[laterIndex];\r\n\t\t\t\tbeforeLater.Remove( nextIndex );\r\n\r\n\t\t\t\tif ( beforeLater.Count == 0 )\r\n\t\t\t\t\tearliestRemaining.Enqueue( laterIndex );\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tinvalidConstraint = default;\r\n\t\treturn result.Count == _itemCount;\r\n\t}\r\n}\r\n"},{"Ident":"facepunch.libevents","Path":"StateMachine.cs","FileName":"StateMachine.cs","PackageType":"library","CodeKind":"Game","AssetVersionId":65480,"Code":"using System;\r\nusing System.Collections.Generic;\r\nusing System.Linq;\r\nusing Sandbox.Diagnostics;\r\n\r\nnamespace Sandbox.States;\r\n\r\n[Title( \u0022State Machine\u0022 ), Icon( \u0022smart_toy\u0022 ), Category( \u0022State Machines\u0022 )]\r\npublic sealed class StateMachineComponent : Component\r\n{\r\n\t/// \u003Csummary\u003E\r\n\t/// How many instant state transitions in a row until we throw an error?\r\n\t/// \u003C/summary\u003E\r\n\tpublic const int MaxInstantTransitions = 16;\r\n\r\n\tprivate readonly Dictionary\u003Cint, State\u003E _states = new();\r\n\tprivate readonly Dictionary\u003Cint, Transition\u003E _transitions = new();\r\n\r\n\tprivate int _nextId = 0;\r\n\r\n\t/// \u003Csummary\u003E\r\n\t/// All states in this machine.\r\n\t/// \u003C/summary\u003E\r\n\tpublic IEnumerable\u003CState\u003E States =\u003E _states.Values;\r\n\r\n\t/// \u003Csummary\u003E\r\n\t/// All transitions between states in this machine.\r\n\t/// \u003C/summary\u003E\r\n\tpublic IEnumerable\u003CTransition\u003E Transitions =\u003E _transitions.Values;\r\n\r\n\t/// \u003Csummary\u003E\r\n\t/// Which state becomes active when the machine starts?\r\n\t/// \u003C/summary\u003E\r\n\tpublic State? InitialState { get; set; }\r\n\r\n\t/// \u003Csummary\u003E\r\n\t/// Which state is currently active?\r\n\t/// \u003C/summary\u003E\r\n\tpublic State? CurrentState\r\n\t{\r\n\t\tget =\u003E CurrentStateId is {} id ? _states!.GetValueOrDefault( id ) : null;\r\n\t\tprivate set =\u003E CurrentStateId = value?.Id;\r\n\t}\r\n\r\n\t[Property] private int? CurrentStateId { get; set; }\r\n\r\n\tprivate float _stateTime;\r\n\r\n\tprivate bool _firstUpdate = true;\r\n\r\n\tprotected override void OnStart()\r\n\t{\r\n\t\tif ( !Network.IsProxy \u0026\u0026 InitialState is { } initial )\r\n\t\t{\r\n\t\t\tCurrentState = initial;\r\n\t\t}\r\n\t}\r\n\r\n\tprivate static void InvokeSafe( Action? action )\r\n\t{\r\n\t\ttry\r\n\t\t{\r\n\t\t\taction?.Invoke();\r\n\t\t}\r\n\t\tcatch ( Exception ex )\r\n\t\t{\r\n\t\t\tLog.Error( ex );\r\n\t\t}\r\n\t}\r\n\r\n\tprotected override void OnFixedUpdate()\r\n\t{\r\n\t\tif ( _firstUpdate )\r\n\t\t{\r\n\t\t\t_firstUpdate = false;\r\n\r\n\t\t\tInvokeSafe( CurrentState?.OnEnterState );\r\n\t\t}\r\n\r\n\t\tif ( !Network.IsProxy )\r\n\t\t{\r\n\t\t\tvar transitions = 0;\r\n\t\t\tvar prevTime = _stateTime;\r\n\r\n\t\t\t_stateTime \u002B= Time.Delta;\r\n\r\n\t\t\twhile ( transitions\u002B\u002B \u003C MaxInstantTransitions \u0026\u0026 CurrentState?.GetNextTransition( prevTime, _stateTime ) is { } transition )\r\n\t\t\t{\r\n\t\t\t\tDoTransition( transition.Id );\r\n\r\n\t\t\t\tprevTime = 0f;\r\n\r\n\t\t\t\tif ( transition.Delay is { } delay )\r\n\t\t\t\t{\r\n\t\t\t\t\t_stateTime -= delay;\r\n\t\t\t\t}\r\n\t\t\t\telse\r\n\t\t\t\t{\r\n\t\t\t\t\t_stateTime = 0f;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tInvokeSafe( CurrentState?.OnUpdateState );\r\n\t}\r\n\r\n\t[Broadcast( NetPermission.OwnerOnly )]\r\n\tprivate void DoTransition( int transitionId )\r\n\t{\r\n\t\tvar transition = _transitions!.GetValueOrDefault( transitionId )\r\n\t\t\t?? throw new Exception( $\u0022Unknown transition id: {transitionId}\u0022 );\r\n\r\n\t\tvar current = CurrentState!;\r\n\r\n\t\tAssert.AreEqual( current, transition.Source );\r\n\r\n\t\tInvokeSafe( current.OnLeaveState );\r\n\t\tInvokeSafe( transition.OnTransition );\r\n\r\n\t\tCurrentState = current = transition.Target;\r\n\r\n\t\tInvokeSafe( current.OnEnterState );\r\n\t}\r\n\r\n\tpublic State AddState()\r\n\t{\r\n\t\tvar state = new State( this, _nextId\u002B\u002B );\r\n\r\n\t\t_states.Add( state.Id, state );\r\n\r\n\t\tstate.IsValid = true;\r\n\r\n\t\tInitialState ??= state;\r\n\r\n\t\treturn state;\r\n\t}\r\n\r\n\tinternal void RemoveState( State state )\r\n\t{\r\n\t\tAssert.AreEqual( this, state.StateMachine );\r\n\t\tAssert.AreEqual( state, _states[state.Id] );\r\n\r\n\t\tif ( InitialState == state )\r\n\t\t{\r\n\t\t\tInitialState = null;\r\n\t\t}\r\n\r\n\t\tif ( CurrentState == state )\r\n\t\t{\r\n\t\t\tCurrentState = null;\r\n\t\t}\r\n\r\n\t\tvar transitions = Transitions\r\n\t\t\t.Where( x =\u003E x.Source == state || x.Target == state )\r\n\t\t\t.ToArray();\r\n\r\n\t\tforeach ( var transition in transitions )\r\n\t\t{\r\n\t\t\ttransition.Remove();\r\n\t\t}\r\n\r\n\t\t_states.Remove( state.Id );\r\n\r\n\t\tstate.IsValid = false;\r\n\t}\r\n\r\n\tinternal Transition AddTransition( State source, State target )\r\n\t{\r\n\t\tArgumentNullException.ThrowIfNull( source, nameof( source ) );\r\n\t\tArgumentNullException.ThrowIfNull( target, nameof( target ) );\r\n\r\n\t\tAssert.AreEqual( this, source.StateMachine );\r\n\t\tAssert.AreEqual( this, target.StateMachine );\r\n\r\n\t\tvar transition = new Transition( _nextId\u002B\u002B, source, target );\r\n\r\n\t\t_transitions.Add( transition.Id, transition );\r\n\r\n\t\ttransition.IsValid = true;\r\n\r\n\t\tsource.InvalidateTransitions();\r\n\r\n\t\treturn transition;\r\n\t}\r\n\r\n\tinternal void RemoveTransition( Transition transition )\r\n\t{\r\n\t\tAssert.AreEqual( this, transition.StateMachine );\r\n\t\tAssert.AreEqual( transition, _transitions[transition.Id] );\r\n\r\n\t\t_transitions.Remove( transition.Id );\r\n\r\n\t\ttransition.IsValid = false;\r\n\t\ttransition.Source.InvalidateTransitions();\r\n\t}\r\n\r\n\tinternal void Clear()\r\n\t{\r\n\t\t_states.Clear();\r\n\t\t_transitions.Clear();\r\n\r\n\t\tInitialState = null;\r\n\r\n\t\t_nextId = 0;\r\n\t}\r\n\r\n\t[Property]\r\n\tprivate Model Serialized\r\n\t{\r\n\t\tget =\u003E Serialize();\r\n\t\tset =\u003E Deserialize( value );\r\n\t}\r\n\r\n\tinternal record Model(\r\n\t\tIReadOnlyList\u003CState.Model\u003E States,\r\n\t\tIReadOnlyList\u003CTransition.Model\u003E Transitions,\r\n\t\tint? InitialStateId );\r\n\r\n\tinternal Model Serialize()\r\n\t{\r\n\t\treturn new Model(\r\n\t\t\tStates.Select( x =\u003E x.Serialize() ).OrderBy( x =\u003E x.Id ).ToArray(),\r\n\t\t\tTransitions.Select( x =\u003E x.Serialize() ).OrderBy( x =\u003E x.Id ).ToArray(),\r\n\t\t\tInitialState?.Id );\r\n\t}\r\n\r\n\tinternal void Deserialize( Model model )\r\n\t{\r\n\t\tClear();\r\n\r\n\t\tforeach ( var stateModel in model.States )\r\n\t\t{\r\n\t\t\tvar state = new State( this, stateModel.Id );\r\n\r\n\t\t\t_states.Add( state.Id, state );\r\n\t\t\t_nextId = Math.Max( _nextId, state.Id \u002B 1 );\r\n\r\n\t\t\tstate.Deserialize( stateModel );\r\n\t\t}\r\n\r\n\t\tforeach ( var transitionModel in model.Transitions )\r\n\t\t{\r\n\t\t\tvar transition = new Transition( transitionModel.Id,\r\n\t\t\t\t_states[transitionModel.SourceId],\r\n\t\t\t\t_states[transitionModel.TargetId] );\r\n\r\n\t\t\t_transitions.Add( transition.Id, transition );\r\n\t\t\t_nextId = Math.Max( _nextId, transition.Id \u002B 1 );\r\n\r\n\t\t\ttransition.Deserialize( transitionModel );\r\n\t\t}\r\n\r\n\t\tInitialState = model.InitialStateId is { } id ? _states[id] : null;\r\n\t}\r\n}\r\n"},{"Ident":"facepunch.libevents","Path":"GameEvents/GameEvent.cs","FileName":"GameEvent.cs","PackageType":"library","CodeKind":"Game","AssetVersionId":65480,"Code":"using System;\r\nusing System.Collections.Generic;\r\nusing System.Collections.Immutable;\r\nusing System.Linq;\r\n\r\nnamespace Sandbox.Events;\r\n\r\n/// \u003Csummary\u003E\r\n/// Interface for event payloads that can be listened for by \u003Csee cref=\u0022IGameEventHandler{T}\u0022/\u003Es.\r\n/// \u003C/summary\u003E\r\npublic interface IGameEvent { }\r\n\r\n/// \u003Csummary\u003E\r\n/// Interface for components that handle game events with a payload of type \u003Csee cref=\u0022T\u0022/\u003E.\r\n/// \u003C/summary\u003E\r\n/// \u003Ctypeparam name=\u0022T\u0022\u003EEvent payload type.\u003C/typeparam\u003E\r\npublic interface IGameEventHandler\u003Cin T\u003E\r\n\twhere T : IGameEvent\r\n{\r\n\t/// \u003Csummary\u003E\r\n\t/// Called when an event with payload of type \u003Csee cref=\u0022T\u0022/\u003E is dispatched on a \u003Csee cref=\u0022GameObject\u0022/\u003E\r\n\t/// that contains this component, including on a descendant.\r\n\t/// \u003C/summary\u003E\r\n\t/// \u003Cparam name=\u0022eventArgs\u0022\u003EEvent payload.\u003C/param\u003E\r\n\tvoid OnGameEvent( T eventArgs );\r\n}\r\n\r\n/// \u003Csummary\u003E\r\n/// Helper for dispatching game events in a scene.\r\n/// \u003C/summary\u003E\r\npublic static class GameEvent\r\n{\r\n\tprivate static Dictionary\u003CType, IReadOnlyDictionary\u003CType, int\u003E\u003E HandlerOrderingCache { get; } = new();\r\n\r\n\t/// \u003Csummary\u003E\r\n\t/// Notifies all \u003Csee cref=\u0022IGameEventHandler{T}\u0022/\u003E components that are within \u003Cparamref name=\u0022root\u0022/\u003E,\r\n\t/// with a payload of type \u003Ctypeparamref name=\u0022T\u0022/\u003E.\r\n\t/// \u003C/summary\u003E\r\n\tpublic static void Dispatch\u003CT\u003E( this GameObject root, T eventArgs )\r\n\t\twhere T : IGameEvent\r\n\t{\r\n\t\tvar handlers = (root is Scene scene\r\n\t\t\t? scene.GetAllComponents\u003CIGameEventHandler\u003CT\u003E\u003E() // I think this is more efficient?\r\n\t\t\t: root.Components.GetAll\u003CIGameEventHandler\u003CT\u003E\u003E())\r\n\t\t\t.ToArray();\r\n\r\n\t\tif ( !HandlerOrderingCache.TryGetValue( typeof(T), out var ordering ) || handlers.Any( x =\u003E !ordering.ContainsKey( x.GetType() ) ) )\r\n\t\t{\r\n\t\t\tordering = HandlerOrderingCache[typeof(T)] = GetHandlerOrdering\u003CT\u003E();\r\n\t\t}\r\n\r\n\t\tList\u003CException\u003E? exceptions = null;\r\n\r\n\t\tforeach ( var handler in handlers.OrderBy( x =\u003E ordering[x.GetType()] ) )\r\n\t\t{\r\n\t\t\ttry\r\n\t\t\t{\r\n\t\t\t\thandler.OnGameEvent( eventArgs );\r\n\t\t\t}\r\n\t\t\tcatch ( Exception e )\r\n\t\t\t{\r\n\t\t\t\texceptions ??= new();\r\n\t\t\t\texceptions.Add( e );\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tswitch ( exceptions?.Count )\r\n\t\t{\r\n\t\t\tcase 1:\r\n\t\t\t\tLog.Error( exceptions[0] );\r\n\t\t\t\tbreak;\r\n\r\n\t\t\tcase \u003E 1:\r\n\t\t\t\tLog.Error( new AggregateException( exceptions ) );\r\n\t\t\t\tbreak;\r\n\t\t}\r\n\t}\r\n\r\n\tprivate static bool IsImplementingMethodName( string methodName )\r\n\t{\r\n\t\tif ( methodName == nameof(IGameEventHandler\u003CIGameEvent\u003E.OnGameEvent) )\r\n\t\t{\r\n\t\t\treturn true;\r\n\t\t}\r\n\r\n\t\treturn methodName.StartsWith( \u0022Sandbox.Events.IGameEventHandler\u003C\u0022 ) \u0026\u0026 methodName.EndsWith( \u0022\u003E.OnGameEvent\u0022 );\r\n\t}\r\n\r\n\tprivate static MethodDescription? GetImplementation\u003CT\u003E( TypeDescription type )\r\n\t{\r\n\t\tforeach ( var method in type.Methods )\r\n\t\t{\r\n\t\t\tif ( method.IsStatic ) continue;\r\n\t\t\tif ( method.Parameters.Length != 1 ) continue;\r\n\t\t\tif ( method.Parameters[0].ParameterType != typeof( T ) ) continue;\r\n\r\n\t\t\tif ( !IsImplementingMethodName( method.Name ) ) continue;\r\n\r\n\t\t\treturn method;\r\n\t\t}\r\n\r\n\t\treturn null;\r\n\t}\r\n\r\n\tprivate static IReadOnlyDictionary\u003CType, int\u003E GetHandlerOrdering\u003CT\u003E()\r\n\t\twhere T : IGameEvent\r\n\t{\r\n\t\tvar types = TypeLibrary.GetTypes\u003CIGameEventHandler\u003CT\u003E\u003E().ToArray();\r\n\t\tvar helper = new SortingHelper( types.Length );\r\n\r\n\t\tfor ( var i = 0; i \u003C types.Length; \u002B\u002Bi )\r\n\t\t{\r\n\t\t\tvar type = types[i];\r\n\t\t\tvar method = GetImplementation\u003CT\u003E( type );\r\n\r\n\t\t\tif ( method is null )\r\n\t\t\t{\r\n\t\t\t\tLog.Warning( $\u0022Can\u0027t find {nameof( IGameEventHandler\u003CT\u003E )}\u003C{typeof( T ).Name}\u003E implementation in {type.Name}!\u0022 );\r\n\t\t\t\tcontinue;\r\n\t\t\t}\r\n\r\n\t\t\tforeach ( var attrib in method.Attributes )\r\n\t\t\t{\r\n\t\t\t\tswitch ( attrib )\r\n\t\t\t\t{\r\n\t\t\t\t\tcase EarlyAttribute:\r\n\t\t\t\t\t\thelper.AddFirst( i );\r\n\t\t\t\t\t\tbreak;\r\n\r\n\t\t\t\t\tcase LateAttribute:\r\n\t\t\t\t\t\thelper.AddLast( i );\r\n\t\t\t\t\t\tbreak;\r\n\r\n\t\t\t\t\tcase IBeforeAttribute before:\r\n\t\t\t\t\t\tfor ( var j = 0; j \u003C types.Length; \u002B\u002Bj )\r\n\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\tif ( i == j ) continue;\r\n\r\n\t\t\t\t\t\t\tvar other = types[j];\r\n\r\n\t\t\t\t\t\t\tif ( before.Type.IsAssignableFrom( other.TargetType ) )\r\n\t\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\t\thelper.AddConstraint( i, j );\r\n\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t}\r\n\r\n\t\t\t\t\t\tbreak;\r\n\r\n\t\t\t\t\tcase IAfterAttribute after:\r\n\t\t\t\t\t\tfor ( var j = 0; j \u003C types.Length; \u002B\u002Bj )\r\n\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\tif ( i == j ) continue;\r\n\r\n\t\t\t\t\t\t\tvar other = types[j];\r\n\r\n\t\t\t\t\t\t\tif ( after.Type.IsAssignableFrom( other.TargetType ) )\r\n\t\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\t\thelper.AddConstraint( j, i );\r\n\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t}\r\n\r\n\t\t\t\t\t\tbreak;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tvar ordering = new List\u003Cint\u003E();\r\n\r\n\t\tif ( !helper.Sort( ordering, out var invalid ) )\r\n\t\t{\r\n\t\t\tLog.Error( $\u0022Invalid event ordering constraint between {types[invalid.EarlierIndex].Name} and {types[invalid.LaterIndex].Name}!\u0022 );\r\n\t\t\treturn ImmutableDictionary\u003CType, int\u003E.Empty;\r\n\t\t}\r\n\r\n\t\treturn Enumerable.Range( 0, ordering.Count )\r\n\t\t\t.ToImmutableDictionary( i =\u003E types[ordering[i]].TargetType, i =\u003E i );\r\n\t}\r\n}\r\n\r\npublic delegate void GameEventAction\u003Cin T\u003E( T eventArgs )\r\n\twhere T : IGameEvent;\r\n\r\n/// \u003Csummary\u003E\r\n/// Base class for components that expose game events to Action Graph.\r\n/// \u003C/summary\u003E\r\npublic abstract class GameEventComponent\u003CT\u003E : Component, IGameEventHandler\u003CT\u003E\r\n\twhere T : IGameEvent\r\n{\r\n\t/// \u003Csummary\u003E\r\n\t/// Action invoked when the \u003Ctypeparamref name=\u0022T\u0022/\u003E event is dispatched.\r\n\t/// \u003C/summary\u003E\r\n\t[Property]\r\n\tpublic GameEventAction\u003CT\u003E? OnEvent { get; set; }\r\n\r\n\tvoid IGameEventHandler\u003CT\u003E.OnGameEvent( T eventArgs )\r\n\t{\r\n\t\tOnEvent?.Invoke( eventArgs );\r\n\t}\r\n}\r\n"},{"Ident":"facepunch.libevents","Path":"__gen_RazorNamespace.cs","FileName":"__gen_RazorNamespace.cs","PackageType":"library","CodeKind":"Game","AssetVersionId":65480,"Code":"global using Microsoft.AspNetCore.Components; \nglobal using Microsoft.AspNetCore.Components.Rendering;\n"},{"Ident":"facepunch.libevents","Path":"GameEvents/Attributes.cs","FileName":"Attributes.cs","PackageType":"library","CodeKind":"Game","AssetVersionId":65480,"Code":"\r\nusing System;\r\n\r\nnamespace Sandbox.Events;\r\n\r\n/// \u003Csummary\u003E\r\n/// Only valid on \u003Csee cref=\u0022IGameEventHandler{T}.OnGameEvent\u0022/\u003E implementations. Forces this\r\n/// event handler to be invoked before any handlers not marked as early, except if more specific\r\n/// constraints are given (i.e., \u003Csee cref=\u0022BeforeAttribute{T}\u0022/\u003E, \u003Csee cref=\u0022AfterAttribute{T}\u0022/\u003E).\r\n/// \u003C/summary\u003E\r\n[AttributeUsage( AttributeTargets.Method )]\r\npublic sealed class EarlyAttribute : Attribute\r\n{\r\n\r\n}\r\n\r\n/// \u003Csummary\u003E\r\n/// Only valid on \u003Csee cref=\u0022IGameEventHandler{T}.OnGameEvent\u0022/\u003E implementations. Forces this\r\n/// event handler to be invoked after any handlers not marked as late, except if more specific\r\n/// constraints are given (i.e., \u003Csee cref=\u0022BeforeAttribute{T}\u0022/\u003E, \u003Csee cref=\u0022AfterAttribute{T}\u0022/\u003E).\r\n/// \u003C/summary\u003E\r\n[AttributeUsage( AttributeTargets.Method )]\r\npublic sealed class LateAttribute : Attribute\r\n{\r\n\r\n}\r\n\r\ninternal interface IBeforeAttribute\r\n{\r\n\tType Type { get; }\r\n}\r\n\r\ninternal interface IAfterAttribute\r\n{\r\n\tType Type { get; }\r\n}\r\n\r\n/// \u003Csummary\u003E\r\n/// Only valid on \u003Csee cref=\u0022IGameEventHandler{T}.OnGameEvent\u0022/\u003E implementations. Forces this\r\n/// event handler to be invoked before any handlers in the specified type.\r\n/// \u003C/summary\u003E\r\n[AttributeUsage( AttributeTargets.Method, AllowMultiple = true )]\r\npublic sealed class BeforeAttribute\u003CT\u003E : Attribute, IBeforeAttribute\r\n{\r\n\tType IBeforeAttribute.Type =\u003E typeof(T);\r\n}\r\n\r\n/// \u003Csummary\u003E\r\n/// Only valid on \u003Csee cref=\u0022IGameEventHandler{T}.OnGameEvent\u0022/\u003E implementations. Forces this\r\n/// event handler to be invoked after any handlers in the specified type.\r\n/// \u003C/summary\u003E\r\n[AttributeUsage( AttributeTargets.Method, AllowMultiple = true )]\r\npublic sealed class AfterAttribute\u003CT\u003E : Attribute, IAfterAttribute\r\n{\r\n\tType IAfterAttribute.Type =\u003E typeof( T );\r\n}\r\n"},{"Ident":"facepunch.libevents","Path":"State.cs","FileName":"State.cs","PackageType":"library","CodeKind":"Game","AssetVersionId":65480,"Code":"using System;\r\nusing System.Collections.Generic;\r\nusing Sandbox.Diagnostics;\r\n\r\nnamespace Sandbox.States;\r\n\r\npublic sealed class State : IValid\r\n{\r\n\tprivate readonly List\u003CTransition\u003E _orderedTransitions = new();\r\n\tprivate bool _transitionsDirty = false;\r\n\r\n\tpublic StateMachineComponent StateMachine { get; }\r\n\r\n\t/// \u003Csummary\u003E\r\n\t/// Unique ID of this state in its containing \u003Csee cref=\u0022StateMachineComponent\u0022/\u003E.\r\n\t/// \u003C/summary\u003E\r\n\tpublic int Id { get; }\r\n\r\n\t/// \u003Csummary\u003E\r\n\t/// Helpful name of this state.\r\n\t/// \u003C/summary\u003E\r\n\tpublic string Name { get; set; } = \u0022Unnamed\u0022;\r\n\r\n\tpublic bool IsValid { get; internal set; }\r\n\r\n\tpublic IReadOnlyList\u003CTransition\u003E Transitions\r\n\t{\r\n\t\tget\r\n\t\t{\r\n\t\t\tif ( _transitionsDirty ) UpdateTransitions();\r\n\t\t\treturn _orderedTransitions;\r\n\t\t}\r\n\t}\r\n\r\n\t/// \u003Csummary\u003E\r\n\t/// Event dispatched on the owner when this state is entered.\r\n\t/// \u003C/summary\u003E\r\n\tpublic Action? OnEnterState { get; set; }\r\n\r\n\t/// \u003Csummary\u003E\r\n\t/// Event dispatched on the owner while this state is active.\r\n\t/// \u003C/summary\u003E\r\n\tpublic Action? OnUpdateState { get; set; }\r\n\r\n\t/// \u003Csummary\u003E\r\n\t/// Event dispatched on the owner when this state is exited.\r\n\t/// \u003C/summary\u003E\r\n\tpublic Action? OnLeaveState { get; set; }\r\n\r\n\tpublic Vector2 EditorPosition { get; set; }\r\n\r\n\tinternal State( StateMachineComponent stateMachine, int id )\r\n\t{\r\n\t\tStateMachine = stateMachine;\r\n\t\tId = id;\r\n\t}\r\n\r\n\tprivate void UpdateTransitions()\r\n\t{\r\n\t\t_transitionsDirty = false;\r\n\t\t_orderedTransitions.Clear();\r\n\r\n\t\tforeach ( var transition in StateMachine.Transitions )\r\n\t\t{\r\n\t\t\tif ( transition.Source == this )\r\n\t\t\t{\r\n\t\t\t\t_orderedTransitions.Add( transition );\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\t_orderedTransitions.Sort();\r\n\t}\r\n\r\n\tinternal Transition? GetNextTransition( float prevTime, float nextTime )\r\n\t{\r\n\t\tforeach ( var transition in Transitions )\r\n\t\t{\r\n\t\t\tif ( transition.Delay is { } delay )\r\n\t\t\t{\r\n\t\t\t\tif ( delay \u003C prevTime || delay \u003E nextTime )\r\n\t\t\t\t{\r\n\t\t\t\t\tcontinue;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\r\n\t\t\ttry\r\n\t\t\t{\r\n\t\t\t\tif ( transition.Condition?.Invoke() is not false )\r\n\t\t\t\t{\r\n\t\t\t\t\treturn transition;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t\tcatch ( Exception e )\r\n\t\t\t{\r\n\t\t\t\tLog.Error( e );\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\treturn null;\r\n\t}\r\n\r\n\tpublic Transition AddTransition( State target )\r\n\t{\r\n\t\treturn StateMachine.AddTransition( this, target );\r\n\t}\r\n\r\n\tpublic void Remove()\r\n\t{\r\n\t\tif ( !IsValid ) return;\r\n\t\tStateMachine.RemoveState( this );\r\n\t}\r\n\r\n\tinternal void InvalidateTransitions()\r\n\t{\r\n\t\t_transitionsDirty = true;\r\n\t}\r\n\r\n\tinternal record Model( int Id, string Name, Action? OnEnterState, Action? OnUpdateState, Action? OnLeaveState, Model.UserDataModel? UserData )\r\n\t{\r\n\t\tpublic record UserDataModel( Vector2 Position );\r\n\t}\r\n\r\n\tinternal Model Serialize()\r\n\t{\r\n\t\treturn new Model( Id, Name, OnEnterState, OnUpdateState, OnLeaveState, new Model.UserDataModel( EditorPosition ) );\r\n\t}\r\n\r\n\tinternal void Deserialize( Model model )\r\n\t{\r\n\t\tAssert.AreEqual( Id, model.Id );\r\n\r\n\t\tName = model.Name;\r\n\r\n\t\tOnEnterState = model.OnEnterState;\r\n\t\tOnUpdateState = model.OnUpdateState;\r\n\t\tOnLeaveState = model.OnLeaveState;\r\n\r\n\t\tEditorPosition = model.UserData?.Position ?? Vector2.Zero;\r\n\t}\r\n}\r\n"},{"Ident":"facepunch.libevents","Path":"Code/GameEvents/Attributes.cs","FileName":"Attributes.cs","PackageType":"library","CodeKind":"Game","AssetVersionId":65480,"Code":"\r\nusing System;\r\n\r\nnamespace Sandbox.Events;\r\n\r\n/// \u003Csummary\u003E\r\n/// Only valid on \u003Csee cref=\u0022IGameEventHandler{T}.OnGameEvent\u0022/\u003E implementations. Forces this\r\n/// event handler to be invoked before any handlers not marked as early, except if more specific\r\n/// constraints are given (i.e., \u003Csee cref=\u0022BeforeAttribute{T}\u0022/\u003E, \u003Csee cref=\u0022AfterAttribute{T}\u0022/\u003E).\r\n/// \u003C/summary\u003E\r\n[AttributeUsage( AttributeTargets.Method )]\r\npublic sealed class EarlyAttribute : Attribute\r\n{\r\n\r\n}\r\n\r\n/// \u003Csummary\u003E\r\n/// Only valid on \u003Csee cref=\u0022IGameEventHandler{T}.OnGameEvent\u0022/\u003E implementations. Forces this\r\n/// event handler to be invoked after any handlers not marked as late, except if more specific\r\n/// constraints are given (i.e., \u003Csee cref=\u0022BeforeAttribute{T}\u0022/\u003E, \u003Csee cref=\u0022AfterAttribute{T}\u0022/\u003E).\r\n/// \u003C/summary\u003E\r\n[AttributeUsage( AttributeTargets.Method )]\r\npublic sealed class LateAttribute : Attribute\r\n{\r\n\r\n}\r\n\r\ninternal interface IBeforeAttribute\r\n{\r\n\tType Type { get; }\r\n}\r\n\r\ninternal interface IAfterAttribute\r\n{\r\n\tType Type { get; }\r\n}\r\n\r\n/// \u003Csummary\u003E\r\n/// Only valid on \u003Csee cref=\u0022IGameEventHandler{T}.OnGameEvent\u0022/\u003E implementations. Forces this\r\n/// event handler to be invoked before any handlers in the specified type.\r\n/// \u003C/summary\u003E\r\n[AttributeUsage( AttributeTargets.Method, AllowMultiple = true )]\r\npublic sealed class BeforeAttribute\u003CT\u003E : Attribute, IBeforeAttribute\r\n{\r\n\tType IBeforeAttribute.Type =\u003E typeof(T);\r\n}\r\n\r\n/// \u003Csummary\u003E\r\n/// Only valid on \u003Csee cref=\u0022IGameEventHandler{T}.OnGameEvent\u0022/\u003E implementations. Forces this\r\n/// event handler to be invoked after any handlers in the specified type.\r\n/// \u003C/summary\u003E\r\n[AttributeUsage( AttributeTargets.Method, AllowMultiple = true )]\r\npublic sealed class AfterAttribute\u003CT\u003E : Attribute, IAfterAttribute\r\n{\r\n\tType IAfterAttribute.Type =\u003E typeof( T );\r\n}\r\n"},{"Ident":"facepunch.libevents","Path":"UnitTests/UnitTest.cs","FileName":"UnitTest.cs","PackageType":"library","CodeKind":"UnitTest","AssetVersionId":65480,"Code":"global using Microsoft.VisualStudio.TestTools.UnitTesting;\r\nusing System.Reflection;\r\nusing Sandbox.Internal;\r\n\r\nnamespace Sandbox.Events.Tests;\r\n\r\n[TestClass]\r\npublic class TestInit\r\n{\r\n\t[AssemblyInitialize]\r\n\tpublic static void ClassInitialize( TestContext context )\r\n\t{\r\n\t\tApplication.InitUnitTest();\r\n\r\n\t\tvar addAssemblyMethod = typeof(TypeLibrary)\r\n\t\t\t.GetMethod( \u0022AddAssembly\u0022, BindingFlags.NonPublic | BindingFlags.Instance, new[] { typeof(Assembly), typeof(bool) } )!;\r\n\r\n\t\taddAssemblyMethod.Invoke( GlobalGameNamespace.TypeLibrary, new object?[] { Assembly.GetExecutingAssembly(), true } );\r\n\t}\r\n}\r\n"},{"Ident":"facepunch.libevents","Path":"UnitTests/DispatchTests.cs","FileName":"DispatchTests.cs","PackageType":"library","CodeKind":"UnitTest","AssetVersionId":65480,"Code":"namespace Sandbox.Events.Tests;\r\n\r\n[TestClass]\r\npublic class DispatchTests\r\n{\r\n\t[TestMethod]\r\n\tpublic void Simple()\r\n\t{\r\n\t\tvar scene = new Scene();\r\n\r\n\t\tusing var _ = scene.Push();\r\n\r\n\t\tvar go = new GameObject();\r\n\r\n\t\tgo.Components.Create\u003CEarlyHandler\u003E();\r\n\t\tgo.Components.Create\u003CHandler\u003E();\r\n\t\tgo.Components.Create\u003CLateHandler\u003E();\r\n\r\n\t\tgo.Components.Create\u003CAfterLateHandler\u003E();\r\n\t\tgo.Components.Create\u003CBeforeLateHandler\u003E();\r\n\r\n\t\tgo.Components.Create\u003CAfterEarlyHandler\u003E();\r\n\t\tgo.Components.Create\u003CBeforeEarlyHandler\u003E();\r\n\r\n\t\tgo.Components.Create\u003CBeforeHandler\u003E();\r\n\t\tgo.Components.Create\u003CAfterHandler\u003E();\r\n\r\n\t\tscene.Dispatch( new ExampleEventArgs() );\r\n\r\n\t\tAssert.IsTrue( go.Components.Get\u003CEarlyHandler\u003E().Index \u003C go.Components.Get\u003CHandler\u003E().Index );\r\n\t\tAssert.IsTrue( go.Components.Get\u003CHandler\u003E().Index \u003C go.Components.Get\u003CLateHandler\u003E().Index );\r\n\r\n\t\tAssert.IsTrue( go.Components.Get\u003CBeforeEarlyHandler\u003E().Index \u003C go.Components.Get\u003CEarlyHandler\u003E().Index );\r\n\t\tAssert.IsTrue( go.Components.Get\u003CEarlyHandler\u003E().Index \u003C go.Components.Get\u003CAfterEarlyHandler\u003E().Index );\r\n\r\n\t\tAssert.IsTrue( go.Components.Get\u003CBeforeHandler\u003E().Index \u003C go.Components.Get\u003CHandler\u003E().Index );\r\n\t\tAssert.IsTrue( go.Components.Get\u003CHandler\u003E().Index \u003C go.Components.Get\u003CAfterHandler\u003E().Index );\r\n\r\n\t\tAssert.IsTrue( go.Components.Get\u003CBeforeLateHandler\u003E().Index \u003C go.Components.Get\u003CLateHandler\u003E().Index );\r\n\t\tAssert.IsTrue( go.Components.Get\u003CLateHandler\u003E().Index \u003C go.Components.Get\u003CAfterLateHandler\u003E().Index );\r\n\t}\r\n}\r\n\r\npublic class ExampleEventArgs : IGameEvent\r\n{\r\n\tpublic int HandleCount { get; set; }\r\n}\r\n\r\npublic abstract class BaseHandler : Component\r\n{\r\n\tpublic int Index { get; set; }\r\n\r\n\tprotected void Handle( ExampleEventArgs eventArgs )\r\n\t{\r\n\t\tIndex = \u002B\u002BeventArgs.HandleCount;\r\n\t}\r\n}\r\n\r\npublic sealed class Handler : BaseHandler, IGameEventHandler\u003CExampleEventArgs\u003E\r\n{\r\n\tvoid IGameEventHandler\u003CExampleEventArgs\u003E.OnGameEvent( ExampleEventArgs eventArgs ) =\u003E Handle( eventArgs );\r\n}\r\n\r\npublic sealed class EarlyHandler : BaseHandler, IGameEventHandler\u003CExampleEventArgs\u003E\r\n{\r\n\t[Early]\r\n\tvoid IGameEventHandler\u003CExampleEventArgs\u003E.OnGameEvent( ExampleEventArgs eventArgs ) =\u003E Handle( eventArgs );\r\n}\r\n\r\npublic sealed class LateHandler : BaseHandler, IGameEventHandler\u003CExampleEventArgs\u003E\r\n{\r\n\t[Late]\r\n\tvoid IGameEventHandler\u003CExampleEventArgs\u003E.OnGameEvent( ExampleEventArgs eventArgs ) =\u003E Handle( eventArgs );\r\n}\r\n\r\npublic sealed class BeforeHandler : BaseHandler, IGameEventHandler\u003CExampleEventArgs\u003E\r\n{\r\n\t[Before\u003CHandler\u003E]\r\n\tvoid IGameEventHandler\u003CExampleEventArgs\u003E.OnGameEvent( ExampleEventArgs eventArgs ) =\u003E Handle( eventArgs );\r\n}\r\n\r\npublic sealed class AfterHandler : BaseHandler, IGameEventHandler\u003CExampleEventArgs\u003E\r\n{\r\n\t[After\u003CHandler\u003E]\r\n\tvoid IGameEventHandler\u003CExampleEventArgs\u003E.OnGameEvent( ExampleEventArgs eventArgs ) =\u003E Handle( eventArgs );\r\n}\r\n\r\npublic sealed class BeforeEarlyHandler : BaseHandler, IGameEventHandler\u003CExampleEventArgs\u003E\r\n{\r\n\t[Before\u003CEarlyHandler\u003E]\r\n\tvoid IGameEventHandler\u003CExampleEventArgs\u003E.OnGameEvent( ExampleEventArgs eventArgs ) =\u003E Handle( eventArgs );\r\n}\r\n\r\npublic sealed class AfterEarlyHandler : BaseHandler, IGameEventHandler\u003CExampleEventArgs\u003E\r\n{\r\n\t[After\u003CEarlyHandler\u003E]\r\n\tvoid IGameEventHandler\u003CExampleEventArgs\u003E.OnGameEvent( ExampleEventArgs eventArgs ) =\u003E Handle( eventArgs );\r\n}\r\n\r\npublic sealed class BeforeLateHandler : BaseHandler, IGameEventHandler\u003CExampleEventArgs\u003E\r\n{\r\n\t[Before\u003CLateHandler\u003E]\r\n\tvoid IGameEventHandler\u003CExampleEventArgs\u003E.OnGameEvent( ExampleEventArgs eventArgs ) =\u003E Handle( eventArgs );\r\n}\r\n\r\npublic sealed class AfterLateHandler : BaseHandler, IGameEventHandler\u003CExampleEventArgs\u003E\r\n{\r\n\t[After\u003CLateHandler\u003E]\r\n\tvoid IGameEventHandler\u003CExampleEventArgs\u003E.OnGameEvent( ExampleEventArgs eventArgs ) =\u003E Handle( eventArgs );\r\n}\r\n"},{"Ident":"facepunch.libevents","Path":".obj/__compiler_extra.cs","FileName":"__compiler_extra.cs","PackageType":"library","CodeKind":"Game","AssetVersionId":65480,"Code":"global using static Sandbox.Internal.GlobalGameNamespace;\r\n[assembly: global::System.Reflection.AssemblyMetadata( \u0022AddonTitle\u0022, \u0022Game Events\u0022 )]\r\n[assembly: global::System.Reflection.AssemblyMetadata( \u0022AddonIdent\u0022, \u0022libevents\u0022 )]\r\n[assembly: global::System.Reflection.AssemblyMetadata( \u0022OrgIdent\u0022, \u0022facepunch\u0022 )]\r\n[assembly: global::System.Reflection.AssemblyMetadata( \u0022Ident\u0022, \u0022facepunch.libevents\u0022 )]\r\n[assembly: global::System.Reflection.AssemblyMetadata( \u0022CompileTime\u0022, \u00228/11/2024 10:59:39 AM\u0022 )]\r\n[assembly: global::System.Reflection.AssemblyMetadata( \u0022EngineVersion\u0022, \u002217\u0022 )]\r\n[assembly: global::System.Reflection.AssemblyMetadata( \u0022EngineMinorVersion\u0022, \u00221\u0022 )]\r\n\r\n[assembly: System.Runtime.Versioning.TargetFramework( \u0022.NETCoreApp,Version=v7.0\u0022, FrameworkDisplayName = \u0022.NET 7.0\u0022 )]\r\n[assembly: global::System.Reflection.AssemblyVersion(\u00220.0.153.0\u0022)]\r\n[assembly: global::System.Reflection.AssemblyFileVersion(\u00220.0.153.0\u0022)]"},{"Ident":"facepunch.libevents","Path":"Code/GameEvents/GameEvent.cs","FileName":"GameEvent.cs","PackageType":"library","CodeKind":"Game","AssetVersionId":65480,"Code":"using System;\r\nusing System.Collections.Generic;\r\nusing System.Collections.Immutable;\r\nusing System.Linq;\r\n\r\nnamespace Sandbox.Events;\r\n\r\n/// \u003Csummary\u003E\r\n/// Interface for event payloads that can be listened for by \u003Csee cref=\u0022IGameEventHandler{T}\u0022/\u003Es.\r\n/// \u003C/summary\u003E\r\npublic interface IGameEvent { }\r\n\r\n/// \u003Csummary\u003E\r\n/// Interface for components that handle game events with a payload of type \u003Csee cref=\u0022T\u0022/\u003E.\r\n/// \u003C/summary\u003E\r\n/// \u003Ctypeparam name=\u0022T\u0022\u003EEvent payload type.\u003C/typeparam\u003E\r\npublic interface IGameEventHandler\u003Cin T\u003E\r\n\twhere T : IGameEvent\r\n{\r\n\t/// \u003Csummary\u003E\r\n\t/// Called when an event with payload of type \u003Csee cref=\u0022T\u0022/\u003E is dispatched on a \u003Csee cref=\u0022GameObject\u0022/\u003E\r\n\t/// that contains this component, including on a descendant.\r\n\t/// \u003C/summary\u003E\r\n\t/// \u003Cparam name=\u0022eventArgs\u0022\u003EEvent payload.\u003C/param\u003E\r\n\tvoid OnGameEvent( T eventArgs );\r\n}\r\n\r\n/// \u003Csummary\u003E\r\n/// Helper for dispatching game events in a scene.\r\n/// \u003C/summary\u003E\r\npublic static class GameEvent\r\n{\r\n\tprivate static Dictionary\u003CType, IReadOnlyDictionary\u003CType, int\u003E\u003E HandlerOrderingCache { get; } = new();\r\n\r\n\t/// \u003Csummary\u003E\r\n\t/// Notifies all \u003Csee cref=\u0022IGameEventHandler{T}\u0022/\u003E components that are within \u003Cparamref name=\u0022root\u0022/\u003E,\r\n\t/// with a payload of type \u003Ctypeparamref name=\u0022T\u0022/\u003E.\r\n\t/// \u003C/summary\u003E\r\n\tpublic static void Dispatch\u003CT\u003E( this GameObject root, T eventArgs )\r\n\t\twhere T : IGameEvent\r\n\t{\r\n\t\tvar handlers = (root is Scene scene\r\n\t\t\t? scene.GetAllComponents\u003CIGameEventHandler\u003CT\u003E\u003E() // I think this is more efficient?\r\n\t\t\t: root.Components.GetAll\u003CIGameEventHandler\u003CT\u003E\u003E())\r\n\t\t\t.ToArray();\r\n\r\n\t\tif ( !HandlerOrderingCache.TryGetValue( typeof(T), out var ordering ) || handlers.Any( x =\u003E !ordering.ContainsKey( x.GetType() ) ) )\r\n\t\t{\r\n\t\t\tordering = HandlerOrderingCache[typeof(T)] = GetHandlerOrdering\u003CT\u003E();\r\n\t\t}\r\n\r\n\t\tList\u003CException\u003E? exceptions = null;\r\n\r\n\t\tforeach ( var handler in handlers.OrderBy( x =\u003E ordering[x.GetType()] ) )\r\n\t\t{\r\n\t\t\ttry\r\n\t\t\t{\r\n\t\t\t\thandler.OnGameEvent( eventArgs );\r\n\t\t\t}\r\n\t\t\tcatch ( Exception e )\r\n\t\t\t{\r\n\t\t\t\texceptions ??= new();\r\n\t\t\t\texceptions.Add( e );\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tswitch ( exceptions?.Count )\r\n\t\t{\r\n\t\t\tcase 1:\r\n\t\t\t\tLog.Error( exceptions[0] );\r\n\t\t\t\tbreak;\r\n\r\n\t\t\tcase \u003E 1:\r\n\t\t\t\tLog.Error( new AggregateException( exceptions ) );\r\n\t\t\t\tbreak;\r\n\t\t}\r\n\t}\r\n\r\n\tprivate static bool IsImplementingMethodName( string methodName )\r\n\t{\r\n\t\tif ( methodName == nameof(IGameEventHandler\u003CIGameEvent\u003E.OnGameEvent) )\r\n\t\t{\r\n\t\t\treturn true;\r\n\t\t}\r\n\r\n\t\treturn methodName.StartsWith( \u0022Sandbox.Events.IGameEventHandler\u003C\u0022 ) \u0026\u0026 methodName.EndsWith( \u0022\u003E.OnGameEvent\u0022 );\r\n\t}\r\n\r\n\tprivate static MethodDescription? GetImplementation\u003CT\u003E( TypeDescription type )\r\n\t{\r\n\t\tforeach ( var method in type.Methods )\r\n\t\t{\r\n\t\t\tif ( method.IsStatic ) continue;\r\n\t\t\tif ( method.Parameters.Length != 1 ) continue;\r\n\t\t\tif ( method.Parameters[0].ParameterType != typeof( T ) ) continue;\r\n\r\n\t\t\tif ( !IsImplementingMethodName( method.Name ) ) continue;\r\n\r\n\t\t\treturn method;\r\n\t\t}\r\n\r\n\t\treturn null;\r\n\t}\r\n\r\n\tprivate static IReadOnlyDictionary\u003CType, int\u003E GetHandlerOrdering\u003CT\u003E()\r\n\t\twhere T : IGameEvent\r\n\t{\r\n\t\tvar types = TypeLibrary.GetTypes\u003CIGameEventHandler\u003CT\u003E\u003E().ToArray();\r\n\t\tvar helper = new SortingHelper( types.Length );\r\n\r\n\t\tfor ( var i = 0; i \u003C types.Length; \u002B\u002Bi )\r\n\t\t{\r\n\t\t\tvar type = types[i];\r\n\t\t\tvar method = GetImplementation\u003CT\u003E( type );\r\n\r\n\t\t\tif ( method is null )\r\n\t\t\t{\r\n\t\t\t\tLog.Warning( $\u0022Can\u0027t find {nameof( IGameEventHandler\u003CT\u003E )}\u003C{typeof( T ).Name}\u003E implementation in {type.Name}!\u0022 );\r\n\t\t\t\tcontinue;\r\n\t\t\t}\r\n\r\n\t\t\tforeach ( var attrib in method.Attributes )\r\n\t\t\t{\r\n\t\t\t\tswitch ( attrib )\r\n\t\t\t\t{\r\n\t\t\t\t\tcase EarlyAttribute:\r\n\t\t\t\t\t\thelper.AddFirst( i );\r\n\t\t\t\t\t\tbreak;\r\n\r\n\t\t\t\t\tcase LateAttribute:\r\n\t\t\t\t\t\thelper.AddLast( i );\r\n\t\t\t\t\t\tbreak;\r\n\r\n\t\t\t\t\tcase IBeforeAttribute before:\r\n\t\t\t\t\t\tfor ( var j = 0; j \u003C types.Length; \u002B\u002Bj )\r\n\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\tif ( i == j ) continue;\r\n\r\n\t\t\t\t\t\t\tvar other = types[j];\r\n\r\n\t\t\t\t\t\t\tif ( before.Type.IsAssignableFrom( other.TargetType ) )\r\n\t\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\t\thelper.AddConstraint( i, j );\r\n\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t}\r\n\r\n\t\t\t\t\t\tbreak;\r\n\r\n\t\t\t\t\tcase IAfterAttribute after:\r\n\t\t\t\t\t\tfor ( var j = 0; j \u003C types.Length; \u002B\u002Bj )\r\n\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\tif ( i == j ) continue;\r\n\r\n\t\t\t\t\t\t\tvar other = types[j];\r\n\r\n\t\t\t\t\t\t\tif ( after.Type.IsAssignableFrom( other.TargetType ) )\r\n\t\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\t\thelper.AddConstraint( j, i );\r\n\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t}\r\n\r\n\t\t\t\t\t\tbreak;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tvar ordering = new List\u003Cint\u003E();\r\n\r\n\t\tif ( !helper.Sort( ordering, out var invalid ) )\r\n\t\t{\r\n\t\t\tLog.Error( $\u0022Invalid event ordering constraint between {types[invalid.EarlierIndex].Name} and {types[invalid.LaterIndex].Name}!\u0022 );\r\n\t\t\treturn ImmutableDictionary\u003CType, int\u003E.Empty;\r\n\t\t}\r\n\r\n\t\treturn Enumerable.Range( 0, ordering.Count )\r\n\t\t\t.ToImmutableDictionary( i =\u003E types[ordering[i]].TargetType, i =\u003E i );\r\n\t}\r\n}\r\n\r\npublic delegate void GameEventAction\u003Cin T\u003E( T eventArgs )\r\n\twhere T : IGameEvent;\r\n\r\n/// \u003Csummary\u003E\r\n/// Base class for components that expose game events to Action Graph.\r\n/// \u003C/summary\u003E\r\npublic abstract class GameEventComponent\u003CT\u003E : Component, IGameEventHandler\u003CT\u003E\r\n\twhere T : IGameEvent\r\n{\r\n\t/// \u003Csummary\u003E\r\n\t/// Action invoked when the \u003Ctypeparamref name=\u0022T\u0022/\u003E event is dispatched.\r\n\t/// \u003C/summary\u003E\r\n\t[Property]\r\n\tpublic GameEventAction\u003CT\u003E? OnEvent { get; set; }\r\n\r\n\tvoid IGameEventHandler\u003CT\u003E.OnGameEvent( T eventArgs )\r\n\t{\r\n\t\tOnEvent?.Invoke( eventArgs );\r\n\t}\r\n}\r\n"},{"Ident":"facepunch.libevents","Path":"Transition.cs","FileName":"Transition.cs","PackageType":"library","CodeKind":"Game","AssetVersionId":65480,"Code":"using System;\r\nusing Sandbox.Diagnostics;\r\n\r\nnamespace Sandbox.States;\r\n\r\npublic sealed class Transition : IComparable\u003CTransition\u003E, IValid\r\n{\r\n\tprivate float? _delay;\r\n\tprivate Func\u003Cbool\u003E? _condition;\r\n\r\n\t/// \u003Csummary\u003E\r\n\t/// The state machine containing this transition.\r\n\t/// \u003C/summary\u003E\r\n\tpublic StateMachineComponent StateMachine =\u003E Source.StateMachine;\r\n\r\n\t/// \u003Csummary\u003E\r\n\t/// Unique ID of this transition in the \u003Csee cref=\u0022StateMachineComponent\u0022/\u003E.\r\n\t/// \u003C/summary\u003E\r\n\tpublic int Id { get; }\r\n\r\n\t/// \u003Csummary\u003E\r\n\t/// The state this transition originates from.\r\n\t/// \u003C/summary\u003E\r\n\tpublic State Source { get; }\r\n\r\n\t/// \u003Csummary\u003E\r\n\t/// The destination of this transition.\r\n\t/// \u003C/summary\u003E\r\n\tpublic State Target { get; }\r\n\r\n\t/// \u003Csummary\u003E\r\n\t/// Does this transition still belong to a state.\r\n\t/// \u003C/summary\u003E\r\n\tpublic bool IsValid { get; internal set; }\r\n\r\n\tinternal Transition( int id, State source, State target )\r\n\t{\r\n\t\tSource = source;\r\n\t\tTarget = target;\r\n\t\tId = id;\r\n\t}\r\n\r\n\t/// \u003Csummary\u003E\r\n\t/// Optional delay before this transition is taken.\r\n\t/// If null, this transition can be taken at any time.\r\n\t/// \u003C/summary\u003E\r\n\tpublic float? Delay\r\n\t{\r\n\t\tget =\u003E _delay;\r\n\t\tset\r\n\t\t{\r\n\t\t\t_delay = value;\r\n\t\t\tSource.InvalidateTransitions();\r\n\t\t}\r\n\t}\r\n\r\n\t/// \u003Csummary\u003E\r\n\t/// Optional condition to evaluate.\r\n\t/// \u003C/summary\u003E\r\n\tpublic Func\u003Cbool\u003E? Condition\r\n\t{\r\n\t\tget =\u003E _condition;\r\n\t\tset\r\n\t\t{\r\n\t\t\t_condition = value;\r\n\t\t\tSource.InvalidateTransitions();\r\n\t\t}\r\n\t}\r\n\r\n\t/// \u003Csummary\u003E\r\n\t/// Action performed when this transition is taken.\r\n\t/// \u003C/summary\u003E\r\n\tpublic Action? OnTransition { get; set; }\r\n\r\n\tpublic void Remove()\r\n\t{\r\n\t\tif ( !IsValid ) return;\r\n\t\tStateMachine.RemoveTransition( this );\r\n\t}\r\n\r\n\tpublic int CompareTo( Transition? other )\r\n\t{\r\n\t\tif ( other is null ) return 1;\r\n\r\n\t\tvar delayCompare = (Delay ?? float.PositiveInfinity).CompareTo( other.Delay ?? float.PositiveInfinity );\r\n\t\tif ( delayCompare != 0 ) return delayCompare;\r\n\r\n\t\tvar conditionCompare = (Condition is null).CompareTo( other.Condition is null );\r\n\t\tif ( conditionCompare != 0 ) return conditionCompare;\r\n\r\n\t\treturn Target.Id.CompareTo( other.Target.Id );\r\n\t}\r\n\r\n\tinternal record Model( int Id, int SourceId, int TargetId, float? Delay, Func\u003Cbool\u003E? Condition, Action? OnTransition );\r\n\r\n\tinternal Model Serialize()\r\n\t{\r\n\t\treturn new Model( Id, Source.Id, Target.Id, Delay, Condition, OnTransition );\r\n\t}\r\n\r\n\tinternal void Deserialize( Model model )\r\n\t{\r\n\t\tAssert.AreEqual( Id, model.Id );\r\n\t\tAssert.AreEqual( Source.Id, model.SourceId );\r\n\t\tAssert.AreEqual( Target.Id, model.TargetId );\r\n\r\n\t\tDelay = model.Delay;\r\n\t\tCondition = model.Condition;\r\n\t\tOnTransition = model.OnTransition;\r\n\t}\r\n}\r\n"}]}