Skip to content

Commit f280318

Browse files
committed
fix: wind charge PVP protection, copper golem trust, and visualization improvements
- Add wind charge knockback and damage protection in PVP-safe claims - Treat copper golems as animals requiring container trust - Fix visualization surface detection to always search from world surface downward for consistent results - Add leaves to transparent block list for better visualization placement - Replace sendMessage with sendRateLimitedErrorMessage for PVP messages to reduce spam - Version bump to 17.0.10
1 parent ba710b9 commit f280318

File tree

4 files changed

+76
-11
lines changed

4 files changed

+76
-11
lines changed

pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
<groupId>com.griefprevention</groupId>
66
<artifactId>GriefPrevention</artifactId>
7-
<version>17.0.8</version>
7+
<version>17.0.10</version>
88

99
<name>GriefPrevention</name>
1010
<description>The official self-service anti-griefing Bukkit plugin for Minecraft servers since 2011.</description>

src/main/java/com/griefprevention/visualization/impl/FakeBlockVisualization.java

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -283,13 +283,17 @@ protected void onElementAdded(@NotNull IntVector location, @NotNull BlockData fa
283283

284284
/**
285285
* Find a location that should be visible to players. This causes the visualization to "cling" to the ground.
286+
* Always searches from the world surface downward to ensure consistent results regardless of player position.
286287
*
287288
* @param vector the {@link IntVector} of the display location
288289
* @return the located {@link Block}
289290
*/
290291
protected @NotNull Block getVisibleLocation(@NotNull IntVector vector)
291292
{
292-
Block start = vector.toBlock(world);
293+
// Always start from the highest block at this X,Z to ensure consistent surface detection
294+
// regardless of whether the player is underground or on the surface
295+
int highestY = world.getHighestBlockYAt(vector.x(), vector.z());
296+
Block start = world.getBlockAt(vector.x(), highestY, vector.z());
293297
return snapToSurface(start);
294298
}
295299

@@ -301,6 +305,7 @@ protected void onElementAdded(@NotNull IntVector location, @NotNull BlockData fa
301305
Block column = start;
302306

303307
// Step upward until we reach an open cell (air/liquid depending on context).
308+
// This handles cases where getHighestBlockYAt returns a block below overhangs.
304309
while (!isTransparent(column) && column.getY() < maxY)
305310
{
306311
column = column.getRelative(BlockFace.UP);
@@ -456,7 +461,8 @@ protected boolean isTransparent(@NotNull Block block)
456461
|| Tag.FENCE_GATES.isTagged(blockMaterial)
457462
|| Tag.SIGNS.isTagged(blockMaterial)
458463
|| Tag.WALLS.isTagged(blockMaterial)
459-
|| Tag.WALL_SIGNS.isTagged(blockMaterial))
464+
|| Tag.WALL_SIGNS.isTagged(blockMaterial)
465+
|| Tag.LEAVES.isTagged(blockMaterial))
460466
return true;
461467

462468
return block.getType().isTransparent();

src/main/java/me/ryanhamshire/GriefPrevention/EntityDamageHandler.java

Lines changed: 64 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
import org.bukkit.event.entity.EntityCombustEvent;
4343
import org.bukkit.event.entity.EntityDamageByEntityEvent;
4444
import org.bukkit.event.entity.EntityDamageEvent;
45+
import org.bukkit.event.entity.EntityKnockbackByEntityEvent;
4546
import org.bukkit.event.entity.EntityTargetEvent;
4647
import org.bukkit.event.entity.PotionSplashEvent;
4748
import org.bukkit.event.vehicle.VehicleDamageEvent;
@@ -117,6 +118,46 @@ public void onEntityCombustByEntity(@NotNull EntityCombustByEntityEvent event) {
117118
this.handleEntityDamageEvent(new EntityDamageInstance(event), false);
118119
}
119120

121+
// Handle wind charge knockback - wind charges deal knockback separately from damage
122+
@EventHandler(ignoreCancelled = true, priority = EventPriority.LOWEST)
123+
public void onEntityKnockbackByEntity(@NotNull EntityKnockbackByEntityEvent event) {
124+
// Only handle wind charge knockback on players
125+
if (!(event.getEntity() instanceof Player defender))
126+
return;
127+
128+
Entity source = event.getSourceEntity();
129+
if (source == null)
130+
return;
131+
132+
// Check if the source is a wind charge
133+
String sourceTypeName = source.getType().name();
134+
if (!sourceTypeName.contains("WIND_CHARGE"))
135+
return;
136+
137+
// Get the player who threw the wind charge
138+
Player attacker = null;
139+
if (source instanceof Projectile projectile && projectile.getShooter() instanceof Player shooter) {
140+
attacker = shooter;
141+
}
142+
143+
// Allow self-knockback (e.g., for movement tricks)
144+
if (attacker == null || attacker == defender)
145+
return;
146+
147+
// Only protect when PVP rules are enabled for this world
148+
if (!instance.pvpRulesApply(defender.getWorld()))
149+
return;
150+
151+
// Check if defender is in a PVP-protected claim
152+
PlayerData defenderData = dataStore.getPlayerData(defender.getUniqueId());
153+
Claim claim = dataStore.getClaimAt(defender.getLocation(), false, defenderData.lastClaim);
154+
if (claim != null && instance.claimIsPvPSafeZone(claim)) {
155+
defenderData.lastClaim = claim;
156+
event.setCancelled(true);
157+
GriefPrevention.sendRateLimitedErrorMessage(attacker, Messages.CantFightWhileImmune);
158+
}
159+
}
160+
120161
private void handleEntityDamageEvent(@NotNull EntityDamageInstance event, boolean sendMessages) {
121162
// monsters are never protected
122163
if (isHostile(event.damaged()))
@@ -171,6 +212,25 @@ private void handleEntityDamageEvent(@NotNull EntityDamageInstance event, boolea
171212
}
172213
}
173214

215+
// Handle wind charge damage - wind charges are projectiles that deal knockback and damage
216+
// When PVP rules are enabled for the world, protect players in claims from wind charge attacks
217+
String damagerTypeName = damageSource != null ? damageSource.getType().name() : "";
218+
if (damagerTypeName.contains("WIND_CHARGE") && event.damaged() instanceof Player defender) {
219+
if (attacker != null && attacker != defender && instance.pvpRulesApply(defender.getWorld())) {
220+
// Check if defender is in a protected claim
221+
PlayerData defenderData = dataStore.getPlayerData(defender.getUniqueId());
222+
Claim claim = dataStore.getClaimAt(defender.getLocation(), false, defenderData.lastClaim);
223+
if (claim != null && instance.claimIsPvPSafeZone(claim)) {
224+
defenderData.lastClaim = claim;
225+
event.setCancelled(true);
226+
if (sendMessages) {
227+
GriefPrevention.sendRateLimitedErrorMessage(attacker, Messages.CantFightWhileImmune);
228+
}
229+
return;
230+
}
231+
}
232+
}
233+
174234
// Specific handling for PVP-enabled situations.
175235
if (instance.pvpRulesApply(event.damaged().getWorld())) {
176236
if (event.damaged() instanceof Player defender) {
@@ -378,9 +438,8 @@ private boolean handlePvpDamageByPlayer(
378438
if (attackerData.pvpImmune || defenderData.pvpImmune) {
379439
event.setCancelled(true);
380440
if (sendMessages)
381-
GriefPrevention.sendMessage(
441+
GriefPrevention.sendRateLimitedErrorMessage(
382442
attacker,
383-
TextMode.Err,
384443
attackerData.pvpImmune ? Messages.CantFightWhileImmune : Messages.ThatPlayerPvPImmune);
385444
return true;
386445
}
@@ -397,7 +456,7 @@ private boolean handlePvpDamageByPlayer(
397456
Consumer<Messages> cancelHandler = message -> {
398457
event.setCancelled(true);
399458
if (sendMessages)
400-
GriefPrevention.sendMessage(attacker, TextMode.Err, message);
459+
GriefPrevention.sendRateLimitedErrorMessage(attacker, message);
401460
};
402461
// Return whether PVP is handled by a claim at the attacker or defender's
403462
// locations.
@@ -472,7 +531,7 @@ private boolean handlePvpPetDamageByPlayer(
472531
if (attackerData.pvpImmune) {
473532
event.setCancelled(true);
474533
if (sendMessages)
475-
GriefPrevention.sendMessage(attacker, TextMode.Err, Messages.CantFightWhileImmune);
534+
GriefPrevention.sendRateLimitedErrorMessage(attacker, Messages.CantFightWhileImmune);
476535
return true;
477536
}
478537

@@ -1091,7 +1150,7 @@ public void onPotionSplash(@NotNull PotionSplashEvent event) {
10911150
Consumer<Messages> cancelHandler = message -> {
10921151
event.setIntensity(affected, 0);
10931152
if (messagedPlayer.compareAndSet(false, true))
1094-
GriefPrevention.sendMessage(thrower, TextMode.Err, message);
1153+
GriefPrevention.sendRateLimitedErrorMessage(thrower, message);
10951154
};
10961155
if (handlePvpInClaim(thrower, affectedPlayer, thrower.getLocation(), playerData,
10971156
() -> cancelHandler.accept(Messages.CantFightWhileImmune))) {

src/main/java/me/ryanhamshire/GriefPrevention/PlayerEventHandler.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1326,8 +1326,8 @@ public void onPlayerInteractEntity(PlayerInteractEntityEvent event)
13261326
}
13271327
}
13281328

1329-
//if the entity is an animal, apply container rules
1330-
if ((instance.config_claims_preventTheft && (entity instanceof Animals || entity instanceof Fish)) || (entity.getType() == EntityType.VILLAGER && instance.config_claims_villagerTradingRequiresTrust))
1329+
//if the entity is an animal or copper golem, apply container rules
1330+
if ((instance.config_claims_preventTheft && (entity instanceof Animals || entity instanceof Fish || entity.getType().name().equals("COPPER_GOLEM"))) || (entity.getType() == EntityType.VILLAGER && instance.config_claims_villagerTradingRequiresTrust))
13311331
{
13321332
//if the entity is in a claim
13331333
Claim claim = this.dataStore.getClaimAt(entity.getLocation(), false, null);
@@ -1352,7 +1352,7 @@ public void onPlayerInteractEntity(PlayerInteractEntityEvent event)
13521352
}
13531353

13541354
ItemStack itemInHand = instance.getItemInHand(player, event.getHand());
1355-
1355+
13561356
//if preventing theft, prevent leashing claimed creatures and boats
13571357
if (instance.config_claims_preventTheft && itemInHand.getType() == Material.LEAD)
13581358
{

0 commit comments

Comments
 (0)