// outgoing packets are checked here before being converted to JSON and sent to the client
        public static bool HandleOutgoingPacket(ref object packet, Session session)
        {
            string sessionString = session.Player != null ? session.Player.Username : session.IpAddress;
            Type type = packet.GetType();

            PacketInfo packetInfo;
            if (!serverToClientPackets.TryGetValue(type, out packetInfo))
            {
                LogManager.Write("Packet Manager", "Invalid outgoing packet type {0}!", type.Name);
                return false;
            }

            if (!HasSessionType(session.Type, packetInfo.SessionType))
            {
                LogManager.Write("Packet Manager", "Failed to send packet {0}, {1} has invalid session type {2}!",
                    packetInfo.Name, sessionString, session.Type.ToString());
                return false;
            }

            if (session.Player == null && packetInfo.AuthRequired)
            {
                LogManager.Write("Packet Manager", "Failed to send packet {0}, {1} isn't authenticated!", packetInfo.Name, sessionString);
                return false;
            }

            var packetHeader = (PacketHeader)packet;
            packetHeader.Msg = packetInfo.Name;

            #if DEBUG
            if (packetHeader.Msg != "Ping")
                LogManager.Write("Packet Manager", "DEBUG: Sent packet {0} to {1}.", packetHeader.Msg, sessionString);
            #endif

            return true;
        }
        public static void HandleDeckList(object packet, Session session)
        {
            foreach (var deck in session.Player.Decks)
                deck.CalculateAge();

            var deckList = new PacketDeckList()
            {
                Decks = session.Player.Decks
            };

            session.Send(deckList);
        }
        public static void HandleDeckDelete(object packet, Session session)
        {
            var packetDeckDeleteCli = (PacketDeckDelete)packet;

            var deck = session.Player.GetDeck(packetDeckDeleteCli.Name);
            if (deck == null)
            {
                LogManager.Write("Player", "Player {0} requested deletion of non existant deck {1}!",
                    session.Player.Id, packetDeckDeleteCli.Name);
                return;
            }

            session.Player.Decks.Remove(deck);
            deck.Delete();

            session.SendOkPacket("DeckDelete");
        }
        public static bool AddPlayerSession(Session session)
        {
            if (session.Player == null)
                return false;

            if (IsPlayerOnline(session.Player.Username))
                return false;

            if (sessionMap.TryAdd(session.Player.Username, session))
            {
                if (!session.Player.HasFlag(PlayerFlags.HidePlayer))
                    SessionCountIncrement();

                return true;
            }

            return false;
        }
        public static void HandleDeckCards(object packet, Session session)
        {
            var packetDeckCardsCli = (PacketDeckCards)packet;

            var deck = session.Player.GetDeck(packetDeckCardsCli.Deck);
            if (deck == null)
            {
                LogManager.Write("Player", "Player {0} requested cards for non existant deck {1}!",
                    session.Player.Id, packetDeckCardsCli.Deck);
                return;
            }

            var deckCards = new PacketDeckCards()
            {
                Deck    = deck.Name,
                Scrolls = deck.Scrolls
            };

            session.Send(deckCards);
        }
        public static void HandleDeckSave(object packet, Session session)
        {
            if (session.Player.ValidatedDeck.Count == 0)
            {
                LogManager.Write("Player", "Player {0} tried to save a deck without validating it!", session.Player.Id);
                return;
            }

            // scrolls sent are completly ignored by server, saved scrolls from DeckValidate are used instead
            var packetDeckSaveCli = (PacketDeckSave)packet;

            var deck = session.Player.GetDeck(packetDeckSaveCli.Name);
            if (deck != null)
            {
                // update current deck
                deck.Scrolls.Clear();
                foreach (var scrollId in session.Player.ValidatedDeck)
                    deck.Scrolls.Add(session.Player.GetScroll(scrollId));

                deck.Timestamp = (ulong)DateTime.UtcNow.Ticks;
                deck.CalculateResources();
                deck.Save();
            }
            else
            {
                // create new deck
                var newDeck = new Deck(session.Player, AssetManager.GetNewDeckInstanceId(), packetDeckSaveCli.Name,
                    (ulong)DateTime.UtcNow.Ticks, DeckFlags.None);

                foreach (var scrollId in session.Player.ValidatedDeck)
                    newDeck.Scrolls.Add(session.Player.GetScroll(scrollId));

                newDeck.CalculateResources();
                newDeck.Save();

                session.Player.Decks.Add(newDeck);
            }

            session.Player.ValidatedDeck.Clear();
            session.SendOkPacket("DeckSave");
        }
        public static void HandleLibraryView(object packet, Session session)
        {
            var libraryView = new PacketLibraryView()
            {
                ProfileId = session.Player.Id,
                Cards     = session.Player.Scrolls
            };

            session.Send(libraryView);
        }
        public static void HandleDeckValidate(object packet, Session session)
        {
            var packetDeckValidateCli = (PacketDeckValidate)packet;

            if (packetDeckValidateCli.Scolls.Count == 0)
            {
                LogManager.Write("Player", "Player {0} tried to validate a deck with no cards!", session.Player.Id);
                return;
            }

            // make sure player is owner of every scroll
            foreach (var scrollId in packetDeckValidateCli.Scolls)
            {
                if (session.Player.GetScroll(scrollId) == null)
                {
                    LogManager.Write("Player", "Player {0} tried to validate a deck with cards they don't own!", session.Player.Id);
                    return;
                }
            }

            session.Send(new PacketDeckValidate());
            session.Player.ValidatedDeck = packetDeckValidateCli.Scolls;
        }
        public static bool RemovePlayerSession(Session session)
        {
            if (session.Player == null)
                return false;

            if (!IsPlayerOnline(session.Player.Username))
                return false;

            Session removedSession;
            if (sessionMap.TryRemove(session.Player.Username, out removedSession))
            {
                if (!session.Player.HasFlag(PlayerFlags.HidePlayer))
                    SessionCountDecrement();

                return true;
            }

            return false;
        }
        // raw JSON packets are checked here before invoking it's handler
        public static bool HandleRawPacket(Session session, string json)
        {
            DateTime startTime = DateTime.Now;
            string sessionString = session.Player != null ? session.Player.Username : session.IpAddress;

            if (json.Length > MAX_INCOMING_PACKET_SIZE)
            {
                LogManager.Write("Packet Manager", "Received malformed packet (too large: {0} > {1}) from {2}!",
                    json.Length, MAX_INCOMING_PACKET_SIZE, sessionString);
                return false;
            }

            PacketHeader packetHeader;
            try
            {
                packetHeader = JsonConvert.DeserializeObject<PacketHeader>(json);
            }
            catch (Exception exception)
            {
                LogManager.Write("Packet Manager", "An exception occurred while deserialising incoming data (header) from {0}!", sessionString);
                LogManager.Write("Packet Manager", "Exception: {0}", exception.Message);
                return false;
            }

            if (string.IsNullOrEmpty(packetHeader.Msg))
            {
                LogManager.Write("Packet Manager", "Received malformed packet (missing opcode) from {0}!", sessionString);
                return false;
            }

            #region Debug
            /*if (packetHeader.msg != "Ping")
                LogManager.Write("Packet Manager", "DEBUG: Received packet {0} from {1}.", packetHeader.msg, sessionString);*/
            #endregion

            if (!clientToServerPackets.ContainsKey(packetHeader.Msg))
            {
                LogManager.Write("Packet Manager", "Received unknown packet {0} from {1}!", packetHeader.Msg, sessionString);
                return false;
            }

            if (!packetHandlers.ContainsKey(packetHeader.Msg))
            {
                LogManager.Write("Packet Manager", "Received unhandled packet {0} from {1}.", packetHeader.Msg, sessionString);
                return false;
            }

            PacketHandler packetHandler;
            if (!packetHandlers.TryGetValue(packetHeader.Msg, out packetHandler))
                return false;

            PacketInfo packetInfo;
            if (!clientToServerPackets.TryGetValue(packetHeader.Msg, out packetInfo))
                return false;

            if (!HasSessionType(session.Type, packetInfo.SessionType))
            {
                LogManager.Write("Packet Manager", "Failed to handle packet {0}, {1} has invalid session type {1}!",
                    packetHeader.Msg, sessionString, session.Type.ToString());
                return false;
            }

            if (session.Player == null && packetInfo.AuthRequired)
            {
                LogManager.Write("Packet Manager", "Failed to handle packet {0}, {1} isn't authenticated!", packetHeader.Msg, sessionString);
                return false;
            }

            object packet;
            try
            {
                packet = JsonConvert.DeserializeObject(json, packetInfo.Type);
            }
            catch (Exception exception)
            {
                LogManager.Write("Packet Manager", "An exception occurred while deserialising incoming data (body) from {0}!", sessionString);
                LogManager.Write("Packet Manager", "Exception: {0}", exception.Message);
                return false;
            }

            packetHandler.Invoke(packet, session);

            #region Debug
            if (packetHeader.Msg != "Ping")
                LogManager.Write("Packet Manager", "DEBUG: Handled packet {0} to {1} millisecond(s).", packetHeader.Msg, (DateTime.Now - startTime).Milliseconds);
            #endregion

            return true;
        }