#region Arthea License /*********************************************************************** * Arthea MUD by R. Jennings (2007) http://arthea.googlecode.com/ * * By using this code you comply with the Artistic and GPLv2 Licenses. * ***********************************************************************/ #endregion using System; using System.Collections.Generic; using System.Collections.Specialized; using System.Diagnostics; using System.Net; using System.Net.Sockets; using System.Runtime.Remoting.Channels; #if !MONO using System.Runtime.Remoting.Channels.Tcp; #endif using System.Text; using System.Text.RegularExpressions; using System.Threading; using System.Xml.Serialization; using Arthea.Commands.Admin; using Arthea.Connections; using Arthea.Environment; using Arthea.Updates; namespace Arthea { /// <summary> /// The starting point /// </summary> public class Server { #region [rgn] Fields (10) private const int BacklogSize = 20; private static readonly Server instance = (!Persistance.XmlFileExists(Paths.ServerConfigFile)) ? new Server() : Persistance.Load<Server>(Paths.ServerConfigFile); private readonly string about; private readonly string version; private bool databaseLog; private string databaseName; private string databasePassword; private string databaseServer; private string databaseType; private string databaseUser; private bool databaseEnabled = true; private ServerState serverState; private Socket socket; #endregion [rgn] #region [rgn] Constructors (1) private Server() { version = string.Format("Arthea v{0}.{1}", System.Environment.Version.Major, System.Environment.Version.Minor); StringBuilder buf = new StringBuilder(); buf.AppendLine("by R. Jennings 2007"); buf.AppendLine("See http://arthea.googlecode.com/ for more information."); about = buf.ToString(); } #endregion [rgn] #region [rgn] Properties (8) /// <summary> /// Gets information about the server. /// </summary> public string About { get { return about; } } /// <summary> /// Gets or sets a value indicating whether [database enabled]. /// </summary> /// <value><c>true</c> if [database enabled]; otherwise, <c>false</c>.</value> [XmlIgnore] public bool DatabaseEnabled { get { return databaseEnabled; } set { databaseEnabled = value; } } /// <summary> /// Gets or sets the name of the database. /// </summary> /// <value>The name of the database.</value> public string DatabaseName { get { return databaseName; } set { databaseName = value; } } /// <summary> /// Gets or sets the database password. /// </summary> /// <value>The database password.</value> public string DatabasePassword { get { return databasePassword; } set { databasePassword = value; } } /// <summary> /// Gets or sets the database server. /// </summary> /// <value>The database server.</value> public string DatabaseServer { get { return databaseServer; } set { databaseServer = value; } } /// <summary> /// Gets or sets the type of the database. /// </summary> /// <value>The type of the database.</value> public string DatabaseType { get { return databaseType; } set { databaseType = value; } } /// <summary> /// Gets or sets the database user. /// </summary> /// <value>The database user.</value> public string DatabaseUser { get { return databaseUser; } set { databaseUser = value; } } /// <summary> /// Gets or sets a value indicating whether [database log]. /// </summary> /// <value><c>true</c> if [database log]; otherwise, <c>false</c>.</value> public bool DatabaseLog { get { return databaseLog; } set { databaseLog = value; } } /// <summary> /// Gets or sets the run level. /// </summary> /// <value>The run level.</value> [XmlIgnore] public ServerState State { get { return serverState; } set { serverState = value; switch (value) { case ServerState.Running: Log.Info("Arthea running on port {0}.", Globals.PortNumber); break; case ServerState.Stopping: Log.Info("Arthea is shutting down..."); break; default: Log.Info("Arthea is {0}...", value.ToString().ToLower()); break; } } } /// <summary> /// Gets the version of the server. /// </summary> public string Version { get { return version; } } /// <summary> /// Gets the instance. /// </summary> /// <value>The instance.</value> public static Server Instance { get { return instance; } } #endregion [rgn] #region [rgn] Methods (2) // [rgn] Public Methods (2) /// <summary> /// Shutdowns this instance. /// </summary> public void Shutdown() { if (socket != null) { socket.Close(); socket = null; } } /// <summary> /// Starts the server. /// </summary> /// <param name="rebooting">if set to <c>true</c> [rebooting].</param> public void Start(bool rebooting) { #if !MONO Dictionary<string, SocketInformation> recoveredConnections = null; if (rebooting) { TcpChannel channel = new TcpChannel(); ChannelServices.RegisterChannel(channel, false); RebootRecovery recovery = (RebootRecovery) Activator.GetObject(typeof (RebootRecovery), string.Format("tcp://localhost:{0}/RebootRecovery", RebootRecovery.Port)); if (recovery == null) { Log.Fatal("Reboot recovery failed."); return; } serverState = ServerState.Rebooting; try { recoveredConnections = recovery.TransferConnections(Process.GetCurrentProcess().Id); ChannelServices.UnregisterChannel(channel); // signal we are done to close the previous process recovery.Finished(); // wait a bit for sanity Thread.Sleep(1000); } catch (Exception ex) { Log.Bug(ex.Message); Log.Error(ex.StackTrace); Thread.Sleep(5000); return; } } #endif socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); try { socket.Bind(new IPEndPoint(IPAddress.Any, Globals.PortNumber)); } catch (Exception) { Log.Fatal("Port {0} is in use.", Globals.PortNumber); Thread.Sleep(5000); System.Environment.Exit(1); } socket.Listen(BacklogSize); State = ServerState.Booting; Lists.Continents.Load(); #if !MONO if (recoveredConnections != null) { Log.Info("Recovering {0} player{1}...", recoveredConnections.Count, recoveredConnections.Count == 1 ? "" : "s"); foreach (KeyValuePair<string, SocketInformation> entry in recoveredConnections) { try { Connection connection = new Connection(entry.Key, entry.Value); connection.Attach(); } catch { Log.Error("Failed to load {0}.", entry.Key); } } } #endif Lists.Areas.Update(); State = ServerState.Running; try { while (State == ServerState.Running) { if (socket.Poll(1, SelectMode.SelectRead)) { new Connection(socket.Accept()); } Lists.Connections.ReadInput(); //Update world UpdateManager.Update(); Lists.Connections.Update(); Lists.Connections.ProcessOutput(); Thread.Sleep(1); } } catch (ThreadAbortException ex) { Log.Info(ex.ExceptionState); } finally { Cleanup(); Shutdown(); } } // [rgn] Private Methods (1) /// <summary> /// Cleanups this instance. /// </summary> private void Cleanup() { Globals.Save(); Persistance.Save(Paths.ServerConfigFile, this); } #endregion [rgn] } /// <summary> /// Arguments class /// </summary> public class Arguments { #region [rgn] Fields (1) // Variables private readonly StringDictionary Parameters; #endregion [rgn] #region [rgn] Constructors (1) /// <summary> /// Initializes a new instance of the <see cref="Arguments"/> class. /// </summary> /// <param name="Args">The args.</param> public Arguments(IEnumerable<string> Args) { Parameters = new StringDictionary(); Regex Spliter = new Regex(@"^-{1,2}|^/|=|:", RegexOptions.IgnoreCase | RegexOptions.Compiled); Regex Remover = new Regex(@"^['""]?(.*?)['""]?$", RegexOptions.IgnoreCase | RegexOptions.Compiled); string Parameter = null; string[] Parts; // Valid parameters forms: // {-,/,--}param{ ,=,:}((",')value(",')) // Examples: // -param1 value1 --param2 /param3:"Test-:-work" // /param4=happy -param5 '--=nice=--' foreach (string Txt in Args) { // Look for new parameters (-,/ or --) and a // possible enclosed value (=,:) Parts = Spliter.Split(Txt, 3); switch (Parts.Length) { // Found a value (for the last parameter // found (space separator)) case 1: if (Parameter != null) { if (!Parameters.ContainsKey(Parameter)) { Parts[0] = Remover.Replace(Parts[0], "$1"); Parameters.Add(Parameter, Parts[0]); } Parameter = null; } // else Error: no parameter waiting for a value (skipped) break; // Found just a parameter case 2: // The last parameter is still waiting. // With no value, set it to true. if (Parameter != null) { if (!Parameters.ContainsKey(Parameter)) Parameters.Add(Parameter, "true"); } Parameter = Parts[1]; break; // Parameter with enclosed value case 3: // The last parameter is still waiting. // With no value, set it to true. if (Parameter != null) { if (!Parameters.ContainsKey(Parameter)) Parameters.Add(Parameter, "true"); } Parameter = Parts[1]; // Remove possible enclosing characters (",') if (!Parameters.ContainsKey(Parameter)) { Parts[2] = Remover.Replace(Parts[2], "$1"); Parameters.Add(Parameter, Parts[2]); } Parameter = null; break; } } // In case a parameter is still waiting if (Parameter != null) { if (!Parameters.ContainsKey(Parameter)) Parameters.Add(Parameter, "true"); } } #endregion [rgn] #region [rgn] Properties (1) /// <summary> /// Gets the <see cref="System.String"/> with the specified param. /// </summary> /// <value>the parameter value</value> public string this[string Param] { get { return (Parameters[Param]); } } #endregion [rgn] } }