Skip to content

Private messages, last-seen display and server-based presence (0.6.0) - #6

Merged
Macaron27 merged 1 commit into
mainfrom
social-features
Oct 3, 2026
Merged

Macaron27 merged 1 commit into
mainfrom
social-features

Conversation

@Macaron27

Copy link
Copy Markdown
Owner

What

Three social features, on Velocity, BungeeCord and Paper (one shared core), API 0.6.0.

1. Private messages

  • /msg <friend> <message> (also /tell, /w, /whisper) and /r <message> (also /reply). Friends only, network-wide; the recipient must be online and not appearing offline.
  • Lines look like From [VIP] Bob: hi / To Bob: hi; clicking one starts a reply.
  • FriendMessageEvent (Bukkit, Velocity, BungeeCord) fires before delivery. If a plugin cancels it, Friends delivers nothing, echoes nothing and leaves both players' /r target unchanged, so an external chat plugin can own the messaging layer. setMessage lets a filter rewrite the text.
  • Messages go straight to Redis (not behind the SQL queue) and are never stored. Player text is never parsed (no MiniMessage or & codes); § and control characters are stripped.
  • private-messages.enabled: false keeps /msg and /r unregistered, for networks that have their own.

2. Last seen

  • LastSeen (API, Java 8): LastSeen.format(friend.getLastSeen()) gives "Last seen 17 minutes ago" ("just now", "3 days ago", …), for other plugins' menus and scoreboards.
  • /f list:
    ● Player1 — Playing BedWars Solo      green online, yellow "Away · …", red "Busy · …"
    ● Player2 — In lobby-1                no presence rule matches the server
    ● Player3 — Last seen 17 minutes ago  offline or appearing offline
    
  • /msg to an offline friend replies "Bob is offline. (Last seen 3 days ago)".

3. Server-based presence

  • presence.rules in config.yml: each server-name regex maps to a generic PlayerActivity(game, mode). The regex must match the whole name, case is ignored, the first match wins, and mode is optional.
  • Computed on join and on server switch. It travels in the existing Presence / Event.Updated through the Redis presence hash, so there are no new keys, and nothing is published if nothing changed.
  • FriendsAPI.getActivity(UUID) returns an Optional<PlayerActivity>.
  • A bad rule (invalid regex, no game) is skipped with a warning; Friends keeps running.
  • Generic on purpose: no party state here, that belongs to Parties & Chill.

Upgrading from 0.5.0

  • No database change.
  • Missing config.yml sections are added to the end of the existing file with their comments; nothing the server owner wrote is touched.
  • /msg and its aliases are new. On Paper they are registered when the plugin starts, not through plugin.yml: a plugin that declares /msg in its plugin.yml (e.g. Essentials) keeps it, and Friends logs that its own is /friends:msg. On both proxies, Friends replaces an existing /msg and logs it. I checked this in the BungeeCord API and Velocity proxy bytecode: Map.put, then removeChildByName + addChild.
  • Multi-proxy: upgrade every proxy together. Older proxies ignore the new presence field and keep working, but they drop the new message event, so a /msg to a player on a proxy that isn't upgraded yet is lost. This is written in the README.

Decisions taken with the maintainer

  • Coloured ● (U+25CF) instead of the emoji 🟢/⚫. I couldn't confirm 🟢 (outside the Basic Multilingual Plane) renders on 1.8 clients.
  • PlayerActivity is read-only, like Friend and FriendRequest, with no setters: one shared object is synchronised across proxies, so a setter would change what every proxy sees.

Tests

  • Unit and integration (local only, per the repo policy that src/test stays out of git): 149 core, 7 Paper (MockBukkit), 5 Velocity, 6 BungeeCord, all passing. They also pass with -Dfriends.mysql and -Dfriends.redis against a throwaway MySQL 8.0 and redis-server. They cover:
    • rule parsing and matching, LastSeen boundaries, config upgrade
    • /msg and /r: not friends, offline, appearing offline, cancelled, rewritten, re-checked after the event listeners, text injection
    • cross-proxy messages, including one that arrives before the friendship it relies on
    • activity sync, and a proxy that starts later
    • JSON compatibility with 0.5.0's format
    • the event on each platform
    • each proxy's YAML loader reading the upgraded config
    • Paper commands: registration, the on/off switch, and a conflict with another plugin
  • Real servers (tools/e2e, all passing): Paper 1.8.8 and 26.3, Folia 1.21.11, Velocity and BungeeCord with 1.8.8 and 26.3 players, the API probe on 4 platforms, mixed (BungeeCord + Velocity on MySQL + Redis: Alice on BungeeCord sees Bob's activity from Velocity, and /msg↔/r go through Redis), plus the new upgrade-paper / upgrade-velocity (the 0.5.0 jar first, then this build on the same data: friendship and nickname kept, config extended, /w working). /msg, /tell and /w take over vanilla's on both real Paper versions.
  • ./gradlew build (including verifyBuilds, Java 8 bytecode, Javadoc lint) passes.

Not included (easy to add if wanted)

Offline mail, ignore lists, social spy, a message rate limit, an activity-change event, and an API method to send a message.

🤖 Generated with Claude Code

…6.0)

Private messages: /msg <friend> <message> (/tell, /w, /whisper) and /r (/reply),
friends only, network-wide. A cancellable FriendMessageEvent (Bukkit, Velocity,
BungeeCord) fires first: cancel it to own the messaging layer, or rewrite the text.
Messages go straight to Redis (not behind the SQL queue) and are never stored.
Player text is never parsed; section signs and control characters are stripped.
private-messages.enabled: false leaves the commands to another plugin.

Last seen: LastSeen (API) words times ("Last seen 17 minutes ago"); /f list
shows "● Bob — Playing BedWars Solo" / "● Bob — Last seen 3 days ago", and
/msg to an offline friend says when they were last seen.

Presence: presence.rules map backend server-name regexes to a generic
PlayerActivity (game, mode). Evaluated on join and server switch, carried by
the existing Presence / Event.Updated through Redis (published only when it
changed), exposed as FriendsAPI.getActivity. Bad rules are skipped with a
warning. No party logic: that belongs to Parties & Chill.

Upgrades: no schema change; missing config.yml sections are appended with
their comments; older proxies ignore the new Presence field. New e2e
scenarios upgrade-paper / upgrade-velocity run the previous release, then this
build on the same data.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@Macaron27
Macaron27 merged commit 343605f into main Oct 3, 2026
1 check passed
@Macaron27
Macaron27 deleted the social-features branch October 3, 2026 12:31
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