Skip to content

Commit

Permalink
Added a headless ravenfall test client
Browse files Browse the repository at this point in the history
  • Loading branch information
zerratar committed Mar 14, 2021
1 parent 4c66126 commit 780ebcb
Show file tree
Hide file tree
Showing 84 changed files with 3,010 additions and 1,136 deletions.
12 changes: 6 additions & 6 deletions RavenNest.sln
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RavenNest.SDK", "src\RavenN
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RavenNest.UnitTests", "Tests\RavenNest.UnitTests\RavenNest.UnitTests.csproj", "{C7ACFCA9-38B3-4063-B478-05804197850E}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RavenNest.TestClient", "src\RavenNest.TestClient\RavenNest.TestClient.csproj", "{6B58CBB1-4EB9-42FA-9B15-65EC5196D6DB}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{B9B540A3-FCD7-4E76-9A9D-D27108AEC010}"
ProjectSection(SolutionItems) = preProject
.editorconfig = .editorconfig
Expand All @@ -30,6 +28,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RavenNest.Blazor", "src\Rav
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RavenNest.Blazor.Services", "src\RavenNest.Blazor.Services\RavenNest.Blazor.Services.csproj", "{F82556D9-E183-45B6-ADD2-4621E37B69A4}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RavenNest.HeadlessClient", "src\RavenNest.HeadlessClient\RavenNest.HeadlessClient.csproj", "{D39CFBB9-6152-4C23-BF39-3FD07063CC76}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand All @@ -56,10 +56,6 @@ Global
{C7ACFCA9-38B3-4063-B478-05804197850E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C7ACFCA9-38B3-4063-B478-05804197850E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C7ACFCA9-38B3-4063-B478-05804197850E}.Release|Any CPU.Build.0 = Release|Any CPU
{6B58CBB1-4EB9-42FA-9B15-65EC5196D6DB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{6B58CBB1-4EB9-42FA-9B15-65EC5196D6DB}.Debug|Any CPU.Build.0 = Debug|Any CPU
{6B58CBB1-4EB9-42FA-9B15-65EC5196D6DB}.Release|Any CPU.ActiveCfg = Release|Any CPU
{6B58CBB1-4EB9-42FA-9B15-65EC5196D6DB}.Release|Any CPU.Build.0 = Release|Any CPU
{58297EFA-7E78-47DB-988B-3E8F02D8A282}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{58297EFA-7E78-47DB-988B-3E8F02D8A282}.Debug|Any CPU.Build.0 = Debug|Any CPU
{58297EFA-7E78-47DB-988B-3E8F02D8A282}.Release|Any CPU.ActiveCfg = Release|Any CPU
Expand All @@ -72,6 +68,10 @@ Global
{F82556D9-E183-45B6-ADD2-4621E37B69A4}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F82556D9-E183-45B6-ADD2-4621E37B69A4}.Release|Any CPU.ActiveCfg = Release|Any CPU
{F82556D9-E183-45B6-ADD2-4621E37B69A4}.Release|Any CPU.Build.0 = Release|Any CPU
{D39CFBB9-6152-4C23-BF39-3FD07063CC76}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{D39CFBB9-6152-4C23-BF39-3FD07063CC76}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D39CFBB9-6152-4C23-BF39-3FD07063CC76}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D39CFBB9-6152-4C23-BF39-3FD07063CC76}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down
18 changes: 18 additions & 0 deletions src/RavenNest.HeadlessClient/AppSettings.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
namespace RavenNest.HeadlessClient
{
public class AppSettings
{
public AppSettings(string username, string password, string gameKey, string gameVersion)
{
Username = username;
Password = password;
GameKey = gameKey;
GameVersion = gameVersion;
}

public string Username { get; }
public string Password { get; }
public string GameKey { get; }
public string GameVersion { get; }
}
}
21 changes: 21 additions & 0 deletions src/RavenNest.HeadlessClient/Core/IIoC.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
using System;

