LogicLooper 1.6.0
dotnet add package LogicLooper --version 1.6.0
NuGet\Install-Package LogicLooper -Version 1.6.0
<PackageReference Include="LogicLooper" Version="1.6.0" />
paket add LogicLooper --version 1.6.0
#r "nuget: LogicLooper, 1.6.0"
// Install LogicLooper as a Cake Addin #addin nuget:?package=LogicLooper&version=1.6.0 // Install LogicLooper as a Cake Tool #tool nuget:?package=LogicLooper&version=1.6.0
LogicLooper
这个库是用来在 .NET 上使用循环动作编程模型构建服务器应用程序的。这个库专注于构建带服务器逻辑的游戏服务器。
例如,如果您有以下游戏循环,则库将提供一种比使用简单的 Task
更高效地聚合和处理的方式。
while (true)
{
// some stuff to do ...
network.Receive();
world.Update();
players.Update();
network.Send();
// some stuff to do ...
// wait for next frame
await Task.Delay(16);
}
using var looper = new LogicLooper(60);
await looper.RegisterActionAsync((in LogicLooperActionContext ctx) =>
{
// The action will be called by looper every frame.
// some stuff to do ...
network.Receive();
world.Update();
players.Update();
network.Send();
// some stuff to do ...
return true; // wait for next update
});
目录
安装
PS> Install-Package LogicLooper
$ dotnet add package LogicLooper
使用
单循环应用
Looper 绑定一个线程并开始一个主循环。您可以注册多个循环动作用于 Looper。这类似于在游戏引擎的一帧中调用多个 Update
方法。
using Cysharp.Threading;
// Create a looper.
const int targetFps = 60;
using var looper = new LogicLooper(targetFps);
// Register a action to the looper and wait for completion.
await looper.RegisterActionAsync((in LogicLooperActionContext ctx) =>
{
// If you want to stop/complete the loop, return false to stop.
if (...) { return false; }
// some stuff to do ...
return true; // wait for a next update.
});
使用 LooperPool 的多循环应用
例如,如果您的服务器有多个核心,运行多个循环将更有效。 LooperPool
提供了多个 Looper 和使用它们的界面。
using Cysharp.Threading;
// Create a looper pool.
// If your machine has 4-cores, the LooperPool creates 4-Looper instances.
const int targetFps = 60;
var looperCount = Environment.ProcessorCount;
using var looperPool = new LogicLooperPool(targetFps, looperCount, RoundRobinLogicLooperPoolBalancer.Instance);
// Register a action to the looper and wait for completion.
await looperPool.RegisterActionAsync((in LogicLooperActionContext ctx) =>
{
// If you want to stop/complete the loop, return false to stop.
if (...) { return false; }
// some stuff to do ...
return true; // wait for a next update.
});
与 Microsoft.Extensions.Hosting 集成
高级
单元测试 / 帧级执行
如果您想使用 LogicLooper 编写单元测试或手动更新帧,您可以使用 ManualLogicLooper
/ ManualLogicLooperPool
。
var looper = new ManualLogicLooper(60.0); // `ElapsedTimeFromPreviousFrame` will be fixed to `1000 / FrameTargetFrameRate`.
var count = 0;
var t1 = looper.RegisterActionAsync((in LogicLooperActionContext ctx) =>
{
count++;
return count != 3;
});
looper.Tick(); // Update frame
Console.WriteLine(count); // => 1
looper.Tick(); // Update frame
Console.WriteLine(count); // => 2
looper.Tick(); // Update frame (t1 will be completed)
Console.WriteLine(count); // => 3
looper.Tick(); // Update frame (no action)
Console.WriteLine(count); // => 3
协程
LogicLooper 支持类似协程的操作。如果您使用 Unity,您应该熟悉协程模式。
using var looper = new LogicLooper(60);
var coroutine = default(LogicLooperCoroutine);
await looper.RegisterActionAsync((in LogicLooperActionContext ctx) =>
{
if (/* ... */)
{
// Launch a coroutine in the looper that same as the loop action.
coroutine = ctx.RunCoroutine(async coCtx =>
{
// NOTE: `DelayFrame`, `DelayNextFrame`, `Delay` methods are allowed and awaitable in the coroutine.
// If you await a Task or Task-like, the coroutine throws an exception.
await coCtx.DelayFrame(60);
// some stuff to do ...
await coCtx.DelayNextFrame();
// some stuff to do ...
await coCtx.Delay(TimeSpan.FromMilliseconds(16.66666));
});
}
if (coroutine.IsCompleted)
{
// When the coroutine has completed, you can do some stuff ...
}
return true;
});
TargetFrameRateOverride
TargetFrameRateOverride
选项允许覆盖每个动作的帧率。这在需要混合多个帧率的情况下很有用,比如期望主循环以 30fps 运行,但某些动作需要在 5fps 下调用。
您还可以设置每个执行循环的 Looper 的帧率,但由于 LogicLooper 的设计是每个线程 1 个循环,所以在原则上我们希望 Loopers 的数量与核心数相符。通过为每个动作设置帧率,即使在工作量变化的情况下,也可以保持 Loopers 的数量不变。
using var looper = new LogicLooper(60); // 60 fps
await looper.RegisterActionAsync((in LogicLooperActionContext ctx) =>
{
// Something to do ...
return true;
}); // The action will be called at 60fps.
await looper.RegisterActionAsync((in LogicLooperActionContext ctx) =>
{
// Something to do (low priority) ...
return true;
}, LooperActionOptions.Default with { TargetFrameRateOverride = 10 }); // The action will be called at 10fps.
动作执行的粒度会根据主循环的执行频率而变化。这意味着准确性可能低于 Looper 的目标帧率。
实验性
async-aware loop actions
对可等待异步事件的循环动作的实验性支持。
使用 SynchronizationContext,所有异步延续都将在循环线程上执行。请注意,与同步操作不同,异步操作会跨越多个帧执行。
await looper.RegisterActionAsync(static async (ctx, state) =>
{
state.Add("1"); // Frame: 1
await Task.Delay(250);
state.Add("2"); // Frame: 2 or later
return false;
});
[!警告] 如果动作立即完成(
ValueTask.IsCompleted = true
),与非异步版本相比没有性能差异。但如果需要等待,则速度非常慢。这一异步支持作为一个紧急出口,当需要以低频率与外部进行通信时。我们不建议以高频率进行异步处理。
产品 | 版本 兼容的和额外的计算目标框架版本。 |
---|---|
.NET | net5.0 已计算。 net5.0-windows 已计算。 net6.0 兼容。 net6.0-android 已计算。 net6.0-ios 已计算。 net6.0-maccatalyst 已计算。 net6.0-macos 已计算。 net6.0-tvos 已计算。 net6.0-windows 已计算。 net7.0 已计算。 net7.0-android 已计算。 net7.0-ios 已计算。 net7.0-maccatalyst 已计算。 net7.0-macos 已计算。 net7.0-tvos 已计算。 net7.0-windows 已计算。 net8.0 兼容。 net8.0-android 已计算。 net8.0-browser 已计算。 net8.0-ios 已计算。 net8.0-maccatalyst 已计算。 net8.0-macos 已计算。 net8.0-tvos 已计算。 net8.0-windows 已计算。 |
.NET Core | netcoreapp2.0 已计算。 netcoreapp2.1 已计算。 netcoreapp2.2 已计算。 netcoreapp3.0 已计算。 netcoreapp3.1 已计算。 |
.NET Standard | netstandard2.0 兼容。 netstandard2.1 兼容。 |
.NET Framework | net461 已计算。 net462 已计算。 net463 已计算。 net47 已计算。 net471 已计算。 net472 已计算。 net48 已计算。 net481 已计算。 |
MonoAndroid | monoandroid 已计算。 |
MonoMac | monomac 已计算。 |
MonoTouch | monotouch 已计算。 |
Tizen | tizen40 已计算。 tizen60 已计算。 |
Xamarin.iOS | xamarinios 已计算。 |
Xamarin.Mac | xamarinmac 已计算。 |
Xamarin.TVOS | xamarintvos 已计算。 |
Xamarin.WatchOS | xamarinwatchos 已计算。 |
-
.NETStandard 2.0
- System.Threading.Tasks.Extensions (>= 4.5.3)
-
.NETStandard 2.1
- 无依赖。
-
net6.0
- 无依赖。
-
net8.0
- 无依赖。
NuGet 包 (1)
展示依赖于 LogicLooper 的前 1 个 NuGet 包
包 | 下载 |
---|---|
R3Extensions.LogicLooper
为 R3 提供的 LogicLooper 属性和方法。 |
GitHub 仓库
此包不被流行的 GitHub 仓库所使用。