Files
ShrinkEventBus/Runtime/ShrinkEventContracts.cs
cneicy 1c921f5aec
Publish UPM package / publish (push) Failing after 1s
chore: initialize standalone UPM package
2026-08-26 02:50:18 +08:00

214 lines
8.0 KiB
C#

#nullable enable
using System;
using System.Runtime.CompilerServices;
namespace ShrinkEventBus
{
/// <summary>Marker shared by managed, Unity and ECS event payloads.</summary>
public interface IShrinkEvent
{
}
public interface IShrinkCancelableEvent : IShrinkEvent
{
bool IsCanceled { get; }
void SetCanceled(bool value);
}
public interface IShrinkResultEvent<TResult> : IShrinkEvent
{
TResult Result { get; }
void SetResult(TResult result);
}
public enum ShrinkEventPriority
{
Highest = 0,
High = 1,
Normal = 2,
Low = 3,
Lowest = 4,
Monitor = 5
}
public enum ShrinkDispatchMode
{
Ordered = 0,
Parallel = 1
}
public enum ShrinkBusSchedulerKind
{
Inline = 0,
MainThread = 1,
DedicatedThread = 2,
TaskPool = 3
}
public enum ShrinkQueueOverflowPolicy
{
Reject = 0,
DropNewest = 1,
DropOldest = 2,
Wait = 3
}
public enum ShrinkPostFailure
{
None = 0,
BusStopped = 1,
QueueFull = 2,
Canceled = 3,
HandlerException = 4,
InvalidEvent = 5
}
public readonly struct ShrinkPostResult
{
private const int AcceptedMask = 1 << 0;
private const int HandledMask = 1 << 1;
private const int CanceledMask = 1 << 2;
private const int FailureShift = 8;
private readonly int _value;
public ShrinkPostResult(bool accepted, bool handled, bool canceled,
ShrinkPostFailure failure = ShrinkPostFailure.None)
{
_value = (accepted ? AcceptedMask : 0) |
(handled ? HandledMask : 0) |
(canceled ? CanceledMask : 0) |
((int)failure << FailureShift);
}
public bool Accepted => (_value & AcceptedMask) != 0;
public bool Handled => (_value & HandledMask) != 0;
public bool Canceled => (_value & CanceledMask) != 0;
public ShrinkPostFailure Failure => (ShrinkPostFailure)((uint)_value >> FailureShift);
public bool Succeeded => Accepted && Failure == ShrinkPostFailure.None;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ShrinkPostResult Rejected(ShrinkPostFailure failure) =>
new(false, false, false, failure);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ShrinkPostResult Completed(bool handled, bool canceled = false) =>
new(true, handled, canceled);
}
public readonly struct ShrinkBusKey : IEquatable<ShrinkBusKey>
{
public ShrinkBusKey(string scope, string name)
{
if (string.IsNullOrWhiteSpace(scope))
throw new ArgumentException("Bus scope must not be empty.", nameof(scope));
if (string.IsNullOrWhiteSpace(name))
throw new ArgumentException("Bus name must not be empty.", nameof(name));
Scope = scope.Trim().ToLowerInvariant();
Name = name.Trim();
}
public string Scope { get; }
public string Name { get; }
public string Id => Scope == "game" && Name == "default" ? "game" : $"{Scope}:{Name}";
public static ShrinkBusKey Game => new("game", "default");
public static ShrinkBusKey Server => new("server", "default");
public static ShrinkBusKey Scene(string sceneId) => new("scene", sceneId);
public static ShrinkBusKey Mod(string modId) => new("mod", modId);
public static ShrinkBusKey World(string worldId) => new("world", worldId);
public static ShrinkBusKey Parse(string? value)
{
if (string.IsNullOrWhiteSpace(value) || string.Equals(value.Trim(), "game", StringComparison.OrdinalIgnoreCase))
return Game;
var normalized = value.Trim();
var separator = normalized.IndexOf(':');
return separator <= 0 || separator == normalized.Length - 1
? new ShrinkBusKey("custom", normalized)
: new ShrinkBusKey(normalized.Substring(0, separator), normalized.Substring(separator + 1));
}
public bool Equals(ShrinkBusKey other) =>
string.Equals(Scope, other.Scope, StringComparison.OrdinalIgnoreCase) &&
string.Equals(Name, other.Name, StringComparison.Ordinal);
public override bool Equals(object? obj) => obj is ShrinkBusKey other && Equals(other);
public override int GetHashCode() => HashCode.Combine(Scope.ToLowerInvariant(), Name);
public override string ToString() => Id;
public static bool operator ==(ShrinkBusKey left, ShrinkBusKey right) => left.Equals(right);
public static bool operator !=(ShrinkBusKey left, ShrinkBusKey right) => !left.Equals(right);
}
public sealed class ShrinkBusOptions
{
public ShrinkBusSchedulerKind Scheduler { get; set; } = ShrinkBusSchedulerKind.Inline;
public ShrinkDispatchMode DispatchMode { get; set; } = ShrinkDispatchMode.Ordered;
public int QueueCapacity { get; set; } = 4096;
public int MaxConcurrency { get; set; } = Math.Max(1, Environment.ProcessorCount);
public ShrinkQueueOverflowPolicy OverflowPolicy { get; set; } = ShrinkQueueOverflowPolicy.Reject;
public bool DrainOnShutdown { get; set; } = true;
public TimeSpan ShutdownTimeout { get; set; } = TimeSpan.FromSeconds(5);
public static ShrinkBusOptions Inline(ShrinkDispatchMode mode = ShrinkDispatchMode.Ordered) =>
new() { Scheduler = ShrinkBusSchedulerKind.Inline, DispatchMode = mode };
public static ShrinkBusOptions MainThread(int queueCapacity = 4096) =>
new() { Scheduler = ShrinkBusSchedulerKind.MainThread, QueueCapacity = queueCapacity };
public static ShrinkBusOptions DedicatedThread(int queueCapacity = 4096) =>
new() { Scheduler = ShrinkBusSchedulerKind.DedicatedThread, QueueCapacity = queueCapacity };
public static ShrinkBusOptions TaskPool(int maxConcurrency = 0, int queueCapacity = 4096) =>
new()
{
Scheduler = ShrinkBusSchedulerKind.TaskPool,
DispatchMode = ShrinkDispatchMode.Parallel,
QueueCapacity = queueCapacity,
MaxConcurrency = maxConcurrency > 0 ? maxConcurrency : Math.Max(1, Environment.ProcessorCount)
};
internal ShrinkBusOptions CloneValidated()
{
if (QueueCapacity <= 0)
throw new ArgumentOutOfRangeException(nameof(QueueCapacity));
if (MaxConcurrency <= 0)
throw new ArgumentOutOfRangeException(nameof(MaxConcurrency));
if (ShutdownTimeout < TimeSpan.Zero)
throw new ArgumentOutOfRangeException(nameof(ShutdownTimeout));
if (OverflowPolicy == ShrinkQueueOverflowPolicy.Wait && Scheduler == ShrinkBusSchedulerKind.Inline)
throw new InvalidOperationException("Inline buses do not have a queue and cannot use Wait overflow policy.");
return new ShrinkBusOptions
{
Scheduler = Scheduler,
DispatchMode = DispatchMode,
QueueCapacity = QueueCapacity,
MaxConcurrency = MaxConcurrency,
OverflowPolicy = OverflowPolicy,
DrainOnShutdown = DrainOnShutdown,
ShutdownTimeout = ShutdownTimeout
};
}
}
public readonly struct ShrinkSubscribeDescriptor
{
public ShrinkSubscribeDescriptor(string? bus, ShrinkEventPriority priority,
int numericPriority, bool receiveCanceled)
{
Bus = bus ?? string.Empty;
Priority = priority;
NumericPriority = numericPriority;
ReceiveCanceled = receiveCanceled;
}
public string Bus { get; }
public ShrinkEventPriority Priority { get; }
public int NumericPriority { get; }
public bool ReceiveCanceled { get; }
}
}