mirror of
https://github.com/vcmi/vcmi.git
synced 2025-01-26 03:52:01 +02:00
Merge pull request #1381 from dydzio0614/client-commands-improvements
Client commands code improvements + ingame support
This commit is contained in:
commit
672bcb488b
452
client/CMT.cpp
452
client/CMT.cpp
@ -13,8 +13,6 @@
|
||||
|
||||
#include <boost/program_options.hpp>
|
||||
|
||||
#include <vcmi/scripting/Service.h>
|
||||
|
||||
#include "gui/SDL_Extensions.h"
|
||||
#include "CGameInfo.h"
|
||||
#include "mapHandler.h"
|
||||
@ -33,32 +31,26 @@
|
||||
#include "../lib/CBuildingHandler.h"
|
||||
#include "CVideoHandler.h"
|
||||
#include "../lib/CHeroHandler.h"
|
||||
#include "../lib/CCreatureHandler.h"
|
||||
#include "../lib/spells/CSpellHandler.h"
|
||||
#include "CMusicHandler.h"
|
||||
#include "../lib/CGeneralTextHandler.h"
|
||||
#include "Graphics.h"
|
||||
#include "Client.h"
|
||||
#include "../lib/CConfigHandler.h"
|
||||
#include "../lib/serializer/BinaryDeserializer.h"
|
||||
#include "../lib/serializer/BinarySerializer.h"
|
||||
#include "../lib/VCMI_Lib.h"
|
||||
#include "../lib/VCMIDirs.h"
|
||||
#include "../lib/NetPacks.h"
|
||||
#include "CMessage.h"
|
||||
#include "../lib/CModHandler.h"
|
||||
#include "../lib/ScriptHandler.h"
|
||||
#include "../lib/CTownHandler.h"
|
||||
#include "../lib/CArtHandler.h"
|
||||
#include "../lib/GameConstants.h"
|
||||
#include "gui/CGuiHandler.h"
|
||||
#include "../lib/logging/CBasicLogConfigurator.h"
|
||||
#include "../lib/StringConstants.h"
|
||||
#include "../lib/CPlayerState.h"
|
||||
#include "gui/CAnimation.h"
|
||||
#include "../lib/serializer/Connection.h"
|
||||
#include "CServerHandler.h"
|
||||
#include "gui/NotificationHandler.h"
|
||||
#include "ClientCommandManager.h"
|
||||
|
||||
#include <boost/asio.hpp>
|
||||
|
||||
@ -71,7 +63,7 @@
|
||||
#ifdef VCMI_ANDROID
|
||||
#include "lib/CAndroidVMHelper.h"
|
||||
#endif
|
||||
#include "../lib/UnlockGuard.h"
|
||||
|
||||
#include "CMT.h"
|
||||
|
||||
#if __MINGW32__
|
||||
@ -249,7 +241,14 @@ int main(int argc, char * argv[])
|
||||
std::cout.flags(std::ios::unitbuf);
|
||||
#ifndef VCMI_IOS
|
||||
console = new CConsoleHandler();
|
||||
*console->cb = processCommand;
|
||||
|
||||
auto callbackFunction = [](std::string buffer, bool calledFromIngameConsole)
|
||||
{
|
||||
ClientCommandManager commandController;
|
||||
commandController.processCommand(buffer, calledFromIngameConsole);
|
||||
};
|
||||
|
||||
*console->cb = callbackFunction;
|
||||
console->start();
|
||||
#endif
|
||||
|
||||
@ -566,437 +565,6 @@ int main(int argc, char * argv[])
|
||||
return 0;
|
||||
}
|
||||
|
||||
void printInfoAboutIntObject(const CIntObject *obj, int level)
|
||||
{
|
||||
std::stringstream sbuffer;
|
||||
sbuffer << std::string(level, '\t');
|
||||
|
||||
sbuffer << typeid(*obj).name() << " *** ";
|
||||
if (obj->active)
|
||||
{
|
||||
#define PRINT(check, text) if (obj->active & CIntObject::check) sbuffer << text
|
||||
PRINT(LCLICK, 'L');
|
||||
PRINT(RCLICK, 'R');
|
||||
PRINT(HOVER, 'H');
|
||||
PRINT(MOVE, 'M');
|
||||
PRINT(KEYBOARD, 'K');
|
||||
PRINT(TIME, 'T');
|
||||
PRINT(GENERAL, 'A');
|
||||
PRINT(WHEEL, 'W');
|
||||
PRINT(DOUBLECLICK, 'D');
|
||||
#undef PRINT
|
||||
}
|
||||
else
|
||||
sbuffer << "inactive";
|
||||
sbuffer << " at " << obj->pos.x <<"x"<< obj->pos.y;
|
||||
sbuffer << " (" << obj->pos.w <<"x"<< obj->pos.h << ")";
|
||||
logGlobal->info(sbuffer.str());
|
||||
|
||||
for(const CIntObject *child : obj->children)
|
||||
printInfoAboutIntObject(child, level+1);
|
||||
}
|
||||
|
||||
void removeGUI()
|
||||
{
|
||||
// CClient::endGame
|
||||
GH.curInt = nullptr;
|
||||
if(GH.topInt())
|
||||
GH.topInt()->deactivate();
|
||||
adventureInt = nullptr;
|
||||
GH.listInt.clear();
|
||||
GH.objsToBlit.clear();
|
||||
GH.statusbar = nullptr;
|
||||
logGlobal->info("Removed GUI.");
|
||||
|
||||
LOCPLINT = nullptr;
|
||||
}
|
||||
|
||||
#ifndef VCMI_IOS
|
||||
void processCommand(const std::string &message)
|
||||
{
|
||||
std::istringstream readed;
|
||||
readed.str(message);
|
||||
std::string cn; //command name
|
||||
readed >> cn;
|
||||
|
||||
// Check mantis issue 2292 for details
|
||||
// if(LOCPLINT && LOCPLINT->cingconsole)
|
||||
// LOCPLINT->cingconsole->print(message);
|
||||
|
||||
if(message==std::string("die, fool"))
|
||||
{
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
||||
else if(cn==std::string("activate"))
|
||||
{
|
||||
int what;
|
||||
readed >> what;
|
||||
switch (what)
|
||||
{
|
||||
case 0:
|
||||
GH.topInt()->activate();
|
||||
break;
|
||||
case 1:
|
||||
adventureInt->activate();
|
||||
break;
|
||||
case 2:
|
||||
LOCPLINT->castleInt->activate();
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if(cn=="redraw")
|
||||
{
|
||||
GH.totalRedraw();
|
||||
}
|
||||
else if(cn=="screen")
|
||||
{
|
||||
std::cout << "Screenbuf points to ";
|
||||
|
||||
if(screenBuf == screen)
|
||||
logGlobal->error("screen");
|
||||
else if(screenBuf == screen2)
|
||||
logGlobal->error("screen2");
|
||||
else
|
||||
logGlobal->error("?!?");
|
||||
|
||||
SDL_SaveBMP(screen, "Screen_c.bmp");
|
||||
SDL_SaveBMP(screen2, "Screen2_c.bmp");
|
||||
}
|
||||
else if(cn=="save")
|
||||
{
|
||||
if(!CSH->client)
|
||||
{
|
||||
std::cout << "Game in not active";
|
||||
return;
|
||||
}
|
||||
std::string fname;
|
||||
readed >> fname;
|
||||
CSH->client->save(fname);
|
||||
}
|
||||
// else if(cn=="load")
|
||||
// {
|
||||
// // TODO: this code should end the running game and manage to call startGame instead
|
||||
// std::string fname;
|
||||
// readed >> fname;
|
||||
// CSH->client->loadGame(fname);
|
||||
// }
|
||||
else if(message=="convert txt")
|
||||
{
|
||||
VLC->generaltexth->dumpAllTexts();
|
||||
}
|
||||
else if(message=="get config")
|
||||
{
|
||||
std::cout << "Command accepted.\t";
|
||||
|
||||
const bfs::path outPath =
|
||||
VCMIDirs::get().userExtractedPath() / "configuration";
|
||||
|
||||
bfs::create_directories(outPath);
|
||||
|
||||
const std::vector<std::string> contentNames = {"heroClasses", "artifacts", "creatures", "factions", "objects", "heroes", "spells", "skills"};
|
||||
|
||||
for(auto contentName : contentNames)
|
||||
{
|
||||
auto & content = (*VLC->modh->content)[contentName];
|
||||
|
||||
auto contentOutPath = outPath / contentName;
|
||||
bfs::create_directories(contentOutPath);
|
||||
|
||||
for(auto & iter : content.modData)
|
||||
{
|
||||
const JsonNode & modData = iter.second.modData;
|
||||
|
||||
for(auto & nameAndObject : modData.Struct())
|
||||
{
|
||||
const JsonNode & object = nameAndObject.second;
|
||||
|
||||
std::string name = CModHandler::normalizeIdentifier(object.meta, CModHandler::scopeBuiltin(), nameAndObject.first);
|
||||
|
||||
boost::algorithm::replace_all(name,":","_");
|
||||
|
||||
const bfs::path filePath = contentOutPath / (name + ".json");
|
||||
bfs::ofstream file(filePath);
|
||||
file << object.toJson();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::cout << "\rExtracting done :)\n";
|
||||
std::cout << " Extracted files can be found in " << outPath << " directory\n";
|
||||
}
|
||||
#if SCRIPTING_ENABLED
|
||||
else if(message=="get scripts")
|
||||
{
|
||||
std::cout << "Command accepted.\t";
|
||||
|
||||
const bfs::path outPath =
|
||||
VCMIDirs::get().userExtractedPath() / "scripts";
|
||||
|
||||
bfs::create_directories(outPath);
|
||||
|
||||
for(auto & kv : VLC->scriptHandler->objects)
|
||||
{
|
||||
std::string name = kv.first;
|
||||
boost::algorithm::replace_all(name,":","_");
|
||||
|
||||
const scripting::ScriptImpl * script = kv.second.get();
|
||||
bfs::path filePath = outPath / (name + ".lua");
|
||||
bfs::ofstream file(filePath);
|
||||
file << script->getSource();
|
||||
}
|
||||
std::cout << "\rExtracting done :)\n";
|
||||
std::cout << " Extracted files can be found in " << outPath << " directory\n";
|
||||
}
|
||||
#endif
|
||||
else if(message=="get txt")
|
||||
{
|
||||
std::cout << "Command accepted.\t";
|
||||
|
||||
const bfs::path outPath =
|
||||
VCMIDirs::get().userExtractedPath();
|
||||
|
||||
auto list = CResourceHandler::get()->getFilteredFiles([](const ResourceID & ident)
|
||||
{
|
||||
return ident.getType() == EResType::TEXT && boost::algorithm::starts_with(ident.getName(), "DATA/");
|
||||
});
|
||||
|
||||
for (auto & filename : list)
|
||||
{
|
||||
const bfs::path filePath = outPath / (filename.getName() + ".TXT");
|
||||
|
||||
bfs::create_directories(filePath.parent_path());
|
||||
|
||||
bfs::ofstream file(filePath);
|
||||
auto text = CResourceHandler::get()->load(filename)->readAll();
|
||||
|
||||
file.write((char*)text.first.get(), text.second);
|
||||
}
|
||||
|
||||
std::cout << "\rExtracting done :)\n";
|
||||
std::cout << " Extracted files can be found in " << outPath << " directory\n";
|
||||
}
|
||||
else if(cn=="crash")
|
||||
{
|
||||
int *ptr = nullptr;
|
||||
*ptr = 666;
|
||||
//disaster!
|
||||
}
|
||||
else if(cn == "mp" && adventureInt)
|
||||
{
|
||||
if(const CGHeroInstance *h = dynamic_cast<const CGHeroInstance *>(adventureInt->selection))
|
||||
std::cout << h->movement << "; max: " << h->maxMovePoints(true) << "/" << h->maxMovePoints(false) << std::endl;
|
||||
}
|
||||
else if(cn == "bonuses")
|
||||
{
|
||||
bool jsonFormat = (message == "bonuses json");
|
||||
auto format = [jsonFormat](const BonusList & b) -> std::string
|
||||
{
|
||||
if(jsonFormat)
|
||||
return b.toJsonNode().toJson(true);
|
||||
std::ostringstream ss;
|
||||
ss << b;
|
||||
return ss.str();
|
||||
};
|
||||
std::cout << "Bonuses of " << adventureInt->selection->getObjectName() << std::endl
|
||||
<< format(adventureInt->selection->getBonusList()) << std::endl;
|
||||
|
||||
std::cout << "\nInherited bonuses:\n";
|
||||
TCNodes parents;
|
||||
adventureInt->selection->getParents(parents);
|
||||
for(const CBonusSystemNode *parent : parents)
|
||||
{
|
||||
std::cout << "\nBonuses from " << typeid(*parent).name() << std::endl << format(*parent->getAllBonuses(Selector::all, Selector::all)) << std::endl;
|
||||
}
|
||||
}
|
||||
else if(cn == "not dialog")
|
||||
{
|
||||
LOCPLINT->showingDialog->setn(false);
|
||||
}
|
||||
else if(cn == "gui")
|
||||
{
|
||||
for(auto & child : GH.listInt)
|
||||
{
|
||||
const auto childPtr = child.get();
|
||||
if(const CIntObject * obj = dynamic_cast<const CIntObject *>(childPtr))
|
||||
printInfoAboutIntObject(obj, 0);
|
||||
else
|
||||
std::cout << typeid(childPtr).name() << std::endl;
|
||||
}
|
||||
}
|
||||
else if(cn=="tell")
|
||||
{
|
||||
std::string what;
|
||||
int id1, id2;
|
||||
readed >> what >> id1 >> id2;
|
||||
if(what == "hs")
|
||||
{
|
||||
for(const CGHeroInstance *h : LOCPLINT->cb->getHeroesInfo())
|
||||
if(h->type->ID.getNum() == id1)
|
||||
if(const CArtifactInstance *a = h->getArt(ArtifactPosition(id2)))
|
||||
std::cout << a->nodeName();
|
||||
}
|
||||
}
|
||||
else if (cn == "set")
|
||||
{
|
||||
std::string what, value;
|
||||
readed >> what;
|
||||
|
||||
Settings conf = settings.write["session"][what];
|
||||
|
||||
readed >> value;
|
||||
|
||||
if (value == "on")
|
||||
{
|
||||
conf->Bool() = true;
|
||||
logGlobal->info("Option %s enabled!", what);
|
||||
}
|
||||
else if (value == "off")
|
||||
{
|
||||
conf->Bool() = false;
|
||||
logGlobal->info("Option %s disabled!", what);
|
||||
}
|
||||
}
|
||||
else if(cn == "unlock")
|
||||
{
|
||||
std::string mxname;
|
||||
readed >> mxname;
|
||||
if(mxname == "pim" && LOCPLINT)
|
||||
LOCPLINT->pim->unlock();
|
||||
}
|
||||
else if(cn == "def2bmp")
|
||||
{
|
||||
std::string URI;
|
||||
readed >> URI;
|
||||
std::unique_ptr<CAnimation> anim = std::make_unique<CAnimation>(URI);
|
||||
anim->preload();
|
||||
anim->exportBitmaps(VCMIDirs::get().userExtractedPath());
|
||||
}
|
||||
else if(cn == "extract")
|
||||
{
|
||||
std::string URI;
|
||||
readed >> URI;
|
||||
|
||||
if (CResourceHandler::get()->existsResource(ResourceID(URI)))
|
||||
{
|
||||
const bfs::path outPath = VCMIDirs::get().userExtractedPath() / URI;
|
||||
|
||||
auto data = CResourceHandler::get()->load(ResourceID(URI))->readAll();
|
||||
|
||||
bfs::create_directories(outPath.parent_path());
|
||||
bfs::ofstream outFile(outPath, bfs::ofstream::binary);
|
||||
outFile.write((char*)data.first.get(), data.second);
|
||||
}
|
||||
else
|
||||
logGlobal->error("File not found!");
|
||||
}
|
||||
else if(cn == "setBattleAI")
|
||||
{
|
||||
std::string fname;
|
||||
readed >> fname;
|
||||
std::cout << "Will try loading that AI to see if it is correct name...\n";
|
||||
try
|
||||
{
|
||||
if(auto ai = CDynLibHandler::getNewBattleAI(fname)) //test that given AI is indeed available... heavy but it is easy to make a typo and break the game
|
||||
{
|
||||
Settings neutralAI = settings.write["server"]["neutralAI"];
|
||||
neutralAI->String() = fname;
|
||||
std::cout << "Setting changed, from now the battle ai will be " << fname << "!\n";
|
||||
}
|
||||
}
|
||||
catch(std::exception &e)
|
||||
{
|
||||
logGlobal->warn("Failed opening %s: %s", fname, e.what());
|
||||
logGlobal->warn("Setting not changes, AI not found or invalid!");
|
||||
}
|
||||
}
|
||||
|
||||
auto giveTurn = [&](PlayerColor player)
|
||||
{
|
||||
YourTurn yt;
|
||||
yt.player = player;
|
||||
yt.daysWithoutCastle = CSH->client->getPlayerState(player)->daysWithoutCastle;
|
||||
yt.applyCl(CSH->client);
|
||||
};
|
||||
|
||||
Settings session = settings.write["session"];
|
||||
if(cn == "autoskip")
|
||||
{
|
||||
session["autoSkip"].Bool() = !session["autoSkip"].Bool();
|
||||
}
|
||||
else if(cn == "gosolo")
|
||||
{
|
||||
boost::unique_lock<boost::recursive_mutex> un(*CPlayerInterface::pim);
|
||||
if(!CSH->client)
|
||||
{
|
||||
std::cout << "Game in not active";
|
||||
return;
|
||||
}
|
||||
PlayerColor color;
|
||||
if(session["aiSolo"].Bool())
|
||||
{
|
||||
for(auto & elem : CSH->client->gameState()->players)
|
||||
{
|
||||
if(elem.second.human)
|
||||
CSH->client->installNewPlayerInterface(std::make_shared<CPlayerInterface>(elem.first), elem.first);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
color = LOCPLINT->playerID;
|
||||
removeGUI();
|
||||
for(auto & elem : CSH->client->gameState()->players)
|
||||
{
|
||||
if(elem.second.human)
|
||||
{
|
||||
auto AiToGive = CSH->client->aiNameForPlayer(*CSH->client->getPlayerSettings(elem.first), false);
|
||||
logNetwork->info("Player %s will be lead by %s", elem.first, AiToGive);
|
||||
CSH->client->installNewPlayerInterface(CDynLibHandler::getNewAI(AiToGive), elem.first);
|
||||
}
|
||||
}
|
||||
GH.totalRedraw();
|
||||
giveTurn(color);
|
||||
}
|
||||
session["aiSolo"].Bool() = !session["aiSolo"].Bool();
|
||||
}
|
||||
else if(cn == "controlai")
|
||||
{
|
||||
std::string colorName;
|
||||
readed >> colorName;
|
||||
boost::to_lower(colorName);
|
||||
|
||||
boost::unique_lock<boost::recursive_mutex> un(*CPlayerInterface::pim);
|
||||
if(!CSH->client)
|
||||
{
|
||||
std::cout << "Game in not active";
|
||||
return;
|
||||
}
|
||||
PlayerColor color;
|
||||
if(LOCPLINT)
|
||||
color = LOCPLINT->playerID;
|
||||
for(auto & elem : CSH->client->gameState()->players)
|
||||
{
|
||||
if(elem.second.human || (colorName.length() &&
|
||||
elem.first.getNum() != vstd::find_pos(GameConstants::PLAYER_COLOR_NAMES, colorName)))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
removeGUI();
|
||||
CSH->client->installNewPlayerInterface(std::make_shared<CPlayerInterface>(elem.first), elem.first);
|
||||
}
|
||||
GH.totalRedraw();
|
||||
if(color != PlayerColor::NEUTRAL)
|
||||
giveTurn(color);
|
||||
}
|
||||
// Check mantis issue 2292 for details
|
||||
/* else if(client && client->serv && client->serv->connected && LOCPLINT) //send to server
|
||||
{
|
||||
boost::unique_lock<boost::recursive_mutex> un(*CPlayerInterface::pim);
|
||||
LOCPLINT->cb->sendMessage(message);
|
||||
}*/
|
||||
}
|
||||
#endif
|
||||
|
||||
//plays intro, ends when intro is over or button has been pressed (handles events)
|
||||
void playIntro()
|
||||
{
|
||||
|
@ -19,5 +19,4 @@ extern SDL_Surface *screen; // main screen surface
|
||||
extern SDL_Surface *screen2; // and hlp surface (used to store not-active interfaces layer)
|
||||
extern SDL_Surface *screenBuf; // points to screen (if only advmapint is present) or screen2 (else) - should be used when updating controls which are not regularly redrawed
|
||||
|
||||
void removeGUI();
|
||||
void handleQuit(bool ask = true);
|
||||
|
@ -82,6 +82,7 @@ set(client_SRCS
|
||||
NetPacksClient.cpp
|
||||
NetPacksLobbyClient.cpp
|
||||
SDLRWwrapper.cpp
|
||||
ClientCommandManager.cpp
|
||||
)
|
||||
|
||||
set(client_HEADERS
|
||||
@ -168,6 +169,7 @@ set(client_HEADERS
|
||||
mapHandler.h
|
||||
resource.h
|
||||
SDLRWwrapper.h
|
||||
ClientCommandManager.h
|
||||
)
|
||||
|
||||
if(APPLE_IOS)
|
||||
|
@ -46,9 +46,9 @@
|
||||
#include "../lib/CThreadHelper.h"
|
||||
#include "../lib/registerTypes/RegisterTypes.h"
|
||||
#include "gui/CGuiHandler.h"
|
||||
#include "CMT.h"
|
||||
#include "CServerHandler.h"
|
||||
#include "../lib/ScriptHandler.h"
|
||||
#include "windows/CAdvmapInterface.h"
|
||||
#include <vcmi/events/EventBus.h>
|
||||
|
||||
#ifdef VCMI_ANDROID
|
||||
@ -761,6 +761,21 @@ void CClient::reinitScripting()
|
||||
#endif
|
||||
}
|
||||
|
||||
void CClient::removeGUI()
|
||||
{
|
||||
// CClient::endGame
|
||||
GH.curInt = nullptr;
|
||||
if(GH.topInt())
|
||||
GH.topInt()->deactivate();
|
||||
adventureInt.reset();
|
||||
GH.listInt.clear();
|
||||
GH.objsToBlit.clear();
|
||||
GH.statusbar.reset();
|
||||
logGlobal->info("Removed GUI.");
|
||||
|
||||
LOCPLINT = nullptr;
|
||||
}
|
||||
|
||||
#ifdef VCMI_ANDROID
|
||||
extern "C" JNIEXPORT void JNICALL Java_eu_vcmi_vcmi_NativeMethods_clientSetupJNI(JNIEnv * env, jobject cls)
|
||||
{
|
||||
|
@ -240,6 +240,7 @@ public:
|
||||
|
||||
void showInfoDialog(InfoWindow * iw) override {};
|
||||
void showInfoDialog(const std::string & msg, PlayerColor player) override {};
|
||||
void removeGUI();
|
||||
|
||||
#if SCRIPTING_ENABLED
|
||||
scripting::Pool * getGlobalContextPool() const override;
|
||||
|
494
client/ClientCommandManager.cpp
Normal file
494
client/ClientCommandManager.cpp
Normal file
@ -0,0 +1,494 @@
|
||||
/*
|
||||
* ClientCommandManager.cpp, part of VCMI engine
|
||||
*
|
||||
* Authors: listed in file AUTHORS in main folder
|
||||
*
|
||||
* License: GNU General Public License v2.0 or later
|
||||
* Full text of license available in license.txt file, in main folder
|
||||
*
|
||||
*/
|
||||
#include "StdInc.h"
|
||||
|
||||
#include "ClientCommandManager.h"
|
||||
|
||||
#include "Client.h"
|
||||
#include "CPlayerInterface.h"
|
||||
#include "CServerHandler.h"
|
||||
#include "gui/CGuiHandler.h"
|
||||
#include "../lib/NetPacks.h"
|
||||
#include "../lib/CConfigHandler.h"
|
||||
#include "../lib/CGameState.h"
|
||||
#include "../lib/CPlayerState.h"
|
||||
#include "../lib/StringConstants.h"
|
||||
#include "gui/CAnimation.h"
|
||||
#include "windows/CAdvmapInterface.h"
|
||||
#include "windows/CCastleInterface.h"
|
||||
#include "../CCallback.h"
|
||||
#include "../lib/CGeneralTextHandler.h"
|
||||
#include "../lib/CHeroHandler.h"
|
||||
#include "../lib/CModHandler.h"
|
||||
#include "../lib/VCMIDirs.h"
|
||||
|
||||
#ifdef SCRIPTING_ENABLED
|
||||
#include "../lib/ScriptHandler.h"
|
||||
#endif
|
||||
|
||||
void ClientCommandManager::handleGoSolo()
|
||||
{
|
||||
Settings session = settings.write["session"];
|
||||
|
||||
boost::unique_lock<boost::recursive_mutex> un(*CPlayerInterface::pim);
|
||||
if(!CSH->client)
|
||||
{
|
||||
printCommandMessage("Game is not in playing state");
|
||||
return;
|
||||
}
|
||||
PlayerColor color;
|
||||
if(session["aiSolo"].Bool())
|
||||
{
|
||||
for(auto & elem : CSH->client->gameState()->players)
|
||||
{
|
||||
if(elem.second.human)
|
||||
CSH->client->installNewPlayerInterface(std::make_shared<CPlayerInterface>(elem.first), elem.first);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
color = LOCPLINT->playerID;
|
||||
CSH->client->removeGUI();
|
||||
for(auto & elem : CSH->client->gameState()->players)
|
||||
{
|
||||
if(elem.second.human)
|
||||
{
|
||||
auto AiToGive = CSH->client->aiNameForPlayer(*CSH->client->getPlayerSettings(elem.first), false);
|
||||
printCommandMessage("Player " + elem.first.getStr() + " will be lead by " + AiToGive, ELogLevel::INFO);
|
||||
CSH->client->installNewPlayerInterface(CDynLibHandler::getNewAI(AiToGive), elem.first);
|
||||
}
|
||||
}
|
||||
GH.totalRedraw();
|
||||
giveTurn(color);
|
||||
}
|
||||
session["aiSolo"].Bool() = !session["aiSolo"].Bool();
|
||||
}
|
||||
|
||||
void ClientCommandManager::handleControlAi(const std::string &colorName)
|
||||
{
|
||||
boost::unique_lock<boost::recursive_mutex> un(*CPlayerInterface::pim);
|
||||
if(!CSH->client)
|
||||
{
|
||||
printCommandMessage("Game is not in playing state");
|
||||
return;
|
||||
}
|
||||
PlayerColor color;
|
||||
if(LOCPLINT)
|
||||
color = LOCPLINT->playerID;
|
||||
for(auto & elem : CSH->client->gameState()->players)
|
||||
{
|
||||
if(elem.second.human || (colorName.length() &&
|
||||
elem.first.getNum() != vstd::find_pos(GameConstants::PLAYER_COLOR_NAMES, colorName)))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
CSH->client->removeGUI();
|
||||
CSH->client->installNewPlayerInterface(std::make_shared<CPlayerInterface>(elem.first), elem.first);
|
||||
}
|
||||
GH.totalRedraw();
|
||||
if(color != PlayerColor::NEUTRAL)
|
||||
giveTurn(color);
|
||||
}
|
||||
|
||||
void ClientCommandManager::processCommand(const std::string &message, bool calledFromIngameConsole)
|
||||
{
|
||||
std::istringstream singleWordBuffer;
|
||||
singleWordBuffer.str(message);
|
||||
std::string commandName;
|
||||
singleWordBuffer >> commandName;
|
||||
currentCallFromIngameConsole = calledFromIngameConsole;
|
||||
|
||||
if(message==std::string("die, fool"))
|
||||
{
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
||||
else if(commandName == std::string("activate"))
|
||||
{
|
||||
int what;
|
||||
singleWordBuffer >> what;
|
||||
switch (what)
|
||||
{
|
||||
case 0:
|
||||
GH.topInt()->activate();
|
||||
break;
|
||||
case 1:
|
||||
adventureInt->activate();
|
||||
break;
|
||||
case 2:
|
||||
LOCPLINT->castleInt->activate();
|
||||
break;
|
||||
default:
|
||||
printCommandMessage("Wrong argument specified!", ELogLevel::ERROR);
|
||||
}
|
||||
}
|
||||
else if(commandName == "redraw")
|
||||
{
|
||||
GH.totalRedraw();
|
||||
}
|
||||
else if(commandName == "screen")
|
||||
{
|
||||
printCommandMessage("Screenbuf points to ");
|
||||
|
||||
if(screenBuf == screen)
|
||||
printCommandMessage("screen", ELogLevel::ERROR);
|
||||
else if(screenBuf == screen2)
|
||||
printCommandMessage("screen2", ELogLevel::ERROR);
|
||||
else
|
||||
printCommandMessage("?!?", ELogLevel::ERROR);
|
||||
|
||||
SDL_SaveBMP(screen, "Screen_c.bmp");
|
||||
SDL_SaveBMP(screen2, "Screen2_c.bmp");
|
||||
}
|
||||
else if(commandName == "save")
|
||||
{
|
||||
if(!CSH->client)
|
||||
{
|
||||
printCommandMessage("Game is not in playing state");
|
||||
return;
|
||||
}
|
||||
std::string fname;
|
||||
singleWordBuffer >> fname;
|
||||
CSH->client->save(fname);
|
||||
}
|
||||
// else if(commandName=="load")
|
||||
// {
|
||||
// // TODO: this code should end the running game and manage to call startGame instead
|
||||
// std::string fname;
|
||||
// singleWordBuffer >> fname;
|
||||
// CSH->client->loadGame(fname);
|
||||
// }
|
||||
else if(message=="convert txt")
|
||||
{
|
||||
VLC->generaltexth->dumpAllTexts();
|
||||
}
|
||||
else if(message=="get config")
|
||||
{
|
||||
printCommandMessage("Command accepted.\t");
|
||||
|
||||
const boost::filesystem::path outPath =
|
||||
VCMIDirs::get().userExtractedPath() / "configuration";
|
||||
|
||||
boost::filesystem::create_directories(outPath);
|
||||
|
||||
const std::vector<std::string> contentNames = {"heroClasses", "artifacts", "creatures", "factions", "objects", "heroes", "spells", "skills"};
|
||||
|
||||
for(auto contentName : contentNames)
|
||||
{
|
||||
auto & content = (*VLC->modh->content)[contentName];
|
||||
|
||||
auto contentOutPath = outPath / contentName;
|
||||
boost::filesystem::create_directories(contentOutPath);
|
||||
|
||||
for(auto & iter : content.modData)
|
||||
{
|
||||
const JsonNode & modData = iter.second.modData;
|
||||
|
||||
for(auto & nameAndObject : modData.Struct())
|
||||
{
|
||||
const JsonNode & object = nameAndObject.second;
|
||||
|
||||
std::string name = CModHandler::normalizeIdentifier(object.meta, CModHandler::scopeBuiltin(), nameAndObject.first);
|
||||
|
||||
boost::algorithm::replace_all(name,":","_");
|
||||
|
||||
const boost::filesystem::path filePath = contentOutPath / (name + ".json");
|
||||
boost::filesystem::ofstream file(filePath);
|
||||
file << object.toJson();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
printCommandMessage("\rExtracting done :)\n");
|
||||
printCommandMessage("Extracted files can be found in " + outPath.string() + " directory\n");
|
||||
}
|
||||
#if SCRIPTING_ENABLED
|
||||
else if(message=="get scripts")
|
||||
{
|
||||
printCommandMessage("Command accepted.\t");
|
||||
|
||||
const boost::filesystem::path outPath =
|
||||
VCMIDirs::get().userExtractedPath() / "scripts";
|
||||
|
||||
boost::filesystem::create_directories(outPath);
|
||||
|
||||
for(auto & kv : VLC->scriptHandler->objects)
|
||||
{
|
||||
std::string name = kv.first;
|
||||
boost::algorithm::replace_all(name,":","_");
|
||||
|
||||
const scripting::ScriptImpl * script = kv.second.get();
|
||||
boost::filesystem::path filePath = outPath / (name + ".lua");
|
||||
boost::filesystem::ofstream file(filePath);
|
||||
file << script->getSource();
|
||||
}
|
||||
printCommandMessage("\rExtracting done :)\n");
|
||||
printCommandMessage("Extracted files can be found in " + outPath.string() + " directory\n");
|
||||
}
|
||||
#endif
|
||||
else if(message=="get txt")
|
||||
{
|
||||
printCommandMessage("Command accepted.\t");
|
||||
|
||||
const boost::filesystem::path outPath =
|
||||
VCMIDirs::get().userExtractedPath();
|
||||
|
||||
auto list =
|
||||
CResourceHandler::get()->getFilteredFiles([](const ResourceID & ident)
|
||||
{
|
||||
return ident.getType() == EResType::TEXT && boost::algorithm::starts_with(ident.getName(), "DATA/");
|
||||
});
|
||||
|
||||
for (auto & filename : list)
|
||||
{
|
||||
const boost::filesystem::path filePath = outPath / (filename.getName() + ".TXT");
|
||||
|
||||
boost::filesystem::create_directories(filePath.parent_path());
|
||||
|
||||
boost::filesystem::ofstream file(filePath);
|
||||
auto text = CResourceHandler::get()->load(filename)->readAll();
|
||||
|
||||
file.write((char*)text.first.get(), text.second);
|
||||
}
|
||||
|
||||
printCommandMessage("\rExtracting done :)\n");
|
||||
printCommandMessage("Extracted files can be found in " + outPath.string() + " directory\n");
|
||||
}
|
||||
else if(commandName == "crash")
|
||||
{
|
||||
int *ptr = nullptr;
|
||||
*ptr = 666;
|
||||
//disaster!
|
||||
}
|
||||
else if(commandName == "mp" && adventureInt)
|
||||
{
|
||||
if(const CGHeroInstance *h = dynamic_cast<const CGHeroInstance *>(adventureInt->selection))
|
||||
printCommandMessage(std::to_string(h->movement) + "; max: " + std::to_string(h->maxMovePoints(true)) + "/" + std::to_string(h->maxMovePoints(false)) + "\n");
|
||||
}
|
||||
else if(commandName == "bonuses")
|
||||
{
|
||||
bool jsonFormat = (message == "bonuses json");
|
||||
auto format = [jsonFormat](const BonusList & b) -> std::string
|
||||
{
|
||||
if(jsonFormat)
|
||||
return b.toJsonNode().toJson(true);
|
||||
std::ostringstream ss;
|
||||
ss << b;
|
||||
return ss.str();
|
||||
};
|
||||
printCommandMessage("Bonuses of " + adventureInt->selection->getObjectName() + "\n");
|
||||
printCommandMessage(format(adventureInt->selection->getBonusList()) + "\n");
|
||||
|
||||
printCommandMessage("\nInherited bonuses:\n");
|
||||
TCNodes parents;
|
||||
adventureInt->selection->getParents(parents);
|
||||
for(const CBonusSystemNode *parent : parents)
|
||||
{
|
||||
printCommandMessage(std::string("\nBonuses from ") + typeid(*parent).name() + "\n" + format(*parent->getAllBonuses(Selector::all, Selector::all)) + "\n");
|
||||
}
|
||||
}
|
||||
else if(commandName == "not dialog")
|
||||
{
|
||||
LOCPLINT->showingDialog->setn(false);
|
||||
}
|
||||
else if(commandName == "gui")
|
||||
{
|
||||
for(auto & child : GH.listInt)
|
||||
{
|
||||
const auto childPtr = child.get();
|
||||
if(const CIntObject * obj = dynamic_cast<const CIntObject *>(childPtr))
|
||||
printInfoAboutInterfaceObject(obj, 0);
|
||||
else
|
||||
printCommandMessage(std::string(typeid(childPtr).name()) + "\n");
|
||||
}
|
||||
}
|
||||
else if(commandName == "tell")
|
||||
{
|
||||
std::string what;
|
||||
int id1, id2;
|
||||
singleWordBuffer >> what >> id1 >> id2;
|
||||
if(what == "hs")
|
||||
{
|
||||
for(const CGHeroInstance *h : LOCPLINT->cb->getHeroesInfo())
|
||||
if(h->type->ID.getNum() == id1)
|
||||
if(const CArtifactInstance *a = h->getArt(ArtifactPosition(id2)))
|
||||
printCommandMessage(a->nodeName());
|
||||
}
|
||||
}
|
||||
else if (commandName == "set")
|
||||
{
|
||||
std::string what, value;
|
||||
singleWordBuffer >> what;
|
||||
|
||||
Settings config = settings.write["session"][what];
|
||||
|
||||
singleWordBuffer >> value;
|
||||
|
||||
if (value == "on")
|
||||
{
|
||||
config->Bool() = true;
|
||||
printCommandMessage("Option " + what + " enabled!", ELogLevel::INFO);
|
||||
}
|
||||
else if (value == "off")
|
||||
{
|
||||
config->Bool() = false;
|
||||
printCommandMessage("Option " + what + " disabled!", ELogLevel::INFO);
|
||||
}
|
||||
}
|
||||
else if(commandName == "unlock")
|
||||
{
|
||||
std::string mxname;
|
||||
singleWordBuffer >> mxname;
|
||||
if(mxname == "pim" && LOCPLINT)
|
||||
LOCPLINT->pim->unlock();
|
||||
}
|
||||
else if(commandName == "def2bmp")
|
||||
{
|
||||
std::string URI;
|
||||
singleWordBuffer >> URI;
|
||||
std::unique_ptr<CAnimation> anim = std::make_unique<CAnimation>(URI);
|
||||
anim->preload();
|
||||
anim->exportBitmaps(VCMIDirs::get().userExtractedPath());
|
||||
}
|
||||
else if(commandName == "extract")
|
||||
{
|
||||
std::string URI;
|
||||
singleWordBuffer >> URI;
|
||||
|
||||
if (CResourceHandler::get()->existsResource(ResourceID(URI)))
|
||||
{
|
||||
const boost::filesystem::path outPath = VCMIDirs::get().userExtractedPath() / URI;
|
||||
|
||||
auto data = CResourceHandler::get()->load(ResourceID(URI))->readAll();
|
||||
|
||||
boost::filesystem::create_directories(outPath.parent_path());
|
||||
boost::filesystem::ofstream outFile(outPath, boost::filesystem::ofstream::binary);
|
||||
outFile.write((char*)data.first.get(), data.second);
|
||||
}
|
||||
else
|
||||
printCommandMessage("File not found!", ELogLevel::ERROR);
|
||||
}
|
||||
else if(commandName == "setBattleAI")
|
||||
{
|
||||
std::string fname;
|
||||
singleWordBuffer >> fname;
|
||||
printCommandMessage("Will try loading that AI to see if it is correct name...\n");
|
||||
try
|
||||
{
|
||||
if(auto ai = CDynLibHandler::getNewBattleAI(fname)) //test that given AI is indeed available... heavy but it is easy to make a typo and break the game
|
||||
{
|
||||
Settings neutralAI = settings.write["server"]["neutralAI"];
|
||||
neutralAI->String() = fname;
|
||||
printCommandMessage("Setting changed, from now the battle ai will be " + fname + "!\n");
|
||||
}
|
||||
}
|
||||
catch(std::exception &e)
|
||||
{
|
||||
printCommandMessage("Failed opening " + fname + ": " + e.what(), ELogLevel::WARN);
|
||||
printCommandMessage("Setting not changed, AI not found or invalid!", ELogLevel::WARN);
|
||||
}
|
||||
}
|
||||
else if(commandName == "autoskip")
|
||||
{
|
||||
Settings session = settings.write["session"];
|
||||
session["autoSkip"].Bool() = !session["autoSkip"].Bool();
|
||||
}
|
||||
else if(commandName == "gosolo")
|
||||
{
|
||||
ClientCommandManager::handleGoSolo();
|
||||
}
|
||||
else if(commandName == "controlai")
|
||||
{
|
||||
std::string colorName;
|
||||
singleWordBuffer >> colorName;
|
||||
boost::to_lower(colorName);
|
||||
|
||||
ClientCommandManager::handleControlAi(colorName);
|
||||
}
|
||||
else
|
||||
{
|
||||
printCommandMessage("Command not found :(", ELogLevel::ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
void ClientCommandManager::giveTurn(const PlayerColor &colorIdentifier)
|
||||
{
|
||||
YourTurn yt;
|
||||
yt.player = colorIdentifier;
|
||||
yt.daysWithoutCastle = CSH->client->getPlayerState(colorIdentifier)->daysWithoutCastle;
|
||||
yt.applyCl(CSH->client);
|
||||
}
|
||||
|
||||
void ClientCommandManager::printInfoAboutInterfaceObject(const CIntObject *obj, int level)
|
||||
{
|
||||
std::stringstream sbuffer;
|
||||
sbuffer << std::string(level, '\t');
|
||||
|
||||
sbuffer << typeid(*obj).name() << " *** ";
|
||||
if (obj->active)
|
||||
{
|
||||
#define PRINT(check, text) if (obj->active & CIntObject::check) sbuffer << text
|
||||
PRINT(LCLICK, 'L');
|
||||
PRINT(RCLICK, 'R');
|
||||
PRINT(HOVER, 'H');
|
||||
PRINT(MOVE, 'M');
|
||||
PRINT(KEYBOARD, 'K');
|
||||
PRINT(TIME, 'T');
|
||||
PRINT(GENERAL, 'A');
|
||||
PRINT(WHEEL, 'W');
|
||||
PRINT(DOUBLECLICK, 'D');
|
||||
#undef PRINT
|
||||
}
|
||||
else
|
||||
sbuffer << "inactive";
|
||||
sbuffer << " at " << obj->pos.x <<"x"<< obj->pos.y;
|
||||
sbuffer << " (" << obj->pos.w <<"x"<< obj->pos.h << ")";
|
||||
printCommandMessage(sbuffer.str(), ELogLevel::INFO);
|
||||
|
||||
for(const CIntObject *child : obj->children)
|
||||
printInfoAboutInterfaceObject(child, level+1);
|
||||
}
|
||||
|
||||
void ClientCommandManager::printCommandMessage(const std::string &commandMessage, ELogLevel::ELogLevel messageType)
|
||||
{
|
||||
switch(messageType)
|
||||
{
|
||||
case ELogLevel::NOT_SET:
|
||||
std::cout << commandMessage;
|
||||
break;
|
||||
case ELogLevel::TRACE:
|
||||
logGlobal->trace(commandMessage);
|
||||
break;
|
||||
case ELogLevel::DEBUG:
|
||||
logGlobal->debug(commandMessage);
|
||||
break;
|
||||
case ELogLevel::INFO:
|
||||
logGlobal->info(commandMessage);
|
||||
break;
|
||||
case ELogLevel::WARN:
|
||||
logGlobal->warn(commandMessage);
|
||||
break;
|
||||
case ELogLevel::ERROR:
|
||||
logGlobal->error(commandMessage);
|
||||
break;
|
||||
default:
|
||||
std::cout << commandMessage;
|
||||
break;
|
||||
}
|
||||
|
||||
if(currentCallFromIngameConsole)
|
||||
{
|
||||
boost::unique_lock<boost::recursive_mutex> un(*CPlayerInterface::pim);
|
||||
if(LOCPLINT && LOCPLINT->cingconsole)
|
||||
{
|
||||
LOCPLINT->cingconsole->print(commandMessage);
|
||||
}
|
||||
}
|
||||
}
|
31
client/ClientCommandManager.h
Normal file
31
client/ClientCommandManager.h
Normal file
@ -0,0 +1,31 @@
|
||||
/*
|
||||
* ClientCommandManager.h, part of VCMI engine
|
||||
*
|
||||
* Authors: listed in file AUTHORS in main folder
|
||||
*
|
||||
* License: GNU General Public License v2.0 or later
|
||||
* Full text of license available in license.txt file, in main folder
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
VCMI_LIB_NAMESPACE_BEGIN
|
||||
class PlayerColor;
|
||||
VCMI_LIB_NAMESPACE_END
|
||||
class CIntObject;
|
||||
|
||||
class ClientCommandManager //take mantis #2292 issue about account if thinking about handling cheats from command-line
|
||||
{
|
||||
bool currentCallFromIngameConsole;
|
||||
|
||||
void giveTurn(const PlayerColor &color);
|
||||
void printInfoAboutInterfaceObject(const CIntObject *obj, int level);
|
||||
void printCommandMessage(const std::string &commandMessage, ELogLevel::ELogLevel messageType = ELogLevel::NOT_SET);
|
||||
void handleGoSolo();
|
||||
void handleControlAi(const std::string &colorName);
|
||||
|
||||
public:
|
||||
ClientCommandManager() = default;
|
||||
void processCommand(const std::string &message, bool calledFromIngameConsole);
|
||||
};
|
@ -48,6 +48,7 @@
|
||||
#include "../../lib/mapping/CMap.h"
|
||||
#include "../../lib/NetPacksBase.h"
|
||||
#include "../../lib/StringConstants.h"
|
||||
#include "ClientCommandManager.h"
|
||||
|
||||
CList::CListItem::CListItem(CList * Parent)
|
||||
: CIntObject(LCLICK | RCLICK | HOVER),
|
||||
@ -1141,15 +1142,29 @@ void CInGameConsole::startEnteringText()
|
||||
GH.statusbar->setEnteredText(enteredText);
|
||||
}
|
||||
|
||||
void CInGameConsole::endEnteringText(bool printEnteredText)
|
||||
void CInGameConsole::endEnteringText(bool processEnteredText)
|
||||
{
|
||||
captureAllKeys = false;
|
||||
prevEntDisp = -1;
|
||||
if(printEnteredText)
|
||||
if(processEnteredText)
|
||||
{
|
||||
std::string txt = enteredText.substr(0, enteredText.size()-1);
|
||||
LOCPLINT->cb->sendMessage(txt, LOCPLINT->getSelection());
|
||||
previouslyEntered.push_back(txt);
|
||||
|
||||
if(txt.at(0) == '/')
|
||||
{
|
||||
//some commands like gosolo don't work when executed from GUI thread
|
||||
auto threadFunction = [=]()
|
||||
{
|
||||
ClientCommandManager commandController;
|
||||
commandController.processCommand(txt.substr(1), true);
|
||||
};
|
||||
|
||||
boost::thread clientCommandThread(threadFunction);
|
||||
clientCommandThread.detach();
|
||||
}
|
||||
else
|
||||
LOCPLINT->cb->sendMessage(txt, LOCPLINT->getSelection());
|
||||
}
|
||||
enteredText.clear();
|
||||
|
||||
|
@ -424,7 +424,7 @@ public:
|
||||
void textEdited(const SDL_TextEditingEvent & event) override;
|
||||
|
||||
void startEnteringText();
|
||||
void endEnteringText(bool printEnteredText);
|
||||
void endEnteringText(bool processEnteredText);
|
||||
void refreshEnteredText();
|
||||
|
||||
CInGameConsole();
|
||||
|
@ -230,7 +230,7 @@ int CConsoleHandler::run()
|
||||
{
|
||||
if ( getline(std::cin, buffer).good() )
|
||||
if ( cb && *cb )
|
||||
(*cb)(buffer);
|
||||
(*cb)(buffer, false);
|
||||
}
|
||||
else
|
||||
boost::this_thread::sleep(boost::posix_time::millisec(100));
|
||||
@ -239,7 +239,7 @@ int CConsoleHandler::run()
|
||||
#else
|
||||
std::getline(std::cin, buffer);
|
||||
if ( cb && *cb )
|
||||
(*cb)(buffer);
|
||||
(*cb)(buffer, false);
|
||||
#endif
|
||||
}
|
||||
return -1;
|
||||
@ -263,7 +263,7 @@ CConsoleHandler::CConsoleHandler() : thread(nullptr)
|
||||
#else
|
||||
defColor = "\x1b[0m";
|
||||
#endif
|
||||
cb = new std::function<void(const std::string &)>;
|
||||
cb = new std::function<void(const std::string &, bool)>;
|
||||
}
|
||||
CConsoleHandler::~CConsoleHandler()
|
||||
{
|
||||
|
@ -75,8 +75,8 @@ public:
|
||||
funlockfile(stdout);
|
||||
#endif
|
||||
}
|
||||
|
||||
std::function<void(const std::string &)> *cb; //function to be called when message is received
|
||||
//function to be called when message is received - string: message, bool: whether call was made from in-game console
|
||||
std::function<void(const std::string &, bool)> *cb;
|
||||
|
||||
private:
|
||||
int run();
|
||||
|
Loading…
x
Reference in New Issue
Block a user