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();
};