Hello, Thank you for all you work, if we can, can we please update to include all the new valan and deep sea items, codelocks etc please?
Naval update
that would be great to check the new builded boats.
Поддерживаю. Тоже требуется обновление.
I've made an unofficial patch that adds support for the Naval Update and also includes a few additional fixes and improvements.
The patch adds:
Player-built boat and vehicle privilege support
Boat/vehicle authorization information
Steering wheel and boat lock information
Improved owner detection for player-built boats and vehicle entities
Additional vehicle layers in the entity raycast
Full localization support for the remaining hardcoded plugin messages
Translatable labels such as "Whitelisted players", "Guest players" and "None"
Localized player chat while keeping F1 console and server logs consistently in English
Consistent ShowCode behavior for lock codes
Some additional null-safety and compatibility improvements
The original functionality and overall plugin design have been preserved. I only extended and fixed the existing behavior where needed.
I've attached/shared the patched file here for anyone who wants to test it. The source is clearly marked as an unofficial patch by dFxPhoeniX.
using System.Collections.Generic;
using Oxide.Core.Configuration;
using UnityEngine;
using Oxide.Core;
using System;
using System.Linq;
using System.Collections;
using System.Reflection;
// Patched by dFxPhoeniX
// Patched version: 1.16.0
// Changes:
// - Added localization support for all plugin-owned chat and console messages.
// - Changed hardcoded uppercase labels to translatable sentence-case language messages.
// - Added Naval Update compatibility for player-built boats and vehicle privileges.
// - Added vehicle layers to entity raycasts for improved boat and vehicle detection.
// - Added vehicle/boat authorization and steering-wheel lock information support.
// - Improved owner resolution for vehicle and player-built boat entities.
// - Made ShowCode consistently control whether lock codes are displayed.
// - Improved configuration compatibility, data safety, and current Unity object cleanup usage.
// - Kept player chat and F1 console output localized while forcing server console logs to English.
namespace Oxide.Plugins
{
[Info("AdminHammer", "mvrb", "1.16.0")]
class AdminHammer : RustPlugin
{
public static AdminHammer plugin;
private const string permAllow = "adminhammer.allow";
private bool logToConsole = true;
private bool logAdminInfo = true;
private bool showBoxContents = true;
private bool showCode = true;
private float toolDistance = 200f;
private string toolUsed = "hammer";
private string commandToRun = "box";
private string chatCommand = "b";
private bool showSphere = false;
private bool performanceMode = false;
private int layerMask = LayerMask.GetMask("Construction", "Deployed", "Default", "Vehicle_World", "Vehicle_Detailed", "Vehicle_Large");
private readonly DynamicConfigFile dataFile = Interface.Oxide.DataFileSystem.GetFile("AdminHammer");
private List<ulong> Users = new List<ulong>();
protected override void LoadDefaultConfig()
{
Config["LogToConsole"] = logToConsole = GetConfig("LogToConsole", GetConfig("LogToFile", true));
Config["LogAdminInfo"] = logAdminInfo = GetConfig("LogAdminInfo", true);
Config["ShowBoxContents"] = showBoxContents = GetConfig("ShowBoxContents", true);
Config["ShowCode"] = showCode = GetConfig("ShowCode", true);
Config["ShowSphere"] = showSphere = GetConfig("ShowSphere", false);
Config["ToolDistance"] = toolDistance = GetConfig("ToolDistance", 200f);
Config["ToolUsed"] = toolUsed = GetConfig("ToolUsed", "hammer");
Config["CommandToRun (Leave blank for no command)"] = commandToRun = GetConfig("CommandToRun (Leave blank for no command)", "box");
Config["ChatCommand"] = chatCommand = GetConfig("ChatCommand", "b");
Config["PerformanceMode"] = performanceMode = GetConfig("PerformanceMode", false);
SaveConfig();
}
private void LoadDefaultMessages()
{
lang.RegisterMessages(new Dictionary<string, string>
{
["NoAuthorizedPlayers"] = "No authorized players.",
["AuthorizedPlayers"] = "Authorized players in the {0} owned by {1}:",
["NoEntityFound"] = "No entity found. Look at an entity and right-click while holding a {0}.",
["NoOwner"] = "No owner found for this entity.",
["ChatEntityOwnedBy"] = "This {0} is owned by {1}",
["ConsoleEntityOwnedBy"] = "This {0} is owned by http://www.steamcommunity.com/profiles/{1}",
["WhitelistedPlayers"] = "Whitelisted players:",
["GuestPlayers"] = "Guest players:",
["None"] = "None",
["CodeLockCode"] = "Code lock code: <color=yellow>{0}</color>",
["SleepingBagAssigned"] = "This sleeping bag has been assigned to {0} by {1}.",
["ItemsInEntity"] = "Items in the {0} owned by {1}:",
["ProtectionTime"] = "{0:D2} days {1:D2} hours {2:D2} minutes {3:D2} seconds",
["BaseProtectedFor"] = "The base is protected for {0}.",
["ServerSpawn"] = "Server spawn",
["UnknownPlayer"] = "Unknown player",
["ToolActivated"] = "You have enabled AdminHammer.",
["ToolDeactivated"] = "You have disabled AdminHammer.",
["AdminUsedTool"] = "{0} [{1}] used AdminHammer on a {2} owned by {3} [{4}] located at {5}",
["PerformanceMode"] = "Performance mode is enabled, so you have to use the chat command <color=yellow>/{0}</color> instead of right-clicking"
}, this, "en");
}
private void Init()
{
plugin = this;
Users = dataFile.ReadObject<List<ulong>>() ?? new List<ulong>();
LoadDefaultConfig();
permission.RegisterPermission(permAllow, this);
cmd.AddChatCommand("ah", this, "CmdAdminHammer");
cmd.AddChatCommand("adminhammer", this, "CmdAdminHammer");
cmd.AddChatCommand(chatCommand, this, "CmdCheckEntity");
}
private void OnServerInitialized()
{
if (performanceMode) return;
bool fileChanged = false;
foreach (var player in BasePlayer.activePlayerList)
{
if (Users.Contains(player.userID))
{
if (!permission.UserHasPermission(player.UserIDString, permAllow))
{
Users.Remove(player.userID);
fileChanged = true;
continue;
}
if (player.gameObject.GetComponent<AH>() == null)
{
player.gameObject.AddComponent<AH>();
}
}
}
if (fileChanged)
{
dataFile.WriteObject(Users);
}
}
private void Unload()
{
foreach (var ah in UnityEngine.Object.FindObjectsByType<AH>(FindObjectsSortMode.None))
{
GameObject.Destroy(ah);
}
}
private void OnPlayerConnected(BasePlayer player)
{
if (!Users.Contains(player.userID)) return;
if (!permission.UserHasPermission(player.UserIDString, permAllow))
{
Users.Remove(player.userID);
dataFile.WriteObject(Users);
return;
}
if (performanceMode) return;
if (player.gameObject.GetComponent<AH>() == null)
{
player.gameObject.AddComponent<AH>();
}
}
private void OnPlayerDisconnected(BasePlayer player, string reason)
{
player.gameObject.GetComponent<AH>()?.Destroy();
}
private void CmdAdminHammer(BasePlayer player)
{
if (!permission.UserHasPermission(player.UserIDString, permAllow)) return;
if (performanceMode)
{
player.ChatMessage(Lang("PerformanceMode", player.UserIDString, chatCommand));
return;
}
if (Users.Contains(player.userID))
{
Users.Remove(player.userID);
if (player.gameObject.GetComponent<AH>() != null)
player.gameObject.GetComponent<AH>().Destroy();
player.ChatMessage(Lang("ToolDeactivated", player.UserIDString));
Interface.Oxide.CallHook("OnAdminHammerDisabled", player);
}
else
{
Users.Add(player.userID);
if (player.gameObject.GetComponent<AH>() == null)
player.gameObject.AddComponent<AH>();
player.ChatMessage(Lang("ToolActivated", player.UserIDString));
Interface.Oxide.CallHook("OnAdminHammerEnabled", player);
}
dataFile.WriteObject(Users);
}
private void CmdCheckEntity(BasePlayer player)
{
if (!permission.UserHasPermission(player.UserIDString, permAllow)) return;
CheckEntity(player);
}
private void CheckEntity(BasePlayer player)
{
RaycastHit hit;
var raycast = Physics.Raycast(player.eyes.HeadRay(), out hit, toolDistance, layerMask);
BaseEntity entity = raycast ? hit.GetEntity() : null;
if (!entity)
{
SendMessage(player, Lang("NoEntityFound", player.UserIDString, toolUsed));
return;
}
bool hasSentMsg = false;
ulong ownerId = GetEffectiveOwnerId(entity);
if (entity is Door)
{
var door = entity as Door;
var lockSlot = door.GetSlot(BaseEntity.Slot.Lock);
if (lockSlot is CodeLock)
{
var codeLock = (CodeLock)lockSlot;
SendMessage(player, BuildCodeLockMessage(entity, codeLock, player));
}
else
{
SendOwnershipMessage(player, entity, ownerId);
}
hasSentMsg = true;
}
else if (entity is SleepingBag)
{
var sleepingBag = entity as SleepingBag;
SendMessage(player,
Lang("SleepingBagAssigned", player.UserIDString,
GetName(sleepingBag.deployerUserID.ToString(), player.UserIDString),
GetName(sleepingBag.OwnerID.ToString(), player.UserIDString)));
hasSentMsg = true;
}
else if (entity is AutoTurret)
{
SendMessage(player, GetAuthorized(entity, player));
if (showBoxContents)
{
var turret = entity as AutoTurret;
string msg = Lang("ItemsInEntity", player.UserIDString, entity.ShortPrefabName, GetName(ownerId.ToString(), player.UserIDString)) + "\n";
foreach (var item in turret.inventory.itemList)
{
msg += $"{item.amount}x {item.info.displayName.english}\n";
}
SendMessage(player, msg);
}
hasSentMsg = true;
}
else if (entity is BuildingPrivlidge)
{
SendMessage(player, GetAuthorized(entity, player));
var priv = entity as BuildingPrivlidge;
if (priv != null)
{
TimeSpan t = TimeSpan.FromMinutes(priv.GetProtectedMinutes());
string formattedTime = Lang("ProtectionTime", player.UserIDString, t.Days, t.Hours, t.Minutes, t.Seconds);
SendMessage(player, Lang("BaseProtectedFor", player.UserIDString, formattedTime));
}
if (!string.IsNullOrEmpty(commandToRun)) player.Command("chat.say /" + commandToRun);
hasSentMsg = true;
}
else if (entity is StorageContainer)
{
var storageContainer = entity as StorageContainer;
if (showBoxContents)
{
string msg = Lang("ItemsInEntity", player.UserIDString, storageContainer.ShortPrefabName, GetName(ownerId.ToString(), player.UserIDString)) + "\n";
foreach (var item in storageContainer.inventory.itemList)
{
msg += $"{item.amount}x {item.info.displayName.english}\n";
}
SendMessage(player, msg);
}
var codeLock = entity.GetSlot(BaseEntity.Slot.Lock) as CodeLock;
if (codeLock != null)
{
SendMessage(player, BuildCodeLockMessage(entity, codeLock, player));
}
SendOwnershipMessage(player, entity, ownerId);
if (!string.IsNullOrEmpty(commandToRun)) player.Command("chat.say /" + commandToRun);
hasSentMsg = true;
}
if (!hasSentMsg)
{
var vehiclePrivilege = FindVehiclePrivilege(entity);
if (vehiclePrivilege != null && TryBuildVehiclePrivilegeMessage(entity, vehiclePrivilege, player, out var vehicleMsg))
{
SendMessage(player, vehicleMsg);
hasSentMsg = true;
}
}
if (!hasSentMsg)
{
SendOwnershipMessage(player, entity, ownerId);
}
if (showSphere) player.SendConsoleCommand("ddraw.sphere", 2f, Color.blue, entity.CenterPoint(), 1f);
if (logToConsole)
Puts(ownerId == 0 ? LangEnglish("NoOwner", entity.ShortPrefabName) : LangEnglish("ConsoleEntityOwnedBy", entity.ShortPrefabName, ownerId.ToString()));
if (logAdminInfo)
Puts(LangEnglish("AdminUsedTool", player.displayName, player.userID, entity.ShortPrefabName, GetName(ownerId.ToString(), null, true), ownerId.ToString(), entity.transform.position.ToString()));
}
private void SendMessage(BasePlayer player, string message)
{
if (player == null || string.IsNullOrEmpty(message)) return;
player.ChatMessage(message);
if (logToConsole) player.ConsoleMessage(message);
}
private void SendOwnershipMessage(BasePlayer player, BaseEntity entity, ulong ownerId)
{
if (player == null || entity == null) return;
SendMessage(player,
ownerId == 0
? Lang("NoOwner", player.UserIDString, entity.ShortPrefabName)
: Lang("ChatEntityOwnedBy", player.UserIDString, entity.ShortPrefabName, GetName(ownerId.ToString(), player.UserIDString)));
}
private string BuildCodeLockMessage(BaseEntity entity, CodeLock codeLock, BasePlayer player)
{
string msg = Lang("AuthorizedPlayers", player.UserIDString, entity.ShortPrefabName, GetName(GetEffectiveOwnerId(entity).ToString(), player.UserIDString)) + "\n";
msg += "\n" + Lang("WhitelistedPlayers", player.UserIDString);
if (showCode && !string.IsNullOrEmpty(codeLock.code))
msg += $" [<color=yellow>{codeLock.code}</color>]";
msg += "\n";
if (codeLock.whitelistPlayers.Count == 0)
{
msg += "- " + Lang("None", player.UserIDString) + "\n";
}
else
{
int whitelisted = 0;
foreach (var user in codeLock.whitelistPlayers)
{
whitelisted++;
msg += $"{whitelisted}. {GetName(user.ToString(), player.UserIDString)}\n";
}
}
msg += "\n" + Lang("GuestPlayers", player.UserIDString);
if (showCode && !string.IsNullOrEmpty(codeLock.guestCode))
msg += $" [<color=yellow>{codeLock.guestCode}</color>]";
msg += "\n";
var guestPlayers = codeLock.guestPlayers.Where(user => !codeLock.whitelistPlayers.Contains(user)).ToList();
if (guestPlayers.Count == 0)
{
msg += "- " + Lang("None", player.UserIDString);
}
else
{
int guests = 0;
foreach (var user in guestPlayers)
{
guests++;
msg += $"{guests}. {GetName(user.ToString(), player.UserIDString)}\n";
}
}
return msg.TrimEnd();
}
private VehiclePrivilege FindVehiclePrivilege(BaseEntity entity)
{
if (entity == null) return null;
if (entity is VehiclePrivilege) return entity as VehiclePrivilege;
BaseEntity current = entity;
for (int i = 0; current != null && i < 8; i++)
{
var direct = current.GetComponent<VehiclePrivilege>();
if (direct != null) return direct;
if (string.Equals(current.GetType().Name, "PlayerBoat", StringComparison.Ordinal))
{
var boatPrivilege = current.GetComponentInChildren<VehiclePrivilege>(true);
if (boatPrivilege != null) return boatPrivilege;
}
current = current.GetParentEntity();
}
return null;
}
private bool TryBuildVehiclePrivilegeMessage(BaseEntity entity, VehiclePrivilege privilege, BasePlayer player, out string message)
{
message = null;
if (entity == null || privilege == null || player == null) return false;
if (!TryGetAuthorizedIds(privilege, out var authorizedIds))
return false;
ulong ownerId = GetEffectiveOwnerId(entity);
if (ownerId == 0 && privilege.OwnerID != 0) ownerId = privilege.OwnerID;
string msg = Lang("AuthorizedPlayers", player.UserIDString, entity.ShortPrefabName, GetName(ownerId.ToString(), player.UserIDString)) + "\n";
if (authorizedIds.Count == 0)
{
msg += "- " + Lang("None", player.UserIDString);
}
else
{
int authed = 0;
foreach (var userId in authorizedIds)
{
authed++;
msg += $"{authed}. {GetName(userId.ToString(), player.UserIDString)}\n";
}
}
if (showCode && TryGetPlayerBoatLockCode(entity, out var code) && !string.IsNullOrEmpty(code))
msg += "\n" + Lang("CodeLockCode", player.UserIDString, code);
message = msg.TrimEnd();
return true;
}
private bool TryGetAuthorizedIds(object source, out List<ulong> ids)
{
ids = new List<ulong>();
if (source == null) return false;
object value = GetMemberValue(source, "authorizedPlayers", "AuthorizedPlayers", "players", "Players");
if (value == null)
{
value = InvokeNoArgs(source, "GetAuthorizedPlayers") ??
InvokeNoArgs(source, "GetAuthorizedPlayerIds") ??
InvokeNoArgs(source, "GetPlayerIds") ??
InvokeNoArgs(source, "GetPlayers");
}
if (value == null) return false;
AddIdsFromValue(value, ids);
ids = ids.Where(id => id != 0).Distinct().ToList();
return true;
}
private void AddIdsFromValue(object value, List<ulong> ids)
{
if (value == null || ids == null) return;
if (value is ulong)
{
ids.Add((ulong)value);
return;
}
if (value is string)
{
if (ulong.TryParse((string)value, out var parsed)) ids.Add(parsed);
return;
}
if (value is IEnumerable enumerable)
{
foreach (var entry in enumerable)
{
if (entry == null) continue;
if (entry is ulong)
{
ids.Add((ulong)entry);
continue;
}
var idValue = GetMemberValue(entry, "userid", "userID", "UserID", "steamid", "SteamId", "SteamID", "Key");
if (idValue != null && ulong.TryParse(idValue.ToString(), out var memberId))
{
ids.Add(memberId);
continue;
}
if (ulong.TryParse(entry.ToString(), out memberId))
ids.Add(memberId);
}
}
}
private bool TryGetPlayerBoatLockCode(BaseEntity entity, out string code)
{
code = null;
if (entity == null) return false;
BaseEntity current = entity;
for (int i = 0; current != null && i < 8; i++)
{
var lockSlot = current.GetSlot(BaseEntity.Slot.Lock);
if (lockSlot != null)
{
if (lockSlot is CodeLock)
{
code = (lockSlot as CodeLock).code;
return true;
}
if (string.Equals(lockSlot.GetType().Name, "PlayerBoatLock", StringComparison.Ordinal))
{
var value = GetMemberValue(lockSlot, "code", "Code");
if (value != null)
{
code = value.ToString();
return true;
}
}
}
if (string.Equals(current.GetType().Name, "PlayerBoat", StringComparison.Ordinal))
{
foreach (var component in current.GetComponentsInChildren<Component>(true))
{
if (component == null || !string.Equals(component.GetType().Name, "PlayerBoatLock", StringComparison.Ordinal)) continue;
var value = GetMemberValue(component, "code", "Code");
if (value != null)
{
code = value.ToString();
return true;
}
}
}
current = current.GetParentEntity();
}
return false;
}
private ulong GetEffectiveOwnerId(BaseEntity entity)
{
BaseEntity current = entity;
for (int i = 0; current != null && i < 8; i++)
{
if (current.OwnerID != 0) return current.OwnerID;
current = current.GetParentEntity();
}
var privilege = FindVehiclePrivilege(entity);
return privilege != null ? privilege.OwnerID : 0;
}
private object GetMemberValue(object source, params string[] names)
{
if (source == null || names == null) return null;
var type = source.GetType();
while (type != null)
{
foreach (var name in names)
{
var field = type.GetField(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
if (field != null) return field.GetValue(source);
var property = type.GetProperty(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
if (property != null && property.GetIndexParameters().Length == 0)
{
try { return property.GetValue(source, null); }
catch { }
}
}
type = type.BaseType;
}
return null;
}
private object InvokeNoArgs(object source, string methodName)
{
if (source == null || string.IsNullOrEmpty(methodName)) return null;
var type = source.GetType();
while (type != null)
{
var method = type.GetMethod(methodName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, Type.EmptyTypes, null);
if (method != null)
{
try { return method.Invoke(source, null); }
catch { return null; }
}
type = type.BaseType;
}
return null;
}
private class AH : MonoBehaviour
{
public BasePlayer player;
private float lastCheck;
private void Awake()
{
player = GetComponent<BasePlayer>();
lastCheck = Time.realtimeSinceStartup;
}
private void FixedUpdate()
{
if (player == null || !player.IsConnected)
{
Destroy();
return;
}
float currentTime = Time.realtimeSinceStartup;
if (!player.serverInput.WasJustPressed(BUTTON.FIRE_SECONDARY) || (player.GetActiveItem() as Item)?.info.shortname != plugin.toolUsed) return;
if (currentTime - lastCheck >= 0.25f)
{
plugin.CheckEntity(player);
lastCheck = currentTime;
}
}
public void Destroy()
{
Destroy(this);
}
}
private string GetAuthorized(BaseEntity entity, BasePlayer player)
{
string msg = Lang("AuthorizedPlayers", player.UserIDString, entity.ShortPrefabName, GetName(GetEffectiveOwnerId(entity).ToString(), player.UserIDString)) + "\n";
var turret = entity as AutoTurret;
var priv = entity as BuildingPrivlidge;
int authed = 0;
foreach (var user in (turret ? turret.authorizedPlayers : priv.authorizedPlayers))
{
authed++;
msg += $"{authed}. {GetName(user.ToString(), player.UserIDString)}\n";
if (logToConsole) Puts($"{authed}. {user} {GetName(user.ToString(), null, true)}");
}
return authed == 0 ? Lang("NoAuthorizedPlayers", player.UserIDString) : msg.TrimEnd();
}
private string GetPlayerColor(ulong id) => BasePlayer.FindByID(id) != null ? "green" : "red";
private string GetName(string id, string languageId = null, bool english = false)
{
if (id == "0") return english ? LangEnglish("ServerSpawn") : Lang("ServerSpawn", languageId);
if (!ulong.TryParse(id, out var userId)) return english ? LangEnglish("UnknownPlayer") : Lang("UnknownPlayer", languageId);
string color = GetPlayerColor(userId);
string name = covalence.Players.FindPlayerById(id)?.Name;
if (string.IsNullOrEmpty(name)) name = english ? LangEnglish("UnknownPlayer") : Lang("UnknownPlayer", languageId);
return $"<color={color}> {name} </color> ({id})";
}
private string LangEnglish(string key, params object[] args)
{
var messages = lang.GetMessages("en", this);
string message;
if (messages == null || !messages.TryGetValue(key, out message))
message = lang.GetMessage(key, this);
return string.Format(message, args);
}
private T GetConfig<T>(string name, T value) => Config[name] == null ? value : (T)Convert.ChangeType(Config[name], typeof(T));
private string Lang(string key, string id = null, params object[] args) => string.Format(lang.GetMessage(key, this, id), args);
}
}