using System; using System.Collections.Generic; using System.Text; using System.Threading; using System.Data; using System.Linq; using Snowlight.Specialized; using Snowlight.Storage; using Snowlight.Game.Sessions; using Snowlight.Communication.Outgoing; using Snowlight.Game.Rights; using Snowlight.Game.Messenger; using Snowlight.Game.Navigation; using Snowlight.Game.Items; using Snowlight.Game.Pets; namespace Snowlight.Game.Rooms { public static class RoomManager { #region Fields & Properties public const int ROOM_UPDATE_SPEED = 500; private static Timer mRoomInstanceThread; private static Timer mRoomWritebackThread; private static Dictionary mRoomInstances; private static Dictionary mRoomModels; private static Dictionary mWritebackItems; private static List mWritebackPets; private static uint mInstanceIdGenerator; private static object mIdGeneratorSyncLock; public static Dictionary RoomInstances { get { return mRoomInstances; } } #endregion #region Initialization and collection management public static void Initialize(SqlDatabaseClient MySqlClient) { mRoomInstances = new Dictionary(); mRoomModels = new Dictionary(); mWritebackItems = new Dictionary(); mWritebackPets = new List(); ReloadModels(MySqlClient); mRoomInstanceThread = new Timer(new TimerCallback(ProcessRooms), null, TimeSpan.FromSeconds(10), TimeSpan.FromSeconds(10)); mRoomWritebackThread = new Timer(new TimerCallback(ProcessWritebacks), null, TimeSpan.FromSeconds(10), TimeSpan.FromSeconds(10)); mInstanceIdGenerator = 1; mIdGeneratorSyncLock = new object(); } public static uint GenerateInstanceId() { lock (mIdGeneratorSyncLock) { return mInstanceIdGenerator++; } } public static void ReloadModels(SqlDatabaseClient MySqlClient) { lock (mRoomModels) { mRoomModels.Clear(); MySqlClient.SetParameter("enabled", "1"); DataTable Table = MySqlClient.ExecuteQueryTable("SELECT * FROM room_models WHERE enabled = @enabled"); foreach (DataRow Row in Table.Rows) { mRoomModels.Add((string)Row["id"], new RoomModel((string)Row["id"], ((string)Row["type"] == "flat" ? RoomModelType.Flat : RoomModelType.Public), new Heightmap((string)Row["heightmap"]), new Vector3((int)Row["door_x"], (int)Row["door_y"], (double)Row["door_z"]), (int)Row["door_dir"], (ClubSubscriptionLevel)(int.Parse(Row["subscription_requirement"].ToString())), (int)Row["max_users"])); } } } public static RoomModel GetModel(string Id) { lock (mRoomModels) { foreach (RoomModel Model in mRoomModels.Values) { if (Model.Id == Id) { return Model; } } } return null; } #endregion #region Room Instance Process Thread private static void ProcessRooms(object state) { Dictionary Copy = new Dictionary(); lock (mRoomInstances) { foreach (KeyValuePair CopyItem in mRoomInstances) { Copy.Add(CopyItem.Key, CopyItem.Value); } } List ToDispose = new List(); List ToUnload = new List(); foreach (RoomInstance Instance in Copy.Values) { if (Instance.Unloaded) { if (Instance.TimeUnloaded > 15) { ToDispose.Add(Instance.InstanceId); continue; } } else if (Instance.HumanActorCount == 0) { if (Instance.MarkedAsEmpty >= 1) // 1 = 10 seconds because this method runs every 10 seconds { ToUnload.Add(Instance.InstanceId); } else { Instance.MarkedAsEmpty++; } continue; } else if (Instance.MarkedAsEmpty > 0) { Instance.MarkedAsEmpty = 0; } //Instance.PerformUpdate(); } lock (mRoomInstances) { foreach (uint UnloadId in ToUnload) { if (mRoomInstances.ContainsKey(UnloadId)) { mRoomInstances[UnloadId].Unload(); Output.WriteLine("[RoomMgr] Unloaded room instance " + UnloadId + ".", OutputLevel.DebugInformation); } } foreach (uint DisposeId in ToDispose) { if (mRoomInstances.ContainsKey(DisposeId)) { mRoomInstances[DisposeId].Dispose(); mRoomInstances[DisposeId] = null; mRoomInstances.Remove(DisposeId); Output.WriteLine("[RoomMgr] Disposed of room instance " + DisposeId + " and associated resources.", OutputLevel.DebugInformation); } } } } #endregion #region Database writeback management private static void ProcessWritebacks(object state) { // Item location/state writebacks Dictionary WritebackCopy = new Dictionary(); lock (mWritebackItems) { foreach (KeyValuePair CopyItem in mWritebackItems) { WritebackCopy.Add(CopyItem.Key, CopyItem.Value); } mWritebackItems.Clear(); } if (WritebackCopy.Count > 0) { using (SqlDatabaseClient MySqlClient = SqlDatabaseManager.GetClient()) { foreach (KeyValuePair Item in WritebackCopy) { Item.Key.SynchronizeDatabase(MySqlClient, Item.Value); } } } // Actorcount writebacks List InstanceCopy = new List(); lock (mRoomInstances) { foreach (RoomInstance CopyItem in mRoomInstances.Values) { InstanceCopy.Add(CopyItem); } } foreach (RoomInstance Instance in InstanceCopy) { if (Instance.ActorCountDatabaseWritebackNeeded) { Instance.DoActorCountSync(); } } // Pet writebacks List PetWritebackCopy = new List(); lock (mWritebackPets) { foreach (Pet CopyItem in mWritebackPets) { PetWritebackCopy.Add(CopyItem); } mWritebackPets.Clear(); } if (PetWritebackCopy.Count > 0) { using (SqlDatabaseClient MySqlClient = SqlDatabaseManager.GetClient()) { foreach (Pet Pet in PetWritebackCopy) { Pet.SynchronizeDatabase(MySqlClient); } } } } public static void MarkWriteback(Pet Pet) { lock (mWritebackPets) { if (mWritebackPets.Contains(Pet)) { return; } mWritebackPets.Add(Pet); } } public static void MarkWriteback(Item Item, bool FlagsOnly) { lock (mWritebackItems) { if (mWritebackItems.ContainsKey(Item)) { if (mWritebackItems[Item] == true && !FlagsOnly) { mWritebackItems[Item] = false; } return; } mWritebackItems.Add(Item, FlagsOnly); } } public static void UnmarkWriteback(Item Item) { lock (mWritebackItems) { mWritebackItems.Remove(Item); } } #endregion #region Instance/room data management public static bool InstanceIsLoadedForRoom(uint RoomId) { return (GetInstanceByRoomId(RoomId) != null); } public static bool TryLoadRoomInstance(uint RoomId) { lock (mRoomInstances) { RoomInstance OldInstance = GetInstanceByRoomId(RoomId); if (OldInstance != null) { return false; // instance already exists, bail! } uint NewInstanceId = GenerateInstanceId(); RoomInstance Instance = RoomInstance.TryCreateRoomInstance(NewInstanceId, RoomId); if (Instance == null) { return false; } lock (mRoomInstances) { mRoomInstances.Add(NewInstanceId, Instance); } Output.WriteLine("[RoomMgr] Room instance " + NewInstanceId + " has been loaded for room " + RoomId + ".", OutputLevel.DebugInformation); } return true; } public static RoomInstance GetInstanceByRoomId(uint RoomId) { lock (mRoomInstances) { foreach (RoomInstance Instance in mRoomInstances.Values) { if (Instance.Unloaded) { continue; } if (Instance.Info.Id == RoomId) { return Instance; } } } return null; } public static uint CreateRoom(uint OwnerId, string Name, string Model) { string Result = string.Empty; using (SqlDatabaseClient MySqlClient = SqlDatabaseManager.GetClient()) { MySqlClient.SetParameter("type", "flat"); MySqlClient.SetParameter("ownerid", OwnerId); MySqlClient.SetParameter("name", Name); MySqlClient.SetParameter("model", Model); Result = MySqlClient.ExecuteScalar("INSERT INTO rooms (type,owner_id,name,model) VALUES (@type,@ownerid,@name,@model); SELECT LAST_INSERT_ID();").ToString(); } return (Result == string.Empty ? 0 : uint.Parse(Result)); } public static void DeleteRoom(SqlDatabaseClient MySqlClient, uint RoomId) { MySqlClient.SetParameter("id", RoomId); MySqlClient.ExecuteNonQuery("DELETE FROM rooms WHERE id = @id LIMIT 1"); MySqlClient.SetParameter("id", RoomId); MySqlClient.ExecuteNonQuery("DELETE FROM navigator_frontpage WHERE room_id = @id"); RoomInfoLoader.RemoveFromCache(RoomId); Navigator.ReloadOfficialItems(MySqlClient); } public static bool RemoveUserFromRoom(Session Session, bool SendKick = true) { uint CurrentRoomId = Session.AbsoluteRoomId; bool Ok = false; if (CurrentRoomId > 0) { if (Session.RoomJoined) { RoomInstance Instance = RoomManager.GetInstanceByRoomId(CurrentRoomId); if (Instance != null) { Instance.RemoveCharacterFromRoom(Session.CharacterId); } } Session.AbsoluteRoomId = 0; Session.RoomAuthed = false; Session.RoomJoined = false; MessengerHandler.MarkUpdateNeeded(Session, 0, false); Ok = true; } if (SendKick) { Session.SendData(RoomKickedComposer.Compose()); } return Ok; } #endregion } }