Skip to content

Commit

Permalink
Merge pull request #3 from nnaaa-vr/development-avalonia
Browse files Browse the repository at this point in the history
Development avalonia
  • Loading branch information
nnaaa-vr authored Apr 30, 2021
2 parents 69b6a86 + 92b1c78 commit 1e333df
Show file tree
Hide file tree
Showing 17 changed files with 945 additions and 63 deletions.
44 changes: 38 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,42 @@
Simple VRChat log parser leveraging the XSNotifications library at https://github.com/nnaaa-vr/XSNotifications to display toast notifications in VR. Currently supported events include world changes, player joined, player left, portal dropped, and shader keywords exceeded. Logged events are also saved with timestamps to session logs, so previous sessions can be referenced if necessary (see who was where, what world you were in, et cetera).
# XSOverlay VRChat Parser
A tool for parsing the VRChat output log and leveraging [XSNotifications](https://github.com/nnaaa-vr/XSNotifications) to display notifications to the user via [XSOverlay](https://store.steampowered.com/app/1173510/XSOverlay/). The tool is useful in desktop mode as well, and will continue to output to the UI. Events currently include: Player Joined, Player Left, Portal Dropped, World Changed, and Shader Keywords Exceeded. The application is built on .NET 5.0 and both framework-dependent and standalone builds can be found in [Releases](https://github.com/nnaaa-vr/XSOverlay-VRChat-Parser/releases). Things are a little messy right now, but should run fine.

On first run, a config.json file will be generated at %AppData%\..\LocalLow\XSOverlay VRChat Parser\config.json. Please see this file for configuration options, and then restart the process after making any changes.
I'm currentling leveraging [Avalonia](https://github.com/AvaloniaUI/Avalonia) for the UI, but I plan to move away from it after .NET 6 and a first party framework like [MAUI](https://github.com/dotnet/maui) are stable.

The process runs in the background and does not have an active window.
The parsing is a bit messy at the moment (but functional). VRChat's log output is very inconsistent and has changed fairly frequently. The parsing function was written around two years ago and has just been patched as time went on.
Improvements and feature implementations are welcome, so feel free to submit your PRs if you do something cool!

There are plans to expand the feature set of this application, including additional event types for those running extended logging in VRChat. Of course, feel free to make PRs yourselves!
-------------
#### Contributions welcome

More detailed documentation will come soon.
For news and updates about the parser, feel free to contact me here on GitHub, in the [XSOverlay Discord](https://discord.gg/PvccFrfqTw), or even my [Twitter](https://twitter.com/nnaaa_vr).

--------------
#### Session Logs

When reporting bugs or simply to review past sessions, note that every session with the application has its output logged to the following directory:

>"%AppData%\\..\\LocalLow\\VRChat\\vrchat\\Logs
--------------
#### Advanced Configuration

For the time being, only a subset of the available configuration options are exposed to the GUI. To access additional configuration options like how frequently the parser checks for updates, what sounds it uses, what icons it uses, where it looks for output logs, please see the configuration json file at the following path:

>"%AppData%\\..\\LocalLow\\VRChat\\vrchat\\config.json
--------------
#### Interface
An example of the interface can be seen below.

![](https://github.com/nnaaa-vr/XSOverlay-VRChat-Parser/blob/development-avalonia/SampleImages/GUISample.png)

-------------
#### Known Issues

- Some glyphs don't display correctly in the Event Log. There's a known issue with the UI framework regarding glyph fallbacks for font families. This will either be resolved in a later patch, or when we move to a new UI framework. Most names will display correctly, and I've embedded Noto for its reasonable support of many languages.

- Currently, the parser does not detect whether or not XSOverlay is running, and does not fall back to playing audio on your primary audio device in the event that it can't have XSOverlay play audio for it. This also means it does not have an option to close when XSOverlay closes at this time. This is a planned feature.

- The window is currently not resizable. The UI isn't built in a way that is conducive to expanding and contracting due to time constraints and my relative unfamiliarity with XAML. This isn't currently a high priority item, but is on the list!

- Hyperlinks don't currently work in the Event Log. This is due to a defect in AvaloniaEdit that I came across while adding support for the generation of world links in the log. I've created an issue for it [here](https://github.com/AvaloniaUI/AvaloniaEdit/issues/133) and am waiting to hear back on it.
Binary file added SampleImages/GUISample.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
8 changes: 7 additions & 1 deletion XSOverlay VRChat Parser.sln
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,24 @@ Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.31105.61
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "XSOverlay VRChat Parser", "XSOverlay VRChat Parser\XSOverlay VRChat Parser.csproj", "{02DC8D37-5DAC-448B-BAFE-14C8FEFBD782}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "XSOverlay VRChat Parser", "XSOverlay VRChat Parser\XSOverlay VRChat Parser.csproj", "{02DC8D37-5DAC-448B-BAFE-14C8FEFBD782}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Debug|x64 = Debug|x64
Release|Any CPU = Release|Any CPU
Release|x64 = Release|x64
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{02DC8D37-5DAC-448B-BAFE-14C8FEFBD782}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{02DC8D37-5DAC-448B-BAFE-14C8FEFBD782}.Debug|Any CPU.Build.0 = Debug|Any CPU
{02DC8D37-5DAC-448B-BAFE-14C8FEFBD782}.Debug|x64.ActiveCfg = Debug|x64
{02DC8D37-5DAC-448B-BAFE-14C8FEFBD782}.Debug|x64.Build.0 = Debug|x64
{02DC8D37-5DAC-448B-BAFE-14C8FEFBD782}.Release|Any CPU.ActiveCfg = Release|Any CPU
{02DC8D37-5DAC-448B-BAFE-14C8FEFBD782}.Release|Any CPU.Build.0 = Release|Any CPU
{02DC8D37-5DAC-448B-BAFE-14C8FEFBD782}.Release|x64.ActiveCfg = Release|x64
{02DC8D37-5DAC-448B-BAFE-14C8FEFBD782}.Release|x64.Build.0 = Release|x64
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file not shown.
44 changes: 44 additions & 0 deletions XSOverlay VRChat Parser/Avalonia/Controls/AcrylicWindow.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
using Avalonia.Controls.Primitives;
using Avalonia.Platform;
using Avalonia.Styling;
using System;

namespace Avalonia.Controls
{
public class AcrylicWindow : Window, IStyleable
{
Type IStyleable.StyleKey => typeof(Window);

public AcrylicWindow()
{
ExtendClientAreaToDecorationsHint = true;
ExtendClientAreaTitleBarHeightHint = -1;
TransparencyLevelHint = WindowTransparencyLevel.AcrylicBlur;

this.GetObservable(WindowStateProperty)
.Subscribe(x =>
{
PseudoClasses.Set(":maximized", x == WindowState.Maximized);
PseudoClasses.Set(":fullscreen", x == WindowState.FullScreen);
});

this.GetObservable(IsExtendedIntoWindowDecorationsProperty)
.Subscribe(x =>
{
if (!x)
{
SystemDecorations = SystemDecorations.Full;
TransparencyLevelHint = WindowTransparencyLevel.Blur;
}
});
}

protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
{
base.OnApplyTemplate(e);
ExtendClientAreaChromeHints =
ExtendClientAreaChromeHints.PreferSystemChrome |
ExtendClientAreaChromeHints.OSXThickTitleBar;
}
}
}
13 changes: 13 additions & 0 deletions XSOverlay VRChat Parser/Avalonia/UIMain.axaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<Application xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:XSOverlay_VRChat_Parser.Avalonia"
xmlns:views="clr-namespace:XSOverlay_VRChat_Parser.Avalonia.Views"
x:Class="XSOverlay_VRChat_Parser.Avalonia.UIMain" Name="XSOverlay VRChat Parser">
<Application.DataTemplates>
<local:ViewLocator/>
</Application.DataTemplates>
<Application.Styles>
<FluentTheme Mode="Dark"/>
<StyleInclude Source="avares://AvaloniaEdit/AvaloniaEdit.xaml" />
</Application.Styles>
</Application>
61 changes: 61 additions & 0 deletions XSOverlay VRChat Parser/Avalonia/UIMain.axaml.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
using Avalonia;
using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Markup.Xaml;
using System;
using System.Threading.Tasks;
using XSOverlay_VRChat_Parser.Avalonia.ViewModels;
using XSOverlay_VRChat_Parser.Avalonia.Views;
using XSOverlay_VRChat_Parser.Models;

namespace XSOverlay_VRChat_Parser.Avalonia
{
public class UIMain : Application
{
public static ConfigurationModel Configuration { get; set; }
private static DateTime LastSaveRequest { get; set; }
private static Task SaveTask { get; set; }

public static void SaveConfigurationDebounced()
{
LastSaveRequest = DateTime.Now;

if (SaveTask != null && SaveTask.IsCompleted)
{
SaveTask.Dispose();
SaveTask = null;
}

if (SaveTask == null)
{
SaveTask = new Task(() =>
{
while (LastSaveRequest.AddSeconds(1) > DateTime.Now)
Task.Delay(100).GetAwaiter().GetResult();
ConfigurationModel.Save(Configuration);
});

SaveTask.Start();
}
}

public static void Start(string[] args, AppBuilder builder, ConfigurationModel _configuration)
{
Configuration = _configuration;

builder.StartWithClassicDesktopLifetime(args);
}

public override void Initialize()
{
AvaloniaXamlLoader.Load(this);
}

public override void OnFrameworkInitializationCompleted()
{
if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
desktop.MainWindow = new MainWindow() { DataContext = new MainWindowViewModel() };

base.OnFrameworkInitializationCompleted();
}
}
}
28 changes: 28 additions & 0 deletions XSOverlay VRChat Parser/Avalonia/ViewLocator.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
using Avalonia.Controls;
using Avalonia.Controls.Templates;
using System;
using XSOverlay_VRChat_Parser.Avalonia.ViewModels;

namespace XSOverlay_VRChat_Parser.Avalonia
{
public class ViewLocator : IDataTemplate
{
public bool SupportsRecycling => false;

public IControl Build(object data)
{
var name = data.GetType().FullName!.Replace("ViewModel", "View");
var type = Type.GetType(name);

if (type != null)
return (Control)Activator.CreateInstance(type);
else
return new TextBlock { Text = "Not Found: " + name };
}

public bool Match(object data)
{
return data is ViewModelBase;
}
}
}
Loading

0 comments on commit 1e333df

Please sign in to comment.