namespace RavenNest.HeadlessClient.Core
{
public interface IIoC : IDisposable
{
void RegisterShared<TInterface, TImplementation>();

void Register<TInterface, TImplementation>();

void Register<TImplementation>();

void RegisterCustomShared<T>(Func<object> func);

void RegisterCustom<T>(Func<object> func);

TInterface Resolve<TInterface>(params object[] args);

object Resolve(Type t, params object[] args);
}
}
145 changes: 145 additions & 0 deletions src/RavenNest.HeadlessClient/Core/IoC.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;

namespace RavenNest.HeadlessClient.Core
{
public class IoC : IIoC
{
private readonly ConcurrentDictionary<Type, object> instances
= new ConcurrentDictionary<Type, object>();

private readonly ConcurrentDictionary<Type, TypeLookup> typeLookup
= new ConcurrentDictionary<Type, TypeLookup>();

private readonly ConcurrentDictionary<Type, Func<object>> typeFactories
= new ConcurrentDictionary<Type, Func<object>>();

public void RegisterShared<TInterface, TImplementation>()
{
typeLookup[typeof(TInterface)] = new TypeLookup(typeof(TImplementation), true);
}

public void Register<TInterface, TImplementation>()
{
typeLookup[typeof(TInterface)] = new TypeLookup(typeof(TImplementation), false);
}

public void Register<TImplementation>()
{
typeLookup[typeof(TImplementation)] = new TypeLookup(typeof(TImplementation), false);
}

public void RegisterCustomShared<T>(Func<object> func)
{
typeLookup[typeof(T)] = new TypeLookup(typeof(T), true);
typeFactories[typeof(T)] = func;
}

public void RegisterCustom<T>(Func<object> func)
{
typeLookup[typeof(T)] = new TypeLookup(typeof(T), false);
typeFactories[typeof(T)] = func;
}

public TInterface Resolve<TInterface>(params object[] args)
{
return (TInterface)Resolve(typeof(TInterface), args);
}

public object Resolve(Type t, params object[] args)
{
var interfaceType = t;

if (!typeLookup.TryGetValue(t, out var targetType))
throw new Exception($"Unable to resolve the type {t.Name}");

if (targetType.Shared)
{
if (instances.TryGetValue(t, out var obj))
{
return obj;
}
}

if (typeFactories.TryGetValue(interfaceType, out var factory))
{
var item = factory();
instances[interfaceType] = item;
return item;
}

var publicConstructors = targetType.Type
.GetConstructors(BindingFlags.Public | BindingFlags.CreateInstance | BindingFlags.Instance);

foreach (var ctor in publicConstructors)
{
var param = ctor.GetParameters();
if (param.Length == 0)
{
var instance = ctor.Invoke(null);
if (targetType.Shared) instances[interfaceType] = instance;
return instance;
}

var customArgIndex = 0;
var hasCustomArgs = args.Length > 0;
var badConstructor = false;
var ctorArgs = new List<object>();
foreach (var x in param)
{
if (x.ParameterType.IsValueType || x.ParameterType == typeof(string))
{
if (!hasCustomArgs || args.Length <= customArgIndex)
{
badConstructor = true;
break;
}

ctorArgs.Add(args[customArgIndex++]);
continue;
}

ctorArgs.Add(Resolve(x.ParameterType));
}

if (badConstructor)
{
continue;
}

var item = ctor.Invoke(ctorArgs.ToArray());
if (targetType.Shared) instances[interfaceType] = item;
return item;
}
throw new Exception($"Unable to resolve the type {targetType.Type.Name}");
}

public void Dispose()
{
foreach (var instance in instances.Values)
{
if (instance is IDisposable disposable)
{
disposable.Dispose();
}
}
}

private class TypeLookup
{
public TypeLookup(Type type, bool shared)
{
Type = type;
Shared = shared;
}

public Type Type { get; }
public bool Shared { get; }
}
}
}
9 changes: 9 additions & 0 deletions src/RavenNest.HeadlessClient/Game/GameCache.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
using RavenNest.SDK.Endpoints;

