Skip to content

Commit

Permalink
Fixed mixup in character skin names vs. filenames.
Browse files Browse the repository at this point in the history
One .skin file can define multiple skins, so skins must be described by full name, not filename. The fix applies to both cvars (local configuration) and RoRnet.

Codechanges:
- CacheSystem.h: added search method NAME_FULL and helper func `MatchExact()`
- CacheSystem.cpp: more comments in `Query()`
- RORnet: renamed skin field + constant to clarify filename vs. name.
- Network.cpp - just reflect the renaming in rornet
- CharacterFactory: added helper `fetchCharacterSkin()` using the correct lookup for both local+remote characters.
- GameContext.cpp - `OnLoaderGuiApply()` - fixed configuration cvars to contain skin names instead of filenames
  • Loading branch information
ohlidalp committed Jun 20, 2023
1 parent 5d65834 commit ba08564
Show file tree
Hide file tree
Showing 7 changed files with 67 additions and 58 deletions.
4 changes: 2 additions & 2 deletions source/main/GameContext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -766,7 +766,7 @@ void GameContext::OnLoaderGuiApply(LoaderType type, CacheEntry* entry, std::stri
App::sim_player_character->setStr(m_current_selection.asr_cache_entry->fname);
if (m_current_selection.asr_skin_entry)
{
App::sim_player_character_skin->setStr(m_current_selection.asr_skin_entry->fname);
App::sim_player_character_skin->setStr(m_current_selection.asr_skin_entry->dname);
}
else
{
Expand All @@ -778,7 +778,7 @@ void GameContext::OnLoaderGuiApply(LoaderType type, CacheEntry* entry, std::stri
App::mp_override_character->setStr(m_current_selection.asr_cache_entry->fname);
if (m_current_selection.asr_skin_entry)
{
App::mp_override_character_skin->setStr(m_current_selection.asr_skin_entry->fname);
App::mp_override_character_skin->setStr(m_current_selection.asr_skin_entry->dname);
}
else
{
Expand Down
77 changes: 32 additions & 45 deletions source/main/gameplay/CharacterFactory.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ Character* CharacterFactory::CreateLocalCharacter()
Ogre::UTFString playerName = "";
// Singleplayer character presets
std::string characterFile = App::sim_player_character->getStr();
std::string characterSkinfile = App::sim_player_character_skin->getStr();
std::string characterSkin = App::sim_player_character_skin->getStr();

#ifdef USE_SOCKETW
if (App::mp_state->getEnum<MpState>() == MpState::CONNECTED)
Expand All @@ -51,7 +51,7 @@ Character* CharacterFactory::CreateLocalCharacter()
playerName = tryConvertUTF(info.username);
// Multiplayer character presets
characterFile = info.character_file;
characterSkinfile = info.character_skinfile;
characterSkin = info.character_skin;
}
#endif // USE_SOCKETW

Expand Down Expand Up @@ -84,29 +84,7 @@ Character* CharacterFactory::CreateLocalCharacter()
return nullptr;
}

CacheEntry* skin_entry = nullptr;
if (characterSkinfile != "")
{
skin_entry = App::GetCacheSystem()->FindEntryByFilename(LT_Skin, /*partial:*/false, characterSkinfile);

if (skin_entry)
{
std::shared_ptr<SkinDef> skin_def = App::GetCacheSystem()->FetchSkinDef(skin_entry); // Make sure it exists
if (skin_def == nullptr)
{
skin_entry = nullptr; // Error already logged
}
// The loaded document is now pinned to the CacheEntry
}
else
{
App::GetConsole()->putMessage(Console::CONSOLE_MSGTYPE_INFO, Console::CONSOLE_SYSTEM_WARNING,
fmt::format("Could not find character skin '{}' in mod cache, continuing without it.", characterSkinfile));
}
}



CacheEntry* skin_entry = this->fetchCharacterSkin(characterSkin, _L("local player"));
m_local_character = std::unique_ptr<Character>(new Character(cache_entry, skin_entry, -1, 0, playerName, colourNum, false));
App::GetGfxScene()->RegisterGfxCharacter(m_local_character.get());
return m_local_character.get();
Expand Down Expand Up @@ -141,26 +119,6 @@ void CharacterFactory::createRemoteInstance(int sourceid, int streamid)
return;
}

CacheEntry* skin_entry = nullptr;
if (std::string(info.character_skinfile) != "")
{
skin_entry = App::GetCacheSystem()->FindEntryByFilename(LT_Skin, /*partial:*/false, info.character_skinfile);
if (skin_entry)
{
std::shared_ptr<SkinDef> skin_def = App::GetCacheSystem()->FetchSkinDef(skin_entry); // Make sure it exists
if (skin_def == nullptr)
{
skin_entry = nullptr; // Error already logged
}
// Note the skin def is cached in the CacheEntry so the GfxCharacter can just rely on it.
}
else
{
App::GetConsole()->putMessage(Console::CONSOLE_MSGTYPE_INFO, Console::CONSOLE_SYSTEM_WARNING,
fmt::format("Could not skin character for {} - skin '{}' not found in mod cache.", info_str, info.character_skinfile));
}
}

CharacterDocumentPtr document = App::GetCacheSystem()->FetchCharacterDef(cache_entry);
if (!document)
{
Expand All @@ -169,6 +127,7 @@ void CharacterFactory::createRemoteInstance(int sourceid, int streamid)
return;
}

CacheEntry* skin_entry = this->fetchCharacterSkin(info.character_skin, info_str);
Character* ch = new Character(cache_entry, skin_entry, sourceid, streamid, name, colour, true);
App::GetGfxScene()->RegisterGfxCharacter(ch);
m_remote_characters.push_back(std::unique_ptr<Character>(ch));
Expand Down Expand Up @@ -246,3 +205,31 @@ void CharacterFactory::handleStreamData(std::vector<RoR::NetRecvPacket> packet_b
}
}
#endif // USE_SOCKETW

