Example #1
0
        /// <summary>
        /// Process all the given status ticks for a creature's active statuses.
        /// </summary>
        public void ProcessStatusTicks()
        {
            foreach (var kvp in _currentStatuses)
            {
                GameLog.DebugFormat("OnTick: {0}, {1}", Name, kvp.Value.Name);

                if (kvp.Value.Expired)
                {
                    var removed = RemoveStatus(kvp.Key);
                    if (removed && kvp.Value.Name.ToLower() == "coma")
                    {
                        // Coma removal from expiration means: dead
                        (this as User).OnDeath();
                    }
                    GameLog.DebugFormat($"Status {kvp.Value.Name} has expired: removal was {removed}");
                }

                if (kvp.Value.ElapsedSinceTick >= kvp.Value.Tick)
                {
                    kvp.Value.OnTick();
                    if (this is User)
                    {
                        (this as User).SendStatusUpdate(kvp.Value);
                    }
                }
            }
        }
Example #2
0
 /// <summary>
 /// Atomically update the last time we received a packet (in ticks).
 /// This also automatically marks the client as not idle.
 /// </summary>
 public void UpdateLastReceived(bool updateIdle = true)
 {
     Interlocked.Exchange(ref _lastReceived, DateTime.Now.Ticks);
     if (updateIdle)
     {
         Interlocked.Exchange(ref _idle, 0);
     }
     GameLog.DebugFormat("cid {0}: lastReceived now {1}", ConnectionId, _lastReceived);
 }
Example #3
0
 public virtual void Teleport(ushort mapid, byte x, byte y)
 {
     if (!World.WorldData.ContainsKey <Map>(mapid))
     {
         return;
     }
     Map?.Remove(this);
     GameLog.DebugFormat("Teleporting {0} to {1}.", Name, World.WorldData.Get <Map>(mapid).Name);
     World.WorldData.Get <Map>(mapid).Insert(this, x, y);
 }
Example #4
0
 public void SendAnimation(ServerPacket packet)
 {
     GameLog.DebugFormat("SendAnimation");
     GameLog.DebugFormat("SendAnimation byte format is: {0}", BitConverter.ToString(packet.ToArray()));
     foreach (var user in Map.EntityTree.GetObjects(GetViewport()).OfType <User>())
     {
         var nPacket = (ServerPacket)packet.Clone();
         GameLog.DebugFormat("SendAnimation to {0}", user.Name);
         user.Enqueue(nPacket);
     }
 }
Example #5
0
 public void SendCastLine(ServerPacket packet)
 {
     GameLog.DebugFormat("SendCastLine");
     GameLog.DebugFormat($"SendCastLine byte format is: {BitConverter.ToString(packet.ToArray())}");
     foreach (var user in Map.EntityTree.GetObjects(GetViewport()).OfType <User>())
     {
         var nPacket = (ServerPacket)packet.Clone();
         GameLog.DebugFormat($"SendCastLine to {user.Name}");
         user.Enqueue(nPacket);
     }
 }
Example #6
0
        public virtual void Teleport(string name, byte x, byte y)
        {
            Map targetMap;

            if (string.IsNullOrEmpty(name) || !World.WorldData.TryGetValueByIndex(name, out targetMap))
            {
                return;
            }
            Map?.Remove(this);
            GameLog.DebugFormat("Teleporting {0} to {1}.", Name, targetMap.Name);
            targetMap.Insert(this, x, y);
        }
Example #7
0
 public override void OnClick(User invoker)
 {
     GameLog.DebugFormat("Signpost was clicked");
     if (!IsMessageboard)
     {
         invoker.SendMessage(Message, Message.Length < 1024 ? (byte)MessageTypes.SLATE : (byte)MessageTypes.SLATE_WITH_SCROLLBAR);
     }
     else
     {
         invoker.Enqueue(MessagingController.GetMessageList(invoker.UuidReference, (ushort)Board.Id, 0, true).Packet());
     }
 }
Example #8
0
        public virtual void Show()
        {
            var withinViewport = Map.EntityTree.GetObjects(GetViewport());

            GameLog.DebugFormat("WithinViewport contains {0} objects", withinViewport.Count);

            foreach (var obj in withinViewport)
            {
                GameLog.DebugFormat("Object type is {0} and its name is {1}", obj.GetType(), obj.Name);
                obj.AoiEntry(this);
            }
        }
