Disable sync (#21)

* fix running from running room

* fix: ~serverplayer()

* since room player's id won't change anymore, don't use :getId()

* we won't access lua from other thread, so drop the mutex

* bugfix
This commit is contained in:
notify 2022-05-02 14:00:47 +08:00 committed by GitHub
parent 5628e67a78
commit 60b9839fd7
16 changed files with 97 additions and 138 deletions

View File

@ -43,7 +43,11 @@ fk.client_callback["Setup"] = function(jsonData)
self:setScreenName(name)
self:setAvatar(avatar)
Self = ClientPlayer:new(fk.Self)
table.insert(ClientInstance.players, Self)
end
fk.client_callback["EnterRoom"] = function(jsonData)
ClientInstance.players = {Self}
ClientInstance:notifyUI("EnterRoom", jsonData)
end
fk.client_callback["AddPlayer"] = function(jsonData)

View File

@ -150,10 +150,22 @@ function GameLogic:action()
self:trigger(fk.DrawInitialCards, p, { num = 4 })
end
local function checkNoHuman()
for _, p in ipairs(room.players) do
if p.serverplayer:getStateString() == "online" then
return false
end
end
return true
end
while true do
self:trigger(fk.TurnStart, room.current)
if room.game_finished then break end
room.current = room.current:getNextAlive()
if checkNoHuman() then
room:gameOver()
end
end
end

View File

@ -52,7 +52,6 @@ end
function Room:run()
for _, p in fk.qlist(self.room:getPlayers()) do
local player = ServerPlayer:new(p)
player.state = p:getStateString()
player.room = self
table.insert(self.players, player)
end
@ -74,7 +73,7 @@ end
---@param property string
function Room:notifyProperty(p, player, property)
p:doNotify("PropertyUpdate", json.encode{
player:getId(),
player.id,
property,
player[property],
})
@ -134,7 +133,7 @@ function Room:notifyMoveFocus(players, command)
local ids = {}
for _, p in ipairs(players) do
table.insert(ids, p:getId())
table.insert(ids, p.id)
end
self:doBroadcastNotify("MoveFocus", json.encode{
@ -165,7 +164,7 @@ function Room:adjustSeats()
local player_circle = {}
for i = 1, #self.players do
self.players[i].seat = i
table.insert(player_circle, self.players[i]:getId())
table.insert(player_circle, self.players[i].id)
end
self:doBroadcastNotify("ArrangeSeats", json.encode(player_circle))
@ -233,7 +232,7 @@ function Room:notifyMoveCards(players, card_moves, forceVisible)
-- FIXME: move.moveInfo is an array, fix this
move.moveVisible = (forceVisible)
-- if move is relevant to player, it should be open
or ((move.from == p:getId()) or (move.to == p:getId() and move.toArea ~= Card.PlayerSpecial))
or ((move.from == p.id) or (move.to == p.id and move.toArea ~= Card.PlayerSpecial))
-- cards move from/to equip/judge/discard/processing should be open
or move.moveInfo.fromArea == Card.PlayerEquip
or move.toArea == Card.PlayerEquip
@ -364,10 +363,10 @@ function Room:drawCards(player, num, skillName, fromPlace)
local topCards = self:getNCards(num, fromPlace)
self:moveCards({
ids = topCards,
to = player:getId(),
to = player.id,
toArea = Card.PlayerHand,
moveReason = fk.ReasonDraw,
proposer = player:getId(),
proposer = player.id,
skillName = skillName,
})
@ -394,10 +393,10 @@ function Room:askForDiscard(player, minNum, maxNum, includeEquip, skillName)
self:moveCards({
ids = toDiscard,
from = player:getId(),
from = player.id,
toArea = Card.DiscardPile,
moveReason = fk.ReasonDiscard,
proposer = player:getId(),
proposer = player.id,
skillName = skillName
})
end
@ -408,7 +407,7 @@ function Room:getPlayerById(id)
assert(type(id) == "number")
for _, p in ipairs(self.players) do
if p:getId() == id then
if p.id == id then
return p
end
end
@ -453,7 +452,7 @@ end
function Room:getOtherPlayers(player, sortBySeat)
local alivePlayers = self:getAlivePlayers(sortBySeat)
for _, p in ipairs(alivePlayers) do
if p:getId() == player:getId() then
if p.id == player.id then
table.removeOne(alivePlayers, player)
break
end
@ -568,7 +567,7 @@ function Room:changeHp(player, num, reason, skillName, damageStruct)
if player.hp < 1 then
---@type DyingStruct
local dyingStruct = {
who = player:getId(),
who = player.id,
damage = damageStruct,
}
self:enterDying(dyingStruct)
@ -624,7 +623,7 @@ function Room:changeMaxHp(player, num)
end
if player.maxHp == 0 then
self:killPlayer({ who = player:getId() })
self:killPlayer({ who = player.id })
end
self.logic:trigger(fk.MaxHpChanged, player, { num = num })
@ -718,7 +717,7 @@ function Room:enterDying(dyingStruct)
if dyingPlayer.hp < 1 then
---@type DeathStruct
local deathData = {
who = dyingPlayer:getId(),
who = dyingPlayer.id,
damage = dyingStruct.damage,
}
self:killPlayer(deathData)
@ -976,45 +975,6 @@ fk.room_callback["AddRobot"] = function(jsonData)
end
end
fk.room_callback["PlayerRunned"] = function(jsonData)
-- jsonData: [ int runner_id, int robot_id ]
-- note: this function is not called by Router.
-- note: when this function is called, the room must be started
local data = json.decode(jsonData)
local runner = data[1]
local robot = data[2]
for _, p in ipairs(RoomInstance.players) do
if p:getId() == runner then
p.serverplayer = RoomInstance.room:findPlayer(robot)
p.id = p.serverplayer:getId()
end
end
end
fk.room_callback["PlayerStateChanged"] = function(jsonData)
-- jsonData: [ int uid, string stateString ]
-- note: this function is not called by Router.
-- note: when this function is called, the room must be started
local data = json.decode(jsonData)
local id = data[1]
local stateString = data[2]
RoomInstance:getPlayerById(id).state = stateString
end
fk.room_callback["RoomDeleted"] = function(jsonData)
debug.sethook(function ()
error("Room is deleted when running")
end, "l")
end
fk.room_callback["DoLuaScript"] = function(jsonData)
-- jsonData: [ int uid, string luaScript ]
-- warning: only use this in debugging mode.
if not DebugMode then return end
local data = json.decode(jsonData)
assert(load(data[2]))()
end
function CreateRoom(_room)
RoomInstance = Room:new(_room)
end

View File

@ -15,6 +15,7 @@ function ServerPlayer:initialize(_self)
Player.initialize(self)
self.serverplayer = _self
self.id = _self:getId()
self.state = _self:getStateString()
self.room = nil
self.next = nil
@ -27,11 +28,6 @@ function ServerPlayer:initialize(_self)
self.phases = {}
end
---@return integer
function ServerPlayer:getId()
return self.id
end
---@param command string
---@param jsonData string
function ServerPlayer:doNotify(command, jsonData)

View File

@ -33,7 +33,7 @@ GameRule = fk.CreateTriggerSkill{
player:addCards(Player.Hand, cardIds)
local move_to_notify = {} ---@type CardsMoveStruct
move_to_notify.toArea = Card.PlayerHand
move_to_notify.to = player:getId()
move_to_notify.to = player.id
move_to_notify.moveInfo = {}
for _, id in ipairs(cardIds) do
table.insert(move_to_notify.moveInfo,
@ -85,14 +85,14 @@ GameRule = fk.CreateTriggerSkill{
[Player.Play] = function()
while not player.dead do
room:notifyMoveFocus(player, "PlayCard")
local result = room:doRequest(player, "PlayCard", player:getId())
local result = room:doRequest(player, "PlayCard", player.id)
if result == "" then break end
local data = json.decode(result)
local card = data.card
local targets = data.targets
local use = {} ---@type CardUseStruct
use.from = player:getId()
use.from = player.id
use.tos = {}
for _, target in ipairs(targets) do
table.insert(use.tos, { target })

View File

@ -1,6 +1,6 @@
import QtQuick 2.15
import QtQuick.Controls 2.0
import QtQuick.Window 2.0
import QtQuick.Window 2.15
import "Logic.js" as Logic
import "Pages"
@ -130,4 +130,8 @@ Window {
}
}
}
onClosing: {
Backend.quitLobby();
}
}

View File

@ -56,6 +56,8 @@ QString Player::getStateString() const
return QStringLiteral("online");
case Trust:
return QStringLiteral("trust");
case Run:
return QStringLiteral("run");
case Robot:
return QStringLiteral("robot");
case Offline:
@ -77,6 +79,8 @@ void Player::setStateString(const QString &state)
setState(Online);
else if (state == QStringLiteral("trust"))
setState(Trust);
else if (state == QStringLiteral("run"))
setState(Run);
else if (state == QStringLiteral("robot"))
setState(Robot);
else if (state == QStringLiteral("offline"))

View File

@ -10,7 +10,8 @@ public:
enum State{
Invalid,
Online,
Trust, // Trust or run
Trust,
Run,
Robot, // only for real robot
Offline
};

View File

@ -161,9 +161,7 @@ void Router::handlePacket(const QByteArray& rawPacket)
arr.prepend(player->getId());
Room *room = player->getRoom();
room->lockLua(__FUNCTION__);
room->callLua(command, QJsonDocument(arr).toJson());
room->unlockLua(__FUNCTION__);
}
}
else if (type & TYPE_REQUEST) {

View File

@ -32,8 +32,6 @@ Room::~Room()
{
// TODO
if (isRunning()) {
callLua("RoomDeleted", "");
unlockLua(__FUNCTION__);
wait();
}
lua_close(L);
@ -173,44 +171,39 @@ void Room::addRobot(ServerPlayer *player)
void Room::removePlayer(ServerPlayer *player)
{
players.removeOne(player);
emit playerRemoved(player);
if (!gameStarted) {
players.removeOne(player);
emit playerRemoved(player);
if (isLobby()) return;
if (gameStarted) {
// TODO: if the player is died..
// create robot first
ServerPlayer *robot = new ServerPlayer(this);
robot->setState(Player::Robot);
robot->setId(robot_id);
robot->setAvatar(player->getAvatar());
robot->setScreenName(QString("COMP-%1").arg(robot_id));
robot_id--;
players.append(robot);
// tell lua & clients
QJsonArray jsonData;
jsonData << player->getId();
jsonData << robot->getId();
callLua("PlayerRunned", QJsonDocument(jsonData).toJson());
doBroadcastNotify(getPlayers(), "PlayerRunned", QJsonDocument(jsonData).toJson());
runned_players << player->getId();
// FIXME: abortRequest here will result crash
// but if dont abort and room is abandoned, the main thread will wait until replyed
// player->abortRequest();
} else {
QJsonArray jsonData;
jsonData << player->getId();
doBroadcastNotify(getPlayers(), "RemovePlayer", QJsonDocument(jsonData).toJson());
if (isLobby()) return;
} else {
// TODO: if the player is died..
// change the socket and state to runned player
ClientSocket *socket = player->getSocket();
player->setState(Player::Run);
// and then create a new ServerPlayer for the runner
ServerPlayer *runner = new ServerPlayer(this);
runner->setSocket(socket);
connect(runner, &ServerPlayer::disconnected, server, &Server::onUserDisconnected);
connect(runner, &Player::stateChanged, server, &Server::onUserStateChanged);
runner->setScreenName(player->getScreenName());
runner->setAvatar(player->getAvatar());
runner->setId(player->getId());
// finally update Server's player list and clean
server->addPlayer(runner);
emit playerRemoved(runner);
runner->abortRequest();
}
if (isAbandoned()) {
// FIXME: do not delete room here
// create a new thread and delete the room
emit abandoned();
} else if (player == owner) {
setOwner(players.first());
@ -283,28 +276,8 @@ void Room::gameOver()
}
}
void Room::lockLua(const QString &caller)
{
if (!gameStarted) return;
lua_mutex.lock();
#ifdef QT_DEBUG
//qDebug() << caller << "=> room->L is locked.";
#endif
}
void Room::unlockLua(const QString &caller)
{
if (!gameStarted) return;
lua_mutex.unlock();
#ifdef QT_DEBUG
//qDebug() << caller << "=> room->L is unlocked.";
#endif
}
void Room::run()
{
gameStarted = true;
lockLua(__FUNCTION__);
roomStart();
unlockLua(__FUNCTION__);
}

View File

@ -56,9 +56,6 @@ public:
void roomStart();
LuaFunction startGame;
void lockLua(const QString &caller);
void unlockLua(const QString &caller);
signals:
void abandoned();

View File

@ -67,6 +67,15 @@ ServerPlayer *Server::findPlayer(int id) const
return players.value(id);
}
void Server::addPlayer(ServerPlayer *player)
{
int id = player->getId();
if (players.contains(id))
players.remove(id);
players.insert(id, player);
}
void Server::removePlayer(int id) {
players.remove(id);
}
@ -252,10 +261,4 @@ void Server::onUserDisconnected()
}
void Server::onUserStateChanged()
{
ServerPlayer *player = qobject_cast<ServerPlayer *>(sender());
QJsonArray arr;
arr << player->getId();
arr << player->getStateString();
player->getRoom()->callLua("PlayerStateChanged", QJsonDocument(arr).toJson());
}
{}

View File

@ -21,6 +21,7 @@ public:
Room *lobby() const;
ServerPlayer *findPlayer(int id) const;
void addPlayer(ServerPlayer *player);
void removePlayer(int id);
void updateRoomList();

View File

@ -21,7 +21,8 @@ ServerPlayer::~ServerPlayer()
// now we are in lobby, so quit lobby
room->removePlayer(this);
}
server->removePlayer(getId());
if (server->findPlayer(getId()) == this)
server->removePlayer(getId());
router->deleteLater();
}
@ -42,6 +43,11 @@ void ServerPlayer::setSocket(ClientSocket *socket)
router->setSocket(socket);
}
ClientSocket *ServerPlayer::getSocket() const
{
return socket;
}
Server *ServerPlayer::getServer() const
{
return server;
@ -76,21 +82,20 @@ void ServerPlayer::abortRequest()
QString ServerPlayer::waitForReply()
{
room->unlockLua(__FUNCTION__);
QString ret;
if (getState() != Player::Online) {
QThread::sleep(1);
ret = "";
Player::State state = getState();
if (state != Player::Online) {
if (state != Player::Run)
QThread::sleep(1);
ret = QString("__state=%1").arg(getStateString());
} else {
ret = router->waitForReply();
}
room->lockLua(__FUNCTION__);
return ret;
}
QString ServerPlayer::waitForReply(int timeout)
{
room->unlockLua(__FUNCTION__);
QString ret;
if (getState() != Player::Online) {
QThread::sleep(1);
@ -98,7 +103,6 @@ QString ServerPlayer::waitForReply(int timeout)
} else {
ret = router->waitForReply(timeout);
}
room->lockLua(__FUNCTION__);
return ret;
}

View File

@ -15,6 +15,7 @@ public:
~ServerPlayer();
void setSocket(ClientSocket *socket);
ClientSocket *getSocket() const;
Server *getServer() const;
Room *getRoom() const;

View File

@ -58,7 +58,8 @@ void QmlBackend::joinServer(QString address)
void QmlBackend::quitLobby()
{
delete ClientInstance;
if (ClientInstance)
delete ClientInstance;
}
void QmlBackend::emitNotifyUI(const QString &command, const QString &jsonData) {