Skip to content

Commit

Permalink
Refactor|Client|Server|libdeng2: Current game and audiences moved to …
Browse files Browse the repository at this point in the history
…de::App

Added de::game::Game as the base class for actual games. The de::game
namespace was used because de::Game is currently in use by the apps.

A null game is now identified by an empty string as the identifier.
Removed the non-OO isNullGame() method (replaced by Game::isNull()).

de::App is responsible for knowing the current Game instance. The
games collection and the concrete game classes remain in the
client/server app.

Added a new audience that gets notified before a game is unloaded.
  • Loading branch information
skyjake committed Sep 8, 2013
1 parent ca7a186 commit 1fe5f6d
Show file tree
Hide file tree
Showing 17 changed files with 272 additions and 101 deletions.
12 changes: 0 additions & 12 deletions doomsday/client/include/dd_main.h
Original file line number Diff line number Diff line change
Expand Up @@ -182,10 +182,6 @@ de::ResourceClass& DD_ResourceClassByName(de::String name);
/// @return Symbolic name of the material scheme associated with @a textureSchemeName.
de::String DD_MaterialSchemeNameForTextureScheme(de::String textureSchemeName);

/// @todo The GameChange audience belongs in the App class.
DENG2_DECLARE_AUDIENCE(GameChange, void currentGameChanged(de::Game &newGame))
DENG2_EXTERN_AUDIENCE(GameChange)

/// @return @c true iff there is presently a game loaded.
boolean App_GameLoaded();

Expand All @@ -197,14 +193,6 @@ de::Game &App_CurrentGame();
*/
bool App_ChangeGame(de::Game &game, bool allowReload = false);

/**
* Temporarily change the current game. (Which is presently necessary when locating
* resource files for said game and during app init.)
*
* @param newGame New game to temporarily change to.
*/
void App_SetCurrentGame(de::Game const &newGame);

/// @return The application's global Games (collection).
de::Games &App_Games();

Expand Down
8 changes: 2 additions & 6 deletions doomsday/client/include/game.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
#include "api_plugin.h"
#include <de/ddstring.h>
#include <de/Error>
#include <de/game/Game>

/**
* @defgroup printGameFlags Print Game Flags
Expand Down Expand Up @@ -54,7 +55,7 @@ class ResourceManifest;
*
* @ingroup core
*/
class Game
class Game : public de::game::Game
{
public:
typedef QMultiMap<resourceclassid_t, ResourceManifest *> Manifests;
Expand Down Expand Up @@ -198,11 +199,6 @@ class NullGame : public Game
}
};

/// @return @c true= @a game is a "null-game" object (not a real playable game).
inline bool isNullGame(Game const &game) {
return !!dynamic_cast<NullGame const *>(&game);
}

} // namespace de

#endif /* DENG_GAME_H */
7 changes: 6 additions & 1 deletion doomsday/client/src/clientapp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,11 @@ static void continueInitWithEventLoopRunning()

Value *Binding_App_GamePlugin(Context &, Function::ArgumentValues const &)
{
if(App_CurrentGame().isNull())
{
// The null game has no plugin.
return 0;
}
String name = Plug_FileForPlugin(App_CurrentGame().pluginId()).name().fileNameWithoutExtension();
if(name.startsWith("lib")) name.remove(0, 3);
return new TextValue(name);
Expand Down Expand Up @@ -307,7 +312,7 @@ ClientApp::ClientApp(int &argc, char **argv)
setTerminateFunc(handleLegacyCoreTerminate);

// We must presently set the current game manually (the collection is global).
App_SetCurrentGame(d->games.nullGame());
setGame(d->games.nullGame());

d->initScriptBindings();
}
Expand Down
74 changes: 25 additions & 49 deletions doomsday/client/src/dd_main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -186,29 +186,6 @@ static Materials *materials;
// The app's global Texture collection.
static Textures *textures;

/// Notified when the current game changes. @todo Should be owned by App.
GameChangeAudience audienceForGameChange;

/**
* Delegates game change notifications to scripts.
*/
class GameChangeScriptAudience : public IGameChangeObserver
{
public:
void currentGameChanged(Game &newGame)
{
ArrayValue args;
args << DictionaryValue() << TextValue(Str_Text(newGame.identityKey()));
App::scriptSystem().nativeModule("App")["audienceForGameChange"]
.value<ArrayValue>().callElements(args);
}
};

static GameChangeScriptAudience scriptAudienceForGameChange;

/// Current game. @todo Should be owned by App.
Game *currentGame;

#ifdef __CLIENT__

D_CMD(CheckForUpdates)
Expand Down Expand Up @@ -1320,19 +1297,6 @@ de::Games &App_Games()
throw Error("App_Games", "App not yet initialized");
}

Game &App_CurrentGame()
{
DENG_ASSERT(currentGame != 0);
return *currentGame;
}

