示例#1
0
文件: Handshake.cs 项目: Tobi406/SM3
 public void Read(IPacketReader reader)
 {
     ProtocolVersion = reader.ReadVarInt();
     ServerAddress   = reader.ReadString().ToString();
     Port            = reader.ReadInt16();
     NextStage       = (MCConnectionStage)reader.ReadVarInt();
 }
        public static void WriteAddonInfo(this IAuthHandler authHandler, IPacketReader inPacket, IPacketWriter outPacket, int size)
        {
            int count = 0x100; // arbitrary number

            if (Authenticator.ClientBuild >= 9464)
            {
                count = inPacket.ReadInt32(); // addon count
            }
            int i = 0;

            while (inPacket.Position < size && i < count)
            {
                string addonName   = inPacket.ReadString();
                bool   enabled     = inPacket.ReadBool();
                uint   filecrc     = inPacket.ReadUInt32();
                uint   urlcrc      = inPacket.ReadUInt32();
                bool   requireskey = filecrc != 0x1C776D01u && filecrc != 0x4C1C776Du; // offical crcs

                outPacket.WriteUInt8(2);                                               // blizzard
                outPacket.WriteBool(true);                                             // enabled
                outPacket.WriteBool(requireskey);
                if (requireskey)
                {
                    outPacket.Write(AddonPublicKey);
                }
                outPacket.WriteUInt32(0); // use addon url file
                outPacket.WriteUInt8(0);  // addon url filename, cstring

                i++;
            }

            outPacket.WriteUInt32(0); // banned addon count
        }
示例#3
0
        public void HandleAuthSession(ref IPacketReader packet, ref IWorldManager manager)
        {
            Authenticator.PacketCrypt.Initialised = true;

            packet.Position += 8; // client version, session id
            string name = packet.ReadString().ToUpper();

            packet.Position += 4 + 4 + 20 + 4 + 4; // realm type, salt, encrypted password
            int addonsize = packet.ReadInt32();

            Account account = new Account(name);

            account.Load <Character>();
            manager.Account = account;

            // enable addons
            var addonPacketInfo     = new PacketReader(this.GetAddonInfo(packet), false);
            var addonPacketResponse = new PacketWriter(Sandbox.Instance.Opcodes[global::Opcodes.SMSG_ADDON_INFO], "SMSG_ADDON_INFO");

            this.WriteAddonInfo(addonPacketInfo, addonPacketResponse, addonsize);
            manager.Send(addonPacketResponse);

            PacketWriter writer = new PacketWriter(Sandbox.Instance.Opcodes[global::Opcodes.SMSG_AUTH_RESPONSE], "SMSG_AUTH_RESPONSE");

            writer.WriteUInt8(0x0C); // AUTH_OK
            writer.WriteUInt32(0);
            writer.WriteUInt8(0);
            writer.WriteUInt32(0);
            writer.WriteUInt8(Authenticator.ExpansionLevel.Clamp(1, 2)); // Expansion level
            manager.Send(writer);
        }
示例#4
0
        public void Read(IPacketReader reader)
        {
            Identifier = reader.ReadString().ToString();
            var data = reader.ReadBytes((int)reader.Buffer.Length);

            Data = new Memory <byte>(new byte[data.Length]);
            data.CopyTo(Data.Span);
        }
示例#5
0
 public void Read(IPacketReader reader)
 {
     Locale         = reader.ReadString().ToString();
     RenderDistance = reader.ReadUInt8();
     ChatMode       = reader.ReadVarInt();
     ChatColors     = reader.ReadBoolean();
     SkinParts      = reader.ReadUInt8();
     MainHand       = reader.ReadVarInt();
 }
