using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Text;
using System.Threading.Tasks;
using OWOGame.Controller;
using OWOGame.Infraestructure;
[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: InternalsVisibleTo("Tests")]
[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2")]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: AssemblyCompany("OWO")]
[assembly: AssemblyConfiguration("Debug")]
[assembly: AssemblyDescription("OWO SDK for CSharp projects")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0+c96c7a9964ae7f34d8fd25f10807bb974d16e4c0")]
[assembly: AssemblyProduct("OWO")]
[assembly: AssemblyTitle("OWO")]
[assembly: AssemblyVersion("1.0.0.0")]
namespace OWOGame
{
public class OWO
{
private static OWO instance;
private readonly Client client;
private readonly SendSensation send;
private readonly StopSensation stop;
private readonly Disconnect disconnect;
private readonly Connect connect;
private readonly RealTimeClock clock;
private static OWO Instance => instance ?? (instance = new OWO(ClientFactory.Create(new UDPNetwork()), new RealTimeClock()));
public static ConnectionState ConnectionState => Instance.client.State;
public static string[] DiscoveredApps => Instance.client.DiscoveredServers.ToArray();
internal OWO(Client client, RealTimeClock clock)
{
this.client = client;
send = new SendSensation(client);
stop = new StopSensation(client);
connect = new Connect(client);
disconnect = new Disconnect(client);
this.clock = clock;
}
public static void Configure(GameAuth game)
{
Instance.ConfigureB(game);
}
internal void ConfigureB(GameAuth game)
{
connect.Configure(game);
send.Configure(game);
stop.Configure(game);
}
public static Task AutoConnect()
{
return Instance.AutoConnectB();
}
internal Task AutoConnectB()
{
return Task.Run((Func<Task?>)connect.AutoConnect);
}
public static void StartScan()
{
Instance.StartScanB();
}
internal void StartScanB()
{
Task.Run((Func<Task?>)connect.ScanServer);
}
public static Task Connect(params string[] ips)
{
return Instance.ConnectB(ips);
}
internal Task ConnectB(params string[] ips)
{
return Task.Run(() => connect.ManualConnect(ips));
}
public static void Stop()
{
Instance.StopB();
}
internal void StopB()
{
stop.Execute();
send.ResetPriority();
}
public static void Send(Sensation sensation, params Muscle[] muscles)
{
Instance.SendB(sensation, muscles);
}
internal void SendB(Sensation sensation, params Muscle[] muscles)
{
send.Execute(sensation.WithMuscles(muscles), clock.TotalMilliseconds);
}
public static void Disconnect()
{
Instance.DisconnectB();
}
internal void DisconnectB()
{
disconnect.Execute();
}
}
public class BakedSensation : Sensation
{
public readonly int id;
public readonly string name;
public readonly Family Family;
public readonly Sensation reference;
public readonly Icon icon;
public override float Duration => reference.Duration;
internal BakedSensation(int id, string name, Sensation reference, Icon icon, Family family)
{
this.id = id;
this.name = name;
this.reference = reference;
this.icon = icon;
Family = family;
}
public new static BakedSensation Parse(string message)
{
return ((Sensation)message) as BakedSensation;
}
public BakedSensation WithIcon(Icon icon)
{
return new BakedSensation(id, name, reference, icon, Family).WithPriority(base.Priority) as BakedSensation;
}
public BakedSensation BelongsTo(Family family)
{
return new BakedSensation(id, name, reference, icon, family).WithPriority(base.Priority) as BakedSensation;
}
public string Stringify()
{
return BakedSensationsBuilder.Stringify(this);
}
}
internal static class BakedSensationsBuilder
{
private const string SEPARATOR = "~";
public static string From(BakedSensation sensation)
{
int id = sensation.id;
return id.ToString();
}
public static string Stringify(BakedSensation sensation)
{
string[] array = new string[9];
int id = sensation.id;
array[0] = id.ToString();
array[1] = "~";
array[2] = sensation.name;
array[3] = "~";
array[4] = sensation.reference;
array[5] = "~";
array[6] = sensation.icon;
array[7] = "~";
array[8] = sensation.Family;
return string.Concat(array);
}
}
internal static class GamesBuilder
{
private const string SEPARATOR = "#";
public static string Build(GameAuth theGame)
{
if (theGame.sensations.Length == 0)
{
return string.Empty;
}
string text = theGame.sensations[0].Stringify();
for (int i = 1; i < theGame.sensations.Length; i++)
{
text = text + "#\n" + theGame.sensations[i].Stringify();
}
return text;
}
}
internal static class MusclesBuilder
{
public static string From(params Muscle[] muscles)
{
string text = From(muscles[0]);
for (int i = 1; i < muscles.Length; i++)
{
text = text + "," + From(muscles[i]);
}
return text;
}
private static string From(Muscle muscle)
{
return $"{muscle.id}%{muscle.intensity}";
}
}
internal static class SensationsBuilder
{
public static string From(Sensation sensation)
{
if (sensation is MicroSensation microsensation)
{
return From(microsensation);
}
if (sensation is SensationWithMuscles sensation2)
{
return From(sensation2);
}
if (sensation is SensationsSequence sequence)
{
return From(sequence);
}
return BakedSensationsBuilder.From(sensation as BakedSensation);
}
private static string From(SensationsSequence sequence)
{
string text = From(sequence.sensations[0]);
for (int i = 1; i < sequence.sensations.Count; i++)
{
text = text + "&" + From(sequence.sensations[i]);
}
return text;
}
private static string From(SensationWithMuscles sensation)
{
return From(sensation.reference) + "|" + sensation.muscles.Stringify();
}
private static string From(MicroSensation microsensation)
{
return $"{microsensation.frequency},{(int)Math.Round(microsensation.duration * 10f)},{microsensation.intensity}," + $"{(int)Math.Round(microsensation.rampUp * 1000f)},{(int)Math.Round(microsensation.rampDown * 1000f)},{(int)Math.Round(microsensation.exitDelay * 10f)},{microsensation.name}";
}
}
public static class MusclesExtensions
{
public static Muscle[] WithIntensity(this Muscle[] muscles, int intensity)
{
return muscles.Select((Muscle m) => m.WithIntensity(intensity)).ToArray();
}
public static Muscle Mirror(this Muscle of)
{
return new Muscle(MirrorOf(of.id), of.intensity);
}
public static Muscle[] Mirror(this Muscle[] of)
{
return of.Select(Mirror).ToArray();
}
private static int MirrorOf(int aPosition)
{
return (aPosition % 2 == 0) ? (aPosition + 1) : (aPosition - 1);
}
public static string Stringify(this Muscle muscle)
{
return MusclesBuilder.From(muscle);
}
public static string Stringify(this Muscle[] muscles)
{
return MusclesBuilder.From(muscles);
}
}
public static class SensationExtensions
{
public static Sensation WithPriority(this Sensation source, int priority)
{
source.Priority = priority;
return source;
}
public static Sensation Append(this Sensation source, Sensation addend)
{
return new SensationsSequence(source, addend).WithPriority(source.Priority);
}
public static BakedSensation Bake(this Sensation source, int id, string name)
{
if (source is BakedSensation result)
{
return result;
}
return new BakedSensation(id, name, source, Icon.Empty, Family.None).WithPriority(source.Priority) as BakedSensation;
}
public static Sensation WithMuscles(this Sensation source, params Muscle[] muscles)
{
if (muscles.Length == 0 || source is SensationWithMuscles)
{
return source;
}
if (source is SensationsSequence sensationsSequence)
{
return new SensationsSequence(sensationsSequence.sensations.Select((Sensation s) => s.WithMuscles(muscles)).ToArray()).WithPriority(source.Priority);
}
return new SensationWithMuscles(source, muscles).WithPriority(source.Priority);
}
}
public struct Family
{
private readonly string name;
public static Family None { get; } = "";
private Family(string name)
{
this.name = name;
}
public static implicit operator string(Family family)
{
return family.name;
}
public static implicit operator Family(string name)
{
return new Family(name);
}
public override string ToString()
{
return name;
}
}
public class GameAuth
{
public readonly string id;
public readonly BakedSensation[] sensations = new BakedSensation[0];
public static GameAuth Empty => Create().WithId("0");
internal GameAuth()
{
}
internal GameAuth(string id, params BakedSensation[] sensations)
{
this.id = id;
this.sensations = sensations;
}
public GameAuth WithId(string id)
{
return new GameAuth(id, sensations);
}
public static GameAuth Parse(string auth)
{
return auth;
}
public static GameAuth Create(params BakedSensation[] sensations)
{
return new GameAuth("0", sensations);
}
public override string ToString()
{
return this;
}
public static implicit operator GameAuth(string auth)
{
return GamesParser.From(auth);
}
public static implicit operator string(GameAuth auth)
{
return GamesBuilder.Build(auth);
}
}
public struct Icon
{
[StructLayout(LayoutKind.Sequential, Size = 1)]
public struct Impact
{
private const string PREFIX = "Impact-";
public static Icon Ball => new Icon("Impact-" + 0);
public static Icon Dart => new Icon("Impact-" + 1);
public static Icon Punch => new Icon("Impact-" + 2);
public static Icon Bullet => new Icon("Impact-" + 3);
}
[StructLayout(LayoutKind.Sequential, Size = 1)]
public struct Weapon
{
private const string PREFIX = "Weapon-";
public static Icon Axe => new Icon("Weapon-" + 0);
public static Icon Dagger => new Icon("Weapon-" + 1);
public static Icon Gun => new Icon("Weapon-" + 2);
public static Icon SubMachineGun => new Icon("Weapon-" + 3);
}
private readonly string name;
public static Icon Empty => new Icon("0");
public static Icon Death => new Icon("Death-0");
public static Icon Spiders => new Icon("Spider-0");
public static Icon Weight => new Icon("Weight-0");
public static Icon Environment => new Icon("Environment-0");
public static Icon Alert => new Icon("Alert-0");
public static Icon Victory => new Icon("Victory-0");
internal Icon(string name)
{
this.name = name;
}
public static implicit operator Icon(string message)
{
return new Icon(message);
}
public static implicit operator string(Icon icon)
{
return icon.name;
}
public override string ToString()
{
return this;
}
}
internal static class Math
{
public static T Clamp<T>(T val, T min, T max) where T : IComparable<T>
{
if (val.CompareTo(min) < 0)
{
return min;
}
if (val.CompareTo(max) > 0)
{
return max;
}
return val;
}
public static float Round(float value, int decimals = 1)
{
return (float)System.Math.Round(value, decimals);
}
}
public class MicroSensation : Sensation
{
public readonly int frequency;
public readonly float duration;
public readonly int intensity;
public readonly float rampUp;
public readonly float rampDown;
public readonly float exitDelay;
public readonly string name;
public override float Duration => duration + exitDelay;
internal MicroSensation(int frequency, float duration, int intensity, float rampUp, float rampDown, float exitDelay, string name = "")
{
this.frequency = Math.Clamp(frequency, 1, 100);
this.duration = Math.Round(Math.Clamp(duration, 0.1f, 20f));
this.intensity = Math.Clamp(intensity, 0, 100);
this.rampUp = Math.Round(Math.Clamp(rampUp, 0f, 2f));
this.rampDown = Math.Round(Math.Clamp(rampDown, 0f, 2f));
this.exitDelay = Math.Round(Math.Clamp(exitDelay, 0f, 20f));
this.name = name;
}
public MicroSensation WithName(string name)
{
return new MicroSensation(frequency, duration, intensity, rampUp, rampDown, exitDelay, name).WithPriority(base.Priority) as MicroSensation;
}
}
public readonly struct Muscle
{
public readonly int id;
public readonly int intensity;
public static Muscle Pectoral_R = new Muscle(0);
public static Muscle Pectoral_L = new Muscle(1);
public static Muscle Abdominal_R = new Muscle(2);
public static Muscle Abdominal_L = new Muscle(3);
public static Muscle Arm_R = new Muscle(4);
public static Muscle Arm_L = new Muscle(5);
public static Muscle Dorsal_R = new Muscle(6);
public static Muscle Dorsal_L = new Muscle(7);
public static Muscle Lumbar_R = new Muscle(8);
public static Muscle Lumbar_L = new Muscle(9);
public static Muscle[] All => Front.Concat(Back).ToArray();
public static Muscle[] Front => new Muscle[6] { Pectoral_R, Pectoral_L, Abdominal_R, Abdominal_L, Arm_R, Arm_L };
public static Muscle[] Back => new Muscle[4] { Dorsal_R, Dorsal_L, Lumbar_R, Lumbar_L };
internal Muscle(int id, int intensity = 100)
{
this.id = id;
this.intensity = intensity;
}
public Muscle WithIntensity(int intensity)
{
return new Muscle(id, intensity);
}
public static Muscle[] Parse(string muscles)
{
return MusclesParser.Parse(muscles);
}
}
internal static class BakedSensationsParser
{
private const char SEPARATOR = '~';
public static bool CanParse(string message)
{
return (!Enumerable.Contains(message, ',') && !Enumerable.Contains(message, '|')) || Enumerable.Contains(message, '~');
}
public static BakedSensation From(string message)
{
if (!Enumerable.Contains(message, '~'))
{
return SensationsFactory.Create().Bake(int.Parse(message), "");
}
string[] array = message.Split(new char[1] { '~' });
return new BakedSensation(int.Parse(array[0]), array[1], array[2], array[3], FamilyFrom(array));
}
private static Family FamilyFrom(string[] parameters)
{
return (parameters.Length < 5) ? Family.None : ((Family)parameters[4]);
}
}
internal static class GamesParser
{
public static GameAuth From(string auth)
{
string[] array = auth.Split(new char[1] { '#' });
if (int.TryParse(auth, out var _))
{
return new GameAuth().WithId(auth);
}
if (string.IsNullOrEmpty(array[0]))
{
return new GameAuth();
}
return new GameAuth("0", array.Select((string s) => BakedSensationsParser.From(s)).ToArray());
}
}
internal static class MicrosensationsParser
{
public const char SEPARATOR = ',';
public static MicroSensation From(string message)
{
string[] array = message.Split(new char[1] { ',' });
return new MicroSensation(int.Parse(array[0]), float.Parse(array[1]) / 10f, int.Parse(array[2]), float.Parse(array[3]) / 1000f, float.Parse(array[4]) / 1000f, float.Parse(array[5]) / 10f, NameFrom(array));
}
private static string NameFrom(string[] parameters)
{
return (parameters.Length >= 7) ? parameters[6] : "";
}
}
internal static class MusclesParser
{
public static Muscle[] Parse(string message)
{
string[] source = message.Split(new char[1] { ',' });
return source.Select((string m) => ParseSingle(m)).ToArray();
}
public static Muscle ParseSingle(string message)
{
string[] array = message.Split(new char[1] { '%' });
return new Muscle(int.Parse(array[0]), int.Parse(array[1]));
}
}
internal static class SensationsParser
{
public static Sensation From(string message)
{
if (BakedSensationsParser.CanParse(message))
{
return BakedSensationsParser.From(message);
}
if (SequenceParser.CanParse(message))
{
return SequenceParser.From(message);
}
if (SensationWithMusclesParser.CanParse(message))
{
return SensationWithMusclesParser.From(message);
}
return MicrosensationsParser.From(message);
}
}
internal static class SensationWithMusclesParser
{
public const char SEPARATOR = '|';
public static bool CanParse(string message)
{
return Enumerable.Contains(message, '|');
}
public static SensationWithMuscles From(string message)
{
string[] array = message.Split(new char[1] { '|' });
return new SensationWithMuscles(array[0], MusclesParser.Parse(array[1]));
}
}
internal static class SequenceParser
{
public const char SEPARATOR = '&';
public static bool CanParse(string message)
{
return Enumerable.Contains(message, '&');
}
public static Sensation From(string message)
{
string[] source = message.Split(new char[1] { '&' });
Sensation[] sensations = ((IEnumerable<string>)source).Select((Func<string, Sensation>)((string s) => s)).ToArray();
return new SensationsSequence(sensations);
}
}
public abstract class Sensation
{
public int Priority { get; set; } = 0;
public abstract float Duration { get; }
public static Sensation Ball => SensationsFactory.Create();
public static Sensation Dart => SensationsFactory.Create(10);
public static Sensation Dagger => DaggerEntry.Append(DaggerMovement);
public static Sensation DaggerEntry => SensationsFactory.Create(60, 0.2f);
public static Sensation DaggerMovement => SensationsFactory.Create(100, 2f, 100, 0.3f, 0.1f);
public static Sensation ShotWithExit => ShotEntry.Append(ShotExit).Append(ShotBleeding);
public static Sensation ShotEntry => SensationsFactory.Create(30).WithMuscles(Muscle.Pectoral_R);
public static Sensation ShotExit => SensationsFactory.Create(20).WithMuscles(Muscle.Dorsal_R);
public static Sensation ShotBleeding => SensationsFactory.Create(50, 0.5f, 80, 0f, 0.3f).WithMuscles(Muscle.Pectoral_R, Muscle.Pectoral_L);
public static Sensation Parse(string message)
{
return message;
}
public static implicit operator Sensation(string message)
{
return SensationsParser.From(message);
}
public static implicit operator string(Sensation sensation)
{
return SensationsBuilder.From(sensation);
}
public override string ToString()
{
return this;
}
}
public static class SensationsFactory
{
public static MicroSensation Create(int frequency = 100, float durationSeconds = 0.1f, int intensityPercentage = 100, float rampUpMillis = 0f, float rampDownMillis = 0f, float exitDelaySeconds = 0f)
{
return new MicroSensation(frequency, durationSeconds, intensityPercentage, rampUpMillis, rampDownMillis, exitDelaySeconds);
}
}
public class SensationsSequence : Sensation
{
public readonly List<Sensation> sensations;
public override float Duration => sensations.Sum((Sensation s) => s.Duration);
public SensationsSequence(params Sensation[] sensations)
{
this.sensations = new List<Sensation>(sensations);
}
}
public class SensationWithMuscles : Sensation
{
public readonly Sensation reference;
public readonly Muscle[] muscles;
public override float Duration => reference.Duration;
public SensationWithMuscles(Sensation reference, Muscle[] muscles)
{
this.reference = reference;
this.muscles = muscles;
}
}
public enum ConnectionState
{
Connected,
Disconnected,
Connecting
}
public interface Network
{
List<Address> ConnectedServers { get; }
ConnectionState State { get; set; }
bool IsConnecting { get; }
bool IsConnected { get; }
void SendTo(string message, string addressee);
string Listen(out Address sender);
void Connect(Address server);
void Disconnect();
void Close();
void PortTo(int newPort);
}
public class Client
{
private readonly Network network;
private readonly SendMessage sendMessage;
private readonly FindServer findServer;
private readonly ListenForDisconnection disconnection;
private readonly CandidatesVault candidates;
public ConnectionState State => network.State;
public bool IsConnected => network.ConnectedServers != null && network.ConnectedServers.Count != 0;
private bool CanScan => network.State == ConnectionState.Disconnected;
public List<string> DiscoveredServers => candidates.StoredServers;
internal Client(Network network, SendMessage sendMessage, FindServer findServer, ListenForDisconnection disconnection, CandidatesVault keys)
{
this.network = network;
this.sendMessage = sendMessage;
this.findServer = findServer;
this.disconnection = disconnection;
candidates = keys;
}
~Client()
{
Close();
}
internal Task ScanServer()
{
if (!CanScan)
{
return Task.CompletedTask;
}
candidates.Clean();
return findServer.Scan();
}
internal Task FindServer(string auth, params string[] addresses)
{
if (!CanScan)
{
return Task.CompletedTask;
}
if (addresses[0] == "255.255.255.255")
{
candidates.Clean();
}
if (addresses.Length == 1)
{
return FindServer(addresses[0], auth);
}
return Task.Run(() => findServer.Execute(addresses, auth));
}
private async Task FindServer(string address, string auth)
{
await findServer.ExecuteWithAbscense(address, auth);
ListenForDisconnection(address, auth);
}
private async Task ListenForDisconnection(string addressee, string auth)
{
if (await disconnection.Listen())
{
Disconnect();
await FindServer(addressee, auth);
}
}
public void Send(string message)
{
network.ConnectedServers.ForEach(delegate(Address server)
{
sendMessage.Execute(message, server);
});
}
public void Disconnect()
{
network.Disconnect();
}
public void Close()
{
network.Close();
}
public void ChangeConnectionAttemptRate(int newRate)
{
findServer.DelayTime = newRate;
disconnection.delayTime = newRate;
}
public void PortTo(int newPort)
{
network.PortTo(newPort);
}
}
internal static class ClientFactory
{
public static Client Create(Network network, Message secretKey = default(Message), CandidatesVault keysVault = null, int scanDelayMs = 500)
{
if (keysVault == null)
{
keysVault = new CandidatesVault();
}
if (secretKey.addressee != null)
{
keysVault.Store(secretKey);
}
SendMessage sendMessage = new SendMessage(network);
NotifyAbscense notifyAbscense = new NotifyAbscense(network, keysVault);
SendAuthMessage sendAuth = new SendAuthMessage(sendMessage, keysVault);
ReceiveAvailableApp receiveSecretKey = new ReceiveAvailableApp(keysVault, network);
FindServer findServer = new FindServer(network, notifyAbscense, receiveSecretKey, sendAuth, keysVault)
{
DelayTime = scanDelayMs
};
ListenForDisconnection disconnection = new ListenForDisconnection(network);
return new Client(network, sendMessage, findServer, disconnection, keysVault);
}
}
internal class FindServer
{
private readonly Network network;
private readonly NotifyAbscense notifyAbscense;
private readonly ReceiveAvailableApp interpretAppMessage;
private readonly SendAuthMessage sendAuth;
private readonly CandidatesVault candidates;
public int DelayTime = 500;
private Task TimeBetweenAttempts => Task.Delay(DelayTime);
public FindServer(Network network, NotifyAbscense notifyAbscense, ReceiveAvailableApp receiveSecretKey, SendAuthMessage sendAuth, CandidatesVault keys)
{
this.network = network;
this.notifyAbscense = notifyAbscense;
interpretAppMessage = receiveSecretKey;
this.sendAuth = sendAuth;
candidates = keys;
}
public async Task ExecuteWithAbscense(Address addressee, string auth)
{
await Execute(new string[1] { addressee }, auth);
notifyAbscense.Execute(auth);
}
public async Task Execute(string[] addressees, string auth)
{
network.State = ConnectionState.Connecting;
foreach (string address in addressees)
{
if (candidates.ContainsCandidate(address))
{
sendAuth.Execute(auth, address);
}
}
do
{
Address sender;
string lastMessage = ReceiveMessage(out sender);
if (string.IsNullOrEmpty(lastMessage))
{
foreach (string address2 in addressees)
{
NotifyPresence(address2);
}
}
if (lastMessage.Equals("okay"))
{
candidates.Store(new Message("", sender));
sendAuth.Execute(auth, sender);
}
else if (IsConnectionVerification(addressees, sender, lastMessage))
{
network.Connect(sender);
}
await TimeBetweenAttempts;
sender = default(Address);
}
while (network.ConnectedServers.Count != addressees.Count() && network.State != ConnectionState.Disconnected);
}
private bool IsConnectionVerification(string[] addresse, string sender, string lastMessage)
{
return lastMessage.Equals("pong") && (addresse.Contains(sender) || addresse[0] == "255.255.255.255");
}
private string ReceiveMessage(out Address sender)
{
return network.Listen(out sender);
}
public async Task Scan()
{
while (network.State == ConnectionState.Disconnected)
{
NotifyPresence("255.255.255.255");
await interpretAppMessage.Execute();
await TimeBetweenAttempts;
}
}
private void NotifyPresence(string addressee)
{
network.SendTo("ping", addressee);
}
}
internal class ListenForDisconnection
{
private readonly Network network;
public int delayTime = 50;
private Task ListenDelay => Task.Delay(delayTime);
public ListenForDisconnection(Network network)
{
this.network = network;
}
public async Task<bool> Listen()
{
while (network.State == ConnectionState.Connected)
{
Address sender;
string message = network.Listen(out sender);
if (message.Equals("OWO_Close") && network.ConnectedServers.Contains(sender))
{
return true;
}
await ListenDelay;
sender = default(Address);
}
return false;
}
}
internal class NotifyAbscense
{
private readonly Network network;
private readonly CandidatesVault candidates;
public NotifyAbscense(Network network, CandidatesVault candidates)
{
this.network = network;
this.candidates = candidates;
}
public void Execute(string authCommand)
{
string text = authCommand.Split(new char[1] { '*' })[0];
foreach (string storedServer in candidates.StoredServers)
{
string message = text + "*GAMEUNAVAILABLE";
network.SendTo(message, storedServer);
}
}
}
internal class ReceiveAvailableApp
{
private readonly Network network;
private readonly CandidatesVault keys;
public ReceiveAvailableApp(CandidatesVault secretKeys, Network network)
{
keys = secretKeys;
this.network = network;
}
public Task Execute()
{
Address sender;
string text = network.Listen(out sender);
if (!text.Equals("okay"))
{
return Task.CompletedTask;
}
keys.Store(new Message("", sender));
return Task.CompletedTask;
}
}
internal class SendAuthMessage
{
private readonly SendMessage send;
private readonly CandidatesVault keys;
public SendAuthMessage(SendMessage send, CandidatesVault keys)
{
this.send = send;
this.keys = keys;
}
public void Execute(string auth, Address addressee)
{
if (addressee.Equals(Address.Any))
{
foreach (string storedServer in keys.StoredServers)
{
send.Execute(auth, storedServer);
}
return;
}
if (keys.ContainsCandidate(addressee))
{
send.Execute(auth, addressee);
}
}
}
internal class SendMessage
{
private readonly Network network;
public SendMessage(Network network)
{
this.network = network;
}
public void Execute(string message, Address addressee)
{
if (addressee.IsValid)
{
network.SendTo(message, addressee);
}
}
}
internal class ASCIIEncoder
{
public string Decode(byte[] buffer, int messageLength)
{
return Encoding.ASCII.GetString(buffer, 0, messageLength);
}
public byte[] Encode(string message)
{
return Encoding.ASCII.GetBytes(message);
}
}
internal class UDPNetwork : Network
{
private readonly byte[] buffer;
private readonly Socket socket;
private readonly ASCIIEncoder encoding;
private int PORT = 54020;
public List<Address> ConnectedServers { get; private set; } = new List<Address>();
public ConnectionState State { get; set; } = ConnectionState.Disconnected;
public bool IsConnecting => State == ConnectionState.Connecting;
public bool IsConnected => State == ConnectionState.Connected;
public UDPNetwork()
{
buffer = new byte[1024];
socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
socket.EnableBroadcast = true;
socket.ReceiveTimeout = 2500;
socket.Blocking = false;
encoding = new ASCIIEncoder();
}
public string Listen(out Address address)
{
try
{
EndPoint remoteEP = new IPEndPoint(0L, 0);
string result = encoding.Decode(buffer, socket.ReceiveFrom(buffer, ref remoteEP));
address = new Address((remoteEP as IPEndPoint).Address.ToString());
return result;
}
catch
{
address = Address.Empty;
return string.Empty;
}
}
public void SendTo(string message, string addressee)
{
socket.SendTo(encoding.Encode(message), new IPEndPoint(IPAddress.Parse(addressee), PORT));
}
public void Connect(Address address)
{
if (!ConnectedServers.Contains(address))
{
ConnectedServers.Add(address);
}
State = ConnectionState.Connected;
}
public void Disconnect()
{
ConnectedServers.Clear();
State = ConnectionState.Disconnected;
}
public void Close()
{
socket.Close();
}
public void PortTo(int newPort)
{
PORT = newPort;
}
}
public struct Address
{
public readonly string value;
public bool IsValid => !string.IsNullOrEmpty(value);
public static Address Any => new Address("255.255.255.255");
public static Address Empty => new Address(string.Empty);
public static Address Null => new Address(null);
public Address(string value)
{
this.value = value;
}
public static implicit operator string(Address addressee)
{
return addressee.value;
}
public static implicit operator Address(string value)
{
return new Address(value);
}
public static Address Create(string ip)
{
return new Address(ip);
}
}
internal class CandidatesVault
{
private HashSet<Address> candidateServers = new HashSet<Address>();
public Address LastApp => candidateServers.FirstOrDefault();
public List<string> StoredServers => candidateServers.Select((Address candidate) => candidate.value).ToList();
public void Store(Message message)
{
if (message.HasAddressee)
{
candidateServers.Add(message.addressee);
}
}
public void Clean()
{
candidateServers.Clear();
}
public bool ContainsCandidate(Address address)
{
return candidateServers.Contains(address);
}
}
internal struct Message
{
public readonly string value;
public readonly string addressee;
public bool IsEmpty => string.IsNullOrEmpty(value);
public bool HasAddressee => !string.IsNullOrEmpty(addressee);
public static Message Invalid => new Message(string.Empty, Address.Empty);
public Message(string value, string addresseeIP)
{
this.value = value;
addressee = addresseeIP;
}
}
}
namespace OWOGame.Infraestructure
{
public class RealTimeClock
{
private readonly Stopwatch stopwatch;
public long TotalMilliseconds => (long)stopwatch.Elapsed.TotalMilliseconds;
public RealTimeClock()
{
stopwatch = new Stopwatch();
stopwatch.Start();
}
}
}
namespace OWOGame.Controller
{
internal class Connect
{
private GameAuth game = GameAuth.Empty;
private readonly Client client;
public Connect(Client client)
{
this.client = client;
}
public Task ScanServer()
{
return client.ScanServer();
}
public Task AutoConnect()
{
return client.FindServer($"{game.id}*AUTH*{game}", "255.255.255.255");
}
public Task ManualConnect(params string[] ips)
{
return client.FindServer($"{game.id}*AUTH*{game}", ips);
}
public void Configure(GameAuth game)
{
this.game = game;
}
}
internal class Disconnect
{
private readonly Client client;
public Disconnect(Client client)
{
this.client = client;
}
public void Execute()
{
if (client.State != ConnectionState.Disconnected)
{
client.Disconnect();
}
}
}
internal class SendMessage
{
private readonly Client client;
public SendMessage(Client client)
{
this.client = client;
}
public void Execute(string message)
{
if (client.IsConnected)
{
client.Send(message);
}
}
}
internal class SendSensation : SendMessage
{
private long whenLastSensationEnds;
private int lastPriority = -1;
private GameAuth game = GameAuth.Empty;
public SendSensation(Client network)
: base(network)
{
}
public void Execute(Sensation sensation, long currentTimeMs)
{
if (lastPriority <= sensation.Priority || currentTimeMs >= whenLastSensationEnds)
{
Execute($"{game.id}*SENSATION*{sensation}");
whenLastSensationEnds = currentTimeMs + (int)(sensation.Duration * 1000f);
lastPriority = sensation.Priority;
}
}
public void Configure(GameAuth game)
{
this.game = game;
}
public void ResetPriority()
{
whenLastSensationEnds = 0L;
}
}
internal class StopSensation : SendMessage
{
private GameAuth game = GameAuth.Empty;
public StopSensation(Client network)
: base(network)
{
}
public void Execute()
{
Execute(game.id + "*STOP");
}
public void Configure(GameAuth game)
{
this.game = game;
}
}
}