Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/nightly.yml
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,7 @@ jobs:
placeholderapi | optional
chunky | optional
worldguard | optional
teams-api | optional
ezcountdown | optional
ezeconomy | optional

Expand Down
1 change: 1 addition & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,7 @@ jobs:
placeholderapi | optional
chunky | optional
worldguard | optional
teams-api | optional
ezcountdown | optional
ezeconomy | optional

Expand Down
13 changes: 13 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,19 @@ Release tags use the `v` prefix (e.g. `v3.0.2`).

---

## [3.2.0] - 2026-05-11

### Added

- **TeamsAPI claim avoidance**: EzRTP now integrates with [TeamsAPI](https://modrinth.com/plugin/teams-api) to skip chunk-claimed areas during RTP destination search.
- New protection provider id: `teamsapi`. Works alongside the existing `worldguard` and `griefprevention` providers.
- Enabled automatically when TeamsAPI (with a compatible claim provider) is installed. Silently skipped when absent, no errors, no configuration changes required.
- `teamsapi` added to the default `protection.providers` list in `rtp.yml`.
- `TeamsAPI` added to `softdepend` in `plugin.yml`.
- Claim availability is evaluated dynamically per check, so a claim plugin that loads after EzRTP is picked up without a reload.

---

## [3.1.0] - 2026-05-09

### Added
Expand Down
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ It is designed for **safety-first teleportation**, **cross-platform compatibilit
- **Biome-aware and cache-assisted searching** with optional rare-biome optimization.
- **Heatmap/statistics tooling** for operators to inspect RTP distribution and performance.
- **WorldGuard region command mode** for region-scoped RTP entry points.
- **TeamsAPI integration** to skip chunk-claimed areas (works with any TeamsAPI-compatible team plugin).
- **Optional first-join/on-join teleport flow**.
- **Optional proxy/network destination menu support** for multi-server setups.
- **Chunky integration** for pre-generation workflows.
Expand All @@ -64,6 +65,7 @@ It is designed for **safety-first teleportation**, **cross-platform compatibilit
- [EzEconomy](https://modrinth.com/plugin/ezeconomy)
- [WorldGuard](https://modrinth.com/plugin/worldguard)
- [GriefPrevention](https://modrinth.com/plugin/griefprevention)
- [TeamsAPI](https://github.com/ez-plugins/teams-api) (chunk-claim avoidance via any compatible team plugin)
- [PlaceholderAPI](https://modrinth.com/mod/placeholderapi)
- [Chunky](https://modrinth.com/plugin/chunky/)

Expand Down
2 changes: 2 additions & 0 deletions docs/config/rtp.md
Original file line number Diff line number Diff line change
Expand Up @@ -222,9 +222,11 @@ protection:
providers:
- worldguard
- griefprevention
- teamsapi
```

Set `avoid-claims: true` to enable. Only providers that are installed will be used.
Supported values: `worldguard`, `griefprevention`, `teamsapi`.

---

Expand Down
33 changes: 28 additions & 5 deletions docs/integrations/protection-worldguard-griefprevention.md
Original file line number Diff line number Diff line change
@@ -1,21 +1,22 @@
---
title: WorldGuard & GriefPrevention
title: WorldGuard, GriefPrevention & TeamsAPI
nav_order: 4
parent: Integrations
---

# WorldGuard / GriefPrevention Protection Integration
# Protection Integrations (WorldGuard, GriefPrevention, TeamsAPI)

Use this integration when you want EzRTP to avoid protected regions/claims.
Use these integrations when you want EzRTP to avoid protected regions or claimed chunks.

## What this integration does

When enabled, EzRTP validates candidate RTP locations against configured protection providers and rejects protected points.

Supported provider names in config:

- `worldguard`
- `griefprevention`
- `worldguard` — WorldGuard protected regions
- `griefprevention` — GriefPrevention claims
- `teamsapi` — Chunk claims managed by any [TeamsAPI](https://modrinth.com/plugin/teams-api)-compatible team plugin

If a provider plugin is not installed, EzRTP continues with available providers (safe fallback behavior).

Expand All @@ -29,6 +30,7 @@ protection:
providers:
- worldguard
- griefprevention
- teamsapi
```

### Key settings
Expand All @@ -42,6 +44,25 @@ protection:

Why here: claim/region safety is part of RTP destination validation, so it belongs in `rtp.yml`.

## TeamsAPI claim avoidance

TeamsAPI is a universal bridge between Minecraft team / faction plugins and consumer plugins.
When a team plugin that supports chunk claiming registers a `TeamsClaimService` with TeamsAPI,
EzRTP automatically detects this and avoids claimed chunks during RTP destination search.

**Requirements:**

- [TeamsAPI](https://modrinth.com/plugin/teams-api) plugin installed (soft-depend — not mandatory).
- A compatible team plugin with claim support installed alongside TeamsAPI.

**Behaviour:**

- If TeamsAPI is absent or no claim provider is registered, the `teamsapi` entry in
`providers` is silently skipped. No errors are logged unless `avoid-claims: true` and
*all* configured providers are unavailable.
- Claim availability is re-checked dynamically, so a claim plugin that loads after EzRTP
is picked up without requiring a reload.

## Optional WorldGuard region command mode

You can also enable region-centric command routing:
Expand All @@ -61,5 +82,7 @@ worldguard:
## Recommended settings by server type

- **Survival / Towny-like protection-heavy servers:** set `avoid-claims: true`.
- **Factions / Teams servers using TeamsAPI:** set `avoid-claims: true` and ensure
`teamsapi` is in `providers`.
- **Minigame / event servers with custom handling:** keep `avoid-claims: false` unless conflicts occur.
- **Mixed networks:** enable only on servers where claim boundaries matter.
4 changes: 2 additions & 2 deletions ezrtp-api/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,13 @@
<parent>
<groupId>com.skyblockexp</groupId>
<artifactId>ezrtp-parent</artifactId>
<version>3.1.0</version>
<version>3.2.0</version>
<relativePath>../pom.xml</relativePath>
</parent>

<groupId>com.skyblockexp</groupId>
<artifactId>ezrtp-api</artifactId>
<version>3.1.0</version>
<version>3.2.0</version>
<packaging>jar</packaging>
<name>EzRTP API</name>
<description>Lightweight public API for EzRTP intended for third-party plugins.</description>
Expand Down
6 changes: 3 additions & 3 deletions ezrtp-bukkit/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,13 @@
<parent>
<groupId>com.skyblockexp</groupId>
<artifactId>ezrtp-parent</artifactId>
<version>3.1.0</version>
<version>3.2.0</version>
<relativePath>../pom.xml</relativePath>
</parent>

<groupId>com.skyblockexp</groupId>
<artifactId>ezrtp-bukkit</artifactId>
<version>3.1.0</version>
<version>3.2.0</version>
<packaging>jar</packaging>
<name>EzRTP (Bukkit)</name>
<description>EzRTP Bukkit-compatible plugin module.</description>
Expand Down Expand Up @@ -111,7 +111,7 @@
<dependency>
<groupId>com.skyblockexp</groupId>
<artifactId>ezrtp-common</artifactId>
<version>3.1.0</version>
<version>3.2.0</version>
</dependency>
</dependencies>

Expand Down
10 changes: 8 additions & 2 deletions ezrtp-common/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,13 @@
<parent>
<groupId>com.skyblockexp</groupId>
<artifactId>ezrtp-parent</artifactId>
<version>3.1.0</version>
<version>3.2.0</version>
<relativePath>../pom.xml</relativePath>
</parent>

<groupId>com.skyblockexp</groupId>
<artifactId>ezrtp-common</artifactId>
<version>3.1.0</version>
<version>3.2.0</version>
<packaging>jar</packaging>
<name>EzRTP Common</name>
<description>Shared utilities for EzRTP (platform-independent)</description>
Expand Down Expand Up @@ -73,6 +73,12 @@
<version>2.12.2</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.github.ez-plugins</groupId>
<artifactId>teams-api</artifactId>
<version>1.4.1</version>
<scope>provided</scope>
</dependency>
<!-- HikariCP connection pool — used by MySqlHotspotStorage for the persistent
hotspot write path. The basic DriverManager pattern in MySqlRtpUsageStorage is
sufficient for low-frequency usage-stat writes, but hotspot saves happen on every
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
import java.util.Locale;

public final class ProtectionSettings {
private static final List<String> DEFAULT_PROVIDERS = List.of("worldguard", "griefprevention");
private static final List<String> DEFAULT_PROVIDERS = List.of("worldguard", "griefprevention", "teamsapi");

private final boolean avoidClaims;
private final List<String> providers;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ public ProtectionRegistry(PluginManager pluginManager, Logger logger) {
this.logger = logger != null ? logger : java.util.logging.Logger.getAnonymousLogger();
registerProvider(new WorldGuardProtectionProvider(pluginManager, this.logger));
registerProvider(new GriefPreventionProtectionProvider(pluginManager, this.logger));
registerProvider(new TeamsApiProtectionProvider(pluginManager, this.logger));
}

public Optional<String> findProtectionProvider(Location location, ProtectionSettings settings) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
package com.skyblockexp.ezrtp.protection;

import com.skyblockexp.teamsapi.api.TeamsAPI;
import com.skyblockexp.teamsapi.api.TeamsClaimService;
import org.bukkit.Chunk;
import org.bukkit.Location;
import org.bukkit.World;
import org.bukkit.plugin.Plugin;
import org.bukkit.plugin.PluginManager;

import java.util.Objects;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
* Protection provider backed by TeamsAPI's {@link TeamsClaimService}.
*
* <p>TeamsAPI is a soft-dependency: if it is not installed on the server, this
* provider reports itself as unavailable and skips all checks without error.
* If TeamsAPI is installed but no team plugin has registered a claim provider,
* {@link #isAvailable()} returns {@code false} at the time of the check and
* the provider is silently skipped.</p>
*
* <p>Availability is re-evaluated on every call to {@link #isAvailable()} so
* that a claim provider registered after EzRTP starts is picked up
* automatically without requiring a reload.</p>
*/
public final class TeamsApiProtectionProvider implements ProtectionProvider {

private final Logger logger;
/**
* {@code true} when the TeamsAPI plugin was detected as loaded and enabled
* at the time this provider was constructed. When {@code false}, every
* method short-circuits immediately.
*/
private final boolean teamsApiPluginPresent;

public TeamsApiProtectionProvider(PluginManager pluginManager, Logger logger) {
this.logger = Objects.requireNonNull(logger, "logger");
boolean present = false;
if (pluginManager != null) {
try {
Plugin plugin = pluginManager.getPlugin("TeamsAPI");
present = plugin != null && plugin.isEnabled();
} catch (Exception e) {
// Defensive: plugin manager threw unexpectedly; treat as absent.
}
}
this.teamsApiPluginPresent = present;
}

@Override
public String getId() {
return "teamsapi";
}

/**
* Returns {@code true} when TeamsAPI is installed <em>and</em> a
* {@link TeamsClaimService} provider is currently registered.
*
* <p>This check is dynamic: it re-queries Bukkit's service registry on
* every call so that a claim plugin registered after server startup is
* transparently picked up.</p>
*/
@Override
public boolean isAvailable() {
if (!teamsApiPluginPresent) {
return false;
}
try {
return TeamsAPI.isClaimAvailable();
} catch (NoClassDefFoundError e) {
return false;
}
}

@Override
public boolean isLocationProtected(Location location) {
if (!teamsApiPluginPresent || location == null) {
return false;
}
World world = location.getWorld();
if (world == null) {
return false;
}
try {
TeamsClaimService claimService = TeamsAPI.getClaimService();
if (claimService == null) {
return false;
}
Chunk chunk = location.getChunk();
return claimService.isClaimed(world.getName(), chunk.getX(), chunk.getZ());
} catch (NoClassDefFoundError | RuntimeException e) {
logger.log(Level.WARNING, "Failed to query TeamsAPI claims for RTP protection check.", e);
return false;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package com.skyblockexp.ezrtp.protection;

import org.bukkit.Location;
import org.bukkit.plugin.PluginManager;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;

import java.util.logging.Logger;

import static org.junit.jupiter.api.Assertions.*;

class TeamsApiProtectionProviderTest {

@Test
void getId_returnsTeamsapi() {
PluginManager pm = Mockito.mock(PluginManager.class);
TeamsApiProtectionProvider provider = new TeamsApiProtectionProvider(pm, Logger.getAnonymousLogger());
assertEquals("teamsapi", provider.getId());
}

@Test
void isAvailable_falseWhenPluginNotPresent() {
PluginManager pm = Mockito.mock(PluginManager.class);
// pm.getPlugin("TeamsAPI") returns null by default — plugin not installed
TeamsApiProtectionProvider provider = new TeamsApiProtectionProvider(pm, Logger.getAnonymousLogger());
assertFalse(provider.isAvailable(),
"Provider should be unavailable when TeamsAPI plugin is not installed");
}

@Test
void isLocationProtected_returnsFalseWhenPluginNotPresent() {
PluginManager pm = Mockito.mock(PluginManager.class);
TeamsApiProtectionProvider provider = new TeamsApiProtectionProvider(pm, Logger.getAnonymousLogger());
// Should not throw and should return false when plugin is absent
assertFalse(provider.isLocationProtected(null));
}

@Test
void isLocationProtected_returnsFalseForNullLocation() {
PluginManager pm = Mockito.mock(PluginManager.class);
TeamsApiProtectionProvider provider = new TeamsApiProtectionProvider(pm, Logger.getAnonymousLogger());
assertFalse(provider.isLocationProtected(null));
}

@Test
void constructor_toleratesNullPluginManager() {
// Must not throw even when no plugin manager is supplied
TeamsApiProtectionProvider provider = new TeamsApiProtectionProvider(null, Logger.getAnonymousLogger());
assertFalse(provider.isAvailable());
assertFalse(provider.isLocationProtected(Mockito.mock(Location.class)));
}
}
6 changes: 3 additions & 3 deletions ezrtp-paper/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,13 @@
<parent>
<groupId>com.skyblockexp</groupId>
<artifactId>ezrtp-parent</artifactId>
<version>3.1.0</version>
<version>3.2.0</version>
<relativePath>../pom.xml</relativePath>
</parent>

<groupId>com.skyblockexp</groupId>
<artifactId>ezrtp-paper</artifactId>
<version>3.1.0</version>
<version>3.2.0</version>
<packaging>jar</packaging>
<name>EzRTP (Paper)</name>
<description>Paper-optimized module for EzRTP</description>
Expand All @@ -32,7 +32,7 @@
<dependency>
<groupId>com.skyblockexp</groupId>
<artifactId>ezrtp-common</artifactId>
<version>3.1.0</version>
<version>3.2.0</version>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
Expand Down
Loading
Loading