示例#6
0
        public void HandleAuthSession(ref IPacketReader packet, ref IWorldManager manager)
        {
            Authenticator.PacketCrypt.Initialised = true;

            packet.Position = 58;
            int addonsize        = packet.ReadInt32();
            int decompressedSize = packet.ReadInt32();

            byte[] addonData = this.GetAddonInfo(packet);

            // get account name
            packet.Position = 62 + addonsize;
            var    bitUnpack = new BitUnpacker(packet);
            int    nameLen   = bitUnpack.GetBits <int>(12);
            string name      = packet.ReadString(nameLen).ToUpper();

            Account account = new Account(name);

            account.Load <Character>();
            manager.Account = account;

            PacketWriter writer  = new PacketWriter(Sandbox.Instance.Opcodes[global::Opcodes.SMSG_AUTH_RESPONSE], "SMSG_AUTH_RESPONSE");
            var          bitPack = new BitPacker(writer);

            bitPack.Write(0); // IsInQueue
            bitPack.Write(1); // HasAccountData
            bitPack.Flush();
            writer.WriteUInt8(0);
            writer.WriteUInt8(4);
            writer.WriteUInt32(0);
            writer.WriteUInt32(0);
            writer.WriteUInt8(4);
            writer.WriteUInt32(0);
            writer.WriteUInt8(0xC);
            manager.Send(writer);

            // create addoninfo packet
            var addonPacketInfo     = new PacketReader(addonData, false);
            var addonPacketResponse = new PacketWriter(Sandbox.Instance.Opcodes[global::Opcodes.SMSG_ADDON_INFO], "SMSG_ADDON_INFO");

            this.WriteAddonInfo(addonPacketInfo, addonPacketResponse, decompressedSize);
            manager.Send(addonPacketResponse);

            // Tutorial Flags : REQUIRED
            PacketWriter tutorial = new PacketWriter(Sandbox.Instance.Opcodes[global::Opcodes.SMSG_TUTORIAL_FLAGS], "SMSG_TUTORIAL_FLAGS");

            for (int i = 0; i < 8; i++)
            {
                tutorial.WriteUInt32(0);
            }
            manager.Send(tutorial);
        }
示例#7
0
        public override async Task Handle(LoginStageUser user, IPacketReader packet)
        {
            var name = packet.ReadString();

            var result = await user.Stage.CharacterRepository.CheckExistsByName(name);

            var response = new UnstructuredOutgoingPacket(PacketSendOperations.CheckDuplicatedIDResult);

            response.WriteString(name);
            response.WriteBool(result);

            await user.Dispatch(response);
        }
示例#8
0
        protected override async Task Handle(GameStageUser stageUser, IFieldObjUser user, IPacketReader packet)
        {
            _ = packet.ReadInt();

            var type = (GroupMessageType)packet.ReadByte();

            var recipientCount = packet.ReadByte();
            var recipients     = new int[recipientCount];

            for (var i = 0; i < recipientCount; i++)
            {
                recipients[i] = packet.ReadInt();
            }

            var text = packet.ReadString();

            switch (type)
            {
            case GroupMessageType.Party:
            {
                if (user.Party == null)
                {
                    return;
                }

                var partyChat = new UnstructuredOutgoingPacket(PacketSendOperations.GroupMessage);

                partyChat.WriteByte((byte)GroupMessageType.Party);
                partyChat.WriteString(user.Character.Name);
                partyChat.WriteString(text);

                var dispatchRequest = new DispatchToCharactersRequest {
                    Data = ByteString.CopyFrom(partyChat.Buffer)
                };

                dispatchRequest.Characters.Add(user.Party.Members
                                               .Select(m => m.ID)
                                               .Where(m => m != user.ID)
                                               );

                await stageUser.Stage.DispatchService.DispatchToCharacters(dispatchRequest);

                break;
            }

            default:
                stageUser.Stage.Logger.LogWarning($"Unhandled group message type: {type}");
                break;
            }
        }