Example #9
0
 public override void OnClick(User invoker)
 {
     GameLog.DebugFormat("Signpost was clicked");
     if (!IsMessageboard)
     {
         invoker.SendMessage(Message, Message.Length < 1024 ? (byte)MessageTypes.SLATE : (byte)MessageTypes.SLATE_WITH_SCROLLBAR);
     }
     else
     {
         invoker.Enqueue(Board.RenderToPacket(true));
     }
 }
Example #10
0
 public void Enqueue(ServerPacket packet)
 {
     GameLog.DebugFormat("Enqueueing ServerPacket {0}", packet.Opcode);
     if (!Connected)
     {
         Disconnect();
         throw new ObjectDisposedException($"cid {ConnectionId}");
     }
     else
     {
         ClientState.SendBufferAdd(packet);
     }
 }
Example #11
0
    /// <summary>
    /// Check to see if a client is idle
    /// </summary>
    public void CheckIdle()
    {
        var now      = DateTime.Now.Ticks;
        var idletime = new TimeSpan(now - _lastReceived);

        if (idletime.TotalSeconds > Constants.IDLE_TIME)
        {
            GameLog.DebugFormat("cid {0}: idle for {1} seconds, marking as idle", ConnectionId, idletime.TotalSeconds);
            ToggleIdle();
            GameLog.DebugFormat("cid {0}: ToggleIdle: {1}", ConnectionId, IsIdle());
        }
        else
        {
            GameLog.DebugFormat("cid {0}: idle for {1} seconds, not idle", ConnectionId, idletime.TotalSeconds);
        }
    }
Example #12
0
 /// <summary>
 /// Pretty print an object, which is essentially a dump of its properties, at the moment.
 /// </summary>
 /// <param name="obj">The object to be pretty printed, using Hybrasyl.Utility.Logger.</param>
 public static void PrettyPrint(object obj)
 {
     GameLog.DebugFormat("object dump follows");
     try
     {
         foreach (PropertyDescriptor descriptor in TypeDescriptor.GetProperties(obj))
         {
             string name  = descriptor.Name;
             object value = descriptor.GetValue(obj);
             GameLog.DebugFormat("{0} = {1}", name, value);
         }
     }
     catch (Exception e)
     {
         GameLog.ErrorFormat("Couldn't pretty print: {0}", e.ToString());
     }
 }
Example #13
0
 public void Enqueue(ClientPacket packet)
 {
     GameLog.DebugFormat("Enqueueing ClientPacket {0}", packet.Opcode);
     if (!Connected)
     {
         Disconnect();
         throw new ObjectDisposedException($"cid {ConnectionId}");
     }
     else
     {
         ClientState.ReceiveBufferAdd(packet);
         if (!packet.ShouldEncrypt || (packet.ShouldEncrypt && EncryptionKey != null))
         {
             FlushReceiveBuffer();
         }
     }
 }
Example #14
0
    public void SendCallback(IAsyncResult ar)
    {
        ClientState state = (ClientState)ar.AsyncState;
        Client      client;

        GameLog.DebugFormat($"EndSend: SocketConnected: {state.WorkSocket.Connected}, IAsyncResult: Completed: {ar.IsCompleted}, CompletedSynchronously: {ar.CompletedSynchronously}");

        try
        {
            SocketError errorCode;
            var         bytesSent = state.WorkSocket.EndSend(ar, out errorCode);
            if (!GlobalConnectionManifest.ConnectedClients.TryGetValue(state.Id, out client))
            {
                GameLog.ErrorFormat("Send: socket should not exist: cid {0}", state.Id);
                state.WorkSocket.Close();
                state.WorkSocket.Dispose();
                return;
            }

            if (bytesSent == 0 || errorCode != SocketError.Success)
            {
                GameLog.ErrorFormat("cid {0}: disconnected");
                client.Disconnect();
                throw new SocketException((int)errorCode);
            }
        }
        catch (SocketException e)
        {
            Game.ReportException(e);
            GameLog.Error($"Error Code: {e.ErrorCode}, {e.Message}");
            state.WorkSocket.Close();
        }
        catch (ObjectDisposedException)
        {
            //client.Disconnect();
            GameLog.Error($"ObjectDisposedException");
            state.WorkSocket.Close();
        }
        state.SendComplete.Set();
    }
