Serverlist (#360)
Some checks are pending
Check Whitespace and New Line / check (push) Waiting to run
Deploy Sphinx documentation to Pages / pages (push) Waiting to run

## 服务器列表

- 重制了之前的加入服务器与保存密码的页面,以及功能
- 密码在本地以明文保存
- 根据游戏目录中的server-list.json读取公共服务器列表,还没做自动更新
- 可能有不少bug
This commit is contained in:
notify 2024-07-03 01:31:22 +08:00 committed by GitHub
parent 79ede70b6b
commit 0325c73443
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
17 changed files with 356 additions and 313 deletions

1
.gitignore vendored
View File

@ -31,6 +31,7 @@ freekill-wrap.cxx
/freekill.server.error.log /freekill.server.error.log
/freekill.server.info.log /freekill.server.info.log
/flist.txt /flist.txt
/server-list.json
# windeployqt # windeployqt
/bearer/ /bearer/

View File

@ -6,7 +6,7 @@
cmake_minimum_required(VERSION 3.16) cmake_minimum_required(VERSION 3.16)
project(FreeKill VERSION 0.4.18) project(FreeKill VERSION 0.4.19)
add_definitions(-DFK_VERSION=\"${CMAKE_PROJECT_VERSION}\") add_definitions(-DFK_VERSION=\"${CMAKE_PROJECT_VERSION}\")
find_package(Qt6 REQUIRED COMPONENTS find_package(Qt6 REQUIRED COMPONENTS

View File

@ -10,7 +10,8 @@ QtObject {
property real winHeight property real winHeight
property var conf: ({}) property var conf: ({})
property string lastLoginServer property string lastLoginServer
property var savedPassword: ({}) //property var savedPassword: ({})
property var favoriteServers: []
property string lobbyBg property string lobbyBg
property string roomBg property string roomBg
property string bgmFile property string bgmFile
@ -39,6 +40,7 @@ QtObject {
// Player property of client // Player property of client
property string serverAddr property string serverAddr
property int serverPort
property string screenName: "" property string screenName: ""
property string password: "" property string password: ""
property string cipherText property string cipherText
@ -63,6 +65,43 @@ QtObject {
// disableGeneralSchemes[disableSchemeIdx] = disabledGenerals; // disableGeneralSchemes[disableSchemeIdx] = disabledGenerals;
// } // }
function findFavorite(addr, port) {
for (const s of favoriteServers) {
if (s.addr === addr && s.port === port) {
return s;
}
}
return undefined;
}
function removeFavorite(addr, port) {
for (const i in favoriteServers) {
const s = favoriteServers[i];
if (s.addr === addr && s.port === port) {
favoriteServers.splice(i, 1);
saveConf();
return;
}
}
}
function addFavorite(addr, port, name, username, password) {
for (const i in favoriteServers) {
const s = favoriteServers[i];
if (s.addr === addr && s.port === port) {
s.name = name;
s.username = username;
s.password = password;
saveConf();
return false;
}
}
favoriteServers.unshift({ addr, port, name, username, password });
saveConf();
return true;
}
function loadConf() { function loadConf() {
conf = JSON.parse(Backend.loadConf()); conf = JSON.parse(Backend.loadConf());
winX = conf.winX ?? 100; winX = conf.winX ?? 100;
@ -70,7 +109,8 @@ QtObject {
winWidth = conf.winWidth ?? 960; winWidth = conf.winWidth ?? 960;
winHeight = conf.winHeight ?? 540; winHeight = conf.winHeight ?? 540;
lastLoginServer = conf.lastLoginServer ?? "127.0.0.1"; lastLoginServer = conf.lastLoginServer ?? "127.0.0.1";
savedPassword = conf.savedPassword ?? {}; //savedPassword = conf.savedPassword ?? {};
favoriteServers = conf.favoriteServers ?? [];
lobbyBg = conf.lobbyBg ?? AppPath + "/image/background"; lobbyBg = conf.lobbyBg ?? AppPath + "/image/background";
roomBg = conf.roomBg ?? AppPath + "/image/gamebg"; roomBg = conf.roomBg ?? AppPath + "/image/gamebg";
bgmFile = conf.bgmFile ?? AppPath + "/audio/system/bgm.mp3"; bgmFile = conf.bgmFile ?? AppPath + "/audio/system/bgm.mp3";
@ -116,7 +156,8 @@ QtObject {
conf.winWidth = realMainWin.width; conf.winWidth = realMainWin.width;
conf.winHeight = realMainWin.height; conf.winHeight = realMainWin.height;
conf.lastLoginServer = lastLoginServer; conf.lastLoginServer = lastLoginServer;
conf.savedPassword = savedPassword; //conf.savedPassword = savedPassword;
conf.favoriteServers = favoriteServers;
conf.lobbyBg = lobbyBg; conf.lobbyBg = lobbyBg;
conf.roomBg = roomBg; conf.roomBg = roomBg;
conf.bgmFile = bgmFile; conf.bgmFile = bgmFile;

View File

@ -26,7 +26,8 @@ callbacks["ServerDetected"] = (j) => {
} }
const item = serverDialog.item; const item = serverDialog.item;
if (item) { if (item) {
toast.show(qsTr("Detected Server %1").arg(j.slice(7)), 10000); // toast.show(qsTr("Detected Server %1").arg(j.slice(7)), 10000);
item.addLANServer(j.slice(7))
} }
} }
@ -38,7 +39,9 @@ callbacks["GetServerDetail"] = (j) => {
} }
const item = serverDialog.item; const item = serverDialog.item;
if (item) { if (item) {
item.updateServerDetail(addr, [ver, icon, desc, capacity, count]); let [_addr, port] = addr.split(',');
port = parseInt(port);
item.updateServerDetail(_addr, port, [ver, icon, desc, capacity, count]);
} }
} }
@ -46,18 +49,18 @@ callbacks["NetworkDelayTest"] = (jsonData) => {
// jsonData: RSA pub key // jsonData: RSA pub key
let cipherText; let cipherText;
let aeskey; let aeskey;
const savedPw = config.savedPassword[config.serverAddr]; // const savedPw = config.savedPassword[config.serverAddr];
if (savedPw?.shorten_password === config.password) { // if (savedPw?.shorten_password === config.password) {
cipherText = config.savedPassword[config.serverAddr].password; // cipherText = config.savedPassword[config.serverAddr].password;
aeskey = config.savedPassword[config.serverAddr].key; // aeskey = config.savedPassword[config.serverAddr].key;
config.aeskey = aeskey ?? ""; // config.aeskey = aeskey ?? "";
Backend.setAESKey(aeskey); // Backend.setAESKey(aeskey);
if (Debugging) // if (Debugging)
console.log("use remembered password", config.password); // console.log("use remembered password", config.password);
} else { // } else {
cipherText = Backend.pubEncrypt(jsonData, config.password); cipherText = Backend.pubEncrypt(jsonData, config.password);
config.aeskey = Backend.getAESKey(); config.aeskey = Backend.getAESKey();
} // }
config.cipherText = cipherText; config.cipherText = cipherText;
Backend.replyDelayTest(config.screenName, cipherText); Backend.replyDelayTest(config.screenName, cipherText);
} }
@ -130,12 +133,12 @@ callbacks["EnterLobby"] = (jsonData) => {
if (mainStack.depth === 1) { if (mainStack.depth === 1) {
// we enter the lobby successfully, so save password now. // we enter the lobby successfully, so save password now.
config.lastLoginServer = config.serverAddr; config.lastLoginServer = config.serverAddr;
config.savedPassword[config.serverAddr] = { // config.savedPassword[config.serverAddr] = {
username: config.screenName, // username: config.screenName,
password: config.cipherText, // password: config.cipherText,
key: config.aeskey, // key: config.aeskey,
shorten_password: config.cipherText.slice(0, 8) // shorten_password: config.cipherText.slice(0, 8)
} // }
mainStack.push(lobby); mainStack.push(lobby);
} else { } else {
mainStack.pop(); mainStack.pop();

View File

@ -60,12 +60,16 @@ Item {
text: qsTr("Console start") text: qsTr("Console start")
onClicked: { onClicked: {
config.serverAddr = "127.0.0.1"; config.serverAddr = "127.0.0.1";
const serverCfg = config.savedPassword["127.0.0.1"] ?? {}; config.serverPort = 9527;
config.screenName = serverCfg.username ?? "player"; // const serverCfg = config.savedPassword["127.0.0.1"] ?? {};
config.password = serverCfg.shorten_password ?? "1234"; const serverCfg = config.findFavorite("127.0.0.1", 9527);
config.screenName = serverCfg?.username ?? "player";
config.password = serverCfg?.password ?? "1234";
mainWindow.busy = true; mainWindow.busy = true;
config.addFavorite(config.serverAddr, config.serverPort, "",
config.screenName, config.password);
Backend.startServer(9527); Backend.startServer(9527);
Backend.joinServer("127.0.0.1"); Backend.joinServer("127.0.0.1", 9527);
} }
} }

View File

@ -3,10 +3,12 @@
import QtQuick import QtQuick
import QtQuick.Layouts import QtQuick.Layouts
import QtQuick.Controls import QtQuick.Controls
import Fk
Item { Item {
id: root id: root
anchors.fill: parent anchors.fill: parent
property var selectedServer: serverModel.get(serverList.currentIndex)
Timer { Timer {
id: opTimer id: opTimer
@ -18,18 +20,24 @@ Item {
Item { Item {
height: 64 height: 64
width: serverList.width - 48 width: serverList.width / 2 - 4
clip: true
RowLayout { RowLayout {
anchors.fill: parent anchors.fill: parent
spacing: 16 spacing: 16
Item {}
Image { Image {
Layout.preferredHeight: 60 Layout.alignment: Qt.AlignVCenter | Qt.AlignHCenter
Layout.preferredWidth: 60 Layout.preferredHeight: 56
Layout.preferredWidth: 56
fillMode: Image.PreserveAspectFit fillMode: Image.PreserveAspectFit
source: favicon source: {
if (!favicon) return SkinBank.MISC_DIR + "server_icon";
if (favicon === "default") return AppPath + "/image/icon.png";
return favicon;
}
} }
ColumnLayout { ColumnLayout {
@ -37,22 +45,51 @@ Item {
Text { Text {
Layout.fillWidth: true Layout.fillWidth: true
horizontalAlignment: Text.AlignLeft horizontalAlignment: Text.AlignLeft
text: serverIP + " " + misMatchMsg text: {
if (name) return name;
let a = addr;
let p = port;
if (p === 9527) p = 0;
if (a.includes(":") && p) { // IPv6
a = `[${a}]`;
}
if (p) {
p = `:${p}`;
} else {
p = "";
}
return `${a}${p}`;
}
font.bold: true font.bold: true
color: favicon ? "black" : "gray"
} }
Text { Text {
Layout.fillWidth: true Layout.fillWidth: true
horizontalAlignment: Text.AlignLeft horizontalAlignment: Text.AlignLeft
text: description text: delay + " ms " + misMatchMsg
textFormat: TextEdit.RichText textFormat: TextEdit.RichText
color: {
if (delay < 0) {
return "gray";
} else if (delay >= 0 && delay < 100) {
return "green";
} else if (delay >= 100 && delay < 500) {
return "orange";
} else {
return "red";
}
}
} }
} }
Text { Text {
text: online + "/" + capacity text: online + "/<font size='1'>" + capacity + "</font>"
font.pixelSize: 30 font.pixelSize: 26
color: favicon ? "black" : "gray"
} }
Item {}
} }
TapHandler { TapHandler {
@ -64,90 +101,51 @@ Item {
} }
} }
} }
ColumnLayout {
x: 6
height: parent.height
Item { Layout.fillHeight: true }
Image {
Layout.preferredWidth: 24; Layout.preferredHeight: 23
source: SkinBank.MISC_DIR + "network_local"
visible: lan
}
Image {
Layout.preferredWidth: 24; Layout.preferredHeight: 23
source: SkinBank.MISC_DIR + "favorite"
visible: favorite
}
}
} }
} }
ListView { RowLayout {
id: serverList id: serverListBar
height: parent.height - controlPanel.height - 30 height: childrenRect.height
width: parent.width - 80 width: serverList.width
anchors.horizontalCenter: parent.horizontalCenter Text {
anchors.top: parent.top text: "已收藏服务器与公共服务器列表"
anchors.topMargin: 10 font.pixelSize: 18
contentHeight: serverDelegate.height * count font.bold: true
model: ListModel { x: 32; y: 8
id: serverModel
} }
delegate: serverDelegate Item { Layout.fillWidth: true }
ScrollBar.vertical: ScrollBar {}
clip: true
highlight: Rectangle {
color: "#AA9ABFEF"; 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 { 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") text: qsTr("Refresh List")
enabled: !opTimer.running enabled: !opTimer.running
onClicked: { onClicked: {
opTimer.start(); opTimer.start();
for (let i = 0; i < serverModel.count; i++) { for (let i = 0; i < serverModel.count; i++) {
const item = serverModel.get(i); const item = serverModel.get(i);
Backend.getServerInfo(item.serverIP); if (!item.favorite && !item.lan) break;
item.delayBegin = (new Date).getTime();
Backend.getServerInfo(item.addr, item.port);
} }
} }
} }
Button { Button {
Layout.fillWidth: true
text: qsTr("Detect LAN") text: qsTr("Detect LAN")
enabled: !opTimer.running enabled: !opTimer.running
onClicked: { onClicked: {
@ -157,251 +155,234 @@ Item {
} }
Button { Button {
Layout.fillWidth: true
text: qsTr("Go Back") text: qsTr("Go Back")
onClicked: serverDialog.hide(); onClicked: serverDialog.hide();
} }
} }
Component { ColumnLayout {
id: newServerComponent id: serverPanel
ColumnLayout { anchors.right: parent.right
signal finished(); anchors.top: parent.top
anchors.margins: 8
height: parent.height - 16
width: parent.width * 0.3
TextField {
id: addressEdit
maximumLength: 64
Layout.fillWidth: true
placeholderText: "服务器地址"
text: selectedServer?.addr ?? ""
}
TextField {
id: portEdit
maximumLength: 6
Layout.fillWidth: true
placeholderText: "端口"
text: selectedServer?.port ?? ""
}
Flickable {
Layout.fillHeight: true
Layout.fillWidth: true
contentHeight: descText.height
clip: true
Text { Text {
text: qsTr("@NewServer") id: descText
font.pixelSize: 24 width: parent.width
font.bold: true text: selectedServer?.description ?? ""
Layout.fillWidth: true wrapMode: Text.WrapAnywhere
wrapMode: Text.WordWrap font.pixelSize: 18
} }
}
Text { RowLayout {
text: qsTr("@NewServerHint") Layout.fillWidth: true
font.pixelSize: 16
Layout.fillWidth: true
wrapMode: Text.WordWrap
}
TextField { TextField {
id: serverAddrEdit id: usernameEdit
Layout.fillWidth: true
placeholderText: qsTr("Server Addr")
text: ""
}
TextField {
id: screenNameEdit
maximumLength: 32 maximumLength: 32
Layout.fillWidth: true Layout.fillWidth: true
placeholderText: qsTr("Username") placeholderText: "用户名"
text: "" text: selectedServer?.username ?? ""
} }
TextField { TextField {
id: passwordEdit id: passwordEdit
maximumLength: 64 maximumLength: 32
Layout.fillWidth: true Layout.fillWidth: true
placeholderText: qsTr("Password") placeholderText: "密码"
text: ""
echoMode: showPasswordCheck.checked ? TextInput.Normal
: TextInput.Password
passwordCharacter: "*" passwordCharacter: "*"
echoMode: TextInput.Password
text: selectedServer?.password ?? ""
} }
}
CheckBox { Button {
id: showPasswordCheck text: "登录(首次登录自动注册)"
text: qsTr("Show Password") Layout.fillWidth: true
} enabled: !!(addressEdit.text && portEdit.text &&
usernameEdit.text && passwordEdit.text)
Button { onClicked: {
Layout.fillWidth: true const _addr = addressEdit.text;
enabled: serverAddrEdit.text !== "" && screenNameEdit.text !== "" const _port = parseInt(portEdit.text);
&& passwordEdit.text !== "" const _username = usernameEdit.text;
text: "OK" const _password = passwordEdit.text;
onClicked: { config.screenName = _username;
root.addNewServer(serverAddrEdit.text, screenNameEdit.text, config.password = _password;
passwordEdit.text); mainWindow.busy = true;
finished(); config.serverAddr = _addr;
config.serverPort = _port;
let name = selectedServer?.name;
if (_addr !== selectedServer?.addr || _port !== selectedServer?.port) {
name = "";
} }
addFavorite(config.serverAddr, config.serverPort, name,
config.screenName, config.password);
Backend.joinServer(_addr, _port);
}
}
Button {
text: "从收藏夹删除"
Layout.fillWidth: true
visible: !!(selectedServer?.favorite)
onClicked: {
removeFavorite(selectedServer.addr, selectedServer.port);
} }
} }
} }
Component { GridView {
id: editServerComponent id: serverList
ColumnLayout { height: parent.height - 16 - serverListBar.height
signal finished(); width: parent.width - 24 - serverPanel.width
anchors.top: serverListBar.bottom
Text { anchors.left: parent.left
text: qsTr("@EditServer") anchors.margins: 8
font.pixelSize: 24 model: ListModel { id: serverModel }
font.bold: true delegate: serverDelegate
Layout.fillWidth: true cellHeight: 64 + 8
wrapMode: Text.WordWrap cellWidth: serverList.width / 2
}
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 clip: true
dragMargin: 0 highlight: Rectangle {
scale: mainWindow.scale color: "#AA9ABFEF"; radius: 5
transformOrigin: Item.TopLeft // border.color: "black"; border.width: 2
Loader {
id: drawerLoader
anchors.fill: parent
anchors.margins: 16
onSourceChanged: {
if (item === null)
return;
item.finished.connect(() => {
sourceComponent = undefined;
drawer.close();
});
}
onSourceComponentChanged: sourceChanged();
} }
currentIndex: -1
} }
function addNewServer(addr, name, password) { function addFavorite(addr, port, name, username, password) {
if (config.savedPassword[addr]) { const newItem = config.addFavorite(addr, port, name, username, password);
return; if (!newItem) {
for (let i = 0; i < serverModel.count; i++) {
const s = serverModel.get(i);
if (s.addr === addr && s.port === port && s.favorite) {
s.name = name;
s.username = username;
s.password = password;
return;
}
}
} }
serverModel.insert(0, {
config.savedPassword[addr] = { addr, port, name, username, password,
username: name,
password: password,
};
config.saveConf();
serverModel.append({
serverIP: addr,
misMatchMsg: "", misMatchMsg: "",
description: qsTr("Server not up"), description: qsTr("Server not up"),
online: "-", online: "?",
capacity: "-", capacity: "??",
favicon: "https://img1.imgtp.com/2023/07/01/DGUdj8eu.png", favicon: "",
delayBegin: (new Date).getTime(),
delay: -1,
favorite: true,
lan: false,
}); });
Backend.getServerInfo(addr); Backend.getServerInfo(addr, port);
} }
function editCurrentServer(name, password) { function removeFavorite(addr, port) {
const addr = serverModel.get(serverList.currentIndex).serverIP; config.removeFavorite(addr, port);
if (!config.savedPassword[addr]) { for (let i = 0; i < serverModel.count; i++) {
return; const s = serverModel.get(i);
if (s.addr === addr && s.port === port && s.favorite) {
serverModel.remove(i);
serverList.currentIndex = -1;
return;
}
} }
config.savedPassword[addr] = {
username: name,
password: password,
shorten_password: undefined,
key: undefined,
};
config.saveConf();
} }
function deleteCurrentServer() { function addLANServer(addr) {
const addr = serverModel.get(serverList.currentIndex).serverIP; const port = 9527;
if (!config.savedPassword[addr]) { if (config.findFavorite(addr, port)) return;
return; for (let i = 0; i < serverModel.count; i++) {
const s = serverModel.get(i);
if (s.addr === addr && s.port === port && s.lan) {
s.delayBegin = (new Date).getTime();
Backend.getServerInfo(addr, port);
return;
}
if (!s.lan && !s.favorite) break;
} }
serverModel.insert(0, {
config.savedPassword[addr] = undefined; addr, port,
config.saveConf(); name: "",
username: "",
serverModel.remove(serverList.currentIndex, 1); password: "",
serverList.currentIndex = -1; misMatchMsg: "",
description: qsTr("Server not up"),
online: "?",
capacity: "??",
favicon: "",
delayBegin: (new Date).getTime(),
delay: -1,
favorite: false,
lan: true,
});
Backend.getServerInfo(addr, port);
} }
function updateServerDetail(addr, data) { function updateServerDetail(addr, port, data) {
const [ver, icon, desc, capacity, count] = data; const [ver, icon, desc, capacity, count] = data;
for (let i = 0; i < serverModel.count; i++) { for (let i = 0; i < serverModel.count; i++) {
const item = serverModel.get(i); const item = serverModel.get(i);
const ip = item.serverIP; const ip = item.addr;
if (addr.endsWith(ip)) { // endsWithIPv6ip const itemPort = item.port;
if (addr === ip && port == itemPort) {
const ms = (new Date).getTime();
item.misMatchMsg = ""; item.misMatchMsg = "";
if (FkVersion !== ver) { if (FkVersion !== ver) {
item.misMatchMsg = qsTr("@VersionMismatch").arg(ver); item.misMatchMsg = qsTr("@VersionMismatch").arg(ver);
} }
item.delay = ms - item.delayBegin;
item.description = desc; item.description = desc;
item.favicon = icon; item.favicon = icon;
item.online = count.toString(); item.online = count.toString();
item.capacity = capacity.toString(); item.capacity = capacity.toString();
return;
} }
} }
} }
function loadConfig() { function loadConfig() {
if (serverModel.count > 0) { if (serverModel.count > 0) { return; }
return; const serverList = JSON.parse(Backend.getPublicServerList());
} serverList.unshift(...config.favoriteServers);
for (let key in config.savedPassword) { for (const server of serverList) {
let { addr, port, name, username, password } = server;
name = name ?? "";
username = username ?? "";
password = password ?? "";
if (port === -1) break;
if (!password && config.findFavorite(addr, port)) continue;
serverModel.append({ serverModel.append({
serverIP: key, addr, port, name, username, password,
misMatchMsg: "", misMatchMsg: "",
description: qsTr("Server not up"), description: qsTr("Server not up"),
online: "-", online: "?",
capacity: "-", capacity: "??",
favicon: "", favicon: "",
delayBegin: (new Date).getTime(),
delay: -1,
favorite: !!password,
lan: false,
}); });
Backend.getServerInfo(key); Backend.getServerInfo(addr, port);
} }
} }
} }

View File

@ -18,6 +18,7 @@ var EQUIP_ICON_DIR = AppPath + "/image/card/equipIcon/";
var PIXANIM_DIR = AppPath + "/image/anim/" var PIXANIM_DIR = AppPath + "/image/anim/"
var TILE_ICON_DIR = AppPath + "/image/button/tileicon/" var TILE_ICON_DIR = AppPath + "/image/button/tileicon/"
var LOBBY_IMG_DIR = AppPath + "/image/lobby/"; var LOBBY_IMG_DIR = AppPath + "/image/lobby/";
var MISC_DIR = AppPath + "/image/misc/";
const searchPkgResource = function(path, name, suffix) { const searchPkgResource = function(path, name, suffix) {
let ret; let ret;

View File

@ -3,8 +3,8 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android" <manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="org.notify.FreeKill" package="org.notify.FreeKill"
android:installLocation="preferExternal" android:installLocation="preferExternal"
android:versionCode="418" android:versionCode="419"
android:versionName="0.4.18"> android:versionName="0.4.19">
<uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />

View File

@ -1,7 +1,7 @@
{ {
"banwords": [], "banwords": [],
"description": "FreeKill Server", "description": "FreeKill Server",
"iconUrl": "https://img1.imgtp.com/2023/07/01/DGUdj8eu.png", "iconUrl": "default",
"capacity": 100, "capacity": 100,
"tempBanTime": 20, "tempBanTime": 20,
"motd": "Welcome!", "motd": "Welcome!",

BIN
image/misc/favorite.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

BIN
image/misc/server_icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

View File

@ -21,7 +21,7 @@
<message> <message>
<source>@VersionMismatch</source> <source>@VersionMismatch</source>
<translation>&lt;font color="red" size="4">&lt;b>Mismatch version: server is at v%1&lt;/b>&lt;/font></translation> <translation>&lt;font color="red" size="4">&lt;b>!! v%1&lt;/b>&lt;/font></translation>
</message> </message>
<message> <message>

View File

@ -242,7 +242,7 @@
<message> <message>
<source>@VersionMismatch</source> <source>@VersionMismatch</source>
<translation>&lt;font color="red" size="4">&lt;b>v%1&lt;/b>&lt;/font></translation> <translation>&lt;font color="red" size="4">&lt;b>!! v%1&lt;/b>&lt;/font></translation>
</message> </message>
<message> <message>
<source>Server not up</source> <source>Server not up</source>

View File

@ -386,7 +386,7 @@ void Server::readConfig() {
// defaults // defaults
SET_DEFAULT_CONFIG("description", "FreeKill Server"); SET_DEFAULT_CONFIG("description", "FreeKill Server");
SET_DEFAULT_CONFIG("iconUrl", "https://img1.imgtp.com/2023/07/01/DGUdj8eu.png"); SET_DEFAULT_CONFIG("iconUrl", "default");
SET_DEFAULT_CONFIG("capacity", 100); SET_DEFAULT_CONFIG("capacity", 100);
SET_DEFAULT_CONFIG("tempBanTime", 20); SET_DEFAULT_CONFIG("tempBanTime", 20);
SET_DEFAULT_CONFIG("motd", "Welcome!"); SET_DEFAULT_CONFIG("motd", "Welcome!");

View File

@ -210,7 +210,7 @@ void QmlBackend::startServer(ushort port) {
} }
} }
void QmlBackend::joinServer(QString address) { void QmlBackend::joinServer(QString address, ushort port) {
if (ClientInstance != nullptr) if (ClientInstance != nullptr)
return; return;
Client *client = new Client(this); Client *client = new Client(this);
@ -222,6 +222,7 @@ void QmlBackend::joinServer(QString address) {
emit notifyUI("ErrorMsg", msg); emit notifyUI("ErrorMsg", msg);
emit notifyUI("BackToStart", "[]"); emit notifyUI("BackToStart", "[]");
}); });
/*
QString addr = "127.0.0.1"; QString addr = "127.0.0.1";
ushort port = 9527u; ushort port = 9527u;
@ -232,7 +233,7 @@ void QmlBackend::joinServer(QString address) {
} else { } else {
addr = address; addr = address;
// SRV解析查询 // SRV解析查询
QDnsLookup* dns = new QDnsLookup(QDnsLookup::SRV, "_freekill._tcp." + addr); QDnsLookup *dns = new QDnsLookup(QDnsLookup::SRV, "_freekill._tcp." + addr);
QEventLoop eventLoop; QEventLoop eventLoop;
// 阻塞的SRV解析查询回调 // 阻塞的SRV解析查询回调
connect(dns, &QDnsLookup::finished,[&eventLoop](void){ connect(dns, &QDnsLookup::finished,[&eventLoop](void){
@ -250,8 +251,9 @@ void QmlBackend::joinServer(QString address) {
} }
} }
} }
*/
client->connectToHost(addr, port); client->connectToHost(address, port);
} }
void QmlBackend::quitLobby(bool close) { void QmlBackend::quitLobby(bool close) {
@ -327,6 +329,22 @@ QVariant QmlBackend::evalLuaExp(const QString &lua) {
return result; return result;
} }
QString QmlBackend::getPublicServerList() {
QFile conf("server-list.json");
// TODO: Download new JSON via http
if (!conf.exists()) {
conf.open(QIODevice::WriteOnly);
static const char *init_conf = "{}";
conf.write(init_conf);
conf.close();
return init_conf;
}
conf.open(QIODevice::ReadOnly);
auto ret = conf.readAll();
conf.close();
return ret;
}
QString QmlBackend::pubEncrypt(const QString &key, const QString &data) { QString QmlBackend::pubEncrypt(const QString &key, const QString &data) {
// 在用公钥加密口令时也随机生成AES密钥/IV并随着口令一起加密 // 在用公钥加密口令时也随机生成AES密钥/IV并随着口令一起加密
// AES密钥和IV都是固定16字节的所以可以放在开头 // AES密钥和IV都是固定16字节的所以可以放在开头
@ -473,21 +491,14 @@ void QmlBackend::detectServer() {
9527); 9527);
} }
void QmlBackend::getServerInfo(const QString &address) { void QmlBackend::getServerInfo(const QString &address, ushort port) {
QString addr = "127.0.0.1"; QString addr = address;
ushort port = 9527u; // ushort port = 9527u;
static const char *ask_str = "fkGetDetail,"; 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;
}
QByteArray ask(ask_str); QByteArray ask(ask_str);
ask.append(address.toLatin1()); ask.append(address.toLatin1());
ask.append(QString(",%1").arg(port).toUtf8());
if (QHostAddress(addr).isNull()) { // 不是ip考虑解析域名 if (QHostAddress(addr).isNull()) { // 不是ip考虑解析域名
QHostInfo::lookupHost(addr, this, [=](const QHostInfo &host) { QHostInfo::lookupHost(addr, this, [=](const QHostInfo &host) {

View File

@ -33,7 +33,7 @@ public:
void setEngine(QQmlApplicationEngine *engine); void setEngine(QQmlApplicationEngine *engine);
Q_INVOKABLE void startServer(ushort port); Q_INVOKABLE void startServer(ushort port);
Q_INVOKABLE void joinServer(QString address); Q_INVOKABLE void joinServer(QString address, ushort port = 9527);
// Lobby // Lobby
Q_INVOKABLE void quitLobby(bool close = true); Q_INVOKABLE void quitLobby(bool close = true);
@ -44,6 +44,7 @@ public:
QVariantList params); QVariantList params);
Q_INVOKABLE QVariant evalLuaExp(const QString &lua); Q_INVOKABLE QVariant evalLuaExp(const QString &lua);
Q_INVOKABLE QString getPublicServerList();
Q_INVOKABLE QString pubEncrypt(const QString &key, const QString &data); Q_INVOKABLE QString pubEncrypt(const QString &key, const QString &data);
Q_INVOKABLE QString loadConf(); Q_INVOKABLE QString loadConf();
Q_INVOKABLE QString loadTips(); Q_INVOKABLE QString loadTips();
@ -62,7 +63,7 @@ public:
Q_INVOKABLE void createModBackend(); Q_INVOKABLE void createModBackend();
Q_INVOKABLE void detectServer(); Q_INVOKABLE void detectServer();
Q_INVOKABLE void getServerInfo(const QString &addr); Q_INVOKABLE void getServerInfo(const QString &addr, ushort port = 9527u);
Q_INVOKABLE void showDialog(const QString &type, const QString &text, Q_INVOKABLE void showDialog(const QString &type, const QString &text,
const QString &orig = QString()); const QString &orig = QString());