示例#9
0
        public void HandleAuthSession(ref IPacketReader packet, ref IWorldManager manager)
        {
            packet.ReadUInt64();
            string name = packet.ReadString().ToUpper();

            Account account = new Account(name);

            account.Load <Character>();
            manager.Account = account;

            PacketWriter writer = new PacketWriter(Sandbox.Instance.Opcodes[global::Opcodes.SMSG_AUTH_RESPONSE], "SMSG_AUTH_RESPONSE");

            writer.WriteUInt8(0x0C); //AUTH_OK
            manager.Send(writer);
        }
        /// <inheritdoc />
        public override CtfFieldValue Read(IPacketReader reader, CtfFieldValue parent = null)
        {
            Guard.NotNull(reader, nameof(reader));

            reader.Align((uint)this.Align);

            byte[] value = reader.ReadString();

            if (this.Encoding == EncodingTypes.Ascii)
            {
                return(new CtfStringValue(System.Text.Encoding.ASCII.GetString(value, 0, value.Length - 1)));
            }

            return(new CtfStringValue(System.Text.Encoding.UTF8.GetString(value, 0, value.Length - 1)));
        }
示例#11
0
        public void HandleCharCreate(ref IPacketReader packet, ref IWorldManager manager)
        {
            string name = packet.ReadString();

            Character cha = new Character()
            {
                Name       = name.ToUpperFirst(),
                Race       = packet.ReadByte(),
                Class      = packet.ReadByte(),
                Gender     = packet.ReadByte(),
                Skin       = packet.ReadByte(),
                Face       = packet.ReadByte(),
                HairStyle  = packet.ReadByte(),
                HairColor  = packet.ReadByte(),
                FacialHair = packet.ReadByte()
            };

            // HACK neutral panda faction template doesn't work
            if (cha.Race == (byte)Races.PANDAREN_NEUTRAL)
            {
                cha.Race = (byte)Races.PANDAREN_ALLI;
            }

            var          result = manager.Account.Characters.Where(x => x.Build == Sandbox.Instance.Build);
            PacketWriter writer = new PacketWriter(Sandbox.Instance.Opcodes[global::Opcodes.SMSG_CHAR_CREATE], "SMSG_CHAR_CREATE");

            if (result.Any(x => x.Name.Equals(cha.Name, StringComparison.CurrentCultureIgnoreCase)))
            {
                writer.WriteUInt8(0x32); // Duplicate name
                manager.Send(writer);
                return;
            }

            cha.Guid        = (ulong)(manager.Account.Characters.Count + 1);
            cha.Location    = new Location(-8949.95f, -132.493f, 83.5312f, 0, 0);
            cha.RestedState = (byte)new Random().Next(1, 2);
            cha.SetDefaultValues();

            manager.Account.Characters.Add(cha);
            manager.Account.Save();

            // Success
            writer.WriteUInt8(0x2F);
            manager.Send(writer);
        }
示例#12
0
        public void HandleAuthSession(ref IPacketReader packet, ref IWorldManager manager)
        {
            Common.Cryptography.Authenticator.ClientBuild = packet.ReadUInt16();

            packet.Position = 14;
            string login    = packet.ReadString();
            string username = login.Split(new[] { "\r\n", "\n" }, StringSplitOptions.RemoveEmptyEntries)[0];

            Account account = new Account(username.ToUpper());

            account.Load <Character>();
            manager.Account = account;

            PacketWriter writer = new PacketWriter(Sandbox.Instance.Opcodes[global::Opcodes.SMSG_AUTH_RESPONSE], "SMSG_AUTH_RESPONSE");

            writer.WriteUInt8(0x0C); // AUTH_OK
            manager.Send(writer);
        }
示例#13
0
        public override async Task Handle(LoginStageUser user, IPacketReader packet)
        {
            var spw         = packet.ReadString();
            var characterID = packet.ReadInt();

            var result    = LoginResultCode.Success;
            var response  = new UnstructuredOutgoingPacket(PacketSendOperations.DeleteCharacterResult);
            var character = await user.Stage.CharacterRepository.Retrieve(characterID);

            if (!BCrypt.Net.BCrypt.EnhancedVerify(spw, user.Account.SPW))
            {
                result = LoginResultCode.IncorrectSPW;
            }
            if (character == null || character.AccountWorldID != user.AccountWorld.ID)
            {
                result = LoginResultCode.DBFail;
            }

            var guild = (await user.Stage.GuildService.LoadByCharacter(new GuildLoadByCharacterRequest {
                Character = characterID
            })).Guild;
            var party = (await user.Stage.PartyService.LoadByCharacter(new PartyLoadByCharacterRequest {
                Character = characterID
            })).Party;

            if (result == LoginResultCode.Success)
            {
                // TODO: guild withdraw if not master

                if (party != null)
                {
                    _ = user.Stage.PartyService.Withdraw(new PartyWithdrawRequest {
                        Character = characterID
                    });
                }

                await user.Stage.CharacterRepository.Delete(characterID);
            }

            response.WriteInt(characterID);
            response.WriteByte((byte)result);

            await user.Dispatch(response);
        }