Example #15
0
        /// <summary>
        /// Process all the given status ticks for a creature's active statuses.
        /// </summary>
        public void ProcessStatusTicks()
        {
            foreach (var kvp in _currentStatuses)
            {
                GameLog.DebugFormat("OnTick: {0}, {1}", Name, kvp.Value.Name);

                if (kvp.Value.Expired)
                {
                    var removed = RemoveStatus(kvp.Key);
                    GameLog.DebugFormat($"Status {kvp.Value.Name} has expired: removal was {removed}");
                }

                if (kvp.Value.ElapsedSinceTick >= kvp.Value.Tick)
                {
                    kvp.Value.OnTick();
                    if (this is User)
                    {
                        (this as User).SendStatusUpdate(kvp.Value);
                    }
                }
            }
        }
Example #16
0
    public byte[] GetBytes()
    {
        var buffer = Encoding.ASCII.GetBytes(Name);

        GameLog.DebugFormat("buffer is {0} and Name is {1}", BitConverter.ToString(buffer), Name);

        // X quadrant, offset, Y quadrant, offset, length of the name, the name, plus a 64-bit(?!) ID
        var bytes = new List <Byte>();

        GameLog.DebugFormat("{0}, {1}, {2}, {3}, {4}, mappoint ID is {5}", XQuadrant, XOffset, YQuadrant,
                            YOffset, Name.Length, Id);

        bytes.Add((byte)XQuadrant);
        bytes.Add((byte)XOffset);
        bytes.Add((byte)YQuadrant);
        bytes.Add((byte)YOffset);
        bytes.Add((byte)Name.Length);
        bytes.AddRange(buffer);
        bytes.AddRange(BitConverter.GetBytes(Id));

        return(bytes.ToArray());
    }
Example #17
0
    /// <summary>
    /// Determine whether either heartbeat has "expired" (meaning REAP_HEARTBEAT_INTERVAL has
    /// passed since we received a heartbeat response).
    /// </summary>
    /// <returns>True or false, indicating expiration.</returns>
    public bool IsHeartbeatExpired()
    {
        // If we have no record of sending a heartbeat, obviously it hasn't expired
        if (_tickHeartbeatSent == 0 && _byteHeartbeatSent == 0)
        {
            return(false);
        }

        var tickSpan = new TimeSpan(_tickHeartbeatReceived - _tickHeartbeatSent);
        var byteSpan = new TimeSpan(_byteHeartbeatReceived - _byteHeartbeatSent);

        GameLog.DebugFormat("cid {0}: tick heartbeat elapsed seconds {1}, byte heartbeat elapsed seconds {2}",
                            ConnectionId, tickSpan.TotalSeconds, byteSpan.TotalSeconds);

        if (tickSpan.TotalSeconds > Constants.REAP_HEARTBEAT_INTERVAL ||
            byteSpan.TotalSeconds > Constants.REAP_HEARTBEAT_INTERVAL)
        {
            // DON'T FEAR THE REAPER
            GameLog.InfoFormat("cid {0}: heartbeat expired", ConnectionId);
            return(true);
        }
        return(false);
    }
Example #18
0
    public byte[] GetBytes()
    {
        // Returns the representation of the worldmap as an array of bytes,
        // suitable to passing to a map packet.

        var buffer = Encoding.ASCII.GetBytes(ClientMap);
        var bytes  = new List <Byte> {
            (byte)ClientMap.Length
        };

        bytes.AddRange(buffer);
        bytes.Add((byte)Points.Count);
        bytes.Add(0x00);

        foreach (var mappoint in Points)
        {
            bytes.AddRange(mappoint.GetBytes());
        }

        GameLog.DebugFormat("I am sending the following map packet:");
        GameLog.DebugFormat("{0}", BitConverter.ToString(bytes.ToArray()));

        return(bytes.ToArray());
    }
