4

So I want to detect if any player breaks any block using command blocks in Minecraft. I already know how to detect for specific blocks using scoreboards and the minecraft.mined criteria, but not ANY block.

1
  • I dont really know much about this but maybe try specifying * as the block – Penguin May 19 at 20:46
1

Unfortunately, I don't think this is possible with vanilla command blocks although you could achieve this in a hacky way. The /cloning would probably create lots of lag, however. It would only look at blocks around the player, not if they shot an flame arrow at tnt far away. Also, it would find blocks other players, or an enderman or player moved or the player placed.

According to the fandom page on /clone you can test whether a player has broken any blocks by /cloning the region the player is in to another location far away, then /cloning the same region. If any blocks have changed, it should result in the number of blocks that were changed.

You could practically achieve this with command blocks. Every so often, spawn an invisible entity that floats where the player is, and clone a region of a set size around the player. After some time, clone that same regen around the spawned entity into the location you spawned the first one into and store the result into a variable with /execute store result. That variable is how many blocks were changed, whether created or destroyed.

There might also be a way to do a for loop for every block in a region to see if it was mined, although I do not know how to do it.

1

You could modify all of the block's loot tables to drop a dummy item. You can then use the minecraft:copy_nbt loot table function (or, as of latest terms, 'item modifier') to copy the player's UUID, specifically the one who mined the block. Here's an example:

{
    "type": "minecraft:block",
    "pools": [
        {
            "rolls": 1,
            "entries": [
                {
                    "type": "minecraft:item",
                    "name": "minecraft:dirt"
                }
            ],
            "conditions": [
                {
                    "condition": "minecraft:survives_explosion"
                }
            ]
        },
        {
            "rolls": 1,
            "entries": [
                {
                    "type": "minecraft:item",
                    "name": "minecraft:barrier"
                }
            ],
            "functions": [
                {
                    "function": "minecraft:copy_nbt",
                    "source": "this",
                    "ops": [
                        {
                            "source": "UUID",
                            "target": "PlayerUUID",
                            "op": "replace"
                        }
                    ]
                },
                {
                    "function": "minecraft:set_nbt",
                    "tag": "{custom_item: 1b}"
                }
            ]
        }
    ]
}

In this example, we're overriding the dirt.json loot table inside the data\minecraft\loot_tables\blocks folder to make it drop a Barrier item that has the custom_item: 1b and PlayerUUID NBT. The PlayerUUID NBT would store the UUID of the player that has mined the block since we've set the value for the source field inside the object for the minecraft:copy_nbt loot table function to "this", which refers to the entity that has mined the block (if the loot table is used by blocks.)

You can then execute as the dummy item, store the stored UUID in its NBT to a scoreboard objective to search for the player that has the matching UUID value. But first, you would need a function that would run every tick.

You can create a 'tick' function by creating an .mcfunction file inside our data\<namespace>\functions folder, and a .json file named tick inside your data\minecraft\tags\functions folder in your datapack, with this being the contents of it:

{
    "values": [
        "namespace:function"
    ]
}
  • namespace:function being the function you've created inside your data\<namespace>\functions folder

After creating the tick function, you would then execute as and at the dummy item that has the custom_item: 1b NBT, like so:

execute as @e[type = item, nbt = {Item: {tag: {custom_item: 1b}}}] at @s

Then, you would create another function and a scoreboard objective. The function will be used for storing the values of the PlayerUUID NBT in the item to a scoreboard objective. Let's say the function will be named check_item_uuid, the command above should now look like so:

execute as @e[type = item, nbt = {Item: {tag: {custom_item: 1b}}}] at @s run function namespace:check_item_uuid

Inside your check_item_uuid function, you would use execute store result score to store the result of the run command to a scoreboard. We'll be using 4 "fake" players (not actual players), since there are 4 elements inside the PlayerUUID list:

# check_item_uuid.mcfunction

# Create a dummy scoreboard objective
scoreboard objectives add uuid_chk dummy


# Store the values of the elements in the `PlayerUUID` NBT of the item to the `uuid_chk` objective
execute store result score $item[0] uuid_chk run data get entity @s Item.tag.PlayerUUID[0]
execute store result score $item[1] uuid_chk run data get entity @s Item.tag.PlayerUUID[1]
execute store result score $item[2] uuid_chk run data get entity @s Item.tag.PlayerUUID[2]
execute store result score $item[3] uuid_chk run data get entity @s Item.tag.PlayerUUID[3]
  • $item[X] being the fake players for each element in the list
  • Item.tag.PlayerUUID is an NBT path that will refer to the item's PlayerUUID NBT, the numbers inside the brackets being the index of the element. Setting it to 0 would select the first element, 1 to select the second element, and so on.

You would then create another function, which would practically do the same as the function above, but for the player instead. We'll name it check_player_uuid. Before tackling the contents of the function, we must first reference the function by executing as all the players before running the function, like so:

# check_item_uuid.mcfunction

# Create a dummy scoreboard objective
scoreboard objectives add uuid_chk dummy


