public void InsertSignpost(Objects.Signpost post) { World.Insert(post); Insert(post, post.X, post.Y); Signposts[new Tuple <byte, byte>(post.X, post.Y)] = post; GameLog.InfoFormat("Inserted signpost {0}@{1},{2}", post.Map.Name, post.X, post.Y); }
public void Redirect(Redirect redirect, bool isLogoff = false) { GameLog.InfoFormat("Processing redirect"); GlobalConnectionManifest.RegisterRedirect(this, redirect); GameLog.InfoFormat("Redirect: cid {0}", this.ConnectionId); GameLog.Info($"Redirect EncryptionKey is {Encoding.ASCII.GetString(redirect.EncryptionKey)}"); if (isLogoff) { GlobalConnectionManifest.DeregisterClient(this); } redirect.Destination.ExpectedConnections.TryAdd(redirect.Id, redirect); var endPoint = Socket.RemoteEndPoint as IPEndPoint; byte[] addressBytes = IPAddress.IsLoopback(endPoint.Address) ? IPAddress.Loopback.GetAddressBytes() : Game.IpAddress.GetAddressBytes(); Array.Reverse(addressBytes); var x03 = new ServerPacket(0x03); x03.Write(addressBytes); x03.WriteUInt16((ushort)redirect.Destination.Port); x03.WriteByte((byte)(redirect.EncryptionKey.Length + Encoding.ASCII.GetBytes(redirect.Name).Length + 7)); x03.WriteByte(redirect.EncryptionSeed); x03.WriteByte((byte)redirect.EncryptionKey.Length); x03.Write(redirect.EncryptionKey); x03.WriteString8(redirect.Name); x03.WriteUInt32(redirect.Id); Thread.Sleep(100); Enqueue(x03); }
public Client(Socket socket, Server server) { ClientState = new ClientState(socket); Server = server; GameLog.InfoFormat("Connection {0} from {1}:{2}", ConnectionId, ((IPEndPoint)Socket.RemoteEndPoint).Address.ToString(), ((IPEndPoint)Socket.RemoteEndPoint).Port); if (server is Lobby) { EncryptionKey = Game.Config.ApiEndpoints.EncryptionEndpoint != null?GlobalConnectionManifest.RequestEncryptionKey(Game.Config.ApiEndpoints.EncryptionEndpoint.Url, ((IPEndPoint)socket.RemoteEndPoint).Address) : Encoding.ASCII.GetBytes("UrkcnItnI"); GameLog.InfoFormat($"EncryptionKey is {Encoding.ASCII.GetString(EncryptionKey)}"); var valid = Game.Config.ApiEndpoints.ValidationEndpoint != null?GlobalConnectionManifest.ValidateEncryptionKey(Game.Config.ApiEndpoints.ValidationEndpoint.Url, new ServerToken { Ip = ((IPEndPoint)socket.RemoteEndPoint).Address.ToString(), Seed = EncryptionKey }) : true; if (!valid) { GameLog.ErrorFormat("Invalid key from {IP}", ((IPEndPoint)Socket.RemoteEndPoint).Address.ToString()); socket.Disconnect(true); } } EncryptionKeyTable = new byte[1024]; _lastReceived = DateTime.Now.Ticks; GlobalConnectionManifest.RegisterClient(this); ConnectedSince = DateTime.Now.Ticks; }
public static void DeregisterClient(Client client) { ConnectedClients.TryRemove(client.ConnectionId, out Client _); GameLog.InfoFormat("Deregistering {0}", client.ConnectionId); // Send a control message to clean up after World users; Lobby and Login handle themselves if (client.ServerType == ServerTypes.World) { if (!WorldClients.TryRemove(client.ConnectionId, out Client _)) { GameLog.Error("Couldn't deregister cid {id}", client.ConnectionId); } try { if (!World.ControlMessageQueue.IsCompleted) { World.ControlMessageQueue.Add(new HybrasylControlMessage(ControlOpcodes.CleanupUser, CleanupType.ByConnectionId, client.ConnectionId)); } } catch (InvalidOperationException e) { Game.ReportException(e); if (!World.ControlMessageQueue.IsCompleted) { GameLog.ErrorFormat("Connection {id}: DeregisterClient failed", client.ConnectionId); } } } }
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 (!World.PlayerExists(name)) { client.LoginMessage("That character does not exist", 3); GameLog.InfoFormat("cid {0}: attempt to login as nonexistent character {1}", client.ConnectionId, name); return; } if (World.TryGetUser(name, out User loginUser)) { if (loginUser.VerifyPassword(password)) { GameLog.DebugFormat("cid {0}: password verified for {1}", client.ConnectionId, name); if (Game.World.WorldData.ContainsKey <User>(name)) { 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)); 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); loginUser.Login.LastLogin = DateTime.Now; loginUser.Login.LastLoginFrom = ((IPEndPoint)client.Socket.RemoteEndPoint).Address.ToString(); loginUser.Save(); client.Redirect(redirect); } else { GameLog.WarningFormat("cid {0} ({1}): password incorrect", client.ConnectionId, name); client.LoginMessage("Incorrect password", 3); loginUser.Login.LastLoginFailure = DateTime.Now; loginUser.Login.LoginFailureCount++; loginUser.Save(); } } else { // Something bad has happened client.LoginMessage("An unknown error occurred. Please contact Hybrasyl support.", 3); } }
// General notes about this god awful packet: /* Offsets: * 00-0F: no human body + pants * 10-1F: male human body + pants * 20-2F: female human body, no pants * 30-3F: male spirit + pants * 40-4F: female spirit, no pants * 50-5F: invisible male body + pants * 60-6F: invisible female body, no pants * 70-7F: male doll body + pants * 80-8F: male mounted body + pants * 90-9F: female mounted body, no pants * A0-FF: no human body + pants */ internal ServerPacket Packet() { ServerPacket packet = new ServerPacket(OpCode); packet.WriteUInt16(X); packet.WriteUInt16(Y); packet.WriteByte((byte)Direction); packet.WriteUInt32(Id); packet.WriteUInt16(Helmet); GameLog.InfoFormat($"Gender is {Gender}"); if (!DisplayAsMonster) { packet.WriteByte((byte)(((byte)Gender * 16) + BodySpriteOffset)); packet.WriteUInt16(Armor); packet.WriteByte(Boots); packet.WriteUInt16(Armor); packet.WriteByte(Shield); packet.WriteUInt16(Weapon); packet.WriteByte(HairColor); packet.WriteByte(BootsColor); packet.WriteByte(FirstAccColor); packet.WriteUInt16(FirstAcc); packet.WriteByte(SecondAccColor); packet.WriteUInt16(SecondAcc); packet.WriteByte(ThirdAccColor); packet.WriteUInt16(ThirdAcc); packet.WriteByte((byte)LanternSize); packet.WriteByte((byte)RestPosition); packet.WriteUInt16(Overcoat); packet.WriteByte(OvercoatColor); packet.WriteByte((byte)SkinColor); packet.WriteBoolean(Invisible); packet.WriteByte(FaceShape); } else { packet.WriteUInt16(MonsterSprite); packet.WriteByte(HairColor); packet.WriteByte(BootsColor); // Unknown packet.WriteByte(0x00); packet.WriteByte(0x00); packet.WriteByte(0x00); packet.WriteByte(0x00); packet.WriteByte(0x00); packet.WriteByte(0x00); } packet.WriteByte((byte)NameStyle); packet.WriteString8(Name ?? string.Empty); packet.WriteString8(GroupName ?? string.Empty); return(packet); }
public bool StartExchange() { GameLog.InfoFormat("Starting exchange between {0} and {1}", _source.Name, _target.Name); _active = true; _source.Condition.InExchange = true; _target.Condition.InExchange = true; _source.ActiveExchange = this; _target.ActiveExchange = this; // Send "open window" packet to both clients _target.SendExchangeInitiation(_source); _source.SendExchangeInitiation(_target); return(true); }
public Lobby(int port) : base(port) { GameLog.InfoFormat("LobbyConstructor: port is {0}", port); PacketHandlers = new LobbyPacketHandler[256]; for (int i = 0; i < 256; ++i) { PacketHandlers[i] = (c, p) => GameLog.WarningFormat("Lobby: Unhandled opcode 0x{0:X2}", p.Opcode); } PacketHandlers[0x00] = PacketHandler_0x00_ClientVersion; PacketHandlers[0x57] = PacketHandler_0x57_ServerTable; }
// Chart for all error password-related error codes were provided by kojasou@ on // https://github.com/hybrasyl/server/pull/11. private void PacketHandler_0x26_ChangePassword(Client client, ClientPacket packet) { var name = packet.ReadString8(); var currentPass = packet.ReadString8(); // Clientside validation ensures that the same string is typed twice for the new // password, and the new password is only sent to the server once. We can assume // that they matched if 0x26 request is sent from the client. var newPass = packet.ReadString8(); // TODO: REDIS User player; if (!World.TryGetUser(name, out player)) { client.LoginMessage(GetPasswordError(0x0E), 0x0E); GameLog.InfoFormat("cid {0}: Password change attempt on nonexistent player {1}", client.ConnectionId, name); return; } if (player.VerifyPassword(currentPass)) { // Check if the password is valid. byte err = 0x00; if (ValidPassword(newPass, out err)) { player.Password.Hash = HashPassword(newPass); player.Password.LastChanged = DateTime.Now; player.Password.LastChangedFrom = ((IPEndPoint)client.Socket.RemoteEndPoint).Address.ToString(); player.Save(); // Let the user know the good news. client.LoginMessage("Your password has been changed successfully.", 0x0); GameLog.InfoFormat("Password successfully changed for `{0}`", name); } else { client.LoginMessage(GetPasswordError(err), err); GameLog.ErrorFormat("Invalid new password proposed during password change attempt for `{0}`", name); } } // The current password is incorrect. Don't allow any changes to happen. else { client.LoginMessage(GetPasswordError(0x0F), 0x0F); GameLog.ErrorFormat("Invalid current password during password change attempt for `{0}`", name); } }
public UserGroup(User founder) { Members = new List <User>(); ClassCount = new Dictionary <Xml.Class, uint>(); foreach (var cl in Enum.GetValues(typeof(Xml.Class)).Cast <Xml.Class>()) { ClassCount[cl] = 0; } GameLog.InfoFormat("Creating new group with {0} as founder.", founder.Name); // Distribute full experience to everyone with a bonus if a member of each // class is present. ExperienceDistributionFunc = Distribution_AllClassBonus; Add(founder); CreatedOn = DateTime.Now; }
public void StartListening() { Listener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); Listener.Bind(new IPEndPoint(IPAddress.Any, Port)); Active = true; Listener.Listen(100); GameLog.InfoFormat("Starting TcpListener: {0}:{1}", IPAddress.Any.ToString(), Port); while (true) { if (StopToken.IsCancellationRequested) { Active = false; return; } AllDone.Reset(); Listener.BeginAccept(new AsyncCallback(AcceptConnection), Listener); AllDone.WaitOne(); } }
private void PacketHandler_0x57_ServerTable(Client client, ClientPacket packet) { var mismatch = packet.ReadByte(); if (mismatch == 1) { var x56 = new ServerPacket(0x56); x56.WriteUInt16((ushort)Game.ServerTable.Length); x56.Write(Game.ServerTable); GameLog.InfoFormat("ServerTable: Sent: {0}", BitConverter.ToString(x56.ToArray())); client.Enqueue(x56); } else { var server = packet.ReadByte(); var redirect = new Redirect(client, this, Game.Login, "socket", client.EncryptionSeed, client.EncryptionKey); client.Redirect(redirect); } }
public Login(int port) : base(port) { GameLog.InfoFormat("LoginConstructor: port is {0}", port); PacketHandlers = new LoginPacketHandler[256]; for (int i = 0; i < 256; ++i) { PacketHandlers[i] = (c, p) => GameLog.WarningFormat("Login: Unhandled opcode 0x{0:X2}", p.Opcode); } PacketHandlers[0x02] = PacketHandler_0x02_CreateA; PacketHandlers[0x03] = PacketHandler_0x03_Login; PacketHandlers[0x04] = PacketHandler_0x04_CreateB; PacketHandlers[0x10] = PacketHandler_0x10_ClientJoin; PacketHandlers[0x26] = PacketHandler_0x26_ChangePassword; PacketHandlers[0x4B] = PacketHandler_0x4B_RequestNotification; PacketHandlers[0x68] = PacketHandler_0x68_RequestHomepage; }
/// <summary> /// Confirm the exchange. Once both sides confirm, perform the exchange. /// </summary> /// <returns>Boolean indicating success.</returns> public void ConfirmExchange(User requestor) { if (_source == requestor) { GameLog.InfoFormat("Exchange: source ({0}) confirmed", _source.Name); _sourceConfirmed = true; _target.SendExchangeConfirmation(false); } if (_target == requestor) { GameLog.InfoFormat("Exchange: target ({0}) confirmed", _target.Name); _targetConfirmed = true; _source.SendExchangeConfirmation(false); } if (_sourceConfirmed && _targetConfirmed) { GameLog.Info("Exchange: Both sides confirmed"); _source.SendExchangeConfirmation(); _target.SendExchangeConfirmation(); PerformExchange(); } }
public static void DeregisterClient(Client client) { ((IDictionary)ConnectedClients).Remove(client.ConnectionId); GameLog.InfoFormat("Deregistering {0}", client.ConnectionId); // Send a control message to clean up after World users; Lobby and Login handle themselves if (client.ServerType == ServerTypes.World) { ((IDictionary)WorldClients).Remove(client.ConnectionId); // This will also handle removing the user from WorldClients if necessary try { World.ControlMessageQueue.Add(new HybrasylControlMessage(ControlOpcodes.CleanupUser, client.ConnectionId)); } catch (InvalidOperationException) { if (!World.ControlMessageQueue.IsCompleted) { GameLog.ErrorFormat("Connection {id}: DeregisterClient failed", client.ConnectionId); } } } }
/// <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); }