Skip to content

Commit

Permalink
#6131: Convert game connection dialog to dockable panel
Browse files Browse the repository at this point in the history
  • Loading branch information
codereader committed Oct 21, 2022
1 parent 37571b4 commit 1cdeeb5
Show file tree
Hide file tree
Showing 6 changed files with 111 additions and 123 deletions.
56 changes: 35 additions & 21 deletions plugins/dm.gameconnection/GameConnection.cpp
Expand Up @@ -2,7 +2,6 @@
#include "DiffStatus.h"
#include "DiffDoom3MapWriter.h"
#include "AutomationEngine.h"
#include "GameConnectionDialog.h"

#include "i18n.h"
#include "igame.h"
Expand All @@ -11,6 +10,7 @@
#include "imap.h"
#include "ientity.h"
#include "iselection.h"
#include "ui/iuserinterface.h"
#include "ui/imenumanager.h"
#include "ui/imainframe.h"

Expand All @@ -25,20 +25,23 @@
#include <wx/artprov.h>
#include <wx/process.h>

#include "GameConnectionControl.h"
#include "messages/NotificationMessage.h"

namespace gameconn
{

namespace
{
//this is how often this class "thinks" when idle
const int THINK_INTERVAL = 123;
constexpr int THINK_INTERVAL = 123;

//all ordinary requests, executed synchronously
const int TAG_GENERIC = 5;
constexpr int TAG_GENERIC = 5;
//camera DR->TDM sync is continuous, executed asynchronously
const int TAG_CAMERA = 6;
constexpr int TAG_CAMERA = 6;
//multistep procedure for TDM game start/restart
const int TAG_RESTART = 7;
constexpr int TAG_RESTART = 7;

inline std::string messagePreamble(const std::string& type) {
return fmt::format("message \"{}\"\n", type);
Expand Down Expand Up @@ -167,7 +170,6 @@ GameConnection::~GameConnection()
disconnect(true);
};


void GameConnection::restartGame(bool dmap)
{
enum Steps {
Expand Down Expand Up @@ -197,8 +199,9 @@ void GameConnection::restartGame(bool dmap)

if (step == STEP_START) {
//fetch all settings: mission, map, TDM path
if (GlobalMapModule().isUnnamed()) {
showError("Cannot start TDM because no map file is opened.");
if (GlobalMapModule().isUnnamed())
{
radiant::NotificationMessage::SendError("Cannot start TDM because no map file is opened.");
return {STEP_FINISHED, {}};
}
tdmDir = os::standardPathWithSlash(registry::getValue<std::string>(RKEY_ENGINE_PATH));
Expand Down Expand Up @@ -241,7 +244,7 @@ void GameConnection::restartGame(bool dmap)
wxString cmdline = wxString::Format("%s +set com_automation 1", exeFullPath.c_str());
long res = wxExecute(cmdline, wxEXEC_ASYNC, nullptr, &env);
if (res <= 0) {
showError("Failed to run TheDarkMod executable.");
radiant::NotificationMessage::SendError("Failed to run TheDarkMod executable.");
return {STEP_FINISHED, {}};
}

Expand All @@ -265,7 +268,8 @@ void GameConnection::restartGame(bool dmap)
return {STEP_SETMOD, {}};
}
if (timestampNow - timestampStartAttach > TDM_ATTACH_TIMEOUT) {
showError("Timeout when connecting to just started TheDarkMod process.\nMake sure the game is in main menu, has com_automation enabled, and firewall does not block it.");
radiant::NotificationMessage::SendError("Timeout when connecting to just started TheDarkMod process.\n"
"Make sure the game is in main menu, has com_automation enabled, and firewall does not block it.");
return {STEP_FINISHED, {}};
}

Expand Down Expand Up @@ -298,14 +302,15 @@ void GameConnection::restartGame(bool dmap)
if (pendingSeqno) {
std::string response = _engine->getResponse(pendingSeqno);
if (response != "done") {
showError("Failed to change installed mission in TheDarkMod.\nMake sure ?DR mission? is configured properly and game version is 2.09 or above.");
radiant::NotificationMessage::SendError("Failed to change installed mission in TheDarkMod.\n"
"Make sure ?DR mission? is configured properly and game version is 2.09 or above.");
return {STEP_FINISHED, {}};
}
}
//recheck currently installed FM just to be sure
std::map<std::string, std::string> statusProps = executeQueryStatus();
if (statusProps["currentfm"] != drModName) {
showError(fmt::format("Installed mission is {} despite trying to change it.", statusProps["currentfm"]));
radiant::NotificationMessage::SendError(fmt::format("Installed mission is {} despite trying to change it.", statusProps["currentfm"]));
return {STEP_FINISHED, {}};
}

Expand All @@ -324,7 +329,7 @@ void GameConnection::restartGame(bool dmap)
if (pendingSeqno) {
std::string response = _engine->getResponse(pendingSeqno);
if (response.find("ERROR:") != std::string::npos) {
showError("Dmap printed error.\nPlease look at TheDarkMod console.");
radiant::NotificationMessage::SendError("Dmap printed error.\nPlease look at TheDarkMod console.");
return {STEP_FINISHED, {}};
}
}
Expand All @@ -343,15 +348,15 @@ void GameConnection::restartGame(bool dmap)
//last check: everything should match!
std::map<std::string, std::string> statusProps = executeQueryStatus();
if (statusProps["currentfm"] != drModName) {
showError(fmt::format("Installed mission is still {}.", statusProps["currentfm"]));
radiant::NotificationMessage::SendError(fmt::format("Installed mission is still {}.", statusProps["currentfm"]));
return {STEP_FINISHED, {}};
}
if (statusProps["mapname"] != drMapName) {
showError(fmt::format("Active map is {} despite trying to start the map.", statusProps["mapname"]));
radiant::NotificationMessage::SendError(fmt::format("Active map is {} despite trying to start the map.", statusProps["mapname"]));
return {STEP_FINISHED, {}};
}
if (statusProps["guiactive"] != "") {
showError(fmt::format("GUI {} is active while we expect the game to start", statusProps["guiactive"]));
radiant::NotificationMessage::SendError(fmt::format("GUI {} is active while we expect the game to start", statusProps["guiactive"]));
return {STEP_FINISHED, {}};
}

Expand Down Expand Up @@ -387,7 +392,7 @@ void GameConnection::restartGame(bool dmap)
catch(const DisconnectException&) {
//connection was lost unexpectedly during some step
//most likely user has closed TDM while it was still being prepared
showError("Game restart failed: connection lost unexpectedly.");
radiant::NotificationMessage::SendError("Game restart failed: connection lost unexpectedly.");
}
return {STEP_FINISHED, {}};
};
Expand Down Expand Up @@ -797,7 +802,7 @@ const StringSet& GameConnection::getDependencies() const
static StringSet _dependencies {
MODULE_CAMERA_MANAGER, MODULE_COMMANDSYSTEM, MODULE_MAP,
MODULE_SCENEGRAPH, MODULE_SELECTIONSYSTEM, MODULE_EVENTMANAGER,
MODULE_MENUMANAGER, MODULE_MAINFRAME
MODULE_MENUMANAGER, MODULE_USERINTERFACE, MODULE_MAINFRAME
};
return _dependencies;
}
Expand All @@ -809,15 +814,24 @@ void GameConnection::initialiseModule(const IApplicationContext& ctx)
return;

// Show/hide GUI window
GlobalCommandSystem().addCommand("GameConnectionDialogToggle",
gameconn::GameConnectionDialog::toggleDialog);
GlobalCommandSystem().addStatement("GameConnectionDialogToggle",
fmt::format("ToggleControl {0}", ui::GameConnectionControl::Name), false);
GlobalMenuManager().add("main/map", "GameConnectionDialog", ui::menu::ItemType::Item,
_("Game Connection..."), "", "GameConnectionDialogToggle");

GlobalUserInterface().registerControl(std::make_shared<ui::GameConnectionControl>());

// Add to mainframe after startup, set control default location
GlobalMainFrame().signal_MainFrameConstructed().connect([&]()
{
GlobalMainFrame().addControl(ui::GameConnectionControl::Name, { IMainFrame::Location::FloatingWindow, false });
});

// Restart game
GlobalCommandSystem().addCommand(
"GameConnectionRestartGame",
[this](const cmd::ArgumentList& args) {
[this](const cmd::ArgumentList& args)
{
bool dmap = false;
for (int i = 0; i < args.size(); i++)
if (args[i].getString() == "dmap")
Expand Down
37 changes: 37 additions & 0 deletions plugins/dm.gameconnection/GameConnectionControl.h
@@ -0,0 +1,37 @@
#pragma once

#include "i18n.h"
#include "ui/iusercontrol.h"
#include "GameConnectionPanel.h"

namespace ui
{

class GameConnectionControl :
public IUserControl
{
public:
constexpr static const char* Name = "GameConnectionPanel";

std::string getControlName() override
{
return Name;
}

std::string getDisplayName() override
{
return _("Game Connection");
}

std::string getIcon() override
{
return {};
}

wxWindow* createWidget(wxWindow* parent) override
{
return new GameConnectionPanel(parent);
}
};

}
@@ -1,9 +1,4 @@
#include "GameConnectionDialog.h"
#include "GameConnection.h"

#include "i18n.h"
#include "ui/imainframe.h"
#include "ui/idialogmanager.h"
#include "GameConnectionPanel.h"

#if HAVE_ACTIVITYINDICATOR
#include <wx/activityindicator.h>
Expand All @@ -14,70 +9,35 @@
#include <wx/button.h>
#include <wx/sizer.h>

#include "wxutil/dialog/MessageBox.h"

namespace
{
const char* GameConnectionDialog_TITLE = N_("Game connection");
}


namespace gameconn
{

void showError(const std::string& text)
{
auto dlg = GlobalDialogManager().createMessageBox(
_("Game connection error"), text, ui::IDialog::MESSAGE_ERROR
);
if (dlg)
dlg->run();
}

GameConnectionDialog& GameConnectionDialog::Instance()
{
static std::unique_ptr<GameConnectionDialog> _instance;

if (!_instance) {
_instance.reset(new GameConnectionDialog());

// Pre-destruction cleanup
GlobalMainFrame().signal_MainFrameShuttingDown().connect([]() {
if (_instance->IsShownOnScreen())
_instance->Hide();
_instance->SendDestroyEvent();
_instance.reset();
});
}

return *_instance;
}
#include "GameConnection.h"

void GameConnectionDialog::toggleDialog(const cmd::ArgumentList& args)
namespace ui
{
// Toggle the instance
Instance().ToggleVisibility();
}

GameConnectionDialog::~GameConnectionDialog()
GameConnectionPanel::~GameConnectionPanel()
{
_updateOnStatusChangeSignal.disconnect();
}

GameConnectionDialog::GameConnectionDialog() :
wxutil::TransientWindow(_(GameConnectionDialog_TITLE), GlobalMainFrame().getWxTopLevelWindow(), true)
GameConnectionPanel::GameConnectionPanel(wxWindow* parent) :
wxPanel(parent)
{
wxPanel* panel = loadNamedPanel(this, "GameConnectionMainPanel");
auto mainPanel = loadNamedPanel(this, "GameConnectionMainPanel");
SetSizer(new wxBoxSizer(wxVERTICAL));
GetSizer()->Add(mainPanel, 1, wxEXPAND);

#if HAVE_ACTIVITYINDICATOR
//could not find activity indicator in wxFormBuilder
_connectedActivityIndicator = new wxActivityIndicator(panel, wxID_ANY, wxDefaultPosition, wxDefaultSize, 0, wxT("ConnectedActivityIndicator"));
_connectedActivityIndicator = new wxActivityIndicator(mainPanel, wxID_ANY, wxDefaultPosition, wxDefaultSize, 0, wxT("ConnectedActivityIndicator"));
replaceControl(findNamedObject<wxWindow>(this, "ConnectedActivityIndicator"), _connectedActivityIndicator);
_connectedActivityIndicator->Start();
#else
_connectedActivityIndicator = findNamedObject<wxStaticBitmap>(this, "ConnectedActivityIndicator");
panel; //fix warning
#endif
SetMinSize(panel->GetEffectiveMinSize());
SetMinSize(mainPanel->GetEffectiveMinSize());
Layout();

//don't want to call findNamedObject every time, risking a typo
Expand All @@ -97,7 +57,7 @@ GameConnectionDialog::GameConnectionDialog() :
updateConnectedStatus();
//update GUI when anything changes in connection
_updateOnStatusChangeSignal = Impl().signal_StatusChanged.connect([this](int) {
GameConnectionDialog::updateConnectedStatus();
GameConnectionPanel::updateConnectedStatus();
});

//===================================
Expand All @@ -108,7 +68,7 @@ GameConnectionDialog::GameConnectionDialog() :
if (_connectedCheckbox->IsChecked()) {
bool success = Impl().connect();
if (!success)
showError("Failed to connect to game.\nMaybe try 'Restart game' button?");
wxutil::Messagebox::ShowError("Failed to connect to game.\nMaybe try 'Restart game' button?", this);
}
else
Impl().disconnect(true);
Expand Down Expand Up @@ -152,23 +112,13 @@ GameConnectionDialog::GameConnectionDialog() :
});
}

void GameConnectionDialog::_preShow()
{
TransientWindow::_preShow();
}

void GameConnectionDialog::_preHide()
{
TransientWindow::_preHide();
}

GameConnection& GameConnectionDialog::Impl()
gameconn::GameConnection& GameConnectionPanel::Impl()
{
static module::InstanceReference<GameConnection> _reference("GameConnection");
static module::InstanceReference<gameconn::GameConnection> _reference("GameConnection");
return _reference;
}

void GameConnectionDialog::updateConnectedStatus()
void GameConnectionPanel::updateConnectedStatus()
{
bool connected = Impl().isAlive();
bool restarting = Impl().isGameRestarting();
Expand Down

0 comments on commit 1cdeeb5

Please sign in to comment.