Add Discord RPC setup tutorial, Discord Bridge AllowOtherBotMessages config, and enhance integration tests

Co-authored-by: milutinke <441903+milutinke@users.noreply.github.com>
Agent-Logs-Url: https://github.com/MCCTeam/Minecraft-Console-Client/sessions/a41f3e83-c88d-4c57-971b-54c380891556
This commit is contained in:
copilot-swe-agent[bot] 2026-03-23 00:41:40 +00:00
parent 02686de79c
commit 5a9786cb0b
5 changed files with 164 additions and 4 deletions

View file

@ -9,9 +9,19 @@ This skill uses a fixed set of stable commands for local offline integration tes
- `inventory player list`
- `/gamemode creative`
- `inventory creativegive 36 Diamond 16`
- `inventory creativegive 37 IronSword 1`
- `inventory creativegive 38 GoldenApple 8`
- `inventory creativeclear 38`
- `entity`
- `/time query daytime`
- `look up`
- `look down`
- `look east`
- `/gamemode survival`
- `respawn`
- `/tp CursorBot 0 -60 0`
- `smoke_test_from_mcc_full_spectrum`
- `integration_test_chat_response`
Notes:
- Lines starting with `/` are sent to the server as chat/commands.
@ -24,6 +34,11 @@ Notes:
- `gamerule logAdminCommands true`
- `time set day`
- `weather clear`
- `say Hello from the server console`
- `msg CursorBot This is a private whisper`
- `effect give CursorBot minecraft:speed 30 1`
- `effect give CursorBot minecraft:regeneration 10 1`
- `kill CursorBot`
## Representative entity coverage
@ -34,6 +49,21 @@ Notes:
- `execute as CursorBot at @s run summon minecraft:villager ~-2 ~ ~`
- `execute as CursorBot at @s run summon minecraft:allay ~-4 ~ ~`
- `execute as CursorBot at @s run summon minecraft:armor_stand ~ ~ ~2`
- `execute as CursorBot at @s run summon minecraft:item_display ~-6 ~ ~ {item:{id:"minecraft:diamond",count:1}}`
- `execute as CursorBot at @s run summon minecraft:spider ~10 ~ ~`
- `execute as CursorBot at @s run summon minecraft:pig ~-8 ~ ~`
## Block placement coverage
- `execute as CursorBot at @s run fill ~1 ~ ~1 ~3 ~2 ~3 minecraft:stone`
- `execute as CursorBot at @s run setblock ~5 ~ ~5 minecraft:chest`
- `execute as CursorBot at @s run setblock ~5 ~1 ~5 minecraft:furnace`
- `execute as CursorBot at @s run setblock ~6 ~ ~5 minecraft:crafting_table`
## Dimension change coverage
- `execute in minecraft:the_nether run tp CursorBot 0 64 0`
- `execute in minecraft:overworld run tp CursorBot 0 -60 0`
## Representative particle coverage
@ -41,13 +71,21 @@ Notes:
- `execute as CursorBot at @s run particle minecraft:end_rod ~ ~1 ~ 0.5 0.5 0.5 0.01 20 force`
- `execute as CursorBot at @s run particle minecraft:explosion ~ ~1 ~ 0 0 0 0 1 force`
- `execute as CursorBot at @s run particle minecraft:totem_of_undying ~ ~1 ~ 0.5 0.5 0.5 0.1 20 force`
- `execute as CursorBot at @s run particle minecraft:flame ~ ~1 ~ 0.2 0.2 0.2 0.02 30 force`
- `execute as CursorBot at @s run particle minecraft:heart ~ ~2 ~ 0.3 0.3 0.3 0 5 force`
## Representative sound coverage
- `execute as CursorBot at @s run playsound minecraft:entity.lightning_bolt.thunder master CursorBot ~ ~ ~ 1 1 0`
- `execute as CursorBot at @s run playsound minecraft:block.note_block.bell master CursorBot ~ ~ ~ 1 1 0`
- `execute as CursorBot at @s run playsound minecraft:entity.experience_orb.pickup master CursorBot ~ ~ ~ 1 1 0`
## Explosion coverage
- `execute as CursorBot at @s run summon minecraft:tnt ~3 ~ ~`
- `execute as CursorBot at @s run summon minecraft:tnt ~6 ~ ~`
## Kill and respawn cycle
- `kill CursorBot` (via RCON, requires survival mode)
- `respawn` (via MCC command after death)

View file

