mirror of
https://github.com/OpenDiablo2/OpenDiablo2
synced 2025-02-04 15:46:51 -05: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:
parent
d6c9748fef
commit
5ac03d6f49
70
d2app/app.go
70
d2app/app.go
@ -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
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
|
@ -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")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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)
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
@ -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 {
|
||||||
|
@ -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")
|
|
||||||
}
|
}
|
||||||
|
28
d2core/d2config/default_directories.go
Normal file
28
d2core/d2config/default_directories.go
Normal 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)
|
||||||
|
}
|
@ -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 {
|
||||||
|
@ -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)
|
||||||
|
Loading…
Reference in New Issue
Block a user