From 19f47b7f70b07bcdcbc0f188013746051f2cf821 Mon Sep 17 00:00:00 2001
From: Ricky Merc <65817116+RickyB505@users.noreply.github.com>
Date: Mon, 6 Apr 2026 14:42:30 -0400
Subject: [PATCH 01/11] fix(vehicle): fixes bug with underglow colour grabbing
---
vMenu/menus/VehicleOptions.cs | 10 ++++++----
1 file changed, 6 insertions(+), 4 deletions(-)
diff --git a/vMenu/menus/VehicleOptions.cs b/vMenu/menus/VehicleOptions.cs
index 17da329be..e9f755aa0 100644
--- a/vMenu/menus/VehicleOptions.cs
+++ b/vMenu/menus/VehicleOptions.cs
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
+using System.Drawing;
using System.Linq;
using CitizenFX.Core;
@@ -2541,7 +2542,7 @@ internal static int GetHeadlightsColorForVehicle(Vehicle vehicle)
///
///
///
- private System.Drawing.Color GetColorFromIndex(int index)
+ private static System.Drawing.Color GetColorFromIndex(int index)
{
if (index is >= 0 and < 13)
{
@@ -2807,9 +2808,10 @@ public static void CreateCustomColourMenu(Menu menu, RGBType type = RGBType.prim
}
else if (type == RGBType.underglow)
{
- int red = vehicle.Mods.TireSmokeColor.R;
- int green = vehicle.Mods.NeonLightsColor.G;
- int blue = vehicle.Mods.NeonLightsColor.B;
+ Color underglow = GetColorFromIndex(newIndex);
+ int red = underglow.R;
+ int green = underglow.G;
+ int blue = underglow.B;
redColour.BarColor = System.Drawing.Color.FromArgb(255, red, green, blue);
greenColour.BarColor = System.Drawing.Color.FromArgb(255, red, green, blue);
From 26b1db7c6b494b9034e025043717d8d062389406 Mon Sep 17 00:00:00 2001
From: Ricky Merc <65817116+RickyB505@users.noreply.github.com>
Date: Mon, 6 Apr 2026 14:49:36 -0400
Subject: [PATCH 02/11] fix(vehicle): headlight index going out of range
---
vMenu/menus/VehicleOptions.cs | 15 ++++++++-------
1 file changed, 8 insertions(+), 7 deletions(-)
diff --git a/vMenu/menus/VehicleOptions.cs b/vMenu/menus/VehicleOptions.cs
index e9f755aa0..cc4ba3986 100644
--- a/vMenu/menus/VehicleOptions.cs
+++ b/vMenu/menus/VehicleOptions.cs
@@ -2723,9 +2723,10 @@ public static void CreateCustomColourMenu(Menu menu, RGBType type = RGBType.prim
{
if (IsToggleModOn(vehicle.Handle, 22))
{
- r = VehicleData.NeonLightColors[GetHeadlightsColorForVehicle(vehicle)][0];
- g = VehicleData.NeonLightColors[GetHeadlightsColorForVehicle(vehicle)][1];
- b = VehicleData.NeonLightColors[GetHeadlightsColorForVehicle(vehicle)][2];
+ int headlight = GetHeadlightsColorForVehicle(vehicle) < 0 ? 0 : GetHeadlightsColorForVehicle(vehicle);
+ r = VehicleData.NeonLightColors[headlight][0];
+ g = VehicleData.NeonLightColors[headlight][1];
+ b = VehicleData.NeonLightColors[headlight][2];
}
}
@@ -2828,10 +2829,10 @@ public static void CreateCustomColourMenu(Menu menu, RGBType type = RGBType.prim
}
else if (type == RGBType.headlight)
{
-
- int red = VehicleData.NeonLightColors[GetHeadlightsColorForVehicle(vehicle)][0];
- int green = VehicleData.NeonLightColors[GetHeadlightsColorForVehicle(vehicle)][1];
- int blue = VehicleData.NeonLightColors[GetHeadlightsColorForVehicle(vehicle)][2];
+ int headlight = GetHeadlightsColorForVehicle(vehicle) < 0 ? 0 : GetHeadlightsColorForVehicle(vehicle);
+ int red = VehicleData.NeonLightColors[headlight][0];
+ int green = VehicleData.NeonLightColors[headlight][1];
+ int blue = VehicleData.NeonLightColors[headlight][2];
redColour.BarColor = System.Drawing.Color.FromArgb(255, red, green, blue);
From 09acff53de697166b50a6806c15bd50e56eb8b95 Mon Sep 17 00:00:00 2001
From: Ricky Merc <65817116+RickyB505@users.noreply.github.com>
Date: Mon, 6 Apr 2026 14:57:14 -0400
Subject: [PATCH 03/11] fix(vehicle): fixes bugs related to spelling, and
missing text fields
---
vMenu/menus/VehicleOptions.cs | 57 ++++++++++++++++++-----------------
1 file changed, 29 insertions(+), 28 deletions(-)
diff --git a/vMenu/menus/VehicleOptions.cs b/vMenu/menus/VehicleOptions.cs
index cc4ba3986..6c89be7f7 100644
--- a/vMenu/menus/VehicleOptions.cs
+++ b/vMenu/menus/VehicleOptions.cs
@@ -2153,7 +2153,7 @@ public void UpdateMods(int selectedIndex = 0)
}
var headlightsButton = new MenuItem("Headlights");
- var headlightsMenu = new Menu("Headlights");
+ var headlightsMenu = new Menu("Headlights", "headlights");
var xenonHeadlights = new MenuCheckboxItem("Xenon Headlights", "Enable or disable ~b~xenon ~s~headlights.", IsToggleModOn(veh.Handle, 22));
headlightsMenu.AddMenuItem(xenonHeadlights);
var currentHeadlightColor = GetHeadlightsColorForVehicle(veh);
@@ -2215,7 +2215,7 @@ public void UpdateMods(int selectedIndex = 0)
}
var tireSmokeButton = new MenuItem("Tire Smoke");
- var tireSmokeMenu = new Menu("Tire Smoke");
+ var tireSmokeMenu = new Menu("Tire Smoke", "Tire Smoke");
// Create a list of tire smoke options.
var tireSmokes = new List() { "Red", "Orange", "Yellow", "Gold", "Light Green", "Dark Green", "Light Blue", "Dark Blue", "Purple", "Pink", "Black" };
var tireSmokeColors = new Dictionary()
@@ -2587,22 +2587,22 @@ public enum RGBType
public static void CreateCustomColourMenu(Menu menu, RGBType type = RGBType.primaryPaint)
{
- var hexColour = new MenuItem("Hex Colour Code");
+ var hexColour = new MenuItem("Hex Color Code");
var typeList = new MenuListItem("Paint Finish", Enum.GetNames(typeof(Colour_Types)).ToList(), 0);
int r = 0, g = 0, b = 0;
- var redColour = new MenuSliderItem("Red Colour", 0, 255, 128, true)
+ var redColour = new MenuSliderItem("Red Color", 0, 255, 128, true)
{
BarColor = System.Drawing.Color.FromArgb(155, 0, 0, 0),
BackgroundColor = System.Drawing.Color.FromArgb(200, 79, 79, 79),
};
- var greenColour = new MenuSliderItem("Green Colour", 0, 255, 128, true)
+ var greenColour = new MenuSliderItem("Green Color", 0, 255, 128, true)
{
BarColor = System.Drawing.Color.FromArgb(155, 0, 0, 0),
BackgroundColor = System.Drawing.Color.FromArgb(200, 79, 79, 79),
};
- var blueColour = new MenuSliderItem("Blue Colour", 0, 255, 128, true)
+ var blueColour = new MenuSliderItem("Blue Color", 0, 255, 128, true)
{
BarColor = System.Drawing.Color.FromArgb(155, 0, 0, 0),
BackgroundColor = System.Drawing.Color.FromArgb(200, 79, 79, 79),
@@ -2635,9 +2635,10 @@ public static void CreateCustomColourMenu(Menu menu, RGBType type = RGBType.prim
{
if (IsToggleModOn(vehicle.Handle, 22))
{
- r = VehicleData.NeonLightColors[GetHeadlightsColorForVehicle(vehicle)][0];
- g = VehicleData.NeonLightColors[GetHeadlightsColorForVehicle(vehicle)][1];
- b = VehicleData.NeonLightColors[GetHeadlightsColorForVehicle(vehicle)][2];
+ int headlight = GetHeadlightsColorForVehicle(vehicle) < 0 ? 0 : GetHeadlightsColorForVehicle(vehicle);
+ r = VehicleData.NeonLightColors[headlight][0];
+ g = VehicleData.NeonLightColors[headlight][1];
+ b = VehicleData.NeonLightColors[headlight][2];
}
}
@@ -2646,13 +2647,13 @@ public static void CreateCustomColourMenu(Menu menu, RGBType type = RGBType.prim
GetVehicleTyreSmokeColor(vehicle.Handle, ref r, ref g, ref b);
- redColour.Text = $"Red Colour ({r})";
+ redColour.Text = $"Red Color ({r})";
redColour.Position = r;
- greenColour.Text = $"Green Colour ({g})";
+ greenColour.Text = $"Green Color ({g})";
greenColour.Position = g;
- blueColour.Text = $"Blue Colour ({b})";
+ blueColour.Text = $"Blue Color ({b})";
blueColour.Position = b;
redColour.BarColor = System.Drawing.Color.FromArgb(255, redColour.Position, greenColour.Position, blueColour.Position);
@@ -2666,7 +2667,7 @@ public static void CreateCustomColourMenu(Menu menu, RGBType type = RGBType.prim
if (item == hexColour)
{
string hexValue = redColour.Position.ToString("X2") + greenColour.Position.ToString("X2") + blueColour.Position.ToString("X2");
- var result = await GetUserInput(windowTitle: "Enter Colour Hex", defaultText: (hexValue).Replace("#", ""), maxInputLength: 6);
+ var result = await GetUserInput(windowTitle: "Enter Color Hex", defaultText: (hexValue).Replace("#", ""), maxInputLength: 6);
if (!string.IsNullOrEmpty(result))
{
if (IsHex(result))
@@ -2691,13 +2692,13 @@ public static void CreateCustomColourMenu(Menu menu, RGBType type = RGBType.prim
greenColour.BarColor = System.Drawing.Color.FromArgb(255, red, green, blue);
blueColour.BarColor = System.Drawing.Color.FromArgb(255, red, green, blue);
- redColour.Text = $"Red Colour ({red})";
+ redColour.Text = $"Red Color ({red})";
redColour.Position = red;
- greenColour.Text = $"Green Colour ({green})";
+ greenColour.Text = $"Green Color ({green})";
greenColour.Position = green;
- blueColour.Text = $"Blue Colour ({blue})";
+ blueColour.Text = $"Blue Color ({blue})";
blueColour.Position = blue;
}
}
@@ -2748,7 +2749,7 @@ public static void CreateCustomColourMenu(Menu menu, RGBType type = RGBType.prim
else if (type == RGBType.tiresmoke)
vehicle.Mods.TireSmokeColor = System.Drawing.Color.FromArgb(255, newPosition, g, b);
- redColour.Text = $"Red Colour ({newPosition})";
+ redColour.Text = $"Red Color ({newPosition})";
}
if (sliderItem == greenColour)
{
@@ -2763,7 +2764,7 @@ public static void CreateCustomColourMenu(Menu menu, RGBType type = RGBType.prim
else if (type == RGBType.tiresmoke)
vehicle.Mods.TireSmokeColor = System.Drawing.Color.FromArgb(255, r, newPosition, b);
- greenColour.Text = $"Green Colour ({newPosition})";
+ greenColour.Text = $"Green Color ({newPosition})";
}
if (sliderItem == blueColour)
{
@@ -2778,7 +2779,7 @@ public static void CreateCustomColourMenu(Menu menu, RGBType type = RGBType.prim
else if (type == RGBType.tiresmoke)
vehicle.Mods.TireSmokeColor = System.Drawing.Color.FromArgb(255, r, g, newPosition);
- blueColour.Text = $"Blue Colour ({newPosition})";
+ blueColour.Text = $"Blue Color ({newPosition})";
}
redColour.BarColor = System.Drawing.Color.FromArgb(255, redColour.Position, greenColour.Position, blueColour.Position);
greenColour.BarColor = System.Drawing.Color.FromArgb(255, redColour.Position, greenColour.Position, blueColour.Position);
@@ -2818,13 +2819,13 @@ public static void CreateCustomColourMenu(Menu menu, RGBType type = RGBType.prim
greenColour.BarColor = System.Drawing.Color.FromArgb(255, red, green, blue);
blueColour.BarColor = System.Drawing.Color.FromArgb(255, red, green, blue);
- redColour.Text = $"Red Colour ({red})";
+ redColour.Text = $"Red Color ({red})";
redColour.Position = red;
- greenColour.Text = $"Green Colour ({green})";
+ greenColour.Text = $"Green Color ({green})";
greenColour.Position = green;
- blueColour.Text = $"Blue Colour ({blue})";
+ blueColour.Text = $"Blue Color ({blue})";
blueColour.Position = blue;
}
else if (type == RGBType.headlight)
@@ -2839,13 +2840,13 @@ public static void CreateCustomColourMenu(Menu menu, RGBType type = RGBType.prim
greenColour.BarColor = System.Drawing.Color.FromArgb(255, red, green, blue);
blueColour.BarColor = System.Drawing.Color.FromArgb(255, red, green, blue);
- redColour.Text = $"Red Colour ({red})";
+ redColour.Text = $"Red Color ({red})";
redColour.Position = red;
- greenColour.Text = $"Green Colour ({green})";
+ greenColour.Text = $"Green Color ({green})";
greenColour.Position = green;
- blueColour.Text = $"Blue Colour ({blue})";
+ blueColour.Text = $"Blue Color ({blue})";
blueColour.Position = blue;
}
else if (type == RGBType.tiresmoke)
@@ -2858,13 +2859,13 @@ public static void CreateCustomColourMenu(Menu menu, RGBType type = RGBType.prim
greenColour.BarColor = System.Drawing.Color.FromArgb(255, red, green, blue);
blueColour.BarColor = System.Drawing.Color.FromArgb(255, red, green, blue);
- redColour.Text = $"Red Colour ({red})";
+ redColour.Text = $"Red Color ({red})";
redColour.Position = red;
- greenColour.Text = $"Green Colour ({green})";
+ greenColour.Text = $"Green Color ({green})";
greenColour.Position = green;
- blueColour.Text = $"Blue Colour ({blue})";
+ blueColour.Text = $"Blue Color ({blue})";
blueColour.Position = blue;
}
From ee278e9ebe9feb0b6a9f25c6f4c596e376bd0a73 Mon Sep 17 00:00:00 2001
From: Tom Grobbe
Date: Sat, 18 Apr 2026 19:08:26 +0200
Subject: [PATCH 04/11] Gitignore update
---
.gitignore | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/.gitignore b/.gitignore
index fdc8383c4..61d26fcf0 100644
--- a/.gitignore
+++ b/.gitignore
@@ -267,4 +267,6 @@ external/
*.cmd.bak
# VS Build Output
-build/
\ No newline at end of file
+build/
+
+*.lscache
\ No newline at end of file
From b6a8bbe60889186452d6a4b619fbc6a36bec44ae Mon Sep 17 00:00:00 2001
From: Tom <31419184+TomGrobbe@users.noreply.github.com>
Date: Fri, 5 Jun 2026 15:35:35 +0200
Subject: [PATCH 05/11] Supplemental permissions pilot (#619)
* feat/fix: addon permissions/updating outdated function (#610)
* feat/fix: addon permissions/updating outdated function
added individual permissions to addon vehicles/weapons/peds, and added a whitelist to regular vehicles/ped. changed (uint)GetHashKey() to Game.GenerateHashASCII() for better optimization
* fix: updated what was requested
* fix: typo
* fix: fixed what needed changing
* fix: fixed controller controls and permissions for weapons (#613)
* New features and fixes (#614)
* vehicle spawning ratelimit
* fix: move setting the vehicle speed inside where we check if they spawn inside
* feat: add command "vMenu:DV"
* feat: add back smooth time transitions
* fix: fixes server sided permissions so it checks the parent permissions
* fix: disable post build command on non windows machines
* fix: Changes and fixes requested
---------
Co-authored-by: Ricky Merc <65817116+RickyB505@users.noreply.github.com>
---
SharedClasses/ConfigManager.cs | 19 +-
SharedClasses/PermissionsManager.cs | 4 +-
.../SupplementaryPermissionManager.cs | 240 ++++++++++++++++++
vMenu/CommonFunctions.cs | 184 +++++++++++---
vMenu/EntitySpawner.cs | 2 +-
vMenu/EventManager.cs | 79 +++++-
vMenu/FunctionsController.cs | 198 +++++++++------
vMenu/MainMenu.cs | 43 +++-
vMenu/data/BlipInfo.cs | 105 ++++----
vMenu/data/PedModels.cs | 87 +++----
vMenu/data/ValidAddonWeapon.cs | 4 +-
vMenu/data/ValidWeapon.cs | 4 +-
vMenu/menus/MpPedCustomization.cs | 34 +--
vMenu/menus/PlayerAppearance.cs | 29 ++-
vMenu/menus/PlayerOptions.cs | 2 +-
vMenu/menus/Recording.cs | 2 +-
vMenu/menus/VehicleOptions.cs | 21 +-
vMenu/menus/VehicleSpawner.cs | 34 ++-
vMenu/menus/WeaponOptions.cs | 19 +-
vMenu/vMenuClient.csproj | 5 +-
vMenuServer/MainServer.cs | 132 ++++++++--
vMenuServer/config/model-whitelists.json | 23 ++
vMenuServer/config/permissions.cfg | 8 +
vMenuServer/vMenuServer.csproj | 6 +-
24 files changed, 962 insertions(+), 322 deletions(-)
create mode 100644 SharedClasses/SupplementaryPermissionManager.cs
create mode 100644 vMenuServer/config/model-whitelists.json
diff --git a/SharedClasses/ConfigManager.cs b/SharedClasses/ConfigManager.cs
index c8b07fe7a..5f212c2dd 100644
--- a/SharedClasses/ConfigManager.cs
+++ b/SharedClasses/ConfigManager.cs
@@ -34,8 +34,10 @@ public enum Setting
vmenu_disable_entity_outlines_tool,
vmenu_disable_player_stats_setup,
- // Vehicle Chameleon Colours
+ // Vehicle Settings
vmenu_using_chameleon_colours,
+ vmenu_vehicle_spawn_delay,
+ vmenu_delete_vehicle_distance,
// Prevent Extras Abuse
vmenu_prevent_extras_when_damaged,
@@ -61,6 +63,7 @@ public enum Setting
vmenu_vehicle_blackout_enabled,
vmenu_weather_change_duration,
vmenu_enable_snow,
+ vmenu_smooth_time_transitions,
// Time settings
vmenu_enable_time_sync,
@@ -92,12 +95,12 @@ public static bool GetSettingsBool(Setting setting)
///
///
///
- public static int GetSettingsInt(Setting setting)
+ public static int GetSettingsInt(Setting setting, int defaultValue = -1)
{
- var convarInt = GetConvarInt(setting.ToString(), -1);
- if (convarInt == -1)
+ var convarInt = GetConvarInt(setting.ToString(), defaultValue);
+ if (convarInt == defaultValue)
{
- if (int.TryParse(GetConvar(setting.ToString(), "-1"), out var convarIntAlt))
+ if (int.TryParse(GetConvar(setting.ToString(), defaultValue.ToString()), out var convarIntAlt))
{
return convarIntAlt;
}
@@ -110,13 +113,13 @@ public static int GetSettingsInt(Setting setting)
///
///
///
- public static float GetSettingsFloat(Setting setting)
+ public static float GetSettingsFloat(Setting setting, float defaultValue = -1f)
{
- if (float.TryParse(GetConvar(setting.ToString(), "-1.0"), out var result))
+ if (float.TryParse(GetConvar(setting.ToString(), defaultValue.ToString()), out var result))
{
return result;
}
- return -1f;
+ return defaultValue;
}
///
diff --git a/SharedClasses/PermissionsManager.cs b/SharedClasses/PermissionsManager.cs
index f3240bc6c..d5826c735 100644
--- a/SharedClasses/PermissionsManager.cs
+++ b/SharedClasses/PermissionsManager.cs
@@ -113,6 +113,7 @@ public enum Permission
#region vehicle spawner
VSMenu,
VSAll,
+ VSBypassRateLimit,
VSDisableReplacePrevious,
VSSpawnByName,
VSAddon,
@@ -481,8 +482,7 @@ private static bool IsAllowedServer(Permission permission, string playerHandle)
{
return false;
}
-
- return IsPlayerAceAllowed(playerHandle, GetAceName(permission));
+ return GetPermissionAndParentPermissions(permission).Any(p => IsPlayerAceAllowed(playerHandle, GetAceName(p)));
}
#endif
diff --git a/SharedClasses/SupplementaryPermissionManager.cs b/SharedClasses/SupplementaryPermissionManager.cs
new file mode 100644
index 000000000..8ef9944c6
--- /dev/null
+++ b/SharedClasses/SupplementaryPermissionManager.cs
@@ -0,0 +1,240 @@
+using System;
+
+using System.Collections.Generic;
+using System.Linq;
+
+using CitizenFX.Core;
+
+using static CitizenFX.Core.Native.API;
+using CitizenFX.Core.Native;
+
+namespace vMenuShared
+{
+ public static class SupplementaryPermissionManager
+ {
+ public static List Permission = new()
+ {
+ "VWAll",
+ "PWAll",
+ "WWAll",
+ };
+
+ public static Dictionary Permissions { get; private set; } = new Dictionary();
+ public static bool ArePermissionsSetup { get; set; } = false;
+
+
+#if SERVER
+ ///
+ /// Public function to check if a permission is allowed.
+ ///
+ ///
+ ///
+ ///
+ public static bool IsAllowed(string permission, Player source) => IsAllowedServer(permission, source);
+
+ ///
+ /// Public function to check if a permission is allowed.
+ ///
+ ///
+ ///
+ ///
+ public static bool IsAllowed(string permission, string playerHandle) => IsAllowedServer(permission, playerHandle);
+#endif
+
+#if CLIENT
+ ///
+ /// Public function to check if a permission is allowed.
+ ///
+ ///
+ /// if true, then the permissions will be checked even if they aren't setup yet.
+ ///
+ public static bool IsAllowed(string permission, bool checkAnyway = false) => IsAllowedClient(permission, checkAnyway);
+
+ private static readonly Dictionary allowedPerms = new();
+ ///
+ /// Private function that handles client side permission requests.
+ ///
+ ///
+ ///
+ private static bool IsAllowedClient(string permission, bool checkAnyway)
+ {
+ if (ArePermissionsSetup || checkAnyway)
+ {
+ if (allowedPerms.ContainsKey(permission) && allowedPerms[permission])
+ {
+ return true;
+ }
+ else if (!allowedPerms.ContainsKey(permission))
+ {
+ allowedPerms[permission] = false;
+ }
+
+ // Get a list of all permissions that are (parents) of the current permission, including the current permission.
+ var permissionsToCheck = GetPermissionAndParentPermissions(permission);
+
+ // Check if any of those permissions is allowed, if so, return true.
+ if (permissionsToCheck.Any(p => Permissions.ContainsKey(p) && Permissions[p]))
+ {
+ allowedPerms[permission] = true;
+ return true;
+ }
+ }
+ switch (permission.Substring(0, 2))
+ {
+ case "VW":
+ allowedPerms[permission] = PermissionsManager.IsAllowed(PermissionsManager.Permission.VSAll);
+ return PermissionsManager.IsAllowed(PermissionsManager.Permission.VSAll);
+ case "PW":
+ allowedPerms[permission] = PermissionsManager.IsAllowed(PermissionsManager.Permission.PAAll);
+ return PermissionsManager.IsAllowed(PermissionsManager.Permission.PAAll);
+ case "WW":
+ allowedPerms[permission] = PermissionsManager.IsAllowed(PermissionsManager.Permission.WPAll);
+ return PermissionsManager.IsAllowed(PermissionsManager.Permission.WPAll);
+ }
+ return false;
+ }
+#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;
+ }
+
+ return IsAllowedServer(permission, source.Handle);
+ }
+
+ ///
+ /// Checks if the player is allowed that specific permission.
+ ///
+ ///
+ ///
+ ///
+ private static bool IsAllowedServer(string permission, string playerHandle)
+ {
+ if (!DoesPlayerExist(playerHandle))
+ {
+ return false;
+ }
+
+ return GetPermissionAndParentPermissions(permission).Any(p => IsPlayerAceAllowed(playerHandle, GetAceName(p)));
+ }
+#endif
+
+ private static readonly Dictionary> parentPermissions = new();
+
+ ///
+ /// Gets the current permission and all parent permissions.
+ ///
+ ///
+ ///
+ public static List GetPermissionAndParentPermissions(string permission)
+ {
+ if (parentPermissions.ContainsKey(permission))
+ {
+ return parentPermissions[permission];
+ }
+ else
+ {
+ var list = new List() { "Everything", permission };
+
+ // if the first 2 characters are both uppercase
+ if (permission.Substring(0, 2).ToUpper() == permission.Substring(0, 2))
+ {
+ if (permission.Substring(2) is not "All")
+ {
+ list.AddRange(Permission.Where(a => a.ToString() == permission.Substring(0, 2) + "All"));
+ }
+ }
+ //else // it's one of the .Everything, .DontKickMe, DontBanMe, NoClip, Staff, etc perms that are not menu specific.
+ //{
+ // // do nothing
+ //}
+ parentPermissions[permission] = list;
+ return list;
+ }
+ }
+
+#if SERVER
+
+
+ ///
+ /// Sets the permissions for a specific player (checks server side, sends event to client side).
+ ///
+ ///
+ public static void SetPermissionsForPlayer([FromSource] Player player)
+ {
+ if (player == null)
+ {
+ return;
+ }
+
+ var perms = new Dictionary();
+
+ // Loop through all permissions and check if they're allowed.
+ foreach (string permission in Permission)
+ {
+ if (!perms.ContainsKey(permission))
+ {
+ perms.Add(permission, IsAllowed(permission, player)); // triggers IsAllowedServer
+ }
+ }
+ // Send the permissions to the client.
+ player.TriggerEvent("vMenu:SetSupplementaryPermissions", Newtonsoft.Json.JsonConvert.SerializeObject(perms));
+ }
+#endif
+#if CLIENT
+ ///
+ /// Sets the permission (client side event handler).
+ ///
+ ///
+ public static void SetPermissions(string permissions)
+ {
+ Permissions = Newtonsoft.Json.JsonConvert.DeserializeObject>(permissions);
+
+ // if debug logging.
+ if (GetResourceMetadata(GetCurrentResourceName(), "client_debug_mode", 0) == "true")
+ {
+ Debug.WriteLine("[vMenu] [Permissions] " + Newtonsoft.Json.JsonConvert.SerializeObject(Permissions, Newtonsoft.Json.Formatting.None));
+ }
+ }
+#endif
+#if SERVER
+ ///
+ /// Gets the full permission ace name for the specific enum.
+ ///
+ ///
+ ///
+ public static string GetAceName(string permission)
+ {
+ var name = permission.ToString();
+
+ var prefix = "vMenu.";
+
+ switch (name.Substring(0, 2))
+ {
+ case "VW":
+ prefix += "VehicleSpawner.WhitelistedModels";
+ break;
+ case "PW":
+ prefix += "PlayerAppearance.WhitelistedModels";
+ break;
+ case "WW":
+ prefix += "WeaponOptions.WhitelistedModels";
+ break;
+ default:
+ return prefix + name;
+ }
+
+ return prefix + "." + name.Substring(2);
+ }
+#endif
+ }
+}
diff --git a/vMenu/CommonFunctions.cs b/vMenu/CommonFunctions.cs
index 3dc95ebd0..cbf2e8025 100644
--- a/vMenu/CommonFunctions.cs
+++ b/vMenu/CommonFunctions.cs
@@ -4,6 +4,7 @@
using System.Threading.Tasks;
using CitizenFX.Core;
+using static CitizenFX.Core.Native.API;
using MenuAPI;
@@ -12,9 +13,9 @@
using vMenuClient.data;
using vMenuClient.menus;
-using static CitizenFX.Core.Native.API;
using static CitizenFX.Core.UI.Screen;
using static vMenuShared.PermissionsManager;
+using vMenuShared;
namespace vMenuClient
{
@@ -23,7 +24,6 @@ public static class CommonFunctions
#region Variables
private static string _currentScenario = "";
private static Vehicle _previousVehicle;
-
internal static bool DriveToWpTaskActive = false;
internal static bool DriveWanderTaskActive = false;
#endregion
@@ -121,7 +121,7 @@ public static async void LockOrUnlockDoors(Vehicle veh, bool lockDoors)
///
///
///
- public static string GetVehDisplayNameFromModel(string name) => GetLabelText(GetDisplayNameFromVehicleModel((uint)GetHashKey(name)));
+ public static string GetVehDisplayNameFromModel(string name) => GetLabelText(GetDisplayNameFromVehicleModel(Game.GenerateHashASCII(name)));
#endregion
#region DoesModelExist
@@ -130,7 +130,7 @@ public static async void LockOrUnlockDoors(Vehicle veh, bool lockDoors)
///
/// The model name
///
- public static bool DoesModelExist(string modelName) => DoesModelExist((uint)GetHashKey(modelName));
+ public static bool DoesModelExist(string modelName) => DoesModelExist(Game.GenerateHashASCII(modelName));
///
/// Does this model exist?
@@ -213,7 +213,7 @@ public static Vehicle GetVehicle(Player player, bool lastVehicle = false)
///
/// Entity/vehicle.
/// Returns the (uint) model hash from a (vehicle) entity.
- public static uint GetVehicleModel(int vehicle) => (uint)GetHashKey(GetEntityModel(vehicle).ToString());
+ public static uint GetVehicleModel(int vehicle) => Game.GenerateHashASCII(GetEntityModel(vehicle).ToString());
#endregion
#region Is ped pointing
@@ -873,35 +873,35 @@ public static async void CommitSuicide()
if (Game.PlayerPed.Weapons.HasWeapon(WeaponHash.PistolMk2))
{
- weaponHash = (uint)GetHashKey("WEAPON_PISTOL_MK2");
+ weaponHash = Game.GenerateHashASCII("WEAPON_PISTOL_MK2");
}
else if (Game.PlayerPed.Weapons.HasWeapon(WeaponHash.CombatPistol))
{
- weaponHash = (uint)GetHashKey("WEAPON_COMBATPISTOL");
+ weaponHash = Game.GenerateHashASCII("WEAPON_COMBATPISTOL");
}
else if (Game.PlayerPed.Weapons.HasWeapon(WeaponHash.Pistol))
{
- weaponHash = (uint)GetHashKey("WEAPON_PISTOL");
+ weaponHash = Game.GenerateHashASCII("WEAPON_PISTOL");
}
- else if (Game.PlayerPed.Weapons.HasWeapon((WeaponHash)(uint)GetHashKey("WEAPON_SNSPISTOL_MK2")))
+ else if (Game.PlayerPed.Weapons.HasWeapon((WeaponHash)Game.GenerateHashASCII("WEAPON_SNSPISTOL_MK2")))
{
- weaponHash = (uint)GetHashKey("WEAPON_SNSPISTOL_MK2");
+ weaponHash = Game.GenerateHashASCII("WEAPON_SNSPISTOL_MK2");
}
else if (Game.PlayerPed.Weapons.HasWeapon(WeaponHash.SNSPistol))
{
- weaponHash = (uint)GetHashKey("WEAPON_SNSPISTOL");
+ weaponHash = Game.GenerateHashASCII("WEAPON_SNSPISTOL");
}
else if (Game.PlayerPed.Weapons.HasWeapon(WeaponHash.Pistol50))
{
- weaponHash = (uint)GetHashKey("WEAPON_PISTOL50");
+ weaponHash = Game.GenerateHashASCII("WEAPON_PISTOL50");
}
else if (Game.PlayerPed.Weapons.HasWeapon(WeaponHash.HeavyPistol))
{
- weaponHash = (uint)GetHashKey("WEAPON_HEAVYPISTOL");
+ weaponHash = Game.GenerateHashASCII("WEAPON_HEAVYPISTOL");
}
else if (Game.PlayerPed.Weapons.HasWeapon(WeaponHash.VintagePistol))
{
- weaponHash = (uint)GetHashKey("WEAPON_VINTAGEPISTOL");
+ weaponHash = Game.GenerateHashASCII("WEAPON_VINTAGEPISTOL");
}
else
{
@@ -912,7 +912,7 @@ public static async void CommitSuicide()
// If we take the pill, remove any weapons in our hands.
if (takePill)
{
- SetCurrentPedWeapon(Game.PlayerPed.Handle, (uint)GetHashKey("weapon_unarmed"), true);
+ SetCurrentPedWeapon(Game.PlayerPed.Handle, Game.GenerateHashASCII("weapon_unarmed"), true);
}
// Otherwise, give the ped a gun.
else if (weaponHash != null)
@@ -922,8 +922,8 @@ public static async void CommitSuicide()
}
else
{
- GiveWeaponToPed(Game.PlayerPed.Handle, (uint)GetHashKey("weapon_pistol_mk2"), 1, false, true);
- SetCurrentPedWeapon(Game.PlayerPed.Handle, (uint)GetHashKey("weapon_pistol_mk2"), true);
+ GiveWeaponToPed(Game.PlayerPed.Handle, Game.GenerateHashASCII("weapon_pistol_mk2"), 1, false, true);
+ SetCurrentPedWeapon(Game.PlayerPed.Handle, Game.GenerateHashASCII("weapon_pistol_mk2"), true);
SetPedDropsWeaponsWhenDead(Game.PlayerPed.Handle, true);
}
@@ -935,7 +935,7 @@ public static async void CommitSuicide()
while (true)
{
var time = GetEntityAnimCurrentTime(Game.PlayerPed.Handle, "MP_SUICIDE", takePill ? "pill" : "pistol");
- if (HasAnimEventFired(Game.PlayerPed.Handle, (uint)GetHashKey("Fire")) && !shot) // shoot the gun if the animation event is triggered.
+ if (HasAnimEventFired(Game.PlayerPed.Handle, Game.GenerateHashASCII("Fire")) && !shot) // shoot the gun if the animation event is triggered.
{
ClearEntityLastDamageEntity(Game.PlayerPed.Handle);
SetPedShootsAtCoord(Game.PlayerPed.Handle, 0f, 0f, 0f, false);
@@ -1209,7 +1209,7 @@ public static async Task SpawnVehicle(string vehicleName = "custom", bool s
if (!string.IsNullOrEmpty(result))
{
// Convert it into a model hash.
- var model = (uint)GetHashKey(result);
+ var model = Game.GenerateHashASCII(result);
return await SpawnVehicle(vehicleHash: model, spawnInside: spawnInside, replacePrevious: replacePrevious, skipLoad: false, vehicleInfo: new VehicleInfo(),
saveName: null);
}
@@ -1220,12 +1220,14 @@ public static async Task SpawnVehicle(string vehicleName = "custom", bool s
return 0;
}
}
- return await SpawnVehicle(vehicleHash: (uint)GetHashKey(vehicleName), spawnInside: spawnInside, replacePrevious: replacePrevious, skipLoad: false,
+ return await SpawnVehicle(vehicleHash: Game.GenerateHashASCII(vehicleName), spawnInside: spawnInside, replacePrevious: replacePrevious, skipLoad: false,
vehicleInfo: new VehicleInfo(), saveName: null);
}
#endregion
#region Main Spawn Vehicle Function
+ public static int lastSpawnTime = 0;
+ public static int spawnTime = ConfigManager.GetSettingsInt(ConfigManager.Setting.vmenu_vehicle_spawn_delay, 5) * 1000;
///
/// Spawns a vehicle.
///
@@ -1252,6 +1254,27 @@ public static async Task SpawnVehicle(uint vehicleHash, bool spawnInside, b
Notify.Alert("You are not allowed to spawn this vehicle, because it belongs to a category which is restricted by the server owner.");
return 0;
}
+
+ if (VehicleSpawner.WhitelistVehicles.Values.Contains(vehicleHash))
+ {
+ if (!vMenuShared.SupplementaryPermissionManager.IsAllowed("VW" + VehicleSpawner.WhitelistVehicles.FirstOrDefault(x => x.Value == vehicleHash).Key.ToLower()))
+ {
+ Notify.Alert("You are not allowed to spawn this vehicle, because it is restricted by the server owner.");
+ return 0;
+ }
+ }
+
+ int gameTime = GetGameTimer();
+ if (!IsAllowed(Permission.VSBypassRateLimit))
+ {
+ if (lastSpawnTime + spawnTime > gameTime)
+ {
+ Notify.Error($"You are spawning vehicles too quickly. Please wait {Math.Ceiling((double)(lastSpawnTime + spawnTime - gameTime)/1000)} second(s) before trying again.");
+ return 0;
+ }
+ }
+
+ lastSpawnTime = gameTime;
if (!skipLoad)
{
@@ -1366,6 +1389,13 @@ public static async Task SpawnVehicle(uint vehicleHash, bool spawnInside, b
{
vehicle.PlaceOnGround();
}
+
+ if (!vehicle.Model.IsTrain) // to be extra fucking safe
+ {
+ // workaround of retarded feature above:
+ SetVehicleForwardSpeed(vehicle.Handle, speed);
+ }
+ vehicle.CurrentRPM = rpm;
}
// If mod info about the vehicle was specified, check if it's not null.
@@ -1376,13 +1406,6 @@ public static async Task SpawnVehicle(uint vehicleHash, bool spawnInside, b
// Set the previous vehicle to the new vehicle.
_previousVehicle = vehicle;
- //vehicle.Speed = speed; // retarded feature that randomly breaks for no fucking reason
- if (!vehicle.Model.IsTrain) // to be extra fucking safe
- {
- // workaround of retarded feature above:
- SetVehicleForwardSpeed(vehicle.Handle, speed);
- }
- vehicle.CurrentRPM = rpm;
int vehicleDefaultRadio = UserDefaults.VehicleDefaultRadio;
@@ -2132,7 +2155,7 @@ public static void UpdateServerTime(int hours, int minutes)
{
realMinutes = 0;
}
- TriggerServerEvent("vMenu:UpdateServerTime", realHours, realMinutes);
+ TriggerServerEvent("vMenu:UpdateServerTime", realHours, realMinutes, EventManager.IsServerTimeFrozen);
}
///
@@ -2245,7 +2268,7 @@ public struct PedInfo
/// Sets the player's model to the provided modelName.
///
/// The model name.
- public static async Task SetPlayerSkin(string modelName, PedInfo pedCustomizationOptions, bool keepWeapons = true) => await SetPlayerSkin((uint)GetHashKey(modelName), pedCustomizationOptions, keepWeapons);
+ public static async Task SetPlayerSkin(string modelName, PedInfo pedCustomizationOptions, bool keepWeapons = true) => await SetPlayerSkin(Game.GenerateHashASCII(modelName), pedCustomizationOptions, keepWeapons);
///
/// Sets the player's model to the provided modelHash.
@@ -2255,6 +2278,14 @@ public static async Task SetPlayerSkin(uint modelHash, PedInfo pedCustomizationO
{
if (IsModelInCdimage(modelHash))
{
+ if (PlayerAppearance.WhitelistedPeds.Values.Contains(modelHash))
+ {
+ if (!vMenuShared.SupplementaryPermissionManager.IsAllowed("PW" + PlayerAppearance.WhitelistedPeds.FirstOrDefault(x => x.Value == modelHash).Key.ToLower()))
+ {
+ Notify.Alert("You are not allowed to spawn this ped, because it is restricted by the server owner.");
+ return;
+ }
+ }
if (keepWeapons)
{
SaveWeaponLoadout("vmenu_temp_weapons_loadout_before_respawn");
@@ -2265,7 +2296,6 @@ public static async Task SetPlayerSkin(uint modelHash, PedInfo pedCustomizationO
{
await Delay(0);
}
-
if ((uint)GetEntityModel(Game.PlayerPed.Handle) != modelHash) // only change skins if the player is not yet using the new skin.
{
// check if the ped is in a vehicle.
@@ -2348,7 +2378,7 @@ public static async Task SetPlayerSkin(uint modelHash, PedInfo pedCustomizationO
{
await SpawnWeaponLoadoutAsync("vmenu_temp_weapons_loadout_before_respawn", false, true, false);
}
- if (modelHash == (uint)GetHashKey("mp_f_freemode_01") || modelHash == (uint)GetHashKey("mp_m_freemode_01"))
+ if (modelHash == Game.GenerateHashASCII("mp_f_freemode_01") || modelHash == Game.GenerateHashASCII("mp_m_freemode_01"))
{
//var headBlendData = Game.PlayerPed.GetHeadBlendData();
if (pedCustomizationOptions.version == -1)
@@ -2376,7 +2406,7 @@ public static async void SpawnPedByName()
var input = await GetUserInput(windowTitle: "Enter Ped Model Name", maxInputLength: 30);
if (!string.IsNullOrEmpty(input))
{
- await SetPlayerSkin((uint)GetHashKey(input), new PedInfo() { version = -1 });
+ await SetPlayerSkin(Game.GenerateHashASCII(input), new PedInfo() { version = -1 });
}
else
{
@@ -2438,7 +2468,7 @@ public static async Task SavePed(string forceName = null, bool overrideExi
data.props = props;
data.propTextures = propTextures;
- data.isMpPed = model == (uint)GetHashKey("mp_f_freemode_01") || model == (uint)GetHashKey("mp_m_freemode_01");
+ data.isMpPed = model == Game.GenerateHashASCII("mp_f_freemode_01") || model == Game.GenerateHashASCII("mp_m_freemode_01");
if (data.isMpPed)
{
Notify.Alert("Note, you should probably use the MP Character creator if you want more advanced features. Saving Multiplayer characters with this function does NOT save a lot of the online peds customization.");
@@ -2707,7 +2737,7 @@ public static async void SpawnCustomWeapon()
}
}
- var model = (uint)GetHashKey(inputName.ToUpper());
+ var model = Game.GenerateHashASCII(inputName.ToUpper());
if (IsWeaponValid(model))
{
@@ -2872,7 +2902,7 @@ public static async Task SpawnWeaponLoadoutAsync(string saveName, bool appendWea
}
// Set the current weapon to 'unarmed'.
- SetCurrentPedWeapon(Game.PlayerPed.Handle, (uint)GetHashKey("weapon_unarmed"), true);
+ SetCurrentPedWeapon(Game.PlayerPed.Handle, Game.GenerateHashASCII("weapon_unarmed"), true);
if (!(saveName == "vmenu_temp_weapons_loadout_before_respawn" || dontNotify))
{
@@ -2990,9 +3020,9 @@ public static bool SaveWeaponLoadout(string saveName)
///
public static async void SetWalkingStyle(string walkingStyle)
{
- if (IsPedModel(Game.PlayerPed.Handle, (uint)GetHashKey("mp_f_freemode_01")) || IsPedModel(Game.PlayerPed.Handle, (uint)GetHashKey("mp_m_freemode_01")))
+ if (IsPedModel(Game.PlayerPed.Handle, Game.GenerateHashASCII("mp_f_freemode_01")) || IsPedModel(Game.PlayerPed.Handle, Game.GenerateHashASCII("mp_m_freemode_01")))
{
- var isPedMale = IsPedModel(Game.PlayerPed.Handle, (uint)GetHashKey("mp_m_freemode_01"));
+ var isPedMale = IsPedModel(Game.PlayerPed.Handle, Game.GenerateHashASCII("mp_m_freemode_01"));
ClearPedAlternateMovementAnim(Game.PlayerPed.Handle, 0, 1f);
ClearPedAlternateMovementAnim(Game.PlayerPed.Handle, 1, 1f);
ClearPedAlternateMovementAnim(Game.PlayerPed.Handle, 2, 1f);
@@ -3444,7 +3474,7 @@ public static async void PressKeyFob(Vehicle veh)
var player = Game.Player;
if (player != null && !player.IsDead && !player.Character.IsInVehicle())
{
- var KeyFobHashKey = (uint)GetHashKey("p_car_keys_01");
+ var KeyFobHashKey = Game.GenerateHashASCII("p_car_keys_01");
RequestModel(KeyFobHashKey);
while (!HasModelLoaded(KeyFobHashKey))
{
@@ -3456,7 +3486,7 @@ public static async void PressKeyFob(Vehicle veh)
SetModelAsNoLongerNeeded(KeyFobHashKey); // cleanup model from memory
ClearPedTasks(player.Character.Handle);
- SetCurrentPedWeapon(Game.PlayerPed.Handle, (uint)GetHashKey("WEAPON_UNARMED"), true);
+ SetCurrentPedWeapon(Game.PlayerPed.Handle, Game.GenerateHashASCII("WEAPON_UNARMED"), true);
//if (player.Character.Weapons.Current.Hash != WeaponHash.Unarmed)
//{
// player.Character.Weapons.Give(WeaponHash.Unarmed, 1, true, true);
@@ -3599,5 +3629,81 @@ .. Enum.GetValues(typeof(VehicleData.ModType))
];
}
#endregion
+
+ #region Delete vehicle function
+
+ public async static void DeleteVehicle()
+ {
+ var player = Game.PlayerPed;
+
+ if (!player.IsAlive)
+ return;
+
+ if (player.IsInVehicle())
+ {
+ var veh = GetVehicle();
+
+ if (veh != null && veh.Exists() && veh.Driver == player)
+ {
+ SetVehicleHasBeenOwnedByPlayer(veh.Handle, false);
+ SetEntityAsMissionEntity(veh.Handle, false, false);
+ veh.Delete();
+ }
+ else
+ {
+ Notify.Error("This vehicle does not exist (somehow) or you need to be the driver of this vehicle to delete it!");
+ }
+
+ return;
+ }
+
+ float distance = ConfigManager.GetSettingsFloat(ConfigManager.Setting.vmenu_delete_vehicle_distance, 5.0f);
+ int maxDeleteTries = 5;
+ int maxHitTries = 5;
+
+ var forward = GetOffsetFromEntityInWorldCoords(player.Handle, 0f, distance, 0f);
+ var ray = StartShapeTestCapsule(player.Position.X, player.Position.Y, player.Position.Z, forward.X, forward.Y, forward.Z, 5f, 10, player.Handle, 7);
+
+ bool hit = false;
+ Vector3 endCoords = Vector3.Zero;
+ Vector3 surfaceNormal = Vector3.Zero;
+ int entity = 0;
+
+ for (int i = 0; i < maxHitTries; i++)
+ {
+ GetShapeTestResult(ray, ref hit, ref endCoords, ref surfaceNormal, ref entity);
+ if (hit) break;
+ await Task.FromResult(0);
+ }
+
+ if (!hit || !DoesEntityExist(entity) || !IsEntityAVehicle(entity))
+ {
+ Notify.Error("No vehicle found in front of you to delete!");
+ return;
+ }
+
+ var hitVeh = new Vehicle(entity);
+
+ for (int i = 0; i <= maxDeleteTries && DoesEntityExist(entity); i++)
+ {
+ NetworkRequestControlOfEntity(entity);
+ SetVehicleHasBeenOwnedByPlayer(entity, false);
+ SetEntityAsMissionEntity(entity, false, false);
+ hitVeh.Delete();
+ await Task.FromResult(0);
+ }
+
+ if (DoesEntityExist(entity))
+ {
+ Notify.Error("Failed to delete the vehicle in front of you. Try again or ask an admin for help.");
+ }
+ else
+ {
+ Notify.Success("Vehicle deleted successfully.");
+ }
+ }
+
+
+ #endregion
}
}
diff --git a/vMenu/EntitySpawner.cs b/vMenu/EntitySpawner.cs
index 0401f9b77..c3719ab8a 100644
--- a/vMenu/EntitySpawner.cs
+++ b/vMenu/EntitySpawner.cs
@@ -47,7 +47,7 @@ public EntitySpawner()
/// true spawn was succesful
public static void SpawnEntity(string model, Vector3 coords)
{
- SpawnEntity((uint)GetHashKey(model), coords);
+ SpawnEntity(Game.GenerateHashASCII(model), coords);
}
///
diff --git a/vMenu/EventManager.cs b/vMenu/EventManager.cs
index 3a57dafc2..682c6825a 100644
--- a/vMenu/EventManager.cs
+++ b/vMenu/EventManager.cs
@@ -41,6 +41,7 @@ public EventManager()
EventHandlers.Add("vMenu:SetAddons", new Action(SetConfigOptions)); // DEPRECATED: Backwards-compatible event handler; use 'vMenu:SetConfigOptions' instead
EventHandlers.Add("vMenu:SetConfigOptions", new Action(SetConfigOptions));
EventHandlers.Add("vMenu:SetPermissions", new Action(MainMenu.SetPermissions));
+ EventHandlers.Add("vMenu:SetSupplementaryPermissions", new Action(MainMenu.SetSupplementaryPermissions));
EventHandlers.Add("vMenu:KillMe", new Action(KillMe));
EventHandlers.Add("vMenu:Notify", new Action(NotifyPlayer));
EventHandlers.Add("vMenu:SetClouds", new Action(SetClouds));
@@ -98,6 +99,7 @@ private async void SetAppearanceOnFirstSpawn()
private void SetConfigOptions()
{
SetAddons();
+ SetWhiteLists();
SetExtras();
SetTattoos();
@@ -128,7 +130,7 @@ private void SetAddons()
{
if (!VehicleSpawner.AddonVehicles.ContainsKey(addon))
{
- VehicleSpawner.AddonVehicles.Add(addon, (uint)GetHashKey(addon));
+ VehicleSpawner.AddonVehicles.Add(addon, Game.GenerateHashASCII(addon));
}
else
{
@@ -144,7 +146,7 @@ private void SetAddons()
{
if (!WeaponOptions.AddonWeapons.ContainsKey(addon))
{
- WeaponOptions.AddonWeapons.Add(addon, (uint)GetHashKey(addon));
+ WeaponOptions.AddonWeapons.Add(addon, Game.GenerateHashASCII(addon));
}
else
{
@@ -160,7 +162,7 @@ private void SetAddons()
{
if (!PlayerAppearance.AddonPeds.ContainsKey(addon))
{
- PlayerAppearance.AddonPeds.Add(addon, (uint)GetHashKey(addon));
+ PlayerAppearance.AddonPeds.Add(addon, Game.GenerateHashASCII(addon));
}
else
{
@@ -190,6 +192,75 @@ private void SetAddons()
}
}
+ ///
+ /// Sets the addon models from the addons.json file.
+ ///
+ private void SetWhiteLists()
+ {
+ // reset addons
+ VehicleSpawner.WhitelistVehicles = new Dictionary();
+ PlayerAppearance.WhitelistedPeds = new Dictionary();
+ WeaponOptions.WeaponWhitelist = new Dictionary();
+
+ var jsonData = LoadResourceFile(GetCurrentResourceName(), "config/model-whitelists.json") ?? "{}";
+ try
+ {
+ // load new addons.
+ var whitelists = JsonConvert.DeserializeObject>>(jsonData);
+
+ // load whitelist vehicles
+ if (whitelists.ContainsKey("whitelistedvehicle"))
+ {
+ foreach (var whitelist in whitelists["whitelistedvehicle"])
+ {
+ if (!VehicleSpawner.WhitelistVehicles.ContainsKey(whitelist))
+ {
+ VehicleSpawner.WhitelistVehicles.Add(whitelist, Game.GenerateHashASCII(whitelist));
+ }
+ else
+ {
+ Debug.WriteLine($"[vMenu] [Error] Your model-whitelists.json file contains 2 or more entries with the same vehicle name! ({whitelist}) Please remove duplicate lines!");
+ }
+ }
+ }
+
+ // load whitelisted peds.
+ if (whitelists.ContainsKey("whitelistedpeds"))
+ {
+ foreach (var whitelist in whitelists["whitelistedpeds"])
+ {
+ if (!PlayerAppearance.WhitelistedPeds.ContainsKey(whitelist))
+ {
+ PlayerAppearance.WhitelistedPeds.Add(whitelist, Game.GenerateHashASCII(whitelist));
+ }
+ else
+ {
+ Debug.WriteLine($"[vMenu] [Error] Your model-whitelists.json file contains 2 or more entries with the same ped name! ({whitelist}) Please remove duplicate lines!");
+ }
+ }
+ }
+
+ // load whitelisted weapon.
+ if (whitelists.ContainsKey("whitelistedweapons"))
+ {
+ foreach (var whitelist in whitelists["whitelistedweapons"])
+ {
+ if (!WeaponOptions.WeaponWhitelist.ContainsKey(whitelist))
+ {
+ WeaponOptions.WeaponWhitelist.Add(whitelist, Game.GenerateHashASCII(whitelist));
+ }
+ else
+ {
+ Debug.WriteLine($"[vMenu] [Error] Your model-whitelists.json file contains 2 or more entries with the same ped name! ({whitelist}) Please remove duplicate lines!");
+ }
+ }
+ }
+ }
+ catch (JsonReaderException ex)
+ {
+ Debug.WriteLine($"\n\n^1[vMenu] [ERROR] ^7Your model-whitelists.json file contains a problem! Error details: {ex.Message}\n\n");
+ }
+ }
///
/// Sets the extras labels from the extras.json file.
///
@@ -207,7 +278,7 @@ private void SetExtras()
foreach (string model in extras.Keys)
{
- uint modelHash = (uint)GetHashKey(model);
+ uint modelHash = Game.GenerateHashASCII(model);
if (extras[model] != null && extras[model].Count > 0)
{
diff --git a/vMenu/FunctionsController.cs b/vMenu/FunctionsController.cs
index 12dbc85e5..a584fc027 100644
--- a/vMenu/FunctionsController.cs
+++ b/vMenu/FunctionsController.cs
@@ -51,7 +51,7 @@ class FunctionsController : BaseScript
private const string snowball_anim_dict = "anim@mp_snowball";
private const string snowball_anim_name = "pickup_snowball";
- private readonly uint snowball_hash = (uint)GetHashKey("weapon_snowball");
+ private readonly uint snowball_hash = Game.GenerateHashASCII("weapon_snowball");
private bool showSnowballInfo = false;
private bool stopPropsLoop = false;
@@ -76,6 +76,7 @@ public void SetupTickFunctions()
Tick += MiscSettings;
Tick += GeneralTasks;
Tick += GcTick;
+ Tick += ControllerTick;
if (GetSettingsBool(Setting.keep_player_head_props))
{
@@ -214,6 +215,36 @@ private async Task GcTick()
}
#endregion
+ #region Controller tick and functions
+ private async Task ControllerTick()
+ {
+ if (!Game.IsPaused && !IsPauseMenuRestarting() && IsScreenFadedIn() && !IsPlayerSwitchInProgress() && !Game.Player.IsDead && !MenuController.DisableMenuButtons)
+ {
+ if (Game.CurrentInputMode == InputMode.GamePad)
+ {
+ await HandleMenuToggleKeyForController();
+ }
+ }
+ await Task.FromResult(0);
+ }
+ private async Task HandleMenuToggleKeyForController()
+ {
+ int tmpTimer = GetGameTimer();
+ while ((Game.IsControlPressed(0, Control.InteractionMenu) || Game.IsDisabledControlPressed(0, Control.InteractionMenu)) && !Game.IsPaused && IsScreenFadedIn() && !Game.Player.IsDead && !IsPlayerSwitchInProgress() && !MenuController.DontOpenAnyMenu)
+ {
+ if (GetGameTimer() - tmpTimer > 400)
+ {
+ if (MainMenu.Menu != null)
+ {
+ MainMenu.Menu.OpenMenu();
+ }
+ break;
+ }
+ await Delay(0);
+ }
+ }
+ #endregion
+
#region General Tasks
///
/// All general tasks that run every 1 game ticks (and are not (sub)menu specific).
@@ -590,7 +621,6 @@ private async Task VehicleOptions()
var subMenus = new List
///
///
- private System.Drawing.Color GetColorFromIndex(int index)
+ private static System.Drawing.Color GetColorFromIndex(int index)
{
if (index is >= 0 and < 13)
{
@@ -2807,9 +2808,10 @@ public static void CreateCustomColourMenu(Menu menu, RGBType type = RGBType.prim
}
else if (type == RGBType.underglow)
{
- int red = vehicle.Mods.TireSmokeColor.R;
- int green = vehicle.Mods.NeonLightsColor.G;
- int blue = vehicle.Mods.NeonLightsColor.B;
+ Color underglow = GetColorFromIndex(newIndex);
+ int red = underglow.R;
+ int green = underglow.G;
+ int blue = underglow.B;
redColour.BarColor = System.Drawing.Color.FromArgb(255, red, green, blue);
greenColour.BarColor = System.Drawing.Color.FromArgb(255, red, green, blue);
From cdf242b12fde733ee546a87603740fd01ce243f7 Mon Sep 17 00:00:00 2001
From: Ricky Merc <65817116+RickyB505@users.noreply.github.com>
Date: Mon, 6 Apr 2026 14:49:36 -0400
Subject: [PATCH 07/11] fix(vehicle): headlight index going out of range
---
vMenu/menus/VehicleOptions.cs | 15 ++++++++-------
1 file changed, 8 insertions(+), 7 deletions(-)
diff --git a/vMenu/menus/VehicleOptions.cs b/vMenu/menus/VehicleOptions.cs
index e9f755aa0..cc4ba3986 100644
--- a/vMenu/menus/VehicleOptions.cs
+++ b/vMenu/menus/VehicleOptions.cs
@@ -2723,9 +2723,10 @@ public static void CreateCustomColourMenu(Menu menu, RGBType type = RGBType.prim
{
if (IsToggleModOn(vehicle.Handle, 22))
{
- r = VehicleData.NeonLightColors[GetHeadlightsColorForVehicle(vehicle)][0];
- g = VehicleData.NeonLightColors[GetHeadlightsColorForVehicle(vehicle)][1];
- b = VehicleData.NeonLightColors[GetHeadlightsColorForVehicle(vehicle)][2];
+ int headlight = GetHeadlightsColorForVehicle(vehicle) < 0 ? 0 : GetHeadlightsColorForVehicle(vehicle);
+ r = VehicleData.NeonLightColors[headlight][0];
+ g = VehicleData.NeonLightColors[headlight][1];
+ b = VehicleData.NeonLightColors[headlight][2];
}
}
@@ -2828,10 +2829,10 @@ public static void CreateCustomColourMenu(Menu menu, RGBType type = RGBType.prim
}
else if (type == RGBType.headlight)
{
-
- int red = VehicleData.NeonLightColors[GetHeadlightsColorForVehicle(vehicle)][0];
- int green = VehicleData.NeonLightColors[GetHeadlightsColorForVehicle(vehicle)][1];
- int blue = VehicleData.NeonLightColors[GetHeadlightsColorForVehicle(vehicle)][2];
+ int headlight = GetHeadlightsColorForVehicle(vehicle) < 0 ? 0 : GetHeadlightsColorForVehicle(vehicle);
+ int red = VehicleData.NeonLightColors[headlight][0];
+ int green = VehicleData.NeonLightColors[headlight][1];
+ int blue = VehicleData.NeonLightColors[headlight][2];
redColour.BarColor = System.Drawing.Color.FromArgb(255, red, green, blue);
From f897190cedbe02a4cd3b2aa11e1aeef893284685 Mon Sep 17 00:00:00 2001
From: Ricky Merc <65817116+RickyB505@users.noreply.github.com>
Date: Mon, 6 Apr 2026 14:57:14 -0400
Subject: [PATCH 08/11] fix(vehicle): fixes bugs related to spelling, and
missing text fields
---
vMenu/menus/VehicleOptions.cs | 57 ++++++++++++++++++-----------------
1 file changed, 29 insertions(+), 28 deletions(-)
diff --git a/vMenu/menus/VehicleOptions.cs b/vMenu/menus/VehicleOptions.cs
index cc4ba3986..6c89be7f7 100644
--- a/vMenu/menus/VehicleOptions.cs
+++ b/vMenu/menus/VehicleOptions.cs
@@ -2153,7 +2153,7 @@ public void UpdateMods(int selectedIndex = 0)
}
var headlightsButton = new MenuItem("Headlights");
- var headlightsMenu = new Menu("Headlights");
+ var headlightsMenu = new Menu("Headlights", "headlights");
var xenonHeadlights = new MenuCheckboxItem("Xenon Headlights", "Enable or disable ~b~xenon ~s~headlights.", IsToggleModOn(veh.Handle, 22));
headlightsMenu.AddMenuItem(xenonHeadlights);
var currentHeadlightColor = GetHeadlightsColorForVehicle(veh);
@@ -2215,7 +2215,7 @@ public void UpdateMods(int selectedIndex = 0)
}
var tireSmokeButton = new MenuItem("Tire Smoke");
- var tireSmokeMenu = new Menu("Tire Smoke");
+ var tireSmokeMenu = new Menu("Tire Smoke", "Tire Smoke");
// Create a list of tire smoke options.
var tireSmokes = new List() { "Red", "Orange", "Yellow", "Gold", "Light Green", "Dark Green", "Light Blue", "Dark Blue", "Purple", "Pink", "Black" };
var tireSmokeColors = new Dictionary()
@@ -2587,22 +2587,22 @@ public enum RGBType
public static void CreateCustomColourMenu(Menu menu, RGBType type = RGBType.primaryPaint)
{
- var hexColour = new MenuItem("Hex Colour Code");
+ var hexColour = new MenuItem("Hex Color Code");
var typeList = new MenuListItem("Paint Finish", Enum.GetNames(typeof(Colour_Types)).ToList(), 0);
int r = 0, g = 0, b = 0;
- var redColour = new MenuSliderItem("Red Colour", 0, 255, 128, true)
+ var redColour = new MenuSliderItem("Red Color", 0, 255, 128, true)
{
BarColor = System.Drawing.Color.FromArgb(155, 0, 0, 0),
BackgroundColor = System.Drawing.Color.FromArgb(200, 79, 79, 79),
};
- var greenColour = new MenuSliderItem("Green Colour", 0, 255, 128, true)
+ var greenColour = new MenuSliderItem("Green Color", 0, 255, 128, true)
{
BarColor = System.Drawing.Color.FromArgb(155, 0, 0, 0),
BackgroundColor = System.Drawing.Color.FromArgb(200, 79, 79, 79),
};
- var blueColour = new MenuSliderItem("Blue Colour", 0, 255, 128, true)
+ var blueColour = new MenuSliderItem("Blue Color", 0, 255, 128, true)
{
BarColor = System.Drawing.Color.FromArgb(155, 0, 0, 0),
BackgroundColor = System.Drawing.Color.FromArgb(200, 79, 79, 79),
@@ -2635,9 +2635,10 @@ public static void CreateCustomColourMenu(Menu menu, RGBType type = RGBType.prim
{
if (IsToggleModOn(vehicle.Handle, 22))
{
- r = VehicleData.NeonLightColors[GetHeadlightsColorForVehicle(vehicle)][0];
- g = VehicleData.NeonLightColors[GetHeadlightsColorForVehicle(vehicle)][1];
- b = VehicleData.NeonLightColors[GetHeadlightsColorForVehicle(vehicle)][2];
+ int headlight = GetHeadlightsColorForVehicle(vehicle) < 0 ? 0 : GetHeadlightsColorForVehicle(vehicle);
+ r = VehicleData.NeonLightColors[headlight][0];
+ g = VehicleData.NeonLightColors[headlight][1];
+ b = VehicleData.NeonLightColors[headlight][2];
}
}
@@ -2646,13 +2647,13 @@ public static void CreateCustomColourMenu(Menu menu, RGBType type = RGBType.prim
GetVehicleTyreSmokeColor(vehicle.Handle, ref r, ref g, ref b);
- redColour.Text = $"Red Colour ({r})";
+ redColour.Text = $"Red Color ({r})";
redColour.Position = r;
- greenColour.Text = $"Green Colour ({g})";
+ greenColour.Text = $"Green Color ({g})";
greenColour.Position = g;
- blueColour.Text = $"Blue Colour ({b})";
+ blueColour.Text = $"Blue Color ({b})";
blueColour.Position = b;
redColour.BarColor = System.Drawing.Color.FromArgb(255, redColour.Position, greenColour.Position, blueColour.Position);
@@ -2666,7 +2667,7 @@ public static void CreateCustomColourMenu(Menu menu, RGBType type = RGBType.prim
if (item == hexColour)
{
string hexValue = redColour.Position.ToString("X2") + greenColour.Position.ToString("X2") + blueColour.Position.ToString("X2");
- var result = await GetUserInput(windowTitle: "Enter Colour Hex", defaultText: (hexValue).Replace("#", ""), maxInputLength: 6);
+ var result = await GetUserInput(windowTitle: "Enter Color Hex", defaultText: (hexValue).Replace("#", ""), maxInputLength: 6);
if (!string.IsNullOrEmpty(result))
{
if (IsHex(result))
@@ -2691,13 +2692,13 @@ public static void CreateCustomColourMenu(Menu menu, RGBType type = RGBType.prim
greenColour.BarColor = System.Drawing.Color.FromArgb(255, red, green, blue);
blueColour.BarColor = System.Drawing.Color.FromArgb(255, red, green, blue);
- redColour.Text = $"Red Colour ({red})";
+ redColour.Text = $"Red Color ({red})";
redColour.Position = red;
- greenColour.Text = $"Green Colour ({green})";
+ greenColour.Text = $"Green Color ({green})";
greenColour.Position = green;
- blueColour.Text = $"Blue Colour ({blue})";
+ blueColour.Text = $"Blue Color ({blue})";
blueColour.Position = blue;
}
}
@@ -2748,7 +2749,7 @@ public static void CreateCustomColourMenu(Menu menu, RGBType type = RGBType.prim
else if (type == RGBType.tiresmoke)
vehicle.Mods.TireSmokeColor = System.Drawing.Color.FromArgb(255, newPosition, g, b);
- redColour.Text = $"Red Colour ({newPosition})";
+ redColour.Text = $"Red Color ({newPosition})";
}
if (sliderItem == greenColour)
{
@@ -2763,7 +2764,7 @@ public static void CreateCustomColourMenu(Menu menu, RGBType type = RGBType.prim
else if (type == RGBType.tiresmoke)
vehicle.Mods.TireSmokeColor = System.Drawing.Color.FromArgb(255, r, newPosition, b);
- greenColour.Text = $"Green Colour ({newPosition})";
+ greenColour.Text = $"Green Color ({newPosition})";
}
if (sliderItem == blueColour)
{
@@ -2778,7 +2779,7 @@ public static void CreateCustomColourMenu(Menu menu, RGBType type = RGBType.prim
else if (type == RGBType.tiresmoke)
vehicle.Mods.TireSmokeColor = System.Drawing.Color.FromArgb(255, r, g, newPosition);
- blueColour.Text = $"Blue Colour ({newPosition})";
+ blueColour.Text = $"Blue Color ({newPosition})";
}
redColour.BarColor = System.Drawing.Color.FromArgb(255, redColour.Position, greenColour.Position, blueColour.Position);
greenColour.BarColor = System.Drawing.Color.FromArgb(255, redColour.Position, greenColour.Position, blueColour.Position);
@@ -2818,13 +2819,13 @@ public static void CreateCustomColourMenu(Menu menu, RGBType type = RGBType.prim
greenColour.BarColor = System.Drawing.Color.FromArgb(255, red, green, blue);
blueColour.BarColor = System.Drawing.Color.FromArgb(255, red, green, blue);
- redColour.Text = $"Red Colour ({red})";
+ redColour.Text = $"Red Color ({red})";
redColour.Position = red;
- greenColour.Text = $"Green Colour ({green})";
+ greenColour.Text = $"Green Color ({green})";
greenColour.Position = green;
- blueColour.Text = $"Blue Colour ({blue})";
+ blueColour.Text = $"Blue Color ({blue})";
blueColour.Position = blue;
}
else if (type == RGBType.headlight)
@@ -2839,13 +2840,13 @@ public static void CreateCustomColourMenu(Menu menu, RGBType type = RGBType.prim
greenColour.BarColor = System.Drawing.Color.FromArgb(255, red, green, blue);
blueColour.BarColor = System.Drawing.Color.FromArgb(255, red, green, blue);
- redColour.Text = $"Red Colour ({red})";
+ redColour.Text = $"Red Color ({red})";
redColour.Position = red;
- greenColour.Text = $"Green Colour ({green})";
+ greenColour.Text = $"Green Color ({green})";
greenColour.Position = green;
- blueColour.Text = $"Blue Colour ({blue})";
+ blueColour.Text = $"Blue Color ({blue})";
blueColour.Position = blue;
}
else if (type == RGBType.tiresmoke)
@@ -2858,13 +2859,13 @@ public static void CreateCustomColourMenu(Menu menu, RGBType type = RGBType.prim
greenColour.BarColor = System.Drawing.Color.FromArgb(255, red, green, blue);
blueColour.BarColor = System.Drawing.Color.FromArgb(255, red, green, blue);
- redColour.Text = $"Red Colour ({red})";
+ redColour.Text = $"Red Color ({red})";
redColour.Position = red;
- greenColour.Text = $"Green Colour ({green})";
+ greenColour.Text = $"Green Color ({green})";
greenColour.Position = green;
- blueColour.Text = $"Blue Colour ({blue})";
+ blueColour.Text = $"Blue Color ({blue})";
blueColour.Position = blue;
}
From dca2473cbfd7700298b0a2666be7c6a935596cd4 Mon Sep 17 00:00:00 2001
From: Tom Grobbe
Date: Sat, 18 Apr 2026 19:08:26 +0200
Subject: [PATCH 09/11] Gitignore update
---
.gitignore | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/.gitignore b/.gitignore
index fdc8383c4..61d26fcf0 100644
--- a/.gitignore
+++ b/.gitignore
@@ -267,4 +267,6 @@ external/
*.cmd.bak
# VS Build Output
-build/
\ No newline at end of file
+build/
+
+*.lscache
\ No newline at end of file
From 0df96a494036423065ec4f4fbfb12cd598d54532 Mon Sep 17 00:00:00 2001
From: Tom <31419184+TomGrobbe@users.noreply.github.com>
Date: Fri, 5 Jun 2026 15:35:35 +0200
Subject: [PATCH 10/11] Supplemental permissions pilot (#619)
* feat/fix: addon permissions/updating outdated function (#610)
* feat/fix: addon permissions/updating outdated function
added individual permissions to addon vehicles/weapons/peds, and added a whitelist to regular vehicles/ped. changed (uint)GetHashKey() to Game.GenerateHashASCII() for better optimization
* fix: updated what was requested
* fix: typo
* fix: fixed what needed changing
* fix: fixed controller controls and permissions for weapons (#613)
* New features and fixes (#614)
* vehicle spawning ratelimit
* fix: move setting the vehicle speed inside where we check if they spawn inside
* feat: add command "vMenu:DV"
* feat: add back smooth time transitions
* fix: fixes server sided permissions so it checks the parent permissions
* fix: disable post build command on non windows machines
* fix: Changes and fixes requested
---------
Co-authored-by: Ricky Merc <65817116+RickyB505@users.noreply.github.com>
---
SharedClasses/ConfigManager.cs | 19 +-
SharedClasses/PermissionsManager.cs | 4 +-
.../SupplementaryPermissionManager.cs | 240 ++++++++++++++++++
vMenu/CommonFunctions.cs | 184 +++++++++++---
vMenu/EntitySpawner.cs | 2 +-
vMenu/EventManager.cs | 79 +++++-
vMenu/FunctionsController.cs | 198 +++++++++------
vMenu/MainMenu.cs | 43 +++-
vMenu/data/BlipInfo.cs | 105 ++++----
vMenu/data/PedModels.cs | 87 +++----
vMenu/data/ValidAddonWeapon.cs | 4 +-
vMenu/data/ValidWeapon.cs | 4 +-
vMenu/menus/MpPedCustomization.cs | 34 +--
vMenu/menus/PlayerAppearance.cs | 29 ++-
vMenu/menus/PlayerOptions.cs | 2 +-
vMenu/menus/Recording.cs | 2 +-
vMenu/menus/VehicleOptions.cs | 21 +-
vMenu/menus/VehicleSpawner.cs | 34 ++-
vMenu/menus/WeaponOptions.cs | 19 +-
vMenu/vMenuClient.csproj | 5 +-
vMenuServer/MainServer.cs | 132 ++++++++--
vMenuServer/config/model-whitelists.json | 23 ++
vMenuServer/config/permissions.cfg | 8 +
vMenuServer/vMenuServer.csproj | 6 +-
24 files changed, 962 insertions(+), 322 deletions(-)
create mode 100644 SharedClasses/SupplementaryPermissionManager.cs
create mode 100644 vMenuServer/config/model-whitelists.json
diff --git a/SharedClasses/ConfigManager.cs b/SharedClasses/ConfigManager.cs
index c8b07fe7a..5f212c2dd 100644
--- a/SharedClasses/ConfigManager.cs
+++ b/SharedClasses/ConfigManager.cs
@@ -34,8 +34,10 @@ public enum Setting
vmenu_disable_entity_outlines_tool,
vmenu_disable_player_stats_setup,
- // Vehicle Chameleon Colours
+ // Vehicle Settings
vmenu_using_chameleon_colours,
+ vmenu_vehicle_spawn_delay,
+ vmenu_delete_vehicle_distance,
// Prevent Extras Abuse
vmenu_prevent_extras_when_damaged,
@@ -61,6 +63,7 @@ public enum Setting
vmenu_vehicle_blackout_enabled,
vmenu_weather_change_duration,
vmenu_enable_snow,
+ vmenu_smooth_time_transitions,
// Time settings
vmenu_enable_time_sync,
@@ -92,12 +95,12 @@ public static bool GetSettingsBool(Setting setting)
///
///
///
- public static int GetSettingsInt(Setting setting)
+ public static int GetSettingsInt(Setting setting, int defaultValue = -1)
{
- var convarInt = GetConvarInt(setting.ToString(), -1);
- if (convarInt == -1)
+ var convarInt = GetConvarInt(setting.ToString(), defaultValue);
+ if (convarInt == defaultValue)
{
- if (int.TryParse(GetConvar(setting.ToString(), "-1"), out var convarIntAlt))
+ if (int.TryParse(GetConvar(setting.ToString(), defaultValue.ToString()), out var convarIntAlt))
{
return convarIntAlt;
}
@@ -110,13 +113,13 @@ public static int GetSettingsInt(Setting setting)
///
///
///
- public static float GetSettingsFloat(Setting setting)
+ public static float GetSettingsFloat(Setting setting, float defaultValue = -1f)
{
- if (float.TryParse(GetConvar(setting.ToString(), "-1.0"), out var result))
+ if (float.TryParse(GetConvar(setting.ToString(), defaultValue.ToString()), out var result))
{
return result;
}
- return -1f;
+ return defaultValue;
}
///
diff --git a/SharedClasses/PermissionsManager.cs b/SharedClasses/PermissionsManager.cs
index f3240bc6c..d5826c735 100644
--- a/SharedClasses/PermissionsManager.cs
+++ b/SharedClasses/PermissionsManager.cs
@@ -113,6 +113,7 @@ public enum Permission
#region vehicle spawner
VSMenu,
VSAll,
+ VSBypassRateLimit,
VSDisableReplacePrevious,
VSSpawnByName,
VSAddon,
@@ -481,8 +482,7 @@ private static bool IsAllowedServer(Permission permission, string playerHandle)
{
return false;
}
-
- return IsPlayerAceAllowed(playerHandle, GetAceName(permission));
+ return GetPermissionAndParentPermissions(permission).Any(p => IsPlayerAceAllowed(playerHandle, GetAceName(p)));
}
#endif
diff --git a/SharedClasses/SupplementaryPermissionManager.cs b/SharedClasses/SupplementaryPermissionManager.cs
new file mode 100644
index 000000000..8ef9944c6
--- /dev/null
+++ b/SharedClasses/SupplementaryPermissionManager.cs
@@ -0,0 +1,240 @@
+using System;
+
+using System.Collections.Generic;
+using System.Linq;
+
+using CitizenFX.Core;
+
+using static CitizenFX.Core.Native.API;
+using CitizenFX.Core.Native;
+
+namespace vMenuShared
+{
+ public static class SupplementaryPermissionManager
+ {
+ public static List Permission = new()
+ {
+ "VWAll",
+ "PWAll",
+ "WWAll",
+ };
+
+ public static Dictionary Permissions { get; private set; } = new Dictionary();
+ public static bool ArePermissionsSetup { get; set; } = false;
+
+
+#if SERVER
+ ///
+ /// Public function to check if a permission is allowed.
+ ///
+ ///
+ ///
+ ///
+ public static bool IsAllowed(string permission, Player source) => IsAllowedServer(permission, source);
+
+ ///
+ /// Public function to check if a permission is allowed.
+ ///
+ ///
+ ///
+ ///
+ public static bool IsAllowed(string permission, string playerHandle) => IsAllowedServer(permission, playerHandle);
+#endif
+
+#if CLIENT
+ ///
+ /// Public function to check if a permission is allowed.
+ ///
+ ///
+ /// if true, then the permissions will be checked even if they aren't setup yet.
+ ///
+ public static bool IsAllowed(string permission, bool checkAnyway = false) => IsAllowedClient(permission, checkAnyway);
+
+ private static readonly Dictionary allowedPerms = new();
+ ///
+ /// Private function that handles client side permission requests.
+ ///
+ ///
+ ///
+ private static bool IsAllowedClient(string permission, bool checkAnyway)
+ {
+ if (ArePermissionsSetup || checkAnyway)
+ {
+ if (allowedPerms.ContainsKey(permission) && allowedPerms[permission])
+ {
+ return true;
+ }
+ else if (!allowedPerms.ContainsKey(permission))
+ {
+ allowedPerms[permission] = false;
+ }
+
+ // Get a list of all permissions that are (parents) of the current permission, including the current permission.
+ var permissionsToCheck = GetPermissionAndParentPermissions(permission);
+
+ // Check if any of those permissions is allowed, if so, return true.
+ if (permissionsToCheck.Any(p => Permissions.ContainsKey(p) && Permissions[p]))
+ {
+ allowedPerms[permission] = true;
+ return true;
+ }
+ }
+ switch (permission.Substring(0, 2))
+ {
+ case "VW":
+ allowedPerms[permission] = PermissionsManager.IsAllowed(PermissionsManager.Permission.VSAll);
+ return PermissionsManager.IsAllowed(PermissionsManager.Permission.VSAll);
+ case "PW":
+ allowedPerms[permission] = PermissionsManager.IsAllowed(PermissionsManager.Permission.PAAll);
+ return PermissionsManager.IsAllowed(PermissionsManager.Permission.PAAll);
+ case "WW":
+ allowedPerms[permission] = PermissionsManager.IsAllowed(PermissionsManager.Permission.WPAll);
+ return PermissionsManager.IsAllowed(PermissionsManager.Permission.WPAll);
+ }
+ return false;
+ }
+#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;
+ }
+
+ return IsAllowedServer(permission, source.Handle);
+ }
+
+ ///
+ /// Checks if the player is allowed that specific permission.
+ ///
+ ///
+ ///
+ ///
+ private static bool IsAllowedServer(string permission, string playerHandle)
+ {
+ if (!DoesPlayerExist(playerHandle))
+ {
+ return false;
+ }
+
+ return GetPermissionAndParentPermissions(permission).Any(p => IsPlayerAceAllowed(playerHandle, GetAceName(p)));
+ }
+#endif
+
+ private static readonly Dictionary> parentPermissions = new();
+
+ ///
+ /// Gets the current permission and all parent permissions.
+ ///
+ ///
+ ///
+ public static List GetPermissionAndParentPermissions(string permission)
+ {
+ if (parentPermissions.ContainsKey(permission))
+ {
+ return parentPermissions[permission];
+ }
+ else
+ {
+ var list = new List() { "Everything", permission };
+
+ // if the first 2 characters are both uppercase
+ if (permission.Substring(0, 2).ToUpper() == permission.Substring(0, 2))
+ {
+ if (permission.Substring(2) is not "All")
+ {
+ list.AddRange(Permission.Where(a => a.ToString() == permission.Substring(0, 2) + "All"));
+ }
+ }
+ //else // it's one of the .Everything, .DontKickMe, DontBanMe, NoClip, Staff, etc perms that are not menu specific.
+ //{
+ // // do nothing
+ //}
+ parentPermissions[permission] = list;
+ return list;
+ }
+ }
+
+#if SERVER
+
+
+ ///
+ /// Sets the permissions for a specific player (checks server side, sends event to client side).
+ ///
+ ///
+ public static void SetPermissionsForPlayer([FromSource] Player player)
+ {
+ if (player == null)
+ {
+ return;
+ }
+
+ var perms = new Dictionary();
+
+ // Loop through all permissions and check if they're allowed.
+ foreach (string permission in Permission)
+ {
+ if (!perms.ContainsKey(permission))
+ {
+ perms.Add(permission, IsAllowed(permission, player)); // triggers IsAllowedServer
+ }
+ }
+ // Send the permissions to the client.
+ player.TriggerEvent("vMenu:SetSupplementaryPermissions", Newtonsoft.Json.JsonConvert.SerializeObject(perms));
+ }
+#endif
+#if CLIENT
+ ///
+ /// Sets the permission (client side event handler).
+ ///
+ ///
+ public static void SetPermissions(string permissions)
+ {
+ Permissions = Newtonsoft.Json.JsonConvert.DeserializeObject>(permissions);
+
+ // if debug logging.
+ if (GetResourceMetadata(GetCurrentResourceName(), "client_debug_mode", 0) == "true")
+ {
+ Debug.WriteLine("[vMenu] [Permissions] " + Newtonsoft.Json.JsonConvert.SerializeObject(Permissions, Newtonsoft.Json.Formatting.None));
+ }
+ }
+#endif
+#if SERVER
+ ///
+ /// Gets the full permission ace name for the specific enum.
+ ///
+ ///
+ ///
+ public static string GetAceName(string permission)
+ {
+ var name = permission.ToString();
+
+ var prefix = "vMenu.";
+
+ switch (name.Substring(0, 2))
+ {
+ case "VW":
+ prefix += "VehicleSpawner.WhitelistedModels";
+ break;
+ case "PW":
+ prefix += "PlayerAppearance.WhitelistedModels";
+ break;
+ case "WW":
+ prefix += "WeaponOptions.WhitelistedModels";
+ break;
+ default:
+ return prefix + name;
+ }
+
+ return prefix + "." + name.Substring(2);
+ }
+#endif
+ }
+}
diff --git a/vMenu/CommonFunctions.cs b/vMenu/CommonFunctions.cs
index 3dc95ebd0..cbf2e8025 100644
--- a/vMenu/CommonFunctions.cs
+++ b/vMenu/CommonFunctions.cs
@@ -4,6 +4,7 @@
using System.Threading.Tasks;
using CitizenFX.Core;
+using static CitizenFX.Core.Native.API;
using MenuAPI;
@@ -12,9 +13,9 @@
using vMenuClient.data;
using vMenuClient.menus;
-using static CitizenFX.Core.Native.API;
using static CitizenFX.Core.UI.Screen;
using static vMenuShared.PermissionsManager;
+using vMenuShared;
namespace vMenuClient
{
@@ -23,7 +24,6 @@ public static class CommonFunctions
#region Variables
private static string _currentScenario = "";
private static Vehicle _previousVehicle;
-
internal static bool DriveToWpTaskActive = false;
internal static bool DriveWanderTaskActive = false;
#endregion
@@ -121,7 +121,7 @@ public static async void LockOrUnlockDoors(Vehicle veh, bool lockDoors)
///
///
///
- public static string GetVehDisplayNameFromModel(string name) => GetLabelText(GetDisplayNameFromVehicleModel((uint)GetHashKey(name)));
+ public static string GetVehDisplayNameFromModel(string name) => GetLabelText(GetDisplayNameFromVehicleModel(Game.GenerateHashASCII(name)));
#endregion
#region DoesModelExist
@@ -130,7 +130,7 @@ public static async void LockOrUnlockDoors(Vehicle veh, bool lockDoors)
///
/// The model name
///
- public static bool DoesModelExist(string modelName) => DoesModelExist((uint)GetHashKey(modelName));
+ public static bool DoesModelExist(string modelName) => DoesModelExist(Game.GenerateHashASCII(modelName));
///
/// Does this model exist?
@@ -213,7 +213,7 @@ public static Vehicle GetVehicle(Player player, bool lastVehicle = false)
///
/// Entity/vehicle.
/// Returns the (uint) model hash from a (vehicle) entity.
- public static uint GetVehicleModel(int vehicle) => (uint)GetHashKey(GetEntityModel(vehicle).ToString());
+ public static uint GetVehicleModel(int vehicle) => Game.GenerateHashASCII(GetEntityModel(vehicle).ToString());
#endregion
#region Is ped pointing
@@ -873,35 +873,35 @@ public static async void CommitSuicide()
if (Game.PlayerPed.Weapons.HasWeapon(WeaponHash.PistolMk2))
{
- weaponHash = (uint)GetHashKey("WEAPON_PISTOL_MK2");
+ weaponHash = Game.GenerateHashASCII("WEAPON_PISTOL_MK2");
}
else if (Game.PlayerPed.Weapons.HasWeapon(WeaponHash.CombatPistol))
{
- weaponHash = (uint)GetHashKey("WEAPON_COMBATPISTOL");
+ weaponHash = Game.GenerateHashASCII("WEAPON_COMBATPISTOL");
}
else if (Game.PlayerPed.Weapons.HasWeapon(WeaponHash.Pistol))
{
- weaponHash = (uint)GetHashKey("WEAPON_PISTOL");
+ weaponHash = Game.GenerateHashASCII("WEAPON_PISTOL");
}
- else if (Game.PlayerPed.Weapons.HasWeapon((WeaponHash)(uint)GetHashKey("WEAPON_SNSPISTOL_MK2")))
+ else if (Game.PlayerPed.Weapons.HasWeapon((WeaponHash)Game.GenerateHashASCII("WEAPON_SNSPISTOL_MK2")))
{
- weaponHash = (uint)GetHashKey("WEAPON_SNSPISTOL_MK2");
+ weaponHash = Game.GenerateHashASCII("WEAPON_SNSPISTOL_MK2");
}
else if (Game.PlayerPed.Weapons.HasWeapon(WeaponHash.SNSPistol))
{
- weaponHash = (uint)GetHashKey("WEAPON_SNSPISTOL");
+ weaponHash = Game.GenerateHashASCII("WEAPON_SNSPISTOL");
}
else if (Game.PlayerPed.Weapons.HasWeapon(WeaponHash.Pistol50))
{
- weaponHash = (uint)GetHashKey("WEAPON_PISTOL50");
+ weaponHash = Game.GenerateHashASCII("WEAPON_PISTOL50");
}
else if (Game.PlayerPed.Weapons.HasWeapon(WeaponHash.HeavyPistol))
{
- weaponHash = (uint)GetHashKey("WEAPON_HEAVYPISTOL");
+ weaponHash = Game.GenerateHashASCII("WEAPON_HEAVYPISTOL");
}
else if (Game.PlayerPed.Weapons.HasWeapon(WeaponHash.VintagePistol))
{
- weaponHash = (uint)GetHashKey("WEAPON_VINTAGEPISTOL");
+ weaponHash = Game.GenerateHashASCII("WEAPON_VINTAGEPISTOL");
}
else
{
@@ -912,7 +912,7 @@ public static async void CommitSuicide()
// If we take the pill, remove any weapons in our hands.
if (takePill)
{
- SetCurrentPedWeapon(Game.PlayerPed.Handle, (uint)GetHashKey("weapon_unarmed"), true);
+ SetCurrentPedWeapon(Game.PlayerPed.Handle, Game.GenerateHashASCII("weapon_unarmed"), true);
}
// Otherwise, give the ped a gun.
else if (weaponHash != null)
@@ -922,8 +922,8 @@ public static async void CommitSuicide()
}
else
{
- GiveWeaponToPed(Game.PlayerPed.Handle, (uint)GetHashKey("weapon_pistol_mk2"), 1, false, true);
- SetCurrentPedWeapon(Game.PlayerPed.Handle, (uint)GetHashKey("weapon_pistol_mk2"), true);
+ GiveWeaponToPed(Game.PlayerPed.Handle, Game.GenerateHashASCII("weapon_pistol_mk2"), 1, false, true);
+ SetCurrentPedWeapon(Game.PlayerPed.Handle, Game.GenerateHashASCII("weapon_pistol_mk2"), true);
SetPedDropsWeaponsWhenDead(Game.PlayerPed.Handle, true);
}
@@ -935,7 +935,7 @@ public static async void CommitSuicide()
while (true)
{
var time = GetEntityAnimCurrentTime(Game.PlayerPed.Handle, "MP_SUICIDE", takePill ? "pill" : "pistol");
- if (HasAnimEventFired(Game.PlayerPed.Handle, (uint)GetHashKey("Fire")) && !shot) // shoot the gun if the animation event is triggered.
+ if (HasAnimEventFired(Game.PlayerPed.Handle, Game.GenerateHashASCII("Fire")) && !shot) // shoot the gun if the animation event is triggered.
{
ClearEntityLastDamageEntity(Game.PlayerPed.Handle);
SetPedShootsAtCoord(Game.PlayerPed.Handle, 0f, 0f, 0f, false);
@@ -1209,7 +1209,7 @@ public static async Task SpawnVehicle(string vehicleName = "custom", bool s
if (!string.IsNullOrEmpty(result))
{
// Convert it into a model hash.
- var model = (uint)GetHashKey(result);
+ var model = Game.GenerateHashASCII(result);
return await SpawnVehicle(vehicleHash: model, spawnInside: spawnInside, replacePrevious: replacePrevious, skipLoad: false, vehicleInfo: new VehicleInfo(),
saveName: null);
}
@@ -1220,12 +1220,14 @@ public static async Task SpawnVehicle(string vehicleName = "custom", bool s
return 0;
}
}
- return await SpawnVehicle(vehicleHash: (uint)GetHashKey(vehicleName), spawnInside: spawnInside, replacePrevious: replacePrevious, skipLoad: false,
+ return await SpawnVehicle(vehicleHash: Game.GenerateHashASCII(vehicleName), spawnInside: spawnInside, replacePrevious: replacePrevious, skipLoad: false,
vehicleInfo: new VehicleInfo(), saveName: null);
}
#endregion
#region Main Spawn Vehicle Function
+ public static int lastSpawnTime = 0;
+ public static int spawnTime = ConfigManager.GetSettingsInt(ConfigManager.Setting.vmenu_vehicle_spawn_delay, 5) * 1000;
///
/// Spawns a vehicle.
///
@@ -1252,6 +1254,27 @@ public static async Task SpawnVehicle(uint vehicleHash, bool spawnInside, b
Notify.Alert("You are not allowed to spawn this vehicle, because it belongs to a category which is restricted by the server owner.");
return 0;
}
+
+ if (VehicleSpawner.WhitelistVehicles.Values.Contains(vehicleHash))
+ {
+ if (!vMenuShared.SupplementaryPermissionManager.IsAllowed("VW" + VehicleSpawner.WhitelistVehicles.FirstOrDefault(x => x.Value == vehicleHash).Key.ToLower()))
+ {
+ Notify.Alert("You are not allowed to spawn this vehicle, because it is restricted by the server owner.");
+ return 0;
+ }
+ }
+
+ int gameTime = GetGameTimer();
+ if (!IsAllowed(Permission.VSBypassRateLimit))
+ {
+ if (lastSpawnTime + spawnTime > gameTime)
+ {
+ Notify.Error($"You are spawning vehicles too quickly. Please wait {Math.Ceiling((double)(lastSpawnTime + spawnTime - gameTime)/1000)} second(s) before trying again.");
+ return 0;
+ }
+ }
+
+ lastSpawnTime = gameTime;
if (!skipLoad)
{
@@ -1366,6 +1389,13 @@ public static async Task SpawnVehicle(uint vehicleHash, bool spawnInside, b
{
vehicle.PlaceOnGround();
}
+
+ if (!vehicle.Model.IsTrain) // to be extra fucking safe
+ {
+ // workaround of retarded feature above:
+ SetVehicleForwardSpeed(vehicle.Handle, speed);
+ }
+ vehicle.CurrentRPM = rpm;
}
// If mod info about the vehicle was specified, check if it's not null.
@@ -1376,13 +1406,6 @@ public static async Task SpawnVehicle(uint vehicleHash, bool spawnInside, b
// Set the previous vehicle to the new vehicle.
_previousVehicle = vehicle;
- //vehicle.Speed = speed; // retarded feature that randomly breaks for no fucking reason
- if (!vehicle.Model.IsTrain) // to be extra fucking safe
- {
- // workaround of retarded feature above:
- SetVehicleForwardSpeed(vehicle.Handle, speed);
- }
- vehicle.CurrentRPM = rpm;
int vehicleDefaultRadio = UserDefaults.VehicleDefaultRadio;
@@ -2132,7 +2155,7 @@ public static void UpdateServerTime(int hours, int minutes)
{
realMinutes = 0;
}
- TriggerServerEvent("vMenu:UpdateServerTime", realHours, realMinutes);
+ TriggerServerEvent("vMenu:UpdateServerTime", realHours, realMinutes, EventManager.IsServerTimeFrozen);
}
///
@@ -2245,7 +2268,7 @@ public struct PedInfo
/// Sets the player's model to the provided modelName.
///
/// The model name.
- public static async Task SetPlayerSkin(string modelName, PedInfo pedCustomizationOptions, bool keepWeapons = true) => await SetPlayerSkin((uint)GetHashKey(modelName), pedCustomizationOptions, keepWeapons);
+ public static async Task SetPlayerSkin(string modelName, PedInfo pedCustomizationOptions, bool keepWeapons = true) => await SetPlayerSkin(Game.GenerateHashASCII(modelName), pedCustomizationOptions, keepWeapons);
///
/// Sets the player's model to the provided modelHash.
@@ -2255,6 +2278,14 @@ public static async Task SetPlayerSkin(uint modelHash, PedInfo pedCustomizationO
{
if (IsModelInCdimage(modelHash))
{
+ if (PlayerAppearance.WhitelistedPeds.Values.Contains(modelHash))
+ {
+ if (!vMenuShared.SupplementaryPermissionManager.IsAllowed("PW" + PlayerAppearance.WhitelistedPeds.FirstOrDefault(x => x.Value == modelHash).Key.ToLower()))
+ {
+ Notify.Alert("You are not allowed to spawn this ped, because it is restricted by the server owner.");
+ return;
+ }
+ }
if (keepWeapons)
{
SaveWeaponLoadout("vmenu_temp_weapons_loadout_before_respawn");
@@ -2265,7 +2296,6 @@ public static async Task SetPlayerSkin(uint modelHash, PedInfo pedCustomizationO
{
await Delay(0);
}
-
if ((uint)GetEntityModel(Game.PlayerPed.Handle) != modelHash) // only change skins if the player is not yet using the new skin.
{
// check if the ped is in a vehicle.
@@ -2348,7 +2378,7 @@ public static async Task SetPlayerSkin(uint modelHash, PedInfo pedCustomizationO
{
await SpawnWeaponLoadoutAsync("vmenu_temp_weapons_loadout_before_respawn", false, true, false);
}
- if (modelHash == (uint)GetHashKey("mp_f_freemode_01") || modelHash == (uint)GetHashKey("mp_m_freemode_01"))
+ if (modelHash == Game.GenerateHashASCII("mp_f_freemode_01") || modelHash == Game.GenerateHashASCII("mp_m_freemode_01"))
{
//var headBlendData = Game.PlayerPed.GetHeadBlendData();
if (pedCustomizationOptions.version == -1)
@@ -2376,7 +2406,7 @@ public static async void SpawnPedByName()
var input = await GetUserInput(windowTitle: "Enter Ped Model Name", maxInputLength: 30);
if (!string.IsNullOrEmpty(input))
{
- await SetPlayerSkin((uint)GetHashKey(input), new PedInfo() { version = -1 });
+ await SetPlayerSkin(Game.GenerateHashASCII(input), new PedInfo() { version = -1 });
}
else
{
@@ -2438,7 +2468,7 @@ public static async Task SavePed(string forceName = null, bool overrideExi
data.props = props;
data.propTextures = propTextures;
- data.isMpPed = model == (uint)GetHashKey("mp_f_freemode_01") || model == (uint)GetHashKey("mp_m_freemode_01");
+ data.isMpPed = model == Game.GenerateHashASCII("mp_f_freemode_01") || model == Game.GenerateHashASCII("mp_m_freemode_01");
if (data.isMpPed)
{
Notify.Alert("Note, you should probably use the MP Character creator if you want more advanced features. Saving Multiplayer characters with this function does NOT save a lot of the online peds customization.");
@@ -2707,7 +2737,7 @@ public static async void SpawnCustomWeapon()
}
}
- var model = (uint)GetHashKey(inputName.ToUpper());
+ var model = Game.GenerateHashASCII(inputName.ToUpper());
if (IsWeaponValid(model))
{
@@ -2872,7 +2902,7 @@ public static async Task SpawnWeaponLoadoutAsync(string saveName, bool appendWea
}
// Set the current weapon to 'unarmed'.
- SetCurrentPedWeapon(Game.PlayerPed.Handle, (uint)GetHashKey("weapon_unarmed"), true);
+ SetCurrentPedWeapon(Game.PlayerPed.Handle, Game.GenerateHashASCII("weapon_unarmed"), true);
if (!(saveName == "vmenu_temp_weapons_loadout_before_respawn" || dontNotify))
{
@@ -2990,9 +3020,9 @@ public static bool SaveWeaponLoadout(string saveName)
///
public static async void SetWalkingStyle(string walkingStyle)
{
- if (IsPedModel(Game.PlayerPed.Handle, (uint)GetHashKey("mp_f_freemode_01")) || IsPedModel(Game.PlayerPed.Handle, (uint)GetHashKey("mp_m_freemode_01")))
+ if (IsPedModel(Game.PlayerPed.Handle, Game.GenerateHashASCII("mp_f_freemode_01")) || IsPedModel(Game.PlayerPed.Handle, Game.GenerateHashASCII("mp_m_freemode_01")))
{
- var isPedMale = IsPedModel(Game.PlayerPed.Handle, (uint)GetHashKey("mp_m_freemode_01"));
+ var isPedMale = IsPedModel(Game.PlayerPed.Handle, Game.GenerateHashASCII("mp_m_freemode_01"));
ClearPedAlternateMovementAnim(Game.PlayerPed.Handle, 0, 1f);
ClearPedAlternateMovementAnim(Game.PlayerPed.Handle, 1, 1f);
ClearPedAlternateMovementAnim(Game.PlayerPed.Handle, 2, 1f);
@@ -3444,7 +3474,7 @@ public static async void PressKeyFob(Vehicle veh)
var player = Game.Player;
if (player != null && !player.IsDead && !player.Character.IsInVehicle())
{
- var KeyFobHashKey = (uint)GetHashKey("p_car_keys_01");
+ var KeyFobHashKey = Game.GenerateHashASCII("p_car_keys_01");
RequestModel(KeyFobHashKey);
while (!HasModelLoaded(KeyFobHashKey))
{
@@ -3456,7 +3486,7 @@ public static async void PressKeyFob(Vehicle veh)
SetModelAsNoLongerNeeded(KeyFobHashKey); // cleanup model from memory
ClearPedTasks(player.Character.Handle);
- SetCurrentPedWeapon(Game.PlayerPed.Handle, (uint)GetHashKey("WEAPON_UNARMED"), true);
+ SetCurrentPedWeapon(Game.PlayerPed.Handle, Game.GenerateHashASCII("WEAPON_UNARMED"), true);
//if (player.Character.Weapons.Current.Hash != WeaponHash.Unarmed)
//{
// player.Character.Weapons.Give(WeaponHash.Unarmed, 1, true, true);
@@ -3599,5 +3629,81 @@ .. Enum.GetValues(typeof(VehicleData.ModType))
];
}
#endregion
+
+ #region Delete vehicle function
+
+ public async static void DeleteVehicle()
+ {
+ var player = Game.PlayerPed;
+
+ if (!player.IsAlive)
+ return;
+
+ if (player.IsInVehicle())
+ {
+ var veh = GetVehicle();
+
+ if (veh != null && veh.Exists() && veh.Driver == player)
+ {
+ SetVehicleHasBeenOwnedByPlayer(veh.Handle, false);
+ SetEntityAsMissionEntity(veh.Handle, false, false);
+ veh.Delete();
+ }
+ else
+ {
+ Notify.Error("This vehicle does not exist (somehow) or you need to be the driver of this vehicle to delete it!");
+ }
+
+ return;
+ }
+
+ float distance = ConfigManager.GetSettingsFloat(ConfigManager.Setting.vmenu_delete_vehicle_distance, 5.0f);
+ int maxDeleteTries = 5;
+ int maxHitTries = 5;
+
+ var forward = GetOffsetFromEntityInWorldCoords(player.Handle, 0f, distance, 0f);
+ var ray = StartShapeTestCapsule(player.Position.X, player.Position.Y, player.Position.Z, forward.X, forward.Y, forward.Z, 5f, 10, player.Handle, 7);
+
+ bool hit = false;
+ Vector3 endCoords = Vector3.Zero;
+ Vector3 surfaceNormal = Vector3.Zero;
+ int entity = 0;
+
+ for (int i = 0; i < maxHitTries; i++)
+ {
+ GetShapeTestResult(ray, ref hit, ref endCoords, ref surfaceNormal, ref entity);
+ if (hit) break;
+ await Task.FromResult(0);
+ }
+
+ if (!hit || !DoesEntityExist(entity) || !IsEntityAVehicle(entity))
+ {
+ Notify.Error("No vehicle found in front of you to delete!");
+ return;
+ }
+
+ var hitVeh = new Vehicle(entity);
+
+ for (int i = 0; i <= maxDeleteTries && DoesEntityExist(entity); i++)
+ {
+ NetworkRequestControlOfEntity(entity);
+ SetVehicleHasBeenOwnedByPlayer(entity, false);
+ SetEntityAsMissionEntity(entity, false, false);
+ hitVeh.Delete();
+ await Task.FromResult(0);
+ }
+
+ if (DoesEntityExist(entity))
+ {
+ Notify.Error("Failed to delete the vehicle in front of you. Try again or ask an admin for help.");
+ }
+ else
+ {
+ Notify.Success("Vehicle deleted successfully.");
+ }
+ }
+
+
+ #endregion
}
}
diff --git a/vMenu/EntitySpawner.cs b/vMenu/EntitySpawner.cs
index 0401f9b77..c3719ab8a 100644
--- a/vMenu/EntitySpawner.cs
+++ b/vMenu/EntitySpawner.cs
@@ -47,7 +47,7 @@ public EntitySpawner()
/// true spawn was succesful
public static void SpawnEntity(string model, Vector3 coords)
{
- SpawnEntity((uint)GetHashKey(model), coords);
+ SpawnEntity(Game.GenerateHashASCII(model), coords);
}
///
diff --git a/vMenu/EventManager.cs b/vMenu/EventManager.cs
index 3a57dafc2..682c6825a 100644
--- a/vMenu/EventManager.cs
+++ b/vMenu/EventManager.cs
@@ -41,6 +41,7 @@ public EventManager()
EventHandlers.Add("vMenu:SetAddons", new Action(SetConfigOptions)); // DEPRECATED: Backwards-compatible event handler; use 'vMenu:SetConfigOptions' instead
EventHandlers.Add("vMenu:SetConfigOptions", new Action(SetConfigOptions));
EventHandlers.Add("vMenu:SetPermissions", new Action(MainMenu.SetPermissions));
+ EventHandlers.Add("vMenu:SetSupplementaryPermissions", new Action(MainMenu.SetSupplementaryPermissions));
EventHandlers.Add("vMenu:KillMe", new Action(KillMe));
EventHandlers.Add("vMenu:Notify", new Action(NotifyPlayer));
EventHandlers.Add("vMenu:SetClouds", new Action(SetClouds));
@@ -98,6 +99,7 @@ private async void SetAppearanceOnFirstSpawn()
private void SetConfigOptions()
{
SetAddons();
+ SetWhiteLists();
SetExtras();
SetTattoos();
@@ -128,7 +130,7 @@ private void SetAddons()
{
if (!VehicleSpawner.AddonVehicles.ContainsKey(addon))
{
- VehicleSpawner.AddonVehicles.Add(addon, (uint)GetHashKey(addon));
+ VehicleSpawner.AddonVehicles.Add(addon, Game.GenerateHashASCII(addon));
}
else
{
@@ -144,7 +146,7 @@ private void SetAddons()
{
if (!WeaponOptions.AddonWeapons.ContainsKey(addon))
{
- WeaponOptions.AddonWeapons.Add(addon, (uint)GetHashKey(addon));
+ WeaponOptions.AddonWeapons.Add(addon, Game.GenerateHashASCII(addon));
}
else
{
@@ -160,7 +162,7 @@ private void SetAddons()
{
if (!PlayerAppearance.AddonPeds.ContainsKey(addon))
{
- PlayerAppearance.AddonPeds.Add(addon, (uint)GetHashKey(addon));
+ PlayerAppearance.AddonPeds.Add(addon, Game.GenerateHashASCII(addon));
}
else
{
@@ -190,6 +192,75 @@ private void SetAddons()
}
}
+ ///
+ /// Sets the addon models from the addons.json file.
+ ///
+ private void SetWhiteLists()
+ {
+ // reset addons
+ VehicleSpawner.WhitelistVehicles = new Dictionary();
+ PlayerAppearance.WhitelistedPeds = new Dictionary();
+ WeaponOptions.WeaponWhitelist = new Dictionary();
+
+ var jsonData = LoadResourceFile(GetCurrentResourceName(), "config/model-whitelists.json") ?? "{}";
+ try
+ {
+ // load new addons.
+ var whitelists = JsonConvert.DeserializeObject>>(jsonData);
+
+ // load whitelist vehicles
+ if (whitelists.ContainsKey("whitelistedvehicle"))
+ {
+ foreach (var whitelist in whitelists["whitelistedvehicle"])
+ {
+ if (!VehicleSpawner.WhitelistVehicles.ContainsKey(whitelist))
+ {
+ VehicleSpawner.WhitelistVehicles.Add(whitelist, Game.GenerateHashASCII(whitelist));
+ }
+ else
+ {
+ Debug.WriteLine($"[vMenu] [Error] Your model-whitelists.json file contains 2 or more entries with the same vehicle name! ({whitelist}) Please remove duplicate lines!");
+ }
+ }
+ }
+
+ // load whitelisted peds.
+ if (whitelists.ContainsKey("whitelistedpeds"))
+ {
+ foreach (var whitelist in whitelists["whitelistedpeds"])
+ {
+ if (!PlayerAppearance.WhitelistedPeds.ContainsKey(whitelist))
+ {
+ PlayerAppearance.WhitelistedPeds.Add(whitelist, Game.GenerateHashASCII(whitelist));
+ }
+ else
+ {
+ Debug.WriteLine($"[vMenu] [Error] Your model-whitelists.json file contains 2 or more entries with the same ped name! ({whitelist}) Please remove duplicate lines!");
+ }
+ }
+ }
+
+ // load whitelisted weapon.
+ if (whitelists.ContainsKey("whitelistedweapons"))
+ {
+ foreach (var whitelist in whitelists["whitelistedweapons"])
+ {
+ if (!WeaponOptions.WeaponWhitelist.ContainsKey(whitelist))
+ {
+ WeaponOptions.WeaponWhitelist.Add(whitelist, Game.GenerateHashASCII(whitelist));
+ }
+ else
+ {
+ Debug.WriteLine($"[vMenu] [Error] Your model-whitelists.json file contains 2 or more entries with the same ped name! ({whitelist}) Please remove duplicate lines!");
+ }
+ }
+ }
+ }
+ catch (JsonReaderException ex)
+ {
+ Debug.WriteLine($"\n\n^1[vMenu] [ERROR] ^7Your model-whitelists.json file contains a problem! Error details: {ex.Message}\n\n");
+ }
+ }
///
/// Sets the extras labels from the extras.json file.
///
@@ -207,7 +278,7 @@ private void SetExtras()
foreach (string model in extras.Keys)
{
- uint modelHash = (uint)GetHashKey(model);
+ uint modelHash = Game.GenerateHashASCII(model);
if (extras[model] != null && extras[model].Count > 0)
{
diff --git a/vMenu/FunctionsController.cs b/vMenu/FunctionsController.cs
index 12dbc85e5..a584fc027 100644
--- a/vMenu/FunctionsController.cs
+++ b/vMenu/FunctionsController.cs
@@ -51,7 +51,7 @@ class FunctionsController : BaseScript
private const string snowball_anim_dict = "anim@mp_snowball";
private const string snowball_anim_name = "pickup_snowball";
- private readonly uint snowball_hash = (uint)GetHashKey("weapon_snowball");
+ private readonly uint snowball_hash = Game.GenerateHashASCII("weapon_snowball");
private bool showSnowballInfo = false;
private bool stopPropsLoop = false;
@@ -76,6 +76,7 @@ public void SetupTickFunctions()
Tick += MiscSettings;
Tick += GeneralTasks;
Tick += GcTick;
+ Tick += ControllerTick;
if (GetSettingsBool(Setting.keep_player_head_props))
{
@@ -214,6 +215,36 @@ private async Task GcTick()
}
#endregion
+ #region Controller tick and functions
+ private async Task ControllerTick()
+ {
+ if (!Game.IsPaused && !IsPauseMenuRestarting() && IsScreenFadedIn() && !IsPlayerSwitchInProgress() && !Game.Player.IsDead && !MenuController.DisableMenuButtons)
+ {
+ if (Game.CurrentInputMode == InputMode.GamePad)
+ {
+ await HandleMenuToggleKeyForController();
+ }
+ }
+ await Task.FromResult(0);
+ }
+ private async Task HandleMenuToggleKeyForController()
+ {
+ int tmpTimer = GetGameTimer();
+ while ((Game.IsControlPressed(0, Control.InteractionMenu) || Game.IsDisabledControlPressed(0, Control.InteractionMenu)) && !Game.IsPaused && IsScreenFadedIn() && !Game.Player.IsDead && !IsPlayerSwitchInProgress() && !MenuController.DontOpenAnyMenu)
+ {
+ if (GetGameTimer() - tmpTimer > 400)
+ {
+ if (MainMenu.Menu != null)
+ {
+ MainMenu.Menu.OpenMenu();
+ }
+ break;
+ }
+ await Delay(0);
+ }
+ }
+ #endregion
+
#region General Tasks
///
/// All general tasks that run every 1 game ticks (and are not (sub)menu specific).
@@ -590,7 +621,6 @@ private async Task VehicleOptions()
var subMenus = new List()
{
- MainMenu.VehicleOptionsMenu.DeleteConfirmMenu,
MainMenu.VehicleOptionsMenu.VehicleColorsMenu,
MainMenu.VehicleOptionsMenu.VehicleComponentsMenu,
MainMenu.VehicleOptionsMenu.VehicleDoorsMenu,
@@ -1462,9 +1492,9 @@ private async Task WeaponOptions()
if (MainMenu.WeaponOptionsMenu.UnlimitedParachutes)
{
- if (!HasPedGotWeapon(Game.PlayerPed.Handle, (uint)GetHashKey("gadget_parachute"), false))
+ if (!HasPedGotWeapon(Game.PlayerPed.Handle, Game.GenerateHashASCII("gadget_parachute"), false))
{
- GiveWeaponToPed(Game.PlayerPed.Handle, (uint)GetHashKey("gadget_parachute"), 0, false, false);
+ GiveWeaponToPed(Game.PlayerPed.Handle, Game.GenerateHashASCII("gadget_parachute"), 0, false, false);
}
if (!GetPlayerHasReserveParachute(Game.Player.Handle))
@@ -2460,38 +2490,38 @@ private async Task OnlinePlayersTasks()
/*
private readonly List flareVehicles = new List()
{
- (uint)GetHashKey("mogul"),
- (uint)GetHashKey("rogue"),
- (uint)GetHashKey("starling"),
- (uint)GetHashKey("seabreeze"),
- (uint)GetHashKey("tula"),
- (uint)GetHashKey("bombushka"),
- (uint)GetHashKey("hunter"),
- (uint)GetHashKey("nokota"),
- (uint)GetHashKey("pyro"),
- (uint)GetHashKey("molotok"),
- (uint)GetHashKey("havok"),
- (uint)GetHashKey("alphaz1"),
- (uint)GetHashKey("microlight"),
- (uint)GetHashKey("howard"),
- (uint)GetHashKey("avenger"),
- (uint)GetHashKey("thruster"),
- (uint)GetHashKey("volatol")
+ Game.GenerateHashASCII("mogul"),
+ Game.GenerateHashASCII("rogue"),
+ Game.GenerateHashASCII("starling"),
+ Game.GenerateHashASCII("seabreeze"),
+ Game.GenerateHashASCII("tula"),
+ Game.GenerateHashASCII("bombushka"),
+ Game.GenerateHashASCII("hunter"),
+ Game.GenerateHashASCII("nokota"),
+ Game.GenerateHashASCII("pyro"),
+ Game.GenerateHashASCII("molotok"),
+ Game.GenerateHashASCII("havok"),
+ Game.GenerateHashASCII("alphaz1"),
+ Game.GenerateHashASCII("microlight"),
+ Game.GenerateHashASCII("howard"),
+ Game.GenerateHashASCII("avenger"),
+ Game.GenerateHashASCII("thruster"),
+ Game.GenerateHashASCII("volatol")
};
private readonly List bombVehicles = new List()
{
- (uint)GetHashKey("cuban800"),
- (uint)GetHashKey("mogul"),
- (uint)GetHashKey("rogue"),
- (uint)GetHashKey("starling"),
- (uint)GetHashKey("seabreeze"),
- (uint)GetHashKey("tula"),
- (uint)GetHashKey("bombushka"),
- (uint)GetHashKey("hunter"),
- (uint)GetHashKey("avenger"),
- (uint)GetHashKey("akula"),
- (uint)GetHashKey("volatol")
+ Game.GenerateHashASCII("cuban800"),
+ Game.GenerateHashASCII("mogul"),
+ Game.GenerateHashASCII("rogue"),
+ Game.GenerateHashASCII("starling"),
+ Game.GenerateHashASCII("seabreeze"),
+ Game.GenerateHashASCII("tula"),
+ Game.GenerateHashASCII("bombushka"),
+ Game.GenerateHashASCII("hunter"),
+ Game.GenerateHashASCII("avenger"),
+ Game.GenerateHashASCII("akula"),
+ Game.GenerateHashASCII("volatol")
};
/// Returns true if the player can currently fire flares.
@@ -3031,67 +3061,67 @@ private async Task SwitchHelmetOnce()
{
var sportBikes = new List()
{
- (uint)GetHashKey("AKUMA"),
- (uint)GetHashKey("BATI"),
- (uint)GetHashKey("BATI2"),
- (uint)GetHashKey("CARBONRS"),
- (uint)GetHashKey("DEFILER"),
- (uint)GetHashKey("DIABLOUS2"),
- (uint)GetHashKey("DOUBLE"),
- (uint)GetHashKey("FCR"),
- (uint)GetHashKey("FCR2"),
- (uint)GetHashKey("HAKUCHOU"),
- (uint)GetHashKey("HAKUCHOU2"),
- (uint)GetHashKey("LECTRO"),
- (uint)GetHashKey("NEMESIS"),
- (uint)GetHashKey("OPPRESSOR"),
- (uint)GetHashKey("OPPRESSOR2"),
- (uint)GetHashKey("PCJ"),
- (uint)GetHashKey("RUFFIAN"),
- (uint)GetHashKey("SHOTARO"),
- (uint)GetHashKey("VADER"),
- (uint)GetHashKey("VORTEX"),
+ Game.GenerateHashASCII("AKUMA"),
+ Game.GenerateHashASCII("BATI"),
+ Game.GenerateHashASCII("BATI2"),
+ Game.GenerateHashASCII("CARBONRS"),
+ Game.GenerateHashASCII("DEFILER"),
+ Game.GenerateHashASCII("DIABLOUS2"),
+ Game.GenerateHashASCII("DOUBLE"),
+ Game.GenerateHashASCII("FCR"),
+ Game.GenerateHashASCII("FCR2"),
+ Game.GenerateHashASCII("HAKUCHOU"),
+ Game.GenerateHashASCII("HAKUCHOU2"),
+ Game.GenerateHashASCII("LECTRO"),
+ Game.GenerateHashASCII("NEMESIS"),
+ Game.GenerateHashASCII("OPPRESSOR"),
+ Game.GenerateHashASCII("OPPRESSOR2"),
+ Game.GenerateHashASCII("PCJ"),
+ Game.GenerateHashASCII("RUFFIAN"),
+ Game.GenerateHashASCII("SHOTARO"),
+ Game.GenerateHashASCII("VADER"),
+ Game.GenerateHashASCII("VORTEX"),
};
var chopperBikes = new List()
{
- (uint)GetHashKey("SANCTUS"),
- (uint)GetHashKey("ZOMBIEA"),
- (uint)GetHashKey("ZOMBIEB"),
+ Game.GenerateHashASCII("SANCTUS"),
+ Game.GenerateHashASCII("ZOMBIEA"),
+ Game.GenerateHashASCII("ZOMBIEB"),
};
var dirtBikes = new List()
{
- (uint)GetHashKey("BF400"),
- (uint)GetHashKey("ENDURO"),
- (uint)GetHashKey("MANCHEZ"),
- (uint)GetHashKey("SANCHEZ"),
- (uint)GetHashKey("SANCHEZ2"),
- (uint)GetHashKey("ESSKEY"),
+ Game.GenerateHashASCII("BF400"),
+ Game.GenerateHashASCII("ENDURO"),
+ Game.GenerateHashASCII("MANCHEZ"),
+ Game.GenerateHashASCII("SANCHEZ"),
+ Game.GenerateHashASCII("SANCHEZ2"),
+ Game.GenerateHashASCII("ESSKEY"),
};
var scooters = new List()
{
- (uint)GetHashKey("FAGGIO"),
- (uint)GetHashKey("FAGGIO2"),
- (uint)GetHashKey("FAGGIO3"),
- (uint)GetHashKey("CLIFFHANGER"),
- (uint)GetHashKey("BAGGER"),
+ Game.GenerateHashASCII("FAGGIO"),
+ Game.GenerateHashASCII("FAGGIO2"),
+ Game.GenerateHashASCII("FAGGIO3"),
+ Game.GenerateHashASCII("CLIFFHANGER"),
+ Game.GenerateHashASCII("BAGGER"),
};
var policeb = new List()
{
- (uint)GetHashKey("AVARUS"),
- (uint)GetHashKey("CHIMERA"),
- (uint)GetHashKey("POLICEB"),
- (uint)GetHashKey("SOVEREIGN"),
- (uint)GetHashKey("HEXER"),
- (uint)GetHashKey("INNOVATION"),
- (uint)GetHashKey("NIGHTBLADE"),
- (uint)GetHashKey("RATBIKE"),
- (uint)GetHashKey("DAEMON"),
- (uint)GetHashKey("DAEMON2"),
- (uint)GetHashKey("DIABLOUS"),
- (uint)GetHashKey("GARGOYLE"),
- (uint)GetHashKey("THRUST"),
- (uint)GetHashKey("VINDICATOR"),
- (uint)GetHashKey("WOLFSBANE"),
+ Game.GenerateHashASCII("AVARUS"),
+ Game.GenerateHashASCII("CHIMERA"),
+ Game.GenerateHashASCII("POLICEB"),
+ Game.GenerateHashASCII("SOVEREIGN"),
+ Game.GenerateHashASCII("HEXER"),
+ Game.GenerateHashASCII("INNOVATION"),
+ Game.GenerateHashASCII("NIGHTBLADE"),
+ Game.GenerateHashASCII("RATBIKE"),
+ Game.GenerateHashASCII("DAEMON"),
+ Game.GenerateHashASCII("DAEMON2"),
+ Game.GenerateHashASCII("DIABLOUS"),
+ Game.GenerateHashASCII("GARGOYLE"),
+ Game.GenerateHashASCII("THRUST"),
+ Game.GenerateHashASCII("VINDICATOR"),
+ Game.GenerateHashASCII("WOLFSBANE"),
};
if (policeb.Contains((uint)veh.Model.Hash))
@@ -3205,7 +3235,7 @@ private async Task PickupSnowballOnce()
await Delay(0);
if (!fired)
{
- if (HasAnimEventFired(Game.PlayerPed.Handle, (uint)GetHashKey("CreateObject")))
+ if (HasAnimEventFired(Game.PlayerPed.Handle, Game.GenerateHashASCII("CreateObject")))
{
AddAmmoToPed(Game.PlayerPed.Handle, snowball_hash, 2);
GiveWeaponToPed(Game.PlayerPed.Handle, snowball_hash, 0, true, true);
@@ -3215,12 +3245,12 @@ private async Task PickupSnowballOnce()
}
fired = true;
}
- else if (HasAnimEventFired(Game.PlayerPed.Handle, (uint)GetHashKey("Interrupt")))
+ else if (HasAnimEventFired(Game.PlayerPed.Handle, Game.GenerateHashASCII("Interrupt")))
{
break;
}
}
- else if (HasAnimEventFired(Game.PlayerPed.Handle, (uint)GetHashKey("Interrupt")))
+ else if (HasAnimEventFired(Game.PlayerPed.Handle, Game.GenerateHashASCII("Interrupt")))
{
break;
}
diff --git a/vMenu/MainMenu.cs b/vMenu/MainMenu.cs
index 550f29cce..35365efd0 100644
--- a/vMenu/MainMenu.cs
+++ b/vMenu/MainMenu.cs
@@ -10,6 +10,8 @@
using vMenuClient.menus;
+using vMenuShared;
+
using static CitizenFX.Core.Native.API;
using static vMenuClient.CommonFunctions;
using static vMenuShared.ConfigManager;
@@ -23,6 +25,7 @@ public class MainMenu : BaseScript
public static bool PermissionsSetupComplete => ArePermissionsSetup;
public static bool ConfigOptionsSetupComplete = false;
+ public static bool AddonPermissionSetup = false;
public static string MenuToggleKey { get; private set; } = "M"; // M by default
public static string NoClipKey { get; private set; } = "F2"; // F2 by default
@@ -144,6 +147,14 @@ public MainMenu()
}
}
}), false);
+
+ RegisterCommand("vMenu:DV", new Action, string>((source, args, rawCommand) =>
+ {
+ if (IsAllowed(Permission.VODelete))
+ {
+ DeleteVehicle();
+ }
+ }), false);
if (!(GetSettingsString(Setting.vmenu_noclip_toggle_key) == null))
{
@@ -377,7 +388,19 @@ public static async Task RequestPlayerCoordinates(int serverId)
return coords;
}
#endregion
+ #region Set Permissions function
+ ///
+ /// Set the permissions for this client.
+ ///
+ ///
+ public static void SetSupplementaryPermissions(string permissionsList)
+ {
+ vMenuShared.SupplementaryPermissionManager.SetPermissions(permissionsList);
+ AddonPermissionSetup = true;
+ SupplementaryPermissionManager.ArePermissionsSetup = true;
+ }
+ #endregion
#region Set Permissions function
///
/// Set the permissions for this client.
@@ -414,7 +437,7 @@ public static async void SetPermissions(string permissionsList)
IsAllowed(Permission.VSOpenWheel, checkAnyway: true)
};
ArePermissionsSetup = true;
- while (!ConfigOptionsSetupComplete)
+ while (!ConfigOptionsSetupComplete && !AddonPermissionSetup)
{
await Delay(100);
}
@@ -488,21 +511,21 @@ static bool canUseMenu()
// Manage Stamina
if (PlayerOptionsMenu != null && PlayerOptionsMenu.PlayerStamina && IsAllowed(Permission.POUnlimitedStamina))
{
- StatSetInt((uint)GetHashKey("MP0_STAMINA"), 100, true);
+ StatSetInt(Game.GenerateHashASCII("MP0_STAMINA"), 100, true);
}
else
{
- StatSetInt((uint)GetHashKey("MP0_STAMINA"), 0, true);
+ StatSetInt(Game.GenerateHashASCII("MP0_STAMINA"), 0, true);
}
// Manage other stats, in order of appearance in the pause menu (stats) page.
- StatSetInt((uint)GetHashKey("MP0_SHOOTING_ABILITY"), 100, true); // Shooting
- StatSetInt((uint)GetHashKey("MP0_STRENGTH"), 100, true); // Strength
- StatSetInt((uint)GetHashKey("MP0_STEALTH_ABILITY"), 100, true); // Stealth
- StatSetInt((uint)GetHashKey("MP0_FLYING_ABILITY"), 100, true); // Flying
- StatSetInt((uint)GetHashKey("MP0_WHEELIE_ABILITY"), 100, true); // Driving
- StatSetInt((uint)GetHashKey("MP0_LUNG_CAPACITY"), 100, true); // Lung Capacity
- StatSetFloat((uint)GetHashKey("MP0_PLAYER_MENTAL_STATE"), 0f, true); // Mental State
+ StatSetInt(Game.GenerateHashASCII("MP0_SHOOTING_ABILITY"), 100, true); // Shooting
+ StatSetInt(Game.GenerateHashASCII("MP0_STRENGTH"), 100, true); // Strength
+ StatSetInt(Game.GenerateHashASCII("MP0_STEALTH_ABILITY"), 100, true); // Stealth
+ StatSetInt(Game.GenerateHashASCII("MP0_FLYING_ABILITY"), 100, true); // Flying
+ StatSetInt(Game.GenerateHashASCII("MP0_WHEELIE_ABILITY"), 100, true); // Driving
+ StatSetInt(Game.GenerateHashASCII("MP0_LUNG_CAPACITY"), 100, true); // Lung Capacity
+ StatSetFloat(Game.GenerateHashASCII("MP0_PLAYER_MENTAL_STATE"), 0f, true); // Mental State
}
RegisterCommand($"vMenu:{GetKeyMappingId()}:MenuToggle", new Action, string>((dynamic source, List args, string rawCommand) =>
diff --git a/vMenu/data/BlipInfo.cs b/vMenu/data/BlipInfo.cs
index 822f855b4..e4924164c 100644
--- a/vMenu/data/BlipInfo.cs
+++ b/vMenu/data/BlipInfo.cs
@@ -1,6 +1,7 @@
using System.Collections.Generic;
using static CitizenFX.Core.Native.API;
+using CitizenFX.Core;
namespace vMenuClient.data
{
@@ -11,69 +12,69 @@ public static int GetBlipSpriteForVehicle(int vehicle)
var model = (uint)GetEntityModel(vehicle);
var sprites = new Dictionary()
{
- { (uint)GetHashKey("taxi"), 56 },
+ { Game.GenerateHashASCII("taxi"), 56 },
//
- { (uint)GetHashKey("nightshark"), 225 },
+ { Game.GenerateHashASCII("nightshark"), 225 },
//
- { (uint)GetHashKey("rhino"), 421 },
+ { Game.GenerateHashASCII("rhino"), 421 },
//
- { (uint)GetHashKey("lazer"), 424 },
- { (uint)GetHashKey("besra"), 424 },
- { (uint)GetHashKey("hydra"), 424 },
+ { Game.GenerateHashASCII("lazer"), 424 },
+ { Game.GenerateHashASCII("besra"), 424 },
+ { Game.GenerateHashASCII("hydra"), 424 },
//
- { (uint)GetHashKey("insurgent"), 426 },
- { (uint)GetHashKey("insurgent2"), 426 },
- { (uint)GetHashKey("insurgent3"), 426 },
+ { Game.GenerateHashASCII("insurgent"), 426 },
+ { Game.GenerateHashASCII("insurgent2"), 426 },
+ { Game.GenerateHashASCII("insurgent3"), 426 },
//
- { (uint)GetHashKey("limo2"), 460 },
+ { Game.GenerateHashASCII("limo2"), 460 },
//
- { (uint)GetHashKey("blazer5"), 512 },
+ { Game.GenerateHashASCII("blazer5"), 512 },
//
- { (uint)GetHashKey("phantom2"), 528 },
- { (uint)GetHashKey("boxville5"), 529 },
- { (uint)GetHashKey("ruiner2"), 530 },
- { (uint)GetHashKey("dune4"), 531 },
- { (uint)GetHashKey("dune5"), 531 },
- { (uint)GetHashKey("wastelander"), 532 },
- { (uint)GetHashKey("voltic2"), 533 },
- { (uint)GetHashKey("technical2"), 534 },
- { (uint)GetHashKey("technical3"), 534 },
- { (uint)GetHashKey("technical"), 534 },
+ { Game.GenerateHashASCII("phantom2"), 528 },
+ { Game.GenerateHashASCII("boxville5"), 529 },
+ { Game.GenerateHashASCII("ruiner2"), 530 },
+ { Game.GenerateHashASCII("dune4"), 531 },
+ { Game.GenerateHashASCII("dune5"), 531 },
+ { Game.GenerateHashASCII("wastelander"), 532 },
+ { Game.GenerateHashASCII("voltic2"), 533 },
+ { Game.GenerateHashASCII("technical2"), 534 },
+ { Game.GenerateHashASCII("technical3"), 534 },
+ { Game.GenerateHashASCII("technical"), 534 },
//
- { (uint)GetHashKey("apc"), 558 },
- { (uint)GetHashKey("oppressor"), 559 },
- { (uint)GetHashKey("oppressor2"), 559 },
- { (uint)GetHashKey("halftrack"), 560 },
- { (uint)GetHashKey("dune3"), 561 },
- { (uint)GetHashKey("tampa3"), 562 },
- { (uint)GetHashKey("trailersmall2"), 563 },
+ { Game.GenerateHashASCII("apc"), 558 },
+ { Game.GenerateHashASCII("oppressor"), 559 },
+ { Game.GenerateHashASCII("oppressor2"), 559 },
+ { Game.GenerateHashASCII("halftrack"), 560 },
+ { Game.GenerateHashASCII("dune3"), 561 },
+ { Game.GenerateHashASCII("tampa3"), 562 },
+ { Game.GenerateHashASCII("trailersmall2"), 563 },
//
- { (uint)GetHashKey("alphaz1"), 572 },
- { (uint)GetHashKey("bombushka"), 573 },
- { (uint)GetHashKey("havok"), 574 },
- { (uint)GetHashKey("howard"), 575 },
- { (uint)GetHashKey("hunter"), 576 },
- { (uint)GetHashKey("microlight"), 577 },
- { (uint)GetHashKey("mogul"), 578 },
- { (uint)GetHashKey("molotok"), 579 },
- { (uint)GetHashKey("nokota"), 580 },
- { (uint)GetHashKey("pyro"), 581 },
- { (uint)GetHashKey("rogue"), 582 },
- { (uint)GetHashKey("starling"), 583 },
- { (uint)GetHashKey("seabreeze"), 584 },
- { (uint)GetHashKey("tula"), 585 },
+ { Game.GenerateHashASCII("alphaz1"), 572 },
+ { Game.GenerateHashASCII("bombushka"), 573 },
+ { Game.GenerateHashASCII("havok"), 574 },
+ { Game.GenerateHashASCII("howard"), 575 },
+ { Game.GenerateHashASCII("hunter"), 576 },
+ { Game.GenerateHashASCII("microlight"), 577 },
+ { Game.GenerateHashASCII("mogul"), 578 },
+ { Game.GenerateHashASCII("molotok"), 579 },
+ { Game.GenerateHashASCII("nokota"), 580 },
+ { Game.GenerateHashASCII("pyro"), 581 },
+ { Game.GenerateHashASCII("rogue"), 582 },
+ { Game.GenerateHashASCII("starling"), 583 },
+ { Game.GenerateHashASCII("seabreeze"), 584 },
+ { Game.GenerateHashASCII("tula"), 585 },
//
- { (uint)GetHashKey("avenger"), 589 },
+ { Game.GenerateHashASCII("avenger"), 589 },
//
- { (uint)GetHashKey("stromberg"), 595 },
- { (uint)GetHashKey("deluxo"), 596 },
- { (uint)GetHashKey("thruster"), 597 },
- { (uint)GetHashKey("khanjali"), 598 },
- { (uint)GetHashKey("riot2"), 599 },
- { (uint)GetHashKey("volatol"), 600 },
- { (uint)GetHashKey("barrage"), 601 },
- { (uint)GetHashKey("akula"), 602 },
- { (uint)GetHashKey("chernobog"), 603 },
+ { Game.GenerateHashASCII("stromberg"), 595 },
+ { Game.GenerateHashASCII("deluxo"), 596 },
+ { Game.GenerateHashASCII("thruster"), 597 },
+ { Game.GenerateHashASCII("khanjali"), 598 },
+ { Game.GenerateHashASCII("riot2"), 599 },
+ { Game.GenerateHashASCII("volatol"), 600 },
+ { Game.GenerateHashASCII("barrage"), 601 },
+ { Game.GenerateHashASCII("akula"), 602 },
+ { Game.GenerateHashASCII("chernobog"), 603 },
};
if (sprites.ContainsKey(model))
diff --git a/vMenu/data/PedModels.cs b/vMenu/data/PedModels.cs
index 6345c16c3..da264eff5 100644
--- a/vMenu/data/PedModels.cs
+++ b/vMenu/data/PedModels.cs
@@ -1,6 +1,7 @@
using System.Collections.Generic;
using static CitizenFX.Core.Native.API;
+using CitizenFX.Core;
namespace vMenuClient.data
{
@@ -8,49 +9,49 @@ public static class PedModels
{
public static List AnimalHashes = new()
{
- (uint)GetHashKey("a_c_boar"),
- (uint)GetHashKey("a_c_boar_02"), // mp2023_01
- (uint)GetHashKey("a_c_cat_01"),
- (uint)GetHashKey("a_c_cat_02"), // mp2025_02
- (uint)GetHashKey("a_c_chickenhawk"),
- (uint)GetHashKey("a_c_chimp"),
- (uint)GetHashKey("a_c_chimp_02"), // mpchristmas3
- (uint)GetHashKey("a_c_chop"),
- (uint)GetHashKey("a_c_chop_02"), // mpsecurity
- (uint)GetHashKey("a_c_cormorant"),
- (uint)GetHashKey("a_c_cow"),
- (uint)GetHashKey("a_c_coyote"),
- (uint)GetHashKey("a_c_coyote_02"), // mp2023_01
- (uint)GetHashKey("a_c_crow"),
- (uint)GetHashKey("a_c_deer"),
- (uint)GetHashKey("a_c_deer_02"), // mp2023_01
- (uint)GetHashKey("a_c_dolphin"),
- (uint)GetHashKey("a_c_fish"),
- (uint)GetHashKey("a_c_hen"),
- (uint)GetHashKey("a_c_humpback"),
- (uint)GetHashKey("a_c_husky"),
- (uint)GetHashKey("a_c_killerwhale"),
- (uint)GetHashKey("a_c_mtlion"),
- (uint)GetHashKey("a_c_mtlion_02"), // mp2023_01
- (uint)GetHashKey("a_c_panther"), // mpheist4
- (uint)GetHashKey("a_c_pig"),
- (uint)GetHashKey("a_c_pigeon"),
- (uint)GetHashKey("a_c_poodle"),
- (uint)GetHashKey("a_c_pug"),
- (uint)GetHashKey("a_c_pug_02"), // mp2023_01
- (uint)GetHashKey("a_c_rabbit_01"),
- (uint)GetHashKey("a_c_rabbit_02"), // mpchristmas3
- (uint)GetHashKey("a_c_rat"),
- (uint)GetHashKey("a_c_retriever"),
- (uint)GetHashKey("a_c_rhesus"),
- (uint)GetHashKey("a_c_rottweiler"),
- (uint)GetHashKey("a_c_rottweiler_02"), // mp2025_01
- (uint)GetHashKey("a_c_seagull"),
- (uint)GetHashKey("a_c_sharkhammer"),
- (uint)GetHashKey("a_c_sharktiger"),
- (uint)GetHashKey("a_c_shepherd"),
- (uint)GetHashKey("a_c_stingray"),
- (uint)GetHashKey("a_c_westy")
+ Game.GenerateHashASCII("a_c_boar"),
+ Game.GenerateHashASCII("a_c_boar_02"), // mp2023_01
+ Game.GenerateHashASCII("a_c_cat_01"),
+ Game.GenerateHashASCII("a_c_cat_02"), // mp2025_02
+ Game.GenerateHashASCII("a_c_chickenhawk"),
+ Game.GenerateHashASCII("a_c_chimp"),
+ Game.GenerateHashASCII("a_c_chimp_02"), // mpchristmas3
+ Game.GenerateHashASCII("a_c_chop"),
+ Game.GenerateHashASCII("a_c_chop_02"), // mpsecurity
+ Game.GenerateHashASCII("a_c_cormorant"),
+ Game.GenerateHashASCII("a_c_cow"),
+ Game.GenerateHashASCII("a_c_coyote"),
+ Game.GenerateHashASCII("a_c_coyote_02"), // mp2023_01
+ Game.GenerateHashASCII("a_c_crow"),
+ Game.GenerateHashASCII("a_c_deer"),
+ Game.GenerateHashASCII("a_c_deer_02"), // mp2023_01
+ Game.GenerateHashASCII("a_c_dolphin"),
+ Game.GenerateHashASCII("a_c_fish"),
+ Game.GenerateHashASCII("a_c_hen"),
+ Game.GenerateHashASCII("a_c_humpback"),
+ Game.GenerateHashASCII("a_c_husky"),
+ Game.GenerateHashASCII("a_c_killerwhale"),
+ Game.GenerateHashASCII("a_c_mtlion"),
+ Game.GenerateHashASCII("a_c_mtlion_02"), // mp2023_01
+ Game.GenerateHashASCII("a_c_panther"), // mpheist4
+ Game.GenerateHashASCII("a_c_pig"),
+ Game.GenerateHashASCII("a_c_pigeon"),
+ Game.GenerateHashASCII("a_c_poodle"),
+ Game.GenerateHashASCII("a_c_pug"),
+ Game.GenerateHashASCII("a_c_pug_02"), // mp2023_01
+ Game.GenerateHashASCII("a_c_rabbit_01"),
+ Game.GenerateHashASCII("a_c_rabbit_02"), // mpchristmas3
+ Game.GenerateHashASCII("a_c_rat"),
+ Game.GenerateHashASCII("a_c_retriever"),
+ Game.GenerateHashASCII("a_c_rhesus"),
+ Game.GenerateHashASCII("a_c_rottweiler"),
+ Game.GenerateHashASCII("a_c_rottweiler_02"), // mp2025_01
+ Game.GenerateHashASCII("a_c_seagull"),
+ Game.GenerateHashASCII("a_c_sharkhammer"),
+ Game.GenerateHashASCII("a_c_sharktiger"),
+ Game.GenerateHashASCII("a_c_shepherd"),
+ Game.GenerateHashASCII("a_c_stingray"),
+ Game.GenerateHashASCII("a_c_westy")
};
}
}
diff --git a/vMenu/data/ValidAddonWeapon.cs b/vMenu/data/ValidAddonWeapon.cs
index 82e497cdc..842c8aa88 100644
--- a/vMenu/data/ValidAddonWeapon.cs
+++ b/vMenu/data/ValidAddonWeapon.cs
@@ -89,12 +89,12 @@ private static void CreateAddonWeaponsList()
var realName = addonWeapon.Key;
var localizedName = addonWeapon.Value;
if (realName == "weapon_unarmed") continue;
- var hash = (uint)GetHashKey(realName);
+ var hash = Game.GenerateHashASCII(realName);
var componentHashes = new Dictionary();
var weaponComponents = ValidWeapons.GetWeaponComponents();
foreach (var comp in weaponComponents.Keys)
{
- uint componentHash = (uint)GetHashKey(comp);
+ uint componentHash = Game.GenerateHashASCII(comp);
if (DoesWeaponTakeWeaponComponent(hash, componentHash))
{
string componentName = weaponComponents[comp];
diff --git a/vMenu/data/ValidWeapon.cs b/vMenu/data/ValidWeapon.cs
index f4a0b81c1..a58972b44 100644
--- a/vMenu/data/ValidWeapon.cs
+++ b/vMenu/data/ValidWeapon.cs
@@ -108,13 +108,13 @@ private static void CreateWeaponsList()
var realName = weapon.Key;
var localizedName = weapon.Value;
if (realName == "weapon_unarmed") continue;
- var hash = (uint)GetHashKey(realName);
+ var hash = Game.GenerateHashASCII(realName);
var componentHashes = new Dictionary();
var weaponComponents = GetWeaponComponents();
var weaponComponentKeys = weaponComponents.Keys;
foreach (var comp in weaponComponentKeys)
{
- var componentHash = (uint)GetHashKey(comp);
+ var componentHash = Game.GenerateHashASCII(comp);
if (DoesWeaponTakeWeaponComponent(hash, componentHash))
{
var componentName = weaponComponents[comp];
diff --git a/vMenu/menus/MpPedCustomization.cs b/vMenu/menus/MpPedCustomization.cs
index 610804f6e..8747ab9bc 100644
--- a/vMenu/menus/MpPedCustomization.cs
+++ b/vMenu/menus/MpPedCustomization.cs
@@ -159,7 +159,7 @@ private void MakeCreateCharacterMenu(bool male, bool editPed = false)
currentCharacter.PropVariations.props = new Dictionary>();
currentCharacter.PedHeadBlendData = Game.PlayerPed.GetHeadBlendData();
currentCharacter.Version = 1;
- currentCharacter.ModelHash = male ? (uint)GetHashKey("mp_m_freemode_01") : (uint)GetHashKey("mp_f_freemode_01");
+ currentCharacter.ModelHash = male ? Game.GenerateHashASCII("mp_m_freemode_01") : Game.GenerateHashASCII("mp_f_freemode_01");
currentCharacter.IsMale = male;
// Places the sliders in the middle by default
@@ -1626,13 +1626,13 @@ void ApplySavedTattoos()
foreach (var tattoo in allTattoos)
{
- AddPedDecorationFromHashes(Game.PlayerPed.Handle, (uint)GetHashKey(tattoo.Key), (uint)GetHashKey(tattoo.Value));
+ AddPedDecorationFromHashes(Game.PlayerPed.Handle, Game.GenerateHashASCII(tattoo.Key), Game.GenerateHashASCII(tattoo.Value));
}
if (!string.IsNullOrEmpty(currentCharacter.PedAppearance.HairOverlay.Key) && !string.IsNullOrEmpty(currentCharacter.PedAppearance.HairOverlay.Value))
{
// reset hair value
- AddPedDecorationFromHashes(Game.PlayerPed.Handle, (uint)GetHashKey(currentCharacter.PedAppearance.HairOverlay.Key), (uint)GetHashKey(currentCharacter.PedAppearance.HairOverlay.Value));
+ AddPedDecorationFromHashes(Game.PlayerPed.Handle, Game.GenerateHashASCII(currentCharacter.PedAppearance.HairOverlay.Key), Game.GenerateHashASCII(currentCharacter.PedAppearance.HairOverlay.Value));
}
}
@@ -1653,7 +1653,7 @@ void ApplySavedTattoos()
var tat = new KeyValuePair(Tattoo.collectionName, Tattoo.name);
if (!currentCharacter.PedTatttoos.HairTattoos.Contains(tat))
{
- AddPedDecorationFromHashes(Game.PlayerPed.Handle, (uint)GetHashKey(tat.Key), (uint)GetHashKey(tat.Value));
+ AddPedDecorationFromHashes(Game.PlayerPed.Handle, Game.GenerateHashASCII(tat.Key), Game.GenerateHashASCII(tat.Value));
}
}
else if (menuIndex == 1) // head
@@ -1662,7 +1662,7 @@ void ApplySavedTattoos()
var tat = new KeyValuePair(Tattoo.collectionName, Tattoo.name);
if (!currentCharacter.PedTatttoos.HeadTattoos.Contains(tat))
{
- AddPedDecorationFromHashes(Game.PlayerPed.Handle, (uint)GetHashKey(tat.Key), (uint)GetHashKey(tat.Value));
+ AddPedDecorationFromHashes(Game.PlayerPed.Handle, Game.GenerateHashASCII(tat.Key), Game.GenerateHashASCII(tat.Value));
}
}
else if (menuIndex == 2) // torso
@@ -1671,7 +1671,7 @@ void ApplySavedTattoos()
var tat = new KeyValuePair(Tattoo.collectionName, Tattoo.name);
if (!currentCharacter.PedTatttoos.TorsoTattoos.Contains(tat))
{
- AddPedDecorationFromHashes(Game.PlayerPed.Handle, (uint)GetHashKey(tat.Key), (uint)GetHashKey(tat.Value));
+ AddPedDecorationFromHashes(Game.PlayerPed.Handle, Game.GenerateHashASCII(tat.Key), Game.GenerateHashASCII(tat.Value));
}
}
else if (menuIndex == 3) // left arm
@@ -1680,7 +1680,7 @@ void ApplySavedTattoos()
var tat = new KeyValuePair(Tattoo.collectionName, Tattoo.name);
if (!currentCharacter.PedTatttoos.LeftArmTattoos.Contains(tat))
{
- AddPedDecorationFromHashes(Game.PlayerPed.Handle, (uint)GetHashKey(tat.Key), (uint)GetHashKey(tat.Value));
+ AddPedDecorationFromHashes(Game.PlayerPed.Handle, Game.GenerateHashASCII(tat.Key), Game.GenerateHashASCII(tat.Value));
}
}
else if (menuIndex == 4) // right arm
@@ -1689,7 +1689,7 @@ void ApplySavedTattoos()
var tat = new KeyValuePair(Tattoo.collectionName, Tattoo.name);
if (!currentCharacter.PedTatttoos.RightArmTattoos.Contains(tat))
{
- AddPedDecorationFromHashes(Game.PlayerPed.Handle, (uint)GetHashKey(tat.Key), (uint)GetHashKey(tat.Value));
+ AddPedDecorationFromHashes(Game.PlayerPed.Handle, Game.GenerateHashASCII(tat.Key), Game.GenerateHashASCII(tat.Value));
}
}
else if (menuIndex == 5) // left leg
@@ -1698,7 +1698,7 @@ void ApplySavedTattoos()
var tat = new KeyValuePair(Tattoo.collectionName, Tattoo.name);
if (!currentCharacter.PedTatttoos.LeftLegTattoos.Contains(tat))
{
- AddPedDecorationFromHashes(Game.PlayerPed.Handle, (uint)GetHashKey(tat.Key), (uint)GetHashKey(tat.Value));
+ AddPedDecorationFromHashes(Game.PlayerPed.Handle, Game.GenerateHashASCII(tat.Key), Game.GenerateHashASCII(tat.Value));
}
}
else if (menuIndex == 6) // right leg
@@ -1707,7 +1707,7 @@ void ApplySavedTattoos()
var tat = new KeyValuePair(Tattoo.collectionName, Tattoo.name);
if (!currentCharacter.PedTatttoos.RightLegTattoos.Contains(tat))
{
- AddPedDecorationFromHashes(Game.PlayerPed.Handle, (uint)GetHashKey(tat.Key), (uint)GetHashKey(tat.Value));
+ AddPedDecorationFromHashes(Game.PlayerPed.Handle, Game.GenerateHashASCII(tat.Key), Game.GenerateHashASCII(tat.Value));
}
}
else if (menuIndex == 7) // badges
@@ -1716,7 +1716,7 @@ void ApplySavedTattoos()
var tat = new KeyValuePair(Tattoo.collectionName, Tattoo.name);
if (!currentCharacter.PedTatttoos.BadgeTattoos.Contains(tat))
{
- AddPedDecorationFromHashes(Game.PlayerPed.Handle, (uint)GetHashKey(tat.Key), (uint)GetHashKey(tat.Value));
+ AddPedDecorationFromHashes(Game.PlayerPed.Handle, Game.GenerateHashASCII(tat.Key), Game.GenerateHashASCII(tat.Value));
}
}
else if (menuIndex == 8) // addon
@@ -1725,7 +1725,7 @@ void ApplySavedTattoos()
var tat = new KeyValuePair(Tattoo.collectionName, Tattoo.name);
if (!currentCharacter.PedTatttoos.AddonTattoos.Contains(tat))
{
- AddPedDecorationFromHashes(Game.PlayerPed.Handle, (uint)GetHashKey(tat.Key), (uint)GetHashKey(tat.Value));
+ AddPedDecorationFromHashes(Game.PlayerPed.Handle, Game.GenerateHashASCII(tat.Key), Game.GenerateHashASCII(tat.Value));
}
}
};
@@ -2232,7 +2232,7 @@ void ApplySavedTattoos()
{
if (item == createMaleBtn)
{
- var model = (uint)GetHashKey("mp_m_freemode_01");
+ var model = Game.GenerateHashASCII("mp_m_freemode_01");
if (!HasModelLoaded(model))
{
@@ -2267,7 +2267,7 @@ void ApplySavedTattoos()
}
else if (item == createFemaleBtn)
{
- var model = (uint)GetHashKey("mp_f_freemode_01");
+ var model = Game.GenerateHashASCII("mp_f_freemode_01");
if (!HasModelLoaded(model))
{
@@ -3213,7 +3213,7 @@ internal void ChangePlayerHair(int newHairIndex)
currentCharacter.PedAppearance.hairStyle = newHairIndex;
if (hairOverlays.ContainsKey(newHairIndex))
{
- SetPedFacialDecoration(Game.PlayerPed.Handle, (uint)GetHashKey(hairOverlays[newHairIndex].Key), (uint)GetHashKey(hairOverlays[newHairIndex].Value));
+ SetPedFacialDecoration(Game.PlayerPed.Handle, Game.GenerateHashASCII(hairOverlays[newHairIndex].Key), Game.GenerateHashASCII(hairOverlays[newHairIndex].Value));
currentCharacter.PedAppearance.HairOverlay = new KeyValuePair(hairOverlays[newHairIndex].Key, hairOverlays[newHairIndex].Value);
}
}
@@ -3366,7 +3366,7 @@ internal async Task AppySavedDataToPed(MultiplayerPedData character, int pedHand
SetPedHairColor(pedHandle, appData.hairColor, appData.hairHighlightColor);
if (!string.IsNullOrEmpty(appData.HairOverlay.Key) && !string.IsNullOrEmpty(appData.HairOverlay.Value))
{
- SetPedFacialDecoration(pedHandle, (uint)GetHashKey(appData.HairOverlay.Key), (uint)GetHashKey(appData.HairOverlay.Value));
+ SetPedFacialDecoration(pedHandle, Game.GenerateHashASCII(appData.HairOverlay.Key), Game.GenerateHashASCII(appData.HairOverlay.Value));
}
// blemishes
SetPedHeadOverlay(pedHandle, 0, appData.blemishesStyle, appData.blemishesOpacity);
@@ -3470,7 +3470,7 @@ internal async Task AppySavedDataToPed(MultiplayerPedData character, int pedHand
foreach (var tattoo in allTattoos)
{
- AddPedDecorationFromHashes(pedHandle, (uint)GetHashKey(tattoo.Key), (uint)GetHashKey(tattoo.Value));
+ AddPedDecorationFromHashes(pedHandle, Game.GenerateHashASCII(tattoo.Key), Game.GenerateHashASCII(tattoo.Value));
}
#endregion
}
diff --git a/vMenu/menus/PlayerAppearance.cs b/vMenu/menus/PlayerAppearance.cs
index 2c66b1163..9c799ccf4 100644
--- a/vMenu/menus/PlayerAppearance.cs
+++ b/vMenu/menus/PlayerAppearance.cs
@@ -29,6 +29,7 @@ public class PlayerAppearance
private readonly Menu otherPedsMenu = new("Other Peds", "Spawn A Ped");
public static Dictionary AddonPeds;
+ public static Dictionary WhitelistedPeds;
public static int ClothingAnimationType { get; set; } = UserDefaults.PAClothingAnimationType;
@@ -342,19 +343,24 @@ void UpdateSavedPedsMenu()
var pedBtn = new MenuItem(ped.Key, "Click to spawn this model.") { Label = $"({name})" };
+ checkPedWhitelist(ped.Key, pedBtn);
+
if (!IsModelInCdimage(ped.Value) || !IsModelAPed(ped.Value))
{
pedBtn.Enabled = false;
pedBtn.LeftIcon = MenuItem.Icon.LOCK;
pedBtn.Description = "This ped is not (correctly) streamed. If you are the server owner, please ensure that the ped name and model are valid!";
+ }
+ else
+ {
+ checkPedWhitelist(ped.Key, pedBtn);
}
-
addonPedsMenu.AddMenuItem(pedBtn);
}
addonPedsMenu.OnItemSelect += async (sender, item, index) =>
{
- await SetPlayerSkin((uint)GetHashKey(item.Text), new PedInfo() { version = -1 }, true);
+ await SetPlayerSkin(Game.GenerateHashASCII(item.Text), new PedInfo() { version = -1 }, true);
};
}
@@ -386,30 +392,35 @@ void UpdateSavedPedsMenu()
foreach (var animal in animalModels)
{
var animalBtn = new MenuItem(animal.Key, "Click to spawn this animal.") { Label = $"({animal.Value})" };
+ checkPedWhitelist(animal.Key, animalBtn);
animalsPedsMenu.AddMenuItem(animalBtn);
}
foreach (var ped in mainModels)
{
var pedBtn = new MenuItem(ped.Key, "Click to spawn this ped.") { Label = $"({ped.Value})" };
+ checkPedWhitelist(ped.Key, pedBtn);
mainPedsMenu.AddMenuItem(pedBtn);
}
foreach (var ped in maleModels)
{
var pedBtn = new MenuItem(ped.Key, "Click to spawn this ped.") { Label = $"({ped.Value})" };
+ checkPedWhitelist(ped.Key, pedBtn);
malePedsMenu.AddMenuItem(pedBtn);
}
foreach (var ped in femaleModels)
{
var pedBtn = new MenuItem(ped.Key, "Click to spawn this ped.") { Label = $"({ped.Value})" };
+ checkPedWhitelist(ped.Key, pedBtn);
femalePedsMenu.AddMenuItem(pedBtn);
}
foreach (var ped in otherPeds)
{
var pedBtn = new MenuItem(ped.Key, "Click to spawn this ped.") { Label = $"({ped.Value})" };
+ checkPedWhitelist(ped.Key, pedBtn);
otherPedsMenu.AddMenuItem(pedBtn);
}
@@ -450,7 +461,7 @@ void ResetMenuFilter(Menu m)
async void SpawnPed(Menu m, MenuItem item, int index)
{
- var model = (uint)GetHashKey(item.Text);
+ var model = Game.GenerateHashASCII(item.Text);
if (m == animalsPedsMenu && !Game.PlayerPed.IsInWater)
{
switch (item.Text)
@@ -761,6 +772,18 @@ void ChangeListItem(MenuListItem item, int newListIndex)
};
}
+ public static void checkPedWhitelist(string ped, MenuItem pedBtn)
+ {
+ if (WhitelistedPeds.ContainsKey(ped.ToLower()))
+ {
+ if (!vMenuShared.SupplementaryPermissionManager.IsAllowed("PW" + ped.ToLower()))
+ {
+ pedBtn.Enabled = false;
+ pedBtn.LeftIcon = MenuItem.Icon.LOCK;
+ pedBtn.Description = "Access to this has been restricted by the server owner.";
+ }
+ }
+ }
#endregion
diff --git a/vMenu/menus/PlayerOptions.cs b/vMenu/menus/PlayerOptions.cs
index 94911aa5a..77660db43 100644
--- a/vMenu/menus/PlayerOptions.cs
+++ b/vMenu/menus/PlayerOptions.cs
@@ -372,7 +372,7 @@ private void CreateMenu()
else if (item == unlimitedStaminaCheckbox)
{
PlayerStamina = _checked;
- StatSetInt((uint)GetHashKey("MP0_STAMINA"), _checked ? 100 : 0, true);
+ StatSetInt(Game.GenerateHashASCII("MP0_STAMINA"), _checked ? 100 : 0, true);
}
// Fast run toggled.
else if (item == fastRunCheckbox)
diff --git a/vMenu/menus/Recording.cs b/vMenu/menus/Recording.cs
index dcbdab5a5..89fe90012 100644
--- a/vMenu/menus/Recording.cs
+++ b/vMenu/menus/Recording.cs
@@ -48,7 +48,7 @@ private void CreateMenu()
}
else if (item == openPmGallery)
{
- ActivateFrontendMenu((uint)GetHashKey("FE_MENU_VERSION_MP_PAUSE"), true, 3);
+ ActivateFrontendMenu(Game.GenerateHashASCII("FE_MENU_VERSION_MP_PAUSE"), true, 3);
}
else if (item == takePic)
{
diff --git a/vMenu/menus/VehicleOptions.cs b/vMenu/menus/VehicleOptions.cs
index 6c89be7f7..394904472 100644
--- a/vMenu/menus/VehicleOptions.cs
+++ b/vMenu/menus/VehicleOptions.cs
@@ -461,27 +461,8 @@ private void CreateMenu()
}
else
{
- var veh = GetVehicle();
- if (veh != null && veh.Exists() && GetVehicle().Driver == Game.PlayerPed)
- {
- SetVehicleHasBeenOwnedByPlayer(veh.Handle, false);
- SetEntityAsMissionEntity(veh.Handle, false, false);
- veh.Delete();
- }
- else
- {
- if (!Game.PlayerPed.IsInVehicle())
- {
- Notify.Alert(CommonErrors.NoVehicle);
- }
- else
- {
- Notify.Alert("You need to be in the driver's seat if you want to delete a vehicle.");
- }
-
- }
+ DeleteVehicle();
DeleteConfirmMenu.GoBack();
- menu.GoBack();
}
};
#endregion
diff --git a/vMenu/menus/VehicleSpawner.cs b/vMenu/menus/VehicleSpawner.cs
index 6683ea567..0a6c6dee4 100644
--- a/vMenu/menus/VehicleSpawner.cs
+++ b/vMenu/menus/VehicleSpawner.cs
@@ -7,6 +7,8 @@
using vMenuClient.data;
+using vMenuShared;
+
using static CitizenFX.Core.Native.API;
using static vMenuClient.CommonFunctions;
using static vMenuShared.PermissionsManager;
@@ -18,6 +20,7 @@ public class VehicleSpawner
// Variables
private Menu menu;
public static Dictionary AddonVehicles;
+ public static Dictionary WhitelistVehicles;
public bool SpawnInVehicle { get; private set; } = UserDefaults.VehicleSpawnerSpawnInside;
public bool ReplaceVehicle { get; private set; } = UserDefaults.VehicleSpawnerReplacePrevious;
@@ -91,6 +94,13 @@ private void CreateMenu()
Label = $"({veh.Key})",
ItemData = veh.Key // store the model name in the button data.
};
+
+ if (!SupplementaryPermissionManager.IsAllowed("VW" + veh.Key))
+ {
+ carBtn.Enabled = false;
+ carBtn.LeftIcon = MenuItem.Icon.LOCK;
+ carBtn.Description = "Access to this has been restricted by the server owner.";
+ }
// This should be impossible to be false, but we check it anyway.
if (IsModelInCdimage(veh.Value))
@@ -305,7 +315,7 @@ private void CreateMenu()
// Get the localized vehicle name, if it's "NULL" (no label found) then use the "properCasedModelName" created above.
var vehName = GetVehDisplayNameFromModel(veh) != "NULL" ? GetVehDisplayNameFromModel(veh) : properCasedModelName;
var vehModelName = veh;
- var model = (uint)GetHashKey(vehModelName);
+ var model = Game.GenerateHashASCII(vehModelName);
var topSpeed = Map(GetVehicleModelEstimatedMaxSpeed(model), 0f, speedValues[vehClass], 0f, 1f);
var acceleration = Map(GetVehicleModelAcceleration(model), 0f, accelerationValues[vehClass], 0f, 1f);
@@ -347,6 +357,16 @@ private void CreateMenu()
ItemData = new float[4] { topSpeed, acceleration, maxBraking, maxTraction }
};
vehicleClassMenu.AddMenuItem(vehBtn);
+
+ if (WhitelistVehicles.ContainsKey(veh.ToLower()))
+ {
+ if (!SupplementaryPermissionManager.IsAllowed("VW" + veh.ToLower()))
+ {
+ vehBtn.Enabled = false;
+ vehBtn.LeftIcon = MenuItem.Icon.LOCK;
+ vehBtn.Description = "Access to this has been restricted by the server owner.";
+ }
+ }
}
else
{
@@ -359,7 +379,7 @@ private void CreateMenu()
vehicleClassMenu.AddMenuItem(vehBtn);
vehBtn.RightIcon = MenuItem.Icon.LOCK;
}
-
+
// Mark duplicate as true and break from the loop because we already found the duplicate.
duplicate = true;
break;
@@ -378,6 +398,16 @@ private void CreateMenu()
ItemData = new float[4] { topSpeed, acceleration, maxBraking, maxTraction }
};
vehicleClassMenu.AddMenuItem(vehBtn);
+
+ if (WhitelistVehicles.ContainsKey(veh.ToLower()))
+ {
+ if (!SupplementaryPermissionManager.IsAllowed("VW" + veh.ToLower()))
+ {
+ vehBtn.Enabled = false;
+ vehBtn.LeftIcon = MenuItem.Icon.LOCK;
+ vehBtn.Description = "Access to this has been restricted by the server owner.";
+ }
+ }
}
else
{
diff --git a/vMenu/menus/WeaponOptions.cs b/vMenu/menus/WeaponOptions.cs
index bcd1fc68b..bf42e9aa7 100644
--- a/vMenu/menus/WeaponOptions.cs
+++ b/vMenu/menus/WeaponOptions.cs
@@ -23,6 +23,7 @@ public class WeaponOptions
public bool UnlimitedParachutes { get; private set; } = UserDefaults.WeaponsUnlimitedParachutes;
public static Dictionary AddonWeapons = new();
+ public static Dictionary WeaponWhitelist = new();
private Dictionary weaponInfo;
private Dictionary addonWeaponInfo;
@@ -113,6 +114,16 @@ private void CreateMenu()
ItemData = stats
};
+ string spawnName = addonWeapon.SpawnName;
+
+ if (!vMenuShared.SupplementaryPermissionManager.IsAllowed("WW" + spawnName.ToLower().Replace("weapon_", "")))
+ {
+ addonWeaponItem.Enabled = false;
+ addonWeaponItem.LeftIcon = MenuItem.Icon.LOCK;
+ addonWeaponItem.Description = "Access to this has been restricted by the server owner.";
+ }
+
+
addonWeaponInfo.Add(addonWeaponMenu, addonWeapon);
var getOrRemoveWeapon = new MenuItem("Equip/Remove Weapon", "Add or remove this weapon to/form your inventory.")
@@ -363,15 +374,15 @@ void EquipWeaponComponent(uint weaponHash, uint componentHash)
{
if (item == togglePrimary)
{
- if (HasPedGotWeapon(Game.PlayerPed.Handle, (uint)GetHashKey("gadget_parachute"), false))
+ if (HasPedGotWeapon(Game.PlayerPed.Handle, Game.GenerateHashASCII("gadget_parachute"), false))
{
Subtitle.Custom("Primary parachute removed.");
- RemoveWeaponFromPed(Game.PlayerPed.Handle, (uint)GetHashKey("gadget_parachute"));
+ RemoveWeaponFromPed(Game.PlayerPed.Handle, Game.GenerateHashASCII("gadget_parachute"));
}
else
{
Subtitle.Custom("Primary parachute added.");
- GiveWeaponToPed(Game.PlayerPed.Handle, (uint)GetHashKey("gadget_parachute"), 0, false, false);
+ GiveWeaponToPed(Game.PlayerPed.Handle, Game.GenerateHashASCII("gadget_parachute"), 0, false, false);
}
}
else if (item == toggleReserve)
@@ -801,7 +812,7 @@ void EquipWeaponComponent(uint weaponHash, uint componentHash)
SetPedAmmo(Game.PlayerPed.Handle, avw.Hash, ammo);
}
- SetCurrentPedWeapon(Game.PlayerPed.Handle, (uint)GetHashKey("weapon_unarmed"), true);
+ SetCurrentPedWeapon(Game.PlayerPed.Handle, Game.GenerateHashASCII("weapon_unarmed"), true);
}
else if (item == removeAllWeapons)
{
diff --git a/vMenu/vMenuClient.csproj b/vMenu/vMenuClient.csproj
index 939eb3ead..7c974cb9e 100644
--- a/vMenu/vMenuClient.csproj
+++ b/vMenu/vMenuClient.csproj
@@ -11,8 +11,7 @@
false
-
-
+
@@ -51,7 +50,7 @@
-
+
diff --git a/vMenuServer/MainServer.cs b/vMenuServer/MainServer.cs
index 284192509..c6fd8d0a6 100644
--- a/vMenuServer/MainServer.cs
+++ b/vMenuServer/MainServer.cs
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
+using System.IO;
using System.Linq;
using System.Threading.Tasks;
@@ -185,6 +186,7 @@ private int DynamicWeatherMinutes
"XMAS",
"HALLOWEEN"
};
+
#endregion
#region Constructor
@@ -224,10 +226,11 @@ public MainServer()
}));
// check addons file for errors
+ Dictionary> addonsData = new();
var addons = LoadResourceFile(GetCurrentResourceName(), "config/addons.json") ?? "{}";
try
{
- JsonConvert.DeserializeObject>>(addons);
+ addonsData = JsonConvert.DeserializeObject>>(addons);
// If the above crashes, then the json is invalid and it'll throw warnings in the console.
}
catch (JsonReaderException ex)
@@ -235,6 +238,22 @@ public MainServer()
Debug.WriteLine($"\n\n^1[vMenu] [ERROR] ^7Your addons.json file contains a problem! Error details: {ex.Message}\n\n");
}
+ // check model-whitelists file for errors
+ Dictionary> whitelistData = new();
+ var whitelist = LoadResourceFile(GetCurrentResourceName(), "config/model-whitelists.json") ?? "{}";
+ try
+ {
+ whitelistData = JsonConvert.DeserializeObject>>(whitelist);
+ // If the above crashes, then the json is invalid and it'll throw warnings in the console.
+ }
+ catch (JsonReaderException ex)
+ {
+ Debug.WriteLine($"\n\n^1[vMenu] [ERROR] ^7Your model-whitelists.json file contains a problem! Error details: {ex.Message}\n\n");
+ }
+
+ // setup addon permissions
+ SetupAddonPerms(whitelistData, addonsData);
+
// check extras file for errors
string extras = LoadResourceFile(GetCurrentResourceName(), "config/extras.json") ?? "{}";
try
@@ -528,7 +547,7 @@ internal void ServerCommandHandler(int source, List
///
///
+ ///
[EventHandler("vMenu:UpdateServerTime")]
- internal void UpdateTime([FromSource] Player source, int newHours, int newMinutes)
+ internal async void UpdateTime([FromSource] Player source, int newHours, int newMinutes, bool newFreezeTime)
{
- if (!PermissionsManager.IsAllowed(PermissionsManager.Permission.TOSetTime, source) && !PermissionsManager.IsAllowed(PermissionsManager.Permission.TOAll, source))
+ if (!PermissionsManager.IsAllowed(PermissionsManager.Permission.TOSetTime, source))
{
BanManager.BanCheater(source);
return;
}
+ if (GetSettingsBool(Setting.vmenu_smooth_time_transitions))
+ {
+ CurrentHours = CurrentHours;
+ CurrentMinutes = CurrentMinutes;
+ FreezeTime = true;
+ while (newHours != CurrentHours)
+ {
+ if ((CurrentMinutes + 1) > 59)
+ {
+ CurrentMinutes = 0;
+ if ((CurrentHours + 1) > 23)
+ {
+ CurrentHours = 0;
+ }
+ else
+ {
+ CurrentHours++;
+ }
+ }
+ else
+ {
+ CurrentMinutes = CurrentMinutes + 5;
+ }
+ await Delay(0);
+ }
+ FreezeTime = newFreezeTime;
+ }
CurrentHours = newHours;
CurrentMinutes = newMinutes;
}
@@ -811,7 +857,7 @@ internal void UpdateTime([FromSource] Player source, int newHours, int newMinute
[EventHandler("vMenu:FreezeServerTime")]
internal void FreezeServerTime([FromSource] Player source, bool freezeTime)
{
- if (!PermissionsManager.IsAllowed(PermissionsManager.Permission.TOFreezeTime, source) && !PermissionsManager.IsAllowed(PermissionsManager.Permission.TOAll, source))
+ if (!PermissionsManager.IsAllowed(PermissionsManager.Permission.TOFreezeTime, source))
{
BanManager.BanCheater(source);
return;
@@ -831,7 +877,7 @@ internal void FreezeServerTime([FromSource] Player source, bool freezeTime)
[EventHandler("vMenu:KickPlayer")]
internal void KickPlayer([FromSource] Player source, int target, string kickReason = "You have been kicked from the server.")
{
- if (!PermissionsManager.IsAllowed(PermissionsManager.Permission.OPKick, source) && !PermissionsManager.IsAllowed(PermissionsManager.Permission.OPAll, source))
+ if (!PermissionsManager.IsAllowed(PermissionsManager.Permission.OPKick, source))
{
BanManager.BanCheater(source);
return;
@@ -866,7 +912,7 @@ internal void KickPlayer([FromSource] Player source, int target, string kickReas
[EventHandler("vMenu:KillPlayer")]
internal void KillPlayer([FromSource] Player source, int target)
{
- if (!PermissionsManager.IsAllowed(PermissionsManager.Permission.OPKill, source) && !PermissionsManager.IsAllowed(PermissionsManager.Permission.OPAll, source))
+ if (!PermissionsManager.IsAllowed(PermissionsManager.Permission.OPKill, source))
{
BanManager.BanCheater(source);
return;
@@ -890,7 +936,7 @@ internal void KillPlayer([FromSource] Player source, int target)
[EventHandler("vMenu:SummonPlayer")]
internal async void SummonPlayer([FromSource] Player source, int target, int numberOfSeats)
{
- if (!PermissionsManager.IsAllowed(PermissionsManager.Permission.OPSummon, source) && !PermissionsManager.IsAllowed(PermissionsManager.Permission.OPAll, source))
+ if (!PermissionsManager.IsAllowed(PermissionsManager.Permission.OPSummon, source))
{
BanManager.BanCheater(source);
return;
@@ -968,7 +1014,7 @@ internal async void SummonPlayer([FromSource] Player source, int target, int num
[EventHandler("vMenu:SendMessageToPlayer")]
internal void SendPrivateMessage([FromSource] Player source, int target, string message)
{
- if (!PermissionsManager.IsAllowed(PermissionsManager.Permission.OPSendMessage, source) && !PermissionsManager.IsAllowed(PermissionsManager.Permission.OPAll, source))
+ if (!PermissionsManager.IsAllowed(PermissionsManager.Permission.OPSendMessage, source))
{
BanManager.BanCheater(source);
return;
@@ -1002,7 +1048,7 @@ internal void SendPrivateMessage([FromSource] Player source, int target, string
foreach (string playerHandle in joinedPlayers)
{
- if (!PermissionsManager.IsAllowed(PermissionsManager.Permission.OPSeePrivateMessages, playerHandle) && !PermissionsManager.IsAllowed(PermissionsManager.Permission.OPAll, playerHandle))
+ if (!PermissionsManager.IsAllowed(PermissionsManager.Permission.OPSeePrivateMessages, playerHandle))
{
continue;
}
@@ -1044,7 +1090,7 @@ private static void KickLog(string kickLogMesage)
[EventHandler("vMenu:SaveTeleportLocation")]
internal void AddTeleportLocation([FromSource] Player source, string locationJson)
{
- if (!PermissionsManager.IsAllowed(PermissionsManager.Permission.MSTeleportSaveLocation, source) && !PermissionsManager.IsAllowed(PermissionsManager.Permission.MSAll, source))
+ if (!PermissionsManager.IsAllowed(PermissionsManager.Permission.MSTeleportSaveLocation, source))
{
BanManager.BanCheater(source);
return;
@@ -1094,7 +1140,7 @@ internal void GetPlayerCoords([FromSource] Player source, long rpcId, int player
{
var coords = Vector3.Zero;
- if (PermissionsManager.IsAllowed(PermissionsManager.Permission.OPTeleport, source) || PermissionsManager.IsAllowed(PermissionsManager.Permission.OPAll, source))
+ if (PermissionsManager.IsAllowed(PermissionsManager.Permission.OPTeleport, source))
{
Player targetPlayer = GetPlayerFromServerId(playerId);
@@ -1122,7 +1168,7 @@ private IEnumerable GetJoinQuitNotifPlayers()
foreach (string playerHandle in joinedPlayers)
{
- if (!PermissionsManager.IsAllowed(PermissionsManager.Permission.MSJoinQuitNotifs, playerHandle) && !PermissionsManager.IsAllowed(PermissionsManager.Permission.MSAll, playerHandle))
+ if (!PermissionsManager.IsAllowed(PermissionsManager.Permission.MSJoinQuitNotifs, playerHandle))
{
continue;
}
@@ -1150,6 +1196,7 @@ private async Task PlayersFirstTick()
joinedPlayers.Add(player.Handle);
PermissionsManager.SetPermissionsForPlayer(player);
+ SupplementaryPermissionManager.SetPermissionsForPlayer(player);
}
}
@@ -1159,6 +1206,7 @@ internal void OnPlayerJoining([FromSource] Player sourcePlayer)
joinedPlayers.Add(sourcePlayer.Handle);
PermissionsManager.SetPermissionsForPlayer(sourcePlayer);
+ SupplementaryPermissionManager.SetPermissionsForPlayer(sourcePlayer);
string sourcePlayerName = sourcePlayer.Name;
@@ -1188,6 +1236,52 @@ internal void OnPlayerDropped([FromSource] Player sourcePlayer, string reason)
#endregion
#region Utilities
+ private void SetupAddonPerms(Dictionary> whitelists, Dictionary> addons)
+ {
+ if (whitelists.ContainsKey("whitelistedweapons"))
+ {
+ foreach (var whitelist in whitelists["whitelistedweapons"])
+ {
+ if (!SupplementaryPermissionManager.Permission.Contains("WW" + whitelist.ToLower().Replace("weapon_", "")))
+ {
+ SupplementaryPermissionManager.Permission.Add("WW" + whitelist.ToLower().Replace("weapon_", ""));
+ }
+ }
+ }
+ if (whitelists.ContainsKey("whitelistedvehicle"))
+ {
+ foreach (var whitelist in whitelists["whitelistedvehicle"])
+ {
+ if (!SupplementaryPermissionManager.Permission.Contains("VW" + whitelist.ToLower()))
+ {
+ SupplementaryPermissionManager.Permission.Add("VW" + whitelist.ToLower());
+ }
+ }
+ }
+ if (whitelists.ContainsKey("whitelistedpeds"))
+ {
+ foreach (var whitelist in whitelists["whitelistedpeds"])
+ {
+ if (!SupplementaryPermissionManager.Permission.Contains("PW" + whitelist.ToLower()))
+ {
+ SupplementaryPermissionManager.Permission.Add("PW" + whitelist.ToLower());
+ }
+ }
+ }
+
+ List supplementaryPermissions = [
+ "#################################################################",
+ "# THIS IS A TEMPLATE FILE. #",
+ "# DO NOT EDIT, MAKE A COPY AND EDIT THE COPY. #",
+ "#################################################################",
+ ];
+ foreach (string permission in SupplementaryPermissionManager.Permission)
+ {
+ supplementaryPermissions.Add("add_ace builtin.everyone \"" + SupplementaryPermissionManager.GetAceName(permission) + "\" allow");
+ }
+ Directory.CreateDirectory(Path.Combine(GetResourcePath(GetCurrentResourceName()), "config", "templates"));
+ File.WriteAllLines(Path.Combine(GetResourcePath(GetCurrentResourceName()), "config", "templates", "SupplementaryPermissionTemplate.cfg"), supplementaryPermissions.ToArray());
+ }
private Player GetPlayerFromServerId(string serverId)
{
if (!int.TryParse(serverId, out int serverIdInt))
diff --git a/vMenuServer/config/model-whitelists.json b/vMenuServer/config/model-whitelists.json
new file mode 100644
index 000000000..ca3801e98
--- /dev/null
+++ b/vMenuServer/config/model-whitelists.json
@@ -0,0 +1,23 @@
+{
+ // Any model you list here will have a permission generated that you can use
+ // inside the permissions.cfg file to give people access to use that model.
+ //
+ // After entering the model names here and starting vMenu, the
+ // config/templates/SupplementaryPermissionTemplate.cfg file will be updated to show you which
+ // permissions are available, and provides an example usage.
+ //
+ // Copy those entries to your permissions.cfg to start configuring them
+ // for the groups that you want.
+ "whitelistedvehicle": [
+ "whitelistedvehiclename1",
+ "whitelistedvehiclename2"
+ ],
+ "whitelistedpeds": [
+ "whitelistedpedname1",
+ "whitelistedpedname2"
+ ],
+ "whitelistedweapons": [
+ "whitelistedweaponname1",
+ "whitelistedweaponname2"
+ ]
+}
\ No newline at end of file
diff --git a/vMenuServer/config/permissions.cfg b/vMenuServer/config/permissions.cfg
index 6c6df4a66..aebb7ec73 100644
--- a/vMenuServer/config/permissions.cfg
+++ b/vMenuServer/config/permissions.cfg
@@ -114,6 +114,8 @@ setr vmenu_enable_snow false
setr vmenu_enable_time_sync true
# Set this to true if you want time to be frozen by default.
setr vmenu_freeze_time false
+# Enables smooth time transitions.
+setr vmenu_smooth_time_transitions true
# This setting determines how long one in-game minute lasts in real time.
# By default, one GTA V minute, takes 2 seconds (2000 miliseconds).
# The value here is measured in miliseconds, and must be a positive number at least greater than 100.
@@ -136,6 +138,11 @@ setr vmenu_prevent_extras_when_damaged false
setr vmenu_allowed_engine_damage_for_extra_change 800
# This is the amount of body damage before the extras will be blocked. Value must be between 0 and 1000 (inclusive)
setr vmenu_allowed_body_damage_for_extra_change 800
+# This setting adjusts the vehicle spawning ratelimit in seconds. Value must be between 0 and infinity
+setr vmenu_vehicle_spawn_rate_limit 5
+# This setting adjusts the "vMenu:DV" command delete distance.
+setr vmenu_delete_vehicle_distance 5.0
+
### MP Ped options ###
# Setting this to true will enable a 3D ped preview when viewing saved MP Peds.
@@ -296,6 +303,7 @@ add_ace builtin.everyone "vMenu.VehicleOptions.All" allow
####################################
add_ace builtin.everyone "vMenu.VehicleSpawner.Menu" allow
add_ace builtin.everyone "vMenu.VehicleSpawner.All" allow
+#add_ace builtin.everyone "vMenu.VehicleSpawner.BypassRateLimit" 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.
diff --git a/vMenuServer/vMenuServer.csproj b/vMenuServer/vMenuServer.csproj
index 5959069d3..a596abcc8 100644
--- a/vMenuServer/vMenuServer.csproj
+++ b/vMenuServer/vMenuServer.csproj
@@ -14,8 +14,7 @@
x64
-
-
+
@@ -35,9 +34,6 @@
Always
-
- Always
-
From 6ff052132193c20efed9f6697658f9ea6d632b1b Mon Sep 17 00:00:00 2001
From: Tom <31419184+TomGrobbe@users.noreply.github.com>
Date: Fri, 5 Jun 2026 15:47:10 +0200
Subject: [PATCH 11/11] fix(ci): truncate Discord commit field to avoid 400 on
long messages
---
.github/workflows/build.yml | 11 ++++++++++-
1 file changed, 10 insertions(+), 1 deletion(-)
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 111014725..771273930 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -246,6 +246,7 @@ jobs:
shell: bash
env:
DISCORD_WEBHOOK_URL: ${{ secrets.DISCORD_WEBHOOK_URL }}
+ COMMIT_MSG: ${{ github.event.head_commit.message }}
run: |
if [ -z "$DISCORD_WEBHOOK_URL" ]; then
echo "DISCORD_WEBHOOK_URL not configured, skipping."
@@ -283,6 +284,14 @@ jobs:
SHA="${{ github.sha }}"
SHORT_SHA="${SHA:0:7}"
+ # Discord caps embed field values at 1024 chars. Use only the commit
+ # subject (first line) and truncate it so the payload is never rejected
+ # with HTTP 400 on long, multi-line (e.g. squash-merged) messages.
+ COMMIT_SUBJECT=$(printf '%s' "$COMMIT_MSG" | head -n 1)
+ if [ "${#COMMIT_SUBJECT}" -gt 256 ]; then
+ COMMIT_SUBJECT="${COMMIT_SUBJECT:0:253}..."
+ fi
+
PAYLOAD=$(jq -n \
--arg title "vMenu (v${DISPLAY_VERSION})" \
--arg desc "$STATUS_TEXT" \
@@ -294,7 +303,7 @@ jobs:
--arg link_url "$LINK_URL" \
--arg short_sha "$SHORT_SHA" \
--arg commit_url "https://github.com/${{ github.repository }}/commit/${{ github.sha }}" \
- --arg commit_msg "${{ github.event.head_commit.message }}" \
+ --arg commit_msg "$COMMIT_SUBJECT" \
'{
embeds: [{
title: $title,