mirror of
https://github.com/Cockatrice/Cockatrice.git
synced 2025-12-12 15:49:28 -08:00
Implement replay sharing (#6066)
* new protos * implement commands on server * add buttons * icons * run formatter * Message on get replay code failure * Add new commands to switch statement * Better failure messages * Fix permission check query * Change hash method * Prevent adding duplicate replays * Clean up TabReplay ui * Copy over replay name * base64 encode the hash * Shorten hash * Better failure messages * change icon back to search icon * check hash before checking if user already has access * update share icon * Update label text
This commit is contained in:
@@ -33,6 +33,7 @@
|
||||
<file>resources/icons/scales.svg</file>
|
||||
<file>resources/icons/search.svg</file>
|
||||
<file>resources/icons/settings.svg</file>
|
||||
<file>resources/icons/share.svg</file>
|
||||
<file>resources/icons/spectator.svg</file>
|
||||
<file>resources/icons/swap.svg</file>
|
||||
<file>resources/icons/sync.svg</file>
|
||||
|
||||
25
cockatrice/resources/icons/share.svg
Normal file
25
cockatrice/resources/icons/share.svg
Normal file
@@ -0,0 +1,25 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
|
||||
|
||||
<svg height="800"
|
||||
width="800"
|
||||
version="1.1"
|
||||
id="_x32_"
|
||||
viewBox="0 0 512 512"
|
||||
xml:space="preserve"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<defs id="defs1"/>
|
||||
<style type="text/css"
|
||||
id="style1">
|
||||
.st0{fill:#64C0FF;stroke:black;stroke-width:3;stroke-miterlimit:4;stroke-opacity:1}
|
||||
</style>
|
||||
<g id="g1"
|
||||
transform="matrix(0.87097097,0,0,1.0008579,38.609049,-0.21963163)"
|
||||
style="stroke-width:3.42738;stroke-dasharray:none">
|
||||
<path class="st0"
|
||||
d="M 512,255.995 277.045,65.394 v 103.574 c -17.255,0 -36.408,0 -57.542,0 -208.59,0 -249.35,153.44 -201.394,266.128 9.586,-103.098 142.053,-100.701 237.358,-100.701 7.247,0 14.446,0 21.578,0 v 112.211 z"
|
||||
id="path1"
|
||||
style="stroke-width:20;stroke:#000000;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.0 KiB |
@@ -6,15 +6,19 @@
|
||||
#include "../../settings/cache_settings.h"
|
||||
#include "pb/command_replay_delete_match.pb.h"
|
||||
#include "pb/command_replay_download.pb.h"
|
||||
#include "pb/command_replay_get_code.pb.h"
|
||||
#include "pb/command_replay_modify_match.pb.h"
|
||||
#include "pb/command_replay_submit_code.pb.h"
|
||||
#include "pb/event_replay_added.pb.h"
|
||||
#include "pb/game_replay.pb.h"
|
||||
#include "pb/response.pb.h"
|
||||
#include "pb/response_replay_download.pb.h"
|
||||
#include "pb/response_replay_get_code.pb.h"
|
||||
#include "tab_game.h"
|
||||
|
||||
#include <QAction>
|
||||
#include <QApplication>
|
||||
#include <QClipboard>
|
||||
#include <QDesktopServices>
|
||||
#include <QFileSystemModel>
|
||||
#include <QGroupBox>
|
||||
@@ -129,13 +133,26 @@ QGroupBox *TabReplays::createRightLayout()
|
||||
serverDirView = new RemoteReplayList_TreeWidget(client);
|
||||
|
||||
// Right side layout
|
||||
QToolBar *toolBar = new QToolBar;
|
||||
/* put an invisible dummy QToolBar in the leftmost column so that the main toolbar is centered.
|
||||
* Really ugly workaround, but I couldn't figure out the proper way to make it centered */
|
||||
QToolBar *dummyToolBar = new QToolBar(this);
|
||||
QSizePolicy sizePolicy = dummyToolBar->sizePolicy();
|
||||
sizePolicy.setRetainSizeWhenHidden(true);
|
||||
dummyToolBar->setSizePolicy(sizePolicy);
|
||||
dummyToolBar->setVisible(false);
|
||||
|
||||
QToolBar *toolBar = new QToolBar(this);
|
||||
toolBar->setOrientation(Qt::Horizontal);
|
||||
toolBar->setIconSize(QSize(32, 32));
|
||||
QHBoxLayout *toolBarLayout = new QHBoxLayout;
|
||||
toolBarLayout->addStretch();
|
||||
toolBarLayout->addWidget(toolBar);
|
||||
toolBarLayout->addStretch();
|
||||
|
||||
QToolBar *rightmostToolBar = new QToolBar(this);
|
||||
rightmostToolBar->setOrientation(Qt::Horizontal);
|
||||
rightmostToolBar->setIconSize(QSize(32, 32));
|
||||
|
||||
QGridLayout *toolBarLayout = new QGridLayout;
|
||||
toolBarLayout->addWidget(dummyToolBar, 0, 0, Qt::AlignLeft);
|
||||
toolBarLayout->addWidget(toolBar, 0, 1, Qt::AlignHCenter);
|
||||
toolBarLayout->addWidget(rightmostToolBar, 0, 2, Qt::AlignRight);
|
||||
|
||||
QVBoxLayout *vbox = new QVBoxLayout;
|
||||
vbox->addWidget(serverDirView);
|
||||
@@ -157,12 +174,22 @@ QGroupBox *TabReplays::createRightLayout()
|
||||
aDeleteRemoteReplay = new QAction(this);
|
||||
aDeleteRemoteReplay->setIcon(QPixmap("theme:icons/remove_row"));
|
||||
connect(aDeleteRemoteReplay, &QAction::triggered, this, &TabReplays::actDeleteRemoteReplay);
|
||||
aGetReplayCode = new QAction(this);
|
||||
aGetReplayCode->setIcon(QPixmap("theme:icons/share"));
|
||||
connect(aGetReplayCode, &QAction::triggered, this, &TabReplays::actGetReplayCode);
|
||||
|
||||
aSubmitReplayCode = new QAction(this);
|
||||
aSubmitReplayCode->setIcon(QPixmap("theme:icons/search"));
|
||||
connect(aSubmitReplayCode, &QAction::triggered, this, &TabReplays::actSubmitReplayCode);
|
||||
|
||||
// Add actions to toolbars
|
||||
toolBar->addAction(aOpenRemoteReplay);
|
||||
toolBar->addAction(aDownload);
|
||||
toolBar->addAction(aKeep);
|
||||
toolBar->addAction(aDeleteRemoteReplay);
|
||||
toolBar->addAction(aGetReplayCode);
|
||||
|
||||
rightmostToolBar->addAction(aSubmitReplayCode);
|
||||
|
||||
return groupBox;
|
||||
}
|
||||
@@ -181,6 +208,9 @@ void TabReplays::retranslateUi()
|
||||
aDownload->setText(tr("Download replay"));
|
||||
aKeep->setText(tr("Toggle expiration lock"));
|
||||
aDeleteRemoteReplay->setText(tr("Delete"));
|
||||
aGetReplayCode->setText(tr("Get replay share code"));
|
||||
|
||||
aSubmitReplayCode->setText(tr("Look up replay by share code"));
|
||||
}
|
||||
|
||||
void TabReplays::handleConnected(const ServerInfo_User &userInfo)
|
||||
@@ -204,6 +234,8 @@ void TabReplays::setRemoteEnabled(bool enabled)
|
||||
aDownload->setEnabled(enabled);
|
||||
aKeep->setEnabled(enabled);
|
||||
aDeleteRemoteReplay->setEnabled(enabled);
|
||||
aGetReplayCode->setEnabled(enabled);
|
||||
aSubmitReplayCode->setEnabled(enabled);
|
||||
|
||||
if (enabled) {
|
||||
serverDirView->refreshTree();
|
||||
@@ -480,13 +512,108 @@ void TabReplays::deleteRemoteReplayFinished(const Response &r, const CommandCont
|
||||
serverDirView->removeMatchInfo(cmd.game_id());
|
||||
}
|
||||
|
||||
void TabReplays::actGetReplayCode()
|
||||
{
|
||||
const auto curRights = serverDirView->getSelectedReplayMatches();
|
||||
if (curRights.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (const auto curRight : curRights) {
|
||||
Command_ReplayGetCode cmd;
|
||||
cmd.set_game_id(curRight->game_id());
|
||||
|
||||
PendingCommand *pend = client->prepareSessionCommand(cmd);
|
||||
connect(pend, &PendingCommand::finished, this, &TabReplays::getReplayCodeFinished);
|
||||
client->sendCommand(pend);
|
||||
}
|
||||
}
|
||||
|
||||
void TabReplays::getReplayCodeFinished(const Response &r, const CommandContainer & /*commandContainer*/)
|
||||
{
|
||||
if (r.response_code() == Response::RespFunctionNotAllowed) {
|
||||
QMessageBox msgBox;
|
||||
msgBox.setIcon(QMessageBox::Warning);
|
||||
msgBox.setText(tr("Failed to get code"));
|
||||
msgBox.setInformativeText(
|
||||
tr("Either this server does not support replay sharing, or does not permit replay sharing for you."));
|
||||
msgBox.exec();
|
||||
return;
|
||||
}
|
||||
|
||||
if (r.response_code() != Response::RespOk) {
|
||||
QMessageBox::warning(this, tr("Failed"), tr("Could not get replay code"));
|
||||
return;
|
||||
}
|
||||
|
||||
const Response_ReplayGetCode &resp = r.GetExtension(Response_ReplayGetCode::ext);
|
||||
QString code = QString::fromStdString(resp.replay_code());
|
||||
|
||||
QMessageBox msgBox;
|
||||
msgBox.setText(tr("Replay Share Code"));
|
||||
msgBox.setInformativeText(
|
||||
tr("Others can use this code to add the replay to their list of remote replays:\n%1").arg(code));
|
||||
msgBox.setStandardButtons(QMessageBox::Ok);
|
||||
QPushButton *copyToClipboardButton = msgBox.addButton(tr("Copy to clipboard"), QMessageBox::ActionRole);
|
||||
connect(copyToClipboardButton, &QPushButton::clicked, this, [code] { QApplication::clipboard()->setText(code); });
|
||||
msgBox.setDefaultButton(copyToClipboardButton);
|
||||
msgBox.exec();
|
||||
}
|
||||
|
||||
void TabReplays::actSubmitReplayCode()
|
||||
{
|
||||
bool ok;
|
||||
QString code = QInputDialog::getText(this, tr("Look up replay by share code"), tr("Replay share code"),
|
||||
QLineEdit::Normal, "", &ok);
|
||||
|
||||
if (!ok) {
|
||||
return;
|
||||
}
|
||||
|
||||
Command_ReplaySubmitCode cmd;
|
||||
cmd.set_replay_code(code.toStdString());
|
||||
|
||||
PendingCommand *pend = client->prepareSessionCommand(cmd);
|
||||
connect(pend, &PendingCommand::finished, this, &TabReplays::submitReplayCodeFinished);
|
||||
client->sendCommand(pend);
|
||||
}
|
||||
|
||||
void TabReplays::submitReplayCodeFinished(const Response &r, const CommandContainer & /*commandContainer*/)
|
||||
{
|
||||
switch (r.response_code()) {
|
||||
case Response::RespOk: {
|
||||
QMessageBox msgBox;
|
||||
msgBox.setIcon(QMessageBox::Information);
|
||||
msgBox.setText(tr("Replay code found"));
|
||||
msgBox.setInformativeText(tr("Replay was added, or you already had access to it."));
|
||||
msgBox.exec();
|
||||
break;
|
||||
}
|
||||
case Response::RespNameNotFound:
|
||||
QMessageBox::warning(this, tr("Failed"), tr("Replay code not found"));
|
||||
break;
|
||||
case Response::RespFunctionNotAllowed: {
|
||||
QMessageBox msgBox;
|
||||
msgBox.setIcon(QMessageBox::Warning);
|
||||
msgBox.setText(tr("Failed to submit code"));
|
||||
msgBox.setInformativeText(
|
||||
tr("Either this server does not support replay sharing, or does not permit replay sharing for you."));
|
||||
msgBox.exec();
|
||||
break;
|
||||
}
|
||||
default:
|
||||
QMessageBox::warning(this, tr("Failed"), tr("Unexpected error"));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void TabReplays::replayAddedEventReceived(const Event_ReplayAdded &event)
|
||||
{
|
||||
if (event.has_match_info()) {
|
||||
// 99.9% of events will have match info (Normal Workflow)
|
||||
serverDirView->addMatchInfo(event.match_info());
|
||||
} else {
|
||||
// When a Moderator force adds a replay, we need to refresh their view
|
||||
// When a Moderator force adds a replay or a user submits a replay code, we need to refresh their view
|
||||
serverDirView->refreshTree();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,7 +28,8 @@ private:
|
||||
|
||||
QAction *aOpenLocalReplay, *aRenameLocal, *aNewLocalFolder, *aDeleteLocalReplay;
|
||||
QAction *aOpenReplaysFolder;
|
||||
QAction *aOpenRemoteReplay, *aDownload, *aKeep, *aDeleteRemoteReplay;
|
||||
QAction *aOpenRemoteReplay, *aDownload, *aKeep, *aDeleteRemoteReplay, *aGetReplayCode;
|
||||
QAction *aSubmitReplayCode;
|
||||
|
||||
QGroupBox *createLeftLayout();
|
||||
QGroupBox *createRightLayout();
|
||||
@@ -62,6 +63,12 @@ private slots:
|
||||
void actDeleteRemoteReplay();
|
||||
void deleteRemoteReplayFinished(const Response &r, const CommandContainer &commandContainer);
|
||||
|
||||
void actGetReplayCode();
|
||||
void getReplayCodeFinished(const Response &r, const CommandContainer &commandContainer);
|
||||
|
||||
void actSubmitReplayCode();
|
||||
void submitReplayCodeFinished(const Response &r, const CommandContainer &commandContainer);
|
||||
|
||||
void replayAddedEventReceived(const Event_ReplayAdded &event);
|
||||
signals:
|
||||
void openReplay(GameReplay *replay);
|
||||
|
||||
@@ -37,6 +37,8 @@ set(PROTO_FILES
|
||||
command_replay_list.proto
|
||||
command_replay_download.proto
|
||||
command_replay_modify_match.proto
|
||||
command_replay_get_code.proto
|
||||
command_replay_submit_code.proto
|
||||
command_reveal_cards.proto
|
||||
command_reverse_turn.proto
|
||||
command_roll_die.proto
|
||||
@@ -130,6 +132,7 @@ set(PROTO_FILES
|
||||
response_password_salt.proto
|
||||
response_register.proto
|
||||
response_replay_download.proto
|
||||
response_replay_get_code.proto
|
||||
response_replay_list.proto
|
||||
response_viewlog_history.proto
|
||||
response_warn_history.proto
|
||||
|
||||
9
common/pb/command_replay_get_code.proto
Normal file
9
common/pb/command_replay_get_code.proto
Normal file
@@ -0,0 +1,9 @@
|
||||
syntax = "proto2";
|
||||
import "session_commands.proto";
|
||||
|
||||
message Command_ReplayGetCode {
|
||||
extend SessionCommand {
|
||||
optional Command_ReplayGetCode ext = 1104;
|
||||
}
|
||||
optional sint32 game_id = 1 [default = -1];
|
||||
}
|
||||
9
common/pb/command_replay_submit_code.proto
Normal file
9
common/pb/command_replay_submit_code.proto
Normal file
@@ -0,0 +1,9 @@
|
||||
syntax = "proto2";
|
||||
import "session_commands.proto";
|
||||
|
||||
message Command_ReplaySubmitCode {
|
||||
extend SessionCommand {
|
||||
optional Command_ReplaySubmitCode ext = 1105;
|
||||
}
|
||||
optional string replay_code = 1;
|
||||
}
|
||||
@@ -65,6 +65,7 @@ message Response {
|
||||
GET_ADMIN_NOTES = 1018;
|
||||
REPLAY_LIST = 1100;
|
||||
REPLAY_DOWNLOAD = 1101;
|
||||
REPLAY_GET_CODE = 1102;
|
||||
}
|
||||
required uint64 cmd_id = 1;
|
||||
optional ResponseCode response_code = 2;
|
||||
|
||||
9
common/pb/response_replay_get_code.proto
Normal file
9
common/pb/response_replay_get_code.proto
Normal file
@@ -0,0 +1,9 @@
|
||||
syntax = "proto2";
|
||||
import "response.proto";
|
||||
|
||||
message Response_ReplayGetCode {
|
||||
extend Response {
|
||||
optional Response_ReplayGetCode ext = 1102;
|
||||
}
|
||||
optional string replay_code = 1;
|
||||
}
|
||||
@@ -31,6 +31,8 @@ message SessionCommand {
|
||||
REPLAY_DOWNLOAD = 1101;
|
||||
REPLAY_MODIFY_MATCH = 1102;
|
||||
REPLAY_DELETE_MATCH = 1103;
|
||||
REPLAY_GET_CODE = 1104;
|
||||
REPLAY_SUBMIT_CODE = 1105;
|
||||
}
|
||||
extensions 100 to max;
|
||||
}
|
||||
|
||||
@@ -31,13 +31,16 @@
|
||||
#include "pb/command_deck_upload.pb.h"
|
||||
#include "pb/command_replay_delete_match.pb.h"
|
||||
#include "pb/command_replay_download.pb.h"
|
||||
#include "pb/command_replay_get_code.pb.h"
|
||||
#include "pb/command_replay_list.pb.h"
|
||||
#include "pb/command_replay_modify_match.pb.h"
|
||||
#include "pb/command_replay_submit_code.pb.h"
|
||||
#include "pb/commands.pb.h"
|
||||
#include "pb/event_add_to_list.pb.h"
|
||||
#include "pb/event_connection_closed.pb.h"
|
||||
#include "pb/event_notify_user.pb.h"
|
||||
#include "pb/event_remove_from_list.pb.h"
|
||||
#include "pb/event_replay_added.pb.h"
|
||||
#include "pb/event_server_identification.pb.h"
|
||||
#include "pb/event_server_message.pb.h"
|
||||
#include "pb/event_user_message.pb.h"
|
||||
@@ -50,6 +53,7 @@
|
||||
#include "pb/response_password_salt.pb.h"
|
||||
#include "pb/response_register.pb.h"
|
||||
#include "pb/response_replay_download.pb.h"
|
||||
#include "pb/response_replay_get_code.pb.h"
|
||||
#include "pb/response_replay_list.pb.h"
|
||||
#include "pb/response_viewlog_history.pb.h"
|
||||
#include "pb/response_warn_history.pb.h"
|
||||
@@ -179,6 +183,10 @@ Response::ResponseCode AbstractServerSocketInterface::processExtendedSessionComm
|
||||
return cmdReplayModifyMatch(cmd.GetExtension(Command_ReplayModifyMatch::ext), rc);
|
||||
case SessionCommand::REPLAY_DELETE_MATCH:
|
||||
return cmdReplayDeleteMatch(cmd.GetExtension(Command_ReplayDeleteMatch::ext), rc);
|
||||
case SessionCommand::REPLAY_GET_CODE:
|
||||
return cmdReplayGetCode(cmd.GetExtension(Command_ReplayGetCode::ext), rc);
|
||||
case SessionCommand::REPLAY_SUBMIT_CODE:
|
||||
return cmdReplaySubmitCode(cmd.GetExtension(Command_ReplaySubmitCode::ext), rc);
|
||||
case SessionCommand::REGISTER:
|
||||
return cmdRegisterAccount(cmd.GetExtension(Command_Register::ext), rc);
|
||||
break;
|
||||
@@ -735,6 +743,135 @@ Response::ResponseCode AbstractServerSocketInterface::cmdReplayDeleteMatch(const
|
||||
return query->numRowsAffected() > 0 ? Response::RespOk : Response::RespNameNotFound;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a hash for the given replay folder, used for auth when replay sharing.
|
||||
* This is a separate function in case we change the hash implementation in the future.
|
||||
*
|
||||
* Currently, we append together the first 128 bytes of the first 3 replays in the game.
|
||||
* Then we md5 hash it, base64 encode it, and truncate the result to 10 characters.
|
||||
*
|
||||
* @param gameId The replay match to hash
|
||||
* @return The hash as a QString. Returns an empty string if failed
|
||||
*/
|
||||
QString AbstractServerSocketInterface::createHashForReplay(int gameId)
|
||||
{
|
||||
QSqlQuery *query =
|
||||
sqlInterface->prepareQuery("select replay from {prefix}_replays where id_game = :id_game limit 3");
|
||||
query->bindValue(":id_game", gameId);
|
||||
|
||||
if (!sqlInterface->execSqlQuery(query))
|
||||
return "";
|
||||
|
||||
QByteArray replaysBytes;
|
||||
while (query->next()) {
|
||||
QByteArray replay = query->value(0).toByteArray();
|
||||
replay.truncate(128);
|
||||
replaysBytes.append(replay);
|
||||
}
|
||||
|
||||
auto hash =
|
||||
QCryptographicHash::hash(replaysBytes, QCryptographicHash::Md5).toBase64(QByteArray::OmitTrailingEquals);
|
||||
hash.truncate(10);
|
||||
return hash;
|
||||
}
|
||||
|
||||
Response::ResponseCode AbstractServerSocketInterface::cmdReplayGetCode(const Command_ReplayGetCode &cmd,
|
||||
ResponseContainer &rc)
|
||||
{
|
||||
if (authState != PasswordRight)
|
||||
return Response::RespFunctionNotAllowed;
|
||||
|
||||
// Check that user has access to replay match
|
||||
{
|
||||
QSqlQuery *query = sqlInterface->prepareQuery(
|
||||
"select 1 from {prefix}_replays_access where id_game = :id_game and id_player = :id_player");
|
||||
query->bindValue(":id_game", cmd.game_id());
|
||||
query->bindValue(":id_player", userInfo->id());
|
||||
if (!sqlInterface->execSqlQuery(query))
|
||||
return Response::RespInternalError;
|
||||
if (!query->next())
|
||||
return Response::RespAccessDenied;
|
||||
}
|
||||
|
||||
QString hash = createHashForReplay(cmd.game_id());
|
||||
if (hash.isEmpty()) {
|
||||
return Response::RespInternalError;
|
||||
}
|
||||
|
||||
// code is of the form <game-id>-<hash>
|
||||
QString code = QString(QString::number(cmd.game_id()) + "-" + hash);
|
||||
|
||||
Response_ReplayGetCode *re = new Response_ReplayGetCode;
|
||||
re->set_replay_code(code.toStdString());
|
||||
rc.setResponseExtension(re);
|
||||
|
||||
return Response::RespOk;
|
||||
}
|
||||
|
||||
Response::ResponseCode AbstractServerSocketInterface::cmdReplaySubmitCode(const Command_ReplaySubmitCode &cmd,
|
||||
ResponseContainer & /*rc*/)
|
||||
{
|
||||
// code is of the form <game-id>-<hash>
|
||||
QString code = QString::fromStdString(cmd.replay_code());
|
||||
QStringList split = code.split("-");
|
||||
if (split.size() != 2) {
|
||||
// always return the same error response if code is incorrect, to not leak info to user
|
||||
return Response::RespNameNotFound;
|
||||
}
|
||||
QString gameId = split[0];
|
||||
QString hash = split[1];
|
||||
|
||||
// Determine if the replay actually exists (and grab the replay name while at it)
|
||||
auto *replayExistsQuery =
|
||||
sqlInterface->prepareQuery("select replay_name from {prefix}_replays_access where id_game = :id_game limit 1");
|
||||
replayExistsQuery->bindValue(":id_game", gameId);
|
||||
if (!sqlInterface->execSqlQuery(replayExistsQuery)) {
|
||||
return Response::RespInternalError;
|
||||
}
|
||||
if (!replayExistsQuery->next()) {
|
||||
return Response::RespNameNotFound;
|
||||
}
|
||||
|
||||
const auto &replayName = replayExistsQuery->value(0).toString();
|
||||
|
||||
// Check if hash is correct
|
||||
if (hash != createHashForReplay(gameId.toInt())) {
|
||||
return Response::RespNameNotFound;
|
||||
}
|
||||
|
||||
// Determine if user already has access to replay
|
||||
auto *alreadyAccessQuery = sqlInterface->prepareQuery(
|
||||
"select 1 from {prefix}_replays_access where id_game = :id_game and id_player = :id_player");
|
||||
alreadyAccessQuery->bindValue(":id_game", gameId);
|
||||
alreadyAccessQuery->bindValue(":id_player", userInfo->id());
|
||||
if (!sqlInterface->execSqlQuery(alreadyAccessQuery)) {
|
||||
return Response::RespInternalError;
|
||||
}
|
||||
if (alreadyAccessQuery->next()) {
|
||||
return Response::RespOk;
|
||||
}
|
||||
|
||||
// Grant the User access to the replay
|
||||
auto *grantReplayAccessQuery =
|
||||
sqlInterface->prepareQuery("insert into {prefix}_replays_access (id_game, id_player, replay_name, do_not_hide) "
|
||||
"values(:idgame, :idplayer, :replayname, 0)");
|
||||
grantReplayAccessQuery->bindValue(":idgame", gameId);
|
||||
grantReplayAccessQuery->bindValue(":idplayer", userInfo->id());
|
||||
grantReplayAccessQuery->bindValue(":replayname", replayName);
|
||||
|
||||
if (!sqlInterface->execSqlQuery(grantReplayAccessQuery)) {
|
||||
return Response::RespInternalError;
|
||||
}
|
||||
|
||||
// update user's view
|
||||
Event_ReplayAdded event;
|
||||
SessionEvent *se = prepareSessionEvent(event);
|
||||
sendProtocolItem(*se);
|
||||
delete se;
|
||||
|
||||
return Response::RespOk;
|
||||
}
|
||||
|
||||
// MODERATOR FUNCTIONS.
|
||||
// May be called by admins and moderators. Permission is checked by the calling function.
|
||||
Response::ResponseCode AbstractServerSocketInterface::cmdGetLogHistory(const Command_ViewLogHistory &cmd,
|
||||
|
||||
@@ -44,6 +44,8 @@ class Command_ReplayList;
|
||||
class Command_ReplayDownload;
|
||||
class Command_ReplayModifyMatch;
|
||||
class Command_ReplayDeleteMatch;
|
||||
class Command_ReplayGetCode;
|
||||
class Command_ReplaySubmitCode;
|
||||
|
||||
class Command_BanFromServer;
|
||||
class Command_UpdateServerMessage;
|
||||
@@ -97,6 +99,9 @@ private:
|
||||
Response::ResponseCode cmdReplayDownload(const Command_ReplayDownload &cmd, ResponseContainer &rc);
|
||||
Response::ResponseCode cmdReplayModifyMatch(const Command_ReplayModifyMatch &cmd, ResponseContainer &rc);
|
||||
Response::ResponseCode cmdReplayDeleteMatch(const Command_ReplayDeleteMatch &cmd, ResponseContainer &rc);
|
||||
QString createHashForReplay(int gameId);
|
||||
Response::ResponseCode cmdReplayGetCode(const Command_ReplayGetCode &cmd, ResponseContainer &rc);
|
||||
Response::ResponseCode cmdReplaySubmitCode(const Command_ReplaySubmitCode &cmd, ResponseContainer &rc);
|
||||
Response::ResponseCode cmdBanFromServer(const Command_BanFromServer &cmd, ResponseContainer &rc);
|
||||
Response::ResponseCode cmdWarnUser(const Command_WarnUser &cmd, ResponseContainer &rc);
|
||||
Response::ResponseCode cmdGetLogHistory(const Command_ViewLogHistory &cmd, ResponseContainer &rc);
|
||||
|
||||
Reference in New Issue
Block a user