initial commit
This commit is contained in:
parent
6715289efe
commit
788c3389af
37645 changed files with 2526849 additions and 80 deletions
|
@ -0,0 +1,164 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEngine.LowLevel;
|
||||
using Unity.Services.Core.Internal;
|
||||
using NotNull = JetBrains.Annotations.NotNullAttribute;
|
||||
|
||||
namespace Unity.Services.Core.Scheduler.Internal
|
||||
{
|
||||
class ActionScheduler : IActionScheduler
|
||||
{
|
||||
readonly ITimeProvider m_TimeProvider;
|
||||
|
||||
readonly object m_Lock = new object();
|
||||
|
||||
readonly MinimumBinaryHeap<ScheduledInvocation> m_ScheduledActions
|
||||
= new MinimumBinaryHeap<ScheduledInvocation>(new ScheduledInvocationComparer());
|
||||
|
||||
readonly Dictionary<long, ScheduledInvocation> m_IdScheduledInvocationMap
|
||||
= new Dictionary<long, ScheduledInvocation>();
|
||||
|
||||
const long k_MinimumIdValue = 1;
|
||||
|
||||
internal readonly PlayerLoopSystem SchedulerLoopSystem;
|
||||
|
||||
long m_NextId = k_MinimumIdValue;
|
||||
|
||||
public ActionScheduler()
|
||||
: this(new UtcTimeProvider()) {}
|
||||
|
||||
public ActionScheduler(ITimeProvider timeProvider)
|
||||
{
|
||||
m_TimeProvider = timeProvider;
|
||||
SchedulerLoopSystem = new PlayerLoopSystem
|
||||
{
|
||||
type = typeof(ActionScheduler),
|
||||
updateDelegate = ExecuteExpiredActions
|
||||
};
|
||||
}
|
||||
|
||||
public int ScheduledActionsCount => m_ScheduledActions.Count;
|
||||
|
||||
public long ScheduleAction([NotNull] Action action, double delaySeconds = 0)
|
||||
{
|
||||
if (delaySeconds < 0)
|
||||
{
|
||||
throw new ArgumentException("delaySeconds can not be negative");
|
||||
}
|
||||
|
||||
if (action is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(action));
|
||||
}
|
||||
|
||||
var scheduledInvocation = new ScheduledInvocation
|
||||
{
|
||||
Action = action,
|
||||
InvocationTime = m_TimeProvider.Now.AddSeconds(delaySeconds),
|
||||
ActionId = m_NextId++
|
||||
};
|
||||
|
||||
if (m_NextId < k_MinimumIdValue)
|
||||
{
|
||||
m_NextId = k_MinimumIdValue;
|
||||
}
|
||||
|
||||
lock (m_Lock)
|
||||
{
|
||||
m_ScheduledActions.Insert(scheduledInvocation);
|
||||
m_IdScheduledInvocationMap.Add(scheduledInvocation.ActionId, scheduledInvocation);
|
||||
}
|
||||
|
||||
return scheduledInvocation.ActionId;
|
||||
}
|
||||
|
||||
public void CancelAction(long actionId)
|
||||
{
|
||||
lock (m_Lock)
|
||||
{
|
||||
if (!m_IdScheduledInvocationMap.ContainsKey(actionId))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var scheduledInvocation = m_IdScheduledInvocationMap[actionId];
|
||||
|
||||
m_ScheduledActions.Remove(scheduledInvocation);
|
||||
m_IdScheduledInvocationMap.Remove(scheduledInvocation.ActionId);
|
||||
}
|
||||
}
|
||||
|
||||
internal void ExecuteExpiredActions()
|
||||
{
|
||||
List<ScheduledInvocation> scheduledInvocationList = new List<ScheduledInvocation>();
|
||||
lock (m_Lock)
|
||||
{
|
||||
while (m_ScheduledActions.Count > 0
|
||||
&& m_ScheduledActions.Min?.InvocationTime <= m_TimeProvider.Now)
|
||||
{
|
||||
var scheduledInvocation = m_ScheduledActions.ExtractMin();
|
||||
scheduledInvocationList.Add(scheduledInvocation);
|
||||
m_ScheduledActions.Remove(scheduledInvocation);
|
||||
m_IdScheduledInvocationMap.Remove(scheduledInvocation.ActionId);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var scheduledInv in scheduledInvocationList)
|
||||
{
|
||||
try
|
||||
{
|
||||
scheduledInv.Action();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
CoreLogger.LogException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR
|
||||
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)]
|
||||
static void ClearActionSchedulerFromPlayerLoop()
|
||||
{
|
||||
var currentPlayerLoop = PlayerLoop.GetCurrentPlayerLoop();
|
||||
var currentSubSystems = new List<PlayerLoopSystem>(currentPlayerLoop.subSystemList);
|
||||
for (var i = currentSubSystems.Count - 1; i >= 0; i--)
|
||||
{
|
||||
if (currentSubSystems[i].type == typeof(ActionScheduler))
|
||||
{
|
||||
currentSubSystems.RemoveAt(i);
|
||||
}
|
||||
}
|
||||
|
||||
UpdateSubSystemList(currentSubSystems, currentPlayerLoop);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
static void UpdateSubSystemList(List<PlayerLoopSystem> subSystemList, PlayerLoopSystem currentPlayerLoop)
|
||||
{
|
||||
currentPlayerLoop.subSystemList = subSystemList.ToArray();
|
||||
PlayerLoop.SetPlayerLoop(currentPlayerLoop);
|
||||
}
|
||||
|
||||
public void JoinPlayerLoopSystem()
|
||||
{
|
||||
var currentPlayerLoop = PlayerLoop.GetCurrentPlayerLoop();
|
||||
var currentSubSystems = new List<PlayerLoopSystem>(currentPlayerLoop.subSystemList);
|
||||
if (!currentSubSystems.Contains(SchedulerLoopSystem))
|
||||
{
|
||||
currentSubSystems.Add(SchedulerLoopSystem);
|
||||
UpdateSubSystemList(currentSubSystems, currentPlayerLoop);
|
||||
}
|
||||
}
|
||||
|
||||
public void QuitPlayerLoopSystem()
|
||||
{
|
||||
var currentPlayerLoop = PlayerLoop.GetCurrentPlayerLoop();
|
||||
var currentSubSystems = new List<PlayerLoopSystem>(currentPlayerLoop.subSystemList);
|
||||
currentSubSystems.Remove(SchedulerLoopSystem);
|
||||
UpdateSubSystemList(currentSubSystems, currentPlayerLoop);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 57e3220438bdf43bebec66c4476f6677
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,12 @@
|
|||
using System.Runtime.CompilerServices;
|
||||
|
||||
[assembly: InternalsVisibleTo("Unity.Services.Core.Registration")]
|
||||
[assembly: InternalsVisibleTo("Unity.Services.Core.TestUtils")]
|
||||
|
||||
#if UNITY_INCLUDE_TESTS
|
||||
[assembly: InternalsVisibleTo("Unity.Services.Core.Tests")]
|
||||
[assembly: InternalsVisibleTo("Unity.Services.Core.EditorTests")]
|
||||
[assembly: InternalsVisibleTo("Unity.Services.Core.TestUtils.Tests")]
|
||||
[assembly: InternalsVisibleTo("Unity.Services.Core.TestUtils.EditorTests")]
|
||||
[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2")]
|
||||
#endif
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: dbc247fa3550140d3983e118699f3f18
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,197 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Unity.Services.Core.Scheduler.Internal
|
||||
{
|
||||
abstract class MinimumBinaryHeap
|
||||
{
|
||||
internal const float IncreaseFactor = 1.5f;
|
||||
internal const float DecreaseFactor = 2.0f;
|
||||
}
|
||||
|
||||
class MinimumBinaryHeap<T> : MinimumBinaryHeap
|
||||
{
|
||||
readonly IComparer<T> m_Comparer;
|
||||
readonly int m_MinimumCapacity;
|
||||
|
||||
T[] m_HeapArray;
|
||||
|
||||
internal IReadOnlyList<T> HeapArray => m_HeapArray;
|
||||
|
||||
public int Count { get; private set; }
|
||||
|
||||
public T Min => m_HeapArray[0];
|
||||
|
||||
public MinimumBinaryHeap(int minimumCapacity = 10)
|
||||
: this(Comparer<T>.Default, minimumCapacity) {}
|
||||
|
||||
public MinimumBinaryHeap(IComparer<T> comparer, int minimumCapacity = 10)
|
||||
: this(null, comparer, minimumCapacity) {}
|
||||
|
||||
internal MinimumBinaryHeap(ICollection<T> collection, IComparer<T> comparer, int minimumCapacity = 10)
|
||||
{
|
||||
if (minimumCapacity <= 0)
|
||||
{
|
||||
throw new ArgumentException("capacity must be more than 0");
|
||||
}
|
||||
|
||||
m_MinimumCapacity = minimumCapacity;
|
||||
m_Comparer = comparer;
|
||||
|
||||
Count = collection?.Count ?? 0;
|
||||
var startSize = Math.Max(Count, minimumCapacity);
|
||||
m_HeapArray = new T[startSize];
|
||||
if (collection is null)
|
||||
return;
|
||||
|
||||
// Reset count since we insert all items.
|
||||
Count = 0;
|
||||
foreach (var item in collection)
|
||||
{
|
||||
Insert(item);
|
||||
}
|
||||
}
|
||||
|
||||
public void Insert(T data)
|
||||
{
|
||||
IncreaseHeapCapacityWhenFull();
|
||||
var dataPos = Count;
|
||||
m_HeapArray[Count] = data;
|
||||
Count++;
|
||||
while (dataPos != 0
|
||||
&& m_Comparer.Compare(m_HeapArray[dataPos], m_HeapArray[Parent(dataPos)]) < 0)
|
||||
{
|
||||
Swap(ref m_HeapArray[dataPos], ref m_HeapArray[Parent(dataPos)]);
|
||||
dataPos = Parent(dataPos);
|
||||
}
|
||||
}
|
||||
|
||||
void IncreaseHeapCapacityWhenFull()
|
||||
{
|
||||
if (Count != m_HeapArray.Length)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var newCapacity = (int)Math.Ceiling(Count * IncreaseFactor);
|
||||
var newHeapArray = new T[newCapacity];
|
||||
Array.Copy(m_HeapArray, newHeapArray, Count);
|
||||
m_HeapArray = newHeapArray;
|
||||
}
|
||||
|
||||
public void Remove(T data)
|
||||
{
|
||||
var key = GetKey(data);
|
||||
if (key < 0)
|
||||
return;
|
||||
|
||||
while (key != 0)
|
||||
{
|
||||
Swap(ref m_HeapArray[key], ref m_HeapArray[Parent(key)]);
|
||||
key = Parent(key);
|
||||
}
|
||||
|
||||
ExtractMin();
|
||||
}
|
||||
|
||||
public T ExtractMin()
|
||||
{
|
||||
if (Count <= 0)
|
||||
{
|
||||
throw new InvalidOperationException("Can not ExtractMin: BinaryHeap is empty.");
|
||||
}
|
||||
|
||||
var data = m_HeapArray[0];
|
||||
|
||||
if (Count == 1)
|
||||
{
|
||||
Count--;
|
||||
m_HeapArray[0] = default;
|
||||
return data;
|
||||
}
|
||||
|
||||
Count--;
|
||||
m_HeapArray[0] = m_HeapArray[Count];
|
||||
m_HeapArray[Count] = default;
|
||||
MinHeapify();
|
||||
DecreaseHeapCapacityWhenSpare();
|
||||
return data;
|
||||
}
|
||||
|
||||
void DecreaseHeapCapacityWhenSpare()
|
||||
{
|
||||
var spareThreshold = (int)Math.Ceiling(m_HeapArray.Length / DecreaseFactor);
|
||||
if (Count <= m_MinimumCapacity
|
||||
|| Count > spareThreshold)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var newHeapArray = new T[Count];
|
||||
Array.Copy(m_HeapArray, newHeapArray, Count);
|
||||
m_HeapArray = newHeapArray;
|
||||
}
|
||||
|
||||
int GetKey(T data)
|
||||
{
|
||||
var key = -1;
|
||||
for (var i = 0; i < Count; i++)
|
||||
{
|
||||
if (m_HeapArray[i].Equals(data))
|
||||
{
|
||||
key = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return key;
|
||||
}
|
||||
|
||||
void MinHeapify()
|
||||
{
|
||||
int key;
|
||||
var smallest = 0;
|
||||
do
|
||||
{
|
||||
key = smallest;
|
||||
UpdateSmallestKey();
|
||||
if (smallest == key)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Swap(ref m_HeapArray[key], ref m_HeapArray[smallest]);
|
||||
}
|
||||
while (smallest != key);
|
||||
|
||||
void UpdateSmallestKey()
|
||||
{
|
||||
smallest = key;
|
||||
var leftKey = LeftChild(key);
|
||||
var rightKey = RightChild(key);
|
||||
|
||||
UpdateSmallestIfCandidateIsSmaller(leftKey);
|
||||
UpdateSmallestIfCandidateIsSmaller(rightKey);
|
||||
}
|
||||
|
||||
void UpdateSmallestIfCandidateIsSmaller(int candidate)
|
||||
{
|
||||
if (candidate >= Count
|
||||
|| m_Comparer.Compare(m_HeapArray[candidate], m_HeapArray[smallest]) >= 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
smallest = candidate;
|
||||
}
|
||||
}
|
||||
|
||||
static void Swap(ref T lhs, ref T rhs) => (lhs, rhs) = (rhs, lhs);
|
||||
|
||||
static int Parent(int key) => (key - 1) / 2;
|
||||
|
||||
static int LeftChild(int key) => 2 * key + 1;
|
||||
|
||||
static int RightChild(int key) => 2 * key + 2;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 0d3c36eeb6c9d46459ddede8d8248f3d
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,13 @@
|
|||
using System;
|
||||
|
||||
namespace Unity.Services.Core.Scheduler.Internal
|
||||
{
|
||||
class ScheduledInvocation
|
||||
{
|
||||
public Action Action;
|
||||
|
||||
public DateTime InvocationTime;
|
||||
|
||||
public long ActionId;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 07c14def3cbf48069bc2ef1c10d936c5
|
||||
timeCreated: 1661810859
|
|
@ -0,0 +1,35 @@
|
|||
using System.Collections.Generic;
|
||||
|
||||
namespace Unity.Services.Core.Scheduler.Internal
|
||||
{
|
||||
class ScheduledInvocationComparer : IComparer<ScheduledInvocation>
|
||||
{
|
||||
public int Compare(ScheduledInvocation x, ScheduledInvocation y)
|
||||
{
|
||||
if (ReferenceEquals(x, y))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (ReferenceEquals(null, y))
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (ReferenceEquals(null, x))
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
var compareResult = x.InvocationTime.CompareTo(y.InvocationTime);
|
||||
|
||||
// Actions with same invocation time will execute in id order (schedule order).
|
||||
if (compareResult == 0)
|
||||
{
|
||||
compareResult = x.ActionId.CompareTo(y.ActionId);
|
||||
}
|
||||
|
||||
return compareResult;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 15ce1c0ae24d491795cb39aa859e1e5b
|
||||
timeCreated: 1661972043
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 89da8fcade106e248a5d3190a19d35e3
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,9 @@
|
|||
using System;
|
||||
|
||||
namespace Unity.Services.Core.Scheduler.Internal
|
||||
{
|
||||
interface ITimeProvider
|
||||
{
|
||||
DateTime Now { get; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 5d6b5b582538b37428e63b2b6c506ea7
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,9 @@
|
|||
using System;
|
||||
|
||||
namespace Unity.Services.Core.Scheduler.Internal
|
||||
{
|
||||
class UtcTimeProvider : ITimeProvider
|
||||
{
|
||||
public DateTime Now => DateTime.UtcNow;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: af50c025240a3184ca1e1bf183b8a167
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,3 @@
|
|||
// This file is generated. Do not modify by hand.
|
||||
// XML documentation file not found. To check if public methods have XML comments,
|
||||
// make sure the XML doc file is present and located next to the scraped dll
|
|
@ -0,0 +1,7 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 93f3fa6ca41e54380b7362a953d48f4f
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,16 @@
|
|||
{
|
||||
"name": "Unity.Services.Core.Scheduler",
|
||||
"references": [
|
||||
"Unity.Services.Core.Internal",
|
||||
"Unity.Services.Core"
|
||||
],
|
||||
"includePlatforms": [],
|
||||
"excludePlatforms": [],
|
||||
"allowUnsafeCode": false,
|
||||
"overrideReferences": false,
|
||||
"precompiledReferences": [],
|
||||
"autoReferenced": false,
|
||||
"defineConstraints": [],
|
||||
"versionDefines": [],
|
||||
"noEngineReferences": false
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 13e422d7b3aa04e7994f974a3d50c391
|
||||
AssemblyDefinitionImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
Loading…
Add table
Add a link
Reference in a new issue