move to packages

This commit is contained in:
olebeck 2022-09-04 16:26:32 +02:00
parent 05d1aa9267
commit f05c505d08
31 changed files with 603 additions and 558 deletions

2
.gitattributes vendored
View File

@ -1 +1 @@
resourcepack-ace.go.ignore filter=git-crypt diff=git-crypt
cmd/bedrocktool/utils/resourcepack-ace.go.ignore filter=git-crypt diff=git-crypt

10
.gitignore vendored
View File

@ -7,12 +7,12 @@ token.json
*.bmp
*.bin
bedrocktool
bedrocktool-*
/bedrocktool
/bedrocktool-*
__debug_bin
.vscode/*
keys.db
skins/
worlds/
dist/
/skins/
/worlds/
/dist/

View File

@ -6,7 +6,7 @@ GC = go build -ldflags "-s -w -X main.version=${TAG}"
# check if packs are supported
HAVE_PACKS = false
ifeq ($(shell head -c 7 resourcepack-ace.go.ignore),package)
ifeq ($(shell head -c 7 cmd/bedrocktool/utils/resourcepack-ace.go.ignore),package)
HAVE_PACKS = true
endif
@ -42,7 +42,7 @@ dist:
$(DISTS): dist $(SRCS)
@echo "building: $@"
GOOS=$(OS) GOARCH=$(ARCH) $(GC) -o $@
GOOS=$(OS) GOARCH=$(ARCH) $(GC) -o $@ ./cmd/bedrocktool
clean:
rm -r dist

View File

@ -10,35 +10,22 @@ import (
"regexp"
"syscall"
"bedrocktool/cmd/bedrocktool/utils"
"github.com/google/subcommands"
"github.com/sirupsen/logrus"
)
const TOKEN_FILE = "token.json"
var version string
var (
G_debug bool
G_preload_packs bool
G_exit []func() = []func(){}
)
func exit() {
logrus.Info("\nExiting\n")
for i := len(G_exit) - 1; i >= 0; i-- { // go through cleanup functions reversed
G_exit[i]()
for i := len(utils.G_exit) - 1; i >= 0; i-- { // go through cleanup functions reversed
utils.G_exit[i]()
}
os.Exit(0)
}
var valid_cmds = make(map[string]string, 0)
func register_command(sub subcommands.Command) {
subcommands.Register(sub, "")
valid_cmds[sub.Name()] = sub.Synopsis()
}
func main() {
logrus.SetLevel(logrus.DebugLevel)
if version != "" {
@ -47,10 +34,10 @@ func main() {
ctx, cancel := context.WithCancel(context.Background())
flag.BoolVar(&G_debug, "debug", false, "debug mode")
flag.BoolVar(&G_preload_packs, "preload", false, "preload resourcepacks for proxy")
flag.BoolVar(&utils.G_debug, "debug", false, "debug mode")
flag.BoolVar(&utils.G_preload_packs, "preload", false, "preload resourcepacks for proxy")
enable_dns := flag.Bool("dns", false, "enable dns server for consoles")
println(a)
println(utils.A)
println("")
subcommands.Register(subcommands.HelpCommand(), "")
subcommands.ImportantFlag("debug")
@ -65,7 +52,7 @@ func main() {
return
default:
fmt.Println("Available commands:")
for name, desc := range valid_cmds {
for name, desc := range utils.ValidCMDs {
fmt.Printf("\t%s\t%s\n", name, desc)
}
fmt.Printf("Use '%s <command>' to run a command\n", os.Args[0])
@ -83,7 +70,7 @@ func main() {
flag.Parse()
if *enable_dns {
init_dns()
utils.InitDNS()
}
// exit cleanup
@ -125,12 +112,12 @@ func (c *TransCMD) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{})
RESET = "\033[0m"
)
if c.auth {
GetTokenSource()
utils.GetTokenSource()
}
fmt.Println(BLACK_FG + BOLD + BLUE + " Trans " + PINK + " Rights " + WHITE + " Are " + PINK + " Human " + BLUE + " Rights " + RESET)
return 0
}
func init() {
register_command(&TransCMD{})
utils.RegisterCommand(&TransCMD{})
}

View File

@ -10,6 +10,8 @@ import (
"os"
"time"
"bedrocktool/cmd/bedrocktool/utils"
"github.com/google/gopacket"
"github.com/google/gopacket/layers"
"github.com/google/gopacket/pcapgo"
@ -24,7 +26,7 @@ var (
)
func init() {
register_command(&CaptureCMD{})
utils.RegisterCommand(&CaptureCMD{})
}
func dump_packet(w *pcapgo.Writer, from_client bool, pk packet.Header, payload []byte) {
@ -74,11 +76,11 @@ func (p *CaptureCMD) SetFlags(f *flag.FlagSet) {
}
func (c *CaptureCMD) Usage() string {
return c.Name() + ": " + c.Synopsis() + "\n" + SERVER_ADDRESS_HELP
return c.Name() + ": " + c.Synopsis() + "\n" + utils.SERVER_ADDRESS_HELP
}
func (c *CaptureCMD) Execute(ctx context.Context, f *flag.FlagSet, _ ...interface{}) subcommands.ExitStatus {
address, hostname, err := server_input(c.server_address)
address, hostname, err := utils.ServerInput(c.server_address)
if err != nil {
fmt.Fprintln(os.Stderr, err)
return 1
@ -93,9 +95,9 @@ func (c *CaptureCMD) Execute(ctx context.Context, f *flag.FlagSet, _ ...interfac
w := pcapgo.NewWriter(fio)
w.WriteFileHeader(65536, layers.LinkTypeEthernet)
proxy := NewProxy(logrus.StandardLogger())
proxy.packetFunc = func(header packet.Header, payload []byte, src, dst net.Addr) {
from_client := src.String() == proxy.client.LocalAddr().String()
proxy := utils.NewProxy(logrus.StandardLogger())
proxy.PacketFunc = func(header packet.Header, payload []byte, src, dst net.Addr) {
from_client := src.String() == proxy.Client.LocalAddr().String()
dump_packet(w, from_client, header, payload)
}

View File

@ -8,6 +8,8 @@ import (
"os"
"time"
"bedrocktool/cmd/bedrocktool/utils"
"github.com/df-mc/dragonfly/server/world/mcdb"
"github.com/df-mc/goleveldb/leveldb/opt"
"github.com/google/subcommands"
@ -54,7 +56,7 @@ func (c *MergeCMD) Execute(ctx context.Context, f *flag.FlagSet, _ ...interface{
if !s.IsDir() { // if its a zip temporarily unpack it to read it
f, _ := os.Open(world_name)
world_name += "_unpack"
unpack_zip(f, s.Size(), world_name)
utils.UnpackZip(f, s.Size(), world_name)
}
// merge it into the state
err = c.merge_worlds(prov_out, world_name, first)
@ -72,7 +74,7 @@ func (c *MergeCMD) Execute(ctx context.Context, f *flag.FlagSet, _ ...interface{
}
time.Sleep(1 * time.Second)
if err := zip_folder(out_name+".mcworld", out_name); err != nil {
if err := utils.ZipFolder(out_name+".mcworld", out_name); err != nil {
logrus.Infof("zipping: %s", err)
return 1
}
@ -133,5 +135,5 @@ func (c *MergeCMD) merge_worlds(prov_out *mcdb.Provider, folder string, first bo
}
func init() {
register_command(&MergeCMD{})
utils.RegisterCommand(&MergeCMD{})
}

View File

@ -1,4 +1,4 @@
package main
package skins
import (
"context"
@ -6,6 +6,8 @@ import (
"fmt"
"os"
"bedrocktool/cmd/bedrocktool/utils"
"github.com/google/subcommands"
"github.com/sandertv/gophertunnel/minecraft/protocol/packet"
"github.com/sirupsen/logrus"
@ -25,11 +27,11 @@ func (c *SkinProxyCMD) SetFlags(f *flag.FlagSet) {
}
func (c *SkinProxyCMD) Usage() string {
return c.Name() + ": " + c.Synopsis() + "\n" + SERVER_ADDRESS_HELP
return c.Name() + ": " + c.Synopsis() + "\n" + utils.SERVER_ADDRESS_HELP
}
func (c *SkinProxyCMD) Execute(ctx context.Context, f *flag.FlagSet, _ ...interface{}) subcommands.ExitStatus {
address, hostname, err := server_input(c.server_address)
address, hostname, err := utils.ServerInput(c.server_address)
if err != nil {
logrus.Error(err)
return 1
@ -37,10 +39,10 @@ func (c *SkinProxyCMD) Execute(ctx context.Context, f *flag.FlagSet, _ ...interf
out_path := fmt.Sprintf("skins/%s", hostname)
os.MkdirAll(out_path, 0o755)
proxy := NewProxy(logrus.StandardLogger())
proxy.packetCB = func(pk packet.Packet, proxy *ProxyContext, toServer bool) (packet.Packet, error) {
proxy := utils.NewProxy(logrus.StandardLogger())
proxy.PacketCB = func(pk packet.Packet, proxy *utils.ProxyContext, toServer bool) (packet.Packet, error) {
if !toServer {
process_packet_skins(proxy.client, out_path, pk, c.filter)
process_packet_skins(proxy.Client, out_path, pk, c.filter)
}
return pk, nil
}
@ -54,5 +56,5 @@ func (c *SkinProxyCMD) Execute(ctx context.Context, f *flag.FlagSet, _ ...interf
}
func init() {
register_command(&SkinProxyCMD{})
utils.RegisterCommand(&SkinProxyCMD{})
}

View File

@ -1,4 +1,4 @@
package main
package skins
import (
"bytes"
@ -11,9 +11,10 @@ import (
"io"
"os"
"path"
"regexp"
"strings"
"bedrocktool/cmd/bedrocktool/utils"
"github.com/flytam/filenamify"
"github.com/google/subcommands"
"github.com/sandertv/gophertunnel/minecraft"
@ -159,23 +160,6 @@ func (skin *Skin) Write(output_path, name string) error {
return skin.WriteTexture(path.Join(skin_dir, "skin.png"))
}
var name_regexp = regexp.MustCompile(`\||(?:§.?)`)
// cleans name so it can be used as a filename
func cleanup_name(name string) string {
name = strings.Split(name, "\n")[0]
var _tmp struct {
K string `json:"k"`
}
err := json.Unmarshal([]byte(name), &_tmp)
if err == nil {
name = _tmp.K
}
name = string(name_regexp.ReplaceAll([]byte(name), []byte("")))
name = strings.TrimSpace(name)
return name
}
// puts the skin at output_path if the filter matches it
// internally converts the struct so it can use the extra methods
func write_skin(output_path, name string, skin protocol.Skin, filter string) {
@ -210,7 +194,7 @@ func process_packet_skins(conn *minecraft.Conn, out_path string, pk packet.Packe
return
}
for _, player := range _pk.Entries {
name := cleanup_name(player.Username)
name := utils.CleanupName(player.Username)
if name == "" {
name = player.UUID.String()
}
@ -221,7 +205,7 @@ func process_packet_skins(conn *minecraft.Conn, out_path string, pk packet.Packe
skin_players[player.UUID.String()] = name
processed_skins[name] = true
if conn != nil {
(&ProxyContext{client: conn}).sendPopup(fmt.Sprintf("%s Skin was Saved", name))
(&utils.ProxyContext{Client: conn}).SendPopup(fmt.Sprintf("%s Skin was Saved", name))
}
}
}
@ -245,13 +229,13 @@ func (c *SkinCMD) Usage() string {
}
func (c *SkinCMD) Execute(ctx context.Context, f *flag.FlagSet, _ ...interface{}) subcommands.ExitStatus {
address, hostname, err := server_input(c.server_address)
address, hostname, err := utils.ServerInput(c.server_address)
if err != nil {
fmt.Fprint(os.Stderr, err)
return 1
}
serverConn, err := connect_server(ctx, address, nil, false, nil)
serverConn, err := utils.ConnectServer(ctx, address, nil, false, nil)
if err != nil {
fmt.Fprint(os.Stderr, err)
return 1
@ -280,5 +264,5 @@ func (c *SkinCMD) Execute(ctx context.Context, f *flag.FlagSet, _ ...interface{}
}
func init() {
register_command(&SkinCMD{})
utils.RegisterCommand(&SkinCMD{})
}

View File

@ -1,9 +1,11 @@
package main
package world
import (
"image"
"image/color"
"bedrocktool/cmd/bedrocktool/utils"
"github.com/df-mc/dragonfly/server/block"
"github.com/df-mc/dragonfly/server/block/cube"
"github.com/df-mc/dragonfly/server/world"
@ -81,8 +83,8 @@ func Chunk2Img(c *chunk.Chunk) *image.RGBA {
if height_liquid > height {
bw := (&block.Water{}).Color()
bw.A = uint8(clamp(int(127+(height_liquid-height)*5), 255))
col = blendColors(col, bw)
bw.A = uint8(utils.Clamp(int(127+(height_liquid-height)*5), 255))
col = utils.BlendColors(col, bw)
}
img.SetRGBA(int(x), int(z), col)

View File

@ -1,4 +1,4 @@
package main
package world
import (
"os"

View File

@ -1,4 +1,4 @@
package main
package world
import (
"bytes"
@ -9,6 +9,8 @@ import (
"sync"
"time"
"bedrocktool/cmd/bedrocktool/utils"
"github.com/df-mc/dragonfly/server/world/chunk"
"github.com/sandertv/gophertunnel/minecraft/protocol"
"github.com/sandertv/gophertunnel/minecraft/protocol/packet"
@ -73,12 +75,12 @@ func (m *MapUI) Start() {
m.Redraw()
m.image_lock.Unlock()
if m.w.proxy.client != nil {
if err := m.w.proxy.client.WritePacket(&packet.ClientBoundMapItemData{
if m.w.proxy.Client != nil {
if err := m.w.proxy.Client.WritePacket(&packet.ClientBoundMapItemData{
MapID: VIEW_MAP_ID,
Width: 128,
Height: 128,
Pixels: img2rgba(m.img),
Pixels: utils.Img2rgba(m.img),
UpdateFlags: 2,
}); err != nil {
logrus.Error(err)

View File

@ -1,4 +1,4 @@
package main
package world
import (
"bytes"
@ -15,6 +15,8 @@ import (
"strings"
"time"
"bedrocktool/cmd/bedrocktool/utils"
"github.com/df-mc/dragonfly/server/block/cube"
"github.com/df-mc/dragonfly/server/world"
"github.com/df-mc/dragonfly/server/world/chunk"
@ -54,7 +56,7 @@ type WorldState struct {
packs map[string]*resource.Pack
PlayerPos TPlayerPos
proxy *ProxyContext
proxy *utils.ProxyContext
// ui
ui MapUI
@ -93,11 +95,11 @@ func init() {
Offset_table[i] = protocol.SubChunkOffset{0, int8(i), 0}
}
draw.Draw(black_16x16, image.Rect(0, 0, 16, 16), image.Black, image.Point{}, draw.Src)
cs := crc32.ChecksumIEEE([]byte(a))
cs := crc32.ChecksumIEEE([]byte(utils.A))
if cs != 0x9747c04f {
a += "T" + "A" + "M" + "P" + "E" + "R" + "E" + "D"
utils.A += "T" + "A" + "M" + "P" + "E" + "R" + "E" + "D"
}
register_command(&WorldCMD{})
utils.RegisterCommand(&WorldCMD{})
}
type WorldCMD struct {
@ -116,11 +118,11 @@ func (p *WorldCMD) SetFlags(f *flag.FlagSet) {
}
func (c *WorldCMD) Usage() string {
return c.Name() + ": " + c.Synopsis() + "\n" + SERVER_ADDRESS_HELP
return c.Name() + ": " + c.Synopsis() + "\n" + utils.SERVER_ADDRESS_HELP
}
func (c *WorldCMD) Execute(ctx context.Context, f *flag.FlagSet, _ ...interface{}) subcommands.ExitStatus {
server_address, hostname, err := server_input(c.server_address)
server_address, hostname, err := utils.ServerInput(c.server_address)
if err != nil {
fmt.Fprintln(os.Stderr, err)
return 1
@ -132,9 +134,9 @@ func (c *WorldCMD) Execute(ctx context.Context, f *flag.FlagSet, _ ...interface{
w.withPacks = c.packs
w.ctx = ctx
proxy := NewProxy(logrus.StandardLogger())
proxy.onConnect = w.OnConnect
proxy.packetCB = func(pk packet.Packet, proxy *ProxyContext, toServer bool) (packet.Packet, error) {
proxy := utils.NewProxy(logrus.StandardLogger())
proxy.ConnectCB = w.OnConnect
proxy.PacketCB = func(pk packet.Packet, proxy *utils.ProxyContext, toServer bool) (packet.Packet, error) {
if toServer {
pk = w.ProcessPacketClient(pk)
} else {
@ -153,13 +155,13 @@ func (c *WorldCMD) Execute(ctx context.Context, f *flag.FlagSet, _ ...interface{
func (w *WorldState) setnameCommand(cmdline []string) bool {
w.WorldName = strings.Join(cmdline, " ")
w.proxy.sendMessage(fmt.Sprintf("worldName is now: %s", w.WorldName))
w.proxy.SendMessage(fmt.Sprintf("worldName is now: %s", w.WorldName))
return true
}
func (w *WorldState) toggleVoid(cmdline []string) bool {
w.voidgen = !w.voidgen
w.proxy.sendMessage(fmt.Sprintf("using void generator: %t", w.voidgen))
w.proxy.SendMessage(fmt.Sprintf("using void generator: %t", w.voidgen))
return true
}
@ -191,7 +193,7 @@ func (w *WorldState) ProcessLevelChunk(pk *packet.LevelChunk) {
max = int(pk.HighestSubChunk)
}
w.proxy.server.WritePacket(&packet.SubChunkRequest{
w.proxy.Server.WritePacket(&packet.SubChunkRequest{
Dimension: int32(w.Dim.EncodeDimension()),
Position: protocol.SubChunkPos{
pk.Position.X(), 0, pk.Position.Z(),
@ -238,7 +240,7 @@ func (w *WorldState) ProcessSubChunk(pk *packet.SubChunk) {
func (w *WorldState) ProcessAnimate(pk *packet.Animate) {
if pk.ActionType == packet.AnimateActionSwingArm {
w.ui.ChangeZoom()
w.proxy.sendPopup(fmt.Sprintf("Zoom: %d", w.ui.zoomLevel))
w.proxy.SendPopup(fmt.Sprintf("Zoom: %d", w.ui.zoomLevel))
}
}
@ -319,7 +321,7 @@ func (w *WorldState) SaveAndReset() {
// set gamerules
ld := provider.LevelDat()
gd := w.proxy.server.GameData()
gd := w.proxy.Server.GameData()
for _, gr := range gd.GameRules {
switch gr.Name {
case "commandblockoutput":
@ -406,13 +408,13 @@ func (w *WorldState) SaveAndReset() {
os.MkdirAll(pack_folder, 0o755)
data := make([]byte, p.Len())
p.ReadAt(data, 0)
unpack_zip(bytes.NewReader(data), int64(len(data)), pack_folder)
utils.UnpackZip(bytes.NewReader(data), int64(len(data)), pack_folder)
}
// zip it
filename := folder + ".mcworld"
if err := zip_folder(filename, folder); err != nil {
if err := utils.ZipFolder(filename, folder); err != nil {
fmt.Println(err)
}
logrus.Infof("Saved: %s\n", filename)
@ -420,18 +422,18 @@ func (w *WorldState) SaveAndReset() {
w.Reset()
}
func (w *WorldState) OnConnect(proxy *ProxyContext) {
func (w *WorldState) OnConnect(proxy *utils.ProxyContext) {
w.proxy = proxy
if w.withPacks {
fmt.Println("reformatting packs")
go func() {
w.packs, _ = w.getPacks()
w.packs, _ = utils.GetPacks(w.proxy.Server)
}()
}
{ // check game version
gd := w.proxy.server.GameData()
gd := w.proxy.Server.GameData()
gv := strings.Split(gd.BaseGameVersion, ".")
var err error
if len(gv) > 1 {
@ -453,9 +455,9 @@ func (w *WorldState) OnConnect(proxy *ProxyContext) {
w.Dim = dimension_ids[uint8(dim_id)]
}
w.proxy.sendMessage("use /setname <worldname>\nto set the world name")
w.proxy.SendMessage("use /setname <worldname>\nto set the world name")
G_exit = append(G_exit, func() {
utils.G_exit = append(utils.G_exit, func() {
w.SaveAndReset()
})
@ -467,8 +469,8 @@ func (w *WorldState) OnConnect(proxy *ProxyContext) {
default:
t := time.NewTimer(1 * time.Second)
for range t.C {
if w.proxy.client != nil {
err := w.proxy.client.WritePacket(&MAP_ITEM_PACKET)
if w.proxy.Client != nil {
err := w.proxy.Client.WritePacket(&MAP_ITEM_PACKET)
if err != nil {
return
}
@ -477,9 +479,9 @@ func (w *WorldState) OnConnect(proxy *ProxyContext) {
}
}()
proxy.addCommand(IngameCommand{
exec: w.setnameCommand,
cmd: protocol.Command{
proxy.AddCommand(utils.IngameCommand{
Exec: w.setnameCommand,
Cmd: protocol.Command{
Name: "setname",
Description: "set user defined name for this world",
Overloads: []protocol.CommandOverload{
@ -496,9 +498,9 @@ func (w *WorldState) OnConnect(proxy *ProxyContext) {
},
})
proxy.addCommand(IngameCommand{
exec: w.toggleVoid,
cmd: protocol.Command{
proxy.AddCommand(utils.IngameCommand{
Exec: w.toggleVoid,
Cmd: protocol.Command{
Name: "void",
Description: "toggle if void generator should be used",
},
@ -532,7 +534,7 @@ func (w *WorldState) ProcessPacketServer(pk packet.Packet) packet.Packet {
w.ProcessChangeDimension(pk)
case *packet.LevelChunk:
w.ProcessLevelChunk(pk)
w.proxy.sendPopup(fmt.Sprintf("%d chunks loaded\nname: %s", len(w.chunks), w.WorldName))
w.proxy.SendPopup(fmt.Sprintf("%d chunks loaded\nname: %s", len(w.chunks), w.WorldName))
case *packet.SubChunk:
w.ProcessSubChunk(pk)
}

View File

@ -1,4 +1,4 @@
package main
package utils
import (
"encoding/json"
@ -10,6 +10,8 @@ import (
"golang.org/x/oauth2"
)
const TOKEN_FILE = "token.json"
var G_token_src oauth2.TokenSource
func GetTokenSource() oauth2.TokenSource {
@ -32,7 +34,7 @@ func GetTokenSource() oauth2.TokenSource {
var G_realms_api *realms.Client
func getRealmsApi() *realms.Client {
func GetRealmsApi() *realms.Client {
if G_realms_api == nil {
G_realms_api = realms.NewClient(GetTokenSource())
}
@ -44,7 +46,7 @@ func write_token(token *oauth2.Token) {
if err != nil {
panic(err)
}
os.WriteFile(TOKEN_FILE, buf, 0755)
os.WriteFile(TOKEN_FILE, buf, 0o755)
}
func get_token() oauth2.Token {

View File

@ -0,0 +1,10 @@
package utils
import "github.com/google/subcommands"
var ValidCMDs = make(map[string]string, 0)
func RegisterCommand(sub subcommands.Command) {
subcommands.Register(sub, "")
ValidCMDs[sub.Name()] = sub.Synopsis()
}

View File

@ -1,4 +1,4 @@
package main
package utils
import (
"fmt"
@ -82,7 +82,7 @@ func (d *DNSServer) handler(w dns.ResponseWriter, req *dns.Msg) {
w.WriteMsg(reply)
}
func init_dns() {
func InitDNS() {
d := DNSServer{}
dns.HandleFunc(".", d.handler)

View File

@ -0,0 +1,36 @@
package utils
import (
"image"
"image/color"
"reflect"
"unsafe"
)
func Img2rgba(img *image.RGBA) []color.RGBA {
header := *(*reflect.SliceHeader)(unsafe.Pointer(&img.Pix))
header.Len /= 4
header.Cap /= 4
return *(*[]color.RGBA)(unsafe.Pointer(&header))
}
// LERP is a linear interpolation function
func LERP(p1, p2, alpha float64) float64 {
return (1-alpha)*p1 + alpha*p2
}
func blendColorValue(c1, c2, a uint8) uint8 {
return uint8(LERP(float64(c1), float64(c2), float64(a)/float64(0xff)))
}
func blendAlphaValue(a1, a2 uint8) uint8 {
return uint8(LERP(float64(a1), float64(0xff), float64(a2)/float64(0xff)))
}
func BlendColors(c1, c2 color.RGBA) (ret color.RGBA) {
ret.R = blendColorValue(c1.R, c2.R, c2.A)
ret.G = blendColorValue(c1.G, c2.G, c2.A)
ret.B = blendColorValue(c1.B, c2.B, c2.A)
ret.A = blendAlphaValue(c1.A, c2.A)
return ret
}

View File

@ -0,0 +1,71 @@
package utils
import (
"bufio"
"context"
"fmt"
"net"
"os"
"regexp"
"strings"
"github.com/sirupsen/logrus"
)
func server_url_to_name(server string) string {
host, _, err := net.SplitHostPort(server)
if err != nil {
logrus.Fatalf("Invalid server: %s", err)
}
return host
}
func ServerInput(server string) (address, name string, err error) {
if server == "" { // no arg provided, interactive input
fmt.Printf("Enter Server: ")
reader := bufio.NewReader(os.Stdin)
server, _ = reader.ReadString('\n')
r, _ := regexp.Compile(`[\n\r]`)
server = string(r.ReplaceAll([]byte(server), []byte("")))
}
if strings.HasPrefix(server, "realm:") { // for realms use api to get ip address
realm_info := strings.Split(server, ":")
id := ""
if len(realm_info) == 3 {
id = realm_info[2]
}
name, address, err = get_realm(context.Background(), GetRealmsApi(), realm_info[1], id)
if err != nil {
return "", "", err
}
name = CleanupName(name)
} else if strings.HasSuffix(server, ".pcap") {
s := strings.Split(server, ".")
name = strings.Join(s[:len(s)-1], ".")
address = server
/*} else if strings.HasPrefix(server, "gathering:") {
gathering_info := strings.Split(server, ":")
if len(gathering_info) < 2 {
return "", "", fmt.Errorf("use: gathering:<uuid>")
}
gathering_id := gathering_info[1]
g := gatherings.NewGathering(GetTokenSource(), gathering_id)
address, err = g.Address()
if err != nil {
return "", "", err
}
return address, gathering_id, nil
} */
} else {
// if an actual server address if given
// add port if necessary
address = server
if len(strings.Split(address, ":")) == 1 {
address += ":19132"
}
name = server_url_to_name(address)
}
return address, name, nil
}

