Skip to content

Commit 180b177

Browse files
committed
feat: skin manager
1 parent 6b64e9f commit 180b177

File tree

11 files changed

+697
-0
lines changed

11 files changed

+697
-0
lines changed

cmd/mc/root.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package mc
33
import (
44
"github.com/MakeNowJust/heredoc"
55
"github.com/mworzala/mc/cmd/mc/modrinth"
6+
"github.com/mworzala/mc/cmd/mc/skin"
67

78
"github.com/mworzala/mc/cmd/mc/profile"
89

@@ -36,6 +37,7 @@ func NewRootCmd(app *cli.App) *cobra.Command {
3637
cmd.AddCommand(account.NewAccountCmd(app))
3738
cmd.AddCommand(java.NewJavaCmd(app))
3839
cmd.AddCommand(profile.NewProfileCmd(app))
40+
cmd.AddCommand(skin.NewSkinCmd(app))
3941
cmd.AddCommand(newLaunchCmd(app))
4042
cmd.AddCommand(newInstallCmd(app))
4143
cmd.AddCommand(modrinth.NewModrinthCmd(app))

cmd/mc/skin/add.go

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
package skin
2+
3+
import (
4+
"errors"
5+
"fmt"
6+
"slices"
7+
8+
"github.com/google/uuid"
9+
"github.com/mworzala/mc/internal/pkg/cli"
10+
"github.com/spf13/cobra"
11+
)
12+
13+
type addSkinOpts struct {
14+
app *cli.App
15+
16+
account string
17+
variant string
18+
cape string
19+
name string
20+
apply bool
21+
}
22+
23+
var (
24+
ErrInvalidType = errors.New("invalid type")
25+
ErrInvalidVariant = errors.New("invalid variant")
26+
27+
validVariants = []string{"classic", "slim"}
28+
)
29+
30+
func newAddCmd(app *cli.App) *cobra.Command {
31+
var o addSkinOpts
32+
33+
cmd := &cobra.Command{
34+
Use: "add",
35+
Short: "Add a skin to your list",
36+
Args: func(cmd *cobra.Command, args []string) error {
37+
o.app = app
38+
return o.validateArgs(cmd, args)
39+
},
40+
RunE: func(_ *cobra.Command, args []string) error {
41+
o.app = app
42+
return o.execute(args)
43+
},
44+
}
45+
46+
cmd.Flags().StringVar(&o.account, "account", "", "Account to use")
47+
cmd.Flags().StringVar(&o.variant, "variant", "classic", "Skin variant [classic/slim]")
48+
cmd.Flags().StringVar(&o.cape, "cape", "", "Cape name, 'none' to remove")
49+
cmd.Flags().BoolVar(&o.apply, "apply", false, "Apply the skin")
50+
cmd.Flags().BoolVar(&o.apply, "set", false, "Apply the skin")
51+
52+
cmd.Flags().FlagUsages()
53+
54+
return cmd
55+
}
56+
57+
func (o *addSkinOpts) validateArgs(cmd *cobra.Command, args []string) (err error) {
58+
if err := cobra.MinimumNArgs(1)(cmd, args); err != nil {
59+
return err
60+
}
61+
62+
if !slices.Contains(validVariants, o.variant) {
63+
return ErrInvalidVariant
64+
}
65+
66+
return nil
67+
}
68+
69+
func (o *addSkinOpts) execute(args []string) error {
70+
if len(args) > 1 {
71+
o.name = args[1]
72+
}
73+
74+
if o.name == "" {
75+
o.name = uuid.New().String()
76+
}
77+
78+
if o.account == "" {
79+
o.account = o.app.AccountManager().GetDefault()
80+
}
81+
82+
token, err := o.app.AccountManager().GetAccountToken(o.account)
83+
if err != nil {
84+
return err
85+
}
86+
87+
info, err := o.app.SkinManager().GetProfileInformation(token)
88+
if err != nil {
89+
return err
90+
}
91+
92+
if o.cape == "" {
93+
for _, cape := range info.Capes {
94+
if cape.State == "ACTIVE" {
95+
o.cape = cape.ID
96+
}
97+
}
98+
} else if o.cape != "none" {
99+
for _, cape := range info.Capes {
100+
if cape.Alias == o.cape {
101+
o.cape = cape.ID
102+
}
103+
}
104+
}
105+
106+
skinData := args[0]
107+
108+
skin, err := o.app.SkinManager().CreateSkin(o.name, o.variant, skinData, o.cape)
109+
if err != nil {
110+
return err
111+
}
112+
113+
if o.apply {
114+
115+
err = skin.Apply(token)
116+
if err != nil {
117+
return err
118+
}
119+
}
120+
121+
fmt.Printf("skin %s with cape %s was added to the list", skin.Name, skin.Cape)
122+
123+
return o.app.SkinManager().Save()
124+
125+
}

cmd/mc/skin/apply.go

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
package skin
2+
3+
import (
4+
"github.com/mworzala/mc/internal/pkg/cli"
5+
"github.com/spf13/cobra"
6+
)
7+
8+
type applySkinOpts struct {
9+
app *cli.App
10+
11+
account string
12+
}
13+
14+
func newApplyCmd(app *cli.App) *cobra.Command {
15+
var o applySkinOpts
16+
17+
cmd := &cobra.Command{
18+
Use: "apply",
19+
Short: "Apply a saved skin",
20+
Aliases: []string{"set"},
21+
Args: func(cmd *cobra.Command, args []string) error {
22+
o.app = app
23+
return o.validateArgs(cmd, args)
24+
},
25+
RunE: func(_ *cobra.Command, args []string) error {
26+
o.app = app
27+
return o.execute(args)
28+
},
29+
}
30+
31+
cmd.Flags().StringVar(&o.account, "account", "", "Account to use")
32+
33+
return cmd
34+
}
35+
36+
func (o *applySkinOpts) validateArgs(cmd *cobra.Command, args []string) (err error) {
37+
if err := cobra.MinimumNArgs(1)(cmd, args); err != nil {
38+
return err
39+
}
40+
41+
return nil
42+
}
43+
44+
func (o *applySkinOpts) execute(args []string) error {
45+
skinName := args[0]
46+
47+
if o.account == "" {
48+
o.account = o.app.AccountManager().GetDefault()
49+
}
50+
51+
token, err := o.app.AccountManager().GetAccountToken(o.account)
52+
if err != nil {
53+
return err
54+
}
55+
56+
skin, err := o.app.SkinManager().GetSkin(skinName)
57+
if err != nil {
58+
return err
59+
}
60+
61+
return skin.Apply(token)
62+
}

cmd/mc/skin/list.go

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
package skin
2+
3+
import (
4+
"github.com/mworzala/mc/internal/pkg/cli"
5+
appModel "github.com/mworzala/mc/internal/pkg/cli/model"
6+
"github.com/spf13/cobra"
7+
)
8+
9+
type listSkinsOpts struct {
10+
app *cli.App
11+
}
12+
13+
func newListCmd(app *cli.App) *cobra.Command {
14+
var o listSkinsOpts
15+
16+
cmd := &cobra.Command{
17+
Use: "list",
18+
Short: "List saved skins",
19+
Args: cobra.NoArgs,
20+
RunE: func(_ *cobra.Command, args []string) error {
21+
o.app = app
22+
return o.listSkins()
23+
},
24+
}
25+
26+
return cmd
27+
}
28+
29+
func (o *listSkinsOpts) listSkins() error {
30+
skinManager := o.app.SkinManager()
31+
32+
var result appModel.SkinList
33+
for _, skin := range skinManager.Skins() {
34+
result = append(result, &appModel.Skin{
35+
Name: skin.Name,
36+
Date: skin.AddedDate,
37+
})
38+
}
39+
40+
return o.app.Present(result)
41+
}

cmd/mc/skin/skin.go

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package skin
2+
3+
import (
4+
"github.com/mworzala/mc/internal/pkg/cli"
5+
"github.com/spf13/cobra"
6+
)
7+
8+
func NewSkinCmd(app *cli.App) *cobra.Command {
9+
cmd := &cobra.Command{
10+
Use: "skin",
11+
Short: "Manage Minecraft Skins and Capes",
12+
}
13+
14+
cmd.AddCommand(newListCmd(app))
15+
cmd.AddCommand(newAddCmd(app))
16+
cmd.AddCommand(newApplyCmd(app))
17+
18+
return cmd
19+
}

go.mod

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ require (
66
github.com/MakeNowJust/heredoc v1.0.0
77
github.com/atotto/clipboard v0.1.4
88
github.com/gosuri/uitable v0.0.4
9+
github.com/google/uuid v1.6.0
910
github.com/mitchellh/mapstructure v1.5.0
1011
github.com/spf13/cobra v1.7.0
1112
github.com/spf13/viper v1.16.0

go.sum

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,8 @@ github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLe
128128
github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
129129
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
130130
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
131+
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
132+
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
131133
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
132134
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
133135
github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g=

internal/pkg/cli/app.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import (
1111
"github.com/mworzala/mc/internal/pkg/java"
1212
"github.com/mworzala/mc/internal/pkg/platform"
1313
"github.com/mworzala/mc/internal/pkg/profile"
14+
"github.com/mworzala/mc/internal/pkg/skin"
1415
"github.com/spf13/viper"
1516
)
1617

@@ -34,6 +35,7 @@ type App struct {
3435
versionManager *game.VersionManager
3536
profileManager profile.Manager
3637
gameManager game.Manager
38+
skinManager skin.Manager
3739
}
3840

3941
func NewApp(build BuildInfo) *App {
@@ -133,3 +135,15 @@ func (a *App) GameManager() game.Manager {
133135

134136
return a.gameManager
135137
}
138+
139+
func (a *App) SkinManager() skin.Manager {
140+
if a.skinManager == nil {
141+
var err error
142+
a.skinManager, err = skin.NewManager(a.ConfigDir)
143+
if err != nil {
144+
a.Fatal(err)
145+
}
146+
}
147+
148+
return a.skinManager
149+
}

internal/pkg/cli/model/skin.go

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
package model
2+
3+
import (
4+
"fmt"
5+
"time"
6+
7+
"github.com/gosuri/uitable"
8+
)
9+
10+
type Skin struct {
11+
Name string
12+
Date time.Time
13+
}
14+
15+
func (i *Skin) String() string {
16+
return fmt.Sprintf("%s\t%s", i.Name, i.Date)
17+
}
18+
19+
type SkinList []*Skin
20+
21+
func (l SkinList) String() string {
22+
table := uitable.New()
23+
table.AddRow("NAME", "DATE")
24+
for _, skin := range l {
25+
table.AddRow(skin.Name, skin.Date)
26+
}
27+
return table.String()
28+
}

0 commit comments

Comments
 (0)