void App_SetCurrentGame(Game const &game)
{
// Ensure the specified game is actually in this collection (NullGame is implicitly).
DENG_ASSERT(isNullGame(game) || App_Games().id(game) > 0);
currentGame = const_cast<Game *>(&game);
}

boolean App_GameLoaded()
{
#ifdef __CLIENT__
Expand All @@ -1341,14 +1305,14 @@ boolean App_GameLoaded()
#ifdef __SERVER__
if(!ServerApp::haveApp()) return false;
#endif
return !isNullGame(App_CurrentGame());
return !App_CurrentGame().isNull();
}

void DD_DestroyGames()
{
destroyPathList(&sessionResourceFileList, &numSessionResourceFileList);
App_Games().clear();
currentGame = &App_Games().nullGame();
App::app().setGame(App_Games().nullGame());
}

static void populateGameInfo(GameInfo& info, de::Game& game)
Expand Down Expand Up @@ -1459,6 +1423,11 @@ gameid_t DD_GameIdForKey(char const *identityKey)
return 0; // Invalid id.
}

de::Game &App_CurrentGame()
{
return App::game().as<de::Game>();
}

bool App_ChangeGame(Game &game, bool allowReload)
{
//LOG_AS("App_ChangeGame");
Expand All @@ -1481,6 +1450,12 @@ bool App_ChangeGame(Game &game, bool allowReload)
isReload = true;
}

// The current game will be gone very soon.
DENG2_FOR_EACH_OBSERVER(App::GameUnloadAudience, i, App::app().audienceForGameUnload)
{
i->aboutToUnloadGame(App::game());
}

// Quit netGame if one is in progress.
#ifdef __SERVER__
if(netGame && isServer)
Expand All @@ -1503,7 +1478,7 @@ bool App_ChangeGame(Game &game, bool allowReload)
GL_ResetTextureManager();
GL_SetFilter(false);

if(!isNullGame(game))
if(!game.isNull())
{
ClientWindow &mainWin = ClientWindow::main();
mainWin.taskBar().close();
Expand Down Expand Up @@ -1560,7 +1535,7 @@ bool App_ChangeGame(Game &game, bool allowReload)
}

// The current game is now the special "null-game".
currentGame = &App_Games().nullGame();
App::app().setGame(App_Games().nullGame());

Con_InitDatabases();
DD_Register();
Expand Down Expand Up @@ -1596,7 +1571,7 @@ bool App_ChangeGame(Game &game, bool allowReload)
/// @todo Material collection should not be destroyed during a reload.
App_DeleteMaterials();

if(!isNullGame(game))
if(!game.isNull())
{
LOG_VERBOSE("Selecting game '%s'...") << Str_Text(game.identityKey());
}
Expand Down Expand Up @@ -1630,7 +1605,7 @@ bool App_ChangeGame(Game &game, bool allowReload)
}

// This is now the current game.
currentGame = &game;
App::app().setGame(game);

#ifdef __CLIENT__
DD_ComposeMainWindowTitle(buf);
Expand Down Expand Up @@ -1679,7 +1654,7 @@ bool App_ChangeGame(Game &game, bool allowReload)
}

/// @todo Kludge: Use more appropriate task names when unloading a game.
if(isNullGame(game))
if(game.isNull())
{
gameChangeTasks[0].name = "Unloading game...";
gameChangeTasks[3].name = "Switching to ringzero...";
Expand Down Expand Up @@ -1724,7 +1699,11 @@ bool App_ChangeGame(Game &game, bool allowReload)
}
#endif

DENG2_FOR_AUDIENCE(GameChange, i) i->currentGameChanged(App_CurrentGame());
// Game change is complete.
DENG2_FOR_EACH_OBSERVER(App::GameChangeAudience, i, App::app().audienceForGameChange)
{
i->currentGameChanged(App::game());
}

return true;
}
Expand Down Expand Up @@ -1848,10 +1827,9 @@ void DD_FinishInitializationAfterWindowReady()
}

/// @todo This notification should be done from the app.
for(App::StartupCompleteAudience::Loop iter(App::app().audienceForStartupComplete);
!iter.done(); ++iter)
DENG2_FOR_EACH_OBSERVER(App::StartupCompleteAudience, i, App::app().audienceForStartupComplete)
{
iter->appStartupCompleted();
i->appStartupCompleted();
}
}

Expand Down Expand Up @@ -1896,8 +1874,6 @@ boolean DD_Init(void)
}
#endif

audienceForGameChange += scriptAudienceForGameChange;

