move to packages
This commit is contained in:
parent
05d1aa9267
commit
f05c505d08
|
@ -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
|
|
@ -7,12 +7,12 @@ token.json
|
|||
*.bmp
|
||||
*.bin
|
||||
|
||||
bedrocktool
|
||||
bedrocktool-*
|
||||
/bedrocktool
|
||||
/bedrocktool-*
|
||||
__debug_bin
|
||||
|
||||
.vscode/*
|
||||
keys.db
|
||||
skins/
|
||||
worlds/
|
||||
dist/
|
||||
/skins/
|
||||
/worlds/
|
||||
/dist/
|
||||
|
|
4
Makefile
4
Makefile
|
@ -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
|
|
@ -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{})
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
|
|
@ -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{})
|
||||
}
|
|
@ -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{})
|
||||
}
|
|
@ -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{})
|
||||
}
|
|
@ -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)
|
|
@ -1,4 +1,4 @@
|
|||
package main
|
||||
package world
|
||||
|
||||
import (
|
||||
"os"
|
|
@ -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)
|
|
@ -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)
|
||||
}
|
|
@ -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 {
|
|
@ -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()
|
||||
}
|
|
@ -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)
|
||||
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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 ""
|
||||
}
|
|
@ -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)
|
||||
}
|
|
@ -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
|
|
@ -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{})
|
||||
}
|
|
@ -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.
|
@ -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
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
}
|
9
magic.go
9
magic.go
|
@ -1,9 +0,0 @@
|
|||
package main
|
||||
|
||||
func magic_key() byte {
|
||||
return 0xd
|
||||
}
|
||||
|
||||
func magic() string {
|
||||
return "606c7f6668797d616c6e68236a62"
|
||||
}
|
|
@ -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
11
rp.go
|
@ -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
375
utils.go
|
@ -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
|
||||
}
|
Loading…
Reference in New Issue