Skip to content

Public API for other plugins, with events on Paper, Velocity and BungeeCord - #3

Merged
Macaron27 merged 1 commit into
mainfrom
public-api
Oct 1, 2026
Merged

Macaron27 merged 1 commit into
mainfrom
public-api

Conversation

@Macaron27

Copy link
Copy Markdown
Owner

What

A public API so other plugins can read and act on friendships, plus events on every platform. Version bumped to 0.5.0.

  • New api module (com.friends:friends-api, Java 8, no dependencies):

    • FriendsAPI, obtained with FriendsAPI.get() (on Paper it is also a Bukkit service).
    • Reads from memory: isLoaded, getFriends, areFriends, getStatus, getServer, incoming/outgoing requests.
    • Async calls returning a CompletableFuture: loadFriends, and the actions sendRequest, acceptRequest, denyRequest, removeFriend, setBestFriend, setNickname, setStatus. Each action completes with a Result enum.
    • Immutable Friend / FriendRequest snapshots, and Status (moved here from common).
  • Six events per platform (com.friends.api.bukkit, .velocity, .bungee):

    Event Cancellable Fired
    FriendRequestSendEvent yes before a request is sent
    FriendAddEvent yes before two players become friends
    FriendAddedEvent no after, exactly once
    FriendRemoveEvent yes before a friendship ends (once per friend, /f removeall too)
    FriendRemovedEvent no after, exactly once
    FriendStatusChangeEvent yes before /status changes
  • Docs: full JavaDoc, enforced by the build (-Xdoclint:all -Werror), and an "API for other plugins" section in the README.

Why it's built this way

  • Java 8 API. On Paper the real plugin lives in an embedded Java 25 jar that other plugins can't see. The API and events go in the outer jar instead, where every Paper version's class rewriter (1.13–1.20) can read them. The published Gradle metadata declares JVM 8, so plugins on any toolchain can depend on it.
  • Threads:
    • Every method is thread-safe; value-returning reads never block.
    • Actions run entirely on Friends' threads, so an event can never fire from the caller's thread (Bukkit rejects async events fired from the server thread).
    • Hooks never run on the database thread. Otherwise a listener that waits on an API future would deadlock; there's a test that fails if this breaks.
  • Cancellable events fire outside the core's lock, then the action is checked again under it. That keeps third-party code from holding the global lock. "After" events are emitted from the locked commit and delivered in order by one thread in FriendsRuntime.
  • Offline friend lists are cached for 30 s, with no new dependency. An entry is dropped on any friendship or join/leave change. The entry is stored before its database read is queued, so a read that overlaps a change is never kept (also covered by a test that fails when the order is reversed).
  • Nothing throws: nulls and bad input give an empty result, false or INVALID_ARGUMENT; database errors give Result.ERROR.

Reviewer notes

  • Bukkit events only fire on standalone Paper. On a network Friends runs on the proxy, so listeners use the Velocity or BungeeCord events there; each fires once network-wide, on the proxy where the change happened.
  • API actions don't send the acting player a chat reply (the Result says what happened). The other player is still notified, and commands and API actions fire the same events.
  • Known race: if one request is accepted twice at the same instant, a cancellable event can fire and its action then fail the recheck. The README says rewards belong in the "after" events.
  • Velocity commands now run on virtual threads, so waiting for listeners never blocks Velocity's own threads.
  • Paper tests run against the shipped plugin jar, not the plain classes. Unrelocated Adventure 5 breaks MockBukkit's paper-api 1.21.11: BookMeta implements Adventure 4's Book, which is sealed in Adventure 5. MockBukkit also ignores plugin.yml permission defaults, so the test grants friends.use itself.
  • CI now also needs MockBukkit 4.116.3 (Maven Central) and paper-api 1.21.11-R0.1-SNAPSHOT (PaperMC repo), and compiles the small tools/e2e/probe test plugin.
  • Publishing: the repo is private, so the API is only set up for ./gradlew :api:publishToMavenLocal.

Testing

  • ./gradlew build with MySQL 8.0.46 and Redis: every suite passes (1 skipped, a SQLite-only check).

    Suite Tests
    Core on SQLite + MySQL + Redis (new: 19 API tests) 120
    sqlite-jdbc 3.7.2 30
    1.8 chat API 4
    Paper on MockBukkit 4
    Velocity / BungeeCord hooks 2 / 3
  • Real servers (tools/e2e): 15/15 scenarios pass, all on 0.4.0 jars (the version bump came after):

    • every existing scenario: Paper 1.8.8 → 26.3, both proxies, and mixed BungeeCord + Velocity on MySQL + Redis;
    • new api-* scenarios with a probe plugin: Paper 1.20.6 and 26.3, BungeeCord, Velocity.
  • Rerun after the bump: api-1.8.8 (also run separately on 0.4.0), api-26.3, api-bungee and api-velocity pass on 0.5.0. The probe sees the API, every event (async, never on the server thread), a cancelled /status away, and API reads and actions from its listeners and from the Paper server thread.

🤖 Generated with Claude Code

…d BungeeCord

New `api` module (com.friends:friends-api, Java 8, no dependencies): FriendsAPI
(reads from memory, async actions returning a Result), Friend/FriendRequest
snapshots, Status, and six events per platform: four cancellable "before" events
(request, add, remove, status) and two exactly-once "after" events.

- Java 8 because on Paper the API sits next to the bootstrap, where other
  plugins' class loaders can see it and Paper's class rewriter can read it.
- Core: commands return a Result, accept a name or a UUID, ask plugins outside
  the lock and re-check after; hooks never run on the database thread.
- Offline players' friend lists are cached for 30 s, dropped on any friendship
  change; a read that races a change is never kept.
- Nulls and bad input never throw (empty, false or INVALID_ARGUMENT).
- Tests: FriendsApiTest, MockBukkit on the shipped Paper jar, proxy event tests,
  and api-* e2e scenarios with a probe plugin on real servers.
- Version 0.5.0.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@Macaron27
Macaron27 merged commit 0c00e86 into main Oct 1, 2026
1 check passed
@Macaron27
Macaron27 deleted the public-api branch October 2, 2026 09:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant