diff --git a/binaries/data/mods/official/gui/test/3_loading.xml b/binaries/data/mods/official/gui/test/3_loading.xml
index 6c1ad31089..7f5c278d74 100644
--- a/binaries/data/mods/official/gui/test/3_loading.xml
+++ b/binaries/data/mods/official/gui/test/3_loading.xml
@@ -36,8 +36,16 @@
-
-
+
+
diff --git a/binaries/data/mods/official/gui/test/functions_pregame_load.js b/binaries/data/mods/official/gui/test/functions_pregame_load.js
index 120d8dfb27..ff91c5942b 100644
--- a/binaries/data/mods/official/gui/test/functions_pregame_load.js
+++ b/binaries/data/mods/official/gui/test/functions_pregame_load.js
@@ -42,24 +42,43 @@ function startLoadingScreen()
getGUIObjectByName("loading_screen_tip").caption = "Wise man once say ...\nHe who thinks slow, he act in haste, be rash and quick and foolish. But he that thinks too much, acts too slowly. The stupid always win, Commandersan. Remember that. You are tiny grasshopper.";
// Begin game session.
- setTimeout( loadSession, 200 );
+// setTimeout( loadSession, 200 );
+ startGame(); // new version returns quickly; we will hit the main loop
+ // occasionally, so need for evil timeout hack
}
// ====================================================================
-function loadSession()
-{
- if (! startGame())
- {
- // Failed to start the game; go back to the main menu. TODO: display an error message.
- GUIObjectHide("loading_screen");
- GUIObjectUnhide("PREGAME_GUI");
- // Show an error message
- btCaptions = new Array("OK");
- btCode = new Array("");
- messageBox(400, 200, "The game could not be started with the given parameters. You probably have entered an invalid map name.", "Error", 0, btCaptions, btCode);
- }
+//function loadSession()
+//{
+// if (! startGame())
+// {
+// // Failed to start the game; go back to the main menu. TODO: display an error message.
+// GUIObjectHide("loading_screen");
+// GUIObjectUnhide("PREGAME_GUI");
+// // Show an error message
+// btCaptions = new Array("OK");
+// btCode = new Array("");
+// messageBox(400, 200, "The game could not be started with the given parameters. You probably have entered an invalid map name.", "Error", 0, btCaptions, btCode);
+// }
+//
+// // Create resource pools for each player, etc.
+// setupSession();
+//
+// FlipGUI(GUIType);
+//
+// // Select session peace track.
+// curr_session_playlist_1 = newRandomSound("music", "peace");
+// // Fade out main theme and fade in session theme.
+// CrossFade(curr_music, curr_session_playlist_1, 0.0001);
+//
+// // Switch GUI from main menu to game session.
+// GUIObjectHide("loading_screen");
+// GUIObjectUnhide("SESSION_GUI");
+//}
+function reallyStartGame()
+{
// Create resource pools for each player, etc.
setupSession();
@@ -68,7 +87,9 @@ function loadSession()
// Select session peace track.
curr_session_playlist_1 = newRandomSound("music", "peace");
// Fade out main theme and fade in session theme.
- CrossFade(curr_music, curr_session_playlist_1, 0.0001);
+ CrossFade(curr_music, curr_session_playlist_1, 0.1);
+ // janwas: greatly accelerate this timesink;
+ // will be replaced soon by native version that doesn't block.
// Switch GUI from main menu to game session.
GUIObjectHide("loading_screen");
diff --git a/source/graphics/GameView.cpp b/source/graphics/GameView.cpp
index c19ad0e601..e1974272e3 100755
--- a/source/graphics/GameView.cpp
+++ b/source/graphics/GameView.cpp
@@ -16,6 +16,7 @@
#include "Pyrogenesis.h"
#include "Hotkey.h"
#include "ConfigDB.h"
+#include "Loader.h"
#include "Quaternion.h"
#include "Unit.h"
@@ -69,6 +70,7 @@ CGameView::~CGameView()
UnloadResources();
}
+
void CGameView::Initialize(CGameAttributes *pAttribs)
{
CConfigValue* cfg;
@@ -96,6 +98,32 @@ void CGameView::Initialize(CGameAttributes *pAttribs)
InitResources();
}
+struct ThunkParams
+{
+ CGameView* const this_;
+ CGameAttributes* const pAttribs;
+ ThunkParams(CGameView* this__, CGameAttributes* pAttribs_)
+ : this_(this__), pAttribs(pAttribs_) {}
+};
+
+static int LoadThunk(void* param, double time_left)
+{
+ const ThunkParams* p = (const ThunkParams*)param;
+ CGameView* const this_ = p->this_;
+ CGameAttributes* const pAttribs = p->pAttribs;
+
+ this_->Initialize(pAttribs);
+ delete p;
+ return 0;
+}
+
+void CGameView::RegisterInit(CGameAttributes *pAttribs)
+{
+ void* param = new ThunkParams(this, pAttribs);
+ THROW_ERR(LDR_Register(LoadThunk, param, L"CGameView", 1000));
+}
+
+
void CGameView::Render()
{
g_Renderer.SetCamera(m_Camera);
diff --git a/source/graphics/GameView.h b/source/graphics/GameView.h
index 91b8f5852f..4b9dd61bfb 100755
--- a/source/graphics/GameView.h
+++ b/source/graphics/GameView.h
@@ -60,8 +60,9 @@ public:
CGameView(CGame *pGame);
~CGameView();
+ void RegisterInit(CGameAttributes *pAttribs);
void Initialize(CGameAttributes *pGameAttributes);
-
+
// Update: Update all the view information (i.e. rotate camera, scroll,
// whatever). This will *not* change any World information - only the
// *presentation*
diff --git a/source/lib/lib.h b/source/lib/lib.h
index feb2d45187..60066be3ed 100755
--- a/source/lib/lib.h
+++ b/source/lib/lib.h
@@ -118,6 +118,17 @@ STMT(\
)
#endif
+#define THROW_ERR(func)\
+STMT(\
+ int err__ = (int)((func) & UINT_MAX);\
+ if(err__ < 0)\
+ {\
+ assert(0 && "FYI: CHECK_ERR reports that a function failed."\
+ "feel free to ignore or suppress this warning.");\
+ throw err__;\
+ }\
+)
+
// useful because VC6 may return 0 on failure, instead of throwing.
diff --git a/source/main.cpp b/source/main.cpp
index d797e06224..3be191f358 100755
--- a/source/main.cpp
+++ b/source/main.cpp
@@ -26,8 +26,8 @@
#endif
#include "lib/res/cursor.h"
+#include "ps/Loader.h"
#include "ps/Font.h"
-
#include "ps/CConsole.h"
#include "ps/Game.h"
@@ -53,6 +53,7 @@
#include "EntityManager.h"
#include "PathfindEngine.h"
#include "Scheduler.h"
+#include "StringConvert.h"
#include "scripting/ScriptingHost.h"
#include "scripting/JSInterface_Entity.h"
@@ -481,7 +482,7 @@ static void Render()
oglCheck();
- if (g_Game)
+ if (g_Game && g_Game->IsGameStarted())
{
g_Game->GetView()->Render();
@@ -943,6 +944,45 @@ TIMER(InitRenderer)
g_Renderer.SetViewport(vp);
}
+
+static int ProgressiveLoad()
+{
+ wchar_t description[100];
+ int progress_percent;
+ int ret = LDR_ProgressiveLoad(100e-3, description, ARRAY_SIZE(description), &progress_percent);
+ switch(ret)
+ {
+ // no load active => no-op (skip code below)
+ case 1:
+ return 1;
+ // current task isn't complete (we don't care about this distinction)
+ case ERR_TIMED_OUT:
+ break;
+ // just finished loading
+ case 0:
+ g_Game->ReallyStartGame();
+ wcscpy_s(description, ARRAY_SIZE(description), L"Game is starting..");
+ // LDR_ProgressiveLoad returns L""; set to valid text to
+ // avoid problems in converting to JSString
+ break;
+ // error!
+ default:
+ CHECK_ERR(ret);
+ // can't do this above due to legit ERR_TIMED_OUT
+ break;
+ }
+
+ // display progress / description in loading screen
+ CStrW i18n_description = translate(description);
+ JSString* js_desc = StringConvert::wstring_to_jsstring(g_ScriptingHost.getContext(), i18n_description);
+ g_ScriptingHost.SetGlobal("g_Progress", INT_TO_JSVAL(progress_percent));
+ g_ScriptingHost.SetGlobal("g_LoadDescription", STRING_TO_JSVAL(js_desc));
+ g_GUI.SendEventToAll("progress");
+ return 0;
+}
+
+
+
u64 PREVTSC;
static void Shutdown()
@@ -1227,6 +1267,9 @@ PREVTSC=CURTSC;
}
#endif
+
+
+
}
static void Frame()
@@ -1244,11 +1287,11 @@ static void Frame()
assert(TimeSinceLastFrame >= 0.0f);
MICROLOG(L"reload files");
-
res_reload_changed_files();
- MICROLOG(L"input");
+ ProgressiveLoad();
+ MICROLOG(L"input");
in_get_events();
g_SessionManager.Poll();
@@ -1259,7 +1302,7 @@ static void Frame()
if (g_Game && g_Game->IsGameStarted())
{
g_Game->Update(TimeSinceLastFrame);
-
+
if (!g_FixedFrameTiming)
g_Game->GetView()->Update(float(TimeSinceLastFrame));
diff --git a/source/ps/Game.cpp b/source/ps/Game.cpp
index 04492b81be..aa96e64d86 100755
--- a/source/ps/Game.cpp
+++ b/source/ps/Game.cpp
@@ -6,6 +6,7 @@
#include "gui/CGUI.h"
#endif
#include "timer.h"
+#include "Loader.h"
CGame *g_Game=NULL;
@@ -39,6 +40,42 @@ CGame::~CGame()
debug_out("CGame::~CGame(): Game object DESTROYED\n");
}
+
+
+PSRETURN CGame::RegisterInit(CGameAttributes* pAttribs)
+{
+ LDR_BeginRegistering();
+
+ // RC, 040804 - GameView needs to be initialised before World, otherwise GameView initialisation
+ // overwrites anything stored in the map file that gets loaded by CWorld::Initialize with default
+ // values. At the minute, it's just lighting settings, but could be extended to store camera position.
+ // Storing lighting settings in the gameview seems a little odd, but it's no big deal; maybe move it at
+ // some point to be stored in the world object?
+ m_GameView.RegisterInit(pAttribs);
+ m_World.RegisterInit(pAttribs);
+ m_Simulation.RegisterInit(pAttribs);
+
+ LDR_EndRegistering();
+ return 0;
+}
+
+PSRETURN CGame::ReallyStartGame()
+{
+ jsval rval;
+ JSBool ok = JS_CallFunctionName(g_ScriptingHost.getContext(),
+ g_GUI.GetScriptObject(), "reallyStartGame", 0, NULL, &rval);
+ assert(ok);
+
+ debug_out("GAME STARTED, ALL INIT COMPLETE\n");
+ m_GameStarted=true;
+
+#ifndef NO_GUI
+ g_GUI.SendEventToAll("sessionstart");
+#endif
+
+ return 0;
+}
+
PSRETURN CGame::StartGame(CGameAttributes *pAttribs)
{
try
@@ -54,21 +91,7 @@ PSRETURN CGame::StartGame(CGameAttributes *pAttribs)
m_pLocalPlayer=m_Players[1];
- // RC, 040804 - GameView needs to be initialised before World, otherwise GameView initialisation
- // overwrites anything stored in the map file that gets loaded by CWorld::Initialize with default
- // values. At the minute, it's just lighting settings, but could be extended to store camera position.
- // Storing lighting settings in the gameview seems a little odd, but it's no big deal; maybe move it at
- // some point to be stored in the world object?
- m_GameView.Initialize(pAttribs);
- m_World.Initialize(pAttribs);
- m_Simulation.Initialize(pAttribs);
-
- debug_out("GAME STARTED, ALL INIT COMPLETE\n");
- m_GameStarted=true;
-
-#ifndef NO_GUI
- g_GUI.SendEventToAll("sessionstart");
-#endif
+ RegisterInit(pAttribs);
}
catch (PSERROR_Game e)
{
diff --git a/source/ps/Game.h b/source/ps/Game.h
index a0259f6f42..e1c1fc6ca7 100755
--- a/source/ps/Game.h
+++ b/source/ps/Game.h
@@ -41,7 +41,8 @@ public:
Return: 0 on OK - a PSRETURN code otherwise
*/
PSRETURN StartGame(CGameAttributes *pGameAttributes);
-
+ PSRETURN ReallyStartGame();
+
/*
Perform all per-frame updates
*/
@@ -74,6 +75,9 @@ public:
{ return &m_GameView; }
inline CSimulation *GetSimulation()
{ return &m_Simulation; }
+
+private:
+ PSRETURN RegisterInit(CGameAttributes* pAttribs);
};
extern CGame *g_Game;
diff --git a/source/ps/Loader.cpp b/source/ps/Loader.cpp
index 48f0e433fa..35ce397241 100644
--- a/source/ps/Loader.cpp
+++ b/source/ps/Loader.cpp
@@ -158,7 +158,7 @@ static bool HaveTimeForNextTask(double time_left, double time_budget, int estima
// (if it's less than that, we won't check the next task length)
if(time_left < 0.40*time_budget)
{
- const double estimated_duration = estimated_duration_ms * 1e-3;
+ const double estimated_duration = estimated_duration_ms*1e-3;
// .. and the upcoming task is expected to be long -
// leave it for the next timeslice.
if(estimated_duration > time_left + time_budget*0.20)
@@ -201,10 +201,10 @@ int LDR_ProgressiveLoad(double time_budget, wchar_t* description_,
while(!load_requests.empty())
{
- const double time_left = end_time - get_time();
- const LoadRequest& lr = load_requests.front();
+ double time_left = end_time - get_time();
// do actual work of loading
+ const LoadRequest& lr = load_requests.front();
ret = lr.func(lr.param, time_left);
// .. either finished entirely, or failed => remove from queue
if(ret != ERR_TIMED_OUT)
@@ -228,6 +228,7 @@ int LDR_ProgressiveLoad(double time_budget, wchar_t* description_,
// check if we're out of time; take into account next task length.
// note: do this at the end of the loop to make sure there's
// progress even if the timer is low-resolution (=> time_left = 0).
+ time_left = end_time - get_time();
if(!HaveTimeForNextTask(time_left, time_budget, lr.estimated_duration_ms))
{
ret = ERR_TIMED_OUT;
@@ -237,7 +238,6 @@ int LDR_ProgressiveLoad(double time_budget, wchar_t* description_,
// queue is empty, we just finished.
state = IDLE;
- assert(progress_percent == 100);
ret = 0;
// set output params (there are several return points above)
@@ -250,5 +250,7 @@ done:
description = load_requests.front().description.c_str();
wcscpy_s(description_, max_chars, description);
+ debug_out("LDR_ProgressiveLoad RETURNING; desc=%ls progress=%d\n", description_, progress_percent);
+
return ret;
}
diff --git a/source/ps/World.cpp b/source/ps/World.cpp
index 2470effbbe..9eac6a1883 100755
--- a/source/ps/World.cpp
+++ b/source/ps/World.cpp
@@ -12,6 +12,7 @@
#include "BaseEntityCollection.h"
#include "EntityManager.h"
#include "timer.h"
+#include "Loader.h"
#define LOG_CATEGORY "world"
@@ -41,6 +42,32 @@ void CWorld::Initialize(CGameAttributes *pAttribs)
}
}
+struct ThunkParams
+{
+ CWorld* const this_;
+ CGameAttributes* const pAttribs;
+ ThunkParams(CWorld* this__, CGameAttributes* pAttribs_)
+ : this_(this__), pAttribs(pAttribs_) {}
+};
+
+static int LoadThunk(void* param, double time_left)
+{
+ const ThunkParams* p = (const ThunkParams*)param;
+ CWorld* const this_ = p->this_;
+ CGameAttributes* const pAttribs = p->pAttribs;
+
+ this_->Initialize(pAttribs);
+ delete p;
+ return 0;
+}
+
+void CWorld::RegisterInit(CGameAttributes *pAttribs)
+{
+ void* param = new ThunkParams(this, pAttribs);
+ THROW_ERR(LDR_Register(LoadThunk, param, L"CWorld", 1000));
+}
+
+
CWorld::~CWorld()
{
// The Entity Manager should perhaps be converted into a CWorld member..
diff --git a/source/ps/World.h b/source/ps/World.h
index 6cdceca08e..9bbc4b28da 100755
--- a/source/ps/World.h
+++ b/source/ps/World.h
@@ -18,6 +18,7 @@ class CWorld
// been converted
CUnitManager &m_UnitManager;
CEntityManager &m_EntityManager;
+
public:
inline CWorld(CGame *pGame):
m_pGame(pGame),
@@ -28,11 +29,13 @@ public:
~CWorld();
+ void RegisterInit(CGameAttributes *pGameAttributes);
/*
- Initialize the World - load the map and all objects
+ Initialize the World - load the map and all objects
*/
void Initialize(CGameAttributes *pGameAttributes);
-
+
+
inline CTerrain *GetTerrain()
{ return &m_Terrain; }
inline CUnitManager *GetUnitManager()
diff --git a/source/simulation/Simulation.cpp b/source/simulation/Simulation.cpp
index 24b3a89878..61f5c8207a 100755
--- a/source/simulation/Simulation.cpp
+++ b/source/simulation/Simulation.cpp
@@ -12,6 +12,7 @@
#include "CConsole.h"
#include "Unit.h"
#include "Model.h"
+#include "Loader.h"
#include "gui/CGUI.h"
@@ -37,6 +38,34 @@ void CSimulation::Initialize(CGameAttributes *pAttribs)
g_EntityManager.InitializeAll();
}
+
+struct ThunkParams
+{
+ CSimulation* const this_;
+ CGameAttributes* const pAttribs;
+ ThunkParams(CSimulation* this__, CGameAttributes* pAttribs_)
+ : this_(this__), pAttribs(pAttribs_) {}
+};
+
+static int LoadThunk(void* param, double time_left)
+{
+ const ThunkParams* p = (const ThunkParams*)param;
+ CSimulation* const this_ = p->this_;
+ CGameAttributes* const pAttribs = p->pAttribs;
+
+ this_->Initialize(pAttribs);
+ delete p;
+ return 0;
+}
+
+void CSimulation::RegisterInit(CGameAttributes *pAttribs)
+{
+ void* param = new ThunkParams(this, pAttribs);
+ THROW_ERR(LDR_Register(LoadThunk, param, L"CSimulation", 1000));
+}
+
+
+
void CSimulation::Update(double frameTime)
{
m_DeltaTime += frameTime;
diff --git a/source/simulation/Simulation.h b/source/simulation/Simulation.h
index 7502af8404..9f53d62a9e 100755
--- a/source/simulation/Simulation.h
+++ b/source/simulation/Simulation.h
@@ -33,8 +33,9 @@ public:
inline CTurnManager *GetTurnManager()
{ return m_pTurnManager; }
+ void RegisterInit(CGameAttributes *pGameAttributes);
void Initialize(CGameAttributes *pGameAttributes);
-
+
// Perform all CSimulation updates for the specified elapsed time.
void Update(double frameTime);