mirror of
https://github.com/vcmi/vcmi.git
synced 2025-01-26 03:52:01 +02:00
Merge branch 'vcmi:develop' into refactoring-art
This commit is contained in:
commit
3bf0cfe4fa
@ -201,7 +201,14 @@ int main(int argc, char * argv[])
|
|||||||
("donotstartserver,d","do not attempt to start server and just connect to it instead server")
|
("donotstartserver,d","do not attempt to start server and just connect to it instead server")
|
||||||
("serverport", po::value<si64>(), "override port specified in config file")
|
("serverport", po::value<si64>(), "override port specified in config file")
|
||||||
("saveprefix", po::value<std::string>(), "prefix for auto save files")
|
("saveprefix", po::value<std::string>(), "prefix for auto save files")
|
||||||
("savefrequency", po::value<si64>(), "limit auto save creation to each N days");
|
("savefrequency", po::value<si64>(), "limit auto save creation to each N days")
|
||||||
|
("lobby", "parameters address, port, uuid to connect ro remote lobby session")
|
||||||
|
("lobby-address", po::value<std::string>(), "address to remote lobby")
|
||||||
|
("lobby-port", po::value<ui16>(), "port to remote lobby")
|
||||||
|
("lobby-host", "if this client hosts session")
|
||||||
|
("lobby-uuid", po::value<std::string>(), "uuid to the server")
|
||||||
|
("lobby-connections", po::value<ui16>(), "connections of server")
|
||||||
|
("uuid", po::value<std::string>(), "uuid for the client");
|
||||||
|
|
||||||
if(argc > 1)
|
if(argc > 1)
|
||||||
{
|
{
|
||||||
@ -483,6 +490,28 @@ int main(int argc, char * argv[])
|
|||||||
session["autoSkip"].Bool() = vm.count("autoSkip");
|
session["autoSkip"].Bool() = vm.count("autoSkip");
|
||||||
session["oneGoodAI"].Bool() = vm.count("oneGoodAI");
|
session["oneGoodAI"].Bool() = vm.count("oneGoodAI");
|
||||||
session["aiSolo"].Bool() = false;
|
session["aiSolo"].Bool() = false;
|
||||||
|
|
||||||
|
session["lobby"].Bool() = false;
|
||||||
|
if(vm.count("lobby"))
|
||||||
|
{
|
||||||
|
session["lobby"].Bool() = true;
|
||||||
|
session["host"].Bool() = false;
|
||||||
|
session["address"].String() = vm["lobby-address"].as<std::string>();
|
||||||
|
CSH->uuid = vm["uuid"].as<std::string>();
|
||||||
|
session["port"].Integer() = vm["lobby-port"].as<ui16>();
|
||||||
|
logGlobal->info("Remote lobby mode at %s:%d, uuid is %s", session["address"].String(), session["port"].Integer(), CSH->uuid);
|
||||||
|
if(vm.count("lobby-host"))
|
||||||
|
{
|
||||||
|
session["host"].Bool() = true;
|
||||||
|
session["hostConnections"].String() = std::to_string(vm["lobby-connections"].as<ui16>());
|
||||||
|
session["hostUuid"].String() = vm["lobby-uuid"].as<std::string>();
|
||||||
|
logGlobal->info("This client will host session, server uuid is %s", session["hostUuid"].String());
|
||||||
|
}
|
||||||
|
|
||||||
|
//we should not reconnect to previous game in online mode
|
||||||
|
Settings saveSession = settings.write["server"]["reconnect"];
|
||||||
|
saveSession->Bool() = false;
|
||||||
|
}
|
||||||
|
|
||||||
if(vm.count("testmap"))
|
if(vm.count("testmap"))
|
||||||
{
|
{
|
||||||
@ -638,7 +667,7 @@ void processCommand(const std::string &message)
|
|||||||
std::cout << "Command accepted.\t";
|
std::cout << "Command accepted.\t";
|
||||||
|
|
||||||
const bfs::path outPath =
|
const bfs::path outPath =
|
||||||
VCMIDirs::get().userCachePath() / "extracted";
|
VCMIDirs::get().userExtractedPath();
|
||||||
|
|
||||||
bfs::create_directories(outPath);
|
bfs::create_directories(outPath);
|
||||||
|
|
||||||
@ -671,7 +700,7 @@ void processCommand(const std::string &message)
|
|||||||
std::cout << "Command accepted.\t";
|
std::cout << "Command accepted.\t";
|
||||||
|
|
||||||
const bfs::path outPath =
|
const bfs::path outPath =
|
||||||
VCMIDirs::get().userCachePath() / "extracted" / "configuration";
|
VCMIDirs::get().userExtractedPath() / "configuration";
|
||||||
|
|
||||||
bfs::create_directories(outPath);
|
bfs::create_directories(outPath);
|
||||||
|
|
||||||
@ -712,7 +741,7 @@ void processCommand(const std::string &message)
|
|||||||
std::cout << "Command accepted.\t";
|
std::cout << "Command accepted.\t";
|
||||||
|
|
||||||
const bfs::path outPath =
|
const bfs::path outPath =
|
||||||
VCMIDirs::get().userCachePath() / "extracted" / "scripts";
|
VCMIDirs::get().userExtractedPath() / "scripts";
|
||||||
|
|
||||||
bfs::create_directories(outPath);
|
bfs::create_directories(outPath);
|
||||||
|
|
||||||
@ -735,7 +764,7 @@ void processCommand(const std::string &message)
|
|||||||
std::cout << "Command accepted.\t";
|
std::cout << "Command accepted.\t";
|
||||||
|
|
||||||
const bfs::path outPath =
|
const bfs::path outPath =
|
||||||
VCMIDirs::get().userCachePath() / "extracted";
|
VCMIDirs::get().userExtractedPath();
|
||||||
|
|
||||||
auto list = CResourceHandler::get()->getFilteredFiles([](const ResourceID & ident)
|
auto list = CResourceHandler::get()->getFilteredFiles([](const ResourceID & ident)
|
||||||
{
|
{
|
||||||
@ -851,7 +880,7 @@ void processCommand(const std::string &message)
|
|||||||
readed >> URI;
|
readed >> URI;
|
||||||
std::unique_ptr<CAnimation> anim = make_unique<CAnimation>(URI);
|
std::unique_ptr<CAnimation> anim = make_unique<CAnimation>(URI);
|
||||||
anim->preload();
|
anim->preload();
|
||||||
anim->exportBitmaps(VCMIDirs::get().userCachePath() / "extracted");
|
anim->exportBitmaps(VCMIDirs::get().userExtractedPath());
|
||||||
}
|
}
|
||||||
else if(cn == "extract")
|
else if(cn == "extract")
|
||||||
{
|
{
|
||||||
@ -860,7 +889,7 @@ void processCommand(const std::string &message)
|
|||||||
|
|
||||||
if (CResourceHandler::get()->existsResource(ResourceID(URI)))
|
if (CResourceHandler::get()->existsResource(ResourceID(URI)))
|
||||||
{
|
{
|
||||||
const bfs::path outPath = VCMIDirs::get().userCachePath() / "extracted" / URI;
|
const bfs::path outPath = VCMIDirs::get().userExtractedPath() / URI;
|
||||||
|
|
||||||
auto data = CResourceHandler::get()->load(ResourceID(URI))->readAll();
|
auto data = CResourceHandler::get()->load(ResourceID(URI))->readAll();
|
||||||
|
|
||||||
|
@ -118,6 +118,9 @@ CServerHandler::CServerHandler()
|
|||||||
: state(EClientState::NONE), mx(std::make_shared<boost::recursive_mutex>()), client(nullptr), loadMode(0), campaignStateToSend(nullptr), campaignServerRestartLock(false)
|
: state(EClientState::NONE), mx(std::make_shared<boost::recursive_mutex>()), client(nullptr), loadMode(0), campaignStateToSend(nullptr), campaignServerRestartLock(false)
|
||||||
{
|
{
|
||||||
uuid = boost::uuids::to_string(boost::uuids::random_generator()());
|
uuid = boost::uuids::to_string(boost::uuids::random_generator()());
|
||||||
|
//read from file to restore last session
|
||||||
|
if(!settings["server"]["uuid"].isNull() && !settings["server"]["uuid"].String().empty())
|
||||||
|
uuid = settings["server"]["uuid"].String();
|
||||||
applier = std::make_shared<CApplier<CBaseForLobbyApply>>();
|
applier = std::make_shared<CApplier<CBaseForLobbyApply>>();
|
||||||
registerTypesLobbyPacks(*applier);
|
registerTypesLobbyPacks(*applier);
|
||||||
}
|
}
|
||||||
@ -190,9 +193,17 @@ void CServerHandler::startLocalServerAndConnect()
|
|||||||
}
|
}
|
||||||
#elif defined(SINGLE_PROCESS_APP)
|
#elif defined(SINGLE_PROCESS_APP)
|
||||||
boost::condition_variable cond;
|
boost::condition_variable cond;
|
||||||
threadRunLocalServer = std::make_shared<boost::thread>([&cond, this] {
|
std::vector<std::string> args{"--uuid=" + uuid, "--port=" + boost::lexical_cast<std::string>(getHostPort())};
|
||||||
|
if(settings["session"]["lobby"].Bool() && settings["session"]["host"].Bool())
|
||||||
|
{
|
||||||
|
args.push_back("--lobby=" + settings["session"]["address"].String());
|
||||||
|
args.push_back("--connections=" + settings["session"]["hostConnections"].String());
|
||||||
|
args.push_back("--lobby-port=" + boost::lexical_cast<std::string>(settings["session"]["port"].Integer()));
|
||||||
|
args.push_back("--lobby-uuid=" + settings["session"]["hostUuid"].String());
|
||||||
|
}
|
||||||
|
threadRunLocalServer = std::make_shared<boost::thread>([&cond, args, this] {
|
||||||
setThreadName("CVCMIServer");
|
setThreadName("CVCMIServer");
|
||||||
CVCMIServer::create(&cond, uuid);
|
CVCMIServer::create(&cond, args);
|
||||||
onServerFinished();
|
onServerFinished();
|
||||||
});
|
});
|
||||||
threadRunLocalServer->detach();
|
threadRunLocalServer->detach();
|
||||||
@ -259,8 +270,8 @@ void CServerHandler::justConnectToServer(const std::string & addr, const ui16 po
|
|||||||
{
|
{
|
||||||
logNetwork->info("Establishing connection...");
|
logNetwork->info("Establishing connection...");
|
||||||
c = std::make_shared<CConnection>(
|
c = std::make_shared<CConnection>(
|
||||||
addr.size() ? addr : settings["server"]["server"].String(),
|
addr.size() ? addr : getHostAddress(),
|
||||||
port ? port : getDefaultPort(),
|
port ? port : getHostPort(),
|
||||||
NAME, uuid);
|
NAME, uuid);
|
||||||
}
|
}
|
||||||
catch(...)
|
catch(...)
|
||||||
@ -278,12 +289,12 @@ void CServerHandler::justConnectToServer(const std::string & addr, const ui16 po
|
|||||||
|
|
||||||
c->handler = std::make_shared<boost::thread>(&CServerHandler::threadHandleConnection, this);
|
c->handler = std::make_shared<boost::thread>(&CServerHandler::threadHandleConnection, this);
|
||||||
|
|
||||||
if(!addr.empty() && addr != localhostAddress)
|
if(!addr.empty() && addr != getHostAddress())
|
||||||
{
|
{
|
||||||
Settings serverAddress = settings.write["server"]["server"];
|
Settings serverAddress = settings.write["server"]["server"];
|
||||||
serverAddress->String() = addr;
|
serverAddress->String() = addr;
|
||||||
}
|
}
|
||||||
if(port && port != getDefaultPort())
|
if(port && port != getHostPort())
|
||||||
{
|
{
|
||||||
Settings serverPort = settings.write["server"]["port"];
|
Settings serverPort = settings.write["server"]["port"];
|
||||||
serverPort->Integer() = port;
|
serverPort->Integer() = port;
|
||||||
@ -358,10 +369,7 @@ bool CServerHandler::isGuest() const
|
|||||||
|
|
||||||
ui16 CServerHandler::getDefaultPort()
|
ui16 CServerHandler::getDefaultPort()
|
||||||
{
|
{
|
||||||
if(settings["session"]["serverport"].Integer())
|
return static_cast<ui16>(settings["server"]["port"].Integer());
|
||||||
return static_cast<ui16>(settings["session"]["serverport"].Integer());
|
|
||||||
else
|
|
||||||
return static_cast<ui16>(settings["server"]["port"].Integer());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string CServerHandler::getDefaultPortStr()
|
std::string CServerHandler::getDefaultPortStr()
|
||||||
@ -369,6 +377,28 @@ std::string CServerHandler::getDefaultPortStr()
|
|||||||
return boost::lexical_cast<std::string>(getDefaultPort());
|
return boost::lexical_cast<std::string>(getDefaultPort());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string CServerHandler::getHostAddress() const
|
||||||
|
{
|
||||||
|
if(settings["session"]["lobby"].isNull() || !settings["session"]["lobby"].Bool())
|
||||||
|
return settings["server"]["server"].String();
|
||||||
|
|
||||||
|
if(settings["session"]["host"].Bool())
|
||||||
|
return localhostAddress;
|
||||||
|
|
||||||
|
return settings["session"]["address"].String();
|
||||||
|
}
|
||||||
|
|
||||||
|
ui16 CServerHandler::getHostPort() const
|
||||||
|
{
|
||||||
|
if(settings["session"]["lobby"].isNull() || !settings["session"]["lobby"].Bool())
|
||||||
|
return getDefaultPort();
|
||||||
|
|
||||||
|
if(settings["session"]["host"].Bool())
|
||||||
|
return getDefaultPort();
|
||||||
|
|
||||||
|
return settings["session"]["port"].Integer();
|
||||||
|
}
|
||||||
|
|
||||||
void CServerHandler::sendClientConnecting() const
|
void CServerHandler::sendClientConnecting() const
|
||||||
{
|
{
|
||||||
LobbyClientConnected lcc;
|
LobbyClientConnected lcc;
|
||||||
@ -813,9 +843,17 @@ void CServerHandler::threadRunServer()
|
|||||||
setThreadName("CServerHandler::threadRunServer");
|
setThreadName("CServerHandler::threadRunServer");
|
||||||
const std::string logName = (VCMIDirs::get().userLogsPath() / "server_log.txt").string();
|
const std::string logName = (VCMIDirs::get().userLogsPath() / "server_log.txt").string();
|
||||||
std::string comm = VCMIDirs::get().serverPath().string()
|
std::string comm = VCMIDirs::get().serverPath().string()
|
||||||
+ " --port=" + getDefaultPortStr()
|
+ " --port=" + boost::lexical_cast<std::string>(getHostPort())
|
||||||
+ " --run-by-client"
|
+ " --run-by-client"
|
||||||
+ " --uuid=" + uuid;
|
+ " --uuid=" + uuid;
|
||||||
|
if(settings["session"]["lobby"].Bool() && settings["session"]["host"].Bool())
|
||||||
|
{
|
||||||
|
comm += " --lobby=" + settings["session"]["address"].String();
|
||||||
|
comm += " --connections=" + settings["session"]["hostConnections"].String();
|
||||||
|
comm += " --lobby-port=" + boost::lexical_cast<std::string>(settings["session"]["port"].Integer());
|
||||||
|
comm += " --lobby-uuid=" + settings["session"]["hostUuid"].String();
|
||||||
|
}
|
||||||
|
|
||||||
if(shm)
|
if(shm)
|
||||||
{
|
{
|
||||||
comm += " --enable-shm";
|
comm += " --enable-shm";
|
||||||
@ -823,6 +861,7 @@ void CServerHandler::threadRunServer()
|
|||||||
comm += " --enable-shm-uuid";
|
comm += " --enable-shm-uuid";
|
||||||
}
|
}
|
||||||
comm += " > \"" + logName + '\"';
|
comm += " > \"" + logName + '\"';
|
||||||
|
logGlobal->info("Server command line: %s", comm);
|
||||||
|
|
||||||
#ifdef VCMI_WINDOWS
|
#ifdef VCMI_WINDOWS
|
||||||
int result = -1;
|
int result = -1;
|
||||||
|
@ -111,6 +111,9 @@ public:
|
|||||||
static const std::string localhostAddress;
|
static const std::string localhostAddress;
|
||||||
|
|
||||||
CServerHandler();
|
CServerHandler();
|
||||||
|
|
||||||
|
std::string getHostAddress() const;
|
||||||
|
ui16 getHostPort() const;
|
||||||
|
|
||||||
void resetStateForLobby(const StartInfo::EMode mode, const std::vector<std::string> * names = nullptr);
|
void resetStateForLobby(const StartInfo::EMode mode, const std::vector<std::string> * names = nullptr);
|
||||||
void startLocalServerAndConnect();
|
void startLocalServerAndConnect();
|
||||||
|
@ -50,7 +50,13 @@ int client_main(int argc, char * argv[])
|
|||||||
id __block startGameObserver = [NSNotificationCenter.defaultCenter addObserverForName:@"StartGame" object:nil queue:nil usingBlock:^(NSNotification * _Nonnull note) {
|
id __block startGameObserver = [NSNotificationCenter.defaultCenter addObserverForName:@"StartGame" object:nil queue:nil usingBlock:^(NSNotification * _Nonnull note) {
|
||||||
[NSNotificationCenter.defaultCenter removeObserver:startGameObserver];
|
[NSNotificationCenter.defaultCenter removeObserver:startGameObserver];
|
||||||
startGameObserver = nil;
|
startGameObserver = nil;
|
||||||
startSDLManually(argc, argv);
|
|
||||||
|
NSArray<NSString *> * args = note.userInfo[@"args"];
|
||||||
|
const char * newArgv[args.count];
|
||||||
|
NSUInteger i = 0;
|
||||||
|
for (NSString * obj in args)
|
||||||
|
newArgv[i++] = obj.UTF8String;
|
||||||
|
startSDLManually(args.count, (char **)(newArgv));
|
||||||
}];
|
}];
|
||||||
return qt_main_wrapper(argc, argv);
|
return qt_main_wrapper(argc, argv);
|
||||||
}
|
}
|
||||||
|
@ -481,8 +481,8 @@ CSimpleJoinScreen::CSimpleJoinScreen(bool host)
|
|||||||
|
|
||||||
inputAddress->giveFocus();
|
inputAddress->giveFocus();
|
||||||
}
|
}
|
||||||
inputAddress->setText(host ? CServerHandler::localhostAddress : settings["server"]["server"].String(), true);
|
inputAddress->setText(host ? CServerHandler::localhostAddress : CSH->getHostAddress(), true);
|
||||||
inputPort->setText(CServerHandler::getDefaultPortStr(), true);
|
inputPort->setText(boost::lexical_cast<std::string>(CSH->getHostPort()), true);
|
||||||
|
|
||||||
buttonCancel = std::make_shared<CButton>(Point(142, 142), "MUBCANC.DEF", CGI->generaltexth->zelp[561], std::bind(&CSimpleJoinScreen::leaveScreen, this), SDLK_ESCAPE);
|
buttonCancel = std::make_shared<CButton>(Point(142, 142), "MUBCANC.DEF", CGI->generaltexth->zelp[561], std::bind(&CSimpleJoinScreen::leaveScreen, this), SDLK_ESCAPE);
|
||||||
statusBar = CGStatusBar::create(std::make_shared<CPicture>(Rect(7, 186, 218, 18), 0));
|
statusBar = CGStatusBar::create(std::make_shared<CPicture>(Rect(7, 186, 218, 18), 0));
|
||||||
|
@ -1038,12 +1038,10 @@ void CInGameConsole::keyPressed (const SDL_KeyboardEvent & key)
|
|||||||
{
|
{
|
||||||
if(captureAllKeys)
|
if(captureAllKeys)
|
||||||
{
|
{
|
||||||
captureAllKeys = false;
|
|
||||||
endEnteringText(false);
|
endEnteringText(false);
|
||||||
}
|
}
|
||||||
else if(SDLK_TAB == key.keysym.sym)
|
else if(SDLK_TAB == key.keysym.sym)
|
||||||
{
|
{
|
||||||
captureAllKeys = true;
|
|
||||||
startEnteringText();
|
startEnteringText();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@ -1052,7 +1050,6 @@ void CInGameConsole::keyPressed (const SDL_KeyboardEvent & key)
|
|||||||
{
|
{
|
||||||
if(enteredText.size() > 0 && captureAllKeys)
|
if(enteredText.size() > 0 && captureAllKeys)
|
||||||
{
|
{
|
||||||
captureAllKeys = false;
|
|
||||||
endEnteringText(true);
|
endEnteringText(true);
|
||||||
CCS->soundh->playSound("CHAT");
|
CCS->soundh->playSound("CHAT");
|
||||||
}
|
}
|
||||||
@ -1129,6 +1126,8 @@ void CInGameConsole::textEdited(const SDL_TextEditingEvent & event)
|
|||||||
|
|
||||||
void CInGameConsole::startEnteringText()
|
void CInGameConsole::startEnteringText()
|
||||||
{
|
{
|
||||||
|
captureAllKeys = true;
|
||||||
|
|
||||||
CSDL_Ext::startTextInput(&GH.statusbar->pos);
|
CSDL_Ext::startTextInput(&GH.statusbar->pos);
|
||||||
|
|
||||||
enteredText = "_";
|
enteredText = "_";
|
||||||
@ -1148,6 +1147,8 @@ void CInGameConsole::startEnteringText()
|
|||||||
|
|
||||||
void CInGameConsole::endEnteringText(bool printEnteredText)
|
void CInGameConsole::endEnteringText(bool printEnteredText)
|
||||||
{
|
{
|
||||||
|
captureAllKeys = false;
|
||||||
|
|
||||||
CSDL_Ext::stopTextInput();
|
CSDL_Ext::stopTextInput();
|
||||||
|
|
||||||
prevEntDisp = -1;
|
prevEntDisp = -1;
|
||||||
|
@ -388,6 +388,20 @@ void CGStatusBar::init()
|
|||||||
GH.statusbar = shared_from_this();
|
GH.statusbar = shared_from_this();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CGStatusBar::clickLeft(tribool down, bool previousState)
|
||||||
|
{
|
||||||
|
if(!down && onClick)
|
||||||
|
{
|
||||||
|
onClick();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CGStatusBar::setOnClick(std::function<void()> handler)
|
||||||
|
{
|
||||||
|
onClick = handler;
|
||||||
|
addUsedEvents(LCLICK);
|
||||||
|
}
|
||||||
|
|
||||||
Point CGStatusBar::getBorderSize()
|
Point CGStatusBar::getBorderSize()
|
||||||
{
|
{
|
||||||
//Width of borders where text should not be printed
|
//Width of borders where text should not be printed
|
||||||
|
@ -124,6 +124,11 @@ class CGStatusBar : public CLabel, public std::enable_shared_from_this<CGStatusB
|
|||||||
protected:
|
protected:
|
||||||
Point getBorderSize() override;
|
Point getBorderSize() override;
|
||||||
|
|
||||||
|
void clickLeft(tribool down, bool previousState) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::function<void()> onClick;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
template<typename ...Args>
|
template<typename ...Args>
|
||||||
static std::shared_ptr<CGStatusBar> create(Args... args)
|
static std::shared_ptr<CGStatusBar> create(Args... args)
|
||||||
@ -138,6 +143,8 @@ public:
|
|||||||
void show(SDL_Surface * to) override; //shows statusbar (with current text)
|
void show(SDL_Surface * to) override; //shows statusbar (with current text)
|
||||||
|
|
||||||
void lock(bool shouldLock); //If true, current text cannot be changed until lock(false) is called
|
void lock(bool shouldLock); //If true, current text cannot be changed until lock(false) is called
|
||||||
|
|
||||||
|
void setOnClick(std::function<void()> handler);
|
||||||
};
|
};
|
||||||
|
|
||||||
class CFocusable;
|
class CFocusable;
|
||||||
|
@ -716,6 +716,11 @@ CAdvMapInt::CAdvMapInt():
|
|||||||
worldViewUnderground->block(!CGI->mh->map->twoLevel);
|
worldViewUnderground->block(!CGI->mh->map->twoLevel);
|
||||||
|
|
||||||
addUsedEvents(MOVE);
|
addUsedEvents(MOVE);
|
||||||
|
|
||||||
|
statusbar->setOnClick([&]
|
||||||
|
{
|
||||||
|
if(LOCPLINT) LOCPLINT->cingconsole->startEnteringText();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
CAdvMapInt::~CAdvMapInt()
|
CAdvMapInt::~CAdvMapInt()
|
||||||
@ -949,6 +954,7 @@ void CAdvMapInt::activate()
|
|||||||
}
|
}
|
||||||
minimap.activate();
|
minimap.activate();
|
||||||
terrain.activate();
|
terrain.activate();
|
||||||
|
statusbar->activate();
|
||||||
|
|
||||||
GH.fakeMouseMove(); //to restore the cursor
|
GH.fakeMouseMove(); //to restore the cursor
|
||||||
}
|
}
|
||||||
|
@ -293,7 +293,7 @@
|
|||||||
},
|
},
|
||||||
"names" : {
|
"names" : {
|
||||||
"type" : "array",
|
"type" : "array",
|
||||||
"default" : {},
|
"default" : [],
|
||||||
"items":
|
"items":
|
||||||
{
|
{
|
||||||
"type" : "string",
|
"type" : "string",
|
||||||
@ -380,7 +380,7 @@
|
|||||||
"type" : "object",
|
"type" : "object",
|
||||||
"default": {},
|
"default": {},
|
||||||
"additionalProperties" : false,
|
"additionalProperties" : false,
|
||||||
"required" : [ "repositoryURL", "enableInstalledMods", "extraResolutionsModPath", "autoCheckRepositories", "updateOnStartup", "updateConfigUrl" ],
|
"required" : [ "repositoryURL", "enableInstalledMods", "extraResolutionsModPath", "autoCheckRepositories", "updateOnStartup", "updateConfigUrl", "lobbyUrl", "lobbyPort", "lobbyUsername", "connectionTimeout" ],
|
||||||
"properties" : {
|
"properties" : {
|
||||||
"repositoryURL" : {
|
"repositoryURL" : {
|
||||||
"type" : "array",
|
"type" : "array",
|
||||||
@ -410,6 +410,26 @@
|
|||||||
"updateConfigUrl" : {
|
"updateConfigUrl" : {
|
||||||
"type" : "string",
|
"type" : "string",
|
||||||
"default" : "https://raw.githubusercontent.com/vcmi/vcmi-updates/master/vcmi-updates.json"
|
"default" : "https://raw.githubusercontent.com/vcmi/vcmi-updates/master/vcmi-updates.json"
|
||||||
|
},
|
||||||
|
"lobbyUrl" : {
|
||||||
|
"type" : "string",
|
||||||
|
"description" : "ip address or web link to remote proxy server",
|
||||||
|
"default" : "beholder.vcmi.eu"
|
||||||
|
},
|
||||||
|
"lobbyPort" : {
|
||||||
|
"type" : "number",
|
||||||
|
"description" : "connection port for remote proxy server",
|
||||||
|
"default" : 5002
|
||||||
|
},
|
||||||
|
"lobbyUsername" : {
|
||||||
|
"type" : "string",
|
||||||
|
"description" : "username for the client on the remote proxy server",
|
||||||
|
"default" : ""
|
||||||
|
},
|
||||||
|
"connectionTimeout" : {
|
||||||
|
"type" : "number",
|
||||||
|
"description" : "maximum time in ms, should be enough to establish socket connection to remote proxy server.",
|
||||||
|
"default" : 2000
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -33,6 +33,9 @@ set(launcher_SRCS
|
|||||||
launcherdirs.cpp
|
launcherdirs.cpp
|
||||||
jsonutils.cpp
|
jsonutils.cpp
|
||||||
updatedialog_moc.cpp
|
updatedialog_moc.cpp
|
||||||
|
lobby/lobby.cpp
|
||||||
|
lobby/lobby_moc.cpp
|
||||||
|
lobby/lobbyroomrequest_moc.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
set(launcher_HEADERS
|
set(launcher_HEADERS
|
||||||
@ -43,6 +46,9 @@ set(launcher_HEADERS
|
|||||||
launcherdirs.h
|
launcherdirs.h
|
||||||
jsonutils.h
|
jsonutils.h
|
||||||
updatedialog_moc.h
|
updatedialog_moc.h
|
||||||
|
lobby/lobby.h
|
||||||
|
lobby/lobby_moc.h
|
||||||
|
lobby/lobbyroomrequest_moc.h
|
||||||
main.h
|
main.h
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -52,6 +58,8 @@ set(launcher_FORMS
|
|||||||
settingsView/csettingsview_moc.ui
|
settingsView/csettingsview_moc.ui
|
||||||
mainwindow_moc.ui
|
mainwindow_moc.ui
|
||||||
updatedialog_moc.ui
|
updatedialog_moc.ui
|
||||||
|
lobby/lobby_moc.ui
|
||||||
|
lobby/lobbyroomrequest_moc.ui
|
||||||
)
|
)
|
||||||
|
|
||||||
if(APPLE_IOS)
|
if(APPLE_IOS)
|
||||||
|
BIN
launcher/icons/menu-lobby.png
Normal file
BIN
launcher/icons/menu-lobby.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 7.2 KiB |
BIN
launcher/icons/room-private.png
Normal file
BIN
launcher/icons/room-private.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 683 B |
@ -26,5 +26,9 @@ void launchGame(int argc, char * argv[]) {
|
|||||||
qtNativeWindow.windowScene = nil;
|
qtNativeWindow.windowScene = nil;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
[NSNotificationCenter.defaultCenter postNotificationName:@"StartGame" object:nil];
|
|
||||||
}
|
__auto_type args = [NSMutableArray arrayWithCapacity:argc];
|
||||||
|
for (int i = 0; i < argc; ++i)
|
||||||
|
[args addObject:@(argv[i])];
|
||||||
|
[NSNotificationCenter.defaultCenter postNotificationName:@"StartGame" object:nil userInfo:@{@"args": args}];
|
||||||
|
}
|
123
launcher/lobby/lobby.cpp
Normal file
123
launcher/lobby/lobby.cpp
Normal file
@ -0,0 +1,123 @@
|
|||||||
|
/*
|
||||||
|
* lobby.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 "lobby.h"
|
||||||
|
#include "../lib/GameConstants.h"
|
||||||
|
|
||||||
|
SocketLobby::SocketLobby(QObject *parent) :
|
||||||
|
QObject(parent)
|
||||||
|
{
|
||||||
|
socket = new QTcpSocket(this);
|
||||||
|
connect(socket, SIGNAL(connected()), this, SLOT(connected()));
|
||||||
|
connect(socket, SIGNAL(disconnected()), this, SLOT(disconnected()));
|
||||||
|
connect(socket, SIGNAL(readyRead()), this, SLOT(readyRead()));
|
||||||
|
connect(socket, SIGNAL(bytesWritten(qint64)), this, SLOT(bytesWritten(qint64)));
|
||||||
|
}
|
||||||
|
|
||||||
|
void SocketLobby::connectServer(const QString & host, int port, const QString & usr, int timeout)
|
||||||
|
{
|
||||||
|
username = usr;
|
||||||
|
|
||||||
|
socket->connectToHost(host, port);
|
||||||
|
|
||||||
|
if(!socket->waitForDisconnected(timeout) && !isConnected)
|
||||||
|
{
|
||||||
|
emit text("Error: " + socket->errorString());
|
||||||
|
emit disconnect();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SocketLobby::disconnectServer()
|
||||||
|
{
|
||||||
|
socket->disconnectFromHost();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SocketLobby::requestNewSession(const QString & session, int totalPlayers, const QString & pswd, const QMap<QString, QString> & mods)
|
||||||
|
{
|
||||||
|
const QString sessionMessage = ProtocolStrings[CREATE].arg(session, pswd, QString::number(totalPlayers), prepareModsClientString(mods));
|
||||||
|
send(sessionMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SocketLobby::requestJoinSession(const QString & session, const QString & pswd, const QMap<QString, QString> & mods)
|
||||||
|
{
|
||||||
|
const QString sessionMessage = ProtocolStrings[JOIN].arg(session, pswd, prepareModsClientString(mods));
|
||||||
|
send(sessionMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SocketLobby::requestLeaveSession(const QString & session)
|
||||||
|
{
|
||||||
|
const QString sessionMessage = ProtocolStrings[LEAVE].arg(session);
|
||||||
|
send(sessionMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SocketLobby::requestReadySession(const QString & session)
|
||||||
|
{
|
||||||
|
const QString sessionMessage = ProtocolStrings[READY].arg(session);
|
||||||
|
send(sessionMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SocketLobby::send(const QString & msg)
|
||||||
|
{
|
||||||
|
QByteArray str = msg.toUtf8();
|
||||||
|
int sz = str.size();
|
||||||
|
QByteArray pack((const char *)&sz, sizeof(sz));
|
||||||
|
pack.append(str);
|
||||||
|
socket->write(pack);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SocketLobby::connected()
|
||||||
|
{
|
||||||
|
isConnected = true;
|
||||||
|
emit text("Connected!");
|
||||||
|
|
||||||
|
QByteArray greetingBytes;
|
||||||
|
greetingBytes.append(ProtocolVersion);
|
||||||
|
greetingBytes.append(ProtocolEncoding.size());
|
||||||
|
const QString greetingConst = QString(greetingBytes)
|
||||||
|
+ ProtocolStrings[GREETING].arg(QString::fromStdString(ProtocolEncoding),
|
||||||
|
username,
|
||||||
|
QString::fromStdString(GameConstants::VCMI_VERSION));
|
||||||
|
send(greetingConst);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SocketLobby::disconnected()
|
||||||
|
{
|
||||||
|
isConnected = false;
|
||||||
|
emit disconnect();
|
||||||
|
emit text("Disconnected!");
|
||||||
|
}
|
||||||
|
|
||||||
|
void SocketLobby::bytesWritten(qint64 bytes)
|
||||||
|
{
|
||||||
|
qDebug() << "We wrote: " << bytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SocketLobby::readyRead()
|
||||||
|
{
|
||||||
|
qDebug() << "Reading...";
|
||||||
|
emit receive(socket->readAll());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
ServerCommand::ServerCommand(ProtocolConsts cmd, const QStringList & args):
|
||||||
|
command(cmd),
|
||||||
|
arguments(args)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
QString prepareModsClientString(const QMap<QString, QString> & mods)
|
||||||
|
{
|
||||||
|
QStringList result;
|
||||||
|
for(auto & mod : mods.keys())
|
||||||
|
{
|
||||||
|
result << mod + "&" + mods[mod];
|
||||||
|
}
|
||||||
|
return result.join(";");
|
||||||
|
}
|
188
launcher/lobby/lobby.h
Normal file
188
launcher/lobby/lobby.h
Normal file
@ -0,0 +1,188 @@
|
|||||||
|
/*
|
||||||
|
* lobby.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
|
||||||
|
|
||||||
|
#include <QTcpSocket>
|
||||||
|
#include <QAbstractSocket>
|
||||||
|
|
||||||
|
const unsigned int ProtocolVersion = 3;
|
||||||
|
const std::string ProtocolEncoding = "utf8";
|
||||||
|
|
||||||
|
class ProtocolError: public std::runtime_error
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ProtocolError(const char * w): std::runtime_error(w) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
enum ProtocolConsts
|
||||||
|
{
|
||||||
|
//client consts
|
||||||
|
GREETING, USERNAME, MESSAGE, VERSION, CREATE, JOIN, LEAVE, KICK, READY, FORCESTART,
|
||||||
|
|
||||||
|
//server consts
|
||||||
|
SESSIONS, CREATED, JOINED, KICKED, SRVERROR, CHAT, START, STATUS, HOST, MODS, CLIENTMODS
|
||||||
|
};
|
||||||
|
|
||||||
|
const QMap<ProtocolConsts, QString> ProtocolStrings
|
||||||
|
{
|
||||||
|
//=== client commands ===
|
||||||
|
|
||||||
|
//handshaking with server
|
||||||
|
//%1: first byte is protocol_version, then size of encoding string in bytes, then encoding string
|
||||||
|
//%2: client name
|
||||||
|
//%3: VCMI version
|
||||||
|
{GREETING, "%1<GREETINGS>%2<VER>%3"},
|
||||||
|
|
||||||
|
//[unsupported] autorization with username
|
||||||
|
//%1: username
|
||||||
|
{USERNAME, "<USER>%1"},
|
||||||
|
|
||||||
|
//sending message to the chat
|
||||||
|
//%1: message text
|
||||||
|
{MESSAGE, "<MSG>%1"},
|
||||||
|
|
||||||
|
//create new room
|
||||||
|
//%1: room name
|
||||||
|
//%2: password for the room
|
||||||
|
//%3: max number of players
|
||||||
|
//%4: mods used by host
|
||||||
|
// each mod has a format modname&modversion, mods should be separated by ; symbol
|
||||||
|
{CREATE, "<NEW>%1<PSWD>%2<COUNT>%3<MODS>%4"},
|
||||||
|
|
||||||
|
//join to the room
|
||||||
|
//%1: room name
|
||||||
|
//%2: password for the room
|
||||||
|
//%3: list of mods used by player
|
||||||
|
// each mod has a format modname&modversion, mods should be separated by ; symbol
|
||||||
|
{JOIN, "<JOIN>%1<PSWD>%2<MODS>%3"},
|
||||||
|
|
||||||
|
//leave the room
|
||||||
|
//%1: room name
|
||||||
|
{LEAVE, "<LEAVE>%1"},
|
||||||
|
|
||||||
|
//kick user from the current room
|
||||||
|
//%1: player username
|
||||||
|
{KICK, "<KICK>%1"},
|
||||||
|
|
||||||
|
//signal that player is ready for game
|
||||||
|
//%1: room name
|
||||||
|
{READY, "<READY>%1"},
|
||||||
|
|
||||||
|
//[unsupported] start session immediately
|
||||||
|
//%1: room name
|
||||||
|
{FORCESTART, "<FORCESTART>%1"},
|
||||||
|
|
||||||
|
//=== server commands ===
|
||||||
|
//server commands are started from :>>, arguments are enumerated by : symbol
|
||||||
|
|
||||||
|
//new session was created
|
||||||
|
//arg[0]: room name
|
||||||
|
{CREATED, "CREATED"},
|
||||||
|
|
||||||
|
//list of existing sessions
|
||||||
|
//arg[0]: amount of sessions, following arguments depending on it
|
||||||
|
//arg[x]: session name
|
||||||
|
//arg[x+1]: amount of players in the session
|
||||||
|
//arg[x+2]: total amount of players allowed
|
||||||
|
//arg[x+3]: True if session is protected by password
|
||||||
|
{SESSIONS, "SESSIONS"},
|
||||||
|
|
||||||
|
//user has joined to the session
|
||||||
|
//arg[0]: session name
|
||||||
|
//arg[1]: username (who was joined)
|
||||||
|
{JOINED, "JOIN"},
|
||||||
|
|
||||||
|
//user has left the session
|
||||||
|
//arg[0]: session name
|
||||||
|
//arg[1]: username (who has left)
|
||||||
|
{KICKED, "KICK"},
|
||||||
|
|
||||||
|
//session has been started
|
||||||
|
//arg[0]: session name
|
||||||
|
//arg[1]: uuid to be used for connection
|
||||||
|
{START, "START"},
|
||||||
|
|
||||||
|
//host ownership for the game session
|
||||||
|
//arg[0]: uuid to be used by vcmiserver
|
||||||
|
//arg[1]: amount of players (clients) to be connected
|
||||||
|
{HOST, "HOST"},
|
||||||
|
|
||||||
|
//room status
|
||||||
|
//arg[0]: amount of players, following arguments depending on it
|
||||||
|
//arg[x]: player username
|
||||||
|
//arg[x+1]: True if player is ready
|
||||||
|
{STATUS, "STATUS"}, //joined_players:player_name:is_ready
|
||||||
|
|
||||||
|
//server error
|
||||||
|
//arg[0]: error message
|
||||||
|
{SRVERROR, "ERROR"},
|
||||||
|
|
||||||
|
//mods used in the session by host player
|
||||||
|
//arg[0]: amount of mods, following arguments depending on it
|
||||||
|
//arg[x]: mod name
|
||||||
|
//arg[x+1]: mod version
|
||||||
|
{MODS, "MODS"},
|
||||||
|
|
||||||
|
//mods used by user
|
||||||
|
//arg[0]: username
|
||||||
|
//arg[1]: amount of mods, following arguments depending on it
|
||||||
|
//arg[x]: mod name
|
||||||
|
//arg[x+1]: mod version
|
||||||
|
{CLIENTMODS, "MODSOTHER"},
|
||||||
|
|
||||||
|
//received chat message
|
||||||
|
//arg[0]: sender username
|
||||||
|
//arg[1]: message text
|
||||||
|
{CHAT, "MSG"}
|
||||||
|
};
|
||||||
|
|
||||||
|
class ServerCommand
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ServerCommand(ProtocolConsts, const QStringList & arguments);
|
||||||
|
|
||||||
|
const ProtocolConsts command;
|
||||||
|
const QStringList arguments;
|
||||||
|
};
|
||||||
|
|
||||||
|
class SocketLobby : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
explicit SocketLobby(QObject *parent = 0);
|
||||||
|
void connectServer(const QString & host, int port, const QString & username, int timeout);
|
||||||
|
void disconnectServer();
|
||||||
|
void requestNewSession(const QString & session, int totalPlayers, const QString & pswd, const QMap<QString, QString> & mods);
|
||||||
|
void requestJoinSession(const QString & session, const QString & pswd, const QMap<QString, QString> & mods);
|
||||||
|
void requestLeaveSession(const QString & session);
|
||||||
|
void requestReadySession(const QString & session);
|
||||||
|
|
||||||
|
void send(const QString &);
|
||||||
|
|
||||||
|
signals:
|
||||||
|
|
||||||
|
void text(QString);
|
||||||
|
void receive(QString);
|
||||||
|
void disconnect();
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
|
||||||
|
void connected();
|
||||||
|
void disconnected();
|
||||||
|
void bytesWritten(qint64 bytes);
|
||||||
|
void readyRead();
|
||||||
|
|
||||||
|
private:
|
||||||
|
QTcpSocket *socket;
|
||||||
|
bool isConnected = false;
|
||||||
|
QString username;
|
||||||
|
};
|
||||||
|
|
||||||
|
QString prepareModsClientString(const QMap<QString, QString> & mods);
|
417
launcher/lobby/lobby_moc.cpp
Normal file
417
launcher/lobby/lobby_moc.cpp
Normal file
@ -0,0 +1,417 @@
|
|||||||
|
/*
|
||||||
|
* lobby_moc.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 "main.h"
|
||||||
|
#include "lobby_moc.h"
|
||||||
|
#include "ui_lobby_moc.h"
|
||||||
|
#include "lobbyroomrequest_moc.h"
|
||||||
|
#include "../mainwindow_moc.h"
|
||||||
|
#include "../modManager/cmodlist.h"
|
||||||
|
#include "../../lib/CConfigHandler.h"
|
||||||
|
|
||||||
|
Lobby::Lobby(QWidget *parent) :
|
||||||
|
QWidget(parent),
|
||||||
|
ui(new Ui::Lobby)
|
||||||
|
{
|
||||||
|
ui->setupUi(this);
|
||||||
|
|
||||||
|
connect(&socketLobby, SIGNAL(text(QString)), this, SLOT(sysMessage(QString)));
|
||||||
|
connect(&socketLobby, SIGNAL(receive(QString)), this, SLOT(dispatchMessage(QString)));
|
||||||
|
connect(&socketLobby, SIGNAL(disconnect()), this, SLOT(onDisconnected()));
|
||||||
|
|
||||||
|
QString hostString("%1:%2");
|
||||||
|
hostString = hostString.arg(QString::fromStdString(settings["launcher"]["lobbyUrl"].String()));
|
||||||
|
hostString = hostString.arg(settings["launcher"]["lobbyPort"].Integer());
|
||||||
|
|
||||||
|
ui->serverEdit->setText(hostString);
|
||||||
|
ui->userEdit->setText(QString::fromStdString(settings["launcher"]["lobbyUsername"].String()));
|
||||||
|
ui->kickButton->setVisible(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
Lobby::~Lobby()
|
||||||
|
{
|
||||||
|
delete ui;
|
||||||
|
}
|
||||||
|
|
||||||
|
QMap<QString, QString> Lobby::buildModsMap() const
|
||||||
|
{
|
||||||
|
QMap<QString, QString> result;
|
||||||
|
QObject * mainWindow = qApp->activeWindow();
|
||||||
|
if(!mainWindow)
|
||||||
|
mainWindow = parent();
|
||||||
|
if(!mainWindow)
|
||||||
|
return result; //probably something is really wrong here
|
||||||
|
|
||||||
|
while(mainWindow->parent())
|
||||||
|
mainWindow = mainWindow->parent();
|
||||||
|
const auto & modlist = qobject_cast<MainWindow*>(mainWindow)->getModList();
|
||||||
|
|
||||||
|
for(auto & modname : modlist.getModList())
|
||||||
|
{
|
||||||
|
auto mod = modlist.getMod(modname);
|
||||||
|
if(mod.isEnabled())
|
||||||
|
{
|
||||||
|
result[modname] = mod.getValue("version").toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Lobby::isModAvailable(const QString & modName, const QString & modVersion) const
|
||||||
|
{
|
||||||
|
QObject * mainWindow = qApp->activeWindow();
|
||||||
|
while(mainWindow->parent())
|
||||||
|
mainWindow = mainWindow->parent();
|
||||||
|
const auto & modlist = qobject_cast<MainWindow*>(mainWindow)->getModList();
|
||||||
|
|
||||||
|
if(!modlist.hasMod(modName))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
auto mod = modlist.getMod(modName);
|
||||||
|
return (mod.isInstalled () || mod.isAvailable()) && (mod.getValue("version") == modVersion);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Lobby::serverCommand(const ServerCommand & command) try
|
||||||
|
{
|
||||||
|
//initialize variables outside of switch block
|
||||||
|
const QString statusPlaceholder("%1 %2\n");
|
||||||
|
const auto & args = command.arguments;
|
||||||
|
int amount, tagPoint;
|
||||||
|
QString joinStr;
|
||||||
|
switch(command.command)
|
||||||
|
{
|
||||||
|
case SRVERROR:
|
||||||
|
protocolAssert(args.size());
|
||||||
|
chatMessage("System error", args[0], true);
|
||||||
|
if(authentificationStatus == AuthStatus::AUTH_NONE)
|
||||||
|
authentificationStatus = AuthStatus::AUTH_ERROR;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case CREATED:
|
||||||
|
protocolAssert(args.size());
|
||||||
|
hostSession = args[0];
|
||||||
|
session = args[0];
|
||||||
|
sysMessage("new session started");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SESSIONS:
|
||||||
|
protocolAssert(args.size());
|
||||||
|
amount = args[0].toInt();
|
||||||
|
protocolAssert(amount * 4 == (args.size() - 1));
|
||||||
|
ui->sessionsTable->setRowCount(amount);
|
||||||
|
|
||||||
|
tagPoint = 1;
|
||||||
|
for(int i = 0; i < amount; ++i)
|
||||||
|
{
|
||||||
|
QTableWidgetItem * sessionNameItem = new QTableWidgetItem(args[tagPoint++]);
|
||||||
|
ui->sessionsTable->setItem(i, 0, sessionNameItem);
|
||||||
|
|
||||||
|
int playersJoined = args[tagPoint++].toInt();
|
||||||
|
int playersTotal = args[tagPoint++].toInt();
|
||||||
|
QTableWidgetItem * sessionPlayerItem = new QTableWidgetItem(QString("%1/%2").arg(playersJoined).arg(playersTotal));
|
||||||
|
ui->sessionsTable->setItem(i, 1, sessionPlayerItem);
|
||||||
|
|
||||||
|
QTableWidgetItem * sessionProtectedItem = new QTableWidgetItem();
|
||||||
|
bool isPrivate = (args[tagPoint++] == "True");
|
||||||
|
sessionProtectedItem->setData(Qt::UserRole, isPrivate);
|
||||||
|
if(isPrivate)
|
||||||
|
sessionProtectedItem->setIcon(QIcon("icons:room-private.png"));
|
||||||
|
ui->sessionsTable->setItem(i, 2, sessionProtectedItem);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case JOINED:
|
||||||
|
case KICKED:
|
||||||
|
protocolAssert(args.size() == 2);
|
||||||
|
joinStr = (command.command == JOINED ? "%1 joined to the session %2" : "%1 left session %2");
|
||||||
|
|
||||||
|
if(args[1] == username)
|
||||||
|
{
|
||||||
|
ui->buttonReady->setText("Ready");
|
||||||
|
ui->chat->clear(); //cleanup the chat
|
||||||
|
sysMessage(joinStr.arg("you", args[0]));
|
||||||
|
session = args[0];
|
||||||
|
ui->stackedWidget->setCurrentWidget(command.command == JOINED ? ui->roomPage : ui->sessionsPage);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
sysMessage(joinStr.arg(args[1], args[0]));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case MODS: {
|
||||||
|
protocolAssert(args.size() > 0);
|
||||||
|
amount = args[0].toInt();
|
||||||
|
protocolAssert(amount * 2 == (args.size() - 1));
|
||||||
|
|
||||||
|
tagPoint = 1;
|
||||||
|
ui->modsList->clear();
|
||||||
|
auto enabledMods = buildModsMap();
|
||||||
|
for(int i = 0; i < amount; ++i, tagPoint += 2)
|
||||||
|
{
|
||||||
|
if(enabledMods.contains(args[tagPoint]))
|
||||||
|
{
|
||||||
|
if(enabledMods[args[tagPoint]] == args[tagPoint + 1])
|
||||||
|
enabledMods.remove(args[tagPoint]);
|
||||||
|
else
|
||||||
|
ui->modsList->addItem(new QListWidgetItem(QIcon("icons:mod-update.png"), QString("%1 (v%2)").arg(args[tagPoint], args[tagPoint + 1])));
|
||||||
|
}
|
||||||
|
else if(isModAvailable(args[tagPoint], args[tagPoint + 1]))
|
||||||
|
ui->modsList->addItem(new QListWidgetItem(QIcon("icons:mod-enabled.png"), QString("%1 (v%2)").arg(args[tagPoint], args[tagPoint + 1])));
|
||||||
|
else
|
||||||
|
ui->modsList->addItem(new QListWidgetItem(QIcon("icons:mod-delete.png"), QString("%1 (v%2)").arg(args[tagPoint], args[tagPoint + 1])));
|
||||||
|
}
|
||||||
|
for(auto & remainMod : enabledMods.keys())
|
||||||
|
{
|
||||||
|
ui->modsList->addItem(new QListWidgetItem(QIcon("icons:mod-disabled.png"), QString("%1 (v%2)").arg(remainMod, enabledMods[remainMod])));
|
||||||
|
}
|
||||||
|
if(!ui->modsList->count())
|
||||||
|
ui->modsList->addItem("No issues detected");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case CLIENTMODS: {
|
||||||
|
protocolAssert(args.size() > 1);
|
||||||
|
amount = args[1].toInt();
|
||||||
|
protocolAssert(amount * 2 == (args.size() - 2));
|
||||||
|
|
||||||
|
tagPoint = 2;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
case STATUS:
|
||||||
|
protocolAssert(args.size() > 0);
|
||||||
|
amount = args[0].toInt();
|
||||||
|
protocolAssert(amount * 2 == (args.size() - 1));
|
||||||
|
|
||||||
|
tagPoint = 1;
|
||||||
|
ui->playersList->clear();
|
||||||
|
for(int i = 0; i < amount; ++i, tagPoint += 2)
|
||||||
|
{
|
||||||
|
if(args[tagPoint + 1] == "True")
|
||||||
|
ui->playersList->addItem(new QListWidgetItem(QIcon("icons:mod-enabled.png"), args[tagPoint]));
|
||||||
|
else
|
||||||
|
ui->playersList->addItem(new QListWidgetItem(QIcon("icons:mod-disabled.png"), args[tagPoint]));
|
||||||
|
|
||||||
|
if(args[tagPoint] == username)
|
||||||
|
if(args[tagPoint + 1] == "True")
|
||||||
|
ui->buttonReady->setText("Not ready");
|
||||||
|
else
|
||||||
|
ui->buttonReady->setText("Ready");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case START: {
|
||||||
|
protocolAssert(args.size() == 1);
|
||||||
|
//actually start game
|
||||||
|
gameArgs << "--lobby";
|
||||||
|
gameArgs << "--lobby-address" << serverUrl;
|
||||||
|
gameArgs << "--lobby-port" << QString::number(serverPort);
|
||||||
|
gameArgs << "--uuid" << args[0];
|
||||||
|
startGame(gameArgs);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case HOST: {
|
||||||
|
protocolAssert(args.size() == 2);
|
||||||
|
gameArgs << "--lobby-host";
|
||||||
|
gameArgs << "--lobby-uuid" << args[0];
|
||||||
|
gameArgs << "--lobby-connections" << args[1];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case CHAT: {
|
||||||
|
protocolAssert(args.size() > 1);
|
||||||
|
QString msg;
|
||||||
|
for(int i = 1; i < args.size(); ++i)
|
||||||
|
msg += args[i];
|
||||||
|
chatMessage(args[0], msg);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
sysMessage("Unknown server command");
|
||||||
|
}
|
||||||
|
|
||||||
|
if(authentificationStatus == AuthStatus::AUTH_ERROR)
|
||||||
|
{
|
||||||
|
socketLobby.disconnectServer();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
authentificationStatus = AuthStatus::AUTH_OK;
|
||||||
|
ui->newButton->setEnabled(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch(const ProtocolError & e)
|
||||||
|
{
|
||||||
|
chatMessage("System error", e.what(), true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Lobby::dispatchMessage(QString txt) try
|
||||||
|
{
|
||||||
|
if(txt.isEmpty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
QStringList parseTags = txt.split(":>>");
|
||||||
|
protocolAssert(parseTags.size() > 1 && parseTags[0].isEmpty() && !parseTags[1].isEmpty());
|
||||||
|
|
||||||
|
for(int c = 1; c < parseTags.size(); ++c)
|
||||||
|
{
|
||||||
|
QStringList parseArgs = parseTags[c].split(":");
|
||||||
|
protocolAssert(parseArgs.size() > 1);
|
||||||
|
|
||||||
|
auto ctype = ProtocolStrings.key(parseArgs[0]);
|
||||||
|
parseArgs.pop_front();
|
||||||
|
ServerCommand cmd(ctype, parseArgs);
|
||||||
|
serverCommand(cmd);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch(const ProtocolError & e)
|
||||||
|
{
|
||||||
|
chatMessage("System error", e.what(), true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Lobby::onDisconnected()
|
||||||
|
{
|
||||||
|
authentificationStatus = AuthStatus::AUTH_NONE;
|
||||||
|
ui->stackedWidget->setCurrentWidget(ui->sessionsPage);
|
||||||
|
ui->connectButton->setChecked(false);
|
||||||
|
ui->serverEdit->setEnabled(true);
|
||||||
|
ui->userEdit->setEnabled(true);
|
||||||
|
ui->newButton->setEnabled(false);
|
||||||
|
ui->joinButton->setEnabled(false);
|
||||||
|
ui->sessionsTable->clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Lobby::chatMessage(QString title, QString body, bool isSystem)
|
||||||
|
{
|
||||||
|
QTextCharFormat fmtBody, fmtTitle;
|
||||||
|
fmtTitle.setFontWeight(QFont::Bold);
|
||||||
|
if(isSystem)
|
||||||
|
fmtBody.setFontWeight(QFont::DemiBold);
|
||||||
|
|
||||||
|
QTextCursor curs(ui->chat->document());
|
||||||
|
curs.movePosition(QTextCursor::End);
|
||||||
|
curs.insertText(title + ": ", fmtTitle);
|
||||||
|
curs.insertText(body + "\n", fmtBody);
|
||||||
|
ui->chat->ensureCursorVisible();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Lobby::sysMessage(QString body)
|
||||||
|
{
|
||||||
|
chatMessage("System", body, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Lobby::protocolAssert(bool expr)
|
||||||
|
{
|
||||||
|
if(!expr)
|
||||||
|
throw ProtocolError("Protocol error");
|
||||||
|
}
|
||||||
|
|
||||||
|
void Lobby::on_messageEdit_returnPressed()
|
||||||
|
{
|
||||||
|
socketLobby.send(ProtocolStrings[MESSAGE].arg(ui->messageEdit->text()));
|
||||||
|
ui->messageEdit->clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Lobby::on_connectButton_toggled(bool checked)
|
||||||
|
{
|
||||||
|
if(checked)
|
||||||
|
{
|
||||||
|
authentificationStatus = AuthStatus::AUTH_NONE;
|
||||||
|
username = ui->userEdit->text();
|
||||||
|
const int connectionTimeout = settings["launcher"]["connectionTimeout"].Integer();
|
||||||
|
|
||||||
|
auto serverStrings = ui->serverEdit->text().split(":");
|
||||||
|
if(serverStrings.size() != 2)
|
||||||
|
{
|
||||||
|
QMessageBox::critical(this, "Connection error", "Server address must have the format URL:port");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
serverUrl = serverStrings[0];
|
||||||
|
serverPort = serverStrings[1].toInt();
|
||||||
|
|
||||||
|
Settings node = settings.write["launcher"];
|
||||||
|
node["lobbyUrl"].String() = serverUrl.toStdString();
|
||||||
|
node["lobbyPort"].Integer() = serverPort;
|
||||||
|
node["lobbyUsername"].String() = username.toStdString();
|
||||||
|
|
||||||
|
ui->serverEdit->setEnabled(false);
|
||||||
|
ui->userEdit->setEnabled(false);
|
||||||
|
|
||||||
|
sysMessage("Connecting to " + serverUrl + ":" + QString::number(serverPort));
|
||||||
|
//show text immediately
|
||||||
|
ui->chat->repaint();
|
||||||
|
qApp->processEvents();
|
||||||
|
|
||||||
|
socketLobby.connectServer(serverUrl, serverPort, username, connectionTimeout);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ui->serverEdit->setEnabled(true);
|
||||||
|
ui->userEdit->setEnabled(true);
|
||||||
|
socketLobby.disconnectServer();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Lobby::on_newButton_clicked()
|
||||||
|
{
|
||||||
|
new LobbyRoomRequest(socketLobby, "", buildModsMap(), this);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Lobby::on_joinButton_clicked()
|
||||||
|
{
|
||||||
|
auto * item = ui->sessionsTable->item(ui->sessionsTable->currentRow(), 0);
|
||||||
|
if(item)
|
||||||
|
{
|
||||||
|
auto isPrivate = ui->sessionsTable->item(ui->sessionsTable->currentRow(), 2)->data(Qt::UserRole).toBool();
|
||||||
|
if(isPrivate)
|
||||||
|
new LobbyRoomRequest(socketLobby, item->text(), buildModsMap(), this);
|
||||||
|
else
|
||||||
|
socketLobby.requestJoinSession(item->text(), "", buildModsMap());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Lobby::on_buttonLeave_clicked()
|
||||||
|
{
|
||||||
|
socketLobby.requestLeaveSession(session);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Lobby::on_buttonReady_clicked()
|
||||||
|
{
|
||||||
|
if(ui->buttonReady->text() == "Ready")
|
||||||
|
ui->buttonReady->setText("Not ready");
|
||||||
|
else
|
||||||
|
ui->buttonReady->setText("Ready");
|
||||||
|
socketLobby.requestReadySession(session);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Lobby::on_sessionsTable_itemSelectionChanged()
|
||||||
|
{
|
||||||
|
auto selection = ui->sessionsTable->selectedItems();
|
||||||
|
ui->joinButton->setEnabled(!selection.empty());
|
||||||
|
}
|
||||||
|
|
||||||
|
void Lobby::on_playersList_currentRowChanged(int currentRow)
|
||||||
|
{
|
||||||
|
ui->kickButton->setVisible(ui->playersList->currentItem()
|
||||||
|
&& currentRow > 0
|
||||||
|
&& ui->playersList->currentItem()->text() != username);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Lobby::on_kickButton_clicked()
|
||||||
|
{
|
||||||
|
if(ui->playersList->currentItem() && ui->playersList->currentItem()->text() != username)
|
||||||
|
socketLobby.send(ProtocolStrings[KICK].arg(ui->playersList->currentItem()->text()));
|
||||||
|
}
|
||||||
|
|
76
launcher/lobby/lobby_moc.h
Normal file
76
launcher/lobby/lobby_moc.h
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
/*
|
||||||
|
* lobby_moc.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
|
||||||
|
#include <QWidget>
|
||||||
|
#include "lobby.h"
|
||||||
|
|
||||||
|
namespace Ui {
|
||||||
|
class Lobby;
|
||||||
|
}
|
||||||
|
|
||||||
|
class Lobby : public QWidget
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit Lobby(QWidget *parent = nullptr);
|
||||||
|
~Lobby();
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void on_messageEdit_returnPressed();
|
||||||
|
|
||||||
|
void chatMessage(QString title, QString body, bool isSystem = false);
|
||||||
|
void sysMessage(QString body);
|
||||||
|
void dispatchMessage(QString);
|
||||||
|
void serverCommand(const ServerCommand &);
|
||||||
|
|
||||||
|
void on_connectButton_toggled(bool checked);
|
||||||
|
|
||||||
|
void on_newButton_clicked();
|
||||||
|
|
||||||
|
void on_joinButton_clicked();
|
||||||
|
|
||||||
|
void on_buttonLeave_clicked();
|
||||||
|
|
||||||
|
void on_buttonReady_clicked();
|
||||||
|
|
||||||
|
void onDisconnected();
|
||||||
|
|
||||||
|
void on_sessionsTable_itemSelectionChanged();
|
||||||
|
|
||||||
|
void on_playersList_currentRowChanged(int currentRow);
|
||||||
|
|
||||||
|
void on_kickButton_clicked();
|
||||||
|
|
||||||
|
private:
|
||||||
|
QString serverUrl;
|
||||||
|
int serverPort;
|
||||||
|
|
||||||
|
Ui::Lobby *ui;
|
||||||
|
SocketLobby socketLobby;
|
||||||
|
QString hostSession;
|
||||||
|
QString session;
|
||||||
|
QString username;
|
||||||
|
QStringList gameArgs;
|
||||||
|
|
||||||
|
enum AuthStatus
|
||||||
|
{
|
||||||
|
AUTH_NONE, AUTH_OK, AUTH_ERROR
|
||||||
|
};
|
||||||
|
|
||||||
|
AuthStatus authentificationStatus = AUTH_NONE;
|
||||||
|
|
||||||
|
private:
|
||||||
|
QMap<QString, QString> buildModsMap() const;
|
||||||
|
bool isModAvailable(const QString & modName, const QString & modVersion) const;
|
||||||
|
|
||||||
|
|
||||||
|
void protocolAssert(bool);
|
||||||
|
};
|
216
launcher/lobby/lobby_moc.ui
Normal file
216
launcher/lobby/lobby_moc.ui
Normal file
@ -0,0 +1,216 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<ui version="4.0">
|
||||||
|
<class>Lobby</class>
|
||||||
|
<widget class="QWidget" name="Lobby">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>652</width>
|
||||||
|
<height>329</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="windowTitle">
|
||||||
|
<string>Form</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QGridLayout" name="gridLayout">
|
||||||
|
<item row="0" column="5">
|
||||||
|
<widget class="QPushButton" name="connectButton">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Connect</string>
|
||||||
|
</property>
|
||||||
|
<property name="checkable">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="3" column="0" colspan="3">
|
||||||
|
<widget class="QLineEdit" name="messageEdit"/>
|
||||||
|
</item>
|
||||||
|
<item row="0" column="3">
|
||||||
|
<widget class="QLabel" name="label_4">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Username</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="0" column="4">
|
||||||
|
<widget class="QLineEdit" name="userEdit"/>
|
||||||
|
</item>
|
||||||
|
<item row="0" column="1">
|
||||||
|
<widget class="QLineEdit" name="serverEdit">
|
||||||
|
<property name="text">
|
||||||
|
<string>127.0.0.1:5002</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="2" column="0" colspan="3">
|
||||||
|
<widget class="QPlainTextEdit" name="chat">
|
||||||
|
<property name="readOnly">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="0" column="0">
|
||||||
|
<widget class="QLabel" name="label">
|
||||||
|
<property name="text">
|
||||||
|
<string>Server</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="2" column="3" rowspan="2" colspan="3">
|
||||||
|
<widget class="QStackedWidget" name="stackedWidget">
|
||||||
|
<property name="currentIndex">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<widget class="QWidget" name="sessionsPage">
|
||||||
|
<layout class="QGridLayout" name="gridLayout_2">
|
||||||
|
<item row="0" column="0" colspan="2">
|
||||||
|
<widget class="QTableWidget" name="sessionsTable">
|
||||||
|
<property name="editTriggers">
|
||||||
|
<set>QAbstractItemView::NoEditTriggers</set>
|
||||||
|
</property>
|
||||||
|
<property name="selectionMode">
|
||||||
|
<enum>QAbstractItemView::SingleSelection</enum>
|
||||||
|
</property>
|
||||||
|
<property name="selectionBehavior">
|
||||||
|
<enum>QAbstractItemView::SelectRows</enum>
|
||||||
|
</property>
|
||||||
|
<attribute name="horizontalHeaderCascadingSectionResizes">
|
||||||
|
<bool>false</bool>
|
||||||
|
</attribute>
|
||||||
|
<attribute name="horizontalHeaderDefaultSectionSize">
|
||||||
|
<number>80</number>
|
||||||
|
</attribute>
|
||||||
|
<attribute name="horizontalHeaderShowSortIndicator" stdset="0">
|
||||||
|
<bool>false</bool>
|
||||||
|
</attribute>
|
||||||
|
<attribute name="horizontalHeaderStretchLastSection">
|
||||||
|
<bool>true</bool>
|
||||||
|
</attribute>
|
||||||
|
<attribute name="verticalHeaderMinimumSectionSize">
|
||||||
|
<number>20</number>
|
||||||
|
</attribute>
|
||||||
|
<attribute name="verticalHeaderDefaultSectionSize">
|
||||||
|
<number>20</number>
|
||||||
|
</attribute>
|
||||||
|
<column>
|
||||||
|
<property name="text">
|
||||||
|
<string>Session</string>
|
||||||
|
</property>
|
||||||
|
</column>
|
||||||
|
<column>
|
||||||
|
<property name="text">
|
||||||
|
<string>Players</string>
|
||||||
|
</property>
|
||||||
|
</column>
|
||||||
|
<column>
|
||||||
|
<property name="text">
|
||||||
|
<string/>
|
||||||
|
</property>
|
||||||
|
</column>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="0">
|
||||||
|
<widget class="QPushButton" name="newButton">
|
||||||
|
<property name="enabled">
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>New room</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="1">
|
||||||
|
<widget class="QPushButton" name="joinButton">
|
||||||
|
<property name="enabled">
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Join room</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
<widget class="QWidget" name="roomPage">
|
||||||
|
<layout class="QGridLayout" name="gridLayout_3">
|
||||||
|
<item row="5" column="1">
|
||||||
|
<widget class="QPushButton" name="buttonReady">
|
||||||
|
<property name="text">
|
||||||
|
<string>Ready</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="3" column="0">
|
||||||
|
<widget class="QLabel" name="label_5">
|
||||||
|
<property name="text">
|
||||||
|
<string>Mods mismatch</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="5" column="0">
|
||||||
|
<widget class="QPushButton" name="buttonLeave">
|
||||||
|
<property name="text">
|
||||||
|
<string>Leave</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="4" column="0" colspan="2">
|
||||||
|
<widget class="QListWidget" name="modsList">
|
||||||
|
<property name="editTriggers">
|
||||||
|
<set>QAbstractItemView::NoEditTriggers</set>
|
||||||
|
</property>
|
||||||
|
<property name="selectionMode">
|
||||||
|
<enum>QAbstractItemView::NoSelection</enum>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="2" column="0" colspan="2">
|
||||||
|
<widget class="QListWidget" name="playersList">
|
||||||
|
<property name="editTriggers">
|
||||||
|
<set>QAbstractItemView::NoEditTriggers</set>
|
||||||
|
</property>
|
||||||
|
<property name="selectionMode">
|
||||||
|
<enum>QAbstractItemView::SingleSelection</enum>
|
||||||
|
</property>
|
||||||
|
<property name="selectionBehavior">
|
||||||
|
<enum>QAbstractItemView::SelectRows</enum>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="1">
|
||||||
|
<widget class="QPushButton" name="kickButton">
|
||||||
|
<property name="text">
|
||||||
|
<string>Kick player</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="0">
|
||||||
|
<widget class="QLabel" name="label_3">
|
||||||
|
<property name="text">
|
||||||
|
<string>Players in the room</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
<resources/>
|
||||||
|
<connections/>
|
||||||
|
</ui>
|
50
launcher/lobby/lobbyroomrequest_moc.cpp
Normal file
50
launcher/lobby/lobbyroomrequest_moc.cpp
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
/*
|
||||||
|
* lobbyroomrequest_moc.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 "lobbyroomrequest_moc.h"
|
||||||
|
#include "ui_lobbyroomrequest_moc.h"
|
||||||
|
|
||||||
|
LobbyRoomRequest::LobbyRoomRequest(SocketLobby & socket, const QString & room, const QMap<QString, QString> & mods, QWidget *parent) :
|
||||||
|
QDialog(parent),
|
||||||
|
ui(new Ui::LobbyRoomRequest),
|
||||||
|
socketLobby(socket),
|
||||||
|
mods(mods)
|
||||||
|
{
|
||||||
|
ui->setupUi(this);
|
||||||
|
ui->nameEdit->setText(room);
|
||||||
|
if(!room.isEmpty())
|
||||||
|
{
|
||||||
|
ui->nameEdit->setReadOnly(true);
|
||||||
|
ui->totalPlayers->setEnabled(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
show();
|
||||||
|
}
|
||||||
|
|
||||||
|
LobbyRoomRequest::~LobbyRoomRequest()
|
||||||
|
{
|
||||||
|
delete ui;
|
||||||
|
}
|
||||||
|
|
||||||
|
void LobbyRoomRequest::on_buttonBox_accepted()
|
||||||
|
{
|
||||||
|
if(ui->nameEdit->isReadOnly())
|
||||||
|
{
|
||||||
|
socketLobby.requestJoinSession(ui->nameEdit->text(), ui->passwordEdit->text(), mods);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if(!ui->nameEdit->text().isEmpty())
|
||||||
|
{
|
||||||
|
int totalPlayers = ui->totalPlayers->currentIndex() + 2; //where 2 is a minimum amount of players
|
||||||
|
socketLobby.requestNewSession(ui->nameEdit->text(), totalPlayers, ui->passwordEdit->text(), mods);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
37
launcher/lobby/lobbyroomrequest_moc.h
Normal file
37
launcher/lobby/lobbyroomrequest_moc.h
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
/*
|
||||||
|
* lobbyroomrequest_moc.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
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#ifndef LOBBYROOMREQUEST_MOC_H
|
||||||
|
#define LOBBYROOMREQUEST_MOC_H
|
||||||
|
|
||||||
|
#include <QDialog>
|
||||||
|
#include "lobby.h"
|
||||||
|
|
||||||
|
namespace Ui {
|
||||||
|
class LobbyRoomRequest;
|
||||||
|
}
|
||||||
|
|
||||||
|
class LobbyRoomRequest : public QDialog
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit LobbyRoomRequest(SocketLobby & socket, const QString & room, const QMap<QString, QString> & mods, QWidget *parent = nullptr);
|
||||||
|
~LobbyRoomRequest();
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void on_buttonBox_accepted();
|
||||||
|
|
||||||
|
private:
|
||||||
|
Ui::LobbyRoomRequest *ui;
|
||||||
|
SocketLobby & socketLobby;
|
||||||
|
QMap<QString, QString> mods;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // LOBBYROOMREQUEST_MOC_H
|
148
launcher/lobby/lobbyroomrequest_moc.ui
Normal file
148
launcher/lobby/lobbyroomrequest_moc.ui
Normal file
@ -0,0 +1,148 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<ui version="4.0">
|
||||||
|
<class>LobbyRoomRequest</class>
|
||||||
|
<widget class="QDialog" name="LobbyRoomRequest">
|
||||||
|
<property name="windowModality">
|
||||||
|
<enum>Qt::WindowModal</enum>
|
||||||
|
</property>
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>193</width>
|
||||||
|
<height>188</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="windowTitle">
|
||||||
|
<string>Room settings</string>
|
||||||
|
</property>
|
||||||
|
<property name="locale">
|
||||||
|
<locale language="English" country="UnitedStates"/>
|
||||||
|
</property>
|
||||||
|
<property name="sizeGripEnabled">
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
|
<property name="modal">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
<layout class="QGridLayout" name="gridLayout">
|
||||||
|
<item row="0" column="0" colspan="2">
|
||||||
|
<widget class="QLabel" name="label">
|
||||||
|
<property name="text">
|
||||||
|
<string>Room name</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="0" colspan="2">
|
||||||
|
<widget class="QLineEdit" name="nameEdit"/>
|
||||||
|
</item>
|
||||||
|
<item row="2" column="0">
|
||||||
|
<widget class="QLabel" name="label_2">
|
||||||
|
<property name="text">
|
||||||
|
<string>Maximum players</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="2" column="1">
|
||||||
|
<widget class="QComboBox" name="totalPlayers">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<item>
|
||||||
|
<property name="text">
|
||||||
|
<string>2</string>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<property name="text">
|
||||||
|
<string>3</string>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<property name="text">
|
||||||
|
<string>4</string>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<property name="text">
|
||||||
|
<string>5</string>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<property name="text">
|
||||||
|
<string>6</string>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<property name="text">
|
||||||
|
<string>7</string>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<property name="text">
|
||||||
|
<string>8</string>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="3" column="0" colspan="2">
|
||||||
|
<widget class="QLabel" name="label_3">
|
||||||
|
<property name="text">
|
||||||
|
<string>Password (optional)</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="4" column="0" colspan="2">
|
||||||
|
<widget class="QLineEdit" name="passwordEdit"/>
|
||||||
|
</item>
|
||||||
|
<item row="5" column="0" colspan="2">
|
||||||
|
<widget class="QDialogButtonBox" name="buttonBox">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
<property name="standardButtons">
|
||||||
|
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
<resources/>
|
||||||
|
<connections>
|
||||||
|
<connection>
|
||||||
|
<sender>buttonBox</sender>
|
||||||
|
<signal>rejected()</signal>
|
||||||
|
<receiver>LobbyRoomRequest</receiver>
|
||||||
|
<slot>reject()</slot>
|
||||||
|
<hints>
|
||||||
|
<hint type="sourcelabel">
|
||||||
|
<x>316</x>
|
||||||
|
<y>260</y>
|
||||||
|
</hint>
|
||||||
|
<hint type="destinationlabel">
|
||||||
|
<x>286</x>
|
||||||
|
<y>274</y>
|
||||||
|
</hint>
|
||||||
|
</hints>
|
||||||
|
</connection>
|
||||||
|
<connection>
|
||||||
|
<sender>buttonBox</sender>
|
||||||
|
<signal>accepted()</signal>
|
||||||
|
<receiver>LobbyRoomRequest</receiver>
|
||||||
|
<slot>accept()</slot>
|
||||||
|
<hints>
|
||||||
|
<hint type="sourcelabel">
|
||||||
|
<x>248</x>
|
||||||
|
<y>254</y>
|
||||||
|
</hint>
|
||||||
|
<hint type="destinationlabel">
|
||||||
|
<x>157</x>
|
||||||
|
<y>274</y>
|
||||||
|
</hint>
|
||||||
|
</hints>
|
||||||
|
</connection>
|
||||||
|
</connections>
|
||||||
|
</ui>
|
@ -1,38 +1,86 @@
|
|||||||
/*
|
/*
|
||||||
* main.cpp, part of VCMI engine
|
* main.cpp, part of VCMI engine
|
||||||
*
|
*
|
||||||
* Authors: listed in file AUTHORS in main folder
|
* Authors: listed in file AUTHORS in main folder
|
||||||
*
|
*
|
||||||
* License: GNU General Public License v2.0 or later
|
* License: GNU General Public License v2.0 or later
|
||||||
* Full text of license available in license.txt file, in main folder
|
* Full text of license available in license.txt file, in main folder
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
#include "main.h"
|
#include "StdInc.h"
|
||||||
#include "mainwindow_moc.h"
|
#include "main.h"
|
||||||
|
#include "mainwindow_moc.h"
|
||||||
#include <QApplication>
|
|
||||||
|
#include <QApplication>
|
||||||
// Conan workaround https://github.com/conan-io/conan-center-index/issues/13332
|
#include <QProcess>
|
||||||
#ifdef VCMI_IOS
|
#include <QMessageBox>
|
||||||
#if __has_include("QIOSIntegrationPlugin.h")
|
#include "../lib/VCMIDirs.h"
|
||||||
#include "QIOSIntegrationPlugin.h"
|
|
||||||
#endif
|
// Conan workaround https://github.com/conan-io/conan-center-index/issues/13332
|
||||||
#endif
|
#ifdef VCMI_IOS
|
||||||
|
#if __has_include("QIOSIntegrationPlugin.h")
|
||||||
int main(int argc, char * argv[])
|
#include "QIOSIntegrationPlugin.h"
|
||||||
{
|
#endif
|
||||||
int result;
|
int argcForClient;
|
||||||
#ifdef VCMI_IOS
|
char ** argvForClient;
|
||||||
{
|
#endif
|
||||||
#endif
|
|
||||||
QApplication vcmilauncher(argc, argv);
|
int main(int argc, char * argv[])
|
||||||
MainWindow mainWindow;
|
{
|
||||||
mainWindow.show();
|
int result;
|
||||||
result = vcmilauncher.exec();
|
#ifdef VCMI_IOS
|
||||||
#ifdef VCMI_IOS
|
{
|
||||||
}
|
#endif
|
||||||
if (result == 0)
|
QApplication vcmilauncher(argc, argv);
|
||||||
launchGame(argc, argv);
|
MainWindow mainWindow;
|
||||||
#endif
|
mainWindow.show();
|
||||||
return result;
|
result = vcmilauncher.exec();
|
||||||
}
|
#ifdef VCMI_IOS
|
||||||
|
}
|
||||||
|
if (result == 0)
|
||||||
|
launchGame(argcForClient, argvForClient);
|
||||||
|
#endif
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void startGame(const QStringList & args)
|
||||||
|
{
|
||||||
|
logGlobal->warn("Starting game with the arguments: %s", args.join(" ").toStdString());
|
||||||
|
|
||||||
|
#ifdef Q_OS_IOS
|
||||||
|
argcForClient = args.size() + 1; //first argument is omitted
|
||||||
|
argvForClient = new char*[argcForClient];
|
||||||
|
argvForClient[0] = "vcmiclient";
|
||||||
|
for(int i = 1; i < argcForClient; ++i)
|
||||||
|
{
|
||||||
|
std::string s = args.at(i - 1).toStdString();
|
||||||
|
argvForClient[i] = new char[s.size() + 1];
|
||||||
|
strcpy(argvForClient[i], s.c_str());
|
||||||
|
}
|
||||||
|
qApp->quit();
|
||||||
|
#else
|
||||||
|
startExecutable(pathToQString(VCMIDirs::get().clientPath()), args);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifndef Q_OS_IOS
|
||||||
|
void startExecutable(QString name, const QStringList & args)
|
||||||
|
{
|
||||||
|
QProcess process;
|
||||||
|
|
||||||
|
// Start the executable
|
||||||
|
if(process.startDetached(name, args))
|
||||||
|
{
|
||||||
|
qApp->quit();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
QMessageBox::critical(qApp->activeWindow(),
|
||||||
|
"Error starting executable",
|
||||||
|
"Failed to start " + name + "\n"
|
||||||
|
"Reason: " + process.errorString(),
|
||||||
|
QMessageBox::Ok,
|
||||||
|
QMessageBox::Ok);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
@ -1,14 +1,18 @@
|
|||||||
/*
|
/*
|
||||||
* main.h, part of VCMI engine
|
* main.h, part of VCMI engine
|
||||||
*
|
*
|
||||||
* Authors: listed in file AUTHORS in main folder
|
* Authors: listed in file AUTHORS in main folder
|
||||||
*
|
*
|
||||||
* License: GNU General Public License v2.0 or later
|
* License: GNU General Public License v2.0 or later
|
||||||
* Full text of license available in license.txt file, in main folder
|
* Full text of license available in license.txt file, in main folder
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#ifdef VCMI_IOS
|
void startGame(const QStringList & args);
|
||||||
extern "C" void launchGame(int argc, char * argv[]);
|
|
||||||
#endif
|
#ifdef VCMI_IOS
|
||||||
|
extern "C" void launchGame(int argc, char * argv[]);
|
||||||
|
#else
|
||||||
|
void startExecutable(QString name, const QStringList & args);
|
||||||
|
#endif
|
||||||
|
@ -11,7 +11,6 @@
|
|||||||
#include "mainwindow_moc.h"
|
#include "mainwindow_moc.h"
|
||||||
#include "ui_mainwindow_moc.h"
|
#include "ui_mainwindow_moc.h"
|
||||||
|
|
||||||
#include <QProcess>
|
|
||||||
#include <QDir>
|
#include <QDir>
|
||||||
|
|
||||||
#include "../lib/CConfigHandler.h"
|
#include "../lib/CConfigHandler.h"
|
||||||
@ -20,6 +19,7 @@
|
|||||||
#include "../lib/logging/CBasicLogConfigurator.h"
|
#include "../lib/logging/CBasicLogConfigurator.h"
|
||||||
|
|
||||||
#include "updatedialog_moc.h"
|
#include "updatedialog_moc.h"
|
||||||
|
#include "main.h"
|
||||||
|
|
||||||
void MainWindow::load()
|
void MainWindow::load()
|
||||||
{
|
{
|
||||||
@ -85,9 +85,9 @@ MainWindow::MainWindow(QWidget * parent)
|
|||||||
}
|
}
|
||||||
ui->tabListWidget->setCurrentIndex(0);
|
ui->tabListWidget->setCurrentIndex(0);
|
||||||
|
|
||||||
ui->settingsView->isExtraResolutionsModEnabled = ui->stackedWidgetPage2->isExtraResolutionsModEnabled();
|
ui->settingsView->isExtraResolutionsModEnabled = ui->modlistView->isExtraResolutionsModEnabled();
|
||||||
ui->settingsView->setDisplayList();
|
ui->settingsView->setDisplayList();
|
||||||
connect(ui->stackedWidgetPage2, &CModListView::extraResolutionsEnabledChanged,
|
connect(ui->modlistView, &CModListView::extraResolutionsEnabledChanged,
|
||||||
ui->settingsView, &CSettingsView::fillValidResolutions);
|
ui->settingsView, &CSettingsView::fillValidResolutions);
|
||||||
|
|
||||||
connect(ui->tabSelectList, &QListWidget::currentRowChanged, [this](int i) {
|
connect(ui->tabSelectList, &QListWidget::currentRowChanged, [this](int i) {
|
||||||
@ -97,6 +97,11 @@ MainWindow::MainWindow(QWidget * parent)
|
|||||||
#endif
|
#endif
|
||||||
ui->tabListWidget->setCurrentIndex(i);
|
ui->tabListWidget->setCurrentIndex(i);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
#ifdef Q_OS_IOS
|
||||||
|
QScroller::grabGesture(ui->tabSelectList, QScroller::LeftMouseButtonGesture);
|
||||||
|
ui->tabSelectList->setVerticalScrollMode(QAbstractItemView::ScrollPerPixel);
|
||||||
|
#endif
|
||||||
|
|
||||||
if(settings["launcher"]["updateOnStartup"].Bool())
|
if(settings["launcher"]["updateOnStartup"].Bool())
|
||||||
UpdateDialog::showUpdateDialog(false);
|
UpdateDialog::showUpdateDialog(false);
|
||||||
@ -114,31 +119,15 @@ MainWindow::~MainWindow()
|
|||||||
|
|
||||||
void MainWindow::on_startGameButton_clicked()
|
void MainWindow::on_startGameButton_clicked()
|
||||||
{
|
{
|
||||||
#ifdef Q_OS_IOS
|
startGame({});
|
||||||
qApp->quit();
|
|
||||||
#else
|
|
||||||
startExecutable(pathToQString(VCMIDirs::get().clientPath()));
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifndef Q_OS_IOS
|
void MainWindow::on_tabSelectList_currentRowChanged(int currentRow)
|
||||||
void MainWindow::startExecutable(QString name)
|
|
||||||
{
|
{
|
||||||
QProcess process;
|
ui->startGameButton->setEnabled(currentRow != TabRows::LOBBY);
|
||||||
|
}
|
||||||
// Start the executable
|
|
||||||
if(process.startDetached(name, {}))
|
const CModList & MainWindow::getModList() const
|
||||||
{
|
{
|
||||||
close(); // exit launcher
|
return ui->modlistView->getModList();
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
QMessageBox::critical(this,
|
|
||||||
"Error starting executable",
|
|
||||||
"Failed to start " + name + "\n"
|
|
||||||
"Reason: " + process.errorString(),
|
|
||||||
QMessageBox::Ok,
|
|
||||||
QMessageBox::Ok);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
@ -19,6 +19,8 @@ const QString appName = "VCMI Launcher";
|
|||||||
}
|
}
|
||||||
|
|
||||||
class QTableWidgetItem;
|
class QTableWidgetItem;
|
||||||
|
class CModList;
|
||||||
|
|
||||||
|
|
||||||
class MainWindow : public QMainWindow
|
class MainWindow : public QMainWindow
|
||||||
{
|
{
|
||||||
@ -27,14 +29,22 @@ class MainWindow : public QMainWindow
|
|||||||
private:
|
private:
|
||||||
Ui::MainWindow * ui;
|
Ui::MainWindow * ui;
|
||||||
void load();
|
void load();
|
||||||
#ifndef Q_OS_IOS
|
|
||||||
void startExecutable(QString name);
|
enum TabRows
|
||||||
#endif
|
{
|
||||||
|
MODS = 0, SETTINGS = 1, LOBBY = 2
|
||||||
|
};
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit MainWindow(QWidget * parent = 0);
|
explicit MainWindow(QWidget * parent = 0);
|
||||||
~MainWindow();
|
~MainWindow();
|
||||||
|
|
||||||
private slots:
|
const CModList & getModList() const;
|
||||||
|
|
||||||
|
|
||||||
|
public slots:
|
||||||
void on_startGameButton_clicked();
|
void on_startGameButton_clicked();
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void on_tabSelectList_currentRowChanged(int currentRow);
|
||||||
};
|
};
|
||||||
|
@ -51,6 +51,9 @@
|
|||||||
<property name="horizontalScrollBarPolicy">
|
<property name="horizontalScrollBarPolicy">
|
||||||
<enum>Qt::ScrollBarAlwaysOff</enum>
|
<enum>Qt::ScrollBarAlwaysOff</enum>
|
||||||
</property>
|
</property>
|
||||||
|
<property name="sizeAdjustPolicy">
|
||||||
|
<enum>QAbstractScrollArea::AdjustToContents</enum>
|
||||||
|
</property>
|
||||||
<property name="editTriggers">
|
<property name="editTriggers">
|
||||||
<set>QAbstractItemView::NoEditTriggers</set>
|
<set>QAbstractItemView::NoEditTriggers</set>
|
||||||
</property>
|
</property>
|
||||||
@ -75,6 +78,9 @@
|
|||||||
<property name="flow">
|
<property name="flow">
|
||||||
<enum>QListView::TopToBottom</enum>
|
<enum>QListView::TopToBottom</enum>
|
||||||
</property>
|
</property>
|
||||||
|
<property name="isWrapping" stdset="0">
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
<property name="resizeMode">
|
<property name="resizeMode">
|
||||||
<enum>QListView::Adjust</enum>
|
<enum>QListView::Adjust</enum>
|
||||||
</property>
|
</property>
|
||||||
@ -91,7 +97,7 @@
|
|||||||
<bool>true</bool>
|
<bool>true</bool>
|
||||||
</property>
|
</property>
|
||||||
<property name="wordWrap">
|
<property name="wordWrap">
|
||||||
<bool>true</bool>
|
<bool>false</bool>
|
||||||
</property>
|
</property>
|
||||||
<item>
|
<item>
|
||||||
<property name="text">
|
<property name="text">
|
||||||
@ -111,13 +117,21 @@
|
|||||||
<normaloff>icons:menu-settings.png</normaloff>icons:menu-settings.png</iconset>
|
<normaloff>icons:menu-settings.png</normaloff>icons:menu-settings.png</iconset>
|
||||||
</property>
|
</property>
|
||||||
</item>
|
</item>
|
||||||
|
<item>
|
||||||
|
<property name="text">
|
||||||
|
<string>Lobby</string>
|
||||||
|
</property>
|
||||||
|
<property name="icon">
|
||||||
|
<iconset>
|
||||||
|
<normaloff>icons:menu-lobby.png</normaloff>icons:menu-lobby.png</iconset>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="2" column="0">
|
<item row="2" column="0">
|
||||||
<widget class="QLabel" name="startGameTitle">
|
<widget class="QLabel" name="startGameTitle">
|
||||||
<property name="font">
|
<property name="font">
|
||||||
<font>
|
<font>
|
||||||
<weight>75</weight>
|
|
||||||
<bold>true</bold>
|
<bold>true</bold>
|
||||||
</font>
|
</font>
|
||||||
</property>
|
</property>
|
||||||
@ -143,8 +157,9 @@
|
|||||||
<property name="currentIndex">
|
<property name="currentIndex">
|
||||||
<number>0</number>
|
<number>0</number>
|
||||||
</property>
|
</property>
|
||||||
<widget class="CModListView" name="stackedWidgetPage2"/>
|
<widget class="CModListView" name="modlistView"/>
|
||||||
<widget class="CSettingsView" name="settingsView"/>
|
<widget class="CSettingsView" name="settingsView"/>
|
||||||
|
<widget class="Lobby" name="lobbyView"/>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="1" column="0">
|
<item row="1" column="0">
|
||||||
@ -202,6 +217,12 @@
|
|||||||
<header>settingsView/csettingsview_moc.h</header>
|
<header>settingsView/csettingsview_moc.h</header>
|
||||||
<container>1</container>
|
<container>1</container>
|
||||||
</customwidget>
|
</customwidget>
|
||||||
|
<customwidget>
|
||||||
|
<class>Lobby</class>
|
||||||
|
<extends>QWidget</extends>
|
||||||
|
<header>lobby/lobby_moc.h</header>
|
||||||
|
<container>1</container>
|
||||||
|
</customwidget>
|
||||||
</customwidgets>
|
</customwidgets>
|
||||||
<tabstops>
|
<tabstops>
|
||||||
<tabstop>tabSelectList</tabstop>
|
<tabstop>tabSelectList</tabstop>
|
||||||
|
@ -102,6 +102,17 @@ CModListView::CModListView(QWidget * parent)
|
|||||||
{
|
{
|
||||||
manager->resetRepositories();
|
manager->resetRepositories();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef Q_OS_IOS
|
||||||
|
for(auto * scrollWidget : {
|
||||||
|
(QAbstractItemView*)ui->allModsView,
|
||||||
|
(QAbstractItemView*)ui->screenshotsList})
|
||||||
|
{
|
||||||
|
QScroller::grabGesture(scrollWidget, QScroller::LeftMouseButtonGesture);
|
||||||
|
scrollWidget->setVerticalScrollMode(QAbstractItemView::ScrollPerPixel);
|
||||||
|
scrollWidget->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void CModListView::loadRepositories()
|
void CModListView::loadRepositories()
|
||||||
@ -825,3 +836,9 @@ void CModListView::on_showInfoButton_clicked()
|
|||||||
{
|
{
|
||||||
showModInfo();
|
showModInfo();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const CModList & CModListView::getModList() const
|
||||||
|
{
|
||||||
|
assert(modModel);
|
||||||
|
return *modModel;
|
||||||
|
}
|
||||||
|
@ -18,6 +18,7 @@ class CModListView;
|
|||||||
}
|
}
|
||||||
|
|
||||||
class CModManager;
|
class CModManager;
|
||||||
|
class CModList;
|
||||||
class CModListModel;
|
class CModListModel;
|
||||||
class CModFilterModel;
|
class CModFilterModel;
|
||||||
class CDownloadManager;
|
class CDownloadManager;
|
||||||
@ -80,6 +81,8 @@ public:
|
|||||||
void selectMod(const QModelIndex & index);
|
void selectMod(const QModelIndex & index);
|
||||||
bool isExtraResolutionsModEnabled() const;
|
bool isExtraResolutionsModEnabled() const;
|
||||||
|
|
||||||
|
const CModList & getModList() const;
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void dataChanged(const QModelIndex & topleft, const QModelIndex & bottomRight);
|
void dataChanged(const QModelIndex & topleft, const QModelIndex & bottomRight);
|
||||||
void modSelected(const QModelIndex & current, const QModelIndex & previous);
|
void modSelected(const QModelIndex & current, const QModelIndex & previous);
|
||||||
|
@ -23,6 +23,8 @@ bfs::path IVCMIDirs::userLogsPath() const { return userCachePath(); }
|
|||||||
|
|
||||||
bfs::path IVCMIDirs::userSavePath() const { return userDataPath() / "Saves"; }
|
bfs::path IVCMIDirs::userSavePath() const { return userDataPath() / "Saves"; }
|
||||||
|
|
||||||
|
bfs::path IVCMIDirs::userExtractedPath() const { return userCachePath() / "extracted"; }
|
||||||
|
|
||||||
bfs::path IVCMIDirs::fullLibraryPath(const std::string &desiredFolder, const std::string &baseLibName) const
|
bfs::path IVCMIDirs::fullLibraryPath(const std::string &desiredFolder, const std::string &baseLibName) const
|
||||||
{
|
{
|
||||||
return libraryPath() / desiredFolder / libraryName(baseLibName);
|
return libraryPath() / desiredFolder / libraryName(baseLibName);
|
||||||
@ -36,15 +38,16 @@ std::string IVCMIDirs::genHelpString() const
|
|||||||
const auto gdStringA = boost::algorithm::join(tempVec, ":");
|
const auto gdStringA = boost::algorithm::join(tempVec, ":");
|
||||||
|
|
||||||
return
|
return
|
||||||
" game data: " + gdStringA + "\n"
|
" game data: " + gdStringA + "\n"
|
||||||
" libraries: " + libraryPath().string() + "\n"
|
" libraries: " + libraryPath().string() + "\n"
|
||||||
" server: " + serverPath().string() + "\n"
|
" server: " + serverPath().string() + "\n"
|
||||||
"\n"
|
"\n"
|
||||||
" user data: " + userDataPath().string() + "\n"
|
" user data: " + userDataPath().string() + "\n"
|
||||||
" user cache: " + userCachePath().string() + "\n"
|
" user cache: " + userCachePath().string() + "\n"
|
||||||
" user config: " + userConfigPath().string() + "\n"
|
" user config: " + userConfigPath().string() + "\n"
|
||||||
" user logs: " + userLogsPath().string() + "\n"
|
" user logs: " + userLogsPath().string() + "\n"
|
||||||
" user saves: " + userSavePath().string() + "\n"; // Should end without new-line?
|
" user saves: " + userSavePath().string() + "\n";
|
||||||
|
" user extracted: " + userExtractedPath().string() + "\n"; // Should end without new-line?
|
||||||
}
|
}
|
||||||
|
|
||||||
void IVCMIDirs::init()
|
void IVCMIDirs::init()
|
||||||
@ -148,24 +151,24 @@ bool StartBatchCopyDataProgram(
|
|||||||
class VCMIDirsWIN32 final : public IVCMIDirs
|
class VCMIDirsWIN32 final : public IVCMIDirs
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
boost::filesystem::path userDataPath() const override;
|
bfs::path userDataPath() const override;
|
||||||
boost::filesystem::path userCachePath() const override;
|
bfs::path userCachePath() const override;
|
||||||
boost::filesystem::path userConfigPath() const override;
|
bfs::path userConfigPath() const override;
|
||||||
|
|
||||||
std::vector<boost::filesystem::path> dataPaths() const override;
|
std::vector<bfs::path> dataPaths() const override;
|
||||||
|
|
||||||
boost::filesystem::path clientPath() const override;
|
bfs::path clientPath() const override;
|
||||||
boost::filesystem::path serverPath() const override;
|
bfs::path serverPath() const override;
|
||||||
|
|
||||||
boost::filesystem::path libraryPath() const override;
|
bfs::path libraryPath() const override;
|
||||||
boost::filesystem::path binaryPath() const override;
|
bfs::path binaryPath() const override;
|
||||||
|
|
||||||
std::string libraryName(const std::string& basename) const override;
|
std::string libraryName(const std::string& basename) const override;
|
||||||
|
|
||||||
void init() override;
|
void init() override;
|
||||||
protected:
|
protected:
|
||||||
boost::filesystem::path oldUserDataPath() const;
|
bfs::path oldUserDataPath() const;
|
||||||
boost::filesystem::path oldUserSavePath() const;
|
bfs::path oldUserSavePath() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
void VCMIDirsWIN32::init()
|
void VCMIDirsWIN32::init()
|
||||||
@ -355,8 +358,8 @@ std::string VCMIDirsWIN32::libraryName(const std::string& basename) const { retu
|
|||||||
class IVCMIDirsUNIX : public IVCMIDirs
|
class IVCMIDirsUNIX : public IVCMIDirs
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
boost::filesystem::path clientPath() const override;
|
bfs::path clientPath() const override;
|
||||||
boost::filesystem::path serverPath() const override;
|
bfs::path serverPath() const override;
|
||||||
|
|
||||||
virtual bool developmentMode() const;
|
virtual bool developmentMode() const;
|
||||||
};
|
};
|
||||||
@ -427,14 +430,14 @@ bfs::path VCMIDirsIOS::binaryPath() const { return {iOS_utils::bundlePath()}; }
|
|||||||
class VCMIDirsOSX final : public VCMIDirsApple
|
class VCMIDirsOSX final : public VCMIDirsApple
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
boost::filesystem::path userDataPath() const override;
|
bfs::path userDataPath() const override;
|
||||||
boost::filesystem::path userCachePath() const override;
|
bfs::path userCachePath() const override;
|
||||||
boost::filesystem::path userLogsPath() const override;
|
bfs::path userLogsPath() const override;
|
||||||
|
|
||||||
std::vector<boost::filesystem::path> dataPaths() const override;
|
std::vector<bfs::path> dataPaths() const override;
|
||||||
|
|
||||||
boost::filesystem::path libraryPath() const override;
|
bfs::path libraryPath() const override;
|
||||||
boost::filesystem::path binaryPath() const override;
|
bfs::path binaryPath() const override;
|
||||||
|
|
||||||
void init() override;
|
void init() override;
|
||||||
};
|
};
|
||||||
@ -464,12 +467,12 @@ void VCMIDirsOSX::init()
|
|||||||
|
|
||||||
for (bfs::directory_iterator file(from); file != bfs::directory_iterator(); ++file)
|
for (bfs::directory_iterator file(from); file != bfs::directory_iterator(); ++file)
|
||||||
{
|
{
|
||||||
const boost::filesystem::path& srcFilePath = file->path();
|
const bfs::path& srcFilePath = file->path();
|
||||||
const boost::filesystem::path dstFilePath = to / srcFilePath.filename();
|
const bfs::path dstFilePath = to / srcFilePath.filename();
|
||||||
|
|
||||||
// TODO: Aplication should ask user what to do when file exists:
|
// TODO: Aplication should ask user what to do when file exists:
|
||||||
// replace/ignore/stop process/replace all/ignore all
|
// replace/ignore/stop process/replace all/ignore all
|
||||||
if (!boost::filesystem::exists(dstFilePath))
|
if (!bfs::exists(dstFilePath))
|
||||||
bfs::rename(srcFilePath, dstFilePath);
|
bfs::rename(srcFilePath, dstFilePath);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -526,14 +529,14 @@ bfs::path VCMIDirsOSX::binaryPath() const { return "."; }
|
|||||||
class VCMIDirsXDG : public IVCMIDirsUNIX
|
class VCMIDirsXDG : public IVCMIDirsUNIX
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
boost::filesystem::path userDataPath() const override;
|
bfs::path userDataPath() const override;
|
||||||
boost::filesystem::path userCachePath() const override;
|
bfs::path userCachePath() const override;
|
||||||
boost::filesystem::path userConfigPath() const override;
|
bfs::path userConfigPath() const override;
|
||||||
|
|
||||||
std::vector<boost::filesystem::path> dataPaths() const override;
|
std::vector<bfs::path> dataPaths() const override;
|
||||||
|
|
||||||
boost::filesystem::path libraryPath() const override;
|
bfs::path libraryPath() const override;
|
||||||
boost::filesystem::path binaryPath() const override;
|
bfs::path binaryPath() const override;
|
||||||
|
|
||||||
std::string libraryName(const std::string& basename) const override;
|
std::string libraryName(const std::string& basename) const override;
|
||||||
};
|
};
|
||||||
|
@ -29,6 +29,9 @@ public:
|
|||||||
// Path to saved games
|
// Path to saved games
|
||||||
virtual boost::filesystem::path userSavePath() const;
|
virtual boost::filesystem::path userSavePath() const;
|
||||||
|
|
||||||
|
// Path to "extracted" directory, used to temporarily hold extracted Original H3 files
|
||||||
|
virtual boost::filesystem::path userExtractedPath() const;
|
||||||
|
|
||||||
// Paths to global system-wide data directories. First items have higher priority
|
// Paths to global system-wide data directories. First items have higher priority
|
||||||
virtual std::vector<boost::filesystem::path> dataPaths() const = 0;
|
virtual std::vector<boost::filesystem::path> dataPaths() const = 0;
|
||||||
|
|
||||||
|
@ -39,13 +39,13 @@ VCMI_LIB_NAMESPACE_BEGIN
|
|||||||
|
|
||||||
LibClasses * VLC = nullptr;
|
LibClasses * VLC = nullptr;
|
||||||
|
|
||||||
DLL_LINKAGE void preinitDLL(CConsoleHandler * Console, bool onlyEssential)
|
DLL_LINKAGE void preinitDLL(CConsoleHandler * Console, bool onlyEssential, bool extractArchives)
|
||||||
{
|
{
|
||||||
console = Console;
|
console = Console;
|
||||||
VLC = new LibClasses();
|
VLC = new LibClasses();
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
VLC->loadFilesystem(onlyEssential);
|
VLC->loadFilesystem(onlyEssential, extractArchives);
|
||||||
}
|
}
|
||||||
catch(...)
|
catch(...)
|
||||||
{
|
{
|
||||||
@ -157,7 +157,7 @@ void LibClasses::updateEntity(Metatype metatype, int32_t index, const JsonNode &
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void LibClasses::loadFilesystem(bool onlyEssential)
|
void LibClasses::loadFilesystem(bool onlyEssential, bool extractArchives)
|
||||||
{
|
{
|
||||||
CStopWatch totalTime;
|
CStopWatch totalTime;
|
||||||
CStopWatch loadTime;
|
CStopWatch loadTime;
|
||||||
@ -165,7 +165,7 @@ void LibClasses::loadFilesystem(bool onlyEssential)
|
|||||||
CResourceHandler::initialize();
|
CResourceHandler::initialize();
|
||||||
logGlobal->info("\tInitialization: %d ms", loadTime.getDiff());
|
logGlobal->info("\tInitialization: %d ms", loadTime.getDiff());
|
||||||
|
|
||||||
CResourceHandler::load("config/filesystem.json");
|
CResourceHandler::load("config/filesystem.json", extractArchives);
|
||||||
logGlobal->info("\tData loading: %d ms", loadTime.getDiff());
|
logGlobal->info("\tData loading: %d ms", loadTime.getDiff());
|
||||||
|
|
||||||
modh = new CModHandler();
|
modh = new CModHandler();
|
||||||
|
@ -100,8 +100,8 @@ public:
|
|||||||
void init(bool onlyEssential); //uses standard config file
|
void init(bool onlyEssential); //uses standard config file
|
||||||
void clear(); //deletes all handlers and its data
|
void clear(); //deletes all handlers and its data
|
||||||
|
|
||||||
|
// basic initialization. should be called before init(). Can also extract original H3 archives
|
||||||
void loadFilesystem(bool onlyEssential);// basic initialization. should be called before init()
|
void loadFilesystem(bool onlyEssential, bool extractArchives = false);
|
||||||
|
|
||||||
#if SCRIPTING_ENABLED
|
#if SCRIPTING_ENABLED
|
||||||
void scriptsLoaded();
|
void scriptsLoaded();
|
||||||
@ -151,7 +151,7 @@ public:
|
|||||||
|
|
||||||
extern DLL_LINKAGE LibClasses * VLC;
|
extern DLL_LINKAGE LibClasses * VLC;
|
||||||
|
|
||||||
DLL_LINKAGE void preinitDLL(CConsoleHandler * Console, bool onlyEssential = false);
|
DLL_LINKAGE void preinitDLL(CConsoleHandler * Console, bool onlyEssential = false, bool extractArchives = false);
|
||||||
DLL_LINKAGE void loadDLLClasses(bool onlyEssential = false);
|
DLL_LINKAGE void loadDLLClasses(bool onlyEssential = false);
|
||||||
|
|
||||||
|
|
||||||
|
@ -10,6 +10,7 @@
|
|||||||
#include "StdInc.h"
|
#include "StdInc.h"
|
||||||
#include "CArchiveLoader.h"
|
#include "CArchiveLoader.h"
|
||||||
|
|
||||||
|
#include "VCMIDirs.h"
|
||||||
#include "CFileInputStream.h"
|
#include "CFileInputStream.h"
|
||||||
#include "CCompressedStream.h"
|
#include "CCompressedStream.h"
|
||||||
|
|
||||||
@ -23,9 +24,10 @@ ArchiveEntry::ArchiveEntry()
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
CArchiveLoader::CArchiveLoader(std::string _mountPoint, boost::filesystem::path _archive) :
|
CArchiveLoader::CArchiveLoader(std::string _mountPoint, bfs::path _archive, bool extractArchives) :
|
||||||
archive(std::move(_archive)),
|
archive(std::move(_archive)),
|
||||||
mountPoint(std::move(_mountPoint))
|
mountPoint(std::move(_mountPoint)),
|
||||||
|
extractArchives(extractArchives)
|
||||||
{
|
{
|
||||||
// Open archive file(.snd, .vid, .lod)
|
// Open archive file(.snd, .vid, .lod)
|
||||||
CFileInputStream fileStream(archive);
|
CFileInputStream fileStream(archive);
|
||||||
@ -77,6 +79,25 @@ void CArchiveLoader::initLODArchive(const std::string &mountPoint, CFileInputStr
|
|||||||
|
|
||||||
// Add lod entry to local entries map
|
// Add lod entry to local entries map
|
||||||
entries[ResourceID(mountPoint + entry.name)] = entry;
|
entries[ResourceID(mountPoint + entry.name)] = entry;
|
||||||
|
|
||||||
|
if(extractArchives)
|
||||||
|
{
|
||||||
|
si64 currentPosition = fileStream.tell(); // save filestream position
|
||||||
|
|
||||||
|
std::string fName = filename;
|
||||||
|
boost::to_upper(fName);
|
||||||
|
|
||||||
|
if(fName.find(".PCX") != std::string::npos)
|
||||||
|
extractToFolder("IMAGES", mountPoint, entry);
|
||||||
|
else if ((fName.find(".DEF") != std::string::npos ) || (fName.find(".MSK") != std::string::npos) || (fName.find(".FNT") != std::string::npos) || (fName.find(".PAL") != std::string::npos))
|
||||||
|
extractToFolder("SPRITES", mountPoint, entry);
|
||||||
|
else if ((fName.find(".h3c") != std::string::npos))
|
||||||
|
extractToFolder("SPRITES", mountPoint, entry);
|
||||||
|
else
|
||||||
|
extractToFolder("MISC", mountPoint, entry);
|
||||||
|
|
||||||
|
fileStream.seek(currentPosition); // restore filestream position
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -112,6 +133,9 @@ void CArchiveLoader::initVIDArchive(const std::string &mountPoint, CFileInputStr
|
|||||||
auto it = offsets.find(entry.second.offset);
|
auto it = offsets.find(entry.second.offset);
|
||||||
it++;
|
it++;
|
||||||
entry.second.fullSize = *it - entry.second.offset;
|
entry.second.fullSize = *it - entry.second.offset;
|
||||||
|
|
||||||
|
if(extractArchives)
|
||||||
|
extractToFolder("VIDEO", fileStream, entry.second);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -139,6 +163,9 @@ void CArchiveLoader::initSNDArchive(const std::string &mountPoint, CFileInputStr
|
|||||||
entry.fullSize = reader.readInt32();
|
entry.fullSize = reader.readInt32();
|
||||||
entry.compressedSize = 0;
|
entry.compressedSize = 0;
|
||||||
entries[ResourceID(mountPoint + entry.name)] = entry;
|
entries[ResourceID(mountPoint + entry.name)] = entry;
|
||||||
|
|
||||||
|
if(extractArchives)
|
||||||
|
extractToFolder("SOUND", fileStream, entry);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -182,4 +209,39 @@ std::unordered_set<ResourceID> CArchiveLoader::getFilteredFiles(std::function<bo
|
|||||||
return foundID;
|
return foundID;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CArchiveLoader::extractToFolder(const std::string & outputSubFolder, CInputStream & fileStream, ArchiveEntry entry)
|
||||||
|
{
|
||||||
|
si64 currentPosition = fileStream.tell(); // save filestream position
|
||||||
|
|
||||||
|
std::vector<ui8> data(entry.fullSize);
|
||||||
|
fileStream.seek(entry.offset);
|
||||||
|
fileStream.read(data.data(), entry.fullSize);
|
||||||
|
|
||||||
|
bfs::path extractedFilePath = createExtractedFilePath(outputSubFolder, entry.name);
|
||||||
|
|
||||||
|
// writeToOutputFile
|
||||||
|
std::ofstream out(extractedFilePath.string(), std::ofstream::binary);
|
||||||
|
out.exceptions(std::ifstream::failbit | std::ifstream::badbit);
|
||||||
|
out.write((char*)data.data(), entry.fullSize);
|
||||||
|
|
||||||
|
fileStream.seek(currentPosition); // restore filestream position
|
||||||
|
}
|
||||||
|
|
||||||
|
void CArchiveLoader::extractToFolder(const std::string & outputSubFolder, const std::string & mountPoint, ArchiveEntry entry)
|
||||||
|
{
|
||||||
|
std::unique_ptr<CInputStream> inputStream = load(ResourceID(mountPoint + entry.name));
|
||||||
|
|
||||||
|
extractToFolder(outputSubFolder, *inputStream, entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
bfs::path createExtractedFilePath(const std::string & outputSubFolder, const std::string & entryName)
|
||||||
|
{
|
||||||
|
bfs::path extractionFolderPath = VCMIDirs::get().userExtractedPath() / outputSubFolder;
|
||||||
|
bfs::path extractedFilePath = extractionFolderPath / entryName;
|
||||||
|
|
||||||
|
bfs::create_directories(extractionFolderPath);
|
||||||
|
|
||||||
|
return extractedFilePath;
|
||||||
|
}
|
||||||
|
|
||||||
VCMI_LIB_NAMESPACE_END
|
VCMI_LIB_NAMESPACE_END
|
||||||
|
@ -12,6 +12,8 @@
|
|||||||
#include "ISimpleResourceLoader.h"
|
#include "ISimpleResourceLoader.h"
|
||||||
#include "ResourceID.h"
|
#include "ResourceID.h"
|
||||||
|
|
||||||
|
namespace bfs = boost::filesystem;
|
||||||
|
|
||||||
VCMI_LIB_NAMESPACE_BEGIN
|
VCMI_LIB_NAMESPACE_BEGIN
|
||||||
|
|
||||||
class CFileInputStream;
|
class CFileInputStream;
|
||||||
@ -52,10 +54,11 @@ public:
|
|||||||
* These are valid extensions: .LOD, .SND, .VID
|
* These are valid extensions: .LOD, .SND, .VID
|
||||||
*
|
*
|
||||||
* @param archive Specifies the file path to the archive which should be indexed and loaded.
|
* @param archive Specifies the file path to the archive which should be indexed and loaded.
|
||||||
|
* @param extractArchives Specifies if the original H3 archives should be extracted to a separate folder.
|
||||||
*
|
*
|
||||||
* @throws std::runtime_error if the archive wasn't found or if the archive isn't supported
|
* @throws std::runtime_error if the archive wasn't found or if the archive isn't supported
|
||||||
*/
|
*/
|
||||||
CArchiveLoader(std::string mountPoint, boost::filesystem::path archive);
|
CArchiveLoader(std::string mountPoint, bfs::path archive, bool extractArchives = false);
|
||||||
|
|
||||||
/// Interface implementation
|
/// Interface implementation
|
||||||
/// @see ISimpleResourceLoader
|
/// @see ISimpleResourceLoader
|
||||||
@ -64,6 +67,10 @@ public:
|
|||||||
std::string getMountPoint() const override;
|
std::string getMountPoint() const override;
|
||||||
void updateFilteredFiles(std::function<bool(const std::string &)> filter) const override {}
|
void updateFilteredFiles(std::function<bool(const std::string &)> filter) const override {}
|
||||||
std::unordered_set<ResourceID> getFilteredFiles(std::function<bool(const ResourceID &)> filter) const override;
|
std::unordered_set<ResourceID> getFilteredFiles(std::function<bool(const ResourceID &)> filter) const override;
|
||||||
|
/** Extracts one archive entry to the specified subfolder. Used for Video and Sound */
|
||||||
|
void extractToFolder(const std::string & outputSubFolder, CInputStream & fileStream, ArchiveEntry entry);
|
||||||
|
/** Extracts one archive entry to the specified subfolder. Used for Images, Sprites, etc */
|
||||||
|
void extractToFolder(const std::string & outputSubFolder, const std::string & mountPoint, ArchiveEntry entry);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/**
|
/**
|
||||||
@ -88,12 +95,18 @@ private:
|
|||||||
void initSNDArchive(const std::string &mountPoint, CFileInputStream & fileStream);
|
void initSNDArchive(const std::string &mountPoint, CFileInputStream & fileStream);
|
||||||
|
|
||||||
/** The file path to the archive which is scanned and indexed. */
|
/** The file path to the archive which is scanned and indexed. */
|
||||||
boost::filesystem::path archive;
|
bfs::path archive;
|
||||||
|
|
||||||
std::string mountPoint;
|
std::string mountPoint;
|
||||||
|
|
||||||
/** Holds all entries of the archive file. An entry can be accessed via the entry name. **/
|
/** Holds all entries of the archive file. An entry can be accessed via the entry name. **/
|
||||||
std::unordered_map<ResourceID, ArchiveEntry> entries;
|
std::unordered_map<ResourceID, ArchiveEntry> entries;
|
||||||
|
|
||||||
|
/** Specifies if Original H3 archives should be extracted to a separate folder **/
|
||||||
|
bool extractArchives;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/** Constructs the file path for the extracted file. Creates the subfolder hierarchy aswell **/
|
||||||
|
bfs::path createExtractedFilePath(const std::string & outputSubFolder, const std::string & entryName);
|
||||||
|
|
||||||
VCMI_LIB_NAMESPACE_END
|
VCMI_LIB_NAMESPACE_END
|
||||||
|
@ -26,9 +26,10 @@ VCMI_LIB_NAMESPACE_BEGIN
|
|||||||
std::map<std::string, ISimpleResourceLoader*> CResourceHandler::knownLoaders = std::map<std::string, ISimpleResourceLoader*>();
|
std::map<std::string, ISimpleResourceLoader*> CResourceHandler::knownLoaders = std::map<std::string, ISimpleResourceLoader*>();
|
||||||
CResourceHandler CResourceHandler::globalResourceHandler;
|
CResourceHandler CResourceHandler::globalResourceHandler;
|
||||||
|
|
||||||
CFilesystemGenerator::CFilesystemGenerator(std::string prefix):
|
CFilesystemGenerator::CFilesystemGenerator(std::string prefix, bool extractArchives):
|
||||||
filesystem(new CFilesystemList()),
|
filesystem(new CFilesystemList()),
|
||||||
prefix(prefix)
|
prefix(prefix),
|
||||||
|
extractArchives(extractArchives)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -105,7 +106,7 @@ void CFilesystemGenerator::loadArchive(const std::string &mountPoint, const Json
|
|||||||
std::string URI = prefix + config["path"].String();
|
std::string URI = prefix + config["path"].String();
|
||||||
auto filename = CResourceHandler::get("initial")->getResourceName(ResourceID(URI, archiveType));
|
auto filename = CResourceHandler::get("initial")->getResourceName(ResourceID(URI, archiveType));
|
||||||
if (filename)
|
if (filename)
|
||||||
filesystem->addLoader(new CArchiveLoader(mountPoint, *filename), false);
|
filesystem->addLoader(new CArchiveLoader(mountPoint, *filename, extractArchives), false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CFilesystemGenerator::loadJsonMap(const std::string &mountPoint, const JsonNode & config)
|
void CFilesystemGenerator::loadJsonMap(const std::string &mountPoint, const JsonNode & config)
|
||||||
@ -200,13 +201,13 @@ ISimpleResourceLoader * CResourceHandler::get(std::string identifier)
|
|||||||
return knownLoaders.at(identifier);
|
return knownLoaders.at(identifier);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CResourceHandler::load(const std::string &fsConfigURI)
|
void CResourceHandler::load(const std::string &fsConfigURI, bool extractArchives)
|
||||||
{
|
{
|
||||||
auto fsConfigData = get("initial")->load(ResourceID(fsConfigURI, EResType::TEXT))->readAll();
|
auto fsConfigData = get("initial")->load(ResourceID(fsConfigURI, EResType::TEXT))->readAll();
|
||||||
|
|
||||||
const JsonNode fsConfig((char*)fsConfigData.first.get(), fsConfigData.second);
|
const JsonNode fsConfig((char*)fsConfigData.first.get(), fsConfigData.second);
|
||||||
|
|
||||||
addFilesystem("data", "core", createFileSystem("", fsConfig["filesystem"]));
|
addFilesystem("data", "core", createFileSystem("", fsConfig["filesystem"], extractArchives));
|
||||||
}
|
}
|
||||||
|
|
||||||
void CResourceHandler::addFilesystem(const std::string & parent, const std::string & identifier, ISimpleResourceLoader * loader)
|
void CResourceHandler::addFilesystem(const std::string & parent, const std::string & identifier, ISimpleResourceLoader * loader)
|
||||||
@ -246,9 +247,9 @@ bool CResourceHandler::removeFilesystem(const std::string & parent, const std::s
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
ISimpleResourceLoader * CResourceHandler::createFileSystem(const std::string & prefix, const JsonNode &fsConfig)
|
ISimpleResourceLoader * CResourceHandler::createFileSystem(const std::string & prefix, const JsonNode &fsConfig, bool extractArchives)
|
||||||
{
|
{
|
||||||
CFilesystemGenerator generator(prefix);
|
CFilesystemGenerator generator(prefix, extractArchives);
|
||||||
generator.loadConfig(fsConfig);
|
generator.loadConfig(fsConfig);
|
||||||
return generator.getFilesystem();
|
return generator.getFilesystem();
|
||||||
}
|
}
|
||||||
|
@ -36,7 +36,8 @@ class DLL_LINKAGE CFilesystemGenerator
|
|||||||
TLoadFunctorMap genFunctorMap();
|
TLoadFunctorMap genFunctorMap();
|
||||||
public:
|
public:
|
||||||
/// prefix = prefix that will be given to file entries in all nodes of this filesystem
|
/// prefix = prefix that will be given to file entries in all nodes of this filesystem
|
||||||
CFilesystemGenerator(std::string prefix);
|
/// extractArchives = Specifies if Original H3 archives should be extracted to a separate folder
|
||||||
|
CFilesystemGenerator(std::string prefix, bool extractArchives = false);
|
||||||
|
|
||||||
/// loads configuration from json
|
/// loads configuration from json
|
||||||
/// config - configuration to load, using format of "filesystem" entry in config/filesystem.json
|
/// config - configuration to load, using format of "filesystem" entry in config/filesystem.json
|
||||||
@ -44,6 +45,9 @@ public:
|
|||||||
|
|
||||||
/// returns generated filesystem
|
/// returns generated filesystem
|
||||||
CFilesystemList * getFilesystem();
|
CFilesystemList * getFilesystem();
|
||||||
|
|
||||||
|
/** Specifies if Original H3 archives should be extracted to a separate folder **/
|
||||||
|
bool extractArchives;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -81,7 +85,7 @@ public:
|
|||||||
* Will load all filesystem data from Json data at this path (normally - config/filesystem.json)
|
* Will load all filesystem data from Json data at this path (normally - config/filesystem.json)
|
||||||
* @param fsConfigURI - URI from which data will be loaded
|
* @param fsConfigURI - URI from which data will be loaded
|
||||||
*/
|
*/
|
||||||
static void load(const std::string & fsConfigURI);
|
static void load(const std::string & fsConfigURI, bool extractArchives = false);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief addFilesystem adds filesystem into global resource loader
|
* @brief addFilesystem adds filesystem into global resource loader
|
||||||
@ -104,7 +108,7 @@ public:
|
|||||||
* @param fsConfig - configuration to load
|
* @param fsConfig - configuration to load
|
||||||
* @return generated filesystem that contains all config entries
|
* @return generated filesystem that contains all config entries
|
||||||
*/
|
*/
|
||||||
static ISimpleResourceLoader * createFileSystem(const std::string &prefix, const JsonNode & fsConfig);
|
static ISimpleResourceLoader * createFileSystem(const std::string &prefix, const JsonNode & fsConfig, bool extractArchives = false);
|
||||||
|
|
||||||
~CResourceHandler() = default;
|
~CResourceHandler() = default;
|
||||||
private:
|
private:
|
||||||
|
@ -19,7 +19,7 @@
|
|||||||
#include "../lib/filesystem/ISimpleResourceLoader.h"
|
#include "../lib/filesystem/ISimpleResourceLoader.h"
|
||||||
#include "../lib/JsonNode.h"
|
#include "../lib/JsonNode.h"
|
||||||
#include "../lib/CRandomGenerator.h"
|
#include "../lib/CRandomGenerator.h"
|
||||||
|
#include "../lib/VCMIDirs.h"
|
||||||
|
|
||||||
typedef std::map<size_t, std::vector<JsonNode>> source_map;
|
typedef std::map<size_t, std::vector<JsonNode>> source_map;
|
||||||
|
|
||||||
@ -686,6 +686,39 @@ std::shared_ptr<QImage> Animation::getImage(size_t frame, size_t group, bool ver
|
|||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Animation::exportBitmaps(const QDir & path) const
|
||||||
|
{
|
||||||
|
if(images.empty())
|
||||||
|
{
|
||||||
|
logGlobal->error("Nothing to export, animation is empty");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString actualPath = path.absolutePath() + "/SPRITES/" + QString::fromStdString(name);
|
||||||
|
QDir().mkdir(actualPath);
|
||||||
|
|
||||||
|
size_t counter = 0;
|
||||||
|
|
||||||
|
for(const auto& groupPair : images)
|
||||||
|
{
|
||||||
|
size_t group = groupPair.first;
|
||||||
|
|
||||||
|
for(const auto& imagePair : groupPair.second)
|
||||||
|
{
|
||||||
|
size_t frame = imagePair.first;
|
||||||
|
const auto img = imagePair.second;
|
||||||
|
|
||||||
|
QString filename = QString("%1_%2_%3.png").arg(QString::fromStdString(name)).arg(group).arg(frame);
|
||||||
|
QString filePath = actualPath + "/" + filename;
|
||||||
|
img->save(filePath, "PNG");
|
||||||
|
|
||||||
|
counter++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
logGlobal->info("Exported %d frames to %s", counter, actualPath.toStdString());
|
||||||
|
}
|
||||||
|
|
||||||
void Animation::load()
|
void Animation::load()
|
||||||
{
|
{
|
||||||
for (auto & elem : source)
|
for (auto & elem : source)
|
||||||
@ -774,4 +807,4 @@ void Animation::createFlippedGroup(const size_t sourceGroup, const size_t target
|
|||||||
auto image = getImage(frame, targetGroup);
|
auto image = getImage(frame, targetGroup);
|
||||||
*image = image->transformed(QTransform::fromScale(1, -1));
|
*image = image->transformed(QTransform::fromScale(1, -1));
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -83,6 +83,8 @@ public:
|
|||||||
void load (size_t frame, size_t group = 0);
|
void load (size_t frame, size_t group = 0);
|
||||||
void unload(size_t frame, size_t group = 0);
|
void unload(size_t frame, size_t group = 0);
|
||||||
|
|
||||||
|
void exportBitmaps(const QDir & path) const;
|
||||||
|
|
||||||
//total count of frames in group (including not loaded)
|
//total count of frames in group (including not loaded)
|
||||||
size_t size(size_t group = 0) const;
|
size_t size(size_t group = 0) const;
|
||||||
|
|
||||||
|
@ -24,6 +24,7 @@ set(editor_SRCS
|
|||||||
inspector/messagewidget.cpp
|
inspector/messagewidget.cpp
|
||||||
inspector/rewardswidget.cpp
|
inspector/rewardswidget.cpp
|
||||||
inspector/questwidget.cpp
|
inspector/questwidget.cpp
|
||||||
|
resourceExtractor/ResourceConverter.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
set(editor_HEADERS
|
set(editor_HEADERS
|
||||||
@ -51,6 +52,7 @@ set(editor_HEADERS
|
|||||||
inspector/messagewidget.h
|
inspector/messagewidget.h
|
||||||
inspector/rewardswidget.h
|
inspector/rewardswidget.h
|
||||||
inspector/questwidget.h
|
inspector/questwidget.h
|
||||||
|
resourceExtractor/ResourceConverter.h
|
||||||
)
|
)
|
||||||
|
|
||||||
set(editor_FORMS
|
set(editor_FORMS
|
||||||
|
@ -88,7 +88,34 @@ void MainWindow::saveUserSettings()
|
|||||||
s.setValue(mainWindowPositionSetting, pos());
|
s.setValue(mainWindowPositionSetting, pos());
|
||||||
}
|
}
|
||||||
|
|
||||||
MainWindow::MainWindow(QWidget *parent) :
|
void MainWindow::parseCommandLine(ExtractionOptions & extractionOptions)
|
||||||
|
{
|
||||||
|
QCommandLineParser parser;
|
||||||
|
parser.addHelpOption();
|
||||||
|
parser.addPositionalArgument("map", QCoreApplication::translate("main", "Filepath of the map to open."));
|
||||||
|
|
||||||
|
parser.addOptions({
|
||||||
|
{"e", QCoreApplication::translate("main", "Extract original H3 archives into a separate folder.")},
|
||||||
|
{"s", QCoreApplication::translate("main", "From an extracted archive, it Splits TwCrPort, CPRSMALL, FlagPort, ITPA, ITPt, Un32 and Un44 into individual PNG's.")},
|
||||||
|
{"c", QCoreApplication::translate("main", "From an extracted archive, Converts single Images (found in Images folder) from .pcx to png.")},
|
||||||
|
{"d", QCoreApplication::translate("main", "Delete original files, for the ones splitted / converted.")},
|
||||||
|
});
|
||||||
|
|
||||||
|
parser.process(qApp->arguments());
|
||||||
|
|
||||||
|
const QStringList positionalArgs = parser.positionalArguments();
|
||||||
|
|
||||||
|
if(!positionalArgs.isEmpty())
|
||||||
|
mapFilePath = positionalArgs.at(0);
|
||||||
|
|
||||||
|
extractionOptions = {
|
||||||
|
parser.isSet("e"), {
|
||||||
|
parser.isSet("s"),
|
||||||
|
parser.isSet("c"),
|
||||||
|
parser.isSet("d")}};
|
||||||
|
}
|
||||||
|
|
||||||
|
MainWindow::MainWindow(QWidget* parent) :
|
||||||
QMainWindow(parent),
|
QMainWindow(parent),
|
||||||
ui(new Ui::MainWindow),
|
ui(new Ui::MainWindow),
|
||||||
controller(this)
|
controller(this)
|
||||||
@ -96,49 +123,55 @@ MainWindow::MainWindow(QWidget *parent) :
|
|||||||
ui->setupUi(this);
|
ui->setupUi(this);
|
||||||
loadUserSettings(); //For example window size
|
loadUserSettings(); //For example window size
|
||||||
setTitle();
|
setTitle();
|
||||||
|
|
||||||
// Set current working dir to executable folder.
|
// Set current working dir to executable folder.
|
||||||
// This is important on Mac for relative paths to work inside DMG.
|
// This is important on Mac for relative paths to work inside DMG.
|
||||||
QDir::setCurrent(QApplication::applicationDirPath());
|
QDir::setCurrent(QApplication::applicationDirPath());
|
||||||
|
|
||||||
|
ExtractionOptions extractionOptions;
|
||||||
|
parseCommandLine(extractionOptions);
|
||||||
|
|
||||||
//configure logging
|
//configure logging
|
||||||
const boost::filesystem::path logPath = VCMIDirs::get().userLogsPath() / "VCMI_Editor_log.txt";
|
const boost::filesystem::path logPath = VCMIDirs::get().userLogsPath() / "VCMI_Editor_log.txt";
|
||||||
console = new CConsoleHandler();
|
console = new CConsoleHandler();
|
||||||
logConfig = new CBasicLogConfigurator(logPath, console);
|
logConfig = new CBasicLogConfigurator(logPath, console);
|
||||||
logConfig->configureDefault();
|
logConfig->configureDefault();
|
||||||
logGlobal->info("The log file will be saved to %s", logPath);
|
logGlobal->info("The log file will be saved to %s", logPath);
|
||||||
|
|
||||||
//init
|
//init
|
||||||
preinitDLL(::console);
|
preinitDLL(::console, false, extractionOptions.extractArchives);
|
||||||
settings.init();
|
settings.init();
|
||||||
|
|
||||||
// Initialize logging based on settings
|
// Initialize logging based on settings
|
||||||
logConfig->configure();
|
logConfig->configure();
|
||||||
logGlobal->debug("settings = %s", settings.toJsonNode().toJson());
|
logGlobal->debug("settings = %s", settings.toJsonNode().toJson());
|
||||||
|
|
||||||
// Some basic data validation to produce better error messages in cases of incorrect install
|
// Some basic data validation to produce better error messages in cases of incorrect install
|
||||||
auto testFile = [](std::string filename, std::string message) -> bool
|
auto testFile = [](std::string filename, std::string message) -> bool
|
||||||
{
|
{
|
||||||
if (CResourceHandler::get()->existsResource(ResourceID(filename)))
|
if (CResourceHandler::get()->existsResource(ResourceID(filename)))
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
logGlobal->error("Error: %s was not found!", message);
|
logGlobal->error("Error: %s was not found!", message);
|
||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
|
|
||||||
if(!testFile("DATA/HELP.TXT", "Heroes III data") ||
|
if (!testFile("DATA/HELP.TXT", "Heroes III data") ||
|
||||||
!testFile("MODS/VCMI/MOD.JSON", "VCMI data"))
|
!testFile("MODS/VCMI/MOD.JSON", "VCMI data"))
|
||||||
{
|
{
|
||||||
QApplication::quit();
|
QApplication::quit();
|
||||||
}
|
}
|
||||||
|
|
||||||
conf.init();
|
conf.init();
|
||||||
logGlobal->info("Loading settings");
|
logGlobal->info("Loading settings");
|
||||||
|
|
||||||
init();
|
init();
|
||||||
|
|
||||||
graphics = new Graphics(); // should be before curh->init()
|
graphics = new Graphics(); // should be before curh->init()
|
||||||
graphics->load();//must be after Content loading but should be in main thread
|
graphics->load();//must be after Content loading but should be in main thread
|
||||||
|
|
||||||
|
if (extractionOptions.extractArchives)
|
||||||
|
ResourceConverter::convertExtractedResourceFiles(extractionOptions.conversionOptions);
|
||||||
|
|
||||||
ui->mapView->setScene(controller.scene(0));
|
ui->mapView->setScene(controller.scene(0));
|
||||||
ui->mapView->setController(&controller);
|
ui->mapView->setController(&controller);
|
||||||
@ -167,8 +200,8 @@ MainWindow::MainWindow(QWidget *parent) :
|
|||||||
show();
|
show();
|
||||||
|
|
||||||
//Load map from command line
|
//Load map from command line
|
||||||
if(qApp->arguments().size() >= 2)
|
if(!mapFilePath.isEmpty())
|
||||||
openMap(qApp->arguments().at(1));
|
openMap(mapFilePath);
|
||||||
}
|
}
|
||||||
|
|
||||||
MainWindow::~MainWindow()
|
MainWindow::~MainWindow()
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
#include <QStandardItemModel>
|
#include <QStandardItemModel>
|
||||||
#include "mapcontroller.h"
|
#include "mapcontroller.h"
|
||||||
#include "../lib/Terrain.h"
|
#include "../lib/Terrain.h"
|
||||||
|
#include "resourceExtractor/ResourceConverter.h"
|
||||||
|
|
||||||
class CMap;
|
class CMap;
|
||||||
class ObjectBrowser;
|
class ObjectBrowser;
|
||||||
@ -129,6 +129,8 @@ private:
|
|||||||
void loadUserSettings();
|
void loadUserSettings();
|
||||||
void saveUserSettings();
|
void saveUserSettings();
|
||||||
|
|
||||||
|
void parseCommandLine(ExtractionOptions & extractionOptions);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Ui::MainWindow * ui;
|
Ui::MainWindow * ui;
|
||||||
ObjectBrowser * objectBrowser = nullptr;
|
ObjectBrowser * objectBrowser = nullptr;
|
||||||
@ -142,4 +144,7 @@ private:
|
|||||||
int mapLevel = 0;
|
int mapLevel = 0;
|
||||||
|
|
||||||
std::set<int> catalog;
|
std::set<int> catalog;
|
||||||
|
|
||||||
|
// command line options
|
||||||
|
QString mapFilePath; // FilePath to the H3 or VCMI map to open
|
||||||
};
|
};
|
||||||
|
91
mapeditor/resourceExtractor/ResourceConverter.cpp
Normal file
91
mapeditor/resourceExtractor/ResourceConverter.cpp
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
/*
|
||||||
|
* ResourceConverter.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 "ResourceConverter.h"
|
||||||
|
|
||||||
|
#include "../lib/JsonNode.h"
|
||||||
|
#include "../lib/VCMIDirs.h"
|
||||||
|
#include "../lib/filesystem/Filesystem.h"
|
||||||
|
|
||||||
|
#include "BitmapHandler.h"
|
||||||
|
#include "Animation.h"
|
||||||
|
|
||||||
|
#include "boost/filesystem/path.hpp"
|
||||||
|
#include "boost/locale.hpp"
|
||||||
|
|
||||||
|
void ResourceConverter::convertExtractedResourceFiles(ConversionOptions conversionOptions)
|
||||||
|
{
|
||||||
|
if (conversionOptions.splitDefs)
|
||||||
|
splitDefFiles(conversionOptions.deleteOriginals);
|
||||||
|
|
||||||
|
if (conversionOptions.convertPcxToPng)
|
||||||
|
doConvertPcxToPng(conversionOptions.deleteOriginals);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ResourceConverter::doConvertPcxToPng(bool deleteOriginals)
|
||||||
|
{
|
||||||
|
std::string filename;
|
||||||
|
|
||||||
|
bfs::path imagesPath = VCMIDirs::get().userExtractedPath() / "IMAGES";
|
||||||
|
bfs::directory_iterator end_iter;
|
||||||
|
|
||||||
|
for(bfs::directory_iterator dir_itr(imagesPath); dir_itr != end_iter; ++dir_itr)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (!bfs::is_regular_file(dir_itr->status()))
|
||||||
|
return;
|
||||||
|
|
||||||
|
std::string filePath = dir_itr->path().string();
|
||||||
|
std::string fileStem = dir_itr->path().stem().string();
|
||||||
|
filename = dir_itr->path().filename().string();
|
||||||
|
std::string filenameLowerCase = boost::locale::to_lower(filename);
|
||||||
|
|
||||||
|
if(bfs::extension(filenameLowerCase) == ".pcx")
|
||||||
|
{
|
||||||
|
auto img = BitmapHandler::loadBitmap(filenameLowerCase);
|
||||||
|
bfs::path pngFilePath = imagesPath / (fileStem + ".png");
|
||||||
|
img.save(pathToQString(pngFilePath), "PNG");
|
||||||
|
|
||||||
|
if(deleteOriginals)
|
||||||
|
bfs::remove(filePath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch(const std::exception & ex)
|
||||||
|
{
|
||||||
|
logGlobal->info(filename + " " + ex.what() + "\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ResourceConverter::splitDefFile(const std::string & fileName, const bfs::path & spritesPath, bool deleteOriginals)
|
||||||
|
{
|
||||||
|
if(CResourceHandler::get()->existsResource(ResourceID("SPRITES/" + fileName)))
|
||||||
|
{
|
||||||
|
std::unique_ptr<Animation> anim = make_unique<Animation>(fileName);
|
||||||
|
anim->preload();
|
||||||
|
anim->exportBitmaps(pathToQString(VCMIDirs::get().userExtractedPath()));
|
||||||
|
|
||||||
|
if(deleteOriginals)
|
||||||
|
bfs::remove(spritesPath / fileName);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
logGlobal->error("Def File Split error! " + fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ResourceConverter::splitDefFiles(bool deleteOriginals)
|
||||||
|
{
|
||||||
|
bfs::path spritesPath = VCMIDirs::get().userExtractedPath() / "SPRITES";
|
||||||
|
|
||||||
|
for(std::string defFilename : {"TwCrPort.def", "CPRSMALL.def", "FlagPort.def", "ITPA.def", "ITPt.def", "Un32.def", "Un44.def"})
|
||||||
|
splitDefFile(defFilename, spritesPath, deleteOriginals);
|
||||||
|
}
|
49
mapeditor/resourceExtractor/ResourceConverter.h
Normal file
49
mapeditor/resourceExtractor/ResourceConverter.h
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
/*
|
||||||
|
* ResourceConverter.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
|
||||||
|
|
||||||
|
namespace bfs = boost::filesystem;
|
||||||
|
|
||||||
|
// Struct for holding all Convertor Options
|
||||||
|
struct ConversionOptions
|
||||||
|
{
|
||||||
|
bool splitDefs = false; // splits TwCrPort, CPRSMALL, FlagPort, ITPA, ITPt, Un32 and Un44 into individual PNG's
|
||||||
|
bool convertPcxToPng = false; // converts single Images (found in Images folder) from .pcx to png.
|
||||||
|
bool deleteOriginals = false; // delete original files, for the ones splitted / converted.
|
||||||
|
};
|
||||||
|
|
||||||
|
// Struct for holding all Resource Extractor / Converter options
|
||||||
|
struct ExtractionOptions
|
||||||
|
{
|
||||||
|
bool extractArchives = false; // if set, original H3 archives will be extracted into a separate folder
|
||||||
|
ConversionOptions conversionOptions;
|
||||||
|
};
|
||||||
|
|
||||||
|
class ResourceConverter
|
||||||
|
{
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
// Splits def files that are shared between factions and converts pcx to bmp depending on Extraction Options
|
||||||
|
static void convertExtractedResourceFiles(ConversionOptions conversionOptions);
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
// converts all pcx files from /Images into PNG
|
||||||
|
static void doConvertPcxToPng(bool deleteOriginals);
|
||||||
|
|
||||||
|
// splits a def file into individual parts and converts the output to PNG format
|
||||||
|
static void splitDefFile(const std::string& fileName, const bfs::path& spritesPath, bool deleteOriginals);
|
||||||
|
|
||||||
|
// splits def files (TwCrPort, CPRSMALL, FlagPort, ITPA, ITPt, Un32 and Un44) so that faction resources are independent
|
||||||
|
// (town creature portraits, hero army creature portraits, adventure map dwellings, small town icons, big town icons,
|
||||||
|
// hero speciality small icons, hero speciality large icons)
|
||||||
|
static void splitDefFiles(bool deleteOriginals);
|
||||||
|
};
|
@ -170,6 +170,10 @@ void CVCMIServer::run()
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
startAsyncAccept();
|
startAsyncAccept();
|
||||||
|
if(!remoteConnectionsThread && cmdLineOptions.count("lobby"))
|
||||||
|
{
|
||||||
|
remoteConnectionsThread = vstd::make_unique<boost::thread>(&CVCMIServer::establishRemoteConnections, this);
|
||||||
|
}
|
||||||
|
|
||||||
#if defined(VCMI_ANDROID)
|
#if defined(VCMI_ANDROID)
|
||||||
CAndroidVMHelper vmHelper;
|
CAndroidVMHelper vmHelper;
|
||||||
@ -195,6 +199,38 @@ void CVCMIServer::run()
|
|||||||
boost::this_thread::sleep(boost::posix_time::milliseconds(50));
|
boost::this_thread::sleep(boost::posix_time::milliseconds(50));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CVCMIServer::establishRemoteConnections()
|
||||||
|
{
|
||||||
|
uuid = cmdLineOptions["lobby-uuid"].as<std::string>();
|
||||||
|
int numOfConnections = cmdLineOptions["connections"].as<ui16>();
|
||||||
|
auto address = cmdLineOptions["lobby"].as<std::string>();
|
||||||
|
int port = cmdLineOptions["lobby-port"].as<ui16>();
|
||||||
|
logGlobal->info("Server is connecting to remote at %s:%d with uuid %s %d times", address, port, uuid, numOfConnections);
|
||||||
|
|
||||||
|
for(int i = 0; i < numOfConnections; ++i)
|
||||||
|
connectToRemote(address, port);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CVCMIServer::connectToRemote(const std::string & addr, int port)
|
||||||
|
{
|
||||||
|
std::shared_ptr<CConnection> c;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
logNetwork->info("Establishing connection...");
|
||||||
|
c = std::make_shared<CConnection>(addr, port, SERVER_NAME, uuid);
|
||||||
|
}
|
||||||
|
catch(...)
|
||||||
|
{
|
||||||
|
logNetwork->error("\nCannot establish remote connection!");
|
||||||
|
}
|
||||||
|
|
||||||
|
if(c)
|
||||||
|
{
|
||||||
|
connections.insert(c);
|
||||||
|
c->handler = std::make_shared<boost::thread>(&CVCMIServer::threadHandleClient, this, c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void CVCMIServer::threadAnnounceLobby()
|
void CVCMIServer::threadAnnounceLobby()
|
||||||
{
|
{
|
||||||
while(state != EServerState::SHUTDOWN)
|
while(state != EServerState::SHUTDOWN)
|
||||||
@ -952,10 +988,6 @@ void handleLinuxSignal(int sig)
|
|||||||
static void handleCommandOptions(int argc, char * argv[], boost::program_options::variables_map & options)
|
static void handleCommandOptions(int argc, char * argv[], boost::program_options::variables_map & options)
|
||||||
{
|
{
|
||||||
namespace po = boost::program_options;
|
namespace po = boost::program_options;
|
||||||
#ifdef SINGLE_PROCESS_APP
|
|
||||||
options.emplace("run-by-client", po::variable_value{true, true});
|
|
||||||
options.emplace("uuid", po::variable_value{std::string{argv[1]}, true});
|
|
||||||
#else
|
|
||||||
po::options_description opts("Allowed options");
|
po::options_description opts("Allowed options");
|
||||||
opts.add_options()
|
opts.add_options()
|
||||||
("help,h", "display help and exit")
|
("help,h", "display help and exit")
|
||||||
@ -964,7 +996,11 @@ static void handleCommandOptions(int argc, char * argv[], boost::program_options
|
|||||||
("uuid", po::value<std::string>(), "")
|
("uuid", po::value<std::string>(), "")
|
||||||
("enable-shm-uuid", "use UUID for shared memory identifier")
|
("enable-shm-uuid", "use UUID for shared memory identifier")
|
||||||
("enable-shm", "enable usage of shared memory")
|
("enable-shm", "enable usage of shared memory")
|
||||||
("port", po::value<ui16>(), "port at which server will listen to connections from client");
|
("port", po::value<ui16>(), "port at which server will listen to connections from client")
|
||||||
|
("lobby", po::value<std::string>(), "address to remote lobby")
|
||||||
|
("lobby-port", po::value<ui16>(), "port at which server connect to remote lobby")
|
||||||
|
("lobby-uuid", po::value<std::string>(), "")
|
||||||
|
("connections", po::value<ui16>(), "amount of connections to remote lobby");
|
||||||
|
|
||||||
if(argc > 1)
|
if(argc > 1)
|
||||||
{
|
{
|
||||||
@ -977,7 +1013,6 @@ static void handleCommandOptions(int argc, char * argv[], boost::program_options
|
|||||||
std::cerr << "Failure during parsing command-line options:\n" << e.what() << std::endl;
|
std::cerr << "Failure during parsing command-line options:\n" << e.what() << std::endl;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
po::notify(options);
|
po::notify(options);
|
||||||
|
|
||||||
@ -1079,19 +1114,21 @@ int main(int argc, char * argv[])
|
|||||||
}
|
}
|
||||||
|
|
||||||
#ifdef VCMI_ANDROID
|
#ifdef VCMI_ANDROID
|
||||||
void CVCMIServer::create()
|
void CVCMIServer::create(const std::vector<std::string> & args)
|
||||||
{
|
{
|
||||||
const char * foo[1] = {"android-server"};
|
const char * foo = "android-server";
|
||||||
|
std::vector<const void *> argv = {foo};
|
||||||
|
for(auto & a : args)
|
||||||
|
argv.push_back(a.c_str());
|
||||||
|
|
||||||
main(1, const_cast<char **>(foo));
|
main(argv.size(), const_cast<char **>(foo));
|
||||||
}
|
}
|
||||||
#elif defined(SINGLE_PROCESS_APP)
|
#elif defined(SINGLE_PROCESS_APP)
|
||||||
void CVCMIServer::create(boost::condition_variable * cond, const std::string & uuid)
|
void CVCMIServer::create(boost::condition_variable * cond, const std::vector<std::string> & args)
|
||||||
{
|
{
|
||||||
const std::initializer_list<const void *> argv = {
|
std::vector<const void *> argv = {cond};
|
||||||
cond,
|
for(auto & a : args)
|
||||||
uuid.c_str(),
|
argv.push_back(a.c_str());
|
||||||
};
|
main(argv.size(), reinterpret_cast<char **>(const_cast<void **>(&*argv.begin())));
|
||||||
main(argv.size(), reinterpret_cast<char **>(const_cast<void **>(argv.begin())));
|
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
@ -52,7 +52,7 @@ class CVCMIServer : public LobbyInfo
|
|||||||
std::list<std::unique_ptr<CPackForLobby>> announceQueue;
|
std::list<std::unique_ptr<CPackForLobby>> announceQueue;
|
||||||
boost::recursive_mutex mx;
|
boost::recursive_mutex mx;
|
||||||
std::shared_ptr<CApplier<CBaseForServerApply>> applier;
|
std::shared_ptr<CApplier<CBaseForServerApply>> applier;
|
||||||
std::unique_ptr<boost::thread> announceLobbyThread;
|
std::unique_ptr<boost::thread> announceLobbyThread, remoteConnectionsThread;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
std::shared_ptr<CGameHandler> gh;
|
std::shared_ptr<CGameHandler> gh;
|
||||||
@ -74,6 +74,8 @@ public:
|
|||||||
void prepareToRestart();
|
void prepareToRestart();
|
||||||
void startGameImmidiately();
|
void startGameImmidiately();
|
||||||
|
|
||||||
|
void establishRemoteConnections();
|
||||||
|
void connectToRemote(const std::string & addr, int port);
|
||||||
void startAsyncAccept();
|
void startAsyncAccept();
|
||||||
void connectionAccepted(const boost::system::error_code & ec);
|
void connectionAccepted(const boost::system::error_code & ec);
|
||||||
void threadHandleClient(std::shared_ptr<CConnection> c);
|
void threadHandleClient(std::shared_ptr<CConnection> c);
|
||||||
@ -112,8 +114,8 @@ public:
|
|||||||
ui8 getIdOfFirstUnallocatedPlayer() const;
|
ui8 getIdOfFirstUnallocatedPlayer() const;
|
||||||
|
|
||||||
#ifdef VCMI_ANDROID
|
#ifdef VCMI_ANDROID
|
||||||
static void create();
|
static void create(const std::vector<std::string> & args);
|
||||||
#elif defined(SINGLE_PROCESS_APP)
|
#elif defined(SINGLE_PROCESS_APP)
|
||||||
static void create(boost::condition_variable * cond, const std::string & uuid);
|
static void create(boost::condition_variable * cond, const std::vector<std::string> & args);
|
||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
|
Loading…
x
Reference in New Issue
Block a user