diff --git a/server/CircleScape.csproj b/server/CircleScape.csproj index 6176eaa..49f97d4 100644 --- a/server/CircleScape.csproj +++ b/server/CircleScape.csproj @@ -71,6 +71,7 @@ + @@ -140,7 +141,8 @@ - XCOPY "$(ProjectDir)Assets" "$(TargetDir)" /Y + XCOPY "$(ProjectDir)Assets" "$(TargetDir)" /Y /E +COPY "$(ProjectDir)config.ini" "$(TargetDir)" /Y diff --git a/server/Configuration.cs b/server/Configuration.cs new file mode 100644 index 0000000..75a4c22 --- /dev/null +++ b/server/Configuration.cs @@ -0,0 +1,56 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Square.INI; + +namespace CircleScape { + public static class Configuration { + private static SettingsFile Settings; + + static Configuration() { + Settings = new SettingsFile( + "config.ini", + new List { + new SectionRules { + Name = "General", + Required = true, + RequiredFields = new string[] { + "Run Master", + "Master Port", + "Master Addr", + "Max Users" + } + }, + + new SectionRules { + Name = "Server", + AllowMultiple = true, + Required = true, + RequiredFields = new string[] { + "Id", + "Port" + } + } + } + ); + } + + public static Section Get(string section) { + return Settings[section]; + } + + public static Section General { + get { + return Settings["General"]; + } + } + + public static Section Servers { + get { + return Settings["Server"]; + } + } + } +} diff --git a/server/Entrypoint.cs b/server/Entrypoint.cs index 57ccdfc..6cf5440 100644 --- a/server/Entrypoint.cs +++ b/server/Entrypoint.cs @@ -11,6 +11,7 @@ using System.Net; namespace CircleScape { class Entrypoint { static void Main(string[] args) { + foreach() var server = new Kneesocks.Server(6770, PoolManager.Pending); server.Start(); diff --git a/server/Libraries/Kneesocks/Connection.cs b/server/Libraries/Kneesocks/Connection.cs index bf9df61..cfd849d 100644 --- a/server/Libraries/Kneesocks/Connection.cs +++ b/server/Libraries/Kneesocks/Connection.cs @@ -31,6 +31,7 @@ namespace Kneesocks { private TcpClient Socket = null; private NetworkStream Stream = null; + public Server Server { get; internal set; } ReadBuffer Buffer; private byte[] FirstTwoBytes = null; @@ -93,6 +94,7 @@ namespace Kneesocks { Socket = conn.Socket; Stream = conn.Stream; + Server = conn.Server; Buffer = conn.Buffer; FirstTwoBytes = conn.FirstTwoBytes; diff --git a/server/Libraries/Kneesocks/Server.cs b/server/Libraries/Kneesocks/Server.cs index 0c929c6..0cfa808 100644 --- a/server/Libraries/Kneesocks/Server.cs +++ b/server/Libraries/Kneesocks/Server.cs @@ -7,19 +7,12 @@ using System.Net.Sockets; using System.Net; namespace Kneesocks { - public class Server where T : Connection, new() { - private TcpListener Socket; - private Thread Listener = null; - private Pool ConnectionPool = null; - public bool Started { get; private set; } = false; - public UInt16 Port { get; private set; } - - public Server(UInt16 port, Pool pool) { - Port = port; - Socket = new TcpListener(IPAddress.Any, port); - Listener = new Thread(new ThreadStart(ListenThread)); - ConnectionPool = pool; - } + public abstract class Server { + protected TcpListener Socket; + protected Thread Listener = null; + public bool Started { get; protected set; } = false; + public UInt16 Port { get; protected set; } + public object Configuration { get; protected set; } public void Start() { if(!Started) { @@ -34,13 +27,26 @@ namespace Kneesocks { Listener.Join(); } } + } - private void ListenThread() { + public class Server : Server where T : Connection, new() { + protected Pool ConnectionPool = null; + + public Server(UInt16 port, Pool pool, object config = null) { + Port = port; + Socket = new TcpListener(IPAddress.Any, port); + Listener = new Thread(new ThreadStart(ListenThread)); + ConnectionPool = pool; + Configuration = config; + } + + protected void ListenThread() { Socket.Start(); while(Started) { if(Socket.Pending()) { var templatedConnection = new T(); + templatedConnection.Server = this; templatedConnection.Initialize(Socket.AcceptTcpClient()); ConnectionPool.AddConnection(templatedConnection); } diff --git a/server/Libraries/Square/INI/Instance.cs b/server/Libraries/Square/INI/Instance.cs index ebe0e8b..376a94b 100644 --- a/server/Libraries/Square/INI/Instance.cs +++ b/server/Libraries/Square/INI/Instance.cs @@ -1,13 +1,43 @@ using System; +using System.Collections; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Square.INI { - class Instance { - private Dictionary Data { get; set; } + public class Instance : IEnumerable> { + private Dictionary Data + = new Dictionary(StringComparer.OrdinalIgnoreCase); + internal Instance() { } + internal void Push(string line) { + if(line.Contains('=')) { + var parts = line.Split(new char[] { '=' }, 2); + Data.Add(parts[0].Trim(), new Value(parts[1].Trim())); + } else + throw new FormatException("Line is not a key-value pair delimited by an equals sign."); + } + + public Value this[string key] { + get { + if(Data.ContainsKey(key)) + return Data[key]; + else return null; + } + } + + public bool ContainsKey(string key) { + return Data.ContainsKey(key); + } + + IEnumerator IEnumerable.GetEnumerator() { + return Data.GetEnumerator(); + } + + IEnumerator> IEnumerable>.GetEnumerator() { + return Data.GetEnumerator(); + } } } diff --git a/server/Libraries/Square/INI/Section.cs b/server/Libraries/Square/INI/Section.cs index aabdd7f..4b69259 100644 --- a/server/Libraries/Square/INI/Section.cs +++ b/server/Libraries/Square/INI/Section.cs @@ -6,10 +6,39 @@ using System.Text; using System.Threading.Tasks; namespace Square.INI { - public class Section : IEnumerable { + public class Section : IEnumerable { private List Instances; - public IEnumerator GetEnumerator() { + internal Section() { } + + internal Instance Push() { + Instances.Add(new Instance()); + return Instances[Instances.Count - 1]; + } + + public string this[string key] { + get { + return Instances[0][key]; + } + } + + public Instance this[int i] { + get { + return Instances[i]; + } + } + + public int Count { + get { + return Instances.Count; + } + } + + IEnumerator IEnumerable.GetEnumerator() { + return Instances.GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() { return Instances.GetEnumerator(); } } diff --git a/server/Libraries/Square/INI/SectionRules.cs b/server/Libraries/Square/INI/SectionRules.cs index 35ac38b..1326154 100644 --- a/server/Libraries/Square/INI/SectionRules.cs +++ b/server/Libraries/Square/INI/SectionRules.cs @@ -6,6 +6,9 @@ using System.Threading.Tasks; namespace Square.INI { public class SectionRules { - + public string Name { get; set; } + public bool Required { get; set; } = true; + public bool AllowMultiple { get; set; } = false; + public string[] RequiredFields { get; set; } = new string[0]; } } diff --git a/server/Libraries/Square/INI/SettingsFile.cs b/server/Libraries/Square/INI/SettingsFile.cs index 62b1e54..a595ea3 100644 --- a/server/Libraries/Square/INI/SettingsFile.cs +++ b/server/Libraries/Square/INI/SettingsFile.cs @@ -13,14 +13,47 @@ namespace Square.INI { public SettingsFile(string path) { var lines = File.ReadAllLines(path); - string currentSection = null; - foreach(var line in lines) { + Instance currentInstance = null; + foreach(var rawLine in lines) { + var line = rawLine.Trim(); + if(line.StartsWith("!", "#", ";")) + continue; + if(line.StartsWith("[") && line.EndsWith("]")) { + var section = line.Substring(1, line.Length - 2); + if(!Sections.ContainsKey(section)) + Sections.Add(section, new Section()); + + currentInstance = Sections[section].Push(); + } else { + if(currentInstance != null) + currentInstance.Push(line); + else + throw new FormatException("Non-section line before any define sections in '"+ path +"'"); + } } } - public SettingsFile(string path, List rules) { + public SettingsFile(string path, List rules) : this(path) { + foreach(var rule in rules) { + var name = rule.Name; + if(ContainsSection(name)) { + var section = Sections[name]; + if(!rule.AllowMultiple && section.Count > 1) + throw new FormatException("Section '"+ name +"' is not allowed to have multiple declarations in '"+ path +"'"); + + if(rule.RequiredFields.Length > 0) { + foreach(var instance in section) { + foreach(var field in rule.RequiredFields) { + if(instance.ContainsKey(field)) + throw new FormatException("Expected field '"+ field +"' in section '" + name + "' was not found in '" + path + "'"); + } + } + } + } else if(rule.Required) + throw new FormatException("Expected section '"+ name +"' was not found in '"+ path +"'"); + } } public Section this[string section] { @@ -30,5 +63,9 @@ namespace Square.INI { else return null; } } + + public bool ContainsSection(string section) { + return Sections.ContainsKey(section); + } } } diff --git a/server/Libraries/Square/INI/Value.cs b/server/Libraries/Square/INI/Value.cs new file mode 100644 index 0000000..3f30d6e --- /dev/null +++ b/server/Libraries/Square/INI/Value.cs @@ -0,0 +1,37 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Square.INI { + public class Value { + private string Raw; + + public Value(string raw) { + Raw = raw; + } + + public static implicit operator string(Value value) { + return value.Raw; + } + + public static implicit operator bool(Value value) { + return Boolean.TryParse(value.Raw, out bool retval) + ? retval + : false; + } + + public static implicit operator int(Value value) { + return Int32.TryParse(value.Raw, out int retval) + ? retval + : 0; + } + + public static implicit operator double(Value value) { + return Double.TryParse(value.Raw, out double retval) + ? retval + : 0; + } + } +} diff --git a/server/Libraries/Square/Square.csproj b/server/Libraries/Square/Square.csproj index 8c25c5a..2cea401 100644 --- a/server/Libraries/Square/Square.csproj +++ b/server/Libraries/Square/Square.csproj @@ -48,6 +48,7 @@ + diff --git a/server/Libraries/Square/StringExtensions.cs b/server/Libraries/Square/StringExtensions.cs index 2636c39..e7f570b 100644 --- a/server/Libraries/Square/StringExtensions.cs +++ b/server/Libraries/Square/StringExtensions.cs @@ -29,5 +29,14 @@ namespace Square { public static byte[] Base64DecodeRaw(this string str) => Convert.FromBase64String(str); + + public static bool StartsWith(this string str, params string[] strings) { + foreach(var checkString in strings) { + if(str.StartsWith(checkString)) + return true; + } + + return false; + } } } diff --git a/server/config.ini b/server/config.ini new file mode 100644 index 0000000..0c615ed --- /dev/null +++ b/server/config.ini @@ -0,0 +1,25 @@ +[General] +; determines if this server instance should run the master server + Run Master = false + +; address and port of the master server +;; if master server is in this instance, addr should be localhost +;; and port determines what port the master server runs on +Master Addr = localhost +Master Port = 16670 + +; this value used if the max users isn't specified in a server instance + Max Users = 100 + +[Server] + Id = 1 + Port = 6770 +Max Users = 300 + +[Server] + Id = 2 + Port = 6780 + +[Server] + Id = 3 + Port = 6790 \ No newline at end of file