Browse Source

Reworked console printers to print directly to the console window instead of going through stdout first

pull/46/head
Anairkoen Schno 5 years ago
parent
commit
1b8b74628d
10 changed files with 195 additions and 46 deletions
  1. +1
    -1
      BuildTools
  2. +0
    -1
      IPA.Injector/IPA.Injector.csproj
  3. +5
    -3
      IPA.Injector/Injector.cs
  4. +2
    -0
      IPA.Loader/IPA.Loader.csproj
  5. +21
    -5
      IPA.Loader/Logging/ConsoleWindow.cs
  6. +75
    -5
      IPA.Loader/Logging/Printers/ColoredConsolePrinter.cs
  7. +33
    -0
      IPA.Loader/Logging/Printers/ColorlessConsolePrinter.cs
  8. +57
    -30
      IPA.Loader/Logging/StandardLogger.cs
  9. +1
    -1
      IPA/obj/Debug/IPA.csproj.CoreCompileInputs.cache
  10. BIN
      Refs/UnityEngine.CoreModule.dll

+ 1
- 1
BuildTools

@ -1 +1 @@
Subproject commit e0cefce0a1bb1aacc0afdbf305df1fa65da55464
Subproject commit de919c49496a7e7e11382bd876473018ddceaa1a

+ 0
- 1
IPA.Injector/IPA.Injector.csproj View File

@ -56,7 +56,6 @@
<Compile Include="Backups\BackupManager.cs" /> <Compile Include="Backups\BackupManager.cs" />
<Compile Include="Backups\BackupUnit.cs" /> <Compile Include="Backups\BackupUnit.cs" />
<Compile Include="Bootstrapper.cs" /> <Compile Include="Bootstrapper.cs" />
<Compile Include="ConsoleWindow.cs" />
<Compile Include="Injector.cs" /> <Compile Include="Injector.cs" />
<Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Updates.cs" /> <Compile Include="Updates.cs" />


+ 5
- 3
IPA.Injector/Injector.cs View File

@ -35,14 +35,14 @@ namespace IPA.Injector
SetupLibraryLoading(); SetupLibraryLoading();
EnsureUserData();
EnsureDirectories();
// this is weird, but it prevents Mono from having issues loading the type. // this is weird, but it prevents Mono from having issues loading the type.
// IMPORTANT: NO CALLS TO ANY LOGGER CAN HAPPEN BEFORE THIS // IMPORTANT: NO CALLS TO ANY LOGGER CAN HAPPEN BEFORE THIS
var unused = StandardLogger.PrintFilter; var unused = StandardLogger.PrintFilter;
#region // Above hack explaination #region // Above hack explaination
/* /*
* Due to an unknown bug in the version of Mono that Unity 2018.1.8 uses, if the first access to StandardLogger
* Due to an unknown bug in the version of Mono that Unity uses, if the first access to StandardLogger
* is a call to a constructor, then Mono fails to load the type correctly. However, if the first access is to * is a call to a constructor, then Mono fails to load the type correctly. However, if the first access is to
* the above static property (or maybe any, but I don't really know) it behaves as expected and works fine. * the above static property (or maybe any, but I don't really know) it behaves as expected and works fine.
*/ */
@ -66,11 +66,13 @@ namespace IPA.Injector
} }
} }
private static void EnsureUserData()
private static void EnsureDirectories()
{ {
string path; string path;
if (!Directory.Exists(path = Path.Combine(Environment.CurrentDirectory, "UserData"))) if (!Directory.Exists(path = Path.Combine(Environment.CurrentDirectory, "UserData")))
Directory.CreateDirectory(path); Directory.CreateDirectory(path);
if (!Directory.Exists(path = Path.Combine(Environment.CurrentDirectory, "Plugins")))
Directory.CreateDirectory(path);
} }
private static void SetupLibraryLoading() private static void SetupLibraryLoading()


+ 2
- 0
IPA.Loader/IPA.Loader.csproj View File

