Commands, raycast teleport, inventory GUIs, and click events

Commands Example

This script demonstrates several core PyJavaBridge features: registering chat commands, using raycast for teleportation, creating inventory-based GUIs, and handling inventory click/drag events.

Drop this file into plugins/PyJavaBridge/scripts/ and reload the server.

from bridge import *
from typing import Optional

# ─── Simple Command ─────────────────────────────────────────────────
# The @command decorator registers a chat command whose name matches
# the function name.  The string argument becomes the command's
# description (shown in /help).
#
# Every command handler receives an Event object.  event.player is the
# Player who ran the command.

@command("Hello world command")
async def helloworld(event: Event):
    """Send a Hello World message to the player."""
    # send_message() delivers a chat message only to this player.
    event.player.send_message("Hello, World!")


# ─── Command with Arguments ────────────────────────────────────────
# Extra function parameters after `event` become command arguments.
# Optional parameters (with a default) are not required in-game.
# PJB auto-generates the usage string from the function signature.

@command("Greet command")
async def greet(event: Event, name: Optional[str] = None):
    """Greet a player by name."""
    # If the player doesn't supply a name, fall back to their own name.
    event.player.send_message(f"Hello, {{name or event.player.name}}!")


# ─── Raycast & Teleport ────────────────────────────────────────────
# raycast() fires an invisible ray through the world and returns the
# first block it hits (or None if it reaches max distance).
#
# Arguments: world name, origin (x,y,z), direction (yaw, pitch),
#            max distance, step size, ignore fluids.
#
# This example casts straight down from y=320 at (0.5, 0.5) to find
# the highest block, then teleports the player on top of it.

@command("Teleport to spawn")
async def spawn(event: Event):
    """Teleport the player to spawn using raycast."""
    # Cast a ray straight down (pitch 90°) from build limit.
    ray = await raycast('world', (0.5, 320, 0.5), (0, 90), 384, 0.5, False)

    if ray is not None:
        # ray.y is the top of the block we hit; add 0.5 so the player
        # stands on top rather than inside the block.
        event.player.teleport(Location(0.5, ray.y + 0.5, 0.5, world='world'))


# ─── Inventory GUI ──────────────────────────────────────────────────
# Inventories can be used as custom GUIs.  Create an Inventory with a
# size (must be a multiple of 9) and a title, then open it for a
# player.  Items placed inside act as clickable buttons.

@command("open test gui")
async def gui(event: Event):
    """Open a test inventory GUI."""
    # Create a filler pane with a blank name so it looks decorative.
    pane = Item('light_gray_stained_glass_pane')
    await pane.set_name(" ")

    # Fill all 27 slots (3 rows × 9 columns) with the filler pane.
    inv = Inventory(3 * 9, title="Test GUI", contents=[pane for _ in range(3 * 9)])

    # Place a special item in the center slot (row 1, column 4 → slot 13).
    totem = Item('totem_of_undying')
    await totem.set_name("Bing chilling")
    inv.set_item(9 + 4, totem)

    # Open the inventory for the player who ran the command.
    inv.open(event.player)


# ─── Handling Inventory Clicks ──────────────────────────────────────
# To make a GUI interactive, listen for the inventory_click event.
# Check event.inventory.title to make sure the click happened in YOUR
# GUI (other plugins may have inventories open too).
#
# Always call event.cancel() to prevent the player from taking items
# out of the GUI.

@event
async def inventory_click(event: Event):
    """Handle clicks in the test GUI."""
    # Ignore clicks in inventories that aren't ours.
    if event.inventory.title != 'Test GUI':
        return

    # Cancel the click so items can't be moved out of the GUI.
    event.cancel()

    # Slot 13 (9 + 4) is where we placed the totem.
    if event.slot == 9 + 4:
        # Play the totem revival sound and send a chat message.
        event.player.play_sound(Sound.from_name('ITEM_TOTEM_USE'))
        event.player.send_message('Bing chilling')
        # Close the GUI after the player clicks the button.
        event.inventory.close()


# ─── Preventing Drags ──────────────────────────────────────────────
# Players can also drag items across slots.  Cancel that too so the
# GUI stays intact.

@event
async def inventory_drag(event: Event):
    """Cancel drags in the test GUI."""
    if event.inventory.title == 'Test GUI':
        event.cancel()


# ─── Viewing Another Player's Inventory ────────────────────────────
# Player(name=...) looks up an online player by name.  Accessing
# .inventory returns their real inventory, and .open() shows it to
# another player.

@command("Open someones inv")
async def invsee(event: Event, p: str):
    """Open another player's inventory."""
    # Don't let players open their own inventory through this command –
    # it would cause a confusing double-open.
    if p == event.player.name:
        event.player.send_message("You cannot open your own inventory!")
        event.player.play_sound(Sound.from_name('BLOCK_NOTE_BLOCK_BASS'))
        return

    # Look up the target by name and open their inventory for the caller.
    Player(name=p).inventory.open(event.player)