- 对旁观和重连进行优化,减轻服务器CPU负担
- 加强Lua与C++交互能力,现在可以直接传const QVariant &参数
- 借助上一条,删除了客户端侧代码绝大多数冗余的json.encode/JSON.parse
- 微调swig代码减少生成量,将int映射到lua integer而不是number
This commit is contained in:
notify 2024-04-19 20:53:19 +08:00 committed by GitHub
parent 2be00fb3b0
commit 9d9217da2c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
32 changed files with 753 additions and 771 deletions

View File

@ -31,7 +31,7 @@ find_package(Lua)
find_package(SQLite3) find_package(SQLite3)
set(CMAKE_AUTOMOC ON) set(CMAKE_AUTOMOC ON)
set(CMAKE_CXX_STANDARD 11) set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED True) set(CMAKE_CXX_STANDARD_REQUIRED True)
set(REQUIRED_QT_VERSION "6.4") set(REQUIRED_QT_VERSION "6.4")
@ -45,15 +45,8 @@ 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") if (DEFINED FK_SERVER_ONLY)
set(SWIG_SOURCE ${PROJECT_SOURCE_DIR}/src/swig/freekill-wasm.i)
elseif (DEFINED FK_SERVER_ONLY)
set(SWIG_SOURCE ${PROJECT_SOURCE_DIR}/src/swig/freekill-nogui.i) set(SWIG_SOURCE ${PROJECT_SOURCE_DIR}/src/swig/freekill-nogui.i)
else () else ()
set(SWIG_SOURCE ${PROJECT_SOURCE_DIR}/src/swig/freekill.i) set(SWIG_SOURCE ${PROJECT_SOURCE_DIR}/src/swig/freekill.i)
@ -87,54 +80,4 @@ add_custom_command(
COMMAND echo ${CMAKE_PROJECT_VERSION} > ${PROJECT_SOURCE_DIR}/fk_ver COMMAND echo ${CMAKE_PROJECT_VERSION} > ${PROJECT_SOURCE_DIR}/fk_ver
) )
if (${CMAKE_SYSTEM_NAME} MATCHES "Emscripten")
set(CMAKE_MODULE_LINKER_FLAGS ${CMAKE_MODULE_LINKER_FLAGS}
"-s INITIAL_MEMORY=64MB"
)
file(GLOB_RECURSE FK_SCRIPT_FILES
RELATIVE ${PROJECT_SOURCE_DIR}
*.lua *.qml *.js *.fkp *.sql zh_CN.qm
)
qt_add_resources(FreeKill "scripts_qrc"
PREFIX "/"
FILES ${FK_SCRIPT_FILES}
)
qt_add_resources(FreeKill "font_qrc"
PREFIX "/"
FILES "fonts/FZLBGBK.ttf"
)
file(GLOB_RECURSE FK_IMG_FILES
RELATIVE ${PROJECT_SOURCE_DIR}
${PROJECT_SOURCE_DIR}/image/*.jpg
${PROJECT_SOURCE_DIR}/image/*.png
)
qt_add_resources(FreeKill "img_qrc"
PREFIX "/"
FILES ${FK_IMG_FILES}
)
file(GLOB_RECURSE FK_AUDIO_FILES
RELATIVE ${PROJECT_SOURCE_DIR}
${PROJECT_SOURCE_DIR}/audio/*.mp3
)
qt_add_resources(FreeKill "audio_qrc"
PREFIX "/"
FILES ${FK_AUDIO_FILES}
)
file(GLOB_RECURSE FK_PKG_FILES
RELATIVE ${PROJECT_SOURCE_DIR}
${PROJECT_SOURCE_DIR}/packages/*.mp3
${PROJECT_SOURCE_DIR}/packages/*.jpg
${PROJECT_SOURCE_DIR}/packages/*.png
)
qt_add_resources(FreeKill "pkg_qrc"
PREFIX "/"
FILES ${FK_PKG_FILES}
)
endif()
add_subdirectory(src) add_subdirectory(src)

View File

@ -98,8 +98,7 @@ callbacks["BackToStart"] = (jsonData) => {
} }
} }
callbacks["SetServerSettings"] = (j) => { callbacks["SetServerSettings"] = (data) => {
const data = JSON.parse(j);
const [ motd, hiddenPacks, enableBots ] = data; const [ motd, hiddenPacks, enableBots ] = data;
config.serverMotd = motd; config.serverMotd = motd;
config.serverHiddenPacks = hiddenPacks; config.serverHiddenPacks = hiddenPacks;
@ -127,9 +126,8 @@ callbacks["EnterLobby"] = (jsonData) => {
config.saveConf(); config.saveConf();
} }
callbacks["EnterRoom"] = (jsonData) => { callbacks["EnterRoom"] = (data) => {
// jsonData: int capacity, int timeout // jsonData: int capacity, int timeout
const data = JSON.parse(jsonData);
config.roomCapacity = data[0]; config.roomCapacity = data[0];
config.roomTimeout = data[1] - 1; config.roomTimeout = data[1] - 1;
const roomSettings = data[2]; const roomSettings = data[2];
@ -139,11 +137,11 @@ callbacks["EnterRoom"] = (jsonData) => {
mainWindow.busy = false; mainWindow.busy = false;
} }
callbacks["UpdateRoomList"] = (jsonData) => { callbacks["UpdateRoomList"] = (data) => {
const current = mainStack.currentItem; // should be lobby const current = mainStack.currentItem; // should be lobby
if (mainStack.depth === 2) { if (mainStack.depth === 2) {
current.roomModel.clear(); current.roomModel.clear();
JSON.parse(jsonData).forEach(room => { data.forEach(room => {
const [roomId, roomName, gameMode, playerNum, capacity, hasPassword, const [roomId, roomName, gameMode, playerNum, capacity, hasPassword,
outdated] = room; outdated] = room;
current.roomModel.append({ current.roomModel.append({
@ -154,10 +152,9 @@ callbacks["UpdateRoomList"] = (jsonData) => {
} }
} }
callbacks["UpdatePlayerNum"] = (j) => { callbacks["UpdatePlayerNum"] = (data) => {
const current = mainStack.currentItem; // should be lobby const current = mainStack.currentItem; // should be lobby
if (mainStack.depth === 2) { if (mainStack.depth === 2) {
const data = JSON.parse(j);
const l = data[0]; const l = data[0];
const s = data[1]; const s = data[1];
current.lobbyPlayerNum = l; current.lobbyPlayerNum = l;
@ -165,10 +162,9 @@ callbacks["UpdatePlayerNum"] = (j) => {
} }
} }
callbacks["Chat"] = (jsonData) => { callbacks["Chat"] = (data) => {
// jsonData: { string userName, string general, string time, string msg } // jsonData: { string userName, string general, string time, string msg }
const current = mainStack.currentItem; // lobby or room const current = mainStack.currentItem; // lobby or room
const data = JSON.parse(jsonData);
const pid = data.sender; const pid = data.sender;
const userName = data.userName; const userName = data.userName;
const general = luatr(data.general); const general = luatr(data.general);

View File

@ -421,10 +421,8 @@ function setCardFootnote(id, footnote) {
card.footnoteVisible = true; card.footnoteVisible = true;
} }
callbacks["SetCardFootnote"] = (j) => { callbacks["SetCardFootnote"] = (data) => {
const data = JSON.parse(j); const [id, note] = data;
const id = data[0];
const note = data[1];
setCardFootnote(id, note); setCardFootnote(id, note);
} }
@ -444,10 +442,8 @@ function setCardVirtName(id, name) {
card.virt_name = name; card.virt_name = name;
} }
callbacks["SetCardVirtName"] = (j) => { callbacks["SetCardVirtName"] = (data) => {
const data = JSON.parse(j); const [ids, note] = data;
const ids = data[0];
const note = data[1];
ids.forEach(id => setCardVirtName(id, note)); ids.forEach(id => setCardVirtName(id, note));
} }
@ -508,8 +504,7 @@ function processPrompt(prompt) {
return raw; return raw;
} }
callbacks["MaxCard"] = (jsonData) => { callbacks["MaxCard"] = (data) => {
const data = JSON.parse(jsonData);
const id = data.id; const id = data.id;
const cardMax = data.pcardMax; const cardMax = data.pcardMax;
const photo = getPhoto(id); const photo = getPhoto(id);
@ -530,7 +525,7 @@ function changeSelf(id) {
dashboard.self = photos.itemAt(i); dashboard.self = photos.itemAt(i);
} }
} }
callbacks["ArrangeSeats"](JSON.stringify(order)); callbacks["ArrangeSeats"](order);
// update dashboard // update dashboard
dashboard.update(); dashboard.update();
@ -542,12 +537,11 @@ function changeSelf(id) {
} }
} }
callbacks["AddPlayer"] = (jsonData) => { callbacks["AddPlayer"] = (data) => {
// jsonData: int id, string screenName, string avatar, bool ready // jsonData: int id, string screenName, string avatar, bool ready
for (let i = 0; i < photoModel.count; i++) { for (let i = 0; i < photoModel.count; i++) {
const item = photoModel.get(i); const item = photoModel.get(i);
if (item.id === -1) { if (item.id === -1) {
const data = JSON.parse(jsonData);
const uid = data[0]; const uid = data[0];
const name = data[1]; const name = data[1];
const avatar = data[2]; const avatar = data[2];
@ -740,9 +734,9 @@ function updateSelectedTargets(playerid, selected) {
} }
} }
callbacks["RemovePlayer"] = (jsonData) => { callbacks["RemovePlayer"] = (data) => {
// jsonData: int uid // jsonData: int uid
const uid = JSON.parse(jsonData)[0]; const uid = data[0];
const model = getPhotoModel(uid); const model = getPhotoModel(uid);
if (typeof(model) !== "undefined") { if (typeof(model) !== "undefined") {
model.id = -1; model.id = -1;
@ -753,9 +747,9 @@ callbacks["RemovePlayer"] = (jsonData) => {
} }
} }
callbacks["RoomOwner"] = (jsonData) => { callbacks["RoomOwner"] = (data) => {
// jsonData: int uid of the owner // jsonData: int uid of the owner
const uid = JSON.parse(jsonData)[0]; const uid = data[0];
roomScene.isOwner = (Self.id === uid); roomScene.isOwner = (Self.id === uid);
@ -777,8 +771,7 @@ function checkAllReady() {
roomScene.isAllReady = allReady; roomScene.isAllReady = allReady;
} }
callbacks["ReadyChanged"] = (j) => { callbacks["ReadyChanged"] = (data) => {
const data = JSON.parse(j);
const id = data[0]; const id = data[0];
const ready = data[1]; const ready = data[1];
@ -793,8 +786,7 @@ callbacks["ReadyChanged"] = (j) => {
} }
} }
callbacks["NetStateChanged"] = (j) => { callbacks["NetStateChanged"] = (data) => {
const data = JSON.parse(j);
const id = data[0]; const id = data[0];
let state = data[1]; let state = data[1];
@ -805,9 +797,8 @@ callbacks["NetStateChanged"] = (j) => {
model.netstate = state; model.netstate = state;
} }
callbacks["PropertyUpdate"] = (jsonData) => { callbacks["PropertyUpdate"] = (data) => {
// jsonData: int id, string property_name, value // jsonData: int id, string property_name, value
const data = JSON.parse(jsonData);
const uid = data[0]; const uid = data[0];
const property_name = data[1]; const property_name = data[1];
let value = data[2]; let value = data[2];
@ -863,9 +854,8 @@ callbacks["StartGame"] = (jsonData) => {
} }
} }
callbacks["ArrangeSeats"] = (jsonData) => { callbacks["ArrangeSeats"] = (order) => {
// jsonData: seat order // jsonData: seat order
const order = JSON.parse(jsonData);
for (let i = 0; i < photoModel.count; i++) { for (let i = 0; i < photoModel.count; i++) {
const item = photoModel.get(i); const item = photoModel.get(i);
@ -895,10 +885,9 @@ function cancelAllFocus() {
} }
} }
callbacks["MoveFocus"] = (jsonData) => { callbacks["MoveFocus"] = (data) => {
// jsonData: int[] focuses, string command // jsonData: int[] focuses, string command
cancelAllFocus(); cancelAllFocus();
const data = JSON.parse(jsonData);
const focuses = data[0]; const focuses = data[0];
const command = data[1]; const command = data[1];
@ -925,9 +914,8 @@ callbacks["MoveFocus"] = (jsonData) => {
} }
} }
callbacks["PlayerRunned"] = (jsonData) => { callbacks["PlayerRunned"] = (data) => {
// jsonData: int runner, int robot // jsonData: int runner, int robot
const data = JSON.parse(jsonData);
const runner = data[0]; const runner = data[0];
const robot = data[1]; const robot = data[1];
@ -937,9 +925,8 @@ callbacks["PlayerRunned"] = (jsonData) => {
} }
} }
callbacks["AskForGeneral"] = (jsonData) => { callbacks["AskForGeneral"] = (data) => {
// jsonData: string[] Generals // jsonData: string[] Generals
const data = JSON.parse(jsonData);
const generals = data[0]; const generals = data[0];
const n = data[1]; const n = data[1];
const convert = data[2]; const convert = data[2];
@ -960,9 +947,8 @@ callbacks["AskForGeneral"] = (jsonData) => {
box.updatePosition(); box.updatePosition();
} }
callbacks["AskForSkillInvoke"] = (jsonData) => { callbacks["AskForSkillInvoke"] = (data) => {
// jsonData: [ string name, string prompt ] // jsonData: [ string name, string prompt ]
const data = JSON.parse(jsonData);
const skill = data[0]; const skill = data[0];
const prompt = data[1]; const prompt = data[1];
roomScene.promptText = prompt ? processPrompt(prompt) roomScene.promptText = prompt ? processPrompt(prompt)
@ -973,8 +959,7 @@ callbacks["AskForSkillInvoke"] = (jsonData) => {
roomScene.cancelButton.enabled = true; roomScene.cancelButton.enabled = true;
} }
callbacks["AskForGuanxing"] = (jsonData) => { callbacks["AskForGuanxing"] = (data) => {
const data = JSON.parse(jsonData);
const cards = []; const cards = [];
const min_top_cards = data.min_top_cards; const min_top_cards = data.min_top_cards;
const max_top_cards = data.max_top_cards; const max_top_cards = data.max_top_cards;
@ -1011,8 +996,7 @@ callbacks["AskForGuanxing"] = (jsonData) => {
}); });
} }
callbacks["AskForExchange"] = (jsonData) => { callbacks["AskForExchange"] = (data) => {
const data = JSON.parse(jsonData);
const cards = []; const cards = [];
const cards_name = []; const cards_name = [];
const capacities = []; const capacities = [];
@ -1041,10 +1025,9 @@ callbacks["AskForExchange"] = (jsonData) => {
}); });
} }
callbacks["AskForChoice"] = (jsonData) => { callbacks["AskForChoice"] = (data) => {
// jsonData: [ string[] choices, string skill ] // jsonData: [ string[] choices, string skill ]
// TODO: multiple choices, e.g. benxi_ol // TODO: multiple choices, e.g. benxi_ol
const data = JSON.parse(jsonData);
const choices = data[0]; const choices = data[0];
const all_choices = data[1]; const all_choices = data[1];
const skill_name = data[2]; const skill_name = data[2];
@ -1073,10 +1056,9 @@ callbacks["AskForChoice"] = (jsonData) => {
}); });
} }
callbacks["AskForChoices"] = (jsonData) => { callbacks["AskForChoices"] = (data) => {
// jsonData: [ string[] choices, string skill ] // jsonData: [ string[] choices, string skill ]
// TODO: multiple choices, e.g. benxi_ol // TODO: multiple choices, e.g. benxi_ol
const data = JSON.parse(jsonData);
const choices = data[0]; const choices = data[0];
const all_choices = data[1]; const all_choices = data[1];
const min_num = data[2][0]; const min_num = data[2][0];
@ -1115,10 +1097,9 @@ callbacks["AskForChoices"] = (jsonData) => {
}); });
} }
callbacks["AskForCardChosen"] = (jsonData) => { callbacks["AskForCardChosen"] = (data) => {
// jsonData: [ int[] handcards, int[] equips, int[] delayedtricks, // jsonData: [ int[] handcards, int[] equips, int[] delayedtricks,
// string reason ] // string reason ]
const data = JSON.parse(jsonData);
const reason = data._reason; const reason = data._reason;
const prompt = data._prompt; const prompt = data._prompt;
if (prompt === "") { if (prompt === "") {
@ -1145,10 +1126,9 @@ callbacks["AskForCardChosen"] = (jsonData) => {
box.cardSelected.connect(cid => replyToServer(cid)); box.cardSelected.connect(cid => replyToServer(cid));
} }
callbacks["AskForCardsChosen"] = (jsonData) => { callbacks["AskForCardsChosen"] = (data) => {
// jsonData: [ int[] handcards, int[] equips, int[] delayedtricks, // jsonData: [ int[] handcards, int[] equips, int[] delayedtricks,
// int min, int max, string reason ] // int min, int max, string reason ]
const data = JSON.parse(jsonData);
const min = data._min; const min = data._min;
const max = data._max; const max = data._max;
const reason = data._reason; const reason = data._reason;
@ -1182,8 +1162,8 @@ callbacks["AskForCardsChosen"] = (jsonData) => {
}); });
} }
callbacks["AskForPoxi"] = (jsonData) => { callbacks["AskForPoxi"] = (dat) => {
const { type, data, extra_data, cancelable } = JSON.parse(jsonData); const { type, data, extra_data, cancelable } = dat;
roomScene.state = "replying"; roomScene.state = "replying";
roomScene.popupBox.sourceComponent = roomScene.popupBox.sourceComponent =
@ -1208,8 +1188,7 @@ callbacks["AskForPoxi"] = (jsonData) => {
}); });
} }
callbacks["AskForMoveCardInBoard"] = (jsonData) => { callbacks["AskForMoveCardInBoard"] = (data) => {
const data = JSON.parse(jsonData);
const { cards, cardsPosition, generalNames, playerIds } = data; const { cards, cardsPosition, generalNames, playerIds } = data;
roomScene.state = "replying"; roomScene.state = "replying";
@ -1241,15 +1220,13 @@ callbacks["AskForMoveCardInBoard"] = (jsonData) => {
}); });
} }
callbacks["MoveCards"] = (jsonData) => { callbacks["MoveCards"] = (moves) => {
// jsonData: merged moves // jsonData: merged moves
const moves = JSON.parse(jsonData);
moveCards(moves); moveCards(moves);
} }
callbacks["PlayCard"] = (jsonData) => { callbacks["PlayCard"] = (playerId) => {
// jsonData: int playerId // jsonData: int playerId
const playerId = parseInt(jsonData);
if (playerId === Self.id) { if (playerId === Self.id) {
roomScene.setPrompt(luatr("#PlayCard"), true); roomScene.setPrompt(luatr("#PlayCard"), true);
roomScene.state = "playing"; roomScene.state = "playing";
@ -1257,9 +1234,8 @@ callbacks["PlayCard"] = (jsonData) => {
} }
} }
callbacks["LoseSkill"] = (jsonData) => { callbacks["LoseSkill"] = (data) => {
// jsonData: [ int player_id, string skill_name ] // jsonData: [ int player_id, string skill_name ]
const data = JSON.parse(jsonData);
const id = data[0]; const id = data[0];
const skill_name = data[1]; const skill_name = data[1];
const prelight = data[2]; const prelight = data[2];
@ -1268,9 +1244,8 @@ callbacks["LoseSkill"] = (jsonData) => {
} }
} }
callbacks["AddSkill"] = (jsonData) => { callbacks["AddSkill"] = (data) => {
// jsonData: [ int player_id, string skill_name ] // jsonData: [ int player_id, string skill_name ]
const data = JSON.parse(jsonData);
const id = data[0]; const id = data[0];
const skill_name = data[1]; const skill_name = data[1];
const prelight = data[2]; const prelight = data[2];
@ -1279,17 +1254,15 @@ callbacks["AddSkill"] = (jsonData) => {
} }
} }
callbacks["PrelightSkill"] = (jsonData) => { callbacks["PrelightSkill"] = (data) => {
const data = JSON.parse(jsonData);
const skill_name = data[0]; const skill_name = data[0];
const prelight = data[1]; const prelight = data[1];
dashboard.prelightSkill(skill_name, prelight); dashboard.prelightSkill(skill_name, prelight);
} }
callbacks["AskForUseActiveSkill"] = (jsonData) => { callbacks["AskForUseActiveSkill"] = (data) => {
// jsonData: string skill_name, string prompt // jsonData: string skill_name, string prompt
const data = JSON.parse(jsonData);
const skill_name = data[0]; const skill_name = data[0];
const prompt = data[1]; const prompt = data[1];
const cancelable = data[2]; const cancelable = data[2];
@ -1323,9 +1296,8 @@ callbacks["GameLog"] = (jsonData) => {
roomScene.addToLog(jsonData) roomScene.addToLog(jsonData)
} }
callbacks["AskForUseCard"] = (jsonData) => { callbacks["AskForUseCard"] = (data) => {
// jsonData: card, pattern, prompt, cancelable, {} // jsonData: card, pattern, prompt, cancelable, {}
const data = JSON.parse(jsonData);
const cardname = data[0]; const cardname = data[0];
const pattern = data[1]; const pattern = data[1];
const prompt = data[2]; const prompt = data[2];
@ -1355,9 +1327,8 @@ callbacks["AskForUseCard"] = (jsonData) => {
cancelButton.enabled = true; cancelButton.enabled = true;
} }
callbacks["AskForResponseCard"] = (jsonData) => { callbacks["AskForResponseCard"] = (data) => {
// jsonData: card_name, pattern, prompt, cancelable, {} // jsonData: card_name, pattern, prompt, cancelable, {}
const data = JSON.parse(jsonData);
const cardname = data[0]; const cardname = data[0];
const pattern = data[1]; const pattern = data[1];
const prompt = data[2]; const prompt = data[2];
@ -1381,8 +1352,7 @@ callbacks["WaitForNullification"] = () => {
roomScene.state = "notactive"; roomScene.state = "notactive";
} }
callbacks["SetPlayerMark"] = (jsonData) => { callbacks["SetPlayerMark"] = (data) => {
const data = JSON.parse(jsonData);
const player = getPhoto(data[0]); const player = getPhoto(data[0]);
const mark = data[1]; const mark = data[1];
const value = data[2] instanceof Object ? data[2] : data[2].toString(); const value = data[2] instanceof Object ? data[2] : data[2].toString();
@ -1394,8 +1364,7 @@ callbacks["SetPlayerMark"] = (jsonData) => {
} }
} }
callbacks["SetBanner"] = (jsonData) => { callbacks["SetBanner"] = (data) => {
const data = JSON.parse(jsonData);
const mark = data[0]; const mark = data[0];
const value = data[1] instanceof Object ? data[1] : data[1].toString(); const value = data[1] instanceof Object ? data[1] : data[1].toString();
let area = roomScene.banner; let area = roomScene.banner;
@ -1406,9 +1375,8 @@ callbacks["SetBanner"] = (jsonData) => {
} }
} }
callbacks["Animate"] = (jsonData) => { callbacks["Animate"] = (data) => {
// jsonData: [Object object] // jsonData: [Object object]
const data = JSON.parse(jsonData);
switch (data.type) { switch (data.type) {
case "Indicate": case "Indicate":
data.to.forEach(item => { data.to.forEach(item => {
@ -1471,9 +1439,8 @@ callbacks["Animate"] = (jsonData) => {
} }
} }
callbacks["LogEvent"] = (jsonData) => { callbacks["LogEvent"] = (data) => {
// jsonData: [Object object] // jsonData: [Object object]
const data = JSON.parse(jsonData);
switch (data.type) { switch (data.type) {
case "Damage": { case "Damage": {
const item = getPhotoOrDashboard(data.to); const item = getPhotoOrDashboard(data.to);
@ -1557,8 +1524,7 @@ callbacks["GameOver"] = (jsonData) => {
// roomScene.isStarted = false; // roomScene.isStarted = false;
} }
callbacks["FillAG"] = (j) => { callbacks["FillAG"] = (data) => {
const data = JSON.parse(j);
const ids = data[0]; const ids = data[0];
roomScene.manualBox.sourceComponent = roomScene.manualBox.sourceComponent =
Qt.createComponent("../RoomElement/AG.qml"); Qt.createComponent("../RoomElement/AG.qml");
@ -1570,9 +1536,8 @@ callbacks["AskForAG"] = (j) => {
roomScene.manualBox.item.interactive = true; roomScene.manualBox.item.interactive = true;
} }
callbacks["TakeAG"] = (j) => { callbacks["TakeAG"] = (data) => {
if (!roomScene.manualBox.item) return; if (!roomScene.manualBox.item) return;
const data = JSON.parse(j);
const pid = data[0]; const pid = data[0];
const cid = data[1]; const cid = data[1];
const item = getPhoto(pid); const item = getPhoto(pid);
@ -1584,8 +1549,7 @@ callbacks["TakeAG"] = (j) => {
callbacks["CloseAG"] = () => roomScene.manualBox.item.close(); callbacks["CloseAG"] = () => roomScene.manualBox.item.close();
callbacks["CustomDialog"] = (j) => { callbacks["CustomDialog"] = (data) => {
const data = JSON.parse(j);
const path = data.path; const path = data.path;
const dat = data.data; const dat = data.data;
roomScene.state = "replying"; roomScene.state = "replying";
@ -1595,8 +1559,7 @@ callbacks["CustomDialog"] = (j) => {
} }
} }
callbacks["MiniGame"] = (j) => { callbacks["MiniGame"] = (data) => {
const data = JSON.parse(j);
const game = data.type; const game = data.type;
const dat = data.data; const dat = data.data;
const gdata = lcall("GetMiniGame", game, Self.id, JSON.stringify(dat)); const gdata = lcall("GetMiniGame", game, Self.id, JSON.stringify(dat));
@ -1607,15 +1570,13 @@ callbacks["MiniGame"] = (j) => {
} }
} }
callbacks["UpdateMiniGame"] = (j) => { callbacks["UpdateMiniGame"] = (data) => {
const data = JSON.parse(j);
if (roomScene.popupBox.item) { if (roomScene.popupBox.item) {
roomScene.popupBox.item.updateData(data); roomScene.popupBox.item.updateData(data);
} }
} }
callbacks["UpdateLimitSkill"] = (j) => { callbacks["UpdateLimitSkill"] = (data) => {
const data = JSON.parse(j);
const id = data[0]; const id = data[0];
const skill = data[1]; const skill = data[1];
const time = data[2]; const time = data[2];
@ -1636,8 +1597,7 @@ callbacks["UpdateRoundNum"] = (j) => {
roomScene.miscStatus.roundNum = data; roomScene.miscStatus.roundNum = data;
} }
callbacks["UpdateGameData"] = (j) => { callbacks["UpdateGameData"] = (data) => {
const data = JSON.parse(j);
const id = data[0]; const id = data[0];
const total = data[1]; const total = data[1];
const win = data[2]; const win = data[2];

View File

@ -261,21 +261,11 @@ Window {
// fake global functions // fake global functions
function lcall(funcName, ...params) { function lcall(funcName, ...params) {
const ret = Backend.callLuaFunction(funcName, [...params]); return Backend.callLuaFunction(funcName, [...params]);
try {
return JSON.parse(ret);
} catch (e) {
return ret;
}
} }
function leval(lua) { function leval(lua) {
const ret = Backend.evalLuaExp(`return json.encode(${lua})`); return Backend.evalLuaExp(`return ${lua}`);
try {
return JSON.parse(ret);
} catch (e) {
return ret;
}
} }
function luatr(src) { function luatr(src) {

View File

@ -8,6 +8,7 @@
---@field public current ClientPlayer @ 当前回合玩家 ---@field public current ClientPlayer @ 当前回合玩家
---@field public discard_pile integer[] @ 弃牌堆 ---@field public discard_pile integer[] @ 弃牌堆
---@field public observing boolean ---@field public observing boolean
---@field public record any
Client = AbstractRoom:subclass('Client') Client = AbstractRoom:subclass('Client')
-- load client classes -- load client classes
@ -23,11 +24,17 @@ local pattern_refresh_commands = {
"AskForResponseCard", "AskForResponseCard",
} }
-- 无需进行JSON.parse但可能传入JSON字符串的command
local no_decode_commands = {
"ErrorMsg",
"Heartbeat",
}
function Client:initialize() function Client:initialize()
AbstractRoom.initialize(self) AbstractRoom.initialize(self)
self.client = fk.ClientInstance self.client = fk.ClientInstance
self.notifyUI = function(self, command, jsonData) self.notifyUI = function(self, command, data)
fk.Backend:emitNotifyUI(command, jsonData) fk.Backend:notifyUI(command, data)
end end
self.client.callback = function(_self, command, jsonData, isRequest) self.client.callback = function(_self, command, jsonData, isRequest)
if self.recording then if self.recording then
@ -35,6 +42,18 @@ function Client:initialize()
end end
local cb = fk.client_callback[command] local cb = fk.client_callback[command]
local data
if table.contains(no_decode_commands, command) then
data = jsonData
else
local err, ret = pcall(json.decode, jsonData)
if err == false then
-- 不关心报错
data = jsonData
else
data = ret
end
end
if table.contains(pattern_refresh_commands, command) then if table.contains(pattern_refresh_commands, command) then
Fk.currentResponsePattern = nil Fk.currentResponsePattern = nil
@ -42,9 +61,9 @@ function Client:initialize()
end end
if (type(cb) == "function") then if (type(cb) == "function") then
cb(jsonData) cb(data)
else else
self:notifyUI(command, jsonData); self:notifyUI(command, data)
end end
end end
@ -58,7 +77,7 @@ function Client:initialize()
end end
---@param id integer ---@param id integer
---@return ClientPlayer? ---@return ClientPlayer
function Client:getPlayerById(id) function Client:getPlayerById(id)
if id == Self.id then return Self end if id == Self.id then return Self end
for _, p in ipairs(self.players) do for _, p in ipairs(self.players) do
@ -95,7 +114,7 @@ function Client:moveCards(moves)
for _, move in ipairs(moves) do for _, move in ipairs(moves) do
if move.from and move.fromArea then if move.from and move.fromArea then
local from = self:getPlayerById(move.from) local from = self:getPlayerById(move.from)
self:notifyUI("MaxCard", json.encode{ self:notifyUI("MaxCard", {
pcardMax = from:getMaxCards(), pcardMax = from:getMaxCards(),
id = move.from, id = move.from,
}) })
@ -114,7 +133,7 @@ function Client:moveCards(moves)
if move.to and move.toArea then if move.to and move.toArea then
local ids = move.ids local ids = move.ids
self:notifyUI("MaxCard", json.encode{ self:notifyUI("MaxCard", {
pcardMax = self:getPlayerById(move.to):getMaxCards(), pcardMax = self:getPlayerById(move.to):getMaxCards(),
id = move.to, id = move.to,
}) })
@ -172,6 +191,7 @@ local function parseMsg(msg, nocolor)
local from = getPlayerStr(data.from, "#0C8F0C") local from = getPlayerStr(data.from, "#0C8F0C")
---@type any
local to = data.to or Util.DummyTable local to = data.to or Util.DummyTable
local to_str = {} local to_str = {}
for _, id in ipairs(to) do for _, id in ipairs(to) do
@ -179,6 +199,7 @@ local function parseMsg(msg, nocolor)
end end
to = table.concat(to_str, ", ") to = table.concat(to_str, ", ")
---@type any
local card = data.card or Util.DummyTable local card = data.card or Util.DummyTable
local allUnknown = true local allUnknown = true
local unknownCount = 0 local unknownCount = 0
@ -238,20 +259,16 @@ end
function Client:setCardNote(ids, msg) function Client:setCardNote(ids, msg)
for _, id in ipairs(ids) do for _, id in ipairs(ids) do
if id ~= -1 then if id ~= -1 then
self:notifyUI("SetCardFootnote", json.encode{ id, parseMsg(msg, true) }) self:notifyUI("SetCardFootnote", { id, parseMsg(msg, true) })
end end
end end
end end
fk.client_callback["SetCardFootnote"] = function(jsonData) fk.client_callback["SetCardFootnote"] = function(data)
local data = json.decode(jsonData)
ClientInstance:setCardNote(data[1], data[2]); ClientInstance:setCardNote(data[1], data[2]);
end end
fk.client_callback["Setup"] = function(jsonData) local function setup(id, name, avatar)
-- jsonData: [ int id, string screenName, string avatar ]
local data = json.decode(jsonData)
local id, name, avatar = data[1], data[2], data[3]
local self = fk.Self local self = fk.Self
self:setId(id) self:setId(id)
self:setScreenName(name) self:setScreenName(name)
@ -259,16 +276,21 @@ fk.client_callback["Setup"] = function(jsonData)
Self = ClientPlayer:new(fk.Self) Self = ClientPlayer:new(fk.Self)
end end
fk.client_callback["EnterRoom"] = function(jsonData) fk.client_callback["Setup"] = function(data)
-- jsonData: [ int id, string screenName, string avatar ]
local id, name, avatar = data[1], data[2], data[3]
setup(id, name, avatar)
end
fk.client_callback["EnterRoom"] = function(_data)
Self = ClientPlayer:new(fk.Self) Self = ClientPlayer:new(fk.Self)
ClientInstance = Client:new() -- clear old client data ClientInstance = Client:new() -- clear old client data
ClientInstance.players = {Self} ClientInstance.players = {Self}
ClientInstance.alive_players = {Self} ClientInstance.alive_players = {Self}
ClientInstance.discard_pile = {} ClientInstance.discard_pile = {}
local _data = json.decode(jsonData)
local data = _data[3] local data = _data[3]
ClientInstance.enter_room_data = jsonData; ClientInstance.enter_room_data = json.encode(_data);
ClientInstance.room_settings = data ClientInstance.room_settings = data
table.insertTableIfNeed( table.insertTableIfNeed(
data.disabledPack, data.disabledPack,
@ -276,25 +298,23 @@ fk.client_callback["EnterRoom"] = function(jsonData)
) )
ClientInstance.disabled_packs = data.disabledPack ClientInstance.disabled_packs = data.disabledPack
ClientInstance.disabled_generals = data.disabledGenerals ClientInstance.disabled_generals = data.disabledGenerals
ClientInstance:notifyUI("EnterRoom", jsonData) ClientInstance:notifyUI("EnterRoom", _data)
end end
fk.client_callback["AddPlayer"] = function(jsonData) fk.client_callback["AddPlayer"] = function(data)
-- jsonData: [ int id, string screenName, string avatar ] -- jsonData: [ int id, string screenName, string avatar ]
-- when other player enter the room, we create clientplayer(C and lua) for them -- when other player enter the room, we create clientplayer(C and lua) for them
local data = json.decode(jsonData)
local id, name, avatar, time = data[1], data[2], data[3], data[5] local id, name, avatar, time = data[1], data[2], data[3], data[5]
local player = fk.ClientInstance:addPlayer(id, name, avatar) local player = fk.ClientInstance:addPlayer(id, name, avatar)
player:addTotalGameTime(time or 0) -- 以防再次智迟 player:addTotalGameTime(time or 0) -- 以防再次智迟
local p = ClientPlayer:new(player) local p = ClientPlayer:new(player)
table.insert(ClientInstance.players, p) table.insert(ClientInstance.players, p)
table.insert(ClientInstance.alive_players, p) table.insert(ClientInstance.alive_players, p)
ClientInstance:notifyUI("AddPlayer", jsonData) ClientInstance:notifyUI("AddPlayer", data)
end end
fk.client_callback["RemovePlayer"] = function(jsonData) fk.client_callback["RemovePlayer"] = function(data)
-- jsonData: [ int id ] -- jsonData: [ int id ]
local data = json.decode(jsonData)
local id = data[1] local id = data[1]
for _, p in ipairs(ClientInstance.players) do for _, p in ipairs(ClientInstance.players) do
if p.player:getId() == id then if p.player:getId() == id then
@ -305,14 +325,13 @@ fk.client_callback["RemovePlayer"] = function(jsonData)
end end
if id ~= Self.id then if id ~= Self.id then
fk.ClientInstance:removePlayer(id) fk.ClientInstance:removePlayer(id)
ClientInstance:notifyUI("RemovePlayer", jsonData) ClientInstance:notifyUI("RemovePlayer", data)
end end
end end
fk.client_callback["AddObserver"] = function(jsonData) fk.client_callback["AddObserver"] = function(data)
-- jsonData: [ int id, string screenName, string avatar ] -- jsonData: [ int id, string screenName, string avatar ]
-- when observer enter the room, we create lua clientplayer for them -- when observer enter the room, we create lua clientplayer for them
local data = json.decode(jsonData)
local id, name, avatar = data[1], data[2], data[3] local id, name, avatar = data[1], data[2], data[3]
local player = { local player = {
getId = function() return id end, getId = function() return id end,
@ -323,8 +342,7 @@ fk.client_callback["AddObserver"] = function(jsonData)
table.insert(ClientInstance.observers, p) table.insert(ClientInstance.observers, p)
end end
fk.client_callback["RemoveObserver"] = function(jsonData) fk.client_callback["RemoveObserver"] = function(data)
local data = json.decode(jsonData)
local id = data[1] local id = data[1]
for _, p in ipairs(ClientInstance.observers) do for _, p in ipairs(ClientInstance.observers) do
if p.player:getId() == id then if p.player:getId() == id then
@ -334,8 +352,7 @@ fk.client_callback["RemoveObserver"] = function(jsonData)
end end
end end
fk.client_callback["ArrangeSeats"] = function(jsonData) fk.client_callback["ArrangeSeats"] = function(data)
local data = json.decode(jsonData)
local n = #ClientInstance.players local n = #ClientInstance.players
local players = {} local players = {}
@ -352,12 +369,11 @@ fk.client_callback["ArrangeSeats"] = function(jsonData)
ClientInstance.players = players ClientInstance.players = players
ClientInstance:notifyUI("ArrangeSeats", jsonData) ClientInstance:notifyUI("ArrangeSeats", data)
end end
fk.client_callback["PropertyUpdate"] = function(jsonData) fk.client_callback["PropertyUpdate"] = function(data)
-- jsonData: [ int id, string property_name, value ] -- jsonData: [ int id, string property_name, value ]
local data = json.decode(jsonData)
local id, name, value = data[1], data[2], data[3] local id, name, value = data[1], data[2], data[3]
local p = ClientInstance:getPlayerById(id) local p = ClientInstance:getPlayerById(id)
p[name] = value p[name] = value
@ -370,16 +386,15 @@ fk.client_callback["PropertyUpdate"] = function(jsonData)
end end
end end
ClientInstance:notifyUI("PropertyUpdate", jsonData) ClientInstance:notifyUI("PropertyUpdate", data)
ClientInstance:notifyUI("MaxCard", json.encode{ ClientInstance:notifyUI("MaxCard", {
pcardMax = ClientInstance:getPlayerById(id):getMaxCards(), pcardMax = ClientInstance:getPlayerById(id):getMaxCards(),
id = id, id = id,
}) })
end end
fk.client_callback["AskForCardChosen"] = function(jsonData) fk.client_callback["AskForCardChosen"] = function(data)
-- jsonData: [ int target_id, string flag, int reason ] -- jsonData: [ int target_id, string flag, int reason ]
local data = json.decode(jsonData)
local id, flag, reason, prompt = data[1], data[2], data[3], data[4] local id, flag, reason, prompt = data[1], data[2], data[3], data[4]
local target = ClientInstance:getPlayerById(id) local target = ClientInstance:getPlayerById(id)
local hand = target.player_cards[Player.Hand] local hand = target.player_cards[Player.Hand]
@ -411,13 +426,13 @@ fk.client_callback["AskForCardChosen"] = function(jsonData)
ui_data._reason = reason ui_data._reason = reason
ui_data._prompt = prompt ui_data._prompt = prompt
end end
ClientInstance:notifyUI("AskForCardChosen", json.encode(ui_data)) ClientInstance:notifyUI("AskForCardChosen", ui_data)
end end
fk.client_callback["AskForCardsChosen"] = function(jsonData) fk.client_callback["AskForCardsChosen"] = function(data)
-- jsonData: [ int target_id, int min, int max, string flag, int reason ] -- jsonData: [ int target_id, int min, int max, string flag, int reason ]
local data = json.decode(jsonData) local id, min, max, flag, reason, prompt = table.unpack(data)
local id, min, max, flag, reason, prompt = data[1], data[2], data[3], data[4], data[5], data[6] --data[1], data[2], data[3], data[4], data[5], data[6]
local target = ClientInstance:getPlayerById(id) local target = ClientInstance:getPlayerById(id)
local hand = target.player_cards[Player.Hand] local hand = target.player_cards[Player.Hand]
local equip = target.player_cards[Player.Equip] local equip = target.player_cards[Player.Equip]
@ -452,7 +467,7 @@ fk.client_callback["AskForCardsChosen"] = function(jsonData)
ui_data._reason = reason ui_data._reason = reason
ui_data._prompt = prompt ui_data._prompt = prompt
end end
ClientInstance:notifyUI("AskForCardsChosen", json.encode(ui_data)) ClientInstance:notifyUI("AskForCardsChosen", ui_data)
end end
--- separated moves to many moves(one card per move) --- separated moves to many moves(one card per move)
@ -646,23 +661,21 @@ local function sendMoveCardLog(move)
end end
end end
fk.client_callback["MoveCards"] = function(jsonData) fk.client_callback["MoveCards"] = function(raw_moves)
-- jsonData: CardsMoveStruct[] -- jsonData: CardsMoveStruct[]
local raw_moves = json.decode(jsonData)
local separated = separateMoves(raw_moves) local separated = separateMoves(raw_moves)
ClientInstance:moveCards(separated) ClientInstance:moveCards(separated)
local merged = mergeMoves(separated) local merged = mergeMoves(separated)
ClientInstance:notifyUI("MoveCards", json.encode(merged)) ClientInstance:notifyUI("MoveCards", merged)
for _, move in ipairs(merged) do for _, move in ipairs(merged) do
sendMoveCardLog(move) sendMoveCardLog(move)
end end
end end
fk.client_callback["ShowCard"] = function(jsonData) fk.client_callback["ShowCard"] = function(data)
local data = json.decode(jsonData)
local from = data.from local from = data.from
local cards = data.cards local cards = data.cards
ClientInstance:notifyUI("MoveCards", json.encode{ ClientInstance:notifyUI("MoveCards", {
{ {
ids = cards, ids = cards,
fromArea = Card.DrawPile, fromArea = Card.DrawPile,
@ -679,15 +692,14 @@ local function updateLimitSkill(pid, skill, times)
if skill:isSwitchSkill() then if skill:isSwitchSkill() then
local _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
if times == -1 then _times = -1 end if times == -1 then _times = -1 end
ClientInstance:notifyUI("UpdateLimitSkill", json.encode{ pid, skill.switchSkillName, _times }) ClientInstance:notifyUI("UpdateLimitSkill", { 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", { pid, skill.name, times })
end end
end end
fk.client_callback["LoseSkill"] = function(jsonData) fk.client_callback["LoseSkill"] = function(data)
-- jsonData: [ int player_id, string skill_name ] -- jsonData: [ int player_id, string skill_name ]
local data = json.decode(jsonData)
local id, skill_name, fake = data[1], data[2], data[3] local id, skill_name, fake = data[1], data[2], data[3]
local target = ClientInstance:getPlayerById(id) local target = ClientInstance:getPlayerById(id)
local skill = Fk.skills[skill_name] local skill = Fk.skills[skill_name]
@ -695,7 +707,7 @@ fk.client_callback["LoseSkill"] = function(jsonData)
if not fake then if not fake then
target:loseSkill(skill) target:loseSkill(skill)
if skill.visible then if skill.visible then
ClientInstance:notifyUI("LoseSkill", jsonData) ClientInstance:notifyUI("LoseSkill", data)
end end
elseif skill.visible then elseif skill.visible then
-- 按理说能弄得更好的但还是复制粘贴舒服 -- 按理说能弄得更好的但还是复制粘贴舒服
@ -711,7 +723,7 @@ fk.client_callback["LoseSkill"] = function(jsonData)
if table.find(sks, function(s) return s:isInstanceOf(TriggerSkill) end) then if table.find(sks, function(s) return s:isInstanceOf(TriggerSkill) end) then
chk = true chk = true
ClientInstance:notifyUI("LoseSkill", jsonData) ClientInstance:notifyUI("LoseSkill", data)
end end
local active = table.filter(sks, function(s) local active = table.filter(sks, function(s)
@ -720,13 +732,13 @@ fk.client_callback["LoseSkill"] = function(jsonData)
if #active > 0 then if #active > 0 then
chk = true chk = true
ClientInstance:notifyUI("LoseSkill", json.encode { ClientInstance:notifyUI("LoseSkill", {
id, skill_name, id, skill_name,
}) })
end end
if not chk then if not chk then
ClientInstance:notifyUI("LoseSkill", json.encode { ClientInstance:notifyUI("LoseSkill", {
id, skill_name, id, skill_name,
}) })
end end
@ -735,9 +747,8 @@ fk.client_callback["LoseSkill"] = function(jsonData)
updateLimitSkill(id, skill, -1) updateLimitSkill(id, skill, -1)
end end
fk.client_callback["AddSkill"] = function(jsonData) fk.client_callback["AddSkill"] = function(data)
-- jsonData: [ int player_id, string skill_name ] -- jsonData: [ int player_id, string skill_name ]
local data = json.decode(jsonData)
local id, skill_name, fake = data[1], data[2], data[3] local id, skill_name, fake = data[1], data[2], data[3]
local target = ClientInstance:getPlayerById(id) local target = ClientInstance:getPlayerById(id)
local skill = Fk.skills[skill_name] local skill = Fk.skills[skill_name]
@ -745,7 +756,7 @@ fk.client_callback["AddSkill"] = function(jsonData)
if not fake then if not fake then
target:addSkill(skill) target:addSkill(skill)
if skill.visible then if skill.visible then
ClientInstance:notifyUI("AddSkill", jsonData) ClientInstance:notifyUI("AddSkill", data)
end end
elseif skill.visible then elseif skill.visible then
-- 添加假技能:服务器只会传一个主技能来。 -- 添加假技能:服务器只会传一个主技能来。
@ -758,7 +769,7 @@ fk.client_callback["AddSkill"] = function(jsonData)
if table.find(sks, function(s) return s:isInstanceOf(TriggerSkill) end) then if table.find(sks, function(s) return s:isInstanceOf(TriggerSkill) end) then
chk = true chk = true
ClientInstance:notifyUI("AddSkill", jsonData) ClientInstance:notifyUI("AddSkill", data)
end end
local active = table.filter(sks, function(s) local active = table.filter(sks, function(s)
@ -767,14 +778,14 @@ fk.client_callback["AddSkill"] = function(jsonData)
if #active > 0 then if #active > 0 then
chk = true chk = true
ClientInstance:notifyUI("AddSkill", json.encode { ClientInstance:notifyUI("AddSkill", {
id, skill_name, id, skill_name,
}) })
end end
-- 面板上总得有点啥东西表明自己有技能吧 = = -- 面板上总得有点啥东西表明自己有技能吧 = =
if not chk then if not chk then
ClientInstance:notifyUI("AddSkill", json.encode { ClientInstance:notifyUI("AddSkill", {
id, skill_name, id, skill_name,
}) })
end end
@ -787,30 +798,28 @@ fk.client_callback["AddSkill"] = function(jsonData)
updateLimitSkill(id, skill, target:usedSkillTimes(skill_name, Player.HistoryGame)) updateLimitSkill(id, skill, target:usedSkillTimes(skill_name, Player.HistoryGame))
end end
fk.client_callback["AskForUseActiveSkill"] = function(jsonData) fk.client_callback["AskForUseActiveSkill"] = function(data)
-- jsonData: [ string skill_name, string prompt, bool cancelable. json extra_data ] -- jsonData: [ string skill_name, string prompt, bool cancelable. json extra_data ]
local data = json.decode(jsonData)
local skill = Fk.skills[data[1]] local skill = Fk.skills[data[1]]
local extra_data = data[4] local extra_data = data[4]
skill._extra_data = extra_data skill._extra_data = extra_data
Fk.currentResponseReason = extra_data.skillName Fk.currentResponseReason = extra_data.skillName
ClientInstance:notifyUI("AskForUseActiveSkill", jsonData) ClientInstance:notifyUI("AskForUseActiveSkill", data)
end end
fk.client_callback["AskForUseCard"] = function(jsonData) fk.client_callback["AskForUseCard"] = function(data)
Fk.currentResponsePattern = json.decode(jsonData)[2] Fk.currentResponsePattern = data[2]
ClientInstance:notifyUI("AskForUseCard", jsonData) ClientInstance:notifyUI("AskForUseCard", data)
end end
fk.client_callback["AskForResponseCard"] = function(jsonData) fk.client_callback["AskForResponseCard"] = function(data)
Fk.currentResponsePattern = json.decode(jsonData)[2] Fk.currentResponsePattern = data[2]
ClientInstance:notifyUI("AskForResponseCard", jsonData) ClientInstance:notifyUI("AskForResponseCard", data)
end end
fk.client_callback["SetPlayerMark"] = function(jsonData) fk.client_callback["SetPlayerMark"] = function(data)
-- jsonData: [ int id, string mark, int value ] -- jsonData: [ int id, string mark, int value ]
local data = json.decode(jsonData)
local player, mark, value = data[1], data[2], data[3] local player, mark, value = data[1], data[2], data[3]
local p = ClientInstance:getPlayerById(player) local p = ClientInstance:getPlayerById(player)
p:setMark(mark, value) p:setMark(mark, value)
@ -825,37 +834,34 @@ fk.client_callback["SetPlayerMark"] = function(jsonData)
if text == "#hidden" then return end if text == "#hidden" then return end
end end
end end
ClientInstance:notifyUI("SetPlayerMark", jsonData) ClientInstance:notifyUI("SetPlayerMark", data)
end end
end end
fk.client_callback["SetBanner"] = function(jsonData) fk.client_callback["SetBanner"] = function(data)
-- jsonData: [ int id, string mark, int value ] -- jsonData: [ int id, string mark, int value ]
local data = json.decode(jsonData)
local mark, value = data[1], data[2] local mark, value = data[1], data[2]
ClientInstance:setBanner(mark, value) ClientInstance:setBanner(mark, value)
if string.sub(mark, 1, 1) == "@" then if string.sub(mark, 1, 1) == "@" then
ClientInstance:notifyUI("SetBanner", jsonData) ClientInstance:notifyUI("SetBanner", data)
end end
end end
fk.client_callback["SetCardMark"] = function(jsonData) fk.client_callback["SetCardMark"] = function(data)
-- jsonData: [ int id, string mark, int value ] -- jsonData: [ int id, string mark, int value ]
local data = json.decode(jsonData)
local card, mark, value = data[1], data[2], data[3] local card, mark, value = data[1], data[2], data[3]
Fk:getCardById(card):setMark(mark, value) Fk:getCardById(card):setMark(mark, value)
ClientInstance:notifyUI("UpdateCard", tostring(card)) ClientInstance:notifyUI("UpdateCard", card)
end end
fk.client_callback["Chat"] = function(jsonData) fk.client_callback["Chat"] = function(data)
-- jsonData: { int type, int sender, string msg } -- jsonData: { int type, int sender, string msg }
local data = json.decode(jsonData)
if data.type == 1 then if data.type == 1 then
data.general = "" data.general = ""
data.time = os.date("%H:%M:%S") data.time = os.date("%H:%M:%S")
ClientInstance:notifyUI("Chat", json.encode(data)) ClientInstance:notifyUI("Chat", data)
return return
end end
@ -873,37 +879,32 @@ fk.client_callback["Chat"] = function(jsonData)
end end
data.userName = p.player:getScreenName() data.userName = p.player:getScreenName()
data.time = os.date("%H:%M:%S") data.time = os.date("%H:%M:%S")
ClientInstance:notifyUI("Chat", json.encode(data)) ClientInstance:notifyUI("Chat", data)
end end
fk.client_callback["GameLog"] = function(jsonData) fk.client_callback["GameLog"] = function(data)
local data = json.decode(jsonData)
ClientInstance:appendLog(data) ClientInstance:appendLog(data)
end end
fk.client_callback["LogEvent"] = function(jsonData) fk.client_callback["LogEvent"] = function(data)
local data = json.decode(jsonData)
if data.type == "Death" then if data.type == "Death" then
table.removeOne( table.removeOne(
ClientInstance.alive_players, ClientInstance.alive_players,
ClientInstance:getPlayerById(data.to) ClientInstance:getPlayerById(data.to)
) )
end end
ClientInstance:notifyUI("LogEvent", jsonData) ClientInstance:notifyUI("LogEvent", data)
end end
fk.client_callback["AddCardUseHistory"] = function(jsonData) fk.client_callback["AddCardUseHistory"] = function(data)
local data = json.decode(jsonData)
Self:addCardUseHistory(data[1], data[2]) Self:addCardUseHistory(data[1], data[2])
end end
fk.client_callback["SetCardUseHistory"] = function(jsonData) fk.client_callback["SetCardUseHistory"] = function(data)
local data = json.decode(jsonData)
Self:setCardUseHistory(data[1], data[2], data[3]) Self:setCardUseHistory(data[1], data[2], data[3])
end end
fk.client_callback["AddSkillUseHistory"] = function(jsonData) fk.client_callback["AddSkillUseHistory"] = function(data)
local data = json.decode(jsonData)
local playerid, skill_name, time = data[1], data[2], data[3] local playerid, skill_name, time = data[1], data[2], data[3]
local player = ClientInstance:getPlayerById(playerid) local player = ClientInstance:getPlayerById(playerid)
player:addSkillUseHistory(skill_name, time) player:addSkillUseHistory(skill_name, time)
@ -913,8 +914,7 @@ fk.client_callback["AddSkillUseHistory"] = function(jsonData)
updateLimitSkill(playerid, Fk.skills[skill_name], player:usedSkillTimes(skill_name, Player.HistoryGame)) updateLimitSkill(playerid, Fk.skills[skill_name], player:usedSkillTimes(skill_name, Player.HistoryGame))
end end
fk.client_callback["SetSkillUseHistory"] = function(jsonData) fk.client_callback["SetSkillUseHistory"] = function(data)
local data = json.decode(jsonData)
local id, skill_name, time, scope = data[1], data[2], data[3], data[4] local id, skill_name, time, scope = data[1], data[2], data[3], data[4]
local player = ClientInstance:getPlayerById(id) local player = ClientInstance:getPlayerById(id)
player:setSkillUseHistory(skill_name, time, scope) player:setSkillUseHistory(skill_name, time, scope)
@ -924,8 +924,7 @@ fk.client_callback["SetSkillUseHistory"] = function(jsonData)
updateLimitSkill(id, Fk.skills[skill_name], player:usedSkillTimes(skill_name, Player.HistoryGame)) updateLimitSkill(id, Fk.skills[skill_name], player:usedSkillTimes(skill_name, Player.HistoryGame))
end end
fk.client_callback["AddVirtualEquip"] = function(jsonData) fk.client_callback["AddVirtualEquip"] = function(data)
local data = json.decode(jsonData)
local cname = data.name local cname = data.name
local player = ClientInstance:getPlayerById(data.player) local player = ClientInstance:getPlayerById(data.player)
local subcards = data.subcards local subcards = data.subcards
@ -934,8 +933,7 @@ fk.client_callback["AddVirtualEquip"] = function(jsonData)
player:addVirtualEquip(c) player:addVirtualEquip(c)
end end
fk.client_callback["RemoveVirtualEquip"] = function(jsonData) fk.client_callback["RemoveVirtualEquip"] = function(data)
local data = json.decode(jsonData)
local player = ClientInstance:getPlayerById(data.player) local player = ClientInstance:getPlayerById(data.player)
player:removeVirtualEquip(data.id) player:removeVirtualEquip(data.id)
end end
@ -944,38 +942,35 @@ fk.client_callback["Heartbeat"] = function()
ClientInstance.client:notifyServer("Heartbeat", "") ClientInstance.client:notifyServer("Heartbeat", "")
end end
fk.client_callback["ChangeSelf"] = function(jsonData) fk.client_callback["ChangeSelf"] = function(data)
local data = json.decode(jsonData) local p = ClientInstance:getPlayerById(data.id)
ClientInstance:getPlayerById(data.id).player_cards[Player.Hand] = data.handcards p.player_cards[Player.Hand] = data.handcards
ClientInstance:getPlayerById(data.id).special_cards = data.special_cards p.special_cards = data.special_cards
ClientInstance:notifyUI("ChangeSelf", data.id) ClientInstance:notifyUI("ChangeSelf", data.id)
end end
fk.client_callback["UpdateQuestSkillUI"] = function(jsonData) fk.client_callback["UpdateQuestSkillUI"] = function(data)
local data = json.decode(jsonData)
local player, skillName, usedTimes = data[1], data[2], data[3] local player, skillName, usedTimes = data[1], data[2], data[3]
updateLimitSkill(player, Fk.skills[skillName], usedTimes) updateLimitSkill(player, Fk.skills[skillName], usedTimes)
end end
fk.client_callback["UpdateGameData"] = function(jsonData) fk.client_callback["UpdateGameData"] = function(data)
local data = json.decode(jsonData)
local player, total, win, run = data[1], data[2], data[3], data[4] local player, total, win, run = data[1], data[2], data[3], data[4]
player = ClientInstance:getPlayerById(player) player = ClientInstance:getPlayerById(player)
if player then if player then
player.player:setGameData(total, win, run) player.player:setGameData(total, win, run)
end end
ClientInstance:notifyUI("UpdateGameData", jsonData) ClientInstance:notifyUI("UpdateGameData", data)
end end
fk.client_callback["AddTotalGameTime"] = function(jsonData) fk.client_callback["AddTotalGameTime"] = function(data)
local data = json.decode(jsonData)
local player, time = data[1], data[2] local player, time = data[1], data[2]
player = ClientInstance:getPlayerById(player) player = ClientInstance:getPlayerById(player)
if player then if player then
player.player:addTotalGameTime(time) player.player:addTotalGameTime(time)
if player == Self then if player == Self then
ClientInstance:notifyUI("AddTotalGameTime", jsonData) ClientInstance:notifyUI("AddTotalGameTime", data)
end end
end end
end end
@ -1051,30 +1046,184 @@ fk.client_callback["EnterLobby"] = function(jsonData)
c:notifyUI("EnterLobby", jsonData) c:notifyUI("EnterLobby", jsonData)
end end
fk.client_callback["PrintCard"] = function(j) fk.client_callback["PrintCard"] = function(data)
local data = json.decode(j)
local n, s, num = table.unpack(data) local n, s, num = table.unpack(data)
local cd = Fk:cloneCard(n, s, num) local cd = Fk:cloneCard(n, s, num)
Fk:_addPrintedCard(cd) Fk:_addPrintedCard(cd)
end end
fk.client_callback["AddBuddy"] = function(j) fk.client_callback["AddBuddy"] = function(data)
local c = ClientInstance local c = ClientInstance
local data = json.decode(j)
local id, hand = table.unpack(data) local id, hand = table.unpack(data)
local to = c:getPlayerById(id) local to = c:getPlayerById(id)
Self:addBuddy(to) Self:addBuddy(to)
to.player_cards[Player.Hand] = hand to.player_cards[Player.Hand] = hand
end end
fk.client_callback["RmBuddy"] = function(j) fk.client_callback["RmBuddy"] = function(data)
local c = ClientInstance local c = ClientInstance
local id = tonumber(j) local id = data
local to = c:getPlayerById(id) local to = c:getPlayerById(id)
Self:removeBuddy(to) Self:removeBuddy(to)
to.player_cards[Player.Hand] = table.map(to.player_cards, function() return -1 end) to.player_cards[Player.Hand] = table.map(to.player_cards, function() return -1 end)
end end
local function loadPlayerSummary(pdata)
local f = fk.client_callback["PropertyUpdate"]
local id = pdata.d[1]
local properties = {
"general", "deputyGeneral", "maxHp", "hp", "shield", "gender", "kingdom",
"dead", "role", "rest", "seat", "phase", "faceup", "chained",
"sealedSlots",
}
for _, k in ipairs(properties) do
if pdata.p[k] ~= nil then
f{ id, k, pdata.p[k] }
end
end
local card_moves = {}
local cards = pdata.c
if #cards[Player.Hand] ~= 0 then
local info = {}
for _, i in ipairs(cards[Player.Hand]) do
table.insert(info, { cardId = i, fromArea = Card.DrawPile })
end
local move = { moveInfo = info, to = id, toArea = Card.PlayerHand }
table.insert(card_moves, move)
end
if #cards[Player.Equip] ~= 0 then
local info = {}
for _, i in ipairs(cards[Player.Equip]) do
table.insert(info, { cardId = i, fromArea = Card.DrawPile })
end
local move = { moveInfo = info, to = id, toArea = Card.PlayerEquip }
table.insert(card_moves, move)
end
if #cards[Player.Judge] ~= 0 then
local info = {}
for _, i in ipairs(cards[Player.Judge]) do
table.insert(info, { cardId = i, fromArea = Card.DrawPile })
end
local move = { moveInfo = info, to = id, toArea = Card.PlayerJudge }
table.insert(card_moves, move)
end
for k, v in pairs(pdata.sc) do
local info = {}
for _, i in ipairs(v) do
table.insert(info, { cardId = i, fromArea = Card.DrawPile })
end
local move = {
moveInfo = info,
to = id,
toArea = Card.PlayerSpecial,
specialName = k,
specialVisible = Self.id == id,
}
table.insert(card_moves, move)
end
if #card_moves > 0 then
-- TODO: visibility
fk.client_callback["MoveCards"](card_moves)
end
f = fk.client_callback["SetPlayerMark"]
for k, v in pairs(pdata.m) do
f{ id, k, v }
end
f = fk.client_callback["AddSkill"]
for _, v in pairs(pdata.s) do
f{ id, v }
end
f = fk.client_callback["AddCardUseHistory"]
for k, v in pairs(pdata.ch) do
if v[1] > 0 then
f{ k, v[1] }
end
end
f = fk.client_callback["SetSkillUseHistory"]
for k, v in pairs(pdata.sh) do
if v[4] > 0 then
f{ id, k, v[1], 1 }
f{ id, k, v[2], 2 }
f{ id, k, v[3], 3 }
f{ id, k, v[4], 4 }
end
end
end
local function loadRoomSummary(data)
local players = data.p
fk.client_callback["StartGame"]("")
for _, pid in ipairs(data.circle) do
if pid ~= data.you then
fk.client_callback["AddPlayer"](players[tostring(pid)].d)
end
end
fk.client_callback["ArrangeSeats"](data.circle)
for _, d in ipairs(data.pc) do
local cd = Fk:cloneCard(table.unpack(d))
Fk:_addPrintedCard(cd)
end
for cid, marks in pairs(data.cm) do
for k, v in pairs(marks) do
Fk:getCardById(tonumber(cid)):setMark(k, v)
ClientInstance:notifyUI("UpdateCard", cid)
end
end
for k, v in pairs(data.b) do
fk.client_callback["SetBanner"]{ k, v }
end
for _, pid in ipairs(data.circle) do
local pdata = data.p[tostring(pid)]
loadPlayerSummary(pdata)
end
ClientInstance:notifyUI("UpdateDrawPile", data.dp)
ClientInstance:notifyUI("UpdateRoundNum", data.rnd)
end
fk.client_callback["Reconnect"] = function(data)
local players = data.p
local setup_data = players[tostring(data.you)].d
setup(setup_data[1], setup_data[2], setup_data[3])
fk.client_callback["AddTotalGameTime"]{ setup_data[1], setup_data[5] }
local enter_room_data = data.d
table.insert(enter_room_data, 1, #data.circle)
fk.client_callback["EnterLobby"]("")
fk.client_callback["EnterRoom"](enter_room_data)
loadRoomSummary(data)
end
fk.client_callback["Observe"] = function(data)
local players = data.p
local setup_data = players[tostring(data.you)].d
setup(setup_data[1], setup_data[2], setup_data[3])
local enter_room_data = data.d
table.insert(enter_room_data, 1, #data.circle)
fk.client_callback["EnterRoom"](enter_room_data)
fk.client_callback["StartGame"]("")
loadRoomSummary(data)
end
-- Create ClientInstance (used by Lua) -- Create ClientInstance (used by Lua)
ClientInstance = Client:new() ClientInstance = Client:new()
dofile "lua/client/client_util.lua" dofile "lua/client/client_util.lua"

View File

@ -9,7 +9,7 @@ end
function GetGeneralData(name) function GetGeneralData(name)
local general = Fk.generals[name] local general = Fk.generals[name]
if general == nil then general = Fk.generals["diaochan"] end if general == nil then general = Fk.generals["diaochan"] end
return json.encode { return {
package = general.package.name, package = general.package.name,
extension = general.package.extensionName, extension = general.package.extensionName,
kingdom = general.kingdom, kingdom = general.kingdom,
@ -65,16 +65,16 @@ function GetGeneralDetail(name)
table.insertIfNeed(ret.companions, g.name) table.insertIfNeed(ret.companions, g.name)
end end
end end
return json.encode(ret) return ret
end end
function GetSameGenerals(name) function GetSameGenerals(name)
return json.encode(Fk:getSameGenerals(name)) return Fk:getSameGenerals(name)
end end
function IsCompanionWith(general, general2) function IsCompanionWith(general, general2)
local _general, _general2 = Fk.generals[general], Fk.generals[general2] local _general, _general2 = Fk.generals[general], Fk.generals[general2]
return json.encode(_general:isCompanionWith(_general2)) return _general:isCompanionWith(_general2)
end end
local cardSubtypeStrings = { local cardSubtypeStrings = {
@ -89,7 +89,7 @@ local cardSubtypeStrings = {
function GetCardData(id, virtualCardForm) function GetCardData(id, virtualCardForm)
local card = Fk:getCardById(id) local card = Fk:getCardById(id)
if card == nil then return json.encode{ if card == nil then return {
cid = id, cid = id,
known = false known = false
} end } end
@ -124,7 +124,7 @@ function GetCardData(id, virtualCardForm)
ret.subtype = cardSubtypeStrings[virtualCard.sub_type] ret.subtype = cardSubtypeStrings[virtualCard.sub_type]
end end
end end
return json.encode(ret) return ret
end end
function GetCardExtensionByName(cardName) function GetCardExtensionByName(cardName)
@ -133,11 +133,11 @@ function GetCardExtensionByName(cardName)
end end
function GetAllMods() function GetAllMods()
return json.encode(Fk.extensions) return Fk.extensions
end end
function GetAllModNames() function GetAllModNames()
return json.encode(Fk.extension_names) return Fk.extension_names
end end
function GetAllGeneralPack() function GetAllGeneralPack()
@ -147,28 +147,28 @@ function GetAllGeneralPack()
table.insert(ret, name) table.insert(ret, name)
end end
end end
return json.encode(ret) return ret
end end
function GetGenerals(pack_name) function GetGenerals(pack_name)
if not Fk.packages[pack_name] then return "[]" end if not Fk.packages[pack_name] then return {} end
local ret = {} local ret = {}
for _, g in ipairs(Fk.packages[pack_name].generals) do for _, g in ipairs(Fk.packages[pack_name].generals) do
if not g.total_hidden then if not g.total_hidden then
table.insert(ret, g.name) table.insert(ret, g.name)
end end
end end
return json.encode(ret) return ret
end end
function SearchAllGenerals(word) function SearchAllGenerals(word)
local ret = {} local ret = {}
for _, name in ipairs(Fk.package_names) do for _, name in ipairs(Fk.package_names) do
if Fk.packages[name].type == Package.GeneralPack then if Fk.packages[name].type == Package.GeneralPack then
table.insertTable(ret, json.decode(SearchGenerals(name, word))) table.insertTable(ret, SearchGenerals(name, word))
end end
end end
return json.encode(ret) return ret
end end
function SearchGenerals(pack_name, word) function SearchGenerals(pack_name, word)
@ -179,7 +179,7 @@ function SearchGenerals(pack_name, word)
table.insert(ret, g.name) table.insert(ret, g.name)
end end
end end
return json.encode(ret) return ret
end end
function UpdatePackageEnable(pkg, enabled) function UpdatePackageEnable(pkg, enabled)
@ -220,7 +220,7 @@ function GetAllCardPack()
table.insert(ret, name) table.insert(ret, name)
end end
end end
return json.encode(ret) return ret
end end
function GetCards(pack_name) function GetCards(pack_name)
@ -228,7 +228,7 @@ function GetCards(pack_name)
for _, c in ipairs(Fk.packages[pack_name].cards) do for _, c in ipairs(Fk.packages[pack_name].cards) do
table.insert(ret, c.id) table.insert(ret, c.id)
end end
return json.encode(ret) return ret
end end
function GetCardSkill(cid) function GetCardSkill(cid)
@ -236,7 +236,7 @@ function GetCardSkill(cid)
end end
function GetCardSpecialSkills(cid) function GetCardSpecialSkills(cid)
return json.encode(Fk:getCardById(cid).special_skills or Util.DummyTable) return Fk:getCardById(cid).special_skills or Util.DummyTable
end end
function DistanceTo(from, to) function DistanceTo(from, to)
@ -246,21 +246,21 @@ function DistanceTo(from, to)
end end
function GetPile(id, name) function GetPile(id, name)
return json.encode(ClientInstance:getPlayerById(id):getPile(name) or {}) return ClientInstance:getPlayerById(id):getPile(name) or Util.DummyTable
end end
function GetAllPiles(id) function GetAllPiles(id)
return json.encode(ClientInstance:getPlayerById(id).special_cards or Util.DummyTable) return ClientInstance:getPlayerById(id).special_cards or Util.DummyTable
end end
function GetPlayerSkills(id) function GetPlayerSkills(id)
local p = ClientInstance:getPlayerById(id) local p = ClientInstance:getPlayerById(id)
return json.encode(table.map(p.player_skills, function(s) return table.map(p.player_skills, function(s)
return s.visible and { return s.visible and {
name = s.name, name = s.name,
description = Fk:getDescription(s.name), description = Fk:getDescription(s.name),
} or nil } or nil
end)) end)
end end
---@param card string | integer ---@param card string | integer
@ -278,11 +278,11 @@ function CanUseCard(card, player, extra_data_str)
if skill:isInstanceOf(ViewAsSkill) then if skill:isInstanceOf(ViewAsSkill) then
c = skill:viewAs(selected_cards) c = skill:viewAs(selected_cards)
if not c then if not c then
return "false" return false
end end
else else
-- ActiveSkill should return true here -- ActiveSkill should return true here
return "true" return true
end end
end end
@ -294,13 +294,13 @@ function CanUseCard(card, player, extra_data_str)
if min_target > 0 then if min_target > 0 then
for _, p in ipairs(ClientInstance.players) do for _, p in ipairs(ClientInstance.players) do
if c.skill:targetFilter(p.id, {}, {}, c, extra_data) then if c.skill:targetFilter(p.id, {}, {}, c, extra_data) then
return "true" return true
end end
end end
return "false" return false
end end
end end
return json.encode(ret) return ret
end end
function CardProhibitedUse(card) function CardProhibitedUse(card)
@ -317,11 +317,11 @@ function CardProhibitedUse(card)
end end
end end
if c == nil then if c == nil then
return "true" return true
else else
ret = Self:prohibitUse(c) ret = Self:prohibitUse(c)
end end
return json.encode(ret) return ret
end end
---@param card string | integer ---@param card string | integer
@ -331,7 +331,7 @@ end
function CanUseCardToTarget(card, to_select, selected, extra_data_str) function CanUseCardToTarget(card, to_select, selected, extra_data_str)
local extra_data = extra_data_str == "" and nil or json.decode(extra_data_str) local extra_data = extra_data_str == "" and nil or json.decode(extra_data_str)
if ClientInstance:getPlayerById(to_select).dead then if ClientInstance:getPlayerById(to_select).dead then
return "false" return false
end end
local c ---@type Card local c ---@type Card
local selected_cards local selected_cards
@ -345,7 +345,7 @@ function CanUseCardToTarget(card, to_select, selected, extra_data_str)
local ret = c.skill:targetFilter(to_select, selected, selected_cards, c, extra_data) local ret = c.skill:targetFilter(to_select, selected, selected_cards, c, extra_data)
ret = ret and not Self:isProhibited(Fk:currentRoom():getPlayerById(to_select), c) ret = ret and not Self:isProhibited(Fk:currentRoom():getPlayerById(to_select), c)
return json.encode(ret) return ret
end end
---@param card string | integer ---@param card string | integer
@ -362,7 +362,7 @@ function CanSelectCardForSkill(card, to_select, selected_targets)
end end
local ret = c.skill:cardFilter(to_select, selected_cards, selected_targets) local ret = c.skill:cardFilter(to_select, selected_cards, selected_targets)
return json.encode(ret) return ret
end end
---@param card string | integer ---@param card string | integer
@ -379,14 +379,14 @@ function CardFeasible(card, selected_targets)
end end
local ret = c.skill:feasible(selected_targets, selected_cards, Self, c) local ret = c.skill:feasible(selected_targets, selected_cards, Self, c)
return json.encode(ret) return ret
end end
-- Handle skills -- Handle skills
function GetSkillData(skill_name) function GetSkillData(skill_name)
local skill = Fk.skills[skill_name] local skill = Fk.skills[skill_name]
if not skill then return "null" end if not skill then return nil end
local freq = "notactive" local freq = "notactive"
if skill:isInstanceOf(ActiveSkill) or skill:isInstanceOf(ViewAsSkill) then if skill:isInstanceOf(ActiveSkill) or skill:isInstanceOf(ViewAsSkill) then
freq = "active" freq = "active"
@ -399,7 +399,7 @@ function GetSkillData(skill_name)
elseif skill.frequency == Skill.Quest then elseif skill.frequency == Skill.Quest then
frequency = "quest" frequency = "quest"
end end
return json.encode{ return {
skill = Fk:translate(skill_name), skill = Fk:translate(skill_name),
orig_skill = skill_name, orig_skill = skill_name,
extension = skill.package.extensionName, extension = skill.package.extensionName,
@ -439,7 +439,7 @@ function ActiveCanUse(skill_name, extra_data_str)
end end
end end
end end
return json.encode(ret) return ret
end end
function ActiveSkillPrompt(skill_name, selected, selected_targets) function ActiveSkillPrompt(skill_name, selected, selected_targets)
@ -452,7 +452,7 @@ function ActiveSkillPrompt(skill_name, selected, selected_targets)
ret = skill.prompt ret = skill.prompt
end end
end end
return json.encode(ret or "") return ret or ""
end end
function ActiveCardFilter(skill_name, to_select, selected, selected_targets) function ActiveCardFilter(skill_name, to_select, selected, selected_targets)
@ -465,7 +465,7 @@ function ActiveCardFilter(skill_name, to_select, selected, selected_targets)
ret = skill:cardFilter(to_select, selected) ret = skill:cardFilter(to_select, selected)
end end
end end
return json.encode(ret) return ret
end end
function ActiveTargetFilter(skill_name, to_select, selected, selected_cards, extra_data) function ActiveTargetFilter(skill_name, to_select, selected, selected_cards, extra_data)
@ -482,7 +482,7 @@ function ActiveTargetFilter(skill_name, to_select, selected, selected_cards, ext
end end
end end
end end
return json.encode(ret) return ret
end end
function ActiveFeasible(skill_name, selected, selected_cards) function ActiveFeasible(skill_name, selected, selected_cards)
@ -498,7 +498,7 @@ function ActiveFeasible(skill_name, selected, selected_cards)
end end
end end
end end
return json.encode(ret) return ret
end end
function CanViewAs(skill_name, card_ids) function CanViewAs(skill_name, card_ids)
@ -511,7 +511,7 @@ function CanViewAs(skill_name, card_ids)
ret = true ret = true
end end
end end
return json.encode(ret) return ret
end end
-- card_name may be id, name of card, or json string -- card_name may be id, name of card, or json string
@ -532,12 +532,12 @@ function CardFitPattern(card_name, pattern)
ret = exp:match(c) ret = exp:match(c)
end end
else else
return "true" return true
end end
else else
ret = exp:matchExp(card_name) ret = exp:matchExp(card_name)
end end
return json.encode(ret) return ret
end end
function SkillFitPattern(skill_name, pattern) function SkillFitPattern(skill_name, pattern)
@ -547,7 +547,7 @@ function SkillFitPattern(skill_name, pattern)
local exp = Exppattern:Parse(pattern) local exp = Exppattern:Parse(pattern)
ret = exp:matchExp(skill.pattern) ret = exp:matchExp(skill.pattern)
end end
return json.encode(ret) return ret
end end
function CardProhibitedResponse(card) function CardProhibitedResponse(card)
@ -564,11 +564,11 @@ function CardProhibitedResponse(card)
end end
end end
if c == nil then if c == nil then
return "true" return true
else else
ret = Self:prohibitResponse(c) ret = Self:prohibitResponse(c)
end end
return json.encode(ret) return ret
end end
function SkillCanResponse(skill_name, cardResponsing) function SkillCanResponse(skill_name, cardResponsing)
@ -577,13 +577,13 @@ function SkillCanResponse(skill_name, cardResponsing)
if skill and skill:isInstanceOf(ViewAsSkill) then if skill and skill:isInstanceOf(ViewAsSkill) then
ret = skill:enabledAtResponse(Self, cardResponsing) ret = skill:enabledAtResponse(Self, cardResponsing)
end end
return json.encode(ret) return ret
end end
function GetVirtualEquip(player, cid) function GetVirtualEquip(player, cid)
local c = ClientInstance:getPlayerById(player):getVirualEquip(cid) local c = ClientInstance:getPlayerById(player):getVirualEquip(cid)
if not c then return "null" end if not c then return nil end
return json.encode{ return {
name = c.name, name = c.name,
cid = c.subcards[1], cid = c.subcards[1],
} }
@ -598,7 +598,7 @@ function GetExpandPileOfSkill(skillName)
end end
if type(e) == "table" then if type(e) == "table" then
return json.encode(e) return e
else else
return e or "" return e or ""
end end
@ -615,15 +615,15 @@ function GetGameModes()
}) })
end end
table.sort(ret, function(a, b) return a.name > b.name end) table.sort(ret, function(a, b) return a.name > b.name end)
return json.encode(ret) return ret
end end
function GetInteractionOfSkill(skill_name) function GetInteractionOfSkill(skill_name)
local skill = Fk.skills[skill_name] local skill = Fk.skills[skill_name]
if skill and skill.interaction then if skill and skill.interaction then
return json.encode(skill:interaction()) return skill:interaction()
end end
return "null" return nil
end end
function SetInteractionDataOfSkill(skill_name, data) function SetInteractionDataOfSkill(skill_name, data)
@ -642,13 +642,13 @@ end
function GetPlayerHandcards(pid) function GetPlayerHandcards(pid)
local c = ClientInstance local c = ClientInstance
local p = c:getPlayerById(pid) local p = c:getPlayerById(pid)
return p and json.encode(p.player_cards[Player.Hand]) or "" return p and p.player_cards[Player.Hand] or ""
end end
function GetPlayerEquips(pid) function GetPlayerEquips(pid)
local c = ClientInstance local c = ClientInstance
local p = c:getPlayerById(pid) local p = c:getPlayerById(pid)
return json.encode(p.player_cards[Player.Equip]) return p.player_cards[Player.Equip]
end end
function ResetClientLua() function ResetClientLua()
@ -673,20 +673,20 @@ function ResetAddPlayer(j)
end end
function GetRoomConfig() function GetRoomConfig()
return json.encode(ClientInstance.room_settings) return ClientInstance.room_settings
end end
function GetPlayerGameData(pid) function GetPlayerGameData(pid)
local c = ClientInstance local c = ClientInstance
local p = c:getPlayerById(pid) local p = c:getPlayerById(pid)
if not p then return "[0, 0, 0, 0]" end if not p then return {0, 0, 0, 0} end
local raw = p.player:getGameData() local raw = p.player:getGameData()
local ret = {} local ret = {}
for _, i in fk.qlist(raw) do for _, i in fk.qlist(raw) do
table.insert(ret, i) table.insert(ret, i)
end end
table.insert(ret, p.player:getTotalGameTime()) table.insert(ret, p.player:getTotalGameTime())
return json.encode(ret) return ret
end end
function SetPlayerGameData(pid, data) function SetPlayerGameData(pid, data)
@ -708,7 +708,7 @@ end
function CheckSurrenderAvailable(playedTime) function CheckSurrenderAvailable(playedTime)
local curMode = ClientInstance.room_settings.gameMode local curMode = ClientInstance.room_settings.gameMode
return json.encode(Fk.game_modes[curMode]:surrenderFunc(playedTime)) return Fk.game_modes[curMode]:surrenderFunc(playedTime)
end end
function SaveRecord() function SaveRecord()
@ -756,22 +756,22 @@ end
function PoxiFilter(poxi_type, to_select, selected, data, extra_data) function PoxiFilter(poxi_type, to_select, selected, data, extra_data)
local poxi = Fk.poxi_methods[poxi_type] local poxi = Fk.poxi_methods[poxi_type]
if not poxi then return "false" end if not poxi then return false end
return json.encode(poxi.card_filter(to_select, selected, data, extra_data)) return poxi.card_filter(to_select, selected, data, extra_data)
end end
function PoxiFeasible(poxi_type, selected, data, extra_data) function PoxiFeasible(poxi_type, selected, data, extra_data)
local poxi = Fk.poxi_methods[poxi_type] local poxi = Fk.poxi_methods[poxi_type]
if not poxi then return "false" end if not poxi then return false end
return json.encode(poxi.feasible(selected, data, extra_data)) return poxi.feasible(selected, data, extra_data)
end end
function GetQmlMark(mtype, name, value, p) function GetQmlMark(mtype, name, value, p)
local spec = Fk.qml_marks[mtype] local spec = Fk.qml_marks[mtype]
if not spec then return "{}" end if not spec then return {} end
p = ClientInstance:getPlayerById(p) p = ClientInstance:getPlayerById(p)
value = json.decode(value) value = json.decode(value)
return json.encode { return {
qml_path = type(spec.qml_path) == "function" and spec.qml_path(name, value, p) or spec.qml_path, qml_path = type(spec.qml_path) == "function" and spec.qml_path(name, value, p) or spec.qml_path,
text = spec.how_to_show(name, value, p) text = spec.how_to_show(name, value, p)
} }
@ -781,7 +781,7 @@ function GetMiniGame(gtype, p, data)
local spec = Fk.mini_games[gtype] local spec = Fk.mini_games[gtype]
p = ClientInstance:getPlayerById(p) p = ClientInstance:getPlayerById(p)
data = json.decode(data) data = json.decode(data)
return json.encode { return {
qml_path = type(spec.qml_path) == "function" and spec.qml_path(p, data) or spec.qml_path, qml_path = type(spec.qml_path) == "function" and spec.qml_path(p, data) or spec.qml_path,
} }
end end

View File

@ -38,7 +38,7 @@ function AbstractRoom:getPlayerById(id) end
--- 获取一张牌所处的区域。 --- 获取一张牌所处的区域。
---@param cardId integer | Card @ 要获得区域的那张牌可以是Card或者一个id ---@param cardId integer | Card @ 要获得区域的那张牌可以是Card或者一个id
---@return CardArea @ 这张牌的区域 ---@return CardArea @ 这张牌的区域
function AbstractRoom:getCardArea(cardId) end function AbstractRoom:getCardArea(cardId) return Card.Unknown end
function AbstractRoom:setBanner(name, value) function AbstractRoom:setBanner(name, value)
if value == 0 then value = nil end if value == 0 then value = nil end

View File

@ -24,13 +24,13 @@
]]-- ]]--
---@class Matcher ---@class Matcher
---@field public trueName string[] ---@field public trueName? string[]
---@field public number integer[] ---@field public number? integer[]
---@field public suit string[] ---@field public suit? string[]
---@field public place string[] ---@field public place? string[]
---@field public name string[] ---@field public name? string[]
---@field public cardType string[] ---@field public cardType? string[]
---@field public id integer[] ---@field public id? integer[]
-- v0.2.6改动: cardType会被解析为trueName数组和name数组而不是自己单独成立 -- v0.2.6改动: cardType会被解析为trueName数组和name数组而不是自己单独成立

View File

@ -449,11 +449,14 @@ end
-- override default string.len -- override default string.len
string.rawlen = string.len string.rawlen = string.len
---@param self string
---@diagnostic disable-next-line: duplicate-set-field ---@diagnostic disable-next-line: duplicate-set-field
function string:len() function string:len()
return utf8.len(self) return utf8.len(self)
end end
---@param self string
---@param delimiter string ---@param delimiter string
---@return string[] ---@return string[]
function string:split(delimiter) function string:split(delimiter)
@ -470,16 +473,20 @@ function string:split(delimiter)
return result return result
end end
---@param self string
function string:startsWith(start) function string:startsWith(start)
return self:sub(1, #start) == start return self:sub(1, #start) == start
end end
---@param self string
function string:endsWith(e) function string:endsWith(e)
return e == "" or self:sub(-#e) == e return e == "" or self:sub(-#e) == e
end end
FileIO = { FileIO = {
pwd = fk.QmlBackend_pwd, pwd = fk.QmlBackend_pwd,
---@return string[]
ls = function(filename) ls = function(filename)
if filename == nil then if filename == nil then
return fk.QmlBackend_ls(".") return fk.QmlBackend_ls(".")

View File

@ -11,7 +11,30 @@ package.path = package.path .. ";./lua/lib/?.lua"
class = require "middleclass" class = require "middleclass"
-- json: 提供json处理支持能解析JSON和生成JSON -- json: 提供json处理支持能解析JSON和生成JSON
json = require "json" -- 仍借助luajson处理简单类型。
local luajson = require "json"
json = {
encode = function(val, t)
if type(val) ~= "table" then return luajson.encode(val) end
t = t or 1 -- Compact
---@diagnostic disable-next-line
local doc = fk.QJsonDocument_fromVariant(val)
local ret = doc:toJson(t)
return ret
end,
decode = function(str)
if str == "null" then return nil end
local start = str:sub(1, 1)
if start ~= "[" and start ~= "{" then
return luajson.decode(str)
end
---@diagnostic disable-next-line
local doc = fk.QJsonDocument_fromJson(str)
local ret = doc:toVariant()
-- if ret == "" then ret = luajson.decode(str) end
return ret
end,
}
-- 初始化随机数种子 -- 初始化随机数种子
math.randomseed(os.time()) math.randomseed(os.time())

View File

@ -24,10 +24,6 @@ SPlayerList = {}
---@return integer microsecond ---@return integer microsecond
function fk:GetMicroSecond()end function fk:GetMicroSecond()end
--- construct a QList<ServerPlayer *>.
---@return fk.SPlayerList
function fk:SPlayerList()end
function fk.QmlBackend_pwd()end function fk.QmlBackend_pwd()end
---@return string[] ---@return string[]
@ -44,3 +40,5 @@ function fk.qCritical(msg) end
function fk.qInfo(msg) end function fk.qInfo(msg) end
function fk.qDebug(msg) end function fk.qDebug(msg) end
function fk.qWarning(msg) end function fk.qWarning(msg) end
fk.FK_VER = '0.0.0'

View File

@ -5,41 +5,21 @@
---@class fk.Player ---@class fk.Player
FPlayer = {} FPlayer = {}
---@return integer id
function FPlayer:getId()end function FPlayer:getId()end
---@return string name
function FPlayer:getScreenName()end function FPlayer:getScreenName()end
---@return string avatar
function FPlayer:getAvatar()end function FPlayer:getAvatar()end
---@class fk.ServerPlayer : fk.Player ---@class fk.ServerPlayer : fk.Player
FServerPlayer = {} FServerPlayer = {}
--- Send a request to client, and allow client to reply within *timeout* seconds.
---
--- *timeout* must not be negative or **nil**.
---@param command string
---@param jsonData string
---@param timeout integer
function FServerPlayer:doRequest(command,jsonData,timeout)end function FServerPlayer:doRequest(command,jsonData,timeout)end
--- Wait for at most *timeout* seconds for reply from client.
---
--- If *timeout* is negative or **nil**, the function will wait forever until get reply.
---@param timeout integer @ seconds to wait
---@return string @ JSON data
---@overload fun()
function FServerPlayer:waitForReply(timeout)end function FServerPlayer:waitForReply(timeout)end
--- Notice the client.
---@param command string
---@param jsonData string
function FServerPlayer:doNotify(command,jsonData)end function FServerPlayer:doNotify(command,jsonData)end
function FServerPlayer:setBusy(_) end function FServerPlayer:setBusy(_) end
function FServerPlayer:isBusy(_) end function FServerPlayer:isBusy(_) end
function FServerPlayer:setThinking(_) end function FServerPlayer:setThinking(_) end
function FServerPlayer:getState() end function FServerPlayer:getState() end
---@type any
fk.Self = nil

View File

@ -1 +1,14 @@
-- SPDX-License-Identifier: GPL-3.0-or-later -- SPDX-License-Identifier: GPL-3.0-or-later
---@diagnostic disable
---@class fk.Room
local Room = {}
function Room:getId() return 1 end
---@return fk.SPlayerList
function Room:getPlayers() end
---@return fk.SPlayerList
function Room:getObservers() end

View File

@ -1 +1,22 @@
-- SPDX-License-Identifier: GPL-3.0-or-later -- SPDX-License-Identifier: GPL-3.0-or-later
-- 暂且用来当client.lua用了别在意
---@class fk.Client
---@field callback fun(s: fk.Client, c: string, j: string, r: boolean)
local C = {}
function C:replyToServer(c, j) end
function C:notifyServer(c, j) end
function C:addPlayer(id, name, avatar) end
function C:removePlayer(id) end
function C:changeSelf(id) end
function C:saveRecord(j, fname) end
fk.ClientInstance = C
---@class fk.QmlBackend
local B = {}
function B:emitNotifyUI(c, j) end
fk.Backend = B

View File

@ -2,60 +2,8 @@
local function tellRoomToObserver(self, player) local function tellRoomToObserver(self, player)
local observee = self.players[1] local observee = self.players[1]
player:doNotify("Setup", json.encode{ local summary = self:getSummary(observee, true)
observee.id, player:doNotify("Observe", json.encode(summary))
observee._splayer:getScreenName(),
observee._splayer:getAvatar(),
})
player:doNotify("EnterRoom", json.encode{
#self.players, self.timeout, self.settings
})
player:doNotify("StartGame", "")
-- send player data
for _, p in ipairs(self:getOtherPlayers(observee, false, true)) do
player:doNotify("AddPlayer", json.encode{
p.id,
p._splayer:getScreenName(),
p._splayer:getAvatar(),
false,
p._splayer:getTotalGameTime(),
})
end
local player_circle = {}
for i = 1, #self.players do
table.insert(player_circle, self.players[i].id)
end
player:doNotify("ArrangeSeats", json.encode(player_circle))
-- send printed_cards
for i = -2, -math.huge, -1 do
local c = Fk.printed_cards[i]
if not c then break end
player:doNotify("PrintCard", json.encode{ c.name, c.suit, c.number })
end
-- send card marks
for id, marks in pairs(self.card_marks) do
for k, v in pairs(marks) do
player:doNotify("SetCardMark", json.encode{ id, k, v })
end
end
-- send banners
for k, v in pairs(self.banners) do
player:doNotify("SetBanner", json.encode{ k, v })
end
for _, p in ipairs(self.players) do
self:notifyProperty(player, p, "general")
self:notifyProperty(player, p, "deputyGeneral")
p:marshal(player, true)
end
player:doNotify("UpdateDrawPile", #self.draw_pile)
player:doNotify("UpdateRoundNum", self:getTag("RoundCount") or 0)
table.insert(self.observers, {observee.id, player, player:getId()}) table.insert(self.observers, {observee.id, player, player:getId()})
end end

View File

@ -655,6 +655,45 @@ function Room:changeKingdom(player, kingdom, sendLog)
}) })
end end
--- 房间信息摘要,返回房间的大致信息
--- 用于旁观和重连但也可用于debug
function Room:getSummary(player, observe)
local printed_cards = {}
for i = -2, -math.huge, -1 do
local c = Fk.printed_cards[i]
if not c then break end
table.insert(printed_cards, { c.name, c.suit, c.number })
end
local players = {}
for _, p in ipairs(self.players) do
players[tostring(p.id)] = p:getSummary(player, observe)
end
local cmarks = {}
for k, v in pairs(self.card_marks) do
cmarks[tostring(k)] = v
end
return {
you = player.id or player:getId(),
-- data for EnterRoom
d = {
-- #self.players, 留给客户端自己思考
self.timeout,
self.settings,
},
pc = printed_cards,
cm = cmarks,
b = self.banners,
circle = table.map(self.players, Util.IdMapper),
p = players,
rnd = self:getTag("RoundCount") or 0,
dp = #self.draw_pile,
}
end
------------------------------------------------------------------------ ------------------------------------------------------------------------
-- 网络通信有关 -- 网络通信有关
------------------------------------------------------------------------ ------------------------------------------------------------------------

View File

@ -192,199 +192,95 @@ function ServerPlayer:waitForReply(timeout)
return result return result
end end
local function assign(t1, t2, k)
t1[k] = t2[k]
end
-- 获取摘要信息。供重连/旁观使用
-- 根据参数,返回一个大表保存自己的信息,客户端自行分析
---@param player ServerPlayer ---@param player ServerPlayer
---@param observe? boolean ---@param observe? boolean
function ServerPlayer:marshal(player, observe) function ServerPlayer:getSummary(player, observe)
local room = self.room local room = self.room
if not room.game_started then if not room.game_started then
local ret = { p = {} }
-- If game does not starts, that mean we are entering room that -- If game does not starts, that mean we are entering room that
-- all players are choosing their generals. -- all players are choosing their generals.
-- Note that when we are in this function, the main thread must be -- Note that when we are in this function, the main thread must be
-- calling delay() or waiting for reply. -- calling delay() or waiting for reply.
if self.role_shown then if self.role_shown then
room:notifyProperty(player, self, "role") -- room:notifyProperty(player, self, "role")
ret.p.general = self.general
ret.p.deputyGeneral = self.deputyGeneral
ret.p.role = self.role
end end
return return ret
end end
room:notifyProperty(player, self, "maxHp") local properties = {}
room:notifyProperty(player, self, "hp")
room:notifyProperty(player, self, "shield") assign(properties, self, "general")
room:notifyProperty(player, self, "gender") assign(properties, self, "deputyGeneral")
room:notifyProperty(player, self, "kingdom") assign(properties, self, "maxHp")
assign(properties, self, "hp")
assign(properties, self, "shield")
assign(properties, self, "gender")
assign(properties, self, "kingdom")
if self.dead then if self.dead then
room:notifyProperty(player, self, "dead") assign(properties, self, "dead")
room:notifyProperty(player, self, self.rest > 0 and "rest" or "role") assign(properties, self, self.rest > 0 and "rest" or "role")
else else
room:notifyProperty(player, self, "seat") assign(properties, self, "seat")
room:notifyProperty(player, self, "phase") assign(properties, self, "phase")
end end
if not self.faceup then if not self.faceup then
room:notifyProperty(player, self, "faceup") assign(properties, self, "faceup")
end end
if self.chained then if self.chained then
room:notifyProperty(player, self, "chained") assign(properties, self, "chained")
end
local card_moves = {}
if #self.player_cards[Player.Hand] ~= 0 then
local info = {}
for _, i in ipairs(self.player_cards[Player.Hand]) do
table.insert(info, { cardId = i, fromArea = Card.DrawPile })
end
local move = {
moveInfo = info,
to = self.id,
toArea = Card.PlayerHand
}
table.insert(card_moves, move)
end
if #self.player_cards[Player.Equip] ~= 0 then
local info = {}
for _, i in ipairs(self.player_cards[Player.Equip]) do
table.insert(info, { cardId = i, fromArea = Card.DrawPile })
end
local move = {
moveInfo = info,
to = self.id,
toArea = Card.PlayerEquip
}
table.insert(card_moves, move)
end
if #self.player_cards[Player.Judge] ~= 0 then
local info = {}
for _, i in ipairs(self.player_cards[Player.Judge]) do
table.insert(info, { cardId = i, fromArea = Card.DrawPile })
end
local move = {
moveInfo = info,
to = self.id,
toArea = Card.PlayerJudge
}
table.insert(card_moves, move)
end
for k, v in pairs(self.special_cards) do
local info = {}
for _, i in ipairs(v) do
table.insert(info, { cardId = i, fromArea = Card.DrawPile })
end
local move = {
moveInfo = info,
to = self.id,
toArea = Card.PlayerSpecial,
specialName = k,
specialVisible = self == player,
}
table.insert(card_moves, move)
end
if #card_moves > 0 then
room:notifyMoveCards({ player }, card_moves, observe and self.seat == 1)
end
for k, v in pairs(self.mark) do
player:doNotify("SetPlayerMark", json.encode{self.id, k, v})
end
for _, s in ipairs(self.player_skills) do
player:doNotify("AddSkill", json.encode{self.id, s.name})
end
for k, v in pairs(self.cardUsedHistory) do
if v[1] > 0 then
player:doNotify("AddCardUseHistory", json.encode{k, v[1]})
end
end
for k, v in pairs(self.skillUsedHistory) do
if v[4] > 0 then
player:doNotify("SetSkillUseHistory", json.encode{self.id, k, v[1], 1})
player:doNotify("SetSkillUseHistory", json.encode{self.id, k, v[2], 2})
player:doNotify("SetSkillUseHistory", json.encode{self.id, k, v[3], 3})
player:doNotify("SetSkillUseHistory", json.encode{self.id, k, v[4], 4})
end
end end
if self.role_shown then if self.role_shown then
room:notifyProperty(player, self, "role") assign(properties, self, "role")
end end
if #self.sealedSlots > 0 then if #self.sealedSlots > 0 then
room:notifyProperty(player, self, "sealedSlots") assign(properties, self, "sealedSlots")
end end
local sp = self._splayer
return {
-- data for Setup/AddPlayer
d = {
self.id,
sp:getScreenName(),
sp:getAvatar(),
false,
sp:getTotalGameTime(),
},
p = properties,
ch = self.cardUsedHistory,
sh = self.skillUsedHistory,
m = self.mark,
s = table.map(self.player_skills, Util.NameMapper),
c = self.player_cards,
sc = self.special_cards,
}
end end
function ServerPlayer:reconnect() function ServerPlayer:reconnect()
local room = self.room local room = self.room
self.serverplayer:setState(fk.Player_Online) self.serverplayer:setState(fk.Player_Online)
self:doNotify("Setup", json.encode{ local summary = room:getSummary(self, false)
self.id, self:doNotify("Reconnect", json.encode(summary))
self._splayer:getScreenName(),
self._splayer:getAvatar(),
})
self:doNotify("AddTotalGameTime", json.encode {
self.id,
self._splayer:getTotalGameTime(),
})
self:doNotify("EnterLobby", "")
self:doNotify("EnterRoom", json.encode{
#room.players, room.timeout, room.settings,
})
self:doNotify("StartGame", "")
room:notifyProperty(self, self, "role") room:notifyProperty(self, self, "role")
-- send player data
for _, p in ipairs(room:getOtherPlayers(self, false, true)) do
self:doNotify("AddPlayer", json.encode{
p.id,
p._splayer:getScreenName(),
p._splayer:getAvatar(),
false,
p._splayer:getTotalGameTime(),
})
end
self:doNotify("RoomOwner", json.encode{ room.room:getOwner():getId() }) self:doNotify("RoomOwner", json.encode{ room.room:getOwner():getId() })
local player_circle = {}
for i = 1, #room.players do
table.insert(player_circle, room.players[i].id)
end
self:doNotify("ArrangeSeats", json.encode(player_circle))
-- send printed_cards
for i = -2, -math.huge, -1 do
local c = Fk.printed_cards[i]
if not c then break end
self:doNotify("PrintCard", json.encode{ c.name, c.suit, c.number })
end
-- send card marks
for id, marks in pairs(room.card_marks) do
for k, v in pairs(marks) do
self:doNotify("SetCardMark", json.encode{ id, k, v })
end
end
-- send banners
for k, v in pairs(room.banners) do
self:doNotify("SetBanner", json.encode{ k, v })
end
for _, p in ipairs(room.players) do
room:notifyProperty(self, p, "general")
room:notifyProperty(self, p, "deputyGeneral")
p:marshal(self)
end
self:doNotify("UpdateDrawPile", #room.draw_pile)
self:doNotify("UpdateRoundNum", room:getTag("RoundCount") or 0)
-- send fake skills -- send fake skills
for _, s in ipairs(self._manually_fake_skills) do for _, s in ipairs(self._manually_fake_skills) do
self:doNotify("AddSkill", json.encode{ self.id, s.name, true }) self:doNotify("AddSkill", json.encode{ self.id, s.name, true })

View File

@ -58,21 +58,6 @@ elseif (ANDROID)
QT_ANDROID_EXTRA_LIBS "${LUA_LIB};${SQLITE3_LIB};${CRYPTO_LIB};${SSL_LIB};${SSH_LIB};${GIT_LIB}" QT_ANDROID_EXTRA_LIBS "${LUA_LIB};${SQLITE3_LIB};${CRYPTO_LIB};${SSL_LIB};${SSH_LIB};${GIT_LIB}"
) )
list(REMOVE_ITEM QT_LIB Qt6::QuickControls2) list(REMOVE_ITEM QT_LIB Qt6::QuickControls2)
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(LUA_LIB ${PROJECT_SOURCE_DIR}/lib/wasm/liblua.a)
# set(CRYPTO_LIB ${PROJECT_SOURCE_DIR}/lib/wasm/libcrypto.a)
# set other libs by yourself
set(IDBFS_LIB idbfs.js)
include(${FK_WASM_TOOLCHAIN})
else () else ()
set(LUA_LIB lua5.4) set(LUA_LIB lua5.4)
set(SQLITE3_LIB sqlite3) set(SQLITE3_LIB sqlite3)

0
src/applink.c Executable file → Normal file
View File

View File

@ -95,7 +95,7 @@ void PackMan::loadSummary(const QString &jsonData, bool useThread) {
connect(thread, &QThread::finished, [=]() { connect(thread, &QThread::finished, [=]() {
thread->deleteLater(); thread->deleteLater();
#ifndef FK_SERVER_ONLY #ifndef FK_SERVER_ONLY
Backend->emitNotifyUI("DownloadComplete", ""); Backend->notifyUI("DownloadComplete", "");
#endif #endif
}); });
} else { } else {
@ -135,7 +135,7 @@ void PackMan::downloadNewPack(const QString &url, bool useThread) {
connect(thread, &QThread::finished, [=]() { connect(thread, &QThread::finished, [=]() {
thread->deleteLater(); thread->deleteLater();
#ifndef FK_SERVER_ONLY #ifndef FK_SERVER_ONLY
Backend->emitNotifyUI("DownloadComplete", ""); Backend->notifyUI("DownloadComplete", "");
#endif #endif
}); });
} else { } else {
@ -252,14 +252,14 @@ static int transfer_progress_cb(const git_indexer_progress *stats,
auto msg = QString("Resolving deltas %1/%2") auto msg = QString("Resolving deltas %1/%2")
.arg(stats->indexed_deltas) .arg(stats->indexed_deltas)
.arg(stats->total_deltas); .arg(stats->total_deltas);
Backend->emitNotifyUI("UpdateBusyText", msg); Backend->notifyUI("UpdateBusyText", msg);
} else if (stats->total_objects > 0) { } else if (stats->total_objects > 0) {
auto msg = QString("Received %1/%2 objects (%3) in %4 KiB") auto msg = QString("Received %1/%2 objects (%3) in %4 KiB")
.arg(stats->received_objects) .arg(stats->received_objects)
.arg(stats->total_objects) .arg(stats->total_objects)
.arg(stats->indexed_objects) .arg(stats->indexed_objects)
.arg(stats->received_bytes / 1024); .arg(stats->received_bytes / 1024);
Backend->emitNotifyUI("UpdateBusyText", msg); Backend->notifyUI("UpdateBusyText", msg);
} }
#endif #endif
} }

View File

@ -4,8 +4,11 @@
#define _PACKMAN_H #define _PACKMAN_H
#include <qtmetamacros.h> #include <qtmetamacros.h>
// 管理拓展包所需的类本质上是libgit2接口的再封装。
class PackMan : public QObject { class PackMan : public QObject {
Q_OBJECT Q_OBJECT
public: public:
PackMan(QObject *parent = nullptr); PackMan(QObject *parent = nullptr);
~PackMan(); ~PackMan();
@ -20,6 +23,7 @@ public:
Q_INVOKABLE void upgradePack(const QString &pack); Q_INVOKABLE void upgradePack(const QString &pack);
Q_INVOKABLE void removePack(const QString &pack); Q_INVOKABLE void removePack(const QString &pack);
Q_INVOKABLE QString listPackages(); Q_INVOKABLE QString listPackages();
private: private:
sqlite3 *db; sqlite3 *db;

View File

@ -5,11 +5,7 @@
using namespace fkShell; using namespace fkShell;
#include "packman.h" #include "packman.h"
#ifndef Q_OS_WASM
#include "server.h" #include "server.h"
#else
#include <emscripten.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"
@ -29,7 +25,7 @@ using namespace fkShell;
#include "qmlbackend.h" #include "qmlbackend.h"
#endif #endif
#if defined(Q_OS_ANDROID) || defined(Q_OS_WASM) #if defined(Q_OS_ANDROID)
static bool copyPath(const QString &srcFilePath, const QString &tgtFilePath) { static bool copyPath(const QString &srcFilePath, const QString &tgtFilePath) {
QFileInfo srcFileInfo(srcFilePath); QFileInfo srcFileInfo(srcFilePath);
if (srcFileInfo.isDir()) { if (srcFileInfo.isDir()) {
@ -196,7 +192,6 @@ int main(int argc, char *argv[]) {
} }
} }
#ifndef FK_CLIENT_ONLY
// 分析命令行,如果有 -s 或者 --server 就在命令行直接开服务器 // 分析命令行,如果有 -s 或者 --server 就在命令行直接开服务器
QCommandLineParser parser; QCommandLineParser parser;
parser.setApplicationDescription("FreeKill server"); parser.setApplicationDescription("FreeKill server");
@ -244,7 +239,6 @@ int main(int argc, char *argv[]) {
} }
return app->exec(); return app->exec();
} }
#endif
#ifdef FK_SERVER_ONLY #ifdef FK_SERVER_ONLY
// 根本没编译 GUI 相关的功能,直接在此退出 // 根本没编译 GUI 相关的功能,直接在此退出
@ -252,17 +246,6 @@ int main(int argc, char *argv[]) {
Please use ./FreeKill -s to start a server in command line."); Please use ./FreeKill -s to start a server in command line.");
#else #else
#ifdef Q_OS_WASM
EM_ASM (
FS.mkdir('/assets');
FS.mount(IDBFS, {}, '/assets');
FS.chdir('/assets');
FS.syncfs(true, function(err) {
});
);
copyPath(":/", QDir::currentPath());
#endif
app = new QApplication(argc, argv); app = new QApplication(argc, argv);
#ifdef DESKTOP_BUILD #ifdef DESKTOP_BUILD
((QApplication *)app)->setWindowIcon(QIcon("image/icon.png")); ((QApplication *)app)->setWindowIcon(QIcon("image/icon.png"));
@ -328,11 +311,12 @@ int main(int argc, char *argv[]) {
Pacman = new PackMan; Pacman = new PackMan;
// 向 Qml 中先定义几个全局变量 // 向 Qml 中先定义几个全局变量
engine->rootContext()->setContextProperty("FkVersion", FK_VERSION); auto root = engine->rootContext();
engine->rootContext()->setContextProperty("Backend", &backend); root->setContextProperty("FkVersion", FK_VERSION);
engine->rootContext()->setContextProperty("ModBackend", nullptr); root->setContextProperty("Backend", &backend);
engine->rootContext()->setContextProperty("Pacman", Pacman); root->setContextProperty("ModBackend", nullptr);
engine->rootContext()->setContextProperty("SysLocale", localeName); root->setContextProperty("Pacman", Pacman);
root->setContextProperty("SysLocale", localeName);
#ifdef QT_DEBUG #ifdef QT_DEBUG
bool debugging = true; bool debugging = true;
@ -344,9 +328,6 @@ int main(int argc, char *argv[]) {
QString system; QString system;
#if defined(Q_OS_ANDROID) #if defined(Q_OS_ANDROID)
system = "Android"; system = "Android";
#elif defined(Q_OS_WASM)
system = "Web";
engine->rootContext()->setContextProperty("ServerAddr", "127.0.0.1:9527");
#elif defined(Q_OS_WIN32) #elif defined(Q_OS_WIN32)
system = "Win"; system = "Win";
::system("chcp 65001"); ::system("chcp 65001");
@ -355,9 +336,9 @@ int main(int argc, char *argv[]) {
#else #else
system = "Other"; system = "Other";
#endif #endif
engine->rootContext()->setContextProperty("OS", system); root->setContextProperty("OS", system);
engine->rootContext()->setContextProperty( root->setContextProperty(
"AppPath", QUrl::fromLocalFile(QDir::currentPath())); "AppPath", QUrl::fromLocalFile(QDir::currentPath()));
engine->addImportPath(QDir::currentPath()); engine->addImportPath(QDir::currentPath());
@ -378,20 +359,8 @@ int main(int argc, char *argv[]) {
delete engine; delete engine;
delete Pacman; delete Pacman;
#ifdef Q_OS_WASM if (info_log) fclose(info_log);
EM_ASM ( if (err_log) fclose(err_log);
FS.syncfs(function(err) {});
);
#endif
if (info_log) {
fclose(info_log);
info_log = nullptr;
}
if (err_log) {
fclose(err_log);
info_log = nullptr;
}
return ret; return ret;
#endif #endif

View File

@ -5,10 +5,8 @@
#include "client_socket.h" #include "client_socket.h"
#include "roomthread.h" #include "roomthread.h"
#include <qjsondocument.h> #include <qjsondocument.h>
#ifndef FK_CLIENT_ONLY
#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)
@ -18,9 +16,7 @@ Router::Router(QObject *parent, ClientSocket *socket, RouterType type)
setSocket(socket); setSocket(socket);
expectedReplyId = -1; expectedReplyId = -1;
replyTimeout = 0; replyTimeout = 0;
#ifndef FK_CLIENT_ONLY
extraReplyReadySemaphore = nullptr; extraReplyReadySemaphore = nullptr;
#endif
} }
Router::~Router() { abortRequest(); } Router::~Router() { abortRequest(); }
@ -57,15 +53,12 @@ bool Router::isConsoleStart() const {
return socket->peerAddress() == "127.0.0.1"; return socket->peerAddress() == "127.0.0.1";
} }
#ifndef FK_CLIENT_ONLY
void Router::setReplyReadySemaphore(QSemaphore *semaphore) { void Router::setReplyReadySemaphore(QSemaphore *semaphore) {
extraReplyReadySemaphore = semaphore; extraReplyReadySemaphore = semaphore;
} }
#endif
void Router::request(int type, const QString &command, const QString &jsonData, void Router::request(int type, const QString &command, const QString &jsonData,
int timeout) { int timeout) {
#ifndef FK_CLIENT_ONLY
// 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());
@ -88,7 +81,6 @@ void Router::request(int type, const QString &command, const QString &jsonData,
body << timeout; body << timeout;
emit messageReady(JsonArray2Bytes(body)); emit messageReady(JsonArray2Bytes(body));
#endif
} }
void Router::reply(int type, const QString &command, const QString &jsonData) { void Router::reply(int type, const QString &command, const QString &jsonData) {
@ -115,7 +107,6 @@ int Router::getTimeout() const { return requestTimeout; }
// cancel last request from the sender // cancel last request from the sender
void Router::cancelRequest() { void Router::cancelRequest() {
#ifndef FK_CLIENT_ONLY
replyMutex.lock(); replyMutex.lock();
expectedReplyId = -1; expectedReplyId = -1;
replyTimeout = 0; replyTimeout = 0;
@ -124,22 +115,18 @@ void Router::cancelRequest() {
if (replyReadySemaphore.available() > 0) if (replyReadySemaphore.available() > 0)
replyReadySemaphore.acquire(replyReadySemaphore.available()); replyReadySemaphore.acquire(replyReadySemaphore.available());
#endif
} }
QString Router::waitForReply(int timeout) { QString Router::waitForReply(int timeout) {
QString ret; QString ret;
#ifndef FK_CLIENT_ONLY
replyReadySemaphore.tryAcquire(1, timeout * 1000); replyReadySemaphore.tryAcquire(1, timeout * 1000);
replyMutex.lock(); replyMutex.lock();
ret = m_reply; ret = m_reply;
replyMutex.unlock(); replyMutex.unlock();
#endif
return ret; return ret;
} }
void Router::abortRequest() { void Router::abortRequest() {
#ifndef FK_CLIENT_ONLY
replyMutex.lock(); replyMutex.lock();
if (expectedReplyId != -1) { if (expectedReplyId != -1) {
replyReadySemaphore.release(); replyReadySemaphore.release();
@ -149,7 +136,6 @@ void Router::abortRequest() {
extraReplyReadySemaphore = nullptr; extraReplyReadySemaphore = nullptr;
} }
replyMutex.unlock(); replyMutex.unlock();
#endif
} }
void Router::handlePacket(const QByteArray &rawPacket) { void Router::handlePacket(const QByteArray &rawPacket) {
@ -167,9 +153,7 @@ void Router::handlePacket(const QByteArray &rawPacket) {
#ifndef FK_SERVER_ONLY #ifndef FK_SERVER_ONLY
ClientInstance->callLua(command, jsonData, false); ClientInstance->callLua(command, jsonData, false);
#endif #endif
} } else {
#ifndef FK_CLIENT_ONLY
else {
ServerPlayer *player = qobject_cast<ServerPlayer *>(parent()); ServerPlayer *player = qobject_cast<ServerPlayer *>(parent());
if (command == "Heartbeat") { if (command == "Heartbeat") {
player->alive = true; player->alive = true;
@ -179,7 +163,6 @@ void Router::handlePacket(const QByteArray &rawPacket) {
Room *room = player->getRoom(); Room *room = player->getRoom();
room->handlePacket(player, command, jsonData); room->handlePacket(player, command, jsonData);
} }
#endif
} else if (type & TYPE_REQUEST) { } else if (type & TYPE_REQUEST) {
this->requestId = requestId; this->requestId = requestId;
this->requestTimeout = packet[4].toInt(); this->requestTimeout = packet[4].toInt();
@ -192,9 +175,7 @@ void Router::handlePacket(const QByteArray &rawPacket) {
// requesting server is not allowed // requesting server is not allowed
Q_ASSERT(false); Q_ASSERT(false);
} }
} } else if (type & TYPE_REPLY) {
#ifndef FK_CLIENT_ONLY
else if (type & TYPE_REPLY) {
QMutexLocker locker(&replyMutex); QMutexLocker locker(&replyMutex);
ServerPlayer *player = qobject_cast<ServerPlayer *>(parent()); ServerPlayer *player = qobject_cast<ServerPlayer *>(parent());
@ -226,5 +207,4 @@ void Router::handlePacket(const QByteArray &rawPacket) {
locker.unlock(); locker.unlock();
emit replyReady(); emit replyReady();
} }
#endif
} }

View File

@ -34,9 +34,7 @@ public:
void installAESKey(const QByteArray &key); void installAESKey(const QByteArray &key);
bool isConsoleStart() const; bool isConsoleStart() const;
#ifndef FK_CLIENT_ONLY
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);
@ -72,10 +70,8 @@ private:
int expectedReplyId; int expectedReplyId;
int replyTimeout; int replyTimeout;
QString m_reply; // should be json string QString m_reply; // should be json string
#ifndef FK_CLIENT_ONLY
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

View File

@ -5,6 +5,7 @@
class ClientSocket; class ClientSocket;
// 只是对QTcpServer的简单封装
class ServerSocket : public QObject { class ServerSocket : public QObject {
Q_OBJECT Q_OBJECT
@ -17,6 +18,7 @@ signals:
void new_connection(ClientSocket *socket); void new_connection(ClientSocket *socket);
private slots: private slots:
// 新建一个ClientSocket然后立刻交给Server相关函数处理。
void processNewConnection(); void processNewConnection();
private: private:

View File

@ -17,14 +17,10 @@ typedef int LuaFunction;
#include "sqlite3.h" #include "sqlite3.h"
#define OPENSSL_API_COMPAT 0x10101000L #define OPENSSL_API_COMPAT 0x10101000L
#if !defined (Q_OS_ANDROID) && !defined (Q_OS_WASM) #if !defined (Q_OS_ANDROID)
#define DESKTOP_BUILD #define DESKTOP_BUILD
#endif #endif
#if defined(Q_OS_WASM)
#define FK_CLIENT_ONLY
#endif
// You may define FK_SERVER_ONLY with cmake .. -D... // You may define FK_SERVER_ONLY with cmake .. -D...
#ifndef FK_SERVER_ONLY #ifndef FK_SERVER_ONLY
#include <QApplication> #include <QApplication>

View File

@ -4,8 +4,7 @@
%nodefaultdtor QmlBackend; %nodefaultdtor QmlBackend;
class QmlBackend : public QObject { class QmlBackend : public QObject {
public: public:
void emitNotifyUI(const QString &command, const QString &json_data); void notifyUI(const QString &command, const QVariant &data);
static void cd(const QString &path); static void cd(const QString &path);
static QStringList ls(const QString &dir); static QStringList ls(const QString &dir);
static QString pwd(); static QString pwd();

View File

@ -1,19 +0,0 @@
// SPDX-License-Identifier: GPL-3.0-or-later
%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"
QString GetDisabledPacks();

View File

@ -4,6 +4,17 @@
// type bindings // type bindings
// ------------------------------------------------------ // ------------------------------------------------------
%{
#include <qmlbackend.h>
%}
// Lua 5.4 特有的不能pushnumber swig迟迟不更只好手动调教
%typemap(out) int
%{
lua_pushinteger(L, $1);
SWIG_arg ++;
%}
// LuaFunction(int) and lua function // LuaFunction(int) and lua function
%naturalvar LuaFunction; %naturalvar LuaFunction;
%typemap(in) LuaFunction %typemap(in) LuaFunction
@ -65,6 +76,7 @@ SWIG_arg ++;
// QStringList // QStringList
%naturalvar QStringList; %naturalvar QStringList;
/* luaQStringList
%typemap(in, checkfn = "lua_istable") QStringList %typemap(in, checkfn = "lua_istable") QStringList
%{ %{
for (size_t i = 0; i < lua_rawlen(L, $input); ++i) { for (size_t i = 0; i < lua_rawlen(L, $input); ++i) {
@ -74,6 +86,7 @@ for (size_t i = 0; i < lua_rawlen(L, $input); ++i) {
lua_pop(L, 1); lua_pop(L, 1);
} }
%} %}
*/
%typemap(out) QStringList %typemap(out) QStringList
%{ %{
@ -94,4 +107,37 @@ SWIG_arg++;
$1 = lua_istable(L, $input) ? 1 : 0; $1 = lua_istable(L, $input) ? 1 : 0;
%} %}
// QByteArray: 仅out
%typemap(out) QByteArray
%{
lua_pushstring(L, $1.constData());
SWIG_arg++;
%}
// const QByteArray &: 仅in
%typemap(arginit) QByteArray const &
"QByteArray $1_str;"
%typemap(in, checkfn = "lua_isstring") QByteArray const &
%{
$1_str = QByteArray(lua_tostring(L, $input));
$1 = &$1_str;
%}
// QVariant: 用于jsonout
%typemap(out) QVariant
%{
QmlBackend::pushLuaValue(L, $1);
SWIG_arg++;
%}
// const QVariant &: 用于jsonin
%typemap(arginit) QVariant const &
"QVariant $1_var;"
%typemap(in) QVariant const &
%{
$1_var = QmlBackend::readLuaValue(L, $input);
$1 = &$1_var;
%}

View File

@ -1,44 +1,26 @@
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
// Make the base classes look like "complete" // Make the base classes look like "complete"
class QObject {};
class QThread {
public:
static void msleep(long msec);
};
%nodefaultctor QObject;
%nodefaultdtor QObject;
class QObject {};
%nodefaultctor QThread;
%nodefaultdtor QThread;
class QThread {};
%nodefaultctor QList;
%nodefaultdtor QList;
template <class T> template <class T>
class QList { class QList {
public: public:
QList();
~QList();
int length() const; int length() const;
void append(const T &elem); T at(int i) const;
void prepend(const T &elem);
bool isEmpty() const;
bool contains(const T &value) const;
T first() const;
T last() const;
void removeAt(int i);
int removeAll(const T &value);
bool removeOne(const T &value);
QList<T> mid(int pos, int length = -1) const;
int indexOf(const T &value, int from = 0);
void replace(int i, const T &value);
void swapItemsAt(int i, int j);
}; };
%extend QList {
T at(int i) const
{
return $self->value(i);
}
}
%template(SPlayerList) QList<ServerPlayer *>; %template(SPlayerList) QList<ServerPlayer *>;
%template(PlayerList) QList<const Player *>;
%template(IntList) QList<int>; %template(IntList) QList<int>;
%template(BoolList) QList<bool>;
%native(GetMicroSecond) int GetMicroSecond(lua_State *L); %native(GetMicroSecond) int GetMicroSecond(lua_State *L);
%{ %{
@ -56,3 +38,15 @@ void qDebug(const char *msg, ...);
void qInfo(const char *msg, ...); void qInfo(const char *msg, ...);
void qWarning(const char *msg, ...); void qWarning(const char *msg, ...);
void qCritical(const char *msg, ...); void qCritical(const char *msg, ...);
class QJsonDocument {
public:
enum JsonFormat {
Indented,
Compact,
};
static QJsonDocument fromJson(const QByteArray &json);
static QJsonDocument fromVariant(const QVariant &variant);
QByteArray toJson(QJsonDocument::JsonFormat format = 1) const;
QVariant toVariant() const;
};

View File

@ -1,7 +1,9 @@
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
#include "qmlbackend.h" #include "qmlbackend.h"
#include <lua.h>
#include <qjsondocument.h> #include <qjsondocument.h>
#include <qvariant.h>
#ifndef FK_SERVER_ONLY #ifndef FK_SERVER_ONLY
#include <qaudiooutput.h> #include <qaudiooutput.h>
@ -16,9 +18,7 @@
#endif #endif
#include <cstdlib> #include <cstdlib>
#ifndef Q_OS_WASM
#include "server.h" #include "server.h"
#endif
#include "client.h" #include "client.h"
#include "util.h" #include "util.h"
#include "replayer.h" #include "replayer.h"
@ -81,7 +81,6 @@ 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);
@ -90,7 +89,6 @@ 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) {
@ -144,10 +142,6 @@ void QmlBackend::quitLobby(bool close) {
// ServerInstance->deleteLater(); // ServerInstance->deleteLater();
} }
void QmlBackend::emitNotifyUI(const QString &command, const QString &jsonData) {
emit notifyUI(command, jsonData);
}
QString QmlBackend::translate(const QString &src) { QString QmlBackend::translate(const QString &src) {
if (!ClientInstance) if (!ClientInstance)
return src; return src;
@ -179,6 +173,9 @@ void QmlBackend::pushLuaValue(lua_State *L, QVariant v) {
case QMetaType::UInt: case QMetaType::UInt:
lua_pushinteger(L, v.toInt()); lua_pushinteger(L, v.toInt());
break; break;
case QMetaType::LongLong:
lua_pushinteger(L, v.toLongLong());
break;
case QMetaType::Double: case QMetaType::Double:
lua_pushnumber(L, v.toDouble()); lua_pushnumber(L, v.toDouble());
break; break;
@ -217,9 +214,79 @@ void QmlBackend::pushLuaValue(lua_State *L, QVariant v) {
} }
} }
QString QmlBackend::callLuaFunction(const QString &func_name, // 要求返回一个QVariant而不对栈产生影响
QVariant QmlBackend::readLuaValue(lua_State *L, int index,
QHash<const void *, bool> stack) {
if (index == 0) index = lua_gettop(L);
auto tp = lua_type(L, index);
switch (tp) {
case LUA_TNIL:
return QVariant::fromValue(nullptr);
case LUA_TBOOLEAN:
return QVariant((bool)lua_toboolean(L, index));
case LUA_TNUMBER:
return QVariant(lua_tonumber(L, index));
case LUA_TSTRING:
return QVariant(lua_tostring(L, index));
case LUA_TTABLE: {
auto p = lua_topointer(L, index);
if (stack[p]) {
luaL_error(L, "circular reference detected");
return QVariant(); // won't return
}
stack[p] = true;
lua_len(L, index);
int length = lua_tointeger(L, -1);
lua_pop(L, 1);
if (length == 0) {
bool empty = true;
QVariantMap map;
lua_pushnil(L);
while (lua_next(L, index) != 0) {
if (lua_type(L, -2) != LUA_TSTRING) {
luaL_error(L, "key of object must be string");
return QVariant();
}
const char *key = lua_tostring(L, -2);
auto value = readLuaValue(L, lua_gettop(L), stack);
lua_pop(L, 1);
map[key] = value;
empty = false;
}
if (empty) {
return QVariantList();
} else {
return map;
}
} else {
QVariantList arr;
for (int i = 1; i <= length; i++) {
lua_rawgeti(L, index, i);
arr << readLuaValue(L, lua_gettop(L), stack);
lua_pop(L, 1);
}
return arr;
}
break;
}
// ignore function, userdata and thread
default:
luaL_error(L, "unexpected value type %s", lua_typename(L, tp));
}
return QVariant(); // won't return
}
QVariant QmlBackend::callLuaFunction(const QString &func_name,
QVariantList params) { QVariantList params) {
if (!ClientInstance) return "{}"; if (!ClientInstance) return QVariantMap();
lua_State *L = ClientInstance->getLuaState(); lua_State *L = ClientInstance->getLuaState();
lua_getglobal(L, func_name.toLatin1().data()); lua_getglobal(L, func_name.toLatin1().data());
@ -229,19 +296,19 @@ QString QmlBackend::callLuaFunction(const QString &func_name,
} }
int err = lua_pcall(L, params.length(), 1, 0); int err = lua_pcall(L, params.length(), 1, 0);
const char *result = lua_tostring(L, -1);
if (err) { if (err) {
qCritical() << result; qCritical() << lua_tostring(L, -1);
lua_pop(L, 1); lua_pop(L, 1);
return ""; return QVariant();
} }
auto result = readLuaValue(L);
lua_pop(L, 1); lua_pop(L, 1);
return QString(result); return result;
} }
QString QmlBackend::evalLuaExp(const QString &lua) { QVariant QmlBackend::evalLuaExp(const QString &lua) {
if (!ClientInstance) return "{}"; if (!ClientInstance) return QVariantMap();
lua_State *L = ClientInstance->getLuaState(); lua_State *L = ClientInstance->getLuaState();
int err; int err;
@ -252,15 +319,15 @@ QString QmlBackend::evalLuaExp(const QString &lua) {
return ""; return "";
} }
err = lua_pcall(L, 0, 1, 0); err = lua_pcall(L, 0, 1, 0);
const char *result = luaL_tolstring(L, -1, NULL);
if (err) { if (err) {
qCritical() << result; qCritical() << lua_tostring(L, -1);
lua_pop(L, 1); lua_pop(L, 1);
return ""; return QVariant();
} }
auto result = readLuaValue(L);
lua_pop(L, 1); lua_pop(L, 1);
return QString(result); return result;
} }
QString QmlBackend::pubEncrypt(const QString &key, const QString &data) { QString QmlBackend::pubEncrypt(const QString &key, const QString &data) {
@ -505,13 +572,13 @@ void QmlBackend::setReplayer(Replayer *rep) {
replayer = rep; replayer = rep;
if (rep) { if (rep) {
connect(rep, &Replayer::duration_set, this, [this](int sec) { connect(rep, &Replayer::duration_set, this, [this](int sec) {
this->emitNotifyUI("ReplayerDurationSet", QString::number(sec)); this->notifyUI("ReplayerDurationSet", QString::number(sec));
}); });
connect(rep, &Replayer::elasped, this, [this](int sec) { connect(rep, &Replayer::elasped, this, [this](int sec) {
this->emitNotifyUI("ReplayerElapsedChange", QString::number(sec)); this->notifyUI("ReplayerElapsedChange", QString::number(sec));
}); });
connect(rep, &Replayer::speed_changed, this, [this](qreal speed) { connect(rep, &Replayer::speed_changed, this, [this](qreal speed) {
this->emitNotifyUI("ReplayerSpeedChange", QString::number(speed)); this->notifyUI("ReplayerSpeedChange", QString::number(speed));
}); });
connect(this, &QmlBackend::replayerToggle, rep, &Replayer::toggle); connect(this, &QmlBackend::replayerToggle, rep, &Replayer::toggle);
connect(this, &QmlBackend::replayerSlowDown, rep, &Replayer::slowDown); connect(this, &QmlBackend::replayerSlowDown, rep, &Replayer::slowDown);

View File

@ -23,6 +23,11 @@ public:
static Q_INVOKABLE bool exists(const QString &file); static Q_INVOKABLE bool exists(const QString &file);
static Q_INVOKABLE bool isDir(const QString &file); static Q_INVOKABLE bool isDir(const QString &file);
// 这俩函数为啥要写在这。。
static void pushLuaValue(lua_State *L, QVariant v);
static QVariant readLuaValue(lua_State *L, int index = 0,
QHash<const void *, bool> stack = QHash<const void *, bool>());
#ifndef FK_SERVER_ONLY #ifndef FK_SERVER_ONLY
QQmlApplicationEngine *getEngine() const; QQmlApplicationEngine *getEngine() const;
void setEngine(QQmlApplicationEngine *engine); void setEngine(QQmlApplicationEngine *engine);
@ -33,14 +38,11 @@ public:
// Lobby // Lobby
Q_INVOKABLE void quitLobby(bool close = true); Q_INVOKABLE void quitLobby(bool close = true);
// lua --> qml
void emitNotifyUI(const QString &command, const QString &jsonData);
// read data from lua, call lua functions // read data from lua, call lua functions
Q_INVOKABLE QString translate(const QString &src); Q_INVOKABLE QString translate(const QString &src);
Q_INVOKABLE QString callLuaFunction(const QString &func_name, Q_INVOKABLE QVariant callLuaFunction(const QString &func_name,
QVariantList params); QVariantList params);
Q_INVOKABLE QString evalLuaExp(const QString &lua); Q_INVOKABLE QVariant evalLuaExp(const QString &lua);
Q_INVOKABLE QString pubEncrypt(const QString &key, const QString &data); Q_INVOKABLE QString pubEncrypt(const QString &key, const QString &data);
Q_INVOKABLE QString loadConf(); Q_INVOKABLE QString loadConf();
@ -74,7 +76,7 @@ public:
Q_INVOKABLE void controlReplayer(QString type); Q_INVOKABLE void controlReplayer(QString type);
signals: signals:
void notifyUI(const QString &command, const QString &jsonData); void notifyUI(const QString &command, const QVariant &data);
void volumeChanged(qreal); void volumeChanged(qreal);
void replayerToggle(); void replayerToggle();
void replayerSpeedUp(); void replayerSpeedUp();
@ -96,8 +98,6 @@ private:
qreal m_volume; qreal m_volume;
Replayer *replayer; Replayer *replayer;
void pushLuaValue(lua_State *L, QVariant v);
#endif #endif
}; };