From 8d65b707e65da796332025bf4fe9f8020f27db5f Mon Sep 17 00:00:00 2001 From: driAn Date: Sat, 27 Oct 2007 18:22:20 +0000 Subject: [PATCH] masterserver git-svn-id: svn://svn.code.sf.net/p/actiongame/code/trunk/ac@1402 e016b1c5-ed68-44aa-a2f7-31cac88cffde --- source/CubeMS/Admin/Default.aspx | 65 +++++++ source/CubeMS/Admin/Default.aspx.cs | 18 ++ source/CubeMS/App_Code/CubeMS.cs | 147 ++++++++++++++++ .../CubeMS/App_Code/CubeMsRequestHandler.cs | 158 ++++++++++++++++++ source/CubeMS/App_Code/IPTools.cs | 30 ++++ source/CubeMS/App_Code/Log.cs | 41 +++++ source/CubeMS/App_Themes/Pro/StyleSheet.css | 9 + source/CubeMS/Bin/API.dll | Bin 0 -> 20480 bytes source/CubeMS/Bin/ActionCube.dll | Bin 0 -> 24576 bytes source/CubeMS/Bin/Cube.dll | Bin 0 -> 20480 bytes source/CubeMS/Bin/CubeEngineBase.dll | Bin 0 -> 24576 bytes source/CubeMS/Default.aspx | 52 ++++++ source/CubeMS/Default.aspx.cs | 17 ++ source/CubeMS/README.txt | 42 +++++ source/CubeMS/web.config | 53 ++++++ 15 files changed, 632 insertions(+) create mode 100644 source/CubeMS/Admin/Default.aspx create mode 100644 source/CubeMS/Admin/Default.aspx.cs create mode 100644 source/CubeMS/App_Code/CubeMS.cs create mode 100644 source/CubeMS/App_Code/CubeMsRequestHandler.cs create mode 100644 source/CubeMS/App_Code/IPTools.cs create mode 100644 source/CubeMS/App_Code/Log.cs create mode 100644 source/CubeMS/App_Themes/Pro/StyleSheet.css create mode 100644 source/CubeMS/Bin/API.dll create mode 100644 source/CubeMS/Bin/ActionCube.dll create mode 100644 source/CubeMS/Bin/Cube.dll create mode 100644 source/CubeMS/Bin/CubeEngineBase.dll create mode 100644 source/CubeMS/Default.aspx create mode 100644 source/CubeMS/Default.aspx.cs create mode 100644 source/CubeMS/README.txt create mode 100644 source/CubeMS/web.config diff --git a/source/CubeMS/Admin/Default.aspx b/source/CubeMS/Admin/Default.aspx new file mode 100644 index 00000000..533446e0 --- /dev/null +++ b/source/CubeMS/Admin/Default.aspx @@ -0,0 +1,65 @@ +<%@ Page Language="C#" AutoEventWireup="true" CodeFile="Default.aspx.cs" Inherits="Admin_Admin" %> + + + + + + AssaultCube Admin + + +
+
+

AssaultCube Admin

+ +

Server List

+ + + + + + + + + + + + + + + + + + + +

Log

+ + + + + + + + + + + + + + + +   + +
+ Logged in as: + +
+ + +
+
+ + diff --git a/source/CubeMS/Admin/Default.aspx.cs b/source/CubeMS/Admin/Default.aspx.cs new file mode 100644 index 00000000..f085c6f1 --- /dev/null +++ b/source/CubeMS/Admin/Default.aspx.cs @@ -0,0 +1,18 @@ +using System; +using System.Data; +using System.Configuration; +using System.Collections; +using System.Web; +using System.Web.Security; +using System.Web.UI; +using System.Web.UI.WebControls; +using System.Web.UI.WebControls.WebParts; +using System.Web.UI.HtmlControls; + +public partial class Admin_Admin : System.Web.UI.Page +{ + protected void Page_Load(object sender, EventArgs e) + { + + } +} diff --git a/source/CubeMS/App_Code/CubeMS.cs b/source/CubeMS/App_Code/CubeMS.cs new file mode 100644 index 00000000..ef1c45cf --- /dev/null +++ b/source/CubeMS/App_Code/CubeMS.cs @@ -0,0 +1,147 @@ +using System; +using System.Data; +using System.Configuration; +using System.Web; +using System.Web.Security; +using System.Web.UI; +using System.Web.UI.WebControls; +using System.Web.UI.WebControls.WebParts; +using System.Web.UI.HtmlControls; + +using System.Net; +using System.Xml; +using System.IO; +using System.Data.SqlClient; +using System.Text; + +using AHSoftware.CubeMon; +using AHSoftware.CubeMon.Games.ActionCube.v094; + +/// +/// Summary description for CubeMS +/// +public class CubeMS +{ + string connectionString; + + public string ConnectionString + { + get { return connectionString; } + set { connectionString = value; } + } + + private bool AddServer(IPEndPoint address, string name, string description) + { + SqlConnection con = new SqlConnection(connectionString); + SqlCommand cmd = new SqlCommand("AddServer", con); + cmd.CommandType = CommandType.StoredProcedure; + cmd.Parameters.AddWithValue("IP", IPTools.IPToInt(address.Address)); + cmd.Parameters.AddWithValue("Port", address.Port); + cmd.Parameters.AddWithValue("Name", name); + cmd.Parameters.AddWithValue("Description", description); + try + { + con.Open(); + cmd.ExecuteNonQuery(); + return true; + } + catch + { + return false; + } + finally + { + con.Close(); + } + } + + public bool Register(IPEndPoint address) + { + Client c = new Client(); + c.Socket = new IPEndPoint(address.Address, address.Port+1); + ServerInfo info = null; + try + { + info = (ServerInfo) c.RequestInfo(); + } + catch + { + return false; + } + + string name = string.Empty; + try + { + name = Dns.GetHostEntry(info.Socket.Address).HostName; + } + catch { }; + + return AddServer(address, name, info.Description); + } + + public string GetServers(bool xml) + { + SqlConnection con = new SqlConnection(connectionString); + SqlCommand cmd = new SqlCommand(xml ? "GetServersXML" : "GetServers", con); + cmd.CommandType = CommandType.StoredProcedure; + try + { + con.Open(); + SqlDataReader r = cmd.ExecuteReader(); + StringBuilder sb = new StringBuilder(); + + if(xml) + { + // sql server handles xml conversion for us, just embed in a new xmldoc + sb.AppendLine(""); + sb.AppendLine(""); + if(r.Read()) + { + sb.AppendLine(r[0] as string); + } + sb.AppendLine(""); + } + else + { + // construct server list as cubescript + while(r.Read()) + { + sb.AppendFormat("addserver {0} {1};{2}", IPTools.IntToIp((int)r["IP"]).ToString(), r["Port"], Environment.NewLine); + } + } + + return sb.ToString(); + } + catch + { + return null; + } + finally + { + con.Close(); + } + } + + public string GetClientScript(string HTTPAgent) + { + SqlConnection con = new SqlConnection(connectionString); + SqlCommand cmd = new SqlCommand("GetClientScript", con); + cmd.CommandType = CommandType.StoredProcedure; + cmd.Parameters.AddWithValue("HTTPAgent", HTTPAgent); + try + { + con.Open(); + SqlDataReader r = cmd.ExecuteReader(); + r.Read(); + return r["Script"] as string; + } + catch + { + return null; + } + finally + { + con.Close(); + } + } +} diff --git a/source/CubeMS/App_Code/CubeMsRequestHandler.cs b/source/CubeMS/App_Code/CubeMsRequestHandler.cs new file mode 100644 index 00000000..f8cf8c6a --- /dev/null +++ b/source/CubeMS/App_Code/CubeMsRequestHandler.cs @@ -0,0 +1,158 @@ +using System; +using System.Data; +using System.Configuration; +using System.Web; +using System.Web.Security; +using System.Web.UI; +using System.Web.UI.WebControls; +using System.Web.UI.WebControls.WebParts; +using System.Web.UI.HtmlControls; + +using System.Net; +using System.Xml; +using System.IO; +using System.Web.Configuration; +using System.Web.Caching; + +using AHSoftware.CubeMon; +using AHSoftware.CubeMon.Games.ActionCube.v094; + +/// +/// Summary description for RequestHandler +/// +public class CubeMsRequestHandler : IHttpHandler +{ + Log log = new Log(); + CubeMS master; + int serverListCacheMinutes; + + CubeMsRequestHandler() + { + master = new CubeMS(); + master.ConnectionString = WebConfigurationManager.ConnectionStrings["CubeMS"].ConnectionString; + serverListCacheMinutes = int.Parse(WebConfigurationManager.AppSettings["ServerListCacheMinutes"] ?? "0"); + } + + #region IHttpHandler Members + + public bool IsReusable + { + get { return false; } + } + + public void ProcessRequest(HttpContext context) + { + IPEndPoint address = new IPEndPoint(IPAddress.Parse(context.Request.UserHostAddress), 0); + string requestFile = Path.GetFileName(context.Request.FilePath); + + try + { + if(requestFile == "register.do" && context.Request.QueryString["action"] == "add") + { + context.Response.ContentType = "text/plain"; + + try + { + address.Port = int.Parse(context.Request.QueryString["port"]); + } + catch + { + context.Response.Write("Server not registered, invalid request. Maybe you use an older server version?"); + log.AddEvent(Log.EventType.RegistrationFailed, "invalid request", address); + return; + } + + if(master.Register(address)) + { + // TODO: *DOS safety + context.Response.Write("Registration successful. Due to caching it might take a few minutes to see the your server in the serverlist"); + log.AddEvent(Log.EventType.RegistrationSuceeeded, null, address); + } + else + { + context.Response.Write("Server not registered, could not ping you. Make sure your server is accessible from the internet."); + log.AddEvent(Log.EventType.RegistrationFailed, "ping", address); + } + } + else if(requestFile == "retrieve.do") + { + string type = context.Request.QueryString["item"]; + if(type == "list") + { + context.Response.ContentType = "text/plain"; + + try + { + + // send server list + string servers = context.Cache[type] as string; + if(servers == null || servers.Length == 0) // empty cache, or empty server list + { + servers = master.GetServers(false); + if(serverListCacheMinutes > 0) context.Cache.Add(type, servers, null, Cache.NoAbsoluteExpiration, new TimeSpan(0, serverListCacheMinutes, 0), CacheItemPriority.Normal, null); + } + + context.Response.Write(servers); + context.Response.Flush(); + log.AddEvent(Log.EventType.RetrieveServers, "list", address); + + // append client script if its an older or unknown client + if(bool.Parse((WebConfigurationManager.AppSettings["EnableClientScripts"] ?? "false")) && context.Request.UserAgent != WebConfigurationManager.AppSettings["ClientAgent"]) + { + string script = context.Cache["script"] as string; + if(script == null) + { + if((script = master.GetClientScript(context.Request.UserAgent)) != null) + { + int cacheminutes = int.Parse(WebConfigurationManager.AppSettings["ClientScriptCacheMinutes"] ?? "0"); + context.Cache.Add("script", script, null, Cache.NoAbsoluteExpiration, new TimeSpan(0, cacheminutes, 0), CacheItemPriority.Normal, null); + } + } + + if(script != null) + { + context.Response.Write(script); + context.Response.Flush(); + log.AddEvent(Log.EventType.RetrieveServers, "script", address); + } + } + } + catch + { + // send error to the client + context.Response.Write("echo [An error ocurred on the masterserver]"); + context.Response.Flush(); + throw; // log errors globally + } + } + else if(context.Request.QueryString["item"] == "xml") + { + context.Response.ContentType = "text/xml"; + + string serversXml = context.Cache[type] as string; + if(serversXml == null || serversXml.Length == 0) // empty cache, or empty server list + { + serversXml = master.GetServers(true); + if(serverListCacheMinutes > 0) context.Cache.Add(type, serversXml, null, Cache.NoAbsoluteExpiration, new TimeSpan(0, serverListCacheMinutes, 0), CacheItemPriority.Normal, null); + } + + context.Response.Write(serversXml); + context.Response.Flush(); + + log.AddEvent(Log.EventType.RetrieveServers, "xml", address); + } + } + } + catch(Exception ex) + { + try + { + // TODO: log all uncaught exceptions + // log.AddEvent(Log.EventType.Exception, ex.Message, address); + } + catch { } + } + } + + #endregion +} diff --git a/source/CubeMS/App_Code/IPTools.cs b/source/CubeMS/App_Code/IPTools.cs new file mode 100644 index 00000000..9028a907 --- /dev/null +++ b/source/CubeMS/App_Code/IPTools.cs @@ -0,0 +1,30 @@ +using System; +using System.Data; +using System.Configuration; +using System.Web; +using System.Web.Security; +using System.Web.UI; +using System.Web.UI.WebControls; +using System.Web.UI.WebControls.WebParts; +using System.Web.UI.HtmlControls; + +using System.Net; + +/// +/// Summary description for IPTools +/// +public static class IPTools +{ + public static int IPToInt(IPAddress address) + { + byte[] ip = address.GetAddressBytes(); + if(ip.Length == 4) return (int)(ip[0] | (ip[1] << 8) | (ip[2] << 16) | (ip[3] << 24)); + else return 0; + } + + public static IPAddress IntToIp(int address) + { + byte[] octets = { (byte)(address & 0xFF), (byte)((address >> 8) & 0xFF), (byte)((address >> 16) & 0xFF), (byte)((address >> 24) & 0xFF) }; + return new IPAddress(octets); + } +} diff --git a/source/CubeMS/App_Code/Log.cs b/source/CubeMS/App_Code/Log.cs new file mode 100644 index 00000000..325a0475 --- /dev/null +++ b/source/CubeMS/App_Code/Log.cs @@ -0,0 +1,41 @@ +using System; +using System.Data; +using System.Configuration; +using System.Web; +using System.Web.Security; +using System.Web.UI; +using System.Web.UI.WebControls; +using System.Web.UI.WebControls.WebParts; +using System.Web.UI.HtmlControls; + +using System.IO; +using System.Xml; +using System.Net; +using System.Data.SqlClient; + +/// +/// Summary description for Log +/// +public class Log +{ + public enum EventType : int + { + RegistrationFailed = 0, + RegistrationSuceeeded, + RetrieveServers, + Exception, + } + + public void AddEvent(EventType type, string data, IPEndPoint address) + { + SqlConnection con = new SqlConnection(ConfigurationManager.ConnectionStrings["CubeMS"].ConnectionString as string); + SqlCommand cmd = new SqlCommand("AddEvent", con); + cmd.CommandType = CommandType.StoredProcedure; + cmd.Parameters.AddWithValue("Type", type); + cmd.Parameters.AddWithValue("Data", data); + cmd.Parameters.AddWithValue("IP", IPTools.IPToInt(address.Address)); + con.Open(); + cmd.ExecuteNonQuery(); + con.Close(); + } +} diff --git a/source/CubeMS/App_Themes/Pro/StyleSheet.css b/source/CubeMS/App_Themes/Pro/StyleSheet.css new file mode 100644 index 00000000..0aa7b503 --- /dev/null +++ b/source/CubeMS/App_Themes/Pro/StyleSheet.css @@ -0,0 +1,9 @@ +body +{ + Font-family: "Bitstream Vera Serif", Cambria, Baskerville, Palatino, Georgia, Times, "Times New Roman", serif.; + font-size: 12px; +} + +h1 +{ +} diff --git a/source/CubeMS/Bin/API.dll b/source/CubeMS/Bin/API.dll new file mode 100644 index 0000000000000000000000000000000000000000..6a88f7f44ded2fd4ad9e7edaf6ef795fb1b3d0ad GIT binary patch literal 20480 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 + + + + + + AssaultCube Master Server + + +
+
+

