Skip to content
RustPluginGenerator

OnFrame

OnFrame is called once per server frame by the Oxide CSharp extension for every loaded C# plugin that declares it, with the frame's delta time as its only argument.

Signature
void OnFrame(float delta)

Parameters

Hook parameters
ParameterTypeDescription
deltafloatSeconds elapsed since the previous frame. Multiply per-second rates by it to stay frame-rate independent, or accumulate it to build your own timer interval.

Return value

The original method returns void and each plugin's hook is invoked with the frame delta through the loader loop, so the return value is ignored entirely. Returning null or any other value changes nothing.

How it works

OnFrame fires from CSharpExtension.OnFrame, which the Oxide core runs on the Unity/frame loop. It is delivered only to C# plugins (not Lua or JS) that actually declare the hook, so plugins without it pay nothing. The method receives delta, the elapsed seconds since the last frame; that value carries no information about any specific game action. Because it runs every frame regardless of player activity, it belongs to polling patterns: smoothing values, driving CUI updates at a controlled cadence, watching positions or building budgets, and counting down internal state that has no game event of its own. Do not narrow the parameter: delta is a float, and declaring a different type makes Oxide skip the call. Treat the hook as a tick and guard expensive work behind your own interval accumulator rather than running it every frame. There is no way to suppress or veto the call; the return value is not used.

Example plugin: Show a frame-rate counter to admins

AdminFrameCounter.cs
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
using Oxide.Core;
using Oxide.Game.Rust.Cui;
using UnityEngine;

namespace Oxide.Plugins
{
    [Info("Admin Frame Counter", "RustPluginGenerator", "1.0.0")]
    [Description("Shows a throttled FPS and playercount panel to admins")]
    public class AdminFrameCounter : RustPlugin
    {
        private const string PermUse = "adminframecounter.use";
        private const string PanelName = "AdminFrameCounterPanel";

        private PluginConfig config;
        private float accumulator;
        private float deltaSum;
        private int deltaSamples;

        private class PluginConfig
        {
            [JsonProperty("Refresh interval (seconds)")]
            public float RefreshInterval = 2f;
        }

        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(PermUse, this);
        }

        private void OnFrame(float delta)
        {
            deltaSum += delta;
            deltaSamples++;
            accumulator += delta;

            if (accumulator < config.RefreshInterval) return;

            float average = deltaSamples > 0 ? deltaSum / deltaSamples : delta;
            double fps = average > 0f ? 1.0 / average : 0.0;
            int online = BasePlayer.activePlayerList.Count;

            accumulator = 0f;
            deltaSum = 0f;
            deltaSamples = 0;

            foreach (BasePlayer player in BasePlayer.activePlayerList)
            {
                if (player == null || !player.IsConnected) continue;
                if (!player.IsAdmin && !permission.UserHasPermission(player.UserIDString, PermUse)) continue;
                DrawPanel(player, fps, online);
            }
        }

        private void DrawPanel(BasePlayer player, double fps, int online)
        {
            var container = new CuiElementContainer();
            container.Add(new CuiPanel
            {
                Image = { Color = "0.05 0.05 0.05 0.75" },
                RectTransform = { AnchorMin = "0.75 0.93", AnchorMax = "0.99 0.99" }
            }, "Overlay", PanelName);

            container.Add(new CuiLabel
            {
                Text =
                {
                    Text = string.Format("FPS {0:0.0}  |  Online {1}", fps, online),
                    FontSize = 12,
                    Align = TextAnchor.MiddleCenter
                },
                RectTransform = { AnchorMin = "0 0", AnchorMax = "1 1" }
            }, PanelName);

            CuiHelper.DestroyUi(player, PanelName);
            CuiHelper.AddUi(player, container);
        }

        private void Unload()
        {
            foreach (BasePlayer player in BasePlayer.activePlayerList)
            {
                if (player == null) continue;
                CuiHelper.DestroyUi(player, PanelName);
            }
        }
    }
}

The plugin accumulates delta in OnFrame and, once a configurable interval has passed, pushes a small CUI panel to every admin or permission holder showing the measured FPS (1 / average delta over the window) and the number of active players. Frames are only sampled, not printed, so nothing goes to the console from the hook. The panel element is destroyed and rebuilt each update, and Unload removes it from every connected player so no orphaned UI is left behind. Configuration controls the refresh interval and the permission constant gates access to non-admin users.

Common use cases

  • Custom update cadence

    Accumulate delta and run your own logic every N seconds without scheduling a timer, which is useful when the interval should adapt to load or configuration.

  • Live admin HUD

    Push a CUI overlay with server stats to admins, throttled to a refresh rate that avoids flooding players with UI updates every frame.

  • Position and state polling

    Track something that has no hook of its own, such as distance between players, and act only when a threshold is crossed.

Pitfalls

  • This runs 30 times a second or more

    Looping all players, building strings or writing to the console inside OnFrame will noticeably hurt server performance. Throttle with an accumulator and keep per-frame work trivial.

  • delta is not wall-clock time

    delta is the frame interval in seconds, so multiply rates by it instead of adding 1 to a counter of seconds. Lag spikes make it larger and would otherwise skew your timing.

  • Do not print from the hook

    Logging or warning every frame floods the server console and log file within seconds. Collect data and report on a timer instead.

Build a plugin with OnFrame

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

Prompt: Custom update cadence using the OnFrame 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 OnFrame

Generate