bedrocktool/skins.go

223 lines
5.3 KiB
Go
Raw Normal View History

2022-03-05 11:59:36 +00:00
package main
import (
2022-03-05 12:59:11 +00:00
"bytes"
2022-03-05 11:59:36 +00:00
"context"
"encoding/json"
"flag"
"fmt"
"image"
"image/png"
2022-03-05 12:59:11 +00:00
"io"
2022-03-05 11:59:36 +00:00
"os"
2022-03-05 12:59:11 +00:00
"path"
2022-03-05 11:59:36 +00:00
"regexp"
"strings"
"github.com/google/subcommands"
2022-03-05 11:59:36 +00:00
"github.com/sandertv/gophertunnel/minecraft"
"github.com/sandertv/gophertunnel/minecraft/protocol"
"github.com/sandertv/gophertunnel/minecraft/protocol/packet"
)
type Skin struct {
protocol.Skin
2022-03-05 11:59:36 +00:00
}
func (skin *Skin) WriteGeometry(output_path string) error {
2022-03-05 12:59:11 +00:00
os.Mkdir(output_path, 0755)
f, err := os.Create(path.Join(output_path, "geometry.json"))
if err != nil {
return fmt.Errorf("failed to write Geometry %s: %s", output_path, err)
2022-03-05 12:59:11 +00:00
}
defer f.Close()
io.Copy(f, bytes.NewReader(skin.SkinGeometry))
return nil
2022-03-05 12:59:11 +00:00
}
func (skin *Skin) WriteCape(output_path string) error {
2022-03-05 12:59:11 +00:00
os.Mkdir(output_path, 0755)
f, err := os.Create(path.Join(output_path, "cape.png"))
if err != nil {
return fmt.Errorf("failed to write Cape %s: %s", output_path, err)
2022-03-05 12:59:11 +00:00
}
defer f.Close()
cape_tex := image.NewRGBA(image.Rect(0, 0, int(skin.CapeImageWidth), int(skin.CapeImageHeight)))
cape_tex.Pix = skin.CapeData
if err := png.Encode(f, cape_tex); err != nil {
return fmt.Errorf("error writing skin: %s", err)
2022-03-05 12:59:11 +00:00
}
return nil
2022-03-05 12:59:11 +00:00
}
func (skin *Skin) WriteAnimations(output_path string) error {
2022-03-05 12:59:11 +00:00
os.Mkdir(output_path, 0755)
2022-03-18 18:22:31 +00:00
return fmt.Errorf("%s has animations (unimplemented)", output_path)
2022-03-05 12:59:11 +00:00
}
func (skin *Skin) WriteTexture(output_path string) error {
f, err := os.Create(output_path + ".png")
if err != nil {
return fmt.Errorf("error writing Texture: %s", err)
2022-03-05 13:39:46 +00:00
}
defer f.Close()
skin_tex := image.NewRGBA(image.Rect(0, 0, int(skin.SkinImageWidth), int(skin.SkinImageHeight)))
skin_tex.Pix = skin.SkinData
if err := png.Encode(f, skin_tex); err != nil {
return fmt.Errorf("error writing Texture: %s", err)
}
return nil
}
func (skin *Skin) Write(output_path, name string) error {
2022-03-05 12:59:11 +00:00
complex := false
skin_dir := path.Join(output_path, name)
2022-03-05 11:59:36 +00:00
if len(skin.SkinGeometry) > 0 {
if err := skin.WriteGeometry(skin_dir); err != nil {
return err
}
2022-03-05 12:59:11 +00:00
complex = true
}
if len(skin.CapeData) > 0 {
if err := skin.WriteCape(skin_dir); err != nil {
return err
}
2022-03-05 12:59:11 +00:00
complex = true
}
if len(skin.Animations) > 0 {
if err := skin.WriteAnimations(skin_dir); err != nil {
return err
}
2022-03-05 12:59:11 +00:00
complex = true
}
var err error
2022-03-05 12:59:11 +00:00
if complex {
err = skin.WriteTexture(path.Join(skin_dir, "skin"))
2022-03-05 11:59:36 +00:00
} else {
err = skin.WriteTexture(skin_dir)
2022-03-05 11:59:36 +00:00
}
return err
}
var name_regexp = regexp.MustCompile(`§.`)
2022-03-05 13:39:46 +00:00
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
}
func write_skin(output_path, name string, skin protocol.Skin) {
2022-03-18 18:22:31 +00:00
if !strings.HasPrefix(name, skin_filter_player) {
return
}
fmt.Printf("Writing skin for %s\n", name)
_skin := &Skin{skin}
if err := _skin.Write(output_path, name); err != nil {
fmt.Fprintf(os.Stderr, "Error writing skin: %s\n", err)
}
}
2022-03-18 18:22:31 +00:00
var skin_filter_player string // who to filter
var skin_players = make(map[string]string)
var skin_player_counts = make(map[string]int)
2022-03-27 14:09:46 +00:00
var processed_skins = make(map[string]bool)
2022-03-18 18:22:31 +00:00
2022-03-27 14:09:46 +00:00
func process_packet_skins(conn *minecraft.Conn, out_path string, pk packet.Packet) {
2022-03-18 18:22:31 +00:00
switch _pk := pk.(type) {
case *packet.PlayerSkin:
name := skin_players[_pk.UUID.String()]
if name == "" {
name = _pk.UUID.String()
}
skin_player_counts[name]++
name = fmt.Sprintf("%s_%d", name, skin_player_counts[name])
write_skin(out_path, name, _pk.Skin)
case *packet.PlayerList:
if _pk.ActionType == 1 { // remove
return
}
for _, player := range _pk.Entries {
name := cleanup_name(player.Username)
if name == "" {
name = player.UUID.String()
}
2022-03-27 14:09:46 +00:00
if _, ok := processed_skins[name]; ok {
continue
}
2022-03-18 18:22:31 +00:00
write_skin(out_path, name, player.Skin)
skin_players[player.UUID.String()] = name
2022-03-27 14:09:46 +00:00
processed_skins[name] = true
if conn != nil {
send_popup(conn, fmt.Sprintf("%s Skin was Saved", name))
}
2022-03-18 18:22:31 +00:00
}
}
}
type SkinCMD struct {
server_address string
filter string
}
2022-07-29 16:12:06 +00:00
func (*SkinCMD) Name() string { return "skins" }
func (*SkinCMD) Synopsis() string { return "download all skins from players on a server" }
2022-07-29 16:12:06 +00:00
func (c *SkinCMD) SetFlags(f *flag.FlagSet) {
f.StringVar(&c.server_address, "address", "", "remote server address")
f.StringVar(&c.filter, "filter", "", "player name filter prefix")
}
func (c *SkinCMD) Usage() string {
return c.Name() + ": " + c.Synopsis() + "\n"
}
2022-03-05 11:59:36 +00:00
func (c *SkinCMD) Execute(ctx context.Context, f *flag.FlagSet, _ ...interface{}) subcommands.ExitStatus {
address, hostname, err := server_input(c.server_address)
2022-08-13 14:30:46 +00:00
if err != nil {
fmt.Fprint(os.Stderr, err)
return 1
2022-08-13 14:30:46 +00:00
}
serverConn, err := connect_server(ctx, address, nil)
2022-03-05 11:59:36 +00:00
if err != nil {
fmt.Fprint(os.Stderr, err)
return 1
2022-03-05 11:59:36 +00:00
}
2022-03-18 18:22:31 +00:00
defer serverConn.Close()
2022-03-05 11:59:36 +00:00
2022-04-16 11:02:32 +00:00
out_path := fmt.Sprintf("skins/%s", hostname)
2022-03-05 11:59:36 +00:00
if err := serverConn.DoSpawnContext(ctx); err != nil {
fmt.Fprint(os.Stderr, err)
return 1
2022-03-05 11:59:36 +00:00
}
println("Connected")
println("Press ctrl+c to exit")
os.MkdirAll(out_path, 0755)
for {
pk, err := serverConn.ReadPacket()
if err != nil {
return 1
}
2022-03-27 14:09:46 +00:00
process_packet_skins(nil, out_path, pk)
2022-03-05 11:59:36 +00:00
}
}
func init() {
register_command(&SkinCMD{})
}