Expanded upon EventScheduler
- Events are now sorted - Thread waits until either a new event is scheduled or an existing event is ready before continuing.
This commit is contained in:
parent
01b3742763
commit
2db4df9b5d
@ -5,6 +5,7 @@ namespace TrueCraft.API.Server
|
||||
public interface IEventScheduler
|
||||
{
|
||||
void ScheduleEvent(DateTime when, Action<IMultiplayerServer> action);
|
||||
void Update();
|
||||
void Start();
|
||||
void Stop();
|
||||
}
|
||||
}
|
@ -1,17 +1,23 @@
|
||||
using System;
|
||||
using TrueCraft.API.Server;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using TrueCraft.API.Logging;
|
||||
|
||||
namespace TrueCraft
|
||||
{
|
||||
public class EventScheduler : IEventScheduler
|
||||
{
|
||||
// TODO: This could be done more efficiently if the list were kept sorted
|
||||
|
||||
private IList<ScheduledEvent> Events { get; set; }
|
||||
private List<ScheduledEvent> Events { get; set; }
|
||||
private object EventLock = new object();
|
||||
private IMultiplayerServer Server { get; set; }
|
||||
|
||||
private SemaphoreSlim Sem = new SemaphoreSlim(0, 1);
|
||||
|
||||
private CancellationTokenSource Cancel;
|
||||
|
||||
public EventScheduler(IMultiplayerServer server)
|
||||
{
|
||||
Events = new List<ScheduledEvent>();
|
||||
@ -24,30 +30,99 @@ namespace TrueCraft
|
||||
{
|
||||
Events.Add(new ScheduledEvent { When = when, Action = action });
|
||||
}
|
||||
|
||||
if (Sem.CurrentCount == 0)
|
||||
Sem.Release();
|
||||
}
|
||||
|
||||
public void Update()
|
||||
public void Start()
|
||||
{
|
||||
lock (EventLock)
|
||||
Cancel = new CancellationTokenSource();
|
||||
|
||||
Thread scheduleThread = new Thread(Update);
|
||||
scheduleThread.IsBackground = true;
|
||||
|
||||
scheduleThread.Start();
|
||||
}
|
||||
|
||||
public void Stop()
|
||||
{
|
||||
Cancel.Cancel();
|
||||
}
|
||||
|
||||
private void Update()
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
var start = DateTime.Now;
|
||||
for (int i = 0; i < Events.Count; i++)
|
||||
if (Cancel.IsCancellationRequested)
|
||||
break;
|
||||
|
||||
try
|
||||
{
|
||||
var e = Events[i];
|
||||
if (e.When <= start)
|
||||
ScheduledEvent? nextEvent = null;
|
||||
lock (EventLock)
|
||||
{
|
||||
e.Action(Server);
|
||||
Events.RemoveAt(i);
|
||||
i--;
|
||||
var evts = Events.ToList();
|
||||
evts.Sort();
|
||||
|
||||
DateTime now = DateTime.Now;
|
||||
for (int i = 0; i < evts.Count; i++)
|
||||
{
|
||||
ScheduledEvent evt = evts[i];
|
||||
|
||||
if (evt.When < now)
|
||||
break;
|
||||
|
||||
evts.RemoveAt(i);
|
||||
i--;
|
||||
|
||||
evt.Action(Server);
|
||||
}
|
||||
|
||||
if (evts.Count > 0)
|
||||
nextEvent = evts.First();
|
||||
|
||||
Events = evts;
|
||||
}
|
||||
|
||||
var tasks = new List<Task> { Sem.WaitAsync(Cancel.Token) };
|
||||
if (nextEvent != null)
|
||||
{
|
||||
TimeSpan ts = nextEvent.Value.When - DateTime.Now;
|
||||
|
||||
if (ts < TimeSpan.Zero)
|
||||
continue;
|
||||
|
||||
tasks.Add(Task.Delay(ts, Cancel.Token));
|
||||
}
|
||||
|
||||
Task.WhenAny(tasks).Wait();
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
return;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Server.Log(LogCategory.Error, "Scheduler Error", ex.ToString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private struct ScheduledEvent
|
||||
private struct ScheduledEvent : IComparable<ScheduledEvent>
|
||||
{
|
||||
public DateTime When;
|
||||
public Action<IMultiplayerServer> Action;
|
||||
|
||||
public int CompareTo(ScheduledEvent other)
|
||||
{
|
||||
if (When > other.When)
|
||||
return 1;
|
||||
if (When == other.When)
|
||||
return 0;
|
||||
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -113,6 +113,8 @@ namespace TrueCraft
|
||||
Listener.Start();
|
||||
EndPoint = (IPEndPoint)Listener.LocalEndpoint;
|
||||
|
||||
Scheduler.Start();
|
||||
|
||||
SocketAsyncEventArgs args = new SocketAsyncEventArgs();
|
||||
args.Completed += AcceptClient;
|
||||
|
||||
@ -135,6 +137,8 @@ namespace TrueCraft
|
||||
w.Save();
|
||||
foreach (var c in Clients)
|
||||
DisconnectClient(c);
|
||||
|
||||
Scheduler.Stop();
|
||||
}
|
||||
|
||||
public void AddWorld(IWorld world)
|
||||
@ -294,7 +298,6 @@ namespace TrueCraft
|
||||
{
|
||||
if (ShuttingDown)
|
||||
return;
|
||||
Scheduler.Update();
|
||||
foreach (var manager in EntityManagers)
|
||||
{
|
||||
manager.Update();
|
||||
|
@ -9,6 +9,8 @@
|
||||
<AssemblyName>TrueCraft</AssemblyName>
|
||||
<ProductVersion>8.0.30703</ProductVersion>
|
||||
<SchemaVersion>2.0</SchemaVersion>
|
||||
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
|
||||
<TargetFrameworkProfile />
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<Optimize>false</Optimize>
|
||||
@ -16,11 +18,13 @@
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<ConsolePause>false</ConsolePause>
|
||||
<Prefer32Bit>false</Prefer32Bit>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>bin\Release</OutputPath>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<Prefer32Bit>false</Prefer32Bit>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="System" />
|
||||
@ -75,6 +79,7 @@
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="app.config" />
|
||||
<None Include="packages.config" />
|
||||
</ItemGroup>
|
||||
</Project>
|
3
TrueCraft/app.config
Normal file
3
TrueCraft/app.config
Normal file
@ -0,0 +1,3 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<configuration>
|
||||
<startup><supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5"/></startup></configuration>
|
Loading…
x
Reference in New Issue
Block a user