From 7cc20ae3bbaf76fdb1b54a3f287760981fbcfffc Mon Sep 17 00:00:00 2001 From: Robert Paciorek Date: Fri, 19 Jan 2024 20:06:24 +0000 Subject: [PATCH] support for "WorldEvents" (ship battle) --- src/CommandHandlers/DateTimeHandler.cs | 2 +- src/CommandHandlers/JoinRoomHandler.cs | 6 +- src/CommandHandlers/LoginHandler.cs | 23 ++- src/CommandHandlers/WorldEventHandlers.cs | 115 +++++++++++++++ src/Core/Room.cs | 6 + src/Core/Utils.cs | 30 ++++ src/Core/WorldEvent.cs | 168 ++++++++++++++++++++++ 7 files changed, 343 insertions(+), 7 deletions(-) create mode 100644 src/CommandHandlers/WorldEventHandlers.cs create mode 100644 src/Core/WorldEvent.cs diff --git a/src/CommandHandlers/DateTimeHandler.cs b/src/CommandHandlers/DateTimeHandler.cs index e2f4bc5..753d459 100644 --- a/src/CommandHandlers/DateTimeHandler.cs +++ b/src/CommandHandlers/DateTimeHandler.cs @@ -9,7 +9,7 @@ class DateTimeHandler : ICommandHandler { public void Handle(Client client, NetworkObject receivedObject) { NetworkObject cmd = new(); NetworkObject obj = new(); - obj.Add("arr", new string[] { "DT", DateTime.UtcNow.ToString("MM/dd/yyyy HH:mm") }); + obj.Add("arr", new string[] { "DT", DateTime.UtcNow.ToString("MM/dd/yyyy HH:mm:ss") }); cmd.Add("c", "DT"); cmd.Add("p", obj); client.Send(NetworkObject.WrapObject(1, 13, cmd).Serialize()); diff --git a/src/CommandHandlers/JoinRoomHandler.cs b/src/CommandHandlers/JoinRoomHandler.cs index 533b5f6..60ca1af 100644 --- a/src/CommandHandlers/JoinRoomHandler.cs +++ b/src/CommandHandlers/JoinRoomHandler.cs @@ -14,9 +14,7 @@ class JoinRoomHandler : ICommandHandler string roomName = receivedObject.Get("p").Get("rn"); client.LeaveRoom(); client.InvalidatePlayerData(); - if (!Room.Exists(roomName)) - Room.Add(roomName); - room = Room.Get(roomName); + room = Room.GetOrAdd(roomName); Console.WriteLine($"Join Room: {roomName} RoomID: {room.Id} IID: {client.ClientID}"); this.client = client; @@ -103,4 +101,4 @@ class JoinRoomHandler : ICommandHandler } } -} \ No newline at end of file +} diff --git a/src/CommandHandlers/LoginHandler.cs b/src/CommandHandlers/LoginHandler.cs index baba273..294fd1f 100644 --- a/src/CommandHandlers/LoginHandler.cs +++ b/src/CommandHandlers/LoginHandler.cs @@ -28,6 +28,25 @@ class LoginHandler : ICommandHandler NetworkArray r2 = new(); + NetworkArray we = new(); + NetworkArray we1 = new(); + NetworkArray we2 = new(); + + we1.Add("WE_ScoutAttack"); + we1.Add((Byte)4); + we1.Add(WorldEvent.Get().EventInfo()); + we1.Add(false); + we1.Add(true); + + we2.Add("WEN_ScoutAttack"); + we2.Add((Byte)4); + we2.Add(WorldEvent.Get().EventInfoNext()); + we2.Add(false); + we2.Add(true); + + we.Add(we1); + we.Add(we2); + r2.Add(1); r2.Add("ADMIN"); r2.Add("default"); @@ -36,7 +55,7 @@ class LoginHandler : ICommandHandler r2.Add(true); r2.Add((short)0); r2.Add((short)1); - r2.Add(new NetworkArray()); + r2.Add(we); rl.Add(r2); NetworkArray r3 = new(); @@ -61,4 +80,4 @@ class LoginHandler : ICommandHandler client.Send(NetworkObject.WrapObject(0, 1, content).Serialize()); } -} \ No newline at end of file +} diff --git a/src/CommandHandlers/WorldEventHandlers.cs b/src/CommandHandlers/WorldEventHandlers.cs new file mode 100644 index 0000000..311a7c2 --- /dev/null +++ b/src/CommandHandlers/WorldEventHandlers.cs @@ -0,0 +1,115 @@ +using System.Globalization; +using sodoffmmo.Attributes; +using sodoffmmo.Core; +using sodoffmmo.Data; + +namespace sodoffmmo.CommandHandlers; + +[ExtensionCommandHandler("wex.WES")] // event status request +class WorldEventStatusHandler : ICommandHandler { + public void Handle(Client client, NetworkObject receivedObject) { + client.Send(Utils.ArrNetworkPacket( new string[] { + "WESR", + "WE_ScoutAttack|" + WorldEvent.Get().EventInfo() + })); + } +} + +[ExtensionCommandHandler("wex.OV")] +class WorldEventHealthHandler : ICommandHandler { + // rec: {"a":13,"c":1,"p":{"c":"wex.OV","p":{"en":"","event":"ScoutAttack","eventUID":"ZydLUmCC","oh":"0.003444444","uid":"ZydLUmCC1"},"r":-1}} + public void Handle(Client client, NetworkObject receivedObject) { + NetworkObject p = receivedObject.Get("p"); + float healthUpdateVal = float.Parse( + p.Get("oh"), + System.Globalization.CultureInfo.InvariantCulture + ); + string targetUid = p.Get("uid"); + + float health = WorldEvent.Get().UpdateHealth(targetUid, healthUpdateVal); + + if (health >= 0.0f) { + // send: {"a":11,"c":0,"p":{"r":367256,"vl":[["WEH_ZydLUmCC1",4,"0.33133352,Thu Jun 22 02:02:43 UTC 2023",false,false]]}} + NetworkPacket packet = Utils.VlNetworkPacket( + "WEH_" + targetUid, + health.ToString("0.0#####", CultureInfo.GetCultureInfo("en-US")) + "," + DateTime.UtcNow.ToString("ddd MMM dd HH:mm:ss UTC yyyy", CultureInfo.GetCultureInfo("en-US")) + ); + foreach (var roomClient in WorldEvent.Get().GetRoom().Clients) { + roomClient.Send(packet); + } + } + } +} + +[ExtensionCommandHandler("wex.OVF")] // flare info from ship AI -> resend as WEF_ +class WorldEventFlareHandler : ICommandHandler { + // rec: {"a":13,"c":1,"p":{"c":"wex.OVF","p":{"en":"","fuid":"WpnpDyJ51,14,0","oh":"0","ts":"6/29/2023 3:03:18 AM"},"r":-1}} + public void Handle(Client client, NetworkObject receivedObject) { + NetworkObject p = receivedObject.Get("p"); + + // send: {"a":11,"c":0,"p":{"r":403777,"vl":[["WEF_WpnpDyJ51,14,0",4,"0,6/29/2023 3:03:18 AM",false,false]]}} + NetworkPacket packet = Utils.VlNetworkPacket( + "WEF_" + p.Get("fuid"), + p.Get("oh") + "," + p.Get("ts") + ); + foreach (var roomClient in WorldEvent.Get().GetRoom().Clients) { + roomClient.Send(packet); + } + } +} + +[ExtensionCommandHandler("wex.ST")] // missile info from ship AI -> resend as WA +class WorldEventMissileHandler : ICommandHandler { + // rec: {"a":13,"c":1,"p":{"c":"wex.ST","p":{"en":"","objID":"-4X_gWAo1","tID":"f5b6254a-df78-4e24-aa9d-7e14539fb858","uID":"1f8eeb6b-753f-4e7f-af13-42cdd69d14e7","wID":"5"},"r":-1}} + public void Handle(Client client, NetworkObject receivedObject) { + NetworkObject p = receivedObject.Get("p"); + + // send: {"a":13,"c":1,"p":{"c":"","p":{"arr":["WA","1f8eeb6b-753f-4e7f-af13-42cdd69d14e7","5","f5b6254a-df78-4e24-aa9d-7e14539fb858","-4X_gWAo1"]}}} + NetworkPacket packet = Utils.ArrNetworkPacket(new string[] { + "WA", + p.Get("uID"), + p.Get("wID"), + p.Get("tID"), + p.Get("objID") + }); + foreach (var roomClient in WorldEvent.Get().GetRoom().Clients) { + roomClient.Send(packet); + } + } +} + +[ExtensionCommandHandler("wex.PS")] +class WorldEventScoreHandler : ICommandHandler { + // rec: {"a":13,"c":1,"p":{"c":"wex.PS","p":{"ScoreData":"Datashyo/10","en":"","id":"ScoutAttack"},"r":-1}} + public void Handle(Client client, NetworkObject receivedObject) { + string scoreData = receivedObject.Get("p").Get("ScoreData"); + WorldEvent.Get().UpdateScore(client, scoreData); + } +} + +[ExtensionCommandHandler("wex.AIACK")] // AI ack +class WorldEventAIACKHandler : ICommandHandler { + // rec: {"a":13,"c":1,"p":{"c":"wex.AIACK","p":{"en":"","id":"f322dd98-e9fb-4b2d-a5e0-1c98680517b5","uid":"SoDOff1"},"r":-1}} + public void Handle(Client client, NetworkObject receivedObject) { + WorldEvent.Get().UpdateAI(client); + } +} +[ExtensionCommandHandler("wex.AIP")] // AI ping +class WorldEventAIPingHandler : ICommandHandler { + // rec: {"a":13,"c":1,"p":{"c":"wex.AIP","p":{"en":""},"r":-1}} + public void Handle(Client client, NetworkObject receivedObject) { + WorldEvent.Get().UpdateAI(client); + } +} + +[ExtensionCommandHandler("wex.ETS")] // time span +class WorldEventTimeSpanHandler : ICommandHandler { + // rec: {"a":13,"c":1,"p":{"c":"wex.ETS","p":{"en":"","timeSpan":"300"},"r":-1}} + public void Handle(Client client, NetworkObject receivedObject) { + float timeSpan = float.Parse( + receivedObject.Get("p").Get("timeSpan"), + System.Globalization.CultureInfo.InvariantCulture + ); + WorldEvent.Get().SetTimeSpan(client, timeSpan); + } +} diff --git a/src/Core/Room.cs b/src/Core/Room.cs index 3b51c27..94dc6b0 100644 --- a/src/Core/Room.cs +++ b/src/Core/Room.cs @@ -38,6 +38,12 @@ public class Room { } public static Room Get(string name) => rooms[name]; + + public static Room GetOrAdd(string name) { + if (!Room.Exists(name)) + Room.Add(name); + return rooms[name]; + } public static bool Exists(string name) => rooms.ContainsKey(name); diff --git a/src/Core/Utils.cs b/src/Core/Utils.cs index aaac55e..0cf7d4f 100644 --- a/src/Core/Utils.cs +++ b/src/Core/Utils.cs @@ -5,6 +5,8 @@ using System.Linq; using System.Text; using System.Threading.Tasks; +using sodoffmmo.Data; + namespace sodoffmmo.Core; internal static class Utils { public static bool VariablesValid(Client client) { @@ -19,4 +21,32 @@ internal static class Utils { } return true; } + + public static NetworkPacket VlNetworkPacket(NetworkArray vl2, int roomID) { + NetworkObject wedata = new(); + NetworkArray vl = new(); + vl.Add(vl2); + wedata.Add("r", roomID); + wedata.Add("vl", vl); + return NetworkObject.WrapObject(0, 11, wedata).Serialize(); + } + + public static NetworkPacket VlNetworkPacket(string a, string b) { + NetworkArray vl2 = new(); + vl2.Add(a); + vl2.Add((Byte)4); + vl2.Add(b); + vl2.Add(false); + vl2.Add(false); + return VlNetworkPacket(vl2, WorldEvent.Get().GetRoom().Id); + } + + public static NetworkPacket ArrNetworkPacket(string[] data) { + NetworkObject cmd = new(); + NetworkObject obj = new(); + obj.Add("arr", data); + cmd.Add("c", ""); + cmd.Add("p", obj); + return NetworkObject.WrapObject(1, 13, cmd).Serialize(); + } } diff --git a/src/Core/WorldEvent.cs b/src/Core/WorldEvent.cs new file mode 100644 index 0000000..86766bf --- /dev/null +++ b/src/Core/WorldEvent.cs @@ -0,0 +1,168 @@ +using System.Globalization; +using sodoffmmo.Data; + +namespace sodoffmmo.Core; +class WorldEvent { + private static WorldEvent _instance = null; + private object EventLock = new object(); + private Random random = new Random(); + + public static WorldEvent Get() { + if (_instance == null) { + _instance = new WorldEvent(); + } + return _instance; + } + + private WorldEvent() { + Reset(0.5f); + } + + private string uid; + private Room room; + private DateTime startTime; + private DateTime endTime; + private string startTimeString; + private Dictionary health; + private Dictionary players; + private Client operatorAI; + private DateTime AITime; + private bool endTimeIsSet; + + private void Reset(float time) { + startTime = DateTime.UtcNow.AddMinutes(time); + startTimeString = startTime.ToString("MM/dd/yyyy HH:mm:ss"); + uid = Path.GetRandomFileName().Substring(0, 8); // this is used as RandomSeed for random select ship variant + room = Room.GetOrAdd("HubTrainingDO"); + endTime = startTime.AddMinutes(10); + endTimeIsSet = false; + operatorAI = null; + health = new(); + players = new(); + } + + private void InitEvent() { + lock (EventLock) { + if (operatorAI is null || AITime < DateTime.UtcNow) { + var clients = room.Clients.ToList(); + operatorAI = clients[random.Next(0, clients.Count)]; + AITime = DateTime.UtcNow.AddSeconds(3.5); + } else { + return; + } + } + operatorAI.Send(Utils.VlNetworkPacket("WE__AI", operatorAI.PlayerData.Uid)); + Console.WriteLine($"Event AI operator: {operatorAI.PlayerData.Uid}"); + } + + private bool EndEvent(bool force = false) { + bool results = true; + string targets = ""; + foreach (var x in health) { + results = results && (x.Value == 0.0f); + targets += x.Key + ":" + x.Value.ToString("0.0#####", CultureInfo.GetCultureInfo("en-US")) + ","; + } + if (results || force) { + string scores = ""; + foreach (var x in players) { + scores += x.Value + ","; + } + + NetworkPacket packet = Utils.VlNetworkPacket( + "WE_ScoutAttack_End", + $"{uid};{results};{scores};{targets}" + ); + foreach (var roomClient in room.Clients) { + roomClient.Send(packet); + } + + NetworkObject wedata = new(); + NetworkArray vl = new(); + NetworkArray vl1 = new(); + vl1.Add("WE__AI"); + vl1.Add((Byte)0); + vl1.Add(""); + vl1.Add(false); + vl1.Add(false); + vl.Add(vl1); + foreach (var t in health) { + NetworkArray vl2 = new(); + vl2.Add("WEH_" + t.Key); + vl2.Add((Byte)0); + vl2.Add(""); + vl2.Add(false); + vl2.Add(false); + vl.Add(vl2); + NetworkArray vl3 = new(); + vl3.Add("WEF_" + t.Key); + vl3.Add((Byte)0); + vl3.Add(""); + vl3.Add(false); + vl3.Add(false); + vl.Add(vl3); + } + wedata.Add("r", room.Id); + wedata.Add("vl", vl); + packet = NetworkObject.WrapObject(0, 11, wedata).Serialize(); + + Reset(5); + return true; + } + return false; + } + + public string EventInfo() { + return startTimeString + "," + uid + ", false, HubTrainingDO"; + } + + public string EventInfoNext() { + return startTimeString; // TODO on og this was different time (real next event?) + } + + public string GetUid() => uid; + + public Room GetRoom() => room; + + public float UpdateHealth(string targetUid, float updateVal) { + InitEvent(); // TODO better place for this + + if (!health.ContainsKey(targetUid)) + health.Add(targetUid, 1.0f); + health[targetUid] -= updateVal; + + if (health[targetUid] < 0) { + health[targetUid] = 0.0f; + if (EndEvent()) + return -1.0f; + } + + if (endTime < DateTime.UtcNow) { + EndEvent(true); + return -1.0f; + } + + return health[targetUid]; + } + + public void UpdateScore(Client client, string value) { + if (!players.ContainsKey(client)) { + players.Add(client, value); + } else { + players[client] = value; + } + } + + public void UpdateAI(Client client) { + if (client == operatorAI) + AITime = DateTime.UtcNow.AddSeconds(7); + } + + public void SetTimeSpan(Client client, float seconds) { + if (client == operatorAI || !endTimeIsSet) { + endTime = startTime.AddSeconds(seconds); + endTimeIsSet = true; + } + } + + public float GetHealth(string targetUid) => health[targetUid]; +}