Files
VRCX/SQLiteLegacy.cs
Teacup 0101f3474f Add systems for local world persistence (#553)
* chore: Change vscode workspace settings to work with omnisharp

* refactor(.NET): Use connection string builder to init sqlite database

* docs(.NET): Add method documentation to most things that matter

* docs(.NET): Add more docs I forgot to commit apparently

* feat: Add PoC world database structure ^& http listener

* fix: Send a response if VRCX isn't initialized rather than hanging

* feat: Initialize world db schema on startup

* feat: Allow worlds to store data in db through logfile

* use existing current location for worldDB

* Add tooltips

* chore: Make it so vscode can format C# files without prettier

* refactor: Add sqlite-net to (eventually) replace sqlite impl

* refactor: Make use of sqlite-net for world database

* docs: Add todo for fixing some random exception

* refactor: Remove now-unused SQLiteWorld

* refactor: Fix DB init query and change table structure again

* refactor: Add WorldDataRequest, add attributes for camelcase json keys

* Support current user location from API in addition to gameLog

* Change current location check for worldDB

* feat: Take store requests in JSON, identify worlds by GUID on store.

* refactor: Remove unused worldId param from connection key generator

* docs: Add more documentation to the methods for the world database

* fix: Hey wait that's not a primary key

* feat: Add a 10MB data cap for worlds shared across all of their rows.

* fix: Don't calculate size of world date twice when inserting

* refactor: Discard the guid variable since we only check for validity

* docs: Add docs/comments for new data cap functionality

* feat: Implement /getbulk API endpoint

* fix: Correct WorldDB init query typo

* fix: Update data entries properly instead of using 'OR REPLACE'

* refactor: Move endpoint processing to separate methods

* refactor: Add another check for error 503, remove old code

* feat: Add debug capability to /vrcx/getbulk

* fix: Correct the usage of getUser in actuallyGetCurrentLocation

* feat: Add store errors, implement external reading, stop 404ing

* docs: Add docs for new world db funcs

* refactor: Change world db server listen port to 22500

* fix: Use getUser correctly, dumb dumb

* fix: This error set shouldn't be here

* feat: Future-proof api endpoints. Add /status endpoint

---------

Co-authored-by: Natsumi <cmcooper123@hotmail.com>
2023-06-01 10:34:50 +12:00

152 lines
4.6 KiB
C#

using CefSharp;
using System;
using System.Collections.Generic;
using System.Data.SQLite;
using System.IO;
using System.Threading;
namespace VRCX
{
public class SQLiteLegacy
{
public static readonly SQLiteLegacy Instance;
private readonly ReaderWriterLockSlim m_ConnectionLock;
private readonly SQLiteConnection m_Connection;
static SQLiteLegacy()
{
Instance = new SQLiteLegacy();
}
public SQLiteLegacy()
{
m_ConnectionLock = new ReaderWriterLockSlim();
var dataSource = Program.ConfigLocation;
m_Connection = new SQLiteConnection($"Data Source=\"{dataSource}\";Version=3;PRAGMA locking_mode=NORMAL;PRAGMA busy_timeout=5000", true);
}
internal void Init()
{
m_Connection.Open();
}
internal void Exit()
{
m_Connection.Close();
m_Connection.Dispose();
}
public void Execute(IJavascriptCallback callback, string sql, IDictionary<string, object> args = null)
{
try
{
m_ConnectionLock.EnterReadLock();
try
{
using (var command = new SQLiteCommand(sql, m_Connection))
{
if (args != null)
{
foreach (var arg in args)
{
command.Parameters.Add(new SQLiteParameter(arg.Key, arg.Value));
}
}
using (var reader = command.ExecuteReader())
{
while (reader.Read() == true)
{
var values = new object[reader.FieldCount];
reader.GetValues(values);
if (callback.CanExecute == true)
{
callback.ExecuteAsync(null, values);
}
}
}
}
if (callback.CanExecute == true)
{
callback.ExecuteAsync(null, null);
}
}
finally
{
m_ConnectionLock.ExitReadLock();
}
}
catch (Exception e)
{
if (callback.CanExecute == true)
{
callback.ExecuteAsync(e.Message, null);
}
}
callback.Dispose();
}
public void Execute(Action<object[]> callback, string sql, IDictionary<string, object> args = null)
{
m_ConnectionLock.EnterReadLock();
try
{
using (var command = new SQLiteCommand(sql, m_Connection))
{
if (args != null)
{
foreach (var arg in args)
{
command.Parameters.Add(new SQLiteParameter(arg.Key, arg.Value));
}
}
using (var reader = command.ExecuteReader())
{
while (reader.Read() == true)
{
var values = new object[reader.FieldCount];
reader.GetValues(values);
callback(values);
}
}
}
}
catch
{
}
finally
{
m_ConnectionLock.ExitReadLock();
}
}
public int ExecuteNonQuery(string sql, IDictionary<string, object> args = null)
{
int result = -1;
m_ConnectionLock.EnterWriteLock();
try
{
using (var command = new SQLiteCommand(sql, m_Connection))
{
if (args != null)
{
foreach (var arg in args)
{
command.Parameters.Add(new SQLiteParameter(arg.Key, arg.Value));
}
}
result = command.ExecuteNonQuery();
}
}
finally
{
m_ConnectionLock.ExitWriteLock();
}
return result;
}
}
}