示例#14
0
        public void HandleMessageChat(ref IPacketReader packet, ref IWorldManager manager)
        {
            PacketWriter writer = new PacketWriter(Sandbox.Instance.Opcodes[global::Opcodes.SMSG_MESSAGECHAT], "SMSG_MESSAGECHAT");

            writer.WriteUInt8((byte)packet.ReadInt32()); //System Message
            packet.ReadUInt32();
            writer.WriteUInt32(0);                       //Language: General
            writer.WriteUInt64(manager.Account.ActiveCharacter.Guid);

            string message = packet.ReadString();

            writer.WriteString(message);
            writer.WriteUInt8(0);

            if (!CommandManager.InvokeHandler(message, manager))
            {
                manager.Send(writer);
            }
        }
示例#15
0
        public void HandleAuthSession(ref IPacketReader packet, ref IWorldManager manager)
        {
            Authenticator.PacketCrypt.Initialised = true;
            Authenticator.PacketCrypt.DigestSize  = 40;

            packet.ReadUInt64();
            string name = packet.ReadString().ToUpper();

            Account account = new Account(name);

            account.Load <Character>();
            manager.Account = account;

            PacketWriter writer = new PacketWriter(Sandbox.Instance.Opcodes[global::Opcodes.SMSG_AUTH_RESPONSE], "SMSG_AUTH_RESPONSE");

            writer.WriteUInt8(0x0C); // AUTH_OK
            writer.WriteUInt32(0);
            writer.WriteUInt8(0);
            writer.WriteUInt32(0);
            manager.Send(writer);
        }
示例#16
0
        protected override async Task Handle(GameStageUser stageUser, IFieldObjUser user, IPacketReader packet)
        {
            _ = packet.ReadInt();
            var message     = packet.ReadString();
            var onlyBalloon = packet.ReadBool();

            if (message.StartsWith("!") || message.StartsWith("@")) // TODO: config?
            {
                await stageUser.Stage.CommandProcessor.Process(user, message.Substring(1));

                return;
            }

            var chatPacket1 = new UnstructuredOutgoingPacket(PacketSendOperations.UserChat);

            chatPacket1.WriteInt(user.ID);
            chatPacket1.WriteBool(user.Account.GradeCode > 0 || user.Account.SubGradeCode > 0); // TODO: proper gm chat checks
            chatPacket1.WriteString(message);
            chatPacket1.WriteBool(onlyBalloon);

            await user.FieldSplit.Dispatch(chatPacket1);

            if (onlyBalloon)
            {
                return;
            }

            var chatPacket2 = new UnstructuredOutgoingPacket(PacketSendOperations.UserChatNLCPQ);

            chatPacket2.WriteInt(user.ID);
            chatPacket2.WriteBool(user.Account.GradeCode > 0 || user.Account.SubGradeCode > 0); // TODO: proper gm chat checks
            chatPacket2.WriteString(message);
            chatPacket2.WriteBool(onlyBalloon);
            chatPacket2.WriteString(user.Character.Name);

            await Task.WhenAll(user.Field
                               .GetUsers()
                               .Except(user.FieldSplit.GetWatchers())
                               .Select(u => u.Dispatch(chatPacket2)));
        }
