From 9ffbf1320c3561635a9cbcdc1ba44a6ecbf120d5 Mon Sep 17 00:00:00 2001 From: gucio321 <73652197+gucio321@users.noreply.github.com> Date: Sat, 21 Nov 2020 11:33:22 +0100 Subject: [PATCH] D2core logger (#934) * logger for d2audio & d2map * logger for d2ui e.t.c * d2inventory now passes on error messages * no more importing log in d2core * implemented #925 * added logger to part of d2networking & fixed "need to be changed" comments * fixed lints * fixed errors Co-authored-by: M. Sz --- d2app/app.go | 12 +-- .../d2audio/ebiten/ebiten_audio_provider.go | 24 +++-- d2core/d2audio/sound_engine.go | 32 +++++-- d2core/d2gui/box.go | 33 ++++--- d2core/d2gui/gui_manager.go | 16 +++- d2core/d2gui/label.go | 8 +- d2core/d2gui/label_button.go | 17 +++- d2core/d2gui/layout.go | 10 +- d2core/d2hero/hero_skill.go | 5 +- d2core/d2inventory/inventory_item_factory.go | 95 ++++++++++++++----- d2core/d2map/d2mapengine/engine.go | 27 ++++-- d2core/d2map/d2mapgen/act1_overworld.go | 3 +- d2core/d2map/d2mapgen/map_generator.go | 13 ++- d2core/d2map/d2maprenderer/renderer.go | 24 +++-- d2core/d2map/d2maprenderer/tile_cache.go | 8 +- d2core/d2map/d2mapstamp/factory.go | 23 ++++- d2core/d2records/cubemain_loader.go | 42 +++++--- d2core/d2records/monster_stats2_loader.go | 16 ++-- d2core/d2records/object_lookup_record_test.go | 3 +- d2core/d2records/record_manager.go | 5 +- d2core/d2records/skill_details_loader.go | 38 ++++---- d2core/d2screen/screen_manager.go | 26 +++-- d2core/d2ui/button.go | 6 +- d2core/d2ui/checkbox.go | 6 +- d2core/d2ui/d2ui.go | 10 ++ d2core/d2ui/frame.go | 14 +-- d2core/d2ui/label.go | 10 +- d2core/d2ui/scrollbar.go | 4 +- d2core/d2ui/sprite.go | 12 ++- d2core/d2ui/textbox.go | 3 +- d2core/d2ui/ui_manager.go | 12 ++- d2game/d2gamescreen/game.go | 4 +- d2game/d2gamescreen/map_engine_testing.go | 12 ++- d2game/d2player/key_binding_menu.go | 8 +- .../d2localclient/local_client_connection.go | 15 ++- .../remote_client_connection.go | 22 +++-- d2networking/d2client/game_client.go | 38 +++++--- d2networking/d2server/game_server.go | 66 +++++++------ d2networking/dedicated_server.go | 4 +- 39 files changed, 471 insertions(+), 255 deletions(-) diff --git a/d2app/app.go b/d2app/app.go index 3359a484..e20c3c36 100644 --- a/d2app/app.go +++ b/d2app/app.go @@ -138,7 +138,7 @@ func (a *App) startDedicatedServer() error { srvChanIn := make(chan int) srvChanLog := make(chan string) - srvErr := d2networking.StartDedicatedServer(a.asset, srvChanIn, srvChanLog, maxPlayers) + srvErr := d2networking.StartDedicatedServer(a.asset, srvChanIn, srvChanLog, a.config.LogLevel, maxPlayers) if srvErr != nil { return srvErr } @@ -179,7 +179,7 @@ func (a *App) loadEngine() error { a.asset.SetLogLevel(logLevel) - audio := ebiten2.CreateAudio(a.asset) + audio := ebiten2.CreateAudio(a.config.LogLevel, a.asset) inputManager := d2input.NewInputManager() @@ -195,7 +195,7 @@ func (a *App) loadEngine() error { scriptEngine := d2script.CreateScriptEngine() - uiManager := d2ui.NewUIManager(a.asset, renderer, inputManager, audio) + uiManager := d2ui.NewUIManager(a.asset, renderer, inputManager, a.config.LogLevel, audio) a.inputManager = inputManager a.terminal = term @@ -409,14 +409,14 @@ func (a *App) initialize() error { } } - gui, err := d2gui.CreateGuiManager(a.asset, a.inputManager) + gui, err := d2gui.CreateGuiManager(a.asset, a.config.LogLevel, a.inputManager) if err != nil { return err } a.guiManager = gui - a.screen = d2screen.NewScreenManager(a.ui, a.guiManager) + a.screen = d2screen.NewScreenManager(a.ui, a.config.LogLevel, a.guiManager) a.audio.SetVolumes(a.config.BgmVolume, a.config.SfxVolume) @@ -931,7 +931,7 @@ func (a *App) ToSelectHero(connType d2clientconnectiontype.ClientConnectionType, // ToCreateGame forces the game to transition to the Create Game screen func (a *App) ToCreateGame(filePath string, connType d2clientconnectiontype.ClientConnectionType, host string) { - gameClient, err := d2client.Create(connType, a.asset, a.scriptEngine) + gameClient, err := d2client.Create(connType, a.asset, a.config.LogLevel, a.scriptEngine) if err != nil { a.logger.Error(err.Error()) } diff --git a/d2core/d2audio/ebiten/ebiten_audio_provider.go b/d2core/d2audio/ebiten/ebiten_audio_provider.go index 58ed6b13..ddee16b1 100644 --- a/d2core/d2audio/ebiten/ebiten_audio_provider.go +++ b/d2core/d2audio/ebiten/ebiten_audio_provider.go @@ -3,9 +3,9 @@ package ebiten import ( "io" - "log" "github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface" + "github.com/OpenDiablo2/OpenDiablo2/d2common/d2util" "github.com/OpenDiablo2/OpenDiablo2/d2core/d2asset" "github.com/hajimehoshi/ebiten/v2/audio" @@ -14,14 +14,20 @@ import ( const sampleRate = 44100 +const logPrefix = "Ebiten Audio Provider" + var _ d2interface.AudioProvider = &AudioProvider{} // Static check to confirm struct conforms to interface // CreateAudio creates an instance of ebiten's audio provider -func CreateAudio(am *d2asset.AssetManager) *AudioProvider { +func CreateAudio(l d2util.LogLevel, am *d2asset.AssetManager) *AudioProvider { result := &AudioProvider{ asset: am, } + result.Logger = d2util.NewLogger() + result.Logger.SetLevel(l) + result.Logger.SetPrefix(logPrefix) + result.audioContext = audio.NewContext(sampleRate) return result @@ -36,6 +42,8 @@ type AudioProvider struct { lastBgm string sfxVolume float64 bgmVolume float64 + + *d2util.Logger } // PlayBGM loads an audio stream and plays it in the background @@ -55,7 +63,7 @@ func (eap *AudioProvider) PlayBGM(song string) { err := eap.bgmAudio.Close() if err != nil { - log.Panic(err) + eap.Fatal(err.Error()) } } @@ -66,20 +74,20 @@ func (eap *AudioProvider) PlayBGM(song string) { } if _, err = audioStream.Seek(0, io.SeekStart); err != nil { - log.Fatal(err) + eap.Fatal(err.Error()) } eap.bgmStream, err = wav.Decode(eap.audioContext, audioStream) if err != nil { - log.Fatal(err) + eap.Fatal(err.Error()) } s := audio.NewInfiniteLoop(eap.bgmStream, eap.bgmStream.Length()) eap.bgmAudio, err = audio.NewPlayer(eap.audioContext, s) if err != nil { - log.Fatal(err) + eap.Fatal(err.Error()) } eap.bgmAudio.SetVolume(eap.bgmVolume) @@ -142,7 +150,7 @@ func (eap *AudioProvider) createSoundEffect(sfx string, context *audio.Context, d, err := wav.Decode(context, audioData) if err != nil { - log.Fatal(err) + eap.Fatal(err.Error()) } var player *audio.Player @@ -157,7 +165,7 @@ func (eap *AudioProvider) createSoundEffect(sfx string, context *audio.Context, } if err != nil { - log.Fatal(err) + eap.Fatal(err.Error()) } result.player = player diff --git a/d2core/d2audio/sound_engine.go b/d2core/d2audio/sound_engine.go index 22e69328..f811c1f8 100644 --- a/d2core/d2audio/sound_engine.go +++ b/d2core/d2audio/sound_engine.go @@ -1,7 +1,7 @@ package d2audio import ( - "log" + "fmt" "math/rand" "github.com/OpenDiablo2/OpenDiablo2/d2core/d2asset" @@ -9,10 +9,15 @@ import ( "github.com/OpenDiablo2/OpenDiablo2/d2core/d2records" "github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface" + "github.com/OpenDiablo2/OpenDiablo2/d2common/d2util" ) type envState int +const ( + logPrefix = "Sound Engine" +) + const ( envAttack = 0 envSustain = 1 @@ -32,6 +37,8 @@ type Sound struct { vRate float64 state envState // panning float64 // lets forget about this for now + + *d2util.Logger } func (s *Sound) update(elapsed float64) { @@ -66,7 +73,7 @@ func (s *Sound) SetPan(pan float64) { // Play the sound func (s *Sound) Play() { - log.Println("starting sound", s.entry.Handle) + s.Info("starting sound" + s.entry.Handle) s.effect.Play() if s.entry.FadeIn != 0 { @@ -103,11 +110,13 @@ type SoundEngine struct { timer float64 accTime float64 sounds map[*Sound]struct{} + + *d2util.Logger } // NewSoundEngine creates a new sound engine func NewSoundEngine(provider d2interface.AudioProvider, - asset *d2asset.AssetManager, term d2interface.Terminal) *SoundEngine { + asset *d2asset.AssetManager, l d2util.LogLevel, term d2interface.Terminal) *SoundEngine { r := SoundEngine{ asset: asset, provider: provider, @@ -115,11 +124,15 @@ func NewSoundEngine(provider d2interface.AudioProvider, timer: 1, } + r.Logger = d2util.NewLogger() + r.Logger.SetPrefix(logPrefix) + r.Logger.SetLevel(l) + err := term.BindAction("playsoundid", "plays the sound for a given id", func(id int) { r.PlaySoundID(id) }) if err != nil { - log.Print(err) + r.Error(err.Error()) return nil } @@ -127,25 +140,25 @@ func NewSoundEngine(provider d2interface.AudioProvider, r.PlaySoundHandle(handle) }) if err != nil { - log.Print(err) + r.Error(err.Error()) return nil } err = term.BindAction("activesounds", "list currently active sounds", func() { for s := range r.sounds { if err != nil { - log.Print(err) + r.Error(err.Error()) return } - log.Println(s) + r.Info(fmt.Sprint(s)) } }) err = term.BindAction("killsounds", "kill active sounds", func() { for s := range r.sounds { if err != nil { - log.Print(err) + r.Error(err.Error()) return } @@ -207,13 +220,14 @@ func (s *SoundEngine) PlaySoundID(id int) *Sound { effect, err := s.provider.LoadSound(entry.FileName, entry.Loop, entry.MusicVol) if err != nil { - log.Print(err) + s.Error(err.Error()) return nil } snd := Sound{ entry: entry, effect: effect, + Logger: s.Logger, } s.sounds[&snd] = struct{}{} diff --git a/d2core/d2gui/box.go b/d2core/d2gui/box.go index 9836d4f3..446d5e10 100644 --- a/d2core/d2gui/box.go +++ b/d2core/d2gui/box.go @@ -1,8 +1,6 @@ package d2gui import ( - "log" - "github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface" "github.com/OpenDiablo2/OpenDiablo2/d2common/d2resource" "github.com/OpenDiablo2/OpenDiablo2/d2common/d2util" @@ -66,6 +64,8 @@ type Box struct { disableBorder bool isOpen bool title string + + *d2util.Logger } // NewBox return a new Box instance @@ -76,9 +76,10 @@ func NewBox( contentLayout *Layout, width, height int, x, y int, + l d2util.LogLevel, title string, ) *Box { - return &Box{ + box := &Box{ asset: asset, renderer: renderer, uiManager: ui, @@ -90,6 +91,12 @@ func NewBox( x: x, y: y, } + + box.Logger = d2util.NewLogger() + box.Logger.SetLevel(l) + box.Logger.SetPrefix(logPrefix) + + return box } // GetLayout returns the box layout @@ -145,12 +152,12 @@ func (box *Box) setupTopBorder(offsetY int) { for _, frameIndex := range topEdgePiece { f, err := box.uiManager.NewSprite(d2resource.BoxPieces, d2resource.PaletteSky) if err != nil { - log.Print(err) + box.Error(err.Error()) } err = f.SetCurrentFrame(frameIndex) if err != nil { - log.Print(err) + box.Error(err.Error()) } f.SetPosition(currentX, currentY) @@ -187,12 +194,12 @@ func (box *Box) setupBottomBorder(offsetY int) { for _, frameIndex := range bottomEdgePiece { f, err := box.uiManager.NewSprite(d2resource.BoxPieces, d2resource.PaletteSky) if err != nil { - log.Print(err) + box.Error(err.Error()) } err = f.SetCurrentFrame(frameIndex) if err != nil { - log.Print(err) + box.Error(err.Error()) } f.SetPosition(currentX, currentY) @@ -227,12 +234,12 @@ func (box *Box) setupLeftBorder() { for _, frameIndex := range leftBorderPiece { f, err := box.uiManager.NewSprite(d2resource.BoxPieces, d2resource.PaletteSky) if err != nil { - log.Print(err) + box.Error(err.Error()) } err = f.SetCurrentFrame(frameIndex) if err != nil { - log.Print(err) + box.Error(err.Error()) } f.SetPosition(currentX, currentY) @@ -266,12 +273,12 @@ func (box *Box) setupRightBorder() { for _, frameIndex := range rightBorderPiece { f, err := box.uiManager.NewSprite(d2resource.BoxPieces, d2resource.PaletteSky) if err != nil { - log.Print(err) + box.Error(err.Error()) } err = f.SetCurrentFrame(frameIndex) if err != nil { - log.Print(err) + box.Error(err.Error()) } f.SetPosition(currentX, currentY) @@ -302,12 +309,12 @@ func (box *Box) setupCorners() { for _, frameIndex := range cornersFrames { f, err := box.uiManager.NewSprite(d2resource.BoxPieces, d2resource.PaletteSky) if err != nil { - log.Print(err) + box.Error(err.Error()) } err = f.SetCurrentFrame(frameIndex) if err != nil { - log.Print(err) + box.Error(err.Error()) } switch frameIndex { diff --git a/d2core/d2gui/gui_manager.go b/d2core/d2gui/gui_manager.go index 6c361d8e..4cf080d2 100644 --- a/d2core/d2gui/gui_manager.go +++ b/d2core/d2gui/gui_manager.go @@ -2,14 +2,18 @@ package d2gui import ( "image/color" - "log" "math" "github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface" "github.com/OpenDiablo2/OpenDiablo2/d2common/d2resource" + "github.com/OpenDiablo2/OpenDiablo2/d2common/d2util" "github.com/OpenDiablo2/OpenDiablo2/d2core/d2asset" ) +const ( + logPrefix = "GUI Manager" +) + // GuiManager is a GUI widget manager that handles dynamic layout/positioning of widgets type GuiManager struct { asset *d2asset.AssetManager @@ -20,10 +24,12 @@ type GuiManager struct { loadingAnim d2interface.Animation cursorVisible bool loading bool + + *d2util.Logger } // CreateGuiManager creates an instance of the GuiManager -func CreateGuiManager(asset *d2asset.AssetManager, inputManager d2interface.InputManager) (*GuiManager, error) { +func CreateGuiManager(asset *d2asset.AssetManager, l d2util.LogLevel, inputManager d2interface.InputManager) (*GuiManager, error) { cursorAnim, err := asset.LoadAnimation(d2resource.CursorDefault, d2resource.PaletteUnits) if err != nil { return nil, err @@ -41,6 +47,10 @@ func CreateGuiManager(asset *d2asset.AssetManager, inputManager d2interface.Inpu cursorVisible: true, } + manager.Logger = d2util.NewLogger() + manager.Logger.SetPrefix(logPrefix) + manager.Logger.SetLevel(l) + manager.clear() if err := inputManager.BindHandler(manager); err != nil { @@ -159,7 +169,7 @@ func (m *GuiManager) ShowLoadScreen(progress float64) { err := animation.SetCurrentFrame(int(float64(frameCount-1) * progress)) if err != nil { - log.Print(err) + m.Error(err.Error()) } m.loading = true diff --git a/d2core/d2gui/label.go b/d2core/d2gui/label.go index 7d144ff6..03427f79 100644 --- a/d2core/d2gui/label.go +++ b/d2core/d2gui/label.go @@ -2,7 +2,6 @@ package d2gui import ( "image/color" - "log" "time" "github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface" @@ -36,7 +35,7 @@ type Label struct { blinkTimer time.Time } -func createLabel(renderer d2interface.Renderer, text string, font *d2asset.Font, col color.RGBA) *Label { +func createLabel(renderer d2interface.Renderer, text string, font *d2asset.Font, col color.RGBA) (*Label, error) { label := &Label{ font: font, renderer: renderer, @@ -46,13 +45,12 @@ func createLabel(renderer d2interface.Renderer, text string, font *d2asset.Font, err := label.setText(text) if err != nil { - log.Print(err) - return nil + return nil, err } label.SetVisible(true) - return label + return label, nil } // SetHoverColor will set the value of hoverColor diff --git a/d2core/d2gui/label_button.go b/d2core/d2gui/label_button.go index be338a4f..e4c30f73 100644 --- a/d2core/d2gui/label_button.go +++ b/d2core/d2gui/label_button.go @@ -2,7 +2,6 @@ package d2gui import ( "image/color" - "log" "github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface" "github.com/OpenDiablo2/OpenDiablo2/d2common/d2util" @@ -19,11 +18,13 @@ type LabelButton struct { isHovered bool layout *Layout x, y int + + *d2util.Logger } // NewLabelButton generates a new instance of LabelButton -func NewLabelButton(x, y int, text string, col color.RGBA, callback func()) *LabelButton { - return &LabelButton{ +func NewLabelButton(x, y int, text string, col color.RGBA, l d2util.LogLevel, callback func()) *LabelButton { + lb := &LabelButton{ x: x, y: y, hoverColor: col, @@ -31,6 +32,12 @@ func NewLabelButton(x, y int, text string, col color.RGBA, callback func()) *Lab callback: callback, canHover: true, } + + lb.Logger = d2util.NewLogger() + lb.Logger.SetLevel(l) + lb.Logger.SetPrefix(logPrefix) + + return lb } // IsInRect checks if the given point is within the overlay layout rectangle @@ -60,13 +67,13 @@ func (lb *LabelButton) Load(renderer d2interface.Renderer, asset *d2asset.AssetM mainLayout.SetMouseEnterHandler(func(event d2interface.MouseMoveEvent) { if err := l.SetIsHovered(true); err != nil { - log.Printf("could not change label to hover state: %v", err) + lb.Errorf("could not change label to hover state: %v", err) } }) mainLayout.SetMouseLeaveHandler(func(event d2interface.MouseMoveEvent) { if err := l.SetIsHovered(false); err != nil { - log.Printf("could not change label to hover state: %v", err) + lb.Errorf("could not change label to hover state: %v", err) } }) diff --git a/d2core/d2gui/layout.go b/d2core/d2gui/layout.go index 03376bf7..43125acf 100644 --- a/d2core/d2gui/layout.go +++ b/d2core/d2gui/layout.go @@ -167,7 +167,10 @@ func (l *Layout) AddLabel(text string, fontStyle FontStyle) (*Label, error) { return nil, err } - label := createLabel(l.renderer, text, font, d2util.Color(ColorWhite)) + label, err := createLabel(l.renderer, text, font, d2util.Color(ColorWhite)) + if err != nil { + return nil, err + } l.entries = append(l.entries, &layoutEntry{widget: label}) @@ -181,7 +184,10 @@ func (l *Layout) AddLabelWithColor(text string, fontStyle FontStyle, col color.R return nil, err } - label := createLabel(l.renderer, text, font, col) + label, err := createLabel(l.renderer, text, font, col) + if err != nil { + return nil, err + } l.entries = append(l.entries, &layoutEntry{widget: label}) diff --git a/d2core/d2hero/hero_skill.go b/d2core/d2hero/hero_skill.go index 9296d745..d3dafafc 100644 --- a/d2core/d2hero/hero_skill.go +++ b/d2core/d2hero/hero_skill.go @@ -2,7 +2,6 @@ package d2hero import ( "encoding/json" - "log" "github.com/OpenDiablo2/OpenDiablo2/d2core/d2records" ) @@ -27,10 +26,10 @@ func (hs *HeroSkill) MarshalJSON() ([]byte, error) { // only serialize the Shallow object instead of the SkillRecord & SkillDescriptionRecord bytes, err := json.Marshal(hs.Shallow) if err != nil { - log.Fatalln(err) + return nil, err } - return bytes, err + return bytes, nil } // UnmarshalJSON overrides the default logic used when the HeroSkill is deserialized from a byte array. diff --git a/d2core/d2inventory/inventory_item_factory.go b/d2core/d2inventory/inventory_item_factory.go index 370d585c..fb081fcc 100644 --- a/d2core/d2inventory/inventory_item_factory.go +++ b/d2core/d2inventory/inventory_item_factory.go @@ -1,7 +1,7 @@ package d2inventory import ( - "log" + "fmt" "github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum" "github.com/OpenDiablo2/OpenDiablo2/d2core/d2asset" @@ -11,7 +11,10 @@ import ( func NewInventoryItemFactory(asset *d2asset.AssetManager) (*InventoryItemFactory, error) { factory := &InventoryItemFactory{asset: asset} - factory.loadHeroObjects() + err := factory.loadHeroObjects() + if err != nil { + return nil, err + } return factory, nil } @@ -23,46 +26,88 @@ type InventoryItemFactory struct { } // LoadHeroObjects loads the equipment objects of the hero -func (f *InventoryItemFactory) loadHeroObjects() { +func (f *InventoryItemFactory) loadHeroObjects() error { // https://github.com/OpenDiablo2/OpenDiablo2/issues/795 //Mode: d2enum.AnimationModePlayerNeutral.String(), //Base: "/data/global/chars", + shield, err := f.GetArmorItemByCode("buc") + if err != nil { + return err + } + + rhhex, err := f.GetWeaponItemByCode("hax") + if err != nil { + return err + } + + rhwnd, err := f.GetWeaponItemByCode("wnd") + if err != nil { + return err + } + + rhssd, err := f.GetWeaponItemByCode("ssd") + if err != nil { + return err + } + + rhktr, err := f.GetWeaponItemByCode("ktr") + if err != nil { + return err + } + + rhsst, err := f.GetWeaponItemByCode("sst") + if err != nil { + return err + } + + rhjav, err := f.GetWeaponItemByCode("jav") + if err != nil { + return err + } + + rhclb, err := f.GetWeaponItemByCode("clb") + if err != nil { + return err + } + f.DefaultHeroItems = map[d2enum.Hero]CharacterEquipment{ d2enum.HeroBarbarian: { - RightHand: f.GetWeaponItemByCode("hax"), - Shield: f.GetArmorItemByCode("buc"), + RightHand: rhhex, + Shield: shield, }, d2enum.HeroNecromancer: { - RightHand: f.GetWeaponItemByCode("wnd"), + RightHand: rhwnd, }, d2enum.HeroPaladin: { - RightHand: f.GetWeaponItemByCode("ssd"), - Shield: f.GetArmorItemByCode("buc"), + RightHand: rhssd, + Shield: shield, }, d2enum.HeroAssassin: { - RightHand: f.GetWeaponItemByCode("ktr"), - Shield: f.GetArmorItemByCode("buc"), + RightHand: rhktr, + Shield: shield, }, d2enum.HeroSorceress: { - RightHand: f.GetWeaponItemByCode("sst"), - LeftHand: f.GetWeaponItemByCode("sst"), + RightHand: rhsst, + LeftHand: rhsst, }, d2enum.HeroAmazon: { - RightHand: f.GetWeaponItemByCode("jav"), - Shield: f.GetArmorItemByCode("buc"), + RightHand: rhjav, + Shield: shield, }, d2enum.HeroDruid: { - RightHand: f.GetWeaponItemByCode("clb"), - Shield: f.GetArmorItemByCode("buc"), + RightHand: rhclb, + Shield: shield, }, } + + return nil } // GetArmorItemByCode returns the armor item for the given code -func (f *InventoryItemFactory) GetArmorItemByCode(code string) *InventoryItemArmor { +func (f *InventoryItemFactory) GetArmorItemByCode(code string) (*InventoryItemArmor, error) { result := f.asset.Records.Item.Armors[code] if result == nil { - log.Fatalf("Could not find armor entry for code '%s'", code) + return nil, fmt.Errorf("could not find armor entry for code '%s'", code) } return &InventoryItemArmor{ @@ -71,14 +116,14 @@ func (f *InventoryItemFactory) GetArmorItemByCode(code string) *InventoryItemArm ItemName: result.Name, ItemCode: result.Code, ArmorClass: d2enum.ArmorClassLite, // comes from ArmType.txt - } + }, nil } // GetMiscItemByCode returns the miscellaneous item for the given code -func (f *InventoryItemFactory) GetMiscItemByCode(code string) *InventoryItemMisc { +func (f *InventoryItemFactory) GetMiscItemByCode(code string) (*InventoryItemMisc, error) { result := f.asset.Records.Item.Misc[code] if result == nil { - log.Fatalf("Could not find misc item entry for code '%s'", code) + return nil, fmt.Errorf("could not find misc item entry for code '%s'", code) } return &InventoryItemMisc{ @@ -86,15 +131,15 @@ func (f *InventoryItemFactory) GetMiscItemByCode(code string) *InventoryItemMisc InventorySizeY: result.InventoryHeight, ItemName: result.Name, ItemCode: result.Code, - } + }, nil } // GetWeaponItemByCode returns the weapon item for the given code -func (f *InventoryItemFactory) GetWeaponItemByCode(code string) *InventoryItemWeapon { +func (f *InventoryItemFactory) GetWeaponItemByCode(code string) (*InventoryItemWeapon, error) { // https://github.com/OpenDiablo2/OpenDiablo2/issues/796 result := f.asset.Records.Item.Weapons[code] if result == nil { - log.Fatalf("Could not find weapon entry for code '%s'", code) + return nil, fmt.Errorf("could not find weapon entry for code '%s'", code) } return &InventoryItemWeapon{ @@ -104,5 +149,5 @@ func (f *InventoryItemFactory) GetWeaponItemByCode(code string) *InventoryItemWe ItemCode: result.Code, WeaponClass: result.WeaponClass, WeaponClassOffHand: result.WeaponClass2Hand, - } + }, nil } diff --git a/d2core/d2map/d2mapengine/engine.go b/d2core/d2map/d2mapengine/engine.go index b514e429..4ab2e17f 100644 --- a/d2core/d2map/d2mapengine/engine.go +++ b/d2core/d2map/d2mapengine/engine.go @@ -1,7 +1,6 @@ package d2mapengine import ( - "log" "strings" "github.com/OpenDiablo2/OpenDiablo2/d2core/d2records" @@ -13,10 +12,15 @@ import ( "github.com/OpenDiablo2/OpenDiablo2/d2common/d2fileformats/d2dt1" "github.com/OpenDiablo2/OpenDiablo2/d2common/d2geom" "github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface" + "github.com/OpenDiablo2/OpenDiablo2/d2common/d2util" "github.com/OpenDiablo2/OpenDiablo2/d2core/d2asset" "github.com/OpenDiablo2/OpenDiablo2/d2core/d2map/d2mapstamp" ) +const ( + logPrefix = "Map Engine" +) + // MapEngine loads the tiles which make up the isometric map and the entities type MapEngine struct { asset *d2asset.AssetManager @@ -34,6 +38,8 @@ type MapEngine struct { // https://github.com/OpenDiablo2/OpenDiablo2/issues/789 IsLoading bool // (temp) Whether we have processed the GenerateMapPacket(only for remote client) + + *d2util.Logger } const ( @@ -41,9 +47,9 @@ const ( ) // CreateMapEngine creates a new instance of the map engine and returns a pointer to it. -func CreateMapEngine(asset *d2asset.AssetManager) *MapEngine { +func CreateMapEngine(l d2util.LogLevel, asset *d2asset.AssetManager) *MapEngine { entity, _ := d2mapentity.NewMapEntityFactory(asset) - stamp := d2mapstamp.NewStampFactory(asset, entity) + stamp := d2mapstamp.NewStampFactory(asset, l, entity) engine := &MapEngine{ asset: asset, @@ -53,6 +59,10 @@ func CreateMapEngine(asset *d2asset.AssetManager) *MapEngine { IsLoading: false, } + engine.Logger = d2util.NewLogger() + engine.Logger.SetLevel(l) + engine.Logger.SetPrefix(logPrefix) + return engine } @@ -89,14 +99,13 @@ func (m *MapEngine) addDT1(fileName string) { fileData, err := m.asset.LoadFile("/data/global/tiles/" + fileName) if err != nil { - log.Printf("Could not load /data/global/tiles/%s", fileName) - // panic(err) + m.Fatalf("Could not load /data/global/tiles/%s", fileName) return } dt1, err := d2dt1.LoadDT1(fileData) if err != nil { - log.Print(err) + m.Error(err.Error()) } m.dt1TileData = append(m.dt1TileData, dt1.Tiles...) @@ -118,7 +127,7 @@ func (m *MapEngine) AddDS1(fileName string) { ds1, err := d2ds1.LoadDS1(fileData) if err != nil { - log.Print(err) + m.Error(err.Error()) } for idx := range ds1.Files { @@ -138,7 +147,7 @@ func (m *MapEngine) LevelType() d2records.LevelTypeRecord { // SetSeed sets the seed of the map for generation. func (m *MapEngine) SetSeed(seed int64) { - log.Printf("Setting map engine seed to %d", seed) + m.Infof("Setting map engine seed to %d", seed) m.seed = seed } @@ -259,7 +268,7 @@ func (m *MapEngine) GetTiles(style, sequence int, tileType d2enum.TileType) []d2 } if len(tiles) == 0 { - log.Printf("Unknown tile ID [%d %d %d]\n", style, sequence, tileType) + m.Warningf("Unknown tile ID [%d %d %d]", style, sequence, tileType) return nil } diff --git a/d2core/d2map/d2mapgen/act1_overworld.go b/d2core/d2map/d2mapgen/act1_overworld.go index 90f40956..58626065 100644 --- a/d2core/d2map/d2mapgen/act1_overworld.go +++ b/d2core/d2map/d2mapgen/act1_overworld.go @@ -4,7 +4,6 @@ package d2mapgen // is experiemental, and mapgen will likely change dramatically in the future. import ( - "log" "math/rand" "strings" @@ -49,7 +48,7 @@ func (g *MapGenerator) GenerateAct1Overworld() { townStamp.RegionPath() townSize := townStamp.Size() - log.Printf("Region Path: %s", townStamp.RegionPath()) + g.Infof("Region Path: %s", townStamp.RegionPath()) switch { case strings.Contains(townStamp.RegionPath(), "E1"): diff --git a/d2core/d2map/d2mapgen/map_generator.go b/d2core/d2map/d2mapgen/map_generator.go index 068fb657..5aa8bb40 100644 --- a/d2core/d2map/d2mapgen/map_generator.go +++ b/d2core/d2map/d2mapgen/map_generator.go @@ -5,17 +5,26 @@ import ( "github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum" "github.com/OpenDiablo2/OpenDiablo2/d2common/d2geom" + "github.com/OpenDiablo2/OpenDiablo2/d2common/d2util" "github.com/OpenDiablo2/OpenDiablo2/d2core/d2map/d2mapengine" "github.com/OpenDiablo2/OpenDiablo2/d2core/d2map/d2mapstamp" ) +const ( + logPrefix = "Map Generator" +) + // NewMapGenerator creates a map generator instance -func NewMapGenerator(a *d2asset.AssetManager, e *d2mapengine.MapEngine) (*MapGenerator, error) { +func NewMapGenerator(a *d2asset.AssetManager, l d2util.LogLevel, e *d2mapengine.MapEngine) (*MapGenerator, error) { generator := &MapGenerator{ asset: a, engine: e, } + generator.Logger = d2util.NewLogger() + generator.Logger.SetLevel(l) + generator.Logger.SetPrefix(logPrefix) + return generator, nil } @@ -23,6 +32,8 @@ func NewMapGenerator(a *d2asset.AssetManager, e *d2mapengine.MapEngine) (*MapGen type MapGenerator struct { asset *d2asset.AssetManager engine *d2mapengine.MapEngine + + *d2util.Logger } func (g *MapGenerator) loadPreset(id, index int) *d2mapstamp.Stamp { diff --git a/d2core/d2map/d2maprenderer/renderer.go b/d2core/d2map/d2maprenderer/renderer.go index 96e3b075..ab0be25a 100644 --- a/d2core/d2map/d2maprenderer/renderer.go +++ b/d2core/d2map/d2maprenderer/renderer.go @@ -2,9 +2,7 @@ package d2maprenderer import ( "errors" - "fmt" "image/color" - "log" "math" "github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum" @@ -17,6 +15,10 @@ import ( "github.com/OpenDiablo2/OpenDiablo2/d2core/d2map/d2mapengine" ) +const ( + logPrefix = "Map Renderer" +) + const ( screenMiddleX = 400 two = 2 @@ -59,12 +61,14 @@ type MapRenderer struct { entityDebugVisLevel int // Entity Debug visibility index (0=none, 1=vectors) lastFrameTime float64 // The last time the map was rendered currentFrame int // Current render frame (for animations) + + *d2util.Logger } // CreateMapRenderer creates a new MapRenderer, sets the required fields and returns a pointer to it. func CreateMapRenderer(asset *d2asset.AssetManager, renderer d2interface.Renderer, mapEngine *d2mapengine.MapEngine, - term d2interface.Terminal, startX, startY float64) *MapRenderer { + term d2interface.Terminal, l d2util.LogLevel, startX, startY float64) *MapRenderer { result := &MapRenderer{ asset: asset, renderer: renderer, @@ -72,6 +76,10 @@ func CreateMapRenderer(asset *d2asset.AssetManager, renderer d2interface.Rendere viewport: NewViewport(0, 0, 800, 600), } + result.Logger = d2util.NewLogger() + result.Logger.SetPrefix(logPrefix) + result.Logger.SetLevel(l) + result.Camera = Camera{} rx, ry := result.WorldToOrtho(startX, startY) startPosition := d2vector.NewPosition(rx, ry) @@ -84,7 +92,7 @@ func CreateMapRenderer(asset *d2asset.AssetManager, renderer d2interface.Rendere }) if err != nil { - fmt.Printf("could not bind the mapdebugvis action, err: %v\n", err) + result.Errorf("could not bind the mapdebugvis action, err: %v", err) } err = term.BindAction("entitydebugvis", "set entity debug visualization level", func(level int) { @@ -92,7 +100,7 @@ func CreateMapRenderer(asset *d2asset.AssetManager, renderer d2interface.Rendere }) if err != nil { - fmt.Printf("could not bind the entitydebugvis action, err: %v\n", err) + result.Errorf("could not bind the entitydebugvis action, err: %v", err) } if mapEngine.LevelType().ID != 0 { @@ -363,7 +371,7 @@ func (mr *MapRenderer) renderFloor(tile d2ds1.FloorShadowRecord, target d2interf } if img == nil { - log.Printf("Render called on uncached floor {%v,%v}", tile.Style, tile.Sequence) + mr.Warningf("Render called on uncached floor {%v,%v}", tile.Style, tile.Sequence) return } @@ -379,7 +387,7 @@ func (mr *MapRenderer) renderFloor(tile d2ds1.FloorShadowRecord, target d2interf func (mr *MapRenderer) renderWall(tile d2ds1.WallRecord, viewport *Viewport, target d2interface.Surface) { img := mr.getImageCacheRecord(tile.Style, tile.Sequence, tile.Type, tile.RandomIndex) if img == nil { - log.Printf("Render called on uncached wall {%v,%v,%v}", tile.Style, tile.Sequence, tile.Type) + mr.Warningf("Render called on uncached wall {%v,%v,%v}", tile.Style, tile.Sequence, tile.Type) return } @@ -395,7 +403,7 @@ func (mr *MapRenderer) renderWall(tile d2ds1.WallRecord, viewport *Viewport, tar func (mr *MapRenderer) renderShadow(tile d2ds1.FloorShadowRecord, target d2interface.Surface) { img := mr.getImageCacheRecord(tile.Style, tile.Sequence, 13, tile.RandomIndex) if img == nil { - log.Printf("Render called on uncached shadow {%v,%v}", tile.Style, tile.Sequence) + mr.Warningf("Render called on uncached shadow {%v,%v}", tile.Style, tile.Sequence) return } diff --git a/d2core/d2map/d2maprenderer/tile_cache.go b/d2core/d2map/d2maprenderer/tile_cache.go index f52e46c5..56991a59 100644 --- a/d2core/d2map/d2maprenderer/tile_cache.go +++ b/d2core/d2map/d2maprenderer/tile_cache.go @@ -1,8 +1,6 @@ package d2maprenderer import ( - "log" - "github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum" "github.com/OpenDiablo2/OpenDiablo2/d2common/d2fileformats/d2ds1" "github.com/OpenDiablo2/OpenDiablo2/d2common/d2fileformats/d2dt1" @@ -28,7 +26,7 @@ func (mr *MapRenderer) generateTileCache() { mr.palette, err = mr.loadPaletteForAct(d2enum.RegionIdType(mr.mapEngine.LevelType().ID)) if err != nil { - log.Print(err) + mr.Error(err.Error()) } tiles := *mr.mapEngine.Tiles() @@ -61,7 +59,7 @@ func (mr *MapRenderer) generateFloorCache(tile *d2ds1.FloorShadowRecord) { var tileData []*d2dt1.Tile if tileOptions == nil { - log.Printf("Could not locate tile Style:%d, Seq: %d, Type: %d\n", tile.Style, tile.Sequence, 0) + mr.Errorf("Could not locate tile Style:%d, Seq: %d, Type: %d", tile.Style, tile.Sequence, 0) tileData = append(tileData, &d2dt1.Tile{}) tileData[0].Width = defaultFloorTileWidth @@ -205,7 +203,7 @@ func (mr *MapRenderer) generateWallCache(tile *d2ds1.WallRecord) { } if realHeight == 0 { - log.Printf("Invalid 0 height for wall tile") + mr.Error("Invalid 0 height for wall tile") return } diff --git a/d2core/d2map/d2mapstamp/factory.go b/d2core/d2map/d2mapstamp/factory.go index 70afea00..774ee0a3 100644 --- a/d2core/d2map/d2mapstamp/factory.go +++ b/d2core/d2map/d2mapstamp/factory.go @@ -1,7 +1,6 @@ package d2mapstamp import ( - "log" "math" "math/rand" @@ -10,12 +9,24 @@ import ( "github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum" "github.com/OpenDiablo2/OpenDiablo2/d2common/d2fileformats/d2ds1" "github.com/OpenDiablo2/OpenDiablo2/d2common/d2fileformats/d2dt1" + "github.com/OpenDiablo2/OpenDiablo2/d2common/d2util" "github.com/OpenDiablo2/OpenDiablo2/d2core/d2asset" ) +const logPrefix = "Map Stamp" + // NewStampFactory creates a MapStamp factory instance -func NewStampFactory(asset *d2asset.AssetManager, entity *d2mapentity.MapEntityFactory) *StampFactory { - return &StampFactory{asset, entity} +func NewStampFactory(asset *d2asset.AssetManager, l d2util.LogLevel, entity *d2mapentity.MapEntityFactory) *StampFactory { + result := &StampFactory{ + asset: asset, + entity: entity, + } + + result.Logger = d2util.NewLogger() + result.Logger.SetLevel(l) + result.Logger.SetPrefix(logPrefix) + + return result } // StampFactory is responsible for loading map stamps. A stamp can be thought of like a @@ -23,6 +34,8 @@ func NewStampFactory(asset *d2asset.AssetManager, entity *d2mapentity.MapEntityF type StampFactory struct { asset *d2asset.AssetManager entity *d2mapentity.MapEntityFactory + + *d2util.Logger } // LoadStamp loads the Stamp data from file, using the given level type, level preset index, and @@ -48,7 +61,7 @@ func (f *StampFactory) LoadStamp(levelType d2enum.RegionIdType, levelPreset, fil dt1, err := d2dt1.LoadDT1(fileData) if err != nil { - log.Print(err) + f.Error(err.Error()) return nil } @@ -82,7 +95,7 @@ func (f *StampFactory) LoadStamp(levelType d2enum.RegionIdType, levelPreset, fil stamp.ds1, err = d2ds1.LoadDS1(fileData) if err != nil { - log.Print(err) + f.Error(err.Error()) return nil } diff --git a/d2core/d2records/cubemain_loader.go b/d2core/d2records/cubemain_loader.go index 6fbfea34..76692521 100644 --- a/d2core/d2records/cubemain_loader.go +++ b/d2core/d2records/cubemain_loader.go @@ -1,7 +1,7 @@ package d2records import ( - "log" + "fmt" "strconv" "strings" @@ -24,6 +24,11 @@ func cubeRecipeLoader(r *RecordManager, d *d2txt.DataDictionary) error { var inputFields = []string{"input 1", "input 2", "input 3", "input 4", "input 5", "input 6", "input 7"} for d.Next() { + class, err := classFieldToEnum(d.String("class")) + if err != nil { + return err + } + record := &CubeRecipeRecord{ Description: d.String("description"), @@ -37,7 +42,7 @@ func cubeRecipeLoader(r *RecordManager, d *d2txt.DataDictionary) error { ReqOperation: d.Number("op"), ReqValue: d.Number("value"), - Class: classFieldToEnum(d.String("class")), + Class: class, NumInputs: d.Number("numinputs"), } @@ -45,17 +50,25 @@ func cubeRecipeLoader(r *RecordManager, d *d2txt.DataDictionary) error { // Create inputs - input 1-7 record.Inputs = make([]CubeRecipeItem, len(inputFields)) for i := range inputFields { - record.Inputs[i] = newCubeRecipeItem( + record.Inputs[i], err = newCubeRecipeItem( d.String(inputFields[i])) + if err != nil { + return err + } } // Create outputs - output "", b, c record.Outputs = make([]CubeRecipeResult, len(outputLabels)) - for o, outLabel := range outputLabels { - record.Outputs[o] = CubeRecipeResult{ - Item: newCubeRecipeItem( - d.String(outputFields[o])), + for o, outLabel := range outputLabels { + item, err := newCubeRecipeItem( + d.String(outputFields[o])) + if err != nil { + return err + } + + record.Outputs[o] = CubeRecipeResult{ + Item: item, Level: d.Number(outLabel + "lvl"), ILevel: d.Number(outLabel + "plvl"), PLevel: d.Number(outLabel + "ilvl"), @@ -94,7 +107,7 @@ func cubeRecipeLoader(r *RecordManager, d *d2txt.DataDictionary) error { // arguments. arguments include at least an item and sometimes // parameters and/or a count (qty parameter). For example: // "weap,sock,mag,qty=10" -func newCubeRecipeItem(f string) CubeRecipeItem { +func newCubeRecipeItem(f string) (CubeRecipeItem, error) { args := splitFieldValue(f) item := CubeRecipeItem{ @@ -115,7 +128,8 @@ func newCubeRecipeItem(f string) CubeRecipeItem { count, err := strconv.Atoi(strings.Split(arg, "=")[1]) if err != nil { - log.Fatal("Error parsing item count:", err) + // need to be verified + return item, fmt.Errorf("error parsing item count %e", err) } item.Count = count @@ -132,7 +146,7 @@ func newCubeRecipeItem(f string) CubeRecipeItem { // No other arguments were provided if len(args) == 0 { - return item + return item, nil } // Record the argument strings @@ -141,11 +155,11 @@ func newCubeRecipeItem(f string) CubeRecipeItem { item.Params[idx] = arg } - return item + return item, nil } // classFieldToEnum converts class tokens to s2enum.Hero. -func classFieldToEnum(f string) []d2enum.Hero { +func classFieldToEnum(f string) ([]d2enum.Hero, error) { split := splitFieldValue(f) enums := make([]d2enum.Hero, len(split)) @@ -170,11 +184,11 @@ func classFieldToEnum(f string) []d2enum.Hero { case "dru": enums[idx] = d2enum.HeroDruid default: - log.Fatalf("Unknown hero token: '%s'", class) + return nil, fmt.Errorf("unknown hero token: '%s'", class) } } - return enums + return enums, nil } // splitFieldValue splits a string array from the following format: diff --git a/d2core/d2records/monster_stats2_loader.go b/d2core/d2records/monster_stats2_loader.go index fae69fe2..51981d27 100644 --- a/d2core/d2records/monster_stats2_loader.go +++ b/d2core/d2records/monster_stats2_loader.go @@ -1,7 +1,7 @@ package d2records import ( - "log" + "fmt" "github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum" "github.com/OpenDiablo2/OpenDiablo2/d2common/d2fileformats/d2txt" @@ -13,6 +13,11 @@ func monsterStats2Loader(r *RecordManager, d *d2txt.DataDictionary) error { records := make(MonStats2) for d.Next() { + resurrectMode, err := monsterAnimationModeFromString(d.String("ResurrectMode")) + if err != nil { + return err + } + record := &MonStats2Record{ Key: d.String("Id"), Height: d.Number("Height"), @@ -145,7 +150,7 @@ func monsterStats2Loader(r *RecordManager, d *d2txt.DataDictionary) error { InfernoLen: d.Number("InfernoLen"), InfernoAnim: d.Number("InfernoAnim"), InfernoRollback: d.Number("InfernoRollback"), - ResurrectMode: monsterAnimationModeFromString(d.String("ResurrectMode")), + ResurrectMode: resurrectMode, ResurrectSkill: d.String("ResurrectSkill"), } @@ -170,12 +175,11 @@ var monsterAnimationModeLookup = map[string]d2enum.MonsterAnimationMode{ d2enum.MonsterAnimationModeSequence.String(): d2enum.MonsterAnimationModeSequence, } -func monsterAnimationModeFromString(s string) d2enum.MonsterAnimationMode { +func monsterAnimationModeFromString(s string) (d2enum.MonsterAnimationMode, error) { v, ok := monsterAnimationModeLookup[s] if !ok { - log.Fatalf("unhandled MonsterAnimationMode %q", s) - return d2enum.MonsterAnimationModeNeutral + return d2enum.MonsterAnimationModeNeutral, fmt.Errorf("unhandled MonsterAnimationMode %q", s) } - return v + return v, nil } diff --git a/d2core/d2records/object_lookup_record_test.go b/d2core/d2records/object_lookup_record_test.go index 8a8b204a..ef6882ce 100644 --- a/d2core/d2records/object_lookup_record_test.go +++ b/d2core/d2records/object_lookup_record_test.go @@ -1,7 +1,6 @@ package d2records import ( - "log" "testing" "github.com/OpenDiablo2/OpenDiablo2/d2common/d2util" @@ -17,7 +16,7 @@ func TestIndexObjects(t *testing.T) { r, err := NewRecordManager(d2util.LogLevelDefault) if err != nil { - log.Print(err) + t.Error(err) } testObjects := []ObjectLookupRecord{ diff --git a/d2core/d2records/record_manager.go b/d2core/d2records/record_manager.go index 1d43bb25..716a4f4d 100644 --- a/d2core/d2records/record_manager.go +++ b/d2core/d2records/record_manager.go @@ -2,7 +2,6 @@ package d2records import ( "fmt" - "log" "github.com/OpenDiablo2/OpenDiablo2/d2common/d2util" @@ -37,7 +36,7 @@ func NewRecordManager(l d2util.LogLevel) (*RecordManager, error) { // RecordManager stores all of the records loaded from txt files type RecordManager struct { - Logger *d2util.Logger + *d2util.Logger boundLoaders map[string][]recordLoader // there can be more than one loader bound for a file Animation struct { Data d2data.AnimationData @@ -411,7 +410,7 @@ func (r *RecordManager) initObjectRecords(lookups []ObjectLookupRecord) { func (r *RecordManager) LookupObject(act, typ, id int) *ObjectLookupRecord { object := r.lookupObject(act, typ, id) if object == nil { - log.Panicf("Failed to look up object Act: %d, Type: %d, ID: %d", act, typ, id) + r.Fatalf("Failed to look up object Act: %d, Type: %d, ID: %d", act, typ, id) } return object diff --git a/d2core/d2records/skill_details_loader.go b/d2core/d2records/skill_details_loader.go index cc5e163e..da790eed 100644 --- a/d2core/d2records/skill_details_loader.go +++ b/d2core/d2records/skill_details_loader.go @@ -1,7 +1,7 @@ package d2records import ( - "log" + "fmt" "github.com/OpenDiablo2/OpenDiablo2/d2common/d2calculation/d2parser" "github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum" @@ -19,6 +19,11 @@ func skillDetailsLoader(r *RecordManager, d *d2txt.DataDictionary) error { name := d.String("skill") parser.SetCurrentReference("skill", name) + anim, err := animToEnum(d.String("anim")) + if err != nil { + return err + } + record := &SkillRecord{ Skill: d.String("skill"), ID: d.Number("Id"), @@ -141,7 +146,7 @@ func skillDetailsLoader(r *RecordManager, d *d2txt.DataDictionary) error { Itypeb3: d.String("itypeb3"), Etypeb1: d.String("etypeb1"), Etypeb2: d.String("etypeb2"), - Anim: animToEnum(d.String("anim")), + Anim: anim, Seqtrans: d.String("seqtrans"), Monanim: d.String("monanim"), Seqnum: d.Number("seqnum"), @@ -277,45 +282,42 @@ func skillDetailsLoader(r *RecordManager, d *d2txt.DataDictionary) error { return nil } -func animToEnum(anim string) d2enum.PlayerAnimationMode { +func animToEnum(anim string) (d2enum.PlayerAnimationMode, error) { switch anim { case "SC": - return d2enum.PlayerAnimationModeCast + return d2enum.PlayerAnimationModeCast, nil case "TH": - return d2enum.PlayerAnimationModeThrow + return d2enum.PlayerAnimationModeThrow, nil case "KK": - return d2enum.PlayerAnimationModeKick + return d2enum.PlayerAnimationModeKick, nil case "SQ": - return d2enum.PlayerAnimationModeSequence + return d2enum.PlayerAnimationModeSequence, nil case "S1": - return d2enum.PlayerAnimationModeSkill1 + return d2enum.PlayerAnimationModeSkill1, nil case "S2": - return d2enum.PlayerAnimationModeSkill2 + return d2enum.PlayerAnimationModeSkill2, nil case "S3": - return d2enum.PlayerAnimationModeSkill3 + return d2enum.PlayerAnimationModeSkill3, nil case "S4": - return d2enum.PlayerAnimationModeSkill4 + return d2enum.PlayerAnimationModeSkill4, nil case "A1": - return d2enum.PlayerAnimationModeAttack1 + return d2enum.PlayerAnimationModeAttack1, nil case "A2": - return d2enum.PlayerAnimationModeAttack2 + return d2enum.PlayerAnimationModeAttack2, nil case "": - return d2enum.PlayerAnimationModeNone - - default: - log.Fatalf("Unknown skill anim value [%s]", anim) + return d2enum.PlayerAnimationModeNone, nil } // should not be reached - return d2enum.PlayerAnimationModeNone + return d2enum.PlayerAnimationModeNone, fmt.Errorf("unknown skill anim value [%s]", anim) } diff --git a/d2core/d2screen/screen_manager.go b/d2core/d2screen/screen_manager.go index 9dfa54f5..0820c392 100644 --- a/d2core/d2screen/screen_manager.go +++ b/d2core/d2screen/screen_manager.go @@ -1,13 +1,16 @@ package d2screen import ( - "log" - "github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface" + "github.com/OpenDiablo2/OpenDiablo2/d2common/d2util" "github.com/OpenDiablo2/OpenDiablo2/d2core/d2gui" "github.com/OpenDiablo2/OpenDiablo2/d2core/d2ui" ) +const ( + logPrefix = "Screen Manager" +) + // ScreenManager manages game screens (main menu, credits, character select, game, etc) type ScreenManager struct { uiManager *d2ui.UIManager @@ -16,11 +19,22 @@ type ScreenManager struct { loadingState LoadingState currentScreen Screen guiManager *d2gui.GuiManager + + *d2util.Logger } // NewScreenManager creates a screen manager -func NewScreenManager(ui *d2ui.UIManager, guiManager *d2gui.GuiManager) *ScreenManager { - return &ScreenManager{uiManager: ui, guiManager: guiManager} +func NewScreenManager(ui *d2ui.UIManager, l d2util.LogLevel, guiManager *d2gui.GuiManager) *ScreenManager { + sm := &ScreenManager{ + uiManager: ui, + guiManager: guiManager, + } + + sm.Logger = d2util.NewLogger() + sm.Logger.SetPrefix(logPrefix) + sm.Logger.SetLevel(l) + + return sm } // SetNextScreen is about to set a given screen as next @@ -35,11 +49,11 @@ func (sm *ScreenManager) Advance(elapsed float64) error { // this call blocks execution and could lead to deadlock if a screen implements OnLoad incorreclty load, ok := <-sm.loadingState.updates if !ok { - log.Println("loadingState chan should not be closed while in a loading screen") + sm.Warning("loadingState chan should not be closed while in a loading screen") } if load.err != nil { - log.Printf("PROBLEM LOADING THE SCREEN: %v", load.err) + sm.Errorf("PROBLEM LOADING THE SCREEN: %v", load.err) return load.err } diff --git a/d2core/d2ui/button.go b/d2core/d2ui/button.go index 53ab88cc..41980630 100644 --- a/d2core/d2ui/button.go +++ b/d2core/d2ui/button.go @@ -445,7 +445,7 @@ func (ui *UIManager) NewButton(buttonType ButtonType, text string) *Button { buttonSprite, err := ui.NewSprite(buttonLayout.ResourceName, buttonLayout.PaletteName) if err != nil { - log.Print(err) + ui.Error(err.Error()) return nil } @@ -455,7 +455,7 @@ func (ui *UIManager) NewButton(buttonType ButtonType, text string) *Button { for i := 0; i < buttonLayout.XSegments; i++ { w, _, frameSizeErr := buttonSprite.GetFrameSize(i) if frameSizeErr != nil { - log.Print(frameSizeErr) + ui.Error(frameSizeErr.Error()) return nil } @@ -469,7 +469,7 @@ func (ui *UIManager) NewButton(buttonType ButtonType, text string) *Button { for i := 0; i < buttonLayout.YSegments; i++ { _, h, frameSizeErr := buttonSprite.GetFrameSize(i * buttonLayout.YSegments) if frameSizeErr != nil { - log.Print(frameSizeErr) + ui.Error(frameSizeErr.Error()) return nil } diff --git a/d2core/d2ui/checkbox.go b/d2core/d2ui/checkbox.go index 3a5d326a..c91bccbe 100644 --- a/d2core/d2ui/checkbox.go +++ b/d2core/d2ui/checkbox.go @@ -1,8 +1,6 @@ package d2ui import ( - "log" - "github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum" "github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface" "github.com/OpenDiablo2/OpenDiablo2/d2common/d2resource" @@ -35,13 +33,13 @@ func (ui *UIManager) NewCheckbox(checkState bool) *Checkbox { checkboxSprite, err := ui.NewSprite(d2resource.Checkbox, d2resource.PaletteFechar) if err != nil { - log.Print(err) + ui.Error(err.Error()) return nil } result.width, result.height, err = checkboxSprite.GetFrameSize(0) if err != nil { - log.Print(err) + ui.Error(err.Error()) return nil } diff --git a/d2core/d2ui/d2ui.go b/d2core/d2ui/d2ui.go index 91a9030f..72dcbcdf 100644 --- a/d2core/d2ui/d2ui.go +++ b/d2core/d2ui/d2ui.go @@ -2,12 +2,17 @@ package d2ui import ( "github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface" + "github.com/OpenDiablo2/OpenDiablo2/d2common/d2util" "github.com/OpenDiablo2/OpenDiablo2/d2core/d2asset" ) // CursorButton represents a mouse button type CursorButton uint8 +const ( + logPrefix = "UI Manager" +) + const ( // CursorButtonLeft represents the left mouse button CursorButtonLeft CursorButton = 1 @@ -30,6 +35,7 @@ func NewUIManager( asset *d2asset.AssetManager, renderer d2interface.Renderer, input d2interface.InputManager, + l d2util.LogLevel, audio d2interface.AudioProvider, ) *UIManager { ui := &UIManager{ @@ -39,5 +45,9 @@ func NewUIManager( audio: audio, } + ui.Logger = d2util.NewLogger() + ui.Logger.SetPrefix(logPrefix) + ui.Logger.SetLevel(l) + return ui } diff --git a/d2core/d2ui/frame.go b/d2core/d2ui/frame.go index 236978fb..4d66c1cd 100644 --- a/d2core/d2ui/frame.go +++ b/d2core/d2ui/frame.go @@ -1,8 +1,6 @@ package d2ui import ( - "log" - "github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface" "github.com/OpenDiablo2/OpenDiablo2/d2common/d2resource" "github.com/OpenDiablo2/OpenDiablo2/d2core/d2asset" @@ -69,6 +67,8 @@ func NewUIFrame( } frame.Load() + frame.asset.Logger.SetPrefix(logPrefix) // workaround + return frame } @@ -76,7 +76,7 @@ func NewUIFrame( func (u *UIFrame) Load() { sprite, err := u.manager.NewSprite(d2resource.Frame, d2resource.PaletteSky) if err != nil { - log.Print(err) + u.asset.Logger.Error(err.Error()) } u.frame = sprite @@ -111,7 +111,7 @@ func (u *UIFrame) calculateSize() { for i := range framesWidth { w, _, err := u.frame.GetFrameSize(framesWidth[i]) if err != nil { - log.Print(err) + u.asset.Logger.Error(err.Error()) } u.width += w @@ -120,7 +120,7 @@ func (u *UIFrame) calculateSize() { for i := range framesHeight { _, h, err := u.frame.GetFrameSize(framesHeight[i]) if err != nil { - log.Print(err) + u.asset.Logger.Error(err.Error()) } u.height += h @@ -132,11 +132,11 @@ func (u *UIFrame) Render(target d2interface.Surface) { switch u.frameOrientation { case FrameLeft: if err := u.renderLeft(target); err != nil { - log.Printf("Render error %e", err) + u.asset.Logger.Error("Render error" + err.Error()) } case FrameRight: if err := u.renderRight(target); err != nil { - log.Printf("Render error %e", err) + u.asset.Logger.Error("Render error" + err.Error()) } } } diff --git a/d2core/d2ui/label.go b/d2core/d2ui/label.go index 1e80cef1..1004a21c 100644 --- a/d2core/d2ui/label.go +++ b/d2core/d2ui/label.go @@ -2,7 +2,6 @@ package d2ui import ( "image/color" - "log" "regexp" "strings" @@ -20,13 +19,15 @@ type Label struct { font *d2asset.Font Color map[int]color.Color backgroundColor color.Color + + *d2util.Logger } // NewLabel creates a new instance of a UI label func (ui *UIManager) NewLabel(fontPath, palettePath string) *Label { font, err := ui.asset.LoadFont(fontPath+".tbl", fontPath+".dc6", palettePath) if err != nil { - log.Print(err) + ui.Error(err.Error()) return nil } @@ -37,6 +38,7 @@ func (ui *UIManager) NewLabel(fontPath, palettePath string) *Label { Alignment: HorizontalAlignLeft, Color: map[int]color.Color{0: color.White}, font: font, + Logger: ui.Logger, } result.bindManager(ui) @@ -75,7 +77,7 @@ func (v *Label) Render(target d2interface.Surface) { err := v.font.RenderText(character, target) if err != nil { - log.Print(err) + v.Error(err.Error()) } target.PushTranslation(charWidth, 0) @@ -160,7 +162,7 @@ func (v *Label) getAlignOffset(textWidth int) int { case HorizontalAlignRight: return -textWidth default: - log.Fatal("Invalid Alignment") + v.Fatal("Invalid Alignment") return 0 } } diff --git a/d2core/d2ui/scrollbar.go b/d2core/d2ui/scrollbar.go index 73181fc6..db41da5d 100644 --- a/d2core/d2ui/scrollbar.go +++ b/d2core/d2ui/scrollbar.go @@ -1,8 +1,6 @@ package d2ui import ( - "log" - "github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface" "github.com/OpenDiablo2/OpenDiablo2/d2common/d2resource" ) @@ -33,7 +31,7 @@ type Scrollbar struct { func (ui *UIManager) NewScrollbar(x, y, height int) *Scrollbar { scrollbarSprite, err := ui.NewSprite(d2resource.Scrollbar, d2resource.PaletteSky) if err != nil { - log.Print(err) + ui.Error(err.Error()) return nil } diff --git a/d2core/d2ui/sprite.go b/d2core/d2ui/sprite.go index cc864c21..91988891 100644 --- a/d2core/d2ui/sprite.go +++ b/d2core/d2ui/sprite.go @@ -4,17 +4,19 @@ import ( "fmt" "image" "image/color" - "log" "github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum" "github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface" "github.com/OpenDiablo2/OpenDiablo2/d2common/d2math" + "github.com/OpenDiablo2/OpenDiablo2/d2common/d2util" ) // Sprite is a positioned visual object. type Sprite struct { *BaseWidget animation d2interface.Animation + + *d2util.Logger } const ( @@ -34,7 +36,9 @@ func (ui *UIManager) NewSprite(animationPath, palettePath string) (*Sprite, erro return &Sprite{ BaseWidget: base, - animation: animation}, nil + animation: animation, + Logger: ui.Logger, + }, nil } // Render renders the sprite on the given surface @@ -70,7 +74,7 @@ func (s *Sprite) RenderSegmented(target d2interface.Surface, segmentsX, segments for x := 0; x < segmentsX; x++ { idx := x + y*segmentsX + frameOffset*segmentsX*segmentsY if err := s.animation.SetCurrentFrame(idx); err != nil { - log.Printf("SetCurrentFrame error %e", err) + s.Error("SetCurrentFrame error" + err.Error()) } target.PushTranslation(s.x+currentX, s.y+currentY) @@ -150,7 +154,7 @@ func (s *Sprite) SetCurrentFrame(frameIndex int) error { func (s *Sprite) Rewind() { err := s.animation.SetCurrentFrame(0) if err != nil { - log.Print(err) + s.Error(err.Error()) } } diff --git a/d2core/d2ui/textbox.go b/d2core/d2ui/textbox.go index 17edfe80..b1f6d2af 100644 --- a/d2core/d2ui/textbox.go +++ b/d2core/d2ui/textbox.go @@ -1,7 +1,6 @@ package d2ui import ( - "log" "strings" "time" @@ -29,7 +28,7 @@ type TextBox struct { func (ui *UIManager) NewTextbox() *TextBox { bgSprite, err := ui.NewSprite(d2resource.TextBox2, d2resource.PaletteUnits) if err != nil { - log.Print(err) + ui.Error(err.Error()) return nil } diff --git a/d2core/d2ui/ui_manager.go b/d2core/d2ui/ui_manager.go index db25bf50..68ce97e7 100644 --- a/d2core/d2ui/ui_manager.go +++ b/d2core/d2ui/ui_manager.go @@ -1,7 +1,6 @@ package d2ui import ( - "log" "sort" "github.com/OpenDiablo2/OpenDiablo2/d2core/d2asset" @@ -9,6 +8,7 @@ import ( "github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum" "github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface" "github.com/OpenDiablo2/OpenDiablo2/d2common/d2resource" + "github.com/OpenDiablo2/OpenDiablo2/d2common/d2util" ) // UIManager manages a collection of UI elements (buttons, textboxes, labels) @@ -25,6 +25,8 @@ type UIManager struct { CursorY int pressedWidget ClickableWidget clickSfx d2interface.SoundEffect + + *d2util.Logger } // Note: methods for creating buttons and stuff are in their respective files @@ -34,13 +36,13 @@ type UIManager struct { func (ui *UIManager) Initialize() { sfx, err := ui.audio.LoadSound(d2resource.SFXButtonClick, false, false) if err != nil { - log.Fatalf("failed to initialize ui: %v", err) + ui.Fatalf("failed to initialize ui: %v", err) } ui.clickSfx = sfx if err := ui.inputManager.BindHandler(ui); err != nil { - log.Fatalf("failed to initialize ui: %v", err) + ui.Fatalf("failed to initialize ui: %v", err) } } @@ -64,7 +66,7 @@ func (ui *UIManager) addWidgetGroup(group *WidgetGroup) { func (ui *UIManager) addWidget(widget Widget) { err := ui.inputManager.BindHandler(widget) if err != nil { - log.Print(err) + ui.Error(err.Error()) } clickable, ok := widget.(ClickableWidget) @@ -159,7 +161,7 @@ func (ui *UIManager) Advance(elapsed float64) { if widget.GetVisible() { err := widget.Advance(elapsed) if err != nil { - log.Print(err) + ui.Error(err.Error()) } } } diff --git a/d2game/d2gamescreen/game.go b/d2game/d2gamescreen/game.go index 7b6408e9..41abe231 100644 --- a/d2game/d2gamescreen/game.go +++ b/d2game/d2gamescreen/game.go @@ -72,13 +72,13 @@ func CreateGame( lastRegionType: d2enum.RegionNone, ticksSinceLevelCheck: 0, mapRenderer: d2maprenderer.CreateMapRenderer(asset, renderer, - gameClient.MapEngine, term, startX, startY), + gameClient.MapEngine, term, l, startX, startY), escapeMenu: d2player.NewEscapeMenu(navigator, renderer, audioProvider, ui, guiManager, asset, l, keyMap), inputManager: inputManager, audioProvider: audioProvider, renderer: renderer, terminal: term, - soundEngine: d2audio.NewSoundEngine(audioProvider, asset, term), + soundEngine: d2audio.NewSoundEngine(audioProvider, asset, l, term), uiManager: ui, guiManager: guiManager, keyMap: keyMap, diff --git a/d2game/d2gamescreen/map_engine_testing.go b/d2game/d2gamescreen/map_engine_testing.go index ac4dae7f..17167d28 100644 --- a/d2game/d2gamescreen/map_engine_testing.go +++ b/d2game/d2gamescreen/map_engine_testing.go @@ -116,6 +116,7 @@ func CreateMapEngineTest(currentRegion, audioProvider: audioProvider, screen: screen, playerStateFactory: heroStateFactory, + logLevel: l, } mapEngineTest.playerState = heroStateFactory.CreateTestGameState() @@ -152,7 +153,8 @@ type MapEngineTest struct { regionSpec regionSpec filesCount int - logger *d2util.Logger + logger *d2util.Logger + logLevel d2util.LogLevel } func (met *MapEngineTest) loadRegionByIndex(n, levelPreset, fileIndex int) { @@ -187,14 +189,14 @@ func (met *MapEngineTest) loadRegionByIndex(n, levelPreset, fileIndex int) { met.levelPreset = levelPreset } - mapGen, _ := d2mapgen.NewMapGenerator(met.asset, met.mapEngine) + mapGen, _ := d2mapgen.NewMapGenerator(met.asset, met.logLevel, met.mapEngine) met.mapGen = mapGen if n == 0 { met.mapEngine.SetSeed(time.Now().UnixNano()) met.mapGen.GenerateAct1Overworld() } else { - met.mapEngine = d2mapengine.CreateMapEngine(met.asset) // necessary for map name update + met.mapEngine = d2mapengine.CreateMapEngine(met.logLevel, met.asset) // necessary for map name update met.mapEngine.SetSeed(time.Now().UnixNano()) met.mapEngine.GenerateMap(d2enum.RegionIdType(n), levelPreset, fileIndex) } @@ -216,12 +218,12 @@ func (met *MapEngineTest) OnLoad(loading d2screen.LoadingState) { loading.Progress(twentyPercent) - met.mapEngine = d2mapengine.CreateMapEngine(met.asset) + met.mapEngine = d2mapengine.CreateMapEngine(met.logLevel, met.asset) loading.Progress(fiftyPercent) met.mapRenderer = d2maprenderer.CreateMapRenderer(met.asset, met.renderer, met.mapEngine, - met.terminal, 0.0, 0.0) + met.terminal, met.logLevel, 0.0, 0.0) loading.Progress(seventyPercent) met.loadRegionByIndex(met.currentRegion, met.levelPreset, met.fileIndex) diff --git a/d2game/d2player/key_binding_menu.go b/d2game/d2player/key_binding_menu.go index 6402fd94..e2d004f2 100644 --- a/d2game/d2player/key_binding_menu.go +++ b/d2game/d2player/key_binding_menu.go @@ -77,23 +77,23 @@ func NewKeyBindingMenu( ret.Box = d2gui.NewBox( asset, renderer, ui, ret.mainLayout, keyBindingMenuWidth, keyBindingMenuHeight, - keyBindingMenuX, keyBindingMenuY, "", + keyBindingMenuX, keyBindingMenuY, l, "", ) ret.Box.SetPadding(keyBindingMenuPaddingX, keyBindingSettingPaddingY) ret.Box.SetOptions([]*d2gui.LabelButton{ - d2gui.NewLabelButton(0, 0, "Cancel", d2util.Color(d2gui.ColorRed), func() { + d2gui.NewLabelButton(0, 0, "Cancel", d2util.Color(d2gui.ColorRed), d2util.LogLevelDefault, func() { if err := ret.onCancelClicked(); err != nil { ret.logger.Error("error while clicking option Cancel: %v" + err.Error()) } }), - d2gui.NewLabelButton(0, 0, "Default", d2util.Color(d2gui.ColorBlue), func() { + d2gui.NewLabelButton(0, 0, "Default", d2util.Color(d2gui.ColorBlue), d2util.LogLevelDefault, func() { if err := ret.onDefaultClicked(); err != nil { ret.logger.Error("error while clicking option Default: %v" + err.Error()) } }), - d2gui.NewLabelButton(0, 0, "Accept", d2util.Color(d2gui.ColorGreen), func() { + d2gui.NewLabelButton(0, 0, "Accept", d2util.Color(d2gui.ColorGreen), d2util.LogLevelDefault, func() { if err := ret.onAcceptClicked(); err != nil { ret.logger.Error("error while clicking option Accept: %v" + err.Error()) } diff --git a/d2networking/d2client/d2localclient/local_client_connection.go b/d2networking/d2client/d2localclient/local_client_connection.go index 3e0ec6c1..a1294d6c 100644 --- a/d2networking/d2client/d2localclient/local_client_connection.go +++ b/d2networking/d2client/d2localclient/local_client_connection.go @@ -3,6 +3,7 @@ package d2localclient import ( "github.com/google/uuid" + "github.com/OpenDiablo2/OpenDiablo2/d2common/d2util" "github.com/OpenDiablo2/OpenDiablo2/d2core/d2asset" "github.com/OpenDiablo2/OpenDiablo2/d2core/d2hero" @@ -22,16 +23,18 @@ type LocalClientConnection struct { openNetworkServer bool // True if this is a server playerState *d2hero.HeroState // Local player state gameServer *d2server.GameServer // Game Server + + logLevel d2util.LogLevel } // GetUniqueID returns LocalClientConnection.uniqueID. -func (l LocalClientConnection) GetUniqueID() string { +func (l *LocalClientConnection) GetUniqueID() string { return l.uniqueID } // GetConnectionType returns an enum representing the connection type. // See: d2clientconnectiontype -func (l LocalClientConnection) GetConnectionType() d2clientconnectiontype.ClientConnectionType { +func (l *LocalClientConnection) GetConnectionType() d2clientconnectiontype.ClientConnectionType { return d2clientconnectiontype.Local } @@ -42,7 +45,10 @@ func (l *LocalClientConnection) SendPacketToClient(packet d2netpacket.NetPacket) // Create constructs a new LocalClientConnection and returns // a pointer to it. -func Create(asset *d2asset.AssetManager, openNetworkServer bool) (*LocalClientConnection, error) { +func Create( + asset *d2asset.AssetManager, + l d2util.LogLevel, + openNetworkServer bool) (*LocalClientConnection, error) { heroStateFactory, err := d2hero.NewHeroStateFactory(asset) if err != nil { return nil, err @@ -53,6 +59,7 @@ func Create(asset *d2asset.AssetManager, openNetworkServer bool) (*LocalClientCo asset: asset, uniqueID: uuid.New().String(), openNetworkServer: openNetworkServer, + logLevel: l, } return result, nil @@ -64,7 +71,7 @@ func (l *LocalClientConnection) Open(_, saveFilePath string) error { l.SetPlayerState(l.heroState.LoadHeroState(saveFilePath)) - l.gameServer, err = d2server.NewGameServer(l.asset, l.openNetworkServer, 30) + l.gameServer, err = d2server.NewGameServer(l.asset, l.openNetworkServer, l.logLevel, 30) if err != nil { return err } diff --git a/d2networking/d2client/d2remoteclient/remote_client_connection.go b/d2networking/d2client/d2remoteclient/remote_client_connection.go index 96bd2ea4..1fefad1f 100644 --- a/d2networking/d2client/d2remoteclient/remote_client_connection.go +++ b/d2networking/d2client/d2remoteclient/remote_client_connection.go @@ -3,12 +3,12 @@ package d2remoteclient import ( "encoding/json" "fmt" - "log" "net" "strings" "github.com/google/uuid" + "github.com/OpenDiablo2/OpenDiablo2/d2common/d2util" "github.com/OpenDiablo2/OpenDiablo2/d2core/d2asset" "github.com/OpenDiablo2/OpenDiablo2/d2core/d2hero" "github.com/OpenDiablo2/OpenDiablo2/d2networking" @@ -17,6 +17,8 @@ import ( "github.com/OpenDiablo2/OpenDiablo2/d2networking/d2netpacket/d2netpackettype" ) +const logPrefix = "Remote Client" + // RemoteClientConnection is the implementation of ClientConnection // for a remote client. type RemoteClientConnection struct { @@ -26,11 +28,13 @@ type RemoteClientConnection struct { uniqueID string // Unique ID generated on construction tcpConnection *net.TCPConn // UDP connection to the server active bool // The connection is currently open + + *d2util.Logger } // Create constructs a new RemoteClientConnection // and returns a pointer to it. -func Create(asset *d2asset.AssetManager) (*RemoteClientConnection, error) { +func Create(l d2util.LogLevel, asset *d2asset.AssetManager) (*RemoteClientConnection, error) { heroStateFactory, err := d2hero.NewHeroStateFactory(asset) if err != nil { return nil, err @@ -42,6 +46,10 @@ func Create(asset *d2asset.AssetManager) (*RemoteClientConnection, error) { uniqueID: uuid.New().String(), } + result.Logger = d2util.NewLogger() + result.Logger.SetPrefix(logPrefix) + result.Logger.SetLevel(l) + return result, nil } @@ -66,14 +74,14 @@ func (r *RemoteClientConnection) Open(connectionString, saveFilePath string) err r.active = true go r.serverListener() - log.Printf("Connected to server at %s", r.tcpConnection.RemoteAddr().String()) + r.Infof("Connected to server at %s", r.tcpConnection.RemoteAddr().String()) gameState := r.heroState.LoadHeroState(saveFilePath) packet := d2netpacket.CreatePlayerConnectionRequestPacket(r.GetUniqueID(), gameState) err = r.SendPacketToServer(packet) if err != nil { - log.Print("RemoteClientConnection: error sending PlayerConnectionRequestPacket to server.") + r.Errorf("RemoteClientConnection: error sending PlayerConnectionRequestPacket to server.") return err } @@ -134,18 +142,18 @@ func (r *RemoteClientConnection) serverListener() { for { err := decoder.Decode(&packet) if err != nil { - log.Printf("failed to decode the packet, err: %v\n", err) + r.Errorf("failed to decode the packet, err: %v\n", err) return } p, err := r.decodeToPacket(packet.PacketType, string(packet.PacketData)) if err != nil { - log.Println(packet.PacketType, err) + r.Error(fmt.Sprintln(packet.PacketType, err)) } err = r.clientListener.OnPacketReceived(p) if err != nil { - log.Println(packet.PacketType, err) + r.Error(fmt.Sprintln(packet.PacketType, err)) } } } diff --git a/d2networking/d2client/game_client.go b/d2networking/d2client/game_client.go index 1d22470e..5bc8bbe1 100644 --- a/d2networking/d2client/game_client.go +++ b/d2networking/d2client/game_client.go @@ -2,7 +2,6 @@ package d2client import ( "fmt" - "log" "os" "github.com/OpenDiablo2/OpenDiablo2/d2core/d2hero" @@ -15,6 +14,7 @@ import ( "github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum" "github.com/OpenDiablo2/OpenDiablo2/d2common/d2math/d2vector" + "github.com/OpenDiablo2/OpenDiablo2/d2common/d2util" "github.com/OpenDiablo2/OpenDiablo2/d2core/d2map/d2mapengine" "github.com/OpenDiablo2/OpenDiablo2/d2core/d2map/d2mapentity" "github.com/OpenDiablo2/OpenDiablo2/d2core/d2records" @@ -26,6 +26,8 @@ import ( "github.com/OpenDiablo2/OpenDiablo2/d2script" ) +const logPrefix = "Game Client" + const ( numSubtilesPerTile = 5 ) @@ -44,24 +46,32 @@ type GameClient struct { Players map[string]*d2mapentity.Player // IDs of the other players Seed int64 // Map seed RegenMap bool // Regenerate tile cache on render (map has changed) + + *d2util.Logger } // Create constructs a new GameClient and returns a pointer to it. func Create(connectionType d2clientconnectiontype.ClientConnectionType, - asset *d2asset.AssetManager, scriptEngine *d2script.ScriptEngine) (*GameClient, error) { + asset *d2asset.AssetManager, + l d2util.LogLevel, + scriptEngine *d2script.ScriptEngine) (*GameClient, error) { result := &GameClient{ asset: asset, - MapEngine: d2mapengine.CreateMapEngine(asset), + MapEngine: d2mapengine.CreateMapEngine(l, asset), Players: make(map[string]*d2mapentity.Player), connectionType: connectionType, scriptEngine: scriptEngine, } + result.Logger = d2util.NewLogger() + result.Logger.SetPrefix(logPrefix) + result.Logger.SetLevel(l) + // for a remote client connection, set loading to true - wait until we process the GenerateMapPacket // before we start updating map entites result.MapEngine.IsLoading = connectionType == d2clientconnectiontype.LANClient - mapGen, err := d2mapgen.NewMapGenerator(asset, result.MapEngine) + mapGen, err := d2mapgen.NewMapGenerator(asset, l, result.MapEngine) if err != nil { return nil, err } @@ -70,11 +80,11 @@ func Create(connectionType d2clientconnectiontype.ClientConnectionType, switch connectionType { case d2clientconnectiontype.LANClient: - result.clientConnection, err = d2remoteclient.Create(asset) + result.clientConnection, err = d2remoteclient.Create(l, asset) case d2clientconnectiontype.LANServer: - result.clientConnection, err = d2localclient.Create(asset, true) + result.clientConnection, err = d2localclient.Create(asset, l, true) case d2clientconnectiontype.Local: - result.clientConnection, err = d2localclient.Create(asset, false) + result.clientConnection, err = d2localclient.Create(asset, l, false) default: err = fmt.Errorf("unknown client connection type specified: %d", connectionType) } @@ -147,20 +157,20 @@ func (g *GameClient) OnPacketReceived(packet d2netpacket.NetPacket) error { } case d2netpackettype.Ping: if err := g.handlePingPacket(); err != nil { - log.Printf("GameClient: error responding to server ping: %s", err) + g.Errorf("GameClient: error responding to server ping: %s", err) } case d2netpackettype.PlayerDisconnectionNotification: // Not implemented - log.Printf("RemoteClientConnection: received disconnect: %s", packet.PacketData) + g.Infof("RemoteClientConnection: received disconnect: %s", packet.PacketData) case d2netpackettype.ServerClosed: // https://github.com/OpenDiablo2/OpenDiablo2/issues/802 - log.Print("Server has been closed") + g.Infof("Server has been closed") os.Exit(0) case d2netpackettype.ServerFull: - log.Println("Server is full") + g.Infof("Server is full") // need to be verified os.Exit(0) default: - log.Fatalf("Invalid packet type: %d", packet.PacketType) + g.Fatalf("Invalid packet type: %d", packet.PacketType) } return nil @@ -196,7 +206,7 @@ func (g *GameClient) handleUpdateServerInfoPacket(packet d2netpacket.NetPacket) g.MapEngine.SetSeed(serverInfo.Seed) g.PlayerID = serverInfo.PlayerID g.Seed = serverInfo.Seed - log.Printf("Player id set to %s", serverInfo.PlayerID) + g.Infof("Player id set to %s", serverInfo.PlayerID) return nil } @@ -259,7 +269,7 @@ func (g *GameClient) handleMovePlayerPacket(packet d2netpacket.NetPacket) error if err != nil { fmtStr := "GameClient: error setting animation mode for player %s: %s" - log.Printf(fmtStr, player.ID(), err) + g.Errorf(fmtStr, player.ID(), err) } }) } diff --git a/d2networking/d2server/game_server.go b/d2networking/d2server/game_server.go index 867a96b5..47cbdde2 100644 --- a/d2networking/d2server/game_server.go +++ b/d2networking/d2server/game_server.go @@ -5,7 +5,6 @@ import ( "encoding/json" "errors" "fmt" - "log" "net" "sync" "time" @@ -13,6 +12,7 @@ import ( "github.com/robertkrimen/otto" "github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum" + "github.com/OpenDiablo2/OpenDiablo2/d2common/d2util" "github.com/OpenDiablo2/OpenDiablo2/d2core/d2asset" "github.com/OpenDiablo2/OpenDiablo2/d2core/d2hero" "github.com/OpenDiablo2/OpenDiablo2/d2core/d2map/d2mapengine" @@ -23,6 +23,8 @@ import ( "github.com/OpenDiablo2/OpenDiablo2/d2script" ) +const logPrefix = "Game Server" + const ( port = "6669" chunkSize int = 4096 // nolint:deadcode,unused,varcheck // WIP @@ -51,6 +53,8 @@ type GameServer struct { maxConnections int packetManagerChan chan []byte heroStateFactory *d2hero.HeroStateFactory + + *d2util.Logger } // NewGameServer builds a new GameServer that can be started @@ -58,7 +62,9 @@ type GameServer struct { // ctx: required context item // networkServer: true = 0.0.0.0 | false = 127.0.0.1 // maxConnections (default: 8): maximum number of TCP connections allowed open -func NewGameServer(asset *d2asset.AssetManager, networkServer bool, +func NewGameServer(asset *d2asset.AssetManager, + networkServer bool, + l d2util.LogLevel, maxConnections ...int) (*GameServer, error) { if len(maxConnections) == 0 { @@ -86,11 +92,15 @@ func NewGameServer(asset *d2asset.AssetManager, networkServer bool, heroStateFactory: heroStateFactory, } - mapEngine := d2mapengine.CreateMapEngine(asset) + gameServer.Logger = d2util.NewLogger() + gameServer.Logger.SetPrefix(logPrefix) + gameServer.Logger.SetLevel(l) + + mapEngine := d2mapengine.CreateMapEngine(l, asset) mapEngine.SetSeed(gameServer.seed) mapEngine.ResetMap(d2enum.RegionAct1Town, 100, 100) - mapGen, err := d2mapgen.NewMapGenerator(asset, mapEngine) + mapGen, err := d2mapgen.NewMapGenerator(asset, l, mapEngine) if err != nil { return nil, err } @@ -118,7 +128,7 @@ func (g *GameServer) Start() error { listenerAddress = "0.0.0.0:" + port } - log.Printf("Starting Game Server @ %s\n", listenerAddress) + g.Infof("Starting Game Server @ %s\n", listenerAddress) l, err := net.Listen("tcp4", listenerAddress) if err != nil { @@ -133,7 +143,7 @@ func (g *GameServer) Start() error { for { c, err := g.listener.Accept() if err != nil { - log.Printf("Unable to accept connection: %s\n", err) + g.Errorf("Unable to accept connection: %s", err) return } @@ -150,7 +160,7 @@ func (g *GameServer) Stop() { g.cancel() if err := g.listener.Close(); err != nil { - log.Printf("failed to close the listener %s, err: %v\n", g.listener.Addr(), err) + g.Errorf("failed to close the listener %s, err: %v\n", g.listener.Addr(), err) } } @@ -168,14 +178,14 @@ func (g *GameServer) packetManager() { case d2netpackettype.PlayerConnectionRequest: player, err := d2netpacket.UnmarshalNetPacket(p) if err != nil { - log.Printf("Unable to unmarshal PlayerConnectionRequestPacket: %s\n", err) + g.Errorf("Unable to unmarshal PlayerConnectionRequestPacket: %s\n", err) } g.sendPacketToClients(player) case d2netpackettype.MovePlayer: move, err := d2netpacket.UnmarshalNetPacket(p) if err != nil { - log.Println(err) + g.Error(err.Error()) continue } @@ -183,7 +193,7 @@ func (g *GameServer) packetManager() { case d2netpackettype.CastSkill: castSkill, err := d2netpacket.UnmarshalNetPacket(p) if err != nil { - log.Println(err) + g.Error(err.Error()) continue } @@ -191,7 +201,7 @@ func (g *GameServer) packetManager() { case d2netpackettype.SpawnItem: item, err := d2netpacket.UnmarshalNetPacket(p) if err != nil { - log.Println(err) + g.Error(err.Error()) continue } @@ -206,7 +216,7 @@ func (g *GameServer) packetManager() { func (g *GameServer) sendPacketToClients(packet d2netpacket.NetPacket) { for _, c := range g.connections { if err := c.SendPacketToClient(packet); err != nil { - log.Printf("GameServer: error sending packet: %s to client %s: %s", packet.PacketType, c.GetUniqueID(), err) + g.Errorf("GameServer: error sending packet: %s to client %s: %s", packet.PacketType, c.GetUniqueID(), err) } } } @@ -218,11 +228,11 @@ func (g *GameServer) handleConnection(conn net.Conn) { var packet d2netpacket.NetPacket - log.Printf("Accepting connection: %s\n", conn.RemoteAddr().String()) + g.Infof("Accepting connection: %s\n", conn.RemoteAddr().String()) defer func() { if err := conn.Close(); err != nil { - log.Printf("failed to close the connection: %s\n", conn.RemoteAddr()) + g.Errorf("failed to close the connection: %s\n", conn.RemoteAddr()) } }() @@ -231,7 +241,7 @@ func (g *GameServer) handleConnection(conn net.Conn) { for { err := decoder.Decode(&packet) if err != nil { - log.Println(err) + g.Error(err.Error()) return // exit this connection as we could not read the first packet } @@ -240,16 +250,16 @@ func (g *GameServer) handleConnection(conn net.Conn) { // to. if connected == 0 { if packet.PacketType != d2netpackettype.PlayerConnectionRequest { - log.Printf("Closing connection with %s: did not receive new player connection request...\n", conn.RemoteAddr().String()) + g.Infof("Closing connection with %s: did not receive new player connection request...", conn.RemoteAddr().String()) } if err := g.registerConnection(packet.PacketData, conn); err != nil { switch err { case errServerFull: // Server is currently full and not accepting new connections. _, errServerFullPacket := conn.Write(d2netpacket.MarshalPacket(d2netpacket.CreateServerFullPacket())) - log.Println(errServerFullPacket) + g.Error(fmt.Sprintln(errServerFullPacket)) case errPlayerAlreadyExists: // Player is already registered and did not disconnection correctly. - log.Println(err) + g.Error(fmt.Sprintln(err)) } return @@ -283,7 +293,7 @@ func (g *GameServer) registerConnection(b []byte, conn net.Conn) error { // if it is not full, unmarshal the playerConnectionRequest packet, err := d2netpacket.UnmarshalPlayerConnectionRequest(b) if err != nil { - log.Printf("Failed to unmarshal PlayerConnectionRequest: %s\n", err) + g.Errorf("Failed to unmarshal PlayerConnectionRequest: %s\n", err) } // check to see if the player is already registered @@ -294,7 +304,7 @@ func (g *GameServer) registerConnection(b []byte, conn net.Conn) error { // Client a new TCP Client Connection and add it to the connections map client := d2tcpclientconnection.CreateTCPClientConnection(conn, packet.ID) client.SetPlayerState(packet.PlayerState) - log.Printf("Client connected with an id of %s", client.GetUniqueID()) + g.Infof("Client connected with an id of %s", client.GetUniqueID()) g.connections[client.GetUniqueID()] = client // Temporary position hack -------------------------------------------- @@ -330,7 +340,7 @@ func (g *GameServer) OnClientConnected(client ClientConnection) { clientPlayerState.Y = sy // -------------------------------------------------------------------- - log.Printf("Client connected with an id of %s", client.GetUniqueID()) + g.Infof("Client connected with an id of %s", client.GetUniqueID()) g.connections[client.GetUniqueID()] = client g.handleClientConnection(client, sx, sy) @@ -339,12 +349,12 @@ func (g *GameServer) OnClientConnected(client ClientConnection) { func (g *GameServer) handleClientConnection(client ClientConnection, x, y float64) { err := client.SendPacketToClient(d2netpacket.CreateUpdateServerInfoPacket(g.seed, client.GetUniqueID())) if err != nil { - log.Printf("GameServer: error sending UpdateServerInfoPacket to client %s: %s", client.GetUniqueID(), err) + g.Errorf("GameServer: error sending UpdateServerInfoPacket to client %s: %s", client.GetUniqueID(), err) } err = client.SendPacketToClient(d2netpacket.CreateGenerateMapPacket(d2enum.RegionAct1Town)) if err != nil { - log.Printf("GameServer: error sending GenerateMapPacket to client %s: %s", client.GetUniqueID(), err) + g.Errorf("GameServer: error sending GenerateMapPacket to client %s: %s", client.GetUniqueID(), err) } playerState := client.GetPlayerState() @@ -371,7 +381,7 @@ func (g *GameServer) handleClientConnection(client ClientConnection, x, y float6 for _, connection := range g.connections { err := connection.SendPacketToClient(createPlayerPacket) if err != nil { - log.Printf("GameServer: error sending %T to client %s: %s", createPlayerPacket, connection.GetUniqueID(), err) + g.Errorf("GameServer: error sending %T to client %s: %s", createPlayerPacket, connection.GetUniqueID(), err) } if connection.GetUniqueID() == client.GetUniqueID() { @@ -397,7 +407,7 @@ func (g *GameServer) handleClientConnection(client ClientConnection, x, y float6 ) if err != nil { - log.Printf("GameServer: error sending CreateAddPlayerPacket to client %s: %s", connection.GetUniqueID(), err) + g.Errorf("GameServer: error sending CreateAddPlayerPacket to client %s: %s", connection.GetUniqueID(), err) } } } @@ -405,7 +415,7 @@ func (g *GameServer) handleClientConnection(client ClientConnection, x, y float6 // OnClientDisconnected removes the given client from the list // of client connections. func (g *GameServer) OnClientDisconnected(client ClientConnection) { - log.Printf("Client disconnected with an id of %s", client.GetUniqueID()) + g.Infof("Client disconnected with an id of %s", client.GetUniqueID()) delete(g.connections, client.GetUniqueID()) } @@ -443,10 +453,10 @@ func (g *GameServer) OnPacketReceived(client ClientConnection, packet d2netpacke err = g.heroStateFactory.Save(playerState) if err != nil { - log.Printf("GameServer: error saving saving Player: %s", err) + g.Errorf("GameServer: error saving saving Player: %s", err) } default: - log.Printf("GameServer: received unknown packet %T", packet) + g.Warningf("GameServer: received unknown packet %T", packet) } return nil diff --git a/d2networking/dedicated_server.go b/d2networking/dedicated_server.go index 8059da50..9eb8e3e7 100644 --- a/d2networking/dedicated_server.go +++ b/d2networking/dedicated_server.go @@ -3,6 +3,7 @@ package d2networking import ( "os" + "github.com/OpenDiablo2/OpenDiablo2/d2common/d2util" "github.com/OpenDiablo2/OpenDiablo2/d2core/d2asset" "github.com/OpenDiablo2/OpenDiablo2/d2networking/d2server" ) @@ -33,9 +34,10 @@ func StartDedicatedServer( manager *d2asset.AssetManager, in chan int, log chan string, + l d2util.LogLevel, maxPlayers int, ) error { - server, err := d2server.NewGameServer(manager, true, maxPlayers) + server, err := d2server.NewGameServer(manager, true, l, maxPlayers) if err != nil { return err }