+ work so far
This commit is contained in:
commit
e0b1d39d78
241
mc.sh
Executable file
241
mc.sh
Executable file
|
@ -0,0 +1,241 @@
|
||||||
|
#!/bin/bash
|
||||||
|
state=''
|
||||||
|
dyed=0
|
||||||
|
keepalive=0
|
||||||
|
pos=(0 0 0)
|
||||||
|
players=()
|
||||||
|
TEMP=/dev/shm/witchcraft/
|
||||||
|
mkdir -p $TEMP $TEMP/players $TEMP/world
|
||||||
|
|
||||||
|
source src/log.sh
|
||||||
|
source src/int.sh
|
||||||
|
source src/packet.sh
|
||||||
|
source src/hooks.sh
|
||||||
|
|
||||||
|
function keep_alive() {
|
||||||
|
while true; do
|
||||||
|
sleep 5
|
||||||
|
log "sending keepalive"
|
||||||
|
echo '092100000000000000ff' | xxd -p -r
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
function position_delta() {
|
||||||
|
local deltaX
|
||||||
|
local deltaY
|
||||||
|
local deltaZ
|
||||||
|
|
||||||
|
# pos[0]=0
|
||||||
|
# pos[1]=0
|
||||||
|
# pos[2]=0
|
||||||
|
pos_old[0]=0
|
||||||
|
pos_old[1]=0
|
||||||
|
pos_old[2]=0
|
||||||
|
|
||||||
|
while true; do
|
||||||
|
sleep 0.1
|
||||||
|
for i in $(ls "$TEMP/players/"); do
|
||||||
|
if [[ "$i" != "$nick" ]]; then
|
||||||
|
pos[0]=$(cat $TEMP/players/$i/position | awk -F, '{print($1)}') # idk, floating point broke
|
||||||
|
pos[1]=$(cat $TEMP/players/$i/position | awk -F, '{print($2)}')
|
||||||
|
pos[2]=$(cat $TEMP/players/$i/position | awk -F, '{print($3)}')
|
||||||
|
log "posX: ${pos[0]}"
|
||||||
|
deltaX=$((((${pos[0]}*32) - (${pos_old[0]}*32)) * 128))
|
||||||
|
deltaY=$(((${pos[1]}*32 - ${pos_old[1]}*32) * 128))
|
||||||
|
deltaZ=$(((${pos[2]}*32 - ${pos_old[2]}*32) * 128))
|
||||||
|
|
||||||
|
|
||||||
|
if [[ $deltaX != 0 || $deltaY != 0 || $deltaZ != 0 ]]; then
|
||||||
|
pkt_position $deltaX $deltaY $deltaZ $(cat $TEMP/players/$i/eid)
|
||||||
|
fi
|
||||||
|
pos_old=("${pos[@]}")
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
function handle_broadcast() {
|
||||||
|
while true; do
|
||||||
|
for i in $(ls "$TEMP/players/"); do
|
||||||
|
if [[ "$i" != "$nick" ]]; then
|
||||||
|
if [[ -f $TEMP/players/$i/broadcast ]]; then
|
||||||
|
packet="$(cat $TEMP/players/$i/broadcast)"
|
||||||
|
if [[ "$last" != "$packet" ]]; then
|
||||||
|
cat $TEMP/players/$i/broadcast
|
||||||
|
last="$packet"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
last=''
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
sleep 1
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
function spawn_players() {
|
||||||
|
for i in $(ls "$TEMP/players/"); do # fite me
|
||||||
|
if [[ $i != $nick && ${players[@]} != *"$i"* ]]; then
|
||||||
|
log "name: $i EID: $eid"
|
||||||
|
pkt_playerinfo_add $i $(cat $TEMP/players/$i/eid)
|
||||||
|
pkt_spawnplayer $(cat $TEMP/players/$i/eid)
|
||||||
|
players+=("$nick")
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
while true; do
|
||||||
|
len=$(varint2int)
|
||||||
|
|
||||||
|
a=$(dd count=$len bs=1 status=none | xxd -p)
|
||||||
|
if [[ "$a" == '' ]]; then
|
||||||
|
log "connection dyed"
|
||||||
|
pkill -P $$
|
||||||
|
pkt_chatmessage "- $nick" "00000000000000000000000000000000" > $TEMP/players/$nick/broadcast
|
||||||
|
sleep 1
|
||||||
|
rm -R "$TEMP/players/$nick"
|
||||||
|
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
|
||||||
|
|
||||||
|
state=''
|
||||||
|
elif [[ "$state" == '02' ]]; then
|
||||||
|
nick=$(cut -c 5- <<< "$a" | xxd -p -r | grep -Poh '[A-Za-z0-9_-]*')
|
||||||
|
eid=$(printf "%02x" $RANDOM)
|
||||||
|
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
|
||||||
|
keepalive=1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# random uuid string len string (nick)
|
||||||
|
res="0000000000000000000000000000$eid$(str_len "$nick")$(echo -n "$nick" | xxd -p)"
|
||||||
|
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+="00" # not hardcore
|
||||||
|
#res+="00" # survival mode
|
||||||
|
#res+="01" # ... as previously seen on Creative Mode (ignored)
|
||||||
|
#res+="01" # one dimension
|
||||||
|
#res+="13$(echo -n "minecraft:overworld" | xxd -p)"
|
||||||
|
#res+="0a000000" # dimension codec
|
||||||
|
#res+="0a000000" # dimension
|
||||||
|
#res+="13$(echo -n "minecraft:overworld" | xxd -p)" # dimension being spawned into
|
||||||
|
#res+="0000000000000000" # beginning of sha256 of seed
|
||||||
|
#res+="0f" # max players (ignored)
|
||||||
|
#res+="02" # view distance (min 2 chunks)
|
||||||
|
#res+="02" # simulation distance
|
||||||
|
#res+="00" # reduced debug info? (false)
|
||||||
|
#res+="00" # enable respawn screen
|
||||||
|
#res+="00" # is debug (surprisingly, no)
|
||||||
|
#res+="01" # is flat (yeah, sure)
|
||||||
|
|
||||||
|
#rhexlog "$(hexpacket_len "$res")26$res"
|
||||||
|
#echo -n "$(hexpacket_len "$res")26$res" | xxd -p -r
|
||||||
|
#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
|
||||||
|
|
||||||
|
echo -n "$(hexpacket_len "$res")14$res" | xxd -p -r
|
||||||
|
log "sent inventory"
|
||||||
|
|
||||||
|
pkt_pos
|
||||||
|
|
||||||
|
source src/palette.sh
|
||||||
|
|
||||||
|
hook_chunks
|
||||||
|
|
||||||
|
spawn_players
|
||||||
|
|
||||||
|
state=''
|
||||||
|
else
|
||||||
|
if [[ $a == *"01" ]]; then # 01 - next state: status
|
||||||
|
log 'set status'
|
||||||
|
state='01'
|
||||||
|
else # 02 - next state: login
|
||||||
|
log 'set login'
|
||||||
|
state='02'
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
elif [[ $a == "01"* ]]; then
|
||||||
|
log "responding to 01"
|
||||||
|
echo "$len$a" | xxd -p -r
|
||||||
|
log "bye"
|
||||||
|
exit
|
||||||
|
elif [[ $a == "0f"* ]]; then
|
||||||
|
log "received keepalive"
|
||||||
|
date "+%s" > $TEMP/players/$nick/ping
|
||||||
|
elif [[ $a == "11"* ]]; then
|
||||||
|
pos[0]=$(from_ieee754 $(cut -c 3-18 <<< "$a"))
|
||||||
|
pos[1]=$(from_ieee754 $(cut -c 19-34 <<< "$a"))
|
||||||
|
pos[2]=$(from_ieee754 $(cut -c 35-50 <<< "$a"))
|
||||||
|
echo "${pos[0]},${pos[1]},${pos[2]}" > $TEMP/players/$nick/position
|
||||||
|
hook_move
|
||||||
|
elif [[ $a == "12"* ]]; then
|
||||||
|
hook_move
|
||||||
|
elif [[ $a == "13"* ]]; then
|
||||||
|
hook_move
|
||||||
|
elif [[ $a == "1a"* ]]; then
|
||||||
|
hook_dig
|
||||||
|
elif [[ $a == "2c"* ]]; then
|
||||||
|
hook_swing
|
||||||
|
elif [[ $a == "2e"* ]]; then
|
||||||
|
hook_block
|
||||||
|
elif [[ $a == "03"* ]]; then
|
||||||
|
if [[ $((0x$(cut -c 3-4 <<< "$a"))) -lt 127 ]]; then # lazy varint check
|
||||||
|
msg=$(cut -c 5- <<< "$a" | xxd -p -r)
|
||||||
|
else
|
||||||
|
msg=$(cut -c 3- <<< "$a" | xxd -p -r)
|
||||||
|
fi
|
||||||
|
hook_chat
|
||||||
|
else
|
||||||
|
log "unknown data from client"
|
||||||
|
rhexlog "$a"
|
||||||
|
fi
|
||||||
|
|
||||||
|
done
|
60
src/hooks.sh
Normal file
60
src/hooks.sh
Normal file
|
@ -0,0 +1,60 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
# hooks.sh - dummy hooks functions
|
||||||
|
|
||||||
|
# on player dig
|
||||||
|
function hook_dig() {
|
||||||
|
log "received Player Dig"
|
||||||
|
rhexlog $a
|
||||||
|
}
|
||||||
|
|
||||||
|
# on player arm swing
|
||||||
|
function hook_swing() {
|
||||||
|
log "received Arm Swing"
|
||||||
|
}
|
||||||
|
|
||||||
|
# on player block placement
|
||||||
|
function hook_block() {
|
||||||
|
log "received Player Block Placement"
|
||||||
|
rhexlog $a
|
||||||
|
}
|
||||||
|
|
||||||
|
# on chat message
|
||||||
|
# msg available as $msg
|
||||||
|
function hook_chat() {
|
||||||
|
chatlog "$nick: $msg"
|
||||||
|
pkt_chatmessage "$nick: $msg" "0000000000000000000000000000$eid"
|
||||||
|
pkt_chatmessage "$nick: $msg" "0000000000000000000000000000$eid" > $TEMP/players/$nick/broadcast
|
||||||
|
}
|
||||||
|
|
||||||
|
# on move/move+rotation/rotation
|
||||||
|
# X/Y/Z available as ${pos[0]} through ${pos[2]}
|
||||||
|
function hook_move() {
|
||||||
|
log "received Player Position"
|
||||||
|
}
|
||||||
|
|
||||||
|
# after login, join; intended for loading chunks
|
||||||
|
function hook_chunks() {
|
||||||
|
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
|
||||||
|
|
||||||
|
pkt_chunk FFFFFFFF 00000002
|
||||||
|
pkt_chunk 00000000 00000002
|
||||||
|
pkt_chunk 00000001 00000002
|
||||||
|
}
|
||||||
|
|
||||||
|
# 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 &
|
||||||
|
}
|
144
src/int.sh
Normal file
144
src/int.sh
Normal file
|
@ -0,0 +1,144 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
# int.sh - int conversion functions, and other numeric functions
|
||||||
|
|
||||||
|
# int2varint(int)
|
||||||
|
function int2varint() {
|
||||||
|
# very proud of my implementation; haven't seen anyone use a modulo for this ;p
|
||||||
|
local a
|
||||||
|
local b
|
||||||
|
local c
|
||||||
|
local out
|
||||||
|
out=$(printf '%02x' "$1")
|
||||||
|
if [[ $1 -lt 128 ]]; then
|
||||||
|
:
|
||||||
|
elif [[ $1 -lt 16384 ]]; then
|
||||||
|
a=$(($1%128))
|
||||||
|
b=$(($1/128))
|
||||||
|
out=$(printf "%02x" $((a+128)))$(printf "%02x" $b)
|
||||||
|
elif [[ $1 -lt $((128*128*128)) ]]; then
|
||||||
|
a=$(($1%128))
|
||||||
|
c=$((($1/128)%128))
|
||||||
|
b=$(($1/16384))
|
||||||
|
out=$(printf "%02x" $((a+128)))$(printf "%02x" $((c+128)))$(printf "%02x" $b)
|
||||||
|
fi
|
||||||
|
echo -n "$out"
|
||||||
|
}
|
||||||
|
|
||||||
|
# varint2int() <<< varint
|
||||||
|
function varint2int() {
|
||||||
|
local x
|
||||||
|
local uwu
|
||||||
|
local out
|
||||||
|
out=""
|
||||||
|
x=1
|
||||||
|
while true; do
|
||||||
|
uwu=$(dd count=1 bs=1 status=none | xxd -p)
|
||||||
|
|
||||||
|
out=$((out+((0x$uwu&127)*x)))
|
||||||
|
x=$((x*128))
|
||||||
|
if [[ $((0x$uwu>>7)) == 0 ]]; then
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
echo -n "$out"
|
||||||
|
}
|
||||||
|
|
||||||
|
# parse_position(Position)
|
||||||
|
# https://wiki.vg/Protocol#Position
|
||||||
|
function parse_position() {
|
||||||
|
x=$((0x$1 >> 38))
|
||||||
|
y=$((0x$1 & 0xFFF))
|
||||||
|
z=$(((0x$1 >> 12) & 0x3FFFFFF))
|
||||||
|
|
||||||
|
[[ $x -gt 33554431 ]] && x=$((x-67108864))
|
||||||
|
[[ $y -gt 2047 ]] && y=$((y-4095))
|
||||||
|
[[ $z -gt 33554431 ]] && z=$((z-67108864))
|
||||||
|
}
|
||||||
|
|
||||||
|
# encode_position(x, y, z)
|
||||||
|
function encode_position() {
|
||||||
|
local x
|
||||||
|
local y
|
||||||
|
local z
|
||||||
|
|
||||||
|
x=$1
|
||||||
|
y=$2
|
||||||
|
z=$3
|
||||||
|
|
||||||
|
[[ $x -lt 33554433 ]] && x=$((x+67108864))
|
||||||
|
[[ $y -lt 2049 ]] && y=$((y+4095))
|
||||||
|
[[ $z -lt 33554433 ]] && z=$((z+67108864))
|
||||||
|
|
||||||
|
printf "%016x" $((((x & 0x3FFFFFF)<<38) | ((z & 0x3FFFFFF)<<12) | (y & 0xFFF)))
|
||||||
|
}
|
||||||
|
|
||||||
|
# packet_len(packet)
|
||||||
|
function packet_len() {
|
||||||
|
int2varint $((($(echo -n "$1" | wc -c)+1)))
|
||||||
|
}
|
||||||
|
|
||||||
|
# hexpacket_len(hexpacket)
|
||||||
|
function hexpacket_len() {
|
||||||
|
int2varint $((($(echo -n "$1" | xxd -p -r | wc -c)+1)))
|
||||||
|
}
|
||||||
|
|
||||||
|
# str_len(string)
|
||||||
|
function str_len() {
|
||||||
|
int2varint $(echo -n "$1" | wc -c)
|
||||||
|
}
|
||||||
|
|
||||||
|
# hexstrl_len(hexstring)
|
||||||
|
function hexstr_len() {
|
||||||
|
int2varint $(echo -n "$1" | xxd -p -r | wc -c)
|
||||||
|
}
|
||||||
|
|
||||||
|
# hex2bin(hexstring)
|
||||||
|
function hex2bin() {
|
||||||
|
# \o/
|
||||||
|
echo -n "$1" | sed -E 's/0/0000/g;s/1/0001/g;s/2/0010/g;s/3/0011/g;s/4/0100/g;s/5/0101/g;s/6/0110/g;s/7/0111/g;s/8/1000/g;s/9/1001/g;s/a/1010/g;s/b/1011/g;s/c/1100/g;s/d/1101/g;s/e/1110/g;s/f/1111/g'
|
||||||
|
}
|
||||||
|
|
||||||
|
# from_ieee754(hexstring)
|
||||||
|
function from_ieee754() {
|
||||||
|
local sign
|
||||||
|
local exponent
|
||||||
|
local asdf
|
||||||
|
local exponent_
|
||||||
|
local val
|
||||||
|
|
||||||
|
val=$(hex2bin "$1")
|
||||||
|
sign=$(cut -c 1 <<< $val)
|
||||||
|
exponent=$(cut -c 2-12 <<< $val)
|
||||||
|
|
||||||
|
asdf=$(cut -c 13- <<< $val | sed -E 's/./,&/g;s/,//' | tr -d '\n' | awk -F , \
|
||||||
|
'{
|
||||||
|
power_count=-1
|
||||||
|
x=0;
|
||||||
|
for(i=1; i<=NF; i++) {
|
||||||
|
x=(x + ($i * (2 ** power_count)))
|
||||||
|
power_count=power_count-1;
|
||||||
|
}
|
||||||
|
print(x+1)
|
||||||
|
}')
|
||||||
|
|
||||||
|
exponent_=$((2#$exponent))
|
||||||
|
|
||||||
|
if [[ $sign == 0 ]]; then
|
||||||
|
echo "$asdf $exponent_" | awk '{print (int($1 * (2 ** ($2 - 1023))))}'
|
||||||
|
else
|
||||||
|
echo "$asdf $exponent_" | awk '{print -(int($1 * (2 ** ($2 - 1023))))}'
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# to_short(number)
|
||||||
|
function to_short() {
|
||||||
|
if [[ $1 -lt 0 && $1 -gt -32769 ]]; then
|
||||||
|
printf "%04x" $(($1+65536))
|
||||||
|
elif [[ $1 -lt 32768 && $1 -gt -1 ]]; then
|
||||||
|
printf "%04x" $1
|
||||||
|
elif [[ $1 -lt -32768 ]]; then
|
||||||
|
printf "8000"
|
||||||
|
elif [[ $1 -gt 32767 ]]; then
|
||||||
|
printf "7FFF"
|
||||||
|
fi
|
||||||
|
}
|
21
src/log.sh
Normal file
21
src/log.sh
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
# log.sh - logging functions
|
||||||
|
|
||||||
|
function log() {
|
||||||
|
echo "[INFO] $@" >&2
|
||||||
|
}
|
||||||
|
function warn() {
|
||||||
|
echo "[WARN] $@" >&2
|
||||||
|
}
|
||||||
|
function err() {
|
||||||
|
echo "[FAIL] $@" >&2
|
||||||
|
}
|
||||||
|
function chatlog() {
|
||||||
|
echo "[CHAT] $@" >&2
|
||||||
|
}
|
||||||
|
function hexlog() {
|
||||||
|
echo -n "$@" | xxd >&2
|
||||||
|
}
|
||||||
|
function rhexlog() {
|
||||||
|
echo -n "$@" | xxd -p -r | xxd >&2
|
||||||
|
}
|
189
src/packet.sh
Normal file
189
src/packet.sh
Normal file
|
@ -0,0 +1,189 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
# packet.sh - play state packets
|
||||||
|
|
||||||
|
function pkt_pos() {
|
||||||
|
res="0000001000000000" # X
|
||||||
|
res+="0000000000000000" # Y
|
||||||
|
res+="0000000000000000" # Z
|
||||||
|
res+="00000000" # yaw
|
||||||
|
res+="00000000" # pitch
|
||||||
|
res+="00" # bit field; all absolute
|
||||||
|
res+="00" # teleport id (?)
|
||||||
|
res+="00" # dismount vehicle?
|
||||||
|
|
||||||
|
echo -n "$(hexpacket_len "$res")38$res" | xxd -p -r
|
||||||
|
log "sent player look and position"
|
||||||
|
}
|
||||||
|
|
||||||
|
function pkt_chunk() {
|
||||||
|
# palettes are really cool once you figure them out :3
|
||||||
|
# https://wiki.vg/Protocol#Chunk_Data_And_Update_Light
|
||||||
|
|
||||||
|
local chunk
|
||||||
|
|
||||||
|
res="$1" # chunk X
|
||||||
|
res+="$2" # chunk Z
|
||||||
|
|
||||||
|
# here goes the scary NBT field
|
||||||
|
# nbt tag MOTION_BLOCKING len light data for a superflat map
|
||||||
|
res+="0a00000c000f 4d4f54494f4e5f424c4f434b494e47 00000025 010080402010080401008040201008040100804020100804010080402010080401008040201008040100804020100804010080402010080401008040201008040100804020100804010080402010080401008040201008040100804020100804010080402010080401008040201008040100804020100804010080402010080401008040201008040100804020100804010080402010080401008040201008040100804020100804010080402010080401008040201008040100804020100804010080402010080401008040201008040100804020100804010080402010080401008040201008040100804020100804010080402010080401008040201008040100804020100804010080402010080401008040201008040100804020100804 0000000020100804 00"
|
||||||
|
|
||||||
|
chunk="ffff" # amount of blocks, doesnt matter
|
||||||
|
chunk+="08" # palette - bits per block
|
||||||
|
chunk+="$(int2varint ${#palette[@]})" # palette - entries amount
|
||||||
|
|
||||||
|
chunk+="${palette[@]}"
|
||||||
|
#res+="0f af0b 01 00"
|
||||||
|
|
||||||
|
chunk+="8002" # len of next array
|
||||||
|
l=$(echo -n "8002" | xxd -p -r | varint2int)
|
||||||
|
|
||||||
|
for (( i=0; i<$((l*8)); i++ )); do
|
||||||
|
chunk+="01" # third entry of palette
|
||||||
|
done
|
||||||
|
|
||||||
|
chunk+="0001" # biome palette
|
||||||
|
for i in {1..26}; do
|
||||||
|
chunk+="0000000000000001" # set biome
|
||||||
|
done
|
||||||
|
|
||||||
|
res+="$(int2varint $(hexstr_len "$chunk"))" # Data len
|
||||||
|
res+="$chunk" # Chunk data itself
|
||||||
|
|
||||||
|
res+="00 01 00 00 00 00 00 00" # empty bitsets and light arrays
|
||||||
|
|
||||||
|
echo -n "$(hexpacket_len "$res")22$res" | xxd -p -r
|
||||||
|
|
||||||
|
log "sending chunk data"
|
||||||
|
}
|
||||||
|
|
||||||
|
# pkt_effect(x, y, z, effect_id)
|
||||||
|
function pkt_effect() {
|
||||||
|
res="$(printf '%08x' $4)"
|
||||||
|
res+="$(encode_position $1 $2 $3)"
|
||||||
|
res+="00000000"
|
||||||
|
res+="00"
|
||||||
|
echo -n "$(hexpacket_len "$res")23$res" | xxd -p -r
|
||||||
|
log "sending effect"
|
||||||
|
}
|
||||||
|
|
||||||
|
# pkt_playerinfo(name, eid)
|
||||||
|
function pkt_playerinfo_add() {
|
||||||
|
res="00" # add player
|
||||||
|
res+="01" # total players
|
||||||
|
res+="0000000000000000000000000000$2" # random UUID
|
||||||
|
|
||||||
|
res+="$(str_len "$1")$(echo -n "$1" | xxd -p)"
|
||||||
|
|
||||||
|
res+="00" # array len; can be zero, but no skins then
|
||||||
|
|
||||||
|
res+="01" # gamemode: creative
|
||||||
|
res+="01" # ping: 1ms
|
||||||
|
res+="00" # has display name: false
|
||||||
|
|
||||||
|
echo -n "$(hexpacket_len "$res")36$res" | xxd -p -r
|
||||||
|
|
||||||
|
log "sent playerinfo"
|
||||||
|
}
|
||||||
|
|
||||||
|
# pkt_spawnplayer(eid)
|
||||||
|
function pkt_spawnplayer() {
|
||||||
|
res="$(int2varint $((0x$1)))" # entity id
|
||||||
|
res+="0000000000000000000000000000$1" # a really badly made UUID
|
||||||
|
res+="00 00 00 00 00 00 00 00" # X
|
||||||
|
res+="00 00 00 00 00 00 00 00" # Y
|
||||||
|
res+="00 00 00 00 00 00 00 00" # Z
|
||||||
|
|
||||||
|
res+="00" # Angle (256 steps)
|
||||||
|
res+="00" # Pitch (...)
|
||||||
|
|
||||||
|
echo -n "$(hexpacket_len "$res")04$res" | xxd -p -r
|
||||||
|
rhexlog "$(hexpacket_len "$res")04$res"
|
||||||
|
log "sent spawnplayer"
|
||||||
|
}
|
||||||
|
|
||||||
|
# pkt_position(deltaX, deltaY, deltaZ, eid)
|
||||||
|
function pkt_position() {
|
||||||
|
local deltaX
|
||||||
|
local deltaY
|
||||||
|
local deltaZ
|
||||||
|
local stepX
|
||||||
|
local stepY
|
||||||
|
local stepZ
|
||||||
|
local n
|
||||||
|
|
||||||
|
deltaX=$1
|
||||||
|
deltaY=$2
|
||||||
|
deltaZ=$3
|
||||||
|
stepX=0
|
||||||
|
stepY=0
|
||||||
|
stepZ=0
|
||||||
|
|
||||||
|
while true; do
|
||||||
|
n=false
|
||||||
|
if [[ $deltaX -gt 32767 ]]; then
|
||||||
|
stepX=32767
|
||||||
|
deltaX=$((deltaX-32767))
|
||||||
|
n=true
|
||||||
|
fi
|
||||||
|
if [[ $deltaX -lt -32768 ]]; then
|
||||||
|
stepX=-32768
|
||||||
|
deltaX=$((deltaX+32768))
|
||||||
|
n=true
|
||||||
|
fi
|
||||||
|
if [[ $deltaY -gt 32767 ]]; then
|
||||||
|
stepY=32767
|
||||||
|
deltaY=$((deltaY-32767))
|
||||||
|
n=true
|
||||||
|
fi
|
||||||
|
if [[ $deltaY -lt -32768 ]]; then
|
||||||
|
stepY=-32768
|
||||||
|
deltaY=$((deltaY+32768))
|
||||||
|
n=true
|
||||||
|
fi
|
||||||
|
if [[ $deltaZ -gt 32767 ]]; then
|
||||||
|
stepZ=32767
|
||||||
|
deltaX=$((deltaZ-32767))
|
||||||
|
n=true
|
||||||
|
fi
|
||||||
|
if [[ $deltaZ -lt -32768 ]]; then
|
||||||
|
stepZ=-32768
|
||||||
|
deltaZ=$((deltaZ+32768))
|
||||||
|
n=true
|
||||||
|
fi
|
||||||
|
|
||||||
|
[[ $n == false ]] && break
|
||||||
|
|
||||||
|
pkt_position $stepX $stepY $stepZ
|
||||||
|
done
|
||||||
|
|
||||||
|
res="$(int2varint $((0x$4)))" # entity ID
|
||||||
|
res+="$(to_short $deltaX)"
|
||||||
|
res+="$(to_short $deltaY)"
|
||||||
|
res+="$(to_short $deltaZ)"
|
||||||
|
res+="00" # on ground
|
||||||
|
echo -n "$(hexpacket_len "$res")29$res" | xxd -p -r
|
||||||
|
}
|
||||||
|
|
||||||
|
# pkt_chatmessage(msg, sender_uuid)
|
||||||
|
function pkt_chatmessage() {
|
||||||
|
local msg
|
||||||
|
local json
|
||||||
|
local res
|
||||||
|
|
||||||
|
msg=$(sed -E 's/"//g;s@\\@@g' <<< "$1")
|
||||||
|
json='{"text":"'"$msg"'"}'
|
||||||
|
res="$(str_len "$json")$(echo -n "$json" | xxd -p)"
|
||||||
|
res+="00" # position: chat box
|
||||||
|
res+="$2"
|
||||||
|
|
||||||
|
echo -n "$(hexpacket_len "$res")0F$res" | xxd -p -r
|
||||||
|
}
|
||||||
|
|
||||||
|
# pkt_title(msg)
|
||||||
|
function pkt_title() {
|
||||||
|
local txt
|
||||||
|
txt='{"text":"'"$1"'"}'
|
||||||
|
res="$(str_len "$txt")$(echo -n "$txt" | xxd -p)"
|
||||||
|
echo -n "$(hexpacket_len "$res")5a$res" | xxd -p -r
|
||||||
|
}
|
40
src/palette.sh
Normal file
40
src/palette.sh
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
#!/bin/bash
|
||||||
|
# palette.sh - up to 255 block definitions
|
||||||
|
# maps first 32 IDs to pre-flattening IDs, just for fun; rest is rather random
|
||||||
|
# empty blocks (00) is what I can't easily implement :(
|
||||||
|
# https://minecraft.fandom.com/wiki/Java_Edition_data_values/Pre-flattening/Block_IDs
|
||||||
|
|
||||||
|
palette=()
|
||||||
|
|
||||||
|
palette+=("00") # air
|
||||||
|
palette+=("01") # stone
|
||||||
|
palette+=("09") # grass
|
||||||
|
palette+=("0a") # dirt
|
||||||
|
palette+=("0e") # cobblestone
|
||||||
|
palette+=("0f") # planks
|
||||||
|
palette+=("15") # sapling
|
||||||
|
palette+=("21") # bedrock
|
||||||
|
palette+=("00") #
|
||||||
|
palette+=("31") # water
|
||||||
|
palette+=("00") #
|
||||||
|
palette+=("41") # lava
|
||||||
|
palette+=("42") # sand
|
||||||
|
palette+=("44") # gravel
|
||||||
|
palette+=("45") # gold ore
|
||||||
|
palette+=("47") # iron ore
|
||||||
|
|
||||||
|
palette+=("49") # coal ore
|
||||||
|
palette+=("4d") # wood
|
||||||
|
palette+=("9401") # leaves
|
||||||
|
palette+=("8402") # sponge
|
||||||
|
palette+=("8602") # glass
|
||||||
|
palette+=("8702") # lapis ore
|
||||||
|
palette+=("8902")
|
||||||
|
palette+=("00") #
|
||||||
|
palette+=("9602") # sandstone
|
||||||
|
palette+=("00") #
|
||||||
|
palette+=("00") #
|
||||||
|
palette+=("00") #
|
||||||
|
palette+=("00") #
|
||||||
|
palette+=("00") #
|
||||||
|
palette+=("f60a") # grass
|
Loading…
Reference in a new issue