Example #19
0
        public void DisplayPursuits(User invoker)
        {
            var greeting     = World.Strings.Merchant.FirstOrDefault(x => x.Key == "greeting");
            var optionsCount = 0;
            var options      = new MerchantOptions();

            options.Options = new List <MerchantDialogOption>();
            var merchant = this as Merchant;

            if (merchant?.Jobs.HasFlag(MerchantJob.Vend) ?? false)
            {
                optionsCount += 2;
                options.Options.Add(new MerchantDialogOption {
                    Id = (ushort)MerchantMenuItem.BuyItemMenu, Text = "Buy"
                });
                options.Options.Add(new MerchantDialogOption {
                    Id = (ushort)MerchantMenuItem.SellItemMenu, Text = "Sell"
                });
            }
            if (merchant?.Jobs.HasFlag(MerchantJob.Bank) ?? false)
            {
                optionsCount += 4;
                options.Options.Add(new MerchantDialogOption {
                    Id = (ushort)MerchantMenuItem.DepositGoldMenu, Text = "Deposit Gold"
                });
                options.Options.Add(new MerchantDialogOption {
                    Id = (ushort)MerchantMenuItem.WithdrawGoldMenu, Text = "Withdraw Gold"
                });
                options.Options.Add(new MerchantDialogOption {
                    Id = (ushort)MerchantMenuItem.DepositItemMenu, Text = "Deposit Item"
                });
                options.Options.Add(new MerchantDialogOption {
                    Id = (ushort)MerchantMenuItem.WithdrawItemMenu, Text = "Withdraw Item"
                });
            }
            if (merchant?.Jobs.HasFlag(MerchantJob.Repair) ?? false)
            {
                optionsCount += 2;
                options.Options.Add(new MerchantDialogOption {
                    Id = (ushort)MerchantMenuItem.RepairItemMenu, Text = "Fix Item"
                });
                options.Options.Add(new MerchantDialogOption {
                    Id = (ushort)MerchantMenuItem.RepairAllItems, Text = "Fix All Items"
                });
            }
            if (merchant?.Jobs.HasFlag(MerchantJob.Skills) ?? false)
            {
                optionsCount += 2;
                options.Options.Add(new MerchantDialogOption {
                    Id = (ushort)MerchantMenuItem.LearnSkillMenu, Text = "Learn Skill"
                });
                options.Options.Add(new MerchantDialogOption {
                    Id = (ushort)MerchantMenuItem.ForgetSkillMenu, Text = "Forget Skill"
                });
            }
            if (merchant?.Jobs.HasFlag(MerchantJob.Spells) ?? false)
            {
                optionsCount += 2;
                options.Options.Add(new MerchantDialogOption {
                    Id = (ushort)MerchantMenuItem.LearnSpellMenu, Text = "Learn Secret"
                });
                options.Options.Add(new MerchantDialogOption {
                    Id = (ushort)MerchantMenuItem.ForgetSpellMenu, Text = "Forget Secret"
                });
            }
            if (merchant?.Jobs.HasFlag(MerchantJob.Post) ?? false)
            {
                if (invoker.HasParcels)
                {
                    options.Options.Add(new MerchantDialogOption {
                        Id = (ushort)MerchantMenuItem.ReceiveParcel, Text = "Receive Parcel"
                    });
                    optionsCount++;
                }
                options.Options.Add(new MerchantDialogOption {
                    Id = (ushort)MerchantMenuItem.SendParcelMenu, Text = "Send Parcel"
                });
                optionsCount++;

                /* if user has item named "Letter"
                 *     menupacket.WriteString8("Send Letter");
                 *     menupacket.WriteUInt16((ushort)MerchantMenuItem.SendLetterMenu);
                 *     pursuitCount++;
                 * if user has incoming parcel
                 *     menupacket.WriteString8("Receive Parcel");
                 *     menupacket.WriteUInt16((ushort)MerchantMenuItem.ReceiveParcel);
                 *     pursuitCount++;
                 */
            }

            foreach (var pursuit in Pursuits)
            {
                GameLog.DebugFormat("Pursuit {0}, id {1}", pursuit.Name, pursuit.Id);
                if (pursuit.MenuCheckExpression != string.Empty)
                {
                    var ret = Script.ExecuteAndReturn(pursuit.MenuCheckExpression, invoker);
                    // If the menu check expression returns anything other than true, we don't include the
                    // pursuit on the main menu that is sent to the user
                    if (!ret.CastToBool())
                    {
                        GameLog.ScriptingDebug($"{pursuit.MenuCheckExpression} evaluated to {ret}");
                        continue;
                    }
                }
                options.Options.Add(new MerchantDialogOption {
                    Id = (ushort)pursuit.Id.Value, Text = pursuit.Name
                });
                optionsCount++;
            }

            var packet = new ServerPacketStructures.MerchantResponse()
            {
                MerchantDialogType       = MerchantDialogType.Options,
                MerchantDialogObjectType = MerchantDialogObjectType.Merchant,
                ObjectId     = Id,
                Tile1        = (ushort)(0x4000 + Sprite),
                Color1       = 0,
                Tile2        = (ushort)(0x4000 + Sprite),
                Color2       = 0,
                PortraitType = 1,
                Name         = Name,
                Text         = greeting?.Value ?? string.Empty,
                Options      = options
            };

            invoker.Enqueue(packet.Packet());
        }
