/// <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); } } } }
/// <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); }
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); }
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); } }
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); } }
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); }
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()); } }
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); } }
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)); } }
public void Enqueue(ServerPacket packet) { GameLog.DebugFormat("Enqueueing ServerPacket {0}", packet.Opcode); if (!Connected) { Disconnect(); throw new ObjectDisposedException($"cid {ConnectionId}"); } else { ClientState.SendBufferAdd(packet); } }
/// <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); } }
/// <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()); } }
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(); } } }
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(); }
/// <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); } } } }
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()); }
/// <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); }
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()); }
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()); }
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; } }
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; } } }
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); }