Prevent almost all HTTP requests from blocking
The ones that remain blocking are the ones that run on different threads; see Task, yet another big mess to clean up.
This commit is contained in:
parent
c2f8a7df25
commit
c73fa1bcdd
@ -18,6 +18,12 @@ inline std::pair<Signed, Signed> floorDiv(Signed a, Signed b)
|
|||||||
return { quo, rem };
|
return { quo, rem };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<class Signed>
|
||||||
|
inline std::pair<Signed, Signed> ceilDiv(Signed a, Signed b)
|
||||||
|
{
|
||||||
|
return floorDiv(a + b - Signed(1), b);
|
||||||
|
}
|
||||||
|
|
||||||
//Linear interpolation
|
//Linear interpolation
|
||||||
template <typename T> inline T LinearInterpolate(T val1, T val2, T lowerCoord, T upperCoord, T coord)
|
template <typename T> inline T LinearInterpolate(T val1, T val2, T lowerCoord, T upperCoord, T coord)
|
||||||
{
|
{
|
||||||
|
@ -7,6 +7,8 @@
|
|||||||
#include "client/SaveFile.h"
|
#include "client/SaveFile.h"
|
||||||
#include "client/SaveInfo.h"
|
#include "client/SaveInfo.h"
|
||||||
#include "client/http/requestmanager/RequestManager.h"
|
#include "client/http/requestmanager/RequestManager.h"
|
||||||
|
#include "client/http/GetSaveRequest.h"
|
||||||
|
#include "client/http/GetSaveDataRequest.h"
|
||||||
#include "common/platform/Platform.h"
|
#include "common/platform/Platform.h"
|
||||||
#include "graphics/Graphics.h"
|
#include "graphics/Graphics.h"
|
||||||
#include "simulation/SaveRenderer.h"
|
#include "simulation/SaveRenderer.h"
|
||||||
@ -462,15 +464,31 @@ int main(int argc, char * argv[])
|
|||||||
}
|
}
|
||||||
int saveId = saveIdPart.ToNumber<int>();
|
int saveId = saveIdPart.ToNumber<int>();
|
||||||
|
|
||||||
auto newSave = Client::Ref().GetSave(saveId, 0);
|
auto getSave = std::make_unique<http::GetSaveRequest>(saveId, 0);
|
||||||
if (!newSave)
|
getSave->Start();
|
||||||
throw std::runtime_error("Could not load save info");
|
getSave->Wait();
|
||||||
auto saveData = Client::Ref().GetSaveData(saveId, 0);
|
std::unique_ptr<SaveInfo> newSave;
|
||||||
if (!saveData.size())
|
try
|
||||||
throw std::runtime_error(("Could not load save\n" + Client::Ref().GetLastError()).ToUtf8());
|
{
|
||||||
auto newGameSave = std::make_unique<GameSave>(std::move(saveData));
|
newSave = getSave->Finish();
|
||||||
newSave->SetGameSave(std::move(newGameSave));
|
}
|
||||||
|
catch (const http::RequestError &ex)
|
||||||
|
{
|
||||||
|
throw std::runtime_error("Could not load save info\n" + ByteString(ex.what()));
|
||||||
|
}
|
||||||
|
auto getSaveData = std::make_unique<http::GetSaveDataRequest>(saveId, 0);
|
||||||
|
getSaveData->Start();
|
||||||
|
getSaveData->Wait();
|
||||||
|
std::unique_ptr<GameSave> saveData;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
saveData = std::make_unique<GameSave>(getSaveData->Finish());
|
||||||
|
}
|
||||||
|
catch (const http::RequestError &ex)
|
||||||
|
{
|
||||||
|
throw std::runtime_error("Could not load save\n" + ByteString(ex.what()));
|
||||||
|
}
|
||||||
|
newSave->SetGameSave(std::move(saveData));
|
||||||
gameController->LoadSave(std::move(newSave));
|
gameController->LoadSave(std::move(newSave));
|
||||||
}
|
}
|
||||||
catch (std::exception & e)
|
catch (std::exception & e)
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
#include "Client.h"
|
#include "Client.h"
|
||||||
#include "prefs/GlobalPrefs.h"
|
#include "prefs/GlobalPrefs.h"
|
||||||
#include "client/http/Request.h"
|
#include "client/http/StartupRequest.h"
|
||||||
#include "ClientListener.h"
|
#include "ClientListener.h"
|
||||||
#include "Format.h"
|
#include "Format.h"
|
||||||
#include "MD5.h"
|
#include "MD5.h"
|
||||||
@ -13,7 +13,6 @@
|
|||||||
#include "graphics/Graphics.h"
|
#include "graphics/Graphics.h"
|
||||||
#include "prefs/Prefs.h"
|
#include "prefs/Prefs.h"
|
||||||
#include "lua/CommandInterface.h"
|
#include "lua/CommandInterface.h"
|
||||||
#include "gui/preview/Comment.h"
|
|
||||||
#include "Config.h"
|
#include "Config.h"
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
@ -29,8 +28,6 @@
|
|||||||
|
|
||||||
Client::Client():
|
Client::Client():
|
||||||
messageOfTheDay("Fetching the message of the day..."),
|
messageOfTheDay("Fetching the message of the day..."),
|
||||||
versionCheckRequest(nullptr),
|
|
||||||
alternateVersionCheckRequest(nullptr),
|
|
||||||
usingAltUpdateServer(false),
|
usingAltUpdateServer(false),
|
||||||
updateAvailable(false),
|
updateAvailable(false),
|
||||||
authUser(0, "")
|
authUser(0, "")
|
||||||
@ -40,16 +37,7 @@ Client::Client():
|
|||||||
authUser.Username = prefs.Get("User.Username", ByteString(""));
|
authUser.Username = prefs.Get("User.Username", ByteString(""));
|
||||||
authUser.SessionID = prefs.Get("User.SessionID", ByteString(""));
|
authUser.SessionID = prefs.Get("User.SessionID", ByteString(""));
|
||||||
authUser.SessionKey = prefs.Get("User.SessionKey", ByteString(""));
|
authUser.SessionKey = prefs.Get("User.SessionKey", ByteString(""));
|
||||||
auto elevation = prefs.Get("User.Elevation", ByteString(""));
|
authUser.UserElevation = prefs.Get("User.Elevation", User::ElevationNone);
|
||||||
authUser.UserElevation = User::ElevationNone;
|
|
||||||
if (elevation == "Admin")
|
|
||||||
{
|
|
||||||
authUser.UserElevation = User::ElevationAdmin;
|
|
||||||
}
|
|
||||||
if (elevation == "Mod")
|
|
||||||
{
|
|
||||||
authUser.UserElevation = User::ElevationModerator;
|
|
||||||
}
|
|
||||||
firstRun = !prefs.BackedByFile();
|
firstRun = !prefs.BackedByFile();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -88,24 +76,14 @@ void Client::Initialize()
|
|||||||
}
|
}
|
||||||
|
|
||||||
//Begin version check
|
//Begin version check
|
||||||
versionCheckRequest = std::make_unique<http::Request>(ByteString::Build(SCHEME, SERVER, "/Startup.json"));
|
versionCheckRequest = std::make_unique<http::StartupRequest>(false);
|
||||||
|
|
||||||
if (authUser.UserID)
|
|
||||||
{
|
|
||||||
versionCheckRequest->AuthHeaders(ByteString::Build(authUser.UserID), authUser.SessionID);
|
|
||||||
}
|
|
||||||
versionCheckRequest->Start();
|
versionCheckRequest->Start();
|
||||||
|
|
||||||
if constexpr (USE_UPDATESERVER)
|
if constexpr (USE_UPDATESERVER)
|
||||||
{
|
{
|
||||||
// use an alternate update server
|
// use an alternate update server
|
||||||
alternateVersionCheckRequest = std::make_unique<http::Request>(ByteString::Build(SCHEME, UPDATESERVER, "/Startup.json"));
|
alternateVersionCheckRequest = std::make_unique<http::StartupRequest>(true);
|
||||||
usingAltUpdateServer = true;
|
|
||||||
if (authUser.UserID)
|
|
||||||
{
|
|
||||||
alternateVersionCheckRequest->AuthHeaders(authUser.Username, "");
|
|
||||||
}
|
|
||||||
alternateVersionCheckRequest->Start();
|
alternateVersionCheckRequest->Start();
|
||||||
|
usingAltUpdateServer = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -125,203 +103,82 @@ String Client::GetMessageOfTheDay()
|
|||||||
return messageOfTheDay;
|
return messageOfTheDay;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Client::AddServerNotification(std::pair<String, ByteString> notification)
|
void Client::AddServerNotification(ServerNotification notification)
|
||||||
{
|
{
|
||||||
serverNotifications.push_back(notification);
|
serverNotifications.push_back(notification);
|
||||||
notifyNewNotification(notification);
|
notifyNewNotification(notification);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<std::pair<String, ByteString> > Client::GetServerNotifications()
|
std::vector<ServerNotification> Client::GetServerNotifications()
|
||||||
{
|
{
|
||||||
return serverNotifications;
|
return serverNotifications;
|
||||||
}
|
}
|
||||||
|
|
||||||
RequestStatus Client::ParseServerReturn(ByteString &result, int status, bool json)
|
|
||||||
{
|
|
||||||
lastError = "";
|
|
||||||
// no server response, return "Malformed Response"
|
|
||||||
if (status == 200 && !result.size())
|
|
||||||
{
|
|
||||||
status = 603;
|
|
||||||
}
|
|
||||||
if (status == 302)
|
|
||||||
return RequestOkay;
|
|
||||||
if (status != 200)
|
|
||||||
{
|
|
||||||
lastError = String::Build("HTTP Error ", status, ": ", http::StatusText(status));
|
|
||||||
return RequestFailure;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (json)
|
|
||||||
{
|
|
||||||
std::istringstream datastream(result);
|
|
||||||
Json::Value root;
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
datastream >> root;
|
|
||||||
// assume everything is fine if an empty [] is returned
|
|
||||||
if (root.size() == 0)
|
|
||||||
{
|
|
||||||
return RequestOkay;
|
|
||||||
}
|
|
||||||
int status = root.get("Status", 1).asInt();
|
|
||||||
if (status != 1)
|
|
||||||
{
|
|
||||||
lastError = ByteString(root.get("Error", "Unspecified Error").asString()).FromUtf8();
|
|
||||||
return RequestFailure;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (std::exception &e)
|
|
||||||
{
|
|
||||||
// sometimes the server returns a 200 with the text "Error: 401"
|
|
||||||
if (!strncmp(result.c_str(), "Error: ", 7))
|
|
||||||
{
|
|
||||||
status = ByteString(result.begin() + 7, result.end()).ToNumber<int>();
|
|
||||||
lastError = String::Build("HTTP Error ", status, ": ", http::StatusText(status));
|
|
||||||
return RequestFailure;
|
|
||||||
}
|
|
||||||
lastError = "Could not read response: " + ByteString(e.what()).FromUtf8();
|
|
||||||
return RequestFailure;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (strncmp(result.c_str(), "OK", 2))
|
|
||||||
{
|
|
||||||
lastError = result.FromUtf8();
|
|
||||||
return RequestFailure;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return RequestOkay;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Client::Tick()
|
void Client::Tick()
|
||||||
{
|
{
|
||||||
CheckUpdate(versionCheckRequest, true);
|
auto applyUpdateInfo = false;
|
||||||
CheckUpdate(alternateVersionCheckRequest, false);
|
if (versionCheckRequest && versionCheckRequest->CheckDone())
|
||||||
}
|
|
||||||
|
|
||||||
void Client::CheckUpdate(std::unique_ptr<http::Request> &updateRequest, bool checkSession)
|
|
||||||
{
|
|
||||||
//Check status on version check request
|
|
||||||
if (updateRequest && updateRequest->CheckDone())
|
|
||||||
{
|
{
|
||||||
auto [ status, data ] = updateRequest->Finish();
|
if (versionCheckRequest->StatusCode() == 618)
|
||||||
|
|
||||||
if (checkSession && status == 618)
|
|
||||||
{
|
{
|
||||||
AddServerNotification({ "Failed to load SSL certificates", ByteString(SCHEME) + "powdertoy.co.uk/FAQ.html" });
|
AddServerNotification({ "Failed to load SSL certificates", ByteString(SCHEME) + "powdertoy.co.uk/FAQ.html" });
|
||||||
}
|
}
|
||||||
|
try
|
||||||
if (status != 200)
|
|
||||||
{
|
{
|
||||||
//free(data);
|
auto info = versionCheckRequest->Finish();
|
||||||
if (usingAltUpdateServer && !checkSession)
|
if (!info.sessionGood)
|
||||||
this->messageOfTheDay = String::Build("HTTP Error ", status, " while checking for updates: ", http::StatusText(status));
|
|
||||||
else
|
|
||||||
this->messageOfTheDay = String::Build("HTTP Error ", status, " while fetching MotD");
|
|
||||||
}
|
|
||||||
else if(data.size())
|
|
||||||
{
|
|
||||||
std::istringstream dataStream(data);
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
{
|
||||||
Json::Value objDocument;
|
SetAuthUser(User(0, ""));
|
||||||
dataStream >> objDocument;
|
|
||||||
|
|
||||||
//Check session
|
|
||||||
if (checkSession)
|
|
||||||
{
|
|
||||||
if (!objDocument["Session"].asBool())
|
|
||||||
{
|
|
||||||
SetAuthUser(User(0, ""));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//Notifications from server
|
|
||||||
Json::Value notificationsArray = objDocument["Notifications"];
|
|
||||||
for (Json::UInt j = 0; j < notificationsArray.size(); j++)
|
|
||||||
{
|
|
||||||
ByteString notificationLink = notificationsArray[j]["Link"].asString();
|
|
||||||
String notificationText = ByteString(notificationsArray[j]["Text"].asString()).FromUtf8();
|
|
||||||
|
|
||||||
std::pair<String, ByteString> item = std::pair<String, ByteString>(notificationText, notificationLink);
|
|
||||||
AddServerNotification(item);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
//MOTD
|
|
||||||
if (!usingAltUpdateServer || !checkSession)
|
|
||||||
{
|
|
||||||
this->messageOfTheDay = ByteString(objDocument["MessageOfTheDay"].asString()).FromUtf8();
|
|
||||||
notifyMessageOfTheDay();
|
|
||||||
|
|
||||||
if constexpr (!IGNORE_UPDATES)
|
|
||||||
{
|
|
||||||
//Check for updates
|
|
||||||
Json::Value versions = objDocument["Updates"];
|
|
||||||
if constexpr (!SNAPSHOT)
|
|
||||||
{
|
|
||||||
Json::Value stableVersion = versions["Stable"];
|
|
||||||
int stableMajor = stableVersion["Major"].asInt();
|
|
||||||
int stableMinor = stableVersion["Minor"].asInt();
|
|
||||||
int stableBuild = stableVersion["Build"].asInt();
|
|
||||||
ByteString stableFile = stableVersion["File"].asString();
|
|
||||||
String stableChangelog = ByteString(stableVersion["Changelog"].asString()).FromUtf8();
|
|
||||||
if (stableBuild > BUILD_NUM)
|
|
||||||
{
|
|
||||||
updateAvailable = true;
|
|
||||||
updateInfo = UpdateInfo(stableMajor, stableMinor, stableBuild, stableFile, stableChangelog, UpdateInfo::Stable);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!updateAvailable)
|
|
||||||
{
|
|
||||||
Json::Value betaVersion = versions["Beta"];
|
|
||||||
int betaMajor = betaVersion["Major"].asInt();
|
|
||||||
int betaMinor = betaVersion["Minor"].asInt();
|
|
||||||
int betaBuild = betaVersion["Build"].asInt();
|
|
||||||
ByteString betaFile = betaVersion["File"].asString();
|
|
||||||
String betaChangelog = ByteString(betaVersion["Changelog"].asString()).FromUtf8();
|
|
||||||
if (betaBuild > BUILD_NUM)
|
|
||||||
{
|
|
||||||
updateAvailable = true;
|
|
||||||
updateInfo = UpdateInfo(betaMajor, betaMinor, betaBuild, betaFile, betaChangelog, UpdateInfo::Beta);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if constexpr (SNAPSHOT || MOD)
|
|
||||||
{
|
|
||||||
Json::Value snapshotVersion = versions["Snapshot"];
|
|
||||||
int snapshotSnapshot = snapshotVersion["Snapshot"].asInt();
|
|
||||||
ByteString snapshotFile = snapshotVersion["File"].asString();
|
|
||||||
String snapshotChangelog = ByteString(snapshotVersion["Changelog"].asString()).FromUtf8();
|
|
||||||
if (snapshotSnapshot > SNAPSHOT_ID)
|
|
||||||
{
|
|
||||||
updateAvailable = true;
|
|
||||||
updateInfo = UpdateInfo(snapshotSnapshot, snapshotFile, snapshotChangelog, UpdateInfo::Snapshot);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(updateAvailable)
|
|
||||||
{
|
|
||||||
notifyUpdateAvailable();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
catch (std::exception & e)
|
if (!usingAltUpdateServer)
|
||||||
{
|
{
|
||||||
//Do nothing
|
updateInfo = info.updateInfo;
|
||||||
|
applyUpdateInfo = true;
|
||||||
|
messageOfTheDay = info.messageOfTheDay;
|
||||||
|
}
|
||||||
|
for (auto ¬ification : info.notifications)
|
||||||
|
{
|
||||||
|
AddServerNotification(notification);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
updateRequest.reset();
|
catch (const http::RequestError &ex)
|
||||||
|
{
|
||||||
|
if (!usingAltUpdateServer)
|
||||||
|
{
|
||||||
|
messageOfTheDay = ByteString::Build("Error while fetching MotD: ", ex.what()).FromUtf8();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
versionCheckRequest.reset();
|
||||||
|
}
|
||||||
|
if (alternateVersionCheckRequest && alternateVersionCheckRequest->CheckDone())
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
auto info = alternateVersionCheckRequest->Finish();
|
||||||
|
updateInfo = info.updateInfo;
|
||||||
|
applyUpdateInfo = true;
|
||||||
|
messageOfTheDay = info.messageOfTheDay;
|
||||||
|
for (auto ¬ification : info.notifications)
|
||||||
|
{
|
||||||
|
AddServerNotification(notification);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (const http::RequestError &ex)
|
||||||
|
{
|
||||||
|
messageOfTheDay = ByteString::Build("Error while checking for updates: ", ex.what()).FromUtf8();
|
||||||
|
}
|
||||||
|
alternateVersionCheckRequest.reset();
|
||||||
|
}
|
||||||
|
if (applyUpdateInfo && !IGNORE_UPDATES)
|
||||||
|
{
|
||||||
|
if (updateInfo)
|
||||||
|
{
|
||||||
|
notifyUpdateAvailable();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
UpdateInfo Client::GetUpdateInfo()
|
std::optional<UpdateInfo> Client::GetUpdateInfo()
|
||||||
{
|
{
|
||||||
return updateInfo;
|
return updateInfo;
|
||||||
}
|
}
|
||||||
@ -350,7 +207,7 @@ void Client::notifyAuthUserChanged()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Client::notifyNewNotification(std::pair<String, ByteString> notification)
|
void Client::notifyNewNotification(ServerNotification notification)
|
||||||
{
|
{
|
||||||
for (std::vector<ClientListener*>::iterator iterator = listeners.begin(), end = listeners.end(); iterator != end; ++iterator)
|
for (std::vector<ClientListener*>::iterator iterator = listeners.begin(), end = listeners.end(); iterator != end; ++iterator)
|
||||||
{
|
{
|
||||||
@ -391,16 +248,7 @@ void Client::SetAuthUser(User user)
|
|||||||
prefs.Set("User.SessionID", authUser.SessionID);
|
prefs.Set("User.SessionID", authUser.SessionID);
|
||||||
prefs.Set("User.SessionKey", authUser.SessionKey);
|
prefs.Set("User.SessionKey", authUser.SessionKey);
|
||||||
prefs.Set("User.Username", authUser.Username);
|
prefs.Set("User.Username", authUser.Username);
|
||||||
ByteString elevation = "None";
|
prefs.Set("User.Elevation", authUser.UserElevation);
|
||||||
if (authUser.UserElevation == User::ElevationAdmin)
|
|
||||||
{
|
|
||||||
elevation = "Admin";
|
|
||||||
}
|
|
||||||
if (authUser.UserElevation == User::ElevationModerator)
|
|
||||||
{
|
|
||||||
elevation = "Mod";
|
|
||||||
}
|
|
||||||
prefs.Set("User.Elevation", elevation);
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -415,65 +263,6 @@ User Client::GetAuthUser()
|
|||||||
return authUser;
|
return authUser;
|
||||||
}
|
}
|
||||||
|
|
||||||
RequestStatus Client::UploadSave(SaveInfo & save)
|
|
||||||
{
|
|
||||||
lastError = "";
|
|
||||||
int dataStatus;
|
|
||||||
ByteString data;
|
|
||||||
ByteString userID = ByteString::Build(authUser.UserID);
|
|
||||||
if (authUser.UserID)
|
|
||||||
{
|
|
||||||
if (!save.GetGameSave())
|
|
||||||
{
|
|
||||||
lastError = "Empty game save";
|
|
||||||
return RequestFailure;
|
|
||||||
}
|
|
||||||
|
|
||||||
save.SetID(0);
|
|
||||||
|
|
||||||
auto [ fromNewerVersion, gameData ] = save.GetGameSave()->Serialise();
|
|
||||||
(void)fromNewerVersion;
|
|
||||||
|
|
||||||
if (!gameData.size())
|
|
||||||
{
|
|
||||||
lastError = "Cannot serialize game save";
|
|
||||||
return RequestFailure;
|
|
||||||
}
|
|
||||||
else if (ALLOW_FAKE_NEWER_VERSION && fromNewerVersion && save.GetPublished())
|
|
||||||
{
|
|
||||||
lastError = "Cannot publish save, incompatible with latest release version.";
|
|
||||||
return RequestFailure;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::tie(dataStatus, data) = http::Request::SimpleAuth(ByteString::Build(SCHEME, SERVER, "/Save.api"), userID, authUser.SessionID, {
|
|
||||||
{ "Name", save.GetName().ToUtf8() },
|
|
||||||
{ "Description", save.GetDescription().ToUtf8() },
|
|
||||||
{ "Data:save.bin", ByteString(gameData.begin(), gameData.end()) },
|
|
||||||
{ "Publish", save.GetPublished() ? "Public" : "Private" },
|
|
||||||
{ "Key", authUser.SessionKey }
|
|
||||||
});
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
lastError = "Not authenticated";
|
|
||||||
return RequestFailure;
|
|
||||||
}
|
|
||||||
|
|
||||||
RequestStatus ret = ParseServerReturn(data, dataStatus, false);
|
|
||||||
if (ret == RequestOkay)
|
|
||||||
{
|
|
||||||
int saveID = ByteString(data.begin() + 3, data.end()).ToNumber<int>();
|
|
||||||
if (!saveID)
|
|
||||||
{
|
|
||||||
lastError = "Server did not return Save ID";
|
|
||||||
ret = RequestFailure;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
save.SetID(saveID);
|
|
||||||
}
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Client::MoveStampToFront(ByteString stampID)
|
void Client::MoveStampToFront(ByteString stampID)
|
||||||
{
|
{
|
||||||
auto it = std::find(stampIDs.begin(), stampIDs.end(), stampID);
|
auto it = std::find(stampIDs.begin(), stampIDs.end(), stampID);
|
||||||
@ -618,312 +407,6 @@ const std::vector<ByteString> &Client::GetStamps() const
|
|||||||
return stampIDs;
|
return stampIDs;
|
||||||
}
|
}
|
||||||
|
|
||||||
RequestStatus Client::ExecVote(int saveID, int direction)
|
|
||||||
{
|
|
||||||
lastError = "";
|
|
||||||
int dataStatus;
|
|
||||||
ByteString data;
|
|
||||||
|
|
||||||
if (authUser.UserID)
|
|
||||||
{
|
|
||||||
ByteString saveIDText = ByteString::Build(saveID);
|
|
||||||
ByteString userIDText = ByteString::Build(authUser.UserID);
|
|
||||||
std::tie(dataStatus, data) = http::Request::SimpleAuth(ByteString::Build(SCHEME, SERVER, "/Vote.api"), userIDText, authUser.SessionID, {
|
|
||||||
{ "ID", saveIDText },
|
|
||||||
{ "Action", direction ? (direction == 1 ? "Up" : "Down") : "Reset" },
|
|
||||||
{ "Key", authUser.SessionKey }
|
|
||||||
});
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
lastError = "Not authenticated";
|
|
||||||
return RequestFailure;
|
|
||||||
}
|
|
||||||
RequestStatus ret = ParseServerReturn(data, dataStatus, false);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<char> Client::GetSaveData(int saveID, int saveDate)
|
|
||||||
{
|
|
||||||
lastError = "";
|
|
||||||
ByteString urlStr;
|
|
||||||
if (saveDate)
|
|
||||||
urlStr = ByteString::Build(STATICSCHEME, STATICSERVER, "/", saveID, "_", saveDate, ".cps");
|
|
||||||
else
|
|
||||||
urlStr = ByteString::Build(STATICSCHEME, STATICSERVER, "/", saveID, ".cps");
|
|
||||||
|
|
||||||
auto [ dataStatus, data ] = http::Request::Simple(urlStr);
|
|
||||||
|
|
||||||
// will always return failure
|
|
||||||
ParseServerReturn(data, dataStatus, false);
|
|
||||||
if (data.size() && dataStatus == 200)
|
|
||||||
{
|
|
||||||
return std::vector<char>(data.begin(), data.end());
|
|
||||||
}
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
LoginStatus Client::Login(ByteString username, ByteString password, User & user)
|
|
||||||
{
|
|
||||||
lastError = "";
|
|
||||||
|
|
||||||
user.UserID = 0;
|
|
||||||
user.Username = "";
|
|
||||||
user.SessionID = "";
|
|
||||||
user.SessionKey = "";
|
|
||||||
|
|
||||||
auto [ dataStatus, data ] = http::Request::Simple(ByteString::Build("https://", SERVER, "/Login.json"), {
|
|
||||||
{ "name", username },
|
|
||||||
{ "pass", password },
|
|
||||||
});
|
|
||||||
|
|
||||||
RequestStatus ret = ParseServerReturn(data, dataStatus, true);
|
|
||||||
if (ret == RequestOkay)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
std::istringstream dataStream(data);
|
|
||||||
Json::Value objDocument;
|
|
||||||
dataStream >> objDocument;
|
|
||||||
|
|
||||||
ByteString usernameTemp = objDocument["Username"].asString();
|
|
||||||
int userIDTemp = objDocument["UserID"].asInt();
|
|
||||||
ByteString sessionIDTemp = objDocument["SessionID"].asString();
|
|
||||||
ByteString sessionKeyTemp = objDocument["SessionKey"].asString();
|
|
||||||
ByteString userElevationTemp = objDocument["Elevation"].asString();
|
|
||||||
|
|
||||||
Json::Value notificationsArray = objDocument["Notifications"];
|
|
||||||
for (Json::UInt j = 0; j < notificationsArray.size(); j++)
|
|
||||||
{
|
|
||||||
ByteString notificationLink = notificationsArray[j]["Link"].asString();
|
|
||||||
String notificationText = ByteString(notificationsArray[j]["Text"].asString()).FromUtf8();
|
|
||||||
|
|
||||||
std::pair<String, ByteString> item = std::pair<String, ByteString>(notificationText, notificationLink);
|
|
||||||
AddServerNotification(item);
|
|
||||||
}
|
|
||||||
|
|
||||||
user.Username = usernameTemp;
|
|
||||||
user.UserID = userIDTemp;
|
|
||||||
user.SessionID = sessionIDTemp;
|
|
||||||
user.SessionKey = sessionKeyTemp;
|
|
||||||
ByteString userElevation = userElevationTemp;
|
|
||||||
if(userElevation == "Admin")
|
|
||||||
user.UserElevation = User::ElevationAdmin;
|
|
||||||
else if(userElevation == "Mod")
|
|
||||||
user.UserElevation = User::ElevationModerator;
|
|
||||||
else
|
|
||||||
user.UserElevation= User::ElevationNone;
|
|
||||||
return LoginOkay;
|
|
||||||
}
|
|
||||||
catch (std::exception &e)
|
|
||||||
{
|
|
||||||
lastError = "Could not read response: " + ByteString(e.what()).FromUtf8();
|
|
||||||
return LoginError;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return LoginError;
|
|
||||||
}
|
|
||||||
|
|
||||||
RequestStatus Client::DeleteSave(int saveID)
|
|
||||||
{
|
|
||||||
lastError = "";
|
|
||||||
ByteString data;
|
|
||||||
ByteString url = ByteString::Build(SCHEME, SERVER, "/Browse/Delete.json?ID=", saveID, "&Mode=Delete&Key=", authUser.SessionKey);
|
|
||||||
int dataStatus;
|
|
||||||
if(authUser.UserID)
|
|
||||||
{
|
|
||||||
ByteString userID = ByteString::Build(authUser.UserID);
|
|
||||||
std::tie(dataStatus, data) = http::Request::SimpleAuth(url, userID, authUser.SessionID);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
lastError = "Not authenticated";
|
|
||||||
return RequestFailure;
|
|
||||||
}
|
|
||||||
RequestStatus ret = ParseServerReturn(data, dataStatus, true);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
RequestStatus Client::AddComment(int saveID, String comment)
|
|
||||||
{
|
|
||||||
lastError = "";
|
|
||||||
ByteString data;
|
|
||||||
int dataStatus;
|
|
||||||
ByteString url = ByteString::Build(SCHEME, SERVER, "/Browse/Comments.json?ID=", saveID);
|
|
||||||
if(authUser.UserID)
|
|
||||||
{
|
|
||||||
ByteString userID = ByteString::Build(authUser.UserID);
|
|
||||||
std::tie(dataStatus, data) = http::Request::SimpleAuth(url, userID, authUser.SessionID, {
|
|
||||||
{ "Comment", comment.ToUtf8() },
|
|
||||||
{ "Key", authUser.SessionKey }
|
|
||||||
});
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
lastError = "Not authenticated";
|
|
||||||
return RequestFailure;
|
|
||||||
}
|
|
||||||
RequestStatus ret = ParseServerReturn(data, dataStatus, true);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
RequestStatus Client::FavouriteSave(int saveID, bool favourite)
|
|
||||||
{
|
|
||||||
lastError = "";
|
|
||||||
ByteStringBuilder urlStream;
|
|
||||||
ByteString data;
|
|
||||||
int dataStatus;
|
|
||||||
urlStream << SCHEME << SERVER << "/Browse/Favourite.json?ID=" << saveID << "&Key=" << authUser.SessionKey;
|
|
||||||
if(!favourite)
|
|
||||||
urlStream << "&Mode=Remove";
|
|
||||||
if(authUser.UserID)
|
|
||||||
{
|
|
||||||
ByteString userID = ByteString::Build(authUser.UserID);
|
|
||||||
std::tie(dataStatus, data) = http::Request::SimpleAuth(urlStream.Build(), userID, authUser.SessionID);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
lastError = "Not authenticated";
|
|
||||||
return RequestFailure;
|
|
||||||
}
|
|
||||||
RequestStatus ret = ParseServerReturn(data, dataStatus, true);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
RequestStatus Client::ReportSave(int saveID, String message)
|
|
||||||
{
|
|
||||||
lastError = "";
|
|
||||||
ByteString data;
|
|
||||||
int dataStatus;
|
|
||||||
ByteString url = ByteString::Build(SCHEME, SERVER, "/Browse/Report.json?ID=", saveID, "&Key=", authUser.SessionKey);
|
|
||||||
if(authUser.UserID)
|
|
||||||
{
|
|
||||||
ByteString userID = ByteString::Build(authUser.UserID);
|
|
||||||
std::tie(dataStatus, data) = http::Request::SimpleAuth(url, userID, authUser.SessionID, {
|
|
||||||
{ "Reason", message.ToUtf8() },
|
|
||||||
});
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
lastError = "Not authenticated";
|
|
||||||
return RequestFailure;
|
|
||||||
}
|
|
||||||
RequestStatus ret = ParseServerReturn(data, dataStatus, true);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
RequestStatus Client::UnpublishSave(int saveID)
|
|
||||||
{
|
|
||||||
lastError = "";
|
|
||||||
ByteString data;
|
|
||||||
int dataStatus;
|
|
||||||
ByteString url = ByteString::Build(SCHEME, SERVER, "/Browse/Delete.json?ID=", saveID, "&Mode=Unpublish&Key=", authUser.SessionKey);
|
|
||||||
if(authUser.UserID)
|
|
||||||
{
|
|
||||||
ByteString userID = ByteString::Build(authUser.UserID);
|
|
||||||
std::tie(dataStatus, data) = http::Request::SimpleAuth(url, userID, authUser.SessionID);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
lastError = "Not authenticated";
|
|
||||||
return RequestFailure;
|
|
||||||
}
|
|
||||||
RequestStatus ret = ParseServerReturn(data, dataStatus, true);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
RequestStatus Client::PublishSave(int saveID)
|
|
||||||
{
|
|
||||||
lastError = "";
|
|
||||||
ByteString data;
|
|
||||||
int dataStatus;
|
|
||||||
ByteString url = ByteString::Build(SCHEME, SERVER, "/Browse/View.json?ID=", saveID, "&Key=", authUser.SessionKey);
|
|
||||||
if (authUser.UserID)
|
|
||||||
{
|
|
||||||
ByteString userID = ByteString::Build(authUser.UserID);
|
|
||||||
std::tie(dataStatus, data) = http::Request::SimpleAuth(url, userID, authUser.SessionID, {
|
|
||||||
{ "ActionPublish", "bagels" },
|
|
||||||
});
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
lastError = "Not authenticated";
|
|
||||||
return RequestFailure;
|
|
||||||
}
|
|
||||||
RequestStatus ret = ParseServerReturn(data, dataStatus, true);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::unique_ptr<SaveInfo> Client::GetSave(int saveID, int saveDate)
|
|
||||||
{
|
|
||||||
lastError = "";
|
|
||||||
ByteStringBuilder urlStream;
|
|
||||||
urlStream << SCHEME << SERVER << "/Browse/View.json?ID=" << saveID;
|
|
||||||
if(saveDate)
|
|
||||||
{
|
|
||||||
urlStream << "&Date=" << saveDate;
|
|
||||||
}
|
|
||||||
ByteString data;
|
|
||||||
int dataStatus;
|
|
||||||
if(authUser.UserID)
|
|
||||||
{
|
|
||||||
ByteString userID = ByteString::Build(authUser.UserID);
|
|
||||||
std::tie(dataStatus, data) = http::Request::SimpleAuth(urlStream.Build(), userID, authUser.SessionID);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
std::tie(dataStatus, data) = http::Request::Simple(urlStream.Build());
|
|
||||||
}
|
|
||||||
if(dataStatus == 200 && data.size())
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
std::istringstream dataStream(data);
|
|
||||||
Json::Value objDocument;
|
|
||||||
dataStream >> objDocument;
|
|
||||||
|
|
||||||
int tempID = objDocument["ID"].asInt();
|
|
||||||
int tempScoreUp = objDocument["ScoreUp"].asInt();
|
|
||||||
int tempScoreDown = objDocument["ScoreDown"].asInt();
|
|
||||||
int tempMyScore = objDocument["ScoreMine"].asInt();
|
|
||||||
ByteString tempUsername = objDocument["Username"].asString();
|
|
||||||
String tempName = ByteString(objDocument["Name"].asString()).FromUtf8();
|
|
||||||
String tempDescription = ByteString(objDocument["Description"].asString()).FromUtf8();
|
|
||||||
int tempCreatedDate = objDocument["DateCreated"].asInt();
|
|
||||||
int tempUpdatedDate = objDocument["Date"].asInt();
|
|
||||||
bool tempPublished = objDocument["Published"].asBool();
|
|
||||||
bool tempFavourite = objDocument["Favourite"].asBool();
|
|
||||||
int tempComments = objDocument["Comments"].asInt();
|
|
||||||
int tempViews = objDocument["Views"].asInt();
|
|
||||||
int tempVersion = objDocument["Version"].asInt();
|
|
||||||
|
|
||||||
Json::Value tagsArray = objDocument["Tags"];
|
|
||||||
std::list<ByteString> tempTags;
|
|
||||||
for (Json::UInt j = 0; j < tagsArray.size(); j++)
|
|
||||||
tempTags.push_back(tagsArray[j].asString());
|
|
||||||
|
|
||||||
auto tempSave = std::make_unique<SaveInfo>(tempID, tempCreatedDate, tempUpdatedDate, tempScoreUp,
|
|
||||||
tempScoreDown, tempMyScore, tempUsername, tempName,
|
|
||||||
tempDescription, tempPublished, tempTags);
|
|
||||||
tempSave->Comments = tempComments;
|
|
||||||
tempSave->Favourite = tempFavourite;
|
|
||||||
tempSave->Views = tempViews;
|
|
||||||
tempSave->Version = tempVersion;
|
|
||||||
return tempSave;
|
|
||||||
}
|
|
||||||
catch (std::exception & e)
|
|
||||||
{
|
|
||||||
lastError = "Could not read response: " + ByteString(e.what()).FromUtf8();
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
lastError = http::StatusText(dataStatus);
|
|
||||||
}
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::unique_ptr<SaveFile> Client::LoadSaveFile(ByteString filename)
|
std::unique_ptr<SaveFile> Client::LoadSaveFile(ByteString filename)
|
||||||
{
|
{
|
||||||
ByteString err;
|
ByteString err;
|
||||||
@ -964,84 +447,6 @@ std::unique_ptr<SaveFile> Client::LoadSaveFile(ByteString filename)
|
|||||||
return file;
|
return file;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::list<ByteString> * Client::RemoveTag(int saveID, ByteString tag)
|
|
||||||
{
|
|
||||||
lastError = "";
|
|
||||||
std::list<ByteString> * tags = NULL;
|
|
||||||
ByteString data;
|
|
||||||
int dataStatus;
|
|
||||||
ByteString url = ByteString::Build(SCHEME, SERVER, "/Browse/EditTag.json?Op=delete&ID=", saveID, "&Tag=", tag, "&Key=", authUser.SessionKey);
|
|
||||||
if(authUser.UserID)
|
|
||||||
{
|
|
||||||
ByteString userID = ByteString::Build(authUser.UserID);
|
|
||||||
std::tie(dataStatus, data) = http::Request::SimpleAuth(url, userID, authUser.SessionID);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
lastError = "Not authenticated";
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
RequestStatus ret = ParseServerReturn(data, dataStatus, true);
|
|
||||||
if (ret == RequestOkay)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
std::istringstream dataStream(data);
|
|
||||||
Json::Value responseObject;
|
|
||||||
dataStream >> responseObject;
|
|
||||||
|
|
||||||
Json::Value tagsArray = responseObject["Tags"];
|
|
||||||
tags = new std::list<ByteString>();
|
|
||||||
for (Json::UInt j = 0; j < tagsArray.size(); j++)
|
|
||||||
tags->push_back(tagsArray[j].asString());
|
|
||||||
}
|
|
||||||
catch (std::exception &e)
|
|
||||||
{
|
|
||||||
lastError = "Could not read response: " + ByteString(e.what()).FromUtf8();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return tags;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::list<ByteString> * Client::AddTag(int saveID, ByteString tag)
|
|
||||||
{
|
|
||||||
lastError = "";
|
|
||||||
std::list<ByteString> * tags = NULL;
|
|
||||||
ByteString data;
|
|
||||||
int dataStatus;
|
|
||||||
ByteString url = ByteString::Build(SCHEME, SERVER, "/Browse/EditTag.json?Op=add&ID=", saveID, "&Tag=", tag, "&Key=", authUser.SessionKey);
|
|
||||||
if(authUser.UserID)
|
|
||||||
{
|
|
||||||
ByteString userID = ByteString::Build(authUser.UserID);
|
|
||||||
std::tie(dataStatus, data) = http::Request::SimpleAuth(url, userID, authUser.SessionID);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
lastError = "Not authenticated";
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
RequestStatus ret = ParseServerReturn(data, dataStatus, true);
|
|
||||||
if (ret == RequestOkay)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
std::istringstream dataStream(data);
|
|
||||||
Json::Value responseObject;
|
|
||||||
dataStream >> responseObject;
|
|
||||||
|
|
||||||
Json::Value tagsArray = responseObject["Tags"];
|
|
||||||
tags = new std::list<ByteString>();
|
|
||||||
for (Json::UInt j = 0; j < tagsArray.size(); j++)
|
|
||||||
tags->push_back(tagsArray[j].asString());
|
|
||||||
}
|
|
||||||
catch (std::exception & e)
|
|
||||||
{
|
|
||||||
lastError = "Could not read response: " + ByteString(e.what()).FromUtf8();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return tags;
|
|
||||||
}
|
|
||||||
|
|
||||||
// stamp-specific wrapper for MergeAuthorInfo
|
// stamp-specific wrapper for MergeAuthorInfo
|
||||||
// also used for clipboard and lua stamps
|
// also used for clipboard and lua stamps
|
||||||
void Client::MergeStampAuthorInfo(Json::Value stampAuthors)
|
void Client::MergeStampAuthorInfo(Json::Value stampAuthors)
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include "common/String.h"
|
#include "common/String.h"
|
||||||
#include "common/ExplicitSingleton.h"
|
#include "common/ExplicitSingleton.h"
|
||||||
|
#include "StartupInfo.h"
|
||||||
#include "User.h"
|
#include "User.h"
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
@ -10,53 +11,27 @@
|
|||||||
|
|
||||||
class SaveInfo;
|
class SaveInfo;
|
||||||
class SaveFile;
|
class SaveFile;
|
||||||
class SaveComment;
|
|
||||||
class GameSave;
|
class GameSave;
|
||||||
class VideoBuffer;
|
class VideoBuffer;
|
||||||
|
|
||||||
enum LoginStatus {
|
|
||||||
LoginOkay, LoginError
|
|
||||||
};
|
|
||||||
|
|
||||||
enum RequestStatus {
|
|
||||||
RequestOkay, RequestFailure
|
|
||||||
};
|
|
||||||
|
|
||||||
class UpdateInfo
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
enum BuildType { Stable, Beta, Snapshot };
|
|
||||||
ByteString File;
|
|
||||||
String Changelog;
|
|
||||||
int Major;
|
|
||||||
int Minor;
|
|
||||||
int Build;
|
|
||||||
int Time;
|
|
||||||
BuildType Type;
|
|
||||||
UpdateInfo() : File(""), Changelog(""), Major(0), Minor(0), Build(0), Time(0), Type(Stable) {}
|
|
||||||
UpdateInfo(int major, int minor, int build, ByteString file, String changelog, BuildType type) : File(file), Changelog(changelog), Major(major), Minor(minor), Build(build), Time(0), Type(type) {}
|
|
||||||
UpdateInfo(int time, ByteString file, String changelog, BuildType type) : File(file), Changelog(changelog), Major(0), Minor(0), Build(0), Time(time), Type(type) {}
|
|
||||||
};
|
|
||||||
|
|
||||||
class Prefs;
|
class Prefs;
|
||||||
class RequestListener;
|
class RequestListener;
|
||||||
class ClientListener;
|
class ClientListener;
|
||||||
namespace http
|
namespace http
|
||||||
{
|
{
|
||||||
class Request;
|
class StartupRequest;
|
||||||
}
|
}
|
||||||
class Client: public ExplicitSingleton<Client> {
|
class Client: public ExplicitSingleton<Client> {
|
||||||
private:
|
private:
|
||||||
String messageOfTheDay;
|
String messageOfTheDay;
|
||||||
std::vector<std::pair<String, ByteString> > serverNotifications;
|
std::vector<ServerNotification> serverNotifications;
|
||||||
|
|
||||||
std::unique_ptr<http::Request> versionCheckRequest;
|
std::unique_ptr<http::StartupRequest> versionCheckRequest;
|
||||||
std::unique_ptr<http::Request> alternateVersionCheckRequest;
|
std::unique_ptr<http::StartupRequest> alternateVersionCheckRequest;
|
||||||
bool usingAltUpdateServer;
|
bool usingAltUpdateServer;
|
||||||
bool updateAvailable;
|
bool updateAvailable;
|
||||||
UpdateInfo updateInfo;
|
std::optional<UpdateInfo> updateInfo;
|
||||||
|
|
||||||
String lastError;
|
|
||||||
bool firstRun;
|
bool firstRun;
|
||||||
|
|
||||||
std::vector<ByteString> stampIDs;
|
std::vector<ByteString> stampIDs;
|
||||||
@ -69,7 +44,7 @@ private:
|
|||||||
void notifyUpdateAvailable();
|
void notifyUpdateAvailable();
|
||||||
void notifyAuthUserChanged();
|
void notifyAuthUserChanged();
|
||||||
void notifyMessageOfTheDay();
|
void notifyMessageOfTheDay();
|
||||||
void notifyNewNotification(std::pair<String, ByteString> notification);
|
void notifyNewNotification(ServerNotification notification);
|
||||||
|
|
||||||
// Save stealing info
|
// Save stealing info
|
||||||
Json::Value authors;
|
Json::Value authors;
|
||||||
@ -91,7 +66,7 @@ public:
|
|||||||
void ClearAuthorInfo() { authors.clear(); }
|
void ClearAuthorInfo() { authors.clear(); }
|
||||||
bool IsAuthorsEmpty() { return authors.size() == 0; }
|
bool IsAuthorsEmpty() { return authors.size() == 0; }
|
||||||
|
|
||||||
UpdateInfo GetUpdateInfo();
|
std::optional<UpdateInfo> GetUpdateInfo();
|
||||||
|
|
||||||
Client();
|
Client();
|
||||||
~Client();
|
~Client();
|
||||||
@ -99,8 +74,8 @@ public:
|
|||||||
ByteString FileOpenDialogue();
|
ByteString FileOpenDialogue();
|
||||||
//std::string FileSaveDialogue();
|
//std::string FileSaveDialogue();
|
||||||
|
|
||||||
void AddServerNotification(std::pair<String, ByteString> notification);
|
void AddServerNotification(ServerNotification notification);
|
||||||
std::vector<std::pair<String, ByteString> > GetServerNotifications();
|
std::vector<ServerNotification> GetServerNotifications();
|
||||||
|
|
||||||
void SetMessageOfTheDay(String message);
|
void SetMessageOfTheDay(String message);
|
||||||
String GetMessageOfTheDay();
|
String GetMessageOfTheDay();
|
||||||
@ -111,9 +86,6 @@ public:
|
|||||||
void AddListener(ClientListener * listener);
|
void AddListener(ClientListener * listener);
|
||||||
void RemoveListener(ClientListener * listener);
|
void RemoveListener(ClientListener * listener);
|
||||||
|
|
||||||
RequestStatus ExecVote(int saveID, int direction);
|
|
||||||
RequestStatus UploadSave(SaveInfo & save);
|
|
||||||
|
|
||||||
std::unique_ptr<SaveFile> GetStamp(ByteString stampID);
|
std::unique_ptr<SaveFile> GetStamp(ByteString stampID);
|
||||||
void DeleteStamp(ByteString stampID);
|
void DeleteStamp(ByteString stampID);
|
||||||
ByteString AddStamp(std::unique_ptr<GameSave> saveData);
|
ByteString AddStamp(std::unique_ptr<GameSave> saveData);
|
||||||
@ -121,30 +93,11 @@ public:
|
|||||||
const std::vector<ByteString> &GetStamps() const;
|
const std::vector<ByteString> &GetStamps() const;
|
||||||
void MoveStampToFront(ByteString stampID);
|
void MoveStampToFront(ByteString stampID);
|
||||||
|
|
||||||
RequestStatus AddComment(int saveID, String comment);
|
|
||||||
|
|
||||||
std::vector<char> GetSaveData(int saveID, int saveDate);
|
|
||||||
|
|
||||||
LoginStatus Login(ByteString username, ByteString password, User & user);
|
|
||||||
|
|
||||||
std::unique_ptr<SaveInfo> GetSave(int saveID, int saveDate);
|
|
||||||
std::unique_ptr<SaveFile> LoadSaveFile(ByteString filename);
|
std::unique_ptr<SaveFile> LoadSaveFile(ByteString filename);
|
||||||
|
|
||||||
RequestStatus DeleteSave(int saveID);
|
|
||||||
RequestStatus ReportSave(int saveID, String message);
|
|
||||||
RequestStatus UnpublishSave(int saveID);
|
|
||||||
RequestStatus PublishSave(int saveID);
|
|
||||||
RequestStatus FavouriteSave(int saveID, bool favourite);
|
|
||||||
void SetAuthUser(User user);
|
void SetAuthUser(User user);
|
||||||
User GetAuthUser();
|
User GetAuthUser();
|
||||||
std::list<ByteString> * RemoveTag(int saveID, ByteString tag); //TODO RequestStatus
|
|
||||||
std::list<ByteString> * AddTag(int saveID, ByteString tag);
|
|
||||||
String GetLastError() {
|
|
||||||
return lastError;
|
|
||||||
}
|
|
||||||
RequestStatus ParseServerReturn(ByteString &result, int status, bool json);
|
|
||||||
void Tick();
|
void Tick();
|
||||||
void CheckUpdate(std::unique_ptr<http::Request> &updateRequest, bool checkSession);
|
|
||||||
|
|
||||||
String DoMigration(ByteString fromDir, ByteString toDir);
|
String DoMigration(ByteString fromDir, ByteString toDir);
|
||||||
};
|
};
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include "common/String.h"
|
#include "common/String.h"
|
||||||
|
#include "client/ServerNotification.h"
|
||||||
|
|
||||||
class Client;
|
class Client;
|
||||||
class ClientListener
|
class ClientListener
|
||||||
@ -11,6 +12,6 @@ public:
|
|||||||
virtual void NotifyUpdateAvailable(Client * sender) {}
|
virtual void NotifyUpdateAvailable(Client * sender) {}
|
||||||
virtual void NotifyAuthUserChanged(Client * sender) {}
|
virtual void NotifyAuthUserChanged(Client * sender) {}
|
||||||
virtual void NotifyMessageOfTheDay(Client * sender) {}
|
virtual void NotifyMessageOfTheDay(Client * sender) {}
|
||||||
virtual void NotifyNewNotification(Client * sender, std::pair<String, ByteString> notification) {}
|
virtual void NotifyNewNotification(Client * sender, ServerNotification notification) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
11
src/client/Comment.h
Normal file
11
src/client/Comment.h
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "User.h"
|
||||||
|
|
||||||
|
struct Comment
|
||||||
|
{
|
||||||
|
ByteString authorName;
|
||||||
|
User::Elevation authorElevation;
|
||||||
|
bool authorIsSelf;
|
||||||
|
bool authorIsBanned;
|
||||||
|
String content;
|
||||||
|
};
|
10
src/client/LoginInfo.h
Normal file
10
src/client/LoginInfo.h
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "User.h"
|
||||||
|
#include "ServerNotification.h"
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
struct LoginInfo
|
||||||
|
{
|
||||||
|
User user;
|
||||||
|
std::vector<ServerNotification> notifications;
|
||||||
|
};
|
@ -45,7 +45,7 @@ void SaveInfo::SetName(String name)
|
|||||||
{
|
{
|
||||||
this->name = name;
|
this->name = name;
|
||||||
}
|
}
|
||||||
String SaveInfo::GetName()
|
const String &SaveInfo::GetName() const
|
||||||
{
|
{
|
||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
@ -54,7 +54,7 @@ void SaveInfo::SetDescription(String description)
|
|||||||
{
|
{
|
||||||
Description = description;
|
Description = description;
|
||||||
}
|
}
|
||||||
String SaveInfo::GetDescription()
|
const String &SaveInfo::GetDescription() const
|
||||||
{
|
{
|
||||||
return Description;
|
return Description;
|
||||||
}
|
}
|
||||||
|
@ -37,10 +37,10 @@ public:
|
|||||||
SaveInfo(int _id, int _createdDate, int _updatedDate, int _votesUp, int _votesDown, int _vote, ByteString _userName, String _name, String description_, bool published_, std::list<ByteString> tags);
|
SaveInfo(int _id, int _createdDate, int _updatedDate, int _votesUp, int _votesDown, int _vote, ByteString _userName, String _name, String description_, bool published_, std::list<ByteString> tags);
|
||||||
|
|
||||||
void SetName(String name);
|
void SetName(String name);
|
||||||
String GetName();
|
const String &GetName() const;
|
||||||
|
|
||||||
void SetDescription(String description);
|
void SetDescription(String description);
|
||||||
String GetDescription();
|
const String &GetDescription() const;
|
||||||
|
|
||||||
void SetPublished(bool published);
|
void SetPublished(bool published);
|
||||||
bool GetPublished() const;
|
bool GetPublished() const;
|
||||||
|
17
src/client/Search.h
Normal file
17
src/client/Search.h
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
namespace http
|
||||||
|
{
|
||||||
|
enum Category
|
||||||
|
{
|
||||||
|
categoryNone,
|
||||||
|
categoryMyOwn,
|
||||||
|
categoryFavourites,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum Sort
|
||||||
|
{
|
||||||
|
sortByVotes,
|
||||||
|
sortByDate,
|
||||||
|
};
|
||||||
|
}
|
8
src/client/ServerNotification.h
Normal file
8
src/client/ServerNotification.h
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "common/String.h"
|
||||||
|
|
||||||
|
struct ServerNotification
|
||||||
|
{
|
||||||
|
String text;
|
||||||
|
ByteString link;
|
||||||
|
};
|
29
src/client/StartupInfo.h
Normal file
29
src/client/StartupInfo.h
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "common/String.h"
|
||||||
|
#include "ServerNotification.h"
|
||||||
|
#include <vector>
|
||||||
|
#include <optional>
|
||||||
|
|
||||||
|
struct UpdateInfo
|
||||||
|
{
|
||||||
|
enum Channel
|
||||||
|
{
|
||||||
|
channelStable,
|
||||||
|
channelBeta,
|
||||||
|
channelSnapshot,
|
||||||
|
};
|
||||||
|
Channel channel;
|
||||||
|
ByteString file;
|
||||||
|
String changeLog;
|
||||||
|
int major = 0;
|
||||||
|
int minor = 0;
|
||||||
|
int build = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct StartupInfo
|
||||||
|
{
|
||||||
|
bool sessionGood = false;
|
||||||
|
String messageOfTheDay;
|
||||||
|
std::vector<ServerNotification> notifications;
|
||||||
|
std::optional<UpdateInfo> updateInfo;
|
||||||
|
};
|
32
src/client/User.cpp
Normal file
32
src/client/User.cpp
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
#include "User.h"
|
||||||
|
|
||||||
|
static const std::vector<std::pair<User::Elevation, ByteString>> elevationStrings = {
|
||||||
|
{ User::ElevationAdmin , "Admin" },
|
||||||
|
{ User::ElevationMod , "Mod" },
|
||||||
|
{ User::ElevationHalfMod, "HalfMod" },
|
||||||
|
{ User::ElevationNone , "None" },
|
||||||
|
};
|
||||||
|
|
||||||
|
User::Elevation User::ElevationFromString(ByteString str)
|
||||||
|
{
|
||||||
|
auto it = std::find_if(elevationStrings.begin(), elevationStrings.end(), [&str](auto &item) {
|
||||||
|
return item.second == str;
|
||||||
|
});
|
||||||
|
if (it != elevationStrings.end())
|
||||||
|
{
|
||||||
|
return it->first;
|
||||||
|
}
|
||||||
|
return ElevationNone;
|
||||||
|
}
|
||||||
|
|
||||||
|
ByteString User::ElevationToString(Elevation elevation)
|
||||||
|
{
|
||||||
|
auto it = std::find_if(elevationStrings.begin(), elevationStrings.end(), [elevation](auto &item) {
|
||||||
|
return item.first == elevation;
|
||||||
|
});
|
||||||
|
if (it != elevationStrings.end())
|
||||||
|
{
|
||||||
|
return it->second;
|
||||||
|
}
|
||||||
|
return "None";
|
||||||
|
}
|
@ -7,8 +7,14 @@ class User
|
|||||||
public:
|
public:
|
||||||
enum Elevation
|
enum Elevation
|
||||||
{
|
{
|
||||||
ElevationAdmin, ElevationModerator, ElevationNone
|
ElevationNone,
|
||||||
|
ElevationHalfMod,
|
||||||
|
ElevationMod,
|
||||||
|
ElevationAdmin,
|
||||||
};
|
};
|
||||||
|
static Elevation ElevationFromString(ByteString str);
|
||||||
|
static ByteString ElevationToString(Elevation elevation);
|
||||||
|
|
||||||
int UserID;
|
int UserID;
|
||||||
ByteString Username;
|
ByteString Username;
|
||||||
ByteString SessionID;
|
ByteString SessionID;
|
||||||
|
@ -1,35 +1,36 @@
|
|||||||
#include "APIRequest.h"
|
#include "APIRequest.h"
|
||||||
|
|
||||||
#include "client/Client.h"
|
#include "client/Client.h"
|
||||||
|
|
||||||
namespace http
|
namespace http
|
||||||
{
|
{
|
||||||
APIRequest::APIRequest(ByteString url) : Request(url)
|
APIRequest::APIRequest(ByteString url, AuthMode authMode, bool newCheckStatus) : Request(url), checkStatus(newCheckStatus)
|
||||||
{
|
{
|
||||||
User user = Client::Ref().GetAuthUser();
|
auto user = Client::Ref().GetAuthUser();
|
||||||
AuthHeaders(ByteString::Build(user.UserID), user.SessionID);
|
if (authMode == authRequire && !user.UserID)
|
||||||
|
{
|
||||||
|
FailEarly("Not authenticated");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (authMode != authOmit && user.UserID)
|
||||||
|
{
|
||||||
|
AuthHeaders(ByteString::Build(user.UserID), user.SessionID);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
APIRequest::Result APIRequest::Finish()
|
Json::Value APIRequest::Finish()
|
||||||
{
|
{
|
||||||
Result result;
|
auto [ status, data ] = Request::Finish();
|
||||||
|
ParseResponse(data, status, checkStatus ? responseJson : responseData);
|
||||||
|
Json::Value document;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
ByteString data;
|
std::istringstream ss(data);
|
||||||
std::tie(result.status, data) = Request::Finish();
|
ss >> document;
|
||||||
Client::Ref().ParseServerReturn(data, result.status, true);
|
|
||||||
if (result.status == 200 && data.size())
|
|
||||||
{
|
|
||||||
std::istringstream dataStream(data);
|
|
||||||
Json::Value objDocument;
|
|
||||||
dataStream >> objDocument;
|
|
||||||
result.document = std::unique_ptr<Json::Value>(new Json::Value(objDocument));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
catch (std::exception & e)
|
catch (const std::exception &ex)
|
||||||
{
|
{
|
||||||
|
throw RequestError("Could not read response: " + ByteString(ex.what()));
|
||||||
}
|
}
|
||||||
return result;
|
return document;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,22 +2,22 @@
|
|||||||
#include "Request.h"
|
#include "Request.h"
|
||||||
#include "common/String.h"
|
#include "common/String.h"
|
||||||
#include <json/json.h>
|
#include <json/json.h>
|
||||||
#include <memory>
|
|
||||||
#include <map>
|
|
||||||
|
|
||||||
namespace http
|
namespace http
|
||||||
{
|
{
|
||||||
class APIRequest : public Request
|
class APIRequest : public Request
|
||||||
{
|
{
|
||||||
|
bool checkStatus;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
struct Result
|
enum AuthMode
|
||||||
{
|
{
|
||||||
int status;
|
authRequire,
|
||||||
std::unique_ptr<Json::Value> document;
|
authUse,
|
||||||
|
authOmit,
|
||||||
};
|
};
|
||||||
|
APIRequest(ByteString url, AuthMode authMode, bool newCheckStatus);
|
||||||
|
|
||||||
APIRequest(ByteString url);
|
Json::Value Finish();
|
||||||
|
|
||||||
Result Finish();
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
21
src/client/http/AddCommentRequest.cpp
Normal file
21
src/client/http/AddCommentRequest.cpp
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
#include "AddCommentRequest.h"
|
||||||
|
#include "client/Client.h"
|
||||||
|
#include "Config.h"
|
||||||
|
|
||||||
|
namespace http
|
||||||
|
{
|
||||||
|
AddCommentRequest::AddCommentRequest(int saveID, String comment) :
|
||||||
|
APIRequest(ByteString::Build(SCHEME, SERVER, "/Browse/Comments.json?ID=", saveID), authRequire, true)
|
||||||
|
{
|
||||||
|
auto user = Client::Ref().GetAuthUser();
|
||||||
|
AddPostData(FormData{
|
||||||
|
{ "Comment", comment.ToUtf8() },
|
||||||
|
{ "Key", user.SessionKey },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void AddCommentRequest::Finish()
|
||||||
|
{
|
||||||
|
APIRequest::Finish();
|
||||||
|
}
|
||||||
|
}
|
13
src/client/http/AddCommentRequest.h
Normal file
13
src/client/http/AddCommentRequest.h
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "APIRequest.h"
|
||||||
|
|
||||||
|
namespace http
|
||||||
|
{
|
||||||
|
class AddCommentRequest : public APIRequest
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
AddCommentRequest(int saveID, String comment);
|
||||||
|
|
||||||
|
void Finish();
|
||||||
|
};
|
||||||
|
}
|
29
src/client/http/AddTagRequest.cpp
Normal file
29
src/client/http/AddTagRequest.cpp
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
#include "AddTagRequest.h"
|
||||||
|
#include "client/Client.h"
|
||||||
|
#include "Config.h"
|
||||||
|
|
||||||
|
namespace http
|
||||||
|
{
|
||||||
|
AddTagRequest::AddTagRequest(int saveID, ByteString tag) :
|
||||||
|
APIRequest(ByteString::Build(SCHEME, SERVER, "/Browse/EditTag.json?Op=add&ID=", saveID, "&Tag=", tag, "&Key=", Client::Ref().GetAuthUser().SessionKey), authRequire, true)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
std::list<ByteString> AddTagRequest::Finish()
|
||||||
|
{
|
||||||
|
auto result = APIRequest::Finish();
|
||||||
|
std::list<ByteString> tags;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
for (auto &tag : result["Tags"])
|
||||||
|
{
|
||||||
|
tags.push_back(tag.asString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (const std::exception &ex)
|
||||||
|
{
|
||||||
|
throw RequestError("Could not read response: " + ByteString(ex.what()));
|
||||||
|
}
|
||||||
|
return tags;
|
||||||
|
}
|
||||||
|
}
|
14
src/client/http/AddTagRequest.h
Normal file
14
src/client/http/AddTagRequest.h
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "APIRequest.h"
|
||||||
|
#include <list>
|
||||||
|
|
||||||
|
namespace http
|
||||||
|
{
|
||||||
|
class AddTagRequest : public APIRequest
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
AddTagRequest(int saveID, ByteString tag);
|
||||||
|
|
||||||
|
std::list<ByteString> Finish();
|
||||||
|
};
|
||||||
|
}
|
16
src/client/http/DeleteSaveRequest.cpp
Normal file
16
src/client/http/DeleteSaveRequest.cpp
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
#include "DeleteSaveRequest.h"
|
||||||
|
#include "client/Client.h"
|
||||||
|
#include "Config.h"
|
||||||
|
|
||||||
|
namespace http
|
||||||
|
{
|
||||||
|
DeleteSaveRequest::DeleteSaveRequest(int saveID) :
|
||||||
|
APIRequest(ByteString::Build(SCHEME, SERVER, "/Browse/Delete.json?ID=", saveID, "&Mode=Delete&Key=", Client::Ref().GetAuthUser().SessionKey), authRequire, true)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void DeleteSaveRequest::Finish()
|
||||||
|
{
|
||||||
|
APIRequest::Finish();
|
||||||
|
}
|
||||||
|
}
|
13
src/client/http/DeleteSaveRequest.h
Normal file
13
src/client/http/DeleteSaveRequest.h
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "APIRequest.h"
|
||||||
|
|
||||||
|
namespace http
|
||||||
|
{
|
||||||
|
class DeleteSaveRequest : public APIRequest
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
DeleteSaveRequest(int saveID);
|
||||||
|
|
||||||
|
void Finish();
|
||||||
|
};
|
||||||
|
}
|
30
src/client/http/ExecVoteRequest.cpp
Normal file
30
src/client/http/ExecVoteRequest.cpp
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
#include "ExecVoteRequest.h"
|
||||||
|
#include "client/Client.h"
|
||||||
|
#include "Config.h"
|
||||||
|
|
||||||
|
namespace http
|
||||||
|
{
|
||||||
|
ExecVoteRequest::ExecVoteRequest(int saveID, int newDirection) :
|
||||||
|
APIRequest(ByteString::Build(SCHEME, SERVER, "/Vote.api"), authRequire, false),
|
||||||
|
direction(newDirection)
|
||||||
|
{
|
||||||
|
auto user = Client::Ref().GetAuthUser();
|
||||||
|
if (!user.UserID)
|
||||||
|
{
|
||||||
|
FailEarly("Not authenticated");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
AuthHeaders(ByteString::Build(user.UserID), user.SessionID);
|
||||||
|
AddPostData(FormData{
|
||||||
|
{ "ID", ByteString::Build(saveID) },
|
||||||
|
{ "Action", direction ? (direction == 1 ? "Up" : "Down") : "Reset" },
|
||||||
|
{ "Key", user.SessionKey },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void ExecVoteRequest::Finish()
|
||||||
|
{
|
||||||
|
auto [ status, data ] = Request::Finish();
|
||||||
|
ParseResponse(data, status, responseOk);
|
||||||
|
}
|
||||||
|
}
|
20
src/client/http/ExecVoteRequest.h
Normal file
20
src/client/http/ExecVoteRequest.h
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "APIRequest.h"
|
||||||
|
|
||||||
|
namespace http
|
||||||
|
{
|
||||||
|
class ExecVoteRequest : public APIRequest
|
||||||
|
{
|
||||||
|
int direction;
|
||||||
|
|
||||||
|
public:
|
||||||
|
ExecVoteRequest(int saveID, int newDirection);
|
||||||
|
|
||||||
|
void Finish();
|
||||||
|
|
||||||
|
int Direction() const
|
||||||
|
{
|
||||||
|
return direction;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
28
src/client/http/FavouriteSaveRequest.cpp
Normal file
28
src/client/http/FavouriteSaveRequest.cpp
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
#include "FavouriteSaveRequest.h"
|
||||||
|
#include "client/Client.h"
|
||||||
|
#include "Config.h"
|
||||||
|
|
||||||
|
namespace http
|
||||||
|
{
|
||||||
|
static ByteString Url(int saveID, bool favourite)
|
||||||
|
{
|
||||||
|
ByteStringBuilder builder;
|
||||||
|
builder << SCHEME << SERVER << "/Browse/Favourite.json?ID=" << saveID << "&Key=" << Client::Ref().GetAuthUser().SessionKey;
|
||||||
|
if (!favourite)
|
||||||
|
{
|
||||||
|
builder << "&Mode=Remove";
|
||||||
|
}
|
||||||
|
return builder.Build();
|
||||||
|
}
|
||||||
|
|
||||||
|
FavouriteSaveRequest::FavouriteSaveRequest(int saveID, bool newFavourite) :
|
||||||
|
APIRequest(Url(saveID, newFavourite), authRequire, true),
|
||||||
|
favourite(newFavourite)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void FavouriteSaveRequest::Finish()
|
||||||
|
{
|
||||||
|
APIRequest::Finish();
|
||||||
|
}
|
||||||
|
}
|
20
src/client/http/FavouriteSaveRequest.h
Normal file
20
src/client/http/FavouriteSaveRequest.h
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "APIRequest.h"
|
||||||
|
|
||||||
|
namespace http
|
||||||
|
{
|
||||||
|
class FavouriteSaveRequest : public APIRequest
|
||||||
|
{
|
||||||
|
bool favourite;
|
||||||
|
|
||||||
|
public:
|
||||||
|
FavouriteSaveRequest(int saveID, bool newFavourite);
|
||||||
|
|
||||||
|
void Finish();
|
||||||
|
|
||||||
|
bool Favourite() const
|
||||||
|
{
|
||||||
|
return favourite;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
36
src/client/http/GetCommentsRequest.cpp
Normal file
36
src/client/http/GetCommentsRequest.cpp
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
#include "GetCommentsRequest.h"
|
||||||
|
#include "client/Client.h"
|
||||||
|
#include "Config.h"
|
||||||
|
|
||||||
|
namespace http
|
||||||
|
{
|
||||||
|
GetCommentsRequest::GetCommentsRequest(int saveID, int start, int count) :
|
||||||
|
APIRequest(ByteString::Build(SCHEME, SERVER, "/Browse/Comments.json?ID=", saveID, "&Start=", start, "&Count=", count), authOmit, false)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<Comment> GetCommentsRequest::Finish()
|
||||||
|
{
|
||||||
|
auto result = APIRequest::Finish();
|
||||||
|
std::vector<Comment> comments;
|
||||||
|
auto user = Client::Ref().GetAuthUser();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
for (auto &comment : result)
|
||||||
|
{
|
||||||
|
comments.push_back({
|
||||||
|
comment["Username"].asString(),
|
||||||
|
User::ElevationFromString(comment["Elevation"].asString()),
|
||||||
|
ByteString(comment["UserID"].asString()).ToNumber<int>() == user.UserID,
|
||||||
|
comment["IsBanned"].asBool(),
|
||||||
|
ByteString(comment["Text"].asString()).FromUtf8(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (const std::exception &ex)
|
||||||
|
{
|
||||||
|
throw RequestError("Could not read response: " + ByteString(ex.what()));
|
||||||
|
}
|
||||||
|
return comments;
|
||||||
|
}
|
||||||
|
}
|
14
src/client/http/GetCommentsRequest.h
Normal file
14
src/client/http/GetCommentsRequest.h
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "APIRequest.h"
|
||||||
|
#include "client/Comment.h"
|
||||||
|
|
||||||
|
namespace http
|
||||||
|
{
|
||||||
|
class GetCommentsRequest : public APIRequest
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
GetCommentsRequest(int saveID, int start, int count);
|
||||||
|
|
||||||
|
std::vector<Comment> Finish();
|
||||||
|
};
|
||||||
|
}
|
28
src/client/http/GetSaveDataRequest.cpp
Normal file
28
src/client/http/GetSaveDataRequest.cpp
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
#include "GetSaveDataRequest.h"
|
||||||
|
#include "Config.h"
|
||||||
|
|
||||||
|
namespace http
|
||||||
|
{
|
||||||
|
static ByteString Url(int saveID, int saveDate)
|
||||||
|
{
|
||||||
|
ByteStringBuilder builder;
|
||||||
|
builder << STATICSCHEME << STATICSERVER << "/" << saveID;
|
||||||
|
if (saveDate)
|
||||||
|
{
|
||||||
|
builder << "_" << saveDate;
|
||||||
|
}
|
||||||
|
builder << ".cps";
|
||||||
|
return builder.Build();
|
||||||
|
}
|
||||||
|
|
||||||
|
GetSaveDataRequest::GetSaveDataRequest(int saveID, int saveDate) : Request(Url(saveID, saveDate))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<char> GetSaveDataRequest::Finish()
|
||||||
|
{
|
||||||
|
auto [ status, data ] = Request::Finish();
|
||||||
|
ParseResponse(data, status, responseData);
|
||||||
|
return std::vector<char>(data.begin(), data.end());
|
||||||
|
}
|
||||||
|
}
|
13
src/client/http/GetSaveDataRequest.h
Normal file
13
src/client/http/GetSaveDataRequest.h
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "Request.h"
|
||||||
|
|
||||||
|
namespace http
|
||||||
|
{
|
||||||
|
class GetSaveDataRequest : public Request
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
GetSaveDataRequest(int saveID, int saveDate);
|
||||||
|
|
||||||
|
std::vector<char> Finish();
|
||||||
|
};
|
||||||
|
}
|
69
src/client/http/GetSaveRequest.cpp
Normal file
69
src/client/http/GetSaveRequest.cpp
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
#include "GetSaveRequest.h"
|
||||||
|
#include "client/Client.h"
|
||||||
|
#include "client/SaveInfo.h"
|
||||||
|
#include "client/GameSave.h"
|
||||||
|
#include "Config.h"
|
||||||
|
|
||||||
|
namespace http
|
||||||
|
{
|
||||||
|
static ByteString Url(int saveID, int saveDate)
|
||||||
|
{
|
||||||
|
ByteStringBuilder builder;
|
||||||
|
builder << SCHEME << SERVER << "/Browse/View.json?ID=" << saveID;
|
||||||
|
if (saveDate)
|
||||||
|
{
|
||||||
|
builder << "&Date=" << saveDate;
|
||||||
|
}
|
||||||
|
return builder.Build();
|
||||||
|
}
|
||||||
|
|
||||||
|
GetSaveRequest::GetSaveRequest(int saveID, int saveDate) : Request(Url(saveID, saveDate))
|
||||||
|
{
|
||||||
|
auto user = Client::Ref().GetAuthUser();
|
||||||
|
if (user.UserID)
|
||||||
|
{
|
||||||
|
// This is needed so we know how we rated this save.
|
||||||
|
AuthHeaders(ByteString::Build(user.UserID), user.SessionID);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<SaveInfo> GetSaveRequest::Finish()
|
||||||
|
{
|
||||||
|
auto [ status, data ] = Request::Finish();
|
||||||
|
ParseResponse(data, status, responseData);
|
||||||
|
std::unique_ptr<SaveInfo> saveInfo;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Json::Value document;
|
||||||
|
std::istringstream ss(data);
|
||||||
|
ss >> document;
|
||||||
|
std::list<ByteString> tags;
|
||||||
|
for (auto &tag : document["Tags"])
|
||||||
|
{
|
||||||
|
tags.push_back(tag.asString());
|
||||||
|
}
|
||||||
|
saveInfo = std::make_unique<SaveInfo>(
|
||||||
|
document["ID"].asInt(),
|
||||||
|
document["DateCreated"].asInt(),
|
||||||
|
document["Date"].asInt(),
|
||||||
|
document["ScoreUp"].asInt(),
|
||||||
|
document["ScoreDown"].asInt(),
|
||||||
|
document["ScoreMine"].asInt(),
|
||||||
|
document["Username"].asString(),
|
||||||
|
ByteString(document["Name"].asString()).FromUtf8(),
|
||||||
|
ByteString(document["Description"].asString()).FromUtf8(),
|
||||||
|
document["Published"].asBool(),
|
||||||
|
tags
|
||||||
|
);
|
||||||
|
saveInfo->Comments = document["Comments"].asInt();
|
||||||
|
saveInfo->Favourite = document["Favourite"].asBool();
|
||||||
|
saveInfo->Views = document["Views"].asInt();
|
||||||
|
saveInfo->Version = document["Version"].asInt();
|
||||||
|
}
|
||||||
|
catch (const std::exception &ex)
|
||||||
|
{
|
||||||
|
throw RequestError("Could not read response: " + ByteString(ex.what()));
|
||||||
|
}
|
||||||
|
return saveInfo;
|
||||||
|
}
|
||||||
|
}
|
16
src/client/http/GetSaveRequest.h
Normal file
16
src/client/http/GetSaveRequest.h
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "Request.h"
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
class SaveInfo;
|
||||||
|
|
||||||
|
namespace http
|
||||||
|
{
|
||||||
|
class GetSaveRequest : public Request
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
GetSaveRequest(int saveID, int saveDate);
|
||||||
|
|
||||||
|
std::unique_ptr<SaveInfo> Finish();
|
||||||
|
};
|
||||||
|
}
|
@ -5,18 +5,18 @@
|
|||||||
namespace http
|
namespace http
|
||||||
{
|
{
|
||||||
GetUserInfoRequest::GetUserInfoRequest(ByteString username) :
|
GetUserInfoRequest::GetUserInfoRequest(ByteString username) :
|
||||||
APIRequest(ByteString::Build(SCHEME, SERVER, "/User.json?Name=", username))
|
APIRequest(ByteString::Build(SCHEME, SERVER, "/User.json?Name=", username), authOmit, false)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<UserInfo> GetUserInfoRequest::Finish()
|
UserInfo GetUserInfoRequest::Finish()
|
||||||
{
|
{
|
||||||
std::unique_ptr<UserInfo> user_info;
|
|
||||||
auto result = APIRequest::Finish();
|
auto result = APIRequest::Finish();
|
||||||
if (result.document)
|
UserInfo userInfo;
|
||||||
|
try
|
||||||
{
|
{
|
||||||
auto &user = (*result.document)["User"];
|
auto &user = result["User"];
|
||||||
user_info = std::unique_ptr<UserInfo>(new UserInfo(
|
userInfo = UserInfo(
|
||||||
user["ID"].asInt(),
|
user["ID"].asInt(),
|
||||||
user["Age"].asInt(),
|
user["Age"].asInt(),
|
||||||
user["Username"].asString(),
|
user["Username"].asString(),
|
||||||
@ -29,9 +29,13 @@ namespace http
|
|||||||
user["Forum"]["Topics"].asInt(),
|
user["Forum"]["Topics"].asInt(),
|
||||||
user["Forum"]["Replies"].asInt(),
|
user["Forum"]["Replies"].asInt(),
|
||||||
user["Forum"]["Reputation"].asInt()
|
user["Forum"]["Reputation"].asInt()
|
||||||
));
|
);
|
||||||
}
|
}
|
||||||
return user_info;
|
catch (const std::exception &ex)
|
||||||
|
{
|
||||||
|
throw RequestError("Could not read response: " + ByteString(ex.what()));
|
||||||
|
}
|
||||||
|
return userInfo;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include "APIRequest.h"
|
#include "APIRequest.h"
|
||||||
|
#include "client/UserInfo.h"
|
||||||
class UserInfo;
|
|
||||||
|
|
||||||
namespace http
|
namespace http
|
||||||
{
|
{
|
||||||
@ -10,6 +9,6 @@ namespace http
|
|||||||
public:
|
public:
|
||||||
GetUserInfoRequest(ByteString username);
|
GetUserInfoRequest(ByteString username);
|
||||||
|
|
||||||
std::unique_ptr<UserInfo> Finish();
|
UserInfo Finish();
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -1,29 +1,27 @@
|
|||||||
#include "ImageRequest.h"
|
#include "ImageRequest.h"
|
||||||
#include "graphics/Graphics.h"
|
#include "graphics/Graphics.h"
|
||||||
|
#include "client/Client.h"
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
namespace http
|
namespace http
|
||||||
{
|
{
|
||||||
ImageRequest::ImageRequest(ByteString url, Vec2<int> size):
|
ImageRequest::ImageRequest(ByteString url, Vec2<int> newRequestedSize) : Request(url), requestedSize(newRequestedSize)
|
||||||
Request(std::move(url)),
|
{
|
||||||
size(size)
|
}
|
||||||
{}
|
|
||||||
|
|
||||||
std::unique_ptr<VideoBuffer> ImageRequest::Finish()
|
std::unique_ptr<VideoBuffer> ImageRequest::Finish()
|
||||||
{
|
{
|
||||||
auto [ status, data ] = Request::Finish();
|
auto [ status, data ] = Request::Finish();
|
||||||
(void)status; // We don't use this for anything, not ideal >_>
|
ParseResponse(data, status, responseData);
|
||||||
std::unique_ptr<VideoBuffer> vb;
|
auto vb = VideoBuffer::FromPNG(std::vector<char>(data.begin(), data.end()));
|
||||||
if (data.size())
|
if (vb)
|
||||||
{
|
{
|
||||||
vb = VideoBuffer::FromPNG(std::vector<char>(data.begin(), data.end()));
|
vb->Resize(requestedSize, true);
|
||||||
if (vb)
|
}
|
||||||
vb->Resize(size, true);
|
else
|
||||||
else
|
{
|
||||||
{
|
vb = std::make_unique<VideoBuffer>(Vec2(15, 16));
|
||||||
vb = std::make_unique<VideoBuffer>(Vec2(15, 16));
|
vb->BlendChar(Vec2(2, 4), 0xE06E, 0xFFFFFF_rgb .WithAlpha(0xFF));
|
||||||
vb->BlendChar(Vec2(2, 4), 0xE06E, 0xFFFFFF_rgb .WithAlpha(0xFF));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return vb;
|
return vb;
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,6 @@
|
|||||||
#include "common/String.h"
|
#include "common/String.h"
|
||||||
#include "common/Vec2.h"
|
#include "common/Vec2.h"
|
||||||
#include "Request.h"
|
#include "Request.h"
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
class VideoBuffer;
|
class VideoBuffer;
|
||||||
@ -11,10 +10,10 @@ namespace http
|
|||||||
{
|
{
|
||||||
class ImageRequest : public Request
|
class ImageRequest : public Request
|
||||||
{
|
{
|
||||||
Vec2<int> size;
|
Vec2<int> requestedSize;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
ImageRequest(ByteString url, Vec2<int> size);
|
ImageRequest(ByteString url, Vec2<int> newRequestedSize);
|
||||||
|
|
||||||
std::unique_ptr<VideoBuffer> Finish();
|
std::unique_ptr<VideoBuffer> Finish();
|
||||||
};
|
};
|
||||||
|
45
src/client/http/LoginRequest.cpp
Normal file
45
src/client/http/LoginRequest.cpp
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
#include "LoginRequest.h"
|
||||||
|
#include "Config.h"
|
||||||
|
#include "client/Client.h"
|
||||||
|
#include <json/json.h>
|
||||||
|
|
||||||
|
namespace http
|
||||||
|
{
|
||||||
|
LoginRequest::LoginRequest(ByteString username, ByteString password) : Request(ByteString::Build("https://", SERVER, "/Login.json"))
|
||||||
|
{
|
||||||
|
AddPostData(FormData{
|
||||||
|
{ "name", username },
|
||||||
|
{ "pass", password },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
LoginInfo LoginRequest::Finish()
|
||||||
|
{
|
||||||
|
auto [ status, data ] = Request::Finish();
|
||||||
|
ParseResponse(data, status, responseJson);
|
||||||
|
LoginInfo loginInfo = { { 0, "" }, {} };
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Json::Value document;
|
||||||
|
std::istringstream ss(data);
|
||||||
|
ss >> document;
|
||||||
|
loginInfo.user.Username = document["Username"].asString();
|
||||||
|
loginInfo.user.UserID = document["UserID"].asInt();
|
||||||
|
loginInfo.user.SessionID = document["SessionID"].asString();
|
||||||
|
loginInfo.user.SessionKey = document["SessionKey"].asString();
|
||||||
|
loginInfo.user.UserElevation = User::ElevationFromString(document["Elevation"].asString());
|
||||||
|
for (auto &item : document["Notifications"])
|
||||||
|
{
|
||||||
|
loginInfo.notifications.push_back({
|
||||||
|
ByteString(item["Text"].asString()).FromUtf8(),
|
||||||
|
item["Link"].asString(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (const std::exception &ex)
|
||||||
|
{
|
||||||
|
throw RequestError("Could not read response: " + ByteString(ex.what()));
|
||||||
|
}
|
||||||
|
return loginInfo;
|
||||||
|
}
|
||||||
|
}
|
14
src/client/http/LoginRequest.h
Normal file
14
src/client/http/LoginRequest.h
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "Request.h"
|
||||||
|
#include "client/LoginInfo.h"
|
||||||
|
|
||||||
|
namespace http
|
||||||
|
{
|
||||||
|
class LoginRequest : public Request
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
LoginRequest(ByteString username, ByteString password);
|
||||||
|
|
||||||
|
LoginInfo Finish();
|
||||||
|
};
|
||||||
|
}
|
20
src/client/http/LogoutRequest.cpp
Normal file
20
src/client/http/LogoutRequest.cpp
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
#include "LogoutRequest.h"
|
||||||
|
#include "client/Client.h"
|
||||||
|
#include "Config.h"
|
||||||
|
|
||||||
|
namespace http
|
||||||
|
{
|
||||||
|
LogoutRequest::LogoutRequest() :
|
||||||
|
APIRequest(ByteString::Build(SCHEME, SERVER, "/Logout.json"), authRequire, false)
|
||||||
|
{
|
||||||
|
auto user = Client::Ref().GetAuthUser();
|
||||||
|
AddPostData(FormData{
|
||||||
|
{ "Key", user.SessionKey },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void LogoutRequest::Finish()
|
||||||
|
{
|
||||||
|
APIRequest::Finish();
|
||||||
|
}
|
||||||
|
}
|
13
src/client/http/LogoutRequest.h
Normal file
13
src/client/http/LogoutRequest.h
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "APIRequest.h"
|
||||||
|
|
||||||
|
namespace http
|
||||||
|
{
|
||||||
|
class LogoutRequest : public APIRequest
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
LogoutRequest();
|
||||||
|
|
||||||
|
void Finish();
|
||||||
|
};
|
||||||
|
}
|
19
src/client/http/PublishSaveRequest.cpp
Normal file
19
src/client/http/PublishSaveRequest.cpp
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
#include "PublishSaveRequest.h"
|
||||||
|
#include "client/Client.h"
|
||||||
|
#include "Config.h"
|
||||||
|
|
||||||
|
namespace http
|
||||||
|
{
|
||||||
|
PublishSaveRequest::PublishSaveRequest(int saveID) :
|
||||||
|
APIRequest(ByteString::Build(SCHEME, SERVER, "/Browse/View.json?ID=", saveID, "&Key=", Client::Ref().GetAuthUser().SessionKey), authRequire, true)
|
||||||
|
{
|
||||||
|
AddPostData(FormData{
|
||||||
|
{ "ActionPublish", "bagels" },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void PublishSaveRequest::Finish()
|
||||||
|
{
|
||||||
|
APIRequest::Finish();
|
||||||
|
}
|
||||||
|
}
|
13
src/client/http/PublishSaveRequest.h
Normal file
13
src/client/http/PublishSaveRequest.h
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "APIRequest.h"
|
||||||
|
|
||||||
|
namespace http
|
||||||
|
{
|
||||||
|
class PublishSaveRequest : public APIRequest
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
PublishSaveRequest(int saveID);
|
||||||
|
|
||||||
|
void Finish();
|
||||||
|
};
|
||||||
|
}
|
29
src/client/http/RemoveTagRequest.cpp
Normal file
29
src/client/http/RemoveTagRequest.cpp
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
#include "RemoveTagRequest.h"
|
||||||
|
#include "client/Client.h"
|
||||||
|
#include "Config.h"
|
||||||
|
|
||||||
|
namespace http
|
||||||
|
{
|
||||||
|
RemoveTagRequest::RemoveTagRequest(int saveID, ByteString tag) :
|
||||||
|
APIRequest(ByteString::Build(SCHEME, SERVER, "/Browse/EditTag.json?Op=delete&ID=", saveID, "&Tag=", tag, "&Key=", Client::Ref().GetAuthUser().SessionKey), authRequire, true)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
std::list<ByteString> RemoveTagRequest::Finish()
|
||||||
|
{
|
||||||
|
auto result = APIRequest::Finish();
|
||||||
|
std::list<ByteString> tags;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
for (auto &tag : result["Tags"])
|
||||||
|
{
|
||||||
|
tags.push_back(tag.asString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (const std::exception &ex)
|
||||||
|
{
|
||||||
|
throw RequestError("Could not read response: " + ByteString(ex.what()));
|
||||||
|
}
|
||||||
|
return tags;
|
||||||
|
}
|
||||||
|
}
|
14
src/client/http/RemoveTagRequest.h
Normal file
14
src/client/http/RemoveTagRequest.h
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "APIRequest.h"
|
||||||
|
#include <list>
|
||||||
|
|
||||||
|
namespace http
|
||||||
|
{
|
||||||
|
class RemoveTagRequest : public APIRequest
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
RemoveTagRequest(int saveID, ByteString tag);
|
||||||
|
|
||||||
|
std::list<ByteString> Finish();
|
||||||
|
};
|
||||||
|
}
|
19
src/client/http/ReportSaveRequest.cpp
Normal file
19
src/client/http/ReportSaveRequest.cpp
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
#include "ReportSaveRequest.h"
|
||||||
|
#include "client/Client.h"
|
||||||
|
#include "Config.h"
|
||||||
|
|
||||||
|
namespace http
|
||||||
|
{
|
||||||
|
ReportSaveRequest::ReportSaveRequest(int saveID, String message) :
|
||||||
|
APIRequest(ByteString::Build(SCHEME, SERVER, "/Browse/Report.json?ID=", saveID, "&Key=", Client::Ref().GetAuthUser().SessionKey), authRequire, true)
|
||||||
|
{
|
||||||
|
AddPostData(FormData{
|
||||||
|
{ "Reason", message.ToUtf8() },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void ReportSaveRequest::Finish()
|
||||||
|
{
|
||||||
|
APIRequest::Finish();
|
||||||
|
}
|
||||||
|
}
|
13
src/client/http/ReportSaveRequest.h
Normal file
13
src/client/http/ReportSaveRequest.h
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "APIRequest.h"
|
||||||
|
|
||||||
|
namespace http
|
||||||
|
{
|
||||||
|
class ReportSaveRequest : public APIRequest
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ReportSaveRequest(int saveID, String message);
|
||||||
|
|
||||||
|
void Finish();
|
||||||
|
};
|
||||||
|
}
|
@ -2,6 +2,8 @@
|
|||||||
#include "requestmanager/RequestManager.h"
|
#include "requestmanager/RequestManager.h"
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
#include <cstring>
|
||||||
|
#include <json/json.h>
|
||||||
|
|
||||||
namespace http
|
namespace http
|
||||||
{
|
{
|
||||||
@ -13,12 +15,28 @@ namespace http
|
|||||||
|
|
||||||
Request::~Request()
|
Request::~Request()
|
||||||
{
|
{
|
||||||
if (handle->state != RequestHandle::ready)
|
bool tryUnregister;
|
||||||
{
|
{
|
||||||
|
std::lock_guard lk(handle->stateMx);
|
||||||
|
tryUnregister = handle->state == RequestHandle::running;
|
||||||
|
}
|
||||||
|
if (tryUnregister)
|
||||||
|
{
|
||||||
|
// At this point it may have already finished and been unregistered but that's ok,
|
||||||
|
// attempting to unregister a request multiple times is allowed. We only do the
|
||||||
|
// state-checking dance so we don't wake up RequestManager if we don't have to.
|
||||||
|
// In fact, we could just not unregister requests here at all, they'd just run to
|
||||||
|
// completion and be unregistered later. All this does is cancel them early.
|
||||||
RequestManager::Ref().UnregisterRequest(*this);
|
RequestManager::Ref().UnregisterRequest(*this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Request::FailEarly(ByteString error)
|
||||||
|
{
|
||||||
|
assert(handle->state == RequestHandle::ready);
|
||||||
|
handle->failEarly = error;
|
||||||
|
}
|
||||||
|
|
||||||
void Request::Verb(ByteString newVerb)
|
void Request::Verb(ByteString newVerb)
|
||||||
{
|
{
|
||||||
assert(handle->state == RequestHandle::ready);
|
assert(handle->state == RequestHandle::ready);
|
||||||
@ -84,18 +102,36 @@ namespace http
|
|||||||
return handle->responseHeaders;
|
return handle->responseHeaders;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::pair<int, ByteString> Request::Finish()
|
void Request::Wait()
|
||||||
{
|
{
|
||||||
std::unique_lock lk(handle->stateMx);
|
std::unique_lock lk(handle->stateMx);
|
||||||
if (handle->state == RequestHandle::running)
|
assert(handle->state == RequestHandle::running);
|
||||||
|
handle->stateCv.wait(lk, [this]() {
|
||||||
|
return handle->state == RequestHandle::done;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
int Request::StatusCode() const
|
||||||
|
{
|
||||||
{
|
{
|
||||||
handle->stateCv.wait(lk, [this]() {
|
std::unique_lock lk(handle->stateMx);
|
||||||
return handle->state == RequestHandle::done;
|
assert(handle->state == RequestHandle::done);
|
||||||
});
|
}
|
||||||
|
return handle->statusCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::pair<int, ByteString> Request::Finish()
|
||||||
|
{
|
||||||
|
{
|
||||||
|
std::unique_lock lk(handle->stateMx);
|
||||||
|
assert(handle->state == RequestHandle::done);
|
||||||
}
|
}
|
||||||
assert(handle->state == RequestHandle::done);
|
|
||||||
handle->state = RequestHandle::dead;
|
handle->state = RequestHandle::dead;
|
||||||
return { handle->statusCode, std::move(handle->responseData) };
|
if (handle->error)
|
||||||
|
{
|
||||||
|
throw RequestError(*handle->error);
|
||||||
|
}
|
||||||
|
return std::pair{ handle->statusCode, std::move(handle->responseData) };
|
||||||
}
|
}
|
||||||
|
|
||||||
void RequestHandle::MarkDone()
|
void RequestHandle::MarkDone()
|
||||||
@ -106,30 +142,13 @@ namespace http
|
|||||||
state = RequestHandle::done;
|
state = RequestHandle::done;
|
||||||
}
|
}
|
||||||
stateCv.notify_one();
|
stateCv.notify_one();
|
||||||
if (error.size())
|
if (error)
|
||||||
{
|
{
|
||||||
std::cerr << error << std::endl;
|
std::cerr << *error << std::endl;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::pair<int, ByteString> Request::Simple(ByteString uri, FormData postData)
|
const char *StatusText(int ret)
|
||||||
{
|
|
||||||
return SimpleAuth(uri, "", "", postData);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::pair<int, ByteString> Request::SimpleAuth(ByteString uri, ByteString ID, ByteString session, FormData postData)
|
|
||||||
{
|
|
||||||
auto request = std::make_unique<Request>(uri);
|
|
||||||
if (!postData.empty())
|
|
||||||
{
|
|
||||||
request->AddPostData(postData);
|
|
||||||
}
|
|
||||||
request->AuthHeaders(ID, session);
|
|
||||||
request->Start();
|
|
||||||
return request->Finish();
|
|
||||||
}
|
|
||||||
|
|
||||||
String StatusText(int ret)
|
|
||||||
{
|
{
|
||||||
switch (ret)
|
switch (ret)
|
||||||
{
|
{
|
||||||
@ -211,7 +230,69 @@ namespace http
|
|||||||
case 619: return "SSL: Failed to Load CRL File";
|
case 619: return "SSL: Failed to Load CRL File";
|
||||||
case 620: return "SSL: Issuer Check Failed";
|
case 620: return "SSL: Issuer Check Failed";
|
||||||
case 621: return "SSL: Pinned Public Key Mismatch";
|
case 621: return "SSL: Pinned Public Key Mismatch";
|
||||||
default: return "Unknown Status Code";
|
}
|
||||||
|
return "Unknown Status Code";
|
||||||
|
}
|
||||||
|
|
||||||
|
void Request::ParseResponse(const ByteString &result, int status, ResponseType responseType)
|
||||||
|
{
|
||||||
|
// no server response, return "Malformed Response"
|
||||||
|
if (status == 200 && !result.size())
|
||||||
|
{
|
||||||
|
status = 603;
|
||||||
|
}
|
||||||
|
if (status == 302)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (status != 200)
|
||||||
|
{
|
||||||
|
throw RequestError(ByteString::Build("HTTP Error ", status, ": ", http::StatusText(status)));
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (responseType)
|
||||||
|
{
|
||||||
|
case responseOk:
|
||||||
|
if (strncmp(result.c_str(), "OK", 2))
|
||||||
|
{
|
||||||
|
throw RequestError(result);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case responseJson:
|
||||||
|
{
|
||||||
|
std::istringstream ss(result);
|
||||||
|
Json::Value root;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
ss >> root;
|
||||||
|
// assume everything is fine if an empty [] is returned
|
||||||
|
if (root.size() == 0)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
int status = root.get("Status", 1).asInt();
|
||||||
|
if (status != 1)
|
||||||
|
{
|
||||||
|
throw RequestError(ByteString(root.get("Error", "Unspecified Error").asString()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (const std::exception &ex)
|
||||||
|
{
|
||||||
|
// sometimes the server returns a 200 with the text "Error: 401"
|
||||||
|
if (!strncmp(result.c_str(), "Error: ", 7))
|
||||||
|
{
|
||||||
|
status = ByteString(result.begin() + 7, result.end()).ToNumber<int>();
|
||||||
|
throw RequestError(ByteString::Build("HTTP Error ", status, ": ", http::StatusText(status)));
|
||||||
|
}
|
||||||
|
throw RequestError("Could not read response: " + ByteString(ex.what()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case responseData:
|
||||||
|
// no further processing required
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,11 +6,18 @@
|
|||||||
#include <vector>
|
#include <vector>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <condition_variable>
|
#include <condition_variable>
|
||||||
|
#include <optional>
|
||||||
|
|
||||||
namespace http
|
namespace http
|
||||||
{
|
{
|
||||||
struct RequestHandle;
|
struct RequestHandle;
|
||||||
|
|
||||||
|
// Thrown by Finish and ParseResponse
|
||||||
|
struct RequestError : public std::runtime_error
|
||||||
|
{
|
||||||
|
using runtime_error::runtime_error;
|
||||||
|
};
|
||||||
|
|
||||||
class Request
|
class Request
|
||||||
{
|
{
|
||||||
std::shared_ptr<RequestHandle> handle;
|
std::shared_ptr<RequestHandle> handle;
|
||||||
@ -21,6 +28,8 @@ namespace http
|
|||||||
Request &operator =(const Request &) = delete;
|
Request &operator =(const Request &) = delete;
|
||||||
~Request();
|
~Request();
|
||||||
|
|
||||||
|
void FailEarly(ByteString error);
|
||||||
|
|
||||||
void Verb(ByteString newVerb);
|
void Verb(ByteString newVerb);
|
||||||
void AddHeader(ByteString header);
|
void AddHeader(ByteString header);
|
||||||
|
|
||||||
@ -32,13 +41,21 @@ namespace http
|
|||||||
|
|
||||||
std::pair<int, int> CheckProgress() const; // total, done
|
std::pair<int, int> CheckProgress() const; // total, done
|
||||||
const std::vector<ByteString> &ResponseHeaders() const;
|
const std::vector<ByteString> &ResponseHeaders() const;
|
||||||
|
void Wait();
|
||||||
|
|
||||||
|
int StatusCode() const; // status
|
||||||
std::pair<int, ByteString> Finish(); // status, data
|
std::pair<int, ByteString> Finish(); // status, data
|
||||||
|
|
||||||
static std::pair<int, ByteString> Simple(ByteString uri, FormData postData = {});
|
enum ResponseType
|
||||||
static std::pair<int, ByteString> SimpleAuth(ByteString uri, ByteString ID, ByteString session, FormData postData = {});
|
{
|
||||||
|
responseOk,
|
||||||
|
responseJson,
|
||||||
|
responseData,
|
||||||
|
};
|
||||||
|
static void ParseResponse(const ByteString &result, int status, ResponseType responseType);
|
||||||
|
|
||||||
friend class RequestManager;
|
friend class RequestManager;
|
||||||
};
|
};
|
||||||
|
|
||||||
String StatusText(int code);
|
const char *StatusText(int code);
|
||||||
}
|
}
|
||||||
|
@ -1,29 +1,20 @@
|
|||||||
#include "SaveUserInfoRequest.h"
|
#include "SaveUserInfoRequest.h"
|
||||||
#include "client/UserInfo.h"
|
|
||||||
#include "Config.h"
|
#include "Config.h"
|
||||||
|
|
||||||
namespace http
|
namespace http
|
||||||
{
|
{
|
||||||
SaveUserInfoRequest::SaveUserInfoRequest(UserInfo &info) :
|
SaveUserInfoRequest::SaveUserInfoRequest(UserInfo info) :
|
||||||
APIRequest(ByteString::Build(SCHEME, SERVER, "/Profile.json"))
|
APIRequest(ByteString::Build(SCHEME, SERVER, "/Profile.json"), authRequire, true)
|
||||||
{
|
{
|
||||||
AddPostData(FormData{
|
AddPostData(FormData{
|
||||||
{ "Location", info.location.ToUtf8() },
|
{ "Location", info.location.ToUtf8() },
|
||||||
{ "Biography", info.biography.ToUtf8() }
|
{ "Biography", info.biography.ToUtf8() },
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SaveUserInfoRequest::Finish()
|
void SaveUserInfoRequest::Finish()
|
||||||
{
|
{
|
||||||
auto result = APIRequest::Finish();
|
APIRequest::Finish();
|
||||||
if (result.document)
|
|
||||||
{
|
|
||||||
return (*result.document)["Status"].asInt() == 1;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,15 +1,14 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include "APIRequest.h"
|
#include "APIRequest.h"
|
||||||
|
#include "client/UserInfo.h"
|
||||||
class UserInfo;
|
|
||||||
|
|
||||||
namespace http
|
namespace http
|
||||||
{
|
{
|
||||||
class SaveUserInfoRequest : public APIRequest
|
class SaveUserInfoRequest : public APIRequest
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
SaveUserInfoRequest(UserInfo &info);
|
SaveUserInfoRequest(UserInfo info);
|
||||||
|
|
||||||
bool Finish();
|
void Finish();
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
85
src/client/http/SearchSavesRequest.cpp
Normal file
85
src/client/http/SearchSavesRequest.cpp
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
#include "SearchSavesRequest.h"
|
||||||
|
#include "Config.h"
|
||||||
|
#include "client/Client.h"
|
||||||
|
#include "client/GameSave.h"
|
||||||
|
#include "Format.h"
|
||||||
|
|
||||||
|
namespace http
|
||||||
|
{
|
||||||
|
static ByteString Url(int start, int count, ByteString query, Sort sort, Category category)
|
||||||
|
{
|
||||||
|
ByteStringBuilder builder;
|
||||||
|
builder << SCHEME << SERVER << "/Browse.json?Start=" << start << "&Count=" << count;
|
||||||
|
auto appendToQuery = [&query](ByteString str) {
|
||||||
|
if (query.size())
|
||||||
|
{
|
||||||
|
query += " ";
|
||||||
|
}
|
||||||
|
query += str;
|
||||||
|
};
|
||||||
|
switch (sort)
|
||||||
|
{
|
||||||
|
case sortByDate:
|
||||||
|
appendToQuery("sort:date");
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
auto user = Client::Ref().GetAuthUser();
|
||||||
|
switch (category)
|
||||||
|
{
|
||||||
|
case categoryFavourites:
|
||||||
|
builder << "&Category=Favourites";
|
||||||
|
break;
|
||||||
|
|
||||||
|
case categoryMyOwn:
|
||||||
|
assert(user.UserID);
|
||||||
|
appendToQuery("user:" + user.Username);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (query.size())
|
||||||
|
{
|
||||||
|
builder << "&Search_Query=" << format::URLEncode(query);
|
||||||
|
}
|
||||||
|
return builder.Build();
|
||||||
|
}
|
||||||
|
|
||||||
|
SearchSavesRequest::SearchSavesRequest(int start, int count, ByteString query, Sort sort, Category category) : APIRequest(Url(start, count, query, sort, category), authUse, false)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
std::pair<int, std::vector<std::unique_ptr<SaveInfo>>> SearchSavesRequest::Finish()
|
||||||
|
{
|
||||||
|
std::vector<std::unique_ptr<SaveInfo>> saves;
|
||||||
|
auto result = APIRequest::Finish();
|
||||||
|
int count;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
count = result["Count"].asInt();
|
||||||
|
for (auto &save : result["Saves"])
|
||||||
|
{
|
||||||
|
auto saveInfo = std::make_unique<SaveInfo>(
|
||||||
|
save["ID"].asInt(),
|
||||||
|
save["Created"].asInt(),
|
||||||
|
save["Updated"].asInt(),
|
||||||
|
save["ScoreUp"].asInt(),
|
||||||
|
save["ScoreDown"].asInt(),
|
||||||
|
save["Username"].asString(),
|
||||||
|
ByteString(save["Name"].asString()).FromUtf8()
|
||||||
|
);
|
||||||
|
saveInfo->Version = save["Version"].asInt();
|
||||||
|
saveInfo->SetPublished(save["Published"].asBool());
|
||||||
|
saves.push_back(std::move(saveInfo));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (const std::exception &ex)
|
||||||
|
{
|
||||||
|
throw RequestError("Could not read response: " + ByteString(ex.what()));
|
||||||
|
}
|
||||||
|
return std::pair{ count, std::move(saves) };
|
||||||
|
}
|
||||||
|
}
|
15
src/client/http/SearchSavesRequest.h
Normal file
15
src/client/http/SearchSavesRequest.h
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "APIRequest.h"
|
||||||
|
#include "client/SaveInfo.h"
|
||||||
|
#include "client/Search.h"
|
||||||
|
|
||||||
|
namespace http
|
||||||
|
{
|
||||||
|
class SearchSavesRequest : public APIRequest
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
SearchSavesRequest(int start, int count, ByteString query, Sort sort, Category category);
|
||||||
|
|
||||||
|
std::pair<int, std::vector<std::unique_ptr<SaveInfo>>> Finish();
|
||||||
|
};
|
||||||
|
}
|
42
src/client/http/SearchTagsRequest.cpp
Normal file
42
src/client/http/SearchTagsRequest.cpp
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
#include "SearchTagsRequest.h"
|
||||||
|
#include "Config.h"
|
||||||
|
#include "Format.h"
|
||||||
|
|
||||||
|
namespace http
|
||||||
|
{
|
||||||
|
static ByteString Url(int start, int count, ByteString query)
|
||||||
|
{
|
||||||
|
ByteStringBuilder builder;
|
||||||
|
builder << SCHEME << SERVER << "/Browse/Tags.json?Start=" << start << "&Count=" << count;
|
||||||
|
if (query.size())
|
||||||
|
{
|
||||||
|
builder << "&Search_Query=" << format::URLEncode(query);
|
||||||
|
}
|
||||||
|
return builder.Build();
|
||||||
|
}
|
||||||
|
|
||||||
|
SearchTagsRequest::SearchTagsRequest(int start, int count, ByteString query) : APIRequest(Url(start, count, query), authOmit, false)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::pair<ByteString, int>> SearchTagsRequest::Finish()
|
||||||
|
{
|
||||||
|
std::vector<std::pair<ByteString, int>> tags;
|
||||||
|
auto result = APIRequest::Finish();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
for (auto &tag : result["Tags"])
|
||||||
|
{
|
||||||
|
tags.push_back({
|
||||||
|
tag["Tag"].asString(),
|
||||||
|
tag["Count"].asInt(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (const std::exception &ex)
|
||||||
|
{
|
||||||
|
throw RequestError("Could not read response: " + ByteString(ex.what()));
|
||||||
|
}
|
||||||
|
return tags;
|
||||||
|
}
|
||||||
|
}
|
13
src/client/http/SearchTagsRequest.h
Normal file
13
src/client/http/SearchTagsRequest.h
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "APIRequest.h"
|
||||||
|
|
||||||
|
namespace http
|
||||||
|
{
|
||||||
|
class SearchTagsRequest : public APIRequest
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
SearchTagsRequest(int start, int count, ByteString query);
|
||||||
|
|
||||||
|
std::vector<std::pair<ByteString, int>> Finish();
|
||||||
|
};
|
||||||
|
}
|
102
src/client/http/StartupRequest.cpp
Normal file
102
src/client/http/StartupRequest.cpp
Normal file
@ -0,0 +1,102 @@
|
|||||||
|
#include "StartupRequest.h"
|
||||||
|
#include "client/Client.h"
|
||||||
|
#include "Config.h"
|
||||||
|
|
||||||
|
namespace http
|
||||||
|
{
|
||||||
|
// TODO: update Client::messageOfTheDay
|
||||||
|
StartupRequest::StartupRequest(bool newAlternate) :
|
||||||
|
Request(ByteString::Build(SCHEME, newAlternate ? UPDATESERVER : SERVER, "/Startup.json")),
|
||||||
|
alternate(newAlternate)
|
||||||
|
{
|
||||||
|
auto user = Client::Ref().GetAuthUser();
|
||||||
|
if (user.UserID)
|
||||||
|
{
|
||||||
|
if (alternate)
|
||||||
|
{
|
||||||
|
// Cursed
|
||||||
|
AuthHeaders(user.Username, "");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
AuthHeaders(ByteString::Build(user.UserID), user.SessionID);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
StartupInfo StartupRequest::Finish()
|
||||||
|
{
|
||||||
|
auto [ status, data ] = Request::Finish();
|
||||||
|
ParseResponse(data, status, responseJson);
|
||||||
|
StartupInfo startupInfo;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Json::Value document;
|
||||||
|
std::istringstream ss(data);
|
||||||
|
ss >> document;
|
||||||
|
startupInfo.sessionGood = document["Session"].asBool();
|
||||||
|
startupInfo.messageOfTheDay = ByteString(document["MessageOfTheDay"].asString()).FromUtf8();
|
||||||
|
for (auto ¬ification : document["Notifications"])
|
||||||
|
{
|
||||||
|
startupInfo.notifications.push_back({
|
||||||
|
ByteString(notification["Text"].asString()).FromUtf8(),
|
||||||
|
notification["Link"].asString()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if constexpr (!IGNORE_UPDATES)
|
||||||
|
{
|
||||||
|
auto &versions = document["Updates"];
|
||||||
|
auto parseUpdate = [this, &versions, &startupInfo](ByteString key, UpdateInfo::Channel channel, std::function<bool (int)> updateAvailableFunc) {
|
||||||
|
if (!versions.isMember(key))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
auto &info = versions[key];
|
||||||
|
auto getOr = [&info](ByteString key, int defaultValue) {
|
||||||
|
if (!info.isMember(key))
|
||||||
|
{
|
||||||
|
return defaultValue;
|
||||||
|
}
|
||||||
|
return info[key].asInt();
|
||||||
|
};
|
||||||
|
auto build = getOr(key == "Snapshot" ? "Snapshot" : "Build", -1);
|
||||||
|
if (!updateAvailableFunc(build))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
startupInfo.updateInfo = UpdateInfo{
|
||||||
|
channel,
|
||||||
|
ByteString::Build(SCHEME, alternate ? UPDATESERVER : SERVER, info["File"].asString()),
|
||||||
|
ByteString(info["Changelog"].asString()).FromUtf8(),
|
||||||
|
getOr("Major", -1),
|
||||||
|
getOr("Minor", -1),
|
||||||
|
build,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
if constexpr (SNAPSHOT || MOD)
|
||||||
|
{
|
||||||
|
parseUpdate("Snapshot", UpdateInfo::channelSnapshot, [](int build) {
|
||||||
|
return build > SNAPSHOT_ID;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
parseUpdate("Stable", UpdateInfo::channelStable, [](int build) {
|
||||||
|
return build > BUILD_NUM;
|
||||||
|
});
|
||||||
|
if (!startupInfo.updateInfo.has_value())
|
||||||
|
{
|
||||||
|
parseUpdate("Beta", UpdateInfo::channelBeta, [](int build) {
|
||||||
|
return build > BUILD_NUM;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (const std::exception &ex)
|
||||||
|
{
|
||||||
|
throw RequestError("Could not read response: " + ByteString(ex.what()));
|
||||||
|
}
|
||||||
|
return startupInfo;
|
||||||
|
}
|
||||||
|
}
|
16
src/client/http/StartupRequest.h
Normal file
16
src/client/http/StartupRequest.h
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "APIRequest.h"
|
||||||
|
#include "client/StartupInfo.h"
|
||||||
|
|
||||||
|
namespace http
|
||||||
|
{
|
||||||
|
class StartupRequest : public Request
|
||||||
|
{
|
||||||
|
bool alternate;
|
||||||
|
|
||||||
|
public:
|
||||||
|
StartupRequest(bool newAlternate);
|
||||||
|
|
||||||
|
StartupInfo Finish();
|
||||||
|
};
|
||||||
|
}
|
17
src/client/http/UnpublishSaveRequest.cpp
Normal file
17
src/client/http/UnpublishSaveRequest.cpp
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
#include "UnpublishSaveRequest.h"
|
||||||
|
#include "client/Client.h"
|
||||||
|
#include "Config.h"
|
||||||
|
|
||||||
|
namespace http
|
||||||
|
{
|
||||||
|
UnpublishSaveRequest::UnpublishSaveRequest(int saveID) :
|
||||||
|
APIRequest(ByteString::Build(SCHEME, SERVER, "/Browse/Delete.json?ID=", saveID, "&Mode=Unpublish&Key=", Client::Ref().GetAuthUser().SessionKey), authRequire, true)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void UnpublishSaveRequest::Finish()
|
||||||
|
{
|
||||||
|
APIRequest::Finish();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
13
src/client/http/UnpublishSaveRequest.h
Normal file
13
src/client/http/UnpublishSaveRequest.h
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "APIRequest.h"
|
||||||
|
|
||||||
|
namespace http
|
||||||
|
{
|
||||||
|
class UnpublishSaveRequest : public APIRequest
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
UnpublishSaveRequest(int saveID);
|
||||||
|
|
||||||
|
void Finish();
|
||||||
|
};
|
||||||
|
}
|
0
src/client/http/UpdateRequest.cpp
Normal file
0
src/client/http/UpdateRequest.cpp
Normal file
0
src/client/http/UpdateRequest.h
Normal file
0
src/client/http/UpdateRequest.h
Normal file
49
src/client/http/UploadSaveRequest.cpp
Normal file
49
src/client/http/UploadSaveRequest.cpp
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
#include "UploadSaveRequest.h"
|
||||||
|
#include "client/SaveInfo.h"
|
||||||
|
#include "client/Client.h"
|
||||||
|
#include "client/GameSave.h"
|
||||||
|
#include "Config.h"
|
||||||
|
|
||||||
|
namespace http
|
||||||
|
{
|
||||||
|
UploadSaveRequest::UploadSaveRequest(const SaveInfo &saveInfo) : Request(ByteString::Build(SCHEME, SERVER, "/Save.api"))
|
||||||
|
{
|
||||||
|
auto [ fromNewerVersion, gameData ] = saveInfo.GetGameSave()->Serialise();
|
||||||
|
if (!gameData.size())
|
||||||
|
{
|
||||||
|
FailEarly("Cannot serialize game save");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else if (ALLOW_FAKE_NEWER_VERSION && fromNewerVersion && saveInfo.GetPublished())
|
||||||
|
{
|
||||||
|
FailEarly("Cannot publish save, incompatible with latest release version");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
auto user = Client::Ref().GetAuthUser();
|
||||||
|
if (!user.UserID)
|
||||||
|
{
|
||||||
|
FailEarly("Not authenticated");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
AuthHeaders(ByteString::Build(user.UserID), user.SessionID);
|
||||||
|
AddPostData(FormData{
|
||||||
|
{ "Name", saveInfo.GetName().ToUtf8() },
|
||||||
|
{ "Description", saveInfo.GetDescription().ToUtf8() },
|
||||||
|
{ "Data:save.bin", ByteString(gameData.begin(), gameData.end()) },
|
||||||
|
{ "Publish", saveInfo.GetPublished() ? "Public" : "Private" },
|
||||||
|
{ "Key", user.SessionKey },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
int UploadSaveRequest::Finish()
|
||||||
|
{
|
||||||
|
auto [ status, data ] = Request::Finish();
|
||||||
|
ParseResponse(data, status, responseOk);
|
||||||
|
int saveID = ByteString(data.begin() + 3, data.end()).ToNumber<int>();
|
||||||
|
if (!saveID)
|
||||||
|
{
|
||||||
|
throw RequestError("Server did not return Save ID");
|
||||||
|
}
|
||||||
|
return saveID;
|
||||||
|
}
|
||||||
|
}
|
15
src/client/http/UploadSaveRequest.h
Normal file
15
src/client/http/UploadSaveRequest.h
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "Request.h"
|
||||||
|
|
||||||
|
class SaveInfo;
|
||||||
|
|
||||||
|
namespace http
|
||||||
|
{
|
||||||
|
class UploadSaveRequest : public Request
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
UploadSaveRequest(const SaveInfo &saveInfo);
|
||||||
|
|
||||||
|
int Finish();
|
||||||
|
};
|
||||||
|
}
|
@ -5,6 +5,25 @@ client_files += files(
|
|||||||
'Request.cpp',
|
'Request.cpp',
|
||||||
'SaveUserInfoRequest.cpp',
|
'SaveUserInfoRequest.cpp',
|
||||||
'ThumbnailRequest.cpp',
|
'ThumbnailRequest.cpp',
|
||||||
|
'AddTagRequest.cpp',
|
||||||
|
'RemoveTagRequest.cpp',
|
||||||
|
'GetSaveRequest.cpp',
|
||||||
|
'PublishSaveRequest.cpp',
|
||||||
|
'UnpublishSaveRequest.cpp',
|
||||||
|
'ReportSaveRequest.cpp',
|
||||||
|
'FavouriteSaveRequest.cpp',
|
||||||
|
'AddCommentRequest.cpp',
|
||||||
|
'DeleteSaveRequest.cpp',
|
||||||
|
'LoginRequest.cpp',
|
||||||
|
'GetSaveDataRequest.cpp',
|
||||||
|
'ExecVoteRequest.cpp',
|
||||||
|
'UploadSaveRequest.cpp',
|
||||||
|
'StartupRequest.cpp',
|
||||||
|
'UpdateRequest.cpp',
|
||||||
|
'SearchSavesRequest.cpp',
|
||||||
|
'SearchTagsRequest.cpp',
|
||||||
|
'GetCommentsRequest.cpp',
|
||||||
|
'LogoutRequest.cpp',
|
||||||
)
|
)
|
||||||
|
|
||||||
subdir('requestmanager')
|
subdir('requestmanager')
|
||||||
|
@ -22,6 +22,13 @@ namespace http
|
|||||||
|
|
||||||
void RequestManager::RegisterRequest(Request &request)
|
void RequestManager::RegisterRequest(Request &request)
|
||||||
{
|
{
|
||||||
|
if (request.handle->failEarly)
|
||||||
|
{
|
||||||
|
request.handle->error = request.handle->failEarly.value();
|
||||||
|
request.handle->statusCode = 600;
|
||||||
|
request.handle->MarkDone();
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (disableNetwork)
|
if (disableNetwork)
|
||||||
{
|
{
|
||||||
request.handle->statusCode = 604;
|
request.handle->statusCode = 604;
|
||||||
|
@ -285,16 +285,20 @@ namespace http
|
|||||||
void RequestManager::RegisterRequestImpl(Request &request)
|
void RequestManager::RegisterRequestImpl(Request &request)
|
||||||
{
|
{
|
||||||
auto manager = static_cast<RequestManagerImpl *>(this);
|
auto manager = static_cast<RequestManagerImpl *>(this);
|
||||||
std::lock_guard lk(manager->sharedStateMx);
|
{
|
||||||
manager->requestHandlesToRegister.push_back(request.handle);
|
std::lock_guard lk(manager->sharedStateMx);
|
||||||
|
manager->requestHandlesToRegister.push_back(request.handle);
|
||||||
|
}
|
||||||
curl_multi_wakeup(manager->curlMulti);
|
curl_multi_wakeup(manager->curlMulti);
|
||||||
}
|
}
|
||||||
|
|
||||||
void RequestManager::UnregisterRequestImpl(Request &request)
|
void RequestManager::UnregisterRequestImpl(Request &request)
|
||||||
{
|
{
|
||||||
auto manager = static_cast<RequestManagerImpl *>(this);
|
auto manager = static_cast<RequestManagerImpl *>(this);
|
||||||
std::lock_guard lk(manager->sharedStateMx);
|
{
|
||||||
manager->requestHandlesToUnregister.push_back(request.handle);
|
std::lock_guard lk(manager->sharedStateMx);
|
||||||
|
manager->requestHandlesToUnregister.push_back(request.handle);
|
||||||
|
}
|
||||||
curl_multi_wakeup(manager->curlMulti);
|
curl_multi_wakeup(manager->curlMulti);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8,6 +8,7 @@
|
|||||||
#include <memory>
|
#include <memory>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <condition_variable>
|
#include <condition_variable>
|
||||||
|
#include <optional>
|
||||||
|
|
||||||
namespace http
|
namespace http
|
||||||
{
|
{
|
||||||
@ -42,7 +43,8 @@ namespace http
|
|||||||
int statusCode = 0;
|
int statusCode = 0;
|
||||||
ByteString responseData;
|
ByteString responseData;
|
||||||
std::vector<ByteString> responseHeaders;
|
std::vector<ByteString> responseHeaders;
|
||||||
ByteString error;
|
std::optional<ByteString> error;
|
||||||
|
std::optional<ByteString> failEarly;
|
||||||
|
|
||||||
RequestHandle(CtorTag)
|
RequestHandle(CtorTag)
|
||||||
{
|
{
|
||||||
|
@ -5,6 +5,7 @@ client_files = files(
|
|||||||
'ThumbnailRendererTask.cpp',
|
'ThumbnailRendererTask.cpp',
|
||||||
'Client.cpp',
|
'Client.cpp',
|
||||||
'GameSave.cpp',
|
'GameSave.cpp',
|
||||||
|
'User.cpp',
|
||||||
)
|
)
|
||||||
|
|
||||||
subdir('http')
|
subdir('http')
|
||||||
|
@ -63,6 +63,7 @@
|
|||||||
|
|
||||||
#include "Config.h"
|
#include "Config.h"
|
||||||
#include <SDL.h>
|
#include <SDL.h>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
#ifdef GetUserName
|
#ifdef GetUserName
|
||||||
# undef GetUserName // dammit windows
|
# undef GetUserName // dammit windows
|
||||||
@ -699,6 +700,7 @@ bool GameController::KeyRelease(int key, int scan, bool repeat, bool shift, bool
|
|||||||
|
|
||||||
void GameController::Tick()
|
void GameController::Tick()
|
||||||
{
|
{
|
||||||
|
gameModel->Tick();
|
||||||
if(firstTick)
|
if(firstTick)
|
||||||
{
|
{
|
||||||
commandInterface->Init();
|
commandInterface->Init();
|
||||||
@ -1440,16 +1442,9 @@ void GameController::FrameStep()
|
|||||||
|
|
||||||
void GameController::Vote(int direction)
|
void GameController::Vote(int direction)
|
||||||
{
|
{
|
||||||
if(gameModel->GetSave() && gameModel->GetUser().UserID && gameModel->GetSave()->GetID())
|
if (gameModel->GetSave() && gameModel->GetUser().UserID && gameModel->GetSave()->GetID())
|
||||||
{
|
{
|
||||||
try
|
gameModel->SetVote(direction);
|
||||||
{
|
|
||||||
gameModel->SetVote(direction);
|
|
||||||
}
|
|
||||||
catch(GameModelException & ex)
|
|
||||||
{
|
|
||||||
new ErrorMessage("Error while voting", ByteString(ex.what()).FromUtf8());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1536,7 +1531,7 @@ void GameController::NotifyAuthUserChanged(Client * sender)
|
|||||||
gameModel->SetUser(newUser);
|
gameModel->SetUser(newUser);
|
||||||
}
|
}
|
||||||
|
|
||||||
void GameController::NotifyNewNotification(Client * sender, std::pair<String, ByteString> notification)
|
void GameController::NotifyNewNotification(Client * sender, ServerNotification notification)
|
||||||
{
|
{
|
||||||
class LinkNotification : public Notification
|
class LinkNotification : public Notification
|
||||||
{
|
{
|
||||||
@ -1550,7 +1545,7 @@ void GameController::NotifyNewNotification(Client * sender, std::pair<String, By
|
|||||||
Platform::OpenURI(link);
|
Platform::OpenURI(link);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
gameModel->AddNotification(new LinkNotification(notification.second, notification.first));
|
gameModel->AddNotification(new LinkNotification(notification.link, notification.text));
|
||||||
}
|
}
|
||||||
|
|
||||||
void GameController::NotifyUpdateAvailable(Client * sender)
|
void GameController::NotifyUpdateAvailable(Client * sender)
|
||||||
@ -1564,7 +1559,13 @@ void GameController::NotifyUpdateAvailable(Client * sender)
|
|||||||
|
|
||||||
void Action() override
|
void Action() override
|
||||||
{
|
{
|
||||||
UpdateInfo info = Client::Ref().GetUpdateInfo();
|
auto optinfo = Client::Ref().GetUpdateInfo();
|
||||||
|
if (!optinfo.has_value())
|
||||||
|
{
|
||||||
|
std::cerr << "odd, the update has disappeared" << std::endl;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
UpdateInfo info = optinfo.value();
|
||||||
StringBuilder updateMessage;
|
StringBuilder updateMessage;
|
||||||
if (Platform::CanUpdate())
|
if (Platform::CanUpdate())
|
||||||
{
|
{
|
||||||
@ -1593,36 +1594,41 @@ void GameController::NotifyUpdateAvailable(Client * sender)
|
|||||||
}
|
}
|
||||||
|
|
||||||
updateMessage << "\nNew version:\n ";
|
updateMessage << "\nNew version:\n ";
|
||||||
if (info.Type == UpdateInfo::Beta)
|
if (info.channel == UpdateInfo::channelBeta)
|
||||||
{
|
{
|
||||||
updateMessage << info.Major << "." << info.Minor << " Beta, Build " << info.Build;
|
updateMessage << info.major << "." << info.minor << " Beta, Build " << info.build;
|
||||||
}
|
}
|
||||||
else if (info.Type == UpdateInfo::Snapshot)
|
else if (info.channel == UpdateInfo::channelSnapshot)
|
||||||
{
|
{
|
||||||
if constexpr (MOD)
|
if constexpr (MOD)
|
||||||
{
|
{
|
||||||
updateMessage << "Mod version " << info.Time;
|
updateMessage << "Mod version " << info.build;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
updateMessage << "Snapshot " << info.Time;
|
updateMessage << "Snapshot " << info.build;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if(info.Type == UpdateInfo::Stable)
|
else if(info.channel == UpdateInfo::channelStable)
|
||||||
{
|
{
|
||||||
updateMessage << info.Major << "." << info.Minor << " Stable, Build " << info.Build;
|
updateMessage << info.major << "." << info.minor << " Stable, Build " << info.build;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (info.Changelog.length())
|
if (info.changeLog.length())
|
||||||
updateMessage << "\n\nChangelog:\n" << info.Changelog;
|
updateMessage << "\n\nChangelog:\n" << info.changeLog;
|
||||||
|
|
||||||
new ConfirmPrompt("Run Updater", updateMessage.Build(), { [this] { c->RunUpdater(); } });
|
new ConfirmPrompt("Run Updater", updateMessage.Build(), { [this, info] { c->RunUpdater(info); } });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
switch(sender->GetUpdateInfo().Type)
|
auto optinfo = sender->GetUpdateInfo();
|
||||||
|
if (!optinfo.has_value())
|
||||||
{
|
{
|
||||||
case UpdateInfo::Snapshot:
|
return;
|
||||||
|
}
|
||||||
|
switch(optinfo.value().channel)
|
||||||
|
{
|
||||||
|
case UpdateInfo::channelSnapshot:
|
||||||
if constexpr (MOD)
|
if constexpr (MOD)
|
||||||
{
|
{
|
||||||
gameModel->AddNotification(new UpdateNotification(this, "A new mod update is available - click here to update"));
|
gameModel->AddNotification(new UpdateNotification(this, "A new mod update is available - click here to update"));
|
||||||
@ -1632,10 +1638,10 @@ void GameController::NotifyUpdateAvailable(Client * sender)
|
|||||||
gameModel->AddNotification(new UpdateNotification(this, "A new snapshot is available - click here to update"));
|
gameModel->AddNotification(new UpdateNotification(this, "A new snapshot is available - click here to update"));
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case UpdateInfo::Stable:
|
case UpdateInfo::channelStable:
|
||||||
gameModel->AddNotification(new UpdateNotification(this, "A new version is available - click here to update"));
|
gameModel->AddNotification(new UpdateNotification(this, "A new version is available - click here to update"));
|
||||||
break;
|
break;
|
||||||
case UpdateInfo::Beta:
|
case UpdateInfo::channelBeta:
|
||||||
gameModel->AddNotification(new UpdateNotification(this, "A new beta is available - click here to update"));
|
gameModel->AddNotification(new UpdateNotification(this, "A new beta is available - click here to update"));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -1646,25 +1652,16 @@ void GameController::RemoveNotification(Notification * notification)
|
|||||||
gameModel->RemoveNotification(notification);
|
gameModel->RemoveNotification(notification);
|
||||||
}
|
}
|
||||||
|
|
||||||
void GameController::RunUpdater()
|
void GameController::RunUpdater(UpdateInfo info)
|
||||||
{
|
{
|
||||||
if (Platform::CanUpdate())
|
if (Platform::CanUpdate())
|
||||||
{
|
{
|
||||||
Exit();
|
Exit();
|
||||||
new UpdateActivity();
|
new UpdateActivity(info);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
ByteString file;
|
Platform::OpenURI(info.file);
|
||||||
if constexpr (USE_UPDATESERVER)
|
|
||||||
{
|
|
||||||
file = ByteString::Build(SCHEME, UPDATESERVER, Client::Ref().GetUpdateInfo().File);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
file = ByteString::Build(SCHEME, SERVER, Client::Ref().GetUpdateInfo().File);
|
|
||||||
}
|
|
||||||
Platform::OpenURI(file);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include "client/ClientListener.h"
|
#include "client/ClientListener.h"
|
||||||
|
#include "client/StartupInfo.h"
|
||||||
#include "gui/interface/Point.h"
|
#include "gui/interface/Point.h"
|
||||||
#include "gui/interface/Colour.h"
|
#include "gui/interface/Colour.h"
|
||||||
#include "simulation/Sign.h"
|
#include "simulation/Sign.h"
|
||||||
@ -181,8 +182,8 @@ public:
|
|||||||
|
|
||||||
void NotifyUpdateAvailable(Client * sender) override;
|
void NotifyUpdateAvailable(Client * sender) override;
|
||||||
void NotifyAuthUserChanged(Client * sender) override;
|
void NotifyAuthUserChanged(Client * sender) override;
|
||||||
void NotifyNewNotification(Client * sender, std::pair<String, ByteString> notification) override;
|
void NotifyNewNotification(Client * sender, ServerNotification notification) override;
|
||||||
void RunUpdater();
|
void RunUpdater(UpdateInfo info);
|
||||||
bool GetMouseClickRequired();
|
bool GetMouseClickRequired();
|
||||||
|
|
||||||
void RemoveCustomGOLType(const ByteString &identifier);
|
void RemoveCustomGOLType(const ByteString &identifier);
|
||||||
|
@ -17,6 +17,7 @@
|
|||||||
#include "client/GameSave.h"
|
#include "client/GameSave.h"
|
||||||
#include "client/SaveFile.h"
|
#include "client/SaveFile.h"
|
||||||
#include "client/SaveInfo.h"
|
#include "client/SaveInfo.h"
|
||||||
|
#include "client/http/ExecVoteRequest.h"
|
||||||
#include "common/platform/Platform.h"
|
#include "common/platform/Platform.h"
|
||||||
#include "graphics/Renderer.h"
|
#include "graphics/Renderer.h"
|
||||||
#include "simulation/Air.h"
|
#include "simulation/Air.h"
|
||||||
@ -30,6 +31,7 @@
|
|||||||
#include "simulation/ToolClasses.h"
|
#include "simulation/ToolClasses.h"
|
||||||
#include "gui/game/DecorationTool.h"
|
#include "gui/game/DecorationTool.h"
|
||||||
#include "gui/interface/Engine.h"
|
#include "gui/interface/Engine.h"
|
||||||
|
#include "gui/dialogues/ErrorMessage.h"
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
@ -780,18 +782,33 @@ void GameModel::SetUndoHistoryLimit(unsigned int undoHistoryLimit_)
|
|||||||
|
|
||||||
void GameModel::SetVote(int direction)
|
void GameModel::SetVote(int direction)
|
||||||
{
|
{
|
||||||
if(currentSave)
|
queuedVote = direction;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GameModel::Tick()
|
||||||
|
{
|
||||||
|
if (execVoteRequest && execVoteRequest->CheckDone())
|
||||||
{
|
{
|
||||||
RequestStatus status = Client::Ref().ExecVote(currentSave->GetID(), direction);
|
try
|
||||||
if(status == RequestOkay)
|
|
||||||
{
|
{
|
||||||
currentSave->vote = direction;
|
execVoteRequest->Finish();
|
||||||
|
currentSave->vote = execVoteRequest->Direction();
|
||||||
notifySaveChanged();
|
notifySaveChanged();
|
||||||
}
|
}
|
||||||
else
|
catch (const http::RequestError &ex)
|
||||||
{
|
{
|
||||||
throw GameModelException("Could not vote: "+Client::Ref().GetLastError());
|
new ErrorMessage("Error while voting", ByteString(ex.what()).FromUtf8());
|
||||||
}
|
}
|
||||||
|
execVoteRequest.reset();
|
||||||
|
}
|
||||||
|
if (!execVoteRequest && queuedVote)
|
||||||
|
{
|
||||||
|
if (currentSave)
|
||||||
|
{
|
||||||
|
execVoteRequest = std::make_unique<http::ExecVoteRequest>(currentSave->GetID(), *queuedVote);
|
||||||
|
execVoteRequest->Start();
|
||||||
|
}
|
||||||
|
queuedVote.reset();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
#include <vector>
|
#include <vector>
|
||||||
#include <deque>
|
#include <deque>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
#include <optional>
|
||||||
|
|
||||||
class Menu;
|
class Menu;
|
||||||
class Tool;
|
class Tool;
|
||||||
@ -21,6 +22,11 @@ class Snapshot;
|
|||||||
struct SnapshotDelta;
|
struct SnapshotDelta;
|
||||||
class GameSave;
|
class GameSave;
|
||||||
|
|
||||||
|
namespace http
|
||||||
|
{
|
||||||
|
class ExecVoteRequest;
|
||||||
|
};
|
||||||
|
|
||||||
class ToolSelection
|
class ToolSelection
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@ -40,6 +46,8 @@ struct HistoryEntry
|
|||||||
|
|
||||||
class GameModel
|
class GameModel
|
||||||
{
|
{
|
||||||
|
std::unique_ptr<http::ExecVoteRequest> execVoteRequest;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::vector<Notification*> notifications;
|
std::vector<Notification*> notifications;
|
||||||
//int clipboardSize;
|
//int clipboardSize;
|
||||||
@ -119,10 +127,14 @@ private:
|
|||||||
|
|
||||||
void SaveToSimParameters(const GameSave &saveData);
|
void SaveToSimParameters(const GameSave &saveData);
|
||||||
|
|
||||||
|
std::optional<int> queuedVote;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
GameModel();
|
GameModel();
|
||||||
~GameModel();
|
~GameModel();
|
||||||
|
|
||||||
|
void Tick();
|
||||||
|
|
||||||
void SetEdgeMode(int edgeMode);
|
void SetEdgeMode(int edgeMode);
|
||||||
int GetEdgeMode();
|
int GetEdgeMode();
|
||||||
void SetTemperatureScale(int temperatureScale);
|
void SetTemperatureScale(int temperatureScale);
|
||||||
|
@ -1426,8 +1426,7 @@ void GameView::OnKeyPress(int key, int scan, bool repeat, bool shift, bool ctrl,
|
|||||||
c->ReloadSim();
|
c->ReloadSim();
|
||||||
break;
|
break;
|
||||||
case SDL_SCANCODE_A:
|
case SDL_SCANCODE_A:
|
||||||
if ((Client::Ref().GetAuthUser().UserElevation == User::ElevationModerator
|
if (Client::Ref().GetAuthUser().UserElevation != User::ElevationNone && ctrl)
|
||||||
|| Client::Ref().GetAuthUser().UserElevation == User::ElevationAdmin) && ctrl)
|
|
||||||
{
|
{
|
||||||
ByteString authorString = Client::Ref().GetAuthorInfo().toStyledString();
|
ByteString authorString = Client::Ref().GetAuthorInfo().toStyledString();
|
||||||
new InformationMessage("Save authorship info", authorString.FromUtf8(), true);
|
new InformationMessage("Save authorship info", authorString.FromUtf8(), true);
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
#include "LoginController.h"
|
#include "LoginController.h"
|
||||||
|
|
||||||
#include "client/Client.h"
|
#include "client/Client.h"
|
||||||
|
#include "client/http/LoginRequest.h"
|
||||||
|
#include "client/http/LogoutRequest.h"
|
||||||
#include "LoginView.h"
|
#include "LoginView.h"
|
||||||
#include "LoginModel.h"
|
#include "LoginModel.h"
|
||||||
#include "Controller.h"
|
#include "Controller.h"
|
||||||
@ -23,15 +23,19 @@ void LoginController::Login(ByteString username, ByteString password)
|
|||||||
loginModel->Login(username, password);
|
loginModel->Login(username, password);
|
||||||
}
|
}
|
||||||
|
|
||||||
User LoginController::GetUser()
|
void LoginController::Logout()
|
||||||
{
|
{
|
||||||
return loginModel->GetUser();
|
loginModel->Logout();
|
||||||
|
}
|
||||||
|
|
||||||
|
void LoginController::Tick()
|
||||||
|
{
|
||||||
|
loginModel->Tick();
|
||||||
}
|
}
|
||||||
|
|
||||||
void LoginController::Exit()
|
void LoginController::Exit()
|
||||||
{
|
{
|
||||||
loginView->CloseActiveWindow();
|
loginView->CloseActiveWindow();
|
||||||
Client::Ref().SetAuthUser(loginModel->GetUser());
|
|
||||||
if (onDone)
|
if (onDone)
|
||||||
onDone();
|
onDone();
|
||||||
HasExited = true;
|
HasExited = true;
|
||||||
|
@ -14,8 +14,9 @@ public:
|
|||||||
bool HasExited;
|
bool HasExited;
|
||||||
LoginController(std::function<void ()> onDone = nullptr);
|
LoginController(std::function<void ()> onDone = nullptr);
|
||||||
void Login(ByteString username, ByteString password);
|
void Login(ByteString username, ByteString password);
|
||||||
|
void Logout();
|
||||||
|
void Tick();
|
||||||
void Exit();
|
void Exit();
|
||||||
LoginView * GetView() { return loginView; }
|
LoginView * GetView() { return loginView; }
|
||||||
User GetUser();
|
~LoginController();
|
||||||
virtual ~LoginController();
|
|
||||||
};
|
};
|
||||||
|
@ -1,39 +1,32 @@
|
|||||||
#include "LoginModel.h"
|
#include "LoginModel.h"
|
||||||
|
|
||||||
#include "LoginView.h"
|
#include "LoginView.h"
|
||||||
|
|
||||||
#include "client/Client.h"
|
#include "client/Client.h"
|
||||||
|
#include "client/http/LoginRequest.h"
|
||||||
LoginModel::LoginModel():
|
#include "client/http/LogoutRequest.h"
|
||||||
currentUser(0, "")
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
void LoginModel::Login(ByteString username, ByteString password)
|
void LoginModel::Login(ByteString username, ByteString password)
|
||||||
{
|
{
|
||||||
if (username.Contains("@"))
|
if (username.Contains("@"))
|
||||||
{
|
{
|
||||||
statusText = "Use your Powder Toy account to log in, not your email. If you don't have a Powder Toy account, you can create one at https://powdertoy.co.uk/Register.html";
|
statusText = "Use your Powder Toy account to log in, not your email. If you don't have a Powder Toy account, you can create one at https://powdertoy.co.uk/Register.html";
|
||||||
loginStatus = false;
|
loginStatus = loginIdle;
|
||||||
notifyStatusChanged();
|
notifyStatusChanged();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
statusText = "Logging in...";
|
statusText = "Logging in...";
|
||||||
loginStatus = false;
|
loginStatus = loginWorking;
|
||||||
notifyStatusChanged();
|
notifyStatusChanged();
|
||||||
LoginStatus status = Client::Ref().Login(username, password, currentUser);
|
loginRequest = std::make_unique<http::LoginRequest>(username, password);
|
||||||
switch(status)
|
loginRequest->Start();
|
||||||
{
|
}
|
||||||
case LoginOkay:
|
|
||||||
statusText = "Logged in";
|
void LoginModel::Logout()
|
||||||
loginStatus = true;
|
{
|
||||||
break;
|
statusText = "Logging out...";
|
||||||
case LoginError:
|
loginStatus = loginWorking;
|
||||||
statusText = Client::Ref().GetLastError();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
notifyStatusChanged();
|
notifyStatusChanged();
|
||||||
|
logoutRequest = std::make_unique<http::LogoutRequest>();
|
||||||
|
logoutRequest->Start();
|
||||||
}
|
}
|
||||||
|
|
||||||
void LoginModel::AddObserver(LoginView * observer)
|
void LoginModel::AddObserver(LoginView * observer)
|
||||||
@ -46,14 +39,47 @@ String LoginModel::GetStatusText()
|
|||||||
return statusText;
|
return statusText;
|
||||||
}
|
}
|
||||||
|
|
||||||
User LoginModel::GetUser()
|
void LoginModel::Tick()
|
||||||
{
|
{
|
||||||
return currentUser;
|
if (loginRequest && loginRequest->CheckDone())
|
||||||
}
|
{
|
||||||
|
try
|
||||||
bool LoginModel::GetStatus()
|
{
|
||||||
{
|
auto info = loginRequest->Finish();
|
||||||
return loginStatus;
|
auto &client = Client::Ref();
|
||||||
|
client.SetAuthUser(info.user);
|
||||||
|
for (auto &item : info.notifications)
|
||||||
|
{
|
||||||
|
client.AddServerNotification(item);
|
||||||
|
}
|
||||||
|
statusText = "Logged in";
|
||||||
|
loginStatus = loginSucceeded;
|
||||||
|
}
|
||||||
|
catch (const http::RequestError &ex)
|
||||||
|
{
|
||||||
|
statusText = ByteString(ex.what()).FromUtf8();
|
||||||
|
loginStatus = loginIdle;
|
||||||
|
}
|
||||||
|
notifyStatusChanged();
|
||||||
|
loginRequest.reset();
|
||||||
|
}
|
||||||
|
if (logoutRequest && logoutRequest->CheckDone())
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
logoutRequest->Finish();
|
||||||
|
auto &client = Client::Ref();
|
||||||
|
client.SetAuthUser(User(0, ""));
|
||||||
|
statusText = "Logged out";
|
||||||
|
}
|
||||||
|
catch (const http::RequestError &ex)
|
||||||
|
{
|
||||||
|
statusText = ByteString(ex.what()).FromUtf8();
|
||||||
|
}
|
||||||
|
loginStatus = loginIdle;
|
||||||
|
notifyStatusChanged();
|
||||||
|
logoutRequest.reset();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void LoginModel::notifyStatusChanged()
|
void LoginModel::notifyStatusChanged()
|
||||||
@ -64,6 +90,7 @@ void LoginModel::notifyStatusChanged()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
LoginModel::~LoginModel() {
|
LoginModel::~LoginModel()
|
||||||
|
{
|
||||||
|
// Satisfy std::unique_ptr
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,21 +2,41 @@
|
|||||||
#include "common/String.h"
|
#include "common/String.h"
|
||||||
#include "client/User.h"
|
#include "client/User.h"
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
namespace http
|
||||||
|
{
|
||||||
|
class LoginRequest;
|
||||||
|
class LogoutRequest;
|
||||||
|
}
|
||||||
|
|
||||||
|
enum LoginStatus
|
||||||
|
{
|
||||||
|
loginIdle,
|
||||||
|
loginWorking,
|
||||||
|
loginSucceeded,
|
||||||
|
};
|
||||||
|
|
||||||
class LoginView;
|
class LoginView;
|
||||||
class LoginModel
|
class LoginModel
|
||||||
{
|
{
|
||||||
|
std::unique_ptr<http::LoginRequest> loginRequest;
|
||||||
|
std::unique_ptr<http::LogoutRequest> logoutRequest;
|
||||||
std::vector<LoginView*> observers;
|
std::vector<LoginView*> observers;
|
||||||
String statusText;
|
String statusText;
|
||||||
bool loginStatus;
|
LoginStatus loginStatus = loginIdle;
|
||||||
void notifyStatusChanged();
|
void notifyStatusChanged();
|
||||||
User currentUser;
|
|
||||||
public:
|
public:
|
||||||
LoginModel();
|
|
||||||
void Login(ByteString username, ByteString password);
|
void Login(ByteString username, ByteString password);
|
||||||
|
void Logout();
|
||||||
void AddObserver(LoginView * observer);
|
void AddObserver(LoginView * observer);
|
||||||
String GetStatusText();
|
String GetStatusText();
|
||||||
bool GetStatus();
|
LoginStatus GetStatus() const
|
||||||
|
{
|
||||||
|
return loginStatus;
|
||||||
|
}
|
||||||
|
void Tick();
|
||||||
User GetUser();
|
User GetUser();
|
||||||
virtual ~LoginModel();
|
~LoginModel();
|
||||||
};
|
};
|
||||||
|
@ -1,18 +1,13 @@
|
|||||||
#include "LoginView.h"
|
#include "LoginView.h"
|
||||||
|
|
||||||
#include "LoginModel.h"
|
#include "LoginModel.h"
|
||||||
#include "LoginController.h"
|
#include "LoginController.h"
|
||||||
|
|
||||||
#include "graphics/Graphics.h"
|
#include "graphics/Graphics.h"
|
||||||
#include "gui/interface/Button.h"
|
#include "gui/interface/Button.h"
|
||||||
#include "gui/interface/Label.h"
|
#include "gui/interface/Label.h"
|
||||||
#include "gui/interface/Textbox.h"
|
#include "gui/interface/Textbox.h"
|
||||||
#include "gui/Style.h"
|
#include "gui/Style.h"
|
||||||
|
|
||||||
#include "client/Client.h"
|
#include "client/Client.h"
|
||||||
|
|
||||||
#include "Misc.h"
|
#include "Misc.h"
|
||||||
|
|
||||||
#include <SDL.h>
|
#include <SDL.h>
|
||||||
|
|
||||||
LoginView::LoginView():
|
LoginView::LoginView():
|
||||||
@ -39,11 +34,15 @@ LoginView::LoginView():
|
|||||||
loginButton->Appearance.HorizontalAlign = ui::Appearance::AlignRight;
|
loginButton->Appearance.HorizontalAlign = ui::Appearance::AlignRight;
|
||||||
loginButton->Appearance.VerticalAlign = ui::Appearance::AlignMiddle;
|
loginButton->Appearance.VerticalAlign = ui::Appearance::AlignMiddle;
|
||||||
loginButton->Appearance.TextInactive = style::Colour::ConfirmButton;
|
loginButton->Appearance.TextInactive = style::Colour::ConfirmButton;
|
||||||
loginButton->SetActionCallback({ [this] { c->Login(usernameField->GetText().ToUtf8(), passwordField->GetText().ToUtf8()); } });
|
loginButton->SetActionCallback({ [this] {
|
||||||
|
c->Login(usernameField->GetText().ToUtf8(), passwordField->GetText().ToUtf8());
|
||||||
|
} });
|
||||||
AddComponent(cancelButton);
|
AddComponent(cancelButton);
|
||||||
cancelButton->Appearance.HorizontalAlign = ui::Appearance::AlignLeft;
|
cancelButton->Appearance.HorizontalAlign = ui::Appearance::AlignLeft;
|
||||||
cancelButton->Appearance.VerticalAlign = ui::Appearance::AlignMiddle;
|
cancelButton->Appearance.VerticalAlign = ui::Appearance::AlignMiddle;
|
||||||
cancelButton->SetActionCallback({ [this] { c->Exit(); } });
|
cancelButton->SetActionCallback({ [this] {
|
||||||
|
c->Logout();
|
||||||
|
} });
|
||||||
AddComponent(titleLabel);
|
AddComponent(titleLabel);
|
||||||
titleLabel->Appearance.HorizontalAlign = ui::Appearance::AlignLeft;
|
titleLabel->Appearance.HorizontalAlign = ui::Appearance::AlignLeft;
|
||||||
titleLabel->Appearance.VerticalAlign = ui::Appearance::AlignMiddle;
|
titleLabel->Appearance.VerticalAlign = ui::Appearance::AlignMiddle;
|
||||||
@ -85,12 +84,17 @@ void LoginView::NotifyStatusChanged(LoginModel * sender)
|
|||||||
targetSize.Y = 87;
|
targetSize.Y = 87;
|
||||||
infoLabel->SetText(sender->GetStatusText());
|
infoLabel->SetText(sender->GetStatusText());
|
||||||
infoLabel->AutoHeight();
|
infoLabel->AutoHeight();
|
||||||
|
auto notWorking = sender->GetStatus() != loginWorking;
|
||||||
|
loginButton->Enabled = notWorking;
|
||||||
|
cancelButton->Enabled = notWorking && Client::Ref().GetAuthUser().UserID;
|
||||||
|
usernameField->Enabled = notWorking;
|
||||||
|
passwordField->Enabled = notWorking;
|
||||||
if (sender->GetStatusText().length())
|
if (sender->GetStatusText().length())
|
||||||
{
|
{
|
||||||
targetSize.Y += infoLabel->Size.Y+2;
|
targetSize.Y += infoLabel->Size.Y+2;
|
||||||
infoLabel->Visible = true;
|
infoLabel->Visible = true;
|
||||||
}
|
}
|
||||||
if(sender->GetStatus())
|
if (sender->GetStatus() == loginSucceeded)
|
||||||
{
|
{
|
||||||
c->Exit();
|
c->Exit();
|
||||||
}
|
}
|
||||||
@ -98,6 +102,7 @@ void LoginView::NotifyStatusChanged(LoginModel * sender)
|
|||||||
|
|
||||||
void LoginView::OnTick(float dt)
|
void LoginView::OnTick(float dt)
|
||||||
{
|
{
|
||||||
|
c->Tick();
|
||||||
//if(targetSize != Size)
|
//if(targetSize != Size)
|
||||||
{
|
{
|
||||||
ui::Point difference = targetSize-Size;
|
ui::Point difference = targetSize-Size;
|
||||||
|
@ -1,24 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
#include "common/String.h"
|
|
||||||
|
|
||||||
class SaveComment
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
int authorID;
|
|
||||||
ByteString authorName;
|
|
||||||
ByteString authorNameFormatted;
|
|
||||||
String comment;
|
|
||||||
SaveComment(int userID, ByteString username, ByteString usernameFormatted, String commentText):
|
|
||||||
authorID(userID), authorName(username), authorNameFormatted(usernameFormatted), comment(commentText)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
SaveComment(const SaveComment & comment):
|
|
||||||
authorID(comment.authorID), authorName(comment.authorName), authorNameFormatted(comment.authorNameFormatted), comment(comment.comment)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
SaveComment(const SaveComment * comment):
|
|
||||||
authorID(comment->authorID), authorName(comment->authorName), authorNameFormatted(comment->authorNameFormatted), comment(comment->comment)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
@ -6,6 +6,10 @@
|
|||||||
#include "client/Client.h"
|
#include "client/Client.h"
|
||||||
#include "client/SaveInfo.h"
|
#include "client/SaveInfo.h"
|
||||||
#include "client/GameSave.h"
|
#include "client/GameSave.h"
|
||||||
|
#include "client/http/GetSaveRequest.h"
|
||||||
|
#include "client/http/GetSaveDataRequest.h"
|
||||||
|
#include "client/http/GetCommentsRequest.h"
|
||||||
|
#include "client/http/FavouriteSaveRequest.h"
|
||||||
#include "common/platform/Platform.h"
|
#include "common/platform/Platform.h"
|
||||||
#include "graphics/Graphics.h"
|
#include "graphics/Graphics.h"
|
||||||
#include "gui/dialogues/ErrorMessage.h"
|
#include "gui/dialogues/ErrorMessage.h"
|
||||||
@ -51,30 +55,6 @@ void PreviewController::Update()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool PreviewController::SubmitComment(String comment)
|
|
||||||
{
|
|
||||||
if(comment.length() < 4)
|
|
||||||
{
|
|
||||||
new ErrorMessage("Error", "Comment is too short");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
RequestStatus status = Client::Ref().AddComment(saveId, comment);
|
|
||||||
if(status != RequestOkay)
|
|
||||||
{
|
|
||||||
new ErrorMessage("Error submitting comment", Client::Ref().GetLastError());
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
previewModel->CommentAdded();
|
|
||||||
previewModel->UpdateComments(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void PreviewController::ShowLogin()
|
void PreviewController::ShowLogin()
|
||||||
{
|
{
|
||||||
loginWindow = new LoginController();
|
loginWindow = new LoginController();
|
||||||
@ -106,32 +86,11 @@ void PreviewController::DoOpen()
|
|||||||
previewModel->SetDoOpen(true);
|
previewModel->SetDoOpen(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
void PreviewController::Report(String message)
|
|
||||||
{
|
|
||||||
if(Client::Ref().ReportSave(saveId, message) == RequestOkay)
|
|
||||||
{
|
|
||||||
Exit();
|
|
||||||
new InformationMessage("Information", "Report submitted", false);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
new ErrorMessage("Error", "Unable to file report: " + Client::Ref().GetLastError());
|
|
||||||
}
|
|
||||||
|
|
||||||
void PreviewController::FavouriteSave()
|
void PreviewController::FavouriteSave()
|
||||||
{
|
{
|
||||||
if(previewModel->GetSaveInfo() && Client::Ref().GetAuthUser().UserID)
|
if (previewModel->GetSaveInfo() && Client::Ref().GetAuthUser().UserID)
|
||||||
{
|
{
|
||||||
try
|
previewModel->SetFavourite(!previewModel->GetSaveInfo()->Favourite);
|
||||||
{
|
|
||||||
if(previewModel->GetSaveInfo()->Favourite)
|
|
||||||
previewModel->SetFavourite(false);
|
|
||||||
else
|
|
||||||
previewModel->SetFavourite(true);
|
|
||||||
}
|
|
||||||
catch (PreviewModelException & e)
|
|
||||||
{
|
|
||||||
new ErrorMessage("Error", ByteString(e.what()).FromUtf8());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -161,6 +120,12 @@ bool PreviewController::PrevCommentPage()
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void PreviewController::CommentAdded()
|
||||||
|
{
|
||||||
|
previewModel->CommentAdded();
|
||||||
|
previewModel->UpdateComments(1);
|
||||||
|
}
|
||||||
|
|
||||||
void PreviewController::Exit()
|
void PreviewController::Exit()
|
||||||
{
|
{
|
||||||
previewView->CloseActiveWindow();
|
previewView->CloseActiveWindow();
|
||||||
|
@ -23,7 +23,6 @@ public:
|
|||||||
void Exit();
|
void Exit();
|
||||||
void DoOpen();
|
void DoOpen();
|
||||||
void OpenInBrowser();
|
void OpenInBrowser();
|
||||||
void Report(String message);
|
|
||||||
void ShowLogin();
|
void ShowLogin();
|
||||||
bool GetDoOpen();
|
bool GetDoOpen();
|
||||||
const SaveInfo *GetSaveInfo() const;
|
const SaveInfo *GetSaveInfo() const;
|
||||||
@ -31,10 +30,10 @@ public:
|
|||||||
PreviewView * GetView() { return previewView; }
|
PreviewView * GetView() { return previewView; }
|
||||||
void Update();
|
void Update();
|
||||||
void FavouriteSave();
|
void FavouriteSave();
|
||||||
bool SubmitComment(String comment);
|
|
||||||
|
|
||||||
bool NextCommentPage();
|
bool NextCommentPage();
|
||||||
bool PrevCommentPage();
|
bool PrevCommentPage();
|
||||||
|
void CommentAdded();
|
||||||
|
|
||||||
virtual ~PreviewController();
|
virtual ~PreviewController();
|
||||||
};
|
};
|
||||||
|
@ -1,46 +1,27 @@
|
|||||||
#include "PreviewModel.h"
|
#include "PreviewModel.h"
|
||||||
#include "client/http/Request.h"
|
#include "client/http/GetSaveDataRequest.h"
|
||||||
|
#include "client/http/GetSaveRequest.h"
|
||||||
|
#include "client/http/GetCommentsRequest.h"
|
||||||
|
#include "client/http/FavouriteSaveRequest.h"
|
||||||
#include "Format.h"
|
#include "Format.h"
|
||||||
|
#include "Misc.h"
|
||||||
#include "client/Client.h"
|
#include "client/Client.h"
|
||||||
#include "client/GameSave.h"
|
#include "client/GameSave.h"
|
||||||
#include "client/SaveInfo.h"
|
#include "client/SaveInfo.h"
|
||||||
#include "gui/dialogues/ErrorMessage.h"
|
#include "gui/dialogues/ErrorMessage.h"
|
||||||
#include "gui/preview/Comment.h"
|
|
||||||
#include "PreviewModelException.h"
|
#include "PreviewModelException.h"
|
||||||
#include "PreviewView.h"
|
#include "PreviewView.h"
|
||||||
#include "Config.h"
|
#include "Config.h"
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
PreviewModel::PreviewModel():
|
constexpr auto commentsPerPage = 20;
|
||||||
doOpen(false),
|
|
||||||
canOpen(true),
|
|
||||||
saveData(NULL),
|
|
||||||
saveComments(NULL),
|
|
||||||
commentBoxEnabled(false),
|
|
||||||
commentsLoaded(false),
|
|
||||||
commentsTotal(0),
|
|
||||||
commentsPageNumber(1)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
PreviewModel::~PreviewModel()
|
|
||||||
{
|
|
||||||
delete saveData;
|
|
||||||
ClearComments();
|
|
||||||
}
|
|
||||||
|
|
||||||
void PreviewModel::SetFavourite(bool favourite)
|
void PreviewModel::SetFavourite(bool favourite)
|
||||||
{
|
{
|
||||||
if (saveInfo)
|
if (saveInfo)
|
||||||
{
|
{
|
||||||
if (Client::Ref().FavouriteSave(saveInfo->id, favourite) == RequestOkay)
|
queuedFavourite = favourite;
|
||||||
saveInfo->Favourite = favourite;
|
|
||||||
else if (favourite)
|
|
||||||
throw PreviewModelException("Error, could not fav. the save: " + Client::Ref().GetLastError());
|
|
||||||
else
|
|
||||||
throw PreviewModelException("Error, could not unfav. the save: " + Client::Ref().GetLastError());
|
|
||||||
notifySaveChanged();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -64,37 +45,22 @@ void PreviewModel::UpdateSave(int saveID, int saveDate)
|
|||||||
this->saveDate = saveDate;
|
this->saveDate = saveDate;
|
||||||
|
|
||||||
saveInfo.reset();
|
saveInfo.reset();
|
||||||
if (saveData)
|
saveData.reset();
|
||||||
{
|
saveComments.reset();
|
||||||
delete saveData;
|
|
||||||
saveData = NULL;
|
|
||||||
}
|
|
||||||
ClearComments();
|
|
||||||
notifySaveChanged();
|
notifySaveChanged();
|
||||||
notifySaveCommentsChanged();
|
notifySaveCommentsChanged();
|
||||||
|
|
||||||
ByteString url;
|
saveDataDownload = std::make_unique<http::GetSaveDataRequest>(saveID, saveDate);
|
||||||
if (saveDate)
|
|
||||||
url = ByteString::Build(STATICSCHEME, STATICSERVER, "/", saveID, "_", saveDate, ".cps");
|
|
||||||
else
|
|
||||||
url = ByteString::Build(STATICSCHEME, STATICSERVER, "/", saveID, ".cps");
|
|
||||||
saveDataDownload = std::make_unique<http::Request>(url);
|
|
||||||
saveDataDownload->Start();
|
saveDataDownload->Start();
|
||||||
|
|
||||||
url = ByteString::Build(SCHEME, SERVER , "/Browse/View.json?ID=", saveID);
|
saveInfoDownload = std::make_unique<http::GetSaveRequest>(saveID, saveDate);
|
||||||
if (saveDate)
|
|
||||||
url += ByteString::Build("&Date=", saveDate);
|
|
||||||
saveInfoDownload = std::make_unique<http::Request>(url);
|
|
||||||
saveInfoDownload->AuthHeaders(ByteString::Build(Client::Ref().GetAuthUser().UserID), Client::Ref().GetAuthUser().SessionID);
|
|
||||||
saveInfoDownload->Start();
|
saveInfoDownload->Start();
|
||||||
|
|
||||||
if (!GetDoOpen())
|
if (!GetDoOpen())
|
||||||
{
|
{
|
||||||
commentsLoaded = false;
|
commentsLoaded = false;
|
||||||
|
|
||||||
url = ByteString::Build(SCHEME, SERVER, "/Browse/Comments.json?ID=", saveID, "&Start=", (commentsPageNumber-1)*20, "&Count=20");
|
commentsDownload = std::make_unique<http::GetCommentsRequest>(saveID, (commentsPageNumber - 1) * commentsPerPage, commentsPerPage);
|
||||||
commentsDownload = std::make_unique<http::Request>(url);
|
|
||||||
commentsDownload->AuthHeaders(ByteString::Build(Client::Ref().GetAuthUser().UserID), Client::Ref().GetAuthUser().SessionID);
|
|
||||||
commentsDownload->Start();
|
commentsDownload->Start();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -131,7 +97,7 @@ int PreviewModel::GetCommentsPageNum()
|
|||||||
|
|
||||||
int PreviewModel::GetCommentsPageCount()
|
int PreviewModel::GetCommentsPageCount()
|
||||||
{
|
{
|
||||||
return std::max(1, (int)(ceil(commentsTotal/20.0f)));
|
return std::max(1, ceilDiv(commentsTotal, commentsPerPage).first);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool PreviewModel::GetCommentsLoaded()
|
bool PreviewModel::GetCommentsLoaded()
|
||||||
@ -144,14 +110,12 @@ void PreviewModel::UpdateComments(int pageNumber)
|
|||||||
if (commentsLoaded)
|
if (commentsLoaded)
|
||||||
{
|
{
|
||||||
commentsLoaded = false;
|
commentsLoaded = false;
|
||||||
ClearComments();
|
saveComments.reset();
|
||||||
|
|
||||||
commentsPageNumber = pageNumber;
|
commentsPageNumber = pageNumber;
|
||||||
if (!GetDoOpen())
|
if (!GetDoOpen())
|
||||||
{
|
{
|
||||||
ByteString url = ByteString::Build(SCHEME, SERVER, "/Browse/Comments.json?ID=", saveID, "&Start=", (commentsPageNumber-1)*20, "&Count=20");
|
commentsDownload = std::make_unique<http::GetCommentsRequest>(saveID, (commentsPageNumber - 1) * commentsPerPage, commentsPerPage);
|
||||||
commentsDownload = std::make_unique<http::Request>(url);
|
|
||||||
commentsDownload->AuthHeaders(ByteString::Build(Client::Ref().GetAuthUser().UserID), Client::Ref().GetAuthUser().SessionID);
|
|
||||||
commentsDownload->Start();
|
commentsDownload->Start();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -189,175 +153,105 @@ void PreviewModel::OnSaveReady()
|
|||||||
notifySaveCommentsChanged();
|
notifySaveCommentsChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
void PreviewModel::ClearComments()
|
|
||||||
{
|
|
||||||
if (saveComments)
|
|
||||||
{
|
|
||||||
for (size_t i = 0; i < saveComments->size(); i++)
|
|
||||||
delete saveComments->at(i);
|
|
||||||
saveComments->clear();
|
|
||||||
delete saveComments;
|
|
||||||
saveComments = NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool PreviewModel::ParseSaveInfo(ByteString &saveInfoResponse)
|
|
||||||
{
|
|
||||||
saveInfo.reset();
|
|
||||||
|
|
||||||
try // how does this differ from Client::GetSave?
|
|
||||||
{
|
|
||||||
std::istringstream dataStream(saveInfoResponse);
|
|
||||||
Json::Value objDocument;
|
|
||||||
dataStream >> objDocument;
|
|
||||||
|
|
||||||
int tempID = objDocument["ID"].asInt();
|
|
||||||
int tempScoreUp = objDocument["ScoreUp"].asInt();
|
|
||||||
int tempScoreDown = objDocument["ScoreDown"].asInt();
|
|
||||||
int tempMyScore = objDocument["ScoreMine"].asInt();
|
|
||||||
ByteString tempUsername = objDocument["Username"].asString();
|
|
||||||
String tempName = ByteString(objDocument["Name"].asString()).FromUtf8();
|
|
||||||
String tempDescription = ByteString(objDocument["Description"].asString()).FromUtf8();
|
|
||||||
int tempCreatedDate = objDocument["DateCreated"].asInt();
|
|
||||||
int tempUpdatedDate = objDocument["Date"].asInt();
|
|
||||||
bool tempPublished = objDocument["Published"].asBool();
|
|
||||||
bool tempFavourite = objDocument["Favourite"].asBool();
|
|
||||||
int tempComments = objDocument["Comments"].asInt();
|
|
||||||
int tempViews = objDocument["Views"].asInt();
|
|
||||||
int tempVersion = objDocument["Version"].asInt();
|
|
||||||
|
|
||||||
Json::Value tagsArray = objDocument["Tags"];
|
|
||||||
std::list<ByteString> tempTags;
|
|
||||||
for (Json::UInt j = 0; j < tagsArray.size(); j++)
|
|
||||||
tempTags.push_back(tagsArray[j].asString());
|
|
||||||
|
|
||||||
auto newSaveInfo = std::make_unique<SaveInfo>(tempID, tempCreatedDate, tempUpdatedDate, tempScoreUp,
|
|
||||||
tempScoreDown, tempMyScore, tempUsername, tempName,
|
|
||||||
tempDescription, tempPublished, tempTags);
|
|
||||||
newSaveInfo->Comments = tempComments;
|
|
||||||
newSaveInfo->Favourite = tempFavourite;
|
|
||||||
newSaveInfo->Views = tempViews;
|
|
||||||
newSaveInfo->Version = tempVersion;
|
|
||||||
|
|
||||||
// This is a workaround for a bug on the TPT server where the wrong 404 save is returned
|
|
||||||
// Redownload the .cps file for a fixed version of the 404 save
|
|
||||||
if (tempID == 404 && this->saveID != 404)
|
|
||||||
{
|
|
||||||
delete saveData;
|
|
||||||
saveData = NULL;
|
|
||||||
saveDataDownload = std::make_unique<http::Request>(ByteString::Build(STATICSCHEME, STATICSERVER, "/2157797.cps"));
|
|
||||||
saveDataDownload->Start();
|
|
||||||
}
|
|
||||||
saveInfo = std::move(newSaveInfo);
|
|
||||||
}
|
|
||||||
catch (std::exception &e)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool PreviewModel::ParseComments(ByteString &commentsResponse)
|
|
||||||
{
|
|
||||||
ClearComments();
|
|
||||||
saveComments = new std::vector<SaveComment*>();
|
|
||||||
try
|
|
||||||
{
|
|
||||||
std::istringstream dataStream(commentsResponse);
|
|
||||||
Json::Value commentsArray;
|
|
||||||
dataStream >> commentsArray;
|
|
||||||
|
|
||||||
for (Json::UInt j = 0; j < commentsArray.size(); j++)
|
|
||||||
{
|
|
||||||
int userID = ByteString(commentsArray[j]["UserID"].asString()).ToNumber<int>();
|
|
||||||
ByteString username = commentsArray[j]["Username"].asString();
|
|
||||||
ByteString formattedUsername = commentsArray[j]["FormattedUsername"].asString();
|
|
||||||
if (formattedUsername == "jacobot")
|
|
||||||
formattedUsername = "\bt" + formattedUsername;
|
|
||||||
String comment = ByteString(commentsArray[j]["Text"].asString()).FromUtf8();
|
|
||||||
saveComments->push_back(new SaveComment(userID, username, formattedUsername, comment));
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
catch (std::exception &e)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void PreviewModel::Update()
|
void PreviewModel::Update()
|
||||||
{
|
{
|
||||||
|
auto triggerOnSaveReady = false;
|
||||||
if (saveDataDownload && saveDataDownload->CheckDone())
|
if (saveDataDownload && saveDataDownload->CheckDone())
|
||||||
{
|
{
|
||||||
auto [ status, ret ] = saveDataDownload->Finish();
|
try
|
||||||
|
|
||||||
ByteString nothing;
|
|
||||||
Client::Ref().ParseServerReturn(nothing, status, true);
|
|
||||||
if (status == 200 && ret.size())
|
|
||||||
{
|
{
|
||||||
delete saveData;
|
saveData = saveDataDownload->Finish();
|
||||||
saveData = new std::vector<char>(ret.begin(), ret.end());
|
triggerOnSaveReady = true;
|
||||||
if (saveInfo && saveData)
|
|
||||||
OnSaveReady();
|
|
||||||
}
|
}
|
||||||
else
|
catch (const http::RequestError &ex)
|
||||||
{
|
{
|
||||||
|
auto why = ByteString(ex.what()).FromUtf8();
|
||||||
for (size_t i = 0; i < observers.size(); i++)
|
for (size_t i = 0; i < observers.size(); i++)
|
||||||
{
|
{
|
||||||
observers[i]->SaveLoadingError(Client::Ref().GetLastError());
|
observers[i]->SaveLoadingError(why);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
saveDataDownload.reset();
|
saveDataDownload.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (saveInfoDownload && saveInfoDownload->CheckDone())
|
if (saveInfoDownload && saveInfoDownload->CheckDone())
|
||||||
{
|
{
|
||||||
auto [ status, ret ] = saveInfoDownload->Finish();
|
try
|
||||||
|
|
||||||
ByteString nothing;
|
|
||||||
Client::Ref().ParseServerReturn(nothing, status, true);
|
|
||||||
if (status == 200 && ret.size())
|
|
||||||
{
|
{
|
||||||
if (ParseSaveInfo(ret))
|
saveInfo = saveInfoDownload->Finish();
|
||||||
|
triggerOnSaveReady = true;
|
||||||
|
// This is a workaround for a bug on the TPT server where the wrong 404 save is returned
|
||||||
|
// Redownload the .cps file for a fixed version of the 404 save
|
||||||
|
if (saveInfo->GetID() == 404 && saveID != 404)
|
||||||
{
|
{
|
||||||
if (saveInfo && saveData)
|
saveData.reset();
|
||||||
OnSaveReady();
|
saveDataDownload = std::make_unique<http::GetSaveDataRequest>(2157797, 0);
|
||||||
}
|
saveDataDownload->Start();
|
||||||
else
|
|
||||||
{
|
|
||||||
for (size_t i = 0; i < observers.size(); i++)
|
|
||||||
observers[i]->SaveLoadingError("Could not parse save info");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
catch (const http::RequestError &ex)
|
||||||
{
|
{
|
||||||
|
auto why = ByteString(ex.what()).FromUtf8();
|
||||||
for (size_t i = 0; i < observers.size(); i++)
|
for (size_t i = 0; i < observers.size(); i++)
|
||||||
observers[i]->SaveLoadingError(Client::Ref().GetLastError());
|
{
|
||||||
|
observers[i]->SaveLoadingError(why);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
saveInfoDownload.reset();
|
saveInfoDownload.reset();
|
||||||
}
|
}
|
||||||
|
if (triggerOnSaveReady && saveInfo && saveData)
|
||||||
|
{
|
||||||
|
OnSaveReady();
|
||||||
|
}
|
||||||
|
|
||||||
if (commentsDownload && commentsDownload->CheckDone())
|
if (commentsDownload && commentsDownload->CheckDone())
|
||||||
{
|
{
|
||||||
auto [ status, ret ] = commentsDownload->Finish();
|
try
|
||||||
ClearComments();
|
{
|
||||||
|
saveComments = commentsDownload->Finish();
|
||||||
ByteString nothing;
|
}
|
||||||
Client::Ref().ParseServerReturn(nothing, status, true);
|
catch (const http::RequestError &ex)
|
||||||
if (status == 200 && ret.size())
|
{
|
||||||
ParseComments(ret);
|
// TODO: handle
|
||||||
|
}
|
||||||
commentsLoaded = true;
|
commentsLoaded = true;
|
||||||
notifySaveCommentsChanged();
|
notifySaveCommentsChanged();
|
||||||
notifyCommentsPageChanged();
|
notifyCommentsPageChanged();
|
||||||
|
|
||||||
commentsDownload.reset();
|
commentsDownload.reset();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<SaveComment*> * PreviewModel::GetComments()
|
if (favouriteSaveRequest && favouriteSaveRequest->CheckDone())
|
||||||
{
|
{
|
||||||
return saveComments;
|
try
|
||||||
|
{
|
||||||
|
favouriteSaveRequest->Finish();
|
||||||
|
if (saveInfo)
|
||||||
|
{
|
||||||
|
saveInfo->Favourite = favouriteSaveRequest->Favourite();
|
||||||
|
notifySaveChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (const http::RequestError &ex)
|
||||||
|
{
|
||||||
|
if (favouriteSaveRequest->Favourite())
|
||||||
|
{
|
||||||
|
throw PreviewModelException("Error, could not fav. the save: " + ByteString(ex.what()).FromUtf8());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw PreviewModelException("Error, could not unfav. the save: " + ByteString(ex.what()).FromUtf8());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
favouriteSaveRequest.reset();
|
||||||
|
}
|
||||||
|
if (!favouriteSaveRequest && queuedFavourite)
|
||||||
|
{
|
||||||
|
if (saveInfo)
|
||||||
|
{
|
||||||
|
favouriteSaveRequest = std::make_unique<http::FavouriteSaveRequest>(saveInfo->id, *queuedFavourite);
|
||||||
|
favouriteSaveRequest->Start();
|
||||||
|
}
|
||||||
|
queuedFavourite.reset();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void PreviewModel::notifySaveChanged()
|
void PreviewModel::notifySaveChanged()
|
||||||
|
@ -1,47 +1,54 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include "common/String.h"
|
#include "common/String.h"
|
||||||
|
#include "client/Comment.h"
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
#include <optional>
|
||||||
|
|
||||||
namespace http
|
namespace http
|
||||||
{
|
{
|
||||||
class Request;
|
class GetSaveDataRequest;
|
||||||
|
class GetSaveRequest;
|
||||||
|
class GetCommentsRequest;
|
||||||
|
class FavouriteSaveRequest;
|
||||||
}
|
}
|
||||||
|
|
||||||
class PreviewView;
|
class PreviewView;
|
||||||
class SaveInfo;
|
class SaveInfo;
|
||||||
class SaveComment;
|
|
||||||
class PreviewModel
|
class PreviewModel
|
||||||
{
|
{
|
||||||
bool doOpen;
|
bool doOpen = false;
|
||||||
bool canOpen;
|
bool canOpen = true;
|
||||||
std::vector<PreviewView*> observers;
|
std::vector<PreviewView*> observers;
|
||||||
std::unique_ptr<SaveInfo> saveInfo;
|
std::unique_ptr<SaveInfo> saveInfo;
|
||||||
std::vector<char> * saveData;
|
std::optional<std::vector<char>> saveData;
|
||||||
std::vector<SaveComment*> * saveComments;
|
std::optional<std::vector<Comment>> saveComments;
|
||||||
void notifySaveChanged();
|
void notifySaveChanged();
|
||||||
void notifySaveCommentsChanged();
|
void notifySaveCommentsChanged();
|
||||||
void notifyCommentsPageChanged();
|
void notifyCommentsPageChanged();
|
||||||
void notifyCommentBoxEnabledChanged();
|
void notifyCommentBoxEnabledChanged();
|
||||||
|
|
||||||
std::unique_ptr<http::Request> saveDataDownload;
|
std::unique_ptr<http::GetSaveDataRequest> saveDataDownload;
|
||||||
std::unique_ptr<http::Request> saveInfoDownload;
|
std::unique_ptr<http::GetSaveRequest> saveInfoDownload;
|
||||||
std::unique_ptr<http::Request> commentsDownload;
|
std::unique_ptr<http::GetCommentsRequest> commentsDownload;
|
||||||
|
std::unique_ptr<http::FavouriteSaveRequest> favouriteSaveRequest;
|
||||||
int saveID;
|
int saveID;
|
||||||
int saveDate;
|
int saveDate;
|
||||||
|
|
||||||
bool commentBoxEnabled;
|
bool commentBoxEnabled = false;
|
||||||
bool commentsLoaded;
|
bool commentsLoaded = false;
|
||||||
int commentsTotal;
|
int commentsTotal = 0;
|
||||||
int commentsPageNumber;
|
int commentsPageNumber = 1;
|
||||||
|
|
||||||
|
std::optional<bool> queuedFavourite;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
PreviewModel();
|
|
||||||
~PreviewModel();
|
|
||||||
|
|
||||||
const SaveInfo *GetSaveInfo() const;
|
const SaveInfo *GetSaveInfo() const;
|
||||||
std::unique_ptr<SaveInfo> TakeSaveInfo();
|
std::unique_ptr<SaveInfo> TakeSaveInfo();
|
||||||
std::vector<SaveComment*> * GetComments();
|
const std::vector<Comment> *GetComments() const
|
||||||
|
{
|
||||||
|
return saveComments ? &*saveComments : nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
bool GetCommentBoxEnabled();
|
bool GetCommentBoxEnabled();
|
||||||
void SetCommentBoxEnabled(bool enabledState);
|
void SetCommentBoxEnabled(bool enabledState);
|
||||||
@ -59,7 +66,6 @@ public:
|
|||||||
bool GetCanOpen();
|
bool GetCanOpen();
|
||||||
void SetDoOpen(bool doOpen);
|
void SetDoOpen(bool doOpen);
|
||||||
void Update();
|
void Update();
|
||||||
void ClearComments();
|
|
||||||
void OnSaveReady();
|
void OnSaveReady();
|
||||||
bool ParseSaveInfo(ByteString &saveInfoResponse);
|
bool ParseSaveInfo(ByteString &saveInfoResponse);
|
||||||
bool ParseComments(ByteString &commentsResponse);
|
bool ParseComments(ByteString &commentsResponse);
|
||||||
|
@ -4,6 +4,8 @@
|
|||||||
|
|
||||||
#include "client/Client.h"
|
#include "client/Client.h"
|
||||||
#include "client/SaveInfo.h"
|
#include "client/SaveInfo.h"
|
||||||
|
#include "client/http/AddCommentRequest.h"
|
||||||
|
#include "client/http/ReportSaveRequest.h"
|
||||||
|
|
||||||
#include "gui/dialogues/TextPrompt.h"
|
#include "gui/dialogues/TextPrompt.h"
|
||||||
#include "gui/profile/ProfileActivity.h"
|
#include "gui/profile/ProfileActivity.h"
|
||||||
@ -17,12 +19,12 @@
|
|||||||
#include "gui/interface/Textbox.h"
|
#include "gui/interface/Textbox.h"
|
||||||
#include "gui/interface/Engine.h"
|
#include "gui/interface/Engine.h"
|
||||||
#include "gui/dialogues/ErrorMessage.h"
|
#include "gui/dialogues/ErrorMessage.h"
|
||||||
|
#include "gui/dialogues/InformationMessage.h"
|
||||||
#include "gui/interface/Point.h"
|
#include "gui/interface/Point.h"
|
||||||
#include "gui/interface/Window.h"
|
#include "gui/interface/Window.h"
|
||||||
#include "gui/Style.h"
|
#include "gui/Style.h"
|
||||||
|
|
||||||
#include "common/tpt-rand.h"
|
#include "common/tpt-rand.h"
|
||||||
#include "Comment.h"
|
|
||||||
#include "Format.h"
|
#include "Format.h"
|
||||||
#include "Misc.h"
|
#include "Misc.h"
|
||||||
|
|
||||||
@ -57,6 +59,7 @@ PreviewView::PreviewView(std::unique_ptr<VideoBuffer> newSavePreview):
|
|||||||
favButton = new ui::Button(ui::Point(50, Size.Y-19), ui::Point(51, 19), "Fav");
|
favButton = new ui::Button(ui::Point(50, Size.Y-19), ui::Point(51, 19), "Fav");
|
||||||
favButton->Appearance.HorizontalAlign = ui::Appearance::AlignLeft;
|
favButton->Appearance.HorizontalAlign = ui::Appearance::AlignLeft;
|
||||||
favButton->Appearance.VerticalAlign = ui::Appearance::AlignMiddle;
|
favButton->Appearance.VerticalAlign = ui::Appearance::AlignMiddle;
|
||||||
|
favButton->SetTogglable(true);
|
||||||
favButton->SetIcon(IconFavourite);
|
favButton->SetIcon(IconFavourite);
|
||||||
favButton->SetActionCallback({ [this] { c->FavouriteSave(); } });
|
favButton->SetActionCallback({ [this] { c->FavouriteSave(); } });
|
||||||
favButton->Enabled = Client::Ref().GetAuthUser().UserID?true:false;
|
favButton->Enabled = Client::Ref().GetAuthUser().UserID?true:false;
|
||||||
@ -68,7 +71,12 @@ PreviewView::PreviewView(std::unique_ptr<VideoBuffer> newSavePreview):
|
|||||||
reportButton->SetIcon(IconReport);
|
reportButton->SetIcon(IconReport);
|
||||||
reportButton->SetActionCallback({ [this] {
|
reportButton->SetActionCallback({ [this] {
|
||||||
new TextPrompt("Report Save", "Things to consider when reporting:\n\bw1)\bg When reporting stolen saves, please include the ID of the original save.\n\bw2)\bg Do not ask for saves to be removed from front page unless they break the rules.\n\bw3)\bg You may report saves for comments or tags too (including your own saves)", "", "[reason]", true, { [this](String const &resultText) {
|
new TextPrompt("Report Save", "Things to consider when reporting:\n\bw1)\bg When reporting stolen saves, please include the ID of the original save.\n\bw2)\bg Do not ask for saves to be removed from front page unless they break the rules.\n\bw3)\bg You may report saves for comments or tags too (including your own saves)", "", "[reason]", true, { [this](String const &resultText) {
|
||||||
c->Report(resultText);
|
if (reportSaveRequest)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
reportSaveRequest = std::make_unique<http::ReportSaveRequest>(c->SaveID(), resultText);
|
||||||
|
reportSaveRequest->Start();
|
||||||
} });
|
} });
|
||||||
} });
|
} });
|
||||||
reportButton->Enabled = Client::Ref().GetAuthUser().UserID?true:false;
|
reportButton->Enabled = Client::Ref().GetAuthUser().UserID?true:false;
|
||||||
@ -222,7 +230,12 @@ void PreviewView::CheckComment()
|
|||||||
if (!commentWarningLabel)
|
if (!commentWarningLabel)
|
||||||
return;
|
return;
|
||||||
String text = addCommentBox->GetText().ToLower();
|
String text = addCommentBox->GetText().ToLower();
|
||||||
if (!userIsAuthor && (text.Contains("stolen") || text.Contains("copied")))
|
if (addCommentRequest)
|
||||||
|
{
|
||||||
|
commentWarningLabel->SetText("Submitting comment...");
|
||||||
|
commentHelpText = true;
|
||||||
|
}
|
||||||
|
else if (!userIsAuthor && (text.Contains("stolen") || text.Contains("copied")))
|
||||||
{
|
{
|
||||||
if (!commentHelpText)
|
if (!commentHelpText)
|
||||||
{
|
{
|
||||||
@ -375,6 +388,37 @@ void PreviewView::OnTick(float dt)
|
|||||||
ErrorMessage::Blocking("Error loading save", doErrorMessage);
|
ErrorMessage::Blocking("Error loading save", doErrorMessage);
|
||||||
c->Exit();
|
c->Exit();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (reportSaveRequest && reportSaveRequest->CheckDone())
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
reportSaveRequest->Finish();
|
||||||
|
c->Exit();
|
||||||
|
new InformationMessage("Information", "Report submitted", false);
|
||||||
|
}
|
||||||
|
catch (const http::RequestError &ex)
|
||||||
|
{
|
||||||
|
new ErrorMessage("Error", "Unable to file report: " + ByteString(ex.what()).FromUtf8());
|
||||||
|
}
|
||||||
|
reportSaveRequest.reset();
|
||||||
|
}
|
||||||
|
if (addCommentRequest && addCommentRequest->CheckDone())
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
addCommentBox->SetText("");
|
||||||
|
c->CommentAdded();
|
||||||
|
}
|
||||||
|
catch (const http::RequestError &ex)
|
||||||
|
{
|
||||||
|
new ErrorMessage("Error submitting comment", ByteString(ex.what()).FromUtf8());
|
||||||
|
}
|
||||||
|
submitCommentButton->Enabled = true;
|
||||||
|
commentBoxAutoHeight();
|
||||||
|
addCommentRequest.reset();
|
||||||
|
CheckComment();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void PreviewView::OnTryExit(ExitMethod method)
|
void PreviewView::OnTryExit(ExitMethod method)
|
||||||
@ -448,16 +492,16 @@ void PreviewView::NotifySaveChanged(PreviewModel * sender)
|
|||||||
if(save->Favourite)
|
if(save->Favourite)
|
||||||
{
|
{
|
||||||
favButton->Enabled = true;
|
favButton->Enabled = true;
|
||||||
favButton->SetText("Unfav");
|
favButton->SetToggleState(true);
|
||||||
}
|
}
|
||||||
else if(Client::Ref().GetAuthUser().UserID)
|
else if(Client::Ref().GetAuthUser().UserID)
|
||||||
{
|
{
|
||||||
favButton->Enabled = true;
|
favButton->Enabled = true;
|
||||||
favButton->SetText("Fav");
|
favButton->SetToggleState(false);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
favButton->SetText("Fav");
|
favButton->SetToggleState(false);
|
||||||
favButton->Enabled = false;
|
favButton->Enabled = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -477,6 +521,7 @@ void PreviewView::NotifySaveChanged(PreviewModel * sender)
|
|||||||
saveNameLabel->SetText("");
|
saveNameLabel->SetText("");
|
||||||
authorDateLabel->SetText("");
|
authorDateLabel->SetText("");
|
||||||
saveDescriptionLabel->SetText("");
|
saveDescriptionLabel->SetText("");
|
||||||
|
favButton->SetToggleState(false);
|
||||||
favButton->Enabled = false;
|
favButton->Enabled = false;
|
||||||
if (!sender->GetCanOpen())
|
if (!sender->GetCanOpen())
|
||||||
openButton->Enabled = false;
|
openButton->Enabled = false;
|
||||||
@ -485,21 +530,22 @@ void PreviewView::NotifySaveChanged(PreviewModel * sender)
|
|||||||
|
|
||||||
void PreviewView::submitComment()
|
void PreviewView::submitComment()
|
||||||
{
|
{
|
||||||
if(addCommentBox)
|
if (addCommentBox)
|
||||||
{
|
{
|
||||||
String comment = addCommentBox->GetText();
|
String comment = addCommentBox->GetText();
|
||||||
|
if (comment.length() < 4)
|
||||||
|
{
|
||||||
|
new ErrorMessage("Error", "Comment is too short");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
submitCommentButton->Enabled = false;
|
submitCommentButton->Enabled = false;
|
||||||
addCommentBox->SetText("");
|
|
||||||
addCommentBox->SetPlaceholder("Submitting comment"); //This doesn't appear to ever show since no separate thread is created
|
|
||||||
FocusComponent(NULL);
|
FocusComponent(NULL);
|
||||||
|
|
||||||
if (!c->SubmitComment(comment))
|
addCommentRequest = std::make_unique<http::AddCommentRequest>(c->SaveID(), comment);
|
||||||
addCommentBox->SetText(comment);
|
addCommentRequest->Start();
|
||||||
|
|
||||||
addCommentBox->SetPlaceholder("Add comment");
|
CheckComment();
|
||||||
submitCommentButton->Enabled = true;
|
|
||||||
|
|
||||||
commentBoxAutoHeight();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -564,7 +610,7 @@ void PreviewView::NotifyCommentsPageChanged(PreviewModel * sender)
|
|||||||
|
|
||||||
void PreviewView::NotifyCommentsChanged(PreviewModel * sender)
|
void PreviewView::NotifyCommentsChanged(PreviewModel * sender)
|
||||||
{
|
{
|
||||||
std::vector<SaveComment*> * comments = sender->GetComments();
|
auto commentsPtr = sender->GetComments();
|
||||||
|
|
||||||
for (size_t i = 0; i < commentComponents.size(); i++)
|
for (size_t i = 0; i < commentComponents.size(); i++)
|
||||||
{
|
{
|
||||||
@ -575,8 +621,9 @@ void PreviewView::NotifyCommentsChanged(PreviewModel * sender)
|
|||||||
commentTextComponents.clear();
|
commentTextComponents.clear();
|
||||||
commentsPanel->InnerSize = ui::Point(0, 0);
|
commentsPanel->InnerSize = ui::Point(0, 0);
|
||||||
|
|
||||||
if (comments)
|
if (commentsPtr)
|
||||||
{
|
{
|
||||||
|
auto &comments = *commentsPtr;
|
||||||
for (size_t i = 0; i < commentComponents.size(); i++)
|
for (size_t i = 0; i < commentComponents.size(); i++)
|
||||||
{
|
{
|
||||||
commentsPanel->RemoveChild(commentComponents[i]);
|
commentsPanel->RemoveChild(commentComponents[i]);
|
||||||
@ -589,11 +636,11 @@ void PreviewView::NotifyCommentsChanged(PreviewModel * sender)
|
|||||||
ui::Label * tempUsername;
|
ui::Label * tempUsername;
|
||||||
ui::Label * tempComment;
|
ui::Label * tempComment;
|
||||||
ui::AvatarButton * tempAvatar;
|
ui::AvatarButton * tempAvatar;
|
||||||
for (size_t i = 0; i < comments->size(); i++)
|
for (size_t i = 0; i < comments.size(); i++)
|
||||||
{
|
{
|
||||||
if (showAvatars)
|
if (showAvatars)
|
||||||
{
|
{
|
||||||
tempAvatar = new ui::AvatarButton(ui::Point(2, currentY+7), ui::Point(26, 26), comments->at(i)->authorName);
|
tempAvatar = new ui::AvatarButton(ui::Point(2, currentY+7), ui::Point(26, 26), comments[i].authorName);
|
||||||
tempAvatar->SetActionCallback({ [tempAvatar] {
|
tempAvatar->SetActionCallback({ [tempAvatar] {
|
||||||
if (tempAvatar->GetUsername().size() > 0)
|
if (tempAvatar->GetUsername().size() > 0)
|
||||||
{
|
{
|
||||||
@ -604,25 +651,38 @@ void PreviewView::NotifyCommentsChanged(PreviewModel * sender)
|
|||||||
commentsPanel->AddChild(tempAvatar);
|
commentsPanel->AddChild(tempAvatar);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto authorNameFormatted = comments[i].authorName.FromUtf8();
|
||||||
|
if (comments[i].authorElevation != User::ElevationNone || comments[i].authorName == "jacobot")
|
||||||
|
{
|
||||||
|
authorNameFormatted = "\bt" + authorNameFormatted;
|
||||||
|
}
|
||||||
|
else if (comments[i].authorIsBanned)
|
||||||
|
{
|
||||||
|
authorNameFormatted = "\bg" + authorNameFormatted;
|
||||||
|
}
|
||||||
|
else if (Client::Ref().GetAuthUser().UserID && Client::Ref().GetAuthUser().Username == comments[i].authorName)
|
||||||
|
{
|
||||||
|
authorNameFormatted = "\bo" + authorNameFormatted;
|
||||||
|
}
|
||||||
|
else if (sender->GetSaveInfo() && sender->GetSaveInfo()->GetUserName() == comments[i].authorName)
|
||||||
|
{
|
||||||
|
authorNameFormatted = "\bl" + authorNameFormatted;
|
||||||
|
}
|
||||||
if (showAvatars)
|
if (showAvatars)
|
||||||
tempUsername = new ui::Label(ui::Point(31, currentY+3), ui::Point(Size.X-((XRES/2) + 13 + 26), 16), comments->at(i)->authorNameFormatted.FromUtf8());
|
tempUsername = new ui::Label(ui::Point(31, currentY+3), ui::Point(Size.X-((XRES/2) + 13 + 26), 16), authorNameFormatted);
|
||||||
else
|
else
|
||||||
tempUsername = new ui::Label(ui::Point(5, currentY+3), ui::Point(Size.X-((XRES/2) + 13), 16), comments->at(i)->authorNameFormatted.FromUtf8());
|
tempUsername = new ui::Label(ui::Point(5, currentY+3), ui::Point(Size.X-((XRES/2) + 13), 16), authorNameFormatted);
|
||||||
tempUsername->Appearance.HorizontalAlign = ui::Appearance::AlignLeft;
|
tempUsername->Appearance.HorizontalAlign = ui::Appearance::AlignLeft;
|
||||||
tempUsername->Appearance.VerticalAlign = ui::Appearance::AlignBottom;
|
tempUsername->Appearance.VerticalAlign = ui::Appearance::AlignBottom;
|
||||||
if (Client::Ref().GetAuthUser().UserID && Client::Ref().GetAuthUser().Username == comments->at(i)->authorName)
|
|
||||||
tempUsername->SetTextColour(ui::Colour(255, 255, 100));
|
|
||||||
else if (sender->GetSaveInfo() && sender->GetSaveInfo()->GetUserName() == comments->at(i)->authorName)
|
|
||||||
tempUsername->SetTextColour(ui::Colour(255, 100, 100));
|
|
||||||
currentY += 16;
|
currentY += 16;
|
||||||
|
|
||||||
commentComponents.push_back(tempUsername);
|
commentComponents.push_back(tempUsername);
|
||||||
commentsPanel->AddChild(tempUsername);
|
commentsPanel->AddChild(tempUsername);
|
||||||
|
|
||||||
if (showAvatars)
|
if (showAvatars)
|
||||||
tempComment = new ui::Label(ui::Point(31, currentY+5), ui::Point(Size.X-((XRES/2) + 13 + 26), -1), comments->at(i)->comment);
|
tempComment = new ui::Label(ui::Point(31, currentY+5), ui::Point(Size.X-((XRES/2) + 13 + 26), -1), comments[i].content);
|
||||||
else
|
else
|
||||||
tempComment = new ui::Label(ui::Point(5, currentY+5), ui::Point(Size.X-((XRES/2) + 13), -1), comments->at(i)->comment);
|
tempComment = new ui::Label(ui::Point(5, currentY+5), ui::Point(Size.X-((XRES/2) + 13), -1), comments[i].content);
|
||||||
tempComment->SetMultiline(true);
|
tempComment->SetMultiline(true);
|
||||||
tempComment->Appearance.HorizontalAlign = ui::Appearance::AlignLeft;
|
tempComment->Appearance.HorizontalAlign = ui::Appearance::AlignLeft;
|
||||||
tempComment->Appearance.VerticalAlign = ui::Appearance::AlignTop;
|
tempComment->Appearance.VerticalAlign = ui::Appearance::AlignTop;
|
||||||
|
@ -5,6 +5,12 @@
|
|||||||
#include "common/String.h"
|
#include "common/String.h"
|
||||||
#include "gui/interface/Window.h"
|
#include "gui/interface/Window.h"
|
||||||
|
|
||||||
|
namespace http
|
||||||
|
{
|
||||||
|
class AddCommentRequest;
|
||||||
|
class ReportSaveRequest;
|
||||||
|
}
|
||||||
|
|
||||||
namespace ui
|
namespace ui
|
||||||
{
|
{
|
||||||
class Button;
|
class Button;
|
||||||
@ -64,6 +70,10 @@ class PreviewView: public ui::Window
|
|||||||
void submitComment();
|
void submitComment();
|
||||||
bool CheckSwearing(String text);
|
bool CheckSwearing(String text);
|
||||||
void CheckComment();
|
void CheckComment();
|
||||||
|
|
||||||
|
std::unique_ptr<http::AddCommentRequest> addCommentRequest;
|
||||||
|
std::unique_ptr<http::ReportSaveRequest> reportSaveRequest;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
void AttachController(PreviewController * controller);
|
void AttachController(PreviewController * controller);
|
||||||
PreviewView(std::unique_ptr<VideoBuffer> newSavePreviev);
|
PreviewView(std::unique_ptr<VideoBuffer> newSavePreviev);
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
#include "ProfileActivity.h"
|
#include "ProfileActivity.h"
|
||||||
#include "client/Client.h"
|
#include "client/Client.h"
|
||||||
|
#include "client/http/SaveUserInfoRequest.h"
|
||||||
|
#include "client/http/GetUserInfoRequest.h"
|
||||||
#include "common/platform/Platform.h"
|
#include "common/platform/Platform.h"
|
||||||
#include "gui/Style.h"
|
#include "gui/Style.h"
|
||||||
#include "gui/interface/AvatarButton.h"
|
#include "gui/interface/AvatarButton.h"
|
||||||
@ -200,30 +202,30 @@ void ProfileActivity::OnTick(float dt)
|
|||||||
|
|
||||||
if (saveUserInfoRequest && saveUserInfoRequest->CheckDone())
|
if (saveUserInfoRequest && saveUserInfoRequest->CheckDone())
|
||||||
{
|
{
|
||||||
auto SaveUserInfoStatus = saveUserInfoRequest->Finish();
|
try
|
||||||
if (SaveUserInfoStatus)
|
|
||||||
{
|
{
|
||||||
|
saveUserInfoRequest->Finish();
|
||||||
Exit();
|
Exit();
|
||||||
}
|
}
|
||||||
else
|
catch (const http::RequestError &ex)
|
||||||
{
|
{
|
||||||
doError = true;
|
doError = true;
|
||||||
doErrorMessage = "Could not save user info: " + Client::Ref().GetLastError();
|
doErrorMessage = "Could not save user info: " + ByteString(ex.what()).FromUtf8();
|
||||||
}
|
}
|
||||||
saveUserInfoRequest.reset();
|
saveUserInfoRequest.reset();
|
||||||
}
|
}
|
||||||
if (getUserInfoRequest && getUserInfoRequest->CheckDone())
|
if (getUserInfoRequest && getUserInfoRequest->CheckDone())
|
||||||
{
|
{
|
||||||
auto getUserInfoResult = getUserInfoRequest->Finish();
|
try
|
||||||
if (getUserInfoResult)
|
|
||||||
{
|
{
|
||||||
|
auto userInfo = getUserInfoRequest->Finish();
|
||||||
loading = false;
|
loading = false;
|
||||||
setUserInfo(*getUserInfoResult);
|
setUserInfo(userInfo);
|
||||||
}
|
}
|
||||||
else
|
catch (const http::RequestError &ex)
|
||||||
{
|
{
|
||||||
doError = true;
|
doError = true;
|
||||||
doErrorMessage = "Could not load user info: " + Client::Ref().GetLastError();
|
doErrorMessage = "Could not load user info: " + ByteString(ex.what()).FromUtf8();
|
||||||
}
|
}
|
||||||
getUserInfoRequest.reset();
|
getUserInfoRequest.reset();
|
||||||
}
|
}
|
||||||
|
@ -2,10 +2,14 @@
|
|||||||
#include "common/String.h"
|
#include "common/String.h"
|
||||||
#include "Activity.h"
|
#include "Activity.h"
|
||||||
#include "client/UserInfo.h"
|
#include "client/UserInfo.h"
|
||||||
#include "client/http/SaveUserInfoRequest.h"
|
|
||||||
#include "client/http/GetUserInfoRequest.h"
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
|
namespace http
|
||||||
|
{
|
||||||
|
class SaveUserInfoRequest;
|
||||||
|
class GetUserInfoRequest;
|
||||||
|
}
|
||||||
|
|
||||||
namespace ui
|
namespace ui
|
||||||
{
|
{
|
||||||
class Label;
|
class Label;
|
||||||
|
@ -1,7 +1,5 @@
|
|||||||
#include "ServerSaveActivity.h"
|
#include "ServerSaveActivity.h"
|
||||||
|
|
||||||
#include "graphics/Graphics.h"
|
#include "graphics/Graphics.h"
|
||||||
|
|
||||||
#include "gui/interface/Label.h"
|
#include "gui/interface/Label.h"
|
||||||
#include "gui/interface/Textbox.h"
|
#include "gui/interface/Textbox.h"
|
||||||
#include "gui/interface/Button.h"
|
#include "gui/interface/Button.h"
|
||||||
@ -10,13 +8,11 @@
|
|||||||
#include "gui/dialogues/SaveIDMessage.h"
|
#include "gui/dialogues/SaveIDMessage.h"
|
||||||
#include "gui/dialogues/ConfirmPrompt.h"
|
#include "gui/dialogues/ConfirmPrompt.h"
|
||||||
#include "gui/dialogues/InformationMessage.h"
|
#include "gui/dialogues/InformationMessage.h"
|
||||||
|
|
||||||
#include "client/Client.h"
|
#include "client/Client.h"
|
||||||
#include "client/ThumbnailRendererTask.h"
|
#include "client/ThumbnailRendererTask.h"
|
||||||
#include "client/GameSave.h"
|
#include "client/GameSave.h"
|
||||||
|
#include "client/http/UploadSaveRequest.h"
|
||||||
#include "tasks/Task.h"
|
#include "tasks/Task.h"
|
||||||
|
|
||||||
#include "gui/Style.h"
|
#include "gui/Style.h"
|
||||||
|
|
||||||
class SaveUploadTask: public Task
|
class SaveUploadTask: public Task
|
||||||
@ -36,7 +32,19 @@ class SaveUploadTask: public Task
|
|||||||
bool doWork() override
|
bool doWork() override
|
||||||
{
|
{
|
||||||
notifyProgress(-1);
|
notifyProgress(-1);
|
||||||
return Client::Ref().UploadSave(save) == RequestOkay;
|
auto uploadSaveRequest = std::make_unique<http::UploadSaveRequest>(save);
|
||||||
|
uploadSaveRequest->Start();
|
||||||
|
uploadSaveRequest->Wait();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
save.SetID(uploadSaveRequest->Finish());
|
||||||
|
}
|
||||||
|
catch (const http::RequestError &ex)
|
||||||
|
{
|
||||||
|
notifyError(ByteString(ex.what()).FromUtf8());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
@ -168,7 +176,7 @@ void ServerSaveActivity::NotifyDone(Task * task)
|
|||||||
if(!task->GetSuccess())
|
if(!task->GetSuccess())
|
||||||
{
|
{
|
||||||
Exit();
|
Exit();
|
||||||
new ErrorMessage("Error", Client::Ref().GetLastError());
|
new ErrorMessage("Error", task->GetError());
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -182,24 +190,20 @@ void ServerSaveActivity::NotifyDone(Task * task)
|
|||||||
|
|
||||||
void ServerSaveActivity::Save()
|
void ServerSaveActivity::Save()
|
||||||
{
|
{
|
||||||
if(nameField->GetText().length())
|
if (!nameField->GetText().length())
|
||||||
{
|
{
|
||||||
if(Client::Ref().GetAuthUser().Username != save->GetUserName() && publishedCheckbox->GetChecked())
|
new ErrorMessage("Error", "You must specify a save name.");
|
||||||
{
|
return;
|
||||||
new ConfirmPrompt("Publish", "This save was created by " + save->GetUserName().FromUtf8() + ", you're about to publish this under your own name; If you haven't been given permission by the author to do so, please uncheck the publish box, otherwise continue", { [this] {
|
}
|
||||||
Exit();
|
if(Client::Ref().GetAuthUser().Username != save->GetUserName() && publishedCheckbox->GetChecked())
|
||||||
saveUpload();
|
{
|
||||||
} });
|
new ConfirmPrompt("Publish", "This save was created by " + save->GetUserName().FromUtf8() + ", you're about to publish this under your own name; If you haven't been given permission by the author to do so, please uncheck the publish box, otherwise continue", { [this] {
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Exit();
|
|
||||||
saveUpload();
|
saveUpload();
|
||||||
}
|
} });
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
new ErrorMessage("Error", "You must specify a save name.");
|
saveUpload();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -223,6 +227,7 @@ void ServerSaveActivity::AddAuthorInfo()
|
|||||||
|
|
||||||
void ServerSaveActivity::saveUpload()
|
void ServerSaveActivity::saveUpload()
|
||||||
{
|
{
|
||||||
|
okayButton->Enabled = false;
|
||||||
save->SetName(nameField->GetText());
|
save->SetName(nameField->GetText());
|
||||||
save->SetDescription(descriptionField->GetText());
|
save->SetDescription(descriptionField->GetText());
|
||||||
save->SetPublished(publishedCheckbox->GetChecked());
|
save->SetPublished(publishedCheckbox->GetChecked());
|
||||||
@ -234,16 +239,8 @@ void ServerSaveActivity::saveUpload()
|
|||||||
save->SetGameSave(std::move(gameSave));
|
save->SetGameSave(std::move(gameSave));
|
||||||
}
|
}
|
||||||
AddAuthorInfo();
|
AddAuthorInfo();
|
||||||
|
uploadSaveRequest = std::make_unique<http::UploadSaveRequest>(*save);
|
||||||
if(Client::Ref().UploadSave(*save) != RequestOkay)
|
uploadSaveRequest->Start();
|
||||||
{
|
|
||||||
new ErrorMessage("Error", "Upload failed with error:\n"+Client::Ref().GetLastError());
|
|
||||||
}
|
|
||||||
else if (onUploaded)
|
|
||||||
{
|
|
||||||
new SaveIDMessage(save->GetID());
|
|
||||||
onUploaded(std::move(save));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ServerSaveActivity::Exit()
|
void ServerSaveActivity::Exit()
|
||||||
@ -364,6 +361,26 @@ void ServerSaveActivity::OnTick(float dt)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (uploadSaveRequest && uploadSaveRequest->CheckDone())
|
||||||
|
{
|
||||||
|
okayButton->Enabled = true;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
save->SetID(uploadSaveRequest->Finish());
|
||||||
|
Exit();
|
||||||
|
new SaveIDMessage(save->GetID());
|
||||||
|
if (onUploaded)
|
||||||
|
{
|
||||||
|
onUploaded(std::move(save));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (const http::RequestError &ex)
|
||||||
|
{
|
||||||
|
new ErrorMessage("Error", "Upload failed with error:\n" + ByteString(ex.what()).FromUtf8());
|
||||||
|
}
|
||||||
|
uploadSaveRequest.reset();
|
||||||
|
}
|
||||||
|
|
||||||
if(saveUploadTask)
|
if(saveUploadTask)
|
||||||
saveUploadTask->Poll();
|
saveUploadTask->Poll();
|
||||||
}
|
}
|
||||||
|
@ -13,6 +13,11 @@
|
|||||||
|
|
||||||
#include "save_online.png.h"
|
#include "save_online.png.h"
|
||||||
|
|
||||||
|
namespace http
|
||||||
|
{
|
||||||
|
class UploadSaveRequest;
|
||||||
|
}
|
||||||
|
|
||||||
namespace ui
|
namespace ui
|
||||||
{
|
{
|
||||||
class Label;
|
class Label;
|
||||||
@ -25,6 +30,8 @@ class Task;
|
|||||||
class VideoBuffer;
|
class VideoBuffer;
|
||||||
class ServerSaveActivity: public WindowActivity, public TaskListener
|
class ServerSaveActivity: public WindowActivity, public TaskListener
|
||||||
{
|
{
|
||||||
|
std::unique_ptr<http::UploadSaveRequest> uploadSaveRequest;
|
||||||
|
|
||||||
using OnUploaded = std::function<void (std::unique_ptr<SaveInfo>)>;
|
using OnUploaded = std::function<void (std::unique_ptr<SaveInfo>)>;
|
||||||
std::unique_ptr<PlaneAdapter<std::vector<pixel_rgba>>> saveToServerImage = format::PixelsFromPNG(
|
std::unique_ptr<PlaneAdapter<std::vector<pixel_rgba>>> saveToServerImage = format::PixelsFromPNG(
|
||||||
std::vector<char>(save_online_png, save_online_png + save_online_png_size)
|
std::vector<char>(save_online_png, save_online_png + save_online_png_size)
|
||||||
|
@ -7,6 +7,12 @@
|
|||||||
#include "client/Client.h"
|
#include "client/Client.h"
|
||||||
#include "client/SaveInfo.h"
|
#include "client/SaveInfo.h"
|
||||||
#include "client/GameSave.h"
|
#include "client/GameSave.h"
|
||||||
|
#include "client/http/DeleteSaveRequest.h"
|
||||||
|
#include "client/http/PublishSaveRequest.h"
|
||||||
|
#include "client/http/UnpublishSaveRequest.h"
|
||||||
|
#include "client/http/FavouriteSaveRequest.h"
|
||||||
|
#include "client/http/SearchSavesRequest.h"
|
||||||
|
#include "client/http/SearchTagsRequest.h"
|
||||||
#include "common/platform/Platform.h"
|
#include "common/platform/Platform.h"
|
||||||
#include "common/tpt-minmax.h"
|
#include "common/tpt-minmax.h"
|
||||||
#include "graphics/Graphics.h"
|
#include "graphics/Graphics.h"
|
||||||
@ -132,13 +138,13 @@ void SearchController::SetPageRelative(int offset)
|
|||||||
|
|
||||||
void SearchController::ChangeSort()
|
void SearchController::ChangeSort()
|
||||||
{
|
{
|
||||||
if(searchModel->GetSort() == "new")
|
if(searchModel->GetSort() == http::sortByDate)
|
||||||
{
|
{
|
||||||
searchModel->SetSort("best");
|
searchModel->SetSort(http::sortByVotes);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
searchModel->SetSort("new");
|
searchModel->SetSort(http::sortByDate);
|
||||||
}
|
}
|
||||||
searchModel->UpdateSaveList(1, searchModel->GetLastQuery());
|
searchModel->UpdateSaveList(1, searchModel->GetLastQuery());
|
||||||
}
|
}
|
||||||
@ -183,7 +189,7 @@ void SearchController::SelectAllSaves()
|
|||||||
if (!Client::Ref().GetAuthUser().UserID)
|
if (!Client::Ref().GetAuthUser().UserID)
|
||||||
return;
|
return;
|
||||||
if (searchModel->GetShowOwn() ||
|
if (searchModel->GetShowOwn() ||
|
||||||
Client::Ref().GetAuthUser().UserElevation == User::ElevationModerator ||
|
Client::Ref().GetAuthUser().UserElevation == User::ElevationMod ||
|
||||||
Client::Ref().GetAuthUser().UserElevation == User::ElevationAdmin)
|
Client::Ref().GetAuthUser().UserElevation == User::ElevationAdmin)
|
||||||
searchModel->SelectAllSaves();
|
searchModel->SelectAllSaves();
|
||||||
|
|
||||||
@ -245,9 +251,16 @@ void SearchController::removeSelectedC()
|
|||||||
for (size_t i = 0; i < saves.size(); i++)
|
for (size_t i = 0; i < saves.size(); i++)
|
||||||
{
|
{
|
||||||
notifyStatus(String::Build("Deleting save [", saves[i], "] ..."));
|
notifyStatus(String::Build("Deleting save [", saves[i], "] ..."));
|
||||||
if (Client::Ref().DeleteSave(saves[i])!=RequestOkay)
|
auto deleteSaveRequest = std::make_unique<http::DeleteSaveRequest>(saves[i]);
|
||||||
|
deleteSaveRequest->Start();
|
||||||
|
deleteSaveRequest->Wait();
|
||||||
|
try
|
||||||
{
|
{
|
||||||
notifyError(String::Build("Failed to delete [", saves[i], "]: ", Client::Ref().GetLastError()));
|
deleteSaveRequest->Finish();
|
||||||
|
}
|
||||||
|
catch (const http::RequestError &ex)
|
||||||
|
{
|
||||||
|
notifyError(String::Build("Failed to delete [", saves[i], "]: ", ByteString(ex.what()).FromAscii()));
|
||||||
c->Refresh();
|
c->Refresh();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -286,37 +299,49 @@ void SearchController::unpublishSelectedC(bool publish)
|
|||||||
public:
|
public:
|
||||||
UnpublishSavesTask(std::vector<int> saves_, SearchController *c_, bool publish_) { saves = saves_; c = c_; publish = publish_; }
|
UnpublishSavesTask(std::vector<int> saves_, SearchController *c_, bool publish_) { saves = saves_; c = c_; publish = publish_; }
|
||||||
|
|
||||||
bool PublishSave(int saveID)
|
void PublishSave(int saveID)
|
||||||
{
|
{
|
||||||
notifyStatus(String::Build("Publishing save [", saveID, "]"));
|
notifyStatus(String::Build("Publishing save [", saveID, "]"));
|
||||||
if (Client::Ref().PublishSave(saveID) != RequestOkay)
|
auto publishSaveRequest = std::make_unique<http::PublishSaveRequest>(saveID);
|
||||||
return false;
|
publishSaveRequest->Start();
|
||||||
return true;
|
publishSaveRequest->Wait();
|
||||||
|
publishSaveRequest->Finish();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool UnpublishSave(int saveID)
|
void UnpublishSave(int saveID)
|
||||||
{
|
{
|
||||||
notifyStatus(String::Build("Unpublishing save [", saveID, "]"));
|
notifyStatus(String::Build("Unpublishing save [", saveID, "]"));
|
||||||
if (Client::Ref().UnpublishSave(saveID) != RequestOkay)
|
auto unpublishSaveRequest = std::make_unique<http::UnpublishSaveRequest>(saveID);
|
||||||
return false;
|
unpublishSaveRequest->Start();
|
||||||
return true;
|
unpublishSaveRequest->Wait();
|
||||||
|
unpublishSaveRequest->Finish();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool doWork() override
|
bool doWork() override
|
||||||
{
|
{
|
||||||
bool ret;
|
|
||||||
for (size_t i = 0; i < saves.size(); i++)
|
for (size_t i = 0; i < saves.size(); i++)
|
||||||
{
|
{
|
||||||
if (publish)
|
try
|
||||||
ret = PublishSave(saves[i]);
|
{
|
||||||
else
|
if (publish)
|
||||||
ret = UnpublishSave(saves[i]);
|
{
|
||||||
if (!ret)
|
PublishSave(saves[i]);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
UnpublishSave(saves[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (const http::RequestError &ex)
|
||||||
{
|
{
|
||||||
if (publish) // uses html page so error message will be spam
|
if (publish) // uses html page so error message will be spam
|
||||||
|
{
|
||||||
notifyError(String::Build("Failed to publish [", saves[i], "], is this save yours?"));
|
notifyError(String::Build("Failed to publish [", saves[i], "], is this save yours?"));
|
||||||
|
}
|
||||||
else
|
else
|
||||||
notifyError(String::Build("Failed to unpublish [", saves[i], "]: " + Client::Ref().GetLastError()));
|
{
|
||||||
|
notifyError(String::Build("Failed to unpublish [", saves[i], "]: ", ByteString(ex.what()).FromAscii()));
|
||||||
|
}
|
||||||
c->Refresh();
|
c->Refresh();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -343,9 +368,16 @@ void SearchController::FavouriteSelected()
|
|||||||
for (size_t i = 0; i < saves.size(); i++)
|
for (size_t i = 0; i < saves.size(); i++)
|
||||||
{
|
{
|
||||||
notifyStatus(String::Build("Favouring save [", saves[i], "]"));
|
notifyStatus(String::Build("Favouring save [", saves[i], "]"));
|
||||||
if (Client::Ref().FavouriteSave(saves[i], true)!=RequestOkay)
|
auto favouriteSaveRequest = std::make_unique<http::FavouriteSaveRequest>(saves[i], true);
|
||||||
|
favouriteSaveRequest->Start();
|
||||||
|
favouriteSaveRequest->Wait();
|
||||||
|
try
|
||||||
{
|
{
|
||||||
notifyError(String::Build("Failed to favourite [", saves[i], "]: " + Client::Ref().GetLastError()));
|
favouriteSaveRequest->Finish();
|
||||||
|
}
|
||||||
|
catch (const http::RequestError &ex)
|
||||||
|
{
|
||||||
|
notifyError(String::Build("Failed to favourite [", saves[i], "]: ", ByteString(ex.what()).FromAscii()));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
notifyProgress((i + 1) * 100 / saves.size());
|
notifyProgress((i + 1) * 100 / saves.size());
|
||||||
@ -364,9 +396,16 @@ void SearchController::FavouriteSelected()
|
|||||||
for (size_t i = 0; i < saves.size(); i++)
|
for (size_t i = 0; i < saves.size(); i++)
|
||||||
{
|
{
|
||||||
notifyStatus(String::Build("Unfavouring save [", saves[i], "]"));
|
notifyStatus(String::Build("Unfavouring save [", saves[i], "]"));
|
||||||
if (Client::Ref().FavouriteSave(saves[i], false)!=RequestOkay)
|
auto unfavouriteSaveRequest = std::make_unique<http::FavouriteSaveRequest>(saves[i], false);
|
||||||
|
unfavouriteSaveRequest->Start();
|
||||||
|
unfavouriteSaveRequest->Wait();
|
||||||
|
try
|
||||||
{
|
{
|
||||||
notifyError(String::Build("Failed to unfavourite [", saves[i], "]: " + Client::Ref().GetLastError()));
|
unfavouriteSaveRequest->Finish();
|
||||||
|
}
|
||||||
|
catch (const http::RequestError &ex)
|
||||||
|
{
|
||||||
|
notifyError(String::Build("Failed to unfavourite [", saves[i], "]: ", ByteString(ex.what()).FromAscii()));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
notifyProgress((i + 1) * 100 / saves.size());
|
notifyProgress((i + 1) * 100 / saves.size());
|
||||||
|
@ -4,12 +4,14 @@
|
|||||||
#include "client/SaveInfo.h"
|
#include "client/SaveInfo.h"
|
||||||
#include "client/GameSave.h"
|
#include "client/GameSave.h"
|
||||||
#include "client/Client.h"
|
#include "client/Client.h"
|
||||||
|
#include "client/http/SearchSavesRequest.h"
|
||||||
|
#include "client/http/SearchTagsRequest.h"
|
||||||
#include "common/tpt-minmax.h"
|
#include "common/tpt-minmax.h"
|
||||||
#include <thread>
|
#include <thread>
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
|
|
||||||
SearchModel::SearchModel():
|
SearchModel::SearchModel():
|
||||||
currentSort("best"),
|
currentSort(http::sortByVotes),
|
||||||
currentPage(1),
|
currentPage(1),
|
||||||
resultCount(0),
|
resultCount(0),
|
||||||
showOwn(false),
|
showOwn(false),
|
||||||
@ -28,81 +30,26 @@ bool SearchModel::GetShowTags()
|
|||||||
return showTags;
|
return showTags;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SearchModel::BeginSearchSaves(int start, int count, String query, ByteString sort, ByteString category)
|
void SearchModel::BeginSearchSaves(int start, int count, String query, http::Sort sort, http::Category category)
|
||||||
{
|
{
|
||||||
lastError = "";
|
lastError = "";
|
||||||
resultCount = 0;
|
resultCount = 0;
|
||||||
ByteStringBuilder urlStream;
|
searchSaves = std::make_unique<http::SearchSavesRequest>(start, count, query.ToUtf8(), sort, category);
|
||||||
ByteString data;
|
|
||||||
urlStream << SCHEME << SERVER << "/Browse.json?Start=" << start << "&Count=" << count;
|
|
||||||
if(query.length() || sort.length())
|
|
||||||
{
|
|
||||||
urlStream << "&Search_Query=";
|
|
||||||
if(query.length())
|
|
||||||
urlStream << format::URLEncode(query.ToUtf8());
|
|
||||||
if(sort == "date")
|
|
||||||
{
|
|
||||||
if(query.length())
|
|
||||||
urlStream << format::URLEncode(" ");
|
|
||||||
urlStream << format::URLEncode("sort:") << format::URLEncode(sort);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if(category.length())
|
|
||||||
{
|
|
||||||
urlStream << "&Category=" << format::URLEncode(category);
|
|
||||||
}
|
|
||||||
searchSaves = std::make_unique<http::Request>(urlStream.Build());
|
|
||||||
auto authUser = Client::Ref().GetAuthUser();
|
|
||||||
if (authUser.UserID)
|
|
||||||
{
|
|
||||||
searchSaves->AuthHeaders(ByteString::Build(Client::Ref().GetAuthUser().UserID), Client::Ref().GetAuthUser().SessionID);
|
|
||||||
}
|
|
||||||
searchSaves->Start();
|
searchSaves->Start();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<std::unique_ptr<SaveInfo>> SearchModel::EndSearchSaves()
|
std::vector<std::unique_ptr<SaveInfo>> SearchModel::EndSearchSaves()
|
||||||
{
|
{
|
||||||
std::vector<std::unique_ptr<SaveInfo>> saveArray;
|
std::vector<std::unique_ptr<SaveInfo>> saveArray;
|
||||||
auto [ dataStatus, data ] = searchSaves->Finish();
|
try
|
||||||
|
{
|
||||||
|
std::tie(resultCount, saveArray) = searchSaves->Finish();
|
||||||
|
}
|
||||||
|
catch (const http::RequestError &ex)
|
||||||
|
{
|
||||||
|
lastError = ByteString(ex.what()).FromUtf8();
|
||||||
|
}
|
||||||
searchSaves.reset();
|
searchSaves.reset();
|
||||||
auto &client = Client::Ref();
|
|
||||||
client.ParseServerReturn(data, dataStatus, true);
|
|
||||||
if (dataStatus == 200 && data.size())
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
std::istringstream dataStream(data);
|
|
||||||
Json::Value objDocument;
|
|
||||||
dataStream >> objDocument;
|
|
||||||
|
|
||||||
resultCount = objDocument["Count"].asInt();
|
|
||||||
Json::Value savesArray = objDocument["Saves"];
|
|
||||||
for (Json::UInt j = 0; j < savesArray.size(); j++)
|
|
||||||
{
|
|
||||||
int tempID = savesArray[j]["ID"].asInt();
|
|
||||||
int tempCreatedDate = savesArray[j]["Created"].asInt();
|
|
||||||
int tempUpdatedDate = savesArray[j]["Updated"].asInt();
|
|
||||||
int tempScoreUp = savesArray[j]["ScoreUp"].asInt();
|
|
||||||
int tempScoreDown = savesArray[j]["ScoreDown"].asInt();
|
|
||||||
ByteString tempUsername = savesArray[j]["Username"].asString();
|
|
||||||
String tempName = ByteString(savesArray[j]["Name"].asString()).FromUtf8();
|
|
||||||
int tempVersion = savesArray[j]["Version"].asInt();
|
|
||||||
bool tempPublished = savesArray[j]["Published"].asBool();
|
|
||||||
auto tempSaveInfo = std::make_unique<SaveInfo>(tempID, tempCreatedDate, tempUpdatedDate, tempScoreUp, tempScoreDown, tempUsername, tempName);
|
|
||||||
tempSaveInfo->Version = tempVersion;
|
|
||||||
tempSaveInfo->SetPublished(tempPublished);
|
|
||||||
saveArray.push_back(std::move(tempSaveInfo));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (std::exception &e)
|
|
||||||
{
|
|
||||||
lastError = "Could not read response: " + ByteString(e.what()).FromUtf8();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
lastError = client.GetLastError();
|
|
||||||
}
|
|
||||||
return saveArray;
|
return saveArray;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -117,40 +64,22 @@ void SearchModel::BeginGetTags(int start, int count, String query)
|
|||||||
if(query.length())
|
if(query.length())
|
||||||
urlStream << format::URLEncode(query.ToUtf8());
|
urlStream << format::URLEncode(query.ToUtf8());
|
||||||
}
|
}
|
||||||
getTags = std::make_unique<http::Request>(urlStream.Build());
|
getTags = std::make_unique<http::SearchTagsRequest>(start, count, query.ToUtf8());
|
||||||
getTags->Start();
|
getTags->Start();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<std::pair<ByteString, int>> SearchModel::EndGetTags()
|
std::vector<std::pair<ByteString, int>> SearchModel::EndGetTags()
|
||||||
{
|
{
|
||||||
std::vector<std::pair<ByteString, int>> tagArray;
|
std::vector<std::pair<ByteString, int>> tagArray;
|
||||||
auto [ dataStatus, data ] = getTags->Finish();
|
try
|
||||||
|
{
|
||||||
|
tagArray = getTags->Finish();
|
||||||
|
}
|
||||||
|
catch (const http::RequestError &ex)
|
||||||
|
{
|
||||||
|
lastError = ByteString(ex.what()).FromUtf8();
|
||||||
|
}
|
||||||
getTags.reset();
|
getTags.reset();
|
||||||
if(dataStatus == 200 && data.size())
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
std::istringstream dataStream(data);
|
|
||||||
Json::Value objDocument;
|
|
||||||
dataStream >> objDocument;
|
|
||||||
|
|
||||||
Json::Value tagsArray = objDocument["Tags"];
|
|
||||||
for (Json::UInt j = 0; j < tagsArray.size(); j++)
|
|
||||||
{
|
|
||||||
int tagCount = tagsArray[j]["Count"].asInt();
|
|
||||||
ByteString tag = tagsArray[j]["Tag"].asString();
|
|
||||||
tagArray.push_back(std::pair<ByteString, int>(tag, tagCount));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (std::exception & e)
|
|
||||||
{
|
|
||||||
lastError = "Could not read response: " + ByteString(e.what()).FromUtf8();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
lastError = http::StatusText(dataStatus);
|
|
||||||
}
|
|
||||||
return tagArray;
|
return tagArray;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -166,7 +95,7 @@ bool SearchModel::UpdateSaveList(int pageNumber, String query)
|
|||||||
//resultCount = 0;
|
//resultCount = 0;
|
||||||
currentPage = pageNumber;
|
currentPage = pageNumber;
|
||||||
|
|
||||||
if(pageNumber == 1 && !showOwn && !showFavourite && currentSort == "best" && query == "")
|
if(pageNumber == 1 && !showOwn && !showFavourite && currentSort == http::sortByVotes && query == "")
|
||||||
SetShowTags(true);
|
SetShowTags(true);
|
||||||
else
|
else
|
||||||
SetShowTags(false);
|
SetShowTags(false);
|
||||||
@ -182,12 +111,16 @@ bool SearchModel::UpdateSaveList(int pageNumber, String query)
|
|||||||
BeginGetTags(0, 24, "");
|
BeginGetTags(0, 24, "");
|
||||||
}
|
}
|
||||||
|
|
||||||
ByteString category = "";
|
auto category = http::categoryNone;
|
||||||
if(showFavourite)
|
if (showFavourite)
|
||||||
category = "Favourites";
|
{
|
||||||
if(showOwn && Client::Ref().GetAuthUser().UserID)
|
category = http::categoryFavourites;
|
||||||
category = "by:"+Client::Ref().GetAuthUser().Username;
|
}
|
||||||
BeginSearchSaves((currentPage-1)*20, 20, lastQuery, currentSort=="new"?"date":"votes", category);
|
if (showOwn && Client::Ref().GetAuthUser().UserID)
|
||||||
|
{
|
||||||
|
category = http::categoryMyOwn;
|
||||||
|
}
|
||||||
|
BeginSearchSaves((currentPage-1)*20, 20, lastQuery, currentSort, category);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
@ -363,7 +296,7 @@ void SearchModel::notifySelectedChanged()
|
|||||||
|
|
||||||
int SearchModel::GetPageCount()
|
int SearchModel::GetPageCount()
|
||||||
{
|
{
|
||||||
if (!showOwn && !showFavourite && currentSort == "best" && lastQuery == "")
|
if (!showOwn && !showFavourite && currentSort == http::sortByVotes && lastQuery == "")
|
||||||
return std::max(1, (int)(ceil(resultCount/20.0f))+1); //add one for front page (front page saves are repeated twice)
|
return std::max(1, (int)(ceil(resultCount/20.0f))+1); //add one for front page (front page saves are repeated twice)
|
||||||
else
|
else
|
||||||
return std::max(1, (int)(ceil(resultCount/20.0f)));
|
return std::max(1, (int)(ceil(resultCount/20.0f)));
|
||||||
|
@ -1,26 +1,32 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include "common/String.h"
|
#include "common/String.h"
|
||||||
#include "client/http/Request.h"
|
#include "client/Search.h"
|
||||||
#include "Config.h"
|
#include "Config.h"
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
|
namespace http
|
||||||
|
{
|
||||||
|
class SearchSavesRequest;
|
||||||
|
class SearchTagsRequest;
|
||||||
|
}
|
||||||
|
|
||||||
class SaveInfo;
|
class SaveInfo;
|
||||||
class SearchView;
|
class SearchView;
|
||||||
class SearchModel
|
class SearchModel
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
std::unique_ptr<http::Request> searchSaves;
|
std::unique_ptr<http::SearchSavesRequest> searchSaves;
|
||||||
void BeginSearchSaves(int start, int count, String query, ByteString sort, ByteString category);
|
void BeginSearchSaves(int start, int count, String query, http::Sort sort, http::Category category);
|
||||||
std::vector<std::unique_ptr<SaveInfo>> EndSearchSaves();
|
std::vector<std::unique_ptr<SaveInfo>> EndSearchSaves();
|
||||||
|
|
||||||
void BeginGetTags(int start, int count, String query);
|
void BeginGetTags(int start, int count, String query);
|
||||||
std::vector<std::pair<ByteString, int>> EndGetTags();
|
std::vector<std::pair<ByteString, int>> EndGetTags();
|
||||||
std::unique_ptr<http::Request> getTags;
|
std::unique_ptr<http::SearchTagsRequest> getTags;
|
||||||
|
|
||||||
std::unique_ptr<SaveInfo> loadedSave;
|
std::unique_ptr<SaveInfo> loadedSave;
|
||||||
ByteString currentSort;
|
http::Sort currentSort;
|
||||||
String lastQuery;
|
String lastQuery;
|
||||||
String lastError;
|
String lastError;
|
||||||
std::vector<int> selected;
|
std::vector<int> selected;
|
||||||
@ -55,8 +61,8 @@ public:
|
|||||||
int GetPageCount();
|
int GetPageCount();
|
||||||
int GetPageNum() { return currentPage; }
|
int GetPageNum() { return currentPage; }
|
||||||
String GetLastQuery() { return lastQuery; }
|
String GetLastQuery() { return lastQuery; }
|
||||||
void SetSort(ByteString sort) { if(!searchSaves) { currentSort = sort; } notifySortChanged(); }
|
void SetSort(http::Sort sort) { if(!searchSaves) { currentSort = sort; } notifySortChanged(); }
|
||||||
ByteString GetSort() { return currentSort; }
|
http::Sort GetSort() { return currentSort; }
|
||||||
void SetShowOwn(bool show) { if(!searchSaves) { if(show!=showOwn) { showOwn = show; } } notifyShowOwnChanged(); }
|
void SetShowOwn(bool show) { if(!searchSaves) { if(show!=showOwn) { showOwn = show; } } notifyShowOwnChanged(); }
|
||||||
bool GetShowOwn() { return showOwn; }
|
bool GetShowOwn() { return showOwn; }
|
||||||
void SetShowFavourite(bool show) { if(show!=showFavourite && !searchSaves) { showFavourite = show; } notifyShowFavouriteChanged(); }
|
void SetShowFavourite(bool show) { if(show!=showFavourite && !searchSaves) { showFavourite = show; } notifyShowFavouriteChanged(); }
|
||||||
|
@ -211,7 +211,7 @@ void SearchView::Search(String query)
|
|||||||
|
|
||||||
void SearchView::NotifySortChanged(SearchModel * sender)
|
void SearchView::NotifySortChanged(SearchModel * sender)
|
||||||
{
|
{
|
||||||
if(sender->GetSort() == "best")
|
if(sender->GetSort() == http::sortByVotes)
|
||||||
{
|
{
|
||||||
sortButton->SetToggleState(false);
|
sortButton->SetToggleState(false);
|
||||||
sortButton->SetText("By votes");
|
sortButton->SetText("By votes");
|
||||||
@ -228,7 +228,7 @@ void SearchView::NotifySortChanged(SearchModel * sender)
|
|||||||
void SearchView::NotifyShowOwnChanged(SearchModel * sender)
|
void SearchView::NotifyShowOwnChanged(SearchModel * sender)
|
||||||
{
|
{
|
||||||
ownButton->SetToggleState(sender->GetShowOwn());
|
ownButton->SetToggleState(sender->GetShowOwn());
|
||||||
if(sender->GetShowOwn() || Client::Ref().GetAuthUser().UserElevation == User::ElevationAdmin || Client::Ref().GetAuthUser().UserElevation == User::ElevationModerator)
|
if(sender->GetShowOwn() || Client::Ref().GetAuthUser().UserElevation == User::ElevationAdmin || Client::Ref().GetAuthUser().UserElevation == User::ElevationMod)
|
||||||
{
|
{
|
||||||
unpublishSelected->Enabled = true;
|
unpublishSelected->Enabled = true;
|
||||||
removeSelected->Enabled = true;
|
removeSelected->Enabled = true;
|
||||||
@ -248,7 +248,7 @@ void SearchView::NotifyShowFavouriteChanged(SearchModel * sender)
|
|||||||
unpublishSelected->Enabled = false;
|
unpublishSelected->Enabled = false;
|
||||||
removeSelected->Enabled = false;
|
removeSelected->Enabled = false;
|
||||||
}
|
}
|
||||||
else if(sender->GetShowOwn() || Client::Ref().GetAuthUser().UserElevation == User::ElevationAdmin || Client::Ref().GetAuthUser().UserElevation == User::ElevationModerator)
|
else if(sender->GetShowOwn() || Client::Ref().GetAuthUser().UserElevation == User::ElevationAdmin || Client::Ref().GetAuthUser().UserElevation == User::ElevationMod)
|
||||||
{
|
{
|
||||||
unpublishSelected->Enabled = true;
|
unpublishSelected->Enabled = true;
|
||||||
removeSelected->Enabled = true;
|
removeSelected->Enabled = true;
|
||||||
@ -323,7 +323,7 @@ void SearchView::CheckAccess()
|
|||||||
favButton->Enabled = true;
|
favButton->Enabled = true;
|
||||||
favouriteSelected->Enabled = true;
|
favouriteSelected->Enabled = true;
|
||||||
|
|
||||||
if (Client::Ref().GetAuthUser().UserElevation == User::ElevationAdmin || Client::Ref().GetAuthUser().UserElevation == User::ElevationModerator)
|
if (Client::Ref().GetAuthUser().UserElevation == User::ElevationAdmin || Client::Ref().GetAuthUser().UserElevation == User::ElevationMod)
|
||||||
{
|
{
|
||||||
unpublishSelected->Enabled = true;
|
unpublishSelected->Enabled = true;
|
||||||
removeSelected->Enabled = true;
|
removeSelected->Enabled = true;
|
||||||
@ -569,7 +569,7 @@ void SearchView::NotifySaveListChanged(SearchModel * sender)
|
|||||||
});
|
});
|
||||||
if(Client::Ref().GetAuthUser().UserID)
|
if(Client::Ref().GetAuthUser().UserID)
|
||||||
saveButton->SetSelectable(true);
|
saveButton->SetSelectable(true);
|
||||||
if (saves[i]->GetUserName() == Client::Ref().GetAuthUser().Username || Client::Ref().GetAuthUser().UserElevation == User::ElevationAdmin || Client::Ref().GetAuthUser().UserElevation == User::ElevationModerator)
|
if (saves[i]->GetUserName() == Client::Ref().GetAuthUser().Username || Client::Ref().GetAuthUser().UserElevation == User::ElevationAdmin || Client::Ref().GetAuthUser().UserElevation == User::ElevationMod)
|
||||||
saveButton->SetShowVotes(true);
|
saveButton->SetShowVotes(true);
|
||||||
saveButtons.push_back(saveButton);
|
saveButtons.push_back(saveButton);
|
||||||
AddComponent(saveButton);
|
AddComponent(saveButton);
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
#include "TagsController.h"
|
#include "TagsController.h"
|
||||||
|
|
||||||
#include "TagsModel.h"
|
#include "TagsModel.h"
|
||||||
#include "TagsView.h"
|
#include "TagsView.h"
|
||||||
|
#include "client/http/AddTagRequest.h"
|
||||||
|
#include "client/http/RemoveTagRequest.h"
|
||||||
#include "gui/interface/Engine.h"
|
#include "gui/interface/Engine.h"
|
||||||
#include "client/SaveInfo.h"
|
#include "client/SaveInfo.h"
|
||||||
#include "Controller.h"
|
#include "Controller.h"
|
||||||
@ -36,6 +36,11 @@ void TagsController::AddTag(ByteString tag)
|
|||||||
tagsModel->AddTag(tag);
|
tagsModel->AddTag(tag);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TagsController::Tick()
|
||||||
|
{
|
||||||
|
tagsModel->Tick();
|
||||||
|
}
|
||||||
|
|
||||||
void TagsController::Exit()
|
void TagsController::Exit()
|
||||||
{
|
{
|
||||||
tagsView->CloseActiveWindow();
|
tagsView->CloseActiveWindow();
|
||||||
|
@ -18,5 +18,6 @@ public:
|
|||||||
void RemoveTag(ByteString tag);
|
void RemoveTag(ByteString tag);
|
||||||
void AddTag(ByteString tag);
|
void AddTag(ByteString tag);
|
||||||
void Exit();
|
void Exit();
|
||||||
|
void Tick();
|
||||||
virtual ~TagsController();
|
virtual ~TagsController();
|
||||||
};
|
};
|
||||||
|
@ -1,13 +1,15 @@
|
|||||||
#include "TagsModel.h"
|
#include "TagsModel.h"
|
||||||
|
|
||||||
#include "TagsView.h"
|
#include "TagsView.h"
|
||||||
#include "TagsModelException.h"
|
#include "TagsModelException.h"
|
||||||
|
|
||||||
#include "client/Client.h"
|
#include "client/Client.h"
|
||||||
#include "client/SaveInfo.h"
|
#include "client/SaveInfo.h"
|
||||||
|
#include "client/http/AddTagRequest.h"
|
||||||
|
#include "client/http/RemoveTagRequest.h"
|
||||||
|
#include "gui/dialogues/ErrorMessage.h"
|
||||||
|
|
||||||
void TagsModel::SetSave(SaveInfo *newSave /* non-owning */)
|
void TagsModel::SetSave(SaveInfo *newSave /* non-owning */)
|
||||||
{
|
{
|
||||||
|
queuedTags.clear();
|
||||||
this->save = newSave;
|
this->save = newSave;
|
||||||
notifyTagsChanged();
|
notifyTagsChanged();
|
||||||
}
|
}
|
||||||
@ -17,40 +19,73 @@ SaveInfo *TagsModel::GetSave() // non-owning
|
|||||||
return save;
|
return save;
|
||||||
}
|
}
|
||||||
|
|
||||||
void TagsModel::RemoveTag(ByteString tag)
|
void TagsModel::Tick()
|
||||||
{
|
{
|
||||||
if(save)
|
auto triggerTags = false;
|
||||||
|
std::list<ByteString> tags;
|
||||||
|
if (addTagRequest && addTagRequest->CheckDone())
|
||||||
{
|
{
|
||||||
std::list<ByteString> * tags = Client::Ref().RemoveTag(save->GetID(), tag);
|
try
|
||||||
if(tags)
|
|
||||||
{
|
{
|
||||||
save->SetTags(std::list<ByteString>(*tags));
|
tags = addTagRequest->Finish();
|
||||||
notifyTagsChanged();
|
triggerTags = true;
|
||||||
delete tags;
|
|
||||||
}
|
}
|
||||||
else
|
catch (const http::RequestError &ex)
|
||||||
{
|
{
|
||||||
throw TagsModelException(Client::Ref().GetLastError());
|
new ErrorMessage("Could not add tag", ByteString(ex.what()).FromUtf8());
|
||||||
|
}
|
||||||
|
addTagRequest.reset();
|
||||||
|
}
|
||||||
|
if (removeTagRequest && removeTagRequest->CheckDone())
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
tags = removeTagRequest->Finish();
|
||||||
|
triggerTags = true;
|
||||||
|
}
|
||||||
|
catch (const http::RequestError &ex)
|
||||||
|
{
|
||||||
|
new ErrorMessage("Could not remove tag", ByteString(ex.what()).FromUtf8());
|
||||||
|
}
|
||||||
|
removeTagRequest.reset();
|
||||||
|
}
|
||||||
|
if (triggerTags)
|
||||||
|
{
|
||||||
|
if (save)
|
||||||
|
{
|
||||||
|
save->SetTags(tags);
|
||||||
|
}
|
||||||
|
notifyTagsChanged();
|
||||||
|
}
|
||||||
|
if (!addTagRequest && !removeTagRequest && !queuedTags.empty())
|
||||||
|
{
|
||||||
|
auto it = queuedTags.begin();
|
||||||
|
auto [ tag, add ] = *it;
|
||||||
|
queuedTags.erase(it);
|
||||||
|
if (save)
|
||||||
|
{
|
||||||
|
if (add)
|
||||||
|
{
|
||||||
|
addTagRequest = std::make_unique<http::AddTagRequest>(save->GetID(), tag);
|
||||||
|
addTagRequest->Start();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
removeTagRequest = std::make_unique<http::RemoveTagRequest>(save->GetID(), tag);
|
||||||
|
removeTagRequest->Start();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TagsModel::RemoveTag(ByteString tag)
|
||||||
|
{
|
||||||
|
queuedTags[tag] = false;
|
||||||
|
}
|
||||||
|
|
||||||
void TagsModel::AddTag(ByteString tag)
|
void TagsModel::AddTag(ByteString tag)
|
||||||
{
|
{
|
||||||
if(save)
|
queuedTags[tag] = true;
|
||||||
{
|
|
||||||
std::list<ByteString> * tags = Client::Ref().AddTag(save->GetID(), tag);
|
|
||||||
if(tags)
|
|
||||||
{
|
|
||||||
save->SetTags(std::list<ByteString>(*tags));
|
|
||||||
notifyTagsChanged();
|
|
||||||
delete tags;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
throw TagsModelException(Client::Ref().GetLastError());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void TagsModel::AddObserver(TagsView * observer)
|
void TagsModel::AddObserver(TagsView * observer)
|
||||||
|
@ -1,11 +1,22 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include "common/String.h"
|
#include "common/String.h"
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
#include <map>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
namespace http
|
||||||
|
{
|
||||||
|
class AddTagRequest;
|
||||||
|
class RemoveTagRequest;
|
||||||
|
}
|
||||||
|
|
||||||
class SaveInfo;
|
class SaveInfo;
|
||||||
|
|
||||||
class TagsView;
|
class TagsView;
|
||||||
class TagsModel {
|
class TagsModel {
|
||||||
|
std::unique_ptr<http::AddTagRequest> addTagRequest;
|
||||||
|
std::unique_ptr<http::RemoveTagRequest> removeTagRequest;
|
||||||
|
std::map<ByteString, bool> queuedTags;
|
||||||
SaveInfo *save = nullptr; // non-owning
|
SaveInfo *save = nullptr; // non-owning
|
||||||
std::vector<TagsView*> observers;
|
std::vector<TagsView*> observers;
|
||||||
void notifyTagsChanged();
|
void notifyTagsChanged();
|
||||||
@ -15,4 +26,5 @@ public:
|
|||||||
void RemoveTag(ByteString tag);
|
void RemoveTag(ByteString tag);
|
||||||
void AddTag(ByteString tag);
|
void AddTag(ByteString tag);
|
||||||
SaveInfo *GetSave(); // non-owning
|
SaveInfo *GetSave(); // non-owning
|
||||||
|
void Tick();
|
||||||
};
|
};
|
||||||
|
@ -49,6 +49,11 @@ TagsView::TagsView():
|
|||||||
AddComponent(title);
|
AddComponent(title);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TagsView::OnTick(float dt)
|
||||||
|
{
|
||||||
|
c->Tick();
|
||||||
|
}
|
||||||
|
|
||||||
void TagsView::OnDraw()
|
void TagsView::OnDraw()
|
||||||
{
|
{
|
||||||
Graphics * g = GetGraphics();
|
Graphics * g = GetGraphics();
|
||||||
@ -76,7 +81,7 @@ void TagsView::NotifyTagsChanged(TagsModel * sender)
|
|||||||
tags.push_back(tempLabel);
|
tags.push_back(tempLabel);
|
||||||
AddComponent(tempLabel);
|
AddComponent(tempLabel);
|
||||||
|
|
||||||
if(sender->GetSave()->GetUserName() == Client::Ref().GetAuthUser().Username || Client::Ref().GetAuthUser().UserElevation == User::ElevationAdmin || Client::Ref().GetAuthUser().UserElevation == User::ElevationModerator)
|
if(sender->GetSave()->GetUserName() == Client::Ref().GetAuthUser().Username || Client::Ref().GetAuthUser().UserElevation == User::ElevationAdmin || Client::Ref().GetAuthUser().UserElevation == User::ElevationMod)
|
||||||
{
|
{
|
||||||
ui::Button * tempButton = new ui::Button(ui::Point(15, 37+(16*i)), ui::Point(11, 12));
|
ui::Button * tempButton = new ui::Button(ui::Point(15, 37+(16*i)), ui::Point(11, 12));
|
||||||
tempButton->Appearance.icon = IconDelete;
|
tempButton->Appearance.icon = IconDelete;
|
||||||
@ -85,14 +90,7 @@ void TagsView::NotifyTagsChanged(TagsModel * sender)
|
|||||||
tempButton->Appearance.HorizontalAlign = ui::Appearance::AlignCentre;
|
tempButton->Appearance.HorizontalAlign = ui::Appearance::AlignCentre;
|
||||||
tempButton->Appearance.VerticalAlign = ui::Appearance::AlignMiddle;
|
tempButton->Appearance.VerticalAlign = ui::Appearance::AlignMiddle;
|
||||||
tempButton->SetActionCallback({ [this, tag] {
|
tempButton->SetActionCallback({ [this, tag] {
|
||||||
try
|
c->RemoveTag(tag);
|
||||||
{
|
|
||||||
c->RemoveTag(tag);
|
|
||||||
}
|
|
||||||
catch(TagsModelException & ex)
|
|
||||||
{
|
|
||||||
new ErrorMessage("Could not remove tag", ByteString(ex.what()).FromUtf8());
|
|
||||||
}
|
|
||||||
} });
|
} });
|
||||||
tags.push_back(tempButton);
|
tags.push_back(tempButton);
|
||||||
AddComponent(tempButton);
|
AddComponent(tempButton);
|
||||||
@ -125,13 +123,6 @@ void TagsView::addTag()
|
|||||||
new ErrorMessage("Tag not long enough", "Must be at least 4 letters");
|
new ErrorMessage("Tag not long enough", "Must be at least 4 letters");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
try
|
c->AddTag(tagInput->GetText().ToUtf8());
|
||||||
{
|
|
||||||
c->AddTag(tagInput->GetText().ToUtf8());
|
|
||||||
}
|
|
||||||
catch(TagsModelException & ex)
|
|
||||||
{
|
|
||||||
new ErrorMessage("Could not add tag", ByteString(ex.what()).FromUtf8());
|
|
||||||
}
|
|
||||||
tagInput->SetText("");
|
tagInput->SetText("");
|
||||||
}
|
}
|
||||||
|
@ -22,6 +22,7 @@ class TagsView: public ui::Window {
|
|||||||
public:
|
public:
|
||||||
TagsView();
|
TagsView();
|
||||||
void OnDraw() override;
|
void OnDraw() override;
|
||||||
|
void OnTick(float dt) override;
|
||||||
void AttachController(TagsController * c_) { c = c_; }
|
void AttachController(TagsController * c_) { c = c_; }
|
||||||
void OnKeyPress(int key, int scan, bool repeat, bool shift, bool ctrl, bool alt) override;
|
void OnKeyPress(int key, int scan, bool repeat, bool shift, bool ctrl, bool alt) override;
|
||||||
void NotifyTagsChanged(TagsModel * sender);
|
void NotifyTagsChanged(TagsModel * sender);
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
#include "UpdateActivity.h"
|
#include "UpdateActivity.h"
|
||||||
#include "client/http/Request.h"
|
#include "client/http/Request.h"
|
||||||
#include "prefs/GlobalPrefs.h"
|
#include "prefs/GlobalPrefs.h"
|
||||||
#include "client/Client.h"
|
|
||||||
#include "common/platform/Platform.h"
|
#include "common/platform/Platform.h"
|
||||||
#include "tasks/Task.h"
|
#include "tasks/Task.h"
|
||||||
#include "tasks/TaskWindow.h"
|
#include "tasks/TaskWindow.h"
|
||||||
@ -53,7 +52,16 @@ private:
|
|||||||
Platform::Millisleep(1);
|
Platform::Millisleep(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto [ status, data ] = request->Finish();
|
int status;
|
||||||
|
ByteString data;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
std::tie(status, data) = request->Finish();
|
||||||
|
}
|
||||||
|
catch (const http::RequestError &ex)
|
||||||
|
{
|
||||||
|
return niceNotifyError("Could not download update: " + String::Build("Server responded with Status ", ByteString(ex.what()).FromAscii()));
|
||||||
|
}
|
||||||
if (status!=200)
|
if (status!=200)
|
||||||
{
|
{
|
||||||
return niceNotifyError("Could not download update: " + String::Build("Server responded with Status ", status));
|
return niceNotifyError("Could not download update: " + String::Build("Server responded with Status ", status));
|
||||||
@ -107,9 +115,9 @@ private:
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
UpdateActivity::UpdateActivity() {
|
UpdateActivity::UpdateActivity(UpdateInfo info)
|
||||||
ByteString file = ByteString::Build(SCHEME, USE_UPDATESERVER ? UPDATESERVER : SERVER, Client::Ref().GetUpdateInfo().File);
|
{
|
||||||
updateDownloadTask = new UpdateDownloadTask(file, this);
|
updateDownloadTask = new UpdateDownloadTask(info.file, this);
|
||||||
updateWindow = new TaskWindow("Downloading update...", updateDownloadTask, true);
|
updateWindow = new TaskWindow("Downloading update...", updateDownloadTask, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
#include "client/StartupInfo.h"
|
||||||
|
|
||||||
class Task;
|
class Task;
|
||||||
class TaskWindow;
|
class TaskWindow;
|
||||||
@ -7,7 +8,7 @@ class UpdateActivity
|
|||||||
Task * updateDownloadTask;
|
Task * updateDownloadTask;
|
||||||
TaskWindow * updateWindow;
|
TaskWindow * updateWindow;
|
||||||
public:
|
public:
|
||||||
UpdateActivity();
|
UpdateActivity(UpdateInfo info);
|
||||||
virtual ~UpdateActivity();
|
virtual ~UpdateActivity();
|
||||||
void Exit();
|
void Exit();
|
||||||
virtual void NotifyDone(Task * sender);
|
virtual void NotifyDone(Task * sender);
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user