mirror of
https://github.com/vcmi/vcmi.git
synced 2025-06-23 00:28:08 +02:00
11
ChangeLog.md
11
ChangeLog.md
@ -1,3 +1,14 @@
|
|||||||
|
# 1.5.0 -> 1.5.1
|
||||||
|
|
||||||
|
### Stability
|
||||||
|
* Fixed possible crash on accessing faction description
|
||||||
|
* Fixed possible thread race on leaving to main menu
|
||||||
|
* Game will now show error message instead of silent crash on corrupted H3 data
|
||||||
|
* Fixed possible crash on double-deletion of quest artifacts placed by RMG
|
||||||
|
|
||||||
|
### Interface
|
||||||
|
* Fixed possible freeze on attempt to move hero when hero has non-zero movement points but not enough to reach first tile in path
|
||||||
|
|
||||||
# 1.4.5 -> 1.5.0
|
# 1.4.5 -> 1.5.0
|
||||||
|
|
||||||
### General
|
### General
|
||||||
|
@ -10,7 +10,7 @@ android {
|
|||||||
applicationId "is.xyz.vcmi"
|
applicationId "is.xyz.vcmi"
|
||||||
minSdk 19
|
minSdk 19
|
||||||
targetSdk 33
|
targetSdk 33
|
||||||
versionCode 1510
|
versionCode 1511
|
||||||
versionName "1.5.1"
|
versionName "1.5.1"
|
||||||
setProperty("archivesBaseName", "vcmi")
|
setProperty("archivesBaseName", "vcmi")
|
||||||
}
|
}
|
||||||
|
@ -31,6 +31,7 @@
|
|||||||
#include "../lib/CConfigHandler.h"
|
#include "../lib/CConfigHandler.h"
|
||||||
#include "../lib/CGeneralTextHandler.h"
|
#include "../lib/CGeneralTextHandler.h"
|
||||||
#include "../lib/CThreadHelper.h"
|
#include "../lib/CThreadHelper.h"
|
||||||
|
#include "../lib/ExceptionsCommon.h"
|
||||||
#include "../lib/VCMIDirs.h"
|
#include "../lib/VCMIDirs.h"
|
||||||
#include "../lib/VCMI_Lib.h"
|
#include "../lib/VCMI_Lib.h"
|
||||||
#include "../lib/filesystem/Filesystem.h"
|
#include "../lib/filesystem/Filesystem.h"
|
||||||
@ -56,6 +57,7 @@ namespace po = boost::program_options;
|
|||||||
namespace po_style = boost::program_options::command_line_style;
|
namespace po_style = boost::program_options::command_line_style;
|
||||||
|
|
||||||
static std::atomic<bool> headlessQuit = false;
|
static std::atomic<bool> headlessQuit = false;
|
||||||
|
static std::optional<std::string> criticalInitializationError;
|
||||||
|
|
||||||
#ifndef VCMI_IOS
|
#ifndef VCMI_IOS
|
||||||
void processCommand(const std::string &message);
|
void processCommand(const std::string &message);
|
||||||
@ -69,9 +71,16 @@ static CBasicLogConfigurator *logConfig;
|
|||||||
void init()
|
void init()
|
||||||
{
|
{
|
||||||
CStopWatch tmh;
|
CStopWatch tmh;
|
||||||
|
try
|
||||||
|
{
|
||||||
loadDLLClasses();
|
loadDLLClasses();
|
||||||
CGI->setFromLib();
|
CGI->setFromLib();
|
||||||
|
}
|
||||||
|
catch (const DataLoadingException & e)
|
||||||
|
{
|
||||||
|
criticalInitializationError = e.what();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
logGlobal->info("Initializing VCMI_Lib: %d ms", tmh.getDiff());
|
logGlobal->info("Initializing VCMI_Lib: %d ms", tmh.getDiff());
|
||||||
|
|
||||||
@ -322,6 +331,11 @@ int main(int argc, char * argv[])
|
|||||||
#endif // ANDROID
|
#endif // ANDROID
|
||||||
#endif // THREADED
|
#endif // THREADED
|
||||||
|
|
||||||
|
if (criticalInitializationError.has_value())
|
||||||
|
{
|
||||||
|
handleFatalError(criticalInitializationError.value(), false);
|
||||||
|
}
|
||||||
|
|
||||||
if(!settings["session"]["headless"].Bool())
|
if(!settings["session"]["headless"].Bool())
|
||||||
{
|
{
|
||||||
pomtime.getDiff();
|
pomtime.getDiff();
|
||||||
@ -412,7 +426,7 @@ static void mainLoop()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[[noreturn]] static void quitApplicationImmediately()
|
[[noreturn]] static void quitApplicationImmediately(int error_code)
|
||||||
{
|
{
|
||||||
// Perform quick exit without executing static destructors and let OS cleanup anything that we did not
|
// Perform quick exit without executing static destructors and let OS cleanup anything that we did not
|
||||||
// We generally don't care about them and this leads to numerous issues, e.g.
|
// We generally don't care about them and this leads to numerous issues, e.g.
|
||||||
@ -420,9 +434,9 @@ static void mainLoop()
|
|||||||
// Android - std::quick_exit is available only starting from API level 21
|
// Android - std::quick_exit is available only starting from API level 21
|
||||||
// Mingw, macOS and iOS - std::quick_exit is unavailable (at least in current version of CI)
|
// Mingw, macOS and iOS - std::quick_exit is unavailable (at least in current version of CI)
|
||||||
#if (defined(__ANDROID_API__) && __ANDROID_API__ < 21) || (defined(__MINGW32__)) || defined(VCMI_APPLE)
|
#if (defined(__ANDROID_API__) && __ANDROID_API__ < 21) || (defined(__MINGW32__)) || defined(VCMI_APPLE)
|
||||||
::exit(0);
|
::exit(error_code);
|
||||||
#else
|
#else
|
||||||
std::quick_exit(0);
|
std::quick_exit(error_code);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -476,7 +490,7 @@ static void mainLoop()
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::cout << "Ending...\n";
|
std::cout << "Ending...\n";
|
||||||
quitApplicationImmediately();
|
quitApplicationImmediately(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void handleQuit(bool ask)
|
void handleQuit(bool ask)
|
||||||
@ -500,7 +514,7 @@ void handleQuit(bool ask)
|
|||||||
// proper solution would be to abort init thread (or wait for it to finish)
|
// proper solution would be to abort init thread (or wait for it to finish)
|
||||||
if (!CCS->curh)
|
if (!CCS->curh)
|
||||||
{
|
{
|
||||||
quitApplicationImmediately();
|
quitApplicationImmediately(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (LOCPLINT)
|
if (LOCPLINT)
|
||||||
@ -521,5 +535,5 @@ void handleFatalError(const std::string & message, bool terminate)
|
|||||||
if (terminate)
|
if (terminate)
|
||||||
throw std::runtime_error(message);
|
throw std::runtime_error(message);
|
||||||
else
|
else
|
||||||
exit(1);
|
quitApplicationImmediately(1);
|
||||||
}
|
}
|
||||||
|
@ -119,8 +119,11 @@ CServerHandler::~CServerHandler()
|
|||||||
if (serverRunner)
|
if (serverRunner)
|
||||||
serverRunner->wait();
|
serverRunner->wait();
|
||||||
serverRunner.reset();
|
serverRunner.reset();
|
||||||
|
{
|
||||||
|
auto unlockInterface = vstd::makeUnlockGuard(GH.interfaceMutex);
|
||||||
threadNetwork.join();
|
threadNetwork.join();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
catch (const std::runtime_error & e)
|
catch (const std::runtime_error & e)
|
||||||
{
|
{
|
||||||
logGlobal->error("Failed to shut down network thread! Reason: %s", e.what());
|
logGlobal->error("Failed to shut down network thread! Reason: %s", e.what());
|
||||||
@ -421,6 +424,7 @@ void CServerHandler::sendClientDisconnecting()
|
|||||||
networkConnection->close();
|
networkConnection->close();
|
||||||
networkConnection.reset();
|
networkConnection.reset();
|
||||||
logicConnection.reset();
|
logicConnection.reset();
|
||||||
|
waitForServerShutdown();
|
||||||
}
|
}
|
||||||
|
|
||||||
void CServerHandler::setCampaignState(std::shared_ptr<CampaignState> newCampaign)
|
void CServerHandler::setCampaignState(std::shared_ptr<CampaignState> newCampaign)
|
||||||
@ -901,6 +905,8 @@ void CServerHandler::onPacketReceived(const std::shared_ptr<INetworkConnection>
|
|||||||
|
|
||||||
void CServerHandler::onDisconnected(const std::shared_ptr<INetworkConnection> & connection, const std::string & errorMessage)
|
void CServerHandler::onDisconnected(const std::shared_ptr<INetworkConnection> & connection, const std::string & errorMessage)
|
||||||
{
|
{
|
||||||
|
boost::mutex::scoped_lock interfaceLock(GH.interfaceMutex);
|
||||||
|
|
||||||
if (connection != networkConnection)
|
if (connection != networkConnection)
|
||||||
{
|
{
|
||||||
// ServerHandler already closed this connection on its own
|
// ServerHandler already closed this connection on its own
|
||||||
@ -918,8 +924,6 @@ void CServerHandler::onDisconnected(const std::shared_ptr<INetworkConnection> &
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
boost::mutex::scoped_lock interfaceLock(GH.interfaceMutex);
|
|
||||||
|
|
||||||
logNetwork->error("Lost connection to server! Connection has been closed");
|
logNetwork->error("Lost connection to server! Connection has been closed");
|
||||||
|
|
||||||
if(client)
|
if(client)
|
||||||
@ -954,7 +958,6 @@ void CServerHandler::waitForServerShutdown()
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
boost::mutex::scoped_lock interfaceLock(GH.interfaceMutex);
|
|
||||||
if (getState() == EClientState::CONNECTING)
|
if (getState() == EClientState::CONNECTING)
|
||||||
{
|
{
|
||||||
showServerError(CGI->generaltexth->translate("vcmi.server.errors.existingProcess"));
|
showServerError(CGI->generaltexth->translate("vcmi.server.errors.existingProcess"));
|
||||||
|
@ -519,7 +519,10 @@ void CClient::handlePack(CPack * pack)
|
|||||||
{
|
{
|
||||||
apply->applyOnClBefore(this, pack);
|
apply->applyOnClBefore(this, pack);
|
||||||
logNetwork->trace("\tMade first apply on cl: %s", typeid(*pack).name());
|
logNetwork->trace("\tMade first apply on cl: %s", typeid(*pack).name());
|
||||||
|
{
|
||||||
|
boost::unique_lock<boost::shared_mutex> lock(CGameState::mutex);
|
||||||
gs->apply(pack);
|
gs->apply(pack);
|
||||||
|
}
|
||||||
logNetwork->trace("\tApplied on gs: %s", typeid(*pack).name());
|
logNetwork->trace("\tApplied on gs: %s", typeid(*pack).name());
|
||||||
apply->applyOnClAfter(this, pack);
|
apply->applyOnClAfter(this, pack);
|
||||||
logNetwork->trace("\tMade second apply on cl: %s", typeid(*pack).name());
|
logNetwork->trace("\tMade second apply on cl: %s", typeid(*pack).name());
|
||||||
|
@ -456,7 +456,7 @@ bool AdventureMapShortcuts::optionHeroSelected()
|
|||||||
bool AdventureMapShortcuts::optionHeroCanMove()
|
bool AdventureMapShortcuts::optionHeroCanMove()
|
||||||
{
|
{
|
||||||
const auto * hero = LOCPLINT->localState->getCurrentHero();
|
const auto * hero = LOCPLINT->localState->getCurrentHero();
|
||||||
return optionInMapView() && hero && hero->movementPointsRemaining() != 0 && LOCPLINT->localState->hasPath(hero);
|
return optionInMapView() && hero && LOCPLINT->localState->hasPath(hero) && LOCPLINT->localState->getPath(hero).nextNode().turns == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AdventureMapShortcuts::optionHasNextHero()
|
bool AdventureMapShortcuts::optionHasNextHero()
|
||||||
|
@ -334,8 +334,12 @@ CShowableAnim::CShowableAnim(int x, int y, const AnimationPath & name, ui8 Flags
|
|||||||
anim->loadGroup(group);
|
anim->loadGroup(group);
|
||||||
last = anim->size(group);
|
last = anim->size(group);
|
||||||
|
|
||||||
pos.w = anim->getImage(0, group)->width();
|
auto image = anim->getImage(0, group);
|
||||||
pos.h = anim->getImage(0, group)->height();
|
if (!image)
|
||||||
|
throw std::runtime_error("Failed to load group " + std::to_string(group) + " of animation file " + name.getOriginalName());
|
||||||
|
|
||||||
|
pos.w = image->width();
|
||||||
|
pos.h = image->height();
|
||||||
pos.x+= x;
|
pos.x+= x;
|
||||||
pos.y+= y;
|
pos.y+= y;
|
||||||
|
|
||||||
|
@ -466,7 +466,7 @@ void CInteractableTownTooltip::init(const CGTownInstance * town)
|
|||||||
if(town->id == townId && town->builtBuildings.count(BuildingID::TAVERN))
|
if(town->id == townId && town->builtBuildings.count(BuildingID::TAVERN))
|
||||||
LOCPLINT->showTavernWindow(town, nullptr, QueryID::NONE);
|
LOCPLINT->showTavernWindow(town, nullptr, QueryID::NONE);
|
||||||
}
|
}
|
||||||
}, [&]{
|
}, [town]{
|
||||||
if(!town->town->faction->getDescriptionTranslated().empty())
|
if(!town->town->faction->getDescriptionTranslated().empty())
|
||||||
CRClickPopup::createAndPush(town->town->faction->getDescriptionTranslated());
|
CRClickPopup::createAndPush(town->town->faction->getDescriptionTranslated());
|
||||||
});
|
});
|
||||||
|
@ -1351,7 +1351,7 @@ void CCastleInterface::recreateIcons()
|
|||||||
{
|
{
|
||||||
if(town->builtBuildings.count(BuildingID::TAVERN))
|
if(town->builtBuildings.count(BuildingID::TAVERN))
|
||||||
LOCPLINT->showTavernWindow(town, nullptr, QueryID::NONE);
|
LOCPLINT->showTavernWindow(town, nullptr, QueryID::NONE);
|
||||||
}, [&]{
|
}, [this]{
|
||||||
if(!town->town->faction->getDescriptionTranslated().empty())
|
if(!town->town->faction->getDescriptionTranslated().empty())
|
||||||
CRClickPopup::createAndPush(town->town->faction->getDescriptionTranslated());
|
CRClickPopup::createAndPush(town->town->faction->getDescriptionTranslated());
|
||||||
});
|
});
|
||||||
|
@ -12,6 +12,7 @@
|
|||||||
|
|
||||||
#include "ArtifactUtils.h"
|
#include "ArtifactUtils.h"
|
||||||
#include "CGeneralTextHandler.h"
|
#include "CGeneralTextHandler.h"
|
||||||
|
#include "ExceptionsCommon.h"
|
||||||
#include "GameSettings.h"
|
#include "GameSettings.h"
|
||||||
#include "mapObjects/MapObjects.h"
|
#include "mapObjects/MapObjects.h"
|
||||||
#include "constants/StringConstants.h"
|
#include "constants/StringConstants.h"
|
||||||
@ -354,7 +355,13 @@ std::vector<JsonNode> CArtHandler::loadLegacyData()
|
|||||||
artData["slot"].Vector().emplace_back(artSlot);
|
artData["slot"].Vector().emplace_back(artSlot);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
artData["class"].String() = classes.at(parser.readString()[0]);
|
|
||||||
|
std::string artClass = parser.readString();
|
||||||
|
|
||||||
|
if (classes.count(artClass[0]))
|
||||||
|
artData["class"].String() = classes.at(artClass[0]);
|
||||||
|
else
|
||||||
|
throw DataLoadingException("File ARTRAITS.TXT is invalid or corrupted! Please reinstall Heroes III data files");
|
||||||
artData["text"]["description"].String() = parser.readString();
|
artData["text"]["description"].String() = parser.readString();
|
||||||
|
|
||||||
parser.endLine();
|
parser.endLine();
|
||||||
|
@ -660,6 +660,7 @@ set(lib_MAIN_HEADERS
|
|||||||
CStack.h
|
CStack.h
|
||||||
CStopWatch.h
|
CStopWatch.h
|
||||||
CTownHandler.h
|
CTownHandler.h
|
||||||
|
ExceptionsCommon.h
|
||||||
ExtraOptionsInfo.h
|
ExtraOptionsInfo.h
|
||||||
FunctionList.h
|
FunctionList.h
|
||||||
GameCallbackHolder.h
|
GameCallbackHolder.h
|
||||||
|
16
lib/ExceptionsCommon.h
Normal file
16
lib/ExceptionsCommon.h
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
/*
|
||||||
|
* ExceptionsCommon.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
|
||||||
|
|
||||||
|
class DLL_LINKAGE DataLoadingException: public std::runtime_error
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
using std::runtime_error::runtime_error;
|
||||||
|
};
|
@ -144,33 +144,33 @@ DLL_LINKAGE std::string MetaString::toString() const
|
|||||||
switch(elem)
|
switch(elem)
|
||||||
{
|
{
|
||||||
case EMessage::APPEND_RAW_STRING:
|
case EMessage::APPEND_RAW_STRING:
|
||||||
dst += exactStrings[exSt++];
|
dst += exactStrings.at(exSt++);
|
||||||
break;
|
break;
|
||||||
case EMessage::APPEND_LOCAL_STRING:
|
case EMessage::APPEND_LOCAL_STRING:
|
||||||
dst += getLocalString(localStrings[loSt++]);
|
dst += getLocalString(localStrings.at(loSt++));
|
||||||
break;
|
break;
|
||||||
case EMessage::APPEND_TEXTID_STRING:
|
case EMessage::APPEND_TEXTID_STRING:
|
||||||
dst += VLC->generaltexth->translate(stringsTextID[textID++]);
|
dst += VLC->generaltexth->translate(stringsTextID.at(textID++));
|
||||||
break;
|
break;
|
||||||
case EMessage::APPEND_NUMBER:
|
case EMessage::APPEND_NUMBER:
|
||||||
dst += std::to_string(numbers[nums++]);
|
dst += std::to_string(numbers.at(nums++));
|
||||||
break;
|
break;
|
||||||
case EMessage::REPLACE_RAW_STRING:
|
case EMessage::REPLACE_RAW_STRING:
|
||||||
boost::replace_first(dst, "%s", exactStrings[exSt++]);
|
boost::replace_first(dst, "%s", exactStrings.at(exSt++));
|
||||||
break;
|
break;
|
||||||
case EMessage::REPLACE_LOCAL_STRING:
|
case EMessage::REPLACE_LOCAL_STRING:
|
||||||
boost::replace_first(dst, "%s", getLocalString(localStrings[loSt++]));
|
boost::replace_first(dst, "%s", getLocalString(localStrings.at(loSt++)));
|
||||||
break;
|
break;
|
||||||
case EMessage::REPLACE_TEXTID_STRING:
|
case EMessage::REPLACE_TEXTID_STRING:
|
||||||
boost::replace_first(dst, "%s", VLC->generaltexth->translate(stringsTextID[textID++]));
|
boost::replace_first(dst, "%s", VLC->generaltexth->translate(stringsTextID.at(textID++)));
|
||||||
break;
|
break;
|
||||||
case EMessage::REPLACE_NUMBER:
|
case EMessage::REPLACE_NUMBER:
|
||||||
boost::replace_first(dst, "%d", std::to_string(numbers[nums++]));
|
boost::replace_first(dst, "%d", std::to_string(numbers.at(nums++)));
|
||||||
break;
|
break;
|
||||||
case EMessage::REPLACE_POSITIVE_NUMBER:
|
case EMessage::REPLACE_POSITIVE_NUMBER:
|
||||||
if (dst.find("%+d") != std::string::npos)
|
if (dst.find("%+d") != std::string::npos)
|
||||||
{
|
{
|
||||||
int64_t value = numbers[nums];
|
int64_t value = numbers.at(nums);
|
||||||
if (value > 0)
|
if (value > 0)
|
||||||
boost::replace_first(dst, "%+d", '+' + std::to_string(value));
|
boost::replace_first(dst, "%+d", '+' + std::to_string(value));
|
||||||
else
|
else
|
||||||
@ -179,7 +179,7 @@ DLL_LINKAGE std::string MetaString::toString() const
|
|||||||
nums++;
|
nums++;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
boost::replace_first(dst, "%d", std::to_string(numbers[nums++]));
|
boost::replace_first(dst, "%d", std::to_string(numbers.at(nums++)));
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
logGlobal->error("MetaString processing error! Received message of type %d", static_cast<int>(elem));
|
logGlobal->error("MetaString processing error! Received message of type %d", static_cast<int>(elem));
|
||||||
@ -199,41 +199,41 @@ DLL_LINKAGE std::string MetaString::buildList() const
|
|||||||
std::string lista;
|
std::string lista;
|
||||||
for(int i = 0; i < message.size(); ++i)
|
for(int i = 0; i < message.size(); ++i)
|
||||||
{
|
{
|
||||||
if(i > 0 && (message[i] == EMessage::APPEND_RAW_STRING || message[i] == EMessage::APPEND_LOCAL_STRING))
|
if(i > 0 && (message.at(i) == EMessage::APPEND_RAW_STRING || message.at(i) == EMessage::APPEND_LOCAL_STRING))
|
||||||
{
|
{
|
||||||
if(exSt == exactStrings.size() - 1)
|
if(exSt == exactStrings.size() - 1)
|
||||||
lista += VLC->generaltexth->allTexts[141]; //" and "
|
lista += VLC->generaltexth->allTexts[141]; //" and "
|
||||||
else
|
else
|
||||||
lista += ", ";
|
lista += ", ";
|
||||||
}
|
}
|
||||||
switch(message[i])
|
switch(message.at(i))
|
||||||
{
|
{
|
||||||
case EMessage::APPEND_RAW_STRING:
|
case EMessage::APPEND_RAW_STRING:
|
||||||
lista += exactStrings[exSt++];
|
lista += exactStrings.at(exSt++);
|
||||||
break;
|
break;
|
||||||
case EMessage::APPEND_LOCAL_STRING:
|
case EMessage::APPEND_LOCAL_STRING:
|
||||||
lista += getLocalString(localStrings[loSt++]);
|
lista += getLocalString(localStrings.at(loSt++));
|
||||||
break;
|
break;
|
||||||
case EMessage::APPEND_TEXTID_STRING:
|
case EMessage::APPEND_TEXTID_STRING:
|
||||||
lista += VLC->generaltexth->translate(stringsTextID[textID++]);
|
lista += VLC->generaltexth->translate(stringsTextID.at(textID++));
|
||||||
break;
|
break;
|
||||||
case EMessage::APPEND_NUMBER:
|
case EMessage::APPEND_NUMBER:
|
||||||
lista += std::to_string(numbers[nums++]);
|
lista += std::to_string(numbers.at(nums++));
|
||||||
break;
|
break;
|
||||||
case EMessage::REPLACE_RAW_STRING:
|
case EMessage::REPLACE_RAW_STRING:
|
||||||
lista.replace(lista.find("%s"), 2, exactStrings[exSt++]);
|
lista.replace(lista.find("%s"), 2, exactStrings.at(exSt++));
|
||||||
break;
|
break;
|
||||||
case EMessage::REPLACE_LOCAL_STRING:
|
case EMessage::REPLACE_LOCAL_STRING:
|
||||||
lista.replace(lista.find("%s"), 2, getLocalString(localStrings[loSt++]));
|
lista.replace(lista.find("%s"), 2, getLocalString(localStrings.at(loSt++)));
|
||||||
break;
|
break;
|
||||||
case EMessage::REPLACE_TEXTID_STRING:
|
case EMessage::REPLACE_TEXTID_STRING:
|
||||||
lista.replace(lista.find("%s"), 2, VLC->generaltexth->translate(stringsTextID[textID++]));
|
lista.replace(lista.find("%s"), 2, VLC->generaltexth->translate(stringsTextID.at(textID++)));
|
||||||
break;
|
break;
|
||||||
case EMessage::REPLACE_NUMBER:
|
case EMessage::REPLACE_NUMBER:
|
||||||
lista.replace(lista.find("%d"), 2, std::to_string(numbers[nums++]));
|
lista.replace(lista.find("%d"), 2, std::to_string(numbers.at(nums++)));
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
logGlobal->error("MetaString processing error! Received message of type %d", int(message[i]));
|
logGlobal->error("MetaString processing error! Received message of type %d", int(message.at(i)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return lista;
|
return lista;
|
||||||
|
@ -75,8 +75,6 @@ public:
|
|||||||
void applyOnGS(CGameState *gs, CPack * pack) const override
|
void applyOnGS(CGameState *gs, CPack * pack) const override
|
||||||
{
|
{
|
||||||
T *ptr = static_cast<T*>(pack);
|
T *ptr = static_cast<T*>(pack);
|
||||||
|
|
||||||
boost::unique_lock<boost::shared_mutex> lock(CGameState::mutex);
|
|
||||||
ptr->applyGs(gs);
|
ptr->applyGs(gs);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -1101,6 +1099,9 @@ BattleField CGameState::battleGetBattlefieldType(int3 tile, CRandomGenerator & r
|
|||||||
if(map->isCoastalTile(tile)) //coastal tile is always ground
|
if(map->isCoastalTile(tile)) //coastal tile is always ground
|
||||||
return BattleField(*VLC->identifiers()->getIdentifier("core", "battlefield.sand_shore"));
|
return BattleField(*VLC->identifiers()->getIdentifier("core", "battlefield.sand_shore"));
|
||||||
|
|
||||||
|
if (t.terType->battleFields.empty())
|
||||||
|
throw std::runtime_error("Failed to find battlefield for terrain " + t.terType->getJsonKey());
|
||||||
|
|
||||||
return BattleField(*RandomGeneratorUtil::nextItem(t.terType->battleFields, rand));
|
return BattleField(*RandomGeneratorUtil::nextItem(t.terType->battleFields, rand));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -117,7 +117,7 @@ void NetworkConnection::onPacketReceived(const boost::system::error_code & ec, u
|
|||||||
|
|
||||||
if (readBuffer.size() < expectedPacketSize)
|
if (readBuffer.size() < expectedPacketSize)
|
||||||
{
|
{
|
||||||
throw std::runtime_error("Failed to read packet!");
|
throw std::runtime_error("Failed to read packet! " + std::to_string(readBuffer.size()) + " bytes read, but " + std::to_string(expectedPacketSize) + " bytes expected!");
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<std::byte> message(expectedPacketSize);
|
std::vector<std::byte> message(expectedPacketSize);
|
||||||
|
@ -1579,7 +1579,7 @@ void ChangeStackCount::applyGs(CGameState * gs)
|
|||||||
{
|
{
|
||||||
auto * srcObj = gs->getArmyInstance(army);
|
auto * srcObj = gs->getArmyInstance(army);
|
||||||
if(!srcObj)
|
if(!srcObj)
|
||||||
logNetwork->error("[CRITICAL] ChangeStackCount: invalid army object %d, possible game state corruption.", army.getNum());
|
throw std::runtime_error("ChangeStackCount: invalid army object " + std::to_string(army.getNum()) + ", possible game state corruption.");
|
||||||
|
|
||||||
if(absoluteValue)
|
if(absoluteValue)
|
||||||
srcObj->setStackCount(slot, count);
|
srcObj->setStackCount(slot, count);
|
||||||
@ -1591,7 +1591,7 @@ void SetStackType::applyGs(CGameState * gs)
|
|||||||
{
|
{
|
||||||
auto * srcObj = gs->getArmyInstance(army);
|
auto * srcObj = gs->getArmyInstance(army);
|
||||||
if(!srcObj)
|
if(!srcObj)
|
||||||
logNetwork->error("[CRITICAL] SetStackType: invalid army object %d, possible game state corruption.", army.getNum());
|
throw std::runtime_error("SetStackType: invalid army object " + std::to_string(army.getNum()) + ", possible game state corruption.");
|
||||||
|
|
||||||
srcObj->setStackType(slot, type);
|
srcObj->setStackType(slot, type);
|
||||||
}
|
}
|
||||||
@ -1600,7 +1600,7 @@ void EraseStack::applyGs(CGameState * gs)
|
|||||||
{
|
{
|
||||||
auto * srcObj = gs->getArmyInstance(army);
|
auto * srcObj = gs->getArmyInstance(army);
|
||||||
if(!srcObj)
|
if(!srcObj)
|
||||||
logNetwork->error("[CRITICAL] EraseStack: invalid army object %d, possible game state corruption.", army.getNum());
|
throw std::runtime_error("EraseStack: invalid army object " + std::to_string(army.getNum()) + ", possible game state corruption.");
|
||||||
|
|
||||||
srcObj->eraseStack(slot);
|
srcObj->eraseStack(slot);
|
||||||
}
|
}
|
||||||
@ -1609,11 +1609,11 @@ void SwapStacks::applyGs(CGameState * gs)
|
|||||||
{
|
{
|
||||||
auto * srcObj = gs->getArmyInstance(srcArmy);
|
auto * srcObj = gs->getArmyInstance(srcArmy);
|
||||||
if(!srcObj)
|
if(!srcObj)
|
||||||
logNetwork->error("[CRITICAL] SwapStacks: invalid army object %d, possible game state corruption.", srcArmy.getNum());
|
throw std::runtime_error("SwapStacks: invalid army object " + std::to_string(srcArmy.getNum()) + ", possible game state corruption.");
|
||||||
|
|
||||||
auto * dstObj = gs->getArmyInstance(dstArmy);
|
auto * dstObj = gs->getArmyInstance(dstArmy);
|
||||||
if(!dstObj)
|
if(!dstObj)
|
||||||
logNetwork->error("[CRITICAL] SwapStacks: invalid army object %d, possible game state corruption.", dstArmy.getNum());
|
throw std::runtime_error("SwapStacks: invalid army object " + std::to_string(dstArmy.getNum()) + ", possible game state corruption.");
|
||||||
|
|
||||||
CStackInstance * s1 = srcObj->detachStack(srcSlot);
|
CStackInstance * s1 = srcObj->detachStack(srcSlot);
|
||||||
CStackInstance * s2 = dstObj->detachStack(dstSlot);
|
CStackInstance * s2 = dstObj->detachStack(dstSlot);
|
||||||
@ -1627,18 +1627,18 @@ void InsertNewStack::applyGs(CGameState *gs)
|
|||||||
if(auto * obj = gs->getArmyInstance(army))
|
if(auto * obj = gs->getArmyInstance(army))
|
||||||
obj->putStack(slot, new CStackInstance(type, count));
|
obj->putStack(slot, new CStackInstance(type, count));
|
||||||
else
|
else
|
||||||
logNetwork->error("[CRITICAL] InsertNewStack: invalid army object %d, possible game state corruption.", army.getNum());
|
throw std::runtime_error("InsertNewStack: invalid army object " + std::to_string(army.getNum()) + ", possible game state corruption.");
|
||||||
}
|
}
|
||||||
|
|
||||||
void RebalanceStacks::applyGs(CGameState * gs)
|
void RebalanceStacks::applyGs(CGameState * gs)
|
||||||
{
|
{
|
||||||
auto * srcObj = gs->getArmyInstance(srcArmy);
|
auto * srcObj = gs->getArmyInstance(srcArmy);
|
||||||
if(!srcObj)
|
if(!srcObj)
|
||||||
logNetwork->error("[CRITICAL] RebalanceStacks: invalid army object %d, possible game state corruption.", srcArmy.getNum());
|
throw std::runtime_error("RebalanceStacks: invalid army object " + std::to_string(srcArmy.getNum()) + ", possible game state corruption.");
|
||||||
|
|
||||||
auto * dstObj = gs->getArmyInstance(dstArmy);
|
auto * dstObj = gs->getArmyInstance(dstArmy);
|
||||||
if(!dstObj)
|
if(!dstObj)
|
||||||
logNetwork->error("[CRITICAL] RebalanceStacks: invalid army object %d, possible game state corruption.", dstArmy.getNum());
|
throw std::runtime_error("RebalanceStacks: invalid army object " + std::to_string(dstArmy.getNum()) + ", possible game state corruption.");
|
||||||
|
|
||||||
StackLocation src(srcObj, srcSlot);
|
StackLocation src(srcObj, srcSlot);
|
||||||
StackLocation dst(dstObj, dstSlot);
|
StackLocation dst(dstObj, dstSlot);
|
||||||
|
@ -126,6 +126,9 @@ std::unique_ptr<CMap> CMapGenerator::generate()
|
|||||||
fillZones();
|
fillZones();
|
||||||
//updated guarded tiles will be calculated in CGameState::initMapObjects()
|
//updated guarded tiles will be calculated in CGameState::initMapObjects()
|
||||||
map->getZones().clear();
|
map->getZones().clear();
|
||||||
|
|
||||||
|
// undo manager keeps pointers to object that might be removed during gameplay. Remove them to prevent any hanging pointer after gameplay
|
||||||
|
map->getEditManager()->getUndoManager().clearAll();
|
||||||
}
|
}
|
||||||
catch (rmgException &e)
|
catch (rmgException &e)
|
||||||
{
|
{
|
||||||
|
Reference in New Issue
Block a user