diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 00000000..a2964aca --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,63 @@ +# Contributing + +Thanks for contributing to Sapphire! + +First, we'd like to mention that a lot of discussion regarding the project happens in our Discord server. +We value research and discussion as to how we should tackle our issues as well as improving what is already in. +Of course we also value testing - many things tend to break due to patches, or mistakes/edge cases. + +Regardless of how you plan on contributing, your thoughts are appreciated and you're welcome to join our Discord (link in README.md). + +## Research + +Care in implementating features should be taken. It tends to be end up weird, and replicating the expected behavior +is always preferred. Avoid assumptions and guesswork whenever possible. + +As much research possible should be done before writing it out - on game data, testing with retail, +and even common practices in server development (emulators or not). + +## Pull Requests + +When making a PR, please make sure that it follows our style guidelines and good practices. + +### Coding style + +Indentations are Allman-style based, 4-space, no tabs. +Space between arguments in function calls, as well as for types. + +Example (shortened from ActionHandler.cpp): + +```cpp +switch( commandId ) +{ + case 0x01: // Toggle sheathe + { + if ( param11 == 1 ) + pPlayer->setStance( Entity::Actor::Stance::Active ); + else + { + pPlayer->setStance( Entity::Actor::Stance::Passive ); + pPlayer->setAutoattack( false ); + } + + pPlayer->sendToInRangeSet( ActorControlPacket142( pPlayer->getId(), 0, param11, 1 ) ); + + break; + } + case 0x03: // Change target + { + uint64_t targetId = inPacket.getValAt< uint64_t >( 0x24 ); + pPlayer->changeTarget( targetId ); + break; + } + default: + { + break; + } +} +``` + +### Feature implementation + +Please make sure edge cases have been tested, behavior is aligned with retail and (if applicable) your queries make sense. +Any changes to the SQL base should be noted (and reflected in the update.sql file in rootDirectory/sql). \ No newline at end of file diff --git a/src/servers/Server_REST/PlayerMinimal.cpp b/src/servers/Server_REST/PlayerMinimal.cpp index 0822b9e5..d182b1b8 100644 --- a/src/servers/Server_REST/PlayerMinimal.cpp +++ b/src/servers/Server_REST/PlayerMinimal.cpp @@ -345,8 +345,8 @@ namespace Core { void PlayerMinimal::insertDbGlobalItem( uint32_t weaponId, uint64_t uniqueId ) const { - auto stmtItemGlobal = g_charaDb.getPreparedStatement(Db::CHARA_ITEMGLOBAL_INS ); - stmtItemGlobal->setInt(1, m_id); + auto stmtItemGlobal = g_charaDb.getPreparedStatement( Db::CHARA_ITEMGLOBAL_INS ); + stmtItemGlobal->setInt( 1, m_id ); stmtItemGlobal->setInt64( 2, uniqueId ); stmtItemGlobal->setInt( 3, weaponId ); g_charaDb.directExecute( stmtItemGlobal ); diff --git a/src/servers/Server_Zone/Actor/Player.cpp b/src/servers/Server_Zone/Actor/Player.cpp index 14686b27..2c5528af 100644 --- a/src/servers/Server_Zone/Actor/Player.cpp +++ b/src/servers/Server_Zone/Actor/Player.cpp @@ -1396,7 +1396,7 @@ uint8_t * Core::Entity::Player::getTitleList() uint16_t Core::Entity::Player::getTitle() const { - return m_title; + return m_activeTitle; } void Core::Entity::Player::addTitle( uint16_t titleId ) @@ -1417,7 +1417,7 @@ void Core::Entity::Player::setTitle( uint16_t titleId ) if ( ( m_titleList[index] & value ) == 0 ) // Player doesn't have title - bail return; - m_title = titleId; + m_activeTitle = titleId; sendToInRangeSet( ActorControlPacket142( getId(), SetTitle, titleId ), true ); } diff --git a/src/servers/Server_Zone/Actor/Player.h b/src/servers/Server_Zone/Actor/Player.h index ac607372..ff5563f7 100644 --- a/src/servers/Server_Zone/Actor/Player.h +++ b/src/servers/Server_Zone/Actor/Player.h @@ -566,7 +566,7 @@ private: uint8_t status; } m_retainerInfo[8]; - uint16_t m_title; + uint16_t m_activeTitle; uint8_t m_titleList[48]; uint8_t m_howTo[33]; uint8_t m_minions[33]; diff --git a/src/servers/Server_Zone/Actor/PlayerSql.cpp b/src/servers/Server_Zone/Actor/PlayerSql.cpp index 336002b9..35b509d2 100644 --- a/src/servers/Server_Zone/Actor/PlayerSql.cpp +++ b/src/servers/Server_Zone/Actor/PlayerSql.cpp @@ -81,15 +81,21 @@ bool Core::Entity::Player::load( uint32_t charId, Core::SessionPtr pSession ) setRotation( 0.0f ); } + // Stats + m_hp = res->getUInt( "Hp" ); m_mp = res->getUInt( "Mp" ); m_tp = 0; + // Position + m_pos.x = res->getFloat( "PosX" ); m_pos.y = res->getFloat( "PosY" ); m_pos.z = res->getFloat( "PosZ" ); setRotation( res->getFloat( "PosR" ) ); + // Model + auto custom = res->getBlobVector( "Customize" ); memcpy( reinterpret_cast< char* >( m_customize ), custom.data(), custom.size() ); @@ -98,6 +104,8 @@ bool Core::Entity::Player::load( uint32_t charId, Core::SessionPtr pSession ) auto modelEq = res->getBlobVector( "ModelEquip" ); memcpy( reinterpret_cast< char* >( m_modelEquip ), modelEq.data(), modelEq.size() ); + // Minimal info + m_guardianDeity = res->getUInt8( "GuardianDeity" ); m_birthDay = res->getUInt8( "BirthDay" ); m_birthMonth = res->getUInt8( "BirthMonth" ); @@ -105,12 +113,27 @@ bool Core::Entity::Player::load( uint32_t charId, Core::SessionPtr pSession ) m_class = static_cast< ClassJob >( res->getUInt( "Class" ) ); m_homePoint = res->getUInt8( "Homepoint" ); - auto howTo = res->getBlobVector( "HowTo" ); - memcpy( reinterpret_cast< char* >( m_howTo ), howTo.data(), howTo.size() ); + // Additional data m_contentId = res->getUInt64( "ContentId" ); - m_voice = res->getUInt8( "Voice" ); + m_startTown = res->getUInt8( "StartTown" ); + m_playTime = res->getUInt( "TotalPlayTime" ); + + m_bNewGame = res->getBoolean( "IsNewGame" ); + m_bNewAdventurer = res->getBoolean( "IsNewAdventurer" ); + m_openingSequence = res->getUInt8( "OpeningSequence" ); + + m_gc = res->getUInt8( "GrandCompany" ); + m_cfPenaltyUntil = res->getUInt( "CFPenaltyUntil" ); + m_activeTitle = res->getUInt16( "ActiveTitle" ); + + m_gmRank = res->getUInt8( "GMRank" ); + + // Blobs + + auto howTo = res->getBlobVector( "HowTo" ); + memcpy( reinterpret_cast< char* >( m_howTo ), howTo.data(), howTo.size() ); auto questCompleteFlags = res->getBlobVector( "QuestCompleteFlags" ); memcpy( reinterpret_cast< char* >( m_questCompleteFlags ), questCompleteFlags.data(), questCompleteFlags.size() ); @@ -118,8 +141,6 @@ bool Core::Entity::Player::load( uint32_t charId, Core::SessionPtr pSession ) auto questTracking = res->getBlobVector( "QuestTracking" ); memcpy( reinterpret_cast< char* >( m_questTracking ), questTracking.data(), questTracking.size() ); - m_bNewGame = res->getBoolean( "IsNewGame" ); - auto aetheryte = res->getBlobVector( "Aetheryte" ); memcpy( reinterpret_cast< char* >( m_aetheryte ), aetheryte.data(), aetheryte.size() ); @@ -129,21 +150,12 @@ bool Core::Entity::Player::load( uint32_t charId, Core::SessionPtr pSession ) auto discovery = res->getBlobVector( "Discovery" ); memcpy( reinterpret_cast< char* >( m_discovery ), discovery.data(), discovery.size() ); - m_startTown = res->getUInt8( "StartTown" ); - m_playTime = res->getUInt( "TotalPlayTime" ); - - m_bNewAdventurer = res->getBoolean( "IsNewAdventurer" ); - - m_gc = res->getUInt8( "GrandCompany" ); + auto titleList = res->getBlobVector( "TitleList" ); + memcpy( reinterpret_cast< char* >( m_titleList ), titleList.data(), titleList.size() ); + auto gcRank = res->getBlobVector( "GrandCompanyRank" ); memcpy( reinterpret_cast< char* >( m_gcRank ), gcRank.data(), gcRank.size() ); - m_cfPenaltyUntil = res->getUInt( "CFPenaltyUntil" ); - - m_openingSequence = res->getUInt8( "OpeningSequence" ); - - m_gmRank = res->getUInt8( "GMRank" ); - res->free(); m_pCell = nullptr; @@ -338,7 +350,7 @@ void Core::Entity::Player::updateSql() stmt->setInt( 35, 0 ); // RestPoint stmt->setInt( 36, 0 ); // ActiveTitle - std::vector< uint8_t > titleListVec( 32 ); + std::vector< uint8_t > titleListVec( sizeof ( m_titleList ) ); stmt->setBinary( 37, titleListVec ); std::vector< uint8_t > achievementVec( 16 ); @@ -484,6 +496,4 @@ void Core::Entity::Player::insertQuest( uint16_t questId, uint8_t index, uint8_t stmt->setInt( 11, 0 ); stmt->setInt( 12, 0 ); g_charaDb.execute( stmt ); -} - - +} \ No newline at end of file