1
1
mirror of https://github.com/OpenDiablo2/OpenDiablo2 synced 2024-06-10 18:00:42 +00:00

refactored game bootstrap, removed d2config.Config singleton (#899)

* Remove d2config.Config singleton

* refactored config file bootstrap
* `d2loader.Loader` adds the config directories during init
* `d2asset.AssetManager` loads the config file during init
* mpq verification logic removed from d2config; this is done by d2loader
* added `errorMessage` to `d2app.App` for setting the error message for the error screen.

* fixed loader test
This commit is contained in:
gravestench 2020-11-03 12:54:15 +00:00 committed by GitHub
parent d6c9748fef
commit 5ac03d6f49
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 224 additions and 195 deletions

View File

@ -73,6 +73,8 @@ type App struct {
ui *d2ui.UIManager ui *d2ui.UIManager
tAllocSamples *ring.Ring tAllocSamples *ring.Ring
guiManager *d2gui.GuiManager guiManager *d2gui.GuiManager
config *d2config.Configuration
errorMessage error
*Options *Options
} }
@ -99,12 +101,20 @@ const (
// Create creates a new instance of the application // Create creates a new instance of the application
func Create(gitBranch, gitCommit string) *App { func Create(gitBranch, gitCommit string) *App {
assetManager, assetError := d2asset.NewAssetManager()
// we can throw away the error here because by this time it's already been loaded
config, _ := assetManager.LoadConfig()
return &App{ return &App{
gitBranch: gitBranch, gitBranch: gitBranch,
gitCommit: gitCommit, gitCommit: gitCommit,
asset: assetManager,
config: config,
Options: &Options{ Options: &Options{
Server: &d2networking.ServerOptions{}, Server: &d2networking.ServerOptions{},
}, },
errorMessage: assetError,
} }
} }
@ -113,20 +123,6 @@ func updateNOOP() error {
} }
func (a *App) startDedicatedServer() error { func (a *App) startDedicatedServer() error {
// hack, for now we need to create the asset manager here
// Attempt to load the configuration file
err := d2config.Load()
if err != nil {
return err
}
asset, err := d2asset.NewAssetManager(d2config.Config, *a.Options.LogLevel)
if err != nil {
return err
}
a.asset = asset
min, max := d2networking.ServerMinPlayers, d2networking.ServerMaxPlayersDefault min, max := d2networking.ServerMinPlayers, d2networking.ServerMaxPlayersDefault
maxPlayers := d2math.ClampInt(*a.Options.Server.MaxPlayers, min, max) maxPlayers := d2math.ClampInt(*a.Options.Server.MaxPlayers, min, max)
@ -154,35 +150,27 @@ func (a *App) startDedicatedServer() error {
} }
func (a *App) loadEngine() error { func (a *App) loadEngine() error {
// Attempt to load the configuration file
configError := d2config.Load()
// Create our renderer // Create our renderer
renderer, err := ebiten.CreateRenderer() renderer, err := ebiten.CreateRenderer(a.config)
if err != nil { if err != nil {
return err return err
} }
a.renderer = renderer a.renderer = renderer
// If we failed to load our config, lets show the boot panic screen if a.errorMessage != nil {
if configError != nil { return a.renderer.Run(a.updateInitError, updateNOOP, 800, 600, "OpenDiablo2")
return configError
} }
// if the log level was specified at the command line, use it // if the log level was specified at the command line, use it
logLevel := *a.Options.LogLevel logLevel := *a.Options.LogLevel
if logLevel == d2util.LogLevelUnspecified { if logLevel == d2util.LogLevelUnspecified {
logLevel = d2config.Config.LogLevel logLevel = a.config.LogLevel
} }
// Create the asset manager a.asset.SetLogLevel(logLevel)
asset, err := d2asset.NewAssetManager(d2config.Config, logLevel)
if err != nil {
return err
}
audio := ebiten2.CreateAudio(asset) audio := ebiten2.CreateAudio(a.asset)
inputManager := d2input.NewInputManager() inputManager := d2input.NewInputManager()
@ -191,21 +179,20 @@ func (a *App) loadEngine() error {
return err return err
} }
err = asset.BindTerminalCommands(term) err = a.asset.BindTerminalCommands(term)
if err != nil { if err != nil {
return err return err
} }
scriptEngine := d2script.CreateScriptEngine() scriptEngine := d2script.CreateScriptEngine()
uiManager := d2ui.NewUIManager(asset, renderer, inputManager, audio) uiManager := d2ui.NewUIManager(a.asset, renderer, inputManager, audio)
a.inputManager = inputManager a.inputManager = inputManager
a.terminal = term a.terminal = term
a.scriptEngine = scriptEngine a.scriptEngine = scriptEngine
a.audio = audio a.audio = audio
a.ui = uiManager a.ui = uiManager
a.asset = asset
a.tAllocSamples = createZeroedRing(nSamplesTAlloc) a.tAllocSamples = createZeroedRing(nSamplesTAlloc)
if a.gitBranch == "" { if a.gitBranch == "" {
@ -273,6 +260,13 @@ func (a *App) Run() error {
return fmt.Errorf(fmtVersion, a.gitBranch, a.gitCommit) return fmt.Errorf(fmtVersion, a.gitBranch, a.gitCommit)
} }
logLevel := *a.Options.LogLevel
if logLevel == d2util.LogLevelUnspecified {
logLevel = a.config.LogLevel
}
a.asset.SetLogLevel(logLevel)
// start profiler if argument was supplied // start profiler if argument was supplied
if len(*a.Options.profiler) > 0 { if len(*a.Options.profiler) > 0 {
profiler := enableProfiler(*a.Options.profiler) profiler := enableProfiler(*a.Options.profiler)
@ -296,7 +290,11 @@ func (a *App) Run() error {
windowTitle := fmt.Sprintf("OpenDiablo2 (%s)", a.gitBranch) windowTitle := fmt.Sprintf("OpenDiablo2 (%s)", a.gitBranch)
// If we fail to initialize, we will show the error screen // If we fail to initialize, we will show the error screen
if err := a.initialize(); err != nil { if err := a.initialize(); err != nil {
if gameErr := a.renderer.Run(updateInitError, updateNOOP, 800, 600, if a.errorMessage == nil {
a.errorMessage = err // if there was an error during init, don't clobber it
}
if gameErr := a.renderer.Run(a.updateInitError, updateNOOP, 800, 600,
windowTitle); gameErr != nil { windowTitle); gameErr != nil {
return gameErr return gameErr
} }
@ -352,8 +350,7 @@ func (a *App) initialize() error {
a.screen = d2screen.NewScreenManager(a.ui, a.guiManager) a.screen = d2screen.NewScreenManager(a.ui, a.guiManager)
config := d2config.Config a.audio.SetVolumes(a.config.BgmVolume, a.config.SfxVolume)
a.audio.SetVolumes(config.BgmVolume, config.SfxVolume)
if err := a.loadStrings(); err != nil { if err := a.loadStrings(); err != nil {
return err return err
@ -727,11 +724,10 @@ func enableProfiler(profileOption string) interface{ Stop() } {
return nil return nil
} }
func updateInitError(target d2interface.Surface) error { func (a *App) updateInitError(target d2interface.Surface) error {
target.Clear(colornames.Darkred) target.Clear(colornames.Darkred)
target.PushTranslation(errMsgPadding, errMsgPadding) target.PushTranslation(errMsgPadding, errMsgPadding)
target.DrawTextf(`Could not find the MPQ files in the directory: target.DrawTextf(a.errorMessage.Error())
%s\nPlease put the files and re-run the game.`, d2config.Config.MpqPath)
return nil return nil
} }

View File

@ -1,7 +1,6 @@
package d2loader package d2loader
import ( import (
"errors"
"fmt" "fmt"
"os" "os"
"path/filepath" "path/filepath"
@ -35,10 +34,8 @@ const (
) )
// NewLoader creates a new loader // NewLoader creates a new loader
func NewLoader(config *d2config.Configuration, l d2util.LogLevel) (*Loader, error) { func NewLoader(l d2util.LogLevel) (*Loader, error) {
loader := &Loader{ loader := &Loader{}
config: config,
}
loader.Cache = d2cache.CreateCache(defaultCacheBudget) loader.Cache = d2cache.CreateCache(defaultCacheBudget)
loader.Logger = d2util.NewLogger() loader.Logger = d2util.NewLogger()
@ -46,9 +43,9 @@ func NewLoader(config *d2config.Configuration, l d2util.LogLevel) (*Loader, erro
loader.Logger.SetPrefix(logPrefix) loader.Logger.SetPrefix(logPrefix)
loader.Logger.SetLevel(l) loader.Logger.SetLevel(l)
err := loader.initFromConfig() loader.bootstrap()
return loader, err return loader, nil
} }
// Loader represents the manager that handles loading and caching assets with the asset Sources // Loader represents the manager that handles loading and caching assets with the asset Sources
@ -60,35 +57,9 @@ type Loader struct {
Sources []asset.Source Sources []asset.Source
} }
const ( func (l *Loader) bootstrap() {
errConfigFileNotFound = "config file not found" _, _ = l.AddSource(filepath.Dir(d2config.LocalConfigPath()))
fmtErrSourceNotFound = `file not found: %s _, _ = l.AddSource(filepath.Dir(d2config.DefaultConfigPath()))
Please check your config file at %s
Also, verify that the MPQ files exist at %s
Capitalization matters!
`
)
func (l *Loader) initFromConfig() error {
if l.config == nil {
return errors.New(errConfigFileNotFound)
}
for _, mpqName := range l.config.MpqLoadOrder {
cleanDir := filepath.Clean(l.config.MpqPath)
srcPath := filepath.Join(cleanDir, mpqName)
_, err := l.AddSource(srcPath)
if err != nil {
// nolint:stylecheck // we want a multiline error message here..
return fmt.Errorf(fmtErrSourceNotFound, srcPath, l.config.Path(), l.config.MpqPath)
}
}
return nil
} }
// Load attempts to load an asset with the given sub-path. The sub-path is relative to the root // Load attempts to load an asset with the given sub-path. The sub-path is relative to the root
@ -125,7 +96,7 @@ func (l *Loader) Load(subPath string) (asset.Asset, error) {
continue continue
} }
srcBase := filepath.Base(source.Path()) srcBase, _ := filepath.Abs(source.Path())
l.Info(fmt.Sprintf("from %s, loading %s", srcBase, subPath)) l.Info(fmt.Sprintf("from %s, loading %s", srcBase, subPath))
return loadedAsset, l.Insert(subPath, loadedAsset, defaultCacheEntryWeight) return loadedAsset, l.Insert(subPath, loadedAsset, defaultCacheEntryWeight)

View File

@ -26,7 +26,7 @@ const (
) )
func TestLoader_NewLoader(t *testing.T) { func TestLoader_NewLoader(t *testing.T) {
loader, _ := NewLoader(nil, d2util.LogLevelDefault) loader, _ := NewLoader(d2util.LogLevelDefault)
if loader.Cache == nil { if loader.Cache == nil {
t.Error("loader should not be nil") t.Error("loader should not be nil")
@ -34,7 +34,7 @@ func TestLoader_NewLoader(t *testing.T) {
} }
func TestLoader_AddSource(t *testing.T) { func TestLoader_AddSource(t *testing.T) {
loader, _ := NewLoader(nil, d2util.LogLevelDefault) loader, _ := NewLoader(d2util.LogLevelDefault)
sourceA, errA := loader.AddSource(sourcePathA) sourceA, errA := loader.AddSource(sourcePathA)
sourceB, errB := loader.AddSource(sourcePathB) sourceB, errB := loader.AddSource(sourcePathB)
@ -85,9 +85,10 @@ func TestLoader_AddSource(t *testing.T) {
// nolint:gocyclo // this is just a test, not a big deal if we ignore linter here // nolint:gocyclo // this is just a test, not a big deal if we ignore linter here
func TestLoader_Load(t *testing.T) { func TestLoader_Load(t *testing.T) {
loader, _ := NewLoader(nil, d2util.LogLevelDefault) loader, _ := NewLoader(d2util.LogLevelDefault)
_, err := loader.AddSource(sourcePathB) // we expect files common to any source to come from here // we expect files common to any source to come from here
commonSource, err := loader.AddSource(sourcePathB)
if err != nil { if err != nil {
t.Fail() t.Fail()
log.Print(err) log.Print(err)
@ -123,7 +124,7 @@ func TestLoader_Load(t *testing.T) {
if entryCommon == nil || errCommon != nil { if entryCommon == nil || errCommon != nil {
t.Error("common entry should exist") t.Error("common entry should exist")
} else if entryCommon.Source() != loader.Sources[0] { } else if entryCommon.Source() != commonSource {
t.Error("common entry should come from the first loader source") t.Error("common entry should come from the first loader source")
} }

View File

@ -37,10 +37,10 @@ const colorEscapeReset = "\033[0m"
// Log format strings for log levels // Log format strings for log levels
const ( const (
fmtPrefix = "[%s]" fmtPrefix = "[%s]"
LogFmtDebug = "[DEBUG]" + colorEscapeReset + " %s" LogFmtDebug = "[DEBUG]" + colorEscapeReset + " %s\r\n"
LogFmtInfo = "[INFO]" + colorEscapeReset + " %s" LogFmtInfo = "[INFO]" + colorEscapeReset + " %s\r\n"
LogFmtWarning = "[WARNING]" + colorEscapeReset + " %s" LogFmtWarning = "[WARNING]" + colorEscapeReset + " %s\r\n"
LogFmtError = "[ERROR]" + colorEscapeReset + " %s" LogFmtError = "[ERROR]" + colorEscapeReset + " %s\r\n"
) )
// NewLogger creates a new logger with a default // NewLogger creates a new logger with a default
@ -73,6 +73,10 @@ func (l *Logger) SetPrefix(s string) {
// SetLevel sets the log level // SetLevel sets the log level
func (l *Logger) SetLevel(level LogLevel) { func (l *Logger) SetLevel(level LogLevel) {
if level == LogLevelUnspecified {
level = LogLevelDefault
}
l.level = level l.level = level
} }

View File

@ -1,8 +1,12 @@
package d2asset package d2asset
import ( import (
"encoding/json"
"fmt" "fmt"
"image/color" "image/color"
"path/filepath"
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2config"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2util" "github.com/OpenDiablo2/OpenDiablo2/d2common/d2util"
@ -38,8 +42,22 @@ const (
paletteTransformBudget = 64 paletteTransformBudget = 64
) )
const (
logPrefix = "Asset Manager"
fmtLoadAsset = "could not load file stream %s (%v)"
fmtLoadAnimation = "loading animation %s with palette %s, draw effect %d"
fmtLoadComposite = "loading composite: type %d, token %s, palette %s"
fmtLoadFont = "loading font: table %s, sprite %s, palette %s"
fmtLoadPalette = "loading palette %s"
fmtLoadStringTable = "loading string table: %s"
fmtLoadTransform = "loading palette transform: %s"
fmtLoadDict = "loading data dictionary: %s"
fmtLoadAnimData = "loading animation data from: %s"
)
// AssetManager loads files and game objects // AssetManager loads files and game objects
type AssetManager struct { type AssetManager struct {
config *d2config.Configuration
logger *d2util.Logger logger *d2util.Logger
loader *d2loader.Loader loader *d2loader.Loader
tables d2interface.Cache tables d2interface.Cache
@ -51,14 +69,98 @@ type AssetManager struct {
} }
func (am *AssetManager) init() error { func (am *AssetManager) init() error {
err := am.initDataDictionaries() var err error
config, err := am.LoadConfig()
if err != nil { if err != nil {
return err return err
} }
am.logger.SetLevel(config.LogLevel)
am.Records.Logger.SetLevel(config.LogLevel)
am.loader.Logger.SetLevel(config.LogLevel)
err = am.initConfig(config)
if err != nil {
return err
}
if err := am.initDataDictionaries(); err != nil {
return err
}
return nil return nil
} }
func (am *AssetManager) initConfig(config *d2config.Configuration) error {
am.config = config
for _, mpqName := range am.config.MpqLoadOrder {
cleanDir := filepath.Clean(am.config.MpqPath)
srcPath := filepath.Join(cleanDir, mpqName)
_, err := am.loader.AddSource(srcPath)
if err != nil {
// nolint:stylecheck // we want a multiline error message here..
return fmt.Errorf(fmtErrSourceNotFound, srcPath, am.config.Path(), am.config.MpqPath)
}
}
return nil
}
// SetLogLevel sets the log level for the asset manager, record manager, and file loader
func (am *AssetManager) SetLogLevel(level d2util.LogLevel) {
am.logger.SetLevel(level)
am.Records.Logger.SetLevel(level)
am.loader.Logger.SetLevel(level)
}
// LoadConfig loads the OpenDiablo2 config file
func (am *AssetManager) LoadConfig() (*d2config.Configuration, error) {
// by now the, the loader has initialized and added our config dirs as sources...
configBaseName := filepath.Base(d2config.DefaultConfigPath())
configAsset, _ := am.LoadAsset(configBaseName)
config := &d2config.Configuration{}
// create the default if not found
if configAsset == nil {
config = d2config.DefaultConfig()
fullPath := filepath.Join(config.Dir(), config.Base())
config.SetPath(fullPath)
am.logger.Infof("creating default configuration file at %s...", fullPath)
saveErr := config.Save()
return config, saveErr
}
if err := json.NewDecoder(configAsset).Decode(config); err != nil {
return nil, err
}
config.SetPath(filepath.Join(configAsset.Source().Path(), configAsset.Path()))
am.logger.Infof("loaded configuration file from %s", config.Path())
return config, nil
}
const (
fmtErrSourceNotFound = `file not found: %s
Please check your config file at %s
Also, verify that the MPQ files exist at %s
Capitalization matters!
`
)
func (am *AssetManager) initDataDictionaries() error { func (am *AssetManager) initDataDictionaries() error {
dictPaths := []string{ dictPaths := []string{
d2resource.LevelType, d2resource.LevelPreset, d2resource.LevelWarp, d2resource.LevelType, d2resource.LevelPreset, d2resource.LevelWarp,
@ -108,19 +210,6 @@ func (am *AssetManager) initDataDictionaries() error {
return nil return nil
} }
const (
logPrefix = "Asset Manager"
fmtLoadAsset = "could not load file stream %s (%v)"
fmtLoadAnimation = "loading animation %s with palette %s, draw effect %d"
fmtLoadComposite = "loading composite: type %d, token %s, palette %s"
fmtLoadFont = "loading font: table %s, sprite %s, palette %s"
fmtLoadPalette = "loading palette %s"
fmtLoadStringTable = "loading string table: %s"
fmtLoadTransform = "loading palette transform: %s"
fmtLoadDict = "loading data dictionary: %s"
fmtLoadAnimData = "loading animation data from: %s"
)
// LoadAsset loads an asset // LoadAsset loads an asset
func (am *AssetManager) LoadAsset(filePath string) (asset.Asset, error) { func (am *AssetManager) LoadAsset(filePath string) (asset.Asset, error) {
data, err := am.loader.Load(filePath) data, err := am.loader.Load(filePath)

View File

@ -4,40 +4,35 @@ import (
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2cache" "github.com/OpenDiablo2/OpenDiablo2/d2common/d2cache"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2loader" "github.com/OpenDiablo2/OpenDiablo2/d2common/d2loader"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2util" "github.com/OpenDiablo2/OpenDiablo2/d2common/d2util"
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2config"
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2records" "github.com/OpenDiablo2/OpenDiablo2/d2core/d2records"
) )
// NewAssetManager creates and assigns all necessary dependencies for the AssetManager top-level functions to work correctly // NewAssetManager creates and assigns all necessary dependencies for the AssetManager top-level functions to work correctly
func NewAssetManager(config *d2config.Configuration, l d2util.LogLevel) (*AssetManager, error) { func NewAssetManager() (*AssetManager, error) {
loader, err := d2loader.NewLoader(config, l) loader, err := d2loader.NewLoader(d2util.LogLevelDefault)
if err != nil { if err != nil {
return nil, err return nil, err
} }
records, err := d2records.NewRecordManager(l) records, err := d2records.NewRecordManager(d2util.LogLevelDebug)
if err != nil { if err != nil {
return nil, err return nil, err
} }
manager := &AssetManager{ manager := &AssetManager{
d2util.NewLogger(), logger: d2util.NewLogger(),
loader, loader: loader,
d2cache.CreateCache(tableBudget), tables: d2cache.CreateCache(tableBudget),
d2cache.CreateCache(animationBudget), animations: d2cache.CreateCache(animationBudget),
d2cache.CreateCache(fontBudget), fonts: d2cache.CreateCache(fontBudget),
d2cache.CreateCache(paletteBudget), palettes: d2cache.CreateCache(paletteBudget),
d2cache.CreateCache(paletteTransformBudget), transforms: d2cache.CreateCache(paletteTransformBudget),
records, Records: records,
} }
manager.logger.SetPrefix(logPrefix) manager.logger.SetPrefix(logPrefix)
manager.logger.SetLevel(l)
err = manager.init() err = manager.init()
if err != nil {
return nil, err
}
return manager, nil return manager, err
} }

View File

@ -120,7 +120,7 @@ func (eap *AudioProvider) createSoundEffect(sfx string, context *audio.Context,
loop bool) *SoundEffect { loop bool) *SoundEffect {
result := &SoundEffect{} result := &SoundEffect{}
soundFile := "/data/global/sfx/" soundFile := "data/global/sfx/"
if _, exists := eap.asset.Records.Sound.Details[sfx]; exists { if _, exists := eap.asset.Records.Sound.Details[sfx]; exists {
soundEntry := eap.asset.Records.Sound.Details[sfx] soundEntry := eap.asset.Records.Sound.Details[sfx]
@ -132,7 +132,7 @@ func (eap *AudioProvider) createSoundEffect(sfx string, context *audio.Context,
audioData, err := eap.asset.LoadFileStream(soundFile) audioData, err := eap.asset.LoadFileStream(soundFile)
if err != nil { if err != nil {
audioData, err = eap.asset.LoadFileStream("/data/global/music/" + sfx) audioData, err = eap.asset.LoadFileStream("data/global/music/" + sfx)
} }
if err != nil { if err != nil {

View File

@ -2,16 +2,13 @@ package d2config
import ( import (
"encoding/json" "encoding/json"
"log"
"os" "os"
"path" "path"
"path/filepath"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2util" "github.com/OpenDiablo2/OpenDiablo2/d2common/d2util"
) )
// 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 // Configuration defines the configuration for the engine, loaded from config.json
type Configuration struct { type Configuration struct {
MpqLoadOrder []string MpqLoadOrder []string
@ -29,72 +26,19 @@ type Configuration struct {
path string path string
} }
// Load loads a configuration object from disk
func Load() error {
Config = new(Configuration)
if Config.Load() != nil {
return Config.Save()
}
return nil
}
// Load loads a configuration object from disk
func (c *Configuration) Load() error {
configPaths := []string{
defaultConfigPath(),
localConfigPath(),
}
for _, configPath := range configPaths {
log.Printf("loading configuration file from %s...", configPath)
if _, err := os.Stat(configPath); os.IsNotExist(err) {
continue
}
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
}
c.path = configPath
return nil
}
log.Println("failed to load configuration file, saving default configuration...")
Config = defaultConfig()
return Config.Save()
}
// Save saves the configuration object to disk // Save saves the configuration object to disk
func (c *Configuration) Save() error { func (c *Configuration) Save() error {
configPath := defaultConfigPath() configDir := path.Dir(c.path)
log.Printf("saving configuration file to %s...", configPath)
configDir := path.Dir(configPath)
if err := os.MkdirAll(configDir, 0750); err != nil { if err := os.MkdirAll(configDir, 0750); err != nil {
return err return err
} }
configFile, err := os.Create(configPath) configFile, err := os.Create(c.path)
if err != nil { if err != nil {
return err return err
} }
buf, err := json.MarshalIndent(Config, "", " ") buf, err := json.MarshalIndent(c, "", " ")
if err != nil { if err != nil {
return err return err
} }
@ -106,23 +50,22 @@ func (c *Configuration) Save() error {
return configFile.Close() return configFile.Close()
} }
// Path returns the path of the config file // Dir returns the directory component of the path
func (c *Configuration) Path() string { func (c *Configuration) Dir() string {
if c.path == "" { return filepath.Dir(c.path)
c.path = defaultConfigPath() }
}
// Base returns the base component of the path
func (c *Configuration) Base() string {
return filepath.Base(c.path)
}
// Path returns the config file path
func (c *Configuration) Path() string {
return c.path return c.path
} }
func defaultConfigPath() string { // SetPath sets where the config file is saved to (a full path)
if configDir, err := os.UserConfigDir(); err == nil { func (c *Configuration) SetPath(p string) {
return path.Join(configDir, "OpenDiablo2", "config.json") c.path = p
}
return localConfigPath()
}
func localConfigPath() string {
return path.Join(path.Dir(os.Args[0]), "config.json")
} }

View File

@ -0,0 +1,28 @@
package d2config
import (
"os"
"path"
)
const (
od2ConfigDirName = "OpenDiablo2"
)
const (
od2ConfigFileName = "config.json"
)
// DefaultConfigPath returns the absolute path for the default config file location
func DefaultConfigPath() string {
if configDir, err := os.UserConfigDir(); err == nil {
return path.Join(configDir, od2ConfigDirName, od2ConfigFileName)
}
return LocalConfigPath()
}
// LocalConfigPath returns the absolute path to the directory of the OpenDiablo2 executable
func LocalConfigPath() string {
return path.Join(path.Dir(os.Args[0]), od2ConfigFileName)
}

View File

@ -8,7 +8,8 @@ import (
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2util" "github.com/OpenDiablo2/OpenDiablo2/d2common/d2util"
) )
func defaultConfig() *Configuration { // DefaultConfig creates and returns a default configuration
func DefaultConfig() *Configuration {
const ( const (
defaultSfxVolume = 1.0 defaultSfxVolume = 1.0
defaultBgmVolume = 0.3 defaultBgmVolume = 0.3
@ -38,6 +39,7 @@ func defaultConfig() *Configuration {
"d2speech.mpq", "d2speech.mpq",
}, },
LogLevel: d2util.LogLevelDefault, LogLevel: d2util.LogLevelDefault,
path: DefaultConfigPath(),
} }
switch runtime.GOOS { switch runtime.GOOS {

View File

@ -66,11 +66,11 @@ func (r *Renderer) Layout(_, _ int) (width, height int) {
} }
// CreateRenderer creates an ebiten renderer instance // CreateRenderer creates an ebiten renderer instance
func CreateRenderer() (*Renderer, error) { func CreateRenderer(cfg *d2config.Configuration) (*Renderer, error) {
result := &Renderer{} result := &Renderer{}
if d2config.Config != nil { if cfg != nil {
config := d2config.Config config := cfg
ebiten.SetCursorMode(ebiten.CursorModeHidden) ebiten.SetCursorMode(ebiten.CursorModeHidden)
ebiten.SetFullscreen(config.FullScreen) ebiten.SetFullscreen(config.FullScreen)