C#:
using Newtonsoft.Json;
using Oxide.Core;
using Oxide.Game.Rust.Cui;
using Rust;
using Rust.Modular;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using UnityEngine;
using UnityEngine.UI;
namespace Oxide.Plugins
{
[Info("Virtual Car Garage", "DaBoiJason", "1.0.0")]
[Description("Stores modular cars virtually and lets players load them from a polished car-lift garage UI.")]
public class VirtualCarGarage : RustPlugin
{
#region References / constants
private const string PermissionUse = "virtualcargarage.use";
private const string DataFileName = "VirtualCarGarage";
private const string LiftUi = "VirtualCarGarage.Lift";
private const string GarageUi = "VirtualCarGarage.Garage";
private const string ModalUi = "VirtualCarGarage.Modal";
private const string ChassisPrefab2 =
"assets/content/vehicles/modularcar/car_chassis_2module.entity.prefab";
private const string ChassisPrefab3 =
"assets/content/vehicles/modularcar/car_chassis_3module.entity.prefab";
private const string ChassisPrefab4 =
"assets/content/vehicles/modularcar/car_chassis_4module.entity.prefab";
private readonly Dictionary<ulong, ModularCarGarage> _activeLifts =
new Dictionary<ulong, ModularCarGarage>();
private readonly HashSet<ulong> _busyPlayers =
new HashSet<ulong>();
private StoredData _data;
private Configuration _config;
#endregion
#region Oxide hooks
private void Init()
{
permission.RegisterPermission(PermissionUse, this);
string lockBypassPermission =
GetLockBypassPermission();
if (!string.IsNullOrWhiteSpace(
lockBypassPermission))
{
permission.RegisterPermission(
lockBypassPermission,
this
);
}
if (_config.PermissionVehicleLimits != null)
{
foreach (KeyValuePair<string, int> entry in _config.PermissionVehicleLimits)
{
if (!string.IsNullOrWhiteSpace(entry.Key))
permission.RegisterPermission(entry.Key, this);
}
}
LoadData();
}
private void OnServerInitialized()
{
foreach (BasePlayer player in BasePlayer.activePlayerList)
{
DestroyAllUi(player);
}
}
private void Unload()
{
foreach (BasePlayer player in BasePlayer.activePlayerList)
{
DestroyAllUi(player);
}
SaveData();
}
private void OnServerSave()
{
SaveData();
}
private void OnLootEntity(
BasePlayer player,
ModularCarGarage lift)
{
if (player == null || lift == null)
return;
if (!HasPermission(player))
return;
_activeLifts[player.userID] = lift;
NextTick(delegate
{
if (player == null || !player.IsConnected)
return;
ModularCarGarage currentLift;
if (!_activeLifts.TryGetValue(
player.userID,
out currentLift) ||
currentLift != lift)
{
return;
}
ShowLiftUi(player, lift);
});
}
private void OnPlayerLootEnd(
PlayerLoot loot)
{
if (loot == null)
return;
BasePlayer player =
loot.baseEntity as BasePlayer;
if (player == null)
return;
_activeLifts.Remove(player.userID);
DestroyAllUi(player);
}
private void OnPlayerDisconnected(
BasePlayer player,
string reason)
{
if (player == null)
return;
_activeLifts.Remove(player.userID);
_busyPlayers.Remove(player.userID);
}
#endregion
#region Commands - lift / garage
// Saves the car then removes the real one from the lift.
[ConsoleCommand("virtualcar.save")]
private void CmdSave(
ConsoleSystem.Arg arg)
{
BasePlayer player = arg.Player();
if (!ValidatePlayer(player))
return;
ModularCarGarage lift;
if (!TryGetActiveLift(
player,
out lift))
{
return;
}
if (_busyPlayers.Contains(
player.userID))
{
return;
}
ModularCar car =
lift.carOccupant;
if (car == null)
{
Reply(
player,
"There is no modular car on this lift."
);
return;
}
// Do not let somebody garage a locked car they cannot use.
if (!CanPlayerStoreLockedVehicle(
player,
car))
{
return;
}
PlayerGarage garage =
GetGarage(player.userID);
int vehicleLimit =
GetVehicleLimit(player);
if (garage.Vehicles.Count >=
vehicleLimit)
{
Reply(
player,
string.Format(
"Your virtual garage is full ({0}/{1}).",
garage.Vehicles.Count,
vehicleLimit
)
);
return;
}
if (!ValidateVehicleContentsForSave(
player,
car))
{
return;
}
_busyPlayers.Add(
player.userID
);
try
{
VirtualCar snapshot =
CaptureVehicle(
player,
car,
garage
);
// Save it before removing the real car.
garage.Vehicles.Add(
snapshot
);
SaveData();
// Clear it first so stuff cannot dupe.
StripPhysicalCar(car);
if (lift.carOccupant == car)
{
lift.ReleaseOccupant();
}
car.Kill();
Reply(
player,
string.Format(
"Stored <color=#F3A84B>{0}</color> in your virtual garage.",
snapshot.Name
)
);
RefreshLiftUi(player);
}
catch (Exception ex)
{
PrintError(
string.Format(
"Failed to save virtual car for {0} ({1}): {2}",
player.displayName,
player.userID,
ex
)
);
Reply(
player,
"The car could not be stored. Check the server console."
);
}
finally
{
_busyPlayers.Remove(
player.userID
);
}
}
[ConsoleCommand("virtualcar.garage")]
private void CmdGarage(
ConsoleSystem.Arg arg)
{
BasePlayer player = arg.Player();
if (!ValidatePlayer(player))
return;
ModularCarGarage lift;
if (!TryGetActiveLift(
player,
out lift))
{
return;
}
OpenGarageUi(player);
}
[ConsoleCommand("virtualcar.garage.close")]
private void CmdGarageClose(
ConsoleSystem.Arg arg)
{
BasePlayer player = arg.Player();
if (player == null)
return;
CuiHelper.DestroyUi(
player,
GarageUi
);
CuiHelper.DestroyUi(
player,
ModalUi
);
}
// Loads the selected car back onto the lift.
[ConsoleCommand("virtualcar.load")]
private void CmdLoad(
ConsoleSystem.Arg arg)
{
BasePlayer player = arg.Player();
if (!ValidatePlayer(player))
return;
if (arg.Args == null ||
arg.Args.Length < 1)
{
return;
}
if (_busyPlayers.Contains(
player.userID))
{
return;
}
ModularCarGarage lift;
if (!TryGetActiveLift(
player,
out lift))
{
return;
}
if (lift.carOccupant != null)
{
Reply(
player,
"The Modular Car Lift must be empty before loading a stored vehicle."
);
return;
}
string vehicleId =
arg.Args[0].ToString();
PlayerGarage garage =
GetGarage(player.userID);
VirtualCar stored =
garage.Vehicles.FirstOrDefault(
x => string.Equals(
x.Id,
vehicleId,
StringComparison.OrdinalIgnoreCase
)
);
if (stored == null)
{
Reply(
player,
"That stored vehicle no longer exists."
);
OpenGarageUi(player);
return;
}
_busyPlayers.Add(
player.userID
);
// Remove the saved one first so we do not end up with 2 cars.
garage.Vehicles.Remove(stored);
SaveData();
ModularCar spawnedCar = null;
try
{
spawnedCar =
SpawnStoredVehicleSkeleton(
player,
lift,
stored
);
if (spawnedCar == null)
{
throw new Exception(
"CreateEntity returned null."
);
}
CuiHelper.DestroyUi(
player,
GarageUi
);
CuiHelper.DestroyUi(
player,
ModalUi
);
// Storage boxes are sometimes not ready straight away.
TryFinalizeLoadedVehicle(
player.userID,
spawnedCar,
stored,
0
);
}
catch (Exception ex)
{
RollbackFailedLoad(
player.userID,
spawnedCar,
stored,
ex
);
}
}
// Waits a bit for the engine and storage boxes to be ready.
private void TryFinalizeLoadedVehicle(
ulong userId,
ModularCar car,
VirtualCar stored,
int attempt)
{
if (car == null ||
car.IsDestroyed)
{
RollbackFailedLoad(
userId,
car,
stored,
new Exception(
"Vehicle was destroyed before restoration completed."
)
);
return;
}
if (!AreStoredVehicleContainersReady(
car,
stored))
{
if (attempt <
_config.LoadRestoreMaximumRetries)
{
timer.Once(
_config
.LoadRestoreRetryDelaySeconds,
() =>
{
TryFinalizeLoadedVehicle(
userId,
car,
stored,
attempt + 1
);
}
);
return;
}
RollbackFailedLoad(
userId,
car,
stored,
new Exception(
"Timed out waiting for modular-car engine/storage containers to initialize."
)
);
return;
}
try
{
RestoreVehicleState(
car,
stored
);
// Put the built-in car lock back after all modules are ready.
RestoreVehicleLock(
car,
stored
);
car.SendNetworkUpdateImmediate();
BasePlayer player =
BasePlayer.FindByID(
userId
);
if (player != null &&
player.IsConnected)
{
Reply(
player,
string.Format(
"Loaded <color=#7CD37C>{0}</color> onto the lift.",
stored.Name
)
);
RefreshLiftUi(player);
}
_busyPlayers.Remove(
userId
);
}
catch (Exception ex)
{
RollbackFailedLoad(
userId,
car,
stored,
ex
);
}
}
// Checks if all the boxes we need are ready yet.
private bool AreStoredVehicleContainersReady(
ModularCar car,
VirtualCar stored)
{
if (car == null ||
stored == null ||
stored.Modules == null)
{
return false;
}
foreach (
VirtualModule savedModule
in stored.Modules)
{
BaseVehicleModule module;
if (!car.TryGetModuleAt(
savedModule.Socket,
out module) ||
module == null ||
module.FirstSocketIndex !=
savedModule.Socket)
{
return false;
}
VehicleModuleEngine engineModule =
module as VehicleModuleEngine;
if (_config
.SaveAndRestoreEngineParts &&
engineModule != null &&
savedModule.EngineParts != null &&
savedModule.EngineParts.Count > 0)
{
EngineStorage engineStorage =
engineModule
.GetContainer()
as EngineStorage;
if (engineStorage == null ||
engineStorage.inventory == null)
{
return false;
}
}
if (_config
.SaveModuleStorageContents &&
savedModule
.StorageContainers != null &&
savedModule
.StorageContainers.Count > 0)
{
List<StorageContainer>
liveContainers =
GetModuleStorageContainers(
module,
engineModule
);
if (liveContainers.Count <
savedModule
.StorageContainers.Count)
{
return false;
}
}
}
return true;
}
// Load failed, remove the bad car and put the save back.
private void RollbackFailedLoad(
ulong userId,
ModularCar spawnedCar,
VirtualCar stored,
Exception ex)
{
PrintError(
string.Format(
"Failed to load virtual car {0} for {1}: {2}",
stored != null
? stored.Id
: "unknown",
userId,
ex
)
);
if (spawnedCar != null &&
!spawnedCar.IsDestroyed)
{
try
{
StripPhysicalCar(
spawnedCar
);
spawnedCar.Kill();
}
catch
{
// Just clean up what we can.
}
}
if (stored != null)
{
PlayerGarage garage =
GetGarage(userId);
bool alreadyRestored =
garage.Vehicles.Any(
x => string.Equals(
x.Id,
stored.Id,
StringComparison
.OrdinalIgnoreCase
)
);
if (!alreadyRestored)
{
garage.Vehicles.Add(
stored
);
SortGarage(garage);
SaveData();
}
}
_busyPlayers.Remove(userId);
BasePlayer player =
BasePlayer.FindByID(userId);
if (player != null &&
player.IsConnected)
{
Reply(
player,
"The vehicle could not be fully restored. Its virtual copy was returned to your garage."
);
OpenGarageUi(player);
RefreshLiftUi(player);
}
}
#endregion
#region Commands - rename / delete
[ConsoleCommand("virtualcar.rename.ask")]
private void CmdRenameAsk(
ConsoleSystem.Arg arg)
{
BasePlayer player = arg.Player();
if (!ValidatePlayer(player))
return;
if (arg.Args == null ||
arg.Args.Length < 1)
{
return;
}
VirtualCar car =
FindVehicle(
player.userID,
arg.Args[0].ToString()
);
if (car == null)
return;
ShowRenameModal(
player,
car
);
}
[ConsoleCommand("virtualcar.rename.submit")]
private void CmdRenameSubmit(
ConsoleSystem.Arg arg)
{
BasePlayer player = arg.Player();
if (!ValidatePlayer(player))
return;
if (arg.Args == null ||
arg.Args.Length < 2)
{
Reply(
player,
"Enter a vehicle name."
);
return;
}
string vehicleId =
arg.Args[0].ToString();
string newName =
string.Join(
" ",
arg.Args
.Skip(1)
.Select(
x => x.ToString()
)
.ToArray()
);
newName =
SanitizeVehicleName(
newName
);
if (string.IsNullOrEmpty(
newName))
{
Reply(
player,
"Vehicle name cannot be empty."
);
return;
}
VirtualCar car =
FindVehicle(
player.userID,
vehicleId
);
if (car == null)
return;
car.Name = newName;
SaveData();
CuiHelper.DestroyUi(
player,
ModalUi
);
OpenGarageUi(player);
}
[ConsoleCommand("virtualcar.delete.ask")]
private void CmdDeleteAsk(
ConsoleSystem.Arg arg)
{
BasePlayer player = arg.Player();
if (!ValidatePlayer(player))
return;
if (arg.Args == null ||
arg.Args.Length < 1)
{
return;
}
VirtualCar car =
FindVehicle(
player.userID,
arg.Args[0].ToString()
);
if (car == null)
return;
ShowDeleteModal(
player,
car
);
}
[ConsoleCommand("virtualcar.delete.confirm")]
private void CmdDeleteConfirm(
ConsoleSystem.Arg arg)
{
BasePlayer player = arg.Player();
if (!ValidatePlayer(player))
return;
if (arg.Args == null ||
arg.Args.Length < 1)
{
return;
}
PlayerGarage garage =
GetGarage(
player.userID
);
string vehicleId =
arg.Args[0].ToString();
VirtualCar car =
garage.Vehicles
.FirstOrDefault(
x => string.Equals(
x.Id,
vehicleId,
StringComparison
.OrdinalIgnoreCase
)
);
if (car == null)
return;
garage.Vehicles.Remove(car);
SaveData();
CuiHelper.DestroyUi(
player,
ModalUi
);
Reply(
player,
string.Format(
"Deleted <color=#E77878>{0}</color> from the virtual garage.",
car.Name
)
);
OpenGarageUi(player);
RefreshLiftUi(player);
}
[ConsoleCommand("virtualcar.modal.close")]
private void CmdModalClose(
ConsoleSystem.Arg arg)
{
BasePlayer player = arg.Player();
if (player == null)
return;
CuiHelper.DestroyUi(
player,
ModalUi
);
}
#endregion
#region Capture / restore
// Checks storage and engine rules before the car can be saved.
private bool ValidateVehicleContentsForSave(
BasePlayer player,
ModularCar car)
{
if (car == null)
return false;
HashSet<string> blacklist =
new HashSet<string>(
(
_config.StorageItemBlacklist ??
new List<string>()
)
.Where(
x =>
!string.IsNullOrWhiteSpace(
x
)
)
.Select(
x => x.Trim()
),
StringComparer.OrdinalIgnoreCase
);
for (
int socketIndex = 0;
socketIndex < car.TotalSockets;
socketIndex++)
{
BaseVehicleModule module;
if (!car.TryGetModuleAt(
socketIndex,
out module) ||
module == null ||
module.FirstSocketIndex !=
socketIndex)
{
continue;
}
VehicleModuleEngine engineModule =
module as VehicleModuleEngine;
// Check engine parts.
if (!_config
.SaveAndRestoreEngineParts &&
_config
.RequireEngineEmptyWhenEnginePartsAreNotSaved &&
engineModule != null)
{
EngineStorage engineStorage =
engineModule
.GetContainer()
as EngineStorage;
if (engineStorage != null &&
ContainerHasItems(
engineStorage.inventory
))
{
Reply(
player,
"Remove all engine parts before storing this vehicle."
);
return false;
}
}
// Check storage and blocked items.
List<StorageContainer>
storageContainers =
GetModuleStorageContainers(
module,
engineModule
);
foreach (
StorageContainer container
in storageContainers)
{
if (container == null ||
container.inventory == null)
{
continue;
}
if (_config
.RequireModuleStorageEmptyBeforeSave &&
ContainerHasItems(
container.inventory
))
{
Reply(
player,
"Empty all vehicle module storage before storing this vehicle."
);
return false;
}
if (blacklist.Count > 0)
{
string blockedShortname;
if (TryFindBlacklistedItem(
container.inventory,
blacklist,
out blockedShortname))
{
Reply(
player,
string.Format(
"This vehicle cannot be stored while its storage contains blacklisted item: <color=#E77878>{0}</color>.",
blockedShortname
)
);
return false;
}
}
}
}
return true;
}
private bool ContainerHasItems(
ItemContainer container)
{
return
container != null &&
container.itemList != null &&
container.itemList.Count > 0;
}
private bool TryFindBlacklistedItem(
ItemContainer container,
HashSet<string> blacklist,
out string shortname)
{
shortname = null;
if (container == null ||
container.itemList == null)
{
return false;
}
foreach (
Item item
in container.itemList)
{
if (item == null ||
item.info == null)
{
continue;
}
if (blacklist.Contains(
item.info.shortname))
{
shortname =
item.info.shortname;
return true;
}
if (item.contents != null &&
TryFindBlacklistedItem(
item.contents,
blacklist,
out shortname))
{
return true;
}
}
return false;
}
// Checks the built-in car lock before SAVE.
private bool CanPlayerStoreLockedVehicle(
BasePlayer player,
ModularCar car)
{
if (player == null ||
car == null ||
car.CarLock == null ||
!car.CarLock.HasALock)
{
return true;
}
if (_config.AllowSavingLockedCarsWithoutAuthorization)
return true;
string bypassPermission =
GetLockBypassPermission();
if (!string.IsNullOrWhiteSpace(
bypassPermission) &&
permission.UserHasPermission(
player.UserIDString,
bypassPermission))
{
return true;
}
bool? directAccess =
TryCheckCarLockAccess(
car.CarLock,
player
);
if (directAccess == true)
return true;
ulong lockOwnerId =
GetCarLockOwnerId(
car.CarLock
);
if (lockOwnerId != 0 &&
lockOwnerId == player.userID)
{
return true;
}
List<ulong> authorizedPlayers =
GetCarLockAuthorizedPlayers(
car.CarLock
);
if (authorizedPlayers.Contains(
player.userID))
{
return true;
}
Reply(
player,
"You are not authorized to this vehicle lock."
);
return false;
}
private string GetLockBypassPermission()
{
if (_config == null ||
string.IsNullOrWhiteSpace(
_config.LockAuthorizationBypassPermission))
{
return
"virtualcargarage.bypasslockauth";
}
return
_config
.LockAuthorizationBypassPermission
.Trim();
}
// Saves the built-in modular car code lock.
private VirtualCarLock CaptureVehicleLock(
ModularCar car)
{
if (!_config.SaveAndRestoreVehicleLock ||
car == null ||
car.CarLock == null ||
!car.CarLock.HasALock)
{
return null;
}
VirtualCarLock saved =
new VirtualCarLock();
saved.HasLock = true;
saved.Code =
GetCarLockCode(
car.CarLock
);
saved.OwnerId =
GetCarLockOwnerId(
car.CarLock
);
saved.AuthorizedPlayers =
GetCarLockAuthorizedPlayers(
car.CarLock
);
if (saved.OwnerId != 0 &&
!saved.AuthorizedPlayers.Contains(
saved.OwnerId))
{
saved.AuthorizedPlayers.Add(
saved.OwnerId
);
}
if (string.IsNullOrEmpty(
saved.Code))
{
throw new Exception(
"Could not read the modular car lock code. The car was not stored so the lock is not lost."
);
}
return saved;
}
// Adds the saved built-in lock back onto the loaded car.
private void RestoreVehicleLock(
ModularCar car,
VirtualCar stored)
{
if (!_config.SaveAndRestoreVehicleLock ||
car == null ||
stored == null ||
stored.Lock == null ||
!stored.Lock.HasLock)
{
return;
}
if (string.IsNullOrEmpty(
stored.Lock.Code))
{
throw new Exception(
"Saved vehicle has a lock but no saved lock code."
);
}
if (car.CarLock == null)
{
throw new Exception(
"Loaded vehicle does not have a CarLock component."
);
}
if (car.CarLock.HasALock)
{
car.CarLock.RemoveLock();
}
ulong lockOwnerId =
stored.Lock.OwnerId;
if (lockOwnerId == 0)
{
lockOwnerId =
stored.OriginalOwnerId;
}
if (lockOwnerId == 0 &&
stored.Lock.AuthorizedPlayers != null &&
stored.Lock.AuthorizedPlayers.Count > 0)
{
lockOwnerId =
stored.Lock.AuthorizedPlayers[0];
}
if (!car.CarLock.TryAddALock(
stored.Lock.Code,
lockOwnerId))
{
throw new Exception(
"Could not put the saved vehicle lock back onto the car."
);
}
List<ulong> players =
stored.Lock.AuthorizedPlayers ??
new List<ulong>();
bool authRestored =
RestoreCarLockAuthorizedPlayers(
car.CarLock,
players
);
int extraAuthCount =
players.Count(
x => x != 0 &&
x != lockOwnerId
);
if (!authRestored &&
extraAuthCount > 0)
{
throw new Exception(
"Vehicle lock was added but its authorized player list could not be restored."
);
}
car.SendNetworkUpdateImmediate();
}
private bool? TryCheckCarLockAccess(
object carLock,
BasePlayer player)
{
if (carLock == null ||
player == null)
{
return null;
}
string[] methodNames =
{
"HasAccess",
"HasPlayerAuth",
"IsPlayerAuthorized",
"IsAuthorized",
"CanAccess",
"HasLockPermission",
"GetPlayerLockPermission"
};
Type type =
carLock.GetType();
foreach (string methodName in methodNames)
{
MethodInfo[] methods =
type.GetMethods(
BindingFlags.Instance |
BindingFlags.Public |
BindingFlags.NonPublic
);
foreach (MethodInfo method in methods)
{
if (!string.Equals(
method.Name,
methodName,
StringComparison.OrdinalIgnoreCase) ||
method.ReturnType != typeof(bool))
{
continue;
}
ParameterInfo[] parameters =
method.GetParameters();
if (parameters.Length != 1)
continue;
try
{
object argument;
if (parameters[0]
.ParameterType
.IsAssignableFrom(
typeof(BasePlayer)))
{
argument = player;
}
else if (parameters[0].ParameterType ==
typeof(ulong))
{
argument = player.userID;
}
else
{
continue;
}
return (bool)method.Invoke(
carLock,
new[] { argument }
);
}
catch
{
// Try the saved auth list below instead.
}
}
}
return null;
}
private string GetCarLockCode(
object carLock)
{
object value =
GetCarLockMemberValue(
carLock,
typeof(string),
new[]
{
"code",
"Code",
"lockCode",
"LockCode"
},
new[]
{
"code"
}
);
return value as string;
}
private ulong GetCarLockOwnerId(
object carLock)
{
object value =
GetCarLockMemberValue(
carLock,
typeof(ulong),
new[]
{
"ownerID",
"OwnerID",
"ownerId",
"OwnerId",
"lockOwnerID",
"LockOwnerID",
"lockOwnerId",
"LockOwnerId"
},
new[]
{
"owner"
}
);
return value is ulong
? (ulong)value
: 0UL;
}
private List<ulong> GetCarLockAuthorizedPlayers(
object carLock)
{
List<ulong> result =
new List<ulong>();
if (carLock == null)
return result;
Type type =
carLock.GetType();
BindingFlags flags =
BindingFlags.Instance |
BindingFlags.Public |
BindingFlags.NonPublic;
foreach (FieldInfo field in
type.GetFields(flags))
{
if (!IsLikelyAuthMemberName(
field.Name))
{
continue;
}
AddUlongValues(
result,
field.GetValue(carLock)
);
}
foreach (PropertyInfo property in
type.GetProperties(flags))
{
if (!property.CanRead ||
property.GetIndexParameters().Length > 0 ||
!IsLikelyAuthMemberName(
property.Name))
{
continue;
}
try
{
AddUlongValues(
result,
property.GetValue(
carLock,
null
)
);
}
catch
{
// Ignore members that cannot be read safely.
}
}
return result
.Where(x => x != 0)
.Distinct()
.ToList();
}
private bool RestoreCarLockAuthorizedPlayers(
object carLock,
List<ulong> players)
{
if (carLock == null ||
players == null ||
players.Count == 0)
{
return true;
}
Type type =
carLock.GetType();
BindingFlags flags =
BindingFlags.Instance |
BindingFlags.Public |
BindingFlags.NonPublic;
bool foundCollection = false;
foreach (FieldInfo field in
type.GetFields(flags))
{
if (!IsLikelyAuthMemberName(
field.Name))
{
continue;
}
object value =
field.GetValue(carLock);
if (AddPlayersToCollection(
value,
players))
{
foundCollection = true;
}
}
foreach (PropertyInfo property in
type.GetProperties(flags))
{
if (!property.CanRead ||
property.GetIndexParameters().Length > 0 ||
!IsLikelyAuthMemberName(
property.Name))
{
continue;
}
try
{
object value =
property.GetValue(
carLock,
null
);
if (AddPlayersToCollection(
value,
players))
{
foundCollection = true;
}
}
catch
{
// Skip anything that cannot be changed.
}
}
return foundCollection;
}
private bool AddPlayersToCollection(
object collection,
List<ulong> players)
{
ICollection<ulong> genericCollection =
collection as ICollection<ulong>;
if (genericCollection != null)
{
foreach (ulong userId in players)
{
if (userId != 0 &&
!genericCollection.Contains(
userId))
{
genericCollection.Add(
userId
);
}
}
return true;
}
if (collection == null)
return false;
MethodInfo addMethod =
collection
.GetType()
.GetMethod(
"Add",
new[] { typeof(ulong) }
);
if (addMethod == null)
return false;
foreach (ulong userId in players)
{
if (userId == 0)
continue;
try
{
addMethod.Invoke(
collection,
new object[] { userId }
);
}
catch
{
return false;
}
}
return true;
}
private void AddUlongValues(
List<ulong> target,
object value)
{
if (target == null ||
value == null ||
value is string)
{
return;
}
IEnumerable enumerable =
value as IEnumerable;
if (enumerable == null)
return;
foreach (object item in enumerable)
{
if (item is ulong)
{
target.Add((ulong)item);
}
}
}
private bool IsLikelyAuthMemberName(
string name)
{
if (string.IsNullOrEmpty(name))
return false;
string lower =
name.ToLowerInvariant();
if (lower.Contains("denied") ||
lower.Contains("blocked") ||
lower.Contains("failed") ||
lower.Contains("attempt"))
{
return false;
}
return
lower.Contains("auth") ||
lower.Contains("whitelist") ||
lower.Contains("player") ||
lower.Contains("user");
}
private object GetCarLockMemberValue(
object instance,
Type wantedType,
string[] exactNames,
string[] nameParts)
{
if (instance == null)
return null;
Type type =
instance.GetType();
BindingFlags flags =
BindingFlags.Instance |
BindingFlags.Public |
BindingFlags.NonPublic;
foreach (string name in exactNames)
{
FieldInfo field =
type.GetField(
name,
flags
);
if (field != null &&
wantedType.IsAssignableFrom(
field.FieldType))
{
return field.GetValue(
instance
);
}
PropertyInfo property =
type.GetProperty(
name,
flags
);
if (property != null &&
property.CanRead &&
property.GetIndexParameters().Length == 0 &&
wantedType.IsAssignableFrom(
property.PropertyType))
{
try
{
return
// ... [Code preview truncated due to length. Full source in attached file] ...