Enhancement2 (#275)
- TODO: 好友系统,目前在画饼状态 - 修老朱然bug,现在可以在cleaner环节胆守 - 修卢弈死了手谈不消失(UI) - 修重连时丢失房主信息
This commit is contained in:
parent
d1619672a2
commit
183dae9ae1
|
@ -58,6 +58,9 @@ Item {
|
||||||
if (usedtimes >= 1) {
|
if (usedtimes >= 1) {
|
||||||
x.visible = true;
|
x.visible = true;
|
||||||
bg.source = SkinBank.LIMIT_SKILL_DIR + "limit-used";
|
bg.source = SkinBank.LIMIT_SKILL_DIR + "limit-used";
|
||||||
|
} else {
|
||||||
|
x.visible = false;
|
||||||
|
bg.source = SkinBank.LIMIT_SKILL_DIR + "limit";
|
||||||
}
|
}
|
||||||
} else if (skilltype === 'switch') {
|
} else if (skilltype === 'switch') {
|
||||||
visible = true;
|
visible = true;
|
||||||
|
|
|
@ -0,0 +1,45 @@
|
||||||
|
关于好友系统
|
||||||
|
=============
|
||||||
|
|
||||||
|
“好友系统”这几个字可谓是囊括的面有点多啊。总而言之:
|
||||||
|
|
||||||
|
- 添加好友、管理好友、删除好友
|
||||||
|
- 和好友进行私聊
|
||||||
|
|
||||||
|
没了,就只有以上两点而已。
|
||||||
|
|
||||||
|
好友信息肯定要放在各个服务器自己的数据库中。
|
||||||
|
|
||||||
|
登入时自动获取好友列表和消息列表。因此好友列表有上限,最多50人,不然负载太大。
|
||||||
|
列表至少要有好友的名字、头像,然后最好还有在线状态。前二者查数据库,第三者要根据id在Server范围查询玩家,然后根据是否找的出、Room是大厅还是确切Room、Room是否已开始分为离线、空闲、等待中、游戏中、观战中四个状态。
|
||||||
|
|
||||||
|
好友列表查出之后就要暂时放在ServerPlayer类里面,也就是放在RAM中。因为状态一变动就要广播所有好友自己的状态,变动的情况有:
|
||||||
|
|
||||||
|
- 登入/登出;
|
||||||
|
- 进入房间/进入大厅;
|
||||||
|
- 游戏开始时/结束时
|
||||||
|
|
||||||
|
每当状态变化了就通知好友?还是好友进大厅的时候就获取一次信息?答案是要一直通知。
|
||||||
|
但是现阶段可以先只针对大厅中的好友通知。毕竟房间里面没地方摆好友UI呢。
|
||||||
|
|
||||||
|
简而言之,只要进入大厅,就获取好友信息(和自动获取房间列表性质一致)。然后一直接收通知修改好友状态。
|
||||||
|
|
||||||
|
接下来就是处理如何加好友了,顺便处理私聊之事。
|
||||||
|
首先加好友只能在Room中加,这就避免了搜索好友的问题。将加好友请求视为一种特殊的私信吧。
|
||||||
|
|
||||||
|
私信
|
||||||
|
-----
|
||||||
|
|
||||||
|
私信的问题在于已读和未读。未读信息要暂存在服务器;已读信息和聊天记录可以放在客户端本地的数据库中。
|
||||||
|
|
||||||
|
反正二者都要用到数据库啊。综上,涉及三张表:服务端需要好友关系表、未读信息表;客户端需要聊天记录表。
|
||||||
|
|
||||||
|
服务器端 - 好友关系表:
|
||||||
|
|
||||||
|
.. code:: sql
|
||||||
|
|
||||||
|
CREATE TABLE 好友 {
|
||||||
|
user1, user2, type
|
||||||
|
}
|
||||||
|
|
||||||
|
type是个数字,可能是好友或者黑名单。
|
|
@ -621,8 +621,9 @@ end
|
||||||
local function updateLimitSkill(pid, skill, times)
|
local function updateLimitSkill(pid, skill, times)
|
||||||
if not skill.visible then return end
|
if not skill.visible then return end
|
||||||
if skill:isSwitchSkill() then
|
if skill:isSwitchSkill() then
|
||||||
times = ClientInstance:getPlayerById(pid):getSwitchSkillState(skill.switchSkillName) == fk.SwitchYang and 0 or 1
|
local _times = ClientInstance:getPlayerById(pid):getSwitchSkillState(skill.switchSkillName) == fk.SwitchYang and 0 or 1
|
||||||
ClientInstance:notifyUI("UpdateLimitSkill", json.encode{ pid, skill.switchSkillName, times })
|
if times == -1 then _times = -1 end
|
||||||
|
ClientInstance:notifyUI("UpdateLimitSkill", json.encode{ pid, skill.switchSkillName, _times })
|
||||||
elseif skill.frequency == Skill.Limited or skill.frequency == Skill.Wake or skill.frequency == Skill.Quest then
|
elseif skill.frequency == Skill.Limited or skill.frequency == Skill.Wake or skill.frequency == Skill.Quest then
|
||||||
ClientInstance:notifyUI("UpdateLimitSkill", json.encode{ pid, skill.name, times })
|
ClientInstance:notifyUI("UpdateLimitSkill", json.encode{ pid, skill.name, times })
|
||||||
end
|
end
|
||||||
|
|
|
@ -41,6 +41,7 @@ dofile "lua/server/events/gameflow.lua"
|
||||||
GameEvent.Pindian = 19
|
GameEvent.Pindian = 19
|
||||||
dofile "lua/server/events/pindian.lua"
|
dofile "lua/server/events/pindian.lua"
|
||||||
|
|
||||||
|
-- 20 = CardEffect
|
||||||
GameEvent.ChangeProperty = 21
|
GameEvent.ChangeProperty = 21
|
||||||
dofile "lua/server/events/misc.lua"
|
dofile "lua/server/events/misc.lua"
|
||||||
|
|
||||||
|
|
|
@ -13,7 +13,9 @@
|
||||||
---@field public extra_clear_funcs fun(self:GameEvent)[] @ 事件结束时执行的自定义函数列表
|
---@field public extra_clear_funcs fun(self:GameEvent)[] @ 事件结束时执行的自定义函数列表
|
||||||
---@field public exit_func fun(self: GameEvent) @ 事件结束后执行的函数
|
---@field public exit_func fun(self: GameEvent) @ 事件结束后执行的函数
|
||||||
---@field public extra_exit_funcs fun(self:GameEvent)[] @ 事件结束后执行的自定义函数
|
---@field public extra_exit_funcs fun(self:GameEvent)[] @ 事件结束后执行的自定义函数
|
||||||
---@field public interrupted boolean @ 事件是否是因为被强行中断而结束的
|
---@field public interrupted boolean @ 事件是否是因为被中断而结束的,可能是防止事件或者被杀
|
||||||
|
---@field public killed boolean @ 事件因为终止一切结算而被中断(所谓的“被杀”)
|
||||||
|
---@field public revived boolean @ 事件被killed,但因为在cleaner中发生而被复活
|
||||||
local GameEvent = class("GameEvent")
|
local GameEvent = class("GameEvent")
|
||||||
|
|
||||||
---@type (fun(self: GameEvent): bool)[]
|
---@type (fun(self: GameEvent): bool)[]
|
||||||
|
@ -169,6 +171,8 @@ function GameEvent:clear()
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
local zhuran_jmp, zhuran_msg -- SB老朱然
|
||||||
|
|
||||||
while true do
|
while true do
|
||||||
local err, yield_result, extra_yield_result = coroutine.resume(clear_co)
|
local err, yield_result, extra_yield_result = coroutine.resume(clear_co)
|
||||||
|
|
||||||
|
@ -185,12 +189,37 @@ function GameEvent:clear()
|
||||||
if yield_result == "__handleRequest" then
|
if yield_result == "__handleRequest" then
|
||||||
-- yield to requestLoop
|
-- yield to requestLoop
|
||||||
coroutine.yield(yield_result, extra_yield_result)
|
coroutine.yield(yield_result, extra_yield_result)
|
||||||
|
|
||||||
|
elseif type(yield_result) == "table" and yield_result.class
|
||||||
|
and yield_result:isInstanceOf(GameEvent) and self ~= yield_result then
|
||||||
|
|
||||||
|
-- 不是,谁TM还在cleaner里面玩老朱然啊
|
||||||
|
-- 总之,cleaner不能断
|
||||||
|
-- 倒是没必要手动resume,新一轮while true会自动resume,只要把返回值
|
||||||
|
-- 传回去就行
|
||||||
|
|
||||||
|
-- 一般来说都是由cleaner中的trigger引起
|
||||||
|
-- 以胆守合击为例就是trigger -> SkillEffect事件 -> UseCard事件 -> 胆守
|
||||||
|
-- 此时胆守的话最后从SkillEffect事件的exec内部yield出来
|
||||||
|
-- 当前协程就应该正在执行room:useSkill函数,resume会去只会让那个函数返回
|
||||||
|
|
||||||
|
if zhuran_jmp == nil or zhuran_jmp.id > yield_result.id then
|
||||||
|
zhuran_jmp = yield_result
|
||||||
|
zhuran_msg = extra_yield_result
|
||||||
|
end
|
||||||
|
|
||||||
|
-- 自己本来应该被杀的但是因为自己正在执行self:clear()而逃过一劫啊
|
||||||
|
-- 还是得标记一下被杀才行,顺便因为实际上没死所以标记被复活
|
||||||
|
self.killed = true
|
||||||
|
self.revived = true
|
||||||
|
-- 什么都不做,等下轮while自己resume
|
||||||
else
|
else
|
||||||
coroutine.close(clear_co)
|
coroutine.close(clear_co)
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- cleaner顺利执行完了,出栈吧
|
||||||
local logic = RoomInstance.logic
|
local logic = RoomInstance.logic
|
||||||
local end_id = logic.current_event_id + 1
|
local end_id = logic.current_event_id + 1
|
||||||
if self.id ~= end_id - 1 then
|
if self.id ~= end_id - 1 then
|
||||||
|
@ -203,6 +232,21 @@ function GameEvent:clear()
|
||||||
|
|
||||||
logic.game_event_stack:pop()
|
logic.game_event_stack:pop()
|
||||||
|
|
||||||
|
-- 好了确保cleaner走完了,此时中断就会进入下层事件的正常中断处理
|
||||||
|
if zhuran_jmp then
|
||||||
|
coroutine.close(self._co)
|
||||||
|
coroutine.yield(zhuran_jmp, zhuran_msg)
|
||||||
|
|
||||||
|
-- 此时仍可能出现在插结在其他事件的clear函数中
|
||||||
|
-- 但就算被交付回去了,也能安然返回而不是继续while
|
||||||
|
-- 但愿如此吧
|
||||||
|
end
|
||||||
|
|
||||||
|
-- 保险而已,其实如果被杀的话应该已经在前面的yield终止了
|
||||||
|
-- 但担心cleaner嵌套(三国杀是这样的)还是补一刀
|
||||||
|
if self.killed then return end
|
||||||
|
|
||||||
|
-- 恭喜没被杀掉,我们来执行一些事件结束之后的结算吧
|
||||||
Pcall(self.exit_func, self)
|
Pcall(self.exit_func, self)
|
||||||
for _, f in ipairs(self.extra_exit_funcs) do
|
for _, f in ipairs(self.extra_exit_funcs) do
|
||||||
if type(f) == "function" then
|
if type(f) == "function" then
|
||||||
|
@ -243,6 +287,7 @@ function GameEvent:exec()
|
||||||
table.insert(logic.event_recorder[self.event], self)
|
table.insert(logic.event_recorder[self.event], self)
|
||||||
|
|
||||||
local co = coroutine.create(self.main_func)
|
local co = coroutine.create(self.main_func)
|
||||||
|
self._co = co
|
||||||
while true do
|
while true do
|
||||||
local err, yield_result, extra_yield_result = coroutine.resume(co)
|
local err, yield_result, extra_yield_result = coroutine.resume(co)
|
||||||
|
|
||||||
|
@ -269,12 +314,17 @@ function GameEvent:exec()
|
||||||
if self ~= yield_result then
|
if self ~= yield_result then
|
||||||
-- yield to corresponding GameEvent, first pop self from stack
|
-- yield to corresponding GameEvent, first pop self from stack
|
||||||
self.interrupted = true
|
self.interrupted = true
|
||||||
|
self.killed = true -- 老朱然!你不得好死
|
||||||
self:clear()
|
self:clear()
|
||||||
-- logic.game_event_stack:pop(self)
|
-- logic.game_event_stack:pop(self)
|
||||||
coroutine.close(co)
|
coroutine.close(co)
|
||||||
|
|
||||||
-- then, call yield
|
-- then, call yield
|
||||||
coroutine.yield(yield_result, extra_yield_result)
|
coroutine.yield(yield_result, extra_yield_result)
|
||||||
|
|
||||||
|
-- 如果是在cleaner/exit里面发生此类中断的话是会被cleaner原地返回的
|
||||||
|
-- 此时正常执行程序流就变成继续while循环了,这是不行的
|
||||||
|
break
|
||||||
elseif extra_yield_result == "__breakEvent" then
|
elseif extra_yield_result == "__breakEvent" then
|
||||||
if breakEvent(self) then
|
if breakEvent(self) then
|
||||||
coroutine.close(co)
|
coroutine.close(co)
|
||||||
|
|
|
@ -335,6 +335,10 @@ function GameLogic:trigger(event, target, data, refresh_only)
|
||||||
local skills_to_refresh = self.refresh_skill_table[event] or Util.DummyTable
|
local skills_to_refresh = self.refresh_skill_table[event] or Util.DummyTable
|
||||||
local _target = room.current -- for iteration
|
local _target = room.current -- for iteration
|
||||||
local player = _target
|
local player = _target
|
||||||
|
local cur_event = self:getCurrentEvent() or {}
|
||||||
|
-- 如果当前事件被杀,就强制只refresh
|
||||||
|
-- 因为被杀的事件再进行正常trigger只可能在cleaner和exit了
|
||||||
|
refresh_only = refresh_only or cur_event.killed
|
||||||
|
|
||||||
if #skills_to_refresh > 0 then repeat do
|
if #skills_to_refresh > 0 then repeat do
|
||||||
-- refresh skills. This should not be broken
|
-- refresh skills. This should not be broken
|
||||||
|
@ -380,7 +384,9 @@ function GameLogic:trigger(event, target, data, refresh_only)
|
||||||
skill_names = table.map(table.filter(skills, filter_func), Util.NameMapper)
|
skill_names = table.map(table.filter(skills, filter_func), Util.NameMapper)
|
||||||
|
|
||||||
broken = broken or (event == fk.AskForPeaches
|
broken = broken or (event == fk.AskForPeaches
|
||||||
and room:getPlayerById(data.who).hp > 0)
|
and room:getPlayerById(data.who).hp > 0) or cur_event.revived
|
||||||
|
-- ^^^^^^^^^^^^^^^^^
|
||||||
|
-- 如果事件复活了,那么其实说明事件已经死过了,赶紧break掉
|
||||||
|
|
||||||
if broken then break end
|
if broken then break end
|
||||||
end
|
end
|
||||||
|
|
|
@ -339,6 +339,7 @@ function ServerPlayer:reconnect()
|
||||||
p._splayer:getAvatar(),
|
p._splayer:getAvatar(),
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
|
self:doNotify("RoomOwner", json.encode{ room.room:getOwner():getId() })
|
||||||
|
|
||||||
local player_circle = {}
|
local player_circle = {}
|
||||||
for i = 1, #room.players do
|
for i = 1, #room.players do
|
||||||
|
|
|
@ -25,6 +25,12 @@ CREATE TABLE IF NOT EXISTS banuuid (
|
||||||
uuid VARCHAR(32)
|
uuid VARCHAR(32)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS friendinfo (
|
||||||
|
id1 INTEGER,
|
||||||
|
id2 INTEGER,
|
||||||
|
reltype INTEGER -- 1=好友 2=黑名单
|
||||||
|
);
|
||||||
|
|
||||||
-- 胜率相关
|
-- 胜率相关
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS winRate (
|
CREATE TABLE IF NOT EXISTS winRate (
|
||||||
|
|
|
@ -451,15 +451,14 @@ void Server::handleNameAndPassword(ClientSocket *client, const QString &name,
|
||||||
client->disconnect(this);
|
client->disconnect(this);
|
||||||
if (players.count() <= 10) {
|
if (players.count() <= 10) {
|
||||||
broadcast("ServerMessage", tr("%1 backed").arg(player->getScreenName()));
|
broadcast("ServerMessage", tr("%1 backed").arg(player->getScreenName()));
|
||||||
if (room->getOwner() == player) {
|
|
||||||
auto owner = room->getOwner();
|
|
||||||
auto jsonData = QJsonArray();
|
|
||||||
jsonData << owner->getId();
|
|
||||||
player->doNotify("RoomOwner", JsonArray2Bytes(jsonData));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (room && !room->isLobby()) {
|
if (room && !room->isLobby()) {
|
||||||
|
player->doNotify("SetServerSettings", JsonArray2Bytes({
|
||||||
|
getConfig("motd"),
|
||||||
|
getConfig("hiddenPacks"),
|
||||||
|
getConfig("enableBots"),
|
||||||
|
}));
|
||||||
room->pushRequest(QString("%1,reconnect").arg(id));
|
room->pushRequest(QString("%1,reconnect").arg(id));
|
||||||
} else {
|
} else {
|
||||||
// 懒得处理掉线玩家在大厅了!踢掉得了
|
// 懒得处理掉线玩家在大厅了!踢掉得了
|
||||||
|
@ -670,6 +669,16 @@ void Server::temporarilyBan(int playerId) {
|
||||||
emit player->kicked();
|
emit player->kicked();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Server::beginTransaction() {
|
||||||
|
transaction_mutex.lock();
|
||||||
|
ExecSQL(db, "BEGIN;");
|
||||||
|
}
|
||||||
|
|
||||||
|
void Server::endTransaction() {
|
||||||
|
ExecSQL(db, "COMMIT;");
|
||||||
|
transaction_mutex.unlock();
|
||||||
|
}
|
||||||
|
|
||||||
void Server::readPendingDatagrams() {
|
void Server::readPendingDatagrams() {
|
||||||
while (udpSocket->hasPendingDatagrams()) {
|
while (udpSocket->hasPendingDatagrams()) {
|
||||||
QNetworkDatagram datagram = udpSocket->receiveDatagram();
|
QNetworkDatagram datagram = udpSocket->receiveDatagram();
|
||||||
|
|
|
@ -47,6 +47,9 @@ public:
|
||||||
bool checkBanWord(const QString &str);
|
bool checkBanWord(const QString &str);
|
||||||
void temporarilyBan(int playerId);
|
void temporarilyBan(int playerId);
|
||||||
|
|
||||||
|
void beginTransaction();
|
||||||
|
void endTransaction();
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void roomCreated(Room *room);
|
void roomCreated(Room *room);
|
||||||
void playerAdded(ServerPlayer *player);
|
void playerAdded(ServerPlayer *player);
|
||||||
|
@ -78,6 +81,7 @@ private:
|
||||||
RSA *rsa;
|
RSA *rsa;
|
||||||
QString public_key;
|
QString public_key;
|
||||||
sqlite3 *db;
|
sqlite3 *db;
|
||||||
|
QMutex transaction_mutex;
|
||||||
QString md5;
|
QString md5;
|
||||||
|
|
||||||
static RSA *initServerRSA();
|
static RSA *initServerRSA();
|
||||||
|
|
|
@ -9,6 +9,7 @@ public:
|
||||||
int getId() const;
|
int getId() const;
|
||||||
|
|
||||||
QList<ServerPlayer *> getPlayers() const;
|
QList<ServerPlayer *> getPlayers() const;
|
||||||
|
ServerPlayer *getOwner() const;
|
||||||
|
|
||||||
QList<ServerPlayer *> getObservers() const;
|
QList<ServerPlayer *> getObservers() const;
|
||||||
bool hasObserver(ServerPlayer *player) const;
|
bool hasObserver(ServerPlayer *player) const;
|
||||||
|
|
Loading…
Reference in New Issue