diff --git a/Fk/Cheat/PlayerDetail.qml b/Fk/Cheat/PlayerDetail.qml index c649ec21..bd44565b 100644 --- a/Fk/Cheat/PlayerDetail.qml +++ b/Fk/Cheat/PlayerDetail.qml @@ -62,10 +62,18 @@ Flickable { } // TODO: player details - Text { - id: screenName - Layout.fillWidth: true - font.pixelSize: 18 + RowLayout { + spacing: 16 + Text { + id: screenName + font.pixelSize: 18 + } + + Text { + id: playerGameData + Layout.fillWidth: true + font.pixelSize: 18 + } } TextEdit { @@ -95,6 +103,7 @@ Flickable { onExtra_dataChanged: { if (!extra_data.photo) return; screenName.text = ""; + playerGameData.text = ""; skillDesc.text = ""; const id = extra_data.photo.playerid; @@ -103,6 +112,15 @@ Flickable { screenName.text = extra_data.photo.screenName; + const gamedata = JSON.parse(Backend.callLuaFunction("GetPlayerGameData", [id])); + const total = gamedata[0]; + const win = gamedata[1]; + const run = gamedata[2]; + const winRate = (win / total) * 100; + const runRate = (run / total) * 100; + playerGameData.text = Backend.translate("Win=%1 Run=%2 Total=%3").arg(winRate.toFixed(2)) + .arg(runRate.toFixed(2)).arg(total); + const data = JSON.parse(Backend.callLuaFunction("GetPlayerSkills", [id])); data.forEach(t => { skillDesc.append("" + Backend.translate(t.name) + ": " + t.description) diff --git a/lua/client/client.lua b/lua/client/client.lua index c6f43608..9d811b7c 100644 --- a/lua/client/client.lua +++ b/lua/client/client.lua @@ -51,6 +51,7 @@ end ---@param id integer ---@return ClientPlayer function Client:getPlayerById(id) + if id == Self.id then return Self end for _, p in ipairs(self.players) do if p.id == id then return p end end @@ -248,7 +249,7 @@ fk.client_callback["AddPlayer"] = function(jsonData) -- jsonData: [ int id, string screenName, string avatar ] -- when other player enter the room, we create clientplayer(C and lua) for them local data = json.decode(jsonData) - local id, name, avatar = data[1], data[2], data[3] + local id, name, avatar, gameData = data[1], data[2], data[3] local player = fk.ClientInstance:addPlayer(id, name, avatar) local p = ClientPlayer:new(player) table.insert(ClientInstance.players, p) @@ -763,6 +764,15 @@ fk.client_callback["UpdateQuestSkillUI"] = function(jsonData) updateLimitSkill(player, Fk.skills[skillName], usedTimes) end +fk.client_callback["UpdateGameData"] = function(jsonData) + local data = json.decode(jsonData) + local player, total, win, run = data[1], data[2], data[3], data[4] + player = ClientInstance:getPlayerById(player) + if player then + player.player:setGameData(total, win, run) + end +end + -- Create ClientInstance (used by Lua) ClientInstance = Client:new() dofile "lua/client/client_util.lua" diff --git a/lua/client/client_util.lua b/lua/client/client_util.lua index fc14e5d4..99d4ffca 100644 --- a/lua/client/client_util.lua +++ b/lua/client/client_util.lua @@ -573,4 +573,15 @@ function GetRoomConfig() return json.encode(ClientInstance.room_settings) end +function GetPlayerGameData(pid) + local c = ClientInstance + local p = c:getPlayerById(pid) + local raw = p.player:getGameData() + local ret = {} + for _, i in fk.qlist(raw) do + table.insert(ret, i) + end + return json.encode(ret) +end + dofile "lua/client/i18n/init.lua" diff --git a/lua/client/i18n/zh_CN.lua b/lua/client/i18n/zh_CN.lua index d101e66f..7b4beddd 100644 --- a/lua/client/i18n/zh_CN.lua +++ b/lua/client/i18n/zh_CN.lua @@ -53,6 +53,7 @@ Fk:loadTranslationTable{ ["Give Egg"] = "砸蛋", ["Give Shoe"] = "拖鞋", ["Kick From Room"] = "踢出房间", + ["Win=%1 Run=%2 Total=%3"] = "胜率%1% 逃率%2% 总场次%3", ["$OnlineInfo"] = "大厅人数:%1,总在线人数:%2", diff --git a/lua/server/events/death.lua b/lua/server/events/death.lua index 752ed6a9..d8e8aa98 100644 --- a/lua/server/events/death.lua +++ b/lua/server/events/death.lua @@ -35,6 +35,7 @@ GameEvent.functions[GameEvent.Death] = function(self) local self = self.room local victim = self:getPlayerById(deathStruct.who) victim.dead = true + victim._splayer:setDied(true) table.removeOne(self.alive_players, victim) local logic = self.logic diff --git a/lua/server/room.lua b/lua/server/room.lua index 6868b021..c0b43bfc 100644 --- a/lua/server/room.lua +++ b/lua/server/room.lua @@ -2818,6 +2818,7 @@ end function Room:revivePlayer(player, sendLog) if not player.dead then return end self:setPlayerProperty(player, "dead", false) + player._splayer:setDied(false) self:setPlayerProperty(player, "dying", false) self:setPlayerProperty(player, "hp", player.maxHp) table.insertIfNeed(self.alive_players, player) @@ -2862,11 +2863,11 @@ function Room:gameOver(winner) if p.id > 0 then if table.contains(winner:split("+"), p.role) then - self.room:updateWinRate(id, general, mode, 1) + self.room:updateWinRate(id, general, mode, 1, p.dead) elseif winner == "" then - self.room:updateWinRate(id, general, mode, 3) + self.room:updateWinRate(id, general, mode, 3, p.dead) else - self.room:updateWinRate(id, general, mode, 2) + self.room:updateWinRate(id, general, mode, 2, p.dead) end end end diff --git a/lua/server/serverplayer.lua b/lua/server/serverplayer.lua index 3f8f192d..c663c69f 100644 --- a/lua/server/serverplayer.lua +++ b/lua/server/serverplayer.lua @@ -175,11 +175,6 @@ function ServerPlayer:marshal(player) room:notifyProperty(player, self, "gender") room:notifyProperty(player, self, "kingdom") - if self.kingdom ~= Fk.generals[self.general].kingdom then - self.kingdom = Fk.generals[self.general].kingdom - room:notifyProperty(player, self, "kingdom") - end - if self.dead then room:notifyProperty(player, self, "dead") room:notifyProperty(player, self, "role") diff --git a/server/init.sql b/server/init.sql index b201290d..6072d2ab 100644 --- a/server/init.sql +++ b/server/init.sql @@ -28,6 +28,13 @@ CREATE TABLE IF NOT EXISTS winRate ( PRIMARY KEY (id, general, mode) ); +CREATE TABLE IF NOT EXISTS runRate ( + id INTEGER, + mode VARCHAR(16), + run INTEGER, + PRIMARY KEY (id, mode) +); + CREATE VIEW IF NOT EXISTS playerWinRate AS SELECT winRate.id, name, mode, SUM(win) AS 'win', diff --git a/src/core/packman.cpp b/src/core/packman.cpp index 75856925..ece2490e 100644 --- a/src/core/packman.cpp +++ b/src/core/packman.cpp @@ -64,7 +64,10 @@ void PackMan::loadSummary(const QString &jsonData, bool useThread) { .arg(obj["hash"].toString()) .arg(name)); enablePack(name); - updatePack(name); + + if (head(name) != obj["hash"].toString()) { + updatePack(name); + } } }; if (useThread) { diff --git a/src/core/player.cpp b/src/core/player.cpp index 2604ba13..6f37293a 100644 --- a/src/core/player.cpp +++ b/src/core/player.cpp @@ -3,7 +3,8 @@ #include "player.h" Player::Player(QObject *parent) - : QObject(parent), id(0), state(Player::Invalid), ready(false) {} + : QObject(parent), id(0), state(Player::Invalid), ready(false), + totalGames(0), winCount(0), runCount(0) {} Player::~Player() {} @@ -35,6 +36,8 @@ QString Player::getStateString() const { return QStringLiteral("trust"); case Run: return QStringLiteral("run"); + case Leave: + return QStringLiteral("leave"); case Robot: return QStringLiteral("robot"); case Offline: @@ -70,3 +73,30 @@ void Player::setReady(bool ready) { this->ready = ready; emit readyChanged(); } + +QList Player::getGameData() { + return QList({ totalGames, winCount, runCount }); +} + +void Player::setGameData(int total, int win, int run) { + totalGames = total; + winCount = win; + runCount = run; + emit gameDataChanged(); +} + +QString Player::getLastGameMode() const { + return lastGameMode; +} + +void Player::setLastGameMode(const QString &mode) { + lastGameMode = mode; +} + +bool Player::isDied() const { + return died; +} + +void Player::setDied(bool died) { + this->died = died; +} diff --git a/src/core/player.h b/src/core/player.h index 06358f1f..1072cb41 100644 --- a/src/core/player.h +++ b/src/core/player.h @@ -14,6 +14,7 @@ public: Online, Trust, Run, + Leave, Robot, // only for real robot Offline }; @@ -38,11 +39,20 @@ public: bool isReady() const; void setReady(bool ready); + QList getGameData(); + void setGameData(int total, int win, int run); + QString getLastGameMode() const; + void setLastGameMode(const QString &mode); + + bool isDied() const; + void setDied(bool died); + signals: void screenNameChanged(); void avatarChanged(); void stateChanged(); void readyChanged(); + void gameDataChanged(); private: int id; @@ -50,6 +60,12 @@ private: QString avatar; State state; bool ready; + bool died; + + QString lastGameMode; + int totalGames; + int winCount; + int runCount; }; #endif // _PLAYER_H diff --git a/src/server/room.cpp b/src/server/room.cpp index 0d309eab..ec6eafc1 100644 --- a/src/server/room.cpp +++ b/src/server/room.cpp @@ -129,6 +129,8 @@ void Room::addPlayer(ServerPlayer *player) { } QJsonArray jsonData; + auto settings = QJsonDocument::fromJson(getSettings()); + auto mode = settings["gameMode"].toString(); // 告诉房里所有玩家有新人进来了 if (!isLobby()) { @@ -165,6 +167,13 @@ void Room::addPlayer(ServerPlayer *player) { jsonData << p->getAvatar(); jsonData << p->isReady(); player->doNotify("AddPlayer", JsonArray2Bytes(jsonData)); + + jsonData = QJsonArray(); + jsonData << p->getId(); + foreach (int i, p->getGameData()) { + jsonData << i; + } + player->doNotify("UpdateGameData", JsonArray2Bytes(jsonData)); } if (this->owner != nullptr) { @@ -173,6 +182,17 @@ void Room::addPlayer(ServerPlayer *player) { player->doNotify("RoomOwner", JsonArray2Bytes(jsonData)); } + if (player->getLastGameMode() != mode) { + player->setLastGameMode(mode); + updatePlayerGameData(player->getId(), mode); + } else { + auto jsonData = QJsonArray(); + jsonData << player->getId(); + foreach (int i, player->getGameData()) { + jsonData << i; + } + doBroadcastNotify(getPlayers(), "UpdateGameData", JsonArray2Bytes(jsonData)); + } // 玩家手动启动 // if (isFull() && !gameStarted) // start(); @@ -225,6 +245,10 @@ void Room::removePlayer(ServerPlayer *player) { player->setState(Player::Run); player->removeSocket(); + if (!player->isDied()) { + runned_players << player->getId(); + } + // 然后基于跑路玩家的socket,创建一个新ServerPlayer对象用来通信 ServerPlayer *runner = new ServerPlayer(this); runner->setSocket(socket); @@ -234,6 +258,8 @@ void Room::removePlayer(ServerPlayer *player) { runner->setScreenName(player->getScreenName()); runner->setAvatar(player->getAvatar()); runner->setId(player->getId()); + auto gamedata = player->getGameData(); + runner->setGameData(gamedata[0], gamedata[1], gamedata[2]); // 最后向服务器玩家列表中增加这个人 // 原先的跑路机器人会在游戏结束后自动销毁掉 @@ -346,22 +372,36 @@ void Room::chat(ServerPlayer *sender, const QString &jsonData) { doc["msg"].toString().toUtf8().constData()); } +static const QString findWinRate = + QString("SELECT win, lose, draw " + "FROM winRate WHERE id = %1 and general = '%2' and mode = '%3';"); + +static const QString updateWinRate = + QString("UPDATE winRate " + "SET win = %4, lose = %5, draw = %6 " + "WHERE id = %1 and general = '%2' and mode = '%3';"); + +static const QString insertWinRate = + QString("INSERT INTO winRate " + "(id, general, mode, win, lose, draw) " + "VALUES (%1, '%2', '%3', %4, %5, %6);"); + +static const QString findRunRate = + QString("SELECT run " + "FROM runRate WHERE id = %1 and mode = '%2';"); + +static const QString updateRunRate = + QString("UPDATE runRate " + "SET run = %3 " + "WHERE id = %1 and mode = '%2';"); + +static const QString insertRunRate = + QString("INSERT INTO runRate " + "(id, mode, run) " + "VALUES (%1, '%2', %3);"); + void Room::updateWinRate(int id, const QString &general, const QString &mode, - int game_result) { - static const QString findWinRate = - QString("SELECT win, lose, draw " - "FROM winRate WHERE id = %1 and general = '%2' and mode = '%3';"); - - static const QString updateRate = - QString("UPDATE winRate " - "SET win = %4, lose = %5, draw = %6 " - "WHERE id = %1 and general = '%2' and mode = '%3';"); - - static const QString insertRate = - QString("INSERT INTO winRate " - "(id, general, mode, win, lose, draw) " - "VALUES (%1, '%2', '%3', %4, %5, %6);"); - + int game_result, bool dead) { if (!CheckSqlString(general)) return; if (!CheckSqlString(mode)) @@ -370,6 +410,7 @@ void Room::updateWinRate(int id, const QString &general, const QString &mode, int win = 0; int lose = 0; int draw = 0; + int run = 0; switch (game_result) { case 1: @@ -391,7 +432,7 @@ void Room::updateWinRate(int id, const QString &general, const QString &mode, if (result.isEmpty()) { ExecSQL(server->getDatabase(), - insertRate.arg(QString::number(id), general, mode, + insertWinRate.arg(QString::number(id), general, mode, QString::number(win), QString::number(lose), QString::number(draw))); } else { @@ -400,10 +441,76 @@ void Room::updateWinRate(int id, const QString &general, const QString &mode, lose += obj["lose"].toString().toInt(); draw += obj["draw"].toString().toInt(); ExecSQL(server->getDatabase(), - updateRate.arg(QString::number(id), general, mode, + ::updateWinRate.arg(QString::number(id), general, mode, QString::number(win), QString::number(lose), QString::number(draw))); } + + if (runned_players.contains(id)) { + addRunRate(id, mode); + } + + auto player = server->findPlayer(id); + if (players.contains(player)) { + player->setLastGameMode(mode); + updatePlayerGameData(id, mode); + } +} + +void Room::addRunRate(int id, const QString &mode) { + int run = 1; + QJsonArray result = + SelectFromDatabase(server->getDatabase(), + findRunRate.arg(QString::number(id), mode)); + + if (result.isEmpty()) { + ExecSQL(server->getDatabase(), + insertRunRate.arg(QString::number(id), mode, + QString::number(run))); + } else { + auto obj = result[0].toObject(); + run += obj["run"].toString().toInt(); + ExecSQL(server->getDatabase(), + updateRunRate.arg(QString::number(id), mode, + QString::number(run))); + } +} + +void Room::updatePlayerGameData(int id, const QString &mode) { + static const QString findModeRate = QString("SELECT win, total FROM playerWinRate " + "WHERE id = %1 and mode = '%2';"); + + if (id < 0) return; + auto player = server->findPlayer(id); + if (player->getState() == Player::Robot || !player->getRoom()) { + return; + } + + int total = 0; + int win = 0; + int run = 0; + + auto result = SelectFromDatabase(server->getDatabase(), + findRunRate.arg(QString::number(id), mode)); + + if (!result.isEmpty()) { + run = result[0].toObject()["run"].toString().toInt(); + } + + result = SelectFromDatabase(server->getDatabase(), + findModeRate.arg(QString::number(id), mode)); + + if (!result.isEmpty()) { + total = result[0].toObject()["total"].toString().toInt(); + win = result[0].toObject()["win"].toString().toInt(); + } + + auto room = player->getRoom(); + player->setGameData(total, win, run); + auto data_arr = QJsonArray({ player->getId(), total, win, run }); + if (!room->isLobby()) { + room->doBroadcastNotify(room->getPlayers(), "UpdateGameData", JsonArray2Bytes(data_arr)); + } } void Room::gameOver() { @@ -427,6 +534,7 @@ void Room::manuallyStart() { if (isFull() && !gameStarted) { foreach (auto p, players) { p->setReady(false); + p->setDied(false); } gameStarted = true; m_thread->pushRequest(QString("-1,%1,newroom").arg(QString::number(id))); diff --git a/src/server/room.h b/src/server/room.h index 95fd40a9..424a9699 100644 --- a/src/server/room.h +++ b/src/server/room.h @@ -57,7 +57,7 @@ class Room : public QObject { void chat(ServerPlayer *sender, const QString &jsonData); void updateWinRate(int id, const QString &general, const QString &mode, - int result); + int result, bool dead); void gameOver(); void manuallyStart(); void pushRequest(const QString &req); @@ -86,6 +86,9 @@ class Room : public QObject { bool m_ready; int timeout; + + void addRunRate(int id, const QString &mode); + void updatePlayerGameData(int id, const QString &mode); }; #endif // _ROOM_H diff --git a/src/server/server.cpp b/src/server/server.cpp index 8e677747..933f304d 100644 --- a/src/server/server.cpp +++ b/src/server/server.cpp @@ -45,8 +45,6 @@ Server::Server(QObject *parent) : QObject(parent) { connect(lobby(), &Room::playerAdded, this, &Server::updateRoomList); connect(lobby(), &Room::playerRemoved, this, &Server::updateRoomList); - threads.append(new RoomThread(this)); - // 启动心跳包线程 auto heartbeatThread = QThread::create([=]() { while (true) { diff --git a/src/swig/player.i b/src/swig/player.i index 85c9cbbb..df7120de 100644 --- a/src/swig/player.i +++ b/src/swig/player.i @@ -9,6 +9,7 @@ public: Online, Trust, Run, + Leave, Robot, // only for real robot Offline }; @@ -24,6 +25,12 @@ public: State getState() const; void setState(State state); + + QList getGameData(); + void setGameData(int total, int win, int run); + + bool isDied() const; + void setDied(bool died); }; %nodefaultctor ClientPlayer; diff --git a/src/swig/server.i b/src/swig/server.i index f698add1..edd4775e 100644 --- a/src/swig/server.i +++ b/src/swig/server.i @@ -15,7 +15,7 @@ public: void checkAbandoned(); void updateWinRate(int id, const QString &general, const QString &mode, - int result); + int result, bool dead); void gameOver(); };