1
0
mirror of https://github.com/vcmi/vcmi.git synced 2025-11-29 23:07:48 +02:00

merge beta branch into develop

This commit is contained in:
Ivan Savenko
2022-12-29 22:08:53 +02:00
11 changed files with 149 additions and 32 deletions

View File

@@ -1079,7 +1079,8 @@ static bool recreateWindow(int w, int h, int bpp, bool fullscreen, int displayIn
if (displayIndex < 0) if (displayIndex < 0)
displayIndex = 0; displayIndex = 0;
} }
#ifdef VCMI_IOS
#if defined(VCMI_ANDROID) || defined(VCMI_IOS)
SDL_GetWindowSize(mainWindow, &w, &h); SDL_GetWindowSize(mainWindow, &w, &h);
#else #else
if(!checkVideoMode(displayIndex, w, h)) if(!checkVideoMode(displayIndex, w, h))

View File

@@ -409,6 +409,8 @@ void CMusicHandler::release()
void CMusicHandler::playMusic(const std::string & musicURI, bool loop, bool fromStart) void CMusicHandler::playMusic(const std::string & musicURI, bool loop, bool fromStart)
{ {
boost::mutex::scoped_lock guard(mutex);
if (current && current->isPlaying() && current->isTrack(musicURI)) if (current && current->isPlaying() && current->isTrack(musicURI))
return; return;
@@ -422,6 +424,8 @@ void CMusicHandler::playMusicFromSet(const std::string & musicSet, const std::st
void CMusicHandler::playMusicFromSet(const std::string & whichSet, bool loop, bool fromStart) void CMusicHandler::playMusicFromSet(const std::string & whichSet, bool loop, bool fromStart)
{ {
boost::mutex::scoped_lock guard(mutex);
auto selectedSet = musicsSet.find(whichSet); auto selectedSet = musicsSet.find(whichSet);
if (selectedSet == musicsSet.end()) if (selectedSet == musicsSet.end())
{ {
@@ -441,8 +445,6 @@ void CMusicHandler::queueNext(std::unique_ptr<MusicEntry> queued)
if (!initialized) if (!initialized)
return; return;
boost::mutex::scoped_lock guard(mutex);
next = std::move(queued); next = std::move(queued);
if (current.get() == nullptr || !current->stop(1000)) if (current.get() == nullptr || !current->stop(1000))
@@ -487,13 +489,32 @@ void CMusicHandler::setVolume(ui32 percent)
void CMusicHandler::musicFinishedCallback() void CMusicHandler::musicFinishedCallback()
{ {
boost::mutex::scoped_lock guard(mutex); // boost::mutex::scoped_lock guard(mutex);
// FIXME: WORKAROUND FOR A POTENTIAL DEADLOCK
// It is possible for:
// 1) SDL thread to call this method on end of playback
// 2) VCMI code to call queueNext() method to queue new file
// this leads to:
// 1) SDL thread waiting to acquire music lock in this method (while keeping internal SDL mutex locked)
// 2) VCMI thread waiting to acquire internal SDL mutex (while keeping music mutex locked)
// Because of that (and lack of clear way to fix that)
// We will try to acquire lock here and if failed - do nothing
// This may break music playback till next song is enqued but won't deadlock the game
if (!mutex.try_lock())
{
logGlobal->error("Failed to acquire mutex! Unable to restart music!");
return;
}
if (current.get() != nullptr) if (current.get() != nullptr)
{ {
// if music is looped, play it again // if music is looped, play it again
if (current->play()) if (current->play())
{
mutex.unlock();
return; return;
}
else else
current.reset(); current.reset();
} }
@@ -503,6 +524,7 @@ void CMusicHandler::musicFinishedCallback()
current.reset(next.release()); current.reset(next.release());
current->play(); current->play();
} }
mutex.unlock();
} }
MusicEntry::MusicEntry(CMusicHandler *owner, std::string setName, std::string musicURI, bool looped, bool fromStart): MusicEntry::MusicEntry(CMusicHandler *owner, std::string setName, std::string musicURI, bool looped, bool fromStart):
@@ -597,7 +619,7 @@ bool MusicEntry::play()
bool MusicEntry::stop(int fade_ms) bool MusicEntry::stop(int fade_ms)
{ {
if (Mix_PlayingMusic()) if (playing)
{ {
playing = false; playing = false;
loop = 0; loop = 0;

View File

@@ -2251,6 +2251,8 @@ void CPlayerInterface::artifactRemoved(const ArtifactLocation &al)
if (artWin) if (artWin)
artWin->artifactRemoved(al); artWin->artifactRemoved(al);
} }
waitWhileDialog();
} }
void CPlayerInterface::artifactMoved(const ArtifactLocation &src, const ArtifactLocation &dst) void CPlayerInterface::artifactMoved(const ArtifactLocation &src, const ArtifactLocation &dst)
@@ -2265,6 +2267,8 @@ void CPlayerInterface::artifactMoved(const ArtifactLocation &src, const Artifact
} }
if(!GH.objsToBlit.empty()) if(!GH.objsToBlit.empty())
GH.objsToBlit.back()->redraw(); GH.objsToBlit.back()->redraw();
waitWhileDialog();
} }
void CPlayerInterface::artifactPossibleAssembling(const ArtifactLocation & dst) void CPlayerInterface::artifactPossibleAssembling(const ArtifactLocation & dst)

View File

@@ -235,7 +235,11 @@ std::shared_ptr<CButton> CMenuEntry::createButton(CMenuScreen * parent, const Js
if(posy < 0) if(posy < 0)
posy = pos.h + posy; posy = pos.h + posy;
return std::make_shared<CButton>(Point(posx, posy), button["name"].String(), help, command, (int)button["hotkey"].Float()); auto result = std::make_shared<CButton>(Point(posx, posy), button["name"].String(), help, command, (int)button["hotkey"].Float());
if (button["center"].Bool())
result->moveBy(Point(-result->pos.w/2, -result->pos.h/2));
return result;
} }
CMenuEntry::CMenuEntry(CMenuScreen * parent, const JsonNode & config) CMenuEntry::CMenuEntry(CMenuScreen * parent, const JsonNode & config)

View File

@@ -11,12 +11,16 @@
[ [
{"type" : "lod", "path" : "Data/H3ab_bmp.lod"}, {"type" : "lod", "path" : "Data/H3ab_bmp.lod"},
{"type" : "lod", "path" : "Data/H3bitmap.lod"}, {"type" : "lod", "path" : "Data/H3bitmap.lod"},
{"type" : "lod", "path" : "Data/h3abp_bm.lod"}, // Polish version of H3 only
{"type" : "lod", "path" : "Data/H3pbitma.lod"}, // Polish version of H3 only
{"type" : "dir", "path" : "Data"} {"type" : "dir", "path" : "Data"}
], ],
"SPRITES/": "SPRITES/":
[ [
{"type" : "lod", "path" : "Data/H3ab_spr.lod"}, {"type" : "lod", "path" : "Data/H3ab_spr.lod"},
{"type" : "lod", "path" : "Data/H3sprite.lod"}, {"type" : "lod", "path" : "Data/H3sprite.lod"},
{"type" : "lod", "path" : "Data/h3abp_sp.lod"}, // Polish version of H3 only
{"type" : "lod", "path" : "Data/H3psprit.lod"}, // Polish version of H3 only
{"type" : "dir", "path" : "Sprites"} {"type" : "dir", "path" : "Sprites"}
], ],
"SOUNDS/": "SOUNDS/":

View File

@@ -10,29 +10,29 @@
"background" : "gamselbk", "background" : "gamselbk",
//"scalable" : true, //background will be scaled to screen size //"scalable" : true, //background will be scaled to screen size
//"video" : {"x": 8, "y": 105, "name":"CREDITS.SMK" },//Floating WoG logo. Disabled due to different position in various versions of H3. //"video" : {"x": 8, "y": 105, "name":"CREDITS.SMK" },//Floating WoG logo. Disabled due to different position in various versions of H3.
//"images" : [],//Optioal, contains any additional images in the same format as video //"images" : [],//Optional, contains any additional images in the same format as video
"items" : "items" :
[ [
{ {
"name" : "main", "name" : "main",
"buttons": "buttons":
[ [
{"x": 540, "y": 10, "name":"MMENUNG", "hotkey" : 110, "help": 3, "command": "to new"}, {"x": 644, "y": 70, "center" : true, "name":"MMENUNG", "hotkey" : 110, "help": 3, "command": "to new"},
{"x": 532, "y": 132, "name":"MMENULG", "hotkey" : 108, "help": 4, "command": "to load"}, {"x": 645, "y": 192, "center" : true, "name":"MMENULG", "hotkey" : 108, "help": 4, "command": "to load"},
{"x": 524, "y": 251, "name":"MMENUHS", "hotkey" : 104, "help": 5, "command": "highscores"}, {"x": 643, "y": 296, "center" : true, "name":"MMENUHS", "hotkey" : 104, "help": 5, "command": "highscores"},
{"x": 557, "y": 359, "name":"MMENUCR", "hotkey" : 99, "help": 6, "command": "to credits"}, {"x": 643, "y": 414, "center" : true, "name":"MMENUCR", "hotkey" : 99, "help": 6, "command": "to credits"},
{"x": 586, "y": 468, "name":"MMENUQT", "hotkey" : 27, "help": 7, "command": "exit"} {"x": 643, "y": 520, "center" : true, "name":"MMENUQT", "hotkey" : 27, "help": 7, "command": "exit"}
] ]
}, },
{ {
"name" : "new", "name" : "new",
"buttons": "buttons":
[ [
{"x": 545, "y": 4, "name":"GTSINGL", "hotkey" : 115, "help": 10, "command": "start single"}, {"x": 649, "y": 65, "center" : true, "name":"GTSINGL", "hotkey" : 115, "help": 10, "command": "start single"},
{"x": 568, "y": 120, "name":"GTMULTI", "hotkey" : 109, "help": 12, "command": "start multi"}, {"x": 649, "y": 180, "center" : true, "name":"GTMULTI", "hotkey" : 109, "help": 12, "command": "start multi"},
{"x": 541, "y": 233, "name":"GTCAMPN", "hotkey" : 99, "help": 11, "command": "to campaign"}, {"x": 646, "y": 298, "center" : true, "name":"GTCAMPN", "hotkey" : 99, "help": 11, "command": "to campaign"},
{"x": 545, "y": 358, "name":"GTTUTOR", "hotkey" : 116, "help": 13, "command": "start tutorial"}, {"x": 647, "y": 412, "center" : true, "name":"GTTUTOR", "hotkey" : 116, "help": 13, "command": "start tutorial"},
{"x": 582, "y": 464, "name":"GTBACK", "hotkey" : 27, "help": 14, "command": "to main"} {"x": 645, "y": 517, "center" : true, "name":"GTBACK", "hotkey" : 27, "help": 14, "command": "to main"}
], ],
"images": [ {"x": 114, "y": 312, "name":"NEWGAME"} ] "images": [ {"x": 114, "y": 312, "name":"NEWGAME"} ]
}, },
@@ -40,11 +40,11 @@
"name" : "load", "name" : "load",
"buttons": "buttons":
[ [
{"x": 545, "y": 8, "name":"GTSINGL", "hotkey" : 115, "help": 10, "command": "load single"}, {"x": 649, "y": 65, "center" : true, "name":"GTSINGL", "hotkey" : 115, "help": 10, "command": "load single"},
{"x": 568, "y": 120, "name":"GTMULTI", "hotkey" : 109, "help": 12, "command": "load multi"}, {"x": 649, "y": 180, "center" : true, "name":"GTMULTI", "hotkey" : 109, "help": 12, "command": "load multi"},
{"x": 541, "y": 233, "name":"GTCAMPN", "hotkey" : 99, "help": 11, "command": "load campaign"}, {"x": 646, "y": 298, "center" : true, "name":"GTCAMPN", "hotkey" : 99, "help": 11, "command": "load campaign"},
{"x": 545, "y": 358, "name":"GTTUTOR", "hotkey" : 116, "help": 13, "command": "load tutorial"}, {"x": 647, "y": 412, "center" : true, "name":"GTTUTOR", "hotkey" : 116, "help": 13, "command": "load tutorial"},
{"x": 582, "y": 464, "name":"GTBACK", "hotkey" : 27, "help": 14, "command": "to main"} {"x": 645, "y": 517, "center" : true, "name":"GTBACK", "hotkey" : 27, "help": 14, "command": "to main"}
], ],
"images": [ {"x": 114, "y": 312, "name":"LOADGAME"} ] "images": [ {"x": 114, "y": 312, "name":"LOADGAME"} ]
}, },
@@ -52,11 +52,11 @@
"name" : "campaign", "name" : "campaign",
"buttons": "buttons":
[ [
{"x": 535, "y": 4, "name":"CSSSOD", "hotkey" : 119, "command": "campaigns sod"}, {"x": 634, "y": 67, "center" : true, "name":"CSSSOD", "hotkey" : 119, "command": "campaigns sod"},
{"x": 494, "y": 117, "name":"CSSROE", "hotkey" : 114, "command": "campaigns roe"}, {"x": 637, "y": 181, "center" : true, "name":"CSSROE", "hotkey" : 114, "command": "campaigns roe"},
{"x": 486, "y": 241, "name":"CSSARM", "hotkey" : 97, "command": "campaigns ab"}, {"x": 638, "y": 301, "center" : true, "name":"CSSARM", "hotkey" : 97, "command": "campaigns ab"},
{"x": 550, "y": 358, "name":"CSSCUS", "hotkey" : 99, "command": "start campaign"}, {"x": 638, "y": 413, "center" : true, "name":"CSSCUS", "hotkey" : 99, "command": "start campaign"},
{"x": 582, "y": 464, "name":"GTBACK", "hotkey" : 27, "command": "to new"} {"x": 639, "y": 518, "center" : true, "name":"CSSEXIT", "hotkey" : 27, "command": "to new"}
], ],
} }
] ]

