> For the complete documentation index, see [llms.txt](https://docs.ohalee.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.ohalee.com/products/ultimateguilds/for-developers.md).

# For Developers

UltimateGuilds ships an `api` module (`com.ohalee.ultimateguilds.api`) that exposes a stable, read/write interface to guild data. Add UltimateGuilds to your `plugin.yml` (`depend` or `softdepend`) so the API is loaded before your plugin.

## Obtaining the API

The entry point is `UltimateGuildsProvider`. Call it once the UltimateGuilds plugin has enabled (for example inside your own `onEnable`), not from a constructor.

{% code title="Accessing the API" %}

```java
UltimateGuilds api = UltimateGuildsProvider.get();
GuildManager guildManager = api.getGuildManager();
```

{% endcode %}

{% hint style="warning" %}
`UltimateGuildsProvider.get()` throws an `IllegalStateException` if the API is not yet loaded. This usually means UltimateGuilds isn't installed/enabled, or your plugin doesn't declare a dependency on it.
{% endhint %}

```java
public interface UltimateGuilds {

    /**
     * The manager responsible for all guild operations.
     */
    @NotNull GuildManager getGuildManager();

}
```

## GuildManager

`GuildManager` is the main service. Its methods fall into a few groups:

* **Database lookups** — blocking I/O that queries the database (e.g. `loadGuild`, `find`, `getLogs`, `hasGuild`).
* **Asynchronous modification** — non-blocking writes returning `CompletableFuture` (e.g. `createGuild`, `renameGuild`, `addMember`, `banPlayer`).
* **Server cache retrieval** — in-memory, non-blocking reads (e.g. `getLoadedGuild`, `getCached`, `getLoadedGuilds`).
* **Cosmetics & messaging** — suffix refreshing and cross-server message delivery.

{% hint style="info" %}
Methods annotated `@Blocking` (and the `loadGuild`/`find` lookups) touch the database. Never call them on the main server thread — use the cache methods there, or run the lookup asynchronously.
{% endhint %}

{% code title="GuildManager (selected methods)" %}

```java
public interface GuildManager {

    // --- Database lookups (blocking / async) ---
    CompletableFuture<Optional<Guild>> loadGuild(int guildId);
    CompletableFuture<Optional<Guild>> loadGuild(UUID memberUniqueId);
    CompletableFuture<Optional<Pair<Guild, GuildMember>>> find(UUID memberUniqueId);
    CompletableFuture<Optional<Pair<Guild, GuildMember>>> find(String memberUsername);
    CompletableFuture<Optional<Guild>> findByName(String name);

    @Blocking boolean hasGuild(UUID uniqueId);
    @Blocking boolean isNameUnique(String name);
    @Blocking boolean isTagUnique(String tag);

    List<Log> getLogs(int guildId, int page, int pageSize);
    CompletableFuture<List<Log>> getLogsAsync(int guildId, int page, int pageSize);

    // --- Asynchronous modification ---
    CompletableFuture<Guild> createGuild(String name, String tag, int maxMembers,
                                         UUID leaderUuid, String leaderUsername);
    CompletableFuture<Void> renameGuild(Guild guild, String newName);
    CompletableFuture<Void> updateTag(Guild guild, String newTag, String hexTagColor);
    CompletableFuture<Void> updateDescription(Guild guild, String description);
    CompletableFuture<Void> addMember(Guild guild, UUID uuid, String username, GuildRank rank);
    CompletableFuture<Void> removeMember(Guild guild, UUID uuid);
    CompletableFuture<Void> updateMemberRank(Guild guild, UUID uuid, GuildRank newRank);
    CompletableFuture<Void> banPlayer(Guild guild, UUID targetUuid, String targetUsername,
                                      UUID bannedByUuid, String bannedByUsername);
    CompletableFuture<Void> unbanPlayer(Guild guild, UUID targetUuid);
    CompletableFuture<Void> disbandGuild(Guild guild);
    CompletableFuture<Void> updateGuildStats(Guild guild);

    // --- Server cache retrieval ---
    Optional<Guild> getLoadedGuild(int guildId);
    Optional<Guild> getLoadedGuild(UUID memberUniqueId);
    @Nullable Guild getCached(int guildId);
    @Nullable Guild getCachedByPlayer(UUID uniqueId);
    Set<Guild> getLoadedGuilds();
    void invalidateLocalCache(int guildId);

    // --- Invites, bans & members ---
    Set<GuildInvite> getInvites(int guildId);
    boolean hasInvite(UUID targetId);
    Set<GuildMember> getOnlineMembers(int guildId);
    CompletableFuture<List<GuildBan>> getBans(int guildId);

    // --- Cosmetics & messaging ---
    void refreshSuffixes(Guild guild);
    void sendMessage(UUID uniqueId, Component message);
    void broadcastMessage(Guild guild, Component message);
    void insertLog(int guildId, Log... logs);
}
```

{% endcode %}

## Guild

A `Guild` is the mutable model returned by the manager. It exposes the guild's identity, progression and roster.

```java
public interface Guild {

    int id();
    String name();
    void name(String name);

    @Nullable String tag();
    void tag(@Nullable String tag);
    String tagColor();               // hex string, e.g. "#FFFFFF"
    void tagColor(String tagColor);

    @Nullable String description();
    void description(@Nullable String description);

    int level();      void level(int level);
    int xp();         void xp(int xp);        void addXp(int amount);
    double multiplier();  void multiplier(double multiplier);
    int maxMembers(); void maxMembers(int maxMembers);
    long createdAt();

    void addMember(GuildMember member);
    void removeMember(UUID uuid);
    void updateMemberRank(UUID uuid, GuildRank newRank);

    Collection<GuildMember> members();
    List<GuildMember> getMembersByRank(GuildRank rank);
    Optional<GuildMember> getMember(UUID uniqueId);
    Optional<GuildMember> getMember(String username);
    boolean isMember(UUID uniqueId);

    void broadcast(Component component);
}
```

## Models

`GuildMember`, `GuildRank`, `GuildBan` and `GuildInvite` are lightweight, immutable models.

```java
// A member of a guild.
public record GuildMember(UUID uniqueId, String username, GuildRank rank,
                          long joined, long lastSeen) {}

// The five ranks, ordered from highest (ordinal 0) to lowest.
public enum GuildRank {
    GUILD_MASTER, CO_LEADER, MODERATOR, MEMBER, RECRUIT;

    public static GuildRank getRank(int id);
    public GuildRank nextRank();      // the rank above (null past CO_LEADER)
    public GuildRank previousRank();  // the rank below
    public Component displayName();
}

// A ban entry.
public record GuildBan(UUID targetUuid, String targetUsername,
                       UUID bannedByUuid, String bannedByUsername, long timestamp) {}

// A pending invite.
public record GuildInvite(UUID targetUuid, String username,
                          String invitedByUsername, long createdAt) {}
```

{% hint style="info" %}
`GuildRank` is ordered so that a **lower ordinal means a higher rank** — `GUILD_MASTER` is `0` and `RECRUIT` is the last value. Keep this in mind when comparing ranks (`rank().ordinal() <= other.ordinal()` means "at least as senior").
{% endhint %}
