Skip to content

Commit 76b44f0

Browse files
cryptobenchclaude
andcommitted
Add item pickup protection for claimed areas
Protect flowers, bottles, and other harvestable items from being picked up by untrusted players in claimed chunks. This fixes the reported issue where flowers and bottles were not protected in claims. Changes: - Add ItemPickupProtectionSystem to handle InteractivelyPickupItemEvent - Add cannotPickupItemsHere() protection message - Register the new system in the main plugin class CI/Release improvements: - Update workflow to include commit messages as release notes - Add "What's Changed" section to GitHub releases - Document commit message requirements in CLAUDE.md Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
1 parent 50da051 commit 76b44f0

5 files changed

Lines changed: 167 additions & 36 deletions

File tree

.github/workflows/build.yml

Lines changed: 24 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,11 @@ jobs:
3939
contents: write
4040

4141
steps:
42+
- name: Checkout code
43+
uses: actions/checkout@v4
44+
with:
45+
fetch-depth: 0
46+
4247
- name: Download artifact
4348
uses: actions/download-artifact@v4
4449
with:
@@ -57,6 +62,20 @@ jobs:
5762
id: version
5863
run: echo "VERSION=v1.0.${{ github.run_number }}" >> $GITHUB_OUTPUT
5964

65+
- name: Get commit message
66+
id: commit
67+
run: |
68+
# Get the full commit message (subject + body)
69+
COMMIT_MSG=$(git log -1 --pretty=format:"%B")
70+
# Escape for GitHub Actions output
71+
COMMIT_MSG="${COMMIT_MSG//'%'/'%25'}"
72+
COMMIT_MSG="${COMMIT_MSG//$'\n'/'%0A'}"
73+
COMMIT_MSG="${COMMIT_MSG//$'\r'/'%0D'}"
74+
echo "MESSAGE<<EOF" >> $GITHUB_OUTPUT
75+
git log -1 --pretty=format:"%B" >> $GITHUB_OUTPUT
76+
echo "" >> $GITHUB_OUTPUT
77+
echo "EOF" >> $GITHUB_OUTPUT
78+
6079
- name: Rename JAR
6180
run: mv dist/EasyClaims-*.jar dist/EasyClaims-${{ steps.version.outputs.VERSION }}.jar
6281

@@ -71,46 +90,15 @@ jobs:
7190
**Commit:** `${{ steps.sha.outputs.SHORT_SHA }}`
7291
**Date:** ${{ steps.date.outputs.DATE }}
7392
93+
### What's Changed
94+
${{ steps.commit.outputs.MESSAGE }}
95+
96+
---
97+
7498
### Installation
7599
1. Download `EasyClaims-${{ steps.version.outputs.VERSION }}.jar`
76100
2. Place in your Hytale server's `mods/` folder
77101
3. Restart the server
78-
79-
### Features
80-
- Chunk-based land claiming with visual map integration
81-
- In-game GUI for managing claims and trusted players
82-
- Playtime-based claim limits (more playtime = more claims)
83-
- Trust system with 4 permission levels
84-
- Full block protection for claimed areas
85-
86-
### Commands
87-
| Command | Description |
88-
|---------|-------------|
89-
| `/easyclaims gui` | Open claim map GUI |
90-
| `/easyclaims settings` | Open settings GUI to manage trusted players |
91-
| `/easyclaims claim` | Claim the chunk you're standing in |
92-
| `/easyclaims unclaim` | Unclaim current chunk |
93-
| `/easyclaims list` | List all your claims |
94-
| `/easyclaims trust <player> [level]` | Trust a player (levels: use, container, workstation, build) |
95-
| `/easyclaims untrust <player>` | Remove trust |
96-
| `/easyclaims trustlist` | List trusted players |
97-
| `/easyclaims playtime` | Show your playtime and claim slots |
98-
99-
### Admin Commands
100-
| Command | Description |
101-
|---------|-------------|
102-
| `/easyclaims admin gui` | Open claim manager GUI (admin mode) |
103-
| `/easyclaims admin config` | Show current server settings |
104-
| `/easyclaims admin set starting <n>` | Set starting claims for new players |
105-
| `/easyclaims admin set perhour <n>` | Set claims earned per hour played |
106-
| `/easyclaims admin set max <n>` | Set maximum claims allowed |
107-
| `/easyclaims admin reload` | Reload config from file |
108-
109-
### Permissions
110-
```
111-
perm group add Adventure easyclaims.use
112-
perm group add admin easyclaims.admin
113-
```
114102
files: dist/EasyClaims-${{ steps.version.outputs.VERSION }}.jar
115103
prerelease: false
116104
make_latest: true

