commit 4fab4f9d86c47408783ac24621da325ddc35cb0f Author: Melissa Geels Date: Tue Apr 28 16:14:34 2020 +0200 Initial commit (2011) diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b60ecf6 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +bin/ +*.pidb diff --git a/gServer.sln b/gServer.sln new file mode 100644 index 0000000..0d5a30b --- /dev/null +++ b/gServer.sln @@ -0,0 +1,34 @@ + +Microsoft Visual Studio Solution File, Format Version 10.00 +# Visual Studio 2008 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "gServer", "gServer\gServer.csproj", "{6F404B72-8B0D-4A82-BD61-5A5168EF8724}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x86 = Debug|x86 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {6F404B72-8B0D-4A82-BD61-5A5168EF8724}.Debug|x86.ActiveCfg = Debug|x86 + {6F404B72-8B0D-4A82-BD61-5A5168EF8724}.Debug|x86.Build.0 = Debug|x86 + {6F404B72-8B0D-4A82-BD61-5A5168EF8724}.Release|x86.ActiveCfg = Release|x86 + {6F404B72-8B0D-4A82-BD61-5A5168EF8724}.Release|x86.Build.0 = Release|x86 + EndGlobalSection + GlobalSection(MonoDevelopProperties) = preSolution + StartupItem = gServer\gServer.csproj + Policies = $0 + $0.TextStylePolicy = $1 + $1.inheritsSet = null + $1.scope = text/x-csharp + $0.CSharpFormattingPolicy = $2 + $2.StatementBraceStyle = NextLine + $2.PlaceElseOnNewLine = True + $2.PlaceElseIfOnNewLine = True + $2.PlaceCatchOnNewLine = True + $2.PlaceFinallyOnNewLine = True + $2.PlaceWhileOnNewLine = True + $2.inheritsSet = Mono + $2.inheritsScope = text/x-csharp + $2.scope = text/x-csharp + EndGlobalSection +EndGlobal diff --git a/gServer.userprefs b/gServer.userprefs new file mode 100644 index 0000000..3fd8057 --- /dev/null +++ b/gServer.userprefs @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/gServer/AssemblyInfo.cs b/gServer/AssemblyInfo.cs new file mode 100644 index 0000000..da4d94a --- /dev/null +++ b/gServer/AssemblyInfo.cs @@ -0,0 +1,27 @@ +using System.Reflection; +using System.Runtime.CompilerServices; + +// Information about this assembly is defined by the following attributes. +// Change them to the values specific to your project. + +[assembly: AssemblyTitle("gServer")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("")] +[assembly: AssemblyCopyright("")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}". +// The form "{Major}.{Minor}.*" will automatically update the build and revision, +// and "{Major}.{Minor}.{Build}.*" will update just the revision. + +[assembly: AssemblyVersion("1.0.*")] + +// The following attributes are used to specify the signing key for the assembly, +// if desired. See the Mono documentation for more information about signing. + +//[assembly: AssemblyDelaySign(false)] +//[assembly: AssemblyKeyFile("")] + diff --git a/gServer/Main.cs b/gServer/Main.cs new file mode 100644 index 0000000..3c4cdce --- /dev/null +++ b/gServer/Main.cs @@ -0,0 +1,182 @@ +using System; +using System.Threading; +using System.Collections; +using System.Collections.Generic; +using System.Data; +using MySql.Data.MySqlClient; + +namespace gServer +{ + public class MainClass + { + public int SecondsPassed = 0; + public int SecondsPassedHalf = 0; + + private String MySQL_Host = "localhost"; + private String MySQL_Username = "gs"; + private String MySQL_Password = "gs"; + private String MySQL_Database = "gs"; + private String MySQL_Connector = ""; + private IDbConnection MySQL_Connection; + + public MrAG.Serverlist.Server Listener; + public MrAG.Serverlist.Server ListenerAlternative; + + public MrAG.Serverlist.Server ServerlistListener; + + public List Users = new List(); + public List ServerlistServers = new List(); + public List Servers = new List(); + + public void Start() + { + this.MySQL_Connector = "Server=" + this.MySQL_Host + ";" + + "Database=" + this.MySQL_Database + ";" + + "User ID=" + this.MySQL_Username + ";" + + "Password=" + this.MySQL_Password + ";" + "Pooling=false"; + + this.MySQL_Connection = new MySqlConnection(this.MySQL_Connector); + this.MySQL_Connection.Open(); + + this.Query("UPDATE `users` SET `API_Sessionkey`=''"); + + this.Listener = MrAG.Serverlist.Host("__site", 17000, "__site", false); + this.ListenerAlternative = MrAG.Serverlist.Host("__site", 2222, "__site", false); + + this.Listener.OnIncomingConnection = new Action(IncomingConnection); + this.Listener.OnFinishedConnection = new Action(FinishedConnection); + this.ListenerAlternative.OnIncomingConnection = new Action(IncomingConnection); + this.ListenerAlternative.OnFinishedConnection = new Action(FinishedConnection); + + this.Listener.Timeout = 2000; + this.ListenerAlternative.Timeout = 2000; + + this.Log("GamerServices server started on port 17000 (and 2222)."); + + this.ServerlistListener = MrAG.Serverlist.Host("__site", 8443, "__site", false); + this.ServerlistListener.OnIncomingConnection = new Action(Serverlist_IncomingConnection); + this.ServerlistListener.OnFinishedConnection = new Action(Serverlist_FinishedConnection); + this.ServerlistListener.Timeout = 2000; + + double LastKeepAliveForSQL = DateTime.Now.TimeOfDay.TotalMinutes; // keepalive :3 + while(true) + { + if (Math.Abs(LastKeepAliveForSQL - DateTime.Now.TimeOfDay.TotalMinutes) >= 30){ // 30 min interval + this.Query("SELECT 1"); // Completly useless query, just for keepalive :3 + LastKeepAliveForSQL = DateTime.Now.TimeOfDay.TotalMinutes; + } + + this.Listener.Update(); + this.ListenerAlternative.Update(); + this.ServerlistListener.Update(); + + for(int i=0; i ret = new List(); + + while(resource.Read()) + { + Hashtable newRow = new Hashtable(); + for (int i=0; i \\ + ret = ret.Replace("'", "\\'"); // ' -> \' + return ret; + } + + public string MD5(string input) + { + System.Security.Cryptography.MD5CryptoServiceProvider x = new System.Security.Cryptography.MD5CryptoServiceProvider(); + System.Text.StringBuilder s = new System.Text.StringBuilder(); + byte[] bs = System.Text.Encoding.UTF8.GetBytes(input); + + bs = x.ComputeHash(bs); + foreach (byte b in bs) + { + s.Append(b.ToString("x2")); + } + + return s.ToString().ToLower(); + } + + public static void Main (string[] args) + { + new MainClass().Start(); + } + } +} diff --git a/gServer/ServerlistServer.cs b/gServer/ServerlistServer.cs new file mode 100644 index 0000000..947c53a --- /dev/null +++ b/gServer/ServerlistServer.cs @@ -0,0 +1,151 @@ +using System; +using System.Data; +using System.Collections; +using System.Collections.Generic; +using MySql.Data.MySqlClient; + +namespace gServer +{ + public class ServerData + { + public string Name = "null"; + public string Meta = "null"; + public string Game = "null"; + public int CurrentUsers = 0; + public int MaxUsers = 0; + + public string IP = "null"; + public int Port = 0; + + public long lastUpdate; + } + + public class ServerlistServer + { + public MainClass main; + public MrAG.Networking.IncomingConnection Connection; + + public ServerData serverData = new ServerData(); + + public ServerlistServer (MrAG.Networking.IncomingConnection c, MainClass main) + { + this.main = main; + + c.Connection.Packets[0] = new Action(Packet_Add); + c.Connection.Packets[1] = new Action(Packet_Update); + c.Connection.Packets[2] = new Action(Packet_Offline); + c.Connection.Packets[3] = new Action(Packet_UpdatePlayerCount); + c.Connection.Packets[4] = new Action(Packet_UpdateMeta); + + this.Connection = c; + } + + public void Disconnected(bool Unexpected) + { + if(Unexpected) + this.main.Log(this.Connection.IP + " from Serverlist Server unexpectedly disconnected."); + + this.main.Servers.Remove(this.serverData); + this.main.ServerlistServers.Remove(this); + } + + public void Update() + { + try + { + this.Connection.Connection.Update(); + }catch(Exception ex){ + if(ex.Message == "The object was used after being disposed.") + { + this.Disconnected(true); + } + this.main.Log(this.Connection.IP + " from Serverlist Server errored: " + ex.Message); + this.Connection.Kick("Socket error"); + } + } + + public void InvalidPacket() + { + this.Connection.Kick("Invalid packet received!"); + this.main.Log(this.Connection.IP + " from Serverlist Server kicked for an invalid packet."); + this.Disconnected(false); + } + + public void Packet_Add(MrAG.Networking.Packet p) + { + bool shouldAdd = this.serverData.IP == "null"; + this.serverData.IP = this.Connection.IP; + //this.serverData.IP = "127.0.0.1"; + + try + { + this.serverData.Name = p.ReadString(); + this.serverData.Meta = p.ReadString(); + this.serverData.Game = p.ReadString(); + this.serverData.Port = p.ReadInt(); + this.serverData.MaxUsers = p.ReadInt(); + }catch{ + this.InvalidPacket(); + return; + } + + if(shouldAdd) + { + this.main.Servers.Add(this.serverData); + this.main.Log("Added server " + this.serverData.Name + " to " + this.serverData.Game); + }else + this.main.Log("Tried adding " + this.serverData.Name + ", but shouldAdd = false"); + + p.Start_Send(); + p.AddByte(0); + p.AddBool(shouldAdd); + if(!shouldAdd) + p.AddString("Already added!"); + p.Finish_Send(); + } + + public void Packet_Update(MrAG.Networking.Packet p) + { + this.serverData.lastUpdate = this.main.Epoch(); + + if(this.serverData.IP == "null") + { + this.Connection.Connection.Connection.Start_Send(); + this.Connection.Connection.Connection.AddByte(1); + this.Connection.Connection.Connection.AddBool(false); + this.Connection.Connection.Connection.Finish_Send(); + } + } + + public void Packet_Offline(MrAG.Networking.Packet p) + { + this.main.Servers.Remove(this.serverData); + + this.serverData = new ServerData(); + + this.main.Log("Server " + this.Connection.IP + " stopped"); + } + + public void Packet_UpdatePlayerCount(MrAG.Networking.Packet p) + { + try + { + this.serverData.CurrentUsers = p.ReadInt(); + }catch{ + this.InvalidPacket(); + return; + } + } + + public void Packet_UpdateMeta(MrAG.Networking.Packet p) + { + try + { + this.serverData.Meta = p.ReadString(); + }catch{ + this.InvalidPacket(); + return; + } + } + } +} diff --git a/gServer/User.cs b/gServer/User.cs new file mode 100644 index 0000000..d750dc5 --- /dev/null +++ b/gServer/User.cs @@ -0,0 +1,757 @@ +using System; +using System.Data; +using System.Collections; +using System.Collections.Generic; +using MySql.Data.MySqlClient; + +namespace gServer +{ + public enum MessageboxIcon : byte { Asterisk, Error, Exclamation, Hand, Info, None, Question, Stop, Warning } + + public class User + { + public MainClass main; + + public MrAG.Networking.IncomingConnection Connection; + + public int UserID = 0; + public String Username = ""; + public String Sessionkey = ""; + public String Avatar = ""; + + public int UpdateCount = 0; + + public List Friends = new List(); + + public User(MrAG.Networking.IncomingConnection c, MainClass main) + { + this.main = main; + + // 0-2: User related packets + c.Connection.Packets[0] = new Action(Packet_Login); + c.Connection.Packets[1] = new Action(Packet_Logout); + c.Connection.Packets[2] = new Action(Packet_Ping); + + // 3-7: Friends related packets + c.Connection.Packets[3] = new Action(Packet_Friendslist); + c.Connection.Packets[5] = new Action(Packet_Chatmessage); + c.Connection.Packets[6] = new Action(Packet_FriendrequestConfirmation); + c.Connection.Packets[7] = new Action(Packet_Friendrequest); + + // 10-29: Game related packets + c.Connection.Packets[10] = new Action(Packet_Gamelist); + c.Connection.Packets[11] = new Action(Packet_Updates); + c.Connection.Packets[12] = new Action(Packet_GameAchievements); + c.Connection.Packets[13] = new Action(Packet_GameLeaderboards); + c.Connection.Packets[14] = new Action(Packet_IngameChange); + c.Connection.Packets[15] = new Action(Packet_ServerList); + + // 30-39: General + c.Connection.Packets[31] = new Action(Packet_AvatarRequest); + + + // 40-49: Statistics submissions + c.Connection.Packets[40] = new Action(Packet_Achievement_Add); + c.Connection.Packets[41] = new Action(Packet_Achievement_Set); + c.Connection.Packets[42] = new Action(Packet_Leaderboard_Submit); + + this.Connection = c; + } + + public Hashtable UserRow + { + get + { + return this.main.Query("SELECT * FROM `users` WHERE `ID`=" + this.UserID)[0]; + } + } + + public void Messagebox(String Title, String Text, MessageboxIcon Icon) + { + this.Connection.Connection.Connection.Start_Send(); + this.Connection.Connection.Connection.AddByte(30); + this.Connection.Connection.Connection.AddString(Title); + this.Connection.Connection.Connection.AddString(Text); + this.Connection.Connection.Connection.AddByte((byte)Icon); + this.Connection.Connection.Connection.Finish_Send(); + } + + public void Disconnected(bool Unexpected) + { + if(Unexpected) + this.main.Log(this.Connection.IP + " (" + this.Username + ") unexpectedly disconnected."); + + if(this.UserID != 0) + this.main.Query("UPDATE `users` SET `API_SessionKey`='' WHERE `ID`=" + this.UserID); + + foreach (User Friend in this.Friends){ + Friend.Connection.Connection.Connection.Start_Send(); + Friend.Connection.Connection.Connection.AddByte(4); + Friend.Connection.Connection.Connection.AddInt(this.UserID); + Friend.Connection.Connection.Connection.AddBool(false); + Friend.Connection.Connection.Connection.Finish_Send(); + + Friend.Friends.Remove(this); + } + + this.main.Users.Remove(this); + } + + public void Update() + { + try + { + this.Connection.Connection.Update(); + }catch(Exception ex){ + if(ex.Message == "The object was used after being disposed.") + { + this.Disconnected(true); + } + this.main.Log(this.Connection.IP + " (" + this.Username + ") errored: " + ex.Message); + this.Connection.Kick("Socket error"); + } + + UpdateCount++; + if(UpdateCount >= 1000) + { + + UpdateCount = 0; + try + { + this.Connection.Connection.Connection.Start_Send(); + this.Connection.Connection.Connection.AddByte(2); + this.Connection.Connection.Connection.Finish_Send(); + }catch{} + } + } + + public void InvalidPacket() + { + this.Connection.Kick("Invalid packet received!"); + this.main.Log(this.Connection.IP + " (" + this.Username + ") kicked for an invalid packet."); + this.Disconnected(false); + } + + public void Packet_Login(MrAG.Networking.Packet p) + { + String Username; + String Password; + + try + { + Username = p.ReadString(); + Password = p.ReadString(); + }catch{ + this.InvalidPacket(); + return; + } + + Hashtable[] rows = this.main.Query("SELECT * FROM `users` WHERE `Username`='" + this.main.Safe(Username) + "' AND `Password`='" + this.main.Safe(Password) + "'"); + if(rows.Length == 1) + { + this.UserID = int.Parse((String)rows[0]["ID"]); + this.Username = (String)rows[0]["Username"]; + this.Sessionkey = this.main.MD5(DateTime.Now.TimeOfDay.TotalMilliseconds.ToString() + "GamerServices :D"); + this.Avatar = this.main.MD5((String)rows[0]["Email"]); + + this.main.Query("UPDATE `users` SET `API_SessionKey`='" + this.Sessionkey + "' WHERE `ID`=" + rows[0]["ID"]); + + p.Start_Send(); + p.AddByte(0); + p.AddBool(true); + p.AddString(this.Sessionkey); + p.Finish_Send(); + + Hashtable[] FriendConnections = this.main.Query("SELECT * FROM `friends` WHERE `User`=" + this.UserID + " OR `Friend`=" + this.UserID); + + for(int i=0; i 0 ? 1 : 0; + } + }else // fuck looperdieloops, we are strong like freaking chuck noris! + gamestosend = Rows.Length; + + + p.AddInt(gamestosend); + for(int i=0; i 0) + { + p.AddString((String)Rows[i]["Identifier"]); + p.AddString((String)Rows[i]["Name"]); + p.AddShort(gameID); + p.AddString((String)Rows[i]["Description"]); + p.AddString((String)Rows[i]["Executable"]); + + + p.AddBool(isadmin || accesslist[gameID] > 1); + } + } + p.Finish_Send(); + } + + public void Packet_Updates(MrAG.Networking.Packet p) + { + int AppID = 0; + + try + { + AppID = (int)p.ReadShort(); + }catch{ + this.InvalidPacket(); + return; + } + + p.Start_Send(); + p.AddByte(11); + p.AddString("1.0.0"); //TODO: Change. + p.AddShort((short)AppID); + p.Finish_Send(); + } + + public void Packet_GameAchievements(MrAG.Networking.Packet p) + { + short AppID = 0; + + try + { + AppID = (short)p.ReadShort(); + }catch{ + this.InvalidPacket(); + return; + } + + p.Start_Send(); + p.AddByte(12); + p.AddShort(AppID); + + Hashtable[] Rows = this.main.Query("SELECT * FROM `achievements` WHERE `Game`=" + AppID.ToString()); + + p.AddInt(Rows.Length); + + for(int i=0; i int.Parse((String)CurrentEntry["Score"])) + this.main.Query("UPDATE `leaderboards_entries` SET `Score`=" + Score + " WHERE `ID`=" + (String)CurrentEntry["ID"]); + } + + // Send back current location + int C = 0; + Hashtable[] Entries = this.main.Query("SELECT * FROM `leaderboards_entries` WHERE `Leaderboard`=" + (String)Leaderboard["ID"] + " ORDER BY `Score` DESC"); + for(int i=0; i this.main.Epoch() - 30) + { + serverCount++; + } + } + p.AddInt(serverCount); + foreach(ServerData server in this.main.Servers) + { + if(server.Game == gameIdent && server.lastUpdate > this.main.Epoch() - 30) + { + p.AddString(server.IP); + p.AddInt(server.Port); + p.AddString(server.Name); + p.AddString(server.Meta); + p.AddInt(server.CurrentUsers); + p.AddInt(server.MaxUsers); + } + } + p.Finish_Send(); + + this.main.Log("Sent " + this.Username + " " + serverCount.ToString() + "/" + this.main.Servers.Count.ToString() + " servers for " + gameIdent + "."); + } + } +} diff --git a/gServer/gServer.csproj b/gServer/gServer.csproj new file mode 100644 index 0000000..54eb7fd --- /dev/null +++ b/gServer/gServer.csproj @@ -0,0 +1,53 @@ + + + + Debug + x86 + 9.0.21022 + 2.0 + {6F404B72-8B0D-4A82-BD61-5A5168EF8724} + Exe + gServer + gServer + v3.5 + + + true + full + false + bin\Debug + DEBUG + prompt + 4 + x86 + true + + + none + false + bin\Release + prompt + 4 + x86 + true + + + + + + False + bin\Debug\MySql.Data.dll + + + False + bin\Debug\MrAG.dll + + + + + + + + + + \ No newline at end of file