mirror of
https://github.com/Qsgs-Fans/FreeKill.git
synced 2024-11-15 19:22:25 +08:00
Enhancement2 (#275)
- TODO: 好友系统,目前在画饼状态 - 修老朱然bug,现在可以在cleaner环节胆守 - 修卢弈死了手谈不消失(UI) - 修重连时丢失房主信息
This commit is contained in:
parent
d1619672a2
commit
183dae9ae1
|
@ -58,6 +58,9 @@ Item {
|
|||
if (usedtimes >= 1) {
|
||||
x.visible = true;
|
||||
bg.source = SkinBank.LIMIT_SKILL_DIR + "limit-used";
|
||||
} else {
|
||||
x.visible = false;
|
||||
bg.source = SkinBank.LIMIT_SKILL_DIR + "limit";
|
||||
}
|
||||
} else if (skilltype === 'switch') {
|
||||
visible = true;
|
||||
|
|
45
docs/dev/friend_system.rst
Normal file
45
docs/dev/friend_system.rst
Normal file
|
@ -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)
|
||||
if not skill.visible then return end
|
||||
if skill:isSwitchSkill() then
|
||||
times = ClientInstance:getPlayerById(pid):getSwitchSkillState(skill.switchSkillName) == fk.SwitchYang and 0 or 1
|
||||
ClientInstance:notifyUI("UpdateLimitSkill", json.encode{ pid, skill.switchSkillName, times })
|
||||
local _times = ClientInstance:getPlayerById(pid):getSwitchSkillState(skill.switchSkillName) == fk.SwitchYang and 0 or 1
|
||||
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
|
||||
ClientInstance:notifyUI("UpdateLimitSkill", json.encode{ pid, skill.name, times })
|
||||
end
|
||||
|
|
|
@ -41,6 +41,7 @@ dofile "lua/server/events/gameflow.lua"
|
|||
GameEvent.Pindian = 19
|
||||
dofile "lua/server/events/pindian.lua"
|
||||
|
||||
-- 20 = CardEffect
|
||||
GameEvent.ChangeProperty = 21
|
||||
dofile "lua/server/events/misc.lua"
|
||||
|
||||
|
|
|
@ -13,7 +13,9 @@
|
|||
---@field public extra_clear_funcs fun(self:GameEvent)[] @ 事件结束时执行的自定义函数列表
|
||||
---@field public exit_func 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")
|
||||
|
||||
---@type (fun(self: GameEvent): bool)[]
|
||||
|
@ -169,6 +171,8 @@ function GameEvent:clear()
|
|||
end
|
||||
end)
|
||||
|
||||
local zhuran_jmp, zhuran_msg -- SB老朱然
|
||||
|
||||
while true do
|
||||
local err, yield_result, extra_yield_result = coroutine.resume(clear_co)
|
||||
|
||||
|
@ -185,12 +189,37 @@ function GameEvent:clear()
|
|||
if yield_result == "__handleRequest" then
|
||||
-- yield to requestLoop
|
||||
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
|
||||
coroutine.close(clear_co)
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
-- cleaner顺利执行完了,出栈吧
|
||||
local logic = RoomInstance.logic
|
||||
local end_id = logic.current_event_id + 1
|
||||
if self.id ~= end_id - 1 then
|
||||
|
@ -203,6 +232,21 @@ function GameEvent:clear()
|
|||
|
||||
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)
|
||||
for _, f in ipairs(self.extra_exit_funcs) do
|
||||
if type(f) == "function" then
|
||||
|
@ -243,6 +287,7 @@ function GameEvent:exec()
|
|||
table.insert(logic.event_recorder[self.event], self)
|
||||
|
||||
local co = coroutine.create(self.main_func)
|
||||
self._co = co
|
||||
while true do
|
||||
local err, yield_result, extra_yield_result = coroutine.resume(co)
|
||||
|
||||
|
@ -269,12 +314,17 @@ function GameEvent:exec()
|
|||
if self ~= yield_result then
|
||||
-- yield to corresponding GameEvent, first pop self from stack
|
||||
self.interrupted = true
|
||||
self.killed = true -- 老朱然!你不得好死
|
||||
self:clear()
|
||||
-- logic.game_event_stack:pop(self)
|
||||
coroutine.close(co)
|
||||
|
||||
-- then, call yield
|
||||
coroutine.yield(yield_result, extra_yield_result)
|
||||
|
||||
-- 如果是在cleaner/exit里面发生此类中断的话是会被cleaner原地返回的
|
||||
-- 此时正常执行程序流就变成继续while循环了,这是不行的
|
||||
break
|
||||
elseif extra_yield_result == "__breakEvent" then
|
||||
if breakEvent(self) then
|
||||
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 _target = room.current -- for iteration
|
||||
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
|
||||
-- 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)
|
||||
|
||||
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
|
||||
end
|
||||
|
|
|
@ -339,6 +339,7 @@ function ServerPlayer:reconnect()
|
|||
p._splayer:getAvatar(),
|
||||
})
|
||||
end
|
||||
self:doNotify("RoomOwner", json.encode{ room.room:getOwner():getId() })
|
||||
|
||||
local player_circle = {}
|
||||
for i = 1, #room.players do
|
||||
|
|
|
@ -25,6 +25,12 @@ CREATE TABLE IF NOT EXISTS banuuid (
|
|||
uuid VARCHAR(32)
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS friendinfo (
|
||||
id1 INTEGER,
|
||||
id2 INTEGER,
|
||||
reltype INTEGER -- 1=好友 2=黑名单
|
||||
);
|
||||
|
||||
-- 胜率相关
|
||||
|
||||
CREATE TABLE IF NOT EXISTS winRate (
|
||||
|
|
|
@ -451,15 +451,14 @@ void Server::handleNameAndPassword(ClientSocket *client, const QString &name,
|
|||
client->disconnect(this);
|
||||
if (players.count() <= 10) {
|
||||
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()) {
|
||||
player->doNotify("SetServerSettings", JsonArray2Bytes({
|
||||
getConfig("motd"),
|
||||
getConfig("hiddenPacks"),
|
||||
getConfig("enableBots"),
|
||||
}));
|
||||
room->pushRequest(QString("%1,reconnect").arg(id));
|
||||
} else {
|
||||
// 懒得处理掉线玩家在大厅了!踢掉得了
|
||||
|
@ -670,6 +669,16 @@ void Server::temporarilyBan(int playerId) {
|
|||
emit player->kicked();
|
||||
}
|
||||
|
||||
void Server::beginTransaction() {
|
||||
transaction_mutex.lock();
|
||||
ExecSQL(db, "BEGIN;");
|
||||
}
|
||||
|
||||
void Server::endTransaction() {
|
||||
ExecSQL(db, "COMMIT;");
|
||||
transaction_mutex.unlock();
|
||||
}
|
||||
|
||||
void Server::readPendingDatagrams() {
|
||||
while (udpSocket->hasPendingDatagrams()) {
|
||||
QNetworkDatagram datagram = udpSocket->receiveDatagram();
|
||||
|
|
|
@ -47,6 +47,9 @@ public:
|
|||
bool checkBanWord(const QString &str);
|
||||
void temporarilyBan(int playerId);
|
||||
|
||||
void beginTransaction();
|
||||
void endTransaction();
|
||||
|
||||
signals:
|
||||
void roomCreated(Room *room);
|
||||
void playerAdded(ServerPlayer *player);
|
||||
|
@ -78,6 +81,7 @@ private:
|
|||
RSA *rsa;
|
||||
QString public_key;
|
||||
sqlite3 *db;
|
||||
QMutex transaction_mutex;
|
||||
QString md5;
|
||||
|
||||
static RSA *initServerRSA();
|
||||
|
|
|
@ -9,6 +9,7 @@ public:
|
|||
int getId() const;
|
||||
|
||||
QList<ServerPlayer *> getPlayers() const;
|
||||
ServerPlayer *getOwner() const;
|
||||
|
||||
QList<ServerPlayer *> getObservers() const;
|
||||
bool hasObserver(ServerPlayer *player) const;
|
||||
|
|
Loading…
Reference in New Issue
Block a user