1
debian/rules vendored
View File

@@ -8,6 +8,7 @@ override_dh_auto_configure:
dh_auto_configure -- \ dh_auto_configure -- \
-DCMAKE_INSTALL_RPATH_USE_LINK_PATH=ON \ -DCMAKE_INSTALL_RPATH_USE_LINK_PATH=ON \
-DCMAKE_INSTALL_RPATH=/usr/lib/$(DEB_HOST_MULTIARCH)/vcmi \ -DCMAKE_INSTALL_RPATH=/usr/lib/$(DEB_HOST_MULTIARCH)/vcmi \
-DCMAKE_BUILD_TYPE=RelWithDebInfo \
-DBIN_DIR=games \ -DBIN_DIR=games \
-DFORCE_BUNDLED_FL=OFF \ -DFORCE_BUNDLED_FL=OFF \
-DENABLE_TEST=0 -DENABLE_TEST=0

View File

@@ -152,12 +152,12 @@ void CArchiveLoader::initSNDArchive(const std::string &mountPoint, CFileInputStr
char filename[40]; char filename[40];
reader.read(reinterpret_cast<ui8*>(filename), 40); reader.read(reinterpret_cast<ui8*>(filename), 40);
//for some reason entries in snd have format NAME\0WAVRUBBISH.... // for some reason entries in snd have format NAME\0WAVRUBBISH....
//we need to replace first \0 with dot and take the 3 chars with extension (and drop the rest) // and Polish version does not have extension at all
// we need to replace first \0 with dot and add wav extension manuall - we don't expect other types here anyway
ArchiveEntry entry; ArchiveEntry entry;
entry.name = filename; // till 1st \0 entry.name = filename; // till 1st \0
entry.name += '.'; entry.name += ".wav";
entry.name += std::string(filename + entry.name.size(), 3);
entry.offset = reader.readInt32(); entry.offset = reader.readInt32();
entry.fullSize = reader.readInt32(); entry.fullSize = reader.readInt32();

View File

@@ -31,9 +31,18 @@ using namespace boost::asio::ip;
#define LIL_ENDIAN #define LIL_ENDIAN
#endif #endif
struct ConnectionBuffers
{
boost::asio::streambuf readBuffer;
boost::asio::streambuf writeBuffer;
};
void CConnection::init() void CConnection::init()
{ {
enableBufferedWrite = false;
enableBufferedRead = false;
connectionBuffers = std::make_unique<ConnectionBuffers>();
socket->set_option(boost::asio::ip::tcp::no_delay(true)); socket->set_option(boost::asio::ip::tcp::no_delay(true));
try try
{ {
@@ -72,6 +81,7 @@ CConnection::CConnection(std::string host, ui16 port, std::string Name, std::str
int i; int i;
boost::system::error_code error = asio::error::host_not_found; boost::system::error_code error = asio::error::host_not_found;
socket = std::make_shared<tcp::socket>(*io_service); socket = std::make_shared<tcp::socket>(*io_service);
tcp::resolver resolver(*io_service); tcp::resolver resolver(*io_service);
tcp::resolver::iterator end, pom, endpoint_iterator = resolver.resolve(tcp::resolver::query(host, std::to_string(port)),error); tcp::resolver::iterator end, pom, endpoint_iterator = resolver.resolve(tcp::resolver::query(host, std::to_string(port)),error);
if(error) if(error)
@@ -138,10 +148,39 @@ CConnection::CConnection(std::shared_ptr<TAcceptor> acceptor, std::shared_ptr<bo
} }
init(); init();
} }
void CConnection::flushBuffers()
{
if(!enableBufferedWrite)
return;
try
{
asio::write(*socket, connectionBuffers->writeBuffer);
}
catch(...)
{
//connection has been lost
connected = false;
throw;
}
enableBufferedWrite = false;
}
int CConnection::write(const void * data, unsigned size) int CConnection::write(const void * data, unsigned size)
{ {
try try
{ {
if(enableBufferedWrite)
{
std::ostream ostream(&connectionBuffers->writeBuffer);
ostream.write(static_cast<const char *>(data), size);
return size;
}
int ret; int ret;
ret = static_cast<int>(asio::write(*socket,asio::const_buffers_1(asio::const_buffer(data,size)))); ret = static_cast<int>(asio::write(*socket,asio::const_buffers_1(asio::const_buffer(data,size))));
return ret; return ret;
@@ -153,10 +192,29 @@ int CConnection::write(const void * data, unsigned size)
throw; throw;
} }
} }
int CConnection::read(void * data, unsigned size) int CConnection::read(void * data, unsigned size)
{ {
try try
{ {
if(enableBufferedRead)
{
auto available = connectionBuffers->readBuffer.size();
while(available < size)
{
auto bytesRead = socket->read_some(connectionBuffers->readBuffer.prepare(1024));
connectionBuffers->readBuffer.commit(bytesRead);
available = connectionBuffers->readBuffer.size();
}
std::istream istream(&connectionBuffers->readBuffer);
istream.read(static_cast<char *>(data), size);
return size;
}
int ret = static_cast<int>(asio::read(*socket,asio::mutable_buffers_1(asio::mutable_buffer(data,size)))); int ret = static_cast<int>(asio::read(*socket,asio::mutable_buffers_1(asio::mutable_buffer(data,size))));
return ret; return ret;
} }
@@ -167,6 +225,7 @@ int CConnection::read(void * data, unsigned size)
throw; throw;
} }
} }
CConnection::~CConnection() CConnection::~CConnection()
{ {
if(handler) if(handler)
@@ -210,6 +269,8 @@ void CConnection::reportState(vstd::CLoggerBase * out)
CPack * CConnection::retrievePack() CPack * CConnection::retrievePack()
{ {
enableBufferedRead = true;
CPack * pack = nullptr; CPack * pack = nullptr;
boost::unique_lock<boost::mutex> lock(*mutexRead); boost::unique_lock<boost::mutex> lock(*mutexRead);
iser & pack; iser & pack;
@@ -222,6 +283,9 @@ CPack * CConnection::retrievePack()
{ {
pack->c = this->shared_from_this(); pack->c = this->shared_from_this();
} }
enableBufferedRead = false;
return pack; return pack;
} }
@@ -229,7 +293,12 @@ void CConnection::sendPack(const CPack * pack)
{ {
boost::unique_lock<boost::mutex> lock(*mutexWrite); boost::unique_lock<boost::mutex> lock(*mutexWrite);
logNetwork->trace("Sending a pack of type %s", typeid(*pack).name()); logNetwork->trace("Sending a pack of type %s", typeid(*pack).name());
enableBufferedWrite = true;
oser & pack; oser & pack;
flushBuffers();
} }
void CConnection::disableStackSendingByID() void CConnection::disableStackSendingByID()

View File

@@ -52,6 +52,7 @@ typedef boost::asio::basic_socket_acceptor<boost::asio::ip::tcp, boost::asio::so
VCMI_LIB_NAMESPACE_BEGIN VCMI_LIB_NAMESPACE_BEGIN
struct CPack; struct CPack;
struct ConnectionBuffers;
/// Main class for network communication /// Main class for network communication
/// Allows establishing connection and bidirectional read-write /// Allows establishing connection and bidirectional read-write
@@ -63,8 +64,14 @@ class DLL_LINKAGE CConnection
int write(const void * data, unsigned size) override; int write(const void * data, unsigned size) override;
int read(void * data, unsigned size) override; int read(void * data, unsigned size) override;
void flushBuffers();
std::shared_ptr<boost::asio::io_service> io_service; //can be empty if connection made from socket std::shared_ptr<boost::asio::io_service> io_service; //can be empty if connection made from socket
bool enableBufferedWrite;
bool enableBufferedRead;
std::unique_ptr<ConnectionBuffers> connectionBuffers;
public: public:
BinaryDeserializer iser; BinaryDeserializer iser;
BinarySerializer oser; BinarySerializer oser;

View File

@@ -87,6 +87,11 @@ warning ()
warn_user=true warn_user=true
} }
#checks whether specified directory exists. Also works with globs
dir_exists() {
[ -d "$1" ]
}
# check if selected options are correct. # check if selected options are correct.
if [[ -n "$data_dir" ]] if [[ -n "$data_dir" ]]
@@ -177,7 +182,7 @@ then
cd "$data_dir" && innoextract "$gog_file" cd "$data_dir" && innoextract "$gog_file"
# some versions of gog.com installer (or innoextract tool?) place game files inside /app directory # some versions of gog.com installer (or innoextract tool?) place game files inside /app directory
if [[ -d "$data_dir"/app ]] if dir_exists "$data_dir"/app/[Dd][Aa][Tt][Aa]
then then
data_dir="$data_dir"/app data_dir="$data_dir"/app
fi fi