@ -155,6 +155,7 @@ run_server_command "time set day"
run_server_command "weather clear"
sleep 2
# ── Phase 1: Basic status and info commands ──
run_mcc_command "health"
run_mcc_command "list"
run_mcc_command "inventory player list"
@ -165,6 +166,34 @@ run_mcc_command "entity"
run_mcc_command "/time query daytime"
run_mcc_command "smoke_test_from_mcc_full_spectrum"
# ── Phase 2: Movement and look commands ──
run_mcc_command "/tp CursorBot 0 -60 0"
sleep 3
run_mcc_command "look up"
sleep 1
run_mcc_command "look down"
sleep 1
run_mcc_command "look east"
sleep 1
# ── Phase 3: Advanced inventory operations ──
run_mcc_command "inventory creativegive 37 IronSword 1"
run_mcc_command "inventory creativegive 38 GoldenApple 8"
run_mcc_command "inventory player list"
run_mcc_command "inventory creativeclear 38"
run_mcc_command "inventory player list"
# ── Phase 4: Block placement and interaction ──
run_server_command "execute as CursorBot at @s run fill ~1 ~ ~1 ~3 ~2 ~3 minecraft:stone"
sleep 2
run_server_command "execute as CursorBot at @s run setblock ~5 ~ ~5 minecraft:chest"
sleep 1
run_server_command "execute as CursorBot at @s run setblock ~5 ~1 ~5 minecraft:furnace"
sleep 1
run_server_command "execute as CursorBot at @s run setblock ~6 ~ ~5 minecraft:crafting_table"
sleep 1
# ── Phase 5: Entity spawning (expanded coverage) ──
run_server_command "execute as CursorBot at @s run summon minecraft:cow ~2 ~ ~"
run_server_command "execute as CursorBot at @s run summon minecraft:zombie ~4 ~ ~"
run_server_command "execute as CursorBot at @s run summon minecraft:creeper ~6 ~ ~"
@ -172,41 +201,99 @@ run_server_command "execute as CursorBot at @s run summon minecraft:skeleton ~8
run_server_command "execute as CursorBot at @s run summon minecraft:villager ~-2 ~ ~"
run_server_command "execute as CursorBot at @s run summon minecraft:allay ~-4 ~ ~"
run_server_command "execute as CursorBot at @s run summon minecraft:armor_stand ~ ~ ~2"
run_server_command "execute as CursorBot at @s run summon minecraft:item_display ~-6 ~ ~ {item:{id:\"minecraft:diamond\",count:1}}"
run_server_command "execute as CursorBot at @s run summon minecraft:spider ~10 ~ ~"
run_server_command "execute as CursorBot at @s run summon minecraft:pig ~-8 ~ ~"
sleep 2
run_mcc_command "entity"
# ── Phase 6: Effects and environment ──
run_server_command "effect give CursorBot minecraft:speed 30 1"
sleep 2
run_mcc_command "health"
run_server_command "effect give CursorBot minecraft:regeneration 10 1"
sleep 2
run_mcc_command "health"
# ── Phase 7: Gamemode cycling ──
run_mcc_command "/gamemode survival"
sleep 2
run_mcc_command "health"
run_mcc_command "/gamemode creative"
sleep 2
# ── Phase 8: Dimension change (nether) ──
run_server_command "execute in minecraft:the_nether run tp CursorBot 0 64 0"
sleep 4
run_mcc_command "health"
run_server_command "execute in minecraft:overworld run tp CursorBot 0 -60 0"
sleep 4
# ── Phase 9: Server chat and whisper ──
run_server_command "say Hello from the server console"
sleep 2
run_server_command "msg CursorBot This is a private whisper"
sleep 2
run_mcc_command "integration_test_chat_response"
# ── Phase 10: Particles, sounds, and explosions ──
run_server_command "execute as CursorBot at @s run particle minecraft:happy_villager ~ ~1 ~ 0.5 0.5 0.5 0 12 force"
run_server_command "execute as CursorBot at @s run particle minecraft:end_rod ~ ~1 ~ 0.5 0.5 0.5 0.01 20 force"
run_server_command "execute as CursorBot at @s run particle minecraft:explosion ~ ~1 ~ 0 0 0 0 1 force"
run_server_command "execute as CursorBot at @s run particle minecraft:totem_of_undying ~ ~1 ~ 0.5 0.5 0.5 0.1 20 force"
run_server_command "execute as CursorBot at @s run particle minecraft:flame ~ ~1 ~ 0.2 0.2 0.2 0.02 30 force"
run_server_command "execute as CursorBot at @s run particle minecraft:heart ~ ~2 ~ 0.3 0.3 0.3 0 5 force"
run_server_command "execute as CursorBot at @s run playsound minecraft:entity.lightning_bolt.thunder master CursorBot ~ ~ ~ 1 1 0"
run_server_command "execute as CursorBot at @s run playsound minecraft:block.note_block.bell master CursorBot ~ ~ ~ 1 1 0"
run_server_command "execute as CursorBot at @s run playsound minecraft:entity.experience_orb.pickup master CursorBot ~ ~ ~ 1 1 0"
run_server_command "execute as CursorBot at @s run summon minecraft:tnt ~3 ~ ~"
sleep 2
run_server_command "execute as CursorBot at @s run summon minecraft:tnt ~6 ~ ~"
# ── Phase 11: Kill and respawn cycle ──
run_mcc_command "/gamemode survival"
sleep 2
run_server_command "kill CursorBot"
sleep 4
run_mcc_command "respawn"
sleep 4
run_mcc_command "health"
run_mcc_command "/gamemode creative"
sleep 2
sleep 6
capture_server_logs
# ── Assertions: MCC log ──
assert_contains "$MCC_LOG" "Server was successfully joined." "MCC never joined the server"
assert_contains "$MCC_LOG" "[FileInput] > inventory player list" "Inventory command was not executed"
assert_contains "$MCC_LOG" "[FileInput] > entity" "Entity command was not executed"
assert_contains "$MCC_LOG" "[FileInput] > /gamemode creative" "Creative mode command was not executed from MCC"
assert_contains "$MCC_LOG" "Requested Diamond x16 in slot #36" "Creative inventory give did not succeed"
assert_contains "$MCC_LOG" "smoke_test_from_mcc_full_spectrum" "Client-originated chat was not observed"
assert_contains "$MCC_LOG" "[FileInput] > look up" "Look command was not executed"
assert_contains "$MCC_LOG" "[FileInput] > /gamemode survival" "Survival mode switch was not executed"
assert_contains "$MCC_LOG" "[FileInput] > respawn" "Respawn command was not executed"
assert_contains "$MCC_LOG" "[FileInput] > health" "Health command was not executed"
assert_contains "$MCC_LOG" "integration_test_chat_response" "Chat response test message was not observed"
assert_not_contains "$MCC_LOG" "Please enable InventoryHandling" "Inventory handling is still disabled"
assert_not_contains "$MCC_LOG" "Please enable EntityHandling" "Entity handling is still disabled"
assert_not_contains "$MCC_LOG" "You must be in Creative gamemode" "Creative mode was not active when creativegive ran"
assert_not_contains "$MCC_LOG" "Failed to load settings" "MCC failed to reload its config"
assert_not_contains "$MCC_LOG" "NullReferenceException" "A NullReferenceException occurred during the test"
# ── Assertions: Server log ──
assert_contains "$SERVER_FILE_LOG" "CursorBot joined the game" "Server never saw CursorBot join"
assert_contains "$SERVER_FILE_LOG" "smoke_test_from_mcc_full_spectrum" "Server never received the client chat message"
assert_contains "$SERVER_FILE_LOG" "Displaying particle minecraft:happy_villager" "Particle events were not recorded on the server"
assert_contains "$SERVER_FILE_LOG" "Played sound minecraft:block.note_block.bell to CursorBot" "Sound events were not recorded on the server"
assert_contains "$SERVER_FILE_LOG" "Summoned new Primed TNT" "TNT summon did not occur on the server"
assert_contains "$SERVER_FILE_LOG" "integration_test_chat_response" "Server never received the chat response test message"
assert_contains "$SERVER_FILE_LOG" "Hello from the server console" "Server say command was not logged"
assert_contains "$SERVER_FILE_LOG" "Killed CursorBot" "Server kill command did not execute"
assert_not_contains "$SERVER_FILE_LOG" "Sending unknown packet 'clientbound/minecraft:disconnect'" "Server hit the disconnect packet regression during the test"
cat <<EOF

