1
1
mirror of https://github.com/OpenDiablo2/OpenDiablo2 synced 2025-02-03 23:26:41 -05:00

adding d2util.Loggers instances to existing systems

This commit is contained in:
gravestench 2020-11-14 09:52:07 -08:00
parent e2bd1d8c71
commit bdf3a2e75d
12 changed files with 307 additions and 176 deletions

View File

@ -1,6 +1,7 @@
package d2systems
import (
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2util"
"io"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface"
@ -29,14 +30,18 @@ const (
assetCacheEntryWeight = 1 // may want to make different weights for different asset types
)
const (
LogPrefixAssetLoader = "Asset Loader System"
)
// NewAssetLoader creates a new asset loader instance
func NewAssetLoader() *AssetLoaderSystem {
// we are going to check entities that dont yet have loaded asset types
filesToLoad := akara.NewFilter().
Require(d2components.FilePath).
Require(d2components.FilePath). // we want to process entities with these file components
Require(d2components.FileType).
Require(d2components.FileHandle).
Forbid(d2components.FileSource).
Forbid(d2components.FileSource). // but we forbid files that are already loaded
Forbid(d2components.GameConfig).
Forbid(d2components.StringTable).
Forbid(d2components.DataDictionary).
@ -55,16 +60,22 @@ func NewAssetLoader() *AssetLoaderSystem {
Require(d2components.FileSource).
Build()
return &AssetLoaderSystem{
assetLoader := &AssetLoaderSystem{
SubscriberSystem: akara.NewSubscriberSystem(filesToLoad, fileSources),
cache: d2cache.CreateCache(assetCacheBudget).(*d2cache.Cache),
Logger: d2util.NewLogger(),
}
assetLoader.SetPrefix(LogPrefixAssetLoader)
return assetLoader
}
var _ akara.System = &AssetLoaderSystem{}
type AssetLoaderSystem struct {
*akara.SubscriberSystem
*d2util.Logger
fileSub *akara.Subscription
sourceSub *akara.Subscription
cache *d2cache.Cache
@ -95,6 +106,8 @@ func (m *AssetLoaderSystem) Init(world *akara.World) {
return
}
m.Info("initializing ...")
for subIdx := range m.Subscriptions {
m.Subscriptions[subIdx] = m.AddSubscription(m.Subscriptions[subIdx].Filter)
}
@ -130,29 +143,37 @@ func (m *AssetLoaderSystem) Process() {
}
func (m *AssetLoaderSystem) loadAsset(id akara.EID) {
// make sure everything is kosher
fp, found := m.filePaths.GetFilePath(id)
if !found {
m.Errorf("filepath component not found for entity %d", id)
return
}
ft, found := m.fileTypes.GetFileType(id)
if !found {
m.Errorf("filetype component not found for entity %d", id)
return
}
fh, found := m.fileHandles.GetFileHandle(id)
if !found {
m.Errorf("filehandle component not found for entity %d", id)
return
}
if found := m.pullFromCache(id, fp.Path, ft.Type); found {
// try to pull from the cache and assign to the given entity id
if found := m.assignFromCache(id, fp.Path, ft.Type); found {
m.Debugf("Retrieving %s from cache", fp.Path)
return
}
// make sure to seek back to 0 if the filehandle was cached
_, _ = fh.Data.Seek(0, 0)
data, buf := make([]byte, 0), make([]byte, 16)
// read, parse, and cache the data
for {
numRead, err := fh.Data.Read(buf)
data = append(data, buf[:numRead]...)
@ -165,12 +186,13 @@ func (m *AssetLoaderSystem) loadAsset(id akara.EID) {
m.parseAndCache(id, fp.Path, ft.Type, data)
}
func (m *AssetLoaderSystem) pullFromCache(id akara.EID, path string, t d2enum.FileType) bool {
func (m *AssetLoaderSystem) assignFromCache(id akara.EID, path string, t d2enum.FileType) bool {
entry, found := m.cache.Retrieve(path)
if !found {
return found
}
// if we found what we're looking for, create the appropriate component and assign what we retrieved
switch t {
case d2enum.FileTypeStringTable:
m.stringTables.AddStringTable(id).TextDictionary = entry.(*d2tbl.TextDictionary)

View File

@ -1,6 +1,7 @@
package d2systems
import (
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2util"
"strings"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface"
@ -24,6 +25,10 @@ const (
fileHandleCacheEntryWeight = 1
)
const (
logPrefixFileHandleResolver = "File Handle Resolver"
)
func NewFileHandleResolver() *FileHandleResolutionSystem {
// this filter is for entities that have a file path and file type but no file handle.
filesToSource := akara.NewFilter().
@ -37,18 +42,20 @@ func NewFileHandleResolver() *FileHandleResolutionSystem {
RequireOne(d2components.FileSource).
Build()
configsToUse := akara.NewFilter().
Require(d2components.GameConfig).
Build()
return &FileHandleResolutionSystem{
SubscriberSystem: akara.NewSubscriberSystem(filesToSource, sourcesToUse, configsToUse),
fhr := &FileHandleResolutionSystem{
SubscriberSystem: akara.NewSubscriberSystem(filesToSource, sourcesToUse),
cache: d2cache.CreateCache(fileHandleCacheBudget).(*d2cache.Cache),
Logger: d2util.NewLogger(),
}
fhr.SetPrefix(logPrefixFileHandleResolver)
return fhr
}
type FileHandleResolutionSystem struct {
*akara.SubscriberSystem
*d2util.Logger
cache *d2cache.Cache
filesToLoad *akara.Subscription
sourcesToUse *akara.Subscription
@ -62,15 +69,17 @@ type FileHandleResolutionSystem struct {
func (m *FileHandleResolutionSystem) Init(world *akara.World) {
m.World = world
for subIdx := range m.Subscriptions {
m.Subscriptions[subIdx] = m.AddSubscription(m.Subscriptions[subIdx].Filter)
}
if world == nil {
m.SetActive(false)
return
}
for subIdx := range m.Subscriptions {
m.Subscriptions[subIdx] = m.AddSubscription(m.Subscriptions[subIdx].Filter)
}
m.Info("initializing ...")
m.filesToLoad = m.Subscriptions[0]
m.sourcesToUse = m.Subscriptions[1]
@ -169,7 +178,7 @@ func (m *FileHandleResolutionSystem) loadFileWithSource(fileID, sourceID akara.E
}
}
//fmt.Printf("%s -> %s\n", sourceFp.Path, fp.Path)
m.Infof("resolved `%s` with source `%s`", fp.Path, sourceFp.Path)
component := m.fileHandles.AddFileHandle(fileID)
component.Data = data

View File

@ -1,6 +1,7 @@
package d2systems
import (
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2util"
"os"
"path/filepath"
"strings"
@ -13,6 +14,10 @@ import (
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2components"
)
const (
logPrefixFileSourceResolver = "File Source Resolver"
)
func NewFileSourceResolver() *FileSourceResolver {
// subscribe to entities with a file type and file path, but no file source type
filesToCheck := akara.NewFilter().
@ -21,13 +26,19 @@ func NewFileSourceResolver() *FileSourceResolver {
Forbid(d2components.FileSource).
Build()
return &FileSourceResolver{
fsr := &FileSourceResolver{
SubscriberSystem: akara.NewSubscriberSystem(filesToCheck),
Logger: d2util.NewLogger(),
}
fsr.SetPrefix(logPrefixFileSourceResolver)
return fsr
}
type FileSourceResolver struct {
*akara.SubscriberSystem
*d2util.Logger
fileSub *akara.Subscription
filePaths *d2components.FilePathMap
fileTypes *d2components.FileTypeMap
@ -43,6 +54,8 @@ func (m *FileSourceResolver) Init(world *akara.World) {
return
}
m.Info("initializing ...")
for subIdx := range m.Subscriptions {
m.Subscriptions[subIdx] = m.AddSubscription(m.Subscriptions[subIdx].Filter)
}
@ -60,13 +73,12 @@ func (m *FileSourceResolver) Init(world *akara.World) {
func (m *FileSourceResolver) Process() {
for subIdx := range m.Subscriptions {
for _, sourceEntityID := range m.Subscriptions[subIdx].GetEntities() {
m.ProcessEntity(sourceEntityID)
m.processSourceEntity(sourceEntityID)
}
}
}
// ProcessEntity updates an individual entity in the system
func (m *FileSourceResolver) ProcessEntity(id akara.EID) {
func (m *FileSourceResolver) processSourceEntity(id akara.EID) {
fp, found := m.filePaths.GetFilePath(id)
if !found {
return
@ -79,9 +91,9 @@ func (m *FileSourceResolver) ProcessEntity(id akara.EID) {
switch ft.Type {
case d2enum.FileTypeUnknown:
m.Errorf("unknown file type for file `%s`", fp.Path)
return
case d2enum.FileTypeMPQ:
source := m.fileSources.AddFileSource(id)
instance, err := m.makeMpqSource(fp.Path)
if err != nil {
@ -89,9 +101,11 @@ func (m *FileSourceResolver) ProcessEntity(id akara.EID) {
break
}
source := m.fileSources.AddFileSource(id)
m.Infof("creating MPQ source for `%s`", fp.Path)
source.AbstractSource = instance
case d2enum.FileTypeDirectory:
source := m.fileSources.AddFileSource(id)
instance, err := m.makeFileSystemSource(fp.Path)
if err != nil {
@ -99,6 +113,9 @@ func (m *FileSourceResolver) ProcessEntity(id akara.EID) {
break
}
source := m.fileSources.AddFileSource(id)
m.Infof("creating FILESYSTEM source for `%s`", fp.Path)
source.AbstractSource = instance
}
}
@ -125,6 +142,10 @@ func (s *fsSource) fullPath(path string) string {
return filepath.Clean(filepath.Join(s.rootDir, path))
}
func (s *fsSource) Path() string {
return filepath.Clean(s.rootDir)
}
// mpq source
func (m *FileSourceResolver) makeMpqSource(path string) (d2components.AbstractSource, error) {
mpq, err := d2mpq.Load(path)
@ -157,3 +178,7 @@ func (s *mpqSource) cleanMpqPath(path string) string {
return path
}
func (s *mpqSource) Path() string {
return filepath.Clean(s.mpq.Path())
}

View File

@ -1,6 +1,7 @@
package d2systems
import (
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2util"
"os"
"path/filepath"
"strings"
@ -12,6 +13,10 @@ import (
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2components"
)
const (
logPrefixFileTypeResolver = "File Type Resolver"
)
// NewFileTypeResolver creates a new file type resolution system.
func NewFileTypeResolver() *FileTypeResolver {
// we subscribe only to entities that have a filepath
@ -21,9 +26,14 @@ func NewFileTypeResolver() *FileTypeResolver {
Forbid(d2components.FileType).
Build()
return &FileTypeResolver{
ftr := &FileTypeResolver{
SubscriberSystem: akara.NewSubscriberSystem(filesToCheck),
Logger: d2util.NewLogger(),
}
ftr.SetPrefix(logPrefixFileTypeResolver)
return ftr
}
// static check that FileTypeResolver implements the System interface
@ -36,6 +46,7 @@ var _ akara.System = &FileTypeResolver{}
// from its subscription.
type FileTypeResolver struct {
*akara.SubscriberSystem
*d2util.Logger
filesToCheck *akara.Subscription
filePaths *d2components.FilePathMap
fileTypes *d2components.FileTypeMap
@ -50,6 +61,8 @@ func (m *FileTypeResolver) Init(world *akara.World) {
return
}
m.Info("initializing ...")
for subIdx := range m.Subscriptions {
m.Subscriptions[subIdx] = m.AddSubscription(m.Subscriptions[subIdx].Filter)
}

View File

@ -1,11 +1,11 @@
package d2systems
import (
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2util"
"github.com/gravestench/akara"
"os"
"path"
"github.com/gravestench/akara"
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2components"
)
@ -14,82 +14,89 @@ const (
configFileName = "config.json"
)
const (
LoggerPrefixBootstrap = "Bootstrap System"
)
// static check that the game config system implements the system interface
var _ akara.System = &GameBootstrapSystem{}
func NewGameBootstrapSystem() *GameBootstrapSystem {
// we are going to check entities that dont yet have loaded asset types
thingsToCheck := akara.NewFilter().
Require(d2components.FilePath).
Require(d2components.FileType).
Require(d2components.FileHandle).
Forbid(d2components.GameConfig).
Forbid(d2components.StringTable).
Forbid(d2components.DataDictionary).
Forbid(d2components.Palette).
Forbid(d2components.PaletteTransform).
Forbid(d2components.Cof).
Forbid(d2components.Dc6).
Forbid(d2components.Dcc).
Forbid(d2components.Ds1).
Forbid(d2components.Dt1).
Forbid(d2components.Wav).
Forbid(d2components.AnimData).
filesToCheck := akara.NewFilter().
Require( // files that need to be loaded
d2components.FileType,
d2components.FileHandle,
d2components.FilePath,
).
Forbid( // files which have been loaded
d2components.GameConfig,
d2components.StringTable,
d2components.DataDictionary,
d2components.Palette,
d2components.PaletteTransform,
d2components.Cof,
d2components.Dc6,
d2components.Dcc,
d2components.Ds1,
d2components.Dt1,
d2components.Wav,
d2components.AnimData,
).
Build()
// we are interested in actual game config instances, too
gameConfigs := akara.NewFilter().Require(d2components.GameConfig).Build()
gcs := &GameBootstrapSystem{
SubscriberSystem: akara.NewSubscriberSystem(thingsToCheck, gameConfigs),
maps: struct {
gameConfigs *d2components.GameConfigMap
filePaths *d2components.FilePathMap
fileTypes *d2components.FileTypeMap
fileHandles *d2components.FileHandleMap
fileSources *d2components.FileSourceMap
}{},
bootstrapSys := &GameBootstrapSystem{
SubscriberSystem: akara.NewSubscriberSystem(filesToCheck, gameConfigs),
Logger: d2util.NewLogger(),
}
return gcs
bootstrapSys.SetPrefix(LoggerPrefixBootstrap)
bootstrapSys.Debug("Created")
return bootstrapSys
}
// GameBootstrapSystem is responsible for setting up the regular diablo2 game launch
type GameBootstrapSystem struct {
*akara.SubscriberSystem
filesToCheck *akara.Subscription
gameConfigs *akara.Subscription
maps struct {
gameConfigs *d2components.GameConfigMap
filePaths *d2components.FilePathMap
fileTypes *d2components.FileTypeMap
fileHandles *d2components.FileHandleMap
fileSources *d2components.FileSourceMap
}
*d2util.Logger
subscribedFiles *akara.Subscription
subscribedConfigs *akara.Subscription
*d2components.GameConfigMap
*d2components.FilePathMap
*d2components.FileTypeMap
*d2components.FileHandleMap
*d2components.FileSourceMap
}
func (m *GameBootstrapSystem) Init(world *akara.World) {
m.World = world
if world == nil {
m.Error("world is nil, deactivating.")
m.SetActive(false)
return
}
m.Info("initializing ...")
for subIdx := range m.Subscriptions {
m.Subscriptions[subIdx] = m.AddSubscription(m.Subscriptions[subIdx].Filter)
}
m.filesToCheck = m.Subscriptions[0]
m.gameConfigs = m.Subscriptions[1]
m.subscribedFiles = m.Subscriptions[0]
m.subscribedConfigs = m.Subscriptions[1]
// try to inject the components we require, then cast the returned
// abstract ComponentMap back to the concrete implementation
m.maps.filePaths = world.InjectMap(d2components.FilePath).(*d2components.FilePathMap)
m.maps.fileTypes = world.InjectMap(d2components.FileType).(*d2components.FileTypeMap)
m.maps.fileHandles = world.InjectMap(d2components.FileHandle).(*d2components.FileHandleMap)
m.maps.fileSources = world.InjectMap(d2components.FileSource).(*d2components.FileSourceMap)
m.maps.gameConfigs = world.InjectMap(d2components.GameConfig).(*d2components.GameConfigMap)
m.GameConfigMap = world.InjectMap(d2components.GameConfig).(*d2components.GameConfigMap)
m.FilePathMap = world.InjectMap(d2components.FilePath).(*d2components.FilePathMap)
m.FileTypeMap = world.InjectMap(d2components.FileType).(*d2components.FileTypeMap)
m.FileHandleMap = world.InjectMap(d2components.FileHandle).(*d2components.FileHandleMap)
m.FileSourceMap = world.InjectMap(d2components.FileSource).(*d2components.FileSourceMap)
m.bootstrap()
}
@ -100,41 +107,58 @@ func (m *GameBootstrapSystem) bootstrap() {
// we make two entities and assign file paths for the two directories that
// we assume a config file may be inside of. These will be processed in the future by
// the file type resolver system, and then the file source resolver system. At that point,
// there will be sources for these two directories that can resolve the config file.
// there will be sources for these two directories that can possibly resolve a config file.
// A new config file is created if one is not found.
// make the two entities, these will be the file sources
e1, e2 := m.NewEntity(), m.NewEntity()
fp1, fp2 := m.maps.filePaths.AddFilePath(e1), m.maps.filePaths.AddFilePath(e2)
// the od2 directory has the highest priority
fp1.Path = path.Dir(os.Args[0])
// add file path components to these entities
fp1, fp2 := m.AddFilePath(e1), m.AddFilePath(e2)
// the user config directory is second highest
configDir, err := os.UserConfigDir()
// the first entity gets a filepath for the od2 directory, this one is checked first
// eg. if OD2 binary is in `~/src/OpenDiablo2/`, then this directory is checked first for a config file
cfgPath1 := path.Dir(os.Args[0])
fp1.Path = cfgPath1
m.Infof("setting up local directory %s for processing", cfgPath1)
// now add the user config directory
// this directory on a linux machine would be something like `~/.config/OpenDiablo2/`
cfgPath2, err := os.UserConfigDir()
if err == nil {
fp2.Path = path.Join(configDir, configDirectoryName)
fp2.Path = path.Join(cfgPath2, configDirectoryName)
m.Infof("setting up user config directory %s for processing", fp2.Path)
} else {
// we couldn't find the directory
// we couldn't find the directory, destroy this entity
m.Error("user config directory not found, skipping")
m.RemoveEntity(e2)
}
// now we set up the config file to be loaded. this happens after the directories
// above are recognized as file sources.
e3 := m.NewEntity()
fp3 := m.maps.filePaths.AddFilePath(e3)
fp3.Path = configFileName
// now that we have set up where we look for config files,
// we need to make an entity for the config file we want to load
m.AddFilePath(m.NewEntity()).Path = configFileName
// The actual processing of all of this happens when the world updates the systems.
// this process may take more than one iteration over the systems
}
func (m *GameBootstrapSystem) Process() {
configs := m.gameConfigs.GetEntities()
configs := m.subscribedConfigs.GetEntities()
if len(configs) < 1 {
return
}
cfg, found := m.maps.gameConfigs.GetGameConfig(configs[0])
m.Infof("found %d new configs to parse", len(configs))
firstConfigEntityID := configs[0]
cfg, found := m.GetGameConfig(firstConfigEntityID)
if !found {
return
}
m.initMpqSources(cfg)
m.Info("game bootstrap complete, deactivating system")
m.SetActive(false) // bootstrap is complete!
}
@ -142,8 +166,10 @@ func (m *GameBootstrapSystem) initMpqSources(cfg *d2components.GameConfigCompone
for _, mpqFileName := range cfg.MpqLoadOrder {
fullMpqFilePath := path.Join(cfg.MpqPath, mpqFileName)
m.Infof("adding mpq: %s", fullMpqFilePath)
// make a new entity for the mpq file source
mpqSource := m.maps.filePaths.AddFilePath(m.NewEntity())
mpqSource := m.AddFilePath(m.NewEntity())
mpqSource.Path = fullMpqFilePath
}
}

View File

@ -2,8 +2,8 @@ package d2systems
import (
"encoding/json"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2util"
"github.com/gravestench/akara"
@ -13,9 +13,13 @@ import (
// static check that the game config system implements the system interface
var _ akara.System = &GameConfigSystem{}
const (
loggerPrefixGameConfig = "Game Config"
)
func NewGameConfigSystem() *GameConfigSystem {
// we are going to check entities that dont yet have loaded asset types
thingsToCheck := akara.NewFilter().
filesToCheck := akara.NewFilter().
Require(d2components.FilePath).
Require(d2components.FileType).
Require(d2components.FileHandle).
@ -34,20 +38,17 @@ func NewGameConfigSystem() *GameConfigSystem {
Build()
// we are interested in actual game config instances, too
gameConfigs := akara.NewFilter().Require(d2components.GameConfig).Build()
gameConfigs := akara.NewFilter().
Require(d2components.GameConfig).
Build()
gcs := &GameConfigSystem{
SubscriberSystem: akara.NewSubscriberSystem(thingsToCheck, gameConfigs),
maps: struct {
gameConfigs *d2components.GameConfigMap
filePaths *d2components.FilePathMap
fileTypes *d2components.FileTypeMap
fileHandles *d2components.FileHandleMap
fileSources *d2components.FileSourceMap
dirty *d2components.DirtyMap
}{},
SubscriberSystem: akara.NewSubscriberSystem(filesToCheck, gameConfigs),
Logger: d2util.NewLogger(),
}
gcs.SetPrefix(loggerPrefixGameConfig)
return gcs
}
@ -63,16 +64,15 @@ func NewGameConfigSystem() *GameConfigSystem {
// this system either...
type GameConfigSystem struct {
*akara.SubscriberSystem
*d2util.Logger
filesToCheck *akara.Subscription
gameConfigs *akara.Subscription
maps struct {
gameConfigs *d2components.GameConfigMap
filePaths *d2components.FilePathMap
fileTypes *d2components.FileTypeMap
fileHandles *d2components.FileHandleMap
fileSources *d2components.FileSourceMap
dirty *d2components.DirtyMap
}
*d2components.GameConfigMap
*d2components.FilePathMap
*d2components.FileTypeMap
*d2components.FileHandleMap
*d2components.FileSourceMap
*d2components.DirtyMap
}
func (m *GameConfigSystem) Init(world *akara.World) {
@ -83,6 +83,8 @@ func (m *GameConfigSystem) Init(world *akara.World) {
return
}
m.Info("initializing ...")
for subIdx := range m.Subscriptions {
m.Subscriptions[subIdx] = m.AddSubscription(m.Subscriptions[subIdx].Filter)
}
@ -92,39 +94,26 @@ func (m *GameConfigSystem) Init(world *akara.World) {
// try to inject the components we require, then cast the returned
// abstract ComponentMap back to the concrete implementation
m.maps.filePaths = world.InjectMap(d2components.FilePath).(*d2components.FilePathMap)
m.maps.fileTypes = world.InjectMap(d2components.FileType).(*d2components.FileTypeMap)
m.maps.fileHandles = world.InjectMap(d2components.FileHandle).(*d2components.FileHandleMap)
m.maps.fileSources = world.InjectMap(d2components.FileSource).(*d2components.FileSourceMap)
m.maps.gameConfigs = world.InjectMap(d2components.GameConfig).(*d2components.GameConfigMap)
m.maps.dirty = world.InjectMap(d2components.Dirty).(*d2components.DirtyMap)
m.FilePathMap = world.InjectMap(d2components.FilePath).(*d2components.FilePathMap)
m.FileTypeMap = world.InjectMap(d2components.FileType).(*d2components.FileTypeMap)
m.FileHandleMap = world.InjectMap(d2components.FileHandle).(*d2components.FileHandleMap)
m.FileSourceMap = world.InjectMap(d2components.FileSource).(*d2components.FileSourceMap)
m.GameConfigMap = world.InjectMap(d2components.GameConfig).(*d2components.GameConfigMap)
m.DirtyMap = world.InjectMap(d2components.Dirty).(*d2components.DirtyMap)
}
func (m *GameConfigSystem) Process() {
m.clearDirty(m.gameConfigs.GetEntities())
m.checkForNewConfig(m.filesToCheck.GetEntities())
}
func (m *GameConfigSystem) clearDirty(entities []akara.EID) {
for _, eid := range entities {
dc, found := m.maps.dirty.GetDirty(eid)
if !found {
m.maps.dirty.AddDirty(eid) // adds it, but it's false
continue
}
dc.IsDirty = false
}
}
func (m *GameConfigSystem) checkForNewConfig(entities []akara.EID) {
for _, eid := range entities {
fp, found := m.maps.filePaths.GetFilePath(eid)
fp, found := m.GetFilePath(eid)
if !found {
continue
}
ft, found := m.maps.fileTypes.GetFileType(eid)
ft, found := m.GetFileType(eid)
if !found {
continue
}
@ -133,20 +122,21 @@ func (m *GameConfigSystem) checkForNewConfig(entities []akara.EID) {
continue
}
m.Info("loading config file ...")
m.loadConfig(eid)
}
}
func (m *GameConfigSystem) loadConfig(eid akara.EID) {
fh, found := m.maps.fileHandles.GetFileHandle(eid)
fh, found := m.GetFileHandle(eid)
if !found {
return
}
gameConfig := m.maps.gameConfigs.AddGameConfig(eid)
gameConfig := m.AddGameConfig(eid)
if err := json.NewDecoder(fh.Data).Decode(gameConfig); err != nil {
m.maps.gameConfigs.Remove(eid)
m.GameConfigMap.Remove(eid)
return
}
}

View File

@ -11,21 +11,13 @@ import (
func Test_integration(t *testing.T) {
cfg := akara.NewWorldConfig()
bootstrap := NewGameBootstrapSystem()
fileTypeResolver := NewFileTypeResolver()
fileHandleResolver := NewFileHandleResolver()
fileSourceResolver := NewFileSourceResolver()
gameConfig := NewGameConfigSystem()
assetLoader := NewAssetLoader()
renderer := NewRenderSystem()
cfg.With(fileTypeResolver).
With(fileSourceResolver).
With(fileHandleResolver).
With(gameConfig).
With(assetLoader).
With(renderer).
With(bootstrap)
cfg.With(NewFileTypeResolver()).
With(NewFileSourceResolver()).
With(NewFileHandleResolver()).
With(NewGameConfigSystem()).
With(NewAssetLoader()).
With(NewRenderSystem()).
With(NewGameBootstrapSystem())
world := akara.NewWorld(cfg)
@ -43,11 +35,8 @@ func Test_integration(t *testing.T) {
mm, _ := world.ComponentManager.GetMap(d2components.Dc6)
dc6map := mm.(*d2components.Dc6Map)
updateCount := 0
for {
world.Update(0)
updateCount++
_, found := dc6map.GetDc6(e1)
if found {

View File

@ -1,6 +1,7 @@
package d2systems
import (
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2util"
"time"
"github.com/gravestench/akara"
@ -8,6 +9,10 @@ import (
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2components"
)
const (
logPrefixMovementSystem = "Movement System"
)
// NewMovementSystem creates a movement system
func NewMovementSystem() *MovementSystem {
cfg := akara.NewFilter().Require(d2components.Position, d2components.Velocity)
@ -16,6 +21,7 @@ func NewMovementSystem() *MovementSystem {
return &MovementSystem{
SubscriberSystem: akara.NewSubscriberSystem(filter),
Logger: d2util.NewLogger(),
}
}
@ -25,6 +31,7 @@ var _ akara.System = &MovementSystem{}
// MovementSystem handles entity movement based on velocity and position components
type MovementSystem struct {
*akara.SubscriberSystem
*d2util.Logger
positions *d2components.PositionMap
velocities *d2components.VelocityMap
}
@ -38,6 +45,8 @@ func (m *MovementSystem) Init(world *akara.World) {
return
}
m.Info("initializing ...")
for subIdx := range m.Subscriptions {
m.Subscriptions[subIdx] = m.AddSubscription(m.Subscriptions[subIdx].Filter)
}
@ -52,6 +61,9 @@ func (m *MovementSystem) Init(world *akara.World) {
func (m *MovementSystem) Process() {
for subIdx := range m.Subscriptions {
entities := m.Subscriptions[subIdx].GetEntities()
m.Infof("Processing movement for %d entities ...", len(entities))
for entIdx := range entities {
m.ProcessEntity(entities[entIdx])
}

View File

@ -1,10 +1,13 @@
package d2systems
import (
"errors"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2util"
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2config"
"time"
"github.com/gravestench/akara"
"github.com/hajimehoshi/ebiten"
"github.com/hajimehoshi/ebiten/v2"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface"
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2components"
@ -13,6 +16,7 @@ import (
const (
gameTitle = "Open Diablo 2"
logPrefixRenderSystem = "Render System"
)
// NewRenderSystem creates a movement system
@ -20,9 +24,14 @@ func NewRenderSystem() *RenderSystem {
viewports := akara.NewFilter().Require(d2components.ViewPort).Build()
gameConfigs := akara.NewFilter().Require(d2components.GameConfig).Build()
return &RenderSystem{
r := &RenderSystem{
SubscriberSystem: akara.NewSubscriberSystem(viewports, gameConfigs),
Logger: d2util.NewLogger(),
}
r.SetPrefix(logPrefixRenderSystem)
return r
}
// static check that RenderSystem implements the System interface
@ -31,12 +40,13 @@ var _ akara.System = &RenderSystem{}
// RenderSystem handles entity movement based on velocity and position components
type RenderSystem struct {
*akara.SubscriberSystem
*d2util.Logger
renderer d2interface.Renderer
screenSurface d2interface.Surface
viewports *akara.Subscription
configs *akara.Subscription
configMap *d2components.GameConfigMap
viewportMap *d2components.ViewPortMap
*d2components.GameConfigMap
*d2components.ViewPortMap
lastUpdate time.Time
}
@ -49,6 +59,8 @@ func (m *RenderSystem) Init(world *akara.World) {
return
}
m.Info("initializing ...")
for subIdx := range m.Subscriptions {
m.Subscriptions[subIdx] = m.AddSubscription(m.Subscriptions[subIdx].Filter)
}
@ -58,8 +70,8 @@ func (m *RenderSystem) Init(world *akara.World) {
// try to inject the components we require, then cast the returned
// abstract ComponentMap back to the concrete implementation
m.configMap = m.InjectMap(d2components.GameConfig).(*d2components.GameConfigMap)
m.viewportMap = m.InjectMap(d2components.ViewPort).(*d2components.ViewPortMap)
m.GameConfigMap = m.InjectMap(d2components.GameConfig).(*d2components.GameConfigMap)
m.ViewPortMap = m.InjectMap(d2components.ViewPort).(*d2components.ViewPortMap)
}
// Process will create a renderer if it doesnt exist yet,
@ -68,14 +80,6 @@ func (m *RenderSystem) Process() {
if m.renderer == nil {
m.createRenderer()
}
if m.screenSurface == nil {
return
}
for _, eid := range m.viewports.GetEntities() {
m.render(eid)
}
}
func (m *RenderSystem) createRenderer() {
@ -84,12 +88,28 @@ func (m *RenderSystem) createRenderer() {
return
}
config, found := m.configMap.GetGameConfig(configs[0])
config, found := m.GetGameConfig(configs[0])
if !found {
return
}
renderer, err := d2render.CreateRenderer()
// d2render.CreateRenderer should use a GameConfigComponent instead ...
oldStyleConfig := &d2config.Configuration{
MpqLoadOrder: config.MpqLoadOrder,
Language: config.Language,
MpqPath: config.MpqPath,
TicksPerSecond: config.TicksPerSecond,
FpsCap: config.FpsCap,
SfxVolume: config.SfxVolume,
BgmVolume: config.BgmVolume,
FullScreen: config.FullScreen,
RunInBackground: config.RunInBackground,
VsyncEnabled: config.VsyncEnabled,
Backend: config.Backend,
LogLevel: config.LogLevel,
}
renderer, err := d2render.CreateRenderer(oldStyleConfig)
if err != nil {
panic(err)
}
@ -105,23 +125,27 @@ func (m *RenderSystem) createRenderer() {
m.lastUpdate = time.Now()
_ = m.renderer.Run(m.wrapWorldUpdate, 800, 600, gameTitle)
_ = m.renderer.Run(m.render, m.updateWorld, 800, 600, gameTitle)
}
func (m *RenderSystem) render(id akara.EID) {
vp, found := m.viewportMap.GetViewPort(id)
if !found {
return
func (m *RenderSystem) render(screen d2interface.Surface) error {
m.screenSurface = screen
for _, id := range m.viewports.GetEntities() {
vp, found := m.GetViewPort(id)
if !found {
return errors.New("viewport not found")
}
if m.screenSurface != nil {
m.screenSurface.Render(vp.Surface)
}
}
if m.screenSurface != nil {
_ = m.screenSurface.Render(vp.Surface)
}
return nil
}
func (m *RenderSystem) wrapWorldUpdate(s d2interface.Surface) error {
m.screenSurface = s
func (m *RenderSystem) updateWorld() error {
currentTime := time.Now()
elapsed := currentTime.Sub(m.lastUpdate)

View File

@ -1,6 +1,7 @@
package d2systems
import (
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2util"
"time"
"github.com/gravestench/akara"
@ -10,12 +11,19 @@ const (
defaultScale float64 = 1
)
const (
logPrefixTimeScaleSystem = "Time Scale"
)
// NewTimeScaleSystem creates a timescale system
func NewTimeScaleSystem() *TimeScaleSystem {
m := &TimeScaleSystem{
BaseSystem: &akara.BaseSystem{},
Logger: d2util.NewLogger(),
}
m.SetPrefix(logPrefixTimeScaleSystem)
return m
}
@ -27,20 +35,28 @@ var _ akara.System = &TimeScaleSystem{}
// up the game time without affecting the render rate.
type TimeScaleSystem struct {
*akara.BaseSystem
*d2util.Logger
scale float64
lastScale float64
}
// Init will initialize the TimeScale system
func (t *TimeScaleSystem) Init(world *akara.World) {
t.World = world
t.Info("initializing ...")
t.scale = defaultScale
}
// Process scales the worlds time delta for this frame
func (t *TimeScaleSystem) Process() {
if !t.Active() {
if !t.Active() || t.scale == t.lastScale{
return
}
t.Infof("setting time scale to %.1f", t.scale)
t.lastScale = t.scale
t.World.TimeDelta *= time.Duration(t.scale)
}

2
go.mod
View File

@ -6,10 +6,10 @@ require (
github.com/JoshVarga/blast v0.0.0-20180421040937-681c804fb9f0
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 // indirect
github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d // indirect
github.com/davecgh/go-spew v1.1.0
github.com/go-restruct/restruct v1.2.0-alpha
github.com/google/uuid v1.1.2
github.com/gravestench/akara v0.0.0-20201014060234-a64208a7fd3c
github.com/hajimehoshi/ebiten v1.12.3
github.com/hajimehoshi/ebiten/v2 v2.0.0
github.com/pkg/errors v0.9.1 // indirect
github.com/pkg/profile v1.5.0

9
go.sum
View File

@ -15,11 +15,15 @@ github.com/go-restruct/restruct v1.2.0-alpha h1:2Lp474S/9660+SJjpVxoKuWX09JsXHSr
github.com/go-restruct/restruct v1.2.0-alpha/go.mod h1:KqrpKpn4M8OLznErihXTGLlsXFGeLxHUrLRRI/1YjGk=
github.com/gofrs/flock v0.8.0 h1:MSdYClljsF3PbENUUEx85nkWfJSGfzYI9yEBZOJz6CY=
github.com/gofrs/flock v0.8.0/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU=
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k=
github.com/google/uuid v1.1.2 h1:EVhdT+1Kseyi1/pUmXKaFxYsDNy9RQYkMWRH68J/W7Y=
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/gravestench/akara v0.0.0-20201014060234-a64208a7fd3c h1:WopE590cKxkcKXcOee4gPXHqtzwbarLClCaWNCdLqgI=
github.com/gravestench/akara v0.0.0-20201014060234-a64208a7fd3c/go.mod h1:fTeda1SogMg5Lkd4lXMEd/Pk/a5/gQuLGaAI2rn1PBQ=
github.com/hajimehoshi/bitmapfont v1.3.0/go.mod h1:/Qb7yVjHYNUV4JdqNkPs6BSZwLjKqkZOMIp6jZD0KgE=
github.com/hajimehoshi/bitmapfont/v2 v2.1.0/go.mod h1:2BnYrkTQGThpr/CY6LorYtt/zEPNzvE/ND69CRTaHMs=
github.com/hajimehoshi/ebiten v1.12.3 h1:/KYCLW5VvfMKOMb8TqjKFlDCQAibM+OiA+LUwjS8t0E=
github.com/hajimehoshi/ebiten v1.12.3/go.mod h1:9JW9BAz1+swszT0SXR8VUvNvDafs9VspevAcY+KPlV8=
github.com/hajimehoshi/ebiten/v2 v2.0.0 h1:G8mhkKFtnDPPZ/ChaGWx4Bm0NusYEcafGCJ8QLxEaYs=
github.com/hajimehoshi/ebiten/v2 v2.0.0/go.mod h1:hpZZQ/kk8DZqft7QsQ5hZLRQXHSZPdKnaa0tcJ3CZFE=
github.com/hajimehoshi/file2byteslice v0.0.0-20200812174855-0e5e8a80490e/go.mod h1:CqqAHp7Dk/AqQiwuhV1yT2334qbA/tFWQW0MD2dGqUE=
@ -62,6 +66,7 @@ golang.org/x/exp v0.0.0-20201008143054-e3b2a7f2fdc7/go.mod h1:1phAWC201xIgDyaFpm
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
golang.org/x/image v0.0.0-20190703141733-d6a02ce849c9/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/image v0.0.0-20200801110659-972c09e46d76/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/image v0.0.0-20200927104501-e162460cd6b5 h1:QelT11PB4FXiDEXucrfNckHoFxwt8USGY1ajP1ZF5lM=
golang.org/x/image v0.0.0-20200927104501-e162460cd6b5/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
@ -86,6 +91,7 @@ golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20190429190828-d89cdac9e872/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200918174421-af09f7315aff/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201009025420-dfb3f7c4e634 h1:bNEHhJCnrwMKNMmOx3yAynp5vs5/gRy+XWFtZFu7NBM=
golang.org/x/sys v0.0.0-20201009025420-dfb3f7c4e634/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201028215240-c5abc1b1d397 h1:YZ169h3kkKEXsueizzMwOT9AaiffbOa6oXSmUFJ4vxM=
@ -94,9 +100,8 @@ golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20200117012304-6edc0a871e69/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200117220505-0cba7a3a9ee9 h1:KOkk4e2xd5OeCDJGwacvr75ICCbCsShrHiqPEdsA9hg=
golang.org/x/tools v0.0.0-20200117220505-0cba7a3a9ee9/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200918232735-d647fc253266/go.mod h1:z6u4i615ZeAfBE4XtMziQW1fSVJXACjjbWkB/mvPzlU=
golang.org/x/tools v0.0.0-20201009162240-fcf82128ed91/go.mod h1:z6u4i615ZeAfBE4XtMziQW1fSVJXACjjbWkB/mvPzlU=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=