mirror of
https://github.com/MrUnknownDE/VRCX.git
synced 2026-04-17 05:43:51 +02:00
v2019.08.19
This commit is contained in:
37
Browser.cs
37
Browser.cs
@@ -6,13 +6,14 @@
|
||||
using CefSharp;
|
||||
using CefSharp.OffScreen;
|
||||
using SharpDX.Direct3D11;
|
||||
using System;
|
||||
using System.Drawing;
|
||||
using System.Threading;
|
||||
|
||||
namespace VRCX
|
||||
{
|
||||
public class Browser : ChromiumWebBrowser
|
||||
{
|
||||
private readonly ReaderWriterLockSlim m_Lock = new ReaderWriterLockSlim();
|
||||
private Texture2D m_Texture;
|
||||
|
||||
public Browser(Texture2D texture, string address)
|
||||
@@ -24,38 +25,46 @@ namespace VRCX
|
||||
m_Texture = texture;
|
||||
Size = new Size(texture.Description.Width, texture.Description.Height);
|
||||
RenderHandler.Dispose();
|
||||
RenderHandler = new RenderHandler(this);
|
||||
RenderHandler = new RenderHandler(this, m_Lock);
|
||||
var options = new BindingOptions()
|
||||
{
|
||||
CamelCaseJavascriptNames = false
|
||||
};
|
||||
JavascriptObjectRepository.Register("VRCX", new VRCX(), true, options);
|
||||
JavascriptObjectRepository.Register("VRCXStorage", new VRCXStorage(), false, options);
|
||||
JavascriptObjectRepository.Register("SQLite", new SQLite(), true, options);
|
||||
}
|
||||
|
||||
public new void Dispose()
|
||||
{
|
||||
RenderHandler.Dispose();
|
||||
RenderHandler = null;
|
||||
base.Dispose();
|
||||
}
|
||||
|
||||
public void Render()
|
||||
{
|
||||
var handler = (RenderHandler)RenderHandler;
|
||||
lock (handler.BufferLock)
|
||||
m_Lock.EnterReadLock();
|
||||
try
|
||||
{
|
||||
if (handler.Buffer.IsAllocated)
|
||||
var H = (RenderHandler)RenderHandler;
|
||||
if (H.Buffer.IsAllocated)
|
||||
{
|
||||
var context = m_Texture.Device.ImmediateContext;
|
||||
var box = context.MapSubresource(m_Texture, 0, MapMode.WriteDiscard, MapFlags.None);
|
||||
if (box.DataPointer != IntPtr.Zero)
|
||||
if (box.IsEmpty)
|
||||
{
|
||||
var width = handler.Width;
|
||||
var height = handler.Height;
|
||||
if (box.RowPitch == width * 4)
|
||||
if (box.RowPitch == H.Width * 4)
|
||||
{
|
||||
WinApi.CopyMemory(box.DataPointer, handler.Buffer.AddrOfPinnedObject(), (uint)handler.BufferSize);
|
||||
WinApi.CopyMemory(box.DataPointer, H.Buffer.AddrOfPinnedObject(), (uint)H.BufferSize);
|
||||
}
|
||||
else
|
||||
{
|
||||
var dest = box.DataPointer;
|
||||
var src = handler.Buffer.AddrOfPinnedObject();
|
||||
var src = H.Buffer.AddrOfPinnedObject();
|
||||
var pitch = box.RowPitch;
|
||||
var length = width * 4;
|
||||
var length = H.Width * 4;
|
||||
var height = H.Height;
|
||||
for (var i = 0; i < height; ++i)
|
||||
{
|
||||
WinApi.CopyMemory(dest, src, (uint)length);
|
||||
@@ -67,6 +76,10 @@ namespace VRCX
|
||||
context.UnmapSubresource(m_Texture, 0);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
m_Lock.ExitReadLock();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -13,36 +13,32 @@ namespace VRCX
|
||||
public static float CpuUsage { get; private set; }
|
||||
private static Thread m_Thread;
|
||||
|
||||
public static void Start()
|
||||
public static void Init()
|
||||
{
|
||||
if (m_Thread == null)
|
||||
m_Thread = new Thread(() =>
|
||||
{
|
||||
m_Thread = new Thread(() =>
|
||||
PerformanceCounter counter = null;
|
||||
try
|
||||
{
|
||||
PerformanceCounter cpuCounter = null;
|
||||
try
|
||||
{
|
||||
cpuCounter = new PerformanceCounter("Processor Information", "% Processor Utility", "_Total", true);
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
try
|
||||
{
|
||||
if (cpuCounter == null)
|
||||
{
|
||||
cpuCounter = new PerformanceCounter("Processor", "% Processor Time", "_Total", true);
|
||||
}
|
||||
}
|
||||
catch
|
||||
counter = new PerformanceCounter("Processor Information", "% Processor Utility", "_Total", true);
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
try
|
||||
{
|
||||
if (counter == null)
|
||||
{
|
||||
counter = new PerformanceCounter("Processor", "% Processor Time", "_Total", true);
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
if (counter != null)
|
||||
{
|
||||
while (m_Thread != null)
|
||||
{
|
||||
if (cpuCounter != null)
|
||||
{
|
||||
CpuUsage = cpuCounter.NextValue();
|
||||
}
|
||||
try
|
||||
{
|
||||
Thread.Sleep(1000);
|
||||
@@ -51,31 +47,23 @@ namespace VRCX
|
||||
{
|
||||
// ThreadInterruptedException
|
||||
}
|
||||
CpuUsage = counter.NextValue();
|
||||
}
|
||||
if (cpuCounter != null)
|
||||
{
|
||||
cpuCounter.Dispose();
|
||||
}
|
||||
});
|
||||
m_Thread.Start();
|
||||
}
|
||||
counter.Dispose();
|
||||
}
|
||||
})
|
||||
{
|
||||
IsBackground = true
|
||||
};
|
||||
m_Thread.Start();
|
||||
}
|
||||
|
||||
public static void Stop()
|
||||
public static void Exit()
|
||||
{
|
||||
var thread = m_Thread;
|
||||
if (thread != null)
|
||||
{
|
||||
m_Thread = null;
|
||||
try
|
||||
{
|
||||
thread.Interrupt();
|
||||
thread.Join();
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
}
|
||||
var T = m_Thread;
|
||||
m_Thread = null;
|
||||
T.Interrupt();
|
||||
T.Join();
|
||||
}
|
||||
}
|
||||
}
|
||||
99
Discord.cs
99
Discord.cs
@@ -4,38 +4,80 @@
|
||||
// For a copy, see <https://opensource.org/licenses/MIT>.
|
||||
|
||||
using DiscordRPC;
|
||||
using System.Threading;
|
||||
|
||||
namespace VRCX
|
||||
{
|
||||
public class Discord
|
||||
{
|
||||
private static RichPresence m_Presence = new RichPresence();
|
||||
private static DiscordRpcClient m_Client;
|
||||
private static readonly ReaderWriterLockSlim m_Lock = new ReaderWriterLockSlim();
|
||||
private static readonly RichPresence m_Presence = new RichPresence();
|
||||
private static Thread m_Thread;
|
||||
private static bool m_Active;
|
||||
|
||||
public static void Update()
|
||||
public static void Init()
|
||||
{
|
||||
if (m_Client != null)
|
||||
m_Thread = new Thread(() =>
|
||||
{
|
||||
m_Client.Invoke();
|
||||
}
|
||||
if (m_Active)
|
||||
{
|
||||
if (m_Client == null)
|
||||
DiscordRpcClient client = null;
|
||||
while (m_Thread != null)
|
||||
{
|
||||
m_Client = new DiscordRpcClient("525953831020920832");
|
||||
m_Client.Initialize();
|
||||
try
|
||||
{
|
||||
Thread.Sleep(1000);
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ThreadInterruptedException
|
||||
}
|
||||
if (client != null)
|
||||
{
|
||||
m_Lock.EnterReadLock();
|
||||
try
|
||||
{
|
||||
client.SetPresence(m_Presence);
|
||||
}
|
||||
finally
|
||||
{
|
||||
m_Lock.ExitReadLock();
|
||||
}
|
||||
client.Invoke();
|
||||
}
|
||||
if (m_Active)
|
||||
{
|
||||
if (client == null)
|
||||
{
|
||||
client = new DiscordRpcClient("525953831020920832");
|
||||
if (!client.Initialize())
|
||||
{
|
||||
client.Dispose();
|
||||
client = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (client != null)
|
||||
{
|
||||
client.Dispose();
|
||||
client = null;
|
||||
}
|
||||
}
|
||||
lock (m_Presence)
|
||||
if (client != null)
|
||||
{
|
||||
m_Client.SetPresence(m_Presence);
|
||||
client.Dispose();
|
||||
}
|
||||
}
|
||||
else if (m_Client != null)
|
||||
})
|
||||
{
|
||||
m_Client.Dispose();
|
||||
m_Client = null;
|
||||
}
|
||||
IsBackground = true
|
||||
};
|
||||
m_Thread.Start();
|
||||
}
|
||||
|
||||
public static void Exit()
|
||||
{
|
||||
var T = m_Thread;
|
||||
m_Thread = null;
|
||||
T.Interrupt();
|
||||
T.Join();
|
||||
}
|
||||
|
||||
public void SetActive(bool active)
|
||||
@@ -45,16 +87,22 @@ namespace VRCX
|
||||
|
||||
public void SetText(string details, string state)
|
||||
{
|
||||
lock (m_Presence)
|
||||
m_Lock.EnterWriteLock();
|
||||
try
|
||||
{
|
||||
m_Presence.Details = details;
|
||||
m_Presence.State = state;
|
||||
}
|
||||
finally
|
||||
{
|
||||
m_Lock.ExitWriteLock();
|
||||
}
|
||||
}
|
||||
|
||||
public void SetAssets(string largeKey, string largeText, string smallKey, string smallText)
|
||||
{
|
||||
lock (m_Presence)
|
||||
m_Lock.EnterWriteLock();
|
||||
try
|
||||
{
|
||||
if (string.IsNullOrEmpty(largeKey) &&
|
||||
string.IsNullOrEmpty(smallKey))
|
||||
@@ -73,6 +121,10 @@ namespace VRCX
|
||||
m_Presence.Assets.SmallImageText = smallText;
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
m_Lock.ExitWriteLock();
|
||||
}
|
||||
}
|
||||
|
||||
// JSB Sucks
|
||||
@@ -83,7 +135,8 @@ namespace VRCX
|
||||
|
||||
public static void SetTimestamps(ulong startUnixMilliseconds, ulong endUnixMilliseconds)
|
||||
{
|
||||
lock (m_Presence)
|
||||
m_Lock.EnterWriteLock();
|
||||
try
|
||||
{
|
||||
if (startUnixMilliseconds == 0)
|
||||
{
|
||||
@@ -106,6 +159,10 @@ namespace VRCX
|
||||
}
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
m_Lock.ExitWriteLock();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
345
LogWatcher.cs
345
LogWatcher.cs
@@ -5,176 +5,194 @@
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
|
||||
namespace VRCX
|
||||
{
|
||||
public class VRCX_LogWatcher
|
||||
public class LogWatcherFile
|
||||
{
|
||||
public long Length;
|
||||
public long Position;
|
||||
}
|
||||
|
||||
public class LogWatcher
|
||||
{
|
||||
private static readonly ReaderWriterLockSlim m_Lock = new ReaderWriterLockSlim();
|
||||
private static List<string[]> m_GameLog = new List<string[]>();
|
||||
private static Thread m_Thread;
|
||||
private static bool m_Reset;
|
||||
|
||||
public static void Start()
|
||||
// NOTE
|
||||
// FileSystemWatcher() is unreliable
|
||||
|
||||
public static void Init()
|
||||
{
|
||||
if (m_Thread == null)
|
||||
m_Thread = new Thread(() =>
|
||||
{
|
||||
m_Thread = new Thread(() =>
|
||||
var D = new Dictionary<string, LogWatcherFile>();
|
||||
var di = new DirectoryInfo(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData) + @"Low\VRChat\VRChat");
|
||||
while (m_Thread != null)
|
||||
{
|
||||
var lastPosition = 0L;
|
||||
var firstLine = string.Empty;
|
||||
while (m_Thread != null)
|
||||
try
|
||||
{
|
||||
if (m_Reset)
|
||||
Thread.Sleep(1000);
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ThreadInterruptedException
|
||||
}
|
||||
if (m_Reset)
|
||||
{
|
||||
m_Reset = false;
|
||||
D.Clear();
|
||||
m_Lock.EnterWriteLock();
|
||||
try
|
||||
{
|
||||
m_Reset = false;
|
||||
firstLine = string.Empty;
|
||||
lastPosition = 0;
|
||||
m_GameLog.Clear();
|
||||
}
|
||||
var info = new DirectoryInfo(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData) + @"Low\VRChat\VRChat");
|
||||
if (info != null &&
|
||||
info.Exists)
|
||||
finally
|
||||
{
|
||||
var files = info.GetFiles("output_log_*.txt", SearchOption.TopDirectoryOnly);
|
||||
if (files != null &&
|
||||
files.Length >= 1)
|
||||
m_Lock.ExitWriteLock();
|
||||
}
|
||||
}
|
||||
var S = new HashSet<string>(D.Keys);
|
||||
di.Refresh();
|
||||
if (di.Exists)
|
||||
{
|
||||
var files = di.GetFiles("output_log_*.txt", SearchOption.TopDirectoryOnly);
|
||||
Array.Sort(files, (A, B) => A.CreationTime.CompareTo(B.CreationTime));
|
||||
var bias = DateTime.Now.AddMinutes(-5d);
|
||||
foreach (var fi in files)
|
||||
{
|
||||
if (bias.CompareTo(fi.LastWriteTime) <= 0)
|
||||
{
|
||||
Array.Sort(files, (A, B) => B.LastWriteTime.CompareTo(A.LastWriteTime));
|
||||
if (firstLine == string.Empty)
|
||||
fi.Refresh();
|
||||
}
|
||||
if (D.TryGetValue(fi.Name, out LogWatcherFile F))
|
||||
{
|
||||
S.Remove(fi.Name);
|
||||
if (F.Length == fi.Length)
|
||||
{
|
||||
for (var i = files.Length - 1; i >= 1; --i)
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
F = new LogWatcherFile();
|
||||
D.Add(fi.Name, F);
|
||||
}
|
||||
F.Length = fi.Length;
|
||||
Parse(fi, ref F.Position);
|
||||
}
|
||||
}
|
||||
foreach (var key in S)
|
||||
{
|
||||
D.Remove(key);
|
||||
}
|
||||
}
|
||||
})
|
||||
{
|
||||
IsBackground = true
|
||||
};
|
||||
m_Thread.Start();
|
||||
}
|
||||
|
||||
public static void Exit()
|
||||
{
|
||||
var T = m_Thread;
|
||||
m_Thread = null;
|
||||
T.Interrupt();
|
||||
T.Join();
|
||||
}
|
||||
|
||||
public static void Parse(FileInfo info, ref long position)
|
||||
{
|
||||
try
|
||||
{
|
||||
using (var stream = info.Open(FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
|
||||
using (var reader = new StreamReader(stream, Encoding.UTF8))
|
||||
{
|
||||
stream.Position = position;
|
||||
var s = string.Empty;
|
||||
while ((s = reader.ReadLine()) != null)
|
||||
{
|
||||
if (s.Length > 35)
|
||||
{
|
||||
var c = s[35];
|
||||
if (c == 'R')
|
||||
{
|
||||
// 2019.07.31 22:26:24 Log - [RoomManager] Joining wrld_4432ea9b-729c-46e3-8eaf-846aa0a37fdd:6974~private(usr_4f76a584-9d4b-46f6-8209-8305eb683661)~nonce(0000000000000000000000000000000000000000000000000000000000000000)
|
||||
// 2019.07.31 22:26:24 Log - [RoomManager] Joining or Creating Room: VRChat Home
|
||||
if (s.Length > 56 &&
|
||||
string.Compare(s, 34, "[RoomManager] Joining ", 0, "[RoomManager] Joining ".Length, StringComparison.Ordinal) == 0 &&
|
||||
string.Compare(s, 56, "or ", 0, "or ".Length, StringComparison.Ordinal) != 0)
|
||||
{
|
||||
var item = new[]
|
||||
{
|
||||
using (var stream = files[i].Open(FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
|
||||
using (var reader = new StreamReader(stream, Encoding.UTF8))
|
||||
{
|
||||
var line = string.Empty;
|
||||
while ((line = reader.ReadLine()) != null)
|
||||
{
|
||||
if (line.Length > 32 &&
|
||||
line[31] == '-')
|
||||
{
|
||||
ParseLine(line);
|
||||
}
|
||||
}
|
||||
}
|
||||
ConvertLogTimeToISO8601(s),
|
||||
"Location",
|
||||
s.Substring(56)
|
||||
};
|
||||
m_Lock.EnterWriteLock();
|
||||
try
|
||||
{
|
||||
m_GameLog.Add(item);
|
||||
}
|
||||
finally
|
||||
{
|
||||
m_Lock.ExitWriteLock();
|
||||
}
|
||||
}
|
||||
using (var stream = files[0].Open(FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
|
||||
using (var reader = new StreamReader(stream, Encoding.UTF8))
|
||||
}
|
||||
else if (c == 'N')
|
||||
{
|
||||
// 2019.07.31 22:41:18 Log - [NetworkManager] OnPlayerJoined pypy
|
||||
if (s.Length > 66 &&
|
||||
string.Compare(s, 34, "[NetworkManager] OnPlayerJoined ", 0, "[NetworkManager] OnPlayerJoined ".Length, StringComparison.Ordinal) == 0)
|
||||
{
|
||||
var line = reader.ReadLine();
|
||||
if (line != null)
|
||||
var item = new[]
|
||||
{
|
||||
if (string.Equals(firstLine, line))
|
||||
{
|
||||
stream.Position = lastPosition;
|
||||
}
|
||||
else
|
||||
{
|
||||
firstLine = line;
|
||||
}
|
||||
do
|
||||
{
|
||||
lastPosition = stream.Position;
|
||||
ParseLine(line);
|
||||
}
|
||||
while ((line = reader.ReadLine()) != null);
|
||||
ConvertLogTimeToISO8601(s),
|
||||
"OnPlayerJoined",
|
||||
s.Substring(66)
|
||||
};
|
||||
m_Lock.EnterWriteLock();
|
||||
try
|
||||
{
|
||||
m_GameLog.Add(item);
|
||||
}
|
||||
finally
|
||||
{
|
||||
m_Lock.ExitWriteLock();
|
||||
}
|
||||
}
|
||||
// 2019.07.31 22:29:31 Log - [NetworkManager] OnPlayerLeft pypy
|
||||
else if (s.Length > 64 &&
|
||||
string.Compare(s, 34, "[NetworkManager] OnPlayerLeft ", 0, "[NetworkManager] OnPlayerLeft ".Length, StringComparison.Ordinal) == 0)
|
||||
{
|
||||
var item = new[]
|
||||
{
|
||||
ConvertLogTimeToISO8601(s),
|
||||
"OnPlayerLeft",
|
||||
s.Substring(64)
|
||||
};
|
||||
m_Lock.EnterWriteLock();
|
||||
try
|
||||
{
|
||||
m_GameLog.Add(item);
|
||||
}
|
||||
finally
|
||||
{
|
||||
m_Lock.ExitWriteLock();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
try
|
||||
{
|
||||
Thread.Sleep(3000);
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ThreadInterruptedException
|
||||
}
|
||||
}
|
||||
});
|
||||
m_Thread.Start();
|
||||
}
|
||||
}
|
||||
|
||||
public static void Stop()
|
||||
{
|
||||
var thread = m_Thread;
|
||||
if (thread != null)
|
||||
{
|
||||
m_Thread = null;
|
||||
try
|
||||
{
|
||||
thread.Interrupt();
|
||||
thread.Join();
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static string ConvertLogTimeToISO8601(string s)
|
||||
{
|
||||
// 2019.07.31 22:26:24
|
||||
var dt = new DateTime(
|
||||
int.Parse(s.Substring(0, 4)),
|
||||
int.Parse(s.Substring(5, 2)),
|
||||
int.Parse(s.Substring(8, 2)),
|
||||
int.Parse(s.Substring(11, 2)),
|
||||
int.Parse(s.Substring(14, 2)),
|
||||
int.Parse(s.Substring(17, 2)),
|
||||
DateTimeKind.Local
|
||||
).ToUniversalTime();
|
||||
return $"{dt:yyyy'-'MM'-'dd'T'HH':'mm':'ss'Z'}";
|
||||
}
|
||||
|
||||
private static void ParseLine(string line)
|
||||
{
|
||||
try
|
||||
{
|
||||
// 2019.07.31 22:26:24 Log - [RoomManager] Joining wrld_4432ea9b-729c-46e3-8eaf-846aa0a37fdd:6974~private(usr_4f76a584-9d4b-46f6-8209-8305eb683661)~nonce(0000000000000000000000000000000000000000000000000000000000000000)
|
||||
// 2019.07.31 22:26:24 Log - [RoomManager] Joining or Creating Room: VRChat Home
|
||||
if (string.Compare(line, 34, "[RoomManager] Joining ", 0, "[RoomManager] Joining ".Length) == 0 &&
|
||||
string.Compare(line, 56, "or ", 0, "or ".Length) != 0)
|
||||
{
|
||||
lock (m_GameLog)
|
||||
{
|
||||
m_GameLog.Add(new[]
|
||||
{
|
||||
ConvertLogTimeToISO8601(line),
|
||||
"Location",
|
||||
line.Substring(56)
|
||||
});
|
||||
}
|
||||
}
|
||||
// 2019.07.31 22:41:18 Log - [NetworkManager] OnPlayerJoined pypy
|
||||
else if (string.Compare(line, 34, "[NetworkManager] OnPlayerJoined ", 0, "[NetworkManager] OnPlayerJoined ".Length) == 0)
|
||||
{
|
||||
lock (m_GameLog)
|
||||
{
|
||||
m_GameLog.Add(new[]
|
||||
{
|
||||
ConvertLogTimeToISO8601(line),
|
||||
"OnPlayerJoined",
|
||||
line.Substring(66)
|
||||
});
|
||||
}
|
||||
}
|
||||
// 2019.07.31 22:29:31 Log - [NetworkManager] OnPlayerLeft pypy
|
||||
else if (string.Compare(line, 34, "[NetworkManager] OnPlayerLeft ", 0, "[NetworkManager] OnPlayerLeft ".Length) == 0)
|
||||
{
|
||||
lock (m_GameLog)
|
||||
{
|
||||
m_GameLog.Add(new[]
|
||||
{
|
||||
ConvertLogTimeToISO8601(line),
|
||||
"OnPlayerLeft",
|
||||
line.Substring(64)
|
||||
});
|
||||
}
|
||||
position = stream.Position;
|
||||
}
|
||||
}
|
||||
catch
|
||||
@@ -182,31 +200,54 @@ namespace VRCX
|
||||
}
|
||||
}
|
||||
|
||||
private static string ConvertLogTimeToISO8601(string s)
|
||||
{
|
||||
// 2019.07.31 22:26:24
|
||||
if (!DateTime.TryParseExact(s.Substring(0, 19),
|
||||
"yyyy.MM.dd HH:mm:ss",
|
||||
CultureInfo.InvariantCulture,
|
||||
DateTimeStyles.AdjustToUniversal | DateTimeStyles.AssumeLocal,
|
||||
out DateTime dt))
|
||||
{
|
||||
dt = DateTime.UtcNow;
|
||||
}
|
||||
return $"{dt:yyyy'-'MM'-'dd'T'HH':'mm':'ss'Z'}";
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
lock (m_GameLog)
|
||||
if (m_Thread != null)
|
||||
{
|
||||
m_Reset = true;
|
||||
m_GameLog.Clear();
|
||||
m_Thread.Interrupt();
|
||||
}
|
||||
m_Thread.Interrupt();
|
||||
}
|
||||
|
||||
public string[][] GetLogs()
|
||||
public string[][] Get()
|
||||
{
|
||||
lock (m_GameLog)
|
||||
m_Lock.EnterUpgradeableReadLock();
|
||||
try
|
||||
{
|
||||
if (m_Reset ||
|
||||
m_GameLog.Count == 0)
|
||||
{
|
||||
return new string[][] { };
|
||||
}
|
||||
var array = m_GameLog.ToArray();
|
||||
m_GameLog.Clear();
|
||||
m_Lock.EnterWriteLock();
|
||||
try
|
||||
{
|
||||
m_GameLog.Clear();
|
||||
}
|
||||
finally
|
||||
{
|
||||
m_Lock.ExitWriteLock();
|
||||
}
|
||||
return array;
|
||||
}
|
||||
}
|
||||
|
||||
public bool HasLog()
|
||||
{
|
||||
lock (m_GameLog)
|
||||
finally
|
||||
{
|
||||
return m_GameLog.Count > 0;
|
||||
m_Lock.ExitUpgradeableReadLock();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
10
MainForm.Designer.cs
generated
10
MainForm.Designer.cs
generated
@@ -33,16 +33,8 @@ namespace VRCX
|
||||
/// </summary>
|
||||
private void InitializeComponent()
|
||||
{
|
||||
this.components = new System.ComponentModel.Container();
|
||||
this.timer = new System.Windows.Forms.Timer(this.components);
|
||||
this.SuspendLayout();
|
||||
//
|
||||
// timer
|
||||
//
|
||||
this.timer.Enabled = true;
|
||||
this.timer.Interval = 1000;
|
||||
this.timer.Tick += new System.EventHandler(this.timer_Tick);
|
||||
//
|
||||
// MainForm
|
||||
//
|
||||
this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 12F);
|
||||
@@ -56,7 +48,5 @@ namespace VRCX
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private System.Windows.Forms.Timer timer;
|
||||
}
|
||||
}
|
||||
@@ -43,7 +43,8 @@ namespace VRCX
|
||||
};
|
||||
Browser.JavascriptObjectRepository.Register("VRCX", new VRCX(), true, options);
|
||||
Browser.JavascriptObjectRepository.Register("VRCXStorage", new VRCXStorage(), false, options);
|
||||
Browser.JavascriptObjectRepository.Register("LogWatcher", new VRCX_LogWatcher(), true, options);
|
||||
Browser.JavascriptObjectRepository.Register("SQLite", new SQLite(), true, options);
|
||||
Browser.JavascriptObjectRepository.Register("LogWatcher", new LogWatcher(), true, options);
|
||||
Browser.JavascriptObjectRepository.Register("Discord", new Discord(), true, options);
|
||||
Browser.IsBrowserInitializedChanged += (A, B) =>
|
||||
{
|
||||
@@ -51,10 +52,5 @@ namespace VRCX
|
||||
};
|
||||
Controls.Add(Browser);
|
||||
}
|
||||
|
||||
private void timer_Tick(object sender, System.EventArgs e)
|
||||
{
|
||||
Discord.Update();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -117,7 +117,4 @@
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<metadata name="timer.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
|
||||
<value>17, 17</value>
|
||||
</metadata>
|
||||
</root>
|
||||
16
Program.cs
16
Program.cs
@@ -53,15 +53,19 @@ namespace VRCX
|
||||
{
|
||||
Application.EnableVisualStyles();
|
||||
Application.SetCompatibleTextRenderingDefault(false);
|
||||
CpuMonitor.Start();
|
||||
VRCXStorage.Load();
|
||||
VRCXVR.Setup();
|
||||
VRCX_LogWatcher.Start();
|
||||
SQLite.Init();
|
||||
CpuMonitor.Init();
|
||||
Discord.Init();
|
||||
LogWatcher.Init();
|
||||
VRCXVR.Init();
|
||||
Application.Run(new MainForm());
|
||||
VRCX_LogWatcher.Stop();
|
||||
VRCXVR.Stop();
|
||||
VRCXVR.Exit();
|
||||
LogWatcher.Exit();
|
||||
Discord.Exit();
|
||||
CpuMonitor.Exit();
|
||||
SQLite.Exit();
|
||||
VRCXStorage.Save();
|
||||
CpuMonitor.Stop();
|
||||
Cef.Shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Threading;
|
||||
using CefSharp;
|
||||
using CefSharp.Enums;
|
||||
using CefSharp.OffScreen;
|
||||
@@ -15,26 +16,32 @@ namespace VRCX
|
||||
public class RenderHandler : IRenderHandler
|
||||
{
|
||||
private ChromiumWebBrowser m_Browser;
|
||||
public readonly object BufferLock = new object();
|
||||
private ReaderWriterLockSlim m_Lock;
|
||||
public int BufferSize { get; private set; }
|
||||
public GCHandle Buffer { get; private set; }
|
||||
public int Width { get; private set; }
|
||||
public int Height { get; private set; }
|
||||
|
||||
public RenderHandler(ChromiumWebBrowser browser)
|
||||
public RenderHandler(ChromiumWebBrowser browser, ReaderWriterLockSlim @lock)
|
||||
{
|
||||
m_Browser = browser;
|
||||
m_Lock = @lock;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
lock (BufferLock)
|
||||
m_Lock.EnterWriteLock();
|
||||
try
|
||||
{
|
||||
if (Buffer.IsAllocated)
|
||||
{
|
||||
Buffer.Free();
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
m_Lock.ExitWriteLock();
|
||||
}
|
||||
m_Browser = null;
|
||||
}
|
||||
|
||||
@@ -64,23 +71,36 @@ namespace VRCX
|
||||
{
|
||||
if (type == PaintElementType.View)
|
||||
{
|
||||
lock (BufferLock)
|
||||
m_Lock.EnterUpgradeableReadLock();
|
||||
try
|
||||
{
|
||||
if (!Buffer.IsAllocated ||
|
||||
width != Width ||
|
||||
height != Height)
|
||||
{
|
||||
Width = width;
|
||||
Height = height;
|
||||
BufferSize = width * height * 4;
|
||||
if (Buffer.IsAllocated)
|
||||
m_Lock.EnterWriteLock();
|
||||
try
|
||||
{
|
||||
Buffer.Free();
|
||||
Width = width;
|
||||
Height = height;
|
||||
BufferSize = width * height * 4;
|
||||
if (Buffer.IsAllocated)
|
||||
{
|
||||
Buffer.Free();
|
||||
}
|
||||
Buffer = GCHandle.Alloc(new byte[BufferSize], GCHandleType.Pinned);
|
||||
}
|
||||
finally
|
||||
{
|
||||
m_Lock.ExitWriteLock();
|
||||
}
|
||||
Buffer = GCHandle.Alloc(new byte[BufferSize], GCHandleType.Pinned);
|
||||
}
|
||||
WinApi.CopyMemory(Buffer.AddrOfPinnedObject(), buffer, (uint)BufferSize);
|
||||
}
|
||||
finally
|
||||
{
|
||||
m_Lock.ExitUpgradeableReadLock();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
91
SQLite.cs
Normal file
91
SQLite.cs
Normal file
@@ -0,0 +1,91 @@
|
||||
using CefSharp;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.Data.SQLite;
|
||||
using System.Threading;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace VRCX
|
||||
{
|
||||
public class SQLite
|
||||
{
|
||||
private static readonly ReaderWriterLockSlim m_Lock = new ReaderWriterLockSlim();
|
||||
private static SQLiteConnection m_Connection;
|
||||
|
||||
public static void Init()
|
||||
{
|
||||
m_Connection = new SQLiteConnection($"Data Source={Application.StartupPath}/VRCX.sqlite;Version=3");
|
||||
m_Connection.Open();
|
||||
}
|
||||
|
||||
public static void Exit()
|
||||
{
|
||||
m_Connection.Close();
|
||||
m_Connection.Dispose();
|
||||
}
|
||||
|
||||
public void Execute(string sql, IDictionary<string, object> param = null)
|
||||
{
|
||||
m_Lock.EnterWriteLock();
|
||||
try
|
||||
{
|
||||
if (m_Connection.State != ConnectionState.Open)
|
||||
{
|
||||
m_Connection.Close();
|
||||
m_Connection.Open();
|
||||
}
|
||||
using (var C = new SQLiteCommand(sql, m_Connection))
|
||||
{
|
||||
if (param != null)
|
||||
{
|
||||
foreach (var prop in param)
|
||||
{
|
||||
C.Parameters.Add(new SQLiteParameter("@" + prop.Key, prop.Value));
|
||||
}
|
||||
}
|
||||
C.ExecuteNonQuery();
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
m_Lock.ExitWriteLock();
|
||||
}
|
||||
}
|
||||
|
||||
public void ExecuteQuery(IJavascriptCallback callback, string sql, IDictionary<string, object> param = null)
|
||||
{
|
||||
m_Lock.EnterReadLock();
|
||||
try
|
||||
{
|
||||
if (m_Connection.State != ConnectionState.Open)
|
||||
{
|
||||
m_Connection.Close();
|
||||
m_Connection.Open();
|
||||
}
|
||||
using (var C = new SQLiteCommand(sql, m_Connection))
|
||||
{
|
||||
if (param != null)
|
||||
{
|
||||
foreach (var prop in param)
|
||||
{
|
||||
C.Parameters.Add(new SQLiteParameter("@" + prop.Key, prop.Value));
|
||||
}
|
||||
}
|
||||
using (var R = C.ExecuteReader())
|
||||
{
|
||||
while (R.Read())
|
||||
{
|
||||
var row = new object[R.FieldCount];
|
||||
R.GetValues(row);
|
||||
callback.ExecuteAsync(row);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
m_Lock.ExitReadLock();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
4
VRCX.cs
4
VRCX.cs
@@ -69,12 +69,12 @@ namespace VRCX
|
||||
|
||||
public void StartVR()
|
||||
{
|
||||
VRCXVR.Start();
|
||||
VRCXVR.SetActive(true);
|
||||
}
|
||||
|
||||
public void StopVR()
|
||||
{
|
||||
VRCXVR.Stop();
|
||||
VRCXVR.SetActive(false);
|
||||
}
|
||||
|
||||
public void RefreshVR()
|
||||
|
||||
@@ -110,6 +110,9 @@
|
||||
</Reference>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Core" />
|
||||
<Reference Include="System.Data.SQLite, Version=1.0.111.0, Culture=neutral, PublicKeyToken=db937bc2d44ff139, processorArchitecture=MSIL">
|
||||
<HintPath>packages\System.Data.SQLite.Core.1.0.111.0\lib\net451\System.Data.SQLite.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Xml.Linq" />
|
||||
<Reference Include="System.Data.DataSetExtensions" />
|
||||
<Reference Include="Microsoft.CSharp" />
|
||||
@@ -125,6 +128,7 @@
|
||||
<Compile Include="CpuMonitor.cs" />
|
||||
<Compile Include="Browser.cs" />
|
||||
<Compile Include="RenderHandler.cs" />
|
||||
<Compile Include="SQLite.cs" />
|
||||
<Compile Include="VRForm.cs">
|
||||
<SubType>Form</SubType>
|
||||
</Compile>
|
||||
@@ -204,8 +208,10 @@
|
||||
<Error Condition="!Exists('packages\CefSharp.WinForms.73.1.130\build\CefSharp.WinForms.targets')" Text="$([System.String]::Format('$(ErrorText)', 'packages\CefSharp.WinForms.73.1.130\build\CefSharp.WinForms.targets'))" />
|
||||
<Error Condition="!Exists('packages\CefSharp.OffScreen.73.1.130\build\CefSharp.OffScreen.props')" Text="$([System.String]::Format('$(ErrorText)', 'packages\CefSharp.OffScreen.73.1.130\build\CefSharp.OffScreen.props'))" />
|
||||
<Error Condition="!Exists('packages\CefSharp.OffScreen.73.1.130\build\CefSharp.OffScreen.targets')" Text="$([System.String]::Format('$(ErrorText)', 'packages\CefSharp.OffScreen.73.1.130\build\CefSharp.OffScreen.targets'))" />
|
||||
<Error Condition="!Exists('packages\System.Data.SQLite.Core.1.0.111.0\build\net451\System.Data.SQLite.Core.targets')" Text="$([System.String]::Format('$(ErrorText)', 'packages\System.Data.SQLite.Core.1.0.111.0\build\net451\System.Data.SQLite.Core.targets'))" />
|
||||
</Target>
|
||||
<Import Project="packages\CefSharp.Common.73.1.130\build\CefSharp.Common.targets" Condition="Exists('packages\CefSharp.Common.73.1.130\build\CefSharp.Common.targets')" />
|
||||
<Import Project="packages\CefSharp.WinForms.73.1.130\build\CefSharp.WinForms.targets" Condition="Exists('packages\CefSharp.WinForms.73.1.130\build\CefSharp.WinForms.targets')" />
|
||||
<Import Project="packages\CefSharp.OffScreen.73.1.130\build\CefSharp.OffScreen.targets" Condition="Exists('packages\CefSharp.OffScreen.73.1.130\build\CefSharp.OffScreen.targets')" />
|
||||
<Import Project="packages\System.Data.SQLite.Core.1.0.111.0\build\net451\System.Data.SQLite.Core.targets" Condition="Exists('packages\System.Data.SQLite.Core.1.0.111.0\build\net451\System.Data.SQLite.Core.targets')" />
|
||||
</Project>
|
||||
@@ -4,71 +4,114 @@
|
||||
// For a copy, see <https://opensource.org/licenses/MIT>.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace VRCX
|
||||
{
|
||||
public class VRCXStorage
|
||||
{
|
||||
private static readonly ReaderWriterLockSlim m_Lock = new ReaderWriterLockSlim();
|
||||
private static Dictionary<string, string> m_Storage = new Dictionary<string, string>();
|
||||
private static bool m_Dirty;
|
||||
|
||||
public static void Load()
|
||||
{
|
||||
JsonSerializer.Deserialize(Application.StartupPath + "/VRCX.json", ref m_Storage);
|
||||
m_Lock.EnterWriteLock();
|
||||
try
|
||||
{
|
||||
JsonSerializer.Deserialize(Application.StartupPath + "/VRCX.json", ref m_Storage);
|
||||
m_Dirty = false;
|
||||
}
|
||||
finally
|
||||
{
|
||||
m_Lock.ExitWriteLock();
|
||||
}
|
||||
}
|
||||
|
||||
public static void Save()
|
||||
{
|
||||
JsonSerializer.Serialize(Application.StartupPath + "/VRCX.json", m_Storage);
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
lock (m_Storage)
|
||||
m_Lock.EnterReadLock();
|
||||
try
|
||||
{
|
||||
m_Dirty = true;
|
||||
m_Storage.Clear();
|
||||
if (m_Dirty)
|
||||
{
|
||||
JsonSerializer.Serialize(Application.StartupPath + "/VRCX.json", m_Storage);
|
||||
m_Dirty = false;
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
m_Lock.ExitReadLock();
|
||||
}
|
||||
}
|
||||
|
||||
public void Flush()
|
||||
{
|
||||
lock (m_Storage)
|
||||
Save();
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
m_Lock.EnterWriteLock();
|
||||
try
|
||||
{
|
||||
if (m_Dirty)
|
||||
if (m_Storage.Count > 0)
|
||||
{
|
||||
m_Dirty = false;
|
||||
Save();
|
||||
m_Dirty = true;
|
||||
}
|
||||
m_Storage.Clear();
|
||||
}
|
||||
finally
|
||||
{
|
||||
m_Lock.ExitWriteLock();
|
||||
}
|
||||
}
|
||||
|
||||
public bool Remove(string key)
|
||||
{
|
||||
lock (m_Storage)
|
||||
m_Lock.EnterWriteLock();
|
||||
try
|
||||
{
|
||||
m_Dirty = true;
|
||||
return m_Storage.Remove(key);
|
||||
var result = m_Storage.Remove(key);
|
||||
if (result)
|
||||
{
|
||||
m_Dirty = true;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
finally
|
||||
{
|
||||
m_Lock.ExitWriteLock();
|
||||
}
|
||||
}
|
||||
|
||||
public string Get(string key)
|
||||
{
|
||||
lock (m_Storage)
|
||||
m_Lock.EnterReadLock();
|
||||
try
|
||||
{
|
||||
return m_Storage.TryGetValue(key, out string value)
|
||||
? value
|
||||
: string.Empty;
|
||||
}
|
||||
finally
|
||||
{
|
||||
m_Lock.ExitReadLock();
|
||||
}
|
||||
}
|
||||
|
||||
public void Set(string key, string value)
|
||||
{
|
||||
lock (m_Storage)
|
||||
m_Lock.EnterWriteLock();
|
||||
try
|
||||
{
|
||||
m_Dirty = true;
|
||||
m_Storage[key] = value;
|
||||
m_Dirty = true;
|
||||
}
|
||||
finally
|
||||
{
|
||||
m_Lock.ExitWriteLock();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
415
VRCXVR.cs
415
VRCXVR.cs
@@ -21,7 +21,7 @@ namespace VRCX
|
||||
{
|
||||
public static class VRCXVR
|
||||
{
|
||||
private static readonly object m_LockObject = new object();
|
||||
private static readonly ReaderWriterLockSlim m_Lock = new ReaderWriterLockSlim();
|
||||
private static List<string[]> m_Devices = new List<string[]>();
|
||||
private static Thread m_Thread;
|
||||
private static Device m_Device;
|
||||
@@ -29,6 +29,7 @@ namespace VRCX
|
||||
private static Texture2D m_Texture2;
|
||||
private static Browser m_Browser1;
|
||||
private static Browser m_Browser2;
|
||||
private static bool m_Active;
|
||||
private static float[] m_Rotation = { 0f, 0f, 0f };
|
||||
private static float[] m_Translation = { 0f, 0f, 0f };
|
||||
private static float[] m_L_Translation = { -7f / 100f, -5f / 100f, 6f / 100f };
|
||||
@@ -38,7 +39,7 @@ namespace VRCX
|
||||
|
||||
// NOTE
|
||||
// 메모리 릭 때문에 미리 생성해놓고 계속 사용함
|
||||
public static void Setup()
|
||||
public static void Init()
|
||||
{
|
||||
m_Device = new Device(DriverType.Hardware, DeviceCreationFlags.SingleThreaded | DeviceCreationFlags.BgraSupport);
|
||||
m_Texture1 = new Texture2D(m_Device, new Texture2DDescription()
|
||||
@@ -67,38 +68,139 @@ namespace VRCX
|
||||
});
|
||||
m_Browser1 = new Browser(m_Texture1, Application.StartupPath + "/html/vr.html?1");
|
||||
m_Browser2 = new Browser(m_Texture2, Application.StartupPath + "/html/vr.html?2");
|
||||
}
|
||||
|
||||
public static void Start()
|
||||
{
|
||||
lock (m_LockObject)
|
||||
m_Thread = new Thread(() =>
|
||||
{
|
||||
if (m_Thread == null)
|
||||
var active = false;
|
||||
var e = new VREvent_t();
|
||||
var nextInit = DateTime.MinValue;
|
||||
var nextDeviceUpdate = DateTime.MinValue;
|
||||
var nextOverlay = DateTime.MinValue;
|
||||
var overlayIndex = OpenVR.k_unTrackedDeviceIndexInvalid;
|
||||
var overlayVisible1 = false;
|
||||
var overlayVisible2 = false;
|
||||
var dashboardHandle = 0UL;
|
||||
var overlayHandle1 = 0UL;
|
||||
var overlayHandle2 = 0UL;
|
||||
while (m_Thread != null)
|
||||
{
|
||||
m_Thread = new Thread(ThreadProc);
|
||||
m_Thread.Start();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void Stop()
|
||||
{
|
||||
lock (m_LockObject)
|
||||
{
|
||||
var thread = m_Thread;
|
||||
if (thread != null)
|
||||
{
|
||||
m_Thread = null;
|
||||
try
|
||||
{
|
||||
thread.Interrupt();
|
||||
thread.Join();
|
||||
Thread.Sleep(10);
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ThreadInterruptedException
|
||||
}
|
||||
if (m_Active)
|
||||
{
|
||||
m_Browser1.Render();
|
||||
m_Browser2.Render();
|
||||
var system = OpenVR.System;
|
||||
if (system == null)
|
||||
{
|
||||
if (DateTime.Now.CompareTo(nextInit) <= 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
var _err = EVRInitError.None;
|
||||
system = OpenVR.Init(ref _err, EVRApplicationType.VRApplication_Overlay);
|
||||
nextInit = DateTime.Now.AddSeconds(5);
|
||||
if (system == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
active = true;
|
||||
}
|
||||
while (system.PollNextEvent(ref e, (uint)Marshal.SizeOf(e)))
|
||||
{
|
||||
var type = (EVREventType)e.eventType;
|
||||
if (type == EVREventType.VREvent_Quit)
|
||||
{
|
||||
active = false;
|
||||
OpenVR.Shutdown();
|
||||
nextInit = DateTime.Now.AddSeconds(10);
|
||||
system = null;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (system != null)
|
||||
{
|
||||
if (DateTime.Now.CompareTo(nextDeviceUpdate) >= 0)
|
||||
{
|
||||
overlayIndex = OpenVR.k_unTrackedDeviceIndexInvalid;
|
||||
UpdateDevices(system, ref overlayIndex);
|
||||
if (overlayIndex != OpenVR.k_unTrackedDeviceIndexInvalid)
|
||||
{
|
||||
nextOverlay = DateTime.Now.AddSeconds(10);
|
||||
}
|
||||
nextDeviceUpdate = DateTime.Now.AddSeconds(0.1);
|
||||
}
|
||||
var overlay = OpenVR.Overlay;
|
||||
if (overlay != null)
|
||||
{
|
||||
var dashboardVisible = overlay.IsDashboardVisible();
|
||||
var err = ProcessDashboard(overlay, ref dashboardHandle, dashboardVisible);
|
||||
if (err != EVROverlayError.None &&
|
||||
dashboardHandle != 0)
|
||||
{
|
||||
overlay.DestroyOverlay(dashboardHandle);
|
||||
dashboardHandle = 0;
|
||||
}
|
||||
err = ProcessOverlay1(overlay, ref overlayHandle1, ref overlayVisible1, dashboardVisible, overlayIndex, nextOverlay);
|
||||
if (err != EVROverlayError.None &&
|
||||
overlayHandle1 != 0)
|
||||
{
|
||||
overlay.DestroyOverlay(overlayHandle1);
|
||||
overlayHandle1 = 0;
|
||||
}
|
||||
err = ProcessOverlay2(overlay, ref overlayHandle2, ref overlayVisible2, dashboardVisible);
|
||||
if (err != EVROverlayError.None &&
|
||||
overlayHandle2 != 0)
|
||||
{
|
||||
overlay.DestroyOverlay(overlayHandle2);
|
||||
overlayHandle2 = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (active)
|
||||
{
|
||||
active = false;
|
||||
OpenVR.Shutdown();
|
||||
m_Lock.EnterWriteLock();
|
||||
try
|
||||
{
|
||||
m_Devices.Clear();
|
||||
}
|
||||
finally
|
||||
{
|
||||
m_Lock.ExitWriteLock();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
{
|
||||
IsBackground = true
|
||||
};
|
||||
m_Thread.Start();
|
||||
}
|
||||
|
||||
public static void Exit()
|
||||
{
|
||||
var T = m_Thread;
|
||||
m_Thread = null;
|
||||
T.Interrupt();
|
||||
T.Join();
|
||||
m_Browser2.Dispose();
|
||||
m_Browser1.Dispose();
|
||||
m_Texture2.Dispose();
|
||||
m_Texture1.Dispose();
|
||||
m_Device.Dispose();
|
||||
}
|
||||
|
||||
public static void SetActive(bool active)
|
||||
{
|
||||
m_Active = active;
|
||||
}
|
||||
|
||||
public static void Refresh()
|
||||
@@ -109,86 +211,105 @@ namespace VRCX
|
||||
|
||||
public static string[][] GetDevices()
|
||||
{
|
||||
lock (m_Devices)
|
||||
m_Lock.EnterReadLock();
|
||||
try
|
||||
{
|
||||
return m_Devices.ToArray();
|
||||
}
|
||||
finally
|
||||
{
|
||||
m_Lock.ExitReadLock();
|
||||
}
|
||||
}
|
||||
|
||||
private static void UpdateDevices(CVRSystem system, ref uint trackingIndex)
|
||||
private static void UpdateDevices(CVRSystem system, ref uint overlayIndex)
|
||||
{
|
||||
lock (m_Devices)
|
||||
m_Lock.EnterWriteLock();
|
||||
try
|
||||
{
|
||||
m_Devices.Clear();
|
||||
var sb = new StringBuilder(256);
|
||||
var state = new VRControllerState_t();
|
||||
for (var i = 0u; i < OpenVR.k_unMaxTrackedDeviceCount; ++i)
|
||||
}
|
||||
finally
|
||||
{
|
||||
m_Lock.ExitWriteLock();
|
||||
}
|
||||
var sb = new StringBuilder(256);
|
||||
var state = new VRControllerState_t();
|
||||
for (var i = 0u; i < OpenVR.k_unMaxTrackedDeviceCount; ++i)
|
||||
{
|
||||
var devClass = system.GetTrackedDeviceClass(i);
|
||||
if (devClass == ETrackedDeviceClass.Controller ||
|
||||
devClass == ETrackedDeviceClass.GenericTracker)
|
||||
{
|
||||
var devClass = system.GetTrackedDeviceClass(i);
|
||||
if (devClass == ETrackedDeviceClass.Controller ||
|
||||
devClass == ETrackedDeviceClass.GenericTracker)
|
||||
var err = ETrackedPropertyError.TrackedProp_Success;
|
||||
var batteryPercentage = system.GetFloatTrackedDeviceProperty(i, ETrackedDeviceProperty.Prop_DeviceBatteryPercentage_Float, ref err);
|
||||
if (err != ETrackedPropertyError.TrackedProp_Success)
|
||||
{
|
||||
var err = ETrackedPropertyError.TrackedProp_Success;
|
||||
var batteryPercentage = system.GetFloatTrackedDeviceProperty(i, ETrackedDeviceProperty.Prop_DeviceBatteryPercentage_Float, ref err);
|
||||
if (err != ETrackedPropertyError.TrackedProp_Success)
|
||||
{
|
||||
batteryPercentage = 1f;
|
||||
}
|
||||
sb.Clear();
|
||||
system.GetStringTrackedDeviceProperty(i, ETrackedDeviceProperty.Prop_TrackingSystemName_String, sb, (uint)sb.Capacity, ref err);
|
||||
var isOculus = sb.ToString().IndexOf("oculus", StringComparison.OrdinalIgnoreCase) >= 0;
|
||||
// Oculus : B/Y, Bit 1, Mask 2
|
||||
// Oculus : A/X, Bit 7, Mask 128
|
||||
// Vive : Menu, Bit 1, Mask 2,
|
||||
// Vive : Grip, Bit 2, Mask 4
|
||||
var role = system.GetControllerRoleForTrackedDeviceIndex(i);
|
||||
if (role == ETrackedControllerRole.LeftHand ||
|
||||
role == ETrackedControllerRole.RightHand)
|
||||
{
|
||||
if (system.GetControllerState(i, ref state, (uint)Marshal.SizeOf(state)) &&
|
||||
(state.ulButtonPressed & (isOculus ? 2u : 4u)) != 0)
|
||||
{
|
||||
if (role == ETrackedControllerRole.LeftHand)
|
||||
{
|
||||
Array.Copy(m_L_Translation, m_Translation, 3);
|
||||
Array.Copy(m_L_Rotation, m_Rotation, 3);
|
||||
}
|
||||
else
|
||||
{
|
||||
Array.Copy(m_R_Translation, m_Translation, 3);
|
||||
Array.Copy(m_R_Rotation, m_Rotation, 3);
|
||||
}
|
||||
trackingIndex = i;
|
||||
}
|
||||
}
|
||||
var type = string.Empty;
|
||||
if (devClass == ETrackedDeviceClass.Controller)
|
||||
batteryPercentage = 1f;
|
||||
}
|
||||
sb.Clear();
|
||||
system.GetStringTrackedDeviceProperty(i, ETrackedDeviceProperty.Prop_TrackingSystemName_String, sb, (uint)sb.Capacity, ref err);
|
||||
var isOculus = sb.ToString().IndexOf("oculus", StringComparison.OrdinalIgnoreCase) >= 0;
|
||||
// Oculus : B/Y, Bit 1, Mask 2
|
||||
// Oculus : A/X, Bit 7, Mask 128
|
||||
// Vive : Menu, Bit 1, Mask 2,
|
||||
// Vive : Grip, Bit 2, Mask 4
|
||||
var role = system.GetControllerRoleForTrackedDeviceIndex(i);
|
||||
if (role == ETrackedControllerRole.LeftHand ||
|
||||
role == ETrackedControllerRole.RightHand)
|
||||
{
|
||||
if (system.GetControllerState(i, ref state, (uint)Marshal.SizeOf(state)) &&
|
||||
(state.ulButtonPressed & (isOculus ? 2u : 4u)) != 0)
|
||||
{
|
||||
if (role == ETrackedControllerRole.LeftHand)
|
||||
{
|
||||
type = "leftController";
|
||||
}
|
||||
else if (role == ETrackedControllerRole.RightHand)
|
||||
{
|
||||
type = "rightController";
|
||||
Array.Copy(m_L_Translation, m_Translation, 3);
|
||||
Array.Copy(m_L_Rotation, m_Rotation, 3);
|
||||
}
|
||||
else
|
||||
{
|
||||
type = "controller";
|
||||
Array.Copy(m_R_Translation, m_Translation, 3);
|
||||
Array.Copy(m_R_Rotation, m_Rotation, 3);
|
||||
}
|
||||
overlayIndex = i;
|
||||
}
|
||||
else if (devClass == ETrackedDeviceClass.GenericTracker)
|
||||
}
|
||||
var type = string.Empty;
|
||||
if (devClass == ETrackedDeviceClass.Controller)
|
||||
{
|
||||
if (role == ETrackedControllerRole.LeftHand)
|
||||
{
|
||||
type = "tracker";
|
||||
type = "leftController";
|
||||
}
|
||||
m_Devices.Add(new[]
|
||||
else if (role == ETrackedControllerRole.RightHand)
|
||||
{
|
||||
type,
|
||||
system.IsTrackedDeviceConnected(i)
|
||||
? "connected"
|
||||
: "disconnected",
|
||||
(batteryPercentage * 100).ToString()
|
||||
});
|
||||
type = "rightController";
|
||||
}
|
||||
else
|
||||
{
|
||||
type = "controller";
|
||||
}
|
||||
}
|
||||
else if (devClass == ETrackedDeviceClass.GenericTracker)
|
||||
{
|
||||
type = "tracker";
|
||||
}
|
||||
var item = new[]
|
||||
{
|
||||
type,
|
||||
system.IsTrackedDeviceConnected(i)
|
||||
? "connected"
|
||||
: "disconnected",
|
||||
(batteryPercentage * 100).ToString()
|
||||
};
|
||||
m_Lock.EnterWriteLock();
|
||||
try
|
||||
{
|
||||
m_Devices.Add(item);
|
||||
}
|
||||
finally
|
||||
{
|
||||
m_Lock.ExitWriteLock();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -267,7 +388,7 @@ namespace VRCX
|
||||
return err;
|
||||
}
|
||||
|
||||
private static EVROverlayError ProcessOverlay1(CVROverlay overlay, ref ulong overlayHandle, ref bool overlayVisible, bool dashboardVisible, uint trackingIndex, DateTime nextRender)
|
||||
private static EVROverlayError ProcessOverlay1(CVROverlay overlay, ref ulong overlayHandle, ref bool overlayVisible, bool dashboardVisible, uint overlayIndex, DateTime nextOverlay)
|
||||
{
|
||||
var err = EVROverlayError.None;
|
||||
|
||||
@@ -304,7 +425,7 @@ namespace VRCX
|
||||
}
|
||||
}
|
||||
|
||||
if (trackingIndex != OpenVR.k_unTrackedDeviceIndexInvalid)
|
||||
if (overlayIndex != OpenVR.k_unTrackedDeviceIndexInvalid)
|
||||
{
|
||||
// http://www.opengl-tutorial.org/beginners-tutorials/tutorial-3-matrices
|
||||
// Scaling-Rotation-Translation
|
||||
@@ -328,7 +449,7 @@ namespace VRCX
|
||||
m10 = m.M33,
|
||||
m11 = m.M43,
|
||||
};
|
||||
err = overlay.SetOverlayTransformTrackedDeviceRelative(overlayHandle, trackingIndex, ref hm34);
|
||||
err = overlay.SetOverlayTransformTrackedDeviceRelative(overlayHandle, overlayIndex, ref hm34);
|
||||
if (err != EVROverlayError.None)
|
||||
{
|
||||
return err;
|
||||
@@ -336,7 +457,7 @@ namespace VRCX
|
||||
}
|
||||
|
||||
if (!dashboardVisible &&
|
||||
DateTime.Now.CompareTo(nextRender) <= 0)
|
||||
DateTime.Now.CompareTo(nextOverlay) <= 0)
|
||||
{
|
||||
var texture = new Texture_t
|
||||
{
|
||||
@@ -462,123 +583,5 @@ namespace VRCX
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
private static void ThreadProc()
|
||||
{
|
||||
var e = new VREvent_t();
|
||||
var nextOpenVRInit = DateTime.MinValue;
|
||||
var nextDeviceInfoUpdate = DateTime.MinValue;
|
||||
var nextRender = DateTime.MinValue;
|
||||
var trackingIndex = OpenVR.k_unTrackedDeviceIndexInvalid;
|
||||
var overlayVisible1 = false;
|
||||
var overlayVisible2 = false;
|
||||
var dashboardHandle = 0UL;
|
||||
var overlayHandle1 = 0UL;
|
||||
var overlayHandle2 = 0UL;
|
||||
|
||||
while (m_Thread != null)
|
||||
{
|
||||
m_Browser1.Render();
|
||||
m_Browser2.Render();
|
||||
|
||||
try
|
||||
{
|
||||
Thread.Sleep(10);
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ThreadInterruptedException
|
||||
}
|
||||
|
||||
var system = OpenVR.System;
|
||||
|
||||
if (system == null)
|
||||
{
|
||||
if (DateTime.Now.CompareTo(nextOpenVRInit) < 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
var _err = EVRInitError.None;
|
||||
system = OpenVR.Init(ref _err, EVRApplicationType.VRApplication_Overlay);
|
||||
nextOpenVRInit = DateTime.Now.AddSeconds(5);
|
||||
if (system == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
while (system.PollNextEvent(ref e, (uint)Marshal.SizeOf(e)))
|
||||
{
|
||||
var type = (EVREventType)e.eventType;
|
||||
if (type == EVREventType.VREvent_Quit)
|
||||
{
|
||||
OpenVR.Shutdown();
|
||||
// VRChat이 실행 중일 때만 켜는 옵션이 생겨서 시간을 줄임
|
||||
nextOpenVRInit = DateTime.Now.AddSeconds(10);
|
||||
system = null;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (system == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (DateTime.Now.CompareTo(nextDeviceInfoUpdate) >= 0)
|
||||
{
|
||||
trackingIndex = OpenVR.k_unTrackedDeviceIndexInvalid;
|
||||
UpdateDevices(system, ref trackingIndex);
|
||||
if (trackingIndex != OpenVR.k_unTrackedDeviceIndexInvalid)
|
||||
{
|
||||
nextRender = DateTime.Now.AddSeconds(10);
|
||||
}
|
||||
nextDeviceInfoUpdate = DateTime.Now.AddSeconds(0.1);
|
||||
}
|
||||
|
||||
var overlay = OpenVR.Overlay;
|
||||
|
||||
if (overlay == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var dashboardVisible = overlay.IsDashboardVisible();
|
||||
|
||||
var err = ProcessDashboard(overlay, ref dashboardHandle, dashboardVisible);
|
||||
|
||||
if (err != EVROverlayError.None &&
|
||||
dashboardHandle != 0)
|
||||
{
|
||||
overlay.DestroyOverlay(dashboardHandle);
|
||||
dashboardHandle = 0;
|
||||
}
|
||||
|
||||
err = ProcessOverlay1(overlay, ref overlayHandle1, ref overlayVisible1, dashboardVisible, trackingIndex, nextRender);
|
||||
|
||||
if (err != EVROverlayError.None &&
|
||||
overlayHandle1 != 0)
|
||||
{
|
||||
overlay.DestroyOverlay(overlayHandle1);
|
||||
overlayHandle1 = 0;
|
||||
}
|
||||
|
||||
err = ProcessOverlay2(overlay, ref overlayHandle2, ref overlayVisible2, dashboardVisible);
|
||||
|
||||
if (err != EVROverlayError.None &&
|
||||
overlayHandle2 != 0)
|
||||
{
|
||||
overlay.DestroyOverlay(overlayHandle2);
|
||||
overlayHandle2 = 0;
|
||||
}
|
||||
}
|
||||
|
||||
lock (m_Devices)
|
||||
{
|
||||
m_Devices.Clear();
|
||||
}
|
||||
|
||||
OpenVR.Shutdown();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -44,8 +44,10 @@ namespace VRCX
|
||||
};
|
||||
Browser1.JavascriptObjectRepository.Register("VRCX", new VRCX(), true, options);
|
||||
Browser1.JavascriptObjectRepository.Register("VRCXStorage", new VRCXStorage(), false, options);
|
||||
Browser1.JavascriptObjectRepository.Register("SQLite", new SQLite(), true, options);
|
||||
Browser2.JavascriptObjectRepository.Register("VRCX", new VRCX(), true, options);
|
||||
Browser2.JavascriptObjectRepository.Register("VRCXStorage", new VRCXStorage(), false, options);
|
||||
Browser2.JavascriptObjectRepository.Register("SQLite", new SQLite(), true, options);
|
||||
Browser1.IsBrowserInitializedChanged += (A, B) =>
|
||||
{
|
||||
// Browser1.ShowDevTools();
|
||||
@@ -60,9 +62,9 @@ namespace VRCX
|
||||
|
||||
private void button_refresh_Click(object sender, System.EventArgs e)
|
||||
{
|
||||
VRCXVR.Refresh();
|
||||
Browser1.ExecuteScriptAsync("location.reload()");
|
||||
Browser2.ExecuteScriptAsync("location.reload()");
|
||||
VRCXVR.Refresh();
|
||||
}
|
||||
|
||||
private void button_devtools_Click(object sender, System.EventArgs e)
|
||||
|
||||
@@ -9,6 +9,7 @@ module.exports = {
|
||||
'CefSharp': 'readonly',
|
||||
'VRCX': 'readonly',
|
||||
'VRCXStorage': 'readonly',
|
||||
'SQLite': 'readonly',
|
||||
'LogWatcher': 'readonly',
|
||||
'Discord': 'readonly',
|
||||
'Noty': 'readonly',
|
||||
|
||||
43
html/app.js
43
html/app.js
@@ -7,6 +7,7 @@ if (window.CefSharp) {
|
||||
Promise.all([
|
||||
CefSharp.BindObjectAsync('VRCX'),
|
||||
CefSharp.BindObjectAsync('VRCXStorage'),
|
||||
CefSharp.BindObjectAsync('SQLite'),
|
||||
CefSharp.BindObjectAsync('LogWatcher'),
|
||||
CefSharp.BindObjectAsync('Discord')
|
||||
]).catch(() => {
|
||||
@@ -3490,6 +3491,8 @@ if (window.CefSharp) {
|
||||
var ref;
|
||||
var i;
|
||||
var j;
|
||||
// FIXME
|
||||
// 여러 개 켠다면 gameLogTable의 데이터가 시간순이 아닐 수도 있음
|
||||
i = this.gameLogTable.data.length;
|
||||
j = 0;
|
||||
while (j < 25) {
|
||||
@@ -4460,31 +4463,27 @@ if (window.CefSharp) {
|
||||
};
|
||||
|
||||
$app.methods.refreshGameLog = function () {
|
||||
LogWatcher.HasLog().then((result) => {
|
||||
if (result) {
|
||||
LogWatcher.GetLogs().then((logs) => {
|
||||
logs.forEach((log) => {
|
||||
var ctx = {
|
||||
created_at: log[0],
|
||||
type: log[1],
|
||||
data: log[2]
|
||||
};
|
||||
this.gameLogTable.data.push(ctx);
|
||||
if (ctx.type === 'Location') {
|
||||
this.lastLocation = ctx.data;
|
||||
}
|
||||
});
|
||||
this.sweepGameLog();
|
||||
this.updateSharedFeed();
|
||||
// sweepGameLog로 기록이 삭제되면
|
||||
// 아무 것도 없는데 알림이 떠서 이상함
|
||||
if (this.gameLogTable.length) {
|
||||
this.notifyMenu('gameLog');
|
||||
LogWatcher.Get().then((logs) => {
|
||||
if (logs.length) {
|
||||
logs.forEach((log) => {
|
||||
var ctx = {
|
||||
created_at: log[0],
|
||||
type: log[1],
|
||||
data: log[2]
|
||||
};
|
||||
this.gameLogTable.data.push(ctx);
|
||||
if (ctx.type === 'Location') {
|
||||
this.lastLocation = ctx.data;
|
||||
}
|
||||
});
|
||||
} else {
|
||||
this.updateSharedFeed();
|
||||
this.sweepGameLog();
|
||||
// sweepGameLog로 기록이 삭제되면
|
||||
// 아무 것도 없는데 알림이 떠서 이상함
|
||||
if (this.gameLogTable.length) {
|
||||
this.notifyMenu('gameLog');
|
||||
}
|
||||
}
|
||||
this.updateSharedFeed();
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
@@ -6,7 +6,8 @@
|
||||
if (window.CefSharp) {
|
||||
Promise.all([
|
||||
CefSharp.BindObjectAsync('VRCX'),
|
||||
CefSharp.BindObjectAsync('VRCXStorage')
|
||||
CefSharp.BindObjectAsync('VRCXStorage'),
|
||||
CefSharp.BindObjectAsync('SQLite')
|
||||
]).catch(() => {
|
||||
location = 'https://github.com/pypy-vrc/vrcx';
|
||||
}).then(() => {
|
||||
|
||||
@@ -14,4 +14,5 @@
|
||||
<package id="SharpDX.Direct3D11" version="4.2.0" targetFramework="net452" />
|
||||
<package id="SharpDX.DXGI" version="4.2.0" targetFramework="net452" />
|
||||
<package id="SharpDX.Mathematics" version="4.2.0" targetFramework="net452" />
|
||||
<package id="System.Data.SQLite.Core" version="1.0.111.0" targetFramework="net452" />
|
||||
</packages>
|
||||
Reference in New Issue
Block a user