Example #20
0
    private void PacketHandler_0x03_Login(Client client, ClientPacket packet)
    {
        var name     = packet.ReadString8();
        var password = packet.ReadString8();

        GameLog.DebugFormat("cid {0}: Login request for {1}", client.ConnectionId, name);

        if (Game.World.WorldData.TryGetAuthInfo(name, out AuthInfo login))
        {
            if (string.IsNullOrEmpty(login.PasswordHash))
            {
                client.LoginMessage("ERROR: Authentication information corrupt [HYB-LOGIN-01]", 3);
                return;
            }

            if (login.VerifyPassword(password))
            {
                GameLog.DebugFormat("cid {0}: password verified for {1}", client.ConnectionId, name);

                if (login.CurrentState == UserState.Redirect && login.StateChangeDuration < 1000)
                {
                    client.LoginMessage("That character is already logging in. Please try again.", 3);
                    return;
                }

                if (login.IsLoggedIn)
                {
                    login.CurrentState = UserState.Disconnected;
                    // Is the user actually in world?
                    if (Game.World.TryGetActiveUser(login.Username, out _))
                    {
                        GameLog.InfoFormat("cid {0}: {1} logging on again, disconnecting previous connection",
                                           client.ConnectionId, name);
                        client.LoginMessage("That character is already online. Please try again.", 3);
                        World.ControlMessageQueue.Add(new HybrasylControlMessage(ControlOpcodes.LogoffUser, name));
                        login.Save();
                        return;
                    }
                }

                // Make sure user can be deserialized without errors
                if (!Game.World.WorldData.TryGetUser(name, out _))
                {
                    // Something bad has happened
                    client.LoginMessage("An unknown error occurred. Please contact Hybrasyl support.", 3);
                    return;
                }

                GameLog.DebugFormat("cid {0} ({1}): logging in", client.ConnectionId, name);
                client.LoginMessage("\0", 0);
                client.SendMessage("Welcome to Hybrasyl!", 3);
                GameLog.DebugFormat("cid {0} ({1}): sending redirect to world", client.ConnectionId, name);

                var redirect = new Redirect(client, this, Game.World, name, client.EncryptionSeed,
                                            client.EncryptionKey);
                GameLog.InfoFormat("cid {0} ({1}): login successful, redirecting to world server",
                                   client.ConnectionId, name);
                login.LastLogin       = DateTime.Now;
                login.LastLoginFrom   = ((IPEndPoint)client.Socket.RemoteEndPoint).Address.ToString();
                login.CurrentState    = UserState.Redirect;
                login.LastStateChange = login.LastLogin;
                login.Save();
                client.Redirect(redirect);
            }
            else
            {
                GameLog.WarningFormat("cid {0} ({1}): password incorrect", client.ConnectionId, name);
                client.LoginMessage("Incorrect password", 3);
                login.LastLoginFailure = DateTime.Now;
                login.LoginFailureCount++;
                login.LastLoginFailureFrom = ((IPEndPoint)client.Socket.RemoteEndPoint).Address.ToString();
                login.CurrentState         = UserState.Login;
                login.LastStateChange      = login.LastLoginFailure;
                login.Save();
            }
        }
        else
        {
            client.LoginMessage("That character does not exist", 3);
            GameLog.InfoFormat("cid {0}: attempt to login as nonexistent character {1}", client.ConnectionId, name);
            return;
        }
    }