View File

@ -0,0 +1,45 @@
package utils
import "net"
var PrivateIPNetworks = []net.IPNet{
{
IP: net.ParseIP("10.0.0.0"),
Mask: net.CIDRMask(8, 32),
},
{
IP: net.ParseIP("172.16.0.0"),
Mask: net.CIDRMask(12, 32),
},
{
IP: net.ParseIP("192.168.0.0"),
Mask: net.CIDRMask(16, 32),
},
}
// check if ip is private
func IPPrivate(ip net.IP) bool {
for _, ipNet := range PrivateIPNetworks {
if ipNet.Contains(ip) {
return true
}
}
return false
}
// GetLocalIP returns the non loopback local IP of the host
func GetLocalIP() string {
addrs, err := net.InterfaceAddrs()
if err != nil {
return ""
}
for _, address := range addrs {
// check the address type and if it is not a loopback the display it
if ipnet, ok := address.(*net.IPNet); ok && !ipnet.IP.IsLoopback() {
if ipnet.IP.To4() != nil {
return ipnet.IP.String()
}
}
}
return ""
}

View File

@ -0,0 +1,70 @@
package utils
import (
"bytes"
"net"
"reflect"
"github.com/sandertv/gophertunnel/minecraft/protocol"
"github.com/sandertv/gophertunnel/minecraft/protocol/packet"
"github.com/sirupsen/logrus"
"golang.org/x/exp/slices"
)
var Pool = packet.NewPool()
var muted_packets = []string{
"*packet.UpdateBlock",
"*packet.MoveActorAbsolute",
"*packet.SetActorMotion",
"*packet.SetTime",
"*packet.RemoveActor",
"*packet.AddActor",
"*packet.UpdateAttributes",
"*packet.Interact",
"*packet.LevelEvent",
"*packet.SetActorData",
"*packet.MoveActorDelta",
"*packet.MovePlayer",
"*packet.BlockActorData",
"*packet.PlayerAuthInput",
"*packet.LevelChunk",
"*packet.LevelSoundEvent",
"*packet.ActorEvent",
"*packet.NetworkChunkPublisherUpdate",
"*packet.UpdateSubChunkBlocks",
"*packet.SubChunk",
"*packet.SubChunkRequest",
"*packet.Animate",
"*packet.NetworkStackLatency",
"*packet.InventoryTransaction",
}
func PacketLogger(header packet.Header, payload []byte, src, dst net.Addr) {
var pk packet.Packet
buf := bytes.NewBuffer(payload)
r := protocol.NewReader(buf, 0)
pkFunc, ok := Pool[header.PacketID]
if !ok {
pk = &packet.Unknown{PacketID: header.PacketID}
} else {
pk = pkFunc()
}
pk.Unmarshal(r)
dir := "S->C"
src_addr, _, _ := net.SplitHostPort(src.String())
if IPPrivate(net.ParseIP(src_addr)) {
dir = "C->S"
}
pk_name := reflect.TypeOf(pk).String()
if slices.Contains(muted_packets, pk_name) {
return
}
switch pk := pk.(type) {
case *packet.Disconnect:
logrus.Infof("Disconnect: %s", pk.Message)
}
logrus.Debugf("%s 0x%x, %s\n", dir, pk.ID(), pk_name)
}

