diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md
index f750428a65..9e69ae9dae 100644
--- a/.github/CONTRIBUTING.md
+++ b/.github/CONTRIBUTING.md
@@ -48,7 +48,7 @@ If the command runs successfully you're all set up. Now, here is the debugging p
1. Set your breakpoints
1. Start the service
1. Use your environment to attach to `TGServerService.exe` for debugging
-1. If you need to debug startup, you'll have to add `System.Diagnostics.Debugger.Start()` where you want the service to wait for you. Note that breaking in the constructor of `TGServerService.Service` will cause Windows to terminate the process if you don't move fast enough due to it thinking the service is malfunctioning. In fact, if you need to debug the constructor, it's better to launch `TGServerService.exe` as a regular debug session, you won't be able to pass the `Service.Run()` call that way, however.
+1. If you need to debug startup, you'll have to add `System.Diagnostics.Debugger.Start()` where you want the service to wait for you. Do not write a constructor for the Service classas Windows will not let you debug it properly
Now be careful while debugging. The service runs with root level privileges and you wouldn't want any [accidents](http://i.imgur.com/zvGEpJD.png) to happen, would you?
diff --git a/DMAPI/server_tools.dm b/DMAPI/server_tools.dm
index 88daeb3667..ea1172964d 100644
--- a/DMAPI/server_tools.dm
+++ b/DMAPI/server_tools.dm
@@ -1,4 +1,5 @@
-// /tg/station 13 server tools API v3.1.0.2
+// /tg/station 13 server tools API
+#define SERVICE_API_VERSION_STRING "3.2.0.0"
//CONFIGURATION
//use this define if you want to do configuration outside of this file
@@ -64,14 +65,13 @@
//IMPLEMENTATION
-#define SERVICE_API_VERSION_STRING "3.1.0.2"
-
#define REBOOT_MODE_NORMAL 0
#define REBOOT_MODE_HARD 1
#define REBOOT_MODE_SHUTDOWN 2
#define SERVICE_WORLD_PARAM "server_service"
#define SERVICE_VERSION_PARAM "server_service_version"
+#define SERVICE_INSTANCE_PARAM "server_instance"
#define SERVICE_PR_TEST_JSON "prtestjob.json"
#define SERVICE_INTERFACE_DLL "TGServiceInterface.dll"
#define SERVICE_INTERFACE_FUNCTION "DDEntryPoint"
diff --git a/DMAPI/st_interface.dm b/DMAPI/st_interface.dm
index 07e6a40bff..fc017830de 100644
--- a/DMAPI/st_interface.dm
+++ b/DMAPI/st_interface.dm
@@ -30,7 +30,7 @@ SERVER_TOOLS_DEFINE_AND_SET_GLOBAL(server_tools_api_compatible, FALSE)
return
if(skip_compat_check && !fexists(SERVICE_INTERFACE_DLL))
CRASH("Service parameter present but no interface DLL detected. This is symptomatic of running a service less than version 3.1! Please upgrade.")
- call(SERVICE_INTERFACE_DLL, SERVICE_INTERFACE_FUNCTION)(command) //trust no retval
+ call(SERVICE_INTERFACE_DLL, SERVICE_INTERFACE_FUNCTION)("[params[SEVICE_INSTANCE_PARAM]] [command]") //trust no retval
return TRUE
/world/proc/ChatBroadcast(message)
diff --git a/README.md b/README.md
index 0002660659..89d12d953e 100644
--- a/README.md
+++ b/README.md
@@ -16,7 +16,7 @@ Requires python 2.7/3.6 to be installed for changelog generation
## Installing (GUI):
1. Launch TGControlPanel.exe as an administrator. A shortcut can be found on your desktop
-1. Optionally switch to the `Server` tab and change the Server Path location from C:\\tgstation-server-3 to wherever you wish
+1. Optionally switch to the `Server` tab and change the Server Path location from `C:\\tgstation-server-3` to wherever you wish
1. Go to the `Repository` Tab and set the remote address and branch of the git you with to track
1. Hit the clone button
1. While waiting go to the BYOND tab and install the BYOND version you wish
@@ -120,10 +120,13 @@ The service supports updates while running a DreamDaemon instance. Simply instal
* `prtestjob.json`
* This contains information about current test merged pull requests in the Repository folder
-
+
* `TGS3.json`
* This is a copy of TGS3.json from the Repository. If a repostory change creates differences between the two, update operations will be blocked until the user confirms they want to change it
+* `Instance.cfg`
+ * The encrypted internal configuration settings for a server instance. Note that access to this file bypasses API user restrictions
+
### Starting the game server:
To run the game server, open the `Server` tab of the control panel and click either `Start`
diff --git a/TGServerService/DeprecatedInstanceConfig.cs b/TGServerService/DeprecatedInstanceConfig.cs
index 501426c241..152962fdfa 100644
--- a/TGServerService/DeprecatedInstanceConfig.cs
+++ b/TGServerService/DeprecatedInstanceConfig.cs
@@ -1,5 +1,5 @@
using System;
-using TGServiceInterface;
+using System.Configuration;
namespace TGServerService
{
@@ -8,6 +8,11 @@ namespace TGServerService
///
class DeprecatedInstanceConfig : InstanceConfig
{
+ ///
+ /// The default directory
+ ///
+ const string DefaultInstallationPath = "C:\\tgstation-server-3";
+
///
/// Convert the settings version 6 .NET settings file to a config json
///
@@ -15,33 +20,78 @@ namespace TGServerService
public static InstanceConfig CreateFromNETSettings()
{
var Config = Properties.Settings.Default;
- var result = new DeprecatedInstanceConfig((string)Config.GetPreviousVersion("ServerDirectory"))
- {
- ProjectName = (string)Config.GetPreviousVersion("ProjectName"),
- Port = (ushort)Config.GetPreviousVersion("ServerPort"),
- CommitterName = (string)Config.GetPreviousVersion("CommitterName"),
- CommitterEmail = (string)Config.GetPreviousVersion("CommitterEmail"),
- Security = (DreamDaemonSecurity)Config.GetPreviousVersion("ServerSecurity"),
- Autostart = (bool)Config.GetPreviousVersion("DDAutoStart"),
- ChatProviderData = (string)Config.GetPreviousVersion("ChatProviderData"),
- ChatProviderEntropy = (string)Config.GetPreviousVersion("ChatProviderEntropy"),
- ReattachRequired = (bool)Config.GetPreviousVersion("ReattachToDD"),
- ReattachProcessID = (int)Config.GetPreviousVersion("ReattachPID"),
- ReattachPort = (ushort)Config.GetPreviousVersion("ReattachPort"),
- ReattachCommsKey = (string)Config.GetPreviousVersion("ReattachCommsKey"),
- ReattachAPIVersion = (string)Config.GetPreviousVersion("ReattachAPIVersion"),
- AutoUpdateInterval = (ulong)Config.GetPreviousVersion("AutoUpdateInterval"),
- AuthorizedUserGroupSID = (string)Config.GetPreviousVersion("AuthorizedGroupSID")
- };
+ var result = new DeprecatedInstanceConfig(LoadPreviousNetPropertyOrDefault("ServerDirectory", "C:\\tgstation-server-3"));
+ // using nameof for sanity where possible
+ result.ProjectName = LoadPreviousNetPropertyOrDefault(nameof(ProjectName), result.ProjectName);
+ result.Port = LoadPreviousNetPropertyOrDefault("ServerPort", result.Port);
+ result.CommitterName = LoadPreviousNetPropertyOrDefault(nameof(CommitterName), result.CommitterName);
+ result.CommitterEmail = LoadPreviousNetPropertyOrDefault(nameof(CommitterEmail), result.CommitterEmail);
+ result.Security = LoadPreviousNetPropertyOrDefault("ServerSecurity", result.Security);
+ result.Autostart = LoadPreviousNetPropertyOrDefault("DDAutoStart", result.Autostart);
+ result.ChatProviderData = LoadPreviousNetPropertyOrDefault(nameof(ChatProviderData), result.ChatProviderData);
+ result.ChatProviderEntropy = LoadPreviousNetPropertyOrDefault(nameof(ChatProviderEntropy), result.ChatProviderEntropy);
+ result.ReattachRequired = LoadPreviousNetPropertyOrDefault("ReattachToDD", result.ReattachRequired);
+ result.ReattachProcessID = LoadPreviousNetPropertyOrDefault("ReattachPID", result.ReattachProcessID);
+ result.ReattachPort = LoadPreviousNetPropertyOrDefault(nameof(ReattachPort), result.ReattachPort);
+ result.ReattachCommsKey = LoadPreviousNetPropertyOrDefault(nameof(ReattachCommsKey), result.ReattachCommsKey);
+ result.ReattachAPIVersion = LoadPreviousNetPropertyOrDefault(nameof(ReattachAPIVersion), result.ReattachAPIVersion);
+ result.AutoUpdateInterval = LoadPreviousNetPropertyOrDefault(nameof(AutoUpdateInterval), result.AutoUpdateInterval);
+ result.AuthorizedUserGroupSID = LoadPreviousNetPropertyOrDefault("AuthorizedGroupSID", result.AuthorizedUserGroupSID);
result.MigrateToCurrentVersion();
return result;
}
+ ///
+ /// Loads a previous .NET config and returns it or some if it wasn't set
+ ///
+ /// The type of the
+ /// The config key of the property
+ /// The default value of the
+ /// The if it exists, otherwise the
+ static T LoadPreviousNetPropertyOrDefault(string property, T defaultValue)
+ {
+ //try it the simple way first
+ try
+ {
+ var result = (T)Properties.Settings.Default.GetPreviousVersion(property);
+ return result == null ? defaultValue : result;
+ }
+ catch
+ {
+ try
+ {
+ //.NET is fucking stupid
+ //If we don't have the correct property in our *CURRENT* .settings file it will automatically throw an exception when it tries to load it
+ //Which means we can never fucking delete config settings
+ //Which is fucking retarded
+ //This hooks into the settings provider and forces it to load it anyway
+ var Config = Properties.Settings.Default;
+ var Provider = Config.Properties[nameof(Config.SettingsVersion)].Provider; //nameof for sanity
+
+ var sp = new SettingsProperty(property)
+ {
+ PropertyType = typeof(T),
+ DefaultValue = defaultValue,
+ Provider = Provider
+ };
+
+ var ProviderInterface = Provider as IApplicationSettingsProvider;
+
+ var result = ProviderInterface.GetPreviousVersion(Config.Context, sp);
+ if (result != null && result.PropertyValue != null)
+ return (T)result.PropertyValue;
+ }
+ catch { }
+ //f u c k i t
+ return defaultValue;
+ }
+ }
+
///
/// Construct a . Used by the deserializer
///
[Obsolete("This method is for use by the deserializer only.", true)]
- public DeprecatedInstanceConfig() : base(null) { }
+ public DeprecatedInstanceConfig() : base(DefaultInstallationPath) { }
///
/// Construct a for a at
diff --git a/TGServerService/InstanceConfig.cs b/TGServerService/InstanceConfig.cs
index 7d0080e8b5..54996683fd 100644
--- a/TGServerService/InstanceConfig.cs
+++ b/TGServerService/InstanceConfig.cs
@@ -16,7 +16,6 @@ namespace TGServerService
public string InstanceDirectory { get; private set; }
public ulong Version { get; protected set; } = CurrentVersion;
- public Guid ID { get; private set; } = Guid.NewGuid();
public string Name { get; set; } = "TG Station Server";
public bool Enabled { get; set; } = true;
@@ -55,7 +54,9 @@ namespace TGServerService
public void Save()
{
- File.WriteAllText(Path.Combine(InstanceDirectory, JSONFilename), new JavaScriptSerializer().Serialize(this));
+ var data = new JavaScriptSerializer().Serialize(this);
+ var path = Path.Combine(InstanceDirectory, JSONFilename);
+ File.WriteAllText(path, data);
}
public static InstanceConfig Load(string path)
diff --git a/TGServerService/Program.cs b/TGServerService/Program.cs
index 30e08ba944..dce1973967 100644
--- a/TGServerService/Program.cs
+++ b/TGServerService/Program.cs
@@ -9,7 +9,7 @@ namespace TGServerService
///
/// Entry point to the program
///
- static void Main() => new Service();
+ static void Main() => Service.Launch();
///
/// Copy a file from to , but first ensure the destination directory exists
diff --git a/TGServerService/ServerInstance/DreamDaemon.cs b/TGServerService/ServerInstance/DreamDaemon.cs
index 1ac4fcf034..1c27e161d5 100644
--- a/TGServerService/ServerInstance/DreamDaemon.cs
+++ b/TGServerService/ServerInstance/DreamDaemon.cs
@@ -577,7 +577,7 @@ namespace TGServerService
GenCommsKey();
StartingSecurity = Config.Security;
- Proc.StartInfo.Arguments = String.Format("{0} -port {1} {5}-close -verbose -params \"server_service={3}&server_service_version={4}\" -{2} -public", DMB, Config.Port, SecurityWord(), serviceCommsKey, Version(), Config.Webclient ? "-webclient " : "");
+ Proc.StartInfo.Arguments = String.Format("{0} -port {1} {5}-close -verbose -params \"server_service={3}&server_service_version={4}&server_instance={6}\" -{2} -public", DMB, Config.Port, SecurityWord(), serviceCommsKey, Version(), Config.Webclient ? "-webclient " : "", Config.Name);
UpdateInterfaceDll(true);
lock (topicLock)
{
diff --git a/TGServerService/ServerInstance/ServerInstance.cs b/TGServerService/ServerInstance/ServerInstance.cs
index 995708e849..64b05b9a15 100644
--- a/TGServerService/ServerInstance/ServerInstance.cs
+++ b/TGServerService/ServerInstance/ServerInstance.cs
@@ -24,7 +24,7 @@ namespace TGServerService
///
/// The configuration settings for the instance
///
- public readonly InstanceConfig Config;
+ readonly InstanceConfig Config;
///
/// Constructs and a
///
@@ -51,6 +51,7 @@ namespace TGServerService
DisposeByond();
DisposeRepo();
DisposeChat();
+ Config.Save();
}
///
diff --git a/TGServerService/Service.cs b/TGServerService/Service.cs
index f9e52c8306..8295fa2756 100644
--- a/TGServerService/Service.cs
+++ b/TGServerService/Service.cs
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
+using System.Collections.Specialized;
using System.Diagnostics;
using System.IO;
using System.Security.Principal;
@@ -13,7 +14,8 @@ namespace TGServerService
///
/// The windows service the application runs as
///
- sealed partial class Service : ServiceBase
+ [ServiceBehavior(ConcurrencyMode = ConcurrencyMode.Multiple, InstanceContextMode = InstanceContextMode.Single)]
+ sealed class Service : ServiceBase, ITGSService, ITGConnectivity
{
///
/// The logging ID used for events
@@ -50,6 +52,25 @@ namespace TGServerService
ActiveService.EventLog.WriteEntry(message, eventType, (int)id + loggingID);
}
+ ///
+ /// Constructs and runs a . Do not add any code that might need debugging here as it's near impossible to get Windows to debug it properly without timing out
+ ///
+ public static void Launch()
+ {
+ if (ActiveService != null)
+ throw new Exception("There is already a Service instance running!");
+ ActiveService = new Service();
+ Run(ActiveService);
+ }
+
+ ///
+ /// Sets up the service name. Do not add any more code due to the reasons outlined in
+ ///
+ Service()
+ {
+ ServiceName = "TG Station Server";
+ }
+
///
/// The WCF host that contains connects to
///
@@ -69,49 +90,16 @@ namespace TGServerService
/// The version to migrate from
void MigrateSettings(int oldVersion)
{
+ var Config = Properties.Settings.Default;
switch (oldVersion)
{
case 6: //switch to per-instance configs
var IC = DeprecatedInstanceConfig.CreateFromNETSettings();
IC.Save();
- Properties.Settings.Default.InstancePaths.Add(IC.InstanceDirectory);
+ Config.InstancePaths.Add(IC.InstanceDirectory);
break;
}
}
-
- //you should seriously not add anything here
- //Use OnStart instead
- ///
- /// Construct and run a . Can only execute in the context of the Windows service manager
- ///
- public Service()
- {
- var Config = Properties.Settings.Default;
- try
- {
- Environment.CurrentDirectory = Directory.CreateDirectory(Path.GetTempPath() + "/TGStationServerService").FullName; //MOVE THIS POINTER BECAUSE ONE TIME I ALMOST ACCIDENTALLY NUKED MYSELF BY REFACTORING! http://imgur.com/zvGEpJD.png
- if (Properties.Settings.Default.UpgradeRequired)
- {
- var newVersion = Config.SettingsVersion;
- Config.Upgrade();
-
- for(var oldVersion = Config.SettingsVersion; oldVersion < newVersion; ++oldVersion)
- MigrateSettings(oldVersion);
-
- Config.SettingsVersion = newVersion;
-
- Config.UpgradeRequired = false;
- Config.Save();
- }
- ServiceName = "TG Station Server";
- ActiveService = this;
- Run(this);
- }
- finally
- {
- Config.Save();
- }
- }
///
/// Overrides and saves the configured if requested by command line parameters
@@ -146,6 +134,10 @@ namespace TGServerService
/// Command line arguments for the
protected override void OnStart(string[] args)
{
+ Environment.CurrentDirectory = Directory.CreateDirectory(Path.GetTempPath() + "/TGStationServerService").FullName; //MOVE THIS POINTER BECAUSE ONE TIME I ALMOST ACCIDENTALLY NUKED MYSELF BY REFACTORING! http://imgur.com/zvGEpJD.png
+
+ SetupConfig();
+
ChangePortFromCommandLine(args);
SetupService();
@@ -155,13 +147,48 @@ namespace TGServerService
OnlineAllHosts();
}
+ ///
+ /// Writes some changes to the that always need to be done.
+ ///
+ void PrePrepConfig()
+ {
+ var Config = Properties.Settings.Default;
+
+ if (Config.InstancePaths == null)
+ Config.InstancePaths = new StringCollection();
+ }
+
+ ///
+ /// Upgrades up the service configuration
+ ///
+ void SetupConfig()
+ {
+ var Config = Properties.Settings.Default;
+ if (Config.UpgradeRequired)
+ {
+ var newVersion = Config.SettingsVersion;
+ Config.Upgrade();
+
+ PrePrepConfig();
+
+ for (var oldVersion = Config.SettingsVersion; oldVersion < newVersion; ++oldVersion)
+ MigrateSettings(oldVersion);
+
+ Config.SettingsVersion = newVersion;
+
+ Config.UpgradeRequired = false;
+ Config.Save();
+ }
+ }
+
///
/// Creates the for
///
void SetupService()
{
- serviceHost = CreateHost(this);
- AddEndpoint(serviceHost, typeof(ITGSService), Interface.MasterInterfaceName);
+ serviceHost = CreateHost(this, Interface.ServiceInterfaceName);
+ AddEndpoint(serviceHost, typeof(ITGSService));
+ AddEndpoint(serviceHost, typeof(ITGConnectivity));
serviceHost.Authorization.ServiceAuthorizationManager = new AdministrativeAuthorizationManager(); //only admins can diddle us
}
@@ -180,9 +207,9 @@ namespace TGServerService
///
/// The
/// The created
- static ServiceHost CreateHost(object singleton)
+ static ServiceHost CreateHost(object singleton, string endpointPostfix)
{
- return new ServiceHost(singleton, new Uri[] { new Uri("net.pipe://localhost"), new Uri(String.Format("https://localhost:{0}", Properties.Settings.Default.RemoteAccessPort)) })
+ return new ServiceHost(singleton, new Uri[] { new Uri(String.Format("net.pipe://localhost/{0}", endpointPostfix)), new Uri(String.Format("https://localhost:{0}/{1}", Properties.Settings.Default.RemoteAccessPort, endpointPostfix)) })
{
CloseTimeout = new TimeSpan(0, 0, 5)
};
@@ -237,19 +264,21 @@ namespace TGServerService
ServiceHost SetupInstance(string path)
{
ServerInstance instance;
+ string instanceName;
try
{
var config = InstanceConfig.Load(path);
if (hosts.ContainsKey(path))
{
var datInstance = ((ServerInstance)hosts[path].SingletonInstance);
- WriteEntry(String.Format("Unable to start instance at path {0}. Has the same name as instance at path {1} ({2}). Detaching...", path, datInstance.ServerDirectory(), datInstance.Config.Name), EventID.InstanceInitializationFailure, EventLogEntryType.Error, LoggingID);
+ WriteEntry(String.Format("Unable to start instance at path {0}. Has the same name as instance at path {1}. Detaching...", path, datInstance.ServerDirectory()), EventID.InstanceInitializationFailure, EventLogEntryType.Error, LoggingID);
return null;
}
if (!config.Enabled)
return null;
var ID = LockLoggingID();
WriteEntry(String.Format("Instance {0} ({1}) assigned logging ID {2}", config.Name, path, ID), EventID.InstanceIDAssigned, EventLogEntryType.Information, ID);
+ instanceName = config.Name;
instance = new ServerInstance(config, ID);
}
catch (Exception e)
@@ -258,12 +287,11 @@ namespace TGServerService
return null;
}
- var host = CreateHost(instance);
- hosts.Add(instance.Config.Name, host);
-
- var endpointPrefix = String.Format("{0}/{1}", Interface.MasterInterfaceName, instance.Config.Name);
+ var host = CreateHost(instance, String.Format("{0}/{1}", Interface.InstanceInterfaceName, instanceName));
+ hosts.Add(instanceName, host);
+
foreach (var J in Interface.ValidInterfaces)
- AddEndpoint(host, J, endpointPrefix);
+ AddEndpoint(host, J);
host.Authorization.ServiceAuthorizationManager = instance;
return host;
@@ -275,9 +303,9 @@ namespace TGServerService
/// The service host to add the component to
/// The type of the component
/// The URI prefix for accessing the
- void AddEndpoint(ServiceHost host, Type typetype, string PipePrefix)
+ void AddEndpoint(ServiceHost host, Type typetype)
{
- var bindingName = PipePrefix + "/" + typetype.Name;
+ var bindingName = typetype.Name;
host.AddServiceEndpoint(typetype, new NetNamedPipeBinding() { SendTimeout = new TimeSpan(0, 0, 30), MaxReceivedMessageSize = Interface.TransferLimitLocal }, bindingName);
var httpsBinding = new WSHttpBinding()
{
@@ -310,6 +338,8 @@ namespace TGServerService
{
WriteEntry(e.ToString(), EventID.ServiceShutdownFail, EventLogEntryType.Error, LoggingID);
}
+ Properties.Settings.Default.Save();
+ ActiveService = null;
}
///
diff --git a/TGServerService/TGServerService.csproj b/TGServerService/TGServerService.csproj
index ff077bacc3..4e5f0cd8c2 100644
--- a/TGServerService/TGServerService.csproj
+++ b/TGServerService/TGServerService.csproj
@@ -65,6 +65,7 @@
..\packages\System.Collections.Immutable.1.3.1\lib\portable-net45+win8+wp8+wpa81\System.Collections.Immutable.dll
+
diff --git a/TGServiceInterface/Components/Interop.cs b/TGServiceInterface/Components/Interop.cs
index 2a4af3bcfd..e57e74cb08 100644
--- a/TGServiceInterface/Components/Interop.cs
+++ b/TGServiceInterface/Components/Interop.cs
@@ -3,7 +3,7 @@
namespace TGServiceInterface.Components
{
///
- /// Used by DD to access the interop API with call()()
+ /// Used by DreamDaemon to access the interop API with call()(). Restrictions are in place so that only a DreamDaemon instance launched by the service can use this API
///
[ServiceContract]
public interface ITGInterop
diff --git a/TGServiceInterface/DreamDaemonBridge.cs b/TGServiceInterface/DreamDaemonBridge.cs
index a85ae6541b..2542a51f4a 100644
--- a/TGServiceInterface/DreamDaemonBridge.cs
+++ b/TGServiceInterface/DreamDaemonBridge.cs
@@ -1,5 +1,6 @@
using RGiesecke.DllExport;
using System;
+using System.Collections.Generic;
using System.Runtime.InteropServices;
using TGServiceInterface.Components;
@@ -21,13 +22,17 @@ namespace TGServiceInterface
{
try
{
- var channel = Interface.CreateChannel();
+ var parsedArgs = new List();
+ parsedArgs.AddRange(args);
+ Interface.InstanceName = parsedArgs[0];
+ parsedArgs.RemoveAt(0);
+ var channel = Interface.GetComponent();
try
{
- channel.CreateChannel().InteropMessage(String.Join(" ", args));
+ channel.InteropMessage(String.Join(" ", parsedArgs));
}
catch { }
- Interface.CloseChannel(channel);
+ Interface.CloseAllChannels();
}
catch { }
return 0;
diff --git a/TGServiceInterface/Interface.cs b/TGServiceInterface/Interface.cs
index 1cd73b4242..816e5f9576 100644
--- a/TGServiceInterface/Interface.cs
+++ b/TGServiceInterface/Interface.cs
@@ -29,13 +29,26 @@ namespace TGServiceInterface
///
/// The maximum message size to and from a remote server
///
- public const long TransferLimitRemote = 10485760; //10 MB
+ public const long TransferLimitRemote = 10485760; //10 MB
///
- /// Base name of the communication pipe
- /// they are formatted as MasterPipeName/ComponentName
+ /// Base name of communication URLs
///
- public const string MasterInterfaceName = "TGStationServerService";
+ const string MasterInterfaceName = "TGStationServerService";
+ ///
+ /// Base name of the root service URLs
+ ///
+ public const string ServiceInterfaceName = MasterInterfaceName + "/Service";
+ ///
+ /// Base name of instance URLs
+ ///
+ public const string InstanceInterfaceName = MasterInterfaceName + "/Instance";
+
+
+ ///
+ /// The name of the current instance in use
+ ///
+ public static string InstanceName;
///
/// If this is set, we will try and connect to an HTTPS server running at this address
@@ -68,11 +81,13 @@ namespace TGServiceInterface
/// A of s that can be used with the service
static IList CollectComponents()
{
- //find all interfaces in this assembly in this namespace that have the service contract attribute
+ var ServiceComponent = typeof(ITGSService); //this is special
+ //find all interfaces in this assembly in this namespace that have the service contract attribute
var query = from t in Assembly.GetExecutingAssembly().GetTypes()
- where t.IsInterface
- && t.Namespace == typeof(ITGSService).Namespace
+ where t.IsInterface
+ && t.Namespace == ServiceComponent.Namespace
&& t.GetCustomAttribute(typeof(ServiceContractAttribute)) != null
+ && t != ServiceComponent
select t;
return query.ToList();
}
@@ -115,13 +130,13 @@ namespace TGServiceInterface
{
HTTPSURL = null;
HTTPSPassword = null;
- ClearCachedChannels();
+ CloseAllChannels();
}
///
/// Closes all s stored in and clears it
///
- static void ClearCachedChannels()
+ public static void CloseAllChannels()
{
lock (ChannelFactoryCache)
{
@@ -138,7 +153,7 @@ namespace TGServiceInterface
/// if the interface being used to connect to a service does not have the same release version as the service
public static bool VersionMismatch(out string errorMessage)
{
- var splits = GetComponent().Version().Split(' ');
+ var splits = GetService().Version().Split(' ');
var theirs = new Version(splits[splits.Length - 1].Substring(1));
var ours = new Version(FileVersionInfo.GetVersionInfo(System.Reflection.Assembly.GetExecutingAssembly().Location).FileVersion);
if(theirs != ours)
@@ -163,14 +178,14 @@ namespace TGServiceInterface
HTTPSPort = port;
HTTPSUsername = username;
HTTPSPassword = password;
- ClearCachedChannels();
+ CloseAllChannels();
}
///
/// Safely shuts down a single
///
/// The to shutdown
- public static void CloseChannel(ChannelFactory cf)
+ static void CloseChannel(ChannelFactory cf)
{
try
{
@@ -183,12 +198,23 @@ namespace TGServiceInterface
}
///
- /// Returns the requested component . This does not guarantee a successful connection. s created this way are recycled for minimum latency and bandwidth usage
+ /// Returns the requested component for the instance . This does not guarantee a successful connection. s created this way are recycled for minimum latency and bandwidth usage
///
/// The component to retrieve
/// The correct component
public static T GetComponent()
{
+ var ToT = typeof(T);
+ if (!ValidInterfaces.Contains(ToT) && ToT != typeof(ITGSService))
+ throw new Exception("Invalid type!");
+ return GetComponentImpl(true);
+ }
+
+ static T GetComponentImpl(bool useInstanceName)
+ {
+ if (useInstanceName & InstanceName == null)
+ throw new Exception("Instance not selected!");
+
var tot = typeof(T);
ChannelFactory cf;
@@ -207,28 +233,36 @@ namespace TGServiceInterface
ChannelFactoryCache[tot].Abort();
ChannelFactoryCache.Remove(tot);
}
- cf = CreateChannel();
+ cf = CreateChannel(useInstanceName ? InstanceName : null);
ChannelFactoryCache[tot] = cf;
}
return cf.CreateChannel();
}
+ ///
+ /// Returns the component for the service
+ ///
+ /// The component for the service
+ public static ITGSService GetService()
+ {
+ return GetComponentImpl(false);
+ }
+
///
/// Directly creates a for without caching. This should be eventually closed by the caller
///
/// The component of the channel to be created
/// The correct
/// Thrown if isn't a valid component
- public static ChannelFactory CreateChannel()
+ static ChannelFactory CreateChannel(string instanceName)
{
- var ToT = typeof(T);
- if (!ValidInterfaces.Contains(ToT) && ToT != typeof(ITGSService))
- throw new Exception("Invalid type!");
+ var accessPath = instanceName == null ? ServiceInterfaceName : String.Format("{0}/{1}", InstanceInterfaceName, instanceName);
+
var InterfaceName = typeof(T).Name;
if (HTTPSURL == null)
{
var res2 = new ChannelFactory(
- new NetNamedPipeBinding { SendTimeout = new TimeSpan(0, 0, 30), MaxReceivedMessageSize = TransferLimitLocal }, new EndpointAddress(String.Format("net.pipe://localhost/{0}/{1}", MasterInterfaceName, InterfaceName))); //10 megs
+ new NetNamedPipeBinding { SendTimeout = new TimeSpan(0, 0, 30), MaxReceivedMessageSize = TransferLimitLocal }, new EndpointAddress(String.Format("net.pipe://localhost/{0}/{1}", accessPath, InterfaceName))); //10 megs
res2.Credentials.Windows.AllowedImpersonationLevel = TokenImpersonationLevel.Impersonation;
return res2;
}
@@ -243,7 +277,7 @@ namespace TGServiceInterface
binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.None;
binding.Security.Mode = requireAuth ? SecurityMode.TransportWithMessageCredential : SecurityMode.Transport; //do not require auth for a connectivity check
binding.Security.Message.ClientCredentialType = requireAuth ? MessageCredentialType.UserName : MessageCredentialType.None;
- var address = new EndpointAddress(String.Format("https://{0}:{1}/{2}/{3}", HTTPSURL, HTTPSPort, MasterInterfaceName, InterfaceName));
+ var address = new EndpointAddress(String.Format("https://{0}:{1}/{2}/{3}", HTTPSURL, HTTPSPort, accessPath, InterfaceName));
var res = new ChannelFactory(binding, address);
if (requireAuth)
{
@@ -261,7 +295,7 @@ namespace TGServiceInterface
{
try
{
- GetComponent().VerifyConnection();
+ GetComponentImpl(false).VerifyConnection();
return null;
}
catch (Exception e)
@@ -278,7 +312,7 @@ namespace TGServiceInterface
{
try
{
- GetComponent().Version();
+ GetService().Version();
return true;
}
catch
diff --git a/Version.cs b/Version.cs
index 87f3405212..0ec031ba90 100644
--- a/Version.cs
+++ b/Version.cs
@@ -12,6 +12,6 @@ using System.Reflection;
// [assembly: AssemblyVersion("1.0.*")]
//It's impossible to make these a define, don't say I didn't warn you
-[assembly: AssemblyVersion("3.1.5.1")]
-[assembly: AssemblyFileVersion("3.1.5.1")]
-[assembly: AssemblyInformationalVersion("3.1.5.1")]
+[assembly: AssemblyVersion("3.1.5.2")]
+[assembly: AssemblyFileVersion("3.1.5.2")]
+[assembly: AssemblyInformationalVersion("3.1.5.2")]