diff --git a/Fk/Logic.js b/Fk/Logic.js index d133f4cb..9297bc2c 100644 --- a/Fk/Logic.js +++ b/Fk/Logic.js @@ -17,6 +17,29 @@ function createClientPages() { var callbacks = {}; let sheduled_download = ""; +callbacks["ServerDetected"] = (j) => { + const serverDialog = mainStack.currentItem.serverDialog; + if (!serverDialog) { + return; + } + const item = serverDialog.item; + if (item) { + toast.show(qsTr("Detected Server %1").arg(j.slice(7)), 10000); + } +} + +callbacks["GetServerDetail"] = (j) => { + const [addr, ver, icon, desc, capacity, count] = JSON.parse(j); + const serverDialog = mainStack.currentItem.serverDialog; + if (!serverDialog) { + return; + } + const item = serverDialog.item; + if (item) { + item.updateServerDetail(addr, [ver, icon, desc, capacity, count]); + } +} + callbacks["NetworkDelayTest"] = (jsonData) => { // jsonData: RSA pub key let cipherText; diff --git a/Fk/Pages/Init.qml b/Fk/Pages/Init.qml index 839c105f..b940f3f8 100644 --- a/Fk/Pages/Init.qml +++ b/Fk/Pages/Init.qml @@ -6,6 +6,7 @@ import QtQuick.Controls Item { id: root + property alias serverDialog: serverDialogLoader Item { width: 960 * 0.8 @@ -54,62 +55,17 @@ Item { Layout.alignment: Qt.AlignHCenter } - GridLayout { - columns: 2 - rowSpacing: 20 - - Text { - text: qsTr("Server Addr") - } - ComboBox { - id: server_addr - Layout.fillWidth: true - model: [] - editable: true - - onEditTextChanged: { - if (model.indexOf(editText) === -1) { - passwordEdit.text = ""; - } else { - const data = config.savedPassword[editText]; - screenNameEdit.text = data.username; - passwordEdit.text = data.shorten_password; - } - } - } - - Text { - text: qsTr("Username") - } - TextField { - id: screenNameEdit - maximumLength: 32 - Layout.fillWidth: true - placeholderText: qsTr("Username") - text: "" - onTextChanged: { - passwordEdit.text = ""; - const data = config.savedPassword[server_addr.editText]; - if (data) { - if (text === data.username) { - passwordEdit.text = data.shorten_password; - } - } - } - } - - CheckBox { - id: showPasswordCheck - text: qsTr("Show Password") - } - TextField { - id: passwordEdit - maximumLength: 64 - Layout.fillWidth: true - placeholderText: qsTr("Password") - text: "" - echoMode: showPasswordCheck.checked ? TextInput.Normal : TextInput.Password - passwordCharacter: "*" + Button { + Layout.fillWidth: true + text: qsTr("Console start") + onClicked: { + config.serverAddr = "127.0.0.1"; + const serverCfg = config.savedPassword["127.0.0.1"] ?? {}; + config.screenName = serverCfg.username ?? "player"; + config.password = serverCfg.shorten_password ?? "1234"; + mainWindow.busy = true; + Backend.startServer(9527); + Backend.joinServer("127.0.0.1"); } } @@ -117,38 +73,25 @@ Item { text: qsTr("Join Server") Layout.fillWidth: true display: AbstractButton.TextBesideIcon - icon.name: "go-next" - enabled: passwordEdit.text !== "" onClicked: { - config.serverAddr = server_addr.editText; - config.screenName = screenNameEdit.text; - config.password = passwordEdit.text; - mainWindow.busy = true; - Backend.joinServer(server_addr.editText); + serverDialog.show(); } } - RowLayout { - Button { - Layout.preferredWidth: 180 - text: qsTr("Console start") - enabled: passwordEdit.text !== "" - onClicked: { - config.serverAddr = "127.0.0.1"; - config.screenName = screenNameEdit.text; - config.password = passwordEdit.text; - mainWindow.busy = true; - Backend.startServer(9527); - Backend.joinServer("127.0.0.1"); - } + Button { + Layout.fillWidth: true + text: qsTr("PackageManage") + onClicked: { + mainStack.push(packageManage); } + } - Button { - Layout.fillWidth: true - text: qsTr("PackageManage") - onClicked: { - mainStack.push(packageManage); - } + Button { + Layout.fillWidth: true + text: qsTr("Quit Game") + onClicked: { + config.saveConf(); + Qt.quit(); } } } @@ -183,6 +126,63 @@ Item { } } + Item { + id: serverDialog + width: parent.width * 0.8 + height: parent.height * 0.9 + anchors.centerIn: parent + visible: false + + Rectangle { + anchors.fill: parent + opacity: 0.9 + radius: 8 + color: "snow" + border.color: "black" + } + + MouseArea { + anchors.fill: parent + } + + Loader { + id: serverDialogLoader + anchors.fill: parent + source: "JoinServer.qml" + } + + PropertyAnimation on opacity { + id: showAnim + from: 0 + to: 1 + duration: 400 + running: false + onStarted: { + serverDialogLoader.item.loadConfig(); + } + } + + PropertyAnimation on opacity { + id: hideAnim + from: 1 + to: 0 + duration: 400 + running: false + onFinished: { + serverDialog.visible = false; + } + } + + function show() { + visible = true; + showAnim.start(); + } + + function hide() { + hideAnim.start(); + } + } + // Temp Button { text: qsTr("Making Mod") @@ -202,15 +202,5 @@ Item { config.loadConf(); lady.source = config.ladyImg; - - server_addr.model = Object.keys(config.savedPassword); - server_addr.onModelChanged(); - server_addr.currentIndex = server_addr.model.indexOf(config.lastLoginServer); - - const data = config.savedPassword[config.lastLoginServer]; - if (data) { - screenNameEdit.text = data.username; - passwordEdit.text = data.shorten_password; - } } } diff --git a/Fk/Pages/JoinServer.qml b/Fk/Pages/JoinServer.qml new file mode 100644 index 00000000..e3ead7f1 --- /dev/null +++ b/Fk/Pages/JoinServer.qml @@ -0,0 +1,384 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +import QtQuick +import QtQuick.Layouts +import QtQuick.Controls + +Item { + id: root + anchors.fill: parent + + Timer { + id: opTimer + interval: 5000 + } + + Component { + id: serverDelegate + + Item { + height: 64 + width: serverList.width - 48 + + RowLayout { + anchors.fill: parent + spacing: 16 + + Image { + Layout.preferredHeight: 60 + Layout.preferredWidth: 60 + fillMode: Image.PreserveAspectFit + source: favicon + } + + Text { + text: serverIP + } + + Text { + text: description + } + + Text { + text: online + "/" + capacity + } + } + + TapHandler { + onTapped: { + if (serverList.currentIndex === index) { + serverList.currentIndex = -1; + } else { + serverList.currentIndex = index; + } + } + } + } + } + + ListView { + id: serverList + height: parent.height - controlPanel.height - 30 + width: parent.width - 80 + anchors.horizontalCenter: parent.horizontalCenter + anchors.top: parent.top + anchors.topMargin: 10 + contentHeight: serverDelegate.height * count + model: ListModel { + id: serverModel + } + delegate: serverDelegate + ScrollBar.vertical: ScrollBar {} + clip: true + highlight: Rectangle { + color: "transparent"; radius: 5 + border.color: "black"; border.width: 2 + } + highlightMoveDuration: 0 + currentIndex: -1 + } + + GridLayout { + id: controlPanel + anchors.top: serverList.bottom + anchors.topMargin: 10 + width: parent.width - 80 + anchors.horizontalCenter: parent.horizontalCenter + height: joinButton.height * 2 + 10 + columns: 3 + + Button { + id: joinButton + Layout.fillWidth: true + enabled: serverList.currentIndex !== -1 + text: qsTr("Join Server") + onClicked: { + const item = serverModel.get(serverList.currentIndex); + const serverCfg = config.savedPassword[item.serverIP]; + config.serverAddr = item.serverIP; + config.screenName = serverCfg.username; + config.password = serverCfg.shorten_password ?? serverCfg.password; + mainWindow.busy = true; + Backend.joinServer(item.serverIP); + } + } + + Button { + Layout.fillWidth: true + text: qsTr("Add New Server") + onClicked: { + drawerLoader.sourceComponent = newServerComponent; + drawer.open(); + } + } + + Button { + Layout.fillWidth: true + enabled: serverList.currentIndex !== -1 + text: qsTr("Edit Server") + onClicked: { + drawerLoader.sourceComponent = editServerComponent; + drawer.open(); + } + } + + Button { + Layout.fillWidth: true + text: qsTr("Refresh List") + enabled: !opTimer.running + onClicked: { + opTimer.start(); + for (let i = 0; i < serverModel.count; i++) { + const item = serverModel.get(i); + Backend.getServerInfo(item.serverIP); + } + } + } + + Button { + Layout.fillWidth: true + text: qsTr("Detect LAN") + enabled: !opTimer.running + onClicked: { + opTimer.start(); + Backend.detectServer(); + } + } + + Button { + Layout.fillWidth: true + text: qsTr("Go Back") + onClicked: serverDialog.hide(); + } + } + + Component { + id: newServerComponent + ColumnLayout { + signal finished(); + + Text { + text: qsTr("@NewServer") + font.pixelSize: 24 + font.bold: true + Layout.fillWidth: true + wrapMode: Text.WordWrap + } + + Text { + text: qsTr("@NewServerHint") + font.pixelSize: 16 + Layout.fillWidth: true + wrapMode: Text.WordWrap + } + + TextField { + id: serverAddrEdit + Layout.fillWidth: true + placeholderText: qsTr("Server Addr") + text: "" + } + + TextField { + id: screenNameEdit + maximumLength: 32 + Layout.fillWidth: true + placeholderText: qsTr("Username") + text: "" + } + + TextField { + id: passwordEdit + maximumLength: 64 + Layout.fillWidth: true + placeholderText: qsTr("Password") + text: "" + echoMode: showPasswordCheck.checked ? TextInput.Normal : TextInput.Password + passwordCharacter: "*" + } + + CheckBox { + id: showPasswordCheck + text: qsTr("Show Password") + } + + Button { + Layout.fillWidth: true + enabled: serverAddrEdit.text !== "" && screenNameEdit.text !== "" && passwordEdit.text !== "" + text: "OK" + onClicked: { + root.addNewServer(serverAddrEdit.text, screenNameEdit.text, passwordEdit.text); + finished(); + } + } + } + } + + Component { + id: editServerComponent + ColumnLayout { + signal finished(); + + Text { + text: qsTr("@EditServer") + font.pixelSize: 24 + font.bold: true + Layout.fillWidth: true + wrapMode: Text.WordWrap + } + + Text { + text: qsTr("@EditServerHint") + font.pixelSize: 16 + Layout.fillWidth: true + wrapMode: Text.WordWrap + } + + TextField { + id: screenNameEdit + maximumLength: 32 + Layout.fillWidth: true + placeholderText: qsTr("Username") + text: "" + } + + TextField { + id: passwordEdit + maximumLength: 64 + Layout.fillWidth: true + placeholderText: qsTr("Password") + text: "" + echoMode: showPasswordCheck.checked ? TextInput.Normal : TextInput.Password + passwordCharacter: "*" + } + + CheckBox { + id: showPasswordCheck + text: qsTr("Show Password") + } + + Button { + Layout.fillWidth: true + enabled: screenNameEdit.text !== "" && passwordEdit.text !== "" + text: "OK" + onClicked: { + root.editCurrentServer(screenNameEdit.text, passwordEdit.text); + finished(); + } + } + + Button { + Layout.fillWidth: true + text: qsTr("Delete Server") + onClicked: { + root.deleteCurrentServer(); + finished(); + } + } + } + } + + Drawer { + id: drawer + width: parent.width * 0.3 / mainWindow.scale + height: parent.height / mainWindow.scale + dim: false + clip: true + dragMargin: 0 + scale: mainWindow.scale + transformOrigin: Item.TopLeft + + Loader { + id: drawerLoader + anchors.fill: parent + anchors.margins: 16 + onSourceChanged: { + if (item === null) + return; + item.finished.connect(() => { + sourceComponent = undefined; + drawer.close(); + }); + } + onSourceComponentChanged: sourceChanged(); + } + } + + function addNewServer(addr, name, password) { + if (config.savedPassword[addr]) { + return; + } + + config.savedPassword[addr] = { + username: name, + password: password, + }; + config.saveConf(); + + serverModel.append({ + serverIP: addr, + description: qsTr("Server not up"), + online: "-", + capacity: "-", + favicon: "https://img1.imgtp.com/2023/07/01/DGUdj8eu.png", + }); + Backend.getServerInfo(addr); + } + + function editCurrentServer(name, password) { + const addr = serverModel.get(serverList.currentIndex).serverIP; + if (!config.savedPassword[addr]) { + return; + } + + config.savedPassword[addr] = { + username: name, + password: password, + shorten_password: undefined, + key: undefined, + }; + config.saveConf(); + } + + function deleteCurrentServer() { + const addr = serverModel.get(serverList.currentIndex).serverIP; + if (!config.savedPassword[addr]) { + return; + } + + config.savedPassword[addr] = undefined; + config.saveConf(); + + serverModel.remove(serverList.currentIndex, 1); + serverList.currentIndex = -1; + } + + function updateServerDetail(addr, data) { + const [ver, icon, desc, capacity, count] = data; + for (let i = 0; i < serverModel.count; i++) { + const item = serverModel.get(i); + if (addr.endsWith(item.serverIP)) { // endsWith是为了应付IPv6格式的ip + item.description = FkVersion === ver ? desc : "Ver " + ver; + item.favicon = icon; + item.online = count.toString(); + item.capacity = capacity.toString(); + } + } + } + + function loadConfig() { + if (serverModel.count > 0) { + return; + } + for (let key in config.savedPassword) { + serverModel.append({ + serverIP: key, + description: qsTr("Server not up"), + online: "-", + capacity: "-", + favicon: "https://img1.imgtp.com/2023/07/01/DGUdj8eu.png", + }); + Backend.getServerInfo(key); + } + } +} diff --git a/Fk/Pages/Room.qml b/Fk/Pages/Room.qml index e905f2b3..49ba2451 100644 --- a/Fk/Pages/Room.qml +++ b/Fk/Pages/Room.qml @@ -298,9 +298,13 @@ Item { width: childrenRect.width height: childrenRect.height anchors.bottom: parent.bottom + anchors.bottomMargin: 8 + anchors.left: parent.left + anchors.leftMargin: 8 ColumnLayout { MetroButton { text: Backend.translate("Revert Selection") + textFont.pixelSize: 28 enabled: dashboard.pending_skill !== "" onClicked: dashboard.revertSelection(); } @@ -309,9 +313,11 @@ Item { // } MetroButton { text: Backend.translate("Sort Cards") + textFont.pixelSize: 28 } MetroButton { text: Backend.translate("Chat") + textFont.pixelSize: 28 onClicked: roomDrawer.open(); } } diff --git a/lang/zh_CN.ts b/lang/zh_CN.ts index a80de596..9becc946 100644 --- a/lang/zh_CN.ts +++ b/lang/zh_CN.ts @@ -115,26 +115,22 @@ FreeKill 新月杀 - - Username - 用户名 - - - Show Password - 显示密码 - Join Server 加入服务器 Console start - 单机启动 + 单机启动(不推荐) PackageManage 管理拓展包 + + Quit Game + 退出游戏 + Making Mod 制作Mod @@ -143,10 +139,6 @@ Welcome back! 欢迎回来! - - Server Addr - 服务器IP - FAQ 常见疑问 @@ -169,6 +161,77 @@ + + JoinServer + + Join Server + 加入服务器 + + + Add New Server + 添加服务器 + + + Edit Server + 编辑服务器 + + + Refresh List + 刷新列表 + + + Detect LAN + 探测局域网 + + + Go Back + 返回 + + + + Server not up + 服务器似乎没有启动。 + + + + @NewServer + 添加新服务器 + + + @NewServerHint + 请输入服务器的IP或者域名,并输入你在这个服务器所使用的用户名与密码。如果你之前并未游玩过该服务器,则服务器会为你自动注册一个账号。以后登陆到该服务器都需要同样的用户名和密码,请不要忘记密码! + + + Server Addr + 服务器IP + + + Username + 用户名 + + + Password + 密码 + + + Show Password + 显示密码 + + + @EditServer + 编辑服务器 + + + @EditServerHint + 你可以修改在该服务器使用的用户名与密码。 + 你不能直接修改服务器的IP;如确实需要,请删除此服务器然后新增一个。 + + + Delete Server + 删除服务器 + + + main @@ -207,6 +270,10 @@ Logic + + Detected Server %1 + 检测到新月杀服务器 - %1 + MD5 check failed! MD5检测失败!请与服务端保持一致后再登入 diff --git a/lua/core/player.lua b/lua/core/player.lua index 86851252..60355b05 100644 --- a/lua/core/player.lua +++ b/lua/core/player.lua @@ -418,7 +418,7 @@ end --- 通过 二者位次+距离技能之和 与 两者间固定距离 进行对比,更大的为实际距离。 ---@param other Player @ 其他玩家 function Player:distanceTo(other) - -- assert(other:isInstanceOf(Player)) + assert(other:isInstanceOf(Player)) if other == self then return 0 end local right = 0 local temp = self diff --git a/lua/server/gamelogic.lua b/lua/server/gamelogic.lua index 48e9edde..49a3d74d 100644 --- a/lua/server/gamelogic.lua +++ b/lua/server/gamelogic.lua @@ -41,6 +41,7 @@ function GameLogic:run() local room = self.room table.shuffle(self.room.players) self:assignRoles() + self.room.game_started = true room:doBroadcastNotify("StartGame", "") room:adjustSeats() @@ -52,7 +53,6 @@ function GameLogic:run() self:attachSkillToPlayers() self:prepareForStart() - self.room.game_started = true self:action() end diff --git a/lua/server/request.lua b/lua/server/request.lua index 7d13e284..e25cc686 100644 --- a/lua/server/request.lua +++ b/lua/server/request.lua @@ -4,8 +4,8 @@ local function tellRoomToObserver(self, player) local observee = self.players[1] player:doNotify("Setup", json.encode{ observee.id, - observee.serverplayer:getScreenName(), - observee.serverplayer:getAvatar(), + observee._splayer:getScreenName(), + observee._splayer:getAvatar(), }) player:doNotify("EnterRoom", json.encode{ #self.players, self.timeout, self.settings @@ -16,8 +16,8 @@ local function tellRoomToObserver(self, player) for _, p in ipairs(self:getOtherPlayers(observee, true, true)) do player:doNotify("AddPlayer", json.encode{ p.id, - p.serverplayer:getScreenName(), - p.serverplayer:getAvatar(), + p._splayer:getScreenName(), + p._splayer:getAvatar(), }) end diff --git a/lua/server/room.lua b/lua/server/room.lua index 0f5d5c72..48aad9c2 100644 --- a/lua/server/room.lua +++ b/lua/server/room.lua @@ -131,8 +131,9 @@ function Room:resume() end ::GAME_OVER:: - coroutine.close(main_co) - self.main_co = nil + self:gameOver("") + -- coroutine.close(main_co) + -- self.main_co = nil return true end @@ -169,7 +170,7 @@ function Room:isReady() rest = p.request_timeout * 1000 - (os.getms() - p.request_start) / 1000 - if rest <= 0 then + if rest <= 0 or p.serverplayer:getState() ~= fk.Player_Online then p._splayer:setThinking(false) end end @@ -208,6 +209,7 @@ end --- 当这个函数返回之后,整个Room线程也宣告结束。 ---@return nil function Room:run() + self.start_time = os.time() for _, p in fk.qlist(self.room:getPlayers()) do local player = ServerPlayer:new(p) player.room = self @@ -2863,6 +2865,9 @@ local function shouldUpdateWinRate(room) if room.settings.enableFreeAssign then return false end + if os.time() - room.start_time < 45 then + return false + end for _, p in ipairs(room.players) do if p.id < 0 then return false end end diff --git a/lua/server/serverplayer.lua b/lua/server/serverplayer.lua index b9b15d5a..3bb6321d 100644 --- a/lua/server/serverplayer.lua +++ b/lua/server/serverplayer.lua @@ -284,8 +284,8 @@ function ServerPlayer:reconnect() self:doNotify("Setup", json.encode{ self.id, - self.serverplayer:getScreenName(), - self.serverplayer:getAvatar(), + self._splayer:getScreenName(), + self._splayer:getAvatar(), }) self:doNotify("EnterLobby", "") self:doNotify("EnterRoom", json.encode{ @@ -298,8 +298,8 @@ function ServerPlayer:reconnect() for _, p in ipairs(room:getOtherPlayers(self, true, true)) do self:doNotify("AddPlayer", json.encode{ p.id, - p.serverplayer:getScreenName(), - p.serverplayer:getAvatar(), + p._splayer:getScreenName(), + p._splayer:getAvatar(), }) end diff --git a/src/server/room.cpp b/src/server/room.cpp index 0dcc1d25..4e94606c 100644 --- a/src/server/room.cpp +++ b/src/server/room.cpp @@ -267,7 +267,7 @@ void Room::removePlayer(ServerPlayer *player) { server->addPlayer(runner); // 如果走小道的人不是单机启动玩家 那么直接ban - if (!runner->getSocket()->peerAddress().contains("127.0.0.1")) { + if (!runner->getSocket()->peerAddress().contains("127.0.0.1") && !player->isDied()) { server->temporarilyBan(runner->getId()); } diff --git a/src/server/server.cpp b/src/server/server.cpp index bbd3814f..3ee1d558 100644 --- a/src/server/server.cpp +++ b/src/server/server.cpp @@ -38,6 +38,11 @@ Server::Server(QObject *parent) : QObject(parent) { connect(server, &ServerSocket::new_connection, this, &Server::processNewConnection); + udpSocket = new QUdpSocket(this); + udpSocket->bind(9527); + connect(udpSocket, &QUdpSocket::readyRead, + this, &Server::readPendingDatagrams); + // 创建第一个房间,这个房间作为“大厅房间” nextRoomId = 0; createRoom(nullptr, "Lobby", INT32_MAX); @@ -520,6 +525,7 @@ void Server::onUserDisconnected() { player->setSocket(nullptr); // TODO: add a robot } else { + player->setState(Player::Robot); // 大厅!然而又不能设Offline player->deleteLater(); } } @@ -575,6 +581,9 @@ void Server::readConfig() { config = QJsonDocument::fromJson(json).object(); // defaults + SET_DEFAULT_CONFIG("description", "FreeKill Server"); + SET_DEFAULT_CONFIG("iconUrl", "https://img1.imgtp.com/2023/07/01/DGUdj8eu.png"); + SET_DEFAULT_CONFIG("capacity", 100); SET_DEFAULT_CONFIG("tempBanTime", 20); } @@ -610,3 +619,27 @@ void Server::temporarilyBan(int playerId) { }); emit player->kicked(); } + +void Server::readPendingDatagrams() { + while (udpSocket->hasPendingDatagrams()) { + QNetworkDatagram datagram = udpSocket->receiveDatagram(); + if (datagram.isValid()) { + processDatagram(datagram.data(), datagram.senderAddress(), datagram.senderPort()); + } + } +} + +void Server::processDatagram(const QByteArray &msg, const QHostAddress &addr, uint port) { + if (msg == "fkDetectServer") { + udpSocket->writeDatagram("me", addr, port); + } else if (msg == "fkGetDetail") { + udpSocket->writeDatagram(JsonArray2Bytes(QJsonArray({ + FK_VERSION, + getConfig("iconUrl"), + getConfig("description"), + getConfig("capacity"), + players.count(), + })), addr, port); + } + udpSocket->flush(); +} diff --git a/src/server/server.h b/src/server/server.h index b73e5a47..02e23f30 100644 --- a/src/server/server.h +++ b/src/server/server.h @@ -54,6 +54,7 @@ signals: public slots: void processNewConnection(ClientSocket *client); void processRequest(const QByteArray &msg); + void readPendingDatagrams(); void onRoomAbandoned(); void onUserDisconnected(); @@ -62,6 +63,8 @@ public slots: private: friend class Shell; ServerSocket *server; + QUdpSocket *udpSocket; + Room *m_lobby; QMap rooms; QStack idle_rooms; @@ -83,6 +86,7 @@ private: void handleNameAndPassword(ClientSocket *client, const QString &name, const QString &password, const QString &md5_str); + void processDatagram(const QByteArray &msg, const QHostAddress &addr, uint port); }; extern Server *ServerInstance; diff --git a/src/server/shell.cpp b/src/server/shell.cpp index 7fc9a7b5..ae322669 100644 --- a/src/server/shell.cpp +++ b/src/server/shell.cpp @@ -318,6 +318,11 @@ void Shell::run() { return; } + if (!strcmp(bytes, "crash")) { + qFatal("Crashing."); // should dump core + return; + } + if (*bytes) add_history(bytes); diff --git a/src/ui/qmlbackend.cpp b/src/ui/qmlbackend.cpp index 7d1cfc82..e4bf3f3a 100644 --- a/src/ui/qmlbackend.cpp +++ b/src/ui/qmlbackend.cpp @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-3.0-or-later #include "qmlbackend.h" +#include #ifndef FK_SERVER_ONLY #include @@ -26,6 +27,10 @@ QmlBackend::QmlBackend(QObject *parent) : QObject(parent) { #ifndef FK_SERVER_ONLY engine = nullptr; rsa = RSA_new(); + udpSocket = new QUdpSocket(this); + udpSocket->bind(0); + connect(udpSocket, &QUdpSocket::readyRead, + this, &QmlBackend::readPendingDatagrams); #endif } @@ -320,4 +325,50 @@ void QmlBackend::createModBackend() { engine->rootContext()->setContextProperty("ModBackend", new ModMaker); } + +void QmlBackend::detectServer() { + static const char *ask_str = "fkDetectServer"; + udpSocket->writeDatagram(ask_str, + strlen(ask_str), + QHostAddress::Broadcast, + 9527); +} + +void QmlBackend::getServerInfo(const QString &address) { + QString addr = "127.0.0.1"; + ushort port = 9527u; + static const char *ask_str = "fkGetDetail"; + + if (address.contains(QChar(':'))) { + QStringList texts = address.split(QChar(':')); + addr = texts.value(0); + port = texts.value(1).toUShort(); + } else { + addr = address; + } + + udpSocket->writeDatagram(ask_str, + strlen(ask_str), + QHostAddress(addr), port); +} + +void QmlBackend::readPendingDatagrams() { + while (udpSocket->hasPendingDatagrams()) { + QNetworkDatagram datagram = udpSocket->receiveDatagram(); + if (datagram.isValid()) { + auto data = datagram.data(); + auto addr = datagram.senderAddress(); + // auto port = datagram.senderPort(); + + if (data == "me") { + emit notifyUI("ServerDetected", addr.toString()); + } else { + auto arr = QJsonDocument::fromJson(data).array(); + arr.prepend(addr.toString()); + emit notifyUI("GetServerDetail", JsonArray2Bytes(arr)); + } + } + } +} + #endif diff --git a/src/ui/qmlbackend.h b/src/ui/qmlbackend.h index 46d21820..1fc94222 100644 --- a/src/ui/qmlbackend.h +++ b/src/ui/qmlbackend.h @@ -55,6 +55,9 @@ public: Q_INVOKABLE void createModBackend(); + Q_INVOKABLE void detectServer(); + Q_INVOKABLE void getServerInfo(const QString &addr); + qreal volume() const { return m_volume; } void setVolume(qreal v) { m_volume = v; } @@ -64,10 +67,15 @@ signals: void notifyUI(const QString &command, const QString &jsonData); void volumeChanged(qreal); +private slots: + void readPendingDatagrams(); + private: Q_PROPERTY(qreal volume READ volume WRITE setVolume NOTIFY volumeChanged) QQmlApplicationEngine *engine; + + QUdpSocket *udpSocket; RSA *rsa; QString aes_key; qreal m_volume;