Example #21
0
    public void FlushReceiveBuffer()
    {
        lock (ClientState.ReceiveLock)
        {
            try
            {
                ClientPacket packet;
                while (ClientState.ReceiveBufferTake(out packet))
                {
                    if (packet.ShouldEncrypt)
                    {
                        packet.Decrypt(this);
                    }

                    if (packet.Opcode == 0x39 || packet.Opcode == 0x3A)
                    {
                        packet.DecryptDialog();
                    }
                    try
                    {
                        if (Server is Lobby)
                        {
                            GameLog.DebugFormat("Lobby: 0x{0:X2}", packet.Opcode);
                            var handler = (Server as Lobby).PacketHandlers[packet.Opcode];
                            handler.Invoke(this, packet);
                            GameLog.DebugFormat("Lobby packet done");
                            UpdateLastReceived();
                        }
                        else if (Server is Login)
                        {
                            GameLog.Debug($"Login: 0x{packet.Opcode:X2}");
                            var handler = (Server as Login).PacketHandlers[packet.Opcode];
                            handler.Invoke(this, packet);
                            GameLog.DebugFormat("Login packet done");
                            UpdateLastReceived();
                        }
                        else
                        {
                            UpdateLastReceived(packet.Opcode != 0x45 &&
                                               packet.Opcode != 0x75);
                            GameLog.Debug($"Queuing: 0x{packet.Opcode:X2}");
                            //packet.DumpPacket();
                            // Check for throttling
                            var throttleResult = Server.PacketThrottleCheck(this, packet);
                            if (throttleResult == ThrottleResult.OK || throttleResult == ThrottleResult.ThrottleEnd || throttleResult == ThrottleResult.SquelchEnd)
                            {
                                World.MessageQueue.Add(new HybrasylClientMessage(packet, ConnectionId));
                            }
                            else
                            if (packet.Opcode == 0x06)
                            {
                                World.ControlMessageQueue.Add(new HybrasylControlMessage(ControlOpcodes.TriggerRefresh, ConnectionId));
                            }
                        }
                    }
                    catch (Exception e)
                    {
                        Game.ReportException(e);
                        GameLog.ErrorFormat("EXCEPTION IN HANDLING: 0x{0:X2}: {1}", packet.Opcode, e);
                    }
                }
            }
            catch (Exception e)
            {
                Game.ReportException(e);
                Console.WriteLine(e);
                throw;
            }
        }
    }
