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
60 changes: 23 additions & 37 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,66 +2,52 @@ name: CI

on:
pull_request:
branches: [ main, master ]
branches: [ v2.x, v3.x ]

jobs:
unit-tests:
name: Unit tests
runs-on: ubuntu-latest
strategy:
matrix:
include:
- paper-version: "1.21.11-R0.1-SNAPSHOT"
mockbukkit-artifactId: "mockbukkit-v1.21"
mockbukkit-version: "4.101.0"

steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v5

- name: Set up JDK 21
uses: actions/setup-java@v4
- name: Set up Java 25
uses: actions/setup-java@v5
with:
distribution: temurin
java-version: '21'
java-version: '25'
cache: maven

- name: Cache Maven local repository
uses: actions/cache@v4
with:
path: ~/.m2/repository
key: ${{ runner.os }}-m2-${{ matrix.paper-version }}-${{ hashFiles('**/pom.xml') }}
restore-keys: |
${{ runner.os }}-m2-${{ matrix.paper-version }}-
- name: Build and install MockBukkit dev/26.1.1
run: |
git clone --depth=1 --branch dev/26.1.1 https://github.com/MockBukkit/MockBukkit.git /tmp/MockBukkit
cd /tmp/MockBukkit && ./gradlew publishToMavenLocal -xtest -xjavadoc

- name: Run unit tests
run: mvn -B -Dpaper.version=${{ matrix.paper-version }} -Dmockbukkit.artifactId=${{ matrix.mockbukkit-artifactId }} -Dmockbukkit.version=${{ matrix.mockbukkit-version }} test
run: mvn -B -ntp test

feature-tests:
name: Feature tests
runs-on: ubuntu-latest
needs: unit-tests
strategy:
matrix:
include:
- paper-version: "1.21.11-R0.1-SNAPSHOT"
mockbukkit-artifactId: "mockbukkit-v1.21"
mockbukkit-version: "4.101.0"

steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v5

- name: Set up JDK 21
uses: actions/setup-java@v4
- name: Set up Java 25
uses: actions/setup-java@v5
with:
distribution: temurin
java-version: '21'
java-version: '25'
cache: maven

- name: Cache Maven local repository
uses: actions/cache@v4
with:
path: ~/.m2/repository
key: ${{ runner.os }}-m2-feature-${{ matrix.paper-version }}-${{ hashFiles('**/pom.xml') }}
restore-keys: |
${{ runner.os }}-m2-feature-${{ matrix.paper-version }}-
- name: Build and install MockBukkit dev/26.1.1
run: |
git clone --depth=1 --branch dev/26.1.1 https://github.com/MockBukkit/MockBukkit.git /tmp/MockBukkit
cd /tmp/MockBukkit && ./gradlew publishToMavenLocal -xtest -xjavadoc

- name: Run feature tests
run: mvn -B -Dpaper.version=${{ matrix.paper-version }} -Dmockbukkit.artifactId=${{ matrix.mockbukkit-artifactId }} -Dmockbukkit.version=${{ matrix.mockbukkit-version }} -Pfeature-tests -Dtest=*FeatureTest test
run: mvn -B -ntp -Pfeature-tests -Dtest=*FeatureTest test
2 changes: 1 addition & 1 deletion .github/workflows/maven-publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ jobs:
- name: Set up JDK 21
uses: actions/setup-java@v4
with:
java-version: '21'
java-version: '25'
distribution: 'temurin'
server-id: github # Value of the distributionManagement/repository/id field of the pom.xml
settings-path: ${{ github.workspace }} # location for the settings.xml file
Expand Down
605 changes: 84 additions & 521 deletions bbcode-topic.md

Large diffs are not rendered by default.

14 changes: 9 additions & 5 deletions docs/afk-kick-warnings.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
---
title: AFK Kick Warnings
nav_order: 1
parent: Configuration
nav_order: 4
parent: Features
---

# AFK Kick Warnings Feature
# AFK Kick Warnings

EzAfk supports configurable warning messages before a player is kicked for being AFK. This helps notify players and gives them a chance to return before being removed from the server.

Expand Down Expand Up @@ -49,5 +49,9 @@ You can use `%seconds%` as a placeholder for the time remaining.

If your kick timeout is 600 seconds (10 minutes) and intervals are `[60, 30, 10]`, players will be warned at 9:00, 9:30, and 9:50 of inactivity.

---
For more, see the main documentation or contact the plugin author.
## Related

- [AFK Kick](afk-kick): the underlying kick system this warning feature extends
- [AFK Detection](afk-detection): idle detection that starts the kick countdown
- [Messages](../messages): customise all warning message text
- [Permissions](../permissions): `ezafk.kick.bypass`
14 changes: 7 additions & 7 deletions docs/api/AfkReasons.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,12 @@ EzAfk uses the `AfkReason` enum to describe why a player's AFK status changed. B