示例#17
0
        public void HandleMessageChat(ref IPacketReader packet, ref IWorldManager manager)
        {
            var character = manager.Account.ActiveCharacter;

            var bitunpack = new BitUnpacker(packet);
            var language  = packet.ReadUInt32();
            var message   = packet.ReadString(bitunpack.GetBits <int>(9));

            PacketWriter writer = new PacketWriter(Sandbox.Instance.Opcodes[global::Opcodes.SMSG_MESSAGECHAT], "SMSG_MESSAGECHAT");

            writer.WriteUInt8(1);  // System Message
            writer.WriteUInt32(0); // Language: General
            writer.WriteUInt64(character.Guid);
            writer.WriteUInt32(0);
            writer.WriteUInt64(0);
            writer.WriteInt32(message.Length + 1);
            writer.WriteString(message);
            writer.WriteUInt8(0);

            if (!CommandManager.InvokeHandler(message, manager))
            {
                manager.Send(writer);
            }
        }
示例#18
0
        protected override async Task Handle(GameStageUser stageUser, IFieldObjUser user, IPacketReader packet)
        {
            var type = (ConversationRequestType)packet.ReadByte();

            if (!user.IsConversing)
            {
                return;
            }

            var context = user.CurrentConversation.Context;

            if (type == ConversationRequestType.AskQuiz ||
                type == ConversationRequestType.AskSpeedQuiz)
            {
                await context.Respond(
                    new ConversationResponse <string>(type, packet.ReadString())
                    );

                return;
            }

            var answer = packet.ReadByte();

            if (
                type != ConversationRequestType.Say &&
                type != ConversationRequestType.AskYesNo &&
                type != ConversationRequestType.AskAccept &&
                answer == byte.MinValue ||
                (type == ConversationRequestType.Say ||
                 type == ConversationRequestType.AskYesNo ||
                 type == ConversationRequestType.AskAccept) && answer == byte.MaxValue
                )
            {
                await user.EndConversation();

                return;
            }

            switch (type)
            {
            case ConversationRequestType.AskText:
            case ConversationRequestType.AskBoxText:
                await context.Respond(
                    new ConversationResponse <string>(type, packet.ReadString())
                    );

                break;

            case ConversationRequestType.AskNumber:
            case ConversationRequestType.AskMenu:
            case ConversationRequestType.AskSlideMenu:
                await context.Respond(
                    new ConversationResponse <int>(type, packet.ReadInt())
                    );

                break;

            case ConversationRequestType.AskAvatar:
            case ConversationRequestType.AskMemberShopAvatar:
                await context.Respond(
                    new ConversationResponse <byte>(type, packet.ReadByte())
                    );

                break;

            case ConversationRequestType.AskYesNo:
            case ConversationRequestType.AskAccept:
                await context.Respond(
                    new ConversationResponse <bool>(type, Convert.ToBoolean(answer))
                    );

                break;

            default:
                await context.Respond(
                    new ConversationResponse <byte>(type, answer)
                    );

                break;
            }
        }
示例#19
0
        protected override async Task Handle(GameStageUser stageUser, IFieldObjUser user, IPacketReader packet)
        {
            var stage = stageUser.Stage;
            var flag  = (WhisperFlags)packet.ReadByte();

            switch (flag)
            {
            case WhisperFlags.Whisper | WhisperFlags.Request:
            {
                _ = packet.ReadInt();
                var name = packet.ReadString();
                var text = packet.ReadString();

                if (name.Equals(user.Character.Name, StringComparison.CurrentCultureIgnoreCase))
                {
                    return;
                }

                var allowed = false;
                var target  = await stage.CharacterRepository.RetrieveByName(name);

                var response = new UnstructuredOutgoingPacket(PacketSendOperations.Whisper);

                if (target != null)
                {
                    var sessionRequest = new DescribeSessionByCharacterRequest {
                        Character = target.ID
                    };
                    var sessionResponse = await stage.SessionRegistry.DescribeByCharacter(sessionRequest);

                    if (sessionResponse.Session.State == SessionState.LoggedIn)
                    {
                        allowed = true;
                    }
                }

                response.WriteByte((byte)(WhisperFlags.Whisper | WhisperFlags.Result));
                response.WriteString(allowed ? target.Name : name);
                response.WriteBool(allowed);

                if (allowed)
                {
                    var whisper = new UnstructuredOutgoingPacket(PacketSendOperations.Whisper)
                                  .WriteByte((byte)(WhisperFlags.Whisper | WhisperFlags.Receive))
                                  .WriteString(user.Character.Name)
                                  .WriteByte((byte)stage.ChannelID)
                                  .WriteBool(false) // bFromAdmin
                                  .WriteString(text);
                    var whisperRequest = new DispatchToCharactersRequest {
                        Data = ByteString.CopyFrom(whisper.Buffer)
                    };

                    whisperRequest.Characters.Add(target.ID);

                    await stage.DispatchService.DispatchToCharacters(whisperRequest);
                }

                await user.Dispatch(response);

                break;
            }

            default:
                stage.Logger.LogWarning($"Unhandled whisper flag: {flag}");
                break;
            }
        }
