public async Task ModifyStats(Action <IModifyStatContext> action = null, bool exclRequest = false) { var context = new ModifyStatContext(Character); action?.Invoke(context); await UpdateStats(); if (!IsInstantiated) { return; } var statPacket = new UnstructuredOutgoingPacket(PacketSendOperations.StatChanged); statPacket.WriteBool(exclRequest); statPacket.Write(context); statPacket.WriteBool(false); statPacket.WriteBool(false); await Dispatch(statPacket); if (Party != null && (context.Flag.HasFlag(ModifyStatType.Job) || context.Flag.HasFlag(ModifyStatType.Level))) { _ = GameStage.PartyService.UpdateChangeLevelOrJob(new PartyUpdateChangeLevelOrJobRequest { Character = ID, Level = context.Level, Job = context.Job }); } }
public override async Task Handle(LoginStageUser user, IPacketReader packet) { var worldTemplates = (await Task.WhenAll(user.Stage.Info.Worlds .Select(w => user.Stage.WorldTemplates.Retrieve(w)))) .Where(w => w != null) .OrderBy(w => w.ID) .ToList(); foreach (var world in worldTemplates) { var response = new UnstructuredOutgoingPacket(PacketSendOperations.WorldInformation); response.WriteByte((byte)world.ID); response.WriteString(world.Name); response.WriteByte(world.State); response.WriteString(""); // WorldEventDesc response.WriteShort(0); // WorldEventEXP_WSE, WorldSpecificEvent response.WriteShort(0); // WorldEventDrop_WSE, WorldSpecificEvent response.WriteBool(world.BlockCharCreation); var channelServerRequest = new DescribeServerByMetadataRequest(); channelServerRequest.Metadata.Add("Type", Enum.GetName(ServerStageType.Game)); channelServerRequest.Metadata.Add("WorldID", world.ID.ToString()); var channelServers = (await user.Stage.ServerRegistry.DescribeByMetadata(channelServerRequest)).Servers .OrderBy(c => c.Id) .ToList(); response.WriteByte((byte)channelServers.Count); foreach (var channel in channelServers) { response.WriteString(channel.Metadata["ID"]); response.WriteInt(0); // TODO: UserNo response.WriteByte(Convert.ToByte(channel.Metadata["WorldID"])); response.WriteByte(Convert.ToByte(channel.Metadata["ChannelID"])); response.WriteBool(false); // TODO: AdultChannel } response.WriteShort(0); // TODO: Balloon await user.Dispatch(response); } await user.Dispatch( new UnstructuredOutgoingPacket(PacketSendOperations.WorldInformation) .WriteByte(0xFF) ); await user.Dispatch( new UnstructuredOutgoingPacket(PacketSendOperations.LatestConnectedWorld) .WriteInt(user.Account.LatestConnectedWorld ?? 0) ); }
public async Task UpdateAvatar() { var avatarPacket = new UnstructuredOutgoingPacket(PacketSendOperations.UserAvatarModified); avatarPacket.WriteInt(ID); avatarPacket.WriteByte(0x1); // Flag avatarPacket.WriteCharacterLook(Character); avatarPacket.WriteBool(false); avatarPacket.WriteBool(false); avatarPacket.WriteBool(false); avatarPacket.WriteInt(0); await FieldSplit.Dispatch(this, avatarPacket); }
public async Task ModifySkills(Action <IModifySkillContext> action = null, bool exclRequest = false) { var context = new ModifySkillContext(Character); action?.Invoke(context); await UpdateStats(); var skillPacket = new UnstructuredOutgoingPacket(PacketSendOperations.ChangeSkillRecordResult); skillPacket.WriteBool(exclRequest); skillPacket.Write(context); skillPacket.WriteBool(true); await Dispatch(skillPacket); }
public override IPacket GetEnterFieldPacket() { var packet = new UnstructuredOutgoingPacket(PacketSendOperations.UserEnterField); packet.WriteInt(ID); packet.WriteByte(Character.Level); packet.WriteString(Character.Name); packet.WriteString(Guild?.Name ?? ""); packet.WriteShort(Guild?.MarkBg ?? 0); packet.WriteByte(Guild?.MarkBgColor ?? 0); packet.WriteShort(Guild?.Mark ?? 0); packet.WriteByte(Guild?.MarkColor ?? 0); packet.WriteSecondaryStatsToRemote(SecondaryStats); packet.WriteShort(Character.Job); packet.WriteCharacterLook(Character); packet.WriteInt(0); packet.WriteInt(0); packet.WriteInt(0); packet.WriteInt(0); packet.WriteInt(0); packet.WriteInt(0); packet.WritePoint2D(Position); packet.WriteByte((byte)Action); packet.WriteShort((short)(Foothold?.ID ?? 0)); packet.WriteByte(0); packet.WriteBool(false); packet.WriteBool(false); packet.WriteBool(false); packet.WriteBool(false); packet.WriteInt(0); packet.WriteInt(0); packet.WriteInt(0); packet.WriteByte(0); packet.WriteBool(false); packet.WriteBool(false); packet.WriteBool(false); packet.WriteBool(false); packet.WriteByte(0); packet.WriteByte(0); packet.WriteInt(0); return(packet); }
public async Task <PartyChangeBossResponse> ChangeBoss(PartyChangeBossRequest request) { var source = new CancellationTokenSource(); source.CancelAfter(PartyLockTimeoutDuration); var @lock = await _locker.AcquireAsync(PartyLockKey, cancellationToken : source.Token); if (@lock != null) { var result = PartyServiceResult.Ok; var party = await _repository.RetrieveByMember(request.Character); if (party == null) { result = PartyServiceResult.FailedNotInParty; } else if (party.Boss == request.Character) { result = PartyServiceResult.FailedAlreadyBoss; } if (result == PartyServiceResult.Ok) { var targets = party.Members.Select(m => m.ID).ToImmutableList(); var packet = new UnstructuredOutgoingPacket(PacketSendOperations.PartyResult); party.Boss = request.Character; await _repository.Update(party); packet.WriteByte((byte)PartyResultCode.ChangePartyBoss_Done); packet.WriteInt(request.Character); packet.WriteBool(request.IsDisconnect); var dispatchRequest = new DispatchToCharactersRequest { Data = ByteString.CopyFrom(packet.Buffer) }; dispatchRequest.Characters.Add(targets); await _dispatcher.DispatchToCharacters(dispatchRequest); await _messenger.PublishAsync(new PartyUpdateEvent { Party = party }); } await @lock.ReleaseAsync(); return(new PartyChangeBossResponse { Result = result }); } return(new PartyChangeBossResponse { Result = PartyServiceResult.FailedTimeout }); }
public IPacket GetSetFieldPacket() { var packet = new UnstructuredOutgoingPacket(PacketSendOperations.SetField); packet.WriteShort(0); // ClientOpt packet.WriteInt(GameStage.ChannelID); packet.WriteInt(GameStage.WorldID); packet.WriteBool(true); // sNotifierMessage._m_pStr packet.WriteBool(!IsInstantiated); packet.WriteShort(0); // nNotifierCheck, loops if (!IsInstantiated) { packet.WriteUInt(Damage.InitSeed1); packet.WriteUInt(Damage.InitSeed2); packet.WriteUInt(Damage.InitSeed3); packet.WriteCharacterData(Character); packet.WriteInt(0); for (var i = 0; i < 3; i++) { packet.WriteInt(0); } } else { packet.WriteByte(0); packet.WriteInt(Character.FieldID); packet.WriteByte(Character.FieldPortal); packet.WriteInt(Character.HP); packet.WriteBool(false); } packet.WriteDateTime(DateTime.Now); return(packet); }
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))); }
protected virtual IPacket GetMigratePacket(byte[] address, short port) { var packet = new UnstructuredOutgoingPacket(PacketSendOperations.MigrateCommand); packet.WriteBool(true); foreach (var b in address) { packet.WriteByte(b); } packet.WriteShort(port); return(packet); }
protected override async Task Handle(GameStageUser stageUser, IFieldObjUser user, IPacketReader packet) { _ = packet.ReadInt(); var type = (ItemInventoryType)packet.ReadByte(); var response = new UnstructuredOutgoingPacket(PacketSendOperations.GatherItemResult); response.WriteBool(false); response.WriteByte((byte)type); await user.ModifyInventory(i => i[type].Gather(), true); await user.Dispatch(response); }
protected override IPacket GetChangeControllerPacket(bool setAsController) { var packet = new UnstructuredOutgoingPacket(PacketSendOperations.NpcChangeController); packet.WriteBool(setAsController); packet.WriteInt(ID); if (setAsController) { WriteData(packet); } return(packet); }
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); }
public async Task ModifyInventory(Action <IModifyMultiInventoryContext> action = null, bool exclRequest = false) { var context = new ModifyMultiInventoryContext(Character.Inventories, GameStage.ItemTemplates); action?.Invoke(context); var inventoryPacket = new UnstructuredOutgoingPacket(PacketSendOperations.InventoryOperation); inventoryPacket.WriteBool(exclRequest); inventoryPacket.Write(context); inventoryPacket.WriteBool(false); await Dispatch(inventoryPacket); if ( context.History.Any(o => o.Slot < 0) || context.History.OfType <MoveModifyInventoryOperation>().Any(o => o.ToSlot < 0) ) { await UpdateStats(); await UpdateAvatar(); } }
public override async Task Enter(GameStageUser user) { await base.Enter(user); var field = await FieldRepository.Retrieve(user.Character.FieldID); var fieldUser = new FieldObjUser(user); var guildLoadResponse = await GuildService.LoadByCharacter(new GuildLoadByCharacterRequest { Character = user.Character.ID }); var partyLoadResponse = await PartyService.LoadByCharacter(new PartyLoadByCharacterRequest { Character = user.Character.ID }); if (guildLoadResponse.Guild != null) { fieldUser.Guild = new Guild(guildLoadResponse.Guild); } if (partyLoadResponse.Party != null) { fieldUser.Party = new Party(partyLoadResponse.Party); } user.FieldUser = fieldUser; await field.Enter(fieldUser); var functionKeyPacket = new UnstructuredOutgoingPacket(PacketSendOperations.FuncKeyMappedInit); var quickSlotKeyPacket = new UnstructuredOutgoingPacket(PacketSendOperations.QuickslotMappedInit); functionKeyPacket.WriteBool(false); for (var i = 0; i < 90; i++) { var key = user.Character.FunctionKeys[i]; functionKeyPacket.WriteByte(key?.Type ?? 0); functionKeyPacket.WriteInt(key?.Action ?? 0); } quickSlotKeyPacket.WriteBool(false); for (var i = 0; i < 8; i++) { quickSlotKeyPacket.WriteInt(user.Character.QuickSlotKeys[i].Key); } _ = user.Dispatch(functionKeyPacket); _ = user.Dispatch(quickSlotKeyPacket); }
protected override async Task Handle(GameStageUser stageUser, IFieldObjUser user, IPacketReader packet) { var contimove = await stageUser.Stage.ContiMoveRepository.RetrieveByField(user.Field); if (contimove == null) { return; } var response = new UnstructuredOutgoingPacket(PacketSendOperations.CONTISTATE); response.WriteByte((byte)contimove.State); response.WriteBool(contimove.State == ContiMoveState.Event); await user.Dispatch(response); }
protected override async Task Handle(GameStageUser stageUser, IFieldObjUser user, IPacketReader packet) { _ = packet.ReadInt(); var targetID = packet.ReadInt(); var target = user.Watching .SelectMany(w => w.GetObjects <IFieldObjUser>()) .Where(o => o.ID == targetID) .FirstOrDefault(); if (target == null) { return; } var response = new UnstructuredOutgoingPacket(PacketSendOperations.CharacterInfo); response.WriteInt(target.ID); response.WriteByte(target.Character.Level); response.WriteShort(target.Character.Job); response.WriteShort(target.Character.POP); response.WriteByte(0); response.WriteString(target?.Guild?.Name ?? ""); response.WriteString(""); // sAlliance response.WriteByte(0); // Medal? response.WriteBool(false); // Pets response.WriteByte(0); // TamingMobInfo response.WriteByte(0); // Wishlist response.WriteInt(0); // MedalAchievementInfo response.WriteShort(0); var chairs = target.Character.Inventories[ItemInventoryType.Install].Items .Select(kv => kv.Value) .Select(i => i.TemplateID) .Where(i => i / 10000 == 301) .ToList(); response.WriteInt(chairs.Count); chairs.ForEach(i => response.WriteInt(i)); await user.Dispatch(response); }
protected override async Task Handle(GameStageUser stageUser, IFieldObjUser user, IPacketReader packet) { var emotion = packet.ReadInt(); var duration = packet.ReadInt(); var byItemOption = packet.ReadBool(); // TODO item option check var response = new UnstructuredOutgoingPacket(PacketSendOperations.UserEmotion); response.WriteInt(user.ID); response.WriteInt(emotion); response.WriteInt(duration); response.WriteBool(byItemOption); await user.FieldSplit.Dispatch(user, response); }
public async Task ModifySecondaryStats(Action <IModifySecondaryStatContext> action = null, bool exclRequest = false) { var context = new ModifySecondaryStatContext(SecondaryStats as SecondaryStats); action?.Invoke(context); await UpdateStats(); if (context.ResetHistory.ToDictionary().Any()) { var resetLocalPacket = new UnstructuredOutgoingPacket(PacketSendOperations.TemporaryStatReset); var resetRemotePacket = new UnstructuredOutgoingPacket(PacketSendOperations.UserTemporaryStatReset); resetLocalPacket.WriteSecondaryStatsFlag(context.ResetHistory); resetLocalPacket.WriteBool(false); // IsMovementAffectingStat resetRemotePacket.WriteInt(ID); resetRemotePacket.WriteSecondaryStatsFlag(context.ResetHistory); await Dispatch(resetLocalPacket); await FieldSplit.Dispatch(resetRemotePacket); } if (context.SetHistory.ToDictionary().Any()) { var setLocalPacket = new UnstructuredOutgoingPacket(PacketSendOperations.TemporaryStatSet); var setRemotePacket = new UnstructuredOutgoingPacket(PacketSendOperations.UserTemporaryStatSet); setLocalPacket.WriteSecondaryStatsToLocal(context.SetHistory); setLocalPacket.WriteShort(0); // tDelay setLocalPacket.WriteBool(false); // IsMovementAffectingStat setRemotePacket.WriteInt(ID); setRemotePacket.WriteSecondaryStatsToLocal(context.SetHistory); setRemotePacket.WriteShort(0); // tDelay await Dispatch(setLocalPacket); await FieldSplit.Dispatch(setRemotePacket); } }
public override async Task Handle(LoginStageUser user, IPacketReader packet) { var cancel = !packet.ReadBool(); if (cancel) { await user.Disconnect(); return; } var gender = (byte)(packet.ReadBool() ? 1 : 0); var response = new UnstructuredOutgoingPacket(PacketSendOperations.SetAccountResult); user.Account.Gender = gender; response.WriteByte(gender); response.WriteBool(!cancel); await user.Dispatch(response); }
public async Task <PartyWithdrawResponse> Withdraw(PartyWithdrawRequest request) { var source = new CancellationTokenSource(); source.CancelAfter(PartyLockTimeoutDuration); var @lock = await _locker.AcquireAsync(PartyLockKey, cancellationToken : source.Token); if (@lock != null) { var result = PartyServiceResult.Ok; var party = await _repository.RetrieveByMember(request.Character); var member = party?.Members?.FirstOrDefault(m => m.ID == request.Character); if (party == null || member == null) { result = PartyServiceResult.FailedNotInParty; } if (result == PartyServiceResult.Ok) { var targets = party.Members.ToList(); var isDisband = request.Character == party.Boss; if (isDisband) { party.Members.Clear(); await _repository.Delete(party); } else { party.Members.Remove(member); await _repository.Update(party); } await Task.WhenAll(targets.Select(async m => { var packet = new UnstructuredOutgoingPacket(PacketSendOperations.PartyResult); packet.WriteByte((byte)PartyResultCode.WithdrawParty_Done); packet.WriteInt(party.ID); packet.WriteInt(request.Character); packet.WriteBool(!isDisband); if (!isDisband) { packet.WriteBool(request.IsKick); packet.WriteString(member.Name); packet.WritePartyData(party, m.Channel); } var dispatchRequest = new DispatchToCharactersRequest { Data = ByteString.CopyFrom(packet.Buffer) }; dispatchRequest.Characters.Add(m.ID); await _dispatcher.DispatchToCharacters(dispatchRequest); })); await _messenger.PublishAsync(new PartyUpdateEvent { Party = party }); } await @lock.ReleaseAsync(); return(new PartyWithdrawResponse { Result = result }); } return(new PartyWithdrawResponse { Result = PartyServiceResult.FailedTimeout }); }
protected override async Task Handle( GameStageUser stageUser, IFieldObjUser controller, IFieldObjMob controlled, IPacketReader packet ) { if (controlled.HP <= 0) { return; } var mobCtrlSN = packet.ReadShort(); var v7 = packet.ReadByte(); //v85 = nDistance | 4 * (v184 | 2 * ((unsigned __int8)retaddr | 2 * v72)); [ CONFIRMED ] var oldSplit = (v7 & 0xF0) != 0; //this is a type of CFieldSplit var mobMoveStartResult = (v7 & 0xF) != 0; var curSplit = packet.ReadByte(); var illegalVelocity = packet.ReadInt(); var v8 = packet.ReadByte(); var cheatedRandom = (v8 & 0xF0) != 0; var cheatedCtrlMove = (v8 & 0xF) != 0; var multiTargetForBall = packet.ReadInt(); for (var i = 0; i < multiTargetForBall; i++) { packet.ReadLong(); // int, int } var randTimeForAreaAttack = packet.ReadInt(); for (var i = 0; i < randTimeForAreaAttack; i++) { packet.ReadInt(); } packet.ReadInt(); // HackedCode packet.ReadInt(); // idk packet.ReadInt(); // HackedCodeCrc packet.ReadInt(); // idk var path = packet.Read(new MovePath()); await controlled.Move(path); var response = new UnstructuredOutgoingPacket(PacketSendOperations.MobCtrlAck); response.WriteInt(controlled.ID); response.WriteShort(mobCtrlSN); response.WriteBool(mobMoveStartResult); response.WriteShort((short)controlled.MP); // nMP response.WriteByte(0); // SkillCommand response.WriteByte(0); // SLV await controller.Dispatch(response); var movement = new UnstructuredOutgoingPacket(PacketSendOperations.MobMove); movement.WriteInt(controlled.ID); movement.WriteBool(false); // NotForceLandingWhenDiscard movement.WriteBool(false); // NotChangeAction movement.WriteBool(false); // NextAttackPossible movement.WriteBool(false); // Left movement.WriteInt(illegalVelocity); movement.WriteInt(0); // MultiTargetForBall movement.WriteInt(0); // RandTimeForAreaAttack movement.Write(path); await controlled.Field.Dispatch(controller, movement); }
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; } }
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); }
protected override async Task Handle(GameStageUser stageUser, IFieldObjUser user, IPacketReader packet) { var clientAttackInfo = packet.Read(new ClientAttackInfo()); var skill = (Skill)clientAttackInfo.SkillID; var skillLevel = clientAttackInfo.SkillID > 0 ? user.Character.GetSkillLevel(clientAttackInfo.SkillID) : 0; var damageType = Type == AttackType.Magic ? DamageType.Magic : DamageType.Physical; var damageApply = new List <Tuple <IFieldObjMob, int> >(); if (Type == AttackType.Melee) { // TODO bmage blows } if (Type == AttackType.Body) { // TODO teleport mastery } var operation = (PacketSendOperations)((int)PacketSendOperations.UserMeleeAttack + (int)Type); var response = new UnstructuredOutgoingPacket(operation); response.WriteInt(user.ID); response.WriteByte((byte)(clientAttackInfo.DamagePerMob | 16 * clientAttackInfo.MobCount)); response.WriteByte(user.Character.Level); response.WriteByte((byte)skillLevel); if (skillLevel > 0) { response.WriteInt(clientAttackInfo.SkillID); } response.WriteByte((byte)( 1 * Convert.ToByte(clientAttackInfo.IsFinalAfterSlashBlast) | 8 * Convert.ToByte(clientAttackInfo.IsShadowPartner) | 16 * 0 | 32 * Convert.ToByte(clientAttackInfo.IsSerialAttack) )); response.WriteShort((short)( clientAttackInfo.Action & 0x7FFF | Convert.ToByte(clientAttackInfo.IsFacingLeft) << 15) ); if (clientAttackInfo.Action <= 0x110) { response.WriteByte(0); // nMastery response.WriteByte(0); // v82 response.WriteInt(0); // bMovingShoot clientAttackInfo.Mobs.ForEach(m => { var critical = new bool[clientAttackInfo.DamagePerMob]; var damage = new int[clientAttackInfo.DamagePerMob]; var totalDamage = m.Damage.Sum(); var mob = user.Field.GetObject <IFieldObjMob>(m.MobID); response.WriteInt(m.MobID); response.WriteByte(m.HitAction); if (mob != null) { var equipInventory = user.Character.Inventories[ItemInventoryType.Equip]; var serverAttackInfo = new AttackInfo(user, mob) { WeaponID = equipInventory.Items.ContainsKey((short)BodyPart.Weapon) ? equipInventory.Items[(short)BodyPart.Weapon].TemplateID : 0, BulletID = 0, SkillID = clientAttackInfo.SkillID, SkillLevel = skillLevel }; var calculatedDamage = damageType == DamageType.Physical ? user.Damage.CalculateCharacterPDamage(serverAttackInfo) : user.Damage.CalculateCharacterMDamage(serverAttackInfo); var calculatedTotalDamage = calculatedDamage.Select(d => d.Damage).Sum(); // TODO cheatdetector? if (clientAttackInfo.DamagePerMob != calculatedDamage.Length) { user.Message($"Attack count mismatch: {clientAttackInfo.DamagePerMob} : {calculatedDamage.Length}"); return; } if (totalDamage != calculatedTotalDamage) { user.Message($"Client damage: {string.Join(" + ", m.Damage.Select(m => $"{m}"))} = {totalDamage}"); user.Message($"Server damage: {string.Join(" + ", calculatedDamage.Select(m => $"{m.Damage}"))} = {calculatedTotalDamage}"); } damageApply.Add(Tuple.Create(mob, calculatedTotalDamage)); for (var i = 0; i < clientAttackInfo.DamagePerMob; i++) { critical[i] = calculatedDamage[i].IsCritical; damage[i] = calculatedDamage[i].Damage; } } else { user.Damage.SkipCalculationForCharacterDamage(); } for (var i = 0; i < clientAttackInfo.DamagePerMob; i++) { response.WriteBool(critical[i]); response.WriteInt(damage[i]); } }); } // TODO Keydown await user.FieldSplit.Dispatch(user, response); await Task.WhenAll(damageApply.Select(async a => { var(mob, damage) = a; mob.Controller = user; await mob.Damage(user, damage); })); }
public override async Task Handle(LoginStageUser user, IPacketReader packet) { _ = packet.ReadByte(); // Unknown1 var worldID = packet.ReadByte(); var channelID = packet.ReadByte(); var result = LoginResultCode.Success; var response = new UnstructuredOutgoingPacket(PacketSendOperations.SelectWorldResult); var gameServerRequest = new DescribeServerByMetadataRequest(); gameServerRequest.Metadata.Add("Type", Enum.GetName(ServerStageType.Game)); gameServerRequest.Metadata.Add("WorldID", worldID.ToString()); var gameServers = (await user.Stage.ServerRegistry.DescribeByMetadata(gameServerRequest)).Servers .OrderBy(g => g.Metadata["ChannelID"]) .ToList(); if (channelID > gameServers.Count) { result = LoginResultCode.NotConnectableWorld; } response.WriteByte((byte)result); if (result == LoginResultCode.Success) { var gameServer = gameServers[channelID]; var accountWorld = await user.Stage.AccountWorldRepository.RetrieveByAccountAndWorld(user.Account.ID, worldID); if (accountWorld == null) { accountWorld = new AccountWorld { AccountID = user.Account.ID, WorldID = worldID }; await user.Stage.AccountWorldRepository.Insert(accountWorld); } user.State = LoginState.SelectCharacter; user.AccountWorld = accountWorld; user.SelectedWorldID = Convert.ToByte(gameServer.Metadata["WorldID"]); user.SelectedChannelID = Convert.ToByte(gameServer.Metadata["ChannelID"]); var characters = (await user.Stage.CharacterRepository.RetrieveAllByAccountWorld(accountWorld.ID)).ToList(); response.WriteByte((byte)characters.Count); characters.ForEach(c => { response.WriteCharacterStats(c); response.WriteCharacterLook(c); response.WriteBool(false); response.WriteBool(false); }); response.WriteBool(!string.IsNullOrEmpty(user.Account.SPW)); // bLoginOpt TODO: proper bLoginOpt stuff response.WriteInt(accountWorld.SlotCount); response.WriteInt(0); } await user.Dispatch(response); }