mirror of
https://github.com/Cockatrice/Cockatrice.git
synced 2026-06-22 22:53:55 -07:00
[Room][UserList] Introduce style delegate for user list
- Allow users to set a card name and parameters as their background banner - Allow mods to white/blacklist cards - Allow toggling back to the old display style Took 7 minutes Took 28 seconds Took 2 minutes Took 2 minutes
This commit is contained in:
parent
bdb0f12f66
commit
aff93a4435
35 changed files with 1977 additions and 26 deletions
|
|
@ -7,6 +7,8 @@
|
|||
#include <QChar>
|
||||
#include <QDateTime>
|
||||
#include <QDebug>
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonObject>
|
||||
#include <QLoggingCategory>
|
||||
#include <QSqlError>
|
||||
#include <QSqlQuery>
|
||||
|
|
@ -681,6 +683,30 @@ ServerInfo_User Servatrice_DatabaseInterface::evalUserQueryResult(const QSqlQuer
|
|||
if (!clientid.isEmpty()) {
|
||||
result.set_clientid(clientid.toStdString());
|
||||
}
|
||||
|
||||
const QString cardArtParamsJson = query->value(12).toString();
|
||||
if (!cardArtParamsJson.isEmpty()) {
|
||||
const QJsonDocument doc = QJsonDocument::fromJson(cardArtParamsJson.toUtf8());
|
||||
if (doc.isObject()) {
|
||||
const QJsonObject obj = doc.object();
|
||||
auto *cap = result.mutable_card_art_params();
|
||||
if (obj.contains("card_name")) {
|
||||
cap->set_card_name(obj["card_name"].toString().toStdString());
|
||||
}
|
||||
if (obj.contains("marginPctL")) {
|
||||
cap->set_margin_pct_l(obj["marginPctL"].toDouble(0.33));
|
||||
}
|
||||
if (obj.contains("marginPctR")) {
|
||||
cap->set_margin_pct_r(obj["marginPctR"].toDouble(0.02));
|
||||
}
|
||||
if (obj.contains("verticalOffset")) {
|
||||
cap->set_vertical_offset(obj["verticalOffset"].toDouble(0.35));
|
||||
}
|
||||
if (obj.contains("zoom")) {
|
||||
cap->set_zoom(obj["zoom"].toDouble(1.0));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
|
@ -698,7 +724,7 @@ ServerInfo_User Servatrice_DatabaseInterface::getUserData(const QString &name, b
|
|||
|
||||
QSqlQuery *query = prepareQuery("select id, name, admin, country, privlevel, leftPawnColorOverride, "
|
||||
"rightPawnColorOverride, realname, avatar_bmp, registrationDate, "
|
||||
"email, clientid from {prefix}_users where "
|
||||
"email, clientid, card_art_params from {prefix}_users where "
|
||||
"name = :name and active = 1");
|
||||
query->bindValue(":name", name);
|
||||
if (!execSqlQuery(query)) {
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@
|
|||
#include <server.h>
|
||||
#include <server_database_interface.h>
|
||||
|
||||
#define DATABASE_SCHEMA_VERSION 34
|
||||
#define DATABASE_SCHEMA_VERSION 35
|
||||
|
||||
class Servatrice;
|
||||
|
||||
|
|
|
|||
|
|
@ -31,6 +31,8 @@
|
|||
#include <QDateTime>
|
||||
#include <QDebug>
|
||||
#include <QHostAddress>
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonObject>
|
||||
#include <QLoggingCategory>
|
||||
#include <QRegularExpression>
|
||||
#include <QSqlError>
|
||||
|
|
@ -59,8 +61,10 @@
|
|||
#include <libcockatrice/protocol/pb/event_replay_added.pb.h>
|
||||
#include <libcockatrice/protocol/pb/event_server_identification.pb.h>
|
||||
#include <libcockatrice/protocol/pb/event_server_message.pb.h>
|
||||
#include <libcockatrice/protocol/pb/event_user_joined.pb.h>
|
||||
#include <libcockatrice/protocol/pb/event_user_message.pb.h>
|
||||
#include <libcockatrice/protocol/pb/response_ban_history.pb.h>
|
||||
#include <libcockatrice/protocol/pb/response_card_art_rule_entry.pb.h>
|
||||
#include <libcockatrice/protocol/pb/response_deck_download.pb.h>
|
||||
#include <libcockatrice/protocol/pb/response_deck_list.pb.h>
|
||||
#include <libcockatrice/protocol/pb/response_deck_upload.pb.h>
|
||||
|
|
@ -212,6 +216,8 @@ Response::ResponseCode AbstractServerSocketInterface::processExtendedSessionComm
|
|||
return cmdAccountEdit(cmd.GetExtension(Command_AccountEdit::ext), rc);
|
||||
case SessionCommand::ACCOUNT_IMAGE:
|
||||
return cmdAccountImage(cmd.GetExtension(Command_AccountImage::ext), rc);
|
||||
case SessionCommand::SET_CARD_ART_PARAMS:
|
||||
return cmdSetCardArtParams(cmd.GetExtension(Command_SetCardArtParams::ext), rc);
|
||||
case SessionCommand::ACCOUNT_PASSWORD:
|
||||
return cmdAccountPassword(cmd.GetExtension(Command_AccountPassword::ext), rc);
|
||||
case SessionCommand::REQUEST_PASSWORD_SALT:
|
||||
|
|
@ -247,6 +253,12 @@ Response::ResponseCode AbstractServerSocketInterface::processExtendedModeratorCo
|
|||
return cmdGetAdminNotes(cmd.GetExtension(Command_GetAdminNotes::ext), rc);
|
||||
case ModeratorCommand::UPDATE_ADMIN_NOTES:
|
||||
return cmdUpdateAdminNotes(cmd.GetExtension(Command_UpdateAdminNotes::ext), rc);
|
||||
case ModeratorCommand::ADD_CARD_ART_RULE:
|
||||
return cmdAddCardArtRule(cmd.GetExtension((Command_AddCardArtRule::ext)), rc);
|
||||
case ModeratorCommand::REMOVE_CARD_ART_RULE:
|
||||
return cmdRemoveCardArtRule(cmd.GetExtension((Command_RemoveCardArtRule::ext)), rc);
|
||||
case ModeratorCommand::LIST_CARD_ART_RULES:
|
||||
return cmdListCardArtRules(cmd.GetExtension((Command_ListCardArtRules::ext)), rc);
|
||||
default:
|
||||
return Response::RespFunctionNotAllowed;
|
||||
}
|
||||
|
|
@ -1565,6 +1577,146 @@ Response::ResponseCode AbstractServerSocketInterface::cmdAccountImage(const Comm
|
|||
return Response::RespOk;
|
||||
}
|
||||
|
||||
bool AbstractServerSocketInterface::isCardNameAllowed(const QString &cardName)
|
||||
{
|
||||
QSqlQuery *q =
|
||||
sqlInterface->prepareQuery("SELECT mode FROM cockatrice_card_art_name_rules WHERE card_name = :name");
|
||||
|
||||
q->bindValue(":name", cardName);
|
||||
|
||||
if (!sqlInterface->execSqlQuery(q)) {
|
||||
return true; // fail-open to avoid breaking server
|
||||
}
|
||||
|
||||
if (!q->next()) {
|
||||
return true; // default allow
|
||||
}
|
||||
|
||||
return q->value(0).toString() != "DENY";
|
||||
}
|
||||
|
||||
Response::ResponseCode AbstractServerSocketInterface::cmdSetCardArtParams(const Command_SetCardArtParams &cmd,
|
||||
ResponseContainer & /* rc */)
|
||||
{
|
||||
if (authState != PasswordRight) {
|
||||
return Response::RespFunctionNotAllowed;
|
||||
}
|
||||
|
||||
const QString cardName = QString::fromStdString(cmd.card_name());
|
||||
|
||||
if (cardName.isEmpty()) {
|
||||
// Removal path
|
||||
QSqlQuery *q = sqlInterface->prepareQuery("UPDATE {prefix}_users SET card_art_params = NULL WHERE id = :id");
|
||||
q->bindValue(":id", userInfo->id());
|
||||
if (!sqlInterface->execSqlQuery(q)) {
|
||||
return Response::RespInternalError;
|
||||
}
|
||||
userInfo->clear_card_art_params();
|
||||
server->broadcastUserInfoUpdate(this);
|
||||
return Response::RespOk;
|
||||
}
|
||||
|
||||
if (!isCardNameAllowed(cardName)) {
|
||||
return Response::RespFunctionNotAllowed;
|
||||
}
|
||||
|
||||
// Clamp everything to sane ranges server-side so a malicious client
|
||||
// can't store garbage that breaks other clients' rendering.
|
||||
const double marginPctL = qBound(0.0, cmd.margin_pct_l(), 0.95);
|
||||
const double marginPctR = qBound(0.0, cmd.margin_pct_r(), 0.95);
|
||||
const double verticalOffset = qBound(0.0, cmd.vertical_offset(), 1.0);
|
||||
const double zoom = qBound(0.1, cmd.zoom(), 4.0);
|
||||
|
||||
QJsonObject obj;
|
||||
obj["card_name"] = cardName;
|
||||
obj["marginPctL"] = marginPctL;
|
||||
obj["marginPctR"] = marginPctR;
|
||||
obj["verticalOffset"] = verticalOffset;
|
||||
obj["zoom"] = zoom;
|
||||
const QString json = QString::fromUtf8(QJsonDocument(obj).toJson(QJsonDocument::Compact));
|
||||
|
||||
QSqlQuery *query = sqlInterface->prepareQuery("update {prefix}_users set card_art_params=:params where id=:id");
|
||||
query->bindValue(":params", json);
|
||||
query->bindValue(":id", userInfo->id());
|
||||
if (!sqlInterface->execSqlQuery(query)) {
|
||||
return Response::RespInternalError;
|
||||
}
|
||||
|
||||
// Keep the in-memory userInfo in sync
|
||||
auto *cap = userInfo->mutable_card_art_params();
|
||||
cap->set_card_name(cmd.card_name());
|
||||
cap->set_margin_pct_l(marginPctL);
|
||||
cap->set_margin_pct_r(marginPctR);
|
||||
cap->set_vertical_offset(verticalOffset);
|
||||
cap->set_zoom(zoom);
|
||||
|
||||
const QString name = QString::fromStdString(userInfo->name());
|
||||
server->broadcastUserInfoUpdate(this);
|
||||
|
||||
return Response::RespOk;
|
||||
}
|
||||
|
||||
Response::ResponseCode AbstractServerSocketInterface::cmdAddCardArtRule(const Command_AddCardArtRule &cmd,
|
||||
ResponseContainer &)
|
||||
{
|
||||
const QString cardName = QString::fromStdString(cmd.card_name());
|
||||
const QString mode = QString::fromStdString(cmd.mode());
|
||||
|
||||
QSqlQuery *q = sqlInterface->prepareQuery("INSERT INTO cockatrice_card_art_name_rules "
|
||||
"(card_name, mode, reason, created_by) "
|
||||
"VALUES (:name, :mode, :reason, :uid) "
|
||||
"ON DUPLICATE KEY UPDATE mode=:mode2, reason=:reason2");
|
||||
|
||||
q->bindValue(":name", cardName);
|
||||
q->bindValue(":mode", mode);
|
||||
q->bindValue(":mode2", mode);
|
||||
q->bindValue(":reason", QString::fromStdString(cmd.reason()));
|
||||
q->bindValue(":reason2", QString::fromStdString(cmd.reason()));
|
||||
q->bindValue(":uid", userInfo->id());
|
||||
|
||||
if (!sqlInterface->execSqlQuery(q)) {
|
||||
return Response::RespInternalError;
|
||||
}
|
||||
|
||||
return Response::RespOk;
|
||||
}
|
||||
|
||||
Response::ResponseCode AbstractServerSocketInterface::cmdRemoveCardArtRule(const Command_RemoveCardArtRule &cmd,
|
||||
ResponseContainer &)
|
||||
{
|
||||
QSqlQuery *q = sqlInterface->prepareQuery("DELETE FROM cockatrice_card_art_name_rules WHERE card_name=:name");
|
||||
|
||||
q->bindValue(":name", QString::fromStdString(cmd.card_name()));
|
||||
|
||||
if (!sqlInterface->execSqlQuery(q)) {
|
||||
return Response::RespInternalError;
|
||||
}
|
||||
|
||||
return Response::RespOk;
|
||||
}
|
||||
|
||||
Response::ResponseCode AbstractServerSocketInterface::cmdListCardArtRules(const Command_ListCardArtRules &,
|
||||
ResponseContainer &rc)
|
||||
{
|
||||
QSqlQuery *q = sqlInterface->prepareQuery("SELECT card_name, mode, reason FROM cockatrice_card_art_name_rules");
|
||||
|
||||
if (!sqlInterface->execSqlQuery(q)) {
|
||||
return Response::RespInternalError;
|
||||
}
|
||||
|
||||
auto *re = new Response_ListCardArtRules;
|
||||
|
||||
while (q->next()) {
|
||||
auto *entry = re->add_entries();
|
||||
entry->set_card_name(q->value(0).toString().toStdString());
|
||||
entry->set_mode(q->value(1).toString().toStdString());
|
||||
entry->set_reason(q->value(2).toString().toStdString());
|
||||
}
|
||||
|
||||
rc.setResponseExtension(re);
|
||||
return Response::RespOk;
|
||||
}
|
||||
|
||||
Response::ResponseCode AbstractServerSocketInterface::cmdAccountPassword(const Command_AccountPassword &cmd,
|
||||
ResponseContainer & /* rc */)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -129,6 +129,11 @@ private:
|
|||
|
||||
Response::ResponseCode cmdAccountEdit(const Command_AccountEdit &cmd, ResponseContainer &rc);
|
||||
Response::ResponseCode cmdAccountImage(const Command_AccountImage &cmd, ResponseContainer &rc);
|
||||
bool isCardNameAllowed(const QString &cardName);
|
||||
Response::ResponseCode cmdSetCardArtParams(const Command_SetCardArtParams &cmd, ResponseContainer &);
|
||||
Response::ResponseCode cmdAddCardArtRule(const Command_AddCardArtRule &cmd, ResponseContainer &);
|
||||
Response::ResponseCode cmdRemoveCardArtRule(const Command_RemoveCardArtRule &cmd, ResponseContainer &);
|
||||
Response::ResponseCode cmdListCardArtRules(const Command_ListCardArtRules &, ResponseContainer &rc);
|
||||
Response::ResponseCode cmdAccountPassword(const Command_AccountPassword &cmd, ResponseContainer &rc);
|
||||
Response::ResponseCode cmdGrantReplayAccess(const Command_GrantReplayAccess &cmd, ResponseContainer &rc);
|
||||
Response::ResponseCode cmdForceActivateUser(const Command_ForceActivateUser &cmd, ResponseContainer &rc);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue