A small Paper plugin for servers with Bedrock players on Geyser/Floodgate. Version 1.2.0 targets Paper 26.3 (built against paper-api 26.3.build.194-stable), Java 25 and the Floodgate API 2.2.5.
What it does
- Logs Bedrock players' equipment changes. When a Floodgate player's armour changes, or a tool or weapon moves
in or out of a hand, BEB writes one line to plugins/BedrockEnchantBridge/logs/equipment-YYYY-MM-DD.log.
- Resends worn armour. A few ticks after a Bedrock player joins, and on startup and
/beb reload, BEB takes
their worn armour off and puts the same items straight back in the same tick, as 1.0.1 did. Each step is sent to the player's own client at once (Geyser passes it on to Bedrock), so their armour shows again. Paper checks equipment only once a tick and sees no change, so other players get nothing and there is no moment without armour.
- Cleans up after 1.1.0. Items that went through BEB 1.1.0 carry a hidden marker and extra lore lines. 1.2.0
puts them back the way they were (see "Upgrading from 1.1.0").
BEB never changes how combat works. The only items it ever changes are ones still marked by 1.1.0, and only to put them back the way they were (the armour refresh puts back exact copies).
What gets logged
- Every armour-slot change (head, chest, legs, feet).
- Main-hand and off-hand changes, but only when the old or new item is a tool or weapon: swords, axes,
pickaxes, shovels, hoes, spears, bows, crossbows, tridents, maces, shields and the like, any item with real durability (including pack items with their own durability), and enchanted non-block items such as golf clubs (a stick, blaze rod or breeze rod with Knockback). Paper, comparators and other plain items are not logged, even when they have a custom name or lore.
- Changes are noticed however they happen: clicks, scrolling, /give, other plugins, breaking, eating, dying
and respawning. Several changes in one tick become one line.
- Login baselines are saved silently. Attacks, movement and damage are never logged.
A line looks like this (times are in the configured time zone, Central by default):
2026-10-16 21:13:44.772 CDT player=.ExamplePlayer uuid=00000000-0000-0000-0009-000000000001 changed=MAIN_HAND:AIR->NETHERITE_SWORD loadout=HEAD=NETHERITE_HELMET CHEST=AIR LEGS=AIR FEET=AIR MAIN_HAND=NETHERITE_SWORD OFF_HAND=AIR
Lines are written in the background every flush-interval-ticks. If the disk can't be written, BEB keeps up to 10,000 waiting lines (each still filed under its own day), warns once, and says when writing works again. A write that stops halfway (for example on a full disk) is cut back to where it started, so the retry doesn't repeat lines. Only if the disk also refuses that cut-back can a line end up half-written or written twice. Files older than retention-days are deleted on startup and on reload.
What was removed in 1.2.0, and why
- The melee replay (1.0.1 and 1.1.0). It cancelled a Bedrock player's hit and asked Paper to run the very
same hit again. Paper calculated it the same way both times, so it fixed nothing, and it broke things: spear jabs hit one mob instead of several, Lunge fired twice, a second hit in the same tick was dropped, and other plugins saw every Bedrock hit twice. Golf does not need it.
- The item display rewrite and its database (1.1.0 only). 1.1.0 changed the real items Bedrock players
held: extra lore lines, hidden enchantments, forced glint and hidden data, plus a SQLite catalog. Shops saved the changed copies, only 1.1.0 could undo the change, and Geyser showed the normal enchant list anyway.
- The bundled catalog and SQLite driver have been removed.
60 KB, with nothing bundled.
The bedrockenchantbridge.bypass permission is still listed so existing permission setups stay valid, but it does nothing now; it only ever skipped the replay.
Configuration
bridge:
# Master switch. false turns off equipment logging and the armour refresh.
enabled: true
# Resend Floodgate players' worn armour after they join, on startup and on /beb reload.
refresh-equipped-armor: true
armor-refresh-delay-ticks: 3
equipment-logging:
enabled: true
console: false
flush-interval-ticks: 100
retention-days: 7
# Time zone for log timestamps and day files, e.g. America/Chicago or UTC. "system" uses the server's zone.
timezone: America/Chicago
| Key | Default | What it does |
|---|---|---|
bridge.enabled | true | Master switch for logging and the armour refresh. The 1.1.0 cleaner always runs. |
bridge.refresh-equipped-armor | true | Resend worn armour for Bedrock players. |
bridge.armor-refresh-delay-ticks | 3 | Wait this long after a join so Floodgate and the inventory are ready (minimum 1). |
equipment-logging.enabled | true | Write the equipment log. |
equipment-logging.console | false | Also print each line in the console. |
equipment-logging.flush-interval-ticks | 100 | How often lines are written (minimum 20). |
equipment-logging.retention-days | 7 | Delete log files older than this. |
equipment-logging.timezone | America/Chicago | Zone for timestamps and for when a new day file starts. system = the server's zone. |
A value of the wrong type (for example enabled: yes please) or an unknown time zone falls back to the default, with a warning. Numbers may be written with a decimal point, as 1.0.1 allowed (200.0 is 200; a fraction is rounded down, with a warning).
Config safety. On startup BEB adds any keys your config.yml is missing and removes keys it no longer uses, after saving the old file as config.yml.bak (or config.yml.bak.2, .3 ... if a backup already exists). The console says exactly what changed. If the upgraded file can't be saved (for example a read-only plugin folder), your file is still used as it is, missing keys run on their defaults, and the console and /beb status say why. A file that isn't saved as UTF-8 still loads, as it did in 1.0.1, but BEB won't rewrite it (that would garble the characters it can't read), so it is not upgraded; the console and /beb status say so. A file with a YAML mistake is never overwritten: on startup BEB runs on its built-in defaults and says why; /beb reload refuses the file, keeps the settings already running, and tells you the line and column of the problem.
Commands
/bedrockbridge status each feature's state, Floodgate players, cached loadouts, any config.yml problem
/bedrockbridge inspect <player> whether the player is on Bedrock, and what they are wearing and holding
/bedrockbridge reload re-read config.yml (refuses a broken file)
/beb works too. reload applies every setting, including a new time zone and write interval.
Permissions
| Permission | Default | Purpose |
|---|---|---|
bedrockenchantbridge.admin | Operators | Status, inspect and reload. |
bedrockenchantbridge.bypass | False | Does nothing since 1.2.0 (kept so permission setups stay valid). |
Upgrading from 1.0.1 (what live ran on 26.2)
- Move the server to Paper 26.3 first. 1.2.0 declares
api-version: '26.3'and will not load on Paper 26.2.
Use a Floodgate build that starts on 26.3 (see Requirements). Then replace BedrockEnchantBridge-1.0.1.jar with BedrockEnchantBridge-1.2.0.jar and start the server.
- BEB backs up
config.ymltoconfig.yml.bak, addsequipment-logging.timezone, and removes
bridge.replay-unsafe-melee, bridge.minimum-ticks-between-replays and bridge.maximum-target-distance. Your other values stay as they were.
- Old log files are left alone. From now on lines use Central time and there are no blank lines between them.
Day files now start at midnight Central. Before, they rolled over at midnight UTC, which is 7 pm Central in summer and 6 pm in winter.
Upgrading from 1.1.0
- Replace the jar and start the server (Paper 26.3, as above). The
sanitizerandcatalogsections are
removed from config.yml (after the backup above).
- If
item-catalog.dbis in the plugin folder, BEB logs that it is safe to delete. 1.2.0 never opens it. - Items 1.1.0 changed still carry its hidden marker and the original lore it saved. 1.2.0 restores them when a
player joins (their inventory, armour and off hand), when someone opens a chest, barrel, shulker box, ender chest or other storage, when a player picks one up (a thrown trident included), and when one lands in a hand or armour slot. It removes only the lines 1.1.0 added, keeps lore that was edited or added since, puts back the hide-enchantments setting and glint the item had before, and removes the marker. Only items with the marker are touched.
- The item's own lore lines are never removed. While the lore still starts with what the item had before
1.1.0, only lines after that can go, and only if they still look exactly like 1.1.0's: an enchantment's name (Minecraft's own, or one of the item's enchantments) and its level, in one colour, not italic.
- If the lore is already back to what it was before 1.1.0 (another plugin rebuilt it, say), it is left as it is.
- If 1.1.0's lines can't be told apart from the item's own (the lore was edited or rebuilt since), nothing is
removed and the console names the item and where it was. For example, on an item that had no lore before 1.1.0, another plugin's enchantment line added since looks just like 1.1.0's, so both stay.
- Plugin menus are not touched, and BEB doesn't look inside other items. Items in bundles or in shulker boxes
carried as items, in item frames or on armour stands, or in shop listings saved by other plugins stay marked until one of the moments above: the item is in a player's inventory when they join, is in a container someone opens, is picked up off the ground, or goes into a hand or armour slot. Taking an item out of a bundle into the inventory is not one of those moments.
Requirements
- Paper 26.3 and Java 25.
- Floodgate on the Paper server, a build that starts on 26.3 (in testing, Floodgate b141 did and b138 did not).
If Floodgate fails to start, BEB says so and treats everyone as a Java player.
- When Geyser/Floodgate runs on a proxy, Floodgate must also be installed on the backend so BEB can tell who is
on Bedrock.
Building
./gradlew test jar --offline
The jar lands in build/libs/BedrockEnchantBridge-<version>.jar. Each release is kept in dist/<version>/ (jar, zip, source zip and SHA256SUMS; 1.0.1 and 1.1.0 are there too). As with every EGW project, don't run gradle clean. See CHANGELOG.md for the version history.