mirror of
https://github.com/tgstation/tgstation-server.git
synced 2026-08-24 05:27:30 +01:00
@@ -8,6 +8,7 @@ using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Tgstation.Server.Api;
|
||||
using Tgstation.Server.Api.Models;
|
||||
using Tgstation.Server.Host.Components.Events;
|
||||
using Tgstation.Server.Host.Core;
|
||||
using Tgstation.Server.Host.IO;
|
||||
using Tgstation.Server.Host.Jobs;
|
||||
|
||||
@@ -12,6 +12,7 @@ using Tgstation.Server.Api.Models;
|
||||
using Tgstation.Server.Api.Models.Internal;
|
||||
using Tgstation.Server.Host.Components.Byond;
|
||||
using Tgstation.Server.Host.Components.Chat;
|
||||
using Tgstation.Server.Host.Components.Events;
|
||||
using Tgstation.Server.Host.Components.Repository;
|
||||
using Tgstation.Server.Host.Components.Session;
|
||||
using Tgstation.Server.Host.Core;
|
||||
|
||||
+1
-1
@@ -5,7 +5,7 @@ using System.Threading.Tasks;
|
||||
using Tgstation.Server.Host.Components.StaticFiles;
|
||||
using Tgstation.Server.Host.Components.Watchdog;
|
||||
|
||||
namespace Tgstation.Server.Host.Components
|
||||
namespace Tgstation.Server.Host.Components.Events
|
||||
{
|
||||
/// <inheritdoc />
|
||||
sealed class EventConsumer : IEventConsumer
|
||||
@@ -0,0 +1,25 @@
|
||||
using System;
|
||||
|
||||
namespace Tgstation.Server.Host.Components.Events
|
||||
{
|
||||
/// <summary>
|
||||
/// Attribute for indicating the script that a given <see cref="EventType"/> runs.
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Field, AllowMultiple = false)]
|
||||
sealed class EventScriptAttribute : Attribute
|
||||
{
|
||||
/// <summary>
|
||||
/// The name of the script the event script the <see cref="EventType"/> runs.
|
||||
/// </summary>
|
||||
public string ScriptName { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="EventScriptAttribute"/> <see langword="class"/>.
|
||||
/// </summary>
|
||||
/// <param name="scriptName">The value of <see cref="ScriptName"/>.</param>
|
||||
public EventScriptAttribute(string scriptName)
|
||||
{
|
||||
ScriptName = scriptName ?? throw new ArgumentNullException(nameof(scriptName));
|
||||
}
|
||||
}
|
||||
}
|
||||
+18
-1
@@ -1,4 +1,4 @@
|
||||
namespace Tgstation.Server.Host.Components
|
||||
namespace Tgstation.Server.Host.Components.Events
|
||||
{
|
||||
/// <summary>
|
||||
/// Types of events. Mirror in tgs.dm
|
||||
@@ -8,86 +8,103 @@
|
||||
/// <summary>
|
||||
/// Parameters: Reference name, commit sha
|
||||
/// </summary>
|
||||
[EventScript("RepoResetOrigin")]
|
||||
RepoResetOrigin,
|
||||
|
||||
/// <summary>
|
||||
/// Parameters: Checkout target
|
||||
/// </summary>
|
||||
[EventScript("RepoCheckout")]
|
||||
RepoCheckout,
|
||||
|
||||
/// <summary>
|
||||
/// No parameters
|
||||
/// </summary>
|
||||
[EventScript("RepoFetch")]
|
||||
RepoFetch,
|
||||
|
||||
/// <summary>
|
||||
/// Parameters: Pull request number, pull request sha, merger message
|
||||
/// </summary>
|
||||
[EventScript("RepoMergePullRequest")]
|
||||
RepoMergePullRequest,
|
||||
|
||||
/// <summary>
|
||||
/// Parameters: Absolute path to repository root
|
||||
/// </summary>
|
||||
[EventScript("PreSynchronize")]
|
||||
RepoPreSynchronize,
|
||||
|
||||
/// <summary>
|
||||
/// Parameters: Version being installed
|
||||
/// </summary>
|
||||
[EventScript("ByondInstallStart")]
|
||||
ByondInstallStart,
|
||||
|
||||
/// <summary>
|
||||
/// Parameters: Error string
|
||||
/// </summary>
|
||||
[EventScript("ByondInstallFail")]
|
||||
ByondInstallFail,
|
||||
|
||||
/// <summary>
|
||||
/// Parameters: Old active version, new active version
|
||||
/// </summary>
|
||||
[EventScript("ByondActiveVersionChange")]
|
||||
ByondActiveVersionChange,
|
||||
|
||||
/// <summary>
|
||||
/// Parameters: Game directory path, origin commit sha
|
||||
/// </summary>
|
||||
[EventScript("PreCompile")]
|
||||
CompileStart,
|
||||
|
||||
/// <summary>
|
||||
/// No parameters
|
||||
/// </summary>
|
||||
[EventScript("CompileCancelled")]
|
||||
CompileCancelled,
|
||||
|
||||
/// <summary>
|
||||
/// Parameters: Game directory path, "1" if compile succeeded and api validation failed, "0" otherwise
|
||||
/// </summary>
|
||||
[EventScript("CompileFailure")]
|
||||
CompileFailure,
|
||||
|
||||
/// <summary>
|
||||
/// Parameters: Game directory path
|
||||
/// </summary>
|
||||
[EventScript("PostCompile")]
|
||||
CompileComplete,
|
||||
|
||||
/// <summary>
|
||||
/// No parameters
|
||||
/// </summary>
|
||||
[EventScript("InstanceAutoUpdateStart")]
|
||||
InstanceAutoUpdateStart,
|
||||
|
||||
/// <summary>
|
||||
/// Parameters: Base sha, target sha, base reference, target reference
|
||||
/// </summary>
|
||||
[EventScript("RepoMergeConflict")]
|
||||
RepoMergeConflict,
|
||||
|
||||
/// <summary>
|
||||
/// No parameters
|
||||
/// </summary>
|
||||
[EventScript("DeploymentComplete")]
|
||||
DeploymentComplete,
|
||||
|
||||
/// <summary>
|
||||
/// Before the watchdog shutsdown. Not sent for graceful shutdowns. No parameters.
|
||||
/// </summary>
|
||||
[EventScript("WatchdogShutdown")]
|
||||
WatchdogShutdown,
|
||||
|
||||
/// <summary>
|
||||
/// Before the watchdog detaches. No parameters.
|
||||
/// </summary>
|
||||
[EventScript("WatchdogDetach")]
|
||||
WatchdogDetach,
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -2,7 +2,7 @@
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Tgstation.Server.Host.Components
|
||||
namespace Tgstation.Server.Host.Components.Events
|
||||
{
|
||||
/// <summary>
|
||||
/// Consumes <see cref="EventType"/>s and takes the appropriate actions
|
||||
@@ -0,0 +1,8 @@
|
||||
# Event System
|
||||
|
||||
Many actions in TGS generate events. They are dispatched via the [IEventConsumer](./IEventConsumer.cs) ([main implementation](./EventConsumer.cs)). From there, it is further sent to the two actual consumers.
|
||||
|
||||
1. The static files system will attempt to run a script with the given [EventScriptAttribute](./EventScriptAttribute)'s `ScriptName`.
|
||||
1. The watchdog will send a mirror of the event via the DMAPI to be handled by game code.
|
||||
|
||||
With these two endpoints, the behaviour of TGS is highly customizable as server operators can customize the entire process.
|
||||
@@ -9,6 +9,7 @@ using Tgstation.Server.Api.Rights;
|
||||
using Tgstation.Server.Host.Components.Byond;
|
||||
using Tgstation.Server.Host.Components.Chat;
|
||||
using Tgstation.Server.Host.Components.Deployment;
|
||||
using Tgstation.Server.Host.Components.Events;
|
||||
using Tgstation.Server.Host.Components.Repository;
|
||||
using Tgstation.Server.Host.Components.Watchdog;
|
||||
using Tgstation.Server.Host.Database;
|
||||
|
||||
@@ -7,6 +7,7 @@ using Tgstation.Server.Host.Components.Byond;
|
||||
using Tgstation.Server.Host.Components.Chat;
|
||||
using Tgstation.Server.Host.Components.Chat.Commands;
|
||||
using Tgstation.Server.Host.Components.Deployment;
|
||||
using Tgstation.Server.Host.Components.Events;
|
||||
using Tgstation.Server.Host.Components.Interop.Bridge;
|
||||
using Tgstation.Server.Host.Components.Repository;
|
||||
using Tgstation.Server.Host.Components.Session;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Tgstation.Server.Host.Components.Events;
|
||||
|
||||
namespace Tgstation.Server.Host.Components.Interop.Topic
|
||||
{
|
||||
|
||||
@@ -10,6 +10,7 @@ Component code is where the magic and tears of TGS are made. There are six main
|
||||
|
||||
There exist two more namespaces in here that don't directly fit in these 6 components.
|
||||
|
||||
- [Events](./Events) deals with the TGS event system.
|
||||
- [Interop](./Interop) deals with the bulk of DMAPI communication (Though it's not all contained here).
|
||||
- [Session](./Session) contains the classes used for actually executing DreamDaemon, sending topic requests, receiving bridge requests, among other things.
|
||||
|
||||
@@ -19,4 +20,6 @@ While the database represents stored instance data, in component code an instanc
|
||||
|
||||
`IInstance`s are created via the [IInstanceFactory](./IInstanceFactory.cs) ([implementation](./InstanceFactory.cs)) and are generally controlled via the [IInstanceManager](./IInstanceManager.cs) ([implementation](./InstanceManager.cs)).
|
||||
|
||||
Many classes in here implement [IHostedService](), `InstanceManager` being the only one that is called by the ASP.NET runtime. In the case of instances `StartAsync()` is called when an `Instance` is being brought online (from server startup or user request). The `Instance` handles calling `StartAsync()` on its various subcomponents that need it. When an `Instance` is being brought offline (from server shutdown/restart/update or user request) the same pattern is followed calling `StopAsync()`.
|
||||
Many classes in here implement [IHostedService](https://docs.microsoft.com/en-us/aspnet/core/fundamentals/host/hosted-services?view=aspnetcore-3.1&tabs=visual-studio), `InstanceManager` being the only one that is called by the ASP.NET runtime. In the case of instances `StartAsync()` is called when an `Instance` is being brought online (from server startup or user request). The `Instance` handles calling `StartAsync()` on its various subcomponents that need it. When an `Instance` is being brought offline (from server shutdown/restart/update or user request) the same pattern is followed calling `StopAsync()`.
|
||||
|
||||
`IInstanceManager` is the sole point where the controllers talk to component code. It also dispatches bridge requests to their relevant instances.
|
||||
|
||||
@@ -8,6 +8,7 @@ using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Tgstation.Server.Api.Models;
|
||||
using Tgstation.Server.Host.Components.Events;
|
||||
using Tgstation.Server.Host.IO;
|
||||
using Tgstation.Server.Host.Jobs;
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Tgstation.Server.Api.Models;
|
||||
using Tgstation.Server.Host.Components.Events;
|
||||
using Tgstation.Server.Host.Core;
|
||||
using Tgstation.Server.Host.IO;
|
||||
using Tgstation.Server.Host.Jobs;
|
||||
|
||||
@@ -9,6 +9,7 @@ using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Tgstation.Server.Api.Models;
|
||||
using Tgstation.Server.Host.Components.Events;
|
||||
using Tgstation.Server.Host.Core;
|
||||
using Tgstation.Server.Host.IO;
|
||||
using Tgstation.Server.Host.Jobs;
|
||||
@@ -32,12 +33,18 @@ namespace Tgstation.Server.Host.Components.StaticFiles
|
||||
const string CodeModificationsHeadFile = "HeadInclude.dm";
|
||||
const string CodeModificationsTailFile = "TailInclude.dm";
|
||||
|
||||
static readonly IReadOnlyDictionary<EventType, string> EventTypeScriptFileNameMap = new Dictionary<EventType, string>
|
||||
{
|
||||
{ EventType.CompileStart, "PreCompile" },
|
||||
{ EventType.CompileComplete, "PostCompile" },
|
||||
{ EventType.RepoPreSynchronize, "PreSynchronize" }
|
||||
};
|
||||
static readonly IReadOnlyDictionary<EventType, string> EventTypeScriptFileNameMap = new Dictionary<EventType, string>(
|
||||
Enum.GetValues(typeof(EventType))
|
||||
.OfType<EventType>()
|
||||
.Select(
|
||||
eventType => new KeyValuePair<EventType, string>(
|
||||
eventType,
|
||||
typeof(EventType)
|
||||
.GetField(eventType.ToString())
|
||||
.GetCustomAttributes(false)
|
||||
.OfType<EventScriptAttribute>()
|
||||
.First()
|
||||
.ScriptName)));
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="IIOManager"/> for <see cref="Configuration"/>
|
||||
|
||||
@@ -4,6 +4,7 @@ using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Tgstation.Server.Api.Models;
|
||||
using Tgstation.Server.Host.Components.Events;
|
||||
using Tgstation.Server.Host.Security;
|
||||
|
||||
namespace Tgstation.Server.Host.Components.StaticFiles
|
||||
|
||||
@@ -3,6 +3,7 @@ using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Tgstation.Server.Api.Models.Internal;
|
||||
using Tgstation.Server.Host.Components.Events;
|
||||
using Tgstation.Server.Host.Components.Session;
|
||||
|
||||
namespace Tgstation.Server.Host.Components.Watchdog
|
||||
|
||||
@@ -11,6 +11,7 @@ using Tgstation.Server.Api.Models.Internal;
|
||||
using Tgstation.Server.Api.Rights;
|
||||
using Tgstation.Server.Host.Components.Chat;
|
||||
using Tgstation.Server.Host.Components.Deployment;
|
||||
using Tgstation.Server.Host.Components.Events;
|
||||
using Tgstation.Server.Host.Components.Interop.Topic;
|
||||
using Tgstation.Server.Host.Components.Session;
|
||||
using Tgstation.Server.Host.Core;
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using System;
|
||||
|
||||
|
||||
namespace Tgstation.Server.Host.Components.Events.Tests
|
||||
{
|
||||
/// <summary>
|
||||
/// Tests for the <see cref="EventScriptAttribute"/> <see langword="class"/>.
|
||||
/// </summary>
|
||||
[TestClass]
|
||||
public sealed class TestEventScriptAttribute
|
||||
{
|
||||
[TestMethod]
|
||||
public void TestConstruction()
|
||||
{
|
||||
Assert.ThrowsException<ArgumentNullException>(() => new EventScriptAttribute(null));
|
||||
var test = new EventScriptAttribute("test");
|
||||
Assert.AreEqual("test", test.ScriptName);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Tgstation.Server.Host.Components.Events.Tests
|
||||
{
|
||||
[TestClass]
|
||||
public sealed class TestEventType
|
||||
{
|
||||
[TestMethod]
|
||||
public void TestAllEventTypesHaveUniqueEventScriptAttributes()
|
||||
{
|
||||
var allScripts = new HashSet<string>();
|
||||
foreach (var eventType in Enum.GetValues(typeof(EventType))) {
|
||||
var list = typeof(EventType)
|
||||
.GetField(eventType.ToString())
|
||||
.GetCustomAttributes(false)
|
||||
.OfType<EventScriptAttribute>()
|
||||
.ToList();
|
||||
Assert.AreEqual(1, list.Count, $"EventType: {eventType}");
|
||||
|
||||
var attribute = list.First();
|
||||
Assert.IsTrue(allScripts.Add(attribute.ScriptName), $"Non-unique script Name: {attribute.ScriptName}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user