AssaultCube Master Server

+

Server List

+   + + + + + + + + + + + + + + + + + + +

Interfaces

+

+ You can retrieve the server list data using those url's: +

+ + + +

Admin

+ +
+ +
+
+ + diff --git a/source/CubeMS/Default.aspx.cs b/source/CubeMS/Default.aspx.cs new file mode 100644 index 00000000..42c253c5 --- /dev/null +++ b/source/CubeMS/Default.aspx.cs @@ -0,0 +1,17 @@ +using System; +using System.Data; +using System.Configuration; +using System.Web; +using System.Web.Security; +using System.Web.UI; +using System.Web.UI.WebControls; +using System.Web.UI.WebControls.WebParts; +using System.Web.UI.HtmlControls; + +public partial class _Default : System.Web.UI.Page +{ + protected void Page_Load(object sender, EventArgs e) + { + + } +} diff --git a/source/CubeMS/README.txt b/source/CubeMS/README.txt new file mode 100644 index 00000000..2138638a --- /dev/null +++ b/source/CubeMS/README.txt @@ -0,0 +1,42 @@ +CubeMS -- masterserver for the AssaultCube v0.94 + + +License: +ZLIB +http://www.gzip.org/zlib/zlib_license.html + +Author: +Adrian 'driAn' Henke +root AT sprintf.org + +Version: +v0.3 +November 2007 + +About this Software: +The purpose of this software is to let AssaultCube gameservers register +themselves on a central location and distribute the list of available +servers to all requesting AssaultCube game clients. Also it provides +additional interfaces for external application and possbilities to +control the storage and monitor various events. +This software has been developed to replace other PHP/Perl/Java based +solutions that are not easily expandable for future AssaultCube features +that will require more interaction with the masterserver. +This software is implemented as IIS HTTP-Handler written in C#/ASP.NET +with a MSSQL backend. Integrating the masterserver as HTTP-Handler and using +various caching possiblities ensures good scalability on higher workload. + +Installation: +There is no streamlined installation procedure for this software, you +need some knowledge of IIS 5+, MSSQL and ASP.NET to make any use of it. +Here is a quick list of required steps: + +* In IIS, create a new (virtual) directory that points to the CubeMS + directory. Configure security, ASP.NET 2.0, etc. +* In IIS, configure the application assignement for your CubeMS app. + Assign the ".do" ending to your aspnet_isapi.dll, restrict to GET requests + and disable the optional checks for existing files. +* In MSSQL, create a new database as well as tables and stored procedures. + Use the provided scripts to get this done. +* In MSSQL, create a new user and configure the web.config file in the CubeMS + directory accordingly. diff --git a/source/CubeMS/web.config b/source/CubeMS/web.config new file mode 100644 index 00000000..f0e7a535 --- /dev/null +++ b/source/CubeMS/web.config @@ -0,0 +1,53 @@ + + + + + + + + + + + + + + + + + + + + + + + + +