OnBuildingMerge
OnBuildingMerge is called inside ServerBuildingManager.Merge, just before one building's decay entities are reattached to the other building. It fires when the server combines two building structures, for example after a foundation or wall connects two formerly separate buildings.
void OnBuildingMerge(ServerBuildingManager serverBuildingManager, BuildingManager.Building building1, BuildingManager.Building building2)Parameters
| Parameter | Type | Description |
|---|---|---|
serverBuildingManager | ServerBuildingManager | The ServerBuildingManager singleton that is running the merge. Useful if you need to reach the server-side building index, but most plugins ignore it. |
building1 | BuildingManager.Building | The destination Building that surviving entities, decay entities and navmesh carving will be attached to after the call. |
building2 | BuildingManager.Building | The Building that is being absorbed. Its decay entities are moved to building1 right after the hook returns. |
Return value
The return value is ignored. The merge continues regardless of what you return, and there is no way to prevent building2's decay entities from being attached to building1 from inside this hook.
How it works
The hook fires from ServerBuildingManager.Merge(Building, Building), immediately before the merge loop that moves every decay entity from building2 onto building1. The merge is triggered by the game when two separate building IDs become one structure, such as when a player places a foundation or wall that bridges two unconnected bases, or when connecting pieces cause the server to unify construction.
building1 is the destination and building2 is the one being dissolved; after the call, building2's decay entities have their attached building ID set to building1.ID. Both parameters expose ID, decayEntities, buildingBlocks, owners and similar collections, which you can use to identify which base group is being absorbed.
Because the hook fires for every merge, keep the body cheap: no database writes, no PrintToChat loops. If you inspect members on building1 or building2 (for example buildingBlocks or decayEntities), null-check the collections. A typical use is cancelling or replacing the merge side effect by comparing the two buildings yourself, since the hook is not designed to veto the merge - the engine merges regardless.
Due to how Oxide dispatches hooks, declaring building1 as a more specific type than BuildingManager.Building will cause the call to be skipped when the argument is not that type; keep the exact declared types.
Example plugin: Alert admins when two separate bases merge
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
using Oxide.Core;
namespace Oxide.Plugins
{
[Info("BuildingMergeAlert", "RustPluginGenerator", "1.0.0")]
[Description("Notifies admins when two building structures on the server merge.")]
public class BuildingMergeAlert : RustPlugin
{
private const string PermAdmin = "buildingmergealert.admin";
private PluginConfig config;
public class PluginConfig
{
[JsonProperty("Notify in chat")]
public bool NotifyChat { get; set; } = true;
[JsonProperty("Notify in console")]
public bool NotifyConsole { get; set; } = true;
}
protected override void LoadDefaultConfig() => config = new PluginConfig();
protected override void LoadConfig()
{
base.LoadConfig();
try
{
config = Config.ReadObject<PluginConfig>();
if (config == null) throw new System.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>
{
["MergeAlert"] = "Buildings merged: {0} ({1} blocks, owner {2}) absorbed {3} ({4} blocks, owner {5})."
}, 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(PermAdmin, this);
}
private void OnBuildingMerge(ServerBuildingManager serverBuildingManager,
BuildingManager.Building building1, BuildingManager.Building building2)
{
if (building1 == null || building2 == null) return;
int blocks1 = building1.buildingBlocks != null ? building1.buildingBlocks.Count : 0;
int blocks2 = building2.buildingBlocks != null ? building2.buildingBlocks.Count : 0;
string name1 = OwnerName(building1);
string name2 = OwnerName(building2);
foreach (var player in BasePlayer.activePlayerList)
{
if (player == null || !player.IsConnected) continue;
if (!permission.UserHasPermission(player.UserIDString, PermAdmin)) continue;
if (config.NotifyChat)
{
player.ChatMessage(Msg("MergeAlert", player.UserIDString,
building1.ID, blocks1, name1, building2.ID, blocks2, name2));
}
if (config.NotifyConsole)
{
player.SendConsoleCommand("echo", Msg("MergeAlert", player.UserIDString,
building1.ID, blocks1, name1, building2.ID, blocks2, name2));
}
}
}
private string OwnerName(BuildingManager.Building building)
{
if (building.buildingBlocks == null) return "none";
foreach (var block in building.buildingBlocks)
{
if (block == null) continue;
ulong ownerId = block.OwnerID;
if (ownerId == 0) continue;
var player = BasePlayer.FindByID(ownerId);
if (player != null) return player.displayName;
return ownerId.ToString();
}
return "none";
}
}
}The plugin registers the fixed permission buildingmergealert.admin in Init; only online players with that permission receive alerts. When OnBuildingMerge fires, the hook counts the building blocks on each side and determines an owner name: the display name of the first block's owner if that player is online, otherwise the owner ID as a string, or "none" if no block has an owner. It then sends a chat message and an echo console command to each eligible admin, showing both building IDs, block counts and owner names. Notifications are controlled independently by the NotifyChat and NotifyConsole config options. The plugin does not alter the merge itself. Messages are localized through the language file, and there is no persistent state or timers to clean up.
Common use cases
Ownership conflict warnings
Warn staff when an unraidable side of a merged base ends up absorbing a smaller neighbouring structure, which is a common sign of a raid-tower or shared-wall exploit.
Raid logging
Record merges in your own logs alongside player IDs so that later builds and demolitions can be attributed correctly when investigating griefing complaints.
Base consolidation detection
Detect large bases merging with nearby additions in real time and notify staff in chat so they can inspect suspicious wall-stacking before the merge effects settle.
Pitfalls
You cannot block the merge
The return value is ignored, so returning
falseor a non-null value will not preventbuilding2's decay entities from being reattached tobuilding1.Both buildings may have several owners
A Building can belong to multiple authorised players, so inspecting just one owner ID will miss cases where the merged structure is shared between neighbours or clans.
Do not spam chat for large bases
Merges can fire in bursts when players place many connecting pieces, so throttle or dedupe messages or you will flood staff chat.
Build a plugin with OnBuildingMerge
Describe what should happen and get a compiled plugin that uses this hook.
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 OnBuildingMerge
Generate