/// <summary> /// Returns the most appropriate handler for the specified packet type. /// </summary> /// <param name="packet">The packet to select the handler for.</param> /// <returns>An instance of an <see cref="IHandler"/> implementaion.</returns> public IHandler SelectForPacket(IIncomingPacket packet) { packet.ThrowIfNull(nameof(packet)); var packetType = packet.GetType(); if (this.handlersMap.TryGetValue(packetType, out IHandler handler)) { return(handler); } foreach (var iType in packetType.GetInterfaces()) { if (this.handlersMap.TryGetValue(iType, out handler)) { return(handler); } } this.logger.Warning($"No handler found for type {packetType}."); return(null); }
/// <summary> /// Handles the contents of a network message. /// </summary> /// <param name="incomingPacket">The packet to handle.</param> /// <param name="client">A reference to the client from where this request originated from, for context.</param> /// <returns>A collection of <see cref="IOutboundPacket"/>s that compose that synchronous response, if any.</returns> public override IEnumerable <IOutboundPacket> HandleRequest(IIncomingPacket incomingPacket, IClient client) { incomingPacket.ThrowIfNull(nameof(incomingPacket)); client.ThrowIfNull(nameof(client)); if (!(incomingPacket is IBytesInfo debugInfo)) { this.Logger.LogError($"Expected packet info of type {nameof(IBytesInfo)} but got {incomingPacket.GetType().Name}."); return(null); } var sb = new StringBuilder(); foreach (var b in debugInfo.Bytes) { sb.AppendFormat("{0:x2} ", b); } this.Logger.LogInformation($"Default handler drained packet with content:\n\n{sb}"); return(null); }
/// <summary> /// Handles the contents of a network message. /// </summary> /// <param name="incomingPacket">The packet to handle.</param> /// <param name="client">A reference to the client from where this request originated from, for context.</param> /// <returns>A collection of <see cref="IOutboundPacket"/>s that compose that synchronous response, if any.</returns> public override IEnumerable <IOutboundPacket> HandleRequest(IIncomingPacket incomingPacket, IClient client) { incomingPacket.ThrowIfNull(nameof(incomingPacket)); client.ThrowIfNull(nameof(client)); if (!(incomingPacket is IGameLogInInfo loginInfo)) { this.Logger.Error($"Expected packet info of type {nameof(IGameLogInInfo)} but got {incomingPacket.GetType().Name}."); return(null); } if (!(client.Connection is ISocketConnection socketConnection)) { this.Logger.Error($"Expected a {nameof(ISocketConnection)} got a {client.Connection.GetType().Name}."); return(null); } // Associate the xTea key to allow future validate packets from this connection. socketConnection.SetupAuthenticationKey(loginInfo.XteaKey); if (loginInfo.ClientVersion != this.ApplicationContext.Options.SupportedClientVersion.Numeric) { this.Logger.Information($"Client attempted to connect with version: {loginInfo.ClientVersion}, OS: {loginInfo.ClientOs}. Expected version: {this.ApplicationContext.Options.SupportedClientVersion.Numeric}."); // TODO: hardcoded messages. return(new GameServerDisconnectPacket($"You need client version {this.ApplicationContext.Options.SupportedClientVersion.Description} to connect to this server.").YieldSingleItem()); } if (this.Game.WorldInfo.Status == WorldState.Loading) { return(new GameServerDisconnectPacket("The game is just starting.\nPlease try again in a few minutes.").YieldSingleItem()); } using var unitOfWork = this.ApplicationContext.CreateNewUnitOfWork(); if (!(unitOfWork.Accounts.FindOne(a => a.Number == loginInfo.AccountNumber && a.Password.Equals(loginInfo.Password)) is AccountEntity account)) { // TODO: hardcoded messages. return(new GameServerDisconnectPacket("The account number and password combination is invalid.").YieldSingleItem()); } if (!(unitOfWork.Characters.FindOne(c => c.AccountId.Equals(account.Id) && c.Name.Equals(loginInfo.CharacterName)) is CharacterEntity character)) { // TODO: hardcoded messages. return(new GameServerDisconnectPacket("The character selected was not found in this account.").YieldSingleItem()); } // Check bannishment. if (account.Banished) { // Lift if time is up if (account.Banished && account.BanishedUntil > DateTimeOffset.UtcNow) { // TODO: hardcoded messages. return(new GameServerDisconnectPacket("Your account is bannished.").YieldSingleItem()); } else { account.Banished = false; } } else if (account.Deleted) { // TODO: hardcoded messages. return(new GameServerDisconnectPacket("Your account is disabled.\nPlease contact us for more information.").YieldSingleItem()); } else if (unitOfWork.Characters.FindOne(c => c.IsOnline && c.AccountId == account.Id && !c.Name.Equals(loginInfo.CharacterName)) is CharacterEntity otherCharacterOnline) { // TODO: hardcoded messages. // return new GameServerDisconnectPacket("Another character in your account is online.").YieldSingleItem(); } else if (this.Game.WorldInfo.Status == WorldState.Closed) { // TODO: hardcoded messages. // Check if game is open to the public. return(new GameServerDisconnectPacket("This game world is not open to the public yet.\nCheck your access or the news on our webpage.").YieldSingleItem()); } // Set player status to online. character.IsOnline = true; // TODO: possibly a friendly name conversion here. client.Information.Type = Enum.IsDefined(typeof(AgentType), loginInfo.ClientOs) ? (AgentType)loginInfo.ClientOs : AgentType.Windows; client.Information.Version = loginInfo.ClientVersion.ToString(); this.Game.LogPlayerIn(client, character); // save any changes to the entities. unitOfWork.Complete(); return(null); }
/// <summary> /// Handles the contents of a network message. /// </summary> /// <param name="incomingPacket">The packet to handle.</param> /// <param name="client">A reference to the client from where this request originated from, for context.</param> /// <returns>A collection of <see cref="IOutboundPacket"/>s that compose that synchronous response, if any.</returns> public override IEnumerable <IOutboundPacket> HandleRequest(IIncomingPacket incomingPacket, IClient client) { incomingPacket.ThrowIfNull(nameof(incomingPacket)); client.ThrowIfNull(nameof(client)); if (!(incomingPacket is IActionWithoutContentInfo actionInfo)) { this.Logger.Error($"Expected packet info of type {nameof(IActionWithoutContentInfo)} but got {incomingPacket.GetType().Name}."); return(null); } if (!(this.CreatureFinder.FindCreatureById(client.PlayerId) is IPlayer player)) { this.Logger.Warning($"Client's associated player could not be found. [Id={client.PlayerId}]"); return(null); } switch (actionInfo.Action) { case IncomingPacketType.AutoMoveCancel: this.Game.CancelPlayerActions(player, typeof(MovementOperation), async: true); break; case IncomingPacketType.HeartbeatResponse: // NO-OP. break; case IncomingPacketType.Heartbeat: this.Game.SendHeartbeatResponse(player); break; case IncomingPacketType.LogOut: this.Game.LogPlayerOut(player); break; case IncomingPacketType.StartOutfitChange: // this.Game.RequestPlayerOutfitChange(player); break; case IncomingPacketType.StopAllActions: this.Game.CancelPlayerActions(player, null, async: true); break; } return(null); }
/// <summary> /// Handles the contents of a network message. /// </summary> /// <param name="incomingPacket">The packet to handle.</param> /// <param name="client">A reference to the client from where this request originated from, for context.</param> /// <returns>A collection of <see cref="IOutboundPacket"/>s that compose that synchronous response, if any.</returns> public override IEnumerable <IOutboundPacket> HandleRequest(IIncomingPacket incomingPacket, IClient client) { incomingPacket.ThrowIfNull(nameof(incomingPacket)); client.ThrowIfNull(nameof(client)); if (!(incomingPacket is ILookAtInfo lookAtInfo)) { this.Logger.LogError($"Expected packet info of type {nameof(ILookAtInfo)} but got {incomingPacket.GetType().Name}."); return(null); } if (!(this.CreatureFinder.FindCreatureById(client.PlayerId) is IPlayer player)) { this.Logger.LogWarning($"Client's associated player could not be found. [Id={client.PlayerId}]"); return(null); } this.Game.LookAt(lookAtInfo.ThingId, lookAtInfo.Location, lookAtInfo.StackPosition, player); return(null); }
/// <summary> /// Handles the contents of a network message. /// </summary> /// <param name="incomingPacket">The packet to handle.</param> /// <param name="client">A reference to the client from where this request originated from, for context.</param> /// <returns>A collection of <see cref="IOutboundPacket"/>s that compose that synchronous response, if any.</returns> public override IEnumerable <IOutboundPacket> HandleRequest(IIncomingPacket incomingPacket, IClient client) { incomingPacket.ThrowIfNull(nameof(incomingPacket)); client.ThrowIfNull(nameof(client)); if (!(incomingPacket is IWalkOnDemandInfo walkOnDemandInfo)) { this.Logger.Error($"Expected packet info of type {nameof(IWalkOnDemandInfo)} but got {incomingPacket.GetType().Name}."); return(null); } if (!(this.CreatureFinder.FindCreatureById(client.PlayerId) is IPlayer player)) { this.Logger.Warning($"Client's associated player could not be found. [Id={client.PlayerId}]"); return(null); } this.Game.ResetCreatureStaticWalkPlan(player, new[] { walkOnDemandInfo.Direction }); return(null); }
/// <summary> /// Handles the contents of a network message. /// </summary> /// <param name="incomingPacket">The packet to handle.</param> /// <param name="client">A reference to the client from where this request originated from, for context.</param> /// <returns>A collection of <see cref="IOutboundPacket"/>s that compose that synchronous response, if any.</returns> public override IEnumerable <IOutboundPacket> HandleRequest(IIncomingPacket incomingPacket, IClient client) { incomingPacket.ThrowIfNull(nameof(incomingPacket)); client.ThrowIfNull(nameof(client)); if (!(incomingPacket is IAttackInfo attackInfo)) { this.Logger.Error($"Expected packet info of type {nameof(IAttackInfo)} but got {incomingPacket.GetType().Name}."); return(null); } if (!(this.CreatureFinder.FindCreatureById(client.PlayerId) is IPlayer player)) { this.Logger.Warning($"Client's associated player could not be found. [Id={client.PlayerId}]"); return(null); } if (player is ICombatant playerAsCombatant) { var combatantTarget = this.CreatureFinder.FindCreatureById(attackInfo.TargetCreatureId) as ICombatant; // First stop following (formally) if we are. this.Game.SetCombatantFollowTarget(playerAsCombatant, null); this.Game.SetCombatantAttackTarget(playerAsCombatant, combatantTarget); } return(null); }
/// <summary> /// Handles the contents of a network message. /// </summary> /// <param name="incomingPacket">The packet to handle.</param> /// <param name="client">A reference to the client from where this request originated from, for context.</param> /// <returns>A collection of <see cref="IOutboundPacket"/>s that compose that synchronous response, if any.</returns> public override IEnumerable <IOutboundPacket> HandleRequest(IIncomingPacket incomingPacket, IClient client) { incomingPacket.ThrowIfNull(nameof(incomingPacket)); client.ThrowIfNull(nameof(client)); if (!(incomingPacket is ITurnOnDemandInfo turnOnDemandInfo)) { this.Logger.LogError($"Expected packet info of type {nameof(ITurnOnDemandInfo)} but got {incomingPacket.GetType().Name}."); return(null); } if (!(this.CreatureFinder.FindCreatureById(client.PlayerId) is IPlayer player)) { this.Logger.LogWarning($"Client's associated player could not be found. [Id={client.PlayerId}]"); return(null); } // TODO: cancel other pending actions. this.Game.DoCreatureTurnAsync(player.Id, player, turnOnDemandInfo.Direction); return(null); }
/// <summary> /// Handles the contents of a network message. /// </summary> /// <param name="incomingPacket">The packet to handle.</param> /// <param name="client">A reference to the client from where this request originated from, for context.</param> /// <returns>A collection of <see cref="IOutboundPacket"/>s that compose that synchronous response, if any.</returns> public override IEnumerable <IOutboundPacket> HandleRequest(IIncomingPacket incomingPacket, IClient client) { incomingPacket.ThrowIfNull(nameof(incomingPacket)); client.ThrowIfNull(nameof(client)); if (!(incomingPacket is IGatewayLoginInfo accountLoginInfo)) { this.Logger.Error($"Expected packet info of type {nameof(IGatewayLoginInfo)} but got {incomingPacket.GetType().Name}."); return(null); } if (!(client.Connection is ISocketConnection socketConnection)) { this.Logger.Error($"Expected a {nameof(ISocketConnection)} got a {client.Connection.GetType().Name}."); return(null); } // Associate the xTea key to allow future validate packets from this connection. socketConnection.SetupAuthenticationKey(accountLoginInfo.XteaKey); if (accountLoginInfo.ClientVersion != this.ApplicationContext.Options.SupportedClientVersion.Numeric) { this.Logger.Information($"Client attempted to connect with version: {accountLoginInfo.ClientVersion}, OS: {accountLoginInfo.ClientOs}. Expected version: {this.ApplicationContext.Options.SupportedClientVersion.Numeric}."); // TODO: hardcoded messages. return(new GatewayServerDisconnectPacket($"You need client version {this.ApplicationContext.Options.SupportedClientVersion.Description} to connect to this server.").YieldSingleItem()); } using var unitOfWork = this.ApplicationContext.CreateNewUnitOfWork(); // validate credentials. if (!uint.TryParse(accountLoginInfo.AccountName, out uint accountNumber) || !(unitOfWork.Accounts.FindOne(a => a.Number == accountNumber && a.Password.Equals(accountLoginInfo.Password)) is AccountEntity account)) { // TODO: hardcoded messages. return(new GatewayServerDisconnectPacket("Please enter a valid account number and password.").YieldSingleItem()); } var charactersInAccount = unitOfWork.Characters.FindMany(p => p.AccountId == account.Id); if (!charactersInAccount.Any()) { // TODO: hardcoded messages. return(new GatewayServerDisconnectPacket($"You don't have any characters in your account.\nPlease create a new character in our web site: {this.ApplicationContext.Options.WebsiteUrl}").YieldSingleItem()); } var charList = new List <CharacterInfo>(); foreach (var character in charactersInAccount) { charList.Add(new CharacterInfo() { Name = character.Name, Ip = IPAddress.Parse(this.ApplicationContext.Options.World.IpAddress).GetAddressBytes(), Port = this.ApplicationContext.Options.World.Port.Value, World = this.ApplicationContext.Options.World.Name, }); } return(new IOutboundPacket[] { new MessageOfTheDayPacket(this.ApplicationContext.Options.World.MessageOfTheDay), new CharacterListPacket(charList, (ushort)(account.PremiumDays + account.TrialOrBonusPremiumDays)), }); }
/// <summary> /// Handles the contents of a network message. /// </summary> /// <param name="incomingPacket">The packet to handle.</param> /// <param name="client">A reference to the client from where this request originated from, for context.</param> /// <returns>A collection of <see cref="IOutboundPacket"/>s that compose that synchronous response, if any.</returns> public override IEnumerable <IOutboundPacket> HandleRequest(IIncomingPacket incomingPacket, IClient client) { incomingPacket.ThrowIfNull(nameof(incomingPacket)); client.ThrowIfNull(nameof(client)); if (!(incomingPacket is IModesInfo modesInfo)) { this.Logger.Error($"Expected packet info of type {nameof(IModesInfo)} but got {incomingPacket.GetType().Name}."); return(null); } if (!(this.CreatureFinder.FindCreatureById(client.PlayerId) is IPlayer player)) { this.Logger.Warning($"Client's associated player could not be found. [Id={client.PlayerId}]"); return(null); } if (player is ICombatant combatant) { this.Game.SetCombatantModes(combatant, modesInfo.FightMode, modesInfo.ChaseMode, modesInfo.SafeModeOn); } return(null); }
/// <summary> /// Handles the contents of a network message. /// </summary> /// <param name="incomingPacket">The packet to handle.</param> /// <param name="client">A reference to the client from where this request originated from, for context.</param> /// <returns>A collection of <see cref="IOutboundPacket"/>s that compose that synchronous response, if any.</returns> public override IEnumerable <IOutboundPacket> HandleRequest(IIncomingPacket incomingPacket, IClient client) { incomingPacket.ThrowIfNull(nameof(incomingPacket)); client.ThrowIfNull(nameof(client)); if (!(incomingPacket is ISpeechInfo speechInfo)) { this.Logger.LogError($"Expected packet info of type {nameof(ISpeechInfo)} but got {incomingPacket.GetType().Name}."); return(null); } this.Game.DoCreatureSpeechAsync(client.PlayerId, speechInfo.SpeechType, speechInfo.ChannelType, speechInfo.Content, speechInfo.Receiver); return(null); }