在线查看胜率 (#198)

增加了胜率查看功能,长按玩家即可。
- 同步拓展包时若hash一致则跳过
This commit is contained in:
notify 2023-06-16 23:04:31 +08:00 committed by GitHub
parent 9881cb1653
commit f422039b71
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 245 additions and 36 deletions

View File

@ -62,10 +62,18 @@ Flickable {
} }
// TODO: player details // TODO: player details
Text { RowLayout {
id: screenName spacing: 16
Layout.fillWidth: true Text {
font.pixelSize: 18 id: screenName
font.pixelSize: 18
}
Text {
id: playerGameData
Layout.fillWidth: true
font.pixelSize: 18
}
} }
TextEdit { TextEdit {
@ -95,6 +103,7 @@ Flickable {
onExtra_dataChanged: { onExtra_dataChanged: {
if (!extra_data.photo) return; if (!extra_data.photo) return;
screenName.text = ""; screenName.text = "";
playerGameData.text = "";
skillDesc.text = ""; skillDesc.text = "";
const id = extra_data.photo.playerid; const id = extra_data.photo.playerid;
@ -103,6 +112,15 @@ Flickable {
screenName.text = extra_data.photo.screenName; 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])); const data = JSON.parse(Backend.callLuaFunction("GetPlayerSkills", [id]));
data.forEach(t => { data.forEach(t => {
skillDesc.append("<b>" + Backend.translate(t.name) + "</b>: " + t.description) skillDesc.append("<b>" + Backend.translate(t.name) + "</b>: " + t.description)

View File

@ -51,6 +51,7 @@ end
---@param id integer ---@param id integer
---@return ClientPlayer ---@return ClientPlayer
function Client:getPlayerById(id) function Client:getPlayerById(id)
if id == Self.id then return Self end
for _, p in ipairs(self.players) do for _, p in ipairs(self.players) do
if p.id == id then return p end if p.id == id then return p end
end end
@ -248,7 +249,7 @@ fk.client_callback["AddPlayer"] = function(jsonData)
-- jsonData: [ int id, string screenName, string avatar ] -- jsonData: [ int id, string screenName, string avatar ]
-- when other player enter the room, we create clientplayer(C and lua) for them -- when other player enter the room, we create clientplayer(C and lua) for them
local data = json.decode(jsonData) 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 player = fk.ClientInstance:addPlayer(id, name, avatar)
local p = ClientPlayer:new(player) local p = ClientPlayer:new(player)
table.insert(ClientInstance.players, p) table.insert(ClientInstance.players, p)
@ -763,6 +764,15 @@ fk.client_callback["UpdateQuestSkillUI"] = function(jsonData)
updateLimitSkill(player, Fk.skills[skillName], usedTimes) updateLimitSkill(player, Fk.skills[skillName], usedTimes)
end 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) -- Create ClientInstance (used by Lua)
ClientInstance = Client:new() ClientInstance = Client:new()
dofile "lua/client/client_util.lua" dofile "lua/client/client_util.lua"

View File

@ -573,4 +573,15 @@ function GetRoomConfig()
return json.encode(ClientInstance.room_settings) return json.encode(ClientInstance.room_settings)
end 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" dofile "lua/client/i18n/init.lua"

View File

@ -53,6 +53,7 @@ Fk:loadTranslationTable{
["Give Egg"] = "砸蛋", ["Give Egg"] = "砸蛋",
["Give Shoe"] = "拖鞋", ["Give Shoe"] = "拖鞋",
["Kick From Room"] = "踢出房间", ["Kick From Room"] = "踢出房间",
["Win=%1 Run=%2 Total=%3"] = "胜率%1% 逃率%2% 总场次%3",
["$OnlineInfo"] = "大厅人数:%1总在线人数%2", ["$OnlineInfo"] = "大厅人数:%1总在线人数%2",

View File

@ -35,6 +35,7 @@ GameEvent.functions[GameEvent.Death] = function(self)
local self = self.room local self = self.room
local victim = self:getPlayerById(deathStruct.who) local victim = self:getPlayerById(deathStruct.who)
victim.dead = true victim.dead = true
victim._splayer:setDied(true)
table.removeOne(self.alive_players, victim) table.removeOne(self.alive_players, victim)
local logic = self.logic local logic = self.logic

View File

@ -2818,6 +2818,7 @@ end
function Room:revivePlayer(player, sendLog) function Room:revivePlayer(player, sendLog)
if not player.dead then return end if not player.dead then return end
self:setPlayerProperty(player, "dead", false) self:setPlayerProperty(player, "dead", false)
player._splayer:setDied(false)
self:setPlayerProperty(player, "dying", false) self:setPlayerProperty(player, "dying", false)
self:setPlayerProperty(player, "hp", player.maxHp) self:setPlayerProperty(player, "hp", player.maxHp)
table.insertIfNeed(self.alive_players, player) table.insertIfNeed(self.alive_players, player)
@ -2862,11 +2863,11 @@ function Room:gameOver(winner)
if p.id > 0 then if p.id > 0 then
if table.contains(winner:split("+"), p.role) 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 elseif winner == "" then
self.room:updateWinRate(id, general, mode, 3) self.room:updateWinRate(id, general, mode, 3, p.dead)
else else
self.room:updateWinRate(id, general, mode, 2) self.room:updateWinRate(id, general, mode, 2, p.dead)
end end
end end
end end

View File

@ -175,11 +175,6 @@ function ServerPlayer:marshal(player)
room:notifyProperty(player, self, "gender") room:notifyProperty(player, self, "gender")
room:notifyProperty(player, self, "kingdom") 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 if self.dead then
room:notifyProperty(player, self, "dead") room:notifyProperty(player, self, "dead")
room:notifyProperty(player, self, "role") room:notifyProperty(player, self, "role")

View File

@ -28,6 +28,13 @@ CREATE TABLE IF NOT EXISTS winRate (
PRIMARY KEY (id, general, mode) 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 CREATE VIEW IF NOT EXISTS playerWinRate AS
SELECT winRate.id, name, mode, SELECT winRate.id, name, mode,
SUM(win) AS 'win', SUM(win) AS 'win',

View File

@ -64,7 +64,10 @@ void PackMan::loadSummary(const QString &jsonData, bool useThread) {
.arg(obj["hash"].toString()) .arg(obj["hash"].toString())
.arg(name)); .arg(name));
enablePack(name); enablePack(name);
updatePack(name);
if (head(name) != obj["hash"].toString()) {
updatePack(name);
}
} }
}; };
if (useThread) { if (useThread) {

View File

@ -3,7 +3,8 @@
#include "player.h" #include "player.h"
Player::Player(QObject *parent) 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() {} Player::~Player() {}
@ -35,6 +36,8 @@ QString Player::getStateString() const {
return QStringLiteral("trust"); return QStringLiteral("trust");
case Run: case Run:
return QStringLiteral("run"); return QStringLiteral("run");
case Leave:
return QStringLiteral("leave");
case Robot: case Robot:
return QStringLiteral("robot"); return QStringLiteral("robot");
case Offline: case Offline:
@ -70,3 +73,30 @@ void Player::setReady(bool ready) {
this->ready = ready; this->ready = ready;
emit readyChanged(); emit readyChanged();
} }
QList<int> Player::getGameData() {
return QList<int>({ 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;
}

View File

@ -14,6 +14,7 @@ public:
Online, Online,
Trust, Trust,
Run, Run,
Leave,
Robot, // only for real robot Robot, // only for real robot
Offline Offline
}; };
@ -38,11 +39,20 @@ public:
bool isReady() const; bool isReady() const;
void setReady(bool ready); void setReady(bool ready);
QList<int> 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: signals:
void screenNameChanged(); void screenNameChanged();
void avatarChanged(); void avatarChanged();
void stateChanged(); void stateChanged();
void readyChanged(); void readyChanged();
void gameDataChanged();
private: private:
int id; int id;
@ -50,6 +60,12 @@ private:
QString avatar; QString avatar;
State state; State state;
bool ready; bool ready;
bool died;
QString lastGameMode;
int totalGames;
int winCount;
int runCount;
}; };
#endif // _PLAYER_H #endif // _PLAYER_H

View File

@ -129,6 +129,8 @@ void Room::addPlayer(ServerPlayer *player) {
} }
QJsonArray jsonData; QJsonArray jsonData;
auto settings = QJsonDocument::fromJson(getSettings());
auto mode = settings["gameMode"].toString();
// 告诉房里所有玩家有新人进来了 // 告诉房里所有玩家有新人进来了
if (!isLobby()) { if (!isLobby()) {
@ -165,6 +167,13 @@ void Room::addPlayer(ServerPlayer *player) {
jsonData << p->getAvatar(); jsonData << p->getAvatar();
jsonData << p->isReady(); jsonData << p->isReady();
player->doNotify("AddPlayer", JsonArray2Bytes(jsonData)); 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) { if (this->owner != nullptr) {
@ -173,6 +182,17 @@ void Room::addPlayer(ServerPlayer *player) {
player->doNotify("RoomOwner", JsonArray2Bytes(jsonData)); 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) // if (isFull() && !gameStarted)
// start(); // start();
@ -225,6 +245,10 @@ void Room::removePlayer(ServerPlayer *player) {
player->setState(Player::Run); player->setState(Player::Run);
player->removeSocket(); player->removeSocket();
if (!player->isDied()) {
runned_players << player->getId();
}
// 然后基于跑路玩家的socket创建一个新ServerPlayer对象用来通信 // 然后基于跑路玩家的socket创建一个新ServerPlayer对象用来通信
ServerPlayer *runner = new ServerPlayer(this); ServerPlayer *runner = new ServerPlayer(this);
runner->setSocket(socket); runner->setSocket(socket);
@ -234,6 +258,8 @@ void Room::removePlayer(ServerPlayer *player) {
runner->setScreenName(player->getScreenName()); runner->setScreenName(player->getScreenName());
runner->setAvatar(player->getAvatar()); runner->setAvatar(player->getAvatar());
runner->setId(player->getId()); 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()); 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, void Room::updateWinRate(int id, const QString &general, const QString &mode,
int game_result) { int game_result, bool dead) {
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);");
if (!CheckSqlString(general)) if (!CheckSqlString(general))
return; return;
if (!CheckSqlString(mode)) if (!CheckSqlString(mode))
@ -370,6 +410,7 @@ void Room::updateWinRate(int id, const QString &general, const QString &mode,
int win = 0; int win = 0;
int lose = 0; int lose = 0;
int draw = 0; int draw = 0;
int run = 0;
switch (game_result) { switch (game_result) {
case 1: case 1:
@ -391,7 +432,7 @@ void Room::updateWinRate(int id, const QString &general, const QString &mode,
if (result.isEmpty()) { if (result.isEmpty()) {
ExecSQL(server->getDatabase(), 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(win), QString::number(lose),
QString::number(draw))); QString::number(draw)));
} else { } else {
@ -400,10 +441,76 @@ void Room::updateWinRate(int id, const QString &general, const QString &mode,
lose += obj["lose"].toString().toInt(); lose += obj["lose"].toString().toInt();
draw += obj["draw"].toString().toInt(); draw += obj["draw"].toString().toInt();
ExecSQL(server->getDatabase(), 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(win), QString::number(lose),
QString::number(draw))); 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() { void Room::gameOver() {
@ -427,6 +534,7 @@ void Room::manuallyStart() {
if (isFull() && !gameStarted) { if (isFull() && !gameStarted) {
foreach (auto p, players) { foreach (auto p, players) {
p->setReady(false); p->setReady(false);
p->setDied(false);
} }
gameStarted = true; gameStarted = true;
m_thread->pushRequest(QString("-1,%1,newroom").arg(QString::number(id))); m_thread->pushRequest(QString("-1,%1,newroom").arg(QString::number(id)));

View File

@ -57,7 +57,7 @@ class Room : public QObject {
void chat(ServerPlayer *sender, const QString &jsonData); void chat(ServerPlayer *sender, const QString &jsonData);
void updateWinRate(int id, const QString &general, const QString &mode, void updateWinRate(int id, const QString &general, const QString &mode,
int result); int result, bool dead);
void gameOver(); void gameOver();
void manuallyStart(); void manuallyStart();
void pushRequest(const QString &req); void pushRequest(const QString &req);
@ -86,6 +86,9 @@ class Room : public QObject {
bool m_ready; bool m_ready;
int timeout; int timeout;
void addRunRate(int id, const QString &mode);
void updatePlayerGameData(int id, const QString &mode);
}; };
#endif // _ROOM_H #endif // _ROOM_H

View File

@ -45,8 +45,6 @@ Server::Server(QObject *parent) : QObject(parent) {
connect(lobby(), &Room::playerAdded, this, &Server::updateRoomList); connect(lobby(), &Room::playerAdded, this, &Server::updateRoomList);
connect(lobby(), &Room::playerRemoved, this, &Server::updateRoomList); connect(lobby(), &Room::playerRemoved, this, &Server::updateRoomList);
threads.append(new RoomThread(this));
// 启动心跳包线程 // 启动心跳包线程
auto heartbeatThread = QThread::create([=]() { auto heartbeatThread = QThread::create([=]() {
while (true) { while (true) {

View File

@ -9,6 +9,7 @@ public:
Online, Online,
Trust, Trust,
Run, Run,
Leave,
Robot, // only for real robot Robot, // only for real robot
Offline Offline
}; };
@ -24,6 +25,12 @@ public:
State getState() const; State getState() const;
void setState(State state); void setState(State state);
QList<int> getGameData();
void setGameData(int total, int win, int run);
bool isDied() const;
void setDied(bool died);
}; };
%nodefaultctor ClientPlayer; %nodefaultctor ClientPlayer;

View File

@ -15,7 +15,7 @@ public:
void checkAbandoned(); void checkAbandoned();
void updateWinRate(int id, const QString &general, const QString &mode, void updateWinRate(int id, const QString &general, const QString &mode,
int result); int result, bool dead);
void gameOver(); void gameOver();
}; };