View file

@ -58,6 +58,9 @@ namespace MinecraftClient.ChatBots
[TomlInlineComment("$ChatBot.DiscordBridge.MessageSendTimeout$")]
public int Message_Send_Timeout = 3;
[TomlInlineComment("$ChatBot.DiscordBridge.AllowOtherBotMessages$")]
public bool Allow_Other_Bot_Messages = false;
[TomlPrecedingComment("$ChatBot.DiscordBridge.Formats$")]
public string PrivateMessageFormat = "**[Private Message]** {username}: {message}";
public string PublicMessageFormat = "{username}: {message}";
@ -372,7 +375,8 @@ namespace MinecraftClient.ChatBots
if (e.Channel.Id != Config.ChannelId)
return;
if (!Config.OwnersIds.Contains(e.Author.Id))
// Always ignore own messages to prevent loops
if (e.Author.Id == discordBotClient.CurrentUser.Id)
return;
string message = e.Message.Content.Trim();
@ -380,6 +384,17 @@ namespace MinecraftClient.ChatBots
if (string.IsNullOrEmpty(message) || string.IsNullOrWhiteSpace(message))
return;
// Relay messages from other bots when configured, but never process commands from them
if (e.Author.IsBot)
{
if (Config.Allow_Other_Bot_Messages && bridgeDirection != BridgeDirection.Discord)
SendText(message);
return;
}
if (!Config.OwnersIds.Contains(e.Author.Id))
return;
if (bridgeDirection == BridgeDirection.Discord)
{
if (!message.StartsWith(".dscbridge"))

View file

@ -772,7 +772,16 @@ namespace MinecraftClient {
}
/// <summary>
/// Looks up a localized string similar to Automatically farms crops for you (plants, breaks and bonemeals them).
/// Looks up a localized string similar to When enabled, messages from other Discord bots in the channel will be relayed to Minecraft chat..
/// </summary>
internal static string ChatBot_DiscordBridge_AllowOtherBotMessages {
get {
return ResourceManager.GetString("ChatBot.DiscordBridge.AllowOtherBotMessages", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Automatically farms cropsfor you (plants, breaks and bonemeals them).
///Crop types available: Beetroot, Carrot, Melon, Netherwart, Pumpkin, Potato, Wheat.
///Usage: &quot;/farmer start&quot; command and &quot;/farmer stop&quot; command.
///NOTE: This a newly added bot, it is not perfect and was only tested in 1.19.2, there are some minor issues like not being able to bonemeal carrots/potatoes sometimes.
@ -899,7 +908,9 @@ namespace MinecraftClient {
}
/// <summary>
/// Looks up a localized string similar to Show a Discord Rich Presence status with your current Minecraft session info..
/// Looks up a localized string similar to Show a Discord Rich Presence status with your current Minecraft session info.
///Setup:
///1. Go to https://discord.com/developers/applications and log in with your Discord account. [rest of string was truncated]&quot;;.
/// </summary>
internal static string ChatBot_DiscordRpc {
get {

View file

@ -390,6 +390,9 @@ For Discord message formatting, check the following: https://mccteam.github.io/r
<data name="ChatBot.DiscordBridge.Token" xml:space="preserve">
<value>Your Discord Bot token.</value>
</data>
<data name="ChatBot.DiscordBridge.AllowOtherBotMessages" xml:space="preserve">
<value>When enabled, messages from other Discord bots in the channel will be relayed to Minecraft chat. The bridge always ignores its own messages to prevent loops.</value>
</data>
<data name="ChatBot.Farmer" xml:space="preserve">
<value>Automatically farms crops for you (plants, breaks and bonemeals them).
Crop types available: Beetroot, Carrot, Melon, Netherwart, Pumpkin, Potato, Wheat.
@ -829,7 +832,13 @@ If the connection to the Minecraft game server is blocked by the firewall, set E
<value>A Chat Bot that collects items on the ground</value>
</data>
<data name="ChatBot.DiscordRpc" xml:space="preserve">
<value>Show a Discord Rich Presence status with your current Minecraft session info.\nRequires a Discord Application ID from https://discord.com/developers/applications\nYou can customize what is shown using placeholders: {server_host}, {server_port}, {username}, {health}, {max_health}, {food}, {dimension}, {gamemode}, {x}, {y}, {z}, {player_count}, {protocol}</value>
<value>Show a Discord Rich Presence status with your current Minecraft session info.
Setup:
1. Go to https://discord.com/developers/applications and log in with your Discord account.
2. Click "New Application", give it a name (e.g. "MCC") and confirm.
3. On the application page, copy the "Application ID" and paste it in the "ApplicationId" field below.
4. (Optional) Go to "Rich Presence" -&gt; "Art Assets" to upload custom images for LargeImageKey/SmallImageKey.
Note: This does NOT require a Bot Token, only an Application ID. Discord must be running on the same machine as MCC.</value>
</data>
<data name="ChatBot.DiscordRpc.ApplicationId" xml:space="preserve">
<value>Your Discord Application ID. Create one at https://discord.com/developers/applications</value>