public static void MineStatusLogs() { var filename = "./Log_2021-06-07 18-50-31.csv"; var logLines = LoadLog(filename); var maybeStatusLines = logLines .Where(line => line.MessageType == CsvLogFileWriter.LOG_TYPE_MUD_OUTPUT && line.Text.Contains(" >")) .Select(line => ControlCharacterEncoder.Encode(line.Text, forCsv: true)) .ToList(); var grouped = maybeStatusLines .GroupBy(sl => sl) .OrderByDescending(group => group.Count()) .Select((group) => $"{group.Count()}, {group.Key}"); // File.WriteAllLines("./minedStatusLines.txt", grouped); var splitLinedStatusLines = maybeStatusLines.Select(msl => msl.Replace("\\r\\n", "\n").Split('\n')).SelectMany(m => m).ToList(); var x = splitLinedStatusLines.Where(line => line.Contains(" >")).ToList(); var y = x.Where(s => s.StartsWith("o") || s.StartsWith("*")).Distinct().ToList(); var z = x.Where(s => !s.StartsWith("o") && !s.StartsWith("*")).ToList(); File.WriteAllLines("./splitMinedStatusLines.txt", y); var i = 0; }
private LogLine ToLogLine(string output, string messageType) { return(new LogLine { Time = DateTime.Now, MsSinceStart = _sw.Elapsed.TotalMilliseconds, MessageType = messageType, EncodedText = ControlCharacterEncoder.Encode(output, forCsv: true), }); }
private async Task ProcessLogLoop(string filename, TimeSpan timeBetweenMessages, Action onLogParsed, CancellationToken cancellationToken) { var lines = File.ReadAllLines(filename); await Task.Delay(_initialDelay); // todo: dirty hack because the windows need to be load before the log starts flying foreach (var line in lines) { if (cancellationToken.IsCancellationRequested) { return; } // convert the line from csv into data var splitLine = line.Split(','); var logLine = new LogLine { Time = DateTime.Parse(splitLine[0]), MsSinceStart = double.Parse(splitLine[1]), MessageType = splitLine[2], EncodedText = splitLine[3] }; await Task.Delay(timeBetweenMessages); string decodedText = ControlCharacterEncoder.Decode(logLine.EncodedText); if (logLine.MessageType == LOG_TYPE_MUD_INPUT) { await Store.TcpSend.SendAsync(decodedText); } else if (logLine.MessageType == LOG_TYPE_MUD_OUTPUT) { await Store.TcpReceive.SendAsync(decodedText); } else if (logLine.MessageType == LOG_TYPE_CLIENT_INFO) { await Store.ClientInfo.SendAsync(decodedText); } else { throw new Exception("unknown log message type"); } } if (onLogParsed != null) { onLogParsed(); } }
public static List <LogMiner.LogLine> LoadLog(string filename) { var lines = File.ReadAllLines(filename); return(lines.Select((line) => { var splitLine = line.Split(','); return new LogMiner.LogLine { Time = DateTime.Parse(splitLine[0]), MsSinceStart = double.Parse(splitLine[1]), MessageType = splitLine[2], Text = ControlCharacterEncoder.Decode(splitLine[3]), }; }).ToList()); }
public ParsedOutputConverter() { var simpleParser = new SimpleParser(); var attackParser = new AttackParser(); var narrsParser = new NarrsParser(); Store.TcpReceive.SubscribeAsync(async(message) => { var lines = ControlCharacterEncoder.EncodeAndSplit(message); var parsedWithStatusSeparate = new StatusParser().Parse(lines); var parsedWithStatusAndRoomSeparate = RoomParser.Parse(parsedWithStatusSeparate); foreach (var output in parsedWithStatusAndRoomSeparate) { if (output.Type != ParsedOutputType.Raw) { continue; } output.LineMetadata = new LineMetadata[output.Lines.Length]; int i = 0; foreach (var line in output.Lines) { // todo: is having a separate lineMetadata array the way to go? it minimizes copies a little bit but it's ugly and also in some ways doesnt output.LineMetadata[i] = new LineMetadata(); var result = simpleParser.Parse(line); if (result != LineMetadataType.None) { output.LineMetadata[i].Type = result; } else if (attackParser.Matches(line)) { output.LineMetadata[i].Type = LineMetadataType.Attack; } else if (narrsParser.Matches(line)) { output.LineMetadata[i].Type = LineMetadataType.Communication; } i++; } } await Store.ParsedOutput.SendAsync(parsedWithStatusAndRoomSeparate); }); }
public StatusWriter(StatusForm statusForm) { var scoreHealthRegex = new Regex(@"^You have (\d*)\((\d*)\) hit(, (\d*)\((\d*)\) (saidin|saidar|dark power))? and (-?\d*)\((\d*)\) movement points.$", RegexOptions.Compiled); var enemyColors = new Regex(@"\*\\x1B\[3(6|5|1)m([^ ]*)\\x1B\[0m\*", RegexOptions.Compiled); Store.ParsedOutput.Subscribe((outputs) => { foreach (var output in outputs) { if (output.Type == ParsedOutputType.Raw) { foreach (var line in output.Lines) { if (scoreHealthRegex.IsMatch(line)) { statusForm.WriteToOutput(line + "\n", MudColors.ForegroundColor); } if (enemyColors.IsMatch(line)) { // todo: this is inefficient statusForm.WriteToOutput(FormatDecodedText.Format(ControlCharacterEncoder.Decode(line + "\n"))); } } } else if (output.Type == ParsedOutputType.Status) { statusForm.WriteToOutput(output.Lines[0] + "\n", MudColors.ForegroundColor); } else if (output.Type == ParsedOutputType.Room) { foreach (var line in output.Creatures) { if (enemyColors.IsMatch(line)) { // todo: this is inefficient statusForm.WriteToOutput(FormatDecodedText.Format(ControlCharacterEncoder.Decode(line + "\n"))); } } } } }); }
public NarrsWriter(MudClientForm form) { _form = form; Store.ParsedOutput.Subscribe((outputs) => { foreach (var output in outputs) { if (output.Type != ParsedOutputType.Raw) { continue; } foreach (var line in output.Lines) { // todo: also include my narrates in here // todo: should match on colour and also with a regex instead of doing this if (line.Contains(" narrates '") || line.Contains(" tells you '") || line.Contains(" says '") || line.Contains(" speaks from the ") || line.Contains(" bellows '") || line.Contains(" hisses '") || line.Contains(" chats '") || line == "You are hungry." || line == "You are thirsty.") { if (line == "You are hungry." || line == "You are thirsty.") { SystemSounds.Beep.Play(); } // todo: decoding & then converting to rich text will be needlessly wasteful _form.WriteToNarrs(FormatDecodedText.Format(ControlCharacterEncoder.Decode(line + "\n"))); } } } }); }
public static void MineAttackLogs() { var r = new Regex(" (blast|cleave|crush|hack|hit|lance|pierce|pound|scythe|shoot|slash|slice|smite|stab|sting|strike|whip)"); var filename = "./Log_2021-06-12 12-52-06.csv"; var logLines = LoadLog(filename); var matches = logLines .Where(line => line.MessageType == CsvLogFileWriter.LOG_TYPE_MUD_OUTPUT) .Select(l => l.Text.Replace("\\r\\n", "\n").Replace("\\r", "").Split('\n')).SelectMany(m => m) .Where(line => r.IsMatch(line)) .Select(line => ControlCharacterEncoder.Encode(line, forCsv: true)) .GroupBy(l => l) .OrderByDescending(g => g.Key) .OrderByDescending(group => group.Count()) .Select(g => $"{g.Count()}, {g.Key}") .ToList(); File.WriteAllLines("./AttackLines.txt", matches); }
public ParsedOutputWriter(MudClientForm form) { Store.TcpSend.Subscribe((message) => { // output = output + "\n"; // seems to be the most like zMud form.WriteToOutput(" " + message + " ", MudColors.CommandColor); }); Store.ClientInfo.Subscribe((message) => { form.WriteToOutput("\n" + message + "\n", MudColors.ClientInfoColor); }); Store.ParsedOutput.Subscribe((parsedOutputs) => { foreach (var p in parsedOutputs) { switch (p.Type) { case ParsedOutputType.Raw: // todo: dont unsplit things, dont decode things ~ waste of time & effort // probably copy paste a custom version of .FormatOutput /*var lines = p.Lines; * for (int i = 0; i < p.Lines.Length; i++) { * if (!string.IsNullOrWhiteSpace(p.Lines[i])) { * lines[i] = p.LineMetadata[i].Type.ToString().PadRight(15)[..15] + " " + p.Lines[i]; * } * }*/ var formattedOutput = FormatDecodedText.Format(ControlCharacterEncoder.Decode(string.Join("\n", p.Lines) + "\n")); form.WriteToOutput(formattedOutput); if (!form.ContainsFocus) { WindowFlasher.Flash(form); } break; case ParsedOutputType.Room: // todo: there's a break between rooms even if it was a dline form.WriteToOutput("\n" + p.Title, MudColors.RoomTitle); // form.WriteToOutput(string.Join("\n", p.Description) + "\n", MudColors.ForegroundColor); form.WriteToOutput(p.Exits + "\n", MudColors.RoomExits); if (p.Tracks.Length > 0) { form.WriteToOutput(string.Join("\n", p.Tracks) + "\n", MudColors.Tracks); } if (p.Items.Length > 0) { form.WriteToOutput(string.Join("\n", p.Items) + "\n", MudColors.ItemsOnFloor); } if (p.Creatures.Length > 0) { // Todo: does this work correctly? also it's inefficient form.WriteToOutput(FormatDecodedText.Format(ControlCharacterEncoder.Decode("\\x1B[33m" + string.Join("\n", p.Creatures) + "\n"))); } break; case ParsedOutputType.Status: form.WriteToOutput(p.Lines[0] + " ", MudColors.ForegroundColor); break; default: throw new Exception("Unhandled parsed output type"); } } }); }