示例#20
0
 public void Read(IPacketReader reader)
 {
     Username = reader.ReadString().ToString();
 }
示例#21
0
        protected override async Task Handle(GameStageUser stageUser, IFieldObjUser user, IPacketReader packet)
        {
            var stage   = stageUser.Stage;
            var service = stage.PartyService;
            var type    = (PartyRequestCode)packet.ReadByte();

            switch (type)
            {
            case PartyRequestCode.CreateNewParty:
            {
                if (user.Party != null)
                {
                    return;
                }

                var result   = PartyResultCode.CreateNewParty_Done;
                var response = new UnstructuredOutgoingPacket(PacketSendOperations.PartyResult);
                var contract = new PartyCreateRequest
                {
                    Member = new PartyMemberContract
                    {
                        Id      = user.ID,
                        Name    = user.Character.Name,
                        Job     = user.Character.Job,
                        Level   = user.Character.Level,
                        Channel = stage.ChannelID,
                        Field   = user.Field.ID
                    }
                };
                var serviceResponse = await service.Create(contract);

                var serviceResult = serviceResponse.Result;

                if (serviceResult == PartyServiceResult.FailedAlreadyInParty)
                {
                    result = PartyResultCode.CreateNewParty_AlreadyJoined;
                }
                else if (serviceResult != PartyServiceResult.Ok)
                {
                    result = PartyResultCode.CreateNewParty_Unknown;
                }

                response.WriteByte((byte)result);

                if (result == PartyResultCode.CreateNewParty_Done)
                {
                    response.WriteInt(serviceResponse.Party.Id);
                    response.WriteInt(0);                     // TownPortal-TownID
                    response.WriteInt(0);                     // TownPortal-FieldID
                    response.WriteInt(0);                     // TownPortal-SkillID
                    response.WritePoint2D(new Point2D(0, 0)); //TownPortal-Position
                }

                await user.Dispatch(response);

                break;
            }

            case PartyRequestCode.WithdrawParty:
            {
                if (user.Party == null)
                {
                    return;
                }

                var result   = PartyResultCode.WithdrawParty_Done;
                var response = new UnstructuredOutgoingPacket(PacketSendOperations.PartyResult);
                var contract = new PartyWithdrawRequest {
                    Character = user.ID, IsKick = false
                };
                var serviceResponse = await service.Withdraw(contract);

                var serviceResult = serviceResponse.Result;

                if (serviceResult == PartyServiceResult.FailedNotInParty)
                {
                    result = PartyResultCode.WithdrawParty_NotJoined;
                }
                else if (serviceResult != PartyServiceResult.Ok)
                {
                    result = PartyResultCode.WithdrawParty_Unknown;
                }

                response.WriteByte((byte)result);

                if (result == PartyResultCode.WithdrawParty_Done)
                {
                    return;
                }

                await user.Dispatch(response);

                break;
            }

            case PartyRequestCode.InviteParty:
            {
                if (user.Party == null)
                {
                    var contract = new PartyCreateRequest
                    {
                        Member = new PartyMemberContract
                        {
                            Id      = user.ID,
                            Name    = user.Character.Name,
                            Job     = user.Character.Job,
                            Level   = user.Character.Level,
                            Channel = stage.ChannelID,
                            Field   = user.Field.ID
                        }
                    };
                    var serviceResponse = await service.Create(contract);

                    var serviceResult = serviceResponse.Result;

                    if (serviceResult != PartyServiceResult.Ok)
                    {
                        return;
                    }

                    var createResponse = new UnstructuredOutgoingPacket(PacketSendOperations.PartyResult);

                    createResponse.WriteByte((byte)PartyResultCode.CreateNewParty_Done);
                    createResponse.WriteInt(serviceResponse.Party.Id);
                    createResponse.WriteInt(0);                     // TownPortal-TownID
                    createResponse.WriteInt(0);                     // TownPortal-FieldID
                    createResponse.WriteInt(0);                     // TownPortal-SkillID
                    createResponse.WritePoint2D(new Point2D(0, 0)); //TownPortal-Position

                    await user.Dispatch(createResponse);
                }

                if (user?.Party?.Boss != user.ID)
                {
                    return;
                }

                var name      = packet.ReadString();
                var result    = PartyResultCode.InviteParty_Sent;
                var response  = new UnstructuredOutgoingPacket(PacketSendOperations.PartyResult);
                var character = await stage.CharacterRepository.RetrieveByName(name);

                if (character == null || user.Character.ID == character.ID)
                {
                    result = PartyResultCode.InviteParty_BlockedUser;
                }
                if ((await service.LoadByCharacter(new PartyLoadByCharacterRequest {
                        Character = character.ID
                    })).Party != null)
                {
                    result = PartyResultCode.JoinParty_AlreadyJoined;
                }
                else
                {
                    var serviceResponse = await stage.InviteService.Register(new InviteRegisterRequest
                        {
                            Invite = new InviteContract
                            {
                                Type    = InviteType.Party,
                                Invited = character.ID,
                                Inviter = user.ID
                            }
                        });

                    if (serviceResponse.Result != InviteServiceResult.Ok)
                    {
                        result = PartyResultCode.InviteParty_AlreadyInvited;
                    }
                }

                response.WriteByte((byte)result);

                if (result == PartyResultCode.InviteParty_Sent)
                {
                    var invitation        = new UnstructuredOutgoingPacket(PacketSendOperations.PartyResult);
                    var invitationRequest = new DispatchToCharactersRequest();

                    invitation.WriteByte((byte)PartyRequestCode.InviteParty);
                    invitation.WriteInt(user.Character.ID);
                    invitation.WriteString(user.Character.Name);
                    invitation.WriteInt(user.Character.Level);
                    invitation.WriteInt(user.Character.Job);
                    invitation.WriteByte(0);         // PartyOpt

                    invitationRequest.Data = ByteString.CopyFrom(invitation.Buffer);
                    invitationRequest.Characters.Add(character.ID);

                    await stage.DispatchService.DispatchToCharacters(invitationRequest);

                    response.WriteString(character.Name);
                }

                await user.Dispatch(response);

                break;
            }

            case PartyRequestCode.KickParty:
            {
                if (user.Party == null)
                {
                    return;
                }
                if (user.Party.Boss != user.ID)
                {
                    return;
                }

                var id = packet.ReadInt();

                if (!user.Party.Members.Any(m => m.ID == id))
                {
                    return;
                }
                if (id == user.ID)
                {
                    return;
                }

                var result   = PartyResultCode.WithdrawParty_Done;
                var response = new UnstructuredOutgoingPacket(PacketSendOperations.PartyResult);
                var contract = new PartyWithdrawRequest {
                    Character = id, IsKick = true
                };
                var serviceResponse = await service.Withdraw(contract);

                var serviceResult = serviceResponse.Result;

                if (serviceResult == PartyServiceResult.FailedNotInParty)
                {
                    result = PartyResultCode.WithdrawParty_NotJoined;
                }
                else if (serviceResult != PartyServiceResult.Ok)
                {
                    result = PartyResultCode.WithdrawParty_Unknown;
                }

                response.WriteByte((byte)result);

                if (result == PartyResultCode.WithdrawParty_Done)
                {
                    return;
                }

                await user.Dispatch(response);

                break;
            }

            case PartyRequestCode.ChangePartyBoss:
            {
                if (user.Party == null || user.Party.Boss != user.ID)
                {
                    return;
                }

                var id = packet.ReadInt();

                if (!user.Party.Members.Any(m => m.ID == id))
                {
                    return;
                }
                if (id == user.ID)
                {
                    return;
                }

                var result   = PartyResultCode.ChangePartyBoss_Done;
                var response = new UnstructuredOutgoingPacket(PacketSendOperations.PartyResult);
                var contract = new PartyChangeBossRequest {
                    Character = id
                };
                var serviceResponse = await service.ChangeBoss(contract);

                var serviceResult = serviceResponse.Result;

                if (serviceResult != PartyServiceResult.Ok)
                {
                    result = PartyResultCode.WithdrawParty_Unknown;
                }

                response.WriteByte((byte)result);

                if (result == PartyResultCode.ChangePartyBoss_Done)
                {
                    return;
                }

                await user.Dispatch(response);

                break;
            }

            default:
                stage.Logger.LogWarning($"Unhandled party request type: {type}");
                break;
            }
        }
        public override async Task Handle(LoginStageUser user, IPacketReader packet)
        {
            var name      = packet.ReadString();
            var race      = packet.ReadInt();
            var subJob    = packet.ReadShort();
            var face      = packet.ReadInt();
            var hair      = packet.ReadInt();
            var hairColor = packet.ReadInt();
            var skin      = packet.ReadInt();
            var coat      = packet.ReadInt();
            var pants     = packet.ReadInt();
            var shoes     = packet.ReadInt();
            var weapon    = packet.ReadInt();
            var gender    = packet.ReadByte();

            var result   = LoginResultCode.Success;
            var response = new UnstructuredOutgoingPacket(PacketSendOperations.CreateNewCharacterResult);

            var world = await user.Stage.WorldTemplates.Retrieve((int)user.SelectedWorldID);

            if (world.BlockCharCreation)
            {
                result = LoginResultCode.NotConnectableWorld;
            }

            var serverRequest = new DescribeServerByMetadataRequest();

            serverRequest.Metadata.Add("Type", Enum.GetName(ServerStageType.Game));
            serverRequest.Metadata.Add("WorldID", user.SelectedWorldID.ToString());
            serverRequest.Metadata.Add("ChannelID", user.SelectedChannelID.ToString());

            var server = (await user.Stage.ServerRegistry.DescribeByMetadata(serverRequest)).Servers.FirstOrDefault();

            if (server == null)
            {
                result = LoginResultCode.NotConnectableWorld;
            }

            response.WriteByte((byte)result);

            if (result == LoginResultCode.Success)
            {
                var character = new Character
                {
                    AccountWorldID = user.AccountWorld.ID,
                    Name           = name,
                    Job            = 0, // TODO: race -> job
                    Face           = face,
                    Hair           = hair + hairColor,
                    Skin           = (byte)skin,
                    Gender         = gender,
                    FieldID        = 310000000, // TODO: start maps
                    FieldPortal    = 0,
                    SubJob         = 0          // TODO: race -> subjob
                };
                var context = new ModifyMultiInventoryContext(character.Inventories, user.Stage.ItemTemplates);

                context.Set(BodyPart.Clothes, coat);
                context.Set(BodyPart.Shoes, shoes);
                context.Set(BodyPart.Weapon, weapon);
                if (pants > 0)
                {
                    context.Set(BodyPart.Pants, pants);
                }

                await user.Stage.CharacterRepository.Insert(character);

                user.Character = character;
                user.Stage.Logger.LogDebug($"Created new {race} character: {name} (ID: {character.ID})");

                response.WriteCharacterStats(character);
                response.WriteCharacterLook(character);
                response.WriteBool(false);
                response.WriteBool(false);
            }
            else
            {
                response.WriteInt(0);
            }

            await user.Dispatch(response);
        }