diff --git a/README.md b/README.md index 10e2440f64..cc56545b7d 100644 --- a/README.md +++ b/README.md @@ -65,9 +65,9 @@ Create an `appsettings.Production.json` file next to `appsettings.json`. This wi - `General:GitHubAccessToken`: Specify a GitHub personal access token with no scopes here to highly mitigate the possiblity of 429 response codes from GitHub requests -- `Logging:LogLevel:Default`: Can be one of `Trace`, `Debug`, `Information`, `Warning`, `Error`, or `Critical`. Restricts what is put into the log files. Currently `Debug` is reccommended for help with error reporting. +- `General:LogFileLevel`: Can be one of `Trace`, `Debug`, `Information`, `Warning`, `Error`, or `Critical`. Restricts what is put into the log files. Currently `Debug` is reccommended for help with error reporting. -- `Kestrel:Endpoints:Http:Url`: The URL (i.e. interface and ports) your application should listen on. General use case should be `http://localhost:` for restricted local connections. See the Remote Access section for configuring public access to the World Wide Web. +- `Kestrel:Endpoints:Http:Url`: The URL (i.e. interface and ports) your application should listen on. General use case should be `http://localhost:` for restricted local connections. See the Remote Access section for configuring public access to the World Wide Web. This doesn't need to be changed using the docker setup and should be mapped with the `-p` option instead - `Database:DatabaseType`: Can be one of `SqlServer`, `MariaDB`, or `MySql` @@ -107,6 +107,52 @@ A breaking change from V3: tgstation-server 4 now REQUIRES the DMAPI to be integ The DMAPI is fully backwards compatible and should function with any tgstation-server version to date. Updates can be performed in the same manner. Using the `TGS_EXTERNAL_CONFIGURATION` is recommended in order to make the process as easy as replacing `tgs.dm` and the `tgs` folder with a new version +### Example + +Here is a bare minimum example project that implements the essential code changes for integrating the DMAPI + +Before `tgs.dm`: +``` +//Remember, every codebase is different, you probably have better methods for these defines than the ones given here +#define TGS_EXTERNAL_CONFIGURATION +#define TGS_DEFINE_AND_SET_GLOBAL(Name, Value) var/global/##Name = ##Value +#define TGS_READ_GLOBAL(Name) global.##Name +#define TGS_WRITE_GLOBAL(Name, Value) global.##Name = ##Value +#define TGS_WORLD_ANNOUNCE(message) world << ##message +#define TGS_INFO_LOG(message) world.log << "TGS Info: [##message]" +#define TGS_ERROR_LOG(message) world.log << "TGS Error: [##message]" +#define TGS_NOTIFY_ADMINS(event) world.log << "TGS Admin Message: [##event]" +#define TGS_CLIENT_COUNT global.client_cout +#define TGS_PROTECT_DATUM(Path) // Leave blank if your codebase doesn't give administrators code reflection capabilities +``` + +Anywhere else: +```dm +var/global/client_count = 0 + +/world/New() + ..() + TgsNew() + TgsInitializationsComplete() + +/world/Reboot() + TgsReboot() + ..() + +/world/Topic() + TGS_TOPIC + ..() + +/client/New() + ..() + ++global.client_count + +/client/Del() + ..() + --global.client_count + +``` + ## Remote Access tgstation-server is an [ASP.Net Core](https://docs.microsoft.com/en-us/aspnet/core/) based on the Kestrel web server. This section is meant to serve as a general use case overview, but the entire Kestrel configuration can be modified to your liking with the configuration JSON. See [the official documentation](https://docs.microsoft.com/en-us/aspnet/core/fundamentals/servers/kestrel) for details. diff --git a/build/Dockerfile b/build/Dockerfile index 270755a249..240aeb77f9 100644 --- a/build/Dockerfile +++ b/build/Dockerfile @@ -20,7 +20,7 @@ WORKDIR /src/src/Tgstation.Server.Host RUN dotnet publish -c Release -o /app/lib/Default && mv /app/lib/Default/appsettings* /app FROM microsoft/dotnet:2.1-aspnetcore-runtime -EXPOSE 5000 +EXPOSE 80 #needed for byond RUN apt-get update \ @@ -32,9 +32,8 @@ WORKDIR /app COPY --from=build /app . COPY --from=build /src/build/tgs.docker.sh tgs.sh -COPY --from=build /src/src/Tgstation.Server.Host/appsettings.Docker.json . -RUN mkdir /config_data && mv appsettings.Docker.json /config_data/appsettings.Production.json +RUN mkdir /config_data VOLUME ["/config_data", "/tgs_logs", "/app/lib"] ENTRYPOINT ["./tgs.sh"] diff --git a/build/tgs.docker.sh b/build/tgs.docker.sh index f397e9b9b5..690f9e1c03 100755 --- a/build/tgs.docker.sh +++ b/build/tgs.docker.sh @@ -1,6 +1,5 @@ #!/bin/sh -mkdir /config_data -cp -r /config_data/* ./ +ln -s /config_data/appsettings.Production.json /app/appsettings.Production.json exec dotnet Tgstation.Server.Host.Console.dll "$@" diff --git a/src/Tgstation.Server.Api/Models/Internal/DreamDaemonLaunchParameters.cs b/src/Tgstation.Server.Api/Models/Internal/DreamDaemonLaunchParameters.cs index b1f3496329..5e79d35cff 100644 --- a/src/Tgstation.Server.Api/Models/Internal/DreamDaemonLaunchParameters.cs +++ b/src/Tgstation.Server.Api/Models/Internal/DreamDaemonLaunchParameters.cs @@ -1,4 +1,5 @@ -using System.ComponentModel.DataAnnotations; +using System; +using System.ComponentModel.DataAnnotations; namespace Tgstation.Server.Api.Models.Internal { @@ -36,5 +37,17 @@ namespace Tgstation.Server.Api.Models.Internal /// [Required] public uint? StartupTimeout { get; set; } + + /// + /// Check if we match a given set of + /// + /// The to compare against + /// if they match, otherwise + public bool Match(DreamDaemonLaunchParameters otherParameters) => + AllowWebClient == otherParameters.AllowWebClient + && SecurityLevel == otherParameters.SecurityLevel + && PrimaryPort == otherParameters.PrimaryPort + && SecondaryPort == otherParameters.SecondaryPort + && StartupTimeout == otherParameters.StartupTimeout; } } \ No newline at end of file diff --git a/src/Tgstation.Server.Host/Components/Repository/Repository.cs b/src/Tgstation.Server.Host/Components/Repository/Repository.cs index 9f8ec89daa..66845fc093 100644 --- a/src/Tgstation.Server.Host/Components/Repository/Repository.cs +++ b/src/Tgstation.Server.Host/Components/Repository/Repository.cs @@ -369,7 +369,10 @@ namespace Tgstation.Server.Host.Components.Repository var remote = repository.Network.Remotes.First(); try { - repository.Network.Push(remote, String.Format(CultureInfo.InvariantCulture, "+{0}:{0}", branch.CanonicalName), GeneratePushOptions(progressReporter, username, password, cancellationToken)); + var forcePushString = String.Format(CultureInfo.InvariantCulture, "+{0}:{0}", branch.CanonicalName); + repository.Network.Push(remote,forcePushString, GeneratePushOptions(progress => progressReporter((int)(0.9f * progress)), username, password, cancellationToken)); + var removalString = String.Format(CultureInfo.InvariantCulture, ":{0}", branch.CanonicalName); + repository.Network.Push(remote, removalString, GeneratePushOptions(progress => progressReporter(90 + (int)(0.1f * progress)), username, password, cancellationToken)); } catch (UserCancelledException) { diff --git a/src/Tgstation.Server.Host/Components/Watchdog/Watchdog.cs b/src/Tgstation.Server.Host/Components/Watchdog/Watchdog.cs index 412612e49c..2975471eeb 100644 --- a/src/Tgstation.Server.Host/Components/Watchdog/Watchdog.cs +++ b/src/Tgstation.Server.Host/Components/Watchdog/Watchdog.cs @@ -441,11 +441,10 @@ namespace Tgstation.Server.Host.Components.Watchdog monitorState.NextAction = MonitorAction.Continue; break; case MonitorActivationReason.NewDmbAvailable: - monitorState.InactiveServerHasStagedDmb = true; - await UpdateAndRestartInactiveServer(true).ConfigureAwait(false); //next case does same thing - break; + monitorState.InactiveServerHasStagedDmb = true; + goto case MonitorActivationReason.ActiveLaunchParametersUpdated; case MonitorActivationReason.ActiveLaunchParametersUpdated: - await UpdateAndRestartInactiveServer(false).ConfigureAwait(false); + await UpdateAndRestartInactiveServer(true).ConfigureAwait(false); break; } } @@ -617,6 +616,8 @@ namespace Tgstation.Server.Host.Components.Watchdog { using (await SemaphoreSlimContext.Lock(semaphore, cancellationToken).ConfigureAwait(false)) { + if (launchParameters.Match(ActiveLaunchParameters)) + return; ActiveLaunchParameters = launchParameters; if (Running) //queue an update @@ -780,6 +781,7 @@ namespace Tgstation.Server.Host.Components.Watchdog /// public async Task Restart(bool graceful, CancellationToken cancellationToken) { + logger.LogTrace("Begin Restart. Graceful: {0}", graceful); using (await SemaphoreSlimContext.Lock(semaphore, cancellationToken).ConfigureAwait(false)) { if (!graceful || !Running) diff --git a/src/Tgstation.Server.Host/Configuration/GeneralConfiguration.cs b/src/Tgstation.Server.Host/Configuration/GeneralConfiguration.cs index 0451038e48..fc5cf03f09 100644 --- a/src/Tgstation.Server.Host/Configuration/GeneralConfiguration.cs +++ b/src/Tgstation.Server.Host/Configuration/GeneralConfiguration.cs @@ -15,6 +15,11 @@ /// public string LogFileDirectory { get; set; } + /// + /// The stringified for file logging + /// + public string LogFileLevel { get; set; } + /// /// If file logging is disabled /// diff --git a/src/Tgstation.Server.Host/Core/Application.cs b/src/Tgstation.Server.Host/Core/Application.cs index b024cde939..9ee1c8eca9 100644 --- a/src/Tgstation.Server.Host/Core/Application.cs +++ b/src/Tgstation.Server.Host/Core/Application.cs @@ -25,7 +25,6 @@ using Tgstation.Server.Host.Components.Chat; using Tgstation.Server.Host.Components.Repository; using Tgstation.Server.Host.Components.Watchdog; using Tgstation.Server.Host.Configuration; -using Tgstation.Server.Host.Controllers; using Tgstation.Server.Host.IO; using Tgstation.Server.Host.Models; using Tgstation.Server.Host.Security; @@ -47,7 +46,7 @@ namespace Tgstation.Server.Host.Core /// /// The for the /// - readonly Microsoft.Extensions.Configuration.IConfiguration configuration; + readonly IConfiguration configuration; /// /// The for the @@ -55,13 +54,19 @@ namespace Tgstation.Server.Host.Core readonly Microsoft.AspNetCore.Hosting.IHostingEnvironment hostingEnvironment; readonly TaskCompletionSource startupTcs; + static LogLevel GetMinimumLogLevel(string stringLevel) + { + if (String.IsNullOrWhiteSpace(stringLevel) || !Enum.TryParse(stringLevel, out var minimumLevel)) + minimumLevel = LogLevel.Information; + return minimumLevel; + } /// /// Construct an /// /// The value of /// The value of - public Application(Microsoft.Extensions.Configuration.IConfiguration configuration, Microsoft.AspNetCore.Hosting.IHostingEnvironment hostingEnvironment) + public Application(IConfiguration configuration, Microsoft.AspNetCore.Hosting.IHostingEnvironment hostingEnvironment) { this.configuration = configuration ?? throw new ArgumentNullException(nameof(configuration)); this.hostingEnvironment = hostingEnvironment ?? throw new ArgumentNullException(nameof(hostingEnvironment)); @@ -76,9 +81,7 @@ namespace Tgstation.Server.Host.Core /// Configure dependency injected services /// /// The to configure -#pragma warning disable CA1822 // Mark members as static public void ConfigureServices(IServiceCollection services) -#pragma warning restore CA1822 // Mark members as static { if (services == null) throw new ArgumentNullException(nameof(services)); @@ -97,7 +100,8 @@ namespace Tgstation.Server.Host.Core if (generalConfiguration?.DisableFileLogging != true) { var logPath = !String.IsNullOrEmpty(generalConfiguration?.LogFileDirectory) ? generalConfiguration.LogFileDirectory : ioManager.ConcatPath(Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData), VersionPrefix, "Logs"); - services.AddLogging(builder => builder.AddFile(ioManager.ConcatPath(logPath, "tgs-{Date}.log"))); + + services.AddLogging(builder => builder.AddFile(ioManager.ConcatPath(logPath, "tgs-{Date}.log"), GetMinimumLogLevel(generalConfiguration?.LogFileLevel))); } services.AddOptions(); @@ -266,12 +270,15 @@ namespace Tgstation.Server.Host.Core /// public void Ready(Exception initializationError) { - if (startupTcs.Task.IsCompleted) - throw new InvalidOperationException("Ready has already been called!"); - if (initializationError == null) - startupTcs.SetResult(null); - else - startupTcs.SetException(initializationError); + lock (startupTcs) + { + if (startupTcs.Task.IsCompleted) + throw new InvalidOperationException("Ready has already been called!"); + if (initializationError == null) + startupTcs.SetResult(null); + else + startupTcs.SetException(initializationError); + } } } } \ No newline at end of file diff --git a/src/Tgstation.Server.Host/appsettings.Docker.json b/src/Tgstation.Server.Host/appsettings.Docker.json deleted file mode 100644 index 142c7e376b..0000000000 --- a/src/Tgstation.Server.Host/appsettings.Docker.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "General": { - "LogFileDirectory": "/tgs_logs", - "MinimumPasswordLength": 15, - "GitHubAccessToken": null - }, - "Kestrel": { - "Endpoints": { - "Http": { - "Url": "http://localhost:5000" - } - } - }, - "Database": { - "DatabaseType": "SqlServer or MySQL or MariaDB", - "ConnectionString": "", - "MySqlServerVersion": "" - } -} diff --git a/src/Tgstation.Server.Host/appsettings.json b/src/Tgstation.Server.Host/appsettings.json index 79fa36f60b..0504931d30 100644 --- a/src/Tgstation.Server.Host/appsettings.json +++ b/src/Tgstation.Server.Host/appsettings.json @@ -2,6 +2,7 @@ "General": { "LogFileDirectory": null, //use the default path "DisableFileLogging": false, + "LogFileLevel": "Debug", "MinimumPasswordLength": 15, "GitHubAccessToken": null }, @@ -25,10 +26,6 @@ "Default": "Trace", "Microsoft": "Warning" } - }, - "LogLevel": { - "Default": "Information", - "Microsoft": "Warning" } }, "Updates": {