CLAUDE.md

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -385,6 +385,40 @@ mvn clean package
385385
# Copy target/PluginName-1.0.0.jar to Server/mods/
386386
```
387387

388+
## Git Commits & Release Notes
389+
390+
**IMPORTANT:** The CI workflow automatically creates GitHub releases from commits to master/main. The commit message is used as the release notes in the "What's Changed" section.
391+
392+
### Commit Message Requirements
393+
When committing changes, write descriptive commit messages that serve as release notes:
394+
395+
```
396+
<Short summary of change>
397+
398+
<Detailed description of what changed and why>
399+
- List specific changes
400+
- Include any breaking changes
401+
- Mention new features or bug fixes
402+
```
403+
404+
### Example Good Commit Message
405+
```
406+
Add item pickup protection for claimed areas
407+
408+
Protect flowers, bottles, and other harvestable items from being picked up
409+
by untrusted players in claimed chunks.
410+
411+
- Add ItemPickupProtectionSystem for InteractivelyPickupItemEvent
412+
- Add cannotPickupItemsHere() message
413+
- Register system in main plugin class
414+
```
415+
416+
### Why This Matters
417+
- Each push to master triggers a new release
418+
- The commit message becomes the release notes
419+
- Users see "What's Changed" in the GitHub release
420+
- Good commit messages = good release documentation
421+
388422
## Key Imports
389423
```java
390424
// Plugin

