Misc (#169)
- 扣上限心碎 - 进服维护的各种跟后端稳定性有关的代码 - 断线重连/旁观时候计入技能次数 - ban人和banip,相应的也有解禁 - 开房设置现在可以滑动 - 完善网络错误报错 - 现在开始游戏之前需要等待和所有人准备 - 指示掉线之人和走小道之人 - 掉线和走小道的人不再被AI接管 - 延时锦囊牌素材从拓展包找 - 拓展包管理界面UI优化,下载失败的包可以在管理拓展包中删除
|
@ -24,6 +24,8 @@ freekill-wrap.cxx
|
||||||
/server/rsa_pub
|
/server/rsa_pub
|
||||||
/freekill.client.config.json
|
/freekill.client.config.json
|
||||||
/freekill.server.config.json
|
/freekill.server.config.json
|
||||||
|
/freekill.server.error.log
|
||||||
|
/freekill.server.info.log
|
||||||
/flist.txt
|
/flist.txt
|
||||||
|
|
||||||
# windeployqt
|
# windeployqt
|
||||||
|
|
|
@ -43,12 +43,22 @@ Flickable {
|
||||||
|
|
||||||
Button {
|
Button {
|
||||||
text: Backend.translate("Give Shoe")
|
text: Backend.translate("Give Shoe")
|
||||||
enabled: Math.random() < 0.5
|
enabled: Math.random() < 0.3
|
||||||
onClicked: {
|
onClicked: {
|
||||||
root.givePresent("Shoe");
|
root.givePresent("Shoe");
|
||||||
root.finish();
|
root.finish();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Button {
|
||||||
|
text: Backend.translate("Kick From Room")
|
||||||
|
visible: !roomScene.isStarted && roomScene.isOwner
|
||||||
|
enabled: pid !== Self.id
|
||||||
|
onClicked: {
|
||||||
|
ClientInstance.notifyServer("KickPlayer", pid.toString());
|
||||||
|
root.finish();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: player details
|
// TODO: player details
|
||||||
|
|
|
@ -4,194 +4,201 @@ import QtQuick
|
||||||
import QtQuick.Controls
|
import QtQuick.Controls
|
||||||
import QtQuick.Layouts
|
import QtQuick.Layouts
|
||||||
|
|
||||||
ColumnLayout {
|
Flickable {
|
||||||
RowLayout {
|
flickableDirection: Flickable.AutoFlickIfNeeded
|
||||||
anchors.rightMargin: 8
|
clip: true
|
||||||
spacing: 16
|
contentHeight: layout.height
|
||||||
Text {
|
|
||||||
text: Backend.translate("Room Name")
|
|
||||||
}
|
|
||||||
TextField {
|
|
||||||
id: roomName
|
|
||||||
maximumLength: 64
|
|
||||||
font.pixelSize: 18
|
|
||||||
text: Backend.translate("$RoomName").arg(Self.screenName)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
RowLayout {
|
ColumnLayout {
|
||||||
anchors.rightMargin: 8
|
id: layout
|
||||||
spacing: 16
|
RowLayout {
|
||||||
Text {
|
anchors.rightMargin: 8
|
||||||
text: Backend.translate("Player num")
|
spacing: 16
|
||||||
}
|
Text {
|
||||||
SpinBox {
|
text: Backend.translate("Room Name")
|
||||||
id: playerNum
|
}
|
||||||
from: 2
|
TextField {
|
||||||
to: 8
|
id: roomName
|
||||||
value: config.preferedPlayerNum
|
maximumLength: 64
|
||||||
|
font.pixelSize: 18
|
||||||
onValueChanged: {
|
text: Backend.translate("$RoomName").arg(Self.screenName)
|
||||||
config.preferedPlayerNum = value;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
anchors.rightMargin: 8
|
anchors.rightMargin: 8
|
||||||
spacing: 16
|
spacing: 16
|
||||||
Text {
|
Text {
|
||||||
text: Backend.translate("Game Mode")
|
text: Backend.translate("Player num")
|
||||||
}
|
|
||||||
ComboBox {
|
|
||||||
id: gameModeCombo
|
|
||||||
textRole: "name"
|
|
||||||
model: ListModel {
|
|
||||||
id: gameModeList
|
|
||||||
}
|
}
|
||||||
|
SpinBox {
|
||||||
|
id: playerNum
|
||||||
|
from: 2
|
||||||
|
to: 8
|
||||||
|
value: config.preferedPlayerNum
|
||||||
|
|
||||||
onCurrentIndexChanged: {
|
onValueChanged: {
|
||||||
let data = gameModeList.get(currentIndex);
|
config.preferedPlayerNum = value;
|
||||||
playerNum.from = data.minPlayer;
|
}
|
||||||
playerNum.to = data.maxPlayer;
|
|
||||||
|
|
||||||
config.preferedMode = data.orig_name;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
anchors.rightMargin: 8
|
anchors.rightMargin: 8
|
||||||
spacing: 16
|
spacing: 16
|
||||||
Text {
|
Text {
|
||||||
text: Backend.translate("Select general num")
|
text: Backend.translate("Game Mode")
|
||||||
}
|
|
||||||
SpinBox {
|
|
||||||
id: generalNum
|
|
||||||
from: 3
|
|
||||||
to: 18
|
|
||||||
value: config.preferredGeneralNum
|
|
||||||
|
|
||||||
onValueChanged: {
|
|
||||||
config.preferredGeneralNum = value;
|
|
||||||
}
|
}
|
||||||
}
|
ComboBox {
|
||||||
}
|
id: gameModeCombo
|
||||||
|
textRole: "name"
|
||||||
RowLayout {
|
model: ListModel {
|
||||||
anchors.rightMargin: 8
|
id: gameModeList
|
||||||
spacing: 16
|
|
||||||
Text {
|
|
||||||
text: Backend.translate("Operation timeout")
|
|
||||||
}
|
|
||||||
SpinBox {
|
|
||||||
from: 10
|
|
||||||
to: 60
|
|
||||||
editable: true
|
|
||||||
value: config.preferredTimeout
|
|
||||||
|
|
||||||
onValueChanged: {
|
|
||||||
config.preferredTimeout = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
RowLayout {
|
|
||||||
anchors.rightMargin: 8
|
|
||||||
spacing: 16
|
|
||||||
Text {
|
|
||||||
text: Backend.translate("Luck Card Times")
|
|
||||||
}
|
|
||||||
SpinBox {
|
|
||||||
from: 0
|
|
||||||
to: 8
|
|
||||||
value: config.preferredLuckTime
|
|
||||||
|
|
||||||
onValueChanged: {
|
|
||||||
config.preferredLuckTime = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
RowLayout {
|
|
||||||
anchors.rightMargin: 8
|
|
||||||
spacing: 16
|
|
||||||
Text {
|
|
||||||
text: Backend.translate("Room Password")
|
|
||||||
}
|
|
||||||
TextField {
|
|
||||||
id: roomPassword
|
|
||||||
maximumLength: 16
|
|
||||||
font.pixelSize: 18
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Switch {
|
|
||||||
id: freeAssignCheck
|
|
||||||
checked: Debugging ? true : false
|
|
||||||
text: Backend.translate("Enable free assign")
|
|
||||||
}
|
|
||||||
|
|
||||||
Switch {
|
|
||||||
id: deputyCheck
|
|
||||||
checked: Debugging ? true : false
|
|
||||||
text: Backend.translate("Enable deputy general")
|
|
||||||
}
|
|
||||||
|
|
||||||
RowLayout {
|
|
||||||
anchors.rightMargin: 8
|
|
||||||
spacing: 16
|
|
||||||
Button {
|
|
||||||
text: Backend.translate("OK")
|
|
||||||
onClicked: {
|
|
||||||
root.finished();
|
|
||||||
mainWindow.busy = true;
|
|
||||||
|
|
||||||
let disabledGenerals = config.disabledGenerals.slice();
|
|
||||||
if (disabledGenerals.length) {
|
|
||||||
const availablePack = JSON.parse(Backend.callLuaFunction("GetAllGeneralPack", [])).
|
|
||||||
filter((pack) => !config.disabledPack.includes(pack));
|
|
||||||
disabledGenerals = disabledGenerals.filter((general) => {
|
|
||||||
return availablePack.find((pack) => JSON.parse(Backend.callLuaFunction("GetGenerals", [pack])).includes(general));
|
|
||||||
});
|
|
||||||
|
|
||||||
disabledGenerals = Array.from(new Set(disabledGenerals));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ClientInstance.notifyServer(
|
onCurrentIndexChanged: {
|
||||||
"CreateRoom",
|
let data = gameModeList.get(currentIndex);
|
||||||
JSON.stringify([roomName.text, playerNum.value, config.preferredTimeout, {
|
playerNum.from = data.minPlayer;
|
||||||
enableFreeAssign: freeAssignCheck.checked,
|
playerNum.to = data.maxPlayer;
|
||||||
enableDeputy: deputyCheck.checked,
|
|
||||||
gameMode: config.preferedMode,
|
|
||||||
disabledPack: config.disabledPack,
|
|
||||||
generalNum: config.preferredGeneralNum,
|
|
||||||
luckTime: config.preferredLuckTime,
|
|
||||||
password: roomPassword.text,
|
|
||||||
disabledGenerals,
|
|
||||||
}])
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Button {
|
|
||||||
text: Backend.translate("Cancel")
|
|
||||||
onClicked: {
|
|
||||||
root.finished();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Component.onCompleted: {
|
config.preferedMode = data.orig_name;
|
||||||
let mode_data = JSON.parse(Backend.callLuaFunction("GetGameModes", []));
|
}
|
||||||
let i = 0;
|
|
||||||
for (let d of mode_data) {
|
|
||||||
gameModeList.append(d);
|
|
||||||
if (d.orig_name == config.preferedMode) {
|
|
||||||
gameModeCombo.currentIndex = i;
|
|
||||||
}
|
}
|
||||||
i += 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
playerNum.value = config.preferedPlayerNum;
|
RowLayout {
|
||||||
|
anchors.rightMargin: 8
|
||||||
|
spacing: 16
|
||||||
|
Text {
|
||||||
|
text: Backend.translate("Select general num")
|
||||||
|
}
|
||||||
|
SpinBox {
|
||||||
|
id: generalNum
|
||||||
|
from: 3
|
||||||
|
to: 18
|
||||||
|
value: config.preferredGeneralNum
|
||||||
|
|
||||||
|
onValueChanged: {
|
||||||
|
config.preferredGeneralNum = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
anchors.rightMargin: 8
|
||||||
|
spacing: 16
|
||||||
|
Text {
|
||||||
|
text: Backend.translate("Operation timeout")
|
||||||
|
}
|
||||||
|
SpinBox {
|
||||||
|
from: 10
|
||||||
|
to: 60
|
||||||
|
editable: true
|
||||||
|
value: config.preferredTimeout
|
||||||
|
|
||||||
|
onValueChanged: {
|
||||||
|
config.preferredTimeout = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
anchors.rightMargin: 8
|
||||||
|
spacing: 16
|
||||||
|
Text {
|
||||||
|
text: Backend.translate("Luck Card Times")
|
||||||
|
}
|
||||||
|
SpinBox {
|
||||||
|
from: 0
|
||||||
|
to: 8
|
||||||
|
value: config.preferredLuckTime
|
||||||
|
|
||||||
|
onValueChanged: {
|
||||||
|
config.preferredLuckTime = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
anchors.rightMargin: 8
|
||||||
|
spacing: 16
|
||||||
|
Text {
|
||||||
|
text: Backend.translate("Room Password")
|
||||||
|
}
|
||||||
|
TextField {
|
||||||
|
id: roomPassword
|
||||||
|
maximumLength: 16
|
||||||
|
font.pixelSize: 18
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Switch {
|
||||||
|
id: freeAssignCheck
|
||||||
|
checked: Debugging ? true : false
|
||||||
|
text: Backend.translate("Enable free assign")
|
||||||
|
}
|
||||||
|
|
||||||
|
Switch {
|
||||||
|
id: deputyCheck
|
||||||
|
checked: Debugging ? true : false
|
||||||
|
text: Backend.translate("Enable deputy general")
|
||||||
|
}
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
anchors.rightMargin: 8
|
||||||
|
spacing: 16
|
||||||
|
Button {
|
||||||
|
text: Backend.translate("OK")
|
||||||
|
onClicked: {
|
||||||
|
root.finished();
|
||||||
|
mainWindow.busy = true;
|
||||||
|
|
||||||
|
let disabledGenerals = config.disabledGenerals.slice();
|
||||||
|
if (disabledGenerals.length) {
|
||||||
|
const availablePack = JSON.parse(Backend.callLuaFunction("GetAllGeneralPack", [])).
|
||||||
|
filter((pack) => !config.disabledPack.includes(pack));
|
||||||
|
disabledGenerals = disabledGenerals.filter((general) => {
|
||||||
|
return availablePack.find((pack) => JSON.parse(Backend.callLuaFunction("GetGenerals", [pack])).includes(general));
|
||||||
|
});
|
||||||
|
|
||||||
|
disabledGenerals = Array.from(new Set(disabledGenerals));
|
||||||
|
}
|
||||||
|
|
||||||
|
ClientInstance.notifyServer(
|
||||||
|
"CreateRoom",
|
||||||
|
JSON.stringify([roomName.text, playerNum.value, config.preferredTimeout, {
|
||||||
|
enableFreeAssign: freeAssignCheck.checked,
|
||||||
|
enableDeputy: deputyCheck.checked,
|
||||||
|
gameMode: config.preferedMode,
|
||||||
|
disabledPack: config.disabledPack,
|
||||||
|
generalNum: config.preferredGeneralNum,
|
||||||
|
luckTime: config.preferredLuckTime,
|
||||||
|
password: roomPassword.text,
|
||||||
|
disabledGenerals,
|
||||||
|
}])
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Button {
|
||||||
|
text: Backend.translate("Cancel")
|
||||||
|
onClicked: {
|
||||||
|
root.finished();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Component.onCompleted: {
|
||||||
|
let mode_data = JSON.parse(Backend.callLuaFunction("GetGameModes", []));
|
||||||
|
let i = 0;
|
||||||
|
for (let d of mode_data) {
|
||||||
|
gameModeList.append(d);
|
||||||
|
if (d.orig_name == config.preferedMode) {
|
||||||
|
gameModeCombo.currentIndex = i;
|
||||||
|
}
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
playerNum.value = config.preferedPlayerNum;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,195 +6,170 @@ import QtQuick.Layouts
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
id: root
|
id: root
|
||||||
Button {
|
|
||||||
text: qsTr("Quit")
|
|
||||||
anchors.right: parent.right
|
|
||||||
onClicked: {
|
|
||||||
mainStack.pop();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Component {
|
ToolBar {
|
||||||
id: packageDelegate
|
id: bar
|
||||||
|
width: parent.width
|
||||||
Item {
|
RowLayout {
|
||||||
height: 22
|
anchors.fill: parent
|
||||||
width: packageList.width
|
ToolButton {
|
||||||
|
icon.source: AppPath + "/image/modmaker/back"
|
||||||
RowLayout {
|
onClicked: mainStack.pop();
|
||||||
anchors.fill: parent
|
|
||||||
spacing: 16
|
|
||||||
Text {
|
|
||||||
font.pixelSize: 20
|
|
||||||
text: pkgName
|
|
||||||
}
|
|
||||||
|
|
||||||
Text {
|
|
||||||
font.pixelSize: 20
|
|
||||||
Layout.fillWidth: true
|
|
||||||
horizontalAlignment: Text.AlignHCenter
|
|
||||||
text: pkgURL
|
|
||||||
}
|
|
||||||
|
|
||||||
Text {
|
|
||||||
font.pixelSize: 20
|
|
||||||
text: pkgVersion
|
|
||||||
}
|
|
||||||
|
|
||||||
Text {
|
|
||||||
font.pixelSize: 20
|
|
||||||
color: pkgEnabled === "1" ? "green" : "red"
|
|
||||||
text: pkgEnabled === "1" ? qsTr("Enabled") : qsTr("Disabled")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
Label {
|
||||||
|
text: qsTr("Package Manager")
|
||||||
|
horizontalAlignment: Qt.AlignHCenter
|
||||||
|
Layout.fillWidth: true
|
||||||
|
}
|
||||||
|
ToolButton {
|
||||||
|
icon.source: AppPath + "/image/modmaker/menu"
|
||||||
|
onClicked: menu.open()
|
||||||
|
|
||||||
TapHandler {
|
Menu {
|
||||||
onTapped: {
|
id: menu
|
||||||
if (packageList.currentIndex === index) {
|
y: bar.height
|
||||||
packageList.currentIndex = -1;
|
|
||||||
} else {
|
MenuItem {
|
||||||
packageList.currentIndex = index;
|
text: qsTr("Enable All")
|
||||||
|
onTriggered: {
|
||||||
|
for (let i = 0; i < packageModel.count; i++) {
|
||||||
|
let name = packageModel.get(i).pkgName;
|
||||||
|
Pacman.enablePack(name);
|
||||||
|
}
|
||||||
|
updatePackageList();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
MenuItem {
|
||||||
|
text: qsTr("Disable All")
|
||||||
|
onTriggered: {
|
||||||
|
for (let i = 0; i < packageModel.count; i++) {
|
||||||
|
let name = packageModel.get(i).pkgName;
|
||||||
|
Pacman.disablePack(name);
|
||||||
|
}
|
||||||
|
updatePackageList();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
MenuItem {
|
||||||
|
text: qsTr("Upgrade All")
|
||||||
|
onTriggered: {
|
||||||
|
for (let i = 0; i < packageModel.count; i++) {
|
||||||
|
let name = packageModel.get(i).pkgName;
|
||||||
|
Pacman.upgradePack(name);
|
||||||
|
}
|
||||||
|
updatePackageList();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ListModel {
|
Rectangle {
|
||||||
id: packageModel
|
width: parent.width
|
||||||
}
|
height: parent.height - bar.height - urlInstaller.height
|
||||||
|
anchors.top: bar.bottom
|
||||||
|
color: "snow"
|
||||||
|
opacity: 0.75
|
||||||
|
clip: true
|
||||||
|
|
||||||
ColumnLayout {
|
ListView {
|
||||||
anchors.fill: parent
|
id: packageList
|
||||||
|
anchors.fill: parent
|
||||||
RowLayout {
|
model: ListModel {
|
||||||
Layout.fillHeight: true
|
id: packageModel
|
||||||
Layout.alignment: Qt.AlignHCenter
|
|
||||||
Item {
|
|
||||||
Layout.preferredWidth: root.width * 0.9
|
|
||||||
Layout.fillHeight: true
|
|
||||||
Rectangle {
|
|
||||||
anchors.fill: parent
|
|
||||||
color: "#88EEEEEE"
|
|
||||||
}
|
}
|
||||||
ListView {
|
delegate: ItemDelegate {
|
||||||
id: packageList
|
width: root.width
|
||||||
anchors.fill: parent
|
height: 64
|
||||||
|
|
||||||
contentHeight: packageDelegate.height * count
|
ColumnLayout {
|
||||||
ScrollBar.vertical: ScrollBar {}
|
anchors.fill: parent
|
||||||
header: RowLayout {
|
anchors.margins: 8
|
||||||
height: 22
|
|
||||||
width: packageList.width
|
|
||||||
spacing: 16
|
|
||||||
Text {
|
Text {
|
||||||
font.pixelSize: 20
|
text: "<b>" + pkgName + "</b> (" + pkgVersion + ")"
|
||||||
text: qsTr("Name")
|
font.pixelSize: 18
|
||||||
|
textFormat: Text.RichText
|
||||||
|
color: pkgEnabled === "1" ? "black" : "grey"
|
||||||
}
|
}
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
font.pixelSize: 20
|
text: pkgURL
|
||||||
Layout.fillWidth: true
|
color: pkgEnabled === "1" ? "black" : "grey"
|
||||||
horizontalAlignment: Text.AlignHCenter
|
|
||||||
text: "URL"
|
|
||||||
}
|
|
||||||
|
|
||||||
Text {
|
|
||||||
font.pixelSize: 20
|
|
||||||
text: qsTr("Version")
|
|
||||||
}
|
|
||||||
|
|
||||||
Text {
|
|
||||||
font.pixelSize: 20
|
|
||||||
text: qsTr("Enable")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
delegate: packageDelegate
|
|
||||||
model: packageModel
|
|
||||||
highlight: Rectangle { color: "lightsteelblue"; radius: 5 }
|
|
||||||
Component.onCompleted: { currentIndex = -1; }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ColumnLayout {
|
Button {
|
||||||
Button {
|
id: enableBtn
|
||||||
enabled: packageList.currentItem
|
text: pkgEnabled === "0" ? qsTr("Enable") : qsTr("Disable")
|
||||||
text: qsTr("Enable")
|
anchors.right: upgradeBtn.left
|
||||||
onClicked: {
|
anchors.rightMargin: 8
|
||||||
let idx = packageList.currentIndex;
|
onClicked: {
|
||||||
let name = packageModel.get(idx).pkgName;
|
if (pkgEnabled === "0") {
|
||||||
Pacman.enablePack(name);
|
Pacman.enablePack(pkgName);
|
||||||
updatePackageList();
|
} else {
|
||||||
packageList.currentIndex = idx;
|
Pacman.disablePack(pkgName);
|
||||||
|
}
|
||||||
|
updatePackageList();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
Button {
|
Button {
|
||||||
enabled: packageList.currentItem
|
id: upgradeBtn
|
||||||
text: qsTr("Disable")
|
text: qsTr("Upgrade")
|
||||||
onClicked: {
|
anchors.right: delBtn.left
|
||||||
let idx = packageList.currentIndex;
|
anchors.rightMargin: 8
|
||||||
let name = packageModel.get(idx).pkgName;
|
onClicked: {
|
||||||
Pacman.disablePack(name);
|
Pacman.upgradePack(pkgName);
|
||||||
updatePackageList();
|
updatePackageList();
|
||||||
packageList.currentIndex = idx;
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
Button {
|
Button {
|
||||||
enabled: packageList.currentItem
|
id: delBtn
|
||||||
text: qsTr("Upgrade")
|
text: qsTr("Remove")
|
||||||
onClicked: {
|
anchors.right: parent.right
|
||||||
let idx = packageList.currentIndex;
|
anchors.rightMargin: 8
|
||||||
let name = packageModel.get(idx).pkgName;
|
onClicked: {
|
||||||
Pacman.upgradePack(name);
|
Pacman.removePack(pkgName);
|
||||||
updatePackageList();
|
updatePackageList();
|
||||||
packageList.currentIndex = idx;
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
Button {
|
|
||||||
enabled: packageList.currentItem
|
|
||||||
text: qsTr("Remove")
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
let idx = packageList.currentIndex;
|
Backend.copyToClipboard(pkgURL);
|
||||||
let name = packageModel.get(idx).pkgName;
|
toast.show(qsTr("Copied %1.").arg(pkgURL));
|
||||||
Pacman.removePack(name);
|
|
||||||
updatePackageList();
|
|
||||||
packageList.currentIndex = idx;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Button {
|
|
||||||
enabled: packageList.currentItem
|
|
||||||
text: qsTr("Copy URL")
|
|
||||||
onClicked: {
|
|
||||||
let idx = packageList.currentIndex;
|
|
||||||
let name = packageModel.get(idx).pkgURL;
|
|
||||||
Backend.copyToClipboard(name);
|
|
||||||
toast.show(qsTr("Copied."));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
RowLayout {
|
Rectangle {
|
||||||
Layout.fillWidth: true
|
id: urlInstaller
|
||||||
TextField {
|
width: parent.width
|
||||||
id: urlEdit
|
height: childrenRect.height
|
||||||
Layout.fillWidth: true
|
color: "snow"
|
||||||
clip: true
|
opacity: 0.75
|
||||||
}
|
anchors.bottom: parent.bottom
|
||||||
|
|
||||||
Button {
|
RowLayout {
|
||||||
text: qsTr("Install From URL")
|
width: parent.width
|
||||||
enabled: urlEdit.text !== ""
|
TextField {
|
||||||
onClicked: {
|
id: urlEdit
|
||||||
let url = urlEdit.text;
|
Layout.fillWidth: true
|
||||||
mainWindow.busy = true;
|
clip: true
|
||||||
Pacman.downloadNewPack(url, true);
|
}
|
||||||
|
|
||||||
|
Button {
|
||||||
|
text: qsTr("Install From URL")
|
||||||
|
enabled: urlEdit.text !== ""
|
||||||
|
onClicked: {
|
||||||
|
let url = urlEdit.text;
|
||||||
|
mainWindow.busy = true;
|
||||||
|
Pacman.downloadNewPack(url, true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
function updatePackageList() {
|
function updatePackageList() {
|
||||||
packageModel.clear();
|
packageModel.clear();
|
||||||
let data = JSON.parse(Pacman.listPackages());
|
let data = JSON.parse(Pacman.listPackages());
|
||||||
|
|
|
@ -17,6 +17,9 @@ Item {
|
||||||
|
|
||||||
property bool isOwner: false
|
property bool isOwner: false
|
||||||
property bool isStarted: false
|
property bool isStarted: false
|
||||||
|
property bool isFull: false
|
||||||
|
property bool isAllReady: false
|
||||||
|
property bool isReady: false
|
||||||
|
|
||||||
property alias popupBox: popupBox
|
property alias popupBox: popupBox
|
||||||
property alias manualBox: manualBox
|
property alias manualBox: manualBox
|
||||||
|
@ -79,13 +82,36 @@ Item {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Button {
|
Button {
|
||||||
text: "add robot"
|
text: Backend.translate("Add Robot")
|
||||||
visible: isOwner && !isStarted
|
visible: isOwner && !isStarted && !isFull
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
onClicked: {
|
onClicked: {
|
||||||
ClientInstance.notifyServer("AddRobot", "[]");
|
ClientInstance.notifyServer("AddRobot", "[]");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Button {
|
||||||
|
text: Backend.translate("Start Game")
|
||||||
|
visible: isOwner && !isStarted && isFull
|
||||||
|
enabled: isAllReady
|
||||||
|
anchors.centerIn: parent
|
||||||
|
onClicked: {
|
||||||
|
ClientInstance.notifyServer("StartGame", "[]");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Timer {
|
||||||
|
id: opTimer
|
||||||
|
interval: 1000
|
||||||
|
}
|
||||||
|
Button {
|
||||||
|
text: isReady ? Backend.translate("Cancel Ready") : Backend.translate("Ready")
|
||||||
|
visible: !isOwner && !isStarted
|
||||||
|
enabled: !opTimer.running
|
||||||
|
anchors.centerIn: parent
|
||||||
|
onClicked: {
|
||||||
|
opTimer.start();
|
||||||
|
ClientInstance.notifyServer("Ready", "");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
states: [
|
states: [
|
||||||
State { name: "notactive" }, // Normal status
|
State { name: "notactive" }, // Normal status
|
||||||
|
@ -195,6 +221,7 @@ Item {
|
||||||
Photo {
|
Photo {
|
||||||
playerid: model.id
|
playerid: model.id
|
||||||
general: model.general
|
general: model.general
|
||||||
|
avatar: model.avatar
|
||||||
deputyGeneral: model.deputyGeneral
|
deputyGeneral: model.deputyGeneral
|
||||||
screenName: model.screenName
|
screenName: model.screenName
|
||||||
role: model.role
|
role: model.role
|
||||||
|
@ -210,6 +237,7 @@ Item {
|
||||||
chained: model.chained
|
chained: model.chained
|
||||||
drank: model.drank
|
drank: model.drank
|
||||||
isOwner: model.isOwner
|
isOwner: model.isOwner
|
||||||
|
ready: model.ready
|
||||||
|
|
||||||
onSelectedChanged: {
|
onSelectedChanged: {
|
||||||
Logic.updateSelectedTargets(playerid, selected);
|
Logic.updateSelectedTargets(playerid, selected);
|
||||||
|
@ -830,6 +858,36 @@ Item {
|
||||||
cheatDrawer.open();
|
cheatDrawer.open();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function resetToInit() {
|
||||||
|
let datalist = [];
|
||||||
|
for (let i = 0; i < photoModel.count; i++) {
|
||||||
|
let item = photoModel.get(i);
|
||||||
|
if (item.id > 0) {
|
||||||
|
datalist.push({
|
||||||
|
id: item.id,
|
||||||
|
avatar: item.avatar,
|
||||||
|
name: item.screenName,
|
||||||
|
isOwner: item.isOwner,
|
||||||
|
ready: item.ready,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mainStack.pop();
|
||||||
|
mainStack.push(room);
|
||||||
|
mainStack.currentItem.loadPlayerData(datalist);
|
||||||
|
}
|
||||||
|
|
||||||
|
function loadPlayerData(datalist) {
|
||||||
|
datalist.forEach(d => {
|
||||||
|
if (d.id == Self.id) {
|
||||||
|
roomScene.isOwner = d.isOwner;
|
||||||
|
} else {
|
||||||
|
callbacks["AddPlayer"](JSON.stringify([d.id, d.name, d.avatar, d.ready]));
|
||||||
|
}
|
||||||
|
Logic.getPhotoModel(d.id).isOwner = d.isOwner;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
Component.onCompleted: {
|
Component.onCompleted: {
|
||||||
toast.show(Backend.translate("$EnterRoom"));
|
toast.show(Backend.translate("$EnterRoom"));
|
||||||
playerNum = config.roomCapacity;
|
playerNum = config.roomCapacity;
|
||||||
|
@ -839,6 +897,7 @@ Item {
|
||||||
id: i ? -1 : Self.id,
|
id: i ? -1 : Self.id,
|
||||||
index: i, // For animating seat swap
|
index: i, // For animating seat swap
|
||||||
general: i ? "" : Self.avatar,
|
general: i ? "" : Self.avatar,
|
||||||
|
avatar: i ? "" : Self.avatar,
|
||||||
deputyGeneral: "",
|
deputyGeneral: "",
|
||||||
screenName: i ? "" : Self.screenName,
|
screenName: i ? "" : Self.screenName,
|
||||||
role: "unknown",
|
role: "unknown",
|
||||||
|
@ -853,7 +912,8 @@ Item {
|
||||||
faceup: true,
|
faceup: true,
|
||||||
chained: false,
|
chained: false,
|
||||||
drank: 0,
|
drank: 0,
|
||||||
isOwner: false
|
isOwner: false,
|
||||||
|
ready: false,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -334,7 +334,7 @@ function changeSelf(id) {
|
||||||
}
|
}
|
||||||
|
|
||||||
callbacks["AddPlayer"] = function(jsonData) {
|
callbacks["AddPlayer"] = function(jsonData) {
|
||||||
// jsonData: int id, string screenName, string avatar
|
// jsonData: int id, string screenName, string avatar, bool ready
|
||||||
for (let i = 0; i < photoModel.count; i++) {
|
for (let i = 0; i < photoModel.count; i++) {
|
||||||
let item = photoModel.get(i);
|
let item = photoModel.get(i);
|
||||||
if (item.id === -1) {
|
if (item.id === -1) {
|
||||||
|
@ -342,9 +342,22 @@ callbacks["AddPlayer"] = function(jsonData) {
|
||||||
let uid = data[0];
|
let uid = data[0];
|
||||||
let name = data[1];
|
let name = data[1];
|
||||||
let avatar = data[2];
|
let avatar = data[2];
|
||||||
|
let ready = data[3];
|
||||||
|
|
||||||
item.id = uid;
|
item.id = uid;
|
||||||
item.screenName = name;
|
item.screenName = name;
|
||||||
item.general = avatar;
|
item.general = avatar;
|
||||||
|
item.avatar = avatar;
|
||||||
|
item.ready = ready;
|
||||||
|
|
||||||
|
checkAllReady();
|
||||||
|
|
||||||
|
if (getPhoto(-1)) {
|
||||||
|
roomScene.isFull = false;
|
||||||
|
} else {
|
||||||
|
roomScene.isFull = true;
|
||||||
|
}
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -485,6 +498,8 @@ callbacks["RemovePlayer"] = function(jsonData) {
|
||||||
model.id = -1;
|
model.id = -1;
|
||||||
model.screenName = "";
|
model.screenName = "";
|
||||||
model.general = "";
|
model.general = "";
|
||||||
|
model.isOwner = false;
|
||||||
|
roomScene.isFull = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -492,9 +507,7 @@ callbacks["RoomOwner"] = function(jsonData) {
|
||||||
// jsonData: int uid of the owner
|
// jsonData: int uid of the owner
|
||||||
let uid = JSON.parse(jsonData)[0];
|
let uid = JSON.parse(jsonData)[0];
|
||||||
|
|
||||||
if (Self.id === uid) {
|
roomScene.isOwner = (Self.id === uid);
|
||||||
roomScene.isOwner = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
let model = getPhotoModel(uid);
|
let model = getPhotoModel(uid);
|
||||||
if (typeof(model) !== "undefined") {
|
if (typeof(model) !== "undefined") {
|
||||||
|
@ -502,6 +515,46 @@ callbacks["RoomOwner"] = function(jsonData) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function checkAllReady() {
|
||||||
|
let allReady = true;
|
||||||
|
for (let i = 0; i < photoModel.count; i++) {
|
||||||
|
let item = photoModel.get(i);
|
||||||
|
if (!item.isOwner && !item.ready) {
|
||||||
|
allReady = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
roomScene.isAllReady = allReady;
|
||||||
|
}
|
||||||
|
|
||||||
|
callbacks["ReadyChanged"] = (j) => {
|
||||||
|
const data = JSON.parse(j);
|
||||||
|
const id = data[0];
|
||||||
|
const ready = data[1];
|
||||||
|
|
||||||
|
if (id === Self.id) {
|
||||||
|
roomScene.isReady = ready === 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
let model = getPhotoModel(id);
|
||||||
|
if (typeof(model) !== "undefined") {
|
||||||
|
model.ready = ready ? true : false;
|
||||||
|
checkAllReady();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
callbacks["NetStateChanged"] = (j) => {
|
||||||
|
const data = JSON.parse(j);
|
||||||
|
const id = data[0];
|
||||||
|
let state = data[1];
|
||||||
|
|
||||||
|
let model = getPhotoModel(id);
|
||||||
|
if (state == "run" && model.dead) {
|
||||||
|
state = "leave";
|
||||||
|
}
|
||||||
|
model.netstate = state;
|
||||||
|
}
|
||||||
|
|
||||||
callbacks["PropertyUpdate"] = function(jsonData) {
|
callbacks["PropertyUpdate"] = function(jsonData) {
|
||||||
// jsonData: int id, string property_name, value
|
// jsonData: int id, string property_name, value
|
||||||
let data = JSON.parse(jsonData);
|
let data = JSON.parse(jsonData);
|
||||||
|
@ -510,6 +563,7 @@ callbacks["PropertyUpdate"] = function(jsonData) {
|
||||||
let value = data[2];
|
let value = data[2];
|
||||||
|
|
||||||
let model = getPhotoModel(uid);
|
let model = getPhotoModel(uid);
|
||||||
|
|
||||||
if (typeof(model) !== "undefined") {
|
if (typeof(model) !== "undefined") {
|
||||||
model[property_name] = value;
|
model[property_name] = value;
|
||||||
}
|
}
|
||||||
|
@ -520,6 +574,7 @@ callbacks["StartGame"] = function(jsonData) {
|
||||||
|
|
||||||
for (let i = 0; i < photoModel.count; i++) {
|
for (let i = 0; i < photoModel.count; i++) {
|
||||||
let item = photoModel.get(i);
|
let item = photoModel.get(i);
|
||||||
|
item.ready = false;
|
||||||
item.general = "";
|
item.general = "";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1013,6 +1068,12 @@ callbacks["LogEvent"] = function(jsonData) {
|
||||||
Backend.playSound("./audio/system/losehp");
|
Backend.playSound("./audio/system/losehp");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case "ChangeMaxHp": {
|
||||||
|
if (data.num < 0) {
|
||||||
|
Backend.playSound("./audio/system/losemaxhp");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
case "PlaySkillSound": {
|
case "PlaySkillSound": {
|
||||||
let skill = data.name;
|
let skill = data.name;
|
||||||
let extension = data.extension;
|
let extension = data.extension;
|
||||||
|
@ -1042,7 +1103,7 @@ callbacks["GameOver"] = function(jsonData) {
|
||||||
roomScene.popupBox.sourceComponent = Qt.createComponent("../RoomElement/GameOverBox.qml");
|
roomScene.popupBox.sourceComponent = Qt.createComponent("../RoomElement/GameOverBox.qml");
|
||||||
let box = roomScene.popupBox.item;
|
let box = roomScene.popupBox.item;
|
||||||
box.winner = jsonData;
|
box.winner = jsonData;
|
||||||
roomScene.isStarted = false;
|
// roomScene.isStarted = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
callbacks["FillAG"] = (j) => {
|
callbacks["FillAG"] = (j) => {
|
||||||
|
|
|
@ -25,7 +25,7 @@ Item {
|
||||||
Image {
|
Image {
|
||||||
height: 55 * 0.8
|
height: 55 * 0.8
|
||||||
width: 47 * 0.8
|
width: 47 * 0.8
|
||||||
source: SkinBank.DELAYED_TRICK_DIR + name
|
source: SkinBank.getDelayedTrickPicture(name) // SkinBank.DELAYED_TRICK_DIR + name
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,7 +18,8 @@ GraphicsBox {
|
||||||
}
|
}
|
||||||
|
|
||||||
id: root
|
id: root
|
||||||
title.text: Backend.translate("$ChooseGeneral").arg(choiceNum)
|
title.text: Backend.translate("$ChooseGeneral").arg(choiceNum) +
|
||||||
|
(config.enableFreeAssign ? "(" + Backend.translate("Enable free assign") + ")" : "")
|
||||||
width: generalArea.width + body.anchors.leftMargin + body.anchors.rightMargin
|
width: generalArea.width + body.anchors.leftMargin + body.anchors.rightMargin
|
||||||
height: body.implicitHeight + body.anchors.topMargin + body.anchors.bottomMargin
|
height: body.implicitHeight + body.anchors.topMargin + body.anchors.bottomMargin
|
||||||
|
|
||||||
|
|
|
@ -22,6 +22,16 @@ GraphicsBox {
|
||||||
color: "#E4D5A0"
|
color: "#E4D5A0"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MetroButton {
|
||||||
|
text: Backend.translate("Back To Room")
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
|
||||||
|
onClicked: {
|
||||||
|
roomScene.resetToInit();
|
||||||
|
finished();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
MetroButton {
|
MetroButton {
|
||||||
text: Backend.translate("Back To Lobby")
|
text: Backend.translate("Back To Lobby")
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
|
|
@ -13,6 +13,7 @@ Item {
|
||||||
scale: 0.75
|
scale: 0.75
|
||||||
property int playerid: 0
|
property int playerid: 0
|
||||||
property string general: ""
|
property string general: ""
|
||||||
|
property string avatar: ""
|
||||||
property string deputyGeneral: ""
|
property string deputyGeneral: ""
|
||||||
property string screenName: ""
|
property string screenName: ""
|
||||||
property string role: "unknown"
|
property string role: "unknown"
|
||||||
|
@ -29,6 +30,7 @@ Item {
|
||||||
property bool chained: false
|
property bool chained: false
|
||||||
property int drank: 0
|
property int drank: 0
|
||||||
property bool isOwner: false
|
property bool isOwner: false
|
||||||
|
property bool ready: false
|
||||||
property int distance: 0
|
property int distance: 0
|
||||||
property string status: "normal"
|
property string status: "normal"
|
||||||
property int maxCard: 0
|
property int maxCard: 0
|
||||||
|
@ -249,7 +251,7 @@ Item {
|
||||||
anchors.right: parent.right
|
anchors.right: parent.right
|
||||||
anchors.bottomMargin: 8
|
anchors.bottomMargin: 8
|
||||||
anchors.rightMargin: 4
|
anchors.rightMargin: 4
|
||||||
source: SkinBank.PHOTO_DIR + (isOwner ? "owner" : "ready")
|
source: SkinBank.PHOTO_DIR + (isOwner ? "owner" : (ready ? "ready" : "notready"))
|
||||||
visible: screenName != "" && !roomScene.isStarted
|
visible: screenName != "" && !roomScene.isStarted
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -342,6 +344,8 @@ Item {
|
||||||
source: SkinBank.STATE_DIR + root.netstate
|
source: SkinBank.STATE_DIR + root.netstate
|
||||||
x: photoMask.x
|
x: photoMask.x
|
||||||
y: photoMask.y
|
y: photoMask.y
|
||||||
|
scale: 0.9
|
||||||
|
transformOrigin: Item.TopLeft
|
||||||
}
|
}
|
||||||
|
|
||||||
Image {
|
Image {
|
||||||
|
@ -596,6 +600,10 @@ Item {
|
||||||
}
|
}
|
||||||
|
|
||||||
function showDetail() {
|
function showDetail() {
|
||||||
|
if (playerid === 0 || playerid === -1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
roomScene.startCheat("PlayerDetail", { photo: this });
|
roomScene.startCheat("PlayerDetail", { photo: this });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -53,6 +53,22 @@ function getCardPicture(cidOrName) {
|
||||||
return CARD_DIR + "unknown.png";
|
return CARD_DIR + "unknown.png";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getDelayedTrickPicture(name) {
|
||||||
|
let extension = Backend.callLuaFunction("GetCardExtensionByName", [name]);
|
||||||
|
|
||||||
|
let path = AppPath + "/packages/" + extension + "/image/card/delayedTrick/" + name + ".png";
|
||||||
|
if (Backend.exists(path)) {
|
||||||
|
return path;
|
||||||
|
} else {
|
||||||
|
for (let dir of Backend.ls(AppPath + "/packages/")) {
|
||||||
|
path = AppPath + "/packages/" + dir + "/image/card/delayedTrick/" + name + ".png";
|
||||||
|
if (Backend.exists(path)) return path;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return DELAYED_TRICK_DIR + "unknown.png";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
function getEquipIcon(cid, icon) {
|
function getEquipIcon(cid, icon) {
|
||||||
let data = JSON.parse(Backend.callLuaFunction("GetCardData", [cid]));
|
let data = JSON.parse(Backend.callLuaFunction("GetCardData", [cid]));
|
||||||
let extension = data.extension;
|
let extension = data.extension;
|
||||||
|
|
Before Width: | Height: | Size: 2.9 KiB After Width: | Height: | Size: 2.9 KiB |
After Width: | Height: | Size: 112 B |
After Width: | Height: | Size: 4.3 KiB |
|
@ -15,7 +15,59 @@
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Socket access error</source>
|
<source>Socket access error</source>
|
||||||
<translation>套接字访问错误</translation>
|
<translation>无权访问套接字文件</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Socket resource error</source>
|
||||||
|
<translation>系统资源不足</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Socket timeout error</source>
|
||||||
|
<translation>连接超时</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Datagram too large error</source>
|
||||||
|
<translation>报文过长</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Network error</source>
|
||||||
|
<translation>网络错误</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Unsupprted socket operation</source>
|
||||||
|
<translation>不支持的套接字操作</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Unfinished socket operation</source>
|
||||||
|
<translation>未完成的套接字操作</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Proxy auth error</source>
|
||||||
|
<translation>代理服务器认证失败</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Proxy refused</source>
|
||||||
|
<translation>代理服务器拒绝连接</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Proxy closed</source>
|
||||||
|
<translation>代理服务器已关闭连接</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Proxy timeout</source>
|
||||||
|
<translation>代理服务器连接超时</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Proxy protocol error</source>
|
||||||
|
<translation>代理服务器协议错误</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Operation error</source>
|
||||||
|
<translation>不允许的操作</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Temporary error</source>
|
||||||
|
<translation>网络暂时出现故障,请稍后重试</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Unknown error</source>
|
<source>Unknown error</source>
|
||||||
|
@ -187,14 +239,14 @@
|
||||||
<source>no such room</source>
|
<source>no such room</source>
|
||||||
<translation>房间不存在</translation>
|
<translation>房间不存在</translation>
|
||||||
</message>
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>you have been banned!</source>
|
||||||
|
<translation>你已经被该服务器封禁!</translation>
|
||||||
|
</message>
|
||||||
</context>
|
</context>
|
||||||
|
|
||||||
<context>
|
<context>
|
||||||
<name>PackageManage</name>
|
<name>PackageManage</name>
|
||||||
<message>
|
|
||||||
<source>Quit</source>
|
|
||||||
<translation>退出</translation>
|
|
||||||
</message>
|
|
||||||
<message>
|
<message>
|
||||||
<source>Install From URL</source>
|
<source>Install From URL</source>
|
||||||
<translation>从URL安装</translation>
|
<translation>从URL安装</translation>
|
||||||
|
@ -207,14 +259,6 @@
|
||||||
<source>Disable</source>
|
<source>Disable</source>
|
||||||
<translation>禁用</translation>
|
<translation>禁用</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
|
||||||
<source>Enabled</source>
|
|
||||||
<translation>已启用</translation>
|
|
||||||
</message>
|
|
||||||
<message>
|
|
||||||
<source>Disabled</source>
|
|
||||||
<translation>已禁用</translation>
|
|
||||||
</message>
|
|
||||||
<message>
|
<message>
|
||||||
<source>Remove</source>
|
<source>Remove</source>
|
||||||
<translation>删除</translation>
|
<translation>删除</translation>
|
||||||
|
@ -224,20 +268,24 @@
|
||||||
<translation>更新</translation>
|
<translation>更新</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Name</source>
|
<source>Copied %1.</source>
|
||||||
<translation>名称</translation>
|
<translation>已复制到剪贴板。(%1)</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Version</source>
|
<source>Package Manager</source>
|
||||||
<translation>版本</translation>
|
<translation>新月杀拓展包管理器</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Copy URL</source>
|
<source>Enable All</source>
|
||||||
<translation>复制URL</translation>
|
<translation>全部启用</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Copied.</source>
|
<source>Disable All</source>
|
||||||
<translation>已复制。</translation>
|
<translation>全部禁用</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Upgrade All</source>
|
||||||
|
<translation>全部更新</translation>
|
||||||
</message>
|
</message>
|
||||||
</context>
|
</context>
|
||||||
|
|
||||||
|
|
|
@ -34,6 +34,10 @@ Fk:loadTranslationTable{
|
||||||
["Luck Card Times"] = "手气卡次数",
|
["Luck Card Times"] = "手气卡次数",
|
||||||
["Room Password"] = "房间密码",
|
["Room Password"] = "房间密码",
|
||||||
["Please input room's password"] = "请输入房间的密码",
|
["Please input room's password"] = "请输入房间的密码",
|
||||||
|
["Add Robot"] = "添加机器人",
|
||||||
|
["Start Game"] = "开始游戏",
|
||||||
|
["Ready"] = "准备",
|
||||||
|
["Cancel Ready"] = "取消准备",
|
||||||
["Game Mode"] = "游戏模式",
|
["Game Mode"] = "游戏模式",
|
||||||
["Enable free assign"] = "自由选将",
|
["Enable free assign"] = "自由选将",
|
||||||
["Enable deputy general"] = "启用副将机制",
|
["Enable deputy general"] = "启用副将机制",
|
||||||
|
@ -45,6 +49,7 @@ Fk:loadTranslationTable{
|
||||||
["Give Flower"] = "送花",
|
["Give Flower"] = "送花",
|
||||||
["Give Egg"] = "砸蛋",
|
["Give Egg"] = "砸蛋",
|
||||||
["Give Shoe"] = "拖鞋",
|
["Give Shoe"] = "拖鞋",
|
||||||
|
["Kick From Room"] = "踢出房间",
|
||||||
|
|
||||||
["$OnlineInfo"] = "大厅人数:%1,总在线人数:%2",
|
["$OnlineInfo"] = "大厅人数:%1,总在线人数:%2",
|
||||||
|
|
||||||
|
@ -206,6 +211,7 @@ FreeKill使用的是libgit2的C API,与此同时使用Git完成拓展包的下
|
||||||
["$GameOver"] = "游戏结束",
|
["$GameOver"] = "游戏结束",
|
||||||
["$Winner"] = "%1 获胜",
|
["$Winner"] = "%1 获胜",
|
||||||
["$NoWinner"] = "平局!",
|
["$NoWinner"] = "平局!",
|
||||||
|
["Back To Room"] = "回到房间",
|
||||||
["Back To Lobby"] = "返回大厅",
|
["Back To Lobby"] = "返回大厅",
|
||||||
|
|
||||||
["Bulletin Info"] = [==[<h2>v0.2.0 更新说明</h2>
|
["Bulletin Info"] = [==[<h2>v0.2.0 更新说明</h2>
|
||||||
|
|
|
@ -102,12 +102,23 @@ GameEvent.functions[GameEvent.DrawInitial] = function(self)
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
|
|
||||||
if table.every(room:getTag("LuckCardData").playerList, function(id)
|
-- local ldata = room:getTag("LuckCardData")
|
||||||
return room:getTag("LuckCardData")[id].luckTime == 0
|
local ldata = luck_data
|
||||||
|
|
||||||
|
if table.every(ldata.playerList, function(id)
|
||||||
|
return ldata[id].luckTime == 0
|
||||||
end) then
|
end) then
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
|
|
||||||
|
for _, id in ipairs(ldata.playerList) do
|
||||||
|
if room:getPlayerById(id)._splayer:getStateString() ~= "online" then
|
||||||
|
ldata[id].luckTime = 0
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- room:setTag("LuckCardData", ldata)
|
||||||
|
|
||||||
checkNoHuman(room)
|
checkNoHuman(room)
|
||||||
|
|
||||||
coroutine.yield("__handleRequest", (remainTime - elapsed) * 1000)
|
coroutine.yield("__handleRequest", (remainTime - elapsed) * 1000)
|
||||||
|
|
|
@ -242,6 +242,10 @@ GameEvent.functions[GameEvent.ChangeMaxHp] = function(self)
|
||||||
|
|
||||||
player.maxHp = math.max(player.maxHp + num, 0)
|
player.maxHp = math.max(player.maxHp + num, 0)
|
||||||
self:broadcastProperty(player, "maxHp")
|
self:broadcastProperty(player, "maxHp")
|
||||||
|
self:sendLogEvent("ChangeMaxHp", {
|
||||||
|
player = player.id,
|
||||||
|
num = num,
|
||||||
|
})
|
||||||
if player.maxHp == 0 then
|
if player.maxHp == 0 then
|
||||||
self:killPlayer({ who = player.id })
|
self:killPlayer({ who = player.id })
|
||||||
end
|
end
|
||||||
|
|
|
@ -70,7 +70,9 @@ end
|
||||||
local request_handlers = {}
|
local request_handlers = {}
|
||||||
request_handlers["reconnect"] = function(room, id, reqlist)
|
request_handlers["reconnect"] = function(room, id, reqlist)
|
||||||
local p = room:getPlayerById(id)
|
local p = room:getPlayerById(id)
|
||||||
p:reconnect()
|
if p then
|
||||||
|
p:reconnect()
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
request_handlers["observe"] = function(room, id, reqlist)
|
request_handlers["observe"] = function(room, id, reqlist)
|
||||||
|
|
|
@ -102,6 +102,11 @@ local function _waitForReply(player, timeout)
|
||||||
local start = os.getms()
|
local start = os.getms()
|
||||||
local state = player.serverplayer:getStateString()
|
local state = player.serverplayer:getStateString()
|
||||||
if state ~= "online" then
|
if state ~= "online" then
|
||||||
|
if state ~= "robot" then
|
||||||
|
checkNoHuman(player.room)
|
||||||
|
player.room:delay(500)
|
||||||
|
return "__cancel"
|
||||||
|
end
|
||||||
-- Let AI make reply. First handle request
|
-- Let AI make reply. First handle request
|
||||||
local ret_msg = true
|
local ret_msg = true
|
||||||
while ret_msg do
|
while ret_msg do
|
||||||
|
@ -259,6 +264,12 @@ function ServerPlayer:marshal(player)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
for k, v in pairs(self.skillUsedHistory) do
|
||||||
|
if v[1] > 0 then
|
||||||
|
player:doNotify("AddSkillUseHistory", json.encode{self.id, k, v[1]})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
if self.role_shown then
|
if self.role_shown then
|
||||||
room:notifyProperty(player, self, "role")
|
room:notifyProperty(player, self, "role")
|
||||||
end
|
end
|
||||||
|
|
Before Width: | Height: | Size: 2.8 KiB After Width: | Height: | Size: 2.8 KiB |
Before Width: | Height: | Size: 3.0 KiB After Width: | Height: | Size: 3.0 KiB |
After Width: | Height: | Size: 2.9 KiB |
|
@ -5,8 +5,8 @@
|
||||||
#include "clientplayer.h"
|
#include "clientplayer.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
|
|
||||||
Client *ClientInstance;
|
Client *ClientInstance = nullptr;
|
||||||
ClientPlayer *Self;
|
ClientPlayer *Self = nullptr;
|
||||||
|
|
||||||
static ClientPlayer dummyPlayer(0, nullptr);
|
static ClientPlayer dummyPlayer(0, nullptr);
|
||||||
|
|
||||||
|
|
|
@ -82,10 +82,16 @@ void PackMan::loadSummary(const QString &jsonData, bool useThread) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void PackMan::downloadNewPack(const QString &url, bool useThread) {
|
void PackMan::downloadNewPack(const QString &url, bool useThread) {
|
||||||
|
static auto sql_select = QString("SELECT name FROM packages \
|
||||||
|
WHERE name = '%1';");
|
||||||
|
static auto sql_update = QString("INSERT INTO packages (name,url,hash,enabled) \
|
||||||
|
VALUES ('%1','%2','%3',1);");
|
||||||
|
|
||||||
auto threadFunc = [=]() {
|
auto threadFunc = [=]() {
|
||||||
int error = clone(url);
|
int error = clone(url);
|
||||||
if (error < 0)
|
// if (error < 0)
|
||||||
return;
|
// return;
|
||||||
|
|
||||||
auto u = url;
|
auto u = url;
|
||||||
while (u.endsWith('/')) {
|
while (u.endsWith('/')) {
|
||||||
u.chop(1);
|
u.chop(1);
|
||||||
|
@ -94,15 +100,11 @@ void PackMan::downloadNewPack(const QString &url, bool useThread) {
|
||||||
if (fileName.endsWith(".git"))
|
if (fileName.endsWith(".git"))
|
||||||
fileName.chop(4);
|
fileName.chop(4);
|
||||||
|
|
||||||
auto result = SelectFromDatabase(db, QString("SELECT name FROM packages \
|
auto result = SelectFromDatabase(db, sql_select.arg(fileName));
|
||||||
WHERE name = '%1';")
|
|
||||||
.arg(fileName));
|
|
||||||
if (result.isEmpty()) {
|
if (result.isEmpty()) {
|
||||||
ExecSQL(db, QString("INSERT INTO packages (name,url,hash,enabled) \
|
ExecSQL(db, sql_update.arg(fileName)
|
||||||
VALUES ('%1','%2','%3',1);")
|
|
||||||
.arg(fileName)
|
|
||||||
.arg(url)
|
.arg(url)
|
||||||
.arg(head(fileName)));
|
.arg(error < 0 ? "XXXXXXXX" : head(fileName)));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
if (useThread) {
|
if (useThread) {
|
||||||
|
|
52
src/main.cpp
|
@ -97,12 +97,32 @@ static void prepareForLinux() {
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
static FILE *info_log = nullptr;
|
||||||
|
static FILE *err_log = nullptr;
|
||||||
|
|
||||||
void fkMsgHandler(QtMsgType type, const QMessageLogContext &context,
|
void fkMsgHandler(QtMsgType type, const QMessageLogContext &context,
|
||||||
const QString &msg) {
|
const QString &msg) {
|
||||||
auto date = QDate::currentDate();
|
auto date = QDate::currentDate();
|
||||||
|
|
||||||
|
FILE *file;
|
||||||
|
switch (type) {
|
||||||
|
case QtDebugMsg:
|
||||||
|
case QtInfoMsg:
|
||||||
|
file = info_log;
|
||||||
|
break;
|
||||||
|
case QtWarningMsg:
|
||||||
|
case QtCriticalMsg:
|
||||||
|
case QtFatalMsg:
|
||||||
|
file = err_log;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
fprintf(stderr, "\r%02d/%02d ", date.month(), date.day());
|
fprintf(stderr, "\r%02d/%02d ", date.month(), date.day());
|
||||||
fprintf(stderr, "%s ",
|
fprintf(stderr, "%s ",
|
||||||
QTime::currentTime().toString("hh:mm:ss").toLatin1().constData());
|
QTime::currentTime().toString("hh:mm:ss").toLatin1().constData());
|
||||||
|
fprintf(file, "\r%02d/%02d ", date.month(), date.day());
|
||||||
|
fprintf(file, "%s ",
|
||||||
|
QTime::currentTime().toString("hh:mm:ss").toLatin1().constData());
|
||||||
|
|
||||||
auto localMsg = msg.toUtf8();
|
auto localMsg = msg.toUtf8();
|
||||||
auto threadName = QThread::currentThread()->objectName().toLatin1();
|
auto threadName = QThread::currentThread()->objectName().toLatin1();
|
||||||
|
@ -111,19 +131,27 @@ void fkMsgHandler(QtMsgType type, const QMessageLogContext &context,
|
||||||
case QtDebugMsg:
|
case QtDebugMsg:
|
||||||
fprintf(stderr, "%s[D] %s\n", threadName.constData(),
|
fprintf(stderr, "%s[D] %s\n", threadName.constData(),
|
||||||
localMsg.constData());
|
localMsg.constData());
|
||||||
|
fprintf(file, "%s[D] %s\n", threadName.constData(),
|
||||||
|
localMsg.constData());
|
||||||
break;
|
break;
|
||||||
case QtInfoMsg:
|
case QtInfoMsg:
|
||||||
fprintf(stderr, "%s[%s] %s\n", threadName.constData(),
|
fprintf(stderr, "%s[%s] %s\n", threadName.constData(),
|
||||||
Color("I", Green).toUtf8().constData(), localMsg.constData());
|
Color("I", Green).toUtf8().constData(), localMsg.constData());
|
||||||
|
fprintf(file, "%s[%s] %s\n", threadName.constData(),
|
||||||
|
"I", localMsg.constData());
|
||||||
break;
|
break;
|
||||||
case QtWarningMsg:
|
case QtWarningMsg:
|
||||||
fprintf(stderr, "%s[%s] %s\n", threadName.constData(),
|
fprintf(stderr, "%s[%s] %s\n", threadName.constData(),
|
||||||
Color("W", Yellow, Bold).toUtf8().constData(),
|
Color("W", Yellow, Bold).toUtf8().constData(),
|
||||||
localMsg.constData());
|
localMsg.constData());
|
||||||
|
fprintf(file, "%s[%s] %s\n", threadName.constData(),
|
||||||
|
"W", localMsg.constData());
|
||||||
break;
|
break;
|
||||||
case QtCriticalMsg:
|
case QtCriticalMsg:
|
||||||
fprintf(stderr, "%s[%s] %s\n", threadName.constData(),
|
fprintf(stderr, "%s[%s] %s\n", threadName.constData(),
|
||||||
Color("C", Red, Bold).toUtf8().constData(), localMsg.constData());
|
Color("C", Red, Bold).toUtf8().constData(), localMsg.constData());
|
||||||
|
fprintf(file, "%s[%s] %s\n", threadName.constData(),
|
||||||
|
"C", localMsg.constData());
|
||||||
#ifndef FK_SERVER_ONLY
|
#ifndef FK_SERVER_ONLY
|
||||||
if (Backend != nullptr) {
|
if (Backend != nullptr) {
|
||||||
Backend->notifyUI(
|
Backend->notifyUI(
|
||||||
|
@ -135,6 +163,8 @@ void fkMsgHandler(QtMsgType type, const QMessageLogContext &context,
|
||||||
case QtFatalMsg:
|
case QtFatalMsg:
|
||||||
fprintf(stderr, "%s[%s] %s\n", threadName.constData(),
|
fprintf(stderr, "%s[%s] %s\n", threadName.constData(),
|
||||||
Color("E", Red, Bold).toUtf8().constData(), localMsg.constData());
|
Color("E", Red, Bold).toUtf8().constData(), localMsg.constData());
|
||||||
|
fprintf(file, "%s[%s] %s\n", threadName.constData(),
|
||||||
|
"E", localMsg.constData());
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -143,6 +173,19 @@ void fkMsgHandler(QtMsgType type, const QMessageLogContext &context,
|
||||||
int main(int argc, char *argv[]) {
|
int main(int argc, char *argv[]) {
|
||||||
// 初始化一下各种杂项信息
|
// 初始化一下各种杂项信息
|
||||||
QThread::currentThread()->setObjectName("Main");
|
QThread::currentThread()->setObjectName("Main");
|
||||||
|
if (!info_log) {
|
||||||
|
info_log = fopen("freekill.server.info.log", "a+");
|
||||||
|
if (!info_log) {
|
||||||
|
qFatal("Cannot open info.log");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!err_log) {
|
||||||
|
err_log = fopen("freekill.server.error.log", "a+");
|
||||||
|
if (!err_log) {
|
||||||
|
qFatal("Cannot open error.log");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
qInstallMessageHandler(fkMsgHandler);
|
qInstallMessageHandler(fkMsgHandler);
|
||||||
QCoreApplication *app;
|
QCoreApplication *app;
|
||||||
QCoreApplication::setApplicationName("FreeKill");
|
QCoreApplication::setApplicationName("FreeKill");
|
||||||
|
@ -313,6 +356,15 @@ int main(int argc, char *argv[]) {
|
||||||
);
|
);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
if (info_log) {
|
||||||
|
fclose(info_log);
|
||||||
|
info_log = nullptr;
|
||||||
|
}
|
||||||
|
if (err_log) {
|
||||||
|
fclose(err_log);
|
||||||
|
info_log = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
#include "client_socket.h"
|
#include "client_socket.h"
|
||||||
#include <openssl/aes.h>
|
#include <openssl/aes.h>
|
||||||
|
#include <qabstractsocket.h>
|
||||||
#include <qrandom.h>
|
#include <qrandom.h>
|
||||||
|
|
||||||
ClientSocket::ClientSocket() : socket(new QTcpSocket(this)) {
|
ClientSocket::ClientSocket() : socket(new QTcpSocket(this)) {
|
||||||
|
@ -96,8 +97,45 @@ void ClientSocket::raiseError(QAbstractSocket::SocketError socket_error) {
|
||||||
case QAbstractSocket::SocketAccessError:
|
case QAbstractSocket::SocketAccessError:
|
||||||
reason = tr("Socket access error");
|
reason = tr("Socket access error");
|
||||||
break;
|
break;
|
||||||
|
case QAbstractSocket::SocketResourceError:
|
||||||
|
reason = tr("Socket resource error");
|
||||||
|
break;
|
||||||
|
case QAbstractSocket::SocketTimeoutError:
|
||||||
|
reason = tr("Socket timeout error");
|
||||||
|
break;
|
||||||
|
case QAbstractSocket::DatagramTooLargeError:
|
||||||
|
reason = tr("Datagram too large error");
|
||||||
|
break;
|
||||||
case QAbstractSocket::NetworkError:
|
case QAbstractSocket::NetworkError:
|
||||||
return; // this error is ignored ...
|
reason = tr("Network error");
|
||||||
|
break;
|
||||||
|
case QAbstractSocket::UnsupportedSocketOperationError:
|
||||||
|
reason = tr("Unsupprted socket operation");
|
||||||
|
break;
|
||||||
|
case QAbstractSocket::UnfinishedSocketOperationError:
|
||||||
|
reason = tr("Unfinished socket operation");
|
||||||
|
break;
|
||||||
|
case QAbstractSocket::ProxyAuthenticationRequiredError:
|
||||||
|
reason = tr("Proxy auth error");
|
||||||
|
break;
|
||||||
|
case QAbstractSocket::ProxyConnectionRefusedError:
|
||||||
|
reason = tr("Proxy refused");
|
||||||
|
break;
|
||||||
|
case QAbstractSocket::ProxyConnectionClosedError:
|
||||||
|
reason = tr("Proxy closed");
|
||||||
|
break;
|
||||||
|
case QAbstractSocket::ProxyConnectionTimeoutError:
|
||||||
|
reason = tr("Proxy timeout");
|
||||||
|
break;
|
||||||
|
case QAbstractSocket::ProxyProtocolError:
|
||||||
|
reason = tr("Proxy protocol error");
|
||||||
|
break;
|
||||||
|
case QAbstractSocket::OperationError:
|
||||||
|
reason = tr("Operation error");
|
||||||
|
break;
|
||||||
|
case QAbstractSocket::TemporaryError:
|
||||||
|
reason = tr("Temporary error");
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
reason = tr("Unknown error");
|
reason = tr("Unknown error");
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -276,6 +276,16 @@ void Router::handlePacket(const QByteArray &rawPacket) {
|
||||||
room->removePlayer(player);
|
room->removePlayer(player);
|
||||||
} else if (command == "AddRobot") {
|
} else if (command == "AddRobot") {
|
||||||
room->addRobot(player);
|
room->addRobot(player);
|
||||||
|
} else if (command == "KickPlayer") {
|
||||||
|
int i = jsonData.toInt();
|
||||||
|
auto p = room->findPlayer(i);
|
||||||
|
if (p) room->removePlayer(p);
|
||||||
|
} else if (command == "Ready") {
|
||||||
|
player->setReady(!player->isReady());
|
||||||
|
room->doBroadcastNotify(room->getPlayers(), "ReadyChanged",
|
||||||
|
QString("[%1,%2]").arg(player->getId()).arg(player->isReady()));
|
||||||
|
} else if (command == "StartGame") {
|
||||||
|
room->manuallyStart();
|
||||||
} else if (command == "Chat") {
|
} else if (command == "Chat") {
|
||||||
room->chat(player, jsonData);
|
room->chat(player, jsonData);
|
||||||
} else if (command == "PushRequest") {
|
} else if (command == "PushRequest") {
|
||||||
|
|
|
@ -114,6 +114,7 @@ void Room::addPlayer(ServerPlayer *player) {
|
||||||
jsonData << player->getId();
|
jsonData << player->getId();
|
||||||
jsonData << player->getScreenName();
|
jsonData << player->getScreenName();
|
||||||
jsonData << player->getAvatar();
|
jsonData << player->getAvatar();
|
||||||
|
jsonData << player->isReady();
|
||||||
doBroadcastNotify(getPlayers(), "AddPlayer", JsonArray2Bytes(jsonData));
|
doBroadcastNotify(getPlayers(), "AddPlayer", JsonArray2Bytes(jsonData));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -121,7 +122,13 @@ void Room::addPlayer(ServerPlayer *player) {
|
||||||
player->setRoom(this);
|
player->setRoom(this);
|
||||||
|
|
||||||
if (isLobby()) {
|
if (isLobby()) {
|
||||||
player->doNotify("EnterLobby", "[]");
|
// 有机器人进入大厅(可能因为被踢),那么改为销毁
|
||||||
|
if (player->getState() == Player::Robot) {
|
||||||
|
removePlayer(player);
|
||||||
|
player->deleteLater();
|
||||||
|
} else {
|
||||||
|
player->doNotify("EnterLobby", "[]");
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
// Second, let the player enter room and add other players
|
// Second, let the player enter room and add other players
|
||||||
jsonData = QJsonArray();
|
jsonData = QJsonArray();
|
||||||
|
@ -135,6 +142,7 @@ void Room::addPlayer(ServerPlayer *player) {
|
||||||
jsonData << p->getId();
|
jsonData << p->getId();
|
||||||
jsonData << p->getScreenName();
|
jsonData << p->getScreenName();
|
||||||
jsonData << p->getAvatar();
|
jsonData << p->getAvatar();
|
||||||
|
jsonData << p->isReady();
|
||||||
player->doNotify("AddPlayer", JsonArray2Bytes(jsonData));
|
player->doNotify("AddPlayer", JsonArray2Bytes(jsonData));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -144,8 +152,9 @@ void Room::addPlayer(ServerPlayer *player) {
|
||||||
player->doNotify("RoomOwner", JsonArray2Bytes(jsonData));
|
player->doNotify("RoomOwner", JsonArray2Bytes(jsonData));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isFull() && !gameStarted)
|
// 玩家手动启动
|
||||||
start();
|
// if (isFull() && !gameStarted)
|
||||||
|
// start();
|
||||||
}
|
}
|
||||||
emit playerAdded(player);
|
emit playerAdded(player);
|
||||||
}
|
}
|
||||||
|
@ -159,6 +168,7 @@ void Room::addRobot(ServerPlayer *player) {
|
||||||
robot->setId(robot_id);
|
robot->setId(robot_id);
|
||||||
robot->setAvatar("guanyu");
|
robot->setAvatar("guanyu");
|
||||||
robot->setScreenName(QString("COMP-%1").arg(robot_id));
|
robot->setScreenName(QString("COMP-%1").arg(robot_id));
|
||||||
|
robot->setReady(true);
|
||||||
robot_id--;
|
robot_id--;
|
||||||
|
|
||||||
// FIXME: 会触发Lobby:removePlayer
|
// FIXME: 会触发Lobby:removePlayer
|
||||||
|
@ -174,7 +184,7 @@ void Room::removePlayer(ServerPlayer *player) {
|
||||||
|
|
||||||
if (!gameStarted) {
|
if (!gameStarted) {
|
||||||
// 游戏还没开始的话,直接删除这名玩家
|
// 游戏还没开始的话,直接删除这名玩家
|
||||||
if (players.contains(player)) {
|
if (players.contains(player) && !players.isEmpty()) {
|
||||||
players.removeOne(player);
|
players.removeOne(player);
|
||||||
}
|
}
|
||||||
emit playerRemoved(player);
|
emit playerRemoved(player);
|
||||||
|
@ -213,9 +223,13 @@ void Room::removePlayer(ServerPlayer *player) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// 如果房间空了,就把房间标为废弃,Server有信号处理函数的
|
// 如果房间空了,就把房间标为废弃,Server有信号处理函数的
|
||||||
if (isAbandoned() && !m_abandoned) {
|
if (isAbandoned()) {
|
||||||
|
bool tmp = m_abandoned;
|
||||||
m_abandoned = true;
|
m_abandoned = true;
|
||||||
emit abandoned();
|
// 只释放一次信号就行了,他销毁机器人的时候会多次调用removePlayer
|
||||||
|
if (!tmp) {
|
||||||
|
emit abandoned();
|
||||||
|
}
|
||||||
} else if (player == owner) {
|
} else if (player == owner) {
|
||||||
setOwner(players.first());
|
setOwner(players.first());
|
||||||
}
|
}
|
||||||
|
@ -377,9 +391,19 @@ void Room::gameOver() {
|
||||||
}
|
}
|
||||||
// 旁观者不能在这清除,因为removePlayer逻辑不一样
|
// 旁观者不能在这清除,因为removePlayer逻辑不一样
|
||||||
// observers.clear();
|
// observers.clear();
|
||||||
players.clear();
|
// 玩家也不能在这里清除,因为要能返回原来房间继续玩呢
|
||||||
|
// players.clear();
|
||||||
|
// owner = nullptr;
|
||||||
clearRequest();
|
clearRequest();
|
||||||
owner = nullptr;
|
}
|
||||||
|
|
||||||
|
void Room::manuallyStart() {
|
||||||
|
if (isFull() && !gameStarted) {
|
||||||
|
foreach (auto p, players) {
|
||||||
|
p->setReady(false);
|
||||||
|
}
|
||||||
|
start();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
QString Room::fetchRequest() {
|
QString Room::fetchRequest() {
|
||||||
|
|
|
@ -59,6 +59,7 @@ class Room : public QThread {
|
||||||
void initLua();
|
void initLua();
|
||||||
|
|
||||||
void roomStart();
|
void roomStart();
|
||||||
|
void manuallyStart();
|
||||||
LuaFunction startGame;
|
LuaFunction startGame;
|
||||||
|
|
||||||
QString fetchRequest();
|
QString fetchRequest();
|
||||||
|
|
|
@ -19,7 +19,7 @@
|
||||||
#include "serverplayer.h"
|
#include "serverplayer.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
|
|
||||||
Server *ServerInstance;
|
Server *ServerInstance = nullptr;
|
||||||
|
|
||||||
Server::Server(QObject *parent) : QObject(parent) {
|
Server::Server(QObject *parent) : QObject(parent) {
|
||||||
ServerInstance = this;
|
ServerInstance = this;
|
||||||
|
@ -134,22 +134,36 @@ void Server::addPlayer(ServerPlayer *player) {
|
||||||
players.insert(id, player);
|
players.insert(id, player);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Server::removePlayer(int id) { players.remove(id); }
|
void Server::removePlayer(int id) {
|
||||||
|
if (players[id]) {
|
||||||
|
players.remove(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void Server::updateRoomList() {
|
void Server::updateRoomList() {
|
||||||
QJsonArray arr;
|
QJsonArray arr;
|
||||||
|
QJsonArray avail_arr;
|
||||||
foreach (Room *room, rooms) {
|
foreach (Room *room, rooms) {
|
||||||
QJsonArray obj;
|
QJsonArray obj;
|
||||||
auto settings = QJsonDocument::fromJson(room->getSettings());
|
auto settings = QJsonDocument::fromJson(room->getSettings());
|
||||||
auto password = settings["password"].toString();
|
auto password = settings["password"].toString();
|
||||||
|
auto count = room->getPlayers().count(); // playerNum
|
||||||
|
auto cap = room->getCapacity(); // capacity
|
||||||
|
|
||||||
obj << room->getId(); // roomId
|
obj << room->getId(); // roomId
|
||||||
obj << room->getName(); // roomName
|
obj << room->getName(); // roomName
|
||||||
obj << settings["gameMode"]; // gameMode
|
obj << settings["gameMode"]; // gameMode
|
||||||
obj << room->getPlayers().count(); // playerNum
|
obj << count;
|
||||||
obj << room->getCapacity(); // capacity
|
obj << cap;
|
||||||
obj << !password.isEmpty();
|
obj << !password.isEmpty();
|
||||||
arr << obj;
|
|
||||||
|
if (count == cap)
|
||||||
|
arr << obj;
|
||||||
|
else
|
||||||
|
avail_arr << obj;
|
||||||
|
}
|
||||||
|
foreach (auto v, avail_arr) {
|
||||||
|
arr.prepend(v);
|
||||||
}
|
}
|
||||||
auto jsonData = JsonArray2Bytes(arr);
|
auto jsonData = JsonArray2Bytes(arr);
|
||||||
lobby()->doBroadcastNotify(lobby()->getPlayers(), "UpdateRoomList",
|
lobby()->doBroadcastNotify(lobby()->getPlayers(), "UpdateRoomList",
|
||||||
|
@ -171,8 +185,22 @@ void Server::broadcast(const QString &command, const QString &jsonData) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Server::processNewConnection(ClientSocket *client) {
|
void Server::processNewConnection(ClientSocket *client) {
|
||||||
qInfo() << client->peerAddress() << "connected";
|
auto addr = client->peerAddress();
|
||||||
// version check, file check, ban IP, reconnect, etc
|
qInfo() << addr << "connected";
|
||||||
|
auto result = SelectFromDatabase(
|
||||||
|
db, QString("SELECT * FROM banip WHERE ip='%1';").arg(addr));
|
||||||
|
if (!result.isEmpty()) {
|
||||||
|
QJsonArray body;
|
||||||
|
body << -2;
|
||||||
|
body << (Router::TYPE_NOTIFICATION | Router::SRC_SERVER |
|
||||||
|
Router::DEST_CLIENT);
|
||||||
|
body << "ErrorMsg";
|
||||||
|
body << "you have been banned!";
|
||||||
|
client->send(JsonArray2Bytes(body));
|
||||||
|
qInfo() << "Refused banned IP:" << addr;
|
||||||
|
client->disconnectFromHost();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
connect(client, &ClientSocket::disconnected, this,
|
connect(client, &ClientSocket::disconnected, this,
|
||||||
[client]() { qInfo() << client->peerAddress() << "disconnected"; });
|
[client]() { qInfo() << client->peerAddress() << "disconnected"; });
|
||||||
|
@ -334,7 +362,10 @@ void Server::handleNameAndPassword(ClientSocket *client, const QString &name,
|
||||||
obj = result[0].toObject();
|
obj = result[0].toObject();
|
||||||
// check if this username already login
|
// check if this username already login
|
||||||
int id = obj["id"].toString().toInt();
|
int id = obj["id"].toString().toInt();
|
||||||
if (!players.value(id)) {
|
passed = obj["banned"].toString().toInt() == 0;
|
||||||
|
if (!passed) {
|
||||||
|
error_msg = "you have been banned!";
|
||||||
|
} else if (!players.value(id)) {
|
||||||
// check if password is the same
|
// check if password is the same
|
||||||
auto salt = obj["salt"].toString().toLatin1();
|
auto salt = obj["salt"].toString().toLatin1();
|
||||||
decrypted_pw.append(salt);
|
decrypted_pw.append(salt);
|
||||||
|
@ -351,12 +382,21 @@ void Server::handleNameAndPassword(ClientSocket *client, const QString &name,
|
||||||
player->setSocket(client);
|
player->setSocket(client);
|
||||||
player->alive = true;
|
player->alive = true;
|
||||||
client->disconnect(this);
|
client->disconnect(this);
|
||||||
broadcast("ServerMessage",
|
// broadcast("ServerMessage",
|
||||||
tr("%1 backed").arg(player->getScreenName()));
|
// tr("%1 backed").arg(player->getScreenName()));
|
||||||
room->pushRequest(QString("%1,reconnect").arg(id));
|
|
||||||
|
if (room && !room->isLobby()) {
|
||||||
|
room->pushRequest(QString("%1,reconnect").arg(id));
|
||||||
|
} else {
|
||||||
|
// 懒得处理掉线玩家在大厅了!踢掉得了
|
||||||
|
player->doNotify("ErrorMsg", "Unknown Error");
|
||||||
|
player->kicked();
|
||||||
|
}
|
||||||
|
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
error_msg = "others logged in with this name";
|
error_msg = "others logged in with this name";
|
||||||
|
passed = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -365,6 +405,13 @@ void Server::handleNameAndPassword(ClientSocket *client, const QString &name,
|
||||||
}
|
}
|
||||||
|
|
||||||
if (passed) {
|
if (passed) {
|
||||||
|
// update lastLoginIp
|
||||||
|
auto sql_update =
|
||||||
|
QString("UPDATE userinfo SET lastLoginIp='%1' WHERE id=%2;")
|
||||||
|
.arg(client->peerAddress())
|
||||||
|
.arg(obj["id"].toString().toInt());
|
||||||
|
ExecSQL(db, sql_update);
|
||||||
|
|
||||||
// create new ServerPlayer and setup
|
// create new ServerPlayer and setup
|
||||||
ServerPlayer *player = new ServerPlayer(lobby());
|
ServerPlayer *player = new ServerPlayer(lobby());
|
||||||
player->setSocket(client);
|
player->setSocket(client);
|
||||||
|
@ -375,7 +422,8 @@ void Server::handleNameAndPassword(ClientSocket *client, const QString &name,
|
||||||
player->setScreenName(name);
|
player->setScreenName(name);
|
||||||
player->setAvatar(obj["avatar"].toString());
|
player->setAvatar(obj["avatar"].toString());
|
||||||
player->setId(obj["id"].toString().toInt());
|
player->setId(obj["id"].toString().toInt());
|
||||||
broadcast("ServerMessage", tr("%1 logged in").arg(player->getScreenName()));
|
// broadcast("ServerMessage", tr("%1 logged
|
||||||
|
// in").arg(player->getScreenName()));
|
||||||
players.insert(player->getId(), player);
|
players.insert(player->getId(), player);
|
||||||
|
|
||||||
// tell the lobby player's basic property
|
// tell the lobby player's basic property
|
||||||
|
@ -410,6 +458,14 @@ void Server::onRoomAbandoned() {
|
||||||
updateRoomList();
|
updateRoomList();
|
||||||
// room->deleteLater();
|
// room->deleteLater();
|
||||||
idle_rooms.push(room);
|
idle_rooms.push(room);
|
||||||
|
// 懒得改了!
|
||||||
|
// 这里出bug的原因还是在于room的销毁工作没做好
|
||||||
|
// room销毁这块bug很多
|
||||||
|
// if (idle_rooms.length() > 10) {
|
||||||
|
// auto junk = idle_rooms[0];
|
||||||
|
// idle_rooms.removeFirst();
|
||||||
|
// junk->deleteLater();
|
||||||
|
// }
|
||||||
#ifdef QT_DEBUG
|
#ifdef QT_DEBUG
|
||||||
qDebug() << rooms.size() << "running room(s)," << idle_rooms.size()
|
qDebug() << rooms.size() << "running room(s)," << idle_rooms.size()
|
||||||
<< "idle room(s).";
|
<< "idle room(s).";
|
||||||
|
@ -419,7 +475,8 @@ void Server::onRoomAbandoned() {
|
||||||
void Server::onUserDisconnected() {
|
void Server::onUserDisconnected() {
|
||||||
ServerPlayer *player = qobject_cast<ServerPlayer *>(sender());
|
ServerPlayer *player = qobject_cast<ServerPlayer *>(sender());
|
||||||
qInfo() << "Player" << player->getId() << "disconnected";
|
qInfo() << "Player" << player->getId() << "disconnected";
|
||||||
broadcast("ServerMessage", tr("%1 logged out").arg(player->getScreenName()));
|
// broadcast("ServerMessage", tr("%1 logged
|
||||||
|
// out").arg(player->getScreenName()));
|
||||||
Room *room = player->getRoom();
|
Room *room = player->getRoom();
|
||||||
if (room->isStarted()) {
|
if (room->isStarted()) {
|
||||||
if (room->getObservers().contains(player)) {
|
if (room->getObservers().contains(player)) {
|
||||||
|
@ -435,7 +492,15 @@ void Server::onUserDisconnected() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Server::onUserStateChanged() {}
|
void Server::onUserStateChanged() {
|
||||||
|
ServerPlayer *player = qobject_cast<ServerPlayer *>(sender());
|
||||||
|
auto room = player->getRoom();
|
||||||
|
if (!room || room->isLobby() || room->isAbandoned()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
room->doBroadcastNotify(room->getPlayers(), "NetStateChanged",
|
||||||
|
QString("[%1,\"%2\"]").arg(player->getId()).arg(player->getStateString()));
|
||||||
|
}
|
||||||
|
|
||||||
RSA *Server::initServerRSA() {
|
RSA *Server::initServerRSA() {
|
||||||
RSA *rsa = RSA_new();
|
RSA *rsa = RSA_new();
|
||||||
|
@ -451,7 +516,8 @@ RSA *Server::initServerRSA() {
|
||||||
|
|
||||||
BIO_free_all(bp_pub);
|
BIO_free_all(bp_pub);
|
||||||
BIO_free_all(bp_pri);
|
BIO_free_all(bp_pri);
|
||||||
QFile("server/rsa").setPermissions(QFileDevice::ReadOwner | QFileDevice::WriteOwner);
|
QFile("server/rsa")
|
||||||
|
.setPermissions(QFileDevice::ReadOwner | QFileDevice::WriteOwner);
|
||||||
BN_free(bne);
|
BN_free(bne);
|
||||||
}
|
}
|
||||||
FILE *keyFile = fopen("server/rsa_pub", "r");
|
FILE *keyFile = fopen("server/rsa_pub", "r");
|
||||||
|
|
|
@ -20,15 +20,25 @@ static void sigintHandler(int) {
|
||||||
|
|
||||||
void Shell::helpCommand(QStringList &) {
|
void Shell::helpCommand(QStringList &) {
|
||||||
qInfo("Frequently used commands:");
|
qInfo("Frequently used commands:");
|
||||||
#define HELP_MSG(a, b) \
|
#define HELP_MSG(a, b) \
|
||||||
qInfo((a), Color((b), fkShell::Cyan).toUtf8().constData());
|
qInfo((a), Color((b), fkShell::Cyan).toUtf8().constData());
|
||||||
|
|
||||||
HELP_MSG("%s: Display this help message.", "help");
|
HELP_MSG("%s: Display this help message.", "help");
|
||||||
HELP_MSG("%s: Shut down the server.", "quit");
|
HELP_MSG("%s: Shut down the server.", "quit");
|
||||||
HELP_MSG("%s: List all online players.", "lsplayer");
|
HELP_MSG("%s: List all online players.", "lsplayer");
|
||||||
HELP_MSG("%s: List all running rooms.", "lsroom");
|
HELP_MSG("%s: List all running rooms.", "lsroom");
|
||||||
HELP_MSG("%s: Kick a player by his id.", "kick");
|
HELP_MSG("%s: Kick a player by his <id>.", "kick");
|
||||||
HELP_MSG("%s: Broadcast message.", "msg");
|
HELP_MSG("%s: Broadcast message.", "msg");
|
||||||
|
HELP_MSG("%s: Ban 1 or more accounts by their <name>.", "ban");
|
||||||
|
HELP_MSG("%s: Unban 1 or more accounts by their <name>.", "unban");
|
||||||
|
HELP_MSG(
|
||||||
|
"%s: Ban 1 or more IP address according to somebody's 'lastLoginIp'. "
|
||||||
|
"At least 1 <name> required.",
|
||||||
|
"banip");
|
||||||
|
HELP_MSG(
|
||||||
|
"%s: Unban 1 or more IP address according to somebody's 'lastLoginIp'. "
|
||||||
|
"At least 1 <name> required.",
|
||||||
|
"unbanip");
|
||||||
qInfo();
|
qInfo();
|
||||||
qInfo("===== Package commands =====");
|
qInfo("===== Package commands =====");
|
||||||
HELP_MSG("%s: Install a new package from <url>.", "install");
|
HELP_MSG("%s: Install a new package from <url>.", "install");
|
||||||
|
@ -36,7 +46,7 @@ void Shell::helpCommand(QStringList &) {
|
||||||
HELP_MSG("%s: List all packages.", "lspkg");
|
HELP_MSG("%s: List all packages.", "lspkg");
|
||||||
HELP_MSG("%s: Enable a package.", "enable");
|
HELP_MSG("%s: Enable a package.", "enable");
|
||||||
HELP_MSG("%s: Disable a package.", "disable");
|
HELP_MSG("%s: Disable a package.", "disable");
|
||||||
HELP_MSG("%s: Upgrade a package.", "upgrade");
|
HELP_MSG("%s: Upgrade a package. Leave empty to upgrade all.", "upgrade");
|
||||||
qInfo("For more commands, check the documentation.");
|
qInfo("For more commands, check the documentation.");
|
||||||
|
|
||||||
#undef HELP_MSG
|
#undef HELP_MSG
|
||||||
|
@ -86,7 +96,12 @@ void Shell::removeCommand(QStringList &list) {
|
||||||
|
|
||||||
void Shell::upgradeCommand(QStringList &list) {
|
void Shell::upgradeCommand(QStringList &list) {
|
||||||
if (list.isEmpty()) {
|
if (list.isEmpty()) {
|
||||||
qWarning("The 'upgrade' command need a package name to upgrade.");
|
// qWarning("The 'upgrade' command need a package name to upgrade.");
|
||||||
|
auto arr = QJsonDocument::fromJson(Pacman->listPackages().toUtf8()).array();
|
||||||
|
foreach (auto a, arr) {
|
||||||
|
auto obj = a.toObject();
|
||||||
|
Pacman->upgradePack(obj["name"].toString());
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -155,6 +170,111 @@ void Shell::msgCommand(QStringList &list) {
|
||||||
ServerInstance->broadcast("ServerMessage", msg);
|
ServerInstance->broadcast("ServerMessage", msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void banAccount(sqlite3 *db, const QString &name, bool banned) {
|
||||||
|
if (!CheckSqlString(name))
|
||||||
|
return;
|
||||||
|
QString sql_find = QString("SELECT * FROM userinfo \
|
||||||
|
WHERE name='%1';")
|
||||||
|
.arg(name);
|
||||||
|
auto result = SelectFromDatabase(db, sql_find);
|
||||||
|
if (result.isEmpty())
|
||||||
|
return;
|
||||||
|
auto obj = result[0].toObject();
|
||||||
|
int id = obj["id"].toString().toInt();
|
||||||
|
ExecSQL(db, QString("UPDATE userinfo SET banned=%2 WHERE id=%1;")
|
||||||
|
.arg(id)
|
||||||
|
.arg(banned ? 1 : 0));
|
||||||
|
|
||||||
|
if (banned) {
|
||||||
|
auto p = ServerInstance->findPlayer(id);
|
||||||
|
if (p) {
|
||||||
|
p->kicked();
|
||||||
|
}
|
||||||
|
qInfo("Banned %s.", name.toUtf8().constData());
|
||||||
|
} else {
|
||||||
|
qInfo("Unbanned %s.", name.toUtf8().constData());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Shell::banCommand(QStringList &list) {
|
||||||
|
if (list.isEmpty()) {
|
||||||
|
qWarning("The 'ban' command needs at least 1 <name>.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto db = ServerInstance->getDatabase();
|
||||||
|
|
||||||
|
foreach (auto name, list) {
|
||||||
|
banAccount(db, name, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Shell::unbanCommand(QStringList &list) {
|
||||||
|
if (list.isEmpty()) {
|
||||||
|
qWarning("The 'unban' command needs at least 1 <name>.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto db = ServerInstance->getDatabase();
|
||||||
|
|
||||||
|
foreach (auto name, list) {
|
||||||
|
banAccount(db, name, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
static void banIPByName(sqlite3 *db, const QString &name, bool banned) {
|
||||||
|
if (!CheckSqlString(name))
|
||||||
|
return;
|
||||||
|
|
||||||
|
QString sql_find = QString("SELECT * FROM userinfo \
|
||||||
|
WHERE name='%1';")
|
||||||
|
.arg(name);
|
||||||
|
auto result = SelectFromDatabase(db, sql_find);
|
||||||
|
if (result.isEmpty())
|
||||||
|
return;
|
||||||
|
auto obj = result[0].toObject();
|
||||||
|
int id = obj["id"].toString().toInt();
|
||||||
|
auto addr = obj["lastLoginIp"].toString();
|
||||||
|
|
||||||
|
if (banned) {
|
||||||
|
ExecSQL(db, QString("INSERT INTO banip VALUES('%1');").arg(addr));
|
||||||
|
|
||||||
|
auto p = ServerInstance->findPlayer(id);
|
||||||
|
if (p) {
|
||||||
|
p->kicked();
|
||||||
|
}
|
||||||
|
qInfo("Banned IP %s.", addr.toUtf8().constData());
|
||||||
|
} else {
|
||||||
|
ExecSQL(db, QString("DELETE FROM banip WHERE ip='%1';").arg(addr));
|
||||||
|
qInfo("Unbanned IP %s.", addr.toUtf8().constData());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Shell::banipCommand(QStringList &list) {
|
||||||
|
if (list.isEmpty()) {
|
||||||
|
qWarning("The 'banip' command needs at least 1 <name>.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto db = ServerInstance->getDatabase();
|
||||||
|
|
||||||
|
foreach (auto name, list) {
|
||||||
|
banIPByName(db, name, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Shell::unbanipCommand(QStringList &list) {
|
||||||
|
if (list.isEmpty()) {
|
||||||
|
qWarning("The 'unbanip' command needs at least 1 <name>.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto db = ServerInstance->getDatabase();
|
||||||
|
|
||||||
|
foreach (auto name, list) {
|
||||||
|
banIPByName(db, name, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Shell::Shell() {
|
Shell::Shell() {
|
||||||
setObjectName("Shell");
|
setObjectName("Shell");
|
||||||
signal(SIGINT, sigintHandler);
|
signal(SIGINT, sigintHandler);
|
||||||
|
@ -173,6 +293,10 @@ Shell::Shell() {
|
||||||
handlers["disable"] = &Shell::disableCommand;
|
handlers["disable"] = &Shell::disableCommand;
|
||||||
handlers["kick"] = &Shell::kickCommand;
|
handlers["kick"] = &Shell::kickCommand;
|
||||||
handlers["msg"] = &Shell::msgCommand;
|
handlers["msg"] = &Shell::msgCommand;
|
||||||
|
handlers["ban"] = &Shell::banCommand;
|
||||||
|
handlers["unban"] = &Shell::unbanCommand;
|
||||||
|
handlers["banip"] = &Shell::banipCommand;
|
||||||
|
handlers["unbanip"] = &Shell::unbanipCommand;
|
||||||
}
|
}
|
||||||
handler_map = handlers;
|
handler_map = handlers;
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,6 +25,10 @@ private:
|
||||||
void disableCommand(QStringList &);
|
void disableCommand(QStringList &);
|
||||||
void kickCommand(QStringList &);
|
void kickCommand(QStringList &);
|
||||||
void msgCommand(QStringList &);
|
void msgCommand(QStringList &);
|
||||||
|
void banCommand(QStringList &);
|
||||||
|
void banipCommand(QStringList &);
|
||||||
|
void unbanCommand(QStringList &);
|
||||||
|
void unbanipCommand(QStringList &);
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|