From f44846414ac4dedc18ecce7cebc708f5ab161832 Mon Sep 17 00:00:00 2001 From: Vespura <31419184+TomGrobbe@users.noreply.github.com> Date: Sat, 4 Mar 2023 16:01:49 +0100 Subject: [PATCH 1/2] Fix missing using --- vMenu/EntitySpawner.cs | 45 +++++++++++++++++++++--------------------- 1 file changed, 23 insertions(+), 22 deletions(-) diff --git a/vMenu/EntitySpawner.cs b/vMenu/EntitySpawner.cs index 47d8cd31..0febb710 100644 --- a/vMenu/EntitySpawner.cs +++ b/vMenu/EntitySpawner.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.Threading.Tasks; using CitizenFX.Core; @@ -22,16 +23,16 @@ public class EntitySpawner : BaseScript public EntitySpawner() { #if DEBUG - RegisterCommand("testEntity", new Action>((source, args) => - { - string prop = (string)args[0]; - SpawnEntity(prop, Game.PlayerPed.Position); - }), false); - - RegisterCommand("endTest", new Action(() => - { - FinishPlacement(); - }), false); + RegisterCommand("testEntity", new Action>((source, args) => + { + var prop = (string)args[0]; + SpawnEntity(prop, Game.PlayerPed.Position); + }), false); + + RegisterCommand("endTest", new Action(() => + { + FinishPlacement(); + }), false); #endif } @@ -109,8 +110,8 @@ public static async void FinishPlacement(bool duplicate = false) { if (duplicate) { - int hash = CurrentEntity.Model.Hash; - Vector3 position = CurrentEntity.Position; + var hash = CurrentEntity.Model.Hash; + var position = CurrentEntity.Position; CurrentEntity = null; await Delay(1); // Mandatory SpawnEntity((uint)hash, position); @@ -154,7 +155,7 @@ private void DrawButtons() //TODO: Right keys /// Output direction vector private Vector3 RotationToDirection(Vector3 rotation) { - Vector3 adj = new Vector3( + var adj = new Vector3( (float)Math.PI / 180f * rotation.X, (float)Math.PI / 180f * rotation.Y, (float)Math.PI / 180f * rotation.Z @@ -173,20 +174,20 @@ private Vector3 RotationToDirection(Vector3 rotation) /// destination if no hit was found and coords of hit if there was one private Vector3 GetCoordsPlayerIsLookingAt() { - Vector3 camRotation = GetGameplayCamRot(0); - Vector3 camCoords = GetGameplayCamCoord(); - Vector3 camDirection = RotationToDirection(camRotation); + var camRotation = GetGameplayCamRot(0); + var camCoords = GetGameplayCamCoord(); + var camDirection = RotationToDirection(camRotation); - Vector3 dest = new Vector3( + var dest = new Vector3( camCoords.X + (camDirection.X * RayDistance), camCoords.Y + (camDirection.Y * RayDistance), camCoords.Z + (camDirection.Z * RayDistance) ); - RaycastResult res = World.Raycast(camCoords, dest, IntersectOptions.Everything, Game.PlayerPed); + var res = World.Raycast(camCoords, dest, IntersectOptions.Everything, Game.PlayerPed); #if DEBUG - DrawLine(Game.PlayerPed.Position.X, Game.PlayerPed.Position.Y,Game.PlayerPed.Position.Z, dest.X, dest.Y, dest.Z, 255, 0, 0, 255); + DrawLine(Game.PlayerPed.Position.X, Game.PlayerPed.Position.Y, Game.PlayerPed.Position.Z, dest.X, dest.Y, dest.Z, 255, 0, 0, 255); #endif return res.DitHit ? res.HitPosition : dest; @@ -219,7 +220,7 @@ private async Task MoveHandler() } } - float headingOffset = 0f; + var headingOffset = 0f; while (Active) { if (CurrentEntity == null || !CurrentEntity.Exists()) @@ -228,7 +229,7 @@ private async Task MoveHandler() CurrentEntity = null; break; } - int handle = CurrentEntity.Handle; + var handle = CurrentEntity.Handle; DrawButtons(); @@ -238,7 +239,7 @@ private async Task MoveHandler() SetEntityAlpha(handle, (int)(255 * 0.4), 0); CurrentEntity.Heading = (GetGameplayCamRot(0).Z + headingOffset) % 360f; - Vector3 newPosition = GetCoordsPlayerIsLookingAt(); + var newPosition = GetCoordsPlayerIsLookingAt(); CurrentEntity.Position = newPosition; if (CurrentEntity.HeightAboveGround < 3.0f) From 846c9e35dd3251bc58debeef402e6f25c68a5d93 Mon Sep 17 00:00:00 2001 From: local9 Date: Sun, 5 Mar 2023 15:04:41 +0000 Subject: [PATCH 2/2] feat(addon-groups): addon groups --- SharedClasses/ConfigManager.cs | 125 ++++++++++++++++++++++++++++ SharedClasses/PermissionsManager.cs | 38 ++++++++- vMenu/EventManager.cs | 92 ++++++++++++++++++-- vMenu/menus/PlayerAppearance.cs | 90 ++++++++++++++++++-- vMenu/menus/VehicleSpawner.cs | 96 +++++++++++++++++++-- vMenu/menus/WeaponOptions.cs | 91 ++++++++++++++++++-- vMenuServer/MainServer.cs | 33 +++++++- vMenuServer/config/addons.json | 97 ++++++++++++++++++++- vMenuServer/config/permissions.cfg | 3 + 9 files changed, 632 insertions(+), 33 deletions(-) diff --git a/SharedClasses/ConfigManager.cs b/SharedClasses/ConfigManager.cs index d1e68058..bcfc9e13 100644 --- a/SharedClasses/ConfigManager.cs +++ b/SharedClasses/ConfigManager.cs @@ -238,6 +238,131 @@ public LocationBlip(string name, Vector3 coordinates, int spriteID, int color) } } #endregion + + #region Addons + + /// + /// Gets the addons.json data. + /// + /// + public static Addons GetAddons() + { + Addons data = new Addons(); + + string jsonFile = LoadResourceFile(GetCurrentResourceName(), "config/addons.json"); + try + { + if (string.IsNullOrEmpty(jsonFile)) + { +#if CLIENT + vMenuClient.Notify.Error("The addons.json file is empty or does not exist, please tell the server owner to fix this."); +#endif +#if SERVER + vMenuServer.DebugLog.Log("The addons.json file is empty or does not exist, please fix this.", vMenuServer.DebugLog.LogLevel.error); +#endif + } + else + { + data = JsonConvert.DeserializeObject(jsonFile); + } + } + catch (Exception e) + { +#if CLIENT + vMenuClient.Notify.Error("An error occurred while processing the addons.json file. Please correct any errors in the addons.json file."); +#endif + Debug.WriteLine($"[vMenu] json exception details: {e.Message}\nStackTrace:\n{e.StackTrace}"); + } + + return data; + } + +#if SERVER + /// + /// Gets the vehicle group data from the addons.json file. + /// + /// The source of the player. + /// + public static List GetAddonsVehicleGroupData(Player player) + { + return CreateAddonsListFromGroupData(player, GetAddons().groups.vehicles); + } + + /// + /// Gets the ped group data from the addons.json file. + /// + /// The source of the player. + /// + public static List GetAddonsPedGroupData(Player player) + { + return CreateAddonsListFromGroupData(player, GetAddons().groups.peds); + } + + /// + /// Gets the weapon group data from the addons.json file. + /// + /// The source of the player. + /// + public static List GetAddonsWeaponGroupData(Player player) + { + return CreateAddonsListFromGroupData(player, GetAddons().groups.weapons); + } + + /// + /// Generates a list of groups based on the group permission in the addons.json file. + /// + /// + /// + /// + private static List CreateAddonsListFromGroupData(Player player, List groups) + { + List finalLst = new List(); + foreach (Group group in groups) + { + string permission = group.permission; + if (string.IsNullOrEmpty(permission)) + { + finalLst.Add(group); + } + else if (PermissionsManager.IsAllowed(group.permission, player)) + { + finalLst.Add(group); + } + } + return finalLst; + } +#endif + + public struct Addons + { + public string[] vehicles { get; set; } + public string[] peds { get; set; } + public string[] weapons { get; set; } + public string[] weapon_components { get; set; } + public Groups groups { get; set; } + } + + public struct Groups + { + public List vehicles { get; set; } + public List peds { get; set; } + public List weapons { get; set; } + } + + public struct Group + { + public string permission { get; set; } + public string label { get; set; } + public string description { get; set; } + public List items { get; set; } + } + + public struct GroupItem + { + public string label { get; set; } + public string model { get; set; } + } + #endregion } diff --git a/SharedClasses/PermissionsManager.cs b/SharedClasses/PermissionsManager.cs index d155be34..62913726 100644 --- a/SharedClasses/PermissionsManager.cs +++ b/SharedClasses/PermissionsManager.cs @@ -357,6 +357,7 @@ public enum Permission /// if true, then the permissions will be checked even if they aren't setup yet. /// public static bool IsAllowed(Permission permission, Player source, bool checkAnyway = false) => IsAllowedServer(permission, source); + public static bool IsAllowed(string permission, Player source, bool checkAnyway = false) => IsAllowedServer(permission, source); #endif #if CLIENT @@ -408,6 +409,26 @@ private static bool IsAllowedClient(Permission permission, bool checkAnyway) } #endif #if SERVER + /// + /// Checks if the player is allowed that specific permission. + /// + /// + /// + /// + private static bool IsAllowedServer(string permission, Player source) + { + if (source == null) + { + return false; + } + + if (IsPlayerAceAllowed(source.Handle, GetAceName(permission))) + { + return true; + } + return false; + } + /// /// Checks if the player is allowed that specific permission. /// @@ -469,7 +490,7 @@ public static List GetPermissionAndParentPermissions(Permission perm /// Sets the permissions for a specific player (checks server side, sends event to client side). /// /// - public static void SetPermissionsForPlayer([FromSource]Player player) + public static void SetPermissionsForPlayer([FromSource] Player player) { if (player == null) { @@ -554,10 +575,19 @@ public static void SetPermissions(string permissions) private static string GetAceName(Permission permission) { string name = permission.ToString(); + return GetAceName(name); + } + /// + /// Gets the full permission ace name for the specific permission name. + /// + /// + /// + private static string GetAceName(string permission) + { string prefix = "vMenu."; - switch (name.Substring(0, 2)) + switch (permission.Substring(0, 2)) { case "OP": prefix += "OnlinePlayers"; @@ -599,10 +629,10 @@ private static string GetAceName(Permission permission) prefix += "VoiceChat"; break; default: - return prefix + name; + return prefix + permission; } - return prefix + "." + name.Substring(2); + return prefix + "." + permission.Substring(2); } #endif } diff --git a/vMenu/EventManager.cs b/vMenu/EventManager.cs index 54f96993..f901fede 100644 --- a/vMenu/EventManager.cs +++ b/vMenu/EventManager.cs @@ -7,6 +7,8 @@ using Newtonsoft.Json; +using vMenuShared; + using static CitizenFX.Core.Native.API; using static vMenuClient.CommonFunctions; using static vMenuShared.ConfigManager; @@ -105,23 +107,33 @@ private async void SetAppearanceOnFirstSpawn() /// /// Sets the addon models from the addons.json file. /// - private void SetAddons() + private async void SetAddons() { // reset addons VehicleSpawner.AddonVehicles = new Dictionary(); + VehicleSpawner.AddonVehiclesGroups = new List(); + WeaponOptions.AddonWeapons = new Dictionary(); + WeaponOptions.AddonWeaponGroups = new List(); + PlayerAppearance.AddonPeds = new Dictionary(); + PlayerAppearance.AddonPedGroups = new List(); + + bool isVehicleGroupEventComplete = false; + bool isWeaponGroupEventComplete = false; + bool isPedGroupEventComplete = false; + + int eventTimeout = 0; - string jsonData = LoadResourceFile(GetCurrentResourceName(), "config/addons.json") ?? "{}"; try { // load new addons. - var addons = JsonConvert.DeserializeObject>>(jsonData); + var addons = ConfigManager.GetAddons(); // load vehicles - if (addons.ContainsKey("vehicles")) + if (addons.vehicles.Length > 0) { - foreach (string addon in addons["vehicles"]) + foreach (string addon in addons.vehicles) { if (!VehicleSpawner.AddonVehicles.ContainsKey(addon)) VehicleSpawner.AddonVehicles.Add(addon, (uint)GetHashKey(addon)); @@ -131,9 +143,9 @@ private void SetAddons() } // load weapons - if (addons.ContainsKey("weapons")) + if (addons.weapons.Length > 0) { - foreach (string addon in addons["weapons"]) + foreach (string addon in addons.weapons) { if (!WeaponOptions.AddonWeapons.ContainsKey(addon)) WeaponOptions.AddonWeapons.Add(addon, (uint)GetHashKey(addon)); @@ -143,9 +155,9 @@ private void SetAddons() } // load peds. - if (addons.ContainsKey("peds")) + if (addons.peds.Length > 0) { - foreach (string addon in addons["peds"]) + foreach (string addon in addons.peds) { if (!PlayerAppearance.AddonPeds.ContainsKey(addon)) PlayerAppearance.AddonPeds.Add(addon, (uint)GetHashKey(addon)); @@ -153,6 +165,68 @@ private void SetAddons() Debug.WriteLine($"[vMenu] [Error] Your addons.json file contains 2 or more entries with the same ped name! ({addon}) Please remove duplicate lines!"); } } + + Func getVehicleGroup = (data) => + { + VehicleSpawner.AddonVehiclesGroups = JsonConvert.DeserializeObject>(data); + isVehicleGroupEventComplete = true; + return true; + }; + + // load vehicle groups from the server. + while (!isVehicleGroupEventComplete) + { + TriggerServerEvent("vMenu:RequestAddonVehicleGroups", getVehicleGroup); + await BaseScript.Delay(500); + eventTimeout++; + if (eventTimeout > 10) + { + Notify.Error("Failed to get addon vehicle groups from the server."); + break; + } + } + + // load ped groups from the server. + Func getPedGroup = (data) => + { + PlayerAppearance.AddonPedGroups = JsonConvert.DeserializeObject>(data); + isPedGroupEventComplete = true; + return true; + }; + + eventTimeout = 0; + while (!isPedGroupEventComplete) + { + TriggerServerEvent("vMenu:RequestAddonPedGroups", getPedGroup); + await BaseScript.Delay(500); + eventTimeout++; + if (eventTimeout > 10) + { + Notify.Error("Failed to get addon ped groups from the server."); + break; + } + } + + // load weapon groups from the server. + Func getWeaponGroup = (data) => + { + WeaponOptions.AddonWeaponGroups = JsonConvert.DeserializeObject>(data); + isWeaponGroupEventComplete = true; + return true; + }; + + eventTimeout = 0; + while (!isWeaponGroupEventComplete) + { + TriggerServerEvent("vMenu:RequestAddonWeaponGroups", getWeaponGroup); + await BaseScript.Delay(500); + eventTimeout++; + if (eventTimeout > 10) + { + Notify.Error("Failed to get addon weapon groups from the server."); + break; + } + } } catch (JsonReaderException ex) { diff --git a/vMenu/menus/PlayerAppearance.cs b/vMenu/menus/PlayerAppearance.cs index 5211add5..fa28738a 100644 --- a/vMenu/menus/PlayerAppearance.cs +++ b/vMenu/menus/PlayerAppearance.cs @@ -1,14 +1,14 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Text; -using System.Threading.Tasks; -using MenuAPI; -using Newtonsoft.Json; + using CitizenFX.Core; -using static CitizenFX.Core.UI.Screen; + +using MenuAPI; + using static CitizenFX.Core.Native.API; using static vMenuClient.CommonFunctions; +using static vMenuShared.ConfigManager; using static vMenuShared.PermissionsManager; namespace vMenuClient @@ -21,6 +21,7 @@ public class PlayerAppearance private Menu savedPedsMenu; private Menu spawnPedsMenu; private Menu addonPedsMenu; + private Menu groupedAddonPedsMenu; private Menu mainPedsMenu = new Menu("Main Peds", "Spawn A Ped"); private Menu animalsPedsMenu = new Menu("Animals", "Spawn A Ped"); private Menu malePedsMenu = new Menu("Male Peds", "Spawn A Ped"); @@ -28,6 +29,7 @@ public class PlayerAppearance private Menu otherPedsMenu = new Menu("Other Peds", "Spawn A Ped"); public static Dictionary AddonPeds; + public static List AddonPedGroups; public static int ClothingAnimationType { get; set; } = UserDefaults.PAClothingAnimationType; @@ -46,6 +48,7 @@ private void CreateMenu() pedCustomizationMenu = new Menu(Game.Player.Name, "Customize Saved Ped"); spawnPedsMenu = new Menu(Game.Player.Name, "Spawn Ped"); addonPedsMenu = new Menu(Game.Player.Name, "Addon Peds"); + groupedAddonPedsMenu = new Menu(Game.Player.Name, "Grouped Addon Peds"); // Add the (submenus) to the menu pool. @@ -53,6 +56,7 @@ private void CreateMenu() MenuController.AddSubmenu(menu, savedPedsMenu); MenuController.AddSubmenu(menu, spawnPedsMenu); MenuController.AddSubmenu(spawnPedsMenu, addonPedsMenu); + MenuController.AddSubmenu(spawnPedsMenu, groupedAddonPedsMenu); MenuController.AddSubmenu(spawnPedsMenu, mainPedsMenu); MenuController.AddSubmenu(spawnPedsMenu, animalsPedsMenu); MenuController.AddSubmenu(spawnPedsMenu, malePedsMenu); @@ -68,6 +72,7 @@ private void CreateMenu() MenuItem spawnByNameBtn = new MenuItem("Spawn By Name", "Spawn a ped by entering it's name manually."); MenuItem addonPedsBtn = new MenuItem("Addon Peds", "Spawn a ped from the addon peds list.") { Label = "→→→" }; + MenuItem groupedAddonPedsBtn = new MenuItem("Grouped Addon Peds", "Spawn a ped from the groups addon peds list.") { Label = "→→→" }; MenuItem mainPedsBtn = new MenuItem("Main Peds", "Select a new ped from the main player-peds list.") { Label = "→→→" }; MenuItem animalPedsBtn = new MenuItem("Animals", "Become an animal. ~r~Note this may crash your own or other players' game if you die as an animal, godmode can NOT prevent this.") { Label = "→→→" }; MenuItem malePedsBtn = new MenuItem("Male Peds", "Select a male ped.") { Label = "→→→" }; @@ -331,6 +336,81 @@ void UpdateSavedPedsMenu() }; } + if (AddonPedGroups != null && AddonPedGroups.Count > 0 && IsAllowed(Permission.PAAddonPeds)) + { + spawnPedsMenu.AddMenuItem(groupedAddonPedsBtn); + MenuController.BindMenuItem(spawnPedsMenu, groupedAddonPedsMenu, groupedAddonPedsBtn); + + Menu unavailablePeds = new Menu("Grouped Addon Peds", "Unavailable Grouped Addon Peds"); + MenuItem unavailablePedsBtn = new MenuItem("Unavailable Peds", "These peds are not (correctly) streamed. If you are the server owner, please ensure that the ped name and model are valid!") { Label = "→→→" }; + MenuController.AddSubmenu(groupedAddonPedsMenu, unavailablePeds); + + var addons = AddonPedGroups.ToList(); + + foreach (Group group in AddonPedGroups) + { + Menu groupMenu = new Menu($"{group.label} Spawner", group.description); + MenuItem groupButton = new MenuItem(group.label, $"Spawn an addon ped from the {group.label} group.") { Label = "→→→" }; + + groupedAddonPedsMenu.AddMenuItem(groupButton); + + group.items.OrderBy(x => x); + + foreach (GroupItem groupOption in group.items) + { + uint pedHash = (uint)GetHashKey(groupOption.model); + string name = GetLabelText(groupOption.model); + + if (string.IsNullOrEmpty(name) || name == "NULL") + { + name = groupOption.model; + } + + MenuItem pedButton = new MenuItem(name, $"Click to spawn {name}.") + { + Label = $"({groupOption.model})", + ItemData = groupOption.model + }; + + if (IsModelInCdimage(pedHash)) + { + groupMenu.AddMenuItem(pedButton); + } + else + { + pedButton.Enabled = false; + pedButton.Description = "This ped is not available. Please ask the server owner to check if the ped is being streamed correctly."; + pedButton.LeftIcon = MenuItem.Icon.LOCK; + unavailablePeds.AddMenuItem(pedButton); + } + } + + if (groupMenu.Size > 0) + { + MenuController.AddSubmenu(groupedAddonPedsMenu, groupMenu); + MenuController.BindMenuItem(groupedAddonPedsMenu, groupMenu, groupButton); + + groupMenu.OnItemSelect += async (sender, item, index) => + { + await SetPlayerSkin((uint)GetHashKey(item.ItemData), new PedInfo() { version = -1 }, true); + }; + } + else + { + groupButton.Description = "There are no peds available in this group."; + groupButton.Enabled = false; + groupButton.LeftIcon = MenuItem.Icon.LOCK; + groupButton.Label = ""; + } + } + + if (unavailablePeds.Size > 0) + { + groupedAddonPedsMenu.AddMenuItem(unavailablePedsBtn); + MenuController.BindMenuItem(groupedAddonPedsMenu, unavailablePeds, unavailablePedsBtn); + } + } + if (IsAllowed(Permission.PASpawnNew)) { spawnPedsMenu.AddMenuItem(spawnByNameBtn); diff --git a/vMenu/menus/VehicleSpawner.cs b/vMenu/menus/VehicleSpawner.cs index 316a1a29..35f3ab6b 100644 --- a/vMenu/menus/VehicleSpawner.cs +++ b/vMenu/menus/VehicleSpawner.cs @@ -1,14 +1,13 @@ -using System; using System.Collections.Generic; using System.Linq; -using System.Text; -using System.Threading.Tasks; -using MenuAPI; -using Newtonsoft.Json; + using CitizenFX.Core; -using static CitizenFX.Core.UI.Screen; + +using MenuAPI; + using static CitizenFX.Core.Native.API; using static vMenuClient.CommonFunctions; +using static vMenuShared.ConfigManager; using static vMenuShared.PermissionsManager; namespace vMenuClient @@ -18,6 +17,7 @@ public class VehicleSpawner // Variables private Menu menu; public static Dictionary AddonVehicles; + public static List AddonVehiclesGroups; public bool SpawnInVehicle { get; private set; } = UserDefaults.VehicleSpawnerSpawnInside; public bool ReplaceVehicle { get; private set; } = UserDefaults.VehicleSpawnerReplacePrevious; @@ -154,6 +154,90 @@ private void CreateMenu() } #endregion + #region Addon Vehicle Group Menu + Menu groupAddonCarsMenu = new Menu("Grouped Addon Vehicles", "Spawn a Vehicle from Addon Groups"); + MenuItem groupAddonCarsBtn = new MenuItem("Grouped Addon Vehicles", "A list of grouped vehicles available on this server.") { Label = "→→→" }; + + menu.AddMenuItem(groupAddonCarsBtn); + + if (AddonVehiclesGroups != null && AddonVehiclesGroups.Count > 0 && IsAllowed(Permission.VSAddon)) + { + MenuController.BindMenuItem(menu, groupAddonCarsMenu, groupAddonCarsBtn); + MenuController.AddSubmenu(menu, groupAddonCarsMenu); + Menu unavailableCars = new Menu("Grouped Vehicles", "Unavailable Vehicles"); + MenuItem unavailableCarsBtn = new MenuItem("Unavailable Vehicles", "These vehicles are not currently being streamed (correctly) and are not able to be spawned.") { Label = "→→→" }; + MenuController.AddSubmenu(groupAddonCarsMenu, unavailableCars); + + foreach (Group group in AddonVehiclesGroups) + { + Menu groupMenu = new Menu($"{group.label} Spawner", group.description); + MenuItem groupButton = new MenuItem(group.label, $"Spawn an addon vehicle from the {group.label} group.") { Label = "→→→" }; + + groupAddonCarsMenu.AddMenuItem(groupButton); + + group.items.OrderBy(x => x); + + foreach (GroupItem groupOption in group.items) + { + uint vehicleHash = (uint)GetHashKey(groupOption.model); + string localizedName = GetLabelText(GetDisplayNameFromVehicleModel(vehicleHash)); + + string name = localizedName != "NULL" ? localizedName : GetDisplayNameFromVehicleModel(vehicleHash); + name = name != "CARNOTFOUND" ? name : groupOption.label; + + MenuItem carBtn = new MenuItem(name, $"Click to spawn {name}.") + { + Label = $"({groupOption.model})", + ItemData = groupOption.model + }; + + if (IsModelInCdimage(vehicleHash)) + { + groupMenu.AddMenuItem(carBtn); + } + else + { + carBtn.Enabled = false; + carBtn.Description = "This vehicle is not available. Please ask the server owner to check if the vehicle is being streamed correctly."; + carBtn.LeftIcon = MenuItem.Icon.LOCK; + unavailableCars.AddMenuItem(carBtn); + } + } + + if (groupMenu.Size > 0) + { + MenuController.AddSubmenu(groupAddonCarsMenu, groupMenu); + MenuController.BindMenuItem(groupAddonCarsMenu, groupMenu, groupButton); + + groupMenu.OnItemSelect += (sender, item, index) => + { + SpawnVehicle(item.ItemData.ToString(), SpawnInVehicle, ReplaceVehicle); + }; + } + else + { + groupButton.Description = "There are no cars available in this group."; + groupButton.Enabled = false; + groupButton.LeftIcon = MenuItem.Icon.LOCK; + groupButton.Label = ""; + } + } + + if (unavailableCars.Size > 0) + { + groupAddonCarsMenu.AddMenuItem(unavailableCarsBtn); + MenuController.BindMenuItem(groupAddonCarsMenu, unavailableCars, unavailableCarsBtn); + } + } + else + { + groupAddonCarsBtn.Enabled = false; + groupAddonCarsBtn.LeftIcon = MenuItem.Icon.LOCK; + groupAddonCarsBtn.Description = "Access to this list has been restricted by the server owner."; + } + + #endregion + // These are the max speed, acceleration, braking and traction values per vehicle class. float[] speedValues = new float[23] { diff --git a/vMenu/menus/WeaponOptions.cs b/vMenu/menus/WeaponOptions.cs index 0c384ff2..e5237511 100644 --- a/vMenu/menus/WeaponOptions.cs +++ b/vMenu/menus/WeaponOptions.cs @@ -1,14 +1,13 @@ -using System; using System.Collections.Generic; using System.Linq; -using System.Text; -using System.Threading.Tasks; -using MenuAPI; -using Newtonsoft.Json; + using CitizenFX.Core; -using static CitizenFX.Core.UI.Screen; + +using MenuAPI; + using static CitizenFX.Core.Native.API; using static vMenuClient.CommonFunctions; +using static vMenuShared.ConfigManager; using static vMenuShared.PermissionsManager; namespace vMenuClient @@ -24,6 +23,7 @@ public class WeaponOptions public bool UnlimitedParachutes { get; private set; } = UserDefaults.WeaponsUnlimitedParachutes; public static Dictionary AddonWeapons = new Dictionary(); + public static List AddonWeaponGroups; private Dictionary weaponInfo; private Dictionary weaponComponents; @@ -126,6 +126,85 @@ private void CreateMenu() addonWeaponsMenu.RefreshIndex(); #endregion + #region group addon weapons submenu + MenuItem groupAddonWeaponsBtn = new MenuItem("Grouped Addon Weapons", "Equip / remove addon weapons available on this server."); + Menu groupAddonWeaponsMenu = new Menu("Grouped Addon Weapons", "Equip/Remove Addon Weapons"); + menu.AddMenuItem(groupAddonWeaponsBtn); + + #region manage creating and accessing grouped addon weapons menu + + if (AddonWeaponGroups != null && AddonWeaponGroups.Count > 0 && IsAllowed(Permission.WPSpawn)) + { + MenuController.BindMenuItem(menu, groupAddonWeaponsMenu, groupAddonWeaponsBtn); + MenuController.AddSubmenu(menu, groupAddonWeaponsMenu); + + foreach (Group group in AddonWeaponGroups) + { + Menu groupMenu = new Menu($"{group.label}", group.description); + MenuItem groupButton = new MenuItem(group.label, $"Equip/remove a grouped addon weapon from the {group.label} group.") { Label = "→→→" }; + + groupAddonWeaponsMenu.AddMenuItem(groupButton); + + group.items.OrderBy(x => x); + + foreach (GroupItem groupOption in group.items) + { + string name = groupOption.label; + uint model = (uint)GetHashKey(groupOption.model); + var item = new MenuItem(name, $"Click to add/remove this weapon ({name}) to/from your inventory.") + { + ItemData = model + }; + addonWeaponsMenu.AddMenuItem(item); + + if (!IsWeaponValid(model)) + { + item.Enabled = false; + item.LeftIcon = MenuItem.Icon.LOCK; + item.Description = "This model is not available. Please ask the server owner to verify it's being streamed correctly."; + } + } + + if (groupMenu.Size > 0) + { + MenuController.AddSubmenu(groupAddonWeaponsMenu, groupMenu); + MenuController.BindMenuItem(groupAddonWeaponsMenu, groupMenu, groupButton); + + groupMenu.OnItemSelect += (sender, item, index) => + { + var weapon = item.ItemData; + if (HasPedGotWeapon(Game.PlayerPed.Handle, weapon, false)) + { + RemoveWeaponFromPed(Game.PlayerPed.Handle, weapon); + } + else + { + var maxAmmo = 200; + GetMaxAmmo(Game.PlayerPed.Handle, weapon, ref maxAmmo); + GiveWeaponToPed(Game.PlayerPed.Handle, weapon, maxAmmo, false, true); + } + }; + } + else + { + groupButton.Description = "There are no weapons available in this group."; + groupButton.Enabled = false; + groupButton.LeftIcon = MenuItem.Icon.LOCK; + groupButton.Label = ""; + } + } + } + else + { + addonWeaponsBtn.LeftIcon = MenuItem.Icon.LOCK; + addonWeaponsBtn.Enabled = false; + addonWeaponsBtn.Description = "This option is not available on this server because you don't have permission to use it, or it is not setup correctly."; + } + + #endregion + groupAddonWeaponsMenu.RefreshIndex(); + #endregion + #region parachute options menu if (IsAllowed(Permission.WPParachute)) diff --git a/vMenuServer/MainServer.cs b/vMenuServer/MainServer.cs index 69d1dcfd..1e0af3c3 100644 --- a/vMenuServer/MainServer.cs +++ b/vMenuServer/MainServer.cs @@ -2,12 +2,16 @@ using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; + using CitizenFX.Core; -using static CitizenFX.Core.Native.API; + using Newtonsoft.Json; + +using vMenuShared; + +using static CitizenFX.Core.Native.API; using static vMenuServer.DebugLog; using static vMenuShared.ConfigManager; -using vMenuShared; namespace vMenuServer { @@ -982,5 +986,30 @@ private void OnPlayerDropped([FromSource] Player sourcePlayer, string reason) } } #endregion + + #region Addon Lists with Server Side Permissions + + [EventHandler("vMenu:RequestAddonVehicleGroups")] + private void RequestAddonVehicleGroups([FromSource] Player sourcePlayer, NetworkCallbackDelegate networkCallbackDelegate) + { + List lst = ConfigManager.GetAddonsVehicleGroupData(sourcePlayer); + _ = networkCallbackDelegate(JsonConvert.SerializeObject(lst)); + } + + [EventHandler("vMenu:RequestAddonPedGroups")] + private void RequestAddonPedGroups([FromSource] Player sourcePlayer, NetworkCallbackDelegate networkCallbackDelegate) + { + List lst = ConfigManager.GetAddonsPedGroupData(sourcePlayer); + _ = networkCallbackDelegate(JsonConvert.SerializeObject(lst)); + } + + [EventHandler("vMenu:RequestAddonWeaponGroups")] + private void RequestAddonWeaponGroups([FromSource] Player sourcePlayer, NetworkCallbackDelegate networkCallbackDelegate) + { + List lst = ConfigManager.GetAddonsWeaponGroupData(sourcePlayer); + _ = networkCallbackDelegate(JsonConvert.SerializeObject(lst)); + } + + #endregion } } diff --git a/vMenuServer/config/addons.json b/vMenuServer/config/addons.json index b2f1a750..15e2a7c9 100644 --- a/vMenuServer/config/addons.json +++ b/vMenuServer/config/addons.json @@ -14,5 +14,100 @@ "weapon_components": [ "weapon_component_name_1", "weapon_component_name_2" - ] + ], + "groups": { + "vehicles": [ + { + "permission": "VehicleSpawner.Staff", + "label": "Staff Vehicles", + "description": "Staff Vehicles", + "items": [ + { + "label": "Addon Vehicle Name 1", + "model": "addonvehiclename1" + }, + { + "label": "Addon Vehicle Name 2", + "model": "addonvehiclename2" + } + ] + }, + { + "label": "Other Vehicles", + "description": "Other Vehicles", + "items": [ + { + "label": "Addon Vehicle Name 1", + "model": "addonvehiclename1" + }, + { + "label": "Addon Vehicle Name 2", + "model": "addonvehiclename2" + } + ] + } + ], + "peds": [ + { + "permission": "PlayerAppearance.Staff", + "label": "Staff Peds", + "description": "Staff Peds", + "items": [ + { + "label": "Addon Ped Name 1", + "model": "addonpedname1" + }, + { + "label": "Addon Ped Name 2", + "model": "addonpedname2" + } + ] + }, + { + "label": "Other Peds", + "description": "Other Peds", + "items": [ + { + "label": "Addon Ped Name 1", + "model": "addonpedname1" + }, + { + "label": "Addon Ped Name 2", + "model": "addonpedname2" + } + ] + } + ], + "weapons": [ + { + "permission": "WeaponOptions.Staff", + "label": "Staff Weapons", + "description": "Staff Weapons", + "items": [ + { + "label": "Addon Weapon Name 1", + "model": "addonweaponname1" + }, + { + "label": "Addon Weapon Name 2", + "model": "addonweaponname2" + } + ] + }, + { + "label": "Other Weapons", + "description": "Other Weapons", + "items": [ + { + "label": "Addon Weapon Name 1", + "model": "addonweaponname1" + }, + { + "label": "Addon Weapon Name 2", + "model": "addonweaponname2" + } + ] + } + ] + } } diff --git a/vMenuServer/config/permissions.cfg b/vMenuServer/config/permissions.cfg index a83e04fd..07eaa704 100644 --- a/vMenuServer/config/permissions.cfg +++ b/vMenuServer/config/permissions.cfg @@ -275,6 +275,7 @@ add_ace builtin.everyone "vMenu.VehicleSpawner.All" allow #add_ace builtin.everyone "vMenu.VehicleSpawner.DisableReplacePrevious" allow #add_ace builtin.everyone "vMenu.VehicleSpawner.SpawnByName" allow #add_ace builtin.everyone "vMenu.VehicleSpawner.Addon" allow # allows you to spawn an addon car from the Addon Vehicles list. +#add_ace group.staff "vMenu.VehicleSpawner.Staff" allow # example for ACE Group Staff to access Grouped Addon Vehicles with the same permission #add_ace builtin.everyone "vMenu.VehicleSpawner.Compacts" allow #add_ace builtin.everyone "vMenu.VehicleSpawner.Sedans" allow #add_ace builtin.everyone "vMenu.VehicleSpawner.SUVs" allow @@ -326,6 +327,7 @@ add_ace builtin.everyone "vMenu.PersonalVehicle.All" allow #################################### add_ace builtin.everyone "vMenu.PlayerAppearance.Menu" allow add_ace builtin.everyone "vMenu.PlayerAppearance.All" allow +#add_ace group.staff "vMenu.PlayerAppearance.Staff" allow # example for ACE Group Staff to access Grouped Addon Peds with the same permission #add_ace builtin.everyone "vMenu.PlayerAppearance.Customize" allow #add_ace builtin.everyone "vMenu.PlayerAppearance.SpawnSaved" allow #add_ace builtin.everyone "vMenu.PlayerAppearance.SpawnNew" allow @@ -370,6 +372,7 @@ add_ace builtin.everyone "vMenu.WeaponOptions.All" allow # weapons that players can access using the weapon options menu by granting permissions # for every weapon that you want to allow below. "vMenu.WeaponOptions.All" automatically grants all weapons. +# add_ace group.staff "vMenu.PlayerAppearance.Staff" allow # example for ACE Group Staff to access Grouped Addon Weapons with the same permission # add_ace builtin.everyone "vMenu.WeaponOptions.APPistol" allow # add_ace builtin.everyone "vMenu.WeaponOptions.AdvancedRifle" allow # add_ace builtin.everyone "vMenu.WeaponOptions.AssaultRifle" allow