Example #22
0
        public virtual bool Walk(Xml.Direction direction)
        {
            lock (_lock)
            {
                int       oldX = X, oldY = Y, newX = X, newY = Y;
                Rectangle arrivingViewport  = Rectangle.Empty;
                Rectangle departingViewport = Rectangle.Empty;
                Rectangle commonViewport    = Rectangle.Empty;
                var       halfViewport      = Constants.VIEWPORT_SIZE / 2;
                Warp      targetWarp;

                switch (direction)
                {
                // Calculate the differences (which are, in all cases, rectangles of height 12 / width 1 or vice versa)
                // between the old and new viewpoints. The arrivingViewport represents the objects that need to be notified
                // of this object's arrival (because it is now within the viewport distance), and departingViewport represents
                // the reverse. We later use these rectangles to query the quadtree to locate the objects that need to be
                // notified of an update to their AOI (area of interest, which is the object's viewport calculated from its
                // current position).

                case Xml.Direction.North:
                    --newY;
                    arrivingViewport  = new Rectangle(oldX - halfViewport, newY - halfViewport, Constants.VIEWPORT_SIZE, 1);
                    departingViewport = new Rectangle(oldX - halfViewport, oldY + halfViewport, Constants.VIEWPORT_SIZE, 1);
                    break;

                case Xml.Direction.South:
                    ++newY;
                    arrivingViewport  = new Rectangle(oldX - halfViewport, oldY + halfViewport, Constants.VIEWPORT_SIZE, 1);
                    departingViewport = new Rectangle(oldX - halfViewport, newY - halfViewport, Constants.VIEWPORT_SIZE, 1);
                    break;

                case Xml.Direction.West:
                    --newX;
                    arrivingViewport  = new Rectangle(newX - halfViewport, oldY - halfViewport, 1, Constants.VIEWPORT_SIZE);
                    departingViewport = new Rectangle(oldX + halfViewport, oldY - halfViewport, 1, Constants.VIEWPORT_SIZE);
                    break;

                case Xml.Direction.East:
                    ++newX;
                    arrivingViewport  = new Rectangle(oldX + halfViewport, oldY - halfViewport, 1, Constants.VIEWPORT_SIZE);
                    departingViewport = new Rectangle(oldX - halfViewport, oldY - halfViewport, 1, Constants.VIEWPORT_SIZE);
                    break;
                }
                var isWarp = Map.Warps.TryGetValue(new Tuple <byte, byte>((byte)newX, (byte)newY), out targetWarp);

                // Now that we know where we are going, perform some sanity checks.
                // Is the player trying to walk into a wall, or off the map?

                if (newX >= Map.X || newY >= Map.Y || newX < 0 || newY < 0)
                {
                    Refresh();
                    return(false);
                }
                if (Map.IsWall[newX, newY])
                {
                    Refresh();
                    return(false);
                }
                else
                {
                    // Is the player trying to walk into an occupied tile?
                    foreach (var obj in Map.GetTileContents((byte)newX, (byte)newY))
                    {
                        GameLog.DebugFormat("Collsion check: found obj {0}", obj.Name);
                        if (obj is Creature)
                        {
                            GameLog.DebugFormat("Walking prohibited: found {0}", obj.Name);
                            Refresh();
                            return(false);
                        }
                    }
                    // Is this user entering a forbidden (by level or otherwise) warp?
                    if (isWarp)
                    {
                        if (targetWarp.MinimumLevel > Stats.Level)
                        {
                            Refresh();
                            return(false);
                        }
                        else if (targetWarp.MaximumLevel < Stats.Level)
                        {
                            Refresh();
                            return(false);
                        }
                    }
                }

                // Calculate the common viewport between the old and new position

                commonViewport = new Rectangle(oldX - halfViewport, oldY - halfViewport, Constants.VIEWPORT_SIZE, Constants.VIEWPORT_SIZE);
                commonViewport.Intersect(new Rectangle(newX - halfViewport, newY - halfViewport, Constants.VIEWPORT_SIZE, Constants.VIEWPORT_SIZE));
                GameLog.DebugFormat("Moving from {0},{1} to {2},{3}", oldX, oldY, newX, newY);
                GameLog.DebugFormat("Arriving viewport is a rectangle starting at {0}, {1}", arrivingViewport.X, arrivingViewport.Y);
                GameLog.DebugFormat("Departing viewport is a rectangle starting at {0}, {1}", departingViewport.X, departingViewport.Y);
                GameLog.DebugFormat("Common viewport is a rectangle starting at {0}, {1} of size {2}, {3}", commonViewport.X,
                                    commonViewport.Y, commonViewport.Width, commonViewport.Height);

                X         = (byte)newX;
                Y         = (byte)newY;
                Direction = direction;

                // Objects in the common viewport receive a "walk" (0x0C) packet
                // Objects in the arriving viewport receive a "show to" (0x33) packet
                // Objects in the departing viewport receive a "remove object" (0x0E) packet

                foreach (var obj in Map.EntityTree.GetObjects(commonViewport))
                {
                    if (obj != this && obj is User)
                    {
                        var user = obj as User;
                        GameLog.DebugFormat("Sending walk packet for {0} to {1}", Name, user.Name);
                        var x0C = new ServerPacket(0x0C);
                        x0C.WriteUInt32(Id);
                        x0C.WriteUInt16((byte)oldX);
                        x0C.WriteUInt16((byte)oldY);
                        x0C.WriteByte((byte)direction);
                        x0C.WriteByte(0x00);
                        user.Enqueue(x0C);
                    }
                }
                Map.EntityTree.Move(this);

                foreach (var obj in Map.EntityTree.GetObjects(arrivingViewport).Distinct())
                {
                    obj.AoiEntry(this);
                    AoiEntry(obj);
                }

                foreach (var obj in Map.EntityTree.GetObjects(departingViewport).Distinct())
                {
                    obj.AoiDeparture(this);
                    AoiDeparture(obj);
                }
            }
            HasMoved = true;
            return(true);
        }