From 20fcece26d3c54d87dd3c66702b71d5c13f05199 Mon Sep 17 00:00:00 2001 From: MichaelTheShifter Date: Wed, 29 Jun 2016 18:36:17 -0400 Subject: [PATCH] Stop AI from controlling enemy in online sessions Also added bidirectional data transfer to online battles so that I can send damage requests to the opponent when needed. --- .../Controls/Computer.cs | 10 ++ .../Gameplay/HackUI.cs | 147 ++++++++++++++---- .../Online/Hacking/Matchmaker.cs | 104 ++++++++++++- .../Resources/NetBrowser_Enemies.txt | 3 +- 4 files changed, 230 insertions(+), 34 deletions(-) diff --git a/source/WindowsFormsApplication1/Controls/Computer.cs b/source/WindowsFormsApplication1/Controls/Computer.cs index 1d287f8..ad33a58 100644 --- a/source/WindowsFormsApplication1/Controls/Computer.cs +++ b/source/WindowsFormsApplication1/Controls/Computer.cs @@ -168,6 +168,16 @@ public void LaunchAttack(AttackType type, int rate) public string Hostname { get; set; } + internal void throw_repaired() + { + OnRepair?.Invoke(this, new EventArgs()); + } + + internal void throw_damaged() + { + HP_Decreased?.Invoke(this, new EventArgs()); + } + public event EventHandler HP_Decreased; public void Deteriorate(int amount) diff --git a/source/WindowsFormsApplication1/Gameplay/HackUI.cs b/source/WindowsFormsApplication1/Gameplay/HackUI.cs index f62ae62..efafb25 100644 --- a/source/WindowsFormsApplication1/Gameplay/HackUI.cs +++ b/source/WindowsFormsApplication1/Gameplay/HackUI.cs @@ -24,7 +24,9 @@ public HackUI() private bool InOnlineBattle = false; private Online.Hacking.NetTransmitter transmitter = null; + private Online.Hacking.NetTransmitter _playerTransmitter = null; private Online.Hacking.NetListener receiver = null; + private Online.Hacking.NetListener player_listener = null; public HackUI(EnemyHacker enemy) { @@ -32,11 +34,13 @@ public HackUI(EnemyHacker enemy) InitializeComponent(); } - public HackUI(Online.Hacking.NetTransmitter t, Online.Hacking.NetListener l) + public HackUI(Online.Hacking.NetTransmitter t, Online.Hacking.NetListener l, Online.Hacking.NetListener playerListener, Online.Hacking.NetTransmitter playerTransmitter) { InOnlineBattle = true; transmitter = t; receiver = l; + player_listener = playerListener; + _playerTransmitter = playerTransmitter; InitializeComponent(); } @@ -74,6 +78,8 @@ private void LoadPlayerScreen() { SetupTutorialUI(0); } + if (InOnlineBattle) + LoadOnlinePlayer(); } private void VisualizePlayerNetwork() @@ -101,7 +107,60 @@ private void VisualizePlayerNetwork() AddModule(c); } } - + + private void player_listener_ModuleRemoved(object sender, Online.Hacking.Events.ModuleRemoved e) + { + Computer c = null; + foreach (var m in AllPlayerComputers) + { + if (m.Hostname == e.new_module) + { + c = m; + } + } + AllPlayerComputers.Remove(c); + c.Dispose(); + } + + private void player_listener_ModuleHealthSet(object sender, Online.Hacking.Events.Health e) + { + var mod = new Computer(); + foreach (var m in AllPlayerComputers) + { + if (m.Hostname == e.host_name) + mod = m; + } + mod.HP = e.health; + int old_hp = mod.HP; + mod.HP = e.health; + if (mod.HP > old_hp) + { + mod.throw_repaired(); + } + else + { + mod.throw_damaged(); + } + } + + + public void LoadOnlinePlayer() + { + //register event handlers + player_listener.ModuleHealthSet += player_listener_ModuleHealthSet; + player_listener.ModuleRemoved += player_listener_ModuleRemoved; + player_listener.ModuleDisabled += (o, e) => + { + foreach (var c in AllPlayerComputers) + { + if (c.Hostname == e.hostName) + { + c.Disable(); + } + } + }; + } + public List AllPlayerComputers = null; private void tmrplayerhealthdetect_Tick(object sender, EventArgs e) @@ -1360,7 +1419,7 @@ private void Enemy_System_Repaired(object s, EventArgs e) lbenemycompromised.Location = new Point(location, y); lbenemycompromised.Show(); c.Flash(lbenemycompromised); - + _playerTransmitter?.send_message(Online.Hacking.NetTransmitter.Messages.SetHealth, $"{c.Hostname} {c.HP}"); } @@ -1375,6 +1434,7 @@ private void Enemy_System_Damaged(object s, EventArgs e) lbenemycompromised.Location = new Point(location, y); lbenemycompromised.Show(); c.Flash(lbenemycompromised); + _playerTransmitter?.send_message(Online.Hacking.NetTransmitter.Messages.SetHealth, $"{c.Hostname} {c.HP}"); } private decimal TotalEnemyHP = 0; @@ -1390,44 +1450,46 @@ private void tmrenemyhealthdetect_Tick(object sender, EventArgs e) lbcodepoints.Text = $"Codepoints: {API.Codepoints}"; var rnd = new Random(); int chance = 0; - foreach (var pc in AllEnemyComputers) + if (!InOnlineBattle) { - if (pc.Disabled == false) + foreach (var pc in AllEnemyComputers) { - var elist = new List(); - if (pc.Enslaved) - elist = AllEnemyComputers; - else - elist = AllPlayerComputers; - foreach (var enemy in elist) + if (pc.Disabled == false) { - chance = rnd.Next(1, 20); - if (chance == 10) + var elist = new List(); + if (pc.Enslaved) + elist = AllEnemyComputers; + else + elist = AllPlayerComputers; + foreach (var enemy in elist) { - if (IsTutorial) + chance = rnd.Next(1, 20); + if (chance == 10) { - if (TutorialProgress == 9) + if (IsTutorial) { - ThisPlayerPC.LaunchAttack(pc.GetProperType(), pc.GetDamageRate()); - } - else if (TutorialProgress == 32) - { - enemy.LaunchAttack(pc.GetProperType(), pc.GetDamageRate()); + if (TutorialProgress == 9) + { + ThisPlayerPC.LaunchAttack(pc.GetProperType(), pc.GetDamageRate()); + } + else if (TutorialProgress == 32) + { + enemy.LaunchAttack(pc.GetProperType(), pc.GetDamageRate()); + } + else + { + enemy.Enemies.Clear(); + } } else { - enemy.Enemies.Clear(); + enemy.LaunchAttack(pc.GetProperType(), pc.GetDamageRate()); } } - else - { - enemy.LaunchAttack(pc.GetProperType(), pc.GetDamageRate()); - } } } } } - foreach (var pc in AllEnemyComputers) { health += (decimal)pc.HP; @@ -1581,6 +1643,30 @@ public void LoadOnlineEnemy() } } }; + receiver.Won += (o, e) => + { + //the enemy won! + tmrplayerhealthdetect.Stop(); + tmrenemyhealthdetect.Stop(); + //dispose all the modules. + while(AllPlayerComputers.Count > 0) + { + AllPlayerComputers[0].Dispose(); + AllPlayerComputers.RemoveAt(0); + } + while (AllEnemyComputers.Count > 0) + { + AllEnemyComputers[0].Dispose(); + AllEnemyComputers.RemoveAt(0); + } + //Destroy server connection. + Online.Hacking.Matchmaker.DestroySession(); + //Display win message. + API.CreateInfoboxSession($"{e.Winner.Name} won.", $"{e.Winner.Name} has overthrown your defenses and compromised your system.", infobox.InfoboxMode.Info); + //Kill the hacker UI. + UserRequestedClose = false; + this.Close(); + }; } private void Receiver_ModuleRemoved(object sender, Online.Hacking.Events.ModuleRemoved e) @@ -1623,7 +1709,16 @@ private void Receiver_ModuleHealthSet(object sender, Online.Hacking.Events.Healt if (m.Hostname == e.host_name) mod = m; } + int old_hp = mod.HP; mod.HP = e.health; + if(mod.HP > old_hp) + { + mod.throw_repaired(); + } + else + { + mod.throw_damaged(); + } } #endregion diff --git a/source/WindowsFormsApplication1/Online/Hacking/Matchmaker.cs b/source/WindowsFormsApplication1/Online/Hacking/Matchmaker.cs index ecb68ca..2c7d45b 100644 --- a/source/WindowsFormsApplication1/Online/Hacking/Matchmaker.cs +++ b/source/WindowsFormsApplication1/Online/Hacking/Matchmaker.cs @@ -1,4 +1,18 @@ -using Newtonsoft.Json; +/* 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; @@ -10,16 +24,35 @@ namespace ShiftOS.Online.Hacking { public class Matchmaker { + //All available lobbies. public static List Servers = null; + + //All players in the lobby. public static List 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; - public static Network SelectedNetwork = null; - public static NetListener SelectedNetworkListener = null; - public static NetTransmitter SelectedNetworkTransmitter = 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; + /// + /// This either starts matchmaking or grabs server info. Try it out I guess. + /// + /// Fires: Matchmaker.Initiated + /// public static void Initiate() { MakerTimer = new Timer(); @@ -53,6 +86,12 @@ public static void Initiate() } } + /// + /// Matchmake in the supplied lobby. + /// + /// Fires: MorePlayersFound upon player leave/join. + /// + /// The server to matchmake in. public static void Matchmake(ServerInfo si) { SelectedServer = si; @@ -88,8 +127,12 @@ public static void Matchmake(ServerInfo si) { 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 @@ -106,14 +149,42 @@ public static void Matchmake(ServerInfo si) } - + /// + /// Fired when Initiate() finishes. + /// public static event EventHandler Initiated; + + /// + /// Fired when someone enters/exits a lobby that we are in. + /// public static event EventHandler MorePlayersFound; + + /// + /// Helper method to allow me to invoke some code on the ShiftOS desktop thread (for UI access) + /// + /// The code to invoke (use a lambda expression or just pump a void through.) 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 @@ -181,6 +252,11 @@ private void register_events(ServerInfo si, Network net) ModuleUpgraded?.Invoke(this, new Events.ModuleUpgraded { hostname = hostnametoupgrade, grade = newgrade }); }); break; + case "finish": + string json = data.Command.Remove(0, 7); + var winner = JsonConvert.DeserializeObject(json); + Won?.Invoke(this, new Events.Won(winner)); + break; case "disable": invoke(() => { @@ -205,6 +281,7 @@ public void invoke(Action method) public event EventHandler ModuleRemoved; public event EventHandler ModuleUpgraded; public event EventHandler ModuleDisabled; + public event EventHandler Won; } public class NetTransmitter @@ -216,8 +293,6 @@ public NetTransmitter(ServerInfo si, Network enemy) { EnemyIdent = enemy; serverInfo = si; - var h = new HackUI(this, Matchmaker.SelectedNetworkListener); - h.Show(); //HackUI will handle everything else to do with our network. } @@ -241,6 +316,10 @@ public void send_message(Messages msg, object value) 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); @@ -255,6 +334,7 @@ public enum Messages RemoveModule, SetHealth, Disabled, + FinishBattle, } } @@ -271,6 +351,16 @@ 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; } diff --git a/source/WindowsFormsApplication1/Resources/NetBrowser_Enemies.txt b/source/WindowsFormsApplication1/Resources/NetBrowser_Enemies.txt index bc8d7f9..8ab0596 100644 --- a/source/WindowsFormsApplication1/Resources/NetBrowser_Enemies.txt +++ b/source/WindowsFormsApplication1/Resources/NetBrowser_Enemies.txt @@ -4,5 +4,6 @@ "Orange Inc.":{"IsLeader":false,"Name":"Orange Inc.","FriendDesc":"Orange is a computer industry giant. Let's see how they can compete in a Hacker Battle.","Description":"Orange is a computer industry giant, creators of the Tangerine Operating System, and various portable devices like the TangerineBook, OrangePad, and OrangePhone.","FriendSpeed":100,"FriendSkill":200,"Difficulty":"hard","Network":[{"Hostname":"orange_inc.","ModuleType":0,"Type":0,"HP":100,"Grade":1,"X":0,"Y":0},{"Hostname":"blood_orange","ModuleType":0,"Type":6,"HP":0,"Grade":4,"X":484,"Y":203},{"Hostname":"yummy","ModuleType":0,"Type":2,"HP":0,"Grade":3,"X":497,"Y":150},{"Hostname":"juicy","ModuleType":0,"Type":5,"HP":0,"Grade":4,"X":356,"Y":212},{"Hostname":"sweet","ModuleType":0,"Type":9,"HP":0,"Grade":2,"X":316,"Y":212},{"Hostname":"orange","ModuleType":0,"Type":2,"HP":0,"Grade":3,"X":490,"Y":270}]}, "UltraDOS Foundation":{"IsLeader":false,"Name":"UltraDOS Foundation","FriendDesc":"UltraDOS Foundation is a group of competent programmers responsible for the ShiftOS-based UltraDOS operating system.","Description":"UltraDOS Foundation is a group of competent programmers responsible for the ShiftOS-based UltraDOS operating system.","FriendSpeed":65,"FriendSkill":75,"Difficulty":"medium","Network":[{"Hostname":"ultrados_foundation","ModuleType":0,"Type":0,"HP":100,"Grade":1,"X":0,"Y":0},{"Hostname":"ud_trt1","ModuleType":0,"Type":3,"HP":0,"Grade":2,"X":361,"Y":171},{"Hostname":"ud_trt2","ModuleType":0,"Type":3,"HP":0,"Grade":2,"X":358,"Y":220},{"Hostname":"ud_repairer","ModuleType":0,"Type":9,"HP":0,"Grade":4,"X":494,"Y":215}]}, "LadouceurNet":{"IsLeader":false,"Name":"LadouceurNet","FriendDesc":"The LadouceurNet - the Shiftnet that never happened.","Description":"The LadouceurNet - the Shiftnet that never happened.","FriendSpeed":140,"FriendSkill":125,"Difficulty":"medium","Network":[{"Hostname":"ladouceurnet","ModuleType":0,"Type":0,"HP":100,"Grade":1,"X":0,"Y":0},{"Hostname":"mod4","ModuleType":0,"Type":3,"HP":0,"Grade":4,"X":479,"Y":127},{"Hostname":"mod5","ModuleType":0,"Type":3,"HP":0,"Grade":4,"X":382,"Y":131},{"Hostname":"mod5_1","ModuleType":0,"Type":1,"HP":0,"Grade":2,"X":482,"Y":302},{"Hostname":"mod5_2","ModuleType":0,"Type":9,"HP":0,"Grade":4,"X":359,"Y":304},{"Hostname":"mod8","ModuleType":0,"Type":5,"HP":0,"Grade":4,"X":403,"Y":336},{"Hostname":"mod1","ModuleType":0,"Type":3,"HP":0,"Grade":4,"X":444,"Y":335}]}, -"GimmeX":{"IsLeader":false,"Name":"GimmeX","FriendDesc":"The data stealer","Description":"The data stealer","FriendSpeed":10,"FriendSkill":15,"Difficulty":"medium","Network":[{"Hostname":"gimmex","ModuleType":0,"Type":0,"HP":100,"Grade":1,"X":0,"Y":0},{"Hostname":"Repairman","ModuleType":0,"Type":9,"HP":0,"Grade":1,"X":442,"Y":145},{"Hostname":"Stealer","ModuleType":0,"Type":8,"HP":0,"Grade":4,"X":371,"Y":121},{"Hostname":"Attacker","ModuleType":0,"Type":3,"HP":0,"Grade":1,"X":359,"Y":218},{"Hostname":"Defense","ModuleType":0,"Type":5,"HP":0,"Grade":4,"X":494,"Y":227},{"Hostname":"IGiveBreaks","ModuleType":0,"Type":6,"HP":0,"Grade":1,"X":522,"Y":156}]} +"GimmeX":{"IsLeader":false,"Name":"GimmeX","FriendDesc":"The data stealer","Description":"The data stealer","FriendSpeed":10,"FriendSkill":15,"Difficulty":"medium","Network":[{"Hostname":"gimmex","ModuleType":0,"Type":0,"HP":100,"Grade":1,"X":0,"Y":0},{"Hostname":"Repairman","ModuleType":0,"Type":9,"HP":0,"Grade":1,"X":442,"Y":145},{"Hostname":"Stealer","ModuleType":0,"Type":8,"HP":0,"Grade":4,"X":371,"Y":121},{"Hostname":"Attacker","ModuleType":0,"Type":3,"HP":0,"Grade":1,"X":359,"Y":218},{"Hostname":"Defense","ModuleType":0,"Type":5,"HP":0,"Grade":4,"X":494,"Y":227},{"Hostname":"IGiveBreaks","ModuleType":0,"Type":6,"HP":0,"Grade":1,"X":522,"Y":156}]}, +"A-Labs":{"IsLeader":false,"Name":"A-Labs","FriendDesc":"A-Labs is a group that is attempting to find a cure for cancer.","Description":"A-Labs is a group that is attempting to find a cure for cancer.","FriendSpeed":9999,"FriendSkill":9999,"Difficulty":"hard","Network":[{"Hostname":"a-labs","ModuleType":0,"Type":0,"HP":100,"Grade":1,"X":0,"Y":0},{"Hostname":"Subject_1","ModuleType":0,"Type":1,"HP":0,"Grade":2,"X":512,"Y":211},{"Hostname":"Subject_2","ModuleType":0,"Type":7,"HP":0,"Grade":4,"X":330,"Y":194},{"Hostname":"Subject_3","ModuleType":0,"Type":9,"HP":0,"Grade":4,"X":427,"Y":270},{"Hostname":"Subject_4","ModuleType":0,"Type":9,"HP":0,"Grade":4,"X":437,"Y":155},{"Hostname":"Subject_5","ModuleType":0,"Type":2,"HP":0,"Grade":3,"X":284,"Y":204}]} } \ No newline at end of file