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:
@commandwith and without argumentsraycast()for finding ground levelInventorywith custom contents@eventforinventory_clickandinventory_dragEvent.cancel()to prevent default behaviorPlayermessaging, sounds, and teleportation
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:
@eventforblock_break,block_place,block_explode, andloadEvent.cancel()to prevent block modificationEvent.blockto access block coordinatesserver.broadcast()for server-wide messages- Diamond (taxicab) distance check for a protection zone
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:
@commandwith multiple optional argumentsPlayercreation by name for offline lookupsPlayer.kick()with a formatted reasonPlayer.is_opfor permission checking@eventforplayer_jointo enforce bans on login- Python standard library usage (
re,time) - Third-party library usage (
humanfriendly) - State management with a global dictionary
Tips for Writing Scripts
- Always import from bridge:
from bridge import * - Use async/await: Most bridge methods return awaitables
- Name your event handlers after the event:
async def block_break(event)handlesblock_breakevents - Commands auto-register: The function name becomes the command name
- Cancel events to prevent default behavior:
await event.cancel() - Use the
Configclass for persistence instead of global dictionaries