namespace RavenNest.HeadlessClient.Game
{
public class GameCache : IGameCache
{
public bool IsAwaitingGameRestore => false;
}
}
41 changes: 41 additions & 0 deletions src/RavenNest.HeadlessClient/Game/GameManager.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
using RavenNest.Models;
using RavenNest.SDK;
using System.Collections.Concurrent;

namespace RavenNest.HeadlessClient.Game
{
public class GameManager : IGameManager
{
private readonly ILogger logger;
private readonly IPlayerManager playerManager;
private readonly ConcurrentQueue<GameEvent> gameEventQueue = new ConcurrentQueue<GameEvent>();

public GameManager(ILogger logger, IPlayerManager playerManager)
{
this.logger = logger;
this.playerManager = playerManager;
}

public IPlayerManager Players => playerManager;

public void HandleGameEvents(EventList gameEvents)
{
logger.WriteLine("Game Events Received: " + gameEvents.Events.Count);

foreach (var ge in gameEvents.Events)
{
gameEventQueue.Enqueue(ge);
}
}

public int GameEventCount => gameEventQueue.Count;

public void OnAuthenticated()
{
}

public void OnSessionStart()
{
}
}
}
12 changes: 12 additions & 0 deletions src/RavenNest.HeadlessClient/Game/PlayerManager.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
using RavenNest.SDK;

namespace RavenNest.HeadlessClient.Game
{
public class PlayerManager : IPlayerManager
{
public IPlayerController GetPlayerByUserId(string source)
{
return null;
}
}
}
107 changes: 107 additions & 0 deletions src/RavenNest.HeadlessClient/GameClient.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
using RavenNest.SDK;
using System;
using System.Threading.Tasks;

namespace RavenNest.HeadlessClient
{
public class GameClient : IGameClient
{
private readonly AppSettings settings;
private readonly ILogger logger;
private readonly IGameManager gameManager;
private readonly IRavenNestClient ravennest;
private int waitEventCount;
private TimeSpan waitTimeout;
private DateTime waitStart;
private int receivedEventCount;

public GameClient(
AppSettings settings,
ILogger logger,
IGameManager gameManager,
IRavenNestClient ravennest)
{
this.settings = settings;
this.logger = logger;
this.gameManager = gameManager;
this.ravennest = ravennest;
}

public async Task<bool> BeginGameSessionAsync()
{
if (string.IsNullOrEmpty(this.settings.GameKey))
{
logger.Error("settings.json does not contain a gamekey. Please update it and run the application again.");
return false;
}

var result = await ravennest.StartSessionAsync(this.settings.GameVersion, this.settings.GameKey, false);
if (!result)
{
logger.Error("Bad game version or game key. Please update the settings.json and run the application again.");
return false;
}

logger.WriteLine("Game Session Started");
return result;
}

public async Task<bool> AuthenticateAsync()
{
if (string.IsNullOrEmpty(this.settings.Username) || string.IsNullOrEmpty(this.settings.Password))
{
logger.Error("settings.json does not contain a username or password. Please update it and run the application again.");
return false;
}

var loginResult = await ravennest.LoginAsync(this.settings.Username, this.settings.Password);
if (!loginResult)
{
logger.Error("Username or password provided in the settings.json is invalid. Please update it and run the application again.");
return false;
}

logger.WriteLine("Login to RavenNest OK");
return loginResult;
}

public void Dispose()
{
if (this.ravennest.SessionStarted)
{
this.ravennest.EndSessionAsync();
this.logger.WriteLine("Client and session terminated.");
}
else
{
this.logger.Error("Client terminated without a game session.");
}
}

public async Task<bool> WaitForGameEventsAsync(int eventCount, TimeSpan timeout)
{
this.waitEventCount = eventCount;
this.waitTimeout = timeout;

if (this.waitStart == DateTime.MinValue)
{
this.waitStart = DateTime.UtcNow;
}

if (DateTime.UtcNow - this.waitStart >= timeout)
{
return false;
}

// ensure we are connected to the websocket stream
await ravennest.UpdateAsync();

if (this.gameManager.GameEventCount >= eventCount)
{
return false;
}

return true;
}
}
}
Loading

0 comments on commit 780ebcb

Please sign in to comment.