/
service.go
238 lines (192 loc) · 5.61 KB
/
service.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
package config
import (
"encoding/json"
"io/ioutil"
"sync"
"github.com/google/uuid"
"github.com/nokka/slashdiablo-launcher/clients/slashdiablo"
"github.com/nokka/slashdiablo-launcher/storage"
)
// Service is responsible for all things related to configuration.
type Service interface {
// Read will read the configuration and return it.
Read() (*storage.Config, error)
// AddGame adds a new game to the game model.
AddGame()
// UpsertGame updates or creates a new game to the persistent store.
UpsertGame(request UpdateGameRequest) error
// DeleteGame will delete a game from the game model and the persistent store.
DeleteGame(id string) error
// PersistGameModel will persist the current game model to the persistent store.
PersistGameModel() error
// UpdateLaunchDelay will update the launch delay for games in the persistent store.
UpdateLaunchDelay(delay int) error
// GetAvailableMods will fetch the game mode available to each D2 install.
GetAvailableMods() (*GameMods, error)
}
type service struct {
slashdiabloClient slashdiablo.Client
store storage.Store
gameModel *GameModel
mutex sync.Mutex
}
// Read will read the configuration and return it.
func (s *service) Read() (*storage.Config, error) {
conf, err := s.store.Read()
if err != nil {
return nil, err
}
return conf, err
}
// AddGame adds a new game to the game model.
func (s *service) AddGame() {
// Lock before we update the model preventing race conditions.
s.mutex.Lock()
// Unlock when we're done.
defer s.mutex.Unlock()
g := NewGame(nil)
// Generate an ID for the new game.
g.ID = uuid.New().String()
// Default values.
g.Instances = 1
g.Flags = []string{"-w", "-skiptobnet"}
g.HDVersion = ModVersionNone
g.MaphackVersion = ModVersionNone
s.gameModel.AddGame(g)
}
// UpdateGameRequest is the data used to update a game in the game model.
type UpdateGameRequest struct {
ID string `json:"id"`
Location string `json:"location"`
Instances int `json:"instances"`
OverrideBHCfg bool `json:"override_bh_cfg"`
Flags []string `json:"flags"`
HDVersion string `json:"hd_version"`
MaphackVersion string `json:"maphack_version"`
}
// UpsertGame will upsert the game to the config.
func (s *service) UpsertGame(request UpdateGameRequest) error {
// Lock before we update the model preventing race conditions.
s.mutex.Lock()
// Unlock when we're done.
defer s.mutex.Unlock()
// Updates game model with the new information.
var updatedIndex int
games := s.gameModel.Games()
for i := 0; i < len(games); i++ {
if games[i].ID == request.ID {
updatedIndex = i
games[i].Location = request.Location
games[i].Instances = request.Instances
games[i].OverrideBHCfg = request.OverrideBHCfg
games[i].Flags = request.Flags
games[i].HDVersion = request.HDVersion
games[i].MaphackVersion = request.MaphackVersion
}
}
// Notify the UI of the change.
s.gameModel.updateGame(updatedIndex)
return nil
}
// DeleteGame will delete the game from the config.
func (s *service) DeleteGame(id string) error {
// Lock before we update the model preventing race conditions.
s.mutex.Lock()
// Unlock when we're done.
defer s.mutex.Unlock()
// Read the config in order to update it.
conf, err := s.store.Read()
if err != nil {
return err
}
// Delete game from the config.
for i := 0; i < len(conf.Games); i++ {
if conf.Games[i].ID == id {
// Remove the index from the game slice.
conf.Games = append(conf.Games[:i], conf.Games[i+1:]...)
}
}
// Write the new games slice to the config.
err = s.store.Write(conf)
if err != nil {
return err
}
// Delete from the game model too.
games := s.gameModel.Games()
for i := 0; i < len(games); i++ {
if games[i].ID == id {
s.gameModel.removeGame(i)
}
}
return nil
}
// PersistGameModel will persist the current game model to the persistent store.
func (s *service) PersistGameModel() error {
conf, err := s.store.Read()
if err != nil {
return err
}
// Fetch the current game model.
games := s.gameModel.Games()
// Reset the games in the config.
conf.Games = make([]storage.Game, 0)
// Go through all games and populate a config slice.
for i := 0; i < len(games); i++ {
conf.Games = append(conf.Games, storage.Game{
ID: games[i].ID,
Location: games[i].Location,
Instances: games[i].Instances,
OverrideBHCfg: games[i].OverrideBHCfg,
Flags: games[i].Flags,
HDVersion: games[i].HDVersion,
MaphackVersion: games[i].MaphackVersion,
})
}
err = s.store.Write(conf)
if err != nil {
return err
}
return nil
}
// UpdateLaunchDelay will update the Diablo launch delay in the store.
func (s *service) UpdateLaunchDelay(delay int) error {
conf, err := s.store.Read()
if err != nil {
return err
}
// Update launch delay.
conf.LaunchDelay = delay
err = s.store.Write(conf)
if err != nil {
return err
}
return nil
}
// GetAvailableMods will get available mods from the Slashdiablo API.
func (s *service) GetAvailableMods() (*GameMods, error) {
contents, err := s.slashdiabloClient.GetAvailableMods()
if err != nil {
return nil, err
}
bytes, err := ioutil.ReadAll(contents)
if err != nil {
return nil, err
}
var gameMods GameMods
if err := json.Unmarshal(bytes, &gameMods); err != nil {
return nil, err
}
return &gameMods, nil
}
// NewService returns a service with all the dependencies.
func NewService(
slashdiabloClient slashdiablo.Client,
store storage.Store,
gameModel *GameModel,
) Service {
return &service{
slashdiabloClient: slashdiabloClient,
store: store,
gameModel: gameModel,
}
}