* compile with wasm

* pack into qrc

* disable colorized msg for browser and cmd.exe

* run lua in wasm

* run the game

* font & pixmap anim

* special init page for web

* doc for compile wasm
This commit is contained in:
notify 2023-01-03 23:37:14 +08:00 committed by GitHub
parent f434e9ee9b
commit fb425e15fb
19 changed files with 282 additions and 48 deletions

View File

@ -2,8 +2,10 @@ cmake_minimum_required(VERSION 3.16)
project(FreeKill VERSION 0.0.1) project(FreeKill VERSION 0.0.1)
if (NOT ${CMAKE_SYSTEM_NAME} MATCHES "Emscripten")
include_directories(fkparse/src) include_directories(fkparse/src)
add_subdirectory(fkparse) add_subdirectory(fkparse)
endif ()
find_package(Qt6 REQUIRED COMPONENTS find_package(Qt6 REQUIRED COMPONENTS
Gui Gui
@ -31,15 +33,38 @@ include_directories(src/network)
include_directories(src/server) include_directories(src/server)
include_directories(src/ui) include_directories(src/ui)
if (${CMAKE_SYSTEM_NAME} MATCHES "Emscripten")
# Fix include problem
include_directories("/usr/include/openssl-1.1/")
endif()
file(GLOB SWIG_FILES "${PROJECT_SOURCE_DIR}/src/swig/*.i") file(GLOB SWIG_FILES "${PROJECT_SOURCE_DIR}/src/swig/*.i")
if (${CMAKE_SYSTEM_NAME} MATCHES "Emscripten")
set(SWIG_SOURCE ${PROJECT_SOURCE_DIR}/src/swig/freekill-wasm.i)
else ()
set(SWIG_SOURCE ${PROJECT_SOURCE_DIR}/src/swig/freekill.i)
endif ()
add_custom_command( add_custom_command(
OUTPUT ${PROJECT_SOURCE_DIR}/src/swig/freekill-wrap.cxx OUTPUT ${PROJECT_SOURCE_DIR}/src/swig/freekill-wrap.cxx
DEPENDS ${SWIG_FILES} DEPENDS ${SWIG_FILES}
COMMENT "Generating freekill-wrap.cxx" COMMENT "Generating freekill-wrap.cxx"
COMMAND swig -c++ -lua -Wall -o COMMAND swig -c++ -lua -Wall -o
${PROJECT_SOURCE_DIR}/src/swig/freekill-wrap.cxx ${PROJECT_SOURCE_DIR}/src/swig/freekill-wrap.cxx
${PROJECT_SOURCE_DIR}/src/swig/freekill.i ${SWIG_SOURCE}
) )
qt_add_executable(FreeKill) qt_add_executable(FreeKill)
if (${CMAKE_SYSTEM_NAME} MATCHES "Emscripten")
file(GLOB_RECURSE FK_RESOURCE_FILES
RELATIVE ${PROJECT_SOURCE_DIR}
*.lua *.qml *.js *.png *.jpg *.mp3
)
list(APPEND FK_RESOURCE_FILES "fonts/FZLBGBK.ttf")
qt_add_resources(FreeKill "qrc"
PREFIX "/"
FILES ${FK_RESOURCE_FILES}
)
endif()
add_subdirectory(src) add_subdirectory(src)

View File

@ -91,3 +91,30 @@ ___
## 编译安卓版 ## 编译安卓版
用Qt安装器装好Android库然后配置一下android-sdk就能编译了。 用Qt安装器装好Android库然后配置一下android-sdk就能编译了。
___
## WASM下编译
WASM大概就是能在浏览器中跑C++。编译用Qt Creator即可。
### 1. 条件与局限性
如果程序运行在网页上的话那么理应只有客户端然后提供网页的服务器上自然也运行着一个后端服务器。所以说在编译时应该舍弃掉服务端相关的代码。因此依赖库就不再需要sqlite3。
总之是编译个纯客户端的FK。
### 2. 编译OpenSSL
进入OpenSSL的src目录然后
$ ./config -no-asm -no-engine -no-dso
$ emmake make -j8 build_generated libssl.a libcrypto.a
编译Lua的话直接emmake make就行了总之库已经传到仓库了。
### 3. 部署资源文件
由于CMake中`file(GLOB_RECURSE)`所带来的缺陷,每当资源文件变动时,需要手动更新。
把构建目录中的.rcc目录删掉然后重新执行CMake->make即可。每次编译资源文件总要消耗相当多的时间。

BIN
lib/wasm/libcrypto.a Normal file

Binary file not shown.

BIN
lib/wasm/liblua.a Normal file

Binary file not shown.

View File

@ -1,5 +1,4 @@
import QtQuick import QtQuick
import Qt.labs.folderlistmodel
import "../skin-bank.js" as SkinBank import "../skin-bank.js" as SkinBank
Item { Item {
@ -18,12 +17,8 @@ Item {
width: childrenRect.width width: childrenRect.width
height: childrenRect.height height: childrenRect.height
FolderListModel { property string folder: SkinBank.PIXANIM_DIR + source
id: fileModel property int fileModel
folder: SkinBank.PIXANIM_DIR + source
nameFilters: ["*.png"]
showDirs: false
}
Repeater { Repeater {
id: frames id: frames
@ -35,7 +30,7 @@ Item {
onStatusChanged: { onStatusChanged: {
if (status == Image.Ready) { if (status == Image.Ready) {
loadedFrameCount++; loadedFrameCount++;
if (loadedFrameCount == fileModel.count) if (loadedFrameCount == fileModel)
root.loaded(); root.loaded();
} }
} }
@ -52,8 +47,8 @@ Item {
interval: 50 interval: 50
repeat: true repeat: true
onTriggered: { onTriggered: {
if (currentFrame >= fileModel.count) { if (currentFrame >= fileModel) {
frames.itemAt(fileModel.count - 1).visible = false; frames.itemAt(fileModel - 1).visible = false;
if (loop) { if (loop) {
currentFrame = 0; currentFrame = 0;
} else { } else {
@ -73,7 +68,7 @@ Item {
function start() function start()
{ {
if (loadedFrameCount == fileModel.count) { if (loadedFrameCount == fileModel) {
timer.start(); timer.start();
} else { } else {
root.loaded.connect(function(){ root.loaded.connect(function(){
@ -86,4 +81,8 @@ Item {
{ {
timer.stop(); timer.stop();
} }
Component.onCompleted: {
fileModel = Backend.ls(folder).length;
}
} }

View File

@ -170,7 +170,14 @@ function moveCards(moves) {
} }
function setEmotion(id, emotion) { function setEmotion(id, emotion) {
let path = (SkinBank.PIXANIM_DIR + emotion).replace("file://", ""); let path;
if (OS === "Win") {
// Windows: file:/C:/xxx/xxxx
path = (SkinBank.PIXANIM_DIR + emotion).replace("file:/", "");
} else {
path = (SkinBank.PIXANIM_DIR + emotion).replace("file://", "");
}
if (!Backend.exists(path)) { if (!Backend.exists(path)) {
return; return;
} }

61
qml/Pages/WebInit.qml Normal file
View File

@ -0,0 +1,61 @@
import QtQuick
import QtQuick.Controls
Item {
id: root
scale: 2
// Change this to your server's IP or domain name
property string server_addr: "127.0.0.1:9530"
Frame {
id: join_server
anchors.centerIn: parent
background: Rectangle {
color: "#88888888"
radius: 2
}
Column {
spacing: 8
TextField {
id: screenNameEdit
text: "player"
onTextChanged: {
passwordEdit.text = "";
let data = config.savedPassword[server_addr.editText];
if (data) {
if (text === data.username) {
passwordEdit.text = data.shorten_password;
}
}
}
}
TextField {
id: passwordEdit
text: ""
echoMode: TextInput.Password
passwordCharacter: "*"
}
Button {
text: "Login"
enabled: passwordEdit.text !== ""
onClicked: {
config.serverAddr = server_addr;
config.screenName = screenNameEdit.text;
config.password = passwordEdit.text;
mainWindow.busy = true;
Backend.joinServer(server_addr);
}
}
}
}
Component.onCompleted: {
config.loadConf();
let data = config.savedPassword[config.lastLoginServer];
screenNameEdit.text = data.username;
passwordEdit.text = data.shorten_password;
}
}

View File

@ -37,11 +37,12 @@ Item {
StackView { StackView {
id: mainStack id: mainStack
visible: !mainWindow.busy visible: !mainWindow.busy
initialItem: init initialItem: OS !== "Web" ? init : webinit
anchors.fill: parent anchors.fill: parent
} }
Component { id: init; Init {} } Component { id: init; Init {} }
Component { id: webinit; WebInit {} }
Component { id: lobby; Lobby {} } Component { id: lobby; Lobby {} }
Component { id: generalsOverview; GeneralsOverview {} } Component { id: generalsOverview; GeneralsOverview {} }
Component { id: cardsOverview; CardsOverview {} } Component { id: cardsOverview; CardsOverview {} }
@ -152,7 +153,7 @@ Item {
} }
Component.onCompleted: { Component.onCompleted: {
if (!Android) { if (OS !== "Android" && OS !== "Web") {
width = config.winWidth; width = config.winWidth;
height = config.winHeight; height = config.winHeight;
} }

View File

@ -28,6 +28,7 @@ set(freekill_HEADERS
"ui/qmlbackend.h" "ui/qmlbackend.h"
) )
set(FKP_LIB fkparse)
if (WIN32) if (WIN32)
set(LUA_LIB ${PROJECT_SOURCE_DIR}/lib/win/lua54.dll) set(LUA_LIB ${PROJECT_SOURCE_DIR}/lib/win/lua54.dll)
set(SQLITE3_LIB ${PROJECT_SOURCE_DIR}/lib/win/sqlite3.dll) set(SQLITE3_LIB ${PROJECT_SOURCE_DIR}/lib/win/sqlite3.dll)
@ -40,6 +41,19 @@ elseif (ANDROID)
QT_ANDROID_PACKAGE_SOURCE_DIR ${PROJECT_SOURCE_DIR}/android QT_ANDROID_PACKAGE_SOURCE_DIR ${PROJECT_SOURCE_DIR}/android
QT_ANDROID_EXTRA_LIBS "${LUA_LIB};${SQLITE3_LIB};${CRYPTO_LIB}" QT_ANDROID_EXTRA_LIBS "${LUA_LIB};${SQLITE3_LIB};${CRYPTO_LIB}"
) )
elseif (${CMAKE_SYSTEM_NAME} MATCHES "Emscripten")
# WASM
list(REMOVE_ITEM freekill_SRCS
"network/server_socket.cpp"
#"network/client_socket.cpp"
#"network/router.cpp"
"server/server.cpp"
"server/serverplayer.cpp"
"server/room.cpp"
)
set(FKP_LIB "")
set(LUA_LIB ${PROJECT_SOURCE_DIR}/lib/wasm/liblua.a)
set(CRYPTO_LIB ${PROJECT_SOURCE_DIR}/lib/wasm/libcrypto.a)
else () else ()
set(LUA_LIB lua5.4) set(LUA_LIB lua5.4)
set(SQLITE3_LIB sqlite3) set(SQLITE3_LIB sqlite3)
@ -57,7 +71,7 @@ target_link_libraries(FreeKill PRIVATE
${SQLITE3_LIB} ${SQLITE3_LIB}
${CRYPTO_LIB} ${CRYPTO_LIB}
${READLINE_LIB} ${READLINE_LIB}
fkparse ${FKP_LIB}
Qt6::Qml Qt6::Qml
Qt6::Gui Qt6::Gui
Qt6::Widgets Qt6::Widgets

View File

@ -61,6 +61,7 @@ void Dumpstack(lua_State *L)
} }
} }
#ifndef Q_OS_WASM
sqlite3 *OpenDatabase(const QString &filename) sqlite3 *OpenDatabase(const QString &filename)
{ {
sqlite3 *ret; sqlite3 *ret;
@ -151,6 +152,7 @@ RSA *InitServerRSA() {
fclose(keyFile); fclose(keyFile);
return rsa; return rsa;
} }
#endif
static void writeFileMD5(QFile &dest, const QString &fname) { static void writeFileMD5(QFile &dest, const QString &fname) {
QFile f(fname); QFile f(fname);

View File

@ -6,6 +6,7 @@
lua_State *CreateLuaState(); lua_State *CreateLuaState();
bool DoLuaScript(lua_State *L, const char *script); bool DoLuaScript(lua_State *L, const char *script);
#ifndef Q_OS_WASM
sqlite3 *OpenDatabase(const QString &filename = "./server/users.db"); sqlite3 *OpenDatabase(const QString &filename = "./server/users.db");
QJsonObject SelectFromDatabase(sqlite3 *db, const QString &sql); QJsonObject SelectFromDatabase(sqlite3 *db, const QString &sql);
// For Lua // For Lua
@ -14,6 +15,7 @@ void ExecSQL(sqlite3 *db, const QString &sql);
void CloseDatabase(sqlite3 *db); void CloseDatabase(sqlite3 *db);
RSA *InitServerRSA(); RSA *InitServerRSA();
#endif
QString calcFileMD5(); QString calcFileMD5();

View File

@ -1,5 +1,7 @@
#include "qmlbackend.h" #include "qmlbackend.h"
#ifndef Q_OS_WASM
#include "server.h" #include "server.h"
#endif
#if defined(Q_OS_LINUX) && !defined(Q_OS_ANDROID) #if defined(Q_OS_LINUX) && !defined(Q_OS_ANDROID)
#include "shell.h" #include "shell.h"
@ -11,8 +13,9 @@
#include <QSplashScreen> #include <QSplashScreen>
#include <QScreen> #include <QScreen>
#include <QFileDialog>
#ifdef Q_OS_ANDROID #if defined(Q_OS_ANDROID) || defined(Q_OS_WASM)
static bool copyPath(const QString &srcFilePath, const QString &tgtFilePath) static bool copyPath(const QString &srcFilePath, const QString &tgtFilePath)
{ {
QFileInfo srcFileInfo(srcFilePath); QFileInfo srcFileInfo(srcFilePath);
@ -48,19 +51,19 @@ void fkMsgHandler(QtMsgType type, const QMessageLogContext &context, const QStri
auto threadName = QThread::currentThread()->objectName().toLatin1().constData(); auto threadName = QThread::currentThread()->objectName().toLatin1().constData();
switch (type) { switch (type) {
case QtDebugMsg: case QtDebugMsg:
fprintf(stderr, "[%s/\e[1;30mDEBUG\e[0m] %s\n", threadName, localMsg.constData()); fprintf(stderr, "[%s/DEBUG] %s\n", threadName, localMsg.constData());
break; break;
case QtInfoMsg: case QtInfoMsg:
fprintf(stderr, "[%s/\e[1;32mINFO\e[0m] %s\n", threadName, localMsg.constData()); fprintf(stderr, "[%s/INFO] %s\n", threadName, localMsg.constData());
break; break;
case QtWarningMsg: case QtWarningMsg:
fprintf(stderr, "[%s/\e[1;33mWARNING\e[0m] %s\n", threadName, localMsg.constData()); fprintf(stderr, "[%s/WARNING] %s\n", threadName, localMsg.constData());
break; break;
case QtCriticalMsg: case QtCriticalMsg:
fprintf(stderr, "[%s/\e[1;31mCRITICAL\e[0m] %s\n", threadName, localMsg.constData()); fprintf(stderr, "[%s/CRITICAL] %s\n", threadName, localMsg.constData());
break; break;
case QtFatalMsg: case QtFatalMsg:
fprintf(stderr, "[%s/\e[1;31mFATAL\e[0m] %s\n", threadName, localMsg.constData()); fprintf(stderr, "[%s/FATAL] %s\n", threadName, localMsg.constData());
break; break;
} }
} }
@ -73,6 +76,7 @@ int main(int argc, char *argv[])
QCoreApplication::setApplicationName("FreeKill"); QCoreApplication::setApplicationName("FreeKill");
QCoreApplication::setApplicationVersion("Alpha 0.0.1"); QCoreApplication::setApplicationVersion("Alpha 0.0.1");
#ifndef Q_OS_WASM
QCommandLineParser parser; QCommandLineParser parser;
parser.setApplicationDescription("FreeKill server"); parser.setApplicationDescription("FreeKill server");
parser.addHelpOption(); parser.addHelpOption();
@ -105,6 +109,9 @@ int main(int argc, char *argv[])
} }
return app->exec(); return app->exec();
} }
#else
copyPath(":/", QDir::currentPath());
#endif
app = new QApplication(argc, argv); app = new QApplication(argc, argv);
@ -132,19 +139,32 @@ int main(int argc, char *argv[])
backend.setEngine(engine); backend.setEngine(engine);
engine->rootContext()->setContextProperty("Backend", &backend); engine->rootContext()->setContextProperty("Backend", &backend);
engine->rootContext()->setContextProperty("AppPath", QUrl::fromLocalFile(QDir::currentPath()));
#ifdef QT_DEBUG #ifdef QT_DEBUG
bool debugging = true; bool debugging = true;
#else #else
bool debugging = false; bool debugging = false;
#endif #endif
engine->rootContext()->setContextProperty("Debugging", debugging); engine->rootContext()->setContextProperty("Debugging", debugging);
#ifdef Q_OS_ANDROID
engine->rootContext()->setContextProperty("Android", true);
QString system;
#if defined(Q_OS_ANDROID)
system = "Android";
#elif defined(Q_OS_WASM)
system = "Web";
#elif defined(Q_OS_WIN32)
system = "Win";
#elif defined(Q_OS_LINUX)
system = "Linux";
#else #else
engine->rootContext()->setContextProperty("Android", false); system = "Other";
#endif #endif
engine->rootContext()->setContextProperty("OS", system);
engine->rootContext()->setContextProperty("AppPath", QUrl::fromLocalFile(QDir::currentPath()));
engine->load("qml/main.qml"); engine->load("qml/main.qml");
if (engine->rootObjects().isEmpty()) if (engine->rootObjects().isEmpty())
return -1; return -1;

View File

@ -1,8 +1,10 @@
#include "router.h" #include "router.h"
#include "client.h" #include "client.h"
#include "client_socket.h" #include "client_socket.h"
#ifndef Q_OS_WASM
#include "server.h" #include "server.h"
#include "serverplayer.h" #include "serverplayer.h"
#endif
#include "util.h" #include "util.h"
Router::Router(QObject *parent, ClientSocket *socket, RouterType type) Router::Router(QObject *parent, ClientSocket *socket, RouterType type)
@ -13,7 +15,9 @@ Router::Router(QObject *parent, ClientSocket *socket, RouterType type)
setSocket(socket); setSocket(socket);
expectedReplyId = -1; expectedReplyId = -1;
replyTimeout = 0; replyTimeout = 0;
#ifndef Q_OS_WASM
extraReplyReadySemaphore = nullptr; extraReplyReadySemaphore = nullptr;
#endif
} }
Router::~Router() Router::~Router()
@ -44,14 +48,17 @@ void Router::setSocket(ClientSocket *socket)
} }
} }
#ifndef Q_OS_WASM
void Router::setReplyReadySemaphore(QSemaphore *semaphore) void Router::setReplyReadySemaphore(QSemaphore *semaphore)
{ {
extraReplyReadySemaphore = semaphore; extraReplyReadySemaphore = semaphore;
} }
#endif
void Router::request(int type, const QString& command, void Router::request(int type, const QString& command,
const QString& jsonData, int timeout) const QString& jsonData, int timeout)
{ {
#ifndef Q_OS_WASM
// In case a request is called without a following waitForReply call // In case a request is called without a following waitForReply call
if (replyReadySemaphore.available() > 0) if (replyReadySemaphore.available() > 0)
replyReadySemaphore.acquire(replyReadySemaphore.available()); replyReadySemaphore.acquire(replyReadySemaphore.available());
@ -74,6 +81,7 @@ void Router::request(int type, const QString& command,
body << timeout; body << timeout;
emit messageReady(QJsonDocument(body).toJson(QJsonDocument::Compact)); emit messageReady(QJsonDocument(body).toJson(QJsonDocument::Compact));
#endif
} }
void Router::reply(int type, const QString& command, const QString& jsonData) void Router::reply(int type, const QString& command, const QString& jsonData)
@ -106,6 +114,7 @@ int Router::getTimeout() const
// cancel last request from the sender // cancel last request from the sender
void Router::cancelRequest() void Router::cancelRequest()
{ {
#ifndef Q_OS_WASM
replyMutex.lock(); replyMutex.lock();
expectedReplyId = -1; expectedReplyId = -1;
replyTimeout = 0; replyTimeout = 0;
@ -114,22 +123,28 @@ void Router::cancelRequest()
if (replyReadySemaphore.available() > 0) if (replyReadySemaphore.available() > 0)
replyReadySemaphore.acquire(replyReadySemaphore.available()); replyReadySemaphore.acquire(replyReadySemaphore.available());
#endif
} }
QString Router::waitForReply() QString Router::waitForReply()
{ {
#ifndef Q_OS_WASM
replyReadySemaphore.acquire(); replyReadySemaphore.acquire();
return m_reply; return m_reply;
#endif
} }
QString Router::waitForReply(int timeout) QString Router::waitForReply(int timeout)
{ {
#ifndef Q_OS_WASM
replyReadySemaphore.tryAcquire(1, timeout * 1000); replyReadySemaphore.tryAcquire(1, timeout * 1000);
return m_reply; return m_reply;
#endif
} }
void Router::abortRequest() void Router::abortRequest()
{ {
#ifndef Q_OS_WASM
replyMutex.lock(); replyMutex.lock();
if (expectedReplyId != -1) { if (expectedReplyId != -1) {
replyReadySemaphore.release(); replyReadySemaphore.release();
@ -139,10 +154,12 @@ void Router::abortRequest()
extraReplyReadySemaphore = nullptr; extraReplyReadySemaphore = nullptr;
} }
replyMutex.unlock(); replyMutex.unlock();
#endif
} }
void Router::handlePacket(const QByteArray& rawPacket) void Router::handlePacket(const QByteArray& rawPacket)
{ {
#ifndef Q_OS_WASM
static QMap<QString, void (*)(ServerPlayer *, const QString &)> lobby_actions; static QMap<QString, void (*)(ServerPlayer *, const QString &)> lobby_actions;
if (lobby_actions.size() <= 0) { if (lobby_actions.size() <= 0) {
lobby_actions["UpdateAvatar"] = [](ServerPlayer *sender, const QString &jsonData){ lobby_actions["UpdateAvatar"] = [](ServerPlayer *sender, const QString &jsonData){
@ -260,5 +277,28 @@ void Router::handlePacket(const QByteArray& rawPacket)
locker.unlock(); locker.unlock();
emit replyReady(); emit replyReady();
} }
#else
QJsonDocument packet = QJsonDocument::fromJson(rawPacket);
if (packet.isNull() || !packet.isArray())
return;
int requestId = packet[0].toInt();
int type = packet[1].toInt();
QString command = packet[2].toString();
QString jsonData = packet[3].toString();
if (type & TYPE_NOTIFICATION) {
if (type & DEST_CLIENT) {
ClientInstance->callLua(command, jsonData);
}
} else if (type & TYPE_REQUEST) {
this->requestId = requestId;
this->requestTimeout = packet[4].toInt();
if (type & DEST_CLIENT) {
qobject_cast<Client *>(parent())->callLua(command, jsonData);
}
}
#endif
} }

View File

@ -29,7 +29,9 @@ public:
ClientSocket *getSocket() const; ClientSocket *getSocket() const;
void setSocket(ClientSocket *socket); void setSocket(ClientSocket *socket);
#ifndef Q_OS_WASM
void setReplyReadySemaphore(QSemaphore *semaphore); void setReplyReadySemaphore(QSemaphore *semaphore);
#endif
void request(int type, const QString &command, void request(int type, const QString &command,
const QString &jsonData, int timeout); const QString &jsonData, int timeout);
@ -66,8 +68,10 @@ private:
int expectedReplyId; int expectedReplyId;
int replyTimeout; int replyTimeout;
QString m_reply; // should be json string QString m_reply; // should be json string
#ifndef Q_OS_WASM
QSemaphore replyReadySemaphore; QSemaphore replyReadySemaphore;
QSemaphore *extraReplyReadySemaphore; QSemaphore *extraReplyReadySemaphore;
#endif
// Two Lua global table for callbacks and interactions // Two Lua global table for callbacks and interactions
// stored in the lua_State of the sender // stored in the lua_State of the sender

15
src/swig/freekill-wasm.i Normal file
View File

@ -0,0 +1,15 @@
%module fk
%{
#include "client.h"
#include "serverplayer.h"
#include "clientplayer.h"
#include "room.h"
#include "qmlbackend.h"
#include "util.h"
%}
%include "naturalvar.i"
%include "qt.i"
%include "player.i"
%include "client.i"

View File

@ -34,22 +34,3 @@ public:
}; };
extern ClientPlayer *Self; extern ClientPlayer *Self;
%nodefaultctor ServerPlayer;
%nodefaultdtor ServerPlayer;
class ServerPlayer : public Player {
public:
Server *getServer() const;
Room *getRoom() const;
void setRoom(Room *room);
void speak(const QString &message);
void doRequest(const QString &command,
const QString &json_data, int timeout);
QString waitForReply();
QString waitForReply(int timeout);
void doNotify(const QString &command, const QString &json_data);
void prepareForRequest(const QString &command, const QString &data);
};

View File

@ -93,3 +93,21 @@ void Room::roomStart() {
%} %}
%nodefaultctor ServerPlayer;
%nodefaultdtor ServerPlayer;
class ServerPlayer : public Player {
public:
Server *getServer() const;
Room *getRoom() const;
void setRoom(Room *room);
void speak(const QString &message);
void doRequest(const QString &command,
const QString &json_data, int timeout);
QString waitForReply();
QString waitForReply(int timeout);
void doNotify(const QString &command, const QString &json_data);
void prepareForRequest(const QString &command, const QString &data);
};

View File

@ -1,5 +1,7 @@
#include "qmlbackend.h" #include "qmlbackend.h"
#ifndef Q_OS_WASM
#include "server.h" #include "server.h"
#endif
#include "client.h" #include "client.h"
#include "util.h" #include "util.h"
@ -11,14 +13,18 @@ QmlBackend::QmlBackend(QObject* parent)
Backend = this; Backend = this;
engine = nullptr; engine = nullptr;
rsa = RSA_new(); rsa = RSA_new();
#ifndef Q_OS_WASM
parser = fkp_new_parser(); parser = fkp_new_parser();
#endif
} }
QmlBackend::~QmlBackend() QmlBackend::~QmlBackend()
{ {
Backend = nullptr; Backend = nullptr;
RSA_free(rsa); RSA_free(rsa);
#ifndef Q_OS_WASM
fkp_close(parser); fkp_close(parser);
#endif
} }
QQmlApplicationEngine *QmlBackend::getEngine() const QQmlApplicationEngine *QmlBackend::getEngine() const
@ -33,6 +39,7 @@ void QmlBackend::setEngine(QQmlApplicationEngine *engine)
void QmlBackend::startServer(ushort port) void QmlBackend::startServer(ushort port)
{ {
#ifndef Q_OS_WASM
if (!ServerInstance) { if (!ServerInstance) {
Server *server = new Server(this); Server *server = new Server(this);
@ -41,6 +48,7 @@ void QmlBackend::startServer(ushort port)
emit notifyUI("ErrorMsg", tr("Cannot start server!")); emit notifyUI("ErrorMsg", tr("Cannot start server!"));
} }
} }
#endif
} }
void QmlBackend::joinServer(QString address) void QmlBackend::joinServer(QString address)
@ -81,7 +89,7 @@ void QmlBackend::cd(const QString &path) {
} }
QStringList QmlBackend::ls(const QString &dir) { QStringList QmlBackend::ls(const QString &dir) {
return QDir(dir).entryList(QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot); return QDir(QUrl(dir).path()).entryList(QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot);
} }
QString QmlBackend::pwd() { QString QmlBackend::pwd() {
@ -169,12 +177,14 @@ QString QmlBackend::callLuaFunction(const QString &func_name,
} }
QString QmlBackend::pubEncrypt(const QString &key, const QString &data) { QString QmlBackend::pubEncrypt(const QString &key, const QString &data) {
BIO *keyio = BIO_new_mem_buf(key.toLatin1().data(), -1); auto key_bytes = key.toLatin1();
BIO *keyio = BIO_new_mem_buf(key_bytes.constData(), -1);
PEM_read_bio_RSAPublicKey(keyio, &rsa, NULL, NULL); PEM_read_bio_RSAPublicKey(keyio, &rsa, NULL, NULL);
BIO_free_all(keyio); BIO_free_all(keyio);
auto data_bytes = data.toUtf8();
unsigned char buf[RSA_size(rsa)]; unsigned char buf[RSA_size(rsa)];
RSA_public_encrypt(data.length(), (const unsigned char *)data.toUtf8().data(), RSA_public_encrypt(data.length(), (const unsigned char *)data_bytes.constData(),
buf, rsa, RSA_PKCS1_PADDING); buf, rsa, RSA_PKCS1_PADDING);
return QByteArray::fromRawData((const char *)buf, RSA_size(rsa)).toBase64(); return QByteArray::fromRawData((const char *)buf, RSA_size(rsa)).toBase64();
} }
@ -209,6 +219,7 @@ void QmlBackend::saveConf(const QString &conf) {
} }
void QmlBackend::parseFkp(const QString &fileName) { void QmlBackend::parseFkp(const QString &fileName) {
#ifndef Q_OS_WASM
if (!QFile::exists(fileName)) { if (!QFile::exists(fileName)) {
// errorEdit->setText(tr("File does not exist!")); // errorEdit->setText(tr("File does not exist!"));
return; return;
@ -245,8 +256,10 @@ void QmlBackend::parseFkp(const QString &fileName) {
} }
*/ */
QDir::setCurrent(cwd); QDir::setCurrent(cwd);
#endif
} }
#ifndef Q_OS_WASM
static void copyFkpHash2QHash(QHash<QString, QString> &dst, fkp_hash *from) { static void copyFkpHash2QHash(QHash<QString, QString> &dst, fkp_hash *from) {
dst.clear(); dst.clear();
for (size_t i = 0; i < from->capacity; i++) { for (size_t i = 0; i < from->capacity; i++) {
@ -261,6 +274,7 @@ void QmlBackend::readHashFromParser() {
copyFkpHash2QHash(skills, parser->skills); copyFkpHash2QHash(skills, parser->skills);
copyFkpHash2QHash(marks, parser->marks); copyFkpHash2QHash(marks, parser->marks);
} }
#endif
QString QmlBackend::calcFileMD5() { QString QmlBackend::calcFileMD5() {
return ::calcFileMD5(); return ::calcFileMD5();

View File

@ -1,7 +1,9 @@
#ifndef _QMLBACKEND_H #ifndef _QMLBACKEND_H
#define _QMLBACKEND_H #define _QMLBACKEND_H
#ifndef Q_OS_WASM
#include "fkparse.h" #include "fkparse.h"
#endif
#include <qtmetamacros.h> #include <qtmetamacros.h>
class QmlBackend : public QObject { class QmlBackend : public QObject {
@ -48,7 +50,9 @@ signals:
private: private:
QQmlApplicationEngine *engine; QQmlApplicationEngine *engine;
RSA *rsa; RSA *rsa;
#ifndef Q_OS_WASM
fkp_parser *parser; fkp_parser *parser;
#endif
QHash<QString, QString> generals; QHash<QString, QString> generals;
QHash<QString, QString> skills; QHash<QString, QString> skills;
QHash<QString, QString> marks; QHash<QString, QString> marks;