public PacketDocumentViewModel(PacketDocumentSolutionItem solutionItem, IMainThread mainThread, MostRecentlySearchedService mostRecentlySearchedService, IDatabaseProvider databaseProvider, Func <INativeTextDocument> nativeTextDocumentCreator, IMessageBoxService messageBoxService, IPacketFilteringService filteringService, IDocumentManager documentManager, ITextDocumentService textDocumentService, IWindowManager windowManager, IPacketViewerSettings packetSettings, IEnumerable <IPacketDumperProvider> dumperProviders, IInputBoxService inputBoxService, IHistoryManager history, IPacketFilterDialogService filterDialogService, IActionReactionProcessorCreator actionReactionProcessorCreator, IRelatedPacketsFinder relatedPacketsFinder, ITeachingTipService teachingTipService, PacketDocumentSolutionNameProvider solutionNameProvider, ISniffLoader sniffLoader, ISpellStore spellStore, PrettyFlagParameter prettyFlagParameter, Func <IUpdateFieldsHistory> historyCreator) { this.solutionItem = solutionItem; this.mainThread = mainThread; this.messageBoxService = messageBoxService; this.filteringService = filteringService; this.actionReactionProcessorCreator = actionReactionProcessorCreator; this.relatedPacketsFinder = relatedPacketsFinder; this.sniffLoader = sniffLoader; History = history; history.LimitStack(20); packetViewModelCreator = new PacketViewModelFactory(databaseProvider, spellStore); MostRecentlySearched = mostRecentlySearchedService.MostRecentlySearched; Title = solutionNameProvider.GetName(this.solutionItem); SolutionItem = solutionItem; FilterText = nativeTextDocumentCreator(); SelectedPacketPreview = nativeTextDocumentCreator(); SelectedPacketPreview.DisableUndo(); Watch(this, t => t.FilteringProgress, nameof(ProgressUnknown)); AutoDispose(history.AddHandler(new SelectedPacketHistory(this))); filteredPackets = visiblePackets = AllPackets; ApplyFilterCommand = new AsyncCommand(async() => { if (inApplyFilterCommand) { return; } inApplyFilterCommand = true; MostRecentlySearchedItem = FilterText.ToString(); inApplyFilterCommand = false; if (!string.IsNullOrEmpty(FilterText.ToString())) { mostRecentlySearchedService.Add(FilterText.ToString()); } if (currentActionToken != filteringToken) { throw new Exception("Invalid concurrent access!"); } filteringToken?.Cancel(); filteringToken = null; currentActionToken = null; await FilterPackets(FilterText.ToString()); }); SaveToFileCommand = new AsyncCommand(async() => { var path = await windowManager.ShowSaveFileDialog("Text file|txt"); if (path == null) { return; } LoadingInProgress = true; FilteringInProgress = true; try { await using StreamWriter writer = File.CreateText(path); int i = 0; int count = FilteredPackets.Count; foreach (var packet in FilteredPackets) { await writer.WriteLineAsync(packet.Text); if ((i % 100) == 0) { Report(i * 1.0f / count); } } } catch (Exception e) { Console.WriteLine(e); await this.messageBoxService.ShowDialog(new MessageBoxFactory <bool>() .SetTitle("Fatal error") .SetMainInstruction("Fatal error during saving file") .SetContent(e.Message) .WithOkButton(false) .Build()); } LoadingInProgress = false; FilteringInProgress = false; }); OpenHelpCommand = new DelegateCommand(() => windowManager.OpenUrl("https://github.com/BAndysc/WoWDatabaseEditor/wiki/Packet-Viewer")); On(() => SelectedPacket, doc => { if (doc != null) { SelectedPacketPreview.FromString(doc.Text); } }); On(() => SplitUpdate, _ => { SplitPacketsIfNeededAsync().Wait(); ((ICommand)ApplyFilterCommand).Execute(null); }); On(() => HidePlayerMove, _ => { ((ICommand)ApplyFilterCommand).Execute(null); }); On(() => DisableFilters, _ => { ((ICommand)ApplyFilterCommand).Execute(null); }); foreach (var dumper in dumperProviders) { Processors.Add(new(dumper)); } QuickRunProcessor = new AsyncAutoCommand <ProcessorViewModel>(async(processor) => { LoadingInProgress = true; FilteringInProgress = true; try { var tokenSource = new CancellationTokenSource(); AssertNoOnGoingTask(); currentActionToken = tokenSource; var output = await RunProcessorsThreaded(new List <ProcessorViewModel>() { processor }, tokenSource.Token).ConfigureAwait(true); if (output != null && !tokenSource.IsCancellationRequested) { documentManager.OpenDocument(textDocumentService.CreateDocument($"{processor.Name} ({Title})", output, processor.Extension, true)); } } catch (Exception e) { Console.WriteLine(e); await messageBoxService.ShowDialog(new MessageBoxFactory <bool>() .SetIcon(MessageBoxIcon.Error) .SetTitle("Fatal error") .SetMainInstruction("Fatal error during processing") .SetContent( "Sorry, fatal error occured, this is probably a bug in processors, please report it in github\n\n" + e) .WithOkButton(true) .Build()); } LoadingInProgress = false; FilteringInProgress = false; currentActionToken = null; }); RunProcessors = new AsyncAutoCommand(async() => { var processors = Processors.Where(s => s.IsChecked).ToList(); if (processors.Count == 0) { return; } LoadingInProgress = true; FilteringInProgress = true; try { var tokenSource = new CancellationTokenSource(); AssertNoOnGoingTask(); currentActionToken = tokenSource; var output = await RunProcessorsThreaded(processors, tokenSource.Token).ConfigureAwait(true); if (output != null && !tokenSource.IsCancellationRequested) { var extension = processors.Select(p => p.Extension).Distinct().Count() == 1 ? processors[0].Extension : "txt"; documentManager.OpenDocument(textDocumentService.CreateDocument($"{processors[0].Name} ({Title})", output, extension, true)); } } catch (Exception e) { Console.WriteLine(e); await messageBoxService.ShowDialog(new MessageBoxFactory <bool>() .SetIcon(MessageBoxIcon.Error) .SetTitle("Fatal error") .SetMainInstruction("Fatal error during processing") .SetContent( "Sorry, fatal error occured, this is probably a bug in processors, please report it in github\n\n" + e) .WithOkButton(true) .Build()); } LoadingInProgress = false; FilteringInProgress = false; currentActionToken = null; }, () => Processors.Any(c => c.IsChecked)); foreach (var proc in Processors) { AutoDispose(proc.ToObservable(p => p.IsChecked) .SubscribeAction(_ => RunProcessors.RaiseCanExecuteChanged())); } UndoCommand = new DelegateCommand(history.Undo, () => history.CanUndo); RedoCommand = new DelegateCommand(history.Redo, () => history.CanRedo); History.PropertyChanged += (_, _) => { UndoCommand.RaiseCanExecuteChanged(); RedoCommand.RaiseCanExecuteChanged(); }; IEnumerable <int> GetFindEnumerator(int start, int count, int direction, bool wrap) { for (int i = start + direction; i >= 0 && i < count; i += direction) { yield return(i); } if (wrap) { if (direction > 0) { for (int i = 0; i < start; ++i) { yield return(i); } } else { for (int i = count - 1; i > start; --i) { yield return(i); } } } } async Task Find(string searchText, int start, int direction) { var count = VisiblePackets.Count; var searchToLower = searchText.ToLower(); foreach (var i in GetFindEnumerator(start, count, direction, true)) { if (VisiblePackets[i].Text.Contains(searchToLower, StringComparison.InvariantCultureIgnoreCase)) { SelectedPacket = VisiblePackets[i]; return; } } await messageBoxService.ShowDialog(new MessageBoxFactory <bool>() .SetTitle("Find") .SetMainInstruction("Not found") .SetContent("Cannot find text: " + searchText) .WithOkButton(true) .Build()); } ToggleFindCommand = new DelegateCommand(() => FindPanelEnabled = !FindPanelEnabled); CloseFindCommand = new DelegateCommand(() => FindPanelEnabled = false); FindPreviousCommand = new AsyncAutoCommand <string>(async searchText => { var start = SelectedPacket != null ? VisiblePackets.IndexOf(SelectedPacket) : 0; await Find(searchText, start, -1); }, str => str is string s && !string.IsNullOrEmpty(s)); FindNextCommand = new AsyncAutoCommand <string>(async searchText => { var start = SelectedPacket != null ? VisiblePackets.IndexOf(SelectedPacket) : 0; await Find(searchText, start, 1); }, str => str is string s && !string.IsNullOrEmpty(s)); FindRelatedPacketsCommands = new AsyncAutoCommand(async() => { if (selectedPacket == null) { return; } if (!await EnsureSplitOrDismiss()) { return; } FilteringInProgress = true; FilteringProgress = -1; var tokenSource = new CancellationTokenSource(); AssertNoOnGoingTask(); currentActionToken = tokenSource; IFilterData?newFilterData = await GetRelatedFilters(selectedPacket.Packet.BaseData.Number, tokenSource.Token); FilteringInProgress = false; currentActionToken = null; if (newFilterData == null) { return; } if (!FilterData.IsEmpty) { if (await messageBoxService.ShowDialog(new MessageBoxFactory <bool>() .SetTitle("Find related packets") .SetMainInstruction("Your current filter data is not empty") .SetContent("Do you want to override current filter data?") .WithButton("Override", true) .WithButton("Merge", false) .Build())) { FilterData = newFilterData; } else { FilterData.SetMinMax(Min(FilterData.MinPacketNumber, newFilterData.MinPacketNumber), Max(FilterData.MaxPacketNumber, newFilterData.MaxPacketNumber)); if (newFilterData.IncludedGuids != null) { foreach (var guid in newFilterData.IncludedGuids) { FilterData.IncludeGuid(guid); } } if (newFilterData.ForceIncludePacketNumbers != null) { foreach (var packet in newFilterData.ForceIncludePacketNumbers) { FilterData.IncludePacketNumber(packet); } } } } else { FilterData = newFilterData; } await ApplyFilterCommand.ExecuteAsync(); }); ExcludeEntryCommand = new AsyncAutoCommand <PacketViewModel?>(async vm => { if (vm == null || vm.Entry == 0) { return; } FilterData.ExcludeEntry(vm.Entry); await ApplyFilterCommand.ExecuteAsync(); }, vm => vm is PacketViewModel pvm && pvm.Entry != 0); IncludeEntryCommand = new AsyncAutoCommand <PacketViewModel?>(async vm => { if (vm == null || vm.Entry == 0) { return; } FilterData.IncludeEntry(vm.Entry); await ApplyFilterCommand.ExecuteAsync(); }, vm => vm is PacketViewModel pvm && pvm.Entry != 0); ExcludeGuidCommand = new AsyncAutoCommand <PacketViewModel?>(async vm => { if (vm == null || vm.MainActor == null) { return; } FilterData.ExcludeGuid(vm.MainActor); await ApplyFilterCommand.ExecuteAsync(); }, vm => vm is PacketViewModel pvm && pvm.MainActor != null); IncludeGuidCommand = new AsyncAutoCommand <PacketViewModel?>(async vm => { if (vm == null || vm.MainActor == null) { return; } FilterData.IncludeGuid(vm.MainActor); await ApplyFilterCommand.ExecuteAsync(); }, vm => vm is PacketViewModel pvm && pvm.MainActor != null); ExcludeOpcodeCommand = new AsyncAutoCommand <PacketViewModel?>(async vm => { if (vm == null) { return; } FilterData.ExcludeOpcode(vm.Opcode); await ApplyFilterCommand.ExecuteAsync(); }, vm => vm != null); IncludeOpcodeCommand = new AsyncAutoCommand <PacketViewModel?>(async vm => { if (vm == null) { return; } FilterData.IncludeOpcode(vm.Opcode); await ApplyFilterCommand.ExecuteAsync(); }, vm => vm != null); AutoDispose(this.ToObservable(o => o.SelectedPacket).SubscribeAction(_ => { ExcludeEntryCommand.RaiseCanExecuteChanged(); IncludeEntryCommand.RaiseCanExecuteChanged(); ExcludeGuidCommand.RaiseCanExecuteChanged(); IncludeGuidCommand.RaiseCanExecuteChanged(); ExcludeOpcodeCommand.RaiseCanExecuteChanged(); IncludeOpcodeCommand.RaiseCanExecuteChanged(); })); ExplainSelectedPacketCommand = new AsyncAutoCommand(async() => { if (!reasonPanelVisibility || actionReactionProcessor == null) { return; } if (selectedPacket == null) { return; } DetectedActions.Clear(); var detected = actionReactionProcessor.GetAllActions(selectedPacket.Id); if (detected != null) { DetectedActions.AddRange(detected.Select(s => new DetectedActionViewModel(s))); } DetectedEvents.Clear(); var events = actionReactionProcessor.GetAllEvents(selectedPacket.Id); if (events != null) { DetectedEvents.AddRange(events.Select(s => new DetectedEventViewModel(s))); } var reasons = actionReactionProcessor.GetPossibleEventsForAction(selectedPacket.Packet.BaseData); Predictions.Clear(); foreach (var reason in reasons) { Predictions.Add(new ActionReasonPredictionViewModel(selectedPacket.Packet.BaseData, reason.rate.Value, reason.rate.Explain, reason.@event)); } PossibleActions.Clear(); var actions = actionReactionProcessor.GetPossibleActionsForEvent(selectedPacket.Packet.BaseData.Number); foreach (var action in actions) { var actionHappened = actionReactionProcessor.GetAction(action.packetId); if (actionHappened == null) { continue; } PossibleActions.Add(new PossibleActionViewModel(selectedPacket.Packet.BaseData, action.chance, "", actionHappened.Value)); } }, () => ReasonPanelVisibility); On(() => ReasonPanelVisibility, isVisible => { if (!isVisible) { return; } ApplyFilterCommand.ExecuteAsync().ContinueWith(_ => { ExplainSelectedPacketCommand.Execute(null); }, TaskContinuationOptions.NotOnCanceled | TaskContinuationOptions.NotOnFaulted).ListenErrors(); }); AutoDispose(this.ToObservable(o => o.SelectedPacket) .CombineLatest(this.ToObservable(o => o.ReasonPanelVisibility)) .SubscribeAction( _ => { ExplainSelectedPacketCommand.Execute(null); })); OpenFilterDialogCommand = new AsyncAutoCommand(async() => { var newData = await filterDialogService.OpenFilterDialog(FilterData); if (newData != null) { FilterData = newData; await ApplyFilterCommand.ExecuteAsync(); } }); JumpToPacketCommand = new DelegateCommand <int?>(packetId => { if (!packetId.HasValue) { return; } var packet = filteredPackets.FirstOrDefault(p => p.Id == packetId.Value); if (packet != null) { SelectedPacket = packet; } }); GoToPacketCommand = new AsyncAutoCommand(async() => { var min = filteredPackets[0].Id; var max = filteredPackets[^ 1].Id;
public PacketDocumentViewModel(PacketDocumentSolutionItem solutionItem, IMainThread mainThread, MostRecentlySearchedService mostRecentlySearchedService, IDatabaseProvider databaseProvider, Func <INativeTextDocument> nativeTextDocumentCreator, IMessageBoxService messageBoxService, IPacketFilteringService filteringService, IDocumentManager documentManager, ITextDocumentService textDocumentService, IWindowManager windowManager, IPacketViewerSettings packetSettings, IEnumerable <IPacketDumperProvider> dumperProviders, ISniffLoader sniffLoader) { this.solutionItem = solutionItem; this.mainThread = mainThread; this.messageBoxService = messageBoxService; this.filteringService = filteringService; this.sniffLoader = sniffLoader; packetViewModelCreator = new PacketViewModelFactory(databaseProvider); MostRecentlySearched = mostRecentlySearchedService.MostRecentlySearched; Title = "Sniff " + Path.GetFileNameWithoutExtension(solutionItem.File); SolutionItem = solutionItem; FilterText = nativeTextDocumentCreator(); SelectedPacketPreview = nativeTextDocumentCreator(); Watch(this, t => t.FilteringProgress, nameof(ProgressUnknown)); filteredPackets = AllPackets; ApplyFilterCommand = new AsyncCommand(async() => { if (inApplyFilterCommand) { return; } inApplyFilterCommand = true; MostRecentlySearchedItem = FilterText.ToString(); inApplyFilterCommand = false; if (!string.IsNullOrEmpty(FilterText.ToString())) { mostRecentlySearchedService.Add(FilterText.ToString()); } if (currentActionToken != filteringToken) { throw new Exception("Invalid concurrent access!"); } filteringToken?.Cancel(); filteringToken = null; currentActionToken = null; await FilterPackets(FilterText.ToString()); }); SaveToFileCommand = new AsyncCommand(async() => { var path = await windowManager.ShowSaveFileDialog("Text file|txt"); if (path == null) { return; } LoadingInProgress = true; FilteringInProgress = true; try { await using StreamWriter writer = File.CreateText(path); int i = 0; int count = FilteredPackets.Count; foreach (var packet in FilteredPackets) { await writer.WriteLineAsync(packet.Text); if ((i % 100) == 0) { Report(i * 1.0f / count); } } } catch (Exception e) { Console.WriteLine(e); await this.messageBoxService.ShowDialog(new MessageBoxFactory <bool>() .SetTitle("Fatal error") .SetMainInstruction("Fatal error during saving file") .SetContent(e.Message) .WithOkButton(false) .Build()); } LoadingInProgress = false; FilteringInProgress = false; }); OpenHelpCommand = new DelegateCommand(() => windowManager.OpenUrl("https://github.com/BAndysc/WoWDatabaseEditor/wiki/Packet-Viewer")); On(() => SelectedPacket, doc => { if (doc != null) { SelectedPacketPreview.FromString(doc.Text); } }); On(() => SplitUpdate, _ => { SplitPacketsIfNeededAsync().Wait(); ((ICommand)ApplyFilterCommand).Execute(null); }); foreach (var dumper in dumperProviders) { Processors.Add(new(dumper)); } RunProcessors = new AsyncAutoCommand(async() => { var processors = Processors.Where(s => s.IsChecked).ToList(); if (processors.Count == 0) { return; } LoadingInProgress = true; FilteringInProgress = true; try { var tokenSource = new CancellationTokenSource(); AssertNoOnGoingTask(); currentActionToken = tokenSource; var output = await RunProcessorsThreaded(processors, tokenSource.Token).ConfigureAwait(true); if (!tokenSource.IsCancellationRequested) { var extension = processors.Select(p => p.Extension).Distinct().Count() == 1 ? processors[0].Extension : "txt"; documentManager.OpenDocument(textDocumentService.CreateDocument($"{processors[0].Name} ({Title})", output, extension)); } } catch (Exception e) { Console.WriteLine(e); await messageBoxService.ShowDialog(new MessageBoxFactory <bool>() .SetIcon(MessageBoxIcon.Error) .SetTitle("Fatal error") .SetMainInstruction("Fatal error during processing") .SetContent( "Sorry, fatal error occured, this is probably a bug in processors, please report it in github\n\n" + e) .WithOkButton(true) .Build()); } LoadingInProgress = false; FilteringInProgress = false; currentActionToken = null; }, _ => Processors.Any(c => c.IsChecked)); foreach (var proc in Processors) { AutoDispose(proc.ToObservable(p => p.IsChecked) .SubscribeAction(_ => RunProcessors.RaiseCanExecuteChanged())); } wrapLines = packetSettings.Settings.WrapLines; splitUpdate = packetSettings.Settings.AlwaysSplitUpdates; if (!string.IsNullOrEmpty(packetSettings.Settings.DefaultFilter)) { FilterText.FromString(packetSettings.Settings.DefaultFilter); } LoadSniff().ListenErrors(); AutoDispose(new ActionDisposable(() => currentActionToken?.Cancel())); }