Client-mod API consolidation release — high-level Python interface, cleaner extension exports, and client/runtime hardening and readability improvements.
- Consolidated extension entrypoints around
client_modfrombridge.extensions.client_mod. - Standardized per-player calls through
ClientModSession(cm = client_mod.session(player)) and methods likecm.command(...),cm.register_script(...),cm.raycast(...),cm.stream_audio_file(...), andcm.stream_audio_generator(...). - Exposed singleton decorators/registries for event and payload wiring:
client_mod.on_client_data,client_mod.on_permission_change,client_mod.register_request_data(...),client_mod.unregister_request_data(...). - Normalized exports in
bridge.extensionsto favor object-based API usage (ClientMod,ClientModSession,client_mod) over flat helper-only usage.
- Added script-only event bookkeeping via
BridgeInstance.scriptOnlySubscriptionssoBridgeInstance.hasSubscription(...),BridgeInstance.getSubscriptionNames(), and dispatch matching include script-local handlers. - Added
BridgeInstance.resolveInvokeResult(...)to correctly resolve nestedCompletableFuturereturns with timeout/error propagation into Python responses. - Corrected broadcast path in
BridgeInstanceto returnserver.broadcastMessage(...)behavior instead of log-only side effects. - Hardened material handling in
BridgeInstance(sendBlockChange) with explicitMaterial.matchMaterial(...)null checks beforeBukkit.createBlockData(...).
- Added
BridgeSerializerinventory validation indeserialize(...)(sizemust be9..54and a multiple of9). - Removed implicit world mutation from
BridgeSerializerBlockdeserialization (no hiddenblock.setType(...)side effects). - Tightened boolean coercion in
BridgeSerializer.coerceArg(...)to explicitly parse"true"/"false"string inputs. - Added operation volume/bounds enforcement in
RegionFacade.pasteOperations(...)and stronger invalid block-data failure behavior inRegionFacade.parseBlockData(...). - Enforced protocol-version validation in
ClientModFrameCodec.decodeFrame(...).
- Added
BridgeConnectionlocking around_pending_syncand batching (_pending_sync_lock,_batch_lock) to avoid races between reader/caller threads. - Made
BridgeConnection.flush()atomically snapshot/clear_batch_messagesand_batch_futuresbefore sendingcall_batch. - Added atomic abort reporting:
server.atomic()now yields an int-like counter (with server.atomic() as num_failed:/async with ...) andserver.flush()returns the aborted-call count for the flushed batch. - Hardened disconnect/shutdown behavior in
BridgeConnectionso pending sync waits are finalized safely and loop stop is scheduled via_loop.call_soon(...). - Added proxy handle refcount synchronization in
wrappers.py(_handle_refcounts_lock) and deterministic LRU-like UUID cache helpers (_cache_get_player_uuid,_cache_set_player_uuid).
- Prevented duplicate updater loops by task dedupe/cancel logic in
ability.py,combat.py,quest.py,leaderboard.py,scheduler.py, andstate_machine.py. - Added malformed persistence recovery for JSON-backed extensions (
bank.py,levels.py,player_data.py,guild.py). - Fixed transactional currency flow in
trade.pyby checkingbank.withdraw(...)return values and refunding on partial failure. - Tightened parsing/validation across build and content tooling (
dungeon.py,schematic.py,mesh_display.py,image_display.py,loot_table.py,tab_list.py). - Improved client audio stream cleanup/failure surfacing in
client_mod.py(ffmpegstdout checks, producer error propagation, guaranteed stop infinally).
- Reworked docs keying in
docs/build.pyto avoid basename collisions in output names, source maps, and search URLs. - Pinned
.github/workflows/deploy-pages.ymltool dependencies for reproducible docs deployments. - Added missing extension files to
src/main/resources/python/bridge/MANIFEST(loot_table.py,placeholder.py,scheduler.py,schematic.py,state_machine.py,tab_list.py). - Updated stubs to reflect API/type reality in
bridge/__init__.pyiandbridge/extensions/__init__.pyi.
Client integration foundation release — first-class client bridge support, experimental runtime datapack tooling, and broad reliability/configuration improvements.
- Added optional client-mod transport wiring centered on
ClientModChannelBridge,ClientModSessionManager, andClientModProtocol. - Enabled request/response messaging and event ingress from cooperating client mods through bridge call targets.
- Added session-time permission negotiation so scripts can branch on allowed/denied/unavailable capabilities.
- Added runtime registration APIs in
DatapackFacadefor advancements, predicates, model JSON, and registry-scoped entries. - Implemented best-effort application semantics for dynamic/testing workflows rather than strict static-pack replacement.
- Improved core runtime behavior in
PyJavaBridgePlugin,BridgeInstance, and event dispatch paths for startup/shutdown and timeout handling. - Improved failure tolerance around player/session state transitions and partial runtime disconnect scenarios.
- Expanded
config.ymlknobs for bridge payload sizing, timeout behavior, and Python runtime settings. - Updated docs/deploy defaults so production operators can tune bridge throughput and timeout behavior more predictably.
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 release — extension ecosystem growth, networking overhaul, API breadth increase, and substantial internal cleanup.
- Switched bridge transport from TCP sockets to
stdin/stdoutpipes for lower IPC overhead. - Added snake_case -> camelCase fallback in Java invoke resolution so Python calls map cleanly onto Bukkit-style methods.
- Removed slow regex-heavy compatibility paths from hot invoke routes.
- Expanded
EntityAI and behavior APIs: targeting, pathing, awareness, line-of-sight, and look-direction controls. - Added entity tag helpers (
add_tag,remove_tag,tags,is_tagged) with UUID-keyed Python-side state. - Added entity orientation/equipment-style fields (
yaw,pitch,look_direction, equipment accessors). - Added player-side controls (
selected_slot, freeze/unfreeze movement lock,vanish/unvanish). - Added convenience extension fields (
balance,deposit,withdraw,mana,xp,player_level) onPlayerworkflows. - Expanded block/tile APIs (signs, furnaces, container-style interactions) and recipe APIs (shaped, shapeless, furnace recipe wrappers).
- Added firework builder flows and resource-pack controls on
Player. - Added enchantment discovery APIs (
Enchantment.all(),Enchantment.for_item(...)).
- Added ProtocolLib packet APIs:
on_packet_send(...),on_packet_receive(...),send_packet(...). - Added inter-script messaging APIs:
script_send(...),on_script_message(...),get_scripts(). - Added
WorldTimeand@world.at_time(...)time-hook decorators. - Expanded world utility methods (
create_explosion,entities_near,blocks_near).
- Added operator support for
Location(+,-) andVector(+,-,*, reverse multiply patterns). - Added broad enum coverage (including
DamageCause,Enchantment,ItemFlag, and related gameplay enums). - Added
player_respawnreturn override support (returnLocationto redirect respawn). - Added
entity_targetreturn override support (returnEntityto set target). - Added runtime unregistration support via
@event ... .unregister().
- Added helper/feature modules:
Paginator,Quest/QuestTree,Dialog,Bank,Shop,TradeWindow. - Added gameplay systems:
Ability,ManaStore,CombatSystem,LevelSystem. - Added social/data/world systems:
Region,Party,Guild,CustomItem,Leaderboard,VisualEffect,PlayerDataStore,Dungeon.
- Enhanced
@commandwith automaticcmd_prefix stripping (def cmd_greet->/greet). - Added dynamic completion via
@my_command.tab_complete. - Added static completion lists via
tab_complete=parameter (including wildcard-style matching). - Added persistent
Statehelper with shutdown save behavior.
- Expanded event payload fields (
action,hand,from,to,cause,velocity,reason,message,new_slot,previous_slot,amount,slot, etc). - Added
Event.world/Event.locationauto-derivation from related player/entity context. - Added snake_case event field access fallback to Java getters (
getNewSlot,isPreviousSlot, etc). - Propagated Java stack traces into Python exceptions and added typed error classes (for example
EntityGoneException,MethodNotFoundException).
- Fixed
Menurace when opening a second menu from click callbacks. - Improved display helpers (
ActionBarDisplayimmediate refresh;BossBarDisplayrenamelink_cooldown->linked_towith compatibility alias). - Hardened helper runtime behavior in
ManaStoreandNPCloops/callback linkage. - Added CLI tooling with
pjb search <query>andpjb events [filter]. - Added docs search UX (
Ctrl+K) and GradlecopyBridgePythonauto-deploy task. - Split monolithic
bridge.pyinto modular runtime units (connection,wrappers,helpers,types,utils,decorators) and expanded internals docs/stubs.
Feature expansion release — batching and IPC performance work, new world/player APIs, and helper/tooling growth.
- Optimized request flow for lower single-call latency.
- Added call batching for multi-operation workloads.
- Added
orjsonfast-path parsing support. - Added automatic handle lifecycle management to reduce stale-object leaks.
- Added tab-list APIs on
Player. - Added world region helpers on
World:set_block(...),fill(...),replace(...),fill_sphere(...),fill_cylinder(...),fill_line(...). - Added world particle-shape helpers and entity spawn helpers (
spawn_at_player(...),spawn_projectile(...),spawn_with_nbt(...)). - Added server command execution support from bridge-side runtime calls.
- Added
world.entitiesand expanded raycast results (RaycastResult.distance,RaycastResult.hit_face).
- Added UI/state helpers:
Sidebar,Menu,MenuItem,ActionBarDisplay,BossBarDisplay. - Added config/state helpers:
Config(TOML-first),Cooldown, shutdown event hooks. - Added display helpers:
Hologram,BlockDisplay,ItemDisplay,ImageDisplay. - Added item workflow helper:
ItemBuilder.
- Fixed enum typing mismatches around
EnumValueand child enum wrappers. - Made
EntityGoneExceptionderive fromBridgeError. - Added
@taskinterval scheduling decorator. - Added event controls (
priority,throttle_ms) and command metadata (description). - Made attribute-like methods synchronous for core wrappers (
Location.add/clone/distance/distance_squared, scoreboard/team/objective/bossbar creation). - Added multi-format
Configpersistence (toml,json,properties). - Expanded command execution paths to include console execution.
- Reordered core wrappers to define
EntitybeforePlayerfor cleaner inheritance flow. - Split large single-file bridge implementation into multiple modules.
- Improved bridge typing coverage and early wiki/documentation rollout.
- Added dev-version labeling for non-tag builds.
Damage and ownership update — expanded combat event payloads and entity ownership metadata.
- Added damage override support on damage-event handlers.
- Added damage context fields (
damage source,damager) on event payloads. - Added projectile ownership access via shooter metadata.
- Added tame/ownership metadata on entities (
owner,is_tamed).
API cleanup update — synchronous attribute model improvements and quality-of-life fixes.
- Added sync helper paths (
call_sync(...),field_or_call_sync(...)). - Converted method-like fields into direct attributes where appropriate.
- Made wrapper attributes consistently synchronous.
- Added
create(...)classmethod patterns across major wrappers. - Expanded
world.spawn_entity(...)optional argument handling. - Allowed non-living entity spawn paths.
- Fixed player UUID lookup edge cases.
- Made chat-event return values control outgoing chat format.
- Included additional bug-fix follow-through across wrapper internals.
API expansion update — broader API coverage and initial documentation rollout.
- Implemented most missing core APIs from early bridge targets.
- Added structured command argument parsing behavior.
- Added initial documentation pages for bridge usage.
- Included stabilization bug fixes.
Initial release — first bridge implementation with core scripting APIs.
- Added the first set of commonly used bridge APIs across server, player, world, and event workflows.