CacheEntry* CharacterFactory::fetchCharacterSkin(const std::string& skinName, const std::string& errLogPlayer)
{
if (skinName == "")
return nullptr;

CacheQuery skinQuery;
skinQuery.cqy_search_method = CacheSearchMethod::NAME_FULL;
skinQuery.cqy_search_string = skinName;
if (App::GetCacheSystem()->Query(skinQuery) == 0)
{
App::GetConsole()->putMessage(Console::CONSOLE_MSGTYPE_INFO, Console::CONSOLE_SYSTEM_WARNING,
fmt::format("Skin '{}' requested by player player '{}' is not installed, continuing without it.",
skinName, errLogPlayer));
return nullptr;
}

if (!App::GetCacheSystem()->FetchSkinDef(skinQuery.cqy_results[0].cqr_entry))
{
App::GetConsole()->putMessage(Console::CONSOLE_MSGTYPE_INFO, Console::CONSOLE_SYSTEM_WARNING,
fmt::format("Could not load character skin '{}' for player '{}', continuing without it.",
skinName, errLogPlayer));
return nullptr;
}

// The loaded skin is pinned to the CacheEntry now.
return skinQuery.cqy_results[0].cqr_entry;
}
2 changes: 2 additions & 0 deletions source/main/gameplay/CharacterFactory.h
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,8 @@ class CharacterFactory

void createRemoteInstance(int sourceid, int streamid);
void removeStreamSource(int sourceid);

CacheEntry* fetchCharacterSkin(const std::string& skinName, const std::string& errLogPlayer);
};

/// @} // addtogroup Character
Expand Down
4 changes: 2 additions & 2 deletions source/main/network/Network.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -505,12 +505,12 @@ bool Network::ConnectThread()
if (App::mp_override_character->getStr() != "")
{
strncpy(c.character_file, App::mp_override_character->getStr().c_str(), RORNET_MAX_CHARACTER_FILE_LEN - 1);
strncpy(c.character_skinfile, App::mp_override_character_skin->getStr().c_str(), RORNET_MAX_CHARACTER_SKINFILE_LEN - 1);
strncpy(c.character_skin, App::mp_override_character_skin->getStr().c_str(), RORNET_MAX_CHARACTER_SKIN_LEN - 1);
}
else
{
strncpy(c.character_file, App::sim_player_character->getStr().c_str(), RORNET_MAX_CHARACTER_FILE_LEN - 1);
strncpy(c.character_skinfile, App::sim_player_character_skin->getStr().c_str(), RORNET_MAX_CHARACTER_SKINFILE_LEN - 1);
strncpy(c.character_skin, App::sim_player_character_skin->getStr().c_str(), RORNET_MAX_CHARACTER_SKIN_LEN - 1);
}

if (!SendNetMessage(MSG2_USER_INFO, 0, sizeof(RoRnet::UserInfo), (char*)&c))
Expand Down
7 changes: 4 additions & 3 deletions source/main/network/RoRnet.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,9 @@ namespace RoRnet {
#define RORNET_MAX_MESSAGE_LENGTH 8192 //!< maximum size of a RoR message. 8192 bytes = 8 kibibytes
#define RORNET_LAN_BROADCAST_PORT 13000 //!< port used to send the broadcast announcement in LAN mode
#define RORNET_MAX_USERNAME_LEN 40
#define RORNET_MAX_CHARACTER_FILE_LEN 40
#define RORNET_MAX_CHARACTER_SKINFILE_LEN 40
#define RORNET_MAX_CHARACTER_FILE_LEN 60
#define RORNET_MAX_CHARACTER_SKIN_LEN 60


#define RORNET_VERSION "RoRnet_2.44"

Expand Down Expand Up @@ -235,7 +236,7 @@ struct UserInfo
char clientGUID[40]; //!< the clients GUID
char sessiontype[10]; //!< the requested session type. For example "normal", "bot", "rcon"
char character_file[RORNET_MAX_CHARACTER_FILE_LEN]; //!< Filename of the chosen character
char character_skinfile[RORNET_MAX_CHARACTER_SKINFILE_LEN]; //!< Skin filename of the chosen character
char character_skin[RORNET_MAX_CHARACTER_SKIN_LEN]; //!< Skin name for the chosen character
};

