TeamsAPI is a universal bridge plugin for Minecraft servers. Inspired by Vault, it defines a clean, stable interface for team operations so any plugin that needs team data can work with any compatible team plugin without coupling them together.
Your Plugin (consumer) -> TeamsAPI (bridge) -> Team Plugin (provider)
- Providers (e.g. faction, clan, guild plugins) implement
TeamsServiceand register with TeamsAPI duringonEnable(). - Consumers (any plugin that needs team data) call
TeamsAPI.getService()and use the returnedTeamsServicewithout knowing which team plugin is installed. - Server owners install
TeamsAPI.jaralongside any single compatible team plugin.
GitHub Releases | Modrinth | Hangar
Maven (via Jitpack):
<repositories>
<repository>
<id>jitpack.io</id>
<url>https://jitpack.io</url>
</repository>
</repositories>
<dependency>
<groupId>com.github.ez-plugins</groupId>
<artifactId>teams-api</artifactId>
<version>2.0.0</version>
<scope>provided</scope>
</dependency>Gradle:
repositories {
maven { url 'https://jitpack.io' }
}
dependencies {
compileOnly 'com.github.ez-plugins:teams-api:2.0.0'
}// In onEnable() or lazily:
if (!TeamsAPI.isAvailable()) {
getLogger().warning("No team plugin found. Team features disabled.");
return;
}
// Anywhere team data is needed:
TeamsService teams = TeamsAPI.getService();
Optional<Team> team = teams.getPlayerTeam(player.getUniqueId());
team.ifPresent(t -> player.sendMessage("Team: " + t.getDisplayName()));private MyTeamsService teamsService;
@Override
public void onEnable() {
teamsService = new MyTeamsService(this);
TeamsAPI.registerProvider(this, teamsService);
}
@Override
public void onDisable() {
TeamsAPI.unregisterProvider(teamsService);
}If the active team plugin supports invitations, a TeamsInviteService is available:
if (TeamsAPI.isInviteAvailable()) {
TeamsInviteService invites = TeamsAPI.getInviteService();
invites.invitePlayer(teamId, sender.getUniqueId(), target.getUniqueId());
}Providers that support invitations register the service alongside TeamsService:
@Override
public void onEnable() {
TeamsAPI.registerProvider(this, teamsService);
TeamsAPI.registerInviteProvider(this, inviteService);
}
@Override
public void onDisable() {
TeamsAPI.unregisterProvider(teamsService);
TeamsAPI.unregisterInviteProvider(inviteService);
}If the active team plugin supports named warps, a TeamsWarpService is available:
if (TeamsAPI.isWarpAvailable()) {
TeamsWarpService warps = TeamsAPI.getWarpService();
warps.getWarp(teamId, "home").ifPresent(w -> player.teleport(w.getLocation()));
}Providers that support warps register the service alongside TeamsService:
@Override
public void onEnable() {
TeamsAPI.registerProvider(this, teamsService);
TeamsAPI.registerWarpProvider(this, warpService);
}
@Override
public void onDisable() {
TeamsAPI.unregisterProvider(teamsService);
TeamsAPI.unregisterWarpProvider(warpService);
}If the active team plugin supports chunk claims, a TeamsClaimService is available:
if (TeamsAPI.isClaimAvailable()) {
TeamsClaimService claims = TeamsAPI.getClaimService();
Chunk chunk = player.getLocation().getChunk();
boolean claimed = claims.claimChunk(
teamId, player.getUniqueId(),
chunk.getWorld().getName(), chunk.getX(), chunk.getZ()
);
}Territory type checks are also available for SafeZone/WarZone-aware providers:
ClaimTerritoryType territory = claims.getTerritoryTypeAt(
chunk.getWorld().getName(), chunk.getX(), chunk.getZ()
);
if (territory == ClaimTerritoryType.SAFE_ZONE) {
player.sendMessage("You are in SafeZone.");
}Providers that support claims register the service alongside TeamsService:
@Override
public void onEnable() {
TeamsAPI.registerProvider(this, teamsService);
TeamsAPI.registerClaimProvider(this, claimService);
}
@Override
public void onDisable() {
TeamsAPI.unregisterProvider(teamsService);
TeamsAPI.unregisterClaimProvider(claimService);
}If the active team plugin exposes power values, a TeamsPowerService is available:
if (TeamsAPI.isPowerAvailable()) {
TeamsPowerService power = TeamsAPI.getPowerService();
double current = power.getTeamPower(teamId);
double max = power.getTeamMaxPower(teamId);
player.sendMessage("Team power: " + current + " / " + max);
}If the active team plugin exposes power history, a TeamsPowerHistoryService
is available:
if (TeamsAPI.isPowerHistoryAvailable()) {
TeamsPowerHistoryService history = TeamsAPI.getPowerHistoryService();
Collection<TeamPowerHistoryEntry> recent =
history.getPlayerPowerHistory(player.getUniqueId(), 25);
}Providers that expose power history register the service alongside TeamsService:
@Override
public void onEnable() {
TeamsAPI.registerProvider(this, teamsService);
TeamsAPI.registerPowerHistoryProvider(this, powerHistoryService);
}
@Override
public void onDisable() {
TeamsAPI.unregisterProvider(teamsService);
TeamsAPI.unregisterPowerHistoryProvider(powerHistoryService);
}Providers that expose power register the service alongside TeamsService:
@Override
public void onEnable() {
TeamsAPI.registerProvider(this, teamsService);
TeamsAPI.registerPowerProvider(this, powerService);
}
@Override
public void onDisable() {
TeamsAPI.unregisterProvider(teamsService);
TeamsAPI.unregisterPowerProvider(powerService);
}If the active team plugin supports inter-team diplomacy, a TeamsRelationService is
available. Relations are directional — team A can declare ALLY toward team B
independently of what team B declares toward team A.
if (TeamsAPI.isRelationAvailable()) {
TeamsRelationService relations = TeamsAPI.getRelationService();
// Declare an alliance
relations.setRelation(myTeamId, theirTeamId, TeamRelation.ALLY, player.getUniqueId());
// Query the current relation
TeamRelation rel = relations.getRelation(myTeamId, theirTeamId);
player.sendMessage("Relation: " + rel.getDisplayName());
// Convenience helpers (mutual check)
if (relations.areAllies(myTeamId, theirTeamId)) {
player.sendMessage("You are mutual allies!");
}
}Providers that support relations register the service alongside TeamsService:
@Override
public void onEnable() {
TeamsAPI.registerProvider(this, teamsService);
TeamsAPI.registerRelationProvider(this, relationService);
}
@Override
public void onDisable() {
TeamsAPI.unregisterProvider(teamsService);
TeamsAPI.unregisterRelationProvider(relationService);
}TeamRelation values (lowest → highest hostility): ALLY, TRUCE, NEUTRAL, ENEMY.
Vault (optional):
plugin.ymldeclaressoftdepend: [Vault]. When Vault is installed the built-in power shop (/teamsapi power buy) can charge players. TeamsAPI loads normally without Vault; the shop is simply disabled.
Any plugin can register a TeamsSubcommand via TeamsAPI.registerSubcommand(). Team
plugins call TeamsAPI.getSubcommands() in their own command executor to dispatch them,
allowing third-party plugins to extend the team plugin's command tree without any direct
coupling between plugins.
Extend AbstractTeamsSubcommand (recommended) or implement TeamsSubcommand directly:
public class StatsSubcommand extends AbstractTeamsSubcommand {
public StatsSubcommand() {
super("stats", "Show faction statistics.", "myfactions.stats");
}
@Override
public boolean execute(CommandSender sender, String[] args) {
// handle the command
return true; // return false to trigger the usage hint
}
}
// In onEnable:
TeamsAPI.registerSubcommand(this, new StatsSubcommand());
// In onDisable:
TeamsAPI.unregisterSubcommand(statsSubcommand);Team plugins dispatch registered subcommands inside their own command executor:
for (TeamsSubcommand sub : TeamsAPI.getSubcommands()) {
if (sub.getName().equalsIgnoreCase(args[0])) {
String perm = sub.getPermission();
if (perm != null && !sender.hasPermission(perm)) {
sender.sendMessage("You do not have permission.");
return true;
}
if (!sub.execute(sender, args)) {
sender.sendMessage("Usage: " + sub.getUsage());
}
return true;
}
}| Method | Returns | Description |
|---|---|---|
getName() |
String |
Matched case-insensitively against args[0] |
getDescription() |
String |
Optional description for help output |
getPermission() |
String |
Required permission, or null for no check |
execute(sender, args) |
boolean |
Called when dispatched; return false to show usage |
getUsage() |
String |
Usage hint sent when execute returns false |
tabComplete(sender, args) |
List<String> |
Tab-completion suggestions; default: empty list |
Provider events extend TeamEvent. Core events are cancellable:
@EventHandler
public void onTeamJoin(TeamJoinEvent event) {
if (event.getTeam().getSize() >= 10) {
event.setCancelled(true);
}
}
@EventHandler
public void onWarpSet(TeamWarpSetEvent event) {
// Cancel to prevent the warp from being saved
}
@EventHandler
public void onClaim(TeamClaimEvent event) {
// Cancel to block the claim
}
@EventHandler
public void onUnclaim(TeamUnclaimEvent event) {
// Cancel to block the unclaim
}For the complete API reference, see docs/api.md. For integration examples, see docs/developer-guide.md.
TeamsAPI includes bridge plugins for both major proxy platforms:
| Proxy | Plugin | Guide |
|---|---|---|
| Velocity | teams-api-velocity |
Velocity Guide |
| BungeeCord / Waterfall | teams-api-bungeecord |
BungeeCord Guide |
Both bridges expose an async API (CompletableFuture<T>) so proxy-side plugins can
query team data from backend servers without direct coupling.
Both bridge plugins support multi-proxy networks via Redis Pub/Sub. When Redis is
enabled in config.yml, queries that cannot be fulfilled by a local player are
automatically forwarded to another proxy in the network. All proxies must share the
same Redis instance.
# plugins/teamsapi/config.yml
redis:
enabled: true
host: "your-redis-host"
port: 6379
prefix: "teamsapi:"See the Velocity Guide or BungeeCord Guide for the full configuration reference.
| Requirement | Version |
|---|---|
| Java | 17+ (25 recommended) |
| Server software | Paper / Spigot / Purpur / Folia 1.16+ |
| Build tool | Maven 3.8+ or Gradle 8+ |
# Compile
mvn -q -DskipTests compile
# Run tests
mvn -q -pl teams-api test
# Build server JAR
mvn -q -DskipTests package| Module | Description |
|---|---|
teams-api/ |
Public API: interfaces, models, and events. Depend on this. |
teams-api-plugin/ |
Bukkit plugin packaging. Server owners install this JAR. |
teams-api-velocity/ |
Velocity proxy plugin. Bridges team queries to backend servers. Supports Redis for multi-proxy setups. |
teams-api-bungeecord/ |
BungeeCord / Waterfall proxy plugin. Mirrors the Velocity bridge. Supports Redis for multi-proxy setups. |
mvn -q -DskipTests compilemust succeed.mvn -q -pl teams-api testall tests must pass.mvn -q -pl teams-api checkstyle:checkzero violations.- Add tests for non-trivial logic changes.
- Update Javadoc whenever a public API changes.
See AGENTS.md for full coding standards.
MIT — see LICENSE.