Skip to content

Examples

3add edited this page May 18, 2026 · 24 revisions

This page lists examples of things you can do with PacketEventsSK. This page is made for you to understand the syntax and ideas of package management, do not just copy paste the listed content.

Confetti

Based on PacketEventsSK v1.1.2 with SkBee v3.22.1 on Paper v26.1.2 Created by Crebs huge shoutout!

Confetti using text displays

function confetti(p: player):
    loop 1000 times:
        confettiPiece({_p})
        wait 1 tick

local function emptyText(loc: location):: fake entity:

    set {_meta} to text display meta data:
        display content: " "
        display text shadowed state: true
        display billboard: center
        display scale: vector(1.2,1.2,1.2)
        display translation: vector(-0.03,0.65,0)
        display background color: rgb(random integer between 0 and 255, random integer between 0 and 255, random integer between 0 and 255)
        display view range: 1
        display transform interpolation duration: 2 ticks
        display interpolation delay: 0 ticks

    return a new fake text display entity:
        viewers: all players
        location: {_loc} ~ vector(0,0.5,0)
        meta: {_meta}

local function confettiPiece(p: player):
    set {_loc} to {_p}'s location ~ (vector in direction of {_p})*vector(2.5, 2.25, 2.5)
    set {_e} to emptyText({_loc})

    set {_v} to vector(random number between -0.25 and 0.25, random number between 0.15 and 0.35, random number between -0.25 and 0.25)

    set {_axis} to vector(random number between -1 and 1, random number between -1 and 1, random number between -1 and 1)
    set {_angle} to random number between 0 and 360
    set {_spin} to random number between 10 and 25

    run 1 tick later repeating every 2 ticks:
        add 1 to {_c}
        if {_c} >= 70:
            kill fake entity {_e}
            stop

        set {_v} to ({_v} * vector(0.93, 1, 0.93)) - vector(0, 0.05, 0) + vector(random number between -0.01 and 0.01, 0, random number between -0.01 and 0.01)

        set {_t} to vector(-0.03, 0.65, 0) + {_v}
        set fake display translation of {_e} to {_t}
        add {_spin} to {_angle}
        set fake display left rotation of {_e} to axisAngle({_angle}, {_axis})

Sign Exploit

Based on PacketEventsSK v1.1.0 with SkBee v3.22.1 on Paper v26.1.2

This example makes use of a sign exploit described in MC-265322 and on wurst wiki. It can be used to see which mods are or aren't installed on a connected player. (and is thus very powerful for anti cheating purposes)

Basically the way it works:

  1. server sends a fake sign with a line having a translatable component or keybind component (clientbound block change packet and clientbound block entity data packet)
  2. server forces the client to open the editor of the sign (clientbound open sign editor packet)
  3. server forces the client to close the editor (clientbound close window packet)
  4. client closes the editor and as a response sends the "new content" (which is unchanged) to the server (serverbound update sign packet received by server on line 57)
  5. server reads the new content: if it parsed the translatable/keybind component it has the keybind set and thus has the mod otherwise not
on load:
    delete {-blackListedKeybinds::*}
    add "key.freecam.toggle" to {-blackListedKeybinds::*}
    add "key.freecam.playerControl" to {-blackListedKeybinds::*}
    add "key.freecam.tripodReset" to {-blackListedKeybinds::*}
    add "key.freecam.configGui" to {-blackListedKeybinds::*}

