Online protocol improvements (#1393)

* Chartkey generation

* Simfile filehash generation

* Read filehash on NSCRSG. Add FLU packets and friendlist vectors. Add
getserverversion method. Send chartkey, filehash and rate on start request.

* Use filehash to find the selected song if it is not empty.

* Friendlist update broadcast.

* Send note row size if it's bigger than 1

* Used for chartkey generation

* Define the ReportScore function with numNotes when WITHOUT_NETWORKING is defined
This commit is contained in:
Nickito12
2017-02-10 09:57:59 -03:00
committed by Colby Klein
parent 854410599f
commit 6225e114ad
11 changed files with 274 additions and 8 deletions
+102
View File
@@ -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; i<PlayersInThisPacket; ++i)
{
int PStatus = m_packet.Read1();
fl_PlayerStates.push_back(PStatus);
fl_PlayerNames.push_back(m_packet.ReadNT());
}
SCREENMAN->SendMessageToTopScreen(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 );
}
+11 -1
View File
@@ -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<int> m_ActivePlayer;
vector<RString> m_PlayerNames;
//friendlist
std::vector<RString> fl_PlayerNames;
std::vector<int> fl_PlayerStates;
// Used for ScreenNetEvaluation
vector<EndOfGame_PlayerData> 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.
+6
View File
@@ -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<GetNumTracks(); t++ )
+7
View File
@@ -157,9 +157,16 @@ private:
void RemoveATIFromList(all_tracks_iterator* iter) const;
void RemoveATIFromList(all_tracks_const_iterator* iter) const;
// Mina stuf (Used for chartkey hashing)
std::vector<int> NonEmptyRowVector;
public:
void Init();
// Mina stuf (Used for chartkey hashing)
void LogNonEmptyRows();
std::vector<int>& GetNonEmptyRowVector() { return NonEmptyRowVector; };
int GetNumTracks() const { return m_TapNotes.size(); }
void SetNumTracks( int iNewNumTracks );
bool IsComposite() const;
+1 -1
View File
@@ -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 );
+5
View File
@@ -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 );
}
+29 -6
View File
@@ -181,14 +181,37 @@ void ScreenNetSelectMusic::HandleScreenMessage( const ScreenMessage SM )
vector <Song *> 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();
+28
View File
@@ -40,6 +40,8 @@
#include <set>
#include <float.h>
//-Nick12 Used for song file hashing
#include <CryptManager.h>
/**
* @brief The internal version of the cache for StepMania.
@@ -1592,6 +1594,32 @@ vector<RString> 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<RString> Song::GetInstrumentTracksToVectorString() const
{
vector<RString> ret;
+3
View File
@@ -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;
+74
View File
@@ -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<int>& 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<int>(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<int>(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. */
+8
View File
@@ -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] );