mirror of
https://github.com/MCCTeam/Minecraft-Console-Client
synced 2026-08-15 13:04:36 +00:00
492 lines
16 KiB
Markdown
492 lines
16 KiB
Markdown
# MCC ChatBot Reference
|
|
|
|
Self-contained authoring notes for Minecraft Console Client chat bots.
|
|
|
|
## Bot types
|
|
|
|
MCC supports two common authoring paths:
|
|
- standalone script bots loaded at runtime with `/script`
|
|
- built-in bots compiled into the MCC codebase
|
|
|
|
Default to a standalone `/script` bot unless the user explicitly asks for a built-in bot or repo wiring.
|
|
|
|
## Embedded current patterns
|
|
|
|
This skill is intended to work even without an MCC checkout. The patterns below capture the important behavior that would otherwise be borrowed from current repo examples.
|
|
|
|
If the local repo is available, you can verify against files such as `TestBot.cs`, `RemoteControl.cs`, `FollowPlayer.cs`, `ItemsCollector.cs`, and `Farmer.cs`. If it is not available, use the embedded patterns here directly.
|
|
|
|
### Minimal chat parsing pattern
|
|
|
|
Use this as the baseline for public/private chat handling:
|
|
|
|
```csharp
|
|
public override void GetText(string text)
|
|
{
|
|
string message = "";
|
|
string sender = "";
|
|
text = GetVerbatim(text);
|
|
|
|
if (IsPrivateMessage(text, ref message, ref sender))
|
|
{
|
|
LogToConsole("PM from " + sender + ": " + message);
|
|
}
|
|
else if (IsChatMessage(text, ref message, ref sender))
|
|
{
|
|
LogToConsole("Chat from " + sender + ": " + message);
|
|
}
|
|
}
|
|
```
|
|
|
|
What matters:
|
|
- normalize first with `GetVerbatim(text)`
|
|
- handle PMs before public chat if both matter
|
|
- keep simple chat bots deterministic and small
|
|
|
|
### Owner-gated PM control pattern
|
|
|
|
Use this when a bot owner should be able to whisper MCC internal commands:
|
|
|
|
```csharp
|
|
public override void GetText(string text)
|
|
{
|
|
text = GetVerbatim(text).Trim();
|
|
string command = "";
|
|
string sender = "";
|
|
|
|
if (IsPrivateMessage(text, ref command, ref sender)
|
|
&& Settings.Config.Main.Advanced.BotOwners.Contains(sender.ToLowerInvariant()))
|
|
{
|
|
CmdResult result = new();
|
|
PerformInternalCommand(command, ref result);
|
|
SendPrivateMessage(sender, result.ToString());
|
|
}
|
|
}
|
|
```
|
|
|
|
What matters:
|
|
- `PerformInternalCommand(...)` is for MCC commands, not server chat commands
|
|
- owner gating should use `Settings.Config.Main.Advanced.BotOwners`
|
|
- if `CmdResult` is used in a standalone script, add `//using MinecraftClient.CommandHandler`
|
|
|
|
### Periodic work pattern
|
|
|
|
Use `Update()` plus a counter or timestamp for simple repeated work:
|
|
|
|
```csharp
|
|
private int count = 0;
|
|
|
|
public override void Update()
|
|
{
|
|
count++;
|
|
if (count < Settings.DoubleToTick(60))
|
|
return;
|
|
|
|
count = 0;
|
|
SendText("/list");
|
|
}
|
|
```
|
|
|
|
What matters:
|
|
- avoid a worker thread for simple periodic loops
|
|
- avoid `Thread.Sleep(...)` inside `Update()`
|
|
- if sending chat, do it from a join-safe path like `Update()` or `AfterGameJoined()`, not `Initialize()`
|
|
|
|
### Built-in Brigadier command pattern
|
|
|
|
Use this for built-in command bots:
|
|
|
|
```csharp
|
|
public override void Initialize()
|
|
{
|
|
McClient.dispatcher.Register(l => l.Literal("help")
|
|
.Then(l => l.Literal(CommandName)
|
|
.Executes(r => OnCommandHelp(r.Source, string.Empty))
|
|
)
|
|
);
|
|
|
|
McClient.dispatcher.Register(l => l.Literal(CommandName)
|
|
.Then(l => l.Literal("stop")
|
|
.Executes(r => OnCommandStop(r.Source)))
|
|
.Then(l => l.Literal("_help")
|
|
.Executes(r => OnCommandHelp(r.Source, string.Empty))
|
|
.Redirect(McClient.dispatcher.GetRoot().GetChild("help").GetChild(CommandName)))
|
|
);
|
|
}
|
|
|
|
public override void OnUnload()
|
|
{
|
|
McClient.dispatcher.Unregister(CommandName);
|
|
McClient.dispatcher.GetRoot().GetChild("help").RemoveChild(CommandName);
|
|
}
|
|
```
|
|
|
|
What matters:
|
|
- register commands in `Initialize()`
|
|
- unregister the command tree in `OnUnload()`
|
|
- remove the help child you added in `OnUnload()`
|
|
- prefer this over legacy command wrappers for new built-in work
|
|
|
|
### Built-in config and wiring pattern
|
|
|
|
Use this as the default built-in shape:
|
|
|
|
```csharp
|
|
public class ExampleBot : ChatBot
|
|
{
|
|
public static Configs Config = new();
|
|
|
|
[TomlDoNotInlineObject]
|
|
public class Configs
|
|
{
|
|
public bool Enabled = false;
|
|
|
|
public void OnSettingUpdate()
|
|
{
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
Typical host wiring shape:
|
|
|
|
```csharp
|
|
[TomlPrecedingComment("$ChatBot.ExampleBot$")]
|
|
public ChatBots.ExampleBot.Configs ExampleBot
|
|
{
|
|
get { return ChatBots.ExampleBot.Config; }
|
|
set { ChatBots.ExampleBot.Config = value; ChatBots.ExampleBot.Config.OnSettingUpdate(); }
|
|
}
|
|
```
|
|
|
|
```csharp
|
|
if (Config.ChatBot.ExampleBot.Enabled) { BotLoad(new ExampleBot()); }
|
|
```
|
|
|
|
What matters:
|
|
- built-in configurable bots default to `Enabled = false`
|
|
- `OnSettingUpdate()` is the place to normalize config values
|
|
- built-in delivery is incomplete without both config wiring and load registration
|
|
|
|
### Movement gating pattern
|
|
|
|
Use this shape when a built-in bot owns movement:
|
|
|
|
```csharp
|
|
public override void Initialize()
|
|
{
|
|
if (!GetEntityHandlingEnabled())
|
|
{
|
|
LogToConsole("Entity handling is required.");
|
|
UnloadBot();
|
|
return;
|
|
}
|
|
|
|
if (!GetTerrainEnabled())
|
|
{
|
|
LogToConsole("Terrain handling is required.");
|
|
UnloadBot();
|
|
return;
|
|
}
|
|
}
|
|
```
|
|
|
|
```csharp
|
|
var movementLock = BotMovementLock.Instance;
|
|
if (movementLock is { IsLocked: true })
|
|
return;
|
|
|
|
movementLock?.Lock("Example Bot");
|
|
```
|
|
|
|
```csharp
|
|
public override void OnUnload()
|
|
{
|
|
BotMovementLock.Instance?.UnLock("Example Bot");
|
|
}
|
|
```
|
|
|
|
What matters:
|
|
- guard terrain and entity handling before movement logic
|
|
- built-in movement bots should use `BotMovementLock`
|
|
- release the lock on every stop path, including unload and disconnect-sensitive flows
|
|
|
|
### Dropped-item collector pattern
|
|
|
|
Use this as the standalone item-search baseline:
|
|
|
|
```csharp
|
|
private DateTime nextScan = DateTime.MinValue;
|
|
|
|
public override void Update()
|
|
{
|
|
var now = DateTime.UtcNow;
|
|
if (now < nextScan || ClientIsMoving())
|
|
return;
|
|
|
|
nextScan = now.AddSeconds(1);
|
|
|
|
var here = GetCurrentLocation();
|
|
var target = GetEntities().Values
|
|
.Where(entity => entity.Type == EntityType.Item && entity.Location.Distance(here) <= 15)
|
|
.OrderBy(entity => entity.Location.Distance(here))
|
|
.FirstOrDefault();
|
|
|
|
if (target != null)
|
|
MoveToLocation(target.Location);
|
|
}
|
|
```
|
|
|
|
What matters:
|
|
- simple standalone collectors do not need a worker thread
|
|
- simple standalone collectors also do not need `BotMovementLock` by default
|
|
- `GetEntities()` plus distance ordering is the core search pattern
|
|
|
|
### Inventory selection pattern
|
|
|
|
Use this as the default hotbar-switch pattern:
|
|
|
|
```csharp
|
|
private bool TrySwitchToItem(ItemType itemType)
|
|
{
|
|
var inventory = GetPlayerInventory();
|
|
|
|
var hotbarSlots = inventory.SearchItem(itemType)
|
|
.Where(slot => slot >= 36 && slot <= 44)
|
|
.ToArray();
|
|
|
|
if (hotbarSlots.Length == 0)
|
|
return false;
|
|
|
|
ChangeSlot((short)(hotbarSlots[0] - 36));
|
|
return true;
|
|
}
|
|
```
|
|
|
|
What matters:
|
|
- guard with `GetInventoryEnabled()`
|
|
- search inventory snapshots, but mutate real server state with helpers like `ChangeSlot(...)`
|
|
- do not treat local `Container.Items` mutation as real inventory manipulation
|
|
|
|
Use the older config examples only for ideas, not as primary scaffolding.
|
|
|
|
## Standalone script format
|
|
|
|
A standalone script bot has two parts in this order:
|
|
1. metadata block
|
|
2. one or more C# classes, with the main bot class inheriting `ChatBot`
|
|
|
|
Required metadata rules:
|
|
- line 1 must be exactly `//MCCScript 1.0`
|
|
- metadata must include `MCC.LoadBot(new BotClassName());`
|
|
- metadata ends with `//MCCScript Extensions`
|
|
- optional metadata directives use `//using Namespace` and `//dll SomeLibrary.dll`
|
|
- do not insert a space after `//` in metadata directives
|
|
|
|
Typical runtime flow:
|
|
- place the script file beside MCC
|
|
- connect to a server
|
|
- load it with `/script YourBotFile.cs`
|
|
|
|
### Namespace linking for inventory code
|
|
|
|
If a standalone script uses inventory-specific types such as `Container`, `ItemType`, `WindowActionType`, or `ItemMovingHelper`, add this metadata import:
|
|
|
|
```csharp
|
|
//using MinecraftClient.Inventory
|
|
```
|
|
|
|
For built-in bots, use a normal C# import:
|
|
|
|
```csharp
|
|
using MinecraftClient.Inventory;
|
|
```
|
|
|
|
## Lifecycle summary
|
|
|
|
Common lifecycle hooks:
|
|
- `Initialize()`
|
|
called once when the bot loads; use it for cheap setup only
|
|
- `AfterGameJoined()`
|
|
called after the server has been joined successfully, and again after reconnecting; use it when chat can be sent
|
|
- `Update()`
|
|
called roughly every 100 ms
|
|
- `OnUnload()`
|
|
called when the bot unloads; release resources here
|
|
- `OnDisconnect(DisconnectReason reason, string message)`
|
|
called on disconnect; stop background work and clean up reconnect-sensitive state here
|
|
|
|
Important rule:
|
|
- do not send chat from `Initialize()`; use `AfterGameJoined()` instead
|
|
- prefer `Initialize()` over constructors for environment checks and resource setup
|
|
|
|
## Common event hooks
|
|
|
|
Useful event hooks include:
|
|
- `GetText(string text)`
|
|
- `GetText(string text, string? json)`
|
|
- `OnPlayerJoin(Guid uuid, string name)`
|
|
- `OnPlayerLeave(Guid uuid, string? name)`
|
|
- `OnEntitySpawn(Entity entity)`
|
|
- `OnEntityDespawn(Entity entity)`
|
|
- `OnEntityMove(Entity entity)`
|
|
- `OnHealthUpdate(float health, int food)`
|
|
- `OnMapData(...)`
|
|
- `OnInventoryUpdate(int inventoryId)`
|
|
- `OnPluginMessage(string channel, byte[] data)`
|
|
- `OnNetworkPacket(int packetID, List<byte> packetData, bool isLogin, bool isInbound)`
|
|
|
|
Only override hooks that actually exist in the target MCC ChatBot API.
|
|
|
|
## Common helpers
|
|
|
|
Text and messaging helpers:
|
|
- `GetVerbatim(text)` strips Minecraft formatting codes
|
|
- `IsChatMessage(text, ref message, ref sender)` parses public chat
|
|
- `IsPrivateMessage(text, ref message, ref sender)` parses private chat
|
|
- `IsValidName(username)` validates a Minecraft username
|
|
- `SendText(text)` sends chat or server commands
|
|
- `SendPrivateMessage(player, message)` sends a private message
|
|
- `PerformInternalCommand(command, ...)` runs an internal MCC command, not a server command
|
|
- `LogToConsole(text)` writes a bot-prefixed console message
|
|
|
|
Lifecycle and threading helpers:
|
|
- `InvokeOnMainThread(...)`
|
|
- `ScheduleOnMainThread(...)`
|
|
- `ReconnectToTheServer(...)`
|
|
- `UnloadBot()`
|
|
- `BotLoad(chatBot)`
|
|
- `RunScript(filename, ...)`
|
|
|
|
World and player-state helpers:
|
|
- `GetWorld()`
|
|
- `GetEntities()`
|
|
- `GetCurrentLocation()`
|
|
- `ClientIsMoving()`
|
|
- `GetOnlinePlayers()`
|
|
- `GetOnlinePlayersWithUUID()`
|
|
- `GetServerTPS()`
|
|
- `GetProtocolVersion()`
|
|
|
|
Movement and inventory helpers:
|
|
- `MoveToLocation(...)`
|
|
- `LookAtLocation(...)`
|
|
- `GetInventoryEnabled()`
|
|
- `GetPlayerInventory()`
|
|
- `GetInventories()`
|
|
- `GetItemMovingHelper(...)`
|
|
- `WindowAction(...)`
|
|
- `ChangeSlot(...)`
|
|
- `GetCurrentSlot()`
|
|
- `UseItemInHand()`
|
|
- `UseItemInLeftHand()`
|
|
- `CloseInventory(...)`
|
|
- `DigBlock(...)`
|
|
- `InteractEntity(...)`
|
|
|
|
## Inventory notes
|
|
|
|
Inventory handling is optional in MCC. Check `GetInventoryEnabled()` before relying on inventory state or mutation.
|
|
|
|
Important behavior:
|
|
- `GetPlayerInventory()` returns a snapshot copy of the player's inventory
|
|
- `GetInventories()` returns current container snapshots
|
|
- writing to those `Container` objects locally does not update the server
|
|
- to actually change inventory state, use `ChangeSlot(...)`, `WindowAction(...)`, `GetItemMovingHelper(...)`, `UseItemInHand()`, or related helpers
|
|
|
|
Useful practical facts:
|
|
- hotbar selection uses `ChangeSlot(0..8)`
|
|
- hotbar slots are commonly `36..44` in inventory slot numbering
|
|
- the offhand slot is commonly `45`
|
|
- `Container.SearchItem(...)` is the normal way to locate items by type
|
|
|
|
Good inventory workflow:
|
|
1. guard with `GetInventoryEnabled()`
|
|
2. read the current container using `GetPlayerInventory()`
|
|
3. locate slots with `SearchItem(...)` or `Items`
|
|
4. mutate server state using `ChangeSlot(...)`, `WindowAction(...)`, or `ItemMovingHelper`
|
|
5. if needed, react to `OnInventoryUpdate(...)`, `OnInventoryOpen(...)`, or `OnInventoryClose(...)`
|
|
|
|
Plugins and channels:
|
|
- `RegisterPluginChannel(channel)`
|
|
- `UnregisterPluginChannel(channel)`
|
|
- `SendPluginChannelMessage(channel, data, ...)`
|
|
|
|
## Built-in bot pattern
|
|
|
|
A built-in bot usually follows this shape:
|
|
- a class that inherits `ChatBot`
|
|
- an optional static `Config` field
|
|
- a nested `[TomlDoNotInlineObject]` `Configs` class for settings
|
|
- an `Enabled = false` setting by default
|
|
- `OnSettingUpdate()` to normalize or validate config values
|
|
|
|
If the bot is configurable, the host codebase usually also needs:
|
|
- config wiring in the chat-bot config model
|
|
- load registration so enabled bots are instantiated automatically
|
|
|
|
In this MCC checkout, the usual built-in wiring points are:
|
|
- `MinecraftClient/Settings.cs` inside `Settings.ChatBotConfigHealper.ChatBotConfig`
|
|
- `MinecraftClient/McClient.cs` inside `RegisterBots(...)`
|
|
|
|
Match the surrounding `[TomlPrecedingComment(...)]`, property-forwarding, and `BotLoad(new YourBot())` style instead of inventing a different config path.
|
|
When presenting built-in wiring, prefer literal code snippets or patch hunks for those two edits so the wiring can be checked directly.
|
|
|
|
If the bot adds user-facing settings or messages, follow the host codebase's localization and config-comment conventions instead of scattering hardcoded strings.
|
|
|
|
## Command pattern
|
|
|
|
For standalone script bots, prefer chat or PM handling in `GetText(...)` unless the user explicitly asks for built-in command registration.
|
|
|
|
For built-in commands, prefer the current Brigadier dispatcher pattern:
|
|
- register commands in `Initialize()`
|
|
- add a help entry if the bot exposes commands
|
|
- unregister the command tree in `OnUnload()`
|
|
- remove any help child added during registration in `OnUnload()`
|
|
|
|
Avoid using legacy command wrappers if the current codebase uses direct dispatcher registration.
|
|
In this checkout, treat direct `McClient.dispatcher.Register(...)` usage in current built-in bots as the source of truth.
|
|
|
|
## Concurrency and cleanup
|
|
|
|
If the bot starts background work:
|
|
- stop it in `OnUnload()`
|
|
- stop it in `OnDisconnect(...)`
|
|
- consider resetting state in `AfterGameJoined()` after relog
|
|
- prefer `Update()` plus counters or timestamps over unmanaged threads when the task is simple periodic work
|
|
|
|
If the bot controls movement:
|
|
- use a movement-lock discipline
|
|
- release the lock on every stop path
|
|
- avoid fighting other movement bots
|
|
- `BotMovementLock` is mainly for built-in bots or shared long-running automation; a simple standalone script that just calls `MoveToLocation(...)` does not need it by default
|
|
|
|
When interacting with client state from background logic, use the main-thread helpers when required by the codebase.
|
|
|
|
## Practical defaults
|
|
|
|
For simple chat bots:
|
|
- normalize text with `GetVerbatim(text)`
|
|
- inspect private chat first if the bot listens for whispers
|
|
- then inspect public chat
|
|
- keep response logic small and deterministic
|
|
|
|
For long-running automation bots:
|
|
- guard prerequisites early, such as entity handling or terrain support
|
|
- fail fast with a clear log message if prerequisites are missing
|
|
- release all ongoing work cleanly on unload and disconnect
|
|
|
|
## Common pitfalls
|
|
|
|
- Incorrect metadata line 1 will break standalone script loading.
|
|
- Missing `MCC.LoadBot(new BotClassName())` will prevent standalone script registration.
|
|
- Sending chat in `Initialize()` is too early.
|
|
- Doing prerequisite checks or unloading from the constructor is harder to reason about than using `Initialize()`.
|
|
- Parsing raw formatted text without `GetVerbatim()` causes brittle chat matching.
|
|
- Inventing methods not present in the MCC ChatBot API leads to dead code.
|
|
- Built-in bot work is incomplete if config or registration wiring is missing.
|
|
- Command bots are incomplete if they register commands but do not unregister them.
|
|
- `RegisterChatBotCommand(...)` comes from older samples and is not a reliable current pattern for this checkout.
|
|
- `ChatBotCommand` exists, but the current built-in bots use Brigadier directly; do not prefer `ChatBotCommand` for new work.
|
|
- Blocking `Thread.Sleep(...)` inside `Update()` is a bad default. Prefer timers, counters, or timestamp-based scheduling.
|
|
- Mutating the `Container` returned by `GetPlayerInventory()` does not change the server. Use inventory actions instead.
|