@ -73,6 +73,8 @@
<Compile Include="Loader\Features\Feature.cs" /> <Compile Include="Loader\Features\Feature.cs" />
<Compile Include="Loader\PluginLoader.cs" /> <Compile Include="Loader\PluginLoader.cs" />
<Compile Include="Loader\PluginManifest.cs" /> <Compile Include="Loader\PluginManifest.cs" />
<Compile Include="Logging\ConsoleWindow.cs" />
<Compile Include="Logging\Printers\ColorlessConsolePrinter.cs" />
<Compile Include="Logging\Printers\PluginSubLogPrinter.cs" /> <Compile Include="Logging\Printers\PluginSubLogPrinter.cs" />
<Compile Include="PluginInterfaces\BeatSaber\IBeatSaberPlugin.cs" /> <Compile Include="PluginInterfaces\BeatSaber\IBeatSaberPlugin.cs" />
<Compile Include="PluginInterfaces\BeatSaber\IEnhancedBeatSaberPlugin.cs" /> <Compile Include="PluginInterfaces\BeatSaber\IEnhancedBeatSaberPlugin.cs" />


IPA.Injector/ConsoleWindow.cs → IPA.Loader/Logging/ConsoleWindow.cs View File

@ -3,11 +3,22 @@ using System.IO;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using Microsoft.Win32.SafeHandles; using Microsoft.Win32.SafeHandles;
namespace IPA.Injector
namespace IPA.Logging
{ {
// https://stackoverflow.com/a/48864902/3117125 // https://stackoverflow.com/a/48864902/3117125
internal static class WinConsole internal static class WinConsole
{ {
internal static TextWriter ConOut;
internal static TextReader ConIn;
private static SafeFileHandle outHandle;
private static SafeFileHandle inHandle;
internal static IntPtr OutHandle => outHandle.DangerousGetHandle();
internal static IntPtr InHandle => inHandle.DangerousGetHandle();
internal static bool IsInitialized;
public static void Initialize(bool alwaysCreateNewConsole = true) public static void Initialize(bool alwaysCreateNewConsole = true)
{ {
bool consoleAttached = true; bool consoleAttached = true;
@ -21,6 +32,7 @@ namespace IPA.Injector
if (consoleAttached) if (consoleAttached)
{ {
InitializeStreams(); InitializeStreams();
IsInitialized = true;
} }
} }
@ -32,10 +44,11 @@ namespace IPA.Injector
private static void InitializeOutStream() private static void InitializeOutStream()
{ {
var fs = CreateFileStream("CONOUT$", GenericWrite, FileShareWrite, FileAccess.Write);
var fs = CreateFileStream("CONOUT$", GenericWrite, FileShareWrite, FileAccess.Write, out outHandle);
if (fs != null) if (fs != null)
{ {
var writer = new StreamWriter(fs) { AutoFlush = true }; var writer = new StreamWriter(fs) { AutoFlush = true };
ConOut = writer;
Console.SetOut(writer); Console.SetOut(writer);
Console.SetError(writer); Console.SetError(writer);
} }
@ -43,22 +56,25 @@ namespace IPA.Injector
private static void InitializeInStream() private static void InitializeInStream()
{ {
var fs = CreateFileStream("CONIN$", GenericRead, FileShareRead, FileAccess.Read);
var fs = CreateFileStream("CONIN$", GenericRead, FileShareRead, FileAccess.Read, out inHandle);
if (fs != null) if (fs != null)
{ {
Console.SetIn(new StreamReader(fs));
Console.SetIn(ConIn = new StreamReader(fs));
} }
} }
private static FileStream CreateFileStream(string name, uint win32DesiredAccess, uint win32ShareMode, private static FileStream CreateFileStream(string name, uint win32DesiredAccess, uint win32ShareMode,
FileAccess dotNetFileAccess)
FileAccess dotNetFileAccess, out SafeFileHandle handle)
{ {
var file = new SafeFileHandle(CreateFileW(name, win32DesiredAccess, win32ShareMode, IntPtr.Zero, OpenExisting, FileAttributeNormal, IntPtr.Zero), true); var file = new SafeFileHandle(CreateFileW(name, win32DesiredAccess, win32ShareMode, IntPtr.Zero, OpenExisting, FileAttributeNormal, IntPtr.Zero), true);
if (!file.IsInvalid) if (!file.IsInvalid)
{ {
handle = file;
var fs = new FileStream(file, dotNetFileAccess); var fs = new FileStream(file, dotNetFileAccess);
return fs; return fs;
} }
handle = null;
return null; return null;
} }

+ 75
- 5
IPA.Loader/Logging/Printers/ColoredConsolePrinter.cs View File

@ -1,4 +1,5 @@
using System; using System;
using System.Runtime.InteropServices;
namespace IPA.Logging.Printers namespace IPA.Logging.Printers
{ {
@ -7,7 +8,7 @@ namespace IPA.Logging.Printers
/// </summary> /// </summary>
public class ColoredConsolePrinter : LogPrinter public class ColoredConsolePrinter : LogPrinter
{ {
Logger.LogLevel filter = Logger.LogLevel.All;
private Logger.LogLevel filter = Logger.LogLevel.All;
/// <summary> /// <summary>
/// A filter for this specific printer. /// A filter for this specific printer.
@ -19,7 +20,7 @@ namespace IPA.Logging.Printers
public ConsoleColor Color { get; set; } = Console.ForegroundColor; public ConsoleColor Color { get; set; } = Console.ForegroundColor;
/// <summary> /// <summary>
/// Prints an entry to the associated file.
/// Prints an entry to the console window.
/// </summary> /// </summary>
/// <param name="level">the <see cref="Logger.Level"/> of the message</param> /// <param name="level">the <see cref="Logger.Level"/> of the message</param>
/// <param name="time">the <see cref="DateTime"/> the message was recorded at</param> /// <param name="time">the <see cref="DateTime"/> the message was recorded at</param>
@ -28,10 +29,79 @@ namespace IPA.Logging.Printers
public override void Print(Logger.Level level, DateTime time, string logName, string message) public override void Print(Logger.Level level, DateTime time, string logName, string message)
{ {
if (((byte)level & (byte)StandardLogger.PrintFilter) == 0) return; if (((byte)level & (byte)StandardLogger.PrintFilter) == 0) return;
Console.ForegroundColor = Color;
EnsureDefaultsPopulated(WinConsole.OutHandle);
SetColor(Color, WinConsole.OutHandle);
foreach (var line in message.Split(new[] { "\n", Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries)) foreach (var line in message.Split(new[] { "\n", Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries))
Console.WriteLine(Logger.LogFormat, line, logName, time, level.ToString().ToUpper());
Console.ResetColor();
WinConsole.ConOut.WriteLine(Logger.LogFormat, line, logName, time, level.ToString().ToUpper());
ResetColor(WinConsole.OutHandle);
} }
private static bool _haveReadDefaultColors;
private static short _defaultColors;
private void EnsureDefaultsPopulated(IntPtr handle, bool force = false)
{
if (!_haveReadDefaultColors | force)
{
GetConsoleScreenBufferInfo(handle, out var info);
_defaultColors = (short)(info.Attribute & ~15);
_haveReadDefaultColors = true;
}
}
private void ResetColor(IntPtr handle)
{
GetConsoleScreenBufferInfo(handle, out var info);
var otherAttrs = (short)(info.Attribute & ~15);
SetConsoleTextAttribute(handle, (short)(otherAttrs | _defaultColors));
}
private void SetColor(ConsoleColor col, IntPtr handle)
{
GetConsoleScreenBufferInfo(handle, out var info);
var attr = GetAttrForeground(info.Attribute, col);
SetConsoleTextAttribute(handle, attr);
}
private static short GetAttrForeground(int attr, ConsoleColor color)
{
attr &= ~15;
return (short)(attr | (int)color);
}
// ReSharper disable NotAccessedField.Local
#pragma warning disable 649
private struct Coordinate
{
public short X;
public short Y;
}
private struct SmallRect
{
public short Left;
public short Top;
public short Right;
public short Bottom;
}
private struct ConsoleScreenBufferInfo
{
public Coordinate Size;
public Coordinate CursorPosition;
public short Attribute;
public SmallRect Window;
public Coordinate MaxWindowSize;
}
#pragma warning restore 649
// ReSharper restore NotAccessedField.Local
[DllImport("kernel32.dll", EntryPoint = "GetConsoleScreenBufferInfo", SetLastError = true, CharSet = CharSet.Unicode)]
private static extern bool GetConsoleScreenBufferInfo(IntPtr handle, out ConsoleScreenBufferInfo info);
[DllImport("kernel32.dll", EntryPoint = "SetConsoleTextAttribute", SetLastError = true, CharSet = CharSet.Unicode)]
private static extern bool SetConsoleTextAttribute(IntPtr handle, short attribute);
} }
} }

+ 33
- 0
IPA.Loader/Logging/Printers/ColorlessConsolePrinter.cs View File

@ -0,0 +1,33 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace IPA.Logging.Printers
{
/// <summary>
/// A colorless version of <see cref="ColoredConsolePrinter"/>, that indiscriminantly prints to standard out.
/// </summary>
public class ColorlessConsolePrinter : LogPrinter
{
/// <summary>
/// A filter for this specific printer.
/// </summary>
public override Logger.LogLevel Filter { get; set; }
/// <summary>
/// Prints an entry to standard out.
/// </summary>
/// <param name="level">the <see cref="Logger.Level"/> of the message</param>
/// <param name="time">the <see cref="DateTime"/> the message was recorded at</param>
/// <param name="logName">the name of the log that sent the message</param>
/// <param name="message">the message to print</param>
public override void Print(Logger.Level level, DateTime time, string logName, string message)
{
if (((byte)level & (byte)StandardLogger.PrintFilter) == 0) return;
foreach (var line in message.Split(new[] { "\n", Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries))
Console.WriteLine(Logger.LogFormat, line, logName, time, level.ToString().ToUpper());
}
}
}

+ 57
- 30
IPA.Loader/Logging/StandardLogger.cs View File

@ -24,39 +24,57 @@ namespace IPA.Logging
{ {
private static readonly List<LogPrinter> defaultPrinters = new List<LogPrinter>() private static readonly List<LogPrinter> defaultPrinters = new List<LogPrinter>()
{ {
new ColoredConsolePrinter()
{
Filter = LogLevel.DebugOnly,
Color = ConsoleColor.Green,
},
new ColoredConsolePrinter()
{
Filter = LogLevel.InfoOnly,
Color = ConsoleColor.White,
},
new ColoredConsolePrinter()
{
Filter = LogLevel.NoticeOnly,
Color = ConsoleColor.Cyan
},
new ColoredConsolePrinter()
{
Filter = LogLevel.WarningOnly,
Color = ConsoleColor.Yellow,
},
new ColoredConsolePrinter()
{
Filter = LogLevel.ErrorOnly,
Color = ConsoleColor.Red,
},
new ColoredConsolePrinter()
{
Filter = LogLevel.CriticalOnly,
Color = ConsoleColor.Magenta,
},
new GlobalLogFilePrinter() new GlobalLogFilePrinter()
}; };
static StandardLogger()
{
ConsoleColorSupport();
}
private static bool addedConsolePrinters;
private static bool finalizedDefaultPrinters;
internal static void ConsoleColorSupport()
{
if (!addedConsolePrinters && !finalizedDefaultPrinters && WinConsole.IsInitialized )
{
defaultPrinters.AddRange(new []
{
new ColoredConsolePrinter()
{
Filter = LogLevel.DebugOnly,
Color = ConsoleColor.Green,
},
new ColoredConsolePrinter()
{
Filter = LogLevel.InfoOnly,
Color = ConsoleColor.White,
},
new ColoredConsolePrinter()
{
Filter = LogLevel.NoticeOnly,
Color = ConsoleColor.Cyan
},
new ColoredConsolePrinter()
{
Filter = LogLevel.WarningOnly,
Color = ConsoleColor.Yellow,
},
new ColoredConsolePrinter()
{
Filter = LogLevel.ErrorOnly,
Color = ConsoleColor.Red,
},
new ColoredConsolePrinter()
{
Filter = LogLevel.CriticalOnly,
Color = ConsoleColor.Magenta,
}
});
addedConsolePrinters = true;
}
}
/// <summary> /// <summary>
/// The <see cref="TextWriter"/> for writing directly to the console window, or stdout if no window open. /// The <see cref="TextWriter"/> for writing directly to the console window, or stdout if no window open.
/// </summary> /// </summary>
@ -112,6 +130,15 @@ namespace IPA.Logging
internal StandardLogger(string name) internal StandardLogger(string name)
{ {
ConsoleColorSupport();
if (!finalizedDefaultPrinters)
{
if (!addedConsolePrinters)
AddDefaultPrinter(new ColorlessConsolePrinter());
finalizedDefaultPrinters = true;
}
logName = name; logName = name;
printers.Add(new PluginLogFilePrinter(name)); printers.Add(new PluginLogFilePrinter(name));


+ 1
- 1
IPA/obj/Debug/IPA.csproj.CoreCompileInputs.cache View File

@ -1 +1 @@
0a3222410c8b7f878f3aac05d926ef18ba74d12a
5d76d76cc5c14257f2b9071c928c27b3edd80cc0

BIN
Refs/UnityEngine.CoreModule.dll View File


Loading…
Cancel
Save