Complete script examples

Examples

Full working scripts that demonstrate PyJavaBridge features.


Commands & GUI

A script demonstrating commands, raycast teleportation, inventory GUIs, and inventory click events.

from bridge import *

# Simple hello world command
@command("Hello world command")
async def helloworld(event: Event):
    event.player.send_message("Hello, World!")

# Command with arguments
@command("Greet command")
async def greet(event: Event, name: str = None):
    event.player.send_message(f"Hello, {name or event.player.name}!")

# Raycast to find ground level, then teleport
@command("Teleport to spawn")
async def spawn(event: Event):
    ray = await raycast('world', (0.5, 320, 0.5), (0, 90), 384, 0.5, False)
    event.player.teleport((0.5, ray.y + 0.5, 0.5))

# Custom inventory GUI
@command("open test gui")
async def gui(event: Event):
    inv = Inventory(
        3 * 9,
        title="Test GUI",
        contents=[
            Item('light_gray_stained_glass_pane').set_name(" ")
            for _ in range(3 * 9)
        ]
    )
    inv.set_item(9 + 4, Item('totem_of_undying').set_name("Bing chilling"))
    inv.open(event.player)

# Handle clicks in the GUI
@event
async def inventory_click(event: Event):
    if event.inventory.title != 'Test GUI':
        return

    event.cancel()

    if event.slot == 9 + 4:
        event.player.play_sound('item_totem_use')
        event.player.send_message('Bing chilling')
        event.inventory.close()

# Prevent dragging in the GUI
@event
async def inventory_drag(event: Event):
    if event.inventory.title == 'Test GUI':
        event.cancel()

# View another player's inventory
@command("Open someones inv")
async def invsee(event: Event, p: str):
    if p == event.player.name:
        event.player.send_message("You cannot open your own inventory!")
        event.player.play_sound('block_note_block_bass')
        return

    Player(p).inventory.open(event.player)

Concepts demonstrated:


Spawn Protection

A script that prevents building near spawn using block events.

from bridge import *

@event
async def load(event: Event):
    server.broadcast("server start")

@event
async def block_break(event: Event):
    if (abs(event.block.x) + abs(event.block.z)) < 15:
        event.player.send_message("You cannot break blocks there!")
        await event.cancel()

@event
async def block_place(event: Event):
    if (abs(event.block.x) + abs(event.block.z)) < 15:
        event.player.send_message("You cannot place blocks there!")
        await event.cancel()

@event
async def block_explode(event: Event):
    if (abs(event.block.x) + abs(event.block.z)) < 15:
        event.cancel()

Concepts demonstrated:


Temporary Ban System

A full ban system with configurable durations and persistence.

"""Ban users for customizable amounts of time."""
from humanfriendly import format_timespan
from time import time as time_now
from bridge import *
import re

bans = {}

def parse_time(time: str) -> int:
    """Parse a time string like '1h30m' into seconds."""
    if not time:
        raise ValueError('Time string is required')

    units = {
        's': 1, 'm': 60, 'h': 3600,
        'd': 86400, 'w': 604800,
        'mo': 2592000, 'y': 31536000,
    }

    pattern = re.compile(r'(\d+)(mo|[smhdwy])', re.IGNORECASE)
    total = 0
    time_str = time.strip().lower()
    idx = 0

    for match in pattern.finditer(time_str):
        if match.start() != idx:
            raise ValueError(f"Invalid time format: {time}")
        value = int(match.group(1))
        unit = match.group(2)
        if unit not in units:
            raise ValueError(f"Unknown time unit: {unit}")
        total += value * units[unit]
        idx = match.end()

    if idx != len(time_str):
        raise ValueError(f"Invalid time format: {time}")
    return total

@command('Ban someone for a specified time or permanently')
async def ban(event: Event, user: str, t: str = None, r: str = None):
    global bans

    if not event.player.is_op():
        event.player.send_message('No permission!')
        event.player.play_sound('block_note_block_bass')
        return

    target = Player(user)
    duration = parse_time(t) if t else None
    reason = r or "Ban hammer has spoken!"

    reason_text = (
        f"You have been {'permanently ' if not duration else ''}banned"
        f"{f' for {format_timespan(duration)}' if duration else ''}"
        f"\nReason: {reason}"
    )

    await target.kick(reason_text)
    bans[target.uuid] = (time_now() + duration if duration else None, reason)

    event.player.send_message(
        f"{user} has been {'permanently ' if not duration else ''}banned"
        f"{f' for {format_timespan(duration)}' if duration else ''}"
    )

@command('Unban someone')
async def unban(event: Event, user: str):
    global bans

    if not event.player.is_op():
        event.player.send_message('No permission!')
        event.player.play_sound('block_note_block_bass')
        return

    target = Player(user)
    if target.uuid not in bans:
        event.player.send_message("That user is not banned")
        event.player.play_sound('block_note_block_bass')
        return

    bans.pop(target.uuid)
    event.player.send_message(f"{user} has been unbanned.")

@event
async def player_join(event: Event):
    global bans
    uuid = event.player.uuid

    if uuid not in bans:
        return

    time, reason = bans[uuid]
    if time:
        time -= time_now()

    if time and time <= 0:
        bans.pop(uuid)
        return

    event.player.kick(
        f"You have been {'permanently ' if not time else ''}banned"
        f"{f' for {format_timespan(time)}' if time else ''}"
        f"\nReason: {reason}"
    )

Concepts demonstrated:


Tips for Writing Scripts

  1. Always import from bridge: from bridge import *
  2. Use async/await: Most bridge methods return awaitables
  3. Name your event handlers after the event: async def block_break(event) handles block_break events
  4. Commands auto-register: The function name becomes the command name
  5. Cancel events to prevent default behavior: await event.cancel()
  6. Use the Config class for persistence instead of global dictionaries