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 <color=yellow>{0}</color> owned by {1}:",
                ["NoEntityFound"] = "No entity found. Look at an entity and right-click while holding a <color=yellow>{0}</color>.",
                ["NoOwner"] = "No owner found for this entity.",
                ["ChatEntityOwnedBy"] = "This <color=yellow>{0}</color> 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 <color=yellow>{0}</color> owned by {1}:",
                ["ProtectionTime"] = "<color=yellow>{0:D2}</color> days <color=yellow>{1:D2}</color> hours <color=yellow>{2:D2}</color> minutes <color=yellow>{3:D2}</color> 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);
    }
}