// Initialize the subsystems needed prior to entering busy mode for the first time.
Sys_Init();
DD_CreateResourceClasses();
Expand Down
9 changes: 5 additions & 4 deletions doomsday/client/src/game.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ DENG2_PIMPL(Game)
: Base(a), pluginId(0), manifests()
{
Str_Set(Str_InitStd(&identityKey), _identityKey);
DENG_ASSERT(!Str_IsEmpty(&identityKey));
//DENG_ASSERT(!Str_IsEmpty(&identityKey));

Str_InitStd(&title);
Str_InitStd(&author);
Expand Down Expand Up @@ -90,7 +90,8 @@ DENG2_PIMPL(Game)

Game::Game(char const *identityKey, char const *configDir,
char const *title, char const *author)
: d(new Instance(*this, identityKey, configDir))
: game::Game(identityKey),
d(new Instance(*this, identityKey, configDir))
{
if(title) Str_Set(&d->title, title);
if(author) Str_Set(&d->author, author);
Expand Down Expand Up @@ -231,7 +232,7 @@ void Game::printFiles(Game const &game, int rflags, bool printStatus)

void Game::print(Game const &game, int flags)
{
if(isNullGame(game))
if(game.isNull())
flags &= ~PGF_BANNER;

#ifdef DENG_DEBUG
Expand Down Expand Up @@ -269,7 +270,7 @@ void Game::print(Game const &game, int flags)
}

NullGame::NullGame()
: Game("null-game", "doomsday", "null-game", "null-game")
: Game("" /*null*/, "doomsday", "null-game", "null-game")
{}

} // namespace de
5 changes: 3 additions & 2 deletions doomsday/client/src/games.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
#include "filesys/manifest.h"
#include "resource/zip.h"
#include <QtAlgorithms>
#include <de/App>

#include "games.h"

Expand Down Expand Up @@ -175,7 +176,7 @@ void Games::locateStartupResources(Game &game)
if(oldCurrentGame != &game)
{
/// @attention Kludge: Temporarily switch Game.
App_SetCurrentGame(game);
App::app().setGame(game);
DD_ExchangeGamePluginEntryPoints(game.pluginId());

// Re-init the filesystem subspace schemes using the search paths of this Game.
Expand All @@ -193,7 +194,7 @@ void Games::locateStartupResources(Game &game)
if(oldCurrentGame != &game)
{
// Kludge end - Restore the old Game.
App_SetCurrentGame(*oldCurrentGame);
App::app().setGame(*oldCurrentGame);
DD_ExchangeGamePluginEntryPoints(oldCurrentGame->pluginId());

// Re-init the filesystem subspace schemes using the search paths of this Game.
Expand Down
10 changes: 5 additions & 5 deletions doomsday/client/src/ui/clientwindow.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ DENG2_OBSERVES(KeyEventSource, KeyEvent),
DENG2_OBSERVES(MouseEventSource, MouseStateChange),
DENG2_OBSERVES(MouseEventSource, MouseEvent),
DENG2_OBSERVES(Canvas, FocusChange),
public IGameChangeObserver
DENG2_OBSERVES(App, GameChange)
{
bool needMainInit;
bool needRecreateCanvas;
Expand Down Expand Up @@ -101,7 +101,7 @@ public IGameChangeObserver
/// @todo The decision whether to receive input notifications from the
/// canvas is really a concern for the input drivers.

audienceForGameChange += this;
App::app().audienceForGameChange += this;

// Listen to input.
self.canvas().audienceForKeyEvent += this;
Expand All @@ -111,7 +111,7 @@ public IGameChangeObserver

~Instance()
{
audienceForGameChange -= this;
App::app().audienceForGameChange -= this;

self.canvas().audienceForFocusChange -= this;
self.canvas().audienceForMouseStateChange -= this;
Expand Down Expand Up @@ -194,9 +194,9 @@ public IGameChangeObserver
busyRoot.add(busy);
}

void currentGameChanged(Game &newGame)
void currentGameChanged(game::Game const &newGame)
{
if(isNullGame(newGame))
if(newGame.isNull())
{
//legacy->hide();
background->show();
Expand Down
8 changes: 4 additions & 4 deletions doomsday/client/src/ui/widgets/consolecommandwidget.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ using namespace de;

DENG_GUI_PIMPL(ConsoleCommandWidget),
DENG2_OBSERVES(App, StartupComplete),
public IGameChangeObserver
DENG2_OBSERVES(App, GameChange)
{
shell::EditorHistory history;
DocumentWidget *completions;
Expand All @@ -41,7 +41,7 @@ public IGameChangeObserver
Instance(Public *i) : Base(i), history(i)
{
App::app().audienceForStartupComplete += this;
audienceForGameChange += this;
App::app().audienceForGameChange += this;

// Popup for autocompletions.
completions = new DocumentWidget;
Expand Down Expand Up @@ -72,15 +72,15 @@ public IGameChangeObserver
~Instance()
{
App::app().audienceForStartupComplete -= this;
audienceForGameChange -= this;
App::app().audienceForGameChange -= this;
}

void appStartupCompleted()
{
updateLexicon();
}

void currentGameChanged(Game &)
void currentGameChanged(game::Game const &)
{
updateLexicon();
}
Expand Down
Loading

0 comments on commit 1fe5f6d

Please sign in to comment.