diff --git a/.gitignore b/.gitignore
index fdc8383c..61d26fcf 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
diff --git a/SharedClasses/ConfigManager.cs b/SharedClasses/ConfigManager.cs
index c8b07fe7..5f212c2d 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 f3240bc6..d5826c73 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 00000000..8ef9944c
--- /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 3dc95ebd..cbf2e802 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 0401f9b7..c3719ab8 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 3a57dafc..682c6825 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 12dbc85e..a584fc02 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
///
///
+ ///
[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 00000000..ca3801e9
--- /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 6c6df4a6..aebb7ec7 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 5959069d..a596abcc 100644
--- a/vMenuServer/vMenuServer.csproj
+++ b/vMenuServer/vMenuServer.csproj
@@ -14,8 +14,7 @@
x64
-
-
+
@@ -35,9 +34,6 @@
Always
-
- Always
-