/// <summary> /// Gets or sets a specified key/value pair. /// </summary> /// <param name="key">The key to get/set.</param> /// <returns>Returns the specified key's value.</returns> public object this[string key] { get { if (configs.ContainsKey(key)) { return(configs[key]); } return(null); } set { if (configs.ContainsKey(key)) { using (SqlDatabaseClient client = GameServer.Database.GetClient()) { client.AddParameter("k", key); client.AddParameter("v", value); client.ExecuteUpdate("UPDATE configurations SET configurations.value = @v WHERE configurations.key = @k;"); } this.configs[key] = value; } else { Console.WriteLine(0); } } }
/// <summary> /// Updates a character's online status on the database. /// </summary> /// <param name="masterId">The character's master id.</param> /// <param name="online">The character's online status. /// <para>true equals online; false equals offline.</para></param> public void UpdateOnlineStatus(uint masterId, bool online) { using (SqlDatabaseClient client = GameServer.Database.GetClient()) { client.AddParameter("masterId", masterId); client.AddParameter("status", online ? 1 : 0); client.ExecuteUpdate("UPDATE characters SET online = @status WHERE id = @masterId;"); } }
/// <summary> /// Logs the given chat information into the database. /// </summary> /// <param name="name">The name of the character chatting.</param> /// <param name="type">The type of chat.</param> /// <param name="toName">The character who recieved the chat.</param> /// <param name="message">The message written.</param> public static void LogChat(string name, ChatType type, string toName, string message) { using (SqlDatabaseClient client = GameServer.Database.GetClient()) { client.AddParameter("name", name); client.AddParameter("date", DateTime.Now); client.AddParameter("type", type.ToString().ToLower()); client.AddParameter("toName", toName); client.AddParameter("message", @message); client.ExecuteUpdate("INSERT INTO chat_logs (name,date,type,toname,message) VALUES(@name,@date,@type,@toName,@message);"); } }
/// <summary> /// Gets any offences that the character may have caused from the database. /// </summary> /// <param name="username">The character's username.</param> /// <returns>The offense type.</returns> public OffenceType GetOffence(uint id) { DataRow row; using (SqlDatabaseClient client = GameServer.Database.GetClient()) { client.AddParameter("id", id); row = client.ReadDataRow("SELECT type,expire_date FROM offences WHERE userid = @id AND expired = '0' LIMIT 1;"); } try { if (row != null) { if (DateTime.Now >= (DateTime)row[1]) { RemoveOffence(id); return(OffenceType.None); } Console.WriteLine(row[0]); return((OffenceType)row[0]); } } catch (Exception ex) { Program.Logger.WriteException(ex); } return(OffenceType.None); }
/// <summary> /// Removes an offence off a character. /// </summary> /// <param name="username">The character's username.</param> private void RemoveOffence(uint id) { using (SqlDatabaseClient client = GameServer.Database.GetClient()) { // Changes the expired value from 0 to 1 so that when offences are // checked, the checker will know that this offence has expired. client.AddParameter("id", id); client.ExecuteUpdate("UPDATE offences SET expired = '1' WHERE userid = @id AND expired = '0';"); } }
/// <summary> /// Handles the protocol request. /// </summary> /// <param name="request">The instance requesting the protcol handle.</param> public void Handle(LoginRequest request) { request.Buffer.Skip(1); if (request.Buffer.RemainingAmount >= 4) { long longName = request.Buffer.ReadLong(); string name = StringUtilities.LongToString(longName); bool availible = false; // Check if the entered name is availible. using (SqlDatabaseClient client = GameServer.Database.GetClient()) { client.AddParameter("name", name); DataRow row = client.ReadDataRow("SELECT id FROM characters WHERE username = @name LIMIT 1"); if (row == null) { availible = true; } } // Check if the world allows account creation. if (GameEngine.World.AccountCreationEnabled) { // Name lengths must be valid. if (name != null && name.Length > 0 && name.Length < 13 && !name.ContainsBadWord()) { if (availible) { request.Connection.SendData((byte)AccountCreationReturnCode.Good); } else { request.Connection.SendData((byte)AccountCreationReturnCode.AlreadyTaken); } } else { request.Connection.SendData((byte)AccountCreationReturnCode.InvalidUsername); } } else { request.Connection.SendData((byte)AccountCreationReturnCode.Disabled); } } }
/// <summary> /// Attempts to check the database verson via the database to see if it's valid for use. /// </summary> /// <param name="client">The client providing the connection to database.</param> /// <returns>Returns true if valid; false if not.</returns> public object Execute(SqlDatabaseClient client) { client.AddParameter("sver", Program.Version.ToString()); string dbResult = (string)client.ExecuteQuery( "SELECT database_version FROM versioning; " + "UPDATE versioning SET server_version = @sver;"); Version required = new Version(1, 0, 3100); Version dbVersion = Version.Parse(dbResult); if (dbVersion < required) { return(false); } return(true); }
/// <summary> /// Checks whether a character is online from the database. /// </summary> /// <param name="username">The username to check for.</param> /// <returns>Returns true if the character is online; false if offline.</returns> public bool OnlineByDatabase(string username) { DataRow result = null; using (SqlDatabaseClient client = GameServer.Database.GetClient()) { client.AddParameter("username", username); result = client.ReadDataRow("SELECT online FROM characters WHERE username = @username LIMIT 1;"); } if (result != null) { return((bool)result[0]); } return(false); }
/// <summary> /// Constructs a new world. /// /// <para>With the given world id, it will find the configurations /// set in the database, and configure this world using them. This /// allows you to create multiple worlds, yet connect to the same /// world, with the same players etc.</para> /// </summary> /// <param name="worldId">The id of this world.</param> public GameWorld(uint worldId) { // World initializations this.Id = worldId; DataRow vars; // Grab the rest of the world-specific variables from the database. using (SqlDatabaseClient client = GameServer.Database.GetClient()) { client.AddParameter("id", worldId); vars = client.ReadDataRow( "SELECT * FROM worlds WHERE world_id = @id LIMIT 1;" + "UPDATE characters SET online = '0' WHERE online = '1';" + "UPDATE worlds SET startup_time = NOW() WHERE world_id = @id;"); } if (vars != null) { this.Name = (string)vars["world_name"]; this.WelcomeMessage = (string)vars["welcome_message"]; string[] coords = vars["spawn_point"].ToString().Split(','); this.SpawnPoint = Location.Create( short.Parse(coords[0]), short.Parse(coords[1]), byte.Parse(coords[2])); this.Motw = (string)vars["motw"]; this.ExperienceRate = (int)vars["exp_rate"]; this.AccountCreationEnabled = true; this.IdlingEnabled = (bool)vars["enable_idling"]; } else { throw new ArgumentException("No existing world with id '" + worldId + "'."); } // Load bad words. BadWords.Load(); }
/// <summary> /// Handles connection requests. /// </summary> /// <param name="async">The asynchronous handle result.</param> public void OnConnectionRequest(IAsyncResult result) { try { Socket socket = this.listener.EndAcceptSocket(result); StandBy(); if (this.CheckBlacklist) { using (SqlDatabaseClient client = GameServer.Database.GetClient()) { client.AddParameter("ip", socket.RemoteEndPoint.ToString().Split(':')[0]); DataRow row = client.ReadDataRow("SELECT * FROM blacklist WHERE ip_address = @ip LIMIT 1;"); if (row != null) { Program.Logger.WriteDebug("Connection attempt from " + socket.RemoteEndPoint + " denied."); socket.Close(); socket = null; } } } Node node = this.factory.Create(socket); if (node != null) { this.manager.HandleNewConnection(node); } } catch (ObjectDisposedException) { } catch (Exception ex) { Program.Logger.WriteException(ex); } }
/// <summary> /// Saves the character to the mysql database. /// </summary> /// <param name="character">The character to save.</param> public bool Save(Character character) { try { using (SqlDatabaseClient client = GameServer.Database.GetClient()) { client.AddParameter("id", character.MasterId); // Appearance. client.AddParameter("gender", character.Appearance.Gender); client.AddParameter("head", character.Appearance.Head); client.AddParameter("chest", character.Appearance.Torso); client.AddParameter("arms", character.Appearance.Arms); client.AddParameter("hands", character.Appearance.Wrist); client.AddParameter("legs", character.Appearance.Legs); client.AddParameter("feet", character.Appearance.Feet); client.AddParameter("beard", character.Appearance.Beard); client.AddParameter("hair_color", character.Appearance.HairColor); client.AddParameter("torso_color", character.Appearance.TorsoColor); client.AddParameter("leg_color", character.Appearance.LegColor); client.AddParameter("feet_color", character.Appearance.FeetColor); client.AddParameter("skin_color", character.Appearance.SkinColor); // Preferences. client.AddParameter("coord_x", character.Location.X); client.AddParameter("coord_y", character.Location.Y); client.AddParameter("coord_z", character.Location.Z); client.AddParameter("run_energy", character.WalkingQueue.RunEnergy); // Containers. client.AddParameter("inv", character.Inventory.Serialize()); client.AddParameter("eqp", character.Equipment.Serialize()); client.AddParameter("bank", character.Bank.Serialize()); // Friends and ignores. client.AddParameter("friends", character.Contacts.SerializeFriends()); client.AddParameter("ignores", character.Contacts.SerializeIgnores()); // Preferences client.AddParameter("pref_sm", character.Preferences.SingleMouse); client.AddParameter("pref_ce", character.Preferences.DisableChatEffects); client.AddParameter("pref_sc", character.Preferences.SplitChat); client.AddParameter("pref_aa", character.Preferences.AcceptAid); string query = @" UPDATE character_preferences SET single_mouse=@pref_sm,chat_effects=@pref_ce,split_chat=@pref_sc,accept_aid=@pref_aa WHERE master_id=@id; UPDATE characters SET gender=@gender,head=@head,chest=@chest,arms=@arms,hands=@hands,legs=@legs,feet=@feet, beard=@beard,hair_color=@hair_color,torso_color=@torso_color,leg_color=@leg_color, feet_color=@feet_color,skin_color=@skin_color, coord_x=@coord_x,coord_y=@coord_y, coord_z=@coord_z,run_energy=@run_energy,inventory_items=@inv,equipment_items=@eqp, bank_items=@bank,friends=@friends,ignores=@ignores WHERE id=@id;"; client.ExecuteUpdate(query); return(true); } } catch (Exception ex) { Program.Logger.WriteException(ex); return(false); } }
/// <summary> /// Loads an account from the database. /// </summary> /// <param name="details">The character details to look at when loading.</param> public void LoadAccount(Details details, LoginConnectionType loginType) { StringBuilder sbQuery = new StringBuilder(); AccountLoadResult result = this.accountLoader.Load(details, loginType); // Try to load the account. GenericPacketComposer composer = new GenericPacketComposer(); // The packet going to be sent. // Try registering the user if return code is successful so far. if (result.ReturnCode == LoginReturnCode.Successful) { // The world is full. if (!Register(result.Character)) { result.ReturnCode = LoginReturnCode.WorldFull; } } composer.AppendByte((byte)result.ReturnCode); // We only need to send this if the login was successful. if (result.ReturnCode == LoginReturnCode.Successful) { composer.AppendByte((byte)result.Character.ClientRights); composer.AppendByte((byte)0); composer.AppendByte((byte)0); composer.AppendByte((byte)0); composer.AppendByte((byte)1); composer.AppendShort((short)result.Character.Index); composer.AppendByte((byte)1); if (this.logSessions) { sbQuery.Append("UPDATE characters SET last_ip=@ip, last_signin=NOW() WHERE id = @id;"); } } if (this.logAttempts) { sbQuery.Append("INSERT INTO login_attempts (username,date,ip,attempt) VALUES (@name, NOW(), @ip, @attempt);"); } if (!result.Active) { sbQuery.Append("UPDATE characters SET active = '1' WHERE id = @id;"); } if (sbQuery.Length != 0) { // Log the user's login attempt. This is useful for tracking hacking, ddos, etc. using (SqlDatabaseClient client = GameServer.Database.GetClient()) { client.AddParameter("id", result.Character.MasterId); client.AddParameter("name", details.Username); client.AddParameter("ip", details.Session.Connection.IPAddress); client.AddParameter("attempt", result.ReturnCode.ToString()); client.ExecuteUpdate(sbQuery.ToString()); } } // Send results to the client. result.Character.Session.SendData(composer.SerializeBuffer()); // We can now welcome the player and send nessesary packets. if (result.ReturnCode == LoginReturnCode.Successful) { result.Character.Session.StartConnection(); if (!result.Active) { result.Character.Preferences.Add("just_started", true); } Frames.SendLoginWelcome(result.Character); result.Character.Contacts.OnLogin(); } Program.Logger.WriteDebug(result.Character.Name + " returned " + result.ReturnCode + " at login attempt."); }
/// <summary> /// Loads an account from the mysql database using the given character details. /// </summary> /// <param name="details">The details used to load the account.</param> /// <returns>Returns the account load result.</returns> public AccountLoadResult Load(Details details, LoginConnectionType loginType) { AccountLoadResult result = new AccountLoadResult(); if (GameEngine.World.SystemUpdate) { result.Character = new Character(details, 0); result.ReturnCode = LoginReturnCode.SystemUpdate; } try { DataRow data = null; using (SqlDatabaseClient client = GameServer.Database.GetClient()) { /* * Checks if the character exists in the database. It also * checks if the given password matches the stored password. */ client.AddParameter("username", details.Username); client.AddParameter("password", details.Password); data = client.ReadDataRow("SELECT * FROM characters LEFT JOIN (character_preferences) ON (characters.id = character_preferences.master_id) WHERE username = @username AND password = @password LIMIT 1;"); } if (data != null) // Meaning the character exists, and the password is correct. { result.Character = new Character(details, (uint)data[0]); // If a character is offensive, set the proper penalties. OffenceType offence = (OffenceType)GameEngine.World.OffenseManager.GetOffence(result.Character.MasterId); if (offence == OffenceType.Banned) // If the character is banned, flag and end this request. { result.ReturnCode = LoginReturnCode.AccountDisabled; } if (offence == OffenceType.Muted) // If the character is muted, we will mute this character. { result.Character.Muted = true; } /* * Only check if it's a new connection, as reconnections try * connnecting to the server before the older session is removed, * so we must ignore whether or not the character is online. */ if (loginType != LoginConnectionType.Reconnection && (bool)data[5]) { result.ReturnCode = LoginReturnCode.AlreadyOnline; } /* * We only want to assign the character details loaded from * the database if the player has passed though security. */ if (result.ReturnCode == LoginReturnCode.Successful) { // Core info. result.Character.ClientRights = (ClientRights)data[3]; result.Character.ServerRights = (ServerRights)data[4]; result.Active = (bool)data[6]; // Appearance. result.Character.Appearance.Gender = (Gender)data[14]; result.Character.Appearance.Head = (short)data[15]; result.Character.Appearance.Torso = (short)data[16]; result.Character.Appearance.Arms = (short)data[17]; result.Character.Appearance.Wrist = (short)data[18]; result.Character.Appearance.Legs = (short)data[19]; result.Character.Appearance.Feet = (short)data[20]; result.Character.Appearance.Beard = (short)data[21]; result.Character.Appearance.HairColor = (byte)data[22]; result.Character.Appearance.TorsoColor = (byte)data[23]; result.Character.Appearance.LegColor = (byte)data[24]; result.Character.Appearance.FeetColor = (byte)data[25]; result.Character.Appearance.SkinColor = (byte)data[26]; // Location. result.Character.Location = Location.Create((short)data[27], (short)data[28], (byte)data[29]); // Energy. result.Character.WalkingQueue.RunEnergy = (byte)data[30]; // Containers. if (data[31] is string) { result.Character.Inventory.Deserialize((string)data[31]); } if (data[32] is string) { result.Character.Equipment.Deserialize((string)data[32]); } if (data[33] is string) { result.Character.Bank.Deserialize((string)data[33]); } // Friends and ignores if (data[34] is string) { string friends = (string)data[34]; if (friends != string.Empty) { result.Character.Contacts.DeserializeFriends(friends); } } if (data[35] is string) { string ignores = (string)data[35]; if (ignores != string.Empty) { result.Character.Contacts.DeserializeIgnores((string)data[35]); } } // Preferences. result.Character.Preferences.SingleMouse = (bool)data[37]; result.Character.Preferences.DisableChatEffects = (bool)data[38]; result.Character.Preferences.SplitChat = (bool)data[39]; result.Character.Preferences.AcceptAid = (bool)data[40]; } } else // User doesn't exist or password is wrong. { result.Character = new Character(details, 0); result.ReturnCode = LoginReturnCode.WrongPassword; } } catch (Exception ex) { Program.Logger.WriteException(ex); result.Character = new Character(details, 0); result.ReturnCode = LoginReturnCode.BadSession; } return(result); }
/// <summary> /// Handles the protocol request. /// </summary> /// <param name="request">The instance requesting the protcol handle.</param> public void Handle(LoginRequest request) { request.Buffer.Skip(1); byte packetSize = 0; if (request.Buffer.RemainingAmount >= 1) { packetSize = request.Buffer.ReadByte(); } if (request.Buffer.RemainingAmount >= packetSize) { Packet p = new Packet(request.Buffer.GetRemainingData()); /* * I don't know why, but the packet structure changes * varying on the password, and client type, so we * will just have to loop untill we reach 1. */ while (p.Peek() != 1) { p.Skip(1); } // Check if client revision is valid. int clientVersion = p.ReadShort(); if (clientVersion != 508) { request.Remove = true; return; } long longUser = p.ReadLong(); string username = StringUtilities.LongToString(longUser); p.Skip(4); // PADDING string password = p.ReadString(); Console.WriteLine(password); if (password.Contains(username)) { request.Connection.SendData((byte)AccountCreationReturnCode.TooSimilar); return; } if (password.Length < 5 || password.Length > 20) { request.Connection.SendData((byte)AccountCreationReturnCode.InvalidLength); return; } /* * Security is very important when coming to dealing with passwords, * hence why jolt environment hashes the username and password. */ string hash = Hash.GetHash(username + Hash.GetHash(password, HashType.SHA1), HashType.SHA1); p.Skip(6); // Padding(?) byte birthDay = p.ReadByte(); byte birthMonth = (byte)(p.ReadByte() + 1); p.Skip(4); // Padding(?) short birthYear = p.ReadShort(); short country = p.ReadShort(); p.Skip(4); // Unknown. /* * We now attempt to finalize the creation, by rechecking if username * is availible and inserting the given information to the database. */ using (SqlDatabaseClient client = GameServer.Database.GetClient()) { client.AddParameter("username", username); DataRow row = client.ReadDataRow("SELECT id FROM characters WHERE username = @username LIMIT 1"); /* * If the row isn't null, that means that the database * has found a character with the same username. */ if (row != null) { request.Connection.SendData((byte)AccountCreationReturnCode.AlreadyTaken); return; } else { client.AddParameter("password", hash); // Insert the hashed password for security. client.AddParameter("dob", birthDay + "-" + birthMonth + "-" + birthYear); client.AddParameter("country", country); client.AddParameter("ip", request.Connection.IPAddress); client.ExecuteUpdate("INSERT INTO characters (username,password,dob,country,register_ip,register_date) VALUES (@username, @password, @dob, @country, @ip, NOW());"); uint id = (uint)client.ExecuteQuery("SELECT id FROM characters WHERE username = @username AND password = @password;"); client.AddParameter("id", id); client.ExecuteUpdate("INSERT INTO character_preferences (master_id) VALUES (@id);"); // Now that the character is now registered to the core table, we can now grab the auto incremented id. //dbClient.AddParamWithValue("id", dbClient.ReadUInt32("SELECT id FROM characters WHERE username = @username")); request.Connection.SendData((byte)AccountCreationReturnCode.Good); return; } } } }