Skip to content

Commit f4cdd69

Browse files
Detect Player Blocks
1 parent 1a437cd commit f4cdd69

7 files changed

Lines changed: 181 additions & 3 deletions

File tree

.github/workflows/build-dev.yml

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
name: Build Development
2+
3+
on:
4+
push:
5+
branches: [main]
6+
7+
permissions:
8+
contents: write
9+
10+
jobs:
11+
build:
12+
runs-on: ubuntu-latest
13+
steps:
14+
- name: Checkout repository
15+
uses: actions/checkout@v4
16+
17+
- name: Set up JDK 21
18+
uses: actions/setup-java@v4
19+
with:
20+
java-version: 21
21+
distribution: temurin
22+
23+
- name: Download GriefPrevention
24+
run: curl -o GriefPrevention.jar https://api.serversmp.xyz/upload/69f601b7986e1f4bd6f7cf46.jar
25+
26+
- name: Download LogBlock
27+
run: curl -o LogBlock.jar https://api.serversmp.xyz/upload/69f601b7a022054e454d11a4.jar
28+
29+
- name: Install GriefPrevention to Maven
30+
run: mvn install:install-file -Dfile=GriefPrevention.jar -DgroupId=com.github.TechFortress -DartifactId=GriefPrevention -Dversion=16.16.0 -Dpackaging=jar -DgeneratePom=true
31+
32+
- name: Install LogBlock to Maven
33+
run: mvn install:install-file -Dfile=LogBlock.jar -DgroupId=de.diddiz -DartifactId=logblock -Dversion=1.16.1.2-SNAPSHOT -Dpackaging=jar -DgeneratePom=true
34+
35+
- name: Build
36+
run: mvn package --file pom.xml
37+
38+
- name: Extract repository name
39+
run: echo "NAME=$(basename ${{ github.repository }})" >> $GITHUB_ENV
40+
41+
- name: Upload Build
42+
uses: marvinpinto/action-automatic-releases@master
43+
with:
44+
title: "${{ env.NAME }}"
45+
automatic_release_tag: "latest"
46+
repo_token: "${{ secrets.GITHUB_TOKEN }}"
47+
files: "target/*.jar"
48+
prerelease: false

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,4 @@
99
/.apt_generated/
1010
/.apt_generated_tests/
1111
dependency-reduced-pom.xml
12+
.DS_Store

src/main/java/com/syntaxphoenix/spigot/smoothtimber/SmoothTimber.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,12 @@
1313
import com.syntaxphoenix.spigot.smoothtimber.event.AsyncPlayerChoppedTreeEvent;
1414
import com.syntaxphoenix.spigot.smoothtimber.event.AsyncPlayerTreeFallEvent;
1515
import com.syntaxphoenix.spigot.smoothtimber.event.reason.DefaultReason;
16+
import com.syntaxphoenix.spigot.smoothtimber.listener.BlockPlaceListener;
1617
import com.syntaxphoenix.spigot.smoothtimber.platform.Platform;
1718
import com.syntaxphoenix.spigot.smoothtimber.toggle.ToggleStorage;
1819
import com.syntaxphoenix.spigot.smoothtimber.utilities.PluginUtils;
1920
import com.syntaxphoenix.spigot.smoothtimber.utilities.cooldown.CooldownHelper;
21+
import com.syntaxphoenix.spigot.smoothtimber.utilities.locate.PlacedBlockTracker;
2022
import com.syntaxphoenix.spigot.smoothtimber.version.manager.VersionChanger;
2123
import com.syntaxphoenix.syntaxapi.command.CommandManager;
2224
import com.syntaxphoenix.syntaxapi.reflection.ClassCache;
@@ -38,6 +40,8 @@ public void onEnable() {
3840
return;
3941
}
4042
STORAGE = new ToggleStorage();
43+
PlacedBlockTracker.setup(this);
44+
getServer().getPluginManager().registerEvents(new BlockPlaceListener(), this);
4145

4246
// Load classes
4347
CooldownHelper.class.getClass();

src/main/java/com/syntaxphoenix/spigot/smoothtimber/listener/BlockBreakListener.java

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,10 @@ public void onBlockBreak(final BlockBreakEvent event) {
4545
}
4646

4747
final Player player = event.getPlayer();
48+
final Location location = event.getBlock().getLocation();
49+
50+
// Check if block is player-placed (PDC automatically removes the marker when block is destroyed)
51+
final boolean isPlaced = Locator.isPlayerPlaced(location);
4852

