using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Globalization; using System.IO; using System.Threading; using GameServiceWarden.ModuleAPI; namespace GameServiceWarden.Core.Games { public class GameServiceInfo : IDisposable //entity { /// /// The name of the service itself, independent of the name of the module this service is using. /// public string ServiceName { get { return serviceName; } set { Interlocked.Exchange(ref serviceName, value); } } /// /// The services console output stream. /// private volatile string serviceName; //thread-safe(?) public Stream ServiceConsoleStream { get; private set; } // Thread safe. private readonly object controlLock = new object(); private volatile ServiceState state; private readonly IGameService service; private readonly string assemblyName; /// /// Name of module this service uses. /// public string ModuleName { get; private set; } private readonly IReadOnlyDictionary configurables; private bool disposed; public GameServiceInfo(IGameService service, string moduleName, string assemblyName) { this.service = service ?? throw new ArgumentNullException("service"); this.ModuleName = moduleName ?? throw new ArgumentNullException("moduleName"); this.assemblyName = assemblyName ?? throw new ArgumentNullException("assemblyName"); this.service.StateChangeEvent += OnServiceStateChange; Dictionary tempConfigurables = new Dictionary(); foreach (IGameConfigurable configurable in service.Configurables) { tempConfigurables.Add(configurable.OptionName, configurable); } this.configurables = new ReadOnlyDictionary(tempConfigurables); } /// /// Starts this service. /// /// Is thrown when the service is already running. public void Start() { lock (controlLock) { if (state != ServiceState.Stopped) throw new InvalidOperationException("Service instance already running."); DateTimeFormatInfo format = new DateTimeFormatInfo(); format.TimeSeparator = "-"; format.DateSeparator = "_"; ServiceConsoleStream = new MemoryStream(8 * 1024 * 1024); // 8 MB ServiceConsoleStream = Stream.Synchronized(ServiceConsoleStream); service.InitializeService(new StreamWriter(ServiceConsoleStream)); } } /// /// Stops the service. /// /// Is thrown when the is not running. public void Stop() { lock (controlLock) { if (state != ServiceState.Running) throw new InvalidOperationException("Service instance not running."); this.service.ElegantShutdown(); ServiceConsoleStream.Close(); ServiceConsoleStream = null; } } /// /// Sends a command to this service to execute. /// /// The command to execute. /// Is thrown when the service is not running. public void ExecuteCommand(string command) { lock (controlLock) { if (state != ServiceState.Running) throw new InvalidOperationException("Service instance not running."); service.ExecuteCommand(command); } } /// /// Gets the possible 's names for this service. /// /// A returned where the string is the option's name. public ISet GetConfigurableOptions() { return new HashSet(this.configurables.Keys); } public bool SetConfigurableValue(string configurationName, string value) { if (!this.configurables.ContainsKey(configurationName)) throw new KeyNotFoundException($"Unable to find option with name \"{configurationName}\"."); return this.configurables[configurationName].SetValue(value); } public string GetConfigurableValue(string configurationName) { if (!this.configurables.ContainsKey(configurationName)) throw new KeyNotFoundException($"Unable to find option with name \"{configurationName}\"."); return this.configurables[configurationName].GetValue(); } /// The that this service is currently in. public ServiceState GetServiceState() { lock (controlLock) { return state; } } /// The name of assembly this module is contained in. public string GetAssemblyName() { return assemblyName; } private void OnServiceStateChange(object sender, ServiceState current) { lock (controlLock) { this.state = current; } } protected virtual void Dispose(bool disposing) { if (!disposed) { if (disposing) { ServiceConsoleStream?.Dispose(); } //No unmanaged code, therefore, no finalizer. disposed = true; } } public void Dispose() { Dispose(disposing: true); GC.SuppressFinalize(this); } } }