diff --git a/src/NetworkSyncManager.cpp b/src/NetworkSyncManager.cpp index 91e1213fd6..40232aca3d 100644 --- a/src/NetworkSyncManager.cpp +++ b/src/NetworkSyncManager.cpp @@ -21,6 +21,7 @@ bool NetworkSyncManager::Connect( const RString& addy, unsigned short port ) { r RString NetworkSyncManager::GetServerName() { return RString(); } void NetworkSyncManager::ReportNSSOnOff( int i ) { } void NetworkSyncManager::ReportScore( int playerID, int step, int score, int combo, float offset ) { } +void NetworkSyncManager::ReportScore(int playerID, int step, int score, int combo, float offset, int numNotes) { } void NetworkSyncManager::ReportSongOver() { } void NetworkSyncManager::ReportStyle() {} void NetworkSyncManager::StartRequest( short position ) { } @@ -55,6 +56,7 @@ AutoScreenMessage( SM_AddToChat ); AutoScreenMessage( SM_ChangeSong ); AutoScreenMessage( SM_GotEval ); AutoScreenMessage( SM_UsersUpdate ); +AutoScreenMessage( SM_FriendsUpdate ); AutoScreenMessage( SM_SMOnlinePack ); int NetworkSyncManager::GetSMOnlineSalt() @@ -240,6 +242,53 @@ RString NetworkSyncManager::GetServerName() return m_ServerName; } +//Same as the one below except for ctr = uint8_t(STATSMAN->m_CurStageStats.m_player[playerID].GetGrade() * 16 + numNotes); +//Im keeping the old one because it's used for single tap notes +void NetworkSyncManager::ReportScore(int playerID, int step, int score, int combo, float offset, int numNotes) +{ + if (!useSMserver) //Make sure that we are using the network + return; + + LOG->Trace("Player ID %i combo = %i", playerID, combo); + m_packet.ClearPacket(); + + m_packet.Write1(NSCGSU); + step = TranslateStepType(step); + uint8_t ctr = (uint8_t)(playerID * 16 + step - (SMOST_HITMINE - 1)); + m_packet.Write1(ctr); + + ctr = uint8_t(STATSMAN->m_CurStageStats.m_player[playerID].GetGrade() * 16 + numNotes); + + if (STATSMAN->m_CurStageStats.m_player[playerID].m_bFailed) + ctr = uint8_t(112); //Code for failed (failed constant seems not to work) + + m_packet.Write1(ctr); + m_packet.Write4(score); + m_packet.Write2((uint16_t)combo); + m_packet.Write2((uint16_t)m_playerLife[playerID]); + + // Offset Info + // Note: if a 0 is sent, then disregard data. + + // ASSUMED: No step will be more than 16 seconds off center. + // If this assumption is false, read 16 seconds in either direction. + int iOffset = int((offset + 16.384)*2000.0f); + + if (iOffset>65535) + iOffset = 65535; + if (iOffset<1) + iOffset = 1; + + // Report 0 if hold, or miss (don't forget mines should report) + if (step == SMOST_HITMINE || step > SMOST_W1) + iOffset = 0; + + m_packet.Write2((uint16_t)iOffset); + + NetPlayerClient->SendPack((char*)m_packet.Data, m_packet.Position); + +} + void NetworkSyncManager::ReportScore(int playerID, int step, int score, int combo, float offset) { if( !useSMserver ) //Make sure that we are using the network @@ -390,6 +439,31 @@ void NetworkSyncManager::StartRequest( short position ) for (int i=0; i<2-players; ++i) m_packet.WriteNT(""); //Write a NULL if no player + //Send song hash/chartkey + if (m_ServerVersion >= 129) { + tSteps = GAMESTATE->m_pCurSteps[PLAYER_1]; + if (tSteps != NULL && GAMESTATE->IsPlayerEnabled(PLAYER_1)) { + m_packet.WriteNT(tSteps->GetChartKey()); + } + else + { + m_packet.WriteNT(""); + } + + tSteps = GAMESTATE->m_pCurSteps[PLAYER_2]; + if (tSteps != NULL && GAMESTATE->IsPlayerEnabled(PLAYER_2)) { + m_packet.WriteNT(tSteps->GetChartKey()); + } + else + { + m_packet.WriteNT(""); + } + + int rate = (int)(GAMESTATE->m_SongOptions.GetCurrent().m_fMusicRate * 100); + m_packet.Write1(rate); + m_packet.WriteNT(GAMESTATE->m_pCurSong->GetFileHash()); + } + //This needs to be reset before ScreenEvaluation could possibly be called m_EvalPlayerData.clear(); @@ -607,6 +681,12 @@ void NetworkSyncManager::ProcessInput() m_sMainTitle = m_packet.ReadNT(); m_sArtist = m_packet.ReadNT(); m_sSubTitle = m_packet.ReadNT(); + //Read songhash + if (m_ServerVersion >= 129) { + m_sFileHash = m_packet.ReadNT(); + } else { + m_sFileHash = "" ; + } SCREENMAN->SendMessageToTopScreen( SM_ChangeSong ); } break; @@ -668,6 +748,20 @@ void NetworkSyncManager::ProcessInput() m_packet.ClearPacket(); } break; + case FLU: + { + int PlayersInThisPacket = m_packet.Read1(); + fl_PlayerNames.clear(); + fl_PlayerStates.clear(); + for (int i = 0; iSendMessageToTopScreen(SM_FriendsUpdate); + } + break; } m_packet.ClearPacket(); } @@ -698,6 +792,10 @@ void NetworkSyncManager::ReportPlayerOptions() NetPlayerClient->SendPack((char*)&m_packet.Data, m_packet.Position); } +int NetworkSyncManager::GetServerVersion() +{ + return m_ServerVersion; +} void NetworkSyncManager::SelectUserSong() { m_packet.ClearPacket(); @@ -706,6 +804,10 @@ void NetworkSyncManager::SelectUserSong() m_packet.WriteNT( m_sMainTitle ); m_packet.WriteNT( m_sArtist ); m_packet.WriteNT( m_sSubTitle ); + //Send songhash + if (m_ServerVersion >= 129) { + m_packet.WriteNT(GAMESTATE->m_pCurSong->GetFileHash()); + } NetPlayerClient->SendPack( (char*)&m_packet.Data, m_packet.Position ); } diff --git a/src/NetworkSyncManager.h b/src/NetworkSyncManager.h index a328d14f0a..b493c11187 100644 --- a/src/NetworkSyncManager.h +++ b/src/NetworkSyncManager.h @@ -7,7 +7,7 @@ class LoadingWindow; -const int NETPROTOCOLVERSION=3; +const int NETPROTOCOLVERSION=4; const int NETMAXBUFFERSIZE=1020; //1024 - 4 bytes for EzSockets const int NETNUMTAPSCORES=8; @@ -29,6 +29,8 @@ enum NSCommand NSCSMOnline, // 12 [SMLC_SMO] NSCFormatted, // 13 [SMLC_RESERVED1] NSCAttack, // 14 [SMLC_RESERVED2] + XML, // 15 [SMLC_RESERVED3] + FLU, // 15 [SMLC_FriendListUpdate] NUM_NS_COMMANDS }; @@ -114,6 +116,7 @@ public: // If "useSMserver" then send score to server void ReportScore( int playerID, int step, int score, int combo, float offset ); + void ReportScore(int playerID, int step, int score, int combo, float offset, int numNotes); void ReportSongOver(); void ReportStyle(); // Report style, players, and names void ReportNSSOnOff( int i ); // Report song selection screen on/off @@ -145,6 +148,10 @@ public: vector m_ActivePlayer; vector m_PlayerNames; + //friendlist + std::vector fl_PlayerNames; + std::vector fl_PlayerStates; + // Used for ScreenNetEvaluation vector m_EvalPlayerData; @@ -163,9 +170,12 @@ public: RString m_sMainTitle; RString m_sArtist; RString m_sSubTitle; + RString m_sFileHash; int m_iSelectMode; void SelectUserSong(); + int GetServerVersion(); + RString m_sChatText; // FIXME: This should NOT be public. PERIOD. It probably shouldn't be a member at all. diff --git a/src/NoteData.cpp b/src/NoteData.cpp index 0919a971ce..129c4c9623 100644 --- a/src/NoteData.cpp +++ b/src/NoteData.cpp @@ -269,6 +269,12 @@ int NoteData::GetNumTracksWithTapOrHoldHead( int row ) const return iNum; } +void NoteData::LogNonEmptyRows() { + NonEmptyRowVector.clear(); + FOREACH_NONEMPTY_ROW_ALL_TRACKS(*this, row) + NonEmptyRowVector.push_back(row); +} + int NoteData::GetFirstTrackWithTap( int row ) const { for( int t=0; t NonEmptyRowVector; + public: void Init(); + // Mina stuf (Used for chartkey hashing) + void LogNonEmptyRows(); + std::vector& GetNonEmptyRowVector() { return NonEmptyRowVector; }; + int GetNumTracks() const { return m_TapNotes.size(); } void SetNumTracks( int iNewNumTracks ); bool IsComposite() const; diff --git a/src/ScoreKeeperNormal.cpp b/src/ScoreKeeperNormal.cpp index a58fe9a435..835c826100 100644 --- a/src/ScoreKeeperNormal.cpp +++ b/src/ScoreKeeperNormal.cpp @@ -605,7 +605,7 @@ void ScoreKeeperNormal::HandleTapRowScore( const NoteData &nd, int iRow ) float offset = NoteDataWithScoring::LastTapNoteWithResult( nd, iRow ).result.fTapNoteOffset; NSMAN->ReportScore( pn, scoreOfLastTap, m_pPlayerStageStats->m_iScore, - m_pPlayerStageStats->m_iCurCombo, offset ); + m_pPlayerStageStats->m_iCurCombo, offset, m_iNumNotesHitThisRow); Message msg( "ScoreChanged" ); msg.SetParam( "PlayerNumber", m_pPlayerState->m_PlayerNumber ); msg.SetParam( "MultiPlayer", m_pPlayerState->m_mp ); diff --git a/src/ScreenNetSelectBase.cpp b/src/ScreenNetSelectBase.cpp index 54a8e239c9..faca802407 100644 --- a/src/ScreenNetSelectBase.cpp +++ b/src/ScreenNetSelectBase.cpp @@ -28,6 +28,7 @@ AutoScreenMessage( SM_AddToChat ); AutoScreenMessage( SM_UsersUpdate ); +AutoScreenMessage( SM_FriendsUpdate ); AutoScreenMessage( SM_SMOnlinePack ); REGISTER_SCREEN_CLASS( ScreenNetSelectBase ); @@ -135,6 +136,10 @@ void ScreenNetSelectBase::HandleScreenMessage( const ScreenMessage SM ) { UpdateUsers(); } + else if (SM == SM_FriendsUpdate) + { + MESSAGEMAN->Broadcast("FriendsUpdate"); + } ScreenWithMenuElements::HandleScreenMessage( SM ); } diff --git a/src/ScreenNetSelectMusic.cpp b/src/ScreenNetSelectMusic.cpp index 2da02a59c6..92405388d4 100644 --- a/src/ScreenNetSelectMusic.cpp +++ b/src/ScreenNetSelectMusic.cpp @@ -181,14 +181,37 @@ void ScreenNetSelectMusic::HandleScreenMessage( const ScreenMessage SM ) vector AllSongs = SONGMAN->GetAllSongs(); unsigned i; - for( i=0; i < AllSongs.size(); i++ ) + bool found = false; + if (NSMAN->GetServerVersion() >= 129) { - m_cSong = AllSongs[i]; - if( ( !m_cSong->GetTranslitArtist().CompareNoCase( NSMAN->m_sArtist ) ) && - ( !m_cSong->GetTranslitMainTitle().CompareNoCase( NSMAN->m_sMainTitle ) ) && - ( !m_cSong->GetTranslitSubTitle().CompareNoCase( NSMAN->m_sSubTitle ) ) ) - break; + //Dont earch by filehash if none was sent + if(!NSMAN->m_sFileHash.empty()) + for (i = 0; i < AllSongs.size(); i++) + { + m_cSong = AllSongs[i]; + if (NSMAN->m_sArtist == m_cSong->GetTranslitArtist() && + NSMAN->m_sMainTitle == m_cSong->GetTranslitMainTitle() && + NSMAN->m_sSubTitle == m_cSong->GetTranslitSubTitle() && + NSMAN->m_sFileHash == m_cSong->GetFileHash()) + { + found = true; + break; + } + } + } + //If we couldnt find it using file hash search for it without using it, if using SMSERVER < 129 it will go here + if(!found) + for (i = 0; i < AllSongs.size(); i++) + { + m_cSong = AllSongs[i]; + if (NSMAN->m_sArtist == m_cSong->GetTranslitArtist() && + NSMAN->m_sMainTitle == m_cSong->GetTranslitMainTitle() && + NSMAN->m_sSubTitle == m_cSong->GetTranslitSubTitle()) + { + break; + } + } bool haveSong = i != AllSongs.size(); diff --git a/src/Song.cpp b/src/Song.cpp index 2ce0d05e26..3f86d04ddb 100644 --- a/src/Song.cpp +++ b/src/Song.cpp @@ -40,6 +40,8 @@ #include #include +//-Nick12 Used for song file hashing +#include /** * @brief The internal version of the cache for StepMania. @@ -1592,6 +1594,32 @@ vector Song::GetFGChanges1ToVectorString() const return this->GetChangesToVectorString(this->GetForegroundChanges()); } +RString Song::GetFileHash() +{ + if (m_sFileHash.empty()) { + RString sPath = SetExtension(GetSongFilePath(), "sm"); + if (!IsAFile(sPath)) + sPath = SetExtension(GetSongFilePath(), "dwi"); + if (!IsAFile(sPath)) + sPath = SetExtension(GetSongFilePath(), "sma"); + if (!IsAFile(sPath)) + sPath = SetExtension(GetSongFilePath(), "bms"); + if (!IsAFile(sPath)) + sPath = SetExtension(GetSongFilePath(), "ksf"); + if (!IsAFile(sPath)) + sPath = SetExtension(GetSongFilePath(), "json"); + if (!IsAFile(sPath)) + sPath = SetExtension(GetSongFilePath(), "jso"); + if (!IsAFile(sPath)) + sPath = SetExtension(GetSongFilePath(), "ssc"); + if (IsAFile(sPath)) + m_sFileHash = BinaryToHex(CRYPTMAN->GetSHA1ForFile(sPath)); + else + m_sFileHash = ""; + } + return m_sFileHash; +} + vector Song::GetInstrumentTracksToVectorString() const { vector ret; diff --git a/src/Song.h b/src/Song.h index 8058df65ad..76a24d743e 100644 --- a/src/Song.h +++ b/src/Song.h @@ -188,6 +188,9 @@ public: /** @brief The transliterated artist of the Song, if it exists. */ RString m_sArtistTranslit; + RString m_sFileHash; + RString GetFileHash(); + /* If PREFSMAN->m_bShowNative is off, these are the same as GetTranslit* * below. Otherwise, they return the main titles. */ RString GetDisplayMainTitle() const; diff --git a/src/Steps.cpp b/src/Steps.cpp index 7912f4a603..5c24cd0c94 100644 --- a/src/Steps.cpp +++ b/src/Steps.cpp @@ -33,6 +33,9 @@ /* register DisplayBPM with StringConversion */ #include "EnumHelper.h" +// For hashing hart keys - Mina +#include "CryptManager.h" + static const char *DisplayBPMNames[] = { "Actual", @@ -614,6 +617,77 @@ void Steps::SetCachedRadarValues( const RadarValues v[NUM_PLAYERS] ) m_bAreCachedRadarValuesJustLoaded = true; } +RString Steps::GenerateChartKey() +{ + ChartKey = this->GenerateChartKey(*m_pNoteData, this->GetTimingData()); + return ChartKey; +} +RString Steps::GetChartKey() +{ + if (ChartKey.empty()) { + this->Decompress(); + ChartKey = this->GenerateChartKey(*m_pNoteData, this->GetTimingData()); + this->Compress(); + } + return ChartKey; +} +RString Steps::GenerateChartKey(NoteData &nd, TimingData *td) +{ + RString k = ""; + RString o = ""; + float bpm; + nd.LogNonEmptyRows(); + std::vector& nerv = nd.GetNonEmptyRowVector(); + + + RString firstHalf = ""; + RString secondHalf = ""; + +#pragma omp parallel sections + { +#pragma omp section + { + for (size_t r = 0; r < nerv.size() / 2; r++) { + int row = nerv[r]; + for (int t = 0; t < nd.GetNumTracks(); ++t) { + const TapNote &tn = nd.GetTapNote(t, row); + std::ostringstream os; + os << tn.type; + firstHalf.append(os.str()); + } + bpm = td->GetBPMAtRow(row); + std::ostringstream os; + os << static_cast(bpm + 0.374643f); + firstHalf.append(os.str()); + } + } + +#pragma omp section + { + for (size_t r = nerv.size() / 2; r < nerv.size(); r++) { + int row = nerv[r]; + for (int t = 0; t < nd.GetNumTracks(); ++t) { + const TapNote &tn = nd.GetTapNote(t, row); + std::ostringstream os; + os << tn.type; + secondHalf.append(os.str()); + } + bpm = td->GetBPMAtRow(row); + std::ostringstream os; + os << static_cast(bpm + 0.374643f); + firstHalf.append(os.str()); + } + } + } + k = firstHalf + secondHalf; + + //ChartKeyRecord = k; + o.append("X"); // I was thinking of using "C" to indicate chart.. however.. X is cooler... - Mina + o.append(BinaryToHex(CryptManager::GetSHA1ForString(k))); + return o; +} + + // lua start #include "LuaBinding.h" /** @brief Allow Lua to have access to the Steps. */ diff --git a/src/Steps.h b/src/Steps.h index 15fd7f882a..ce2d9928a7 100644 --- a/src/Steps.h +++ b/src/Steps.h @@ -124,6 +124,14 @@ public: void SetChartStyle( RString sChartStyle ); static bool MakeValidEditDescription( RString &sPreferredDescription ); // return true if was modified + /* This is a reimplementation of the lua version of the script to generate chart keys, except this time + using the notedata stored in game memory immediately after reading it than parsing it using lua. - Mina */ + RString GenerateChartKey(NoteData &nd, TimingData *td); + RString GenerateChartKey(); + RString ChartKey; + RString GetChartKey(); + void SetChartKey(const RString &k) { ChartKey = k; } + void SetLoadedFromProfile( ProfileSlot slot ) { m_LoadedFromProfile = slot; } void SetMeter( int meter ); void SetCachedRadarValues( const RadarValues v[NUM_PLAYERS] );