// 실제 데이터 처리 하는 곳 private void PacketWorker_OnEventReceived(string pid, GameEvents gameevent, int[] args) { var text = pid + "|" + gameevent + "|"; var pos = 0; var isfate = false; switch (gameevent) { case GameEvents.InstanceEnter: // [0] = instance code case GameEvents.InstanceLeave: { if (args.Length > 0) { text += GameData.GetInstanceName(args[0]) + "|"; pos++; } _frmOverlay.EventNone(); } break; case GameEvents.FateOccur: // [0] = fate code { var fate = GameData.GetFate(args[0]); text += fate.Name + "|" + fate.Area.Name + "|"; pos++; // 모든 페이트를 골라도 목록에 있는것만 알려줌 if (Settings.SelectedFates.Contains(args[0].ToString())) { _frmOverlay.EventFate(fate); if (_use_notify) { NotifyFate(fate); } PlayEffectSound(); } isfate = true; } break; case GameEvents.MatchQueue: // [0] = MatchType, [1] = code, [...] = instances { var type = (MatchType)args[0]; text += type + "|"; pos++; switch (type) { case MatchType.Roulette: var roulette = GameData.GetRoulette(args[1]); text += roulette.Name + "|"; pos++; _frmOverlay.EventRoulette(roulette); break; case MatchType.Assignment: text += args[1] + "|"; pos++; var p = pos; for (var i = p; i < args.Length; i++) { text += GameData.GetInstanceName(args[i]) + "|"; pos++; } _frmOverlay.EventStatus(args[1]); break; } } break; case GameEvents.MatchDone: // [0] = roulette code, [1] = instance code { var roulette = GameData.GetRoulette(args[0]); var instance = GameData.GetInstance(args[1]); text += roulette.Name + "|"; pos++; text += instance.Name + "|"; pos++; _frmOverlay.EventMatch(instance); if (_use_notify) { NotifyDuty(instance); } PlayEffectSound(); } break; case GameEvents.MatchCancel: _frmOverlay.StopBlink(); break; } for (var i = pos; i < args.Length; i++) { text += args[i] + "|"; } if (isfate) { text += args[0] + "|"; } ActGlobals.oFormActMain.ParseRawLogLine(false, DateTime.Now, "00|" + DateTime.Now.ToString("O") + "|0048|F|" + text); }
// process packet private static void PacketFFXIV(string pid, byte[] message) { var opcode = BitConverter.ToUInt16(message, 18); #if !DEBUG if (opcode != Codes.Instance && opcode != Codes.FATE && opcode != Codes.Duty && opcode != Codes.Match) { return; } #endif var data = message.Skip(32).ToArray(); #if false if (opcode == Codes.Instance) // 인스턴스 { // [2020-01-13] 코드를 찾을 수 없다. var code = BitConverter.ToInt16(data, 4); var type = data[8]; if (type == 0x0B) { // 들어옴 MsgLog.Instance("l-instance-enter", GameData.GetInstanceName(code)); FireEvent(pid, GameEvents.InstanceEnter, new int[] { code }); } else if (type == 0x0C) { // 나감 MsgLog.Instance("l-instance-leave"); FireEvent(pid, GameEvents.InstanceLeave, new int[] { code }); } } // Codes.Instance else #endif if (opcode == Codes.FATE) // FATE 관련 { var type = data[0]; if (type == 0x74) // FATE 시작! 에이리어 이동해도 진행중인 것도 이걸로 처리됨 { var code = BitConverter.ToUInt16(data, 4); if (Settings.LoggingWholeFates || Settings.SelectedFates.Contains(code.ToString())) { MsgLog.Fate("l-fate-occured-info", GameData.GetFate(code).Name); FireEvent(pid, GameEvents.FateOccur, new int[] { code }); } } } // Codes.FATE else if (opcode == Codes.Duty) // 듀티 { var status = data[0]; var reason = data[4]; var roulette = data[Codes.RouletteCode]; if (roulette != 0 && (data[15] == 0 || data[15] == 64)) // 루렛, 한국/글로벌 { MsgLog.Duty("i-queue-roulette", GameData.GetRouletteName(roulette)); FireEvent(pid, GameEvents.MatchQueue, new[] { (int)MatchType.Roulette, roulette }); } else // 골라놓은 듀티 큐 (Dungeon/Trial/Raid) { var instances = new List <int>(); for (var i = 0; i < 5; i++) { var code = BitConverter.ToUInt16(data, 12 + (i * 4)); if (code == 0) { break; } } if (!instances.Any()) { return; } var args = new List <int> { (int)MatchType.Assignment, instances.Count }; foreach (var item in instances) { args.Add(item); } MsgLog.Duty("i-queue-instance", string.Join(", ", instances.Select(x => GameData.GetInstanceName(x)).ToArray())); FireEvent(pid, GameEvents.MatchQueue, args.ToArray()); } } // Codes.Duty else if (opcode == Codes.Match) // 매칭 { var roulette = BitConverter.ToUInt16(data, 2); var code = BitConverter.ToUInt16(data, 20); MsgLog.Duty("i-matched", GameData.GetInstanceName(code)); FireEvent(pid, GameEvents.MatchDone, new int[] { roulette, code }); } // Codes.Match }
private void PacketHandler(string pid, byte[] message) { var opcode = BitConverter.ToUInt16(message, 18); if (opcode != GamePacket.Current.OpFate && opcode != GamePacket.Current.OpDuty && opcode != GamePacket.Current.OpMatch && opcode != GamePacket.Current.OpInstance) { return; } var data = message.Skip(32).ToArray(); // FATE if (opcode == GamePacket.Current.OpFate) { if (data[0] == GamePacket.Current.FateIndex) { var fcode = BitConverter.ToUInt16(data, 4); bool isselected = Settings.SelectedFates.Contains(fcode.ToString()); if (Settings.LoggingWholeFates || isselected) { var fate = GameData.GetFate(fcode); Mesg.Fate("l-fate-occured-info", fate.Name); if (isselected) { PlayEffectSound(txtSoundFate.Text); _frmOverlay.EventFate(fate); if (_use_notify) { NotifyFate(fate); } } } } } // 듀티 else if (opcode == GamePacket.Current.OpDuty) { // 안쓴다 // var status = data[0]; // var reason = data[4]; var rcode = data[GamePacket.Current.DutyRoulette]; if (rcode != 0) { // 루렛 var roulette = GameData.GetRoulette(rcode); Mesg.Duty("i-queue-roulette", roulette.Name); _frmOverlay.EventQueue(roulette.Name); } else { // 직접 골라 큐 var insts = new List <int>(); for (var i = 0; i < 5; i++) { var icode = BitConverter.ToUInt16(data, GamePacket.Current.DutyInstance + (i * 4)); if (icode == 0) { break; } } if (insts.Any()) { Mesg.Duty("i-queue-instance", string.Join(", ", insts.Select(x => GameData.GetInstance(x).Name).ToArray())); _frmOverlay.EventStatus(insts.Count); } } } // 매치 else if (opcode == GamePacket.Current.OpMatch) { var rcode = BitConverter.ToUInt16(data, GamePacket.Current.MatchRoulette); var icode = BitConverter.ToUInt16(data, GamePacket.Current.MatchInstance); string name; if (icode == 0 && rcode != 0) { // 이것 루렛 매칭 var roulette = GameData.GetRoulette(rcode); Mesg.Duty("i-matched", roulette.Name); name = roulette.Name; } else if (icode != 0) { // 이건 골라 매칭 var instance = GameData.GetInstance(icode); Mesg.Duty("i-matched", instance.Name); name = instance.Name; } else { // 루렛도 인스도 아녀 name = Mesg.GetText("l-unknown-instance", icode); } PlayEffectSound(txtSoundFile.Text); _frmOverlay.EventMatch(name); if (_use_notify) { NotifyMatch(name); } } // 인스턴스 관련 else if (opcode == GamePacket.Current.OpInstance && GamePacket.Current.OpInstance != 0) { if (data[4] == 0) { // 0은 최초 입장때만 나오므로 이거 쓰자 var icode = BitConverter.ToUInt16(data, GamePacket.Current.InstanceInstance); var instance = GameData.GetInstance(icode); Mesg.Duty("l-instance-enter", instance.Name); _frmOverlay.EventMatch(Mesg.GetText("l-instance-enter", instance.Name)); if (_use_notify) { NotifyMatch(instance.Name); } } else { // 조용히 시키자 _frmOverlay.EventNone(); } } }
// 받은 메시지 처리기 private void PacketHandler(string pid, byte[] message) { var opcode = BitConverter.ToUInt16(message, 18); if (opcode != GamePacket.Current.OpFate && opcode != GamePacket.Current.OpDuty && opcode != GamePacket.Current.OpMatch && opcode != GamePacket.Current.OpInstance && opcode != GamePacket.Current.CriticalEngagement ) { return; } var data = message.Skip(32).ToArray(); // FATE if (opcode == GamePacket.Current.OpFate) { if (data[0] == GamePacket.Current.FateIndex) { var fcode = BitConverter.ToUInt16(data, 4); bool isselected = Settings.CurrentFate.Selected.Contains(fcode.ToString()); if (Settings.LoggingWholeFates || isselected) { var fate = GameData.GetFate(fcode); Mesg.Fate("l-fate-occured-info", fate.Name); if (isselected) { PlayEffectSound(txtSoundFate.Text); _frmOverlay.EventFate(fate); if (_use_notify) { NotifyFate(fate); } } } } } // 듀티 else if (opcode == GamePacket.Current.OpDuty) { // 안쓴다 // var status = data[0]; // var reason = data[4]; var rcode = data[GamePacket.Current.DutyRoulette]; if (rcode != 0) { // 루렛 var roulette = GameData.GetRoulette(rcode); Mesg.Duty("i-queue-roulette", roulette.Name); _frmOverlay.EventQueue(roulette.Name); } else { // 직접 골라 큐 var insts = new List <int>(); for (var i = 0; i < 5; i++) { var icode = BitConverter.ToUInt16(data, GamePacket.Current.DutyInstance + (i * 4)); if (icode == 0) { break; } } if (insts.Any()) { Mesg.Duty("i-queue-instance", string.Join(", ", insts.Select(x => GameData.GetInstance(x).Name).ToArray())); _frmOverlay.EventStatus(insts.Count); } } //임무에 들어가면 미션은 모두 해제 _missions.Clear(); } // 매치 else if (opcode == GamePacket.Current.OpMatch) { var rcode = BitConverter.ToUInt16(data, GamePacket.Current.MatchRoulette); var icode = BitConverter.ToUInt16(data, GamePacket.Current.MatchInstance); string name; if (icode == 0 && rcode != 0) { // 이것 루렛 매칭 var roulette = GameData.GetRoulette(rcode); Mesg.Duty("i-matched", roulette.Name); name = roulette.Name; } else if (icode != 0) { // 이건 골라 매칭 var instance = GameData.GetInstance(icode); Mesg.Duty("i-matched", instance.Name); name = instance.Name; } else { // 루렛도 인스도 아녀 name = Mesg.GetText("l-unknown-instance", icode); } PlayEffectSound(txtSoundFile.Text); _frmOverlay.EventMatch(name); if (_use_notify) { NotifyMatch(name); } } // 인스턴스 관련 else if (opcode == GamePacket.Current.OpInstance && GamePacket.Current.OpInstance != 0) { if (data[4] == 0) { // 0은 최초 입장때만 나오므로 이거 쓰자 var icode = BitConverter.ToUInt16(data, GamePacket.Current.InstanceInstance); var instance = GameData.GetInstance(icode); Mesg.Duty("l-instance-enter", instance.Name); _frmOverlay.EventMatch(Mesg.GetText("l-instance-enter", instance.Name)); if (_use_notify) { NotifyMatch(instance.Name); } // 미션...이 처리가되나 _missions.Clear(); } else { // 조용히 시키자 _frmOverlay.EventNone(); } } // 보즈야 서던 프론트 크리티컬 인게이지먼트 else if (opcode == GamePacket.Current.CriticalEngagement) { // 0[4] 타임스탬프 // 4[2] 남은시간 (mmss) // 6[2] ? // 8[1] 코드 // 9[1] ? // 10[1] 상태 0=끝, 1=알림/모집, 2=??, 3=진행 // 12[1] 진행 퍼센트 int ce = 30000 + data[8]; // 30000번대 페이트로 취급 if (data[10] == 0 /* || data[10] == 3 */) // 3은 진행했으면 알릴 의미가 없기 때문인데, 일단 패스 { // 제거 if (_missions.ContainsKey(ce)) { // 미션 목록에서 뺀다 _missions.TryRemove(ce, out _); } } else if (data[10] == 1) { // 알림 if (!_missions.ContainsKey(ce)) { // 미션 목록에 없으면 넣는다 _missions.TryAdd(ce, 0); // 일단 페이트 취급 bool isselected = Settings.CurrentFate.Selected.Contains(ce.ToString()); if (Settings.LoggingWholeFates || isselected) { var fate = GameData.GetFate(ce); Mesg.CriticalEngagement("l-fate-occured-info", fate.Name); if (isselected) { PlayEffectSound(txtSoundFile.Text); // 인스턴스 사운드 출력 _frmOverlay.EventFate(fate); if (_use_notify) { NotifyFate(fate); } } } } } #if true else if (Settings.LoggingWholeFates && (data[10] == 2 || data[10] == 3)) // 그냥 한번 로그에 남기자 { if (!_missions.ContainsKey(ce)) { // 미션 목록에 없으면 넣는다 _missions.TryAdd(ce, 0); if (Settings.CurrentFate.Selected.Contains(ce.ToString())) { var fate = GameData.GetFate(ce); Mesg.CriticalEngagement("l-fate-occured-info", fate.Name); } } } #endif } }
// 실제 데이터 처리 하는 곳 private void PacketFFXIV_OnEventReceived(int pid, GameEvents gameevent, int[] args) { var clienttype = _pronets[pid].ClientType; var text = pid + "|" + clienttype + "|" + gameevent + "|"; var pos = 0; var isFate = false; switch (gameevent) { case GameEvents.InstanceEnter: // [0] = instance code case GameEvents.InstanceLeave: { if (args.Length > 0) { text += GameData.GetInstanceName(args[0]) + "|"; pos++; } _frmOverlay.EventNone(); } break; case GameEvents.FateBegin: // [0] = fate code { var fate = GameData.GetFate(args[0]); isFate = true; text += fate.Name + "|" + fate.Area.Name + "|"; pos++; if (Settings.SelectedFates.Contains(args[0].ToString())) // 모든 페이트를 골라도 목록에 있는것만 알려줌 { _frmOverlay.EventFate(GameData.GetFate(args[0])); PlayEffectSound(); } } break; case GameEvents.FateProgress: // [0] = fate code, [1] = progress case GameEvents.FateEnd: // [0] = fate code, [1] = status(?) { var fate = GameData.GetFate(args[0]); isFate = true; text += fate.Name + "|" + fate.Area.Name + "|"; pos++; } break; case GameEvents.MatchBegin: // [0] = match type(0,1), [1] = roulette code or instance count, [...] = instance { text += (MatchType)args[0] + "|"; pos++; switch ((MatchType)args[0]) { case MatchType.Roulette: var roulette = GameData.GetRoulette(args[1]); text += roulette.Name + "|"; pos++; _frmOverlay.EventRoulette(roulette); break; case MatchType.Assignment: text += args[1] + "|"; pos++; var p = pos; for (var i = p; i < args.Length; i++) { text += GameData.GetInstanceName(args[i]) + "|"; pos++; } _frmOverlay.EventStatus(args[1]); break; } } break; case GameEvents.MatchEnd: // [0] = end reason <MatchEndType> { text += (MatchResult)args[0] + "|"; pos++; #if false if (Settings.UseOverlay) { var mres = (MatchResult)args[0]; if (mres == MatchResult.Enter) { _frmOverlay.Hide(); } else { _frmOverlay.Show(); } } #endif _frmOverlay.EventNone(); } break; case GameEvents.MatchOrder: // [0] = order _frmOverlay.EventStatus(args[0]); break; case GameEvents.MatchStatus: // [0] = match type(2,3), [1] = instance code, [2] = status // [3] = tank, [4] = healer, [5] = dps { if ((MatchType)args[0] == MatchType.ShortStatus) { // 5.1 이전 var instance = GameData.GetInstance(args[1]); text += instance.Name + "|"; pos++; _frmOverlay.EventStatus(instance, args[3], args[4], args[5]); } else { // 5.1 부터 [6] = maxtank, [7] = maxhealer, [8] = maxdps _frmOverlay.EventStatus(args[3], args[4], args[5], args[6], args[7], args[8]); } } break; case GameEvents.MatchDone: // [0] = roulette code, [1] = instance code { var roulette = GameData.GetRoulette(args[0]); var instance = GameData.GetInstance(args[1]); text += roulette.Name + "|"; pos++; text += instance.Name + "|"; pos++; _frmOverlay.EventMatch(instance); PlayEffectSound(); } break; case GameEvents.MatchCancel: _frmOverlay.StopBlink(); break; } for (var i = pos; i < args.Length; i++) { text += args[i] + "|"; } if (isFate) { text += args[0] + "|"; } ActGlobals.oFormActMain.ParseRawLogLine(false, DateTime.Now, "00|" + DateTime.Now.ToString("O") + "|0048|F|" + text); }
private static void HandleMessage(int pid, byte[] message, ref MatchStatus state) { try { if (message.Length < 32) { // type == 0x0000 이였던 메시지는 여기서 걸러짐 return; } var opcode = BitConverter.ToUInt16(message, 18); #if !DEBUG if ( opcode != 0x006F && opcode != 0x0078 && opcode != 0x0079 && opcode != 0x0080 && opcode != 0x0121 && opcode != 0x0143 && opcode != 0x022F && // 5.1 opcode != 0x008F && opcode != 0x00AE && opcode != 0x00B3 && opcode != 0x015E && opcode != 0x0304 && // 5.11 opcode != 0x0002 && opcode != 0x0164 && opcode != 0x0339 && opcode != 0x032D && opcode != 0x032F && opcode != 0x03CF) { return; } #endif var data = message.Skip(32).ToArray(); if (opcode == 0x022F) // 인스턴스 들어오고 나가기 { var code = BitConverter.ToInt16(data, 4); if (code == 0) { return; } var type = data[8]; if (type == 0x0B) { // 들어옴 MsgLog.Info("l-field-instance-entered", GameData.GetInstanceName(code)); FireEvent(pid, GameEvents.InstanceEnter, new int[] { code }); } else if (type == 0x0C) { // 나감 MsgLog.Info("l-field-instance-left"); FireEvent(pid, GameEvents.InstanceLeave, new int[] { code }); } } // 22F else if (opcode == 0x0143) // FATE 관련 { #if false // FATE 막음 2019-11-01 var type = data[0]; if (type == 0x9B) { var code = BitConverter.ToUInt16(data, 4); var progress = data[8]; FireEvent(pid, GameEvents.FateProgress, new int[] { code, progress }); } else if (type == 0x79) // FATE 끗 { var code = BitConverter.ToUInt16(data, 4); var status = BitConverter.ToUInt16(data, 28); FireEvent(pid, GameEvents.FateEnd, new int[] { code, status }); } else if (type == 0x74) // FATE 시작! 에이리어 이동해도 진행중인 것도 이걸로 처리됨 { var code = BitConverter.ToUInt16(data, 4); if (Settings.LoggingWholeFates || Settings.SelectedFates.Contains(code.ToString())) { MsgLog.Info("l-fate-occured-info", GameData.GetFate(code).Name); FireEvent(pid, GameEvents.FateBegin, new int[] { code }); } } #endif } // 143 else if (opcode == 0x0078) // 5.1이전 듀티 { var status = data[0]; var reason = data[4]; if (status == 0) // 듀티 큐 { state = MatchStatus.Queued; _rouletteCode = data[20]; if (_rouletteCode != 0 && (data[15] == 0 || data[15] == 64)) // 루렛, 한국/글로벌 { MsgLog.Info("l-queue-started-roulette", GameData.GetRouletteName(_rouletteCode)); FireEvent(pid, GameEvents.MatchBegin, new[] { (int)MatchType.Roulette, _rouletteCode }); } else // 듀티 지정 큐 (Dungeon/Trial/Raid) { _rouletteCode = 0; var instances = new List <int>(); for (var i = 0; i < 5; i++) { var code = BitConverter.ToUInt16(data, 22 + i * 2); if (code == 0) { break; } instances.Add(code); } if (!instances.Any()) { return; } var args = new List <int> { (int)MatchType.Assignment, instances.Count }; foreach (var item in instances) { args.Add(item); } MsgLog.Info("l-queue-started-general", string.Join(", ", instances.Select(x => GameData.GetInstanceName(x)).ToArray())); FireEvent(pid, GameEvents.MatchBegin, args.ToArray()); } } else if (status == 3) // 취소 { state = reason == 8 ? MatchStatus.Queued : MatchStatus.Idle; MsgLog.Info("l-queue-stopped"); FireEvent(pid, GameEvents.MatchEnd, new[] { (int)MatchResult.Cancel }); } else if (status == 6) // 들어가기 { state = MatchStatus.Idle; MsgLog.Info("l-queue-entered"); FireEvent(pid, GameEvents.MatchEnd, new[] { (int)MatchResult.Enter }); } else if (status == 4) // 매치 { var roulette = data[20]; var code = BitConverter.ToUInt16(data, 22); state = MatchStatus.Matched; MsgLog.Info("l-queue-matched", GameData.GetInstanceName(code)); FireEvent(pid, GameEvents.MatchDone, new int[] { roulette, code }); } } // 78 else if (opcode == 0x006F) { var status = data[0]; if (status == 0) { // 플레이어가 매칭 참가 확인 창에서 취소를 누르거나 참가 확인 제한 시간이 초과됨 // 매칭 중단을 알리기 위해 상단 2DB status 3 패킷이 연이어 옴 } if (status == 1) { // 플레이어가 매칭 참가 확인 창에서 확인을 누름 // 다른 매칭 인원들도 전부 확인을 눌렀을 경우 입장을 위해 상단 2DB status 6 패킷이 옴 FireEvent(pid, GameEvents.MatchCancel, new int[] { -1 }); } } // 6F else if (opcode == 0x0121) // 글로벌 깜빡 { var status = data[5]; if (status == 128) { // 매칭 참가 신청 확인 창에서 확인을 누름, 그러니깐 표시 안하도됨 FireEvent(pid, GameEvents.MatchCancel, new int[] { -1 }); } } // 121 else if (opcode == 0x0079) // 매치 상태 { var code = BitConverter.ToUInt16(data, 0); var order = data[4]; var status = data[8]; var tank = data[9]; var dps = data[10]; var healer = data[11]; var member = tank * 10000 + healer * 100 + dps; var instance = GameData.GetInstance(code); if (status == 1) { if (state == MatchStatus.Matched && _lastMember != member) { // 마지막 정보와 다름, 다른 사람에 의한 취소... 인데 이거 되나??!!! state = MatchStatus.Queued; FireEvent(pid, GameEvents.MatchCancel, new int[] { -1 }); } else if (state == MatchStatus.Idle || state == MatchStatus.Queued) { if (state == MatchStatus.Idle) { // 매칭 중간에 플러그인이 시작됨 state = MatchStatus.Queued; } if (_rouletteCode > 0 || (tank == 0 && healer == 0 && dps == 0)) { FireEvent(pid, GameEvents.MatchOrder, new int[] { order }); } else { FireEvent(pid, GameEvents.MatchStatus, new int[] { (int)MatchType.ShortStatus, code, status, tank, healer, dps }); } } _lastMember = member; _lastOrder = order; } else if (status == 2) { // 매칭 파티의 인원 정보 FireEvent(pid, GameEvents.MatchStatus, new int[] { (int)MatchType.ShortStatus, code, status, tank, healer, dps }); return; // 이건 로그를 안뿌린다 } else //if (status == 4) { // 매칭하고 파티 인원 상태 // 추가로: 다른거땜에 오버레이가 지워질 수도 있음... ㅠㅠ FireEvent(pid, GameEvents.MatchStatus, new int[] { (int)MatchType.ShortStatus, code, status, tank, healer, dps }); if (status != 4) { // 기타면 로그 출력X // ....그러고보니 status == 2일때도 여기서 처리해도 되는데 지면이 부족하여 패-_-스 return; } } var memberinfo = $"{order} | {tank}/{instance.Tank}, {healer}/{instance.Healer}, {dps}/{instance.Dps} | {member}"; MsgLog.Info("l-queue-updated", instance.Name, status, memberinfo); } // 79 else if (opcode == 0x0080) { var roulette = data[2]; var code = BitConverter.ToUInt16(data, 4); state = MatchStatus.Matched; FireEvent(pid, GameEvents.MatchDone, new int[] { roulette, code }); MsgLog.Success("l-queue-matched ", code); } // 80 #region 5.1 추가 else if (opcode == 0x008F) // 5.1 큐 (opcode = 0x0078, status = 0) { var status = data[0]; var reason = data[4]; state = MatchStatus.Queued; _rouletteCode = data[8]; if (_rouletteCode != 0 && (data[15] == 0 || data[15] == 64)) // 루렛, 한국/글로벌 { MsgLog.Info("l-queue-started-roulette", GameData.GetRouletteName(_rouletteCode)); FireEvent(pid, GameEvents.MatchBegin, new[] { (int)MatchType.Roulette, _rouletteCode }); } else // 듀티 지정 큐 (Dungeon/Trial/Raid) { _rouletteCode = 0; var instances = new List <int>(); for (var i = 0; i < 5; i++) { var code = BitConverter.ToUInt16(data, 12 + (i * 4)); if (code == 0) { break; } } if (!instances.Any()) { return; } var args = new List <int> { (int)MatchType.Assignment, instances.Count }; foreach (var item in instances) { args.Add(item); } MsgLog.Info("l-queue-started-general", string.Join(", ", instances.Select(x => GameData.GetInstanceName(x)).ToArray())); FireEvent(pid, GameEvents.MatchBegin, args.ToArray()); } } // 8F else if (opcode == 0x00B3) // 5.1 매칭 (opcode = 0x0078, status = 4) { var code = BitConverter.ToUInt16(data, 20); state = MatchStatus.Matched; MsgLog.Info("l-queue-matched", GameData.GetInstanceName(code)); FireEvent(pid, GameEvents.MatchDone, new int[] { _rouletteCode, code }); } // B3 else if (opcode == 0x015E) // 5.1 캔슬 (opcode = 0x0078, status = 3) { var status = data[3]; if (status == 8) // 0이아님 { state = MatchStatus.Idle; MsgLog.Info("l-queue-stopped"); FireEvent(pid, GameEvents.MatchEnd, new[] { (int)MatchResult.Cancel }); } } // 15E else if (opcode == 0x0304) // 5.1, 5.11 상태 (opcode = 0x0078, status = 1) { var order = data[6]; var wait = data[7]; var tank = data[8]; var maxtank = data[9]; var healer = data[10]; var maxhealer = data[11]; var dps = data[12]; var maxdps = data[13]; var member = tank * 10000 + healer * 100 + dps; if (state == MatchStatus.Matched && _lastMember != member) { // 마지막 정보와 다름, 다른 사람에 의한 취소... 인데 이거 되나??!!! state = MatchStatus.Queued; FireEvent(pid, GameEvents.MatchCancel, new int[] { -1 }); } else if (state == MatchStatus.Idle || state == MatchStatus.Queued) { if (state == MatchStatus.Idle) { // 매칭 중간에 플러그인 시작 state = MatchStatus.Queued; } if (_rouletteCode > 0 || (tank == 0 && healer == 0 && dps == 0) || (tank > maxtank || healer > maxhealer || dps > maxdps)) { FireEvent(pid, GameEvents.MatchOrder, new int[] { order }); } else { FireEvent(pid, GameEvents.MatchStatus, new int[] { (int)MatchType.LongStatus, 0, order, tank, healer, dps, maxtank, maxhealer, maxdps }); } } _lastMember = member; _lastOrder = order; var memberinfo = $"{tank}/{maxtank}, {healer}/{maxhealer}, {dps}/{maxdps}"; MsgLog.Info("l-queue-updated", $"#{order}", wait, memberinfo); } // 304 else if (opcode == 0x00AE) // 5.1 매칭하고 파티 인원 (opcode = 0x0078, status = 4) { var code = BitConverter.ToUInt16(data, 8); var tank = data[12]; var healer = data[14]; var dps = data[16]; FireEvent(pid, GameEvents.MatchStatus, new int[] { (int)MatchType.ShortStatus, code, 0, tank, healer, dps }); } // AE #endregion // 5.1 추가 #region 5.11 추가 else if (opcode == 0x0339) // 5.11 인스턴스 들어오고 나가기 { var code = BitConverter.ToInt16(data, 4); if (code == 0) { return; } var type = data[8]; if (type == 0x0B) { // 들어옴 MsgLog.Info("l-field-instance-entered", GameData.GetInstanceName(code)); FireEvent(pid, GameEvents.InstanceEnter, new int[] { code }); } else if (type == 0x0C) { // 나감 MsgLog.Info("l-field-instance-left"); FireEvent(pid, GameEvents.InstanceLeave, new int[] { code }); } } // 339 else if (opcode == 0x0164) // 5.11 큐 { var status = data[0]; var reason = data[4]; state = MatchStatus.Queued; _rouletteCode = data[8]; if (_rouletteCode != 0 && (data[15] == 0 || data[15] == 64)) { MsgLog.Info("l-queue-started-roulette", GameData.GetRouletteName(_rouletteCode)); FireEvent(pid, GameEvents.MatchBegin, new[] { (int)MatchType.Roulette, _rouletteCode }); } else // 듀티 지정 큐 (Dungeon/Trial/Raid) { _rouletteCode = 0; var instances = new List <int>(); for (var i = 0; i < 5; i++) { var code = BitConverter.ToUInt16(data, 12 + (i * 4)); if (code == 0) { break; } } if (!instances.Any()) { return; } var args = new List <int> { (int)MatchType.Assignment, instances.Count }; foreach (var item in instances) { args.Add(item); } MsgLog.Info("l-queue-started-general", string.Join(", ", instances.Select(x => GameData.GetInstanceName(x)).ToArray())); FireEvent(pid, GameEvents.MatchBegin, args.ToArray()); } } // 164 else if (opcode == 0x032D) // 5.11 매칭 { //var roulette = BitConverter.ToUInt16(data, 2); var code = BitConverter.ToUInt16(data, 20); state = MatchStatus.Matched; MsgLog.Info("l-queue-matched", GameData.GetInstanceName(code)); FireEvent(pid, GameEvents.MatchDone, new int[] { _rouletteCode, code }); } // 32D else if (opcode == 0x03CF) // 5.11 듀티 상태 { var status = data[0]; if (status == 0x73) // 매칭 취소 { state = MatchStatus.Idle; MsgLog.Info("l-queue-stopped"); FireEvent(pid, GameEvents.MatchEnd, new[] { (int)MatchResult.Cancel }); } else if (status == 0x81) // 매칭 넣었음 { //state = MatchStatus.Idle; //FireEvent(pid, GameEvents.MatchEnd, new[] { (int)MatchResult.Enter }); } } // 3CF else if (opcode == 0x032F) // 5.11 매칭하고 파티 인원 { var code = BitConverter.ToUInt16(data, 8); var tank = data[12]; var maxtank = data[13]; var healer = data[14]; var maxhealer = data[15]; var dps = data[16]; var maxdps = data[17]; if (tank > maxtank || healer > maxhealer || dps > maxdps) { FireEvent(pid, GameEvents.MatchDone, new int[] { _rouletteCode, code }); } else { //FireEvent(pid, GameEvents.MatchStatus, new int[] { (int)MatchType.ShortStatus, code, 0, tank, healer, dps }); FireEvent(pid, GameEvents.MatchStatus, new int[] { (int)MatchType.LongStatus, code, 0, tank, healer, dps, maxtank, maxhealer, maxdps }); } } // 32F else if (opcode == 0x0002) // 5.11 매칭 완료 { // 딱히 할건없음 //FireEvent(pid, GameEvents.MatchCancel, new int[] { -1 }); } // 2 #endregion // 5.11 추가 } catch (Exception ex) { #if false MsgLog.Exception(ex, "l-analyze-error-handle"); #else // 에이리어 이동할때만 나타난다. 메시지를 출력하지 말자 // DFASSIST에서는 안나는데 플러그인은 이동만 하면 난다 var fmt = Localization.GetText("l-analyze-error-handle"); var msg = MsgLog.Escape(ex.Message); System.Diagnostics.Debug.WriteLine($"{fmt}: {msg}"); // 오버레이는 지워버리는게 좋겠다 //FireEvent(pid, GameEvents.MatchEnd, new[] { (int)MatchResult.Cancel }); #endif } }