parent
0033cd6c07
commit
5843442f98
|
@ -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
|
||||||
|
|
|
@ -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),
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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());
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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;
|
||||||
|
|
Loading…
Reference in New Issue