Removed Configuration interface (#560)

This commit is contained in:
Intyre 2020-07-08 15:16:16 +02:00 committed by GitHub
parent f7e0912f5f
commit db5e844aac
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 190 additions and 361 deletions

View File

@ -17,8 +17,6 @@ import (
"strings"
"sync"
"golang.org/x/image/colornames"
"github.com/OpenDiablo2/OpenDiablo2/d2common"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2data"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2data/d2datadict"
@ -34,7 +32,7 @@ import (
"github.com/OpenDiablo2/OpenDiablo2/d2game/d2gamescreen"
"github.com/OpenDiablo2/OpenDiablo2/d2script"
"github.com/pkg/profile"
"golang.org/x/image/colornames"
"gopkg.in/alecthomas/kingpin.v2"
)
@ -47,7 +45,6 @@ type App struct {
captureState captureState
capturePath string
captureFrames []*image.RGBA
profileOption string
gitBranch string
gitCommit string
terminal d2interface.Terminal
@ -62,9 +59,12 @@ type bindTerminalEntry struct {
action interface{}
}
const defaultFPS = 0.04 // 1/25
const bytesToMegabyte = 1024 * 1024
const nSamplesTAlloc = 100
const (
defaultFPS = 0.04 // 1/25
bytesToMegabyte = 1024 * 1024
nSamplesTAlloc = 100
debugPopN = 6
)
// Create creates a new instance of the application
func Create(gitBranch, gitCommit string,
@ -84,7 +84,7 @@ func Create(gitBranch, gitCommit string,
}
// Run executes the application and kicks off the entire game process
func (p *App) Run() {
func (p *App) Run() error {
profileOption := kingpin.Flag("profile", "Profiles the program, one of (cpu, mem, block, goroutine, trace, thread, mutex)").String()
kingpin.Parse()
@ -99,12 +99,10 @@ func (p *App) Run() {
// If we fail to initialize, we will show the error screen
if err := p.initialize(); err != nil {
if gameErr := p.renderer.Run(updateInitError, 800, 600, windowTitle); gameErr != nil {
log.Fatal(gameErr)
return gameErr
}
log.Fatal(err)
return
return err
}
d2screen.SetNextScreen(d2gamescreen.CreateMainMenu(p.renderer, p.audio, p.terminal))
@ -116,8 +114,10 @@ func (p *App) Run() {
d2common.SetBuildInfo(p.gitBranch, p.gitCommit)
if err := p.renderer.Run(p.update, 800, 600, windowTitle); err != nil {
log.Panic(err)
return err
}
return nil
}
func (p *App) initialize() error {
@ -125,12 +125,8 @@ func (p *App) initialize() error {
p.lastTime = d2common.Now()
p.lastScreenAdvance = p.lastTime
if err := d2config.Load(); err != nil {
return err
}
config := d2config.Get()
d2resource.LanguageCode = config.Language()
config := d2config.Config
d2resource.LanguageCode = config.Language
p.renderer.SetWindowIcon("d2logo.png")
p.terminal.BindLogger()
@ -164,7 +160,7 @@ func (p *App) initialize() error {
return err
}
p.audio.SetVolumes(config.BgmVolume(), config.SfxVolume())
p.audio.SetVolumes(config.BgmVolume, config.SfxVolume)
if err := p.loadDataDict(); err != nil {
return err
@ -280,7 +276,7 @@ func (p *App) renderDebug(target d2interface.Surface) error {
target.DrawText("NumGC " + strconv.FormatInt(int64(m.NumGC), 10))
target.PushTranslation(0, 16)
target.DrawText("Coords " + strconv.FormatInt(int64(cx), 10) + "," + strconv.FormatInt(int64(cy), 10))
target.PopN(6) //nolint:gomnd This is the number of records we have popped
target.PopN(debugPopN)
return nil
}
@ -584,7 +580,7 @@ func updateInitError(target d2interface.Surface) error {
target.PushTranslation(width/5, height/2)
target.DrawText(`Could not find the MPQ files in the directory:
%s\nPlease put the files and re-run the game.`, d2config.Get().MpqPath())
%s\nPlease put the files and re-run the game.`, d2config.Config.MpqPath)
return nil
}

View File

@ -1,21 +0,0 @@
package d2interface
// Configuration saves, loads, and returns the OpenDiablo2
// configuration. This is either loaded from disk, or generated
// when one is not found.
type Configuration interface {
Load() error
Save() error
// Get() Configuration
MpqLoadOrder() []string
Language() string
MpqPath() string
TicksPerSecond() int
FpsCap() int
SfxVolume() float64
BgmVolume() float64
FullScreen() bool
RunInBackground() bool
VsyncEnabled() bool
Backend() string
}

View File

@ -5,15 +5,15 @@ import (
"path"
"sync"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface"
"github.com/OpenDiablo2/OpenDiablo2/d2common"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2fileformats/d2mpq"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface"
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2config"
)
type archiveManager struct {
cache d2interface.Cache
config d2interface.Configuration
config *d2config.Configuration
archives []d2interface.Archive
mutex sync.Mutex
}
@ -22,7 +22,7 @@ const (
archiveBudget = 1024 * 1024 * 512
)
func createArchiveManager(config d2interface.Configuration) d2interface.ArchiveManager {
func createArchiveManager(config *d2config.Configuration) d2interface.ArchiveManager {
return &archiveManager{cache: d2common.CreateCache(archiveBudget), config: config}
}
@ -85,14 +85,14 @@ func (am *archiveManager) LoadArchive(archivePath string) (d2interface.Archive,
// CacheArchiveEntries updates the archive entries
func (am *archiveManager) CacheArchiveEntries() error {
if len(am.archives) == len(am.config.MpqLoadOrder()) {
if len(am.archives) == len(am.config.MpqLoadOrder) {
return nil
}
am.archives = nil
for _, archiveName := range am.config.MpqLoadOrder() {
archivePath := path.Join(am.config.MpqPath(), archiveName)
for _, archiveName := range am.config.MpqLoadOrder {
archivePath := path.Join(am.config.MpqPath, archiveName)
archive, err := am.LoadArchive(archivePath)
if err != nil {

View File

@ -1,11 +1,12 @@
package d2asset
import (
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface"
"strings"
"github.com/OpenDiablo2/OpenDiablo2/d2common"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2resource"
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2config"
)
const (
@ -15,10 +16,10 @@ const (
type fileManager struct {
cache d2interface.Cache
archiveManager d2interface.ArchiveManager
config d2interface.Configuration
config *d2config.Configuration
}
func createFileManager(config d2interface.Configuration,
func createFileManager(config *d2config.Configuration,
archiveManager d2interface.ArchiveManager) d2interface.ArchivedFileManager {
return &fileManager{
d2common.CreateCache(fileBudget),
@ -90,7 +91,7 @@ func (fm *fileManager) removeLocaleTokens(filePath string) string {
tableToken := d2resource.LanguageTableToken
fontToken := d2resource.LanguageFontToken
filePath = strings.ReplaceAll(filePath, tableToken, fm.config.Language())
filePath = strings.ReplaceAll(filePath, tableToken, fm.config.Language)
// fixme: not all languages==latin
filePath = strings.ReplaceAll(filePath, fontToken, "latin")

View File

@ -1,22 +1,21 @@
package d2asset
import (
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2config"
"log"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface"
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2config"
)
var singleton *assetManager
var singleton *assetManager //nolint:gochecknoglobals // Currently global by design
// Initialize creates and assigns all necessary dependencies for the assetManager top-level functions to work correctly
func Initialize(renderer d2interface.Renderer,
term d2interface.Terminal) error {
var (
config = d2config.Get()
archiveManager = createArchiveManager(config)
archivedFileManager = createFileManager(config, archiveManager)
archiveManager = createArchiveManager(d2config.Config)
archivedFileManager = createFileManager(d2config.Config, archiveManager)
paletteManager = createPaletteManager()
paletteTransformManager = createPaletteTransformManager()
animationManager = createAnimationManager(renderer)

View File

@ -1,194 +0,0 @@
package d2config
import (
"encoding/json"
"io/ioutil"
"os"
"os/user"
"path"
"runtime"
)
const defaultSfxVolume = 1.0
const defaultBgmVolume = 0.3
func getDefaultConfig() *Configuration {
config := &Configuration{
language: "ENG",
fullScreen: false,
ticksPerSecond: -1,
runInBackground: true,
vsyncEnabled: true,
sfxVolume: defaultSfxVolume,
bgmVolume: defaultBgmVolume,
mpqPath: "C:/Program Files (x86)/Diablo II",
backend: "Ebiten",
mpqLoadOrder: []string{
"Patch_D2.mpq",
"d2exp.mpq",
"d2xmusic.mpq",
"d2xtalk.mpq",
"d2xvideo.mpq",
"d2data.mpq",
"d2char.mpq",
"d2music.mpq",
"d2sfx.mpq",
"d2video.mpq",
"d2speech.mpq",
},
}
switch runtime.GOOS {
case "windows":
if runtime.GOARCH == "386" {
config.mpqPath = "C:/Program Files/Diablo II"
}
case "darwin":
config.mpqPath = "/Applications/Diablo II/"
config.mpqLoadOrder = []string{
"Diablo II Patch",
"Diablo II Expansion Data",
"Diablo II Expansion Movies",
"Diablo II Expansion Music",
"Diablo II Expansion Speech",
"Diablo II Game Data",
"Diablo II Graphics",
"Diablo II Movies",
"Diablo II Music",
"Diablo II Sounds",
"Diablo II Speech",
}
case "linux":
if usr, err := user.Current(); err == nil {
config.mpqPath = path.Join(usr.HomeDir, ".wine/drive_c/Program Files (x86)/Diablo II")
}
}
return config
}
func getDefaultConfigPath() string {
if configDir, err := os.UserConfigDir(); err == nil {
return path.Join(configDir, "OpenDiablo2", "config.json")
}
return getLocalConfigPath()
}
func getLocalConfigPath() string {
return path.Join(path.Dir(os.Args[0]), "config.json")
}
func load(configPath string) error {
configFile, err := os.Open(configPath) //nolint:gosec will fix the security error later
if err != nil {
return err
}
data, err := ioutil.ReadAll(configFile)
if err != nil {
return err
}
if err := unmarshalIntoInterface(data); err != nil {
return err
}
err = configFile.Close()
if err != nil {
return err
}
return nil
}
func unmarshalIntoInterface(d []byte) error {
tmp := &hack{} // an empty concrete implementation
if err := json.Unmarshal(d, tmp); err != nil {
return err
}
tmp2cfg(tmp, singleton) // transfer tmp values to singleton
return nil
}
// TODO figure out a way to unmarshal into an interface
type hack struct{
MpqLoadOrder []string
Language string
MpqPath string
TicksPerSecond int
FpsCap int
SfxVolume float64
BgmVolume float64
FullScreen bool
RunInBackground bool
VsyncEnabled bool
Backend string
}
func cfg2tmp (a *Configuration, b *hack) {
b.MpqLoadOrder = a.mpqLoadOrder
b.Language = a.language
b.MpqPath = a.mpqPath
b.TicksPerSecond = a.ticksPerSecond
b.FpsCap = a.fpsCap
b.SfxVolume = a.sfxVolume
b.BgmVolume = a.bgmVolume
b.FullScreen = a.fullScreen
b.RunInBackground = a.runInBackground
b.VsyncEnabled = a.vsyncEnabled
b.Backend = a.backend
}
func tmp2cfg (b *hack, a *Configuration) {
a.mpqLoadOrder = b.MpqLoadOrder
a.language = b.Language
a.mpqPath = b.MpqPath
a.ticksPerSecond = b.TicksPerSecond
a.fpsCap = b.FpsCap
a.sfxVolume = b.SfxVolume
a.bgmVolume = b.BgmVolume
a.fullScreen = b.FullScreen
a.runInBackground = b.RunInBackground
a.vsyncEnabled = b.VsyncEnabled
a.backend = b.Backend
}
func save(configPath string) error {
configDir := path.Dir(configPath)
if err := os.MkdirAll(configDir, 0750); err != nil {
return err
}
configFile, err := os.Create(configPath)
if err != nil {
return err
}
tmp := &hack{}
cfg2tmp(singleton, tmp)
data, err := json.MarshalIndent(tmp, "", " ")
if err != nil {
return err
}
if _, writeErr := configFile.Write(data); writeErr != nil {
return writeErr
}
err = configFile.Close()
if err != nil {
return err
}
return nil
}

View File

@ -1,106 +1,108 @@
package d2config
import (
"encoding/json"
"log"
"os"
"path"
)
// Config holds the configuration from config.json
var Config *Configuration //nolint:gochecknoglobals // Currently global by design
// Configuration defines the configuration for the engine, loaded from config.json
type Configuration struct {
mpqLoadOrder []string
language string
mpqPath string
ticksPerSecond int
fpsCap int
sfxVolume float64
bgmVolume float64
fullScreen bool
runInBackground bool
vsyncEnabled bool
backend string
MpqLoadOrder []string
Language string
MpqPath string
TicksPerSecond int
FpsCap int
SfxVolume float64
BgmVolume float64
FullScreen bool
RunInBackground bool
VsyncEnabled bool
Backend string
}
func (c *Configuration) MpqLoadOrder() []string {
return c.mpqLoadOrder
}
func (c *Configuration) Language() string {
return c.language
}
func (c *Configuration) MpqPath() string {
return c.mpqPath
}
func (c *Configuration) TicksPerSecond() int {
return c.ticksPerSecond
}
func (c *Configuration) FpsCap() int {
return c.fpsCap
}
func (c *Configuration) SfxVolume() float64 {
return c.sfxVolume
}
func (c *Configuration) BgmVolume() float64 {
return c.bgmVolume
}
func (c *Configuration) FullScreen() bool {
return c.fullScreen
}
func (c *Configuration) RunInBackground() bool {
return c.runInBackground
}
func (c *Configuration) VsyncEnabled() bool {
return c.vsyncEnabled
}
func (c *Configuration) Backend() string {
return c.backend
// Load loads a configuration object from disk
func Load() error {
Config = new(Configuration)
return Config.Load()
}
// Load loads a configuration object from disk
func (c *Configuration) Load() error {
configPaths := []string{
getDefaultConfigPath(),
getLocalConfigPath(),
defaultConfigPath(),
localConfigPath(),
}
var loaded bool
for _, configPath := range configPaths {
log.Printf("loading configuration file from %s...", configPath)
if err := load(configPath); err == nil {
loaded = true
break
if _, err := os.Stat(configPath); os.IsNotExist(err) {
continue
}
}
if !loaded {
log.Println("failed to load configuration file, saving default configuration...")
if err := Save(); err != nil {
configFile, err := os.Open(path.Clean(configPath))
if err != nil {
return err
}
if err := json.NewDecoder(configFile).Decode(&Config); err != nil {
return err
}
if err := configFile.Close(); err != nil {
return err
}
return nil
}
return nil
log.Println("failed to load configuration file, saving default configuration...")
Config = defaultConfig()
return Config.Save()
}
// Save saves the configuration object to disk
func (c *Configuration) Save() error {
configPath := getDefaultConfigPath()
configPath := defaultConfigPath()
log.Printf("saving configuration file to %s...", configPath)
var err error
if err = save(configPath); err != nil {
log.Printf("failed to write configuration file (%s)", err)
configDir := path.Dir(configPath)
if err := os.MkdirAll(configDir, 0750); err != nil {
return err
}
return err
configFile, err := os.Create(configPath)
if err != nil {
return err
}
buf, err := json.MarshalIndent(Config, "", " ")
if err != nil {
return err
}
if _, err := configFile.Write(buf); err != nil {
return err
}
return configFile.Close()
}
func defaultConfigPath() string {
if configDir, err := os.UserConfigDir(); err == nil {
return path.Join(configDir, "OpenDiablo2", "config.json")
}
return localConfigPath()
}
func localConfigPath() string {
return path.Join(path.Dir(os.Args[0]), "config.json")
}

View File

@ -0,0 +1,67 @@
package d2config
import (
"os/user"
"path"
"runtime"
)
func defaultConfig() *Configuration {
const (
defaultSfxVolume = 1.0
defaultBgmVolume = 0.3
)
config := &Configuration{
Language: "ENG",
FullScreen: false,
TicksPerSecond: -1,
RunInBackground: true,
VsyncEnabled: true,
SfxVolume: defaultSfxVolume,
BgmVolume: defaultBgmVolume,
MpqPath: "C:/Program Files (x86)/Diablo II",
Backend: "Ebiten",
MpqLoadOrder: []string{
"Patch_D2.mpq",
"d2exp.mpq",
"d2xmusic.mpq",
"d2xtalk.mpq",
"d2xvideo.mpq",
"d2data.mpq",
"d2char.mpq",
"d2music.mpq",
"d2sfx.mpq",
"d2video.mpq",
"d2speech.mpq",
},
}
switch runtime.GOOS {
case "windows":
if runtime.GOARCH == "386" {
config.MpqPath = "C:/Program Files/Diablo II"
}
case "darwin":
config.MpqPath = "/Applications/Diablo II/"
config.MpqLoadOrder = []string{
"Diablo II Patch",
"Diablo II Expansion Data",
"Diablo II Expansion Movies",
"Diablo II Expansion Music",
"Diablo II Expansion Speech",
"Diablo II Game Data",
"Diablo II Graphics",
"Diablo II Movies",
"Diablo II Music",
"Diablo II Sounds",
"Diablo II Speech",
}
case "linux":
if usr, err := user.Current(); err == nil {
config.MpqPath = path.Join(usr.HomeDir, ".wine/drive_c/Program Files (x86)/Diablo II")
}
}
return config
}

View File

@ -1,22 +0,0 @@
package d2config
import "github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface"
// TODO remove this shit
var singleton = getDefaultConfig()
// Load loads a configuration object from disk
func Load() error {
return singleton.Load()
}
// Save saves the configuration object to disk
func Save() error {
return singleton.Save()
}
// Get returns a configuration object
func Get() d2interface.Configuration {
return singleton
}

View File

@ -1,15 +1,13 @@
package ebiten
import (
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum"
"image"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface"
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2config"
"github.com/hajimehoshi/ebiten"
"github.com/hajimehoshi/ebiten/ebitenutil"
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2config"
)
type Renderer struct {
@ -31,13 +29,12 @@ func (r *Renderer) Layout(outsideWidth, outsideHeight int) (screenWidth, screenH
func CreateRenderer() (*Renderer, error) {
result := &Renderer{}
config := d2config.Get()
config := d2config.Config
ebiten.SetCursorMode(ebiten.CursorModeHidden)
ebiten.SetFullscreen(config.FullScreen())
ebiten.SetRunnableOnUnfocused(config.RunInBackground())
ebiten.SetVsyncEnabled(config.VsyncEnabled())
ebiten.SetMaxTPS(config.TicksPerSecond())
ebiten.SetFullscreen(config.FullScreen)
ebiten.SetRunnableOnUnfocused(config.RunInBackground)
ebiten.SetVsyncEnabled(config.VsyncEnabled)
ebiten.SetMaxTPS(config.TicksPerSecond)
return result, nil
}

14
main.go
View File

@ -3,13 +3,11 @@ package main
import (
"log"
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2render/ebiten"
"github.com/OpenDiablo2/OpenDiablo2/d2app"
ebiten2 "github.com/OpenDiablo2/OpenDiablo2/d2core/d2audio/ebiten"
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2config"
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2input"
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2render/ebiten"
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2term"
)
@ -25,6 +23,10 @@ func main() {
log.SetFlags(log.Lshortfile)
log.Println("OpenDiablo2 - Open source Diablo 2 engine")
if err := d2config.Load(); err != nil {
panic(err)
}
// Initialize our providers
renderer, err := ebiten.CreateRenderer()
if err != nil {
@ -44,5 +46,7 @@ func main() {
}
app := d2app.Create(GitBranch, GitCommit, term, audio, renderer)
app.Run()
if err := app.Run(); err != nil {
log.Fatal(err)
}
}