mirror of
https://github.com/copyrighttxt/watrbx-game-engine.git
synced 2026-09-06 13:47:48 +00:00
GEEKING
This commit is contained in:
@@ -0,0 +1,292 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Threading;
|
||||
using System.Web.Services.Protocols;
|
||||
using Microsoft.Ccr.Core;
|
||||
using Roblox.Ccr;
|
||||
|
||||
namespace Roblox.Common;
|
||||
|
||||
public class AsyncHelper
|
||||
{
|
||||
private struct AsyncLookupItem<T0, T1>
|
||||
{
|
||||
public int Index;
|
||||
|
||||
public T0 Key;
|
||||
|
||||
public PortSet<T1, Exception> Result;
|
||||
|
||||
public AsyncLookupItem(int index, T0 key, PortSet<T1, Exception> result)
|
||||
{
|
||||
Index = index;
|
||||
Key = key;
|
||||
Result = result;
|
||||
}
|
||||
}
|
||||
|
||||
public delegate void DoLookup<T0, T1>(T0 key, PortSet<T1, Exception> result);
|
||||
|
||||
private static readonly string performanceCategory;
|
||||
|
||||
private static readonly PerformanceCounter perfTotalAsyncCalls;
|
||||
|
||||
private static readonly PerformanceCounter timeoutCounts;
|
||||
|
||||
private static readonly PerformanceCounter riskyTimeoutCounts;
|
||||
|
||||
private static readonly PerformanceCounter perfPendingAsyncCallsCount;
|
||||
|
||||
static AsyncHelper()
|
||||
{
|
||||
performanceCategory = "Roblox.AsyncHelper";
|
||||
if (!PerformanceCounterCategory.Exists(performanceCategory))
|
||||
{
|
||||
CounterCreationDataCollection collection = new CounterCreationDataCollection
|
||||
{
|
||||
new CounterCreationData("Pending Async Calls", string.Empty, PerformanceCounterType.NumberOfItems64),
|
||||
new CounterCreationData("Total Async Calls", string.Empty, PerformanceCounterType.NumberOfItems64),
|
||||
new CounterCreationData("Timeouts", string.Empty, PerformanceCounterType.NumberOfItems64),
|
||||
new CounterCreationData("Risky Timeouts", string.Empty, PerformanceCounterType.NumberOfItems64)
|
||||
};
|
||||
PerformanceCounterCategory.Create(performanceCategory, string.Empty, PerformanceCounterCategoryType.SingleInstance, collection);
|
||||
}
|
||||
perfTotalAsyncCalls = new PerformanceCounter(performanceCategory, "Total Async Calls", readOnly: false);
|
||||
perfTotalAsyncCalls.RawValue = 0L;
|
||||
timeoutCounts = new PerformanceCounter(performanceCategory, "Timeouts", readOnly: false);
|
||||
timeoutCounts.RawValue = 0L;
|
||||
riskyTimeoutCounts = new PerformanceCounter(performanceCategory, "Risky Timeouts", readOnly: false);
|
||||
riskyTimeoutCounts.RawValue = 0L;
|
||||
perfPendingAsyncCallsCount = new PerformanceCounter(performanceCategory, "Pending Async Calls", readOnly: false);
|
||||
perfPendingAsyncCallsCount.RawValue = 0L;
|
||||
}
|
||||
|
||||
private static AsyncLookupItem<T0, T1>[] GetAsyncLookupItems<T0, T1>(ICollection<T0> lookupKeys, DoLookup<T0, T1> asyncLookup)
|
||||
{
|
||||
int index = 0;
|
||||
AsyncLookupItem<T0, T1>[] asyncLookupItems = new AsyncLookupItem<T0, T1>[lookupKeys.Count];
|
||||
foreach (T0 lookupKey in lookupKeys)
|
||||
{
|
||||
AsyncLookupItem<T0, T1> asyncLookupItem = new AsyncLookupItem<T0, T1>(index, lookupKey, new PortSet<T1, Exception>());
|
||||
asyncLookup(asyncLookupItem.Key, asyncLookupItem.Result);
|
||||
asyncLookupItems[index] = asyncLookupItem;
|
||||
index++;
|
||||
}
|
||||
return asyncLookupItems;
|
||||
}
|
||||
|
||||
private static IEnumerator<ITask> GetCollectionIterator<T0, T1>(ICollection<T0> keys, DoLookup<T0, T1> itemGetter, PortSet<ICollection<T1>, Exception> result)
|
||||
{
|
||||
AsyncLookupItem<T0, T1>[] lookupItems = GetAsyncLookupItems(keys, itemGetter);
|
||||
using IEnumerator<ITask> enumerarator = HandleAsyncLookupItems(lookupItems, result);
|
||||
while (enumerarator.MoveNext())
|
||||
{
|
||||
yield return enumerarator.Current;
|
||||
}
|
||||
}
|
||||
|
||||
private static IEnumerator<ITask> HandleAsyncLookupItems<T0, T1>(AsyncLookupItem<T0, T1>[] asyncLookupItems, PortSet<ICollection<T1>, Exception> result)
|
||||
{
|
||||
int countDown = asyncLookupItems.Length;
|
||||
if (countDown == 0)
|
||||
{
|
||||
result.Post(new List<T1>());
|
||||
yield break;
|
||||
}
|
||||
T1[] items = new T1[asyncLookupItems.Length];
|
||||
for (int i = 0; i < asyncLookupItems.Length; i++)
|
||||
{
|
||||
AsyncLookupItem<T0, T1> asyncLookupItem = asyncLookupItems[i];
|
||||
yield return (Choice)asyncLookupItem.Result;
|
||||
Exception ex = asyncLookupItem.Result.Test<Exception>();
|
||||
if (ex != null)
|
||||
{
|
||||
result.Post(ex);
|
||||
break;
|
||||
}
|
||||
items[asyncLookupItem.Index] = asyncLookupItem.Result;
|
||||
if (Interlocked.Decrement(ref countDown) == 0)
|
||||
{
|
||||
result.Post(items);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static Choice Call<TResult>(Func<AsyncCallback, object, IAsyncResult> begin, Func<IAsyncResult, TResult> end, PortSet<TResult, Exception> result, TimeSpan timeout, Action finalizer)
|
||||
{
|
||||
Choice choice = result;
|
||||
try
|
||||
{
|
||||
IAsyncResult asyncResult = begin(delegate(IAsyncResult ar)
|
||||
{
|
||||
perfPendingAsyncCallsCount.Decrement();
|
||||
PortSet<TResult, Exception> portSet2 = Interlocked.Exchange(ref result, null);
|
||||
if (portSet2 != null)
|
||||
{
|
||||
asyncResult = null;
|
||||
}
|
||||
try
|
||||
{
|
||||
TResult item = end(ar);
|
||||
portSet2?.Post(item);
|
||||
}
|
||||
catch (Exception item2)
|
||||
{
|
||||
portSet2?.Post(item2);
|
||||
}
|
||||
finally
|
||||
{
|
||||
FinalizeOnce(ref finalizer);
|
||||
}
|
||||
}, null);
|
||||
perfPendingAsyncCallsCount.Increment();
|
||||
perfTotalAsyncCalls.Increment();
|
||||
if (timeout < TimeSpan.MaxValue)
|
||||
{
|
||||
CcrService.Singleton.Activate<Receiver<DateTime>>(Arbiter.Receive(persist: false, CcrService.Singleton.TimeoutPort(timeout), delegate(DateTime time)
|
||||
{
|
||||
PortSet<TResult, Exception> portSet = Interlocked.Exchange(ref result, null);
|
||||
if (portSet != null)
|
||||
{
|
||||
portSet.Post(new TimeoutException(string.Format("AsyncHelper: timeout of {1} before {0}", end, time)));
|
||||
timeoutCounts.Increment();
|
||||
if (!(asyncResult is WebClientAsyncResult webClientAsyncResult))
|
||||
{
|
||||
riskyTimeoutCounts.Increment();
|
||||
}
|
||||
else
|
||||
{
|
||||
webClientAsyncResult.Abort();
|
||||
}
|
||||
}
|
||||
FinalizeOnce(ref finalizer);
|
||||
}));
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
FinalizeOnce(ref finalizer);
|
||||
Interlocked.Exchange(ref result, null)?.Post(ex);
|
||||
}
|
||||
return choice;
|
||||
}
|
||||
|
||||
private static void FinalizeOnce(ref Action finalizer)
|
||||
{
|
||||
Interlocked.Exchange(ref finalizer, null)?.Invoke();
|
||||
}
|
||||
|
||||
public static TResult BlockingCall<TResult>(Func<AsyncCallback, object, IAsyncResult> begin, Func<IAsyncResult, TResult> end, TimeSpan timeout, Action finalizer)
|
||||
{
|
||||
EventWaitHandle wait = new EventWaitHandle(initialState: false, EventResetMode.ManualReset);
|
||||
try
|
||||
{
|
||||
ExceptionPort<TResult> result = new ExceptionPort<TResult>();
|
||||
Call(begin, end, result, timeout, finalizer);
|
||||
CcrService.Singleton.Activate<Choice>(Arbiter.Choice(result, delegate(TResult t)
|
||||
{
|
||||
result.Post(t);
|
||||
wait.Set();
|
||||
}, delegate(Exception e)
|
||||
{
|
||||
result.Post(e);
|
||||
wait.Set();
|
||||
}));
|
||||
wait.WaitOne();
|
||||
return result;
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (wait != null)
|
||||
{
|
||||
((IDisposable)wait).Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void Call<TResult, Arg0>(Func<Arg0, AsyncCallback, object, IAsyncResult> begin, Arg0 arg0, Func<IAsyncResult, TResult> end, PortSet<TResult, Exception> result, TimeSpan timeout, Action finalizer)
|
||||
{
|
||||
Call((AsyncCallback a, object o) => begin(arg0, a, o), end, result, timeout, finalizer);
|
||||
}
|
||||
|
||||
public static Choice Call<TResult, Arg0, Arg1>(Func<Arg0, Arg1, AsyncCallback, object, IAsyncResult> begin, Arg0 arg0, Arg1 arg1, Func<IAsyncResult, TResult> end, PortSet<TResult, Exception> result, TimeSpan timeout, Action finalizer)
|
||||
{
|
||||
return Call((AsyncCallback a, object o) => begin(arg0, arg1, a, o), end, result, timeout, finalizer);
|
||||
}
|
||||
|
||||
public static void Call<TResult, Arg0, Arg1, Arg2>(Func<Arg0, Arg1, Arg2, AsyncCallback, object, IAsyncResult> begin, Arg0 arg0, Arg1 arg1, Arg2 arg2, Func<IAsyncResult, TResult> end, PortSet<TResult, Exception> result, TimeSpan timeout, Action finalizer)
|
||||
{
|
||||
Call((AsyncCallback a, object o) => begin(arg0, arg1, arg2, a, o), end, result, timeout, finalizer);
|
||||
}
|
||||
|
||||
public static void Call<TResult, Arg0, Arg1, Arg2, Arg3>(Func<Arg0, Arg1, Arg2, Arg3, AsyncCallback, object, IAsyncResult> begin, Arg0 arg0, Arg1 arg1, Arg2 arg2, Arg3 arg3, Func<IAsyncResult, TResult> end, PortSet<TResult, Exception> result, TimeSpan timeout, Action finalizer)
|
||||
{
|
||||
Call((AsyncCallback a, object o) => begin(arg0, arg1, arg2, arg3, a, o), end, result, timeout, finalizer);
|
||||
}
|
||||
|
||||
public static void Call<TResult, Arg0, Arg1, Arg2, Arg3, Arg4>(Func<Arg0, Arg1, Arg2, Arg3, Arg4, AsyncCallback, object, IAsyncResult> begin, Arg0 arg0, Arg1 arg1, Arg2 arg2, Arg3 arg3, Arg4 arg4, Func<IAsyncResult, TResult> end, PortSet<TResult, Exception> result, TimeSpan timeout, Action finalizer)
|
||||
{
|
||||
Call((AsyncCallback a, object o) => begin(arg0, arg1, arg2, arg3, arg4, a, o), end, result, timeout, finalizer);
|
||||
}
|
||||
|
||||
public static void Call(Func<AsyncCallback, object, IAsyncResult> begin, Action<IAsyncResult> end, SuccessFailurePort result, TimeSpan timeout, Action finalizer)
|
||||
{
|
||||
Call((AsyncCallback a, object o) => begin(a, o), delegate(IAsyncResult a)
|
||||
{
|
||||
end(a);
|
||||
return SuccessResult.Instance;
|
||||
}, result, timeout, finalizer);
|
||||
}
|
||||
|
||||
public static void Call<Arg0>(Func<Arg0, AsyncCallback, object, IAsyncResult> begin, Arg0 arg0, Action<IAsyncResult> end, SuccessFailurePort result, TimeSpan timeout, Action finalizer)
|
||||
{
|
||||
Call((AsyncCallback a, object o) => begin(arg0, a, o), delegate(IAsyncResult a)
|
||||
{
|
||||
end(a);
|
||||
return SuccessResult.Instance;
|
||||
}, result, timeout, finalizer);
|
||||
}
|
||||
|
||||
public static void Call<Arg0, Arg1>(Func<Arg0, Arg1, AsyncCallback, object, IAsyncResult> begin, Arg0 arg0, Arg1 arg1, Action<IAsyncResult> end, SuccessFailurePort result, TimeSpan timeout, Action finalizer)
|
||||
{
|
||||
Call((AsyncCallback a, object o) => begin(arg0, arg1, a, o), delegate(IAsyncResult a)
|
||||
{
|
||||
end(a);
|
||||
return SuccessResult.Instance;
|
||||
}, result, timeout, finalizer);
|
||||
}
|
||||
|
||||
public static void Call<Arg0, Arg1, Arg2>(Func<Arg0, Arg1, Arg2, AsyncCallback, object, IAsyncResult> begin, Arg0 arg0, Arg1 arg1, Arg2 arg2, Action<IAsyncResult> end, SuccessFailurePort result, TimeSpan timeout, Action finalizer)
|
||||
{
|
||||
Call((AsyncCallback a, object o) => begin(arg0, arg1, arg2, a, o), delegate(IAsyncResult a)
|
||||
{
|
||||
end(a);
|
||||
return SuccessResult.Instance;
|
||||
}, result, timeout, finalizer);
|
||||
}
|
||||
|
||||
public static void Call<Arg0, Arg1, Arg2, Arg3>(Func<Arg0, Arg1, Arg2, Arg3, AsyncCallback, object, IAsyncResult> begin, Arg0 arg0, Arg1 arg1, Arg2 arg2, Arg3 arg3, Action<IAsyncResult> end, SuccessFailurePort result, TimeSpan timeout, Action finalizer)
|
||||
{
|
||||
Call((AsyncCallback a, object o) => begin(arg0, arg1, arg2, arg3, a, o), delegate(IAsyncResult a)
|
||||
{
|
||||
end(a);
|
||||
return SuccessResult.Instance;
|
||||
}, result, timeout, finalizer);
|
||||
}
|
||||
|
||||
public static void Call<Arg0, Arg1, Arg2, Arg3, Arg4>(Func<Arg0, Arg1, Arg2, Arg3, Arg4, AsyncCallback, object, IAsyncResult> begin, Arg0 arg0, Arg1 arg1, Arg2 arg2, Arg3 arg3, Arg4 arg4, Action<IAsyncResult> end, SuccessFailurePort result, TimeSpan timeout, Action finalizer)
|
||||
{
|
||||
Call((AsyncCallback a, object o) => begin(arg0, arg1, arg2, arg3, arg4, a, o), delegate(IAsyncResult a)
|
||||
{
|
||||
end(a);
|
||||
return SuccessResult.Instance;
|
||||
}, result, timeout, finalizer);
|
||||
}
|
||||
|
||||
public static void GetCollection<T0, T1>(ICollection<T0> keys, DoLookup<T0, T1> itemGetter, PortSet<ICollection<T1>, Exception> result)
|
||||
{
|
||||
CcrService.Singleton.SpawnIterator(keys, itemGetter, result, GetCollectionIterator);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
using System;
|
||||
using Microsoft.Ccr.Core;
|
||||
|
||||
namespace Roblox.Common;
|
||||
|
||||
public class AsyncWorkQueue<T>
|
||||
{
|
||||
internal class WorkItem
|
||||
{
|
||||
private Action _CompletionTask;
|
||||
|
||||
private T _Item;
|
||||
|
||||
private SuccessFailurePort _Result;
|
||||
|
||||
internal Action CompletionTask => _CompletionTask;
|
||||
|
||||
internal T Item => _Item;
|
||||
|
||||
internal SuccessFailurePort Result => _Result;
|
||||
|
||||
internal WorkItem(T item)
|
||||
{
|
||||
_Item = item;
|
||||
}
|
||||
|
||||
internal WorkItem(T item, Action completionTask)
|
||||
{
|
||||
_CompletionTask = completionTask;
|
||||
_Item = item;
|
||||
}
|
||||
|
||||
internal WorkItem(T item, SuccessFailurePort result)
|
||||
{
|
||||
_Item = item;
|
||||
_Result = result;
|
||||
}
|
||||
}
|
||||
|
||||
public delegate void AsyncItemHandler(T item, SuccessFailurePort result);
|
||||
|
||||
private DispatcherQueue _DispatcherQueue;
|
||||
|
||||
private AsyncItemHandler _ItemHandler;
|
||||
|
||||
private Port<WorkItem> _QueuedItems = new Port<WorkItem>();
|
||||
|
||||
public AsyncWorkQueue(DispatcherQueue dispatcherQueue, AsyncItemHandler itemHandler)
|
||||
{
|
||||
if (itemHandler == null)
|
||||
{
|
||||
throw new ApplicationException("AsyncWorkQueue initialization failed. Valid AsyncItemHandler required.");
|
||||
}
|
||||
_DispatcherQueue = dispatcherQueue;
|
||||
_ItemHandler = itemHandler;
|
||||
Receiver<WorkItem> receiver = Arbiter.Receive(persist: true, _QueuedItems, delegate(WorkItem workItem)
|
||||
{
|
||||
DoWork(workItem);
|
||||
});
|
||||
Arbiter.Activate(_DispatcherQueue, receiver);
|
||||
}
|
||||
|
||||
private void DoCompletionTask(SuccessFailurePort itemHandlerResult, Action completionTask)
|
||||
{
|
||||
Choice choice = Arbiter.Choice(itemHandlerResult, delegate
|
||||
{
|
||||
completionTask();
|
||||
}, delegate(Exception failure)
|
||||
{
|
||||
ExceptionHandler.LogException(failure);
|
||||
});
|
||||
Arbiter.Activate(_DispatcherQueue, choice);
|
||||
}
|
||||
|
||||
private void DoWork(WorkItem workItem)
|
||||
{
|
||||
SuccessFailurePort result = ((workItem.Result == null) ? new SuccessFailurePort() : workItem.Result);
|
||||
_ItemHandler(workItem.Item, result);
|
||||
if (workItem.CompletionTask != null)
|
||||
{
|
||||
DoCompletionTask(result, workItem.CompletionTask);
|
||||
}
|
||||
}
|
||||
|
||||
public void EnqueueWorkItem(T item)
|
||||
{
|
||||
_QueuedItems.Post(new WorkItem(item));
|
||||
}
|
||||
|
||||
public void EnqueueWorkItem(T item, Action completionTask)
|
||||
{
|
||||
_QueuedItems.Post(new WorkItem(item, completionTask));
|
||||
}
|
||||
|
||||
public void EnqueueWorkItem(T item, SuccessFailurePort result)
|
||||
{
|
||||
_QueuedItems.Post(new WorkItem(item, result));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Roblox.Common;
|
||||
|
||||
public static class CollectionsHelper
|
||||
{
|
||||
public static T GetRandomElement<T>(this IList<T> self)
|
||||
{
|
||||
if (self.Count == 0)
|
||||
{
|
||||
return default(T);
|
||||
}
|
||||
return self[new Random().Next(self.Count)];
|
||||
}
|
||||
|
||||
public static void Swap<T>(T[] array, int i, int j)
|
||||
{
|
||||
T d = array[i];
|
||||
array[i] = array[j];
|
||||
array[j] = d;
|
||||
}
|
||||
|
||||
public static IEnumerable<T> RandomizeCollection<T>(this ICollection<T> collection, int numberofItemsToReturn)
|
||||
{
|
||||
return collection.RandomizeCollection(numberofItemsToReturn, (T _) => true);
|
||||
}
|
||||
|
||||
public static IEnumerable<T> RandomizeCollection<T>(this ICollection<T> collection, int numberofItemsToReturn, Func<T, bool> predicate)
|
||||
{
|
||||
int count = collection.Count;
|
||||
T[] array = new T[count];
|
||||
collection.CopyTo(array, 0);
|
||||
if (numberofItemsToReturn > count)
|
||||
{
|
||||
numberofItemsToReturn = count;
|
||||
}
|
||||
for (int newCount = 0; newCount < numberofItemsToReturn && newCount < count; newCount++)
|
||||
{
|
||||
int randIdx = new Random().Next(count);
|
||||
if (!predicate(array[randIdx]))
|
||||
{
|
||||
count--;
|
||||
Swap(array, randIdx, count);
|
||||
newCount--;
|
||||
}
|
||||
else
|
||||
{
|
||||
Swap(array, newCount, randIdx);
|
||||
}
|
||||
}
|
||||
if (numberofItemsToReturn > count)
|
||||
{
|
||||
numberofItemsToReturn = count;
|
||||
}
|
||||
for (int i = 0; i < numberofItemsToReturn; i++)
|
||||
{
|
||||
yield return array[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
namespace Roblox.Common;
|
||||
|
||||
public class CompletionSignal
|
||||
{
|
||||
public static readonly CompletionSignal Instance = new CompletionSignal();
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Roblox.Common;
|
||||
|
||||
public static class Converters
|
||||
{
|
||||
public static string ConvertIntegersToCSV(int[] integers)
|
||||
{
|
||||
return string.Join(",", Array.ConvertAll(integers, ConvertIntegerToString));
|
||||
}
|
||||
|
||||
private static string ConvertIntegerToString(int integer)
|
||||
{
|
||||
return integer.ToString();
|
||||
}
|
||||
|
||||
public static int[] ConvertCSVToIntegers(string[] strings)
|
||||
{
|
||||
return Array.ConvertAll(strings, ConvertStringToInteger);
|
||||
}
|
||||
|
||||
private static int ConvertStringToInteger(string s)
|
||||
{
|
||||
return int.Parse(s);
|
||||
}
|
||||
|
||||
public static List<T> EnumToList<T>()
|
||||
{
|
||||
Type typeFromHandle = typeof(T);
|
||||
if (typeFromHandle.BaseType != typeof(Enum))
|
||||
{
|
||||
throw new ArgumentException("T must be of type System.Enum");
|
||||
}
|
||||
Array values = Enum.GetValues(typeFromHandle);
|
||||
List<T> outList = new List<T>(values.Length);
|
||||
foreach (object value in values)
|
||||
{
|
||||
outList.Add((T)Enum.Parse(typeFromHandle, ((int)value).ToString()));
|
||||
}
|
||||
return outList;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,175 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Threading;
|
||||
using Microsoft.Ccr.Core;
|
||||
|
||||
namespace Roblox.Common;
|
||||
|
||||
public sealed class CustomThreadPool : IDisposable
|
||||
{
|
||||
public delegate void WorkItem();
|
||||
|
||||
private class WaitQueueItem
|
||||
{
|
||||
public WaitCallback Callback;
|
||||
|
||||
public ExecutionContext Context;
|
||||
|
||||
public object State;
|
||||
}
|
||||
|
||||
private Dispatcher _Dispatcher;
|
||||
|
||||
private DispatcherQueue _DispatcherQueue;
|
||||
|
||||
private static readonly string _PerformanceCategory = "Roblox.CustomThreadPool";
|
||||
|
||||
private readonly Port<WaitQueueItem> _WaitQueueItemsPort = new Port<WaitQueueItem>();
|
||||
|
||||
public int QueueCount
|
||||
{
|
||||
get
|
||||
{
|
||||
CheckDisposed();
|
||||
return _DispatcherQueue.Count;
|
||||
}
|
||||
}
|
||||
|
||||
public int PendingTaskCount
|
||||
{
|
||||
get
|
||||
{
|
||||
CheckDisposed();
|
||||
return _DispatcherQueue.Dispatcher.PendingTaskCount;
|
||||
}
|
||||
}
|
||||
|
||||
public long ProcessedTaskCount
|
||||
{
|
||||
get
|
||||
{
|
||||
CheckDisposed();
|
||||
return _DispatcherQueue.Dispatcher.ProcessedTaskCount;
|
||||
}
|
||||
}
|
||||
|
||||
public int WorkerThreadCount
|
||||
{
|
||||
get
|
||||
{
|
||||
CheckDisposed();
|
||||
return _DispatcherQueue.Dispatcher.WorkerThreadCount;
|
||||
}
|
||||
}
|
||||
|
||||
public CustomThreadPool(string name, int threadCount)
|
||||
{
|
||||
CustomThreadPool customThreadPool = this;
|
||||
_Dispatcher = new Dispatcher(threadCount, ThreadPriority.Normal, DispatcherOptions.UseBackgroundThreads, $"{name} Dispatcher");
|
||||
_DispatcherQueue = new PatchedDispatcherQueue($"{name} Dispatcher Queue", _Dispatcher);
|
||||
Arbiter.Activate(_DispatcherQueue, Arbiter.Receive(persist: true, _WaitQueueItemsPort, ExecuteWorkItem));
|
||||
Thread thread = new Thread((ThreadStart)delegate
|
||||
{
|
||||
customThreadPool.MonitorPerformance(name);
|
||||
});
|
||||
thread.IsBackground = true;
|
||||
thread.Name = $"Performance Monitor: {name}";
|
||||
thread.Start();
|
||||
}
|
||||
|
||||
private void CheckDisposed()
|
||||
{
|
||||
if (_DispatcherQueue == null)
|
||||
{
|
||||
throw new ObjectDisposedException(GetType().Name);
|
||||
}
|
||||
}
|
||||
|
||||
private void ExecuteWorkItem(WaitQueueItem item)
|
||||
{
|
||||
try
|
||||
{
|
||||
ExecutionContext.Run(item.Context, item.Callback.Invoke, item.State);
|
||||
}
|
||||
catch (ThreadAbortException)
|
||||
{
|
||||
}
|
||||
catch (ThreadInterruptedException)
|
||||
{
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
ExceptionHandler.LogException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
private void MonitorPerformance(string instanceName)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!PerformanceCounterCategory.Exists(_PerformanceCategory))
|
||||
{
|
||||
CounterCreationDataCollection collection = new CounterCreationDataCollection();
|
||||
collection.Add(new CounterCreationData("Dispatcher Queue Count", string.Empty, PerformanceCounterType.NumberOfItems32));
|
||||
collection.Add(new CounterCreationData("Dispatcher Queue Current Scheduling Rate", string.Empty, PerformanceCounterType.RateOfCountsPerSecond64));
|
||||
collection.Add(new CounterCreationData("Dispatcher Queue Scheduled Task Count", string.Empty, PerformanceCounterType.NumberOfItems64));
|
||||
collection.Add(new CounterCreationData("Dispatcher Pending Task Count", string.Empty, PerformanceCounterType.NumberOfItems32));
|
||||
collection.Add(new CounterCreationData("Dispatcher Processed Task Count", string.Empty, PerformanceCounterType.NumberOfItems64));
|
||||
collection.Add(new CounterCreationData("Dispatcher Worker Thread Count", string.Empty, PerformanceCounterType.NumberOfItems32));
|
||||
PerformanceCounterCategory.Create(_PerformanceCategory, string.Empty, PerformanceCounterCategoryType.SingleInstance, collection);
|
||||
}
|
||||
PerformanceCounter perfDispatcherQueueCount = new PerformanceCounter(_PerformanceCategory, "Dispatcher Queue Count", readOnly: false);
|
||||
PerformanceCounter perfDispatcherQueueCurrentSchedulingRate = new PerformanceCounter(_PerformanceCategory, "Dispatcher Queue Current Scheduling Rate", readOnly: false);
|
||||
PerformanceCounter perfDispatcherQueueScheduledTaskCount = new PerformanceCounter(_PerformanceCategory, "Dispatcher Queue Scheduled Task Count", readOnly: false);
|
||||
PerformanceCounter perfDispatcherPendingTaskCount = new PerformanceCounter(_PerformanceCategory, "Dispatcher Pending Task Count", readOnly: false);
|
||||
PerformanceCounter perfDispatcherProcessedTaskCount = new PerformanceCounter(_PerformanceCategory, "Dispatcher Processed Task Count", readOnly: false);
|
||||
PerformanceCounter perfDispatcherWorkerThreadCount = new PerformanceCounter(_PerformanceCategory, "Dispatcher Worker Thread Count", readOnly: false);
|
||||
long num = _DispatcherQueue.ScheduledTaskCount;
|
||||
while (true)
|
||||
{
|
||||
perfDispatcherQueueCount.RawValue = _DispatcherQueue.Count;
|
||||
long scheduledTaskCount = _DispatcherQueue.ScheduledTaskCount;
|
||||
perfDispatcherQueueCurrentSchedulingRate.IncrementBy(scheduledTaskCount - num);
|
||||
num = (perfDispatcherQueueScheduledTaskCount.RawValue = scheduledTaskCount);
|
||||
perfDispatcherPendingTaskCount.RawValue = _DispatcherQueue.Dispatcher.PendingTaskCount;
|
||||
perfDispatcherProcessedTaskCount.RawValue = _DispatcherQueue.Dispatcher.ProcessedTaskCount;
|
||||
perfDispatcherWorkerThreadCount.RawValue = _DispatcherQueue.Dispatcher.WorkerThreadCount;
|
||||
Thread.Sleep(500);
|
||||
}
|
||||
}
|
||||
catch (ThreadAbortException)
|
||||
{
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
ExceptionHandler.LogException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_DispatcherQueue?.Dispose();
|
||||
_DispatcherQueue = null;
|
||||
_Dispatcher?.Dispose();
|
||||
_Dispatcher = null;
|
||||
}
|
||||
|
||||
public void QueueUserWorkItem(WaitCallback callback)
|
||||
{
|
||||
QueueUserWorkItem(callback, null);
|
||||
}
|
||||
|
||||
public void QueueUserWorkItem(WaitCallback callback, object state)
|
||||
{
|
||||
CheckDisposed();
|
||||
if (callback == null)
|
||||
{
|
||||
throw new ArgumentNullException("callback");
|
||||
}
|
||||
WaitQueueItem item = new WaitQueueItem();
|
||||
item.Callback = callback;
|
||||
item.State = state;
|
||||
item.Context = ExecutionContext.Capture();
|
||||
_WaitQueueItemsPort.Post(item);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
using System.Diagnostics;
|
||||
using System.Net;
|
||||
using System.Net.Mail;
|
||||
using System.Net.Mime;
|
||||
using System.Text.RegularExpressions;
|
||||
using Roblox.Common.Mime;
|
||||
using Roblox.Common.Properties;
|
||||
|
||||
namespace Roblox.Common;
|
||||
|
||||
public class Email
|
||||
{
|
||||
private static string defaultSMTPServer;
|
||||
|
||||
private const string linebreak = "\r\n";
|
||||
|
||||
static Email()
|
||||
{
|
||||
defaultSMTPServer = Settings.Default.SMTPServer;
|
||||
EventLog.WriteEntry("Web Server", $"Roblox.Common.Email.SMTPServer is {defaultSMTPServer}", EventLogEntryType.Information);
|
||||
}
|
||||
|
||||
public static void SendEmail(string to, string from, string subject, string body)
|
||||
{
|
||||
SendEmail(to, from, subject, body, defaultSMTPServer);
|
||||
}
|
||||
|
||||
public static void SendEmail(string to, string from, string subject, string body, string smtpServer)
|
||||
{
|
||||
if (to.Trim().Length == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
SmtpClient smtpClient = new SmtpClient(smtpServer);
|
||||
using MailMessage message = new MailMessage(from, to, subject, body);
|
||||
smtpClient.Send(message);
|
||||
}
|
||||
|
||||
public static void SendMimeEmail(string to, string from, string subject, string plaintextbody, string htmlbody)
|
||||
{
|
||||
ContentType mimeType = new ContentType("text/html");
|
||||
AlternateView alternate = AlternateView.CreateAlternateViewFromString(htmlbody, mimeType);
|
||||
SmtpClient client = new SmtpClient(defaultSMTPServer);
|
||||
client.Credentials = CredentialCache.DefaultNetworkCredentials;
|
||||
using MailMessage message = new MailMessage(from, to, subject, plaintextbody);
|
||||
message.AlternateViews.Add(alternate);
|
||||
client.Send(message);
|
||||
}
|
||||
|
||||
public static void SendMimeEmail(string to, string from, string subject, string plaintextbody, string htmlbody, string smtpServer)
|
||||
{
|
||||
if (to.Trim().Length == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
ContentType mimeType = new ContentType("text/html");
|
||||
AlternateView alternate = AlternateView.CreateAlternateViewFromString(htmlbody, mimeType);
|
||||
SmtpClient client = new SmtpClient(smtpServer);
|
||||
client.Credentials = CredentialCache.DefaultNetworkCredentials;
|
||||
using MailMessage message = new MailMessage(from, to, subject, plaintextbody);
|
||||
message.AlternateViews.Add(alternate);
|
||||
client.Send(message);
|
||||
}
|
||||
|
||||
public static MailMessageEx ParseEmail(string rawEmail)
|
||||
{
|
||||
MimeReader reader = new MimeReader(Regex.Split(rawEmail, "\r\n"));
|
||||
MimeEntity mime = reader.CreateMimeEntity();
|
||||
return mime.ToMailMessageEx();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
namespace Roblox.Common;
|
||||
|
||||
public class EntityCollectionItem
|
||||
{
|
||||
public static int ApiVersion = 1;
|
||||
|
||||
public string id { get; private set; }
|
||||
|
||||
public string __stamp { get; private set; }
|
||||
|
||||
public EntityCollectionItem(string id, string stamp)
|
||||
{
|
||||
this.id = id;
|
||||
__stamp = stamp;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Roblox.Common;
|
||||
|
||||
public class EntityCollectionJson : Json
|
||||
{
|
||||
public static int ApiVersion = 1;
|
||||
|
||||
public IEnumerable<EntityCollectionItem> data { get; private set; }
|
||||
|
||||
public EntityCollectionJson(IEnumerable<EntityCollectionItem> data)
|
||||
{
|
||||
this.data = data;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
namespace Roblox.Common;
|
||||
|
||||
public class EntityCountJson : Json
|
||||
{
|
||||
public static int ApiVersion = 1;
|
||||
|
||||
public string count { get; private set; }
|
||||
|
||||
public EntityCountJson(string count)
|
||||
{
|
||||
this.count = count;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,140 @@
|
||||
using System;
|
||||
using System.Threading;
|
||||
|
||||
namespace Roblox.Common;
|
||||
|
||||
public class EventCounter : IDisposable
|
||||
{
|
||||
private class Bucket
|
||||
{
|
||||
private long eventCount;
|
||||
|
||||
public long EventCount => eventCount;
|
||||
|
||||
public Bucket(long value)
|
||||
{
|
||||
eventCount = value;
|
||||
}
|
||||
|
||||
public void LogEvent()
|
||||
{
|
||||
Interlocked.Increment(ref eventCount);
|
||||
}
|
||||
}
|
||||
|
||||
private class BucketAggregation
|
||||
{
|
||||
private Bucket current;
|
||||
|
||||
private BucketAggregation next;
|
||||
|
||||
public readonly TimeSpan Span;
|
||||
|
||||
public BucketAggregation(TimeSpan span)
|
||||
{
|
||||
Span = span;
|
||||
}
|
||||
|
||||
public void AddBucket(Bucket bucket)
|
||||
{
|
||||
if (current == null)
|
||||
{
|
||||
current = bucket;
|
||||
return;
|
||||
}
|
||||
if (next == null)
|
||||
{
|
||||
next = new BucketAggregation(Span.Add(Span));
|
||||
}
|
||||
next.AddBucket(new Bucket(current.EventCount + bucket.EventCount));
|
||||
current = null;
|
||||
}
|
||||
|
||||
public long GetEventCount(TimeSpan span)
|
||||
{
|
||||
if (current != null)
|
||||
{
|
||||
if (span == Span)
|
||||
{
|
||||
return current.EventCount;
|
||||
}
|
||||
if (span > Span && next != null)
|
||||
{
|
||||
return current.EventCount + next.GetEventCount(span.Subtract(Span));
|
||||
}
|
||||
return current.EventCount * span.Ticks / span.Ticks;
|
||||
}
|
||||
if (next != null)
|
||||
{
|
||||
return next.GetEventCount(span);
|
||||
}
|
||||
return 0L;
|
||||
}
|
||||
|
||||
public long GetTotalEventCount()
|
||||
{
|
||||
long eventCount = 0L;
|
||||
if (current != null)
|
||||
{
|
||||
eventCount += current.EventCount;
|
||||
}
|
||||
if (next != null)
|
||||
{
|
||||
eventCount += next.GetTotalEventCount();
|
||||
}
|
||||
return eventCount;
|
||||
}
|
||||
}
|
||||
|
||||
private Bucket currentBucket;
|
||||
|
||||
private readonly BucketAggregation head = new BucketAggregation(SmallestInterval);
|
||||
|
||||
private readonly Timer timer;
|
||||
|
||||
public static readonly TimeSpan SmallestInterval = TimeSpan.FromSeconds(2.0);
|
||||
|
||||
public EventCounter()
|
||||
{
|
||||
currentBucket = new Bucket(0L);
|
||||
timer = new Timer(delegate
|
||||
{
|
||||
head.AddBucket(currentBucket);
|
||||
currentBucket = new Bucket(0L);
|
||||
}, null, SmallestInterval, SmallestInterval);
|
||||
}
|
||||
|
||||
public long GetEventCount(TimeSpan sample)
|
||||
{
|
||||
if (sample == TimeSpan.MaxValue)
|
||||
{
|
||||
return GetTotalEventCount();
|
||||
}
|
||||
return head.GetEventCount(sample);
|
||||
}
|
||||
|
||||
public double GetEventsPerSecond(TimeSpan sample)
|
||||
{
|
||||
long eventCount = GetEventCount(sample);
|
||||
if (sample == TimeSpan.Zero)
|
||||
{
|
||||
return 0.0;
|
||||
}
|
||||
return (double)eventCount / sample.TotalSeconds;
|
||||
}
|
||||
|
||||
public long GetTotalEventCount()
|
||||
{
|
||||
return head.GetTotalEventCount();
|
||||
}
|
||||
|
||||
public void LogEvent()
|
||||
{
|
||||
currentBucket.LogEvent();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
timer.Dispose();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,578 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.IO;
|
||||
using System.Net;
|
||||
using System.Runtime.Serialization.Json;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading;
|
||||
using System.Xml;
|
||||
using Microsoft.Ccr.Core;
|
||||
|
||||
namespace Roblox.Common;
|
||||
|
||||
public static class Extensions
|
||||
{
|
||||
public enum QueryOperators
|
||||
{
|
||||
AND,
|
||||
OR,
|
||||
NOT,
|
||||
NEAR
|
||||
}
|
||||
|
||||
private static readonly char quote = '"';
|
||||
|
||||
private static readonly char space = ' ';
|
||||
|
||||
private static readonly char[] spaceDelimiter = new char[1] { space };
|
||||
|
||||
public static IEnumerable<T> AsEnumerable<T>(this T item)
|
||||
{
|
||||
yield return item;
|
||||
}
|
||||
|
||||
private static IEnumerator<ITask> StreamToArrayIterator(Stream stream, PortSet<byte[], Exception> result)
|
||||
{
|
||||
if (stream is MemoryStream str2)
|
||||
{
|
||||
result.Post(str2.ToArray());
|
||||
yield break;
|
||||
}
|
||||
byte[] buff = new byte[1024];
|
||||
MemoryStream memoryStream;
|
||||
MemoryStream str = (memoryStream = new MemoryStream());
|
||||
using (memoryStream)
|
||||
{
|
||||
PortSet<int, Exception> readPort = new PortSet<int, Exception>();
|
||||
Exception ex;
|
||||
while (true)
|
||||
{
|
||||
AsyncHelper.Call(stream.BeginRead, buff, 0, buff.Length, stream.EndRead, readPort, TimeSpan.FromMinutes(1.0), null);
|
||||
yield return (ITask)readPort;
|
||||
ex = readPort;
|
||||
if (ex != null)
|
||||
{
|
||||
break;
|
||||
}
|
||||
PortSet<int, Exception> index = readPort;
|
||||
if ((int)index == 0)
|
||||
{
|
||||
result.Post(str.ToArray());
|
||||
yield break;
|
||||
}
|
||||
str.Write(buff, 0, index);
|
||||
}
|
||||
result.Post(ex);
|
||||
}
|
||||
}
|
||||
|
||||
private static IEnumerator<ITask> StreamToArrayAndDisposeIterator(Stream stream, PortSet<byte[], Exception> result)
|
||||
{
|
||||
using (stream)
|
||||
{
|
||||
if (stream is MemoryStream memStream2)
|
||||
{
|
||||
result.Post(memStream2.ToArray());
|
||||
yield break;
|
||||
}
|
||||
byte[] buff = new byte[1024];
|
||||
MemoryStream memStream = new MemoryStream();
|
||||
using (memStream)
|
||||
{
|
||||
PortSet<int, Exception> readPort = new PortSet<int, Exception>();
|
||||
while (true)
|
||||
{
|
||||
AsyncHelper.Call(stream.BeginRead, buff, 0, buff.Length, stream.EndRead, readPort, TimeSpan.FromMinutes(1.0), null);
|
||||
yield return (Choice)readPort;
|
||||
Exception ex = readPort;
|
||||
if (ex != null)
|
||||
{
|
||||
result.Post(ex);
|
||||
yield break;
|
||||
}
|
||||
int index = readPort;
|
||||
if (index == 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
memStream.Write(buff, 0, index);
|
||||
}
|
||||
result.Post(memStream.ToArray());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static IEnumerator<ITask> StreamToStringIterator(Stream stream, PortSet<string, Exception> result)
|
||||
{
|
||||
ASCIIEncoding encoding = new ASCIIEncoding();
|
||||
if (stream is MemoryStream memStream2)
|
||||
{
|
||||
result.Post(encoding.GetString(memStream2.ToArray()));
|
||||
yield break;
|
||||
}
|
||||
byte[] buf = new byte[1024];
|
||||
MemoryStream memStream = new MemoryStream();
|
||||
using (memStream)
|
||||
{
|
||||
PortSet<int, Exception> readPort = new PortSet<int, Exception>();
|
||||
while (true)
|
||||
{
|
||||
AsyncHelper.Call(stream.BeginRead, buf, 0, buf.Length, stream.EndRead, readPort, TimeSpan.FromMinutes(1.0), null);
|
||||
yield return (Choice)readPort;
|
||||
Exception ex = readPort;
|
||||
if (ex != null)
|
||||
{
|
||||
result.Post(ex);
|
||||
yield break;
|
||||
}
|
||||
int index = readPort;
|
||||
if (index == 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
memStream.Write(buf, 0, index);
|
||||
}
|
||||
result.Post(encoding.GetString(memStream.ToArray()));
|
||||
}
|
||||
}
|
||||
|
||||
private static IEnumerator<ITask> StreamToStringAndDisposeIterator(Stream stream, PortSet<string, Exception> result)
|
||||
{
|
||||
using (stream)
|
||||
{
|
||||
ASCIIEncoding encoding = new ASCIIEncoding();
|
||||
if (stream is MemoryStream memStream2)
|
||||
{
|
||||
result.Post(encoding.GetString(memStream2.ToArray()));
|
||||
yield break;
|
||||
}
|
||||
byte[] buff = new byte[1024];
|
||||
MemoryStream memStream = new MemoryStream();
|
||||
using (memStream)
|
||||
{
|
||||
PortSet<int, Exception> readPort = new PortSet<int, Exception>();
|
||||
while (true)
|
||||
{
|
||||
AsyncHelper.Call(stream.BeginRead, buff, 0, buff.Length, stream.EndRead, readPort, TimeSpan.FromMinutes(1.0), null);
|
||||
yield return (Choice)readPort;
|
||||
Exception ex = readPort;
|
||||
if (ex != null)
|
||||
{
|
||||
result.Post(ex);
|
||||
yield break;
|
||||
}
|
||||
int index = readPort;
|
||||
if (index == 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
memStream.Write(buff, 0, index);
|
||||
}
|
||||
result.Post(encoding.GetString(memStream.ToArray()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void Exists(this FileInfo fileInfo, PortSet<bool, Exception> result)
|
||||
{
|
||||
FileHelper.ExecuteTask(File.Exists, fileInfo.FullName, result);
|
||||
}
|
||||
|
||||
public static void ToArray(this Stream stream, PortSet<byte[], Exception> result)
|
||||
{
|
||||
CcrService.Singleton.SpawnIterator(stream, result, StreamToArrayIterator);
|
||||
}
|
||||
|
||||
public static void ToArrayAndDispose(this Stream stream, PortSet<byte[], Exception> result)
|
||||
{
|
||||
CcrService.Singleton.SpawnIterator(stream, result, StreamToArrayAndDisposeIterator);
|
||||
}
|
||||
|
||||
public static void ToString(this Stream stream, PortSet<string, Exception> result)
|
||||
{
|
||||
CcrService.Singleton.SpawnIterator(stream, result, StreamToStringIterator);
|
||||
}
|
||||
|
||||
public static void ToStringAndDispose(this Stream stream, PortSet<string, Exception> result)
|
||||
{
|
||||
CcrService.Singleton.SpawnIterator(stream, result, StreamToStringIterator);
|
||||
}
|
||||
|
||||
public static void WriteAsync(this Stream stream, byte[] data, Action<Result<CompletionSignal>> resultHandler)
|
||||
{
|
||||
Request<CompletionSignal>.HandleResponse((Func<byte[], int, int, AsyncCallback, object, IAsyncResult>)stream.BeginWrite, data, 0, data.Length, (Action<IAsyncResult>)stream.EndWrite, resultHandler);
|
||||
}
|
||||
|
||||
public static void WriteAndDisposeAsync(this Stream stream, byte[] data, Action<Result<CompletionSignal>> resultHandler)
|
||||
{
|
||||
Request<CompletionSignal>.HandleResponse(stream.BeginWrite, data, 0, data.Length, delegate(IAsyncResult ar)
|
||||
{
|
||||
try
|
||||
{
|
||||
stream.EndWrite(ar);
|
||||
return CompletionSignal.Instance;
|
||||
}
|
||||
finally
|
||||
{
|
||||
stream?.Dispose();
|
||||
}
|
||||
}, resultHandler);
|
||||
}
|
||||
|
||||
public static string Convert(this string value, Encoding originalEncoding, Encoding newEncoding)
|
||||
{
|
||||
byte[] valueBytes = value.GetBytes(originalEncoding);
|
||||
byte[] newBytes = Encoding.Convert(originalEncoding, newEncoding, valueBytes);
|
||||
return newEncoding.GetString(newBytes);
|
||||
}
|
||||
|
||||
public static byte[] GetBytes(this string value)
|
||||
{
|
||||
return value.GetBytes(new ASCIIEncoding());
|
||||
}
|
||||
|
||||
public static byte[] GetBytes(this string value, Encoding encoding)
|
||||
{
|
||||
return encoding.GetBytes(value);
|
||||
}
|
||||
|
||||
public static bool IsEven(this int value)
|
||||
{
|
||||
return value % 2 == 0;
|
||||
}
|
||||
|
||||
public static bool IsOdd(this int value)
|
||||
{
|
||||
return !value.IsEven();
|
||||
}
|
||||
|
||||
public static int[] IndexesOf(this string s, char value)
|
||||
{
|
||||
List<int> indexes = new List<int>();
|
||||
for (int i = s.IndexOf(value); i > -1; i = s.IndexOf(value, i + 1))
|
||||
{
|
||||
indexes.Add(i);
|
||||
}
|
||||
return indexes.ToArray();
|
||||
}
|
||||
|
||||
public static string ParsedSubString(this string text, string startToken, string endToken, bool startWithLastOccurrences)
|
||||
{
|
||||
return text.ParsedSubString(startToken, endToken, includeStartToken: false, includeEndToken: false, startWithLastOccurrences);
|
||||
}
|
||||
|
||||
public static string ParsedSubString(this string text, string startToken, string endToken)
|
||||
{
|
||||
return text.ParsedSubString(startToken, endToken, includeStartToken: false, includeEndToken: false, startWithLastOccurrences: false);
|
||||
}
|
||||
|
||||
public static string ParsedSubString(this string text, string startToken, string endToken, bool includeStartToken, bool includeEndToken, bool startWithLastOccurrences)
|
||||
{
|
||||
int startIndex = ((!startWithLastOccurrences) ? text.ToLower().IndexOf(startToken.ToLower()) : text.ToLower().LastIndexOf(startToken.ToLower()));
|
||||
startIndex = ((startIndex >= 0) ? (startIndex + ((!includeStartToken) ? startToken.Length : 0)) : 0);
|
||||
text = text.Substring(startIndex);
|
||||
int length = text.ToLower().IndexOf(endToken.ToLower());
|
||||
length = ((length >= 0) ? (length + (includeEndToken ? endToken.Length : 0)) : text.Length);
|
||||
return text.Substring(0, length);
|
||||
}
|
||||
|
||||
public static void GetRequestStreamAsync(this WebRequest webRequest, Action<Result<Stream>> resultHandler)
|
||||
{
|
||||
Request<Stream>.HandleResponse(webRequest.BeginGetRequestStream, webRequest.EndGetRequestStream, resultHandler);
|
||||
}
|
||||
|
||||
public static IAsyncResult BeginGetResponseStream(this WebResponse webResponse, AsyncCallback callback, object state)
|
||||
{
|
||||
FastAsyncResult<Stream> result = new FastAsyncResult<Stream>(callback, state);
|
||||
RobloxThreadPool.QueueUserWorkItem(delegate
|
||||
{
|
||||
try
|
||||
{
|
||||
result.SetCompleted(webResponse.GetResponseStream());
|
||||
}
|
||||
catch (ThreadAbortException)
|
||||
{
|
||||
throw;
|
||||
}
|
||||
catch (Exception completed)
|
||||
{
|
||||
result.SetCompleted(completed);
|
||||
}
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
public static Stream EndGetResponseStream(this WebResponse webResponse, IAsyncResult asyncResult)
|
||||
{
|
||||
using FastAsyncResult<Stream> result = asyncResult as FastAsyncResult<Stream>;
|
||||
return result.GetResult();
|
||||
}
|
||||
|
||||
public static void GetResponseStreamAsync(this WebResponse webResponse, Action<Result<Stream>> resultHandler)
|
||||
{
|
||||
Request<Stream>.HandleResponse(webResponse.BeginGetResponseStream, webResponse.EndGetResponseStream, resultHandler);
|
||||
}
|
||||
|
||||
public static IAsyncResult BeginLoad(this XmlDocument xmlDocument, Stream data, AsyncCallback callback, object state)
|
||||
{
|
||||
FastAsyncResult result = new FastAsyncResult(callback, state);
|
||||
RobloxThreadPool.QueueUserWorkItem(delegate
|
||||
{
|
||||
try
|
||||
{
|
||||
xmlDocument.Load(data);
|
||||
result.SetCompleted();
|
||||
}
|
||||
catch (ThreadAbortException)
|
||||
{
|
||||
throw;
|
||||
}
|
||||
catch (Exception completed)
|
||||
{
|
||||
result.SetCompleted(completed);
|
||||
}
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
public static void EndLoad(this XmlDocument _, IAsyncResult asyncResult)
|
||||
{
|
||||
using FastAsyncResult result = asyncResult as FastAsyncResult;
|
||||
if (result.Error != null)
|
||||
{
|
||||
throw result.Error;
|
||||
}
|
||||
}
|
||||
|
||||
public static void LoadAsync(this XmlDocument xmlDocument, Stream data, Action<Result<CompletionSignal>> resultHandler)
|
||||
{
|
||||
Request<CompletionSignal>.HandleResponse(xmlDocument.BeginLoad, data, delegate(IAsyncResult ar)
|
||||
{
|
||||
xmlDocument.EndLoad(ar);
|
||||
return CompletionSignal.Instance;
|
||||
}, resultHandler);
|
||||
}
|
||||
|
||||
public static string ToQuery(this string query)
|
||||
{
|
||||
return query.ToQuery(QueryOperators.AND);
|
||||
}
|
||||
|
||||
public static string ToQuery(this string query, QueryOperators defaultOperator)
|
||||
{
|
||||
string op = $" {defaultOperator.ToString()} ";
|
||||
string q = "";
|
||||
if (!string.IsNullOrEmpty(query.Trim()))
|
||||
{
|
||||
string searchTerm = "";
|
||||
char qout = '"';
|
||||
char ws = ' ';
|
||||
List<string> searchTerms = new List<string>();
|
||||
bool isQuot = false;
|
||||
query = Regex.Replace(query.Trim(), "\\s{2,}", ws.ToString());
|
||||
char[] bytes = query.ToCharArray();
|
||||
for (int i = 0; i < query.Length; i++)
|
||||
{
|
||||
char @char = bytes[i];
|
||||
searchTerm += @char;
|
||||
if (@char.Equals(qout))
|
||||
{
|
||||
isQuot = !isQuot;
|
||||
}
|
||||
if (isQuot)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (@char.Equals(qout))
|
||||
{
|
||||
AddSearchTerm(searchTerm, ref searchTerms, defaultOperator);
|
||||
searchTerm = "";
|
||||
}
|
||||
if (@char.Equals(ws))
|
||||
{
|
||||
if (!string.IsNullOrEmpty(searchTerm.Trim()))
|
||||
{
|
||||
AddSearchTerm(searchTerm, ref searchTerms, defaultOperator);
|
||||
}
|
||||
searchTerm = "";
|
||||
}
|
||||
}
|
||||
if (searchTerm != "" && (!IsQueryOperator(searchTerm) || searchTerm.Equals(")")))
|
||||
{
|
||||
AddSearchTerm(searchTerm, ref searchTerms, defaultOperator);
|
||||
}
|
||||
if (!IsQueryOperator(searchTerms[0]))
|
||||
{
|
||||
q = searchTerms[0];
|
||||
}
|
||||
for (int j = 1; j < searchTerms.Count; j++)
|
||||
{
|
||||
if (!IsQueryOperator(searchTerms[j]) && !IsQueryOperator(searchTerms[j - 1]) && !searchTerms[j].Trim().Equals(")"))
|
||||
{
|
||||
q += op;
|
||||
}
|
||||
if (searchTerms[j].ToUpper() == QueryOperators.NOT.ToString() && !IsQueryOperator(searchTerms[j - 1]))
|
||||
{
|
||||
q += op;
|
||||
}
|
||||
q = q + " " + searchTerms[j];
|
||||
}
|
||||
q = Regex.Replace(q.Trim(), "\\s{2,}", ws.ToString());
|
||||
}
|
||||
return q;
|
||||
}
|
||||
|
||||
private static bool IsQueryOperator(string s)
|
||||
{
|
||||
s = s.Trim().ToLower();
|
||||
string[] names = Enum.GetNames(typeof(QueryOperators));
|
||||
foreach (string name in names)
|
||||
{
|
||||
if (s == name.ToLower())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (!(s == "("))
|
||||
{
|
||||
return s == ")";
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private static string AddQuotesToSearchTerm(string searchTerm)
|
||||
{
|
||||
char qout = '"';
|
||||
char openBkt = '(';
|
||||
char closeBkt = ')';
|
||||
searchTerm = searchTerm.Trim();
|
||||
if (!IsQueryOperator(searchTerm) && searchTerm[0] == qout)
|
||||
{
|
||||
if (searchTerm[searchTerm.Length - 1] != qout)
|
||||
{
|
||||
searchTerm += qout;
|
||||
}
|
||||
else
|
||||
{
|
||||
searchTerm = ((searchTerm[0] != openBkt) ? (qout + searchTerm) : (openBkt + AddQuotesToSearchTerm(searchTerm.Substring(1))));
|
||||
if (searchTerm[searchTerm.Length - 1] == closeBkt)
|
||||
{
|
||||
searchTerm = AddQuotesToSearchTerm(searchTerm.Substring(0, searchTerm.Length - 1)) + closeBkt;
|
||||
}
|
||||
else if (searchTerm[searchTerm.Length - 1] != qout)
|
||||
{
|
||||
searchTerm += qout;
|
||||
}
|
||||
}
|
||||
}
|
||||
return searchTerm;
|
||||
}
|
||||
|
||||
private static void AddSearchTerm(string searchTerm, ref List<string> searchTerms, QueryOperators defaultOperator)
|
||||
{
|
||||
int idx = searchTerm.IndexOf(quote);
|
||||
searchTerm = Regex.Replace(searchTerm, "^(\\&{1,}|\\+{1,})", " and ");
|
||||
searchTerm = Regex.Replace(searchTerm, "^(\\|{1,})", " or ");
|
||||
searchTerm = Regex.Replace(searchTerm, "^(\\~{1,})", " near ");
|
||||
searchTerm = Regex.Replace(searchTerm, "^(\\-{1,}|\\!{1,})", $" {defaultOperator.ToString()} not ");
|
||||
searchTerm = Regex.Replace(searchTerm.Trim(), "\\s+", space.ToString());
|
||||
int length = searchTerm.Length;
|
||||
if (searchTerm.Contains(quote.ToString()))
|
||||
{
|
||||
length = idx;
|
||||
}
|
||||
if (searchTerm.Substring(0, length).Contains(space.ToString()))
|
||||
{
|
||||
string[] array = searchTerm.Substring(0, length).Split(spaceDelimiter, StringSplitOptions.RemoveEmptyEntries);
|
||||
foreach (string subTerm in array)
|
||||
{
|
||||
searchTerms.Add(AddQuotesToSearchTerm(subTerm));
|
||||
}
|
||||
if (length < searchTerm.Length)
|
||||
{
|
||||
searchTerms.Add(AddQuotesToSearchTerm(searchTerm.Substring(length)));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
searchTerms.Add(AddQuotesToSearchTerm(searchTerm));
|
||||
}
|
||||
}
|
||||
|
||||
public static string ToDescription(this Enum value)
|
||||
{
|
||||
DescriptionAttribute[] array = (DescriptionAttribute[])value.GetType().GetField(value.ToString()).GetCustomAttributes(typeof(DescriptionAttribute), inherit: false);
|
||||
if (array != null && array.Length != 0)
|
||||
{
|
||||
return array[0].Description;
|
||||
}
|
||||
return value.ToString();
|
||||
}
|
||||
|
||||
public static bool UnorderedEqual<T>(this ICollection<T> list, ICollection<T> listToCompare)
|
||||
{
|
||||
if (list.Count != listToCompare.Count)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
Dictionary<T, int> d = new Dictionary<T, int>();
|
||||
foreach (T key in list)
|
||||
{
|
||||
if (d.TryGetValue(key, out var num2))
|
||||
{
|
||||
d[key] = num2 + 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
d.Add(key, 1);
|
||||
}
|
||||
}
|
||||
foreach (T key2 in listToCompare)
|
||||
{
|
||||
if (!d.TryGetValue(key2, out var num))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (num == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
d[key2] = num - 1;
|
||||
}
|
||||
foreach (int value in d.Values)
|
||||
{
|
||||
if (value != 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public static string ToJSON<TSource>(this IEnumerable<TSource> source) where TSource : class
|
||||
{
|
||||
string json = "[";
|
||||
foreach (TSource obj in source)
|
||||
{
|
||||
json = json + obj.ToJSON() + ",";
|
||||
}
|
||||
json = json.TrimEnd(',');
|
||||
return json + "]";
|
||||
}
|
||||
|
||||
public static string ToJSON<T>(this T obj) where T : class
|
||||
{
|
||||
DataContractJsonSerializer dataContractJsonSerializer = new DataContractJsonSerializer(typeof(T));
|
||||
using MemoryStream stream = new MemoryStream();
|
||||
dataContractJsonSerializer.WriteObject(stream, obj);
|
||||
return Encoding.Default.GetString(stream.ToArray());
|
||||
}
|
||||
|
||||
public static T FromJSON<T>(this string json) where T : class
|
||||
{
|
||||
using MemoryStream stream = new MemoryStream(Encoding.Unicode.GetBytes(json));
|
||||
return new DataContractJsonSerializer(typeof(T)).ReadObject(stream) as T;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
using System;
|
||||
using System.Threading;
|
||||
|
||||
namespace Roblox.Common;
|
||||
|
||||
public class FastAsyncResult : IAsyncResult, IDisposable
|
||||
{
|
||||
private Exception _Error;
|
||||
|
||||
private readonly AsyncCallback _Callback;
|
||||
|
||||
private bool _IsCompleted = true;
|
||||
|
||||
private readonly object _State;
|
||||
|
||||
private ManualResetEvent _WaitHandle;
|
||||
|
||||
public WaitHandle AsyncWaitHandle => CreateWaitHandle();
|
||||
|
||||
public object AsyncState => _State;
|
||||
|
||||
public bool CompletedSynchronously => false;
|
||||
|
||||
public bool IsCompleted => _IsCompleted;
|
||||
|
||||
public Exception Error => _Error;
|
||||
|
||||
public FastAsyncResult(AsyncCallback callback, object state)
|
||||
{
|
||||
_Callback = callback;
|
||||
_State = state;
|
||||
}
|
||||
|
||||
private WaitHandle CreateWaitHandle()
|
||||
{
|
||||
if (_WaitHandle != null)
|
||||
{
|
||||
return _WaitHandle;
|
||||
}
|
||||
ManualResetEvent resetEvt = new ManualResetEvent(initialState: false);
|
||||
if (Interlocked.CompareExchange(ref _WaitHandle, resetEvt, null) != null)
|
||||
{
|
||||
resetEvt.Close();
|
||||
}
|
||||
if (_IsCompleted)
|
||||
{
|
||||
_WaitHandle.Set();
|
||||
}
|
||||
return _WaitHandle;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_WaitHandle?.Close();
|
||||
}
|
||||
|
||||
public void SetCompleted()
|
||||
{
|
||||
_IsCompleted = true;
|
||||
Thread.MemoryBarrier();
|
||||
_WaitHandle?.Set();
|
||||
_Callback?.Invoke(this);
|
||||
}
|
||||
|
||||
public void SetCompleted(Exception error)
|
||||
{
|
||||
_Error = error;
|
||||
SetCompleted();
|
||||
}
|
||||
|
||||
public void SetFailed(Exception error)
|
||||
{
|
||||
_Error = error;
|
||||
}
|
||||
}
|
||||
public class FastAsyncResult<T> : FastAsyncResult, IResult<T>
|
||||
{
|
||||
private T result;
|
||||
|
||||
[Obsolete("This property can throw, which is bad design. Use GetResult() and SetCompleted() instead")]
|
||||
public T Token
|
||||
{
|
||||
get
|
||||
{
|
||||
return GetResult();
|
||||
}
|
||||
set
|
||||
{
|
||||
result = value;
|
||||
}
|
||||
}
|
||||
|
||||
public FastAsyncResult(AsyncCallback callback, object state)
|
||||
: base(callback, state)
|
||||
{
|
||||
}
|
||||
|
||||
public T GetResult()
|
||||
{
|
||||
if (base.Error != null)
|
||||
{
|
||||
throw base.Error;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public void SetCompleted(T result)
|
||||
{
|
||||
this.result = result;
|
||||
SetCompleted();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,137 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Threading;
|
||||
using Microsoft.Ccr.Core;
|
||||
|
||||
namespace Roblox.Common;
|
||||
|
||||
public class FileHelper : IDisposable
|
||||
{
|
||||
private static readonly int _DefaultPoolSize;
|
||||
|
||||
private static readonly DispatcherQueue _DispatcherQueue;
|
||||
|
||||
private static readonly Port<FileHelper> _Pool;
|
||||
|
||||
private FileHelper()
|
||||
{
|
||||
}
|
||||
|
||||
static FileHelper()
|
||||
{
|
||||
_DefaultPoolSize = 25;
|
||||
_DispatcherQueue = new PatchedDispatcherQueue("Roblox FileHelper", new Dispatcher(0, ThreadPriority.Normal, DispatcherOptions.UseBackgroundThreads, "Roblox FileHelper"));
|
||||
_Pool = new Port<FileHelper>();
|
||||
Thread thread = new Thread(MonitorPerformance);
|
||||
thread.IsBackground = true;
|
||||
thread.Name = "Performance Monitor: FileHelper";
|
||||
thread.Start();
|
||||
for (int i = 0; i < _DefaultPoolSize; i++)
|
||||
{
|
||||
AddToPool();
|
||||
}
|
||||
}
|
||||
|
||||
public static void ExecuteTask<TResult>(Func<TResult> func, PortSet<TResult, Exception> result)
|
||||
{
|
||||
Port<FileHelper> port = new Port<FileHelper>();
|
||||
Get(port);
|
||||
CcrService.Singleton.Activate<Receiver<FileHelper>>(Arbiter.Receive(persist: false, port, delegate(FileHelper fh)
|
||||
{
|
||||
try
|
||||
{
|
||||
result.Post(func());
|
||||
}
|
||||
catch (Exception item)
|
||||
{
|
||||
result.Post(item);
|
||||
}
|
||||
finally
|
||||
{
|
||||
fh.Dispose();
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
public static void ExecuteTask<Arg0, TResult>(Func<Arg0, TResult> func, Arg0 arg0, PortSet<TResult, Exception> result)
|
||||
{
|
||||
Port<FileHelper> port = new Port<FileHelper>();
|
||||
Get(port);
|
||||
CcrService.Singleton.Activate<Receiver<FileHelper>>(Arbiter.Receive(persist: false, port, delegate(FileHelper fh)
|
||||
{
|
||||
try
|
||||
{
|
||||
result.Post(func(arg0));
|
||||
}
|
||||
catch (Exception item)
|
||||
{
|
||||
result.Post(item);
|
||||
}
|
||||
finally
|
||||
{
|
||||
fh.Dispose();
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
public static void AddToPool()
|
||||
{
|
||||
_Pool.Post(new FileHelper());
|
||||
}
|
||||
|
||||
private static void Get(Port<FileHelper> result)
|
||||
{
|
||||
Arbiter.Activate(_DispatcherQueue, Arbiter.Receive(persist: false, _Pool, delegate(FileHelper h)
|
||||
{
|
||||
result.Post(h);
|
||||
}));
|
||||
}
|
||||
|
||||
private static void MonitorPerformance()
|
||||
{
|
||||
try
|
||||
{
|
||||
string categoryName = "Roblox FileHelper";
|
||||
if (!PerformanceCounterCategory.Exists(categoryName))
|
||||
{
|
||||
CounterCreationDataCollection collection = new CounterCreationDataCollection();
|
||||
collection.Add(new CounterCreationData("Dispatcher Queue Count", string.Empty, PerformanceCounterType.NumberOfItems32));
|
||||
collection.Add(new CounterCreationData("Dispatcher Queue Current Scheduling Rate", string.Empty, PerformanceCounterType.RateOfCountsPerSecond64));
|
||||
collection.Add(new CounterCreationData("Dispatcher Queue Scheduled Task Count", string.Empty, PerformanceCounterType.NumberOfItems64));
|
||||
collection.Add(new CounterCreationData("Dispatcher Pending Task Count", string.Empty, PerformanceCounterType.NumberOfItems32));
|
||||
collection.Add(new CounterCreationData("Dispatcher Processed Task Count", string.Empty, PerformanceCounterType.NumberOfItems64));
|
||||
collection.Add(new CounterCreationData("Dispatcher Worker Thread Count", string.Empty, PerformanceCounterType.NumberOfItems32));
|
||||
PerformanceCounterCategory.Create(categoryName, string.Empty, PerformanceCounterCategoryType.SingleInstance, collection);
|
||||
}
|
||||
PerformanceCounter perfDispatcherQueueCount = new PerformanceCounter(categoryName, "Dispatcher Queue Count", readOnly: false);
|
||||
PerformanceCounter perfDispatcherQueueCurrentSchedulingRate = new PerformanceCounter(categoryName, "Dispatcher Queue Current Scheduling Rate", readOnly: false);
|
||||
PerformanceCounter perfDispatcherQueueScheduledTaskCount = new PerformanceCounter(categoryName, "Dispatcher Queue Scheduled Task Count", readOnly: false);
|
||||
PerformanceCounter perfDispatcherPendingTaskCount = new PerformanceCounter(categoryName, "Dispatcher Pending Task Count", readOnly: false);
|
||||
PerformanceCounter perfDispatcherProcessedTaskCount = new PerformanceCounter(categoryName, "Dispatcher Processed Task Count", readOnly: false);
|
||||
PerformanceCounter perfDispatcherWorkerThreadCount = new PerformanceCounter(categoryName, "Dispatcher Worker Thread Count", readOnly: false);
|
||||
long num = _DispatcherQueue.ScheduledTaskCount;
|
||||
while (true)
|
||||
{
|
||||
perfDispatcherQueueCount.RawValue = _DispatcherQueue.Count;
|
||||
long scheduledTaskCount = _DispatcherQueue.ScheduledTaskCount;
|
||||
perfDispatcherQueueCurrentSchedulingRate.IncrementBy(scheduledTaskCount - num);
|
||||
num = (perfDispatcherQueueScheduledTaskCount.RawValue = scheduledTaskCount);
|
||||
perfDispatcherPendingTaskCount.RawValue = _DispatcherQueue.Dispatcher.PendingTaskCount;
|
||||
perfDispatcherProcessedTaskCount.RawValue = _DispatcherQueue.Dispatcher.ProcessedTaskCount;
|
||||
perfDispatcherWorkerThreadCount.RawValue = _DispatcherQueue.Dispatcher.WorkerThreadCount;
|
||||
}
|
||||
}
|
||||
catch (ThreadAbortException)
|
||||
{
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
ExceptionHandler.LogException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
AddToPool();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
namespace Roblox.Common;
|
||||
|
||||
public abstract class IJSONizable
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
namespace Roblox.Common;
|
||||
|
||||
public interface IResult<T>
|
||||
{
|
||||
T GetResult();
|
||||
}
|
||||
@@ -0,0 +1,145 @@
|
||||
using System.Text;
|
||||
using System.Web.Script.Serialization;
|
||||
|
||||
namespace Roblox.Common;
|
||||
|
||||
public class JSON
|
||||
{
|
||||
public static string WriteError(string message)
|
||||
{
|
||||
return $"{{\"Error\" : \"{message}\"}}";
|
||||
}
|
||||
|
||||
public static string WriteProperty(string propertyName, string propertyValue)
|
||||
{
|
||||
return $"{{\"{propertyName}\" : \"{propertyValue}\"}}";
|
||||
}
|
||||
|
||||
public static string AddListToJSONObject(string jsonString, string propertyName, string propertyValue)
|
||||
{
|
||||
if (!jsonString.Contains("{") && !jsonString.Contains("}") && !jsonString.Contains("[") && !jsonString.Contains("]"))
|
||||
{
|
||||
jsonString = "{" + jsonString + "}";
|
||||
}
|
||||
bool isArray = false;
|
||||
if (jsonString.StartsWith("[") && jsonString.EndsWith("]"))
|
||||
{
|
||||
isArray = true;
|
||||
jsonString = jsonString.Remove(0, 1);
|
||||
jsonString = jsonString.Remove(jsonString.Length - 1);
|
||||
}
|
||||
string property = $"\"{propertyName}\" : {propertyValue}";
|
||||
if (!isArray)
|
||||
{
|
||||
jsonString = jsonString.Remove(jsonString.Length - 1);
|
||||
if (jsonString.Length != 1)
|
||||
{
|
||||
jsonString += ",";
|
||||
}
|
||||
jsonString = jsonString + property + "}";
|
||||
}
|
||||
else
|
||||
{
|
||||
if (jsonString.Length != 0)
|
||||
{
|
||||
jsonString += ",";
|
||||
}
|
||||
jsonString += property;
|
||||
}
|
||||
if (isArray)
|
||||
{
|
||||
jsonString = "[" + jsonString + "]";
|
||||
}
|
||||
return jsonString;
|
||||
}
|
||||
|
||||
public static string AddObjectToJSONObject(string jsonString, string propertyName, string propertyValue)
|
||||
{
|
||||
if (!jsonString.Contains("{") && !jsonString.Contains("}") && !jsonString.Contains("[") && !jsonString.Contains("]"))
|
||||
{
|
||||
jsonString = "{" + jsonString + "}";
|
||||
}
|
||||
bool isArray = false;
|
||||
if (jsonString.StartsWith("[") && jsonString.EndsWith("]"))
|
||||
{
|
||||
isArray = true;
|
||||
jsonString = jsonString.Remove(0, 1);
|
||||
jsonString = jsonString.Remove(jsonString.Length - 1);
|
||||
}
|
||||
string property = $"\"{propertyName}\" : {propertyValue}";
|
||||
if (!isArray)
|
||||
{
|
||||
jsonString = jsonString.Remove(jsonString.Length - 1);
|
||||
if (jsonString.Length != 1)
|
||||
{
|
||||
jsonString += ",";
|
||||
}
|
||||
jsonString = jsonString + property + "}";
|
||||
}
|
||||
else
|
||||
{
|
||||
if (jsonString.Length != 0)
|
||||
{
|
||||
jsonString += ",";
|
||||
}
|
||||
jsonString += property;
|
||||
}
|
||||
if (isArray)
|
||||
{
|
||||
jsonString = "[" + jsonString + "]";
|
||||
}
|
||||
return jsonString;
|
||||
}
|
||||
|
||||
public static string AddPropertyToJSONObject(string jsonString, string propertyName, string propertyValue)
|
||||
{
|
||||
if (!jsonString.Contains("{") && !jsonString.Contains("}") && !jsonString.Contains("[") && !jsonString.Contains("]"))
|
||||
{
|
||||
jsonString = "{" + jsonString + "}";
|
||||
}
|
||||
bool isArray = false;
|
||||
if (jsonString.StartsWith("[") && jsonString.EndsWith("]"))
|
||||
{
|
||||
isArray = true;
|
||||
jsonString = jsonString.Remove(0, 1);
|
||||
jsonString = jsonString.Remove(jsonString.Length - 1);
|
||||
}
|
||||
string property = $"\"{propertyName}\" : \"{propertyValue}\"";
|
||||
if (!isArray)
|
||||
{
|
||||
jsonString = jsonString.Remove(jsonString.Length - 1);
|
||||
if (jsonString.Length != 1)
|
||||
{
|
||||
jsonString += ",";
|
||||
}
|
||||
jsonString = jsonString + property + "}";
|
||||
}
|
||||
else
|
||||
{
|
||||
if (jsonString.Length != 0)
|
||||
{
|
||||
jsonString += ",";
|
||||
}
|
||||
jsonString += property;
|
||||
}
|
||||
if (isArray)
|
||||
{
|
||||
jsonString = "[" + jsonString + "]";
|
||||
}
|
||||
return jsonString;
|
||||
}
|
||||
}
|
||||
public abstract class Json
|
||||
{
|
||||
protected string SerializedData;
|
||||
|
||||
public virtual string Serialize()
|
||||
{
|
||||
if (SerializedData == null)
|
||||
{
|
||||
string value = new JavaScriptSerializer().Serialize(this);
|
||||
SerializedData = value.Convert(Encoding.Unicode, Encoding.UTF8);
|
||||
}
|
||||
return SerializedData;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
namespace Roblox.Common;
|
||||
|
||||
public static class MailHeaders
|
||||
{
|
||||
public const string Bcc = "bcc";
|
||||
|
||||
public const string Cc = "cc";
|
||||
|
||||
public const string Date = "date";
|
||||
|
||||
public const string From = "from";
|
||||
|
||||
public const string Importance = "importance";
|
||||
|
||||
public const string InReplyTo = "in-reply-to";
|
||||
|
||||
public const string MessageId = "message-id";
|
||||
|
||||
public const string Received = "received";
|
||||
|
||||
public const string ReplyTo = "reply-to";
|
||||
|
||||
public const string Subject = "subject";
|
||||
|
||||
public const string To = "to";
|
||||
}
|
||||
@@ -0,0 +1,217 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Net.Mail;
|
||||
using System.Net.Mime;
|
||||
using System.Text.RegularExpressions;
|
||||
using Roblox.Common.Mime;
|
||||
|
||||
namespace Roblox.Common;
|
||||
|
||||
public class MailMessageEx : MailMessage
|
||||
{
|
||||
public const string EmailRegexPattern = "(['\"]{1,}.+['\"]{1,}\\s+)?<?[\\w\\.\\-]+@[^\\.][\\w\\.\\-]+\\.[a-z]{2,}>?";
|
||||
|
||||
private long _octets;
|
||||
|
||||
private int _messageNumber;
|
||||
|
||||
private static readonly char[] AddressDelimiters = new char[2] { ',', ';' };
|
||||
|
||||
private List<MailMessageEx> _children;
|
||||
|
||||
public long Octets
|
||||
{
|
||||
get
|
||||
{
|
||||
return _octets;
|
||||
}
|
||||
set
|
||||
{
|
||||
_octets = value;
|
||||
}
|
||||
}
|
||||
|
||||
public int MessageNumber
|
||||
{
|
||||
get
|
||||
{
|
||||
return _messageNumber;
|
||||
}
|
||||
internal set
|
||||
{
|
||||
_messageNumber = value;
|
||||
}
|
||||
}
|
||||
|
||||
public List<MailMessageEx> Children => _children;
|
||||
|
||||
public string PlainTextBody
|
||||
{
|
||||
get
|
||||
{
|
||||
if (ContentType.MediaType == MediaTypes.TextPlain)
|
||||
{
|
||||
return base.Body;
|
||||
}
|
||||
foreach (AlternateView view in base.AlternateViews)
|
||||
{
|
||||
if (view.ContentType.MediaType == MediaTypes.TextPlain)
|
||||
{
|
||||
StreamReader rdr = new StreamReader(view.ContentStream);
|
||||
return rdr.ReadToEnd();
|
||||
}
|
||||
}
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
public DateTime DeliveryDate
|
||||
{
|
||||
get
|
||||
{
|
||||
string date = GetHeader("date");
|
||||
if (string.IsNullOrEmpty(date))
|
||||
{
|
||||
return DateTime.MinValue;
|
||||
}
|
||||
return Convert.ToDateTime(date);
|
||||
}
|
||||
}
|
||||
|
||||
public MailAddress ReturnAddress
|
||||
{
|
||||
get
|
||||
{
|
||||
string replyTo = GetHeader("reply-to");
|
||||
if (string.IsNullOrEmpty(replyTo))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
return CreateMailAddress(replyTo);
|
||||
}
|
||||
}
|
||||
|
||||
public string Routing => GetHeader("received");
|
||||
|
||||
public string MessageId => GetHeader("message-id");
|
||||
|
||||
public string ReplyToMessageId => GetHeader("in-reply-to", stripBrackts: true);
|
||||
|
||||
public string MimeVersion => GetHeader("mime-version");
|
||||
|
||||
public string ContentId => GetHeader("content-id");
|
||||
|
||||
public string ContentDescription => GetHeader("content-description");
|
||||
|
||||
public ContentDisposition ContentDisposition
|
||||
{
|
||||
get
|
||||
{
|
||||
string contentDisposition = GetHeader("content-disposition");
|
||||
if (string.IsNullOrEmpty(contentDisposition))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
return new ContentDisposition(contentDisposition);
|
||||
}
|
||||
}
|
||||
|
||||
public ContentType ContentType
|
||||
{
|
||||
get
|
||||
{
|
||||
string contentType = GetHeader("content-type");
|
||||
if (string.IsNullOrEmpty(contentType))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
return MimeReader.GetContentType(contentType);
|
||||
}
|
||||
}
|
||||
|
||||
public MailMessageEx()
|
||||
{
|
||||
_children = new List<MailMessageEx>();
|
||||
}
|
||||
|
||||
private string GetHeader(string header)
|
||||
{
|
||||
return GetHeader(header, stripBrackts: false);
|
||||
}
|
||||
|
||||
private string GetHeader(string header, bool stripBrackts)
|
||||
{
|
||||
if (stripBrackts)
|
||||
{
|
||||
return MimeEntity.TrimBrackets(base.Headers[header]);
|
||||
}
|
||||
return base.Headers[header];
|
||||
}
|
||||
|
||||
public static MailMessageEx CreateMailMessageFromEntity(MimeEntity entity)
|
||||
{
|
||||
MailMessageEx message = new MailMessageEx();
|
||||
string[] allKeys = entity.Headers.AllKeys;
|
||||
foreach (string key in allKeys)
|
||||
{
|
||||
string value = entity.Headers[key];
|
||||
if (value.Equals(string.Empty))
|
||||
{
|
||||
value = " ";
|
||||
}
|
||||
message.Headers.Add(key.ToLowerInvariant(), value);
|
||||
switch (key.ToLowerInvariant())
|
||||
{
|
||||
case "bcc":
|
||||
PopulateAddressList(value, message.Bcc);
|
||||
break;
|
||||
case "cc":
|
||||
PopulateAddressList(value, message.CC);
|
||||
break;
|
||||
case "from":
|
||||
message.From = CreateMailAddress(value);
|
||||
break;
|
||||
case "reply-to":
|
||||
message.ReplyTo = CreateMailAddress(value);
|
||||
break;
|
||||
case "subject":
|
||||
message.Subject = value;
|
||||
break;
|
||||
case "to":
|
||||
PopulateAddressList(value, message.To);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return message;
|
||||
}
|
||||
|
||||
public static MailAddress CreateMailAddress(string address)
|
||||
{
|
||||
try
|
||||
{
|
||||
return new MailAddress(address.Trim('\t'));
|
||||
}
|
||||
catch (FormatException e)
|
||||
{
|
||||
throw new ApplicationException("Unable to create mail address from provided string: " + address, e);
|
||||
}
|
||||
}
|
||||
|
||||
public static void PopulateAddressList(string addressList, MailAddressCollection recipients)
|
||||
{
|
||||
foreach (MailAddress address in GetMailAddresses(addressList))
|
||||
{
|
||||
recipients.Add(address);
|
||||
}
|
||||
}
|
||||
|
||||
public static IEnumerable<MailAddress> GetMailAddresses(string addressList)
|
||||
{
|
||||
Regex email = new Regex("(['\"]{1,}.+['\"]{1,}\\s+)?<?[\\w\\.\\-]+@[^\\.][\\w\\.\\-]+\\.[a-z]{2,}>?");
|
||||
foreach (Match match in email.Matches(addressList))
|
||||
{
|
||||
yield return CreateMailAddress(match.Value);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
using Microsoft.Win32;
|
||||
|
||||
namespace Roblox.Common;
|
||||
|
||||
public class MimeTypes
|
||||
{
|
||||
public static string GetExtensionFromMime(string mimeType)
|
||||
{
|
||||
try
|
||||
{
|
||||
RegistryKey regKey = Registry.ClassesRoot.OpenSubKey("Mime\\Database\\Content Type\\" + mimeType, writable: false);
|
||||
if (regKey == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
string ext = regKey.GetValue("Extension") as string;
|
||||
if (string.IsNullOrEmpty(ext))
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
return ext;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
public static string GetMimeFromExtension(string ext)
|
||||
{
|
||||
RegistryKey regKey = Registry.ClassesRoot.OpenSubKey(ext, writable: false);
|
||||
if (regKey == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
return regKey.GetValue("Content Type") as string;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
using System;
|
||||
using System.Threading;
|
||||
|
||||
namespace Roblox.Common;
|
||||
|
||||
public class NonReentrantTimer : MarshalByRefObject, IDisposable
|
||||
{
|
||||
private Timer _timer;
|
||||
|
||||
private TimerCallback _callback;
|
||||
|
||||
private TimerCallback _skipExecutionCallback;
|
||||
|
||||
private int _count;
|
||||
|
||||
public TimerCallback SkipExecutionCallback
|
||||
{
|
||||
get
|
||||
{
|
||||
return _skipExecutionCallback;
|
||||
}
|
||||
set
|
||||
{
|
||||
_skipExecutionCallback = value;
|
||||
}
|
||||
}
|
||||
|
||||
private NonReentrantTimer(TimerCallback callback)
|
||||
{
|
||||
_callback = callback;
|
||||
_count = 0;
|
||||
_skipExecutionCallback = null;
|
||||
}
|
||||
|
||||
public NonReentrantTimer(TimerCallback callback, object state, int dueTime, int period)
|
||||
: this(callback)
|
||||
{
|
||||
_timer = new Timer(InternalCallback, state, dueTime, period);
|
||||
}
|
||||
|
||||
public NonReentrantTimer(TimerCallback callback, object state, long dueTime, long period)
|
||||
: this(callback)
|
||||
{
|
||||
_timer = new Timer(InternalCallback, state, dueTime, period);
|
||||
}
|
||||
|
||||
public NonReentrantTimer(TimerCallback callback, object state, TimeSpan dueTime, TimeSpan period)
|
||||
: this(callback)
|
||||
{
|
||||
_timer = new Timer(InternalCallback, state, dueTime, period);
|
||||
}
|
||||
|
||||
public NonReentrantTimer(TimerCallback callback, object state, uint dueTime, uint period)
|
||||
: this(callback)
|
||||
{
|
||||
_timer = new Timer(InternalCallback, state, dueTime, period);
|
||||
}
|
||||
|
||||
public bool Change(int dueTime, int period)
|
||||
{
|
||||
return _timer.Change(dueTime, period);
|
||||
}
|
||||
|
||||
public bool Change(long dueTime, long period)
|
||||
{
|
||||
return _timer.Change(dueTime, period);
|
||||
}
|
||||
|
||||
public bool Change(TimeSpan dueTime, TimeSpan period)
|
||||
{
|
||||
return _timer.Change(dueTime, period);
|
||||
}
|
||||
|
||||
public bool Change(uint dueTime, uint period)
|
||||
{
|
||||
return _timer.Change(dueTime, period);
|
||||
}
|
||||
|
||||
private void InternalCallback(object state)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (Interlocked.Increment(ref _count) == 1)
|
||||
{
|
||||
_callback(state);
|
||||
}
|
||||
else
|
||||
{
|
||||
_skipExecutionCallback?.Invoke(state);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
Interlocked.Decrement(ref _count);
|
||||
}
|
||||
}
|
||||
|
||||
public bool Dispose(WaitHandle notifyObject)
|
||||
{
|
||||
if (_timer != null)
|
||||
{
|
||||
return _timer.Dispose(notifyObject);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_timer?.Dispose();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace Roblox.Common;
|
||||
|
||||
public class ObjectCounter<T>
|
||||
{
|
||||
private class Count
|
||||
{
|
||||
private static readonly string perfCategory = "Roblox.Common.ObjectCounter";
|
||||
|
||||
private readonly PerformanceCounter perfItemCount;
|
||||
|
||||
private readonly PerformanceCounter perfCollectRate;
|
||||
|
||||
public Count()
|
||||
{
|
||||
if (!PerformanceCounterCategory.Exists(perfCategory))
|
||||
{
|
||||
CounterCreationDataCollection counterCreationDataCollection = new CounterCreationDataCollection
|
||||
{
|
||||
new CounterCreationData("Count", string.Empty, PerformanceCounterType.NumberOfItems32),
|
||||
new CounterCreationData("Collect Rate", string.Empty, PerformanceCounterType.RateOfCountsPerSecond32)
|
||||
};
|
||||
PerformanceCounterCategory.Create(perfCategory, string.Empty, PerformanceCounterCategoryType.MultiInstance, counterCreationDataCollection);
|
||||
}
|
||||
string typeName = typeof(T).Name;
|
||||
Console.WriteLine($"ObjectCounter name: {typeName}");
|
||||
perfItemCount = new PerformanceCounter(perfCategory, "Count", readOnly: false);
|
||||
perfItemCount.RawValue = 0L;
|
||||
perfCollectRate = new PerformanceCounter(perfCategory, "Collect Rate", readOnly: false);
|
||||
}
|
||||
|
||||
internal void Increment()
|
||||
{
|
||||
perfItemCount.Increment();
|
||||
}
|
||||
|
||||
internal void Decrement()
|
||||
{
|
||||
perfItemCount.Decrement();
|
||||
perfCollectRate.Increment();
|
||||
}
|
||||
}
|
||||
|
||||
private static readonly Count count = new Count();
|
||||
|
||||
public ObjectCounter()
|
||||
{
|
||||
count.Increment();
|
||||
}
|
||||
|
||||
~ObjectCounter()
|
||||
{
|
||||
count.Decrement();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,119 @@
|
||||
using System;
|
||||
using System.Threading;
|
||||
using Microsoft.Ccr.Core;
|
||||
|
||||
namespace Roblox.Common;
|
||||
|
||||
public class RefreshAhead<T> : IDisposable
|
||||
{
|
||||
private DateTime _LastRefresh = DateTime.MinValue;
|
||||
|
||||
private readonly Timer _RefreshTimer;
|
||||
|
||||
private T _Value;
|
||||
|
||||
public TimeSpan IntervalSinceRefresh => DateTime.Now.Subtract(_LastRefresh);
|
||||
|
||||
public T Value => _Value;
|
||||
|
||||
private RefreshAhead(T initialValue, TimeSpan refreshInterval, Func<T> refreshDelegate)
|
||||
{
|
||||
RefreshAhead<T> refreshAhead = this;
|
||||
int refreshIntervalInMilliseconds = (int)refreshInterval.TotalMilliseconds;
|
||||
_Value = initialValue;
|
||||
_LastRefresh = DateTime.Now;
|
||||
_RefreshTimer = new Timer(delegate
|
||||
{
|
||||
refreshAhead.Refresh(refreshDelegate);
|
||||
}, null, refreshIntervalInMilliseconds, refreshIntervalInMilliseconds);
|
||||
}
|
||||
|
||||
private RefreshAhead(T initialValue, TimeSpan refreshInterval, Action<PortSet<T, Exception>> refreshDelegate)
|
||||
{
|
||||
RefreshAhead<T> refreshAhead = this;
|
||||
int refreshIntervalInMilliseconds = (int)refreshInterval.TotalMilliseconds;
|
||||
_Value = initialValue;
|
||||
_LastRefresh = DateTime.Now;
|
||||
_RefreshTimer = new Timer(delegate
|
||||
{
|
||||
refreshAhead.Refresh(refreshDelegate);
|
||||
}, null, refreshIntervalInMilliseconds, refreshIntervalInMilliseconds);
|
||||
}
|
||||
|
||||
public RefreshAhead(TimeSpan refreshInterval, Func<T> refreshDelegate)
|
||||
{
|
||||
RefreshAhead<T> refreshAhead = this;
|
||||
int refreshIntervalInMilliseconds = (int)refreshInterval.TotalMilliseconds;
|
||||
_RefreshTimer = new Timer(delegate
|
||||
{
|
||||
refreshAhead.Refresh(refreshDelegate);
|
||||
}, null, refreshIntervalInMilliseconds, refreshIntervalInMilliseconds);
|
||||
}
|
||||
|
||||
private void Refresh(Func<T> refreshDelegate)
|
||||
{
|
||||
try
|
||||
{
|
||||
_Value = refreshDelegate();
|
||||
_LastRefresh = DateTime.Now;
|
||||
}
|
||||
catch (ThreadAbortException)
|
||||
{
|
||||
throw;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
ExceptionHandler.LogException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
private void Refresh(Action<PortSet<T, Exception>> refreshDelegate)
|
||||
{
|
||||
try
|
||||
{
|
||||
PortSet<T, Exception> valueResult = new PortSet<T, Exception>();
|
||||
refreshDelegate(valueResult);
|
||||
CcrService.Singleton.Choice(valueResult, delegate(T value)
|
||||
{
|
||||
_Value = value;
|
||||
_LastRefresh = DateTime.Now;
|
||||
});
|
||||
}
|
||||
catch (ThreadAbortException)
|
||||
{
|
||||
throw;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
ExceptionHandler.LogException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
public static RefreshAhead<T> ConstructAndPopulate(TimeSpan refreshInterval, Func<T> refreshDelegate)
|
||||
{
|
||||
T value = refreshDelegate();
|
||||
return new RefreshAhead<T>(value, refreshInterval, refreshDelegate);
|
||||
}
|
||||
|
||||
public static void ConstructAndPopulate(TimeSpan refreshInterval, Action<PortSet<T, Exception>> refreshDelegate, PortSet<RefreshAhead<T>, Exception> result)
|
||||
{
|
||||
PortSet<T, Exception> valueResult = new PortSet<T, Exception>();
|
||||
refreshDelegate(valueResult);
|
||||
CcrService.Singleton.Choice(valueResult, delegate(T value)
|
||||
{
|
||||
RefreshAhead<T> item = new RefreshAhead<T>(value, refreshInterval, refreshDelegate);
|
||||
result.Post(item);
|
||||
}, delegate(Exception ex)
|
||||
{
|
||||
result.Post(ex);
|
||||
});
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (_RefreshTimer != null)
|
||||
{
|
||||
_RefreshTimer.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
using System;
|
||||
|
||||
namespace Roblox.Common;
|
||||
|
||||
public class Request<T>
|
||||
{
|
||||
public static Result<CompletionSignal> GetResponse(Action<IAsyncResult> responseHandler, IAsyncResult asyncResult)
|
||||
{
|
||||
try
|
||||
{
|
||||
responseHandler(asyncResult);
|
||||
return new Result<CompletionSignal>(CompletionSignal.Instance);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return new Result<CompletionSignal>(ex);
|
||||
}
|
||||
}
|
||||
|
||||
public static Result<T> GetResponse(Func<IAsyncResult, T> responseHandler, IAsyncResult asyncResult)
|
||||
{
|
||||
try
|
||||
{
|
||||
return new Result<T>(responseHandler(asyncResult));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return new Result<T>(ex);
|
||||
}
|
||||
}
|
||||
|
||||
public static void HandleResponse(Func<AsyncCallback, object, IAsyncResult> beginHandler, Action<IAsyncResult> endHandler, Action<Result<CompletionSignal>> resultHandler)
|
||||
{
|
||||
beginHandler(delegate(IAsyncResult ar)
|
||||
{
|
||||
resultHandler(GetResponse(endHandler, ar));
|
||||
}, null);
|
||||
}
|
||||
|
||||
public static void HandleResponse<Arg0>(Func<Arg0, AsyncCallback, object, IAsyncResult> beginHandler, Arg0 arg0, Action<IAsyncResult> endHandler, Action<Result<CompletionSignal>> resultHandler)
|
||||
{
|
||||
HandleResponse((AsyncCallback c, object s) => beginHandler(arg0, c, s), endHandler, resultHandler);
|
||||
}
|
||||
|
||||
public static void HandleResponse<Arg0, Arg1>(Func<Arg0, Arg1, AsyncCallback, object, IAsyncResult> beginHandler, Arg0 arg0, Arg1 arg1, Action<IAsyncResult> endHandler, Action<Result<CompletionSignal>> resultHandler)
|
||||
{
|
||||
HandleResponse((AsyncCallback c, object s) => beginHandler(arg0, arg1, c, s), endHandler, resultHandler);
|
||||
}
|
||||
|
||||
public static void HandleResponse<Arg0, Arg1, Arg2>(Func<Arg0, Arg1, Arg2, AsyncCallback, object, IAsyncResult> beginHandler, Arg0 arg0, Arg1 arg1, Arg2 arg2, Action<IAsyncResult> endHandler, Action<Result<CompletionSignal>> resultHandler)
|
||||
{
|
||||
HandleResponse((AsyncCallback c, object s) => beginHandler(arg0, arg1, arg2, c, s), endHandler, resultHandler);
|
||||
}
|
||||
|
||||
public static void HandleResponse(Func<AsyncCallback, object, IAsyncResult> beginHandler, Func<IAsyncResult, T> endHandler, Action<Result<T>> resultHandler)
|
||||
{
|
||||
beginHandler(delegate(IAsyncResult ar)
|
||||
{
|
||||
resultHandler(GetResponse(endHandler, ar));
|
||||
}, null);
|
||||
}
|
||||
|
||||
public static void HandleResponse<Arg0>(Func<Arg0, AsyncCallback, object, IAsyncResult> beginHandler, Arg0 arg0, Func<IAsyncResult, T> endHandler, Action<Result<T>> resultHandler)
|
||||
{
|
||||
HandleResponse((AsyncCallback c, object s) => beginHandler(arg0, c, s), endHandler, resultHandler);
|
||||
}
|
||||
|
||||
public static void HandleResponse<Arg0, Arg1>(Func<Arg0, Arg1, AsyncCallback, object, IAsyncResult> beginHandler, Arg0 arg0, Arg1 arg1, Func<IAsyncResult, T> endHandler, Action<Result<T>> resultHandler)
|
||||
{
|
||||
HandleResponse((AsyncCallback c, object s) => beginHandler(arg0, arg1, c, s), endHandler, resultHandler);
|
||||
}
|
||||
|
||||
public static void HandleResponse<Arg0, Arg1, Arg2>(Func<Arg0, Arg1, Arg2, AsyncCallback, object, IAsyncResult> beginHandler, Arg0 arg0, Arg1 arg1, Arg2 arg2, Func<IAsyncResult, T> endHandler, Action<Result<T>> resultHandler)
|
||||
{
|
||||
HandleResponse((AsyncCallback c, object s) => beginHandler(arg0, arg1, arg2, c, s), endHandler, resultHandler);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
using System;
|
||||
|
||||
namespace Roblox.Common;
|
||||
|
||||
public class Result<T>
|
||||
{
|
||||
private Exception _Exception;
|
||||
|
||||
private T _Value;
|
||||
|
||||
public Exception Exception => _Exception;
|
||||
|
||||
public T Value
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_Exception != null)
|
||||
{
|
||||
throw _Exception;
|
||||
}
|
||||
return _Value;
|
||||
}
|
||||
}
|
||||
|
||||
public Result(T value)
|
||||
{
|
||||
_Value = value;
|
||||
}
|
||||
|
||||
public Result(Exception exception)
|
||||
{
|
||||
_Exception = exception;
|
||||
}
|
||||
|
||||
public void Test(Action<T> successHandler, Action<Exception> failureHandler)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (_Exception != null)
|
||||
{
|
||||
throw _Exception;
|
||||
}
|
||||
successHandler?.Invoke(_Value);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
failureHandler?.Invoke(ex);
|
||||
}
|
||||
}
|
||||
|
||||
public void Test(Action<T> successHandler, Action<Exception> failureHandler, Action cleanupHandler)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (_Exception != null)
|
||||
{
|
||||
throw _Exception;
|
||||
}
|
||||
successHandler?.Invoke(_Value);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
failureHandler?.Invoke(ex);
|
||||
}
|
||||
finally
|
||||
{
|
||||
cleanupHandler?.Invoke();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
|
||||
namespace Roblox.Common;
|
||||
|
||||
public class RoundRobin<T>
|
||||
{
|
||||
private readonly T[] _Candidates;
|
||||
|
||||
private int _CurrentIndex;
|
||||
|
||||
public RoundRobin(IEnumerable<T> elements)
|
||||
{
|
||||
_Candidates = elements.ToArray();
|
||||
}
|
||||
|
||||
public T Next()
|
||||
{
|
||||
int index = Interlocked.Increment(ref _CurrentIndex);
|
||||
if (index >= _Candidates.Length)
|
||||
{
|
||||
_CurrentIndex = 0;
|
||||
return _Candidates[0];
|
||||
}
|
||||
return _Candidates[index];
|
||||
}
|
||||
}
|
||||
+97
@@ -0,0 +1,97 @@
|
||||
using System;
|
||||
using System.Threading;
|
||||
|
||||
namespace Roblox.Common;
|
||||
|
||||
public class SynchronousCompletionAsyncResult : IAsyncResult
|
||||
{
|
||||
private Exception _Error;
|
||||
|
||||
private readonly AsyncCallback _Callback;
|
||||
|
||||
private bool _IsCompleted = true;
|
||||
|
||||
private readonly object _State;
|
||||
|
||||
public WaitHandle AsyncWaitHandle
|
||||
{
|
||||
get
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
||||
public object AsyncState => _State;
|
||||
|
||||
public bool CompletedSynchronously => true;
|
||||
|
||||
public bool IsCompleted => _IsCompleted;
|
||||
|
||||
public Exception Error => _Error;
|
||||
|
||||
public SynchronousCompletionAsyncResult(AsyncCallback callback, object state)
|
||||
{
|
||||
_Callback = callback;
|
||||
_State = state;
|
||||
}
|
||||
|
||||
public SynchronousCompletionAsyncResult(AsyncCallback callback, object state, bool setComplete)
|
||||
{
|
||||
_Callback = callback;
|
||||
_State = state;
|
||||
if (setComplete)
|
||||
{
|
||||
SetCompleted();
|
||||
}
|
||||
}
|
||||
|
||||
public SynchronousCompletionAsyncResult(AsyncCallback callback, object state, Exception error)
|
||||
{
|
||||
_Callback = callback;
|
||||
_State = state;
|
||||
SetCompleted(error);
|
||||
}
|
||||
|
||||
public void CheckResult()
|
||||
{
|
||||
if (_Error != null)
|
||||
{
|
||||
throw _Error;
|
||||
}
|
||||
}
|
||||
|
||||
public void SetCompleted()
|
||||
{
|
||||
_IsCompleted = true;
|
||||
_Callback?.Invoke(this);
|
||||
}
|
||||
|
||||
public void SetCompleted(Exception error)
|
||||
{
|
||||
_Error = error;
|
||||
SetCompleted();
|
||||
}
|
||||
}
|
||||
public class SynchronousCompletionAsyncResult<T> : SynchronousCompletionAsyncResult, IResult<T>
|
||||
{
|
||||
private T result;
|
||||
|
||||
[Obsolete("This property can throw, which is bad design. Use GetResult() and SetCompleted() instead")]
|
||||
public T Token => GetResult();
|
||||
|
||||
public SynchronousCompletionAsyncResult(T token, AsyncCallback callback, object state)
|
||||
: base(callback, state)
|
||||
{
|
||||
result = token;
|
||||
SetCompleted();
|
||||
}
|
||||
|
||||
public T GetResult()
|
||||
{
|
||||
if (base.Error != null)
|
||||
{
|
||||
throw base.Error;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
using System.Web;
|
||||
|
||||
namespace Roblox.Common;
|
||||
|
||||
public class TextTransforms
|
||||
{
|
||||
public static string transformString(string stringToTransform)
|
||||
{
|
||||
return performCarriageReturnSubstitution(HttpContext.Current.Server.HtmlEncode(stringToTransform));
|
||||
}
|
||||
|
||||
public static string performCarriageReturnSubstitution(string text)
|
||||
{
|
||||
return text.Replace("\n", "<br />");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Roblox.Common;
|
||||
|
||||
public class VisitsFloodCheckBuffer : IDisposable
|
||||
{
|
||||
private readonly ObjectRegistry<int, IDictionary<long, DateTime>> userLogs;
|
||||
|
||||
private static readonly TimeSpan floodCheckInterval = TimeSpan.FromHours(1.0);
|
||||
|
||||
public static readonly VisitsFloodCheckBuffer Singleton = new VisitsFloodCheckBuffer();
|
||||
|
||||
private VisitsFloodCheckBuffer()
|
||||
{
|
||||
userLogs = new ObjectRegistry<int, IDictionary<long, DateTime>>(new ObjectRegistry<int, IDictionary<long, DateTime>>.Configuration("VisitsFloodCheckBuffer")
|
||||
{
|
||||
Lease = floodCheckInterval,
|
||||
Getter = (int k) => new Dictionary<long, DateTime>(),
|
||||
PurgeFrequency = TimeSpan.FromMinutes(5.0)
|
||||
});
|
||||
}
|
||||
|
||||
public bool PassesFloodCheck(int userId, long placeId)
|
||||
{
|
||||
if (!userLogs.TryGetValue(userId, out var log))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
IDictionary<long, DateTime> sync = log;
|
||||
lock (sync)
|
||||
{
|
||||
if (log == null)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
DateTime minValue;
|
||||
return !log.TryGetValue(placeId, out minValue) || minValue == DateTime.MinValue || DateTime.Now.Subtract(minValue) > floodCheckInterval;
|
||||
}
|
||||
}
|
||||
|
||||
public bool RegisterVisit(int userId, long placeId)
|
||||
{
|
||||
DateTime now = DateTime.Now;
|
||||
IDictionary<long, DateTime> dict = userLogs.Get(userId);
|
||||
IDictionary<long, DateTime> sync = dict;
|
||||
KeyValuePair<long, DateTime> item = new KeyValuePair<long, DateTime>(placeId, now);
|
||||
lock (sync)
|
||||
{
|
||||
if (dict.TryGetValue(placeId, out var minValue))
|
||||
{
|
||||
if (!(now.Subtract(minValue) > floodCheckInterval))
|
||||
{
|
||||
dict.Remove(placeId);
|
||||
dict.Add(new KeyValuePair<long, DateTime>(placeId, DateTime.MinValue));
|
||||
return false;
|
||||
}
|
||||
dict.Remove(placeId);
|
||||
}
|
||||
dict.Add(item);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
userLogs.Dispose();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Roblox.Common;
|
||||
|
||||
public static class XMLUtil
|
||||
{
|
||||
public static string GenerateXMLTable(ICollection<KeyValuePair<object, object>> entries)
|
||||
{
|
||||
string text = "<Value><Table>";
|
||||
foreach (KeyValuePair<object, object> entry in entries)
|
||||
{
|
||||
text += $"<Entry><Key>{entry.Key.ToString()}</Key><Value>{entry.Value.ToString()}</Value></Entry>";
|
||||
}
|
||||
return text + "</Table></Value>";
|
||||
}
|
||||
|
||||
public static string GenerateXMLBool(bool value)
|
||||
{
|
||||
return $"<List><Value>{value.ToString()}</Value></List>";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
using System.Text;
|
||||
using System.Xml;
|
||||
|
||||
namespace Roblox.Common;
|
||||
|
||||
public class Xml : XmlBase
|
||||
{
|
||||
private static XmlWriterSettings _XmlWriterSettings = new XmlWriterSettings
|
||||
{
|
||||
Encoding = Encoding.UTF8
|
||||
};
|
||||
|
||||
public static Xml Singleton = new Xml();
|
||||
|
||||
protected override XmlWriterSettings XmlWriterSettings => _XmlWriterSettings;
|
||||
|
||||
private Xml()
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Xml;
|
||||
|
||||
namespace Roblox.Common;
|
||||
|
||||
public abstract class XmlBase
|
||||
{
|
||||
protected abstract XmlWriterSettings XmlWriterSettings { get; }
|
||||
|
||||
private bool IsLegalCharacter(int character)
|
||||
{
|
||||
if (character != 9 && character != 10 && character != 13 && (character < 32 || character > 55295) && (character < 57344 || character > 65533))
|
||||
{
|
||||
if (character >= 65536)
|
||||
{
|
||||
return character <= 1114111;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public string Sanitize(string xmlString)
|
||||
{
|
||||
if (string.IsNullOrEmpty(xmlString))
|
||||
{
|
||||
return xmlString;
|
||||
}
|
||||
StringBuilder builder = new StringBuilder(xmlString.Length);
|
||||
foreach (char c in xmlString)
|
||||
{
|
||||
if (IsLegalCharacter(c))
|
||||
{
|
||||
builder.Append(c);
|
||||
}
|
||||
}
|
||||
return builder.ToString();
|
||||
}
|
||||
|
||||
public virtual byte[] Write(Action<XmlWriter> write)
|
||||
{
|
||||
using MemoryStream ms = new MemoryStream();
|
||||
using XmlWriter writer = XmlWriter.Create(ms, XmlWriterSettings);
|
||||
write(writer);
|
||||
writer.Flush();
|
||||
ms.Seek(0L, SeekOrigin.Begin);
|
||||
return ms.ToArray();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user