4953
if (!PlayerState.isPermitted(player)) {
5054
return;
@@ -77,8 +81,7 @@ public void onBlockBreak(final BlockBreakEvent event) {
7781
if (!change.hasPermissionForCuttingItem(player, tool)) {
7882
return;
7983
}
80-
final Location location = event.getBlock().getLocation();
81-
if (Locator.isPlayerPlaced(location)) {
84+
if (isPlaced) {
8285
return;
8386
}
8487
event.setCancelled(true);
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
package com.syntaxphoenix.spigot.smoothtimber.listener;
2+
3+
import org.bukkit.event.EventHandler;
4+
import org.bukkit.event.EventPriority;
5+
import org.bukkit.event.Listener;
6+
import org.bukkit.event.block.BlockPlaceEvent;
7+
8+
import com.syntaxphoenix.spigot.smoothtimber.utilities.PluginUtils;
9+
import com.syntaxphoenix.spigot.smoothtimber.utilities.locate.PlacedBlockTracker;
10+
import com.syntaxphoenix.spigot.smoothtimber.version.manager.VersionChanger;
11+
12+
public class BlockPlaceListener implements Listener {
13+
14+
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
15+
public void onBlockPlace(BlockPlaceEvent event) {
16+
VersionChanger changer = PluginUtils.CHANGER;
17+
if (changer.isWoodBlock(event.getBlock().getState())) {
18+
PlacedBlockTracker.markAsPlaced(event.getBlock());
19+
}
20+
}
21+
}

src/main/java/com/syntaxphoenix/spigot/smoothtimber/utilities/locate/DefaultResolver.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ public List<Location> resolve(final Location start, final int radius, final List
4747

4848
@Override
4949
public boolean isPlayerPlaced(final Location location) {
50-
return false;
50+
return PlacedBlockTracker.isPlayerPlaced(location);
5151
}
5252

5353
}
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
package com.syntaxphoenix.spigot.smoothtimber.utilities.locate;
2+
3+
import org.bukkit.Chunk;
4+
import org.bukkit.Location;
5+
import org.bukkit.NamespacedKey;
6+
import org.bukkit.block.Block;
7+
import org.bukkit.persistence.PersistentDataType;
8+
import org.bukkit.plugin.Plugin;
9+
10+
/**
11+
* Tracks player-placed blocks using Minecraft's PersistentDataContainer (PDC).
12+
* This approach stores metadata directly on blocks, which automatically handles:
13+
* - Block removal (any method: player break, explosion, fire, WorldEdit, etc.)
14+
* - Server restarts (data persists in chunk files)
15+
* - No memory overhead or file I/O
16+
* - No cleanup needed
17+
*/
18+
public class PlacedBlockTracker {
19+
20+
private static NamespacedKey PLAYER_PLACED_KEY;
21+
22+
public static void setup(Plugin plugin) {
23+
PLAYER_PLACED_KEY = new NamespacedKey(plugin, "player_placed");
24+
}
25+
26+
/**
27+
* Marks a block as player-placed.
28+
* @param block The block to mark
29+
*/
30+
public static void markAsPlaced(Block block) {
31+
if (PLAYER_PLACED_KEY == null) {
32+
return;
33+
}
34+
block.getChunk().getPersistentDataContainer().set(
35+
getChunkKey(block.getLocation()),
36+
PersistentDataType.BYTE,
37+
(byte) 1
38+
);
39+
}
40+
41+
/**
42+
* Gets a unique key for a block location within a chunk.
43+
*/
44+
private static NamespacedKey getChunkKey(Location location) {
45+
String key = "player_placed_" + location.getBlockX() + "_" + location.getBlockY() + "_" + location.getBlockZ();
46+
return new NamespacedKey(PLAYER_PLACED_KEY.getNamespace(), key);
47+
}
48+
49+
/**
50+
* Marks a block at the given location as player-placed.
51+
* @param location The location of the block to mark
52+
*/
53+
public static void markAsPlaced(Location location) {
54+
markAsPlaced(location.getBlock());
55+
}
56+
57+
/**
58+
* Checks if a block was placed by a player.
59+
* @param block The block to check
60+
* @return true if the block was placed by a player, false otherwise
61+
*/
62+
public static boolean isPlayerPlaced(Block block) {
63+
if (PLAYER_PLACED_KEY == null) {
64+
return false;
65+
}
66+
return block.getChunk().getPersistentDataContainer().has(
67+
getChunkKey(block.getLocation()),
68+
PersistentDataType.BYTE
69+
);
70+
}
71+
72+
/**
73+
* Checks if a block at the given location was placed by a player.
74+
* @param location The location of the block to check
75+
* @return true if the block was placed by a player, false otherwise
76+
*/
77+
public static boolean isPlayerPlaced(Location location) {
78+
return isPlayerPlaced(location.getBlock());
79+
}
80+
81+
/**
82+
* Removes the player-placed marker from a block.
83+
* Note: This is usually not needed as the PDC is automatically removed when blocks are destroyed.
84+
* @param block The block to unmark
85+
*/
86+
public static void unmark(Block block) {
87+
if (PLAYER_PLACED_KEY == null) {
88+
return;
89+
}
90+
block.getChunk().getPersistentDataContainer().remove(getChunkKey(block.getLocation()));
91+
}
92+
93+
/**
94+
* Removes the player-placed marker from a block at the given location.
95+
* Note: This is usually not needed as the PDC is automatically removed when blocks are destroyed.
96+
* @param location The location of the block to unmark
97+
*/
98+
public static void unmark(Location location) {
99+
unmark(location.getBlock());
100+
}
101+
}

0 commit comments

Comments
 (0)