internal void instances_callback(int code) { switch (code) { case 283: if (Settings.TrackerEnabled && Settings.AutoTracker && mainForm.TrackerFormLoaded) { mainForm.TrackerForm.new_tracker(1); } return; case 581: if (Settings.TrackerEnabled && Settings.AutoTracker && mainForm.TrackerFormLoaded) { mainForm.TrackerForm.new_tracker(2); } return; case 598: if (Settings.TrackerEnabled && Settings.AutoTracker && mainForm.TrackerFormLoaded) { mainForm.TrackerForm.new_tracker(3); } return; case 639: if (Settings.TrackerEnabled && Settings.AutoTracker && mainForm.TrackerFormLoaded) { mainForm.TrackerForm.new_tracker(4); } return; default: break; } if (isRoulette && Settings.RouletteTips) { Task.Factory.StartNew(() => { var instance = Data.GetInstance(code); var roulette = Data.GetRoulette(queueCode); if (instance.Tips != null) { mainForm.Show_DutyTips(roulette.Name, instance.Name, instance.Tips, instance.Macro); } else if (instance.Macro != null) { var respond = LMessageBox.Dialog($"已通过[{roulette.Name}]进入<{instance.Name}>副本区域,是否复制该副本可用的宏?", $"DFA:<{instance.Name}> 简易攻略", MessageBoxButtons.YesNo, MessageBoxIcon.None, MessageBoxDefaultButton.Button1); if (respond == DialogResult.Yes) { this.Invoke(() => { Clipboard.SetDataObject(instance.Macro, true); }); } } }); } }
internal void instances_callback(int code) { if (Settings.copyMacro) { Task.Factory.StartNew(() => { var instance = Data.GetInstance(code); if (instance.Macro != null) { var respond = LMessageBox.Dialog(Localization.GetText("ui-settings-copymacro-dialog-text", instance.Name), Localization.GetText("ui-settings-copymacro-dialog-title"), MessageBoxButtons.YesNo, MessageBoxIcon.None, MessageBoxDefaultButton.Button1); if (respond == DialogResult.Yes) { this.Invoke(() => { Clipboard.SetDataObject(instance.Macro, true); }); } } }); } }
private void HandleMessage(byte[] message) { try { if (message.Length < 32) { // type == 0x0000 이였던 메시지는 여기서 걸러짐 return; } mainForm.overlayForm.SetStatus(true); var opcode = BitConverter.ToUInt16(message, 18); #if !DEBUG if (opcode != 0x0078 && opcode != 0x0079 && opcode != 0x0080 && opcode != 0x006C && opcode != 0x006F && opcode != 0x0121 && opcode != 0x0143 && opcode != 0x022F) { return; } #endif var data = message.Skip(32).ToArray(); if (opcode == 0x022F) { var code = BitConverter.ToInt16(data, 4); var type = data[8]; if (type == 0x0B) { Log.I("l-field-instance-entered", Data.GetInstance(code).Name); } else if (type == 0x0C) { Log.I("l-field-instance-left"); } /*if (Settings.ShowOverlay && Settings.AutoOverlayHide) * { * mainForm.overlayForm.Invoke(() => * { * if (type == 0x0B) * { * mainForm.overlayForm.Hide(); * } * else if (type == 0x0C) * { * mainForm.overlayForm.Show(); * } * }); * }*/ } else if (opcode == 0x0143) { var type = data[0]; if (type == 0x9B) { /* * var code = BitConverter.ToUInt16(data, 4); * var progress = data[8]; * * var fate = Data.GetFATE(code); * * //Log.D("\"{0}\" 돌발 진행도 {1}%", fate.Name, progress); */ } else if (type == 0x79) { /* * // 돌발 임무 종료 (지역 이동시 발생할 수 있는 모든 임무에 대해 전부 옴) * * var code = BitConverter.ToUInt16(data, 4); * var status = BitConverter.ToUInt16(data, 28); * * var fate = Data.GetFATE(code); * * //Log.D("\"{0}\" 돌발 종료!", fate.Name); */ } else if (type == 0x74) { // 돌발 임무 발생 (지역 이동시에도 기존 돌발 목록이 옴) var code = BitConverter.ToUInt16(data, 4); var fate = Data.GetFATE(code); if (Settings.FATEs.Contains(code)) { mainForm.overlayForm.SetFATEAsOccured(fate); Log.I("l-fate-occured-info", fate.Name); if (!Settings.ShowOverlay) { mainForm.ShowNotification("notification-fate-occured", fate.Name); } if (Settings.FlashWindow) { WinApi.FlashWindow(mainForm.FFXIVProcess); } if (Settings.TwitterEnabled) { WebApi.Tweet("tweet-fate-occured", fate.Name); } } } } /*else if (opcode == 0x006C) // 3.5 cross-world 파티 참가하면 문제가 발생하는 부분. * { * var code = BitConverter.ToUInt16(data, 192); * * var instance = Data.GetInstance(code); * * state = State.QUEUED; * mainForm.overlayForm.SetDutyCount(1); * * Log.I("l-queue-started-general", instance.Name); * }*/ else if (opcode == 0x0078) { var status = data[0]; var reason = data[4]; if (status == 0) { state = State.QUEUED; var rouletteCode = data[20]; if (rouletteCode != 0 && (data[15] == 0 || data[15] == 64)) //무작위 임무 신청, 한국서버/글로벌 서버 { var roulette = Data.GetRoulette(rouletteCode); mainForm.overlayForm.SetRoulleteDuty(roulette); Log.I("l-queue-started-roulette", roulette.Name); } else //특정 임무 신청 { var instances = new List <Instance>(); for (int i = 0; i < 5; i++) { var code = BitConverter.ToUInt16(data, 22 + (i * 2)); if (code == 0) { break; } instances.Add(Data.GetInstance(code)); } if (!instances.Any()) { return; } mainForm.overlayForm.SetDutyCount(instances.Count); Log.I("l-queue-started-general", string.Join(", ", instances.Select(x => x.Name).ToArray())); } } else if (status == 3) { state = reason == 8 ? State.QUEUED : State.IDLE; mainForm.overlayForm.CancelDutyFinder(); Log.E("l-queue-stopped"); } else if (status == 6) { state = State.IDLE; mainForm.overlayForm.CancelDutyFinder(); Log.I("l-queue-entered"); } else if (status == 4) //글섭에서 매칭 잡혔을 때 출력 { var roulette = data[20]; var code = BitConverter.ToUInt16(data, 22); Instance instance; if (!Settings.CheatRoulette && roulette != 0) { instance = new Instance { Name = Data.GetRoulette(roulette).Name }; } else { instance = Data.GetInstance(code); } state = State.MATCHED; mainForm.overlayForm.SetDutyAsMatched(instance); if (Settings.FlashWindow) { WinApi.FlashWindow(mainForm.FFXIVProcess); } if (!Settings.ShowOverlay) { mainForm.ShowNotification("notification-queue-matched", instance.Name); } if (Settings.TwitterEnabled) { WebApi.Tweet("tweet-queue-matched", instance.Name); } Log.S("l-queue-matched", instance.Name); } } else if (opcode == 0x006F) { var status = data[0]; if (status == 0) { // 플레이어가 매칭 참가 확인 창에서 취소를 누르거나 참가 확인 제한 시간이 초과됨 // 매칭 중단을 알리기 위해 상단 2DB status 3 패킷이 연이어 옴 } if (status == 1) { // 플레이어가 매칭 참가 확인 창에서 확인을 누름 // 다른 매칭 인원들도 전부 확인을 눌렀을 경우 입장을 위해 상단 2DB status 6 패킷이 옴 mainForm.overlayForm.StopBlink(); } } else if (opcode == 0x0121) //글로벌 서버 { var status = data[5]; if (status == 128) { // 매칭 참가 신청 확인 창에서 확인을 누름 mainForm.overlayForm.StopBlink(); } } else if (opcode == 0x0079) { var code = BitConverter.ToUInt16(data, 0); var status = data[4]; var tank = data[5]; var dps = data[6]; var healer = data[7]; var instance = Data.GetInstance(code); if (status == 1) { // 인원 현황 패킷 var member = tank * 10000 + dps * 100 + healer; if (state == State.MATCHED && lastMember != member) { // 매칭도중일 때 인원 현황 패킷이 오고 마지막 인원 정보와 다른 경우에 누군가에 의해 큐가 취소된 경우. state = State.QUEUED; mainForm.overlayForm.CancelDutyFinder(); } else if (state == State.IDLE) { // 프로그램이 매칭 중간에 켜짐 state = State.QUEUED; mainForm.overlayForm.SetDutyCount(-1); // 알 수 없음으로 설정함 (TODO: 알아낼 방법 있으면 정확히 나오게 수정하기) mainForm.overlayForm.SetDutyStatus(instance, tank, dps, healer); } else if (state == State.QUEUED) { mainForm.overlayForm.SetDutyStatus(instance, tank, dps, healer); } lastMember = member; } else if (status == 2) { // 현재 매칭된 파티의 역할별 인원 수 정보 // 조율 해제 상태여도 역할별로 정확히 날아옴 mainForm.overlayForm.SetMemberCount(tank, dps, healer); return; } else if (status == 4) { // 매칭 뒤 참가자 확인 현황 패킷 mainForm.overlayForm.SetConfirmStatus(instance, tank, dps, healer); } Log.I("l-queue-updated", instance.Name, status, tank, instance.Tank, healer, instance.Healer, dps, instance.DPS); } else if (opcode == 0x0080) { var roulette = data[2]; var code = BitConverter.ToUInt16(data, 4); Instance instance; if (!Settings.CheatRoulette && roulette != 0) { instance = new Instance { Name = Data.GetRoulette(roulette).Name }; } else { instance = Data.GetInstance(code); } state = State.MATCHED; mainForm.overlayForm.SetDutyAsMatched(instance); if (Settings.FlashWindow) { WinApi.FlashWindow(mainForm.FFXIVProcess); } if (!Settings.ShowOverlay) { mainForm.ShowNotification("notification-queue-matched", instance.Name); } if (Settings.TwitterEnabled) { WebApi.Tweet("tweet-queue-matched", instance.Name); } Log.S("l-queue-matched", instance.Name); } } catch (Exception ex) { Log.Ex(ex, "l-analyze-error-general"); } }
private void HandleMessage(byte[] message) { try { if (message.Length < 32) { // type == 0x0000 이였던 메시지는 여기서 걸러짐 return; } mainForm.overlayForm.SetStatus(true); var opcode = BitConverter.ToUInt16(message, 18); #if !DEBUG if (opcode != 0x0078 && opcode != 0x0079 && opcode != 0x0080 && opcode != 0x006C && opcode != 0x006F && opcode != 0x0121 && opcode != 0x0143 && opcode != 0x022F && // v5.1 opcode != 0x008F && opcode != 0x00AE && opcode != 0x00B3 && opcode != 0x015E && opcode != 0x0121 && opcode != 0x0304 && // v5.11 opcode != 0x0164 && opcode != 0x032D && opcode != 0x03CF && opcode != 0x02A8 && opcode != 0x032F && opcode != 0x0339 && opcode != 0x0002) { return; } #endif var data = message.Skip(32).ToArray(); if (opcode == 0x022F) { var code = BitConverter.ToInt16(data, 4); var type = data[8]; if (type == 0x0B) { Log.I("l-field-instance-entered", Data.GetInstance(code).Name); } else if (type == 0x0C) { Log.I("l-field-instance-left"); } /*if (Settings.ShowOverlay && Settings.AutoOverlayHide) * { * mainForm.overlayForm.Invoke(() => * { * if (type == 0x0B) * { * mainForm.overlayForm.Hide(); * } * else if (type == 0x0C) * { * mainForm.overlayForm.Show(); * } * }); * }*/ } else if (opcode == 0x0143) { var type = data[0]; if (type == 0x9B) { /* * var code = BitConverter.ToUInt16(data, 4); * var progress = data[8]; * * var fate = Data.GetFATE(code); * * //Log.D("\"{0}\" 돌발 진행도 {1}%", fate.Name, progress); */ } else if (type == 0x79) { /* * // 돌발 임무 종료 (지역 이동시 발생할 수 있는 모든 임무에 대해 전부 옴) * * var code = BitConverter.ToUInt16(data, 4); * var status = BitConverter.ToUInt16(data, 28); * * var fate = Data.GetFATE(code); * * //Log.D("\"{0}\" 돌발 종료!", fate.Name); */ } else if (type == 0x74) { // 돌발 임무 발생 (지역 이동시에도 기존 돌발 목록이 옴) var code = BitConverter.ToUInt16(data, 4); var fate = Data.GetFATE(code); if (Settings.FATEs.Contains(code)) { mainForm.overlayForm.SetFATEAsOccured(fate); if (Settings.ShowOverlay) { mainForm.overlayForm.Show(); } Log.I("l-fate-occured-info", fate.Name); if (Settings.FateSound) { fatePlayer = new SoundPlayer(str); fatePlayer.Stream.Position = 0; // fatePlayer가 Play()를 끝내기 전에 다시 Play()를 할 때 (한 번에 여러 돌발이 나타날 때) 버그 방지를 위해 필요한 코드 fatePlayer.Play(); } else if (Settings.CustomSound) { notificationPlayer.Play(); } if (!Settings.ShowOverlay) { mainForm.ShowNotification("notification-fate-occured", fate.Name); } if (Settings.FlashWindow) { WinApi.FlashWindow(mainForm.FFXIVProcess); } if (Settings.TelegramEnabled) { WebApi.Request("telegram", "fate-occured", fate.Name); } if (Settings.DiscordEnabled) { WebApi.Request("discord", "fate-occured", fate.Name); } if (Settings.customHttpRequest && Settings.requestOnFateOccured) { WebApi.customHttpRequest("fate-occured", fate.Name); } } } } else if (opcode == 0x0078) { var status = data[0]; var reason = data[4]; if (status == 0) { state = State.QUEUED; rouletteCode = data[20]; if (Settings.ShowOverlay) { mainForm.overlayForm.Show(); } if (rouletteCode != 0 && (data[15] == 0 || data[15] == 64)) //무작위 임무 신청, 한국서버/글로벌 서버 { var roulette = Data.GetRoulette(rouletteCode); mainForm.overlayForm.SetRoulleteDuty(roulette); Log.I("l-queue-started-roulette", roulette.Name); } else //특정 임무 신청 { rouletteCode = 0; var instances = new List <Instance>(); for (int i = 0; i < 5; i++) { var code = BitConverter.ToUInt16(data, 22 + (i * 2)); if (code == 0) { break; } instances.Add(Data.GetInstance(code)); } if (!instances.Any()) { return; } mainForm.overlayForm.SetDutyCount(instances.Count); Log.I("l-queue-started-general", string.Join(", ", instances.Select(x => x.Name).ToArray())); } } else if (status == 3) { state = reason == 8 ? State.QUEUED : State.IDLE; mainForm.overlayForm.CancelDutyFinder(); Log.E("l-queue-stopped"); } else if (status == 6) { state = State.IDLE; mainForm.overlayForm.CancelDutyFinder(); Log.I("l-queue-entered"); mainForm.overlayForm.instances_callback(lastCode); } else if (status == 4) //글섭에서 매칭 잡혔을 때 출력 { var roulette = data[20]; var code = BitConverter.ToUInt16(data, 22); Instance instance; if (Settings.CustomSound) { notificationPlayer.Play(); } if (!Settings.CheatRoulette && roulette != 0) { instance = new Instance { Name = Data.GetRoulette(roulette).Name }; } else { instance = Data.GetInstance(code); } state = State.MATCHED; mainForm.overlayForm.SetDutyAsMatched(instance); if (Settings.FlashWindow) { WinApi.FlashWindow(mainForm.FFXIVProcess); } if (!Settings.ShowOverlay) { mainForm.ShowNotification("notification-queue-matched", instance.Name); } if (Settings.TelegramEnabled) { WebApi.Request("telegram", "duty-matched", instance.Name); } if (Settings.DiscordEnabled) { WebApi.Request("discord", "duty-matched", instance.Name); } if (Settings.customHttpRequest && Settings.requestOnDutyMatched) { WebApi.customHttpRequest("duty-matched", instance.Name); } Log.S("l-queue-matched", instance.Name); } } else if (opcode == 0x008F || opcode == 0x0164) // v5.1, v5.11 (enroll duty) { var status = data[0]; var reason = data[4]; state = State.QUEUED; // rouletteCode = data[20]; rouletteCode = data[8]; if (Settings.ShowOverlay) { mainForm.overlayForm.Show(); } if (rouletteCode != 0 && (data[15] == 0 || data[15] == 64)) //무작위 임무 신청, 한국서버/글로벌 서버 { var roulette = Data.GetRoulette(rouletteCode); mainForm.overlayForm.SetRoulleteDuty(roulette); Log.I("l-queue-started-roulette", roulette.Name); } else //특정 임무 신청 { rouletteCode = 0; var instances = new List <Instance>(); for (int i = 0; i < 5; i++) { var code = BitConverter.ToUInt16(data, 12 + (i * 4)); if (code == 0) { break; } instances.Add(Data.GetInstance(code)); } if (!instances.Any()) { return; } mainForm.overlayForm.SetDutyCount(instances.Count); mainForm.overlayForm.SetDutyAsMatching(); Log.I("l-queue-started-general", string.Join(", ", instances.Select(x => x.Name).ToArray())); } } else if (opcode == 0x00B3 || opcode == 0x032D) // v5.1, v5.11 (duty matched) { var roulette = rouletteCode; var code = BitConverter.ToUInt16(data, 20); Instance instance; if (Settings.CustomSound) { notificationPlayer.Play(); } if (!Settings.CheatRoulette && roulette != 0) { instance = new Instance { Name = Data.GetRoulette(roulette).Name }; } else { instance = Data.GetInstance(code); } state = State.MATCHED; mainForm.overlayForm.SetDutyAsMatched(instance); if (Settings.FlashWindow) { WinApi.FlashWindow(mainForm.FFXIVProcess); } if (!Settings.ShowOverlay) { mainForm.ShowNotification("notification-queue-matched", instance.Name); } if (Settings.TelegramEnabled) { WebApi.Request("telegram", "duty-matched", instance.Name); } if (Settings.DiscordEnabled) { WebApi.Request("discord", "duty-matched", instance.Name); } if (Settings.customHttpRequest && Settings.requestOnDutyMatched) { WebApi.customHttpRequest("duty-matched", instance.Name); } Log.S("l-queue-matched", instance.Name); } else if (opcode == 0x006F) { var status = data[0]; if (status == 0) { // 플레이어가 매칭 참가 확인 창에서 취소를 누르거나 참가 확인 제한 시간이 초과됨 // 매칭 중단을 알리기 위해 상단 2DB status 3 패킷이 연이어 옴 } if (status == 1) { // 플레이어가 매칭 참가 확인 창에서 확인을 누름 // 다른 매칭 인원들도 전부 확인을 눌렀을 경우 입장을 위해 상단 2DB status 6 패킷이 옴 mainForm.overlayForm.StopBlink(); } } else if (opcode == 0x015E) // cancel duty { if (data[3] == 0 || data[3] == 8) // v5.1에서 8로 바뀌었다고 들음 { state = State.IDLE; mainForm.overlayForm.CancelDutyFinder(); Log.E("l-queue-stopped"); } } else if (opcode == 0x0121) // v5.1 commence duty { var status = data[5]; if (status == 128) { // 매칭 참가 신청 확인 창에서 확인을 누름 mainForm.overlayForm.StopBlink(); } } else if (opcode == 0x03CF) // v5.11 cancel duty { var status = data[0]; if (status == 0x73) // 매칭 취소 { state = State.IDLE; mainForm.overlayForm.CancelDutyFinder(); Log.E("l-queue-stopped"); } } else if (opcode == 0x0079) { var code = BitConverter.ToUInt16(data, 0); var status = data[8]; var tank = data[9]; var dps = data[10]; var healer = data[11]; var order = data[4]; var instance = Data.GetInstance(code); if (status == 1) { // 인원 현황 패킷 var member = tank * 10000 + dps * 100 + healer; if (state == State.MATCHED && lastMember != member) { // 매칭도중일 때 인원 현황 패킷이 오고 마지막 인원 정보와 다른 경우에 누군가에 의해 큐가 취소된 경우. state = State.QUEUED; mainForm.overlayForm.CancelDutyFinder(); } else if (state == State.IDLE) { // 프로그램이 매칭 중간에 켜짐 state = State.QUEUED; mainForm.overlayForm.SetDutyCount(-1); // 알 수 없음으로 설정함 (TODO: 알아낼 방법 있으면 정확히 나오게 수정하기) if (rouletteCode > 0 || (tank == 0 && dps == 0 && healer == 0)) { mainForm.overlayForm.SetDutyStatus(order); } else { mainForm.overlayForm.SetDutyStatus(instance, tank, dps, healer); } } else if (state == State.QUEUED) { if (rouletteCode > 0 || (tank == 0 && dps == 0 && healer == 0)) { mainForm.overlayForm.SetDutyStatus(order); } else { mainForm.overlayForm.SetDutyStatus(instance, tank, dps, healer); } } // 직전 맴버 구성과 같은 상황이면 알림주지 않음 if (Settings.TelegramEnabled && Settings.TelegramQueueStatusEnabled) { if (rouletteCode == 0 && lastMember != member || !(tank == 0 && dps == 0 && healer == 0)) // 무작위 임무가 아님 (Not roulette duty) { WebApi.Request("telegram", "duty-status", $"{instance.Name}, {tank}/{instance.Tank}, {healer}/{instance.Healer}, {dps}/{instance.DPS}"); } else if (order != 0 && lastOrder != order) // 매칭 현황을 받아오는 중이면 제외 (except 'retrieving information') { var roulette = Data.GetRoulette(rouletteCode); WebApi.Request("telegram", "duty-status-roulette", $"{roulette.Name} - #{order}"); } } lastMember = member; lastOrder = order; } else if (status == 2) { // 현재 매칭된 파티의 역할별 인원 수 정보 // 조율 해제 상태여도 역할별로 정확히 날아옴 mainForm.overlayForm.SetMemberCount(tank, dps, healer); return; } else if (status == 4) { // 매칭 뒤 참가자 확인 현황 패킷 mainForm.overlayForm.SetConfirmStatus(instance, tank, dps, healer); } lastCode = code; Log.I("l-queue-updated", instance.Name, status, tank, instance.Tank, healer, instance.Healer, dps, instance.DPS); } else if (opcode == 0x0304) { //var code = BitConverter.ToUInt16(data, 0); // 이제 던전 안알려줌. 대신 max 인원 알려줌. //var status = data[8]; var order = data[6]; var waitTime = data[7]; var tank = data[8]; var tankMax = data[9]; var healer = data[10]; var healerMax = data[11]; var dps = data[12]; var dpsMax = data[13]; //var instance = Data.GetInstance(code); //if (status == 1) //{ // 인원 현황 패킷 var member = tank * 10000 + dps * 100 + healer; if (state == State.MATCHED && lastMember != member) { // 매칭도중일 때 인원 현황 패킷이 오고 마지막 인원 정보와 다른 경우에 누군가에 의해 큐가 취소된 경우. state = State.QUEUED; mainForm.overlayForm.CancelDutyFinder(); } else if (state == State.IDLE) { // 프로그램이 매칭 중간에 켜짐 state = State.QUEUED; mainForm.overlayForm.SetDutyCount(-1); // 알 수 없음으로 설정함 (TODO: 알아낼 방법 있으면 정확히 나오게 수정하기) if (rouletteCode > 0) { mainForm.overlayForm.SetDutyStatus(order); } else { mainForm.overlayForm.SetDutyStatus(tank, tankMax, dps, dpsMax, healer, healerMax); } } else if (state == State.QUEUED) { if (rouletteCode > 0) { mainForm.overlayForm.SetDutyStatus(order); } else { mainForm.overlayForm.SetDutyStatus(tank, tankMax, dps, dpsMax, healer, healerMax); } } // 직전 맴버 구성과 같은 상황이면 알림주지 않음 if (Settings.TelegramEnabled && Settings.TelegramQueueStatusEnabled) { if (rouletteCode == 0 && lastMember != member || !(tank == 0 && dps == 0 && healer == 0)) // 무작위 임무가 아님 (Not roulette duty) { WebApi.Request("telegram", "duty-status", $"{tank}/{tankMax}, {healer}/{healerMax}, {dps}/{dpsMax}"); } else if (order != 0 && lastOrder != order) // 매칭 현황을 받아오는 중이면 제외 (except 'retrieving information') { var roulette = Data.GetRoulette(rouletteCode); WebApi.Request("telegram", "duty-status-roulette", $"{roulette.Name} - #{order}"); } } lastMember = member; lastOrder = order; //} /*else if (status == 2) * { * // 현재 매칭된 파티의 역할별 인원 수 정보 * // 조율 해제 상태여도 역할별로 정확히 날아옴 * mainForm.overlayForm.SetMemberCount(tank, dps, healer); * return; * }*/ Log.I("l-queue-updated", "", /*status*/ 1, tank, tankMax, healer, healerMax, dps, dpsMax); } else if (opcode == 0x00AE || opcode == 0x032F) // v5.1, v5.11 매칭 뒤 참가자 확인 현황 패킷 { var code = BitConverter.ToUInt16(data, 8); var tank = data[12]; var healer = data[14]; var dps = data[16]; var instance = Data.GetInstance(code); mainForm.overlayForm.SetConfirmStatus(instance, tank, dps, healer); lastCode = code; Log.I("l-queue-updated", instance.Name, 4, tank, instance.Tank, healer, instance.Healer, dps, instance.DPS); } else if (opcode == 0x0080) { var roulette = data[2]; var code = BitConverter.ToUInt16(data, 4); Instance instance; if (Settings.CustomSound) { notificationPlayer.Play(); } if (!Settings.CheatRoulette && roulette != 0) { instance = new Instance { Name = Data.GetRoulette(roulette).Name }; } else { instance = Data.GetInstance(code); } state = State.MATCHED; mainForm.overlayForm.SetDutyAsMatched(instance); if (Settings.FlashWindow) { WinApi.FlashWindow(mainForm.FFXIVProcess); } if (!Settings.ShowOverlay) { mainForm.ShowNotification("notification-queue-matched", instance.Name); } if (Settings.TelegramEnabled) { WebApi.Request("telegram", "duty-matched", instance.Name); } if (Settings.DiscordEnabled) { WebApi.Request("discord", "duty-matched", instance.Name); } if (Settings.customHttpRequest && Settings.requestOnDutyMatched) { WebApi.customHttpRequest("duty-matched", instance.Name); } Log.S("l-queue-matched", instance.Name); } } catch (Exception ex) { Log.Ex(ex, "l-analyze-error-general"); } }
private void HandleMessage(byte[] message) { try { if (message.Length < 32) { // type == 0x0000 Messages were filtered here return; } var opcode = BitConverter.ToUInt16(message, 18); #if !DEBUG if (opcode != 0x0078 && opcode != 0x0079 && opcode != 0x0080 && opcode != 0x006C && opcode != 0x006F && opcode != 0x0121 && opcode != 0x0143 && opcode != 0x022F) { return; } #endif var data = message.Skip(32).ToArray(); // Entry / exit if (opcode == 0x022F) { var code = BitConverter.ToInt16(data, 4); var type = data[8]; Log.B(data); if (type == 0x0B) { //Log.I("l-field-instance-entered", Data.GetInstance(code).Name); // EVENT: When worn, code = dungeon code // //Log.I("l-field-instance-entered " + code); Log.I("Incoming code=" + code); fireEvent(EventType.INSTANCE_ENTER, new int[] { code }); } else if (type == 0x0C) { // EVENT: Come out of dress //Log.I("l-field-instance-left"); Log.I("I'm leaving=" + code); fireEvent(EventType.INSTANCE_EXIT, new int[] { code }); } } //Outbreak: occurring, in progress, ending else if (opcode == 0x0143) { var type = data[0]; if (type == 0x9B) { var code = BitConverter.ToUInt16(data, 4); var progress = data[8]; /* * * var fate = Data.GetFATE(code); * * //Log.D("\"{0}\" Breakthrough progress {1}%", fate.Name, progress); */ // EVENT: breakthrough, code = breakthrough, progress = progress percentage //Log.I(code + " Breakthrough progress " + progress); fireEvent(EventType.FATE_PROGRESS, new int[] { code, progress }); } else if (type == 0x79) { // Unexpected mission termination (for all missions that may occur when moving the area) var code = BitConverter.ToUInt16(data, 4); var status = BitConverter.ToUInt16(data, 28); /* * var fate = Data.GetFATE(code); * * //Log.D("\"{0}\" Break out!", fate.Name); */ // EVENT: At the end of the mission, code = emergency code, Log.I("Abrupt end=" + code + ", status=" + status); fireEvent(EventType.FATE_END, new int[] { code, status }); } else if (type == 0x74) { // Occurrence of an outbreak (even if you move the area, the existing breakdown list comes) // EVENT: Occurrence of an unexpected mission, code = Unexpected code (also occurs when moving map) var code = BitConverter.ToUInt16(data, 4); Log.I("Abrupt =" + code); //var fate = Data.GetFATE(code); fireEvent(EventType.FATE_BEGIN, new int[] { code }); } } // Matching information else if (opcode == 0x0078) { var status = data[0]; var reason = data[4]; //apply if (status == 0) { NetCompatibility = false; state = State.QUEUED; var rouletteCode = data[20]; if (rouletteCode != 0 && (data[15] == 0 || data[15] == 64)) //Random assignment application, Korea server / global server { //var roulette = Data.GetRoulette(rouletteCode); //mainForm.overlayForm.SetRoulleteDuty(roulette); //Log.I("l-queue-started-roulette", roulette.Name); Log.I("Matching application, random type=" + rouletteCode); fireEvent(EventType.MATCH_BEGIN, new int[] { (int)MatchType.ROULETTE, rouletteCode }); } else //Apply for a specific mission { // var instances = new List<Instance>(); var instances = new List <int>(); for (int i = 0; i < 5; i++) { var code = BitConverter.ToUInt16(data, 22 + (i * 2)); if (code == 0) { break; } //instances.Add(Data.GetInstance(code)); instances.Add(code); } if (!instances.Any()) { return; } var args = new List <int>(); args.Add((int)MatchType.SELECTIVE); args.Add(instances.Count); for (int i = 0; i < instances.Count; i++) { args.Add(instances[i]); } // Log.I("l-queue-started-general", //string.Join(", ", instances.Select(x => x.Name).ToArray()));*/ Log.I("Matching application, selected =", string.Join(", ", instances) + ", count=" + instances.Count); fireEvent(EventType.MATCH_BEGIN, args.ToArray()); } } // cancel else if (status == 3) { state = reason == 8 ? State.QUEUED : State.IDLE; //mainForm.overlayForm.CancelDutyFinder(); //Log.E("l-queue-stopped"); Log.I("Unmatch, reason=" + reason); fireEvent(EventType.MATCH_END, new int[] { (int)MatchEndType.CANCELLED }); } // Entered else if (status == 6) { state = State.IDLE; //mainForm.overlayForm.CancelDutyFinder(); //Log.I("l-queue-entered"); Log.I("Match entry"); fireEvent(EventType.MATCH_END, new int[] { (int)MatchEndType.ENTER_INSTANCE }); } // Matching else if (status == 4) // Output when matched in the foreground { var roulette = data[20]; var code = BitConverter.ToUInt16(data, 22); Instance instance; //If the matched indent information can not be checked, if (roulette != 0) { instance = new Instance { Name = Data.GetRoulette(roulette).Name }; } else { instance = Data.GetInstance(code); } state = State.MATCHED; Log.I("Matching (Gloss) Match type=" + roulette + ", Matched indones=" + code); fireEvent(EventType.MATCH_ALERT, new int[] { roulette, code }); } } else if (opcode == 0x006F) { /* * var status = data[0]; * * if (status == 0) * { * // Player clicks Cancel in the Match Participation Confirmation window or the Participation Confirmation Timeout is exceeded * // Upper 2db status 3 packets coming in to notify matching termination * log.i("Matched stopped status=" + status + ", cancel or timeout"); * } * if (status == 1) * { * // Player clicks OK in the matching entry confirmation window * // If all other matching personnel have clicked OK, the top 2db status 6 packets for admission * //mainform.overlayform.stopblink(); * log.i("Matched status =" + status + ", press confirm or others); * }*/ } else if (opcode == 0x0121) // Global server { var status = data[5]; if (status == 128) { //Click OK on the Matching Application Confirmation window //mainForm.overlayForm.StopBlink(); Log.I("Matching, Matching Click OK in the application confirmation window (Global)"); } } // Status in matched state else if (opcode == 0x0079) { var code = BitConverter.ToUInt16(data, 0); byte status = 0; byte tank = 0; byte dps = 0; byte healer = 0; if (NetCompatibility) // V4.5 版本兼容性 { order = data[4]; //职能等待顺序 order--; status = data[8]; tank = data[9]; dps = data[10]; healer = data[11]; } else { order = data[5]; status = data[4]; tank = data[5]; dps = data[6]; healer = data[7]; } if (status == 0 && tank == 0 && healer == 0 && dps == 0) // 检查数据异常进行兼容处理 { NetCompatibility = true; order = 255; status = data[8]; tank = data[9]; dps = data[10]; healer = data[11]; } //var instance = Data.GetInstance(code); if (status == 1) { // Personnel status packet var member = tank * 10000 + dps * 100 + healer; if (state == State.MATCHED && lastMember != member) { // If the queue status is canceled by someone when the status packet arrives at the time of matching and is different from the last person information. state = State.QUEUED; //mainForm.overlayForm.CancelDutyFinder(); Log.I("Match progress, someone canceled"); } else if (state == State.IDLE) { // Program is on in the middle of matching state = State.QUEUED; //mainForm.overlayForm.SetDutyCount(-1); // Set to unknown (TODO: If there is a way to find out, fix it to be correct) //mainForm.overlayForm.SetDutyStatus(instance, tank, dps, healer); } else if (state == State.QUEUED) { //mainForm.overlayForm.SetDutyStatus(instance, tank, dps, healer); } lastMember = member; } else if (status == 2) { // Information on the number of people by role of the currently matched party // Even if it is in un-tuned state, //mainForm.overlayForm.SetMemberCount(tank, dps, healer); return; } else if (status == 4) { // After Matching, Participant Confirmation Status Packet //mainForm.overlayForm.SetConfirmStatus(instance, tank, dps, healer); } //Log.I("l-queue-updated", instance.Name, status, tank, instance.Tank, healer, instance.Healer, dps, instance.DPS); Log.I("Matching progress, code=" + code + ", " + status + ", T " + tank + ", H " + healer + ", D " + dps); fireEvent(EventType.MATCH_PROGRESS, new int[] { code, status, tank, healer, dps }); } else if (opcode == 0x0080) { var roulette = data[2]; var code = BitConverter.ToUInt16(data, 4); Instance instance; if (roulette != 0) { instance = new Instance { Name = Data.GetRoulette(roulette).Name }; } else { instance = Data.GetInstance(code); } state = State.MATCHED; Log.S("l-queue-matched " + code); fireEvent(EventType.MATCH_ALERT, new int[] { roulette, code }); } } catch (Exception ex) { Log.Ex(ex, "[" + pid + "]l-analyze-error-general"); } }
private void HandleMessage(byte[] message) { try { if (message.Length < 32) { // type == 0x0000 이였던 메시지는 여기서 걸러짐 return; } mainForm.overlayForm.SetStatus(true); var opcode = BitConverter.ToUInt16(message, 18); if (opcode != 0x0142 && opcode != 0x0143 && opcode != 0x006C && opcode != 0x0074 && opcode != 0x0076 && opcode != 0x02DB && opcode != 0x006F && opcode != 0x02DE && opcode != 0x0339) { return; } var data = message.Skip(32).ToArray(); if (opcode == 0x0142) { var type = data[0]; if (type == 0xCF) { var selfkey = BitConverter.ToInt32(message, 8); var charkey = BitConverter.ToInt32(message, 40); var code = BitConverter.ToUInt16(data, 16); var zone = Data.GetArea(code); byte teleMeasure = message[36]; if (selfkey == charkey) // isSelf { ushort lastCode = (BitConverter.ToUInt16(System.Text.Encoding.Unicode.GetBytes(new char[] { Data.GetAreaName(code).Last() }), 0)); string lastChar = ((lastCode - 0xAC00U) % 28 == 0 || lastCode - 0xAC00U == 8 ? "로" : "으로"); if (teleMeasure != 0x0C) { Log.D("{1}{2} 지역으로 이동했습니다. ({0})", code, Data.GetAreaName(code), lastChar); } else { Log.D("임무에서 퇴장했습니다. ({0})", teleMeasure); } mainForm.overlayForm.currentArea = code; } } } else if (opcode == 0x0143) { var type = data[0]; if (type == 0x9B) { /* * var code = BitConverter.ToUInt16(data, 4); * var progress = data[8]; * * var fate = Data.GetFATE(code); * * //Log.D("\"{0}\" 돌발 진행도 {1}%", fate.Name, progress); */ } else if (type == 0x79) { /* * // 돌발 임무 종료 (지역 이동시 발생할 수 있는 모든 임무에 대해 전부 옴) * * var code = BitConverter.ToUInt16(data, 4); * var status = BitConverter.ToUInt16(data, 28); * * var fate = Data.GetFATE(code); * * //Log.D("\"{0}\" 돌발 종료!", fate.Name); */ } else if (type == 0x74) { // 돌발 임무 발생 (지역 이동시에도 기존 돌발 목록이 옴) var code = BitConverter.ToUInt16(data, 4); var fate = Data.GetFATE(code); if (Settings.FATEs.Contains(code)) { mainForm.overlayForm.SetFATEAsAppeared(fate); if (Settings.FlashWindow) { WinApi.FlashWindow(mainForm.FFXIVProcess); } if (Settings.TwitterEnabled) { WebApi.Tweet("< {0} > 돌발 발생!", fate.Name); } } Log.D("\"{0}\" 돌발 발생!", fate.Name); } } else if (opcode == 0x006C) { var code = BitConverter.ToUInt16(data, 192); var instance = Data.GetInstance(code); state = State.QUEUED; mainForm.overlayForm.SetDutyCount(1); Log.I("DFAN: 매칭 시작됨 (6C) [{0}]", instance.Name); } else if (opcode == 0x0074) { var instances = new List <Instance>(); for (int i = 0; i < 5; i++) { var code = BitConverter.ToUInt16(data, 192 + (i * 2)); if (code == 0) { break; } instances.Add(Data.GetInstance(code)); } if (!instances.Any()) { return; } state = State.QUEUED; mainForm.overlayForm.SetDutyCount(instances.Count); Log.I("DFAN: 매칭 시작됨 (74) [{0}]", string.Join(", ", instances.Select(x => x.Name).ToArray())); } else if (opcode == 0x0076) { var code = data[192]; var roulette = Data.GetRoulette(code); state = State.QUEUED; mainForm.overlayForm.SetRoulleteDuty(roulette); Log.I("DFAN: 무작위 임무 매칭 시작됨 [{0}]", roulette.Name); } else if (opcode == 0x02DB) { var status = data[0]; if (status == 3) { state = State.IDLE; mainForm.overlayForm.CancelDutyFinder(); Log.E("DFAN: 매칭 중지됨 (2DB)"); } else if (status == 6) { state = State.IDLE; mainForm.overlayForm.CancelDutyFinder(); Log.I("DFAN: 입장함"); } } else if (opcode == 0x006F) { var status = data[0]; if (status == 0) { // 플레이어가 매칭 참가 확인 창에서 취소를 누르거나 참가 확인 제한 시간이 초과됨 // 매칭 중단을 알리기 위해 상단 2DB status 3 패킷이 연이어 옴 } if (status == 1) { // 플레이어가 매칭 참가 확인 창에서 확인을 누름 // 다른 매칭 인원들도 전부 확인을 눌렀을 경우 입장을 위해 상단 2DB status 6 패킷이 옴 mainForm.overlayForm.StopBlink(); } } else if (opcode == 0x02DE) { var code = BitConverter.ToUInt16(data, 0); var status = data[4]; var tank = data[5]; var dps = data[6]; var healer = data[7]; var instance = Data.GetInstance(code); if (status == 1) { // 인원 현황 패킷 var member = tank * 10000 + dps * 100 + healer; if (state == State.MATCHED && lastMember != member) { // 매칭도중일 때 인원 현황 패킷이 오고 마지막 인원 정보와 다른 경우에 누군가에 의해 큐가 취소된 경우. state = State.QUEUED; mainForm.overlayForm.CancelDutyFinder(); } else if (state == State.IDLE) { // 프로그램이 매칭 중간에 켜짐 state = State.QUEUED; mainForm.overlayForm.SetDutyCount(-1); // 알 수 없음으로 설정함 (TODO: 알아낼 방법 있으면 정확히 나오게 수정하기) mainForm.overlayForm.SetDutyStatus(instance, tank, dps, healer); } else if (state == State.QUEUED) { mainForm.overlayForm.SetDutyStatus(instance, tank, dps, healer); } lastMember = member; } else if (status == 2) { // 현재 매칭된 파티의 역할별 인원 수 정보 // 조율 해제 상태여도 역할별로 정확히 날아옴 mainForm.overlayForm.SetMemberCount(tank, dps, healer); return; } else if (status == 4) { // 매칭 뒤 참가자 확인 현황 패킷 mainForm.overlayForm.SetConfirmStatus(instance, tank, dps, healer); } Log.I("DFAN: 매칭 상태 업데이트됨 [{0}, {1}, {2}/{3}, {4}/{5}, {6}/{7}]", instance.Name, status, tank, instance.Tank, healer, instance.Healer, dps, instance.DPS); } else if (opcode == 0x0339) { var roulette = data[3]; var code = BitConverter.ToUInt16(data, 4); Instance instance; if (!Settings.CheatRoulette && roulette != 0) { instance = new Instance(Data.GetRoulette(roulette).Name, 0, 0, 0); } else { instance = Data.GetInstance(code); } state = State.MATCHED; mainForm.overlayForm.SetDutyAsMatched(instance); if (Settings.FlashWindow) { WinApi.FlashWindow(mainForm.FFXIVProcess); } if (!Settings.ShowOverlay) { mainForm.ShowNotification("< {0} > 매칭!", instance.Name); } if (Settings.TwitterEnabled) { WebApi.Tweet("< {0} > 매칭!", instance.Name); } Log.S("DFAN: 매칭됨 [{0}]", instance.Name); } } catch (Exception ex) { Log.Ex(ex, "메시지 처리중 에러 발생함"); } }
private async System.Threading.Tasks.Task HandleMessageAsync(byte[] message) { try { if (message.Length < 32) { // type == 0x0000 이였던 메시지는 여기서 걸러짐 // Messages with type == 0x0000 are filtered out here return; } mainForm.overlayForm.SetStatus(true); var opcode = BitConverter.ToUInt16(message, 18); #if !DEBUG if (opcode != 0x0078 && opcode != 0x0079 && opcode != 0x0080 && opcode != 0x006C && opcode != 0x006F && opcode != 0x0121 && opcode != 0x0143 && opcode != 0x022F) { return; } #endif var data = message.Skip(32).ToArray(); if (!seenPackets.Contains(opcode)) { Log.I(string.Format("Saw packet type {0:x4}.", opcode)); seenPackets.Add(opcode); } if (opcode == 0x022F) { var code = BitConverter.ToInt16(data, 4); var type = data[8]; Log.B(data); if (type == 0x0B) { Log.I("l-field-instance-entered", Data.GetInstance(code).Name); } else if (type == 0x0C) { Log.I("l-field-instance-left"); } if (Settings.ShowOverlay && Settings.AutoOverlayHide) { mainForm.overlayForm.Invoke(() => { if (type == 0x0B) { mainForm.overlayForm.Hide(); } else if (type == 0x0C) { mainForm.overlayForm.Show(); } }); } } else if (opcode == 0x0143) { var type = data[0]; if (type == 0x9B) { /* * var code = BitConverter.ToUInt16(data, 4); * var progress = data[8]; * * var fate = Data.GetFATE(code); * * //Log.D("\"{0}\" 돌발 진행도 {1}%", fate.Name, progress); */ } else if (type == 0x79) { /* * // 돌발 임무 종료 (지역 이동시 발생할 수 있는 모든 임무에 대해 전부 옴) * * var code = BitConverter.ToUInt16(data, 4); * var status = BitConverter.ToUInt16(data, 28); * * var fate = Data.GetFATE(code); * * //Log.D("\"{0}\" 돌발 종료!", fate.Name); */ } else if (type == 0x74) { // 돌발 임무 발생 (지역 이동시에도 기존 돌발 목록이 옴) var code = BitConverter.ToUInt16(data, 4); var fate = Data.GetFATE(code); if (Settings.FATEs.Contains(code)) { mainForm.overlayForm.SetFATEAsOccured(fate); Log.I("l-fate-occured-info", fate.Name); if (Settings.FlashWindow) { WinApi.FlashWindow(mainForm.FFXIVProcess); } if (Settings.TwitterEnabled) { WebApi.Tweet("tweet-fate-occured", fate.Name); } } } } /*else if (opcode == 0x006C) // 3.5 cross-world 파티 참가하면 문제가 발생하는 부분. * { * var code = BitConverter.ToUInt16(data, 192); * * var instance = Data.GetInstance(code); * * state = State.QUEUED; * mainForm.overlayForm.SetDutyCount(1); * * Log.I("l-queue-started-general", instance.Name); * }*/ else if (opcode == 0x0078) { var status = data[0]; var reason = data[4]; if (status == 0) { state = State.QUEUED; var rouletteCode = data[20]; // Random assignment, Korea server / global server if (rouletteCode != 0 && (data[15] == 0 || data[15] == 64)) //무작위 임무 신청, 한국서버/글로벌 서버 { var roulette = Data.GetRoulette(rouletteCode); mainForm.overlayForm.SetRoulleteDuty(roulette); Log.I("l-queue-started-roulette", roulette.Name); } else //특정 임무 신청 // Apply for a specific mission { var instances = new List <Instance>(); for (int i = 0; i < 5; i++) { var code = BitConverter.ToUInt16(data, 22 + (i * 2)); if (code == 0) { break; } instances.Add(Data.GetInstance(code)); } if (!instances.Any()) { return; } mainForm.overlayForm.SetDutyCount(instances.Count); Log.I("l-queue-started-general", string.Join(", ", instances.Select(x => x.Name).ToArray())); } } else if (status == 3) { state = reason == 8 ? State.QUEUED : State.IDLE; mainForm.overlayForm.CancelDutyFinder(); Log.E("l-queue-stopped"); } else if (status == 6) { state = State.IDLE; mainForm.overlayForm.CancelDutyFinder(); Log.I("l-queue-entered"); } else if (status == 4) //글섭에서 매칭 잡혔을 때 출력 // Output when matched in Gap { var roulette = data[20]; var code = BitConverter.ToUInt16(data, 22); Instance instance; if (!Settings.CheatRoulette && roulette != 0) { instance = new Instance { Name = Data.GetRoulette(roulette).Name }; } else { instance = Data.GetInstance(code); } state = State.MATCHED; mainForm.overlayForm.SetDutyAsMatched(instance); if (Settings.FlashWindow) { WinApi.FlashWindow(mainForm.FFXIVProcess); } if (!Settings.ShowOverlay) { mainForm.ShowNotification("notification-queue-matched", instance.Name); } if (Settings.TwitterEnabled) { WebApi.Tweet("tweet-queue-matched", instance.Name); } Log.S("l-queue-matched", instance.Name); } } else if (opcode == 0x006F) { var status = data[0]; if (status == 0) { // 플레이어가 매칭 참가 확인 창에서 취소를 누르거나 참가 확인 제한 시간이 초과됨 // 매칭 중단을 알리기 위해 상단 2DB status 3 패킷이 연이어 옴 // Player clicks Cancel in the Match Participation Confirmation window or the Participation Confirmation Timeout is exceeded // Upper 2DB status 3 packets in succession } if (status == 1) { // 플레이어가 매칭 참가 확인 창에서 확인을 누름 // 다른 매칭 인원들도 전부 확인을 눌렀을 경우 입장을 위해 상단 2DB status 6 패킷이 옴 // Player clicks OK in the matching entry confirmation window // If all other matching personnel have clicked OK, the top 2DB status 6 packets for admission mainForm.overlayForm.StopBlink(); } } else if (opcode == 0x0121) // Global server { var status = data[5]; if (status == 128) { // 매칭 참가 신청 확인 창에서 확인을 누름 // Click OK on Confirmation of Matching Application mainForm.overlayForm.StopBlink(); } } else if (opcode == 0x0079) { var code = BitConverter.ToUInt16(data, 0); var status = data[4]; var tank = data[5]; var dps = data[6]; var healer = data[7]; var instance = Data.GetInstance(code); if (status == 1) { // 인원 현황 패킷 // Personnel Status Packet var member = tank * 10000 + dps * 100 + healer; if (state == State.MATCHED && lastMember != member) { // 매칭도중일 때 인원 현황 패킷이 오고 마지막 인원 정보와 다른 경우에 누군가에 의해 큐가 취소된 경우. // If the queue status is canceled by somebody when the status packet arrives when it is in the // middle of matching and is different from the last passer's information. state = State.QUEUED; mainForm.overlayForm.CancelDutyFinder(); } else if (state == State.IDLE) { // 프로그램이 매칭 중간에 켜짐 // program is on in the middle of matching state = State.QUEUED; mainForm.overlayForm.SetDutyCount(-1); // 알 수 없음으로 설정함 (TODO: 알아낼 방법 있으면 정확히 나오게 수정하기) // Set to unknown (TODO: if there is a way to find out, fix it to be correct) mainForm.overlayForm.SetDutyStatus(instance, tank, dps, healer); } else if (state == State.QUEUED) { mainForm.overlayForm.SetDutyStatus(instance, tank, dps, healer); } lastMember = member; } else if (status == 2) { // 현재 매칭된 파티의 역할별 인원 수 정보 // 조율 해제 상태여도 역할별로 정확히 날아옴 // Information on the number of people by role of the currently matched party // Even if it is in un-tuned state, mainForm.overlayForm.SetMemberCount(tank, dps, healer); return; } else if (status == 4) { // 매칭 뒤 참가자 확인 현황 패킷 // Participant check status packet after matching mainForm.overlayForm.SetConfirmStatus(instance, tank, dps, healer); } Log.I("l-queue-updated", instance.Name, status, tank, instance.Tank, healer, instance.Healer, dps, instance.DPS); } else if (opcode == 0x0080) { var roulette = data[2]; var code = BitConverter.ToUInt16(data, 4); Instance instance; if (!Settings.CheatRoulette && roulette != 0) { instance = new Instance { Name = Data.GetRoulette(roulette).Name }; } else { instance = Data.GetInstance(code); } state = State.MATCHED; mainForm.overlayForm.SetDutyAsMatched(instance); if (Settings.FlashWindow) { WinApi.FlashWindow(mainForm.FFXIVProcess); } if (!Settings.ShowOverlay) { mainForm.ShowNotification("notification-queue-matched", instance.Name); } if (Settings.TwitterEnabled) { WebApi.Tweet("tweet-queue-matched", instance.Name); } Log.S("l-queue-matched", instance.Name); } else if (opcode == 0x00e6) { var itemId = BitConverter.ToUInt32(data, 0); int offset = 4; while (offset < data.Length) { var resp = await HandleSaleMessageAsync(itemId, data, offset); if (resp < 0) { break; } offset += resp; } //Log.Bu(data); } else if (opcode == 0x00e1 || opcode == 0xe2 || opcode == 0xe6) { var itemId = BitConverter.ToUInt32(data, 0); // Log.I(string.Format("packet {0:x4} {0}", itemId)); //Log.Bu(data); } } catch (Exception ex) { Log.Ex(ex, "l-analyze-error-general"); } }
private void HandleMessage(byte[] message) { try { //Log.Echo(BitConverter.ToString(message)); if (message.Length < 32) { // type == 0x0000 이였던 메시지는 여기서 걸러짐 return; } mainForm.overlayForm.SetStatus(true); var opcode = BitConverter.ToUInt16(message, 18); //Log.Debug("Debug:opcode" + opcode.ToString()); #if !DEBUG if (opcode != 0x0078 && opcode != 0x0079 && opcode != 0x0080 && opcode != 0x006C && opcode != 0x006F && opcode != 0x0121 && opcode != 0x0143 && opcode != 0x022F) { return; } #endif var data = message.Skip(32).ToArray(); if (opcode == 0x022F) { var code = BitConverter.ToInt16(data, 4); var type = data[8]; if (type == 0x0B) { Log.I("l-field-instance-entered", Data.GetInstance(code).Name); } else if (type == 0x0C) { Log.I("l-field-instance-left"); } //if (Settings.ShowOverlay && Settings.AutoOverlayHide) //{ // mainForm.overlayForm.Invoke(() => // { // if (type == 0x0B) // { // mainForm.overlayForm.Hide(); // } // else if (type == 0x0C) // { // mainForm.overlayForm.Show(); // } // }); //} } else if (opcode == 0x0143) { var type = data[0]; if (type == 0x9B) { /* * var code = BitConverter.ToUInt16(data, 4); * var progress = data[8]; * * var fate = Data.GetFATE(code); * * //Log.D("\"{0}\" 돌발 진행도 {1}%", fate.Name, progress); */ } else if (type == 0x79) { /* * // 돌발 임무 종료 (지역 이동시 발생할 수 있는 모든 임무에 대해 전부 옴) * * var code = BitConverter.ToUInt16(data, 4); * var status = BitConverter.ToUInt16(data, 28); * * var fate = Data.GetFATE(code); * * //Log.D("\"{0}\" 돌발 종료!", fate.Name); */ } else if (type == 0x74) { // 돌발 임무 발생 (지역 이동시에도 기존 돌발 목록이 옴) var code = BitConverter.ToUInt16(data, 4); var fate = Data.GetFATE(code); if (Settings.FATEs.Contains(code)) { if (Settings.AutoTracker && mainForm.TrackerFormLoaded) { mainForm.TrackerForm.set_nm_killed(code); } mainForm.overlayForm.SetFATEAsOccured(fate); } } } /*else if (opcode == 0x006C) // 3.5 cross-world 파티 참가하면 문제가 발생하는 부분. * { * var code = BitConverter.ToUInt16(data, 192); * * var instance = Data.GetInstance(code); * * state = State.QUEUED; * mainForm.overlayForm.SetDutyCount(1); * * Log.I("l-queue-started-general", instance.Name); * }*/ else if (opcode == 0x0078) { var status = data[0]; var reason = data[4]; if (status == 0) { NetCompatibility = false; state = State.QUEUED; rouletteCode = data[20]; if (rouletteCode != 0 && (data[15] == 0 || data[15] == 64)) //무작위 임무 신청, 한국서버/글로벌 서버 { var roulette = Data.GetRoulette(rouletteCode); mainForm.overlayForm.SetRoulleteDuty(roulette); mainForm.overlayForm.queueCode = rouletteCode; Log.I("l-queue-started-roulette", roulette.Name); } else //특정 임무 신청 { rouletteCode = 0; var instances = new List <Instance>(); for (int i = 0; i < 5; i++) { var code = BitConverter.ToUInt16(data, 22 + (i * 2)); if (code == 0) { break; } instances.Add(Data.GetInstance(code)); } if (!instances.Any()) { return; } mainForm.overlayForm.SetDutyCount(instances.Count); mainForm.overlayForm.queueCount = instances.Count; Log.I("l-queue-started-general", string.Join(", ", instances.Select(x => x.Name).ToArray())); } } else if (status == 3) { state = reason == 8 ? State.QUEUED : State.IDLE; if (reason == 8) { mainForm.overlayForm.CancelDuty(); } else { mainForm.overlayForm.CancelDutyFinder(); Log.E("l-queue-stopped"); } } else if (status == 6) { state = State.IDLE; mainForm.overlayForm.CancelDutyFinder(); Log.I("l-queue-entered"); mainForm.overlayForm.instances_callback(lastCode); } else if (status == 4) //글섭에서 매칭 잡혔을 때 출력 { var roulette = data[20]; var code = BitConverter.ToUInt16(data, 22); Instance instance; if (!Settings.CheatRoulette && roulette != 0) { instance = new Instance { Name = Data.GetRoulette(roulette).Name }; } else { instance = Data.GetInstance(code); } state = State.MATCHED; mainForm.overlayForm.SetDutyAsMatched(instance); } } else if (opcode == 0x006F) { var status = data[0]; if (status == 0) { // 取消或确认超时 // 플레이어가 매칭 참가 확인 창에서 취소를 누르거나 참가 확인 제한 시간이 초과됨 // 매칭 중단을 알리기 위해 상단 2DB status 3 패킷이 연이어 옴 } if (status == 1) { // 确认 // 플레이어가 매칭 참가 확인 창에서 확인을 누름 // 다른 매칭 인원들도 전부 확인을 눌렀을 경우 입장을 위해 상단 2DB status 6 패킷이 옴 mainForm.overlayForm.StopBlink(); } } else if (opcode == 0x0121) //글로벌 서버 { var status = data[5]; if (status == 128) { // 매칭 참가 신청 확인 창에서 확인을 누름 mainForm.overlayForm.StopBlink(); } } else if (opcode == 0x0079) { var code = BitConverter.ToUInt16(data, 0); byte status = 0; byte tank = 0; byte dps = 0; byte healer = 0; byte order = 255; if (NetCompatibility) { order = data[4]; order--; status = data[8]; tank = data[9]; dps = data[10]; healer = data[11]; } else { order = data[5]; status = data[4]; tank = data[5]; dps = data[6]; healer = data[7]; } if (status == 0 && tank == 0 && healer == 0 && dps == 0) // 4.5版本兼容性 { Log.Debug("Debug:Patch V4.5 NetCompatibility Enabled"); NetCompatibility = true; order = 255; status = data[8]; tank = data[9]; dps = data[10]; healer = data[11]; } var instance = Data.GetInstance(code); if (status == 1) { // 인원 현황 패킷 var member = tank * 10000 + dps * 100 + healer; if (state == State.MATCHED && lastMember != member) { // 队友取消 // 매칭도중일 때 인원 현황 패킷이 오고 마지막 인원 정보와 다른 경우에 누군가에 의해 큐가 취소된 경우. state = State.QUEUED; mainForm.overlayForm.CancelDuty(); } else if (state == State.IDLE) { // 프로그램이 매칭 중간에 켜짐 state = State.QUEUED; mainForm.overlayForm.SetDutyCount(-1); // 알 수 없음으로 설정함 (TODO: 알아낼 방법 있으면 정확히 나오게 수정하기) if (NetCompatibility && rouletteCode > 0) { mainForm.overlayForm.SetDutyStatus(instance, order, dps, healer); } else { mainForm.overlayForm.SetDutyStatus(instance, tank, dps, healer); } } else if (state == State.QUEUED) { if (NetCompatibility && rouletteCode > 0) { mainForm.overlayForm.SetDutyStatus(instance, order, dps, healer); } else { mainForm.overlayForm.SetDutyStatus(instance, tank, dps, healer); } } lastMember = member; } else if (status == 2) { // 현재 매칭된 파티의 역할별 인원 수 정보 // 조율 해제 상태여도 역할별로 정확히 날아옴 mainForm.overlayForm.SetMemberCount(tank, dps, healer); return; } else if (status == 4) { // 매칭 뒤 참가자 확인 현황 패킷 mainForm.overlayForm.SetConfirmStatus(instance, tank, dps, healer); } lastCode = code; Log.I("l-queue-updated", instance.Name, status, tank, instance.Tank, healer, instance.Healer, dps, instance.DPS); } else if (opcode == 0x0080) { var roulette = data[2]; var code = BitConverter.ToUInt16(data, 4); Instance instance; if (!Settings.CheatRoulette && roulette != 0) { instance = new Instance { Name = Data.GetRoulette(roulette).Name }; } else { instance = Data.GetInstance(code); } state = State.MATCHED; mainForm.overlayForm.SetDutyAsMatched(instance); } } catch (Exception ex) { Log.Ex(ex, "l-analyze-error-general"); } }