Skip to content
RustPluginGenerator

OnFogOfWarImageUpdate

OnFogOfWarImageUpdate is called when a player's client uploads a new fog of war map image for a slot in that player's fog image list, before the server stores it in FileStorage.

Signature
object OnFogOfWarImageUpdate(BasePlayer player, byte b, byte b2, uint num, uint num2, byte[] array)

Parameters

Hook parameters
ParameterTypeDescription
playerBasePlayerThe BasePlayer whose fog of war map image is being updated. Can be null only in unusual cases; always null-check it.
bbyteA flag byte sent by the client alongside the image; in vanilla it gates whether the update is stored at all.
b2byteThe slot index into the player's fog image list that this image belongs to.
numuintThe client-reported value for the slot, compared against the current stored value to decide whether the image is fresh.
num2uintThe old FileStorage entity number for this slot; vanilla calls RemoveEntityNum with it before storing the new image.
arraybyte[]The raw PNG bytes of the fog of war image sent by the client. Null-check before reading it.

Return value

Return null to let Oxide store the image in FileStorage and update the player's fog image list normally. Return any non-null value to stop the original code path, so the new bytes are never stored, the old entity number is not removed and the slot's cached value is left unchanged.

How it works

This hook fires inside BasePlayer.FogImageUpdate, after the server has read the incoming PNG bytes (array) and only when the client's reported value differs from the cached value for that slot. It runs before FileStorage.server.RemoveEntityNum and FileStorage.server.Store, so the image has not yet been written to storage when your hook executes. The parameters let you identify the player, the slot (b2), the old storage id (num2) and the raw image data. Because player is declared as BasePlayer, Oxide only calls your method when the argument is a player instance; declaring a narrower type is not useful here since the game always passes the same BasePlayer. The method can fire fairly regularly for players who explore the map, so keep your handler cheap: inspect b2 or array.Length and return quickly rather than doing expensive work. Returning a non-null value prevents the default storage step from running, which means the image is discarded and the slot keeps its previous id.

Example plugin: Rate-limit fog of war map uploads

FogImageThrottle.cs
using UnityEngine;
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
using Oxide.Core;

namespace Oxide.Plugins
{
    [Info("Fog Image Throttle", "RustPluginGenerator", "1.0.0")]
    [Description("Rate-limits fog of war map image uploads from players")]
    public class FogImageThrottle : RustPlugin
    {
        private const string PermBypass = "fogimagethrottle.bypass";

        private class PluginConfig
        {
            [JsonProperty("Cooldown seconds between accepted uploads")]
            public float CooldownSeconds = 3f;
        }

        private PluginConfig config;
        private readonly Dictionary<ulong, float> lastAccepted = new Dictionary<ulong, float>();
        private Timer pruneTimer;

        protected override void LoadDefaultConfig() => config = new PluginConfig();

        protected override void LoadConfig()
        {
            base.LoadConfig();
            try
            {
                config = Config.ReadObject<PluginConfig>();
                if (config == null) throw new Exception();
            }
            catch
            {
                PrintWarning("Configuration file is invalid, using defaults");
                LoadDefaultConfig();
            }
            SaveConfig();
        }

        protected override void SaveConfig() => Config.WriteObject(config);

        private void Init()
        {
            permission.RegisterPermission(PermBypass, this);
            pruneTimer = timer.Every(60f, PruneTimestamps);
        }

        private void Unload()
        {
            pruneTimer?.Destroy();
            lastAccepted.Clear();
        }

        private object OnFogOfWarImageUpdate(BasePlayer player, byte flag, byte slot, uint reportedValue, uint oldStorageId, byte[] imageBytes)
        {
            if (player == null || imageBytes == null) return null;
            if (permission.UserHasPermission(player.UserIDString, PermBypass)) return null;

            float now = UnityEngine.Time.realtimeSinceStartup;
            float last;
            if (lastAccepted.TryGetValue(player.userID, out last) && now - last < config.CooldownSeconds)
                return true;

            lastAccepted[player.userID] = now;
            return null;
        }

        private void PruneTimestamps()
        {
            if (lastAccepted.Count == 0) return;

            float now = UnityEngine.Time.realtimeSinceStartup;
            var stale = new List<ulong>();
            foreach (var entry in lastAccepted)
            {
                if (now - entry.Value > 600f && now > 600f)
                    stale.Add(entry.Key);
            }

            for (int i = 0; i < stale.Count; i++)
                lastAccepted.Remove(stale[i]);
        }

        [ChatCommand("fogthrottle")]
        private void CmdFogThrottle(BasePlayer player, string command, string[] args)
        {
            if (player != null && !player.IsAdmin) return;
            string reply = $"FogImageThrottle: tracking {lastAccepted.Count} players, cooldown {config.CooldownSeconds}s";
            if (player == null) Puts(reply);
            else SendReply(player, reply);
        }
    }
}

The plugin keeps a per-player timestamp of the last accepted fog image upload. When OnFogOfWarImageUpdate fires it checks the current Time.realtimeSinceStartup, and if the player uploaded within the configured cooldown window it returns a non-null value, which makes Oxide skip the storage step and drop the image. Otherwise it records the new time and returns null so the game stores the image as usual. Cooldown seconds are read from a JSON config file. A repeating timer prunes stale timestamps so the dictionary does not grow forever, and a chat command lets administrators print how many entries are tracked. Keeping the handler to a dictionary lookup and one comparison means the check stays cheap under normal map exploration traffic.

Common use cases

  • Limit map upload bandwidth

    Return non-null when a player exceeds an upload budget in a time window, reducing FileStorage writes and network churn caused by clients spamming fog image updates.

  • Validate uploaded images

    Inspect array.Length and reject images that are obviously larger than a sane PNG for a single fog slot, preventing oversized blobs from being stored on the server.

  • Audit fog image activity

    Record the slot index (b2) and image size per player into a log you flush on save, useful when investigating reports of fog-of-war exploits.

Pitfalls

  • Returning non-null blocks the whole update

    Any non-null return skips storage entirely, including the removal of the old entity number. That leaves the previous image in place, which is fine for throttling but will not clear stale map data.

  • Fires frequently for exploring players

    This hook runs often while players uncover the map. Avoid PrintWarning, Puts, permission lookups or object creation inside it; use a dictionary lookup and return immediately.

  • array can be null

    The vanilla code null-checks the byte array before calling the hook, but treat array as untrusted and null-check anyway before touching Length or indexing.

Build a plugin with OnFogOfWarImageUpdate

Describe what should happen and get a compiled plugin that uses this hook.

Prompt: Limit map upload bandwidth using the OnFogOfWarImageUpdate hook, with a config file and a permission
Generate this plugin

Edit the prompt on the next screen. 2 free plugins a month, no card.

Last updated Oct 26, 2026. Found a mistake? Email support@rustplugingenerator.com.

Build a plugin with OnFogOfWarImageUpdate

Generate