Skip to content
RustPluginGenerator

OnArcadeScoreAdded

OnArcadeScoreAdded is called after a BaseArcadeMachine records a player's score in its score list. It fires from BaseArcadeMachine.AddScore, after the list is trimmed to five entries and before the network update is sent.

Signature
void OnArcadeScoreAdded(BaseArcadeMachine arcadeMachine, BasePlayer player, int score)

Parameters

Hook parameters
ParameterTypeDescription
arcadeMachineBaseArcadeMachineThe BaseArcadeMachine that recorded the score. Use it to read the machine's position, owner or net ID, or to inspect its score list.
playerBasePlayerThe BasePlayer who earned the score. May be null-checked like any player reference before reading UserIDString or sending chat.
scoreintThe integer score value the machine just recorded for this player, after the game has already trimmed and stored the score list.

Return value

The hook is emitted with continue semantics, meaning Oxide ignores whatever you return. Returning null, true or any other value does not alter the score list, the network update or any other part of BaseArcadeMachine.AddScore.

How it works

This hook fires inside BaseArcadeMachine.AddScore, the method the game uses when a player finishes a run on an arcade machine (the arcade cabinets found at monuments and player bases). The call happens after the machine has already inserted the score, trimmed its list down to five entries and queued SendNetworkUpdate, so by the time your plugin runs, the game state is settled.

Use arcadeMachine to identify which cabinet scored (its net.ID.Value, transform.position or OwnerID), player to know who played, and score for the recorded value. This is a good place to award currencies, track leaderboards, announce high scores or log activity per cabinet.

Declaring the first parameter as BaseArcadeMachine is what makes Oxide reach your method at all; declaring a more general BaseNetworkable would also match, but there is only one argument type the game passes here, so there is no narrowing benefit to a more specific type. Because the hook fires once per score submission rather than per frame, light work such as chat replies or data updates is fine, but avoid heavy serialization inside it.

The hook cannot change the score; the value is already stored when it fires.

Example plugin: Announce arcade high scores in chat

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

namespace Oxide.Plugins
{
    [Info("Arcade High Score Announcer", "RustPluginGenerator", "1.0.0")]
    [Description("Tracks per-player arcade machine high scores and announces new records in chat")]
    public class ArcadeHighScoreAnnouncer : RustPlugin
    {
        private const string PermAnnounce = "arcadehighscoreannouncer.announce";

        private PluginConfig config;
        private StoredData data;

        private class PluginConfig
        {
            [JsonProperty("Minimum score to announce")]
            public int MinimumScore { get; set; } = 100;

            [JsonProperty("Broadcast records to the whole server")]
            public bool BroadcastRecords { get; set; } = true;
        }

        private class StoredData
        {
            [JsonProperty("Best scores by player id")]
            public Dictionary<string, int> BestScores { get; set; } = new Dictionary<string, int>();
        }

        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>
            {
                ["NewRecord"] = "New personal arcade record: {0} points!",
                ["ServerRecord"] = "{0} just set an arcade record with {1} points!"
            }, this);
        }

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

        private void Init()
        {
            permission.RegisterPermission(PermAnnounce, this);
        }

        private void OnServerInitialized()
        {
            data = Interface.Oxide.DataFileSystem.ReadObject<StoredData>(Name) ?? new StoredData();
        }

        private void Unload()
        {
            if (data != null)
            {
                Interface.Oxide.DataFileSystem.WriteObject(Name, data);
            }
        }

        private void OnArcadeScoreAdded(BaseArcadeMachine arcadeMachine, BasePlayer player, int score)
        {
            if (arcadeMachine == null || player == null) return;
            if (score < config.MinimumScore) return;
            if (data == null) return;

            string userId = player.UserIDString;
            int previous;
            data.BestScores.TryGetValue(userId, out previous);
            if (score <= previous) return;

            data.BestScores[userId] = score;

            if (player.IsConnected)
            {
                player.ChatMessage(Msg("NewRecord", userId, score));
            }

            if (config.BroadcastRecords && permission.UserHasPermission(userId, PermAnnounce))
            {
                Server.Broadcast(Msg("ServerRecord", null, player.displayName, score));
            }
        }
    }
}

The plugin listens for OnArcadeScoreAdded and compares the new score against a per-player best stored in oxide/data. When a player beats their record, the plugin sends a localized chat message to that player and broadcasts a server-wide announcement through Server.Broadcast. A configurable minimum score filters out low runs so chat stays readable. Records live in a small StoredData class keyed by player ID string, loaded in OnServerInitialized and written back in Unload. All player-facing text passes through the lang system, and every hook parameter is null-checked before use.

Common use cases

  • Personal high-score tracking

    Store each player's best score per machine and tell them when they beat it. This drives repeat visits to arcade cabinets without touching the machine itself.

  • Server-wide leaderboard

    Keep a top-ten list across all cabinets and announce new entries. Because the hook fires once per submission, updates are cheap and infrequent.

  • Reward payouts

    Grant scrap, items or economy balance when a player passes a score threshold. Combine with a cooldown if your server has cabinets players can spam.

Pitfalls

  • The score is already stored

    The hook fires after the machine trimmed and saved its list, so you cannot reject or rewrite the score here. Use OnArcadeScoreAdded for reactions only, not for filtering.

  • Player may not be connected

    The player reference is passed straight through, but the player can disconnect right after a run. Check player.IsConnected before sending chat or CUI, and fall back to a broadcast if needed.

  • Running on both Oxide and Carbon

    Carbon loads Oxide plugins and raises the same hook, so the same file works on both. Keep data writes in oxide/data via Interface.Oxide.DataFileSystem so the data survives a framework switch.

Build a plugin with OnArcadeScoreAdded

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

Prompt: Personal high-score tracking using the OnArcadeScoreAdded 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 OnArcadeScoreAdded

Generate