船新登陆界面 (#214)

* 修一些小bug
* 局域网检测
* 检测服务器数据
* 船新的登陆界面
This commit is contained in:
notify 2023-07-01 04:12:19 +08:00 committed by GitHub
parent c7e0b2fdf3
commit fd270a2edb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 695 additions and 119 deletions

View File

@ -17,6 +17,29 @@ function createClientPages() {
var callbacks = {}; var callbacks = {};
let sheduled_download = ""; 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) => { callbacks["NetworkDelayTest"] = (jsonData) => {
// jsonData: RSA pub key // jsonData: RSA pub key
let cipherText; let cipherText;

View File

@ -6,6 +6,7 @@ import QtQuick.Controls
Item { Item {
id: root id: root
property alias serverDialog: serverDialogLoader
Item { Item {
width: 960 * 0.8 width: 960 * 0.8
@ -54,62 +55,17 @@ Item {
Layout.alignment: Qt.AlignHCenter Layout.alignment: Qt.AlignHCenter
} }
GridLayout { Button {
columns: 2 Layout.fillWidth: true
rowSpacing: 20 text: qsTr("Console start")
onClicked: {
Text { config.serverAddr = "127.0.0.1";
text: qsTr("Server Addr") const serverCfg = config.savedPassword["127.0.0.1"] ?? {};
} config.screenName = serverCfg.username ?? "player";
ComboBox { config.password = serverCfg.shorten_password ?? "1234";
id: server_addr mainWindow.busy = true;
Layout.fillWidth: true Backend.startServer(9527);
model: [] Backend.joinServer("127.0.0.1");
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: "*"
} }
} }
@ -117,38 +73,25 @@ Item {
text: qsTr("Join Server") text: qsTr("Join Server")
Layout.fillWidth: true Layout.fillWidth: true
display: AbstractButton.TextBesideIcon display: AbstractButton.TextBesideIcon
icon.name: "go-next"
enabled: passwordEdit.text !== ""
onClicked: { onClicked: {
config.serverAddr = server_addr.editText; serverDialog.show();
config.screenName = screenNameEdit.text;
config.password = passwordEdit.text;
mainWindow.busy = true;
Backend.joinServer(server_addr.editText);
} }
} }
RowLayout { Button {
Button { Layout.fillWidth: true
Layout.preferredWidth: 180 text: qsTr("PackageManage")
text: qsTr("Console start") onClicked: {
enabled: passwordEdit.text !== "" mainStack.push(packageManage);
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 { Button {
Layout.fillWidth: true Layout.fillWidth: true
text: qsTr("PackageManage") text: qsTr("Quit Game")
onClicked: { onClicked: {
mainStack.push(packageManage); 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 // Temp
Button { Button {
text: qsTr("Making Mod") text: qsTr("Making Mod")
@ -202,15 +202,5 @@ Item {
config.loadConf(); config.loadConf();
lady.source = config.ladyImg; 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;
}
} }
} }

384
Fk/Pages/JoinServer.qml Normal file
View File

@ -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)) { // endsWithIPv6ip
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);
}
}
}

View File

@ -298,9 +298,13 @@ Item {
width: childrenRect.width width: childrenRect.width
height: childrenRect.height height: childrenRect.height
anchors.bottom: parent.bottom anchors.bottom: parent.bottom
anchors.bottomMargin: 8
anchors.left: parent.left
anchors.leftMargin: 8
ColumnLayout { ColumnLayout {
MetroButton { MetroButton {
text: Backend.translate("Revert Selection") text: Backend.translate("Revert Selection")
textFont.pixelSize: 28
enabled: dashboard.pending_skill !== "" enabled: dashboard.pending_skill !== ""
onClicked: dashboard.revertSelection(); onClicked: dashboard.revertSelection();
} }
@ -309,9 +313,11 @@ Item {
// } // }
MetroButton { MetroButton {
text: Backend.translate("Sort Cards") text: Backend.translate("Sort Cards")
textFont.pixelSize: 28
} }
MetroButton { MetroButton {
text: Backend.translate("Chat") text: Backend.translate("Chat")
textFont.pixelSize: 28
onClicked: roomDrawer.open(); onClicked: roomDrawer.open();
} }
} }

View File

@ -115,26 +115,22 @@
<source>FreeKill</source> <source>FreeKill</source>
<translation></translation> <translation></translation>
</message> </message>
<message>
<source>Username</source>
<translation></translation>
</message>
<message>
<source>Show Password</source>
<translation></translation>
</message>
<message> <message>
<source>Join Server</source> <source>Join Server</source>
<translation></translation> <translation></translation>
</message> </message>
<message> <message>
<source>Console start</source> <source>Console start</source>
<translation></translation> <translation></translation>
</message> </message>
<message> <message>
<source>PackageManage</source> <source>PackageManage</source>
<translation></translation> <translation></translation>
</message> </message>
<message>
<source>Quit Game</source>
<translation>退</translation>
</message>
<message> <message>
<source>Making Mod</source> <source>Making Mod</source>
<translation>Mod</translation> <translation>Mod</translation>
@ -143,10 +139,6 @@
<source>Welcome back!</source> <source>Welcome back!</source>
<translation></translation> <translation></translation>
</message> </message>
<message>
<source>Server Addr</source>
<translation>IP</translation>
</message>
<message> <message>
<source>FAQ</source> <source>FAQ</source>
<translation></translation> <translation></translation>
@ -169,6 +161,77 @@
</message> </message>
</context> </context>
<context>
<name>JoinServer</name>
<message>
<source>Join Server</source>
<translation></translation>
</message>
<message>
<source>Add New Server</source>
<translation></translation>
</message>
<message>
<source>Edit Server</source>
<translation></translation>
</message>
<message>
<source>Refresh List</source>
<translation></translation>
</message>
<message>
<source>Detect LAN</source>
<translation></translation>
</message>
<message>
<source>Go Back</source>
<translation></translation>
</message>
<message>
<source>Server not up</source>
<translation></translation>
</message>
<message>
<source>@NewServer</source>
<translation></translation>
</message>
<message>
<source>@NewServerHint</source>
<translation>IP或者域名使</translation>
</message>
<message>
<source>Server Addr</source>
<translation>IP</translation>
</message>
<message>
<source>Username</source>
<translation></translation>
</message>
<message>
<source>Password</source>
<translation></translation>
</message>
<message>
<source>Show Password</source>
<translation></translation>
</message>
<message>
<source>@EditServer</source>
<translation></translation>
</message>
<message>
<source>@EditServerHint</source>
<translation>使
IP</translation>
</message>
<message>
<source>Delete Server</source>
<translation></translation>
</message>
</context>
<context> <context>
<name>main</name> <name>main</name>
<message> <message>
@ -207,6 +270,10 @@
<context> <context>
<name>Logic</name> <name>Logic</name>
<message>
<source>Detected Server %1</source>
<translation> - %1</translation>
</message>
<message> <message>
<source>MD5 check failed!</source> <source>MD5 check failed!</source>
<translation>MD5检测失败</translation> <translation>MD5检测失败</translation>

View File

@ -418,7 +418,7 @@ end
--- 通过 二者位次+距离技能之和 与 两者间固定距离 进行对比,更大的为实际距离。 --- 通过 二者位次+距离技能之和 与 两者间固定距离 进行对比,更大的为实际距离。
---@param other Player @ 其他玩家 ---@param other Player @ 其他玩家
function Player:distanceTo(other) function Player:distanceTo(other)
-- assert(other:isInstanceOf(Player)) assert(other:isInstanceOf(Player))
if other == self then return 0 end if other == self then return 0 end
local right = 0 local right = 0
local temp = self local temp = self

View File

@ -41,6 +41,7 @@ function GameLogic:run()
local room = self.room local room = self.room
table.shuffle(self.room.players) table.shuffle(self.room.players)
self:assignRoles() self:assignRoles()
self.room.game_started = true
room:doBroadcastNotify("StartGame", "") room:doBroadcastNotify("StartGame", "")
room:adjustSeats() room:adjustSeats()
@ -52,7 +53,6 @@ function GameLogic:run()
self:attachSkillToPlayers() self:attachSkillToPlayers()
self:prepareForStart() self:prepareForStart()
self.room.game_started = true
self:action() self:action()
end end

View File

@ -4,8 +4,8 @@ local function tellRoomToObserver(self, player)
local observee = self.players[1] local observee = self.players[1]
player:doNotify("Setup", json.encode{ player:doNotify("Setup", json.encode{
observee.id, observee.id,
observee.serverplayer:getScreenName(), observee._splayer:getScreenName(),
observee.serverplayer:getAvatar(), observee._splayer:getAvatar(),
}) })
player:doNotify("EnterRoom", json.encode{ player:doNotify("EnterRoom", json.encode{
#self.players, self.timeout, self.settings #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 for _, p in ipairs(self:getOtherPlayers(observee, true, true)) do
player:doNotify("AddPlayer", json.encode{ player:doNotify("AddPlayer", json.encode{
p.id, p.id,
p.serverplayer:getScreenName(), p._splayer:getScreenName(),
p.serverplayer:getAvatar(), p._splayer:getAvatar(),
}) })
end end

View File

@ -131,8 +131,9 @@ function Room:resume()
end end
::GAME_OVER:: ::GAME_OVER::
coroutine.close(main_co) self:gameOver("")
self.main_co = nil -- coroutine.close(main_co)
-- self.main_co = nil
return true return true
end end
@ -169,7 +170,7 @@ function Room:isReady()
rest = p.request_timeout * 1000 - (os.getms() - rest = p.request_timeout * 1000 - (os.getms() -
p.request_start) / 1000 p.request_start) / 1000
if rest <= 0 then if rest <= 0 or p.serverplayer:getState() ~= fk.Player_Online then
p._splayer:setThinking(false) p._splayer:setThinking(false)
end end
end end
@ -208,6 +209,7 @@ end
--- 当这个函数返回之后整个Room线程也宣告结束。 --- 当这个函数返回之后整个Room线程也宣告结束。
---@return nil ---@return nil
function Room:run() function Room:run()
self.start_time = os.time()
for _, p in fk.qlist(self.room:getPlayers()) do for _, p in fk.qlist(self.room:getPlayers()) do
local player = ServerPlayer:new(p) local player = ServerPlayer:new(p)
player.room = self player.room = self
@ -2863,6 +2865,9 @@ local function shouldUpdateWinRate(room)
if room.settings.enableFreeAssign then if room.settings.enableFreeAssign then
return false return false
end end
if os.time() - room.start_time < 45 then
return false
end
for _, p in ipairs(room.players) do for _, p in ipairs(room.players) do
if p.id < 0 then return false end if p.id < 0 then return false end
end end

View File

@ -284,8 +284,8 @@ function ServerPlayer:reconnect()
self:doNotify("Setup", json.encode{ self:doNotify("Setup", json.encode{
self.id, self.id,
self.serverplayer:getScreenName(), self._splayer:getScreenName(),
self.serverplayer:getAvatar(), self._splayer:getAvatar(),
}) })
self:doNotify("EnterLobby", "") self:doNotify("EnterLobby", "")
self:doNotify("EnterRoom", json.encode{ self:doNotify("EnterRoom", json.encode{
@ -298,8 +298,8 @@ function ServerPlayer:reconnect()
for _, p in ipairs(room:getOtherPlayers(self, true, true)) do for _, p in ipairs(room:getOtherPlayers(self, true, true)) do
self:doNotify("AddPlayer", json.encode{ self:doNotify("AddPlayer", json.encode{
p.id, p.id,
p.serverplayer:getScreenName(), p._splayer:getScreenName(),
p.serverplayer:getAvatar(), p._splayer:getAvatar(),
}) })
end end

View File

@ -267,7 +267,7 @@ void Room::removePlayer(ServerPlayer *player) {
server->addPlayer(runner); server->addPlayer(runner);
// 如果走小道的人不是单机启动玩家 那么直接ban // 如果走小道的人不是单机启动玩家 那么直接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()); server->temporarilyBan(runner->getId());
} }

View File

@ -38,6 +38,11 @@ Server::Server(QObject *parent) : QObject(parent) {
connect(server, &ServerSocket::new_connection, this, connect(server, &ServerSocket::new_connection, this,
&Server::processNewConnection); &Server::processNewConnection);
udpSocket = new QUdpSocket(this);
udpSocket->bind(9527);
connect(udpSocket, &QUdpSocket::readyRead,
this, &Server::readPendingDatagrams);
// 创建第一个房间,这个房间作为“大厅房间” // 创建第一个房间,这个房间作为“大厅房间”
nextRoomId = 0; nextRoomId = 0;
createRoom(nullptr, "Lobby", INT32_MAX); createRoom(nullptr, "Lobby", INT32_MAX);
@ -520,6 +525,7 @@ void Server::onUserDisconnected() {
player->setSocket(nullptr); player->setSocket(nullptr);
// TODO: add a robot // TODO: add a robot
} else { } else {
player->setState(Player::Robot); // 大厅然而又不能设Offline
player->deleteLater(); player->deleteLater();
} }
} }
@ -575,6 +581,9 @@ void Server::readConfig() {
config = QJsonDocument::fromJson(json).object(); config = QJsonDocument::fromJson(json).object();
// defaults // 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); SET_DEFAULT_CONFIG("tempBanTime", 20);
} }
@ -610,3 +619,27 @@ void Server::temporarilyBan(int playerId) {
}); });
emit player->kicked(); 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();
}

View File

@ -54,6 +54,7 @@ signals:
public slots: public slots:
void processNewConnection(ClientSocket *client); void processNewConnection(ClientSocket *client);
void processRequest(const QByteArray &msg); void processRequest(const QByteArray &msg);
void readPendingDatagrams();
void onRoomAbandoned(); void onRoomAbandoned();
void onUserDisconnected(); void onUserDisconnected();
@ -62,6 +63,8 @@ public slots:
private: private:
friend class Shell; friend class Shell;
ServerSocket *server; ServerSocket *server;
QUdpSocket *udpSocket;
Room *m_lobby; Room *m_lobby;
QMap<int, Room *> rooms; QMap<int, Room *> rooms;
QStack<Room *> idle_rooms; QStack<Room *> idle_rooms;
@ -83,6 +86,7 @@ private:
void handleNameAndPassword(ClientSocket *client, const QString &name, void handleNameAndPassword(ClientSocket *client, const QString &name,
const QString &password, const QString &md5_str); const QString &password, const QString &md5_str);
void processDatagram(const QByteArray &msg, const QHostAddress &addr, uint port);
}; };
extern Server *ServerInstance; extern Server *ServerInstance;

View File

@ -318,6 +318,11 @@ void Shell::run() {
return; return;
} }
if (!strcmp(bytes, "crash")) {
qFatal("Crashing."); // should dump core
return;
}
if (*bytes) if (*bytes)
add_history(bytes); add_history(bytes);

View File

@ -1,6 +1,7 @@
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
#include "qmlbackend.h" #include "qmlbackend.h"
#include <qjsondocument.h>
#ifndef FK_SERVER_ONLY #ifndef FK_SERVER_ONLY
#include <qaudiooutput.h> #include <qaudiooutput.h>
@ -26,6 +27,10 @@ QmlBackend::QmlBackend(QObject *parent) : QObject(parent) {
#ifndef FK_SERVER_ONLY #ifndef FK_SERVER_ONLY
engine = nullptr; engine = nullptr;
rsa = RSA_new(); rsa = RSA_new();
udpSocket = new QUdpSocket(this);
udpSocket->bind(0);
connect(udpSocket, &QUdpSocket::readyRead,
this, &QmlBackend::readPendingDatagrams);
#endif #endif
} }
@ -320,4 +325,50 @@ void QmlBackend::createModBackend() {
engine->rootContext()->setContextProperty("ModBackend", new ModMaker); 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 #endif

View File

@ -55,6 +55,9 @@ public:
Q_INVOKABLE void createModBackend(); Q_INVOKABLE void createModBackend();
Q_INVOKABLE void detectServer();
Q_INVOKABLE void getServerInfo(const QString &addr);
qreal volume() const { return m_volume; } qreal volume() const { return m_volume; }
void setVolume(qreal v) { m_volume = v; } void setVolume(qreal v) { m_volume = v; }
@ -64,10 +67,15 @@ signals:
void notifyUI(const QString &command, const QString &jsonData); void notifyUI(const QString &command, const QString &jsonData);
void volumeChanged(qreal); void volumeChanged(qreal);
private slots:
void readPendingDatagrams();
private: private:
Q_PROPERTY(qreal volume READ volume WRITE setVolume NOTIFY volumeChanged) Q_PROPERTY(qreal volume READ volume WRITE setVolume NOTIFY volumeChanged)
QQmlApplicationEngine *engine; QQmlApplicationEngine *engine;
QUdpSocket *udpSocket;
RSA *rsa; RSA *rsa;
QString aes_key; QString aes_key;
qreal m_volume; qreal m_volume;