From 59d995019d1326cb48e3dabdaf6aee3d58fc9a5f Mon Sep 17 00:00:00 2001 From: Dominika Date: Mon, 14 Feb 2022 01:54:31 +0100 Subject: [PATCH] + digmeout minigame, usability fixes --- demos/digmeout.sh | 180 ++++++++++++++++++++++++++++++++++++++++++++++ demos/map.sh | 31 -------- mc.sh | 62 +++++----------- src/chunk.sh | 2 + src/hooks.sh | 22 ++++++ src/packet.sh | 98 +++++++++++++++++++++++-- 6 files changed, 314 insertions(+), 81 deletions(-) create mode 100644 demos/digmeout.sh delete mode 100644 demos/map.sh diff --git a/demos/digmeout.sh b/demos/digmeout.sh new file mode 100644 index 0000000..ac6d0ed --- /dev/null +++ b/demos/digmeout.sh @@ -0,0 +1,180 @@ +#!/usr/bin/env bash +# digmeout - a score-based minigame +# mine out as much ores before the time runs out + +time_left=30 +score=0 +spawn_pos=(8 -40 8) + +# palette expanded for easier randomization +palette+=("45") # gold +palette+=("47") # iron +palette+=("49") # coal +palette+=("8702") # lapis +palette+=("d21a") # diamond +palette+=("cf2a") # emerald +palette+=("9a8b01") # copper +palette+=("f11e") # redstone +for i in {1..15}; do + palette+=("01") # stone + palette+=("02") # granite + palette+=("04") # diorite +done + +function hook_chunks() { + local ore + local plane + + log "hooking chunks" + rm -R $TEMP/world/* + chunk_header + + oremap=() + for (( y=0; y<16; y++ )); do + plane='' + for (( x=0; x<16; x++ )); do + for (( z=0; z<16; z++ )); do + ore=$(printf '%02x' $(((RANDOM%52)+31))) + chunk+="$ore" + plane+="$ore" + done + done + oremap+=("$plane") + done + + chunk_footer + + echo "${oremap[@]}" > $TEMP/players/$nick/oremap + echo "$chunk" > $TEMP/world/0000000000000000 + + pkt_chunk FFFFFFFF FFFFFFFF 00 + pkt_chunk FFFFFFFF 00000000 00 + pkt_chunk FFFFFFFF 00000001 00 + + pkt_chunk 00000000 FFFFFFFF 00 + pkt_chunk 00000000 00000000 + pkt_chunk 00000000 00000001 00 + + pkt_chunk 00000001 FFFFFFFF 00 + pkt_chunk 00000001 00000000 00 + pkt_chunk 00000001 00000001 00 + + echo '0' > $TEMP/players/$nick/score + pkt_title "Get, set..." +} + +function hook_start() { + timer & + pkt_title "Go!" +} + +function timer() { + while true; do + if [[ $time_left -le 0 ]]; then + pos=$(cat $TEMP/players/$nick/position) + pkt_effect $(awk -F, '{print $1}' <<< "$pos") $(awk -F, '{print $2}' <<< "$pos") $(awk -F, '{print $3}' <<< "$pos") 1032 + sleep 1 + break + elif [[ $time_left -le 5 ]]; then + sleep 1 + pos=$(cat $TEMP/players/$nick/position) + pkt_effect $(awk -F, '{print $1}' <<< "$pos") $(awk -F, '{print $2}' <<< "$pos") $(awk -F, '{print $3}' <<< "$pos") 1000 + time_left=$((time_left-1)) + elif [[ $time_left -le 10 ]]; then + sleep 5 + time_left=$((time_left-5)) + else + sleep 10 + time_left=$((time_left-10)) + fi + pkt_chatmessage "§aTime left: §r${time_left}s" "00000000000000000000000000000000" + done + pkt_disconnect "Time's up! Your final score: §a$(cat $TEMP/players/$nick/score)" +} + +function score() { + # egh, async in Bash sucks + score=$(cat $TEMP/players/$nick/score) + if [[ "$block" == 45 ]]; then + score=$((score+7)) + elif [[ "$block" == 47 ]]; then + score=$((score+5)) + elif [[ "$block" == 49 ]]; then + score=$((score+1)) + elif [[ "$block" == 8702 ]]; then + score=$((score+6)) + elif [[ "$block" == d21a ]]; then + score=$((score+25)) + elif [[ "$block" == cf2a ]]; then + score=$((score+15)) + elif [[ "$block" == 9a8b01 ]]; then + score=$((score+3)) + elif [[ "$block" == f11e ]]; then + score=$((score+15)) + fi + pkt_experience "$score" + echo "$score" > $TEMP/players/$nick/score +} + +function dig_async() { + if [[ $block == 01 ]]; then + d=0.03 + else + d=0.07 + fi + for i in {1..9}; do + pkt_blockbreak $x $y $z 0$i + sleep $d + if [[ $(cat $TEMP/players/$nick/mining) != "$x,$y,$z" ]]; then + pkt_blockbreak $x $y $z ff + break + fi + done + + if [[ $(cat $TEMP/players/$nick/mining) == "$x,$y,$z" ]]; then + pkt_diggingack $x $y $z 00 02 + score + fi +} + +function hook_dig() { + log $x + log $y + log $z + log $a + ore=$(cat $TEMP/players/$nick/oremap | awk '{print $'"$((y+64))"'}' | sed -E 's/.{32}/&\n/g' | head -n $((z+1)) | tail -n 1 | sed -E 's/(.{16})(.{16})/\2\1/g;s/.{2}/&\n/g' | tail -n $((x+2)) | head -n1) + block=${palette[$((0x$ore))]} + if [[ $a == "1a02"* ]]; then # finished digging? + score + elif [[ $a == "1a00"* ]]; then + echo "$x,$y,$z" > $TEMP/players/$nick/mining + dig_async & + elif [[ $a == "1a01"* ]]; then + echo "catgirls" > $TEMP/players/$nick/mining + fi +} + +function hook_move() { + if [[ ${pos[1]} -lt -96 ]]; then + # utf-8 is actually only valid for strings created in the UTF world region + # everything else is just sparkling unicode + pkt_disconnect "$(xxd -p -r <<< "c2af5c5c5f28e38384295f2fc2af")" + fi +} + +function hook_keepalive() { + keep_alive & +} + +function hook_ping() { + json='{"version":{"name":"1.18.1","protocol":757},"players":{"max":1,"online":0,"sample":[]},"description":{"text":"Minigame: §adigmeout§r | '"$time_left"' seconds per game"},"favicon":"data:image/png;base64,'"$(base64 -w0 icon.png)"'"}' + res="$(str_len "$json")$(echo -n "$json" | xxd -p)" + echo "$(hexpacket_len "$res")00$res" | xxd -p -r +} + +function hook_inventory() { + items=($(repeat 36 "0 ")) + items+=("721") + log ${items[1]} + pkt_inventory items +} diff --git a/demos/map.sh b/demos/map.sh deleted file mode 100644 index a5b6b72..0000000 --- a/demos/map.sh +++ /dev/null @@ -1,31 +0,0 @@ -#!/usr/bin/env bash - -function hook_chunks() { - log "hooking chunks" - echo "ffff081f00 01 09 0a 0e 0f 15 21 00 31 00 41 42 44 45 47 49 4d 9401 8402 8602 8702 8902 00 9602 00 00 00 00 00 f60a8004" > $TEMP/world/0000000000000000 - - map="" - for (( i=0; i<4097; i++ )); do - map+="$(printf '%02x' $((RANDOM%15)))" - done - - echo "$map" >> $TEMP/world/0000000000000000 - - echo "000100000000000000010000000000000001000000000000000100000000000000010000000000000001000000000000000100000000000000010000000000000001000000000000000100000000000000010000000000000001000000000000000100000000000000010000000000000001000000000000000100000000000000010000000000000001000000000000000100000000000000010000000000000001000000000000000100000000000000010000000000000001000000000000000100000000000000010000000000000001" >> $TEMP/world/0000000000000000 - - pkt_chunk FFFFFFFF FFFFFFFF - pkt_chunk FFFFFFFF 00000000 - pkt_chunk FFFFFFFF 00000001 - - pkt_chunk 00000000 FFFFFFFF - pkt_chunk 00000000 00000000 - pkt_chunk 00000000 00000001 - - pkt_chunk 00000001 FFFFFFFF - pkt_chunk 00000001 00000000 - pkt_chunk 00000001 00000001 -} - -function hook_block() { - -} diff --git a/mc.sh b/mc.sh index 208a5b4..e75b282 100755 --- a/mc.sh +++ b/mc.sh @@ -4,6 +4,7 @@ dyed=0 keepalive=0 pos=(0 0 0) players=() +time=1609000 TEMP=/dev/shm/witchcraft/ mkdir -p $TEMP $TEMP/players $TEMP/world @@ -22,10 +23,16 @@ fi function keep_alive() { while true; do - sleep 5 + sleep 1 log "sending keepalive" echo '092100000000000000ff' | xxd -p -r # random data + + res=$(printf '%016x' $time) + res=$res$res + log time: $res + time=$((time+240)) + echo -n "$(hexpacket_len "$res")59$res" | xxd -p -r done } @@ -106,30 +113,13 @@ while true; do exit fi - if [[ -f /tmp/block ]]; then - res="$(encode_position 10 -10 10)" - res+="01" - - log "$res" - echo -n "$(hexpacket_len "$res")0c$res" | xxd -p -r - rm -R /tmp/block - fi - - if [[ -f /tmp/spawn ]]; then - spawn_players - rm /tmp/spawn - fi - if [[ $a == "00"* ]]; then log "responding to 00; state: $state" if [[ "$state" == '01' ]]; then log "status response" - #json='{"version":{"name":"1.18.1","protocol":757},"players":{"max":100,"online":5,"sample":[{"name":"uwu","id":"4566e69f-c907-48ee-8d71-d7ba5aa00d20"}]},"description":{"text":"Hello world"}}' - json='{"version":{"name":"§a§kaaa§aUwU§kaaa","protocol":756},"players":{"max":1,"online":0,"sample":[]},"description":{"text":"§aUwU"},"favicon":"data:image/png;base64,'"$(base64 -w0 icon.png)"'"}' - res="$(str_len "$json")$(echo -n "$json" | xxd -p)" - echo "$(hexpacket_len "$res")00$res" | xxd -p -r + hook_ping state='' elif [[ "$state" == '02' ]]; then @@ -137,7 +127,6 @@ while true; do eid=$(printf "%04x" $RANDOM | cut -c 1-4) mkdir -p $TEMP/players/$nick echo -n $eid > $TEMP/players/$nick/eid - pkt_chatmessage "+ $nick" "00000000000000000000000000000000" > $TEMP/players/$nick/broadcast log "login response" if [[ $keepalive == 0 ]]; then hook_keepalive @@ -149,14 +138,7 @@ while true; do log "$(hexpacket_len "$res")02$res" echo -n "$(hexpacket_len "$res")02$res" | xxd -p -r - - res="$(encode_position 0 0 0)" - res+="00000000" # angle as float - - echo -n "$(hexpacket_len "$res")4B$res" | xxd -p -r - log "sent spawn position" - - #res="00000000" # entity id (0 or -2147483648, idk) + #res="$eid" # entity id #res+="00" # not hardcore #res+="00" # survival mode #res+="01" # ... as previously seen on Creative Mode (ignored) @@ -179,25 +161,19 @@ while true; do #log "sent join game" cat nbt_ - log "sent (hardcoded) join game" - - # send inventory (0x14) - res="00" # inventory id - res+="00" # state - res+="09" # item count - for i in {1..9}; do - res+="01 0$i 7f 00" # stone block - done - res+="01 00 01 00" # again, for held + log "sent (hardcoded) join game" + #res="$(encode_position 10 10 10)" + #res+="00000000" # angle as float - echo -n "$(hexpacket_len "$res")14$res" | xxd -p -r - log "sent inventory" + #echo -n "$(hexpacket_len "$res")4B$res" | xxd -p -r + #log "sent spawn position" + hook_inventory hook_chunks - pkt_pos + pkt_pos ${spawn_pos[0]} ${spawn_pos[1]} ${spawn_pos[2]} spawn_players - + (sleep 1; hook_start) & state='' else if [[ $a == *"01" ]]; then # 01 - next state: status @@ -230,7 +206,7 @@ while true; do elif [[ $a == "13"* ]]; then log "received Player Rotation" elif [[ $a == "1a"* ]]; then - #cut -c 3- + decode_position $(cut -c 5-20 <<< "$a") hook_dig elif [[ $a == "2c"* ]]; then hook_swing diff --git a/src/chunk.sh b/src/chunk.sh index 59d9a74..6e3cb9e 100644 --- a/src/chunk.sh +++ b/src/chunk.sh @@ -7,6 +7,8 @@ function chunk_header() { chunk+="$(int2varint ${#palette[@]})" # palette - entries amount chunk+="${palette[@]}" + + chunk+="8004" # len of next array } function chunk_footer() { diff --git a/src/hooks.sh b/src/hooks.sh index 5e3b72a..356370f 100644 --- a/src/hooks.sh +++ b/src/hooks.sh @@ -1,6 +1,8 @@ #!/usr/bin/env bash # hooks.sh - dummy hooks functions +spawn_pos=(0 0 0) # default spawn position + # on player dig function hook_dig() { log "received Player Dig" @@ -51,10 +53,30 @@ function hook_chunks() { pkt_chunk 00000001 00000002 } +# after spawning the player +function hook_start() { + : +} + # during login; useful for disabling all multiplayer functionality function hook_keepalive() { keep_alive & # sleep probably only needed for testing sleep 1 && position_delta & sleep 1 && handle_broadcast & + pkt_chatmessage "+ $nick" "00000000000000000000000000000000" > $TEMP/players/$nick/broadcast +} + +# during server ping; allows you to respond with a custom message. +function hook_ping() { + #json='{"version":{"name":"1.18.1","protocol":757},"players":{"max":100,"online":5,"sample":[{"name":"uwu","id":"4566e69f-c907-48ee-8d71-d7ba5aa00d20"}]},"description":{"text":"Hello world"}}' + json='{"version":{"name":"§a§kaaa§aUwU§kaaa","protocol":756},"players":{"max":1,"online":0,"sample":[]},"description":{"text":"§aUwU"},"favicon":"data:image/png;base64,'"$(base64 -w0 icon.png)"'"}' + res="$(str_len "$json")$(echo -n "$json" | xxd -p)" + echo "$(hexpacket_len "$res")00$res" | xxd -p -r +} + +# on defining inventory contents +function hook_inventory() { + items=() + pkt_inventory items } diff --git a/src/packet.sh b/src/packet.sh index e037074..2c8b750 100644 --- a/src/packet.sh +++ b/src/packet.sh @@ -1,10 +1,11 @@ #!/usr/bin/env bash # packet.sh - play state packets +# pkt_pos(x, y, z) function pkt_pos() { - res="0000001000000000" # X - res+="0000000000000000" # Y - res+="0000000000000000" # Z + res="$(to_ieee754 $1)" # X + res+="$(to_ieee754 $2)" # Y + res+="$(to_ieee754 $3)" # Z res+="00000000" # yaw res+="00000000" # pitch res+="00" # bit field; all absolute @@ -40,7 +41,6 @@ function pkt_chunk() { else chunk_header - chunk+="8004" # len of next array l=$(echo -n "8002" | xxd -p -r | varint2int) chunk+="$(repeat $((l*16)) "$fill")" @@ -73,9 +73,9 @@ function pkt_effect() { function pkt_particle() { res="$(printf '%08x' $4)" # particle id res+="01" # long distance - res+="0000000000000000" # X - res+="0000000000000000" # Y - res+="0000000000000000" # Z + res+="$(to_ieee754 $1)" # X + res+="$(to_ieee754 $2)" # Y + res+="$(to_ieee754 $3)" # Z res+="3f800000" # X offset res+="3f800000" # Y offset res+="3f800000" # Z offset @@ -84,6 +84,7 @@ function pkt_particle() { res+="" # data (left blank) echo -n "$(hexpacket_len "$res")24$res" | xxd -p -r + log $(to_ieee754 $1) $(to_ieee754 $2) $(to_ieee754 $3) rhexlog "$res" log "sending particle" @@ -209,3 +210,86 @@ function pkt_title() { res="$(str_len "$txt")$(echo -n "$txt" | xxd -p)" echo -n "$(hexpacket_len "$res")5a$res" | xxd -p -r } + +# pkt_disconnect(reason) +function pkt_disconnect() { + txt='{"text":"'"$1"'"}' + res="$(str_len "$txt")$(echo -n "$txt" | xxd -p)" + log "$txt" + echo -n "$(hexpacket_len "$res")1a$res" | xxd -p -r + + pkill -P $$ + pkt_chatmessage "- $nick" "00000000000000000000000000000000" > $TEMP/players/$nick/broadcast + sleep 0.3 + rm -R "$TEMP/players/$nick" + exit +} + +# pkt_experience(lvl) +function pkt_experience() { + res="00000000" # experience bar + res+="$(int2varint $1)" + res+="00" + echo -n "$(hexpacket_len "$res")51$res" | xxd -p -r +} + +# pkt_inventory(items) +function pkt_inventory() { + local -n _items=$1 + + res="00" # inventory id + res+="00" # state + res+="$(int2varint ${#_items[@]})" # item count + for i in ${!_items[@]}; do + if [[ $i == 0 ]]; then + res+="00" + else + res+="01 $(int2varint ${_items[$i]}) 01 00" + fi + done + res+="01 00 01 00" + + echo -n "$(hexpacket_len "$res")14$res" | xxd -p -r + log "sent inventory" +} + +# pkt_digginack(x, y, z, block, status) +function pkt_diggingack() { + res="$(encode_position $1 $2 $3)" + res+="$4" + res+="$5" + res+="01" + echo -n "$(hexpacket_len "$res")08$res" | xxd -p -r + log "sent dig ack" +} + +# pkt_blockbreak(x, y, z, stage) +function pkt_blockbreak() { + res="$(int2varint $((0x$eid)))" + res+="$(encode_position $1 $2 $3)" + res+="$4" + echo -n "$(hexpacket_len "$res")09$res" | xxd -p -r +} + +# pkt_soundeffect(x, y, z, id) +# TODO: unbreak this +function pkt_soundeffect() { + res="$(int2varint $4)" # sound ID + res+="05" # "block" category + res+="$(printf '%08x' $(($1*8)))" # x + res+="$(printf '%08x' $(($2*8)))" # y + res+="$(printf '%08x' $(($3*8)))" # z + res+="3f800000" # volume + res+="3f800000" # pitch + + echo -n "$(hexpacket_len "$res")5d$res" | xxd -p -r + log "sound $(hexpacket_len "$res")5d$res" +} + +# pkt_sendblock(x, y, z, id) +function pkt_sendblock() { + res="$(encode_position $1 $2 $3)" + res+="$(int2varint $4)" + + echo -n "$(hexpacket_len "$res")0c$res" | xxd -p -r +}