/* Thor Server 2.0 Project - Codenamed "Stockholm" Copyright 2008 - 2009 Joe Hegarty This file is part of The Thor Server 2.0 Project - Codenamed "Stockholm". The Thor Server 2.0 Project - Codenamed "Stockholm" is free software: you can redistribute it and/or modify it under the terms of the Microsoft Public License The Thor Server Project is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the Microsoft Public License for more details. You should have received a copy of the Microsoft Public License along with The Thor Server Project. If not, see http://www.opensource.org/licenses/ms-pl.html */ using System; using System.Threading.Tasks; using System.Collections.Generic; using System.Linq; using StockholmLibrary.Net; using StockholmLibrary.Game; using StockholmLibrary.Util; using StockholmLibrary.Data; using StockholmLibrary.Game.Sessions; using StockholmLibrary.Game.Packets.Client; using StockholmLibrary.Game.Packets.Server; namespace StockholmLibrary.Game.Handlers.Generic { //------------------------------------ public class GenericNavigatorHandler { //********************************************** public GenericNavigatorHandler() { Stockholm.Get().PacketManager.RegisterGenericHandler(EClientPacket.GET_USER_FLAT_CATS, new PacketRoutingInfo.ProcessPacketDelegate(HandleGetUserFlatCats), true); Stockholm.Get().PacketManager.RegisterGenericHandler(EClientPacket.NAVIGATE, new PacketRoutingInfo.ProcessPacketDelegate(HandleNavigate), true); Stockholm.Get().PacketManager.RegisterGenericHandler(EClientPacket.GETINTERST, new PacketRoutingInfo.ProcessPacketDelegate(HandleGetInterest), true); Stockholm.Get().PacketManager.RegisterGenericHandler(EClientPacket.ROOM_DIRECTORY, new PacketRoutingInfo.ProcessPacketDelegate(HandleRoomDirectory), true); Stockholm.Get().PacketManager.RegisterGenericHandler(EClientPacket.SUSERF, new PacketRoutingInfo.ProcessPacketDelegate(HandleSeeMyFlats), true); Stockholm.Get().PacketManager.RegisterGenericHandler(EClientPacket.SRCHF, new PacketRoutingInfo.ProcessPacketDelegate(HandleSearchFlat), true); Stockholm.Get().PacketManager.RegisterGenericHandler(EClientPacket.GETFLATINFO, new PacketRoutingInfo.ProcessPacketDelegate(HandleGetFlatInfo), true); Stockholm.Get().PacketManager.RegisterGenericHandler(EClientPacket.TRYFLAT, new PacketRoutingInfo.ProcessPacketDelegate(HandleTryFlat), true); Stockholm.Get().PacketManager.RegisterGenericHandler(EClientPacket.GOTOFLAT, new PacketRoutingInfo.ProcessPacketDelegate(HandleGotoFlat), true); Stockholm.Get().PacketManager.RegisterGenericHandler(EClientPacket.GOAWAY, new PacketRoutingInfo.ProcessPacketDelegate(HandleGoAway), true); Stockholm.Get().PacketManager.RegisterGenericHandler(EClientPacket.CREATEFLAT, new PacketRoutingInfo.ProcessPacketDelegate(HandleCreateFlat), true); Stockholm.Get().PacketManager.RegisterGenericHandler(EClientPacket.DELETEFLAT, new PacketRoutingInfo.ProcessPacketDelegate(HandleDeleteFlat), true); Stockholm.Get().PacketManager.RegisterGenericHandler(EClientPacket.UPDATEFLAT, new PacketRoutingInfo.ProcessPacketDelegate(HandleUpdateFlat), true); Stockholm.Get().PacketManager.RegisterGenericHandler(EClientPacket.SETFLATINFO, new PacketRoutingInfo.ProcessPacketDelegate(HandleSetFlatInfo), true); Stockholm.Get().PacketManager.RegisterGenericHandler(EClientPacket.GETFLATCAT, new PacketRoutingInfo.ProcessPacketDelegate(HandleGetFlatCat), true); Stockholm.Get().PacketManager.RegisterGenericHandler(EClientPacket.SETFLATCAT, new PacketRoutingInfo.ProcessPacketDelegate(HandleSetFlatCat), true); } //********************************************** private void HandleGetUserFlatCats(ClientPacket packet) { Task.Factory.StartNew(() => ProcessGetUserFlatCats(packet.Peer.Session)); } //********************************************** private void HandleNavigate(ClientPacket packet) { NavigatePacket navigatePacket = new NavigatePacket(); navigatePacket.Deserialise(packet); Task.Factory.StartNew(() => ProcessHandleNavigate(navigatePacket, packet.Peer.Session)); } //********************************************** private void HandleGetInterest(ClientPacket packet) { InterestInfoPacket responsePacket = new InterestInfoPacket(); packet.Peer.EnqueuePacket(responsePacket); } //********************************************** private void HandleRoomDirectory(ClientPacket packet) { RoomDirectoryPacket roomDirectoryPacket = new RoomDirectoryPacket(); roomDirectoryPacket.Deserialise(packet); Task.Factory.StartNew(() => ProcessRoomDirectory(roomDirectoryPacket, packet.Peer.Session)); } //********************************************** private void HandleGetFlatInfo(ClientPacket packet) { GetFlatInfoPacket flatInfoPacket = new GetFlatInfoPacket(); flatInfoPacket.Deserialise(packet); Task.Factory.StartNew(() => ProcessGetFlatInfo(flatInfoPacket)); } //********************************************** private void HandleTryFlat(ClientPacket packet) { TryFlatPacket tryFlatPacket = new TryFlatPacket(); tryFlatPacket.Deserialise(packet); Task.Factory.StartNew(() => ProcessTryFlat(tryFlatPacket, packet.Peer.Session)); } //********************************************** private void HandleGotoFlat(ClientPacket packet) { GotoFlatPacket gotoFlatPacket = new GotoFlatPacket(); gotoFlatPacket.Deserialise(packet); Task.Factory.StartNew(() => ProcessGotoFlat(gotoFlatPacket, packet.Peer.Session)); } //********************************************** private void HandleGoAway(ClientPacket packet) { ExitCurrentRoom(packet.Peer.Session); } //********************************************** private void HandleSearchFlat(ClientPacket packet) { SearchFlatPacket searchFlatPacket = new SearchFlatPacket(); searchFlatPacket.Deserialise(packet); Task.Factory.StartNew(() => ProcessHandleSearchFlat(searchFlatPacket, packet.Peer.Session)); } //********************************************** private void ProcessHandleSearchFlat(SearchFlatPacket packet, Session session) { // Query all the flats matching this criteria from database var queryResult = from p in Stockholm.Get().Database.Rooms where p.OwnerID != null && (p.User.Username == packet.SearchCriteria || p.RoomName.StartsWith(packet.SearchCriteria)) select p; List flatList = new List(queryResult.Count()); // Only return rooms that are actually visible bool canSeeHiddenOwners = false; // TODO: user right check foreach (var flat in queryResult) { if ((bool)flat.ShowOwner || flat.OwnerID == packet.Peer.Session.User.UserID || canSeeHiddenOwners) { flatList.Add(flat); } } // Any hits? if (flatList.Count() == 0) { session.Peer.EnqueuePacket(new ServerPacket(EServerPacket.NOFLATS)); } else { FlatResultsPacket flatResultsPacket = new FlatResultsPacket(flatList, true); session.Peer.EnqueuePacket(flatResultsPacket); } } //********************************************** private void HandleSeeMyFlats(ClientPacket packet) { Task.Factory.StartNew(() => ProcessHandleSeeMyFlats(packet.Peer.Session)); } //********************************************** private void ProcessHandleSeeMyFlats(Session session) { var queryResult = from p in Stockholm.Get().Database.Rooms where p.OwnerID != null && p.OwnerID == session.User.UserID select p; if (queryResult.Count() == 0) { session.Peer.EnqueuePacket(new ServerPacket(EServerPacket.NOFLATSFORUSER)); } else { FlatResultsPacket resultsPacket = new FlatResultsPacket(queryResult.ToList(), false); session.Peer.EnqueuePacket(resultsPacket); } } //********************************************** private void HandleCreateFlat(ClientPacket packet) { CreateFlatPacket createFlatPacket = new CreateFlatPacket(); createFlatPacket.Deserialise(packet); Task.Factory.StartNew(() => ProcessHandleCreateFlat(createFlatPacket, packet.Peer.Session)); } //********************************************** private void ProcessHandleCreateFlat(CreateFlatPacket packet, Session session) { StockholmDbDataContext db = Stockholm.Get().Database; // Verify if the room model exists var modelQuery = from p in db.RoomModels where p.ModelName == packet.ModelName select p; if (modelQuery.Count() == 0) { Logging.LogEvent("Peer " + session.Peer.PeerID, "Refused to process flat creation request because the specified room model ('" + packet.ModelName + "') does not exist.", Logging.ELogLevel.WARNING); return; } // Verify if the room model is valid RoomModel model = modelQuery.Single(); if (model.CCTList != null || false) // TODO: check for Habbo Club layouts etc { Logging.LogEvent("Peer " + session.Peer.PeerID, "Refused to process flat creation request because the specified room model ('" + model.ModelName + "') was invalid.", Logging.ELogLevel.WARNING); return; } // Verify the room state var stateQuery = from p in db.RoomStates where p.StatusName == packet.StatusName select p; if (stateQuery.Count() == 0) { Logging.LogEvent("Peer " + session.Peer.PeerID, "Refused to process flat creation request because the specified room state ('" + packet.StatusName + "') does not exist.", Logging.ELogLevel.WARNING); return; } // Alright, we have all user-given information now Room room = new Room(); room.OwnerID = session.User.UserID; room.ShowOwner = packet.ShowOwner; room.RoomName = packet.RoomName; room.RoomFriendlyName = packet.RoomName; room.ModelID = model.RoomModelID; room.RoomStatus = stateQuery.Single().StatusID; // Apply constant values or values that aren't known at this point room.RoomDescription = string.Empty; room.Password = null; room.SuperUsers = false; room.WallpaperID = GameConstants.ROOM_DEFAULT_WALLPAPER; room.FloorID = GameConstants.ROOM_DEFAULT_FLOOR; room.CategoryID = GameConstants.ROOM_DEFAULT_CATEGORYID; room.MaxGuests = GameConstants.ROOM_DEFAULT_MAXGUESTS; // Attempt to insert the room into the database db.Rooms.InsertOnSubmit(room); db.SubmitChanges(); if (room.RoomID > 0) { FlatCreatedPacket flatCreatedPacket = new FlatCreatedPacket(room.RoomID, room.RoomName); session.Peer.EnqueuePacket(flatCreatedPacket); } } //********************************************** private void HandleDeleteFlat(ClientPacket packet) { DeleteFlatPacket deletePacket = new DeleteFlatPacket(); deletePacket.Deserialise(packet); Task.Factory.StartNew(() => ProcessHandleDeleteFlat(deletePacket, packet.Peer.Session)); } //********************************************** private void ProcessHandleDeleteFlat(DeleteFlatPacket packet, Session session) { var db = Stockholm.Get().Database; var roomQuery = from p in db.Rooms where p.RoomID == packet.RoomID && p.OwnerID == session.User.UserID select p; // Room exists and is being owned by this session's user? if (roomQuery.Count() > 0) { Room room = roomQuery.Single(); // TODO: destroy instance, delete content etc db.Rooms.DeleteOnSubmit(room); db.SubmitChanges(); } } //********************************************** private void HandleGetFlatCat(ClientPacket packet) { GetFlatCatPacket getFlatCatPacket = new GetFlatCatPacket(); getFlatCatPacket.Deserialise(packet); Task.Factory.StartNew(() => ProcessHandleGetFlatCat(getFlatCatPacket, packet.Peer.Session)); } //********************************************** private void ProcessHandleGetFlatCat(GetFlatCatPacket packet, Session session) { var db = Stockholm.Get().Database; var roomQuery = from p in db.Rooms where p.RoomID == packet.RoomID select p.CategoryID; // Room exists? if (roomQuery.Count() > 0) { int categoryID = roomQuery.Single(); session.Peer.EnqueuePacket(new FlatCatPacket(packet.RoomID, categoryID)); } } //********************************************** private void HandleSetFlatCat(ClientPacket packet) { SetFlatCatPacket setFlatCatPacket = new SetFlatCatPacket(); setFlatCatPacket.Deserialise(packet); Task.Factory.StartNew(() => ProcessHandleSetFlatCat(setFlatCatPacket, packet.Peer.Session)); } //********************************************** private void ProcessHandleSetFlatCat(SetFlatCatPacket packet, Session session) { var db = Stockholm.Get().Database; var roomQuery = from p in db.Rooms where p.RoomID == packet.RoomID && p.OwnerID == session.User.UserID select p; var categoryQuery = from p in db.RoomCategories where p.RoomCategoryID == packet.CategoryID select p; // Room and category exist? if (roomQuery.Count() > 0 && categoryQuery.Count() > 0) { Room room = roomQuery.Single(); RoomCategory category = categoryQuery.Single(); // TODO: check if category is valid etc // Update the category for this room room.CategoryID = category.RoomCategoryID; db.SubmitChanges(); } } //********************************************** private void HandleUpdateFlat(ClientPacket packet) { UpdateFlatPacket updateFlatPacket = new UpdateFlatPacket(); updateFlatPacket.Deserialise(packet); Task.Factory.StartNew(() => ProcessHandleUpdateFlat(updateFlatPacket, packet.Peer.Session)); } //********************************************** private void ProcessHandleUpdateFlat(UpdateFlatPacket packet, Session session) { var db = Stockholm.Get().Database; var roomQuery = from p in db.Rooms where p.RoomID == packet.RoomID && p.OwnerID == session.User.UserID select p; var stateQuery = from p in db.RoomStates where p.StatusName == packet.StatusName select p.StatusID; // Room exists and is being owned by this session's user? if (roomQuery.Count() > 0 && stateQuery.Count() > 0) { Room room = roomQuery.Single(); room.RoomName = packet.RoomName; room.ShowOwner = packet.ShowOwner; room.RoomStatus = stateQuery.Single(); db.SubmitChanges(); } } //********************************************** private void HandleSetFlatInfo(ClientPacket packet) { SetFlatInfoPacket infoPacket = new SetFlatInfoPacket(); infoPacket.Deserialise(packet); Task.Factory.StartNew(() => ProcessHandleSetFlatInfo(infoPacket, packet.Peer.Session)); } //********************************************** private void ProcessHandleSetFlatInfo(SetFlatInfoPacket packet, Session session) { var db = Stockholm.Get().Database; var roomQuery = from p in db.Rooms where p.RoomID == packet.RoomID && p.OwnerID == session.User.UserID select p; // Room exists and is being owned by this session's user? if (roomQuery.Count() > 0) { // Apply the changes to properties Room room = roomQuery.Single(); room.RoomDescription = packet.RoomDescription; room.SuperUsers = packet.SuperUsers; room.Password = packet.Password; if(packet.MaxGuests >= 5 && packet.MaxGuests <= 50) room.MaxGuests = packet.MaxGuests; db.SubmitChanges(); } } //********************************************** private void ProcessHandleNavigate(NavigatePacket packet, Session session) { List subcats = GetSubcatList(packet.CategoryID, session); List rooms = GetRoomList(packet.CategoryID); RoomCategory category = GetCategory(packet.CategoryID, session); FlatListPacket flatListPacket = new FlatListPacket(packet.HideFullRooms, category, subcats, rooms); session.Peer.EnqueuePacket(flatListPacket); } //********************************************** private void ProcessGetUserFlatCats(Session session) { int rootCategoryID = 0; if (GetRootCategoryNode(false, ref rootCategoryID)) { UserFlatCastListPacket packet = new UserFlatCastListPacket(GetSubcatList(rootCategoryID, session)); session.Peer.EnqueuePacket(packet); } } //********************************************** private void ProcessGetFlatInfo(GetFlatInfoPacket packet) { StockholmDbDataContext db = Stockholm.Get().Database; var roomQuery = from p in db.Rooms where p.RoomID == packet.RoomID select p; if (roomQuery.Count() > 0) { Room room = roomQuery.Single(); FlatInfoPacket flatInfoPacket = new FlatInfoPacket(room); packet.Peer.EnqueuePacket(flatInfoPacket); } } //********************************************** private void ProcessTryFlat(TryFlatPacket packet, Session session) { if (Stockholm.Get().GameManager.FlatManager.FlatExists(packet.RoomID)) { if (Stockholm.Get().GameManager.FlatManager.FlatInitialised(packet.RoomID)) { Flats.Flat flat = Stockholm.Get().GameManager.FlatManager.GetFlat(packet.RoomID); if (flat.ExecuteRoomEntranceChecks(session)) { if (flat.AddAuthenticatedSession(session, false)) { session.Peer.EnqueuePacket(new ServerPacket(EServerPacket.FLAT_OK)); } else { session.Peer.EnqueuePacket(new ServerPacket(EServerPacket.HOTEL_VIEW)); } } } } } //********************************************** private void ProcessGotoFlat(GotoFlatPacket packet, Session session) { if (Stockholm.Get().GameManager.FlatManager.FlatExists(packet.RoomID)) { if (Stockholm.Get().GameManager.FlatManager.FlatInitialised(packet.RoomID)) { Stockholm.Get().GameManager.FlatManager.GetFlat(packet.RoomID).EnterFlat(session); return; } } // If we got here, it failed session.Peer.EnqueuePacket(new ServerPacket(EServerPacket.HOTEL_VIEW)); } //********************************************** private void ProcessRoomDirectory(RoomDirectoryPacket packet, Session session) { ExitCurrentRoom(session); if (Stockholm.Get().GameManager.FlatManager.FlatExists(packet.RoomID)) { Stockholm.Get().GameManager.FlatManager.InitialiseFlat(packet.RoomID); if (packet.PublicRoomRequest) { Flats.Flat flat = Stockholm.Get().GameManager.FlatManager.GetFlat(packet.RoomID); if (flat.ExecuteRoomEntranceChecks(session)) { if (flat.AddAuthenticatedSession(session, true)) { flat.EnterFlat(session); } else { session.Peer.EnqueuePacket(new ServerPacket(EServerPacket.HOTEL_VIEW)); } } else { session.Peer.EnqueuePacket(new ServerPacket(EServerPacket.HOTEL_VIEW)); } } else { session.Peer.EnqueuePacket(new ServerPacket(EServerPacket.ROOM_READY)); } } } //********************************************** private bool GetRootCategoryNode(bool systemRooms, ref int outCategoryId) { StockholmDbDataContext db = Stockholm.Get().Database; var nodeQuery = from p in db.RoomCategories where p.RoomCategoryParent == null && p.SystemCategory == systemRooms && p.IsVisible == true select p.RoomCategoryID; int resultCount = nodeQuery.Count(); if (resultCount == 1) { // Correct Number outCategoryId = nodeQuery.Single(); return true; } else if (resultCount == 0) { // None Logging.LogEvent("GenericNavigatorHandler", "There is no root category set up for rooms when the SystemRooms flag is " + systemRooms.ToString() + ". Please note, setting a category as invisible causes it to be ignored.", Logging.ELogLevel.ERROR); ; outCategoryId = -1; return false; } else { // Too Many Logging.LogEvent("GenericNavigatorHandler", "You can not have more than 1 root room category set up. Query was when SystemRooms flag is " + systemRooms.ToString() + ". You can fix this by setting a category as invisible, this causes it to be ignored.", Logging.ELogLevel.ERROR); ; outCategoryId = -1; return false; } } //********************************************** private RoomCategory GetCategory(int categoryId, Session session) { RoomCategory roomCategory = null; StockholmDbDataContext db = Stockholm.Get().Database; bool includeHiddenRooms = Stockholm.Get().GameManager.RoleManager.HasAccess(session.User.RoleID, "fuse_see_invisible_rooms"); if (includeHiddenRooms) { var subcatQuery = from p in db.RoomCategories where p.RoomCategoryID == categoryId select p; roomCategory = subcatQuery.First(); } else { var subcatQuery = from p in db.RoomCategories where p.RoomCategoryID == categoryId && p.IsVisible == true select p; roomCategory = subcatQuery.First(); } return roomCategory; } //********************************************** private List GetSubcatList(int parentCategory, Session session) { List categoryList = new List(); StockholmDbDataContext db = Stockholm.Get().Database; bool includeHiddenRooms = Stockholm.Get().GameManager.RoleManager.HasAccess(session.User.RoleID, "fuse_see_invisible_rooms"); if (includeHiddenRooms) { var subcatQuery = from p in db.RoomCategories where p.RoomCategoryParent == parentCategory select p; categoryList = subcatQuery.ToList(); } else { var subcatQuery = from p in db.RoomCategories where p.RoomCategoryParent == parentCategory && p.IsVisible == true select p; categoryList = subcatQuery.ToList(); } return categoryList; } //********************************************** private List GetRoomList(int parentCategory) { List roomList = new List(); StockholmDbDataContext db = Stockholm.Get().Database; var roomQuery = from p in db.Rooms where p.CategoryID == parentCategory select p; roomList = roomQuery.ToList(); return roomList; } //********************************************** private void ExitCurrentRoom(Session session) { // Time to kick the user out of their current room if (session.CurrentFlat != null) { session.CurrentFlat.ExitFlat(session); session.Peer.EnqueuePacket(new ServerPacket(EServerPacket.HOTEL_VIEW)); } } } }