function createSignNBT(bind: string) :: nbt compound:
    set {_nbtString} to "{front_text:{messages:[{""keybind"":""%{_bind}%""},{""text"":""""},{""text"":""""},{""text"":""""}]}}"
    return nbt compound from {_nbtString}

function startKeybindChecks(p: player):
    set {_uuid} to uuid of {_p}
    
    delete {-keybindQueue::%{_uuid}%::*}
    
    loop {-blackListedKeybinds::*}:
        add loop-value to {-keybindQueue::%{_uuid}%::*}
        
    checkNextKeybind({_p})

function checkNextKeybind(p: player):
    set {_uuid} to uuid of {_p}
    
    set {_keybind} to first element of {-keybindQueue::%{_uuid}%::*}
    
    if {_keybind} is set:
        check({_p}, {_keybind})
    else:
        delete {-keybindQueue::%{_uuid}%::*}

function check(p: player, keybind: string):
    set {_pos} to vector of {_p}'s location

    set {_blockPacket} to a new clientbound block change packet:
        block position: {_pos}
        block state: oak sign[]

    set {_setTextPacket} to a new clientbound block entity data packet:
        block position: {_pos}
        block entity type: sign block entity type
        nbt compound: createSignNBT({_keybind})

    set {_signPacket} to clientbound open sign editor packet:
        block position: {_pos}
        sign side: front
    
    set {_closeWindowPacket} to a clientbound close window packet

    set {_revertBlockPacket} to new clientbound block change packet:
        block position: {_pos}
        block state: air[]
    
    # Sends the packets (order sensitive)
    send packet {_blockPacket}, {_setTextPacket}, {_signPacket}, {_closeWindowPacket} and {_revertBlockPacket} to {_p} 

on serverbound update sign packet async processed:
    set {_p} to event-player
    set {_uuid} to uuid of {_p}
    
    if {-keybindQueue::%{_uuid}%::*} is set:
        
        set {_firstIndex} to first element of indices of {-keybindQueue::%{_uuid}%::*}
        set {_expected} to {-keybindQueue::%{_uuid}%::%{_firstIndex}%}
        
        delete {-keybindQueue::%{_uuid}%::%{_firstIndex}%}

        set {_updateSignPacket} to event-packet
        set {_text} to {_updateSignPacket}.getTextLines()[0]
        
        if {_text} is not {_expected}:
            delete {-keybindQueue::%{_uuid}%::*}
            wait tick # back on main thread
            kick {_p} due to "Unsupported client modification detected!"
        else:
            send "%{_p}% passed keybind check for: %{_expected}%" to console
            wait 1 tick
            if {_p} is online:
                checkNextKeybind({_p})
                
on join:
    wait 3 seconds
    startKeybindChecks(player)

Scrollable GUI

Based on PacketEventsSK v1.1.1 with SkBee v3.22.1 on Paper v26.1.2

This example makes use of another unintended feature of the minecraft protocol that allows for a scrollable GUI (where the content scrolls with you) There are multiple ways of doing this but one of the ways is intercepting a serverbound select bundle item packet and based of it's data determining in which direction the client scrolled. Works like this:

  1. user scrolls, the mc vanilla client sends serverbound select bundle item
  2. the server receives the packet and reads it's content (on line 34-36)
  3. we evaluate if the user scrolled up or down based on the data (line 68-87)
  4. we update the GUI accordingly
on load:
    set {-baseBundle} to a bundle with item flags hide additional tooltip
    add stone, stone, and stone to bundle contents of {-baseBundle}

command blocks:
    trigger:
        openBlocks(player)

function openBlocks(p: player, scroll: integer = 0):
    set {_gui} to new chest inventory with 6 rows named "Blocks"

    set {-prevPlayerScroll::%{_p}'s uuid%} to {_scroll}
    set {_skipAmount} to {_scroll} * 9
    set {_slot} to 0

    loop blocks:
        if any:
            loop-iteration <= {_skipAmount}
            type of loop-value is air
        then:
            continue
            
        if {_slot} >= 54:
            stop loop

        set {_i} to {-baseBundle} named proper case "&f%loop-value%"
        add stone, dirt and cobblestone to bundle contents of {_i}
        set item model of {_i} to "%namespaced key of loop-value%"
        
        set slot {_slot} of {_gui} to {_i}
    
        add 1 to {_slot}

    open {_gui} to {_p}
    
on serverbound select bundle item sync processed:
    set {_index} to field selected item index of event-packet
    set {_slotId} to field slot id of event-packet

    set {_action} to getScrollAction({-prevScroll::%{_slotId}%}, {_index}, 3)

    if {_action} is "DONE":
        clear {-prevScroll::%{_slotId}%}
    else:
        set {-prevScroll::%{_slotId}%} to {_index}
        
    if {_action} is "UP":
        clear {-prevScroll::%{_slotId}%}
        openBlocks(player, {-prevPlayerScroll::%player's uuid%} + 1)
    else if all:
        {_action} is "DOWN"
        {-prevPlayerScroll::%player's uuid%} - 1 > 0
    then:
        clear {-prevScroll::%{_slotId}%}
        openBlocks(player, {-prevPlayerScroll::%player's uuid%} - 1)

local function getScrollAction(prevIndex: integer, newIndex: integer, maxIndex: integer) :: string:
    if {_newIndex} is -1:
        return "DONE"

    if {_prevIndex} is not set: # first scroll
        if {_newIndex} is {_maxIndex} - 1:
            return "UP"
        else if {_newIndex} is 0:
            return "DOWN"

    if all:
        {_prevIndex} is {_maxIndex} - 1
        {_newIndex} is 0
    then:
        return "DOWN"

    if all:
        {_prevIndex} is 0
        {_newIndex} is {_maxIndex} - 1
    then:
        return "UP"

    if {_prevIndex} > {_newIndex}:
        return "UP"
    else:
        return "DOWN"

Clone this wiki locally