initial commit

This commit is contained in:
Jo 2025-01-07 02:06:59 +01:00
parent 6715289efe
commit 788c3389af
37645 changed files with 2526849 additions and 80 deletions

View file

@ -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);
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 57e3220438bdf43bebec66c4476f6677
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -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

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: dbc247fa3550140d3983e118699f3f18
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -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;
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 0d3c36eeb6c9d46459ddede8d8248f3d
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,13 @@
using System;
namespace Unity.Services.Core.Scheduler.Internal
{
class ScheduledInvocation
{
public Action Action;
public DateTime InvocationTime;
public long ActionId;
}
}

View file

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 07c14def3cbf48069bc2ef1c10d936c5
timeCreated: 1661810859

View file

@ -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;
}
}
}

View file

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 15ce1c0ae24d491795cb39aa859e1e5b
timeCreated: 1661972043

View file

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 89da8fcade106e248a5d3190a19d35e3
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,9 @@
using System;
namespace Unity.Services.Core.Scheduler.Internal
{
interface ITimeProvider
{
DateTime Now { get; }
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 5d6b5b582538b37428e63b2b6c506ea7
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,9 @@
using System;
namespace Unity.Services.Core.Scheduler.Internal
{
class UtcTimeProvider : ITimeProvider
{
public DateTime Now => DateTime.UtcNow;
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: af50c025240a3184ca1e1bf183b8a167
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -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

View file

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 93f3fa6ca41e54380b7362a953d48f4f
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -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
}

View file

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 13e422d7b3aa04e7994f974a3d50c391
AssemblyDefinitionImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant: