* cli

* add timeout for class Room

* request func, roomowner

* request

* corrent stupid Qml/JS

* chooseGenerals

* prepareForStart(part 1)

* fix require grammar
This commit is contained in:
Notify-ctrl 2022-03-30 14:14:40 +08:00 committed by GitHub
parent 58ea0ca80a
commit 7fd127b849
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
35 changed files with 878 additions and 139 deletions

BIN
image/photo/owner.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.2 KiB

BIN
image/photo/ready.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.1 KiB

View File

@ -1,7 +1,7 @@
Client = class('Client') Client = class('Client')
-- load client classes -- load client classes
ClientPlayer = require "client/clientplayer" ClientPlayer = require "client.clientplayer"
freekill.client_callback = {} freekill.client_callback = {}

View File

@ -106,8 +106,8 @@ function Engine:getGeneralsRandomly(num, generalPool, except, filter)
except = except or {} except = except or {}
local availableGenerals = {} local availableGenerals = {}
for _, general in ipairs(generalPool) do for _, general in pairs(generalPool) do
if not table.contains(except, general) and not (filter and filter(general)) then if not table.contains(except, general.name) and not (filter and filter(general)) then
table.insert(availableGenerals, general) table.insert(availableGenerals, general)
end end
end end
@ -117,9 +117,9 @@ function Engine:getGeneralsRandomly(num, generalPool, except, filter)
end end
local result = {} local result = {}
while num > 0 do for i = 1, num do
local randomGeneral = math.random(1, #availableGenerals) local randomGeneral = math.random(1, #availableGenerals)
table.insert(result, randomGeneral) table.insert(result, availableGenerals[randomGeneral])
table.remove(availableGenerals, randomGeneral) table.remove(availableGenerals, randomGeneral)
if #availableGenerals == 0 then if #availableGenerals == 0 then

View File

@ -13,7 +13,8 @@ function Player:initialize()
self.chained = false self.chained = false
self.dying = false self.dying = false
self.dead = false self.dead = false
self.state = ""
self.playerSkills = {} self.playerSkills = {}
end end

View File

@ -58,6 +58,8 @@ FileIO = {
isDir = freekill.QmlBackend_isDir isDir = freekill.QmlBackend_isDir
} }
os.getms = freekill.GetMicroSecond
Stack = class("Stack") Stack = class("Stack")
function Stack:initialize() function Stack:initialize()
self.t = {} self.t = {}

View File

@ -8,7 +8,7 @@ package.path = package.path .. ";./lua/lib/?.lua"
class = require "middleclass" class = require "middleclass"
json = require "json" json = require "json"
require "sha256" require "sha256"
Util = require "core/util" Util = require "core.util"
math.randomseed(os.time()) math.randomseed(os.time())
DebugMode = true DebugMode = true
@ -20,12 +20,12 @@ function pt(t)
end end
-- load core classes -- load core classes
Engine = require "core/engine" Engine = require "core.engine"
Package = require "core/package" Package = require "core.package"
General = require "core/general" General = require "core.general"
Card = require "core/card" Card = require "core.card"
Skill = require "core/skill" Skill = require "core.skill"
Player = require "core/player" Player = require "core.player"
-- load packages -- load packages
Fk = Engine:new() Fk = Engine:new()

View File

@ -25,30 +25,106 @@ function GameLogic:run()
self.room:adjustSeats() self.room:adjustSeats()
self:chooseGenerals() self:chooseGenerals()
self:startGame() self:prepareForStart()
self:action()
end end
function GameLogic:assignRoles() function GameLogic:assignRoles()
local n = #self.room.players local room = self.room
local n = #room.players
local roles = self.role_table[n] local roles = self.role_table[n]
table.shuffle(roles) table.shuffle(roles)
for i = 1, n do for i = 1, n do
local p = self.room.players[i] local p = room.players[i]
p.role = roles[i] p.role = roles[i]
if p.role == "lord" then if p.role == "lord" then
self.room:broadcastProperty(p, "role") room:broadcastProperty(p, "role")
else else
self.room:notifyProperty(p, p, "role") room:notifyProperty(p, p, "role")
end end
end end
end end
function GameLogic:chooseGenerals() function GameLogic:chooseGenerals()
local room = self.room
local function setPlayerGeneral(player, general)
if Fk.generals[general] == nil then return end
player.general = general
self.room:notifyProperty(player, player, "general")
end
local lord = room:getLord()
local lord_general = nil
if lord ~= nil then
local generals = Fk:getGeneralsRandomly(3)
for i = 1, #generals do
generals[i] = generals[i].name
end
lord_general = room:askForGeneral(lord, generals)
setPlayerGeneral(lord, lord_general)
room:broadcastProperty(lord, "general")
end
local nonlord = room:getOtherPlayers(lord)
local generals = Fk:getGeneralsRandomly(#nonlord * 3, Fk.generals, {lord_general})
table.shuffle(generals)
for _, p in ipairs(nonlord) do
local arg = {
(table.remove(generals, 1)).name,
(table.remove(generals, 1)).name,
(table.remove(generals, 1)).name,
}
p.request_data = json.encode(arg)
p.default_reply = arg[1]
end
room:doBroadcastRequest("AskForGeneral", nonlord)
for _, p in ipairs(nonlord) do
if p.general == "" and p.reply_ready then
local general = json.decode(p.client_reply)[1]
setPlayerGeneral(p, general)
else
setPlayerGeneral(p, p.default_reply)
end
p.default_reply = ""
end
end end
function GameLogic:startGame() function GameLogic:prepareForStart()
local room = self.room
local players = room.players
room.alive_players = players
for i = 1, #players - 1 do
players[i].next = players[i + 1]
end
players[#players].next = players[1]
for _, p in ipairs(players) do
assert(p.general ~= "")
local general = Fk.generals[p.general]
p.maxHp = general.maxHp
p.hp = general.hp
-- TODO: setup AI here
if p.role ~= "lord" then
room:broadcastProperty(p, "general")
elseif #players >= 5 then
p.maxHp = p.maxHp + 1
p.hp = p.hp + 1
end
room:broadcastProperty(p, "maxHp")
room:broadcastProperty(p, "hp")
-- TODO: add skills to player
end
-- TODO: prepare drawPile
-- TODO: init cards in drawPile
-- TODO: init trigger table for self
end
function GameLogic:action()
end end

View File

@ -3,13 +3,16 @@ local Room = class("Room")
function Room:initialize(_room) function Room:initialize(_room)
self.room = _room self.room = _room
self.players = {} -- ServerPlayer[] self.players = {} -- ServerPlayer[]
self.gameFinished = false self.alive_players = {}
self.game_finished = false
self.timeout = _room:getTimeout()
end end
-- When this function returns, the Room(C++) thread stopped. -- When this function returns, the Room(C++) thread stopped.
function Room:run() function Room:run()
for _, p in freekill.qlist(self.room:getPlayers()) do for _, p in freekill.qlist(self.room:getPlayers()) do
local player = ServerPlayer:new(p) local player = ServerPlayer:new(p)
player.state = p:getStateString()
table.insert(self.players, player) table.insert(self.players, player)
self.server.players[player:getId()] = player self.server.players[player:getId()] = player
end end
@ -32,8 +35,55 @@ function Room:notifyProperty(p, player, property)
}) })
end end
function Room:doBroadcastNotify(command, jsonData) function Room:doBroadcastNotify(command, jsonData, players)
self.room:doBroadcastNotify(self.room:getPlayers(), command, jsonData) players = players or self.players
local tolist = freekill.SPlayerList()
for _, p in ipairs(players) do
tolist:append(p.serverplayer)
end
self.room:doBroadcastNotify(tolist, command, jsonData)
end
function Room:doRequest(player, command, jsonData, wait)
if wait == nil then wait = true end
player:doRequest(command, jsonData, self.timeout)
if wait then
return player:waitForReply(self.timeout)
end
end
function Room:doBroadcastRequest(command, players)
players = players or self.players
self:notifyMoveFocus(players, command)
for _, p in ipairs(players) do
self:doRequest(p, command, p.request_data, false)
end
local remainTime = self.timeout
local currentTime = os.time()
local elapsed = 0
for _, p in ipairs(players) do
elapsed = os.time() - currentTime
remainTime = remainTime - elapsed
p:waitForReply(remainTime)
end
end
function Room:notifyMoveFocus(players, command)
if (players.class) then
players = {players}
end
local ids = {}
for _, p in ipairs(players) do
table.insert(ids, p:getId())
end
self:doBroadcastNotify("MoveFocus", json.encode{
ids,
command
})
end end
function Room:adjustSeats() function Room:adjustSeats()
@ -64,8 +114,45 @@ function Room:adjustSeats()
self:doBroadcastNotify("ArrangeSeats", json.encode(player_circle)) self:doBroadcastNotify("ArrangeSeats", json.encode(player_circle))
end end
function Room:getLord()
local lord = self.players[1]
if lord.role == "lord" then return lord end
for _, p in ipairs(self.players) do
if p.role == "lord" then return p end
end
return nil
end
function Room:getOtherPlayers(expect)
local ret = {table.unpack(self.players)}
table.removeOne(ret, expect)
return ret
end
function Room:askForGeneral(player, generals)
local command = "AskForGeneral"
self:notifyMoveFocus(player, command)
if #generals == 1 then return generals[1] end
local defaultChoice = generals[1]
if (player.state == "online") then
local result = self:doRequest(player, command, json.encode(generals))
if result == "" then
return defaultChoice
else
-- TODO: result is a JSON array
-- update here when choose multiple generals
return json.decode(result)[1]
end
end
return defaultChoice
end
function Room:gameOver() function Room:gameOver()
self.gameFinished = true self.game_finished = true
-- dosomething -- dosomething
self.room:gameOver() self.room:gameOver()
end end

View File

@ -1,9 +1,9 @@
Server = class('Server') Server = class('Server')
-- load server classes -- load server classes
Room = require "server/room" Room = require "server.room"
GameLogic = require "server/gamelogic" GameLogic = require "server.gamelogic"
ServerPlayer = require "server/serverplayer" ServerPlayer = require "server.serverplayer"
freekill.server_callback = {} freekill.server_callback = {}

View File

@ -3,6 +3,14 @@ local ServerPlayer = Player:subclass("ServerPlayer")
function ServerPlayer:initialize(_self) function ServerPlayer:initialize(_self)
Player.initialize(self) Player.initialize(self)
self.serverplayer = _self self.serverplayer = _self
self.next = nil
-- Below are for doBroadcastRequest
self.request_data = ""
self.client_reply = ""
self.default_reply = ""
self.reply_ready = false
end end
function ServerPlayer:getId() function ServerPlayer:getId()
@ -14,7 +22,23 @@ function ServerPlayer:doNotify(command, jsonData)
end end
function ServerPlayer:doRequest(command, jsonData, timeout) function ServerPlayer:doRequest(command, jsonData, timeout)
timeout = timeout or self.room.timeout
self.client_reply = ""
self.reply_ready = false
self.serverplayer:doRequest(command, jsonData, timeout) self.serverplayer:doRequest(command, jsonData, timeout)
end end
function ServerPlayer:waitForReply(timeout)
local result = ""
if timeout == nil then
result = self.serverplayer:waitForReply()
else
result = self.serverplayer:waitForReply(timeout)
end
self.request_data = ""
self.client_reply = result
if result ~= "" then self.reply_ready = true end
return result
end
return ServerPlayer return ServerPlayer

View File

@ -1,5 +1,5 @@
local extension = Package:new("standard") local extension = Package:new("standard")
extension.metadata = require "standard/metadata" extension.metadata = require "standard.metadata"
Fk:loadTranslationTable{ Fk:loadTranslationTable{
["wei"] = "", ["wei"] = "",

View File

@ -9,4 +9,5 @@ QtObject {
// Client data // Client data
property int roomCapacity: 0 property int roomCapacity: 0
property int roomTimeout: 0
} }

View File

@ -29,7 +29,10 @@ callbacks["EnterLobby"] = function(jsonData) {
} }
callbacks["EnterRoom"] = function(jsonData) { callbacks["EnterRoom"] = function(jsonData) {
config.roomCapacity = JSON.parse(jsonData)[0]; // jsonData: int capacity, int timeout
let data = JSON.parse(jsonData);
config.roomCapacity = data[0];
config.roomTimeout = data[1];
mainStack.push(room); mainStack.push(room);
mainWindow.busy = false; mainWindow.busy = false;
} }

69
qml/Pages/MetroButton.qml Normal file
View File

@ -0,0 +1,69 @@
import QtQuick 2.15
Item {
property bool enabled: true
property alias text: title.text
property alias textColor: title.color
property alias textFont: title.font
property alias backgroundColor: bg.color
property alias border: bg.border
property alias iconSource: icon.source
property int padding: 5
signal clicked
id: button
width: icon.width + title.implicitWidth + padding * 2
height: Math.max(icon.height, title.implicitHeight) + padding * 2
Rectangle {
id: bg
anchors.fill: parent
color: "black"
border.width: 2
border.color: "white"
opacity: 0.8
}
states: [
State {
name: "hovered"; when: mouse.containsMouse
PropertyChanges { target: bg; color: "white" }
PropertyChanges { target: title; color: "black" }
},
State {
name: "disabled"; when: !enabled
PropertyChanges { target: button; opacity: 0.2 }
}
]
MouseArea {
id: mouse
anchors.fill: parent
hoverEnabled: parent.enabled
onReleased: if (parent.enabled) parent.clicked()
}
Row {
x: padding
y: padding
anchors.centerIn: parent
spacing: 5
Image {
id: icon
anchors.verticalCenter: parent.verticalCenter
fillMode: Image.PreserveAspectFit
}
Text {
id: title
font.pixelSize: 18
// font.family: "WenQuanYi Micro Hei"
anchors.verticalCenter: parent.verticalCenter
text: ""
color: "white"
}
}
}

View File

@ -13,11 +13,9 @@ Item {
property bool isOwner: false property bool isOwner: false
property bool isStarted: false property bool isStarted: false
property alias popupBox: popupBox
// tmp // tmp
Text {
anchors.centerIn: parent
text: "You are in room."
}
Button { Button {
text: "quit" text: "quit"
anchors.bottom: parent.bottom anchors.bottom: parent.bottom
@ -28,7 +26,7 @@ Item {
} }
Button { Button {
text: "start game" text: "start game"
visible: isOwner && !isStarted visible: dashboardModel.isOwner && !isStarted
anchors.centerIn: parent anchors.centerIn: parent
} }
@ -73,20 +71,20 @@ Item {
id: photos id: photos
model: photoModel model: photoModel
Photo { Photo {
general: _general general: model.general
screenName: _screenName screenName: model.screenName
role: _role role: model.role
kingdom: _kingdom kingdom: model.kingdom
netstate: _netstate netstate: model.netstate
maxHp: _maxHp maxHp: model.maxHp
hp: _hp hp: model.hp
seatNumber: _seatNumber seatNumber: model.seatNumber
isDead: _isDead isDead: model.isDead
dying: _dying dying: model.dying
faceturned: _faceturned faceturned: model.faceturned
chained: _chained chained: model.chained
drank: _drank drank: model.drank
isOwner: _isOwner isOwner: model.isOwner
} }
} }
@ -129,6 +127,30 @@ Item {
self.isOwner: dashboardModel.isOwner self.isOwner: dashboardModel.isOwner
} }
Loader {
id: popupBox
onSourceChanged: {
if (item === null)
return;
item.finished.connect(function(){
source = "";
});
item.widthChanged.connect(function(){
popupBox.moveToCenter();
});
item.heightChanged.connect(function(){
popupBox.moveToCenter();
});
moveToCenter();
}
function moveToCenter()
{
item.x = Math.round((roomArea.width - item.width) / 2);
item.y = Math.round(roomArea.height * 0.67 - item.height / 2);
}
}
Component.onCompleted: { Component.onCompleted: {
toast.show("Sucesessfully entered room."); toast.show("Sucesessfully entered room.");
@ -157,20 +179,20 @@ Item {
photoModel.append({ photoModel.append({
id: -1, id: -1,
index: i - 1, // For animating seat swap index: i - 1, // For animating seat swap
_general: "", general: "",
_screenName: "", screenName: "",
_role: "unknown", role: "unknown",
_kingdom: "qun", kingdom: "qun",
_netstate: "online", netstate: "online",
_maxHp: 0, maxHp: 0,
_hp: 0, hp: 0,
_seatNumber: i + 1, seatNumber: i + 1,
_isDead: false, isDead: false,
_dying: false, dying: false,
_faceturned: false, faceturned: false,
_chained: false, chained: false,
_drank: false, drank: false,
_isOwner: false isOwner: false
}); });
} }

View File

@ -20,9 +20,9 @@ Item {
function remove(outputs) function remove(outputs)
{ {
var result = []; let result = [];
for (var i = 0; i < cards.length; i++) { for (let i = 0; i < cards.length; i++) {
for (var j = 0; j < outputs.length; j++) { for (let j = 0; j < outputs.length; j++) {
if (outputs[j] === cards[i].cid) { if (outputs[j] === cards[i].cid) {
result.push(cards[i]); result.push(cards[i]);
cards.splice(i, 1); cards.splice(i, 1);
@ -37,9 +37,9 @@ Item {
function updateCardPosition(animated) function updateCardPosition(animated)
{ {
var i, card; let i, card;
var overflow = false; let overflow = false;
for (i = 0; i < cards.length; i++) { for (i = 0; i < cards.length; i++) {
card = cards[i]; card = cards[i];
card.origX = i * card.width; card.origX = i * card.width;
@ -52,8 +52,8 @@ Item {
if (overflow) { if (overflow) {
// TODO: Adjust cards in multiple lines if there are too many cards // TODO: Adjust cards in multiple lines if there are too many cards
var xLimit = root.width - card.width; let xLimit = root.width - card.width;
var spacing = xLimit / (cards.length - 1); let spacing = xLimit / (cards.length - 1);
for (i = 0; i < cards.length; i++) { for (i = 0; i < cards.length; i++) {
card = cards[i]; card = cards[i];
card.origX = i * spacing; card.origX = i * spacing;
@ -61,7 +61,7 @@ Item {
} }
} }
var parentPos = roomScene.mapFromItem(root, 0, 0); let parentPos = roomScene.mapFromItem(root, 0, 0);
for (i = 0; i < cards.length; i++) { for (i = 0; i < cards.length; i++) {
card = cards[i]; card = cards[i];
card.origX += parentPos.x; card.origX += parentPos.x;

View File

@ -26,6 +26,7 @@ Item {
property bool footnoteVisible: true property bool footnoteVisible: true
property bool known: true // if false it only show a card back property bool known: true // if false it only show a card back
property bool enabled: true // if false the card will be grey property bool enabled: true // if false the card will be grey
property alias card: cardItem
property alias glow: glowItem property alias glow: glowItem
function getColor() { function getColor() {
@ -38,9 +39,12 @@ Item {
property int cid: 0 property int cid: 0
property bool selectable: true property bool selectable: true
property bool selected: false property bool selected: false
property bool draggable: false
property bool autoBack: true
property int origX: 0 property int origX: 0
property int origY: 0 property int origY: 0
property real origOpacity: 0 property real origOpacity: 1
property bool isClicked: false
property bool moveAborted: false property bool moveAborted: false
property alias goBackAnim: goBackAnimation property alias goBackAnim: goBackAnimation
property int goBackDuration: 500 property int goBackDuration: 500
@ -71,6 +75,7 @@ Item {
source: known ? (name != "" ? SkinBank.CARD_DIR + name : "") source: known ? (name != "" ? SkinBank.CARD_DIR + name : "")
: (SkinBank.CARD_DIR + "card-back") : (SkinBank.CARD_DIR + "card-back")
anchors.fill: parent anchors.fill: parent
fillMode: Image.PreserveAspectCrop
} }
Image { Image {
@ -97,7 +102,7 @@ Item {
Image { Image {
id: colorItem id: colorItem
visible: known && suit == "" visible: known && suit == ""
source: visible ? SkinBank.CARD_SUIT_DIR + "/" + color : "" source: (visible && color != "") ? SkinBank.CARD_SUIT_DIR + "/" + color : ""
x: 1 x: 1
} }
@ -126,6 +131,41 @@ Item {
opacity: 0.7 opacity: 0.7
} }
MouseArea {
anchors.fill: parent
drag.target: draggable ? parent : undefined
drag.axis: Drag.XAndYAxis
hoverEnabled: true
onReleased: {
root.isClicked = mouse.isClick;
parent.released();
if (autoBack)
goBackAnimation.start();
}
onEntered: {
parent.entered();
if (draggable) {
glow.visible = true;
root.z++;
}
}
onExited: {
parent.exited();
if (draggable) {
glow.visible = false;
root.z--;
}
}
onClicked: {
selected = selectable ? !selected : false;
parent.clicked();
}
}
ParallelAnimation { ParallelAnimation {
id: goBackAnimation id: goBackAnimation
@ -180,7 +220,7 @@ Item {
function toData() function toData()
{ {
var data = { let data = {
cid: cid, cid: cid,
name: name, name: name,
suit: suit, suit: suit,

View File

@ -0,0 +1,177 @@
import QtQuick 2.15
import ".."
import "../skin-bank.js" as SkinBank
GraphicsBox {
property alias generalList: generalList
// property var generalList: []
property int choiceNum: 1
property var choices: []
property var selectedItem: []
property bool loaded: false
ListModel {
id: generalList
}
id: root
title.text: qsTr("Please choose ") + choiceNum + qsTr(" general(s)")
width: generalArea.width + body.anchors.leftMargin + body.anchors.rightMargin
height: body.implicitHeight + body.anchors.topMargin + body.anchors.bottomMargin
Column {
id: body
anchors.fill: parent
anchors.margins: 40
anchors.bottomMargin: 20
Item {
id: generalArea
width: (generalList.count >= 5 ? Math.ceil(generalList.count / 2) : Math.max(3, generalList.count)) * 97
height: generalList.count >= 5 ? 290 : 150
z: 1
Repeater {
id: generalMagnetList
model: generalList.count
Item {
width: 93
height: 130
x: (index % Math.ceil(generalList.count / (generalList.count >= 5 ? 2 : 1))) * 98 + (generalList.count >= 5 && index > generalList.count / 2 && generalList.count % 2 == 1 ? 50 : 0)
y: generalList.count < 5 ? 0 : (index < generalList.count / 2 ? 0 : 135)
}
}
}
Item {
id: splitLine
width: parent.width - 80
height: 6
anchors.horizontalCenter: parent.horizontalCenter
clip: true
}
Item {
width: parent.width
height: 165
Row {
id: resultArea
anchors.centerIn: parent
spacing: 10
Repeater {
id: resultList
model: choiceNum
Rectangle {
color: "#1D1E19"
radius: 3
width: 93
height: 130
}
}
}
}
Item {
id: buttonArea
width: parent.width
height: 40
MetroButton {
id: fightButton
anchors.horizontalCenter: parent.horizontalCenter
anchors.bottom: parent.bottom
text: qsTr("Fight")
width: 120
height: 35
enabled: false
onClicked: close();
}
}
}
Repeater {
id: generalCardList
model: generalList
GeneralCardItem {
name: model.name
selectable: true
draggable: true
onClicked: {
let toSelect = true;
for (let i = 0; i < selectedItem.length; i++) {
if (selectedItem[i] === this) {
toSelect = false;
selectedItem.splice(i, 1);
}
}
if (toSelect && selectedItem.length < choiceNum)
selectedItem.push(this);
updatePosition();
}
onReleased: {
if (!isClicked)
arrangeCards();
}
}
}
function arrangeCards()
{
let item, i;
selectedItem = [];
for (i = 0; i < generalList.count; i++) {
item = generalCardList.itemAt(i);
if (item.y > splitLine.y)
selectedItem.push(item);
}
selectedItem.sort((a, b) => a.x - b.x);
if (selectedItem.length > choiceNum)
selectedItem.splice(choiceNum, selectedItem.length - choiceNum);
updatePosition();
}
function updatePosition()
{
choices = [];
let item, magnet, pos, i;
for (i = 0; i < selectedItem.length && i < resultList.count; i++) {
item = selectedItem[i];
choices.push(item.name);
magnet = resultList.itemAt(i);
pos = root.mapFromItem(resultArea, magnet.x, magnet.y);
if (item.origX !== pos.x || item.origY !== item.y) {
item.origX = pos.x;
item.origY = pos.y;
item.goBack(true);
}
}
fightButton.enabled = (choices.length == choiceNum);
for (i = 0; i < generalCardList.count; i++) {
item = generalCardList.itemAt(i);
if (selectedItem.indexOf(item) != -1)
continue;
magnet = generalMagnetList.itemAt(i);
pos = root.mapFromItem(generalMagnetList.parent, magnet.x, magnet.y);
if (item.origX !== pos.x || item.origY !== item.y) {
item.origX = pos.x;
item.origY = pos.y;
item.goBack(true);
}
}
}
}

View File

@ -0,0 +1,24 @@
import QtQuick 2.15
import "../skin-bank.js" as SkinBank
/* Layout of general card:
* +--------+
*kindom|wu 9999| <- hp
*name -|s |
* |q img |
* | |
* | |
* +--------+
* Inherit from CardItem to use common signal
*/
CardItem {
property string kingdom: "qun"
name: "caocao"
// description: Sanguosha.getGeneralDescription(name)
suit: ""
number: 0
footnote: ""
card.source: SkinBank.GENERAL_DIR + name
glow.color: "white" //Engine.kingdomColor[kingdom]
}

View File

@ -0,0 +1,53 @@
import QtQuick 2.15
import QtGraphicalEffects 1.0
Item {
property alias title: titleItem
signal accepted() //Read result
signal finished() //Close the box
id: root
Rectangle {
id: background
anchors.fill: parent
color: "#B0000000"
radius: 5
border.color: "#A6967A"
border.width: 1
}
DropShadow {
source: background
anchors.fill: background
color: "#B0000000"
radius: 5
samples: 12
spread: 0.2
horizontalOffset: 5
verticalOffset: 4
transparentBorder: true
}
Text {
id: titleItem
color: "#E4D5A0"
font.pixelSize: 18
horizontalAlignment: Text.AlignHCenter
anchors.top: parent.top
anchors.topMargin: 4
anchors.horizontalCenter: parent.horizontalCenter
}
MouseArea {
anchors.fill: parent
drag.target: parent
drag.axis: Drag.XAndYAxis
}
function close()
{
accepted();
finished();
}
}

View File

@ -19,7 +19,7 @@ Item {
{ {
cardArea.add(inputs); cardArea.add(inputs);
if (inputs instanceof Array) { if (inputs instanceof Array) {
for (var i = 0; i < inputs.length; i++) for (let i = 0; i < inputs.length; i++)
filterInputCard(inputs[i]); filterInputCard(inputs[i]);
} else { } else {
filterInputCard(inputs); filterInputCard(inputs);
@ -36,9 +36,9 @@ Item {
function remove(outputs) function remove(outputs)
{ {
var result = cardArea.remove(outputs); let result = cardArea.remove(outputs);
var card; let card;
for (var i = 0; i < result.length; i++) { for (let i = 0; i < result.length; i++) {
card = result[i]; card = result[i];
card.draggable = false; card.draggable = false;
card.selectable = false; card.selectable = false;
@ -49,7 +49,7 @@ Item {
function enableCards(cardIds) function enableCards(cardIds)
{ {
var card, i; let card, i;
for (i = 0; i < cards.length; i++) { for (i = 0; i < cards.length; i++) {
card = cards[i]; card = cards[i];
card.selectable = cardIds.contains(card.cid); card.selectable = cardIds.contains(card.cid);
@ -64,7 +64,7 @@ Item {
{ {
cardArea.updateCardPosition(false); cardArea.updateCardPosition(false);
var i, card; let i, card;
for (i = 0; i < cards.length; i++) { for (i = 0; i < cards.length; i++) {
card = cards[i]; card = cards[i];
if (card.selected) if (card.selected)
@ -81,8 +81,8 @@ Item {
{ {
area.updateCardPosition(true); area.updateCardPosition(true);
for (var i = 0; i < cards.length; i++) { for (let i = 0; i < cards.length; i++) {
var card = cards[i]; let card = cards[i];
if (card.selected) { if (card.selected) {
if (!selectedCards.contains(card)) if (!selectedCards.contains(card))
selectCard(card); selectCard(card);
@ -101,7 +101,7 @@ Item {
function unselectCard(card) function unselectCard(card)
{ {
for (var i = 0; i < selectedCards.length; i++) { for (let i = 0; i < selectedCards.length; i++) {
if (selectedCards[i] === card) { if (selectedCards[i] === card) {
selectedCards.splice(i, 1); selectedCards.splice(i, 1);
cardSelected(card.cid, false); cardSelected(card.cid, false);
@ -112,7 +112,7 @@ Item {
function unselectAll(exceptId) { function unselectAll(exceptId) {
let card = undefined; let card = undefined;
for (var i = 0; i < selectedCards.length; i++) { for (let i = 0; i < selectedCards.length; i++) {
if (selectedCards[i].cid !== exceptId) { if (selectedCards[i].cid !== exceptId) {
selectedCards[i].selected = false; selectedCards[i].selected = false;
} else { } else {

View File

@ -10,9 +10,9 @@ Item {
function add(inputs) function add(inputs)
{ {
var card; let card;
if (inputs instanceof Array) { if (inputs instanceof Array) {
for (var i = 0; i < inputs.length; i++) { for (let i = 0; i < inputs.length; i++) {
card = inputs[i]; card = inputs[i];
pendingInput.push(card); pendingInput.push(card);
cards.push(card.toData()); cards.push(card.toData());
@ -38,7 +38,7 @@ Item {
if (!checkExisting) if (!checkExisting)
return true; return true;
for (var i = 0; i < cards.length; i++) for (let i = 0; i < cards.length; i++)
{ {
if (cards[i].cid === cid) if (cards[i].cid === cid)
return true; return true;
@ -48,13 +48,13 @@ Item {
function remove(outputs) function remove(outputs)
{ {
var component = Qt.createComponent("CardItem.qml"); let component = Qt.createComponent("CardItem.qml");
if (component.status !== Component.Ready) if (component.status !== Component.Ready)
return []; return [];
var parentPos = roomScene.mapFromItem(root, 0, 0); let parentPos = roomScene.mapFromItem(root, 0, 0);
var card; let card;
var items = []; let items = [];
for (let i = 0; i < outputs.length; i++) { for (let i = 0; i < outputs.length; i++) {
if (_contains(outputs[i])) { if (_contains(outputs[i])) {
let state = JSON.parse(Sanguosha.getCard4Qml(outputs[i])) let state = JSON.parse(Sanguosha.getCard4Qml(outputs[i]))
@ -82,10 +82,10 @@ Item {
function updateCardPosition(animated) function updateCardPosition(animated)
{ {
var i, card; let i, card;
if (animated) { if (animated) {
var parentPos = roomScene.mapFromItem(root, 0, 0); let parentPos = roomScene.mapFromItem(root, 0, 0);
for (i = 0; i < pendingInput.length; i++) { for (i = 0; i < pendingInput.length; i++) {
card = pendingInput[i]; card = pendingInput[i];
card.origX = parentPos.x - card.width / 2 + ((i - pendingInput.length / 2) * 15); card.origX = parentPos.x - card.width / 2 + ((i - pendingInput.length / 2) * 15);

View File

@ -1,5 +1,6 @@
import QtQuick 2.15 import QtQuick 2.15
import QtGraphicalEffects 1.15 import QtGraphicalEffects 1.15
import QtQuick.Controls 2.15
import "PhotoElement" import "PhotoElement"
import "../skin-bank.js" as SkinBank import "../skin-bank.js" as SkinBank
@ -24,6 +25,9 @@ Item {
property bool drank: false property bool drank: false
property bool isOwner: false property bool isOwner: false
property alias progressBar: progressBar
property alias progressTip: progressTip.text
Behavior on x { Behavior on x {
NumberAnimation { duration: 600; easing.type: Easing.InOutQuad } NumberAnimation { duration: 600; easing.type: Easing.InOutQuad }
} }
@ -95,6 +99,15 @@ Item {
visible: root.isDead visible: root.isDead
} }
Image {
anchors.bottom: parent.bottom
anchors.right: parent.right
anchors.bottomMargin: 8
anchors.rightMargin: 4
source: SkinBank.PHOTO_DIR + (isOwner ? "owner" : "ready")
visible: screenName != "" && !roomScene.isStarted
}
Image { Image {
id: turnedOver id: turnedOver
visible: root.faceturned visible: root.faceturned
@ -155,6 +168,7 @@ Item {
GlowText { GlowText {
id: seatNum id: seatNum
visible: !progressBar.visible
anchors.horizontalCenter: parent.horizontalCenter anchors.horizontalCenter: parent.horizontalCenter
anchors.bottom: parent.bottom anchors.bottom: parent.bottom
anchors.bottomMargin: -32 anchors.bottomMargin: -32
@ -191,4 +205,42 @@ Item {
function tremble() { function tremble() {
trembleAnimation.start() trembleAnimation.start()
} }
ProgressBar {
id: progressBar
width: parent.width
height: 4
anchors.bottom: parent.bottom
anchors.bottomMargin: -4
from: 0.0
to: 100.0
visible: false
NumberAnimation on value {
running: progressBar.visible
from: 100.0
to: 0.0
duration: config.roomTimeout * 1000
onFinished: {
progressBar.visible = false;
root.progressTip = "";
}
}
}
Image {
anchors.top: progressBar.bottom
anchors.topMargin: 1
source: SkinBank.PHOTO_DIR + "control/tip"
visible: progressTip.text != ""
Text {
id: progressTip
font.family: "FZLiBian-S02"
font.pixelSize: 18
x: 18
color: "white"
text: ""
}
}
} }

View File

@ -22,7 +22,7 @@ Item {
running: true running: true
triggeredOnStart: true triggeredOnStart: true
onTriggered: { onTriggered: {
var i, card; let i, card;
if (toVanish) { if (toVanish) {
for (i = 0; i < discardedCards.length; i++) { for (i = 0; i < discardedCards.length; i++) {
card = discardedCards[i]; card = discardedCards[i];
@ -60,13 +60,13 @@ Item {
function remove(outputs) function remove(outputs)
{ {
var i, j; let i, j;
var result = area.remove(outputs); let result = area.remove(outputs);
var vanished = []; let vanished = [];
if (result.length < outputs.length) { if (result.length < outputs.length) {
for (i = 0; i < outputs.length; i++) { for (i = 0; i < outputs.length; i++) {
var exists = false; let exists = false;
for (j = 0; j < result.length; j++) { for (j = 0; j < result.length; j++) {
if (result[j].cid === outputs[i]) { if (result[j].cid === outputs[i]) {
exists = true; exists = true;
@ -96,9 +96,9 @@ Item {
if (cards.length <= 0) if (cards.length <= 0)
return; return;
var i, card; let i, card;
var overflow = false; let overflow = false;
for (i = 0; i < cards.length; i++) { for (i = 0; i < cards.length; i++) {
card = cards[i]; card = cards[i];
card.homeX = i * card.width; card.homeX = i * card.width;
@ -111,8 +111,8 @@ Item {
if (overflow) { if (overflow) {
//@to-do: Adjust cards in multiple lines if there are too many cards //@to-do: Adjust cards in multiple lines if there are too many cards
var xLimit = root.width - card.width; let xLimit = root.width - card.width;
var spacing = xLimit / (cards.length - 1); let spacing = xLimit / (cards.length - 1);
for (i = 0; i < cards.length; i++) { for (i = 0; i < cards.length; i++) {
card = cards[i]; card = cards[i];
card.homeX = i * spacing; card.homeX = i * spacing;
@ -120,8 +120,8 @@ Item {
} }
} }
var offsetX = Math.max(0, (root.width - cards.length * card.width) / 2); let offsetX = Math.max(0, (root.width - cards.length * card.width) / 2);
var parentPos = roomScene.mapFromItem(root, 0, 0); let parentPos = roomScene.mapFromItem(root, 0, 0);
for (i = 0; i < cards.length; i++) { for (i = 0; i < cards.length; i++) {
card = cards[i]; card = cards[i];
card.homeX += parentPos.x + offsetX; card.homeX += parentPos.x + offsetX;

View File

@ -7,14 +7,14 @@ function arrangePhotos() {
* +---------------+ * +---------------+
*/ */
var photoWidth = 175; const photoWidth = 175;
var roomAreaPadding = 10; const roomAreaPadding = 10;
var verticalPadding = Math.max(10, roomArea.width * 0.01); let verticalPadding = Math.max(10, roomArea.width * 0.01);
var horizontalSpacing = Math.max(30, roomArea.height * 0.1); let horizontalSpacing = Math.max(30, roomArea.height * 0.1);
var verticalSpacing = (roomArea.width - photoWidth * 7 - verticalPadding * 2) / 6; let verticalSpacing = (roomArea.width - photoWidth * 7 - verticalPadding * 2) / 6;
// Position 1-7 // Position 1-7
var regions = [ const regions = [
{ x: verticalPadding + (photoWidth + verticalSpacing) * 6, y: roomAreaPadding + horizontalSpacing * 2 }, { x: verticalPadding + (photoWidth + verticalSpacing) * 6, y: roomAreaPadding + horizontalSpacing * 2 },
{ x: verticalPadding + (photoWidth + verticalSpacing) * 5, y: roomAreaPadding + horizontalSpacing }, { x: verticalPadding + (photoWidth + verticalSpacing) * 5, y: roomAreaPadding + horizontalSpacing },
{ x: verticalPadding + (photoWidth + verticalSpacing) * 4, y: roomAreaPadding }, { x: verticalPadding + (photoWidth + verticalSpacing) * 4, y: roomAreaPadding },
@ -24,7 +24,7 @@ function arrangePhotos() {
{ x: verticalPadding, y: roomAreaPadding + horizontalSpacing * 2 }, { x: verticalPadding, y: roomAreaPadding + horizontalSpacing * 2 },
]; ];
var regularSeatIndex = [ const regularSeatIndex = [
[4], [4],
[3, 5], [3, 5],
[1, 4, 7], [1, 4, 7],
@ -33,9 +33,9 @@ function arrangePhotos() {
[1, 2, 3, 5, 6, 7], [1, 2, 3, 5, 6, 7],
[1, 2, 3, 4, 5, 6, 7], [1, 2, 3, 4, 5, 6, 7],
]; ];
var seatIndex = regularSeatIndex[playerNum - 2]; let seatIndex = regularSeatIndex[playerNum - 2];
var item, region, i; let item, region, i;
for (i = 0; i < playerNum - 1; i++) { for (i = 0; i < playerNum - 1; i++) {
item = photos.itemAt(i); item = photos.itemAt(i);
@ -58,9 +58,8 @@ callbacks["AddPlayer"] = function(jsonData) {
let name = data[1]; let name = data[1];
let avatar = data[2]; let avatar = data[2];
item.id = uid; item.id = uid;
item._screenName = name; item.screenName = name;
item._general = avatar; item.general = avatar;
photos.itemAt(i).tremble();
return; return;
} }
} }
@ -73,19 +72,31 @@ callbacks["RemovePlayer"] = function(jsonData) {
let item = photoModel.get(i); let item = photoModel.get(i);
if (item.id === uid) { if (item.id === uid) {
item.id = -1; item.id = -1;
item._screenName = ""; item.screenName = "";
item._general = ""; item.general = "";
return; return;
} }
} }
} }
/*
callbacks["RoomOwner"] = function(jsonData) { callbacks["RoomOwner"] = function(jsonData) {
// jsonData: int uid of the owner // jsonData: int uid of the owner
toast.show(J) let uid = JSON.parse(jsonData)[0];
if (dashboardModel.id === uid) {
dashboardModel.isOwner = true;
roomScene.dashboardModelChanged();
return;
}
for (let i = 0; i < photoModel.count; i++) {
let item = photoModel.get(i);
if (item.id === uid) {
item.isOwner = true;
return;
}
}
} }
*/
callbacks["PropertyUpdate"] = function(jsonData) { callbacks["PropertyUpdate"] = function(jsonData) {
// jsonData: int id, string property_name, value // jsonData: int id, string property_name, value
@ -103,7 +114,7 @@ callbacks["PropertyUpdate"] = 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);
if (item.id === uid) { if (item.id === uid) {
item["_" + property_name] = value; item[property_name] = value;
return; return;
} }
} }
@ -112,10 +123,11 @@ callbacks["PropertyUpdate"] = function(jsonData) {
callbacks["ArrangeSeats"] = function(jsonData) { callbacks["ArrangeSeats"] = function(jsonData) {
// jsonData: seat order // jsonData: seat order
let order = JSON.parse(jsonData); let order = JSON.parse(jsonData);
roomScene.isStarted = true;
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._seatNumber = order.indexOf(item.id) + 1; item.seatNumber = order.indexOf(item.id) + 1;
} }
dashboardModel.seatNumber = order.indexOf(Self.id) + 1; dashboardModel.seatNumber = order.indexOf(Self.id) + 1;
@ -134,3 +146,45 @@ callbacks["ArrangeSeats"] = function(jsonData) {
arrangePhotos(); arrangePhotos();
} }
function cancelAllFocus() {
let item;
for (let i = 0; i < playerNum - 1; i++) {
item = photos.itemAt(i);
item.progressBar.visible = false;
item.progressTip = "";
}
}
callbacks["MoveFocus"] = function(jsonData) {
// jsonData: int[] focuses, string command
cancelAllFocus();
let data = JSON.parse(jsonData);
let focuses = data[0];
let command = data[1];
let item, model;
for (let i = 0; i < playerNum - 1; i++) {
model = photoModel.get(i);
if (focuses.indexOf(model.id) != -1) {
item = photos.itemAt(i);
item.progressBar.visible = true;
item.progressTip = command + " thinking...";
}
}
}
callbacks["AskForGeneral"] = function(jsonData) {
// jsonData: string[] Generals
// TODO: choose multiple generals
let data = JSON.parse(jsonData);
roomScene.popupBox.source = "RoomElement/ChooseGeneralBox.qml";
let box = roomScene.popupBox.item;
box.choiceNum = 1;
box.accepted.connect(() => {
ClientInstance.replyToServer("AskForGeneral", JSON.stringify([box.choices[0]]));
});
for (let i = 0; i < data.length; i++)
box.generalList.append({ "name": data[i] });
box.updatePosition();
}

View File

@ -3,32 +3,39 @@
int main(int argc, char *argv[]) int main(int argc, char *argv[])
{ {
QGuiApplication app(argc, argv); QCoreApplication *app;
QGuiApplication::setApplicationName("FreeKill"); QCoreApplication::setApplicationName("FreeKill");
QGuiApplication::setApplicationVersion("Alpha 0.0.1"); QCoreApplication::setApplicationVersion("Alpha 0.0.1");
QCommandLineParser parser; QCommandLineParser parser;
parser.setApplicationDescription("FreeKill server"); parser.setApplicationDescription("FreeKill server");
parser.addHelpOption(); parser.addHelpOption();
parser.addVersionOption(); parser.addVersionOption();
parser.addOption({{"s", "server"}, "start server at <port>", "port"}); parser.addOption({{"s", "server"}, "start server at <port>", "port"});
parser.process(app); QStringList cliOptions;
for (int i = 0; i < argc; i++)
cliOptions << argv[i];
parser.parse(cliOptions);
bool startServer = parser.isSet("server"); bool startServer = parser.isSet("server");
ushort serverPort = 9527; ushort serverPort = 9527;
if (startServer) { if (startServer) {
app = new QCoreApplication(argc, argv);
bool ok = false; bool ok = false;
if (parser.value("server").toInt(&ok) && ok) if (parser.value("server").toInt(&ok) && ok)
serverPort = parser.value("server").toInt(); serverPort = parser.value("server").toInt();
Server *server = new Server; Server *server = new Server;
if (!server->listen(QHostAddress::Any, serverPort)) { if (!server->listen(QHostAddress::Any, serverPort)) {
fprintf(stderr, "cannot listen on port %d!\n", serverPort); fprintf(stderr, "cannot listen on port %d!\n", serverPort);
app.exit(1); app->exit(1);
} }
return app.exec(); return app->exec();
} }
app = new QGuiApplication(argc, argv);
QQmlApplicationEngine *engine = new QQmlApplicationEngine; QQmlApplicationEngine *engine = new QQmlApplicationEngine;
QmlBackend backend; QmlBackend backend;
@ -44,7 +51,7 @@ int main(int argc, char *argv[])
engine->rootContext()->setContextProperty("Debugging", debugging); engine->rootContext()->setContextProperty("Debugging", debugging);
engine->load("qml/main.qml"); engine->load("qml/main.qml");
int ret = app.exec(); int ret = app->exec();
// delete the engine first // delete the engine first
// to avoid "TypeError: Cannot read property 'xxx' of null" // to avoid "TypeError: Cannot read property 'xxx' of null"

View File

@ -60,7 +60,7 @@ void Router::request(int type, const QString& command,
replyMutex.lock(); replyMutex.lock();
expectedReplyId = requestId; expectedReplyId = requestId;
replyTimeout = 0; replyTimeout = timeout;
requestStartTime = QDateTime::currentDateTime(); requestStartTime = QDateTime::currentDateTime();
m_reply = QString(); m_reply = QString();
replyMutex.unlock(); replyMutex.unlock();
@ -123,7 +123,7 @@ QString Router::waitForReply()
QString Router::waitForReply(int timeout) QString Router::waitForReply(int timeout)
{ {
replyReadySemaphore.tryAcquire(1, timeout); replyReadySemaphore.tryAcquire(1, timeout * 1000);
return m_reply; return m_reply;
} }
@ -188,7 +188,6 @@ void Router::handlePacket(const QByteArray& rawPacket)
m_reply = jsonData; m_reply = jsonData;
// TODO: callback? // TODO: callback?
qDebug() << rawPacket << Qt::endl;
replyReadySemaphore.release(); replyReadySemaphore.release();
if (extraReplyReadySemaphore) { if (extraReplyReadySemaphore) {

View File

@ -8,7 +8,9 @@ Room::Room(Server* server)
server->nextRoomId++; server->nextRoomId++;
this->server = server; this->server = server;
setParent(server); setParent(server);
owner = nullptr;
gameStarted = false; gameStarted = false;
timeout = 15;
if (!isLobby()) { if (!isLobby()) {
connect(this, &Room::playerAdded, server->lobby(), &Room::removePlayer); connect(this, &Room::playerAdded, server->lobby(), &Room::removePlayer);
connect(this, &Room::playerRemoved, server->lobby(), &Room::addPlayer); connect(this, &Room::playerRemoved, server->lobby(), &Room::addPlayer);
@ -76,7 +78,7 @@ void Room::setOwner(ServerPlayer *owner)
this->owner = owner; this->owner = owner;
QJsonArray jsonData; QJsonArray jsonData;
jsonData << owner->getId(); jsonData << owner->getId();
owner->doNotify("RoomOwner", QJsonDocument(jsonData).toJson()); doBroadcastNotify(players, "RoomOwner", QJsonDocument(jsonData).toJson());
} }
void Room::addPlayer(ServerPlayer *player) void Room::addPlayer(ServerPlayer *player)
@ -101,6 +103,7 @@ void Room::addPlayer(ServerPlayer *player)
// Second, let the player enter room and add other players // Second, let the player enter room and add other players
jsonData = QJsonArray(); jsonData = QJsonArray();
jsonData << this->capacity; jsonData << this->capacity;
jsonData << this->timeout;
player->doNotify("EnterRoom", QJsonDocument(jsonData).toJson()); player->doNotify("EnterRoom", QJsonDocument(jsonData).toJson());
foreach (ServerPlayer *p, getOtherPlayers(player)) { foreach (ServerPlayer *p, getOtherPlayers(player)) {
@ -111,6 +114,12 @@ void Room::addPlayer(ServerPlayer *player)
player->doNotify("AddPlayer", QJsonDocument(jsonData).toJson()); player->doNotify("AddPlayer", QJsonDocument(jsonData).toJson());
} }
if (this->owner != nullptr) {
jsonData = QJsonArray();
jsonData << this->owner->getId();
player->doNotify("RoomOwner", QJsonDocument(jsonData).toJson());
}
if (isFull()) if (isFull())
start(); start();
} }
@ -150,11 +159,17 @@ QList<ServerPlayer *> Room::getOtherPlayers(ServerPlayer* expect) const
ServerPlayer *Room::findPlayer(int id) const ServerPlayer *Room::findPlayer(int id) const
{ {
foreach (ServerPlayer *p, players) { return server->findPlayer(id);
if (p->getId() == id) }
return p;
} int Room::getTimeout() const
return nullptr; {
return timeout;
}
void Room::setTimeout(int timeout)
{
this->timeout = timeout;
} }
bool Room::isStarted() const bool Room::isStarted() const

View File

@ -31,6 +31,9 @@ public:
QList<ServerPlayer *> getOtherPlayers(ServerPlayer *expect) const; QList<ServerPlayer *> getOtherPlayers(ServerPlayer *expect) const;
ServerPlayer *findPlayer(int id) const; ServerPlayer *findPlayer(int id) const;
int getTimeout() const;
void setTimeout(int timeout);
bool isStarted() const; bool isStarted() const;
// ====================================} // ====================================}
@ -64,6 +67,8 @@ private:
ServerPlayer *owner; // who created this room? ServerPlayer *owner; // who created this room?
QList<ServerPlayer *> players; QList<ServerPlayer *> players;
bool gameStarted; bool gameStarted;
int timeout;
}; };
#endif // _ROOM_H #endif // _ROOM_H

View File

@ -8,7 +8,7 @@ ServerPlayer::ServerPlayer(Room *room)
{ {
socket = nullptr; socket = nullptr;
router = new Router(this, socket, Router::TYPE_SERVER); router = new Router(this, socket, Router::TYPE_SERVER);
setState(Player::Online);
this->room = room; this->room = room;
server = room->getServer(); server = room->getServer();
} }
@ -68,6 +68,16 @@ void ServerPlayer::doRequest(const QString& command, const QString& jsonData, in
router->request(type, command, jsonData, timeout); router->request(type, command, jsonData, timeout);
} }
QString ServerPlayer::waitForReply()
{
return router->waitForReply();
}
QString ServerPlayer::waitForReply(int timeout)
{
return router->waitForReply(timeout);
}
void ServerPlayer::doNotify(const QString& command, const QString& jsonData) void ServerPlayer::doNotify(const QString& command, const QString& jsonData)
{ {
int type = Router::TYPE_NOTIFICATION | Router::SRC_SERVER | Router::DEST_CLIENT; int type = Router::TYPE_NOTIFICATION | Router::SRC_SERVER | Router::DEST_CLIENT;

View File

@ -24,6 +24,8 @@ public:
void doRequest(const QString &command, void doRequest(const QString &command,
const QString &jsonData, int timeout = -1); const QString &jsonData, int timeout = -1);
QString waitForReply(int timeout);
QString waitForReply();
void doNotify(const QString &command, const QString &jsonData); void doNotify(const QString &command, const QString &jsonData);
void prepareForRequest(const QString &command, void prepareForRequest(const QString &command,

View File

@ -46,7 +46,9 @@ public:
void speak(const QString &message); void speak(const QString &message);
void doRequest(const QString &command, void doRequest(const QString &command,
const QString &json_data, int timeout = -1); const QString &json_data, int timeout);
QString waitForReply();
QString waitForReply(int timeout);
void doNotify(const QString &command, const QString &json_data); void doNotify(const QString &command, const QString &json_data);
void prepareForRequest(const QString &command, const QString &data); void prepareForRequest(const QString &command, const QString &data);

View File

@ -34,3 +34,15 @@ public:
%template(PlayerList) QList<const Player *>; %template(PlayerList) QList<const Player *>;
%template(IntList) QList<int>; %template(IntList) QList<int>;
%template(BoolList) QList<bool>; %template(BoolList) QList<bool>;
%native(GetMicroSecond) int GetMicroSecond(lua_State *L);
%{
#include <sys/time.h>
static int GetMicroSecond(lua_State *L) {
struct timeval tv;
gettimeofday(&tv, nullptr);
long microsecond = tv.tv_sec * 1000000 + tv.tv_usec;
lua_pushnumber(L, microsecond);
return 1;
}
%}

View File

@ -71,6 +71,8 @@ public:
QList<ServerPlayer *> getPlayers() const; QList<ServerPlayer *> getPlayers() const;
ServerPlayer *findPlayer(int id) const; ServerPlayer *findPlayer(int id) const;
int getTimeout() const;
bool isStarted() const; bool isStarted() const;
// ====================================} // ====================================}