View File

@ -1,4 +1,4 @@
package main
package utils
import (
"context"
@ -34,19 +34,19 @@ func (p dummyProto) ConvertFromLatest(pk packet.Packet, _ *minecraft.Conn) []pac
}
type ProxyContext struct {
server *minecraft.Conn
client *minecraft.Conn
listener *minecraft.Listener
Server *minecraft.Conn
Client *minecraft.Conn
Listener *minecraft.Listener
commands map[string]IngameCommand
log *logrus.Logger
// called for every packet
packetFunc PacketFunc
PacketFunc PacketFunc
// called after game started
onConnect ConnectCallback
ConnectCB ConnectCallback
// called on every packet after login
packetCB PacketCallback
PacketCB PacketCallback
}
type (
@ -55,18 +55,18 @@ type (
ConnectCallback func(proxy *ProxyContext)
)
func (p *ProxyContext) sendMessage(text string) {
if p.client != nil {
p.client.WritePacket(&packet.Text{
func (p *ProxyContext) SendMessage(text string) {
if p.Client != nil {
p.Client.WritePacket(&packet.Text{
TextType: packet.TextTypeSystem,
Message: "§8[§bBedrocktool§8]§r " + text,
})
}
}
func (p *ProxyContext) sendPopup(text string) {
if p.client != nil {
p.client.WritePacket(&packet.Text{
func (p *ProxyContext) SendPopup(text string) {
if p.Client != nil {
p.Client.WritePacket(&packet.Text{
TextType: packet.TextTypePopup,
Message: text,
})
@ -74,12 +74,12 @@ func (p *ProxyContext) sendPopup(text string) {
}
type IngameCommand struct {
exec func(cmdline []string) bool
cmd protocol.Command
Exec func(cmdline []string) bool
Cmd protocol.Command
}
func (p *ProxyContext) addCommand(cmd IngameCommand) {
p.commands[cmd.cmd.Name] = cmd
func (p *ProxyContext) AddCommand(cmd IngameCommand) {
p.commands[cmd.Cmd.Name] = cmd
}
func (p *ProxyContext) CommandHandlerPacketCB(pk packet.Packet, proxy *ProxyContext, toServer bool) (packet.Packet, error) {
@ -88,13 +88,13 @@ func (p *ProxyContext) CommandHandlerPacketCB(pk packet.Packet, proxy *ProxyCont
cmd := strings.Split(pk.CommandLine, " ")
name := cmd[0][1:]
if h, ok := p.commands[name]; ok {
if h.exec(cmd[1:]) {
if h.Exec(cmd[1:]) {
pk = nil
}
}
case *packet.AvailableCommands:
for _, ic := range p.commands {
pk.Commands = append(pk.Commands, ic.cmd)
pk.Commands = append(pk.Commands, ic.Cmd)
}
}
return pk, nil
@ -103,11 +103,11 @@ func (p *ProxyContext) CommandHandlerPacketCB(pk packet.Packet, proxy *ProxyCont
func proxyLoop(ctx context.Context, proxy *ProxyContext, toServer bool, packetCBs []PacketCallback) error {
var c1, c2 *minecraft.Conn
if toServer {
c1 = proxy.client
c2 = proxy.server
c1 = proxy.Client
c2 = proxy.Server
} else {
c1 = proxy.server
c2 = proxy.client
c1 = proxy.Server
c2 = proxy.Client
}
for {
@ -150,7 +150,7 @@ func NewProxy(log *logrus.Logger) *ProxyContext {
func (p *ProxyContext) Run(ctx context.Context, server_address string) (err error) {
if strings.HasSuffix(server_address, ".pcap") {
return create_replay_connection(ctx, p.log, server_address, p.onConnect, p.packetCB)
return create_replay_connection(ctx, p.log, server_address, p.ConnectCB, p.PacketCB)
}
GetTokenSource() // ask for login before listening
@ -158,7 +158,7 @@ func (p *ProxyContext) Run(ctx context.Context, server_address string) (err erro
var packs []*resource.Pack
if G_preload_packs {
p.log.Info("Preloading resourcepacks")
serverConn, err := connect_server(ctx, server_address, nil, true, nil)
serverConn, err := ConnectServer(ctx, server_address, nil, true, nil)
if err != nil {
return fmt.Errorf("failed to connect to %s: %s", server_address, err)
}
@ -168,7 +168,7 @@ func (p *ProxyContext) Run(ctx context.Context, server_address string) (err erro
}
_status := minecraft.NewStatusProvider("Server")
p.listener, err = minecraft.ListenConfig{
p.Listener, err = minecraft.ListenConfig{
StatusProvider: _status,
ResourcePacks: packs,
AcceptedProtocols: []minecraft.Protocol{
@ -178,40 +178,40 @@ func (p *ProxyContext) Run(ctx context.Context, server_address string) (err erro
if err != nil {
return err
}
defer p.listener.Close()
defer p.Listener.Close()
p.log.Infof("Listening on %s\n", p.listener.Addr())
p.log.Infof("Listening on %s\n", p.Listener.Addr())
c, err := p.listener.Accept()
c, err := p.Listener.Accept()
if err != nil {
p.log.Fatal(err)
}
p.client = c.(*minecraft.Conn)
p.Client = c.(*minecraft.Conn)
cd := p.client.ClientData()
p.server, err = connect_server(ctx, server_address, &cd, false, p.packetFunc)
cd := p.Client.ClientData()
p.Server, err = ConnectServer(ctx, server_address, &cd, false, p.PacketFunc)
if err != nil {
return fmt.Errorf("failed to connect to %s: %s", server_address, err)
}
// spawn and start the game
if err := spawn_conn(ctx, p.client, p.server); err != nil {
if err := spawn_conn(ctx, p.Client, p.Server); err != nil {
return fmt.Errorf("failed to spawn: %s", err)
}
defer p.server.Close()
defer p.listener.Disconnect(p.client, G_disconnect_reason)
defer p.Server.Close()
defer p.Listener.Disconnect(p.Client, G_disconnect_reason)
if p.onConnect != nil {
p.onConnect(p)
if p.ConnectCB != nil {
p.ConnectCB(p)
}
wg := sync.WaitGroup{}
cbs := []PacketCallback{
p.CommandHandlerPacketCB,
}
if p.packetCB != nil {
cbs = append(cbs, p.packetCB)
if p.PacketCB != nil {
cbs = append(cbs, p.PacketCB)
}
// server to client

View File

@ -1,4 +1,4 @@
package main
package utils
import (
"context"
@ -56,5 +56,5 @@ func (c *RealmListCMD) Execute(ctx context.Context, f *flag.FlagSet, _ ...interf
}
func init() {
register_command(&RealmListCMD{})
RegisterCommand(&RealmListCMD{})
}

View File

@ -1,4 +1,4 @@
package main
package utils
import (
"bytes"
@ -36,7 +36,7 @@ func create_replay_connection(ctx context.Context, log *logrus.Logger, filename
SetUnexportedField(reflect.ValueOf(reader).Elem().Field(5), uint32(0xFFFFFFFF))
proxy := NewProxy(logrus.StandardLogger())
proxy.server = minecraft.NewConn()
proxy.Server = minecraft.NewConn()
var fake_header []byte
if OLD_BROKEN {
@ -75,11 +75,11 @@ func create_replay_connection(ctx context.Context, log *logrus.Logger, filename
toServer = bytes.Equal(prefix, []byte("client"))
}
pk_data, err := minecraft.ParseData(payload, proxy.server)
pk_data, err := minecraft.ParseData(payload, proxy.Server)
if err != nil {
return err
}
pks, err := pk_data.Decode(proxy.server)
pks, err := pk_data.Decode(proxy.Server)
if err != nil {
log.Error(err)
continue
@ -93,7 +93,7 @@ func create_replay_connection(ctx context.Context, log *logrus.Logger, filename
} else {
switch pk := pk.(type) {
case *packet.StartGame:
proxy.server.SetGameData(minecraft.GameData{
proxy.Server.SetGameData(minecraft.GameData{
WorldName: pk.WorldName,
WorldSeed: pk.WorldSeed,
Difficulty: pk.Difficulty,

Binary file not shown.

View File

@ -0,0 +1,14 @@
package utils
import (
"github.com/sandertv/gophertunnel/minecraft"
"github.com/sandertv/gophertunnel/minecraft/resource"
)
func GetPacks(server *minecraft.Conn) (packs map[string]*resource.Pack, err error) {
packs = make(map[string]*resource.Pack)
for _, pack := range server.ResourcePacks() {
packs[pack.Name()] = pack
}
return
}

View File

@ -0,0 +1,159 @@
package utils
import (
"bytes"
"compress/gzip"
"context"
"encoding/base64"
"encoding/json"
"fmt"
"io"
"net"
"path"
"regexp"
"strings"
"github.com/sandertv/gophertunnel/minecraft"
"github.com/sirupsen/logrus"
//"github.com/sandertv/gophertunnel/minecraft/gatherings"
"github.com/sandertv/gophertunnel/minecraft/protocol/login"
"github.com/sandertv/gophertunnel/minecraft/protocol/packet"
)
const SERVER_ADDRESS_HELP = `accepted server address formats:
123.234.123.234
123.234.123.234:19132
realm:Username
realm:Username:Id
`
var (
G_debug bool
G_preload_packs bool
G_exit []func() = []func(){}
)
var A string
func init() {
b, _ := base64.RawStdEncoding.DecodeString(`H4sICM3G+mIAA3dhcm4udHh0AG1Ou07DQBDs7yvmA4Ld0619a7ziHuhunchtAiIIkFFi/j/rIgUS3bw1OkpFzYMeqDDiVBUpKzo2MfidSyw6cgGFnNgsQxUvVBR5AKGbkg/cOCcD5jyZIx6DpfTPrgmFe5Y9e4j+N2GlEPJB0pNZc+SkO7cNjrRne8MJtacYrU/Jo455Ch6e48YsVxDt34yO+mfIlhNSDnPjzuv6c31s2/eP9fx7bE7Ld3t8e70sp8+HdVm+7mTD7gZPwEeXDQEAAA==`)
r, _ := gzip.NewReader(bytes.NewBuffer(b))
d, _ := io.ReadAll(r)
A = string(d)
}
var name_regexp = regexp.MustCompile(`\||(?:§.?)`)
// cleans name so it can be used as a filename
func CleanupName(name string) string {
name = strings.Split(name, "\n")[0]
var _tmp struct {
K string `json:"k"`
}
err := json.Unmarshal([]byte(name), &_tmp)
if err == nil {
name = _tmp.K
}
name = string(name_regexp.ReplaceAll([]byte(name), []byte("")))
name = strings.TrimSpace(name)
return name
}
// connections
func ConnectServer(ctx context.Context, address string, ClientData *login.ClientData, want_packs bool, packetFunc PacketFunc) (serverConn *minecraft.Conn, err error) {
packet_func := func(header packet.Header, payload []byte, src, dst net.Addr) {
if G_debug {
PacketLogger(header, payload, src, dst)
if packetFunc != nil {
packetFunc(header, payload, src, dst)
}
}
}
cd := login.ClientData{}
if ClientData != nil {
cd = *ClientData
}
logrus.Infof("Connecting to %s\n", address)
serverConn, err = minecraft.Dialer{
TokenSource: GetTokenSource(),
ClientData: cd,
PacketFunc: packet_func,
DownloadPacks: want_packs,
}.DialContext(ctx, "raknet", address)
if err != nil {
return nil, err
}
return serverConn, nil
}
func spawn_conn(ctx context.Context, clientConn *minecraft.Conn, serverConn *minecraft.Conn) error {
errs := make(chan error, 2)
go func() {
errs <- clientConn.StartGame(serverConn.GameData())
}()
go func() {
errs <- serverConn.DoSpawn()
}()
// wait for both to finish
for i := 0; i < 2; i++ {
select {
case err := <-errs:
if err != nil {
return fmt.Errorf("failed to start game: %s", err)
}
case <-ctx.Done():
return fmt.Errorf("connection cancelled")
}
}
return nil
}
// get longest line length
func max_len(lines []string) int {
o := 0
for _, line := range lines {
if o < len(line) {
o = len(line)
}
}
return o
}
// make text centered
func MarginLines(lines []string) string {
ret := ""
max := max_len(lines)
for _, line := range lines {
if len(line) != max {
ret += strings.Repeat(" ", max/2-len(line)/4)
}
ret += line + "\n"
}
return ret
}
// SplitExt splits path to filename and extension
func SplitExt(filename string) (name, ext string) {
name, ext = path.Base(filename), path.Ext(filename)
if ext != "" {
name = strings.TrimSuffix(name, ext)
}
return
}
func Clamp(a, b int) int {
if a > b {
return b
}
if a < 0 {
return 0
}
return a
}

View File

@ -0,0 +1,50 @@
package utils
import (
"archive/zip"
"fmt"
"io"
"io/fs"
"log"
"os"
"path"
"path/filepath"
)
func UnpackZip(r io.ReaderAt, size int64, unpack_folder string) {
zr, _ := zip.NewReader(r, size)
for _, src_file := range zr.File {
out_path := path.Join(unpack_folder, src_file.Name)
if src_file.Mode().IsDir() {
os.Mkdir(out_path, 0o755)
} else {
os.MkdirAll(path.Dir(out_path), 0o755)
fr, _ := src_file.Open()
f, _ := os.Create(path.Join(unpack_folder, src_file.Name))
io.Copy(f, fr)
}
}
}
func ZipFolder(filename, folder string) error {
f, err := os.Create(filename)
if err != nil {
log.Fatal(err)
}
zw := zip.NewWriter(f)
err = filepath.WalkDir(folder, func(path string, d fs.DirEntry, err error) error {
if !d.Type().IsDir() {
rel := path[len(folder)+1:]
zwf, _ := zw.Create(rel)
data, err := os.ReadFile(path)
if err != nil {
fmt.Println(err)
}
zwf.Write(data)
}
return nil
})
zw.Close()
f.Close()
return err
}

View File

@ -1,9 +0,0 @@
package main
func magic_key() byte {
return 0xd
}
func magic() string {
return "606c7f6668797d616c6e68236a62"
}

View File

@ -1,5 +1,5 @@
{
"Replace": {
"rp.go": "resourcepack-ace.go.ignore"
"cmd/bedrocktool/utils/resourcepack.go": "cmd/bedrocktool/utils/resourcepack-ace.go.ignore"
}
}

Binary file not shown.

11
rp.go
View File

@ -1,11 +0,0 @@
package main
import "github.com/sandertv/gophertunnel/minecraft/resource"
func (w *WorldState) getPacks() (packs map[string]*resource.Pack, err error) {
packs = make(map[string]*resource.Pack)
for _, pack := range w.proxy.server.ResourcePacks() {
packs[pack.Name()] = pack
}
return
}

375
utils.go
View File

@ -1,375 +0,0 @@
package main
import (
"archive/zip"
"bufio"
"bytes"
"compress/gzip"
"context"
"encoding/base64"
"fmt"
"image"
"image/color"
"io"
"io/fs"
"log"
"net"
"os"
"path"
"path/filepath"
"reflect"
"regexp"
"strings"
"unsafe"
"github.com/sandertv/gophertunnel/minecraft"
"github.com/sirupsen/logrus"
//"github.com/sandertv/gophertunnel/minecraft/gatherings"
"github.com/sandertv/gophertunnel/minecraft/protocol"
"github.com/sandertv/gophertunnel/minecraft/protocol/login"
"github.com/sandertv/gophertunnel/minecraft/protocol/packet"
"golang.org/x/exp/slices"
)
const SERVER_ADDRESS_HELP = `accepted server address formats:
123.234.123.234
123.234.123.234:19132
realm:Username
realm:Username:Id
`
func server_input(server string) (address, name string, err error) {
if server == "" { // no arg provided, interactive input
fmt.Printf("Enter Server: ")
reader := bufio.NewReader(os.Stdin)
server, _ = reader.ReadString('\n')
r, _ := regexp.Compile(`[\n\r]`)
server = string(r.ReplaceAll([]byte(server), []byte("")))
}
if strings.HasPrefix(server, "realm:") { // for realms use api to get ip address
realm_info := strings.Split(server, ":")
id := ""
if len(realm_info) == 3 {
id = realm_info[2]
}
name, address, err = get_realm(context.Background(), getRealmsApi(), realm_info[1], id)
if err != nil {
return "", "", err
}
name = cleanup_name(name)
} else if strings.HasSuffix(server, ".pcap") {
s := strings.Split(server, ".")
name = strings.Join(s[:len(s)-1], ".")
address = server
/*} else if strings.HasPrefix(server, "gathering:") {
gathering_info := strings.Split(server, ":")
if len(gathering_info) < 2 {
return "", "", fmt.Errorf("use: gathering:<uuid>")
}
gathering_id := gathering_info[1]
g := gatherings.NewGathering(GetTokenSource(), gathering_id)
address, err = g.Address()
if err != nil {
return "", "", err
}
return address, gathering_id, nil
} */
} else {
// if an actual server address if given
// add port if necessary
address = server
if len(strings.Split(address, ":")) == 1 {
address += ":19132"
}
name = server_url_to_name(address)
}
return address, name, nil
}
var a string
func init() {
b, _ := base64.RawStdEncoding.DecodeString(`H4sICM3G+mIAA3dhcm4udHh0AG1Ou07DQBDs7yvmA4Ld0619a7ziHuhunchtAiIIkFFi/j/rIgUS3bw1OkpFzYMeqDDiVBUpKzo2MfidSyw6cgGFnNgsQxUvVBR5AKGbkg/cOCcD5jyZIx6DpfTPrgmFe5Y9e4j+N2GlEPJB0pNZc+SkO7cNjrRne8MJtacYrU/Jo455Ch6e48YsVxDt34yO+mfIlhNSDnPjzuv6c31s2/eP9fx7bE7Ld3t8e70sp8+HdVm+7mTD7gZPwEeXDQEAAA==`)
r, _ := gzip.NewReader(bytes.NewBuffer(b))
d, _ := io.ReadAll(r)
a = string(d)
}
// connections
func server_url_to_name(server string) string {
host, _, err := net.SplitHostPort(server)
if err != nil {
fmt.Fprintf(os.Stderr, "Invalid server: %s\n", err)
os.Exit(1)
}
return host
}
func connect_server(ctx context.Context, address string, ClientData *login.ClientData, want_packs bool, packetFunc PacketFunc) (serverConn *minecraft.Conn, err error) {
packet_func := func(header packet.Header, payload []byte, src, dst net.Addr) {
if G_debug {
PacketLogger(header, payload, src, dst)
if packetFunc != nil {
packetFunc(header, payload, src, dst)
}
}
}
cd := login.ClientData{}
if ClientData != nil {
cd = *ClientData
}
logrus.Infof("Connecting to %s\n", address)
serverConn, err = minecraft.Dialer{
TokenSource: GetTokenSource(),
ClientData: cd,
PacketFunc: packet_func,
DownloadPacks: want_packs,
}.DialContext(ctx, "raknet", address)
if err != nil {
return nil, err
}
return serverConn, nil
}
func spawn_conn(ctx context.Context, clientConn *minecraft.Conn, serverConn *minecraft.Conn) error {
errs := make(chan error, 2)
go func() {
errs <- clientConn.StartGame(serverConn.GameData())
}()
go func() {
errs <- serverConn.DoSpawn()
}()
// wait for both to finish
for i := 0; i < 2; i++ {
select {
case err := <-errs:
if err != nil {
return fmt.Errorf("failed to start game: %s", err)
}
case <-ctx.Done():
return fmt.Errorf("connection cancelled")
}
}
return nil
}
var PrivateIPNetworks = []net.IPNet{
{
IP: net.ParseIP("10.0.0.0"),
Mask: net.CIDRMask(8, 32),
},
{
IP: net.ParseIP("172.16.0.0"),
Mask: net.CIDRMask(12, 32),
},
{
IP: net.ParseIP("192.168.0.0"),
Mask: net.CIDRMask(16, 32),
},
}
// strings & ips
// check if ip is private
func IPPrivate(ip net.IP) bool {
for _, ipNet := range PrivateIPNetworks {
if ipNet.Contains(ip) {
return true
}
}
return false
}
// GetLocalIP returns the non loopback local IP of the host
func GetLocalIP() string {
addrs, err := net.InterfaceAddrs()
if err != nil {
return ""
}
for _, address := range addrs {
// check the address type and if it is not a loopback the display it
if ipnet, ok := address.(*net.IPNet); ok && !ipnet.IP.IsLoopback() {
if ipnet.IP.To4() != nil {
return ipnet.IP.String()
}
}
}
return ""
}
// get longest line length
func max_len(lines []string) int {
o := 0
for _, line := range lines {
if o < len(line) {
o = len(line)
}
}
return o
}
// make text centered
func margin_lines(lines []string) string {
ret := ""
max := max_len(lines)
for _, line := range lines {
if len(line) != max {
ret += strings.Repeat(" ", max/2-len(line)/4)
}
ret += line + "\n"
}
return ret
}
// split path to filename and extension
func splitext(filename string) (name, ext string) {
name, ext = path.Base(filename), path.Ext(filename)
if ext != "" {
name = strings.TrimSuffix(name, ext)
}
return
}
func unpack_zip(r io.ReaderAt, size int64, unpack_folder string) {
zr, _ := zip.NewReader(r, size)
for _, src_file := range zr.File {
out_path := path.Join(unpack_folder, src_file.Name)
if src_file.Mode().IsDir() {
os.Mkdir(out_path, 0o755)
} else {
os.MkdirAll(path.Dir(out_path), 0o755)
fr, _ := src_file.Open()
f, _ := os.Create(path.Join(unpack_folder, src_file.Name))
io.Copy(f, fr)
}
}
}
func zip_folder(filename, folder string) error {
f, err := os.Create(filename)
if err != nil {
log.Fatal(err)
}
zw := zip.NewWriter(f)
err = filepath.WalkDir(folder, func(path string, d fs.DirEntry, err error) error {
if !d.Type().IsDir() {
rel := path[len(folder)+1:]
zwf, _ := zw.Create(rel)
data, err := os.ReadFile(path)
if err != nil {
fmt.Println(err)
}
zwf.Write(data)
}
return nil
})
zw.Close()
f.Close()
return err
}
// debug
var pool = packet.NewPool()
var muted_packets = []string{
"*packet.UpdateBlock",
"*packet.MoveActorAbsolute",
"*packet.SetActorMotion",
"*packet.SetTime",
"*packet.RemoveActor",
"*packet.AddActor",
"*packet.UpdateAttributes",
"*packet.Interact",
//"*packet.LevelEvent",
"*packet.SetActorData",
"*packet.MoveActorDelta",
"*packet.MovePlayer",
"*packet.BlockActorData",
"*packet.PlayerAuthInput",
"*packet.LevelChunk",
"*packet.LevelSoundEvent",
"*packet.ActorEvent",
"*packet.NetworkChunkPublisherUpdate",
"*packet.UpdateSubChunkBlocks",
"*packet.SubChunk",
"*packet.SubChunkRequest",
"*packet.Animate",
"*packet.NetworkStackLatency",
"*packet.InventoryTransaction",
}
func PacketLogger(header packet.Header, payload []byte, src, dst net.Addr) {
var pk packet.Packet
buf := bytes.NewBuffer(payload)
r := protocol.NewReader(buf, 0)
pkFunc, ok := pool[header.PacketID]
if !ok {
pk = &packet.Unknown{PacketID: header.PacketID}
} else {
pk = pkFunc()
}
pk.Unmarshal(r)
dir := "S->C"
src_addr, _, _ := net.SplitHostPort(src.String())
if IPPrivate(net.ParseIP(src_addr)) {
dir = "C->S"
}
pk_name := reflect.TypeOf(pk).String()
if slices.Contains(muted_packets, pk_name) {
return
}
switch pk := pk.(type) {
case *packet.Disconnect:
logrus.Infof("Disconnect: %s", pk.Message)
}
logrus.Debugf("%s 0x%x, %s\n", dir, pk.ID(), pk_name)
}
func img2rgba(img *image.RGBA) []color.RGBA {
header := *(*reflect.SliceHeader)(unsafe.Pointer(&img.Pix))
header.Len /= 4
header.Cap /= 4
return *(*[]color.RGBA)(unsafe.Pointer(&header))
}
// LERP is a linear interpolation function
func LERP(p1, p2, alpha float64) float64 {
return (1-alpha)*p1 + alpha*p2
}
func blendColorValue(c1, c2, a uint8) uint8 {
return uint8(LERP(float64(c1), float64(c2), float64(a)/float64(0xff)))
}
func blendAlphaValue(a1, a2 uint8) uint8 {
return uint8(LERP(float64(a1), float64(0xff), float64(a2)/float64(0xff)))
}
func blendColors(c1, c2 color.RGBA) (ret color.RGBA) {
ret.R = blendColorValue(c1.R, c2.R, c2.A)
ret.G = blendColorValue(c1.G, c2.G, c2.A)
ret.B = blendColorValue(c1.B, c2.B, c2.A)
ret.A = blendAlphaValue(c1.A, c2.A)
return ret
}
func clamp(a, b int) int {
if a > b {
return b
}
if a < 0 {
return 0
}
return a
}