mirror of
https://github.com/Qsgs-Fans/FreeKill.git
synced 2024-11-15 19:22:25 +08:00
单机热更新,以及Docker (#336)
以及emo公主的判定小卡换图 --------- Co-authored-by: seven <786852516@qq.com>
This commit is contained in:
parent
e840a3b322
commit
9dd4f55c86
|
@ -3,6 +3,7 @@
|
|||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import Fk.Common
|
||||
import Fk.Pages
|
||||
import Fk.RoomElement
|
||||
|
||||
|
@ -22,7 +23,16 @@ Flickable {
|
|||
width: parent.width - 40
|
||||
x: 20
|
||||
|
||||
// TODO: player details
|
||||
RowLayout {
|
||||
spacing: 8
|
||||
Avatar {
|
||||
id: avatar
|
||||
Layout.preferredWidth: 56
|
||||
Layout.preferredHeight: 56
|
||||
general: "diaochan"
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
Text {
|
||||
id: screenName
|
||||
font.pixelSize: 18
|
||||
|
@ -35,6 +45,8 @@ Flickable {
|
|||
font.pixelSize: 18
|
||||
color: "#E4D5A0"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
MetroButton {
|
||||
|
@ -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;
|
||||
|
@ -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) + ")";
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
# 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
|
||||
|
||||
#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 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
docker/docker-entrypoint.sh
Normal file
29
docker/docker-entrypoint.sh
Normal file
|
@ -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 not shown.
Before Width: | Height: | Size: 2.9 KiB After Width: | Height: | Size: 3.6 KiB |
|
@ -784,4 +784,8 @@ function GetMiniGame(gtype, p, data)
|
|||
}
|
||||
end
|
||||
|
||||
function ReloadPackage(path)
|
||||
Fk:reloadPackage(path)
|
||||
end
|
||||
|
||||
dofile "lua/client/i18n/init.lua"
|
||||
|
|
|
@ -47,7 +47,6 @@ function Engine:initialize()
|
|||
end
|
||||
|
||||
Fk = self
|
||||
|
||||
self.extensions = {
|
||||
["standard"] = { "standard" },
|
||||
["standard_cards"] = { "standard_cards" },
|
||||
|
@ -123,6 +122,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的文件夹,并把它们作为拓展包加载进来。
|
||||
|
@ -215,7 +284,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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
||||
-- 处理异步请求的协程,本身也是个死循环就是了。
|
||||
-- 为了适应调度器,目前又暂且将请求分为“耗时请求”和不耗时请求。
|
||||
-- 耗时请求处理后会立刻挂起。不耗时的请求则会不断处理直到请求队列空后再挂起。
|
||||
|
|
|
@ -154,3 +154,7 @@ function InitScheduler(_thread)
|
|||
requestRoom.thread = _thread
|
||||
Pcall(mainLoop)
|
||||
end
|
||||
|
||||
function IsConsoleStart()
|
||||
return requestRoom.thread:isConsoleStart()
|
||||
end
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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;
|
||||
|
@ -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()) {
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
|
||||
#include "router.h"
|
||||
#include "clientplayer.h"
|
||||
#include <qfilesystemwatcher.h>
|
||||
|
||||
#ifndef FK_SERVER_ONLY
|
||||
#include "qmlbackend.h"
|
||||
|
@ -34,18 +35,24 @@ public:
|
|||
|
||||
void saveRecord(const QString &json, const QString &fname);
|
||||
|
||||
bool isConsoleStart() const;
|
||||
void startWatchFiles();
|
||||
signals:
|
||||
void error_message(const QString &msg);
|
||||
|
||||
public slots:
|
||||
void processReplay(const QString &, const QString &);
|
||||
|
||||
private slots:
|
||||
void updateLuaFiles(const QString &path);
|
||||
|
||||
private:
|
||||
Router *router;
|
||||
QMap<int, ClientPlayer *> players;
|
||||
ClientPlayer *self;
|
||||
|
||||
lua_State *L;
|
||||
QFileSystemWatcher fsWatcher;
|
||||
};
|
||||
|
||||
extern Client *ClientInstance;
|
||||
|
|
19
src/main.cpp
19
src/main.cpp
|
@ -173,6 +173,16 @@ void fkMsgHandler(QtMsgType type, const QMessageLogContext &context,
|
|||
int main(int argc, char *argv[]) {
|
||||
// 初始化一下各种杂项信息
|
||||
QThread::currentThread()->setObjectName("Main");
|
||||
|
||||
qInstallMessageHandler(fkMsgHandler);
|
||||
QCoreApplication *app;
|
||||
QCoreApplication::setApplicationName("FreeKill");
|
||||
QCoreApplication::setApplicationVersion(FK_VERSION);
|
||||
|
||||
#if defined(Q_OS_LINUX) && !defined(Q_OS_ANDROID)
|
||||
prepareForLinux();
|
||||
#endif
|
||||
|
||||
if (!info_log) {
|
||||
info_log = fopen("freekill.server.info.log", "w+");
|
||||
if (!info_log) {
|
||||
|
@ -186,15 +196,6 @@ int main(int argc, char *argv[]) {
|
|||
}
|
||||
}
|
||||
|
||||
qInstallMessageHandler(fkMsgHandler);
|
||||
QCoreApplication *app;
|
||||
QCoreApplication::setApplicationName("FreeKill");
|
||||
QCoreApplication::setApplicationVersion(FK_VERSION);
|
||||
|
||||
#if defined(Q_OS_LINUX) && !defined(Q_OS_ANDROID)
|
||||
prepareForLinux();
|
||||
#endif
|
||||
|
||||
#ifndef FK_CLIENT_ONLY
|
||||
// 分析命令行,如果有 -s 或者 --server 就在命令行直接开服务器
|
||||
QCommandLineParser parser;
|
||||
|
|
|
@ -53,6 +53,10 @@ void Router::installAESKey(const QByteArray &key) {
|
|||
socket->installAESKey(key);
|
||||
}
|
||||
|
||||
bool Router::isConsoleStart() const {
|
||||
return socket->peerAddress() == "127.0.0.1";
|
||||
}
|
||||
|
||||
#ifndef FK_CLIENT_ONLY
|
||||
void Router::setReplyReadySemaphore(QSemaphore *semaphore) {
|
||||
extraReplyReadySemaphore = semaphore;
|
||||
|
|
|
@ -32,6 +32,7 @@ public:
|
|||
void setSocket(ClientSocket *socket);
|
||||
void removeSocket();
|
||||
void installAESKey(const QByteArray &key);
|
||||
bool isConsoleStart() const;
|
||||
|
||||
#ifndef FK_CLIENT_ONLY
|
||||
void setReplyReadySemaphore(QSemaphore *semaphore);
|
||||
|
|
|
@ -4,6 +4,10 @@
|
|||
#include "server.h"
|
||||
#include "util.h"
|
||||
|
||||
#ifndef FK_SERVER_ONLY
|
||||
#include "client.h"
|
||||
#endif
|
||||
|
||||
RoomThread::RoomThread(Server *m_server) {
|
||||
setObjectName("Room");
|
||||
this->m_server = m_server;
|
||||
|
@ -105,3 +109,12 @@ void RoomThread::tryTerminate() {
|
|||
bool RoomThread::isTerminated() const {
|
||||
return terminated;
|
||||
}
|
||||
|
||||
bool RoomThread::isConsoleStart() const {
|
||||
#ifndef FK_SERVER_ONLY
|
||||
if (!ClientInstance) return false;
|
||||
return ClientInstance->isConsoleStart();
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
|
|
@ -31,6 +31,7 @@ class RoomThread : public QThread {
|
|||
void tryTerminate();
|
||||
bool isTerminated() const;
|
||||
|
||||
bool isConsoleStart() const;
|
||||
protected:
|
||||
virtual void run();
|
||||
|
||||
|
|
|
@ -39,6 +39,8 @@ public:
|
|||
|
||||
void trySleep(int ms);
|
||||
bool isTerminated() const;
|
||||
|
||||
bool isConsoleStart() const;
|
||||
};
|
||||
|
||||
%{
|
||||
|
|
Loading…
Reference in New Issue
Block a user