Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

单机热更新,以及Docker #336

Merged
merged 6 commits into from
Apr 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 26 additions & 13 deletions Fk/Cheat/PlayerDetail.qml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import Fk.Common
import Fk.Pages
import Fk.RoomElement

Expand All @@ -22,18 +23,29 @@ Flickable {
width: parent.width - 40
x: 20

// TODO: player details
Text {
id: screenName
font.pixelSize: 18
color: "#E4D5A0"
}
RowLayout {
spacing: 8
Avatar {
id: avatar
Layout.preferredWidth: 56
Layout.preferredHeight: 56
general: "diaochan"
}

ColumnLayout {
Text {
id: screenName
font.pixelSize: 18
color: "#E4D5A0"
}

Text {
id: playerGameData
Layout.fillWidth: true
font.pixelSize: 18
color: "#E4D5A0"
Text {
id: playerGameData
Layout.fillWidth: true
font.pixelSize: 18
color: "#E4D5A0"
}
}
}

RowLayout {
Expand Down Expand Up @@ -163,6 +175,7 @@ Flickable {
if (id === 0) return;
root.pid = id;

avatar.general = extra_data.photo.avatar;
screenName.text = extra_data.photo.screenName;
mainChara.name = extra_data.photo.general;
deputyChara.name = extra_data.photo.deputyGeneral;
Expand All @@ -182,9 +195,9 @@ Flickable {
const h = (totalTime / 3600).toFixed(2);
const m = Math.floor(totalTime / 60);
if (m < 100) {
playerGameData.text += " " + luatr("TotalGameTime: %1 min").arg(m);
screenName.text += " (" + luatr("TotalGameTime: %1 min").arg(m) + ")";
} else {
playerGameData.text += " " + luatr("TotalGameTime: %1 h").arg(h);
screenName.text += " (" + luatr("TotalGameTime: %1 h").arg(h) + ")";
}
}

Expand Down
42 changes: 23 additions & 19 deletions docker/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,25 +1,29 @@
FROM debian
USER root

FROM linuxcontainers/debian-slim:latest

# install dependencies
RUN apt update -y && apt upgrade -y && \
apt install -y \
gcc g++ cmake \
liblua5.4-dev libsqlite3-dev libreadline-dev libssl-dev libgit2-dev swig qt6-base-dev qt6-tools-dev-tools \
gosu && \
apt clean -y && \
rm -rf /var/lib/apt/lists/*

# prepare source code
COPY . /FreeKill

#update apt dependencies
RUN apt update -y && apt upgrade -y

#install compile tools
RUN apt install -y gcc g++ cmake
RUN apt install -y liblua5.4-dev libsqlite3-dev libreadline-dev libssl-dev libgit2-dev swig qt6-base-dev qt6-tools-dev-tools

#change workdir to FreeKill
WORKDIR /FreeKill
# compile and install
RUN mkdir -p /FreeKill/build && \
cd /FreeKill/build && cp -r /usr/include/lua5.4/* ../include && cmake .. -DFK_SERVER_ONLY= && make && \
cd /FreeKill && cmake --install build --config Release && \
cp /FreeKill/docker/docker-entrypoint.sh / && chmod +x /docker-entrypoint.sh && \
mkdir /data && \
cd / && rm -rf /FreeKill

#compile source code
RUN mkdir build && cd build && cp -r /usr/include/lua5.4/* ../include && cmake .. -DFK_SERVER_ONLY=
RUN cd build && make

#build soft link
RUN ln -s build/FreeKill
WORKDIR /data

EXPOSE 9527

ENTRYPOINT ["/FreeKill/FreeKill", "-s"]
ENTRYPOINT ["/docker-entrypoint.sh"]

CMD ["FreeKill", "-s"]
29 changes: 29 additions & 0 deletions docker/docker-entrypoint.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
#!/bin/bash

USER_ID=${LOCAL_USER_ID:-1000}

if [ "${1#-}" != "$1" ]; then
set -- FreeKill -s "$@"
fi

if [ "$1" = 'FreeKill' -a "$(id -u)" = '0' ]; then
id -u freekill >&/dev/null
if [ $? -ne 0 ]; then
useradd --shell /bin/bash -u $USER_ID -o -c "" -m freekill
usermod -aG root freekill
export HOME=/home/freekill
mkdir -p $HOME/.local/share
ln -s /data $HOME/.local/share/FreeKill
chown -R freekill:freekill $HOME
if [ ! -d "/data/server" ]; then
cp -r /usr/local/share/FreeKill/server /data
fi
if [ ! -d "/data/packages" ]; then
cp -r /usr/local/share/FreeKill/packages /data
fi
fi
chown -R freekill /data
exec gosu freekill "$0" "$@"
fi

exec "$@"
Binary file modified image/card/delayedTrick/unknown.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 4 additions & 0 deletions lua/client/client_util.lua
Original file line number Diff line number Diff line change
Expand Up @@ -781,4 +781,8 @@ function GetMiniGame(gtype, p, data)
}
end

function ReloadPackage(path)
Fk:reloadPackage(path)
end

dofile "lua/client/i18n/init.lua"
73 changes: 71 additions & 2 deletions lua/core/engine.lua
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,6 @@ function Engine:initialize()
end

Fk = self

self.extensions = {
["standard"] = { "standard" },
["standard_cards"] = { "standard_cards" },
Expand Down Expand Up @@ -120,6 +119,76 @@ function Engine:loadPackage(pack)
self:addGameModes(pack.game_modes)
end

-- Don't do this
local package = package
function Engine:reloadPackage(path)
path = path:sub(1, #path - 4)
local oldPkg = package.loaded[path]
package.loaded[path] = nil
local ok, err = pcall(require, path)
if not ok then
package.loaded[path] = oldPkg
print("reload failed:", err)
return
end

-- 阉割版重载机制,反正单机用
local function replace(t, skill)
if not t then return end
for k, s in pairs(t) do
if s.name == skill.name then
t[k] = skill
break
end
end
end

local function f(p)
self.packages[p.name] = p
local room = Fk:currentRoom()
local skills = p:getSkills()
local related = {}
for _, skill in ipairs(skills) do
table.insertTableIfNeed(related, skill.related_skills)
end
table.insertTableIfNeed(skills, related)

for _, skill in ipairs(skills) do
if self.skills[skill.name].class ~= skill.class then
fk.qCritical("cannot change class of skill: " .. skill.name)
goto CONTINUE
end
self.skills[skill.name] = skill
if skill:isInstanceOf(TriggerSkill) and RoomInstance then
local logic = room.logic
for _, event in ipairs(skill.refresh_events) do
replace(logic.refresh_skill_table[event], skill)
end
for _, event in ipairs(skill.events) do
replace(logic.skill_table[event], skill)
end
end
if skill:isInstanceOf(StatusSkill) then
replace(room.status_skills[skill.class], skill)
end

for _, p in ipairs(room.players) do
replace(p.player_skills, skill)
end
::CONTINUE::
end
end

local pkg = package.loaded[path]
if type(pkg) ~= "table" then return end
if pkg.class and pkg:isInstanceOf(Package) then
f(pkg)
elseif path:endsWith("init") and not path:find("/ai/") then
for _, p in ipairs(pkg) do f(p) end
end
end


--- 加载所有拓展包。
---
--- Engine会在packages/下搜索所有含有init.lua的文件夹,并把它们作为拓展包加载进来。
Expand Down Expand Up @@ -212,7 +281,7 @@ end
function Engine:addSkill(skill)
assert(skill.class:isSubclassOf(Skill))
if self.skills[skill.name] ~= nil then
error(string.format("Duplicate skill %s detected", skill.name))
fk.qWarning(string.format("Duplicate skill %s detected", skill.name))
end
self.skills[skill.name] = skill

Expand Down
3 changes: 3 additions & 0 deletions lua/server/ai/random_ai.lua
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ function RandomAI:useActiveSkill(skill, card)
local room = self.room
local player = self.player

if skill:isInstanceOf(ViewAsSkill) then return "" end

local filter_func = skill.cardFilter
if card then
filter_func = Util.FalseFunc
Expand Down Expand Up @@ -68,6 +70,7 @@ function RandomAI:useVSSkill(skill, pattern, cancelable, extra_data)
local player = self.player
local room = self.room
local precondition
if not skill then return nil end

if self.command == "PlayCard" then
precondition = skill:enabledAtPlay(player)
Expand Down
6 changes: 6 additions & 0 deletions lua/server/request.lua
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,12 @@ request_handlers["newroom"] = function(s, id)
s:registerRoom(id)
end

request_handlers["reloadpackage"] = function(room, id, reqlist)
if not IsConsoleStart() then return end
local path = reqlist[3]
Fk:reloadPackage(path)
end

-- 处理异步请求的协程,本身也是个死循环就是了。
-- 为了适应调度器,目前又暂且将请求分为“耗时请求”和不耗时请求。
-- 耗时请求处理后会立刻挂起。不耗时的请求则会不断处理直到请求队列空后再挂起。
Expand Down
4 changes: 4 additions & 0 deletions lua/server/scheduler.lua
Original file line number Diff line number Diff line change
Expand Up @@ -154,3 +154,7 @@ function InitScheduler(_thread)
requestRoom.thread = _thread
Pcall(mainLoop)
end

function IsConsoleStart()
return requestRoom.thread:isConsoleStart()
end
22 changes: 22 additions & 0 deletions src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -97,3 +97,25 @@ target_link_libraries(FreeKill PRIVATE
)

install(TARGETS FreeKill DESTINATION bin)
install(DIRECTORY
${PROJECT_SOURCE_DIR}/audio
${PROJECT_SOURCE_DIR}/fonts
${PROJECT_SOURCE_DIR}/image
${PROJECT_SOURCE_DIR}/lua
${PROJECT_SOURCE_DIR}/packages
${PROJECT_SOURCE_DIR}/Fk
${PROJECT_SOURCE_DIR}/server
DESTINATION share/FreeKill
)
install(FILES
${PROJECT_SOURCE_DIR}/fk_ver
DESTINATION share/FreeKill
)

if (NOT DEFINED FK_SERVER_ONLY)
install(FILES
${CMAKE_BINARY_DIR}/zh_CN.qm
${CMAKE_BINARY_DIR}/en_US.qm
DESTINATION share/FreeKill
)
endif()
51 changes: 50 additions & 1 deletion src/client/client.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,11 @@
#include "client.h"
#include "client_socket.h"
#include "clientplayer.h"
#include "qmlbackend.h"
#include "util.h"
#include "server.h"
#include <qforeach.h>
#include <qlogging.h>

Client *ClientInstance = nullptr;
ClientPlayer *Self = nullptr;
Expand Down Expand Up @@ -78,7 +82,10 @@ void Client::changeSelf(int id) {

lua_State *Client::getLuaState() { return L; }

void Client::installAESKey(const QByteArray &key) { router->installAESKey(key); }
void Client::installAESKey(const QByteArray &key) {
startWatchFiles();
router->installAESKey(key);
}

void Client::saveRecord(const QString &json, const QString &fname) {
if (!QDir("recording").exists()) {
Expand All @@ -90,6 +97,48 @@ void Client::saveRecord(const QString &json, const QString &fname) {
c.close();
}

bool Client::isConsoleStart() const {
if (!ClientInstance || !ServerInstance) {
return false;
}

return router->isConsoleStart();
}

void Client::startWatchFiles() {
if (!isConsoleStart()) return;
if (!fsWatcher.files().empty()) return;
QFile flist("flist.txt");
if (!flist.open(QIODevice::ReadOnly)) {
qCritical("Cannot open flist.txt. Won't watch files.");
fsWatcher.addPath("fk_ver"); // dummy
}
auto md5pairs = flist.readAll().split(';');
foreach (auto md5, md5pairs) {
if (md5.isEmpty()) continue;
auto fname = md5.split('=')[0];
if (fname.startsWith("packages") && fname.endsWith(".lua")) {
fsWatcher.addPath(fname);
}
}
connect(&fsWatcher, &QFileSystemWatcher::fileChanged, this,
&Client::updateLuaFiles);
}

void Client::processReplay(const QString &c, const QString &j) {
callLua(c, j);
}

void Client::updateLuaFiles(const QString &path) {
if (!isConsoleStart()) return;
Backend->showToast(tr("File %1 changed, reloading...").arg(path));
QThread::msleep(100);
Backend->callLuaFunction("ReloadPackage", { path });
ClientInstance->notifyServer("PushRequest",
QString("reloadpackage,%1").arg(path));

// according to QT documentation
if (!fsWatcher.files().contains(path) && QFile::exists(path)) {
fsWatcher.addPath(path);
}
}
Loading
Loading