diff --git a/lua/server/request.lua b/lua/server/request.lua index ef99949d..335d03ee 100644 --- a/lua/server/request.lua +++ b/lua/server/request.lua @@ -36,7 +36,7 @@ local function tellRoomToObserver(self, player) player:doNotify("UpdateDrawPile", #self.draw_pile) player:doNotify("UpdateRoundNum", self:getTag("RoundCount") or 0) - table.insert(self.observers, {observee.id, player}) + table.insert(self.observers, {observee.id, player, player:getId()}) end local function addObserver(self, id) @@ -56,12 +56,10 @@ end local function removeObserver(self, id) for _, t in ipairs(self.observers) do - local __, p = table.unpack(t) - if p:getId() == id then + local pid = t[3] + if pid == id then table.removeOne(self.observers, t) - self:doBroadcastNotify("RemoveObserver", json.encode{ - p:getId(), - }) + self:doBroadcastNotify("RemoveObserver", json.encode{ pid }) break end end diff --git a/lua/server/serverplayer.lua b/lua/server/serverplayer.lua index 12b688af..028934da 100644 --- a/lua/server/serverplayer.lua +++ b/lua/server/serverplayer.lua @@ -47,7 +47,7 @@ function ServerPlayer:doNotify(command, jsonData) local room = self.room for _, t in ipairs(room.observers) do local id, p = table.unpack(t) - if id == self.id then + if id == self.id and room.room:hasObserver(p) then p:doNotify(command, jsonData) end end diff --git a/server/init.sql b/server/init.sql index 6072d2ab..b7d86b84 100644 --- a/server/init.sql +++ b/server/init.sql @@ -16,6 +16,15 @@ CREATE TABLE IF NOT EXISTS banip ( ip VARCHAR(64) ); +CREATE TABLE IF NOT EXISTS uuidinfo ( + id INTEGER PRIMARY KEY, + uuid VARCHAR(32) +); + +CREATE TABLE IF NOT EXISTS banuuid ( + uuid VARCHAR(32) +); + -- 胜率相关 CREATE TABLE IF NOT EXISTS winRate ( diff --git a/src/core/util.cpp b/src/core/util.cpp index ed679644..d4e7a1c9 100644 --- a/src/core/util.cpp +++ b/src/core/util.cpp @@ -4,6 +4,7 @@ #include #include #include +#include extern "C" { int luaopen_fk(lua_State *); @@ -203,6 +204,10 @@ QJsonDocument String2Json(const QString &str) { return QJsonDocument::fromJson(bytes); } +QString GetDeviceUuid() { + return QSysInfo::machineUniqueId(); +} + QString Color(const QString &raw, fkShell::TextColor color, fkShell::TextType type) { #ifdef Q_OS_LINUX diff --git a/src/core/util.h b/src/core/util.h index 1a18ad60..72daff01 100644 --- a/src/core/util.h +++ b/src/core/util.h @@ -20,6 +20,8 @@ QString calcFileMD5(); QByteArray JsonArray2Bytes(const QJsonArray &arr); QJsonDocument String2Json(const QString &str); +QString GetDeviceUuid(); + namespace fkShell { enum TextColor { Black, diff --git a/src/server/room.cpp b/src/server/room.cpp index b9f0e2f9..b859b93d 100644 --- a/src/server/room.cpp +++ b/src/server/room.cpp @@ -5,7 +5,12 @@ #include #include +#ifdef FK_SERVER_ONLY +static void *ClientInstance = nullptr; +#else #include "client.h" +#endif + #include "client_socket.h" #include "roomthread.h" #include "server.h" @@ -268,15 +273,15 @@ void Room::removePlayer(ServerPlayer *player) { // 原先的跑路机器人会在游戏结束后自动销毁掉 server->addPlayer(runner); - // 如果走小道的人不是单机启动玩家 那么直接ban - if (!ClientInstance && !player->isDied()) { - server->temporarilyBan(runner->getId()); - } - m_thread->wakeUp(); // 发出信号,让大厅添加这个人 emit playerRemoved(runner); + + // 如果走小道的人不是单机启动玩家 那么直接ban + if (!ClientInstance && !player->isDied()) { + server->temporarilyBan(runner->getId()); + } } // 如果房间空了,就把房间标为废弃,Server有信号处理函数的 @@ -341,6 +346,8 @@ void Room::removeObserver(ServerPlayer *player) { QList Room::getObservers() const { return observers; } +bool Room::hasObserver(ServerPlayer *player) const { return observers.contains(player); } + int Room::getTimeout() const { return timeout; } void Room::setTimeout(int timeout) { this->timeout = timeout; } diff --git a/src/server/room.h b/src/server/room.h index 424a9699..ad7a4b90 100644 --- a/src/server/room.h +++ b/src/server/room.h @@ -45,6 +45,7 @@ class Room : public QObject { void addObserver(ServerPlayer *player); void removeObserver(ServerPlayer *player); QList getObservers() const; + bool hasObserver(ServerPlayer *player) const; int getTimeout() const; void setTimeout(int timeout); diff --git a/src/server/server.cpp b/src/server/server.cpp index a02b4631..748f8836 100644 --- a/src/server/server.cpp +++ b/src/server/server.cpp @@ -273,7 +273,7 @@ void Server::processRequest(const QByteArray &msg) { doc[2] != "Setup") valid = false; else - valid = (String2Json(doc[3].toString()).array().size() == 4); + valid = (String2Json(doc[3].toString()).array().size() == 5); } if (!valid) { @@ -313,13 +313,34 @@ void Server::processRequest(const QByteArray &msg) { return; } + auto uuid = arr[4].toString(); + auto result2 = QJsonArray({1}); + if (CheckSqlString(uuid)) { + result2 = SelectFromDatabase( + db, QString("SELECT * FROM banuuid WHERE uuid='%1';").arg(uuid)); + } + + if (!result2.isEmpty()) { + QJsonArray body; + body << -2; + body << (Router::TYPE_NOTIFICATION | Router::SRC_SERVER | + Router::DEST_CLIENT); + body << "ErrorMsg"; + body << "you have been banned!"; + client->send(JsonArray2Bytes(body)); + qInfo() << "Refused banned UUID:" << uuid; + client->disconnectFromHost(); + return; + } + handleNameAndPassword(client, arr[0].toString(), arr[1].toString(), - arr[2].toString()); + arr[2].toString(), uuid); } void Server::handleNameAndPassword(ClientSocket *client, const QString &name, const QString &password, - const QString &md5_str) { + const QString &md5_str, + const QString &uuid_str) { auto encryted_pw = QByteArray::fromBase64(password.toLatin1()); unsigned char buf[4096] = {0}; RSA_private_decrypt(RSA_size(rsa), (const unsigned char *)encryted_pw.data(), @@ -462,6 +483,9 @@ void Server::handleNameAndPassword(ClientSocket *client, const QString &name, .arg(obj["id"].toString().toInt()); ExecSQL(db, sql_update); + auto uuid_update = QString("REPLACE INTO uuidinfo (id, uuid) VALUES (%1, '%2');").arg(obj["id"].toString().toInt()).arg(uuid_str); + ExecSQL(db, uuid_update); + // create new ServerPlayer and setup ServerPlayer *player = new ServerPlayer(lobby()); player->setSocket(client); diff --git a/src/server/server.h b/src/server/server.h index 02196c64..2e3ddd19 100644 --- a/src/server/server.h +++ b/src/server/server.h @@ -86,7 +86,7 @@ private: void readConfig(); void handleNameAndPassword(ClientSocket *client, const QString &name, - const QString &password, const QString &md5_str); + const QString &password, const QString &md5_str, const QString &uuid_str); void processDatagram(const QByteArray &msg, const QHostAddress &addr, uint port); }; diff --git a/src/server/shell.cpp b/src/server/shell.cpp index ae322669..3724f132 100644 --- a/src/server/shell.cpp +++ b/src/server/shell.cpp @@ -29,16 +29,24 @@ void Shell::helpCommand(QStringList &) { HELP_MSG("%s: List all running rooms.", "lsroom"); HELP_MSG("%s: Kick a player by his .", "kick"); HELP_MSG("%s: Broadcast message.", "msg"); - HELP_MSG("%s: Ban 1 or more accounts by their .", "ban"); + HELP_MSG("%s: Ban 1 or more accounts, IP, UUID by their .", "ban"); HELP_MSG("%s: Unban 1 or more accounts by their .", "unban"); HELP_MSG( - "%s: Ban 1 or more IP address according to somebody's 'lastLoginIp'. " + "%s: Ban 1 or more IP address. " "At least 1 required.", "banip"); HELP_MSG( - "%s: Unban 1 or more IP address according to somebody's 'lastLoginIp'. " + "%s: Unban 1 or more IP address. " "At least 1 required.", "unbanip"); + HELP_MSG( + "%s: Ban 1 or more UUID. " + "At least 1 required.", + "banuuid"); + HELP_MSG( + "%s: Unban 1 or more UUID. " + "At least 1 required.", + "unbanuuid"); qInfo(); qInfo("===== Package commands ====="); HELP_MSG("%s: Install a new package from .", "install"); @@ -207,6 +215,9 @@ void Shell::banCommand(QStringList &list) { foreach (auto name, list) { banAccount(db, name, true); } + + banipCommand(list); + banUuidCommand(list); } void Shell::unbanCommand(QStringList &list) { @@ -220,7 +231,11 @@ void Shell::unbanCommand(QStringList &list) { foreach (auto name, list) { banAccount(db, name, false); } + + unbanipCommand(list); + unbanUuidCommand(list); } + static void banIPByName(sqlite3 *db, const QString &name, bool banned) { if (!CheckSqlString(name)) return; @@ -275,6 +290,65 @@ void Shell::unbanipCommand(QStringList &list) { } } +static void banUuidByName(sqlite3 *db, const QString &name, bool banned) { + if (!CheckSqlString(name)) + return; + + QString sql_find = QString("SELECT * FROM userinfo \ + WHERE name='%1';") + .arg(name); + auto result = SelectFromDatabase(db, sql_find); + if (result.isEmpty()) + return; + auto obj = result[0].toObject(); + int id = obj["id"].toString().toInt(); + + auto result2 = SelectFromDatabase(db, QString("SELECT * FROM uuidinfo WHERE id=%1;").arg(id)); + if (result2.isEmpty()) + return; + + auto uuid = result2[0].toObject()["uuid"].toString(); + + if (banned) { + ExecSQL(db, QString("INSERT INTO banuuid VALUES('%1');").arg(uuid)); + + auto p = ServerInstance->findPlayer(id); + if (p) { + p->kicked(); + } + qInfo("Banned UUID %s.", uuid.toUtf8().constData()); + } else { + ExecSQL(db, QString("DELETE FROM banuuid WHERE uuid='%1';").arg(uuid)); + qInfo("Unbanned UUID %s.", uuid.toUtf8().constData()); + } +} + +void Shell::banUuidCommand(QStringList &list) { + if (list.isEmpty()) { + qWarning("The 'banuuid' command needs at least 1 ."); + return; + } + + auto db = ServerInstance->getDatabase(); + + foreach (auto name, list) { + banUuidByName(db, name, true); + } +} + +void Shell::unbanUuidCommand(QStringList &list) { + if (list.isEmpty()) { + qWarning("The 'unbanuuid' command needs at least 1 ."); + return; + } + + auto db = ServerInstance->getDatabase(); + + foreach (auto name, list) { + banUuidByName(db, name, false); + } +} + Shell::Shell() { setObjectName("Shell"); signal(SIGINT, sigintHandler); @@ -297,6 +371,8 @@ Shell::Shell() { handlers["unban"] = &Shell::unbanCommand; handlers["banip"] = &Shell::banipCommand; handlers["unbanip"] = &Shell::unbanipCommand; + handlers["banuuid"] = &Shell::banUuidCommand; + handlers["unbanuuid"] = &Shell::unbanUuidCommand; } handler_map = handlers; } diff --git a/src/server/shell.h b/src/server/shell.h index 0e4b1564..a1c0b8dc 100644 --- a/src/server/shell.h +++ b/src/server/shell.h @@ -27,8 +27,10 @@ private: void msgCommand(QStringList &); void banCommand(QStringList &); void banipCommand(QStringList &); + void banUuidCommand(QStringList &); void unbanCommand(QStringList &); void unbanipCommand(QStringList &); + void unbanUuidCommand(QStringList &); }; #endif diff --git a/src/swig/server.i b/src/swig/server.i index edd4775e..f352d95d 100644 --- a/src/swig/server.i +++ b/src/swig/server.i @@ -11,6 +11,7 @@ public: QList getPlayers() const; QList getObservers() const; + bool hasObserver(ServerPlayer *player) const; int getTimeout() const; void checkAbandoned(); diff --git a/src/ui/qmlbackend.cpp b/src/ui/qmlbackend.cpp index a75809ce..ebe45d1b 100644 --- a/src/ui/qmlbackend.cpp +++ b/src/ui/qmlbackend.cpp @@ -270,7 +270,7 @@ void QmlBackend::replyDelayTest(const QString &screenName, auto md5 = calcFileMD5(); QJsonArray arr; - arr << screenName << cipher << md5 << FK_VERSION; + arr << screenName << cipher << md5 << FK_VERSION << GetDeviceUuid(); ClientInstance->notifyServer("Setup", JsonArray2Bytes(arr)); }