BlueBeard.UI
0.1.0-ci.24
dotnet add package BlueBeard.UI --version 0.1.0-ci.24
NuGet\Install-Package BlueBeard.UI -Version 0.1.0-ci.24
<PackageReference Include="BlueBeard.UI" Version="0.1.0-ci.24" />
<PackageVersion Include="BlueBeard.UI" Version="0.1.0-ci.24" />
<PackageReference Include="BlueBeard.UI" />
paket add BlueBeard.UI --version 0.1.0-ci.24
#r "nuget: BlueBeard.UI, 0.1.0-ci.24"
#:package BlueBeard.UI@0.1.0-ci.24
#addin nuget:?package=BlueBeard.UI&version=0.1.0-ci.24&prerelease
#tool nuget:?package=BlueBeard.UI&version=0.1.0-ci.24&prerelease
BlueBeard.UI
A reusable framework for building full-screen UIs using Unturned's EffectManager API. Provides a type-driven IUI / IUIScreen / IUIDialog hierarchy with automatic event routing, per-player state, modal management, a push-update dispatch system, and cleanup.
Installation
<ProjectReference Include="..\BlueBeard.UI\BlueBeard.UI.csproj" />
Architecture
IUI<TSelf> (top-level, e.g. FactionUI)
+-- IUIScreen (a page/tab, e.g. MembersScreen)
+-- IUIDialog (a modal popup, e.g. ConfirmKickDialog)
UIManager registers each IUI by type, instantiates every screen and dialog it declares through Configure(UIBuilder), and caches the instances. All navigation is generic — OpenUI<TUI>(), SetScreen<TScreen>(), OpenDialog<TDialog>().
Setup
using BlueBeard.UI;
var uiManager = new UIManager();
uiManager.Load();
uiManager.RegisterUI<FactionUI>();
// ... later, when the player requests the UI:
uiManager.OpenUI<FactionUI>(player);
// On plugin unload:
uiManager.Unload();
Declaring a UI
Inherit from UIBase for virtual no-op defaults and implement IUI<TSelf> for registration:
public class FactionUI : UIBase, IUI<FactionUI>
{
public override string Id => "faction";
public override ushort EffectId => 50600;
public override short EffectKey => (short)EffectId;
public void Configure(UIBuilder builder)
{
builder
.AddScreen<FactionOverviewScreen>(isDefault: true)
.AddScreen<FactionMembersScreen>()
.AddDialog<ConfirmKickDialog>();
}
public override void OnButtonPressed(UIContext ctx, string buttonName)
{
switch (buttonName)
{
case "Faction_Close": MyPlugin.UI.CloseUI(ctx.Player); return;
case "Faction_Tab_Overview": MyPlugin.UI.SetScreen<FactionOverviewScreen>(ctx.Player); return;
case "Faction_Tab_Members": MyPlugin.UI.SetScreen<FactionMembersScreen>(ctx.Player); return;
}
if (ctx.Component.CurrentDialog != null)
ctx.Component.CurrentDialog.OnButtonPressed(ctx, buttonName);
else
ctx.Component.CurrentScreen?.OnButtonPressed(ctx, buttonName);
}
}
Screens and dialogs inherit UIScreenBase / UIDialogBase, each with a public parameterless constructor.
UIManager API
| Method | Description |
|---|---|
RegisterUI<TUI>() |
Instantiate, configure, and cache a UI plus every screen/dialog it declares |
OpenUI<TUI>(player) |
Send the effect, enable modal, show the default screen |
CloseUI(player) |
Run full close lifecycle, clear effect, disable modal, reset component |
SetScreen<TScreen>(player) |
Transition to a different screen on the active UI |
OpenDialog<TDialog>(player) |
Open a dialog registered on the active UI |
CloseDialog(player) |
Close the active dialog |
GetUI<TUI>() / GetScreen<T>() / GetDialog<T>() |
Retrieve cached instance for state inspection |
PushUpdate(player, key, value) |
Dispatch an update through dialog → screen → UI |
PushUpdateAll<TUI>(key, value) |
Broadcast to every player with TUI open |
PushUpdateToScreen<TScreen>(key, value) |
Broadcast to every player on a specific screen |
UIManager automatically hooks EffectManager.onEffectButtonClicked and onEffectTextCommitted, resets per-player state on disconnect, and closes all open UIs on unload.
Push Updates
External managers notify the active UI of state changes without holding references:
// In RentManager after collecting rent (main thread):
MyPlugin.UI.PushUpdate(ownerPlayer, "rent.collected", new Dictionary<string, object>
{
["renter"] = renterName,
["amount"] = property.RentPrice,
});
The update travels dialog → screen → IUI; the first OnUpdate that returns true consumes it. Default (inherited from the abstract base classes) is false (not handled), so updates naturally propagate.
public class ManagementScreen : UIScreenBase
{
public override string Id => "management";
public override bool OnUpdate(UIContext ctx, string key, object value)
{
if (key != "rent.collected") return false;
var data = (Dictionary<string, object>)value;
EffectManager.sendUIEffectText(ctx.EffectKey, ctx.Connection, true,
"Canvas/RentStatus", $"Last payment: {data["amount"]} EXP");
return true;
}
}
Thread safety: PushUpdate calls EffectManager and must run on the main thread. Wrap in ThreadHelper.RunSynchronously if dispatching from a background worker.
UIContext
Every callback receives a UIContext:
| Property | Type | Description |
|---|---|---|
Player |
UnturnedPlayer |
The Rocket player |
Connection |
ITransportConnection |
For EffectManager calls |
EffectKey |
short |
The active UI's effect key |
Component |
UIPlayerComponent |
Per-player state (CurrentUI, CurrentScreen, CurrentDialog, IsOpen, State) |
Per-Player State
UIPlayerComponent persists across callbacks within a single UI session. Use its State dictionary for pagination, selection, pending input, etc., with dot-namespaced keys:
ctx.Component.State["members.page"] = 0;
ctx.Component.State["members.selectedId"] = steamId;
State is cleared by CloseUI and on player disconnect, not by SetScreen.
Documentation
Full reference and examples in the Infrastructure docs.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET Framework | net481 is compatible. |
-
.NETFramework 4.8.1
- BlueBeard.Core (>= 0.1.0-ci.24)
NuGet packages (1)
Showing the top 1 NuGet packages that depend on BlueBeard.UI:
| Package | Downloads |
|---|---|
|
BlueBeard.MiniGames
Framework for timed, interactive mini-games layered over the BlueBeard.UI effect system. |
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 0.1.0-ci.24 | 80 | 7/4/2026 |
| 0.1.0-ci.23 | 97 | 6/25/2026 |
| 0.1.0-ci.22 | 87 | 5/8/2026 |
| 0.1.0-ci.19 | 63 | 5/7/2026 |
| 0.1.0-ci.18 | 64 | 5/7/2026 |
| 0.1.0-ci.17 | 68 | 5/3/2026 |
| 0.1.0-ci.16 | 79 | 5/3/2026 |
| 0.1.0-ci.15 | 82 | 4/13/2026 |
| 0.1.0-ci.14 | 77 | 4/12/2026 |
| 0.1.0-ci.13 | 71 | 4/12/2026 |
| 0.1.0-ci.12 | 75 | 4/12/2026 |
| 0.1.0-ci.11 | 71 | 4/12/2026 |
| 0.1.0-ci.10 | 79 | 4/11/2026 |
| 0.1.0-ci.9 | 74 | 4/7/2026 |
| 0.1.0-ci.8 | 66 | 4/7/2026 |
| 0.1.0-ci.7 | 66 | 4/7/2026 |
| 0.1.0-ci.6 | 77 | 3/14/2026 |
| 0.1.0-ci.5 | 74 | 2/22/2026 |