using MinecraftClient.Protocol;
using System;
using System.Collections.Generic;
using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using System.Timers;
namespace MinecraftClient.Cache
{
///
/// Handle sessions caching and storage.
///
public static class SessionCache
{
const string filename = "cache.bin";
private static Dictionary sessions = new Dictionary();
private static FileSystemWatcher cachemonitor = new FileSystemWatcher();
private static Timer updatetimer = new Timer(100);
private static List> pendingadds = new List>();
private static BinaryFormatter formatter = new BinaryFormatter();
///
/// Retrieve whether SessionCache contains a session for the given login.
///
/// User login used with Minecraft.net
/// TRUE if session is available
public static bool Contains(string login)
{
return sessions.ContainsKey(login);
}
///
/// Store a session and save it to disk if required.
///
/// User login used with Minecraft.net
/// User session token used with Minecraft.net
public static void Store(string login, SessionToken session)
{
if (Contains(login))
{
sessions[login] = session;
}
else
{
sessions.Add(login, session);
}
if (Settings.CacheType == CacheType.DISK && updatetimer.Enabled == true) {
pendingadds.Add(new KeyValuePair(login, session));
}else if (Settings.CacheType == CacheType.DISK)
{
SaveToDisk();
}
}
///
/// Retrieve a session token for the given login.
///
/// User login used with Minecraft.net
/// SessionToken for given login
public static SessionToken Get(string login)
{
return sessions[login];
}
///
/// Initialize cache monitoring to keep cache updated with external changes.
///
/// TRUE if session tokens are seeded from file
public static bool InitializeDiskCache()
{
cachemonitor.Path = AppDomain.CurrentDomain.BaseDirectory;
cachemonitor.IncludeSubdirectories = false;
cachemonitor.Filter = filename;
cachemonitor.NotifyFilter = NotifyFilters.LastWrite;
cachemonitor.Changed += new FileSystemEventHandler(OnChanged);
cachemonitor.EnableRaisingEvents = true;
updatetimer.Elapsed += HandlePending;
return LoadFromDisk();
}
///
/// Reloads cache on external cache file change.
///
/// Sender
/// Event data
private static void OnChanged(object sender, FileSystemEventArgs e)
{
updatetimer.Stop();
updatetimer.Start();
}
///
/// Called after timer elapsed. Reads disk cache and adds new/modified sessions back.
///
/// Sender
/// Event data
private static void HandlePending(object sender, ElapsedEventArgs e)
{
LoadFromDisk();
foreach(KeyValuePair pending in pendingadds.ToArray())
{
Store(pending.Key, pending.Value);
pendingadds.Remove(pending);
}
}
///
/// Reads cache file and loads SessionTokens into SessionCache.
///
/// True if data is successfully loaded
private static bool LoadFromDisk()
{
if (File.Exists(filename))
{
try
{
using (FileStream fs = new FileStream(filename, FileMode.Open, FileAccess.Read, FileShare.Read))
{
sessions = (Dictionary)formatter.Deserialize(fs);
return true;
}
}
catch (IOException ex)
{
Console.WriteLine("Error reading cached sessions from disk: " + ex.Message);
}
catch (SerializationException)
{
Console.WriteLine("Malformed sessions from cache file ");
}
}
return false;
}
///
/// Saves SessionToken's from SessionCache into cache file.
///
private static void SaveToDisk()
{
bool fileexists = File.Exists(filename);
using (FileStream fs = new FileStream(filename, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.None))
{
cachemonitor.EnableRaisingEvents = false;
// delete existing file contents
if (fileexists)
{
fs.SetLength(0);
fs.Flush();
}
formatter.Serialize(fs, sessions);
cachemonitor.EnableRaisingEvents = true;
}
}
}
}