-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Added a headless ravenfall test client
- Loading branch information
Showing
84 changed files
with
3,010 additions
and
1,136 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; } | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; } | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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() | ||
{ | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | ||
} | ||
} | ||
} |
Oops, something went wrong.