From 0f3c0612084d69a81dccc32c24ca0079f1396d22 Mon Sep 17 00:00:00 2001 From: warquys <64769541+warquys@users.noreply.github.com> Date: Mon, 13 Jun 2022 16:55:09 +0200 Subject: [PATCH 1/9] Fix CallPower & DeathMessage --- VT-Api/Core/Command/CommandHandler.cs | 4 -- VT-Api/Core/Command/Commands/CallPower.cs | 2 +- .../Events/EventArguments/PlayerEventArgs.cs | 9 +-- VT-Api/Core/Events/PlayerEvents.cs | 17 +++--- VT-Api/Core/Roles/RoleManager.cs | 12 ++-- .../VtEvent/PlayerPatches/DeathPatch.cs | 59 +++++++++++++++++++ .../PlayerPatches/SynapseDeathPatch.cs | 55 ----------------- .../Patches/VtPatch/CustomDeathReasonPatch.cs | 27 --------- VT-Api/VT-Api.csproj | 3 +- 9 files changed, 80 insertions(+), 108 deletions(-) create mode 100644 VT-Api/Patches/VtEvent/PlayerPatches/DeathPatch.cs delete mode 100644 VT-Api/Patches/VtEvent/PlayerPatches/SynapseDeathPatch.cs delete mode 100644 VT-Api/Patches/VtPatch/CustomDeathReasonPatch.cs diff --git a/VT-Api/Core/Command/CommandHandler.cs b/VT-Api/Core/Command/CommandHandler.cs index 1be781d..0f2589c 100644 --- a/VT-Api/Core/Command/CommandHandler.cs +++ b/VT-Api/Core/Command/CommandHandler.cs @@ -98,10 +98,6 @@ internal void FinalizePluginsCommands() private void RegisterVtCommands() { - if (!_firstLoad) - return; - Logger.Get.Info("Register Command"); - RegisterSynapseCommand(new CallPower(), false); } } diff --git a/VT-Api/Core/Command/Commands/CallPower.cs b/VT-Api/Core/Command/Commands/CallPower.cs index 7102545..baae416 100644 --- a/VT-Api/Core/Command/Commands/CallPower.cs +++ b/VT-Api/Core/Command/Commands/CallPower.cs @@ -14,7 +14,7 @@ namespace VT_Api.Core.Command.Commands Description = "Call the power of your role", Usage = "no argument if you want to call your main power, if not add the id of the power", Permission = "", - Platforms = new[] { Platform.ServerConsole }, + Platforms = new[] { Platform.ClientConsole }, Arguments = new[] { "(powerId)" } )] public class CallPower : ISynapseCommand diff --git a/VT-Api/Core/Events/EventArguments/PlayerEventArgs.cs b/VT-Api/Core/Events/EventArguments/PlayerEventArgs.cs index 8c2b5b7..69d88e6 100644 --- a/VT-Api/Core/Events/EventArguments/PlayerEventArgs.cs +++ b/VT-Api/Core/Events/EventArguments/PlayerEventArgs.cs @@ -1,4 +1,5 @@ -using Synapse.Api; +using PlayerStatsSystem; +using Synapse.Api; using Synapse.Api.Enum; using Synapse.Api.Items; @@ -13,12 +14,12 @@ public class PlayerDamagePostEventArgs : Synapse.Api.Events.EventHandler.ISynaps public DamageType DamageType { get; internal set; } public bool Allow { get; set; } } - public class PlayerDeathPostEventArgs : Synapse.Api.Events.EventHandler.ISynapseEventArgs + public class PlayerKillEventArgs : Synapse.Api.Events.EventHandler.ISynapseEventArgs { public Player Killer { get; internal set; } public Player Victim { get; internal set; } - public DamageType DamageType { get; internal set; } - public bool Allow { get; set; } + public DamageHandlerBase DamageHandler { get; internal set; } + public string DeathReason { get; set; } } diff --git a/VT-Api/Core/Events/PlayerEvents.cs b/VT-Api/Core/Events/PlayerEvents.cs index 6f0d833..71ca176 100644 --- a/VT-Api/Core/Events/PlayerEvents.cs +++ b/VT-Api/Core/Events/PlayerEvents.cs @@ -1,4 +1,5 @@ -using Synapse.Api; +using PlayerStatsSystem; +using Synapse.Api; using Synapse.Api.Enum; using System; using VT_Api.Core.Events.EventArguments; @@ -11,7 +12,7 @@ internal PlayerEvents() { } #region Events public event Synapse.Api.Events.EventHandler.OnSynapseEvent PlayerDamagePostEvent; - public event Synapse.Api.Events.EventHandler.OnSynapseEvent PlayerDeathPostEvent; + public event Synapse.Api.Events.EventHandler.OnSynapseEvent PlayerKillEvent; public event Synapse.Api.Events.EventHandler.OnSynapseEvent PlayerUnloadEvent; public event Synapse.Api.Events.EventHandler.OnSynapseEvent PlayerSpeakIntercomEvent; public event Synapse.Api.Events.EventHandler.OnSynapseEvent PlayerSetClassAdvEvent; @@ -30,19 +31,19 @@ internal void InvokePlayerSetClassAdvEvent(Player player, RoleType role) PlayerSetClassAdvEvent?.Invoke(ev); } - internal void InvokePlayerDeathPostEvent(Player victim, Player killer, DamageType type, ref bool allow) + internal void InvokePlayerKillEvent(Player victim, Player killer, DamageHandlerBase handler, ref string victimeDeathReason) { - var ev = new PlayerDeathPostEventArgs + var ev = new PlayerKillEventArgs { - Allow = allow, - DamageType = type, + DamageHandler = handler, + DeathReason = victimeDeathReason, Killer = killer, Victim = victim }; - PlayerDeathPostEvent?.Invoke(ev); + PlayerKillEvent?.Invoke(ev); - allow = ev.Allow; + victimeDeathReason = ev.DeathReason; } internal void InvokePlayerSpeakIntercomEvent(Player player, ref bool allow) diff --git a/VT-Api/Core/Roles/RoleManager.cs b/VT-Api/Core/Roles/RoleManager.cs index 6498766..b6fe836 100644 --- a/VT-Api/Core/Roles/RoleManager.cs +++ b/VT-Api/Core/Roles/RoleManager.cs @@ -46,7 +46,7 @@ internal void Init() Synapse.Api.Events.EventHandler.Get.Player.PlayerKeyPressEvent += OnPressKey; Synapse.Api.Events.EventHandler.Get.Server.UpdateEvent += OnUpdate; Synapse.Api.Events.EventHandler.Get.Server.TransmitPlayerDataEvent += OnTransmitPlayerData; - VtController.Get.Events.Player.PlayerDeathPostEvent += OnPlayerDeath; + VtController.Get.Events.Player.PlayerKillEvent += OnPlayerDeath; } @@ -144,11 +144,8 @@ private void OnSetClass(PlayerSetClassEventArgs ev) } } - private void OnPlayerDeath(PlayerDeathPostEventArgs ev) + private void OnPlayerDeath(PlayerKillEventArgs ev) { - if (!ev.Allow) - return; - if (OldPlayerRole.ContainsKey(ev.Victim)) OldPlayerRole[ev.Victim] = ev.Victim.RoleID; else @@ -160,14 +157,15 @@ private void OnPlayerDeath(PlayerDeathPostEventArgs ev) message = Regex.Replace(message, "%PlayerName%", ev.Killer.DisplayName, RegexOptions.IgnoreCase); message = Regex.Replace(message, "%RoleName%", role.GetRoleName(), RegexOptions.IgnoreCase); - Patches.VtPatch.CustomDeathReasonPatch.CustomReason = message; + ev.DeathReason = message; } if (ev.Victim.CustomRole is IScpDeathAnnonce scpDeathAnnonce) { var scpName = scpDeathAnnonce.ScpName; var unityName = ev.Killer?.Team == Team.MTF ? ev.Killer.UnitName : "UNKNOWN"; - Server.Get.Map.AnnounceScpDeath(scpName, ev.DamageType.GetScpRecontainmentType(ev.Killer), unityName); + var deathType = ev.DamageHandler.GetDamageType().GetScpRecontainmentType(ev.Killer); + Server.Get.Map.AnnounceScpDeath(scpName, deathType, unityName); } } diff --git a/VT-Api/Patches/VtEvent/PlayerPatches/DeathPatch.cs b/VT-Api/Patches/VtEvent/PlayerPatches/DeathPatch.cs new file mode 100644 index 0000000..7ac80ab --- /dev/null +++ b/VT-Api/Patches/VtEvent/PlayerPatches/DeathPatch.cs @@ -0,0 +1,59 @@ +using HarmonyLib; +using InventorySystem; +using PlayerStatsSystem; +using Synapse.Api; +using Synapse.Api.Enum; +using Synapse.Api.Events; +using Synapse.Api.Events.SynapseEventArguments; +using Synapse.Api.Items; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using VT_Api.Reflexion; + +namespace VT_Api.Patches.VtEvent.PlayerPatches +{ + [HarmonyPatch(typeof(PlayerStats), nameof(PlayerStats.KillPlayer))] + class DeathPatch + { + [HarmonyPrefix] + [HarmonyAfter("synapse.patches")] + private static bool DeathEventPatch(PlayerStats __instance, DamageHandlerBase handler) + { + try + { + var victim = __instance.GetPlayer(); + var attacker = handler is AttackerDamageHandler ahandler ? ahandler.Attacker.GetPlayer() : null; + var allow = true; + string reason = null; + + VtController.Get.Events.Player.InvokePlayerKillEvent(victim, attacker, handler, ref reason); + + Ragdoll.ServerSpawnRagdoll(__instance._hub, handler); + + if (reason != null) + handler = new CustomReasonDamageHandler(reason); + + if (handler is AttackerDamageHandler attackerDamageHandler) + __instance.TargetReceiveAttackerDeathReason(attackerDamageHandler.Attacker.Nickname, attackerDamageHandler.Attacker.Role); + else + __instance.TargetReceiveSpecificDeathReason(handler); + + __instance._hub.inventory.ServerDropEverything(); + CharacterClassManager characterClassManager = __instance._hub.characterClassManager; + characterClassManager.SetClassID(RoleType.Spectator, CharacterClassManager.SpawnReason.Died); + characterClassManager.TargetConsolePrint(characterClassManager.connectionToClient, "You died. Reason: " + handler.ServerLogsText, "yellow"); + + return false; + } + catch (Exception e) + { + Synapse.Api.Logger.Get.Error($"Vt-Event: PlayerDamagePost failed!!\n{e}\nStackTrace:\n{e.StackTrace}"); + return true; + } + } + + } +} diff --git a/VT-Api/Patches/VtEvent/PlayerPatches/SynapseDeathPatch.cs b/VT-Api/Patches/VtEvent/PlayerPatches/SynapseDeathPatch.cs deleted file mode 100644 index 074dfb6..0000000 --- a/VT-Api/Patches/VtEvent/PlayerPatches/SynapseDeathPatch.cs +++ /dev/null @@ -1,55 +0,0 @@ -using HarmonyLib; -using Synapse.Api; -using Synapse.Api.Enum; -using Synapse.Api.Events; -using Synapse.Api.Events.SynapseEventArguments; -using Synapse.Api.Items; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using VT_Api.Reflexion; - -namespace VT_Api.Patches.VtEvent.PlayerPatches -{ - [HarmonyPatch(typeof(PlayerEvents), "InvokePlayerDeathEvent")] - class SynapseDeathPatch - { - [HarmonyPrefix] - private static bool DeathEventPatch(PlayerEvents __instance, Player victim, Player killer, DamageType type, out bool allow) - { - var ev = new PlayerDeathEventArgs(); - try - { - - ev.Allow = true; - ev.SetProperty("Killer", killer); - ev.SetProperty("Victim", victim); - ev.SetProperty("DamageType", type); - - __instance.CallEvent("PlayerDeathEvent", ev); - - allow = ev.Allow; - } - catch (Exception e) - { - allow = ev.Allow; - Logger.Get.Error($"Synapse-Event: PlayerDeath event failed!!\n{e}"); - return false; - } - try - { - VtController.Get.Events.Player.InvokePlayerDeathPostEvent(victim, killer, type, ref allow); - - return false; - } - catch (Exception e) - { - Synapse.Api.Logger.Get.Error($"Vt-Event: PlayerDamagePost failed!!\n{e}\nStackTrace:\n{e.StackTrace}"); - return true; - } - } - - } -} diff --git a/VT-Api/Patches/VtPatch/CustomDeathReasonPatch.cs b/VT-Api/Patches/VtPatch/CustomDeathReasonPatch.cs deleted file mode 100644 index 8e1d8c2..0000000 --- a/VT-Api/Patches/VtPatch/CustomDeathReasonPatch.cs +++ /dev/null @@ -1,27 +0,0 @@ -using HarmonyLib; -using Mirror; -using PlayerStatsSystem; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace VT_Api.Patches.VtPatch -{ - [HarmonyPatch(typeof(PlayerStats), nameof(PlayerStats.TargetReceiveSpecificDeathReason))] - class CustomDeathReasonPatch - { - public static string CustomReason { get; set; } - - [HarmonyPrefix] - public static void TargetReceiveSpecificDeathReason(PlayerStats __instance, DamageHandlerBase handler) - { - if (CustomReason != null) - { - handler = new CustomReasonDamageHandler(CustomReason); - CustomReason = null; - } - } - } -} diff --git a/VT-Api/VT-Api.csproj b/VT-Api/VT-Api.csproj index 270f20a..10015d7 100644 --- a/VT-Api/VT-Api.csproj +++ b/VT-Api/VT-Api.csproj @@ -144,10 +144,9 @@ - + - From 1345f9dd522373a9b01d63986cfd7a8abe4076e9 Mon Sep 17 00:00:00 2001 From: warquys <64769541+warquys@users.noreply.github.com> Date: Sat, 25 Jun 2022 22:14:40 +0200 Subject: [PATCH 2/9] base of Npc --- VT-Api/Config/Config.cs | 2 +- VT-Api/Core/Enum/NpcPathPointType.cs | 15 ++++ VT-Api/Core/NPC/NPC.cs | 49 ++++++++++ VT-Api/Core/NPC/NpcManger.cs | 54 +++++++++++ VT-Api/Core/NPC/NpcMovementController.cs | 77 ++++++++++++++++ VT-Api/Core/NPC/Path/NpcMapPath.cs | 90 +++++++++++++++++++ VT-Api/Core/NPC/Path/NpcPath.cs | 78 ++++++++++++++++ VT-Api/Core/NPC/Path/NpcPathPoint.cs | 46 ++++++++++ VT-Api/Core/NetworkLiar.cs | 2 +- VT-Api/Core/Roles/AbstractRole.cs | 12 ++- .../Display.cs => Roles/CustomDisplay.cs} | 27 +++--- VT-Api/Core/Roles/ICustomPhysicalRole.cs | 4 +- VT-Api/Core/Roles/RoleManager.cs | 23 +++-- VT-Api/Core/Teams/TeamManager.cs | 9 +- VT-Api/Core/VtExtensions.cs | 2 +- VT-Api/Properties/AssemblyInfo.cs | 4 +- VT-Api/VT-Api.csproj | 10 ++- VT-Api/VtController.cs | 3 + 18 files changed, 475 insertions(+), 32 deletions(-) create mode 100644 VT-Api/Core/Enum/NpcPathPointType.cs create mode 100644 VT-Api/Core/NPC/NPC.cs create mode 100644 VT-Api/Core/NPC/NpcManger.cs create mode 100644 VT-Api/Core/NPC/NpcMovementController.cs create mode 100644 VT-Api/Core/NPC/Path/NpcMapPath.cs create mode 100644 VT-Api/Core/NPC/Path/NpcPath.cs create mode 100644 VT-Api/Core/NPC/Path/NpcPathPoint.cs rename VT-Api/Core/{Behaviour/Display.cs => Roles/CustomDisplay.cs} (93%) diff --git a/VT-Api/Config/Config.cs b/VT-Api/Config/Config.cs index 68a02c1..68a80db 100644 --- a/VT-Api/Config/Config.cs +++ b/VT-Api/Config/Config.cs @@ -36,7 +36,7 @@ internal void Init() RankOver = "VOUS POUVEZ LUI DONNER DES ORDRES", RankSame = "MÊME NIVEAU D'ACCRÉDITATION", RankUnder = "SUIVEZ SES ORDRES", - DefaultDeathMessage = "Vous avez été tué par:\\n%PlayerName%\\nen tent que:\\n%RoleName%" + DefaultDeathMessage = "Vous avez été tué par:\\n%PlayerName%\\nen tant que:\\n%RoleName%" }, "FRENCH"); diff --git a/VT-Api/Core/Enum/NpcPathPointType.cs b/VT-Api/Core/Enum/NpcPathPointType.cs new file mode 100644 index 0000000..328ae65 --- /dev/null +++ b/VT-Api/Core/Enum/NpcPathPointType.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace VT_Api.Core.Enum +{ + public enum NpcPathPointType + { + Path = 0, + Door = 1, + Elevator = 4, + } +} diff --git a/VT-Api/Core/NPC/NPC.cs b/VT-Api/Core/NPC/NPC.cs new file mode 100644 index 0000000..afa7e18 --- /dev/null +++ b/VT-Api/Core/NPC/NPC.cs @@ -0,0 +1,49 @@ +using Synapse.Api; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using UnityEngine; + +namespace VT_Api.Core.NPC +{ + public class NPC : Dummy + { + + #region Properties & Variable + + private static uint HighestID; + public uint ID { get; private set; } + public NpcMovementController MovementController { get; private set; } + public Vector3? GoTo { get => MovementController.GoTo; set => MovementController.GoTo = value; } + + #endregion + + #region Constructor & Destructor + public NPC(Vector3 pos, Quaternion rot, RoleType role = RoleType.ClassD, string name = "(null)", string badgetext = "", string badgecolor = "") : this(pos, new Vector2(rot.eulerAngles.x, rot.eulerAngles.y), role, name, badgetext, badgecolor) { } + + public NPC(Vector3 pos, Vector2 rot, RoleType role = RoleType.ClassD, string name = "(null)", string badgetext = "", string badgecolor = "") : base(pos, rot, role, name, badgetext, badgecolor) + { + MovementController = GameObject.AddComponent(); + ID = HighestID; + HighestID++; + } + #endregion + + #region Methods + internal static void ResetID() => HighestID = 0; + + public new static NPC CreateDummy(Vector3 pos, Quaternion rot, RoleType role = RoleType.ClassD, string name = "(null)", string badgetext = "", string badgecolor = "") + => new NPC(pos, rot, role, name, badgetext, badgecolor); + #endregion + + #region Events + #endregion + + + + + + } +} diff --git a/VT-Api/Core/NPC/NpcManger.cs b/VT-Api/Core/NPC/NpcManger.cs new file mode 100644 index 0000000..c4322c3 --- /dev/null +++ b/VT-Api/Core/NPC/NpcManger.cs @@ -0,0 +1,54 @@ +using Synapse; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace VT_Api.Core.NPC +{ + public class NpcManger + { + #region Properties & Variable + public Dictionary NPCs { get; } = new Dictionary(); + public List NpcPathPoints { get; } = new List(); + public List NpcMaps { get; } = new List(); + + public static NpcManger Get => VtController.Get.Npc; + #endregion + + #region Constructor & Destructor + internal NpcManger() { } + #endregion + + #region Methods + internal void Init() + { + Server.Get.Events.Round.RoundEndEvent += OnEnd; + Server.Get.Events.Round.WaitingForPlayersEvent += OnWhait; + } + + private void OnWhait() + { + + } + + private void OnEnd() + { + NPC.ResetID(); + NpcPathPoint.ResetID(); + foreach (var npc in NPCs) + npc.Value.Despawn(); + NPCs.Clear(); + NpcPathPoints.Clear(); + NpcMaps.Clear(); + } + #endregion + + #region Events + #endregion + + + + } +} diff --git a/VT-Api/Core/NPC/NpcMovementController.cs b/VT-Api/Core/NPC/NpcMovementController.cs new file mode 100644 index 0000000..154b0b2 --- /dev/null +++ b/VT-Api/Core/NPC/NpcMovementController.cs @@ -0,0 +1,77 @@ +using Synapse.Api; +using Synapse.Api.Enum; +using System.Collections.Generic; +using System.Linq; +using UnityEngine; +using VT_Api.Core.Behaviour; + +namespace VT_Api.Core.NPC +{ + public class NpcMovementController : RepeatingBehaviour + { + #region Attributes & Properties + + public NPC NPC { get; private set; } + + public NpcMapPath Zone { get; set; } + public Vector3? GoTo + { + get => CurentPath?.End.Postion; + set + { + if (value == null) + { + enabled = false; + CurentPath = null; + } + else + { + CurentPath = NpcPath.FoundPath(NPC.Position, value.Value, Zone); + enabled = true; + } + } + } + public NpcPath CurentPath { get; private set; } + + + #endregion + + #region Constructors & Destructor + NpcMovementController() + { + this.RefreshTime = 100; + var player = gameObject.GetPlayer(); + NPC = (NPC)Map.Get.Dummies.FirstOrDefault(x => x.Player == player); + } + #endregion + + #region Methods + protected override void OnDisable() + { + NPC.Direction = MovementDirection.Stop; + base.OnDisable(); + } + + protected override void OnEnable() + { + if (GoTo != null) + CurentPath = NpcPath.FoundPath(NPC.Position, GoTo.Value, Zone); + base.OnEnable(); + } + + protected override void BehaviourAction() + { + if (CurentPath == null) + enabled = false; + NPC.RotateToPosition(CurentPath.NextPostion); + if (Vector3.Distance(NPC.Position, CurentPath.NextPostion) < 1f) + { + if (CurentPath.LastTravels) + CurentPath = null; + else + CurentPath.NextPoint(); + } + } + #endregion + } +} \ No newline at end of file diff --git a/VT-Api/Core/NPC/Path/NpcMapPath.cs b/VT-Api/Core/NPC/Path/NpcMapPath.cs new file mode 100644 index 0000000..1d34946 --- /dev/null +++ b/VT-Api/Core/NPC/Path/NpcMapPath.cs @@ -0,0 +1,90 @@ +using Synapse.Api.Enum; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using UnityEngine; + +namespace VT_Api.Core.NPC +{ + public class NpcMapPath + { + #region Attributes & Properties + public string Name { get; set; } + public List Points { get; } = new List(); + public List> Path { get; } = new List>(); + public List LinkedZone { get; } = new List(); + #endregion + + #region Constructors & Destructor + public NpcMapPath(string name) + { + Name = name; + NpcManger.Get.NpcMaps.Add(this); + } + #endregion + + #region Methods + public void AddPath(NpcPathPoint beginning, NpcPathPoint end, bool reciprocal = true) + { + if (beginning.Zone == null) + beginning.Zone = this; + if (end.Zone == null) + end.Zone = this; + + Path.Add(new KeyValuePair(beginning, end)); + if (reciprocal) + Path.Add(new KeyValuePair(end, beginning)); + } + + private float Distance(NpcPathPoint beginning, NpcPathPoint end) + => Vector3.Distance(beginning.Postion, end.Postion); + + public float Distance(List path) + { + var resultat = 0f; + for (int i = 0; i < path.Count - 1; i++) + resultat += Distance(path[i], path[i + 1]); + return resultat; + } + + public List ShorterPath(NpcPathPoint beginning, NpcPathPoint end) + { + if (beginning.Zone != end.Zone) + return null; + + var resultat = new List>(); + GetPaths(beginning, end, new List(), ref resultat); + + if (!resultat.Any()) + return null; + + var orderBy = resultat.OrderBy(p => Distance(p)); + return orderBy.First(); + } + + private void GetPaths(NpcPathPoint beginning, NpcPathPoint end, List alreadyPass, ref List> result) + { + if (beginning == end) + { + alreadyPass.Add(beginning); + result.Add(alreadyPass); + } + + var listPossible = Path.Where(p => p.Key == beginning && !alreadyPass.Any(pt => pt == p.Value)); + if (!listPossible.Any()) + return; + + foreach (var element in listPossible) + { + List path = new List(); + path.AddRange(alreadyPass); + path.Add(beginning); + var newPtDepart = Points.First(p => element.Value == p); + GetPaths(newPtDepart, end, path, ref result); + } + } + #endregion + } +} diff --git a/VT-Api/Core/NPC/Path/NpcPath.cs b/VT-Api/Core/NPC/Path/NpcPath.cs new file mode 100644 index 0000000..5c91ffa --- /dev/null +++ b/VT-Api/Core/NPC/Path/NpcPath.cs @@ -0,0 +1,78 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using UnityEngine; + +namespace VT_Api.Core.NPC +{ + public class NpcPath + { + #region Properties & Variable + public bool LastTravels { get; private set; } + public NpcPathPoint Beginning { get; private set; } + public NpcPathPoint End { get; private set; } + public Vector3 EndPostion { get; private set; } + public Vector3 NextPostion { get; private set; } + + public NpcPathPoint _Curent; + public NpcPathPoint Curent + { + get => _Curent; + set + { + _Curent = value; + Refresh(); + } + } + + public List Path { get; private set; } + #endregion + + #region Constructor & Destructor + public NpcPath(Vector3 beginning, Vector3 end, NpcMapPath map) + { + End = NpcPathPoint.Found(end, map); + Beginning = NpcPathPoint.Found(beginning, map); + EndPostion = end; + Curent = null; + NextPostion = beginning; + Path = map.ShorterPath(Beginning, End); + } + #endregion + + #region Methods + public static NpcPath FoundPath(Vector3 beginning, Vector3 end, NpcMapPath map) + => new NpcPath(end, beginning, map); + + public void NextPoint() + { + var nextIndex = Path.IndexOf(Curent) + 1; + if (Path.Count == nextIndex) + { + Curent = Path.Last(); + NextPostion = EndPostion; + LastTravels = true; + return; + } + Curent = Path[nextIndex]; + NextPostion = Path[nextIndex + 1].Postion; + } + + private void Refresh() + { + var index = Path.IndexOf(Curent); + if (Path.Count == index + 1) + { + Curent = Path[index]; + NextPostion = EndPostion; + LastTravels = true; + return; + } + Curent = Path[index]; + NextPostion = Path[index + 1].Postion; + } + #endregion + } +} diff --git a/VT-Api/Core/NPC/Path/NpcPathPoint.cs b/VT-Api/Core/NPC/Path/NpcPathPoint.cs new file mode 100644 index 0000000..3e51004 --- /dev/null +++ b/VT-Api/Core/NPC/Path/NpcPathPoint.cs @@ -0,0 +1,46 @@ +using Synapse.Api; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using UnityEngine; +using VT_Api.Core.Enum; + +namespace VT_Api.Core.NPC +{ + public class NpcPathPoint + { + #region Properties & Variable + private static uint HighestID; + + public NpcPathPointType Type { get; private set; } + public uint ID { get; private set; } + public Vector3 Postion { get; private set; } + public Room Room { get; private set; } + public NpcMapPath Zone { get; internal set; } + #endregion + + #region Constructor & Destructor + public NpcPathPoint(NpcPathPointType type, MapPoint mapPoint) : this(type, mapPoint.Position, mapPoint.Room) { } + + public NpcPathPoint(NpcPathPointType type, Vector3 postion, Room room = null) + { + Type = type; + Postion = postion; + Room = room; + ID = HighestID; + HighestID++; + NpcManger.Get.NpcPathPoints.Add(this); + } + #endregion + + #region Methods + internal static NpcPathPoint Found(Vector3 postion, NpcMapPath zone) + => zone.Points.OrderBy(p => Vector3.Distance(p.Postion, postion)).FirstOrDefault(); + + internal static void ResetID() => HighestID = 0; + + #endregion + } +} diff --git a/VT-Api/Core/NetworkLiar.cs b/VT-Api/Core/NetworkLiar.cs index ffd2ab8..12bb393 100644 --- a/VT-Api/Core/NetworkLiar.cs +++ b/VT-Api/Core/NetworkLiar.cs @@ -49,7 +49,7 @@ public void SendRole(Player player, RoleType info, List players) SendAndRecycle(owner, observer, players, player); } - public void SebdInfoToDisplay(Player player, PlayerInfoArea info, List players) + public void SendInfoToDisplay(Player player, PlayerInfoArea info, List players) { const byte bytecodes = 4; diff --git a/VT-Api/Core/Roles/AbstractRole.cs b/VT-Api/Core/Roles/AbstractRole.cs index 642eb01..fe56d77 100644 --- a/VT-Api/Core/Roles/AbstractRole.cs +++ b/VT-Api/Core/Roles/AbstractRole.cs @@ -1,4 +1,5 @@ -using Synapse; +using Respawning; +using Synapse; using Synapse.Api; using Synapse.Api.Enum; using Synapse.Api.Events.SynapseEventArguments; @@ -10,6 +11,7 @@ using System.Text.RegularExpressions; using VT_Api.Config; using VT_Api.Core.Behaviour; +using VT_Api.Core.Teams; using VT_Api.Extension; namespace VT_Api.Core.Roles @@ -160,6 +162,13 @@ public sealed override void Spawn() Player.RoleType = RoleType; + if (Player.Team == Team.MTF) + { + var units = TeamManager.Get.NamingManager.AllUnitNames; + var lastNtfUnit = units.LastOrDefault(p => p.SpawnableTeam == (byte)SpawnableTeamType.NineTailedFox); + Player.UnitName = string.IsNullOrEmpty(lastNtfUnit.UnitName) ? null : lastNtfUnit.UnitName; + } + if (!string.IsNullOrEmpty(SpawnMessage)) { string message = Regex.Replace(SpawnMessage, "%RoleName%", RoleName, RegexOptions.IgnoreCase); @@ -167,6 +176,7 @@ public sealed override void Spawn() } SetDisplayInfo(); + if (Config != null) { if (Config.Health != null) diff --git a/VT-Api/Core/Behaviour/Display.cs b/VT-Api/Core/Roles/CustomDisplay.cs similarity index 93% rename from VT-Api/Core/Behaviour/Display.cs rename to VT-Api/Core/Roles/CustomDisplay.cs index 3e10f0f..7208531 100644 --- a/VT-Api/Core/Behaviour/Display.cs +++ b/VT-Api/Core/Roles/CustomDisplay.cs @@ -7,12 +7,12 @@ using System.Text; using System.Threading.Tasks; using UnityEngine; +using VT_Api.Core.Behaviour; using VT_Api.Core.Enum; using VT_Api.Core.Events.EventArguments; -using VT_Api.Core.Roles; using VT_Api.Extension; -namespace VT_Api.Core.Behaviour +namespace VT_Api.Core.Roles { //TODO: /* @@ -37,7 +37,7 @@ namespace VT_Api.Core.Behaviour * * */ - public class Display : RoundBehaviour + public class CustomDisplay : RepeatingBehaviour { #region Properties & Variable public Player Player { get; private set; } @@ -87,11 +87,18 @@ public int HierarchyPower } } - const int delata = 5; //this number must be a divisor of 255 + const int delata = 5; //this number must be a divisor of 255 (for the rambo color) private float delay = Time.time; int r = 255, g = 0, b = 0; #endregion + #region Constructor & Destructor + public CustomDisplay() + { + this.RefreshTime = delata * 1000; + } + #endregion + #region Methods private void Start() { @@ -104,14 +111,8 @@ private void Start() } } - private void Update() + protected override void BehaviourAction() { - if (Player.RankColor != "RAINBOW") - { - enabled = false; - return; - } - if (Time.time >= delay) { delay += delata; @@ -215,13 +216,13 @@ public string BuildCustomInfo() displayWhitHierachy += Config.Config.Get.VtTranslation.ActiveTranslation.RankSame; } NetworkLiar.Get.SendDisplayInfo(Player, displayWhitHierachy, new List() { player }); - NetworkLiar.Get.SebdInfoToDisplay(Player, PlayerInfoArea.CustomInfo, Server.Get.Players); + NetworkLiar.Get.SendInfoToDisplay(Player, PlayerInfoArea.CustomInfo, Server.Get.Players); } } else { NetworkLiar.Get.SendDisplayInfo(Player, display, Server.Get.Players); - NetworkLiar.Get.SebdInfoToDisplay(Player, PlayerInfoArea.CustomInfo, Server.Get.Players); + NetworkLiar.Get.SendInfoToDisplay(Player, PlayerInfoArea.CustomInfo, Server.Get.Players); } } #endregion diff --git a/VT-Api/Core/Roles/ICustomPhysicalRole.cs b/VT-Api/Core/Roles/ICustomPhysicalRole.cs index d183e02..b8cf162 100644 --- a/VT-Api/Core/Roles/ICustomPhysicalRole.cs +++ b/VT-Api/Core/Roles/ICustomPhysicalRole.cs @@ -1,9 +1,11 @@ -using Synapse.Api.Roles; +using Synapse.Api; +using Synapse.Api.Roles; namespace VT_Api.Core.Roles { public interface ICustomPhysicalRole : IRole { void UpdateBody(); + bool CanBySee(Player player); } } diff --git a/VT-Api/Core/Roles/RoleManager.cs b/VT-Api/Core/Roles/RoleManager.cs index b6fe836..a909af9 100644 --- a/VT-Api/Core/Roles/RoleManager.cs +++ b/VT-Api/Core/Roles/RoleManager.cs @@ -52,7 +52,6 @@ internal void Init() public bool IsVanilla(int roleID) => roleID > (int)RoleID.None && roleID <= SynRoleManager.HighestRole; - public int GetHierachy(int roleID) { @@ -124,13 +123,17 @@ private void OnSetClass(PlayerSetClassEventArgs ev) { try { - role.InitAll(ev); + role.InitAll(ev); + //ev.Player.GetOrAddComponent().enabled = true; + } catch (Exception ex) { Synapse.Api.Logger.Get.Error($"Fail to init the role {role.GetRoleName()} (ID : {role.GetRoleID()}) :\n{ex}"); } } + //else if (ev.Player.gameObject.TryGetComponent(out var display)) + // display.enabled = false; if (ev.Player.CustomRole is ICustomPhysicalRole customPhyRole) { if (!CustomPhysicaleRoles.Contains(customPhyRole)) @@ -171,21 +174,23 @@ private void OnPlayerDeath(PlayerKillEventArgs ev) private void OnUpdate() { - foreach (var utr in CustomPhysicaleRoles) + foreach (var role in CustomPhysicaleRoles) { - utr.UpdateBody(); + role.UpdateBody(); } } private void OnTransmitPlayerData(TransmitPlayerDataEventArgs ev) { - if (ev.PlayerToShow == ev.Player) - return; - var utr = CustomPhysicaleRoles.FirstOrDefault(p => p.Player == ev.PlayerToShow); - if (utr == null) + if (ev.PlayerToShow == ev.Player || ev.PlayerToShow.CustomRole is not ICustomPhysicalRole customRole) return; - if (ev.Player.RoleID != (int)RoleID.Staff) + + if (ev.Player.RoleID == (int)RoleID.Staff || customRole.CanBySee(ev.Player)) ev.Invisible = false; + else if (ev.Player.RoleType == RoleType.Spectator && ev.Player.CurrentlySpectating == ev.PlayerToShow) + ev.Position += UnityEngine.Vector3.forward * 1.5f; + else + ev.Invisible = true; } #endregion } diff --git a/VT-Api/Core/Teams/TeamManager.cs b/VT-Api/Core/Teams/TeamManager.cs index 3e1d728..4101ef1 100644 --- a/VT-Api/Core/Teams/TeamManager.cs +++ b/VT-Api/Core/Teams/TeamManager.cs @@ -12,6 +12,7 @@ using rnd = UnityEngine.Random; using VT_Api.Reflexion; using VT_Api.Core.Roles; +using Respawning; namespace VT_Api.Core.Teams { @@ -21,6 +22,10 @@ public class TeamManager #region Properties & Variable public static TeamManager Get => VtController.Get.Team; public RespawnTeamInfo NextRespawnInfo { get; set; } = new RespawnTeamInfo(); + + public RespawnManager RespawnManager { get => RespawnManager.Singleton; } + + public UnitNamingManager NamingManager { get => RespawnManager.NamingManager; } #endregion #region Constructor & Destructor @@ -33,6 +38,8 @@ internal void Init() Synapse.Api.Events.EventHandler.Get.Round.TeamRespawnEvent += OnRespawn; } + + public int GetTeam(int roleID) { if (RoleManager.Get.IsVanilla(roleID)) @@ -59,7 +66,7 @@ public string GenerateNtfUnitName(byte maxNubmer = 20) #region Events private void OnRespawn(TeamRespawnEventArgs ev) { - if (NextRespawnInfo.TeamID == (int)TeamID.None) + if (NextRespawnInfo == null || NextRespawnInfo.TeamID == (int)TeamID.None) return; if (NextRespawnInfo.AmountOfPlayers != -1) diff --git a/VT-Api/Core/VtExtensions.cs b/VT-Api/Core/VtExtensions.cs index b566675..992c519 100644 --- a/VT-Api/Core/VtExtensions.cs +++ b/VT-Api/Core/VtExtensions.cs @@ -63,7 +63,7 @@ public static Player GetDeadPlayerInRangeOfPlayer(this Player player, float rang { var players = MapAndRoundManger.Get.GetRagdollOwners(player, range); - players.RemoveAll(p => p.Team == Team.RIP && !p.OverWatch); + players.RemoveAll(p => p.Team != Team.RIP || p.OverWatch); return players.FirstOrDefault(); } diff --git a/VT-Api/Properties/AssemblyInfo.cs b/VT-Api/Properties/AssemblyInfo.cs index 6b67918..61b7dc4 100644 --- a/VT-Api/Properties/AssemblyInfo.cs +++ b/VT-Api/Properties/AssemblyInfo.cs @@ -32,5 +32,5 @@ // Vous pouvez spécifier toutes les valeurs ou indiquer les numéros de build et de révision par défaut // en utilisant '*', comme indiqué ci-dessous : // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.2.0.0")] -[assembly: AssemblyFileVersion("1.2.0.0")] +[assembly: AssemblyVersion("1.2.1.0")] +[assembly: AssemblyFileVersion("1.2.1.0")] diff --git a/VT-Api/VT-Api.csproj b/VT-Api/VT-Api.csproj index 10015d7..d4840ed 100644 --- a/VT-Api/VT-Api.csproj +++ b/VT-Api/VT-Api.csproj @@ -49,10 +49,11 @@ - + + @@ -86,6 +87,12 @@ + + + + + + @@ -159,7 +166,6 @@ - diff --git a/VT-Api/VtController.cs b/VT-Api/VtController.cs index 2468f03..57d959e 100644 --- a/VT-Api/VtController.cs +++ b/VT-Api/VtController.cs @@ -16,6 +16,7 @@ using EventHandler = VT_Api.Core.Events.EventHandler; using VT_Api.Core.Behaviour; +using VT_Api.Core.NPC; public class VtController { @@ -30,6 +31,7 @@ public class VtController public MapAndRoundManger MapAction { get => Singleton.Instance; } public NetworkLiar NetworkLiar { get => Singleton.Instance; } public ItemManager Item { get => Singleton.Instance; } + public NpcManger Npc { get => Singleton.Instance; } internal CommandHandler Commands { get => Singleton.Instance; } // nothing public (yet) public Config Configs { get => Singleton.Instance; } @@ -99,6 +101,7 @@ private void InitAll() Team.Init(); i++; Role.Init(); i++; Item.Init(); i++; + Npc.Init(); i++; } catch (Exception e) { From 8c23ac5bf6a8b5b8ba3ecefacb3f98b5940679f5 Mon Sep 17 00:00:00 2001 From: warquys <64769541+warquys@users.noreply.github.com> Date: Wed, 29 Jun 2022 22:41:06 +0200 Subject: [PATCH 3/9] Audio --- VT-Api/Config/Config.cs | 16 ++- VT-Api/Core/Audio/AudioManager.cs | 75 +++++++++++ VT-Api/Core/Audio/Controller.cs | 121 ++++++++++++++++++ VT-Api/Core/Audio/FakeMicrophone.cs | 95 ++++++++++++++ VT-Api/Core/Command/CommandHandler.cs | 23 +++- VT-Api/Core/Items/ItemManager.cs | 4 +- VT-Api/Core/MapAndRoundManger.cs | 4 +- VT-Api/Core/MiniGame/MiniGameManager.cs | 2 +- VT-Api/Core/NPC/NpcManger.cs | 4 +- VT-Api/Core/NetworkLiar.cs | 2 +- VT-Api/Core/Roles/RoleManager.cs | 5 +- VT-Api/Core/Teams/TeamManager.cs | 2 +- VT-Api/Patches/VtPatch/DissonanceHostPatch.cs | 18 +++ VT-Api/VT-Api.csproj | 10 +- VT-Api/VtController.cs | 35 +++-- ...Voip-Publicized.dll => DissonanceVoip.dll} | Bin 270848 -> 267776 bytes 16 files changed, 379 insertions(+), 37 deletions(-) create mode 100644 VT-Api/Core/Audio/AudioManager.cs create mode 100644 VT-Api/Core/Audio/Controller.cs create mode 100644 VT-Api/Core/Audio/FakeMicrophone.cs create mode 100644 VT-Api/Patches/VtPatch/DissonanceHostPatch.cs rename ref/{DissonanceVoip-Publicized.dll => DissonanceVoip.dll} (52%) diff --git a/VT-Api/Config/Config.cs b/VT-Api/Config/Config.cs index 68a80db..08f2711 100644 --- a/VT-Api/Config/Config.cs +++ b/VT-Api/Config/Config.cs @@ -1,21 +1,26 @@ using Synapse.Config; using Synapse.Translation; +using VT_Api.Core; using VT_Api.Reflexion; namespace VT_Api.Config { public class Config { - public static Config Get { get => VtController.Get.Configs; } - public VtApiConfiguration VtConfiguration; + #region Properties & Variable + public static Config Get => Singleton.Instance; - - public SynapseTranslation VtTranslation; + public VtApiConfiguration VtConfiguration { get; private set; } + public SynapseTranslation VtTranslation { get; private set; }; public SynapseConfiguration SynapseConfiguration => Synapse.Server.Get.Configs.GetFieldValueOrPerties("synapseConfiguration"); + #endregion + #region Constructor & Destructor + internal Config() { } + #endregion - + #region Methods internal void Init() { VtTranslation = new SynapseTranslation(SynapseController.Server.Files.GetTranslationPath("Vt-Api")); @@ -43,5 +48,6 @@ internal void Init() VtConfiguration = new VtApiConfiguration(); VtConfiguration = Synapse.Server.Get.Configs.GetOrSetDefault("VT-API", new VtApiConfiguration()); } + #endregion } } diff --git a/VT-Api/Core/Audio/AudioManager.cs b/VT-Api/Core/Audio/AudioManager.cs new file mode 100644 index 0000000..64f1478 --- /dev/null +++ b/VT-Api/Core/Audio/AudioManager.cs @@ -0,0 +1,75 @@ +using MEC; +using Synapse.Api; +using System.IO; +using UnityEngine; + +namespace VT_Api.Core.Audio +{ + public class AudioManager + { + #region Attributes & Properties + public static AudioManager Get => Singleton.Instance; + + private Controller _controller; + + #endregion + + #region Constructors & Destructor + internal AudioManager() { } + + #endregion + + #region Methods + + internal void Init() + { + _controller = new Controller(); + } + + public void Loop(bool enabled) + { + _controller.Loop = enabled; + //Synapse.Api.Logger.Get.Info($"Loop : {_controller.Loop}"); + } + + private void UnmutePlayer(Player player) + { + var id = player.Radio.mirrorIgnorancePlayer._playerId; + _controller.UnMutePlayer(id); + } + + private void MutePlayer(Player player) + { + var id = player.Radio.mirrorIgnorancePlayer._playerId; + _controller.MutePlayer(id); + } + + public bool Play(string mpgFilePath) + { + if (!File.Exists(mpgFilePath)) + { + Synapse.Api.Logger.Get.Info($"File not found : {mpgFilePath}"); + return false; + } + + Timing.RunCoroutine(_controller.PlayFromFile(mpgFilePath)); + //Synapse.Api.Logger.Get.Info("Playing."); + return true; + } + + public void Stop() + { + _controller.Stop(); + //Synapse.Api.Logger.Get.Info("Stopped."); + } + + public void Volume(uint volume) + { + _controller.Volume = Mathf.Clamp(volume, 0, 100) / 100; + _controller.RefreshChannels(); + + //Synapse.Api.Logger.Get.Info($"Volume set to {volume}."); + } + #endregion + } +} diff --git a/VT-Api/Core/Audio/Controller.cs b/VT-Api/Core/Audio/Controller.cs new file mode 100644 index 0000000..3ff646b --- /dev/null +++ b/VT-Api/Core/Audio/Controller.cs @@ -0,0 +1,121 @@ +using Assets._Scripts.Dissonance; +using Dissonance; +using MEC; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using UnityEngine; +using VT_Api.Reflexion; + +namespace VT_Api.Core.Audio +{ + internal class Controller + { + + #region Attributes & Properties + public DissonanceComms Comms => Radio.comms; + public FakeMicrophone Microphone; + + public bool Loop { get; set; } + public float Volume { get; set; } = 1; + + public readonly List MutedPlayers = new List(); + #endregion + + #region Constructors & Destructor + + public Controller() + { + Microphone = Comms.gameObject.AddComponent(); + Microphone.AudioController = this; + InitEvents(); + } + #endregion + + #region Methods + public void UnMutePlayer(string playerId) + { + MutedPlayers.Remove(playerId); + UnMutePlayer(playerId); + Comms.PlayerChannels.Open(playerId, false, ChannelPriority.Default, Volume); + } + public void MutePlayer(string playerId) + { + var channel = Comms.PlayerChannels._openChannelsBySubId.FirstOrDefault(x => x.Value.TargetId == playerId); + Comms.PlayerChannels.Close(channel.Value); + } + + private void InitEvents() + { + Synapse.Server.Get.Events.Round.RoundRestartEvent += OnRestartingRound; + Synapse.Server.Get.Events.Round.WaitingForPlayersEvent += OnWaitingForPlayers; + + } + + public IEnumerator PlayFromFile(string path, float volume = 100, bool loop = false) + { + if (string.IsNullOrWhiteSpace(path)) + yield break; + + if (!File.Exists(path)) + { + Synapse.Api.Logger.Get.Error($"Error File not found: {path}."); + yield break; + } + + Stop(); + + yield return Timing.WaitForOneFrame; + yield return Timing.WaitForOneFrame; + + Volume = Mathf.Clamp(volume, 0, 100) / 100; + RefreshChannels(); + + Microphone.File = new FileStream(path, FileMode.Open); + Microphone.Stop = false; + Comms._capture.SetField("_microphone", Microphone); + Comms.ResetMicrophoneCapture(); + Comms.IsMuted = false; + Loop = loop; + } + + public void Stop() + { + if (Microphone != null) + Microphone.Stop = true; + } + + public void RefreshChannels() + { + foreach (var channel in Comms.PlayerChannels._openChannelsBySubId.Values.ToList()) + { + Comms.PlayerChannels.Close(channel); + Comms.PlayerChannels.Open(channel.TargetId, false, ChannelPriority.Default, Volume); + } + } + + #endregion + + + #region Events + private void OnRestartingRound() + { + Comms.OnPlayerJoinedSession -= OnPlayerJoinedSession; + } + + private void OnWaitingForPlayers() + { + Comms.OnPlayerJoinedSession += OnPlayerJoinedSession; + Synapse.Server.Get.Host.Radio.Network_syncPrimaryVoicechatButton = true; + Synapse.Server.Get.Host.DissonanceUserSetup.NetworkspeakingFlags = SpeakingFlags.IntercomAsHuman; + } + + private void OnPlayerJoinedSession(VoicePlayerState player) + { + Comms.PlayerChannels.Open(player.Name, false, ChannelPriority.Default, Volume); + } + + #endregion + + } +} diff --git a/VT-Api/Core/Audio/FakeMicrophone.cs b/VT-Api/Core/Audio/FakeMicrophone.cs new file mode 100644 index 0000000..6de848e --- /dev/null +++ b/VT-Api/Core/Audio/FakeMicrophone.cs @@ -0,0 +1,95 @@ +using Dissonance.Audio.Capture; +using Dissonance.Networking; +using NAudio.Wave; +using System; +using System.Collections.Generic; +using System.IO; +using UnityEngine; +using VT_Api.Reflexion; + +namespace VT_Api.Core.Audio +{ + internal class FakeMicrophone : MonoBehaviour, IMicrophoneCapture + { + + #region Properties & Variable + public bool IsRecording { get; private set; } + public TimeSpan Latency { get; private set; } + + public FileStream File; + + public bool Stop { get; set; } + + private readonly List _subscribers = new List(); + + private readonly WaveFormat _format = new WaveFormat(48000, 1); + private readonly float[] _frame = new float[960]; + private readonly byte[] _frameBytes = new byte[960 * 4]; + + private float _elapsedTime; + + public Controller AudioController { get; set; } + #endregion + + #region Methods + public WaveFormat StartCapture(string micName) + { + if (Stop) + return null; + + //Synapse.Api.Logger.Get.Info("Starting capture."); + AudioController.Comms._capture.SetField("_network", AudioController.Comms.GetFieldValueOrPerties("_net")); + + IsRecording = true; + Latency = TimeSpan.Zero; + return _format; + } + + public void StopCapture() + { + //Synapse.Api.Logger.Get.Info("Stopping capture."); + + if (File == null) + return; + + File.Dispose(); + File = null; + } + + public void Subscribe(IMicrophoneSubscriber listener) => _subscribers.Add(listener); + public bool Unsubscribe(IMicrophoneSubscriber listener) => _subscribers.Remove(listener); + + public bool UpdateSubscribers() + { + _elapsedTime += Time.unscaledDeltaTime; + + while (_elapsedTime > 0.02f) + { + _elapsedTime -= 0.02f; + var readLength = File.Read(_frameBytes, 0, _frameBytes.Length); + Array.Clear(_frame, 0, _frame.Length); + Buffer.BlockCopy(_frameBytes, 0, _frame, 0, readLength); + + foreach (var subscriber in _subscribers) + subscriber.ReceiveMicrophoneData(new ArraySegment(_frame), _format); + } + if (Stop) + return true; + if (File.Position != File.Length) + return false; + + if (AudioController.Loop) + File.Position = 0; + else + { + Stop = true; + AudioController.Stop(); + return true; + } + + + return false; + } + #endregion + } +} diff --git a/VT-Api/Core/Command/CommandHandler.cs b/VT-Api/Core/Command/CommandHandler.cs index 0f2589c..89bb7af 100644 --- a/VT-Api/Core/Command/CommandHandler.cs +++ b/VT-Api/Core/Command/CommandHandler.cs @@ -10,13 +10,8 @@ namespace VT_Api.Core.Command public class CommandHandler { - internal void Init() - { - RegisterVtCommands(); - Synapse.Api.Events.EventHandler.Get.Round.WaitingForPlayersEvent += RegisterSubCommand; - } - - public static CommandHandler Get { get => VtController.Get.Commands; } + #region Properties & Variable + public static CommandHandler Get => Singleton.Instance; internal List MainCommands { get; } = new List(); internal List SubCommands { get; } = new List(); @@ -25,6 +20,19 @@ internal void Init() private bool _firstLoad = true; + #endregion + + #region Constructor & Destructor + internal CommandHandler() { } + #endregion + + #region Methods + internal void Init() + { + RegisterVtCommands(); + Synapse.Api.Events.EventHandler.Get.Round.WaitingForPlayersEvent += RegisterSubCommand; + } + internal void RegisterSynapseCommand(ISynapseCommand iSynapseCommand, bool awaitPluginInitialisation) => typeof(Handlers).CallMethod("RegisterCommand", iSynapseCommand, awaitPluginInitialisation); @@ -100,5 +108,6 @@ private void RegisterVtCommands() { RegisterSynapseCommand(new CallPower(), false); } + #endregion } } diff --git a/VT-Api/Core/Items/ItemManager.cs b/VT-Api/Core/Items/ItemManager.cs index 6843e77..91bc355 100644 --- a/VT-Api/Core/Items/ItemManager.cs +++ b/VT-Api/Core/Items/ItemManager.cs @@ -19,9 +19,9 @@ public class ItemManager #region Properties & Variable public const string KeySynapseItemData = "VtScript"; - private readonly List customItems = new List(); + public static ItemManager Get => Singleton.Instance; - public static ItemManager Get { get => VtController.Get.Item; } + private readonly List customItems = new List(); public Dictionary ItemCategoryLimit { get; } = new Dictionary(); diff --git a/VT-Api/Core/MapAndRoundManger.cs b/VT-Api/Core/MapAndRoundManger.cs index 0f48c1e..f069aa9 100644 --- a/VT-Api/Core/MapAndRoundManger.cs +++ b/VT-Api/Core/MapAndRoundManger.cs @@ -20,11 +20,11 @@ namespace VT_Api.Core { public class MapAndRoundManger { + public static MapAndRoundManger Get => Singleton.Instance; + //Original of SanyaPlugin https://github.com/sanyae2439/SanyaPlugin_Exiled public bool isAirBombCurrently = false; - public static MapAndRoundManger Get { get => VtController.Get.MapAction; } - public Vector3[] AirbombPos { get diff --git a/VT-Api/Core/MiniGame/MiniGameManager.cs b/VT-Api/Core/MiniGame/MiniGameManager.cs index 18f9b96..2a5e5d8 100644 --- a/VT-Api/Core/MiniGame/MiniGameManager.cs +++ b/VT-Api/Core/MiniGame/MiniGameManager.cs @@ -10,7 +10,7 @@ namespace VT_Api.Core.MiniGame internal class MiniGameManager { #region Properties & Variable - public static MiniGameManager Get => VtController.Get.MinGames; + public static MiniGameManager Get => Singleton.Instance; private List MiniGames { get; } = new List(); diff --git a/VT-Api/Core/NPC/NpcManger.cs b/VT-Api/Core/NPC/NpcManger.cs index c4322c3..bfea9fe 100644 --- a/VT-Api/Core/NPC/NpcManger.cs +++ b/VT-Api/Core/NPC/NpcManger.cs @@ -10,11 +10,11 @@ namespace VT_Api.Core.NPC public class NpcManger { #region Properties & Variable + public static NpcManger Get => Singleton.Instance; + public Dictionary NPCs { get; } = new Dictionary(); public List NpcPathPoints { get; } = new List(); public List NpcMaps { get; } = new List(); - - public static NpcManger Get => VtController.Get.Npc; #endregion #region Constructor & Destructor diff --git a/VT-Api/Core/NetworkLiar.cs b/VT-Api/Core/NetworkLiar.cs index 12bb393..f0428c0 100644 --- a/VT-Api/Core/NetworkLiar.cs +++ b/VT-Api/Core/NetworkLiar.cs @@ -10,7 +10,7 @@ namespace VT_Api.Core { public class NetworkLiar { - public static NetworkLiar Get { get => VtController.Get.NetworkLiar; } + public static NetworkLiar Get => Singleton.Instance; public void SendRole(Player player, RoleType info, List players) { diff --git a/VT-Api/Core/Roles/RoleManager.cs b/VT-Api/Core/Roles/RoleManager.cs index a909af9..5aa8927 100644 --- a/VT-Api/Core/Roles/RoleManager.cs +++ b/VT-Api/Core/Roles/RoleManager.cs @@ -24,6 +24,8 @@ public class RoleManager { #region Properties & Variable + public static RoleManager Get => Singleton.Instance; + public Dictionary OldPlayerRole { get; } = new Dictionary(); public List CustomPhysicaleRoles { get; } = new List(); @@ -31,8 +33,7 @@ public class RoleManager public static int[] VanilaScpID { get; } = { (int)RoleType.Scp049, (int)RoleType.Scp0492, (int)RoleType.Scp079, (int)RoleType.Scp096, (int)RoleType.Scp106, (int)RoleType.Scp173, (int)RoleType.Scp93953, (int)RoleType.Scp93989 }; - - public static RoleManager Get => VtController.Get.Role; + #endregion #region Constructor & Destructor diff --git a/VT-Api/Core/Teams/TeamManager.cs b/VT-Api/Core/Teams/TeamManager.cs index 4101ef1..5aca1a5 100644 --- a/VT-Api/Core/Teams/TeamManager.cs +++ b/VT-Api/Core/Teams/TeamManager.cs @@ -20,7 +20,7 @@ public class TeamManager { #region Properties & Variable - public static TeamManager Get => VtController.Get.Team; + public static TeamManager Get => Singleton.Instance; public RespawnTeamInfo NextRespawnInfo { get; set; } = new RespawnTeamInfo(); public RespawnManager RespawnManager { get => RespawnManager.Singleton; } diff --git a/VT-Api/Patches/VtPatch/DissonanceHostPatch.cs b/VT-Api/Patches/VtPatch/DissonanceHostPatch.cs new file mode 100644 index 0000000..65e0e7e --- /dev/null +++ b/VT-Api/Patches/VtPatch/DissonanceHostPatch.cs @@ -0,0 +1,18 @@ +using Dissonance; +using Dissonance.Networking; +using Dissonance.Integrations.MirrorIgnorance; +using HarmonyLib; +using VT_Api.Reflexion; + +namespace VT_Api.Patches.VtPatch +{ + [HarmonyPatch(typeof(BaseCommsNetwork), "RunAsDedicatedServer")] + class DissonanceHostPatch + { + public static bool RunAsDedicatedServerPatch(BaseCommsNetwork __instance) + { + __instance.CallMethod("RunAsHost", new object[] { Unit.None, Unit.None }); + return false; + } + } +} diff --git a/VT-Api/VT-Api.csproj b/VT-Api/VT-Api.csproj index d4840ed..3680a5f 100644 --- a/VT-Api/VT-Api.csproj +++ b/VT-Api/VT-Api.csproj @@ -49,6 +49,9 @@ + + + @@ -154,6 +157,7 @@ + @@ -163,15 +167,13 @@ - - - ..\ref\DissonanceVoip-Publicized.dll - Dissonance + + ..\ref\DissonanceVoip.dll ..\ref\NorthwoodLib-Publicized.dll diff --git a/VT-Api/VtController.cs b/VT-Api/VtController.cs index 57d959e..8ae9454 100644 --- a/VT-Api/VtController.cs +++ b/VT-Api/VtController.cs @@ -17,6 +17,7 @@ using EventHandler = VT_Api.Core.Events.EventHandler; using VT_Api.Core.Behaviour; using VT_Api.Core.NPC; +using VT_Api.Core.Audio; public class VtController { @@ -24,6 +25,7 @@ public class VtController public static VtController Get { get; private set; } internal AutoRegisterManager AutoRegister { get => Singleton.Instance; } // nothing public (yet) + public AudioManager Audio { get => Singleton.Instance; } internal MiniGameManager MinGames { get => Singleton.Instance; } // not finish public RoleManager Role { get => Singleton.Instance; } public TeamManager Team { get => Singleton.Instance; } @@ -31,7 +33,7 @@ public class VtController public MapAndRoundManger MapAction { get => Singleton.Instance; } public NetworkLiar NetworkLiar { get => Singleton.Instance; } public ItemManager Item { get => Singleton.Instance; } - public NpcManger Npc { get => Singleton.Instance; } + internal NpcManger Npc { get => Singleton.Instance; } // not finish internal CommandHandler Commands { get => Singleton.Instance; } // nothing public (yet) public Config Configs { get => Singleton.Instance; } @@ -93,15 +95,16 @@ private void InitAll() var i = 0; try { - AutoRegister.Init(); i++; - MinGames.Init(); i++; - Events.Init(); i++; - Commands.Init(); i++; - Configs.Init(); i++; - Team.Init(); i++; - Role.Init(); i++; - Item.Init(); i++; - Npc.Init(); i++; + TryInit(AutoRegister.Init, "Initialising AutoRegister failed"); + TryInit(Audio.Init, "Initialising Audio failed"); + TryInit(MinGames.Init, "Initialising MinGames failed"); + TryInit(Events.Init, "Initialising Events failed"); + TryInit(Commands.Init, "Initialising Commands failed"); + TryInit(Configs.Init, "Initialising Configs failed"); + TryInit(Team.Init, "Initialising Team failed"); + TryInit(Role.Init, "Initialising Role failed"); + TryInit(Item.Init, "Initialising Item failed"); + TryInit(Npc.Init, "Initialising Npc failed"); } catch (Exception e) { @@ -109,6 +112,18 @@ private void InitAll() } } + private void TryInit(Action init, string msg) + { + try + { + init(); + } + catch (Exception ex) + { + Logger.Get.Error("Synapse-Loader: " + msg + "\n" + ex); + } + } + private void AplidePatch() { try diff --git a/ref/DissonanceVoip-Publicized.dll b/ref/DissonanceVoip.dll similarity index 52% rename from ref/DissonanceVoip-Publicized.dll rename to ref/DissonanceVoip.dll index be48484815457924820905feb752bae8dcba2660..600e0573115b708f6e41e442947ed8420da8d38f 100644 GIT binary patch delta 69032 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 delta 72366 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

  • o#q>rvpf~b|!89f_JB70|ZZ+se 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 Date: Sat, 16 Jul 2022 15:45:20 +0200 Subject: [PATCH 4/9] Fix bug 914 --- VT-Api/Config/Config.cs | 2 +- VT-Api/Core/Audio/AudioManager.cs | 8 +- VT-Api/Core/Events/ItemEvents.cs | 4 +- VT-Api/Core/Events/MapEvents.cs | 7 +- VT-Api/Core/Events/PlayerEvents.cs | 6 +- VT-Api/Core/MapAndRoundManger.cs | 4 +- VT-Api/Core/Roles/RoleManager.cs | 50 ++++++++++-- VT-Api/Core/Singleton.cs | 4 +- VT-Api/Core/VtExtensionsReflexion.cs | 28 +++---- .../ItemPatches/ChangeIntoGrenadePatch.cs | 20 +++-- .../VtEvent/MapPaches/Scp914InteractPatch.cs | 4 +- .../Scp914IventoryProcessorPatches.cs | 24 ++++-- .../MapPaches/Scp914PickupProcessorPatches.cs | 76 +++++++++++-------- .../VtEvent/PlayerPatches/DeathPatch.cs | 3 +- VT-Api/Properties/AssemblyInfo.cs | 4 +- VT-Api/VtController.cs | 30 +++----- VT-Api/VtVersion.cs | 2 +- 17 files changed, 171 insertions(+), 105 deletions(-) diff --git a/VT-Api/Config/Config.cs b/VT-Api/Config/Config.cs index 08f2711..2daab44 100644 --- a/VT-Api/Config/Config.cs +++ b/VT-Api/Config/Config.cs @@ -12,7 +12,7 @@ public class Config public static Config Get => Singleton.Instance; public VtApiConfiguration VtConfiguration { get; private set; } - public SynapseTranslation VtTranslation { get; private set; }; + public SynapseTranslation VtTranslation { get; private set; } public SynapseConfiguration SynapseConfiguration => Synapse.Server.Get.Configs.GetFieldValueOrPerties("synapseConfiguration"); #endregion diff --git a/VT-Api/Core/Audio/AudioManager.cs b/VT-Api/Core/Audio/AudioManager.cs index 64f1478..9d5806f 100644 --- a/VT-Api/Core/Audio/AudioManager.cs +++ b/VT-Api/Core/Audio/AudioManager.cs @@ -2,6 +2,7 @@ using Synapse.Api; using System.IO; using UnityEngine; +using VT_Api.Extension; namespace VT_Api.Core.Audio { @@ -15,7 +16,10 @@ public class AudioManager #endregion #region Constructors & Destructor - internal AudioManager() { } + internal AudioManager() { + + Synapse.Api.Logger.Get.Debug("AudioRun"); + } #endregion @@ -23,7 +27,7 @@ internal AudioManager() { } internal void Init() { - _controller = new Controller(); + //_controller = new Controller(); } public void Loop(bool enabled) diff --git a/VT-Api/Core/Events/ItemEvents.cs b/VT-Api/Core/Events/ItemEvents.cs index 01214b3..77af5e2 100644 --- a/VT-Api/Core/Events/ItemEvents.cs +++ b/VT-Api/Core/Events/ItemEvents.cs @@ -15,7 +15,7 @@ public class ItemEvents internal ItemEvents() { } #region Events - public event Synapse.Api.Events.EventHandler.OnSynapseEvent ChangeIntoFragEvent; + public event Synapse.Api.Events.EventHandler.OnSynapseEvent ChangeIntoActivGrenadEvent; public event Synapse.Api.Events.EventHandler.OnSynapseEvent ExplosionGrenadeEvent; public event Synapse.Api.Events.EventHandler.OnSynapseEvent CollisionEvent; public event Synapse.Api.Events.EventHandler.OnSynapseEvent RemoveLimitItemEvent; @@ -77,7 +77,7 @@ internal void InvokeChangeIntoFragEvent(SynapseItem item, TimeGrenade grenade, G Allow = allow }; - ChangeIntoFragEvent?.Invoke(ev); + ChangeIntoActivGrenadEvent?.Invoke(ev); allow = ev.Allow; } diff --git a/VT-Api/Core/Events/MapEvents.cs b/VT-Api/Core/Events/MapEvents.cs index 623951a..2e86715 100644 --- a/VT-Api/Core/Events/MapEvents.cs +++ b/VT-Api/Core/Events/MapEvents.cs @@ -3,6 +3,7 @@ using Synapse.Api.Items; using System; using VT_Api.Core.Events.EventArguments; +using VT_Api.Extension; namespace VT_Api.Core.Events { @@ -21,7 +22,7 @@ internal MapEvents() { } public event Synapse.Api.Events.EventHandler.OnSynapseEvent LockerInteractEvent; public event Synapse.Api.Events.EventHandler.OnSynapseEvent Scp914ActivateEvent; public event Synapse.Api.Events.EventHandler.OnSynapseEvent Scp914UpgradeItemEvent; - public event Synapse.Api.Events.EventHandler.OnSynapseEvent Scp914changeSettingEvent; + public event Synapse.Api.Events.EventHandler.OnSynapseEvent Scp914ChangeSettingEvent; #endregion #region Invoke @@ -72,7 +73,7 @@ internal void InvokeChange914KnobSettingEvent(Player player, ref bool allow) Allow = allow }; - Scp914changeSettingEvent?.Invoke(ev); + Scp914ChangeSettingEvent?.Invoke(ev); allow = ev.Allow; } @@ -124,6 +125,8 @@ internal void InvokeCassieAnnouncementEvent(ref string words, ref bool makeHold, internal void InvokeScp914UpgradeItemEvent(Scp914KnobSetting setting, SynapseItem olditem, ref SynapseItem newItem, ref bool keepOldItem) { + Logger.Get.Debug("InvokeScp914UpgradeItemEvent"); + var ev = new Scp914UpgradeItemEventArgs { Item = olditem, diff --git a/VT-Api/Core/Events/PlayerEvents.cs b/VT-Api/Core/Events/PlayerEvents.cs index 71ca176..b8185f8 100644 --- a/VT-Api/Core/Events/PlayerEvents.cs +++ b/VT-Api/Core/Events/PlayerEvents.cs @@ -12,7 +12,7 @@ internal PlayerEvents() { } #region Events public event Synapse.Api.Events.EventHandler.OnSynapseEvent PlayerDamagePostEvent; - public event Synapse.Api.Events.EventHandler.OnSynapseEvent PlayerKillEvent; + public event Synapse.Api.Events.EventHandler.OnSynapseEvent PlayerDeathReasonEvent; public event Synapse.Api.Events.EventHandler.OnSynapseEvent PlayerUnloadEvent; public event Synapse.Api.Events.EventHandler.OnSynapseEvent PlayerSpeakIntercomEvent; public event Synapse.Api.Events.EventHandler.OnSynapseEvent PlayerSetClassAdvEvent; @@ -31,7 +31,7 @@ internal void InvokePlayerSetClassAdvEvent(Player player, RoleType role) PlayerSetClassAdvEvent?.Invoke(ev); } - internal void InvokePlayerKillEvent(Player victim, Player killer, DamageHandlerBase handler, ref string victimeDeathReason) + internal void InvokePlayerDeathReasonEvent(Player victim, Player killer, DamageHandlerBase handler, ref string victimeDeathReason) { var ev = new PlayerKillEventArgs { @@ -41,7 +41,7 @@ internal void InvokePlayerKillEvent(Player victim, Player killer, DamageHandlerB Victim = victim }; - PlayerKillEvent?.Invoke(ev); + PlayerDeathReasonEvent?.Invoke(ev); victimeDeathReason = ev.DeathReason; } diff --git a/VT-Api/Core/MapAndRoundManger.cs b/VT-Api/Core/MapAndRoundManger.cs index f069aa9..b0fb16a 100644 --- a/VT-Api/Core/MapAndRoundManger.cs +++ b/VT-Api/Core/MapAndRoundManger.cs @@ -267,9 +267,9 @@ public void MtfRespawn(bool isCI, List players, bool useTicket = true) / }); } - public void PlayShoot(ShootSound sound, Vector3 position, byte shootSoundDistance = 25) + public void PlayShoot(ShootSound sound, Vector3 position, byte shootSoundDistance = 25, List players = null) { - foreach (var player in Server.Get.Players) + foreach (var player in players ?? Server.Get.Players) { var msg = new GunAudioMessage(player, 0, shootSoundDistance, player); var to = position - player.Position; diff --git a/VT-Api/Core/Roles/RoleManager.cs b/VT-Api/Core/Roles/RoleManager.cs index 5aa8927..db68c67 100644 --- a/VT-Api/Core/Roles/RoleManager.cs +++ b/VT-Api/Core/Roles/RoleManager.cs @@ -28,12 +28,15 @@ public class RoleManager public Dictionary OldPlayerRole { get; } = new Dictionary(); - public List CustomPhysicaleRoles { get; } = new List(); - - public static int[] VanilaScpID { get; } = { (int)RoleType.Scp049, (int)RoleType.Scp0492, (int)RoleType.Scp079, - (int)RoleType.Scp096, (int)RoleType.Scp106, (int)RoleType.Scp173, - (int)RoleType.Scp93953, (int)RoleType.Scp93989 }; - + public HashSet CustomPhysicaleRoles { get; } = new HashSet(); + + public int[] VanilaScpID { get; } = { (int)RoleType.Scp049, (int)RoleType.Scp0492, (int)RoleType.Scp079, + (int)RoleType.Scp096, (int)RoleType.Scp106, (int)RoleType.Scp173, + (int)RoleType.Scp93953, (int)RoleType.Scp93989 }; + public int[] VanilaNtfID { get; } = { (int)RoleType.NtfCaptain, (int)RoleType.NtfPrivate, + (int)RoleType.NtfSergeant, (int)RoleType.NtfSpecialist }; + public int[] VanilaChiID { get; } = { (int)RoleType.ChaosMarauder, (int)RoleType.ChaosRifleman, + (int)RoleType.ChaosRepressor, (int)RoleType.ChaosConscript }; #endregion #region Constructor & Destructor @@ -47,10 +50,43 @@ internal void Init() Synapse.Api.Events.EventHandler.Get.Player.PlayerKeyPressEvent += OnPressKey; Synapse.Api.Events.EventHandler.Get.Server.UpdateEvent += OnUpdate; Synapse.Api.Events.EventHandler.Get.Server.TransmitPlayerDataEvent += OnTransmitPlayerData; - VtController.Get.Events.Player.PlayerKillEvent += OnPlayerDeath; + VtController.Get.Events.Player.PlayerDeathReasonEvent += OnPlayerDeath; } + public List GetRoles(int teamID) + { + var result = new List(); + foreach (var customrole in Synapse.Api.Roles.RoleManager.Get.CustomRoles) + { + Synapse.Api.Roles.IRole role = (Synapse.Api.Roles.IRole)Activator.CreateInstance(customrole.RoleScript); + if (teamID == role.GetTeamID()) + result.Add(role.GetRoleID()); + } + switch (teamID) + { + case (int)Team.RIP: + result.Add((int)RoleType.Spectator); + break; + case (int)Team.CDP: + result.Add((int)RoleType.ClassD); + break; + case (int)Team.RSC: + result.Add((int)RoleType.Scientist); + break; + case (int)Team.CHI: + result.Concat(VanilaChiID.ToList()); + break; + case (int)Team.MTF: + result.Concat(VanilaNtfID.ToList()); + break; + case (int)Team.SCP: + result.Concat(VanilaScpID.ToList()); + break; + } + return result; + } + public bool IsVanilla(int roleID) => roleID > (int)RoleID.None && roleID <= SynRoleManager.HighestRole; diff --git a/VT-Api/Core/Singleton.cs b/VT-Api/Core/Singleton.cs index 9eaadd6..52d1c35 100644 --- a/VT-Api/Core/Singleton.cs +++ b/VT-Api/Core/Singleton.cs @@ -1,5 +1,7 @@ -using System; +using Synapse.Api; +using System; using System.Reflection; +using VT_Api.Extension; namespace VT_Api.Core { diff --git a/VT-Api/Core/VtExtensionsReflexion.cs b/VT-Api/Core/VtExtensionsReflexion.cs index 5c31772..72459b7 100644 --- a/VT-Api/Core/VtExtensionsReflexion.cs +++ b/VT-Api/Core/VtExtensionsReflexion.cs @@ -5,9 +5,9 @@ namespace VT_Api.Reflexion { public static class VtExtensionsReflexion { - public static object CallMethod(this Type o, string methodName, params object[] args) + public static object CallMethod(this Type element, string methodName, params object[] args) { - var mi = o.GetMethod(methodName, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static); + var mi = element.GetMethod(methodName, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static); if (mi != null) { return mi.Invoke(null, args); @@ -15,24 +15,24 @@ public static object CallMethod(this Type o, string methodName, params object[] return null; } - public static object CallMethod(this object o, string methodName, params object[] args) + public static object CallMethod(this object element, string methodName, params object[] args) { - var mi = o.GetType().GetMethod(methodName, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance); + var mi = element.GetType().GetMethod(methodName, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance); if (mi != null) { - return mi.Invoke(o, args); + return mi.Invoke(element, args); } return null; } public static T GetFieldValueOrPerties(this object element, string fieldName) { - var prop = element.GetType().GetProperty(fieldName, BindingFlags.NonPublic | BindingFlags.Instance); + var prop = element.GetType().GetProperty(fieldName, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance); if (prop != null) { return (T)prop.GetValue(element); } - FieldInfo field = element.GetType().GetField(fieldName, BindingFlags.NonPublic | BindingFlags.Instance); + FieldInfo field = element.GetType().GetField(fieldName, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance); if (field != null) { return (T)field.GetValue(element); @@ -42,12 +42,12 @@ public static T GetFieldValueOrPerties(this object element, string fieldName) public static T GetFieldOrPropertyValue(this Type element, string fieldName) { - var prop = element.GetProperty(fieldName, BindingFlags.NonPublic | BindingFlags.Static); + var prop = element.GetProperty(fieldName, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static); if (prop != null) { return (T)prop.GetValue(null); } - var field = element.GetField(fieldName, BindingFlags.NonPublic | BindingFlags.Static); + var field = element.GetField(fieldName, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static); if (field != null) { return (T)field.GetValue(null); @@ -75,7 +75,7 @@ public static void SetProperty(this object element, string fieldName, T value public static void SetField(this object element, string fieldName, T value) { - var prop = element.GetType().GetField(fieldName, BindingFlags.NonPublic | BindingFlags.Instance); + var prop = element.GetType().GetField(fieldName, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance); if (prop != null) { prop.SetValue(element, value); @@ -84,16 +84,16 @@ public static void SetField(this object element, string fieldName, T value) public static void SetField(this Type element, string fieldName, T value) { - var prop = element.GetField(fieldName, BindingFlags.NonPublic | BindingFlags.Static); + var prop = element.GetField(fieldName, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static); if (prop != null) { prop.SetValue(null, value); } } - public static void CallEvent(this Type o, string eventName,params object[] parameters) + public static void CallEvent(this Type element, string eventName, params object[] parameters) { - var eventsField = o.GetField(eventName, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static); + var eventsField = element.GetField(eventName, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static); if (eventsField != null) { object eventHandlerList = eventsField.GetValue(null); @@ -153,7 +153,7 @@ public static T CopyPropertyAndFeild(this T element, T elementToCopy) prop.SetValue(element, prop.GetValue(elementToCopy)); } } - var fields = typeof(T).GetFields(BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance); + var fields = typeof(T).GetFields(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static | BindingFlags.Instance); if (fields.Length != 0) { foreach (var field in fields) diff --git a/VT-Api/Patches/VtEvent/ItemPatches/ChangeIntoGrenadePatch.cs b/VT-Api/Patches/VtEvent/ItemPatches/ChangeIntoGrenadePatch.cs index 279ff4e..c1ba035 100644 --- a/VT-Api/Patches/VtEvent/ItemPatches/ChangeIntoGrenadePatch.cs +++ b/VT-Api/Patches/VtEvent/ItemPatches/ChangeIntoGrenadePatch.cs @@ -1,5 +1,6 @@ using Footprinting; using HarmonyLib; +using InventorySystem.Items.MicroHID; using InventorySystem.Items.ThrowableProjectiles; using Synapse.Api.Enum; using System; @@ -15,16 +16,21 @@ private static bool ExplosionDetectedPatch(TimedGrenadePickup __instance, Footpr { try { - if (Vector3.Distance(__instance.transform.position, source) / range > TimedGrenadePickup.ActivationRange) - return false; + if (!(Vector3.Distance(__instance.transform.position, source) / range > TimedGrenadePickup.ActivationRange) && !Physics.Linecast(__instance.gameObject.transform.position, source, MicroHIDItem.WallMask)) + { + var allow = true; + var item = __instance.GetSynapseItem(); + var type = (GrenadeType)__instance.Info.ItemId; - var allow = true; - var item = __instance.GetSynapseItem(); - var type = (GrenadeType)__instance.Info.ItemId; + VtController.Get.Events.Item.InvokeChangeIntoFragEvent(item, FragExplosionGrenadePatch.grenade, type, ref allow); - VtController.Get.Events.Item.InvokeChangeIntoFragEvent(item, FragExplosionGrenadePatch.grenade, type, ref allow); + if (!allow) + return false; - return allow; + __instance._replaceNextFrame = true; + __instance._attacker = attacker; + } + return false; } catch (Exception e) { diff --git a/VT-Api/Patches/VtEvent/MapPaches/Scp914InteractPatch.cs b/VT-Api/Patches/VtEvent/MapPaches/Scp914InteractPatch.cs index e68d5c8..78bfc8c 100644 --- a/VT-Api/Patches/VtEvent/MapPaches/Scp914InteractPatch.cs +++ b/VT-Api/Patches/VtEvent/MapPaches/Scp914InteractPatch.cs @@ -22,10 +22,10 @@ private static bool Scp914UsePatch(Scp914Controller __instance, ReferenceHub ply switch ((Scp914InteractCode)colliderId) { case Scp914InteractCode.ChangeMode: - VtController.Get.Events.Map.InvokeChange914KnobSettingEvent(__instance.GetPlayer(), ref flag); + VtController.Get.Events.Map.InvokeChange914KnobSettingEvent(ply.GetPlayer(), ref flag); return flag; case Scp914InteractCode.Activate: - VtController.Get.Events.Map.InvokeScp914ActivateEvent(__instance.GetPlayer(), ref flag); + VtController.Get.Events.Map.InvokeScp914ActivateEvent(ply.GetPlayer(), ref flag); return flag; } return false; diff --git a/VT-Api/Patches/VtEvent/MapPaches/Scp914IventoryProcessorPatches.cs b/VT-Api/Patches/VtEvent/MapPaches/Scp914IventoryProcessorPatches.cs index a1339e3..11b70c9 100644 --- a/VT-Api/Patches/VtEvent/MapPaches/Scp914IventoryProcessorPatches.cs +++ b/VT-Api/Patches/VtEvent/MapPaches/Scp914IventoryProcessorPatches.cs @@ -6,6 +6,7 @@ using InventorySystem.Items.Firearms.Attachments; using Scp914; using Scp914.Processors; +using Synapse.Api; using Synapse.Api.Items; using System; using System.Collections.Generic; @@ -16,17 +17,19 @@ namespace VT_Api.Patches.VtEvent.MapPaches { - class Scp914IventoryProcessorPatch - { - // the ammo cannot be upgrade if they are in the inventory, even if the method exists I do not see any interest in the patch - // call Warkis if you want an event for the ammo or do a pull request + // the ammo cannot be upgrade if they are in the inventory, even if the method exists I do not see any interest in the patch + // call Warkis if you want an event for the ammo or do a pull request - [HarmonyPatch(typeof(FirearmItemProcessor), nameof(FirearmItemProcessor.OnInventoryItemUpgraded))] + [HarmonyPatch(typeof(FirearmItemProcessor), nameof(FirearmItemProcessor.OnInventoryItemUpgraded))] + class InvFirearmUpgradePatch + { [HarmonyPrefix] - private static bool FirearmUpgradePatch(FirearmItemProcessor __instance, Scp914KnobSetting setting, ReferenceHub hub, ushort serial) + private static bool OnInventoryItemUpgraded(FirearmItemProcessor __instance, Scp914KnobSetting setting, ReferenceHub hub, ushort serial) { try { + Logger.Get.Debug("OnInventoryItemUpgraded"); + if (hub.inventory.UserInventory.Items.TryGetValue(serial, out ItemBase item)) { var items = __instance.GetItems(setting, item.ItemTypeId); @@ -132,13 +135,18 @@ private static bool FirearmUpgradePatch(FirearmItemProcessor __instance, Scp914K return true; } } + } - [HarmonyPatch(typeof(StandardItemProcessor), nameof(StandardItemProcessor.OnInventoryItemUpgraded))] + [HarmonyPatch(typeof(StandardItemProcessor), nameof(StandardItemProcessor.OnInventoryItemUpgraded))] + class InvItemUpgradePatch + { [HarmonyPrefix] - private static bool ItemUpgradePatch(StandardItemProcessor __instance, Scp914KnobSetting setting, ReferenceHub hub, ushort serial) + private static bool OnInventoryItemUpgraded(StandardItemProcessor __instance, Scp914KnobSetting setting, ReferenceHub hub, ushort serial) { try { + Logger.Get.Debug("OnInventoryItemUpgraded"); + if (!hub.inventory.UserInventory.Items.TryGetValue(serial, out ItemBase value)) return false; diff --git a/VT-Api/Patches/VtEvent/MapPaches/Scp914PickupProcessorPatches.cs b/VT-Api/Patches/VtEvent/MapPaches/Scp914PickupProcessorPatches.cs index d233273..e670338 100644 --- a/VT-Api/Patches/VtEvent/MapPaches/Scp914PickupProcessorPatches.cs +++ b/VT-Api/Patches/VtEvent/MapPaches/Scp914PickupProcessorPatches.cs @@ -14,14 +14,18 @@ namespace VT_Api.Patches.VtEvent.MapPaches { - class Scp914ItemPickupPatch + + [HarmonyPatch(typeof(AmmoItemProcessor), nameof(AmmoItemProcessor.OnPickupUpgraded))] + class AmmoUpgradePatch { - [HarmonyPatch(typeof(AmmoItemProcessor), nameof(AmmoItemProcessor.OnPickupUpgraded))] [HarmonyPrefix] - private static bool AmmoUpgradePatch(AmmoItemProcessor __instance, Scp914KnobSetting setting, ItemPickupBase ipb, Vector3 newPos) + private static bool OnPickupUpgraded(AmmoItemProcessor __instance, Scp914KnobSetting setting, ItemPickupBase ipb, Vector3 newPos) { try - { + { + Synapse.Api.Logger.Get.Debug("OnAmmoUpgraded"); + + ItemType itemType; switch (setting) { @@ -40,16 +44,16 @@ private static bool AmmoUpgradePatch(AmmoItemProcessor __instance, Scp914KnobSet return false; } - if (ipb is not AmmoPickup ammoPickup) // change this in C#9 + if (ipb is not AmmoPickup ammoPickup) return false; - + var change = 0; var exchangedAmmo = 0; - if (AmmoItemProcessor.TryGetAmmoItem(ammoPickup.Info.ItemId, out AmmoItem ammoItem) && - InventoryItemLoader.AvailableItems.TryGetValue(itemType, out ItemBase itembase) && + if (AmmoItemProcessor.TryGetAmmoItem(ammoPickup.Info.ItemId, out AmmoItem ammoItem) && + InventoryItemLoader.AvailableItems.TryGetValue(itemType, out ItemBase itembase) && itembase is AmmoItem ammoItem2) - { + { var unitPrice1 = ammoItem.UnitPrice; var unitPrice2 = ammoItem2.UnitPrice; var num1 = 0; @@ -72,14 +76,14 @@ private static bool AmmoUpgradePatch(AmmoItemProcessor __instance, Scp914KnobSet change = num1; } - var destroyOldItem = change == 0; + var keepOldItem = change != 0; var oldItem = ammoPickup.GetSynapseItem(); var newItem = exchangedAmmo > 0 ? new SynapseItem(itemType) { Durabillity = exchangedAmmo } : SynapseItem.None; - + oldItem.Durabillity = change; oldItem.Position = newPos; - VtController.Get.Events.Map.InvokeScp914UpgradeItemEvent(setting, oldItem, ref newItem, ref destroyOldItem); + VtController.Get.Events.Map.InvokeScp914UpgradeItemEvent(setting, oldItem, ref newItem, ref keepOldItem); if (newItem.IsDefined()) { @@ -87,7 +91,9 @@ private static bool AmmoUpgradePatch(AmmoItemProcessor __instance, Scp914KnobSet newItem.Rotation = oldItem.Rotation; } - if (destroyOldItem) + Synapse.Api.Logger.Get.Debug(oldItem == null); + + if (!keepOldItem) { oldItem.Destroy(); } @@ -100,25 +106,30 @@ private static bool AmmoUpgradePatch(AmmoItemProcessor __instance, Scp914KnobSet return true; } } + } - [HarmonyPatch(typeof(FirearmItemProcessor), nameof(FirearmItemProcessor.OnPickupUpgraded))] + [HarmonyPatch(typeof(FirearmItemProcessor), nameof(FirearmItemProcessor.OnPickupUpgraded))] + class FirearmUpgradePatch + { [HarmonyPrefix] - private static bool FirearmUpgradePatch(FirearmItemProcessor __instance, Scp914KnobSetting setting, ItemPickupBase ipb, Vector3 newPos) + private static bool OnPickupUpgraded(FirearmItemProcessor __instance, Scp914KnobSetting setting, ItemPickupBase ipb, Vector3 newPos) { try - { + { + Synapse.Api.Logger.Get.Debug("OnFirearmUpgraded"); + var items = __instance.GetItems(setting, ipb.Info.ItemId); foreach (ItemType newItemType in items) { var oldItem = ipb.GetSynapseItem(); var newItem = newItemType == ItemType.None || newItemType == oldItem.ItemType ? SynapseItem.None : new SynapseItem(newItemType); - var destroyOldItem = newItem.IsDefined(); + var keepOldItem = newItem.IsDefined(); var attachments = 0u; if (!InventoryItemLoader.AvailableItems.TryGetValue(newItemType, out ItemBase newItemBase)) { - destroyOldItem = true; + keepOldItem = true; newItem = SynapseItem.None; } @@ -126,7 +137,7 @@ private static bool FirearmUpgradePatch(FirearmItemProcessor __instance, Scp914K throw new InvalidOperationException("FirearmItemProcessor can't be used for non-firearm items, such as " + newItemBase?.ItemTypeId ?? "Unknow"); - if (newItemBase is Firearm newFirearm && + if (newItemBase is Firearm newFirearm && InventoryItemLoader.AvailableItems.TryGetValue(oldFirearmPickup.Info.ItemId, out ItemBase oldItemBase) && oldItemBase is Firearm oldFirearm) { @@ -183,14 +194,14 @@ private static bool FirearmUpgradePatch(FirearmItemProcessor __instance, Scp914K attachments = newFirearm.ValidateAttachmentsCode(0u); } - if (!destroyOldItem) + if (keepOldItem) { oldFirearmPickup.NetworkStatus = new FirearmStatus(0, FirearmStatusFlags.None, AttachmentsUtils.GetRandomAttachmentsCode(newItemType)); } oldItem.Position = newPos; - VtController.Get.Events.Map.InvokeScp914UpgradeItemEvent(setting, oldItem, ref newItem, ref destroyOldItem); + VtController.Get.Events.Map.InvokeScp914UpgradeItemEvent(setting, oldItem, ref newItem, ref keepOldItem); if (newItem.IsDefined()) { @@ -203,7 +214,7 @@ private static bool FirearmUpgradePatch(FirearmItemProcessor __instance, Scp914K firearmPickup.NetworkStatus = new FirearmStatus(0, FirearmStatusFlags.None, attachments); } - if (destroyOldItem) + if (!keepOldItem) { oldItem.Destroy(); } @@ -222,26 +233,32 @@ private static bool FirearmUpgradePatch(FirearmItemProcessor __instance, Scp914K return true; } } - - [HarmonyPatch(typeof(StandardItemProcessor), nameof(StandardItemProcessor.OnPickupUpgraded))] + } + + [HarmonyPatch(typeof(StandardItemProcessor), nameof(StandardItemProcessor.OnPickupUpgraded))] + class ItemUpgradePatch + { [HarmonyPrefix] - private static bool ItemUpgradePatch(StandardItemProcessor __instance, Scp914KnobSetting setting, ItemPickupBase ipb, Vector3 newPosition) + private static bool OnPickupUpgraded(StandardItemProcessor __instance, Scp914KnobSetting setting, ItemPickupBase ipb, Vector3 newPosition) { + try { + Synapse.Api.Logger.Get.Debug("OnPickupUpgraded"); + var itemType = __instance.RandomOutput(setting, ipb.Info.ItemId); var oldItem = ipb.GetSynapseItem(); var newItem = itemType == ItemType.None || itemType == oldItem.ItemType ? SynapseItem.None : new SynapseItem(itemType); - var destroyOldItem = newItem.IsDefined(); + var keepOldItem = !newItem.IsDefined(); - if (!destroyOldItem && __instance._fireUpgradeTrigger && ipb is IUpgradeTrigger upgradeTrigger) + if (!keepOldItem && __instance._fireUpgradeTrigger && ipb is IUpgradeTrigger upgradeTrigger) { upgradeTrigger.ServerOnUpgraded(setting); } oldItem.Position = newPosition; - VtController.Get.Events.Map.InvokeScp914UpgradeItemEvent(setting, oldItem, ref newItem, ref destroyOldItem); + VtController.Get.Events.Map.InvokeScp914UpgradeItemEvent(setting, oldItem, ref newItem, ref keepOldItem); if (newItem.IsDefined()) { @@ -249,12 +266,11 @@ private static bool ItemUpgradePatch(StandardItemProcessor __instance, Scp914Kno newItem.Rotation = oldItem.Rotation; } - if (destroyOldItem) + if (!keepOldItem) { oldItem.Destroy(); } - ipb.DestroySelf(); return false; } catch (Exception e) diff --git a/VT-Api/Patches/VtEvent/PlayerPatches/DeathPatch.cs b/VT-Api/Patches/VtEvent/PlayerPatches/DeathPatch.cs index 7ac80ab..9277efe 100644 --- a/VT-Api/Patches/VtEvent/PlayerPatches/DeathPatch.cs +++ b/VT-Api/Patches/VtEvent/PlayerPatches/DeathPatch.cs @@ -26,10 +26,9 @@ private static bool DeathEventPatch(PlayerStats __instance, DamageHandlerBase ha { var victim = __instance.GetPlayer(); var attacker = handler is AttackerDamageHandler ahandler ? ahandler.Attacker.GetPlayer() : null; - var allow = true; string reason = null; - VtController.Get.Events.Player.InvokePlayerKillEvent(victim, attacker, handler, ref reason); + VtController.Get.Events.Player.InvokePlayerDeathReasonEvent(victim, attacker, handler, ref reason); Ragdoll.ServerSpawnRagdoll(__instance._hub, handler); diff --git a/VT-Api/Properties/AssemblyInfo.cs b/VT-Api/Properties/AssemblyInfo.cs index 61b7dc4..4b1f5e9 100644 --- a/VT-Api/Properties/AssemblyInfo.cs +++ b/VT-Api/Properties/AssemblyInfo.cs @@ -32,5 +32,5 @@ // Vous pouvez spécifier toutes les valeurs ou indiquer les numéros de build et de révision par défaut // en utilisant '*', comme indiqué ci-dessous : // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.2.1.0")] -[assembly: AssemblyFileVersion("1.2.1.0")] +[assembly: AssemblyVersion("1.3.0.0")] +[assembly: AssemblyFileVersion("1.3.0.0")] diff --git a/VT-Api/VtController.cs b/VT-Api/VtController.cs index 8ae9454..0817e93 100644 --- a/VT-Api/VtController.cs +++ b/VT-Api/VtController.cs @@ -92,24 +92,16 @@ private void LogMessage() private void InitAll() { - var i = 0; - try - { - TryInit(AutoRegister.Init, "Initialising AutoRegister failed"); - TryInit(Audio.Init, "Initialising Audio failed"); - TryInit(MinGames.Init, "Initialising MinGames failed"); - TryInit(Events.Init, "Initialising Events failed"); - TryInit(Commands.Init, "Initialising Commands failed"); - TryInit(Configs.Init, "Initialising Configs failed"); - TryInit(Team.Init, "Initialising Team failed"); - TryInit(Role.Init, "Initialising Role failed"); - TryInit(Item.Init, "Initialising Item failed"); - TryInit(Npc.Init, "Initialising Npc failed"); - } - catch (Exception e) - { - throw new VtInitAllHandlerExceptions($"Vt-init: Error while Initialising the handlers #{i} !\n Please fix the Issue and restart your Server!\n{e}\nStackTrace:\n{e.StackTrace}", e); - } + TryInit(AutoRegister.Init, "Initialising AutoRegister failed"); + TryInit(Audio.Init, "Initialising Audio failed"); + TryInit(MinGames.Init, "Initialising MinGames failed"); + TryInit(Events.Init, "Initialising Events failed"); + TryInit(Commands.Init, "Initialising Commands failed"); + TryInit(Configs.Init, "Initialising Configs failed"); + TryInit(Team.Init, "Initialising Team failed"); + TryInit(Role.Init, "Initialising Role failed"); + TryInit(Item.Init, "Initialising Item failed"); + TryInit(Npc.Init, "Initialising Npc failed"); } private void TryInit(Action init, string msg) @@ -120,7 +112,7 @@ private void TryInit(Action init, string msg) } catch (Exception ex) { - Logger.Get.Error("Synapse-Loader: " + msg + "\n" + ex); + Logger.Get.Error(msg + "\n" + ex); } } diff --git a/VT-Api/VtVersion.cs b/VT-Api/VtVersion.cs index 661e322..9fa5742 100644 --- a/VT-Api/VtVersion.cs +++ b/VT-Api/VtVersion.cs @@ -6,7 +6,7 @@ public static class VtVersion { public const int Major = 1; - public const int Minor = 2; + public const int Minor = 3; public const int Patch = 0; From 9f33ce06c84345360dfac895db49595b30bca3ca Mon Sep 17 00:00:00 2001 From: warquys <64769541+warquys@users.noreply.github.com> Date: Tue, 26 Jul 2022 18:34:45 +0200 Subject: [PATCH 5/9] Translation --- VT-Api/Core/Translation/IpInfo.cs | 32 +++++ VT-Api/Core/Translation/TranslationManager.cs | 124 ++++++++++++++++++ VT-Api/Core/VtExtensions.cs | 4 + VT-Api/VT-Api.csproj | 6 +- VT-Api/VtController.cs | 5 + 5 files changed, 170 insertions(+), 1 deletion(-) create mode 100644 VT-Api/Core/Translation/IpInfo.cs create mode 100644 VT-Api/Core/Translation/TranslationManager.cs diff --git a/VT-Api/Core/Translation/IpInfo.cs b/VT-Api/Core/Translation/IpInfo.cs new file mode 100644 index 0000000..2608235 --- /dev/null +++ b/VT-Api/Core/Translation/IpInfo.cs @@ -0,0 +1,32 @@ +using Newtonsoft.Json; + +namespace VT_Api.Core.Translation +{ + //https://stackoverflow.com/questions/4327629/get-user-location-by-ip-address + public class IpInfo + { + [JsonProperty("ip")] + public string Ip { get; set; } + + [JsonProperty("hostname")] + public string Hostname { get; set; } + + [JsonProperty("city")] + public string City { get; set; } + + [JsonProperty("region")] + public string Region { get; set; } + + [JsonProperty("country")] + public string Country { get; set; } + + [JsonProperty("loc")] + public string Loc { get; set; } + + [JsonProperty("org")] + public string Org { get; set; } + + [JsonProperty("postal")] + public string Postal { get; set; } + } +} diff --git a/VT-Api/Core/Translation/TranslationManager.cs b/VT-Api/Core/Translation/TranslationManager.cs new file mode 100644 index 0000000..73f1cc6 --- /dev/null +++ b/VT-Api/Core/Translation/TranslationManager.cs @@ -0,0 +1,124 @@ +using Newtonsoft.Json; +using Synapse.Api; +using Synapse.Api.Events.SynapseEventArguments; +using System; +using System.Collections.Generic; +using System.Globalization; +using System.IO; +using System.Linq; +using System.Net; +using System.Text; +using System.Threading.Tasks; +using Synapse; +using Synapse.Translation; + +namespace VT_Api.Core.Translation +{ + public class TranslationManager + { + + #region Properties & Variables + public static TranslationManager Get => Singleton.Instance; + + public Dictionary PlayersLanguage = new Dictionary(); + #endregion + + #region Constructor & Destructor + internal TranslationManager() { } + #endregion + + #region Methods + internal void Init() + { + Synapse.Api.Events.EventHandler.Get.Player.PlayerJoinEvent += OnJoin; + Synapse.Api.Events.EventHandler.Get.Round.RoundStartEvent += OnRoundRestart; + + } + + //https://stackoverflow.com/questions/4327629/get-user-location-by-ip-address + public IpInfo GetIpInfo(Player player) => GetIpInfo(player.IpAddress); + public IpInfo GetIpInfo(string ip) + { + string info = new WebClient().DownloadString("http://ipinfo.io/" + ip); + return JsonConvert.DeserializeObject(info); + } + + /// The country name in English + public string GetUserCountry(Player player) + { + IpInfo ipInfo = new IpInfo(); + try + { + ipInfo = GetIpInfo(player); + RegionInfo myRI1 = new RegionInfo(ipInfo.Country); + ipInfo.Country = myRI1.EnglishName; + } + catch (Exception) + { + ipInfo.Country = null; + } + + return ipInfo.Country; + } + + /// The country name in English + public string GetUserCountry(string ip) + { + IpInfo ipInfo = new IpInfo(); + try + { + ipInfo = GetIpInfo(ip); + RegionInfo myRI1 = new RegionInfo(ipInfo.Country); + ipInfo.Country = myRI1.EnglishName; + } + catch (Exception) + { + ipInfo.Country = null; + } + + return ipInfo.Country; + } + + public string GetLanguage(Player player) + { + var contry = GetUserCountry(player); + var cultureInfo = CultureInfo.GetCultures(CultureTypes.AllCultures).FirstOrDefault(c => new RegionInfo(c.Name).EnglishName == contry); + return cultureInfo.IetfLanguageTag; + } + + public TPluginTranslation GetTranslation(SynapseTranslation translation, Player player) where TPluginTranslation : IPluginTranslation + { + if (!PlayersLanguage.TryGetValue(player.NickName, out var language)) + { + Logger.Get.Error($"The player language of {player.NickName} is not set !"); + return translation.ActiveTranslation; + } + return translation[language]; + } + #endregion + + #region Events + private void OnJoin(PlayerJoinEventArgs ev) + { + if (PlayersLanguage.ContainsKey(ev.Player.NickName)) + return; + var language = ev.Player.GetData("Language"); + if (language == null) + { + language = GetLanguage(ev.Player); + ev.Player.SetData("Language", GetLanguage(ev.Player)); + } + PlayersLanguage.Add(ev.Player.NickName, language); + } + + private void OnRoundRestart() + { + foreach(var playerLanguage in PlayersLanguage) + { + if (!Server.Get.Players.Any(p => p.NickName == playerLanguage.Key)) + PlayersLanguage.Remove(playerLanguage.Key); + } + } + #endregion + } +} diff --git a/VT-Api/Core/VtExtensions.cs b/VT-Api/Core/VtExtensions.cs index 992c519..32b88de 100644 --- a/VT-Api/Core/VtExtensions.cs +++ b/VT-Api/Core/VtExtensions.cs @@ -2,6 +2,7 @@ using Synapse.Api.Enum; using Synapse.Api.Items; using Synapse.Config; +using Synapse.Translation; using System; using System.Collections.Generic; using System.Linq; @@ -30,6 +31,9 @@ internal static void Debug(this SynLogger logger, object message) logger.Send($"VtApi-Debug: {message}", ConsoleColor.DarkYellow); } + public static TPluginTranslation GetForPlayer(this SynapseTranslation translation, Player player) where TPluginTranslation : IPluginTranslation + => VtController.Get.Translation.GetTranslation(translation, player); + public static void PlayAmbientSound(this Map _, int id) => VtController.Get.MapAction.PlayAmbientSound(id); diff --git a/VT-Api/VT-Api.csproj b/VT-Api/VT-Api.csproj index 3680a5f..f80bf4a 100644 --- a/VT-Api/VT-Api.csproj +++ b/VT-Api/VT-Api.csproj @@ -131,6 +131,8 @@ + + @@ -167,7 +169,6 @@ - @@ -202,6 +203,9 @@ 2.2.1 + + 13.0.1 + 2.10.0 diff --git a/VT-Api/VtController.cs b/VT-Api/VtController.cs index 0817e93..c69e4ea 100644 --- a/VT-Api/VtController.cs +++ b/VT-Api/VtController.cs @@ -18,6 +18,7 @@ using VT_Api.Core.Behaviour; using VT_Api.Core.NPC; using VT_Api.Core.Audio; +using VT_Api.Core.Translation; public class VtController { @@ -33,8 +34,10 @@ public class VtController public MapAndRoundManger MapAction { get => Singleton.Instance; } public NetworkLiar NetworkLiar { get => Singleton.Instance; } public ItemManager Item { get => Singleton.Instance; } + public TranslationManager Translation { get => Singleton.Instance; } internal NpcManger Npc { get => Singleton.Instance; } // not finish internal CommandHandler Commands { get => Singleton.Instance; } // nothing public (yet) + public Config Configs { get => Singleton.Instance; } private static bool _enabled = false; @@ -48,6 +51,7 @@ private VtController() #endregion #region Methods + public static void InitApi() { if (_enabled) return; @@ -101,6 +105,7 @@ private void InitAll() TryInit(Team.Init, "Initialising Team failed"); TryInit(Role.Init, "Initialising Role failed"); TryInit(Item.Init, "Initialising Item failed"); + TryInit(Translation.Init, "Initialising Translation failed"); TryInit(Npc.Init, "Initialising Npc failed"); } From 375d24624a0c18699cde9802bfc9005bd45bd4e6 Mon Sep 17 00:00:00 2001 From: warquys <64769541+warquys@users.noreply.github.com> Date: Thu, 28 Jul 2022 20:09:00 +0200 Subject: [PATCH 6/9] Add Language Command --- VT-Api/Config/VtApiTranslation.cs | 2 + .../Core/Command/Commands/ChangeLanguage.cs | 67 +++++++++++++++++++ VT-Api/Core/Events/EventHandler.cs | 16 ++--- VT-Api/Core/Plugin/VtAbstractPlugins.cs | 6 +- VT-Api/Core/Roles/CustomDisplay.cs | 3 +- VT-Api/Core/Translation/TranslationManager.cs | 24 +++++-- VT-Api/VT-Api.csproj | 1 + 7 files changed, 101 insertions(+), 18 deletions(-) create mode 100644 VT-Api/Core/Command/Commands/ChangeLanguage.cs diff --git a/VT-Api/Config/VtApiTranslation.cs b/VT-Api/Config/VtApiTranslation.cs index c210043..8dadd25 100644 --- a/VT-Api/Config/VtApiTranslation.cs +++ b/VT-Api/Config/VtApiTranslation.cs @@ -16,5 +16,7 @@ public class VtApiTranslation : IPluginTranslation [Description("The death message when you are kill by a custom class of the VT-API")] public string DefaultDeathMessage { get; set; } = "You were killed by\\n%PlayerName%\\nas\\n%RoleName%"; + + } } diff --git a/VT-Api/Core/Command/Commands/ChangeLanguage.cs b/VT-Api/Core/Command/Commands/ChangeLanguage.cs new file mode 100644 index 0000000..37aac20 --- /dev/null +++ b/VT-Api/Core/Command/Commands/ChangeLanguage.cs @@ -0,0 +1,67 @@ +using Synapse.Command; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace VT_Api.Core.Command.Commands +{ + [CommandInformation( + Name = "Language", + Aliases = new[] { "Lang" }, + Description = "For change the language of the text of the serveur", + Usage = "no arguement send you the list of possible language, else add the desired language", + Permission = "", + Platforms = new[] { Platform.ClientConsole }, + Arguments = new[] { "(new language)" } + )] + public class ChangeLanguage : ISynapseCommand + { + public CommandResult Execute(CommandContext context) + { + var result = new CommandResult(); + + try + { + result.State = CommandResultState.Ok; + if (context.Arguments.Any()) + { + var language = context.Arguments.First().ToUpper(); + if (language == "HELP") + { + result.Message = Help(); + } + else if (Translation.TranslationManager.Get.AvailableLanguage.Contains(language)) + { + context.Player.SetData("Language", language); + Translation.TranslationManager.Get.PlayersLanguage[context.Player.NickName] = language; + result.Message = "New language set successfully"; + } + else + { + result.Message = "Invalide Language"; + } + } + else + { + result.Message = Help(); + } + } + catch (Exception e) + { + Synapse.Api.Logger.Get.Error($"Vt-Command : Language failed!!\n{e}\nStackTrace:\n{e.StackTrace}"); + result.Message = e.Message; + result.State = CommandResultState.Error; + } + return result; + } + + public string Help() + { + var message = "All possible languages:\n"; + message += String.Join("\n", Translation.TranslationManager.Get.AvailableLanguage); + return message; + } + } +} diff --git a/VT-Api/Core/Events/EventHandler.cs b/VT-Api/Core/Events/EventHandler.cs index 7990cc7..054291e 100644 --- a/VT-Api/Core/Events/EventHandler.cs +++ b/VT-Api/Core/Events/EventHandler.cs @@ -32,28 +32,26 @@ public class EventHandler internal EventHandler() { SyanpseEventHandler.Get.Player.PlayerJoinEvent += PlayerJoin; - SyanpseEventHandler.Get.Server.RemoteAdminCommandEvent += OnRaOverwatchFix; + //SyanpseEventHandler.Get.Server.RemoteAdminCommandEvent += OnRaOverwatchFix; SyanpseEventHandler.Get.Round.WaitingForPlayersEvent += OnWaiting; #if DEBUG SyanpseEventHandler.Get.Player.PlayerKeyPressEvent += KeyPress; #endif } + #endregion - private void OnWaiting() - { - SynapseController.Server.Host.gameObject.GetOrAddComponent(); - } - + #region Methods internal void Init() { } #endregion - #region Methods - #endregion - #region Events + private void OnWaiting() + { + SynapseController.Server.Host.gameObject.GetOrAddComponent(); + } private void PlayerJoin(Synapse.Api.Events.SynapseEventArguments.PlayerJoinEventArgs ev) { diff --git a/VT-Api/Core/Plugin/VtAbstractPlugins.cs b/VT-Api/Core/Plugin/VtAbstractPlugins.cs index 439fd45..7f36633 100644 --- a/VT-Api/Core/Plugin/VtAbstractPlugins.cs +++ b/VT-Api/Core/Plugin/VtAbstractPlugins.cs @@ -48,7 +48,7 @@ public string PluginDirectory [Obsolete("This is the old Translation system, use Translation", true)] - Translation IPlugin.Translation { get; set; } + Synapse.Api.Plugin.Translation IPlugin.Translation { get; set; } #endregion #region Constructor & Destructor @@ -116,7 +116,7 @@ public string PluginDirectory [Obsolete("This is the old Translation Systemn, use Translation", true)] - Translation IPlugin.Translation { get; set; } + Synapse.Api.Plugin.Translation IPlugin.Translation { get; set; } #endregion #region Constructor & Destructor @@ -183,7 +183,7 @@ public string PluginDirectory [Obsolete("This is the old Translation Systemn, use Translation", true)] - Translation IPlugin.Translation { get; set; } + Synapse.Api.Plugin.Translation IPlugin.Translation { get; set; } #endregion #region Constructor & Destructor diff --git a/VT-Api/Core/Roles/CustomDisplay.cs b/VT-Api/Core/Roles/CustomDisplay.cs index 7208531..b7a2283 100644 --- a/VT-Api/Core/Roles/CustomDisplay.cs +++ b/VT-Api/Core/Roles/CustomDisplay.cs @@ -100,7 +100,8 @@ public CustomDisplay() #endregion #region Methods - private void Start() + + protected override void Start() { Player = this.gameObject.GetPlayer(); diff --git a/VT-Api/Core/Translation/TranslationManager.cs b/VT-Api/Core/Translation/TranslationManager.cs index 73f1cc6..b2f6aa0 100644 --- a/VT-Api/Core/Translation/TranslationManager.cs +++ b/VT-Api/Core/Translation/TranslationManager.cs @@ -20,7 +20,20 @@ public class TranslationManager #region Properties & Variables public static TranslationManager Get => Singleton.Instance; - public Dictionary PlayersLanguage = new Dictionary(); + public Dictionary PlayersLanguage { get; } = new Dictionary(); + + public Dictionary CountryLanguage { get; } = new Dictionary() + { + { "FRANCE", "FRENCH" }, + { "GERMANY", "GERMAN" }, + { "ITALY", "ITALIAN" }, + { "SPAIN", "SPANISH" }, + { "BRITAIN", "ENGLISH" } + }; + + public List AvailableLanguage => CountryLanguage.Values.Distinct().ToList(); + + #endregion #region Constructor & Destructor @@ -82,8 +95,9 @@ public string GetUserCountry(string ip) public string GetLanguage(Player player) { var contry = GetUserCountry(player); - var cultureInfo = CultureInfo.GetCultures(CultureTypes.AllCultures).FirstOrDefault(c => new RegionInfo(c.Name).EnglishName == contry); - return cultureInfo.IetfLanguageTag; + if (!CountryLanguage.TryGetValue(contry, out string language)) + language = "ENGLISH"; + return language; } public TPluginTranslation GetTranslation(SynapseTranslation translation, Player player) where TPluginTranslation : IPluginTranslation @@ -105,8 +119,8 @@ private void OnJoin(PlayerJoinEventArgs ev) var language = ev.Player.GetData("Language"); if (language == null) { - language = GetLanguage(ev.Player); - ev.Player.SetData("Language", GetLanguage(ev.Player)); + language = GetLanguage(ev.Player).ToUpper(); + ev.Player.SetData("Language", language); } PlayersLanguage.Add(ev.Player.NickName, language); } diff --git a/VT-Api/VT-Api.csproj b/VT-Api/VT-Api.csproj index f80bf4a..5ad4013 100644 --- a/VT-Api/VT-Api.csproj +++ b/VT-Api/VT-Api.csproj @@ -52,6 +52,7 @@ + From b9c5e2648db08c36e673d8fb5ef1024e57e96b30 Mon Sep 17 00:00:00 2001 From: Antoniofo Date: Tue, 2 Aug 2022 10:38:14 +0200 Subject: [PATCH 7/9] change NickName to UserID and country empty instead of null --- .idea/.idea.VT-Api/.idea/.gitignore | 13 +++++++++++++ .idea/.idea.VT-Api/.idea/encodings.xml | 4 ++++ .idea/.idea.VT-Api/.idea/indexLayout.xml | 8 ++++++++ .idea/.idea.VT-Api/.idea/vcs.xml | 6 ++++++ VT-Api/Core/Translation/TranslationManager.cs | 12 ++++++------ 5 files changed, 37 insertions(+), 6 deletions(-) create mode 100644 .idea/.idea.VT-Api/.idea/.gitignore create mode 100644 .idea/.idea.VT-Api/.idea/encodings.xml create mode 100644 .idea/.idea.VT-Api/.idea/indexLayout.xml create mode 100644 .idea/.idea.VT-Api/.idea/vcs.xml diff --git a/.idea/.idea.VT-Api/.idea/.gitignore b/.idea/.idea.VT-Api/.idea/.gitignore new file mode 100644 index 0000000..19756cd --- /dev/null +++ b/.idea/.idea.VT-Api/.idea/.gitignore @@ -0,0 +1,13 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Rider ignored files +/.idea.VT-Api.iml +/projectSettingsUpdater.xml +/modules.xml +/contentModel.xml +# Editor-based HTTP Client requests +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/.idea/.idea.VT-Api/.idea/encodings.xml b/.idea/.idea.VT-Api/.idea/encodings.xml new file mode 100644 index 0000000..df87cf9 --- /dev/null +++ b/.idea/.idea.VT-Api/.idea/encodings.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/.idea/.idea.VT-Api/.idea/indexLayout.xml b/.idea/.idea.VT-Api/.idea/indexLayout.xml new file mode 100644 index 0000000..7b08163 --- /dev/null +++ b/.idea/.idea.VT-Api/.idea/indexLayout.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/.idea.VT-Api/.idea/vcs.xml b/.idea/.idea.VT-Api/.idea/vcs.xml new file mode 100644 index 0000000..94a25f7 --- /dev/null +++ b/.idea/.idea.VT-Api/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/VT-Api/Core/Translation/TranslationManager.cs b/VT-Api/Core/Translation/TranslationManager.cs index b2f6aa0..81dda7e 100644 --- a/VT-Api/Core/Translation/TranslationManager.cs +++ b/VT-Api/Core/Translation/TranslationManager.cs @@ -68,7 +68,7 @@ public string GetUserCountry(Player player) } catch (Exception) { - ipInfo.Country = null; + ipInfo.Country = ""; } return ipInfo.Country; @@ -86,7 +86,7 @@ public string GetUserCountry(string ip) } catch (Exception) { - ipInfo.Country = null; + ipInfo.Country = ""; } return ipInfo.Country; @@ -102,7 +102,7 @@ public string GetLanguage(Player player) public TPluginTranslation GetTranslation(SynapseTranslation translation, Player player) where TPluginTranslation : IPluginTranslation { - if (!PlayersLanguage.TryGetValue(player.NickName, out var language)) + if (!PlayersLanguage.TryGetValue(player.UserId, out var language)) { Logger.Get.Error($"The player language of {player.NickName} is not set !"); return translation.ActiveTranslation; @@ -114,7 +114,7 @@ public TPluginTranslation GetTranslation(SynapseTranslation< #region Events private void OnJoin(PlayerJoinEventArgs ev) { - if (PlayersLanguage.ContainsKey(ev.Player.NickName)) + if (PlayersLanguage.ContainsKey(ev.Player.UserId)) return; var language = ev.Player.GetData("Language"); if (language == null) @@ -122,14 +122,14 @@ private void OnJoin(PlayerJoinEventArgs ev) language = GetLanguage(ev.Player).ToUpper(); ev.Player.SetData("Language", language); } - PlayersLanguage.Add(ev.Player.NickName, language); + PlayersLanguage.Add(ev.Player.UserId, language); } private void OnRoundRestart() { foreach(var playerLanguage in PlayersLanguage) { - if (!Server.Get.Players.Any(p => p.NickName == playerLanguage.Key)) + if (!Server.Get.Players.Any(p => p.UserId == playerLanguage.Key)) PlayersLanguage.Remove(playerLanguage.Key); } } From 5c5277f3782295670384a17c67170085c903bf0a Mon Sep 17 00:00:00 2001 From: Sanctur Date: Fri, 28 Oct 2022 16:52:21 +0200 Subject: [PATCH 8/9] Update package --- Exemple-Plugin/Exemple-Plugin.csproj | 16 ++++++++-------- Exemple-Plugin/packages.config | 2 +- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/Exemple-Plugin/Exemple-Plugin.csproj b/Exemple-Plugin/Exemple-Plugin.csproj index 9e4765b..ba70a02 100644 --- a/Exemple-Plugin/Exemple-Plugin.csproj +++ b/Exemple-Plugin/Exemple-Plugin.csproj @@ -35,19 +35,19 @@ ..\packages\Lib.Harmony.2.2.1\lib\net48\0Harmony.dll - ..\packages\SynapseSL.2.10.0\lib\net472\Assembly-CSharp.dll + ..\packages\SynapseSL.2.10.1\lib\net472\Assembly-CSharp.dll - ..\packages\SynapseSL.2.10.0\lib\net472\Assembly-CSharp-firstpass.dll + ..\packages\SynapseSL.2.10.1\lib\net472\Assembly-CSharp-firstpass.dll ..\packages\LiteDB.5.0.11\lib\net45\LiteDB.dll - ..\packages\SynapseSL.2.10.0\lib\net472\Mirror.dll + ..\packages\SynapseSL.2.10.1\lib\net472\Mirror.dll - - ..\packages\SynapseSL.2.10.0\lib\net472\Synapse.dll + + ..\packages\SynapseSL.2.10.1\lib\net472\Synapse.dll @@ -59,13 +59,13 @@ - ..\packages\SynapseSL.2.10.0\lib\net472\UnityEngine.dll + ..\packages\SynapseSL.2.10.1\lib\net472\UnityEngine.dll - ..\packages\SynapseSL.2.10.0\lib\net472\UnityEngine.CoreModule.dll + ..\packages\SynapseSL.2.10.1\lib\net472\UnityEngine.CoreModule.dll - ..\packages\SynapseSL.2.10.0\lib\net472\UnityEngine.PhysicsModule.dll + ..\packages\SynapseSL.2.10.1\lib\net472\UnityEngine.PhysicsModule.dll ..\packages\YamlDotNet.11.2.1\lib\net45\YamlDotNet.dll diff --git a/Exemple-Plugin/packages.config b/Exemple-Plugin/packages.config index 27c73f5..c254a85 100644 --- a/Exemple-Plugin/packages.config +++ b/Exemple-Plugin/packages.config @@ -2,6 +2,6 @@ - + \ No newline at end of file From d68e724c2a21a69f18801cfce510dcabc1e2cdef Mon Sep 17 00:00:00 2001 From: Sanctur Date: Fri, 28 Oct 2022 16:52:44 +0200 Subject: [PATCH 9/9] update package --- VT-Api/VT-Api.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VT-Api/VT-Api.csproj b/VT-Api/VT-Api.csproj index 5ad4013..ea93152 100644 --- a/VT-Api/VT-Api.csproj +++ b/VT-Api/VT-Api.csproj @@ -208,7 +208,7 @@ 13.0.1 - 2.10.0 + 2.10.1