# Store the values of the elements in the `PlayerUUID` NBT of the item to the `uuid_chk` objective
execute store result score $item[0] uuid_chk run data get entity @s Item.tag.PlayerUUID[0]
execute store result score $item[1] uuid_chk run data get entity @s Item.tag.PlayerUUID[1]
execute store result score $item[2] uuid_chk run data get entity @s Item.tag.PlayerUUID[2]
execute store result score $item[3] uuid_chk run data get entity @s Item.tag.PlayerUUID[3]


# Execute as all the players before running the `check_player_uuid` function
execute as @a run function namespace:check_player_uuid 

Now, the contents of the check_player_uuid function will be slightly the same as the check_item_uuid function, but also slightly different. We'll be using different fake players for storing the values of the player's UUID so that we can actually check if they do match, like so:

# Store the values of the elements in the `UUID` NBT of the player to the `uuid_chk` objective
execute store result score $player[0] uuid_chk run data get entity @s UUID[0]
execute store result score $player[1] uuid_chk run data get entity @s UUID[1]
execute store result score $player[2] uuid_chk run data get entity @s UUID[2]
execute store result score $player[3] uuid_chk run data get entity @s UUID[3]
  • $player[X] being the fake players for each element in the UUID list of the player

Afterwards, we'll be using execute if score to check if all of the fake players (for the item and the player) have the same score in the check_player_uuid function, which if they do match, we'll run another function named uuid_match, like so:

# Store the values of the elements in the `UUID` NBT of the player to the `uuid_chk` objective
execute store result score $player[0] uuid_chk run data get entity @s UUID[0]
execute store result score $player[1] uuid_chk run data get entity @s UUID[1]
execute store result score $player[2] uuid_chk run data get entity @s UUID[2]
execute store result score $player[3] uuid_chk run data get entity @s UUID[3]


# Check if all the fake players have the same score
execute if score $item[0] uuid_chk = $player[0] uuid_chk if score $item[1] uuid_chk = $player[1] uuid_chk if score $item[2] uuid_chk = $player[2] uuid_chk if score $item[3] uuid_chk = $player[3] uuid_chk run function namespace:uuid_matched

Inside the uuid_matched function, you can run any commands you wish, which would run the command as the player (e.g: running effect give @s would give the status effect to the player) but at the item (e.g: running setblock ~ ~ ~ air would set a block at the item's position). We will then add a kill command in the check_item_uuid function just after the execute as @a ... command, which will kill the dummy item:

# check_item_uuid.mcfunction

# Create a dummy scoreboard objective
scoreboard objectives add uuid_chk dummy


# Store the values of the elements in the `PlayerUUID` NBT of the item to the `uuid_chk` objective
execute store result score $item[0] uuid_chk run data get entity @s Item.tag.PlayerUUID[0]
execute store result score $item[1] uuid_chk run data get entity @s Item.tag.PlayerUUID[1]
execute store result score $item[2] uuid_chk run data get entity @s Item.tag.PlayerUUID[2]
execute store result score $item[3] uuid_chk run data get entity @s Item.tag.PlayerUUID[3]


# Execute as all the players before running the `check_player_uuid` function
execute as @a run function namespace:check_player_uuid 


# Kill the dummy item
kill @s
5
  • 1
    @ExpertCoder14 Ah, my bad! I'll provide more details and examples. Thank you for the heads up! – eggohito May 20 at 4:31
  • I also have a few queries on the method itself: you are suggesting to use a custom loot table to manipulate the items that the block drops. Will we be able to apply said loot tables to blocks that don't drop anything when mined, such as glass (assuming there is no Silk Touch)? Also, what will happen to the existing loot tables that exist already? Will this method prevent the blocks from dropping their original stuff? If so, can this be worked around also? – ExpertCoder14 May 20 at 4:37
  • @ExpertCoder14 This method requires overriding the block's loot table in the minecraft namespace, but it will still drop its original stuff since we're only adding another pool in the loot table. As for drops for blocks like glass, I'm not entirely sure – eggohito May 20 at 5:13
  • I don't really want to do this since it will take ages to do this for one single world – Crabby Fish May 22 at 12:04
  • Does this mean we will need to do one new file for every single block we want to detect? Does this mean it will not be much better than just a bunch of minecraft.mined scoreboards? – ExpertCoder14 May 25 at 0:47
0

Depending on your situation, this solution could be terrible or exactly what you need.

Since (most) blocks drop an item when mined, you can detect that item.

Here is an example of what I mean:

/execute if entity @e[type=item] run say Block broken!
/kill @e[type=item]

If you don't want the item to disappear, you can do this:

/execute if entity @e[type=item,tag=!found] run say Block broken!
/tag @e[type=item,tag=!found] add found

This has a ton of issues, for example if a player mines a glass block. No item would drop and the block break would not be detected.

If the player simply drops an item, a "block break" would be detected.

If a mob dies and drops items, a "block break" would be detected.

(But who knows, maybe you have a large bedrock room filled with nothing but sand; this solution would work.)

1
  • what about a mob being killed, or a leaf decaying? – Crabby Fish May 22 at 12:00

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.