Skip to content

Perform Temporary Caching #176

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Jan 21, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions Uchu.Core/Config/UchuConfiguration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,14 @@ public class UchuConfiguration
/// </summary>
public class CacheConfig
{
/// <summary>
/// Whether to use an external caching service, like Redis.
/// Setting to false reduces the initial server load times
/// if no service is installed and the connection will
/// always timeout.
/// </summary>
[XmlElement] public bool UseService { get; set; } = true;

/// <summary>
/// Hostname to use when connecting to the cache service
/// </summary>
Expand Down
136 changes: 136 additions & 0 deletions Uchu.Core/Logging/LogQueue.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
using System;
using System.Threading;
using System.Threading.Tasks;

namespace Uchu.Core
{
public class LogEntry
{
public string message { get; set; }
public ConsoleColor color { get; set; }
public LogEntry nextEntry { get; set; }
}

public class LogQueue : IDisposable
{
private readonly Mutex logMutex = new Mutex();
private LogEntry nextEntry = null;
private LogEntry lastEntry = null;

/// <summary>
/// Adds a line to the log queue.
/// </summary>
public void AddLine(string line, ConsoleColor color)
{
// Lock the log.
logMutex.WaitOne();

// Create the new entry.
var entry = new LogEntry()
{
message = line,
color = color,
};

// Update the pointers.
if (this.nextEntry == null)
{
this.nextEntry = entry;
this.lastEntry = entry;
}
else
{
this.lastEntry.nextEntry = entry;
this.lastEntry = entry;
}

// Unlock the log.
this.logMutex.ReleaseMutex();
}

/// <summary>
/// Pops the next entry to output.
/// Returns null if there is no pending entry.
/// </summary>
public LogEntry PopEntry()
{
// Lock the log.
logMutex.WaitOne();

// Pop the next entry.
LogEntry entry = null;
if (this.nextEntry != null)
{
entry = this.nextEntry;
this.nextEntry = entry.nextEntry;
}

// Unlock the log.
this.logMutex.ReleaseMutex();

// Return the entry.
return entry;
}

/// <summary>
/// Starts a thread for outputting the log. This prevents
/// the console IO from blocking the application.
/// </summary>
public void StartConsoleOutput()
{
Task.Factory.StartNew(async () =>
{
// Run the output loop.
while (true)
{
// Read the entries until they are exhausted.
var entry = this.PopEntry();
while (entry != null)
{
// Set the color to output.
if (entry.color == ConsoleColor.White)
{
Console.ResetColor();
}
else
{
Console.ForegroundColor = entry.color;
}

// Write the entry.
await Console.Out.WriteLineAsync(entry.message).ConfigureAwait(false);
entry = this.PopEntry();
}

// Reset the color.
Console.ResetColor();

// Sleep for a bit before resuming.
await Task.Delay(50).ConfigureAwait(false);
}
},CancellationToken.None,TaskCreationOptions.LongRunning,TaskScheduler.Default);
}

/// <summary>
/// Disposed the object.
/// Implemented to silence CA1001 warning.
/// </summary>
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
this.logMutex.Dispose();
}
}

/// <summary>
/// Disposed the object.
/// Implemented to silence CA1001 warning.
/// </summary>
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
}
}
32 changes: 18 additions & 14 deletions Uchu.Core/Logging/Logger.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@ public static class Logger
{
public static UchuConfiguration Config { get; set; }

private static LogQueue logQueue = new LogQueue();

private static bool outputTaskStarted = false;

public static void Log(object content, LogLevel logLevel = LogLevel.Information)
{
switch (logLevel)
Expand Down Expand Up @@ -110,15 +114,6 @@ private static void InternalLog(string message, LogLevel logLevel, ConsoleColor
}

{
if (color == ConsoleColor.White)
{
Console.ResetColor();
}
else
{
Console.ForegroundColor = color;
}

var level = logLevel.ToString();

var padding = new string(Enumerable.Repeat(' ', 12 - level.Length).ToArray());
Expand Down Expand Up @@ -148,10 +143,14 @@ private static void InternalLog(string message, LogLevel logLevel, ConsoleColor

if (consoleLogLevel <= logLevel)
{
finalMessage = configuration.Timestamp ? $"[{DateTime.Now}] {message}" : message;

Console.WriteLine(finalMessage);
Console.ResetColor();
finalMessage = configuration.Timestamp ? $"[{GetCurrentTime()}] {message}" : message;
logQueue.AddLine(finalMessage,color);

if (!outputTaskStarted)
{
outputTaskStarted = true;
logQueue.StartConsoleOutput();
}
}

configuration = Config.FileLogging;
Expand All @@ -162,12 +161,17 @@ private static void InternalLog(string message, LogLevel logLevel, ConsoleColor

var file = configuration.File;

finalMessage = configuration.Timestamp ? $"[{DateTime.Now}] {message}" : message;
finalMessage = configuration.Timestamp ? $"[{GetCurrentTime()}] {message}" : message;

File.AppendAllTextAsync(file, $"{finalMessage}\n");
}
}

public static string GetCurrentTime()
{
return DateTime.Now.ToString("MM/dd/yyyy hh:mm:ss.fff tt");
}

#if DEBUG
private static MethodBase TraceInvoker(StackTrace trace)
{
Expand Down
18 changes: 13 additions & 5 deletions Uchu.Core/UchuServer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -208,14 +208,22 @@ public virtual async Task ConfigureAsync(string configFile)
SsoService = new SsoService(Config.SsoConfig?.Domain ?? "");

// Try to connect to Redis, otherwise fallback to DB caching
try
if (Config.CacheConfig.UseService)
{
SessionCache = new RedisSessionCache(Config.CacheConfig);
Logger.Information($"Established Redis connection at {Config.CacheConfig.Host}:{Config.CacheConfig.Port}");
try
{
SessionCache = new RedisSessionCache(Config.CacheConfig);
Logger.Information($"Established Redis connection at {Config.CacheConfig.Host}:{Config.CacheConfig.Port}");
}
catch (RedisConnectionException)
{
Logger.Error("Failed to establish Redis connection, falling back to database.");
SessionCache = new DatabaseCache();
}
}
catch (RedisConnectionException)
else
{
Logger.Error("Failed to establish Redis connection, falling back to database.");
Logger.Information("Caching service is disabled, falling back to database.");
SessionCache = new DatabaseCache();
}

Expand Down
4 changes: 2 additions & 2 deletions Uchu.StandardScripts/BlockYard/BlockYardProperty.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
using System;
using InfectedRose.Luz;
using System.Numerics;
using Uchu.World.Client;

namespace Uchu.StandardScripts.BlockYard
{
Expand Down Expand Up @@ -189,8 +190,7 @@ private void WithdrawSpider(Player Player, bool Withdraw) // This removes the sp

SpiderQueen.Animate("withdraw");

using var cdClient = new CdClientContext();
var Animation = cdClient.AnimationsTable.FirstOrDefault(
var Animation = ClientCache.GetTable<Animations>().FirstOrDefault(
a => a.Animationname == "withdraw"
);

Expand Down
6 changes: 3 additions & 3 deletions Uchu.StandardScripts/General/LaunchpadEvent.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
using System.Linq;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using Uchu.Core;
using Uchu.Core.Client;
using Uchu.World;
using Uchu.World.Client;
using Uchu.World.Scripting.Native;

namespace Uchu.StandardScripts.General
Expand All @@ -19,11 +21,9 @@ public override Task LoadAsync()
{
var launchpad = message.Associate.GetComponent<RocketLaunchpadComponent>();

await using var cdClient = new CdClientContext();

var id = launchpad.GameObject.Lot.GetComponentId(ComponentId.RocketLaunchComponent);

var launchpadComponent = await cdClient.RocketLaunchpadControlComponentTable.FirstOrDefaultAsync(
var launchpadComponent = (await ClientCache.GetTableAsync<RocketLaunchpadControlComponent>()).FirstOrDefault(
r => r.Id == id
);

Expand Down
41 changes: 37 additions & 4 deletions Uchu.World/Client/CdClient/ClientCache.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using Uchu.Core;
using Uchu.Core.Client;
using Uchu.World.Systems.Missions;
Expand All @@ -11,6 +15,11 @@ namespace Uchu.World.Client
/// </summary>
public static class ClientCache
{
/// <summary>
/// Cache of the table objects used by GetTable and GetTableAsync
/// </summary>
private static Dictionary<string,object> cacheTables = new Dictionary<string,object>();

/// <summary>
/// All missions in the cd client
/// </summary>
Expand All @@ -21,17 +30,18 @@ public static class ClientCache
/// </summary>
public static MissionInstance[] Achievements { get; private set; } = { };

/// <summary>
/// Loads the initial cache
/// </summary>
public static async Task LoadAsync()
{
await using var cdContext = new CdClientContext();

Logger.Debug("Setting up missions cache");
var missionTasks = cdContext.MissionsTable
var missionTasks = GetTable<Missions>()
.ToArray()
.Select(async m =>
{
var instance = new MissionInstance(m.Id ?? 0);
await instance.LoadAsync(cdContext);
await instance.LoadAsync();
return instance;
}).ToList();

Expand All @@ -40,5 +50,28 @@ public static async Task LoadAsync()
Missions = missionTasks.Select(t => t.Result).ToArray();
Achievements = Missions.Where(m => !m.IsMission).ToArray();
}

/// <summary>
/// Fetches the values of a table asynchronously.
/// Will return without waiting if the data is already stored.
/// </summary>
public static async Task<T[]> GetTableAsync<T>() where T : class
{
var tableName = typeof(T).Name + "Table";
if (!cacheTables.ContainsKey(tableName))
{
cacheTables[tableName] = new TableCache<T>(tableName);
}

return await ((TableCache<T>) cacheTables[tableName]).GetValuesAsync();
}

/// <summary>
/// Fetches the values of a table.
/// </summary>
public static T[] GetTable<T>() where T : class
{
return GetTableAsync<T>().Result;
}
}
}
Loading