ShiftOS-C-/source/WindowsFormsApplication1/Online/Hacking/Matchmaker.cs
MichaelTheShifter 6b804f03eb Full ShiftUI conversion
The only bugs are that windows don't show in the center of the screen,
and Gecko webbrowsers are not serializing properly to be converted to
ShiftUI widgets (you can use the ToWidget() extension method to convert
a WinForms control to a ShiftUI widget)

Also multiple desktop panels are removed due to some odd bug I can't
diagnose. Will add them back in the future. Promise. I loved creating
GNOME2 skins.
2016-07-19 21:53:26 -04:00

383 lines
15 KiB
C#

/* ShiftOS Online Hacker Battles - Matchmaker
*
* These classes deal with keeping things in line on the client-side of things.
* They deal with CSP (Client-Side Prediction), sending and receiving messages to
* and from the ShiftOS server, as well as making sure that when you join or leave a
* lobby, the server and other clients actually KNOW you did.
*
* I wouldn't mess with this unless you really, really understand what you're doing,
* as in most cases, modification to the server is required as well (in the case of
* adding new commands). I'd leave modification to the system creator (Michael VanOverbeek) who
* actually wrote this. He's the guy who knows all about how the server works. Wait... why am
* I talking in third person?
*/
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using ShiftUI;
namespace ShiftOS.Online.Hacking
{
public class Matchmaker
{
//All available lobbies.
public static List<ServerInfo> Servers = null;
//All players in the lobby.
public static List<Network> Players = null;
//Some useful info about the lobby in which the player is.
//Also contains server IP to be sent to Package_Grabber.SendMessage().
public static ServerInfo SelectedServer = null;
//Enemy network information (name, codepoints, etc)
public static Network SelectedNetwork = null;
//There's only one transmitter because generally the player
//won't be interacting with the enemy playfield enough to
//warrent a request to the server.
public static NetListener SelectedNetworkListener = null; //Listen for updates from the opponent.
public static NetTransmitter SelectedNetworkTransmitter = null; //Send messages to the server for enemy updates on opponent clients.
public static NetListener PlayerListener = null; //For receiving non-CSP updates from the server.
public static NetTransmitter SecondaryTransmitter = null; //For sending CSP-created update requests to the opponent (enemy health damage, etc)
//Timer that'll run during matchmaking.
public static Timer MakerTimer = null;
/// <summary>
/// This either starts matchmaking or grabs server info. Try it out I guess.
///
/// Fires: Matchmaker.Initiated
/// </summary>
public static void Initiate()
{
MakerTimer = new Timer();
MakerTimer.Interval = 100;
Servers = new List<ServerInfo>();
foreach(var c in Package_Grabber.clients)
{
c.Value.OnReceived += (o, e) =>
{
try
{
var om = (e.Data.Object as ObjectModel);
if (om.Command == "server_info")
{
var si = JsonConvert.DeserializeObject<ServerInfo>(om.OptionalObject as string);
si.IPAddress = c.Value.RemoteHost;
Servers.Add(si);
invoke(() =>
{
Initiated?.Invoke(null, new EventArgs());
});
}
}
catch
{
}
};
Package_Grabber.SendMessage(c.Value.RemoteHost, "get_info");
}
}
/// <summary>
/// Matchmake in the supplied lobby.
///
/// Fires: MorePlayersFound upon player leave/join.
/// </summary>
/// <param name="si">The server to matchmake in.</param>
public static void Matchmake(ServerInfo si)
{
SelectedServer = si;
var rnd = new Random();
Players = new List<Network>();
var server = Package_Grabber.clients[si.IPAddress];
server.OnReceived += (o, e) =>
{
try
{
var om = e.Data.Object as ObjectModel;
if (om.Command == "matchmaking")
{
Players = JsonConvert.DeserializeObject<List<Network>>(om.OptionalObject as string);
invoke(() =>
{
MorePlayersFound?.Invoke(null, new EventArgs());
});
}
}
catch
{
}
};
Package_Grabber.SendMessage(si.IPAddress, "get_matchmaking");
int index = 0;
MakerTimer.Tick += (o, e) =>
{
try
{
if (Players[index].Name != API.CurrentSave.MyOnlineNetwork.Name && Players[index].Name != null)
{
SelectedNetwork = Players[index];
MakerTimer.Stop();
PlayerListener = new NetListener(si, SelectedNetwork);
SecondaryTransmitter = new NetTransmitter(si, API.CurrentSave.MyOnlineNetwork);
SelectedNetworkListener = new NetListener(si, API.CurrentSave.MyOnlineNetwork);
SelectedNetworkTransmitter = new NetTransmitter(si, SelectedNetwork);
var h = new HackUI(SelectedNetworkTransmitter, SelectedNetworkListener, PlayerListener, SecondaryTransmitter);
h.Show();
Package_Grabber.SendMessage(SelectedServer.IPAddress, $"leave_lobby {JsonConvert.SerializeObject(API.CurrentSave.MyOnlineNetwork)}");
}
else
{
index += 1;
}
}
catch
{
}
};
MakerTimer.Interval = 50;
MakerTimer.Start();
}
/// <summary>
/// Fired when Initiate() finishes.
/// </summary>
public static event EventHandler Initiated;
/// <summary>
/// Fired when someone enters/exits a lobby that we are in.
/// </summary>
public static event EventHandler MorePlayersFound;
/// <summary>
/// Helper method to allow me to invoke some code on the ShiftOS desktop thread (for UI access)
/// </summary>
/// <param name="method">The code to invoke (use a lambda expression or just pump a void through.)</param>
public static void invoke(Action method)
{
API.CurrentSession.Invoke(method);
}
internal static void DestroySession()
{
Servers.Clear();
Players.Clear();
ClearEvents();
SelectedNetwork = null;
SelectedNetworkTransmitter = null;
SelectedNetworkListener = null;
PlayerListener = null;
//Good to go, I guess.
}
public static void ClearEvents()
{
Initiated = null;
MorePlayersFound = null;
}
}
public class NetListener
{
public NetListener(ServerInfo si, Network net)
{
register_events(si, net);
}
public List<Module> MyModules = null;
private void register_events(ServerInfo si, Network net)
{
MyModules = new List<Module>();
var server = Package_Grabber.clients[si.IPAddress];
server.OnReceived += (o, e) =>
{
if (e.Data.Object is ObjectModel)
{
var data = (e.Data.Object as ObjectModel);
if (data.Command != null)
{
string[] args = data.Command.Split(' ');
if ((data.OptionalObject as Network)?.Name == net.Name)
{
switch (args[0].ToLower())
{
case "set_health":
string hn = args[1];
int hp = Convert.ToInt32(args[2]);
invoke(() => { ModuleHealthSet?.Invoke(this, new Events.Health { host_name = hn, health = hp }); });
break;
case "place_module":
string hostname = args[1];
int grade = Convert.ToInt32(args[2]);
int newhp = Convert.ToInt32(args[3]);
int x = Convert.ToInt32(args[4]);
int y = Convert.ToInt32(args[5]);
int type = Convert.ToInt32(args[6]);
var moduleToPlace = new Module { Grade = grade, Hostname = hostname, HP = newhp, Type = type, X = x, Y = y };
MyModules.Add(moduleToPlace);
invoke(() => { ModulePlaced?.Invoke(this, new Events.ModulePlaced { new_module = moduleToPlace }); });
break;
case "remove_module":
string hostnametoremove = args[1];
var m = new Module();
foreach (var mod in MyModules)
{
if (mod.Hostname == hostnametoremove)
{
m = mod;
}
}
MyModules.Remove(m);
invoke(() => { ModuleRemoved?.Invoke(this, new Events.ModuleRemoved { new_module = hostnametoremove }); });
break;
case "upgrade":
invoke(() =>
{
string hostnametoupgrade = args[1];
int newgrade = Convert.ToInt32(args[2]);
ModuleUpgraded?.Invoke(this, new Events.ModuleUpgraded { hostname = hostnametoupgrade, grade = newgrade });
});
break;
case "finish":
string json = data.Command.Remove(0, 7);
var winner = JsonConvert.DeserializeObject<Network>(json);
Won?.Invoke(this, new Events.Won(winner));
break;
case "disable":
invoke(() =>
{
string name = args[1];
ModuleDisabled?.Invoke(this, new Events.Disabled { hostName = name });
});
break;
}
}
}
}
};
}
public void invoke(Action method)
{
API.CurrentSession.Invoke(method);
}
public event EventHandler<Events.Health> ModuleHealthSet;
public event EventHandler<Events.ModulePlaced> ModulePlaced;
public event EventHandler<Events.ModuleRemoved> ModuleRemoved;
public event EventHandler<Events.ModuleUpgraded> ModuleUpgraded;
public event EventHandler<Events.Disabled> ModuleDisabled;
public event EventHandler<Events.Won> Won;
}
public class NetTransmitter
{
public ServerInfo serverInfo = null;
public Network EnemyIdent = null;
public NetTransmitter(ServerInfo si, Network enemy)
{
EnemyIdent = enemy;
serverInfo = si;
//HackUI will handle everything else to do with our network.
}
public void send_message(Messages msg, object value)
{
switch(msg)
{
case Messages.PlaceModule:
var m = value as Module;
Package_Grabber.SendMessage(serverInfo.IPAddress, $"place_module {m.Hostname} {m.Grade} {m.HP} {m.X} {m.Y} {m.Type}", EnemyIdent);
break;
case Messages.Upgrade:
string upgradestr = value as string;
Package_Grabber.SendMessage(serverInfo.IPAddress, $"upgrade {upgradestr}", EnemyIdent);
break;
case Messages.RemoveModule:
string hostnametoremove = value as string;
Package_Grabber.SendMessage(serverInfo.IPAddress, $"remove_module {hostnametoremove}", EnemyIdent);
break;
case Messages.SetHealth:
string healthsetstr = value as string;
Package_Grabber.SendMessage(serverInfo.IPAddress, $"set_health {healthsetstr}", EnemyIdent);
break;
case Messages.FinishBattle:
string json = JsonConvert.SerializeObject(value as Network);
Package_Grabber.SendMessage(serverInfo.IPAddress, $"finish {json}");
break;
case Messages.Disabled:
string hnamestr = value as string;
Package_Grabber.SendMessage(serverInfo.IPAddress, $"disable {hnamestr}", EnemyIdent);
break;
}
}
public enum Messages
{
PlaceModule,
Upgrade,
RemoveModule,
SetHealth,
Disabled,
FinishBattle,
}
}
namespace Events
{
public class Health : EventArgs
{
public string host_name { get; set; }
public int health { get; set; }
}
public class Disabled : EventArgs
{
public string hostName { get; set; }
}
public class Won : EventArgs
{
public Network Winner { get; private set; }
public Won(Network winner)
{
Winner = winner;
}
}
public class ModulePlaced : EventArgs
{
public Module new_module { get; set; }
}
public class ModuleRemoved : EventArgs
{
public string new_module { get; set; }
}
public class ModuleUpgraded : EventArgs
{
public string hostname { get; set; }
public int grade { get; set; }
}
}
}