struct VehicleState //!< Formerly `oob_t`
Expand Down
19 changes: 18 additions & 1 deletion source/main/resources/CacheSystem.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1381,6 +1381,7 @@ size_t CacheSystem::Query(CacheQuery& query)
Str<100> wheels_str;
switch (query.cqy_search_method)
{
// Partial case-insensitive match in: name, filename, description, author name/email
case CacheSearchMethod::FULLTEXT:
if (match = this->Match(score, entry.dname, query.cqy_search_string, 0)) { break; }
if (match = this->Match(score, entry.fname, query.cqy_search_string, 100)) { break; }
Expand All @@ -1392,10 +1393,12 @@ size_t CacheSystem::Query(CacheQuery& query)
}
break;

// Partial case-insensitive match of GUID (intentionally - for search box)
case CacheSearchMethod::GUID:
match = this->Match(score, entry.guid, query.cqy_search_string, 0);
break;

// Partial case-insensitive match of author name/email
case CacheSearchMethod::AUTHORS:
for (AuthorInfo const& author: entry.authors)
{
Expand All @@ -1404,16 +1407,24 @@ size_t CacheSystem::Query(CacheQuery& query)
}
break;

// Search by wheel configuration, for example '4x4'
case CacheSearchMethod::WHEELS:
wheels_str << entry.wheelcount << "x" << entry.propwheelcount;
match = this->Match(score, wheels_str.ToCStr(), query.cqy_search_string, 0);
break;

// Partial, case-insensitive match in file name
case CacheSearchMethod::FILENAME:
match = this->Match(score, entry.fname, query.cqy_search_string, 100);
break;

default: // CacheSearchMethod::NONE
// Full case-insensitive match in mod name (useful for skins - one .skin file can define multiple skins)
case CacheSearchMethod::NAME_FULL:
match = this->MatchExact(entry.dname, query.cqy_search_string);
break;

// CacheSearchMethod::NONE -> Show everything
default:
match = true;
break;
};
Expand Down Expand Up @@ -1444,6 +1455,12 @@ bool CacheSystem::Match(size_t& out_score, std::string data, std::string const&
}
}

bool CacheSystem::MatchExact(std::string data, std::string const& query)
{
Ogre::StringUtil::toLowerCase(data);
return data == query;
}

bool CacheQueryResult::operator<(CacheQueryResult const& other) const
{
if (cqr_score == other.cqr_score)
Expand Down
12 changes: 7 additions & 5 deletions source/main/resources/CacheSystem.h
Original file line number Diff line number Diff line change
Expand Up @@ -152,19 +152,20 @@ struct CacheQueryResult

enum class CacheSearchMethod // Always case-insensitive
{
NONE, //!< No searching
NONE, //!< Show everything
FULLTEXT, //!< Partial match in: name, filename, description, author name/mail
GUID, //!< Partial match in: guid
GUID, //!< Partial match in: guid (intentionally - for search box)
AUTHORS, //!< Partial match in: author name/email
WHEELS, //!< Wheel configuration, i.e. 4x4
FILENAME //!< Partial match in file name
FILENAME, //!< Partial match in file name
NAME_FULL //!< Full match of name (case-insensitive)
};

struct CacheQuery
{
RoR::LoaderType cqy_filter_type = RoR::LoaderType::LT_None;
int cqy_filter_category_id = CacheCategoryId::CID_All;
std::string cqy_filter_guid; //!< Exact match; leave empty to disable
std::string cqy_filter_guid; //!< Exact match; leave empty to disable; not the same as CacheSearchMethod::GUID
CacheSearchMethod cqy_search_method = CacheSearchMethod::NONE;
std::string cqy_search_string;

Expand Down Expand Up @@ -249,7 +250,8 @@ class CacheSystem : public ZeroedMemoryAllocator
void GenerateFileCache(CacheEntry &entry, Ogre::String group);
void RemoveFileCache(CacheEntry &entry);

bool Match(size_t& out_score, std::string data, std::string const& query, size_t );
bool Match(size_t& out_score, std::string data, std::string const& query, size_t);
bool MatchExact(std::string data, const std::string& query);

std::time_t m_update_time; //!< Ensures that all inserted files share the same timestamp
std::string m_filenames_hash_loaded; //!< hash from cachefile, for quick update detection
Expand Down

0 comments on commit ba08564

Please sign in to comment.