Skip to content
RustPluginGenerator

OnItemResearch

OnItemResearch is called on a ResearchTable right after a research attempt passes the game's checks and before the timer starts, the table locks, and scrap is charged.

Signature
void OnItemResearch(ResearchTable researchTable, Item targetItem, BasePlayer player)

Parameters

Hook parameters
ParameterTypeDescription
researchTableResearchTableThe ResearchTable entity the player is using. Useful for position, owner checks (researchTable.OwnerID) and for finding the table's storage container if you need to inspect or modify scrap or placed items.
targetItemItemThe item in the table's target slot that is about to be researched. Its info.shortname and skin identify the blueprint; the game requires amount <= 1 before this hook runs.
playerBasePlayerThe BasePlayer who initiated the research. Already validated by the game, but you should still null-check it before using inventory or chat APIs.

Return value

The return value is ignored. The game calls the hook and immediately continues with CollectedForCrafting, the research timer, the lock and the scrap charge, so returning null, false or any other value changes nothing. To stop a research attempt, use CanResearchItem, which is called earlier and aborts the whole block on a non-null return.

How it works

The hook fires inside ResearchTable.DoResearch, after the method has confirmed a target item exists, CanResearchItem returned no objection, the item stack is exactly one, the item is researchable, and the loot rules allow the action. It runs before targetItem.CollectedForCrafting, before researchFinishedTime is set, before the table inventory is locked and before the scrap cost is deducted, so at this point nothing has changed yet.

Because the call happens before the table locks and the scrap is taken, this is a good place to record the attempt, charge an extra currency, or announce the research to a team. It is not a cancellation point: the game does not read a return value here, so blocking the action must be done in CanResearchItem, which runs earlier in the same method and stops the chain when it returns a non-null value.

ResearchTable is the concrete type, so declaring the first parameter as BaseEntity still works but narrows nothing; keep the exact ResearchTable type if you need members like the table's inventory. Null-check targetItem, player and researchTable before using them.

Example plugin: Announce and log successful blueprint research

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

namespace Oxide.Plugins
{
    [Info("Research Announcer", "RustPluginGenerator", "1.0.0")]
    [Description("Announces blueprint research to the player and their team")]
    public class ResearchAnnouncer : RustPlugin
    {
        private class PluginConfig
        {
            [JsonProperty("Announce to the researching player")]
            public bool AnnounceToPlayer = true;

            [JsonProperty("Announce to team members")]
            public bool AnnounceToTeam = true;

            [JsonProperty("Only announce these item shortnames (empty = all)")]
            public List<string> WatchList = new List<string>();
        }

        private PluginConfig config;

        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);

        protected override void LoadDefaultMessages()
        {
            lang.RegisterMessages(new Dictionary<string, string>
            {
                ["Research.Self"] = "You started researching: {0}",
                ["Research.Team"] = "{0} started researching: {1}"
            }, this);
        }

        private string Msg(string key, string userId = null, params object[] args)
        {
            return string.Format(lang.GetMessage(key, this, userId), args);
        }

        private void OnItemResearch(ResearchTable researchTable, Item targetItem, BasePlayer player)
        {
            if (targetItem?.info == null || player == null) return;
            if (config.WatchList != null && config.WatchList.Count > 0 && !config.WatchList.Contains(targetItem.info.shortname)) return;

            string itemName = targetItem.info.displayName != null ? targetItem.info.displayName.english : targetItem.info.shortname;

            if (config.AnnounceToPlayer)
                player.ChatMessage(Msg("Research.Self", player.UserIDString, itemName));

            if (!config.AnnounceToTeam || player.currentTeam == 0) return;

            var team = RelationshipManager.ServerInstance.FindTeam(player.currentTeam);
            if (team == null) return;

            foreach (ulong memberId in team.members)
            {
                if (memberId == player.userID.Get()) continue;
                BasePlayer member = BasePlayer.FindByID(memberId);
                if (member == null || !member.IsConnected) continue;
                member.ChatMessage(Msg("Research.Team", member.UserIDString, player.displayName, itemName));
            }
        }
    }
}

The plugin listens for OnItemResearch and treats it as a confirmed research attempt, since the game already filtered out invalid cases. On each attempt it looks up the item's display name and sends a formatted message to the researching player and, if they are on a team, to every teammate using RelationshipManager.ServerInstance.FindTeam. A short config list of item shortnames can limit announcements to weapons and other high-value items, and the whole feature can be switched off from the config. Messages go through the language file so admins can translate them. Counters are kept in memory only and reset on reload, avoiding any file writes inside a hook that fires on player interaction.

Common use cases

  • Notify teammates of blueprint unlocks

    Call OnItemResearch to tell a player's team which blueprint they just started researching, so group members know the crafting option is about to become available.

  • Grant extra progression rewards

    Because the attempt is already validated, you can safely call a referenced plugin such as Economics or a custom points system to reward the player for researching a specific item.

  • Track what players research

    Accumulate per-player research counts in memory or a data file (written outside the hook, e.g. on a timer or unload) to build player statistics for the server panel.

Pitfalls

  • It cannot cancel the research

    The hook fires after all checks passed and its return value is discarded. Use CanResearchItem if you need to block a specific item from being researched.

  • Nothing is charged yet

    Scrap has not been taken and the blueprint is not yet granted when this hook runs. Do not assume the player already paid or that the item will definitely finish, since the research can still be interrupted.

  • Table ownership is not implied

    OwnerID on the table identifies who placed it, which may differ from player on shared bases or public tables. Do not gate rewards on the table owner unless that is your intent.

Build a plugin with OnItemResearch

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

Prompt: Notify teammates of blueprint unlocks using the OnItemResearch 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 OnItemResearch

Generate