## Possible AfkReasons

- `MANUAL` Player toggled AFK manually.
- `COMMAND_FORCED` Marked AFK by a staff command.
- `INACTIVITY` No recent player activity was detected.
- `ANTI_INFINITE_WATER` Bypass detection: sustained water flow movement.
- `ANTI_VEHICLE` Bypass detection: vehicle movement without input.
- `ANTI_BUBBLE_COLUMN` Bypass detection: bubble column movement.
- `OTHER` AFK status updated by the plugin.
- `MANUAL`: Player toggled AFK manually.
- `COMMAND_FORCED`: Marked AFK by a staff command.
- `INACTIVITY`: No recent player activity was detected.
- `ANTI_INFINITE_WATER`: Bypass detection: sustained water flow movement.
- `ANTI_VEHICLE`: Bypass detection: vehicle movement without input.
- `ANTI_BUBBLE_COLUMN`: Bypass detection: bubble column movement.
- `OTHER`: AFK status updated by the plugin.

Refer to the [PlayerAfkStatusChangeEvent](./events.md#playerafkstatuschangeevent) for how these reasons are used in events.
8 changes: 4 additions & 4 deletions docs/api/events.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,10 @@ EzAfk exposes custom events to allow other plugins to hook into AFK status chang
- **Fired:** When a player goes AFK or returns from AFK.
- **Cancellable:** Yes. Plugins can cancel AFK status changes.
- **Fields:**
- `Player getPlayer()` The player whose status changed
- `boolean isAfk()` `true` if now AFK, `false` if returned
- `AfkReason getReason()` Reason for AFK status change (see [AfkReasons](./AfkReasons.md))
- `String getDetail()` Additional details about the change
- `Player getPlayer()`: The player whose status changed
- `boolean isAfk()`: `true` if now AFK, `false` if returned
- `AfkReason getReason()`: Reason for AFK status change (see [AfkReasons](./AfkReasons.md))
- `String getDetail()`: Additional details about the change

### Example Listener

Expand Down
4 changes: 2 additions & 2 deletions docs/api/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,5 @@ EzAfk exposes a developer API for other plugins to integrate with AFK state chan

| Page | What it covers |
|------|----------------|
| [Events](events) | `PlayerAfkStatusChangeEvent` hook into AFK status changes |
| [AfkReasons](AfkReasons) | `AfkReason` enum reasons for AFK status changes |
| [Events](events) | `PlayerAfkStatusChangeEvent`: hook into AFK status changes |
| [AfkReasons](AfkReasons) | `AfkReason` enum: reasons for AFK status changes |
16 changes: 13 additions & 3 deletions docs/configuration.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
---
title: Configuration
nav_order: 4
has_children: true
---

# EzAfk Configuration Guide (Advanced)
# EzAfk Configuration Guide

This document provides advanced documentation for every configuration option in EzAfk's main configuration files.
This page lists every configuration option in EzAfk's main files.
For feature-level documentation, including explained config options, behaviour walkthroughs, and examples, see the **[Features](features/)** section.

---

Expand All @@ -18,6 +18,8 @@ This document provides advanced documentation for every configuration option in

### afk

See [AFK Detection](features/afk-detection) for a full walkthrough of these options.

- `timeout`: (int, seconds) Time of inactivity before a player is marked AFK.
- `bypass.enabled`: (bool) If true, enables the `ezafk.bypass` permission (OPs by default).
- `broadcast.enabled`: (bool) Broadcasts a message to all players when someone goes AFK.
Expand Down Expand Up @@ -53,6 +55,8 @@ This document provides advanced documentation for every configuration option in

### economy

See [Economy Costs](features/economy-costs) for a full walkthrough of these options.

- `enabled`: (bool) Enable economy-based AFK costs (requires Vault).
- `bypass-permission`: (string) Permission to bypass AFK costs.
- `cost.enter.enabled`: (bool) Charge when a player becomes AFK.
Expand All @@ -67,6 +71,8 @@ This document provides advanced documentation for every configuration option in

### kick

See [AFK Kick](features/afk-kick) and [AFK Kick Warnings](afk-kick-warnings) for full walkthroughs.

- `enabled`: (bool) Enable kicking after being AFK too long.
- `enabledWhenFull`: (bool) Enable kicking when server is full.
- `timeout`: (int, seconds) Time before kicking for AFK.
Expand All @@ -91,6 +97,8 @@ This document provides advanced documentation for every configuration option in

## gui.yml

See [In-Game GUI](features/gui) for a full walkthrough of these options.

### inventory-size

- (int) Number of slots in the GUI. Must be a multiple of 9, between 9 and 54.
Expand All @@ -110,6 +118,8 @@ Each action is a named section (e.g., `kick`, `alert`, `teleport`).

## mysql.yml

See [Storage / MySQL](mysql) for setup instructions.

- `enabled`: (bool) Enable MySQL storage for AFK data.
- `host`: (string) MySQL server address.
- `port`: (int) MySQL server port.
Expand Down
2 changes: 1 addition & 1 deletion docs/faq.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
title: FAQ & Troubleshooting
nav_order: 9
nav_order: 10
---

# FAQ & Troubleshooting
Expand Down
110 changes: 110 additions & 0 deletions docs/features/afk-detection.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
---
title: AFK Detection
nav_order: 1
parent: Features
---

# AFK Detection

The core feature of EzAfk. A player is marked AFK when they have not performed any tracked activity
(movement, block interaction, chat, etc.) for a configurable number of seconds. When they return,
the plugin marks them active and optionally announces the change.

## Configuration

In your `config.yml`:

```yaml
afk:
timeout: 300 # seconds of inactivity before a player is marked AFK
bypass:
enabled: true # allow ezafk.bypass permission to skip AFK detection
broadcast:
enabled: true # announce AFK state in chat
title:
enabled: true # send a title screen when going AFK
hide-screen:
enabled: false # apply a blindness-blur overlay while AFK
animation:
enabled: true # bob the player's name-tag while AFK (per-viewer)
display-name:
enabled: false # prepend/append a prefix/suffix to the in-game display name
prefix: "&7[AFK] "
suffix: ""
format: "%prefix%%player%%suffix%"
sound:
enabled: true
file: "mp3/ezafk-sound.mp3" # relative to plugin folder

unafk:
broadcast:
enabled: true # announce return from AFK in chat
title:
enabled: true # send a title screen when returning
animation:
enabled: true # stop name-tag animation on return
sound:
enabled: true
file: "mp3/ezafk-sound.mp3"
```

- **`afk.timeout`**: How long (in seconds) a player must be idle before EzAfk marks them AFK.
Default: `300` (5 minutes).
- **`afk.bypass.enabled`**: When `true`, players with the `ezafk.bypass` permission are never
marked AFK automatically. Default: `true`.
- **`afk.broadcast.enabled`** / **`unafk.broadcast.enabled`**: Send a chat message to all online
players when someone goes or returns from AFK. Messages are configured in your language file.
- **`afk.title.enabled`** / **`unafk.title.enabled`**: Show a large title overlay to the player
when their AFK state changes. Text is configured in your language file.
- **`afk.hide-screen.enabled`**: Apply a blindness effect to players while they are AFK,
preventing them from seeing the world. Default: `false`.
- **`afk.animation.enabled`** / **`unafk.animation.enabled`**: Toggle a bobbing animation on
the AFK player's name tag as seen by nearby players. Default: `true`.
- **`afk.display-name.enabled`**: Add a prefix and/or suffix to the player's display name while
AFK. Visible in chat and commands. Default: `false`.
- **`afk.display-name.prefix`** / **`suffix`**: Text to add before or after the player's name.
Supports `&` colour codes.
- **`afk.display-name.format`**: Full name format. Available placeholders: `%prefix%`,
`%player%`, `%suffix%`.
- **`afk.sound.enabled`** / **`unafk.sound.enabled`**: Play a sound when a player's AFK state
changes.
- **`afk.sound.file`** / **`unafk.sound.file`**: Path to an `.mp3` file in the plugin's data
folder.

## Customising Messages

Edit your active language file in `messages/` (e.g. `messages/en.yml`):

```yaml
afk:
broadcast: "&e{player} &7is now AFK."
title: "&eYou are AFK"
subtitle: "&7You have been marked as AFK."

unafk:
broadcast: "&e{player} &7is no longer AFK."
title: "&aWelcome back!"
subtitle: "&7You are no longer AFK."
```

See the [Messages](../messages) page for the full reference.

## How It Works

1. Every time a player moves, interacts, chats, or performs another tracked action, EzAfk records
when it happened.
2. A background task runs at a fixed interval and checks each player's idle time against
`afk.timeout`.
3. Once the threshold is exceeded, the player is marked AFK and the configured feedback is triggered
(broadcast, title, display-name change, animation, sound, blindness).
4. The moment any tracked activity is received from an AFK player, they are immediately marked active
and the `unafk` feedback fires.
5. Players with `ezafk.bypass` are skipped entirely unless `afk.bypass.enabled` is `false`.

## Related

- [Anti-Bypass Protection](anti-bypass): prevent waterflow/vehicle tricks from resetting idle time
- [AFK Kick](afk-kick): kick players that stay AFK too long
- [Tab Prefix Integration](../integrations/TabIntegration): show AFK status in the tab list
- [PlaceholderAPI Integration](../integrations/PlaceholderApiIntegration): use AFK placeholders in other plugins
- [Permissions](../permissions): `ezafk.bypass`, `ezafk.afk`
Loading
Loading