예제 #1
0
        /// <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);
        }
예제 #4
0
        /// <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);
        }
예제 #5
0
        /// <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);
        }
예제 #6
0
        /// <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);
        }
예제 #7
0
        /// <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);
        }
예제 #8
0
        /// <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);
        }
예제 #9
0
        /// <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)),
            });
        }
예제 #10
0
        /// <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);
        }
예제 #11
0
        /// <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);
        }