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 @@
新月杀
-
-
- 用户名
-
-
-
- 显示密码
-
加入服务器
- 单机启动
+ 单机启动(不推荐)
管理拓展包
+
+
+ 退出游戏
+
制作Mod
@@ -143,10 +139,6 @@
欢迎回来!
-
-
- 服务器IP
-
常见疑问
@@ -169,6 +161,77 @@
+
+ JoinServer
+
+
+ 加入服务器
+
+
+
+ 添加服务器
+
+
+
+ 编辑服务器
+
+
+
+ 刷新列表
+
+
+
+ 探测局域网
+
+
+
+ 返回
+
+
+
+
+ 服务器似乎没有启动。
+
+
+
+
+ 添加新服务器
+
+
+
+ 请输入服务器的IP或者域名,并输入你在这个服务器所使用的用户名与密码。如果你之前并未游玩过该服务器,则服务器会为你自动注册一个账号。以后登陆到该服务器都需要同样的用户名和密码,请不要忘记密码!
+
+
+
+ 服务器IP
+
+
+
+ 用户名
+
+
+
+ 密码
+
+
+
+ 显示密码
+
+
+
+ 编辑服务器
+
+
+
+ 你可以修改在该服务器使用的用户名与密码。
+ 你不能直接修改服务器的IP;如确实需要,请删除此服务器然后新增一个。
+
+
+
+ 删除服务器
+
+
+
main
@@ -207,6 +270,10 @@
Logic
+
+
+ 检测到新月杀服务器 - %1
+
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;