1
1
mirror of https://github.com/OpenDiablo2/OpenDiablo2 synced 2024-12-25 11:36:26 -05:00

removed singleton screen manager instance (#696)

This commit is contained in:
lord 2020-08-05 19:31:56 -07:00 committed by GitHub
parent 4507b1e1c1
commit ca7412aea6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 155 additions and 138 deletions

View File

@ -6,7 +6,6 @@ import (
"container/ring"
"errors"
"fmt"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2math"
"image"
"image/gif"
"image/png"
@ -26,6 +25,7 @@ import (
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2data"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2data/d2datadict"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2math"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2resource"
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2asset"
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2config"
@ -63,6 +63,7 @@ type App struct {
scriptEngine *d2script.ScriptEngine
audio d2interface.AudioProvider
renderer d2interface.Renderer
screen *d2screen.ScreenManager
tAllocSamples *ring.Ring
}
@ -93,6 +94,7 @@ func Create(gitBranch, gitCommit string,
scriptEngine: scriptEngine,
audio: audio,
renderer: renderer,
screen: d2screen.NewScreenManager(),
tAllocSamples: createZeroedRing(nSamplesTAlloc),
}
@ -362,7 +364,7 @@ func (a *App) renderCapture(target d2interface.Surface) error {
}
func (a *App) render(target d2interface.Surface) error {
if err := d2screen.Render(target); err != nil {
if err := a.screen.Render(target); err != nil {
return err
}
@ -392,7 +394,7 @@ func (a *App) advance(elapsed, elapsedUnscaled, current float64) error {
a.lastScreenAdvance = current
if err := d2screen.Advance(elapsedLastScreenAdvance); err != nil {
if err := a.screen.Advance(elapsedLastScreenAdvance); err != nil {
return err
}
@ -597,7 +599,7 @@ func (a *App) quitGame() {
}
func (a *App) enterGuiPlayground() {
d2screen.SetNextScreen(d2gamescreen.CreateGuiTestMain(a.renderer))
a.screen.SetNextScreen(d2gamescreen.CreateGuiTestMain(a.renderer))
}
func createZeroedRing(n int) *ring.Ring {
@ -668,13 +670,13 @@ func (a *App) ToMainMenu() {
buildInfo := d2gamescreen.BuildInfo{Branch: a.gitBranch, Commit: a.gitCommit}
mainMenu := d2gamescreen.CreateMainMenu(a, a.renderer, a.inputManager, a.audio, buildInfo)
mainMenu.SetScreenMode(d2gamescreen.ScreenModeMainMenu)
d2screen.SetNextScreen(mainMenu)
a.screen.SetNextScreen(mainMenu)
}
// ToSelectHero forces the game to transition to the Select Hero (create character) screen
func (a *App) ToSelectHero(connType d2clientconnectiontype.ClientConnectionType, host string) {
selectHero := d2gamescreen.CreateSelectHeroClass(a, a.renderer, a.audio, connType, host)
d2screen.SetNextScreen(selectHero)
a.screen.SetNextScreen(selectHero)
}
// ToCreateGame forces the game to transition to the Create Game screen
@ -686,23 +688,23 @@ func (a *App) ToCreateGame(filePath string, connType d2clientconnectiontype.Clie
fmt.Printf("can not connect to the host: %s", host)
}
d2screen.SetNextScreen(d2gamescreen.CreateGame(a, a.renderer, a.inputManager, a.audio, gameClient, a.terminal))
a.screen.SetNextScreen(d2gamescreen.CreateGame(a, a.renderer, a.inputManager, a.audio, gameClient, a.terminal))
}
// ToCharacterSelect forces the game to transition to the Character Select (load character) screen
func (a *App) ToCharacterSelect(connType d2clientconnectiontype.ClientConnectionType, connHost string) {
characterSelect := d2gamescreen.CreateCharacterSelect(a, a.renderer, a.inputManager, a.audio, connType, connHost)
d2screen.SetNextScreen(characterSelect)
a.screen.SetNextScreen(characterSelect)
}
// ToMapEngineTest forces the game to transition to the map engine test screen
func (a *App) ToMapEngineTest(region, level int) {
met := d2gamescreen.CreateMapEngineTest(region, level, a.terminal, a.renderer, a.inputManager,
a.audio)
d2screen.SetNextScreen(met)
a.audio, a.screen)
a.screen.SetNextScreen(met)
}
// ToCredits forces the game to transition to the credits screen
func (a *App) ToCredits() {
d2screen.SetNextScreen(d2gamescreen.CreateCredits(a, a.renderer))
a.screen.SetNextScreen(d2gamescreen.CreateCredits(a, a.renderer))
}

View File

@ -1,14 +1,14 @@
package d2screen
import (
"log"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface"
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2gui"
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2ui"
)
// NewScreenManager creates a screen manager
func NewScreenManager() *ScreenManager {
return &ScreenManager{}
}
// Screen is an exported interface
type Screen interface{}
@ -34,119 +34,3 @@ type ScreenRenderHandler interface {
type ScreenAdvanceHandler interface {
Advance(elapsed float64) error
}
var singleton struct {
nextScreen Screen
loadingScreen Screen
loadingState LoadingState
currentScreen Screen
}
// SetNextScreen is about to set a given screen as next
func SetNextScreen(screen Screen) {
singleton.nextScreen = screen
}
// Advance updates the UI on every frame
func Advance(elapsed float64) error {
switch {
case singleton.loadingScreen != nil:
// this call blocks execution and could lead to deadlock if a screen implements OnLoad incorreclty
load, ok := <-singleton.loadingState.updates
if !ok {
log.Println("loadingState chan should not be closed while in a loading screen")
}
if load.err != nil {
log.Printf("PROBLEM LOADING THE SCREEN: %v", load.err)
return load.err
}
d2gui.ShowLoadScreen(load.progress)
if load.done {
singleton.currentScreen = singleton.loadingScreen
singleton.loadingScreen = nil
d2gui.ShowCursor()
d2gui.HideLoadScreen()
}
case singleton.nextScreen != nil:
if handler, ok := singleton.currentScreen.(ScreenUnloadHandler); ok {
if err := handler.OnUnload(); err != nil {
return err
}
}
d2ui.Reset()
d2gui.SetLayout(nil)
if handler, ok := singleton.nextScreen.(ScreenLoadHandler); ok {
d2gui.ShowLoadScreen(0)
d2gui.HideCursor()
singleton.loadingState = LoadingState{updates: make(chan loadingUpdate)}
go func() {
handler.OnLoad(singleton.loadingState)
singleton.loadingState.Done()
}()
singleton.currentScreen = nil
singleton.loadingScreen = singleton.nextScreen
} else {
singleton.currentScreen = singleton.nextScreen
singleton.loadingScreen = nil
}
singleton.nextScreen = nil
case singleton.currentScreen != nil:
if handler, ok := singleton.currentScreen.(ScreenAdvanceHandler); ok {
if err := handler.Advance(elapsed); err != nil {
return err
}
}
}
return nil
}
// Render renders the UI by a given surface
func Render(surface d2interface.Surface) error {
if handler, ok := singleton.currentScreen.(ScreenRenderHandler); ok {
if err := handler.Render(surface); err != nil {
return err
}
}
return nil
}
// LoadingState represents the loading state
type LoadingState struct {
updates chan loadingUpdate
}
const progressCompleted = 1.0
type loadingUpdate struct {
progress float64
err error
done bool
}
// Error provides a way for callers to report an error during loading.
func (l *LoadingState) Error(err error) {
l.updates <- loadingUpdate{err: err}
}
// Progress provides a way for callers to report the ratio between `0` and `1` of the progress made loading a screen.
func (l *LoadingState) Progress(ratio float64) {
l.updates <- loadingUpdate{progress: ratio}
}
// Done provides a way for callers to report that screen loading has been completed.
func (l *LoadingState) Done() {
l.updates <- loadingUpdate{progress: progressCompleted}
l.updates <- loadingUpdate{done: true}
}

View File

@ -0,0 +1,6 @@
package d2screen
// LoadingState represents the loading state
type LoadingState struct {
updates chan loadingUpdate
}

View File

@ -0,0 +1,25 @@
package d2screen
const progressCompleted = 1.0
type loadingUpdate struct {
progress float64
err error
done bool
}
// Error provides a way for callers to report an error during loading.
func (l *LoadingState) Error(err error) {
l.updates <- loadingUpdate{err: err}
}
// Progress provides a way for callers to report the ratio between `0` and `1` of the progress made loading a screen.
func (l *LoadingState) Progress(ratio float64) {
l.updates <- loadingUpdate{progress: ratio}
}
// Done provides a way for callers to report that screen loading has been completed.
func (l *LoadingState) Done() {
l.updates <- loadingUpdate{progress: progressCompleted}
l.updates <- loadingUpdate{done: true}
}

View File

@ -0,0 +1,97 @@
package d2screen
import (
"log"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface"
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2gui"
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2ui"
)
// ScreenManager manages game screens (main menu, credits, character select, game, etc)
type ScreenManager struct {
nextScreen Screen
loadingScreen Screen
loadingState LoadingState
currentScreen Screen
}
// SetNextScreen is about to set a given screen as next
func (sm *ScreenManager) SetNextScreen(screen Screen) {
sm.nextScreen = screen
}
// Advance updates the UI on every frame
func (sm *ScreenManager) Advance(elapsed float64) error {
switch {
case sm.loadingScreen != nil:
// 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")
}
if load.err != nil {
log.Printf("PROBLEM LOADING THE SCREEN: %v", load.err)
return load.err
}
d2gui.ShowLoadScreen(load.progress)
if load.done {
sm.currentScreen = sm.loadingScreen
sm.loadingScreen = nil
d2gui.ShowCursor()
d2gui.HideLoadScreen()
}
case sm.nextScreen != nil:
if handler, ok := sm.currentScreen.(ScreenUnloadHandler); ok {
if err := handler.OnUnload(); err != nil {
return err
}
}
d2ui.Reset()
d2gui.SetLayout(nil)
if handler, ok := sm.nextScreen.(ScreenLoadHandler); ok {
d2gui.ShowLoadScreen(0)
d2gui.HideCursor()
sm.loadingState = LoadingState{updates: make(chan loadingUpdate)}
go func() {
handler.OnLoad(sm.loadingState)
sm.loadingState.Done()
}()
sm.currentScreen = nil
sm.loadingScreen = sm.nextScreen
} else {
sm.currentScreen = sm.nextScreen
sm.loadingScreen = nil
}
sm.nextScreen = nil
case sm.currentScreen != nil:
if handler, ok := sm.currentScreen.(ScreenAdvanceHandler); ok {
if err := handler.Advance(elapsed); err != nil {
return err
}
}
}
return nil
}
// Render renders the UI by a given surface
func (sm *ScreenManager) Render(surface d2interface.Surface) error {
if handler, ok := sm.currentScreen.(ScreenRenderHandler); ok {
if err := handler.Render(surface); err != nil {
return err
}
}
return nil
}

View File

@ -94,6 +94,7 @@ type MapEngineTest struct {
renderer d2interface.Renderer
inputManager d2interface.InputManager
audioProvider d2interface.AudioProvider
screen *d2screen.ScreenManager
lastMouseX, lastMouseY int
selX, selY int
@ -115,6 +116,7 @@ func CreateMapEngineTest(currentRegion,
renderer d2interface.Renderer,
inputManager d2interface.InputManager,
audioProvider d2interface.AudioProvider,
screen *d2screen.ScreenManager,
) *MapEngineTest {
result := &MapEngineTest{
currentRegion: currentRegion,
@ -126,6 +128,7 @@ func CreateMapEngineTest(currentRegion,
renderer: renderer,
inputManager: inputManager,
audioProvider: audioProvider,
screen: screen,
}
result.gameState = d2player.CreateTestGameState()
@ -428,13 +431,13 @@ func (met *MapEngineTest) OnKeyDown(event d2interface.KeyEvent) bool {
switch event.KeyMod() {
case d2enum.KeyModControl:
met.fileIndex++
d2screen.SetNextScreen(met)
met.screen.SetNextScreen(met)
case d2enum.KeyModShift:
met.levelPreset = increment(met.levelPreset, met.regionSpec.startPresetIndex, met.regionSpec.endPresetIndex)
d2screen.SetNextScreen(met)
met.screen.SetNextScreen(met)
default:
met.currentRegion = increment(met.currentRegion, 0, len(getRegions()))
d2screen.SetNextScreen(met)
met.screen.SetNextScreen(met)
}
return true
@ -444,13 +447,13 @@ func (met *MapEngineTest) OnKeyDown(event d2interface.KeyEvent) bool {
switch event.KeyMod() {
case d2enum.KeyModControl:
met.fileIndex--
d2screen.SetNextScreen(met)
met.screen.SetNextScreen(met)
case d2enum.KeyModShift:
met.levelPreset = decrement(met.levelPreset, met.regionSpec.startPresetIndex, met.regionSpec.endPresetIndex)
d2screen.SetNextScreen(met)
met.screen.SetNextScreen(met)
default:
met.currentRegion = decrement(met.currentRegion, 0, len(getRegions()))
d2screen.SetNextScreen(met)
met.screen.SetNextScreen(met)
}
return true