胜率(无UI)
This commit is contained in:
notify 2023-05-13 14:45:23 +08:00 committed by GitHub
parent 0033cd6c07
commit 5843442f98
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 168 additions and 23 deletions

View File

@ -2482,6 +2482,18 @@ function Room:useSkill(player, skill, effect_cb)
end end
end end
---@param room Room
local function shouldUpdateWinRate(room)
if room.settings.gameMode == "heg_mode" then return false end
if room.settings.gameMode == "aaa_role_mode" and #room.players < 5 then
return false
end
for _, p in ipairs(room.players) do
if p.id < 0 then return false end
end
return true
end
--- 结束一局游戏。 --- 结束一局游戏。
---@param winner string @ 获胜的身份,空字符串表示平局 ---@param winner string @ 获胜的身份,空字符串表示平局
function Room:gameOver(winner) function Room:gameOver(winner)
@ -2494,6 +2506,24 @@ function Room:gameOver(winner)
end end
self:doBroadcastNotify("GameOver", winner) self:doBroadcastNotify("GameOver", winner)
if shouldUpdateWinRate(self) then
for _, p in ipairs(self.players) do
local id = p.id
local general = p.general
local mode = self.settings.gameMode
if p.id > 0 then
if table.contains(winner:split("+"), p.role) then
self.room:updateWinRate(id, general, mode, 1)
elseif winner == "" then
self.room:updateWinRate(id, general, mode, 3)
else
self.room:updateWinRate(id, general, mode, 2)
end
end
end
end
self.room:gameOver() self.room:gameOver()
coroutine.yield("__handleRequest") coroutine.yield("__handleRequest")
end end

View File

