| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392 |
- /*
- ** Command & Conquer Generals(tm)
- ** Copyright 2025 Electronic Arts Inc.
- **
- ** This program is free software: you can redistribute it and/or modify
- ** it under the terms of the GNU General Public License as published by
- ** the Free Software Foundation, either version 3 of the License, or
- ** (at your option) any later version.
- **
- ** This program is distributed in the hope that it will be useful,
- ** but WITHOUT ANY WARRANTY; without even the implied warranty of
- ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- ** GNU General Public License for more details.
- **
- ** You should have received a copy of the GNU General Public License
- ** along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
- ////////////////////////////////////////////////////////////////////////////////
- // //
- // (c) 2001-2003 Electronic Arts Inc. //
- // //
- ////////////////////////////////////////////////////////////////////////////////
- // FILE: GameSpyPersistentStorage.cpp //////////////////////////////////////////////////////
- // GameSpy Persistent Storage callbacks, utils, etc
- // Author: Matthew D. Campbell, March 2002
- #include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
- #include "GameSpy/gstats/gpersist.h"
- #include "GameClient/Shell.h"
- #include "GameClient/MessageBox.h"
- #include "GameNetwork/GameSpy.h"
- #include "GameNetwork/GameSpyGP.h"
- #include "GameNetwork/GameSpyPersistentStorage.h"
- #include "GameNetwork/GameSpyThread.h"
- static Bool isProfileAuthorized = false;
- static Bool gameSpyInitPersistentStorageConnection( void );
- static void getPersistentDataCallback(int localid, int profileid, persisttype_t type, int index, int success, char *data, int len, void *instance);
- static void setPersistentDataCallback(int localid, int profileid, persisttype_t type, int index, int success, void *instance);
- class GameSpyPlayerInfo : public GameSpyPlayerInfoInterface
- {
- public:
- GameSpyPlayerInfo() { m_locale.clear(); m_wins = m_losses = m_operationCount = 0; m_shouldDisconnect = false; }
- virtual ~GameSpyPlayerInfo() { reset(); }
- virtual void init( void ) { m_locale.clear(); m_wins = m_losses = m_operationCount = 0; queueDisconnect(); };
- virtual void reset( void ) { m_locale.clear(); m_wins = m_losses = m_operationCount = 0; queueDisconnect(); };
- virtual void update( void );
- virtual AsciiString getLocale( void ) { return m_locale; }
- virtual Int getWins( void ) { return m_wins; }
- virtual Int getLosses( void ) { return m_losses; }
- virtual void setLocale( AsciiString locale, Bool setOnServer );
- virtual void setWins( Int wins, Bool setOnServer );
- virtual void setLosses( Int losses, Bool setOnServer );
- virtual void readFromServer( void );
- virtual void threadReadFromServer( void );
- virtual void threadSetLocale( AsciiString val );
- virtual void threadSetWins ( AsciiString val );
- virtual void threadSetLosses( AsciiString val );
- void queueDisconnect( void ) { MutexClass::LockClass m(TheGameSpyMutex); if (IsStatsConnected()) m_shouldDisconnect = true; else m_shouldDisconnect = false; }
- private:
- void setValue( AsciiString key, AsciiString val, Bool setOnServer );
- AsciiString m_locale;
- Int m_wins;
- Int m_losses;
- Int m_operationCount;
- Bool m_shouldDisconnect;
- };
- void GameSpyPlayerInfo::update( void )
- {
- if (IsStatsConnected())
- {
- if (m_shouldDisconnect)
- {
- DEBUG_LOG(("Persistent Storage close\n"));
- CloseStatsConnection();
- }
- else
- {
- PersistThink();
- }
- }
- }
- void GameSpyPlayerInfo::readFromServer( void )
- {
- TheGameSpyThread->queueReadPersistentStatsFromServer();
- }
- void GameSpyPlayerInfo::threadReadFromServer( void )
- {
- MutexClass::LockClass m(TheGameSpyMutex);
- if (gameSpyInitPersistentStorageConnection())
- {
- // get persistent info
- m_operationCount++;
- DEBUG_LOG(("GameSpyPlayerInfo::readFromServer() operation count = %d\n", m_operationCount));
- GetPersistDataValues(0, TheGameSpyChat->getProfileID(), pd_public_rw, 0, "\\locale\\wins\\losses", getPersistentDataCallback, &m_operationCount);
- }
- else
- {
- //TheGameSpyThread->setNextShellScreen("Menus/WOLWelcomeMenu.wnd");
- //TheShell->pop();
- //TheShell->push("Menus/WOLWelcomeMenu.wnd");
- }
- }
- void GameSpyPlayerInfo::setLocale( AsciiString locale, Bool setOnServer )
- {
- m_locale = locale;
- if (!TheGameSpyChat->getProfileID() || !setOnServer)
- return;
- setValue("locale", m_locale, setOnServer);
- }
- void GameSpyPlayerInfo::setWins( Int wins, Bool setOnServer )
- {
- m_wins = wins;
- if (!TheGameSpyChat->getProfileID() || !setOnServer)
- return;
- AsciiString winStr;
- winStr.format("%d", wins);
- setValue("wins", winStr, setOnServer);
- }
- void GameSpyPlayerInfo::setLosses( Int losses, Bool setOnServer )
- {
- m_losses = losses;
- if (!TheGameSpyChat->getProfileID() || !setOnServer)
- return;
- AsciiString lossesStr;
- lossesStr.format("%d", losses);
- setValue("losses", lossesStr, setOnServer);
- }
- void GameSpyPlayerInfo::setValue( AsciiString key, AsciiString val, Bool setOnServer )
- {
- if (!setOnServer)
- return;
- if (key == "locale")
- TheGameSpyThread->queueUpdateLocale(val);
- else if (key == "wins")
- TheGameSpyThread->queueUpdateWins(val);
- else if (key == "losses")
- TheGameSpyThread->queueUpdateLosses(val);
- }
- void GameSpyPlayerInfo::threadSetLocale( AsciiString val )
- {
- MutexClass::LockClass m(TheGameSpyMutex);
- if (!gameSpyInitPersistentStorageConnection())
- return;
- // set locale info
- AsciiString key = "locale";
- AsciiString str;
- str.format("\\%s\\%s", key.str(), val.str());
- char *writable = strdup(str.str());
- m_operationCount++;
- DEBUG_LOG(("GameSpyPlayerInfo::set%s() operation count = %d\n", key.str(), m_operationCount));
- SetPersistDataValues(0, TheGameSpyChat->getProfileID(), pd_public_rw, 0, writable, setPersistentDataCallback, &m_operationCount);
- free(writable);
- }
- void GameSpyPlayerInfo::threadSetWins( AsciiString val )
- {
- MutexClass::LockClass m(TheGameSpyMutex);
- if (!gameSpyInitPersistentStorageConnection())
- return;
- // set win info
- AsciiString key = "wins";
- AsciiString str;
- str.format("\\%s\\%s", key.str(), val.str());
- char *writable = strdup(str.str());
- m_operationCount++;
- DEBUG_LOG(("GameSpyPlayerInfo::set%s() operation count = %d\n", key.str(), m_operationCount));
- SetPersistDataValues(0, TheGameSpyChat->getProfileID(), pd_public_rw, 0, writable, setPersistentDataCallback, &m_operationCount);
- free(writable);
- }
- void GameSpyPlayerInfo::threadSetLosses( AsciiString val )
- {
- MutexClass::LockClass m(TheGameSpyMutex);
- if (!gameSpyInitPersistentStorageConnection())
- return;
- // set loss info
- AsciiString key = "losses";
- AsciiString str;
- str.format("\\%s\\%s", key.str(), val.str());
- char *writable = strdup(str.str());
- m_operationCount++;
- DEBUG_LOG(("GameSpyPlayerInfo::set%s() operation count = %d\n", key.str(), m_operationCount));
- SetPersistDataValues(0, TheGameSpyChat->getProfileID(), pd_public_rw, 0, writable, setPersistentDataCallback, &m_operationCount);
- free(writable);
- }
- GameSpyPlayerInfoInterface *TheGameSpyPlayerInfo = NULL;
- GameSpyPlayerInfoInterface *createGameSpyPlayerInfo( void )
- {
- return NEW GameSpyPlayerInfo;
- }
- static void persAuthCallback(int localid, int profileid, int authenticated, char *errmsg, void *instance)
- {
- DEBUG_LOG(("Auth callback: localid: %d profileid: %d auth: %d err: %s\n",localid, profileid, authenticated, errmsg));
- isProfileAuthorized = (authenticated != 0);
- }
- static void getPersistentDataCallback(int localid, int profileid, persisttype_t type, int index, int success, char *data, int len, void *instance)
- {
- DEBUG_LOG(("Data get callback: localid: %d profileid: %d success: %d len: %d data: %s\n",localid, profileid, success, len, data));
- if (!TheGameSpyPlayerInfo)
- {
- //TheGameSpyThread->setNextShellScreen("Menus/WOLWelcomeMenu.wnd");
- //TheShell->pop();
- //TheShell->push("Menus/WOLWelcomeMenu.wnd");
- return;
- }
- AsciiString str = data;
- AsciiString key, val;
- while (!str.isEmpty())
- {
- str.nextToken(&key, "\\");
- str.nextToken(&val, "\\");
- if (!key.isEmpty() && !val.isEmpty())
- {
- if (!key.compareNoCase("locale"))
- {
- TheGameSpyPlayerInfo->setLocale(val, false);
- }
- else if (!key.compareNoCase("wins"))
- {
- TheGameSpyPlayerInfo->setWins(atoi(val.str()), false);
- }
- else if (!key.compareNoCase("losses"))
- {
- TheGameSpyPlayerInfo->setLosses(atoi(val.str()), false);
- }
- }
- }
- // decrement count of active operations
- Int *opCount = (Int *)instance;
- (*opCount) --;
- DEBUG_LOG(("getPersistentDataCallback() operation count = %d\n", (*opCount)));
- if (!*opCount)
- {
- DEBUG_LOG(("getPersistentDataCallback() queue disconnect\n"));
- ((GameSpyPlayerInfo *)TheGameSpyPlayerInfo)->queueDisconnect();
- }
- const char *keys[3] = { "locale", "wins", "losses" };
- char valueStrings[3][20];
- char *values[3] = { valueStrings[0], valueStrings[1], valueStrings[2] };
- _snprintf(values[0], 20, "%s", TheGameSpyPlayerInfo->getLocale().str());
- _snprintf(values[1], 20, "%d", TheGameSpyPlayerInfo->getWins());
- _snprintf(values[2], 20, "%d", TheGameSpyPlayerInfo->getLosses());
- peerSetGlobalKeys(TheGameSpyChat->getPeer(), 3, (const char **)keys, (const char **)values);
- peerSetGlobalWatchKeys(TheGameSpyChat->getPeer(), GroupRoom, 3, keys, PEERTrue);
- peerSetGlobalWatchKeys(TheGameSpyChat->getPeer(), StagingRoom, 3, keys, PEERTrue);
- // choose next screen
- if (TheGameSpyPlayerInfo->getLocale().isEmpty())
- {
- TheGameSpyThread->setShowLocaleSelect(true);
- }
- }
- static void setPersistentDataCallback(int localid, int profileid, persisttype_t type, int index, int success, void *instance)
- {
- DEBUG_LOG(("Data save callback: localid: %d profileid: %d success: %d\n", localid, profileid, success));
- Int *opCount = (Int *)instance;
- (*opCount) --;
- DEBUG_LOG(("setPersistentDataCallback() operation count = %d\n", (*opCount)));
- if (!*opCount)
- {
- DEBUG_LOG(("setPersistentDataCallback() queue disconnect\n"));
- ((GameSpyPlayerInfo *)TheGameSpyPlayerInfo)->queueDisconnect();
- }
- }
- static Bool gameSpyInitPersistentStorageConnection( void )
- {
- if (IsStatsConnected())
- return true;
- isProfileAuthorized = false;
- Int result;
- /*********
- First step, set our game authentication info
- We could do:
- strcpy(gcd_gamename,"gmtest");
- strcpy(gcd_secret_key,"HA6zkS");
- ...but this is more secure:
- **********/
- gcd_gamename[0]='g';gcd_gamename[1]='m';gcd_gamename[2]='t';gcd_gamename[3]='e';
- gcd_gamename[4]='s';gcd_gamename[5]='t';gcd_gamename[6]='\0';
- gcd_secret_key[0]='H';gcd_secret_key[1]='A';gcd_secret_key[2]='6';gcd_secret_key[3]='z';
- gcd_secret_key[4]='k';gcd_secret_key[5]='S';gcd_secret_key[6]='\0';
- /*********
- Next, open the stats connection. This may block for
- a 1-2 seconds, so it should be done before the actual game starts.
- **********/
- result = InitStatsConnection(0);
- if (result != GE_NOERROR)
- {
- DEBUG_LOG(("InitStatsConnection returned %d\n",result));
- return isProfileAuthorized;
- }
- if (TheGameSpyChat->getProfileID())
- {
- char validate[33];
- /***********
- We'll go ahead and start the authentication, using a Presence & Messaging SDK
- profileid / password. To generate the new validation token, we'll need to pass
- in the password for the profile we are authenticating.
- Again, if this is done in a client/server setting, with the Persistent Storage
- access being done on the server, and the P&M SDK is used on the client, the
- server will need to send the challenge (GetChallenge(NULL)) to the client, the
- client will create the validation token using GenerateAuth, and send it
- back to the server for use in PreAuthenticatePlayerPM
- ***********/
- char *munkeeHack = strdup(TheGameSpyChat->getPassword().str()); // GenerateAuth takes a char*, not a const char* :P
- GenerateAuth(GetChallenge(NULL), munkeeHack, validate);
- free (munkeeHack);
- /************
- After we get the validation token, we pass it and the profileid of the user
- we are authenticating into PreAuthenticatePlayerPM.
- We pass the same authentication callback as for the first user, but a different
- localid this time.
- ************/
- PreAuthenticatePlayerPM(0, TheGameSpyChat->getProfileID(), validate, persAuthCallback, NULL);
- }
- else
- {
- return isProfileAuthorized;
- }
- UnsignedInt timeoutTime = timeGetTime() + 5000;
- while (!isProfileAuthorized && timeGetTime() < timeoutTime && IsStatsConnected())
- {
- PersistThink();
- msleep(10);
- }
- DEBUG_LOG(("Persistent Storage connect: %d\n", isProfileAuthorized));
- return isProfileAuthorized;
- }
|