Changeset View
Standalone View
source/rlinterface/RLInterface.cpp
Show All 24 Lines | |||||
#include "ps/Game.h" | #include "ps/Game.h" | ||||
#include "ps/GameSetup/GameSetup.h" | #include "ps/GameSetup/GameSetup.h" | ||||
#include "ps/Loader.h" | #include "ps/Loader.h" | ||||
#include "ps/CLogger.h" | #include "ps/CLogger.h" | ||||
#include "simulation2/components/ICmpAIInterface.h" | #include "simulation2/components/ICmpAIInterface.h" | ||||
#include "simulation2/components/ICmpTemplateManager.h" | #include "simulation2/components/ICmpTemplateManager.h" | ||||
#include "simulation2/Simulation2.h" | #include "simulation2/Simulation2.h" | ||||
#include "simulation2/system/LocalTurnManager.h" | #include "simulation2/system/LocalTurnManager.h" | ||||
#include "third_party/mongoose/mongoose.h" | |||||
#include <queue> | #include <queue> | ||||
#include <tuple> | |||||
#include <sstream> | #include <sstream> | ||||
vladislavbelov: Wrong order. | |||||
#include <tuple> | |||||
// Globally accessible pointer to the RL Interface. | // Globally accessible pointer to the RL Interface. | ||||
RLInterface* g_RLInterface = nullptr; | std::unique_ptr<RL::Interface> g_RLInterface; | ||||
namespace RL | |||||
{ | |||||
Interface::Interface(const char* server_address) : m_GameMessage({GameMessageType::None}) | |||||
{ | |||||
LOGMESSAGERENDER("Starting RL interface HTTP server"); | |||||
const char *options[] = { | |||||
"listening_ports", server_address, | |||||
"num_threads", "1", | |||||
Done Inline ActionsDo you need 6 threads? AFAIK you're not using a browser? Stan: Do you need 6 threads? AFAIK you're not using a browser? | |||||
nullptr | |||||
}; | |||||
mg_context* mgContext = mg_start(MgCallback, this, options); | |||||
ENSURE(mgContext); | |||||
} | |||||
Done Inline ActionsYou don't need to add RL for each method, you can just wrap the whole code in namespace RL. vladislavbelov: You don't need to add RL for each method, you can just wrap the whole code in `namespace RL`. | |||||
Done Inline ActionsYeah, that would've been a good idea to make it cleaner. Updated now. irishninja: Yeah, that would've been a good idea to make it cleaner. Updated now. | |||||
Not Done Inline ActionsNo need to assign nullptr to a global object. vladislavbelov: No need to assign `nullptr` to a global object. | |||||
// Interactions with the game engine (g_Game) must be done in the main | // Interactions with the game engine (g_Game) must be done in the main | ||||
// thread as there are specific checks for this. We will pass our commands | // thread as there are specific checks for this. We will pass messages | ||||
// to the main thread to be applied | // to the main thread to be applied (ie, "GameMessage"s). | ||||
std::string RLInterface::SendGameMessage(const GameMessage msg) | std::string Interface::SendGameMessage(GameMessage&& msg) | ||||
Not Done Inline Actionsr-value doesn't make things easier here. vladislavbelov: r-value doesn't make things easier here. | |||||
Done Inline ActionsMy thinking had been that game messages do not need to be used after they are "sent" so I could pass them as an r-value. This should help avoid any additional copying and make it more clear in the code that the message has been "consumed" (so it shouldn't be edited afterwards, etc). Does that make sense? irishninja: My thinking had been that game messages do not need to be used after they are "sent" so I could… | |||||
Not Done Inline ActionsI think vlad's comment in the context that you kept a reference to that r-value below, which didn't help, but since you've fixed that, I think your reasoning holds up. wraitii: I think vlad's comment in the context that you kept a reference to that r-value below, which… | |||||
{ | { | ||||
std::unique_lock<std::mutex> msgLock(m_msgLock); | std::unique_lock<std::mutex> msgLock(m_MsgLock); | ||||
Not Done Inline ActionsDo you know what's going on here? @Itms it's also the question for you. vladislavbelov: Do you know what's going on here?
@Itms it's also the question for you. | |||||
Done Inline ActionsI am setting m_GameMessage to reference msg (which is on the stack). This doesn't result in any issues since m_GameMessage only needs to be available until m_MsgApplied.wait(msgLock) unblocks which certainly is within the lifetime of the previous stack frame (either Step or Reset). I will probably update this to use an rvalue ref (and use std::move) to make ownership more obvious... Thoughts? irishninja: I am setting `m_GameMessage` to reference `msg` (which is on the stack). This doesn't result in… | |||||
Not Done Inline ActionsYes, and that should be avoided. We shouldn't store pointers/references to local variables as class members until we explicitly guarantee the lifetime. vladislavbelov: Yes, and that should be avoided. We shouldn't store pointers/references to local variables as… | |||||
Not Done Inline ActionsIt should be solved. vladislavbelov: It should be solved. | |||||
Done Inline ActionsMy bad. I forgot to make it an rvalue ref here, too. irishninja: My bad. I forgot to make it an rvalue ref here, too. | |||||
Not Done Inline ActionsThe issue is still here. You're storying reference to a local variable. vladislavbelov: The issue is still here. You're storying reference to a local variable. | |||||
Done Inline ActionsAdding m_GameMessage = std::move(msg); should fix this here, right? I was thinking that it should help since the variable is moved to the data member and will no longer be freed after it goes out of scope (ie, when the variable frame is popped off the stack). Is this correct or am I missing something? irishninja: Adding `m_GameMessage = std::move(msg);` should fix this here, right? I was thinking that it… | |||||
m_GameMessage = &msg; | ENSURE(m_GameMessage.type == GameMessageType::None); | ||||
m_msgApplied.wait(msgLock); | m_GameMessage = std::move(msg); | ||||
m_MsgApplied.wait(msgLock, [this]() { return m_GameMessage.type == GameMessageType::None; }); | |||||
return m_GameState; | return m_GameState; | ||||
} | } | ||||
std::string RLInterface::Step(const std::vector<Command> commands) | std::string Interface::Step(std::vector<GameCommand>&& commands) | ||||
{ | { | ||||
std::lock_guard<std::mutex> lock(m_lock); | std::lock_guard<std::mutex> lock(m_Lock); | ||||
GameMessage msg = { GameMessageType::Commands, commands }; | return SendGameMessage({ GameMessageType::Commands, std::move(commands) }); | ||||
return SendGameMessage(msg); | |||||
} | } | ||||
std::string RLInterface::Reset(const ScenarioConfig* scenario) | std::string Interface::Reset(ScenarioConfig&& scenario) | ||||
{ | { | ||||
std::lock_guard<std::mutex> lock(m_lock); | std::lock_guard<std::mutex> lock(m_Lock); | ||||
m_ScenarioConfig = *scenario; | m_ScenarioConfig = std::move(scenario); | ||||
struct GameMessage msg = { GameMessageType::Reset }; | return SendGameMessage({ GameMessageType::Reset }); | ||||
return SendGameMessage(msg); | |||||
} | } | ||||
std::vector<std::string> RLInterface::GetTemplates(const std::vector<std::string> names) const | std::vector<std::string> Interface::GetTemplates(const std::vector<std::string>& names) const | ||||
{ | { | ||||
std::lock_guard<std::mutex> lock(m_lock); | std::lock_guard<std::mutex> lock(m_Lock); | ||||
CSimulation2& simulation = *g_Game->GetSimulation2(); | CSimulation2& simulation = *g_Game->GetSimulation2(); | ||||
CmpPtr<ICmpTemplateManager> cmpTemplateManager(simulation.GetSimContext().GetSystemEntity()); | CmpPtr<ICmpTemplateManager> cmpTemplateManager(simulation.GetSimContext().GetSystemEntity()); | ||||
std::vector<std::string> templates; | std::vector<std::string> templates; | ||||
for (const std::string& templateName : names) | for (const std::string& templateName : names) | ||||
{ | { | ||||
const CParamNode* node = cmpTemplateManager->GetTemplate(templateName); | const CParamNode* node = cmpTemplateManager->GetTemplate(templateName); | ||||
if (node != nullptr) | if (node != nullptr) | ||||
{ | templates.push_back(utf8_from_wstring(node->ToXML())); | ||||
Done Inline ActionsAdditional variables isn't needed here, it doesn't give more information about what's going on here and the lines are pretty short. vladislavbelov: Additional variables isn't needed here, it doesn't give more information about what's going on… | |||||
std::string content = utf8_from_wstring(node->ToXML()); | |||||
templates.push_back(content); | |||||
} | |||||
} | } | ||||
return templates; | return templates; | ||||
} | } | ||||
static void* RLMgCallback(mg_event event, struct mg_connection *conn, const struct mg_request_info *request_info) | void* Interface::MgCallback(mg_event event, struct mg_connection *conn, const struct mg_request_info *request_info) | ||||
{ | { | ||||
RLInterface* interface = (RLInterface*)request_info->user_data; | Interface* interface = (Interface*)request_info->user_data; | ||||
ENSURE(interface); | ENSURE(interface); | ||||
void* handled = (void*)""; // arbitrary non-NULL pointer to indicate successful handling | void* handled = (void*)""; // arbitrary non-NULL pointer to indicate successful handling | ||||
const char* header200 = | const char* header200 = | ||||
"HTTP/1.1 200 OK\r\n" | "HTTP/1.1 200 OK\r\n" | ||||
"Access-Control-Allow-Origin: *\r\n" | "Access-Control-Allow-Origin: *\r\n" | ||||
"Content-Type: text/plain; charset=utf-8\r\n\r\n"; | "Content-Type: text/plain; charset=utf-8\r\n\r\n"; | ||||
Show All 14 Lines | const char* notRunningResponse = | ||||
"Game not running. Please create a scenario first."; | "Game not running. Please create a scenario first."; | ||||
switch (event) | switch (event) | ||||
{ | { | ||||
case MG_NEW_REQUEST: | case MG_NEW_REQUEST: | ||||
{ | { | ||||
std::stringstream stream; | std::stringstream stream; | ||||
std::string uri = request_info->uri; | const std::string uri = request_info->uri; | ||||
if (uri == "/reset") | if (uri == "/reset") | ||||
{ | { | ||||
const char* val = mg_get_header(conn, "Content-Length"); | const char* val = mg_get_header(conn, "Content-Length"); | ||||
if (!val) | if (!val) | ||||
{ | { | ||||
mg_printf(conn, "%s", noPostData); | mg_printf(conn, "%s", noPostData); | ||||
return handled; | return handled; | ||||
} | } | ||||
ScenarioConfig scenario; | ScenarioConfig scenario; | ||||
std::string qs(request_info->query_string); | const std::string qs(request_info->query_string); | ||||
scenario.saveReplay = qs.find("saveReplay") != std::string::npos; | scenario.saveReplay = qs.find("saveReplay") != std::string::npos; | ||||
scenario.playerID = 1; | scenario.playerID = 1; | ||||
char playerID[1]; | char playerID[1]; | ||||
int len = mg_get_var(request_info->query_string, qs.length(), "playerID", playerID, 1); | const int len = mg_get_var(request_info->query_string, qs.length(), "playerID", playerID, 1); | ||||
if (len != -1) | if (len != -1) | ||||
scenario.playerID = std::stoi(playerID); | scenario.playerID = std::stoi(playerID); | ||||
int bufSize = std::atoi(val); | const int bufSize = std::atoi(val); | ||||
Not Done Inline ActionsWhat's happening if the val is incorrect integer? vladislavbelov: What's happening if the val is incorrect integer? | |||||
Done Inline ActionsHmm... I was assuming that it was a properly formed HTTP request. Would you like me to be more pessimistic about malformed HTTP requests? So far, I haven't worried too much about it since "Content-Length" is often handled by http request libraries and the RL interface requires both a flag to be set and then only accepts requests from localhost (unless you change the address to 0.0.0.0). I also figured that there wasn't much motivation for malicious agents to attack the game via the RL interface as it is likely being used for development of an AI rather than a hosting the game engine as a server of sorts. (In that context, I had actually imagined there would still be an orchestrator of sorts collecting and applying the commands as all actions at a given step are part of a single call to Step - and it likely wouldn't make sense for both players to control the stepping of the game arbitrarily.) Regardless, I can certainly update it if preferred. Let me know what you think! irishninja: Hmm... I was assuming that it was a properly formed HTTP request. Would you like me to be more… | |||||
Done Inline Actionsatoi will return 0 if it can't be converted, which ought to be fairly safe. I think we have to trust mongoose here. wraitii: `atoi` will return 0 if it can't be converted, which ought to be fairly safe. I think we have… | |||||
std::unique_ptr<char> buf = std::unique_ptr<char>(new char[bufSize]); | std::unique_ptr<char[]> buf = std::unique_ptr<char[]>(new char[bufSize]); | ||||
Done Inline ActionsIncorrect type of std::unique_ptr<char>, it should be std::unique_ptr<char[]>. vladislavbelov: Incorrect type of `std::unique_ptr<char>`, it should be `std::unique_ptr<char[]>`. | |||||
mg_read(conn, buf.get(), bufSize); | mg_read(conn, buf.get(), bufSize); | ||||
std::string content(buf.get(), bufSize); | const std::string content(buf.get(), bufSize); | ||||
scenario.content = content; | scenario.content = content; | ||||
Not Done Inline ActionsInstead of duplicating allocations you might create a string with enough space: const int contentSize = std::atoi(val); std::string content(' ', contentSize); mg_read(conn, content.data(), content.size()); scenario.content = std::move(content); vladislavbelov: Instead of duplicating allocations you might create a string with enough space:
```lang=cpp… | |||||
Done Inline ActionsMakes sense. However, it looks like content.data() is const and can't be used to set the contents of the string using mg_read. Am I missing something? irishninja: Makes sense. However, it looks like `content.data()` is `const` and can't be used to set the… | |||||
std::string gameState = interface->Reset(&scenario); | const std::string gameState = interface->Reset(std::move(scenario)); | ||||
stream << gameState.c_str(); | stream << gameState.c_str(); | ||||
} | } | ||||
else if (uri == "/step") | else if (uri == "/step") | ||||
{ | { | ||||
if (!interface->IsGameRunning()) | if (!interface->IsGameRunning()) | ||||
{ | { | ||||
mg_printf(conn, "%s", notRunningResponse); | mg_printf(conn, "%s", notRunningResponse); | ||||
return handled; | return handled; | ||||
} | } | ||||
const char* val = mg_get_header(conn, "Content-Length"); | const char* val = mg_get_header(conn, "Content-Length"); | ||||
if (!val) | if (!val) | ||||
{ | { | ||||
mg_printf(conn, "%s", noPostData); | mg_printf(conn, "%s", noPostData); | ||||
return handled; | return handled; | ||||
} | } | ||||
int bufSize = std::atoi(val); | int bufSize = std::atoi(val); | ||||
std::unique_ptr<char> buf = std::unique_ptr<char>(new char[bufSize]); | std::unique_ptr<char[]> buf = std::unique_ptr<char[]>(new char[bufSize]); | ||||
mg_read(conn, buf.get(), bufSize); | mg_read(conn, buf.get(), bufSize); | ||||
std::string postData(buf.get(), bufSize); | const std::string postData(buf.get(), bufSize); | ||||
Not Done Inline ActionsThe same here. vladislavbelov: The same here. | |||||
std::stringstream postStream(postData); | std::stringstream postStream(postData); | ||||
std::string line; | std::string line; | ||||
std::vector<Command> commands; | std::vector<GameCommand> commands; | ||||
while (std::getline(postStream, line, '\n')) | while (std::getline(postStream, line, '\n')) | ||||
{ | { | ||||
Command cmd; | GameCommand cmd; | ||||
const std::size_t splitPos = line.find(";"); | const std::size_t splitPos = line.find(";"); | ||||
if (splitPos != std::string::npos) | if (splitPos != std::string::npos) | ||||
{ | { | ||||
cmd.playerID = std::stoi(line.substr(0, splitPos)); | cmd.playerID = std::stoi(line.substr(0, splitPos)); | ||||
cmd.json_cmd = line.substr(splitPos + 1); | cmd.json_cmd = line.substr(splitPos + 1); | ||||
commands.push_back(cmd); | commands.push_back(cmd); | ||||
} | } | ||||
} | } | ||||
std::string gameState = interface->Step(commands); | const std::string gameState = interface->Step(std::move(commands)); | ||||
if (gameState.empty()) | if (gameState.empty()) | ||||
{ | { | ||||
mg_printf(conn, "%s", notRunningResponse); | mg_printf(conn, "%s", notRunningResponse); | ||||
return handled; | return handled; | ||||
} | } | ||||
else | else | ||||
stream << gameState.c_str(); | stream << gameState.c_str(); | ||||
} | } | ||||
else if (uri == "/templates") | else if (uri == "/templates") | ||||
{ | { | ||||
if (!interface->IsGameRunning()) { | if (!interface->IsGameRunning()) { | ||||
mg_printf(conn, "%s", notRunningResponse); | mg_printf(conn, "%s", notRunningResponse); | ||||
return handled; | return handled; | ||||
} | } | ||||
const char* val = mg_get_header(conn, "Content-Length"); | const char* val = mg_get_header(conn, "Content-Length"); | ||||
if (!val) | if (!val) | ||||
{ | { | ||||
mg_printf(conn, "%s", noPostData); | mg_printf(conn, "%s", noPostData); | ||||
return handled; | return handled; | ||||
} | } | ||||
int bufSize = std::atoi(val); | const int bufSize = std::atoi(val); | ||||
std::unique_ptr<char> buf = std::unique_ptr<char>(new char[bufSize]); | std::unique_ptr<char[]> buf = std::unique_ptr<char[]>(new char[bufSize]); | ||||
mg_read(conn, buf.get(), bufSize); | mg_read(conn, buf.get(), bufSize); | ||||
std::string postData(buf.get(), bufSize); | const std::string postData(buf.get(), bufSize); | ||||
Not Done Inline ActionsAnd here. It seems a code duplication, might be unified. vladislavbelov: And here. It seems a code duplication, might be unified. | |||||
std::stringstream postStream(postData); | std::stringstream postStream(postData); | ||||
std::string line; | std::string line; | ||||
std::vector<std::string> templateNames; | std::vector<std::string> templateNames; | ||||
while (std::getline(postStream, line, '\n')) | while (std::getline(postStream, line, '\n')) | ||||
templateNames.push_back(line); | templateNames.push_back(line); | ||||
for (std::string templateStr : interface->GetTemplates(templateNames)) | for (std::string templateStr : interface->GetTemplates(templateNames)) | ||||
stream << templateStr.c_str() << "\n"; | stream << templateStr.c_str() << "\n"; | ||||
} | } | ||||
else | else | ||||
{ | { | ||||
mg_printf(conn, "%s", header404); | mg_printf(conn, "%s", header404); | ||||
return handled; | return handled; | ||||
} | } | ||||
mg_printf(conn, "%s", header200); | mg_printf(conn, "%s", header200); | ||||
std::string str = stream.str(); | const std::string str = stream.str(); | ||||
mg_write(conn, str.c_str(), str.length()); | mg_write(conn, str.c_str(), str.length()); | ||||
return handled; | return handled; | ||||
} | } | ||||
case MG_HTTP_ERROR: | case MG_HTTP_ERROR: | ||||
return nullptr; | return nullptr; | ||||
case MG_EVENT_LOG: | case MG_EVENT_LOG: | ||||
// Called by Mongoose's cry() | // Called by Mongoose's cry() | ||||
LOGERROR("Mongoose error: %s", request_info->log_message); | LOGERROR("Mongoose error: %s", request_info->log_message); | ||||
return nullptr; | return nullptr; | ||||
case MG_INIT_SSL: | case MG_INIT_SSL: | ||||
return nullptr; | return nullptr; | ||||
default: | default: | ||||
debug_warn(L"Invalid Mongoose event type"); | debug_warn(L"Invalid Mongoose event type"); | ||||
return nullptr; | return nullptr; | ||||
} | } | ||||
}; | }; | ||||
void RLInterface::EnableHTTP(const char* server_address) | bool Interface::TryGetGameMessage(GameMessage& msg) | ||||
Not Done Inline ActionsI suppose Enable != Start. Enable doesn't mean that it starts something. vladislavbelov: I suppose `Enable != Start`. Enable doesn't mean that it starts something. | |||||
Done Inline ActionsThis was following the convention used in CProfiler2 but I don't have a preference myself either way. Let me know if you would like it to be changed to something like StartHTTP irishninja: This was following the convention used in CProfiler2 but I don't have a preference myself… | |||||
{ | { | ||||
LOGMESSAGERENDER("Starting RL interface HTTP server"); | if (m_GameMessage.type != GameMessageType::None) | ||||
Done Inline ActionsThe brace should be on the new line. vladislavbelov: The brace should be on the new line. | |||||
// Ignore multiple enablings | |||||
if (m_MgContext) | |||||
return; | |||||
const char *options[] = { | |||||
"listening_ports", server_address, | |||||
"num_threads", "6", // enough for the browser's parallel connection limit | |||||
nullptr | |||||
}; | |||||
m_MgContext = mg_start(RLMgCallback, this, options); | |||||
ENSURE(m_MgContext); | |||||
} | |||||
bool RLInterface::TryGetGameMessage(GameMessage& msg) | |||||
{ | { | ||||
if (m_GameMessage != nullptr) { | msg = m_GameMessage; | ||||
msg = *m_GameMessage; | m_GameMessage = {GameMessageType::None}; | ||||
m_GameMessage = nullptr; | |||||
return true; | return true; | ||||
} | } | ||||
return false; | return false; | ||||
} | } | ||||
void RLInterface::TryApplyMessage() | void Interface::TryApplyMessage() | ||||
{ | { | ||||
const bool nonVisual = !g_GUI; | |||||
const bool isGameStarted = g_Game && g_Game->IsGameStarted(); | const bool isGameStarted = g_Game && g_Game->IsGameStarted(); | ||||
if (m_NeedsGameState && isGameStarted) | if (m_NeedsGameState && isGameStarted) | ||||
{ | { | ||||
m_GameState = GetGameState(); | m_GameState = GetGameState(); | ||||
m_msgApplied.notify_one(); | m_MsgApplied.notify_one(); | ||||
m_msgLock.unlock(); | m_MsgLock.unlock(); | ||||
m_NeedsGameState = false; | m_NeedsGameState = false; | ||||
} | } | ||||
if (m_msgLock.try_lock()) | if (!m_MsgLock.try_lock()) | ||||
{ | return; | ||||
GameMessage msg; | GameMessage msg; | ||||
if (TryGetGameMessage(msg)) { | if (!TryGetGameMessage(msg)) | ||||
Done Inline ActionsEarly return; Stan: Early return; | |||||
{ | |||||
m_MsgLock.unlock(); | |||||
Done Inline ActionsThe brace should be on the new line. vladislavbelov: The brace should be on the new line. | |||||
Done Inline ActionsInvert, early return, to remove indent. Stan: Invert, early return, to remove indent. | |||||
return; | |||||
} | |||||
ApplyMessage(msg); | |||||
} | |||||
void Interface::ApplyMessage(const GameMessage& msg) | |||||
{ | |||||
const static std::string EMPTY_STATE; | |||||
const bool nonVisual = !g_GUI; | |||||
const bool isGameStarted = g_Game && g_Game->IsGameStarted(); | |||||
switch (msg.type) | switch (msg.type) | ||||
{ | { | ||||
case GameMessageType::Reset: | case GameMessageType::Reset: | ||||
{ | { | ||||
if (isGameStarted) | if (isGameStarted) | ||||
EndGame(); | EndGame(); | ||||
g_Game = new CGame(m_ScenarioConfig.saveReplay); | g_Game = new CGame(m_ScenarioConfig.saveReplay); | ||||
ScriptInterface& scriptInterface = g_Game->GetSimulation2()->GetScriptInterface(); | ScriptInterface& scriptInterface = g_Game->GetSimulation2()->GetScriptInterface(); | ||||
JSContext* cx = scriptInterface.GetContext(); | JSContext* cx = scriptInterface.GetContext(); | ||||
JSAutoRequest rq(cx); | JSAutoRequest rq(cx); | ||||
JS::RootedValue attrs(cx); | JS::RootedValue attrs(cx); | ||||
scriptInterface.ParseJSON(m_ScenarioConfig.content, &attrs); | scriptInterface.ParseJSON(m_ScenarioConfig.content, &attrs); | ||||
g_Game->SetPlayerID(m_ScenarioConfig.playerID); | g_Game->SetPlayerID(m_ScenarioConfig.playerID); | ||||
g_Game->StartGame(&attrs, ""); | g_Game->StartGame(&attrs, ""); | ||||
if (nonVisual) | if (nonVisual) | ||||
Done Inline ActionsCould be a helper function? ApplyMessageMaybe? Stan: Could be a helper function? ApplyMessageMaybe? | |||||
{ | { | ||||
LDR_NonprogressiveLoad(); | LDR_NonprogressiveLoad(); | ||||
ENSURE(g_Game->ReallyStartGame() == PSRETURN_OK); | ENSURE(g_Game->ReallyStartGame() == PSRETURN_OK); | ||||
m_GameState = GetGameState(); | m_GameState = GetGameState(); | ||||
m_msgApplied.notify_one(); | m_MsgApplied.notify_one(); | ||||
m_msgLock.unlock(); | m_MsgLock.unlock(); | ||||
} | } | ||||
else | else | ||||
{ | { | ||||
JS::RootedValue initData(cx); | JS::RootedValue initData(cx); | ||||
scriptInterface.CreateObject(cx, &initData); | scriptInterface.CreateObject(cx, &initData); | ||||
scriptInterface.SetProperty(initData, "attribs", attrs); | scriptInterface.SetProperty(initData, "attribs", attrs); | ||||
JS::RootedValue playerAssignments(cx); | JS::RootedValue playerAssignments(cx); | ||||
Done Inline ActionsConst & ? Stan: Const & ? | |||||
scriptInterface.CreateObject(cx, &playerAssignments); | scriptInterface.CreateObject(cx, &playerAssignments); | ||||
scriptInterface.SetProperty(initData, "playerAssignments", playerAssignments); | scriptInterface.SetProperty(initData, "playerAssignments", playerAssignments); | ||||
g_GUI->SwitchPage(L"page_loading.xml", &scriptInterface, initData); | g_GUI->SwitchPage(L"page_loading.xml", &scriptInterface, initData); | ||||
m_NeedsGameState = true; | m_NeedsGameState = true; | ||||
} | } | ||||
break; | break; | ||||
} | } | ||||
case GameMessageType::Commands: | case GameMessageType::Commands: | ||||
{ | { | ||||
if (!g_Game) | if (!g_Game) | ||||
{ | { | ||||
m_GameState = EMPTY_STATE; | m_GameState = EMPTY_STATE; | ||||
m_msgApplied.notify_one(); | m_MsgApplied.notify_one(); | ||||
m_msgLock.unlock(); | m_MsgLock.unlock(); | ||||
return; | return; | ||||
} | } | ||||
const ScriptInterface& scriptInterface = g_Game->GetSimulation2()->GetScriptInterface(); | const ScriptInterface& scriptInterface = g_Game->GetSimulation2()->GetScriptInterface(); | ||||
CLocalTurnManager* turnMgr = static_cast<CLocalTurnManager*>(g_Game->GetTurnManager()); | CLocalTurnManager* turnMgr = static_cast<CLocalTurnManager*>(g_Game->GetTurnManager()); | ||||
for (Command command : msg.commands) | for (const GameCommand& command : msg.commands) | ||||
{ | { | ||||
JSContext* cx = scriptInterface.GetContext(); | JSContext* cx = scriptInterface.GetContext(); | ||||
JSAutoRequest rq(cx); | JSAutoRequest rq(cx); | ||||
JS::RootedValue commandJSON(cx); | JS::RootedValue commandJSON(cx); | ||||
scriptInterface.ParseJSON(command.json_cmd, &commandJSON); | scriptInterface.ParseJSON(command.json_cmd, &commandJSON); | ||||
turnMgr->PostCommand(command.playerID, commandJSON); | turnMgr->PostCommand(command.playerID, commandJSON); | ||||
} | } | ||||
const double deltaRealTime = DEFAULT_TURN_LENGTH_SP; | const u32 deltaRealTime = DEFAULT_TURN_LENGTH_SP; | ||||
Not Done Inline ActionsMight be const float. vladislavbelov: Might be `const float`. | |||||
if (nonVisual) | if (nonVisual) | ||||
{ | { | ||||
const double deltaSimTime = deltaRealTime * g_Game->GetSimRate(); | const double deltaSimTime = deltaRealTime * g_Game->GetSimRate(); | ||||
size_t maxTurns = static_cast<size_t>(g_Game->GetSimRate()); | const size_t maxTurns = static_cast<size_t>(g_Game->GetSimRate()); | ||||
g_Game->GetTurnManager()->Update(deltaSimTime, maxTurns); | g_Game->GetTurnManager()->Update(deltaSimTime, maxTurns); | ||||
} | } | ||||
else | else | ||||
g_Game->Update(deltaRealTime); | g_Game->Update(deltaRealTime); | ||||
m_GameState = GetGameState(); | m_GameState = GetGameState(); | ||||
m_msgApplied.notify_one(); | m_MsgApplied.notify_one(); | ||||
m_msgLock.unlock(); | m_MsgLock.unlock(); | ||||
break; | break; | ||||
} | } | ||||
} | } | ||||
} | } | ||||
else | |||||
m_msgLock.unlock(); | |||||
} | |||||
} | |||||
std::string RLInterface::GetGameState() | std::string Interface::GetGameState() const | ||||
Done Inline Actionsconst ? Stan: const ? | |||||
{ | { | ||||
const ScriptInterface& scriptInterface = g_Game->GetSimulation2()->GetScriptInterface(); | const ScriptInterface& scriptInterface = g_Game->GetSimulation2()->GetScriptInterface(); | ||||
const CSimContext simContext = g_Game->GetSimulation2()->GetSimContext(); | const CSimContext simContext = g_Game->GetSimulation2()->GetSimContext(); | ||||
CmpPtr<ICmpAIInterface> cmpAIInterface(simContext.GetSystemEntity()); | CmpPtr<ICmpAIInterface> cmpAIInterface(simContext.GetSystemEntity()); | ||||
JSContext* cx = scriptInterface.GetContext(); | JSContext* cx = scriptInterface.GetContext(); | ||||
JSAutoRequest rq(cx); | JSAutoRequest rq(cx); | ||||
JS::RootedValue state(cx); | JS::RootedValue state(cx); | ||||
cmpAIInterface->GetFullRepresentation(&state, true); | cmpAIInterface->GetFullRepresentation(&state, true); | ||||
return scriptInterface.StringifyJSON(&state, false); | return scriptInterface.StringifyJSON(&state, false); | ||||
} | } | ||||
bool RLInterface::IsGameRunning() | bool Interface::IsGameRunning() const | ||||
{ | { | ||||
return !!g_Game; | return g_Game != nullptr; | ||||
} | |||||
Done Inline ActionsMaybe g_Game != nullptr? No strong feelings. Stan: Maybe g_Game != nullptr? No strong feelings. | |||||
} | } |
Wrong order.