src/main/java/com/easyclaims/EasyClaims.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
import com.easyclaims.systems.BlockPlaceProtectionSystem;
1616
import com.easyclaims.systems.BlockUseProtectionSystem;
1717
import com.easyclaims.systems.ClaimTitleSystem;
18+
import com.easyclaims.systems.ItemPickupProtectionSystem;
1819
import com.hypixel.hytale.server.core.event.events.player.PlayerConnectEvent;
1920
import com.hypixel.hytale.server.core.event.events.player.PlayerDisconnectEvent;
2021
import com.hypixel.hytale.server.core.plugin.JavaPlugin;
@@ -112,6 +113,7 @@ public void setup() {
112113
getEntityStoreRegistry().registerSystem(new BlockBreakProtectionSystem(claimManager, getLogger()));
113114
getEntityStoreRegistry().registerSystem(new BlockPlaceProtectionSystem(claimManager, getLogger()));
114115
getEntityStoreRegistry().registerSystem(new BlockUseProtectionSystem(claimManager, getLogger()));
116+
getEntityStoreRegistry().registerSystem(new ItemPickupProtectionSystem(claimManager, getLogger()));
115117

116118
// Register claim title system (shows banner when entering/leaving claims)
117119
claimTitleSystem = new ClaimTitleSystem(claimStorage);
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
package com.easyclaims.systems;
2+
3+
import com.hypixel.hytale.component.ArchetypeChunk;
4+
import com.hypixel.hytale.component.CommandBuffer;
5+
import com.hypixel.hytale.component.Ref;
6+
import com.hypixel.hytale.component.Store;
7+
import com.hypixel.hytale.component.dependency.Dependency;
8+
import com.hypixel.hytale.component.dependency.RootDependency;
9+
import com.hypixel.hytale.component.query.Query;
10+
import com.hypixel.hytale.component.system.EntityEventSystem;
11+
import com.hypixel.hytale.logger.HytaleLogger;
12+
import com.hypixel.hytale.math.vector.Vector3d;
13+
import com.hypixel.hytale.server.core.entity.entities.Player;
14+
import com.hypixel.hytale.server.core.event.events.ecs.InteractivelyPickupItemEvent;
15+
import com.hypixel.hytale.server.core.modules.entity.component.TransformComponent;
16+
import com.hypixel.hytale.server.core.universe.PlayerRef;
17+
import com.hypixel.hytale.server.core.universe.world.storage.EntityStore;
18+
import com.easyclaims.data.TrustLevel;
19+
import com.easyclaims.managers.ClaimManager;
20+
import com.easyclaims.util.Messages;
21+
22+
import javax.annotation.Nonnull;
23+
import javax.annotation.Nullable;
24+
import java.util.Collections;
25+
import java.util.Map;
26+
import java.util.Set;
27+
import java.util.UUID;
28+
import java.util.concurrent.ConcurrentHashMap;
29+
30+
/**
31+
* ECS System that intercepts item pickup events to protect claimed areas.
32+
* This prevents players from picking up items (like flowers) in protected chunks.
33+
*/
34+
public class ItemPickupProtectionSystem extends EntityEventSystem<EntityStore, InteractivelyPickupItemEvent> {
35+
36+
private final ClaimManager claimManager;
37+
private final HytaleLogger logger;
38+
39+
// Rate limit messages - don't spam players
40+
private static final Map<UUID, Long> lastMessageTime = new ConcurrentHashMap<>();
41+
private static final long MESSAGE_COOLDOWN_MS = 2000; // 2 seconds
42+
43+
public ItemPickupProtectionSystem(ClaimManager claimManager, HytaleLogger logger) {
44+
super(InteractivelyPickupItemEvent.class);
45+
this.claimManager = claimManager;
46+
this.logger = logger;
47+
}
48+
49+
private boolean canSendMessage(UUID playerId) {
50+
long now = System.currentTimeMillis();
51+
Long lastTime = lastMessageTime.get(playerId);
52+
if (lastTime == null || now - lastTime > MESSAGE_COOLDOWN_MS) {
53+
lastMessageTime.put(playerId, now);
54+
return true;
55+
}
56+
return false;
57+
}
58+
59+
@Nullable
60+
@Override
61+
public Query<EntityStore> getQuery() {
62+
return PlayerRef.getComponentType();
63+
}
64+
65+
@Nonnull
66+
@Override
67+
public Set<Dependency<EntityStore>> getDependencies() {
68+
return Collections.singleton(RootDependency.first());
69+
}
70+
71+
@Override
72+
public void handle(int entityIndex, @Nonnull ArchetypeChunk<EntityStore> chunk, @Nonnull Store<EntityStore> store,
73+
@Nonnull CommandBuffer<EntityStore> commandBuffer, @Nonnull InteractivelyPickupItemEvent event) {
74+
// Get the entity that triggered this event
75+
Ref<EntityStore> entityRef = chunk.getReferenceTo(entityIndex);
76+
if (entityRef == null) return;
77+
78+
// Get player components
79+
Player player = store.getComponent(entityRef, Player.getComponentType());
80+
PlayerRef playerRef = store.getComponent(entityRef, PlayerRef.getComponentType());
81+
if (player == null || playerRef == null) return;
82+
83+
// Get player's position from TransformComponent
84+
TransformComponent transform = store.getComponent(entityRef, TransformComponent.getComponentType());
85+
if (transform == null) return;
86+
87+
Vector3d position = transform.getPosition();
88+
if (position == null) return;
89+
90+
UUID playerId = playerRef.getUuid();
91+
String worldName = player.getWorld().getName();
92+
93+
// Picking up items requires USE trust level (same as basic interaction)
94+
if (!claimManager.hasPermissionAt(playerId, worldName, position.getX(), position.getZ(), TrustLevel.USE)) {
95+
event.setCancelled(true);
96+
if (canSendMessage(playerId)) {
97+
player.sendMessage(Messages.cannotPickupItemsHere());
98+
}
99+
logger.atFine().log("Blocked item pickup: player=%s position=[%.1f, %.1f]",
100+
playerId, position.getX(), position.getZ());
101+
}
102+
}
103+
}

src/main/java/com/easyclaims/util/Messages.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,10 @@ public static Message cannotDamageHere() {
177177
return Message.raw("You cannot damage blocks in this claimed area!").color(RED);
178178
}
179179

180+
public static Message cannotPickupItemsHere() {
181+
return Message.raw("You cannot pick up items in this claimed area!").color(RED);
182+
}
183+
180184
public static Message cannotUseBlock(TrustLevel required) {
181185
String action = switch (required) {
182186
case USE -> "use this";

0 commit comments

Comments
 (0)