blob: 3e4d6cab12b3fa003f7a9ac47a4eae96dc3bd8ef [file] [log] [blame]
Sergiusz Bazanski0581bbf2020-05-11 03:21:32 +02001package main
2
3import (
4 "context"
5 "flag"
6 "fmt"
7 "io"
8 "io/ioutil"
9 "os"
10 "path/filepath"
11
12 "code.hackerspace.pl/hscloud/go/pki"
13 "github.com/gogo/protobuf/proto"
14 "github.com/golang/glog"
15 "google.golang.org/grpc"
16
17 "code.hackerspace.pl/hscloud/games/factorio/modproxy/modportal"
18 pb "code.hackerspace.pl/hscloud/games/factorio/modproxy/proto"
19)
20
21func init() {
22 flag.Set("logtostderr", "true")
23}
24
25var (
26 flagProxy string
27 flagFactorioPath string
28 flagConfigPath string
29)
30
31func main() {
32 flag.StringVar(&flagProxy, "proxy", "modproxy.factorio.svc.k0.hswaw.net:4200", "Address of modproxy service")
33 flag.StringVar(&flagFactorioPath, "factorio_path", "", "Path to factorio server root")
34 flag.StringVar(&flagConfigPath, "config_path", "config.pb.text", "Path to client config file")
35 flag.Parse()
36
37 conn, err := grpc.Dial(flagProxy, pki.WithClientHSPKI())
38 if err != nil {
39 glog.Exitf("Dial(%q): %v", flagProxy, err)
40 return
41 }
42
43 if flagFactorioPath == "" {
44 glog.Exitf("factorio_path must be set")
45 }
46
47 if flagConfigPath == "" {
48 glog.Exitf("config_path must be set")
49 }
50
51 configBytes, err := ioutil.ReadFile(flagConfigPath)
52 if err != nil {
53 glog.Exitf("could not read config: %v", err)
54 }
55 configString := string(configBytes)
56 config := &pb.ClientConfig{}
57 err = proto.UnmarshalText(configString, config)
58 if err != nil {
59 glog.Exitf("could not parse config: %v", err)
60 }
61
62 ctx := context.Background()
63 proxy := pb.NewModProxyClient(conn)
64
65 // mod name -> wanted mod version
66 managed := make(map[string]string)
67
68 for _, m := range config.Mod {
69 modPath := fmt.Sprintf("%s/mods/%s_%s.zip", flagFactorioPath, m.Name, m.Version)
70 _, err := os.Stat(modPath)
71 if err == nil {
72 glog.Infof("Mod %s/%s up to date, skipping.", m.Name, m.Version)
73 continue
74 }
75
76 i, err := modportal.GetMod(ctx, m.Name)
77 if err != nil {
78 glog.Errorf("Could not fetch info about %s/%s: %v", m.Name, m.Version, err)
79 continue
80 }
81
82 release := i.ReleaseByVersion(m.Version)
83 if release == nil {
84 glog.Errorf("%s/%s: version does not exist!", m.Name, m.Version)
85 continue
86 }
87
88 glog.Infof("Trying to download %s/%s (%s)...", m.Name, m.Version, release.SHA1)
89
90 err = downloadMod(ctx, proxy, m.Name, release.SHA1, modPath)
91 if err != nil {
92 glog.Errorf("%s/%s: could not download mod: %v", m.Name, m.Version, err)
93 } else {
94 glog.Infof("Mod %s/%s downloaded.", m.Name, m.Version)
95 managed[m.Name] = m.Version
96 }
97 }
98
99 glog.Infof("Cleaning up old versions of managed mods...")
100 for mn, mv := range managed {
101 modPath := fmt.Sprintf("%s/mods/%s_%s.zip", flagFactorioPath, mn, mv)
102 modGlob := fmt.Sprintf("%s/mods/%s_*.zip", flagFactorioPath, mn)
103 matches, err := filepath.Glob(modGlob)
104 if err != nil {
105 glog.Errorf("Could not find old versions of %q: %v", mn, err)
106 continue
107 }
108
109 for _, m := range matches {
110 // skip managed version
111 if m == modPath {
112 continue
113 }
114 glog.Infof("Deleting old version: %s", m)
115
116 err := os.Remove(m)
117 if err != nil {
118 glog.Errorf("Could not remove old version %q: %v", m, err)
119 }
120 }
121 }
122 glog.Infof("Done!")
123}
124
125func downloadMod(ctx context.Context, proxy pb.ModProxyClient, modName, sha1, dest string) error {
126 req := &pb.DownloadRequest{
127 ModName: modName,
128 FileSha1: sha1,
129 }
130
131 stream, err := proxy.Download(ctx, req)
132 if err != nil {
133 return err
134 }
135
136 data := []byte{}
137
138 status := pb.DownloadResponse_STATUS_INVALID
139 for {
140 res, err := stream.Recv()
141 if err == io.EOF {
142 break
143 }
144 if err != nil {
145 return err
146 }
147
148 if res.Status != pb.DownloadResponse_STATUS_INVALID {
149 status = res.Status
150 }
151
152 data = append(data, res.Chunk...)
153 }
154
155 switch status {
156 case pb.DownloadResponse_STATUS_OKAY:
157 case pb.DownloadResponse_STATUS_NOT_AVAILABLE:
158 return fmt.Errorf("version not available on proxy")
159 default:
160 return fmt.Errorf("invalid download status: %v", status)
161 }
162
163 return ioutil.WriteFile(dest, data, 0644)
164}