Major release: first-class client-side integration, an experimental datapack runtime, improved bridge reliability and player-data handling, documentation and configuration enhancements, and packaging updates.
- Client-mod integration: optional, bidirectional server↔client protocol with session management and permission negotiation enabling richer client-side features.
- Datapack runtime (experimental): in-memory datapack registration for models, advancements, predicates and registry entries without writing resource files.
- Player-data & reliability: more reliable UUID/name resolution, safer lifecycle/reconnect handling, and improved field caching.
- Documentation & examples: new clientmod and datapack guides, updated examples, and improved docs site.
- Configuration & deployment: new options for timeouts, message size limits, and Python runtime path.
- Packaging & tooling: build and packaging updates to support client features.
- Stability fixes: targeted crash and edge-case fixes, and cleanup of lifecycle races.
-
Client mod support
- Adds an optional client-side bridge allowing server scripts and plugins to send commands and structured data to a cooperating client mod and receive responses/events.
- Permission negotiation occurs at session start; scripts must handle availability, denials, and timeouts gracefully.
-
Datapack runtime (experimental)
- API to register datapack content (models, advancements, predicates, registry entries) at runtime; best-effort and intended for dynamic/testing scenarios.
-
Configuration
- New configuration keys for message size, timeouts, and Python runtime; review settings for production deployment.
Performance overhaul — fire-and-forget calls, msgpack wire protocol, field cache invalidation, and reduced serialization payload.
- Added
call_fire_forget()toBridgeConnection— sends calls withno_response: true, skipping Future creation and await - Added
_call_ff()toProxyBase— convenience wrapper for fire-and-forget bridge calls - Java
handleCall()checksno_responseflag and skips result serialization + response sending - Java
executeBatchCalls()supportsno_responseper-call in both atomic and non-atomic batch paths - Converted ~80+ void/setter methods on Entity and Player to fire-and-forget:
- Entity:
teleport,remove, velocity setter,fire_tickssetter,add_passenger/remove_passenger,custom_namesetter, gravity/glowing/invisible/invulnerable/silent/persistent/collidable setters,portal_cooldown/freeze_tickssetters,eject,leave_vehicle,set_rotation - Entity (Mob):
targetsetter,is_awaresetter,stop_pathfinding,remove_all_goals - Player:
damage,send_message,chat,kick,give_exp,add_effect/remove_effect,set_game_mode,set_scoreboard,set_op,play_sound,send_action_bar,send_title, tab list setters, health/food/level/exp setters, flying/sneaking/sprinting setters, walk/fly speed setters,send_resource_pack, absorption/saturation/exhaustion setters,allow_flightsetter,hide_player/show_player,open_book,send_block_change,send_particle,set_cooldown,set_statistic,max_healthsetter,bed_spawn_location/compass_targetsetters,set_persistent_data
- Entity:
- Added
_invalidate_field()toProxyBase— removes cached field values so next access fetches fresh data from Java - Setters that modify cached values call
_invalidate_field()before sending the fire-and-forget call, preventing desync:teleport→ invalidateslocation,worldgive_exp→ invalidatesexp,levelset_game_mode→ invalidatesgameMode,game_modeset_health→ invalidateshealthset_food_level→ invalidatesfoodLevel,food_levellevelsetter → invalidateslevelexpsetter → invalidatesexpmax_healthsetter → invalidateshealth
- Removed
inventoryfrom Player auto-serialization inBridgeSerializer— inventory is now fetched on demand instead of included in every Player object
- Python: 3-tier serialization import chain — msgpack → orjson → stdlib json
- Python: handshake message sent as JSON on connect to negotiate format with Java
- Java:
handleHandshake()switchesuseMsgpackflag on format negotiation - Java:
serializePayload()/deserializePayload()helper methods convert JsonObject ↔ msgpack at IPC boundaries - Java: full msgpack ↔ Gson
JsonElementtree conversion (unpackValue,packJsonElement) handling all types (nil, bool, int, float, string, binary, array, map) - Java:
bridgeLoop(),send(),sendAll(),sendWithTiming()all route through format-aware helpers - Build: added
com.gradleup.shadowplugin,org.msgpack:msgpack-core:0.9.8shaded and relocated tocom.pyjavabridge.libs.msgpack copyPluginJarandcopyReleaseJartasks updated to depend onshadowJar
- Fixed stray
return self._call_sync(method)in_invalidate_fieldthat would have crashed on every call
Plugging some old holes in the API. Full codebase cleanup. New extensions.
- LootTable: weighted loot pools with conditional entries, rolls, bonus rolls, and stacked item generation
- PlaceholderRegistry: register
%placeholder%expansions with per-player context and batchresolve_many() - TabList: full tab-list customization with header/footer templates, fake entries, groups with prefix/priority sorting
- Scheduler: cron-like real-world-time scheduling with
@every()/@after()decorators and cancellation - StateMachine: per-entity/player state machines with
on_enter/on_exit/on_tickcallbacks and event-driven transitions - Schematic: extracted from Dungeon into standalone module (SchematicCapture.java + schematic.py)
@preservedecorator: hot-reload state persistence — caches return value to JSON and restores across/pjb reload- Custom events:
fire_event(event_name, data)— scripts can fire and listen to custom events across scripts - Villager trade API:
Villager.recipes(get/set),recipe_count,add_recipe(),clear_recipes() - Entity subtypes: ArmorStand, Villager, ItemFrame, FallingBlock, AreaEffectCloud proxy classes
- Entity properties:
velocity,fire_ticks,custom_name,gravity,glowing,invisible,invulnerable,silent,persistent,collidable,bounding_box,metadata - Entity AI goals:
goal_types,remove_goal(),remove_all_goals() - Player:
hidePlayer/showPlayer/canSee,openBook,sendBlockChange,sendParticle, cooldown management, statistics,getMaxHealth/setMaxHealth, bed spawn, compass target, PersistentDataContainer access - Block:
getDrops,getHardness,getBlastResistance, PersistentDataContainer for tile entities - Item: durability, enchantments, item flags,
isUnbreakable/setUnbreakable - World: game rules, world border,
getHighestBlockAt,generateTree,getNearbyEntities,batchSpawn, structure API (save/load/delete/list),createWorld/deleteWorld Entity.__bool__()viaisValid()
- Comprehensive docstrings added across all Python modules and extensions
- Whitespace normalization and consistent spacing across Python source
- Type annotations tightened: lazy imports, Optional callables, assertion guards
str.removeprefix()modernization replacingstartswith()+ sliceserver.playersde-awaited in guild, leaderboard, npc, region (was incorrectlyawaited)@preserveevent handlers fixed: lambdas replaced with proper async def functions- Gradle deprecation linting enabled:
-Xlint:deprecation - Gradle wrapper (8.5) added and committed
- BridgeInstance: 24 pre-sized ArrayList/HashMap allocations (completions, handles, release ids, batch calls, responses, args, tab complete results, merchant recipe ingredients, sign lines, goal types, recipes, lore, item flags, structures, enchantments, spawn batches, tab list setter, process command)
- BridgeInstance:
getDrops→List.copyOf(),clearRecipes→List.of() - BridgeSerializer: stream→manual loop for list serialization, 6 pre-sized ArrayList/HashMap allocations, early
List.of()return for null modifiers - SchematicCapture: 11 pre-sized collections (HashMap, LinkedHashMap, ArrayList, HashSet), 3 stream→manual loop conversions (sum, sort)
- EventDispatcher: simplified
getCachedMethod(), pre-sized lists indispatchBlockMulti() - ScriptCommand, PermissionsFacade, RaycastFacade, RegionFacade, EntitySpawner: pre-sized result collections
_ENUM_TYPE_MAPPINGmoved to module-level constant (was recreated on every enum deserialization)_JSON_SEPARATORS,_RESERVED_KWARGSfrozenset,_MINECRAFT_PREFIXEStuple: module-level constantsevent_batchhandler inlined: avoids temp dict creation and full re-dispatch per payload- XYZ key check: direct
"x" in dinstead offrozenset.issubset(d.keys()) import math/import loggingmoved to module-level__slots__added to 27 classes across helpers and extensions- NPC: squared distance comparison, cached NPC coords outside player loop
- Region: batch-fetch all player locations once per tick, inline bounds check
- Mana: cached instance attributes as locals in regen loop
- Bank: inlined
transfer()to avoid double_save() - Levels: eliminated double xp lookup in
add_xp,xp_to_next/progressavoid redundant bridge call - Ability: inlined
remaining_cooldownto avoid doubletime.time()+ double dict lookup - Leaderboard: tuple-based medal lookup
- Quest: cached locals in async task loops
- Guild: cached member set reference and pre-formatted message
- Cooldown: single
.get()lookup instead ofin+[] _toml_write_table(): single-pass scalar/sub-table separation- UUID cache eviction: indexed deletion instead of slice allocation
- Fixed
server.playersincorrectly awaited in guild, leaderboard, npc, region - Fixed
@preserveevent handler signature (lambda → async def) - Fixed
setLoreNPE when argument is not a list (added else branch withList.of())
- Docs updated for all new extensions (LootTable, Placeholder, TabList, Scheduler, StateMachine)
- Villager trade API documented
- Type stubs updated for all new APIs and extensions
- Gradle wrapper committed
Performance optimization pass — caching, data structures, and hot-path improvements across Java and Python.
- Cache
getMethods()per class inBridgeInstanceandRefFacade— avoids repeated reflection on every reflective invoke - Cache NMS reflection handles (
parseTag,getHandle,spawnNonLivingNmshelpers) inEntitySpawnerstatic fields - Cache LuckPerms API instance and
Node/InheritanceNodeclass objects inPermissionsFacade - Cache resolved event classes across 13 Bukkit packages in
EventSubscription— avoids repeatedClass.forName()for known events - Build static
Map<String, PacketType>lookup tables inPacketBridge— O(1) packet type resolution - Merge dual method+miss cache into single
ConcurrentHashMap<String, Optional<Method>>inEventDispatcherandBridgeSerializer - Cache
getLogicalTypeName()per concrete class inBridgeSerializer— avoids repeated instanceof chains on every serialize
- ThreadLocal
ByteArrayOutputStreamforsend()— avoids allocation per outgoing message - ThreadLocal identity-hash set for cyclic reference detection in
serialize()— reused across calls - New
sendAll()method batches multiple responses under a single lock + flush for batch/frame calls - ItemStack meta: call
displayName()/lore()once with null check instead ofhas*()+get*()pairs - Shallow top-level entry copy instead of
deepCopy()for per-block event payloads inEventDispatcher - Reduce per-pixel overhead in
spawnImagePixels— singleget()+ null check replaceshas()+get()pairs
BridgeInstance.invoke(): else-if dispatch chain replaces sequential instanceof checks — first match winsObjectRegistry:StampedLockreplacessynchronizedblock — lock-free reads viaConcurrentHashMap, write lock only for register/releaseDebugManager:CopyOnWriteArraySetreplacessynchronizedSet— writes are rare (toggle), reads (broadcast) are frequentPlayerUuidResolver: staticHttpClientsingleton, LRU eviction viaLinkedHashMap, cachedusercache.jsonparse with timestampScriptCommand: pre-lowercase tab completions at registration — avoidstoLowerCase()on every keystrokeEventSubscription.lastDispatchNanomarkedvolatilefor safe cross-thread reads- Expanded thread-safe method sets for
Server,OfflinePlayer, andEntity— more calls skip main-thread dispatch - Static empty
JsonObjectsentinel for missingargsininvoke()
- Lazy module-level imports in
connection.py—_ensure_lazy_imports()populates references once, avoids per-callimport/fromoverhead - Single-handler fast path in
_dispatch_event— when only 1 handler, directly call+await without list/gather overhead _build_call_message()extracted — shared betweencall()andcall_sync(), eliminates duplicated message construction- Release queue changed from
listtoset— O(1)discard()replaces O(n)list.remove() _maybe_flush_releases()avoids unnecessary flushes on every call — only flushes when queue ≥ 16_read_exact()optimistic fast path — singleos.read()often returns full message, skipsbytearrayaccumulationsend()writes header+data in onewrite()call instead of two
- Lazy module-level dispatch table for
_proxy_from()— suffix/contains lookup tables built once, avoids per-call dict literal and import - Handle-first fast path in
ProxyBase.__eq__— same handle means same Java object, skip field comparison EnumValue.from_name()dict cache — returns cached instances for repeated enum lookups- Bounded
_player_uuid_cache(max 1000, evict oldest quarter) in bothwrappers.pyandutils.py - Consolidated
isinstancechecks indecorators.pycommand wrapper — merged two separate branches into one
State._instancesusesweakref.ref— allows garbage collection of unreferenced State objects- Shutdown handler updated to dereference weakrefs
Major expansion — extensions, tooling, networking overhaul. Many internal changes, cleanup and optimization.
- Switched from TCP sockets to stdin/stdout pipes for faster IPC
- Java-side method resolution now supports snake_case → camelCase fallback
- Version compatibility improvements and removed slow regex
- Entity AI: targeting, pathfinding, awareness, line of sight, look_at
- Entity tags: add_tag, remove_tag, tags, is_tagged (Python-side, UUID-keyed)
- Entity attributes: yaw, pitch, look_direction, equipment
- Player: selected_slot, freeze/unfreeze (tick-based position lock), vanish/unvanish
- Player extension shortcuts: balance, deposit, withdraw, mana, xp, player_level
- Block/tile entity API: signs, furnaces, containers
- Recipe API: shaped, shapeless, furnace recipes as a class
- Firework effects API with builder pattern
- Resource pack API on Player
- Enchantment discovery: Enchantment.all() and Enchantment.for_item()
- Packet API via ProtocolLib: on_packet_send, on_packet_receive, send_packet
- Inter-script messaging: script_send, on_script_message, get_scripts
- WorldTime class and @world.at_time() decorator for time-based events
- World: create_explosion, entities_near, blocks_near
- Location: + and - operators (with Location and Vector)
- Vector: +, -, * operators (scalar, component-wise, and reverse)
- 18 new enum types (DamageCause, Enchantment, ItemFlag, etc)
- Respawn location override (return Location from player_respawn)
- entity_target event return value handling: override target by returning Entity
- @event .unregister() method to remove event handlers at runtime
- Paginator: multi-page menu with nav buttons
- Quest / QuestTree: objectives, bossbar timers, branching quest lines
- Dialog: branching conversations with timeouts and NPC linking
- Bank: persistent balances with transaction events
- Shop: paginated GUI with bank integration
- TradeWindow: confirm/cancel with anti-dupe delay
- Ability / ManaStore / CombatSystem / LevelSystem
- Region / Party / Guild / CustomItem
- Leaderboard / VisualEffect / PlayerDataStore
- Dungeon: procedural room generation with WFC algorithm
- @command: cmd_ prefix auto-stripping (def cmd_greet → /greet)
- @command: dynamic tab completions via @my_command.tab_complete
- @command: static tab_complete parameter with wildcard support
- Persistent State class with auto-save on shutdown
- Extended event payload: action, hand, from, to, cause, velocity, reason, message, new_slot, previous_slot, amount, slot, etc
- Event.world and Event.location now auto-derive from entity/player when not directly available
- snake_case field access auto-resolves to Java getters (getNewSlot, isPreviousSlot, etc)
- Java stack traces included in Python exceptions
- 18 typed error subclasses (EntityGoneException, MethodNotFoundException, etc)
- Menu: race condition fix when opening a new menu from a click handler
- ActionBarDisplay: immediate refresh on creation (no longer waits for server_boot)
- BossBarDisplay: renamed link_cooldown to linked_to (old name kept as compat alias)
- ManaStore: auto-cleanup on player disconnect, regen loop crash protection
- NPC: dialog linking, player linking, range enter/exit callbacks
pjbCLI tool:pjb search <query>andpjb events [filter]- Doc site full-text search bar with Ctrl+K shortcut
- Gradle copyBridgePython task: auto-deploys bridge to server on build
- Restructured bridge.py into proper Python module (connection, wrappers, helpers, types, utils, decorators)
- Documentation for events, execution, lifecycle, and serialization internals
- Type stubs for all extensions
- Comprehensive Event stubs with all event-specific fields
- MeshDisplay helper (WIP)
- Updated wiki/docs site with all new pages
Feature update
- Optimized for single request latency
- Use batching to speed up multiple requests
- Now uses orjson for faster parsing
- Automatic handle management
- Tab list API on Player
- Region utils on World: .set_block, .fill, .replace, .fill_sphere, .fill_cylinder, .fill_line
- Particle shapes on World
- Entity spawn helpers on World: spawn_at_player, spawn_projectile, spawn_with_nbt.
- Support for command execution on Server
- world.entities property
- RaycastResult.distance and .hit_face
- Sidebar: Scoreboard helper
- Config: TOML config helper
- Cooldown: Automatically manage cooldowns
- Hologram: Show floating text
- Menu / MenuItem: Create easy chest GUIs
- ActionBarDisplay: Manage action bars easily
- BossBarDisplay: Manage boss bars easily
- BlockDisplay: Show fake blocks
- ItemDisplay: Show floating items
- ImageDisplay: Show images in the world
- ItemBuilder: Easily create items
- Shutdown event
- Fixed type errors regarding EnumValue not matching its child classes
- EntityGoneException now extends BridgeError
- @task decorator: Run tasks on an interval
- Added event priority and throttle_ms parameters
- Added command description parameter
- Location: .add, .clone, .distance, .distance_squared are now sync
- Scoreboard, Team, Objective, and BossBar creation methods are now sync
- Config: Added support for multiple formats, toml (default), json, and properties.
- Commands can be now executes as console
- Entity class moved before Player
- Moved most of the code from a single file to multiple
- Improved typing across python bridge
- Added dev versioning for non-release commits
- Added wiki site
Damage event
Changes:
- Added damage override to damage events
- Added damage source and damager attributes to damage events
- Added shooter attribute to projectile entities
- Added owner attribute to tamed entities
- Added is_tamed attribute to entities
API cleanup
Changes:
- Added call_sync and field_or_call_sync helpers
- Turned most attribute-like methods into attributes
- Made all attributes synchronous
- Added create classmethods to most classes
- Added optional args to world.spawn_entity
- Allowed spawning of non-living entities
- Fixed player UUID lookups
- Chat event return value is now the chat message format for that event
- Bugfixes
API expansion
Changes:
- Implemented most missing APIs
- Added proper command argument parsing
- Added docs
- Bugfixes
Initial release
Changes:
- Added most common APIs