@ -1,6 +1,6 @@
/* SPDX-License-Identifier: GPL-3.0-or-later */ /* SPDX-License-Identifier: GPL-3.0-or-later */
CREATE TABLE packages ( CREATE TABLE IF NOT EXISTS packages (
name VARCHAR(128), name VARCHAR(128),
url VARCHAR(255), url VARCHAR(255),
hash CHAR(40), hash CHAR(40),

View File

@ -1,6 +1,8 @@
/* SPDX-License-Identifier: GPL-3.0-or-later */ -- SPDX-License-Identifier: GPL-3.0-or-later
CREATE TABLE userinfo ( -- 用户基本信息
CREATE TABLE IF NOT EXISTS userinfo (
id INTEGER PRIMARY KEY AUTOINCREMENT, id INTEGER PRIMARY KEY AUTOINCREMENT,
name VARCHAR(255), name VARCHAR(255),
password CHAR(64), password CHAR(64),
@ -10,6 +12,40 @@ CREATE TABLE userinfo (
banned BOOLEAN banned BOOLEAN
); );
CREATE TABLE banip ( CREATE TABLE IF NOT EXISTS banip (
ip VARCHAR(64) ip VARCHAR(64)
); );
-- 胜率相关
CREATE TABLE IF NOT EXISTS winRate (
id INTEGER,
general VARCHAR(20),
mode VARCHAR(16),
win INTEGER,
lose INTEGER,
draw INTEGER,
PRIMARY KEY (id, general, mode)
);
CREATE VIEW IF NOT EXISTS playerWinRate AS
SELECT winRate.id, name, mode,
SUM(win) AS 'win',
SUM(lose) AS 'lose',
SUM(draw) AS 'draw',
SUM(win + lose + draw) AS 'total',
ROUND(SUM(win) * 1.0 / (SUM(win + lose + draw) * 1.0) * 100, 2)
AS 'winRate'
FROM winRate, userinfo
WHERE winRate.id = userinfo.id
GROUP BY winRate.id, mode;
CREATE VIEW IF NOT EXISTS generalWinRate AS
SELECT general, mode,
SUM(win) AS 'win',
SUM(lose) AS 'lose',
SUM(draw) AS 'draw',
SUM(win + lose + draw) AS 'total',
ROUND(SUM(win) * 1.0 / (SUM(win + lose + draw) * 1.0) * 100, 2)
AS 'winRate'
FROM winRate GROUP BY general, mode;

View File

@ -63,14 +63,15 @@ void Dumpstack(lua_State *L) {
sqlite3 *OpenDatabase(const QString &filename, const QString &initSql) { sqlite3 *OpenDatabase(const QString &filename, const QString &initSql) {
sqlite3 *ret; sqlite3 *ret;
int rc; int rc;
if (!QFile::exists(filename)) {
QFile file(initSql);
if (!file.open(QIODevice::ReadOnly)) {
qFatal("cannot open %s. Quit now.", initSql.toUtf8().data());
qApp->exit(1);
}
QTextStream in(&file); QFile file(initSql);
if (!file.open(QIODevice::ReadOnly)) {
qFatal("cannot open %s. Quit now.", initSql.toUtf8().data());
qApp->exit(1);
}
QTextStream in(&file);
if (!QFile::exists(filename)) {
char *err_msg; char *err_msg;
sqlite3_open(filename.toLatin1().data(), &ret); sqlite3_open(filename.toLatin1().data(), &ret);
rc = sqlite3_exec(ret, in.readAll().toLatin1().data(), nullptr, nullptr, rc = sqlite3_exec(ret, in.readAll().toLatin1().data(), nullptr, nullptr,
@ -89,10 +90,26 @@ sqlite3 *OpenDatabase(const QString &filename, const QString &initSql) {
sqlite3_close(ret); sqlite3_close(ret);
qApp->exit(1); qApp->exit(1);
} }
char *err_msg;
rc = sqlite3_exec(ret, in.readAll().toLatin1().data(), nullptr, nullptr,
&err_msg);
if (rc != SQLITE_OK) {
qCritical() << "sqlite error:" << err_msg;
sqlite3_free(err_msg);
sqlite3_close(ret);
qApp->exit(1);
}
} }
return ret; return ret;
} }
bool CheckSqlString(const QString &str) {
static const QRegularExpression exp("['\";#]+|(--)|(/\\*)|(\\*/)|(--\\+)");
return (!exp.match(str).hasMatch() && !str.isEmpty());
}
// callback for handling SELECT expression // callback for handling SELECT expression
static int callback(void *jsonDoc, int argc, char **argv, char **cols) { static int callback(void *jsonDoc, int argc, char **argv, char **cols) {
QJsonObject obj; QJsonObject obj;

View File

@ -9,6 +9,7 @@ lua_State *CreateLuaState();
bool DoLuaScript(lua_State *L, const char *script); bool DoLuaScript(lua_State *L, const char *script);
sqlite3 *OpenDatabase(const QString &filename = "./server/users.db", const QString &initSql = "./server/init.sql"); sqlite3 *OpenDatabase(const QString &filename = "./server/users.db", const QString &initSql = "./server/init.sql");
bool CheckSqlString(const QString &str);
QJsonArray SelectFromDatabase(sqlite3 *db, const QString &sql); QJsonArray SelectFromDatabase(sqlite3 *db, const QString &sql);
// For Lua // For Lua
QString SelectFromDb(sqlite3 *db, const QString &sql); QString SelectFromDb(sqlite3 *db, const QString &sql);

View File

@ -155,8 +155,8 @@ void Router::handlePacket(const QByteArray &rawPacket) {
const QString &jsonData) { const QString &jsonData) {
auto arr = String2Json(jsonData).array(); auto arr = String2Json(jsonData).array();
auto avatar = arr[0].toString(); auto avatar = arr[0].toString();
static QRegularExpression nameExp("['\";#]+|(--)|(/\\*)|(\\*/)|(--\\+)");
if (!nameExp.match(avatar).hasMatch()) { if (CheckSqlString(avatar)) {
auto sql = QString("UPDATE userinfo SET avatar='%1' WHERE id=%2;") auto sql = QString("UPDATE userinfo SET avatar='%1' WHERE id=%2;")
.arg(avatar) .arg(avatar)
.arg(sender->getId()); .arg(sender->getId());

View File

@ -302,9 +302,68 @@ void Room::chat(ServerPlayer *sender, const QString &jsonData) {
doBroadcastNotify(observers, "Chat", json); doBroadcastNotify(observers, "Chat", json);
} }
qInfo("[Chat] %s: %s", sender->getScreenName().toUtf8().constData(), qInfo("[Chat] %s: %s", sender->getScreenName().toUtf8().constData(),
doc["msg"].toString().toUtf8().constData()); doc["msg"].toString().toUtf8().constData());
}
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);");
if (!CheckSqlString(general))
return;
if (!CheckSqlString(mode))
return;
int win = 0;
int lose = 0;
int draw = 0;
switch (game_result) {
case 1:
win++;
break;
case 2:
lose++;
break;
case 3:
draw++;
break;
default:
break;
}
QJsonArray result =
SelectFromDatabase(server->getDatabase(),
findWinRate.arg(QString::number(id), general, mode));
if (result.isEmpty()) {
ExecSQL(server->getDatabase(),
insertRate.arg(QString::number(id), general, mode,
QString::number(win), QString::number(lose),
QString::number(draw)));
} else {
auto obj = result[0].toObject();
win += obj["win"].toString().toInt();
lose += obj["lose"].toString().toInt();
draw += obj["draw"].toString().toInt();
ExecSQL(server->getDatabase(),
updateRate.arg(QString::number(id), general, mode,
QString::number(win), QString::number(lose),
QString::number(draw)));
}
} }
void Room::gameOver() { void Room::gameOver() {
@ -324,7 +383,8 @@ void Room::gameOver() {
} }
QString Room::fetchRequest() { QString Room::fetchRequest() {
if (!gameStarted) return ""; if (!gameStarted)
return "";
request_queue_mutex.lock(); request_queue_mutex.lock();
QString ret = ""; QString ret = "";
if (!request_queue.isEmpty()) { if (!request_queue.isEmpty()) {
@ -335,7 +395,8 @@ QString Room::fetchRequest() {
} }
void Room::pushRequest(const QString &req) { void Room::pushRequest(const QString &req) {
if (!gameStarted) return; if (!gameStarted)
return;
request_queue_mutex.lock(); request_queue_mutex.lock();
request_queue.enqueue(req); request_queue.enqueue(req);
request_queue_mutex.unlock(); request_queue_mutex.unlock();

View File

@ -52,6 +52,8 @@ class Room : public QThread {
const QString &command, const QString &jsonData); const QString &command, const QString &jsonData);
void chat(ServerPlayer *sender, const QString &jsonData); void chat(ServerPlayer *sender, const QString &jsonData);
void updateWinRate(int id, const QString &general, const QString &mode,
int result);
void gameOver(); void gameOver();
void initLua(); void initLua();

View File

@ -220,10 +220,6 @@ void Server::processRequest(const QByteArray &msg) {
void Server::handleNameAndPassword(ClientSocket *client, const QString &name, void Server::handleNameAndPassword(ClientSocket *client, const QString &name,
const QString &password, const QString &password,
const QString &md5_str) { const QString &md5_str) {
// First check the name and password
// Matches a string that does not contain special characters
static QRegularExpression nameExp("['\";#]+|(--)|(/\\*)|(\\*/)|(--\\+)");
auto encryted_pw = QByteArray::fromBase64(password.toLatin1()); auto encryted_pw = QByteArray::fromBase64(password.toLatin1());
unsigned char buf[4096] = {0}; unsigned char buf[4096] = {0};
RSA_private_decrypt(RSA_size(rsa), (const unsigned char *)encryted_pw.data(), RSA_private_decrypt(RSA_size(rsa), (const unsigned char *)encryted_pw.data(),
@ -263,7 +259,7 @@ void Server::handleNameAndPassword(ClientSocket *client, const QString &name,
QJsonArray result; QJsonArray result;
QJsonObject obj; QJsonObject obj;
if (!nameExp.match(name).hasMatch() && !name.isEmpty()) { if (CheckSqlString(name)) {
// Then we check the database, // Then we check the database,
QString sql_find = QString("SELECT * FROM userinfo \ QString sql_find = QString("SELECT * FROM userinfo \
WHERE name='%1';") WHERE name='%1';")
@ -311,7 +307,7 @@ void Server::handleNameAndPassword(ClientSocket *client, const QString &name,
player->alive = true; player->alive = true;
client->disconnect(this); client->disconnect(this);
broadcast("ServerMessage", broadcast("ServerMessage",
tr("%1 backed").arg(player->getScreenName())); tr("%1 backed").arg(player->getScreenName()));
room->pushRequest(QString("%1,reconnect").arg(id)); room->pushRequest(QString("%1,reconnect").arg(id));
return; return;
} else { } else {

View File

@ -51,6 +51,8 @@ public:
const QString &jsonData const QString &jsonData
); );
void updateWinRate(int id, const QString &general, const QString &mode,
int result);
void gameOver(); void gameOver();
LuaFunction startGame; LuaFunction startGame;